From 03416d24e1544ba56611f770940ca5674ce64b70 Mon Sep 17 00:00:00 2001 From: PG-SteveT <63470275+PG-SteveT@users.noreply.github.com> Date: Wed, 27 May 2020 12:16:20 -0700 Subject: [PATCH] Initial Source Code commit Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered. --- CnCRemastered.sln | 26 + REDALERT/2KEYFRAM.CPP | 588 ++ REDALERT/2SUPPORT.ASM | 561 ++ REDALERT/2TXTPRNT.ASM | 504 ++ REDALERT/AADATA.CPP | 655 ++ REDALERT/ABSTRACT.CPP | 213 + REDALERT/ABSTRACT.H | 144 + REDALERT/ADATA.CPP | 2553 ++++++ REDALERT/ADPCM.CPP | 80 + REDALERT/AIRCRAFT.CPP | 4437 +++++++++++ REDALERT/AIRCRAFT.H | 252 + REDALERT/ANIM.CPP | 1221 +++ REDALERT/ANIM.H | 189 + REDALERT/AUDIO.CPP | 858 ++ REDALERT/AUDIO.H | 96 + REDALERT/B64PIPE.CPP | 162 + REDALERT/B64PIPE.H | 89 + REDALERT/B64STRAW.CPP | 114 + REDALERT/B64STRAW.H | 88 + REDALERT/BAR.CPP | 236 + REDALERT/BAR.H | 108 + REDALERT/BASE.CPP | 545 ++ REDALERT/BASE.H | 127 + REDALERT/BASE64.CPP | 434 ++ REDALERT/BASE64.H | 37 + REDALERT/BBDATA.CPP | 297 + REDALERT/BDATA.CPP | 3832 +++++++++ REDALERT/BENCH.CPP | 165 + REDALERT/BENCH.H | 121 + REDALERT/BFIOFILE.CPP | 984 +++ REDALERT/BFIOFILE.H | 92 + REDALERT/BIGCHECK.CPP | 81 + REDALERT/BIGCHECK.H | 71 + REDALERT/BLOWFISH.CPP | 595 ++ REDALERT/BLOWFISH.H | 114 + REDALERT/BLOWPIPE.CPP | 199 + REDALERT/BLOWPIPE.H | 82 + REDALERT/BLWSTRAW.CPP | 158 + REDALERT/BLWSTRAW.H | 84 + REDALERT/BMP8.CPP | 186 + REDALERT/BMP8.H | 40 + REDALERT/BUFF.CPP | 217 + REDALERT/BUFF.H | 96 + REDALERT/BUFFERX.H | 98 + REDALERT/BUILDING.CPP | 6054 +++++++++++++++ REDALERT/BUILDING.H | 361 + REDALERT/BULLET.CPP | 1110 +++ REDALERT/BULLET.H | 150 + REDALERT/CARGO.CPP | 180 + REDALERT/CARGO.H | 90 + REDALERT/CARRY.CPP | 150 + REDALERT/CARRY.H | 81 + REDALERT/CBN_.H | 153 + REDALERT/CCDDE.CPP | 428 + REDALERT/CCDDE.H | 86 + REDALERT/CCFILE.CPP | 692 ++ REDALERT/CCFILE.H | 104 + REDALERT/CCINI.CPP | 1484 ++++ REDALERT/CCINI.H | 117 + REDALERT/CCMPATH.CPP | 418 + REDALERT/CCPTR.CPP | 111 + REDALERT/CCPTR.H | 114 + REDALERT/CCTEN.CPP | 600 ++ REDALERT/CDATA.CPP | 3321 ++++++++ REDALERT/CDFILE.CPP | 746 ++ REDALERT/CDFILE.H | 116 + REDALERT/CELL.CPP | 3299 ++++++++ REDALERT/CELL.H | 332 + REDALERT/CHECKBOX.CPP | 99 + REDALERT/CHECKBOX.H | 53 + REDALERT/CHEKLIST.CPP | 362 + REDALERT/CHEKLIST.H | 102 + REDALERT/CLASS.CPP | 16 + REDALERT/COLRLIST.CPP | 275 + REDALERT/COLRLIST.H | 84 + REDALERT/COMBAT.CPP | 422 + REDALERT/COMBUF.CPP | 1161 +++ REDALERT/COMBUF.H | 190 + REDALERT/COMINIT.CPP | 48 + REDALERT/COMINIT.H | 31 + REDALERT/COMPAT.H | 214 + REDALERT/COMQUEUE.CPP | 1002 +++ REDALERT/COMQUEUE.H | 190 + REDALERT/CONFDLG.CPP | 244 + REDALERT/CONFDLG.H | 53 + REDALERT/CONNECT.CPP | 835 ++ REDALERT/CONNECT.H | 278 + REDALERT/CONNMGR.H | 151 + REDALERT/CONQUER.CPP | 5736 ++++++++++++++ REDALERT/CONQUER.H | 571 ++ REDALERT/CONST.CPP | 845 ++ REDALERT/CONTROL.CPP | 223 + REDALERT/CONTROL.H | 87 + REDALERT/COORD.CPP | 704 ++ REDALERT/COORDA.ASM | 126 + REDALERT/COORDA.h | 141 + REDALERT/CPUID.ASM | 182 + REDALERT/CRATE.CPP | 182 + REDALERT/CRATE.H | 76 + REDALERT/CRC.CPP | 133 + REDALERT/CRC.H | 118 + REDALERT/CRCPIPE.CPP | 87 + REDALERT/CRCPIPE.H | 63 + REDALERT/CRCSTRAW.CPP | 91 + REDALERT/CRCSTRAW.H | 64 + REDALERT/CREDITS.CPP | 256 + REDALERT/CREDITS.H | 72 + REDALERT/CREW.CPP | 36 + REDALERT/CREW.H | 67 + REDALERT/CSTRAW.CPP | 97 + REDALERT/CSTRAW.H | 68 + REDALERT/DDE.CPP | 462 ++ REDALERT/DDE.H | 172 + REDALERT/DEBUG.CPP | 557 ++ REDALERT/DEBUG.H | 58 + REDALERT/DEFINES.H | 3628 +++++++++ REDALERT/DESCDLG.CPP | 160 + REDALERT/DESCDLG.H | 66 + REDALERT/DIAL8.CPP | 316 + REDALERT/DIAL8.H | 73 + REDALERT/DIALOG.CPP | 996 +++ REDALERT/DIBAPI.H | 150 + REDALERT/DIBFILE.CPP | 702 ++ REDALERT/DIBUTIL.CPP | 1314 ++++ REDALERT/DIBUTIL.H | 38 + REDALERT/DISPLAY.CPP | 5138 ++++++++++++ REDALERT/DISPLAY.H | 337 + REDALERT/DLLInterface.cpp | 8605 ++++++++++++++++++++ REDALERT/DLLInterface.h | 924 +++ REDALERT/DLLInterfaceEditor.cpp | 830 ++ REDALERT/DOOR.CPP | 199 + REDALERT/DOOR.H | 86 + REDALERT/DPMI.CPP | 167 + REDALERT/DPMI.H | 173 + REDALERT/DRIVE.CPP | 2338 ++++++ REDALERT/DRIVE.H | 208 + REDALERT/DROP.CPP | 216 + REDALERT/DROP.H | 350 + REDALERT/DTABLE.CPP | 1443 ++++ REDALERT/DYNAVEC.CPP | 300 + REDALERT/EDIT.CPP | 472 ++ REDALERT/EDIT.H | 120 + REDALERT/EGOS.CPP | 986 +++ REDALERT/EGOS.H | 60 + REDALERT/ENDING.CPP | 160 + REDALERT/ENDING.H | 40 + REDALERT/EVENT.CPP | 1064 +++ REDALERT/EVENT.H | 257 + REDALERT/EXPAND.CPP | 567 ++ REDALERT/EXTERNS.H | 508 ++ REDALERT/FACE.CPP | 224 + REDALERT/FACE.H | 78 + REDALERT/FACING.CPP | 180 + REDALERT/FACING.H | 77 + REDALERT/FACTORY.CPP | 697 ++ REDALERT/FACTORY.H | 161 + REDALERT/FAKESOCK.H | 50 + REDALERT/FIELD.CPP | 211 + REDALERT/FIELD.H | 82 + REDALERT/FILEPCX.H | 74 + REDALERT/FINDPATH.CPP | 1307 ++++ REDALERT/FIXED.CPP | 245 + REDALERT/FIXED.H | 234 + REDALERT/FLASHER.CPP | 130 + REDALERT/FLASHER.H | 78 + REDALERT/FLY.CPP | 131 + REDALERT/FLY.H | 73 + REDALERT/FOOT.CPP | 2597 +++++++ REDALERT/FOOT.H | 389 + REDALERT/FTIMER.H | 716 ++ REDALERT/FUNCTION.H | 1088 +++ REDALERT/FUSE.CPP | 194 + REDALERT/FUSE.H | 89 + REDALERT/GADGET.CPP | 869 +++ REDALERT/GADGET.H | 273 + REDALERT/GAMEDLG.CPP | 517 ++ REDALERT/GAMEDLG.H | 49 + REDALERT/GAUGE.CPP | 536 ++ REDALERT/GAUGE.H | 106 + REDALERT/GETCPU.CPP | 103 + REDALERT/GLOBALS.CPP | 819 ++ REDALERT/GOPTIONS.CPP | 622 ++ REDALERT/GOPTIONS.H | 96 + REDALERT/GSCREEN.CPP | 484 ++ REDALERT/GSCREEN.H | 136 + REDALERT/HDATA.CPP | 523 ++ REDALERT/HEAP.CPP | 663 ++ REDALERT/HEAP.H | 195 + REDALERT/HELP.CPP | 414 + REDALERT/HELP.H | 140 + REDALERT/HOUSE.CPP | 8173 +++++++++++++++++++ REDALERT/HOUSE.H | 957 +++ REDALERT/HSV.CPP | 208 + REDALERT/HSV.H | 78 + REDALERT/ICONLIST.CPP | 907 +++ REDALERT/ICONLIST.H | 189 + REDALERT/IDATA.CPP | 1530 ++++ REDALERT/INFANTRY.CPP | 4335 +++++++++++ REDALERT/INFANTRY.H | 239 + REDALERT/INI.CPP | 1287 +++ REDALERT/INI.H | 159 + REDALERT/INIBIN.CPP | 36 + REDALERT/INICODE.CPP | 363 + REDALERT/INIT.CPP | 3863 +++++++++ REDALERT/INLINE.H | 1066 +++ REDALERT/INT.CPP | 43 + REDALERT/INT.H | 301 + REDALERT/INTERNET.CPP | 975 +++ REDALERT/INTERNET.H | 53 + REDALERT/INTERPAL.CPP | 465 ++ REDALERT/INTRO.CPP | 302 + REDALERT/INTRO.H | 40 + REDALERT/IOMAP.CPP | 517 ++ REDALERT/IOOBJ.CPP | 898 +++ REDALERT/IPX.CPP | 1164 +++ REDALERT/IPX.H | 188 + REDALERT/IPX95.CPP | 231 + REDALERT/IPX95.H | 101 + REDALERT/IPXADDR.CPP | 506 ++ REDALERT/IPXADDR.H | 104 + REDALERT/IPXCONN.CPP | 876 +++ REDALERT/IPXCONN.H | 203 + REDALERT/IPXGCONN.CPP | 531 ++ REDALERT/IPXGCONN.H | 203 + REDALERT/IPXMGR.CPP | 2066 +++++ REDALERT/IPXMGR.H | 394 + REDALERT/IPXPROT.ASM | 110 + REDALERT/IPXREAL.ASM | 316 + REDALERT/ITABLE.CPP | 1443 ++++ REDALERT/JSHELL.CPP | 659 ++ REDALERT/JSHELL.H | 478 ++ REDALERT/KEY.CPP | 758 ++ REDALERT/KEY.H | 656 ++ REDALERT/KEYBOARD.CPP | 466 ++ REDALERT/KEYBOARD.H | 649 ++ REDALERT/KEYFBUFF.ASM | 5210 +++++++++++++ REDALERT/LANGUAGE.H | 117 + REDALERT/LAYER.CPP | 161 + REDALERT/LAYER.H | 64 + REDALERT/LCW.CPP | 159 + REDALERT/LCW.H | 46 + REDALERT/LCWCOMP.ASM | 286 + REDALERT/LCWPIPE.CPP | 310 + REDALERT/LCWPIPE.H | 102 + REDALERT/LCWSTRAW.CPP | 176 + REDALERT/LCWSTRAW.H | 103 + REDALERT/LCWUNCMP.CPP | 163 + REDALERT/LED.H | 43 + REDALERT/LINK.CPP | 398 + REDALERT/LINK.H | 72 + REDALERT/LINT.H | 185 + REDALERT/LIST.CPP | 969 +++ REDALERT/LIST.H | 799 ++ REDALERT/LISTNODE.H | 170 + REDALERT/LOADDLG.CPP | 783 ++ REDALERT/LOADDLG.H | 90 + REDALERT/LOGIC.CPP | 474 ++ REDALERT/LOGIC.H | 58 + REDALERT/LZO.H | 57 + REDALERT/LZO1X.H | 107 + REDALERT/LZO1X_C.CPP | 348 + REDALERT/LZO1X_D.CPP | 205 + REDALERT/LZOCONF.H | 214 + REDALERT/LZOPIPE.CPP | 318 + REDALERT/LZOPIPE.H | 101 + REDALERT/LZOSTRAW.CPP | 181 + REDALERT/LZOSTRAW.H | 102 + REDALERT/LZO_CONF.H | 283 + REDALERT/LZW.CPP | 536 ++ REDALERT/LZW.H | 86 + REDALERT/LZWOTRAW.CPP | 181 + REDALERT/LZWPIPE.CPP | 312 + REDALERT/LZWPIPE.H | 102 + REDALERT/LZWSTRAW.CPP | 177 + REDALERT/LZWSTRAW.H | 103 + REDALERT/License.txt | 712 ++ REDALERT/MAP.CPP | 2284 ++++++ REDALERT/MAP.H | 183 + REDALERT/MAPEDDLG.CPP | 2705 +++++++ REDALERT/MAPEDIT.CPP | 2170 ++++++ REDALERT/MAPEDIT.H | 341 + REDALERT/MAPEDPLC.CPP | 1821 +++++ REDALERT/MAPEDSEL.CPP | 585 ++ REDALERT/MAPEDTM.CPP | 942 +++ REDALERT/MAPSEL.CPP | 399 + REDALERT/MCI.CPP | 362 + REDALERT/MCI.H | 105 + REDALERT/MCIMOVIE.CPP | 349 + REDALERT/MCIMOVIE.H | 66 + REDALERT/MEMCHECK.H | 2602 +++++++ REDALERT/MENUS.CPP | 1020 +++ REDALERT/MESSAGE.H | 44 + REDALERT/MISSION.CPP | 570 ++ REDALERT/MISSION.H | 199 + REDALERT/MIXFILE.CPP | 598 ++ REDALERT/MIXFILE.H | 131 + REDALERT/MMX.ASM | 329 + REDALERT/MONOC.CPP | 1132 +++ REDALERT/MONOC.H | 237 + REDALERT/MOUSE.CPP | 389 + REDALERT/MOUSE.H | 123 + REDALERT/MOVIE.H | 70 + REDALERT/MP.CPP | 2729 +++++++ REDALERT/MP.H | 173 + REDALERT/MPGSET.CPP | 606 ++ REDALERT/MPGSET.H | 48 + REDALERT/MPLAYER.CPP | 1248 +++ REDALERT/MPLIB.CPP | 127 + REDALERT/MPMGRD.CPP | 363 + REDALERT/MPMGRD.H | 80 + REDALERT/MPMGRW.CPP | 297 + REDALERT/MPMGRW.H | 85 + REDALERT/MPU.CPP | 45 + REDALERT/MPU.H | 68 + REDALERT/MSGBOX.CPP | 517 ++ REDALERT/MSGBOX.H | 52 + REDALERT/MSGLIST.CPP | 1408 ++++ REDALERT/MSGLIST.H | 211 + REDALERT/MiscAsm.cpp | 1572 ++++ REDALERT/NETDLG.CPP | 7844 +++++++++++++++++++ REDALERT/NOSEQCON.CPP | 688 ++ REDALERT/NOSEQCON.H | 126 + REDALERT/NULLCONN.CPP | 284 + REDALERT/NULLCONN.H | 147 + REDALERT/NULLDLG.CPP | 7835 +++++++++++++++++++ REDALERT/NULLMGR.CPP | 2618 +++++++ REDALERT/NULLMGR.H | 223 + REDALERT/OBJECT.CPP | 2488 ++++++ REDALERT/OBJECT.H | 272 + REDALERT/OCIDL.H | 8026 +++++++++++++++++++ REDALERT/ODATA.CPP | 951 +++ REDALERT/OPTIONS.CPP | 920 +++ REDALERT/OPTIONS.H | 157 + REDALERT/OVERLAY.CPP | 381 + REDALERT/OVERLAY.H | 98 + REDALERT/PACKET.CPP | 463 ++ REDALERT/PACKET.H | 98 + REDALERT/PALETTEC.CPP | 450 ++ REDALERT/PALETTEC.H | 81 + REDALERT/PIPE.CPP | 163 + REDALERT/PIPE.H | 89 + REDALERT/PK.CPP | 360 + REDALERT/PK.H | 90 + REDALERT/PKPIPE.CPP | 284 + REDALERT/PKPIPE.H | 133 + REDALERT/PKSTRAW.CPP | 301 + REDALERT/PKSTRAW.H | 122 + REDALERT/POWER.CPP | 475 ++ REDALERT/POWER.H | 123 + REDALERT/PROFILE.CPP | 867 +++ REDALERT/QUEUE.CPP | 4561 +++++++++++ REDALERT/QUEUE.H | 292 + REDALERT/RADAR.CPP | 2683 +++++++ REDALERT/RADAR.H | 250 + REDALERT/RADIO.CPP | 290 + REDALERT/RADIO.H | 104 + REDALERT/RAMFILE.CPP | 472 ++ REDALERT/RAMFILE.H | 110 + REDALERT/RAND.CPP | 170 + REDALERT/RANDOM.CPP | 180 + REDALERT/RANDOM.H | 83 + REDALERT/RAWFILE.CPP | 1345 ++++ REDALERT/RAWFILE.H | 332 + REDALERT/RAWOLAPI.CPP | 2099 +++++ REDALERT/RAWOLAPI.H | 337 + REDALERT/READLINE.CPP | 87 + REDALERT/READLINE.H | 23 + REDALERT/RECT.CPP | 197 + REDALERT/RECT.H | 74 + REDALERT/REGION.H | 56 + REDALERT/REINF.CPP | 748 ++ REDALERT/RESOURCE/RedAlert.rc | 100 + REDALERT/RESOURCE/resource.h | 30 + REDALERT/RGB.CPP | 246 + REDALERT/RGB.H | 140 + REDALERT/RNDSTRAW.CPP | 317 + REDALERT/RNDSTRAW.H | 89 + REDALERT/RNG.H | 51 + REDALERT/ROTBMP.CPP | 414 + REDALERT/ROTBMP.H | 112 + REDALERT/RULES.CPP | 1074 +++ REDALERT/RULES.H | 896 +++ REDALERT/RedAlert.vcxproj | 787 ++ REDALERT/RedAlert.vcxproj.filters | 1842 +++++ REDALERT/SAVEDLG.H | 68 + REDALERT/SAVELOAD.CPP | 1654 ++++ REDALERT/SCENARIO.CPP | 3739 +++++++++ REDALERT/SCENARIO.H | 333 + REDALERT/SCORE.CPP | 1942 +++++ REDALERT/SCORE.H | 145 + REDALERT/SCREEN.H | 65 + REDALERT/SCROLL.CPP | 267 + REDALERT/SCROLL.H | 71 + REDALERT/SDATA.CPP | 568 ++ REDALERT/SEARCH.H | 686 ++ REDALERT/SEDITDLG.CPP | 383 + REDALERT/SEDITDLG.H | 53 + REDALERT/SENDFILE.CPP | 877 +++ REDALERT/SEQCONN.CPP | 560 ++ REDALERT/SEQCONN.H | 108 + REDALERT/SESSION.CPP | 1875 +++++ REDALERT/SESSION.H | 703 ++ REDALERT/SHA.CPP | 314 + REDALERT/SHA.H | 201 + REDALERT/SHAPEBTN.CPP | 179 + REDALERT/SHAPEBTN.H | 65 + REDALERT/SHAPIPE.CPP | 87 + REDALERT/SHAPIPE.H | 63 + REDALERT/SHASTRAW.CPP | 93 + REDALERT/SHASTRAW.H | 65 + REDALERT/SIDEBAR.CPP | 2478 ++++++ REDALERT/SIDEBAR.H | 397 + REDALERT/SIDEBARGlyphx.CPP | 821 ++ REDALERT/SIDEBARGlyphx.H | 209 + REDALERT/SLIDER.CPP | 413 + REDALERT/SLIDER.H | 107 + REDALERT/SMUDGE.CPP | 361 + REDALERT/SMUDGE.H | 96 + REDALERT/SOUNDDLG.CPP | 455 ++ REDALERT/SOUNDDLG.H | 112 + REDALERT/SPECIAL.CPP | 598 ++ REDALERT/SPECIAL.H | 115 + REDALERT/SPRITE.CPP | 326 + REDALERT/STAGE.H | 80 + REDALERT/STARTUP.CPP | 1213 +++ REDALERT/STATBTN.CPP | 270 + REDALERT/STATBTN.H | 72 + REDALERT/STATS.CPP | 911 +++ REDALERT/STRAW.CPP | 138 + REDALERT/STRAW.H | 87 + REDALERT/STUB.CPP | 22 + REDALERT/STYLE.H | 81 + REDALERT/SUPER.CPP | 388 + REDALERT/SUPER.H | 89 + REDALERT/SUPPORT.ASM | 556 ++ REDALERT/SURFACE.CPP | 160 + REDALERT/SURFACE.H | 113 + REDALERT/Shape.cpp | 111 + REDALERT/TAB.CPP | 318 + REDALERT/TAB.H | 76 + REDALERT/TACTION.CPP | 902 +++ REDALERT/TACTION.H | 154 + REDALERT/TARGET.CPP | 941 +++ REDALERT/TARGET.H | 173 + REDALERT/TCPIP.CPP | 906 +++ REDALERT/TCPIP.H | 197 + REDALERT/TDATA.CPP | 916 +++ REDALERT/TEAM.CPP | 3073 ++++++++ REDALERT/TEAM.H | 272 + REDALERT/TEAMTYPE.CPP | 1879 +++++ REDALERT/TEAMTYPE.H | 282 + REDALERT/TECHNO.CPP | 6913 +++++++++++++++++ REDALERT/TECHNO.H | 418 + REDALERT/TEMPLATE.CPP | 238 + REDALERT/TEMPLATE.H | 93 + REDALERT/TENMGR.CPP | 1118 +++ REDALERT/TENMGR.H | 115 + REDALERT/TERRAIN.CPP | 789 ++ REDALERT/TERRAIN.H | 154 + REDALERT/TEVENT.CPP | 766 ++ REDALERT/TEVENT.H | 185 + REDALERT/TEXTBTN.CPP | 348 + REDALERT/TEXTBTN.H | 71 + REDALERT/THEME.CPP | 614 ++ REDALERT/THEME.H | 87 + REDALERT/TOGGLE.CPP | 198 + REDALERT/TOGGLE.H | 79 + REDALERT/TOOLTIP.CPP | 294 + REDALERT/TOOLTIP.H | 74 + REDALERT/TRACKER.CPP | 131 + REDALERT/TRIGGER.CPP | 494 ++ REDALERT/TRIGGER.H | 123 + REDALERT/TRIGTYPE.CPP | 2126 +++++ REDALERT/TRIGTYPE.H | 149 + REDALERT/TURRET.CPP | 111 + REDALERT/TURRET.H | 57 + REDALERT/TXTLABEL.CPP | 98 + REDALERT/TXTLABEL.H | 69 + REDALERT/TXTPRNT.ASM | 520 ++ REDALERT/TYPE.H | 2057 +++++ REDALERT/UDATA.CPP | 1374 ++++ REDALERT/UDPADDR.CPP | 234 + REDALERT/UNIT.CPP | 5111 ++++++++++++ REDALERT/UNIT.H | 250 + REDALERT/UTRACKER.CPP | 247 + REDALERT/UTRACKER.H | 83 + REDALERT/VDATA.CPP | 709 ++ REDALERT/VECTOR.CPP | 672 ++ REDALERT/VECTOR.H | 1012 +++ REDALERT/VERSION.CPP | 850 ++ REDALERT/VERSION.H | 199 + REDALERT/VESSEL.CPP | 2425 ++++++ REDALERT/VESSEL.H | 154 + REDALERT/VISUDLG.CPP | 413 + REDALERT/VISUDLG.H | 73 + REDALERT/VORTEX.CPP | 1257 +++ REDALERT/VORTEX.H | 270 + REDALERT/W95TRACE.CPP | 126 + REDALERT/W95TRACE.H | 55 + REDALERT/WARHEAD.CPP | 189 + REDALERT/WARHEAD.H | 117 + REDALERT/WATCOM.H | 90 + REDALERT/WEAPON.CPP | 333 + REDALERT/WEAPON.H | 162 + REDALERT/WIN32LIB/ALLOC.CPP | 509 ++ REDALERT/WIN32LIB/AUDIO.H | 159 + REDALERT/WIN32LIB/BUFFER.CPP | 128 + REDALERT/WIN32LIB/BUFFER.H | 121 + REDALERT/WIN32LIB/BUFFGLBL.CPP | 80 + REDALERT/WIN32LIB/DDRAW.CPP | 1002 +++ REDALERT/WIN32LIB/DDRAW.H | 3117 ++++++++ REDALERT/WIN32LIB/DEFINES.H | 36 + REDALERT/WIN32LIB/DELAY.CPP | 59 + REDALERT/WIN32LIB/DESCMGMT.H | 90 + REDALERT/WIN32LIB/DIFFTB.INC | 1445 ++++ REDALERT/WIN32LIB/DIPTHONG.CPP | 325 + REDALERT/WIN32LIB/DIPTHONG.H | 21 + REDALERT/WIN32LIB/DPLAY.H | 323 + REDALERT/WIN32LIB/DRAWBUFF.H | 64 + REDALERT/WIN32LIB/DRAWBUFF.INC | 90 + REDALERT/WIN32LIB/DRAWRECT.CPP | 69 + REDALERT/WIN32LIB/DSETUP.H | 71 + REDALERT/WIN32LIB/DSOUND.H | 380 + REDALERT/WIN32LIB/DrawMisc.cpp | 5509 +++++++++++++ REDALERT/WIN32LIB/EXTERNS.H | 37 + REDALERT/WIN32LIB/FASTFILE.H | 39 + REDALERT/WIN32LIB/FILE.H | 257 + REDALERT/WIN32LIB/FILEPCX.H | 74 + REDALERT/WIN32LIB/FILETEMP.H | 57 + REDALERT/WIN32LIB/FONT.CPP | 138 + REDALERT/WIN32LIB/FONT.H | 115 + REDALERT/WIN32LIB/FUNCTION.H | 44 + REDALERT/WIN32LIB/GBUFFER.CPP | 707 ++ REDALERT/WIN32LIB/GBUFFER.H | 1363 ++++ REDALERT/WIN32LIB/GBUFFER.INC | 48 + REDALERT/WIN32LIB/GETSHAPE.CPP | 359 + REDALERT/WIN32LIB/ICONCACH.H | 150 + REDALERT/WIN32LIB/ICONSET.CPP | 341 + REDALERT/WIN32LIB/IFF.CPP | 325 + REDALERT/WIN32LIB/IFF.H | 164 + REDALERT/WIN32LIB/INDEXTB.INC | 1445 ++++ REDALERT/WIN32LIB/IRANDOM.CPP | 183 + REDALERT/WIN32LIB/KEYBOARD.H | 675 ++ REDALERT/WIN32LIB/KEYBOARD.INC | 126 + REDALERT/WIN32LIB/KEYSTRUC.INC | 159 + REDALERT/WIN32LIB/LCWCOMP.ASM | 288 + REDALERT/WIN32LIB/LCWUNCMP.ASM | 294 + REDALERT/WIN32LIB/LOAD.CPP | 375 + REDALERT/WIN32LIB/LOADFONT.CPP | 137 + REDALERT/WIN32LIB/MCGAPRIM.INC | 119 + REDALERT/WIN32LIB/MEMFLAG.H | 106 + REDALERT/WIN32LIB/MISC.H | 268 + REDALERT/WIN32LIB/MODEMREG.H | 69 + REDALERT/WIN32LIB/MONO.H | 182 + REDALERT/WIN32LIB/MOUSE.H | 123 + REDALERT/WIN32LIB/MOUSE.INC | 64 + REDALERT/WIN32LIB/MOUSEWW.CPP | 720 ++ REDALERT/WIN32LIB/NYBBTB.INC | 56 + REDALERT/WIN32LIB/PALETTE.CPP | 381 + REDALERT/WIN32LIB/PALETTE.H | 82 + REDALERT/WIN32LIB/PLAYCD.H | 306 + REDALERT/WIN32LIB/PROFILE.H | 102 + REDALERT/WIN32LIB/PROFILE.INC | 41 + REDALERT/WIN32LIB/RAWFILE.H | 254 + REDALERT/WIN32LIB/SET_FONT.CPP | 87 + REDALERT/WIN32LIB/SHAPE.H | 184 + REDALERT/WIN32LIB/SHAPE.INC | 212 + REDALERT/WIN32LIB/SOS.H | 562 ++ REDALERT/WIN32LIB/SOSCOMP.H | 80 + REDALERT/WIN32LIB/SOSDATA.H | 125 + REDALERT/WIN32LIB/SOSDEFS.H | 80 + REDALERT/WIN32LIB/SOSFNCT.H | 216 + REDALERT/WIN32LIB/SOSRES.H | 126 + REDALERT/WIN32LIB/SOUND.H | 52 + REDALERT/WIN32LIB/SOUNDINT.H | 289 + REDALERT/WIN32LIB/STAMP.INC | 56 + REDALERT/WIN32LIB/STRUCTS.H | 34 + REDALERT/WIN32LIB/SVGAPRIM.INC | 74 + REDALERT/WIN32LIB/TILE.H | 91 + REDALERT/WIN32LIB/TIMER.CPP | 200 + REDALERT/WIN32LIB/TIMER.H | 198 + REDALERT/WIN32LIB/TIMERDWN.CPP | 123 + REDALERT/WIN32LIB/TIMERINI.CPP | 314 + REDALERT/WIN32LIB/TOBUFF.ASM | 294 + REDALERT/WIN32LIB/VIDEO.H | 216 + REDALERT/WIN32LIB/WINCOMM.H | 454 ++ REDALERT/WIN32LIB/WINDOWS.CPP | 973 +++ REDALERT/WIN32LIB/WINHIDE.CPP | 94 + REDALERT/WIN32LIB/WRITEPCX.CPP | 177 + REDALERT/WIN32LIB/WSA.H | 160 + REDALERT/WIN32LIB/WWFILE.H | 68 + REDALERT/WIN32LIB/WWLIB32.H | 65 + REDALERT/WIN32LIB/WWMEM.H | 67 + REDALERT/WIN32LIB/WWMEM.INC | 40 + REDALERT/WIN32LIB/WW_WIN.H | 93 + REDALERT/WIN32LIB/_DIPTABL.CPP | 55 + REDALERT/WIN32LIB/wwstd.h | 342 + REDALERT/WINSTUB.CPP | 968 +++ REDALERT/WOLAPIOB.CPP | 3432 ++++++++ REDALERT/WOLAPIOB.H | 417 + REDALERT/WOLDEBUG.H | 32 + REDALERT/WOLEDIT.CPP | 164 + REDALERT/WOLEDIT.H | 40 + REDALERT/WOLSTRNG.CPP | 804 ++ REDALERT/WOLSTRNG.H | 209 + REDALERT/WOL_CGAM.CPP | 386 + REDALERT/WOL_CHAT.CPP | 1578 ++++ REDALERT/WOL_DNLD.CPP | 260 + REDALERT/WOL_GSUP.CPP | 3500 +++++++++ REDALERT/WOL_GSUP.H | 361 + REDALERT/WOL_LOGN.CPP | 662 ++ REDALERT/WOL_MAIN.CPP | 284 + REDALERT/WOL_OPT.CPP | 254 + REDALERT/WSNWLINK.H | 294 + REDALERT/WSPIPX.CPP | 446 ++ REDALERT/WSPIPX.H | 103 + REDALERT/WSPROTO.CPP | 591 ++ REDALERT/WSPROTO.H | 189 + REDALERT/WSPUDP.CPP | 428 + REDALERT/WSPUDP.H | 83 + REDALERT/WWALLOC.H | 65 + REDALERT/WWFILE.H | 93 + REDALERT/XPIPE.CPP | 163 + REDALERT/XPIPE.H | 92 + REDALERT/XSTRAW.CPP | 148 + REDALERT/XSTRAW.H | 88 + REDALERT/_WSPROTO.CPP | 37 + REDALERT/_WSPROTO.H | 42 + TIBERIANDAWN/AADATA.CPP | 795 ++ TIBERIANDAWN/ABSTRACT.CPP | 133 + TIBERIANDAWN/ABSTRACT.H | 127 + TIBERIANDAWN/ADATA.CPP | 2550 ++++++ TIBERIANDAWN/AIRCRAFT.CPP | 3539 +++++++++ TIBERIANDAWN/AIRCRAFT.H | 256 + TIBERIANDAWN/ALLOC.CPP | 590 ++ TIBERIANDAWN/ANIM.CPP | 1263 +++ TIBERIANDAWN/ANIM.H | 202 + TIBERIANDAWN/AUDIO.CPP | 636 ++ TIBERIANDAWN/AUDIO.H | 96 + TIBERIANDAWN/BASE.CPP | 521 ++ TIBERIANDAWN/BASE.H | 124 + TIBERIANDAWN/BBDATA.CPP | 636 ++ TIBERIANDAWN/BDATA.CPP | 4938 ++++++++++++ TIBERIANDAWN/BUILDING.CPP | 5429 +++++++++++++ TIBERIANDAWN/BUILDING.H | 307 + TIBERIANDAWN/BULLET.CPP | 795 ++ TIBERIANDAWN/BULLET.H | 155 + TIBERIANDAWN/CARGO.CPP | 180 + TIBERIANDAWN/CARGO.H | 88 + TIBERIANDAWN/CCDDE.CPP | 419 + TIBERIANDAWN/CCDDE.H | 86 + TIBERIANDAWN/CCFILE.CPP | 626 ++ TIBERIANDAWN/CCFILE.H | 111 + TIBERIANDAWN/CC_ICON.RC | 16 + TIBERIANDAWN/CDATA.CPP | 2770 +++++++ TIBERIANDAWN/CDFILE.CPP | 534 ++ TIBERIANDAWN/CDFILE.H | 117 + TIBERIANDAWN/CELL.CPP | 2735 +++++++ TIBERIANDAWN/CELL.H | 295 + TIBERIANDAWN/CHECKBOX.CPP | 69 + TIBERIANDAWN/CHECKBOX.H | 52 + TIBERIANDAWN/CHEKLIST.CPP | 165 + TIBERIANDAWN/CHEKLIST.H | 83 + TIBERIANDAWN/COLRLIST.CPP | 280 + TIBERIANDAWN/COLRLIST.H | 84 + TIBERIANDAWN/COMBAT.CPP | 232 + TIBERIANDAWN/COMBUF.CPP | 1036 +++ TIBERIANDAWN/COMBUF.H | 178 + TIBERIANDAWN/COMPAT.H | 170 + TIBERIANDAWN/COMQUEUE.CPP | 1009 +++ TIBERIANDAWN/COMQUEUE.H | 190 + TIBERIANDAWN/CONFDLG.CPP | 247 + TIBERIANDAWN/CONFDLG.H | 53 + TIBERIANDAWN/CONNECT.CPP | 248 + TIBERIANDAWN/CONNECT.H | 340 + TIBERIANDAWN/CONNMGR.H | 148 + TIBERIANDAWN/CONQUER.CPP | 4259 ++++++++++ TIBERIANDAWN/CONQUER.H | 772 ++ TIBERIANDAWN/CONST.CPP | 425 + TIBERIANDAWN/CONTROL.CPP | 199 + TIBERIANDAWN/CONTROL.H | 86 + TIBERIANDAWN/COORD.CPP | 599 ++ TIBERIANDAWN/COORDA.ASM | 131 + TIBERIANDAWN/COORDA.h | 141 + TIBERIANDAWN/CREDITS.CPP | 183 + TIBERIANDAWN/CREDITS.H | 71 + TIBERIANDAWN/CREW.CPP | 35 + TIBERIANDAWN/CREW.H | 62 + TIBERIANDAWN/DDE.CPP | 465 ++ TIBERIANDAWN/DDE.H | 176 + TIBERIANDAWN/DEBUG.CPP | 690 ++ TIBERIANDAWN/DEBUG.H | 39 + TIBERIANDAWN/DEFINES.H | 2961 +++++++ TIBERIANDAWN/DESCDLG.CPP | 181 + TIBERIANDAWN/DESCDLG.H | 66 + TIBERIANDAWN/DIAL8.CPP | 314 + TIBERIANDAWN/DIAL8.H | 73 + TIBERIANDAWN/DIALOG.CPP | 787 ++ TIBERIANDAWN/DISPLAY.CPP | 4407 +++++++++++ TIBERIANDAWN/DISPLAY.H | 341 + TIBERIANDAWN/DLLInterface.cpp | 7667 ++++++++++++++++++ TIBERIANDAWN/DLLInterface.h | 927 +++ TIBERIANDAWN/DLLInterfaceEditor.cpp | 741 ++ TIBERIANDAWN/DOOR.CPP | 199 + TIBERIANDAWN/DOOR.H | 91 + TIBERIANDAWN/DPMI.CPP | 172 + TIBERIANDAWN/DPMI.H | 172 + TIBERIANDAWN/DRIVE.CPP | 2284 ++++++ TIBERIANDAWN/DRIVE.H | 223 + TIBERIANDAWN/EDIT.CPP | 468 ++ TIBERIANDAWN/EDIT.H | 113 + TIBERIANDAWN/ENDING.CPP | 258 + TIBERIANDAWN/ENDING.H | 39 + TIBERIANDAWN/EVENT.CPP | 779 ++ TIBERIANDAWN/EVENT.H | 224 + TIBERIANDAWN/EXPAND.CPP | 420 + TIBERIANDAWN/EXTERNS.H | 407 + TIBERIANDAWN/FACING.CPP | 180 + TIBERIANDAWN/FACING.H | 76 + TIBERIANDAWN/FACTORY.CPP | 785 ++ TIBERIANDAWN/FACTORY.H | 161 + TIBERIANDAWN/FIELD.CPP | 213 + TIBERIANDAWN/FIELD.H | 82 + TIBERIANDAWN/FINDPATH.CPP | 1543 ++++ TIBERIANDAWN/FLASHER.CPP | 129 + TIBERIANDAWN/FLASHER.H | 81 + TIBERIANDAWN/FLY.CPP | 130 + TIBERIANDAWN/FLY.H | 80 + TIBERIANDAWN/FOOT.CPP | 2045 +++++ TIBERIANDAWN/FOOT.H | 304 + TIBERIANDAWN/FTIMER.H | 88 + TIBERIANDAWN/FUNCTION.H | 982 +++ TIBERIANDAWN/FUSE.CPP | 193 + TIBERIANDAWN/FUSE.H | 92 + TIBERIANDAWN/GADGET.CPP | 801 ++ TIBERIANDAWN/GADGET.H | 245 + TIBERIANDAWN/GAMEDLG.CPP | 419 + TIBERIANDAWN/GAMEDLG.H | 49 + TIBERIANDAWN/GAUGE.CPP | 536 ++ TIBERIANDAWN/GAUGE.H | 108 + TIBERIANDAWN/GLOBALS.CPP | 1052 +++ TIBERIANDAWN/GOPTIONS.CPP | 584 ++ TIBERIANDAWN/GOPTIONS.H | 97 + TIBERIANDAWN/GSCREEN.CPP | 528 ++ TIBERIANDAWN/GSCREEN.H | 144 + TIBERIANDAWN/HDATA.CPP | 322 + TIBERIANDAWN/HEAP.CPP | 556 ++ TIBERIANDAWN/HEAP.H | 349 + TIBERIANDAWN/HELP.CPP | 403 + TIBERIANDAWN/HELP.H | 142 + TIBERIANDAWN/HOUSE.CPP | 8637 +++++++++++++++++++++ TIBERIANDAWN/HOUSE.H | 899 +++ TIBERIANDAWN/IDATA.CPP | 2152 +++++ TIBERIANDAWN/INFANTRY.CPP | 3303 ++++++++ TIBERIANDAWN/INFANTRY.H | 251 + TIBERIANDAWN/INI.CPP | 2326 ++++++ TIBERIANDAWN/INIT.CPP | 3054 ++++++++ TIBERIANDAWN/INTERNET.CPP | 886 +++ TIBERIANDAWN/INTERPAL.CPP | 457 ++ TIBERIANDAWN/INTRO.CPP | 323 + TIBERIANDAWN/INTRO.H | 39 + TIBERIANDAWN/IOMAP.CPP | 1104 +++ TIBERIANDAWN/IOOBJ.CPP | 2698 +++++++ TIBERIANDAWN/IPX.CPP | 1108 +++ TIBERIANDAWN/IPX.H | 185 + TIBERIANDAWN/IPX95.CPP | 103 + TIBERIANDAWN/IPX95.H | 54 + TIBERIANDAWN/IPXADDR.CPP | 491 ++ TIBERIANDAWN/IPXADDR.H | 103 + TIBERIANDAWN/IPXCONN.CPP | 677 ++ TIBERIANDAWN/IPXCONN.H | 208 + TIBERIANDAWN/IPXGCONN.CPP | 470 ++ TIBERIANDAWN/IPXGCONN.H | 158 + TIBERIANDAWN/IPXMGR.CPP | 1969 +++++ TIBERIANDAWN/IPXMGR.H | 392 + TIBERIANDAWN/IPXPROT.ASM | 110 + TIBERIANDAWN/IPXREAL.ASM | 316 + TIBERIANDAWN/JSHELL.CPP | 434 ++ TIBERIANDAWN/JSHELL.H | 248 + TIBERIANDAWN/KEYFBUFF.ASM | 4015 ++++++++++ TIBERIANDAWN/KEYFBUFF.INC | 39 + TIBERIANDAWN/KEYFRAME.CPP | 599 ++ TIBERIANDAWN/LAYER.CPP | 161 + TIBERIANDAWN/LAYER.H | 64 + TIBERIANDAWN/LED.H | 43 + TIBERIANDAWN/LINK.CPP | 413 + TIBERIANDAWN/LINK.H | 78 + TIBERIANDAWN/LIST.CPP | 894 +++ TIBERIANDAWN/LIST.H | 146 + TIBERIANDAWN/LOADDLG.CPP | 735 ++ TIBERIANDAWN/LOADDLG.H | 89 + TIBERIANDAWN/LOGIC.CPP | 342 + TIBERIANDAWN/LOGIC.H | 57 + TIBERIANDAWN/License.txt | 712 ++ TIBERIANDAWN/MAP.CPP | 1577 ++++ TIBERIANDAWN/MAP.H | 174 + TIBERIANDAWN/MAPEDDLG.CPP | 3847 +++++++++ TIBERIANDAWN/MAPEDIT.CPP | 1923 +++++ TIBERIANDAWN/MAPEDIT.H | 352 + TIBERIANDAWN/MAPEDPLC.CPP | 1996 +++++ TIBERIANDAWN/MAPEDSEL.CPP | 604 ++ TIBERIANDAWN/MAPEDTM.CPP | 2074 +++++ TIBERIANDAWN/MAPSEL.CPP | 1274 +++ TIBERIANDAWN/MENUS.CPP | 938 +++ TIBERIANDAWN/MESSAGE.H | 44 + TIBERIANDAWN/MISSION.CPP | 474 ++ TIBERIANDAWN/MISSION.H | 133 + TIBERIANDAWN/MIXFILE.CPP | 522 ++ TIBERIANDAWN/MIXFILE.H | 84 + TIBERIANDAWN/MMX.ASM | 328 + TIBERIANDAWN/MONOC.CPP | 804 ++ TIBERIANDAWN/MONOC.H | 187 + TIBERIANDAWN/MOUSE.CPP | 357 + TIBERIANDAWN/MOUSE.H | 127 + TIBERIANDAWN/MPLAYER.CPP | 1417 ++++ TIBERIANDAWN/MSGBOX.CPP | 479 ++ TIBERIANDAWN/MSGBOX.H | 57 + TIBERIANDAWN/MSGLIST.CPP | 809 ++ TIBERIANDAWN/MSGLIST.H | 106 + TIBERIANDAWN/MiscAsm.cpp | 1578 ++++ TIBERIANDAWN/NETDLG.CPP | 5434 +++++++++++++ TIBERIANDAWN/NOSEQCON.CPP | 687 ++ TIBERIANDAWN/NOSEQCON.H | 126 + TIBERIANDAWN/NULLCONN.CPP | 267 + TIBERIANDAWN/NULLCONN.H | 137 + TIBERIANDAWN/NULLDLG.CPP | 8360 ++++++++++++++++++++ TIBERIANDAWN/NULLMGR.CPP | 2350 ++++++ TIBERIANDAWN/NULLMGR.H | 214 + TIBERIANDAWN/OBJECT.CPP | 1706 ++++ TIBERIANDAWN/OBJECT.H | 261 + TIBERIANDAWN/ODATA.CPP | 922 +++ TIBERIANDAWN/OPTIONS.CPP | 797 ++ TIBERIANDAWN/OPTIONS.H | 101 + TIBERIANDAWN/OVERLAY.CPP | 428 + TIBERIANDAWN/OVERLAY.H | 109 + TIBERIANDAWN/PACKET.CPP | 461 ++ TIBERIANDAWN/PACKET.H | 98 + TIBERIANDAWN/PAGFAULT.ASM | 222 + TIBERIANDAWN/PHONE.H | 66 + TIBERIANDAWN/POWER.CPP | 459 ++ TIBERIANDAWN/POWER.H | 123 + TIBERIANDAWN/PROFILE.CPP | 654 ++ TIBERIANDAWN/QUEUE.CPP | 4214 ++++++++++ TIBERIANDAWN/QUEUE.H | 272 + TIBERIANDAWN/RADAR.CPP | 1961 +++++ TIBERIANDAWN/RADAR.H | 211 + TIBERIANDAWN/RADIO.CPP | 249 + TIBERIANDAWN/RADIO.H | 105 + TIBERIANDAWN/RAND.CPP | 170 + TIBERIANDAWN/RAWFILE.CPP | 1346 ++++ TIBERIANDAWN/RAWFILE.H | 332 + TIBERIANDAWN/REAL.H | 937 +++ TIBERIANDAWN/REGION.H | 56 + TIBERIANDAWN/REG_ICON.RC | 16 + TIBERIANDAWN/REINF.CPP | 663 ++ TIBERIANDAWN/RESOURCE/TiberianDawn.rc | 102 + TIBERIANDAWN/RESOURCE/resource.h | 29 + TIBERIANDAWN/RULES.CPP | 126 + TIBERIANDAWN/RULES.H | 275 + TIBERIANDAWN/SAVEDLG.H | 68 + TIBERIANDAWN/SAVELOAD.CPP | 1614 ++++ TIBERIANDAWN/SCENARIO.CPP | 789 ++ TIBERIANDAWN/SCORE.CPP | 2322 ++++++ TIBERIANDAWN/SCORE.H | 156 + TIBERIANDAWN/SCREEN.H | 65 + TIBERIANDAWN/SCROLL.CPP | 254 + TIBERIANDAWN/SCROLL.H | 83 + TIBERIANDAWN/SDATA.CPP | 470 ++ TIBERIANDAWN/SEQCONN.CPP | 561 ++ TIBERIANDAWN/SEQCONN.H | 108 + TIBERIANDAWN/SESSION.H | 551 ++ TIBERIANDAWN/SHAPEBTN.CPP | 163 + TIBERIANDAWN/SHAPEBTN.H | 64 + TIBERIANDAWN/SIDEBAR.CPP | 2559 ++++++ TIBERIANDAWN/SIDEBAR.H | 395 + TIBERIANDAWN/SIDEBARGlyphx.CPP | 833 ++ TIBERIANDAWN/SIDEBARGlyphx.H | 206 + TIBERIANDAWN/SLIDER.CPP | 405 + TIBERIANDAWN/SLIDER.H | 107 + TIBERIANDAWN/SMUDGE.CPP | 405 + TIBERIANDAWN/SMUDGE.H | 105 + TIBERIANDAWN/SOUNDDLG.CPP | 431 + TIBERIANDAWN/SOUNDDLG.H | 156 + TIBERIANDAWN/SPECIAL.CPP | 271 + TIBERIANDAWN/SPECIAL.H | 265 + TIBERIANDAWN/STAGE.H | 97 + TIBERIANDAWN/STARTUP.CPP | 872 +++ TIBERIANDAWN/STATS.CPP | 676 ++ TIBERIANDAWN/SUPER.CPP | 383 + TIBERIANDAWN/SUPER.H | 87 + TIBERIANDAWN/SUPPORT.ASM | 456 ++ TIBERIANDAWN/Shape.cpp | 111 + TIBERIANDAWN/TAB.CPP | 261 + TIBERIANDAWN/TAB.H | 79 + TIBERIANDAWN/TARCOM.CPP | 180 + TIBERIANDAWN/TARCOM.H | 75 + TIBERIANDAWN/TARGET.CPP | 529 ++ TIBERIANDAWN/TARGET.H | 154 + TIBERIANDAWN/TCPIP.CPP | 1002 +++ TIBERIANDAWN/TCPIP.H | 222 + TIBERIANDAWN/TDATA.CPP | 1026 +++ TIBERIANDAWN/TEAM.CPP | 1509 ++++ TIBERIANDAWN/TEAM.H | 255 + TIBERIANDAWN/TEAMTYPE.CPP | 977 +++ TIBERIANDAWN/TEAMTYPE.H | 261 + TIBERIANDAWN/TECHNO.CPP | 4763 ++++++++++++ TIBERIANDAWN/TECHNO.H | 372 + TIBERIANDAWN/TEMPLATE.CPP | 408 + TIBERIANDAWN/TEMPLATE.H | 122 + TIBERIANDAWN/TERRAIN.CPP | 897 +++ TIBERIANDAWN/TERRAIN.H | 179 + TIBERIANDAWN/TEXTBLIT.H | 51 + TIBERIANDAWN/TEXTBTN.CPP | 374 + TIBERIANDAWN/TEXTBTN.H | 71 + TIBERIANDAWN/THEME.CPP | 560 ++ TIBERIANDAWN/THEME.H | 84 + TIBERIANDAWN/TOGGLE.CPP | 196 + TIBERIANDAWN/TOGGLE.H | 79 + TIBERIANDAWN/TRIGGER.CPP | 1483 ++++ TIBERIANDAWN/TRIGGER.H | 261 + TIBERIANDAWN/TURRET.CPP | 508 ++ TIBERIANDAWN/TURRET.H | 82 + TIBERIANDAWN/TXTLABEL.CPP | 95 + TIBERIANDAWN/TXTLABEL.H | 69 + TIBERIANDAWN/TXTPRNT.ASM | 520 ++ TIBERIANDAWN/TYPE.H | 2010 +++++ TIBERIANDAWN/TiberianDawn.vcxproj | 566 ++ TIBERIANDAWN/TiberianDawn.vcxproj.filters | 1235 +++ TIBERIANDAWN/UDATA.CPP | 1974 +++++ TIBERIANDAWN/UNIT.CPP | 4255 ++++++++++ TIBERIANDAWN/UNIT.H | 212 + TIBERIANDAWN/UTRACKER.CPP | 239 + TIBERIANDAWN/UTRACKER.H | 83 + TIBERIANDAWN/VECTOR.CPP | 898 +++ TIBERIANDAWN/VECTOR.H | 988 +++ TIBERIANDAWN/VISUDLG.CPP | 421 + TIBERIANDAWN/VISUDLG.H | 89 + TIBERIANDAWN/WATCOM.H | 74 + TIBERIANDAWN/WIN32LIB/ALLOC.CPP | 509 ++ TIBERIANDAWN/WIN32LIB/AUDIO.H | 158 + TIBERIANDAWN/WIN32LIB/BUFFER.CPP | 128 + TIBERIANDAWN/WIN32LIB/BUFFER.H | 121 + TIBERIANDAWN/WIN32LIB/BUFFGLBL.CPP | 80 + TIBERIANDAWN/WIN32LIB/DDRAW.CPP | 1002 +++ TIBERIANDAWN/WIN32LIB/DDRAW.H | 3117 ++++++++ TIBERIANDAWN/WIN32LIB/DEFINES.H | 36 + TIBERIANDAWN/WIN32LIB/DESCMGMT.H | 90 + TIBERIANDAWN/WIN32LIB/DIFFTB.INC | 1445 ++++ TIBERIANDAWN/WIN32LIB/DIPTHONG.CPP | 325 + TIBERIANDAWN/WIN32LIB/DIPTHONG.H | 21 + TIBERIANDAWN/WIN32LIB/DPLAY.H | 323 + TIBERIANDAWN/WIN32LIB/DRAWBUFF.H | 64 + TIBERIANDAWN/WIN32LIB/DRAWBUFF.INC | 90 + TIBERIANDAWN/WIN32LIB/DRAWRECT.CPP | 69 + TIBERIANDAWN/WIN32LIB/DSETUP.H | 71 + TIBERIANDAWN/WIN32LIB/DSOUND.H | 380 + TIBERIANDAWN/WIN32LIB/DrawMisc.cpp | 5508 +++++++++++++ TIBERIANDAWN/WIN32LIB/EXTERNS.H | 37 + TIBERIANDAWN/WIN32LIB/FACINGFF.ASM | 162 + TIBERIANDAWN/WIN32LIB/FACINGFF.h | 458 ++ TIBERIANDAWN/WIN32LIB/FASTFILE.H | 39 + TIBERIANDAWN/WIN32LIB/FILE.H | 257 + TIBERIANDAWN/WIN32LIB/FILEPCX.H | 74 + TIBERIANDAWN/WIN32LIB/FILETEMP.H | 58 + TIBERIANDAWN/WIN32LIB/FONT.CPP | 138 + TIBERIANDAWN/WIN32LIB/FONT.H | 115 + TIBERIANDAWN/WIN32LIB/FUNCTION.H | 44 + TIBERIANDAWN/WIN32LIB/GBUFFER.CPP | 707 ++ TIBERIANDAWN/WIN32LIB/GBUFFER.H | 1372 ++++ TIBERIANDAWN/WIN32LIB/GBUFFER.INC | 48 + TIBERIANDAWN/WIN32LIB/GETSHAPE.CPP | 358 + TIBERIANDAWN/WIN32LIB/ICONCACH.H | 150 + TIBERIANDAWN/WIN32LIB/ICONSET.CPP | 340 + TIBERIANDAWN/WIN32LIB/IFF.CPP | 325 + TIBERIANDAWN/WIN32LIB/IFF.H | 164 + TIBERIANDAWN/WIN32LIB/INDEXTB.INC | 1445 ++++ TIBERIANDAWN/WIN32LIB/IRANDOM.CPP | 176 + TIBERIANDAWN/WIN32LIB/KEYBOARD.CPP | 533 ++ TIBERIANDAWN/WIN32LIB/KEYBOARD.H | 675 ++ TIBERIANDAWN/WIN32LIB/KEYBOARD.INC | 126 + TIBERIANDAWN/WIN32LIB/KEYSTRUC.INC | 159 + TIBERIANDAWN/WIN32LIB/LOAD.CPP | 375 + TIBERIANDAWN/WIN32LIB/LOADFONT.CPP | 137 + TIBERIANDAWN/WIN32LIB/LOADPAL.CPP | 82 + TIBERIANDAWN/WIN32LIB/MCGAPRIM.INC | 119 + TIBERIANDAWN/WIN32LIB/MEM.CPP | 1086 +++ TIBERIANDAWN/WIN32LIB/MEMFLAG.H | 107 + TIBERIANDAWN/WIN32LIB/MISC.H | 268 + TIBERIANDAWN/WIN32LIB/MODEMREG.H | 69 + TIBERIANDAWN/WIN32LIB/MONO.H | 181 + TIBERIANDAWN/WIN32LIB/MORPHPAL.CPP | 173 + TIBERIANDAWN/WIN32LIB/MOUSE.H | 123 + TIBERIANDAWN/WIN32LIB/MOUSE.INC | 64 + TIBERIANDAWN/WIN32LIB/MOUSEWW.CPP | 720 ++ TIBERIANDAWN/WIN32LIB/NEWDEL.CPP | 125 + TIBERIANDAWN/WIN32LIB/NYBBTB.INC | 56 + TIBERIANDAWN/WIN32LIB/PALETTE.CPP | 381 + TIBERIANDAWN/WIN32LIB/PALETTE.H | 82 + TIBERIANDAWN/WIN32LIB/PLAYCD.H | 306 + TIBERIANDAWN/WIN32LIB/PROFILE.H | 102 + TIBERIANDAWN/WIN32LIB/PROFILE.INC | 41 + TIBERIANDAWN/WIN32LIB/RAWFILE.H | 254 + TIBERIANDAWN/WIN32LIB/REGIONSZ.CPP | 57 + TIBERIANDAWN/WIN32LIB/REMAP.ASM | 172 + TIBERIANDAWN/WIN32LIB/SET_FONT.CPP | 87 + TIBERIANDAWN/WIN32LIB/SHAPE.H | 187 + TIBERIANDAWN/WIN32LIB/SHAPE.INC | 212 + TIBERIANDAWN/WIN32LIB/SOUND.H | 52 + TIBERIANDAWN/WIN32LIB/SOUNDINT.H | 289 + TIBERIANDAWN/WIN32LIB/STAMP.INC | 56 + TIBERIANDAWN/WIN32LIB/STRUCTS.H | 34 + TIBERIANDAWN/WIN32LIB/SVGAPRIM.INC | 74 + TIBERIANDAWN/WIN32LIB/TILE.H | 100 + TIBERIANDAWN/WIN32LIB/TIMER.CPP | 200 + TIBERIANDAWN/WIN32LIB/TIMER.H | 198 + TIBERIANDAWN/WIN32LIB/TIMERDWN.CPP | 123 + TIBERIANDAWN/WIN32LIB/TIMERINI.CPP | 312 + TIBERIANDAWN/WIN32LIB/TOBUFF.ASM | 294 + TIBERIANDAWN/WIN32LIB/VIDEO.H | 216 + TIBERIANDAWN/WIN32LIB/WINCOMM.H | 454 ++ TIBERIANDAWN/WIN32LIB/WINDOWS.CPP | 973 +++ TIBERIANDAWN/WIN32LIB/WINHIDE.CPP | 94 + TIBERIANDAWN/WIN32LIB/WRITEPCX.CPP | 177 + TIBERIANDAWN/WIN32LIB/WSA.CPP | 1153 +++ TIBERIANDAWN/WIN32LIB/WSA.H | 160 + TIBERIANDAWN/WIN32LIB/WWFILE.H | 68 + TIBERIANDAWN/WIN32LIB/WWLIB32.H | 65 + TIBERIANDAWN/WIN32LIB/WWMEM.H | 67 + TIBERIANDAWN/WIN32LIB/WWMEM.INC | 40 + TIBERIANDAWN/WIN32LIB/WWSTD.H | 224 + TIBERIANDAWN/WIN32LIB/WW_WIN.H | 93 + TIBERIANDAWN/WIN32LIB/XORDELTA.ASM | 668 ++ TIBERIANDAWN/WIN32LIB/_DIPTABL.CPP | 55 + TIBERIANDAWN/WIN32LIB/_FILE.H | 180 + TIBERIANDAWN/WINASM.ASM | 887 +++ TIBERIANDAWN/WINSTUB.CPP | 938 +++ TIBERIANDAWN/WWALLOC.H | 65 + TIBERIANDAWN/WWFILE.H | 75 + 1038 files changed, 629779 insertions(+) create mode 100644 CnCRemastered.sln create mode 100644 REDALERT/2KEYFRAM.CPP create mode 100644 REDALERT/2SUPPORT.ASM create mode 100644 REDALERT/2TXTPRNT.ASM create mode 100644 REDALERT/AADATA.CPP create mode 100644 REDALERT/ABSTRACT.CPP create mode 100644 REDALERT/ABSTRACT.H create mode 100644 REDALERT/ADATA.CPP create mode 100644 REDALERT/ADPCM.CPP create mode 100644 REDALERT/AIRCRAFT.CPP create mode 100644 REDALERT/AIRCRAFT.H create mode 100644 REDALERT/ANIM.CPP create mode 100644 REDALERT/ANIM.H create mode 100644 REDALERT/AUDIO.CPP create mode 100644 REDALERT/AUDIO.H create mode 100644 REDALERT/B64PIPE.CPP create mode 100644 REDALERT/B64PIPE.H create mode 100644 REDALERT/B64STRAW.CPP create mode 100644 REDALERT/B64STRAW.H create mode 100644 REDALERT/BAR.CPP create mode 100644 REDALERT/BAR.H create mode 100644 REDALERT/BASE.CPP create mode 100644 REDALERT/BASE.H create mode 100644 REDALERT/BASE64.CPP create mode 100644 REDALERT/BASE64.H create mode 100644 REDALERT/BBDATA.CPP create mode 100644 REDALERT/BDATA.CPP create mode 100644 REDALERT/BENCH.CPP create mode 100644 REDALERT/BENCH.H create mode 100644 REDALERT/BFIOFILE.CPP create mode 100644 REDALERT/BFIOFILE.H create mode 100644 REDALERT/BIGCHECK.CPP create mode 100644 REDALERT/BIGCHECK.H create mode 100644 REDALERT/BLOWFISH.CPP create mode 100644 REDALERT/BLOWFISH.H create mode 100644 REDALERT/BLOWPIPE.CPP create mode 100644 REDALERT/BLOWPIPE.H create mode 100644 REDALERT/BLWSTRAW.CPP create mode 100644 REDALERT/BLWSTRAW.H create mode 100644 REDALERT/BMP8.CPP create mode 100644 REDALERT/BMP8.H create mode 100644 REDALERT/BUFF.CPP create mode 100644 REDALERT/BUFF.H create mode 100644 REDALERT/BUFFERX.H create mode 100644 REDALERT/BUILDING.CPP create mode 100644 REDALERT/BUILDING.H create mode 100644 REDALERT/BULLET.CPP create mode 100644 REDALERT/BULLET.H create mode 100644 REDALERT/CARGO.CPP create mode 100644 REDALERT/CARGO.H create mode 100644 REDALERT/CARRY.CPP create mode 100644 REDALERT/CARRY.H create mode 100644 REDALERT/CBN_.H create mode 100644 REDALERT/CCDDE.CPP create mode 100644 REDALERT/CCDDE.H create mode 100644 REDALERT/CCFILE.CPP create mode 100644 REDALERT/CCFILE.H create mode 100644 REDALERT/CCINI.CPP create mode 100644 REDALERT/CCINI.H create mode 100644 REDALERT/CCMPATH.CPP create mode 100644 REDALERT/CCPTR.CPP create mode 100644 REDALERT/CCPTR.H create mode 100644 REDALERT/CCTEN.CPP create mode 100644 REDALERT/CDATA.CPP create mode 100644 REDALERT/CDFILE.CPP create mode 100644 REDALERT/CDFILE.H create mode 100644 REDALERT/CELL.CPP create mode 100644 REDALERT/CELL.H create mode 100644 REDALERT/CHECKBOX.CPP create mode 100644 REDALERT/CHECKBOX.H create mode 100644 REDALERT/CHEKLIST.CPP create mode 100644 REDALERT/CHEKLIST.H create mode 100644 REDALERT/CLASS.CPP create mode 100644 REDALERT/COLRLIST.CPP create mode 100644 REDALERT/COLRLIST.H create mode 100644 REDALERT/COMBAT.CPP create mode 100644 REDALERT/COMBUF.CPP create mode 100644 REDALERT/COMBUF.H create mode 100644 REDALERT/COMINIT.CPP create mode 100644 REDALERT/COMINIT.H create mode 100644 REDALERT/COMPAT.H create mode 100644 REDALERT/COMQUEUE.CPP create mode 100644 REDALERT/COMQUEUE.H create mode 100644 REDALERT/CONFDLG.CPP create mode 100644 REDALERT/CONFDLG.H create mode 100644 REDALERT/CONNECT.CPP create mode 100644 REDALERT/CONNECT.H create mode 100644 REDALERT/CONNMGR.H create mode 100644 REDALERT/CONQUER.CPP create mode 100644 REDALERT/CONQUER.H create mode 100644 REDALERT/CONST.CPP create mode 100644 REDALERT/CONTROL.CPP create mode 100644 REDALERT/CONTROL.H create mode 100644 REDALERT/COORD.CPP create mode 100644 REDALERT/COORDA.ASM create mode 100644 REDALERT/COORDA.h create mode 100644 REDALERT/CPUID.ASM create mode 100644 REDALERT/CRATE.CPP create mode 100644 REDALERT/CRATE.H create mode 100644 REDALERT/CRC.CPP create mode 100644 REDALERT/CRC.H create mode 100644 REDALERT/CRCPIPE.CPP create mode 100644 REDALERT/CRCPIPE.H create mode 100644 REDALERT/CRCSTRAW.CPP create mode 100644 REDALERT/CRCSTRAW.H create mode 100644 REDALERT/CREDITS.CPP create mode 100644 REDALERT/CREDITS.H create mode 100644 REDALERT/CREW.CPP create mode 100644 REDALERT/CREW.H create mode 100644 REDALERT/CSTRAW.CPP create mode 100644 REDALERT/CSTRAW.H create mode 100644 REDALERT/DDE.CPP create mode 100644 REDALERT/DDE.H create mode 100644 REDALERT/DEBUG.CPP create mode 100644 REDALERT/DEBUG.H create mode 100644 REDALERT/DEFINES.H create mode 100644 REDALERT/DESCDLG.CPP create mode 100644 REDALERT/DESCDLG.H create mode 100644 REDALERT/DIAL8.CPP create mode 100644 REDALERT/DIAL8.H create mode 100644 REDALERT/DIALOG.CPP create mode 100644 REDALERT/DIBAPI.H create mode 100644 REDALERT/DIBFILE.CPP create mode 100644 REDALERT/DIBUTIL.CPP create mode 100644 REDALERT/DIBUTIL.H create mode 100644 REDALERT/DISPLAY.CPP create mode 100644 REDALERT/DISPLAY.H create mode 100644 REDALERT/DLLInterface.cpp create mode 100644 REDALERT/DLLInterface.h create mode 100644 REDALERT/DLLInterfaceEditor.cpp create mode 100644 REDALERT/DOOR.CPP create mode 100644 REDALERT/DOOR.H create mode 100644 REDALERT/DPMI.CPP create mode 100644 REDALERT/DPMI.H create mode 100644 REDALERT/DRIVE.CPP create mode 100644 REDALERT/DRIVE.H create mode 100644 REDALERT/DROP.CPP create mode 100644 REDALERT/DROP.H create mode 100644 REDALERT/DTABLE.CPP create mode 100644 REDALERT/DYNAVEC.CPP create mode 100644 REDALERT/EDIT.CPP create mode 100644 REDALERT/EDIT.H create mode 100644 REDALERT/EGOS.CPP create mode 100644 REDALERT/EGOS.H create mode 100644 REDALERT/ENDING.CPP create mode 100644 REDALERT/ENDING.H create mode 100644 REDALERT/EVENT.CPP create mode 100644 REDALERT/EVENT.H create mode 100644 REDALERT/EXPAND.CPP create mode 100644 REDALERT/EXTERNS.H create mode 100644 REDALERT/FACE.CPP create mode 100644 REDALERT/FACE.H create mode 100644 REDALERT/FACING.CPP create mode 100644 REDALERT/FACING.H create mode 100644 REDALERT/FACTORY.CPP create mode 100644 REDALERT/FACTORY.H create mode 100644 REDALERT/FAKESOCK.H create mode 100644 REDALERT/FIELD.CPP create mode 100644 REDALERT/FIELD.H create mode 100644 REDALERT/FILEPCX.H create mode 100644 REDALERT/FINDPATH.CPP create mode 100644 REDALERT/FIXED.CPP create mode 100644 REDALERT/FIXED.H create mode 100644 REDALERT/FLASHER.CPP create mode 100644 REDALERT/FLASHER.H create mode 100644 REDALERT/FLY.CPP create mode 100644 REDALERT/FLY.H create mode 100644 REDALERT/FOOT.CPP create mode 100644 REDALERT/FOOT.H create mode 100644 REDALERT/FTIMER.H create mode 100644 REDALERT/FUNCTION.H create mode 100644 REDALERT/FUSE.CPP create mode 100644 REDALERT/FUSE.H create mode 100644 REDALERT/GADGET.CPP create mode 100644 REDALERT/GADGET.H create mode 100644 REDALERT/GAMEDLG.CPP create mode 100644 REDALERT/GAMEDLG.H create mode 100644 REDALERT/GAUGE.CPP create mode 100644 REDALERT/GAUGE.H create mode 100644 REDALERT/GETCPU.CPP create mode 100644 REDALERT/GLOBALS.CPP create mode 100644 REDALERT/GOPTIONS.CPP create mode 100644 REDALERT/GOPTIONS.H create mode 100644 REDALERT/GSCREEN.CPP create mode 100644 REDALERT/GSCREEN.H create mode 100644 REDALERT/HDATA.CPP create mode 100644 REDALERT/HEAP.CPP create mode 100644 REDALERT/HEAP.H create mode 100644 REDALERT/HELP.CPP create mode 100644 REDALERT/HELP.H create mode 100644 REDALERT/HOUSE.CPP create mode 100644 REDALERT/HOUSE.H create mode 100644 REDALERT/HSV.CPP create mode 100644 REDALERT/HSV.H create mode 100644 REDALERT/ICONLIST.CPP create mode 100644 REDALERT/ICONLIST.H create mode 100644 REDALERT/IDATA.CPP create mode 100644 REDALERT/INFANTRY.CPP create mode 100644 REDALERT/INFANTRY.H create mode 100644 REDALERT/INI.CPP create mode 100644 REDALERT/INI.H create mode 100644 REDALERT/INIBIN.CPP create mode 100644 REDALERT/INICODE.CPP create mode 100644 REDALERT/INIT.CPP create mode 100644 REDALERT/INLINE.H create mode 100644 REDALERT/INT.CPP create mode 100644 REDALERT/INT.H create mode 100644 REDALERT/INTERNET.CPP create mode 100644 REDALERT/INTERNET.H create mode 100644 REDALERT/INTERPAL.CPP create mode 100644 REDALERT/INTRO.CPP create mode 100644 REDALERT/INTRO.H create mode 100644 REDALERT/IOMAP.CPP create mode 100644 REDALERT/IOOBJ.CPP create mode 100644 REDALERT/IPX.CPP create mode 100644 REDALERT/IPX.H create mode 100644 REDALERT/IPX95.CPP create mode 100644 REDALERT/IPX95.H create mode 100644 REDALERT/IPXADDR.CPP create mode 100644 REDALERT/IPXADDR.H create mode 100644 REDALERT/IPXCONN.CPP create mode 100644 REDALERT/IPXCONN.H create mode 100644 REDALERT/IPXGCONN.CPP create mode 100644 REDALERT/IPXGCONN.H create mode 100644 REDALERT/IPXMGR.CPP create mode 100644 REDALERT/IPXMGR.H create mode 100644 REDALERT/IPXPROT.ASM create mode 100644 REDALERT/IPXREAL.ASM create mode 100644 REDALERT/ITABLE.CPP create mode 100644 REDALERT/JSHELL.CPP create mode 100644 REDALERT/JSHELL.H create mode 100644 REDALERT/KEY.CPP create mode 100644 REDALERT/KEY.H create mode 100644 REDALERT/KEYBOARD.CPP create mode 100644 REDALERT/KEYBOARD.H create mode 100644 REDALERT/KEYFBUFF.ASM create mode 100644 REDALERT/LANGUAGE.H create mode 100644 REDALERT/LAYER.CPP create mode 100644 REDALERT/LAYER.H create mode 100644 REDALERT/LCW.CPP create mode 100644 REDALERT/LCW.H create mode 100644 REDALERT/LCWCOMP.ASM create mode 100644 REDALERT/LCWPIPE.CPP create mode 100644 REDALERT/LCWPIPE.H create mode 100644 REDALERT/LCWSTRAW.CPP create mode 100644 REDALERT/LCWSTRAW.H create mode 100644 REDALERT/LCWUNCMP.CPP create mode 100644 REDALERT/LED.H create mode 100644 REDALERT/LINK.CPP create mode 100644 REDALERT/LINK.H create mode 100644 REDALERT/LINT.H create mode 100644 REDALERT/LIST.CPP create mode 100644 REDALERT/LIST.H create mode 100644 REDALERT/LISTNODE.H create mode 100644 REDALERT/LOADDLG.CPP create mode 100644 REDALERT/LOADDLG.H create mode 100644 REDALERT/LOGIC.CPP create mode 100644 REDALERT/LOGIC.H create mode 100644 REDALERT/LZO.H create mode 100644 REDALERT/LZO1X.H create mode 100644 REDALERT/LZO1X_C.CPP create mode 100644 REDALERT/LZO1X_D.CPP create mode 100644 REDALERT/LZOCONF.H create mode 100644 REDALERT/LZOPIPE.CPP create mode 100644 REDALERT/LZOPIPE.H create mode 100644 REDALERT/LZOSTRAW.CPP create mode 100644 REDALERT/LZOSTRAW.H create mode 100644 REDALERT/LZO_CONF.H create mode 100644 REDALERT/LZW.CPP create mode 100644 REDALERT/LZW.H create mode 100644 REDALERT/LZWOTRAW.CPP create mode 100644 REDALERT/LZWPIPE.CPP create mode 100644 REDALERT/LZWPIPE.H create mode 100644 REDALERT/LZWSTRAW.CPP create mode 100644 REDALERT/LZWSTRAW.H create mode 100644 REDALERT/License.txt create mode 100644 REDALERT/MAP.CPP create mode 100644 REDALERT/MAP.H create mode 100644 REDALERT/MAPEDDLG.CPP create mode 100644 REDALERT/MAPEDIT.CPP create mode 100644 REDALERT/MAPEDIT.H create mode 100644 REDALERT/MAPEDPLC.CPP create mode 100644 REDALERT/MAPEDSEL.CPP create mode 100644 REDALERT/MAPEDTM.CPP create mode 100644 REDALERT/MAPSEL.CPP create mode 100644 REDALERT/MCI.CPP create mode 100644 REDALERT/MCI.H create mode 100644 REDALERT/MCIMOVIE.CPP create mode 100644 REDALERT/MCIMOVIE.H create mode 100644 REDALERT/MEMCHECK.H create mode 100644 REDALERT/MENUS.CPP create mode 100644 REDALERT/MESSAGE.H create mode 100644 REDALERT/MISSION.CPP create mode 100644 REDALERT/MISSION.H create mode 100644 REDALERT/MIXFILE.CPP create mode 100644 REDALERT/MIXFILE.H create mode 100644 REDALERT/MMX.ASM create mode 100644 REDALERT/MONOC.CPP create mode 100644 REDALERT/MONOC.H create mode 100644 REDALERT/MOUSE.CPP create mode 100644 REDALERT/MOUSE.H create mode 100644 REDALERT/MOVIE.H create mode 100644 REDALERT/MP.CPP create mode 100644 REDALERT/MP.H create mode 100644 REDALERT/MPGSET.CPP create mode 100644 REDALERT/MPGSET.H create mode 100644 REDALERT/MPLAYER.CPP create mode 100644 REDALERT/MPLIB.CPP create mode 100644 REDALERT/MPMGRD.CPP create mode 100644 REDALERT/MPMGRD.H create mode 100644 REDALERT/MPMGRW.CPP create mode 100644 REDALERT/MPMGRW.H create mode 100644 REDALERT/MPU.CPP create mode 100644 REDALERT/MPU.H create mode 100644 REDALERT/MSGBOX.CPP create mode 100644 REDALERT/MSGBOX.H create mode 100644 REDALERT/MSGLIST.CPP create mode 100644 REDALERT/MSGLIST.H create mode 100644 REDALERT/MiscAsm.cpp create mode 100644 REDALERT/NETDLG.CPP create mode 100644 REDALERT/NOSEQCON.CPP create mode 100644 REDALERT/NOSEQCON.H create mode 100644 REDALERT/NULLCONN.CPP create mode 100644 REDALERT/NULLCONN.H create mode 100644 REDALERT/NULLDLG.CPP create mode 100644 REDALERT/NULLMGR.CPP create mode 100644 REDALERT/NULLMGR.H create mode 100644 REDALERT/OBJECT.CPP create mode 100644 REDALERT/OBJECT.H create mode 100644 REDALERT/OCIDL.H create mode 100644 REDALERT/ODATA.CPP create mode 100644 REDALERT/OPTIONS.CPP create mode 100644 REDALERT/OPTIONS.H create mode 100644 REDALERT/OVERLAY.CPP create mode 100644 REDALERT/OVERLAY.H create mode 100644 REDALERT/PACKET.CPP create mode 100644 REDALERT/PACKET.H create mode 100644 REDALERT/PALETTEC.CPP create mode 100644 REDALERT/PALETTEC.H create mode 100644 REDALERT/PIPE.CPP create mode 100644 REDALERT/PIPE.H create mode 100644 REDALERT/PK.CPP create mode 100644 REDALERT/PK.H create mode 100644 REDALERT/PKPIPE.CPP create mode 100644 REDALERT/PKPIPE.H create mode 100644 REDALERT/PKSTRAW.CPP create mode 100644 REDALERT/PKSTRAW.H create mode 100644 REDALERT/POWER.CPP create mode 100644 REDALERT/POWER.H create mode 100644 REDALERT/PROFILE.CPP create mode 100644 REDALERT/QUEUE.CPP create mode 100644 REDALERT/QUEUE.H create mode 100644 REDALERT/RADAR.CPP create mode 100644 REDALERT/RADAR.H create mode 100644 REDALERT/RADIO.CPP create mode 100644 REDALERT/RADIO.H create mode 100644 REDALERT/RAMFILE.CPP create mode 100644 REDALERT/RAMFILE.H create mode 100644 REDALERT/RAND.CPP create mode 100644 REDALERT/RANDOM.CPP create mode 100644 REDALERT/RANDOM.H create mode 100644 REDALERT/RAWFILE.CPP create mode 100644 REDALERT/RAWFILE.H create mode 100644 REDALERT/RAWOLAPI.CPP create mode 100644 REDALERT/RAWOLAPI.H create mode 100644 REDALERT/READLINE.CPP create mode 100644 REDALERT/READLINE.H create mode 100644 REDALERT/RECT.CPP create mode 100644 REDALERT/RECT.H create mode 100644 REDALERT/REGION.H create mode 100644 REDALERT/REINF.CPP create mode 100644 REDALERT/RESOURCE/RedAlert.rc create mode 100644 REDALERT/RESOURCE/resource.h create mode 100644 REDALERT/RGB.CPP create mode 100644 REDALERT/RGB.H create mode 100644 REDALERT/RNDSTRAW.CPP create mode 100644 REDALERT/RNDSTRAW.H create mode 100644 REDALERT/RNG.H create mode 100644 REDALERT/ROTBMP.CPP create mode 100644 REDALERT/ROTBMP.H create mode 100644 REDALERT/RULES.CPP create mode 100644 REDALERT/RULES.H create mode 100644 REDALERT/RedAlert.vcxproj create mode 100644 REDALERT/RedAlert.vcxproj.filters create mode 100644 REDALERT/SAVEDLG.H create mode 100644 REDALERT/SAVELOAD.CPP create mode 100644 REDALERT/SCENARIO.CPP create mode 100644 REDALERT/SCENARIO.H create mode 100644 REDALERT/SCORE.CPP create mode 100644 REDALERT/SCORE.H create mode 100644 REDALERT/SCREEN.H create mode 100644 REDALERT/SCROLL.CPP create mode 100644 REDALERT/SCROLL.H create mode 100644 REDALERT/SDATA.CPP create mode 100644 REDALERT/SEARCH.H create mode 100644 REDALERT/SEDITDLG.CPP create mode 100644 REDALERT/SEDITDLG.H create mode 100644 REDALERT/SENDFILE.CPP create mode 100644 REDALERT/SEQCONN.CPP create mode 100644 REDALERT/SEQCONN.H create mode 100644 REDALERT/SESSION.CPP create mode 100644 REDALERT/SESSION.H create mode 100644 REDALERT/SHA.CPP create mode 100644 REDALERT/SHA.H create mode 100644 REDALERT/SHAPEBTN.CPP create mode 100644 REDALERT/SHAPEBTN.H create mode 100644 REDALERT/SHAPIPE.CPP create mode 100644 REDALERT/SHAPIPE.H create mode 100644 REDALERT/SHASTRAW.CPP create mode 100644 REDALERT/SHASTRAW.H create mode 100644 REDALERT/SIDEBAR.CPP create mode 100644 REDALERT/SIDEBAR.H create mode 100644 REDALERT/SIDEBARGlyphx.CPP create mode 100644 REDALERT/SIDEBARGlyphx.H create mode 100644 REDALERT/SLIDER.CPP create mode 100644 REDALERT/SLIDER.H create mode 100644 REDALERT/SMUDGE.CPP create mode 100644 REDALERT/SMUDGE.H create mode 100644 REDALERT/SOUNDDLG.CPP create mode 100644 REDALERT/SOUNDDLG.H create mode 100644 REDALERT/SPECIAL.CPP create mode 100644 REDALERT/SPECIAL.H create mode 100644 REDALERT/SPRITE.CPP create mode 100644 REDALERT/STAGE.H create mode 100644 REDALERT/STARTUP.CPP create mode 100644 REDALERT/STATBTN.CPP create mode 100644 REDALERT/STATBTN.H create mode 100644 REDALERT/STATS.CPP create mode 100644 REDALERT/STRAW.CPP create mode 100644 REDALERT/STRAW.H create mode 100644 REDALERT/STUB.CPP create mode 100644 REDALERT/STYLE.H create mode 100644 REDALERT/SUPER.CPP create mode 100644 REDALERT/SUPER.H create mode 100644 REDALERT/SUPPORT.ASM create mode 100644 REDALERT/SURFACE.CPP create mode 100644 REDALERT/SURFACE.H create mode 100644 REDALERT/Shape.cpp create mode 100644 REDALERT/TAB.CPP create mode 100644 REDALERT/TAB.H create mode 100644 REDALERT/TACTION.CPP create mode 100644 REDALERT/TACTION.H create mode 100644 REDALERT/TARGET.CPP create mode 100644 REDALERT/TARGET.H create mode 100644 REDALERT/TCPIP.CPP create mode 100644 REDALERT/TCPIP.H create mode 100644 REDALERT/TDATA.CPP create mode 100644 REDALERT/TEAM.CPP create mode 100644 REDALERT/TEAM.H create mode 100644 REDALERT/TEAMTYPE.CPP create mode 100644 REDALERT/TEAMTYPE.H create mode 100644 REDALERT/TECHNO.CPP create mode 100644 REDALERT/TECHNO.H create mode 100644 REDALERT/TEMPLATE.CPP create mode 100644 REDALERT/TEMPLATE.H create mode 100644 REDALERT/TENMGR.CPP create mode 100644 REDALERT/TENMGR.H create mode 100644 REDALERT/TERRAIN.CPP create mode 100644 REDALERT/TERRAIN.H create mode 100644 REDALERT/TEVENT.CPP create mode 100644 REDALERT/TEVENT.H create mode 100644 REDALERT/TEXTBTN.CPP create mode 100644 REDALERT/TEXTBTN.H create mode 100644 REDALERT/THEME.CPP create mode 100644 REDALERT/THEME.H create mode 100644 REDALERT/TOGGLE.CPP create mode 100644 REDALERT/TOGGLE.H create mode 100644 REDALERT/TOOLTIP.CPP create mode 100644 REDALERT/TOOLTIP.H create mode 100644 REDALERT/TRACKER.CPP create mode 100644 REDALERT/TRIGGER.CPP create mode 100644 REDALERT/TRIGGER.H create mode 100644 REDALERT/TRIGTYPE.CPP create mode 100644 REDALERT/TRIGTYPE.H create mode 100644 REDALERT/TURRET.CPP create mode 100644 REDALERT/TURRET.H create mode 100644 REDALERT/TXTLABEL.CPP create mode 100644 REDALERT/TXTLABEL.H create mode 100644 REDALERT/TXTPRNT.ASM create mode 100644 REDALERT/TYPE.H create mode 100644 REDALERT/UDATA.CPP create mode 100644 REDALERT/UDPADDR.CPP create mode 100644 REDALERT/UNIT.CPP create mode 100644 REDALERT/UNIT.H create mode 100644 REDALERT/UTRACKER.CPP create mode 100644 REDALERT/UTRACKER.H create mode 100644 REDALERT/VDATA.CPP create mode 100644 REDALERT/VECTOR.CPP create mode 100644 REDALERT/VECTOR.H create mode 100644 REDALERT/VERSION.CPP create mode 100644 REDALERT/VERSION.H create mode 100644 REDALERT/VESSEL.CPP create mode 100644 REDALERT/VESSEL.H create mode 100644 REDALERT/VISUDLG.CPP create mode 100644 REDALERT/VISUDLG.H create mode 100644 REDALERT/VORTEX.CPP create mode 100644 REDALERT/VORTEX.H create mode 100644 REDALERT/W95TRACE.CPP create mode 100644 REDALERT/W95TRACE.H create mode 100644 REDALERT/WARHEAD.CPP create mode 100644 REDALERT/WARHEAD.H create mode 100644 REDALERT/WATCOM.H create mode 100644 REDALERT/WEAPON.CPP create mode 100644 REDALERT/WEAPON.H create mode 100644 REDALERT/WIN32LIB/ALLOC.CPP create mode 100644 REDALERT/WIN32LIB/AUDIO.H create mode 100644 REDALERT/WIN32LIB/BUFFER.CPP create mode 100644 REDALERT/WIN32LIB/BUFFER.H create mode 100644 REDALERT/WIN32LIB/BUFFGLBL.CPP create mode 100644 REDALERT/WIN32LIB/DDRAW.CPP create mode 100644 REDALERT/WIN32LIB/DDRAW.H create mode 100644 REDALERT/WIN32LIB/DEFINES.H create mode 100644 REDALERT/WIN32LIB/DELAY.CPP create mode 100644 REDALERT/WIN32LIB/DESCMGMT.H create mode 100644 REDALERT/WIN32LIB/DIFFTB.INC create mode 100644 REDALERT/WIN32LIB/DIPTHONG.CPP create mode 100644 REDALERT/WIN32LIB/DIPTHONG.H create mode 100644 REDALERT/WIN32LIB/DPLAY.H create mode 100644 REDALERT/WIN32LIB/DRAWBUFF.H create mode 100644 REDALERT/WIN32LIB/DRAWBUFF.INC create mode 100644 REDALERT/WIN32LIB/DRAWRECT.CPP create mode 100644 REDALERT/WIN32LIB/DSETUP.H create mode 100644 REDALERT/WIN32LIB/DSOUND.H create mode 100644 REDALERT/WIN32LIB/DrawMisc.cpp create mode 100644 REDALERT/WIN32LIB/EXTERNS.H create mode 100644 REDALERT/WIN32LIB/FASTFILE.H create mode 100644 REDALERT/WIN32LIB/FILE.H create mode 100644 REDALERT/WIN32LIB/FILEPCX.H create mode 100644 REDALERT/WIN32LIB/FILETEMP.H create mode 100644 REDALERT/WIN32LIB/FONT.CPP create mode 100644 REDALERT/WIN32LIB/FONT.H create mode 100644 REDALERT/WIN32LIB/FUNCTION.H create mode 100644 REDALERT/WIN32LIB/GBUFFER.CPP create mode 100644 REDALERT/WIN32LIB/GBUFFER.H create mode 100644 REDALERT/WIN32LIB/GBUFFER.INC create mode 100644 REDALERT/WIN32LIB/GETSHAPE.CPP create mode 100644 REDALERT/WIN32LIB/ICONCACH.H create mode 100644 REDALERT/WIN32LIB/ICONSET.CPP create mode 100644 REDALERT/WIN32LIB/IFF.CPP create mode 100644 REDALERT/WIN32LIB/IFF.H create mode 100644 REDALERT/WIN32LIB/INDEXTB.INC create mode 100644 REDALERT/WIN32LIB/IRANDOM.CPP create mode 100644 REDALERT/WIN32LIB/KEYBOARD.H create mode 100644 REDALERT/WIN32LIB/KEYBOARD.INC create mode 100644 REDALERT/WIN32LIB/KEYSTRUC.INC create mode 100644 REDALERT/WIN32LIB/LCWCOMP.ASM create mode 100644 REDALERT/WIN32LIB/LCWUNCMP.ASM create mode 100644 REDALERT/WIN32LIB/LOAD.CPP create mode 100644 REDALERT/WIN32LIB/LOADFONT.CPP create mode 100644 REDALERT/WIN32LIB/MCGAPRIM.INC create mode 100644 REDALERT/WIN32LIB/MEMFLAG.H create mode 100644 REDALERT/WIN32LIB/MISC.H create mode 100644 REDALERT/WIN32LIB/MODEMREG.H create mode 100644 REDALERT/WIN32LIB/MONO.H create mode 100644 REDALERT/WIN32LIB/MOUSE.H create mode 100644 REDALERT/WIN32LIB/MOUSE.INC create mode 100644 REDALERT/WIN32LIB/MOUSEWW.CPP create mode 100644 REDALERT/WIN32LIB/NYBBTB.INC create mode 100644 REDALERT/WIN32LIB/PALETTE.CPP create mode 100644 REDALERT/WIN32LIB/PALETTE.H create mode 100644 REDALERT/WIN32LIB/PLAYCD.H create mode 100644 REDALERT/WIN32LIB/PROFILE.H create mode 100644 REDALERT/WIN32LIB/PROFILE.INC create mode 100644 REDALERT/WIN32LIB/RAWFILE.H create mode 100644 REDALERT/WIN32LIB/SET_FONT.CPP create mode 100644 REDALERT/WIN32LIB/SHAPE.H create mode 100644 REDALERT/WIN32LIB/SHAPE.INC create mode 100644 REDALERT/WIN32LIB/SOS.H create mode 100644 REDALERT/WIN32LIB/SOSCOMP.H create mode 100644 REDALERT/WIN32LIB/SOSDATA.H create mode 100644 REDALERT/WIN32LIB/SOSDEFS.H create mode 100644 REDALERT/WIN32LIB/SOSFNCT.H create mode 100644 REDALERT/WIN32LIB/SOSRES.H create mode 100644 REDALERT/WIN32LIB/SOUND.H create mode 100644 REDALERT/WIN32LIB/SOUNDINT.H create mode 100644 REDALERT/WIN32LIB/STAMP.INC create mode 100644 REDALERT/WIN32LIB/STRUCTS.H create mode 100644 REDALERT/WIN32LIB/SVGAPRIM.INC create mode 100644 REDALERT/WIN32LIB/TILE.H create mode 100644 REDALERT/WIN32LIB/TIMER.CPP create mode 100644 REDALERT/WIN32LIB/TIMER.H create mode 100644 REDALERT/WIN32LIB/TIMERDWN.CPP create mode 100644 REDALERT/WIN32LIB/TIMERINI.CPP create mode 100644 REDALERT/WIN32LIB/TOBUFF.ASM create mode 100644 REDALERT/WIN32LIB/VIDEO.H create mode 100644 REDALERT/WIN32LIB/WINCOMM.H create mode 100644 REDALERT/WIN32LIB/WINDOWS.CPP create mode 100644 REDALERT/WIN32LIB/WINHIDE.CPP create mode 100644 REDALERT/WIN32LIB/WRITEPCX.CPP create mode 100644 REDALERT/WIN32LIB/WSA.H create mode 100644 REDALERT/WIN32LIB/WWFILE.H create mode 100644 REDALERT/WIN32LIB/WWLIB32.H create mode 100644 REDALERT/WIN32LIB/WWMEM.H create mode 100644 REDALERT/WIN32LIB/WWMEM.INC create mode 100644 REDALERT/WIN32LIB/WW_WIN.H create mode 100644 REDALERT/WIN32LIB/_DIPTABL.CPP create mode 100644 REDALERT/WIN32LIB/wwstd.h create mode 100644 REDALERT/WINSTUB.CPP create mode 100644 REDALERT/WOLAPIOB.CPP create mode 100644 REDALERT/WOLAPIOB.H create mode 100644 REDALERT/WOLDEBUG.H create mode 100644 REDALERT/WOLEDIT.CPP create mode 100644 REDALERT/WOLEDIT.H create mode 100644 REDALERT/WOLSTRNG.CPP create mode 100644 REDALERT/WOLSTRNG.H create mode 100644 REDALERT/WOL_CGAM.CPP create mode 100644 REDALERT/WOL_CHAT.CPP create mode 100644 REDALERT/WOL_DNLD.CPP create mode 100644 REDALERT/WOL_GSUP.CPP create mode 100644 REDALERT/WOL_GSUP.H create mode 100644 REDALERT/WOL_LOGN.CPP create mode 100644 REDALERT/WOL_MAIN.CPP create mode 100644 REDALERT/WOL_OPT.CPP create mode 100644 REDALERT/WSNWLINK.H create mode 100644 REDALERT/WSPIPX.CPP create mode 100644 REDALERT/WSPIPX.H create mode 100644 REDALERT/WSPROTO.CPP create mode 100644 REDALERT/WSPROTO.H create mode 100644 REDALERT/WSPUDP.CPP create mode 100644 REDALERT/WSPUDP.H create mode 100644 REDALERT/WWALLOC.H create mode 100644 REDALERT/WWFILE.H create mode 100644 REDALERT/XPIPE.CPP create mode 100644 REDALERT/XPIPE.H create mode 100644 REDALERT/XSTRAW.CPP create mode 100644 REDALERT/XSTRAW.H create mode 100644 REDALERT/_WSPROTO.CPP create mode 100644 REDALERT/_WSPROTO.H create mode 100644 TIBERIANDAWN/AADATA.CPP create mode 100644 TIBERIANDAWN/ABSTRACT.CPP create mode 100644 TIBERIANDAWN/ABSTRACT.H create mode 100644 TIBERIANDAWN/ADATA.CPP create mode 100644 TIBERIANDAWN/AIRCRAFT.CPP create mode 100644 TIBERIANDAWN/AIRCRAFT.H create mode 100644 TIBERIANDAWN/ALLOC.CPP create mode 100644 TIBERIANDAWN/ANIM.CPP create mode 100644 TIBERIANDAWN/ANIM.H create mode 100644 TIBERIANDAWN/AUDIO.CPP create mode 100644 TIBERIANDAWN/AUDIO.H create mode 100644 TIBERIANDAWN/BASE.CPP create mode 100644 TIBERIANDAWN/BASE.H create mode 100644 TIBERIANDAWN/BBDATA.CPP create mode 100644 TIBERIANDAWN/BDATA.CPP create mode 100644 TIBERIANDAWN/BUILDING.CPP create mode 100644 TIBERIANDAWN/BUILDING.H create mode 100644 TIBERIANDAWN/BULLET.CPP create mode 100644 TIBERIANDAWN/BULLET.H create mode 100644 TIBERIANDAWN/CARGO.CPP create mode 100644 TIBERIANDAWN/CARGO.H create mode 100644 TIBERIANDAWN/CCDDE.CPP create mode 100644 TIBERIANDAWN/CCDDE.H create mode 100644 TIBERIANDAWN/CCFILE.CPP create mode 100644 TIBERIANDAWN/CCFILE.H create mode 100644 TIBERIANDAWN/CC_ICON.RC create mode 100644 TIBERIANDAWN/CDATA.CPP create mode 100644 TIBERIANDAWN/CDFILE.CPP create mode 100644 TIBERIANDAWN/CDFILE.H create mode 100644 TIBERIANDAWN/CELL.CPP create mode 100644 TIBERIANDAWN/CELL.H create mode 100644 TIBERIANDAWN/CHECKBOX.CPP create mode 100644 TIBERIANDAWN/CHECKBOX.H create mode 100644 TIBERIANDAWN/CHEKLIST.CPP create mode 100644 TIBERIANDAWN/CHEKLIST.H create mode 100644 TIBERIANDAWN/COLRLIST.CPP create mode 100644 TIBERIANDAWN/COLRLIST.H create mode 100644 TIBERIANDAWN/COMBAT.CPP create mode 100644 TIBERIANDAWN/COMBUF.CPP create mode 100644 TIBERIANDAWN/COMBUF.H create mode 100644 TIBERIANDAWN/COMPAT.H create mode 100644 TIBERIANDAWN/COMQUEUE.CPP create mode 100644 TIBERIANDAWN/COMQUEUE.H create mode 100644 TIBERIANDAWN/CONFDLG.CPP create mode 100644 TIBERIANDAWN/CONFDLG.H create mode 100644 TIBERIANDAWN/CONNECT.CPP create mode 100644 TIBERIANDAWN/CONNECT.H create mode 100644 TIBERIANDAWN/CONNMGR.H create mode 100644 TIBERIANDAWN/CONQUER.CPP create mode 100644 TIBERIANDAWN/CONQUER.H create mode 100644 TIBERIANDAWN/CONST.CPP create mode 100644 TIBERIANDAWN/CONTROL.CPP create mode 100644 TIBERIANDAWN/CONTROL.H create mode 100644 TIBERIANDAWN/COORD.CPP create mode 100644 TIBERIANDAWN/COORDA.ASM create mode 100644 TIBERIANDAWN/COORDA.h create mode 100644 TIBERIANDAWN/CREDITS.CPP create mode 100644 TIBERIANDAWN/CREDITS.H create mode 100644 TIBERIANDAWN/CREW.CPP create mode 100644 TIBERIANDAWN/CREW.H create mode 100644 TIBERIANDAWN/DDE.CPP create mode 100644 TIBERIANDAWN/DDE.H create mode 100644 TIBERIANDAWN/DEBUG.CPP create mode 100644 TIBERIANDAWN/DEBUG.H create mode 100644 TIBERIANDAWN/DEFINES.H create mode 100644 TIBERIANDAWN/DESCDLG.CPP create mode 100644 TIBERIANDAWN/DESCDLG.H create mode 100644 TIBERIANDAWN/DIAL8.CPP create mode 100644 TIBERIANDAWN/DIAL8.H create mode 100644 TIBERIANDAWN/DIALOG.CPP create mode 100644 TIBERIANDAWN/DISPLAY.CPP create mode 100644 TIBERIANDAWN/DISPLAY.H create mode 100644 TIBERIANDAWN/DLLInterface.cpp create mode 100644 TIBERIANDAWN/DLLInterface.h create mode 100644 TIBERIANDAWN/DLLInterfaceEditor.cpp create mode 100644 TIBERIANDAWN/DOOR.CPP create mode 100644 TIBERIANDAWN/DOOR.H create mode 100644 TIBERIANDAWN/DPMI.CPP create mode 100644 TIBERIANDAWN/DPMI.H create mode 100644 TIBERIANDAWN/DRIVE.CPP create mode 100644 TIBERIANDAWN/DRIVE.H create mode 100644 TIBERIANDAWN/EDIT.CPP create mode 100644 TIBERIANDAWN/EDIT.H create mode 100644 TIBERIANDAWN/ENDING.CPP create mode 100644 TIBERIANDAWN/ENDING.H create mode 100644 TIBERIANDAWN/EVENT.CPP create mode 100644 TIBERIANDAWN/EVENT.H create mode 100644 TIBERIANDAWN/EXPAND.CPP create mode 100644 TIBERIANDAWN/EXTERNS.H create mode 100644 TIBERIANDAWN/FACING.CPP create mode 100644 TIBERIANDAWN/FACING.H create mode 100644 TIBERIANDAWN/FACTORY.CPP create mode 100644 TIBERIANDAWN/FACTORY.H create mode 100644 TIBERIANDAWN/FIELD.CPP create mode 100644 TIBERIANDAWN/FIELD.H create mode 100644 TIBERIANDAWN/FINDPATH.CPP create mode 100644 TIBERIANDAWN/FLASHER.CPP create mode 100644 TIBERIANDAWN/FLASHER.H create mode 100644 TIBERIANDAWN/FLY.CPP create mode 100644 TIBERIANDAWN/FLY.H create mode 100644 TIBERIANDAWN/FOOT.CPP create mode 100644 TIBERIANDAWN/FOOT.H create mode 100644 TIBERIANDAWN/FTIMER.H create mode 100644 TIBERIANDAWN/FUNCTION.H create mode 100644 TIBERIANDAWN/FUSE.CPP create mode 100644 TIBERIANDAWN/FUSE.H create mode 100644 TIBERIANDAWN/GADGET.CPP create mode 100644 TIBERIANDAWN/GADGET.H create mode 100644 TIBERIANDAWN/GAMEDLG.CPP create mode 100644 TIBERIANDAWN/GAMEDLG.H create mode 100644 TIBERIANDAWN/GAUGE.CPP create mode 100644 TIBERIANDAWN/GAUGE.H create mode 100644 TIBERIANDAWN/GLOBALS.CPP create mode 100644 TIBERIANDAWN/GOPTIONS.CPP create mode 100644 TIBERIANDAWN/GOPTIONS.H create mode 100644 TIBERIANDAWN/GSCREEN.CPP create mode 100644 TIBERIANDAWN/GSCREEN.H create mode 100644 TIBERIANDAWN/HDATA.CPP create mode 100644 TIBERIANDAWN/HEAP.CPP create mode 100644 TIBERIANDAWN/HEAP.H create mode 100644 TIBERIANDAWN/HELP.CPP create mode 100644 TIBERIANDAWN/HELP.H create mode 100644 TIBERIANDAWN/HOUSE.CPP create mode 100644 TIBERIANDAWN/HOUSE.H create mode 100644 TIBERIANDAWN/IDATA.CPP create mode 100644 TIBERIANDAWN/INFANTRY.CPP create mode 100644 TIBERIANDAWN/INFANTRY.H create mode 100644 TIBERIANDAWN/INI.CPP create mode 100644 TIBERIANDAWN/INIT.CPP create mode 100644 TIBERIANDAWN/INTERNET.CPP create mode 100644 TIBERIANDAWN/INTERPAL.CPP create mode 100644 TIBERIANDAWN/INTRO.CPP create mode 100644 TIBERIANDAWN/INTRO.H create mode 100644 TIBERIANDAWN/IOMAP.CPP create mode 100644 TIBERIANDAWN/IOOBJ.CPP create mode 100644 TIBERIANDAWN/IPX.CPP create mode 100644 TIBERIANDAWN/IPX.H create mode 100644 TIBERIANDAWN/IPX95.CPP create mode 100644 TIBERIANDAWN/IPX95.H create mode 100644 TIBERIANDAWN/IPXADDR.CPP create mode 100644 TIBERIANDAWN/IPXADDR.H create mode 100644 TIBERIANDAWN/IPXCONN.CPP create mode 100644 TIBERIANDAWN/IPXCONN.H create mode 100644 TIBERIANDAWN/IPXGCONN.CPP create mode 100644 TIBERIANDAWN/IPXGCONN.H create mode 100644 TIBERIANDAWN/IPXMGR.CPP create mode 100644 TIBERIANDAWN/IPXMGR.H create mode 100644 TIBERIANDAWN/IPXPROT.ASM create mode 100644 TIBERIANDAWN/IPXREAL.ASM create mode 100644 TIBERIANDAWN/JSHELL.CPP create mode 100644 TIBERIANDAWN/JSHELL.H create mode 100644 TIBERIANDAWN/KEYFBUFF.ASM create mode 100644 TIBERIANDAWN/KEYFBUFF.INC create mode 100644 TIBERIANDAWN/KEYFRAME.CPP create mode 100644 TIBERIANDAWN/LAYER.CPP create mode 100644 TIBERIANDAWN/LAYER.H create mode 100644 TIBERIANDAWN/LED.H create mode 100644 TIBERIANDAWN/LINK.CPP create mode 100644 TIBERIANDAWN/LINK.H create mode 100644 TIBERIANDAWN/LIST.CPP create mode 100644 TIBERIANDAWN/LIST.H create mode 100644 TIBERIANDAWN/LOADDLG.CPP create mode 100644 TIBERIANDAWN/LOADDLG.H create mode 100644 TIBERIANDAWN/LOGIC.CPP create mode 100644 TIBERIANDAWN/LOGIC.H create mode 100644 TIBERIANDAWN/License.txt create mode 100644 TIBERIANDAWN/MAP.CPP create mode 100644 TIBERIANDAWN/MAP.H create mode 100644 TIBERIANDAWN/MAPEDDLG.CPP create mode 100644 TIBERIANDAWN/MAPEDIT.CPP create mode 100644 TIBERIANDAWN/MAPEDIT.H create mode 100644 TIBERIANDAWN/MAPEDPLC.CPP create mode 100644 TIBERIANDAWN/MAPEDSEL.CPP create mode 100644 TIBERIANDAWN/MAPEDTM.CPP create mode 100644 TIBERIANDAWN/MAPSEL.CPP create mode 100644 TIBERIANDAWN/MENUS.CPP create mode 100644 TIBERIANDAWN/MESSAGE.H create mode 100644 TIBERIANDAWN/MISSION.CPP create mode 100644 TIBERIANDAWN/MISSION.H create mode 100644 TIBERIANDAWN/MIXFILE.CPP create mode 100644 TIBERIANDAWN/MIXFILE.H create mode 100644 TIBERIANDAWN/MMX.ASM create mode 100644 TIBERIANDAWN/MONOC.CPP create mode 100644 TIBERIANDAWN/MONOC.H create mode 100644 TIBERIANDAWN/MOUSE.CPP create mode 100644 TIBERIANDAWN/MOUSE.H create mode 100644 TIBERIANDAWN/MPLAYER.CPP create mode 100644 TIBERIANDAWN/MSGBOX.CPP create mode 100644 TIBERIANDAWN/MSGBOX.H create mode 100644 TIBERIANDAWN/MSGLIST.CPP create mode 100644 TIBERIANDAWN/MSGLIST.H create mode 100644 TIBERIANDAWN/MiscAsm.cpp create mode 100644 TIBERIANDAWN/NETDLG.CPP create mode 100644 TIBERIANDAWN/NOSEQCON.CPP create mode 100644 TIBERIANDAWN/NOSEQCON.H create mode 100644 TIBERIANDAWN/NULLCONN.CPP create mode 100644 TIBERIANDAWN/NULLCONN.H create mode 100644 TIBERIANDAWN/NULLDLG.CPP create mode 100644 TIBERIANDAWN/NULLMGR.CPP create mode 100644 TIBERIANDAWN/NULLMGR.H create mode 100644 TIBERIANDAWN/OBJECT.CPP create mode 100644 TIBERIANDAWN/OBJECT.H create mode 100644 TIBERIANDAWN/ODATA.CPP create mode 100644 TIBERIANDAWN/OPTIONS.CPP create mode 100644 TIBERIANDAWN/OPTIONS.H create mode 100644 TIBERIANDAWN/OVERLAY.CPP create mode 100644 TIBERIANDAWN/OVERLAY.H create mode 100644 TIBERIANDAWN/PACKET.CPP create mode 100644 TIBERIANDAWN/PACKET.H create mode 100644 TIBERIANDAWN/PAGFAULT.ASM create mode 100644 TIBERIANDAWN/PHONE.H create mode 100644 TIBERIANDAWN/POWER.CPP create mode 100644 TIBERIANDAWN/POWER.H create mode 100644 TIBERIANDAWN/PROFILE.CPP create mode 100644 TIBERIANDAWN/QUEUE.CPP create mode 100644 TIBERIANDAWN/QUEUE.H create mode 100644 TIBERIANDAWN/RADAR.CPP create mode 100644 TIBERIANDAWN/RADAR.H create mode 100644 TIBERIANDAWN/RADIO.CPP create mode 100644 TIBERIANDAWN/RADIO.H create mode 100644 TIBERIANDAWN/RAND.CPP create mode 100644 TIBERIANDAWN/RAWFILE.CPP create mode 100644 TIBERIANDAWN/RAWFILE.H create mode 100644 TIBERIANDAWN/REAL.H create mode 100644 TIBERIANDAWN/REGION.H create mode 100644 TIBERIANDAWN/REG_ICON.RC create mode 100644 TIBERIANDAWN/REINF.CPP create mode 100644 TIBERIANDAWN/RESOURCE/TiberianDawn.rc create mode 100644 TIBERIANDAWN/RESOURCE/resource.h create mode 100644 TIBERIANDAWN/RULES.CPP create mode 100644 TIBERIANDAWN/RULES.H create mode 100644 TIBERIANDAWN/SAVEDLG.H create mode 100644 TIBERIANDAWN/SAVELOAD.CPP create mode 100644 TIBERIANDAWN/SCENARIO.CPP create mode 100644 TIBERIANDAWN/SCORE.CPP create mode 100644 TIBERIANDAWN/SCORE.H create mode 100644 TIBERIANDAWN/SCREEN.H create mode 100644 TIBERIANDAWN/SCROLL.CPP create mode 100644 TIBERIANDAWN/SCROLL.H create mode 100644 TIBERIANDAWN/SDATA.CPP create mode 100644 TIBERIANDAWN/SEQCONN.CPP create mode 100644 TIBERIANDAWN/SEQCONN.H create mode 100644 TIBERIANDAWN/SESSION.H create mode 100644 TIBERIANDAWN/SHAPEBTN.CPP create mode 100644 TIBERIANDAWN/SHAPEBTN.H create mode 100644 TIBERIANDAWN/SIDEBAR.CPP create mode 100644 TIBERIANDAWN/SIDEBAR.H create mode 100644 TIBERIANDAWN/SIDEBARGlyphx.CPP create mode 100644 TIBERIANDAWN/SIDEBARGlyphx.H create mode 100644 TIBERIANDAWN/SLIDER.CPP create mode 100644 TIBERIANDAWN/SLIDER.H create mode 100644 TIBERIANDAWN/SMUDGE.CPP create mode 100644 TIBERIANDAWN/SMUDGE.H create mode 100644 TIBERIANDAWN/SOUNDDLG.CPP create mode 100644 TIBERIANDAWN/SOUNDDLG.H create mode 100644 TIBERIANDAWN/SPECIAL.CPP create mode 100644 TIBERIANDAWN/SPECIAL.H create mode 100644 TIBERIANDAWN/STAGE.H create mode 100644 TIBERIANDAWN/STARTUP.CPP create mode 100644 TIBERIANDAWN/STATS.CPP create mode 100644 TIBERIANDAWN/SUPER.CPP create mode 100644 TIBERIANDAWN/SUPER.H create mode 100644 TIBERIANDAWN/SUPPORT.ASM create mode 100644 TIBERIANDAWN/Shape.cpp create mode 100644 TIBERIANDAWN/TAB.CPP create mode 100644 TIBERIANDAWN/TAB.H create mode 100644 TIBERIANDAWN/TARCOM.CPP create mode 100644 TIBERIANDAWN/TARCOM.H create mode 100644 TIBERIANDAWN/TARGET.CPP create mode 100644 TIBERIANDAWN/TARGET.H create mode 100644 TIBERIANDAWN/TCPIP.CPP create mode 100644 TIBERIANDAWN/TCPIP.H create mode 100644 TIBERIANDAWN/TDATA.CPP create mode 100644 TIBERIANDAWN/TEAM.CPP create mode 100644 TIBERIANDAWN/TEAM.H create mode 100644 TIBERIANDAWN/TEAMTYPE.CPP create mode 100644 TIBERIANDAWN/TEAMTYPE.H create mode 100644 TIBERIANDAWN/TECHNO.CPP create mode 100644 TIBERIANDAWN/TECHNO.H create mode 100644 TIBERIANDAWN/TEMPLATE.CPP create mode 100644 TIBERIANDAWN/TEMPLATE.H create mode 100644 TIBERIANDAWN/TERRAIN.CPP create mode 100644 TIBERIANDAWN/TERRAIN.H create mode 100644 TIBERIANDAWN/TEXTBLIT.H create mode 100644 TIBERIANDAWN/TEXTBTN.CPP create mode 100644 TIBERIANDAWN/TEXTBTN.H create mode 100644 TIBERIANDAWN/THEME.CPP create mode 100644 TIBERIANDAWN/THEME.H create mode 100644 TIBERIANDAWN/TOGGLE.CPP create mode 100644 TIBERIANDAWN/TOGGLE.H create mode 100644 TIBERIANDAWN/TRIGGER.CPP create mode 100644 TIBERIANDAWN/TRIGGER.H create mode 100644 TIBERIANDAWN/TURRET.CPP create mode 100644 TIBERIANDAWN/TURRET.H create mode 100644 TIBERIANDAWN/TXTLABEL.CPP create mode 100644 TIBERIANDAWN/TXTLABEL.H create mode 100644 TIBERIANDAWN/TXTPRNT.ASM create mode 100644 TIBERIANDAWN/TYPE.H create mode 100644 TIBERIANDAWN/TiberianDawn.vcxproj create mode 100644 TIBERIANDAWN/TiberianDawn.vcxproj.filters create mode 100644 TIBERIANDAWN/UDATA.CPP create mode 100644 TIBERIANDAWN/UNIT.CPP create mode 100644 TIBERIANDAWN/UNIT.H create mode 100644 TIBERIANDAWN/UTRACKER.CPP create mode 100644 TIBERIANDAWN/UTRACKER.H create mode 100644 TIBERIANDAWN/VECTOR.CPP create mode 100644 TIBERIANDAWN/VECTOR.H create mode 100644 TIBERIANDAWN/VISUDLG.CPP create mode 100644 TIBERIANDAWN/VISUDLG.H create mode 100644 TIBERIANDAWN/WATCOM.H create mode 100644 TIBERIANDAWN/WIN32LIB/ALLOC.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/AUDIO.H create mode 100644 TIBERIANDAWN/WIN32LIB/BUFFER.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/BUFFER.H create mode 100644 TIBERIANDAWN/WIN32LIB/BUFFGLBL.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/DDRAW.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/DDRAW.H create mode 100644 TIBERIANDAWN/WIN32LIB/DEFINES.H create mode 100644 TIBERIANDAWN/WIN32LIB/DESCMGMT.H create mode 100644 TIBERIANDAWN/WIN32LIB/DIFFTB.INC create mode 100644 TIBERIANDAWN/WIN32LIB/DIPTHONG.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/DIPTHONG.H create mode 100644 TIBERIANDAWN/WIN32LIB/DPLAY.H create mode 100644 TIBERIANDAWN/WIN32LIB/DRAWBUFF.H create mode 100644 TIBERIANDAWN/WIN32LIB/DRAWBUFF.INC create mode 100644 TIBERIANDAWN/WIN32LIB/DRAWRECT.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/DSETUP.H create mode 100644 TIBERIANDAWN/WIN32LIB/DSOUND.H create mode 100644 TIBERIANDAWN/WIN32LIB/DrawMisc.cpp create mode 100644 TIBERIANDAWN/WIN32LIB/EXTERNS.H create mode 100644 TIBERIANDAWN/WIN32LIB/FACINGFF.ASM create mode 100644 TIBERIANDAWN/WIN32LIB/FACINGFF.h create mode 100644 TIBERIANDAWN/WIN32LIB/FASTFILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/FILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/FILEPCX.H create mode 100644 TIBERIANDAWN/WIN32LIB/FILETEMP.H create mode 100644 TIBERIANDAWN/WIN32LIB/FONT.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/FONT.H create mode 100644 TIBERIANDAWN/WIN32LIB/FUNCTION.H create mode 100644 TIBERIANDAWN/WIN32LIB/GBUFFER.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/GBUFFER.H create mode 100644 TIBERIANDAWN/WIN32LIB/GBUFFER.INC create mode 100644 TIBERIANDAWN/WIN32LIB/GETSHAPE.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/ICONCACH.H create mode 100644 TIBERIANDAWN/WIN32LIB/ICONSET.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/IFF.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/IFF.H create mode 100644 TIBERIANDAWN/WIN32LIB/INDEXTB.INC create mode 100644 TIBERIANDAWN/WIN32LIB/IRANDOM.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/KEYBOARD.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/KEYBOARD.H create mode 100644 TIBERIANDAWN/WIN32LIB/KEYBOARD.INC create mode 100644 TIBERIANDAWN/WIN32LIB/KEYSTRUC.INC create mode 100644 TIBERIANDAWN/WIN32LIB/LOAD.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/LOADFONT.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/LOADPAL.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/MCGAPRIM.INC create mode 100644 TIBERIANDAWN/WIN32LIB/MEM.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/MEMFLAG.H create mode 100644 TIBERIANDAWN/WIN32LIB/MISC.H create mode 100644 TIBERIANDAWN/WIN32LIB/MODEMREG.H create mode 100644 TIBERIANDAWN/WIN32LIB/MONO.H create mode 100644 TIBERIANDAWN/WIN32LIB/MORPHPAL.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/MOUSE.H create mode 100644 TIBERIANDAWN/WIN32LIB/MOUSE.INC create mode 100644 TIBERIANDAWN/WIN32LIB/MOUSEWW.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/NEWDEL.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/NYBBTB.INC create mode 100644 TIBERIANDAWN/WIN32LIB/PALETTE.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/PALETTE.H create mode 100644 TIBERIANDAWN/WIN32LIB/PLAYCD.H create mode 100644 TIBERIANDAWN/WIN32LIB/PROFILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/PROFILE.INC create mode 100644 TIBERIANDAWN/WIN32LIB/RAWFILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/REGIONSZ.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/REMAP.ASM create mode 100644 TIBERIANDAWN/WIN32LIB/SET_FONT.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/SHAPE.H create mode 100644 TIBERIANDAWN/WIN32LIB/SHAPE.INC create mode 100644 TIBERIANDAWN/WIN32LIB/SOUND.H create mode 100644 TIBERIANDAWN/WIN32LIB/SOUNDINT.H create mode 100644 TIBERIANDAWN/WIN32LIB/STAMP.INC create mode 100644 TIBERIANDAWN/WIN32LIB/STRUCTS.H create mode 100644 TIBERIANDAWN/WIN32LIB/SVGAPRIM.INC create mode 100644 TIBERIANDAWN/WIN32LIB/TILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/TIMER.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/TIMER.H create mode 100644 TIBERIANDAWN/WIN32LIB/TIMERDWN.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/TIMERINI.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/TOBUFF.ASM create mode 100644 TIBERIANDAWN/WIN32LIB/VIDEO.H create mode 100644 TIBERIANDAWN/WIN32LIB/WINCOMM.H create mode 100644 TIBERIANDAWN/WIN32LIB/WINDOWS.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/WINHIDE.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/WRITEPCX.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/WSA.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/WSA.H create mode 100644 TIBERIANDAWN/WIN32LIB/WWFILE.H create mode 100644 TIBERIANDAWN/WIN32LIB/WWLIB32.H create mode 100644 TIBERIANDAWN/WIN32LIB/WWMEM.H create mode 100644 TIBERIANDAWN/WIN32LIB/WWMEM.INC create mode 100644 TIBERIANDAWN/WIN32LIB/WWSTD.H create mode 100644 TIBERIANDAWN/WIN32LIB/WW_WIN.H create mode 100644 TIBERIANDAWN/WIN32LIB/XORDELTA.ASM create mode 100644 TIBERIANDAWN/WIN32LIB/_DIPTABL.CPP create mode 100644 TIBERIANDAWN/WIN32LIB/_FILE.H create mode 100644 TIBERIANDAWN/WINASM.ASM create mode 100644 TIBERIANDAWN/WINSTUB.CPP create mode 100644 TIBERIANDAWN/WWALLOC.H create mode 100644 TIBERIANDAWN/WWFILE.H diff --git a/CnCRemastered.sln b/CnCRemastered.sln new file mode 100644 index 000000000..52d2e1242 --- /dev/null +++ b/CnCRemastered.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1022 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TiberianDawn", "TiberianDawn\TiberianDawn.vcxproj", "{1380ED08-82A3-49C2-A171-1915574B3382}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedAlert", "RedAlert\RedAlert.vcxproj", "{DA948ED9-EF67-4813-94B7-995BE956786E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1380ED08-82A3-49C2-A171-1915574B3382}.Release|x86.ActiveCfg = Release|Win32 + {1380ED08-82A3-49C2-A171-1915574B3382}.Release|x86.Build.0 = Release|Win32 + {DA948ED9-EF67-4813-94B7-995BE956786E}.Release|x86.ActiveCfg = Release|Win32 + {DA948ED9-EF67-4813-94B7-995BE956786E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {712D2733-25EE-407D-AEF8-B2342757E119} + EndGlobalSection +EndGlobal diff --git a/REDALERT/2KEYFRAM.CPP b/REDALERT/2KEYFRAM.CPP new file mode 100644 index 000000000..bc6942bdc --- /dev/null +++ b/REDALERT/2KEYFRAM.CPP @@ -0,0 +1,588 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/2KEYFRAM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + +#define INITIAL_BIG_SHAPE_BUFFER_SIZE 8000000 +#define THEATER_BIG_SHAPE_BUFFER_SIZE 4000000 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char *BigShapeBufferStart = NULL; + char *TheaterShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = FALSE; + bool IsTheaterShape = false; +} +#ifdef FIXIT_SCORE_CRASH +/* +** Global required to fix the score screen crash bug by allowing disabling of uncompressed shapes. +*/ +bool OriginalUseBigShapeBuffer = false; +#endif //FIXIT +char *BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; + +char *TheaterShapeBufferPtr = NULL; +int TotalTheaterShapes = 0; + + +#define MAX_SLOTS 1500 +#define THEATER_SLOT_START 1000 + +char **KeyFrameSlots [MAX_SLOTS]; +int TotalSlotsUsed=0; +int TheaterSlotsUsed = THEATER_SLOT_START; + + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char *shape_data; + int shape_buffer; //1 if shape is in theater buffer +} ShapeHeaderType; + +static int Length; + +void *Get_Shape_Header_Data(void *ptr) +{ + if (UseBigShapeBuffer) { + + ShapeHeaderType *header = (ShapeHeaderType*) ptr; + return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) ); + + } else { + return (ptr); + } +} + +int Get_Last_Frame_Length(void) +{ + return(Length); +} + + +void Reset_Theater_Shapes (void) +{ + /* + ** Delete any previously allocated slots + */ + for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE; +#ifdef FIXIT_SCORE_CRASH + /* + ** Keep track of our original decision about whether to use cached shapes. + ** This is needed for the score screen crash fix. + */ + OriginalUseBigShapeBuffer = UseBigShapeBuffer; +#endif //FIXIT +#endif +} + + + + + +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr) +{ +#ifdef FIXIT_SCORE_CRASH + char *ptr; + unsigned long offcurr, offdiff; +#else + char *ptr, *lockptr;//, *uncomp_ptr; + unsigned long offcurr, off16, offdiff; +#endif + unsigned long offset[SUBFRAMEOFFS]; + KeyFrameHeaderType *keyfr; + unsigned short buffsize, currframe, subframe; + unsigned long length = 0; + char frameflags; + unsigned long return_value; + char *temp_shape_ptr; + + // + // valid pointer?? + // + Length = 0; + if ( !dataptr || !buffptr ) { + return(0); + } + + // + // look at header then check that frame to build is not greater + // than total frames + // + keyfr = (KeyFrameHeaderType *) dataptr; + + if ( framenumber >= keyfr->frames ) { + return(0); + } + + + if (UseBigShapeBuffer) { + /* + ** If we havnt yet allocated memory for uncompressed shapes then do so now. + ** + */ + if (!BigShapeBufferStart) { + BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL); + BigShapeBufferPtr = BigShapeBufferStart; + + /* + ** Allocate memory for theater specific uncompressed shapes + */ + TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL); + TheaterShapeBufferPtr = TheaterShapeBufferStart; + } + + /* + ** If we are running out of memory (<10k left) for uncompressed shapes + ** then allocate some more. + */ + if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128000) { + ReallocShapeBufferFlag = TRUE; + } + + /* + ** If this animation was not previously uncompressed then + ** allocate memory to keep the pointers to the uncompressed data + ** for these animation frames + */ + if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER) { + keyfr->x = UNCOMPRESS_MAGIC_NUMBER; + if (IsTheaterShape) { + keyfr->y = TheaterSlotsUsed; + TheaterSlotsUsed++; + } else { + keyfr->y = TotalSlotsUsed; + TotalSlotsUsed++; + } + /* + ** Allocate and clear the memory for the shape info + */ + KeyFrameSlots[keyfr->y]= new char *[keyfr->frames]; + memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4); + } + + /* + ** If this frame was previously uncompressed then just return + ** a pointer to the raw data + */ + if (*(KeyFrameSlots[keyfr->y]+framenumber)) { + if (IsTheaterShape) { + return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } else { + return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } + } + } + + // calc buff size + buffsize = keyfr->width * keyfr->height; + + // get offset into data + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], 12L ); + frameflags = (char)(offset[0] >> 24); + + + if ( (frameflags & KF_KEYFRAME) ) { + + ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { // key delta or delta + + if ( (frameflags & KF_DELTA) ) { + currframe = (unsigned short)offset[1]; + + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + } + + // key frame + offcurr = offset[1] & 0x00FFFFFFL; + + // key delta + offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; + + ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + +#ifndef FIXIT_SCORE_CRASH + off16 = (unsigned long)lockptr & 0x00003FFFL; +#endif + + length = LCW_Uncompress( ptr, buffptr, buffsize ); + + if (length > buffsize) { + return(0); + } + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + if ( (frameflags & KF_DELTA) ) { + // adjust to delta after the keydelta + + currframe++; + subframe = 2; + + while (currframe <= framenumber) { + offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + currframe++; + subframe += 2; + + if ( subframe >= (SUBFRAMEOFFS - 1) && + currframe <= framenumber ) { + Mem_Copy( Add_Long_To_Pointer( dataptr, + (((unsigned long)currframe << 3) + + sizeof(KeyFrameHeaderType)) ), + &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + subframe = 0; + } + } + } + } + + + if (UseBigShapeBuffer) { + /* + ** Save the uncompressed shape data so we dont have to uncompress it + ** again next time its drawn. + ** We keep a space free before the raw shape data so we can add line + ** header info before the shape is drawn for the first time + */ + + if (IsTheaterShape) { + /* + ** Shape is a theater specific shape + */ + return_value = (unsigned long) TheaterShapeBufferPtr; + temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr) { + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart; + TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + /* + ** Align the next shape + */ + if (3 & (unsigned)TheaterShapeBufferPtr) { + TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + + } else { + + + return_value=(unsigned long)BigShapeBufferPtr; + temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr) { + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart; + BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + // Align the next shape + if (3 & (unsigned)BigShapeBufferPtr) { + BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + } + + } else { + return ((unsigned long)buffptr); + } +} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} \ No newline at end of file diff --git a/REDALERT/2SUPPORT.ASM b/REDALERT/2SUPPORT.ASM new file mode 100644 index 000000000..d3f0e3a34 --- /dev/null +++ b/REDALERT/2SUPPORT.ASM @@ -0,0 +1,561 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: F:\projects\c&c0\vcs\code\2support.asv 5.0 11 Nov 1996 09:40:36 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + GLOBAL C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mov ecx,[(GraphicViewPort ebx).GVPWidth] + add ecx,[(GraphicViewPort ebx).GVPXAdd] + add ecx,[(GraphicViewPort ebx).GVPPitch] + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + mov edx,ecx + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +if 0 + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + GLOBAL C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + GLOBAL C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + GLOBAL C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + GLOBAL C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX +endif + + + DATASEG + +TabA DD 6949350 + DD 4913933 + DD 3474675 + DD 2456966 + DD 1737338 + DD 1228483 + DD 868669 + DD 614242 + DD 434334 + DD 307121 + DD 217167 + DD 153560 + DD 108584 + DD 76780 + DD 54292 + DD 38390 + DD 27146 + DD 19195 + DD 13573 + DD 9598 + DD 6786 + DD 4799 + DD 3393 + DD 2399 + DD 1697 + DD 1200 + DD 848 + DD 600 + DD 424 + DD 300 + DD 212 + DD 150 + DD 106 + +TabB DD 154 + DD 218 + DD 309 + DD 437 + DD 618 + DD 874 + DD 1236 + DD 1748 + DD 2472 + DD 3496 + DD 4944 + DD 6992 + DD 9888 + DD 13983 + DD 19775 + DD 27967 + DD 39551 + DD 55933 + DD 79101 + DD 111866 + DD 158203 + DD 223732 + DD 316405 + DD 447465 + DD 632811 + DD 894929 + DD 1265621 + DD 1789859 + DD 2531243 + DD 3579718 + DD 5062486 + DD 7159436 + DD 10124971 + + CODESEG + +;*********************************************************************************************** +;* Square_Root -- Finds the square root of the fixed pointer parameter. * +;* * +;* INPUT: val -- The fixed point (16:16) value to find the square root of. * +;* * +;* OUTPUT: Returns with the square root of the fixed pointer parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/04/1995 JLB : Adapted. * +;*=============================================================================================*/ +;unsigned Square_Root(unsigned val); + GLOBAL C Square_Root :NEAR + PROC Square_Root C near + USES ebx,edx + + bsr ebx,eax + jz ??zero + + mul [DWORD 4*ebx + OFFSET TabA] + shrd eax,edx,10h + add eax, [4*ebx + OFFSET TabB] +??zero: + ret + + ENDP Square_Root + +;---------------------------------------------------------------------------- + + END + diff --git a/REDALERT/2TXTPRNT.ASM b/REDALERT/2TXTPRNT.ASM new file mode 100644 index 000000000..b5467effd --- /dev/null +++ b/REDALERT/2TXTPRNT.ASM @@ -0,0 +1,504 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* MCGA_Print -- Assembly MCGA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +GLOBAL C Buffer_Print : NEAR + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL C FontPtr:DWORD +GLOBAL C FontXSpacing:DWORD +GLOBAL C FontYSpacing:DWORD +GLOBAL C ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + CODESEG + + +;*************************************************************************** +;* MCGA_PRINT -- Assembly MCGA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Buffer_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch of direct draw surface + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Buffer_Print +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Get_Font_Palette_Ptr:NEAR + PROC Get_Font_Palette_Ptr C near + mov eax, OFFSET ColorXlat + ret + ENDP Get_Font_Palette_Ptr + + +END diff --git a/REDALERT/AADATA.CPP b/REDALERT/AADATA.CPP new file mode 100644 index 000000000..473826daa --- /dev/null +++ b/REDALERT/AADATA.CPP @@ -0,0 +1,655 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AADATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste* + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class* + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft obj* + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * AircraftTypeClass::LRotorData = NULL; +void const * AircraftTypeClass::RRotorData = NULL; + +// Badger bomber +static AircraftTypeClass const BadgerPlane( + AIRCRAFT_BADGER, // What kind of aircraft is this. + TXT_BADGER, // Translated text number for aircraft. + "BADR", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Photo recon plane. +static AircraftTypeClass const U2Plane( + AIRCRAFT_U2, // What kind of aircraft is this. + TXT_U2, // Translated text number for aircraft. + "U2", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Mig attack aircraft. +static AircraftTypeClass const MigPlane( + AIRCRAFT_MIG, // What kind of aircraft is this. + TXT_MIG, // Translated text number for aircraft. + "MIG", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xC0, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Yak attack aircraft. +static AircraftTypeClass const YakPlane( + AIRCRAFT_YAK, // What kind of aircraft is this. + TXT_YAK, // Translated text number for aircraft. + "YAK", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Transport helicopter. +static AircraftTypeClass const TransportHeli( + AIRCRAFT_TRANSPORT, // What kind of aircraft is this. + TXT_TRANS, // Translated text number for aircraft. + "TRAN", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + true, // Custom rotor sets for each facing? + true, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Longbow attack helicopter +static AircraftTypeClass const AttackHeli( + AIRCRAFT_LONGBOW, // What kind of aircraft is this. + TXT_HELI, // Translated text number for aircraft. + "HELI", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +// Hind +static AircraftTypeClass const OrcaHeli( + AIRCRAFT_HIND, // What kind of aircraft is this. + TXT_ORCA, // Translated text number for aircraft. + "HIND", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +/*********************************************************************************************** + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * * + * This is the constructor for the aircraft object. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass::AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder + ) : + TechnoTypeClass(RTTI_AIRCRAFTTYPE, + int(airtype), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + false, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + false, + false, + true, + true, + rotation, + SPEED_WINGED), + IsFixedWing(is_fixedwing), + IsLandable(is_landable), + IsRotorEquipped(is_rotorequipped), + IsRotorCustom(is_rotorcustom), + Type(airtype), + Mission(deforder), + Building(building), + LandingSpeed(landingspeed) +{ + /* + ** Forced aircraft overrides from the default. + */ + Speed = SPEED_WINGED; +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * * + * This will allocate an aircraft type class object from the memory pool of that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated aircraft type class object. If there * + * was insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AircraftTypeClass::operator new(size_t) +{ + return(AircraftTypes.Alloc()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * * + * This will return the aircraft type class object back to the special memory pool that * + * it was allocated from. * + * * + * INPUT: pointer -- Pointer to the aircraft type class object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::operator delete(void * pointer) +{ + AircraftTypes.Free((AircraftTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * * + * This will initialize the aircraft type class heap by pre-allocating all known aircraft * + * types. It should be called once and before the rules.ini file is processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Init_Heap(void) +{ + /* + ** These aircraft type class objects must be allocated in the exact order that they + ** are specified in the AircraftSmen enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AircraftTypeClass(TransportHeli); + new AircraftTypeClass(BadgerPlane); + new AircraftTypeClass(U2Plane); + new AircraftTypeClass(MigPlane); + new AircraftTypeClass(YakPlane); + new AircraftTypeClass(AttackHeli); + new AircraftTypeClass(OrcaHeli); +} + + +/*********************************************************************************************** + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * * + * This routine is used to convert an ASCII representation of an aircraft into the * + * matching aircraft type number. This is used by the scenario INI reader code. * + * * + * INPUT: name -- Pointer to ASCII name to translate. * + * * + * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no * + * match could be found, then AIRCRAFT_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftType AircraftTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) { + if (stricmp(As_Reference((AircraftType)classid).IniName, name) == 0) { + return(AircraftType)classid; + } + } + } + return(AIRCRAFT_NONE); +} + + +/*********************************************************************************************** + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. * + * * + * This routine is used to perform the onetime initialization of the aircraft type. This * + * includes primarily the shape and other graphic data loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This goes to disk and also must only be called ONCE. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::One_Time(void) +{ + for (int index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + AircraftTypeClass const & uclass = As_Reference((AircraftType)index); + + /* + ** Fetch the supporting data files for the unit. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + } + + LRotorData = MFCD::Retrieve("LROTOR.SHP"); + RRotorData = MFCD::Retrieve("RROTOR.SHP"); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * * + * This routine is used to create an aircraft object that matches the aircraft type. It * + * serves as a shortcut to creating an object using the "new" operator and "if" checks. * + * * + * INPUT: house -- The house owner of the aircraft that is to be created. * + * * + * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be * + * created, then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new AircraftClass(Type, house->Class->House)); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * * + * This routine is used by the scenario editor to prepare for the adding operation. It * + * builds a list of pointers to object types that can be added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Prep_For_Add(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * * + * This routine is used by the scenario editor to display a generic version of the object * + * type. This is displayed in the object selection dialog box. * + * * + * INPUT: x,y -- The coordinates to draw the aircraft at (centered). * + * * + * window -- The window to base the coordinates upon. * + * * + * house -- The owner of this generic aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} +#endif + + +/*********************************************************************************************** + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * * + * This determines the occupation list for the aircraft (if it was landed). * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. * + * * + * WARNINGS: This occupation list is only valid if the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * * + * This routine figures out the overlap list for the aircraft as if it were landed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell offset overlap list for the aircraft. * + * * + * WARNINGS: This overlap list is only valid when the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Overlap_List(void) const +{ + static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * * + * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is * + * the maximum number of passengers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips for this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Max_Pips(void) const +{ + if (PrimaryWeapon != NULL) { + // Camera weapon (ex. on the Spy plane) doesn't display any pips + if (!PrimaryWeapon->IsCamera) { + return(5); + } + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system * + * * + * This routine is used to create and place an aircraft through the normal game system. * + * Since creation of aircraft in this fashion is prohibited, this routine does nothing. * + * * + * INPUT: na * + * * + * OUTPUT: Always returns a failure code (false). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const +{ + return(false); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * * + * This routine will fetch the pixel dimensions of this aircraft type. These dimensions * + * are used to control map refresh and select box rendering. * + * * + * INPUT: width -- Reference to variable that will be filled in with aircraft width. * + * * + * height -- Reference to variable that will be filled in with aircraft height. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Dimensions(int &width, int &height) const +{ + if (Type == AIRCRAFT_BADGER) { + width = 56; + height = 56; + } else { + width = 21; + height = 20; + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * * + * This routine is used to fetch a reference to the aircraft type class object that matches * + * the aircraft type specified. * + * * + * INPUT: aircraft -- The aircraft type to fetch a reference to the type class object of. * + * * + * OUTPUT: Returns with a reference to the type class object of this aircraft type. * + * * + * WARNINGS: Be sure that the aircraft type specified is legal. Illegal values will result * + * in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass & AircraftTypeClass::As_Reference(AircraftType aircraft) +{ + return(*AircraftTypes.Ptr(aircraft)); +} diff --git a/REDALERT/ABSTRACT.CPP b/REDALERT/ABSTRACT.CPP new file mode 100644 index 000000000..772ee4817 --- /dev/null +++ b/REDALERT/ABSTRACT.CPP @@ -0,0 +1,213 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ABSTRACT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * AbstractClass::Distance -- Determines distance to target. * + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * * + * This debug only routine will display various information about this abstract class * + * object to the monochrome screen specified. * + * * + * INPUT: mono -- Pointer to the monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +void AbstractClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(11, 5);mono->Printf("%08X", As_Target()); + mono->Set_Cursor(20, 1);mono->Printf("%08X", Coord); + mono->Set_Cursor(29, 1);mono->Printf("%3d", Height); + if (Owner() != HOUSE_NONE) { + mono->Set_Cursor(1, 3); + mono->Printf("%-18s", Text_String(HouseTypeClass::As_Reference(Owner()).FullName)); + } +} +#endif + + +/*********************************************************************************************** + * AbstractClass::Distance -- Determines distance to target. * + * * + * This will determine the distance (direct line) to the target. The distance is in * + * 'leptons'. This routine is typically used for weapon range checks. * + * * + * INPUT: target -- The target to determine range to. * + * * + * OUTPUT: Returns with the range to the specified target (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1994 JLB : Created. * + *=============================================================================================*/ +int AbstractClass::Distance(TARGET target) const +{ + /* + ** Should subtract a fudge-factor distance for building targets. + */ + BuildingClass * obj = As_Building(target); + int dist = Distance(As_Coord(target)); + + /* + ** If the object is a building the adjust it by the average radius + ** of the object. + */ + if (obj && obj->IsActive) { + dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + if (dist < 0) dist = 0; + } + + /* + ** Return the distance to the target + */ + return(dist); +} + + +/*********************************************************************************************** + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * * + * This is the constructor for AbstractTypeClass objects. It initializes the INI name and * + * the text name for this object type. * + * * + * INPUT: name -- Text number for the full name of the object. * + * * + * ini -- The ini name for this object type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +AbstractTypeClass::AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini) : + RTTI(rtti), + ID(id), + FullName(name) +{ + strncpy((char *)IniName, ini, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; +} + + +/*********************************************************************************************** + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * * + * This routine is called when the placement coordinate should be fixed up according * + * to any special rules specific to this object type. At this level, no transformation * + * occurs. Derived classes will transform the coordinate as necessary. * + * * + * INPUT: coord -- The proposed coordinate that this object type will be placed down at. * + * * + * OUTPUT: Returns with the adjusted coordinate that the object should be placed down at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return(coord); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * * + * This routine is used to fetch the full name of this object type. The name value * + * returned is actually the index number into the text array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name index number for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Full_Name(void) const +{ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == ((RTTI+1) * 100) + ID) { + return(-(index+1)); + } + } +#endif + return(FullName); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * * + * This returns a bit flag that indicates which houses are allowed to own this object * + * type. At this level, all houses have ownership rights. This routine will be overridden * + * by object types that restrict ownership. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a bit flag indicating which houses have ownership rights. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Get_Ownable(void) const +{ + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + + diff --git a/REDALERT/ABSTRACT.H b/REDALERT/ABSTRACT.H new file mode 100644 index 000000000..b85162f5e --- /dev/null +++ b/REDALERT/ABSTRACT.H @@ -0,0 +1,144 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ABSTRACT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : January 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +COORDINATE As_Coord(TARGET target); + +class AbstractTypeClass; + +/* +** This class is the base class for all game objects that have an existence on the +** battlefield. +*/ +class AbstractClass +{ + public: + + /* + ** This specifies the type of object and the unique ID number + ** associated with it. The ID number happens to match the index into + ** the object heap appropriate for this object type. + */ + RTTIType RTTI; + int ID; + + /* + ** The coordinate location of the unit. For vehicles, this is the center + ** point. For buildings, it is the upper left corner. + */ + COORDINATE Coord; + + /* + ** This is the height of the object above ground (expressed in leptons). + */ + int Height; + + /* + ** The actual object ram-space is located in arrays in the data segment. This flag + ** is used to indicate which objects are free to be reused and which are currently + ** in use by the game. + */ + unsigned IsActive:1; + + /* + ** A flag to indicate that this object was recently created. Since an object's allocation is just a matter of whether + ** the IsActive flag is set, during a logic frame an object with a given ID could be 'deleted' then reallocated + ** as a different type of object in a different location. This flag lets us know that this happened. ST - 8/19/2019 5:33PM + */ + unsigned IsRecentlyCreated:1; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + AbstractClass(RTTIType rtti, int id) : RTTI(rtti), ID(id), Coord(0xFFFFFFFFL), Height(0) {}; + AbstractClass(NoInitClass const & x) {x();}; + virtual ~AbstractClass(void) {}; + + /* + ** Query functions. + */ + virtual char const * Name(void) const {return("");} + virtual HousesType Owner(void) const {return HOUSE_NONE;}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + RTTIType What_Am_I(void) const {return(RTTI);}; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass * mono) const; + #endif + + /* + ** Coordinate query support functions. + */ + virtual COORDINATE Center_Coord(void) const {return Coord;}; + virtual COORDINATE Target_Coord(void) const {return Coord;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());}; + DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);}; + DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));}; + DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);}; + int Distance(TARGET target) const; + int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);}; + int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());}; + + /* + ** Object entry and exit from the game system. + */ + virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;}; + + /* + ** AI. + */ + virtual void AI(void) {}; + + /* + ** Set the new recently created flag every time the active flag is set. ST - 8/19/2019 5:41PM + */ + void Set_Active(void) {IsActive = true; IsRecentlyCreated = true;} + +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/ADATA.CPP b/REDALERT/ADATA.CPP new file mode 100644 index 000000000..0d7f38dd0 --- /dev/null +++ b/REDALERT/ADATA.CPP @@ -0,0 +1,2553 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ADATA.CPP 3 3/07/97 4:27p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static AnimTypeClass const AtomBomb( + ANIM_ATOM_BLAST, // Animation number. + "ATOMSFX", // Data name of animation. + 72, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 75, // Virtual stages + 0x300 // Virtual scale +); + +static AnimTypeClass const SputDoor( + ANIM_SPUTDOOR, // Animation number. + "SPUTDOOR", // Data name of animation. + 42, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +// Electrocution death anim from Tesla coil +static AnimTypeClass const ElectricDie( + ANIM_ELECT_DIE, // Animation number. + "ELECTRO", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +// Electrocution death anim from Tesla coil for dog +static AnimTypeClass const DogElectricDie( + ANIM_DOG_ELECT_DIE, // Animation number. + "ELECTDOG", // Data name of animation. + 17, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +static AnimTypeClass const SAMN( + ANIM_SAM_N, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNW( + ANIM_SAM_NW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 22, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*1, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMW( + ANIM_SAM_W, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 40, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*2, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSW( + ANIM_SAM_SW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*3, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMS( + ANIM_SAM_S, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 76, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*4, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSE( + ANIM_SAM_SE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 94, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*5, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAME( + ANIM_SAM_E, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 112, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*6, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNE( + ANIM_SAM_NE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 130, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*7, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const LZSmoke( + ANIM_LZ_SMOKE, // Animation number. + "SMOKLAND", // Data name of animation. + 32, // Maximum dimension of animation. + 72, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 72, // Loop start frame number. + 91, // Ending frame of loop back. + -1, // Number of animation stages. + 255, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations. Primarily used on trees and buildings. +*/ +static AnimTypeClass const BurnSmall( + ANIM_BURN_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnMed( + ANIM_BURN_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnBig( + ANIM_BURN_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations that trail into smoke. Used for +** buildings and the gunboat. +*/ +static AnimTypeClass const OnFireSmall( + ANIM_ON_FIRE_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_SMOKE_M +); +static AnimTypeClass const OnFireMed( + ANIM_ON_FIRE_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_SMALL +); +static AnimTypeClass const OnFireBig( + ANIM_ON_FIRE_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_MED +); +static AnimTypeClass const Parachute( + ANIM_PARACHUTE, // Animation number. + "PARACH", // Data name of animation. + 32, // Maximum dimension of animation. + 15, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ParaBomb( + ANIM_PARA_BOMB, // Animation number. + "PARABOMB", // Data name of animation. + 32, // Maximum dimension of animation. + 8, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FBall1( + ANIM_FBALL1, // Animation number. + "FBALL1", // Data name of animation. + 67, // Maximum dimension of animation. + 6, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag1( + ANIM_FRAG1, // Animation number. + "FRAG1", // Data name of animation. + 45, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM30, // Sound effect to play. + ANIM_NONE, + 29 // Virtual stages +); + +static AnimTypeClass const VehHit1( + ANIM_VEH_HIT1, // Animation number. + "VEH-HIT1", // Data name of animation. + 30, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit2( + ANIM_VEH_HIT2, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit3( + ANIM_VEH_HIT3, // Animation number. + "VEH-HIT3", // Data name of animation. + 19, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const ArtExp1( + ANIM_ART_EXP1, // Animation number. + "ART-EXP1", // Data name of animation. + 41, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM22, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm1( + ANIM_NAPALM1, // Animation number. + "NAPALM1", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm2( + ANIM_NAPALM2, // Animation number. + "NAPALM2", // Data name of animation. + 41, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm3( + ANIM_NAPALM3, // Animation number. + "NAPALM3", // Data name of animation. + 78, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_LAUNCH, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokePuff( + ANIM_SMOKE_PUFF, // Animation number. + "SMOKEY", // Data name of animation. + 24, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FireBallFade( + ANIM_FBALL_FADE, // Animation number. + "FB2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Piff( + ANIM_PIFF, // Animation number. + "PIFF", // Data name of animation. + 13, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const PiffPiff( + ANIM_PIFFPIFF, // Animation number. + "PIFFPIFF", // Data name of animation. + 20, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire3( + ANIM_FIRE_SMALL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_SMALL_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire3Virtual( + ANIM_FIRE_SMALL_VIRTUAL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire1( + ANIM_FIRE_MED2, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_MED2_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire1Virtual( + ANIM_FIRE_MED2_VIRTUAL, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire4( + ANIM_FIRE_TINY, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_TINY_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire4Virtual( + ANIM_FIRE_TINY_VIRTUAL, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire2( + ANIM_FIRE_MED, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_MED_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire2Virtual( + ANIM_FIRE_MED_VIRTUAL, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const OilFieldBurn( + ANIM_OILFIELD_BURN, // Animation number. + "FLMSPT", // Data name of animation. + 42, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 33, // Loop start frame number. + 99, // Ending frame of loop back. + 66, // Number of animation stages. + 65535, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Gunfire( + ANIM_MUZZLE_FLASH, // Animation number. + "GUNFIRE", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 1, // Number of animation stages. + 1, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 10 // Virtual stages +); + +static AnimTypeClass const SmokeM( + ANIM_SMOKE_M, // Animation number. + "SMOKE_M", // Data name of animation. + 28, // Maximum dimension of animation. + 30, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 67, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 6, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 105 // Virtual stages +); + +/* +** Mini-gun fire effect -- used by guard towers. +*/ +static AnimTypeClass const GUNN( + ANIM_GUN_N, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNW( + ANIM_GUN_NW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 6, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNW( + ANIM_GUN_W, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 12, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSW( + ANIM_GUN_SW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNS( + ANIM_GUN_S, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSE( + ANIM_GUN_SE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 30, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNE( + ANIM_GUN_E, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 36, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNE( + ANIM_GUN_NE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 42, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const CDeviator( + ANIM_CRATE_DEVIATOR, // Animation number. + "DEVIATOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateArmor( + ANIM_CRATE_ARMOR, // Animation number. + "ARMOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateSpeed( + ANIM_CRATE_SPEED, // Animation number. + "SPEED", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateFPower( + ANIM_CRATE_FPOWER, // Animation number. + "FPOWER", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateTQuake( + ANIM_CRATE_TQUAKE, // Animation number. + "TQUAKE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CDollar( + ANIM_CRATE_DOLLAR, // Animation number. + "DOLLAR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEarth( + ANIM_CRATE_EARTH, // Animation number. + "EARTH", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEmpulse( + ANIM_CRATE_EMPULSE, // Animation number. + "EMPULSE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CInvun( + ANIM_CRATE_INVUN, // Animation number. + "INVUN", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMine( + ANIM_CRATE_MINE, // Animation number. + "MINE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CRapid( + ANIM_CRATE_RAPID, // Animation number. + "RAPID", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CStealth( + ANIM_CRATE_STEALTH, // Animation number. + "STEALTH2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ChronoBox( + ANIM_CHRONO_BOX, // Animation number. + "CHRONBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const GPSBox( + ANIM_GPS_BOX, // Animation number. + "GPSBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const InvulBox( + ANIM_INVUL_BOX, // Animation number. + "INVULBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ParaBox( + ANIM_PARA_BOX, // Animation number. + "PARABOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const SonarBox( + ANIM_SONAR_BOX, // Animation number. + "SONARBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CMissile( + ANIM_CRATE_MISSILE, // Animation number. + "MISSILE2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const MoveFlash( + ANIM_MOVE_FLASH, // Animation number. + "MOVEFLSH", // Data name of animation. + 24, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + true, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const Corpse1( + ANIM_CORPSE1, // Animation number. + "CORPSE1", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse2( + ANIM_CORPSE2, // Animation number. + "CORPSE2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse3( + ANIM_CORPSE3, // Animation number. + "CORPSE3", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Twinkle1( + ANIM_TWINKLE1, // Animation number. + "TWINKLE1", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle2( + ANIM_TWINKLE2, // Animation number. + "TWINKLE2", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle3( + ANIM_TWINKLE3, // Animation number. + "TWINKLE3", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Flak( + ANIM_FLAK, // Animation number. + "FLAK", // Data name of animation. + 8, // Maximum dimension of animation. + 7, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 17 // Virtual stages +); +static AnimTypeClass const WaterExp1( + ANIM_WATER_EXP1, // Animation number. + "H2O_EXP1", // Data name of animation. + 64, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp2( + ANIM_WATER_EXP2, // Animation number. + "H2O_EXP2", // Data name of animation. + 40, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp3( + ANIM_WATER_EXP3, // Animation number. + "H2O_EXP3", // Data name of animation. + 32, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); + + +static AnimTypeClass const MineExp1( + ANIM_MINE_EXP1, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_MINEBLOW, // Sound effect to play. + ANIM_NONE +); + +#ifdef FIXIT_ANTS +static AnimTypeClass const Ant1Death( + ANIM_ANT1_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE1" +); + +static AnimTypeClass const Ant2Death( + ANIM_ANT2_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE2" +); + +static AnimTypeClass const Ant3Death( + ANIM_ANT3_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE3" +); +#endif + +/*********************************************************************************************** + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * * + * This is the constructor for static objects that elaborate the various animation types * + * allowed in the game. Each animation in the game is of one of these types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass::AnimTypeClass( + AnimType anim, + char const * name, + int size, + int biggest, + bool istheater, + bool isnormal, + bool iswhitetrans, + bool isscorcher, + bool iscrater, + bool issticky, + bool ground, + bool istrans, + bool isflame, + fixed damage, + int delaytime, + int start, + int loopstart, + int loopend, + int stages, + int loops, + VocType soundid, + AnimType chainto, + int virtualstages, + int virtualscale, + char const * virtualname, + AnimType virtualanim) : + ObjectTypeClass(RTTI_ANIMTYPE, + int(anim), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsNormalized(isnormal), + IsGroundLayer(ground), + IsTranslucent(istrans), + IsWhiteTrans(iswhitetrans), + IsFlameThrower(isflame), + IsScorcher(isscorcher), + IsCraterForming(iscrater), + IsSticky(issticky), + IsTheater(istheater), + Type(anim), + Size(size), + Biggest(biggest), + Damage(damage), + Delay(delaytime), + Start(start), + LoopStart(loopstart), + LoopEnd(loopend), + Stages(stages), + Loops(loops), + Sound(soundid), + ChainTo(chainto), + VirtualStages(virtualstages), + VirtualScale(virtualscale), + VirtualName(virtualname), + VirtualAnim(virtualanim) +{ +} + + +/*********************************************************************************************** + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * * + * This routine will allocate an animation type class object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated anim type object. If no anim type * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AnimTypeClass::operator new(size_t) +{ + return(AnimTypes.Alloc()); +} + + +/*********************************************************************************************** + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * * + * This will return the anim type class object back to the memory pool from whence it was * + * previously allocated. * + * * + * INPUT: pointer -- Pointer to the anim type class object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::operator delete(void * pointer) +{ + AnimTypes.Free((AnimTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * * + * This routine is called to initialize the animation type class heap. It allocates all * + * known animation types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init_Heap(void) +{ + /* + ** These anim type class objects must be allocated in the exact order that they + ** are specified in the AnimType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AnimTypeClass(FBall1); + new AnimTypeClass(FireBallFade); + new AnimTypeClass(Frag1); + new AnimTypeClass(VehHit1); + new AnimTypeClass(VehHit2); + new AnimTypeClass(VehHit3); + new AnimTypeClass(ArtExp1); + new AnimTypeClass(Napalm1); + new AnimTypeClass(Napalm2); + new AnimTypeClass(Napalm3); + new AnimTypeClass(SmokePuff); + new AnimTypeClass(Piff); + new AnimTypeClass(PiffPiff); + new AnimTypeClass(Fire3); + new AnimTypeClass(Fire2); + new AnimTypeClass(Fire1); + new AnimTypeClass(Fire4); + new AnimTypeClass(Gunfire); + new AnimTypeClass(SmokeM); + new AnimTypeClass(BurnSmall); + new AnimTypeClass(BurnMed); + new AnimTypeClass(BurnBig); + new AnimTypeClass(OnFireSmall); + new AnimTypeClass(OnFireMed); + new AnimTypeClass(OnFireBig); + new AnimTypeClass(SAMN); + new AnimTypeClass(SAMNE); + new AnimTypeClass(SAME); + new AnimTypeClass(SAMSE); + new AnimTypeClass(SAMS); + new AnimTypeClass(SAMSW); + new AnimTypeClass(SAMW); + new AnimTypeClass(SAMNW); + new AnimTypeClass(GUNN); + new AnimTypeClass(GUNNE); + new AnimTypeClass(GUNE); + new AnimTypeClass(GUNSE); + new AnimTypeClass(GUNS); + new AnimTypeClass(GUNSW); + new AnimTypeClass(GUNW); + new AnimTypeClass(GUNNW); + new AnimTypeClass(LZSmoke); + new AnimTypeClass(CDeviator); + new AnimTypeClass(CDollar); + new AnimTypeClass(CEarth); + new AnimTypeClass(CEmpulse); + new AnimTypeClass(CInvun); + new AnimTypeClass(CMine); + new AnimTypeClass(CRapid); + new AnimTypeClass(CStealth); + new AnimTypeClass(CMissile); + new AnimTypeClass(MoveFlash); + new AnimTypeClass(OilFieldBurn); + new AnimTypeClass(ElectricDie); + new AnimTypeClass(Parachute); + new AnimTypeClass(DogElectricDie); + new AnimTypeClass(Corpse1); + new AnimTypeClass(Corpse2); + new AnimTypeClass(Corpse3); + new AnimTypeClass(SputDoor); + new AnimTypeClass(AtomBomb); + new AnimTypeClass(ChronoBox); + new AnimTypeClass(GPSBox); + new AnimTypeClass(InvulBox); + new AnimTypeClass(ParaBox); + new AnimTypeClass(SonarBox); + new AnimTypeClass(Twinkle1); + new AnimTypeClass(Twinkle2); + new AnimTypeClass(Twinkle3); + new AnimTypeClass(Flak); + new AnimTypeClass(WaterExp1); + new AnimTypeClass(WaterExp2); + new AnimTypeClass(WaterExp3); + new AnimTypeClass(CrateArmor); + new AnimTypeClass(CrateSpeed); + new AnimTypeClass(CrateFPower); + new AnimTypeClass(CrateTQuake); + new AnimTypeClass(ParaBomb); + new AnimTypeClass(MineExp1); +#ifdef FIXIT_ANTS + new AnimTypeClass(Ant1Death); + new AnimTypeClass(Ant2Death); + new AnimTypeClass(Ant3Death); +#endif + new AnimTypeClass(Fire3Virtual); + new AnimTypeClass(Fire2Virtual); + new AnimTypeClass(Fire1Virtual); + new AnimTypeClass(Fire4Virtual); +} + +/*********************************************************************************************** + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * * + * This will load the animation shape data. It is called by the game initialization * + * process. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called ONLY once. * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::One_Time(void) +{ + for (int index = ANIM_FIRST; index < ANIM_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + AnimTypeClass const & anim = As_Reference((AnimType)index); + + if (!anim.IsTheater) { + + _makepath(fullname, NULL, NULL, As_Reference((AnimType)index).IniName, ".SHP"); + + #ifndef NDEBUG + RawFileClass file(fullname); + if (file.Is_Available()) { + ((void const *&)As_Reference((AnimType)index).ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)As_Reference((AnimType)index).ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)As_Reference((AnimType)index).ImageData) = MFCD::Retrieve(fullname); + #endif + } + } +} + + +/*********************************************************************************************** + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * * + * This routine will examine all the animation types and for any that are theater * + * specific, it will fetch a pointer to the artwork appropriate for the theater specified. * + * * + * INPUT: theater -- The theater to align the animation artwork with. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine when the theater changes. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (int index = ANIM_FIRST; index < ANIM_COUNT; index++) { + AnimTypeClass const & anim = As_Reference((AnimType)index); + + if (anim.IsTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + _makepath(fullname, NULL, NULL, anim.IniName, Theaters[theater].Suffix); + ((void const *&)anim.ImageData) = MFCD::Retrieve(fullname); + } + } + } +} + + +/*********************************************************************************************** + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * * + * This will convert the animation type specified into a text name. This name can be used * + * for uniquely identifying the animation. * + * * + * INPUT: anim -- The anim type to convert to a text string. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that identifies this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Anim_Name(AnimType anim) +{ + if (anim == ANIM_NONE) return(""); + + return(AnimTypeClass::As_Reference(anim).IniName); +} + + +/*********************************************************************************************** + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * * + * This routine will convert the animation type specified into a reference to the * + * animation type class object. * + * * + * INPUT: type -- The animation type to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the animation type class object. * + * * + * WARNINGS: Be sure that the animation type specified is legal. If it isn't then the * + * results of this routine are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass & AnimTypeClass::As_Reference(AnimType type) +{ + return(* AnimTypes.Ptr(type)); +} + diff --git a/REDALERT/ADPCM.CPP b/REDALERT/ADPCM.CPP new file mode 100644 index 000000000..763438cb5 --- /dev/null +++ b/REDALERT/ADPCM.CPP @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "function.h" + +extern "C" { +#include "soscomp.h" +#include "itable.cpp" +#include "dtable.cpp" + + +void sosCODECInitStream(_SOS_COMPRESS_INFO* info) +{ + info->dwSampleIndex = 0; + info->dwPredicted = 0; +} + + +unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO* info, unsigned long numbytes) +{ + unsigned long token; + long sample; + unsigned int fastindex; + unsigned char *inbuff; + unsigned short *outbuff; + + inbuff = (unsigned char *)info->lpSource; + outbuff = (unsigned short *)info->lpDest; + + // Preload variables before the big loop + fastindex = (unsigned int)info->dwSampleIndex; + sample = info->dwPredicted; + + if (!numbytes) + goto SkipLoop; + + do { + // First nibble + token = *inbuff++; + fastindex += token & 0x0f; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + + // Second nibble + fastindex += token >> 4; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + } while(--numbytes); + +SkipLoop: + + // Put local vars back + info->dwSampleIndex = (unsigned long)fastindex; + info->dwPredicted = sample; + return(numbytes << 2); +} + +} diff --git a/REDALERT/AIRCRAFT.CPP b/REDALERT/AIRCRAFT.CPP new file mode 100644 index 000000000..7722529e2 --- /dev/null +++ b/REDALERT/AIRCRAFT.CPP @@ -0,0 +1,4437 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AIRCRAFT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * AircraftClass::Mission_Move -- Handles movement mission. * + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * AircraftClass::New_LZ -- Find a good landing zone. * + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * AircraftClass::Response_Move -- Gives audio response to move request. * + * AircraftClass::Response_Select -- Gives audio response when selected. * + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::operator delete -- Deletes the aircraft object. * + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * * + * Examines the specified object to see if it qualifies to be a civilian evacuation. This * + * can only occur if it is a civilian (or Tanya) and the special evacuation flag has been * + * set in the scenario control structure. * + * * + * INPUT: candidate -- Candidate object to examine for civilian evacuation legality. * + * * + * OUTPUT: bool; Is the specified object considered a civilian that must be auto-evacuated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Counts_As_Civ_Evac(ObjectClass const * candidate) +{ + /* + ** If the candidate pointer is missing, then return with failure code. + */ + if (candidate == NULL) return(false); + + /* + ** Only infantry objects can be considered for civilian evacuation action. + */ + if (candidate->What_Am_I() != RTTI_INFANTRY) return(false); + + /* + ** Working infantry object pointer. + */ + InfantryClass const * inf = (InfantryClass const *)candidate; + + /* + ** Certain infantry types will always be considered a civilian evacuation candidate. These + ** include the special one-time infantry that appear in some missions. + */ + if (*inf == INFANTRY_EINSTEIN || *inf == INFANTRY_GENERAL || *inf == INFANTRY_DELPHI || *inf == INFANTRY_CHAN) return(true); + + /* + ** Consider Tanya to be part of the civilian evacuation logic if the scenario is + ** specially flagged for this. + */ + if (Scen.IsTanyaEvac && *inf == INFANTRY_TANYA) return(true); + + /* + ** If the infantry is not a civilian, then it isn't allowed to be a civilian evacuation. + */ + if (!inf->Class->IsCivilian) return(false); + + /* + ** Technicians look like civilians, but are not considered a legal evacuation candidate. + */ + if (inf->IsTechnician) return(false); + + /* + ** All tests pass, so return the success of the infantry as a civilian evacuation candidate. + */ + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * * + * This routine will allocate an aircraft object from the free aircraft object pool. If * + * there are no free object available, then this routine will fail (return NULL). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were * + * available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void * AircraftClass::operator new(size_t) +{ + void * ptr = Aircraft.Allocate(); + if (ptr) { + ((AircraftClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * AircraftClass::operator delete -- Deletes the aircraft object. * + * * + * This routine will return the aircraft object back to the free aircraft object pool. * + * * + * INPUT: ptr -- Pointer to the aircraft object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::operator delete(void * ptr) +{ + if (ptr) { + ((AircraftClass *)ptr)->IsActive = false; + } + Aircraft.Free((AircraftClass *)ptr); +} + + +/*********************************************************************************************** + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * * + * This routine is the constructor for aircraft objects. An aircraft object can be * + * created and possibly placed into the game system by this routine. * + * * + * INPUT: classid -- The type of aircraft to create. * + * * + * house -- The owner of this aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftClass::AircraftClass(AircraftType classid, HousesType house) : + FootClass(RTTI_AIRCRAFT, Aircraft.ID(this), house), + Class(AircraftTypes.Ptr((int)classid)), + SecondaryFacing(PrimaryFacing), + Passenger(false), + IsLanding(false), + IsTakingOff(false), + IsHovering(false), + Jitter(0), + SightTimer(0), + AttacksRemaining(1) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + Height = FLIGHT_LEVEL; + Strength = Class->MaxStrength; + NavCom = TARGET_NONE; + + /* + ** Keep count of the number of units created. Dont track cargo planes as they are created + ** automatically, not bought. + */ +// if (/*classid != AIRCRAFT_CARGO && */ Session.Type == GAME_INTERNET) { +// House->AircraftTotals->Increment_Unit_Total((int)classid); +// } + +} + + +/*********************************************************************************************** + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * * + * This routine is used to transition the aircraft from the limbo to the non limbo state. * + * It occurs when the aircraft is placed on the map for whatever reason. When it is * + * unlimboed, only then will normal game processing recognize it. * + * * + * INPUT: coord -- The coordinate that the aircraft should appear at. * + * * + * dir -- The direction it should start facing. * + * * + * strength (optional) -- sets initial strength * + * * + * mission (optional) -- sets initial mission * + * * + * OUTPUT: bool; Was the aircraft unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (FootClass::Unlimbo(coord, dir)) { + + if (*this == AIRCRAFT_BADGER || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera)) { + IsALoaner = true; + } + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->AScan |= (1L << Class->Type); + House->ActiveAScan |= (1L << Class->Type); + + /* + ** Hack it so that aircraft that are both passenger and cargo carrying + ** will carry passengers at the expense of ammo. + */ + if (Is_Something_Attached()) { + Ammo = 0; + Passenger = true; + } + + /* + ** Forces the body of the helicopter to face the correct direction. + */ + SecondaryFacing = dir; + + /* + ** Start rotor animation. + */ + if (!Class->IsFixedWing) { + Set_Rate(1); + Set_Stage(0); + } + + /* + ** When starting at flight level, then give it speed. When landed + ** then it must be stationary. + */ + if (Height == FLIGHT_LEVEL) { + Set_Speed(0xFF); + } else { + Set_Speed(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * * + * This will determine what shape number to use for the aircraft in its current state. * + * The shape number can be used for drawing or determine shape rectangle size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the aircraft body. * + * * + * WARNINGS: Some aircraft, particularly helicopters, require other shapes attached to it. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Shape_Number(void) const +{ + int shapenum = 0; + + switch (Class->Rotation) { + case 32: + shapenum = UnitClass::BodyShape[Dir_To_32(SecondaryFacing)]; + break; + + case 16: + shapenum = UnitClass::BodyShape[Dir_To_16(SecondaryFacing)*2]/2; + break; + + case 8: + shapenum = UnitClass::BodyShape[Dir_To_8(SecondaryFacing)*4]/4; + break; + + default: + break; + } + + /* + ** If there is a door on this aircraft (Chinook), then adjust the + ** shape number to match the door open state. + */ + if (!Is_Door_Closed()) { + shapenum = Class->Rotation + Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * * + * This routine is used to display the aircraft object at the coordinates specified. * + * The tactical map display uses this routine for all aircraft rendering. * + * * + * INPUT: x,y -- The coordinates to render the aircraft at. * + * * + * window -- The window that the coordinates are based upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (!shapefile) return; + + int shapenum = Shape_Number(); + + /* + ** Certain aircraft use algorithmic rotation for some stages. Set the + ** rotation value accordingly. A rotation of DIR_N means no rotation at all. + */ + DirType rotation = DIR_N; + if (Class->Rotation == 16) { + rotation = DirType(Rotation16[SecondaryFacing]); + } + +#ifdef TOFIX + /* + ** The orca attack helicopter uses a special shape set when it is travelling + ** forward above a certain speed. + */ + if (*this == AIRCRAFT_HIND && Get_Speed() >= MPH_MEDIUM_FAST) { + shapenum += Class->Rotation; + } +#endif + + /* + ** Helicopters that are flying have a "bobbing" effect. + */ + int jitter = 0; + if (Height == FLIGHT_LEVEL && Get_Speed() < 3) { + static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0}; + jitter = _jitter[::Frame % 16]; + } + + // Virtual window needs to draw the body first so it's considered the primary object and the shadow is a sub-object + if (window == WINDOW_VIRTUAL) { + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation); + + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL); + } + } else { + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL); + } + + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation); + } + + /* + ** If this aircraft is equipped with rotor blades, then draw them at this time. + */ + if (Class->IsRotorEquipped) { + Draw_Rotors(x, y+jitter, window); + } + + /* + ** This draws any overlay graphics on the aircraft. + */ + FootClass::Draw_It(x, y-Lepton_To_Pixel(Height), window); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * * + * This routine will draw rotor blades on the aircraft. It is presumed that the aircraft * + * has already been drawn at the X and Y pixel coordinates specified. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to draw the rotor blades. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_Rotors(int x, int y, WindowNumberType window) const +{ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + int shapenum; + + /* + ** The rotor shape number depends on whether the helicopter is idling + ** or not. A landed helicopter uses slow moving "idling" blades. + */ + if (Height == 0) { + shapenum = (Fetch_Stage()%8)+4; + flags = flags | SHAPE_GHOST; + } else { + shapenum = Fetch_Stage()%4; + flags = flags | SHAPE_FADING|SHAPE_PREDATOR; + } + + if (*this == AIRCRAFT_TRANSPORT) { + int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9}; + + /* + ** Dual rotors offset along flight axis. + */ + short xx = x; + short yy = y-Lepton_To_Pixel(Height); + FacingType face = Dir_Facing(SecondaryFacing); + Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]); + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "RROTOR", AircraftTypeClass::RRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2); + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "LROTOR", AircraftTypeClass::LRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + } else { + + /* + ** Single rotor centered about shape. + */ + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "RROTOR", AircraftTypeClass::RRotorData, shapenum, x, ((y-Lepton_To_Pixel(Height))-2), window, flags, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * * + * This routine is used to read the aircraft object data from the INI file buffer * + * specified. This is used by the scenario loader code to interpret the INI file and * + * create the specified objects therein. * + * * + * INPUT: buffer -- Pointer to the INI buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Read_INI(CCINIClass & ini) +{ + AircraftClass * air; // Working unit pointer. + HousesType inhouse; // Unit house. + AircraftType classid; // Unit class. + char buf[128]; + + int counter = ini.Entry_Count(INI_Name()); + for (int index = 0; index < counter; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)-1); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = AircraftTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != AIRCRAFT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + air = new AircraftClass(classid, inhouse); + if (air) { + COORDINATE coord; + int strength; + DirType dir; + + /* + ** Read the raw data. + */ + char * token = strtok(NULL, ","); + if (token) { + strength = atoi(token); + } else { + strength = 0; + } + + token = strtok(NULL, ","); + if (token) { + coord = Cell_Coord((CELL)atoi(token)); + } else { + coord = 0xFFFFFFFFL; + } + + token = strtok(NULL, ","); + if (token) { + dir = (DirType)atoi(token); + } else { + dir = DIR_N; + } + + if (!Map.In_Radar(Coord_Cell(coord))) { + delete air; + } else { + + air->Strength = (int)air->Class->MaxStrength * fixed(strength, 256); // Cast this to (int). ST - 5/8/19 + if (air->Unlimbo(coord, dir)) { + air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r"))); + } else { + delete air; + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * * + * Hunt AI consists of finding a target and attacking it. If there is no target assigned * + * and this unit doesn't automatically hunt for more targets, then it will change * + * mission to a more passive (land and await further orders) type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of ticks before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Hunt(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + if (TarCom != NavCom) { + Assign_Destination(TarCom); + } + + enum { + LOOK_FOR_TARGET, + TAKE_OFF, + FLY_TO_TARGET, + DROP_BOMBS, + REGROUP + }; + switch (Status) { + + /* + ** Acquiring target stage. + */ + case LOOK_FOR_TARGET: + if (Target_Legal(TarCom)) { + Status = TAKE_OFF; + return(1); + } else { + if (!Team.Is_Valid()) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + + /* + ** If there is no target, then this aircraft should just do its normal thing. + */ + if (!Target_Legal(TarCom) && !Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + } + break; + + /* + ** Make the aircraft take off from the airstrip. + */ + case TAKE_OFF: + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact()/*KO && Map[Coord].Cell_Building() == Contact_With_Whom()*/) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_TARGET; + } + return(1); + + /* + ** Homing in on target stage. + */ + case FLY_TO_TARGET: + switch (Can_Fire(TarCom, 0)) { + case FIRE_FACING: + /* + ** Catch the case where it is tightly circling the target. In that + ** case, increase the delay so that it has a chance to fly away and + ** break the circle cycle. + */ + if (In_Range(TarCom, 0) || Passenger) { + return(TICKS_PER_SECOND * 2); + } + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_AMMO: + Status = REGROUP; + break; + + case FIRE_CANT: + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_OK: + Status = DROP_BOMBS; + return(1); + + default: + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + } + return(TICKS_PER_SECOND/2); + + /* + ** Dropping a stream of bombs phase. + */ + case DROP_BOMBS: + TARGET targ; + switch (Can_Fire(TarCom, 0)) { + case FIRE_OK: + targ = ::As_Target(Coord_Move(Center_Coord(), SecondaryFacing, Weapon_Range(0)-0x0200)); + if (Class->PrimaryWeapon != NULL) { + if (Class->PrimaryWeapon->IsCamera) { + Status = REGROUP; + } else { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** Force the target to be the actual target if this aircraft is + ** equipped with homing projectile. + */ + if (Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->ROT > 0) { + targ = TarCom; + } + } + Fire_At(targ, 0); + if (Class->Is_Two_Shooter()) { + Fire_At(targ, 0); + } + return(Arm); + + case FIRE_RANGE: + case FIRE_FACING: + Status = FLY_TO_TARGET; + return(TICKS_PER_SECOND*4); + + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_CANT: + Status = REGROUP; + break; + + case FIRE_AMMO: + AttacksRemaining--; + Status = REGROUP; + break; + + default: + break; + } + return(1); + + /* + ** Pull away to regroup for possibly another attack or a retreat. + */ + case REGROUP: + if (Ammo == 0) { + AttacksRemaining = 0; + if (Team.Is_Valid()) Team->Remove(this); + Enter_Idle_Mode(); + } + + if (Mission == MISSION_ATTACK || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) || (!AttacksRemaining && !Is_Something_Attached())) { + + if (IsALoaner) { + if (Team) Team->Remove(this); + Assign_Mission(MISSION_RETREAT); + Commence(); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + Commence(); + } else { + Status = LOOK_FOR_TARGET; + } + break; + + default: + break; + } + } else { + if (!Ammo) { + if (Team) Team->Remove(this); + Enter_Idle_Mode(); + } else { + if (!Target_Legal(TarCom)) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + if (!Target_Legal(TarCom)) { + Enter_Idle_Mode(); + return(1); + } + } + + Assign_Mission(MISSION_ATTACK); + return(1); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * * + * This handles the non-graphic AI processing for the aircraft. This usually entails * + * maintenance and other AI functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::AI(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Perform any base class AI processing. If during this process, the aircraft was + ** destroyed, then detect this and bail from this AI routine early. + */ + FootClass::AI(); + if (!IsActive) { + return; + } + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Handle any body rotation at this time. Body rotation can occur even if the + ** flying object is not actually moving. + */ + Rotation_AI(); + + /* + ** Handle any aircraft movement at this time. + */ + Movement_AI(); + + /* + ** Any aircraft that is not in the ground layer must be redrawn. This is a + ** performance hit, but there is no other choice. The cells under an aircraft + ** do not know if there is an aircraft above it. Thus, it cannot flag the + ** aircraft to redraw. As a consequence, all aircraft must redraw. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(); + } + + /* + ** Perform sighting every so often as controlled by the sight timer. + */ + //if (IsOwnedByPlayer && Class->SightRange && SightTimer == 0) { // Changed to facilitate client/server multiplayer. ST - 8/2/2019 2:29PM + if (House->IsHuman && Class->SightRange && SightTimer == 0) { + Look(); + SightTimer = TICKS_PER_SECOND; + } + + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Landing_Takeoff_AI()) { + return; + } + + /* + ** Always flag the map draw process to occur if there is an aircraft in the view. + ** This ensures that it will be rendered even if there is nothing else that flagged + ** the map to be redrawn. + */ + if (Map.In_View(Coord_Cell(Coord))) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the + ** aircraft is a non-player produced unit and it has completed its mission. A transport + ** helicopter that has already delivered reinforcements is a good example of this. + */ + if (Edge_Of_World_AI()) { + return; + } +} + + +/*********************************************************************************************** + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * * + * When aircraft are flying, they can overlap quite a number of cells. These cells can * + * be determined from the coordinate where the aircraft is centered and the size of the * + * aircraft's shape. Landed aircraft are a special case and are usually much smaller * + * than when flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that * + * the aircraft overlaps given the aircraft's current state. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftClass::Overlap_List(bool redraw) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static short const _list[] = { + -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), + -1, 0, 1, + (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), + -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), + -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), + REFRESH_EOL + }; + + static short const _listbadger[] = { + -(MAP_CELL_W-2), -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2), + -2, -1, 0, 1, 2, + (MAP_CELL_W-2), (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2), + -((MAP_CELL_W*2)-2), -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), -((MAP_CELL_W*2)+2), + -((MAP_CELL_W*3)-2), -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), -((MAP_CELL_W*3)+2), + REFRESH_EOL + }; + + if (redraw || Height != 0) { +#ifdef PARTIAL + Rect rect; + if (!IsSelected && Class->DimensionData != NULL && Class->IsFixedWing) { + int shapenum = min(Shape_Number(), Get_Build_Frame_Count(Class->Get_Image_Data())-1); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + + /* + ** Increase the rectangle for the aircraft since the aircraft could + ** have its shape algorithmically rotated. + */ + rect.X -= 5; + rect.Y -= 5; + rect.Width += 10; + rect.Height += 10; + + Rect hrect = rect; + + hrect.Y -= Lepton_To_Pixel(Height); + + return(Coord_Spillage_List(Coord, Union(rect, hrect), true)); + } +#endif + + if (*this == AIRCRAFT_BADGER) { + return(_listbadger); + } else { + return(_list); + } + } + return(Class->Overlap_List()); +} + + +/*********************************************************************************************** + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * * + * This routine is used to clear out the aircraft allocation system. It is called in * + * preparation for a scenario load or save game load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Init(void) +{ + Aircraft.Free_All(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * * + * This function is used to handle finding, heading toward, landing, and unloading the * + * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows * + * a different mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks to delay before calling this function again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Unload(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + Assign_Target(NavCom); + return(Mission_Hunt()); + + } else { + enum { + SEARCH_FOR_LZ, + FLY_TO_LZ, + LAND_ON_LZ, + UNLOAD_PASSENGERS, + TAKE_OFF + }; + + switch (Status) { + + /* + ** Search for an appropriate destination spot if one isn't already assigned. + */ + case SEARCH_FOR_LZ: + if (Height == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) { + Status = UNLOAD_PASSENGERS; + } else { + if (!Is_LZ_Clear(NavCom)) { + + FootClass * foot = Attached_Object(); + if (foot != NULL && foot->Team && foot->Team->Class->Origin != -1) { + Assign_Destination(::As_Target(Scen.Waypoint[foot->Team->Class->Origin])); + } else { + Assign_Destination(New_LZ(::As_Target(Scen.Waypoint[WAYPT_REINF]))); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } + } else { + if (Height == FLIGHT_LEVEL) { + Status = FLY_TO_LZ; + } else { + Status = TAKE_OFF; + } + } + } + break; + + /* + ** Fly to destination. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0100) { + SecondaryFacing.Set_Desired(Pose_Dir()); + + if (distance < 0x0010) { + Status = LAND_ON_LZ; + } + return(1); + } else { + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(5); + } + } else { + Status = SEARCH_FOR_LZ; + } + break; + + /* + ** Landing phase. Just delay until landing is complete. At that time, + ** transition to the unloading phase. + */ + case LAND_ON_LZ: + if (IsTakingOff) { + Status = TAKE_OFF; + } else { + if (Process_Landing()) { + Status = UNLOAD_PASSENGERS; + } + } + return(1); + + /* + ** Hold while unloading passengers. When passengers are unloaded the order for this + ** transport gets changed to MISSION_RETREAT. + */ + case UNLOAD_PASSENGERS: + if (!IsTethered) { + if (Is_Something_Attached()) { + FootClass * unit = (FootClass *)Detach_Object(); + + /* + ** First thing is to lift the transport off of the map so that the unlimbo + ** process for the passengers is more likely to succeed. + */ + Map.Pick_Up(Coord_Cell(Coord), this); + + if (!Exit_Object(unit)) { + delete unit; + } + + /* + ** Restore the transport back down on the map. + */ + Map.Place_Down(Coord_Cell(Coord), this); + + if (!Is_Something_Attached()) { + Enter_Idle_Mode(); + } + + } else { + + Enter_Idle_Mode(); + } + } + break; + + /* + ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it + ** will either take off or look for another landing spot to try again. + */ + case TAKE_OFF: { + if (Process_Take_Off()) { + if (Is_Something_Attached()) { + Status = SEARCH_FOR_LZ; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + } else { + Enter_Idle_Mode(); + } + } + return(1); + } + + default: + break; + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * * + * This routine examines the landing zone (as specified by the target parameter) in order * + * to determine if it is free to be landed upon. Call this routine when it is necessary * + * to double check this. Typically this occurs right before a helicopter lands and also * + * when determining the landing zone in the first place. * + * * + * INPUT: target -- The target that is the "landing zone". * + * * + * OUTPUT: bool; Is the landing zone clear for landing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Is_LZ_Clear(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(target)) return(false); + CELL cell = ::As_Cell(target); + if (!Map.In_Radar(cell)) return(false); + + /* + ** If the requested landing location is occupied, then only consider that location + ** legal if the occupying object is in radio contact with the aircraft. This presumes that + ** the two objects know what they are doing. + */ + ObjectClass * object = Map[cell].Cell_Object(); + if (object) { + if (object == this) return(true); + + if (In_Radio_Contact() && Contact_With_Whom() == object) { + return(true); + } + return(false); + } + + if (!Map[cell].Is_Clear_To_Move(SPEED_TRACK, false, false)) return(false); + + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * * + * This routine is used to determine the coordinate to use for sorting the aircraft. This * + * sorting value is used when the aircraft is on the ground. At that time the aircraft * + * must be rendered in proper relationship to the other ground objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground * + * objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Sort_Y(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * * + * This mission will be followed when the aircraft decides that it is time to leave the * + * battle. Typically, this occurs when a loaner transport has dropped off its load or when * + * an attack air vehicle has expended its ordinance. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. * + *=============================================================================================*/ +int AircraftClass::Mission_Retreat(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + if (Class->IsFixedWing && Height < FLIGHT_LEVEL) { + Height += 1; + return(3); + } + return(TICKS_PER_SECOND*10); + } + + enum { + TAKE_OFF, + FACE_MAP_EDGE, + KEEP_FLYING + }; + switch (Status) { + + /* + ** Take off if landed. + */ + case TAKE_OFF: + if (Process_Take_Off()) { + Status = FACE_MAP_EDGE; + } + return(1); + + /* + ** Set facing and speed toward the friendly map edge. + */ + case FACE_MAP_EDGE: + Set_Speed(0xFF); + + /* + ** Take advantage of the fact that the source map edge enumerations happen to + ** occur in a clockwise order and are the first four enumerations of the map + ** edge default for the house. If this value is masked and then shifted, a + ** normalized direction value results. Use this value to head the aircraft + ** toward the "friendly" map edge. + */ + PrimaryFacing.Set_Desired((DirType)((House->Control.Edge & 0x03) << 6)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Status = KEEP_FLYING; + break; + + /* + ** Just do nothing since we are headed toward the map edge. When the edge is + ** reached, the aircraft should be automatically eliminated. + */ + case KEEP_FLYING: + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * * + * This routine is called when the aircraft is to unload a passenger. The passenger must * + * be able to move under its own power. Typical situation is when a transport helicopter * + * is to unload an infantry unit. * + * * + * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. * + * * + * OUTPUT: bool; Was the unit unloaded successfully? * + * * + * WARNINGS: The unload process is merely started by this routine. Radio contact is * + * established with the unloading unit and when the unit is clear of the aircraft * + * the radio contact will be broken and then the aircraft is free to pursue * + * other. * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Exit_Object(TechnoClass * unit) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E}; + CELL cell; + + /* + ** Find a free cell to drop the unit off at. + */ + FacingType face; + for (face = FACING_N; face < FACING_COUNT; face++) { + cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]); + if (unit->Can_Enter_Cell(cell) == MOVE_OK) break; + } + + // Should perform a check here to see if no cell could be found. + + /* + ** If the passenger can be placed on the map, then start it moving toward the + ** destination cell and establish radio contact with the transport. This is used + ** to make sure that the transport waits until the passenger is clear before + ** unloading the next passenger or taking off. + */ + if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(cell)); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + unit->Look(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * * + * Call this routine when a passenger needs to be dropped off by parachute. One passenger * + * is offloaded by a call to this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay time that it is safe to wait before processing any further * + * paradrop actions. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Paradrop_Cargo(void) +{ + FootClass * passenger = Detach_Object(); + if (passenger) { + if (!passenger->Paradrop(Center_Coord())) { + Attach(passenger); + } else { + + /* + ** Play a sound effect of the parachute opening. + */ + Sound_Effect(VOC_CHUTE1, Coord); + + if (Team.Is_Valid()) { + Team->Remove(passenger); + if (passenger->House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + } +// Arm = Rearm_Delay(IsSecondShot); + Arm = 0; + } + } + return(Arm); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * * + * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the * + * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the * + * default speed defined in the BulletTypeClass structure. * + * * + * INPUT: target -- The target that the projectile is heading for. * + * * + * which -- Which weapon to use in the attack. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * AircraftClass::Fire_At(TARGET target, int which) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Passenger aircraft will actually paradrop their cargo instead of + ** firing their weapon. + */ + if (Is_Something_Attached()) { + Paradrop_Cargo(); + return(0); + } + + /* + ** If the weapon is actually a camera, then perform the "snapshot" of the + ** ground instead of normal weapon fire. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) { + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + + if (House->Is_Ally(PlayerPtr)) { + Map.Sight_From(Coord_Cell(Center_Coord()), 9, House, false); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass *house = HouseClass::As_Pointer(house_type); + + if (house == House || House->Is_Ally(house)) { + Map.Sight_From(Coord_Cell(Center_Coord()), 9, house, false); + } + } + } + + Ammo = 0; + Arm = Rearm_Delay(IsSecondShot); + return(0); + } + + + BulletClass * bullet = FootClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Aircraft reveal when firing + */ + HouseClass *player = HouseClass::As_Pointer(Owner()); + if (player != nullptr && player->IsHuman) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, player, false); + } + + /* + ** Falling bullets move at a speed proportionate to the delivery craft. + */ + if (bullet->Class->IsDropping) { + bullet->Fly_Speed(40, MPH_MEDIUM_SLOW); // TCTC To fix. + } + } + return(bullet); +} + + +/*********************************************************************************************** + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * * + * This routine is used to apply damage to the specified aircraft. This is where any * + * special crash animation will be initiated. * + * * + * INPUT: damage -- Reference to the damage that will be applied to the aircraft. * + * This value will be filled in with the actual damage that was * + * applied. * + * * + * distance -- Distance from the source of the explosion to this aircraft. * + * * + * warhead -- The warhead type that the damage occurs from. * + * * + * source -- Pointer to the originator of the damage. This can be used so that * + * proper "thank you" can be delivered. * + * * + * OUTPUT: Returns with the result of the damage as it affects this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1995 JLB : Created. * + *=============================================================================================*/ +ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Flying aircraft take half damage. + */ + if (Height) { + damage /= 2; + } + + /* + ** Apply the damage to the aircraft. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** Special action is performed if the aircraft is killed -- the cargo is destroyed + ** as well. + */ + switch (res) { + case RESULT_DESTROYED: + Kill_Cargo(source); + Death_Announcement(); + new AnimClass(ANIM_FBALL1, Target_Coord()); + + /* + ** Parachute a survivor if possible. + */ + if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) { + InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House); + if (infantry != NULL) { + if (!infantry->Paradrop(Center_Coord())) { + delete infantry; + } + } + } + + delete this; + break; + + default: + case RESULT_HALF: + break; + } + + return(res); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Move -- Handles movement mission. * + * * + * This state machine routine is used when an aircraft (usually helicopter) is to move * + * from one location to another. It will handle any necessary take off and landing this * + * may require. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames that should elapse before this routine * + * is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + enum { + TAKE_OFF, + FLY_TOWARD_TARGET + }; + + switch (Status) { + int distance; + + case TAKE_OFF: + + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TOWARD_TARGET; + } + return(1); + + case FLY_TOWARD_TARGET: + PrimaryFacing.Set_Desired(Direction(NavCom)); + distance = Distance(NavCom); + + if (distance < 0x00C0) { + MissionType mission = MISSION_GUARD; + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers() ) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + + /* + ** If this aircraft has nowhere else to go, meaning that + ** there is no airfield available, then it has to crash. + */ + if (Is_Target_Cell(NavCom)) { + + if (Process_Landing()) { + Strength = 1; + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(1); + } + return(500); + } + mission = MISSION_MOVE; + } + Assign_Mission(mission); + Commence(); + } else { + if (!Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + return(1); + } + break; + + default: + break; + } + + return(5); + } + + enum { + VALIDATE_LZ, + TAKE_OFF, + FLY_TO_LZ, + LAND + }; + switch (Status) { + + /* + ** Double check and change LZ if necessary. + */ + case VALIDATE_LZ: + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } else { + if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } else { + Status = TAKE_OFF; + } + } + break; + + /* + ** Take off if necessary. + */ + case TAKE_OFF: + if (!Target_Legal(NavCom)) { + Status = VALIDATE_LZ; + } else { + if (Process_Take_Off()) { + + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_LZ; + } + return(1); + } + break; + + /* + ** Fly toward target. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LAND; + } + return(1); + } + +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + SecondaryFacing.Set_Desired(Direction(NavCom)); + + } else { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + if (!Target_Legal(NavCom)) { + Status = LAND; + } + } + return(1); + + /* + ** Land on target. + */ + case LAND: + if (IsTakingOff) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + Status = TAKE_OFF; + } + if (Process_Landing()) { + if (MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + } + return(1); + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * * + * Use this routine when the mission for the aircraft is in doubt. This routine will find * + * an appropriate mission for the aircraft and dispatch it. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Enter_Idle_Mode(bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + MissionType mission = (Class->IsFixedWing && IsALoaner && Class->PrimaryWeapon != NULL) ? MISSION_HUNT : MISSION_GUARD; + if (Class->IsFixedWing) { + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + mission = MISSION_RETREAT; + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + if (Team.Is_Valid()) return; + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (Mission != MISSION_ATTACK && IsALoaner && Ammo == 0 && Class->PrimaryWeapon != NULL) { + mission = MISSION_HUNT; + } else { + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); + Assign_Destination(TARGET_NONE); + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + if (Class->IsFixedWing) { + Status = 0; //BG - reset the mission status to avoid landing on the ground next to the airstrip + if (IsLanding) { + Process_Take_Off(); + } + } + mission = MISSION_ENTER; + } else { + mission = MISSION_RETREAT; + } + } + } + } + + } else { + + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + if (Is_Something_Attached()) { + + /* + ** In the case of a computer controlled helicopter that hold passengers, + ** don't unload when landing. Wait for specific instructions from the + ** controlling team. + */ + if (Team.Is_Valid()) { +// if (Team.Is_Valid() && !House->IsHuman) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + } + } else { + mission = MISSION_RETREAT; + } + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + if (Is_Something_Attached()) { + if (IsALoaner) { + if (Team) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + Assign_Destination(Good_LZ()); + } + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + + if (Class->PrimaryWeapon != NULL) { + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (IsALoaner) { + + /* + ** If it has no ammo, then break off of the team and leave the map. + ** If it can fight, then give it fighting orders. + */ + if (Ammo == 0) { + if (Team.Is_Valid()) Team->Remove(this); + mission = MISSION_RETREAT; + } else { + if (!Team.Is_Valid()) { + mission = MISSION_HUNT; + } + } + + } else { + + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()/* && !ship->In_Radio_Contact()*/) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } else { + if (Team) return; + + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } + } + Assign_Mission(mission); + Commence(); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * * + * This support routine is used when the helicopter is to fly to the destination. It can * + * optionally slow the helicopter down as it approaches the destination. * + * * + * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? * + * * + * OUTPUT: Returns with the distance remaining between the aircraft and the destination. * + * * + * WARNINGS: Because the aircraft can move at a fast speed, the distance to target value * + * will probably never be zero. The likely case will be that the aircraft * + * overshoots the target. * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + * 03/05/1996 JLB : Specifies destination target value. * + *=============================================================================================*/ +int AircraftClass::Process_Fly_To(bool slowdown, TARGET dest) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) slowdown = false; + + COORDINATE coord; + if (Is_Target_Building(dest)) { + coord = As_Building(dest)->Docking_Coord(); + } else { + coord = As_Coord(dest); + } + int distance = Distance(coord); + + PrimaryFacing.Set_Desired(Direction(coord)); + + if (slowdown) { + int speed = min(distance, 0x0300); + speed = Bound(speed/3, 0x0020, 0x00FF); + if (Speed != speed) { + Set_Speed(speed); + } + } + + if (distance < 0x0010) { + if (slowdown) { + Set_Speed(0); + } + distance = 0; + } + return(distance); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. * + * * + * This displays the current status of the aircraft class to the mono monitor. By this * + * display bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Debug_Dump(MonoClass * mono) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_AIRCRAFT)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", AttacksRemaining); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * * + * This routine is used when the player clicks over the speicifed object. It will assign * + * the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action that was nominally determined by the What_Action function. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will alter the game sequence and causes an event packet to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_NOMOVE: + return; + + case ACTION_ENTER: + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE); + break; + + default: + break; + } + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * * + * This routine is used when the player clicks the mouse of the specified cell. It will * + * assign the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action nominally determined by What_Action(). * + * * + * cell -- The cell over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will affect the game sequence and causes an event object to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * * + * This routine is called as a result of player input with the intent to change the * + * mission of the aircraft. * + * * + * INPUT: mission -- The mission requested of the aircraft. * + * * + * target -- The value to assign to the aircraft's targeting computer. * + * * + * dest. -- The value to assign to the aircraft's navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: The mission specified will be executed at an indeterminate future game frame. * + * This is controlled by net/modem propagation delay. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(TargetClass(this), mission, target, destination); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine is used to determine what action will likely be performed if the mouse * + * were clicked over the object specified. The display system calls this routine to * + * control the mouse shape. * + * * + * INPUT: target -- Pointer to the object that the mouse is currently over. * + * * + * OUTPUT: Returns with the action that will occur if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(ObjectClass const * target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(target); + + if (action == ACTION_SELF && (!How_Many() || (Height > 0) || IsTethered)) { + action = ACTION_NONE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing && House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_VESSEL && *(VesselClass *)target == VESSEL_CARRIER && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#endif + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_NONE) { + action = What_Action(Coord_Cell(target->Center_Coord())); + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && target->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)target; + if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_ENTER; + } + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine will determine what action would occur if the mouse were clicked over the * + * cell specified. The display system calls this routine to determine what mouse shape * + * to use. * + * * + * INPUT: cell -- The cell over which the mouse is currently positioned. * + * * + * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the * + * specified cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(CELL cell) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (action == ACTION_MOVE && !Map[cell].Is_Visible(PlayerPtr)) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * * + * Use this routine to get the desired facing the aircraft should assume when landing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the normal default facing the aircraft should have when landed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 03/04/1996 JLB : Fixed wing aircraft always face down the runway. * + *=============================================================================================*/ +DirType AircraftClass::Pose_Dir(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (*this == AIRCRAFT_TRANSPORT) { + return(DIR_N); + } + if (Class->IsFixedWing) { + return(DIR_E); + } + return(DIR_NE); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * * + * This routine is the state machine that handles the attack mission for aircraft. It will * + * handling homing in on and firing on the target in the aircraft's targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to pass before this routine must be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Fixes brain dead helicopter for Nod scen #7. * + *=============================================================================================*/ +int AircraftClass::Mission_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + return(Mission_Hunt()); + } + + enum { + VALIDATE_AZ, + PICK_ATTACK_LOCATION, + TAKE_OFF, + FLY_TO_POSITION, + FIRE_AT_TARGET, + FIRE_AT_TARGET2, + RETURN_TO_BASE + }; + switch (Status) { + + /* + ** Double check target and validate the attack zone. + */ + case VALIDATE_AZ: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + + /* + ** Pick a good location to attack from. + */ + case PICK_ATTACK_LOCATION: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Assign_Destination(Good_Fire_Location(TarCom)); + if (Target_Legal(NavCom)) { + Status = TAKE_OFF; + } else { + Status = RETURN_TO_BASE; + } + } + break; + + /* + ** Take off (if necessary). + */ + case TAKE_OFF: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + if (Process_Take_Off()) { + Status = FLY_TO_POSITION; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Start flying toward the destination by skewing at first. + ** As the flight progresses, the body will rotate to face + ** the direction of travel. + */ + int diff = SecondaryFacing.Difference(Direction(NavCom)); + diff = Bound(diff, -128, 128); + PrimaryFacing = DirType((int)SecondaryFacing.Current()+diff); + } + return(1); + } + break; + + /* + ** Fly to attack location. + */ + case FLY_TO_POSITION: + if (Target_Legal(TarCom)) { + + /* + ** If the navcom was cleared mysteriously, then try to pick + ** a new attack location. This is a likely event if the player + ** clicks on a new target while in flight to an existing target. + */ + if (!Target_Legal(NavCom)) { + Status = PICK_ATTACK_LOCATION; + return(1); + } + + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0200) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + + if (distance < 0x0010) { + Status = FIRE_AT_TARGET; + Assign_Destination(TARGET_NONE); + } + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + return(1); + } + } else { + Status = RETURN_TO_BASE; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + Status = FIRE_AT_TARGET2; + break; + + case FIRE_REARM: + case FIRE_FACING: + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = FIRE_AT_TARGET2; + } + break; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET2: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_REARM: + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + + if (Ammo) { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } else { + Status = RETURN_TO_BASE; + } + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + if (!In_Range(TarCom)) { + Status = PICK_ATTACK_LOCATION; + } else { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } + } + break; + } + break; + + /* + ** Fly back to landing spot. + */ + case RETURN_TO_BASE: + /* + ** Break off of firing at the target if there is no more + ** point in attacking it this mission. The player will + ** reassign a target for the next mission. + */ + if (!Ammo && (IsALoaner || House->IsHuman)) { + Assign_Target(TARGET_NONE); + } + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + break; + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::New_LZ -- Find a good landing zone. * + * * + * Use this routine to locate a good landing zone that is nearby the location specified. * + * By using this routine it is possible to assign the same landing zone to several * + * aircraft and they will land nearby without conflict. * + * * + * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). * + * * + * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. * + * * + * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is * + * extensive blocking terrain in the vicinity. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::New_LZ(TARGET oldlz) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) { + COORDINATE coord = As_Coord(oldlz); + + /* + ** Scan outward in a series of concentric rings up to certain distance + ** in cells. + */ + for (int radius = 0; radius < Rule.LZScanRadius / CELL_LEPTON_W; radius++) { + FacingType modifier = Random_Pick(FACING_N, FACING_NW); + CELL lastcell = -1; + + /* + ** Perform a radius scan out from the original center location. Try to + ** find a cell that is allowed to be a legal LZ. + */ + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W)); + if (Map.In_Radar(newcell)) { + TARGET newtarget = ::As_Target(newcell); + + if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) { + return(newtarget); + } + lastcell = newcell; + } + } + } + } + return(oldlz); +} + + +/*********************************************************************************************** + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * * + * This routine receives all radio messages directed at this aircraft. It is used to handle * + * all inter-object coordination. Typically, this would be for transport helicopters and * + * other complex landing operations required of helicopters. * + * * + * INPUT: from -- The source of this radio message. * + * * + * message -- The message itself. * + * * + * param -- An optional parameter that may be used to transfer additional * + * data. * + * * + * OUTPUT: Returns with the radio response from the aircraft. * + * * + * WARNINGS: Some radio messages are handled by the base classes. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + switch (message) { + + case RADIO_PREPARED: + if (Target_Legal(TarCom)) return(RADIO_NEGATIVE); + if ((Height == 0 && Ammo == Class->MaxAmmo) || (Height > 0 && Ammo > 0)) return(RADIO_ROGER); + return(RADIO_NEGATIVE); + + /* + ** Something disastrous has happened to the object in contact with. Fall back + ** and regroup. This means that any landing process is immediately aborted. + */ + case RADIO_RUN_AWAY: + if (IsLanding) { + IsLanding = false; + IsTakingOff = true; + } + if (Class->IsFixedWing) { + Assign_Destination(Good_LZ()); + if (!Target_Legal(NavCom)) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_MOVE); + } + } else { + Scatter(0, true); + } + break; + + /* + ** The ground control requests that this specified landing spot be used. + */ + case RADIO_MOVE_HERE: + FootClass::Receive_Message(from, message, param); + if (Is_Target_Building(param)) { + if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) { + return(RADIO_NEGATIVE); + } + Assign_Mission(MISSION_ENTER); + Assign_Destination((TARGET)param); + } else { + Assign_Mission(MISSION_MOVE); + Assign_Destination((TARGET)param); + } + Commence(); + return(RADIO_ROGER); + + /* + ** Ground control is requesting if the aircraft requires navigation direction. + */ + case RADIO_NEED_TO_MOVE: + FootClass::Receive_Message(from, message, param); + if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + Close_Door(5, 4); + } + + /* + ** If a civilian has entered the transport, then the transport will immediately + ** fly off the map. + */ + if (_Counts_As_Civ_Evac(from)) { + Assign_Mission(MISSION_RETREAT); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + FootClass::Receive_Message(from, message, param); + + if (!IsTethered && !IsLanding && !IsTakingOff && Height == 0) { + + Open_Door(5, 4); + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + CELL cell; + /*DirType dir =*/ Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner()) || Height > 0) return(RADIO_STATIC); + if (/*!In_Radio_Contact() &&*/ How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + default: + break; + } + + /* + ** Let the base class take over processing this message. + */ + return(FootClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * * + * This routine is used by the transport helicopter to determine the location where the * + * infantry passengers should line up before loading. * + * * + * INPUT: object -- The object that is trying to load up on this transport. * + * * + * -- Reference to the cell that the passengers should move to before the * + * actual load process may begin. * + * * + * OUTPUT: Returns with the direction that the helicopter should face for the load operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/30/1995 JLB : Revamped to scan all adjacent cells. * + *=============================================================================================*/ +DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + CELL center = Coord_Cell(Center_Coord()); + for (int sweep = FACING_N; sweep < FACING_S; sweep++) { + moveto = Adjacent_Cell(center, FacingType(FACING_S+sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + + moveto = Adjacent_Cell(center, FacingType(FACING_S-sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * * + * This routine is used by the main game state machine processor. This utility routine * + * handles a helicopter as it transitions from landed to flying state. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter reached flight level now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Take_Off(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsLanding = false; + IsTakingOff = true; + + if (Class->IsFixedWing) { + Set_Speed(0xFF); + if (Height == FLIGHT_LEVEL) { + return(true); + } + + } else { + + switch (Height) { + case 0: + Close_Door(5, 4); + PrimaryFacing = SecondaryFacing; + break; + + case FLIGHT_LEVEL/2: + PrimaryFacing.Set_Desired(Direction(NavCom)); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/3): + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Set_Speed(0x20); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/5): + Set_Speed(0x40); + break; + + case FLIGHT_LEVEL: + Set_Speed(0xFF); + IsTakingOff = false; + return(true); + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * * + * This is a support routine that is called by the main state machine routines. This * + * routine is responsible for handling the helicopter as it transitions from flight to * + * landing. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter completely landed now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 03/04/1996 JLB : Handles fixed wing aircraft. * + *=============================================================================================*/ +bool AircraftClass::Process_Landing(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsTakingOff = false; + IsLanding = true; + + if (Class->IsFixedWing) { + int distance = Distance(NavCom); + + if (distance > 0x0100) { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + + switch (Height) { + case 0: + Set_Speed(0); + IsLanding = false; + return(true); + + default: +// if (distance*2 > Class->LandingSpeed) { +// Set_Speed(Class->LandingSpeed); +// } else { +// Set_Speed(distance/2); +// } + + Set_Speed(Class->LandingSpeed / House->AirspeedBias); + break; + } + + } else { + switch (Height) { + case 0: + IsLanding = false; + return(true); + + case FLIGHT_LEVEL/2: + Set_Speed(0); + break; + + case FLIGHT_LEVEL: + break; + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * * + * This routine is used when the passability of a cell needs to be determined. This is * + * necessary when scanning for a location that the aircraft can land. * + * * + * INPUT: cell -- The cell location to check for landing. * + * * + * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Map.In_Radar(cell)) return(MOVE_NO); + + CellClass * cellptr = &Map[cell]; + + ObjectClass const * occupier = cellptr->Cell_Occupier(); + + if (occupier == NULL || + !occupier->Is_Techno() || + ((TechnoClass *)occupier)->House->Is_Ally(House) || + (((TechnoClass *)occupier)->Cloak != CLOAKED && + (ScenarioInit == 0 && (occupier->What_Am_I() != RTTI_BUILDING || !((BuildingClass*)occupier)->Class->IsInvisible)) ) + ) { + + if (!cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false)) return(MOVE_NO); + } + + if (Session.Type == GAME_NORMAL && IsOwnedByPlayer && !cellptr->IsMapped) { + return(MOVE_NO); + } + + return(MOVE_OK); +} + + +/*********************************************************************************************** + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * * + * Given the specified target, this routine will locate a good spot for the aircraft to * + * fire at the target. * + * * + * INPUT: target -- The target that is desired to be attacked. * + * * + * OUTPUT: Returns with the target location of the place that firing should be made from. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 06/14/1995 JLB : Finer resolution on ring scan. * + * 11/02/1996 JLB : Bias fire position to get closer to moving objects. * + *=============================================================================================*/ +TARGET AircraftClass::Good_Fire_Location(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(target)) { + int range = Weapon_Range(0); + COORDINATE tcoord = As_Coord(target); + CELL bestcell = 0; + CELL best2cell = 0; + int bestval = -1; + int best2val = -1; + + /* + ** Try to get closer to a target that is moving. + */ + COORDINATE altcoord = 0; + if (Is_Target_Object(target) && As_Object(target)->Is_Foot()) { + TARGET alttarg = ((FootClass *)As_Object(target))->NavCom; + if (Target_Legal(alttarg)) { + altcoord = As_Coord(alttarg); + } + } + + for (int r = range-0x0100; r > 0x0100; r -= 0x0100) { + for (int face = 0; face < 255; face += 16) { + COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r); + CELL newcell = Coord_Cell(newcoord); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (Map.In_Radar(newcell) && (Session.Type != GAME_NORMAL || Map[newcell].Is_Visible(PlayerPtr)) && Cell_Seems_Ok(newcell, true)) { + int dist; + if (altcoord != 0) { + dist = ::Distance(newcoord, altcoord); + } else { + dist = Distance(newcoord); + } + if (bestval == -1 || dist < bestval) { + best2val = bestval; + best2cell = bestcell; + bestval = dist; + bestcell = newcell; + } + } + } + if (bestval != -1) break; + } + + if (best2val == -1) { + best2cell = bestcell; + } + + /* + ** If it found a good firing location, then return this location as + ** a target value. + */ + if (bestval != -1) { + if (Percent_Chance(50)) { + return(::As_Target(bestcell)); + } else { + return(::As_Target(best2cell)); + } + } + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * * + * This routine examines the navigation computers of other aircraft in order to see if the * + * specified cell is safe to fly to. The intent of this routine is to avoid unnecessary * + * mid-air collisions. * + * * + * INPUT: cell -- The cell to examine for clear airspace. * + * * + * strict -- Should the scan consider the aircraft, that is making this check, a * + * blocking aircraft. Typically, the aircraft itself is not considered * + * a blockage -- an aircraft can always exist where it is currently * + * located. A strict check is useful for helicopters that need to move * + * around at the slightest provocation. * + * * + * OUTPUT: Is the specified cell free from airspace conflicts? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that no other aircraft are heading to the selected location. If they + ** are, then don't consider the location as valid. + */ + TARGET astarget = ::As_Target(cell); + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * air = Aircraft.Ptr(index); + if (air && (strict || air != this) && !air->IsInLimbo) { + if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) { + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * * + * This routine is used by the render logic to draw the little container "pips". This * + * corresponds to the number of passengers for a transport helicopter or the number of * + * shots remaining for an attack helicopter. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of "pips" to render on the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Pip_Count(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + int retval = 0; + + bool carrying_passengers = (Class->Max_Passengers() > 0) && ((*this != AIRCRAFT_BADGER) || (Mission != MISSION_HUNT)); + if (carrying_passengers) { + retval = How_Many(); + } else { + if (Ammo) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + } + return(retval); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * * + * This routine is used when the aircraft needs to fly for either rearming or repairing. * + * It tries to establish contact with the support building. Once contact is established * + * the ground controller takes care of commanding the aircraft. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/04/1995 JLB : Ground controller gives orders. * + *=============================================================================================*/ +int AircraftClass::Mission_Enter(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + TAKEOFF, + ALTITUDE, + STACK, + DOWNWIND, + CROSSWIND, + TRAVEL, + LANDING + }; + + /* + ** Verify that it has a valid NavCom. If it doesn't then request one from the + ** building this building is trying to land upon. If that fails, then enter + ** idle mode. + */ + if (!Target_Legal(NavCom) && In_Which_Layer() != LAYER_GROUND) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Enter_Idle_Mode(); + return(1); + } + } + + switch (Status) { + case INITIAL: + if (Height < FLIGHT_LEVEL || IsLanding) { + Status = TAKEOFF; + } else { + Status = ALTITUDE; + } + break; + + case TAKEOFF: + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + Status = ALTITUDE; + } + break; + + case ALTITUDE: + /* + ** Establish radio contact with the building this helicopter is trying + ** to land at. + */ + if (In_Radio_Contact()) { + if (!Target_Legal(NavCom)) { + Transmit_Message(RADIO_DOCKING); + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + return(1); + } + } + Status = STACK; + } else { + TechnoClass * tech = As_Techno(NavCom); + if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) { + Transmit_Message(RADIO_HELLO, tech); + Transmit_Message(RADIO_DOCKING); + Status = STACK; + } else { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (tech->What_Am_I() != RTTI_VESSEL) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#else + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); +#endif + } + } + break; + + case STACK: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_STACK)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = DOWNWIND; + } + } else { + Status = DOWNWIND; + } + break; + + case DOWNWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(200); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_DOWNWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = CROSSWIND; + } + } else { + Status = CROSSWIND; + } + break; + + case CROSSWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(140); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_CROSSWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = TRAVEL; + } + } else { + Status = TRAVEL; + } + break; + + case TRAVEL: + Transmit_Message(RADIO_DOCKING); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!In_Radio_Contact() && !Is_Target_Vessel(NavCom)) { +#else + if (!In_Radio_Contact()) { +#endif + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } else { + int distance = Process_Fly_To(true, NavCom); + + if (Class->IsFixedWing) { + + if (distance < 0x0400) { + Status = LANDING; + } + return(1); + + } else { + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Is_Target_Vessel(NavCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + break; + } +#endif + if (distance < 0x0010) { + Status = LANDING; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Is_Target_Vessel(NavCom) && As_Vessel(NavCom)->NavCom) { + Status = TRAVEL; + } +#endif + + } + break; + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + } + return(3); + } + break; + + case LANDING: + if (IsTakingOff && !Class->IsFixedWing) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// If we were trying to land on a carrier and it moved, take off again + if ( As_Vessel(NavCom) && !In_Radio_Contact()) { + Status = INITIAL; + break; + } +#endif + if (Process_Landing()) { + switch (Transmit_Message(RADIO_IM_IN)) { + case RADIO_ROGER: + Assign_Mission(MISSION_GUARD); + break; + + case RADIO_ATTACH: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Contact_With_Whom()->What_Am_I() != RTTI_VESSEL) Limbo(); +#else + Limbo(); +#endif + Contact_With_Whom()->Attach(this); + break; + + default: + Enter_Idle_Mode(); + } + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * AircraftClass::Good_LZ -- Locates a good spot to land. * + * * + * This routine is used when helicopters need a place to land, but there are no obvious * + * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly * + * building if there are no helipads anywhere. In the event that there are no friendly * + * buildings anywhere on the map, then just land right where it is flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target location where this aircraft should land. This value may * + * not be a clear cell, but the normal landing logic will resolve that problem. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::Good_LZ(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Scan through all of the buildings and try to land near + ** the helipad (if there is one) or the nearest friendly building. + */ + CELL bestcell = 0; + int bestdist = -1; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == House) { + int dist = Distance(building); + if (*building == Class->Building) { + dist /= 4; + } + if (bestdist == -1 || dist < bestdist) { + bestdist = dist; + bestcell = Coord_Cell(building->Center_Coord()); + } + } + } + + /* + ** Return with the suitable location if one was found. + */ + if (bestdist != -1) { + return(::As_Target(bestcell)); + } + + /* + ** No good location was found. Just try to land here. + */ + return(::As_Target(Coord_Cell(Coord))); +} + + +/*********************************************************************************************** + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * * + * This routine will set the speed for the aircraft. The speed is specified as a fraction * + * of full speed. * + * * + * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full * + * speed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Set_Speed(int speed) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Set_Speed(speed); + + MPHType sp = MPHType(min(Class->MaxSpeed * SpeedBias * House->AirspeedBias, MPH_LIGHT_SPEED)); + Fly_Speed(speed, sp); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * * + * This routine will determine what direction a projectile would take if it were fired * + * from the aircraft. This is the direction that the aircraft's body is facing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction of projectile fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Fire_Direction(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(SecondaryFacing.Current()); +} + + +/*********************************************************************************************** + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * * + * This is the destructor for aircraft. It will limbo the aircraft if it isn't already * + * and also removes the aircraft from any team it may be attached to. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass::~AircraftClass(void) +{ + if (GameActive && Class) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + AircraftClass::Limbo(); + Class = 0; + } + ID = -1; +} + + +/*********************************************************************************************** + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * * + * This routine will cause the aircraft to move away from its current location and then * + * enter some idle mode. Typically this is called when the aircraft is attacked while on * + * the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Scatter(COORDINATE , bool, bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter) return; + + /* + ** Fixed wing aircraft never scatter. + */ + if (Class->IsFixedWing) return; + + if (IsLanding || Height == 0) { + IsLanding = false; + IsTakingOff = true; + } + Enter_Idle_Mode(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * * + * Aircraft don't like to be in guard mode if in flight. If this situation is detected, * + * then figure out what the aircraft should be doing and go do it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This routine typically calls the normal guard logic for ground units. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 10/10/1995 JLB : Hunts for harvesters that are unescorted. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + + /* + ** If part of a team, then do nothing, since the team + ** handler will take care of giving this aircraft a + ** mission. + */ + if (Team) { + if (Target_Legal(NavCom)) { + Assign_Mission(MISSION_MOVE); + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (Class->PrimaryWeapon == NULL) { + Assign_Destination(::As_Target(Coord_Cell(Coord))); + Assign_Mission(MISSION_MOVE); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + return(1); + } + if (House->IsHuman) return(MissionControl[Mission].Normal_Delay()); + + /* + ** If the aircraft is very badly damaged, then it will search for a + ** repair bay first. + */ + if (House->Available_Money() >= 100 && Health_Ratio() <= Rule.ConditionYellow) { + if (!In_Radio_Contact() || + (Height == 0 && + (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) { + + + BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true); + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft cannot attack anything because of lack of ammo, + ** abort any normal guard logic in order to look for a helipad + ** to rearm. + */ + if (Ammo == 0 && Is_Weapon_Equipped()) { + if (!In_Radio_Contact()) { + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft already has a target, then attack it if possible. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + + /* + ** Transport helicopters don't really do anything but just sit there. + */ + if (!Is_Weapon_Equipped()) { + return(TICKS_PER_SECOND*3); + } + + /* + ** Computer controlled helicopters will defend themselves by bouncing around + ** and looking for a free helipad. + */ + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + /* + ** Perform a special check to hunt for harvesters that are outside of the protective + ** shield of their base. + */ + if (House->State != STATE_ATTACKED) { + TARGET target = House->Find_Juicy_Target(Coord); + + if (Target_Legal(target)) { + Assign_Target(target); + Assign_Mission(MISSION_ATTACK); + } + } + + return(FootClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * * + * This routine handles area guard logic for aircraft. Aircraft require special handling * + * for this mode since they are to guard area only if they are in a position to do so. * + * Otherwise they just defend themselves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard_Area(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + return(FootClass::Mission_Guard_Area()); +} + + +/*********************************************************************************************** + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * * + * This routine is used to give an audio response to an attack order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Move -- Gives audio response to move request. * + * * + * This routine is used to give an audio response to movement orders. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Select -- Gives audio response when selected. * + * * + * This routine is called when an audio response for selection is desired. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Select(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * * + * This routine is used to determine if the aircraft can fire its weapon at the target * + * specified. If it cannot, then the reason why is returned. * + * * + * INPUT: target -- The target that the aircraft might fire upon. * + * * + * which -- The weapon that will be used to fire. * + * * + * OUTPUT: Returns with the reason why it can't fire or with FIRE_OK. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + * 07/11/1996 JLB : Fixed for camera carrying aircraft. * + *=============================================================================================*/ +FireErrorType AircraftClass::Can_Fire(TARGET target, int which) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Passenger && !Is_Something_Attached()) { + return(FIRE_AMMO); + } + + bool camera = (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera); + bool fudge = (Passenger || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->IsParachuted)); + + if (fudge && !camera && !Ammo && !Passenger) { + return(FIRE_AMMO); + } + + /* + ** Passenger aircraft that wish to 'fire' actually are requesting to + ** paradrop or 'throw out' the cargo. This is always allowed if the terrain under the + ** aircraft is generally clear. + */ + if (camera || (fudge && Passenger && Is_Something_Attached())) { + if (Arm != 0) return(FIRE_REARM); + + if (Distance(target) < (camera ? 0x0380 : 0x0200) && Map.In_Radar(Coord_Cell(Center_Coord()))) { +// if (Distance(target) < (camera ? 0x0380 : 0x0280) && Map.In_Radar(Coord_Cell(Center_Coord()))) { + return(FIRE_OK); + } + return(FIRE_RANGE); + } + + FireErrorType canfire = FootClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsFixedWing) { + + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) > (fudge ? 16 : 8)) { + return(FIRE_FACING); + } + } + } + return(canfire); +} + + +/*********************************************************************************************** + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * * + * This routine handles the tricky maneuver of taking off and landing. The process of * + * landing is not entirely safe and thus the aircraft may be destroyed as a consequence. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft destroyed by this process? * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. Be sure to * + * examine the return value and if true, abort all further processing of this * + * aircraft since it is now dead. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Landing_Takeoff_AI(void) +{ + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Is_Door_Closed() && (IsLanding || IsTakingOff)) { + LayerType layer = In_Which_Layer(); + + if (IsLanding) { + Mark(MARK_UP); + if (Height) Height -= Pixel_To_Lepton(1); + if (Height <= 0) { + Height = 0; + IsLanding = false; + Set_Speed(0); + + /* + ** If the NavCom now equals the destination, then clear out the NavCom. + */ + if (Coord_Cell(Center_Coord()) == As_Cell(NavCom)) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If a fixed-wing aircraft just landed on the ground, blow him up + */ + if (Class->IsFixedWing && Mission != MISSION_ENTER) { + Strength = 1; + + int damage = Strength; + Map.Remove(this, layer); + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(true); + } + + if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) { + if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) { + Scatter(0, true); + } + } + } + Mark(MARK_DOWN); + } + if (IsTakingOff) { + Mark(MARK_UP); +// Map.Remove(this, layer); + Height += Pixel_To_Lepton(1); + if (Height >= FLIGHT_LEVEL) { + Height = FLIGHT_LEVEL; + IsTakingOff = false; + } +// Map.Submit(this, In_Which_Layer()); + Mark(MARK_DOWN); + } + + /* + ** Make adjustments for altitude by moving from one layer to another as + ** necessary. + */ + if (layer != In_Which_Layer()) { + + /* + ** When the aircraft is about to enter the ground layer, perform on last + ** check to see if it is legal to enter that location. If not, then + ** start the take off process. Let the normal logic handle this + ** change of plans. + */ + bool ok = true; + if (In_Which_Layer() == LAYER_GROUND && !IsTakingOff && !Class->IsFixedWing) { + if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) { + IsTakingOff = true; + Mark(MARK_UP); + Height += Pixel_To_Lepton(1); + Mark(MARK_DOWN); + ok = false; + } + } + + if (ok) { + + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + /* + ** When the aircraft is close to the ground, it should exist as a ground object. + ** This aspect is controlled by the Place_Down and Pick_Up functions. + */ + if (In_Which_Layer() == LAYER_GROUND) { + Assign_Destination(TARGET_NONE); // Clear the navcom. + Transmit_Message(RADIO_TETHER); + Look(); +// Map.Sight_From(Coord_Cell(Coord), 1, House, false); + } else { + Transmit_Message(RADIO_UNTETHER); + + /* + ** If the navigation computer is not attached to the object this + ** aircraft is in radio contact with, then assume that radio + ** contact is now superfluous. Break radio contact. + */ + if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) { + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * * + * Certain aircraft will be eliminated when they leave the edge of the world presumably * + * after completing their mission. An exception is for aircraft that have been newly * + * created as reinforcements and have not yet completed their mission. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft deleted by this routine? * + * * + * WARNINGS: Be sure to call this routine only once per aircraft per game logic loop. If * + * the return value is true, then abort any further processing of this aircraft * + * since it has been eliminated. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Edge_Of_World_AI(void) +{ + if (!Map.In_Radar(Coord_Cell(Coord))) { + if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) { + + /* + ** Check to see if there are any civilians aboard. If so, then flag the house + ** that the civilian evacuation trigger event has been fulfilled. + */ + while (Is_Something_Attached()) { + FootClass * obj = Detach_Object(); + + /* + ** Flag the owning house that civ evacuation has occurred. + */ + if (_Counts_As_Civ_Evac(obj)) { + obj->House->IsCivEvacuated = true; + } + + if (obj->Team.Is_Valid()) obj->Team->IsLeaveMap = true; + +#ifdef OLD + /* + ** Transport planes that leave can only be because they carry purchased + ** equipment and must be have their cost refunded. + */ + if (*this == AIRCRAFT_CARGO) { + House->Refund_Money(obj->Class_Of().Cost_Of()); + } +#endif + delete obj; + } + if (Team.Is_Valid()) { + Team->IsLeaveMap = true; + } + Stun(); + delete this; + return(true); + } + } else { + IsLocked = true; + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * * + * This routine manages the aircraft movement across the map. If any movement occurred, the * + * aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Movement_AI(void) +{ + /* + ** If for some strange reason, there is a valid NavCom, but this aircraft is not + ** in a movement order, then give it a movement order. + */ + if (Target_Legal(NavCom) && Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + + if (Speed != 0) { + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_UP); + Physics(Coord, PrimaryFacing); + Mark(MARK_DOWN); + } else { + Mark(MARK_CHANGE_REDRAW); + if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { + Mark(MARK_CHANGE_REDRAW); + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * * + * This will process the aircraft visible body and flight model rotation operations. If * + * any rotation occurred, the aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Rotation_AI(void) +{ + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } + if (Class->IsFixedWing) { + SecondaryFacing = PrimaryFacing; + } + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * * + * This is a seldom used function since its only purpose is to be called when an aircraft * + * lands on the ground. * + * * + * INPUT: why -- Why was this per cell process function called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/15/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Per_Cell_Process(PCPType why) +{ + BStart(BENCH_PCP); + FootClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * AircraftClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. Aircraft have their own version of this routine because a fixed-wing plane * + * trying to land will behave poorly if given a new destinatio while it's landing. * * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Assign_Destination(TARGET dest) +{ + assert(IsActive); + if (dest == NavCom) return; + + if (Target_Legal(dest) && Class->IsFixedWing && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { +// if (Target_Legal(dest) /*&& Class->IsFixedWing*/ && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { + +// if (Class->IsFixedWing || As_Cell(dest) != Coord_Cell(Center_Coord())) { + Process_Take_Off(); + Status = 0; +// } + } + FootClass::Assign_Destination(dest); +} + + +/*********************************************************************************************** + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * * + * This examines the aircraft to determine what display layer it should be located * + * in. Fixed wing aircraft must always be in the top layer if they are flying even though * + * they may be low to the ground. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this aircraft resides in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +LayerType AircraftClass::In_Which_Layer(void) const +{ + if (Class->IsFixedWing && Height > 0) { + return(LAYER_TOP); + } + return(FootClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * * + * Aircraft perform a look operation according to their sight range. If the aircraft is * + * on the ground, then it will look a distance of one cell regardless of what its * + * specified sight range is. * + * * + * INPUT: incremental -- Is this an incremental look? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/23/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + if (Height == 0) { + sight_range = 1; + } + + if (sight_range) { + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + } +} + +unsigned AircraftClass::Spied_By() const +{ + unsigned spiedby = FootClass::Spied_By(); + + /* + ** Aircraft inherit the spied by flags of the helipad they've landed on + */ + BuildingClass* building = Map[Coord].Cell_Building(); + if ((Height == 0) && In_Radio_Contact() && (building == Contact_With_Whom())) { + spiedby |= building->Spied_By(); + } + + return spiedby; +} \ No newline at end of file diff --git a/REDALERT/AIRCRAFT.H b/REDALERT/AIRCRAFT.H new file mode 100644 index 000000000..719419c8d --- /dev/null +++ b/REDALERT/AIRCRAFT.H @@ -0,0 +1,252 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AIRCRAFT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AIRCRAFT_H +#define AIRCRAFT_H + +#include "radio.h" +#include "fly.h" +#include "target.h" + + +/* +** This aircraft class is used for all flying sentient objects. This includes fixed wing +** aircraft as well as helicopters. It excludes bullets even though some bullets might +** be considered to be "flying" in a loose interpretatin of the word. +*/ +class AircraftClass : public FootClass, public FlyClass +{ + public: + /* + ** This is a pointer to the class control structure for the aircraft. + */ + CCPtr Class; + + //----------------------------------------------------------------------------- + static void * operator new(size_t); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *); + operator AircraftType(void) const {return Class->Type;}; + AircraftClass(AircraftType classid, HousesType house); + AircraftClass(NoInitClass const & x) : FootClass(x), FlyClass(x), Class(x), SecondaryFacing(x), SightTimer(x) {}; + virtual ~AircraftClass(void); + + static void Init(void); + + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Hunt(void); + virtual int Mission_Retreat(void); + virtual int Mission_Move(void); + virtual int Mission_Enter(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + + virtual void Assign_Destination(TARGET target); + /* + ** State machine support routines. + */ + bool Process_Take_Off(void); + bool Process_Landing(void); + int Process_Fly_To(bool slowdown, TARGET dest); + + /* + ** Query functions. + */ + virtual LayerType In_Which_Layer(void) const; + virtual DirType Turret_Facing(void) const {return(SecondaryFacing.Current());} + int Shape_Number(void) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual int Pip_Count(void) const; + TARGET Good_Fire_Location(TARGET target) const; + bool Cell_Seems_Ok(CELL cell, bool landing=false) const; + DirType Pose_Dir(void) const; + TARGET Good_LZ(void) const; + virtual DirType Fire_Direction(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + /* + ** Landing zone support functionality. + */ + virtual void Per_Cell_Process(PCPType why); + bool Is_LZ_Clear(TARGET target) const; + TARGET New_LZ(TARGET oldlz) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Look(bool incremental=false); + void Draw_Rotors(int x, int y, WindowNumberType window) const; + virtual int Exit_Object(TechnoClass *); + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Set_Speed(int speed); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which); + + /* + ** AI. + */ + bool Landing_Takeoff_AI(void); + bool Edge_Of_World_AI(void); + void Movement_AI(void); + void Rotation_AI(void); + int Paradrop_Cargo(void); + virtual void AI(void); + virtual void Enter_Idle_Mode(bool initial = false); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static char * INI_Name(void) {return "AIRCRAFT";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual unsigned Spied_By() const; + + public: + + /* + ** This is the facing used for the body of the aircraft. Typically, this is the same + ** as the PrimaryFacing, but in the case of helicopters, it can be different. + */ + FacingClass SecondaryFacing; + + /* + ** If this is a passenger carrying aircraft then this flag will be set. This is + ** necessary because once the passengers are unloaded, the fact that it was a + ** passenger carrier must still be known. + */ + bool Passenger; + + private: + + /* + ** Aircraft can be in either state of landing, taking off, or in steady altitude. + ** These flags are used to control transition between flying and landing. It is + ** necessary to handle the transition in this manner so that it occurs smoothly + ** during the graphic processing section. + */ + unsigned IsLanding:1; + unsigned IsTakingOff:1; + + /* + ** It is very common for aircraft to be homing in on a target. When this flag is + ** true, the aircraft will constantly adjust its facing toward the TarCom. When the + ** target is very close (one cell away or less), then this flag is automatically cleared. + ** This is because the homing algorithm is designed to get the aircraft to the destination + ** but no more. Checking when this flag is cleared is a way of flagging transition into + ** a new mode. Example: Transport helicopters go into a hovering into correct position + ** mode when the target is reached. + */ + unsigned IsHoming:1; + + /* + ** Helicopters that are about to land must hover into a position exactly above the landing + ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over + ** the TarCom. The facing of the aircraft is not altered by this movement. The affect + ** like the helicopter is hovering and shifting sideways to position over the landing + ** zone. When the position is over the landing zone, then this flag is set to false. + */ + unsigned IsHovering:1; + + /* + ** This is the jitter tracker to be used when the aircraft is a helicopter and + ** is flying. It is most noticeable when the helicopter is hovering. + */ + unsigned char Jitter; + + private: + + /* + ** This timer controls when the aircraft will reveal the terrain around itself. + ** When this timer expires and this aircraft has a sight range, then the + ** look around process will occur. + */ + CDTimerClass SightTimer; + + /* + ** Most attack aircraft can make several attack runs. This value contains the + ** number of attack runs the aircraft has left. When this value reaches + ** zero then the aircraft is technically out of ammo. + */ + char AttacksRemaining; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/ANIM.CPP b/REDALERT/ANIM.CPP new file mode 100644 index 000000000..65d3a16f5 --- /dev/null +++ b/REDALERT/ANIM.CPP @@ -0,0 +1,1221 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ANIM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Sort_Above -- Sorts the animation above the target specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#define VIC 1 + +/*********************************************************************************************** + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * * + * This routine will convert the supplied ASCII name into the animation type that it * + * represents. * + * * + * INPUT: name -- Pointer to the ASCII name to convert. * + * * + * OUTPUT: Returns with the animation type that matches the name specified. If no match could * + * be found, then ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Anim_From_Name(char const * name) +{ + #ifdef VIC + if (name == NULL) return(ANIM_NONE); + + for (int anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) { + if (stricmp(AnimTypeClass::As_Reference((AnimType)anim).IniName, name) == 0) { + return((AnimType)anim); + } + } +#endif + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj != NULL) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = * Anims.Ptr(index); + + if (As_Object(anim.xObject) == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L)); + } + if (Target_Legal(SortTarget)) { + ObjectClass * obj = As_Object(SortTarget); + if (obj && obj->IsActive) { + return(Coord_Add(obj->Sort_Y(), 0x00010000L)); + } + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/02/1996 JLB : Coordinate based on visual center of object. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(Coord, As_Object(xObject)->Target_Coord())); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Delay) return(false); + if (Map[Center_Coord()].IsVisible) { + const_cast(this)->IsToDisplay = true; // const_cast. ST - 5/8/2019 + } +#endif + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +void AnimClass::Draw_It(int x, int y, WindowNumberType window) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + bool render_legacy = !IsInvisible && (Class->VirtualAnim == ANIM_NONE || window != WINDOW_VIRTUAL); + bool render_virtual = Target_Legal(VirtualAnimTarget) && window == WINDOW_VIRTUAL; + + if (render_legacy) { + BStart(BENCH_ANIMS); + + IsTheaterShape = Class->IsTheater; + + void const * shapefile = Get_Image_Data(); + if (shapefile != NULL) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable; + if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable; + if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + if (transtable != NULL) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape, but ignore legacy if beyond normal stage count. + */ + if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, DIR_N, Class->VirtualScale); + } + } + IsTheaterShape = false; + BEnd(BENCH_ANIMS); + } + if (render_virtual) { + AnimClass* virtual_anim = As_Animation(VirtualAnimTarget); + virtual_anim->Make_Visible(); + virtual_anim->Draw_It(x, y, window); + virtual_anim->Make_Invisible(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +// ObjectClass::Mark(mark); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + if (IsToDelete) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + if (Class->Type == ANIM_ATOM_BLAST) { + return(OverlapAtom); + } + +#ifdef PARTIAL +IsTheaterShape = Class->IsTheater; + if (Class->Get_Image_Data() != NULL) { + int shapenum = Class->Start + Fetch_Stage(); + int count = Get_Build_Frame_Count(Class->Get_Image_Data()); + shapenum = min(shapenum, count-1); + + if (Class->DimensionData == NULL) { + Class->DimensionData = new Rect [count]; + } + if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); +IsTheaterShape = false; + return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum])); + } + } +IsTheaterShape = false; +#endif +#endif + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(bool) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + static short _simple[] = {REFRESH_EOL}; + +#endif + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + Anims.Free_All(); +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void * AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr != NULL) { + ((AnimClass *)ptr)->Set_Active(); + } else { + GlyphX_Debug_Print("AnimClass::operator new FAILED"); + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) : + ObjectClass(RTTI_ANIM, Anims.ID(this)), + Class(AnimTypes.Ptr((int)animnum)), + xObject(TARGET_NONE), + SortTarget(TARGET_NONE), + OwnerHouse(HOUSE_NONE), + Loops(1), + IsToDelete(false), + IsBrandNew(true), + IsInvisible(false), + Delay(timedelay), + Accum(0), + AttachLayer(LAYER_NONE) +{ +#ifdef VIC + if (Class->Stages == -1) { +IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); +IsTheaterShape = false; + + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + if (Class->IsGroundLayer) { + Height = FLIGHT_LEVEL; + } + + AnimClass::Unlimbo(coord); + + VisibleFlags = 0xffff; + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + // Added PlayerPtr here as Sight_From now needs to know who to perform the action for. This should be OK as long as it's not used in MP. ST - 8/2/2019 2:34PM + Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false); + } + + Loops = (unsigned char)(max(loop, 1) * Class->Loops); + Loops = (unsigned char)max(Loops, 1); + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } + + /* + ** Check for a virtual animation + */ + if (Class->VirtualAnim != ANIM_NONE) { + AnimClass* virtual_anim = new AnimClass(Class->VirtualAnim, 0, timedelay, loop); + if (virtual_anim != NULL) { + virtual_anim->Make_Invisible(); + VirtualAnimTarget = virtual_anim->As_Target(); + } else { + VirtualAnimTarget = TARGET_NONE; + } + } else { + VirtualAnimTarget = TARGET_NONE; + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Target_Legal(xObject) && As_Object(xObject) != NULL) { + ObjectClass * to = As_Object(xObject); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + int index; + for (index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index == Anims.Count()) { + to->Fire_Out(); + if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_UP); + to->IsAnimAttached = false; + if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_DOWN); + } + Coord = Coord_Add(to->Center_Coord(), Coord); + xObject = TARGET_NONE; + } + + Limbo(); + } + + xObject = TARGET_NONE; + Class = 0; + ID = -1; + +#endif +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ +#ifdef PARTIAL + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } +#else + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +#endif + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Don't allow corpses on rocks, rivers, and water (looks really weird). + */ + if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { + LandType land = Map[Center_Coord()].Land_Type(); + if (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER) { + IsToDelete = true; + } + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + delete this; + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (xObject != TARGET_NONE && Class->Damage > 0 && stage < Class->Stages) { + Accum += Class->Damage; + + if (Accum >= 1) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = Accum; + Accum -= damage; + if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + delete this; + if (Target_Legal(VirtualAnimTarget)) { + delete As_Animation(VirtualAnimTarget); + } + return; + } + } + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existence while in plain sight. + */ + if (Class->Biggest && Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + + if (Class->VirtualAnim != ANIM_NONE) { + Make_Invisible(); + if (!Target_Legal(VirtualAnimTarget)) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + delete this; + } + } + } + else { + if ((Class->VirtualStages < 0) || (stage >= Class->VirtualStages)) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + delete this; + } + } + } + } + } + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (obj == NULL) return; + assert(obj->IsActive); + + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_DOWN); + Limbo(); + xObject = obj->As_Target(); + Unlimbo(Coord); + AttachLayer = In_Which_Layer(); + Height = (AttachLayer == LAYER_GROUND) ? FLIGHT_LEVEL : 0; + Coord = Coord_Sub(Coord, obj->Target_Coord()); +#endif +} + + +/*********************************************************************************************** + * AnimClass::Sort_Above -- Sorts the animation right above the specified target. * + * * + * Allows an animation to always be sorted above a particular target. Typically used * + * for explosions and other effects that look weird beneath those objects. * + * * + * INPUT: target -- Target to sort above. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/2019 SKY : Created. * + *=============================================================================================*/ +void AnimClass::Sort_Above(TARGET target) +{ +#ifdef VIC + SortTarget = target; +#endif +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (AttachLayer != LAYER_NONE) { + return AttachLayer; + } + + if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { + return(LAYER_SURFACE); + } + + if (Target_Legal(xObject)) { + return As_Object(xObject)->In_Which_Layer(); + } + + if (Class->IsGroundLayer) { + return(LAYER_GROUND); + } +#endif + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsequent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/17/1995 JLB : Ion camera added. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + Do_Atom_Damage(OwnerHouse, cell); + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. Usually, these side effects are in the form of other + ** animations. + */ + switch (Class->Type) { + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2)); + } + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2)); + if (newanim != NULL && xObject != TARGET_NONE) { + newanim->Attach_To(As_Object(xObject)); + } + break; + + default: + break; + } +#endif +} + + +void AnimClass::Chain(void) +{ + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { + + Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + + IsToDelete = false; + Loops = Class->Loops; + Accum = 0; + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + Start(); + } +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (all) { + if (VirtualAnimTarget && VirtualAnimTarget == target) { + VirtualAnimTarget = TARGET_NONE; + } + if (xObject == target) { + Map.Remove(this, In_Which_Layer()); + xObject = TARGET_NONE; + AttachLayer = LAYER_NONE; + IsToDelete = true; + Mark(MARK_UP); + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * * + * This routine will apply damage around the ground-zero cell specified. * + * * + * INPUT: ownerhouse -- The owner of this atom bomb. * + * * + * cell -- The ground zero location to apply the atom bomb damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell) +{ +#ifdef VIC + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (ownerhouse != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (building == NULL) building = (BuildingClass *)backup; + } + + int radius; + int rawdamage; + if (Session.Type == GAME_NORMAL) { + radius = 4; + rawdamage = Rule.AtomDamage; + //WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 + } else { + radius = 3; + rawdamage = Rule.AtomDamage/5; + } + + Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE); + Shake_The_Screen(3); + if (Session.Type == GAME_NORMAL) { + //GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 + } +#endif +} \ No newline at end of file diff --git a/REDALERT/ANIM.H b/REDALERT/ANIM.H new file mode 100644 index 000000000..ff69a8a88 --- /dev/null +++ b/REDALERT/ANIM.H @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ANIM.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ANIM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : May 30, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ANIM_H +#define ANIM_H + +#include "type.h" + + +/********************************************************************************************** +** This is the class that controls the shape animation objects. Shape animation objects are +** displayed over the top of the game map. Typically, they are used for explosion and fire +** effects. +*/ +class AnimClass : public ObjectClass, public StageClass { + /* + ** This points to the type of animation object this is. + */ + CCPtr Class; + + public: + + AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1); + AnimClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~AnimClass(void); + + operator AnimType(void) const {return Class->Type;}; + + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + void Attach_To(ObjectClass *obj); + void Sort_Above(TARGET target); + void Make_Invisible(void) {IsInvisible = true;}; + void Make_Visible(void) {IsInvisible = false;}; + static void Do_Atom_Damage(HousesType ownerhouse, CELL cell); + + /* + ** 2019/09/19 JAS + ** Added functions for accessing which players can see this anim + */ + void Set_Visible_Flags(unsigned flags) { VisibleFlags = flags; } + unsigned Get_Visible_Flags() const { return (Delay == 0) ? VisibleFlags : 0; } + + virtual bool Can_Place_Here(COORDINATE ) const {return true;} + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual bool Render(bool forced) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void AI(void); + virtual void Detach(TARGET target, bool all); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(FileClass & file); + + /* + ** If this animation is attached to an object, then this points to that object. An + ** animation that is attached will follow that object as it moves. This is important + ** for animations such as flames and smoke. + */ + TARGET xObject; + + /* + ** If specified, this animation uses the sort target for Y sorting + */ + TARGET SortTarget; + + /* + ** If this animation has an owner, then it will be recorded here. An owner + ** is used when damage is caused by this animation during the middle of its + ** animation. + */ + HousesType OwnerHouse; + + /* + ** This counter tells how many more times the animation should loop before it + ** terminates. + */ + unsigned char Loops; + + protected: + void Middle(void); + void Start(void); + void Chain(void); + + private: + /* + ** Delete this animation at the next opportunity. This is flagged when the + ** animation is to be prematurely ended as a result of some outside event. + */ + unsigned IsToDelete:1; + + /* + ** If the animation has just been created, then don't do any animation + ** processing until it has been through the render loop at least once. + */ + unsigned IsBrandNew:1; + + /* + ** If this animation is invisible, then this flag will be true. An invisible + ** animation is one that is created for the sole purpose of keeping all + ** machines synchronized. It will not be displayed. + */ + unsigned IsInvisible:1; + + /* + ** 2019/09/19 JAS + ** Flags storing which players can see this anim. + */ + unsigned VisibleFlags; + + /* + ** Is this animation in a temporary suspended state? If so, then it won't + ** be rendered until this value is zero. The flag will be set to false + ** after the first countdown timer reaches 0. + */ + int Delay; + + /* + ** If this is an animation that damages whatever it is attached to, then this + ** value holds the accumulation of fractional damage points. When the accumulated + ** fractions reach 256, then one damage point is applied to the attached object. + */ + fixed Accum; + + /* + ** The map layer this animation is in when attached to an object. + */ + LayerType AttachLayer; + + /* + ** This references the virtual animation. + */ + TARGET VirtualAnimTarget; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + + + +#endif \ No newline at end of file diff --git a/REDALERT/AUDIO.CPP b/REDALERT/AUDIO.CPP new file mode 100644 index 000000000..22e55c2f4 --- /dev/null +++ b/REDALERT/AUDIO.CPP @@ -0,0 +1,858 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AUDIO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Speech_Name -- Fetches the name for the voice specified. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * Voc_Name -- Fetches the name for the sound effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + /* + ** + ** + ** Win32lib stubs + ** + ** + ** + */ + + +SFX_Type SoundType; +Sample_Type SampleType; + +int File_Stream_Sample(char const *filename, BOOL real_time_start) { return 1; }; +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) { return 1; }; +void __cdecl Sound_Callback(void) {}; +void __cdecl far maintenance_callback(void) {}; +void *Load_Sample(char const *filename) { return NULL; }; +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) { return 0; } +long Sample_Read(int fh, void *buffer, long size) { return 0; }; +void Free_Sample(void const *sample) {}; +BOOL Audio_Init(HWND window, int bits_per_sample, BOOL stereo, int rate, int reverse_channels) { return 0; }; +void Sound_End(void) {}; +void Stop_Sample(int handle) {}; +BOOL Sample_Status(int handle) { return 0; }; +BOOL Is_Sample_Playing(void const * sample) { return 0; }; +void Stop_Sample_Playing(void const * sample) {}; +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) { return 1; }; +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) { return 1; }; +int Set_Sound_Vol(int volume) { return 0; }; +int Set_Score_Vol(int volume) { return 0; }; +void Fade_Sample(int handle, int ticks) {}; +int Get_Free_Sample_Handle(int priority) { return 1; }; +int Get_Digi_Handle(void) { return 1; } +long Sample_Length(void const *sample) { return 0; }; +void Restore_Sound_Buffers(void) {}; +BOOL Set_Primary_Buffer_Format(void) { return 0; }; +BOOL Start_Primary_Sound_Buffer(BOOL forced) { return 0; }; +void Stop_Primary_Sound_Buffer(void) {}; + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +} ContextType; + +// static struct { // MBL 02.21.2019 +// Had to name the struct for VS 2017 distributed build. ST - 4/10/2019 3:59PM +struct SoundEffectNameStruct { + char const * Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1 + + /* + ** Infantry and vehicle responses. + */ + {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders" + {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative" + {"EENGIN1", 20, IN_NOVAR}, // VOC_ENG_ENG Engineer: "engineering" + {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting" + {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger" + {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it" + {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir" + + {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound + {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium). + {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short). + {"IRONCUR9", 10, IN_NOVAR}, // VOC_IRON1 + {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out" + {"SONPULSE", 10, IN_NOVAR}, // VOC_SONAR + {"SANDBAG2", 5, IN_NOVAR}, // VOC_SANDBAG sand bag crunch + {"MINEBLO1", 5, IN_NOVAR}, // VOC_MINEBLOW weird mine explosion + {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound. + {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark. + {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine. + {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl. + {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound. + {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound. + {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss. + {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow). + {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast). + {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir" + {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot. + {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect. + {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door. + {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect. + {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled). + {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled). + {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled). + {"SPLASH9", 5, IN_NOVAR}, // VOC_SPLASH water splash + {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp). + {"AACANON3", 1, IN_NOVAR}, + {"TANDETH1", 10, IN_NOVAR}, + {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast). + {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect. + {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch. + {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch. + {"x", 1, IN_NOVAR}, + {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles). + {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound. + {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound. + {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer. + {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot. + {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire. + {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch. + {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire. + {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up. + {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect. + {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect. + {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep. + {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics. + {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect. + {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound. + {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE). + {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion. + {"x", 10, IN_NOVAR}, + {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud). + {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'. + {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble. + {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick. + {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick. + {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound. + {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep. + {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble. + {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble. + {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble. + {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble. + {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence. + {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence. + {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound. + {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake. + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock" + {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha" + {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby" + {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching" + {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got" + {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye" + {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there" + {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me" + {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?" + {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?" + {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up." + {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound. + {"x", 10, IN_NOVAR}, + {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession. + {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound. + {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah" + {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok" + {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes" + {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound + + {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?" + {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir" + {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed" + {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way" + {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country" + {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting" + {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir" + {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative" + {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out" + {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep + + {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?" + {"ANTDIE", 20, IN_NOVAR}, // VOC_ANTDIE + {"ANTBITE", 20, IN_NOVAR}, // VOC_ANTBITE + {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out" + {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok" + {"x", 20, IN_NOVAR}, + {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what" + {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative" +//ADDED VG 2/24/97 + {"STAVCMDR", 20, IN_NOVAR}, + {"STAVCRSE", 20, IN_NOVAR}, + {"STAVYES", 20, IN_NOVAR}, + {"STAVMOV", 20, IN_NOVAR}, + {"BUZZY1", 20, IN_NOVAR}, + {"RAMBO1", 20, IN_NOVAR}, + {"RAMBO2", 20, IN_NOVAR}, + {"RAMBO3", 20, IN_NOVAR}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"MYES1", 20, IN_NOVAR}, // VOC_MECHYES1 Mechanic: "Yes sir!" + {"MHOWDY1", 20, IN_NOVAR}, // VOC_MECHHOWDY1 Mechanic: "Howdy!" + {"MRISE1", 20, IN_NOVAR}, // VOC_MECHRISE1 Mechanic: "Rise 'n shine!" + {"MHUH1", 20, IN_NOVAR}, // VOC_MECHHUH1 Mechanic: "Huh?" + {"MHEAR1", 20, IN_NOVAR}, // VOC_MECHHEAR1 Mechanic: "I Hear Ya!" + {"MLAFF1", 20, IN_NOVAR}, // VOC_MECHLAFF1 Mechanic: guffaw + {"MBOSS1", 20, IN_NOVAR}, // VOC_MECHBOSS1 Mechanic: "Sure Thing, Boss!" + {"MYEEHAW1", 20, IN_NOVAR}, // VOC_MECHYEEHAW1 Mechanic: "Yee Haw!" + {"MHOTDIG1", 20, IN_NOVAR}, // VOC_MECHHOTDIG1 Mechanic: "Hot Diggity Dog!" + {"MWRENCH1", 20, IN_NOVAR}, // VOC_MECHWRENCH1 Mechanic: "I'll get my wrench." + + {"JBURN1", 20, IN_NOVAR}, // VOC_STBURN1 Shock Trooper: "Burn baby burn!" + {"JCHRGE1", 20, IN_NOVAR}, // VOC_STCHRGE1 Shock Trooper: "Fully charged!" + {"JCRISP1", 20, IN_NOVAR}, // VOC_STCRISP1 Shock Trooper: "Extra Crispy!" + {"JDANCE1", 20, IN_NOVAR}, // VOC_STDANCE1 Shock Trooper: "Let's Dance!" + {"JJUICE1", 20, IN_NOVAR}, // VOC_STJUICE1 Shock Trooper: "Got juice?" + {"JJUMP1", 20, IN_NOVAR}, // VOC_STJUMP1 Shock Trooper: "Need a jump?" + {"JLIGHT1", 20, IN_NOVAR}, // VOC_STLIGHT1 Shock Trooper: "Lights out!" + {"JPOWER1", 20, IN_NOVAR}, // VOC_STPOWER1 Shock Trooper: "Power on!" + {"JSHOCK1", 20, IN_NOVAR}, // VOC_STSHOCK1 Shock Trooper: "Shocking!" + {"JYES1", 20, IN_NOVAR}, // VOC_STYES1 Shock Trooper: "Yesssss!" + + {"CHROTNK1", 20, IN_NOVAR}, // VOC_CHRONOTANK1 Chrono tank teleport + {"FIXIT1", 20, IN_NOVAR}, // VOC_MECH_FIXIT1 Mechanic fixes something + {"MADCHRG2", 20, IN_NOVAR}, // VOC_MAD_CHARGE MAD tank charges up + {"MADEXPLO", 20, IN_NOVAR}, // VOC_MAD_EXPLODE MAD tank explodes + {"SHKTROP1", 20, IN_NOVAR}, // VOC_SHOCK_TROOP1 Shock Trooper fires + +#endif +}; + + +// +// External handlers. MBL 06.17.2019 +// +extern void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house); +// extern void On_Speech(int speech_index); MBL 02.06.2020 +extern void On_Speech(int speech_index, HouseClass *house); +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + + + +/*********************************************************************************************** + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * * + * This will find the corresponding VocType from the ASCII string specified. It does this * + * by finding a root filename that matches the string. * + * * + * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. * + * * + * OUTPUT: Returns with the VocType that matches the string specified. If no match could be * + * found, then VOC_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +VocType Voc_From_Name(char const * name) +{ + if (name == NULL) return(VOC_NONE); + + for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) { + if (stricmp(name, SoundEffectName[voc].Name) == 0) { + return(voc); + } + } + + return(VOC_NONE); +} + + +/*********************************************************************************************** + * Voc_Name -- Fetches the name for the sound effect. * + * * + * This routine returns the descriptive name of the sound effect. Currently, this is just * + * the root of the file name. * + * * + * INPUT: voc -- The VocType that the corresponding name is requested. * + * * + * OUTPUT: Returns with a pointer to the text string the represents the sound effect. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Voc_Name(VocType voc) +{ + if (voc == VOC_NONE) return("none"); + return(SoundEffectName[voc].Name); +} + + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + * 09/15/1996 JLB : Revamped volume logic. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation, HousesType house) +{ + // + // Intercept sound effect calls. MBL 06.17.2019 + // + On_Sound_Effect((int)voc, variation, coord, (int)house); + +#if 0 + CELL cell_pos = 0; + int pan_value; + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + fixed volume = 1; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W; + fixed dfixed = fixed(distance, 128+64); + dfixed.Sub_Saturate(1); + volume = fixed(1) - dfixed; + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, volume, variation, pan_value, house); +#endif +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value, HousesType house) +{ + // + // Intercept sound effect calls. MBL 06.17.2019 + // + pan_value; + COORDINATE coord = 0; + On_Sound_Effect((int)voc, variation, coord, (int)house); + +#if 0 + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Alter the volume according to the game volume setting. + */ + volume = volume * Options.Volume; + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** If there is no forced house, then use the current player + ** act like house. + */ + if (house == HOUSE_NONE) { + house = PlayerPtr->ActLike; + } + + /* + ** Change the extension based on the variation and house accent requested. + */ + if (((1 << house) & HOUSEF_ALLIES) != 0) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } else { + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".R00"; + } else { + ext = ".R02"; + } + } else { + if (variation % 2) { + ext = ".R01"; + } else { + ext = ".R03"; + } + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MFCD::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr != NULL) { + volume.Sub_Saturate(1); + return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value)); + } +#endif + + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +/*static PG*/ char const * Speech[VOX_COUNT] = { + "MISNWON1", // VOX_ACCOMPLISHED mission accomplished + "MISNLST1", // VOX_FAIL your mission has failed + "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress + "CONSCMP1", // VOX_CONSTRUCTION construction complete + "UNITRDY1", // VOX_UNIT_READY unit ready + "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options + "NODEPLY1", // VOX_DEPLOY cannot deploy here + "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed + "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power + "NOFUNDS1", // VOX_NO_CASH insufficient funds + "BCT1", // VOX_CONTROL_EXIT battle control terminated + "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived + "CANCLD1", // VOX_CANCELED canceled + "ABLDGIN1", // VOX_BUILDING building + "LOPOWER1", // VOX_LOW_POWER low power + "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds + "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack + "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more + "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef ENGLISH + "TANK01", // VOX_MADTANK_DEPLOYED M.A.D. Tank Deployed +#else + "none", +#endif +#else + "none", +#endif + "none", // VOX_SOVIET_CAPTURED Allied building captured + "UNITLST1", // VOX_UNIT_LOST unit lost + "SLCTTGT1", // VOX_SELECT_TARGET select target + "ENMYAPP1", // VOX_PREPARE enemy approaching + "SILOND1", // VOX_NEED_MO_CAPACITY silos needed + "ONHOLD1", // VOX_SUSPENDED on hold + "REPAIR1", // VOX_REPAIRING repairing + "none", + "none", + "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost + "none", + "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching + "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived + "none", + "none", + "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated + "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging + "CHRORDY1", // VOX_CHRONO_READY chronosphere ready + "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful + "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack + "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated + "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching + "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost + "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed + "CREDIT1", // VOX_MONEY_STOLEN credits stolen + "NAVYLST1", // VOX_SHIP_LOST naval unit lost + "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched + "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available + "none", + "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching + "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived + "TRAIN1", // VOX_TRAINING training + "AREADY1", // VOX_ABOMB_READY + "ALAUNCH1", // VOX_ABOMB_LAUNCH + "AARRIVN1", // VOX_ALLIES_N + "AARRIVS1", // VOX_ALLIES_S + "AARIVE1", // VOX_ALLIES_E + "AARRIVW1", // VOX_ALLIES_W + "1OBJMET1", // VOX_OBJECTIVE1 + "2OBJMET1", // VOX_OBJECTIVE2 + "3OBJMET1", // VOX_OBJECTIVE3 + "IRONCHG1", // VOX_IRON_CHARGING + "IRONRDY1", // VOX_IRON_READY + "KOSYRES1", // VOX_RESCUED + "OBJNMET1", // VOX_OBJECTIVE_NOT + "FLAREN1", // VOX_SIGNAL_N + "FLARES1", // VOX_SIGNAL_S + "FLAREE1", // VOX_SIGNAL_E + "FLAREW1", // VOX_SIGNAL_W + "SPYPLN1", // VOX_SPY_PLANE + "TANYAF1", // VOX_FREED + "ARMORUP1", // VOX_UPGRADE_ARMOR + "FIREPO1", // VOX_UPGRADE_FIREPOWER + "UNITSPD1", // VOX_UPGRADE_SPEED + "MTIMEIN1", // VOX_MISSION_TIMER + "UNITFUL1", // VOX_UNIT_FULL + "UNITREP1", // VOX_UNIT_REPAIRED + "40MINR", // VOX_TIME_40 + "30MINR", // VOX_TIME_30 + "20MINR", // VOX_TIME_20 + "10MINR", // VOX_TIME_10 + "5MINR", // VOX_TIME_5 + "4MINR", // VOX_TIME_4 + "3MINR", // VOX_TIME_3 + "2MINR", // VOX_TIME_2 + "1MINR", // VOX_TIME_1 + "TIMERNO1", // VOX_TIME_STOP + "UNITSLD1", // VOX_UNIT_SOLD + "TIMERGO1", // VOX_TIMER_STARTED + "TARGRES1", // VOX_TARGET_RESCUED + "TARGFRE1", // VOX_TARGET_FREED + "TANYAR1", // VOX_TANYA_RESCUED + "STRUSLD1", // VOX_STRUCTURE_SOLD + "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN + "SOVEMP1", // VOX_SOVIET_SELECTED + "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN + "OPTERM1", // VOX_OPERATION_TERMINATED + "OBJRCH1", // VOX_OBJECTIVE_REACHED + "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED + "OBJMET1", // VOX_OBJECTIVE_MET + "MERCR1", // VOX_MERCENARY_RESCUED + "MERCF1", // VOX_MERCENARY_FREED + "KOSYFRE1", // VOX_KOSOYGEN_FREED + "FLARE1", // VOX_FLARE_DETECTED + "COMNDOR1", // VOX_COMMANDO_RESCUED + "COMNDOF1", // VOX_COMMANDO_FREED + "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS + "ATPREP1", // VOX_ATOM_PREPPING + "ASELECT1", // VOX_ALLIED_SELECTED + "APREP1", // VOX_ABOMB_PREPPING + "ATLNCH1", // VOX_ATOM_LAUNCHED + "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN + "AAVAIL1", // VOX_ABOMB_AVAILABLE + "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS + "SAVE1", // VOX_MISSION_SAVED + "LOAD1" // VOX_MISSION_LOADED +}; + + +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speech_Name -- Fetches the name for the voice specified. * + * * + * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this * + * would be to build a displayable list of the speech types. The trigger system uses this * + * so that a speech type can be selected. * + * * + * INPUT: speech -- The speech type id to convert to ASCII string. * + * * + * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 JLB : Created. * + *=============================================================================================*/ +char const * Speech_Name(VoxType speech) +{ + if (speech == VOX_NONE) return("none"); + return(Speech[speech]); +} + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +// void Speak(VoxType voice) // MBL 02.06.2020 +void Speak(VoxType voice, HouseClass *house, COORDINATE coord) +{ + // MBL 06.17.2019 + if (voice == VOX_NONE) + { + return; + } + + // + // Intercept speech calls. MBL 06.17.2019 + // + + // On_Speech((int)voice); // MBL 02.06.2020 + On_Speech((int)voice, house); + if (coord) { + On_Ping(house, coord); + } + +#if 0 + if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + Speak_AI(); + } +#endif +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 10/11/1996 JLB : Handles multiple speech buffers. * + *=============================================================================================*/ +void Speak_AI(void) +{ +// MBL 06.17.2019 KO +#if 0 + static int _index = 0; + if (Debug_Quiet || SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer[_index])) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + + /* + ** Try to find a previously loaded copy of the EVA speech in one of the + ** speech buffers. + */ + void const * speech = NULL; + for (int index = 0; index < ARRAY_SIZE(SpeechRecord); index++) { + if (SpeechRecord[index] == SpeakQueue) break; + } + + /* + ** If a previous copy could not be located, then load the requested + ** voice into the oldest buffer available. + */ + if (speech == NULL) { + _index = (_index + 1) % ARRAY_SIZE(SpeechRecord); + + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + CCFileClass file(name); + if (file.Is_Available() && file.Read(SpeechBuffer[_index], SPEECH_BUFFER_SIZE)) { + speech = SpeechBuffer[_index]; + SpeechRecord[_index] = SpeakQueue; + } + } + + /* + ** Since the speech file was loaded, play it. + */ + if (speech != NULL) { + Play_Sample(speech, 254, Options.Volume * 256); + CurrentVoice = SpeakQueue; + } + + SpeakQueue = VOX_NONE; + } + } +#endif + + // MBL 06.18.2019 + if (SpeakQueue != VOX_NONE) + { + // On_Speech((int)SpeakQueue); // MBL 02.06.2020 + On_Speech((int)SpeakQueue, NULL); + SpeakQueue = VOX_NONE; + } +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + Stop_Sample_Playing(SpeechBuffer); +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + if (!Debug_Quiet && SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) { + return(true); + } + return(false); +} diff --git a/REDALERT/AUDIO.H b/REDALERT/AUDIO.H new file mode 100644 index 000000000..3793f53b4 --- /dev/null +++ b/REDALERT/AUDIO.H @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\audio.h_v 4.43 05 Jul 1996 17:58:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : June 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "memory.h" + +class AudioClass { + char const * Name; // Name of audio asset. + void const * Data; // Loaded audio data. + int Handle; // Handle of asset (as it is playing). + MemoryClass *Mem; // Pointer to memory handler class. + unsigned IsMIDI:1; // Is this a midi file? + + public: + AudioClass(void); + AudioClass(char const *name, MemoryClass &mem); + virtual ~AudioClass(void); + + bool Load(char const *name = 0); + bool Free(void); + bool Play(int volume = 0xFF); + bool Stop(void); + bool Pause(void); + bool Resume(void); + bool Set_Name(char const *name); + bool Is_Playing(void) const; + bool Is_Loaded(void) const; + bool Is_MIDI(void) const; +}; + +inline AudioClass::AudioClass(void) +{ + Name = 0; + Data = 0; + Mem = 0; + Handle = -1; +}; + +inline AudioClass::AudioClass(char const *name, MemoryClass &mem) +{ + if (mem) { + Mem = &mem; + } else { + Mem = &::Mem; // Uses global default memory handler. + } + Name = strdup(name); + Data = 0; + Handle = -1; +}; + +inline AudioClass::~AudioClass(void) +{ + if (GameActive) { + if (Name) free(Name); + if (Data) Mem->Free(Data); + Name = 0; + Data = 0; + Handle = -1; + } +}; + + +#endif diff --git a/REDALERT/B64PIPE.CPP b/REDALERT/B64PIPE.CPP new file mode 100644 index 000000000..cefe2a4a4 --- /dev/null +++ b/REDALERT/B64PIPE.CPP @@ -0,0 +1,162 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64PIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64pipe.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * * + * This will take the data submitted and either Base64 encode or decode it (as specified * + * in the pipe's constructor). The nature of Base64 encoding means that the data will * + * grow 30% in size when encoding and decrease by a like amount when decoding. * + * * + * INPUT: source -- Pointer to the data to be translated. * + * * + * length -- The number of bytes to translate. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final end of * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + if (Counter > 0) { + int len = (slen < (fromsize-Counter)) ? slen : (fromsize-Counter); + memmove(&from[Counter], source, len); + Counter += len; + slen -= len; + source = ((char *)source) + len; + + if (Counter == fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(from, fromsize, to, tosize); + } else { + outcount = Base64_Decode(from, fromsize, to, tosize); + } + total += Pipe::Put(to, outcount); + Counter = 0; + } + } + + while (slen >= fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(source, fromsize, to, tosize); + } else { + outcount = Base64_Decode(source, fromsize, to, tosize); + } + source = ((char *)source) + fromsize; + total += Pipe::Put(to, outcount); + slen -= fromsize; + } + + if (slen > 0) { + memmove(from, source, slen); + Counter = slen; + } + + return(total); +} + + +/*********************************************************************************************** + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * * + * If there is any non-processed data accumulated in the holding buffer (quite likely when * + * encoding), then it will be processed and flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Flush(void) +{ + int len = 0; + + if (Counter) { + if (Control == ENCODE) { + int chars = Base64_Encode(PBuffer, Counter, CBuffer, sizeof(CBuffer)); + len += Pipe::Put(CBuffer, chars); + } else { + int chars = Base64_Decode(CBuffer, Counter, PBuffer, sizeof(PBuffer)); + len += Pipe::Put(PBuffer, chars); + } + Counter = 0; + } + len += Pipe::Flush(); + return(len); +} + + + diff --git a/REDALERT/B64PIPE.H b/REDALERT/B64PIPE.H new file mode 100644 index 000000000..9b6af7e83 --- /dev/null +++ b/REDALERT/B64PIPE.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64PIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64PIPE_H +#define B64PIPE_H + +#include "pipe.h" + +/* +** This class performs Base64 encoding/decoding to the data that is piped through. Note that +** encoded data will grow in size by about 30%. The reverse occurs when decoding. +*/ +class Base64Pipe : public Pipe +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Pipe(CodeControl control) : Control(control), Counter(0) {} + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Pipe(Base64Pipe & rvalue); + Base64Pipe & operator = (Base64Pipe const & pipe); +}; + +#endif diff --git a/REDALERT/B64STRAW.CPP b/REDALERT/B64STRAW.CPP new file mode 100644 index 000000000..d9283c558 --- /dev/null +++ b/REDALERT/B64STRAW.CPP @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64STRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64straw.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * * + * This routine will fetch the number of bytes requested and perform any conversion as * + * necessary upon the data. The nature of Base 64 encoding means that the data will * + * increase in size by 30% when encoding and decrease in like manner when decoding. * + * * + * INPUT: source -- The buffer to hold the processed data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number is less * + * than requested, then this indicates that the data stream has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Straw::Get(void * source, int slen) +{ + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + /* + ** Process the byte request in code blocks until there are either + ** no more source bytes available or the request has been fulfilled. + */ + while (slen > 0) { + + /* + ** Transfer any processed bytes available to the request buffer. + */ + if (Counter > 0) { + int len = (slen < Counter) ? slen : Counter; + memmove(source, &to[tosize-Counter], len); + Counter -= len; + slen -= len; + source = ((char *)source) + len; + total += len; + } + if (slen == 0) break; + + /* + ** More bytes are needed, so fetch and process another base 64 block. + */ + int incount = Straw::Get(from, fromsize); + if (Control == ENCODE) { + Counter = Base64_Encode(from, incount, to, tosize); + } else { + Counter = Base64_Decode(from, incount, to, tosize); + } + if (Counter == 0) break; + } + + return(total); +} diff --git a/REDALERT/B64STRAW.H b/REDALERT/B64STRAW.H new file mode 100644 index 000000000..022cceff5 --- /dev/null +++ b/REDALERT/B64STRAW.H @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64STRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64STRAW_H +#define B64STRAW_H + +#include "straw.h" + +/* +** Performs Base 64 encoding/decoding on the data that is drawn through the straw. Note that +** encoding increases the data size by about 30%. The reverse occurs when decoding. +*/ +class Base64Straw : public Straw +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Straw(CodeControl control) : Control(control), Counter(0) {} + virtual int Get(void * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Straw(Base64Straw & rvalue); + Base64Straw & operator = (Base64Straw const & pipe); +}; + + +#endif diff --git a/REDALERT/BAR.CPP b/REDALERT/BAR.CPP new file mode 100644 index 000000000..3e432c56a --- /dev/null +++ b/REDALERT/BAR.CPP @@ -0,0 +1,236 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BAR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * ProgressBarClass::Set_Limit -- Set the logic tracking value. * + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "bar.h" +#include "fixed.h" + + +/*********************************************************************************************** + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * * + * This is the constructor for the bargraph object. It establishes the dimensions and * + * coordinate of the bargraph as well as the colors it will use when drawn. * + * * + * INPUT: w,y -- Pixel coordinate of the upper left corner of the bargraph. * + * * + * width,height -- Dimensions of the bargraph. * + * * + * forecolor -- The color to use for the filled portion of the bargraph. * + * * + * backcolor -- The color to use for the non-filled portion of the bargraph. * + * * + * bordercolor -- Optional border color. If not zero, then the bargraph will be * + * outlined with this color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +ProgressBarClass::ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor) : + X(x), + Y(y), + Width(width), + Height(height), + BarColor(forecolor), + BackColor(backcolor), + BorderColor(bordercolor), + CurrentValue(0), + LastDisplayCurrent(0), + IsDrawn(false) +{ +} + + +/*********************************************************************************************** + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * * + * If the bargraph is oriented horizontally, then this function will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this bargraph horizontal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Is_Horizontal(void) const +{ + if (Width > Height) return(true); + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * * + * This will update the value of the bargraph to the fill ratio specified and then * + * redraw it if required. Very small changes to the bargraph value might not result in a * + * visual change. * + * * + * INPUT: value -- The new value to assign to this bargraph. * + * * + * OUTPUT: none * + * * + * WARNINGS: bool; Did this update result in a redraw? * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Update(fixed value) +{ + CurrentValue = value; + + if (!IsDrawn || value - LastDisplayCurrent >= fixed(1, 10)) { + Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * * + * This routine will draw a border around the bargraph if this bargraph has a color * + * specified for the border. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Outline(void) const +{ + if (Is_Outlined()) { + LogicPage->Draw_Line(X, Y, X+Width, Y, BorderColor); + LogicPage->Draw_Line(X, Y, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X, Y+Height, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X+Width, Y, X+Width, Y+Height, BorderColor); + } +} + + +/*********************************************************************************************** + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * * + * This will redraw the entire bargraph. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Redraw(void) const +{ + Hide_Mouse(); + + Outline(); + + /* + ** Determine the inner dimensions of the bargraph. This will be + ** somewhat smaller than indicated if it has a border. + */ + int x = X; + int y = Y; + int w = Width; + int h = Height; + if (Is_Outlined()) { + x += 1; + y += 1; + w -= 2; + h -= 2; + } + + /* + ** The working "length" of the bargraph is dependant on whether the + ** bargraph is horizontal or vertical. + */ + int size = Is_Horizontal() ? w : h; + + /* + ** Determine the number of pixels to fill in the bargraph depending on the + ** size of the internal value. The larger the internal value the more + ** filled the bargraph becomes. + */ + int fill = CurrentValue * size; + + /* + ** Draw the filled portion of the bargraph if there is any pixels to draw. + */ + if (fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x, y, x+fill, y+h, BarColor); + } else { + LogicPage->Fill_Rect(x, y+fill, x+w, y+h, BarColor); + } + } + + /* + ** Draw the unfilled portion of the bargraph if there are any pixels to + ** draw of it. + */ + if (w-fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x+fill, y, x+w, y+h, BackColor); + } else { + LogicPage->Fill_Rect(x, y, x+w, y+fill-1, BackColor); + } + } + + Show_Mouse(); + + ProgressBarClass * me = (ProgressBarClass *)this; + me->LastDisplayCurrent = CurrentValue; + me->IsDrawn = true; +} diff --git a/REDALERT/BAR.H b/REDALERT/BAR.H new file mode 100644 index 000000000..3e0ea79e0 --- /dev/null +++ b/REDALERT/BAR.H @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BAR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BAR_H +#define BAR_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include "fixed.h" + + +/* +** This is a manager for a progress (or other) bargraph. Such a graph consists of a fill +** and a background region. The fill percentage of the bargraph is controlled by an +** update value. The bargraph can be optionally outlined. +*/ +class ProgressBarClass +{ + public: + ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor=0); + + bool Update(fixed value); + void Redraw(void) const; + + private: + + void Outline(void) const; + bool Is_Horizontal(void) const; + bool Is_Outlined(void) const {return(BorderColor != 0);} + + /* + ** This is the upper left coordinates of the bargraph. + */ + int X,Y; + + /* + ** This is the dimensions of the bargraph. + */ + int Width, Height; + + /* + ** These are the colors to use when drawing the progress bar. + */ + int BarColor; + int BackColor; + int BorderColor; + + /* + ** This is the current value of the bargraph. + */ + fixed CurrentValue; + + /* + ** This is the current value as of the last time the bargraph was rendered. + */ + fixed LastDisplayCurrent; + + /* + ** If the bargraph has been drawn at least once, then this flag will + ** be true. + */ + unsigned IsDrawn:1; +}; + + +#endif diff --git a/REDALERT/BASE.CPP b/REDALERT/BASE.CPP new file mode 100644 index 000000000..14107dfd8 --- /dev/null +++ b/REDALERT/BASE.CPP @@ -0,0 +1,545 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * BaseClass::Load -- loads from a saved game file * + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * BaseClass::Read_INI -- INI reading routine * + * BaseClass::Save -- saves to a saved game file * + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * BaseNodeClass::operator != -- inequality operator * + * BaseNodeClass::operator == -- equality operator * + * BaseNodeClass::operator > -- greater-than operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BaseNodeClass::operator == -- equality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * true = equal, false = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator == (BaseNodeClass const & node) +{ + return(Type == node.Type && Cell == node.Cell); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator != -- inequality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator !=(BaseNodeClass const & node) +{ + return(!(*this == node)); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator > -- greater-than operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator > (BaseNodeClass const & ) +{ + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Load -- loads from a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to demand driven data source. * + *=============================================================================================*/ +bool BaseClass::Load(Straw & file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Read in & check the size of this class + */ + if (file.Get(&i, sizeof(i)) != sizeof(i)) { + return(false); + } + + if (i != sizeof(*this)) { + return(false); + } + + /* + ** Read in the House & the number of structures in the base + */ + if (file.Get(&House, sizeof(House)) != sizeof(House)) { + return(false); + } + + if (file.Get(&num_struct, sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Read each node entry & add it to the list + */ + for (i = 0; i < num_struct; i++) { + if (file.Get(&node, sizeof(node)) != sizeof(node)) { + return(false); + } + Nodes.Add(node); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Save -- saves to a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to supply driven data output. * + *=============================================================================================*/ +bool BaseClass::Save(Pipe & file) const +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Write the size of this class + */ + i = sizeof(*this); + file.Put(&i, sizeof(i)); + + /* + ** Write the House & the number of structures in the base + */ + file.Put(&House, sizeof(House)); + + num_struct = Nodes.Count(); + file.Put(&num_struct, sizeof(num_struct)); + + /* + ** Write each node entry + */ + for (i = 0; i < num_struct; i++) { + node = Nodes[i]; + file.Put(&node, sizeof(node)); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * * + * INPUT: * + * index index into base list * + * * + * OUTPUT: * + * true = yes, false = no * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Built(int index) const +{ + if (Get_Building(index) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to already-built building, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/30/1996 JLB : Handle arbitrary overlapper list length. * + *=============================================================================================*/ +BuildingClass * BaseClass::Get_Building(int index) const +{ + ObjectClass * obj[1 + ARRAY_SIZE(Map[(CELL)0].Overlapper)]; + + /* + ** Check the location on the map where this building should be; if it's + ** there, return a pointer to it. + */ + CELL cell = Nodes[index].Cell; + + obj[0] = Map[cell].Cell_Building(); + int count = 1; + for (int xindex = 0; xindex < ARRAY_SIZE(Map[cell].Overlapper); xindex++) { + if (Map[cell].Overlapper[xindex] != NULL) { + obj[count++] = Map[cell].Overlapper[xindex]; + } + } + + BuildingClass * bldg = NULL; + for (int i = 0; i < count; i++) { + if (obj[i] && + Coord_Cell(obj[i]->Coord) == Nodes[index].Cell && + obj[i]->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) { + + bldg = (BuildingClass *)obj[i]; + break; + } + } + + return(bldg); +} + + +/*********************************************************************************************** + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * true = building is a node in the list, false = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Node(BuildingClass const * obj) +{ + if (Get_Node(obj) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to node * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(BuildingClass const * obj) +{ + for (int i = 0; i < Nodes.Count(); i++) { + if (obj->Class->Type == Nodes[i].Type && Coord_Cell(obj->Coord) == Nodes[i].Cell) { + return(&Nodes[i]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * * + * This routine is used to find a matching node the corresponds to the cell specified. * + * * + * INPUT: cell -- The cell to use in finding a match. * + * * + * OUTPUT: Returns a pointer to the matching node if found. If not found, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1996 JLB : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(CELL cell) +{ + for (int index = 0; index < Nodes.Count(); index++) { + if (cell == Nodes[index].Cell) { + return(&Nodes[index]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * * + * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. * + * Otherwise, returns ptr to the next hole in the list of any type. * + * * + * INPUT: * + * type type of building to check for * + * * + * OUTPUT: * + * ptr to a BaseNodeClass, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Next_Buildable(StructType type) +{ + /* + ** Loop through all node entries, returning a pointer to the first + ** un-built one that matches the requested type. + */ + for (int i = 0; i < Nodes.Count(); i++) { + + /* + ** For STRUCT_NONE, return the first hole found + */ + if (type == STRUCT_NONE) { + if (!Is_Built(i)) { + return(&Nodes[i]); + } + + } else { + + /* + ** For a "real" building type, return the first hold for that type + */ + if (Nodes[i].Type==type && !Is_Built(i)) { + return(&Nodes[i]); + } + } + } + + +// If no entry could be found, then create a fake one that will allow +// placement of the building. Make it static and reuse the next time this +// routine is called. + + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Read_INI -- INI reading routine * + * * + * INI entry format: * + * BLDG=COORDINATE * + * BLDG=COORDINATE * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 02/20/1996 JLB : Fixed to know what house to build base from. * + *=============================================================================================*/ +void BaseClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; + char uname[10]; + BaseNodeClass node; // node to add to list + + Mono_Clear_Screen(); + /* + ** First, determine the house of the human player, and set the Base's house + ** accordingly. + */ + House = ini.Get_HousesType(INI_Name(), "Player", PlayerPtr->Class->House); + + /* + ** Read the number of buildings that will go into the base node list + */ + int count = ini.Get_Int(INI_Name(), "Count", 0); + + /* + ** Read each entry in turn, in the same order they were written out. + */ + for (int i = 0; i < count; i++) { + + /* + ** Get an INI entry + */ + sprintf(uname,"%03d",i); + ini.Get_String(INI_Name(), uname, NULL, buf, sizeof(buf)); + + /* + ** Set the node's building type + */ + node.Type = BuildingTypeClass::From_Name(strtok(buf,",")); + + /* + ** Read & set the node's coordinate + */ + node.Cell = atoi(strtok(NULL,",")); + + /* + ** Add this node to the Base's list + */ + Nodes.Add(node); + } +} + + +/*********************************************************************************************** + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * * + * Use this routine to write all prebuild base information to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: If there was any preexisting prebuild base data in the database, it will be * + * be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void BaseClass::Write_INI(CCINIClass & ini) +{ + /* + ** Clear out all existing base data from the ini file. + */ + ini.Clear(INI_Name()); + + if (House != HOUSE_NONE) { + + /* + ** Write out the owner of this buildable list. + */ + ini.Put_HousesType(INI_Name(), "Player", House); + + /* + ** Save the # of buildings in the Nodes list. This is essential because + ** they must be read in the same order they were created, so "000" must be + ** read first, etc. + */ + ini.Put_Int(INI_Name(), "Count", Nodes.Count()); + + /* + ** Write each entry into the INI + */ + for (int i = 0; i < Nodes.Count(); i++) { + char buf[128]; + char uname[10]; + + sprintf(uname,"%03d",i); + sprintf(buf,"%s,%d", + BuildingTypeClass::As_Reference(Nodes[i].Type).IniName, + Nodes[i].Cell); + + ini.Put_String(INI_Name(), uname, buf); + } + } +} diff --git a/REDALERT/BASE.H b/REDALERT/BASE.H new file mode 100644 index 000000000..1e3157c02 --- /dev/null +++ b/REDALERT/BASE.H @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BASE_H +#define BASE_H + + +/**************************************************************************** +** This class defines one "node" in the pre-built base list. Each node +** contains a type of building to build, and the COORDINATE to build it at. +*/ +class BaseNodeClass +{ + public: + BaseNodeClass(void) {}; + BaseNodeClass(StructType building, CELL cell) : Type(building), Cell(cell) {}; + int operator == (BaseNodeClass const & node); + int operator != (BaseNodeClass const & node); + int operator > (BaseNodeClass const & node); + + StructType Type; + CELL Cell; +}; + + +/**************************************************************************** +** This is the class that defines a pre-built base for the computer AI. +** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!) +*/ +class BaseClass +{ + public: + + /* + ** Constructor/Destructor + */ + BaseClass(void) {}; + virtual ~BaseClass() {Nodes.Clear();} + + /* + ** Initialization + */ + void Init(void) {House = HOUSE_NONE; Nodes.Clear();} + + /* + ** The standard suite of load/save support routines + */ + void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "Base";} + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void) {}; + virtual void Decode_Pointers(void) {}; + + /* + ** Tells if the given node has been built or not + */ + bool Is_Built(int index) const; + + /* + ** Returns a pointer to the object for the given node + */ + BuildingClass * Get_Building(int index) const; + + /* + ** Tells if the given building ptr is a node in this base's list. + */ + bool Is_Node(BuildingClass const * obj); + + /* + ** Returns a pointer to the requested node. + */ + BaseNodeClass * Get_Node(BuildingClass const * obj); + BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); } + BaseNodeClass * Get_Node(CELL cell); + + /* + ** Returns a pointer to the next "hole" in the Nodes list. + */ + BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE); + + /* + ** This is the list of "nodes" that define the base. Portions of this + ** list can be pre-built by simply saving those buildings in the INI + ** along with non-base buildings, so Is_Built will return true for them. + */ + DynamicVectorClass Nodes; + + /* + ** This is the house this base belongs to. + */ + HousesType House; +}; + + +#endif + diff --git a/REDALERT/BASE64.CPP b/REDALERT/BASE64.CPP new file mode 100644 index 000000000..a8b1a22c7 --- /dev/null +++ b/REDALERT/BASE64.CPP @@ -0,0 +1,434 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE64.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE64.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * Base64_Encode -- Encode data into Base 64 format. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "base64.h" +#include + +/* +** This is the magic padding character used to fill out the encoded data to a multiple of +** 4 characters even though the source data is less than necessary to accomplish this. +** The pad character lets the decoder know of this condition and it will compensate +** accordingly. +*/ +static char const * const _pad = "="; + +/* +** This encoder translation table will convert a 6 bit number into an ASCII character. +*/ +static char const * const _encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** The decoder translation table takes an ASCII character and converts it into a +** 6 bit number. +*/ +#define BAD 0xFE // Ignore this character in source data. +#define END 0xFF // Signifies premature end of input data. +static unsigned char const _decoder[256] = { + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,62,BAD,BAD,BAD,63, + 52,53,54,55,56,57,58,59,60,61,BAD,BAD,BAD,END,BAD,BAD, + BAD,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,BAD,BAD,BAD,BAD,BAD, + BAD,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD +}; + +int const PacketChars = 4; + + +/* +** The packet type is used to construct and disect the Base64 data blocks. The data +** consists of three source data bytes mapped onto four 6 bit Base64 code elements. +*/ +typedef union { + struct { +#ifdef BIG_ENDIAN + unsigned char C1; + unsigned char C2; + unsigned char C3; +#else + unsigned char C3; + unsigned char C2; + unsigned char C1; +#endif + unsigned char pad; + } Char; + struct { +#ifdef BIG_ENDIAN + unsigned O1:6; + unsigned O2:6; + unsigned O3:6; + unsigned O4:6; +#else + unsigned O4:6; + unsigned O3:6; + unsigned O2:6; + unsigned O1:6; +#endif + unsigned pad:8; + } SubCode; + unsigned int Raw; +} PacketType; + + +/*********************************************************************************************** + * Base64_Encode -- Encode data into Base 64 format. * + * * + * This will take an arbitrary length of source data and transform it into base 64 format * + * data. Base 64 format has the property of being very portable across text editors and * + * country character encoding schemes. As such it is ideal for e-mail. Note that the output * + * data will be about 33% larger than the source. * + * * + * INPUT: source -- Pointer to the source data to convert. * + * * + * slen -- The number of bytes to encode. * + * * + * dest -- Pointer to the destination buffer that will hold the encoded data. * + * * + * dlen -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the encoded output. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Encode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + /* + ** Process the source data in blocks of three bytes. Fewer than three bytes + ** results in special padding output characters (automatically discarded + ** during the decode process). + */ + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen >= PacketChars) { + + /* + ** Fetch 24 bits of source data. + */ + PacketType packet; + + int pad = 0; + packet.Raw = 0; + packet.Char.C1 = *sptr++; + slen--; + if (slen) { + packet.Char.C2 = *sptr++; + slen--; + } else { + pad++; + } + if (slen) { + packet.Char.C3 = *sptr++; + slen--; + } else { + pad++; + } + + /* + ** Translate and write 4 characters of Base64 data. Pad with pad + ** characters if there is insufficient source data for a full packet. + */ + *dptr++ = _encoder[packet.SubCode.O1]; + *dptr++ = _encoder[packet.SubCode.O2]; + if (pad < 2) { + *dptr++ = _encoder[packet.SubCode.O3]; + } else { + *dptr++ = _pad[0]; + } + if (pad < 1) { + *dptr++ = _encoder[packet.SubCode.O4]; + } else { + *dptr++ = _pad[0]; + } + + dlen -= PacketChars; + total += PacketChars; + } + + /* + ** Add a trailing null as a courtesy measure. + */ + if (dlen > 0) { + *dptr = '\0'; + } + + /* + ** Return with the total number of characters in the output buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * * + * Use this routine to decode base 64 data back into the original data. A property of this * + * decode process is that unrecognized input characters are ignored. This allows mangled * + * source (filled with line breaks or spaces) to be correctly decoded. The decode process * + * terminates when the end of the source data has been reached or the special end of data * + * marker is encountered. * + * * + * INPUT: source -- Pointer to the source data to decode. * + * * + * slen -- The number of bytes in the source data buffer. * + * * + * dest -- Pointer to the destination buffer to be filled with the decoded data. * + * * + * dlen -- The maximum size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. This will * + * always be less than the number of source bytes (usually by about 33%). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Decode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen > 0) { + + PacketType packet; + packet.Raw = 0; + + /* + ** Process input until a full packet has been accumulated or the + ** source is exhausted. + */ + int pcount = 0; + while (pcount < PacketChars && slen > 0) { + unsigned char c = *sptr++; + slen--; + + unsigned char code = _decoder[c]; + + /* + ** An unrecognized character is skipped. + */ + if (code == BAD) continue; + + /* + ** The "=" character signifies the end of data regardless of what + ** the source buffer length value may be. + */ + if (code == END) { + slen = 0; + break; + } + + /* + ** A valid Base64 character was found so add it to the packet + ** data. + */ + switch (pcount) { + case 0: + packet.SubCode.O1 = code; + break; + case 1: + packet.SubCode.O2 = code; + break; + case 2: + packet.SubCode.O3 = code; + break; + case 3: + packet.SubCode.O4 = code; + break; + } + pcount++; + } + + /* + ** A packet block is ready for output into the destination buffer. + */ + *dptr++ = packet.Char.C1; + dlen--; + total++; + if (dlen > 0 && pcount > 2) { + *dptr++ = packet.Char.C2; + dlen--; + total++; + } + if (dlen > 0 && pcount > 3) { + *dptr++ = packet.Char.C3; + dlen--; + total++; + } + } + + /* + ** Return with the total number of characters decoded into the + ** output buffer. + */ + return(total); +} + + +/* +Base64 Content-Transfer-Encoding + +The Base64 Content-Transfer-Encoding is designed to represent arbitrary +sequences of octets in a form that need not be humanly readable. The encoding +and decoding algorithms are simple, but the encoded data are consistently +only about 33 percent larger than the unencoded data. This encoding is +virtually identical to the one used in Privacy Enhanced Mail (PEM) +applications, as defined in RFC 1421. The base64 encoding is adapted from +RFC 1421, with one change: base64 eliminates the "*" mechanism for embedded +clear text. + +A 65-character subset of US-ASCII is used, enabling 6 bits to be represented +per printable character. (The extra 65th character, "=", is used to signify a +special processing function.) + +NOTE: + This subset has the important property that it is represented identically + in all versions of ISO 646, including US ASCII, and all characters in the + subset are also represented identically in all versions of EBCDIC. Other + popular encodings, such as the encoding used by the uuencode utility and + the base85 encoding specified as part of Level 2 PostScript, do not share + these properties, and thus do not fulfill the portability requirements a + binary transport encoding for mail must meet. + +The encoding process represents 24-bit groups of input bits as output strings +of 4 encoded characters. Proceeding from left to right, a 24-bit input group is +formed by concatenating 3 8-bit input groups. These 24 bits are then treated as +4 concatenated 6-bit groups, each of which is translated into a single digit in +the base64 alphabet. When encoding a bit stream via the base64 encoding, the +bit stream must be presumed to be ordered with the most-significant-bit first. +That is, the first bit in the stream will be the high-order bit in the first +byte, and the eighth bit will be the low-order bit in the first byte, and so on. + +Each 6-bit group is used as an index into an array of 64 printable characters. +The character referenced by the index is placed in the output string. These +characters, identified in Table 1, below, are selected so as to be universally +representable, and the set excludes characters with particular significance to +SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries defined in this +document (e.g., "-"). + +Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + +The output stream (encoded bytes) must be represented in lines of no more than +76 characters each. All line breaks or other characters not found in Table 1 +must be ignored by decoding software. In base64 data, characters other than +those in Table 1, line breaks, and other white space probably indicate a +transmission error, about which a warning message or even a message rejection +might be appropriate under some circumstances. + +Special processing is performed if fewer than 24 bits are available at the end +of the data being encoded. A full encoding quantum is always completed at the +end of a body. When fewer than 24 input bits are available in an input group, +zero bits are added (on the right) to form an integral number of 6-bit groups. +Padding at the end of the data is performed using the '=' character. Since all +base64 input is an integral number of octets, only the following cases can +arise: (1) the final quantum of encoding input is an integral multiple of 24 +bits; here, the final unit of encoded output will be an integral multiple of 4 +characters with no "=" padding, (2) the final quantum of encoding input is +exactly 8 bits; here, the final unit of encoded output will be two characters +followed by two "=" padding characters, or (3) the final quantum of encoding +input is exactly 16 bits; here, the final unit of encoded output will be three +characters followed by one "=" padding character. + +Because it is used only for padding at the end of the data, the occurrence of +any '=' characters may be taken as evidence that the end of the data has been +reached (without truncation in transit). No such assurance is possible, +however, when the number of octets transmitted was a multiple of three. + +Any characters outside of the base64 alphabet are to be ignored in +base64-encoded data. The same applies to any illegal sequence of characters in +the base64 encoding, such as "=====" + +Care must be taken to use the proper octets for line breaks if base64 encoding +is applied directly to text material that has not been converted to canonical +form. In particular, text line breaks must be converted into CRLF sequences +prior to base64 encoding. The important thing to note is that this may be done +directly by the encoder rather than in a prior canonicalization step in some +implementations. + +NOTE: + There is no need to worry about quoting apparent encapsulation boundaries + within base64-encoded parts of multipart entities because no hyphen + characters are used in the base64 encoding. + +*/ \ No newline at end of file diff --git a/REDALERT/BASE64.H b/REDALERT/BASE64.H new file mode 100644 index 000000000..82cf1cbfe --- /dev/null +++ b/REDALERT/BASE64.H @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE64.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE64.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +int Base64_Encode(void const * source, int slen, void * dest, int dlen); +int Base64_Decode(void const * source, int slen, void * dest, int dlen); diff --git a/REDALERT/BBDATA.CPP b/REDALERT/BBDATA.CPP new file mode 100644 index 000000000..a3db31894 --- /dev/null +++ b/REDALERT/BBDATA.CPP @@ -0,0 +1,297 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BBDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BBDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specif* + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. * + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + + +/*********************************************************************************************** + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * * + * This is basically a constructor for static type objects used by bullets. All bullets * + * are of a type constructed by this routine at game initialization time. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 07/17/1996 JLB : Uses correct default values. * + *=============================================================================================*/ +BulletTypeClass::BulletTypeClass(char const * name) : + ObjectTypeClass( RTTI_BULLETTYPE, + BulletTypes.ID(this), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsHigh(false), + IsShadow(true), + IsArcing(false), + IsDropping(false), + IsInvisible(false), + IsProximityArmed(false), + IsFlameEquipped(false), + IsFueled(false), + IsFaceless(true), + IsInaccurate(false), + IsTranslucent(false), + IsAntiAircraft(false), + IsAntiGround(true), + IsAntiSub(false), + IsDegenerate(false), + IsSubSurface(false), + IsParachuted(false), + IsGigundo(false), + Type(BulletType(ID)), + ROT(0), + Arming(0), + Tumble(0) +{ +} + + +/*********************************************************************************************** + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * * + * This allocates a bullet type object from a special heap that is used just for * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to an allocated block or NULL if the allocation could not * + * occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BulletTypeClass::operator new(size_t) +{ + return(BulletTypes.Alloc()); +} + + +/*********************************************************************************************** + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * * + * This is the counterpart to the operator new function for bullet type objects. It will * + * return the bullet type object back to the special heap used for bullet type object * + * allocation. * + * * + * INPUT: ptr -- Pointer to the bullet type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::operator delete(void * ptr) +{ + BulletTypes.Free((BulletTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * * + * This performs any necessary initialization for the bullet types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::Init_Heap(void) +{ + /* + ** These bullet type class objects must be allocated in the exact order that they + ** are specified in the BulletType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BulletTypeClass("Invisible"); // BULLET_INVISIBLE + new BulletTypeClass("Cannon"); // BULLET_CANNON + new BulletTypeClass("Ack"); // BULLET_ACK + new BulletTypeClass("Torpedo"); // BULLET_TORPEDO + new BulletTypeClass("FROG"); // BULLET_FROG + new BulletTypeClass("HeatSeeker"); // BULLET_HEAT_SEEKER + new BulletTypeClass("LaserGuided"); // BULLET_LASER_GUIDED + new BulletTypeClass("Lobbed"); // BULLET_LOBBED + new BulletTypeClass("Bomblet"); // BULLET_BOMBLET + new BulletTypeClass("Ballistic"); // BULLET_BALLISTIC + new BulletTypeClass("Parachute"); // BULLET_PARACHUTE + new BulletTypeClass("Fireball"); // BULLET_FIREBALL + new BulletTypeClass("LeapDog"); // BULLET_DOG + new BulletTypeClass("Catapult"); // BULLET_CATAPULT + new BulletTypeClass("AAMissile"); // BULLET_AAMISSILE + new BulletTypeClass("GPSSatellite");// BULLET_GPS_SATELLITE + new BulletTypeClass("NukeUp"); // BULLET_NUKE_UP + new BulletTypeClass("NukeDown"); // BULLET_NUKE_DOWN +} + + +/*********************************************************************************************** + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * * + * This routine is used to perform any one time processing for the bullet type class. It * + * handles loading of the shape files. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called before any rendering of bullets occurs and should * + * only be called once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::One_Time(void) +{ + /* + ** Load the bullet shapes. + */ + for (int index = BULLET_FIRST; index < BULLET_COUNT; index++) { + BulletTypeClass const & bullet = As_Reference((BulletType)index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + if (!bullet.IsInvisible) { + _makepath(fullname, NULL, NULL, bullet.GraphicName, ".SHP"); + + #ifdef NDEBUG + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + #else + RawFileClass file(fullname); + + if (file.Is_Available()) { + ((void const *&)bullet.ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + } + #endif + } + } +} + + +/*********************************************************************************************** + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specifi * + * * + * Given a bullet type identifier, this routine will return a reference to the bullet type * + * object it refers to. * + * * + * INPUT: type -- The bullet type identifier to convert to a reference. * + * * + * OUTPUT: Returns with a reference to the bullet type object. * + * * + * WARNINGS: Make sure that the type parameter specified is a valid bullet type. If not, * + * then the results are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletTypeClass & BulletTypeClass::As_Reference(BulletType type) +{ + return(*BulletTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * * + * Use this routine to fetch override information about this bullet type class object * + * from the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to examine. * + * * + * OUTPUT: bool; Was the section for this bullet found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + Arming = ini.Get_Int(Name(), "Arm", Arming); + ROT = ini.Get_Int(Name(), "ROT", ROT); + Tumble = ini.Get_Int(Name(), "Frames", Tumble); + IsHigh = ini.Get_Bool(Name(), "High", IsHigh); + IsShadow = ini.Get_Bool(Name(), "Shadow", IsShadow); + IsArcing = ini.Get_Bool(Name(), "Arcing", IsArcing); + IsDropping = ini.Get_Bool(Name(), "Dropping", IsDropping); + IsInvisible = ini.Get_Bool(Name(), "Inviso", IsInvisible); + IsProximityArmed = ini.Get_Bool(Name(), "Proximity", IsProximityArmed); + IsFlameEquipped = ini.Get_Bool(Name(), "Animates", IsFlameEquipped); + IsFueled = ini.Get_Bool(Name(), "Ranged", IsFueled); + IsInaccurate = ini.Get_Bool(Name(), "Inaccuate", IsInaccurate); + IsAntiAircraft = ini.Get_Bool(Name(), "AA", IsAntiAircraft); + IsAntiGround = ini.Get_Bool(Name(), "AG", IsAntiGround); + IsAntiSub = ini.Get_Bool(Name(), "ASW", IsAntiSub); + IsDegenerate = ini.Get_Bool(Name(), "Degenerates", IsDegenerate); + IsSubSurface = ini.Get_Bool(Name(), "UnderWater", IsSubSurface); + IsParachuted = ini.Get_Bool(Name(), "Parachuted", IsParachuted); + IsFaceless = !ini.Get_Bool(Name(), "Rotates", !IsFaceless); + IsTranslucent = ini.Get_Bool(Name(), "Translucent", IsTranslucent); + IsGigundo = ini.Get_Bool(Name(), "Gigundo", IsGigundo); + ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); + return(true); + } + return(false); +} diff --git a/REDALERT/BDATA.CPP b/REDALERT/BDATA.CPP new file mode 100644 index 000000000..8e0045280 --- /dev/null +++ b/REDALERT/BDATA.CPP @@ -0,0 +1,3832 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BDATA.CPP 2 3/03/97 10:37p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * BuildingTypeClass::Display -- Renders a generic view of building. * + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building * + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obj* + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. * + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * BuildingTypeClass::Width -- Determines width of building in icons. * + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap* + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#define FATSHIP + +#define MCW MAP_CELL_W + +#define XYCELL(x,y) (y*MAP_CELL_W+x) +static short const ExitPyle[] = { + XYCELL(1,2), + XYCELL(2,2), + XYCELL(0,2), + XYCELL(-1,2), + XYCELL(-1,-1), + XYCELL(0,-1), + XYCELL(1,-1), + XYCELL(2,-1), + XYCELL(2,-1), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; + +static short const ExitSub[] = { + XYCELL( 0, 2), + XYCELL( 2, 2), + XYCELL(-1, 2), + XYCELL( 1, 2), + XYCELL( 3, 2) +}; + +static short const ExitWeap[] = { + XYCELL(1,2), + XYCELL(-1,3), + XYCELL(0,3), + XYCELL(1,3), + XYCELL(-2,3), + XYCELL(2,3), + REFRESH_EOL +}; + +static short const ComList[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List000111111[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List0010[] = {MCW, REFRESH_EOL}; +static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List010111100[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), REFRESH_EOL}; +static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1000[] = {0, REFRESH_EOL}; +static short const List101000011[] = {0, 2, (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List1100[] = {0, 1, REFRESH_EOL}; +static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL}; +static short const List11[] = {0, 1, REFRESH_EOL}; +static short const List12[] = {MCW, REFRESH_EOL}; +static short const List1[] = {0, REFRESH_EOL}; +static short const List21[] = {0, 1, REFRESH_EOL}; +static short const List22[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL}; +static short const List22_1100[] = {0, 1, REFRESH_EOL}; +static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL}; +static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL}; +//static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const ListWeap[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL}; +static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL}; +#ifdef FATSHIP +static short const ListSPen[] = {0, 1, 2, MCW, MCW+1, MCW+2, MCW+MCW, MCW+MCW+1, MCW+MCW+2, REFRESH_EOL}; +static short const OListSPen[] = {REFRESH_EOL}; +#else +static short const ListSPen[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const OListSPen[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +#endif +static short const OListWestwood[] = {0, MCW, REFRESH_EOL}; +static short const StoreList[] = {0, REFRESH_EOL}; + +static short const ListFactory[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; + +static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +static short const OListWeap[] = {REFRESH_EOL}; +static short const OComList[] = {1, REFRESH_EOL}; +static short const OList12[] = {0, REFRESH_EOL}; +static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL}; + + +/*************************************************************************** +*/ +static BuildingTypeClass const ClassBarrel( + STRUCT_BARREL, + TXT_BARREL, // NAME: Short name of the structure. + "BARL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarrel3( + STRUCT_BARREL3, + TXT_BARREL, // NAME: Short name of the structure. + "BRL3", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAVMine( + STRUCT_AVMINE, + TXT_AVMINE, // NAME: Short name of the structure. + "MINV", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAPMine( + STRUCT_APMINE, + TXT_APMINE, // NAME: Short name of the structure. + "MINP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassIronCurtain( + STRUCT_IRON_CURTAIN, + TXT_IRON_CURTAIN, // NAME: Short name of the structure. + "IRON", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassForwardCom( + STRUCT_FORWARD_COM, + TXT_FORWARD_COM, // NAME: Short name of the structure. + "FCOM", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedTech( + STRUCT_ADVANCED_TECH, + TXT_ADVANCED_TECH, // NAME: Short name of the structure. + "ATEK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassChronosphere( + STRUCT_CHRONOSPHERE, + TXT_CHRONOSPHERE, // NAME: Short name of the structure. + "PDOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassWeapon( + STRUCT_WEAP, + TXT_WEAPON_FACTORY, // NAME: Short name of the structure. + "WEAP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XY_Coord(CELL_LEPTON_W+(CELL_LEPTON_W/2), CELL_LEPTON_H), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassShipYard( + STRUCT_SHIP_YARD, + TXT_SHIP_YARD, // NAME: Short name of the structure. + "SYRD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSubPen( + STRUCT_SUB_PEN, + TXT_SUB_PEN, // NAME: Short name of the structure. + "SPEN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPillbox( + STRUCT_PILLBOX, + TXT_PILLBOX, // NAME: Short name of the structure. + "PBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCamoPillbox( + STRUCT_CAMOPILLBOX, + TXT_CAMOPILLBOX, // NAME: Short name of the structure. + "HBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTesla( + STRUCT_TESLA, + TXT_TESLA, // NAME: Short name of the structure. + "TSLA", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x00C8, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTurret( + STRUCT_TURRET, + TXT_TURRET, // NAME: Short name of the structure. + "GUN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + (DirType)208, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAAGun( + STRUCT_AAGUN, + TXT_AAGUN, // NAME: Short name of the structure. + "AGUN", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_NE, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFlameTurret( + STRUCT_FLAME_TURRET, + TXT_FLAME_TURRET, // NAME: Short name of the structure. + "FTUR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassConst( + STRUCT_CONST, + TXT_CONST_YARD, // NAME: Short name of the structure. + "FACT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_BUILDINGTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeConst( + STRUCT_FAKECONST, + TXT_FAKE_CONST, // NAME: Short name of the structure. + "FACF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeWeapon( + STRUCT_FAKEWEAP, + TXT_FAKE_WEAP, // NAME: Short name of the structure. + "WEAF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRefinery( + STRUCT_REFINERY, + TXT_REFINERY, // NAME: Short name of the structure. + "PROC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List010111100, // OCCUPYLIST: List of active foundation squares. + (short const *)List101000011 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassStorage( + STRUCT_STORAGE, + TXT_STORAGE, // NAME: Short name of the structure. + "SILO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)StoreList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHelipad( + STRUCT_HELIPAD, + TXT_HELIPAD, // NAME: Short name of the structure. + "HPAD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCommand( + STRUCT_RADAR, + TXT_COMMAND, // NAME: Short name of the structure. + "DOME", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassGapGenerator( + STRUCT_GAP, + TXT_GAP_GENERATOR, // NAME: Short name of the structure. + "GAP", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0010, // OCCUPYLIST: List of active foundation squares. + (short const *)List1 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSAM( + STRUCT_SAM, + TXT_SAM, // NAME: Short name of the structure. + "SAM", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassMissileSilo( + STRUCT_MSLO, + TXT_MSLO, // NAME: Short name of the structure. + "MSLO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAirStrip( + STRUCT_AIRSTRIP, + TXT_AIRSTRIP, // NAME: Short name of the structure. + "AFLD", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPower( + STRUCT_POWER, + TXT_POWER, // NAME: Short name of the structure. + "POWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedPower( + STRUCT_ADVANCED_POWER, + TXT_ADVANCED_POWER, // NAME: Short name of the structure. + "APWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSovietTech( + STRUCT_SOVIET_TECH, + TXT_SOVIET_TECH, // NAME: Short name of the structure. + "STEK", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHospital( + STRUCT_HOSPITAL, + TXT_HOSPITAL, // NAME: Short name of the structure. + "HOSP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBioLab( + STRUCT_BIO_LAB, + TXT_BIO_LAB, // NAME: Short name of the structure. + "BIO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarracks( + STRUCT_BARRACKS, + TXT_BARRACKS, // NAME: Short name of the structure. + "BARR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. +// XYP_COORD(24,47), // Exit point for produced units. + XYP_COORD(18,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTent( + STRUCT_TENT, + TXT_BARRACKS, // NAME: Short name of the structure. + "TENT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassKennel( + STRUCT_KENNEL, + TXT_KENNEL, // NAME: Short name of the structure. + "KENN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(8,16), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. +// (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeShipYard( + STRUCT_FAKE_YARD, + TXT_FAKE_YARD, // NAME: Short name of the structure. + "SYRF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeSubPen( + STRUCT_FAKE_PEN, + TXT_FAKE_PEN, // NAME: Short name of the structure. + "SPEF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeCommand( + STRUCT_FAKE_RADAR, + TXT_FAKE_RADAR, // NAME: Short name of the structure. + "DOMF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRepair( + STRUCT_REPAIR, + TXT_FIX_IT, // NAME: Short name of the structure. + "FIX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFix, // OCCUPYLIST: List of active foundation squares. + (short const *)OListFix // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV01( + STRUCT_V01, + TXT_CIV1, // NAME: Short name of the structure. + "V01", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV02( + STRUCT_V02, + TXT_CIV2, // NAME: Short name of the structure. + "V02", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV03( + STRUCT_V03, + TXT_CIV3, // NAME: Short name of the structure. + "V03", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV04( + STRUCT_V04, + TXT_CIV4, // NAME: Short name of the structure. + "V04", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV05( + STRUCT_V05, + TXT_CIV5, // NAME: Short name of the structure. + "V05", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV06( + STRUCT_V06, + TXT_CIV6, // NAME: Short name of the structure. + "V06", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV07( + STRUCT_V07, + TXT_CIV7, // NAME: Short name of the structure. + "V07", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV08( + STRUCT_V08, + TXT_CIV8, // NAME: Short name of the structure. + "V08", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV09( + STRUCT_V09, + TXT_CIV9, // NAME: Short name of the structure. + "V09", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV10( + STRUCT_V10, + TXT_CIV10, // NAME: Short name of the structure. + "V10", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV11( + STRUCT_V11, + TXT_CIV11, // NAME: Short name of the structure. + "V11", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV12( + STRUCT_V12, + TXT_CIV12, // NAME: Short name of the structure. + "V12", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV13( + STRUCT_V13, + TXT_CIV13, // NAME: Short name of the structure. + "V13", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV14( + STRUCT_V14, + TXT_CIV14, // NAME: Short name of the structure. + "V14", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV15( + STRUCT_V15, + TXT_CIV15, // NAME: Short name of the structure. + "V15", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV16( + STRUCT_V16, + TXT_CIV16, // NAME: Short name of the structure. + "V16", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV17( + STRUCT_V17, + TXT_CIV17, // NAME: Short name of the structure. + "V17", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV18( + STRUCT_V18, + TXT_CIV18, // NAME: Short name of the structure. + "V18", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV19( + STRUCT_PUMP, + TXT_PUMP, // NAME: Short name of the structure. + "V19", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV20( + STRUCT_V20, + TXT_CIV20, // NAME: Short name of the structure. + "V20", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV21( + STRUCT_V21, + TXT_CIV21, // NAME: Short name of the structure. + "V21", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1101, // OCCUPYLIST: List of active foundation squares. + (short const *)List0010 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV22( + STRUCT_V22, + TXT_CIV22, // NAME: Short name of the structure. + "V22", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV23( + STRUCT_V23, + TXT_CIV23, // NAME: Short name of the structure. + "V23", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV24( + STRUCT_V24, + TXT_CIV24, // NAME: Short name of the structure. + "V24", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV25( + STRUCT_V25, + TXT_CIV25, // NAME: Short name of the structure. + "V25", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV26( + STRUCT_V26, + TXT_CIV26, // NAME: Short name of the structure. + "V26", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV27( + STRUCT_V27, + TXT_CIV27, // NAME: Short name of the structure. + "V27", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV28( + STRUCT_V28, + TXT_CIV28, // NAME: Short name of the structure. + "V28", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV29( + STRUCT_V29, + TXT_CIV29, // NAME: Short name of the structure. + "V29", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV30( + STRUCT_V30, + TXT_CIV30, // NAME: Short name of the structure. + "V30", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV31( + STRUCT_V31, + TXT_CIV31, // NAME: Short name of the structure. + "V31", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV32( + STRUCT_V32, + TXT_CIV32, // NAME: Short name of the structure. + "V32", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV33( + STRUCT_V33, + TXT_CIV33, // NAME: Short name of the structure. + "V33", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV34( + STRUCT_V34, + TXT_CIV34, // NAME: Short name of the structure. + "V34", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV35( + STRUCT_V35, + TXT_CIV35, // NAME: Short name of the structure. + "V35", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV36( + STRUCT_V36, + TXT_CIV36, // NAME: Short name of the structure. + "V36", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassV37( + STRUCT_V37, + TXT_CIV37, // NAME: Short name of the structure. + "V37", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_42, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassMission( + STRUCT_MISSION, + TXT_CIVMISS, // NAME: Short name of the structure. + "MISS", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +// Sandbag wall +static BuildingTypeClass const Sandbag( + STRUCT_SANDBAG_WALL, + TXT_SANDBAG_WALL, // NAME: Short name of the structure. + "SBAG", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Cyclone fence +static BuildingTypeClass const Cyclone( + STRUCT_CYCLONE_WALL, + TXT_CYCLONE_WALL, // NAME: Short name of the structure. + "CYCL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Brick wall +static BuildingTypeClass const Brick( + STRUCT_BRICK_WALL, + TXT_BRICK_WALL, // NAME: Short name of the structure. + "BRIK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Barbwire wall +static BuildingTypeClass const Barbwire( + STRUCT_BARBWIRE_WALL, + TXT_BARBWIRE_WALL, // NAME: Short name of the structure. + "BARB", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Wood wall +static BuildingTypeClass const Wood( + STRUCT_WOOD_WALL, + TXT_WOOD_WALL, // NAME: Short name of the structure. + "WOOD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const Fence( + STRUCT_FENCE, + TXT_FENCE, // NAME: Short name of the structure. + "FENC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + + +#ifdef FIXIT_ANTS +static BuildingTypeClass const ClassQueen( + STRUCT_QUEEN, + TXT_NONE, // NAME: Short name of the structure. + "QUEE", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva1( + STRUCT_LARVA1, + TXT_NONE, // NAME: Short name of the structure. + "LAR1", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva2( + STRUCT_LARVA2, + TXT_NONE, // NAME: Short name of the structure. + "LAR2", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +#endif +void const * BuildingTypeClass::WarFactoryOverlay; +void const * LightningShapes; + + +/*********************************************************************************************** + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * * + * This is the constructor used to create the building types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass::BuildingTypeClass( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap) : + TechnoTypeClass(RTTI_BUILDINGTYPE, + int(type), + name, + ininame, + remap, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + is_nominal, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + false, + is_theater, + is_turret_equipped, + is_remappable, + true, + (is_turret_equipped ? 32 : 1), + SPEED_NONE), + IsBase(true), + IsFake(is_fake), + IsBibbed(false), + IsWall(is_wall), + IsSimpleDamage(is_simpledamage), + IsCaptureable(false), + IsRegulated(is_regulated), + IsPowered(false), + IsUnsellable(false), + FoundationFace(foundation), + Adjacent(1), + ToBuild(tobuild), + ExitCoordinate(exitpoint), + ExitList(exitlist), + Type(type), + StartFace(sframe), + Capacity(0), + Power(0), + Drain(0), + Size(size), + OccupyList(sizelist), + OverlapList(overlap), + BuildupData(0) +{ + + Anims[BSTATE_CONSTRUCTION].Start = 0; + Anims[BSTATE_CONSTRUCTION].Count = 1; + Anims[BSTATE_CONSTRUCTION].Rate = 0; + + Anims[BSTATE_IDLE].Start = 0; + Anims[BSTATE_IDLE].Count = 1; + Anims[BSTATE_IDLE].Rate = 0; + + Anims[BSTATE_ACTIVE].Start = 0; + Anims[BSTATE_ACTIVE].Count = 1; + Anims[BSTATE_ACTIVE].Rate = 0; + + Anims[BSTATE_AUX1].Start = 0; + Anims[BSTATE_AUX1].Count = 1; + Anims[BSTATE_AUX1].Rate = 0; + + Anims[BSTATE_AUX2].Start = 0; + Anims[BSTATE_AUX2].Count = 1; + Anims[BSTATE_AUX2].Rate = 0; +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap. * + * * + * This routine will allocate a building type object from the special heap used just for * + * allocation of object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated object. If the allocation could not * + * succeed, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BuildingTypeClass::operator new(size_t) +{ + return(BuildingTypes.Alloc()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap. * + * * + * This will delete a previously allocated building type object. The memory is returned * + * to the special heap that is used for that purpose. * + * * + * INPUT: ptr -- Pointer to the building type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::operator delete(void * ptr) +{ + BuildingTypes.Free((BuildingTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obje * + * * + * This routine performs the necessary heap initializations. Since we know exactly what * + * building type objects will be needed, they are pre-allocated at this time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Heap(void) +{ + /* + ** These building type class objects must be allocated in the exact order that they + ** are specified in the StructType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BuildingTypeClass(ClassAdvancedTech); // STRUCT_ADVANCED_TECH + new BuildingTypeClass(ClassIronCurtain); // STRUCT_IRON_CURTAIN + new BuildingTypeClass(ClassWeapon); // STRUCT_WEAP + new BuildingTypeClass(ClassChronosphere); // STRUCT_CHRONOSPHERE + new BuildingTypeClass(ClassPillbox); // STRUCT_PILLBOX + new BuildingTypeClass(ClassCamoPillbox); // STRUCT_CAMOPILLBOX + new BuildingTypeClass(ClassCommand); // STRUCT_RADAR + new BuildingTypeClass(ClassGapGenerator); // STRUCT_GAP + new BuildingTypeClass(ClassTurret); // STRUCT_TURRET + new BuildingTypeClass(ClassAAGun); // STRUCT_AAGUN + new BuildingTypeClass(ClassFlameTurret); // STRUCT_FLAME_TURRET + new BuildingTypeClass(ClassConst); // STRUCT_CONST + new BuildingTypeClass(ClassRefinery); // STRUCT_REFINERY + new BuildingTypeClass(ClassStorage); // STRUCT_STORAGE + new BuildingTypeClass(ClassHelipad); // STRUCT_HELIPAD + new BuildingTypeClass(ClassSAM); // STRUCT_SAM + new BuildingTypeClass(ClassAirStrip); // STRUCT_AIRSTRIP + new BuildingTypeClass(ClassPower); // STRUCT_POWER + new BuildingTypeClass(ClassAdvancedPower);// STRUCT_ADVANCED_POWER + new BuildingTypeClass(ClassSovietTech); // STRUCT_SOVIET_TECH + new BuildingTypeClass(ClassHospital); // STRUCT_HOSPITAL + new BuildingTypeClass(ClassBarracks); // STRUCT_BARRACKS + new BuildingTypeClass(ClassTent); // STRUCT_TENT + new BuildingTypeClass(ClassKennel); // STRUCT_KENNEL + new BuildingTypeClass(ClassRepair); // STRUCT_REPAIR + new BuildingTypeClass(ClassBioLab); // STRUCT_BIO_LAB + new BuildingTypeClass(ClassMission); // STRUCT_MISSION + new BuildingTypeClass(ClassShipYard); // STRUCT_SHIP_YARD + new BuildingTypeClass(ClassSubPen); // STRUCT_SUB_PEN + new BuildingTypeClass(ClassMissileSilo); // STRUCT_MSLO + new BuildingTypeClass(ClassForwardCom); // STRUCT_FORWARD_COM + new BuildingTypeClass(ClassTesla); // STRUCT_TESLA + new BuildingTypeClass(ClassFakeWeapon); // STRUCT_FAKEWEAP + new BuildingTypeClass(ClassFakeConst); // STRUCT_FAKECONST + new BuildingTypeClass(ClassFakeShipYard); // STRUCT_FAKE_YARD + new BuildingTypeClass(ClassFakeSubPen); // STRUCT_FAKE_PEN + new BuildingTypeClass(ClassFakeCommand); // STRUCT_FAKE_RADAR + new BuildingTypeClass(Sandbag); // STRUCT_SANDBAG_WALL + new BuildingTypeClass(Cyclone); // STRUCT_CYCLONE_WALL + new BuildingTypeClass(Brick); // STRUCT_BRICK_WALL + new BuildingTypeClass(Barbwire); // STRUCT_BARBWIRE_WALL + new BuildingTypeClass(Wood); // STRUCT_WOOD_WALL + new BuildingTypeClass(Fence); // STRUCT_FENCE + new BuildingTypeClass(ClassAVMine); // STRUCT_AVMINE + new BuildingTypeClass(ClassAPMine); // STRUCT_APMINE + new BuildingTypeClass(ClassV01); // STRUCT_V1 + new BuildingTypeClass(ClassV02); // STRUCT_V2 + new BuildingTypeClass(ClassV03); // STRUCT_V3 + new BuildingTypeClass(ClassV04); // STRUCT_V4 + new BuildingTypeClass(ClassV05); // STRUCT_V5 + new BuildingTypeClass(ClassV06); // STRUCT_V6 + new BuildingTypeClass(ClassV07); // STRUCT_V7 + new BuildingTypeClass(ClassV08); // STRUCT_V8 + new BuildingTypeClass(ClassV09); // STRUCT_V9 + new BuildingTypeClass(ClassV10); // STRUCT_V10 + new BuildingTypeClass(ClassV11); // STRUCT_V11 + new BuildingTypeClass(ClassV12); // STRUCT_V12 + new BuildingTypeClass(ClassV13); // STRUCT_V13 + new BuildingTypeClass(ClassV14); // STRUCT_V14 + new BuildingTypeClass(ClassV15); // STRUCT_V15 + new BuildingTypeClass(ClassV16); // STRUCT_V16 + new BuildingTypeClass(ClassV17); // STRUCT_V17 + new BuildingTypeClass(ClassV18); // STRUCT_V18 + new BuildingTypeClass(ClassV19); // STRUCT_PUMP + new BuildingTypeClass(ClassV20); // STRUCT_V20 + new BuildingTypeClass(ClassV21); // STRUCT_V21 + new BuildingTypeClass(ClassV22); // STRUCT_V22 + new BuildingTypeClass(ClassV23); // STRUCT_V23 + new BuildingTypeClass(ClassV24); // STRUCT_V24 + new BuildingTypeClass(ClassV25); // STRUCT_V25 + new BuildingTypeClass(ClassV26); // STRUCT_V26 + new BuildingTypeClass(ClassV27); // STRUCT_V27 + new BuildingTypeClass(ClassV28); // STRUCT_V28 + new BuildingTypeClass(ClassV29); // STRUCT_V29 + new BuildingTypeClass(ClassV30); // STRUCT_V30 + new BuildingTypeClass(ClassV31); // STRUCT_V31 + new BuildingTypeClass(ClassV32); // STRUCT_V32 + new BuildingTypeClass(ClassV33); // STRUCT_V33 + new BuildingTypeClass(ClassV34); // STRUCT_V34 + new BuildingTypeClass(ClassV35); // STRUCT_V35 + new BuildingTypeClass(ClassV36); // STRUCT_V36 + new BuildingTypeClass(ClassV37); // STRUCT_V37 + new BuildingTypeClass(ClassBarrel); // STRUCT_BARREL + new BuildingTypeClass(ClassBarrel3); // STRUCT_BARREL3 + +#ifdef FIXIT_ANTS + new BuildingTypeClass(ClassQueen); // STRUCT_QUEEN + new BuildingTypeClass(ClassLarva1); // STRUCT_LARVA1 + new BuildingTypeClass(ClassLarva2); // STRUCT_LARVA2 +#endif + +} + + +/*********************************************************************************************** + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * * + * This routine is used to do the one time action necessary to handle building type class * + * objects. This entails loading of the building shapes and the brain file used by * + * buildings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should only be called ONCE. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/11/1994 JLB : Updated construction time and frame count logic. * + *=============================================================================================*/ +void BuildingTypeClass::One_Time(void) +{ + static const struct { + StructType Class; // Building class number. + BStateType Stage; // Animation sequence to assign animation range to. + int Start; // Starting frame number. + int Length; // Number of frames (-1 means use all frames). + int Rate; // Rate of animation. + } _anims[] = { + {STRUCT_CHRONOSPHERE, BSTATE_IDLE, 0, 4, 3}, // idling + {STRUCT_CHRONOSPHERE, BSTATE_ACTIVE, 4, 16,3}, // charging up and activating + {STRUCT_MSLO, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_MSLO, BSTATE_ACTIVE, 0, 5, 2}, // door opening + {STRUCT_MSLO, BSTATE_AUX1, 4, 1, 0}, // door held open + {STRUCT_MSLO, BSTATE_AUX2, 5, 3, 2}, // door closing + {STRUCT_CAMOPILLBOX, BSTATE_ACTIVE, 0, 2, 1}, + {STRUCT_GAP, BSTATE_IDLE, 0, 32,3}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_AIRSTRIP, BSTATE_AUX1, 0, 8, 3}, + {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3}, + {STRUCT_TENT, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_TENT, BSTATE_IDLE, 0, 10,3}, +#ifdef FIXIT_ANTS + {STRUCT_QUEEN, BSTATE_IDLE, 0, 10,3}, +#endif + {STRUCT_CONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_FAKECONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4}, + {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4}, + {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2}, + {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_V20, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V21, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V22, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V23, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_IRON_CURTAIN, BSTATE_ACTIVE, 0, 11,3}, + {STRUCT_TESLA, BSTATE_ACTIVE, 0, 10,2}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 8, 3} + }; + + for (int sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + BuildingTypeClass const & building = As_Reference((StructType)sindex); + /* + ** Fetch the sidebar cameo image for this building. + */ + if (building.Level != -1) { +// if (building.IsBuildable) { + sprintf(buffer, "%sICON", building.Graphic_Name()); + + if (building.IsFake) { + buffer[3] = 'F'; + } + + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)building.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch the construction animation for this building. + */ + sprintf(buffer, "%sMAKE", building.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + void const * dataptr; + dataptr = MFCD::Retrieve(fullname); + ((void const *&)building.BuildupData) = dataptr; + if (dataptr != NULL) { + int timedelay = 1; + int count = Get_Build_Frame_Count(dataptr); + if (count > 0) { + timedelay = (Rule.BuildupTime * TICKS_PER_MINUTE) / count; + } + building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + + /* + ** Fetch the normal game shape for this building. + */ + _makepath(fullname, NULL, NULL, building.Graphic_Name(), ".SHP"); + ((void const *&)building.ImageData) = MFCD::Retrieve(fullname); + } + + // Try to load weap2.shp and tesla coil's lightning shapes + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP"); + WarFactoryOverlay = MFCD::Retrieve(fullname); + _makepath(fullname, NULL, NULL, (char const *)"LITNING",".SHP"); + LightningShapes = MFCD::Retrieve(fullname); + + /* + ** Install all the special animation sequences for the different building types. + */ + for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) { + As_Reference(_anims[index].Class).Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate); + } + +} + + +/*********************************************************************************************** + * Struct_From_Name -- Find BData structure from its name. * + * * + * This routine will convert an ASCII name for a building class into * + * the actual building class it represents. * + * * + * INPUT: name -- ASCII representation of a building class. * + * * + * OUTPUT: Returns with the actual building class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +StructType BuildingTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) { + if (stricmp(As_Reference((StructType)classid).IniName, name) == 0) { + return((StructType)classid); + } + } + } + return(STRUCT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * BuildingTypeClass::Display -- Renders a generic view of building. * + * * + * This routine is used to display a generic representation of the * + * building. Typical use of this occurs with the scenario editor. * + * * + * INPUT: x,y -- Coordinate to display the building (centered). * + * * + * window -- The window the building should be rendered * + * relative to. * + * * + * house -- The house color to use for the building. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + IsTheaterShape = IsTheater; + ptr = Get_Image_Data(); + } + CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a * + * * + * This routine is used to prepare the scenario editor for the addition * + * of a building object to the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface routines. * + *=============================================================================================*/ +void BuildingTypeClass::Prep_For_Add(void) +{ + for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * * + * This routine is used by the scenario editor to create and place buildings on the map. * + * * + * INPUT: cell -- The cell that the building is to be placed upon. * + * * + * house -- The owner of the building. * + * * + * OUTPUT: bool; Was the building successfully created and placed on the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + BuildingClass * ptr; + + ptr = new BuildingClass(Type, house); + if (ptr != NULL) { + return(ptr->Unlimbo(Cell_Coord(cell), DIR_N)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * * + * This routine will create a building object of this type. The building object is in a * + * limbo state. It is presumed that the building object will be unlimboed at the correct * + * place and time. Typical use is when the building is created in a factory situation * + * and will be placed on the map when construction completes. * + * * + * INPUT: house -- Pointer to the house that is to be the owner of the building. * + * * + * OUTPUT: Returns with a pointer to the building. If the building could not be created * + * then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const +{ + HousesType htype = HOUSE_NEUTRAL; + if (house != NULL) { + htype = house->Class->House; + } + return(new BuildingClass(Type, htype)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * * + * This routine will initialize one animation control element for a * + * specified building. This modifies a "const" class and thus must * + * perform some strategic casting to get away with this. * + * * + * INPUT: state -- The animation state to apply these data values to. * + * * + * start -- Starting frame for the building's animation. * + * * + * count -- The number of frames in this animation. * + * * + * rate -- The countdown timer between animation frames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/18/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const +{ + ((int &)Anims[state].Start) = start; + ((int &)Anims[state].Count) = count; + ((int &)Anims[state].Rate) = rate; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * * + * This routine is used to perform any initialization that is custom per theater. * + * Typically, this is fetching the building shape data for those building types that have * + * theater specific art. * + * * + * INPUT: theater -- The theater to base this initialization on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + for (int sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const * classptr = &As_Reference((StructType)sindex); + + if (classptr->IsTheater) { + _makepath(fullname, NULL, NULL, classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->ImageData) = MFCD::Retrieve(fullname); + + /* + ** Buildup data is probably theater specific as well. Fetch a pointer to the + ** data at this time as well. + */ + sprintf(fullname, "%sMAKE.%s", classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->BuildupData) = MFCD::Retrieve(fullname); + if (classptr->BuildupData) { + int timedelay = 1; + int count = Get_Build_Frame_Count(classptr->BuildupData); + if (count != 0) { + timedelay = (5 * TICKS_PER_SECOND) / count; + } + classptr->Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * * + * This routine will fetch the dimensions of the building (in pixels). These dimensions are * + * used to render the selection rectangle and the health bar. * + * * + * INPUT: width -- Reference to the pixel width (to be filled in). * + * * + * height -- Reference to the pixel height (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Dimensions(int &width, int &height) const +{ + width = Width() * ICON_PIXEL_W; + width -= (width/5); + height = Height() * ICON_PIXEL_H; + height -= (height/5); +} + + +/*********************************************************************************************** + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * * + * This routine will fetch a reference to the BuildingTypeClass as indicated by the * + * building type number specified. * + * * + * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. * + * * + * OUTPUT: Returns with a reference to the building type class as indicated by the * + * parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass & BuildingTypeClass::As_Reference(StructType type) +{ + return(*BuildingTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * * + * Use this routine to fetch the occupy list pointer for the building. The occupy list is * + * used to determine what cells the building occupies and thus precludes other buildings * + * or objects from using. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells * + * this building occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Occupy_List(bool placement) const +{ + SmudgeType bib = SMUDGE_NONE; + CELL cell=0; + + if (placement && Bib_And_Offset(bib, cell)) { + + SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib); + static short _list[25]; + short * dest = &_list[0]; + + /* + ** Copy the bib overlap list into the working buffer. + */ + short const * src = smudge.Occupy_List(); + while (*src != REFRESH_EOL) { + *dest++ = (*src++) + cell; + } + + /* + ** Append the building occupy list to this working buffer. + */ + src = OccupyList; + while (src && *src != REFRESH_EOL) { + *dest++ = *src++; + } + *dest = REFRESH_EOL; + + return(&_list[0]); + } + + if (OccupyList != NULL) { + return(OccupyList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * * + * This routine will fetch the overlap list for the building. The overlap list is used * + * to determine what cells the building's graphics cover, but is not considered to occupy * + * for movement purposes. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the * + * cells that this building overlaps. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Overlap_List(void) const +{ + if (OverlapList != NULL) { + return(OverlapList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Width -- Determines width of building in icons. * + * * + * Use this routine to determine the width of the building type in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building width in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Width(void) const +{ + static int width[BSIZE_COUNT] = { + 1, + 2, + 1, + 2, + 2, + 3, + 3, + 4, + 5 + }; + return(width[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * * + * Use this routine to find the height of the building in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building height in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Height(bool bib) const +{ + static int height[BSIZE_COUNT] = { + 1, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 5 + }; + return(height[Size] + ((bib && IsBibbed) ? 1 : 0)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * * + * This routine is used to determine what (if any) bib should be used for this building * + * and also the cell offset for the upper left corner of the bib smudge type. * + * * + * INPUT: bib -- Reference to the bib that should be used for this building. * + * * + * cell -- The cell offset for the upper left corner of the bib. This offset is * + * relative to the upper left corner of the building. * + * * + * OUTPUT: Is a bib required for this building? If the result is true, then the correct * + * bib and cell offset will be filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const +{ + bib = SMUDGE_NONE; + + if (IsBibbed) { + switch (Width()) { + case 2: + bib = SMUDGE_BIB3; + break; + + case 3: + bib = SMUDGE_BIB2; + break; + + case 4: + bib = SMUDGE_BIB1; + break; + + default: + bib = SMUDGE_NONE; + break; + } + + /* + ** Adjust the bib position for special buildings that have the bib as part + ** of the building art itself. + */ + if (bib != SMUDGE_NONE) { + cell += ((Height()-1)*MAP_CELL_W); + } + } + return(bib != SMUDGE_NONE); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * * + * Use this routine to determine the maximum number of pips to display on this building * + * when it is rendered. Typically, this is the tiberium capacity divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this building when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Max_Pips(void) const +{ + int maxpips = (Width() * ICON_PIXEL_W) / 4; + return(Bound((int)(Capacity/100), 0, maxpips)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * * + * This routine is used to fetch the real raw base cost of the building. The raw cost * + * is the cost of the building less any free unit that would come with the building * + * if it were built in the normal fashion. Specifically, the helicopter cost is subtracted * + * from the helipad and the harvester cost is subtracted from the refinery. This cost * + * is used for refunding. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the raw (base) cost to build the building of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Raw_Cost(void) const +{ + int cost = TechnoTypeClass::Raw_Cost(); + + if (Type == STRUCT_HELIPAD && !Rule.IsSeparate) { + cost -= (AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost)/2; + } + if (Type == STRUCT_REFINERY) { + cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost; + } + return(cost); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * * + * This routine will fetch the cost to build the building of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Cost_Of(void) const +{ + if (Rule.IsSeparate && Type == STRUCT_HELIPAD) { + return(Raw_Cost()); + } + return(TechnoTypeClass::Cost_Of()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building t * + * * + * This routine is called when a clear space for placement is desired at the cell location * + * specified. Typical use of this routine is by the computer when it wants to build up * + * its base. * + * * + * INPUT: cell -- The cell that the building of this type would like to be placed down at. * + * * + * house -- Pointer to the house that want to clear the foundation zone. * + * * + * OUTPUT: Placement is temporarily blocked, please try again later? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Flush_For_Placement(CELL cell, HouseClass * house) const +{ + bool again = false; + if (cell > 0) { + short const * list = Occupy_List(true); + + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + + if (Map.In_Radar(newcell)) { + TechnoClass * occupier = Map[newcell].Cell_Techno(); + if (occupier != NULL) { + again = true; + if (occupier->House->Is_Ally(house) && occupier->Is_Foot() && !Target_Legal(((FootClass *)occupier)->NavCom)) { + Map[newcell].Incoming(0, true); + } else { +// Base_Is_Attacked(occupier); + } + } + } + } + } + return(again); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * * + * This routine will fetch the building type class data from the INI database file. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the building entry found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + Speed = ini.Get_Bool(Name(), "WaterBound", (Speed == SPEED_FLOAT)) ? SPEED_FLOAT : SPEED_NONE; + Capacity = ini.Get_Int(Name(), "Storage", Capacity); + Adjacent = ini.Get_Int(Name(), "Adjacent", Adjacent); + IsCaptureable = ini.Get_Bool(Name(), "Capturable", IsCaptureable); + IsPowered = ini.Get_Bool(Name(), "Powered", IsPowered); + IsBibbed = ini.Get_Bool(Name(), "Bib", IsBibbed); + IsUnsellable = ini.Get_Bool(Name(), "Unsellable", IsUnsellable); + IsBase = ini.Get_Bool(Name(), "BaseNormal", IsBase); + Power = ini.Get_Int(Name(), "Power", (Power > 0) ? Power : -Drain); + if (Power < 0) { + Drain = -Power; + Power = 0; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * * + * This routine will adjust the specified coordinate so that it will be legal for assignment* + * to this building. All buildings are given a coordinate that is in the upper left corner * + * of a cell. This routine will drop the fractional component of the coordinate. * + * * + * INPUT: coord -- The coordinate to fixup into a legal to assign value. * + * * + * OUTPUT: Returns with a coordinate that can be assigned to the building. * + * * + * WARNINGS: The coordinate is not examined to see if the cell is legal for placing the * + * building. It merely adjusts the coordinate so that is legal at first glance. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * * + * This routine will return the displayable given name for this building type. Normally, * + * this is the official name as well, however in the case of civilian buildings, the * + * name will just be "Civilian Building" unless special options are in place. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the text number of the building type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Full_Name(void) const +{ + if (Debug_Map || Rule.IsNamed || *this < STRUCT_V01 || *this > STRUCT_V37) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN_BUILDING); +} diff --git a/REDALERT/BENCH.CPP b/REDALERT/BENCH.CPP new file mode 100644 index 000000000..0eccdf3c0 --- /dev/null +++ b/REDALERT/BENCH.CPP @@ -0,0 +1,165 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BENCH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BENCH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Benchmark::Begin -- Start the benchmark operation. * + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * Benchmark::End -- Mark the end of a benchmarked operation * + * Benchmark::Reset -- Clear out the benchmark statistics. * + * Benchmark::Value -- Fetch the current average benchmark time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0) +#include "bench.h" +#include "mpu.h" + + +/*********************************************************************************************** + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * * + * This will construct the benchmark object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +Benchmark::Benchmark(void) : + Average(0), + Counter(0), + TotalCount(0) +{ +} + + +/*********************************************************************************************** + * Benchmark::Reset -- Clear out the benchmark statistics. * + * * + * Use this routine to clear out all the accumulated statistics within this benchmark * + * object. The object is set just as if it was freshly constructed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Reset(void) +{ + Average = 0; + Counter = 0; + TotalCount = 0; +} + + +/*********************************************************************************************** + * Benchmark::Begin -- Start the benchmark operation. * + * * + * Call this routine before the operation to be benchmarked is begun. The corresponding * + * End() function must be called after the operation has completed. * + * * + * INPUT: reset -- Should the entire benchmark object be reset at this time as well? * + * * + * OUTPUT: none * + * * + * WARNINGS: The Begin() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Begin(bool reset) +{ + if (reset) Reset(); + Clock = 0; +} + + +/*********************************************************************************************** + * Benchmark::End -- Mark the end of a benchmarked operation * + * * + * This routine is called at the end of the operation that is being benchmarked. It is * + * important to call this routine as soon as possible after the event being benchmarked * + * has completed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The Being() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::End(void) +{ + unsigned long value = Clock; + + if (Counter == MAXIMUM_EVENT_COUNT) { + Average -= Average / MAXIMUM_EVENT_COUNT; + Average += value; + } else { + Average += value; + Counter++; + } + TotalCount++; +} + + +/*********************************************************************************************** + * Benchmark::Value -- Fetch the current average benchmark time. * + * * + * This routine will take the statistics already accumulated and determine the average * + * time recorded. This value will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the average time that all events tracked by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +unsigned long Benchmark::Value(void) const +{ + if (Counter) { + return(Average / Counter); + } + return(0); +} +#endif \ No newline at end of file diff --git a/REDALERT/BENCH.H b/REDALERT/BENCH.H new file mode 100644 index 000000000..b18734fdb --- /dev/null +++ b/REDALERT/BENCH.H @@ -0,0 +1,121 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BENCH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BENCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BENCH_H +#define BENCH_H +#if (0) + +#include "mpu.h" +#include "ftimer.h" + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a timer access object that will fetch the internal Pentium +** clock value. +*/ +class PentiumTimerClass +{ + public: + unsigned long operator () (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} + operator unsigned long (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} +}; + + +/* +** A performance tracking tool object. It is used to track elapsed time. Unlike a simple clock, this +** class will keep a running average of the duration. Typical use of this would be to benchmark some +** process that occurs multiple times. By benchmarking an average time, inconsistencies in a particular +** run can be overcome. +*/ +class Benchmark +{ + public: + Benchmark(void); + + void Begin(bool reset=false); + void End(void); + + void Reset(void); + unsigned long Value(void) const; + unsigned long Count(void) const {return(TotalCount);} + + private: + /* + ** The maximum number of events to keep running average of. If + ** events exceed this number, then older events drop off the + ** accumulated time. This number needs to be as small as + ** is reasonable. The larger this number gets, the less magnitude + ** that the benchmark timer can handle. Example; At a value of + ** 256, the magnitude of the timer can only be 24 bits. + */ + enum {MAXIMUM_EVENT_COUNT=256}; + + /* + ** This is the timer the is used to clock the events. + */ + BasicTimerClass Clock; + + /* + ** The total time off all events tracked so far. + */ + unsigned long Average; + + /* + ** The total number of events tracked so far. + */ + unsigned long Counter; + + /* + ** Absolute total number of events (possibly greater than the + ** number of events tracked in the average). + */ + unsigned long TotalCount; +}; + + +#endif +#endif \ No newline at end of file diff --git a/REDALERT/BFIOFILE.CPP b/REDALERT/BFIOFILE.CPP new file mode 100644 index 000000000..cf690d81c --- /dev/null +++ b/REDALERT/BFIOFILE.CPP @@ -0,0 +1,984 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BFIOFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * BufferIOFileClass::Close -- Perform a closure of the file. * + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * BufferIOFileClass::Read -- Reads data from the file cache. * + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * BufferIOFileClass::Write -- Writes data to the file cache. * + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "bfiofile.h" +#include + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(char const * filename) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ + BufferIOFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * * + * This is the default constructor for a file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(void) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ +} + + +/*********************************************************************************************** + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * * + * This destructor will free all memory allocated thru using Cache routines. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::~BufferIOFileClass(void) +{ + Free(); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Cache( long size, void * ptr ) +{ + if (Buffer) { + // + // if trying to cache again with size or ptr fail + // + if (size || ptr) { + return( false ); + } else { + return( true ); + } + } + + if ( Is_Available() ) { + FileSize = Size(); + } else { + FileSize = 0; + } + + if (size) { + // + // minimum buffer size for performance + // + if (size < MINIMUM_BUFFER_SIZE) { + size = MINIMUM_BUFFER_SIZE; + + /* + ** Specifying a size smaller than the minimum is an error + ** IF a buffer pointer was also specified. In such a case the + ** system cannot use the buffer. + */ + if (ptr) { + Error(EINVAL); + } + } + + BufferSize = size; + } else { + BufferSize = FileSize; + } + + // + // if size == 0 and a ptr to a buffer is specified then that is invalid. + // if the BufferSize is 0 then this must be a new file and no size was + // specified so exit. + // + if ( (size == 0 && ptr) || !BufferSize) { + return( false ); + } + + if (ptr) { + Buffer = ptr; + } else { + Buffer = new char [BufferSize]; + } + + if (Buffer) { + IsAllocated = true; + IsDiskOpen = false; + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + TrueFileStart = 0; + + // + // the file was checked for availability then set the FileSize + // + if (FileSize) { + long readsize; + int opened = false; + long prevpos = 0; + + + if (FileSize <= BufferSize) { + readsize = FileSize; + } else { + readsize = BufferSize; + } + + if ( Is_Open() ) { + // + // get previous file position + // + prevpos = Seek(0); + + // + // get true file position + // + if ( RawFileClass::Is_Open() ) { + TrueFileStart = RawFileClass::Seek(0); + } else { + TrueFileStart = prevpos; + } + + if (FileSize <= BufferSize) { + // + // if previous position is non-zero seek to the beginning + // + if (prevpos) { + Seek(0, SEEK_SET); + } + + // + // set the buffer position for future reads/writes + // + BufferPos = prevpos; + } else { + BufferFilePos = prevpos; + } + + FilePos = prevpos; + } else { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + long actual = Read(Buffer, readsize); + + if (actual != readsize) { + Error(EIO); + } + + if (opened) { + Close(); + } else { + // + // seek to the previous position in the file + // + Seek(prevpos, SEEK_SET); + } + + IsCached = true; + } + + UseBuffer = true; + return(true); + } + + Error(ENOMEM); + + return(false); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * * + * This routine will free the buffer. By using this in conjunction with the * + * Cache() function, one can maintain tight control of memory usage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Free(void) +{ + if (Buffer) { + if (IsAllocated) { + delete [] Buffer; + IsAllocated = false; + } + + Buffer = 0; + } + + BufferSize = 0; + IsOpen = false; + IsCached = false; + IsChanged = false; + UseBuffer = false; +} + + +/*********************************************************************************************** + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * * + * * + * INPUT: none * + * * + * OUTPUT: false, did not need to write the buffer. * + * true, wrote the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Commit( void ) +{ + long size; + + + if (UseBuffer) { + if (IsChanged) { + size = BufferChangeEnd - BufferChangeBeg; + + if (IsDiskOpen) { + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Seek( TrueFileStart + FilePos, SEEK_SET ); + } else { + RawFileClass::Open(); + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Close(); + } + + IsChanged = false; + return( true ); + } else { + return( false ); + } + } else { + return( false ); + } +} + + +/*********************************************************************************************** + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * * + * Checks for a previous filename and that it is cached. If so, then check the * + * new filename against the old. If they are the same then return that filename. * + * Otherwise, the file object's name is set with just the raw filename as passed * + * to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +char const * BufferIOFileClass::Set_Name(char const * filename) +{ + if ( File_Name() && UseBuffer) { + if ( strcmp(filename, File_Name() ) == 0) { + return( File_Name() ); + } else { + Commit(); + IsCached = false; + } + } + + RawFileClass::Set_Name(filename); + return( File_Name() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/16/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Available(int ) +{ + if (UseBuffer) { + return(true); + } + + return( RawFileClass::Is_Available() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * * + * If part or all of the file is cached, then return that it is opened. A closed file * + * doesn't have a valid pointer. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Open(void) const +{ + if (IsOpen && UseBuffer) { + return( true ); + } + + return( RawFileClass::Is_Open() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return( BufferIOFileClass::Open( rights ) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(int rights) +{ + BufferIOFileClass::Close(); + + if (UseBuffer) { + + BufferRights = rights; // save rights requested for checks later + + if (rights != READ || + (rights == READ && FileSize > BufferSize) ) { + + if (rights == WRITE) { + RawFileClass::Open( rights ); + RawFileClass::Close(); + rights = READ | WRITE; + TrueFileStart = 0; // now writing to single file + } + + if (TrueFileStart) { + UseBuffer = false; + Open( rights ); + UseBuffer = true; + } else { + RawFileClass::Open( rights ); + } + + IsDiskOpen = true; + + if (BufferRights == WRITE) { + FileSize = 0; + } + + } else { + IsDiskOpen = false; + } + + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + IsOpen = true; + } else { + RawFileClass::Open( rights ); + } + + return( true ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Write -- Writes data to the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Write(void const * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if (!Open(WRITE)) { + return(0); + } + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + + if (UseBuffer) { + long sizewritten = 0; + + if (BufferRights != READ) { + while (size) { + long sizetowrite; + + if (size >= (BufferSize - BufferPos) ) { + sizetowrite = (BufferSize - BufferPos); + } else { + sizetowrite = size; + } + + if (sizetowrite != BufferSize) { + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + } + + memmove((char *)Buffer + BufferPos, (char *)buffer + sizewritten, sizetowrite); + + IsChanged = true; + sizewritten += sizetowrite; + size -= sizetowrite; + + if (BufferChangeBeg == -1) { + BufferChangeBeg = BufferPos; + BufferChangeEnd = BufferPos; + } else { + if (BufferChangeBeg > BufferPos) { + BufferChangeBeg = BufferPos; + } + } + + BufferPos += sizetowrite; + + if (BufferChangeEnd < BufferPos) { + BufferChangeEnd = BufferPos; + } + + FilePos = BufferFilePos + BufferPos; + + if (FileSize < FilePos) { + FileSize = FilePos; + } + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizewritten; + } else { + size = RawFileClass::Write(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Read -- Reads data from the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Read(void * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + if (UseBuffer) { + long sizeread = 0; + + if (BufferRights != WRITE) { + while (size) { + long sizetoread; + + if (size >= (BufferSize - BufferPos) ) { + sizetoread = (BufferSize - BufferPos); + } else { + sizetoread = size; + } + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + + memmove((char *)buffer + sizeread, (char *)Buffer + BufferPos, sizetoread); + + sizeread += sizetoread; + size -= sizetoread; + BufferPos += sizetoread; + FilePos = BufferFilePos + BufferPos; + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizeread; + } else { + size = RawFileClass::Read(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Seek(long pos, int dir) +{ + if (UseBuffer) { + bool adjusted = false; + + switch (dir) { + case SEEK_END: + FilePos = FileSize; + break; + + case SEEK_SET: + FilePos = 0; + break; + + case SEEK_CUR: + default: + break; + } + + if (TrueFileStart) { + if (pos >= TrueFileStart) { + pos -= TrueFileStart; + adjusted = true; + } + } + + FilePos += pos; + + if (FilePos < 0) { + FilePos = 0; + } + + if (FilePos > FileSize ) { + FilePos = FileSize; + } + + if (FileSize <= BufferSize) { + BufferPos = FilePos; + } else { + if (FilePos >= BufferFilePos && + FilePos < (BufferFilePos + BufferSize) ) { + BufferPos = FilePos - BufferFilePos; + } else { + Commit(); +// check!! + if (TrueFileStart) { + UseBuffer = false; + Seek(FilePos, SEEK_SET); + UseBuffer = true; + } else { + RawFileClass::Seek(FilePos, SEEK_SET); + } + + IsCached = false; + } + } + + if (TrueFileStart && adjusted) { + return( FilePos + TrueFileStart ); + } + + return( FilePos ); + } + + return( RawFileClass::Seek(pos, dir) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * * + * If part or all of the file is cached, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Size(void) +{ + if (IsOpen && UseBuffer) { + return( FileSize ); + } + + return( RawFileClass::Size() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Close -- Perform a closure of the file. * + * * + * Call Commit() to write the buffer if the file is cached and the buffer has changed, * + * then call lower level Close(). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Close(void) +{ + if (UseBuffer) { + Commit(); + + if (IsDiskOpen) { + + if (TrueFileStart) { + UseBuffer = false; + Close(); + UseBuffer = true; + } else { + RawFileClass::Close(); + } + + IsDiskOpen = false; + } + + IsOpen = false; + } else { + RawFileClass::Close(); + } +} + diff --git a/REDALERT/BFIOFILE.H b/REDALERT/BFIOFILE.H new file mode 100644 index 000000000..e034baa9f --- /dev/null +++ b/REDALERT/BFIOFILE.H @@ -0,0 +1,92 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BFIOFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : BFIOFILE.H * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BFIOFILE_H +#define BFIOFILE_H + +#include "rawfile.h" + +/* +** This derivation of the raw file class handles buffering the input/output in order to +** achieve greater speed. The buffering is not active by default. It must be activated +** by setting the appropriate buffer through the Cache() function. +*/ +class BufferIOFileClass : public RawFileClass +{ + public: + + BufferIOFileClass(char const *filename); + BufferIOFileClass(void); + virtual ~BufferIOFileClass(void); + + bool Cache( long size=0, void *ptr=NULL ); + void Free( void ); + bool Commit( void ); + virtual char const * Set_Name(char const *filename); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + + enum {MINIMUM_BUFFER_SIZE=1024}; + + private: + + unsigned IsAllocated:1; + unsigned IsOpen:1; + unsigned IsDiskOpen:1; + unsigned IsCached:1; + unsigned IsChanged:1; + unsigned UseBuffer:1; + + int BufferRights; + + void *Buffer; + + long BufferSize; + long BufferPos; + long BufferFilePos; + long BufferChangeBeg; + long BufferChangeEnd; + long FileSize; + long FilePos; + long TrueFileStart; +}; + +#endif diff --git a/REDALERT/BIGCHECK.CPP b/REDALERT/BIGCHECK.CPP new file mode 100644 index 000000000..1a8f97ac9 --- /dev/null +++ b/REDALERT/BIGCHECK.CPP @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// BigCheck.cpp +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#include "function.h" +#include "bigcheck.h" + +//*********************************************************************************************** +int BigCheckBoxClass::Draw_Me( int forced ) +{ + if (ToggleClass::Draw_Me(forced)) + { + Hide_Mouse(); + + if( !IsOn ) + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 0, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 2, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + else + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 1, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 3, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + + TextPrintType flags = TextFlags; + + RemapControlType* pScheme; + +// if( !IsDisabled ) + pScheme = GadgetClass::Get_Color_Scheme(); +// else +// { +// pScheme = &GreyScheme; +// flags = flags | TPF_MEDIUM_COLOR; +// } + + Conquer_Clip_Text_Print( szCaption, X + BIGCHECK_OFFSETX, Y + BIGCHECK_OFFSETY, pScheme, TBLACK, flags, Width, 0 ); + + Show_Mouse(); + return true; + } + return false; +} + +//*********************************************************************************************** +int BigCheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ +/* if( flags & LEFTPRESS ) + { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } +*/ + return(ToggleClass::Action(flags, key)); +} + +#endif diff --git a/REDALERT/BIGCHECK.H b/REDALERT/BIGCHECK.H new file mode 100644 index 000000000..d53c807a4 --- /dev/null +++ b/REDALERT/BIGCHECK.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// Bigcheck.h +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#ifndef BIGCHECKBOX_H +#define BIGCHECKBOX_H + +#include "toggle.h" + +#define BIGCHECK_OFFSETX 20 +#define BIGCHECK_OFFSETY 0 + +//*********************************************************************************************** +class BigCheckBoxClass : public ToggleClass +{ +public: + BigCheckBoxClass( unsigned id, int x, int y, int w, int h, const char* szCaptionIn, TextPrintType TextFlags, + bool bInitiallyChecked = false ) : + ToggleClass( id, x, y, w, h ), + TextFlags( TextFlags ) + { + szCaption = new char[ strlen( szCaptionIn ) + 1 ]; + strcpy( szCaption, szCaptionIn ); + if( bInitiallyChecked ) + Turn_On(); + IsToggleType = 1; + } + virtual ~BigCheckBoxClass() + { + delete [] szCaption; + } + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + bool Toggle() + { + if( IsOn ) + { + Turn_Off(); + return false; + } + Turn_On(); + return true; + } + +protected: + TextPrintType TextFlags; + char* szCaption; + +}; + +#endif + +#endif diff --git a/REDALERT/BLOWFISH.CPP b/REDALERT/BLOWFISH.CPP new file mode 100644 index 000000000..53225bfb0 --- /dev/null +++ b/REDALERT/BLOWFISH.CPP @@ -0,0 +1,595 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWFISH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWFISH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowfishEngine::Decrypt -- Decrypts data using blowfish algorithm. * + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowfish.h" +#include +#include + + +/* +** Byte order controlled long integer. This integer is constructed +** so that character 0 (C0) is the most significant byte of the +** integer. This is biased toward big endian architecture, but that +** just happens to be how the Blowfish algorithm was designed. +*/ +typedef union { + unsigned long Long; + struct { + unsigned char C3; + unsigned char C2; + unsigned char C1; + unsigned char C0; + } Char; +} Int; + + +/*********************************************************************************************** + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * * + * This destructor will clear out the s-box tables so that even if the memory for the * + * class remains, it will contain no compromising data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +BlowfishEngine::~BlowfishEngine(void) +{ + if (IsKeyed) { + Submit_Key(NULL, 0); + } +} + + +/*********************************************************************************************** + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * * + * This routine must be called before any data can be encrypted or decrypted. This routine * + * need only be called when the key is to be changed or set for the first time. Once the * + * key has been set, the engine may be used to encrypt, decrypt, or both operations * + * indefinitely. The key must be 56 bytes or less in length. This is necessary because * + * any keys longer than that will not correctly affect the encryption process. * + * * + * If the key pointer is NULL, then the S-Box tables are reset to identity. This will * + * mask the previous key setting. Use this method to clear the engine after processing in * + * order to gain a measure of security. * + * * + * INPUT: key -- Pointer to the key data block. * + * * + * length -- The length of the submitted key. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming process. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Submit_Key(void const * key, int length) +{ + assert(length <= MAX_KEY_LENGTH); + + /* + ** Initialize the permutation and S-Box tables to a known + ** constant value. + */ + memcpy(P_Encrypt, P_Init, sizeof(P_Init)); + memcpy(P_Decrypt, P_Init, sizeof(P_Init)); + memcpy(bf_S, S_Init, sizeof(S_Init)); + + /* + ** Validate parameters. + */ + if (key == 0 || length == 0) { + IsKeyed = false; + return; + } + + /* + ** Combine the key with the permutation table. Wrap the key + ** as many times as necessary to ensure that the entire + ** permutation table has been modified. The key is lifted + ** into a long by using endian independent means. + */ + int j = 0; + unsigned char const * key_ptr = (unsigned char const *)key; + unsigned long * p_ptr = &P_Encrypt[0]; + for (int index = 0; index < ROUNDS+2; index++) { + unsigned long data = 0; + + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + + *p_ptr++ ^= data; + } + + /* + ** The permutation table must be scrambled by means of the key. This + ** is how the key is factored into the encryption -- by merely altering + ** the permutation (and S-Box) tables. Because this transformation alters + ** the table data WHILE it is using the table data, the tables are + ** thoroughly obfuscated by this process. + */ + unsigned long left = 0x00000000L; + unsigned long right = 0x00000000L; + unsigned long * p_en = &P_Encrypt[0]; // Encryption table. + unsigned long * p_de = &P_Decrypt[ROUNDS+1]; // Decryption table. + for (int p_index = 0; p_index < ROUNDS+2; p_index += 2) { + Sub_Key_Encrypt(left, right); + + *p_en++ = left; + *p_en++ = right; + + *p_de-- = left; + *p_de-- = right; + } + + /* + ** Perform a similar transmutation to the S-Box tables. Also notice that the + ** working 64 bit number is carried into this process from the previous + ** operation. + */ + for (int sbox_index = 0; sbox_index < 4; sbox_index++) { + for (int ss_index = 0; ss_index < UCHAR_MAX+1; ss_index += 2) { + Sub_Key_Encrypt(left, right); + bf_S[sbox_index][ss_index] = left; + bf_S[sbox_index][ss_index + 1] = right; + } + } + + IsKeyed = true; +} + + +/*********************************************************************************************** + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * * + * Use this routine to encrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be encrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: plaintext-- Pointer to the data block to be encrypted. * + * * + * length -- The length of the data block. * + * * + * cyphertext- Pointer to the output buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes encrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only encrypt * + * the plaintext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertext) +{ + if (plaintext == 0 || length == 0) { + return(0); + } + if (cyphertext == 0) cyphertext = (void *)plaintext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(plaintext, cyphertext, P_Encrypt); + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(cyphertext, plaintext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(cyphertext, plaintext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Decrypt -- Decrypt an arbitrary block of data. * + * * + * Use this routine to decrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be decrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: cyphertext- Pointer to the data block to be decrypted. * + * * + * length -- The length of the data block. * + * * + * plaintext-- Pointer to the output buffer that will hold the decrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes decrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only decrypt * + * the cyphertext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintext) +{ + if (cyphertext == 0 || length == 0) { + return(0); + } + if (plaintext == 0) plaintext = (void *)cyphertext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(cyphertext, plaintext, P_Decrypt); + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(plaintext, cyphertext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(plaintext, cyphertext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * * + * This is the main processing routine for encryption and decryption. The algorithm * + * consists of a 16 round Feistal network and uses mathematics from different algebraic * + * groups (strengthens against differential cryptanalysis). The large S-Boxes and the * + * rounds strengthen it against linear cryptanalysis. * + * * + * INPUT: plaintext -- Pointer to the source text (it actually might be a pointer to * + * the cyphertext if this is called as a decryption process). * + * * + * cyphertext -- Pointer to the output buffer that will hold the processed block. * + * * + * ptable -- Pointer to the permutation table. This algorithm will encrypt * + * and decrypt using the same S-Box tables. The encryption control * + * is handled by the permutation table. * + * * + * OUTPUT: none * + * * + * WARNINGS: The source and destination buffers must be 8 bytes long. * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable) +{ + /* + ** Input the left and right halves of the source block such that + ** the byte order is constant regardless of the endian + ** persuasion of the current processor. The blowfish algorithm is + ** biased toward "big endian" architecture and some optimizations + ** could be done for big endian processors in that case. + */ + unsigned char const * source = (unsigned char const *)plaintext; + Int left; + left.Char.C0 = *source++; + left.Char.C1 = *source++; + left.Char.C2 = *source++; + left.Char.C3 = *source++; + + Int right; + right.Char.C0 = *source++; + right.Char.C1 = *source++; + right.Char.C2 = *source++; + right.Char.C3 = *source; + + /* + ** Perform all Feistal rounds on the block. This is the encryption/decryption + ** process. Since there is an exchange that occurs after each round, two + ** rounds are combined in this loop to avoid unnecessary exchanging. + */ + for (int index = 0; index < ROUNDS/2; index++) { + left.Long ^= *ptable++; + right.Long ^= ((( bf_S[0][left.Char.C0] + bf_S[1][left.Char.C1]) ^ bf_S[2][left.Char.C2]) + bf_S[3][left.Char.C3]); + right.Long ^= *ptable++; + left.Long ^= ((( bf_S[0][right.Char.C0] + bf_S[1][right.Char.C1]) ^ bf_S[2][right.Char.C2]) + bf_S[3][right.Char.C3]); + } + + /* + ** The final two longs in the permutation table are processed into the block. + ** The left and right halves are still reversed as a side effect of the last + ** round. + */ + left.Long ^= *ptable++; + right.Long ^= *ptable; + + /* + ** The final block data is output in endian architecture + ** independent format. Notice that the blocks are output as + ** right first and left second. This is to counteract the final + ** superfluous exchange that occurs as a side effect of the + ** encryption rounds. + */ + unsigned char * out = (unsigned char *)cyphertext; + *out++ = right.Char.C0; + *out++ = right.Char.C1; + *out++ = right.Char.C2; + *out++ = right.Char.C3; + + *out++ = left.Char.C0; + *out++ = left.Char.C1; + *out++ = left.Char.C2; + *out = left.Char.C3; +} + + +/*********************************************************************************************** + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * * + * This is the same as the normal process block function but it doesn't have the endian * + * fixup logic. Since this routine is only called for S-Box table generation and it is * + * known that the S-Box initial data is already in local machine endian format, the * + * byte order fixups are not needed. This also has a tendency to speed up S-Box generation * + * as well. * + * * + * INPUT: left -- The left half of the data block. * + * * + * right -- The right half of the data block. * + * * + * OUTPUT: none, but the processed block is stored back into the left and right half * + * integers. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Sub_Key_Encrypt(unsigned long & left, unsigned long & right) +{ + Int l; + l.Long = left; + + Int r; + r.Long = right; + + for (int index = 0; index < ROUNDS; index += 2) { + l.Long ^= P_Encrypt[index]; + r.Long ^= ((( bf_S[0][l.Char.C0] + bf_S[1][l.Char.C1]) ^ bf_S[2][l.Char.C2]) + bf_S[3][l.Char.C3]); + r.Long ^= P_Encrypt[index+1]; + l.Long ^= ((( bf_S[0][r.Char.C0] + bf_S[1][r.Char.C1]) ^ bf_S[2][r.Char.C2]) + bf_S[3][r.Char.C3]); + } + left = r.Long ^ P_Encrypt[ROUNDS+1]; + right = l.Long ^ P_Encrypt[ROUNDS]; +} + + +/* +** These tables have the bytes stored in machine endian format. Because of this, +** a special block cypher routine is needed when the sub-keys are generated. +** This is kludgier than it otherwise should be. However, storing these +** integers in machine independent format would be even more painful. +*/ + +unsigned long const BlowfishEngine::P_Init[BlowfishEngine::ROUNDS+2] = { + 0x243F6A88U,0x85A308D3U,0x13198A2EU,0x03707344U,0xA4093822U,0x299F31D0U,0x082EFA98U,0xEC4E6C89U, + 0x452821E6U,0x38D01377U,0xBE5466CFU,0x34E90C6CU,0xC0AC29B7U,0xC97C50DDU,0x3F84D5B5U,0xB5470917U, + 0x9216D5D9U,0x8979FB1BU +} ; + +unsigned long const BlowfishEngine::S_Init[4][UCHAR_MAX+1] = { + { + 0xD1310BA6U,0x98DFB5ACU,0x2FFD72DBU,0xD01ADFB7U,0xB8E1AFEDU,0x6A267E96U,0xBA7C9045U,0xF12C7F99U, + 0x24A19947U,0xB3916CF7U,0x0801F2E2U,0x858EFC16U,0x636920D8U,0x71574E69U,0xA458FEA3U,0xF4933D7EU, + 0x0D95748FU,0x728EB658U,0x718BCD58U,0x82154AEEU,0x7B54A41DU,0xC25A59B5U,0x9C30D539U,0x2AF26013U, + 0xC5D1B023U,0x286085F0U,0xCA417918U,0xB8DB38EFU,0x8E79DCB0U,0x603A180EU,0x6C9E0E8BU,0xB01E8A3EU, + 0xD71577C1U,0xBD314B27U,0x78AF2FDAU,0x55605C60U,0xE65525F3U,0xAA55AB94U,0x57489862U,0x63E81440U, + 0x55CA396AU,0x2AAB10B6U,0xB4CC5C34U,0x1141E8CEU,0xA15486AFU,0x7C72E993U,0xB3EE1411U,0x636FBC2AU, + 0x2BA9C55DU,0x741831F6U,0xCE5C3E16U,0x9B87931EU,0xAFD6BA33U,0x6C24CF5CU,0x7A325381U,0x28958677U, + 0x3B8F4898U,0x6B4BB9AFU,0xC4BFE81BU,0x66282193U,0x61D809CCU,0xFB21A991U,0x487CAC60U,0x5DEC8032U, + 0xEF845D5DU,0xE98575B1U,0xDC262302U,0xEB651B88U,0x23893E81U,0xD396ACC5U,0x0F6D6FF3U,0x83F44239U, + 0x2E0B4482U,0xA4842004U,0x69C8F04AU,0x9E1F9B5EU,0x21C66842U,0xF6E96C9AU,0x670C9C61U,0xABD388F0U, + 0x6A51A0D2U,0xD8542F68U,0x960FA728U,0xAB5133A3U,0x6EEF0B6CU,0x137A3BE4U,0xBA3BF050U,0x7EFB2A98U, + 0xA1F1651DU,0x39AF0176U,0x66CA593EU,0x82430E88U,0x8CEE8619U,0x456F9FB4U,0x7D84A5C3U,0x3B8B5EBEU, + 0xE06F75D8U,0x85C12073U,0x401A449FU,0x56C16AA6U,0x4ED3AA62U,0x363F7706U,0x1BFEDF72U,0x429B023DU, + 0x37D0D724U,0xD00A1248U,0xDB0FEAD3U,0x49F1C09BU,0x075372C9U,0x80991B7BU,0x25D479D8U,0xF6E8DEF7U, + 0xE3FE501AU,0xB6794C3BU,0x976CE0BDU,0x04C006BAU,0xC1A94FB6U,0x409F60C4U,0x5E5C9EC2U,0x196A2463U, + 0x68FB6FAFU,0x3E6C53B5U,0x1339B2EBU,0x3B52EC6FU,0x6DFC511FU,0x9B30952CU,0xCC814544U,0xAF5EBD09U, + 0xBEE3D004U,0xDE334AFDU,0x660F2807U,0x192E4BB3U,0xC0CBA857U,0x45C8740FU,0xD20B5F39U,0xB9D3FBDBU, + 0x5579C0BDU,0x1A60320AU,0xD6A100C6U,0x402C7279U,0x679F25FEU,0xFB1FA3CCU,0x8EA5E9F8U,0xDB3222F8U, + 0x3C7516DFU,0xFD616B15U,0x2F501EC8U,0xAD0552ABU,0x323DB5FAU,0xFD238760U,0x53317B48U,0x3E00DF82U, + 0x9E5C57BBU,0xCA6F8CA0U,0x1A87562EU,0xDF1769DBU,0xD542A8F6U,0x287EFFC3U,0xAC6732C6U,0x8C4F5573U, + 0x695B27B0U,0xBBCA58C8U,0xE1FFA35DU,0xB8F011A0U,0x10FA3D98U,0xFD2183B8U,0x4AFCB56CU,0x2DD1D35BU, + 0x9A53E479U,0xB6F84565U,0xD28E49BCU,0x4BFB9790U,0xE1DDF2DAU,0xA4CB7E33U,0x62FB1341U,0xCEE4C6E8U, + 0xEF20CADAU,0x36774C01U,0xD07E9EFEU,0x2BF11FB4U,0x95DBDA4DU,0xAE909198U,0xEAAD8E71U,0x6B93D5A0U, + 0xD08ED1D0U,0xAFC725E0U,0x8E3C5B2FU,0x8E7594B7U,0x8FF6E2FBU,0xF2122B64U,0x8888B812U,0x900DF01CU, + 0x4FAD5EA0U,0x688FC31CU,0xD1CFF191U,0xB3A8C1ADU,0x2F2F2218U,0xBE0E1777U,0xEA752DFEU,0x8B021FA1U, + 0xE5A0CC0FU,0xB56F74E8U,0x18ACF3D6U,0xCE89E299U,0xB4A84FE0U,0xFD13E0B7U,0x7CC43B81U,0xD2ADA8D9U, + 0x165FA266U,0x80957705U,0x93CC7314U,0x211A1477U,0xE6AD2065U,0x77B5FA86U,0xC75442F5U,0xFB9D35CFU, + 0xEBCDAF0CU,0x7B3E89A0U,0xD6411BD3U,0xAE1E7E49U,0x00250E2DU,0x2071B35EU,0x226800BBU,0x57B8E0AFU, + 0x2464369BU,0xF009B91EU,0x5563911DU,0x59DFA6AAU,0x78C14389U,0xD95A537FU,0x207D5BA2U,0x02E5B9C5U, + 0x83260376U,0x6295CFA9U,0x11C81968U,0x4E734A41U,0xB3472DCAU,0x7B14A94AU,0x1B510052U,0x9A532915U, + 0xD60F573FU,0xBC9BC6E4U,0x2B60A476U,0x81E67400U,0x08BA6FB5U,0x571BE91FU,0xF296EC6BU,0x2A0DD915U, + 0xB6636521U,0xE7B9F9B6U,0xFF34052EU,0xC5855664U,0x53B02D5DU,0xA99F8FA1U,0x08BA4799U,0x6E85076AU, + },{ + 0x4B7A70E9U,0xB5B32944U,0xDB75092EU,0xC4192623U,0xAD6EA6B0U,0x49A7DF7DU,0x9CEE60B8U,0x8FEDB266U, + 0xECAA8C71U,0x699A17FFU,0x5664526CU,0xC2B19EE1U,0x193602A5U,0x75094C29U,0xA0591340U,0xE4183A3EU, + 0x3F54989AU,0x5B429D65U,0x6B8FE4D6U,0x99F73FD6U,0xA1D29C07U,0xEFE830F5U,0x4D2D38E6U,0xF0255DC1U, + 0x4CDD2086U,0x8470EB26U,0x6382E9C6U,0x021ECC5EU,0x09686B3FU,0x3EBAEFC9U,0x3C971814U,0x6B6A70A1U, + 0x687F3584U,0x52A0E286U,0xB79C5305U,0xAA500737U,0x3E07841CU,0x7FDEAE5CU,0x8E7D44ECU,0x5716F2B8U, + 0xB03ADA37U,0xF0500C0DU,0xF01C1F04U,0x0200B3FFU,0xAE0CF51AU,0x3CB574B2U,0x25837A58U,0xDC0921BDU, + 0xD19113F9U,0x7CA92FF6U,0x94324773U,0x22F54701U,0x3AE5E581U,0x37C2DADCU,0xC8B57634U,0x9AF3DDA7U, + 0xA9446146U,0x0FD0030EU,0xECC8C73EU,0xA4751E41U,0xE238CD99U,0x3BEA0E2FU,0x3280BBA1U,0x183EB331U, + 0x4E548B38U,0x4F6DB908U,0x6F420D03U,0xF60A04BFU,0x2CB81290U,0x24977C79U,0x5679B072U,0xBCAF89AFU, + 0xDE9A771FU,0xD9930810U,0xB38BAE12U,0xDCCF3F2EU,0x5512721FU,0x2E6B7124U,0x501ADDE6U,0x9F84CD87U, + 0x7A584718U,0x7408DA17U,0xBC9F9ABCU,0xE94B7D8CU,0xEC7AEC3AU,0xDB851DFAU,0x63094366U,0xC464C3D2U, + 0xEF1C1847U,0x3215D908U,0xDD433B37U,0x24C2BA16U,0x12A14D43U,0x2A65C451U,0x50940002U,0x133AE4DDU, + 0x71DFF89EU,0x10314E55U,0x81AC77D6U,0x5F11199BU,0x043556F1U,0xD7A3C76BU,0x3C11183BU,0x5924A509U, + 0xF28FE6EDU,0x97F1FBFAU,0x9EBABF2CU,0x1E153C6EU,0x86E34570U,0xEAE96FB1U,0x860E5E0AU,0x5A3E2AB3U, + 0x771FE71CU,0x4E3D06FAU,0x2965DCB9U,0x99E71D0FU,0x803E89D6U,0x5266C825U,0x2E4CC978U,0x9C10B36AU, + 0xC6150EBAU,0x94E2EA78U,0xA5FC3C53U,0x1E0A2DF4U,0xF2F74EA7U,0x361D2B3DU,0x1939260FU,0x19C27960U, + 0x5223A708U,0xF71312B6U,0xEBADFE6EU,0xEAC31F66U,0xE3BC4595U,0xA67BC883U,0xB17F37D1U,0x018CFF28U, + 0xC332DDEFU,0xBE6C5AA5U,0x65582185U,0x68AB9802U,0xEECEA50FU,0xDB2F953BU,0x2AEF7DADU,0x5B6E2F84U, + 0x1521B628U,0x29076170U,0xECDD4775U,0x619F1510U,0x13CCA830U,0xEB61BD96U,0x0334FE1EU,0xAA0363CFU, + 0xB5735C90U,0x4C70A239U,0xD59E9E0BU,0xCBAADE14U,0xEECC86BCU,0x60622CA7U,0x9CAB5CABU,0xB2F3846EU, + 0x648B1EAFU,0x19BDF0CAU,0xA02369B9U,0x655ABB50U,0x40685A32U,0x3C2AB4B3U,0x319EE9D5U,0xC021B8F7U, + 0x9B540B19U,0x875FA099U,0x95F7997EU,0x623D7DA8U,0xF837889AU,0x97E32D77U,0x11ED935FU,0x16681281U, + 0x0E358829U,0xC7E61FD6U,0x96DEDFA1U,0x7858BA99U,0x57F584A5U,0x1B227263U,0x9B83C3FFU,0x1AC24696U, + 0xCDB30AEBU,0x532E3054U,0x8FD948E4U,0x6DBC3128U,0x58EBF2EFU,0x34C6FFEAU,0xFE28ED61U,0xEE7C3C73U, + 0x5D4A14D9U,0xE864B7E3U,0x42105D14U,0x203E13E0U,0x45EEE2B6U,0xA3AAABEAU,0xDB6C4F15U,0xFACB4FD0U, + 0xC742F442U,0xEF6ABBB5U,0x654F3B1DU,0x41CD2105U,0xD81E799EU,0x86854DC7U,0xE44B476AU,0x3D816250U, + 0xCF62A1F2U,0x5B8D2646U,0xFC8883A0U,0xC1C7B6A3U,0x7F1524C3U,0x69CB7492U,0x47848A0BU,0x5692B285U, + 0x095BBF00U,0xAD19489DU,0x1462B174U,0x23820E00U,0x58428D2AU,0x0C55F5EAU,0x1DADF43EU,0x233F7061U, + 0x3372F092U,0x8D937E41U,0xD65FECF1U,0x6C223BDBU,0x7CDE3759U,0xCBEE7460U,0x4085F2A7U,0xCE77326EU, + 0xA6078084U,0x19F8509EU,0xE8EFD855U,0x61D99735U,0xA969A7AAU,0xC50C06C2U,0x5A04ABFCU,0x800BCADCU, + 0x9E447A2EU,0xC3453484U,0xFDD56705U,0x0E1E9EC9U,0xDB73DBD3U,0x105588CDU,0x675FDA79U,0xE3674340U, + 0xC5C43465U,0x713E38D8U,0x3D28F89EU,0xF16DFF20U,0x153E21E7U,0x8FB03D4AU,0xE6E39F2BU,0xDB83ADF7U, + },{ + 0xE93D5A68U,0x948140F7U,0xF64C261CU,0x94692934U,0x411520F7U,0x7602D4F7U,0xBCF46B2EU,0xD4A20068U, + 0xD4082471U,0x3320F46AU,0x43B7D4B7U,0x500061AFU,0x1E39F62EU,0x97244546U,0x14214F74U,0xBF8B8840U, + 0x4D95FC1DU,0x96B591AFU,0x70F4DDD3U,0x66A02F45U,0xBFBC09ECU,0x03BD9785U,0x7FAC6DD0U,0x31CB8504U, + 0x96EB27B3U,0x55FD3941U,0xDA2547E6U,0xABCA0A9AU,0x28507825U,0x530429F4U,0x0A2C86DAU,0xE9B66DFBU, + 0x68DC1462U,0xD7486900U,0x680EC0A4U,0x27A18DEEU,0x4F3FFEA2U,0xE887AD8CU,0xB58CE006U,0x7AF4D6B6U, + 0xAACE1E7CU,0xD3375FECU,0xCE78A399U,0x406B2A42U,0x20FE9E35U,0xD9F385B9U,0xEE39D7ABU,0x3B124E8BU, + 0x1DC9FAF7U,0x4B6D1856U,0x26A36631U,0xEAE397B2U,0x3A6EFA74U,0xDD5B4332U,0x6841E7F7U,0xCA7820FBU, + 0xFB0AF54EU,0xD8FEB397U,0x454056ACU,0xBA489527U,0x55533A3AU,0x20838D87U,0xFE6BA9B7U,0xD096954BU, + 0x55A867BCU,0xA1159A58U,0xCCA92963U,0x99E1DB33U,0xA62A4A56U,0x3F3125F9U,0x5EF47E1CU,0x9029317CU, + 0xFDF8E802U,0x04272F70U,0x80BB155CU,0x05282CE3U,0x95C11548U,0xE4C66D22U,0x48C1133FU,0xC70F86DCU, + 0x07F9C9EEU,0x41041F0FU,0x404779A4U,0x5D886E17U,0x325F51EBU,0xD59BC0D1U,0xF2BCC18FU,0x41113564U, + 0x257B7834U,0x602A9C60U,0xDFF8E8A3U,0x1F636C1BU,0x0E12B4C2U,0x02E1329EU,0xAF664FD1U,0xCAD18115U, + 0x6B2395E0U,0x333E92E1U,0x3B240B62U,0xEEBEB922U,0x85B2A20EU,0xE6BA0D99U,0xDE720C8CU,0x2DA2F728U, + 0xD0127845U,0x95B794FDU,0x647D0862U,0xE7CCF5F0U,0x5449A36FU,0x877D48FAU,0xC39DFD27U,0xF33E8D1EU, + 0x0A476341U,0x992EFF74U,0x3A6F6EABU,0xF4F8FD37U,0xA812DC60U,0xA1EBDDF8U,0x991BE14CU,0xDB6E6B0DU, + 0xC67B5510U,0x6D672C37U,0x2765D43BU,0xDCD0E804U,0xF1290DC7U,0xCC00FFA3U,0xB5390F92U,0x690FED0BU, + 0x667B9FFBU,0xCEDB7D9CU,0xA091CF0BU,0xD9155EA3U,0xBB132F88U,0x515BAD24U,0x7B9479BFU,0x763BD6EBU, + 0x37392EB3U,0xCC115979U,0x8026E297U,0xF42E312DU,0x6842ADA7U,0xC66A2B3BU,0x12754CCCU,0x782EF11CU, + 0x6A124237U,0xB79251E7U,0x06A1BBE6U,0x4BFB6350U,0x1A6B1018U,0x11CAEDFAU,0x3D25BDD8U,0xE2E1C3C9U, + 0x44421659U,0x0A121386U,0xD90CEC6EU,0xD5ABEA2AU,0x64AF674EU,0xDA86A85FU,0xBEBFE988U,0x64E4C3FEU, + 0x9DBC8057U,0xF0F7C086U,0x60787BF8U,0x6003604DU,0xD1FD8346U,0xF6381FB0U,0x7745AE04U,0xD736FCCCU, + 0x83426B33U,0xF01EAB71U,0xB0804187U,0x3C005E5FU,0x77A057BEU,0xBDE8AE24U,0x55464299U,0xBF582E61U, + 0x4E58F48FU,0xF2DDFDA2U,0xF474EF38U,0x8789BDC2U,0x5366F9C3U,0xC8B38E74U,0xB475F255U,0x46FCD9B9U, + 0x7AEB2661U,0x8B1DDF84U,0x846A0E79U,0x915F95E2U,0x466E598EU,0x20B45770U,0x8CD55591U,0xC902DE4CU, + 0xB90BACE1U,0xBB8205D0U,0x11A86248U,0x7574A99EU,0xB77F19B6U,0xE0A9DC09U,0x662D09A1U,0xC4324633U, + 0xE85A1F02U,0x09F0BE8CU,0x4A99A025U,0x1D6EFE10U,0x1AB93D1DU,0x0BA5A4DFU,0xA186F20FU,0x2868F169U, + 0xDCB7DA83U,0x573906FEU,0xA1E2CE9BU,0x4FCD7F52U,0x50115E01U,0xA70683FAU,0xA002B5C4U,0x0DE6D027U, + 0x9AF88C27U,0x773F8641U,0xC3604C06U,0x61A806B5U,0xF0177A28U,0xC0F586E0U,0x006058AAU,0x30DC7D62U, + 0x11E69ED7U,0x2338EA63U,0x53C2DD94U,0xC2C21634U,0xBBCBEE56U,0x90BCB6DEU,0xEBFC7DA1U,0xCE591D76U, + 0x6F05E409U,0x4B7C0188U,0x39720A3DU,0x7C927C24U,0x86E3725FU,0x724D9DB9U,0x1AC15BB4U,0xD39EB8FCU, + 0xED545578U,0x08FCA5B5U,0xD83D7CD3U,0x4DAD0FC4U,0x1E50EF5EU,0xB161E6F8U,0xA28514D9U,0x6C51133CU, + 0x6FD5C7E7U,0x56E14EC4U,0x362ABFCEU,0xDDC6C837U,0xD79A3234U,0x92638212U,0x670EFA8EU,0x406000E0U, + },{ + 0x3A39CE37U,0xD3FAF5CFU,0xABC27737U,0x5AC52D1BU,0x5CB0679EU,0x4FA33742U,0xD3822740U,0x99BC9BBEU, + 0xD5118E9DU,0xBF0F7315U,0xD62D1C7EU,0xC700C47BU,0xB78C1B6BU,0x21A19045U,0xB26EB1BEU,0x6A366EB4U, + 0x5748AB2FU,0xBC946E79U,0xC6A376D2U,0x6549C2C8U,0x530FF8EEU,0x468DDE7DU,0xD5730A1DU,0x4CD04DC6U, + 0x2939BBDBU,0xA9BA4650U,0xAC9526E8U,0xBE5EE304U,0xA1FAD5F0U,0x6A2D519AU,0x63EF8CE2U,0x9A86EE22U, + 0xC089C2B8U,0x43242EF6U,0xA51E03AAU,0x9CF2D0A4U,0x83C061BAU,0x9BE96A4DU,0x8FE51550U,0xBA645BD6U, + 0x2826A2F9U,0xA73A3AE1U,0x4BA99586U,0xEF5562E9U,0xC72FEFD3U,0xF752F7DAU,0x3F046F69U,0x77FA0A59U, + 0x80E4A915U,0x87B08601U,0x9B09E6ADU,0x3B3EE593U,0xE990FD5AU,0x9E34D797U,0x2CF0B7D9U,0x022B8B51U, + 0x96D5AC3AU,0x017DA67DU,0xD1CF3ED6U,0x7C7D2D28U,0x1F9F25CFU,0xADF2B89BU,0x5AD6B472U,0x5A88F54CU, + 0xE029AC71U,0xE019A5E6U,0x47B0ACFDU,0xED93FA9BU,0xE8D3C48DU,0x283B57CCU,0xF8D56629U,0x79132E28U, + 0x785F0191U,0xED756055U,0xF7960E44U,0xE3D35E8CU,0x15056DD4U,0x88F46DBAU,0x03A16125U,0x0564F0BDU, + 0xC3EB9E15U,0x3C9057A2U,0x97271AECU,0xA93A072AU,0x1B3F6D9BU,0x1E6321F5U,0xF59C66FBU,0x26DCF319U, + 0x7533D928U,0xB155FDF5U,0x03563482U,0x8ABA3CBBU,0x28517711U,0xC20AD9F8U,0xABCC5167U,0xCCAD925FU, + 0x4DE81751U,0x3830DC8EU,0x379D5862U,0x9320F991U,0xEA7A90C2U,0xFB3E7BCEU,0x5121CE64U,0x774FBE32U, + 0xA8B6E37EU,0xC3293D46U,0x48DE5369U,0x6413E680U,0xA2AE0810U,0xDD6DB224U,0x69852DFDU,0x09072166U, + 0xB39A460AU,0x6445C0DDU,0x586CDECFU,0x1C20C8AEU,0x5BBEF7DDU,0x1B588D40U,0xCCD2017FU,0x6BB4E3BBU, + 0xDDA26A7EU,0x3A59FF45U,0x3E350A44U,0xBCB4CDD5U,0x72EACEA8U,0xFA6484BBU,0x8D6612AEU,0xBF3C6F47U, + 0xD29BE463U,0x542F5D9EU,0xAEC2771BU,0xF64E6370U,0x740E0D8DU,0xE75B1357U,0xF8721671U,0xAF537D5DU, + 0x4040CB08U,0x4EB4E2CCU,0x34D2466AU,0x0115AF84U,0xE1B00428U,0x95983A1DU,0x06B89FB4U,0xCE6EA048U, + 0x6F3F3B82U,0x3520AB82U,0x011A1D4BU,0x277227F8U,0x611560B1U,0xE7933FDCU,0xBB3A792BU,0x344525BDU, + 0xA08839E1U,0x51CE794BU,0x2F32C9B7U,0xA01FBAC9U,0xE01CC87EU,0xBCC7D1F6U,0xCF0111C3U,0xA1E8AAC7U, + 0x1A908749U,0xD44FBD9AU,0xD0DADECBU,0xD50ADA38U,0x0339C32AU,0xC6913667U,0x8DF9317CU,0xE0B12B4FU, + 0xF79E59B7U,0x43F5BB3AU,0xF2D519FFU,0x27D9459CU,0xBF97222CU,0x15E6FC2AU,0x0F91FC71U,0x9B941525U, + 0xFAE59361U,0xCEB69CEBU,0xC2A86459U,0x12BAA8D1U,0xB6C1075EU,0xE3056A0CU,0x10D25065U,0xCB03A442U, + 0xE0EC6E0EU,0x1698DB3BU,0x4C98A0BEU,0x3278E964U,0x9F1F9532U,0xE0D392DFU,0xD3A0342BU,0x8971F21EU, + 0x1B0A7441U,0x4BA3348CU,0xC5BE7120U,0xC37632D8U,0xDF359F8DU,0x9B992F2EU,0xE60B6F47U,0x0FE3F11DU, + 0xE54CDA54U,0x1EDAD891U,0xCE6279CFU,0xCD3E7E6FU,0x1618B166U,0xFD2C1D05U,0x848FD2C5U,0xF6FB2299U, + 0xF523F357U,0xA6327623U,0x93A83531U,0x56CCCD02U,0xACF08162U,0x5A75EBB5U,0x6E163697U,0x88D273CCU, + 0xDE966292U,0x81B949D0U,0x4C50901BU,0x71C65614U,0xE6C6C7BDU,0x327A140AU,0x45E1D006U,0xC3F27B9AU, + 0xC9AA53FDU,0x62A80F00U,0xBB25BFE2U,0x35BDD2F6U,0x71126905U,0xB2040222U,0xB6CBCF7CU,0xCD769C2BU, + 0x53113EC0U,0x1640E3D3U,0x38ABBD60U,0x2547ADF0U,0xBA38209CU,0xF746CE76U,0x77AFA1C5U,0x20756060U, + 0x85CBFE4EU,0x8AE88DD8U,0x7AAAF9B0U,0x4CF9AA7EU,0x1948C25CU,0x02FB8A8CU,0x01C36AE4U,0xD6EBE1F9U, + 0x90D4F869U,0xA65CDEA0U,0x3F09252DU,0xC208E69FU,0xB74E6132U,0xCE77E25BU,0x578FDFE3U,0x3AC372E6U + } +}; + diff --git a/REDALERT/BLOWFISH.H b/REDALERT/BLOWFISH.H new file mode 100644 index 000000000..24cfc2f1f --- /dev/null +++ b/REDALERT/BLOWFISH.H @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWFISH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWFISH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : April 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BLOWFISH_H +#define BLOWFISH_H + +#include + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This engine will process data blocks by encryption and decryption. +** The "Blowfish" algorithm is in the public domain. It uses +** a Feistal network (similar to IDEA). It has no known +** weaknesses, but is still relatively new. Blowfish is particularly strong +** against brute force attacks. It is also quite strong against linear and +** differential cryptanalysis. Its weakness is that it takes a relatively +** long time to set up with a new key (1/100th of a second on a P6-200). +** The time to set up a key is equivalent to encrypting 4240 bytes. +*/ +class BlowfishEngine { + public: + BlowfishEngine(void) : IsKeyed(false) {} + ~BlowfishEngine(void); + + void Submit_Key(void const * key, int length); + + int Encrypt(void const * plaintext, int length, void * cyphertext); + int Decrypt(void const * cyphertext, int length, void * plaintext); + + /* + ** This is the maximum key length supported. + */ + enum {MAX_KEY_LENGTH=56}; + + private: + bool IsKeyed; + + void Sub_Key_Encrypt(unsigned long & left, unsigned long & right); + + void Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable); + void Initialize_Tables(void); + + enum { + ROUNDS = 16, // Feistal round count (16 is standard). + BYTES_PER_BLOCK=8 // The number of bytes in each cypher block (don't change). + }; + + /* + ** Initialization data for sub keys. The initial values are constant and + ** filled with a number generated from pi. Thus they are not random but + ** they don't hold a weak pattern either. + */ + static unsigned long const P_Init[(int)ROUNDS+2]; + static unsigned long const S_Init[4][UCHAR_MAX+1]; + + /* + ** Permutation tables for encryption and decryption. + */ + unsigned long P_Encrypt[(int)ROUNDS+2]; + unsigned long P_Decrypt[(int)ROUNDS+2]; + + /* + ** S-Box tables (four). + */ + unsigned long bf_S[4][UCHAR_MAX+1]; +}; + +#endif + diff --git a/REDALERT/BLOWPIPE.CPP b/REDALERT/BLOWPIPE.CPP new file mode 100644 index 000000000..172d2287a --- /dev/null +++ b/REDALERT/BLOWPIPE.CPP @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowpipe.h" +#include +#include + + +/*********************************************************************************************** + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * * + * If there is any pending data in the holding buffer, then this routine will force it to * + * be flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of bytes output at the end final distant pipe * + * segment in the chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Flush(void) +{ + int total = 0; + if (Counter > 0 && BF != NULL) { + total += Pipe::Put(Buffer, Counter); + } + Counter = 0; + total += Pipe::Flush(); + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * * + * This will take the data block specified and process it before passing it on to the next * + * link in the pipe chain. A key must be submitted before this routine will actually perform* + * any processing. Prior to key submission, the data is passed through unchanged. * + * * + * INPUT: source -- Pointer to the buffer that contains the data to pass through. * + * * + * length -- The length of the data in the buffer. * + * * + * OUTPUT: Returns with then actual number of bytes output at the final distant end link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged in any way. + */ + if (BF == NULL) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + /* + ** If there is a partial block accumulated, then tag on the new data to + ** this block and process it if the block is full. Proceed with the bulk + ** processing if there are any left over bytes from this step. This step + ** can be skipped if there are no pending bytes in the buffer. + */ + if (Counter) { + int sublen = ((int)sizeof(Buffer)-Counter < slen) ? (sizeof(Buffer)-Counter) : slen; + memmove(&Buffer[Counter], source, sublen); + Counter += sublen; + source = ((char *)source) + sublen; + slen -= sublen; + + if (Counter == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(Buffer, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + Counter = 0; + } + } + + /* + ** Process the input data in blocks until there is not enough + ** source data to fill a full block of data. + */ + while (slen >= sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(source, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(source, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + source = ((char *)source) + sizeof(Buffer); + slen -= sizeof(Buffer); + } + + /* + ** If there are any left over bytes, then they must be less than the size of + ** the staging buffer. Store the bytes in the staging buffer for later + ** processing. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + + /* + ** Return with the total number of bytes flushed out to the final end of the + ** pipe chain. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * * + * This routine will take the key provided and use it to process the data that passes * + * through this pipe. Prior to a key being submitted, the data passes through the pipe * + * unchanged. * + * * + * INPUT: key -- Pointer to the key data to use. * + * * + * length-- The length of the key. The key length must not be greater than 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowPipe::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} + + + diff --git a/REDALERT/BLOWPIPE.H b/REDALERT/BLOWPIPE.H new file mode 100644 index 000000000..03bd0efca --- /dev/null +++ b/REDALERT/BLOWPIPE.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLOWPIPE_H +#define BLOWPIPE_H + +#include "pipe.h" +#include "blowfish.h" + +/* +** Performs Blowfish encryption/decryption on the data stream that is piped +** through this class. +*/ +class BlowPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowPipe(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowPipe(void) {delete BF;BF = NULL;} + virtual int Flush(void); + + virtual int Put(void const * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this pipe unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowPipe(BlowPipe & rvalue); + BlowPipe & operator = (BlowPipe const & pipe); +}; + + +#endif diff --git a/REDALERT/BLWSTRAW.CPP b/REDALERT/BLWSTRAW.CPP new file mode 100644 index 000000000..430349dbb --- /dev/null +++ b/REDALERT/BLWSTRAW.CPP @@ -0,0 +1,158 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLWSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowStraw::Get -- Fetch a block of data from the straw. * + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blwstraw.h" +#include +#include + + +/*********************************************************************************************** + * BlowStraw::Get -- Fetch a block of data from the straw. * + * * + * This routine will take a block of data from the straw and process it according to the * + * encrypt/decrypt flag and the key supplied. Prior to a key be supplied, the data passes * + * through this straw unchanged. * + * * + * INPUT: source -- Pointer to the buffer to hold the data being requested. * + * * + * length -- The length of the data being requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If the number * + * returned is less than the number requested, then this indicates that the data * + * source has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowStraw::Get(void * source, int slen) +{ + /* + ** Verify the parameter for legality. + */ + if (source == NULL || slen <= 0) { + return(0); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged. + */ + if (BF == NULL) { + return(Straw::Get(source, slen)); + } + + int total = 0; + + while (slen > 0) { + + /* + ** If there are any left over bytes in the buffer, pass them + ** through first. + */ + if (Counter > 0) { + int sublen = (slen < Counter) ? slen : Counter; + memmove(source, &Buffer[sizeof(Buffer)-Counter], sublen); + Counter -= sublen; + source = ((char *)source) + sublen; + slen -= sublen; + total += sublen; + } + if (slen == 0) break; + + /* + ** Fetch and encrypt/decrypt the next block. + */ + int incount = Straw::Get(Buffer, sizeof(Buffer)); + if (incount == 0) break; + + /* + ** Only full blocks are processed. Partial blocks are + ** merely passed through unchanged. + */ + if (incount == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, incount, Buffer); + } else { + BF->Encrypt(Buffer, incount, Buffer); + } + } else { + memmove(&Buffer[sizeof(Buffer)-incount], Buffer, incount); + } + Counter = incount; + } + + /* + ** Return with the total number of bytes placed into the buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * * + * This will take the key specified and use it to process the data that flows through this * + * straw segment. Prior to a key being submitted, the data will flow through unchanged. * + * * + * INPUT: key -- Pointer to the key to submit. * + * * + * length-- The length of the key. The length must not exceed 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowStraw::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} diff --git a/REDALERT/BLWSTRAW.H b/REDALERT/BLWSTRAW.H new file mode 100644 index 000000000..a1779f0c2 --- /dev/null +++ b/REDALERT/BLWSTRAW.H @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLWSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLWSTRAW_H +#define BLWSTRAW_H + +#include "straw.h" +#include "blowfish.h" + + +/* +** Performs Blowfish encryption/decryption to the data that is drawn through this straw. The +** process is controlled by the key which must be submitted to the class before any data +** manipulation will occur. The Blowfish algorithm is symmetric, thus the same key is used +** for encryption as is for decryption. +*/ +class BlowStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowStraw(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowStraw(void) {delete BF;BF = NULL;} + + virtual int Get(void * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this straw unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowStraw(BlowStraw & rvalue); + BlowStraw & operator = (BlowStraw const & straw); +}; + + +#endif diff --git a/REDALERT/BMP8.CPP b/REDALERT/BMP8.CPP new file mode 100644 index 000000000..813326d52 --- /dev/null +++ b/REDALERT/BMP8.CPP @@ -0,0 +1,186 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "bmp8.h" + +//*********************************************************************************************** +BMP8::~BMP8() +{ + // free resources + if( hBitmap ) + ::DeleteObject( hBitmap ); + if( hPal ) + ::DeleteObject( hPal ); +} + +//*********************************************************************************************** +bool BMP8::Init( const char* szFile, HWND hWnd ) +{ + int i; + char string[128]; + DWORD dwRead; + BITMAPFILEHEADER bitmapHeader; + BITMAPINFOHEADER bitmapInfoHeader; + LPLOGPALETTE lpLogPalette; + char *palData; + HGLOBAL hmem2; + LPVOID lpvBits; + PAINTSTRUCT ps; + HDC hdc; + HPALETTE select; + UINT realize; + RECT rect; + + + // Remember window handle for use later. + this->hWnd = hWnd; + + // Retrieve a handle identifying the file. + HANDLE hFile = ::CreateFile( + szFile, + GENERIC_READ, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + (HANDLE)NULL ); + + if( !hFile ) + return false; + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapHeader, sizeof(BITMAPFILEHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Allocate memory for the BITMAPINFO structure. + HGLOBAL infoHeaderMem = ::GlobalAlloc( GHND, sizeof(BITMAPINFOHEADER) + ((1<bmiHeader.biSize = bitmapInfoHeader.biSize; + lpHeaderMem->bmiHeader.biWidth = bitmapInfoHeader.biWidth; + lpHeaderMem->bmiHeader.biHeight = bitmapInfoHeader.biHeight; + lpHeaderMem->bmiHeader.biPlanes = bitmapInfoHeader.biPlanes; + lpHeaderMem->bmiHeader.biBitCount = bitmapInfoHeader.biBitCount; + lpHeaderMem->bmiHeader.biCompression = bitmapInfoHeader.biCompression; + lpHeaderMem->bmiHeader.biSizeImage = bitmapInfoHeader.biSizeImage; + lpHeaderMem->bmiHeader.biXPelsPerMeter = bitmapInfoHeader.biXPelsPerMeter; + lpHeaderMem->bmiHeader.biYPelsPerMeter = bitmapInfoHeader.biYPelsPerMeter; + lpHeaderMem->bmiHeader.biClrUsed = bitmapInfoHeader.biClrUsed; + lpHeaderMem->bmiHeader.biClrImportant = bitmapInfoHeader.biClrImportant; + + // Retrieve the color table. + // 1 << bitmapInfoHeader.biBitCount == 2 ^ bitmapInfoHeader.biBitCount + ::ReadFile( hFile, lpHeaderMem->bmiColors, ((1<palVersion=0x300; + lpLogPalette->palNumEntries=256; + + palData = (char*)lpHeaderMem->bmiColors; + + for( i = 0; i < 256; i++ ) + { + lpLogPalette->palPalEntry[i].peRed = *palData++; + lpLogPalette->palPalEntry[i].peGreen = *palData++; + lpLogPalette->palPalEntry[i].peBlue = *palData++; + lpLogPalette->palPalEntry[i].peFlags = *palData++; + } + hPal = ::CreatePalette( lpLogPalette ); + delete [] lpLogPalette; + + // Allocate memory for the required number of bytes. + hmem2 = ::GlobalAlloc( GHND, (bitmapHeader.bfSize - bitmapHeader.bfOffBits) ); + + lpvBits = ::GlobalLock( hmem2 ); + + // Retrieve the bitmap data. + ::ReadFile( hFile, lpvBits, (bitmapHeader.bfSize - bitmapHeader.bfOffBits), &dwRead, (LPOVERLAPPED)NULL ); + + // Create a bitmap from the data stored in the .BMP file. + hdc = ::GetDC( hWnd ); + select = ::SelectPalette( hdc, hPal, 0 ); + if( !select ) + return false; + realize = ::RealizePalette( hdc ); + if( realize == GDI_ERROR ) + return false; + + hBMP = ::CreateDIBitmap( hdc, &bitmapInfoHeader, CBM_INIT, lpvBits, lpHeaderMem, DIB_RGB_COLORS ); + ::ReleaseDC( hWnd, hdc ); + + // Unlock the global memory objects and close the .BMP file. + ::GlobalUnlock( infoHeaderMem ); + ::GlobalUnlock( hmem2 ); + ::CloseHandle( hFile ); + + if( !hBMP ) + return false; + + return true; +} + + +bit8 BMP8::drawBmp(void) +{ + // Paint the window (and draw the bitmap). + + PAINTSTRUCT ps; + HDC hdc; + char string[128]; + + InvalidateRect(WindowHandle_,NULL,FALSE); // keep windows from screwing up the + // redrawing (as much). + hdc=BeginPaint(WindowHandle_,&ps); + + //Do palette stuff + HPALETTE select=SelectPalette(ps.hdc,PalHandle_,0); + if (select==NULL) + { + sprintf(string,"Select Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + UINT realize=RealizePalette(ps.hdc); + if (realize==GDI_ERROR) + { + sprintf(string,"Realize Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + + HDC hdcMem = CreateCompatibleDC(ps.hdc); + SelectObject(hdcMem, BitmapHandle_); + BITMAP bm; + GetObject(BitmapHandle_, sizeof(BITMAP), (LPSTR) &bm); + + /// for non-stretching version + ///////BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + RECT clientRect; + GetClientRect(WindowHandle_,&clientRect); + SetStretchBltMode(ps.hdc,COLORONCOLOR); + StretchBlt(ps.hdc,0,0,clientRect.right,clientRect.bottom,hdcMem,0,0,bm.bmWidth, + bm.bmHeight,SRCCOPY); + + + DeleteDC(hdcMem); + EndPaint(WindowHandle_,&ps); + return(TRUE); +} diff --git a/REDALERT/BMP8.H b/REDALERT/BMP8.H new file mode 100644 index 000000000..cee488cc4 --- /dev/null +++ b/REDALERT/BMP8.H @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef BMP8_H +#define BMP8_H + +//#include +//#include +//#include "wstypes.h" +//#include "winblows.h" + +class BMP8 +{ +public: + BMP8() : hBMP( NULL ), hPal( NULL ), hWnd( NULL ) {} + ~BMP8(); + + bool Init( const char* szFile, HWND hWnd ); + bool Draw(void); // call this from your WM_PAINT message + +private: + HBITMAP hBMP; + HPALETTE hPal; + HWND hWnd; +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/BUFF.CPP b/REDALERT/BUFF.CPP new file mode 100644 index 000000000..d0f56544f --- /dev/null +++ b/REDALERT/BUFF.CPP @@ -0,0 +1,217 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : September 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Buffer::Buffer -- Constructor for buffer object. * + * Buffer::Buffer -- Copy constructor for buffer object. * + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * Buffer::Reset -- Clears the buffer object to null state. * + * Buffer::operator = -- Assignment operator for the buffer object. * + * Buffer::~Buffer -- Destructor for buffer object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include + + +/*********************************************************************************************** + * Buffer::Buffer -- Constructor for buffer object. * + * * + * This is the normal constructor for a buffer object. The buffer pointer and size are * + * specified as parameters. * + * * + * INPUT: buffer -- Pointer to the buffer. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to construct a Buffer object that has a pointer but a size * + * value of zero. The Buffer object can still be used for its pointer, but it * + * any function that requires a size will fail. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(void * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for char * pointer. +Buffer::Buffer(char * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for void const * pointer. +Buffer::Buffer(void const * buffer, long size) : + BufferPtr((void*)buffer), + Size(size), + IsAllocated(false) +{ +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * * + * This construtor for a buffer object will automatically allocate the bytes necessary * + * to fulfill the size requested. This object is also responsible for deleting the buffer * + * it allocated. * + * * + * INPUT: size -- The size of the buffer to allocated. * + * * + * OUTPUT: none * + * * + * WARNINGS: There is no way to tell if the allocation failed. To verify, call Get_Buffer * + * and compare with NULL. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(long size) : + BufferPtr(NULL), + Size(size), + IsAllocated(false) +{ + if (size > 0) { + BufferPtr = new char[size]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Copy constructor for buffer object. * + * * + * This will make a duplicate of the specified buffer object. The ownership of the pointer * + * remains with the original object. This prevents multiple deletion of the same pointer. * + * * + * INPUT: buffer -- Reference to the buffer object to be dupilcated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(Buffer const & buffer) : + IsAllocated(false) +{ + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; +} + + +/*********************************************************************************************** + * Buffer::operator = -- Assignment operator for the buffer object. * + * * + * This will make a duplicate of the buffer object specified. Any buffer pointed to by the * + * left hand buffer will be lost (possibley freed as a result). * + * * + * INPUT: buffer -- Reference to the right hand buffer object. * + * * + * OUTPUT: Returns with a reference to the copied buffer object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer & Buffer::operator = (Buffer const & buffer) +{ + if (buffer != this) { + if (IsAllocated) { + delete [] BufferPtr; + } + IsAllocated = false; + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; + } + return(*this); +} + + +/*********************************************************************************************** + * Buffer::~Buffer -- Destructor for buffer object. * + * * + * This destructor will free any buffer it is responsible for. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::~Buffer(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * Buffer::Reset -- Clears the buffer object to null state. * + * * + * This routine will bring the buffer object into a null (newly constructed) state. If * + * there was any buffer allocated or referred to by this object, it will be freed or * + * dereferenced as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will free the buffer if it is responsible for doing so when * + * it is no longer referenced. * + * * + * HISTORY: * + * 09/07/1996 JLB : Created. * + *=============================================================================================*/ +void Buffer::Reset(void) +{ + if (IsAllocated) { + delete [] BufferPtr; + } + BufferPtr = NULL; + Size = 0; + IsAllocated = false; +} diff --git a/REDALERT/BUFF.H b/REDALERT/BUFF.H new file mode 100644 index 000000000..c0dfe7b1f --- /dev/null +++ b/REDALERT/BUFF.H @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFF.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCBUFF_H +#define CCBUFF_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** A general purpose buffer pointer handler object. It holds not only the pointer to the +** buffer, but its size as well. By using this class instead of separate pointer and size +** values, function interfaces and algorithms become simpler to manage and understand. +*/ +class Buffer { + public: + Buffer(char * ptr, long size=0); + Buffer(void * ptr=0, long size=0); + Buffer(void const * ptr, long size=0); + Buffer(long size); + Buffer(Buffer const & buffer); + ~Buffer(void); + + Buffer & operator = (Buffer const & buffer); + operator void * (void) const {return(BufferPtr);} + operator char * (void) const {return((char *)BufferPtr);} + + void Reset(void); + void * Get_Buffer(void) const {return(BufferPtr);} + long Get_Size(void) const {return(Size);} + bool Is_Valid(void) const {return(BufferPtr != 0);} + + protected: + + /* + ** Pointer to the buffer memory. + */ + void * BufferPtr; + + /* + ** The size of the buffer memory. + */ + long Size; + + /* + ** Was the buffer allocated by this class? If so, then this class + ** will be responsible for freeing the buffer. + */ + bool IsAllocated; +}; + + +#endif diff --git a/REDALERT/BUFFERX.H b/REDALERT/BUFFERX.H new file mode 100644 index 000000000..62ded0634 --- /dev/null +++ b/REDALERT/BUFFERX.H @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFFERX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFFER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/04/96 * + * * + * Last Update : May 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BUFFERx_H +#define BUFFERx_H + +#include "wwfile.h" + +/* +** This is a transmuter interface designed to aid implementation of compression, encryption, or +** data analysis classes. The Transmuter should be derived into a class that performs the necessary +** processing. +*/ +class Transmuter { + public: + Transmuter(void) : Output(0) {} + virtual ~Transmuter(void) {} + + /* + ** These are the interface function that are used to pass data to the transmuter. The + ** default implementation of these functions do nothing other than pass the data onto + ** the subsequent transmuter. For practical use, these functions should be overloaded to + ** do something more useful. + */ + virtual void Attach(Transmuter * transmuter) {Output = transmuter;} + virtual void Flush(void) {if (Output) Output->Flush();} + virtual void Put(const void * input, unsigned length) {if (Output) Output->Put(input, length);} + + protected: + + /* + ** Pointer to the output transmuter. + */ + Transmuter * Output; +}; + + +class FileTransmuter { + public: + FileTransmuter(FileClass * file = NULL) : OutputFile(file) {} + + virtual void Attach(FileClass * file) {OutputFile = file;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (OutputFile) OutputFile->Write(input, length);} + + protected: + FileClass * OutputFile; +}; + + +class BufferTransmuter { + public: + BufferTransmuter(void * buffer = NULL) : BufferPtr(buffer) {} + + virtual void Attach(void * buffer) {BufferPtr = buffer;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (BufferPtr) {memcpy(BufferPtr, input, length);((char *&)BufferPtr) += length;}} + + protected: + void * BufferPtr; +}; + + +#endif + + diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP new file mode 100644 index 000000000..8a8e2d8c6 --- /dev/null +++ b/REDALERT/BUILDING.CPP @@ -0,0 +1,6054 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUILDING.CPP 5 3/13/97 5:18p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * BuildingClass::BuildingClass -- Constructor for buildings. * + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * BuildingClass::Captured -- Captures the building. * + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern.* + * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * BuildingClass::Detach -- Handles target removal from the game system. * + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this buildin* + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * BuildingClass::Mark -- Building interface to map rendering system. * + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * BuildingClass::Mission_Construction -- Handles mission construction. * + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells * + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * BuildingClass::Value -- Determine the value of this building. * + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * BuildingClass::What_Action -- Determines what action will occur. * + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * BuildingClass::delete -- Deallocates building object. * + * BuildingClass::new -- Allocates a building object from building pool. * + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:35PM +*/ +#include "SidebarGlyphx.h" + +enum SAMState { + SAM_READY, // Launcher can be facing any direction tracking targets. + SAM_FIRING // Stationary while missile is being fired. +}; + + +/*************************************************************************** +** Center of building offset table. +*/ +COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { + 0x00800080L, + 0x008000FFL, + 0x00FF0080L, + 0x00FF00FFL, + 0x018000FFL, + 0x00FF0180L, + 0x01800180L, + + 0x00FF0200L, + + 0x02800280L, +}; + + +/*********************************************************************************************** + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * * + * This routine handles an incoming message to the building. Messages regulate the * + * various cooperative ventures between buildings and units. This might include such * + * actions as coordinating the construction yard animation with the actual building's * + * construction animation. * + * * + * INPUT: from -- The originator of the message received. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter that might be used to return * + * extra information to the message originator. * + * * + * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/09/1994 JLB : Created. * + * 06/26/1995 JLB : Forces refinery load anim to start immediately. * + * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * + *=============================================================================================*/ +RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** This message is received as a request to attach/load/dock with this building. + ** Verify that this is allowed and return the appropriate response. + */ + case RADIO_CAN_LOAD: + TechnoClass::Receive_Message(from, message, param); + if (!House->Is_Ally(from)) return(RADIO_STATIC); + if (Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION || BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact() && Contact_With_Whom() != from)) return(RADIO_NEGATIVE); + switch (Class->Type) { + case STRUCT_AIRSTRIP: + if (from->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_HELIPAD: + if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_REPAIR: + if (from->What_Am_I() == RTTI_UNIT || (from->What_Am_I() == RTTI_AIRCRAFT)) { + if (Transmit_Message(RADIO_ON_DEPOT, from) != RADIO_ROGER) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + case STRUCT_REFINERY: + if (from->What_Am_I() == RTTI_UNIT && + *((UnitClass *)from) == UNIT_HARVESTER && + (ScenarioInit || !Is_Something_Attached())) { + + return(RADIO_ROGER); + } + break; + + default: + break; + } + return(RADIO_STATIC); + + /* + ** This message is received when the object has attached itself to this + ** building. + */ + case RADIO_IM_IN: + if (Mission == MISSION_DECONSTRUCTION) { + return(RADIO_NEGATIVE); + } + switch (Class->Type) { + case STRUCT_REPAIR: + IsReadyToCommence = true; + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_AIRSTRIP: + case STRUCT_HELIPAD: + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_REFINERY: + Mark(MARK_CHANGE); + from->Assign_Mission(MISSION_UNLOAD); + return(RADIO_ROGER); + + default: + break; + } + break; + + /* + ** Docking maneuver maintenance message. See if new order should be given to the + ** unit trying to dock. + */ + case RADIO_DOCKING: + TechnoClass::Receive_Message(from, message, param); + + /* + ** When in radio contact for loading, the refinery starts + ** flashing the lights. + */ + if (*this == STRUCT_REFINERY && BState != BSTATE_FULL) { + Begin_Mode(BSTATE_FULL); + } + + /* + ** If this building is already in radio contact, then it might + ** be able to satisfy the request to load by bumping off any + ** preoccupying task. + */ + if (*this == STRUCT_REPAIR) { + if (Contact_With_Whom() != from) { + if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) { + if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + return(RADIO_ROGER); + } + } + } + } + + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + switch (Class->Type) { + case STRUCT_AIRSTRIP: + param = As_Target(); + break; + + case STRUCT_HELIPAD: + param = As_Target(); + break; + + case STRUCT_REPAIR: + Transmit_Message(RADIO_TETHER); + param = ::As_Target(Coord_Cell(Center_Coord())); + break; + + case STRUCT_REFINERY: + param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S))); + break; + } + + /* + ** Tell the harvester to move to the docking pad of the building. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + + /* + ** Since the harvester is already there, tell it to begin the backup + ** procedure now. If it can't, then tell it to get outta here. + */ + Transmit_Message(RADIO_TETHER); + if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { + from->Scatter(NULL, true, true); + } + } + } + return(RADIO_ROGER); + + /* + ** If a transport or harvester is requesting permission to head toward, dock + ** and load/unload, check to make sure that this is allowed given the current + ** state of the building. + */ + case RADIO_ARE_REFINERY: + if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { + return(RADIO_NEGATIVE); + } + return(RADIO_ROGER); + + /* + ** Someone is telling us that it is starting construction. This should only + ** occur if this is a construction yard and a building was just placed on + ** the map. + */ + case RADIO_BUILDING: + Assign_Mission(MISSION_REPAIR); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Someone is telling us that they have finished construction. This should + ** only occur if this is a construction yard and the building that was being + ** constructed has finished. In this case, stop the construction yard + ** animation. + */ + case RADIO_COMPLETE: + if (Mission != MISSION_DECONSTRUCTION) { + Assign_Mission(MISSION_GUARD); + } + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message may occur unexpectedly if the unit in contact with this + ** building is suddenly destroyed. Handle any cleanup necessary. For example, + ** a construction yard should stop its construction animation in this case. + */ + case RADIO_OVER_OUT: + Begin_Mode(BSTATE_IDLE); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message is received when an object has completely left + ** building. Sometimes special cleanup action is required when + ** this event occurs. + */ + case RADIO_UNLOADED: + if (*this == STRUCT_REPAIR) { + if (Distance(from) < 0x0180) { + return(RADIO_ROGER); + } + } + TechnoClass::Receive_Message(from, message, param); + if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + return(RADIO_ROGER); + + default: + break; + } + + /* + ** Pass along the message to the default message handler in the radio itself. + */ + return(TechnoClass::Receive_Message(from, message, param)); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * * + * This utility function will output the current status of the building class to the * + * monochrome screen. It is through this data that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Debug_Dump(MonoClass * mono) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_BUILDING)); + mono->Fill_Attrib(66, 13, 12, 1, IsRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToRebuild ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsAllowedToSell ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 16, 12, 1, IsCharging ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 17, 12, 1, IsCharged ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 18, 12, 1, IsJamming ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 19, 12, 1, IsJammed ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(1, 11); + if (Factory) { + mono->Printf("%s %d%%", Factory->Get_Object()->Class_Of().IniName, (100*Factory->Completion())/FactoryClass::STEP_COUNT); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * * + * This is the low level graphic routine that displays the building at the location * + * specified. * + * * + * INPUT: x,y -- The coordinate to draw the building at. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a clipping window parameter. * + * 07/06/1995 JLB : Handles damaged silos correctly. * + *=============================================================================================*/ +void BuildingClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** Actually draw the building shape. + */ + IsTheaterShape = Class->IsTheater; //Let Build_Frame know if this is a theater specific shape + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window); + IsTheaterShape = false; + + /* + ** Patch for adding overlay onto weapon factory. Only add the overlay if + ** the building has more than 1 hp. Also, if the building's in radio + ** contact, he must be unloading a constructed vehicle, so draw that + ** vehicle before drawing the overlay. + */ + if (BState != BSTATE_CONSTRUCTION) { + + /* + ** A Tethered object is always rendered AFTER the building. + */ + if (*this == STRUCT_WEAP && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo && Contact_With_Whom()->What_Am_I() != RTTI_BUILDING) { + TechnoClass * contact = Contact_With_Whom(); + + assert(contact->IsActive); + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } + + /* + ** Draw the weapon factory custom overlay graphic. + */ + if ( (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP)) { + int shapenum = Door_Stage(); + if (Health_Ratio() <= Rule.ConditionYellow) shapenum += 4; + // Added override shape file name. ST - 8/1/2019 5:24PM + //Techno_Draw_Object(Class->WarFactoryOverlay, shapenum, x, y, window); + Techno_Draw_Object_Virtual(Class->WarFactoryOverlay, shapenum, x, y, window, DIR_N, 0x0100, "WEAP2"); + } + + /* + ** Draw any repair feedback graphic required. + */ + if (IsRepairing && IsWrenchVisible) { + CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + TechnoClass::Draw_It(x, y, window); + + /* + ** If this is a factory that we're spying on, show what it's producing + */ + if ((Spied_By() & (1<<(PlayerPtr->Class->House)) && Is_Selected_By_Player()) || ((window == WINDOW_VIRTUAL) && (Session.Type != GAME_NORMAL))) { + + /* + ** Fetch the factory that is associate with this building. For computer controlled buildings, the + ** factory pointer is integral to the building itself. For human controlled buildings, the factory + ** pointer is part of the house structure and must be retrieved from there. + */ + FactoryClass * factory = NULL; + if (House->IsHuman) { + factory = House->Fetch_Factory(Class->ToBuild); + } else { + factory = Factory; + } + + /* + ** If there is a factory associated with this building, then fetch any attached + ** object under production and display its cameo image over the top of this building. + */ + if (factory != NULL) { + TechnoClass * obj = factory->Get_Object(); + if (obj != NULL) { +#ifdef FIXIT_CSII + CC_Draw_Shape(obj, obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_NORMAL, NULL); +#else + void const * remapper = obj->House->Remap_Table(false, obj->Techno_Type_Class()->Remap); + CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL | ((remapper != NULL) ? SHAPE_FADING : SHAPE_NORMAL), remapper); +#endif + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * * + * This routine will examine the current state of the building and return with the shape * + * number to use. The shape number is subordinate to the building graphic image data. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when rendering this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Shape_Number(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + int shapenum = Fetch_Stage(); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + if (BState == BSTATE_CONSTRUCTION) { + + /* + ** If the building is deconstructing, then the display frame progresses + ** from the end to the beginning. Reverse the shape number accordingly. + */ + if (Mission == MISSION_DECONSTRUCTION) { + shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; + } + + } else { + + /* + ** If this is a camouflaged pill box and it is not owned by the player, then + ** it is displayed with the MEGA-camouflaged imagery. + */ + if ((!IsOwnedByPlayer) && (*this == STRUCT_CAMOPILLBOX)) { + shapenum += 1; + } + + /* + ** The Tesla Coil has a stage value that can be overridden by + ** its current state. + */ + if (*this == STRUCT_TESLA) { + if (IsCharged) { + shapenum = 3; + } else { + if (IsCharging) { + shapenum = Fetch_Stage(); + } else { + shapenum = 0; + } + } + } + + /* + ** Buildings that contain a turret handle their shape determination + ** differently than normal buildings. They need to take into consideration + ** the direction the turret is facing. + */ + if (Class->IsTurretEquipped) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing.Current())]; + + if (*this == STRUCT_SAM) { + + /* + ** SAM sites that are free to rotate fetch their animation frame + ** from the building's turret facing. All other animation stages + ** fetch their frame from the embedded animation sequencer. + */ +// if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_LOCKING) { +// shapenum = Fetch_Stage(); +// } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 35; + } + } else { + if (IsInRecoilState) { + shapenum += 32; + } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 64; + } + } + } else { + + /* + ** If it is a significantly damaged weapons factory, it is shown in + ** the worst state possible. + */ + if (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP) { + shapenum = 0; + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum = 1; + } + + } else { + + /* + ** Special render stage for silos. The stage is dependent on the current + ** Tiberium collected as it relates to Tiberium capacity. + */ + if (*this == STRUCT_STORAGE) { + + int level = 0; + if (House->Capacity) { + level = (House->Tiberium * 5) / House->Capacity; + } + + shapenum += Bound(level, 0, 4); + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 5; + } + + } else { + + /* + ** If below half strenth, then show the damage frames of the + ** building. + */ + if (Health_Ratio() <= Rule.ConditionYellow) { + if (*this == STRUCT_CHRONOSPHERE) { + shapenum += 29; + } else { + int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; + int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; + int largest = max(last1, last2); + last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; + largest = max(largest, last2); + last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; + largest = max(largest, last2); + shapenum += largest; + } + } + } + } + } + } + return(shapenum); +} + + +/*********************************************************************************************** + * BuildingClass::Mark -- Building interface to map rendering system. * + * * + * This routine is used to mark the map cells so that when it renders * + * the underlying icons will also be updated as necessary. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Building is removed. * + * MARK_CHANGE -- Building changes shape. * + * MARK_DOWN -- Building is added. * + * * + * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * + * when the building is already marked down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1994 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added health bar tracking. * + * 12/23/1994 JLB : Calls low level check before proceeding. * + * 01/27/1995 JLB : Special road spacer template added. * + *=============================================================================================*/ +bool BuildingClass::Mark(MarkType mark) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { + short const * offset = Overlap_List(); + short const * occupy = Occupy_List(); + CELL cell = Coord_Cell(Coord); + SmudgeType bib; + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge != NULL) { + smudge->Disown(cell); + delete smudge; + } + } + break; + + case MARK_DOWN: + + /* + ** Special wall logic is handled here. A building that is really a wall + ** gets converted into an overlay wall type when it is placed down. The + ** actual building object itself is destroyed. + */ + if (Class->IsWall) { + switch (Class->Type) { + case STRUCT_BRICK_WALL: + new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); + break; + + case STRUCT_BARBWIRE_WALL: + new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); + break; + + case STRUCT_SANDBAG_WALL: + new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); + break; + + case STRUCT_WOOD_WALL: + new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); + break; + + case STRUCT_CYCLONE_WALL: + new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); + break; + + case STRUCT_FENCE: + new OverlayClass(OVERLAY_FENCE, cell, House->Class->House); + break; + + default: + break; + } + Transmit_Message(RADIO_OVER_OUT); + delete this; + + } else { + if (Can_Enter_Cell(cell) == MOVE_OK) { + /* + ** Determine if a bib is required for this building. If one is, then + ** create and place it. + */ + CELL newcell = cell; + if (Class->Bib_And_Offset(bib, newcell)) { + new SmudgeClass(bib, Cell_Coord(newcell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } + + Map.Place_Down(cell, this); + } else { + return(false); + } + } + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(false)); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * * + * This function is to handle the AI logic for the building. The graphic logic (facing, * + * firing, and animation) is handled elsewhere. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/26/1994 JLB : Handles production. * + * 06/11/1995 JLB : Revamped. * + *=============================================================================================*/ +void BuildingClass::AI(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Process building animation state changes. Transition to a following state + ** if there is one specified and the current animation sequence has expired. + ** This process must occur before mission AI since the mission AI relies on + ** the bstate change to occur immediately before the MissionClass::AI. + */ + Animation_AI(); + + /* + ** If now is a good time to act on a new mission, then do so. This process occurs + ** here because some outside event may have requested a mission change for the building. + ** Such outside requests (player input) must be initiated BEFORE the normal AI process. + */ + if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** Proceed with normal logic processing. This is where the mission processing + ** occurs. This call must be located after the animation sequence makes the + ** transition to the next frame (see above) in order for the mission logic to + ** act at the exact moment of graphic transition BEFORE it has a chance to + ** be displayed. + */ + TechnoClass::AI(); + + /* + ** Bail if the object died in the AI routine. + */ + if (!IsActive) { + return; + } + + /* + ** Building ammo is instantly reloaded. + */ + if (!Ammo) { + Ammo = Class->MaxAmmo; + } + + /* + ** If now is a good time to act on a new mission, then do so. This occurs here because + ** some AI event may have requested a mission change (usually from another mission + ** state machine). This must occur here before it has a chance to render. + */ + if (IsReadyToCommence) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** If a change of animation was requested, then make the change + ** now. The building animation system acts independently but subordinate + ** to the mission state machine system. By performing the animation change-up + ** here, the mission AI system is ensured of immediate visual affect when it + ** decides to change the animation state of the building. + */ + if (QueueBState != BSTATE_NONE) { + if (BState != QueueBState) { + BState = QueueBState; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + } + QueueBState = BSTATE_NONE; + } + + /* + ** If the building's strength has changed, then update the power + ** accordingly. + */ + if (Strength != LastStrength) { + int oldpower = Power_Output(); + LastStrength = Strength; + int newpower = Power_Output(); + House->Adjust_Power(newpower - oldpower); + } + + /* + ** Check to see if the destruction countdown timer is active. If so, then decrement it. + ** When this timer reaches zero, the building is removed from the map. All the explosions + ** are presumed to be in progress at this time. + */ + if (Strength == 0) { + if (CountDown == 0) { + Limbo(); + Drop_Debris(WhomToRepay); + delete this; + } + return; + } + + /* + ** Charging logic. + */ + Charging_AI(); + + /* + ** Handle any repair process that may be going on. + */ + Repair_AI(); + + /* + ** For computer controlled buildings, determine what should be produced and start + ** production accordingly. + */ + Factory_AI(); + + /* + ** Check for demolition timeout. When timeout has expired, the building explodes. + */ + if (IsGoingToBlow && CountDown == 0) { + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + TechnoClass *saboteur = As_Techno(WhomToRepay); + if (saboteur && saboteur->IsActive && saboteur->House && saboteur->House->IsHuman) { + On_Achievement_Event(saboteur->House, "BUILDING_DESTROYED_C4", object_type->IniName); + } + } + + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay), true); + if (!IsActive) { + return; + } + Mark(MARK_CHANGE); + } + + /* + ** Turret equiped buildings must handle turret rotation logic here. This entails + ** rotating the turret to the desired facing as well as figuring out what that + ** desired facing should be. + */ + Rotation_AI(); + + /* + ** Gap Generators need to scan if they've just become activated, or if + ** the power has just come on enough so they can scan. Also, they need + ** to un-jam if the power has just dropped off. + */ + if (*this == STRUCT_GAP) { + if (Arm == 0) { + IsJamming = false; + Arm = TICKS_PER_MINUTE * Rule.GapRegenInterval + Random_Pick(1, TICKS_PER_SECOND); + } + + if (!IsJamming) { + if (House->Power_Fraction() >= 1) { + Map.Jam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + IsJamming = true; + } + } else { + if (House->Power_Fraction() < 1) { + IsJamming = false; + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + } + } + } + + /* + ** Radar facilities and SAMs need to check for the proximity of a mobile + ** radar jammer. + */ + if ((*this == STRUCT_RADAR || *this == STRUCT_SAM) && (Frame % TICKS_PER_SECOND) == 0) { + IsJammed = false; + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj != NULL && + !obj->IsInLimbo && + !obj->House->Is_Ally(House) && + obj->Class->IsJammer && + Distance(obj) <= Rule.RadarJamRadius) { + + IsJammed = true; + break; + } + } + } + + /* + ** Chronosphere active animation control. + */ + if (*this == STRUCT_CHRONOSPHERE && BState == BSTATE_ACTIVE && QueueBState == BSTATE_NONE && Scen.FadeTimer == 0) { + Begin_Mode(BSTATE_IDLE); + } +} + + +/*********************************************************************************************** + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * * + * Use this routine to transform a building that has been held in limbo * + * state, into one that really exists on the map. Once a building as * + * been unlimboed, then it becomes a normal object in the game world. * + * * + * INPUT: pos -- The position to place the building on the map. * + * * + * dir (optional) -- not used for this class * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: The unlimbo operation might not be successful if the * + * building could not be placed at the location specified. * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 06/07/1994 JLB : Matches virtual function format for base class. * + * 05/09/1995 JLB : Handles wall placement. * + * 06/18/1995 JLB : Checks for wall legality before placing down. * + *=============================================================================================*/ +bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this is a wall type building, then it never gets unlimboed. Instead, it gets + ** converted to an overlay type. + */ + if (Class->IsWall) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + OverlayType otype = OVERLAY_NONE; + switch (Class->Type) { + case STRUCT_SANDBAG_WALL: + otype = OVERLAY_SANDBAG_WALL; + break; + + case STRUCT_CYCLONE_WALL: + otype = OVERLAY_CYCLONE_WALL; + break; + + case STRUCT_BRICK_WALL: + otype = OVERLAY_BRICK_WALL; + break; + + case STRUCT_BARBWIRE_WALL: + otype = OVERLAY_BARBWIRE_WALL; + break; + + case STRUCT_WOOD_WALL: + otype = OVERLAY_WOOD_WALL; + break; + + case STRUCT_FENCE: + otype = OVERLAY_FENCE; + break; + + default: + otype = OVERLAY_NONE; + break; + + } + if (otype != OVERLAY_NONE) { + ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Map[coord].Owner = House->Class->House; + Transmit_Message(RADIO_OVER_OUT); + Map.Sight_From(Coord_Cell(coord), Class->SightRange, House); + delete this; + return(true); + } + } + } + return(false); + } + + /* + ** Normal building unlimbo process. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->BScan |= (1L << Class->Type); + House->ActiveBScan |= (1L << Class->Type); + + /* + ** Recalculate the center point of the house's base. + */ + House->Recalc_Center(); + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Add(this); + + /* + ** Possibly the sidebar will be affected by this addition. + */ + House->IsRecalcNeeded = true; + LastStrength = 0; + + // Changes to support client/server multiplayer. ST - 8/2/2019 2:36PM + //if ((!IsDiscoveredByPlayer && Map[coord].IsVisible) || Session.Type != GAME_NORMAL) { + if ((!Is_Discovered_By_Player(House) && Map[coord].Is_Visible(House)) || Session.Type != GAME_NORMAL) { + if (House->IsHuman) { + //Revealed(PlayerPtr); + Revealed(House); + } + } + if (!House->IsHuman) { + Revealed(House); + } + + // Changes to support client/server multiplayer. ST - 8/2/2019 2:36PM + //if (IsOwnedByPlayer) { + if (Is_Owned_By_Player()) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if ((Class->Ownable & (HOUSEF_GOOD | HOUSEF_BAD)) != (HOUSEF_GOOD | HOUSEF_BAD)) { + if (Class->Ownable & HOUSEF_GOOD) { + ActLike = HOUSE_GREECE; + } else { + ActLike = HOUSE_USSR; + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * * + * This routine will inflict damage points upon the specified building. * + * It will handle the damage animation and building destruction. Use * + * this routine whenever a building is attacked. * + * * + * INPUT: damage -- Amount of damage to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The kind of damage to inflict. * + * * + * source -- The source of the damage. This is used to change targeting. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: true/false; Was the building destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1991 : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added warhead modifier to damage. * + * 06/03/1994 JLB : Added source of damage as target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 07/15/1995 JLB : Power ratio gets adjusted. * + *=============================================================================================*/ +ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + int shakes; + + if (this != source /*&& !Class->IsInsignificant*/) { + + if (source) { + House->LATime = Frame; + House->LAType = source->What_Am_I(); + House->LAZone = House->Which_Zone(this); + House->LAEnemy = source->Owner(); + + if (!House->Is_Ally(source)) { + House->Enemy = source->Owner(); + } + + Base_Is_Attacked(source); + } + + short const * offset = Occupy_List(); + + /* + ** Memorize who they used to be in radio contact with. + */ + TechnoClass *tech = Contact_With_Whom(); + /* + ** Perform the low level damage assessment. + */ + res = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + switch (res) { + case RESULT_DESTROYED: + + /* + ** Add the building to the base prebuild list if allowed. This will force + ** the computer to rebuild this structure if it can. + */ + if (IsToRebuild && Class->Level != -1 && Base.House == House->Class->House && Base.Get_Node(this) == 0) { +// if (IsToRebuild && Class->IsBuildable && Base.House == House->Class->House && Base.Get_Node(this) == 0) { + Base.Nodes.Add(BaseNodeClass(Class->Type, Coord_Cell(Coord))); + } + + /* + ** Destroy all attached objects. + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + /* + ** If we were in contact with a landed plane, blow the plane up too. + */ + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + int damage = 500; + tech->Take_Damage(damage, 0, WARHEAD_AP, source, forced); + } + + Sound_Effect(VOC_KABOOM22, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + + /* + ** If the building is destroyed, then lots of + ** explosions occur. + */ + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 3)); + } + + shakes = Class->Cost_Of() / 400; + if (shakes) { + Shake_The_Screen(shakes, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(shakes, source->Owner()); + } + } + Sound_Effect(VOC_CRUMBLE, Coord); + if (Mission == MISSION_DECONSTRUCTION) { + CountDown = 0; + Set_Rate(0); + } else { + CountDown = 8; + } + + /* + ** If it is in radio contact and the object seems to be attached, then tell + ** it to run away. + */ + if (In_Radio_Contact() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + Transmit_Message(RADIO_RUN_AWAY); + } + + /* + ** A force destruction will not generate survivors. + */ + if (forced || *this == STRUCT_KENNEL) { + IsSurvivorless = true; + } + + /* + ** Destruction of a radar facility or advanced communications + ** center will cause the spiedby field to change... + */ + if (SpiedBy) { + SpiedBy = 0; + StructType struc = *this; + if (struc == STRUCT_RADAR /* || struc == STRUCT_EYE */) { + Update_Radar_Spied(); + } + } + + /* + ** Destruction of a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** Destruction of a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + /* + ** Destruction of a barrel will cause the surrounding squares to + ** be hit with damage. + */ + if (*this == STRUCT_BARREL || *this == STRUCT_BARREL3) { + COORDINATE center = Center_Coord(); + CELL cellcenter = Coord_Cell(center); + + BulletClass * bullet; + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_N)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_N); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_E)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_E); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_S)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_S); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_W)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_W); + } + } + + if (House) { + House->Check_Pertinent_Structures(); + } + + break; + + case RESULT_HALF: + if (*this == STRUCT_PUMP) { + AnimClass * anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); + if (anim) { + anim->Attach_To(this); + } + } + // Fall into next case. + + case RESULT_MAJOR: + Sound_Effect(VOC_KABOOM1, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + AnimClass * anim = NULL; + + /* + ** Show pieces of fire to indicate that a significant change in + ** damage level has occurred. + */ + if (warhead == WARHEAD_FIRE) { + switch (Random_Pick(0, 5+Class->Width()+Class->Height())) { + case 0: + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 6: + case 7: + case 8: + anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 9: + anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); + break; + + default: + break; + } + } else { + if (Percent_Chance(50)) { + /* + ** Building may catch on fire, but only if it wasn't a + ** renovator that caused the damage. + */ + if (source == NULL || source->What_Am_I() != RTTI_INFANTRY || *(InfantryClass *)source != INFANTRY_RENOVATOR) { + anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + } + /* + ** If the animation was created, then attach it to the building. + */ + if (anim) { + anim->Attach_To(this); + } + } + break; + + case RESULT_NONE: + break; + + case RESULT_LIGHT: + break; + } + + if (source && res != RESULT_NONE) { + + /* + ** If any damage occurred, then inform the house of this fact. If it is the player's + ** house, it might announce this fact. + */ + if (!Class->IsInsignificant) { + House->Attacked(this); + } + + /* + ** Save the type of the house that's doing the damage, so if the building burns + ** to death credit can still be given for the kill + */ + WhoLastHurtMe = source->Owner(); + + /* + ** When certain buildings are hit, they "snap out of it" and + ** return fire if they are able and allowed. + */ + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && + !House->Is_Ally(source) && + Class->PrimaryWeapon != NULL && + (!Target_Legal(TarCom) || !In_Range(TarCom))) { + + if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Rule.IsSmartDefense)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Generate a random rotation effect since there is nothing else that this + ** building can do. + */ + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + } + } + } + } + } + + return(res); +} + + +/*********************************************************************************************** + * BuildingClass::new -- Allocates a building object from building pool. * + * * + * This routine will allocate a building slot from the building alloc * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated building. If NULL is * + * returned, then this indicates a failure to allocate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + * 05/17/1994 JLB : Revamped allocation scheme * + * 07/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void * BuildingClass::operator new(size_t ) +{ + void * ptr = Buildings.Allocate(); + if (ptr) { + ((BuildingClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BuildingClass::delete -- Deallocates building object. * + * * + * This is the memory deallocation operation for a building object. * + * Since buildings are allocated out of a fixed memory block, all that * + * is needed is to flag the unit as inactive. * + * * + * INPUT: ptr -- Pointer to building to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::operator delete(void *ptr) +{ + if (ptr) { + ((BuildingClass *)ptr)->IsActive = false; + } + Buildings.Free((BuildingClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingClass::BuildingClass -- Constructor for buildings. * + * * + * This routine inserts a building into the object tracking system. * + * It is placed into a limbo state unless a location is provided for * + * it to unlimbo at. * + * * + * INPUT: type -- The structure type to make this object. * + * * + * house -- The owner of this building. * + * * + * pos -- The position to unlimbo the building. If -1 is * + * specified, then the building remains in a limbo * + * state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + * 08/07/1995 JLB : Fixed act like value to match expected value. * + *=============================================================================================*/ +BuildingClass::BuildingClass(StructType type, HousesType house) : + TechnoClass(RTTI_BUILDING, Buildings.ID(this), house), + Class(BuildingTypes.Ptr((int)type)), + Factory(0), + ActLike(House->ActLike), + IsToRebuild(false), + IsToRepair(false), + IsAllowedToSell(true), + IsReadyToCommence(false), + IsRepairing(false), + IsWrenchVisible(false), + IsGoingToBlow(false), + IsSurvivorless(false), + IsCharging(false), + IsCharged(false), + IsCaptured(false), + IsJamming(false), + IsJammed(false), + HasFired(false), + HasOpened(false), + CountDown(0), + BState(BSTATE_NONE), + QueueBState(BSTATE_NONE), + WhoLastHurtMe(house), + WhomToRepay(TARGET_NONE), + AnimToTrack(TARGET_NONE), + LastStrength(0), + PlacementDelay(0) +{ + House->Tracking_Add(this); + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + Ammo = Class->MaxAmmo; + + /* + ** If the building could never be built, then it can never be sold either. This + ** is due to the lack of buildup animation. + */ + if (Class->Get_Buildup_Data() != NULL) { +// if (!Class->IsBuildable) { + IsAllowedToSell = false; + } + +// if (Session.Type == GAME_INTERNET) { +// House->BuildingTotals->Increment_Unit_Total( (int) type); +// } + +} + + +/*********************************************************************************************** + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * * + * This destructor for building objects will put the building in limbo if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass::~BuildingClass(void) +{ + if (GameActive && Class) { + if (House) { + House->Tracking_Remove(this); + } + BuildingClass::Limbo(); + } + Class = 0; + + delete (FactoryClass *)Factory; + Factory = 0; + ID = -1; +} + + +/*********************************************************************************************** + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * * + * This routine is called when a building is destroyed. It handles * + * placing the rubble down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * + * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * + *=============================================================================================*/ +void BuildingClass::Drop_Debris(TARGET source) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * offset; + CELL cell; + + /* + ** Generate random survivors from the destroyed building. + */ + cell = Coord_Cell(Coord); + offset = Occupy_List(); + int odds = 2; + if (Target_Legal(WhomToRepay)) odds -= 1; + if (IsCaptured) odds += 6; + int count = How_Many_Survivors(); + while (*offset != REFRESH_EOL) { + CELL newcell; + + newcell = cell + *offset++; + CellClass const * cellptr = &Map[newcell]; + + /* + ** Infantry could run out of a destroyed building. + */ + if (!House->IsToDie && count > 0) { + InfantryClass * i = NULL; + + if (Random_Pick(0, odds) == 1) { + i = NULL; + InfantryType typ = Crew_Type(); + if (typ != INFANTRY_NONE) i = new InfantryClass(typ, House->Class->House); + if (i != NULL) { + if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; + ScenarioInit++; + if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { + count--; + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + i->Scatter(0, true); + if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { + i->Assign_Mission(MISSION_ATTACK); + i->Assign_Target(source); + } else { + if (House->IsHuman) { + i->Assign_Mission(MISSION_GUARD); + } else { + i->Assign_Mission(MISSION_HUNT); + } + } + } else { + delete i; + } + ScenarioInit--; + } + } + } + + /* + ** Smoke and fire only appear on terrestrail cells. They should not appear on + ** rivers, clifs, or water cells. + */ + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + + /* + ** Possibly add some smoke rising from the ashes of the building. + */ + switch (Random_Pick(0, 5)) { + case 0: + case 1: + case 2: + new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); + break; + + default: + break; + } + + /* + ** The building always scars the ground in some fashion. + */ + if (Percent_Chance(25)) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); + } else { + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* + * * + * This interface routine handles when the player clicks on the map while this building * + * is currently selected. This is used to assign an override target to a turret or * + * guard tower. * + * * + * INPUT: target -- The target that was clicked upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK && object != NULL) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + + if (action == ACTION_TOGGLE_PRIMARY && Class->Is_Factory()) { + OutList.Add(EventClass(EventClass::PRIMARY, TargetClass(this))); + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * * + * This routine really only serves one purpose -- to allow targeting of the ground for * + * buildings that are equipped with weapons. * + * * + * INPUT: action -- The requested action to perform. * + * * + * cell -- The cell location to perform the action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 10/04/1995 JLB : Handles construction yard undeploy to move logic. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + } + + if (action == ACTION_MOVE && *this == STRUCT_CONST) { + OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(::As_Target(cell)))); + OutList.Add(EventClass(EventClass::SELL, TargetClass(this))); + + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } +} + + +/*********************************************************************************************** + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * * + * Assigning of a target to a building makes sense if the building is one that can attack. * + * This routine would be used to assign the attack target to a turret or guard tower. * + * * + * INPUT: target -- The target that was clicked on while this building was selected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 11/02/1994 JLB : Checks for range before assigning target. * + *=============================================================================================*/ +void BuildingClass::Assign_Target(TARGET target) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && !In_Range(target, 0)) { + target = TARGET_NONE; + } + + TechnoClass::Assign_Target(target); +} + + +/*********************************************************************************************** + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * * + * This routine initializes the building system in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Init(void) +{ + Buildings.Free_All(); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * * + * This function is used to cause an object to exit the building. It is called when a * + * factory produces a vehicle or other mobile object and that object needs to exit the * + * building to join the ranks of a regular unit. Typically, the object is placed down on * + * the map such that it overlaps the building and then it is given a movement order so that * + * it will move to an adjacent free cell. * + * * + * INPUT: base -- Pointer to the object that is to exit the building. * + * * + * OUTPUT: Returns the success rating for the exit attempt; * + * 0 = complete failure (refund money please) * + * 1 = temporarily prevented (try again later please) * + * 2 = successful * + * * + * WARNINGS: The building is placed in radio contact with the object. The object is in a * + * tethered condition. This condition will be automatically broken when the * + * object reaches the adjacent square. * + * * + * HISTORY: * + * 11/28/1994 JLB : Created. * + * 04/10/1995 JLB : Handles building production by computer. * + * 06/17/1995 JLB : Handles refinery exit. * + *=============================================================================================*/ +int BuildingClass::Exit_Object(TechnoClass * base) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!base) return(0); + + TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); + + /* + ** A unit exiting a building is always considered to be "locked". That means, it + ** will be considered as to have legally entered the visible map domain. + */ + base->IsLocked = true; + + /* + ** Find a good cell to unload the object to. The object, probably a vehicle + ** will drive/walk to the adjacent free cell. + */ + CELL cell = 0; + + switch (base->What_Am_I()) { + + case RTTI_AIRCRAFT: + if (!In_Radio_Contact()) { + AircraftClass * air = (AircraftClass *)base; + + air->Height = 0; + ScenarioInit++; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + Transmit_Message(RADIO_HELLO, air); + Transmit_Message(RADIO_TETHER); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } else { + AircraftClass * air = (AircraftClass *)base; + + if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { + cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } else { + cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } + ScenarioInit++; + if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { +//BG air->Assign_Destination(::As_Target(Nearby_Location(air))); +/*BG*/ air->Assign_Destination(::As_Target(air->Nearby_Location(this))); + air->Assign_Mission(MISSION_MOVE); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + case RTTI_VESSEL: + switch (Class->Type) { + case STRUCT_SUB_PEN: + case STRUCT_SHIP_YARD: + ScenarioInit++; + cell = Find_Exit_Cell(base); + if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) { + base->Assign_Mission(MISSION_GUARD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + default: + break; + } + break; + + case RTTI_INFANTRY: + case RTTI_UNIT: + switch (Class->Type) { + case STRUCT_REFINERY: + if (base->What_Am_I() == RTTI_UNIT) { + cell = Coord_Cell(Center_Coord()); + UnitClass * unit = (UnitClass *)base; + + cell = Adjacent_Cell(cell, FACING_SW); + ScenarioInit++; + if (unit->Unlimbo(Cell_Coord(Adjacent_Cell(cell, DIR_S)), DIR_SW_X2)) { + unit->PrimaryFacing = DIR_S; + unit->Assign_Mission(MISSION_HARVEST); + } + ScenarioInit--; + } else { + base->Scatter(0, true); + } + break; + + case STRUCT_WEAP: + if (Mission == MISSION_UNLOAD) { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg->Owner() == Owner() && *bldg == STRUCT_WEAP && bldg != this && bldg->Mission == MISSION_GUARD && !bldg->Factory) { + FactoryClass * temp = Factory; + bldg->Factory = Factory; + Factory = 0; + int retval = (bldg->Exit_Object(base)); + bldg->Factory = 0; + Factory = temp; + return(retval); + } + } + return(1); // fail while we're still unloading previous + } + ScenarioInit++; + if (base->Unlimbo(Exit_Coord(), DIR_S)) { + base->Mark(MARK_UP); + base->Coord = Exit_Coord(); + base->Mark(MARK_DOWN); + Transmit_Message(RADIO_HELLO, base); + Transmit_Message(RADIO_TETHER); + Assign_Mission(MISSION_UNLOAD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + case STRUCT_BARRACKS: + case STRUCT_TENT: + case STRUCT_KENNEL: + + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + + /* + ** Establish radio contact so unload coordination can occur. This + ** radio contact should always succeed. + */ + if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + default: + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + } + break; + + case RTTI_BUILDING: + + if (!House->IsHuman) { + + /* + ** Find the next available spot to place this newly created building. If the + ** building could be placed at the desired location, fine. If not, then this + ** routine will return failure. The calling routine will probably abandon this + ** building in preference to building another. + */ + BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); + COORDINATE coord = 0; + if (node) { + coord = Cell_Coord(node->Cell); + } else { + + /* + ** Find a suitable new spot to place. + */ + coord = House->Find_Build_Location((BuildingClass *)base); + } + + if (coord) { + if (Flush_For_Placement(base, Coord_Cell(coord))) { + return(1); + } + if (base->Unlimbo(coord)) { + if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) { + House->BuildStructure = STRUCT_NONE; + } + return(2); + } + } + } + break; + + default: + break; + } + + /* + ** Failure to exit the object results in a false return value. + */ + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Only do this for real human players. ST - 3/22/2019 1:38PM + */ + if (PlayerPtr != House) { + if (Session.Type != GAME_GLYPHX_MULTIPLAYER || House->IsHuman == false) { + return; + } + } + + bool buildable_via_capture = (IsCaptured && ActLike != House->ActLike) ? true : false; + + if (!IsInLimbo && Is_Discovered_By_Player()) { + switch (Class->ToBuild) { + int i; + int u; + int f; + int a; + int v; + + case RTTI_VESSELTYPE: + for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) { + if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference((VesselType)v), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_VESSELTYPE, v, House, buildable_via_capture); + } else { + Map.Add(RTTI_VESSELTYPE, v, buildable_via_capture); + } + } + } + break; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference((StructType)i), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_BUILDINGTYPE, i, House, buildable_via_capture); + } else { + Map.Add(RTTI_BUILDINGTYPE, i, buildable_via_capture); + } + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference((UnitType)u), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_UNITTYPE, u, House, buildable_via_capture); + } else { + Map.Add(RTTI_UNITTYPE, u, buildable_via_capture); + } + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference((InfantryType)f), ActLike)) { + if (InfantryTypeClass::As_Reference((InfantryType)f).IsDog) { + if (*this == STRUCT_KENNEL) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); + } else { + Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); + } + } + } else { + if (*this != STRUCT_KENNEL) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); + } else { + Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); + } + } + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference((AircraftType)a), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_AIRCRAFTTYPE, a, House, buildable_via_capture); + } else { + Map.Add(RTTI_AIRCRAFTTYPE, a, buildable_via_capture); + } + } + } + break; + + default: + break; + } + } +} + + + +#if (0) //Old code for reference. ST - 8/2/2019 2:41PM +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { + switch (Class->ToBuild) { + int i; + int u; + int f; + int a; + int v; + + case RTTI_VESSELTYPE: + for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) { + if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference((VesselType)v), ActLike)) { + Map.Add(RTTI_VESSELTYPE, v); + } + } + break; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference((StructType)i), ActLike)) { + Map.Add(RTTI_BUILDINGTYPE, i); + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference((UnitType)u), ActLike)) { + Map.Add(RTTI_UNITTYPE, u); + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference((InfantryType)f), ActLike)) { + if (InfantryTypeClass::As_Reference((InfantryType)f).IsDog) { + if (*this == STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } else { + if (*this != STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference((AircraftType)a), ActLike)) { + Map.Add(RTTI_AIRCRAFTTYPE, a); + } + } + break; + + default: + break; + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * * + * This routine is used to perform any fixups necessary when the attached animation has * + * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * + * At that point, normal reload procedures can commence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Fire_Out(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); +} + + +/*********************************************************************************************** + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * * + * This routine will handle the power adjustments for the associated house when the * + * building goes into limbo. This means that its power drain or production is subtracted * + * from the house accumulated totals. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the building limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Limbo(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Remove(this); + House->IsRecalcNeeded = true; + House->Recalc_Center(); + + /* + ** Update the power status of the owner's house. + */ + House->Adjust_Power(-Power_Output()); + House->Adjust_Drain(-Class->Drain); + House->Adjust_Capacity(-Class->Capacity, true); + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + /* + ** This could be a building that builds. If so, then the sidebar may need adjustment. + ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building + ** isn't available. Set it back to false so the rest of the Limbo code works. + ** Otherwise, the sidebar won't properly remove non-available buildables. + */ +// if (IsOwnedByPlayer && !ScenarioInit) { +// IsInLimbo = true; +// Map.Recalc(); +// IsInLimbo = false; +// } + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * * + * This will return the turret facing for this building. Some buildings don't have a * + * visual turret (e.g., pillbox) so they return a turret facing that always faces their * + * current target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current facing of the turret. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Turret_Facing(void) const +{ + if (!Class->IsTurretEquipped && Target_Legal(TarCom)) { + return(::Direction(Center_Coord(), As_Coord(TarCom))); + } + return(PrimaryFacing.Current()); +} + + +/*********************************************************************************************** + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * * + * This routine intercepts the Greatest_Threat function so that it can add the ability * + * to search for ground targets, if this isn't a SAM site. * + * * + * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * + * or THREAT_NORMAL. * + * * + * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * + * returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::Greatest_Threat(ThreatType threat) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + threat = threat | THREAT_RANGE; + +// if (Class->PrimaryWeapon != NULL) { +// if (Class->PrimaryWeapon->Bullet->IsAntiAircraft) { +// threat = threat | THREAT_AIR; +// } +// if (Class->PrimaryWeapon->Bullet->IsAntiGround) { +// threat = threat | THREAT_BUILDINGS|THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES; +// } +// threat = threat | THREAT_RANGE; +// } + return(TechnoClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * * + * This routine is called when construction has finished. Typically, this enables * + * new production options for factories. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + * 06/13/1995 JLB : Added helipad. * + *=============================================================================================*/ +void BuildingClass::Grand_Opening(bool captured) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!HasOpened || captured) { + HasOpened = true; + + /* + ** Adjust the owning house according to the power, drain, and Tiberium capacity that + ** this building has. + */ + House->Adjust_Drain(Class->Drain); + House->Adjust_Capacity(Class->Capacity); + House->IsRecalcNeeded = true; + + /* SPECIAL CASE: + ** Tiberium Refineries get a free harvester. Add a harvester to the + ** reinforcement list at this time. + */ + if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S)); + + UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); + if (unit != NULL) { + + /* + ** Try to place down the harvesters. If it could not be placed, then try + ** to place it in a nearby location. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_W)) { + cell = unit->Nearby_Location(this); + + /* + ** If the harvester could still not be placed, then refund the money + ** to the owner and then bail. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + House->Refund_Money(unit->Class->Cost_Of()); + delete unit; + } + } + } else { + + /* + ** If the harvester could not be created in the first place, then give + ** the full refund price to the owning player. + */ + House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); + } + } + + /* + ** Helicopter pads get a free attack helicopter. + */ + if (!Rule.IsSeparate && *this == STRUCT_HELIPAD && !captured) { + ScenarioInit++; + AircraftClass * air = 0; + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_BAD || House->ActLike == HOUSE_UKRAINE) { + air = new AircraftClass(AIRCRAFT_HIND, House->Class->House); + } else { + air = new AircraftClass(AIRCRAFT_LONGBOW, House->Class->House); + } + if (air) { + air->Height = 0; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + air->Assign_Mission(MISSION_GUARD); + air->Transmit_Message(RADIO_HELLO, this); + Transmit_Message(RADIO_TETHER); + } + } + ScenarioInit--; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * * + * This routine will start, stop, or toggle the repair process. When a building repairs, it * + * occurs incrementally over time. * + * * + * INPUT: control -- Determines how to control the repair process. * + * 0: Turns repair process off (if it was on). * + * 1: Turns repair process on (if it was off). * + * -1:Toggles repair process to other state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (control) { + case -1: + IsRepairing = (IsRepairing == false); + break; + + case 1: + if (IsRepairing) return; + IsRepairing = true; + break; + + case 0: + if (!IsRepairing) return; + IsRepairing = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + VocType soundid = VOC_NONE; + if (IsRepairing) { + if (Strength == Class->MaxStrength) { + soundid = VOC_SCOLD; + } else { + soundid = VOC_CLICK; + if (House->IsPlayerControl) { + Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object + } + IsWrenchVisible = true; + } + } else { + soundid = VOC_CLICK; + } + + if (House->IsPlayerControl) { + Sound_Effect(soundid, Coord); + } +} + + +/*********************************************************************************************** + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * * + * This routine will initiate or stop the sell back process for a building. It is called * + * when the player clicks on a building when the sell mode is active. * + * * + * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * + * -1 = toggle deconstruction state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Sell_Back(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Get_Buildup_Data()) { + bool decon = false; + switch (control) { + case -1: + decon = (Mission != MISSION_DECONSTRUCTION); + break; + + case 1: + if (Mission == MISSION_DECONSTRUCTION) return; + if (IsGoingToBlow) return; + decon = true; + break; + + case 0: + if (Mission != MISSION_DECONSTRUCTION) return; + decon = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + if (decon) { + Assign_Mission(MISSION_DECONSTRUCTION); + Commence(); + if (House->IsPlayerControl) { + Clicked_As_Target(PlayerPtr->Class->House); + } + } + if (House->IsPlayerControl) { + Sound_Effect(VOC_CLICK); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * * + * This routine will determine what action to perform if the mouse was clicked on the * + * object specified. This determination is used to control the mouse imagery and the * + * function process when the mouse button is pressed. * + * * + * INPUT: object -- Pointer to the object that, if clicked on, will control what action * + * is to be performed. * + * * + * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * + * object specified while the building is currently selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(ObjectClass const * object) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(object); + + if (action == ACTION_SELF) { + int index; + if (Class->Is_Factory() && PlayerPtr == House && *House->Factory_Counter(Class->ToBuild) > 1) { + switch (Class->ToBuild) { + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + action = ACTION_NONE; + if (*this == STRUCT_KENNEL) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_KENNEL) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } else { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_INFANTRYTYPE && *bldg != STRUCT_KENNEL) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + action = ACTION_NONE; + if (*this == STRUCT_AIRSTRIP) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_AIRSTRIP) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + else { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_AIRCRAFTTYPE && *bldg != STRUCT_AIRSTRIP) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + break; + + case RTTI_UNITTYPE: + case RTTI_UNIT: + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + action = ACTION_TOGGLE_PRIMARY; + break; + + case RTTI_NONE: + action = ACTION_NONE; + break; + + default: + break; + } + + } else { + action = ACTION_NONE; + } + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. Also don't allow targeting if the object is too + ** far away. + */ + if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN || !In_Range(object, 0))) { + action = ACTION_NONE; + } + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines what action will occur. * + * * + * This routine examines the cell specified and returns with the action that will be * + * performed if that cell were clicked upon while the building is selected. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * + * on this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(CELL cell) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(cell); + + if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Is_MCV_Deploy())) { + action = ACTION_NONE; + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && Class->PrimaryWeapon != NULL && !Class->PrimaryWeapon->Bullet->IsAntiGround) { +// if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN)) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * * + * This routine will start the building animating. This animation will loop indefinitely * + * until explicitly stopped. * + * * + * INPUT: bstate -- The animation state to initiate. * + * * + * OUTPUT: none * + * * + * WARNINGS: The building graphic state will reflect the first stage of this animation the * + * very next time it is rendered. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/02/1995 JLB : Uses normalize animation rate where applicable. * + *=============================================================================================*/ +void BuildingClass::Begin_Mode(BStateType bstate) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + QueueBState = bstate; + if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { + BState = bstate; + QueueBState = BSTATE_NONE; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + int rate = ctrl->Rate; + if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { + rate = Options.Normalize_Delay(rate); + } + Set_Rate(rate); + Set_Stage(ctrl->Start); + } +} + + +/*********************************************************************************************** + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * * + * This routine is used to set the center coordinate for this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate for the center location for the building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Center_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, CenterOffset[Class->Size])); +} + + +/*********************************************************************************************** + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * * + * This routine will return the coordinate to use when an object wishes to dock with this * + * building. Normally the docking coordinate would be the center of the building. * + * Exceptions to this would be the airfield and helipad. Their docking coordinates are * + * offset to match the building artwork. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to head to when trying to dock with this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Docking_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_HELIPAD) { + return(Coord_Add(Coord, XYP_COORD(24, 18))); + } + if (*this == STRUCT_AIRSTRIP) { + return(Coord_Add(Coord, XYP_COORD(ICON_PIXEL_W + ICON_PIXEL_W/2, 28))); + } + return(TechnoClass::Docking_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * * + * Use this routine to see if the building can fire its weapon. * + * * + * * + * INPUT: target -- The target that firing upon is desired. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * + * returned. Other cases will result in appropriate fire code value that indicates * + * why firing is not allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + FireErrorType canfire = TechnoClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsTurretEquipped) { + int diff = PrimaryFacing.Difference(Direction(TarCom)); + diff = abs(diff); + if (ABS(diff) > (*this == STRUCT_SAM ? 64 : 8)) { +// if (ABS(diff) > 8) { + return(FIRE_FACING); + } + + /* + ** If the turret is rotating then firing must be delayed. + */ +// if (PrimaryFacing.Is_Rotating()) { +// return(FIRE_ROTATING); +// } + } + + /* + ** Certain buildings cannot fire if there is insufficient power. + */ + if (Class->IsPowered && House->Power_Fraction() < 1) { + return(FIRE_BUSY); + } + + /* + ** If an obelisk can fire, check the state of charge. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && !IsCharged) { + return(FIRE_BUSY); + } + } + return(canfire); +} + + +/*********************************************************************************************** + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * * + * This routine will change the primary factory state of this building. The primary * + * factory is the one that units will be produced from (by default). * + * * + * INPUT: none * + * * + * OUTPUT: Is this building NOW the primary factory? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Toggle_Primary(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (IsLeader) { + IsLeader = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { + if (Class->ToBuild == RTTI_INFANTRYTYPE) { + if (*building == STRUCT_KENNEL && *this == STRUCT_KENNEL) { + building->IsLeader = false; + } else { + if (*building != STRUCT_KENNEL && *this != STRUCT_KENNEL) { + building->IsLeader = false; + } + } + } else if (Class->ToBuild == RTTI_AIRCRAFTTYPE) { + if (*building == STRUCT_AIRSTRIP && *this == STRUCT_AIRSTRIP) { + building->IsLeader = false; + } else { + if (*building != STRUCT_AIRSTRIP && *this != STRUCT_AIRSTRIP) { + building->IsLeader = false; + } + } + } else { + building->IsLeader = false; + } + } + } + IsLeader = true; + // + // MBL 04.20.2020 - Update so that each player in multiplayer will properly hear this when it applies to them + // + // if ((HouseClass *)House == PlayerPtr) { + // Speak(VOX_PRIMARY_SELECTED); + // } + if ((HouseClass *)House->IsHuman) { + Speak(VOX_PRIMARY_SELECTED, House); + } + } + Mark(MARK_CHANGE); + return(IsLeader); +} + + +/*********************************************************************************************** + * BuildingClass::Captured -- Captures the building. * + * * + * This routine will change the owner of the building. It handles updating any related * + * game systems as a result. Factories are the most prone to have great game related * + * consequences when captured. This could also affect the sidebar and building ownership. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the capture attempt successful? * + * * + * WARNINGS: Capturing could fail if the house is already owned by the one specified or * + * the building isn't allowed to be captured. * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * + *=============================================================================================*/ +bool BuildingClass::Captured(HouseClass * newowner) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsCaptureable && newowner != House) { +#ifdef TOFIX + switch (Owner()) { + case HOUSE_GOOD: + Speak(VOX_GDI_CAPTURED); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_CAPTURED); + break; + } +#endif + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + if (newowner->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + if (newowner->ActLike != House->ActLike) { + On_Achievement_Event(newowner, "OPPOSING_BUILDING_CAPTURED", object_type->IniName); + } else { + On_Achievement_Event(newowner, "BUILDING_CAPTURED", object_type->IniName); + } + } + } + + /* + ** Make sure the capturer isn't spying on his own building, and if + ** it was a radar facility, update the target house's RadarSpied field. + */ + if (SpiedBy & (1<<(newowner->Class->House)) ) { + SpiedBy -= (1<<(newowner->Class->House)); + if (*this == STRUCT_RADAR) { + Update_Radar_Spied(); + } + } + + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + IsJamming = false; + Arm = 0; + } + + /* + ** Add this building to the list of buildings captured this game. For internet stats purposes. + */ + if (Session.Type == GAME_INTERNET) { + newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); + } + + House->Adjust_Power(-Power_Output()); + LastStrength = 0; + House->Adjust_Drain(-Class->Drain); + int booty = House->Adjust_Capacity(-Class->Capacity, true); + + /* + ** If there is something loaded, then it gets captured as well. + */ + TechnoClass * tech = Attached_Object(); + if (tech) tech->Captured(newowner); + + /* + ** If something isn't technically attached, but is sitting on this + ** building for another reason (e.g., helicopter on helipad), then it + ** gets captured as well. + */ + tech = Contact_With_Whom(); + if (tech) { + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && (::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040 || + (tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND)) ) { + tech->Captured(newowner); + } else { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } + } + + /* + ** Abort any computer production in progress. + */ + if (Factory) { + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** Decrement the factory counter for the original owner. + */ + House->Active_Remove(this); + + /* + ** Flag that both owners now need to update their buildable lists. + */ + House->IsRecalcNeeded = true; + newowner->IsRecalcNeeded = true; + HouseClass * oldowner = House; + TARGET tocap = As_Target(); + + IsCaptured = true; + TechnoClass::Captured(newowner); + + oldowner->ToCapture = tocap; + oldowner->Recalc_Center(); + House->Recalc_Center(); + if (House->ToCapture == As_Target()) { + House->ToCapture = TARGET_NONE; + } + + SmudgeType bib; + CELL cell = Coord_Cell(Coord); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } +#ifdef FIXIT_CAPTURE_BIB + if (Session.Type == GAME_NORMAL) { + new SmudgeClass(bib, Cell_Coord(cell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } else { + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); + } +#else + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); +#endif + } + + House->Stole(Refund_Amount()); + + /* + ** Increment the factory count for the new owner. + */ + House->Active_Add(this); + + IsRepairing = false; + Grand_Opening(true); + House->Harvested(booty); + + Mark(MARK_CHANGE); + + /* + ** Perform a look operation when captured if it was the player + ** that performed the capture. + */ + if (Session.Type == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) { + Look(false); + } else { + if (House == PlayerPtr) { + Look(false); + } + } + /* + ** If it was spied upon by the player who just captured it, clear the + ** spiedby flag for that house. + */ + if (SpiedBy & (1 << (newowner->Class->House))) { + SpiedBy &= ~(1 << (newowner->Class->House)); + } + + /* + ** Update the new building's colors on the radar map. + */ + short const * offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + Map.Radar_Pixel(cell); + } + + if (oldowner) { + oldowner->Check_Pertinent_Structures(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * * + * The coordinate value returned from this function should be used for sorting purposes. * + * It has special offset adjustment applied so that vehicles don't overlap (as much). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * + *=============================================================================================*/ +COORDINATE BuildingClass::Sort_Y(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_REPAIR) { + return(Coord); + } + if (*this == STRUCT_HELIPAD) { + return(Center_Coord()); + } + if (*this == STRUCT_AIRSTRIP) { + return(Center_Coord()); + } + if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { + return(Center_Coord()); + } + if (*this == STRUCT_REFINERY) { + return(Center_Coord()); + } + + /* + ** Mines need to bias their sort location such that they are typically drawn + ** before any objects that might overlap them. + */ + if (*this == STRUCT_AVMINE || *this == STRUCT_APMINE) { + return(Coord_Move(Center_Coord(), DIR_N, CELL_LEPTON_H)); + } + + return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * * + * This routine will determine if the building can be placed down at the location * + * specified. * + * * + * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * + * of the building if it were to be placed down. * + * * + * OUTPUT: Returns with the move legality value for placement at the location specified. This * + * will either be MOVE_OK or MOVE_NO. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST && IsDown) { + return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); + } + + if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) { + return(MOVE_NO); + } + + return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * * + * Determines if the player can sell this building. Selling is possible if the building * + * is not currently in construction or deconstruction animation. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building be demolished at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * + * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * + *=============================================================================================*/ +bool BuildingClass::Can_Demolish(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsUnsellable) return(false); + + if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); + return(true); + } + return(false); +} + + +bool BuildingClass::Can_Demolish_Unit(void) const +{ + return((*this == STRUCT_REPAIR || *this == STRUCT_AIRSTRIP) && In_Radio_Contact() && Distance(Contact_With_Whom()) < 0x0080); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * * + * Buildings that can attack are given this mission. They will wait until a suitable target * + * comes within range and then launch into the attack mission. Buildings that have no * + * weaponry will just sit in this routine forever. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine will be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Guard(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this building has a weapon, then search for a target to attack. When + ** a target is found, switch into attack mode to deal with the threat. + */ + if (Is_Weapon_Equipped()) { + + /* + ** Weapon equipped buildings are ALWAYS ready to launch into another mission if + ** they are sitting around in guard mode. + */ + IsReadyToCommence = true; + + /* + ** If there is no target available, then search for one. + */ + if (!Target_Legal(TarCom)) { + ThreatType threat = THREAT_NORMAL; + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** There is a valid target. Switch into attack mode right away. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + Commence(); + return(1); + } + } else { + + /* + ** This is the very simple state machine that basically does + ** nothing. This is the mode that non weapon equipped buildings + ** are normally in. + */ + enum { + INITIAL_ENTRY, + IDLE + }; + switch (Status) { + case INITIAL_ENTRY: + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + case IDLE: + /* + ** Special case to break out of guard mode if this is a repair + ** facility and there is a customer waiting at the grease pit. + */ + if (*this == STRUCT_REPAIR && + In_Radio_Contact() && + Contact_With_Whom()->Is_Techno() && + ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && + Distance(Contact_With_Whom()) < 0x0040 && + Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + + Assign_Mission(MISSION_REPAIR); + return(1); + } + break; + + default: + break; + } + + if (*this == STRUCT_REPAIR) { + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } else { + return(MissionControl[Mission].Normal_Delay() * 3 + Random_Pick(0, 2)); + } + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Construction -- Handles mission construction. * + * * + * This routine will handle mission construction. When this mission is complete, the * + * building will begin normal operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Construction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_CONSTRUCTION); + Transmit_Message(RADIO_BUILDING); + if (House->IsPlayerControl) { + Sound_Effect(VOC_CONSTRUCTION, Coord); + } + Status = DURING; + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** When construction is complete, then transmit this + ** to the construction yard so that it can stop its + ** construction animation. + */ + Transmit_Message(RADIO_COMPLETE); // "I'm finished." + Transmit_Message(RADIO_OVER_OUT); // "You're free." + Begin_Mode(BSTATE_IDLE); + Grand_Opening(); + Assign_Mission(MISSION_GUARD); + PrimaryFacing = Class->StartFace; + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * * + * This state machine is only used when the building is deconstructing as a result of * + * selling. When this mission is finished, the building will no longer exist. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 08/13/1995 JLB : Enable selling of units on a repair bay. * + * 08/20/1995 JLB : Scatters infantry from scattered starting points. * + *=============================================================================================*/ +int BuildingClass::Mission_Deconstruction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Always force repair off. + */ + Repair(0); + + enum { + INITIAL, + HOLDING, + DURING + }; + switch (Status) { + case INITIAL: + + /* + ** Special check for the repair bay which has the ability to sell + ** whatever is on it. If there is something on the repair bay, then + ** it will be sold. If there is nothing on the repair bay, then + ** the repair bay itself will be sold. + */ + if (Can_Demolish_Unit() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * tech = Contact_With_Whom(); + Transmit_Message(RADIO_OVER_OUT); + if (IsOwnedByPlayer) Speak(VOX_UNIT_SOLD); + tech->Sell_Back(1); + Assign_Mission(MISSION_GUARD); + return(1); + } + + /* + ** Selling off a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + IsReadyToCommence = false; + Transmit_Message(RADIO_RUN_AWAY); + Status = HOLDING; + break; + + case HOLDING: + if (!IsTethered) { + + /* + ** The crew will evacuate from the building. The number of crew + ** members leaving is equal to the unrecovered cost of the building + ** divided by 100 (the typical cost of a minigunner infantryman). + */ + if (!Target_Legal(ArchiveTarget) || !Is_MCV_Deploy() || *this != STRUCT_CONST) { + int count = How_Many_Survivors(); + bool engine = false; + + while (count) { + + /* + ** Ensure that the player only gets ONE engineer and not from a captured + ** construction yard. + */ + InfantryType typ = Crew_Type(); + while (typ == INFANTRY_RENOVATOR && engine) { + typ = Crew_Type(); + } + if (typ == INFANTRY_RENOVATOR) engine = true; + + InfantryClass * infantry = 0; + if (typ != INFANTRY_NONE) infantry = new InfantryClass(typ, House->Class->House); + if (infantry != NULL) { + ScenarioInit++; + COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); + coord = Map[coord].Closest_Free_Spot(coord, false); + + if (infantry->Unlimbo(coord, DIR_N)) { + infantry->IsZoneCheat = infantry->Can_Enter_Cell(Coord_Cell(infantry->Center_Coord())) != MOVE_OK; + if (infantry->Class->IsNominal) infantry->IsTechnician = true; + ScenarioInit--; + infantry->Scatter(0, true); + ScenarioInit++; + infantry->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete infantry; + } + ScenarioInit--; + } + count--; + } + } + + if (House->IsPlayerControl) { + Sound_Effect(VOC_CASHTURN, Coord); + } + + /* + ** Destroy all attached objects. ST - 4/24/2020 9:38PM + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + Transmit_Message(RADIO_OVER_OUT); + Status = DURING; + Begin_Mode(BSTATE_CONSTRUCTION); + IsReadyToCommence = false; + break; + } + Transmit_Message(RADIO_RUN_AWAY); + break; + + case DURING: + if (IsReadyToCommence) { + House->IsRecalcNeeded = true; + + // MBL 05.06.2020 - "Structure Sold" is being heard when selecting/moving a redepolyable Con Yard to turn back into an MCV (RA only), so moving below + #if 0 + // MBL 04.06.2020: Fix being heard by wrong player + // if (IsOwnedByPlayer) Speak(VOX_STRUCTURE_SOLD); + if (IsOwnedByPlayer) { + if ((HouseClass *)House == PlayerPtr) { + Speak(VOX_STRUCTURE_SOLD); + } + } + #endif + bool mcv_redeployed = false; + + /* + ** Construction yards that deconstruct, really just revert back + ** to an MCV. + */ + if (Target_Legal(ArchiveTarget) && *this == STRUCT_CONST && House->IsHuman && Strength > 0) { + ScenarioInit++; + UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); + ScenarioInit--; + if (unit != NULL) { + + /* + ** Unlimbo the MCV onto the map. The MCV should start in the same + ** health condition that the construction yard was in. + */ + fixed ratio = Health_Ratio(); + int money = Refund_Amount(); + TARGET arch = ArchiveTarget; + COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE)); + + delete this; + + if (unit->Unlimbo(place, DIR_SW)) { + unit->Strength = (int)unit->Class_Of().MaxStrength * ratio; // Cast to (int). ST - 5/8/2019 + + /* + ** Lift the move destination from the building and assign + ** it to the unit. + */ + if (Target_Legal(arch)) { + unit->Assign_Destination(arch); + unit->Assign_Mission(MISSION_MOVE); + } + + mcv_redeployed = true; + + } else { + + /* + ** If, for some strange reason, the MCV could not be placed on the + ** map, then give the player some money to compensate. + */ + House->Refund_Money(money); + } + } else { + House->Refund_Money(Refund_Amount()); + delete this; + } + + } else { + + /* + ** Selling off a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** A sold building still counts as a kill, but it just isn't directly + ** attributed to the enemy. + */ + WhoLastHurtMe = HOUSE_NONE; + Record_The_Kill(NULL); + + /* + ** The player gets part of the money back for the sell. + */ + House->Refund_Money(Refund_Amount()); + House->Stole(-Refund_Amount()); + Limbo(); + + if (House) { + House->Check_Pertinent_Structures(); + } + + /* + ** Finally, delete the building from the game. + */ + delete this; + } + + // MBL 05.06.2020 - "Structure Sold" was being heard when selecting/moving a redepolyable Con Yard to turn back into an MCV (RA only) above, so moved here + #if 1 + if (!mcv_redeployed) + { + if (IsOwnedByPlayer) { + if ((HouseClass *)House == PlayerPtr) { + Speak(VOX_STRUCTURE_SOLD); + } + } + } + #endif + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * * + * Buildings that can attack are processed by this attack mission state machine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 02/22/1996 JLB : SAM doesn't lower back into ground. * + *=============================================================================================*/ +int BuildingClass::Mission_Attack(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_SAM) { + switch (Status) { + + /* + ** This is the target tracking state of the launcher. It will rotate + ** to face the current TarCom of the launcher. + */ + case SAM_READY: + if ((Class->IsPowered && House->Power_Fraction() < 1) || IsJammed) { + return(1); + } + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING; + } + } + } + return(1); + + /* + ** The launcher is in the process of firing. + */ + case SAM_FIRING: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Fire_At(TarCom, 1); + Status = SAM_READY; + } + } + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); + + } + + if (!Target_Legal(TarCom)) { + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } + + int primary = What_Weapon_Should_I_Use(TarCom); + IsReadyToCommence = true; + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + case FIRE_CANT: + case FIRE_RANGE: + case FIRE_AMMO: + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + break; + + case FIRE_FACING: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(2); + + case FIRE_REARM: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(Arm); + + case FIRE_BUSY: + return(1); + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, primary); + return(1); + + default: + break; + } + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * * + * This state machine handles the refinery when it unloads the harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Harvest(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, // Dock the Tiberium cannister. + WAIT_FOR_DOCK, // Waiting for docking to complete. + MIDDLE, // Offload "bails" of tiberium. + WAIT_FOR_UNDOCK // Waiting for undocking to complete. + }; + switch (Status) { + case INITIAL: + Status = WAIT_FOR_DOCK; + break; + + case WAIT_FOR_DOCK: + if (IsReadyToCommence) { + IsReadyToCommence = false; + Status = MIDDLE; + } + break; + + case MIDDLE: + if (IsReadyToCommence) { + IsReadyToCommence = false; + + /* + ** Force any bib squatters to scatter. + */ + Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_S)].Incoming(0, true, true); + + FootClass * techno = Attached_Object(); + if (techno) { + int bail = techno->Offload_Tiberium_Bail(); + + if (bail) { + House->Harvested(bail); + if (techno->Tiberium_Load() > 0) { + return(1); + } + } + } + Status = WAIT_FOR_UNDOCK; + } + break; + + case WAIT_FOR_UNDOCK: + if (IsReadyToCommence) { + + /* + ** Detach harvester and go back into idle state. + */ + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * * + * This state machine is used when the building is active in some sort of repair or * + * construction mode. The construction yard will animate. The repair facility will repair * + * anything that it docked on it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 06/25/1995 JLB : Handles repair facility * + * 07/29/1995 JLB : Repair rate is controlled by power rating. * + *=============================================================================================*/ +int BuildingClass::Mission_Repair(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = DURING; + break; + + case DURING: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); + } + + if (*this == STRUCT_REPAIR) { + enum { + INITIAL, + IDLE, + DURING + }; + switch (Status) { + case INITIAL: + { + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Assign_Mission(MISSION_GUARD); + return(1); + } + IsReadyToCommence = false; + int distance = 0x10; + TechnoClass *tech = Contact_With_Whom(); + + /* + ** BG: If the unit to repair is an aircraft, and the aircraft is + ** fixed-wing, and it's landed, be much more liberal with the + ** distance check. Fixed-wing aircraft are very inaccurate with + ** their landings. + */ + if (tech->What_Am_I() == RTTI_AIRCRAFT) { + if ( ((AircraftClass *)tech)->Class->IsFixedWing && + ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + distance = 0x80; + } + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < distance) { + Status = IDLE; + return(TICKS_PER_SECOND/4); + } + break; + } + + case IDLE: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * radio = Contact_With_Whom(); + + if ( ((radio->Health_Ratio() < Rule.ConditionGreen) || + (radio->What_Am_I() == RTTI_UNIT && *(UnitClass *)radio == UNIT_MINELAYER)) + && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { + + /* + ** If the object over the repair bay is marked as useless, then + ** sell it back to get some money. + */ + if (radio->IsUseless) { + if (!radio->House->IsHuman) { + radio->Sell_Back(1); + } + Status = INITIAL; + IsReadyToCommence = true; + } else { + + // + // MBL 04.27.2020: Legacy VOX_REPAIRING seems to be never called in TD, but only in RA. + // It is currently supported as a client GUI event when standard repairing begins, with "REPAIR1" on both TD and RA + // + // This repairing is in reference to the repair bay + // There is a newer bug (https://jaas.ea.com/browse/TDRA-6224) reporting that it is heard in multiplayer by + // other players, from this call, so modifiying the original call here: + // + // if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House); + + Status = DURING; + Begin_Mode(BSTATE_ACTIVE); + IsReadyToCommence = false; + } + } else { +// Transmit_Message(RADIO_RUN_AWAY); +///*BG*/ if(radio->Health_Ratio() >= Rule.ConditionGreen) { +// Transmit_Message(RADIO_RUN_AWAY); +// } + } + } + break; + + case DURING: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + return(1); + } + + /* + ** Check to see if the repair light blink has completed and the attached + ** unit is not doing something else. If these conditions are favorable, + ** the repair can proceed another step. + */ + if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + IsReadyToCommence = false; + + /* + ** Tell the attached unit to repair one step. It will respond with how + ** it fared. + */ + switch (Transmit_Message(RADIO_REPAIR)) { + + /* + ** The repair step proceeded smoothly. Proceed normally with the + ** repair process. + */ + case RADIO_ROGER: + break; + + /* + ** The repair operation was aborted because of some reason. Presume + ** that the reason is because of low cash. + */ + case RADIO_CANT: + if (IsOwnedByPlayer) Speak(VOX_NO_CASH); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step resulted in a completely repaired unit. + */ + case RADIO_ALL_DONE: + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House); + +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step could not be completed because this unit is already + ** at full strength. + */ + case RADIO_NEGATIVE: + default: +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (*this == STRUCT_HELIPAD || *this == STRUCT_AIRSTRIP) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { + Begin_Mode(BSTATE_ACTIVE); + Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); + Status = DURING; + return(1); + } + Assign_Mission(MISSION_GUARD); + break; + + case DURING: + if (IsReadyToCommence) { + if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { + Assign_Mission(MISSION_GUARD); + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + return(1); + } else { + fixed pfrac = Saturate(House->Power_Fraction(), 1); + if (pfrac < fixed::_1_2) pfrac = fixed::_1_2; + int time = Inverse(pfrac) * Rule.ReloadRate * TICKS_PER_MINUTE; +// int time = Bound((int)(TICKS_PER_SECOND * Saturate(House->Power_Fraction(), 1)), 0, TICKS_PER_SECOND); +// time = (TICKS_PER_SECOND*3) - time; + IsReadyToCommence = false; + return(time); + } + } + break; + + default: + break; + } + return(3); + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * * + * This handles the Temple of Nod launching its nuclear missile. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int BuildingClass::Mission_Missile(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_ADVANCED_TECH) { + enum { + DOOR_OPENING, + LAUNCH_UP, + SATELLITE_DEPLOY, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building, the missile rising, and smoke broiling. + */ + case DOOR_OPENING: + { +#ifdef FIXIT_VERSION_3 + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + if (sput) { + IsReadyToCommence = false; + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + } +#else + IsReadyToCommence = false; + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + return(1); +#endif + } + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + AnimClass * sput = As_Animation(AnimToTrack); + if (sput) { + if (sput->Fetch_Stage() >= 19) { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + + BulletClass * bullet = new BulletClass(BULLET_GPS_SATELLITE, targ, this, 200, WARHEAD_FIRE, MPH_ROCKET); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Assign_Mission(MISSION_GUARD); + } + } + } + } + return(1); + } + } + + if (*this == STRUCT_MSLO) { + enum { + INITIAL, + DOOR_OPENING, + LAUNCH_UP, + LAUNCH_DOWN, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building. + */ + case INITIAL: + IsReadyToCommence = false; + Begin_Mode(BSTATE_ACTIVE); // open the door + Status = DOOR_OPENING; + return(1); + + /* + ** This polls for the case when the door is actually open and + ** then kicks off the missile smoke. + */ + case DOOR_OPENING: + if (IsReadyToCommence) { + Begin_Mode(BSTATE_AUX1); // hold the door open + Status = LAUNCH_UP; + return(14); + } + return(1); + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + BulletClass * bullet = new BulletClass(BULLET_NUKE_UP, targ, this, 200, WARHEAD_HE, MPH_VERY_FAST); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)28, 0xA0); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Speak(VOX_ABOMB_LAUNCH); + Status = LAUNCH_DOWN; + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + return(6); + } + bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 64; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + return(8 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is in the air, this handles waiting for + ** the missile to be off the screen and then launching one down + ** over the target. + */ + case LAUNCH_DOWN: + { + Begin_Mode(BSTATE_AUX2); // start the door closing + +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + Status = DONE_LAUNCH; + return(6); + } + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + if (bullet) { +#endif + Status = DONE_LAUNCH; + return(6); + } +#ifdef OBSOLETE + } + return(1); +#endif + + /* + ** Once the missile is done launching this handles allowing + ** the building to sit there with its door closed. + */ + case DONE_LAUNCH: + Begin_Mode(BSTATE_IDLE); // keep the door closed. + Assign_Mission(MISSION_GUARD); + return(60); + } + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * * + * This routine will reveal the building to the specified house. It will handle updating * + * the sidebar for player owned buildings. A player owned building that hasn't been * + * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * + * abilities even though it exists on the map for all other purposes. * + * * + * INPUT: house -- The house that this building is being revealed to. * + * * + * OUTPUT: Was this building revealed by this procedure? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Revealed(HouseClass * house) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Revealed(house)) { + + if (!ScenarioInit) { + House->JustBuiltStructure = Class->Type; + House->IsBuiltSomething = true; + } + House->IsRecalcNeeded = true; + + /* + ** Perform any grand opening here so that in the scenarios where a player + ** owned house is not yet revealed, it won't be reflected in the sidebar + ** selection icons. + */ + if (!In_Radio_Contact() && House->IsHuman && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } else { + if (!In_Radio_Contact() && !House->IsHuman && house == House && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * * + * This routine is called when the exact mode of the building isn't known. By examining * + * the building's condition, this routine will assign an appropriate mission. * + * * + * INPUT: initial -- This this being called during scenario init? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Enter_Idle_Mode(bool initial) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then + ** this must be an initial building. Start such buildings in idle state. For other buildings + ** it indicates that it is being placed during game play and thus it must start in + ** the "construction" mission. + */ + MissionType mission = MISSION_GUARD; + + + if (!initial || ScenarioInit || Debug_Map) { + Begin_Mode(BSTATE_IDLE); + mission = MISSION_GUARD; + } else { + Begin_Mode(BSTATE_CONSTRUCTION); + mission = MISSION_CONSTRUCTION; + } + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * * + * This routine will determine the number of pips that should be filled in when rendering * + * the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of pips to display as filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Pip_Count(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Class->Max_Pips() * House->Tiberium_Fraction()); +} + + +/*********************************************************************************************** + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * * + * This routine is called when the building is destroyed by "unnatural" means. Typically * + * as a result of combat. If the building is known to the player, then it should be * + * announced. * + * * + * INPUT: source -- The object most directly responsible for the building's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Death_Announcement(TechnoClass const * source) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (source != NULL && House->IsPlayerControl) { + Speak(VOX_STRUCTURE_DESTROYED); + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * * + * This routine will return with the default direction to use when firing from this * + * building. This is the facing of the turret except for the case of non-turret equipped * + * buildings that have a weapon (e.g., guard tower). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Fire_Direction(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + return(PrimaryFacing.Current()); + } + return(Direction(TarCom)); +} + + +/*********************************************************************************************** + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * * + * Use this routine to fetch the remap table to use. This override function is needed * + * because the default remap table for techno objects presumes the object is a unit. * + * Buildings aren't units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the proper remap table to use for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Remap_Table(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(House->Remap_Table(IsBlushing, Class->Remap)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * * + * This is the unload mission for a building. This really only applies to the weapon's * + * factory, since it needs the sophistication of an unload mission due to the door * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Unload(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_WEAP) { + CELL cell = Coord_Cell(Coord) + Class->ExitList[0]; + COORDINATE coord = Cell_Coord(cell); + CellClass * cellptr = &Map[cell]; + enum { + INITIAL, + CLEAR_BIB, + OPEN, + LEAVE, + CLOSE + }; + enum { + DOOR_STAGES = 5, + DOOR_RATE = 8 + }; + UnitClass * unit; + switch (Status) { + /* + ** Start the door opening. + */ + case INITIAL: +// if (cellptr->Cell_Techno()) { +// cellptr->Incoming(0, true); +// } + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_GUARD); + unit->Commence(); + } + Open_Door(DOOR_RATE, DOOR_STAGES); + Status = CLEAR_BIB; + break; + + /* + ** Now that the occupants can peek out the door, they will tell + ** everyone that could be blocking the way, that they should + ** scatter away. + */ + case CLEAR_BIB: + if (cellptr->Cell_Techno()) { + cellptr->Incoming(0, true, true); + + /* + ** Scatter everything around the weapon's factory door. + */ + for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) { + CellClass * cptr = &cellptr->Adjacent_Cell(f); + if (cptr->Cell_Building() == NULL) { + cptr->Incoming(coord, true, true); + } + } + } else { + Status = OPEN; + } + break; + + /* + ** When the door is finally open and the way is clear, tell the + ** unit to drive out. + */ + case OPEN: + if (Is_Door_Open()) { + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_MOVE); + + if (House->IQ >= Rule.IQGuardArea) { + unit->Assign_Mission(MISSION_GUARD_AREA); + unit->ArchiveTarget = ::As_Target(House->Where_To_Go(unit)); + } + unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, coord); +// unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Adjacent_Cell(Center_Coord(), FACING_S), FACING_S)); + unit->Set_Speed(128); + Status = LEAVE; + } else { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } + } + break; + + /* + ** Wait until the unit has completely left the building. + */ + case LEAVE: + if (!IsTethered) { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } else { + +// if (In_Radio_Contact() && !((FootClass *)Contact_With_Whom())->IsDriving) { +// Transmit_Message(RADIO_OVER_OUT); +// } + + } + break; + + /* + ** Wait while the door closes. + */ + case CLOSE: + if (Is_Door_Closed()) { + Enter_Idle_Mode(); + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + Assign_Mission(MISSION_GUARD); + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * * + * This routine will return the current power output for this building. The power output * + * is adjusted according to the damage level of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current power output for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Power_Output(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Power) { + return(Class->Power * fixed(LastStrength, Class->MaxStrength)); + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Detach -- Handles target removal from the game system. * + * * + * This routine is called when the specified target is about to be removed from the game * + * system. * + * * + * INPUT: target -- The target to be removed from this building's targeting computer. * + * * + * all -- Is the target about to be completely eliminated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach(TARGET target, bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + TechnoClass::Detach(target, all); + if (target == WhomToRepay) { + WhomToRepay = TARGET_NONE; + } + if (target == AnimToTrack) { + AnimToTrack = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * * + * When selling very cheap buildings (such as the silo), a technician will pop out since * + * generating minigunners would be overkill -- the player could use this loophole to * + * gain an advantage. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type that this building will generate as a survivor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType BuildingClass::Crew_Type(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (Class->Type) { + case STRUCT_STORAGE: + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + + case STRUCT_CONST: + if (!IsCaptured && House->IsHuman && Percent_Chance(25)) { + return(INFANTRY_RENOVATOR); + } + break; + + case STRUCT_KENNEL: + if (Percent_Chance(50)) { + return(INFANTRY_DOG); + } else { + return(INFANTRY_NONE); + } + + case STRUCT_TENT: + case STRUCT_BARRACKS: + return(INFANTRY_E1); + + default: + break; + } + return(TechnoClass::Crew_Type()); +} + + +/*********************************************************************************************** + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * * + * When this routine is called, it indicates that the building is about to be destroyed * + * or captured. In such a case any production it may be doing, must be abandoned. * + * * + * INPUT: all -- Is the object about the be completely destroyed? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach_All(bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If it is producing something, then it must be abandoned. + */ + if (Factory) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + if (House) { + FactoryClass * factory = House->Fetch_Factory(Class->ToBuild); + + /* + ** If a factory was found, then temporarily disable this building and then + ** determine if any object that is being produced can still be produced. If + ** not, then the object being produced must be abandoned. + */ + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { + House->Abandon_Production(Class->ToBuild); + } + IsInLimbo = false; + } + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * * + * This routine is used to clear the way for placement of the specified object (usually * + * a building). If there are friendly units blocking the placement area, they are told * + * to scatter. Enemy blocking units are attacked. * + * * + * INPUT: techno -- Pointer to the object that is desired to be placed. * + * * + * cell -- The cell that placement wants to occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1995 JLB : Created. * + * 09/27/1995 JLB : Revised to use type class function. * + *=============================================================================================*/ +bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (techno) { + return (((BuildingTypeClass const &)techno->Class_Of()).Flush_For_Placement(cell, House)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this building * + * * + * This routine is called when the building needs to discharge a unit. It will find a * + * nearby (adjacent) cell that is clear enough for the specified object to enter. Typical * + * use of this routine is when the airfield disgorges its cargo. * + * * + * INPUT: techno -- Pointer to the object that wishes to exit this building. * + * * + * OUTPUT: Returns with the cell number to use for object placement. If no free location * + * could be found, then zero (0) is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 02/20/1996 JLB : Added default case for exit cell calculation. * + *=============================================================================================*/ +CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * ptr; + CELL origin = Coord_Cell(Coord); + + ptr = Class->ExitList; + if (ptr != NULL) { + while (*ptr != REFRESH_EOL) { + CELL cell = origin + *ptr++; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } else { + int x1, x2; + int y1, y2; + CELL cell; + + y1 = -1; + y2 = Class->Height(); + for (x1 = -1; x1 <= Class->Width(); x1++) { + cell = origin + x1 + (y1 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + x1 + (y2 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + + x1 = -1; + x2 = Class->Width(); + for (y1 = -1; y1 <= Class->Height(); y1++) { + cell = origin + (y1 * MAP_CELL_W) + x1; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + (y1 * MAP_CELL_W) + x2; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * * + * This routine answers the question 'can this building be moved?' Typically, only the * + * construction yard can be moved and it does this by undeploying back into a MCV. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building move to a new location under player control? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/04/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Can_Player_Move(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * * + * This routine will return the coordinate where an object that wishes to leave the * + * building will exit at. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that the object should be created at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Exit_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->ExitCoordinate) { + return(Coord_Add(Coord, Class->ExitCoordinate)); + } + return(TechnoClass::Exit_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern. * + * * + * Use this routine to coordinate a landing operation. The specified checkpoint is * + * converted into a cell number. The landing aircraft should fly over that cell and then * + * request the next check point. * + * * + * INPUT: cp -- The check point to convert to a cell number. * + * * + * OUTPUT: Returns with the cell that the aircraft should fly over in order to complete * + * that portion of the landing pattern. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CELL BuildingClass::Check_Point(CheckPointType cp) const +{ + CELL xoffset = 6; // Downwind offset. + CELL yoffset = 5; // Crosswind offset. + CELL cell = Coord_Cell(Center_Coord()); + + switch (cp) { + case CHECK_STACK: + xoffset = 0; + break; + + case CHECK_CROSSWIND: + yoffset = 0; + break; + + case CHECK_DOWNWIND: + default: + break; + } + + if ((Cell_X(cell) - Map.MapCellX) > Map.MapCellWidth/2) { + xoffset = -xoffset; + } + + if ((Cell_Y(cell) - Map.MapCellY) > Map.MapCellHeight/2) { + yoffset = -yoffset; + } + + return(XY_Cell(Cell_X(cell)+xoffset, Cell_Y(cell)+yoffset)); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Radar_Spied - set house's RadarSpied field appropriately. * + * * + * This routine is called when a radar facility is captured or destroyed. It fills in the * + * RadarSpied field of the house based on whether there's a spied-upon radar facility or not* + * * + * INPUT: none * + * * + * OUTPUT: House->RadarSpied field gets set appropriately. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/22/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Update_Radar_Spied(void) +{ + House->RadarSpied = 0; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (*obj == STRUCT_RADAR /* || *obj == STRUCT_EYE */) { + House->RadarSpied |= obj->Spied_By(); + } + } + } + Map.RadarClass::Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * * + * This is the basic scenario initialization of building function. It * + * is called when reading the scenario startup INI file and it handles * + * creation of all specified buildings. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Read_INI(CCINIClass & ini) +{ + BuildingClass * b; // Working unit pointer. + HousesType bhouse; // Building house. + StructType classid; // Building type. + CELL cell; // Cell of building. + char buf[128]; + char * trigname; // building's trigger's name + + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get a building entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + + /* + ** 2nd token: building name. + */ + classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); + + if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { + int strength; + DirType facing; + + /* + ** 3rd token: strength. + */ + strength = atoi(strtok(NULL, ",")); + + /* + ** 4th token: cell #. + */ + cell = atoi(strtok(NULL, ",")); + + /* + ** 5th token: facing. + */ + facing = (DirType)atoi(strtok(NULL, ",")); + + /* + ** 6th token: triggername (can be NULL). + */ + trigname = strtok(NULL, ","); + + bool sellable = false; + char * token_pointer = strtok(NULL, ","); + if (token_pointer) { + sellable = atoi(token_pointer); + } + + bool rebuild = false; + token_pointer = strtok(NULL, ","); + if (token_pointer) { + rebuild = atoi(token_pointer); + } + + if (HouseClass::As_Pointer(bhouse) != NULL) { + b = new BuildingClass(classid, bhouse); + if (b) { + + TriggerTypeClass * tp = TriggerTypeClass::From_Name(trigname); + if (tp) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + b->Trigger = tt; + } + } + b->IsAllowedToSell = sellable; + b->IsToRebuild = rebuild; + b->IsToRepair = rebuild || *b == STRUCT_CONST; + + if (b->Unlimbo(Cell_Coord(cell), facing)) { + strength = min(strength, 0x100); + strength = (int)b->Class->MaxStrength * fixed(strength, 256); // Cast to (int). ST - 5/8/2019 + b->Strength = strength; + if (b->Strength > b->Class->MaxStrength-3) b->Strength = b->Class->MaxStrength; + b->IsALemon = false; + } else { + + /* + ** If the building could not be unlimboed on the map, then this indicates + ** a serious error. Delete the building. + */ + delete b; + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * * + * This will store the building data (as it relates to scenario initialization) to the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that the building data will be stored to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing building data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the data out. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (!building->IsInLimbo) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%d", + building->House->Class->IniName, + building->Class->IniName, + building->Health_Ratio()*256, + Coord_Cell(building->Coord), + building->PrimaryFacing.Current(), + building->Trigger.Is_Valid() ? building->Trigger->Class->IniName : "None", + building->IsAllowedToSell, + building->IsToRebuild + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * * + * This routine will determine the "center" location of this building for purposes of * + * targeting. Usually, this location is somewhere near the foundation of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing upon this building (or trying to * + * walk onto it). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Target_Coord(void) const +{ + COORDINATE coord = Center_Coord(); + + if (Class->FoundationFace != FACING_NONE) { + return(Adjacent_Cell(coord, Class->FoundationFace)); + } + return(coord); +} + + +/*********************************************************************************************** + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * * + * Some building (notably the computer controlled ones) can have a factory object attached. * + * This routine handles processing of that factory and also detecting when production * + * should begin in order to initiate production. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Factory_AI(void) +{ + /* + ** Handle any production tied to this building. Only computer controlled buildings have + ** production attached to the building itself. The player uses the sidebar interface for + ** all production control. + */ + if (Factory.Is_Valid() && Factory->Has_Completed() && PlacementDelay == 0) { + TechnoClass * product = Factory->Get_Object(); +// FactoryClass * fact = Factory; + + switch (Exit_Object(product)) { + + /* + ** If the object could not leave the factory, then either request + ** a transport, place the (what must be a) building using another method, or + ** abort the production and refund money. + */ + case 0: + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + break; + + /* + ** Exiting this building is prevented by some temporary blockage. Wait + ** a bit before trying again. + */ + case 1: + PlacementDelay = TICKS_PER_SECOND*3; + break; + + /* + ** The object was successfully sent from this factory. Inform the house + ** tracking logic that the requested object has been produced. + */ + case 2: + switch (product->What_Am_I()) { + case RTTI_VESSEL: + House->JustBuiltVessel = ((VesselClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_UNIT: + House->JustBuiltUnit = ((UnitClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + House->JustBuiltInfantry = ((InfantryClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + House->JustBuiltStructure = ((BuildingClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + House->JustBuiltAircraft = ((AircraftClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + default: + break; + } +// fact->Completed(); + Factory->Completed(); +// delete fact; + delete (FactoryClass *)Factory; + Factory = 0; + break; + + default: + break; + } + } + + /* + ** Pick something to create for this factory. + */ + if (House->IsStarted && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Buildings that produce other objects have special factory logic handled here. + */ + if (Class->ToBuild != RTTI_NONE) { + if (Factory.Is_Valid()) { + + /* + ** If production has halted, then just abort production and make the + ** funds available for something else. + */ + if (PlacementDelay == 0 && !Factory->Is_Building()) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + } else { + + /* + ** Only look to start production if there is at least a small amount of + ** money available. In cases where there is no practical money left, then + ** production can never complete -- don't bother starting it. + */ + if (House->IsStarted && House->Available_Money() > 10) { + TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild, *this == STRUCT_KENNEL); + + /* + ** If a suitable object type was selected for production, then start + ** producing it now. + */ + if (techno != NULL) { + Factory = new FactoryClass; + if (Factory.Is_Valid()) { + if (!Factory->Set(*techno, *House)) { + delete (FactoryClass *)Factory; + Factory = 0; + } else { + House->Production_Begun(Factory->Get_Object()); + Factory->Start(); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * * + * Some buildings have a turret and this routine handles processing the turret rotation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 10/27/1996 JLB : Rotation does not occur if power and no power avail. * + *=============================================================================================*/ +void BuildingClass::Rotation_AI(void) +{ + if (Class->IsTurretEquipped && + Mission != MISSION_CONSTRUCTION && + Mission != MISSION_DECONSTRUCTION && + (!Class->IsPowered || House->Power_Fraction() >= 1)) { + + /* + ** Rotate turret to match desired facing. + */ + if (PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * * + * This handles the special logic required of the charging tesla coil. It requires special * + * processing since its charge up is dependant upon the target and power surplus of the * + * owning house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Charging_AI(void) +{ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && BState != BSTATE_CONSTRUCTION) { + if (Target_Legal(TarCom) && House->Power_Fraction() >= 1) { + if (!IsCharged) { + if (IsCharging) { +// if (stagechange) { + Mark(MARK_CHANGE); + if (Fetch_Stage() >= 9) { + IsCharged = true; + IsCharging = false; + Set_Rate(0); + } +// } + } else if (!Arm) { + IsCharged = false; + IsCharging = true; + Set_Stage(0); + Set_Rate(3); + Sound_Effect(VOC_TESLA_POWER_UP, Coord); + } + } + } else { + if (IsCharging || IsCharged) { + Mark(MARK_CHANGE); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * * + * This routine handle the repair animation and healing logic. It also detects when the * + * (computer controlled) building should begin repair or sell itself. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair_AI(void) +{ + if (House->IQ >= Rule.IQRepairSell && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + /* + ** Possibly start repair process if the building is below half strength. + */ +// unsigned ratio = MIN(House->Smartness, 0x00F0); + if (Can_Repair()) { + if (House->Available_Money() >= Rule.RepairThreshhold) { + if (!House->DidRepair) { + if (!IsRepairing && (IsCaptured || IsToRepair || House->IsHuman || Session.Type != GAME_NORMAL)) { + House->DidRepair = true; // flag that this house did its repair allocation for this frame + Repair(1); + + if (!House->IsHuman) { + House->RepairTimer = Random_Pick((int)(House->RepairDelay * (TICKS_PER_MINUTE/4)), (int)(House->RepairDelay * TICKS_PER_MINUTE * 2)); + } + } + } + } else { + if ((Session.Type != GAME_NORMAL || IsAllowedToSell) && IsTickedOff && House->Control.TechLevel >= Rule.IQSellBack && Random_Pick(0, 50) < House->Control.TechLevel && !Trigger.Is_Valid() && *this != STRUCT_CONST && Health_Ratio() < Rule.ConditionRed) { + Sell_Back(1); + } + } + } + } + + /* + ** If it is repairing, then apply any repair effects as necessary. + */ + if (IsRepairing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0) { + IsWrenchVisible = (IsWrenchVisible == false); + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + /* + ** Check for and expend any necessary monies to continue the repair. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsRepairing = false; + } + } else { + IsRepairing = false; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * * + * This will process the general building animation mechanism. It detects when the * + * building animation sequence has completed and flags the building to perform mission * + * changes as a result. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Animation_AI(void) +{ + bool stagechange = Graphic_Logic(); + bool toloop = false; + + /* + ** Always refresh the SAM site if it has an animation change. + */ + if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); + + if ((!Class->IsTurretEquipped && *this != STRUCT_TESLA) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { + if (stagechange) { + + /* + ** Check for animation end or if special case of MCV deconstructing when it is allowed + ** to convert back into an MCV. + */ + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + /* + ** When the last frame of the current animation sequence is reached, flag that + ** a new mission may be started. This must occur before the animation actually + ** loops so that if a mission change does occur, it will have a chance to change + ** the building graphic before the last frame is replaced by the first frame of + ** the loop. + */ + if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (!Target_Legal(ArchiveTarget) /*Is_MCV_Deploy()*/ && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { + IsReadyToCommence = true; + } + + /* + ** If the animation advances beyond the last frame, then start the animation + ** sequence over from the beginning. + */ + if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { + toloop = true; + } + Mark(MARK_CHANGE); + } else { + if (BState == BSTATE_NONE || Fetch_Rate() == 0) { + IsReadyToCommence = true; + } + } + } + + /* + ** If there is a door that is animating, then it might cause this building + ** to be redrawn. Check for and flag to redraw as necessary. + */ + if (Time_To_Redraw()) { + Clear_Redraw_Flag(); + Mark(MARK_CHANGE); + } + + /* + ** The animation sequence has looped. Restart it and flag this loop condition. + ** This is used to tell the mission system that the animation has completed. It + ** also signals that now is a good time to act on any pending mission. + */ + if (toloop) { + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * * + * This routine is called to determine how many survivors should run from this building * + * when it is either sold or destroyed. Buildings that are captured have fewer survivors. * + * The number of survivors is a portion of the cost of the building divided by the cost * + * of a minigunner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of soldiers that should run from this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::How_Many_Survivors(void) const +{ + if (IsSurvivorless || !Class->IsCrew) return(0); + + int divisor = InfantryTypeClass::As_Reference(INFANTRY_E1).Raw_Cost(); + if (divisor == 0) return(0); + if (IsCaptured) divisor *= 2; + int count = (Class->Raw_Cost() * Rule.SurvivorFraction) / divisor; + return(Bound(count, 1, 5)); +} + + +/*********************************************************************************************** + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * * + * This routine will return with a pointer to the shape data for the building. The shape * + * data is different than normal when the building is undergoing construction and * + * disassembly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Get_Image_Data(void) const +{ + if (BState == BSTATE_CONSTRUCTION) { + return(Class->Get_Buildup_Data()); + } + return(TechnoClass::Get_Image_Data()); +} + + +/*********************************************************************************************** + * BuildingClass::Value -- Determine the value of this building. * + * * + * The value of the building is normally just its ordinary assigned value. However, in the * + * case of fakes, the value is artificially enhanced to match the structure that is * + * being faked. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the point value of the building type. * + * * + * WARNINGS: The point value returned should not be used for scoring, only for target * + * scanning. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Value(void) const +{ + if (Class->IsFake) { + switch (Class->Type) { + case STRUCT_FAKEWEAP: + return(BuildingTypeClass::As_Reference(STRUCT_WEAP).Reward + BuildingTypeClass::As_Reference(STRUCT_WEAP).Risk); + + case STRUCT_FAKECONST: + return(BuildingTypeClass::As_Reference(STRUCT_CONST).Reward + BuildingTypeClass::As_Reference(STRUCT_CONST).Risk); + + case STRUCT_FAKE_YARD: + return(BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Reward + BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Risk); + + case STRUCT_FAKE_PEN: + return(BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Reward + BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Risk); + + case STRUCT_FAKE_RADAR: + return(BuildingTypeClass::As_Reference(STRUCT_RADAR).Reward + BuildingTypeClass::As_Reference(STRUCT_RADAR).Risk); + + default: + break; + } + } + return(TechnoClass::Value()); +} + + +/*********************************************************************************************** + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/20/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Remove_Gap_Effect(void) +{ + // unjam this one's field... + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:14AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (!House->IsPlayerControl && PlayerPtr->IsGPSActive) { + Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsGPSActive && player_house != House) { + Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, player_house); + } + } + } + + + // and rejam any overlapping buildings' fields + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House && *obj == STRUCT_GAP && obj!=this) { + obj->IsJamming = false; + obj->Arm = 0; +// Map.Jam_From(Coord_Cell(obj->Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + } +} + + +short const * BuildingClass::Overlap_List(bool redraw) const +{ + if ((Spied_By() & (1 << PlayerPtr->Class->House)) != 0 && Is_Selected_By_Player()) { + if (*this == STRUCT_BARRACKS || *this == STRUCT_TENT) { + static short const _list[] = { + -1, 2, (MAP_CELL_W*1)-1, (MAP_CELL_W*1)+2, REFRESH_EOL + }; + return(_list); + } else if (*this == STRUCT_REFINERY) { + static short const _list[] = { + 0, 2, (MAP_CELL_W*2)+0, (MAP_CELL_W*2)+1, (MAP_CELL_W*2)+2, REFRESH_EOL + }; + return(_list); + } + } + return(TechnoClass::Overlap_List(redraw)); +} + + +unsigned BuildingClass::Spied_By() const +{ + unsigned spiedby = TechnoClass::Spied_By(); + + /* + ** If it's an ore refinery or other such storage-capable building, + ** loop thru all of their buildings to see if ANY of them are spied + ** upon, 'cause once you spy any money, you've spied all of it. + */ + if (Class->Capacity) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building->House == House && building->Class->Capacity) { + spiedby |= building->SpiedBy; + } + } + } + + return spiedby; +} \ No newline at end of file diff --git a/REDALERT/BUILDING.H b/REDALERT/BUILDING.H new file mode 100644 index 000000000..f0f991a71 --- /dev/null +++ b/REDALERT/BUILDING.H @@ -0,0 +1,361 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUILDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUILDING_H +#define BUILDING_H + +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "bullet.h" +#include "target.h" +#include "factory.h" +#include "techno.h" + +#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory +#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open +#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling + +/**************************************************************************** +** For each instance of a building in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular building. +*/ +class BuildingClass : public TechnoClass +{ + public: + + /* + ** This points to the control data that gives this building its characteristics. + */ + CCPtr Class; + + /* + ** If this building is in the process of producing something, then this + ** will point to the factory manager. + */ + CCPtr Factory; + + /* + ** This is the house that originally owned this factory. Objects buildable + ** by this house type will be produced from this factory regardless of who + ** the current owner is. + */ + HousesType ActLike; + + /* + ** This building should be rebuilt if it is destroyed. This is in spite + ** of the condition of the prebuilt base list. + */ + unsigned IsToRebuild:1; + + /* + ** Is the building allowed to repair itself? + */ + unsigned IsToRepair:1; + + /* + ** If the computer owns this building, then it is allowed to sell it if + ** the situation warrants it. In the other case, it cannot sell the + ** building regardless of conditions. + */ + unsigned IsAllowedToSell:1; + + /* + ** If the building is at a good point to change orders, then this + ** flag will be set to true. + */ + unsigned IsReadyToCommence:1; + + /* + ** If this building is currently spending money to repair itself, then + ** this flag is true. It will automatically be set to false when the building + ** has reached full strength, when money is exhausted, or if the player + ** specifically stops the repair process. + */ + unsigned IsRepairing:1; + + /* + ** If repair is currently in progress and this flag is true, then a wrench graphic + ** will be overlaid on the building to give visual feedback for the repair process. + */ + unsigned IsWrenchVisible:1; + + /* + ** This flag is set when a commando has raided the building and planted + ** plastic explosives. When the CommandoCountDown timer expires, the + ** building takes massive damage. + */ + unsigned IsGoingToBlow:1; + + /* + ** If this building was destroyed by some method that would prevent + ** survivors, then this flag will be true. + */ + unsigned IsSurvivorless:1; + + /* + ** These state control variables are used by the obelisk for the charging + ** animation. + */ + unsigned IsCharging:1; + unsigned IsCharged:1; + + /* + ** A building that has been captured will not contain the full compliment + ** of crew. This is true even if it subsequently gets captured back. + */ + unsigned IsCaptured:1; + + /* + ** Used by the gap generator to decide if it should jam or unjam + */ + unsigned IsJamming:1; + + /* + ** Used by radar facilities to know if they're being jammed by a mobile + ** radar jammer + */ + unsigned IsJammed:1; + + /* + ** Used only by advanced tech center, this keeps track of whether the + ** GPS satellite has been fired or not. + */ + unsigned HasFired:1; + + /* + ** If Grand_Opening was already called for this building, then this + ** flag will be true. By utilizing this flag, multiple inadvertant + ** calls to Grand_Opening won't cause problems. + */ + unsigned HasOpened:1; + + /* + ** Special countdown to destruction value. If the building is destroyed, + ** it won't actually be removed from the map until this value reaches + ** zero. This delay is for cosmetic reasons. + */ + CDTimerClass CountDown; + + /* + ** This is the current animation processing state that the building is + ** in. + */ + BStateType BState; + BStateType QueueBState; + + /* + ** For multiplayer games, this keeps track of the last house to damage + ** this building, so if it burns to death or otherwise gradually dies, + ** proper credit can be given for the kill. + */ + HousesType WhoLastHurtMe; + + /* + ** This is the saboteur responsible for this building's destruction. + */ + TARGET WhomToRepay; + + /* + ** This is a record of the last strength of the building. Every so often, + ** it will compare this strength to the current strength. If there is a + ** discrepancy, then the owner power is adjusted accordingly. + */ + int LastStrength; + + /* + ** This is a target id of an animation we're keeping track of. Examples + ** of this usage are the advanced tech center, which needs to know + ** when the sputdoor animation has reached a certain stage. + */ + TARGET AnimToTrack; + + /* + ** This is the countdown timer that regulates placement retry logic + ** for factory type buildings. + */ + CDTimerClass PlacementDelay; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BuildingClass(StructType type, HousesType house); +#ifdef FIXIT_MULTI_SAVE + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), Factory(x), CountDown(x), PlacementDelay(x) {}; +#else + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), CountDown(x), PlacementDelay(x) {}; +#endif + virtual ~BuildingClass(void); + operator StructType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + TARGET Target_Scan(void); + BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);}; + + /* + ** Query functions. + */ + virtual int Value(void) const; + virtual void const * Get_Image_Data(void) const; + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual InfantryType Crew_Type(void) const; + virtual int Pip_Count(void) const; + virtual bool Can_Player_Move(void) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual DirType Fire_Direction(void) const; + virtual short const * Overlap_List(bool redraw=false) const; + int Shape_Number(void) const; + int Power_Output(void) const; + CELL Check_Point(CheckPointType cp) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Grand_Opening(bool captured = false); + virtual void Update_Buildables(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const; + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual int Exit_Object(TechnoClass * base); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void Fire_Out(void); + void Begin_Mode(BStateType bstate); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Death_Announcement(TechnoClass const * source=0) const; + virtual FireErrorType Can_Fire(TARGET, int which) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual bool Captured(HouseClass * newowner); + void Update_Radar_Spied(void); + + /* + ** AI. + */ + void Charging_AI(void); + void Rotation_AI(void); + void Factory_AI(void); + void Repair_AI(void); + void Animation_AI(void); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int control); + virtual void Sell_Back(int control); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Assign_Target(TARGET target); + virtual bool Toggle_Primary(void); + bool Flush_For_Placement(TechnoClass * techno, CELL cell); + + virtual int Mission_Unload(void); + virtual int Mission_Repair(void); + virtual int Mission_Attack(void); + virtual int Mission_Harvest(void); + virtual int Mission_Guard(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Missile(void); + virtual void Enter_Idle_Mode(bool initial=false); + void Remove_Gap_Effect(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "STRUCTURES";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual unsigned Spied_By() const; + + private: + void Drop_Debris(TARGET source = TARGET_NONE); + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + static COORDINATE const CenterOffset[BSIZE_COUNT]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/BULLET.CPP b/REDALERT/BULLET.CPP new file mode 100644 index 000000000..06e2c19e5 --- /dev/null +++ b/REDALERT/BULLET.CPP @@ -0,0 +1,1110 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BULLET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : October 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::AI -- Logic processing for bullet. * + * BulletClass::BulletClass -- Bullet constructor. * + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * BulletClass::delete -- Bullet memory delete. * + * BulletClass::new -- Allocates memory for bullet object. * + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Bullet constructor. * + * * + * This is the constructor for the bullet class. It handles all * + * initialization of the bullet and starting it in motion toward its * + * target. * + * * + * INPUT: id -- The type of bullet this is (could be missile). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 06/20/1994 JLB : Firer is a base class pointer. * + * 12/10/1994 JLB : Auto calculate range optional. * + * 12/12/1994 JLB : Handles small arms as an instantaneous effect. * + * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. * + * 12/31/1994 JLB : Removed range parameter (not needed). * + *=============================================================================================*/ +BulletClass::BulletClass(BulletType id, TARGET target, TechnoClass * payback, int strength, WarheadType warhead, int speed) : + ObjectClass(RTTI_BULLET, Bullets.ID(this)), + Class(BulletTypes.Ptr((int)id)), + Payback(payback), + PrimaryFacing(DIR_N), + IsInaccurate(false), + IsToAnimate(false), + IsLocked(true), + TarCom(target), + MaxSpeed(speed), + Warhead(warhead) +{ + Strength = strength; + Height = FLIGHT_LEVEL; +} + + +/*********************************************************************************************** + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * * + * The bullet destructor must detect if a dog has been attached to this bullet. If so, * + * then the attached dog must be unlimboed back onto the map. This operation is necessary * + * because, unlike other objects, the dog flies with the bullet it fires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletClass::~BulletClass(void) +{ + if (GameActive) { + + /* + ** SPECIAL CASE: + ** The dog is attached to the dog bullet in a limbo state. When the bullet is + ** destroyed, the dog must come back out of limbo at the closest location possible to + ** the bullet. + */ + if (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) { + + InfantryClass * dog = (InfantryClass *)Payback; + if (dog) { + bool unlimbo = false; + DirType dogface = dog->PrimaryFacing; + COORDINATE newcoord = Coord; + + /* + ** Ensure that the coordinate, that the dog is to appear at, is legal. If not, + ** then find a nearby legal location. + */ + if (Can_Enter_Cell(newcoord) != MOVE_OK) { + newcoord = Map.Nearby_Location(Coord_Cell(newcoord), dog->Class->Speed); + } + + /* + ** Try to put the dog down where the target impacted. If we can't + ** put it in that cell, then scan through the adjacent cells, + ** starting with our current heading, until we find a place where + ** we can put him down. If all 8 adjacent cell checks fail, then + ** just delete the dog. + */ + for (int i = -1; i < 8; i++) { + if (i != -1) { + newcoord = Adjacent_Cell(Coord, FacingType(i)); + } + ScenarioInit++; + if (dog->Unlimbo(newcoord, dog->PrimaryFacing)) { + dog->Mark(MARK_DOWN); + dog->Do_Action(DO_DOG_MAUL, true); + if (dog->WasSelected) { + dog->Select(); + } + ScenarioInit--; + + unlimbo = true; + break; + } + ScenarioInit--; + } + + Payback = 0; + + if (!unlimbo) { + delete dog; + } + } + } + BulletClass::Limbo(); + } + + Class=0; + Payback=0; +} + + +/*********************************************************************************************** + * BulletClass::new -- Allocates memory for bullet object. * + * * + * This function will "allocate" a block of memory for a bullet object. * + * This memory block is merely lifted from a fixed pool of blocks. * + * * + * INPUT: size -- The size of the memory block needed. * + * * + * OUTPUT: Returns with a pointer to an available bullet object block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void * BulletClass::operator new(size_t ) +{ + void * ptr = Bullets.Allocate(); + if (ptr) { + ((BulletClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BulletClass::delete -- Bullet memory delete. * + * * + * Since bullets memory is merely "allocated" out of a pool, it never * + * actually gets deleted. * + * * + * INPUT: ptr -- Generic pointer to bullet object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::operator delete(void * ptr) +{ + if (ptr) { + ((BulletClass *)ptr)->IsActive = false; + } + Bullets.Free((BulletClass *)ptr); +} + + +/*********************************************************************************************** + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * * + * This function will determine the cell occupation list and return a pointer to it. Most * + * bullets are small and the list is usually short, but on occasion, it can be a list that * + * rivals the size of regular vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet * + * is over. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 01/05/1995 JLB : Handles projectiles with altitude. * + *=============================================================================================*/ +short const * BulletClass::Occupy_List(bool) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Super-gigundo units use the >= 64 coord spillage list logic. + */ + if (Class->IsGigundo) { + static short _list[] = { + -1, 0, 1, + MAP_CELL_W*1-1, MAP_CELL_W*1, MAP_CELL_W*1+1, + -MAP_CELL_W*1-1, -MAP_CELL_W*1, -MAP_CELL_W*1+1, + MAP_CELL_W*2-1, MAP_CELL_W*2, MAP_CELL_W*2+1, + -MAP_CELL_W*2-1, -MAP_CELL_W*2, -MAP_CELL_W*2+1, + -MAP_CELL_W*3-1, -MAP_CELL_W*3, -MAP_CELL_W*3+1, + REFRESH_EOL + }; + return(_list); +// return(Coord_Spillage_List(Coord, 64)); + } + + /* + ** Flying units need a special adjustment to the spillage list to take into account + ** that the bullet imagery and the shadow are widely separated. + */ + if (Height > 0) { + static short _list[25]; + const short * ptr = Coord_Spillage_List(Coord, 5); + int index = 0; + CELL cell1 = Coord_Cell(Coord); + + while (ptr[index] != REFRESH_EOL) { + _list[index] = ptr[index]; + index++; + } + + COORDINATE coord = Coord_Move(Coord, DIR_N, Height); + CELL cell2 = Coord_Cell(coord); + ptr = Coord_Spillage_List(coord, 5); + while (*ptr != REFRESH_EOL) { + _list[index++] = *ptr + (cell2 - cell1); + ptr++; + } + _list[index] = REFRESH_EOL; + return(_list); + } + + return(Coord_Spillage_List(Coord, 10)); +} + + +/*********************************************************************************************** + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * * + * This routine marks the objects under the bullet so that they will * + * be redrawn. This is necessary as the bullet moves -- objects under * + * its path need to be restored. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Mark(MarkType mark) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (!Class->IsInvisible) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::AI -- Logic processing for bullet. * + * * + * This routine will perform all logic (flight) logic on the bullet. * + * Primarily this is motion, fuse tracking, and detonation logic. Call * + * this routine no more than once per bullet per game tick. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::AI(void) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + COORDINATE coord; + + ObjectClass::AI(); + + if (!IsActive) return; + + /* + ** Ballistic objects are handled here. + */ + bool forced = false; // Forced explosion. + if ((Class->IsArcing || Class->IsDropping) && !IsFalling) { + forced = true; + } + + /* + ** Homing projectiles constantly change facing to face toward the target but + ** they only do so every other game frame (improves game speed and makes + ** missiles not so deadly). + */ + if ((Frame & 0x01) && Class->ROT != 0 && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom))); + } + + /* + ** Move the projectile forward according to its speed + ** and direction. + */ + coord = Coord; + if (Class->IsFlameEquipped) { + if (IsToAnimate) { + if (stricmp(Class->GraphicName, "FB1") == 0) { + new AnimClass(ANIM_FBALL_FADE, coord, 1); + } else { + new AnimClass(ANIM_SMOKE_PUFF, coord, 1); + } + } + IsToAnimate = !IsToAnimate; + } + + /* + ** Handle any body rotation at this time. This process must + ** occur every game fame in order to achieve smooth rotation. + */ + if (PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Rotation_Adjust(Class->ROT); + } + switch (Physics(coord, PrimaryFacing)) { + /* + ** When a projectile reaches the edge of the world, it + ** vanishes from existence -- presumed to explode off + ** map. + */ + case IMPACT_EDGE: + Mark(); + if (Payback != NULL && Class->Type == BULLET_GPS_SATELLITE) { + + bool reveal = false; + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (Payback->House == PlayerPtr) { + reveal = true; + } + } else { + if (Payback->House->IsHuman) { + reveal = true; + } + } + if (reveal) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, Payback->House); + } + Map.RadarClass::Flag_To_Redraw(true); + } + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (Payback != NULL && Class->Type == BULLET_NUKE_UP && Payback->House->Control.TechLevel <= 10) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(Payback->House->NukeDest), Payback, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(Payback->House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(Payback->House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + } +#endif + delete this; + break; + + default: + case IMPACT_NONE: + + /* + ** The projectile has moved. Check its fuse. If detonation + ** is signaled, then do so. Otherwise, just move. + */ + case IMPACT_NORMAL: + Mark(); +// if(Class->Type == BULLET_NUKE_DOWN) { +// Render(true); +// } + if (Class->Type == BULLET_NUKE_UP) { + if (Payback != NULL) { + if (Distance(Payback->As_Target()) > 0x0C00) { + delete this; + return; + } + } + } + Coord = coord; + + /* + ** See if the bullet should be forced to explode now in spite of what + ** the fuse would otherwise indicate. Maybe the bullet hit a wall? + */ + if (!forced) { + forced = Is_Forced_To_Explode(Coord); + } + + /* + ** If the bullet is not to explode, then perform normal flight + ** maintenance (usually nothing). Otherwise, explode and then + ** delete the bullet. + */ + if (!forced && (Class->IsDropping || !Fuse_Checkup(Coord))) { + /* + ** Certain projectiles lose strength when they travel. + */ + if (Class->IsDegenerate && Strength > 5) { + Strength--; + } + + } else { + Bullet_Explodes(forced); + delete this; + } + break; + } + +} + + +/*********************************************************************************************** + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * * + * Use this routine to fetch a shape number to use for this bullet object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when drawing this bullet. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int BulletClass::Shape_Number(void) const +{ + int shapenum = 0; + + if (!Class->IsFaceless) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing)]; + } + + /* + ** For tumbling projectiles, fetch offset stage. + */ + if (Class->Tumble > 0) { + shapenum += (long)Frame % Class->Tumble; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * * + * This routine displays the bullet visual at the location specified. * + * * + * INPUT: x,y -- The center coordinate to render the bullet at. * + * * + * window -- The window to clip to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window clipping parameter. * + * 01/08/1995 JLB : Handles translucent colors if necessary. * + *=============================================================================================*/ +void BulletClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Certain projectiles aren't visible. This includes small bullets (which are actually + ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile). + */ + if (Class->IsInvisible) return; + + /* + ** If there is no shape loaded for this object, then + ** it obviously can't be rendered -- just bail. + */ + void const * shapeptr = Get_Image_Data(); + if (shapeptr == NULL) return; + + /* + ** Get the basic shape number for this projectile. + */ + int shapenum = Shape_Number(); + + /* + ** For flying projectiles, draw the shadow and adjust the actual projectile body + ** render position. + */ + if (Height > 0 && Class->IsShadow) { + + if (Class->IsParachuted) { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, AnimTypeClass::As_Reference(ANIM_PARA_BOMB).Get_Image_Data(), 1, x+Lepton_To_Pixel(Height/2), y+10, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } else { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } + y -= Lepton_To_Pixel(Height); + } + + /* + ** Draw the main body of the projectile. + */ + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Class->IsTranslucent) { + flags = SHAPE_GHOST; + } + if (Class->IsSubSurface) + { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::FadingShade); + } else { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * * + * This routine will zero out the bullet tracking list and object array in preparation for * + * the start of a new scenario. All bullets cease to exists after this function is * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Init(void) +{ + Bullets.Free_All(); +} + + +/*********************************************************************************************** + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * * + * When an object is removed from the game system, it must be removed all targeting and * + * tracking systems as well. This routine is used to remove the specified object from the * + * bullet. If the object isn't part of this bullet's tracking system, then no action is * + * performed. * + * * + * INPUT: target -- The target to remove from this tracking system. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Detach(TARGET target, bool all) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + ObjectClass * obj = As_Object(target); + if (Payback != NULL && obj == Payback) { + + /* + ** If we're being called as a result of the dog that fired us being put + ** in limbo, then don't detach. If for any other reason, detach. + */ + if (Payback->What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)Payback)->Class->IsDog) { + Payback = 0; + } + } + + if (all && target == TarCom) { + TarCom = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * * + * This routine is used to take a bullet object that is in limbo and transition it to the * + * game system. A bullet object so transitioned, will be drawn and logic processing * + * performed. In effect, it comes into existence. * + * * + * INPUT: coord -- The location where the bullet object is to appear. * + * * + * dir -- The initial facing for the bullet object. * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Try to unlimbo the bullet as far as the base class is concerned. Use the already + ** set direction and strength if the "punt" values were passed in. This allows a bullet + ** to be setup prior to being launched. + */ + if (!Class->IsHigh) { + Height = 0; + } + if (ObjectClass::Unlimbo(coord)) { + Map.Remove(this, In_Which_Layer()); + + COORDINATE tcoord = As_Coord(TarCom); + + /* + ** Homing projectiles (missiles) do NOT override facing. They just fire in the + ** direction specified and let the chips fall where they may. + */ + if (Class->ROT == 0 && !Class->IsDropping) { + dir = Direction(tcoord); + } + + /* + ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever + ** certain weapons are trained upon targets they were never designed to attack. Example: when + ** turrets or anti-tank missiles are fired at infantry. Indirect + ** fire is inherently inaccurate. + */ + if (IsInaccurate || Class->IsInaccurate || + ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Warhead == WARHEAD_AP || Class->IsFueled))) { + + /* + ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard + ** Circular Error of Probability (CEP) algorithm. High speed projectiles usually + ** just overshoot the target by extending the straight line flight. + */ + if (/*Class->ROT != 0 ||*/ Class->IsArcing) { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.HomingScatter); + scatterdist = max(scatterdist, 0); + + dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF); + tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist)); + } else { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.BallisticScatter); + scatterdist = max(scatterdist, 0); + tcoord = Coord_Move(tcoord, dir, Random_Pick(0, scatterdist)); + } + } + + /* + ** For very fast and invisible projectiles, just make the projectile exist at the target + ** location and dispense with the actual flight. + */ + if (MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) { + Coord = tcoord; + } + + /* + ** Set the range equal to either the class defined range or the calculated + ** number of game frames it would take for the projectile to reach the target. + */ + int range = 0xFF; + if (!Class->IsDropping) { + range = (::Distance(tcoord, Coord) / MaxSpeed) + 4; + } + + /* + ** Projectile speed is usually the default value for that projectile, but + ** certain projectiles alter speed according to the distance to the + ** target. + */ + int speed = MaxSpeed; + if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE; + if (Class->IsArcing) { + speed = MaxSpeed + (Distance(tcoord) / 32); + + /* + ** Set minimum speed (i.e., distance) for arcing projectiles. + */ + speed = max(speed, 25); + } + if (!Class->IsDropping) { + Fly_Speed(255, (MPHType)speed); + } + + /* + ** Arm the fuse. + */ + Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming)); + + /* + ** Projectiles that make a ballistic flight to impact point must determine a + ** vertical component for the projectile launch. This is crudely simulated + ** by biasing ground speed according to target distance and then giving + ** enough vertical velocity to keep the projectile airborne for the + ** desired amount of time. The mathematically correct solution would be to + ** calculate launch angle (given fixed projectile velocity) and then derive + ** the vertical and horizontal components. That solution would require a + ** square root and an arcsine lookup table. OUCH! + */ + Riser = 0; + if (Class->IsArcing) { + IsFalling = true; + Height = 1; + Riser = ((Distance(tcoord)/2) / (speed+1)) * Rule.Gravity; + Riser = max(Riser, 10); + } + if (Class->IsDropping) { + IsFalling = true; + Height = FLIGHT_LEVEL; +// Height = Pixel_To_Lepton(24); + Riser = 0; + if (Class->IsParachuted) { + AnimClass * anim = new AnimClass(ANIM_PARA_BOMB, Target_Coord()); +// AnimClass * anim = new AnimClass(ANIM_PARACHUTE, Target_Coord()); + if (anim) { + anim->Attach_To(this); + } + } + } + Map.Submit(this, In_Which_Layer()); + + PrimaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that should be used when firing at the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Target_Coord(void) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Coord)); +} + + +/*********************************************************************************************** + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * * + * This will return the coordinate to use when sorting this bullet in the display list. * + * Typically, this only occurs for bullets in the ground layer. Since bullets are to be * + * seen a bit more than the normal sorting order would otherwise imply, bias the sort * + * value such that bullets will tend to be drawn on top of the objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting this bullet in the display list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Move(Coord, DIR_S, CELL_LEPTON_H/2)); +} + + +/*********************************************************************************************** + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * * + * This examines the bullet to determine what rendering layer it should be in. The * + * normal logic applies unless this is a torpedo. A torpedo is always in the surface * + * layer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the render layer that this bullet should reside in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +LayerType BulletClass::In_Which_Layer(void) const +{ + if (Class->IsSubSurface) { + return(LAYER_SURFACE); + } + return(ObjectClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * * + * This routine will examine the bullet and where it is travelling in order to determine * + * if it should prematurely explode. Typical of this would be when a bullet hits a wall * + * or a torpedo hits a ship -- regardless of where the projectile was originally aimed. * + * * + * INPUT: coord -- The new coordinate to place the bullet at presuming it is forced to * + * explode and a modification of the bullet's coordinate is needed. * + * Otherwise, the coordinate is not modified. * + * * + * OUTPUT: bool; Should the bullet explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Is_Forced_To_Explode(COORDINATE & coord) const +{ + coord = Coord; + CellClass const * cellptr = &Map[coord]; + + /* + ** Check for impact on a wall or other high obstacle. + */ + if (!Class->IsHigh && cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) { + coord = Cell_Coord(Coord_Cell(coord)); + return(true); + } + + /* + ** Check to make sure that underwater projectiles (torpedoes) will not + ** travel in anything but water. + */ + if (Class->IsSubSurface) { + int d = ::Distance(Coord_Fraction(coord), XY_Coord(CELL_LEPTON_W/2, CELL_LEPTON_W/2)); + if (cellptr->Land_Type() != LAND_WATER || (d < CELL_LEPTON_W/3 && cellptr->Cell_Techno() != NULL && cellptr->Cell_Techno() != Payback)) { + + /* + ** Force explosion to be at center of techno object if one is present. + */ + if (cellptr->Cell_Techno() != NULL) { + coord = cellptr->Cell_Techno()->Target_Coord(); + } + + /* + ** However, if the torpedo was blocked by a bridge, then force the + ** torpedo to explode on top of that bridge cell. + */ + if (cellptr->Is_Bridge_Here()) { + coord = Coord_Snap(coord); + } + + return(true); + } + } + + /* + ** Bullets are generally more effective when they are fired at aircraft. + */ + if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) { + return(true); + } + + /* + ** No reason for forced explosion was detected, so return 'false' to + ** indicate that no forced explosion is required. + */ + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * * + * This handles the exploding bullet action. It will generate the animation and the * + * damage as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The bullet should be deleted after this routine is called. * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Bullet_Explodes(bool forced) +{ + /* + ** When the target is reached, explode and do the damage + ** required of it. For homing objects, don't force the explosion to + ** match the target position. Non-homing projectiles adjust position so + ** that they hit the target. This compensates for the error in line of + ** flight logic. + */ + if ( (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) || + (!forced && !Class->IsArcing && Class->ROT == 0 && Fuse_Target())) { + Coord = Fuse_Target(); + } + + /* + ** Non-aircraft targets apply damage to the ground. + */ + if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) { + Explosion_Damage(Coord, Strength, Payback, Warhead); + if (!IsActive) return; + + } else { + + /* + ** Special damage apply for SAM missiles. This is the only way that missile + ** damage affects the aircraft target. + */ + if (Distance(TarCom) < 0x0080) { + AircraftClass * object = As_Aircraft(TarCom); + + int str = Strength; + if (object) object->Take_Damage(str, 0, Warhead, Payback); + } + } + + /* + ** For projectiles that are invisible while travelling toward the target, + ** allow scatter effect for the impact animation. + */ + if (Class->IsInvisible) { + Coord = Coord_Scatter(Coord, 0x0020); + } + + /* + ** Fetch the land type that the explosion will be upon. Special case for + ** flying aircraft targets, their land type will be LAND_NONE. + */ + CellClass const * cellptr = &Map[Coord]; + LandType land = cellptr->Land_Type(); + if (Is_Target_Aircraft(TarCom) && As_Aircraft(TarCom)->In_Which_Layer() == LAYER_TOP) { + land = LAND_NONE; + } + + AnimType anim = Combat_Anim(Strength, Warhead, land); + + /* + ** If it's a water explosion that's going to play, don't play it + ** if its cell is the same as the center cell of the target ship. + */ + if (anim >= ANIM_WATER_EXP1 && anim <= ANIM_WATER_EXP3 && Is_Target_Vessel(TarCom)) { + if (Coord_Cell(Coord) == Coord_Cell(As_Vessel(TarCom)->Center_Coord())) { + anim = (AnimType) (ANIM_VEH_HIT1 + (anim - ANIM_WATER_EXP1)); + } + } + + if (anim != ANIM_NONE) { + AnimClass * aptr = new AnimClass(anim, Coord); + if (aptr) { + aptr->Sort_Above(TarCom); + } + /* + ** Special case trap: if they're making the nuclear explosion, + ** and no anim is available, force the nuclear damage anyway + ** because nuke damage is done in the middle of the animation + ** and if there's no animation, there won't be any damage. + */ + if (!aptr && anim == ANIM_ATOM_BLAST) { + GlyphX_Debug_Print("FAILED to create ANIM_ATOM_BLAST"); + HousesType house = HOUSE_NONE; + if (Payback) { + house = Payback->House->Class->House; + } + AnimClass::Do_Atom_Damage(house, Coord_Cell(Coord)); + } + + // MBL 05.20.2020 + // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked + // Per https://jaas.ea.com/browse/TDRA-6610 + // + else if (aptr && anim == ANIM_ATOM_BLAST && aptr->OwnerHouse == HOUSE_NONE) { + if (Payback && Payback->House && Payback->House->Class) { + aptr->OwnerHouse = Payback->House->Class->House; + } + } + } + +// if (Payback && Payback->House == PlayerPtr && stricmp(Class->Name(), "GPSSATELLITE") == 0) { + if (Payback && Class->Type == BULLET_GPS_SATELLITE) { + + bool reveal = false; + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (Payback->House == PlayerPtr) { + reveal = true; + } + } else { + if (Payback->House->IsHuman) { + reveal = true; + } + } + + if (reveal) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, Payback->House); + } + Map.RadarClass::Flag_To_Redraw(true); + } +// Sound_Effect(VOC_SATTACT2); + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +} \ No newline at end of file diff --git a/REDALERT/BULLET.H b/REDALERT/BULLET.H new file mode 100644 index 000000000..9853e05ea --- /dev/null +++ b/REDALERT/BULLET.H @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BULLET.H 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BULLET_H +#define BULLET_H + +#include "object.h" +#include "fly.h" +#include "fuse.h" + + +class BulletClass : public ObjectClass, + public FlyClass, + public FuseClass +{ + public: + + /* + ** This specifies exactly what kind of bullet this is. All of the static attributes + ** for this bullet is located in the BulletTypeClass pointed to by this variable. + */ + CCPtr Class; + + private: + /* + ** Records who sent this "present" so that an appropriate "thank you" can + ** be returned. + */ + TechnoClass * Payback; + + /* + ** This is the facing that the projectile is travelling. + */ + FacingClass PrimaryFacing; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BulletClass(BulletType id, TARGET target, TechnoClass * Payback, int strength, WarheadType warhead, int speed); +#ifdef FIXIT_MULTI_SAVE + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x), PrimaryFacing(x) {}; +#else + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x) {}; +#endif + virtual ~BulletClass(void); + operator BulletType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + bool Is_Forced_To_Explode(COORDINATE & coord) const; + void Bullet_Explodes(bool forced); + int Shape_Number(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual void Assign_Target(TARGET target) {TarCom = target;}; + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Detach(TARGET target, bool all); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void AI(void); + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const {return Occupy_List(false);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** If this bullet is forced to be inaccurate because of some outside means. A tank + ** firing while moving is a good example. + */ + unsigned IsInaccurate:1; + + private: + // Crude animation flag. + unsigned IsToAnimate:1; + + /* + ** Is this missile allowed to come in from out of bounds? + */ + unsigned IsLocked:1; + + /* + ** This is the target of the projectile. It is especially significant for those projectiles + ** that home in on a target. + */ + TARGET TarCom; + + /* + ** The speed of this projectile. + */ + int MaxSpeed; + + /* + ** The warhead of this projectile. + */ + WarheadType Warhead; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif diff --git a/REDALERT/CARGO.CPP b/REDALERT/CARGO.CPP new file mode 100644 index 000000000..7e85a6c76 --- /dev/null +++ b/REDALERT/CARGO.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARGO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : 10/31/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CargoClass::Attach -- Add unit to cargo hold. * + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * * + * This routine is used to dump the current cargo value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void CargoClass::Debug_Dump(MonoClass * mono) const +{ + if (How_Many()) { + mono->Set_Cursor(63, 3); + mono->Printf("(%d)%04X", How_Many(), Attached_Object()); + } +} +#endif + + +/*********************************************************************************************** + * CargoClass::Attach -- Add unit to cargo hold. * + * * + * This routine will add the specified unit to the cargo hold. The * + * unit will chain to any existing units in the hold. The chaining is * + * in a LIFO order. * + * * + * INPUT: object-- Pointer to the object to attach to the cargo hold. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 10/31/94 JLB : Handles chained objects. * + *=============================================================================================*/ +void CargoClass::Attach(FootClass * object) +{ + /* + ** If there is no object, then no action is necessary. + */ + if (object == NULL) return; + + object->Limbo(); + + /* + ** Attach any existing cargo hold object to the end of the list as indicated by the + ** object pointer passed into this routine. This is necessary because several objects may + ** be attached at one time or several objects may be attached as a result of several calls + ** to this routine. Either case must be handled properly. + */ + ObjectClass * o = object->Next; + while (o != NULL) { + if (o->Next == (void*)NULL) break; + o = o->Next; + } + if (o != NULL) { + o->Next = CargoHold; + } else { + object->Next = CargoHold; + } + + /* + ** Finally, assign the object pointer as the first object attached to this cargo hold. + */ + CargoHold = object; + Quantity = 0; + object = CargoHold; + while (object != NULL) { + Quantity++; + object = (FootClass *)(ObjectClass *)object->Next; + } +} + + +/*********************************************************************************************** + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * * + * This routine will take a unit from the cargo hold and extract it. * + * The unit extracted is the last unit added to the hold. If there * + * is no unit in the hold or the occupant is not a unit, then NULL is * + * returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the unit that has been extracted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Detach_Object(void) +{ + TechnoClass * unit = Attached_Object(); + + if (unit != NULL) { + CargoHold = (FootClass *)(ObjectClass *)unit->Next; + unit->Next = 0; + Quantity--; + } + return((FootClass *)unit); +} + + +/*********************************************************************************************** + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * * + * This routine will return with a pointer to the attached unit if one * + * is present. One would need to know this if this is a transport * + * unit and it needs to unload. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the attached unit. If there is no * + * attached unit, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Attached_Object(void) const +{ + if (Is_Something_Attached()) { + return(CargoHold); + } + return(NULL); +} + + diff --git a/REDALERT/CARGO.H b/REDALERT/CARGO.H new file mode 100644 index 000000000..524415a75 --- /dev/null +++ b/REDALERT/CARGO.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARGO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CARGO_H +#define CARGO_H + +class FootClass; + +/**************************************************************************** +** This class handles the basic cargo logic. +*/ +class CargoClass { + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CargoClass(void) : Quantity(0), CargoHold(0) {}; + CargoClass(NoInitClass const & ) {}; + ~CargoClass(void) {CargoHold=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void) {}; + + int How_Many(void) const {return Quantity;}; + bool Is_Something_Attached(void) const {return (CargoHold != 0);}; + FootClass * Attached_Object(void) const; + FootClass * Detach_Object(void); + void Attach(FootClass * object); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + + /* + ** This is the number of objects attached to this cargo hold. For transporter + ** objects, they might contain more than one object. + */ + unsigned char Quantity; + + /* + ** This is the target value of any attached object. A value of zero indicates + ** that no object is attached. + */ + FootClass * CargoHold; +}; + +#endif + diff --git a/REDALERT/CARRY.CPP b/REDALERT/CARRY.CPP new file mode 100644 index 000000000..c0fefb516 --- /dev/null +++ b/REDALERT/CARRY.CPP @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARRY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : May 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * CarryoverClass::Create -- Creates a carried over object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * * + * This is the constructor for a carry over object. Such an object is used to record the * + * object that will be "carried over" into a new scenario at some future time. * + * * + * INPUT: techno -- Pointer to the object that will be carried over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +CarryoverClass::CarryoverClass(TechnoClass * techno) : + RTTI(RTTI_NONE), + Cell(0), + Strength(0), + House(HOUSE_NONE) +{ + if (techno) { + RTTI = techno->What_Am_I(); + + switch (RTTI) { + case RTTI_UNIT: + Type.Unit = ((UnitClass *)techno)->Class->Type; + break; + + case RTTI_BUILDING: + Type.Building = ((BuildingClass *)techno)->Class->Type; + break; + + case RTTI_INFANTRY: + Type.Infantry = ((InfantryClass *)techno)->Class->Type; + break; + + case RTTI_VESSEL: + Type.Vessel = ((VesselClass *)techno)->Class->Type; + break; + + default: + break; + } + + House = techno->Owner(); + Strength = techno->Strength; + Cell = Coord_Cell(techno->Coord); + } +} + + +/*********************************************************************************************** + * CarryoverClass::Create -- Creates a carried over object. * + * * + * Use this routine to convert a carried over object into an actual object that will be * + * placed on the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully created and placed on the map? * + * * + * WARNINGS: This routine might not place the object if the old map location was invalid * + * or there are other barriers to the object's creation and placement. * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +bool CarryoverClass::Create(void) const +{ + TechnoClass * techno = 0; + TechnoTypeClass const * ttype = 0; + + switch (RTTI) { + case RTTI_UNIT: + ttype = &UnitTypeClass::As_Reference(Type.Unit); + techno = new UnitClass(Type.Unit, House); + break; + + case RTTI_INFANTRY: + ttype = &InfantryTypeClass::As_Reference(Type.Infantry); + techno = new InfantryClass(Type.Infantry, House); + break; + + case RTTI_BUILDING: + ttype = &BuildingTypeClass::As_Reference(Type.Building); + techno = new BuildingClass(Type.Building, House); + break; + + case RTTI_VESSEL: + ttype = &VesselTypeClass::As_Reference(Type.Vessel); + techno = new VesselClass(Type.Vessel, House); + break; + } + + if (techno) { + bool oldscen = ScenarioInit; + techno->Strength = Strength; + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = 0; + } + techno->Unlimbo(Cell_Coord(Cell)); + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = oldscen; + } + } + + return(false); +} + diff --git a/REDALERT/CARRY.H b/REDALERT/CARRY.H new file mode 100644 index 000000000..971c6a280 --- /dev/null +++ b/REDALERT/CARRY.H @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CARRY_H +#define CARRY_H + +class CarryoverClass : public LinkClass { + public: + CarryoverClass(TechnoClass * techno = 0); + CarryoverClass(NoInitClass const & x) : LinkClass(x) {} + + bool Create(void) const; + + //protected: + /* + ** What type of object this is. + */ + RTTIType RTTI; + + /* + ** This is the object type that is to be carried over. The exact nature of + ** this type depends on the RTTI value. Only certain object types are + ** recorded. + */ + union { + StructType Building; + UnitType Unit; + InfantryType Infantry; + VesselType Vessel; + } Type; + + /* + ** The location of the object. + */ + CELL Cell; + + /* + ** The strength of the object at the time is was recorded. + */ + int Strength; + + /* + ** This is the owner of the object. + */ + HousesType House; +}; + + +#endif diff --git a/REDALERT/CBN_.H b/REDALERT/CBN_.H new file mode 100644 index 000000000..81960ee2b --- /dev/null +++ b/REDALERT/CBN_.H @@ -0,0 +1,153 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* Error Constants, in case of values above 10 hexadecimal please call +* Marx International, Inc. +****************************************************************************/ + +#define PARAM_ERR 0x01 +#define BOX_NOT_FOUND 0x02 + +#define BOX_CRYPT_ERR 0x03 +#define UNI_CRYPT_ERR 0x04 +#define READ_RAM_ERR 0x05 +#define WRITE_RAM_ERR 0x06 +#define RAM_COUNT_ERR 0x07 +#define BOX_READY_ERR 0x08 + + +/**************************************************************************** +* Constants +****************************************************************************/ + +#define CRYPTLENG 32 +#define RAM1LENG 50 +#define RAM2LENG 433 +#define IDEALENG 32 + +/**************************************************************************** +* User Functions +****************************************************************************/ +extern "C" { + +extern short CbN_BoxReady(unsigned short iPortNr, unsigned char *pcBoxName); + +extern short CbN_ReadID1(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID2(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID3(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID4(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID5(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID6(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID7(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID8(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadSER(unsigned short iPortNr, unsigned char *pcSCodeSer, + unsigned long *plSerNum); + +extern short CbN_ReadRAM1(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam1,unsigned short + iStartAdr,unsigned short iLength, unsigned char *pcOutData); + +extern short CbN_ReadRAM2(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam2,unsigned short + iStartAdr,unsigned short iLength, unsigned char *pcOutData); + +extern short CbN_WriteRAM1(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam1,unsigned short + iStartAdr,unsigned short iLength,unsigned char *pcOutData); + +extern short CbN_WriteRAM2(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam2,unsigned short + iStartAdr,unsigned short iLength,unsigned char *pcOutData); + +extern short CbN_IncRAM1(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam1, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_DecRAM1(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam1, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_IncRAM2(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam2, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_DecRAM2(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam2, unsigned short + iCounterAdr, unsigned short *piNewCount); + + +extern short CbN_Decrypt(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned short iSeed, unsigned short iLength, + unsigned char *pcOutData); + +extern short CbN_Encrypt(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned short iSeed,unsigned short iLength, + unsigned char *pcOutData); + +extern short CbN_IDEA_Encrypt(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength); + +extern short CbN_IDEA_Decrypt(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength); + +extern short CbN_IDEA_EncryptP(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength, unsigned long lIdeaKey); + +extern short CbN_IDEA_DecryptP(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength, unsigned long lIdeaKey); + +extern short CbN_SetCounterRAM1(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + iNewCount); + +extern short CbN_ReadCounterRAM1(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + *piCurrentCount); + +extern short CbN_SetCounterRAM2(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + iNewCount); + +extern short CbN_ReadCounterRAM2(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + *piCurrentCount); + +} + \ No newline at end of file diff --git a/REDALERT/CCDDE.CPP b/REDALERT/CCDDE.CPP new file mode 100644 index 000000000..30e26b671 --- /dev/null +++ b/REDALERT/CCDDE.CPP @@ -0,0 +1,428 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DDE_Callback -- DDE server callback function * + * DDEServerClass::DDEServerClass -- class constructor * + * DDEServerClass::Enable -- Enables the DDE callback * + * DDEServerClass::Disable -- Disables the DDE callback * + * DDEServerClass::~DDEServerClass -- class destructor * + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include +#include "ccdde.h" +#include +#include + +DDEServerClass DDEServer; //Instance of the DDE Server class + +Instance_Class *DDE_Class = NULL; // pointer for client callback + // this *must* be called DDE_Class + +BOOL RA95AlreadyRunning = FALSE; //Was there an instance of Red Alert 95 already running when we started? + +/* +** Misc externs so we dont have to include FUNCTION.H +*/ +extern HWND MainWindow; +extern TimerClass GameTimer; +extern bool GameTimerInUse; +extern void WWDebugString (char *string); + + +/*********************************************************************************************** + * DDE_Callback -- DDE server callback function * + * * + * Just acts as a wrapper for the DDEServerClass callback function * + * * + * INPUT: ptr to data from client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:19PM ST : Created * + *=============================================================================================*/ +BOOL CALLBACK DDE_Callback (unsigned char *data, long length) +{ + return (DDEServer.Callback(data, length)); +} + + + + +/*********************************************************************************************** + * DDEServerClass::DDEServerClass -- class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::DDEServerClass(void) +{ + MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet + + //DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); + DDE_Class = new Instance_Class ("REDALERT", "WCHAT"); + + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + + if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ + RA95AlreadyRunning = TRUE; + }else{ + //DDE_Class->Register_Server( DDE_Callback ); // ST - 5/8/2019 + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Enable -- Enables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Enable(void) +{ + if (!IsEnabled){ + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Disable -- Disables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Disable(void) +{ + if (IsEnabled){ + DDE_Class->Enable_Callback( FALSE ); + IsEnabled = FALSE; + } +} + + + + + + +/*********************************************************************************************** + * DDEServerClass::~DDEServerClass -- class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::~DDEServerClass(void) +{ + Delete_MPlayer_Game_Info(); + delete( DDE_Class ); +} + + + +/*********************************************************************************************** + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * * + * * + * * + * INPUT: data from DDE client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Data has length and type as first 2 ints * + * * + * HISTORY: * + * 6/8/96 3:21PM ST : Created * + *=============================================================================================*/ +BOOL DDEServerClass::Callback(unsigned char *data, long length) +{ + + /* + ** If the packet length < 0 then this is a special advisory packet + */ + if ( length<0 ) { + + switch( length ) { + + case DDE_ADVISE_CONNECT: + WWDebugString("RA95 - DDE advisory: client connect detected."); + return TRUE; + + case DDE_ADVISE_DISCONNECT: + WWDebugString("RA95 - DDE advisory: client disconnect detected."); + return TRUE; + + default: + WWDebugString("RA95 - DDE advisory: Unknown DDE advise type."); + return FALSE; + } + + }else{ + + /* + ** Packet must be at least the length of the packet type & size fields to be valid + */ + if (length < 2*sizeof(int)) { + WWDebugString ("RA95 - Received invalid packet."); + return (FALSE); + } + + /* + ** Find out what kind of packet this is and its length. + */ + int *packet_pointer = (int *)data; + int actual_length = ntohl(*packet_pointer++); + int packet_type = ntohl(*packet_pointer++); + + /* + ** Strip the ID int from the start of the packet + */ + data += 2*sizeof (int); + length -= 2*sizeof (int); + actual_length -= 2*sizeof (int); + + /* + ** Take the appropriate action for the packet type + */ + switch ( packet_type ){ + + /* + ** This is a packet with the info required for starting a new internet game. This is really + * just C&CSPAWN.INI sent from WChat instead of read from disk. + */ + case DDE_PACKET_START_MPLAYER_GAME: + WWDebugString("RA95 - Received start game packet."); + Delete_MPlayer_Game_Info(); + MPlayerGameInfo = new char [actual_length + 1]; + memcpy (MPlayerGameInfo, data, actual_length); + *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string + MPlayerGameInfoLength = actual_length; + LastHeartbeat = 0; + break; + + case DDE_TICKLE: + WWDebugString("RA95 - Received 'tickle' packet."); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + break; + + case DDE_PACKET_HEART_BEAT: + WWDebugString("RA95 - Received heart beat packet."); + if (GameTimerInUse){ + LastHeartbeat = GameTimer.Time(); + }else{ + LastHeartbeat = 0; + } + break; + + default: + WWDebugString("RA95 - Received unrecognised packet."); + break; + + } + } + + return (TRUE); + +} + + + +/*********************************************************************************************** + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to data in .INI file format * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:23PM ST : Created * + *=============================================================================================*/ +char *DDEServerClass::Get_MPlayer_Game_Info (void) +{ + return (MPlayerGameInfo); +} + + + +/*********************************************************************************************** + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:24PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Delete_MPlayer_Game_Info(void) +{ + if (MPlayerGameInfo){ + delete [] MPlayerGameInfo; + MPlayerGameInfo = NULL; + } +} + + + +/*********************************************************************************************** + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: time since heartbeat * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:05PM ST : Created * + *=============================================================================================*/ +int DDEServerClass::Time_Since_Heartbeat(void) +{ + return (GameTimer.Time() - LastHeartbeat); +} + + + + +/*********************************************************************************************** + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * * + * * + * INPUT: ptr to the data to send * + * length of data * + * packet type identifier * + * * + * OUTPUT: true if packet successfully sent * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:07PM ST : Created * + *=============================================================================================*/ +BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) +{ + if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { + WWDebugString("RA95 - Failed to connect for POKE!"); + return (FALSE); + } + + char *poke_data = new char [length + 2*sizeof(int)]; + + int *poke_data_int = (int*)poke_data; + + *poke_data_int = htonl (length + 2*sizeof(int)); + *(poke_data_int+1)= htonl (packet_type); + + memcpy (poke_data + 8, data, length); + + + if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { + WWDebugString("RA95 - POKE failed!\n"); + DDE_Class->Close_Poke_Connection(); // close down the link + delete poke_data; + return (FALSE); + } + + DDE_Class->Close_Poke_Connection(); // close down the link + + delete poke_data; + + return (TRUE); +} + + +#endif //WIN32 diff --git a/REDALERT/CCDDE.H b/REDALERT/CCDDE.H new file mode 100644 index 000000000..fc8dd5f93 --- /dev/null +++ b/REDALERT/CCDDE.H @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "dde.h" + +class DDEServerClass { + + public: + + DDEServerClass (void); + ~DDEServerClass (void); + + + char *Get_MPlayer_Game_Info (void); //Returns pointer to game info + int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info + BOOL Callback(unsigned char *data, long length); //DDE callback function + void Delete_MPlayer_Game_Info(void); //release the game info memory + void Enable(void); //Enable the DDE callback + void Disable(void); //Disable the DDE callback + int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat + + /* + ** Enumeration for DDE packet types from WChat + */ + enum { + DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options + DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics. + DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there. + DDE_TICKLE, //Message to prompt other app to take focus. + DDE_CONNECTION_FAILED + }; + + + private: + + char *MPlayerGameInfo; //Pointer to game start packet + int MPlayerGameInfoLength; //Length of game start packet. + BOOL IsEnabled; //Flag for DDE callback enable + int LastHeartbeat; // Time since last heartbeat packet was received from WChat + +}; + + +extern DDEServerClass DDEServer; +extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type); + + +#endif //WIN32 diff --git a/REDALERT/CCFILE.CPP b/REDALERT/CCFILE.CPP new file mode 100644 index 000000000..3743b4ace --- /dev/null +++ b/REDALERT/CCFILE.CPP @@ -0,0 +1,692 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCFILE.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Error -- Handles displaying a file error message. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const * filename) : + Position(0) +{ + CCFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) : + Position(0) +{ +} + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ + if (!Force_CD_Available(RequiredCD)) { + Prog_End("CCFileClass::Error CD not found", true); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const * buffer, long size) +{ + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Is_Resident()) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void * buffer, long size) +{ + bool opened = false; + + /* + ** If the file isn't currently open, then open it. + */ + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Is_Resident()) { + long maximum = Data.Get_Size() - Position; + + size = maximum < size ? maximum : size; +// size = MIN(maximum, size); + if (size) { + memmove(buffer, (char *)Data + Position, size); +// Mem_Copy((char *)Pointer + Position, buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + + /* + ** If the file was opened by this routine, then close it at this time. + */ + if (opened) Close(); + + /* + ** Return with the number of bytes read. + */ + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + /* + ** When the file is resident, a mere adjustment of the virtual file position is + ** all that is required of a seek. + */ + if (Is_Resident()) { + switch (dir) { + case SEEK_END: + Position = Data.Get_Size(); + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + Position = Position < 0 ? 0 : Position; + Position = Position > Data.Get_Size() ? Data.Get_Size() : Position; +// Position = Bound(Position+pos, 0L, Length); + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 08/05/1996 JLB : Handles returning size of embedded file. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + /* + ** If the file is resident, the the size is already known. Just return the size in this + ** case. + */ + if (Is_Resident()) return(Data.Get_Size()); + + /* + ** If the file is not available as a stand alone file, then search for it in the + ** mixfiles in order to get its size. + */ + if (!CDFileClass::Is_Available()) { + long length = 0; + MFCD::Offset(File_Name(), NULL, NULL, NULL, &length); + return(length); + } + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + /* + ** A file that is open is presumed available. + */ + if (Is_Open()) return(true); + + /* + ** A file that is part of a mixfile is also presumed available. + */ + if (MFCD::Offset(File_Name())) { + return(true); + } + + /* + ** Otherwise a manual check of the file system is required to + ** determine if the file is actually available. + */ + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Is_Resident()) return(true); + + /* + ** Otherwise, go to a lower level to determine if the file is open. + */ + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + new(&Data) ::Buffer; + Position = 0; // Starts at beginning offset. + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MFCD * mixfile = NULL; + void * pointer = NULL; + long length = 0; + long start = 0; + if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) { + + assert(mixfile != NULL); + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (pointer == NULL && mixfile != NULL) { + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + Searching(true); + free(dupfile); + Bias(0); + Bias(start, length); + Seek(0, SEEK_SET); + } else { + new (&Data) ::Buffer(pointer, length); + Position = 0; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************************** + * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +unsigned long CCFileClass::Get_Date_Time(void) +{ + unsigned long datetime; + MFCD * mixfile; + + datetime = CDFileClass::Get_Date_Time(); + + if ( !datetime ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Get_Date_Time() ); + } + // else return 0 indicating no file + } + + return( datetime ); +} + + +/*********************************************************************************************** + * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +bool CCFileClass::Set_Date_Time( unsigned long datetime ) +{ + bool status; + MFCD * mixfile; + + status = CDFileClass::Set_Date_Time( datetime ); + + if ( !status ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) ); + } + // else return 0 indicating no file + } + + return( status ); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + + +static CCFileClass Handles[10]; + +int __cdecl Open_File(char const * file_name, int mode) +{ + for (int index = 0; index < ARRAY_SIZE(Handles); index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(WWERROR); +} + +void __cdecl Close_File(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +long __cdecl Read_File(int handle, void * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +long __cdecl Write_File(int handle, void const * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const * file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(char const * file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +int __cdecl Create_File(char const * file_name) +{ + return(CCFileClass(file_name).Create()); +} + +unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +void * __cdecl Load_Alloc_Data(char const * name, int ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +unsigned long __cdecl File_Size(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +unsigned long __cdecl Seek_File(int handle, long offset, int starting) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +void __cdecl WWDOS_Init(void) +{ +} + +void __cdecl WWDOS_Shutdown(void) +{ +} + +int __cdecl Find_Disk_Number(char const *) +{ + return(0); +} +#endif + +//unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} + +#ifdef WIN32 +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} +#endif + + +void Unfragment_File_Cache(void) +{ +} + + diff --git a/REDALERT/CCFILE.H b/REDALERT/CCFILE.H new file mode 100644 index 000000000..4e9478758 --- /dev/null +++ b/REDALERT/CCFILE.H @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 17, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCFILE_H +#define CCFILE_H + +//#include +#include +#include "mixfile.h" +#include "cdfile.h" +#include "buff.h" + + +/* +** This derived class for file access knows about mixfiles (packed files). It can handle opening +** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on +** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive. +*/ +class CCFileClass : public CDFileClass +{ + public: + CCFileClass(char const * filename); + CCFileClass(void); + virtual ~CCFileClass(void) {Position = 0;}; + + // Delete should be overloaded here as well. Don't allow deletes of mixfiles. + + bool Is_Resident(void) const {return(Data.Get_Buffer() != NULL);} + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int rights=READ) {Set_Name(filename);return Open(rights);}; + virtual int Open(int rights=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + private: + + /* + ** This indicates the file is actually part of a resident image of the mixfile + ** itself. In this case, the embedded file handle is invalid. All file access actually + ** gets routed through the cached version of the file. This is a pointer to the start + ** of the RAM image of the file. + */ + ::Buffer Data; +// void * Pointer; + + /* + ** This is the size of the file if it was embedded in a mixfile. The size must be manually + ** kept track of because the DOS file size is invalid. + */ +// long Length; + + /* + ** This is the current seek position of the file. It is duplicated here if the file is + ** part of a mixfile since the DOS seek position is not accurate. This value will + ** range from zero to the size of the file in bytes. + */ + long Position; + + // Force these to never be invoked. + CCFileClass const & operator = (CCFileClass const & c); + CCFileClass (CCFileClass const & ); +}; + +template <> class MixFileClass; + +#endif diff --git a/REDALERT/CCINI.CPP b/REDALERT/CCINI.CPP new file mode 100644 index 000000000..142b72b49 --- /dev/null +++ b/REDALERT/CCINI.CPP @@ -0,0 +1,1484 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCINI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCINI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current databas* + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * CCINIClass::Load -- Load the INI database from the file specified. * + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * CCINIClass::Save -- Save the INI data to the file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the file specified. * + * * + * This routine will load the database from the file specified in much the same manner * + * that the INIClass load function works. However, this class will examine the message * + * digest (if present) and compare it to the actual digest. If they differ, a special * + * return value is used. This will allow verification of the integrity of the ini data. * + * * + * INPUT: file -- Reference to the file that will be read from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: If the file was not read, returns 0. If the file was read ok, returns 1. If the * + * file was read ok, but the digest doesn't verify, returns 2. * + * * + * WARNINGS: If no message digest was present in the INI file, then no verification can * + * be performed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles digest control. * + *=============================================================================================*/ +bool CCINIClass::Load(FileClass & file, bool withdigest) +{ + return(Load(FileStraw(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * * + * This will load the INI database and in the process, it will fetch and verify any * + * message digest present. * + * * + * INPUT: straw -- The data stream to fetch the INI data from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: bool; Was the database loaded ok? (hack: returns "2" if digest doesn't match). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Load(Straw & file, bool withdigest) +{ + bool ok = INIClass::Load(file); + + Invalidate_Message_Digest(); + if (ok && withdigest) { + + /* + ** If a digest is present, fetch it. + */ + unsigned char digest[20]; + int len = Get_UUBlock("Digest", digest, sizeof(digest)); + if (len > 0) { + Clear("Digest"); + + /* + ** Calculate the message digest for the INI data that was read. + */ + Calculate_Message_Digest(); + + /* + ** If the message digests don't match, then return with the special error code. + */ + if (memcmp(digest, Digest, sizeof(digest)) != 0) { + return(2); + } + } + } + return(ok); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Save the INI data to the file specified. * + * * + * This routine will save the INI data to the file. It will add a message digest so that * + * validity check can be performed when the INI data is subsequently read. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: bool; Was the INI data saved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(FileClass & file, bool withdigest) const +{ + return(Save(FilePipe(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * * + * This routine will pipe the INI data to the pipe segment specified. It is functionally * + * the same as the save operation. A message digest is added to the output data so that * + * validity check can occur during a subsequent read. * + * * + * INPUT: straw -- Reference to the pipe that will receive the output ini data stream. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(Pipe & pipe, bool withdigest) const +{ + if (!withdigest) { + return(INIClass::Save(pipe)); + } + + /* + ** Just in case these entries are present, clear them out. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Calculate what the new digest should be. + */ + ((CCINIClass *)this)->Calculate_Message_Digest(); + + /* + ** Store the actual digest into the INI database. + */ + ((CCINIClass *)this)->Put_UUBlock("Digest", Digest, sizeof(Digest)); + + /* + ** Output the database to the pipe specified. + */ + int length = INIClass::Save(pipe); + + /* + ** Remove the digest from the database. It shouldn't stick around as if it were real data + ** since it isn't really part of the INI database proper. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Finally, return with the total number of bytes send out the pipe. + */ + return(length); +} + + +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * * + * This routine will fetch the lepton value as if it were expressed as cells. Example; * + * a value of 1 would mean 256 in leptons. * + * * + * INPUT: section -- The section identifier to look under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default value to use if the specified section and entry could * + * not be located. * + * * + * OUTPUT: Returns with the lepton value of the section & entry specified. If not found, then * + * the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +LEPTON CCINIClass::Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const +{ + fixed result = Get_Fixed(section, entry, fixed(defvalue, CELL_LEPTON_W)); + return(result * CELL_LEPTON_W); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * * + * This routine will store the lepton value as if it were expressed in cells. Example; * + * A lepton of 128 will be stored as ".5". * + * * + * INPUT: section -- The section identifier to store the value under. * + * * + * entry -- The entry to store the lepton value at. * + * * + * value -- The lepton value to store. * + * * + * OUTPUT: bool; Was the lepton value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Lepton(char const * section, char const * entry, LEPTON value) +{ + return(Put_Fixed(section, entry, fixed(value, CELL_LEPTON_W))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * * + * This routine will fetch the speed value as if it were expressed as a number from 0 * + * to 100. The value of 100 would translate into a speed of 256 leptons per game frame. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default speed value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the speed value. If no entry could be found, then the default value * + * will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +MPHType CCINIClass::Get_MPHType(char const * section, char const * entry, MPHType defvalue) const +{ + int val = Get_Int(section, entry, ((int)defvalue * 100) / 256); + return (MPHType(_Scale_To_256(val))); +} + + +/*********************************************************************************************** + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * * + * Use this routine to store a speed value into the INI database. The number stored will * + * be in a 0..100 format. A speed of 256 leptons per tick would be stored as 100. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the speed value to. * + * * + * value -- The speed value to store. * + * * + * OUTPUT: bool; Was the speed value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_MPHType(char const * section, char const * entry, MPHType value) +{ + return(Put_Int(section, entry, ((int)value * 100) / 256)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * * + * Use this to fetch a house bit array value from the INI database. This value will be * + * various bit positions set (1 << house#) for each house specified in the database. * + * Houses can be specified in series by the house name separated by commas or by the * + * special group names of "soviet", and "allies" to cover the houses that are members of * + * these groups. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default house bitfield to use if the entry could not be found. * + * * + * OUTPUT: Returns with the house bitfield value. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Owners(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long ownable = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + ownable = 0; + char * name = strtok(buffer, ","); + + while (name) { + ownable |= Owner_From_Name(name); + name = strtok(NULL, ","); + } + } + return(ownable); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * * + * Use this routine to store the house bitfield data into the database. The bitfield format * + * matches the format used by the Get_Owners function. Example; if both England and * + * Spain were specified in the bitfield, the entry would be stored as "England,Spain". * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier that is assigned the value. * + * * + * value -- The value to assign to the entry. * + * * + * OUTPUT: bool; Was the entry stored in the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Owners(char const * section, char const * entry, long value) +{ + char buffer[128]; + + buffer[0] = '\0'; + + if ((value & HOUSEF_ALLIES) == HOUSEF_ALLIES) { + strcat(buffer, "allies"); + value &= ~HOUSEF_ALLIES; + } + if ((value & HOUSEF_SOVIET) == HOUSEF_SOVIET) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, "soviet"); + value &= ~HOUSEF_SOVIET; + } + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if ((value & (1 << house)) != 0) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, HouseTypeClass::As_Reference(house).Name()); + } + } + + if (buffer[0] != '\0') { + return(Put_String(section, entry, buffer)); + } + return(true); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * * + * This routine will fetch the armor type from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Th identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the armor type specified in the INI database. If it could not be * + * found, then the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType CCINIClass::Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, ArmorName[defvalue], buffer, sizeof(buffer)); + return(Armor_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * * + * Use this routine to store the specified armor type to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the value at. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the entry stored in the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ArmorType(char const * section, char const * entry, ArmorType value) +{ + return(Put_String(section, entry, ArmorName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * * + * This routine will fetch a voc number from the database. The voc number will either * + * be a valid sound effect or VOC_NONE if no match could be found. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the sound effect (VocType) from the INI database. If the entry could * + * not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VocType CCINIClass::Get_VocType(char const * section, char const * entry, VocType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Voc_Name(defvalue), buffer, sizeof(buffer)); + return(Voc_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * * + * Use this routine to store a voc identifier (stored a the text name of the sound) into * + * the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- The entry to assign the value to. * + * * + * value -- The sound effect to store to the entry. * + * * + * OUTPUT: bool; Was the sound effect entry stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VocType(char const * section, char const * entry, VocType value) +{ + if (value == VOC_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Voc_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * * + * This will fetch an AnimType number from the INI database. The anim is stored as a text * + * name of the art file used for that anim. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default AnimType to use if the entry could not be located. * + * * + * OUTPUT: Returns with the anim type specified in the database. If it could not be found, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +AnimType CCINIClass::Get_AnimType(char const * section, char const * entry, AnimType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Anim_Name(defvalue), buffer, sizeof(buffer)); + return(Anim_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * * + * This routine will store the animation identifier (stored as the text name of the art * + * file it uses) to the INI database. * + * * + * INPUT: section -- The section identifier to place the entry under. * + * * + * entry -- The entry identifier to assign the animation number to. * + * * + * value -- The animation identifier to store with the entry. * + * * + * OUTPUT: bool; Was the animation identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_AnimType(char const * section, char const * entry, AnimType value) +{ + if (value == ANIM_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Anim_Name(value))); +} + + +UnitType CCINIClass::Get_UnitType(char const * section, char const * entry, UnitType defvalue) const +{ + char buffer[128]; + + char const * def = ""; + if (defvalue != UNIT_NONE) { + def = UnitTypeClass::As_Reference(defvalue).Name(); + } + Get_String(section, entry, def, buffer, sizeof(buffer)); + return(UnitTypeClass::From_Name(buffer)); +} + + +bool CCINIClass::Put_UnitType(char const * section, char const * entry, UnitType value) +{ + if (value == UNIT_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, UnitTypeClass::As_Reference(value).Name())); +} + + + +/*********************************************************************************************** + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * * + * This routine will fetch the weapon type from the INI database. The weapon type is * + * stored as a custom identifier string. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default weapon value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the weapon type specified by the entry. If the entry could not be * + * found then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType CCINIClass::Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Weapon_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * * + * Store the weapon identifier (as custom string name) to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier to store the weapon identifier with. * + * * + * value -- The weapon identifier to store. * + * * + * OUTPUT: bool; Was the weapon identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WeaponType(char const * section, char const * entry, WeaponType value) +{ + if (value == WEAPON_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WeaponTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * * + * Will fetch the warhead identifier from the INI database. * + * * + * INPUT: section -- The identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default return value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the found warhead type. If the entry could not be found then the * + * default warhead value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WarheadType CCINIClass::Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (WarheadType wh = WARHEAD_FIRST; wh < WARHEAD_COUNT; wh++) { + if (stricmp(WarheadTypeClass::As_Pointer(wh)->Name(), buffer) == 0) { + return(wh); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * * + * This will store the weapon identifier specified to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the warhead identifier. * + * * + * value -- The warhead identifier to store. * + * * + * OUTPUT: bool; Was the warhead identifier stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WarheadType(char const * section, char const * entry, WarheadType value) +{ + if (value == WARHEAD_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WarheadTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * * + * This routine will fetch the overlay identifier from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the overlay identifier found. If it could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +OverlayType CCINIClass::Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(OverlayTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * * + * Use this routine to store the overlay identifier into the INI database. * + * * + * INPUT: section -- Identifier for to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * value -- The overlay type value to store with the entry. * + * * + * OUTPUT: bool; Was the overlay value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_OverlayType(char const * section, char const * entry, OverlayType value) +{ + assert(value != OVERLAY_NONE); + return(Put_String(section, entry, OverlayTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * * + * Use this routine to fetch the bullet type identifier from the INI database. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default bullet type value to return if the entry could not be * + * located. * + * * + * OUTPUT: Returns with the bullet type identifier found. If the entry could not be found * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +BulletType CCINIClass::Get_BulletType(char const * section, char const * entry, BulletType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (BulletType proj = BULLET_FIRST; proj < BULLET_COUNT; proj++) { + if (stricmp(BulletTypeClass::As_Reference(proj).Name(), buffer) == 0) { +// if (stricmp(ProjectileNames[proj], buffer) == 0) { + return(proj); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * * + * This routine will store the projectile name (as the identifier) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry identifier to store the projectile value with. * + * * + * value -- The projectile identifier to store. * + * * + * OUTPUT: bool; Was the projectile identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_BulletType(char const * section, char const * entry, BulletType value) +{ + if (value == BULLET_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, BulletTypeClass::As_Reference(value).Name())); +// return(Put_String(section, entry, ProjectileNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * * + * Use this routine to fetch an individual house identifier from the INI database. This is * + * somewhat similar to the Get_Owners function but is limited to a single house. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the house identifier if it was found. If not found, then the default * + * value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +HousesType CCINIClass::Get_HousesType(char const * section, char const * entry, HousesType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(HouseTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * * + * Use this routine to store the specified house identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * value -- The house identifier to store in the database. * + * * + * OUTPUT: bool; Was the house identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_HousesType(char const * section, char const * entry, HousesType value) +{ + return(Put_String(section, entry, HouseTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * * + * Fetches the VQ movie name (identifier) from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the VQ movie identifier found. If the entry could not be located, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VQType CCINIClass::Get_VQType(char const * section, char const * entry, VQType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (VQType vq = VQ_FIRST; vq < VQ_COUNT; vq++) { + if (stricmp(buffer, VQName[vq]) == 0) { + return(vq); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * * + * Use this routine to store the VQ movie identifier into the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The VQ movie identifier to store to the INI database. * + * * + * OUTPUT: bool; Was the VQ identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VQType(char const * section, char const * entry, VQType value) +{ + if (value == VQ_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, VQName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * * + * This will fetch the theater identifier from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the theater type found. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TheaterType CCINIClass::Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theater_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * * + * Use this routine to store the theater name to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The theater identifier to store. * + * * + * OUTPUT: bool; Was the theater identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TheaterType(char const * section, char const * entry, TheaterType value) +{ + return(Put_String(section, entry, Theaters[value].Name)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * * + * This routine will fetch the trigger type identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * OUTPUT: Returns with the trigger type pointer if a match was found. No match found will * + * return a NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * CCINIClass::Get_TriggerType(char const * section, char const * entry) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TriggerTypeClass::From_Name(buffer)); + } + return(NULL); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * * + * This routine will store the trigger (as its name) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry name to store the trigger identifier to. * + * * + * value -- The trigger type to store. The trigger name will be stored. * + * * + * OUTPUT: bool; Was the trigger name stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value) +{ + return(Put_String(section, entry, value->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * * + * This routine will fetch the theme identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * defvalue -- The default theme identifier to return if the entry could not be found.* + * * + * OUTPUT: Returns with the theme identifier if it was found. If not found, then the default * + * value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ThemeType CCINIClass::Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theme.From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * * + * This routine will store the specified theme identifier to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier for the entry to store the value to. * + * * + * value -- The theme identifier to store. * + * * + * OUTPUT: bool; Was the theme identifier stored. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ThemeType(char const * section, char const * entry, ThemeType value) +{ + return(Put_String(section, entry, Theme.Base_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * * + * This routine will fetch the source (reinforcement edge) identifier from the INI * + * database. * + * * + * INPUT: section -- Identifier for the section that the entry will be searched under. * + * * + * entry -- Identifier for the entry that will be searched for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the source type of the entry if found. If not found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +SourceType CCINIClass::Get_SourceType(char const * section, char const * entry, SourceType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Source_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * * + * This will store the source type (reinforcement edge) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier of the entry to store the source identifier to. * + * * + * value -- The source (edge) value to store. * + * * + * OUTPUT: bool; Was the source identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_SourceType(char const * section, char const * entry, SourceType value) +{ + return(Put_String(section, entry, SourceName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * * + * This will return with the crate type specified in the INI database. * + * * + * INPUT: section -- Identifier for the section to search under. * + * * + * entry -- The entry to find the matching crate value for. * + * * + * defvalue -- The default crate value to return if the entry could not be found. * + * * + * OUTPUT: Returns with the crate type identified with the specified entry. If the entry * + * could not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +CrateType CCINIClass::Get_CrateType(char const * section, char const * entry, CrateType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Crate_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * * + * This will store the specified crate value to the section and entry specified. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the crate value with. * + * * + * value -- The crate value to store. * + * * + * OUTPUT: bool; Was the crate value stored to the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_CrateType(char const * section, char const * entry, CrateType value) +{ + return(Put_String(section, entry, CrateNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * * + * Fetches the terrain type number from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the terrain type if found. If the entry wasn't found, then the * + * default value will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TerrainType CCINIClass::Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TerrainTypeClass::From_Name(strtok(buffer, ","))); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * * + * This will store the terrain type identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier that the terrain number will be stored to. * + * * + * value -- The terrain type identifier to store. * + * * + * OUTPUT: bool; Was the terrain identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TerrainType(char const * section, char const * entry, TerrainType value) +{ + return(Put_String(section, entry, TerrainTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * * + * This routing will fetch the a list of buildings from the INI database. The buildings * + * are expressed as a comma separated list of building identifiers. The return value is * + * a composite of bits that represent these buildings -- one bit per building type. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to fetch the building list from. * + * * + * defvalue -- The default value to return if the section and entry could not be * + * located. * + * * + * OUTPUT: Returns with the building list (as a bitfield). If the entry could not be * + * found, the the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Buildings(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long pre; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + pre = 0; + char * token = strtok(buffer, ","); + while (token != NULL && *token != '\0') { + StructType building = BuildingTypeClass::From_Name(token); + if (building != STRUCT_NONE) { + pre |= (1L << building); + } + token = strtok(NULL, ","); + } + } else { + pre = defvalue; + } + + return(pre); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * * + * This will store a list of buildings to the INI database. The buildings are listed by * + * their identifier names separated by commas. * + * * + * INPUT: section -- The identifier for the section to store the entry under. * + * * + * entry -- The entry to store the building list to. * + * * + * value -- A list of buildings (in the form of a bit field -- one bit per * + * building type). * + * * + * OUTPUT: Was the building list stored to the INI file? * + * * + * WARNINGS: This is limited to the buildings that can be expressed in a bitfield long. * + * Which means, there can be only a maximum of 32 building types listed and * + * even then, the total line length generated must not exceed 128 bytes. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Buildings(char const * section, char const * entry, long value) +{ + char buffer[128] = ""; + int maxi = (32 < STRUCT_COUNT) ? 32 : STRUCT_COUNT; + + for (StructType index = STRUCT_FIRST; index < maxi; index++) { + if ((value & (1L << index)) != 0) { + + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, BuildingTypeClass::As_Reference(index).IniName); + } + } + + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * * + * This is a shorthand version of the message digest. It calculates the ID number from the * + * message digest itself. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a 32 bit unique identifier number for the INI database. * + * * + * WARNINGS: Since the return value is only 32 bits, it is much less secure than the * + * complete message digest. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +int CCINIClass::Get_Unique_ID(void) const +{ + if (!IsDigestPresent) { + ((CCINIClass *)this)->Calculate_Message_Digest(); + } + + return(CRCEngine()(&Digest[0], sizeof(Digest))); +} + + +/*********************************************************************************************** + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current database * + * * + * This will calculate a new message digest according to the current state of the INI * + * database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If the database is changed in any fashion, this message digest will be rendered * + * obsolete. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Calculate_Message_Digest(void) +{ + /* + ** Calculate the message digest for the INI data that was read. + */ + SHAPipe sha; + INIClass::Save(sha); + sha.Result(Digest); + IsDigestPresent = true; +} + + +/*********************************************************************************************** + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * * + * This flags the message digest as being invalid so that it will be recalculated when * + * needed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Invalidate_Message_Digest(void) +{ + IsDigestPresent = false; +} diff --git a/REDALERT/CCINI.H b/REDALERT/CCINI.H new file mode 100644 index 000000000..bfec980a0 --- /dev/null +++ b/REDALERT/CCINI.H @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCINI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCINI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : May 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCINI_H +#define CCINI_H + +#include "ini.h" +#include "fixed.h" +#include "pk.h" + +class TriggerTypeClass; + +/* +** The advanced version of the INI database manager. It handles the C&C expansion types and +** identifiers. In addition, it automatically stores a message digest with the INI data +** so that verification can occur. +*/ +class CCINIClass : public INIClass +{ + public: + CCINIClass(void) : IsDigestPresent(false) {} + + bool Load(FileClass & file, bool withdigest); + int Load(Straw & file, bool withdigest); + int Save(FileClass & file, bool withdigest) const; + int Save(Pipe & pipe, bool withdigest) const; + + long Get_Buildings(char const * section, char const * entry, long defvalue) const; + UnitType Get_UnitType(char const * section, char const * entry, UnitType defvalue) const; + AnimType Get_AnimType(char const * section, char const * entry, AnimType defvalue) const; + ArmorType Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const; + BulletType Get_BulletType(char const * section, char const * entry, BulletType defvalue) const; + HousesType Get_HousesType(char const * section, char const * entry, HousesType defvalue) const; + LEPTON Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const; + MPHType Get_MPHType(char const * section, char const * entry, MPHType defvalue) const; + OverlayType Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const; + SourceType Get_SourceType(char const * section, char const * entry, SourceType defvalue) const; + TerrainType Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const; + TheaterType Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const; + ThemeType Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const; + TriggerTypeClass * Get_TriggerType(char const * section, char const * entry) const; + VQType Get_VQType(char const * section, char const * entry, VQType defvalue) const; + VocType Get_VocType(char const * section, char const * entry, VocType defvalue) const; + WarheadType Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const; + WeaponType Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const; + long Get_Owners(char const * section, char const * entry, long defvalue) const; + CrateType Get_CrateType(char const * section, char const * entry, CrateType defvalue) const; + + + bool Put_Buildings(char const * section, char const * entry, long value); + bool Put_AnimType(char const * section, char const * entry, AnimType value); + bool Put_UnitType(char const * section, char const * entry, UnitType value); + bool Put_ArmorType(char const * section, char const * entry, ArmorType value); + bool Put_BulletType(char const * section, char const * entry, BulletType value); + bool Put_HousesType(char const * section, char const * entry, HousesType value); + bool Put_Lepton(char const * section, char const * entry, LEPTON value); + bool Put_MPHType(char const * section, char const * entry, MPHType value); + bool Put_VQType(char const * section, char const * entry, VQType value); + bool Put_OverlayType(char const * section, char const * entry, OverlayType value); + bool Put_Owners(char const * section, char const * entry, long value); + bool Put_SourceType(char const * section, char const * entry, SourceType value); + bool Put_TerrainType(char const * section, char const * entry, TerrainType value); + bool Put_TheaterType(char const * section, char const * entry, TheaterType value); + bool Put_ThemeType(char const * section, char const * entry, ThemeType value); + bool Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value); + bool Put_VocType(char const * section, char const * entry, VocType value); + bool Put_WarheadType(char const * section, char const * entry, WarheadType value); + bool Put_WeaponType(char const * section, char const * entry, WeaponType value); + bool Put_CrateType(char const * section, char const * entry, CrateType value); + + int Get_Unique_ID(void) const; + + private: + void Calculate_Message_Digest(void); + void Invalidate_Message_Digest(void); + + bool IsDigestPresent:1; + + /* + ** This is the message digest (SHA) of the INI database that was embedded as part of + ** the INI file. + */ + unsigned char Digest[20]; +}; + +#endif diff --git a/REDALERT/CCMPATH.CPP b/REDALERT/CCMPATH.CPP new file mode 100644 index 000000000..4392fc5a6 --- /dev/null +++ b/REDALERT/CCMPATH.CPP @@ -0,0 +1,418 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCMPATH.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : January 11, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_MPATH -- Performs MPATH-specific initialization * + * Shutdown_MPATH -- Shuts down MPATH connections * + * Connect_MPATH -- Waits for connections to other players * + * Destroy_MPATH_Connection -- Destroys the given connection * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + + +/*************************************************************************** + * Init_MPATH -- Performs MPATH-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_MPATH(void) +{ +#if(MPATH) + //------------------------------------------------------------------------ + // Allocate a packet buffer for MPATH's use + //------------------------------------------------------------------------ + Session.MPathPacket = new char[Session.MPathSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_MPATH_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + MPath->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_MPATH(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_MPATH + + +/*************************************************************************** + * Shutdown_MPATH -- Shuts down MPATH connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_MPATH(void) +{ +#if(MPATH) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.MPathPacket) { + delete [] Session.MPathPacket; + Session.MPathPacket = NULL; + } + + if (MPath) { + delete MPath; + MPath = NULL; + } + + return; + +#endif +} // end of Shutdown_MPATH + + +/*************************************************************************** + * Connect_MPATH -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_MPATH(void) +{ +#if(MPATH) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = MPath->Find_Num_Connections(); + num_found = 0; + Session.NumPlayers = num_players + 1; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + MPath->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (MPath->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->MPathAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->MPathAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_MPATH_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_MPATH_Connection(int id, int error) +{ +#if(MPATH) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),MPath->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),MPath->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the MPATH connection + ------------------------------------------------------------------------*/ + MPath->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_MPATH_Connection + + +/***************************** end of ccmpath.cpp **************************/ diff --git a/REDALERT/CCPTR.CPP b/REDALERT/CCPTR.CPP new file mode 100644 index 000000000..fa3021d89 --- /dev/null +++ b/REDALERT/CCPTR.CPP @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCPTR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCPTR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCPtr::operator > -- Greater than comparison operator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + + +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; + + +template FixedIHeapClass* CCPtr::Heap = NULL; + + +/* +** These member functions for the CCPtr class cannot be declared inside the +** class definition since they could refer to other objects that themselves +** contain CCPtr objects. The recursive nature of this type of declaration +** is not handled by Watcom, hence the body declaration is dislocated here. +*/ +template +CCPtr::CCPtr(T * ptr) : ID(-1) +{ + if (ptr != NULL) { + ID = ptr->ID; + } +} + + +/*********************************************************************************************** + * CCPtr::operator > -- Greater than comparison operator. * + * * + * This will compare two pointer value to see if the left hand value is greater than the * + * right hand. The values are compared by comparing based on their Name() functions. * + * * + * INPUT: rvalue -- The right handle CCPtr value. * + * * + * OUTPUT: Is the left hand value greater than the right hand value? * + * * + * WARNINGS: The values pointed to by CCPtr must have a Name() function defined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +bool CCPtr::operator > (CCPtr const & rvalue) const +{ + return (stricmp((*this)->Name(), rvalue->Name()) > 0); +} \ No newline at end of file diff --git a/REDALERT/CCPTR.H b/REDALERT/CCPTR.H new file mode 100644 index 000000000..2b5141d82 --- /dev/null +++ b/REDALERT/CCPTR.H @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCPTR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCPTR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : June 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCPTR_H +#define CCPTR_H + +/* +** The CCPtr class is designed for a specific purpose. It functions like a pointer except that +** it requires no fixups for saving and loading. If pointer fixups are not an issue, than using +** regular pointers would be more efficient. +*/ +template +class CCPtr +{ + public: + CCPtr(void) : ID(-1) {}; + CCPtr(NoInitClass const & ) {}; + CCPtr(T * ptr); + + operator T * (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + T & operator * (void) const { + assert(Heap != NULL && ID < Heap->Length()); + return(*(T*)(*Heap)[ID]); + } + T * operator -> (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + + bool Is_Valid(void) const {return(ID != -1);} + + bool operator == (CCPtr const & rvalue) const {return(ID == rvalue.ID);} + bool operator != (CCPtr const & rvalue) const {return(ID != rvalue.ID);} + bool operator > (CCPtr const & rvalue) const; + bool operator <= (CCPtr const & rvalue) const {return (rvalue > *this);} + bool operator < (CCPtr const & rvalue) const {return (*this != rvalue && rvalue > *this);} + bool operator >= (CCPtr const & rvalue) const {return (*this == rvalue || rvalue > *this);} + + long Raw(void) const {return(ID);} + void Set_Raw(long value) {ID = value;} + + static void Set_Heap(FixedIHeapClass *heap) {Heap = heap;} + + private: + + static FixedIHeapClass * Heap; + + /* + ** This is the ID number of the object it refers to. By using an ID number, this class can + ** be saved and loaded without pointer fixups. + */ + int ID; +}; + +/* +** These template helper functions tell the compiler what to do in the +** ambiguous case of a CCPtr on one side and a regular type pointer on the +** other side. In such a case the compiler could create a temp CCPtr object +** OR call the conversion operator on the existing CCPtr object. Either way +** is technically valid, but the compiler doesn't know which is better so it +** generates an error. These routines force the conversion operator rather than +** creating a temporary object. This presumes that the conversion operator is +** cheaper than constructing a temporary and that cheaper solutions are desirable. +*/ +template +int operator == (CCPtr & lvalue, T * rvalue) +{ + return((T*)lvalue == rvalue); +} + +template +int operator == (T * lvalue, CCPtr & rvalue) +{ + return(lvalue == (T*)rvalue); +} + +#endif \ No newline at end of file diff --git a/REDALERT/CCTEN.CPP b/REDALERT/CCTEN.CPP new file mode 100644 index 000000000..a919246fe --- /dev/null +++ b/REDALERT/CCTEN.CPP @@ -0,0 +1,600 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCTEN.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : November 27, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_TEN -- Performs TEN-specific initialization * + * Shutdown_TEN -- Shuts down TEN connections * + * Connect_TEN -- Waits for connections to other players * + * Destroy_TEN_Connection -- Destroys the given connection * + * Debug_Mono -- Custom mono prints * + * Send_TEN_Win_Packet -- Sends a win packet to server * + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#if(TEN) +#ifdef WIN32 +#define WINDOWS +#endif +#include "ten.h" +#endif + +void Connect_TEN(void); +void Debug_Mono(void); + +/*************************************************************************** + * Init_TEN -- Performs TEN-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_TEN(void) +{ +#if(TEN) + //------------------------------------------------------------------------ + // Allocate a packet buffer for TEN's use + //------------------------------------------------------------------------ + Session.TenPacket = new char[Session.TenSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_TEN_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + Ten->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_TEN(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_TEN + + +/*************************************************************************** + * Shutdown_TEN -- Shuts down TEN connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_TEN(void) +{ +#if(TEN) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.TenPacket) { + delete [] Session.TenPacket; + Session.TenPacket = NULL; + } + + if (Ten) { + delete Ten; + Ten = NULL; + } + + return; + +#endif +} // end of Shutdown_TEN + + +/*************************************************************************** + * Connect_TEN -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * MPlayerCount must have been initialized at this point. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_TEN(void) +{ +#if(TEN) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = Session.NumPlayers - 1; + num_found = 0; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + Ten->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (Ten->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->TenAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->TenAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_TEN_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy (must be a HousesType) * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_TEN_Connection(int id, int error) +{ +#if(TEN) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ten->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ten->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the TEN connection + ------------------------------------------------------------------------*/ + Ten->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_TEN_Connection + + +/*************************************************************************** + * Debug_Mono -- Custom mono prints * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Debug_Mono(void) +{ +#if(TEN) + int i; + int id; + + Mono_Printf("STATE: # Connections:%d\n",Ten->Num_Connections()); + for (i=0;iNum_Connections();i++) { + id = Ten->Connection_ID(i); + Mono_Printf("Connection %d: Name:%s, ID:%d, Address:%d\n", + i, + Ten->Connection_Name(id), + Ten->Connection_ID(i), + Ten->Connection_Address(id)); + } + +#endif +} + + +/*************************************************************************** + * Send_TEN_Win_Packet -- Sends a win packet to server * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Win_Packet(void) +{ +#if(TEN) + char winbuf[80]; + char idbuf[20]; + int first = 1; + HouseClass *hptr; + int i; + + // + // Build a special text buffer to send to the TEN server. Format: + // "winner 'id id'", where 'id' is the Ten Player ID of each player + // on the winning team. (For TEN, the color index is the player ID.) + // + sprintf(winbuf,"winner '"); + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + if (!first) { + strcat(winbuf," "); + } else { + first = 0; + } + sprintf(idbuf,"%d", Session.Players[i]->Player.Color); + strcat (winbuf, idbuf); + } + } + strcat (winbuf,"' "); + tenArSetPlayerState(winbuf); +#endif // TEN + +} // end of Send_TEN_Win_Packet + + +/*************************************************************************** + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * * + * INPUT: * + * whom name of player we're allying / enemying with * + * ally 1 = we're allying; 0 = we're breaking the alliance * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Alliance(char *whom, int ally) +{ +#if(TEN) + char buf[80]; + + if (ally) { + sprintf(buf,"ally '%s' ",whom); + } else { + sprintf(buf,"enemy '%s' ",whom); + } + + tenArSetPlayerState(buf); + +#else + whom = whom; + ally = ally; +#endif // TEN + +} // end of Send_TEN_Alliance + + +/*************************************************************************** + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Out_Of_Sync(void) +{ +#if(TEN) + tenArSetPlayerState("sync '1' "); +#endif // TEN + +} // end of Send_TEN_Out_Of_Sync + + +/*************************************************************************** + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Packet_Too_Late(void) +{ +#if(TEN) + tenArSetPlayerState("toolate '1' "); +#endif // TEN + +} // end of Send_TEN_Packet_Too_Late + + +/***************************** end of ccten.cpp *****************************/ diff --git a/REDALERT/CDATA.CPP b/REDALERT/CDATA.CPP new file mode 100644 index 000000000..572246acc --- /dev/null +++ b/REDALERT/CDATA.CPP @@ -0,0 +1,3321 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static TemplateTypeClass const Empty( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Clear( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Road01( + TEMPLATE_ROAD01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D01", + TXT_ROAD +); +static TemplateTypeClass const Road02( + TEMPLATE_ROAD02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D02", + TXT_ROAD +); +static TemplateTypeClass const Road03( + TEMPLATE_ROAD03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D03", + TXT_ROAD +); +static TemplateTypeClass const Road04( + TEMPLATE_ROAD04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D04", + TXT_ROAD +); +static TemplateTypeClass const Road05( + TEMPLATE_ROAD05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D05", + TXT_ROAD +); +static TemplateTypeClass const Road06( + TEMPLATE_ROAD06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D06", + TXT_ROAD +); +static TemplateTypeClass const Road07( + TEMPLATE_ROAD07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D07", + TXT_ROAD +); +static TemplateTypeClass const Road08( + TEMPLATE_ROAD08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D08", + TXT_ROAD +); +static TemplateTypeClass const Road09( + TEMPLATE_ROAD09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D09", + TXT_ROAD +); +static TemplateTypeClass const Road10( + TEMPLATE_ROAD10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D10", + TXT_ROAD +); +static TemplateTypeClass const Road11( + TEMPLATE_ROAD11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D11", + TXT_ROAD +); +static TemplateTypeClass const Road12( + TEMPLATE_ROAD12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D12", + TXT_ROAD +); +static TemplateTypeClass const Road13( + TEMPLATE_ROAD13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D13", + TXT_ROAD +); +static TemplateTypeClass const Road14( + TEMPLATE_ROAD14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D14", + TXT_ROAD +); +static TemplateTypeClass const Road15( + TEMPLATE_ROAD15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D15", + TXT_ROAD +); +static TemplateTypeClass const Road16( + TEMPLATE_ROAD16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D16", + TXT_ROAD +); +static TemplateTypeClass const Road17( + TEMPLATE_ROAD17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D17", + TXT_ROAD +); +static TemplateTypeClass const Road18( + TEMPLATE_ROAD18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D18", + TXT_ROAD +); +static TemplateTypeClass const Road19( + TEMPLATE_ROAD19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D19", + TXT_ROAD +); +static TemplateTypeClass const Road20( + TEMPLATE_ROAD20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D20", + TXT_ROAD +); +static TemplateTypeClass const Road21( + TEMPLATE_ROAD21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D21", + TXT_ROAD +); +static TemplateTypeClass const Road22( + TEMPLATE_ROAD22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D22", + TXT_ROAD +); +static TemplateTypeClass const Road23( + TEMPLATE_ROAD23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D23", + TXT_ROAD +); +static TemplateTypeClass const Road24( + TEMPLATE_ROAD24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D24", + TXT_ROAD +); +static TemplateTypeClass const Road25( + TEMPLATE_ROAD25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D25", + TXT_ROAD +); +static TemplateTypeClass const Road26( + TEMPLATE_ROAD26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D26", + TXT_ROAD +); +static TemplateTypeClass const Road27( + TEMPLATE_ROAD27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D27", + TXT_ROAD +); +static TemplateTypeClass const Road28( + TEMPLATE_ROAD28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D28", + TXT_ROAD +); +static TemplateTypeClass const Road29( + TEMPLATE_ROAD29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D29", + TXT_ROAD +); +static TemplateTypeClass const Road30( + TEMPLATE_ROAD30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D30", + TXT_ROAD +); +static TemplateTypeClass const Road31( + TEMPLATE_ROAD31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D31", + TXT_ROAD +); +static TemplateTypeClass const Road32( + TEMPLATE_ROAD32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D32", + TXT_ROAD +); +static TemplateTypeClass const Road33( + TEMPLATE_ROAD33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D33", + TXT_ROAD +); +static TemplateTypeClass const Road34( + TEMPLATE_ROAD34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D34", + TXT_ROAD +); +static TemplateTypeClass const Road35( + TEMPLATE_ROAD35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D35", + TXT_ROAD +); +static TemplateTypeClass const Road36( + TEMPLATE_ROAD36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D36", + TXT_ROAD +); +static TemplateTypeClass const Road37( + TEMPLATE_ROAD37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D37", + TXT_ROAD +); +static TemplateTypeClass const Road38( + TEMPLATE_ROAD38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D38", + TXT_ROAD +); +static TemplateTypeClass const Road39( + TEMPLATE_ROAD39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D39", + TXT_ROAD +); +static TemplateTypeClass const Road40( + TEMPLATE_ROAD40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D40", + TXT_ROAD +); +static TemplateTypeClass const Road41( + TEMPLATE_ROAD41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D41", + TXT_ROAD +); +static TemplateTypeClass const Road42( + TEMPLATE_ROAD42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D42", + TXT_ROAD +); +static TemplateTypeClass const Road43( + TEMPLATE_ROAD43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D43", + TXT_ROAD +); +static TemplateTypeClass const Road44( + TEMPLATE_ROAD44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D44", + TXT_ROAD +); +static TemplateTypeClass const Road45( + TEMPLATE_ROAD45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D45", + TXT_ROAD +); +static TemplateTypeClass const Water( + TEMPLATE_WATER, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W1", + TXT_WATER +); +static TemplateTypeClass const Water2( + TEMPLATE_WATER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W2", + TXT_WATER +); +static TemplateTypeClass const Shore01( + TEMPLATE_SHORE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH01", + TXT_SHORE +); +static TemplateTypeClass const Shore02( + TEMPLATE_SHORE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH02", + TXT_SHORE +); +static TemplateTypeClass const Shore03( + TEMPLATE_SHORE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH03", + TXT_SHORE +); +static TemplateTypeClass const Shore04( + TEMPLATE_SHORE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH04", + TXT_SHORE +); +static TemplateTypeClass const Shore05( + TEMPLATE_SHORE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH05", + TXT_SHORE +); +static TemplateTypeClass const Shore06( + TEMPLATE_SHORE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH06", + TXT_SHORE +); +static TemplateTypeClass const Shore07( + TEMPLATE_SHORE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH07", + TXT_SHORE +); +static TemplateTypeClass const Shore08( + TEMPLATE_SHORE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH08", + TXT_SHORE +); +static TemplateTypeClass const Shore09( + TEMPLATE_SHORE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH09", + TXT_SHORE +); +static TemplateTypeClass const Shore10( + TEMPLATE_SHORE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH10", + TXT_SHORE +); +static TemplateTypeClass const Shore11( + TEMPLATE_SHORE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH11", + TXT_SHORE +); +static TemplateTypeClass const Shore12( + TEMPLATE_SHORE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH12", + TXT_SHORE +); +static TemplateTypeClass const Shore13( + TEMPLATE_SHORE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH13", + TXT_SHORE +); +static TemplateTypeClass const Shore14( + TEMPLATE_SHORE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH14", + TXT_SHORE +); +static TemplateTypeClass const Shore15( + TEMPLATE_SHORE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH15", + TXT_SHORE +); +static TemplateTypeClass const Shore16( + TEMPLATE_SHORE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH16", + TXT_SHORE +); +static TemplateTypeClass const Shore17( + TEMPLATE_SHORE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH17", + TXT_SHORE +); +static TemplateTypeClass const Shore18( + TEMPLATE_SHORE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH18", + TXT_SHORE +); +static TemplateTypeClass const Shore19( + TEMPLATE_SHORE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH19", + TXT_SHORE +); +static TemplateTypeClass const Shore20( + TEMPLATE_SHORE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH20", + TXT_SHORE +); +static TemplateTypeClass const Shore21( + TEMPLATE_SHORE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH21", + TXT_SHORE +); +static TemplateTypeClass const Shore22( + TEMPLATE_SHORE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH22", + TXT_SHORE +); +static TemplateTypeClass const Shore23( + TEMPLATE_SHORE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH23", + TXT_SHORE +); +static TemplateTypeClass const Shore24( + TEMPLATE_SHORE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH24", + TXT_SHORE +); +static TemplateTypeClass const Shore25( + TEMPLATE_SHORE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH25", + TXT_SHORE +); +static TemplateTypeClass const Shore26( + TEMPLATE_SHORE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH26", + TXT_SHORE +); +static TemplateTypeClass const Shore27( + TEMPLATE_SHORE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH27", + TXT_SHORE +); +static TemplateTypeClass const Shore28( + TEMPLATE_SHORE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH28", + TXT_SHORE +); +static TemplateTypeClass const Shore29( + TEMPLATE_SHORE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH29", + TXT_SHORE +); +static TemplateTypeClass const Shore30( + TEMPLATE_SHORE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH30", + TXT_SHORE +); +static TemplateTypeClass const Shore31( + TEMPLATE_SHORE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH31", + TXT_SHORE +); +static TemplateTypeClass const Shore32( + TEMPLATE_SHORE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH32", + TXT_SHORE +); +static TemplateTypeClass const Shore33( + TEMPLATE_SHORE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH33", + TXT_SHORE +); +static TemplateTypeClass const Shore34( + TEMPLATE_SHORE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH34", + TXT_SHORE +); +static TemplateTypeClass const Shore35( + TEMPLATE_SHORE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH35", + TXT_SHORE +); +static TemplateTypeClass const Shore36( + TEMPLATE_SHORE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH36", + TXT_SHORE +); +static TemplateTypeClass const Shore37( + TEMPLATE_SHORE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH37", + TXT_SHORE +); +static TemplateTypeClass const Shore38( + TEMPLATE_SHORE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH38", + TXT_SHORE +); +static TemplateTypeClass const Shore39( + TEMPLATE_SHORE39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH39", + TXT_SHORE +); +static TemplateTypeClass const Shore40( + TEMPLATE_SHORE40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH40", + TXT_SHORE +); +static TemplateTypeClass const Shore41( + TEMPLATE_SHORE41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH41", + TXT_SHORE +); +static TemplateTypeClass const Shore42( + TEMPLATE_SHORE42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH42", + TXT_SHORE +); +static TemplateTypeClass const Shore43( + TEMPLATE_SHORE43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH43", + TXT_SHORE +); +static TemplateTypeClass const Shore44( + TEMPLATE_SHORE44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH44", + TXT_SHORE +); +static TemplateTypeClass const Shore45( + TEMPLATE_SHORE45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH45", + TXT_SHORE +); +static TemplateTypeClass const Shore46( + TEMPLATE_SHORE46, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH46", + TXT_SHORE +); +static TemplateTypeClass const Shore47( + TEMPLATE_SHORE47, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH47", + TXT_SHORE +); +static TemplateTypeClass const Shore48( + TEMPLATE_SHORE48, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH48", + TXT_SHORE +); +static TemplateTypeClass const Shore49( + TEMPLATE_SHORE49, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH49", + TXT_SHORE +); +static TemplateTypeClass const Shore50( + TEMPLATE_SHORE50, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH50", + TXT_SHORE +); +static TemplateTypeClass const Shore51( + TEMPLATE_SHORE51, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH51", + TXT_SHORE +); +static TemplateTypeClass const Shore52( + TEMPLATE_SHORE52, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH52", + TXT_SHORE +); +static TemplateTypeClass const Shore53( + TEMPLATE_SHORE53, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH53", + TXT_SHORE +); +static TemplateTypeClass const Shore54( + TEMPLATE_SHORE54, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH54", + TXT_SHORE +); +static TemplateTypeClass const Shore55( + TEMPLATE_SHORE55, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH55", + TXT_SHORE +); +static TemplateTypeClass const Shore56( + TEMPLATE_SHORE56, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH56", + TXT_SHORE +); +static TemplateTypeClass const Boulder1( + TEMPLATE_BOULDER1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B1", + TXT_SLOPE +); +static TemplateTypeClass const Boulder2( + TEMPLATE_BOULDER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B2", + TXT_SLOPE +); +static TemplateTypeClass const Boulder3( + TEMPLATE_BOULDER3, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B3", + TXT_SLOPE +); +static TemplateTypeClass const Boulder4( + TEMPLATE_BOULDER4, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B4", + TXT_SLOPE +); +static TemplateTypeClass const Boulder5( + TEMPLATE_BOULDER5, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B5", + TXT_SLOPE +); +static TemplateTypeClass const Boulder6( + TEMPLATE_BOULDER6, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B6", + TXT_SLOPE +); +static TemplateTypeClass const Slope01( + TEMPLATE_SLOPE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S01", + TXT_SLOPE +); +static TemplateTypeClass const Slope02( + TEMPLATE_SLOPE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S02", + TXT_SLOPE +); +static TemplateTypeClass const Slope03( + TEMPLATE_SLOPE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S03", + TXT_SLOPE +); +static TemplateTypeClass const Slope04( + TEMPLATE_SLOPE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S04", + TXT_SLOPE +); +static TemplateTypeClass const Slope05( + TEMPLATE_SLOPE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S05", + TXT_SLOPE +); +static TemplateTypeClass const Slope06( + TEMPLATE_SLOPE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S06", + TXT_SLOPE +); +static TemplateTypeClass const Slope07( + TEMPLATE_SLOPE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S07", + TXT_SLOPE +); +static TemplateTypeClass const Slope08( + TEMPLATE_SLOPE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S08", + TXT_SLOPE +); +static TemplateTypeClass const Slope09( + TEMPLATE_SLOPE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S09", + TXT_SLOPE +); +static TemplateTypeClass const Slope10( + TEMPLATE_SLOPE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S10", + TXT_SLOPE +); +static TemplateTypeClass const Slope11( + TEMPLATE_SLOPE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S11", + TXT_SLOPE +); +static TemplateTypeClass const Slope12( + TEMPLATE_SLOPE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S12", + TXT_SLOPE +); +static TemplateTypeClass const Slope13( + TEMPLATE_SLOPE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S13", + TXT_SLOPE +); +static TemplateTypeClass const Slope14( + TEMPLATE_SLOPE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S14", + TXT_SLOPE +); +static TemplateTypeClass const Slope15( + TEMPLATE_SLOPE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S15", + TXT_SLOPE +); +static TemplateTypeClass const Slope16( + TEMPLATE_SLOPE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S16", + TXT_SLOPE +); +static TemplateTypeClass const Slope17( + TEMPLATE_SLOPE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S17", + TXT_SLOPE +); +static TemplateTypeClass const Slope18( + TEMPLATE_SLOPE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S18", + TXT_SLOPE +); +static TemplateTypeClass const Slope19( + TEMPLATE_SLOPE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S19", + TXT_SLOPE +); +static TemplateTypeClass const Slope20( + TEMPLATE_SLOPE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S20", + TXT_SLOPE +); +static TemplateTypeClass const Slope21( + TEMPLATE_SLOPE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S21", + TXT_SLOPE +); +static TemplateTypeClass const Slope22( + TEMPLATE_SLOPE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S22", + TXT_SLOPE +); +static TemplateTypeClass const Slope23( + TEMPLATE_SLOPE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S23", + TXT_SLOPE +); +static TemplateTypeClass const Slope24( + TEMPLATE_SLOPE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S24", + TXT_SLOPE +); +static TemplateTypeClass const Slope25( + TEMPLATE_SLOPE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S25", + TXT_SLOPE +); +static TemplateTypeClass const Slope26( + TEMPLATE_SLOPE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S26", + TXT_SLOPE +); +static TemplateTypeClass const Slope27( + TEMPLATE_SLOPE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S27", + TXT_SLOPE +); +static TemplateTypeClass const Slope28( + TEMPLATE_SLOPE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S28", + TXT_SLOPE +); +static TemplateTypeClass const Slope29( + TEMPLATE_SLOPE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S29", + TXT_SLOPE +); +static TemplateTypeClass const Slope30( + TEMPLATE_SLOPE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S30", + TXT_SLOPE +); +static TemplateTypeClass const Slope31( + TEMPLATE_SLOPE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S31", + TXT_SLOPE +); +static TemplateTypeClass const Slope32( + TEMPLATE_SLOPE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S32", + TXT_SLOPE +); +static TemplateTypeClass const Slope33( + TEMPLATE_SLOPE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S33", + TXT_SLOPE +); +static TemplateTypeClass const Slope34( + TEMPLATE_SLOPE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S34", + TXT_SLOPE +); +static TemplateTypeClass const Slope35( + TEMPLATE_SLOPE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S35", + TXT_SLOPE +); +static TemplateTypeClass const Slope36( + TEMPLATE_SLOPE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S36", + TXT_SLOPE +); +static TemplateTypeClass const Slope37( + TEMPLATE_SLOPE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S37", + TXT_SLOPE +); +static TemplateTypeClass const Slope38( + TEMPLATE_SLOPE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S38", + TXT_SLOPE +); +static TemplateTypeClass const Patch01( + TEMPLATE_PATCH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P01", + TXT_PATCH +); +static TemplateTypeClass const Patch02( + TEMPLATE_PATCH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P02", + TXT_PATCH +); +static TemplateTypeClass const Patch03( + TEMPLATE_PATCH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P03", + TXT_PATCH +); +static TemplateTypeClass const Patch04( + TEMPLATE_PATCH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P04", + TXT_PATCH +); +static TemplateTypeClass const Patch07( + TEMPLATE_PATCH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P07", + TXT_PATCH +); +static TemplateTypeClass const Patch08( + TEMPLATE_PATCH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P08", + TXT_PATCH +); +static TemplateTypeClass const Patch13( + TEMPLATE_PATCH13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P13", + TXT_PATCH +); +static TemplateTypeClass const Patch14( + TEMPLATE_PATCH14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P14", + TXT_PATCH +); +static TemplateTypeClass const Patch15( + TEMPLATE_PATCH15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P15", + TXT_PATCH +); +static TemplateTypeClass const River01( + TEMPLATE_RIVER01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV01", + TXT_RIVER +); +static TemplateTypeClass const River02( + TEMPLATE_RIVER02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV02", + TXT_RIVER +); +static TemplateTypeClass const River03( + TEMPLATE_RIVER03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV03", + TXT_RIVER +); +static TemplateTypeClass const River04( + TEMPLATE_RIVER04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV04", + TXT_RIVER +); +static TemplateTypeClass const River05( + TEMPLATE_RIVER05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV05", + TXT_RIVER +); +static TemplateTypeClass const River06( + TEMPLATE_RIVER06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV06", + TXT_RIVER +); +static TemplateTypeClass const River07( + TEMPLATE_RIVER07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV07", + TXT_RIVER +); +static TemplateTypeClass const River08( + TEMPLATE_RIVER08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV08", + TXT_RIVER +); +static TemplateTypeClass const River09( + TEMPLATE_RIVER09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV09", + TXT_RIVER +); +static TemplateTypeClass const River10( + TEMPLATE_RIVER10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV10", + TXT_RIVER +); +static TemplateTypeClass const River11( + TEMPLATE_RIVER11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV11", + TXT_RIVER +); +static TemplateTypeClass const River12( + TEMPLATE_RIVER12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV12", + TXT_RIVER +); +static TemplateTypeClass const River13( + TEMPLATE_RIVER13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV13", + TXT_RIVER +); +static TemplateTypeClass const River14( + TEMPLATE_RIVER14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV14", + TXT_RIVER +); +static TemplateTypeClass const River15( + TEMPLATE_RIVER15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV15", + TXT_RIVER +); +static TemplateTypeClass const Ford1( + TEMPLATE_FORD1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD1", + TXT_RIVER +); +static TemplateTypeClass const Ford2( + TEMPLATE_FORD2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD2", + TXT_RIVER +); +static TemplateTypeClass const Falls1( + TEMPLATE_FALLS1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1", + TXT_RIVER +); +static TemplateTypeClass const Falls1a( + TEMPLATE_FALLS1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1A", + TXT_RIVER +); +static TemplateTypeClass const Falls2( + TEMPLATE_FALLS2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2", + TXT_RIVER +); +static TemplateTypeClass const Falls2a( + TEMPLATE_FALLS2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2A", + TXT_RIVER +); +static TemplateTypeClass const Bridge1x( + TEMPLATE_BRIDGE1X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1( + TEMPLATE_BRIDGE1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1h( + TEMPLATE_BRIDGE1H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1d( + TEMPLATE_BRIDGE1D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2x( + TEMPLATE_BRIDGE2X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2( + TEMPLATE_BRIDGE2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2h( + TEMPLATE_BRIDGE2H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2d( + TEMPLATE_BRIDGE2D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1ax( + TEMPLATE_BRIDGE_1AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1a( + TEMPLATE_BRIDGE_1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1b( + TEMPLATE_BRIDGE_1B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1c( + TEMPLATE_BRIDGE_1C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2ax( + TEMPLATE_BRIDGE_2AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2a( + TEMPLATE_BRIDGE_2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2b( + TEMPLATE_BRIDGE_2B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2c( + TEMPLATE_BRIDGE_2C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3a( + TEMPLATE_BRIDGE_3A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3b( + TEMPLATE_BRIDGE_3B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3c( + TEMPLATE_BRIDGE_3C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3d( + TEMPLATE_BRIDGE_3D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3e( + TEMPLATE_BRIDGE_3E, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3E", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3f( + TEMPLATE_BRIDGE_3F, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3F", + TXT_BRIDGE +); +static TemplateTypeClass const ShoreCliff01( + TEMPLATE_SHORECLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC01", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff02( + TEMPLATE_SHORECLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC02", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff03( + TEMPLATE_SHORECLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC03", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff04( + TEMPLATE_SHORECLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC04", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff05( + TEMPLATE_SHORECLIFF05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC05", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff06( + TEMPLATE_SHORECLIFF06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC06", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff07( + TEMPLATE_SHORECLIFF07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC07", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff08( + TEMPLATE_SHORECLIFF08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC08", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff09( + TEMPLATE_SHORECLIFF09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC09", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff10( + TEMPLATE_SHORECLIFF10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC10", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff11( + TEMPLATE_SHORECLIFF11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC11", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff12( + TEMPLATE_SHORECLIFF12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC12", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff13( + TEMPLATE_SHORECLIFF13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC13", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff14( + TEMPLATE_SHORECLIFF14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC14", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff15( + TEMPLATE_SHORECLIFF15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC15", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff16( + TEMPLATE_SHORECLIFF16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC16", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff17( + TEMPLATE_SHORECLIFF17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC17", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff18( + TEMPLATE_SHORECLIFF18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC18", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff19( + TEMPLATE_SHORECLIFF19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC19", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff20( + TEMPLATE_SHORECLIFF20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC20", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff21( + TEMPLATE_SHORECLIFF21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC21", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff22( + TEMPLATE_SHORECLIFF22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC22", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff23( + TEMPLATE_SHORECLIFF23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC23", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff24( + TEMPLATE_SHORECLIFF24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC24", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff25( + TEMPLATE_SHORECLIFF25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC25", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff26( + TEMPLATE_SHORECLIFF26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC26", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff27( + TEMPLATE_SHORECLIFF27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC27", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff28( + TEMPLATE_SHORECLIFF28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC28", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff29( + TEMPLATE_SHORECLIFF29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC29", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff30( + TEMPLATE_SHORECLIFF30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC30", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff31( + TEMPLATE_SHORECLIFF31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC31", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff32( + TEMPLATE_SHORECLIFF32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC32", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff33( + TEMPLATE_SHORECLIFF33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC33", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff34( + TEMPLATE_SHORECLIFF34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC34", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff35( + TEMPLATE_SHORECLIFF35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC35", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff36( + TEMPLATE_SHORECLIFF36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC36", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff37( + TEMPLATE_SHORECLIFF37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC37", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff38( + TEMPLATE_SHORECLIFF38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC38", + TXT_SHORE +); +static TemplateTypeClass const Rough01( + TEMPLATE_ROUGH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF01", + TXT_ROCK +); +static TemplateTypeClass const Rough02( + TEMPLATE_ROUGH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF02", + TXT_ROCK +); +static TemplateTypeClass const Rough03( + TEMPLATE_ROUGH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF03", + TXT_ROCK +); +static TemplateTypeClass const Rough04( + TEMPLATE_ROUGH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF04", + TXT_ROCK +); +static TemplateTypeClass const Rough05( + TEMPLATE_ROUGH05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF05", + TXT_ROCK +); +static TemplateTypeClass const Rough06( + TEMPLATE_ROUGH06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF06", + TXT_ROCK +); +static TemplateTypeClass const Rough07( + TEMPLATE_ROUGH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF07", + TXT_ROCK +); +static TemplateTypeClass const Rough08( + TEMPLATE_ROUGH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF08", + TXT_ROCK +); +static TemplateTypeClass const Rough09( + TEMPLATE_ROUGH09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF09", + TXT_ROCK +); +static TemplateTypeClass const Rough10( + TEMPLATE_ROUGH10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF10", + TXT_ROCK +); +static TemplateTypeClass const Rough11( + TEMPLATE_ROUGH11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF11", + TXT_ROCK +); +static TemplateTypeClass const RiverCliff01( + TEMPLATE_RIVERCLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC01", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff02( + TEMPLATE_RIVERCLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC02", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff03( + TEMPLATE_RIVERCLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC03", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff04( + TEMPLATE_RIVERCLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC04", + TXT_RIVER +); + +static TemplateTypeClass const F01( + TEMPLATE_F01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F01", + TXT_RIVER +); +static TemplateTypeClass const F02( + TEMPLATE_F02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F02", + TXT_RIVER +); +static TemplateTypeClass const F03( + TEMPLATE_F03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F03", + TXT_RIVER +); +static TemplateTypeClass const F04( + TEMPLATE_F04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F04", + TXT_RIVER +); +static TemplateTypeClass const F05( + TEMPLATE_F05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F05", + TXT_RIVER +); +static TemplateTypeClass const F06( + TEMPLATE_F06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F06", + TXT_RIVER +); + +static TemplateTypeClass const ARRO0001( + TEMPLATE_ARRO0001, + THEATERF_INTERIOR, + "ARRO0001", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0002( + TEMPLATE_ARRO0002, + THEATERF_INTERIOR, + "ARRO0002", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0003( + TEMPLATE_ARRO0003, + THEATERF_INTERIOR, + "ARRO0003", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0004( + TEMPLATE_ARRO0004, + THEATERF_INTERIOR, + "ARRO0004", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0005( + TEMPLATE_ARRO0005, + THEATERF_INTERIOR, + "ARRO0005", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0006( + TEMPLATE_ARRO0006, + THEATERF_INTERIOR, + "ARRO0006", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0007( + TEMPLATE_ARRO0007, + THEATERF_INTERIOR, + "ARRO0007", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0008( + TEMPLATE_ARRO0008, + THEATERF_INTERIOR, + "ARRO0008", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0009( + TEMPLATE_ARRO0009, + THEATERF_INTERIOR, + "ARRO0009", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0010( + TEMPLATE_ARRO0010, + THEATERF_INTERIOR, + "ARRO0010", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0011( + TEMPLATE_ARRO0011, + THEATERF_INTERIOR, + "ARRO0011", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0012( + TEMPLATE_ARRO0012, + THEATERF_INTERIOR, + "ARRO0012", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0013( + TEMPLATE_ARRO0013, + THEATERF_INTERIOR, + "ARRO0013", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0014( + TEMPLATE_ARRO0014, + THEATERF_INTERIOR, + "ARRO0014", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0015( + TEMPLATE_ARRO0015, + THEATERF_INTERIOR, + "ARRO0015", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0001( + TEMPLATE_FLOR0001, + THEATERF_INTERIOR, + "FLOR0001", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0002( + TEMPLATE_FLOR0002, + THEATERF_INTERIOR, + "FLOR0002", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0003( + TEMPLATE_FLOR0003, + THEATERF_INTERIOR, + "FLOR0003", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0004( + TEMPLATE_FLOR0004, + THEATERF_INTERIOR, + "FLOR0004", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0005( + TEMPLATE_FLOR0005, + THEATERF_INTERIOR, + "FLOR0005", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0006( + TEMPLATE_FLOR0006, + THEATERF_INTERIOR, + "FLOR0006", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0007( + TEMPLATE_FLOR0007, + THEATERF_INTERIOR, + "FLOR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0001( + TEMPLATE_GFLR0001, + THEATERF_INTERIOR, + "GFLR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0002( + TEMPLATE_GFLR0002, + THEATERF_INTERIOR, + "GFLR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0003( + TEMPLATE_GFLR0003, + THEATERF_INTERIOR, + "GFLR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0004( + TEMPLATE_GFLR0004, + THEATERF_INTERIOR, + "GFLR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0005( + TEMPLATE_GFLR0005, + THEATERF_INTERIOR, + "GFLR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0001( + TEMPLATE_GSTR0001, + THEATERF_INTERIOR, + "GSTR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0002( + TEMPLATE_GSTR0002, + THEATERF_INTERIOR, + "GSTR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0003( + TEMPLATE_GSTR0003, + THEATERF_INTERIOR, + "GSTR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0004( + TEMPLATE_GSTR0004, + THEATERF_INTERIOR, + "GSTR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0005( + TEMPLATE_GSTR0005, + THEATERF_INTERIOR, + "GSTR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0006( + TEMPLATE_GSTR0006, + THEATERF_INTERIOR, + "GSTR0006", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0007( + TEMPLATE_GSTR0007, + THEATERF_INTERIOR, + "GSTR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0008( + TEMPLATE_GSTR0008, + THEATERF_INTERIOR, + "GSTR0008", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0009( + TEMPLATE_GSTR0009, + THEATERF_INTERIOR, + "GSTR0009", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0010( + TEMPLATE_GSTR0010, + THEATERF_INTERIOR, + "GSTR0010", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0011( + TEMPLATE_GSTR0011, + THEATERF_INTERIOR, + "GSTR0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0001( + TEMPLATE_LWAL0001, + THEATERF_INTERIOR, + "LWAL0001", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0002( + TEMPLATE_LWAL0002, + THEATERF_INTERIOR, + "LWAL0002", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0003( + TEMPLATE_LWAL0003, + THEATERF_INTERIOR, + "LWAL0003", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0004( + TEMPLATE_LWAL0004, + THEATERF_INTERIOR, + "LWAL0004", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0005( + TEMPLATE_LWAL0005, + THEATERF_INTERIOR, + "LWAL0005", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0006( + TEMPLATE_LWAL0006, + THEATERF_INTERIOR, + "LWAL0006", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0007( + TEMPLATE_LWAL0007, + THEATERF_INTERIOR, + "LWAL0007", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0008( + TEMPLATE_LWAL0008, + THEATERF_INTERIOR, + "LWAL0008", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0009( + TEMPLATE_LWAL0009, + THEATERF_INTERIOR, + "LWAL0009", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0010( + TEMPLATE_LWAL0010, + THEATERF_INTERIOR, + "LWAL0010", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0011( + TEMPLATE_LWAL0011, + THEATERF_INTERIOR, + "LWAL0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0012( + TEMPLATE_LWAL0012, + THEATERF_INTERIOR, + "LWAL0012", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0013( + TEMPLATE_LWAL0013, + THEATERF_INTERIOR, + "LWAL0013", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0014( + TEMPLATE_LWAL0014, + THEATERF_INTERIOR, + "LWAL0014", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0015( + TEMPLATE_LWAL0015, + THEATERF_INTERIOR, + "LWAL0015", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0016( + TEMPLATE_LWAL0016, + THEATERF_INTERIOR, + "LWAL0016", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0017( + TEMPLATE_LWAL0017, + THEATERF_INTERIOR, + "LWAL0017", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0018( + TEMPLATE_LWAL0018, + THEATERF_INTERIOR, + "LWAL0018", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0019( + TEMPLATE_LWAL0019, + THEATERF_INTERIOR, + "LWAL0019", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0020( + TEMPLATE_LWAL0020, + THEATERF_INTERIOR, + "LWAL0020", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0021( + TEMPLATE_LWAL0021, + THEATERF_INTERIOR, + "LWAL0021", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0022( + TEMPLATE_LWAL0022, + THEATERF_INTERIOR, + "LWAL0022", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0023( + TEMPLATE_LWAL0023, + THEATERF_INTERIOR, + "LWAL0023", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0024( + TEMPLATE_LWAL0024, + THEATERF_INTERIOR, + "LWAL0024", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0025( + TEMPLATE_LWAL0025, + THEATERF_INTERIOR, + "LWAL0025", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0026( + TEMPLATE_LWAL0026, + THEATERF_INTERIOR, + "LWAL0026", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0027( + TEMPLATE_LWAL0027, + THEATERF_INTERIOR, + "LWAL0027", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0001( + TEMPLATE_STRP0001, + THEATERF_INTERIOR, + "STRP0001", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0002( + TEMPLATE_STRP0002, + THEATERF_INTERIOR, + "STRP0002", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0003( + TEMPLATE_STRP0003, + THEATERF_INTERIOR, + "STRP0003", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0004( + TEMPLATE_STRP0004, + THEATERF_INTERIOR, + "STRP0004", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0005( + TEMPLATE_STRP0005, + THEATERF_INTERIOR, + "STRP0005", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0006( + TEMPLATE_STRP0006, + THEATERF_INTERIOR, + "STRP0006", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0007( + TEMPLATE_STRP0007, + THEATERF_INTERIOR, + "STRP0007", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0008( + TEMPLATE_STRP0008, + THEATERF_INTERIOR, + "STRP0008", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0009( + TEMPLATE_STRP0009, + THEATERF_INTERIOR, + "STRP0009", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0010( + TEMPLATE_STRP0010, + THEATERF_INTERIOR, + "STRP0010", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0011( + TEMPLATE_STRP0011, + THEATERF_INTERIOR, + "STRP0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0001( + TEMPLATE_WALL0001, + THEATERF_INTERIOR, + "WALL0001", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0002( + TEMPLATE_WALL0002, + THEATERF_INTERIOR, + "WALL0002", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0003( + TEMPLATE_WALL0003, + THEATERF_INTERIOR, + "WALL0003", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0004( + TEMPLATE_WALL0004, + THEATERF_INTERIOR, + "WALL0004", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0005( + TEMPLATE_WALL0005, + THEATERF_INTERIOR, + "WALL0005", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0006( + TEMPLATE_WALL0006, + THEATERF_INTERIOR, + "WALL0006", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0007( + TEMPLATE_WALL0007, + THEATERF_INTERIOR, + "WALL0007", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0008( + TEMPLATE_WALL0008, + THEATERF_INTERIOR, + "WALL0008", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0009( + TEMPLATE_WALL0009, + THEATERF_INTERIOR, + "WALL0009", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0010( + TEMPLATE_WALL0010, + THEATERF_INTERIOR, + "WALL0010", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0011( + TEMPLATE_WALL0011, + THEATERF_INTERIOR, + "WALL0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0012( + TEMPLATE_WALL0012, + THEATERF_INTERIOR, + "WALL0012", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0013( + TEMPLATE_WALL0013, + THEATERF_INTERIOR, + "WALL0013", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0014( + TEMPLATE_WALL0014, + THEATERF_INTERIOR, + "WALL0014", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0015( + TEMPLATE_WALL0015, + THEATERF_INTERIOR, + "WALL0015", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0016( + TEMPLATE_WALL0016, + THEATERF_INTERIOR, + "WALL0016", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0017( + TEMPLATE_WALL0017, + THEATERF_INTERIOR, + "WALL0017", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0018( + TEMPLATE_WALL0018, + THEATERF_INTERIOR, + "WALL0018", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0019( + TEMPLATE_WALL0019, + THEATERF_INTERIOR, + "WALL0019", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0020( + TEMPLATE_WALL0020, + THEATERF_INTERIOR, + "WALL0020", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0021( + TEMPLATE_WALL0021, + THEATERF_INTERIOR, + "WALL0021", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0022( + TEMPLATE_WALL0022, + THEATERF_INTERIOR, + "WALL0022", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0023( + TEMPLATE_WALL0023, + THEATERF_INTERIOR, + "WALL0023", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0024( + TEMPLATE_WALL0024, + THEATERF_INTERIOR, + "WALL0024", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0025( + TEMPLATE_WALL0025, + THEATERF_INTERIOR, + "WALL0025", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0026( + TEMPLATE_WALL0026, + THEATERF_INTERIOR, + "WALL0026", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0027( + TEMPLATE_WALL0027, + THEATERF_INTERIOR, + "WALL0027", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0028( + TEMPLATE_WALL0028, + THEATERF_INTERIOR, + "WALL0028", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0029( + TEMPLATE_WALL0029, + THEATERF_INTERIOR, + "WALL0029", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0030( + TEMPLATE_WALL0030, + THEATERF_INTERIOR, + "WALL0030", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0031( + TEMPLATE_WALL0031, + THEATERF_INTERIOR, + "WALL0031", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0032( + TEMPLATE_WALL0032, + THEATERF_INTERIOR, + "WALL0032", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0033( + TEMPLATE_WALL0033, + THEATERF_INTERIOR, + "WALL0033", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0034( + TEMPLATE_WALL0034, + THEATERF_INTERIOR, + "WALL0034", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0035( + TEMPLATE_WALL0035, + THEATERF_INTERIOR, + "WALL0035", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0036( + TEMPLATE_WALL0036, + THEATERF_INTERIOR, + "WALL0036", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0037( + TEMPLATE_WALL0037, + THEATERF_INTERIOR, + "WALL0037", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0038( + TEMPLATE_WALL0038, + THEATERF_INTERIOR, + "WALL0038", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0039( + TEMPLATE_WALL0039, + THEATERF_INTERIOR, + "WALL0039", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0040( + TEMPLATE_WALL0040, + THEATERF_INTERIOR, + "WALL0040", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0041( + TEMPLATE_WALL0041, + THEATERF_INTERIOR, + "WALL0041", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0042( + TEMPLATE_WALL0042, + THEATERF_INTERIOR, + "WALL0042", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0043( + TEMPLATE_WALL0043, + THEATERF_INTERIOR, + "WALL0043", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0044( + TEMPLATE_WALL0044, + THEATERF_INTERIOR, + "WALL0044", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0045( + TEMPLATE_WALL0045, + THEATERF_INTERIOR, + "WALL0045", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0046( + TEMPLATE_WALL0046, + THEATERF_INTERIOR, + "WALL0046", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0047( + TEMPLATE_WALL0047, + THEATERF_INTERIOR, + "WALL0047", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0048( + TEMPLATE_WALL0048, + THEATERF_INTERIOR, + "WALL0048", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0049( + TEMPLATE_WALL0049, + THEATERF_INTERIOR, + "WALL0049", + TXT_INTERIOR +); + +static TemplateTypeClass const Xtra0001( + TEMPLATE_XTRA0001, + THEATERF_INTERIOR, + "XTRA0001", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0002( + TEMPLATE_XTRA0002, + THEATERF_INTERIOR, + "XTRA0002", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0003( + TEMPLATE_XTRA0003, + THEATERF_INTERIOR, + "XTRA0003", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0004( + TEMPLATE_XTRA0004, + THEATERF_INTERIOR, + "XTRA0004", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0005( + TEMPLATE_XTRA0005, + THEATERF_INTERIOR, + "XTRA0005", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0006( + TEMPLATE_XTRA0006, + THEATERF_INTERIOR, + "XTRA0006", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0007( + TEMPLATE_XTRA0007, + THEATERF_INTERIOR, + "XTRA0007", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0008( + TEMPLATE_XTRA0008, + THEATERF_INTERIOR, + "XTRA0008", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0009( + TEMPLATE_XTRA0009, + THEATERF_INTERIOR, + "XTRA0009", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0010( + TEMPLATE_XTRA0010, + THEATERF_INTERIOR, + "XTRA0010", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0011( + TEMPLATE_XTRA0011, + THEATERF_INTERIOR, + "XTRA0011", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0012( + TEMPLATE_XTRA0012, + THEATERF_INTERIOR, + "XTRA0012", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0013( + TEMPLATE_XTRA0013, + THEATERF_INTERIOR, + "XTRA0013", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0014( + TEMPLATE_XTRA0014, + THEATERF_INTERIOR, + "XTRA0014", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0015( + TEMPLATE_XTRA0015, + THEATERF_INTERIOR, + "XTRA0015", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0016( + TEMPLATE_XTRA0016, + THEATERF_INTERIOR, + "XTRA0016", + TXT_INTERIOR +); + +#ifdef FIXIT_ANTS +static TemplateTypeClass const AntHill( + TEMPLATE_HILL01, + THEATERF_TEMPERATE, + "HILL01", + TXT_ROCK +); +#endif + +/*********************************************************************************************** + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * * + * This is the constructor for the template types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass::TemplateTypeClass( + TemplateType iconset, + int theater, + char const * ininame, + int fullname) : + ObjectTypeClass( + RTTI_TEMPLATETYPE, + int(iconset), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(iconset), + Theater(theater), + Width(0), + Height(0) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * * + * This allocates a template type object from the special heap used for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated template type object. If no object * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * TemplateTypeClass::operator new(size_t) +{ + return(TemplateTypes.Alloc()); +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * * + * This routine will return a template type object back to the special heap it was * + * allocated from. * + * * + * INPUT: ptr -- Pointer to the template type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::operator delete(void * ptr) +{ + TemplateTypes.Free((TemplateTypeClass *)ptr); +} + + +static void _Watcom_Ugh_Hack(void) +{ + (void)new TemplateTypeClass(Road37); // TEMPLATE_ROAD37 + (void)new TemplateTypeClass(Road38); // TEMPLATE_ROAD38 + (void)new TemplateTypeClass(Road39); // TEMPLATE_ROAD39 + (void)new TemplateTypeClass(Road40); // TEMPLATE_ROAD40 + (void)new TemplateTypeClass(Road41); // TEMPLATE_ROAD41 + (void)new TemplateTypeClass(Road42); // TEMPLATE_ROAD42 + (void)new TemplateTypeClass(Road43); // TEMPLATE_ROAD43 + (void)new TemplateTypeClass(Rough01); // TEMPLATE_ROUGH01 + (void)new TemplateTypeClass(Rough02); // TEMPLATE_ROUGH02 + (void)new TemplateTypeClass(Rough03); // TEMPLATE_ROUGH03 + (void)new TemplateTypeClass(Rough04); // TEMPLATE_ROUGH04 + (void)new TemplateTypeClass(Rough05); // TEMPLATE_ROUGH05 + (void)new TemplateTypeClass(Rough06); // TEMPLATE_ROUGH06 + (void)new TemplateTypeClass(Rough07); // TEMPLATE_ROUGH07 + (void)new TemplateTypeClass(Rough08); // TEMPLATE_ROUGH08 + (void)new TemplateTypeClass(Rough09); // TEMPLATE_ROUGH09 + (void)new TemplateTypeClass(Rough10); // TEMPLATE_ROUGH10 + (void)new TemplateTypeClass(Rough11); // TEMPLATE_ROUGH11 + (void)new TemplateTypeClass(Road44); // TEMPLATE_ROAD44 + (void)new TemplateTypeClass(Road45); // TEMPLATE_ROAD45 + (void)new TemplateTypeClass(River14); // TEMPLATE_RIVER14 + (void)new TemplateTypeClass(River15); // TEMPLATE_RIVER15 + (void)new TemplateTypeClass(RiverCliff01); // TEMPLATE_RIVERCLIFF01 + (void)new TemplateTypeClass(RiverCliff02); // TEMPLATE_RIVERCLIFF02 + (void)new TemplateTypeClass(RiverCliff03); // TEMPLATE_RIVERCLIFF03 + (void)new TemplateTypeClass(RiverCliff04); // TEMPLATE_RIVERCLIFF04 + (void)new TemplateTypeClass(Bridge1a); // TEMPLATE_BRIDGE_1A + (void)new TemplateTypeClass(Bridge1b); // TEMPLATE_BRIDGE_1B + (void)new TemplateTypeClass(Bridge1c); // TEMPLATE_BRIDGE_1C + (void)new TemplateTypeClass(Bridge2a); // TEMPLATE_BRIDGE_2A + (void)new TemplateTypeClass(Bridge2b); // TEMPLATE_BRIDGE_2B + (void)new TemplateTypeClass(Bridge2c); // TEMPLATE_BRIDGE_2C + (void)new TemplateTypeClass(Bridge3a); // TEMPLATE_BRIDGE_3A + (void)new TemplateTypeClass(Bridge3b); // TEMPLATE_BRIDGE_3B + (void)new TemplateTypeClass(Bridge3c); // TEMPLATE_BRIDGE_3C + (void)new TemplateTypeClass(Bridge3d); // TEMPLATE_BRIDGE_3D + (void)new TemplateTypeClass(Bridge3e); // TEMPLATE_BRIDGE_3E + (void)new TemplateTypeClass(Bridge3f); // TEMPLATE_BRIDGE_3F + (void)new TemplateTypeClass(F01); // TEMPLATE_F01 + (void)new TemplateTypeClass(F02); // TEMPLATE_F02 + (void)new TemplateTypeClass(F03); // TEMPLATE_F03 + (void)new TemplateTypeClass(F04); // TEMPLATE_F04 + (void)new TemplateTypeClass(F05); // TEMPLATE_F05 + (void)new TemplateTypeClass(F06); // TEMPLATE_F06 + (void)new TemplateTypeClass(ARRO0001); // TEMPLATE_ARRO0001 + (void)new TemplateTypeClass(ARRO0002); // TEMPLATE_ARRO0002 + (void)new TemplateTypeClass(ARRO0003); // TEMPLATE_ARRO0003 + (void)new TemplateTypeClass(ARRO0004); // TEMPLATE_ARRO0004 + (void)new TemplateTypeClass(ARRO0005); // TEMPLATE_ARRO0005 + (void)new TemplateTypeClass(ARRO0006); // TEMPLATE_ARRO0006 + (void)new TemplateTypeClass(ARRO0007); // TEMPLATE_ARRO0007 + (void)new TemplateTypeClass(ARRO0008); // TEMPLATE_ARRO0008 + (void)new TemplateTypeClass(ARRO0009); // TEMPLATE_ARRO0009 + (void)new TemplateTypeClass(ARRO0010); // TEMPLATE_ARRO0010 + (void)new TemplateTypeClass(ARRO0011); // TEMPLATE_ARRO0011 + (void)new TemplateTypeClass(ARRO0012); // TEMPLATE_ARRO0012 + (void)new TemplateTypeClass(ARRO0013); // TEMPLATE_ARRO0013 + (void)new TemplateTypeClass(ARRO0014); // TEMPLATE_ARRO0014 + (void)new TemplateTypeClass(ARRO0015); // TEMPLATE_ARRO0015 + (void)new TemplateTypeClass(FLOR0001); // TEMPLATE_FLOR0001 + (void)new TemplateTypeClass(FLOR0002); // TEMPLATE_FLOR0002 + (void)new TemplateTypeClass(FLOR0003); // TEMPLATE_FLOR0003 + (void)new TemplateTypeClass(FLOR0004); // TEMPLATE_FLOR0004 + (void)new TemplateTypeClass(FLOR0005); // TEMPLATE_FLOR0005 + (void)new TemplateTypeClass(FLOR0006); // TEMPLATE_FLOR0006 + (void)new TemplateTypeClass(FLOR0007); // TEMPLATE_FLOR0007 + (void)new TemplateTypeClass(GFLR0001); // TEMPLATE_GFLR0001 + (void)new TemplateTypeClass(GFLR0002); // TEMPLATE_GFLR0002 + (void)new TemplateTypeClass(GFLR0003); // TEMPLATE_GFLR0003 + (void)new TemplateTypeClass(GFLR0004); // TEMPLATE_GFLR0004 + (void)new TemplateTypeClass(GFLR0005); // TEMPLATE_GFLR0005 + (void)new TemplateTypeClass(GSTR0001); // TEMPLATE_GSTR0001 + (void)new TemplateTypeClass(GSTR0002); // TEMPLATE_GSTR0002 + (void)new TemplateTypeClass(GSTR0003); // TEMPLATE_GSTR0003 + (void)new TemplateTypeClass(GSTR0004); // TEMPLATE_GSTR0004 + (void)new TemplateTypeClass(GSTR0005); // TEMPLATE_GSTR0005 + (void)new TemplateTypeClass(GSTR0006); // TEMPLATE_GSTR0006 + (void)new TemplateTypeClass(GSTR0007); // TEMPLATE_GSTR0007 + (void)new TemplateTypeClass(GSTR0008); // TEMPLATE_GSTR0008 + (void)new TemplateTypeClass(GSTR0009); // TEMPLATE_GSTR0009 + (void)new TemplateTypeClass(GSTR0010); // TEMPLATE_GSTR0010 + (void)new TemplateTypeClass(GSTR0011); // TEMPLATE_GSTR0011 + (void)new TemplateTypeClass(LWAL0001); // TEMPLATE_LWAL0001 + (void)new TemplateTypeClass(LWAL0002); // TEMPLATE_LWAL0002 + (void)new TemplateTypeClass(LWAL0003); // TEMPLATE_LWAL0003 + (void)new TemplateTypeClass(LWAL0004); // TEMPLATE_LWAL0004 + (void)new TemplateTypeClass(LWAL0005); // TEMPLATE_LWAL0005 + (void)new TemplateTypeClass(LWAL0006); // TEMPLATE_LWAL0006 + (void)new TemplateTypeClass(LWAL0007); // TEMPLATE_LWAL0007 + (void)new TemplateTypeClass(LWAL0008); // TEMPLATE_LWAL0008 + (void)new TemplateTypeClass(LWAL0009); // TEMPLATE_LWAL0009 + (void)new TemplateTypeClass(LWAL0010); // TEMPLATE_LWAL0010 + (void)new TemplateTypeClass(LWAL0011); // TEMPLATE_LWAL0011 + (void)new TemplateTypeClass(LWAL0012); // TEMPLATE_LWAL0012 + (void)new TemplateTypeClass(LWAL0013); // TEMPLATE_LWAL0013 + (void)new TemplateTypeClass(LWAL0014); // TEMPLATE_LWAL0014 + (void)new TemplateTypeClass(LWAL0015); // TEMPLATE_LWAL0015 + (void)new TemplateTypeClass(LWAL0016); // TEMPLATE_LWAL0016 + (void)new TemplateTypeClass(LWAL0017); // TEMPLATE_LWAL0017 + (void)new TemplateTypeClass(LWAL0018); // TEMPLATE_LWAL0018 + (void)new TemplateTypeClass(LWAL0019); // TEMPLATE_LWAL0019 + (void)new TemplateTypeClass(LWAL0020); // TEMPLATE_LWAL0020 + (void)new TemplateTypeClass(LWAL0021); // TEMPLATE_LWAL0021 + (void)new TemplateTypeClass(LWAL0022); // TEMPLATE_LWAL0022 + (void)new TemplateTypeClass(LWAL0023); // TEMPLATE_LWAL0023 + (void)new TemplateTypeClass(LWAL0024); // TEMPLATE_LWAL0024 + (void)new TemplateTypeClass(LWAL0025); // TEMPLATE_LWAL0025 + (void)new TemplateTypeClass(LWAL0026); // TEMPLATE_LWAL0026 + (void)new TemplateTypeClass(LWAL0027); // TEMPLATE_LWAL0027 + (void)new TemplateTypeClass(STRP0001); // TEMPLATE_STRP0001 + (void)new TemplateTypeClass(STRP0002); // TEMPLATE_STRP0002 + (void)new TemplateTypeClass(STRP0003); // TEMPLATE_STRP0003 + (void)new TemplateTypeClass(STRP0004); // TEMPLATE_STRP0004 + (void)new TemplateTypeClass(STRP0005); // TEMPLATE_STRP0005 + (void)new TemplateTypeClass(STRP0006); // TEMPLATE_STRP0006 + (void)new TemplateTypeClass(STRP0007); // TEMPLATE_STRP0007 + (void)new TemplateTypeClass(STRP0008); // TEMPLATE_STRP0008 + (void)new TemplateTypeClass(STRP0009); // TEMPLATE_STRP0009 + (void)new TemplateTypeClass(STRP0010); // TEMPLATE_STRP0010 + (void)new TemplateTypeClass(STRP0011); // TEMPLATE_STRP0011 + (void)new TemplateTypeClass(WALL0001); // TEMPLATE_WALL0001 + (void)new TemplateTypeClass(WALL0002); // TEMPLATE_WALL0002 + (void)new TemplateTypeClass(WALL0003); // TEMPLATE_WALL0003 + (void)new TemplateTypeClass(WALL0004); // TEMPLATE_WALL0004 + (void)new TemplateTypeClass(WALL0005); // TEMPLATE_WALL0005 + (void)new TemplateTypeClass(WALL0006); // TEMPLATE_WALL0006 + (void)new TemplateTypeClass(WALL0007); // TEMPLATE_WALL0007 + (void)new TemplateTypeClass(WALL0008); // TEMPLATE_WALL0008 + (void)new TemplateTypeClass(WALL0009); // TEMPLATE_WALL0009 + (void)new TemplateTypeClass(WALL0010); // TEMPLATE_WALL0010 + (void)new TemplateTypeClass(WALL0011); // TEMPLATE_WALL0011 + (void)new TemplateTypeClass(WALL0012); // TEMPLATE_WALL0012 + (void)new TemplateTypeClass(WALL0013); // TEMPLATE_WALL0013 + (void)new TemplateTypeClass(WALL0014); // TEMPLATE_WALL0014 + (void)new TemplateTypeClass(WALL0015); // TEMPLATE_WALL0015 + (void)new TemplateTypeClass(WALL0016); // TEMPLATE_WALL0016 + (void)new TemplateTypeClass(WALL0017); // TEMPLATE_WALL0017 + (void)new TemplateTypeClass(WALL0018); // TEMPLATE_WALL0018 + (void)new TemplateTypeClass(WALL0019); // TEMPLATE_WALL0019 + (void)new TemplateTypeClass(WALL0020); // TEMPLATE_WALL0020 + (void)new TemplateTypeClass(WALL0021); // TEMPLATE_WALL0021 + (void)new TemplateTypeClass(WALL0022); // TEMPLATE_WALL0022 + (void)new TemplateTypeClass(WALL0023); // TEMPLATE_WALL0023 + (void)new TemplateTypeClass(WALL0024); // TEMPLATE_WALL0024 + (void)new TemplateTypeClass(WALL0025); // TEMPLATE_WALL0025 + (void)new TemplateTypeClass(WALL0026); // TEMPLATE_WALL0026 + (void)new TemplateTypeClass(WALL0027); // TEMPLATE_WALL0027 + (void)new TemplateTypeClass(WALL0028); // TEMPLATE_WALL0028 + (void)new TemplateTypeClass(WALL0029); // TEMPLATE_WALL0029 + (void)new TemplateTypeClass(WALL0030); // TEMPLATE_WALL0030 + (void)new TemplateTypeClass(WALL0031); // TEMPLATE_WALL0031 + (void)new TemplateTypeClass(WALL0032); // TEMPLATE_WALL0032 + (void)new TemplateTypeClass(WALL0033); // TEMPLATE_WALL0033 + (void)new TemplateTypeClass(WALL0034); // TEMPLATE_WALL0034 + (void)new TemplateTypeClass(WALL0035); // TEMPLATE_WALL0035 + (void)new TemplateTypeClass(WALL0036); // TEMPLATE_WALL0036 + (void)new TemplateTypeClass(WALL0037); // TEMPLATE_WALL0037 + (void)new TemplateTypeClass(WALL0038); // TEMPLATE_WALL0038 + (void)new TemplateTypeClass(WALL0039); // TEMPLATE_WALL0039 + (void)new TemplateTypeClass(WALL0040); // TEMPLATE_WALL0040 + (void)new TemplateTypeClass(WALL0041); // TEMPLATE_WALL0041 + (void)new TemplateTypeClass(WALL0042); // TEMPLATE_WALL0042 + (void)new TemplateTypeClass(WALL0043); // TEMPLATE_WALL0043 + (void)new TemplateTypeClass(WALL0044); // TEMPLATE_WALL0044 + (void)new TemplateTypeClass(WALL0045); // TEMPLATE_WALL0045 + (void)new TemplateTypeClass(WALL0046); // TEMPLATE_WALL0046 + (void)new TemplateTypeClass(WALL0047); // TEMPLATE_WALL0047 + (void)new TemplateTypeClass(WALL0048); // TEMPLATE_WALL0048 + (void)new TemplateTypeClass(WALL0049); // TEMPLATE_WALL0049 + (void)new TemplateTypeClass(Bridge1h); // TEMPLATE_BRIDGE1H + (void)new TemplateTypeClass(Bridge2h); // TEMPLATE_BRIDGE2H + + (void)new TemplateTypeClass(Bridge1ax); // TEMPLATE_BRIDGE_1AX + (void)new TemplateTypeClass(Bridge2ax); // TEMPLATE_BRIDGE_2AX + (void)new TemplateTypeClass(Bridge1x); // TEMPLATE_BRIDGE1X + (void)new TemplateTypeClass(Bridge2x); // TEMPLATE_BRIDGE2X + + (void)new TemplateTypeClass(Xtra0001); // TEMPLATE_XTRA0001 + (void)new TemplateTypeClass(Xtra0002); // TEMPLATE_XTRA0002 + (void)new TemplateTypeClass(Xtra0003); // TEMPLATE_XTRA0003 + (void)new TemplateTypeClass(Xtra0004); // TEMPLATE_XTRA0004 + (void)new TemplateTypeClass(Xtra0005); // TEMPLATE_XTRA0005 + (void)new TemplateTypeClass(Xtra0006); // TEMPLATE_XTRA0006 + (void)new TemplateTypeClass(Xtra0007); // TEMPLATE_XTRA0007 + (void)new TemplateTypeClass(Xtra0008); // TEMPLATE_XTRA0008 + (void)new TemplateTypeClass(Xtra0009); // TEMPLATE_XTRA0009 + (void)new TemplateTypeClass(Xtra0010); // TEMPLATE_XTRA0010 + (void)new TemplateTypeClass(Xtra0011); // TEMPLATE_XTRA0011 + (void)new TemplateTypeClass(Xtra0012); // TEMPLATE_XTRA0012 + (void)new TemplateTypeClass(Xtra0013); // TEMPLATE_XTRA0013 + (void)new TemplateTypeClass(Xtra0014); // TEMPLATE_XTRA0014 + (void)new TemplateTypeClass(Xtra0015); // TEMPLATE_XTRA0015 + (void)new TemplateTypeClass(Xtra0016); // TEMPLATE_XTRA0016 + +#ifdef FIXIT_ANTS + (void)new TemplateTypeClass(AntHill); // TEMPLATE_ROAD36 +#endif +} + + + +void TemplateTypeClass::Init_Heap(void) +{ + /* + ** These template type class objects must be allocated in the exact order that they + ** are specified in the TemplateType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + (void)new TemplateTypeClass(Clear); // TEMPLATE_CLEAR1 + (void)new TemplateTypeClass(Water); // TEMPLATE_WATER + (void)new TemplateTypeClass(Water2); // TEMPLATE_WATER2 + (void)new TemplateTypeClass(Shore01); // TEMPLATE_SHORE1 + (void)new TemplateTypeClass(Shore02); // TEMPLATE_SHORE2 + (void)new TemplateTypeClass(Shore03); // TEMPLATE_SHORE3 + (void)new TemplateTypeClass(Shore04); // TEMPLATE_SHORE4 + (void)new TemplateTypeClass(Shore05); // TEMPLATE_SHORE5 + (void)new TemplateTypeClass(Shore06); // TEMPLATE_SHORE6 + (void)new TemplateTypeClass(Shore07); // TEMPLATE_SHORE7 + (void)new TemplateTypeClass(Shore08); // TEMPLATE_SHORE8 + (void)new TemplateTypeClass(Shore09); // TEMPLATE_SHORE9 + (void)new TemplateTypeClass(Shore10); // TEMPLATE_SHORE10 + (void)new TemplateTypeClass(Shore11); // TEMPLATE_SHORE11 + (void)new TemplateTypeClass(Shore12); // TEMPLATE_SHORE12 + (void)new TemplateTypeClass(Shore13); // TEMPLATE_SHORE13 + (void)new TemplateTypeClass(Shore14); // TEMPLATE_SHORE14 + (void)new TemplateTypeClass(Shore15); // TEMPLATE_SHORE15 + (void)new TemplateTypeClass(Shore16); // TEMPLATE_SHORE16 + (void)new TemplateTypeClass(Shore17); // TEMPLATE_SHORE17 + (void)new TemplateTypeClass(Shore18); // TEMPLATE_SHORE18 + (void)new TemplateTypeClass(Shore19); // TEMPLATE_SHORE19 + (void)new TemplateTypeClass(Shore20); // TEMPLATE_SHORE20 + (void)new TemplateTypeClass(Shore21); // TEMPLATE_SHORE21 + (void)new TemplateTypeClass(Shore22); // TEMPLATE_SHORE22 + (void)new TemplateTypeClass(Shore23); // TEMPLATE_SHORE23 + (void)new TemplateTypeClass(Shore24); // TEMPLATE_SHORE24 + (void)new TemplateTypeClass(Shore25); // TEMPLATE_SHORE25 + (void)new TemplateTypeClass(Shore26); // TEMPLATE_SHORE26 + (void)new TemplateTypeClass(Shore27); // TEMPLATE_SHORE27 + (void)new TemplateTypeClass(Shore28); // TEMPLATE_SHORE28 + (void)new TemplateTypeClass(Shore29); // TEMPLATE_SHORE29 + (void)new TemplateTypeClass(Shore30); // TEMPLATE_SHORE30 + (void)new TemplateTypeClass(Shore31); // TEMPLATE_SHORE31 + (void)new TemplateTypeClass(Shore32); // TEMPLATE_SHORE32 + (void)new TemplateTypeClass(Shore33); // TEMPLATE_SHORE33 + (void)new TemplateTypeClass(Shore34); // TEMPLATE_SHORE34 + (void)new TemplateTypeClass(Shore35); // TEMPLATE_SHORE35 + (void)new TemplateTypeClass(Shore36); // TEMPLATE_SHORE36 + (void)new TemplateTypeClass(Shore37); // TEMPLATE_SHORE37 + (void)new TemplateTypeClass(Shore38); // TEMPLATE_SHORE38 + (void)new TemplateTypeClass(Shore39); // TEMPLATE_SHORE39 + (void)new TemplateTypeClass(Shore40); // TEMPLATE_SHORE40 + (void)new TemplateTypeClass(Shore41); // TEMPLATE_SHORE41 + (void)new TemplateTypeClass(Shore42); // TEMPLATE_SHORE42 + (void)new TemplateTypeClass(Shore43); // TEMPLATE_SHORE43 + (void)new TemplateTypeClass(Shore44); // TEMPLATE_SHORE44 + (void)new TemplateTypeClass(Shore45); // TEMPLATE_SHORE45 + (void)new TemplateTypeClass(Shore46); // TEMPLATE_SHORE46 + (void)new TemplateTypeClass(Shore47); // TEMPLATE_SHORE47 + (void)new TemplateTypeClass(Shore48); // TEMPLATE_SHORE48 + (void)new TemplateTypeClass(Shore49); // TEMPLATE_SHORE49 + (void)new TemplateTypeClass(Shore50); // TEMPLATE_SHORE50 + (void)new TemplateTypeClass(Shore51); // TEMPLATE_SHORE51 + (void)new TemplateTypeClass(Shore52); // TEMPLATE_SHORE52 + (void)new TemplateTypeClass(Shore53); // TEMPLATE_SHORE53 + (void)new TemplateTypeClass(Shore54); // TEMPLATE_SHORE54 + (void)new TemplateTypeClass(Shore55); // TEMPLATE_SHORE55 + (void)new TemplateTypeClass(Shore56); // TEMPLATE_SHORE56 + (void)new TemplateTypeClass(ShoreCliff01); // TEMPLATE_SHORECLIFF01 + (void)new TemplateTypeClass(ShoreCliff02); // TEMPLATE_SHORECLIFF02 + (void)new TemplateTypeClass(ShoreCliff03); // TEMPLATE_SHORECLIFF03 + (void)new TemplateTypeClass(ShoreCliff04); // TEMPLATE_SHORECLIFF04 + (void)new TemplateTypeClass(ShoreCliff05); // TEMPLATE_SHORECLIFF05 + (void)new TemplateTypeClass(ShoreCliff06); // TEMPLATE_SHORECLIFF06 + (void)new TemplateTypeClass(ShoreCliff07); // TEMPLATE_SHORECLIFF07 + (void)new TemplateTypeClass(ShoreCliff08); // TEMPLATE_SHORECLIFF08 + (void)new TemplateTypeClass(ShoreCliff09); // TEMPLATE_SHORECLIFF09 + (void)new TemplateTypeClass(ShoreCliff10); // TEMPLATE_SHORECLIFF10 + (void)new TemplateTypeClass(ShoreCliff11); // TEMPLATE_SHORECLIFF11 + (void)new TemplateTypeClass(ShoreCliff12); // TEMPLATE_SHORECLIFF12 + (void)new TemplateTypeClass(ShoreCliff13); // TEMPLATE_SHORECLIFF13 + (void)new TemplateTypeClass(ShoreCliff14); // TEMPLATE_SHORECLIFF14 + (void)new TemplateTypeClass(ShoreCliff15); // TEMPLATE_SHORECLIFF15 + (void)new TemplateTypeClass(ShoreCliff16); // TEMPLATE_SHORECLIFF16 + (void)new TemplateTypeClass(ShoreCliff17); // TEMPLATE_SHORECLIFF17 + (void)new TemplateTypeClass(ShoreCliff18); // TEMPLATE_SHORECLIFF18 + (void)new TemplateTypeClass(ShoreCliff19); // TEMPLATE_SHORECLIFF19 + (void)new TemplateTypeClass(ShoreCliff20); // TEMPLATE_SHORECLIFF20 + (void)new TemplateTypeClass(ShoreCliff21); // TEMPLATE_SHORECLIFF21 + (void)new TemplateTypeClass(ShoreCliff22); // TEMPLATE_SHORECLIFF22 + (void)new TemplateTypeClass(ShoreCliff23); // TEMPLATE_SHORECLIFF23 + (void)new TemplateTypeClass(ShoreCliff24); // TEMPLATE_SHORECLIFF24 + (void)new TemplateTypeClass(ShoreCliff25); // TEMPLATE_SHORECLIFF25 + (void)new TemplateTypeClass(ShoreCliff26); // TEMPLATE_SHORECLIFF26 + (void)new TemplateTypeClass(ShoreCliff27); // TEMPLATE_SHORECLIFF27 + (void)new TemplateTypeClass(ShoreCliff28); // TEMPLATE_SHORECLIFF28 + (void)new TemplateTypeClass(ShoreCliff29); // TEMPLATE_SHORECLIFF29 + (void)new TemplateTypeClass(ShoreCliff30); // TEMPLATE_SHORECLIFF30 + (void)new TemplateTypeClass(ShoreCliff31); // TEMPLATE_SHORECLIFF31 + (void)new TemplateTypeClass(ShoreCliff32); // TEMPLATE_SHORECLIFF32 + (void)new TemplateTypeClass(ShoreCliff33); // TEMPLATE_SHORECLIFF33 + (void)new TemplateTypeClass(ShoreCliff34); // TEMPLATE_SHORECLIFF34 + (void)new TemplateTypeClass(ShoreCliff35); // TEMPLATE_SHORECLIFF35 + (void)new TemplateTypeClass(ShoreCliff36); // TEMPLATE_SHORECLIFF36 + (void)new TemplateTypeClass(ShoreCliff37); // TEMPLATE_SHORECLIFF37 + (void)new TemplateTypeClass(ShoreCliff38); // TEMPLATE_SHORECLIFF38 + (void)new TemplateTypeClass(Boulder1); // TEMPLATE_BOULDER1 + (void)new TemplateTypeClass(Boulder2); // TEMPLATE_BOULDER2 + (void)new TemplateTypeClass(Boulder3); // TEMPLATE_BOULDER3 + (void)new TemplateTypeClass(Boulder4); // TEMPLATE_BOULDER4 + (void)new TemplateTypeClass(Boulder5); // TEMPLATE_BOULDER5 + (void)new TemplateTypeClass(Boulder6); // TEMPLATE_BOULDER6 + (void)new TemplateTypeClass(Patch01); // TEMPLATE_PATCH1 + (void)new TemplateTypeClass(Patch02); // TEMPLATE_PATCH2 + (void)new TemplateTypeClass(Patch03); // TEMPLATE_PATCH3 + (void)new TemplateTypeClass(Patch04); // TEMPLATE_PATCH4 + (void)new TemplateTypeClass(Patch07); // TEMPLATE_PATCH7 + (void)new TemplateTypeClass(Patch08); // TEMPLATE_PATCH8 + (void)new TemplateTypeClass(Patch13); // TEMPLATE_PATCH13 + (void)new TemplateTypeClass(Patch14); // TEMPLATE_PATCH14 + (void)new TemplateTypeClass(Patch15); // TEMPLATE_PATCH15 + (void)new TemplateTypeClass(River01); // TEMPLATE_RIVER1 + (void)new TemplateTypeClass(River02); // TEMPLATE_RIVER2 + (void)new TemplateTypeClass(River03); // TEMPLATE_RIVER3 + (void)new TemplateTypeClass(River04); // TEMPLATE_RIVER4 + (void)new TemplateTypeClass(River05); // TEMPLATE_RIVER5 + (void)new TemplateTypeClass(River06); // TEMPLATE_RIVER6 + (void)new TemplateTypeClass(River07); // TEMPLATE_RIVER7 + (void)new TemplateTypeClass(River08); // TEMPLATE_RIVER8 + (void)new TemplateTypeClass(River09); // TEMPLATE_RIVER9 + (void)new TemplateTypeClass(River10); // TEMPLATE_RIVER10 + (void)new TemplateTypeClass(River11); // TEMPLATE_RIVER11 + (void)new TemplateTypeClass(River12); // TEMPLATE_RIVER12 + (void)new TemplateTypeClass(River13); // TEMPLATE_RIVER13 + (void)new TemplateTypeClass(Falls1); // TEMPLATE_FALLS1 + (void)new TemplateTypeClass(Falls1a); // TEMPLATE_FALLS1A + (void)new TemplateTypeClass(Falls2); // TEMPLATE_FALLS2 + (void)new TemplateTypeClass(Falls2a); // TEMPLATE_FALLS2A + (void)new TemplateTypeClass(Ford1); // TEMPLATE_FORD1 + (void)new TemplateTypeClass(Ford2); // TEMPLATE_FORD2 + (void)new TemplateTypeClass(Bridge1); // TEMPLATE_BRIDGE1 + (void)new TemplateTypeClass(Bridge1d); // TEMPLATE_BRIDGE1D + (void)new TemplateTypeClass(Bridge2); // TEMPLATE_BRIDGE2 + (void)new TemplateTypeClass(Bridge2d); // TEMPLATE_BRIDGE2D + (void)new TemplateTypeClass(Slope01); // TEMPLATE_SLOPE1 + (void)new TemplateTypeClass(Slope02); // TEMPLATE_SLOPE2 + (void)new TemplateTypeClass(Slope03); // TEMPLATE_SLOPE3 + (void)new TemplateTypeClass(Slope04); // TEMPLATE_SLOPE4 + (void)new TemplateTypeClass(Slope05); // TEMPLATE_SLOPE5 + (void)new TemplateTypeClass(Slope06); // TEMPLATE_SLOPE6 + (void)new TemplateTypeClass(Slope07); // TEMPLATE_SLOPE7 + (void)new TemplateTypeClass(Slope08); // TEMPLATE_SLOPE8 + (void)new TemplateTypeClass(Slope09); // TEMPLATE_SLOPE9 + (void)new TemplateTypeClass(Slope10); // TEMPLATE_SLOPE10 + (void)new TemplateTypeClass(Slope11); // TEMPLATE_SLOPE11 + (void)new TemplateTypeClass(Slope12); // TEMPLATE_SLOPE12 + (void)new TemplateTypeClass(Slope13); // TEMPLATE_SLOPE13 + (void)new TemplateTypeClass(Slope14); // TEMPLATE_SLOPE14 + (void)new TemplateTypeClass(Slope15); // TEMPLATE_SLOPE15 + (void)new TemplateTypeClass(Slope16); // TEMPLATE_SLOPE16 + (void)new TemplateTypeClass(Slope17); // TEMPLATE_SLOPE17 + (void)new TemplateTypeClass(Slope18); // TEMPLATE_SLOPE18 + (void)new TemplateTypeClass(Slope19); // TEMPLATE_SLOPE19 + (void)new TemplateTypeClass(Slope20); // TEMPLATE_SLOPE20 + (void)new TemplateTypeClass(Slope21); // TEMPLATE_SLOPE21 + (void)new TemplateTypeClass(Slope22); // TEMPLATE_SLOPE22 + (void)new TemplateTypeClass(Slope23); // TEMPLATE_SLOPE23 + (void)new TemplateTypeClass(Slope24); // TEMPLATE_SLOPE24 + (void)new TemplateTypeClass(Slope25); // TEMPLATE_SLOPE25 + (void)new TemplateTypeClass(Slope26); // TEMPLATE_SLOPE26 + (void)new TemplateTypeClass(Slope27); // TEMPLATE_SLOPE27 + (void)new TemplateTypeClass(Slope28); // TEMPLATE_SLOPE28 + (void)new TemplateTypeClass(Slope29); // TEMPLATE_SLOPE29 + (void)new TemplateTypeClass(Slope30); // TEMPLATE_SLOPE30 + (void)new TemplateTypeClass(Slope31); // TEMPLATE_SLOPE31 + (void)new TemplateTypeClass(Slope32); // TEMPLATE_SLOPE32 + (void)new TemplateTypeClass(Slope33); // TEMPLATE_SLOPE33 + (void)new TemplateTypeClass(Slope34); // TEMPLATE_SLOPE34 + (void)new TemplateTypeClass(Slope35); // TEMPLATE_SLOPE35 + (void)new TemplateTypeClass(Slope36); // TEMPLATE_SLOPE36 + (void)new TemplateTypeClass(Slope37); // TEMPLATE_SLOPE37 + (void)new TemplateTypeClass(Slope38); // TEMPLATE_SLOPE38 + (void)new TemplateTypeClass(Road01); // TEMPLATE_ROAD1 + (void)new TemplateTypeClass(Road02); // TEMPLATE_ROAD2 + (void)new TemplateTypeClass(Road03); // TEMPLATE_ROAD3 + (void)new TemplateTypeClass(Road04); // TEMPLATE_ROAD4 + (void)new TemplateTypeClass(Road05); // TEMPLATE_ROAD5 + (void)new TemplateTypeClass(Road06); // TEMPLATE_ROAD6 + (void)new TemplateTypeClass(Road07); // TEMPLATE_ROAD7 + (void)new TemplateTypeClass(Road08); // TEMPLATE_ROAD8 + (void)new TemplateTypeClass(Road09); // TEMPLATE_ROAD9 + (void)new TemplateTypeClass(Road10); // TEMPLATE_ROAD10 + (void)new TemplateTypeClass(Road11); // TEMPLATE_ROAD11 + (void)new TemplateTypeClass(Road12); // TEMPLATE_ROAD12 + (void)new TemplateTypeClass(Road13); // TEMPLATE_ROAD13 + (void)new TemplateTypeClass(Road14); // TEMPLATE_ROAD14 + (void)new TemplateTypeClass(Road15); // TEMPLATE_ROAD15 + (void)new TemplateTypeClass(Road16); // TEMPLATE_ROAD16 + (void)new TemplateTypeClass(Road17); // TEMPLATE_ROAD17 + (void)new TemplateTypeClass(Road18); // TEMPLATE_ROAD18 + (void)new TemplateTypeClass(Road19); // TEMPLATE_ROAD19 + (void)new TemplateTypeClass(Road20); // TEMPLATE_ROAD20 + (void)new TemplateTypeClass(Road21); // TEMPLATE_ROAD21 + (void)new TemplateTypeClass(Road22); // TEMPLATE_ROAD22 + (void)new TemplateTypeClass(Road23); // TEMPLATE_ROAD23 + (void)new TemplateTypeClass(Road24); // TEMPLATE_ROAD24 + (void)new TemplateTypeClass(Road25); // TEMPLATE_ROAD25 + (void)new TemplateTypeClass(Road26); // TEMPLATE_ROAD26 + (void)new TemplateTypeClass(Road27); // TEMPLATE_ROAD27 + (void)new TemplateTypeClass(Road28); // TEMPLATE_ROAD28 + (void)new TemplateTypeClass(Road29); // TEMPLATE_ROAD29 + (void)new TemplateTypeClass(Road30); // TEMPLATE_ROAD30 + (void)new TemplateTypeClass(Road31); // TEMPLATE_ROAD31 + (void)new TemplateTypeClass(Road32); // TEMPLATE_ROAD32 + (void)new TemplateTypeClass(Road33); // TEMPLATE_ROAD33 + (void)new TemplateTypeClass(Road34); // TEMPLATE_ROAD34 + (void)new TemplateTypeClass(Road35); // TEMPLATE_ROAD35 + (void)new TemplateTypeClass(Road36); // TEMPLATE_ROAD36 + + /* + ** Separate out the list of new operator calls. Watcom bombs + ** if they are kept together. + */ + _Watcom_Ugh_Hack(); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * * + * This routine will convert the specified icon number into the appropriate land type. The * + * land type can be determined from the embedded colors in the "control template" section * + * of the original art file. This control information is encoded into the icon data file * + * to be retrieved and interpreted as the program sees fit. The engine only recognizes * + * the first 16 colors as control colors, so the control map color value serves as an * + * index into a simple lookup table. * + * * + * INPUT: icon -- The icon number within this template that is to be examined and used * + * to determine the land type. * + * * + * OUTPUT: Returns with the land type that corresponds to the icon number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/12/1995 JLB : Created. * + *=============================================================================================*/ +LandType TemplateTypeClass::Land_Type(int icon) const +{ + IconsetClass const * icontrol = (IconsetClass const *)Get_Image_Data(); + + if (icontrol != NULL) { + unsigned char const * map = icontrol->Control_Map(); + if (map != NULL) { + static LandType _land[16] = { + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, // Clear + LAND_CLEAR, + LAND_CLEAR, + LAND_BEACH, // Beach + LAND_CLEAR, + LAND_ROCK, // Rock + LAND_ROAD, // Road + LAND_WATER, // Water + LAND_RIVER, // River + LAND_CLEAR, + LAND_CLEAR, + LAND_ROUGH, // Rough + LAND_CLEAR, + }; + + return(_land[map[icon % (icontrol->Map_Width() * icontrol->Map_Height())]]); + } + } + return(LAND_CLEAR); +} + + +/*********************************************************************************************** + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * * + * This routine is used to determine the template number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the template. * + * * + * OUTPUT: Returns with the template number. If the name had no match, * + * then returns with TEMPLATE_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +TemplateType TemplateTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(TEMPLATE_NONE); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the template map and build an * + * occupation list. This list is used to render a template cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the template occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 12/12/1995 JLB : Optimized for direct access to iconset data. * + *=============================================================================================*/ +short const * TemplateTypeClass::Occupy_List(bool) const +{ + static short _occupy[13*8+5]; + short * ptr; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + ptr = &_occupy[0]; + for (int index = 0; index < Width * Height; index++) { + if (*map++ != 0xFF) { + *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + + return((short const *)&_occupy[0]); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * * + * This routine loads the template graphic data for all the template * + * type supported for the specified theater. This routine is called * + * whenever the theater for the scenario is first determined. * + * * + * INPUT: theater -- The theater that the template data is to be * + * loaded for. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk! * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/02/1994 JLB : Only handles iconset loading now (as it should). * + *=============================================================================================*/ +void TemplateTypeClass::Init(TheaterType theater) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + void const * ptr; // Working loaded iconset pointer. + + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + TemplateTypeClass const & tplate = As_Reference(index); + + ((void const *&)tplate.ImageData) = NULL; + if (tplate.Theater & (1< 3 || h > 3); + if (scale) { + x -= (w * ICON_PIXEL_W) / 4; + y -= (h * ICON_PIXEL_H) / 4; + } else { + x -= (w * ICON_PIXEL_W) / 2; + y -= (h * ICON_PIXEL_H) / 2; + } + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + for (index = 0; index < w*h; index++) { + if (map[index] != 0xFF) { + HidPage.Draw_Stamp(iconset, index, 0, 0, NULL, WINDOW_MAIN); + if (scale) { + + HidPage.Scale((*LogicPage), 0, 0, + x + ((index % w)*(ICON_PIXEL_W/2)), + y + ((index / w)*(ICON_PIXEL_H/2)), + ICON_PIXEL_W, ICON_PIXEL_H, + ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL); + + } else { + HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)), + y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H); + } + } + } +} + + +/*********************************************************************************************** + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * * + * This routine prepares a list of template objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a template object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 05/28/1994 JLB : Only handles real templates now. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void TemplateTypeClass::Prep_For_Add(void) +{ + for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * * + * This support routine is used by the scenario editor to add a template object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the template object. * + * * + * OUTPUT: bool; Was the template object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TemplateClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * * + * This routine will create an object of this type. For certain template objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a template at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this template type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TemplateClass(Type, -1)); +} + + +/*********************************************************************************************** + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * * + * This will return a reference to the TemplateTypeClass requested. * + * * + * INPUT: type -- The template type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the template type class specified. * + * * + * WARNINGS: Be sure to pass a valid type parameter. This routine doesn't check it for * + * legality. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass & TemplateTypeClass::As_Reference(TemplateType type) +{ + return(*TemplateTypes.Ptr(type)); +} + + +COORDINATE TemplateTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/REDALERT/CDFILE.CPP b/REDALERT/CDFILE.CPP new file mode 100644 index 000000000..a34776ec8 --- /dev/null +++ b/REDALERT/CDFILE.CPP @@ -0,0 +1,746 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : CDFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * CDFileClass::Open -- Opens the file object -- with path search. * + * CDFileClass::Open -- Opens the file wherever it can be found. * + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * harderr_handler -- Handles hard DOS errors. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "cdfile.h" +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +/* +** Pointer to the first search path record. +*/ +CDFileClass::SearchDriveType * CDFileClass::First = 0; + +int CDFileClass::CurrentCDDrive = 0; +int CDFileClass::LastCDDrive = 0; +char CDFileClass::RawPath[512] = {0}; + +CDFileClass::CDFileClass(char const *filename) : + IsDisabled(false) +{ + CDFileClass::Set_Name(filename); +// memset (RawPath, 0, sizeof(RawPath)); +} + + +CDFileClass::CDFileClass(void) : + IsDisabled(false) +{ +} +extern int Get_CD_Index (int cd_drive, int timeout); + +/*********************************************************************************************** + * harderr_handler -- Handles hard DOS errors. * + * * + * This routine will handle the low level DOS error trapper. Instead of displaying the * + * typical "Abort, Retry, Ignore" message, it simply returns with the failure code. The * + * cause of the error will fail. The likely case would be with disk I/O. * + * * + * INPUT: * + * * + * OUTPUT: Return the failure code. * + * * + * WARNINGS: Do no processing in this routine that could possibly generate another * + * hard error condition. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +int harderr_handler(unsigned int , unsigned int , unsigned int *) +#else +int harderr_handler(unsigned int , unsigned int , unsigned int __far *) +#endif +{ + return(0); // _HARDERR_FAIL); +} + + +/*********************************************************************************************** + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * * + * This routine will examine the drive specified to see if there is a disk inserted. It * + * can be used for floppy drives as well as for the CD-ROM. * + * * + * INPUT: disk -- The drive number to examine. 0=A, 1=B, etc. * + * * + * OUTPUT: bool; Is a disk inserted into the specified drive? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +int cdecl Is_Disk_Inserted(int disk) +{ +#ifndef OBSOLETE + disk; + return true; +#if (0) + struct find_t fb; + char scan[] = "?:\\*.*"; + + #ifndef WIN32 + _harderr(harderr_handler); // BG: Install hard error handler + #endif + + scan[0] = (char)('A' + disk); + return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0); +#endif +#else + struct { + struct { + char Length; + char Unit; + char Function; + char Status; + char Reserved[8]; + } ReqHdr; + char MediaDescriptor; // Media descriptor byte from BPB. + void *Transfer; // Pointer to transfer address block. + short Length; // Number of bytes to transfer. + short Sector; // Starting sector number. + void *Volume; // Pointer to requested volume. + } IOCTLI; + char status[5]; + + memset(IOCTLI, 0, sizeof(IOCTLI)); + IOCTLI.ReqHdr.Length = 26; + IOCTLI.ReqHdr.Unit = 0; // First CD-ROM controlled by this driver. + //IOCTLI.ReqHdr.Unit = 11; // Hard coded for K: + IOCTLI.ReqHdr.Function = 3; // IOCTL read + IOCTLI.Transfer = &status[0]; + IOCTLI.Length = sizeof(status); + status[0] = 6; // Fetch device status code. + _AX = 0x440D; + _CX = 0x0003; + geninterrupt(0x21); + return(!(_AX & (1<<11))); +#endif +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file object -- with path search. * + * * + * This will open the file object, but since the file object could have been constructed * + * with a pathname, this routine will try to find the file first. For files opened for * + * writing, then use the existing filename without performing a path search. * + * * + * INPUT: rights -- The access rights to use when opening the file * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(int rights) +{ + return(BufferIOFileClass::Open(rights)); +} +/*********************************************************************************************** + * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:01AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Refresh_Search_Drives (void) +{ + Clear_Search_Drives(); + Set_Search_Drives(RawPath); +} + +#if 0 +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = false; + int empty = false; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + char const * ptr = strtok(pathlist, ";"); + while (ptr) { + char path[PATH_MAX]; // Working path buffer. + SearchDriveType *srch; // Working pointer to path object. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + */ + if (strncmp(path, "?:", 2) == 0) { + #ifndef WIN32 + GetCDClass temp; + int cd = temp.GetCDDrive(); + #else + int cd = 10; + #endif + found = cd; + empty = !Is_Disk_Inserted(cd); + if (!found || empty) goto nextpath; + path[0] = (char)('A' + cd); + } + + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + if (srch) { + found = true; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } + } + + /* + ** Find the next path string and resubmit. + */ +nextpath: + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} +#endif + + +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + * 05/21/1996 ST : Modified to recognise multiple CD drives * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = FALSE; + int empty = FALSE; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + /* + ** Save the path as it was passed in so we can parse it again later. + ** Check for the case where RawPath was passed in. + */ + if (pathlist != RawPath) { + strcat (RawPath, ";"); + strcat (RawPath, pathlist); + } + + char const * ptr = strtok(pathlist, ";"); + while (ptr != NULL) { + if (strlen(ptr) > 0) { + + char path[MAX_PATH]; // Working path buffer. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + ** Adds an extra entry for each CD drive in the system that has a C&C disc inserted. + ** ST - 5/21/96 4:40PM + */ + if (strncmp(path, "?:", 2) == 0) { + if (CurrentCDDrive) { + found = true; + + /* + ** If the drive has a C&C CD in it then add it to the path + */ + if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0) { + path[0] = (char)(CurrentCDDrive + 'A'); + Add_Search_Drive(path); + } + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + continue; + } + + found = true; + Add_Search_Drive(path); + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} + + +/*********************************************************************************************** + * CDFC::Add_Search_Drive -- Add a new path to the search path list * + * * + * * + * * + * INPUT: path * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 10:12AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Add_Search_Drive(char *path) +{ + SearchDriveType *srch; // Working pointer to path object. + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } +} + + +/*********************************************************************************************** + * CDFC::Set_CD_Drive -- sets the current CD drive letter * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:39AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Set_CD_Drive (int drive) +{ + LastCDDrive = CurrentCDDrive; + CurrentCDDrive = drive; +} + + +/*********************************************************************************************** + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * * + * Use this routine to clear out any previous path(s) set with Set_Search_Drives() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void CDFileClass::Clear_Search_Drives(void) +{ + SearchDriveType * chain; // Working pointer to path chain. + + chain = First; + while (chain) { + SearchDriveType *next; + + next = (SearchDriveType *)chain->Next; + if (chain->Path) { + free((char *)chain->Path); + } + delete chain; + + chain = next; + } + First = 0; +} + + +/*********************************************************************************************** + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * * + * This routine will scan all the directories specified in the path list and if the file * + * was found in one of the directories, it will set the filename to a composite of the * + * correct directory and the filename. It is used to allow path searching when searching * + * for files. Typical use is to support CD-ROM drives. This routine examines the current * + * directory first before scanning through the path list. If after scanning the entire * + * path list, the file still could not be found, then the file object's name is set with * + * just the raw filename as passed to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +char const * CDFileClass::Set_Name(char const *filename) +{ + /* + ** Try to find the file in the current directory first. If it can be found, then + ** just return with the normal file name setting process. Do the same if there is + ** no multi-drive search path. + */ + BufferIOFileClass::Set_Name(filename); + if (IsDisabled || !First || BufferIOFileClass::Is_Available()) return(File_Name()); + + /* + ** Attempt to find the file first. Check the current directory. If not found there, then + ** search all the path specifications available. If it still can't be found, then just + ** fall into the normal raw file filename setting system. + */ + SearchDriveType * srch = First; + + while (srch) { + char path[_MAX_PATH]; + + /* + ** Build a pathname to search for. + */ + strcpy(path, srch->Path); + strcat(path, filename); + + /* + ** Check to see if the file could be found. The low level Is_Available logic will + ** prompt if necessary when the CD-ROM drive has been removed. In all other cases, + ** it will return false and the search process will continue. + */ + BufferIOFileClass::Set_Name(path); + if (BufferIOFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** It wasn't found, so try the next path entry. + */ + srch = (SearchDriveType *)srch->Next; + } + + /* + ** At this point, all path searching has failed. Just set the file name to the + ** plain text passed to this routine and be done with it. + */ + BufferIOFileClass::Set_Name(filename); + return(File_Name()); +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file wherever it can be found. * + * * + * This routine is similar to the RawFileClass open except that if the file is being * + * opened only for READ access, it will search all specified directories looking for the * + * file. If after a complete search the file still couldn't be found, then it is opened * + * using the normal BufferIOFileClass system -- resulting in normal error procedures. * + * * + * INPUT: filename -- Pointer to the override filename to supply for this file object. It * + * would be the base filename (sans any directory specification). * + * * + * rights -- The access rights to use when opening the file. * + * * + * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different * + * than requested. The location of the file can be determined by examining the * + * filename of this file object. The filename will contain the complete * + * pathname used to open the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(char const *filename, int rights) +{ + CDFileClass::Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!filename) { + Error(ENOENT, false); + } + + /* + ** If writing is requested, then multiple drive searching is not performed. + */ + if (IsDisabled || rights == WRITE) { + + BufferIOFileClass::Set_Name( filename ); + return( BufferIOFileClass::Open( rights ) ); + } + + /* + ** Perform normal multiple drive searching for the filename and open + ** using the normal procedure. + */ + Set_Name(filename); + return(BufferIOFileClass::Open(rights)); +} + + +const char *CDFileClass::Get_Search_Path(int index) +{ + if (First == NULL) { + return NULL; + } + + SearchDriveType *sd = First; + + for (int i = 0; i <= index; i++) { // We want to loop once, even if index==0 + + if (i == index) { + return sd->Path; + } + + sd = (SearchDriveType *)sd->Next; + if (sd == NULL) { + return NULL; + } + } + + return NULL; +} + + + +#ifdef NEVER +/* +** Get the drive letters if the CD's online */ +*/ +WORD cdecl GetCDDrive(VOID) +{ + _ES = FP_SEG(&cdDrive[0]); + _BX = FP_OFF(&cdDrive[0]); + _AX = 0x150d; + geninterrupt(0x2F); + return((WORD)(*cdDrive)); +} +#endif + +#if 0 +int Get_CD_Drive(void) +{ +#ifdef WIN32 + return(10); +#else + +#ifdef NEVER + for (int index = 0; index < 26; index++) { + union REGS regs; + + regs.w.ax = 0x150B; + regs.w.bx = 0; + regs.w.cx = index; + int386(0x2F, ®s, ®s); + if (regs.w.bx == 0xADAD) { + return(index); + } + } + return(0); +#else + GetCDClass temp; + return(temp.GetCDDrive()); +#endif +#endif +} + +#endif diff --git a/REDALERT/CDFILE.H b/REDALERT/CDFILE.H new file mode 100644 index 000000000..57f9e2098 --- /dev/null +++ b/REDALERT/CDFILE.H @@ -0,0 +1,116 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood LIbrary * + * * + * File Name : CDFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CDFILE_H +#define CDFILE_H + +#include +#include "bfiofile.h" + +/* +** This class is derived from the BufferIOFileClass. This class adds the functionality of searching +** across multiple directories or drives. It is designed for the typical case of a CD-ROM game +** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM. +** Searching for the file occurs by first examining the current directory. If the file does not +** exist there, then all the paths available are examined in turn until the file can be found. +** For opening files to write, only the current directory is examined. The directory search order +** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path +** string is the same as the DOS path string. +*/ +class CDFileClass : public BufferIOFileClass +{ + public: + CDFileClass(char const *filename); + CDFileClass(void); + virtual ~CDFileClass(void) {}; + + virtual char const * Set_Name(char const *filename); + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + + void Searching(int on) {IsDisabled = !on;}; + + static bool Is_There_Search_Drives(void) {return(First != NULL);}; + static int Set_Search_Drives(char * pathlist); + static void Add_Search_Drive(char *path); + static void Clear_Search_Drives(void); + static void Refresh_Search_Drives(void); + static void Set_CD_Drive(int drive); + static int Get_CD_Drive(void) {return(CurrentCDDrive);}; + static int Get_Last_CD_Drive(void) {return(LastCDDrive);}; + + // RawPath will overflow if we keep setting the path. ST - 1/23/2019 11:12AM + static void Reset_Raw_Path(void) { RawPath[0] = 0; } + + // Need to access the paths. ST - 3/15/2019 2:14PM + static const char *Get_Search_Path(int index); + + private: + + /* + ** Is multi-drive searching disabled for this file object? + */ + unsigned IsDisabled:1; + + /* + ** This is the control record for each of the drives specified in the search + ** path. There can be many such search paths available. + */ + typedef struct { + void * Next; // Pointer to next search record. + char const * Path; // Pointer to path string. + } SearchDriveType; + + /* + ** This points to the first path record. + */ + static SearchDriveType * First; + /* + ** This is a copy of the unparsed search path list + */ + static char RawPath[512]; + + /* + ** The drive letter of the current cd drive + */ + static int CurrentCDDrive; + + /* + ** The drive letter of the last used CD drive + */ + static int LastCDDrive; +}; + +#endif + diff --git a/REDALERT/CELL.CPP b/REDALERT/CELL.CPP new file mode 100644 index 000000000..a8e0ed6f1 --- /dev/null +++ b/REDALERT/CELL.CPP @@ -0,0 +1,3299 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CELL.CPP 4 3/14/97 1:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * CellClass::CellClass -- Constructor for cell objects. * + * CellClass::Cell_Building -- Return with building at specified cell. * + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * CellClass::Cell_Coord -- Returns the coordinate of this cell. * + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over* + * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * CellClass::Read -- Reads a particular cell value from a save game file. * + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * + * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:50PM +*/ +#include "SidebarGlyphx.h" + + +/*********************************************************************************************** + * CellClass::CellClass -- Constructor for cell objects. * + * * + * A cell object is constructed into an empty state. It contains no specific objects, * + * templates, or overlays. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/09/1994 JLB : Created. * + * 02/20/1996 JLB : Uses initializer list. * + *=============================================================================================*/ +CellClass::CellClass(void) : + ID(Map.ID(this)), + IsPlot(false), + IsCursorHere(false), + IsMapped(false), + IsVisible(false), + IsWaypoint(false), + IsRadarCursor(false), + IsFlagged(false), + IsToShroud(false), + Jammed(0), + Trigger(NULL), + TType(TEMPLATE_NONE), + TIcon(0), + Overlay(OVERLAY_NONE), + OverlayData(0), + Smudge(SMUDGE_NONE), + SmudgeData(0), + Owner(HOUSE_NONE), + InfType(HOUSE_NONE), + OccupierPtr(0), + Land(LAND_CLEAR), + OverrideLand(LAND_NONE), + IsMappedByPlayerMask(0), + IsVisibleByPlayerMask(0) +{ + for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) { + Zones[zone] = 0; + } + Flag.Composite = 0; + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + Overlapper[index] = 0; + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * * + * Use this routine to determine what radar color to render a radar * + * pixel with. This routine is called many many times to render the * + * radar map, so it must be fast. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the color to display the radar pixel with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * + *=============================================================================================*/ +int CellClass::Cell_Color(bool override) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + BuildingClass * object = Cell_Building(); + if (object && !object->Class->IsInvisible) { + return(ColorRemaps[object->House->RemapColor].Bar); + } + + if (override) { + return(TBLACK); + } + if (LastTheater == THEATER_SNOW) { + return(::SnowColor[Land_Type()]); + } else { + return(::GroundColor[Land_Type()]); + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * * + * Returns an object located in the cell. If there is a * + * building present, it returns a pointer to that, otherwise it returns * + * a pointer to one of the units there. If nothing is present in the * + * specified cell, then it returns NULL. * + * * + * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * + * the desired object within the cell. * + * * + * OUTPUT: Returns a pointer to a building or unit located in cell. If * + * nothing present, just returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +TechnoClass * CellClass::Cell_Techno(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object; + COORDINATE click; // Coordinate of click relative to cell corner. + TechnoClass * close = NULL; + long distance = 0; // Recorded closest distance. + + /* + ** Create a coordinate value that represent the pixel location within the cell. This is + ** actually the lower significant bits (leptons) of a regular coordinate value. + */ + click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); + + if (Cell_Occupier()) { + object = Cell_Occupier(); + while (object) { + if (object->Is_Techno()) { + COORDINATE coord = Coord_Fraction(object->Center_Coord()); + long dist = Distance(coord, click); + if (!close || dist < distance) { + close = (TechnoClass *)object; + distance = dist; + } + } + object = object->Next; + } + } + return(close); +} + + +/*************************************************************************** + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * * + * INPUT: RTTIType the RTTI type we are searching for * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 PWG : Created. * + * 06/12/1995 JLB : Returns object class pointer. * + *=========================================================================*/ +ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(rtti != RTTI_NONE); + + ObjectClass * object = Cell_Occupier(); + + while (object != NULL) { + if (object->What_Am_I() == rtti) { + return(object); + } + object = object->Next; + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Cell_Building -- Return with building at specified cell. * + * * + * Given a cell, determine if there is a building associated * + * and return with a pointer to this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building associated with the * + * cell. If there is no building associated, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +BuildingClass * CellClass::Cell_Building(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * * + * This routine is used to determine the terrain object (if any) that * + * overlaps this cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object that overlaps * + * this cell. If there is no terrain object present, then NULL * + * is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass * CellClass::Cell_Terrain(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * * + * This routine is used to determine which object is to be selected * + * by a player click upon the cell. Not all objects that overlap the * + * cell are selectable by the player. This routine sorts out which * + * is which and returns with the appropriate object pointer. * + * * + * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * + * selecting the object within the cell. This plays a role in those cases * + * where several objects (such as infantry) exist within the same cell. * + * * + * OUTPUT: Returns with pointer to the object clickable within the * + * cell. NULL is returned if there is no clickable object * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Object(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * ptr; + + /* + ** Hack so that aircraft landed on helipads can still be selected if directly + ** clicked on. + */ + ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); + if (ptr) { + return(ptr); + } + + ptr = Cell_Techno(x, y); + if (ptr) { + return(ptr); + } + ptr = Cell_Terrain(); + if (ptr) return(ptr); + return(ptr); +} + + +/*********************************************************************************************** + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * * + * This is a low level routine that marks all objects that overlap this * + * cell to be redrawn. It is necessary to call this routine whenever * + * the underlying icon has to be redrawn. * + * * + * INPUT: forced -- Should this redraw be forced even if flags * + * indicate that it would be redundant? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/20/1994 JLB : Simplified to use object pointers. * + * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * + *=============================================================================================*/ +void CellClass::Redraw_Objects(bool forced) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + + if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { + + /* + ** Flag the icon to be redrawn. + */ + Map.Flag_Cell(cell); + + /* + ** Flag the main object in the cell to be redrawn. + */ + if (Cell_Occupier() != NULL) { + ObjectClass * optr = Cell_Occupier(); + while (optr != NULL && optr->IsActive) { + +#ifdef SORTDRAW + if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) { + optr->Mark(MARK_CHANGE); + } +#else + optr->Mark(MARK_CHANGE); +#endif + if (optr->Next != NULL && !optr->Next->IsActive) { + optr->Next = NULL; + } + optr = optr->Next; + } + } + +#ifdef SORTDRAW + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index]) { + assert(Overlapper[index]->IsActive); + if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#else + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + if (!Overlapper[index]->IsActive) { + Overlapper[index] = NULL; + } else { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#endif + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * * + * This determines if the cell can become a proper foundation for * + * building placement. * + * * + * INPUT: loco -- The locomotion of the object trying to consider if this cell is * + * generally clear. Buildings use the value of SPEED_NONE. * + * * + * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/25/1996 JLB : Handles different locomotion types. * + * 10/05/1996 JLB : Checks for crushable walls and crushable object. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Build(SpeedType loco) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** During scenario initialization, passability is always guaranteed. + */ + if (ScenarioInit) return(true); + + /* + ** If there is an object there, then don't allow building. + */ + if (Cell_Object() != NULL) { + return(false); + } + + /* + ** Prevents a building from being placed over a flag object. + */ +#ifdef FIXIT_FLAG_CHECK + if (IsFlagged) { + return(false); + } +#endif + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes. In normal game mode, all overlays are not buildable. + */ + if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) { + return(false); + } + + /* + ** Building over a bib is not allowed. + */ + if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) { + return(false); + } + + /* + ** Building on certain kinds of terrain is prohibited -- bridges in particular. + ** If the locomotion type is SPEED_NONE, then this check is presumed to be + ** for the purposes of building. + */ + if (loco == SPEED_NONE) { + if (Is_Bridge_Here()) { + return(false); + } + + return(::Ground[Land_Type()].Build); + + } else { + + if (::Ground[Land_Type()].Cost[loco] == fixed(0)) { +// if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) { + return(false); + } + return(true); + } +} + + +/*********************************************************************************************** + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * * + * This routine recalculates the ground type in the cell. The speeds the find path * + * algorithm and other determinations of the cell type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 06/20/1994 JLB : Knows about template pointer in cell object. * + *=============================================================================================*/ +void CellClass::Recalc_Attributes(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Special override for interior terrain set so that a non-template or a clear template + ** is equivalent to impassable rock. + */ + if (LastTheater == THEATER_INTERIOR) { + if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) { + Land = LAND_ROCK; + return; + } + } + + /* + ** Check for wall effects. + */ + if (Overlay != OVERLAY_NONE) { + Land = OverlayTypeClass::As_Reference(Overlay).Land; + if (Land != LAND_CLEAR) return; + } + + /* + ** If there is a template associated with this cell, then fetch the + ** land type given the template type and icon number. + */ + if (TType != TEMPLATE_NONE && TType != 255) { + TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType); + Land = ttype->Land_Type(TIcon); + return; + } + + /* + ** No template is the same as clear terrain. + */ + Land = LAND_CLEAR; +} + + +/*********************************************************************************************** + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * * + * This routine is used to mark the cell as being occupied by the specified object. * + * * + * INPUT: object -- The object that is to occupy the cell * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Occupy_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass * optr; + + if (object == NULL) return; + + /* + ** Always add buildings to the end of the occupation chain. This is necessary because + ** the occupation chain is a single list even though buildings occupy more than one + ** cell. If more than one building is allowed to occupy the same cell, then this chain + ** logic will fail. + */ + if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) { + optr = Cell_Occupier(); + while (optr->Next != NULL) { + assert(optr != object); + assert(optr->What_Am_I() != RTTI_BUILDING); + optr = optr->Next; + } + optr->Next = object; + object->Next = 0; + } else { + object->Next = Cell_Occupier(); + OccupierPtr = object; + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + // Changes for client/server multiplayer. ST - 8/2/2019 2:51PM + //if (IsMapped || Session.Type != GAME_NORMAL) { + // object->Revealed(PlayerPtr); + //} + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (IsMapped || Session.Type != GAME_NORMAL) { + object->Revealed(PlayerPtr); + } + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + if (Is_Visible(house_type)) { + HouseClass *house = HouseClass::As_Pointer(house_type); + object->Revealed(house); + } + } + } + + /* + ** Special occupy bit set. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = true; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = true; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = true; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * * + * This routine will lift the object from the cell and free the cell to be occupied by * + * another object. Only if the cell was previously marked with the object specified, will * + * the object be lifted off. This routine is the counterpart to Occupy_Down(). * + * * + * INPUT: object -- The object that is being lifted off. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * + *=============================================================================================*/ +void CellClass::Occupy_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + if (object == NULL) return; + + ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain. + + if (optr == object) { + OccupierPtr = object->Next; + object->Next = 0; + } else { + bool found = false; + while (optr != NULL) { + if (optr->Next == object) { + optr->Next = object->Next; + object->Next = 0; + found = true; + break; + } + optr = optr->Next; + } +// assert(found); + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** Special occupy bit clear. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = false; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = false; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = false; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * * + * Most game objects can often have their graphic imagery spill into more than one cell * + * even though they are considered to "occupy" only one cell. All cells overlapped are * + * flagged by this routine. Using this information it is possible to keep the tactical map * + * display correct. * + * * + * INPUT: object -- The object to mark as overlapping this cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 07/04/1995 JLB : Ensures that buildings are always marked down. * + *=============================================================================================*/ +void CellClass::Overlap_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass ** ptr = 0; + + if (!object) return; + + int index; + for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) return; + if (!Overlapper[index]) ptr = &Overlapper[index]; + } + + /* + ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody + ** else out in this case. + */ + if (!ptr && object->What_Am_I() == RTTI_BUILDING) { + for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { + switch (Overlapper[index]->What_Am_I()) { + case RTTI_BUILDING: + case RTTI_TERRAIN: + break; + + default: + Overlapper[index] = object; + index = ARRAY_SIZE(Overlapper); + break; + } + } + } + if (ptr) *ptr = object; + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsMapped) { + object->Revealed(PlayerPtr); + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * * + * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * + * specified unit on the cell. * + * * + * INPUT: object -- The object to remove the overlap flag for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Overlap_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) { + Overlapper[index] = 0; + break; + } + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * * + * This routine will determine if a unit is occupying the cell and if so, return a pointer * + * to it. If there is no unit occupying the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * CellClass::Cell_Unit(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * * + * Call this routine to query and return a pointer to a vessel located in the cell. If * + * there is no vessel present, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the vessel class object if one is present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * CellClass::Cell_Vessel(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((VesselClass*)Cell_Find_Object(RTTI_VESSEL)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * + * * + * This routine examines the cell and returns a pointer to the first infantry unit * + * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * CellClass::Cell_Infantry(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); +} + + +#ifdef SORTDRAW +static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy) +{ + int & px = WindowList[WINDOW_PARTIAL][WINDOWX]; + int & py = WindowList[WINDOW_PARTIAL][WINDOWY]; + int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH]; + int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT]; + int & tx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int & ty = WindowList[WINDOW_TACTICAL][WINDOWY]; + int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + px = cellx + tx; + py = celly + ty; + pw = CELL_PIXEL_W; + ph = CELL_PIXEL_H; + + if (px < tx) { + pw -= tx - px; + px = tx; + } + if (pw < 1) return(false); + + if (py < ty) { + ph -= ty - py; + py = ty; + } + if (ph < 1) return(false); + + if (px + pw > tx + tw) { + pw -= (px + pw) - (tx + tw); + } + if (pw < 1) return(false); + + if (py + ph > ty + th) { + ph -= (py + ph) - (ty + th); + } + if (ph < 1) return(false); + + drawx = drawx - (px-tx); + drawy = drawy - (py-ty); + return(true); +} + + +static int _ocompare(const void * left, const void * right) +{ + COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y(); + COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y(); + if (lcoord < rcoord) return(-1); + if (lcoord > rcoord) return(1); + return(0); +} +#endif + + +/*********************************************************************************************** + * CellClass::Get_Template_Info -- Get some info about a template for external use * + * * + * * + * * + * * + * INPUT: Ref to info required * + * * + * OUTPUT: True if image info available * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/10/2019 5:57PM ST : Created. * + *=============================================================================================*/ +bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data) +{ + TemplateTypeClass const *ttype = NULL; + + if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { // Not sure why it's checking for 255 here since that's a valid tile type. ST - 6/4/2019 + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } + else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + if (ttype) { + + strcpy(template_name, ttype->IniName); + image_data = (void*)ttype->ImageData; + + return true; + } + + return false; +} + + + +/*********************************************************************************************** + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * * + * This is the gruntwork cell rendering code. It draws the cell at the screen location * + * specified. This routine doesn't draw any overlapping or occupying units. It only * + * deals with the ground (cell) layer -- icon level. * + * * + * INPUT: x,y -- The screen coordinates to render the cell imagery at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 08/21/1994 JLB : Revised for simple template objects. * + * 11/01/1994 BRR : Updated placement cursor; draws actual object * + * 11/14/1994 BRR : Added remapping code to show passable areas * + * 12/02/1994 BRR : Added trigger display * + * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * + * 04/25/1995 JLB : Smudges drawn BELOW overlays. * + * 07/22/1996 JLB : Objects added to draw process. * + *=============================================================================================*/ +void CellClass::Draw_It(int x, int y, bool objects) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!objects) { + BStart(BENCH_CELL); + + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + CELL cell = Cell_Number(); + void * remap = NULL; + #ifdef SCENARIO_EDITOR + TemplateTypeClass * tptr; +// TriggerClass * trig; + int i; + char waypt[3]; + #endif + + CellCount++; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + #ifdef CHEAT_KEYS + /* + ** Draw the stamp of the template. + */ + if (Debug_Icon) { + LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); + FontXSpacing -= 2; + Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW, + Cell_Y(cell), Cell_X(cell), + //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1, + Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER], + Overlay, OverlayData + ); + FontXSpacing += 2; + } else { + #endif + + #ifdef SCENARIO_EDITOR + /* + ** Set up the remap table for this icon. + */ + if (Debug_Map && Debug_Passable) { + if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && + Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable + remap = DisplayClass::FadingRed; + } else { + if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable + remap = DisplayClass::FadingGreen; + } else { + remap = DisplayClass::FadingYellow; // moderately passable + } + } + } + #endif + + /* + ** This is the underlying terrain icon. + */ + if (ttype->Get_Image_Data()) { + LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + if (remap) { + LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); + } + } + + #ifdef SCENARIO_EDITOR + /* + ** Draw the map editor's "current" cell. This is the cell that can be + ** assigned attributes such as tag labels. + ** This must be draw before the placement cursor, but after drawing the + ** objects in the cell. + */ + if (Debug_Map && CurrentCell == Cell_Number()) { + LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); + } + #endif + + /* + ** Redraw any smudge. + */ + if (Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); + IsTheaterShape = false; + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map) { + /* + ** Draw the cell's Trigger mnemonic, if it has a trigger + */ + if (Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + + /* + ** Draw the cell's Waypoint designation if there is one. + */ + if (IsWaypoint) { + for (i = 0; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i] == Cell_Number()) { + if (i < 26) { + waypt[0] = 'A' + i; + waypt[1] = 0; + } else { + waypt[0] = 'A' + (i/26)-1; + waypt[1] = 'A' + (i % 26); + waypt[2] = 0; + } + Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, + Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW); + break; + } + } + if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) { + Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) { + Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + } + } + #endif + + /* + ** Draw the placement cursor: + ** - First, draw the hash-mark cursor, so it will appear underneath + ** any cursor being drawn + ** - If the PendingObject is a template, overlay, or smudge, draw it + ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it + */ + if (IsCursorHere) { + SpeedType loco = SPEED_NONE; + if (Map.PendingObjectPtr) { + if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) { + BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr); + loco = obj->Class->Speed; + // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD || + // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT; + } + } + + /* + ** Draw the hash-mark cursor: + */ + if (Map.ProximityCheck && Is_Clear_To_Build(loco)) { + LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL); + } else { + LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL); + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map && Map.PendingObject) { + + switch (Map.PendingObject->What_Am_I()) { + + /* + ** Draw a template: + ** - Compute the icon offset of this cell for this template, using + ** ZoneCell+ZoneOffset to get the upper-left corner of the placement + ** cursor + ** - Draw the icon + */ + case RTTI_TEMPLATETYPE: + tptr = (TemplateTypeClass *)Map.PendingObject; + if (tptr->Get_Image_Data()) { + icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + + (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * + tptr->Width; + LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + } + break; + + /* + ** Draw an overlay; just use the existing 'OverlayData' even though + ** it means nothing. + */ + case RTTI_OVERLAYTYPE: + OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); + break; + + /* + ** Draw a smudge + */ + case RTTI_SMUDGETYPE: + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + break; + + default: + break; + } + } + #endif + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (IsFlagged) { + void const * flag_remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, REMAP_NORMAL); + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); + } + + #ifdef CHEAT_KEYS + } + #endif + BEnd(BENCH_CELL); + } + +#ifdef SORTDRAW + if (objects) { + BStart(BENCH_OBJECTS); + + /* + ** Build a list of objects to draw into a working buffer. There is a + ** big presumption here -- it is presumed that if the cell is to be + ** redrawn, then all objects in the cell should properly be flagged to + ** be redrawn as well. Normally, this isn't a problem, but for subs + ** the IsToDisplay flag MUST REMAIN SET. This is because there is a + ** hack overpass after the cells are redrawn so that subs can be + ** redrawn separately. + */ + ObjectClass * optr[20 + ARRAY_SIZE(Overlapper)]; + int count = 0; + ObjectClass * object = Cell_Occupier(); + while (object != NULL) { + if (!object->IsActive) break; + optr[count] = object; + object->IsToDisplay = true; + object = object->Next; + count++; + } + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + object = Overlapper[index]; + if (object != NULL && object->IsActive) { + object->IsToDisplay = true; + optr[count] = object; + count++; + } + } + + /* + ** Sort the object list so that objects will be drawn from + ** back to front. + */ + switch (count) { + + /* + ** If there are zero or one object, then sorting is + ** unnecessary. + */ + case 0: + case 1: + break; + + /* + ** Two objects can be sorted with a single compare and swap. + */ + case 2: + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + break; + + /* + ** Three objects can be sorted with three compares and swaps. + */ + case 3: + if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[0], optr[2]); + } + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[1], optr[2]); + } + break; + + /* + ** Large number of objects can be effeciently sorted by using + ** a quicksort. + */ + default: + qsort(optr, count, sizeof(optr[0]), _ocompare); + break; + } + + /* + ** Draw any objects that happen to be in or overlapping this cell. + */ + for (int index = 0; index < count; index++) { + object = optr[index]; + int xx,yy; + if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) { + if (_Calc_Partial_Window(x, y, xx, yy)) { + object->Draw_It(xx, yy, WINDOW_PARTIAL); + if (Debug_Map) { + object->IsToDisplay = true; + } else { + object->IsToDisplay = false; + } + } + object->IsToDisplay = false; + } + } + BEnd(BENCH_OBJECTS); + } +#endif + +} + + +/*********************************************************************************************** + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * * + * This routine examines the cells around the current one and from this, determines what * + * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * + * for redraw if the icon changed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Concrete_Calc(void) +{ +#ifdef OBSOLETE + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; + static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; + FacingType * ptr; // Working pointer into adjacent cell list. + int index; // Constructed bit index. + int icon; // Icon number. + bool isodd; // Is this for the odd column? + +#define OF_N 0x01 +#define OF_NE 0x02 +#define OF_E 0x04 +#define OF_SE 0x08 +#define OF_S 0x10 + +#define EF_N 0x01 +#define EF_NW 0x10 +#define EF_W 0x08 +#define EF_SW 0x04 +#define EF_S 0x02 + + /* + ** Determine if the even or odd row logic is necessary. + */ + isodd = ((Cell_Number() & 0x01) != 0); + + /* + ** Fetch correct pointer depending on whether this is for an + ** odd or even row. + */ + ptr = (isodd) ? _odd : _even; + + /* + ** Build an index according to the presence of concrete in the special + ** adjacent cells. This is a short list of adjacent cell flags since + ** only 5 adjacent cells need to be examined. The choice of which 5 + ** depends on whether this is for an even or odd column. + */ + index = 0; + for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { + CellClass & cellptr = Adjacent_Cell(*ptr++); + + if (cellptr.Overlay == OVERLAY_CONCRETE) { + index |= (1< 0 && Land == LAND_TIBERIUM) { + if (OverlayData+1 > levels) { + OverlayData -= levels; + reducer = levels; + } else { + Overlay = OVERLAY_NONE; + reducer = OverlayData; + OverlayData = 0; + Recalc_Attributes(); + } + } + return(reducer); +} + + +/*********************************************************************************************** + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * * + * This routine will change the wall shape used for a wall if it's damaged. * + * * + * INPUT: damage -- The number of damage points the wall was hit with. If this value is * + * -1, then the entire wall at this cell will be destroyed. * + * * + * OUTPUT: bool; Was the wall destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BWG : Created. * + * 03/19/1995 JLB : Updates cell information if wall was destroyed. * + * 10/06/1996 JLB : Updates zone as necessary. * + *=============================================================================================*/ +int CellClass::Reduce_Wall(int damage) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (Overlay != OVERLAY_NONE) { + bool destroyed = false; + OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); + + if (wall.IsWall) { + + /* + ** If the damage was great enough to ensure wall destruction, reduce the wall by one + ** level (no more). Otherwise determine wall reduction based on a percentage chance + ** proportional to the damage received and the wall's strength. + */ + if (damage == -1 || damage >= wall.DamagePoints) { + destroyed = true; + } else { + destroyed = Random_Pick(0, wall.DamagePoints) < damage; + } + + /* + ** If the wall is destroyed, destroy it and check for any adjustments to + ** adjacent walls. + */ + if (destroyed) { + OverlayData+=16; + if (damage == -1 || + (OverlayData>>4) >= wall.DamageLevels || + ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) { + + Owner = HOUSE_NONE; + Overlay = OVERLAY_NONE; + OverlayData = 0; + Recalc_Attributes(); + Redraw_Objects(); + Adjacent_Cell(FACING_N).Wall_Update(); + Adjacent_Cell(FACING_W).Wall_Update(); + Adjacent_Cell(FACING_S).Wall_Update(); + Adjacent_Cell(FACING_E).Wall_Update(); + Detach_This_From_All(As_Target()); + + /* + ** The zone calculation changes now for non-crushable zone sensitive + ** travellers. + */ + if (wall.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * * + * INPUT: * + * coord COORDINATE to compute index for * + * * + * OUTPUT: * + * index into StoppingCoord that's closest to this coord * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1994 BR : Created. * + * 12/10/1994 JLB : Uses alternate sub-position algorithm. * + *=============================================================================================*/ +int CellClass::Spot_Index(COORDINATE coord) +{ + COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell. + + /* + ** If the coordinate is close enough to the center of the cell, then return + ** the center position index. + */ + if (Distance(rel, (COORDINATE)0x00800080L) < 60) { + return(0); + } + + /* + ** Since the center cell position has been eliminated, a simple comparison + ** as related to the center of the cell can be used to determine the sub + ** position. Take advantage of the fact that the sub positions are organized + ** from left to right, top to bottom. + */ + int index = 0; + if (Coord_X(rel) > 0x80) index |= 0x01; + if (Coord_Y(rel) > 0x80) index |= 0x02; + return(index+1); +} + + +/*********************************************************************************************** + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * * + * Similar to the CellClass::Free_Spot; this routine finds the spot in * + * the cell closest to the given coordinate, and returns the COORDINATE of * + * that spot if it's available, NULL if it's not. * + * * + * INPUT: * + * coord coordinate to check (only sub cell position examined) * + * * + * any -- If only the closest spot is desired regardless of whether it is free or * + * not, then this parameter will be true. * + * * + * OUTPUT: * + * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell * + * coordinate data portions of the coordinate passed in. Only the lower sub-cell * + * data is altered. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 12/10/1994 JLB : Picks best of closest stopping positions. * + * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * + *=============================================================================================*/ +COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int spot_index = Spot_Index(coord); + + /* + ** This precalculated sequence table records the closest spots to any given spot. Sequential + ** examination of these spots for availability ensures that the closest available one is + ** discovered first. + */ + static unsigned char _sequence[5][4] = { + {1,2,3,4}, + {0,2,3,4}, + {0,1,4,3}, + {0,1,4,2}, + {0,2,3,1} + }; + + /* + ** In the case of the center coordinate being requested, but is occupied, then all other + ** sublocations are equidistant. Instead of picking a static sequence of examination, the + ** order is mixed up by way of this table. + */ + static unsigned char _alternate[4][4] = { + {1,2,3,4}, + {2,3,4,1}, + {3,4,1,2}, + {4,1,2,3}, + }; + coord = Coord_Whole(coord); + + /* + ** Cells occupied by buildings or vehicles don't have any free spots. + */ + if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { + return(NULL); + } + + /* + ** If just the nearest position is desired regardless of whether occupied or not, + ** then just return with the stopping coordinate value. + */ + if (any || Is_Spot_Free(spot_index)) { + return(Coord_Add(coord, StoppingCoordAbs[spot_index])); + } + + /* + ** Scan through all available sub-locations in the cell in order to determine + ** the closest one to the coordinate requested. Use precalculated table so that + ** when the first free position is found, bail. + */ + unsigned char * sequence; + if (spot_index == 0) { + sequence = &_alternate[Random_Pick(0, 3)][0]; + } else { + sequence = &_sequence[spot_index][0]; + } + for (int index = 0; index < 4; index++) { + int pos = *sequence++; + + if (Is_Spot_Free(pos)) { + return(Coord_Add(coord, StoppingCoordAbs[pos])); + } + } + + /* + ** No free spot could be found so return a NULL coordinate. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * * + * This support routine will determine what the clear icon number would be for the cell. * + * The icon number is determined by converting the cell number into an index into a * + * lookup table. This yields what appears to be a randomized map without the necessity of * + * generating and recording randomized map numbers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * + *=============================================================================================*/ +int CellClass::Clear_Icon(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2)); +// return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C)); +} + + +/*********************************************************************************************** + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * * + * This routine is called whenever a great, but slow moving, threat is presented to the * + * occupants of a cell. The occupants will, in most cases, stop what they are doing and * + * try to get out of the way. * + * * + * INPUT: threat -- The coordinate source of the threat. * + * * + * forced -- If this threat is so major that the occupants should stop what * + * they are doing, then this parameter should be set to true. * + * * + * nokidding -- Override the scatter to also affect human controlled objects. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = NULL; + + object = Cell_Occupier(); + while (object != NULL) { + + /* + ** Special check to make sure that friendly units never scatter. + */ + if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) { + object->Scatter(threat, forced, nokidding); + } + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * * + * Use this routine to return a reference to the adjacent cell in the direction specified. * + * * + * INPUT: face -- The direction to use when determining the adjacent cell. * + * * + * OUTPUT: Returns with a reference to the adjacent cell. * + * * + * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +CellClass const & CellClass::Adjacent_Cell(FacingType face) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if ((unsigned)face >= FACING_COUNT) { + return(*this); + } + + //The top row doesn't have any adjacent cells to the north. - LLL + if (ID < MAP_CELL_W && (face == FACING_N || face == FACING_NE || face == FACING_NW)) { + return (*this); + } + + //The bottom row doesn't have any adjacent cells to the south. - LLL + if ((ID > MAP_CELL_TOTAL - MAP_CELL_W) && (face == FACING_S || face == FACING_SE || face == FACING_SW)) { + return (*this); + } + + CellClass const * ptr = this + AdjacentCell[face]; + if ((unsigned)ptr->Cell_Number() > MAP_CELL_TOTAL) return(*this); + return(*ptr); +} + + +/*************************************************************************** + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/24/1995 PWG : Created. * + *=========================================================================*/ +void CellClass::Adjust_Threat(HousesType house, int threat_value) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int region = Map.Cell_Region(Cell_Number()); + + for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { + if (lp == house) continue; + + HouseClass * house_ptr = HouseClass::As_Pointer(lp); + if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { + house_ptr->Adjust_Threat(region, threat_value); + } + } + if (Debug_Threat) { + Map.Flag_To_Redraw(true); + } +} + + +/*********************************************************************************************** + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * + * * + * This routine will adjust the level of the Tiberium in the cell so that it will * + * smoothly blend with the adjacent Tiberium. This routine should only be called for * + * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * + * pattern. * + * * + * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * + * used. * + * * + * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * + * * + * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * + * increase the net worth of the existing Tiberium. * + * * + * HISTORY: * + * 05/16/1995 JLB : Created. * + * 02/20/1996 JLB : Takes into account the ore type. * + *=============================================================================================*/ +long CellClass::Tiberium_Adjust(bool pregame) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + if (Overlay != OVERLAY_NONE) { + if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { + static int _adj[9] = {0,1,3,4,6,7,8,10,11}; + static int _adjgem[9] = {0,0,0,1,1,1,2,2,2}; + int count = 0; + + /* + ** Mixup the Tiberium overlays so that they don't look the same. + ** Since the type of ore is known, also record the nominal + ** value per step of that ore type. + */ + bool gems = false; + int value = 0; + if (pregame) { + switch (Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + value = Rule.GoldValue; + Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4); + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + gems = true; + value = Rule.GemValue*4; + Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4); + break; + + default: + break; + } + } + + /* + ** Add up all adjacent cells that contain tiberium. + ** (Skip those cells which aren't on the map) + */ + for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { + if ((unsigned)::Adjacent_Cell(Cell_Number(), face) >= MAP_CELL_TOTAL) continue; + CellClass & adj = Adjacent_Cell(face); + + if (adj.Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) { + count++; + } + } + + if (gems) { + OverlayData = _adjgem[count]; + OverlayData = min(OverlayData, 2); + } else { + OverlayData = _adj[count]; + } + return((OverlayData+1) * value); + } + } + return(0); +} + +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * * + * Call this routine whenever an object enters a cell. It will check for the existence * + * of a crate and generate any "goodie" it might contain. * + * * + * INPUT: object -- Pointer to the object that is triggering this crate. * + * * + * OUTPUT: Can the object continue to enter this cell? A false return value means that the * + * cell is now occupied and must not be entered. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + * 07/08/1995 JLB : Added a bunch of goodies to the crates. * + * 06/17/1996 JLB : Revamped for Red Alert * + *=============================================================================================*/ +bool CellClass::Goodie_Check(FootClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { + bool force_mcv = false; + int force_money = 0; + int damage; + COORDINATE coord; + + /* + ** Determine the total number of shares for all the crate powerups. This is used as + ** the base pool to determine the odds from. + */ + int total_shares = 0; + for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) { + total_shares += CrateShares[index]; + } + + /* + ** Pick a random crate powerup according to the shares allotted to each powerup. + ** In solo play, the bonus item is dependant upon the rules control. + */ + CrateType powerup; + if (Session.Type == GAME_NORMAL) { + + /* + ** Solo play has money amount determined by rules.ini file. + */ + force_money = Rule.SoloCrateMoney; + + if (Overlay == OVERLAY_STEEL_CRATE) { + powerup = Rule.SilverCrate; + } + + if (Overlay == OVERLAY_WOOD_CRATE) { + powerup = Rule.WoodCrate; + } + + if (Overlay == OVERLAY_WATER_CRATE) { +//Mono_Printf("%d-%s.\n", __LINE__, __FILE__); + powerup = Rule.WaterCrate; + } + + } else { + int pick = Random_Pick(1, total_shares); + + int share_count = 0; + for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) { + share_count += CrateShares[powerup]; + if (pick <= share_count) break; + } + assert(powerup != CRATE_COUNT); + + /* + ** Depending on what was picked, there might be an alternate goodie if the selected + ** goodie would have no effect. + */ + switch (powerup) { + case CRATE_UNIT: + if (object->House->CurUnits > 50) powerup = CRATE_MONEY; + break; + + case CRATE_SQUAD: + if (object->House->CurInfantry > 100) powerup = CRATE_MONEY; + break; + + case CRATE_DARKNESS: + if (object->House->IsGPSActive) powerup = CRATE_MONEY; + break; + + case CRATE_ARMOR: + if (object->ArmorBias != 1) powerup = CRATE_MONEY; + break; + + case CRATE_SPEED: + if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY; + break; + + case CRATE_FIREPOWER: + if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY; + break; + + case CRATE_REVEAL: + if (object->House->IsVisionary) { + if (object->House->IsGPSActive) { + powerup = CRATE_MONEY; + } else { + powerup = CRATE_DARKNESS; + } + } + break; + + case CRATE_CLOAK: + if (object->IsCloakable) powerup = CRATE_MONEY; + break; + +// case CRATE_HEAL_BASE: +// if (object->House->BScan == 0) powerup = CRATE_UNIT; + + case CRATE_MONEY: + break; + + case CRATE_ICBM: + case CRATE_PARA_BOMB: + case CRATE_SONAR: + if (Session.Type != GAME_NORMAL) { + if (MPSuperWeaponDisable) { + powerup = CRATE_MONEY; + } + } + break; + + case CRATE_TIMEQUAKE: + /* + ** For the time quake crate, scan through and count up all the + ** units (and infantry and ships and aircraft) and if either + ** side has very few, allow the time quake. Otherwise, + ** change the crate to money or something. Only do this for + ** multiplay - for solo play, they get what they get. First, + ** check for time - the chance for getting a time quake crate + ** should be very very low when they first start the mission, + ** but as time goes on the chance goes up. + */ + if (Session.Type != GAME_NORMAL) { + int i,ucount; + int minunits = 1000; + bool found = false; + unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE); + if (minutes > 100) minutes = 100; + if (Random_Pick(0,100-(int)minutes) == 0) { + for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) { + ucount = 0; + HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1); + if (hptr != NULL && !hptr->IsDefeated) { + int j; + for( j=0; j < UNIT_COUNT; j++) { + ucount += hptr->QuantityU(j); + } + for( j=0; j < INFANTRY_COUNT; j++) { + ucount += hptr->QuantityI(j); + } + for( j=0; j < AIRCRAFT_COUNT; j++) { + ucount += hptr->QuantityA(j); + } + for( j=0; j < VESSEL_COUNT; j++) { + ucount += hptr->QuantityV(j); + } + int bcount = 0; + for( j=0; j < STRUCT_COUNT; j++) { + bcount += hptr->QuantityB(j); + } + ucount += bcount/2; // weight buildings less + minunits = min(minunits, ucount); + } + } + if (Random_Pick(0, minunits) == minunits) { + found = true; + } + } + + if (!found) { + powerup = CRATE_MONEY; + } + } + break; + } + /* + ** Possibly force it to be an MCV if there is + ** sufficient money and no buildings left. + */ + if ( object->House->BScan == 0 && + object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) && + Session.Options.Bases && + !(object->House->UScan & UNITF_MCV)) { + powerup = CRATE_UNIT; + force_mcv = true; + } + + /* + ** If the powerup is money but there is insufficient money to build a refinery but there is a construction + ** yard available, then force the money to be enough to rebuild the refinery. + */ + if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && + object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) { + + force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias; + } + + /* + ** Special override for water crates so that illegal goodies items + ** won't appear. + */ + if (Overlay == OVERLAY_WATER_CRATE) { + switch (powerup) { + case CRATE_UNIT: + case CRATE_SQUAD: + powerup = CRATE_MONEY; + break; + + default: + break; + } + } + } + + /* + ** Keep track of the number of each type of crate found + */ + if (Session.Type == GAME_INTERNET) { + object->House->TotalCrates->Increment_Unit_Total(powerup); + } + + /* + ** Remove the crate from the map. + */ + Map.Remove_Crate(Cell_Number()); +// Map[Cell_Number()].Overlay = OVERLAY_NONE; + + if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) { + Map.Place_Random_Crate(); + } + + /* + ** Generate any corresponding animation associated with this crate powerup. + */ + if (CrateAnims[powerup] != ANIM_NONE) { + new AnimClass(CrateAnims[powerup], Cell_Coord()); + } + + /* + ** Create the effect requested. + */ + bool tospeak = false; + switch (powerup) { + case CRATE_TIMEQUAKE: + TimeQuake = true; + break; + + /* + ** Give the player money. + */ + case CRATE_MONEY: +crate_money: + if (force_money > 0) { + object->House->Refund_Money(force_money); + } else { + object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900)); + } + break; + + /* + ** Shroud the world in blackness. + */ + case CRATE_DARKNESS: + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM + */ + if (object->House->IsHuman) { + Map.Shroud_The_Map(object->House); + } + break; + + /* + ** Reveal the entire map. + */ + case CRATE_REVEAL: + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM + */ + object->House->IsVisionary = true; + if (object->House->IsHuman) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, object->House); + } + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Try to create a unit where the crate was. + */ + case CRATE_UNIT: { + UnitTypeClass const * utp = NULL; + + /* + ** Give the player an MCV if he has no base left but does have more than enough + ** money to rebuild a new base. Of course, if he already has an MCV, then don't + ** give him another one. + */ + if (force_mcv) { + utp = &UnitTypeClass::As_Reference(UNIT_MCV); + } + + /* + ** If the player has a base and a refinery, but no harvester, then give him + ** a free one. + */ + if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { + utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + } + + /* + ** Check for special unit type override value. + */ + if (Rule.UnitCrateType != UNIT_NONE) { + utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType); + } + + /* + ** If no unit type has been determined, then pick one at random. + */ + while (utp == NULL) { +#ifdef FIXIT_ANTS +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3)); +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3)); +#endif +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); +#endif + if (utype != UNIT_MCV || Session.Options.Bases) { + utp = &UnitTypeClass::As_Reference(utype); + if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) { + break; + } + utp = NULL; + } + } + + if (utp != NULL) { + UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House); + if (goodie_unit != NULL) { + if (goodie_unit->Unlimbo(Cell_Coord())) { + return(false); + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the crate location. + */ + CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed); + if (goodie_unit->Unlimbo(::Cell_Coord(cell))) { + return(false); + } + delete goodie_unit; + goto crate_money; + } + } + } + break; + + /* + ** Create a squad of miscellaneous composition. + */ + case CRATE_SQUAD: + for (int index = 0; index < 5; index++) { + static InfantryType _inf[] = { + INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, + INFANTRY_E2, + INFANTRY_E3, + INFANTRY_RENOVATOR + }; + if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) { + if (index == 0) { + goto crate_money; + } + } + } + return(false); + + /* + ** A one para-bomb mission. + */ + case CRATE_PARA_BOMB: + if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A one time sonar pulse + */ + case CRATE_SONAR: + if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A group of explosions are triggered around the crate. + */ + case CRATE_EXPLOSION: + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_HE, 0, true); + } + for (int index = 0; index < 5; index++) { + COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); + new AnimClass(ANIM_FBALL1, frag_coord); + damage = CrateData[powerup]; + Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE); + } + break; + + /* + ** A napalm blast is triggered. + */ + case CRATE_NAPALM: + coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); + new AnimClass(ANIM_NAPALM3, coord); + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true); + } + damage = CrateData[powerup]; + Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); + break; + + /* + ** All objects within a certain range will gain the ability to cloak. + */ + case CRATE_CLOAK: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IsCloakable = true; + } + } + break; + + /* + ** All of the player's objects heal up. + */ + case CRATE_HEAL_BASE: + if (object->IsOwnedByPlayer) { + Sound_Effect(VOC_HEAL, object->Center_Coord()); + } + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { + obj->Strength = obj->Class_Of().MaxStrength; + } + } + break; + + + case CRATE_ICBM: + if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + case CRATE_ARMOR: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) { + fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256)); + ((TechnoClass *)obj)->ArmorBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_ARMOR); + break; + + case CRATE_SPEED: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) { + FootClass * foot = (FootClass *)obj; + + fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256); + foot->SpeedBias = val; + if (foot->IsOwnedByPlayer) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_SPEED); + break; + + case CRATE_FIREPOWER: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) { + + fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256); + ((TechnoClass *)obj)->FirepowerBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_FIREPOWER); + break; + + case CRATE_INVULN: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256)); + obj->Mark(MARK_CHANGE); + } + } + break; + + /* + ** A chronal vortex appears targetted at the triggering object. + */ + case CRATE_VORTEX: + if ( !ChronalVortex.Is_Active()) { + ChronalVortex.Appear ( Cell_Coord() ); + ChronalVortex.Set_Target ( (ObjectClass*) object ); + Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord()); + } + break; + + default: + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * * + * This routine will place the house flag at this cell location. * + * * + * INPUT: house -- The house that is having its flag placed here. * + * * + * OUTPUT: Was the flag successfully placed here? * + * * + * WARNINGS: Failure to place means that the cell is impassable for some reason. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Place(HousesType house) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) { + IsFlagged = true; + Owner = house; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * * + * This routine will free the cell of any house flag that may be located there. * + * * + * INPUT: none * + * * + * OUTPUT: Was there a flag here that was removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Remove(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (IsFlagged) { + IsFlagged = false; + Owner = HOUSE_NONE; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * * + * This routine is called when some event would cause a momentary disruption in the * + * cloaking device. All objects that are cloaked in the cell will have their cloaking * + * device shimmer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Shimmer(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = Cell_Occupier(); + + while (object) { + object->Do_Shimmer(); + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * * + * This routine is called when determining general passability for purposes of zone * + * calculation. Only blockages that cannot be circumvented are considered to make a cell * + * impassable. All other obstructions can either be destroyed or are temporary. * + * * + * INPUT: loco -- The locomotion type to use when determining passablility. * + * * + * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? * + * * + * ignorevehicles -- If vehicles should be ignored, then this flag will be true. * + * * + * zone -- If specified, the zone must match this value or else movement is * + * presumed disallowed. * + * * + * check -- This specifies the zone type that this check applies to. * + * * + * OUTPUT: Is the cell generally passable to ground targeting? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. * + * 10/05/1996 JLB : Allows checking for crushable blockages. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Flying objects always consider every cell passable since they can fly over everything. + */ + if (loco == SPEED_WINGED) { + return(true); + } + + /* + ** If a zone was specified, then see if the cell is in a legal + ** zone to allow movement. + */ + if (zone != -1) { + if (zone != Zones[check]) { + return(false); + } + } + + /* + ** Check the occupy bits for passable legality. If ignore infantry is true, then + ** don't consider infnatry. + */ + int composite = Flag.Composite; + if (ignoreinfantry) { + composite &= 0xE0; // Drop the infantry occupation bits. + } + if (ignorevehicles) { + composite &= 0x5F; // Drop the vehicle/building bit. + } + if (composite != 0) { + return(false); + } + + /* + ** Fetch the land type of the cell -- to be modified and used later. + */ + LandType land = Land_Type(); + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes unless this is a wall crushing check or if the checking object + ** can destroy walls. + */ + OverlayTypeClass const * overlay = NULL; + if (Overlay != OVERLAY_NONE) { + overlay = &OverlayTypeClass::As_Reference(Overlay); + } + if (overlay != NULL && overlay->IsWall) { + if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) { + return(false); + } + + /* + ** Crushing objects consider crushable walls as clear rather than the + ** typical LAND_WALL setting. + */ + land = LAND_CLEAR; + } + + /* + ** See if the ground type is impassable to this locomotion type and if + ** so, return the error condition. + */ + if (::Ground[land].Cost[loco] == 0) { + return(false); + } + + /* + ** All checks passed, so this cell must be passable. + */ + return(true); +} + + +/*********************************************************************************************** + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * * + * This routine will examine this cell and if there is a bridge here, it will return * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is there a bridge located in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Is_Bridge_Here(void) const +{ + switch (TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE1D: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE2D: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + case TEMPLATE_BRIDGE_3E: + case TEMPLATE_BRIDGE_3F: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * * + * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can * + * grow only if there is Tiberium already present. It can only grow to a certain level * + * and then all further growth is suspended. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Grow(void) const +{ + if (!Rule.IsTGrowth) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData >= 11) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * * + * This routine will examine the cell and determine if there is sufficient Tiberium * + * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is * + * too low, spreading will not occur. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? * + * * + * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells * + * available to spread to. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Spread(void) const +{ + if (!Rule.IsTSpread) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData <= 6) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * * + * This routine will cause the tiberium to grow in the cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did Tiberium grow in the cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Grow_Tiberium(void) +{ + if (Can_Tiberium_Grow()) { + OverlayData++; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * * + * This routine will cause the Tiberium to spread from this cell into an adjacent (random) * + * cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did the Tiberium spread? * + * * + * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this * + * routine will fail. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Spread_Tiberium(bool forced) +{ + if (!forced) { + if (!Can_Tiberium_Spread()) return(false); + } + FacingType offset = Random_Pick(FACING_N, FACING_NW); + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + CellClass * newcell = &Adjacent_Cell(index+offset); + + if (newcell != NULL && newcell->Can_Tiberium_Germinate()) { + new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); + newcell->OverlayData = 0; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * * + * This routine will examine the cell and determine if Tiberium can start growth in it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Germinate(void) const +{ + if (!Map.In_Radar(Cell_Number())) return(false); + + if (Is_Bridge_Here()) return(false); + + /* + ** Don't allow Tiberium to grow on a cell with a building unless that building is + ** invisible. In such a case, the Tiberium must grow or else the location of the + ** building will be revealed. + */ + BuildingClass const * building = Cell_Building(); + if (building != NULL && !building->Class->IsInvisible) return(false); + + if (!Ground[Land_Type()].Build) return(false); + + if (Overlay != OVERLAY_NONE) return(false); + + return(true); +} + + + + + + + + + + +/* +** Additions to CellClass to track visibility per-player. ST - 8/2/2019 2:59PM +** +** +** +** +** +*/ + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsMappedByPlayerMask |= (1 << shift); + } else { + IsMappedByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Mapped(player->Class->House, set); + if (Session.Type == GAME_NORMAL && player->IsHuman) { + IsMapped = set; // Also set the regular flag in single player + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HousesType house) const +{ + int shift = (int) house; + return (IsMappedByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Mapped(player->Class->House); + } + return false; +} + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsVisibleByPlayerMask |= (1 << shift); + } else { + IsVisibleByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Visible(player->Class->House, set); + if (Session.Type == GAME_NORMAL && player->IsHuman) { + IsVisible = set; // Also set the regular flag in single player. This is needed for rendering + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HousesType house) const +{ + int shift = (int) house; + return (IsVisibleByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Visible(player->Class->House); + } + return false; +} + +bool CellClass::Is_Jamming(HousesType house) const +{ + int shift = (int)house; + return (Jammed & (1 << shift)) ? true : false; +} + +bool CellClass::Is_Jamming(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Jamming(player->Class->House); + } + return false; +} + +void CellClass::Override_Land_Type(LandType type) +{ + OverrideLand = type; +} \ No newline at end of file diff --git a/REDALERT/CELL.H b/REDALERT/CELL.H new file mode 100644 index 000000000..94ac7eb22 --- /dev/null +++ b/REDALERT/CELL.H @@ -0,0 +1,332 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CELL_H +#define CELL_H + +#include "building.h" +#include "unit.h" +#include "template.h" + + +/**************************************************************************** +** Each cell on the map is controlled by the following structure. +*/ +class CellClass +{ + public: + /* + ** This is the ID number of this cell. By placing the ID number here, it doesn't have + ** be calculated. Calculating this number requires a divide and would occur about + ** 5.72031 bijillion times per second. + */ + short ID; + + /* + ** Does this cell need to be updated on the radar map? If something changes in the cell + ** that might change the radar map imagery, then this flag will be set. It gets cleared + ** when the cell graphic is updated to the radar map. + */ + unsigned IsPlot:1; + + /* + ** Does this cell contain the special placement cursor graphic? This graphic is + ** present when selecting a site for building placement. + */ + unsigned IsCursorHere:1; + + /* + ** A mapped cell has some portion of it visible. Maybe it has a shroud piece + ** over it and maybe not. + */ + unsigned IsMapped:1; + + /* + ** A visible cell means that it is completely visible with no shroud over + ** it at all. + */ + unsigned IsVisible:1; + + /* + ** Every cell can be assigned a waypoint. A waypoint can only be assigned + ** to one cell, and vice-versa. This bit simply indicates whether this + ** cell is assigned a waypoint or not. + */ + unsigned IsWaypoint:1; + + /* + ** Is this cell currently under the radar map cursor? If so then it + ** needs to be updated whenever the map is updated. + */ + unsigned IsRadarCursor:1; + + /* + ** If this cell contains a house flag, then this will be true. The actual house + ** flag it contains is specified by the Owner field. + */ + unsigned IsFlagged:1; + + /* + ** This is a working flag used to help keep track of what cells should be + ** shrouded. By using this flag it allows a single pass through the map + ** cells for determining shadow regrowth logic. + */ + unsigned IsToShroud:1; + + /* + ** This records the movement zone for this map. Movement zones share the + ** same number if they are contiguous (terrain consideration only). There + ** are basically two kinds of zones. The difference being determined by + ** walls that can be crushed by movement. A vehicle that can crush walls + ** will only consider the CrushZone. All other terrestrial travellers will + ** use the normal Zone. + */ + unsigned char Zones[MZONE_COUNT]; + + /* + ** This field controls whether an area is being jammed by a gap + ** generator. + */ + unsigned short Jammed; + + /* + ** This is the trigger ID for any trigger that might be attached to + ** this cell. + */ + CCPtr Trigger; + + /* + ** This contains the icon number and set to use for the base + ** of the terrain. All rendering on an icon occurs AFTER the icon + ** specified by this element is rendered. It is the lowest of the low. + */ + TemplateType TType; + unsigned char TIcon; + + /* + ** The second layer of 'terrain' icons is represented by a simple + ** type number and a value byte. This is sufficient for handling + ** concrete and walls. + */ + OverlayType Overlay; + unsigned char OverlayData; + + /* + ** This is used to specify any special 'stain' overlay icon. This + ** typically includes infantry bodies or other temporary marks. + */ + SmudgeType Smudge; + unsigned char SmudgeData; + + /* + ** Smudges and walls need to record ownership values. For walls, this + ** allows adjacent building placement logic to work. For smudges, it + ** allows building over smudges that are no longer attached to buildings + ** in addition to fixing the adjacent placement logic. + */ + HousesType Owner; + + /* + ** This flag tells you what type of infantry currently occupy the + ** cell or are moving into it. + */ + HousesType InfType; + + /* + ** These point to the object(s) that are located in this cell or overlap + ** this cell. + */ + private: + ObjectClass * OccupierPtr; + + public: +#ifdef SORTDRAW + ObjectClass * Overlapper[10]; +#else + ObjectClass * Overlapper[6]; +#endif + + + /* + ** Per-player view of whether a cell is mapped. One bit for each house type. ST - 8/2/2019 3:00PM + */ + unsigned int IsMappedByPlayerMask; + + /* + ** Per-player view of whether a cell is visible. One bit for each house type. ST - 8/2/2019 3:00PM + */ + unsigned int IsVisibleByPlayerMask; + + + /* + ** This array of bit flags is used to indicate which sub positions + ** within the cell are either occupied or are soon going to be + ** occupied. For vehicles, the cells that the vehicle is passing over + ** will be flagged with the vehicle bit. For infantry, the the sub + ** position the infantry is stopped at or headed toward will be marked. + ** The sub positions it passes over will NOT be marked. + */ + union { + struct { + unsigned Center:1; + unsigned NW:1; + unsigned NE:1; + unsigned SW:1; + unsigned SE:1; + unsigned Vehicle:1; // Reserved for vehicle occupation. + unsigned Monolith:1; // Some immovable blockage is in cell. + unsigned Building:1; // A building of some time (usually blocks movement). + } Occupy; + unsigned char Composite; + } Flag; + + //---------------------------------------------------------------- + CellClass(void); + CellClass(NoInitClass const & x) : Trigger(x) {} + ~CellClass(void) {OccupierPtr=0;} + + int operator == (CellClass const & cell) const {return &cell == this;} + + /* + ** Query functions. + */ + bool Can_Tiberium_Germinate(void) const; + bool Can_Tiberium_Grow(void) const; + bool Can_Tiberium_Spread(void) const; + bool Is_Bridge_Here(void) const; + RTTIType What_Am_I(void) const {return(RTTI_CELL);} + BuildingClass * Cell_Building(void) const; + CELL Cell_Number(void) const {return(ID);} + COORDINATE Cell_Coord(void) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());} + CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));} + CellClass const & Adjacent_Cell(FacingType face) const; + InfantryClass * Cell_Infantry(void) const; + LandType Land_Type(void) const {return((OverrideLand != LAND_NONE) ? OverrideLand : Land);} + ObjectClass * Cell_Find_Object(RTTIType rtti) const; + ObjectClass * Cell_Object(int x=0, int y=0) const; + ObjectClass * Cell_Occupier(void) const {return(OccupierPtr);} + ObjectClass * Fetch_Occupier(void) const; + TARGET As_Target(void) const {return ::As_Target(Cell_Number());} + TechnoClass * Cell_Techno(int x=0, int y=0) const; + TerrainClass * Cell_Terrain(void) const; + UnitClass * Cell_Unit(void) const; + VesselClass * Cell_Vessel(void) const; + bool Goodie_Check(FootClass * object); + bool Is_Clear_To_Build(SpeedType loco = SPEED_TRACK) const; + bool Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone=-1, MZoneType check=MZONE_NORMAL) const; + bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); } + int Cell_Color(bool override=false) const; + int Clear_Icon(void) const; + static int Spot_Index(COORDINATE coord); + bool Get_Template_Info(char *template_name, int &icon, void *&image_data); + + + /* + ** Object placement and removal flag operations. + */ + void Occupy_Down(ObjectClass * object); + void Occupy_Up(ObjectClass * object); + void Overlap_Down(ObjectClass * object); + void Overlap_Up(ObjectClass * object); + bool Flag_Place(HousesType house); + bool Flag_Remove(void); + + /* + ** File I/O. + */ + bool Should_Save(void) const; + bool Save(Pipe & file) const; + bool Load(Straw & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Display and rendering controls. + */ + void Draw_It(int x, int y, bool objects=false) const; + void Redraw_Objects(bool forced=false); + void Shimmer(void); + + /* + ** Maintenance calculation support. + */ + bool Grow_Tiberium(void); + bool Spread_Tiberium(bool forced=false); + long Tiberium_Adjust(bool pregame=false); + void Wall_Update(void); + void Concrete_Calc(void); + void Recalc_Attributes(void); + int Reduce_Tiberium(int levels); + int Reduce_Wall(int damage); + void Incoming(COORDINATE threat=0, bool forced=false, bool nokidding=false); + void Adjust_Threat(HousesType house, int threat_value); + + int operator != (CellClass const &) const {return 0;} + + + /* + ** Additional per-player functionality is needed for multiplayer. ST - 8/2/2019 3:01PM + */ + void Set_Mapped(HousesType house, bool set = true); + void Set_Mapped(HouseClass *player, bool set = true); + bool Is_Mapped(HousesType house) const; + bool Is_Mapped(HouseClass *player) const; + void Set_Visible(HousesType house, bool set = true); + void Set_Visible(HouseClass *player, bool set = true); + bool Is_Visible(HousesType house) const; + bool Is_Visible(HouseClass *player) const; + bool Is_Jamming(HousesType house) const; + bool Is_Jamming(HouseClass *player) const; + + /* + ** Override land type to fix passability issues on some maps + */ + void Override_Land_Type(LandType type); + + private: + CellClass (CellClass const &) ; + + LandType Land; // The land type of this cell. + + LandType OverrideLand; // The overriden land type of this cell. + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/CHECKBOX.CPP b/REDALERT/CHECKBOX.CPP new file mode 100644 index 000000000..f8029dec4 --- /dev/null +++ b/REDALERT/CHECKBOX.CPP @@ -0,0 +1,99 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHECKBOX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "checkbox.h" + + +/*********************************************************************************************** + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * * + * This routine will draw the checkbox either filled or empty as necessary. * + * * + * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? * + * * + * OUTPUT: Was the check box rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Draw_Me(int forced) +{ + if (ToggleClass::Draw_Me(forced)) { + + Hide_Mouse(); + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, false); + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY); + if (IsOn) { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, LTGREEN); + } + Show_Mouse(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * * + * This routine will detect if the mouse has been clicked on the checkbox object. If so, * + * the check box state will be toggled. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- The key that resulted in this routine being called. * + * * + * OUTPUT: bool; Should normal processing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } + return(ToggleClass::Action(flags, key)); +} diff --git a/REDALERT/CHECKBOX.H b/REDALERT/CHECKBOX.H new file mode 100644 index 000000000..115fa7d56 --- /dev/null +++ b/REDALERT/CHECKBOX.H @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHECKBOX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : May 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "toggle.h" + +class CheckBoxClass : public ToggleClass +{ + public: + CheckBoxClass(unsigned id, int x, int y) : + ToggleClass(id, x, y, 7, 7) + {}; + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + protected: +}; + +#endif diff --git a/REDALERT/CHEKLIST.CPP b/REDALERT/CHEKLIST.CPP new file mode 100644 index 000000000..9ae0ce044 --- /dev/null +++ b/REDALERT/CHEKLIST.CPP @@ -0,0 +1,362 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHEKLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckListClass::Action -- action function for this class * + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * CheckListClass::CheckListClass -- constructor * + * CheckListClass::Check_Item -- [un]checks an items * + * CheckListClass::Draw_Entry -- draws a list box entry * + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spe* + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * CheckListClass::~CheckListClass -- destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CheckListClass::CheckListClass -- constructor * + * * + * INPUT: * + * id control ID for this list box * + * x x-coord * + * y y-coord * + * w width * + * h height * + * flags mouse event flags * + * up ptr to Up-arrow shape * + * down ptr to Down-arrow shape * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + IsReadOnly(false) +{ +} + + +/*********************************************************************************************** + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * * + * This destructor will delete all entries attached to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CheckListClass::~CheckListClass(void) +{ + while (CheckListClass::Count()) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(0); + + ListClass::Remove_Item(0); + delete obj; + } +} + + +/*********************************************************************************************** + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * * + * This routine will add the specified text string to the check list. * + * * + * INPUT: text -- Pointer to the text string to add to the list box. * + * * + * OUTPUT: Returns the index number where the text object was added. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1996 JLB : Created. * + *=============================================================================================*/ +int CheckListClass::Add_Item(char const * text) +{ + CheckObject * obj = new CheckObject(text, false); + return(ListClass::Add_Item((char const *)obj)); +} + + +char const * CheckListClass::Current_Item(void) const +{ + CheckObject * obj = (CheckObject *)ListClass::Current_Item(); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * * + * This routine will find the text associated with the entry specified and return a pointer * + * to that text. * + * * + * INPUT: index -- The entry (index) to fetch a pointer to. * + * * + * OUTPUT: Returns with the text pointer associated with the index specified. * + * * + * WARNINGS: If the index is out of range, then NULL is returned. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * CheckListClass::Get_Item(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * * + * This routine will find the entry that matches the text pointer specified and then * + * delete that entry. * + * * + * INPUT: text -- The text pointer to use to find the exact match in the list. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Remove_Item(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + ListClass::Remove_Item(index); + delete obj; + break; + } + } +} + + +/*********************************************************************************************** + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spec * + * * + * This routine will find the entry that exactly matches the text pointer specified. If * + * found, then that entry will be set as the currently selected index. * + * * + * INPUT: text -- Pointer to the text string to find the match for. * + * * + * OUTPUT: none * + * * + * WARNINGS: If an exact match to the specified text string could not be found, then the * + * currently selected index is not changed. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Set_Selected_Index(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + Set_Selected_Index(index); + break; + } + } +} + + +/*************************************************************************** + * CheckListClass::Check_Item -- [un]checks an items * + * * + * INPUT: * + * index index of item to check or uncheck * + * checked 0 = uncheck, non-zero = check * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +void CheckListClass::Check_Item(int index, bool checked) +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && obj->IsChecked != checked) { + obj->IsChecked = checked; + Flag_To_Redraw(); + } +} + + +/*************************************************************************** + * CheckListClass::Is_Checked -- returns checked state of an item * + * * + * INPUT: * + * index index of item to query * + * * + * OUTPUT: * + * 0 = item is unchecked, 1 = item is checked * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +bool CheckListClass::Is_Checked(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->IsChecked); + } + return(false); +} + + +/*************************************************************************** + * CheckListClass::Action -- action function for this class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Action(unsigned flags, KeyNumType &key) +{ + int rc; + + /* + ** If this is a read-only list, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** Invoke parents Action first, so it can set the SelectedIndex if needed. + */ + rc = ListClass::Action(flags, key); + + /* + ** Now, if this event was a left-press, toggle the checked state of the + ** current item. + */ + if (flags & LEFTPRESS) { + Check_Item(SelectedIndex, !Is_Checked(SelectedIndex)); + } + + return(rc); +} + + +/*************************************************************************** + * CheckListClass::Draw_Entry -- draws a list box entry * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1995 BRR : Created. * + *=========================================================================*/ +void CheckListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (index >= Count()) return; + + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + + if (obj) { + char buffer[100] = ""; + + if (obj->IsChecked) { + buffer[0] = CHECK_CHAR; + } else { + buffer[0] = UNCHECK_CHAR; + } + buffer[1] = ' '; + sprintf(&buffer[2], obj->Text); + + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags, width, Tabs); + } +} + diff --git a/REDALERT/CHEKLIST.H b/REDALERT/CHEKLIST.H new file mode 100644 index 000000000..fafcb5938 --- /dev/null +++ b/REDALERT/CHEKLIST.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHEKLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * This class behaves just like the standard list box, except that if the * + * first character of a list entry is a space, clicking on it toggles the * + * space with a check-mark ('\3'). This makes each entry in the list box * + * "toggle-able". * + *-------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHEKLIST_H +#define CHEKLIST_H + +#include "list.h" + +class CheckObject +{ + public: + CheckObject(char const * text = 0, bool checked=false) : + Text(text), + IsChecked(checked) + {}; + + char const * Text; + bool IsChecked; +}; + + +class CheckListClass : public ListClass +{ + public: + /* + ** Constructor/Destructor + */ + CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + ~CheckListClass(void); + + virtual int Add_Item(int text) {return ListClass::Add_Item(text);} + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void) const; + virtual char const * Get_Item(int index) const; + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int text) {ListClass::Remove_Item(text);} + virtual void Set_Selected_Index(char const * text); + virtual void Set_Selected_Index(int index) {ListClass::Set_Selected_Index(index);}; + + /* + ** Checkmark utility functions + */ + void Check_Item(int index, bool checked); // sets checked state of item + bool Is_Checked(int index) const; // gets checked state of item + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + /* + ** This defines the ASCII value of the checkmark character & non-checkmark + ** character. + */ + typedef enum CheckListClassEnum { + CHECK_CHAR = '\3', + UNCHECK_CHAR = ' ' + } CheckListClassEnum; + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + bool IsReadOnly; +}; + + +#endif diff --git a/REDALERT/CLASS.CPP b/REDALERT/CLASS.CPP new file mode 100644 index 000000000..78c3cd533 --- /dev/null +++ b/REDALERT/CLASS.CPP @@ -0,0 +1,16 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + diff --git a/REDALERT/COLRLIST.CPP b/REDALERT/COLRLIST.CPP new file mode 100644 index 000000000..5bd76c2a4 --- /dev/null +++ b/REDALERT/COLRLIST.CPP @@ -0,0 +1,275 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COLRLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : April 19, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ColorListClass::Add_Item -- Adds an item to the list * + * ColorListClass::ColorListClass -- Class constructor * + * ColorListClass::Draw_Entry -- Draws one text line * + * ColorListClass::Remove_Item -- Removes an item from the list * + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * ColorListClass::~ColorListClass -- Class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ColorListClass::ColorListClass -- class constructor * + * * + * INPUT: * + * id button ID * + * x,y upper-left corner, in pixels * + * w,h width, height, in pixels * + * list ptr to array of char strings to list * + * flags flags for mouse, style of listbox * + * up,down pointers to shapes for up/down buttons * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ColorListClass::ColorListClass (int id, int x, int y, int w, int h, + TextPrintType flags, void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + Style(SELECT_HIGHLIGHT), + SelectColor(NULL) +{ +} + + +/*************************************************************************** + * ColorListClass::~ColorListClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +ColorListClass::~ColorListClass(void) +{ + Colors.Clear(); + SelectColor = 0; +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(char const * text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(int text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Remove_Item -- Removes an item from the list * + * * + * INPUT: * + * text ptr to item to remove * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Remove_Item(char const * text) +{ + int index = List.ID(text); + if (index != -1) { + Colors.Delete(index); + ListClass::Remove_Item(text); + } +} + + +/*************************************************************************** + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * * + * INPUT: * + * style style to draw * + * color color to draw the special style in; -1 = use item's color * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Set_Selected_Style(SelectStyleType style, RemapControlType * color) +{ + Style = style; + SelectColor = color; +} + + +/*************************************************************************** + * ColorListClass::Draw_Entry -- Draws one text line * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * color; + + /* + ** Draw a non-selected item in its color + */ + if (!selected) { + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + return; + } + + /* + ** For selected items, choose the right color & style: + */ + if (SelectColor == NULL) { + color = Colors[index]; + } else { + color = SelectColor; + } + + switch (Style) { + /* + ** NONE: Just print the string in its native color + */ + case SELECT_NORMAL: + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, + TextFlags, width, Tabs); + break; + + /* + ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must + ** be set) + */ + case SELECT_HIGHLIGHT: + if (TextFlags & TPF_6PT_GRAD) { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** BOX: Draw a box around the item in the current select color + */ + case SELECT_BOX: + LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** BAR: draw a color bar under the text + */ + case SELECT_BAR: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + LogicPage->Fill_Rect(x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** INVERT: Draw text as the background color on foreground color + */ + case SELECT_INVERT: + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]->Color); + break; + } +} diff --git a/REDALERT/COLRLIST.H b/REDALERT/COLRLIST.H new file mode 100644 index 000000000..1b1bbe282 --- /dev/null +++ b/REDALERT/COLRLIST.H @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COLRLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COLORLIST_H +#define COLORLIST_H + +#include "list.h" + + +/*************************************************************************** +** This class adds the ability for every list item to have a different color. +*/ +class ColorListClass : public ListClass +{ + public: + /********************************************************************* + ** These enums are the ways a selected item can be drawn + */ + //lint -esym(578,SELECT_NONE) + typedef enum SelectEnum { + SELECT_NORMAL, // selected items aren't drawn differently + SELECT_HIGHLIGHT, // item is highlighted + SELECT_BOX, // draw a box around the item + SELECT_BAR, // draw a bar behind the item + SELECT_INVERT // draw the string inverted + } SelectStyleType; + + ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual ~ColorListClass(void); + + virtual int Add_Item(char const * text, RemapControlType * color = NULL); + virtual int Add_Item(int text, RemapControlType * color = NULL); + virtual void Remove_Item(char const * text); + + virtual void Set_Selected_Style(SelectStyleType style, RemapControlType * color = NULL); + + /* + ** This is the list of colors for each item. + */ + DynamicVectorClass Colors; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This tells how to draw the selected item. + */ + SelectStyleType Style; + RemapControlType * SelectColor; + +}; + +#endif diff --git a/REDALERT/COMBAT.CPP b/REDALERT/COMBAT.CPP new file mode 100644 index 000000000..66f6da953 --- /dev/null +++ b/REDALERT/COMBAT.CPP @@ -0,0 +1,422 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBAT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBAT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 19, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Combat_Anim -- Determines explosion animation to play. * + * Explosion_Damage -- Inflict an explosion damage affect. * + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * Wide_Area_Damage -- Apply wide area damage to the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * * + * This routine is the core of combat tactics. It implements the * + * affect various armor types have against various weapon types. By * + * careful exploitation of this table, tactical advantage can be * + * obtained. * + * * + * INPUT: damage -- The damage points to process. * + * * + * warhead -- The source of the damage points. * + * * + * armor -- The type of armor defending against the damage. * + * * + * distance -- The distance (in leptons) from the source of the damage. * + * * + * OUTPUT: Returns with the adjusted damage points to inflict upon the * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 04/17/1994 JLB : Always does a minimum of damage. * + * 01/01/1995 JLB : Takes into account distance from damage source. * + * 04/11/1996 JLB : Changed damage fall-off formula for less damage fall-off. * + *=============================================================================================*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance) +{ + if (!damage) return(damage); + + /* + ** If there is no raw damage value to start with, then + ** there can be no modified damage either. + */ + if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0); + + /* + ** Negative damage (i.e., heal) is always applied full strength, but only if the heal + ** effect is close enough. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (distance < 0x008) { + if(warhead != WARHEAD_MECHANICAL && armor == ARMOR_NONE) return(damage); + if(warhead == WARHEAD_MECHANICAL && armor != ARMOR_NONE) return(damage); + } +#else + if (distance < 0x008 && armor == ARMOR_NONE) return(damage); +#endif + return(0); + } + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; + + damage = damage * whead->Modifier[armor]; + + /* + ** Reduce damage according to the distance from the impact point. + */ + if (damage) { + if (!whead->SpreadFactor) { + distance /= PIXEL_LEPTON_W/4; + } else { + distance /= whead->SpreadFactor * (PIXEL_LEPTON_W/2); + } + distance = Bound(distance, 0, 16); + if (distance) { + damage = damage / distance; + } + + /* + ** Allow damage to drop to zero only if the distance would have + ** reduced damage to less than 1/4 full damage. Otherwise, ensure + ** that at least one damage point is done. + */ + if (distance < 4) { + damage = max(damage, Rule.MinDamage); + } + } + + damage = min(damage, Rule.MaxDamage); + return(damage); +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + * 06/18/1996 JLB : Strength could be negative for healing effects. * + *=============================================================================================*/ +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead) +{ + CELL cell; // Cell number under explosion. + ObjectClass * object; // Working object pointer. + ObjectClass * objects[32]; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int count; // Number of vehicle IDs in list. + + if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return; + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; +// range = ICON_LEPTON_W*2; + range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1); + cell = Coord_Cell(coord); + if ((unsigned)cell >= MAP_CELL_TOTAL) return; + + CellClass * cellptr = &Map[cell]; + ObjectClass * impacto = cellptr->Cell_Occupier(); + + /* + ** Fill the list of unit IDs that will have damage + ** assessed upon them. The units can be lifted from + ** the cell data directly. + */ + count = 0; + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + /* + ** Fetch a pointer to the cell to examine. This is either + ** an adjacent cell or the center cell. Damage never spills + ** further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /* + ** Add all objects in this cell to the list of objects to possibly apply + ** damage to. The list stops building when the object pointer list becomes + ** full. Do not include overlapping objects; selection state can affect + ** the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(); + while (object) { + if (!object->IsToDamage && object != source) { + object->IsToDamage = true; + objects[count++] = object; + if (count >= ARRAY_SIZE(objects)) break; + } + object = object->Next; + } + if (count >= ARRAY_SIZE(objects)) break; + } + + /* + ** Sweep through the units to be damaged and damage them. When damaging + ** buildings, consider a hit on any cell the building occupies as if it + ** were a direct hit on the building's center. + */ + for (int index = 0; index < count; index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->IsActive) { + if (object->What_Am_I() == RTTI_BUILDING && impacto == object) { + distance = 0; + } else { + distance = Distance(coord, object->Center_Coord()); + } + if (object->IsDown && !object->IsInLimbo && distance < range) { + int damage = strength; + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + /* + ** If there is a wall present at this location, it may be destroyed. Check to + ** make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsTiberium && whead->IsTiberiumDestroyer) { + cellptr->Reduce_Tiberium(strength / 10); + } + if (optr->IsWall) { + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + Map[cell].Reduce_Wall(strength); + } + } + } + + /* + ** If there is a bridge at this location, then it may be destroyed by the + ** combat damage. + */ + if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B ) { + + if (((warhead == WARHEAD_AP || warhead == WARHEAD_HE) && Random_Pick(1, Rule.BridgeStrength) < strength)) { + Map.Destroy_Bridge_At(cell); + } + } +} + + +/*********************************************************************************************** + * Combat_Anim -- Determines explosion animation to play. * + * * + * This routine is called when a projectile impacts. This routine will determine what * + * animation should be played. * + * * + * INPUT: damage -- The amount of damage this warhead possess (warhead size). * + * * + * warhead -- The type of warhead. * + * * + * land -- The land type that this explosion is over. Sometimes, this makes * + * a difference (especially over water). * + * * + * OUTPUT: Returns with the animation to play. If no animation is to be played, then * + * ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land) +{ + /* + ** For cases of no damage or invalid warhead, don't have any + ** animation effect at all. + */ + if (damage == 0 || warhead == WARHEAD_NONE) { + return(ANIM_NONE); + } + + static AnimType _aplist[] = { + ANIM_VEH_HIT3, // Small fragment throwing explosion -- burn/exp mix. + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_FRAG1, // Medium fragment throwing explosion -- short decay. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _helist[] = { + ANIM_VEH_HIT1, // Small fireball explosion (bulges rightward). + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_ART_EXP1, // Large fragment throwing explosion -- many sparkles. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _firelist[] = { + ANIM_NAPALM1, // Small napalm burn. + ANIM_NAPALM2, // Medium napalm burn. + ANIM_NAPALM3, // Large napalm burn. + }; + + static AnimType _waterlist[] = { + ANIM_WATER_EXP3, + ANIM_WATER_EXP2, + ANIM_WATER_EXP1, + }; + + WarheadTypeClass const * wptr = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * wptr = &Warheads[warhead]; + switch (wptr->ExplosionSet) { + case 6: + return(ANIM_ATOM_BLAST); + + case 2: + if (damage > 15) { + return(ANIM_PIFFPIFF); + } + return(ANIM_PIFF); + + case 4: + if (land == LAND_NONE) return(ANIM_FLAK); +// Fixed math error + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 90), 90)]); + return(_aplist[(ARRAY_SIZE(_aplist)-1) * fixed(min(damage, 90), 90)]); + + case 5: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 130), 130)]); + return(_helist[(ARRAY_SIZE(_helist)-1) * fixed(min(damage, 130), 130)]); + + case 3: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 150), 150)]); + return(_firelist[(ARRAY_SIZE(_firelist)-1) * fixed(min(damage, 150), 150)]); + + case 1: + return(ANIM_PIFF); + + default: + break; + } + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Wide_Area_Damage -- Apply wide area damage to the map. * + * * + * This routine will apply damage to a very wide area on the map. The damage will be * + * spread out from the coordinate specified by the radius specified. The amount of damage * + * will attenuate according to the distance from center. * + * * + * INPUT: coord -- The coordinate that the explosion damage will center about. * + * * + * radius -- The radius of the explosion. * + * * + * damage -- The amount of damage to apply at the center location. * + * * + * source -- Pointer to the purpetrator of the damage (if any). * + * * + * warhead -- The type of warhead that is causing the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int rawdamage, TechnoClass * source, WarheadType warhead) +{ + int cell_radius = (radius + CELL_LEPTON_W-1) / CELL_LEPTON_W; + CELL cell = Coord_Cell(coord); + + for (int x = -cell_radius; x <= cell_radius; x++) { + for (int y = -cell_radius; y <= cell_radius; y++) { + int xpos = Cell_X(cell) + x; + int ypos = Cell_Y(cell) + y; + + /* + ** If the potential damage cell is outside of the map bounds, + ** then don't process it. This unusual check method ensures that + ** damage won't wrap from one side of the map to the other. + */ + if ((unsigned)xpos > MAP_CELL_W) { + continue; + } + if ((unsigned)ypos > MAP_CELL_H) { + continue; + } + CELL tcell = XY_Cell(xpos, ypos); + if (!Map.In_Radar(tcell)) continue; + + int dist_from_center = Distance(XY_Coord(x+cell_radius, y+cell_radius), XY_Coord(cell_radius, cell_radius)); + int damage = rawdamage * Inverse(fixed(cell_radius, dist_from_center)); + Explosion_Damage(Cell_Coord(tcell), damage, source, warhead); + if (warhead == WARHEAD_FIRE && damage > 100) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell)); + } + } + } +} diff --git a/REDALERT/COMBUF.CPP b/REDALERT/COMBUF.CPP new file mode 100644 index 000000000..778829f64 --- /dev/null +++ b/REDALERT/COMBUF.CPP @@ -0,0 +1,1161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBUF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : October 23, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommBufferClass::CommBufferClass -- class constructor * + * CommBufferClass::~CommBufferClass -- class destructor * + * CommBufferClass::Init -- initializes this queue * + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * CommBufferClass::Queue_Send -- queues a message for sending * + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * CommBufferClass::Queue_Receive -- queues a received message * + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue* + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * CommBufferClass::Add_Delay -- adds a new delay value for response time* + * CommBufferClass::Avg_Response_Time -- returns average response time * + * CommBufferClass::Max_Response_Time -- returns max response time * + * CommBufferClass::Reset_Response_Time -- resets computations * + * Mono_Debug_Print -- Debug output routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include "combuf.h" +#include "connect.h" // for command names for debug output +#include "wwlib32.h" // to enable mono output + + +/*************************************************************************** + * CommBufferClass::CommBufferClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen, + int extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + MaxExtraSize = extralen; + + //------------------------------------------------------------------------ + // Allocate the queue entries + //------------------------------------------------------------------------ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + SendIndex = new int[numsend]; + ReceiveIndex = new int[numreceive]; + + //------------------------------------------------------------------------ + // Allocate queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + SendQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + SendQueue[i].ExtraBuffer = NULL; + } + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + ReceiveQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + ReceiveQueue[i].ExtraBuffer = NULL; + } + } + + Init(); + +} /* end of CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::~CommBufferClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::~CommBufferClass() +{ + int i; + + //------------------------------------------------------------------------ + // Free queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + if (SendQueue[i].ExtraBuffer) { + delete [] SendQueue[i].ExtraBuffer; + } + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + if (ReceiveQueue[i].ExtraBuffer) { + delete [] ReceiveQueue[i].ExtraBuffer; + } + } + + delete [] SendQueue; + delete [] ReceiveQueue; + + delete [] SendIndex; + delete [] ReceiveIndex; + +} /* end of ~CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Init(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + + ReceiveCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + ReceiveQueue[i].ExtraLen = 0; + + ReceiveIndex[i] = 0; + } + + //------------------------------------------------------------------------ + // Init debug values + //------------------------------------------------------------------------ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugNameCount = 0; + +} /* end of Init */ + + +/*************************************************************************** + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/23/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Init_Send_Queue(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + +} /* end of Init_Send_Queue */ + + +/*************************************************************************** + * CommBufferClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Send(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (SendCount==MaxSend || buflen > MaxPacketSize) + return(0); + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxSend; i++) { + if (SendQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[index].IsActive = 1; // entry is now active + SendQueue[index].IsACK = 0; // entry hasn't been ACK'd + SendQueue[index].FirstTime = 0L; // filled in by Manager when sent + SendQueue[index].LastTime = 0L; // filled in by Manager when sent + SendQueue[index].SendCount = 0L; // filled in by Manager when sent + SendQueue[index].BufLen = buflen; // save buffer size + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(SendQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(SendQueue[index].ExtraBuffer,extrabuf,extralen); + SendQueue[index].ExtraLen = extralen; + } + else { + SendQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + SendIndex[SendCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + SendCount++; + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index "index" of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendIndex[index]].Buffer, + SendQueue[SendIndex[index]].BufLen); + (*buflen) = SendQueue[SendIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,SendQueue[SendIndex[index]].ExtraBuffer, + SendQueue[SendIndex[index]].ExtraLen); + (*extralen) = SendQueue[SendIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[SendIndex[index]].IsActive = 0; + SendQueue[SendIndex[index]].IsACK = 0; + SendQueue[SendIndex[index]].FirstTime = 0L; + SendQueue[SendIndex[index]].LastTime = 0L; + SendQueue[SendIndex[index]].SendCount = 0L; + SendQueue[SendIndex[index]].BufLen = 0; + SendQueue[SendIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < SendCount - 1; i++) { + SendIndex[i] = SendIndex[i + 1]; + } + SendIndex[SendCount - 1] = 0; + SendCount--; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommBufferClass::Get_Send(int index) +{ + if (SendQueue[SendIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&SendQueue[SendIndex[index]]); + } + +} /* end of Get_Send */ + + +/*************************************************************************** + * CommBufferClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Receive(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (ReceiveCount==MaxReceive || buflen > MaxPacketSize) { + return(0); + } + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxReceive; i++) { + if (ReceiveQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[index].IsActive = 1; + ReceiveQueue[index].IsRead = 0; + ReceiveQueue[index].IsACK = 0; + ReceiveQueue[index].BufLen = buflen; + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(ReceiveQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(ReceiveQueue[index].ExtraBuffer,extrabuf,extralen); + ReceiveQueue[index].ExtraLen = extralen; + } + else { + ReceiveQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + ReceiveIndex[ReceiveCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + ReceiveCount++; + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index index of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer, + ReceiveQueue[ReceiveIndex[index]].BufLen); + (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,ReceiveQueue[ReceiveIndex[index]].ExtraBuffer, + ReceiveQueue[ReceiveIndex[index]].ExtraLen); + (*extralen) = ReceiveQueue[ReceiveIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[ReceiveIndex[index]].IsActive = 0; + ReceiveQueue[ReceiveIndex[index]].IsRead = 0; + ReceiveQueue[ReceiveIndex[index]].IsACK = 0; + ReceiveQueue[ReceiveIndex[index]].BufLen = 0; + ReceiveQueue[ReceiveIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < ReceiveCount - 1; i++) { + ReceiveIndex[i] = ReceiveIndex[i + 1]; + } + ReceiveIndex[ReceiveCount - 1] = 0; + ReceiveCount--; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommBufferClass::Get_Receive(int index) +{ + if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&ReceiveQueue[ReceiveIndex[index]]); + } + +} /* end of Get_Receive */ + + +/*************************************************************************** + * CommBufferClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } + else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommBufferClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Configure_Debug(int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + DebugOffset = type_offset; + DebugSize = type_size; + DebugNames = names; + DebugNameStart = namestart; + DebugNameCount = namecount; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // If few enough entries, call the verbose debug version + //------------------------------------------------------------------------ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + if (MaxSend <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } + else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + if (MaxReceive <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } + else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (SendQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], SendQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (ReceiveQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], ReceiveQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + +/************************** end of combuf.cpp ******************************/ + + diff --git a/REDALERT/COMBUF.H b/REDALERT/COMBUF.H new file mode 100644 index 000000000..035c73782 --- /dev/null +++ b/REDALERT/COMBUF.H @@ -0,0 +1,190 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBUF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to store outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * * + * This class stores buffers in a non-sequenced order; it allows freeing * + * any entry, so the buffers can be kept clear, even if packets come in * + * out of order. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMBUF_H +#define COMBUF_H + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommBufferClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommBufferClass(int numsend, int numrecieve, int maxlen, + int extralen = 0); + virtual ~CommBufferClass(); + void Init(void); + void Init_Send_Queue(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int type_offset, int type_size, char **names, + int namestart, int namecount); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + int MaxExtraSize; // max size of extra bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + unsigned long SendTotal; // total # added to send queue + int *SendIndex; // array of Send entry indices + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + unsigned long ReceiveTotal; // total # added to receive queue + int *ReceiveIndex; // array of Receive entry indices + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugNameStart; // number of 1st ID + int DebugNameCount; // # of names in array +}; + +#endif + +/**************************** end of combuf.h ******************************/ + diff --git a/REDALERT/COMINIT.CPP b/REDALERT/COMINIT.CPP new file mode 100644 index 000000000..e33a61ca4 --- /dev/null +++ b/REDALERT/COMINIT.CPP @@ -0,0 +1,48 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// +// If you link with this it will automatically call the COM initialization stuff +// + +#include "cominit.h" +//#include +//#include +//#include +#include +//#include "externs.h" +//#include "text.rh" + +//#include "WolDebug.h" + +ComInit::ComInit() +{ + //HRESULT hRes = CoInitialize(NULL); +// if (SUCCEEDED(hRes)==FALSE) +// exit(0); + HRESULT hRes = OleInitialize(NULL); +} + +ComInit::~ComInit() +{ +// CoUninitialize(); +// debugprint( "pre OleUninitialize\n" ); + OleUninitialize(); +// debugprint( "post OleUninitialize\n" ); +} + +// Creating this instance will setup all COM stuff & do cleanup on program exit +ComInit Global_COM_Initializer; + diff --git a/REDALERT/COMINIT.H b/REDALERT/COMINIT.H new file mode 100644 index 000000000..4c8814c89 --- /dev/null +++ b/REDALERT/COMINIT.H @@ -0,0 +1,31 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef COMINIT_HEADER +#define COMINIT_HEADER + +// +// Link with this to automatically initialize COM at startup +// - See cominit.cpp for more info +// + +class ComInit +{ + public: + ComInit(); + ~ComInit(); +}; + +#endif diff --git a/REDALERT/COMPAT.H b/REDALERT/COMPAT.H new file mode 100644 index 000000000..049a2fd37 --- /dev/null +++ b/REDALERT/COMPAT.H @@ -0,0 +1,214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMPAT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMPAT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/95 * + * * + * Last Update : March 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMPAT_H +#define COMPAT_H + + +#define BuffType BufferClass +//#define movmem(a,b,c) memmove(b,a,c) +#define ShapeBufferSize _ShapeBufferSize + +/*=========================================================================*/ +/* Define some equates for the different graphic routines we will install */ +/* later. */ +/*=========================================================================*/ +#define HIDBUFF ((void *)(0xA0000)) +#define Size_Of_Region(a, b) ((a)*(b)) + +/*=========================================================================*/ +/* Define some Graphic Routines which will only be fixed by these defines */ +/*=========================================================================*/ +#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15) + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. + +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + +#define ERROR_WINDOW 1 +#define ErrorWindow 1 + + +//extern unsigned char *Palette; +extern unsigned char MDisabled; // Is mouse disabled? +extern WORD Hard_Error_Occured; + +/* +** This is the menu control structures. +*/ +typedef enum MenuIndexType { + MENUX, + MENUY, + ITEMWIDTH, + ITEMSHIGH, + MSELECTED, + NORMCOL, + HILITE, + MENUPADDING=0x1000 +} MenuIndexType; + + +#ifdef NEVER +#define BITSPERBYTE 8 +#define MAXSHORT 0x7FFF +#define HIBITS 0x8000 +#define MAXLONG 0x7FFFFFFFL +#define HIBITL 0x80000000 + +#define MAXINT MAXLONG +#define HIBITI HIBITL + +#define DMAXEXP 308 +#define FMAXEXP 38 +#define DMINEXP -307 +#define FMINEXP -37 + +#define MAXDOUBLE 1.797693E+308 +#define MAXFLOAT 3.37E+38F +#define MINDOUBLE 2.225074E-308 +#define MINFLOAT 8.43E-37F + +#define DSIGNIF 53 +#define FSIGNIF 24 + +#define DMAXPOWTWO 0x3FF +#define FMAXPOWTWO 0x7F +#define DEXPLEN 11 +#define FEXPLEN 8 +#define EXPBASE 2 +#define IEEE 1 +#define LENBASE 1 +#define HIDDENBIT 1 +#define LN_MAXDOUBLE 7.0978E+2 +#define LN_MINDOUBLE -7.0840E+2 +#endif + +/* These defines handle the various names given to the same color. */ +#define DKGREEN GREEN +#define DKBLUE BLUE +#define GRAY GREY +#define DKGREY GREY +#define DKGRAY GREY +#define LTGRAY LTGREY + + +class IconsetClass; +#ifndef WIN32 +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; +#endif + +inline int Get_IconSet_MapWidth(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapWidth); + } + return(0); +} + +inline int Get_IconSet_MapHeight(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapHeight); + } + return(0); +} + +inline unsigned char const * Get_IconSet_ControlMap(void const * data) +{ + if (data) { + return((unsigned char const *)((char *)data + ((IControl_Type *)data)->ColorMap)); + } + return(0); +} + +class IconsetClass : protected IControl_Type +{ + public: + /* + ** Query functions. + */ + int Map_Width(void) const {return(MapWidth);}; + int Map_Height(void) const {return(MapHeight);}; + unsigned char * Control_Map(void) {return((unsigned char *)this + ColorMap);}; + unsigned char const * Control_Map(void) const {return((unsigned char const *)this + ColorMap);}; + int Icon_Count(void) const {return(Count);}; + int Pixel_Width(void) const {return(Width);}; + int Pixel_Height(void) const {return(Height);}; + int Total_Size(void) const {return(Size);}; + unsigned char const * Palette_Data(void) const {return((unsigned char const *)this + Palettes);}; + unsigned char * Palette_Data(void) {return((unsigned char *)this + Palettes);}; + unsigned char const * Icon_Data(void) const {return((unsigned char const *)this + Icons);}; + unsigned char * Icon_Data(void) {return((unsigned char *)this + Icons);}; + unsigned char const * Map_Data(void) const {return((unsigned char const *)this + Map);}; + unsigned char * Map_Data(void) {return((unsigned char *)this + Map);}; + unsigned char const * Remap_Data(void) const {return((unsigned char const *)this + Remaps);}; + unsigned char * Remap_Data(void) {return((unsigned char *)this + Remaps);}; + unsigned char const * Trans_Data(void) const {return((unsigned char const *)this + TransFlag);}; + unsigned char * Trans_Data(void) {return((unsigned char *)this + TransFlag);}; + + /* + ** Disallow these operations with an IconsetClass object. + */ + private: + IconsetClass & operator = (IconsetClass const &); + IconsetClass(void); + static void * operator new(size_t); +}; + + +#endif diff --git a/REDALERT/COMQUEUE.CPP b/REDALERT/COMQUEUE.CPP new file mode 100644 index 000000000..8dfe36ad7 --- /dev/null +++ b/REDALERT/COMQUEUE.CPP @@ -0,0 +1,1002 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.cpv 4.1 11 Apr 1996 18:28:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * CommQueueClass::Avg_Response_Time -- returns average response time * + * CommQueueClass::CommQueueClass -- class constructor * + * CommQueueClass::Configure_Debug -- sets up special debug values * + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * CommQueueClass::Init -- initializes this queue * + * CommQueueClass::Max_Response_Time -- returns max response time * + * CommQueueClass::Mono_Debug_Print -- Debug output routine * + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * CommQueueClass::Queue_Receive -- queues a received message * + * CommQueueClass::Queue_Send -- queues a message for sending * + * CommQueueClass::Reset_Response_Time -- resets computations * + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * CommQueueClass::~CommQueueClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0) +#include "function.h" + + +/*************************************************************************** + * CommQueueClass::CommQueueClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ** Init variables + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ** Allocate the queue entries + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + /* + ** Allocate queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); +} + + +/*************************************************************************** + * CommQueueClass::~CommQueueClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::~CommQueueClass() +{ + int i; + + /* + ** Free queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; +} + + +/*************************************************************************** + * CommQueueClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Init(void) +{ + int i; + + /* + ** Init data members + */ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + SendNext = 0; + SendEmpty = 0; + + ReceiveCount = 0; + ReceiveNext = 0; + ReceiveEmpty = 0; + + /* + ** Init the queue entries + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + ReceiveQueue[i].BufLen = 0; + } + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + } + + /* + ** Init debug values + */ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; +} + + +/*************************************************************************** + * CommQueueClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Send(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + SendQueue[SendEmpty].IsActive = 1; // entry is now active + SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd + SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].BufLen = buflen; // save buffer size + + /* + ** Copy the packet data + */ + memcpy(SendQueue[SendEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + SendCount++; + SendEmpty++; + if (SendEmpty==MaxSend) { + SendEmpty = 0; + } + + SendTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Send(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (SendCount==0 || SendQueue[SendNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen); + (*buflen) = SendQueue[SendNext].BufLen; + } + + /* + ** Set entry flags + */ + SendQueue[SendNext].IsActive = 0; + SendQueue[SendNext].IsACK = 0; + SendQueue[SendNext].FirstTime = 0L; + SendQueue[SendNext].LastTime = 0L; + SendQueue[SendNext].SendCount = 0L; + SendQueue[SendNext].BufLen = 0; + SendCount--; + SendNext++; + if (SendNext==MaxSend) { + SendNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Next_Send(void) +{ + if (SendCount==0) { + return(NULL); + } else { + return(&SendQueue[SendNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Get_Send(int index) +{ + int i; + + i = SendNext + index; + if (i >= MaxSend) { + i -= MaxSend; + } + + if (SendQueue[i].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Receive(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveEmpty].IsActive = 1; + ReceiveQueue[ReceiveEmpty].IsRead = 0; + ReceiveQueue[ReceiveEmpty].IsACK = 0; + ReceiveQueue[ReceiveEmpty].BufLen = buflen; + + /* + ** Copy the packet data + */ + memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + ReceiveCount++; + ReceiveEmpty++; + if (ReceiveEmpty==MaxReceive) { + ReceiveEmpty = 0; + } + + ReceiveTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Receive(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveNext].Buffer, + ReceiveQueue[ReceiveNext].BufLen); + (*buflen) = ReceiveQueue[ReceiveNext].BufLen; + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveNext].IsActive = 0; + ReceiveQueue[ReceiveNext].IsRead = 0; + ReceiveQueue[ReceiveNext].IsACK = 0; + ReceiveQueue[ReceiveNext].BufLen = 0; + ReceiveCount--; + ReceiveNext++; + if (ReceiveNext==MaxReceive) { + ReceiveNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Next_Receive(void) +{ + if (ReceiveCount==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Get_Receive(int index) +{ + int i; + + i = ReceiveNext + index; + if (i >= MaxReceive) { + i -= MaxReceive; + } + + if (ReceiveQueue[i].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) { + roundoff = 1; + } + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } +} + + +/*************************************************************************** + * CommQueueClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Avg_Response_Time(void) +{ + return(MeanDelay); +} + + +/*************************************************************************** + * CommQueueClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Max_Response_Time(void) +{ + return(MaxDelay); +} + + +/*************************************************************************** + * CommQueueClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; +} + + +/*************************************************************************** + * CommQueueClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; +} + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** If few enough entries, call the verbose debug version + */ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /* + ** Print Receive Queue items + */ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} + + +/*************************************************************************** + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + /* + ** Print an active entry + */ + if (SendQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + + /* + ** Print Receive Queue items + */ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + /* + ** Print an active entry + */ + if (ReceiveQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} + + +#endif \ No newline at end of file diff --git a/REDALERT/COMQUEUE.H b/REDALERT/COMQUEUE.H new file mode 100644 index 000000000..6c5d7748d --- /dev/null +++ b/REDALERT/COMQUEUE.H @@ -0,0 +1,190 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.h_v 4.1 11 Apr 1996 18:26:02 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to queue up outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * It allows the application to keep track of how many messages have * + * passed through this queue, in & out, so packets can use this as a * + * unique ID. (If packets have a unique ID, the application can use this * + * to detect re-sends.) * + * * + * The queues act as FIFO buffers (First-In, First-Out). The first entry * + * placed in a queue is the first one read from it, and so on. The * + * controlling application must ensure it places the entries on the queue * + * in the order it wants to access them. * + * * + * The queue is implemented as an array of Queue Entries. Index 0 is the * + * first element placed on the queue, and is the first retrieved. Index * + * 1 is the next, and so on. When Index 0 is retrieved, the next-available* + * entry becomes Index 1. The array is circular; when the end is reached, * + * the indices wrap around to the beginning. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMQUEUE_H +#define COMQUEUE_H + + + +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommQueueClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommQueueClass(int numsend, int numrecieve, int maxlen); + virtual ~CommQueueClass(); + void Init(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen); // remove from Send queue + SendQueueType * Next_Send(void); // ptr to next avail entry + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue + ReceiveQueueType * Next_Receive(void); // ptr to next avail entry + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + int SendNext; // next entry read from queue + int SendEmpty; // next empty spot in queue + unsigned long SendTotal; // total # added to send queue + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + int ReceiveNext; // next entry read from queue + int ReceiveEmpty; // next empty spot in queue + unsigned long ReceiveTotal; // total # added to receive queue + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/*************************** end of comqueue.h *****************************/ + diff --git a/REDALERT/CONFDLG.CPP b/REDALERT/CONFDLG.CPP new file mode 100644 index 000000000..bcfe05c47 --- /dev/null +++ b/REDALERT/CONFDLG.CPP @@ -0,0 +1,244 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.cpv 4.67 27 Aug 1996 15:46:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "confdlg.h" + + +bool ConfirmationClass::Process(int text) +{ + text; + return true; + //return(Process(Text_String(text))); +} + +#if (0) // ST - 5/8/2019 +/*********************************************************************************************** + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to confirm a deletion. * + * * + * INPUT: char * string - display in edit box. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +bool ConfirmationClass::Process(char const * string) +{ + enum { + NUM_OF_BUTTONS = 2 + }; + + char buffer[80*3]; + int result = true; + int width; + int bwidth, bheight; // button width and height + int height; + int selection = 0; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + strcpy(buffer, string); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + width += 60; + height += 60; + int x = (320 - width) / 2; + int y = (200 - height) / 2; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30); + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + 10, y + height - (bheight + 5), bwidth ); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + width - (bwidth + 10), + y + height - (bheight + 5), bwidth ); + + nobtn.Add_Tail(yesbtn); + + curbutton = 1; + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(yesbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_NO, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(yesbtn); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + result = false; + } + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_CONFIRMATION, x, y, width); + Fancy_Text_Print(buffer, x+20, y+30, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Draw the titles. + */ + yesbtn.Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = yesbtn.Input(); + + /* + ** Process Input. + */ + switch (input) { + case KeyNumType(BUTTON_YES | KN_BUTTON): + selection = BUTTON_YES; + pressed = true; + break; + + case (KN_ESC): + case KeyNumType(BUTTON_NO | KN_BUTTON): + selection = BUTTON_NO; + pressed = true; + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = NUM_OF_BUTTONS - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_YES; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_YES): + result = true; + process = false; + break; + + case (BUTTON_NO): + result = false; + process = false; + break; + + default: + break; + } + + pressed = false; + } + } + return(result); +} +#endif \ No newline at end of file diff --git a/REDALERT/CONFDLG.H b/REDALERT/CONFDLG.H new file mode 100644 index 000000000..f12a166de --- /dev/null +++ b/REDALERT/CONFDLG.H @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.h_v 4.69 27 Aug 1996 15:43:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef CONFDLG_H +#define CONFDLG_H + +#include "gadget.h" + +class ConfirmationClass +{ + private: + enum ConfirmationClassEnum { + BUTTON_YES=1, // Button number for "Options menu" + BUTTON_NO // Button number for "Options menu" + }; + + public: + ConfirmationClass(void) { }; + bool Process(char const * string); + bool Process(int text); +}; + +#endif diff --git a/REDALERT/CONNECT.CPP b/REDALERT/CONNECT.CPP new file mode 100644 index 000000000..bd679dd5c --- /dev/null +++ b/REDALERT/CONNECT.CPP @@ -0,0 +1,835 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNECT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Init -- Initializes connection queue to empty * + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Service_Send_Queue -- services the send queue * + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" +#include +//#include +#include +#include "connect.h" + +//#include "WolDebug.h" + +/* +********************************* Globals *********************************** +*/ +char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (placed there + by Receive_Packet), and outgoing packets (placed there by Send_Packet). + It can optionally store "extra" bytes, which are stored along with each + packet, but aren't transmitted as part of the packet. If 'extralen' + is 0, the CommBufferClass ignores this parameter. + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen); + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + delete Queue; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void ConnectionClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*------------------------------------------------------------------------ + Set the magic # for the packet + ------------------------------------------------------------------------*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ------------------------------------------------------------------------*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } + else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*------------------------------------------------------------------------ + Now build the packet + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Add it to the queue; don't add any extra data with it. + ------------------------------------------------------------------------*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) { + if (ack_req) { + NumSendAck++; + } + else { + NumSendNoAck++; + } + return(1); + } + else { + return(0); + } + +} /* end of Send_Packet */ + + +/*************************************************************************** + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /*.................................................................. + Get queue entry ptr + ..................................................................*/ + send_entry = Queue->Get_Send(i); + + /*.................................................................. + If ptr is valid, get ptr to its data + ..................................................................*/ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + } + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*..................................................................... + If there's only one slot left, don't tie up the queue with this packet + .....................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + return(0); + } + + /*..................................................................... + Error if we can't queue the packet + .....................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecNoAck++; + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { + /*..................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + .....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { + save_packet = 0; + } + + /*..................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + .....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*..................................................................... + Queue the packet & update our LastSeqID value. + .....................................................................*/ + if (save_packet) { + /*.................................................................. + If there's only one slot left, make sure we only put a packet in it + if this packet will let us increment our LastSeqID; otherwise, we'll + get stuck, forever unable to increment LastSeqID. + ..................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { + return(0); + } + } + + /*.................................................................. + If we can't queue the packet, return; don't send an ACK. + ..................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecAck++; + + /*.................................................................. + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ..................................................................*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................... + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ...............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*..................................................................... + Send an ACK, regardless of whether this was a resend or not. + .....................................................................*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; + Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0); + + return(1); + } + + return(0); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + } + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue: This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + + Service the Receive Queue: This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } + else { + return(0); + } + +} /* end of Service */ + + +/*************************************************************************** + * ConnectionClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /*..................................................................... + Get this queue entry + .....................................................................*/ + send_entry = Queue->Get_Send(i); + + /*..................................................................... + If ACK has been received, unqueue it + .....................................................................*/ + if (send_entry->IsACK) { + + /*.................................................................. + Update this queue's response time + ..................................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /*.................................................................. + Unqueue the packet + ..................................................................*/ + Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + + /*.................................................................. + Send the message + ..................................................................*/ + Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer, + send_entry->ExtraLen); + + /*.................................................................. + Fill in Time fields + ..................................................................*/ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + + /*.................................................................. + Update SendCount + ..................................................................*/ + send_entry->SendCount++; + + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { + return(0); + } + else { + return(1); + } + +} /* end of Service_Send_Queue */ + + +/*************************************************************************** + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + + } + else if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + } + + return(1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ + static struct timeb mytime; // DOS time + unsigned long msec; + +#ifdef WWLIB32_H + + /*------------------------------------------------------------------------ + If the Westwood timer system has been activated, use TickCount's value + ------------------------------------------------------------------------*/ + if (TimerSystemOn) { + return(TickCount); // Westwood Library time + } + /*------------------------------------------------------------------------ + Otherwise, use the DOS timer + ------------------------------------------------------------------------*/ + else { + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + } + +#else + + /*------------------------------------------------------------------------ + If the Westwood library isn't being used, use the DOS timer. + ------------------------------------------------------------------------*/ + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } + else { + return(NULL); + } + +} /* end of Command_Name */ + +/************************** end of connect.cpp *****************************/ + + diff --git a/REDALERT/CONNECT.H b/REDALERT/CONNECT.H new file mode 100644 index 000000000..9bd5da2be --- /dev/null +++ b/REDALERT/CONNECT.H @@ -0,0 +1,278 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNECT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * DESCRIPTION: * + * This class represents a single "connection" with another system. It's * + * a pure virtual base class that acts as a framework for other classes. * + * * + * This class contains a CommBufferClass member, which stores received * + * & transmitted packets. The ConnectionClass has virtual functions to * + * handle adding packets to the queue, reading them from the queue, * + * a Send routine for actually sending data, and a Receive_Packet function * + * which is used to tell the connection that a new packet has come in. * + * * + * The virtual Service routines handle all ACK & Retry logic for * + * communicating between this system & another. Thus, any class derived * + * from this class may overload the basic ACK/Retry logic. * + * * + * THE PACKET HEADER: * + * The Connection Classes prefix every packet sent with a header that's * + * local to this class. The header contains a "Magic Number" which should * + * be unique for each product, and Packet "Code", which will tell the * + * receiving end if this is DATA, or an ACK packet, and a packet ID, which * + * is a unique numerical ID for this packet (useful for detecting resends).* + * The header is stored with each packet in the send & receive Queues; * + * it's removed before it's passed back to the application, via * + * Get_Packet() * + * * + * THE CONNECTION MANAGER: * + * It is assumed that there will be a "Connection Manager" class which * + * will handle parsing incoming packets; it will then tell the connection * + * that new packets have come in, and the connection will process them in * + * whatever way it needs to for its protocol (check for resends, handle * + * ACK packets, etc). The job of the connection manager is to parse * + * incoming packets & distribute them to the connections that need to * + * store them (for multi-connection protocols). * + * * + * NOTES ON ACK/RETRY: * + * This class provides a "non-sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * but, the performance is better than a "sequenced" approach. Also, the * + * Packet ID scheme (see below) ensures that the application will read * + * the packets in the proper order. Thus, this class guarantees delivery * + * and order of deliver. * + * * + * Each packet has a unique numerical ID; the ID is set to a count of the * + * number of packets sent. Different count values are provided, for both * + * DATA_ACK & DATA_NOACK packets. This ensures that the counter can be * + * used to detect resends of DATA_ACK packets; the counters for DATA_NOACK * + * packets aren't currently used. Other counters keep track of the * + * last-sequentially-received packet ID (for DATA_ACK packets), so we * + * can check for resends & missed packets, and the last-sequentially-read * + * packet ID, so we can ensure the app reads the packets in order. * + * * + * If the protocol being used already guarantees delivery of packets, * + * no ACK is required for the packets. In this case, the connection * + * class for this protocol can overload the Service routine to avoid * + * sending ACK packets, or the Connection Manager can just mark the * + * packet as ACK'd when it adds it to the Receive Queue for the connection.* + * * + * Derived classes must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef CONNECTION_H +#define CONNECTION_H + +/* +********************************* Includes ********************************** +*/ +#include "combuf.h" + +/* +********************************** Defines ********************************** +*/ +#define CONN_DEBUG 0 +/*--------------------------------------------------------------------------- +This structure is the header prefixed to any packet sent by the application. +MagicNumber: This is a number unique to the application; it's up to the + Receive_Packet routine to check this value, to be sure we're + not getting data from some other product. This value should + be unique for each application. +Code: This will be one of the below-defined codes. +PacketID: This is a unique numerical ID for this packet. The Connection + sets this ID on all packets sent out. +---------------------------------------------------------------------------*/ +typedef struct { + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; +} CommHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + These are the possible values for the Code field of the CommHeaderType: + .....................................................................*/ + enum ConnectionEnum { + PACKET_DATA_ACK, // this is a data packet requiring an ACK + PACKET_DATA_NOACK, // this is a data packet not requiring an ACK + PACKET_ACK, // this is an ACK for a packet + PACKET_COUNT // for computational purposes + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + ConnectionClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen = 0); + virtual ~ConnectionClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int * buflen); + + /*..................................................................... + The main polling routine for the connection. Should be called as often + as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine is used by the retry logic; returns the current time in + 60ths of a second. + .....................................................................*/ + static unsigned long Time (void); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned short Magic_Num (void) { return (MagicNum); } + unsigned long Retry_Delta (void) { return (RetryDelta); } + void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;} + unsigned long Max_Retries (void) { return (MaxRetries); } + void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;} + unsigned long Time_Out (void) { return (Timeout); } + void Set_TimeOut (unsigned long t) { Timeout = t;} + unsigned long Max_Packet_Len (void) { return (MaxPacketLen); } + static char * Command_Name(int command); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue(void); + virtual int Service_Receive_Queue(void); + + /*..................................................................... + This routine actually performs a hardware-dependent data send. It's + pure virtual, so it must be defined by a derived class. The routine + is protected; it's only called by the ACK/Retry logic, not the + application. + .....................................................................*/ + virtual int Send(char *buf, int buflen, void *extrabuf, + int extralen) = 0; + + /*..................................................................... + This is the maximum packet length, including our own internal header. + .....................................................................*/ + int MaxPacketLen; + + /*..................................................................... + Packet staging area; this is where the CommHeaderType gets tacked onto + the application's packet before it's sent. + .....................................................................*/ + char *PacketBuf; + + /*..................................................................... + This is the magic number assigned to this connection. It is the first + few bytes of any transmission. + .....................................................................*/ + unsigned short MagicNum; + + /*..................................................................... + This value determines the time delay before a packet is re-sent. + .....................................................................*/ + unsigned long RetryDelta; + + /*..................................................................... + This is the maximum number of retries allowed for a packet; if this + value is exceeded, the connection is probably broken. + .....................................................................*/ + unsigned long MaxRetries; + + /*..................................................................... + This is the total timeout for this connection; if this time is exceeded + on a packet, the connection is probably broken. + .....................................................................*/ + unsigned long Timeout; + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; + + /*..................................................................... + Names of all packet commands + .....................................................................*/ + static char * Commands[PACKET_COUNT]; +}; + +#endif + +/**************************** end of connect.h *****************************/ diff --git a/REDALERT/CONNMGR.H b/REDALERT/CONNMGR.H new file mode 100644 index 000000000..9bdfb0d31 --- /dev/null +++ b/REDALERT/CONNMGR.H @@ -0,0 +1,151 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager base class. This is an abstract base * + * class that's just a shell for more functional derived classes. * + * The main job of the Connection Manager classes is to parse a "pool" of * + * incoming packets, which may be from different computers, and distribute * + * those packets to Connection Classes via their Receive_Packet function. * + * * + * This class should be the only access to the network/modem for the * + * application, so if the app needs any functions to access the * + * connections or the queue's, the derived versions of this class should * + * provide them. * + * * + * It's up to the derived class to define: * + * - Service: polling routine; should Service each connection * + * - Init: initialization; should perform hardware-dependent * + * initialization, then Init each connection; this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Send_Message:sends a packet across the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Get_Message: gets a message from the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * * + * If the derived class supports multiple connections, it should provide * + * functions for creating the connections, associating them with a name * + * or ID or both, destroying them, and sending data through all or any * + * connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNMGR_H +#define CONNMGR_H + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONNECTION_NONE = -1 // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/Destructor. These currently do nothing. + .....................................................................*/ + ConnManClass (void) {}; + virtual ~ConnManClass () {}; + + /*..................................................................... + The Service routine: + - Parses incoming packets, and adds them to the Receive Queue for the + Connection Class(s) for this protocol + - Invokes each connection's Service routine; returns an error if the + connection's Service routine indicates an error. + .....................................................................*/ + virtual int Service (void) = 0; + + /*..................................................................... + Sending & receiving data + .....................................................................*/ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE) = 0; + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id) = 0; + + /*..................................................................... + Connection management + .....................................................................*/ + virtual int Num_Connections(void) = 0; + virtual int Connection_ID(int index) = 0; + virtual int Connection_Index(int id) = 0; + + /*..................................................................... + Queue utility routines + .....................................................................*/ + virtual int Global_Num_Send(void) = 0; + virtual int Global_Num_Receive(void) = 0; + virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0; + virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0; + + /*..................................................................... + Timing management + .....................................................................*/ + virtual void Reset_Response_Time(void) = 0; + virtual unsigned long Response_Time(void) = 0; + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) = 0; + + /*..................................................................... + Debugging + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount) = 0; +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh) = 0; +#endif + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This abstract class contains no data members; but a derived class + will contain: + - An instance of one or more derived Connection Classes + - A buffer to store incoming packets + .....................................................................*/ +}; + +#endif diff --git a/REDALERT/CONQUER.CPP b/REDALERT/CONQUER.CPP new file mode 100644 index 000000000..1262c2f59 --- /dev/null +++ b/REDALERT/CONQUER.CPP @@ -0,0 +1,5736 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONQUER.CPP 6 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * List_Copy -- Makes a copy of a cell offset list. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Owner_From_Name -- Convert an owner name into a bitfield. * + * Play_Movie -- Plays a VQ movie. * + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion. + * Is_Aftermath_Installed -- Function to determine the availability of the AM expansion. + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef TESTCODE +class A { + public: + enum {VAR=1}; +}; + +template +class B { + public: + enum {VAR2=T::VAR}; // this is the line in question. +}; + +B test; +#endif + + + +#include "function.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#else +#include "fakesock.h" +TcpipManagerClass Winsock; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" +#include "vortex.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#define PAGE_RESPOND_KEY KN_RETURN //KN_COMMA +#endif + +#ifdef MPEGMOVIE +#ifdef MCIMPEG +#include "mcimovie.h" +#endif +#include "movie.h" +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user); +#endif + +#define SHAPE_TRANS 0x40 + +void * Get_Shape_Header_Data(void * ptr); +extern bool Spawn_WChat(bool can_launch); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void); +#endif + +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef FIXIT_VERSION_3 // Stalemate games. +extern void Do_Draw(void); +#endif + +#ifdef CHEAT_KEYS +bool bNoMovies = false; +#endif + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(void); +void Keyboard_Process(KeyNumType & input); +static void Message_Input(KeyNumType &input); +void Color_Cycle(void); +bool Map_Edit_Loop(void); + +extern "C" { + bool UseOldShapeDraw = false; +} + +#ifdef CHEAT_KEYS +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char * string ); +#endif +static void Do_Record_Playback(void); + +void Toggle_Formation(void); + +extern "C" { + extern char * __nheapbeg; +} + +// +// Special module globals for recording and playback +// +char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type +char TeamNumber = 0; // which team was selected? (1-9) +char FormationEvent = 0; // 0 = no event, 1 = formation was toggled + + + /* -----------------10/14/96 7:29PM------------------ + + --------------------------------------------------*/ + +#if(TEN) +void TEN_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +void Main_Game(int argc, char * argv[]) +{ + static bool fade = true; + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + + // ST 5/14/2019 + if (RunningAsDLL) { + return; + } + + fade = false; + ScenarioInit = 0; // Kludge. + + fade = true; + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + GamePalette.Set(FADE_PALETTE_MEDIUM); + Keyboard->Clear(); + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (Session.Play) { + Hide_Mouse(); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } else { + Show_Mouse(); + } + +#ifdef WIN32 + if (Session.Type == GAME_INTERNET) { + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + } else { +#ifndef WOLAPI_INTEGRATION + DDEServer.Disable(); +#endif // !WOLAPI_INTEGRATION + } +#endif //WIN32 + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + PlayerPtr->Flag_To_Lose(); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + /* + ** Call the game's main loop + */ + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + +/*ifdef FIXIT_VERSION_3 // Stalemate games. + case SDLG_PROPOSE_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_PROPOSE_DRAW)) { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + case SDLG_ACCEPT_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_ACCEPT_DRAW)) { + OutList.Add(EventClass(EventClass::ACCEPT_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; +#endif +*/ + default: + break; + } + } + } +#endif + + +#ifdef WIN32 + /* + ** Send the game stats to WChat if we haven't already done so + */ + if (!GameStatisticsPacketSent && PacketLater) { + Send_Statistics_Packet(); // After game sending if PacketLater set. + } +#endif //WIN32 + + /* + ** Scenario is done; fade palette to black + */ + BlackPalette.Set(FADE_PALETTE_SLOW); + VisiblePage.Clear(); + + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if (Session.Record || Session.Play) { + Session.RecordFile.Close(); + } + + if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) { + if (!Session.Play) { + //PG Modem_Signoff(); + } + } else { + if (Session.Type == GAME_IPX) { + if (!Session.Play) { + //PG Shutdown_Network(); + } + } + } + +#if(TEN) + + if (Session.Type == GAME_TEN) { + Shutdown_TEN(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + Shutdown_MPATH(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // MPATH + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (Session.Play) { + Show_Mouse(); + Session.Type = GAME_NORMAL; + Session.Play = 0; + } +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + if (Special.IsFromWChat) { + //PG Shutdown_Network(); // Clear up the pseudo IPX stuff +#ifndef WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + } + + /* + ** Free the scenario description buffers + */ + Session.Free_Scenario_Descriptions(); +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +void Keyboard_Process(KeyNumType & input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); + +#ifdef WIN32 + /* + ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to + ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed. + */ + + KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT)); + KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT); + + +#else + KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT)); + KeyNumType key = plain; +#endif + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + HousesType h; + + switch (int(input)) { + case int(int(KN_M) | int(KN_SHIFT_BIT)): + case int(int(KN_M) | int(KN_ALT_BIT)): + case int(int(KN_M) | int(KN_CTRL_BIT)): + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + Houses.Ptr(h)->Refund_Money(10000); + } + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +#ifdef CHEAT_KEYS +#ifdef WIN32 + if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) { +#else + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { +#endif + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) { + if (Session.Type == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + } + + if (Debug_Flag && input == KN_SLASH) { + if (Session.Type != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +#endif + + /* + ** Process prerecorded team selection. This will be an additive select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; +#ifdef WIN32 + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; +#else + if (input & KN_SHIFT_BIT) action = 1; + if (input & KN_ALT_BIT) action = 3; + if (input & KN_CTRL_BIT) action = 2; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + if (key != 0 && key == Options.KeyNext) { + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + if (key != 0 && key == Options.KeyPrevious) { + if (action) { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + + + /* + ** All selected units will go into idle mode. + */ + if (key != 0 && key == Options.KeyStop) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to go into guard area mode. + */ + if (key != 0 && key == Options.KeyGuard) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to scatter. + */ + if (key != 0 && key == Options.KeyScatter) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) { + if (CurrentObject.Count()) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif + input = KN_NONE; + } else { + input = Options.KeyBase; + } + } + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + if (key != 0 && key == Options.KeyBase) { + Unselect_All(); + if (PlayerPtr->CurBuildings) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + if (building->IsLeader) break; + } + } + } + if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) { + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + } + if (CurrentObject.Count()) { + Map.Center_Map(); + } else { + if (PlayerPtr->Center != 0) { + Map.Center_Map(PlayerPtr->Center); + } + } + Map.Flag_To_Redraw(true); + input = KN_NONE; + } + + /* + ** Toggle the status of formation for the current team + */ + if (key != 0 && key == Options.KeyFormation) { + Toggle_Formation(); + input = KN_NONE; + } + +#ifdef TOFIX + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + if (input != 0 && input == Options.KeyResign) { + if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + input = KN_NONE; + } +#endif + + /* + ** Handle making and breaking alliances. + */ + if (key != 0 && key == Options.KeyAlliance) { + if (Session.Type != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); + } + } + } + input = KN_NONE; + } + + /* + ** Select all the units on the current display. This is equivalent to + ** drag selecting the whole view. + */ + if (key != 0 && key == Options.KeySelectView) { + Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight)); + input = KN_NONE; + } + + /* + ** Toggles the repair state similarly to pressing the repair button. + */ + if (key != 0 && key == Options.KeyRepair) { + Map.Repair_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the sell state similarly to pressing the sell button. + */ + if (key != 0 && key == Options.KeySell) { + Map.Sell_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the map zoom mode similarly to pressing the map button. + */ + if (key != 0 && key == Options.KeyMap) { + Map.Zoom_Mode_Control(); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar up one slot. + */ + if (key != 0 && key == Options.KeySidebarUp) { + Map.SidebarClass::Scroll(true, -1); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar down one slot. + */ + if (key != 0 && key == Options.KeySidebarDown) { + Map.SidebarClass::Scroll(false, -1); + input = KN_NONE; + } + + /* + ** Brings up the options dialog box. + */ + if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + } + + /* + ** Scrolls the tactical map in the direction specified. + */ + int distance = CELL_LEPTON_W; + if (key != 0 && key == Options.KeyScrollLeft) { + Map.Scroll_Map(DIR_W, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollRight) { + Map.Scroll_Map(DIR_E, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollUp) { + Map.Scroll_Map(DIR_N, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollDown) { + Map.Scroll_Map(DIR_S, distance, true); + input = KN_NONE; + } + + /* + ** Teams are handled by the 10 special team keys. The manual comparison + ** to the KN numbers is because the Windows keyboard driver can vary + ** the base code number for the key depending on the shift or alt key + ** state! + */ + if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) { + Handle_Team(0, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) { + Handle_Team(1, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) { + Handle_Team(2, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) { + Handle_Team(3, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) { + Handle_Team(4, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) { + Handle_Team(5, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) { + Handle_Team(6, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) { + Handle_Team(7, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) { + Handle_Team(8, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) { + Handle_Team(9, action); + input = KN_NONE; + } + + /* + ** Handle the bookmark hotkeys. + */ + if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) { + Handle_View(0, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) { + Handle_View(1, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) { + Handle_View(2, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) { + Handle_View(3, action); + input = KN_NONE; + } + +#ifdef CHEAT_KEYS + if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +void Toggle_Formation(void) { + +// MBL 03.23.2020: this has been copied to DLLExportClass::Team_Units_Formation_Toggle_On(), and modified as needed +#if 0 + int team = -1; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + if (team == -1) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +#endif +} + + +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=============================================================================================*/ +//#pragma off (unreferenced) +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+32]; + int id; +// SerialPacketType * serial_packet; + //int i; + KeyNumType copy_input; + //char *msg; + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ +#ifdef WOLAPI_INTEGRATION + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + ( ( input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) ) || input == PAGE_RESPOND_KEY ) && + !Session.Messages.Is_Edit()) { +#else + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) { +#endif + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) { + + strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:" + + Session.Messages.Add_Edit (Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) { + /* + ** For a network game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + +#ifdef WOLAPI_INTEGRATION + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan && input != PAGE_RESPOND_KEY ) { +#else + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) { +#endif + id = Ipx.Connection_ID(input - KN_F1); + Session.MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } +#ifdef WOLAPI_INTEGRATION + else if( Session.Type == GAME_INTERNET && pWolapi && !pWolapi->bConnectionDown && input == PAGE_RESPOND_KEY ) + { + if( *pWolapi->szExternalPager ) + { + // Respond to a page from external ww online user that paged me. + // Set MessageAddress to all zeroes, as a flag to ourselves later on. + NetNumType blip; + NetNodeType blop; + memset( blip, 0, 4 ); + memset( blop, 0, 6 ); + Session.MessageAddress = IPXAddressClass( blip, blop ); + + // Tell pWolapi not to reset szExternalPager for the time being. + pWolapi->bFreezeExternalPager = true; + + sprintf( txt, Text_String( TXT_TO ), pWolapi->szExternalPager ); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + Keyboard->Clear(); + } + else + { + Session.Messages.Add_Message( NULL, 0, TXT_WOL_NOTPAGED, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( VOC_SYS_ERROR ); + } + } +#endif + } +#if(TEN) + /* + ** For a TEN game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_TEN && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.TenMessageAddress = -1; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < Ten->Num_Connections() && !Session.ObiWan) { + + id = Ten->Connection_ID(input - KN_F1); + Session.TenMessageAddress = Ten->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),Ten->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // TEN +#if(MPATH) + /* + ** For a MPATH game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_MPATH && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MPathMessageAddress = 0; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < MPath->Num_Connections() && !Session.ObiWan) { + + id = MPath->Connection_ID(input - KN_F1); + Session.MPathMessageAddress = MPath->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),MPath->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // MPATH + } + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + copy_input = input; + rc = Session.Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1 && Session.Type != GAME_NORMAL) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. If the edit message was removed, + ** the map must be force-drawn, since it won't be able to compute the + ** cells to redraw; otherwise, let the map compute the cells to redraw, + ** by not force-drawing it, but just setting the IsToRedraw bit. + */ + if (rc==2 && Session.Type != GAME_NORMAL) { + if (copy_input==KN_ESC) { + Map.Flag_To_Redraw(true); +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + // Just in case user was responding to a page from outside the game, and we had frozen the "szExternalPager". + pWolapi->bFreezeExternalPager = false; +#endif + } else { + Map.Flag_To_Redraw(false); + } + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** Send a message + */ + if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) { +#if (0) + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, Session.Players[0]->Name); + serial_packet->ID = Session.ColorIdx; + + if (rc==3) { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** Send the message, and store this message in our LastMessage + ** buffer; the computer may send us a version of it later. + */ + NullModem.Send_Message(NullModem.BuildBuf, + sizeof(SerialPacketType), 1); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + strcpy(Session.LastMessage, serial_packet->Message.Message); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { +#ifdef WOLAPI_INTEGRATION + NetNumType blip; + NetNodeType blop; + Session.MessageAddress.Get_Address( blip, blop ); + if( blip[0] + blip[1] + blip[2] + blip[3] + blop[0] + blop[1] + blop[2] + blop[3] + blop[4] + blop[5] == 0 ) + { + // This message is a response to the last person that paged me. + if( pWolapi && !pWolapi->bConnectionDown ) // (As connection may have gone down.) + { + pWolapi->Page( pWolapi->szExternalPager, Session.Messages.Get_Edit_Buf(), false ); + pWolapi->bFreezeExternalPager = false; + } + } + else +#endif + { + + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (Session.MessageAddress.Is_Broadcast()) { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &Session.GPacket.Message.Buf[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + *ptr = 'X'; // force it to an odd hack so we know it was broadcast. + Enable_Secret_Units(); + } + #endif + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + &Session.MessageAddress); + Ipx.Service(); + + } + + /* + ** Store this message in our LastMessage buffer; the computer may send + ** us a version of it later. + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + +#if(TEN) + /* + ** TEN game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_TEN) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + Ten->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.TenMessageAddress); + } +#endif // TEN + +#if(MPATH) + /* + ** MPATH game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_MPATH) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + MPath->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.MPathMessageAddress); + } +#endif // MPATH + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); +#endif + } +} +//#pragma on (unreferenced) + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + * 07/16/1996 JLB : Faster pulsing of white color. * + *=============================================================================================*/ +void Color_Cycle(void) +{ + static CDTimerClass _timer; + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + bool changed = false; + + if (Options.IsPaletteScroll) { + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE]; + InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor); + + /* + ** Pulse the glowing embers between medium and dark red. + */ + InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80); + InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor); + + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer) { + _timer = TIMER_SECOND/4; + + RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1]; + for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) { + InGamePalette[index] = InGamePalette[index-1]; + } + InGamePalette[CYCLE_COLOR_START] = first; + + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + BStart(BENCH_PALETTE); + InGamePalette.Set(); +// Set_Palette(InGamePalette); + BEnd(BENCH_PALETTE); + } + } +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ + /* + ** Music and speech maintenance + */ + if (SampleType) { + Sound_Callback(); + Theme.AI(); + Speak_AI(); + } + + /* + ** Network maintenance. + */ + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + IPX_Call_Back(); + } + + /* + ** Serial game maintenance. + */ + if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) { + //NullModem.Service(); ST - 5/7/2019 + } + +#ifdef WOLAPI_INTEGRATION + // Wolapi maintenance. + if( pWolapi ) + { + if( pWolapi->bInGame ) + { + if( !pWolapi->bConnectionDown && ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT + 700; // Slower pump during games. + if( pWolapi->bConnectionDown ) + { + // Connection to server lost. + Session.Messages.Add_Message( NULL, 0, TXT_WOL_WOLAPIGONE, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( WOLSOUND_LOGOUT ); +// ajw (Wolapi object is now left around, so we can try to send game results.) +// // Kill wolapi. +// pWolapi->UnsetupCOMStuff(); +// delete pWolapi; +// pWolapi = NULL; + } + } + } + else + { + // When showing a modal dialog during chat, this pumping is turned on. It's turned off immediately following. + if( pWolapi->bPump_In_Call_Back && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + } + } +#endif + +#if(TEN) + if (Session.Type == GAME_TEN) { + TEN_Call_Back(); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + MPATH_Call_Back(); + } +#endif // MPATH +} + + +void IPX_Call_Back(void) +{ +#if (0)//PG + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!Session.NetOpen) { + if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) { + + if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ipx.Num_Connections(); i++) { + + int id = Ipx.Connection_ID(i); + + if (Session.GAddress == (*Ipx.Connection_Address(id))) { + Destroy_Connection(id, 0); + } + } + } else { + + /* + ** Process a message from another user. + */ + + if (Session.GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!Session.NetProtect) { + msg_ok = true; + } else { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (NewUnitsEnabled && !strncmp(Session.GPacket.Message.Buf,"XECRET UNITS ON ",15)) { + Session.GPacket.Message.Buf[0]='S'; + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } else { + Process_Global_Packet(&Session.GPacket, &Session.GAddress); + } + } + } + } + } +#endif +} + + +#if(TEN) +void TEN_Call_Back(void) +{ + int id; + + Ten->Service(); + + if (Ten->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.TenAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ten->Num_Connections(); i++) { + + id = Ten->Connection_ID(i); + + if (Session.TenAddress == Ten->Connection_Address(id)) { + Destroy_TEN_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // TEN + + +#if(MPATH) +void MPATH_Call_Back(void) +{ + int id; + + MPath->Service(); + + if (MPath->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.MPathAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < MPath->Num_Connections(); i++) { + + id = MPath->Connection_ID(i); + + if (Session.MPathAddress == MPath->Connection_Address(id)) { + Destroy_MPATH_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // MPATH + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const * Language_Name(char const * basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const * name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const * name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + + default: + break; + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will delay an amount of time according to the game speed setting. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Accumulate the number of 'spare' ticks that are frittered away here. + */ + SpareTicks += FrameTimer; + + /* + ** Delay until the frame timer expires. This forces the game loop to be regulated to a + ** speed controlled by the game options slider. + */ + while (FrameTimer) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + KeyNumType input = KN_NONE; + int x, y; + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + Color_Cycle(); + Call_Back(); +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); +#endif //WIN32 + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + +Mono_Set_Cursor(0,0); + + if (!GameActive) return(!GameActive); + +#ifdef WIN32 + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); +#endif + + /* + ** Sync-bug trapping code + */ + if (Frame >= Session.TrapFrame) { + Session.Trap_Object(); + } + + // + // Initialize our AI processing timer + // + Session.ProcessTimer = TickCount; + +#if 1 + if (Session.TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + BStart(BENCH_GAME_FRAME); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // In playback mode, run as fast as possible. + // + if (Session.Play) { + FrameTimer = 0; + } else { +#ifdef FIXIT_VERSION_3 + if( !Session.DesiredFrameRate ) + Session.DesiredFrameRate = 60; // A division by zero was happening (very rare). +#endif + framedelay = TIMER_SECOND / Session.DesiredFrameRate; + FrameTimer = framedelay; + } + } else { + if (Options.GameSpeed != 0) { + FrameTimer = Options.GameSpeed + + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) - + (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0); + } else { + FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0); + } + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!Session.Play) { +#ifdef WIN32 + if (SpecialDialog == SDLG_NONE && GameInFocus) { + WWMouse->Erase_Mouse(&HidPage, TRUE); +#else + if (SpecialDialog == SDLG_NONE) { +#endif + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (Session.Record || Session.Play) { + Do_Record_Playback(); + } + +#ifndef SORTDRAW + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will remain in sync. + */ + DisplayClass::Layer[LAYER_GROUND].Sort(); +#endif + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + TimeQuake = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!PendingTimeQuake) { + TimeQuakeCenter = 0; + } +#endif + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + Session.ProcessTicks += (TickCount - Session.ProcessTimer); + Session.ProcessFrames++; + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + Call_Back(); + + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + +#ifdef WIN32 + + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just won. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + return(!GameActive); + } + if (PlayerLoses) { +#ifdef WIN32 + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just lost. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + return(!GameActive); + } + if (PlayerRestarts) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + return(!GameActive); + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 && + Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + // End game in a draw. + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Help_Text(TXT_NONE); + Do_Draw(); + return(!GameActive); + } +#endif + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { + if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) { + GameActive = 0; + } + Map.Validate(); // give debugger a chance to catch it + } + } + + +#if (0) //ST - 5/8/2019 +#ifdef WIN32 + if (Debug_MotionCapture) { + static void ** _array = 0; + static int _sequence = 0; + static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE; + + if (_array == NULL) { + _array = new void * [_seqsize]; + memset(_array, '\0', _seqsize * sizeof(void*)); + } + + if (_array == NULL) { + Debug_MotionCapture = false; + } + + static GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + + int size = SeenBuff.Get_Width() * SeenBuff.Get_Height(); + + if (_sequence < _seqsize) { + if (_array[_sequence] == NULL) { + _array[_sequence] = new char[size]; + } + + if (_array[_sequence] != NULL) { + SeenBuff.Blit(temp_page); + memmove(_array[_sequence], temp_page.Get_Buffer(), size); + } + _sequence++; + + } else { + Debug_MotionCapture = false; + + CDFileClass file; + file.Cache(200000); + char filename[30]; + + for (int index = 0; index < _sequence; index++) { + memmove(temp_page.Get_Buffer(), _array[index], size); + sprintf(filename, "cap%04d.pcx", index); + file.Set_Name(filename); + + Write_PCX_File(file, temp_page, & GamePalette); + } + + _sequence = 0; + } + } +#endif +#endif + BEnd(BENCH_GAME_FRAME); + + Sync_Delay(); + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ +#ifdef WIN32 + HiddenPage.Clear(); +#else + HidPage.Clear(); +#endif + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + +#if (0) +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes) +{ + CCFileClass * file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + + default: + error = 0; + break; + } + + return(error); +} +#endif + +void Rebuild_Interpolated_Palette(unsigned char * interpal) +{ + for (int y=0; y<255; y++) { + for (int x=y+1; x<256; x++) { + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char * InterpolatedPalettes[100]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +/*********************************************************************************************** + * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs * + * * + * * + * * + * INPUT: Name of palette file * + * * + * OUTPUT: Number of palettes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/7/96 9:49AM ST : Created * + *=============================================================================================*/ +int Load_Interpolated_Palettes(char const * filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + + if (!add) { + for (i=0; i < ARRAY_SIZE(InterpolatedPalettes); i++) { + InterpolatedPalettes[i]=NULL; + } + start_palette=0; + } else { + for (start_palette = 0; start_palette < ARRAY_SIZE(InterpolatedPalettes); start_palette++) { + if (!InterpolatedPalettes[start_palette]) break; + } + } + + /* + ** Hack another interpolated palette if the requested one is + ** not present. + */ + if (!file.Is_Available()) { + file.Set_Name("AAGUN.VQP"); + } + + if (file.Is_Available()) { + + file.Open(READ); + file.Read(&num_palettes , 4); + + for (i=0; i < num_palettes; i++) { + InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); + memset (InterpolatedPalettes[i+start_palette], 0, 65536); + for (int y = 0; y < 256; y++) { + file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); + } + + Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); + } + + PalettesRead = TRUE; + file.Close(); + } + PaletteCounter = 0; + return (num_palettes); +} + + +void Free_Interpolated_Palettes(void) +{ + for (int i = 0; i < ARRAY_SIZE(InterpolatedPalettes) ;i++) { + if (InterpolatedPalettes[i]) { + free(InterpolatedPalettes[i]); + InterpolatedPalettes[i]=NULL; + } + } +} + + +/*********************************************************************************************** + * Play_Movie -- Plays a VQ movie. * + * * + * Use this routine to play a VQ movie. It will dispatch the specified movie to the * + * VQ player. The routine will not return until the movie has finished playing. * + * * + * INPUT: name -- The name of the movie file (sans ".VQA"). * + * * + * theme -- The identifier for an optional theme that should be played in the * + * background while this VQ plays. * + * * + * clrscrn -- 'true' if to clear the screen when the movie is over * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Suspend_Audio_Thread(void); +extern void Resume_Audio_Thread(void); + +#ifdef MOVIE640 +extern GraphicBufferClass VQ640; +#endif +#endif + +extern void Play_Movie_GlyphX(const char * movie_name, ThemeType theme, bool immediate); + +void Play_Movie(char const * name, ThemeType theme, bool clrscrn, bool immediate) + { +#if (1) + if (strcmp(name, "x") == 0 || strcmp(name, "X") == 0) { + return; + } + + Play_Movie_GlyphX(name, theme, immediate); + return; +#else + #ifdef MPEGMOVIE + //theme = theme; + //clrscrn = clrscrn; + if( Using_DVD() ) + { + if (PlayMpegMovie(name)) + return; + } + #endif + + #ifdef CHEAT_KEYS + // Mono_Printf("Movie: %s\n", name); + #endif //CHEAT_KEYS + /* + ** Don't play movies in editor mode + */ + if (Debug_Map) { + return; + } + #ifdef CHEAT_KEYS + // Mono_Printf("A\n"); + #endif //CHEAT_KEYS + /* + ** Don't play movies in multiplayer mode + */ + if (Session.Type != GAME_NORMAL) { + return; + } + #ifdef CHEAT_KEYS + //Mono_Printf("b\n"); + #endif //CHEAT_KEYS + + if (name) { + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, name, ".VQA"); + #ifdef WIN32 + char palname [_MAX_FNAME+_MAX_EXT]; + _makepath(palname , NULL, NULL, name, ".VQP"); + #endif //WIN32 + #ifdef CHEAT_KEYS + // Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); + #endif + + if (!CCFileClass(fullname).Is_Available()){ + #ifdef CHEAT_KEYS + // Mono_Printf("fullname: %s\n", fullname); + #endif //CHEAT_KEYS + return; + } + /* + ** Reset the anim control structure. + */ + Anim_Init(); + + /* + ** Prepare to play a movie. First hide the mouse and stop any score that is playing. + ** While the score (if any) is fading to silence, fade the palette to black as well. + ** When the palette has finished fading, wait until the score has finished fading + ** before launching the movie. + */ + Hide_Mouse(); + Theme.Queue_Song(theme); + if (PreserveVQAScreen == 0 && !clrscrn) { + BlackPalette.Set(FADE_PALETTE_MEDIUM); + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + PreserveVQAScreen = 0; + Keyboard->Clear(); + + VQAHandle * vqa = NULL; + + + #ifdef WIN32 + #ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 640; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } else { + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + } + #endif + #endif + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + if ((vqa = VQA_Alloc()) != NULL) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, fullname, &AnimControl) == 0) { + Brokeout = false; + #ifdef WIN32 + //Suspend_Audio_Thread(); + #ifdef MOVIE640 + if(!IsVQ640) { + Load_Interpolated_Palettes(palname); + } + #else + Load_Interpolated_Palettes(palname); + #endif + //Set_Palette(BlackPalette); + SysMemPage.Clear(); + InMovie = true; + #endif //WIN32 + VQA_Play(vqa, VQAMODE_RUN); + VQA_Close(vqa); + #ifdef WIN32 + //Resume_Audio_Thread(); + InMovie = FALSE; + #ifdef MOVIE640 + if(!IsVQ640) { + Free_Interpolated_Palettes(); + } + #else + Free_Interpolated_Palettes(); + #endif + IsVQ640 = false; + Set_Primary_Buffer_Format(); + #endif //WIN32 + + /* + ** Any movie that ends prematurely must have the screen + ** cleared to avoid any unexpected palette glitches. + */ + if (Brokeout) { + clrscrn = true; + VisiblePage.Clear(); + Brokeout = false; + } + } else { + #ifndef NDEBUG + bool error = true; + assert(error); + #endif + } + + VQA_Free(vqa); + } else { + assert(vqa != NULL); + } + #ifdef CHEAT_KEYS + //Mono_Printf("d"); + #endif //CHEAT_KEYS + /* + ** Presume that the screen is left in a garbage state as well as the palette + ** being in an unknown condition. Recover from this by clearing the screen and + ** forcing the palette to black. + */ + if (clrscrn) { + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + Show_Mouse(); + } +#endif +} + + +void Play_Movie(VQType name, ThemeType theme, bool clrscrn, bool immediate) +{ + if (name != VQ_NONE) { + if (name == VQ_REDINTRO) { + IsVQ640 = true; + } + Play_Movie(VQName[name], theme, clrscrn, immediate); + IsVQ640 = false; + } +} + + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +extern LPDIRECTDRAWPALETTE PalettePtr; + +bool PlayMpegMovie(const char* name) + { + char path[MAX_PATH]; + CCFileClass file; + const char* filename; + +#ifdef CHEAT_KEYS + if( bNoMovies ) + return true; +#endif + + sprintf(path, "movies\\%.8s.%.3s", name, "mpg"); + filename = file.Set_Name(path); + + if (!file.Is_Available()) + { + #if(1) + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + sprintf(path, "Couldn't find %s\n", filename); + WWMessageBox().Process(path); + #endif + return false; + } + + // Stop theme music + if (Misc_Focus_Loss_Function) + Misc_Focus_Loss_Function(); + + // Release primary surface + VisiblePage.Un_Init(); + + #ifdef MCIMPEG + if (MciMovie && MpgSettings && (MpgSettings->GetDeviceName() != NULL)) + { + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_NORMAL); + + if (!MciMovie->Open(filename, MpgSettings->GetDeviceName())) + { + WWMessageBox().Process("Couldn't open movie.\n"); + } + else if (!MciMovie->Play(MainWindow)) + { + WWMessageBox().Process("Couldn't play movie.\n"); + } + + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + else + #endif + { + DDSURFACEDESC ddsd; + IDirectDrawSurface* primary = NULL; + bool modeChange = false; + RECT rect; + + if (FAILED(DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 16))) + { + WWMessageBox().Process("Couldn't change display mode.\n"); + } + else + { + // Create primary surface reference + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if (FAILED(DirectDrawObject->CreateSurface(&ddsd, &primary, NULL))) + { + WWMessageBox().Process("Couldn't create primary movie surface.\n"); + } + else + { + rect.top = rect.left = 0; + rect.bottom = ScreenHeight; + rect.right = ScreenWidth; + + MpgSetCallback(MpegCallback, NULL); + MpgPlay(filename, DirectDrawObject, primary, &rect); + + if (primary) + primary->Release(); + + } + + DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 8); + } + } + + // Restore surfaces + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + PaletteSurface->SetPalette(PalettePtr); + AllSurfaces.Set_Surface_Focus(true); + AllSurfaces.Restore_Surfaces(); + return true; + } + + +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user) + { + static IDirectDrawPalette* _palette = NULL; + + user = user; + + switch (cmd) + { + case MPGCMD_ERROR: + WWMessageBox().Process((char const *)data); + break; + + case MPGCMD_INIT: + VisiblePage.Clear(); + break; + + case MPGCMD_CLEANUP: + VisiblePage.Clear(); + + if (_palette != NULL) + { + PaletteSurface->SetPalette(_palette); + _palette->Release(); + _palette = NULL; + } + break; + + case MPGCMD_PALETTE: + if (FAILED(PaletteSurface->GetPalette(&_palette))) + { + WWMessageBox().Process("Couldn't get primary palette.\n"); + } + else + { + if (FAILED(PaletteSurface->SetPalette((IDirectDrawPalette*)data))) + { + WWMessageBox().Process("Couldn't set movie palette.\n"); + } + } + break; + + case MPGCMD_UPDATE: + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) + { + if (Keyboard->Get() == KN_ESC) + { + Keyboard->Clear(); + return MPGRES_QUIT; + } + + Keyboard->Clear(); + } + + if (!GameInFocus) + { + MpgPause(); + + while (!GameInFocus) + { + Check_For_Focus_Loss(); + } + + MpgResume(); + return MPGRES_LOSTFOCUS; + } + break; + + default: + break; + } + + return MPGRES_CONTINUE; + } +#endif + +/*********************************************************************************************** + * Unselect_All -- Causes all selected objects to become unselected. * + * * + * This routine will unselect all objects that are currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void Unselect_All(void) +{ + //while (CurrentObject.Count()) { + // CurrentObject[0]->Unselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + while (CurrentObject.Count()) { + + int count_before = CurrentObject.Count(); + CurrentObject[0]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[0]); + } + } + //End of change - JAS 6/28/2019 + +} + + +void Unselect_All_Except(ObjectClass* object) +{ + int index = 0; + while (index < CurrentObject.Count()) { + + if (CurrentObject[index] == object) { + index++; + continue; + } + + int count_before = CurrentObject.Count(); + CurrentObject[index]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[index]); + } + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char * retval = NULL; + char * buffer = NULL; + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + +#if (0) + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL); +#endif + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we don't add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ +#ifdef WIN32 + void * ptr; + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); +#else //WIN#@ + if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) { +#endif //WIN32 + + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { +#ifdef WIN32 + + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); + +#else //WIN32 + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + int getx = (iconx * 24) + (x << 3) + 4; + int gety = (icony * 24) + (y << 3) + 4; + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); +#endif //WIN32 + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + +extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner); +extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation); + +void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, NULL, HOUSE_NONE); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + +void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale, char override_owner) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, shape_file_name, override_owner); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + + +void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) +{ + if (window == WINDOW_VIRTUAL) { + DLL_Draw_Pip_Intercept(object, shapenum); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). * + * * + * scale -- 24.8 fixed point scale factor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) +{ + int predoffset; +#ifdef WIN32 + unsigned long shape_pointer; +#endif //WIN32 + + + /* + ** Special kludge for E3 to prevent crashes + */ + if ((flags & SHAPE_GHOST) && (!ghostdata)) { + ghostdata = DisplayClass::SpecialGhost; + } + if ((flags & SHAPE_FADING) && (!fadingdata)) { + fadingdata = DisplayClass::FadingShade; + } + + static unsigned char * _xbuffer = 0; + + if (!_xbuffer) { + _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; + } + + if (shapefile != NULL && shapenum != -1) { + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + +#ifdef NEVER + /* + ** Perform a quick clip check against the destination rectangle. + */ + if (flags & SHAPE_CENTER) { + if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return; + if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width/2 < 0) return; + if (y+height/2 < 0) return; + + } else { + if (x >= WindowList[window][WINDOWWIDTH]) return; + if (y >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width < 0) return; + if (y+height < 0) return; + } +#endif + + +#ifdef WIN32 + /* + ** In WIn95, build shape returns a pointer to the shape not its size + */ + shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer); + if (shape_pointer) { + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer); + +#else //WIN32 + if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) { + GraphicViewPortClass draw_window(LogicPage, + WindowList[window][WINDOWX], + WindowList[window][WINDOWY], + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *)_ShapeBuffer; +#endif //WIN32 + + UseOldShapeDraw = false; + /* + ** Rotation handler. + */ + if (rotation != DIR_N) { + + /* + ** Get the raw shape data without the new header and flag to use the old shape drawing + */ + UseOldShapeDraw = true; +#ifdef WIN32 + buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer); +#endif + + if (Debug_Rotate) { +#if (0)//PG + GraphicBufferClass src(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass dst(width, height, _xbuffer); + Rotate_Bitmap(&src, &dst, rotation); + buffer = _xbuffer; +#endif + } else { + + BitmapClass bm(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass gb(width, height, _xbuffer); + TPoint2D pt(width/2, height/2); + + gb.Scale_Rotate(bm, pt, 0x0100, (256-(rotation-64))); + buffer = _xbuffer; + } + } + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = DisplayClass::SpecialGhost; + } + + predoffset = Frame; + + if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + predoffset = -predoffset; + } + + if (draw_window.Lock()) { + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + draw_window.Unlock(); + } + } + } +} + + +/*********************************************************************************************** + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * * + * This routine will calculate (using brute forced) the minimum rectangle that will * + * enclose the pixels of the shape. This rectangle will be relative to the upper left * + * corner of the maximum shape size. By using this minimum rectangle, it is possible to * + * greatly optimize the map 'dirty rectangle' logic. * + * * + * INPUT: shapedata -- Pointer to the shape data block. * + * * + * shapenum -- The shape number to examine. Each shape would have a different * + * dimension rectangle. * + * * + * OUTPUT: Returns with the rectangle that encloses the shape. * + * * + * WARNINGS: This routine uses brute force and is slow. It is presumed that the results * + * will be cached for subsiquent reuse. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Shape_Dimensions(void const * shapedata, int shapenum) +{ + Rect rect; + + if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) { + return(rect); + } + + char * shape; +#ifdef WIN32 + void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer); + if (sh == NULL) return(rect); +// shape = (char *)sh; + shape = (char *)Get_Shape_Header_Data(sh); +#else + Build_Frame(shapedata, shapenum, _ShapeBuffer); + shape = (char *)_ShapeBuffer; +#endif + + int width = Get_Build_Frame_Width(shapedata); + int height = Get_Build_Frame_Height(shapedata); + + rect.X = 0; + rect.Y = 0; + int xlimit = width-1; + int ylimit = height-1; + + /* + ** Find top edge of the shape. + */ + for (int y = 0; y <= ylimit; y++) { + for (int x = 0; x <= xlimit; x++) { + if (shape[y*width + x] != 0) { + rect.Y = y; + rect.X = x; + y = ylimit+1; + break; + } + } + } + + /* + ** Find bottom edge of the shape. + */ + for (int y = ylimit; y >= rect.Y; y--) { + for (int x = xlimit; x >= 0; x--) { + if (shape[y*width + x] != 0) { + rect.Height = (y-rect.Y)+1; + xlimit = x; + y = rect.Y-1; + break; + } + } + } + + /* + ** Find left edge of the shape. + */ + for (int x = 0; x < rect.X; x++) { + for (int y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.X = x; + x = rect.X; + break; + } + } + } + + /* + ** Find the right edge of the shape. + */ + for (int x = width-1; x >= xlimit; x--) { + for (int y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.Width = (x-rect.X)+1; + x = xlimit-1; + break; + } + } + } + + /* + ** Normalize the rectangle around the center of the shape. + */ + rect.X -= width / 2; + rect.Y -= height / 2; + + /* + ** Return with the minimum rectangle that encloses the shape. + */ + return(rect); +} + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference(UnitType(id))); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselTypeClass::As_Reference(VesselType(id))); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference(StructType(id))); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference(InfantryType(id))); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference(AircraftType(id))); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +void VQA_PauseAudio(void) {}; +void Check_VQ_Palette_Set(void); + +extern GraphicBufferClass VQ640; +extern bool IsVQ640; +long VQ_Call_Back(unsigned char *, long ) +{ + return 0; +#if (0)//PG + int key = 0; + if (Keyboard->Check()) { + key = Keyboard->Get(); + Keyboard->Clear(); + } + Check_VQ_Palette_Set(); +#ifdef MOVIE640 + if(IsVQ640) { + VQ640.Blit(SeenBuff); + } else { + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); + } +#else + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); +#endif + //Call_Back(); + + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus) { + VQA_PauseAudio(); + while (!GameInFocus) { + Check_For_Focus_Loss(); + } + } + return(false); +#endif +} + +#else //WIN32 + +long VQ_Call_Back(unsigned char *, long ) +{ + Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + Keyboard->Clear(); + } + return(false); +} +#endif //WIN32 + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + // + // Recording support + // + if (Session.Record) { + TeamNumber = (char)team; + TeamEvent = (char)action + 1; + } + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif //WIn32 + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: { + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = -1; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + } + } + } + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl && + (obj->Group == team) && (obj->Is_Selected_By_Player()) ) { + + /* + ** When a team is first created, they're created without a + ** formation offset, so they will not be created in + ** formation. Later, if they're assigned a formation, the + ** XFormOffset & YFormOffset numbers will change to valid + ** offsets, and they'll be formationed. + */ +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) obj->Group = team; + if (obj->Group == team && obj->Is_Selected_By_Player()) { +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + } + break; + } + + default: + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < ARRAY_SIZE(Scen.Views)) { + if (action == 0) { + + Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view] - (MAP_CELL_W * 4 * RESFACTOR) - (5*RESFACTOR)))); + +#ifdef WIN32 + /* + ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot. + */ + Map.Flag_To_Redraw (true); +#endif //WIN32 + } else { + Scen.Views[view] = Coord_Cell(Map.TacticalCoord) + (MAP_CELL_W*4*RESFACTOR) + (5*RESFACTOR); + } + } +} + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +static char * _CD_Volume_Label[] = { + "CD1", + "CD2", + "CD3", + "CD4", + // Denzil 4/15/98 + #ifdef DVD + "CD1", // ajw - Pushes RADVD to position 5, to match enum in Force_CD_Available(). 4 will never be returned here. + "RADVD", + #endif +}; +static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label); + + +#ifdef WIN32 +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert or CS * + * 3 = Aftermath + * 5 = DVD + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + * 01/20/97 V.Grippi added CS support * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) + { + char volume_name[128]; + char buffer[128]; + unsigned filename_length; + unsigned misc_dword; + int count = 0; + + CountDownTimerClass timer; + + timer.Set(timeout); + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + for (;;) + { + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + if (GetVolumeInformation ((char const *)buffer, &volume_name[0] , + (unsigned long)sizeof(volume_name), (unsigned long *)NULL , + (unsigned long *)&filename_length, (unsigned long *)&misc_dword, + (char *)NULL, (unsigned long)0)) + { + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], volume_name)) + return(i); + } + } + else + { + if (!count) + count++; + else + return -1; + } + } + else + { + /* + ** Failed to get the volume label on a known CD drive. + ** If this is a CD changer it may require time to swap the disks so dont return + ** immediately if the error is ROR_NOT_READY + */ + if (!timer.Time()) + return -1; + + int val = GetLastError(); + + if (val != ROR_NOT_READY) + return -1; + } + } + } +#else +int Get_CD_Index(int cd_drive, int) + { + char buffer[128]; + + /* + ** We need to do this twice because of the possibilities of a directory + ** being cached. If this is so, it will only be discovered when we + ** actually attempt to read a file from the drive. + */ + if(cd_drive) for (int count = 0; count < 2; count ++) + { + struct find_t ft; + int file; + int open_failed; + + /* + ** Create a path for the cd drive and attempt to read the volume label from + ** it. + */ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + /* + ** If we are able to read the volume label, this is good but not enough. + ** Further verification must be done. + */ + if (!_dos_findfirst(buffer, _A_VOLID, &ft)) + { + /* + ** Since some versions of disk cacheing software will cache the CD's + ** directory tracks, we may think the CD is in the drive when it is + ** actually not. To resolve this we must attempt to open a file on + ** the cd. Opening a file will always update the directory tracks + ** (suposedly). + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file); + + if (!open_failed) + { + _dos_close(file); + + /* + ** Hey some times the stupid dos driver appends a period to the + ** name if it is eight characters long. If the last char is a + ** period then erase it. + */ + if (ft.name[strlen(ft.name)-1] == '.') + { + ft.name[strlen(ft.name)-1] = 0; + } + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], ft.name)) + return (i); + } + } + } + } + + return -1; + } +#endif + + +/*********************************************************************************************** + * Force_CD_Available -- Ensures that specified CD is available. * + * * + * Call this routine when you need to ensure that the specified CD is actually in the * + * CD-ROM drive. * + * * + * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * + * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * + * 0 = CD1 + * 1 = CD2 + * 2 = Counterstrike + * 3 = Aftermath + * 4 = Counterstrike or Aftermath + * 5 = DVD + * -1 = Any CD + * -2 = Local Harddisk + * * + * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * + * the player pressed . * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + * 01/20/1997 V.Grippi added expansion cd message + *=============================================================================================*/ + +#if (1) //ST - 5/13/2019 +bool Force_CD_Available(int cd) +{ + static int _last = -1; + + if (_last != cd || cd == ALWAYS_RELOAD_CD) { + if (cd != ALWAYS_RELOAD_CD) { + _last = cd; + } + Theme.Stop(); + + // if (ConquerMix) delete ConquerMix; + if (MoviesMix) { + delete MoviesMix; + MoviesMix = 0; + } + if (GeneralMix) { + delete GeneralMix; + GeneralMix = 0; + } + if (ScoreMix) { + delete ScoreMix; + ScoreMix = 0; + } + if (MainMix) { + delete MainMix; + MainMix = 0; + } + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + // ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + else + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + } + + return true; +} + + +#endif + +#if (0) //ST - 5/13/2019 + +typedef enum { + CD_LOCAL = -2, + CD_ANY = -1, + CD_SOVIET = 0, + CD_ALLIED, + CD_COUNTERSTRIKE, + CD_AFTERMATH, + CD_CS_OR_AM, + CD_DVD +} CD_VOLUME; + +#ifdef FIXIT_VERSION_3 + +#ifndef DVD +#error DVD must be defined! +#endif + +bool Force_CD_Available( int cd_desired ) // ajw +{ + static int _last = -1; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + "ALERTE ROUGE DVD", +}; +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1", + "ALARMSTUFE ROT CD2", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + "ALARMSTUFE ROT DVD", + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + "RED ALERT DVD", + }; +#endif + + + + int new_cd_drive = 0; + int cd_current; + int current_drive; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd_desired == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_current = Get_CD_Index(current_drive, 1*60); + +// debugprint("Get_CD_Index just returned %d\n", cd_current); +// debugprint("We are checking for %d\n", cd_desired); +// debugprint("current_drive = %d\n", current_drive); + + if( Using_DVD() ) + { + // All requested cd indexes get rerouted to the DVD. + cd_desired = CD_DVD; +// if( RequiredCD != -1 ) +// RequiredCD = CD_DVD; // Just seems like a good idea. Not sure if necessary. ajw + } + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the current CD is requested or any CD will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) // Else we have already checked this cd. + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_current = Get_CD_Index(last_drive, 10*60); + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the cd is present or any cd will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + int drive_search_timeout = 2*60; + + for (;;) + { + char buffer[128]; + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + int cd_drive = CDList.Get_Next_CD_Drive(); + cd_current = Get_CD_Index(cd_drive, drive_search_timeout); + + if (cd_current >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + // Require CS or AM + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + if( cd_desired == CD_CS_OR_AM ) + cd_desired = CD_AFTERMATH; + + if( cd_desired == CD_DVD ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + } + else if( cd_desired == CD_COUNTERSTRIKE || cd_desired == CD_AFTERMATH ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd_desired]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd_desired]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd_desired]); + #endif + #endif + } + else if( cd_desired == CD_ANY ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd_desired+1, _cd_name[cd_desired]); + } + else // 0 or 1 + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd_desired+1, _cd_name[cd_desired]); + } + + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); +#ifdef FIXIT_VERSION_3 + while (hidden--) Hide_Mouse(); +#else + Hide_Mouse(); +#endif + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_current; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + if (cd_desired == 4) cd_desired--; + + // ajw - Added condition of cd_desired != 5 to the following if. + // Reason: This was triggering before Init_Secondary_Mixfiles(), which was screwing up the mixfile system somehow. + // + // Since the DVD is the only disk that can possibly be required when Using_DVD(), I never have to reload the mix + // files here, because no other disk could ever have been asked for. And if not Using_DVD(), cd_desired will never + // be equal to 5. So this is safe. + if (cd_desired > -1 && _last != cd_desired && cd_desired != 5) + { + _last = cd_desired; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + else + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + return(true); +} + +#else // FIXIT_VERSION_3 not defined + +bool Force_CD_Available(int cd) +{ + static int _last = -1; +// static char _palette[768]; +// static char _hold[256]; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + + // Denzil 4/15/98 + #ifdef DVD + "ALERTE ROUGE DVD", + #endif +}; + +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1 einlegen", + "ALARMSTUFE ROT CD2 einlegen", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + + // Denzil 4/15/98 + #ifdef DVD + "ALARMSTUFE ROT DVD einlegen", + #endif + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + + // Denzil 4/15/98 + #ifdef DVD + "RED ALERT DVD", + #endif + }; +#endif + + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + + ThemeType theme_playing = THEME_NONE; + +//#ifdef FIXIT_ANTS +// if(Scen.ScenarioName[2] == 'A') +// cd = 2; +//#endif + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + + #ifdef CHEAT_KEYS + // Mono_Printf("Get_CD_Index just returned %d\n", cd_index); + // Mono_Printf("We are checking for %d\n", cd); + // Mono_Printf("current_drive = %d\n", current_drive); + #endif //CHEAT_KEYS + + #ifdef DVD // Denzil + // CD1 and CD2 are ignored, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == CD_CS_OR_AM) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If the current drive is the DVD then requests for CD1 and CD2 are okay + if (cd_index == 4) + { + // CD1, CD2 & DVD requests + if (cd == 0 || cd == 1 || cd == 5) + { + cd_index = cd; + } + } + #endif + + // If the current CD is requested or any CD will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2 disks, force DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If CS or AM was the last drive then use it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If DVD is in drive + if (cd_index == 4) + { + // CD1, CD2 and DVD requests are all on the DVD + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + // If the cd is present or any cd will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) + { + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + cd_drive = CDList.Get_Next_CD_Drive(); + cd_index = Get_CD_Index(cd_drive, drive_search_timeout); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If the disk is CS or AM then request it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + if (cd_index == 4) + { + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + if (cd == cd_index || cd == -1 || cd == -2 ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(cd == 4) cd--; + + // CS or AM + if(cd == 2 || cd == 3) + { + #else + if(cd == 2) + { + #endif + + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd]); + #endif + #endif + } + else + { + #ifdef DVD + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + #else + if (cd == -1 ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]); + } + else + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]); + } + #endif + } + + #ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + #else + GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff); + #endif + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); + Hide_Mouse(); + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_index; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cd == 4) cd--; + #endif +// if (cd > -3 && _last != cd) { + if (cd > -1 && _last != cd) + { + _last = cd; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + + if (CCFileClass("MOVIES1.MIX").Is_Available()) + { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + } + else + { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + } + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + if (theme_playing != THEME_NONE) + { + Theme.Queue_Song(theme_playing); + } + + return(true); + } + + +#endif // FIXIT_VERSION_3 +#endif // ST - 5/13/2019 + + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + return 0x7fffffff; //ST - 5/8/2019 +#if (0) + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +#endif +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass * obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /* + ** Record a game + */ + if (Session.Record) { + + /* + ** Save the map's location + */ + Session.RecordFile.Write(&Map.DesiredTacticalCoord, + sizeof (Map.DesiredTacticalCoord)); + + /* + ** Save the current object list count + */ + count = CurrentObject.Count(); + Session.RecordFile.Write(&count, sizeof(count)); + + /* + ** Save a CRC of the selected-object list. + */ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + Session.RecordFile.Write (&sum, sizeof(sum)); + + /* + ** Save all selected objects. + */ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + Session.RecordFile.Write (&tgt, sizeof(tgt)); + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent)); + Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed)); + Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed)); + Session.RecordFile.Write (&FormMove, sizeof(FormMove)); + Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed)); + Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed)); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } + + /* + ** Play back a game ("attract" mode) + */ + if (Session.Play) { + + /* + ** Read & set the map's location. + */ + if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /* + ** Compute a CRC of the current object-selection list. + */ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /* + ** Load the CRC of the objects on disk; if it doesn't match, select + ** all objects as they're loaded. + */ + Session.RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) { + Unselect_All(); + } + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent)); + if (TeamEvent) { + Handle_Team(TeamNumber, TeamEvent - 1); + } + if (FormationEvent) { + Toggle_Formation(); + } + +Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed)); +Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed)); +Session.RecordFile.Read (&FormMove, sizeof(FormMove)); +Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed)); +Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed)); + /* + ** The map isn't drawn in playback mode, so draw it here. + */ + Map.Render(); + } +} + + +/*********************************************************************************************** + * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. * + * * + * * + * * + * INPUT: Name of file to load * + * * + * OUTPUT: Ptr to loaded file * + * * + * WARNINGS: Caller is responsible for releasing the memory allocated * + * * + * * + * HISTORY: * + * 5/13/96 3:20PM ST : Created * + *=============================================================================================*/ +void * Hires_Load(char * name) +{ + char filename[30]; + int length; + void * return_ptr; + +#ifdef WIN32 + sprintf(filename, "H%s", name); +#else + strcpy(filename, name); +#endif + CCFileClass file (filename); + + if (file.Is_Available()) { + + length = file.Size(); + return_ptr = new char[length]; + file.Read(return_ptr, length); + return (return_ptr); + + } else { + return (NULL); + } +} + + +/*********************************************************************************************** + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * * + * Use this routine to convert an ASCII crate name into a crate type. If no match could * + * be found, then CRATE_MONEY is assumed. * + * * + * INPUT: name -- Pointer to the crate name text to convert into a crate type. * + * * + * OUTPUT: Returns with the crate name converted into a crate type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +CrateType Crate_From_Name(char const * name) +{ + if (name != NULL) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + if (stricmp(name, CrateNames[crate]) == 0) return(crate); + } + } + return(CRATE_MONEY); +} + + +/*********************************************************************************************** + * Owner_From_Name -- Convert an owner name into a bitfield. * + * * + * This will take an owner specification and convert it into a bitfield that represents * + * it. Sometimes this will be just a single house bit, but other times it could be * + * all the allies or soviet house bits combined. * + * * + * INPUT: text -- Pointer to the text to convert into a house bitfield. * + * * + * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with * + * one bit per house type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int Owner_From_Name(char const * text) +{ + int ownable = 0; + if (stricmp(text, "soviet") == 0) { + ownable |= HOUSEF_SOVIET; + } else { + if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) { + ownable |= HOUSEF_ALLIES; + } else { + HousesType h = HouseTypeClass::From_Name(text); + if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) { + ownable |= (1 << h); + } + } + } + return(ownable); +} + + +/*********************************************************************************************** + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * * + * This routine will shake the game screen the number of shakes requested. * + * * + * INPUT: shakes -- The number of shakes to shake the screen. * + * house -- House to perform the shake for (or HOUSE_NONE if all players). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 BWG : Created. * + *=============================================================================================*/ +void Shake_The_Screen(int shakes, HousesType house) +{ + for (char h = HOUSE_FIRST; h < HOUSE_COUNT; ++h) { + if ((house != HOUSE_NONE) && (h != house)) { + continue; + } + HouseClass* hptr = HouseClass::As_Pointer((HousesType)h); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + hptr->ScreenShakeTime = CDTimerClass(hptr->ScreenShakeTime + shakes + shakes); + hptr->ScreenShakeTime.Start(); + } + } +#if (0) +#ifdef WIN32 + shakes += shakes; + + Hide_Mouse(); + SeenPage.Blit(HidPage); + int oldyoff = 0; + int newyoff = 0; + while(shakes--) { + int x = TickCount; +// CountDownTimer = 1; + do { + newyoff = Sim_Random_Pick(0,2) - 1; + } while (newyoff == oldyoff); + switch (newyoff) { + case -1: + HidPage.Blit(SeenPage, 0,2, 0,0, 640,398); + break; + case 0: + HidPage.Blit(SeenPage); + break; + case 1: + HidPage.Blit(SeenPage, 0,0, 0,2, 640,398); + break; + } while (x == TickCount); +// } while (CountDownTimer != 0) ; + } + HidPage.Blit(SeenPage); + Show_Mouse(); +#else + Shake_Screen(shakes); +#endif +#endif +} + + +/*********************************************************************************************** + * List_Copy -- Makes a copy of a cell offset list. * + * * + * This routine will make a copy of a cell offset list. It will only copy the significant * + * elements of the list limited by the maximum length specified. * + * * + * INPUT: source -- Pointer to a cell offset list. * + * * + * len -- The maximum number of cell offset elements to store in to the * + * destination list pointer. * + * * + * dest -- Pointer to the destination list to store the copy into. * + * * + * OUTPUT: none * + * * + * WARNINGS: Ensure that the destination list is large enough to hold the list copy. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void List_Copy(short const * source, int len, short * dest) +{ + if (dest == NULL || dest == NULL) { + return; + } + + while (len > 0) { + *dest = *source; + if (*dest == REFRESH_EOL) break; + dest++; + source++; + len--; + } +} + + + +#if 0 +// +// Boy, this function sure is crummy +// +void Crummy(int crumb1, int crumb2) +{ + if (Debug_Check_Map && Debug_Heap_Dump) { + Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2); + } +} +#endif + + + +/*********************************************************************************************** + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * This is located under HKEY_LOCAL_MACHINE. + * HISTORY: + * 11/19/98 ajw : Created + *=============================================================================================*/ +const char* Game_Registry_Key() +{ +#ifdef ENGLISH + static char szKey[] = "SOFTWARE\\Westwood\\Red Alert Windows 95 Edition"; +#else +#ifdef GERMAN + static char szKey[] = "SOFTWARE\\Westwood\\Alarmstufe Rot Windows 95 Edition"; +#else + static char szKey[] = "SOFTWARE\\Westwood\\Alerte Rouge version Windows 95"; +#endif +#endif + return szKey; +} + + +/*********************************************************************************************** + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Counterstrike is present * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 11:39PM ST : Created * + *=============================================================================================*/ +bool Is_Counterstrike_Installed (void) +{ + return true; // Remasters always have Counterstrike. ST - 10/18/2019 11:06AM + +#if (0) + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "CStrikeInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +#endif +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + *=============================================================================================*/ +bool Is_Aftermath_Installed (void) +{ + return true; // Remasters always have Aftermath. ST - 10/18/2019 11:06AM + +#if (0) + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "AftermathInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +#endif +} +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void) +{ +#if 0 + SecretUnitsEnabled=true; + UnitTypeClass::As_Reference(UNIT_PHASE).Level=10; + VesselTypeClass::As_Reference(VESSEL_CARRIER).Level=10; + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } +#endif +} +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ) +{ + // Calls Force_CD_Available based on type of scenario. szName is assumed to be an official scenario here. + if( Is_Mission_Counterstrike( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 4...\n" ); + return Force_CD_Available( 4 ); + } + else if( Is_Mission_Aftermath( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 3...\n" ); + return Force_CD_Available( 3 ); + } + return true; +} +#endif \ No newline at end of file diff --git a/REDALERT/CONQUER.H b/REDALERT/CONQUER.H new file mode 100644 index 000000000..8fb1b6d0a --- /dev/null +++ b/REDALERT/CONQUER.H @@ -0,0 +1,571 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define TXT_NONE 0 // +#define TXT_CREDIT_FORMAT 1 // %3d.%02d +#define TXT_TIME_FORMAT_HOURS 2 // Temps:%02d:%02d:%02d +#define TXT_TIME_FORMAT_NO_HOURS 3 // Temps:%02d:%02d +#define TXT_BUTTON_SELL 4 // Vente +#define TXT_SELL 5 // Vente structure +#define TXT_BUTTON_REPAIR 6 // R‚paration +#define TXT_YOU 7 // Vous : +#define TXT_ENEMY 8 // Ennemi : +#define TXT_BUILD_DEST 9 // Bƒtiments d‚truits par +#define TXT_UNIT_DEST 10 // Unit‚s d‚truites par +#define TXT_TIB_HARV 11 // Minerai r‚colt‚ par +#define TXT_SCORE_1 12 // Score: %d +#define TXT_YES 13 // Oui +#define TXT_NO 14 // Non +#define TXT_SCENARIO_WON 15 // Mission Accomplie +#define TXT_SCENARIO_LOST 16 // Mission Echou‚e +#define TXT_START_NEW_GAME 17 // Nouvelle partie +#define TXT_INTRO 18 // Intro/Preview +#define TXT_CANCEL 19 // Annuler +#define TXT_ROCK 20 // Rocher +#define TXT_CIVILIAN 21 // Civil +#define TXT_JP 22 // Equipe de confinement +#define TXT_OK 23 // OK +#define TXT_TREE 24 // Arbre +#define TXT_LEFT 25 //  +#define TXT_RIGHT 26 //  +#define TXT_UP 27 //  +#define TXT_DOWN 28 //  +#define TXT_CLEAR 29 // Effacer +#define TXT_WATER 30 // Eau +#define TXT_ROAD 31 // Route +#define TXT_SLOPE 32 // Pente +#define TXT_PATCH 33 // Patch +#define TXT_RIVER 34 // RiviŠre +#define TXT_LOAD_MISSION 35 // Charger Mission +#define TXT_SAVE_MISSION 36 // Sauvegarder Mission +#define TXT_DELETE_MISSION 37 // Effacer Mission +#define TXT_LOAD_BUTTON 38 // Charger +#define TXT_SAVE_BUTTON 39 // Sauvegarder +#define TXT_DELETE_BUTTON 40 // Effacer +#define TXT_GAME_CONTROLS 41 // Contr“les +#define TXT_SOUND_CONTROLS 42 // Son +#define TXT_RESUME_MISSION 43 // Reprendre Mission +#define TXT_VISUAL_CONTROLS 44 // Affichage +#define TXT_QUIT_MISSION 45 // Abandonner Mission +#define TXT_EXIT_GAME 46 // Quitter le jeu +#define TXT_OPTIONS 47 // Options +#define TXT_SQUISH 48 // D‚bris humains +#define TXT_CRATER 49 // CratŠre +#define TXT_SCORCH 50 // Marque de br–lure +#define TXT_BRIGHTNESS 51 // Luminosit‚ : +#define TXT_MUSIC 52 // Musique +#define TXT_VOLUME 53 // Effets sonores +#define TXT_TINT 54 // Teintes : +#define TXT_CONTRAST 55 // Contraste : +#define TXT_SPEED 56 // Vitesse du jeu : +#define TXT_SCROLLRATE 57 // Vitesse d‚filement : +#define TXT_COLOR 58 // Couleur : +#define TXT_RETURN_TO_GAME 59 // Revenir au jeu +#define TXT_ENEMY_SOLDIER 60 // Soldat ennemi +#define TXT_ENEMY_VEHICLE 61 // V‚hicule ennemi +#define TXT_ENEMY_STRUCTURE 62 // Structure ennemie +#define TXT_LTANK 63 // Tank l‚ger +#define TXT_MTANK 64 // Tank lourd +#define TXT_MTANK2 65 // Tank moyen +#define TXT_HTANK 66 // Tank Mammouth +#define TXT_SAM 67 // Missiles SAM +#define TXT_JEEP 68 // Ranger +#define TXT_TRANS 69 // H‚licoptŠre Chinook +#define TXT_HARVESTER 70 // Collecteur minerai +#define TXT_ARTY 71 // Artillerie +#define TXT_E1 72 // Mitrailleurs +#define TXT_E2 73 // Grenadiers +#define TXT_E3 74 // Bazookas +#define TXT_E4 75 // Lance-flammes +#define TXT_HELI 76 // H‚licoptŠre d'assaut +#define TXT_ORCA 77 // Hind +#define TXT_APC 78 // VBT +#define TXT_GUARD_TOWER 79 // Tour de garde +#define TXT_COMMAND 80 // D“me radar +#define TXT_HELIPAD 81 // H‚liport +#define TXT_AIRSTRIP 82 // Piste d'atterrissage +#define TXT_STORAGE 83 // Silo minerai +#define TXT_CONST_YARD 84 // Chantier de construction +#define TXT_REFINERY 85 // Raffinerie de minerai +#define TXT_CIV1 86 // Eglise +#define TXT_CIV2 87 // Chez Hans et Gretel +#define TXT_CIV3 88 // Manoir d'Hewitt +#define TXT_CIV4 89 // Maison de Ricktor +#define TXT_CIV5 90 // Maison de Gretchin +#define TXT_CIV6 91 // La grange +#define TXT_CIV7 92 // Pub Damon +#define TXT_CIV8 93 // Maison de Fran +#define TXT_CIV9 94 // Usine d'instruments +#define TXT_CIV10 95 // Fabricant de jouets +#define TXT_CIV11 96 // Maison de Ludwig +#define TXT_CIV12 97 // Meules de foin +#define TXT_CIV13 98 // Meule de foin +#define TXT_CIV14 99 // Champ de bl‚ +#define TXT_CIV15 100 // Champ en friche +#define TXT_CIV16 101 // Champ de ma‹s +#define TXT_CIV17 102 // Champ de c‚leri +#define TXT_CIV18 103 // Champ de pommes de terre +#define TXT_CIV20 104 // Maison de Sala +#define TXT_CIV21 105 // Maison d'Abdul +#define TXT_CIV22 106 // Le Pub Barjo de Pablo +#define TXT_CIV23 107 // Puits du village +#define TXT_CIV24 108 // Marchand de chameaux +#define TXT_CIV25 109 // Eglise +#define TXT_CIV26 110 // Maison d'Ali +#define TXT_CIV27 111 // Ted le Marchand +#define TXT_CIV28 112 // Maison de Menelik +#define TXT_CIV29 113 // Maison du pasteur John +#define TXT_CIV30 114 // Puits du village +#define TXT_CIV31 115 // Hutte du gu‚risseur +#define TXT_CIV32 116 // Hutte de Rikitikitembo +#define TXT_CIV33 117 // Hutte de Roarke +#define TXT_CIV34 118 // Hutte de Moubasa' +#define TXT_CIV35 119 // Hutte d'Aksoum +#define TXT_CIV36 120 // Hutte de Mambo +#define TXT_CIV37 121 // Le studio +#define TXT_CIVMISS 122 // Centre technologique +#define TXT_TURRET 123 // Tourelle +#define TXT_GUNBOAT 124 // Aviso-torpilleur +#define TXT_MCV 125 // V‚hicule de construction +#define TXT_POWER 126 // Centrale ‚lectrique +#define TXT_ADVANCED_POWER 127 // Centrale ‚lectrique avanc‚e +#define TXT_HOSPITAL 128 // H“pital +#define TXT_BARRACKS 129 // Caserne +#define TXT_PUMP 130 // Pompe +#define TXT_TANKER 131 // P‚trolier +#define TXT_SANDBAG_WALL 132 // Sacs de sable +#define TXT_CYCLONE_WALL 133 // Cl“ture grillag‚e +#define TXT_BRICK_WALL 134 // Mur de b‚ton +#define TXT_BARBWIRE_WALL 135 // Cl“ture barbel‚e +#define TXT_WOOD_WALL 136 // BarriŠre de bois +#define TXT_WEAPON_FACTORY 137 // Usine d'armement +#define TXT_AGUARD_TOWER 138 // Tour de garde avanc‚e +#define TXT_BIO_LAB 139 // Laboratoire biologique +#define TXT_FIX_IT 140 // Centre de service +#define TXT_TAB_SIDEBAR 141 // Contr“les +#define TXT_TAB_BUTTON_CONTROLS 142 // Options +#define TXT_TAB_BUTTON_DATABASE 143 // Base de donn‚es +#define TXT_SHADOW 144 // Terrain inconnu +#define TXT_OPTIONS_MENU 145 // Menu des options +#define TXT_STOP 146 // Stop +#define TXT_PLAY 147 // Lect +#define TXT_SHUFFLE 148 // Al‚at. +#define TXT_REPEAT 149 // R‚p‚ter +#define TXT_MUSIC_VOLUME 150 // Musique : +#define TXT_SOUND_VOLUME 151 // Effets sonores : +#define TXT_ON 152 // Oui +#define TXT_OFF 153 // Non +#define TXT_MULTIPLAYER_GAME 154 // Jeu Multijoueurs +#define TXT_NO_FILES 155 // Pas de fichiers disponibles +#define TXT_DELETE_SINGLE_FILE 156 // Voulez-vous effacer ce +#define TXT_DELETE_MULTIPLE_FILES 157 // Voulez-vous effacer %d +#define TXT_RESET_MENU 158 // D‚faut +#define TXT_CONFIRM_EXIT 159 // Voulez-vous abandonner la +#define TXT_MISSION_DESCRIPTION 160 // Description de la mission +#define TXT_C1 161 // Joe +#define TXT_C2 162 // Barry +#define TXT_C3 163 // Shelly +#define TXT_C4 164 // Maria +#define TXT_C5 165 // Karen +#define TXT_C6 166 // Steve +#define TXT_C7 167 // Phil +#define TXT_C8 168 // Dwight +#define TXT_C9 169 // Erik +#define TXT_EINSTEIN 170 // Prof. Einstein +#define TXT_BIB 171 // Cour +#define TXT_FASTER 172 // Rapide +#define TXT_SLOWER 173 // Lent +#define TXT_AIR_STRIKE 174 // Attaque a‚rienne +#define TXT_STEEL_CRATE 175 // Caisse d'acier +#define TXT_WOOD_CRATE 176 // Caisse de bois +#define TXT_WATER_CRATE 177 // Caisse flottante +#define TXT_FLAG_SPOT 178 // Emplacement du drapeau +#define TXT_UNABLE_READ_SCENARIO 179 // Lecture sc‚nario impossible +#define TXT_ERROR_LOADING_GAME 180 // Erreur de chargement ! +#define TXT_OBSOLETE_SAVEGAME 181 // Sauvegarde obsolŠte +#define TXT_MUSTENTER_DESCRIPTION 182 // Vous devez entrer une +#define TXT_ERROR_SAVING_GAME 183 // Erreur de sauvegarde ! +#define TXT_DELETE_FILE_QUERY 184 // Effacer ce fichier ? +#define TXT_EMPTY_SLOT 185 // [EMPLACEMENT VIDE] +#define TXT_SELECT_MPLAYER_GAME 186 // Choix du jeu Multijoueurs +#define TXT_MODEM_SERIAL 187 // Modem/S‚rie +#define TXT_NETWORK 188 // R‚seau +#define TXT_INIT_NET_ERROR 189 // Initialisation r‚seau +#define TXT_JOIN_NETWORK_GAME 190 // Rejoindre jeu en r‚seau +#define TXT_NEW 191 // Nouveau +#define TXT_JOIN 192 // Joindre +#define TXT_SEND_MESSAGE 193 // Envoi Message +#define TXT_YOUR_NAME 194 // Votre nom : +#define TXT_SIDE_COLON 195 // Camp : +#define TXT_COLOR_COLON 196 // Couleur : +#define TXT_GAMES 197 // Parties +#define TXT_PLAYERS 198 // Joueurs +#define TXT_SCENARIO_COLON 199 // Sc‚nario : +#define TXT_NOT_FOUND 200 // >> NON TROUVE << +#define TXT_START_CREDITS_COLON 201 // Cr‚dits : +#define TXT_BASES_COLON 202 // Bases : +#define TXT_TIBERIUM_COLON 203 // Minerai : +#define TXT_CRATES_COLON 204 // Caisses : +#define TXT_AI_PLAYERS_COLON 205 // Joueurs IA : +#define TXT_REQUEST_DENIED 206 // Demande refus‚e. +#define TXT_UNABLE_PLAY_WAAUGH 207 // Impossible de jouer, +#define TXT_NOTHING_TO_JOIN 208 // Aucune partie disponible ! +#define TXT_NAME_ERROR 209 // Vous devez entrer un nom ! +#define TXT_DUPENAMES_NOTALLOWED 210 // Les noms identiques ne sont +#define TXT_YOURGAME_OUTDATED 211 // La version de votre jeu est +#define TXT_DESTGAME_OUTDATED 212 // Version du jeu de +#define TXT_THATGUYS_GAME 213 // Partie de %s +#define TXT_THATGUYS_GAME_BRACKET 214 // [Partie de %s] +#define TXT_NETGAME_SETUP 215 // Configuration du jeu en +#define TXT_REJECT 216 // Rejeter +#define TXT_CANT_REJECT_SELF 217 // Vous ne pouvez pas vous +#define TXT_SELECT_PLAYER_REJECT 218 // Vous devez s‚lectionner un +#define TXT_BASES 219 // Bases +#define TXT_CRATES 220 // Caisses +#define TXT_AI_PLAYERS 221 // Joueur IA +#define TXT_SCENARIOS 222 // Sc‚narios +#define TXT_CREDITS_COLON 223 // Cr‚dits : +#define TXT_ONLY_ONE 224 // Un seul joueur ? +#define TXT_OOPS 225 // Oops ! +#define TXT_TO 226 // Pour %s : +#define TXT_TO_ALL 227 // Pour tous +#define TXT_MESSAGE 228 // Message : +#define TXT_CONNECTION_LOST 229 // Perte de connexion avec %s +#define TXT_LEFT_GAME 230 // %s a quitt‚ le jeu. +#define TXT_PLAYER_DEFEATED 231 // %s a ‚t‚ vaincu ! +#define TXT_WAITING_CONNECT 232 // Attente de connexion... +#define TXT_NULL_CONNERR_CHECK_CABLES 233 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_REDIALING 234 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_WAITING 235 // Erreur de connexion ! +#define TXT_SELECT_SERIAL_GAME 236 // Choix du jeu en s‚rie +#define TXT_DIAL_MODEM 237 // Appel +#define TXT_ANSWER_MODEM 238 // Attente appel +#define TXT_NULL_MODEM 239 // Null Modem +#define TXT_SETTINGS 240 // ParamŠtres +#define TXT_PORT_COLON 241 // Port : +#define TXT_IRQ_COLON 242 // IRQ : +#define TXT_BAUD_COLON 243 // Bauds : +#define TXT_INIT_STRING 244 // ChaŒne d'initialisation +#define TXT_CWAIT_STRING 245 // ChaŒne d'attente d'appel +#define TXT_TONE_BUTTON 246 // Tonalit‚ +#define TXT_PULSE_BUTTON 247 // Impulsions +#define TXT_HOST_SERIAL_GAME 248 // H“te du jeu en s‚rie +#define TXT_OPPONENT_COLON 249 // Adversaire : +#define TXT_USER_SIGNED_OFF 250 // Utilisateur reparti ! +#define TXT_JOIN_SERIAL_GAME 251 // Rejoindre jeu en s‚rie +#define TXT_PHONE_LIST 252 // R‚pertoire +#define TXT_ADD 253 // Ajouter +#define TXT_EDIT 254 // Editer +#define TXT_DIAL 255 // Appel +#define TXT_DEFAULT 256 // D‚faut +#define TXT_DEFAULT_SETTINGS 257 // D‚faut +#define TXT_CUSTOM_SETTINGS 258 // Autres paramŠtres +#define TXT_PHONE_LISTING 259 // R‚pertoire +#define TXT_NAME_COLON 260 // Nom : +#define TXT_NUMBER_COLON 261 // Num‚ro : +#define TXT_UNABLE_FIND_MODEM 262 // Modem non d‚tect‚. V‚rifiez +#define TXT_NO_CARRIER 263 // Pas de porteuse. +#define TXT_LINE_BUSY 264 // Ligne occup‚e. +#define TXT_NUMBER_INVALID 265 // Num‚ro incorrect. +#define TXT_SYSTEM_NOT_RESPONDING 266 // L'autre systŠme ne r‚pond +#define TXT_OUT_OF_SYNC 267 // Mauvaise synchronisation ! +#define TXT_PACKET_TOO_LATE 268 // Paquet re‡u trop tard ! +#define TXT_PLAYER_LEFT_GAME 269 // L'autre joueur a quitt‚ le +#define TXT_FROM 270 // De %s:%s +#define TXT_SCORE_TIME 271 // TEMPS : +#define TXT_SCORE_LEAD 272 // COMMANDEMENT : +#define TXT_SCORE_EFFI 273 // EFFICACITE : +#define TXT_SCORE_TOTA 274 // SCORE TOTAL : +#define TXT_SCORE_CASU 275 // PERTES : +#define TXT_SCORE_NEUT 276 // NEUTRE : +#define TXT_SCORE_BUIL 277 // BATIMENTS PERDUS +#define TXT_SCORE_BUIL1 278 // BATIMENTS +#define TXT_SCORE_BUIL2 279 // PERDUS : +#define TXT_SCORE_TOP 280 // MEILLEURS SCORES +#define TXT_SCORE_ENDCRED 281 // CREDITS DE FIN : +#define TXT_SCORE_TIMEFORMAT1 282 // %dh %dm +#define TXT_SCORE_TIMEFORMAT2 283 // %dm +#define TXT_DIALING 284 // Appel... +#define TXT_DIALING_CANCELED 285 // Appel annul‚ +#define TXT_WAITING_FOR_CALL 286 // Attente d'appel... +#define TXT_ANSWERING_CANCELED 287 // Attente d'appel annul‚e +#define TXT_E6 288 // Ing‚nieurs +#define TXT_E8 289 // Espion +#define TXT_MODEM_OR_LOOPBACK 290 // Pas de cƒble Null Modem +#define TXT_MAP 291 // Carte +#define TXT_BLOSSOM_TREE 292 // Arbre en fleurs +#define TXT_RESTATE_MISSION 293 // Briefing +#define TXT_COMPUTER 294 // Joueur IA +#define TXT_COUNT 295 // Nombre : +#define TXT_LEVEL 296 // Niveau : +#define TXT_OPPONENT 297 // Adversaire +#define TXT_KILLS_COLON 298 // Vict.: +#define TXT_VIDEO 299 // Vid‚o +#define TXT_C10 300 // Scientifique +#define TXT_CAPTURE_THE_FLAG 301 // Capture drapeau +#define TXT_OBJECTIVE 302 // Objectifs de mission +#define TXT_MISSION 303 // Mission +#define TXT_NO_SAVES 304 // Pas de sauvegardes +#define TXT_CIVILIAN_BUILDING 305 // Bƒtiment civil +#define TXT_TECHNICIAN 306 // Technicien +#define TXT_NO_SAVELOAD 307 // Sauvegarde interdite en +#define TXT_DELPHI 308 // Agent Sp‚cial 1 +#define TXT_TO_REPLAY 309 // Voulez-vous recommencer +#define TXT_RECONN_TO 310 // Reconnexion vers %s. +#define TXT_PLEASE_WAIT 311 // Attendez %02d secondes. +#define TXT_SURRENDER 312 // Voulez-vous vous rendre ? +#define TXT_SEL_TRANS 313 // CHOIX DE LA TRANSMISSION +#define TXT_GAMENAME_MUSTBE_UNIQUE 314 // Les sauvegardes ne peuvent +#define TXT_GAME_IS_CLOSED 315 // Partie ferm‚e. +#define TXT_NAME_MUSTBE_UNIQUE 316 // Les noms doivent ˆtre tous +#define TXT_RECONNECTING_TO 317 // Reconnexion vers %s +#define TXT_WAITING_FOR_CONNECTIONS 318 // Attente de connexions... +#define TXT_TIME_ALLOWED 319 // Temps autoris‚ : %02d +#define TXT_PRESS_ESC 320 // Appuyez sur Echap pour +#define TXT_JUST_YOU_AND_ME 321 // De l'ordinateur : Il ne +#define TXT_CAPTURE_THE_FLAG_COLON 322 // Capture du drapeau : +#define TXT_CHAN 323 // Agent Sp‚cial 2 +#define TXT_HAS_ALLIED 324 // %s s'est alli‚(e) avec %s +#define TXT_AT_WAR 325 // %s d‚clare la guerre … %s +#define TXT_SEL_TARGET 326 // Choisissez un cible +#define TXT_RESIGN 327 // Abandonner +#define TXT_TIBERIUM_FAST 328 // Le minerai pousse trŠs +#define TXT_ANSWERING 329 // R‚ponse en cours... +#define TXT_INITIALIZING_MODEM 330 // Initialisation Modem... +#define TXT_SCENARIOS_DO_NOT_MATCH 331 // Les sc‚narios ne +#define TXT_POWER_OUTPUT 332 // Production d'‚nergie +#define TXT_POWER_OUTPUT_LOW 333 // Production d'‚nergie +#define TXT_CONTINUE 334 // Continuer +#define TXT_QUEUE_FULL 335 // Saturation des donn‚es … +#define TXT_SPECIAL_WARNING 336 // %s a modifi‚ les options de +#define TXT_CD_DIALOG_1 337 // Placez un CD d'Alerte Rouge +#define TXT_CD_DIALOG_2 338 // Placez le CD %d (%s) dans +#define TXT_CD_ERROR1 339 // Alerte Rouge n'a pas +#define TXT_NO_SOUND_CARD 340 // Pas de carte sonore +#define TXT_UNKNOWN 341 // INCONNU +#define TXT_OLD_GAME 342 // (ancien) +#define TXT_NO_SPACE 343 // Espace disque insuffisant +#define TXT_MUST_HAVE_SPACE 344 // Vous devez disposer de %d +#define TXT_RUN_SETUP 345 // Lancez d'abord le programme +#define TXT_WAITING_FOR_OPPONENT 346 // Attente adversaire +#define TXT_SELECT_SETTINGS 347 // Choisissez l'option +#define TXT_PRISON 348 // Prison +#define TXT_GAME_WAS_SAVED 349 // Mission sauvegard‚e +#define TXT_SPACE_CANT_SAVE 350 // Espace disque insuffisant +#define TXT_INVALID_PORT_ADDRESS 351 // Port/Adresse invalide. COM +#define TXT_INVALID_SETTINGS 352 // paramŠtres Port et/ou IRQ +#define TXT_IRQ_ALREADY_IN_USE 353 // IRQ d‚j… utilis‚ +#define TXT_ABORT 354 // Oui +#define TXT_RESTART 355 // Recommencer +#define TXT_RESTARTING 356 // Mission relanc‚e. Attendez +#define TXT_LOADING 357 // Chargement de la mission. +#define TXT_ERROR_IN_INITSTRING 358 // Erreur chaŒne +#define TXT_SHADOW_COLON 359 // Ombre +#define TXT_AVMINE 360 // Mine Anti-V‚hicule +#define TXT_APMINE 361 // Mine Anti-Personnel +#define TXT_NEW_MISSIONS 362 // Nouvelles missions +#define TXT_THIEF 363 // Voleur +#define TXT_MRJ 364 // Brouilleur de radar +#define TXT_GAP_GENERATOR 365 // G‚n‚rateur d'ombre +#define TXT_PILLBOX 366 // Bunker +#define TXT_CAMOPILLBOX 367 // Bunker camoufl‚ +#define TXT_CHRONOSPHERE 368 // ChronosphŠre +#define TXT_ENGLAND 369 // Roy. Uni +#define TXT_GERMANY 370 // Allemagne +#define TXT_SPAIN 371 // Espagne +#define TXT_USSR 372 // URSS +#define TXT_UKRAINE 373 // Ukraine +#define TXT_GREECE 374 // GrŠce +#define TXT_FRANCE 375 // France +#define TXT_TURKEY 376 // Turquie +#define TXT_SHORE 377 // Rivage +#define TXT_PLACE_OBJECT 378 // Choisir objet +#define TXT_SS 379 // Sous-marin +#define TXT_DD 380 // Contre-torpilleur +#define TXT_CA 381 // Croiseur +#define TXT_TRANSPORT 382 // Transport +#define TXT_PT 383 // Aviso-torpilleur +#define TXT_LOBBY 384 // Hall +#define TXT_CHANNEL_GAMES 385 // Parties +#define TXT_SAVING_GAME 386 // Sauvegarder partie... +#define TXT_GAME_FULL 387 // La partie est au complet. +#define TXT_MUST_SELECT_GAME 388 // Vous devez s‚lectionner une +#define TXT_S_PLAYING_S 389 // %s joue contre %s +#define TXT_ONLY_HOST_CAN_MODIFY 390 // Seul l'h“te peut modifier +#define TXT_GAME_CANCELLED 391 // La partie a ‚t‚ annul‚e. +#define TXT_S_FORMED_NEW_GAME 392 // %s a initi‚ une nouvelle +#define TXT_GAME_NOW_IN_PROGRESS 393 // La partie de %s est +#define TXT_TESLA 394 // Bobine de Tesla +#define TXT_MGG 395 // G‚n‚rateur d'ombre mobile +#define TXT_FLAME_TURRET 396 // Tour lance-flammes +#define TXT_AAGUN 397 // Canon Anti-Avion +#define TXT_KENNEL 398 // Niche +#define TXT_SOVIET_TECH 399 // Centre Technique +#define TXT_BADGER 400 // Bombardier +#define TXT_MIG 401 // Mig +#define TXT_YAK 402 // Yak +#define TXT_FENCE 403 // Barbel‚s +#define TXT_MEDIC 404 // M‚decin +#define TXT_SABOTEUR 405 // Saboteur +#define TXT_GENERAL 406 // G‚n‚ral +#define TXT_E7 407 // Tanya +#define TXT_PARA_BOMB 408 // Parabombes +#define TXT_PARA_INFANTRY 409 // Parachutistes +#define TXT_PARA_SABOTEUR 410 // Saboteur parachutiste +#define TXT_SHIP_YARD 411 // Chantier naval +#define TXT_SUB_PEN 412 // Port sous-marin +#define TXT_SCENARIO_OPTIONS 413 // Options Sc‚nario +#define TXT_SPY_MISSION 414 // Avion espion +#define TXT_U2 415 // Avion espion +#define TXT_GUARD_DOG 416 // Chien d'attaque +#define TXT_SPY_INFO 417 // Info Espion +#define TXT_BUILDNGS 418 // Bƒtiments +#define TXT_UNITS 419 // Unit‚s +#define TXT_INFANTRY 420 // Infanterie +#define TXT_AIRCRAFT 421 // Avion +#define TXT_TRUCK 422 // Camion d'approvisionnement +#define TXT_INVUL 423 // Module d'invuln‚rabilit‚ +#define TXT_IRON_CURTAIN 424 // Rideau de Fer +#define TXT_ADVANCED_TECH 425 // Centre technique avanc‚ +#define TXT_V2_LAUNCHER 426 // Lance-roquettes V2 +#define TXT_FORWARD_COM 427 // Poste de commandement +#define TXT_DEMOLITIONER 428 // Bombardeur +#define TXT_MINE_LAYER 429 // Poseur de mines +#define TXT_FAKE_CONST 430 // Chantier de construction +#define TXT_FAKE_WEAP 431 // Usine d'armement leurre +#define TXT_FAKE_YARD 432 // Chantier naval leurre +#define TXT_FAKE_PEN 433 // Port sous-marin leurre +#define TXT_FAKE_RADAR 434 // D“me radar leurre +#define TXT_THEME_BIGF 435 // Bigfoot +#define TXT_THEME_CRUS 436 // La r‚volte +#define TXT_THEME_FAC1 437 // A l'attaque 1 +#define TXT_THEME_FAC2 438 // A l'attaque 2 +#define TXT_THEME_HELL 439 // Marche de l'enfer +#define TXT_THEME_RUN1 440 // Sauve-qui-peut +#define TXT_THEME_SMSH 441 // La d‚bƒcle +#define TXT_THEME_TREN 442 // Tranch‚es +#define TXT_THEME_WORK 443 // Les professionnels +#define TXT_THEME_AWAIT 444 // Attente +#define TXT_THEME_DENSE_R 445 // Dense +#define TXT_THEME_MAP 446 // S‚lection carte +#define TXT_THEME_FOGGER1A 447 // Fogger +#define TXT_THEME_MUD1A 448 // Boue +#define TXT_THEME_RADIO2 449 // Radio 2 +#define TXT_THEME_ROLLOUT 450 // Laminage +#define TXT_THEME_SNAKE 451 // Serpent +#define TXT_THEME_TERMINAT 452 // Extermination +#define TXT_THEME_TWIN 453 // Jumeau +#define TXT_THEME_VECTOR1A 454 // Vecteur +#define TXT_TEAM_MEMBERS 455 // Equipiers +#define TXT_BRIDGE 456 // Pont +#define TXT_BARREL 457 // Baril +#define TXT_GOODGUY 458 // Amical +#define TXT_BADGUY 459 // Ennemi +#define TXT_GOLD 460 // Or +#define TXT_GEMS 461 // Gemmes +#define TXT_TEASER 462 // Film titre +#define TXT_MOVIES 463 // Films +#define TXT_INTERIOR 464 // Int‚rieur +#define TXT_SONAR_PULSE 465 // Signal sonar +#define TXT_MSLO 466 // Silo de missiles +#define TXT_GPS_SATELLITE 467 // Satellite GPS +#define TXT_NUCLEAR_BOMB 468 // Bombe atomique +#define TXT_EASY 469 // Facile +#define TXT_HARD 470 // Difficile +#define TXT_NORMAL 471 // Normal +#define TXT_DIFFICULTY 472 // S‚lectionnez un niveau de +#define TXT_ALLIES 473 // Alli‚s +#define TXT_SOVIET 474 // Soviets +#define TXT_THEME_INTRO 475 // ThŠme Intro +#define TXT_SHADOW_REGROWS 476 // Progr. ombre +#define TXT_ORE_SPREADS 477 // Progr. minerai +#define TXT_THEME_SCORE 478 // Musiques +#define TXT_INTERNET 479 // Internet +#define TXT_ICE 480 // Glace +#define TXT_CRATE 481 // Caisses +#define TXT_SKIRMISH 482 // Escarmouche +#define TXT_CHOOSE 483 // Choisissez votre camp. +#define TXT_MINERALS 484 // Min‚raux pr‚cieux +#define TXT_IGNORE 485 // Ignorer +#define TXT_ERROR_NO_RESP 486 // Erreur - le modem ne r‚pond +#define TXT_ERROR_NO_RESCODE 487 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_INIT 488 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_VERB 489 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_ECHO 490 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_DISABLE 491 // Erreur - Impossible de +#define TXT_ERROR_TOO_MANY 492 // Erreur - Trop d'erreurs +#define TXT_ERROR_ERROR 493 // Erreur - Le modem a +#define TXT_ERROR_TIMEOUT 494 // Erreur - Temps d'attente de +#define TXT_ACCOMPLISHED 495 // Accompli +#define TXT_CLICK_CONTINUE 496 // Cliquez pour continuer +#define TXT_RECEIVING_SCENARIO 497 // R‚ception du sc‚nario de +#define TXT_SENDING_SCENARIO 498 // Envoi du sc‚nario aux +#define TXT_NO_FLOW_CONTROL_RESPONSE 499 // Erreur - Le modem n'a pas +#define TXT_NO_COMPRESSION_RESPONSE 500 // Erreur - Le modem n'a pas +#define TXT_NO_ERROR_CORRECTION_RESPONSE 501 // Erreur - Le modem n'a pas +#define TXT_EXPLAIN_REGISTRATION 502 // Pour jouer … Alerte Rouge +#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 503 // Erreur - Impossible +#define TXT_REGISTER 504 // Enregistrer +#define TXT_ORE_MINE 505 // Gisement Minerai +#define TXT_NO_REGISTERED_MODEM 506 // Aucun modem configur‚ +#define TXT_CHRONOSHIFT 507 // D‚placement chronoporte +#define TXT_UNABLE_TO_OPEN_PORT 508 // Adresse invalide ou en +#define TXT_NO_DIAL_TONE 509 // Pas de tonalit‚. +#define TXT_NO_EXPANSION_SCENARIO 510 // Erreur - L'autre joueur n'a +#define TXT_STAND_BY 511 // Patientez SVP... +#define TXT_THEME_CREDITS 512 // Musique du g‚n‚rique de fin +#define TXT_POWER_AAGUN 513 // Puissance faible: Canon(s) +#define TXT_POWER_TESLA 514 // Puissance faible: Bobine(s) +#define TXT_LOW_POWER 515 // Puissance Faible +#define TXT_COMMANDER 516 // Commandant: +#define TXT_BATTLES_WON 517 // Parties gagn‚es: +#define TXT_MISMATCH 518 // Fichier de donn‚es du jeu +#define TXT_SCENARIO_ERROR 519 // Votre version de jeu +#define TXT_CONNECTING 520 // Connecting +#define TXT_MODEM_INITIALISATION 521 // Initialisation du Modem +#define TXT_DATA_COMPRESSION 522 // Compression des Donn‚es +#define TXT_ERROR_CORRECTION 523 // Correction d'Erreur +#define TXT_HARDWARE_FLOW_CONTROL 524 // Contr“le Mat‚riel du Flux +#define TXT_ADVANCED 525 // Avanc‚ +#define TXT_THEME_2ND_HAND 526 // Seconde main +#define TXT_THEME_ARAZOID 527 // Arazo‹de +#define TXT_THEME_BACKSTAB 528 // Retour … l'envoyeur +#define TXT_THEME_CHAOS2 529 // Chaos2 +#define TXT_THEME_SHUT_IT 530 // Fermez-la ! +#define TXT_THEME_TWINMIX1 531 // Visite de courtoisie +#define TXT_THEME_UNDER3 532 // A couvert +#define TXT_THEME_VR2 533 // VR2 +#define TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING 534 // L'autre systŠme ne r‚pond +#define TXT_ASK_EMERGENCY_SAVE_HUNG_UP 535 // L'autre systŠme a +#define TXT_NO_REG_APP 536 // Alerte Rouge n'a pu +#define TXT_NO_CS_SCENARIOS 537 // Un joueur de la partie n'a +#define TXT_MISSILESUB 538 // Sous-marin MS +#define TXT_SHOCKTROOPER 539 // Electrocuteur +#define TXT_MECHANIC 540 // M‚canicien +#define TXT_CHRONOTANK 541 // Chrono Tank +#define TXT_TESLATANK 542 // Tank Tesla +#define TXT_MAD 543 // Tank M.A.D. +#define TXT_DEMOTRUCK 544 // Camion de d‚molition +#define TXT_PHASETRANSPORT 545 // Transport Cam‚l‚on +#define TXT_THEME_BOG 546 // Mar‚cages +#define TXT_THEME_FLOAT_V2 547 // Volutes +#define TXT_THEME_GLOOM 548 // T‚nŠbres +#define TXT_THEME_GRNDWIRE 549 // Terrain min‚ +#define TXT_THEME_RPT 550 // M‚caniciens 2 +#define TXT_THEME_SEARCH 551 // Battue +#define TXT_THEME_TRACTION 552 // Traction +#define TXT_THEME_WASTELND 553 // Chaos +#define TXT_CARRIER 554 // H‚liport Mobile +#define TXT_INSUFFICIENT_FUNDS 555 \ No newline at end of file diff --git a/REDALERT/CONST.CPP b/REDALERT/CONST.CPP new file mode 100644 index 000000000..912b70a51 --- /dev/null +++ b/REDALERT/CONST.CPP @@ -0,0 +1,845 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 20, 1993 * + * * + * Last Update : September 20, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the access passwords used to activate cheat mode, editor mode, +** and special game options. +*/ +unsigned long const PlayCodes[] = { + 0xE0792D6D, // Dwight Okahara + 0x90046ECF, // Paul S. Mudra + 0xC3EE9A26, // Frank Klepaki + 0xED382178, // Ed Del Castillo + 0L +}; + +unsigned long const CheatCodes[] = { + 0xA0E2AB53, // Joseph Hewitt + 0x00532693, // Mike Lightner + 0x7DDFF824, // Joe Bostic + 0x2CB5CF01, // Phil Gorrow + 0xB5B63531, // Bill Randolph + 0xDFABC23A, // Adam Isgreen + 0x52B19A22, // Erik Yeo + 0xBE79088C, // David Dettmer + 0xB216AE7E, // Barry Green + 0x0E07B213, // Steve Tall + 0L +}; + + +unsigned long const EditorCodes[] = { + 0xA2C09326, // Erik Yeo + 0x1F944BB3, // Mike Lightner + 0xDE07154D, // Adam Isgreen + 0x0E07B213, // Steve Tall + 0x16B170B1, // Joe Bostic + 0L +}; + + +/*********************************************************************************************** +** Unit order names. These names correspond to the player selectable orders +** a unit can have. The system initiated orders have no use for the ASCII name +** associated, but they are listed here for completeness sake. +*/ +char const * Missions[MISSION_COUNT] = { + "Sleep", + "Attack", + "Move", + "QMove", + "Retreat", + "Guard", + "Sticky", + "Enter", + "Capture", + "Harvest", + "Area Guard", + "Return", + "Stop", + "Ambush", + "Hunt", + "Unload", + "Sabotage", + "Construction", + "Selling", + "Repair", + "Rescue", + "Missile", + "Harmless" +}; + + +/*************************************************************************** +** Special weapon names. +*/ +#ifdef SCENARIO_EDITOR +char const * const SpecialWeaponName[SPC_COUNT] = { + "Sonar Pulse", + "Nuclear Missile", + "Chronosphere", + "Parachute Bomb", + "Paratroopers", + "Recon Plane", + "Iron Curtain", + "GPS Satellite" +}; +#endif +int const SpecialWeaponHelp[SPC_COUNT] = { + TXT_SONAR_PULSE, + TXT_NUCLEAR_BOMB, + TXT_CHRONOSHIFT, + TXT_PARA_BOMB, + TXT_PARA_INFANTRY, + TXT_SPY_MISSION, + TXT_INVUL, + TXT_GPS_SATELLITE +}; +char const * const SpecialWeaponFile[SPC_COUNT] = { + "SONR", + "ATOM", + "WARP", + "PBMB", + "PINF", + "CAM", + "INFX", + "GPSS" +}; + + +/*************************************************************************** +** Type of quarry to search out and attack. These values are used for team +** attack missions. +*/ +char const * const QuarryName[QUARRY_COUNT] = { + "N/A", + "Anything", + "Buildings - any", + "Harvesters", + "Infantry", + "Vehicles - any", + "Ships - any", + "Factories", + "Base Defenses", + "Base Threats", + "Power Facilities", + "Fake Buildings" +}; + + +/*************************************************************************** +** These are the text names for the formation types. +*/ +char const * const FormationName[FORMATION_COUNT] = { + "None", + + "Tight", + "Loose", + "Wedge North", + "Wedge East", + "Wedge South", + "Wedge West", + "Line N/S", + "Line E/W" +}; + + +/*************************************************************************** +** These are the ASCII names for the reinforcement sources. +*/ +char const * const SourceName[SOURCE_COUNT] = +{ + "North", + "East", + "South", + "West", + "Air" +}; + + +/*************************************************************************** +** These are the text names for the various armor types a unit may possess. +*/ +char const * const ArmorName[ARMOR_COUNT] = { + "none", + "wood", + "light", + "heavy", + "concrete" +}; + + +// HACK ALERT! This unused text string is here to stop Watcom from crashing. There is some +// magic text heap length that causes a crash before the code executes. This dummy string +// changes the text heap length enough to stop the crash. Who knows why, but it works. +char * __test__ = "alskdfjlasdfjkajsdfkja;sldjfklasj9awutreqjfnfdkvnldzlknvadsjgflkasdjfkajsdfas"; + + +/*************************************************************************** +** The list of VQ filenames. +*/ +char const * const VQName[VQ_COUNT] = { + "AAGUN", + "MIG", + "SFROZEN", + "AIRFIELD", + "BATTLE", + "BMAP", + "BOMBRUN", + "DPTHCHRG", + "GRVESTNE", + "MONTPASS", + "MTNKFACT", + "CRONTEST", + "OILDRUM", + "ALLIEND", + "RADRRAID", + "SHIPYARD", // MISSING + "SHORBOMB", + "SITDUCK", + "SLNTSRVC", + "SNOWBASE", + "EXECUTE", + "REDINTRO", // low res. + "NUKESTOK", + "V2ROCKET", + "SEARCH", + "BINOC", + "ELEVATOR", + "FROZEN", + "MCV", + "SHIPSINK", + "SOVMCV", + "TRINITY", + "ALLYMORF", + "APCESCPE", + "BRDGTILT", + "CRONFAIL", + "STRAFE", + "DESTROYR", + "DOUBLE", + "FLARE", + "SNSTRAFE", + "LANDING", + "ONTHPRWL", + "OVERRUN", + "SNOWBOMB", + "SOVCEMET", + "TAKE_OFF", + "TESLA", + "SOVIET8", + "SPOTTER", + "ALLY1", + "ALLY2", + "ALLY4", + "SOVFINAL", + "ASSESS", + "SOVIET10", + "DUD", + "MCV_LAND", + "MCVBRDGE", + "PERISCOP", + "SHORBOM1", + "SHORBOM2", + "SOVBATL", + "SOVTSTAR", + "AFTRMATH", + "SOVIET11", + "MASASSLT", + "ENGLISH", // High res. Intro + "SOVIET1", + "SOVIET2", + "SOVIET3", + "SOVIET4", + "SOVIET5", + "SOVIET6", + "SOVIET7", + "PROLOG", + "AVERTED", + "COUNTDWN", + "MOVINGIN", + "ALLY10", + "ALLY12", + "ALLY5", + "ALLY6", + "ALLY8", + "TANYA1", + "TANYA2", + "ALLY10B", + "ALLY11", + "ALLY14", + "ALLY9", + "SPY", + "TOOFAR", + "SOVIET12", + "SOVIET13", + "SOVIET9", + "BEACHEAD", + "SOVIET14", + "SIZZLE", //MISSING + "SIZZLE2", //MISSING + "ANTEND", + "ANTINTRO", + + + //2019/11/12 JAS - Added for Retaliation movies + + "RETALIATION_ALLIED1", + "RETALIATION_ALLIED2", + "RETALIATION_ALLIED3", + "RETALIATION_ALLIED4", + "RETALIATION_ALLIED5", + "RETALIATION_ALLIED6", + "RETALIATION_ALLIED7", + "RETALIATION_ALLIED8", + "RETALIATION_ALLIED9", + "RETALIATION_ALLIED10", + + "RETALIATION_SOVIET1", + "RETALIATION_SOVIET2", + "RETALIATION_SOVIET3", + "RETALIATION_SOVIET4", + "RETALIATION_SOVIET5", + "RETALIATION_SOVIET6", + "RETALIATION_SOVIET7", + "RETALIATION_SOVIET8", + "RETALIATION_SOVIET9", + "RETALIATION_SOVIET10", + "RETALIATION_WINA", + "RETALIATION_WINS", + "RETALIATION_ANTS" +}; + + +/*************************************************************************** +** Relative coordinate offsets from the center of a cell for each +** of the legal positions that an object in a cell may stop at. Only infantry +** are allowed to stop at other than the center of the cell. +*/ +COORDINATE const StoppingCoordAbs[5] = { + 0x00800080L, // center + 0x00400040L, // upper left + 0x004000C0L, // upper right + 0x00C00040L, // lower left + 0x00C000C0L // lower right +}; + + +/*************************************************************************** +** Converts pixel values (cell relative) into the appropriate lepton (sub cell) +** value. This is used to convert pixel (screen) coordinates into the underlying +** coordinate system. +*/ +unsigned char const Pixel2Lepton[24] = { + 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B, + 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0, + 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5 +}; + + +/*************************************************************************** +** This array is used to index a facing in order to retrieve a cell +** offset that, when added to another cell, will achieve the adjacent cell +** in the indexed direction. +*/ +CELL const AdjacentCell[FACING_COUNT] = { + -(MAP_CELL_W), // North + -(MAP_CELL_W-1), // North East + 1, // East + MAP_CELL_W+1, // South East + MAP_CELL_W, // South + MAP_CELL_W-1, // South West + -1, // West + -(MAP_CELL_W+1) // North West +}; + +COORDINATE const AdjacentCoord[FACING_COUNT] = { + 0xFF000000L, + 0xFF000100L, + 0x00000100L, + 0x01000100L, + 0x01000000L, + 0x0100FF00L, + 0x0000FF00L, + 0xFF00FF00L +}; + + +/*************************************************************************** +** This specifies the odds of receiving the various random crate power +** ups. The odds are expressed as "shares" of 100 percent. +*/ +int CrateShares[CRATE_COUNT] = { + 50, // CRATE_MONEY + 20, // CRATE_UNIT + 3, // CRATE_PARA_BOMB + 1, // CRATE_HEAL_BASE + 3, // CRATE_CLOAK + 5, // CRATE_EXPLOSION + 5, // CRATE_NAPALM + 20, // CRATE_SQUAD + 1, // CRATE_DARKNESS + 1, // CRATE_REVEAL + 3, // CRATE_SONAR + 10, // CRATE_ARMOR + 10, // CRATE_SPEED + 10, // CRATE_FIREPOWER + 1, // CRATE_ICBM + 1, // CRATE_TIMEQUAKE + 3, // CRATE_INVULN + 5 // CRATE_VORTEX +}; + +AnimType CrateAnims[CRATE_COUNT] = { + ANIM_NONE, // CRATE_MONEY + ANIM_NONE, // CRATE_UNIT + ANIM_NONE, // CRATE_PARA_BOMB + ANIM_NONE, // CRATE_HEAL_BASE + ANIM_NONE, // CRATE_CLOAK + ANIM_NONE, // CRATE_EXPLOSION + ANIM_NONE, // CRATE_NAPALM + ANIM_NONE, // CRATE_SQUAD + ANIM_NONE, // CRATE_DARKNESS + ANIM_NONE, // CRATE_REVEAL + ANIM_NONE, // CRATE_SONAR + ANIM_NONE, // CRATE_ARMOR + ANIM_NONE, // CRATE_SPEED + ANIM_NONE, // CRATE_FIREPOWER + ANIM_NONE, // CRATE_ICBM + ANIM_NONE, // CRATE_TIMEQUAKE + ANIM_NONE, // CRATE_INVULN + ANIM_NONE // CRATE_VORTEX +}; + +int CrateData[CRATE_COUNT] = { + 0, // CRATE_MONEY + 0, // CRATE_UNIT + 0, // CRATE_PARA_BOMB + 0, // CRATE_HEAL_BASE + 0, // CRATE_CLOAK + 0, // CRATE_EXPLOSION + 0, // CRATE_NAPALM + 0, // CRATE_SQUAD + 0, // CRATE_DARKNESS + 0, // CRATE_REVEAL + 0, // CRATE_SONAR + 0, // CRATE_ARMOR + 0, // CRATE_SPEED + 0, // CRATE_FIREPOWER + 0, // CRATE_ICBM + 0, // CRATE_TIMEQUAKE + 0, // CRATE_INVULN + 0 // CRATE_VORTEX +}; + +char const * const CrateNames[CRATE_COUNT] = { + "Money", + "Unit", + "ParaBomb", + "HealBase", + "Cloak", + "Explosion", + "Napalm", + "Squad", + "Darkness", + "Reveal", + "Sonar", + "Armor", + "Speed", + "Firepower", + "ICBM", + "TimeQuake", + "Invulnerability", + "ChronalVortex" +}; + + +/*************************************************************************** +** This converts 0..255 facing values into either 8, 16, or 32 facing values. +** Note: a simple shift won't suffice because 0..255 facing values should +** be converted to the CLOSEST appropriate facing, NOT rounded down to the +** nearest facing. +*/ +unsigned char const Facing8[256] = { + 0,0,0,0,0,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,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +unsigned char const Facing16[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0 +}; + + +signed char const Rotation16[256] = { + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1 +}; + + +/* +** This table incorporates a compensating factor for the distortion caused +** by 3D-Studio when it tries to render 45% angles. +*/ +unsigned char const Facing32[256] = { + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8, + 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19, + 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24, + 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0 +}; + + +#ifdef OBSOLETE +unsigned char const Facing32[256] = { + 0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8, + 9,9,9,9,9,9,9,9, + 10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11, + 12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13, + 14,14,14,14,14,14,14,14, + 15,15,15,15,15,15,15,15, + 16,16,16,16,16,16,16,16, + 17,17,17,17,17,17,17,17, + 18,18,18,18,18,18,18,18, + 19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20, + 21,21,21,21,21,21,21,21, + 22,22,22,22,22,22,22,22, + 23,23,23,23,23,23,23,23, + 24,24,24,24,24,24,24,24, + 25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26, + 27,27,27,27,27,27,27,27, + 28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29, + 30,30,30,30,30,30,30,30, + 31,31,31,31,31,31,31,31, + 0,0,0,0 +}; +#endif + + +/*************************************************************************** +** These are the movement costs (in ticks at fastest speed) to enter each +** of the given terrain cells. +*/ + +int const GroundColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +#ifdef NEVER +int const GroundColor[LAND_COUNT] = { + 46, // "Clear" terrain. + 44, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + 64, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + WHITE, // "Clear" terrain. + LTGRAY, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + LTGRAY, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; +#endif + +GroundType Ground[LAND_COUNT]; + + +/*************************************************************************** +** These are the names of the theaters. +*/ +TheaterDataType const Theaters[THEATER_COUNT] = { + {"TEMPERATE","TEMPERAT","TEM"}, + {"SNOW","SNOW","SNO"}, + {"INTERIOR","INTERIOR","INT"}, +}; + + +unsigned char const RemapCiv2[256] = { + 0,1,2,3,4,5,6,209,8,9,10,11,12,13,12,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,187,188,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,209, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,167, 13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv4[256] = { + 0,1,2,3,4,5,6,187,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,118,110,119, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,188,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv5[256] = { + 0,1,2,3,4,5,6,109,8,9,10,11,131,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,177,110,178, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,111,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv6[256] = { + 0,1,2,3,4,5,6,120,8,9,10,11,12,13,238,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,236,206,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,111, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv7[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,157,212,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,7, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,118,119,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv8[256] = { + 0,1,2,3,4,5,6,182,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,215,7,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,182, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,198,199,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv9[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,7,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,163,165,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,200, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,111,13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv10[256] = { + 0,1,2,3,4,5,6,137,8,9,10,11,12,13,15,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,129,131,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,137, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,163,165,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapEmber[256] = { +#define CEC CC_EMBER_COLOR + 0,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,BLACK,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC +}; + + +//char const Keys[] = +// "[PublicKey]\n" +// "1=AgkCbXo9sKMHOBk=\n" +//#ifdef CHEAT_KEYS +// "[PrivateKey]\n" +// "1=AggxFU55vc7LYQ==\n" +//#endif +// "\n"; + +char const Keys[] = +"[PublicKey]\n" +"1=AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V\n" +#ifdef CHEAT_KEYS +"[PrivateKey]\n" +"1=AigKVje8mROcR8QixnxUEF5b29Curkq01DNDWCdOG99XBqH79OaCiTCB\n" +#endif +"\n"; diff --git a/REDALERT/CONTROL.CPP b/REDALERT/CONTROL.CPP new file mode 100644 index 000000000..1279eb0da --- /dev/null +++ b/REDALERT/CONTROL.CPP @@ -0,0 +1,223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONTROL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : December 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ControlClass::Action -- Normal action for control gadget objects. * + * ControlClass::ControlClass -- Constructor for control class objects. * + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Constructor for control class objects. * + * * + * This is the normal constructor for control class objects. At this level, it only needs * + * to record the ID number assigned to this button. * + * * + * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then * + * this tells the system that no special ID code should be returned. * + * * + * x,y -- Pixel coordinate of upper left corner of gadget's region. * + * * + * w,h -- Pixel dimensions of the gadget's region. * + * * + * flags -- The input event flags that this gadget recognizes. * + * * + * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the * + * gadget list while the mouse button is held down, if the mouse button was * + * initially clicked over its region. This is the behavior of "normal" * + * buttons in Windows. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) : + GadgetClass(x, y, w, h, flags, sticky), + ID(id), + Peer(0) +{ +} + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * * + * This copy constructor for a control gadget is used create a duplicate gadget that * + * is functionally similar. * + * * + * INPUT: control -- Reference to the gadget that is to be copied. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/05/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(ControlClass const & control) : + GadgetClass(control), + ID(control.ID), + Peer(control.Peer) +{ +} + +/*********************************************************************************************** + * ControlClass::Action -- Normal action for control gadget objects. * + * * + * This function gets called when the input event that this control gadget is looking for * + * occurs. In such a case, the return key code value is changed to the gadget's ID number * + * with the special button bit flag attached. * + * * + * INPUT: flags -- The event that triggered this function call. If this value is NULL, then * + * this is a forced (probably due to the sticky flag) call and the key code * + * is not altered. * + * * + * key -- Reference to the key code that will be returned by the controlling * + * Input() function. * + * * + * OUTPUT: bool; Should further list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Action(unsigned flags, KeyNumType & key) +{ + + /* + ** Only if the flags indicate that a recognized action has occurred, do the + ** normal processing of this gadget and set return value to the gadget ID. + */ + if (flags) { + if (ID) { + key = (KeyNumType)(ID | KN_BUTTON); + } else { + key = KN_NONE; + } + } + + /* + ** If there is a peer link established, inform that gadget of this + ** action call. + */ + if (Peer) { + Peer->Peer_To_Peer(flags, key, *this); + } + + return(GadgetClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * * + * This function will assign another gadget to this one. That other gadget will receive * + * notification of any Action() call to this gadget. Presumably, this is how one gadget * + * can automatically adapt to changes in another. Say for example, a slider bar can affect * + * the list box it is attached to. * + * * + * INPUT: gadget -- The gadget to inform when any Action() function is called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ControlClass::Make_Peer(GadgetClass & gadget) +{ + Peer = &gadget; +} + + +/*********************************************************************************************** + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * * + * This function will query and return with the ID number for this gadget. It is primarily * + * used by the Extract_Gadget() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that * + * no ID was assigned to this gadget. This is a special case since a zero value will * + * never be returned as a pseudo-key as is done with non-zero values. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +unsigned ControlClass::Get_ID(void) const +{ + return(ID); +} + + +/*********************************************************************************************** + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * * + * This is called when the control object might need to be redrawn or when redrawing is * + * necessary. Since at this level of the class hierarchy, no actual drawing occurs, this * + * routine doesn't perform any rendering. It does, however, inform any peer attached * + * object that a Draw_Me function has been called. Presumably, the attached peer gadget * + * might very well need to be redrawn as a result of some action by this gadget. Since this * + * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me * + * for the other gadget will not occur. It must rely on the call by this routine in order * + * to update correctly. A typical example of this would be a slider that is attached to * + * a list box. As the slider is being drug around, the attached list box must be redrawn. * + * * + * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the gadget redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Draw_Me(int forced) +{ + if (Peer) { + Peer->Draw_Me(); + } + return(GadgetClass::Draw_Me(forced)); +} diff --git a/REDALERT/CONTROL.H b/REDALERT/CONTROL.H new file mode 100644 index 000000000..ac48b370e --- /dev/null +++ b/REDALERT/CONTROL.H @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONTROL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "gadget.h" + +/*************************************************************************** + * ControlClass -- Region tracking class * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: 0 = new scenario created, -1 = not * + * WARNINGS: This class is Abstract (cannot make an instance of it) * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=========================================================================*/ +class ControlClass : public GadgetClass +{ + public: + ControlClass(NoInitClass const & x) : GadgetClass(x) {}; + ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); + ControlClass(ControlClass const & control); + + virtual void Make_Peer(GadgetClass & gadget); + + /* + ** Render support function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the ID number for this control gadget. This number is used to generate + ** a special pseudo-key when the gadget detects valid input. + */ + unsigned ID; + + protected: + virtual unsigned Get_ID(void) const; + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This points to the peer button to inform when something happens to this + ** gadget. + */ + GadgetClass * Peer; +}; + +#endif + diff --git a/REDALERT/COORD.CPP b/REDALERT/COORD.CPP new file mode 100644 index 000000000..cbeb4bf63 --- /dev/null +++ b/REDALERT/COORD.CPP @@ -0,0 +1,704 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COORD.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COORD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + * Support code to handle the coordinate system is located in this module. * + * Routines here will be called QUITE frequently during play and must be * + * as efficient as possible. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * Coord_Cell -- Convert a coordinate into a cell number. * + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * Distance -- Determines the cell distance between two cells. * + * Distance -- Determines the lepton distance between two coordinates. * + * Distance -- Fetch distance between two target values. * + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * Normal_Move_Point -- Moves point with tilt compensation. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Coord_Cell -- Convert a coordinate into a cell number. * + * * + * This routine will convert the specified coordinate value into a cell number. This is * + * useful to determine the map index number into the cell array that corresponds to a * + * particular coordinate. * + * * + * INPUT: coord -- The coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that corresponds to the coordinate specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +CELL Coord_Cell(COORDINATE coord) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell; + cell.Sub.Y = ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell; + return(cell.Cell); +// return(XY_Cell(((COORD_COMPOSITE)coord).Sub.X, ((COORD_COMPOSITE)composite).Sub.Y)); +} + + +/*********************************************************************************************** + * Distance -- Fetch distance between two target values. * + * * + * This routine will determine the lepton distance between the two specified target * + * values. * + * * + * INPUT: target1 -- First target value. * + * * + * target2 -- Second target value. * + * * + * OUTPUT: Returns with the lepton distance between the two target values. * + * * + * WARNINGS: Be sure that the targets are legal before calling this routine. Otherwise, the * + * return value is meaningless. * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +int Distance(TARGET target1, TARGET target2) +{ + return(Distance(As_Coord(target1), As_Coord(target2))); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance(COORDINATE coord1, COORDINATE coord2) +{ + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * * + * This routine will take an arbitrary position and object size and return with a list of * + * cell offsets from the current cell for all cells that are overlapped by the object. The * + * first cell offset is always zero, so to just get the adjacent spill cell list, add one * + * to the return pointer. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * maxsize -- The maximum width/height of the object (pixels). * + * * + * OUTPUT: Returns with a pointer to a spillage list. * + * * + * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values * + * will generate an incomplete overlap list. * + * * + * HISTORY: * + * 11/06/1993 JLB : Created. * + * 03/25/1994 JLB : Added width optimization. * + * 04/29/1994 JLB : Converted to C. * + * 06/03/1994 JLB : Converted to general purpose spillage functionality. * + * 01/07/1995 JLB : Manually calculates spillage list for large objects. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, int maxsize) +{ + static short const _MoveSpillage[(int)FACING_COUNT+1][5] = { + {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N + {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE + {0, 1, REFRESH_EOL, 0, 0}, // E + {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE + {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S + {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW + {0, -1, REFRESH_EOL, 0, 0}, // W + {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW + {0, REFRESH_EOL, 0, 0, 0} // non-moving. + }; + static short _manual[10]; +//; 00 = on axis +//; 01 = below axis +//; 10 = above axis +//; 11 = undefined + static signed char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1}; + int index=0; + int x,y; + + /* + ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table + ** that covers a 5x5 square region. + */ + if (maxsize > ICON_PIXEL_W * 2) { + static short const _gigundo[] = { + -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2), + -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2), + -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2), + ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2), + +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2), + REFRESH_EOL + }; + return(&_gigundo[0]); + } + + /* + ** For very large objects, build the overlap list by hand. This is time consuming, but + ** not nearly as time consuming as drawing even a single cell unnecessarily. + */ + if (maxsize > ICON_PIXEL_W) { + maxsize = min(maxsize, (ICON_PIXEL_W*2))/2; + + x = (ICON_PIXEL_W * Coord_XLepton(coord)) / ICON_LEPTON_W; + y = (ICON_PIXEL_H * Coord_YLepton(coord)) / ICON_LEPTON_H; + int left = x-maxsize; + int right = x+maxsize; + int top = y-maxsize; + int bottom = y+maxsize; + + _manual[index++] = 0; + if (left < 0) _manual[index++] = -1; + if (right >= ICON_PIXEL_W) _manual[index++] = 1; + if (top < 0) _manual[index++] = -MAP_CELL_W; + if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W; + if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1); + if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1; + if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1; + if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1); + _manual[index] = REFRESH_EOL; + return(&_manual[0]); + } + + /* + ** Determine the number of leptons "leeway" allowed this unit. + */ + int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2]; + + x = Coord_XLepton(coord) - 0x0080; + y = Coord_YLepton(coord) - 0x0080; + if (y > posval) index |= 0x08; // Spilling South. + if (y < -posval) index |= 0x04; // Spilling North. + if (x > posval) index |= 0x02; // Spilling East. + if (x < -posval) index |= 0x01; // Spilling West. + + return(&_MoveSpillage[_SpillTable[index]][0]); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * * + * Given a center coordinate and a dirty rectangle, calcuate a cell offset list for * + * determining such things as overlap and redraw logic. Optionally, the center cell * + * location will not be part of the list. * + * * + * INPUT: coord -- The center coordinate that the dirty rectangle is based off of. * + * * + * rect -- Reference to the dirty rectangle. * + * * + * nocenter -- If true, then the center cell offset will not be part of the spillage * + * list returned. This is handy when the center cell is known to be * + * processed by some other method and it can be safely and efficiently * + * ignored by the list generated. * + * * + * OUTPUT: Returns with a pointer to the spillage list that corresponds to the data * + * specified. This is a pointer to a static buffer and as such it will only be valid * + * until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter) +{ + if (!rect.Is_Valid()) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + CELL coordcell = Coord_Cell(coord); + LEPTON x = Coord_X(coord); + LEPTON y = Coord_Y(coord); + + /* + ** Add the rectangle values to the coordinate in order to normalize the start and end + ** corners of the rectangle. The values are now absolute to the real game world rather + ** than relative to the coordinate. + */ + LEPTON_COMPOSITE startx; + LEPTON_COMPOSITE starty; + LEPTON_COMPOSITE endx; + LEPTON_COMPOSITE endy; + startx.Raw = (int)x + (short)Pixel_To_Lepton(rect.X); + starty.Raw = (int)y + (short)Pixel_To_Lepton(rect.Y); + endx.Raw = startx.Raw + Pixel_To_Lepton(rect.Width-1); + endy.Raw = starty.Raw + Pixel_To_Lepton(rect.Height-1); + + /* + ** Determine the upper left and lower right cell indexes. This is a simple conversion from + ** their lepton counterpart. These cells values are used to form the bounding box for the + ** map offset list. + */ + int cellx = startx.Sub.Cell; + int cellx2 = endx.Sub.Cell; + int celly = starty.Sub.Cell; + int celly2 = endy.Sub.Cell; + + /* + ** Generate the spillage list by counting off the rows and colums of the cells + ** that are affected. This is easy since the upper left and lower right corner cells + ** are known. + */ + int count = 0; + static short _spillagelist[128]; + short * ptr = _spillagelist; + for (int yy = celly; yy <= celly2; yy++) { + for (int xx = cellx; xx <= cellx2; xx++) { + short offset = (XY_Cell(xx, yy) - coordcell); + if (!nocenter || offset != 0) { + *ptr++ = offset; + count++; + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + } + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + + /* + ** Cap the list with the end of list marker and then return a pointer + ** to the completed list. + */ + *ptr = REFRESH_EOL; + return(_spillagelist); +} + + +/*********************************************************************************************** + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * * + * This function will move a coordinate in a using SIN and COS arithmetic. * + * * + * INPUT: start -- The starting coordinate. * + * * + * dir -- The direction to move the coordinate. * + * * + * distance -- The distance to move the coordinate position (in leptons). * + * * + * OUTPUT: Returns the new coordinate position. * + * * + * WARNINGS: This routine uses multiplies -- use with caution. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance) +{ +#ifdef NEVER + short x = Coord_X(start); + short y = Coord_Y(start); + + Move_Point(x, y, dir, distance); + return(XY_Coord(x,y)); +#endif + + Move_Point(*(short *)&start, *(((short *)&start)+1), dir, distance); + return(start); +} + + +/*********************************************************************************************** + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * * + * This routine will perform a scatter algorithm on the specified * + * anchor point in order to return with another coordinate that is * + * randomly nearby the original. Typical use of this would be for * + * missile targeting. * + * * + * INPUT: coord -- This is the anchor coordinate. * + * * + * distance -- This is the distance in pixels that the scatter * + * should fall within. * + * * + * lock -- bool; Convert the new coordinate into a center * + * cell based coordinate? * + * * + * OUTPUT: Returns with a new coordinate that is nearby the original. * + * * + * WARNINGS: Maximum pixel scatter distance is 255. * + * * + * HISTORY: * + * 02/01/1992 JLB : Created. * + * 05/13/1992 JLB : Only uses Random(). * + *=============================================================================================*/ +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock) +{ + COORDINATE newcoord; + + newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance); + + if (newcoord & HIGH_COORD_MASK) newcoord = coord; + + if (lock) { + newcoord = Coord_Snap(newcoord); + } + + return(newcoord); +} + + +int __cdecl calcx(signed short param1, short distance) +{ + __asm { + + //#pragma aux calcx parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax, 1 + rcl dx, 1 + mov al, ah + mov ah, dl + cwd + } +} + + +int __cdecl calcy(signed short param1, short distance) +{ + __asm { + + //#pragma aux calcy parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax, 1 + rcl dx, 1 + mov al, ah + mov ah, dl + cwd + neg eax + } +} + + +#if (0) +extern int calcx(signed short, short distance); +#pragma aux calcx parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ +// "and eax,0FFFFh"; + +extern int calcy(signed short, short distance); +#pragma aux calcy parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ + "neg eax"; +// "and eax,0FFFFh" \ + +#endif + +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ + static unsigned char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static unsigned char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + +#ifdef OBSOLETE + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + x += _DX; +#else + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + static const char *_cos_table = (char*)&CosTable[0]; + x += calcx(_cos_table[dir], distance); +#endif +// asm add [word ptr start],ax + +#ifdef OBSOLETE + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + y += _DX; +#else + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + static const char *_sin_table = (char*)&SinTable[0]; + y += calcy(_sin_table[dir], distance); +#endif +// asm add [word ptr start+2],ax + +} + + +/*********************************************************************************************** + * Normal_Move_Point -- Moves point with tilt compensation. * + * * + * This routine will move the point in the direction and distance specified but it will * + * take into account the tilt of the playing field. Typical use of this routine is to * + * determine positioning as it relates to the playfield. Turrets are a good example of * + * this. * + * * + * INPUT: x,y -- References to the coordinates to adjust. * + * * + * dir -- The direction of the desired movement. * + * * + * distance -- The distance (in coordinate units) to move the point. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1995 JLB : Created. * + *=============================================================================================*/ +// Loss of precision in initializations (8 bits to 7 bits) warning. Hmmm.. can this be fixed? +//lint -e569 +void Normal_Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ + static unsigned char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static unsigned char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + static const char *_sin_table = (char*)&SinTable[0]; + static const char *_cos_table = (char*)&CosTable[0]; + + x += calcx(_cos_table[dir], distance); + + y += calcy(_sin_table[dir] / 2, distance); +} diff --git a/REDALERT/COORDA.ASM b/REDALERT/COORDA.ASM new file mode 100644 index 000000000..d9510b73e --- /dev/null +++ b/REDALERT/COORDA.ASM @@ -0,0 +1,126 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C Cardinal_To_Fixed :NEAR +global C Fixed_To_Cardinal :NEAR + + CODESEG + +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/65536 as the lowest and up to * +;* 65536 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Cardinal_To_Fixed(unsigned base, unsigned cardinal); + + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFFFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 4294967295 + + mov eax,[cardinal] ; otherwise, return (cardinal*65536)/base + shl eax,16 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed + + +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Fixed_To_Cardinal(unsigned base, unsigned fixed); + PROC Fixed_To_Cardinal C near + USES edx + + ARG base:DWORD + ARG fixed:DWORD + + mov eax,[base] + mul [fixed] + add eax,08000h ; eax = (base * fixed) + 0x8000 + + shr eax,16 ; return eax/65536 + ret + + ENDP Fixed_To_Cardinal + + END diff --git a/REDALERT/COORDA.h b/REDALERT/COORDA.h new file mode 100644 index 000000000..912c662ed --- /dev/null +++ b/REDALERT/COORDA.h @@ -0,0 +1,141 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + +#ifndef COORD_A_H + +//IDEAL +//P386 +//MODEL USE32 FLAT + +//global C Cardinal_To_Fixed :NEAR +//global C Fixed_To_Cardinal :NEAR + +// CODESEG +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal); + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed); + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + + + + +#endif COORD_A_H \ No newline at end of file diff --git a/REDALERT/CPUID.ASM b/REDALERT/CPUID.ASM new file mode 100644 index 000000000..fabcf6c6d --- /dev/null +++ b/REDALERT/CPUID.ASM @@ -0,0 +1,182 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: F:\projects\c&c0\vcs\code\cpuid.asv 5.0 11 Nov 1996 09:40:28 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : MMX.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : May 19th, 1996 * +;* * +;* Last Update : May 19th 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + .586 + .model flat + +; +; Variables externs +; +GLOBAL C CPUType:byte +;externdef C CPUType:byte +GLOBAL C VendorID:byte +;externdef C VendorID:byte + +; +; Function externs +; +GLOBAL C Detect_MMX_Availability:near +;externdef C Detect_MMX_Availability:near + + + .code + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local cputype:byte + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [cputype],al + +@@end_get_cpu: mov al,[cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + +end + diff --git a/REDALERT/CRATE.CPP b/REDALERT/CRATE.CPP new file mode 100644 index 000000000..874f1db32 --- /dev/null +++ b/REDALERT/CRATE.CPP @@ -0,0 +1,182 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRATE.CPP 3 3/04/97 3:12p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * * + * This routine will remove the crate from whereever it happens to be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the crate found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Remove_It(void) +{ + if (Is_Valid()) { + Get_Crate(Cell); + Make_Invalid(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * * + * This will create a crate in the cell specified. If the crate could not be crated there * + * then 'false' will be returned. * + * * + * INPUT: cell -- The desired cell to place the crate in. * + * * + * OUTPUT: bool; Was the crate created and placed in the cell? * + * * + * WARNINGS: It is quite possible for the crate not to have been placed. Only the most clear * + * locations are valid for crate placement. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Create_Crate(CELL cell) +{ + /* + ** Remove any existing crate that this crate class is tracking. + */ + Remove_It(); + + /* + ** Try to place a new crate at the cell specified. + */ + if (Put_Crate(cell)) { + Cell = cell; + Timer = Random_Pick(Rule.CrateTime * (TICKS_PER_MINUTE/2), Rule.CrateTime * (TICKS_PER_MINUTE*2)); + Timer.Start(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * * + * This helpter routine will examine the cell and place the appropriate crate type into * + * the cell specified. If the overlay could not be generated, then 'false' is returned. * + * * + * INPUT: cell -- The cell to generate the crate overlay in. * + * * + * OUTPUT: bool; Was the crate overlay generated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + * 10/14/1996 JLB : Takes reference to cell so that tracking can occur. * + *=============================================================================================*/ +bool CrateClass::Put_Crate(CELL & cell) +{ + int old = ScenarioInit; + ScenarioInit = 0; + + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + while (cellptr->Overlay != OVERLAY_NONE && !cellptr->Is_Clear_To_Build(SPEED_FLOAT) && !cellptr->Is_Clear_To_Build(SPEED_FOOT)) { + cell = Map.Pick_Random_Location(); + + if (Percent_Chance(100 * Rule.WaterCrateChance)) { + cell = Map.Nearby_Location(cell, SPEED_FLOAT); + } else { + cell = Map.Nearby_Location(cell, SPEED_TRACK); + } + cellptr = &Map[cell]; + } + + if (cellptr->Is_Clear_To_Build(SPEED_FLOAT)) { + new OverlayClass(OVERLAY_WATER_CRATE, cell); + } else { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + ScenarioInit = old; + return(true); + } + + ScenarioInit = old; + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * * + * This will remove the crate from the cell specified. * + * * + * INPUT: cell -- The cell to examine and remove any crate overlays present. * + * * + * OUTPUT: bool; Was a crate overlay found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Get_Crate(CELL cell) +{ + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + if (cellptr->Overlay == OVERLAY_WOOD_CRATE || + cellptr->Overlay == OVERLAY_STEEL_CRATE || + cellptr->Overlay == OVERLAY_WATER_CRATE) { + + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + return(true); + } + } + return(false); +} diff --git a/REDALERT/CRATE.H b/REDALERT/CRATE.H new file mode 100644 index 000000000..29727ac9e --- /dev/null +++ b/REDALERT/CRATE.H @@ -0,0 +1,76 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRATE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : August 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CRATE_H +#define CRATE_H + +#include "ftimer.h" +#include "jshell.h" + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +class CrateClass { + public: + CrateClass(void) : Timer(NoInitClass()), Cell(-1) {} + void Init(void) {Make_Invalid();} + bool Create_Crate(CELL cell); + bool Is_Here(CELL cell) const {return(Is_Valid() && cell == Cell);} + bool Remove_It(void); + bool Is_Expired(void) const {return(Is_Valid() && Timer == 0);} + bool Is_Valid(void) const {return(Cell != -1);} + + private: + static bool Put_Crate(CELL & cell); + static bool Get_Crate(CELL cell); + + void Make_Invalid(void) {Cell = -1;Timer.Stop();} + + CDTimerClass Timer; + CELL Cell; +}; + + +#endif diff --git a/REDALERT/CRC.CPP b/REDALERT/CRC.CPP new file mode 100644 index 000000000..395d95e9c --- /dev/null +++ b/REDALERT/CRC.CPP @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "crc.h" + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * * + * This routine will take the specified byte of data and submit it to the CRC engine * + * for processing. This routine is designed to be as fast as possible since the typical * + * use of this routine is to feed one of presumably many byte sized chunks of data to the * + * CRC engine. * + * * + * INPUT: datum -- One byte of data to submit to the CRC engine. * + * * + * OUTPUT: none * + * * + * WARNINGS: If possible, use the buffer/size operator to submit data rather than repeated * + * calls to this routine. * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +void CRCEngine::operator() (char datum) +{ + StagingBuffer.Buffer[Index++] = datum; + + if (Index == sizeof(long)) { + CRC = Value(); + StagingBuffer.Composite = 0; + Index = 0; + } +} + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * * + * This routine will submit the specified block to the CRC engine. The block can be of * + * arbitrary length. * + * * + * INPUT: buffer -- Pointer to the buffer that contains the data. The buffer will not * + * be modified. * + * * + * length -- The length of the buffer (in bytes). * + * * + * OUTPUT: Returns with the current CRC value accumulated so far. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +long CRCEngine::operator() (void const * buffer, int length) +{ + if (buffer != NULL && length > 0) { + char const * dataptr = (char const *)buffer; + int bytes_left = length; + + /* + ** If there are any leader bytes (needed to fill the staging buffer) + ** then process those by first using them to fill up the staging + ** buffer. The bulk of the data block will be processed by the high + ** speed longword processing loop. + */ + while (bytes_left && Buffer_Needs_Data()) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + + /* + ** Perform the fast 'bulk' processing by reading long word sized + ** data blocks. + */ + long const * longptr = (long const *)dataptr; + int longcount = bytes_left / sizeof(long); // Whole 'long' elements remaining. + while (longcount--) { + CRC = _lrotl(CRC, 1) + *longptr++; + bytes_left -= sizeof(long); + } + + /* + ** If there are remainder bytes, then process these by adding them + ** to the staging buffer. + */ + dataptr = (char const *)longptr; + while (bytes_left) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + } + + /* + ** Return the current CRC value. + */ + return(Value()); +} diff --git a/REDALERT/CRC.H b/REDALERT/CRC.H new file mode 100644 index 000000000..df2496485 --- /dev/null +++ b/REDALERT/CRC.H @@ -0,0 +1,118 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRC.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRC_H +#define CRC_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a CRC engine class. It will process submitted data and generate a CRC from it. +** Well, actually, the value returned is not a true CRC. However, it shares the same strength +** characteristic and is faster to generate than the traditional CRC. This object is treated like +** a method class. If it is called as a function (using the function operator), it will return +** the CRC value. There are other function operators to submit data for processing. +*/ +class CRCEngine { + public: + + // Constructor for CRC engine (it can have an override initial CRC value). + CRCEngine(long initial=0) : CRC(initial), Index(0) { + StagingBuffer.Composite = 0; + }; + + // Fetches CRC value. + long operator() (void) const {return(Value());}; + + // Submits one byte sized datum to the CRC accumulator. + void operator() (char datum); + + // Submits an arbitrary buffer to the CRC accumulator. + long operator() (void const * buffer, int length); + + // Implicit conversion operator so this object appears like a 'long integer'. + operator long(void) const {return(Value());}; + + protected: + + bool Buffer_Needs_Data(void) const { + return(Index != 0); + }; + + long Value(void) const { + if (Buffer_Needs_Data()) { + return(_lrotl(CRC, 1) + StagingBuffer.Composite); + } + return(CRC); + }; + + /* + ** Current accumulator of the CRC value. This value doesn't take into + ** consideration any pending data in the staging buffer. + */ + long CRC; + + /* + ** This is the sub index into the staging buffer used to keep track of + ** partial data blocks as they are submitted to the CRC engine. + */ + int Index; + + /* + ** This is the buffer that holds the incoming partial data. When the buffer + ** is filled, the value is transformed into the CRC and the buffer is flushed + ** in preparation for additional data. + */ + union { + long Composite; + char Buffer[sizeof(long)]; + } StagingBuffer; +}; + +#endif + diff --git a/REDALERT/CRCPIPE.CPP b/REDALERT/CRCPIPE.CPP new file mode 100644 index 000000000..5b53e151b --- /dev/null +++ b/REDALERT/CRCPIPE.CPP @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCPipe::Result -- Fetches the current CRC of the data. * + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcpipe.h" + + +/*********************************************************************************************** + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * * + * This routine will fetch the number of bytes requested from the straw. The data is * + * not modified by this straw segment, but it is examined by the CRC engine in order to * + * keep an accurate CRC of the data that passes through this routine. * + * * + * INPUT: source -- Pointer to the buffer that will hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number is * + * less than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCPipe::Put(void const * source, int slen) +{ + CRC(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * CRCPipe::Result -- Fetches the current CRC of the data. * + * * + * This routine will return the CRC of the data that has passed through the pipe up to * + * this time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCPipe::Result(void) const +{ + return(CRC()); +} + diff --git a/REDALERT/CRCPIPE.H b/REDALERT/CRCPIPE.H new file mode 100644 index 000000000..78d5652d9 --- /dev/null +++ b/REDALERT/CRCPIPE.H @@ -0,0 +1,63 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCPIPE_H +#define CRCPIPE_H + +#include "pipe.h" +#include "crc.h" + +/* +** This class doesn't modify the data being piped through, but it does examine it and build +** a CRC value from the data. +*/ +class CRCPipe : public Pipe +{ + public: + CRCPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCPipe(CRCPipe & rvalue); + CRCPipe & operator = (CRCPipe const & pipe); +}; + +#endif diff --git a/REDALERT/CRCSTRAW.CPP b/REDALERT/CRCSTRAW.CPP new file mode 100644 index 000000000..d6420b8e5 --- /dev/null +++ b/REDALERT/CRCSTRAW.CPP @@ -0,0 +1,91 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcstraw.h" + + +/*********************************************************************************************** + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * * + * This routine will fetch the number of bytes requested. The data will not be modified * + * by this straw segment, but the CRC engine will examine the data so as to keep an * + * accurate CRC value. * + * * + * INPUT: source -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored in the buffer. If this number is * + * less than that requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + CRC(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * * + * This routine will return the CRC value of the data that has passed through this straw * + * segment. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value of the data this straw segment has seen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCStraw::Result(void) const +{ + return(CRC()); +} diff --git a/REDALERT/CRCSTRAW.H b/REDALERT/CRCSTRAW.H new file mode 100644 index 000000000..0bb94c985 --- /dev/null +++ b/REDALERT/CRCSTRAW.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCSTRAW_H +#define CRCSTRAW_H + +#include "straw.h" +#include "crc.h" + +/* +** This class will build a CRC value from the data stream that is drawn through this class. +** The data is not modified, but it is examined as it passes through. +*/ +class CRCStraw : public Straw +{ + public: + CRCStraw(void) {} + virtual int Get(void * source, int slen); + + // Calculate and return the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCStraw(CRCStraw & rvalue); + CRCStraw & operator = (CRCStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/CREDITS.CPP b/REDALERT/CREDITS.CPP new file mode 100644 index 000000000..2cebff2fe --- /dev/null +++ b/REDALERT/CREDITS.CPP @@ -0,0 +1,256 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREDITS.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDITS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 17, 1994 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CreditClass::AI -- Handles updating the credit display. * + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * * + * This is the constructor for the credit class object. It merely sets the credit display * + * state to null. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +CreditClass::CreditClass(void) : + Credits(0), + Current(0), + IsToRedraw(false), + IsUp(false), + IsAudible(false), + Countdown(0) +{ +} + + +/*********************************************************************************************** + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * * + * This routine should be called whenever the main game screen is to be updated. It will * + * check to see if the credit display should be redrawn. If so, it will redraw it. * + * * + * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw * + * flag is set? This is typically the case when the screen needs to be * + * redrawn from scratch. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +//#define XX (320-120) +//#define WW 50 +void CreditClass::Graphic_Logic(bool forced) +{ + if (forced || IsToRedraw) { + BStart(BENCH_TABS); + + int xx = SeenBuff.Get_Width() - (120 * RESFACTOR); + + /* + ** Adjust the credits display to be above the sidebar for 640x400 + */ +#ifdef WIN32 + xx += 80 * RESFACTOR; +#endif + + /* + ** Play a sound effect when the money display changes, but only if a sound + ** effect was requested. + */ + if (IsAudible) { + if (IsUp) { + Sound_Effect(VOC_MONEY_UP, fixed(1, 2)); + } else { + Sound_Effect(VOC_MONEY_DOWN, fixed(1, 2)); + } + } + + /* + ** Display the new current value. + */ + // PG TabClass::Draw_Credits_Tab(); +#ifdef WIN32 + //PG Fancy_Text_Print("%ld", xx, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL, Current); +#else + Fancy_Text_Print("%ld", xx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, Current); +#endif //WIN32 + + if (Scen.MissionTimer.Is_Active()) { + long secs = Scen.MissionTimer / TICKS_PER_SECOND; + long mins = secs / 60; + long hours = mins / 60; + secs %= 60; + mins %= 60; +#if (0) //Moved to LOGIC.CPP + /* + ** Speak mission timer reminders. + */ + VoxType vox = VOX_NONE; + if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; + if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; + if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; + if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; + if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; + if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; + if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; + if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; + if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; + if (vox != VOX_NONE) { + Speak(vox); + Map.FlasherTimer = 7; + } +#endif +#ifdef WIN32 +#if (0) //PG + if (hours) { + Fancy_Text_Print(TXT_TIME_FORMAT_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, hours, mins, secs); + } else { + Fancy_Text_Print(TXT_TIME_FORMAT_NO_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, mins, secs); + } +#endif +#else + if (hours) { + Fancy_Text_Print("%02d:%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, hours, mins, secs); + } else { + Fancy_Text_Print("%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, mins, secs); + } +#endif //WIN32 + } + + IsToRedraw = false; + IsAudible = false; + BEnd(BENCH_TABS); + } +} + + +/*********************************************************************************************** + * CreditClass::AI -- Handles updating the credit display. * + * * + * This routine handles the logic that controls the rate of credit change in the credit * + * display. It doesn't actually redraw the credit display, but will flag it to be redrawn * + * if it detects that a change is to occur. * + * * + * INPUT: forced -- Should the credit display immediately reflect the current credit * + * total for the player? This is usually desired when initially loading * + * a scenario or saved game. * + * player_ptr -- Player to calculate visible credits for * + * logic_only -- If true, don't flag map for redraw * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + * 10/16/2019 ST : Added house and logic parameters so we can call this from HouseClass::AI * + *=============================================================================================*/ +void CreditClass::AI(bool forced, HouseClass *player_ptr, bool logic_only) +{ + static int _last = 0; + + if (!forced && !logic_only && Frame == _last) return; + if (!logic_only) { + _last = Frame; + } + + if (player_ptr == NULL) { + return; + } + + Credits = player_ptr->Available_Money(); + + /* + ** Make sure that the credit counter doesn't drop below zero. + */ + Credits = max(Credits, 0L); + + if (Scen.MissionTimer.Is_Active() || Scen.MissionTimer) { + IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (Current == Credits) return; + + if (forced) { + IsAudible = false; + Current = Credits; + } else { + + if (Countdown) Countdown--; + if (Countdown) return; + + /* + ** Determine the amount to change the display toward the + ** desired value. + */ + int adder = Credits - Current; + + if (adder > 0) { + Countdown = 1; + } else { + Countdown = 3; + } + + adder = ABS(adder); + adder >>= 3; +// adder >>= 4; +// adder >>= 5; + adder = Bound(adder, 1, 71+72); + if (Current > Credits) adder = -adder; + Current += adder; + if (Current-adder != Current) { + IsAudible = true; + IsUp = (adder > 0); + } + } + IsToRedraw = true; + + if (!logic_only) { + Map.Flag_To_Redraw(false); + } +} \ No newline at end of file diff --git a/REDALERT/CREDITS.H b/REDALERT/CREDITS.H new file mode 100644 index 000000000..28709d450 --- /dev/null +++ b/REDALERT/CREDITS.H @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREDITS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREDITS_H +#define CREDITS_H + +class HouseClass; +extern HouseClass *PlayerPtr; + +/**************************************************************************** +** The animating credit counter display is controlled by this class. +*/ +class CreditClass { + public: + long Credits; // Value of credits trying to update display to. + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CreditClass(void); + CreditClass(NoInitClass const & ) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Update(bool forced=false, bool redraw=false); + + void Graphic_Logic(bool forced=false); + void AI(bool forced=false, HouseClass *player_ptr = PlayerPtr, bool logic_only = false); // Added house and logic_only parameters. ST - 10/16/2019 2:26PM + + long Current; // Credit value currently displayed. + + unsigned IsToRedraw:1; + unsigned IsUp:1; + unsigned IsAudible:1; + + private: + int Countdown; // Delay between ticks. +}; + +#endif diff --git a/REDALERT/CREW.CPP b/REDALERT/CREW.CPP new file mode 100644 index 000000000..27c309113 --- /dev/null +++ b/REDALERT/CREW.CPP @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/REDALERT/CREW.H b/REDALERT/CREW.H new file mode 100644 index 000000000..ecfcf3912 --- /dev/null +++ b/REDALERT/CREW.H @@ -0,0 +1,67 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREW_H +#define CREW_H + + +/**************************************************************************** +** This class handles the basic crew logic. This includes hero tracking, +** crew bail-out, and attached object logic. +*/ +class CrewClass +{ + public: + /* + ** This keeps track of the number of "kills" the unit as accumulated. + ** When it reaches a certain point, the unit improves. + */ + unsigned short Kills; + + /* + ** Constructors, Destructors, and overloaded operators. + */ + CrewClass(void) : Kills(0) {}; + CrewClass(NoInitClass const &) {}; + ~CrewClass(void) {}; + + int Made_A_Kill(void) { + Kills++; + return(Kills); + }; + + private: +}; + +#endif diff --git a/REDALERT/CSTRAW.CPP b/REDALERT/CSTRAW.CPP new file mode 100644 index 000000000..538e5e6b2 --- /dev/null +++ b/REDALERT/CSTRAW.CPP @@ -0,0 +1,97 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CacheStraw::Get -- Fetch data from the data source. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "cstraw.h" +#include + + +/*********************************************************************************************** + * CacheStraw::Get -- Fetch data from the data source. * + * * + * This will supply the data quantity requested. It performs a regulating influence on the * + * data requests passed through it. The data is requested from the next straw in the * + * chain such that the data stream is requested in chunks. This serves to lessen the * + * impact of multiple small data requests. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * slen -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the number of data bytes stored into the buffer specified. If this * + * number is less than that requested, it indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1996 JLB : Created. * + *=============================================================================================*/ +int CacheStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + + /* + ** Keep processing the data request until there is no more data to supply or the request + ** has been fulfilled. + */ + while (slen > 0) { + + /* + ** First try to fetch the data from data previously loaded into the buffer. + */ + if (Length > 0) { + int tocopy = (Length < slen) ? Length : slen; + memmove(source, ((char *)BufferPtr.Get_Buffer()) + Index, tocopy); + slen -= tocopy; + Index += tocopy; + total += tocopy; + Length -= tocopy; + source = (char*)source + tocopy; + } + if (slen == 0) break; + + /* + ** Since there is more to be fulfilled yet the holding buffer is empty, + ** refill the buffer with a fresh block of data from the source. + */ + Length = Straw::Get(BufferPtr, BufferPtr.Get_Size()); + Index = 0; + if (Length == 0) break; + } + } + return(total); +} diff --git a/REDALERT/CSTRAW.H b/REDALERT/CSTRAW.H new file mode 100644 index 000000000..ac37fbda6 --- /dev/null +++ b/REDALERT/CSTRAW.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CSTRAW_H +#define CSTRAW_H + +#include "straw.h" +#include "buff.h" + +/* +** This class handles transfer of data by perform regulated requests for data from the next +** class in the chain. It performs no translation on the data. By using this segment in a +** straw chain, data throughput can be regulated. This can yield great performance increases +** when dealing with a file source. +*/ +class CacheStraw : public Straw +{ + public: + CacheStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0), Length(0) {} + CacheStraw(int length=4096) : BufferPtr(length), Index(0), Length(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; + int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + CacheStraw(CacheStraw & rvalue); + CacheStraw & operator = (CacheStraw const & pipe); +}; + + + +#endif + diff --git a/REDALERT/DDE.CPP b/REDALERT/DDE.CPP new file mode 100644 index 000000000..bd481485d --- /dev/null +++ b/REDALERT/DDE.CPP @@ -0,0 +1,462 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.CPP * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Instance_Class::InstanceClass -- class constructor * + * Instance_Class::InstanceClass -- class destructor * + * Instance_Class::Enable_Callback -- enables local processing of pokes * + * Instance_Class::Register_Servers -- registers a local DDE DNS service * + * Instance_Class::Cleanup_App -- currently does nothing * + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * Instance_Class::Open_Poke_Connection -- pokes some data to server * + * Instance_Class::Close_Poke_Connectionp -- closes connection to remote * + * Instance_Class::Poke_Server -- sends a chunk of data to remote * + * Instance_Class::dde_callback -- processes DDE transactions * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 +#include +#include "dde.h" + +/*************************************************************************** + * These are static members of Instance_Class + *=========================================================================*/ + +DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize +BOOL Instance_Class::process_pokes; // controls response to pokes +char Instance_Class::ascii_name[32]; // name of server + +#if (0) //ST - 5/8/2019 +static BOOL CALLBACK (*Instance_Class::callback) ( + LPBYTE pointer, // pointer to received data + long length // length of received data or advisory flag + ) = NULL; +#endif + +/*************************************************************************** + * Instance_Class::InstanceClass -- class constructor * + * * + * INPUT: * + * name1 null terminated ASCII client name * + * name1 null terminated ASCII server name * + * * + * OUTPUT: * + * dde_error = TRUE if error occurs when initializing DDE * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 ) +{ + name1; name2; + return; + +#if (0) //ST - 5/8/2019 + dde_error = FALSE; // no errors + process_pokes = FALSE; // disable pokes in callback + + id_inst = 0; // set to 0 for first time through + conv_handle = 0; // conversation handle reset + + lstrcpy( ascii_name, name1 ); // keep a record of ASCII name + + if ( DdeInitialize( + (LPDWORD) &id_inst, // instance identifier + dde_callback, + APPCLASS_STANDARD | // filter server messages + CBF_FAIL_SELFCONNECTIONS, // prevent from connecting with self + 0) != DMLERR_NO_ERROR) { // reserved + dde_error = TRUE; // flag an error + } + + local_name = DdeCreateStringHandle( + id_inst, // instance identifier + name1, // string to register + CP_WINANSI); // Windows ANSI code page + + remote_name = DdeCreateStringHandle( + id_inst, // instance identifier + name2, // string to register + CP_WINANSI); // Windows ANSI code page + + poke_topic = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE TOPIC", // System topic + CP_WINANSI); // Windows ANSI code page + + poke_item = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE ITEM", // System topic + CP_WINANSI); // Windows ANSI code page + + system_topic = DdeCreateStringHandle( + id_inst, // instance identifier + SZDDESYS_TOPIC, // System topic + CP_WINANSI); // Windows ANSI code page +#endif +} + +/*************************************************************************** + * Instance_Class::~Instance_Class -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::~Instance_Class() +{ + DdeUninitialize( id_inst ); +} + +/*************************************************************************** + * Instance_Class::Enable_Callback -- enables user callback * + * * + * INPUT: * + * TRUE = enable poke processing * + * FALSE = disable poke processing * + * * + * OUTPUT: * + * echos the input * + * * + * WARNINGS: * + * user callback must be explicitly enabled. Disbabled by default. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Enable_Callback( BOOL flag ) // enable or disable callback +{ + return (process_pokes = flag); + +} + +/*************************************************************************** + * Instance_Class::Register_Server -- registers a local DDE DNS service * + * * + * INPUT: * + * BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl * + * * + * OUTPUT: * + * TRUE == success * + * FALSE == failed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ +#if (0) //ST - 5/8/2019 +BOOL Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) ) +{ + + if (DdeNameService( id_inst, local_name, 0L, DNS_REGISTER ) != 0L) { + callback = callback_fnc; + return ( TRUE ); + } else { + return ( FALSE ); + } +} +#endif + +/*************************************************************************** + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * * + * INPUT: * + * name = HSZ string handle of server name. * + * * + * OUTPUT: * + * TRUE == successfully connected to remote * + * FALSE == failed to connect * + * * + * WARNINGS: * + * - Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * - Disconects before exiting. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Test_Server_Running( HSZ name ) +{ + + if( Open_Poke_Connection( name ) == TRUE) { + Close_Poke_Connection(); + return( TRUE ); + } else { + return( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Open_Poke_Connection -- open a connection to server * + * * + * INPUT: * + * name = HSZ server name. * + * * + * OUTPUT: * + * TRUE == successfully opened connection * + * FALSE == failed to connect * + * * + * WARNINGS: * + * Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Open_Poke_Connection( HSZ name ) +{ + conv_handle = DdeConnect( + id_inst, // instance identifier + name, // service name string handle + poke_topic, // topic string handle + (PCONVCONTEXT) NULL);// use default context + + if (conv_handle == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +/*************************************************************************** + * Instance_Class::Close_Poke_Connection -- closes poke connection * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TRUE == successfully closed connection * + * FALSE == failed to close connection for some reason * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Close_Poke_Connection( void ) +{ + if( conv_handle ) { + HCONV temp_handle = conv_handle; + conv_handle = NULL; + return( DdeDisconnect( temp_handle )); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::Poke_Server -- pokes some data to server * + * * + * INPUT: * + * poke_data points to data to send to remote * + * poke_length length of buffer to send * + * * + * OUTPUT: * + * TRUE == successfully poked the data * + * FALSE == failed to connect * + * * + * WARNINGS: * + * has a 3 second timeout (change POKE_TIMEOUT, in milliseconds) * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +#define POKE_TIMEOUT 60*1000 // 60 sec timeout + +BOOL Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length ) +{ + + if( DdeClientTransaction( + + poke_data, // address of data to pass to server + poke_length, // length of data + conv_handle, // handle of conversation + poke_topic, // handle of item name string + CF_TEXT, // no special clipboard data format + XTYP_POKE, // transaction type + POKE_TIMEOUT, // time-out duration (millisecs) + (LPDWORD) NULL // address of transaction result (don't check) + ) == 0) { + + return( FALSE); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::dde_callback -- callback dde event handler * + * * + * INPUT: * + * dde_event transaction type * + * uFmt clipboard data format * + * hconv handle of the conversation * + * hsz1 handle of a string * + * hsz2 handle of a string * + * hdata handle of a global memory object * + * dwData1 transaction-specific data * + * dwData2 transaction-specific data * + * * + * OUTPUT: * + * context specific HDDEDATA object * + * * + * WARNINGS: * + * NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +HDDEDATA CALLBACK Instance_Class::dde_callback( + + UINT dde_event, // transaction type + UINT uFmt, // clipboard data format + HCONV , // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD , // transaction-specific data + DWORD // transaction-specific data + ) +{ + dde_event; + uFmt; + hsz1; + hsz2; + hdata; + return (HDDEDATA)NULL; + +#if (0) // ST 5/8/2019 + if (!Instance_Class::callback){ + return (HDDEDATA) NULL; + } + + switch ( dde_event ) { + + case XTYP_REGISTER: + case XTYP_UNREGISTER: + + return (HDDEDATA) NULL; + + case XTYP_ADVDATA: + return (HDDEDATA) DDE_FACK; + + case XTYP_XACT_COMPLETE: + + return (HDDEDATA) NULL; + + case XTYP_DISCONNECT: + + Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT); + return (HDDEDATA) NULL; + + case XTYP_CONNECT: { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, Instance_Class::ascii_name)) { + return (HDDEDATA) NULL; + } + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) NULL; + } + + Instance_Class::callback( NULL, DDE_ADVISE_CONNECT); + return (HDDEDATA) TRUE; + } + + case XTYP_POKE: + + if (Instance_Class::process_pokes == FALSE ) { + return (HDDEDATA) DDE_FNOTPROCESSED; // processing disabled + } else { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } else if (uFmt == CF_TEXT) { // make sure it's CF_TEXT + + BOOL processed; + BYTE FAR *pdata; + DWORD dw_length; + + if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } + + processed = Instance_Class::callback((LPBYTE) pdata, dw_length); + + DdeUnaccessData( hdata ); + + if (processed == TRUE) { + return (HDDEDATA) DDE_FACK; + } else { + return (HDDEDATA) NULL; + } + + } + } + + default: + return (HDDEDATA) NULL; + } +#endif +} + +#endif //WIN32 diff --git a/REDALERT/DDE.H b/REDALERT/DDE.H new file mode 100644 index 000000000..1d29bea0e --- /dev/null +++ b/REDALERT/DDE.H @@ -0,0 +1,172 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.H * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * * + * This is the DDE (Instance_Class) which provides a simple CLIENT/SERVER * + * DDE model for data transactions between Windows applications. * + * This is a fairly naieve implementation allowing only one client/server * + * per Instance_Class object. * + * * + * Typical uses for this class are: * + * * + * i. Robust verification of whether an application is running * + * ii. Data transfer between applications * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* +***************************** Class defines ***************************** +*/ + +#ifndef __DDE_H +#define __DDE_H + +#define DDE_ADVISE_CONNECT -1 // advisory "client has connected" +#define DDE_ADVISE_DISCONNECT -2 // advisory "client has disconnected" + +/* +***************************** Class Declaration ***************************** +*/ + +class Instance_Class { + + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + + /*..................................................................... + Constructor: + - takes null terminated ASCII strings names for client and server + .....................................................................*/ + + Instance_Class( // constructor + LPSTR, // null terminated local sever name string + LPSTR // null terminated remote server name string + ); + + /*..................................................................... + Destructor: + .....................................................................*/ + ~Instance_Class(void); // the destructor + + /*..................................................................... + Send data routine: + - sends an unsolicited packet of data to the remote server + .....................................................................*/ + BOOL Poke_Server( LPBYTE, DWORD); + + /*..................................................................... + Send data routine: + - sets up DNS for the server and registers a user callback to handle + incoming data + .....................................................................*/ + BOOL Register_Server( BOOL (CALLBACK *)(LPBYTE, long)); + + /*..................................................................... + Does a trial connect to the remote server. + - used to determine whether server is alive or not (and thus running) + .....................................................................*/ + BOOL Test_Server_Running( HSZ ); + + /*..................................................................... + Enables user callback (disabled by default) + .....................................................................*/ + BOOL Enable_Callback( BOOL ); // enable or disable callback + + /*..................................................................... + Open a connection for sending data to remote server + .....................................................................*/ + BOOL Open_Poke_Connection( HSZ ); + + /*..................................................................... + Close connection with remote server + .....................................................................*/ + BOOL Close_Poke_Connection( void ); + + // + // static members + // + + /*..................................................................... + User callback - called upon receipt of incoming data (static member!) + .....................................................................*/ + static BOOL (CALLBACK *callback) ( + + LPBYTE pointer, // pointer to received data + long length // if >0 length of received data + // if <0 + // -1 == client connect detected + // -2 == client disconnect detected + ); + + /*..................................................................... + DDE callback, called when DDEML has an event for us + .....................................................................*/ + static HDDEDATA CALLBACK dde_callback( + + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); + HANDLE instance; // this application's instance + HWND hwnd; // valid window handle + + /*..................................................................... + member variables + .....................................................................*/ + + static DWORD id_inst; // instance identifier set by DdeInitialize + static BOOL process_pokes; // controls response to pokes + static char ascii_name[32]; // name of server + + // + // non-static member variables + // + + HSZ remote_name; // string handle for remote server name + HSZ local_name; // string handle for local server name + HSZ system_topic; // string handle for the "system" topic + HSZ poke_topic; // string handle for poking data to server topic + HSZ poke_item; // string handle for poking data to server item + + HCONV conv_handle; // conversation handle + BOOL dde_error; // error flag + +}; + +#endif + + \ No newline at end of file diff --git a/REDALERT/DEBUG.CPP b/REDALERT/DEBUG.CPP new file mode 100644 index 000000000..4d8da599a --- /dev/null +++ b/REDALERT/DEBUG.CPP @@ -0,0 +1,557 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Self_Regulate -- Regulates the logic timer to result in smooth animation. * + * Debug_Key -- Debug mode keyboard processing. * + * Bench_Time -- Convert benchmark timer into descriptive string. * + * Benchmarks -- Display the performance tracking benchmarks. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#include + + +#ifdef CHEAT_KEYS + + +static CDTimerClass DebugTimer; + +int VortexFrame = -1; + +/*********************************************************************************************** + * Debug_Key -- Debug mode keyboard processing. * + * * + * If debugging is enabled, then this routine will be called for every keystroke that the * + * game doesn't recognize. These extra keys usually perform some debugging function. * + * * + * INPUT: input -- The key code that was pressed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Debug_Key(unsigned input) +{ + static int map_x = -1; + static int map_y = -1; + static int map_width = -1; + static int map_height = -1; + + if (!input || input & KN_BUTTON) return; + + /* + ** Processing of normal keystrokes. + */ + if (Debug_Flag) { + + switch (input) { + + case KN_BACKSPACE: + + if (ChronalVortex.Is_Active()) { + ChronalVortex.Disappear(); + } else { + int xxxx = Get_Mouse_X() + Map.TacPixelX; + int yyyy = Get_Mouse_Y() + Map.TacPixelY; + CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy); + ChronalVortex.Appear ( Cell_Coord (cell) ); + } + break; + +#ifdef WIN32 + case KN_J: + Debug_MotionCapture = true; + break; + +#ifdef OBSOLETE + case KN_K: + /* + ** time to create a screen shot using the PCX code (if it works) + */ + if (!Debug_MotionCapture) { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + CDFileClass file; + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + sprintf(filename, "scrsht%02d.pcx", lp); + file.Set_Name(filename); + if (!file.Is_Available()) break; + } + + file.Cache(200000); + Write_PCX_File(file, temp_page, & GamePalette); + Sound_Effect(VOC_BEEP); + } + break; +#endif +#endif + + case KN_P: + { + for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) { + PlayerPtr->SuperWeapon[spc].Enable(true, true); + PlayerPtr->SuperWeapon[spc].Forced_Charge(true); + Map.Add(RTTI_SPECIAL, spc); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + case KN_I: + { + Map.Flash_Power(); + Map.Flash_Money(); + } + break; + + case KN_O: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_B: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_GRAVE: + { + WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE); + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + int damage = 1000; + new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord); + Explosion_Damage(coord, damage, NULL, warhead); + } + break; + + case KN_C: + Debug_Cheat = (Debug_Cheat == false); + PlayerPtr->IsRecalcNeeded = true; + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + if (!ScenarioInit) { + Map.Recalc(); + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + } + break; + + case (int)KN_Z|(int)KN_ALT_BIT: + if (map_x == -1) { + map_x = Map.MapCellX; + map_y = Map.MapCellY; + map_width = Map.MapCellWidth; + map_height = Map.MapCellHeight; + Map.MapCellX = 1; + Map.MapCellY = 1; + Map.MapCellWidth = MAP_CELL_W-2; + Map.MapCellHeight = MAP_CELL_H-2; + } else { + Map.MapCellX = map_x; + Map.MapCellY = map_y; + Map.MapCellWidth = map_width; + Map.MapCellHeight = map_height; + map_x = -1; + map_y = -1; + map_width = -1; + map_height = -1; + } + break; + + case KN_M: + if (Debug_Flag) { + if (MonoClass::Is_Enabled()) { + MonoClass::Disable(); + } else { + MonoClass::Enable(); + } + } + break; + + case (int)KN_W|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Win(); + break; + + case (int)KN_L|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Lose(); + break; + + case KN_DELETE: + if (CurrentObject.Count()) { + Map.Recalc(); + //CurrentObject[0]->Detach_All(); + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)CurrentObject[0])->Sell_Back(1); + } else { + ObjectClass * object = CurrentObject[0]; + object->Unselect(); + object->Limbo(); + delete object; + } + } + break; + + case (int)KN_DELETE|(int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + Map.Recalc(); + int damage = 50; + CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); + } + break; + + case KN_INSERT: + if (CurrentObject.Count()) { + Map.PendingObject = &CurrentObject[0]->Class_Of(); + if (Map.PendingObject) { + Map.PendingHouse = CurrentObject[0]->Owner(); + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + } + } + } + break; + + case KN_LBRACKET: + case KN_F11: + if (MonoPage == DMONO_FIRST) { + MonoPage = DMonoType(DMONO_COUNT-1); + } else { + MonoPage = DMonoType(MonoPage - 1); + } + DebugTimer = 0; + break; + + case KN_RBRACKET: + case KN_F12: + MonoPage = DMonoType(MonoPage + 1); + if (MonoPage == DMONO_COUNT) { + MonoPage = DMONO_FIRST; + } + DebugTimer = 0; + break; + + case KN_V: + case KN_F3: + Debug_Icon = (Debug_Icon == false); + Map.Flag_To_Redraw(true); + break; + + /* + ** Reveal entire map to player. + */ +// case KN_F4: +// if (Session.Type == GAME_NORMAL) { +// Debug_Unshroud = (Debug_Unshroud == false); +// Map.Flag_To_Redraw(true); +// } +// break; + + /* + ** Shows sight and fire range in the form of circles emanating from the currently + ** selected unit. The white circle is for sight range, the red circle is for + ** fire range. + */ + case KN_F7: + if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { + TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); + int sight = ((int)ttype.SightRange)<<8; + int weapon = 0; + if (ttype.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->Range; + Set_Logic_Page(SeenBuff); + COORDINATE center = CurrentObject[0]->Center_Coord(); + COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); + + for (int r = 0; r < 255; r += 10) { + int x,y,x1,y1; + DirType r1 = (DirType)r; + DirType r2 = (DirType)((r+10) & 0xFF); + + if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); + } + if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); + } + } + } + break; + + case ((int)KN_F4 | (int)KN_CTRL_BIT): + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * Bench_Time -- Convert benchmark timer into descriptive string. * + * * + * This routine will take the values of the benchmark timer specified and build a string * + * that displays the average time each event consumed as well as the ranking of how much * + * time that event took (total) during the tracking duration (one second?). * + * * + * INPUT: btype -- The benchmark to convert to a descriptive string. * + * * + * OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. * + * * + * WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid * + * until the next time that this routine is called. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static char const * Bench_Time(BenchType btype) +{ + static char buffer[32]; + + int rootcount = Benches[BENCH_GAME_FRAME].Count(); + if (rootcount == 0) rootcount = 1; + int roottime = Benches[BENCH_GAME_FRAME].Value(); + int count = Benches[btype].Count(); + int time = Benches[btype].Value(); + if (count > 0 && count * time > roottime * rootcount) time = roottime / count; + int percent = 0; + if (roottime != 0 && rootcount != 0) { + percent = ((count * time) * 99) / (roottime * rootcount); + } + if (percent > 99) percent = 99; + sprintf(buffer, "%-2d%% %7d", percent, time); + return(buffer); +} + + +/*********************************************************************************************** + * Benchmarks -- Display the performance tracking benchmarks. * + * * + * This will display the benchmarks for the various processes that are being tracked. The * + * display will indicate the fraction that each process is consuming out of the entire * + * process time as well as the time consumed by each individual event. The total fraction * + * is useful for determing what should be optimized. The individual time is useful for * + * guaging the effectiveness of optimization changes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the display will use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static void Benchmarks(MonoClass * mono) +{ + static bool _first = true; + if (_first) { + _first = false; + mono->Clear(); + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_PERFORMANCE)); + if (Benches == NULL) { + mono->Set_Cursor(20, 15); + mono->Printf(TXT_NO_PENTIUM); + } + } + + if (Benches != NULL) { + mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH)); + mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT)); + mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI)); + mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP)); + mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT)); + mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL)); + mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL)); + mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION)); + + mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL)); + mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS)); + mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS)); + + mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE)); + + mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER)); + mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR)); + mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR)); + mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL)); + mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER)); + mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD)); + mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS)); + mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY)); + + mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value()); + mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value()); + + for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) { + if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset(); + } + } +} + + +/*********************************************************************************************** + * Self_Regulate -- Regulates the logic timer to result in smooth animation * + * * + * The self regulation process checks the number of frames displayed * + * per second and from this determines the amount of time to devote * + * to internal logic processing. By adjusting the time allotted to * + * internal processing, smooth animation can be maintained. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: In order for this routine to work properly it MUST be * + * called every display loop. * + * * + * HISTORY: * + * 07/31/1991 JLB : Created. * + * 07/05/1994 JLB : Handles new monochrome system. * + *=============================================================================================*/ +#define UPDATE_INTERVAL TIMER_SECOND +void Self_Regulate(void) +{ + static ObjectClass * _lastobject = 0; + static bool _first=true; + + if (DebugTimer == 0) { + DebugTimer = UPDATE_INTERVAL; + + if (MonoClass::Is_Enabled()) { + if (_first) { + _first = false; + for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) { + MonoArray[index].Clear(); + } + } + + /* + ** Always update the stress tracking mono display even if it + ** currently isn't visible. + */ + Logic.Debug_Dump(&MonoArray[DMONO_STRESS]); + + MonoClass * mono = &MonoArray[MonoPage]; + mono->Set_Default_Attribute(MonoClass::NORMAL); + mono->View(); + + switch (MonoPage) { + case DMONO_EVENTS: + Benchmarks(mono); + break; + + case DMONO_OBJECT: + mono->Clear(); + + /* + ** Display the status of the currently selected object. + */ + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject) { + _lastobject->Debug_Dump(mono); + } + break; + + case DMONO_STRESS: +#ifdef OBSOLETE + mono->Set_Cursor(0, 20); + mono->Printf( + "Heap size:%10ld \r" + "Largest: %10ld \r" + "Ttl Free: %10ld \r" + "Frag: %10ld \r", + Heap_Size(MEM_NORMAL), + Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) + ); +#endif + break; + + case DMONO_HOUSE: + mono->Clear(); + + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject && _lastobject->Is_Techno()) { + ((TechnoClass *)_lastobject)->House->Debug_Dump(mono); + } + break; + + default: + break; + } + + mono->Set_Cursor(0, 0); + } + } +} +#endif diff --git a/REDALERT/DEBUG.H b/REDALERT/DEBUG.H new file mode 100644 index 000000000..f8ac1acf1 --- /dev/null +++ b/REDALERT/DEBUG.H @@ -0,0 +1,58 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define TXT_CLEAR_MAP 0x3e8 // Clear the map +#define TXT_INHERIT_MAP 0x3e9 // Inherit previous map +#define TXT_SPECIAL_OPTIONS 0x3ea // Select Special Options +#define TXT_VISIBLE_TARGET 0x3eb // Targeting flash visible to +#define TXT_TREE_TARGET 0x3ec // Allow targeting of trees. +#define TXT_MCV_DEPLOY 0x3ed // Allow undeploy of +#define TXT_SMART_DEFENCE 0x3ee // Employ smarter self defense +#define TXT_SLOW_BUILD 0x3ef // Moderate production speed. +#define TXT_THREE_POINT 0x3f0 // Use three point turn logic. +#define TXT_TIBERIUM_GROWTH 0x3f1 // Ore will grow. +#define TXT_TIBERIUM_SPREAD 0x3f2 // Ore will spread. +#define TXT_ROAD_PIECES 0x3f3 // Disable building "bib" +#define TXT_SCATTER 0x3f4 // Allow running from +#define TXT_SHOW_NAMES 0x3f5 // Show true object names. +#define TXT_DEFENDER_ADVANTAGE 0x3f6 // Defender has the advantage. +#define TXT_SEPARATE_HELIPAD 0x3f7 // Allow separate helipad +#define TXT_PASSWORD_CAPTION 0x3f8 // Password Request +#define TXT_PASSWORD_MESSAGE 0x3f9 // Enter Red Alert access code +#define TXT_PASSWORD_ERROR 0x3fa // Access code error detected. +#define TXT_TRY_AGAIN 0x3fb // Try Again +#define TXT_MINE_AWARE 0x3fc // Friendly units avoid +#define TXT_TRIGGER_EDITOR 0x3fd // Trigger Editor +#define TXT_TRIGGER_JUST_EVENT 0x3fe // Just This Event +#define TXT_TRIGGER_AND 0x3ff // ... and ... +#define TXT_TRIGGER_OR 0x400 // ... or ... +#define TXT_TRIGGER_LINKED 0x401 // ... linked ... +#define TXT_TRIGGER_JUST_ACTION 0x402 // Just This Action +#define TXT_TEAM_EDIT 0x403 // Team Editor +#define TXT_SELLABLE 0x404 // Sellable +#define TXT_REBUILD 0x405 // Rebuild +#define TXT_SPEED_BUILD 0x406 // Building constructin time +#define TXT_SCENARIO_ERRORx 0x407 // Scenario authentication +#define TXT_DEBUG_STRESS 0x408 // ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄ +#define TXT_DEBUG_VEHICLE 0x409 // ÚFull +#define TXT_DEBUG_INFANTRY 0x40a // ÚFull +#define TXT_DEBUG_SHIP 0x40b // ÚFull +#define TXT_DEBUG_BUILDING 0x40c // ÚFull +#define TXT_DEBUG_PERFORMANCE 0x40d // Game Objects³ Drawing +#define TXT_DEBUG_AIRCRAFT 0x40e // ÚFull +#define TXT_DEBUG_HOUSE 0x40f // ÚFull Name:ÄÄÄÄÄÄÄÄÂAct +#define TXT_NO_PENTIUM 0x410 // **************************** +#define TXT_SIZE_MAP 0x411 // Size Map +#define TXT_TRUCK_CRATE 0x412 // Trucks drop crate when diff --git a/REDALERT/DEFINES.H b/REDALERT/DEFINES.H new file mode 100644 index 000000000..c541e07ad --- /dev/null +++ b/REDALERT/DEFINES.H @@ -0,0 +1,3628 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DEFINES.H 4 3/07/97 9:55a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEFINES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef DEFINES_H +#define DEFINES_H + +/********************************************************************** +** Language control: define the desired language for this build. +*/ +//#define ENGLISH 1 +//#define FRENCH 1 +//#define GERMAN 1 +//#define SPAIN 1 (never used) +// - Language define is now passed in from the makefile. - + +/********************************************************************** +** Controls the nature of the game and its abilities. Only define +** one of these values. +** +** Internal version -- complete with scenario editor. +** Playtest version -- no editor but does have minimal cheat keys. +** Release version -- no editor or cheat keys -- all debugging info removed. +*/ +//#define INTERNAL_VERSION +//#define PLAYTEST_VERSION +#define RELEASE_VERSION + +/********************************************************************** +** ColinM +** Set this to enable dongle protection +*/ +//#define DONGLE + + +// Enable 640x400 VQ movie capability in WIN32 mode +#define MOVIE640 + + +//#if (GERMAN | FRENCH) +//#define BOGUSCD +//#endif + +#define FIXIT_SCORE_CRASH // Fixes score screen crash +#define FIXIT_MULTI_SAVE // Fixes multiplayer save/load +#define FIXIT_NO_COMP_ALLY // Prevent ally with computer +#define FIXIT_DESTNET // Fixes -destnet parameter in Win95 +#define FIXIT_RANDOM_GAME // Fixes random seed at start of multiplayer games +#define FIXIT_FORCE_CD // Forces correct CD load after scenario #1 +#define FIXIT_IP_CRASH // Fixes crash if internet game aborts too quickly +#define FIXIT_IP_STATS // Fixes so vessels show up in internet stat info +#define FIXIT_NAME_OVERRIDE // Allows changing of unit names +#define FIXIT_RADAR_JAMMED // Fixes unjamming by merely starting to build a radar facility +#define FIXIT_CAPTURE_BIB // Fixes so that if fake is captured, you still can't build off of it. +#define FIXIT_BASE_ANNOUNCE // Fixes so player controlled buildings count as base when attacked. +#define FIXIT_APTIVA_MODEM // Fixes crash with Aptiva modem. +#define FIXIT_FLAG_CHECK // Disable placing building over a flag. + +#define FIXIT_ANTS // Adds Ant Units + +#define FIXIT_CSII // Adds Aftermath CounterStrike II units +// ajw 9/28/98 - Note about FIXIT_CSII. Changes seem to have been made for Aftermath ("Counterstrike II") that: a) were +// bug fixes that should never be rolled back, b) change the nature of the game, at least in multi-player. This meant +// that the "Red Alert" executable ( == Counterstrike executable ) could no longer be built. Apparently, at the time, +// this was justified, as it was believed that no further patches to the RA executable would ever be necessary. +// Given that Denzil's DVD changes and my WOLAPI integration are essentially a patch, we've got a problem. +// We've decided to level the field and make sure every who gets or patches to the new version of Red Alert, CS, AM, (and +// their DVD equivalent(s)) will have the same executable. So we're assuming that all of the FIXIT_CSII changes are +// permanent (as, in fact, all prior FIXIT_'s are - makes me wonder why the old non-compiling code has to hang around +// forever), and fixing the code so that the assumption "this is an Aftermath game" is no longer hard-coded, but can +// change at runtime. (Which is what should have been done when Aftermath was created.) +// +#define FIXIT_CARRIER // Adds Aftermath aircraft carrier +#define FIXIT_PHASETRANSPORT // Adds Aftermath cloaking APC +// ajw - Discovered that engineer changing fields were specifically left out of aftrmath.ini, thus this has no effect. +// Engineer changes (and other game rule changes) are in mplayer.ini, which was loaded before aftermath-only mplayer games. +#define FIXIT_ENGINEER // Adds Engineer rules.ini overrides + +//#define FIXIT_FAST_LOAD // Enables faster INI loading + +// These fixes will cause the game to go out of sync. +//#define FIXIT_ENGINEER_CAPTURE // If building not allied, will still capture if engineer not allied with building. +//#define FIXIT_HELI_LANDING // Fixes so new helicopters land at free helipad +//#define FIXIT_MINE_PASSABLE // Fixes units not driving onto mines + +/* Turn on these changes for the 1.08 patch */ +#define FIXIT_PATCH_108 + +#ifdef FIXIT_PATCH_108 +#define STEVES_LOAD_OVERRIDE // Allows loading of CONQUER.ENG instead of from mix file. +#define FIXIT_DIFFICULTY // Fixes no difficulty level for CStrike missions +#define FIXIT_VERSION // Fixes version playability for 1.04, 1.07 & 1.08 +#define FIXIT_MODEM_LOAD_CRASH // Fixes crash after loading a modem game when names are the same +#define FIXIT_PHONELIST_CRASH // Fixes crash when clicking on an empty phonelist +#endif + +// Denotes changes made for version 3 - reunification of all existing versions and undoing of Aftermath divergence. - ajw +#define FIXIT_VERSION_3 +#define DVD + +// Define DVD to turn on RADVD additions/changes - Denzil +#ifdef DVD +//#define INTERNET_OFF +//#define MPEGMOVIE //PG +//#define MCIMPEG +#endif + +// Test to see if partial object drawing is any faster. +//#define PARTIAL +#define SORTDRAW + +/********************************************************************** +** If the scenario editor to to be active in this build then uncomment +** the following #define line. +*/ +#ifdef INTERNAL_VERSION +#define SCENARIO_EDITOR +#endif + + +/********************************************************************** +** This define enables the full set of cheat keys and special +** command line options. +*/ +#if defined(INTERNAL_VERSION) || defined(PLAYTEST_VERSION) +#define CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, the special Virgin limited cheat keys +** are enabled. This allows the "cheat" parameter and then only +** allows the ALT-W to win the mission. +*/ +#ifdef PLAYTEST_VERSION +#define VIRGIN_CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, then the network code will be enabled. +*/ +#define NETWORK +#define TIMING_FIX 1 + + +/********************************************************************** +** Define this to 1 to enable MPath-specific code. Do not define +** TEN at the same time. +*/ +#define MPATH 0 + + +/********************************************************************** +** Define this to 1 to enable TEN-specific code. Do not define +** MPATH at the same time. +*/ +#define TEN 0 + + +/********************************************************************** +** If this is defined, the DoList is "mirrored", for memory trasher +** detection. +*/ +#ifdef CHEAT_KEYS +//#define MIRROR_QUEUE +#endif + + +/********************************************************************** +** This define tells the Version Number class to use the date/time-based +** version numbering system. If this define is not set, the actual +** major/minor version numbers will be used. +*/ +//#define DEV_VERSION +//#define DEV_VER_NAME + + +/********************************************************************** +** This define enables a special additional foreign-version-number +** after the other version number, for display purposes only. +*/ +#if !defined(ENGLISH) +#define FOREIGN_VERSION +#define FOREIGN_VERSION_NUMBER 7 +#endif + + +/********************************************************************** +** This is the multiplier factor to convert low resution coordinates +** into their actual resolution counterparts. +*/ +#ifdef WIN32 +#define RESFACTOR 2 +#else +//#undef SCENARIO_EDITOR +#define RESFACTOR 1 +#endif + + +#define SIDEBAR_WID 80 + + +/********************************************************************** +** Optional parameter control for special options. +*/ + +/* +** Enable the set of limited cheat key options. +*/ +#ifdef VIRGIN_CHEAT_KEYS +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif + +/* +** Enable the full set of cheat key options. +*/ +#ifdef CHEAT_KEYS +#ifndef PARM_PLAYTEST +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif +#endif + + +#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL" + + +// +// Allow normal game play in the MPath version +// +#if(MPATH) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +// +// Allow normal game play in the TEN version +// +#if(TEN) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +/********************************************************************** +** Defines for verifying free disk space +*/ +#define INIT_FREE_DISK_SPACE 8388608 +#define SAVE_GAME_DISK_SPACE (INIT_FREE_DISK_SPACE - (1024*4096)) +//#define SAVE_GAME_DISK_SPACE 100000 + + +/********************************************************************** +** This is the complete list of VQs allowed to be played in the game. +*/ +typedef enum VQType { + VQ_NONE=-1, + VQ_AAGUN, + VQ_MIG, + VQ_SFROZEN, + VQ_AIRFIELD, + VQ_BATTLE, + VQ_BMAP, + VQ_BOMBRUN, + VQ_DPTHCHRG, + VQ_GRVESTNE, + VQ_MONTPASS, + VQ_MTNKFACT, + VQ_CRONTEST, + VQ_OILDRUM, + VQ_ALLYEND, + VQ_RADRRAID, + VQ_SHIPYARD, + VQ_SHORBOMB, + VQ_SITDUCK, + VQ_SLNTSRVC, + VQ_SNOWBASE, + VQ_EXECUTE, + VQ_TITLE, // Low res. + VQ_NUKESTOK, + VQ_V2ROCKET, + VQ_SEARCH, + VQ_BINOC, + VQ_ELEVATOR, + VQ_FROZEN, + VQ_MCV, + VQ_SHIPSINK, + VQ_SOVMCV, + VQ_TRINITY, + VQ_ALLYMORF, + VQ_APCESCPE, + VQ_BRDGTILT, + VQ_CRONFAIL, + VQ_STRAFE, + VQ_DESTROYR, + VQ_DOUBLE, + VQ_FLARE, + VQ_SNSTRAFE, + VQ_LANDING, + VQ_ONTHPRWL, + VQ_OVERRUN, + VQ_SNOWBOMB, + VQ_SOVCEMET, + VQ_TAKE_OFF, + VQ_TESLA, + VQ_SOVIET8, + VQ_SPOTTER, + VQ_SCENE1, + VQ_SCENE2, + VQ_SCENE4, + VQ_SOVFINAL, + VQ_ASSESS, + VQ_SOVIET10, + VQ_DUD, + VQ_MCV_LAND, + VQ_MCVBRDGE, + VQ_PERISCOP, + VQ_SHORBOM1, + VQ_SHORBOM2, + VQ_SOVBATL, + VQ_SOVTSTAR, + VQ_AFTRMATH, + VQ_SOVIET11, + VQ_MASASSLT, + VQ_REDINTRO, // High res + VQ_SOVIET1, + VQ_SOVIET2, + VQ_SOVIET3, + VQ_SOVIET4, + VQ_SOVIET5, + VQ_SOVIET6, + VQ_SOVIET7, + VQ_INTRO_MOVIE, + VQ_AVERTED, + VQ_COUNTDWN, + VQ_MOVINGIN, + VQ_ALLIED10, + VQ_ALLIED12, + VQ_ALLIED5, + VQ_ALLIED6, + VQ_ALLIED8, + VQ_TANYA1, + VQ_TANYA2, + VQ_ALLY10B, + VQ_ALLY11, + VQ_ALLY14, + VQ_ALLY9, + VQ_SPY, + VQ_TOOFAR, + VQ_SOVIET12, + VQ_SOVIET13, + VQ_SOVIET9, + VQ_BEACHEAD, + VQ_SOVIET14, + VQ_SIZZLE, + VQ_SIZZLE2, + VQ_ANTEND, + VQ_ANTINTRO, + + //2019/11/12 JAS - Added for Retaliation movies + VQ_RETALIATION_ALLIED1, + VQ_RETALIATION_ALLIED2, + VQ_RETALIATION_ALLIED3, + VQ_RETALIATION_ALLIED4, + VQ_RETALIATION_ALLIED5, + VQ_RETALIATION_ALLIED6, + VQ_RETALIATION_ALLIED7, + VQ_RETALIATION_ALLIED8, + VQ_RETALIATION_ALLIED9, + VQ_RETALIATION_ALLIED10, + + VQ_RETALIATION_SOVIET1, + VQ_RETALIATION_SOVIET2, + VQ_RETALIATION_SOVIET3, + VQ_RETALIATION_SOVIET4, + VQ_RETALIATION_SOVIET5, + VQ_RETALIATION_SOVIET6, + VQ_RETALIATION_SOVIET7, + VQ_RETALIATION_SOVIET8, + VQ_RETALIATION_SOVIET9, + VQ_RETALIATION_SOVIET10, + VQ_RETALIATION_WINA, + VQ_RETALIATION_WINS, + VQ_RETALIATION_ANTS, + + VQ_COUNT, + VQ_FIRST=0 +} VQType; + + +/********************************************************************** +** These enumerations are used to implement RTTI. The target system +** uses these and thus there can be no more RTTI types than can fit +** in the exponent of a target value. +*/ +typedef enum RTTIType : unsigned char { + RTTI_NONE=0, + RTTI_AIRCRAFT, + RTTI_AIRCRAFTTYPE, + RTTI_ANIM, + RTTI_ANIMTYPE, + RTTI_BUILDING, + RTTI_BUILDINGTYPE, + RTTI_BULLET, + RTTI_BULLETTYPE, + RTTI_CELL, + RTTI_FACTORY, + RTTI_HOUSE, + RTTI_HOUSETYPE, + RTTI_INFANTRY, + RTTI_INFANTRYTYPE, + RTTI_OVERLAY, + RTTI_OVERLAYTYPE, + RTTI_SMUDGE, + RTTI_SMUDGETYPE, + RTTI_SPECIAL, + RTTI_TEAM, + RTTI_TEAMTYPE, + RTTI_TEMPLATE, + RTTI_TEMPLATETYPE, + RTTI_TERRAIN, + RTTI_TERRAINTYPE, + RTTI_TRIGGER, + RTTI_TRIGGERTYPE, + RTTI_UNIT, + RTTI_UNITTYPE, + RTTI_VESSEL, + RTTI_VESSELTYPE, + + RTTI_COUNT +} RTTIType; + + +/********************************************************************** +** These are the difficulty settings of the game. +*/ +typedef enum DiffType : unsigned char { + DIFF_EASY, + DIFF_NORMAL, + DIFF_HARD, + + DIFF_COUNT, + DIFF_FIRST=0 +} DiffType; + + +/********************************************************************** +** This is the size of the speech buffer. This value should be as large +** as the largest speech sample, plus a few bytes for overhead +** (16 bytes is sufficient). +*/ +#define SPEECH_BUFFER_SIZE 50000L + + +/********************************************************************** +** The theater mixfiles are cached into a buffer of this size. Ensure +** that the size specified is at least as large as the largest +** theater mixfile data block. +*/ +#define THEATER_BUFFER_SIZE 1100000L + + +/********************************************************************** +** This is the size of the shape buffer. This buffer is used as a staging +** buffer for the shape drawing technology. It MUST be as big as the +** largest shape (uncompressed) that will be drawn. If this value is +** changed, be sure to update the makefile and rebuild all of the shape +** data files. +*/ +#define SHAPE_BUFFER_SIZE 131072L + + +/********************************************************************** +** Filenames of the data files it can create at run time. +*/ +#define FAME_FILE_NAME "HALLFAME.DAT" +#define NET_SAVE_FILE_NAME "SAVEGAME.NET" +#define CONFIG_FILE_NAME "REDALERT.INI" + + +/********************************************************************** +** Map controls. The map is composed of square elements called 'cells'. +** All larger elements are build upon these. +*/ + +#define HIGH_COORD_MASK 0x80008000L + +// Size of the map in cells. +#define MAP_CELL_W 128 +#define MAP_CELL_H 128 +#define MAP_CELL_TOTAL (MAP_CELL_W*MAP_CELL_H) + +#define REFRESH_EOL 32767 // This number ends a refresh/occupy offset list. +#define REFRESH_SIDEBAR 32766 // This number flags that sidebar needs refreshing. + + +/**************************************************************************** +** These are custom C&C specific types. The CELL is used for map coordinate +** with cell resolution. The COORDINATE type is used for map coordinates that +** have a lepton resolution. CELL is more efficient when indexing into the map +** and when size is critical. COORDINATE is more efficient when dealing with +** accuracy and object movement. +*/ +typedef unsigned short LEPTON; +typedef union { + LEPTON Raw; + struct { +#ifdef BIG_ENDIAN + unsigned char Cell; + unsigned char Lepton; +#else + unsigned char Lepton; + unsigned char Cell; +#endif + } Sub; +} LEPTON_COMPOSITE; + +typedef unsigned long COORDINATE; +typedef union { + COORDINATE Coord; + struct { +#ifdef BIG_ENDIAN + LEPTON_COMPOSITE Y; + LEPTON_COMPOSITE X; +#else + LEPTON_COMPOSITE X; + LEPTON_COMPOSITE Y; +#endif + } Sub; +} COORD_COMPOSITE; + +typedef signed short CELL; +#define SLUFF_BITS (sizeof(CELL)*CHAR_BIT)-(14) +typedef union { + CELL Cell; + struct { +#ifdef BIG_ENDIAN +#if SLUFF_BITS + /* + ** Unused upper bits will cause problems on a big-endian machine unless they + ** are deliberately accounted for. + */ + unsigned sluff:SLUF_BITS; +#endif + unsigned Y:7; + unsigned X:7; +#else + unsigned X:7; + unsigned Y:7; +#endif + } Sub; +} CELL_COMPOSITE; + +typedef int WAYPOINT; + + +/********************************************************************** +** This is the target composit information. Notice that with an RTTI_NONE +** and an index value of 0, the target value returned is identical with +** TARGET_NONE. This is by design and is necessary. +*/ +typedef long TARGET; + +#define TARGET_MANTISSA 24 // Bits of value precision. +#define TARGET_EXPONENT 8 +typedef union { + TARGET Target; + struct { +#ifdef BIG_ENDIAN + unsigned Exponent:TARGET_EXPONENT; + unsigned Mantissa:TARGET_MANTISSA; +#else + unsigned Mantissa:TARGET_MANTISSA; + unsigned Exponent:TARGET_EXPONENT; +#endif + } Sub; +} TARGET_COMPOSITE; + + +inline TARGET Build_Target(RTTIType kind, int value) +{ + TARGET_COMPOSITE target; + + target.Target = 0; + target.Sub.Exponent = kind; + target.Sub.Mantissa = value; + return(target.Target); +} + + +#define TARGET_NONE ((TARGET)0) + + +/* +** The map is broken down into regions of this specified dimensions. +*/ +#define REGION_WIDTH 4 +#define REGION_HEIGHT 4 +#define MAP_REGION_WIDTH (((MAP_CELL_W + (REGION_WIDTH -1)) / REGION_WIDTH)+2) +#define MAP_REGION_HEIGHT (((MAP_CELL_H + (REGION_WIDTH -1)) / REGION_HEIGHT)+2) +#define MAP_TOTAL_REGIONS (MAP_REGION_WIDTH * MAP_REGION_HEIGHT) + + +/********************************************************************** +** This enumerates the various known fear states for infantry units. +** At these stages, certain events or recovery actions are performed. +*/ +typedef enum FearType : unsigned char { + FEAR_NONE=0, // No fear at all (default state). + FEAR_ANXIOUS=10, // Something makes them scared. + FEAR_SCARED=100, // Scared enough to take cover. + FEAR_PANIC=200, // Run away! Run away! + FEAR_MAXIMUM=255 // Scared to death. +} FearType; + + +/********************************************************************** +** When a moving object moves, the Per_Cell_Process function is called +** at various times during the move. Certain operations must be +** performed at different stages of the move. This enum specifies the +** different conditions under which the Per_Cell_Process function is +** called. +*/ +typedef enum PCPType : unsigned char { + PCP_ROTATION, // When sitting in place and performing rotations. + PCP_DURING, // While moving between two cells. + PCP_END, // When the 'center' of a cell is reached during movement. +} PCPType; + + +/********************************************************************** +** A base is broken up into several zones. This type enumerates the +** various zones. +*/ +typedef enum ZoneType : char { + ZONE_CORE, // Center of base. + ZONE_NORTH, // North section. + ZONE_EAST, // East section. + ZONE_SOUTH, // South section. + ZONE_WEST, // West section. + + ZONE_COUNT, + ZONE_FIRST=0, + ZONE_NONE=-1 +} ZoneType; + + +/********************************************************************** +** The map is prescanned to mark of movement zones according to certain +** movement characteristics. This enum specifies those characteristics +** and movement zones kept track of. +*/ +typedef enum MZoneType : unsigned char { + MZONE_NORMAL, // Normal terrestrial objects (can't crush walls). + MZONE_CRUSHER, // Can crush crushable wall types. + MZONE_DESTROYER, // Can destroy walls. + MZONE_WATER, // Water based objects. + + MZONE_COUNT, + MZONE_FIRST=0 +} MZoneType; + +#define MZONEF_NORMAL (1<(static_cast(a) | static_cast(b));} + +inline ThreatType operator&(ThreatType a, ThreatType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ThreatType operator~(ThreatType a) +{return static_cast(~static_cast(a));} + + +#define THREAT_GROUND (THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_INFANTRY) + + +/********************************************************************** +** These return values are used when determine if firing is legal. +** By examining this value it can be determined what should be done +** to fix the reason why firing wasn't allowed. +*/ +typedef enum FireErrorType : unsigned char{ + FIRE_OK, // Weapon is allowed to fire. + FIRE_AMMO, // No ammo available to fire? + FIRE_FACING, // Not correctly facing target? + FIRE_REARM, // It is busy rearming? + FIRE_ROTATING, // Is it in process of rotating? + FIRE_ILLEGAL, // Is it targeting something illegal? + FIRE_CANT, // Is this unit one that cannot fire anything? + FIRE_MOVING, // Is it moving and not allowed to fire while moving? + FIRE_RANGE, // Is the target out of range? + FIRE_CLOAKED, // Is the shooter currently cloaked? + FIRE_BUSY // Is shooter currently doing something else? +} FireErrorType; + + +/********************************************************************** +** If an object can cloak, then it will be in one of these states. +** For objects that cannot cloak, they will always be in the +** UNCLOAKED state. This state controls how the object transitions between +** cloaked and uncloaked conditions. +*/ +typedef enum CloakType : unsigned char { + UNCLOAKED, // Completely visible (normal state). + CLOAKING, // In process of cloaking. + CLOAKED, // Completely cloaked (invisible). + UNCLOAKING // In process of uncloaking. +} CloakType; + + +/********************************************************************** +** For units that are cloaking, these value specify the visual character +** of the object. +*/ +typedef enum VisualType : unsigned char{ + VISUAL_NORMAL, // Completely visible -- normal. + VISUAL_INDISTINCT, // The edges shimmer and become indistinct. + VISUAL_DARKEN, // Color and texture is muted along with shimmering. + VISUAL_SHADOWY, // Body is translucent in addition to shimmering. + VISUAL_RIPPLE, // Just a ripple (true predator effect). + VISUAL_HIDDEN // Nothing at all is visible. +} VisualType; + + +/********************************************************************** +** These missions enumerate the various state machines that can apply to +** a game object. Only one of these state machines is active at any one +** time. +*/ +typedef enum MissionType : char { + MISSION_NONE=-1, + + MISSION_SLEEP, // Do nothing whatsoever. + MISSION_ATTACK, // Attack nearest enemy. + MISSION_MOVE, // Guard location or unit. + MISSION_QMOVE, // A queue list movement mission. + MISSION_RETREAT, // Return home for R & R. + MISSION_GUARD, // Stay still. + MISSION_STICKY, // Stay still -- never recruit. + MISSION_ENTER, // Move into object cooperatively. + MISSION_CAPTURE, // Move into in order to capture. + MISSION_HARVEST, // Hunt for and collect nearby Tiberium. + MISSION_GUARD_AREA, // Active guard of area. + MISSION_RETURN, // Head back to refinery. + MISSION_STOP, // Sit still. + MISSION_AMBUSH, // Wait until discovered. + MISSION_HUNT, // Active search and destroy. + MISSION_UNLOAD, // Search for and deliver cargo. + MISSION_SABOTAGE, // Move into in order to destroy. + MISSION_CONSTRUCTION, // Building buildup operation. + MISSION_DECONSTRUCTION, // Building builddown operation. + MISSION_REPAIR, // Repair process mission. + MISSION_RESCUE, + MISSION_MISSILE, + MISSION_HARMLESS, // Sit around and don't appear like a threat. + + MISSION_COUNT, + MISSION_FIRST=0 +} MissionType; + + +/********************************************************************** +** These are the enumerated animation sequences that a building may +** be processing. These serve to control the way that a building +** appears. +*/ +typedef enum BStateType : char { + BSTATE_NONE=-1, + BSTATE_CONSTRUCTION, // Construction animation. + BSTATE_IDLE, // Idle animation. + BSTATE_ACTIVE, // Animation when building is "doing its thing". + BSTATE_FULL, // Special alternate active state. + BSTATE_AUX1, // Auxiliary animation. + BSTATE_AUX2, // Auxiliary animation. + + BSTATE_COUNT +} BStateType; + + +/********************************************************************** +** Whenever a unit is selected and a click occurs over another object +** or terrain element, there is some action to initiate. This specifies +** the different types of actions possible. This also controls how the +** mouse cursor looks when "hovering" over the spot that clicking would +** occur at. +*/ +typedef enum ActionType : unsigned char { + ACTION_NONE, // Either undefined action or "do nothing". + ACTION_MOVE, // Can move there or at least try to. + ACTION_NOMOVE, // Special case for movable object, but illegal mouse position. + ACTION_ENTER, // Special case for infantry->APC or vehicle->Repair facility. + ACTION_SELF, // Self select special case. + ACTION_ATTACK, // Can attack or fire upon it in some fashion. + ACTION_HARVEST, // Special harvest mode. + ACTION_SELECT, // Would change selection to specified object. + ACTION_TOGGLE_SELECT,// Toggles select state of the object. + ACTION_CAPTURE, // The unit will try to capture the object. + ACTION_REPAIR, // The target object should be repaired. + ACTION_SELL, // The target building should be sold back. + ACTION_SELL_UNIT, // The target unit should be sold back. + ACTION_NO_SELL, // No sell or no repair. + ACTION_NO_REPAIR, // No sell or no repair. + ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object. + ACTION_PARA_BOMB, // Parachute bomb strike. + ACTION_PARA_INFANTRY,// Parachute infantry strike. + ACTION_PARA_SABOTEUR,// Parachute saboteur strike. + ACTION_NUKE_BOMB, // That target object should be blasted. + ACTION_AIR_STRIKE, // That target object should be blasted. + ACTION_CHRONOSPHERE, // That target object should be teleported. + ACTION_CHRONO2, // Teleport it to the given coordinates now. + ACTION_IRON_CURTAIN, // That target object should be invulnerable. + ACTION_SPY_MISSION, // Photo recon mission. + ACTION_GUARD_AREA, // Guard the area/object clicked on. + ACTION_HEAL, // Heal the infantryman clicked on. + ACTION_DAMAGE, // Enter and damage building. + ACTION_GREPAIR, // Enter and complete repair building. + ACTION_NO_DEPLOY, + ACTION_NO_ENTER, + ACTION_NO_GREPAIR, + ACTION_TOGGLE_PRIMARY, // Toggle the primary status of the factory. + + ACTION_COUNT +} ActionType; + + +/********************************************************************** +** When a unit gets damaged, the result of the damage is returned as +** this type. It can range from no damage taken to complete destruction. +*/ +typedef enum ResultType : unsigned char { + RESULT_NONE, // No damage was taken by the target. + RESULT_LIGHT, // Some damage was taken, but no state change occurred. + RESULT_HALF, // Damaged to below half strength (only returned on transition). + RESULT_MAJOR, // Damaged down to 1 hit point. + RESULT_DESTROYED // Damaged to complete destruction. +} ResultType; + + +#ifdef OBSOLETE +/********************************************************************** +** These are the special concrete control defines. They enumerate the +** sequence order of the concrete icons in the concrete art file. +*/ +// DEBUG === convert this to be zero based so that a nulled cell is the +// default cell. +enum ConcreteEnum { + C_NONE=-1, + C_LEFT=0, + C_RIGHT=1, + C_RIGHT_UPDOWN=2, + C_LEFT_UPDOWN=3, + C_UP_RIGHT=4, + C_UP_LEFT=5, + C_DOWN_RIGHT=6, + C_DOWN_LEFT=7, + C_RIGHT_DOWN=8, + C_LEFT_DOWN=9, + C_RIGHT_UP=10, + C_LEFT_UP=11, + C_UPDOWN_RIGHT=12, + C_UPDOWN_LEFT=13 +}; +#endif + + +/********************************************************************** +** Units that move can move at different speeds. These enumerate the +** different speeds that a unit can move. +*/ +typedef enum MPHType : unsigned char { + MPH_IMMOBILE=0, + MPH_VERY_SLOW=5, // 2 + MPH_KINDA_SLOW=6, // 3 + MPH_SLOW=8, // 4 + MPH_SLOW_ISH=10, // 5 + MPH_MEDIUM_SLOW=12, // 6 + MPH_MEDIUM=18, // 9 + MPH_MEDIUM_FAST=30, // 12 + MPH_MEDIUM_FASTER=35, // 14 + MPH_FAST=40, // 16 + MPH_ROCKET=60, // 24 + MPH_VERY_FAST=100, // 40 + MPH_LIGHT_SPEED=255 // 100 +} MPHType; + + +/********************************************************************** +** The houses that can be played are listed here. Each has their own +** personality and strengths. +*/ +typedef enum HousesType : char { + HOUSE_NONE=-1, + HOUSE_SPAIN, // Gold (unremapped) + HOUSE_GREECE, // LtBlue + HOUSE_USSR, // Red + HOUSE_ENGLAND, // Green + HOUSE_UKRAINE, // Orange + HOUSE_GERMANY, // Grey + HOUSE_FRANCE, // Blue + HOUSE_TURKEY, // Brown + HOUSE_GOOD, // Global Defense Initiative + HOUSE_BAD, // Brotherhood of Nod + HOUSE_NEUTRAL, // Civilians + HOUSE_JP, // Disaster Containment Team + HOUSE_MULTI1, // Multi-Player house #1 + HOUSE_MULTI2, // Multi-Player house #2 + HOUSE_MULTI3, // Multi-Player house #3 + HOUSE_MULTI4, // Multi-Player house #4 + HOUSE_MULTI5, // Multi-Player house #5 + HOUSE_MULTI6, // Multi-Player house #6 + HOUSE_MULTI7, // Multi-Player house #7 + HOUSE_MULTI8, // Multi-Player house #8 + HOUSE_COUNT, + HOUSE_FIRST=0 +} HousesType; + +//inline HousesType operator++(HousesType &, int) {return (HousesType)(int; +inline HousesType operator++(HousesType &ht) { ht = (HousesType)(((int)ht)+1); return ht; } + +#define HOUSEF_ALLIES (HOUSEF_ENGLAND|HOUSEF_SPAIN|HOUSEF_GREECE|HOUSEF_GERMANY|HOUSEF_FRANCE|HOUSEF_TURKEY|HOUSEF_GOOD) +#define HOUSEF_SOVIET (HOUSEF_USSR|HOUSEF_UKRAINE|HOUSEF_BAD) +#define HOUSEF_OTHERS (HOUSEF_NEUTRAL|HOUSEF_JP|HOUSEF_MULTI1|HOUSEF_MULTI2|HOUSEF_MULTI3|HOUSEF_MULTI4|HOUSEF_MULTI5|HOUSEF_MULTI6|HOUSEF_MULTI7|HOUSEF_MULTI8) +#define HOUSEF_NONE 0 + +#define HOUSEF_ENGLAND (1L< 2 players. +*/ +typedef enum ScenarioPlayerEnum : char +{ + SCEN_PLAYER_NONE = -1, + SCEN_PLAYER_SPAIN, + SCEN_PLAYER_GREECE, + SCEN_PLAYER_USSR, + SCEN_PLAYER_JP, + SCEN_PLAYER_2PLAYER, + SCEN_PLAYER_MPLAYER, + SCEN_PLAYER_COUNT, + SCEN_PLAYER_FIRST = 0 +} ScenarioPlayerType; + +inline ScenarioPlayerType operator++(ScenarioPlayerType &, int); + + +/********************************************************************** +** These are the directional parameters for a scenario. +*/ +typedef enum ScenarioDirEnum : char +{ + SCEN_DIR_NONE = -1, + SCEN_DIR_EAST, + SCEN_DIR_WEST, + SCEN_DIR_COUNT, + SCEN_DIR_FIRST = 0 +} ScenarioDirType; + +inline ScenarioDirType operator++(ScenarioDirType &, int); + + +/********************************************************************** +** These are the random variations of a scenario. +*/ +typedef enum ScenarioVarEnum : char +{ + SCEN_VAR_NONE = -1, + SCEN_VAR_A, + SCEN_VAR_B, + SCEN_VAR_C, + SCEN_VAR_D, + SCEN_VAR_COUNT, // comes before the Lose value! + SCEN_VAR_LOSE, + SCEN_VAR_FIRST = 0 +} ScenarioVarType; + +//inline ScenarioVarType operator++(ScenarioVarType &, int); +inline ScenarioVarType operator++(ScenarioVarType &n) { n = (ScenarioVarType)(((int)n) + 1); return n; } + + +/********************************************************************** +** The objects to be drawn on the map are grouped into layers. These +** enumerated values specify those layers. The ground layer is sorted +** from back to front. +*/ +typedef enum LayerType : char { + LAYER_NONE=-1, + LAYER_SURFACE, // Flat on the ground (no sorting or apparent vertical height). + LAYER_GROUND, // Touching the ground type object (units & buildings). + LAYER_AIR, // Flying above the ground (explosions & flames). + LAYER_TOP, // Topmost layer (aircraft & bullets). + + LAYER_COUNT, + LAYER_FIRST=0 +} LayerType; + +//PG inline LayerType operator++(LayerType &, int); +inline LayerType operator++(LayerType &n) { n = (LayerType)(((int)n) + 1); return n; } + + +/********************************************************************** +** This enumerates the various bullet types. These types specify bullet's +** visual and explosive characteristics. +*/ +typedef enum BulletType : char { + BULLET_NONE=-1, + + BULLET_INVISIBLE, + BULLET_CANNON, + BULLET_ACK, + BULLET_TORPEDO, + BULLET_FROG, + BULLET_HEAT_SEEKER, + BULLET_LASER_GUIDED, + BULLET_LOBBED, + BULLET_BOMBLET, + BULLET_BALLISTIC, + BULLET_PARACHUTE, + BULLET_FIREBALL, + BULLET_DOG, + BULLET_CATAPULT, + BULLET_AAMISSILE, + BULLET_GPS_SATELLITE, + BULLET_NUKE_UP, + BULLET_NUKE_DOWN, + + BULLET_COUNT, + BULLET_FIRST=0 +} BulletType; + +//PG inline BulletType operator++(BulletType &, int); +inline BulletType operator++(BulletType &n) { n = (BulletType)(((int)n) + 1); return n; } + + +/********************************************************************** +** All game buildings (structures) are enumerated here. This includes +** civilian structures as well. +*/ +typedef enum StructType : char { + STRUCT_NONE=-1, + STRUCT_ADVANCED_TECH, + STRUCT_IRON_CURTAIN, + STRUCT_WEAP, + STRUCT_CHRONOSPHERE, + STRUCT_PILLBOX, + STRUCT_CAMOPILLBOX, + STRUCT_RADAR, + STRUCT_GAP, + STRUCT_TURRET, + STRUCT_AAGUN, + STRUCT_FLAME_TURRET, + STRUCT_CONST, + STRUCT_REFINERY, + STRUCT_STORAGE, + STRUCT_HELIPAD, + STRUCT_SAM, + STRUCT_AIRSTRIP, + STRUCT_POWER, + STRUCT_ADVANCED_POWER, + STRUCT_SOVIET_TECH, + STRUCT_HOSPITAL, + STRUCT_BARRACKS, + STRUCT_TENT, + STRUCT_KENNEL, + STRUCT_REPAIR, + STRUCT_BIO_LAB, + STRUCT_MISSION, + STRUCT_SHIP_YARD, + STRUCT_SUB_PEN, + STRUCT_MSLO, + STRUCT_FORWARD_COM, + STRUCT_TESLA, + + /* + ** All buildings that are never used as a prerequisite + ** for construction, follow this point. Typically, this is + ** limited to civilian structures. Also, the following + ** buildings are NEVER used in the availability bit field + ** record that each house maintains. i.e., STRUCTF_???? + ** bit checking will never occur with the following + ** building types. + */ + STRUCT_FAKEWEAP, + STRUCT_FAKECONST, + STRUCT_FAKE_YARD, + STRUCT_FAKE_PEN, + STRUCT_FAKE_RADAR, + + STRUCT_SANDBAG_WALL, + STRUCT_CYCLONE_WALL, + STRUCT_BRICK_WALL, + STRUCT_BARBWIRE_WALL, + STRUCT_WOOD_WALL, + STRUCT_FENCE, + + STRUCT_AVMINE, + STRUCT_APMINE, + STRUCT_V01, + STRUCT_V02, + STRUCT_V03, + STRUCT_V04, + STRUCT_V05, + STRUCT_V06, + STRUCT_V07, + STRUCT_V08, + STRUCT_V09, + STRUCT_V10, + STRUCT_V11, + STRUCT_V12, + STRUCT_V13, + STRUCT_V14, + STRUCT_V15, + STRUCT_V16, + STRUCT_V17, + STRUCT_V18, + STRUCT_PUMP, + STRUCT_V20, + STRUCT_V21, + STRUCT_V22, + STRUCT_V23, + STRUCT_V24, + STRUCT_V25, + STRUCT_V26, + STRUCT_V27, + STRUCT_V28, + STRUCT_V29, + STRUCT_V30, + STRUCT_V31, + STRUCT_V32, + STRUCT_V33, + STRUCT_V34, + STRUCT_V35, + STRUCT_V36, + STRUCT_V37, + STRUCT_BARREL, + STRUCT_BARREL3, + +#ifdef FIXIT_ANTS + STRUCT_QUEEN, + STRUCT_LARVA1, + STRUCT_LARVA2, +#endif + + STRUCT_COUNT, + STRUCT_FIRST=0 +} StructType; + +//PG inline StructType operator++(StructType &, int); +inline StructType operator++(StructType &n) { n = (StructType)(((int)n) + 1); return n; } + +#define STRUCTF_NONE 0L +#define STRUCTF_ADVANCED_TECH (1L << STRUCT_ADVANCED_TECH) +#define STRUCTF_IRON_CURTAIN (1L << STRUCT_IRON_CURTAIN) +#define STRUCTF_WEAP (1L << STRUCT_WEAP) +#define STRUCTF_CHRONOSPHERE (1L << STRUCT_CHRONOSPHERE) +#define STRUCTF_PILLBOX (1L << STRUCT_PILLBOX) +#define STRUCTF_CAMOPILLBOX (1L << STRUCT_CAMOPILLBOX) +#define STRUCTF_RADAR (1L << STRUCT_RADAR) +#define STRUCTF_GAP (1L << STRUCT_GAP) +#define STRUCTF_TURRET (1L << STRUCT_TURRET) +#define STRUCTF_AAGUN (1L << STRUCT_AAGUN) +#define STRUCTF_FLAME_TURRET (1L << STRUCT_FLAME_TURRET) +#define STRUCTF_CONST (1L << STRUCT_CONST) +#define STRUCTF_REFINERY (1L << STRUCT_REFINERY) +#define STRUCTF_STORAGE (1L << STRUCT_STORAGE) +#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD) +#define STRUCTF_SAM (1L << STRUCT_SAM) +#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP) +#define STRUCTF_POWER (1L << STRUCT_POWER) +#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER) +#define STRUCTF_SOVIET_TECH (1L << STRUCT_SOVIET_TECH) +#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL) +#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS) +#define STRUCTF_TENT (1L << STRUCT_TENT) +#define STRUCTF_KENNEL (1L << STRUCT_KENNEL) +#define STRUCTF_REPAIR (1L << STRUCT_REPAIR) +#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB) +#define STRUCTF_MISSION (1L << STRUCT_MISSION) +#define STRUCTF_SHIP_YARD (1L << STRUCT_SHIP_YARD) +#define STRUCTF_SUB_PEN (1L << STRUCT_SUB_PEN) +#define STRUCTF_MSLO (1L << STRUCT_MSLO) +#define STRUCTF_FAKECONST (1L << STRUCT_FAKECONST) +#define STRUCTF_FAKEWEAP (1L << STRUCT_FAKEWEAP) + + +/********************************************************************** +** The overlays are enumerated here. An overlay functions similarly to +** a transparent icon. It is placed over the terrain but usually falls +** "under" buildings, trees, and units. +*/ +typedef enum OverlayType : char { + OVERLAY_NONE=-1, + OVERLAY_SANDBAG_WALL, // Piled sandbags. + OVERLAY_CYCLONE_WALL, // Chain-link fence. + OVERLAY_BRICK_WALL, // Solid concrete wall. + OVERLAY_BARBWIRE_WALL, // Barbed-wire wall. + OVERLAY_WOOD_WALL, // Wooden fence. + OVERLAY_GOLD1, + OVERLAY_GOLD2, + OVERLAY_GOLD3, + OVERLAY_GOLD4, + OVERLAY_GEMS1, + OVERLAY_GEMS2, + OVERLAY_GEMS3, + OVERLAY_GEMS4, + OVERLAY_V12, // Haystacks + OVERLAY_V13, // Haystack + OVERLAY_V14, // Wheat field + OVERLAY_V15, // Fallow field + OVERLAY_V16, // Corn field + OVERLAY_V17, // Celery field + OVERLAY_V18, // Potato field + OVERLAY_FLAG_SPOT, // Flag start location. + OVERLAY_WOOD_CRATE, // Wooden goodie crate. + OVERLAY_STEEL_CRATE, // Steel goodie crate. + OVERLAY_FENCE, // New fangled fence. + OVERLAY_WATER_CRATE, // Water goodie crate. + + OVERLAY_COUNT, + OVERLAY_FIRST=0 +} OverlayType; + +//PG inline OverlayType operator++(OverlayType &, int); + + +/********************************************************************** +** This specifies the infantry in the game. The "E" designation is +** similar to the army classification of enlisted soldiers. +*/ +typedef enum InfantryType : char { + INFANTRY_NONE=-1, + INFANTRY_E1, // Mini-gun armed. + INFANTRY_E2, // Grenade thrower. + INFANTRY_E3, // Rocket launcher. + INFANTRY_E4, // Flame thrower equipped. + INFANTRY_RENOVATOR, // Engineer. + INFANTRY_TANYA, // Saboteur. + INFANTRY_SPY, // Spy. + INFANTRY_THIEF, // Thief. + INFANTRY_MEDIC, // Field Medic. + INFANTRY_GENERAL, // Field Marshal. + INFANTRY_DOG, // Soviet attack dog + + INFANTRY_C1, // Civilian + INFANTRY_C2, // Civilian + INFANTRY_C3, // Civilian + INFANTRY_C4, // Civilian + INFANTRY_C5, // Civilian + INFANTRY_C6, // Civilian + INFANTRY_C7, // Civilian + INFANTRY_C8, // Civilian + INFANTRY_C9, // Civilian + INFANTRY_C10, // Nikumba + INFANTRY_EINSTEIN, // Einstein + INFANTRY_DELPHI, // Agent "Delphi" + INFANTRY_CHAN, // Dr. Chan + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CounterStrike II only! + INFANTRY_SHOCK, // Shock Trooper + INFANTRY_MECHANIC, +#endif + + INFANTRY_COUNT, + INFANTRY_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + INFANTRY_RA_COUNT = INFANTRY_SHOCK +#endif +} InfantryType; + +#define INFANTRYF_DOG (1L << INFANTRY_DOG) + +//PG inline InfantryType operator++(InfantryType &, int); + + +/********************************************************************** +** The game units are enumerated here. These include not only traditional +** vehicles, but also hovercraft and gunboats. +*/ +typedef enum UnitType : char { + UNIT_NONE=-1, + UNIT_HTANK, // Mammoth tank. + UNIT_MTANK, // Heavy tank. + UNIT_MTANK2, // Medium tank. + UNIT_LTANK, // Light tank ('Bradly'). + UNIT_APC, // APC. + UNIT_MINELAYER, // Mine-laying vehicle. + UNIT_JEEP, // 4x4 jeep replacement. + UNIT_HARVESTER, // Resource gathering vehicle. + UNIT_ARTY, // Artillery unit. + UNIT_MRJ, // Mobile Radar Jammer. + UNIT_MGG, // Mobile Gap Generator + UNIT_MCV, // Mobile construction vehicle. + UNIT_V2_LAUNCHER, // V2 rocket launcher. + UNIT_TRUCK, // Convoy truck + +#ifdef FIXIT_ANTS + UNIT_ANT1, // Warrior ant. + UNIT_ANT2, // Warrior ant. + UNIT_ANT3, // Warrior ant. +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CS II ONLY! + UNIT_CHRONOTANK, // Chrono-shifting tank + UNIT_TESLATANK, // Tesla-equipped tank + UNIT_MAD, // Timequake tank + UNIT_DEMOTRUCK, // Jihad truck +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + UNIT_PHASE, // cloaking APC for special missions +#endif +#endif + + UNIT_COUNT, + UNIT_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + UNIT_RA_COUNT = UNIT_CHRONOTANK +#endif +} UnitType; + +//PG inline UnitType operator++(UnitType &, int); + +#define UNITF_HTANK (1L<(static_cast(a) | static_cast(b));} + +inline TextPrintType operator&(TextPrintType a, TextPrintType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline TextPrintType operator~(TextPrintType a) +{return static_cast(~static_cast(a));} + +// Standard button text print flags. +#define TPF_BUTTON (TPF_CENTER|TPF_6PT_GRAD|TPF_NOSHADOW) +#define TPF_EBUTTON (TPF_CENTER|TPF_EFNT|TPF_NOSHADOW) +#define TPF_TEXT (TPF_6PT_GRAD|TPF_NOSHADOW) + + +/********************************************************************** +** These control the maximum number of objects in the game. Make sure that these +** maximums never exceed the maximum value for the "ID" element in the +** object class. +*/ +#define BUILDING_MAX 500 // Lasts for hours. +#define HOUSE_MAX (HOUSE_COUNT+1) // Lasts entire scenario. +#define INFANTRY_MAX 500 // Lasts for minutes. +#define UNIT_MAX 500 // Lasts for minutes. +#define VESSEL_MAX 100 // Lasts for minutes. +#define TEAMTYPE_MAX 60 // Lasts forever. + +// Save filename description. +#define DESCRIP_MAX 44 // 40 chars + CR + LF + CTRL-Z + NULL + +#define CONQUER_PATH_MAX 12 // Number of cells to look ahead for movement. + +#define EACH_INFANTRY_MAX (INFANTRY_MAX/5) // Default maximum any one player can have. +#define EACH_UNIT_MAX (UNIT_MAX/5) // Default maximum any one player can have. +#define EACH_BUILDING_MAX (BUILDING_MAX/5) // Default maximum any one player can build. +#define EACH_VESSEL_MAX (VESSEL_MAX/5) // Default maximum any one player can build. + + +/********************************************************************** +** Terrain can be of these different classes. At any point in the game +** a particular piece of ground must fall under one of these classifications. +** This is true, even if it is undergoing a temporary transition. +*/ +typedef enum LandType : char { + LAND_CLEAR, // "Clear" terrain. + LAND_ROAD, // Road terrain. + LAND_WATER, // Water. + LAND_ROCK, // Impassable rock. + LAND_WALL, // Wall (blocks movement). + LAND_TIBERIUM, // Tiberium field. + LAND_BEACH, // Beach terrain. + LAND_ROUGH, // Rocky terrain. + LAND_RIVER, // Rocky riverbed. + + LAND_COUNT, + LAND_NONE=-1, + LAND_FIRST=0 +} LandType; + + +/********************************************************************** +** The theaters of operation are as follows. +*/ +typedef enum TheaterType : char { + THEATER_NONE=-1, + THEATER_TEMPERATE, + THEATER_SNOW, + THEATER_INTERIOR, + + THEATER_COUNT, + THEATER_FIRST=0 +} TheaterType; + +//inline TheaterType operator++(TheaterType &, int); +inline TheaterType operator++(TheaterType &n) { n = (TheaterType)(((int)n)+1); return n; } + + +#define THEATERF_TEMPERATE (1<id) + + +#define MAX_LOG_LEVEL 10 + +// Maximum number of multi players possible. +#define MAX_PLAYERS 8 // max # of players we can have + +// Maximum number of teams +#define MAX_TEAMS 10 + +#endif \ No newline at end of file diff --git a/REDALERT/DESCDLG.CPP b/REDALERT/DESCDLG.CPP new file mode 100644 index 000000000..863ca6984 --- /dev/null +++ b/REDALERT/DESCDLG.CPP @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DESCDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DescriptionClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "descdlg.h" + + +/*********************************************************************************************** + * DescriptionClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to "fill-out" a description. * + * * + * INPUT: char *string - return answer here. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void DescriptionClass::Process(char * string) +{ + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, BUTTON_Y); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, 0, BUTTON_Y); + + cancelbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3)*2; + optionsbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3); + optionsbtn.Add_Tail(cancelbtn); + + EditClass edit( + BUTTON_EDIT, + string, + 31, + TPF_6PT_GRAD, + 0, + EDIT_Y, + EDIT_W); + + edit.Set_Focus(); + edit.X = OPTION_X + (OPTION_WIDTH - edit.Width)/2, + optionsbtn.Add_Tail(edit); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT, GadgetClass::LEFTPRESS); + optionsbtn.Add_Tail(dialog); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + optionsbtn.Add_Tail(background); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + Window_Hide_Mouse(WINDOW_EDITOR); + + /* + ** Draw the background + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_BORDER); // has border, raised up + Draw_Caption(TXT_MISSION_DESCRIPTION, OPTION_X, OPTION_Y, OPTION_WIDTH); + + /* + ** Draw the titles + */ + optionsbtn.Draw_All(); + Window_Show_Mouse(); + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = optionsbtn.Input(); + + /* + ** Process Input + */ + switch (input) { + + case KN_RETURN: + case KeyNumType(BUTTON_OPTIONS|KN_BUTTON): + strtrim(string); + process = false; + break; + + case KN_ESC: + case KeyNumType(BUTTON_CANCEL|KN_BUTTON): + string[0]= NULL; + strtrim(string); + process = false; + break; + + case KeyNumType(BUTTON_EDIT|KN_BUTTON): + break; + + default: + break; + } + } +} + diff --git a/REDALERT/DESCDLG.H b/REDALERT/DESCDLG.H new file mode 100644 index 000000000..b3b83fdf8 --- /dev/null +++ b/REDALERT/DESCDLG.H @@ -0,0 +1,66 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DESCDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef DESCDLG_H +#define DESCDLG_H + +#include "gadget.h" + +class DescriptionClass +{ + private: + + enum DescriptionClassEnum { + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2) & ~7), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+32, // Title's x pos + TEXT_Y=OPTION_Y+32, // Add 11 for each following line + BUTTON_OPTIONS=1, // Button number for "Ok" + BUTTON_CANCEL, + BUTTON_EDIT, + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + EDIT_Y =OPTION_Y+50, + EDIT_W =180 //204, + }; + + public: + DescriptionClass(void) {}; + void Process(char *string); +}; + +#endif + + diff --git a/REDALERT/DIAL8.CPP b/REDALERT/DIAL8.CPP new file mode 100644 index 000000000..cf09d6132 --- /dev/null +++ b/REDALERT/DIAL8.CPP @@ -0,0 +1,316 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIAL8.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Dial8Class::Action -- action routine for Dial8Class * + * Dial8Class::Dial8Class -- constructor for the facing dial * + * Dial8Class::Draw_Me -- render routine for Dial8Class * + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Dial8Class::Dial8Class -- constructor for the facing dial * + * * + * INPUT: * + * id button ID * + * x,y,w,h dimensions in window-relative pixels * + * dir numerical initial facing value (0-255); this is the * + * value returned by WWLIB Desired_Facing8() * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +Dial8Class::Dial8Class(int id, int x, int y, int w, int h, DirType dir) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTHELD | LEFTRELEASE, true) +{ + /* + ** Center coordinates. + */ + FaceX = X + (Width / 2); + FaceY = Y + (Height / 2); + + /* + ** Init directions. + */ + Direction = dir; // 0 - 255 + Facing = Dir_Facing(Direction); // 0 - 7 + OldFacing = Facing; // 0 - 7 + + /* + ** Compute the drawing dimensions: a 45-degree angle intersects a unity- + ** radius circle at (.707,.707). Make the decorations 8/10 of the radius, + ** and the line extend to 6/10 of the radius. Use Width/2 for x-radius, + ** Height/2 for y-radius. + */ + FacePoint[0][0] = FaceX; + FacePoint[0][1] = FaceY - (h * 8 / 2) / 10; + + FacePoint[1][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[1][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FacePoint[2][0] = FaceX + (w * 8 / 2) / 10; + FacePoint[2][1] = FaceY; + + FacePoint[3][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[3][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[4][0] = FaceX; + FacePoint[4][1] = FaceY + (h * 8 / 2) / 10; + + FacePoint[5][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[5][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[6][0] = FaceX - (w * 8 / 2) / 10; + FacePoint[6][1] = FaceY; + + FacePoint[7][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[7][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FaceLine[0][0] = FaceX; + FaceLine[0][1] = FaceY - (h * 6 / 2) / 10; + + FaceLine[1][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[1][1] = FaceY - (h * 7 * 6 / 2) / 100; + + FaceLine[2][0] = FaceX + (w * 6 / 2) / 10; + FaceLine[2][1] = FaceY; + + FaceLine[3][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[3][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[4][0] = FaceX; + FaceLine[4][1] = FaceY + (h * 6 / 2) / 10; + + FaceLine[5][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[5][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[6][0] = FaceX - (w * 6 / 2) / 10; + FaceLine[6][1] = FaceY; + + FaceLine[7][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[7][1] = FaceY - (h * 7 * 6 / 2) / 100; +} + + +/*************************************************************************** + * Dial8Class::Action -- activation function for Dial8Class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Action(unsigned flags, KeyNumType &key) +{ + static int is_sel = 0; + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + if (flags & LEFTPRESS) { + is_sel = 1; + } + + /* + ** If left mouse is clicked or held, and the dial has changed its direction, + ** invoke the parent Action routine: + ** GadgetClass::Action handles Sticky processing, & sets IsToRepaint if any + ** flag bits are set. + ** ControlClass::Action handles Peer_To_Peer notification, and substitutes + ** 'key' with the button ID if any flags are set, or 0 if no flags are set + */ + if (flags & LEFTPRESS || ((flags & LEFTHELD) && is_sel)) { + /* + ** Get new dial position (0-255) + */ + Direction = (DirType)Desired_Facing8(FaceX, FaceY, Get_Mouse_X(), Get_Mouse_Y()); + + /* + ** Convert to Facing value (0-7). + */ + Facing = Dir_Facing(Direction); + + /* + ** If it's moved, redraw. + */ + if (Facing!=OldFacing) { + OldFacing = Facing; + ControlClass::Action(flags, key); + return(true); + + } else { + + /* + ** Dial hasn't moved; kill the event & return + */ + key = KN_NONE; + ControlClass::Action(0, key); + return(true); + } + + } else { + + /* + ** Otherwise, no events have occurred; kill the event if it's a LEFTRELEASE, + ** and return + */ + if (flags & LEFTRELEASE) { + key = KN_NONE; + is_sel = 0; + } + return(ControlClass::Action(0, key)); + } +} + + +/*************************************************************************** + * Dial8Class::Draw_Me -- custom render routine for Dial8Class * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state* + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Draw_Me(int forced) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Redraw if parent indicates a redraw is needed + */ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + + /* + ** Draw background & decorations. + */ + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, true); + for (int i=0; i<8; i++) { + Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_RAISED, false); + } + + /* + ** Draw the hand & its shadow. + */ + LogicPage->Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1, scheme->Shadow); + LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1], scheme->Highlight); + + /* + ** Restore the mouse. + */ + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * DirType dial is pointing to * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +DirType Dial8Class::Get_Direction(void) const +{ + return(Direction); +} + + +/*************************************************************************** + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * * + * INPUT: * + * DirType to set dial to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void Dial8Class::Set_Direction(DirType dir) +{ + Direction = dir; + Facing = Dir_Facing(Direction); + OldFacing = Facing; + Flag_To_Redraw(); +} diff --git a/REDALERT/DIAL8.H b/REDALERT/DIAL8.H new file mode 100644 index 000000000..61183de33 --- /dev/null +++ b/REDALERT/DIAL8.H @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIAL8.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DIAL8_H +#define DIAL8_H + +class Dial8Class : public ControlClass +{ + public: + /* + ** Constructor/Destructor + */ + Dial8Class(int id, int x, int y, int w, int h, DirType dir); + + /* + ** Get/Set the direction the dial is currently pointing + */ + DirType Get_Direction(void) const; + void Set_Direction(DirType dir); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + protected: + /* + ** Overloaded event processing routine + */ + virtual int Action(unsigned flags, KeyNumType &key); + + private: + int FaceX; // x-coord of center of face + int FaceY; // y-coord of center of face + int FacePoint[8][2]; // coords of the little dial decorations + int FaceLine[8][2]; // coords for drawing the dial hand + DirType Direction; // 0-255 numerical direction of dial + FacingType Facing; // numerical facing direction of dial (0 - 7) + FacingType OldFacing; // previous Facing value + +}; + +#endif + diff --git a/REDALERT/DIALOG.CPP b/REDALERT/DIALOG.CPP new file mode 100644 index 000000000..d92474d3d --- /dev/null +++ b/REDALERT/DIALOG.CPP @@ -0,0 +1,996 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIALOG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIALOG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clip_Text_Print -- Prints text with clipping and support. * + * Dialog_Box -- draws a dialog background box * + * Display_Place_Building -- Displays the "place building" dialog box. * + * Display_Select_Target -- Displays the "choose target" prompt. * + * Display_Status -- Display the player scenario status box. * + * Draw_Box -- Displays a highlighted box. * + * Draw_Caption -- Draws a caption on a dialog box. * + * Fancy_Text_Print -- Prints text with a drop shadow. * + * Plain_Text_Print -- Prints text without using a color scheme * + * Redraw_Needed -- Determine if sidebar needs to be redrawn. * + * Render_Bar_Graph -- Renders a specified bargraph. * + * Simple_Text_Print -- Prints text with a drop shadow. * + * Window_Box -- Draws a fancy box over the specified window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "defines.h" //VG 10/17/96 + +unsigned char * Font_Palette(int color); + + +/*********************************************************************************************** + * Dialog_Box -- draws a dialog background box * + * * + * INPUT: * + * x,y,w,h the usual * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/26/1995 BR : Created. * + * 07/31/1996 JLB : Uses shapes to draw the box. * + *=============================================================================================*/ +void Dialog_Box(int x, int y, int w, int h) +{ +// Try to expand the box a little taller and a little wider to make room for +// the dialog box graphics in the DOS version. +#ifndef WIN32 + x = max(0, x-4); + y = max(0, y-4); + w = min(w+8, 320-x); + h = min(h+8, 200-y); +#endif + + WindowList[WINDOW_PARTIAL][WINDOWX] = x; + WindowList[WINDOW_PARTIAL][WINDOWY] = y; + WindowList[WINDOW_PARTIAL][WINDOWWIDTH] = w; + WindowList[WINDOW_PARTIAL][WINDOWHEIGHT] = h; + + /* + ** Always draw to the hidpage and then blit forward. + */ +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); +#endif + + /* + ** Draw the background block. + */ + int cx = w/2; + int cy = h/2; + void const * shapedata = MFCD::Retrieve("DD-BKGND.SHP"); +#ifdef WIN32 + CC_Draw_Shape(shapedata, 0, cx-312, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, cx, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 2, cx-312, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 3, cx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); +#else + CC_Draw_Shape(shapedata, 0, cx-156, cy-96, WINDOW_PARTIAL, SHAPE_WIN_REL); +#endif + /* + ** Draw the side strips. + */ + shapedata = MFCD::Retrieve("DD-EDGE.SHP"); + for (int yy = 0; yy < h; yy += 6) { + CC_Draw_Shape(shapedata, 0, 7*RESFACTOR, yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, w-((7+8)*RESFACTOR), yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + } + + /* + ** Draw the border bars. + */ + shapedata = MFCD::Retrieve("DD-LEFT.SHP"); + CC_Draw_Shape(shapedata, 0, 0, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, 0, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-RIGHT.SHP"); + int rightx = w - (7*RESFACTOR); +#ifndef WIN32 + rightx--; +#endif + CC_Draw_Shape(shapedata, 0, rightx, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, rightx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-BOTM.SHP"); + CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, cx, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-TOP.SHP"); + CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, cx, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + + /* + ** Draw the corner caps. + */ + shapedata = MFCD::Retrieve("DD-CRNR.SHP"); + CC_Draw_Shape(shapedata, 0, 0, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, w-(12*RESFACTOR-1), 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 2, 0, h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 3, w-(12*RESFACTOR-1), h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + +#ifdef WIN32 + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff, x, y, x, y, w, h, false); + WWMouse->Erase_Mouse(&HidPage, FALSE); +#else +// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, Map.ShadowPage->Get_Buffer()); + Hide_Mouse(); + HidPage.Blit(SeenBuff); + Show_Mouse(); +// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ((GraphicBufferClass*)Map.Shadow_Address())->Get_Buffer()); +#endif + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * Draw_Box -- Displays a highlighted box. * + * * + * This will draw a highlighted box to the logicpage. It can * + * optionally fill the box with a color as well. This is a low level * + * function and thus, it doesn't do any graphic mode color adjustments. * + * * + * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). * + * * + * w,h -- Width and height of box (in pixels). * + * * + * up -- Is the box rendered in the "up" stated? * + * * + * filled-- Is the box to be filled. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 JLB : Created. * + * 05/30/1992 JLB : Embedded color codes. * + * 07/31/1992 JLB : Depressed option added. * + *=============================================================================================*/ +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // Filler, Shadow, Hilite, Corner colors + + BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = { + { scheme->Background, scheme->Highlight, scheme->Shadow, scheme->Corners}, // Down + { scheme->Background, scheme->Shadow, scheme->Highlight, scheme->Corners}, // Raised + { DKGREY, WHITE, BLACK, DKGREY}, // Disabled down + { DKGREY, BLACK, LTGREY, DKGREY}, // Disabled up + { BLACK, scheme->Box, scheme->Box, BLACK}, // List box + { BLACK, scheme->Box, scheme->Box, BLACK}, // Dialog box + }; + + w--; + h--; + BoxStyleType const &style = ButtonColors[up]; + + if (filled) { + LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler); + } + + switch (up) { + case (BOXSTYLE_BOX): + LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight); + break; + + case (BOXSTYLE_BORDER): + LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight); + break; + + default: + LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow); + LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow); + + LogicPage->Draw_Line(x, y, x+w, y, style.Highlight); + LogicPage->Draw_Line(x, y, x, y+h, style.Highlight); + + LogicPage->Put_Pixel(x, y+h, style.Corner); + LogicPage->Put_Pixel(x+w, y, style.Corner); + break; + } +} + + +/*********************************************************************************************** + * Format_Window_String -- Separates a String into Lines. * + * This function will take a long string and break it up into lines * + * which are not longer then the window width. Any character < ' ' is * + * considered a new line marker and will be replaced by a NULL. * + * * + * INPUT: char *String - string to be formated. * + * int maxlinelen - Max length of any line in pixels. * + * * + * OUTPUT: int - number of lines string is. * + * * + * WARNINGS: The string passed in will be modified - NULLs will be put * + * into each position that will be a new line. * + * * + * HISTORY: * + * 03/27/1992 SB : Created. * + * 05/18/1995 JLB : Greatly revised for new font system. * + * 09/04/1996 BWG : Added '@' is treated as a carriage return for width calculations. * + *=============================================================================================*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + /* + ** Look for special line break character and force a line break when it is + ** discovered. + */ + if (*string == '@') { + *string = '\r'; + } + + // While the current line is less then the max length... + while (linelen < maxlinelen && *string != '\r' && *string != '\0' && *string != '@') { + linelen += Char_Pixel_Width(*string++); + } + + // if the line is to long... + if (linelen >= maxlinelen) { + + /* + ** Back up to an appropriate location to break. + */ + while (*string != ' ' && *string != '\r' && *string != '\0' && *string != '@') { + linelen -= Char_Pixel_Width(*string--); + } + + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *string++ = '\r'; + } + } + return(lines); +} + + +/*********************************************************************************************** + * Window_Box -- Draws a fancy box over the specified window. * + * * + * This routine will draw a fancy (shaded) box over the specified * + * window. This is the effect used to give the polished look to * + * screen rectangles without having to use art. * + * * + * INPUT: window -- Specified window to fill and border. * + * * + * style -- The style to render the window. * + * * + * OUTPUT: none * + * * + * WARNINGS: The rendering is done to the LogicPage. * + * * + * HISTORY: * + * 03/03/1992 JLB : Created. * + * 07/31/1992 JLB : Cool raised border effect. * + * 06/08/1994 JLB : Takes appropriate enumeration parameters. * + *=============================================================================================*/ +void Window_Box(WindowNumberType window, BoxStyleEnum style) +{ + int x = WindowList[window][WINDOWX]; + int y = WindowList[window][WINDOWY]; + int w = WindowList[window][WINDOWWIDTH]; + int h = WindowList[window][WINDOWHEIGHT]; + + /* + ** If it is to be rendered to the seenpage, then + ** hide the mouse. + */ + if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x ,y, x+w, y+h); + + Draw_Box(x, y, w, h, style, true); + + /* + ** Restore the mouse if it has been hidden and return. + */ + if (LogicPage == &SeenBuff) Conditional_Show_Mouse(); +} + + +/*********************************************************************************************** + * Simple_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * The C&C gradient font colors are as follows: * + * 0 transparent (background) * + * 1 foreground color for mono-color fonts only * + * 2 shadow under characters ("drop shadow") * + * 3 shadow all around characters ("full shadow") * + * 4-10 unused * + * 11 top row * + * 12 next row * + * 13 next row * + * 14 next row * + * 15 bottom row * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + *=============================================================================================*/ +void Simple_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag) +{ + static int yspace=0; // Y spacing adjustment for font. + static int xspace=0; // Spacing adjustment for font. + void const * font=0; // Font to use. + int shadow; // Requested shadow value. + unsigned char fontpalette[16]; // Working font palette array. + int forecolor; + + if (fore == NULL) { + fore = &ColorRemaps[PCOLOR_RED]; + } + + /* + ** Init the font palette to the given background color + */ + memset(&fontpalette[0], back, 16); + + forecolor = fore->Color; + + /* + ** A gradient font always requires special fixups for the palette + */ + int point = (flag & (TextPrintType)0x000F); + if (point == TPF_VCR || point == TPF_6PT_GRAD || point == TPF_METAL12 || point == TPF_EFNT || point == TPF_TYPE) { + + /* + ** If a gradient palette is specified, copy the remap table directly, otherwise + ** use the foreground color as the entire font remap color. + */ + if (flag & TPF_USE_GRAD_PAL) { + memcpy(fontpalette, fore->FontRemap, 16); + forecolor = fore->Color; + if (point == TPF_TYPE) { + forecolor = fontpalette[1]; + } + } else { + memset(&fontpalette[4], fore->Color, 12); + forecolor = fore->Color; + } + + /* + ** Medium color: set all font colors to a medium value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_MEDIUM_COLOR) { + forecolor = fore->Color; + memset(&fontpalette[4], fore->Color, 12); + } + + /* + ** Bright color: set all font colors to a bright value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_BRIGHT_COLOR) { + forecolor = fore->Bright; + memset(&fontpalette[4], fore->BrightColor, 12); + } + } + + /* + ** Change the current font if it differs from the font desired. + */ +#ifdef WIN32 + xspace = 1; +#else + xspace = 0; +#endif + yspace = 0; + + switch (point) { + case TPF_SCORE: + font = ScoreFontPtr; + break; + + case TPF_METAL12: + font = Metal12FontPtr; + //xspace += 1; + break; + + case TPF_MAP: + font = MapFontPtr; + xspace -= 1; + break; + + case TPF_VCR: + font = VCRFontPtr; + break; + + case TPF_6PT_GRAD: + font = GradFont6Ptr; + xspace -= 1; + break; + + case TPF_3POINT: + xspace += 1; + font = Font3Ptr; + flag = flag & ~(TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_NOSHADOW); + break; + + case TPF_6POINT: + font = Font6Ptr; + xspace -= 1; + break; + + case TPF_EFNT: + font = EditorFont; +#ifdef WIN32 + yspace += 1; + xspace -= 1; +#endif + xspace -= 1; + break; + + case TPF_8POINT: + font = Font8Ptr; +#ifdef WIN32 + xspace -= 2; + yspace -= 4; +#else + xspace -= 1; + yspace -= 2; +#endif + break; + + case TPF_LED: +#ifdef WIN32 + xspace -= 4; +#else + xspace -= 2; +#endif + font = FontLEDPtr; + break; + + case TPF_TYPE: + font = TypeFontPtr; + xspace -= 1; + +#ifdef WOLAPI_INTEGRATION + xspace -= 2; + yspace += 2; +#else // I am implicitly assuming that TPF_TYPE was no longer being used, before I came along, despite the following. ajw +#ifdef GERMAN + yspace += 4; //VG 10/17/96 +#endif +#endif + + break; + + default: + font = FontPtr; + break; + } + + /* + ** Change the current font palette according to the dropshadow flags. + */ + shadow = (flag & (TPF_NOSHADOW|TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_LIGHTSHADOW)); + switch (shadow) { + + /* + ** The text is rendered plain. + */ + case TPF_NOSHADOW: + fontpalette[2] = back; + fontpalette[3] = back; + xspace -= 1; +#ifdef WIN32 + yspace -= 2; +#else + yspace -= 1; +#endif + break; + + /* + ** The text is rendered with a simple + ** drop shadow. + */ + case TPF_DROPSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Special engraved text look for the options + ** dialog system. + */ + case TPF_LIGHTSHADOW: + fontpalette[2] = ((14 * 16) + 7)+1; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Each letter is surrounded by black. This is used + ** when the text will be over a non-plain background. + */ + case TPF_FULLSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = BLACK; + xspace -= 1; + break; + + default: + break; + } + if (point != TPF_TYPE) { + fontpalette[0] = back; + fontpalette[1] = fore->Color; + } + + /* + ** Set the font and spacing according to the values they should be. + */ + FontXSpacing = xspace; + FontYSpacing = yspace; + Set_Font(font); + Set_Font_Palette(fontpalette); + + /* + ** Display the (centered) message if there is one. + */ + if (text && *text) { + switch (flag & (TPF_CENTER|TPF_RIGHT)) { + case TPF_CENTER: + x -= String_Pixel_Width(text)>>1; + break; + + case TPF_RIGHT: + x -= String_Pixel_Width(text); + break; + + default: + break; + } + + if (x < (unsigned)LogicPage->Get_Width() && y < (unsigned)LogicPage->Get_Height()) { + LogicPage->Print(text, x, y, forecolor, back); +// LogicPage->Print(text, x, y, fore->Color, back); + } + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Text number to print. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 11/29/1994 JLB : Created * + *=============================================================================================*/ +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If the text number is valid, then process it. + */ + if (text != TXT_NONE) { + va_start(arg, flag); + + /* + ** The text string must be locked since the vsprintf function doesn't know + ** how to handle EMS pointers. + */ + char const * tptr = Text_String(text); + vsprintf(buffer, tptr, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are to be changed, since the text number is TXT_NONE. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + * 11/29/1994 JLB : Separated actual draw action. * + *=============================================================================================*/ +void Fancy_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If there is a valid text string pointer then build the final string into the + ** working buffer before sending it to the simple string printing routine. + */ + if (text) { + + /* + ** Since vsprintf doesn't know about EMS pointers, be sure to surround this + ** call with locking code. + */ + va_start(arg, flag); + vsprintf(buffer, text, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are desired to be changed, so call the simple print routine with + ** a NULL text pointer. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Clip_Text_Print -- Prints text with clipping and support. * + * * + * Use this routine to print text that that should be clipped at an arbitrary right margin * + * as well as possibly recognizing characters. Typical users of this routine would * + * be list boxes. * + * * + * INPUT: text -- Reference to the text to print. * + * * + * x,y -- Pixel coordinate of the upper left corner of the text position. * + * * + * fore -- The foreground color to use. * + * * + * back -- The background color to use. * + * * + * flag -- The text print flags to use. * + * * + * width -- The maximum pixel width to draw the text. Extra characters beyond this * + * point will not be printed. * + * * + * tabs -- Optional pointer to a series of pixel tabstop positions. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void Conquer_Clip_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, int width, int const * tabs) +{ + char buffer[512]; + + if (text) { + strcpy(buffer, text); + + /* + ** Set the font and spacing characteristics according to the flag + ** value passed in. + */ + //PG_TO_FIX + //Simple_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, flag); + + char * source = &buffer[0]; + unsigned offset = 0; + int processing = true; + while (processing && offset < (unsigned) width) { + char * ptr = strchr(source, '\t'); + + /* + ** Zap the tab character. It will be processed later. + */ + if (ptr) { + *ptr = '\0'; + } + + if (*source) { + + /* + ** Scan forward until the end of the string is reached or the + ** maximum width, whichever comes first. + */ + int w = 0; + char * bptr = source; + do { + w += Char_Pixel_Width(*bptr++); + } while (*bptr && offset+w < (unsigned)width); + + /* + ** If the maximum width has been exceeded, then remove the last + ** character and signal that further processing is not necessary. + */ + if (offset+w >= (unsigned)width) { + bptr--; + w -= Char_Pixel_Width(*bptr); + *bptr = '\0'; + processing = 0; + } + + /* + ** Print this text block and advance the offset accordingly. + */ + Simple_Text_Print(source, x+offset, y, fore, back, flag); + offset += w; + } + + /* + ** If a was the terminator for this text block, then advance + ** to the next tabstop. + */ + if (ptr) { + if (tabs) { + while ((int)offset > *tabs) { + tabs++; + } + offset = *tabs; + } else { + offset = ((offset+1 / 50) + 1) * 50; + } + source = ptr+1; + } else { + break; + } + } + } +} + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(char const * text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + + +unsigned char * Font_Palette(int color) +{ + static unsigned char _fpalette[16]; + + memset(_fpalette, '\0', sizeof(_fpalette)); + memset(&_fpalette[11], color, 5); + return(_fpalette); +} + + + +/*********************************************************************************************** + * Draw_Caption -- Draws a caption on a dialog box. * + * * + * This routine draws the caption text and any fancy filigree that the dialog may require. * + * * + * INPUT: text -- The text of the caption. This is the text number. * + * * + * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. * + * * + * w -- The width of the dialog box (in pixels). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + *=============================================================================================*/ +void Draw_Caption(int text, int x, int y, int w) +{ + Draw_Caption(Text_String(text), x, y, w); +} + + +void Draw_Caption(char const * text, int x, int y, int w) +{ + /* + ** Draw the caption. + */ + if (text != NULL && *text != '\0') { + if (Debug_Map) { + Fancy_Text_Print(text, w/2 + x, (2 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_EFNT|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } else { + Fancy_Text_Print(text, w/2 + x, (8 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + int length = String_Pixel_Width(text); + LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + (8 * RESFACTOR), (x+(w/2))+(length/2), + y+FontHeight+FontYSpacing + (8 * RESFACTOR), GadgetClass::Get_Color_Scheme()->Box); + } + } +} diff --git a/REDALERT/DIBAPI.H b/REDALERT/DIBAPI.H new file mode 100644 index 000000000..9fa1609ad --- /dev/null +++ b/REDALERT/DIBAPI.H @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef DIBAPI_H +#define DIBAPI_H +/* + * dibapi.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * BitmapToDIB() - Creates a DIB from a bitmap + * ChangeBitmapFormat() - Changes a bitmap to a specified DIB format + * ChangeDIBFormat() - Changes a DIB's BPP and/or compression format + * CopyScreenToBitmap() - Copies entire screen to a standard Bitmap + * CopyScreenToDIB() - Copies entire screen to a DIB + * CopyWindowToBitmap() - Copies a window to a standard Bitmap + * CopyWindowToDIB() - Copies a window to a DIB + * CreateDIBPalette() - Creates a palette from a DIB + * CreateDIB() - Creates a new DIB + * DestroyDIB() - Deletes DIB when finished using it + * DIBError() - Displays message box with error message + * DIBHeight() - Gets the DIB height + * DIBNumColors() - Calculates number of colors in the DIB's color table + * DIBToBitmap() - Creates a bitmap from a DIB + * DIBWidth() - Gets the DIB width + * FindDIBBits() - Sets pointer to the DIB bits + * GetSystemPalette() - Gets the current palette + * LoadDIB() - Loads a DIB from a file + * PaintBitmap() - Displays standard bitmap in the specified DC + * PaintDIB() - Displays DIB in the specified DC + * PalEntriesOnDevice() - Gets the number of palette entries + * PaletteSize() - Calculates the buffer size required by a palette + * PrintDIB() - Prints the specified DIB + * PrintScreen() - Prints the entire screen + * PrintWindow() - Prints all or part of a window + * SaveDIB() - Saves the specified dib in a file + * + * See the file DIBAPI.TXT for more information about these functions. + * + * ajw added + * LoadDIB_FromMemory() - Loads a DIB from BMP file data located at a location in memory. + * + */ + + +/* Handle to a DIB */ +#define HDIB HANDLE + + +/* Print Area selection */ +#define PW_WINDOW 1 +#define PW_CLIENT 2 + + +/* Print Options selection */ +#define PW_BESTFIT 1 +#define PW_STRETCHTOPAGE 2 +#define PW_SCALE 3 + +/* DIB Macros*/ + +// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits" +// parameter is the bit count for the scanline (biWidth * biBitCount), +// and this macro returns the number of DWORD-aligned bytes needed +// to hold those bits. + +#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + +/* Error constants */ +enum { + ERR_MIN = 0, // All error #s >= this value + ERR_NOT_DIB = 0, // Tried to load a file, NOT a DIB! + ERR_MEMORY, // Not enough memory! + ERR_READ, // Error reading file! + ERR_LOCK, // Error on a GlobalLock()! + ERR_OPEN, // Error opening a file! + ERR_CREATEPAL, // Error creating palette. + ERR_GETDC, // Couldn't get a DC. + ERR_CREATEDDB, // Error create a DDB. + ERR_STRETCHBLT, // StretchBlt() returned failure. + ERR_STRETCHDIBITS, // StretchDIBits() returned failure. + ERR_SETDIBITSTODEVICE, // SetDIBitsToDevice() failed. + ERR_STARTDOC, // Error calling StartDoc(). + ERR_NOGDIMODULE, // Couldn't find GDI module in memory. + ERR_SETABORTPROC, // Error calling SetAbortProc(). + ERR_STARTPAGE, // Error calling StartPage(). + ERR_NEWFRAME, // Error calling NEWFRAME escape. + ERR_ENDPAGE, // Error calling EndPage(). + ERR_ENDDOC, // Error calling EndDoc(). + ERR_SETDIBITS, // Error calling SetDIBits(). + ERR_FILENOTFOUND, // Error opening file in GetDib() + ERR_INVALIDHANDLE, // Invalid Handle + ERR_DIBFUNCTION, // Error on call to DIB function + ERR_MAX // All error #s < this value + }; + + + +/* Function prototypes */ + +HDIB FAR BitmapToDIB (HBITMAP hBitmap, HPALETTE hPal); +HDIB FAR ChangeBitmapFormat (HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal); +HDIB FAR ChangeDIBFormat (HDIB hDIB, WORD wBitCount, + DWORD dwCompression); +HBITMAP FAR CopyScreenToBitmap (LPRECT); +HDIB FAR CopyScreenToDIB (LPRECT); +HBITMAP FAR CopyWindowToBitmap (HWND, WORD); +HDIB FAR CopyWindowToDIB (HWND, WORD); +HPALETTE FAR CreateDIBPalette (HDIB hDIB); +HDIB FAR CreateDIB(DWORD, DWORD, WORD); +WORD FAR DestroyDIB (HDIB); +void FAR DIBError (int ErrNo); +DWORD FAR DIBHeight (LPCSTR lpDIB); +WORD FAR DIBNumColors (LPCSTR lpDIB); +HBITMAP FAR DIBToBitmap (HDIB hDIB, HPALETTE hPal); +DWORD FAR DIBWidth (LPCSTR lpDIB); +LPSTR FAR FindDIBBits (LPCSTR lpDIB); +HPALETTE FAR GetSystemPalette (void); +HDIB FAR LoadDIB (LPSTR); +BOOL FAR PaintBitmap (HDC, LPRECT, HBITMAP, LPRECT, HPALETTE); +BOOL FAR PaintDIB (HDC, LPRECT, HDIB, LPRECT, HPALETTE); +int FAR PalEntriesOnDevice (HDC hDC); +WORD FAR PaletteSize (LPCSTR lpDIB); +WORD FAR PrintDIB (HDIB, WORD, WORD, WORD, LPSTR); +WORD FAR PrintScreen (LPRECT, WORD, WORD, WORD, LPSTR); +WORD FAR PrintWindow (HWND, WORD, WORD, WORD, WORD, LPSTR); +WORD FAR SaveDIB (HDIB, LPSTR); + +// ajw added +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ); + +#endif diff --git a/REDALERT/DIBFILE.CPP b/REDALERT/DIBFILE.CPP new file mode 100644 index 000000000..a8598d276 --- /dev/null +++ b/REDALERT/DIBFILE.CPP @@ -0,0 +1,702 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//******************************************************************* +// +// file.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// SaveDIB() - Saves the specified dib in a file +// LoadDIB() - Loads a DIB from a file +// DestroyDIB() - Deletes DIB when finished using it +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample 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. +// +//******************************************************************* +#if (0) // ST - 5/8/2019 + +#include +#include +#include +#include +#include +#include +#include +#include "dibutil.h" +#include "dibapi.h" + +//#include "WolDebug.h" + +/* + * Dib Header Marker - used in writing DIBs to files + */ +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') + +/********************************************************************* + * + * Local Function Prototypes + * + *********************************************************************/ + + +HANDLE ReadDIBFile(int); +BOOL MyRead(int, LPSTR, DWORD); +BOOL SaveDIBFile(void); +BOOL WriteDIB(LPSTR, HANDLE); +DWORD PASCAL MyWrite(int, VOID FAR *, DWORD); + +/************************************************************************* + * + * LoadDIB() + * + * Loads the specified DIB from a file, allocates memory for it, + * and reads the disk file into the memory. + * + * + * Parameters: + * + * LPSTR lpFileName - specifies the file to load a DIB from + * + * Returns: A handle to a DIB, or NULL if unsuccessful. + * + * NOTE: The DIB API were not written to handle OS/2 DIBs; This + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * + *************************************************************************/ + + +HDIB FAR LoadDIB(LPSTR lpFileName) +{ + HDIB hDIB; + int hFile; + OFSTRUCT ofs; + + /* + * Set the cursor to a hourglass, in case the loading operation + * takes more than a sec, the user will know what's going on. + */ + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + if ((hFile = OpenFile(lpFileName, &ofs, OF_READ)) != -1) + { + hDIB = ReadDIBFile(hFile); + _lclose(hFile); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return hDIB; + } + else + { +// DIBError(ERR_FILENOTFOUND); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return NULL; + } +} + + +/************************************************************************* + * + * SaveDIB() + * + * Saves the specified DIB into the specified file name on disk. No + * error checking is done, so if the file already exists, it will be + * written over. + * + * Parameters: + * + * HDIB hDib - Handle to the dib to save + * + * LPSTR lpFileName - pointer to full pathname to save DIB under + * + * Return value: 0 if successful, or one of: + * ERR_INVALIDHANDLE + * ERR_OPEN + * ERR_LOCK + * + * History: + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will not save a file if it is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Taken from DIBVIEW (which was taken + * from SHOWDIB) + * 1/30/92 Mark Bader Fixed problem of writing too many + * bytes to the file + * 6/24/92 Mark Bader Added check for OS/2 DIB + * + *************************************************************************/ + + +WORD FAR SaveDIB(HDIB hDib, LPSTR lpFileName) +{ + BITMAPFILEHEADER bmfHdr; // Header for Bitmap file + LPBITMAPINFOHEADER lpBI; // Pointer to DIB info structure + int fh; // file handle for opened file + OFSTRUCT of; // OpenFile structure + DWORD dwDIBSize; + DWORD dwError; // Error return from MyWrite + + if (!hDib) + return ERR_INVALIDHANDLE; + fh = OpenFile(lpFileName, &of, OF_CREATE | OF_READWRITE); + if (fh == -1) + return ERR_OPEN; + + /* + * Get a pointer to the DIB memory, the first of which contains + * a BITMAPINFO structure + */ + lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib); + if (!lpBI) + return ERR_LOCK; + + // Check to see if we're dealing with an OS/2 DIB. If so, don't + // save it because our functions aren't written to deal with these + // DIBs. + + if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) + { + GlobalUnlock(hDib); + return ERR_NOT_DIB; + } + + /* + * Fill in the fields of the file header + */ + + /* Fill in file type (first 2 bytes must be "BM" for a bitmap) */ + bmfHdr.bfType = DIB_HEADER_MARKER; // "BM" + + // Calculating the size of the DIB is a bit tricky (if we want to + // do it right). The easiest way to do this is to call GlobalSize() + // on our global handle, but since the size of our global memory may have + // been padded a few bytes, we may end up writing out a few too + // many bytes to the file (which may cause problems with some apps, + // like HC 3.0). + // + // So, instead let's calculate the size manually. + // + // To do this, find size of header plus size of color table. Since the + // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains + // the size of the structure, let's use this. + + dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPSTR)lpBI); // Partial Calculation + + // Now calculate the size of the image + + if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) { + + // It's an RLE bitmap, we can't calculate size, so trust the + // biSizeImage field + + dwDIBSize += lpBI->biSizeImage; + } + else { + DWORD dwBmBitsSize; // Size of Bitmap Bits only + + // It's not RLE, so size is Width (DWORD aligned) * Height + + dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * lpBI->biHeight; + + dwDIBSize += dwBmBitsSize; + + // Now, since we have calculated the correct size, why don't we + // fill in the biSizeImage field (this will fix any .BMP files which + // have this field incorrect). + + lpBI->biSizeImage = dwBmBitsSize; + } + + + // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER) + + bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + + /* + * Now, calculate the offset the actual bitmap bits will be in + * the file -- It's the Bitmap file header plus the DIB header, + * plus the size of the color table. + */ + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + + PaletteSize((LPSTR)lpBI); + + /* Write the file header */ + _lwrite(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER)); + + /* + * Write the DIB header and the bits -- use local version of + * MyWrite, so we can write more than 32767 bytes of data + */ + dwError = MyWrite(fh, (LPSTR)lpBI, dwDIBSize); + GlobalUnlock(hDib); + _lclose(fh); + + if (dwError == 0) + return ERR_OPEN; // oops, something happened in the write + else + return 0; // Success code +} + + +/************************************************************************* + * + * DestroyDIB () + * + * Purpose: Frees memory associated with a DIB + * + * Returns: Nothing + * + * History: Date Author Reason + * 9/15/91 Mark Bader Created + * + *************************************************************************/ + + +WORD FAR DestroyDIB(HDIB hDib) +{ + GlobalFree(hDib); + return 0; +} + + +//************************************************************************ +// +// Auxiliary Functions which the above procedures use +// +//************************************************************************ + + +/************************************************************************* + * + * Function: ReadDIBFile (int) + * + * Purpose: Reads in the specified DIB file into a global chunk of + * memory. + * + * Returns: A handle to a dib (hDIB) if successful. + * NULL if an error occurs. + * + * Comments: BITMAPFILEHEADER is stripped off of the DIB. Everything + * from the end of the BITMAPFILEHEADER structure on is + * returned in the global memory handle. + * + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * 6/25/92 Mark Bader Added check for OS/2 DIB + * 7/21/92 Mark Bader Added code to deal with bfOffBits + * field in BITMAPFILEHEADER + * 9/11/92 Mark Bader Fixed Realloc Code to free original mem + * + *************************************************************************/ + +HANDLE ReadDIBFile(int hFile) +{ + BITMAPFILEHEADER bmfHeader; + DWORD dwBitsSize; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + /* + * get length of DIB in bytes for use when reading + */ + + dwBitsSize = filelength(hFile); + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + + hDIB = GlobalAlloc(GMEM_MOVEABLE, + (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); + + if (!hDIB) return NULL; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + if (!lpbi) + { + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file + + if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) + goto ErrExit; + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + goto ErrExit; + + // read the BITMAPINFOHEADER + + if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) + goto ErrExit; + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + goto ErrExit; + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) + * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + + nNumColors * sizeof(RGBQUAD) + + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + goto ErrExitNoUnlock; //MPB + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table + _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) + _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + + if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) + goto OKExit; + + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} + +/************************************************************************* + + Function: MyRead (int, LPSTR, DWORD) + + Purpose: Routine to read files greater than 64K in size. + + Returns: TRUE if successful. + FALSE if an error occurs. + + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + +*************************************************************************/ + + +BOOL MyRead(int hFile, LPSTR lpBuffer, DWORD dwSize) +{ + char huge *lpInBuf = (char huge *)lpBuffer; + int nBytes; + + /* + * Read in the data in 32767 byte chunks (or a smaller amount if it's + * the last chunk of data read) + */ + + while (dwSize) + { + nBytes = (int)(dwSize > (DWORD)32767 ? 32767 : LOWORD (dwSize)); + if (_lread(hFile, (LPSTR)lpInBuf, nBytes) != (WORD)nBytes) + return FALSE; + dwSize -= nBytes; + lpInBuf += nBytes; + } + return TRUE; +} + + +/**************************************************************************** + + FUNCTION : MyWrite(int fh, VOID FAR *pv, DWORD ul) + + PURPOSE : Writes data in steps of 32k till all the data is written. + Normal _lwrite uses a WORD as 3rd parameter, so it is + limited to 32767 bytes, but this procedure is not. + + RETURNS : 0 - If write did not proceed correctly. + number of bytes written otherwise. + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + + ****************************************************************************/ + + +DWORD PASCAL MyWrite(int iFileHandle, VOID FAR *lpBuffer, DWORD dwBytes) +{ + DWORD dwBytesTmp = dwBytes; // Save # of bytes for return value + BYTE huge *hpBuffer = (BYTE huge *)lpBuffer; // make a huge pointer to the data + + /* + * Write out the data in 32767 byte chunks. + */ + + while (dwBytes > 32767) + { + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)32767) != 32767) + return 0; + dwBytes -= 32767; + hpBuffer += 32767; + } + + /* Write out the last chunk (which is < 32767 bytes) */ + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)dwBytes) != (WORD)dwBytes) + return 0; + return dwBytesTmp; +} + +// ajw added +// Added to allow "loading" from a location in memory. +// A modification of ReadDIBFile(), above. +//*********************************************************************************************** +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ) +{ + BITMAPFILEHEADER bmfHeader; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + const unsigned char* const pDataStart = pData; + const unsigned char* pDataEnd = pData + dwBitsSize; // One char past end of "file". + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + +// debugprint( "LoadDIB_FromMemory, GlobalAlloc\n" ); + hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); +// debugprint( "hDIB from GlobalALloc is %i\n", hDIB ); + + if (!hDIB) + { +// debugprint( "LoadDIB_FromMemory error: failed alloc\n" ); + return NULL; + } + +// debugprint( "LoadDIB_FromMemory, lpbi Lock\n" ); + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); +// debugprint( "lpbi is %i\n", lpbi ); + if (!lpbi) + { +// debugprint( "LoadDIB_FromMemory error: failed lock\n" ); + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file +// if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPFILEHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPFILEHEADER %i bytes\n", sizeof( BITMAPFILEHEADER ) ); + memcpy( &bmfHeader, pData, sizeof( BITMAPFILEHEADER ) ); + pData += sizeof( BITMAPFILEHEADER ); + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + { +// debugprint( "LoadDIB_FromMemory error: no BM\n" ); + goto ErrExit; + } + + // read the BITMAPINFOHEADER +// if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPINFOHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size 2\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPINFOHEADER %i bytes\n", sizeof( BITMAPINFOHEADER ) ); + memcpy( lpbi, pData, sizeof( BITMAPINFOHEADER ) ); + pData += sizeof( BITMAPINFOHEADER ); + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + { +// debugprint( "LoadDIB_FromMemory error: lpbi->biSize bad\n" ); + goto ErrExit; + } + + if( lpbi->biCompression != BI_RGB ) + { +// debugprint( "LoadDIB_FromMemory error: Image is compressed\n" ); + goto ErrExit; + } + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + +// debugprint( "biSizeImage is %i. I would say it was %i, because the bpp is %i.\n", lpbi->biSizeImage, ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight, lpbi->biBitCount ); + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); +// debugprint( "LoadDIB_FromMemory, GlobalReAlloc: lpbi->biSize=%i, nNumColors=%i, lpbi->biSizeImage=%i\n", lpbi->biSize, nNumColors,lpbi->biSizeImage ); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + { +// debugprint( "LoadDIB_FromMemory error: realloc failed\n" ); + goto ErrExitNoUnlock; //MPB + } + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table +// _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); +// debugprint( "LoadDIB_FromMemory, memcpy color table %i colors, so %i bytes\n", nNumColors, nNumColors * sizeof(RGBQUAD) ); + memcpy( (LPSTR)(lpbi) + lpbi->biSize, pData, nNumColors * sizeof(RGBQUAD) ); + pData += nNumColors * sizeof(RGBQUAD); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) +// _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + pData = pDataStart + bmfHeader.bfOffBits; + +// debugprint( "bmfHeader.bfOffBits is %i\n", bmfHeader.bfOffBits ); + +// if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) +// goto OKExit; +// debugprint( "Checking that pData(%i) + biSizeImage(%i), which is %i, is equal to pDataEnd(%i)\n", +// pData, lpbi->biSizeImage, pData + lpbi->biSizeImage, pDataEnd ); +// if( pData + lpbi->biSizeImage != pDataEnd ) condition relaxed +// { +// debugprint( "LoadDIB_FromMemory error: bad size 3\n" ); +// goto ErrExit; +// } + +// debugprint( "LoadDIB_FromMemory, memcpy the bits, %i bytes. Image is w %i, h.%i\n", +// lpbi->biSizeImage, lpbi->biWidth, lpbi->biHeight ); +// debugprint( "Writing to lpbi (%i) + offBits (%i)\n", lpbi, offBits ); + + memcpy( (LPSTR)lpbi + offBits, pData, lpbi->biSizeImage ); +// pData += lpbi->biSizeImage; +// if( pData != pDataEnd ) // Should end up one byte past end of data. - condition relaxed +// debugprint( "LoadDIB_FromMemory: ERROR! Ended up at %i instead of %i\n", pData, pDataEnd ); + goto OKExit; + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); +// debugprint( "LoadDIB_FromMemory Error!\n" ); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} +#endif \ No newline at end of file diff --git a/REDALERT/DIBUTIL.CPP b/REDALERT/DIBUTIL.CPP new file mode 100644 index 000000000..023528509 --- /dev/null +++ b/REDALERT/DIBUTIL.CPP @@ -0,0 +1,1314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//********************************************************************** +// +// dibutil.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// CreateDIB() - Creates new DIB +// FindDIBBits() - Sets pointer to the DIB bits +// DIBWidth() - Gets the width of the DIB +// DIBHeight() - Gets the height of the DIB +// PaletteSize() - Calculates the buffer size required by a palette +// DIBNumColors() - Calculates number of colors in the DIB's color table +// CreateDIBPalette() - Creates a palette from a DIB +// DIBToBitmap() - Creates a bitmap from a DIB +// BitmapToDIB() - Creates a DIB from a bitmap +// PalEntriesOnDevice()- Gets the number of palette entries of a device +// GetSystemPalette() - Returns a handle to the current system palette +// AllocRoomForDIB() - Allocates memory for a DIB +// ChangeDIBFormat() - Changes a DIB's BPP and/or compression format +// ChangeBitmapFormat()- Changes a bitmap to a DIB with specified BPP and +// compression format +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample 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. +// +//********************************************************************** +#if (0) // ST - 5/8/2019 +/* header files */ +#include +#include +#include "dibapi.h" +#include "dibutil.h" +#include + + +/************************************************************************* + * + * CreateDIB() + * + * Parameters: + * + * DWORD dwWidth - Width for new bitmap, in pixels + * DWORD dwHeight - Height for new bitmap + * WORD wBitCount - Bit Count for new DIB (1, 4, 8, or 24) + * + * Return Value: + * + * HDIB - Handle to new DIB + * + * Description: + * + * This function allocates memory for and initializes a new DIB by + * filling in the BITMAPINFOHEADER, allocating memory for the color + * table, and allocating memory for the bitmap bits. As with all + * HDIBs, the header, colortable and bits are all in one contiguous + * memory block. This function is similar to the CreateBitmap() + * Windows API. + * + * The colortable and bitmap bits are left uninitialized (zeroed) in the + * returned HDIB. + * + * + * History: Date Author Reason + * 3/20/92 Mark Bader Created + * + ************************************************************************/ + +HDIB FAR CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount) +{ + BITMAPINFOHEADER bi; // bitmap header + LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HDIB hDIB; + DWORD dwBytesPerLine; // Number of bytes per scanline + + + // Make sure bits per pixel is valid + if (wBitCount <= 1) + wBitCount = 1; + else if (wBitCount <= 4) + wBitCount = 4; + else if (wBitCount <= 8) + wBitCount = 8; + else if (wBitCount <= 24) + wBitCount = 24; + else + wBitCount = 4; // set default value to 4 if parameter is bogus + + // initialize BITMAPINFOHEADER + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = dwWidth; // fill in width from parameter + bi.biHeight = dwHeight; // fill in height from parameter + bi.biPlanes = 1; // must be 1 + bi.biBitCount = wBitCount; // from parameter + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; // 0's here mean "default" + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + // calculate size of memory block required to store the DIB. This + // block should be big enough to hold the BITMAPINFOHEADER, the color + // table, and the bits + + dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth); + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + (dwBytesPerLine * dwHeight); + + // alloc memory block to store our bitmap + hDIB = GlobalAlloc(GHND, dwLen); + + // major bummer if we couldn't get memory block + if (!hDIB) + { + return NULL; + } + + // lock memory and get pointer to it + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // use our bitmap info structure to fill in first part of + // our DIB with the BITMAPINFOHEADER + *lpbi = bi; + + // Since we don't know what the colortable and bits should contain, + // just leave these blank. Unlock the DIB and return the HDIB. + + GlobalUnlock(hDIB); + + /* return handle to the DIB */ + return hDIB; +} + + + +/************************************************************************* + * + * FindDIBBits() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * LPSTR - pointer to the DIB bits + * + * Description: + * + * This function calculates the address of the DIB's bits and returns a + * pointer to the DIB bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +LPSTR FAR FindDIBBits(LPCSTR lpDIB) +{ + return (LPSTR)(lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB)); +} + + +/************************************************************************* + * + * DIBWidth() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - width of the DIB + * + * Description: + * + * This function gets the width of the DIB from the BITMAPINFOHEADER + * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * width field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBWidth(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether Win 3.0 and OS/2) */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB width if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biWidth; + else /* it is an OS/2 DIB, so return its width */ + return (DWORD)lpbmc->bcWidth; +} + + +/************************************************************************* + * + * DIBHeight() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - height of the DIB + * + * Description: + * + * This function gets the height of the DIB from the BITMAPINFOHEADER + * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * height field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBHeight(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether OS/2 or Win 3.0 */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB height if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biHeight; + else /* it is an OS/2 DIB, so return its height */ + return (DWORD)lpbmc->bcHeight; +} + + +/************************************************************************* + * + * PaletteSize() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - size of the color palette of the DIB + * + * Description: + * + * This function gets the size required to store the DIB's palette by + * multiplying the number of colors by the size of an RGBQUAD (for a + * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2- + * style DIB). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR PaletteSize(LPCSTR lpDIB) +{ + /* calculate the size required by the palette */ + if (IS_WIN30_DIB (lpDIB)) + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBQUAD)); + else + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBTRIPLE)); +} + + +/************************************************************************* + * + * DIBNumColors() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - number of colors in the color table + * + * Description: + * + * This function calculates the number of colors in the DIB's color table + * by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style + * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256, + * if 24, no colors in color table. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR DIBNumColors(LPCSTR lpDIB) +{ + WORD wBitCount; // DIB bit count + + /* If this is a Windows-style DIB, the number of colors in the + * color table can be less than the number of bits per pixel + * allows for (i.e. lpbi->biClrUsed can be set to some value). + * If this is the case, return the appropriate value. + */ + + if (IS_WIN30_DIB(lpDIB)) + { + DWORD dwClrUsed; + + dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed; + if (dwClrUsed) + return (WORD)dwClrUsed; + } + + /* Calculate the number of colors in the color table based on + * the number of bits per pixel for the DIB. + */ + if (IS_WIN30_DIB(lpDIB)) + wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; + else + wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; + + /* return number of colors based on bits per pixel */ + switch (wBitCount) + { + case 1: + return 2; + + case 4: + return 16; + + case 8: + return 256; + + default: + return 0; + } +} + + +/************************************************************************* + * + * CreateDIBPalette() + * + * Parameter: + * + * HDIB hDIB - specifies the DIB + * + * Return Value: + * + * HPALETTE - specifies the palette + * + * Description: + * + * This function creates a palette from a DIB by allocating memory for the + * logical palette, reading and storing the colors from the DIB's color table + * into the logical palette, creating a palette from this logical palette, + * and then returning the palette's handle. This allows the DIB to be + * displayed using the best possible colors (important for DIBs with 256 or + * more colors). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +HPALETTE FAR CreateDIBPalette(HDIB hDIB) +{ + LPLOGPALETTE lpPal; // pointer to a logical palette + HANDLE hLogPal; // handle to a logical palette + HPALETTE hPal = NULL; // handle to a palette + int i, wNumColors; // loop index, number of colors in color table + LPSTR lpbi; // pointer to packed-DIB + LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0) + LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (OS/2) + BOOL bWinStyleDIB; // flag which signifies whether this is a Win3.0 DIB + + /* if handle to DIB is invalid, return NULL */ + + if (!hDIB) + return NULL; + + /* lock DIB memory block and get a pointer to it */ + lpbi = (LPSTR)GlobalLock(hDIB); + + /* get pointer to BITMAPINFO (Win 3.0) */ + lpbmi = (LPBITMAPINFO)lpbi; + + /* get pointer to BITMAPCOREINFO (OS/2 1.x) */ + lpbmc = (LPBITMAPCOREINFO)lpbi; + + /* get the number of colors in the DIB */ + wNumColors = DIBNumColors(lpbi); + + /* is this a Win 3.0 DIB? */ + bWinStyleDIB = IS_WIN30_DIB(lpbi); + if (wNumColors) + { + /* allocate memory block for logical palette */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * + wNumColors); + + /* if not enough memory, clean up and return NULL */ + if (!hLogPal) + { + GlobalUnlock(hDIB); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set version and number of palette entries */ + lpPal->palVersion = PALVERSION; + lpPal->palNumEntries = (WORD)wNumColors; + + /* store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB) + * into palette + */ + for (i = 0; i < wNumColors; i++) + { + if (bWinStyleDIB) + { + lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; + lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; + lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + else + { + lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed; + lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen; + lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + } + + /* create the palette and get handle to it */ + hPal = CreatePalette(lpPal); + + /* if error getting handle to palette, clean up and return NULL */ + if (!hPal) + { + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + return NULL; + } + } + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + GlobalUnlock(hDIB); + + /* return handle to DIB's palette */ + return hPal; +} + + +/************************************************************************* + * + * DIBToBitmap() + * + * Parameters: + * + * HDIB hDIB - specifies the DIB to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HBITMAP - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a bitmap from a DIB using the specified palette. + * If no palette is specified, default is used. + * + * NOTE: + * + * The bitmap returned from this funciton is always a bitmap compatible + * with the screen (e.g. same bits/pixel and color planes) rather than + * a bitmap with the same attributes as the DIB. This behavior is by + * design, and occurs because this function calls CreateDIBitmap to + * do its work, and CreateDIBitmap always creates a bitmap compatible + * with the hDC parameter passed in (because it in turn calls + * CreateCompatibleBitmap). + * + * So for instance, if your DIB is a monochrome DIB and you call this + * function, you will not get back a monochrome HBITMAP -- you will + * get an HBITMAP compatible with the screen DC, but with only 2 + * colors used in the bitmap. + * + * If your application requires a monochrome HBITMAP returned for a + * monochrome DIB, use the function SetDIBits(). + * + * Also, the DIBpassed in to the function is not destroyed on exit. This + * must be done later, once it is no longer needed. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 3/27/92 Mark Bader Added comments about resulting + * bitmap format + * + ************************************************************************/ + + +HBITMAP FAR DIBToBitmap(HDIB hDIB, HPALETTE hPal) +{ + LPSTR lpDIBHdr, lpDIBBits; // pointer to DIB header, pointer to DIB bits + HBITMAP hBitmap; // handle to device-dependent bitmap + HDC hDC; // handle to DC + HPALETTE hOldPal = NULL; // handle to a palette + + /* if invalid handle, return NULL */ + + if (!hDIB) + return NULL; + + /* lock memory block and get a pointer to it */ + lpDIBHdr = (LPSTR)GlobalLock(hDIB); + + /* get a pointer to the DIB bits */ + lpDIBBits = FindDIBBits(lpDIBHdr); + + /* get a DC */ + hDC = GetDC(NULL); + if (!hDC) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + return NULL; + } + + /* select and realize palette */ + if (hPal) + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* create bitmap from DIB info. and bits */ + hBitmap = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT, + lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS); + + /* restore previous palette */ + if (hOldPal) + SelectPalette(hDC, hOldPal, FALSE); + + /* clean up */ + ReleaseDC(NULL, hDC); + GlobalUnlock(hDIB); + + /* return handle to the bitmap */ + return hBitmap; +} + + +/************************************************************************* + * + * BitmapToDIB() + * + * Parameters: + * + * HBITMAP hBitmap - specifies the bitmap to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HDIB - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a DIB from a bitmap using the specified palette. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/10/91 Patrick Schreiber Added bits per pixel validation + * and check GetObject return value + * + ************************************************************************/ + + +HDIB FAR BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal) +{ + BITMAP bm; // bitmap structure + BITMAPINFOHEADER bi; // bitmap header + BITMAPINFOHEADER FAR *lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HANDLE hDIB, h; // handle to DIB, temp handle + HDC hDC; // handle to DC + WORD biBits; // bits per pixel + + /* check if bitmap handle is valid */ + + if (!hBitmap) + return NULL; + + /* fill in BITMAP structure, return NULL if it didn't work */ + if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm)) + return NULL; + + /* if no palette is specified, use default palette */ + if (hPal == NULL) + hPal = GetStockObject(DEFAULT_PALETTE); + + /* calculate bits per pixel */ + biBits = (WORD)( bm.bmPlanes * bm.bmBitsPixel ); + + /* make sure bits per pixel is valid */ + if (biBits <= 1) + biBits = 1; + else if (biBits <= 4) + biBits = 4; + else if (biBits <= 8) + biBits = 8; + else /* if greater than 8-bit, force to 24-bit */ + biBits = 24; + + /* initialize BITMAPINFOHEADER */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = bm.bmWidth; + bi.biHeight = bm.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = biBits; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* calculate size of memory block required to store BITMAPINFO */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi); + + /* get a DC */ + hDC = GetDC(NULL); + + /* select and realize our palette */ + hPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* alloc memory block to store our bitmap */ + hDIB = GlobalAlloc(GHND, dwLen); + + /* if we couldn't get memory block */ + if (!hDIB) + { + /* clean up and return NULL */ + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* use our bitmap info. to fill BITMAPINFOHEADER */ + *lpbi = bi; + + /* call GetDIBits with a NULL lpBits param, so it will calculate the + * biSizeImage field for us + */ + GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS); + + /* get the info. returned by GetDIBits and unlock memory block */ + bi = *lpbi; + GlobalUnlock(hDIB); + + /* if the driver did not fill in the biSizeImage field, make one up */ + if (bi.biSizeImage == 0) + bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight; + + /* realloc the buffer big enough to hold all the bits */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage; + h = GlobalReAlloc(hDIB, dwLen, 0); + if (h) + hDIB = h; + else + { + /* clean up and return NULL */ + GlobalFree(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* call GetDIBits with a NON-NULL lpBits param, and actualy get the + * bits this time + */ + if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi + ->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS) == 0) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + bi = *lpbi; + + /* clean up */ + GlobalUnlock(hDIB); + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + /* return handle to the DIB */ + return hDIB; +} + + +/************************************************************************* + * + * PalEntriesOnDevice() + * + * Parameter: + * + * HDC hDC - device context + * + * Return Value: + * + * int - number of palette entries on device + * + * Description: + * + * This function gets the number of palette entries on the specified device + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +int FAR PalEntriesOnDevice(HDC hDC) +{ + int nColors; // number of colors + + /* Find out the number of palette entries on this + * device. + */ + + nColors = GetDeviceCaps(hDC, SIZEPALETTE); + + /* For non-palette devices, we'll use the # of system + * colors for our palette size. + */ + if (!nColors) + nColors = GetDeviceCaps(hDC, NUMCOLORS); + assert(nColors); + return nColors; +} + + +/************************************************************************* + * + * GetSystemPalette() + * + * Parameters: + * + * None + * + * Return Value: + * + * HPALETTE - handle to a copy of the current system palette + * + * Description: + * + * This function returns a handle to a palette which represents the system + * palette. The system RGB values are copied into our logical palette using + * the GetSystemPaletteEntries function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/20/91 Mark Bader Added GetSystemPaletteEntries call + * + ************************************************************************/ + + +HPALETTE FAR GetSystemPalette(void) +{ + HDC hDC; // handle to a DC + static HPALETTE hPal = NULL; // handle to a palette + HANDLE hLogPal; // handle to a logical palette + LPLOGPALETTE lpLogPal; // pointer to a logical palette + int nColors; // number of colors + + /* Find out how many palette entries we want. */ + + hDC = GetDC(NULL); + if (!hDC) + return NULL; + nColors = PalEntriesOnDevice(hDC); // Number of palette entries + + /* Allocate room for the palette and lock it. */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * sizeof( + PALETTEENTRY)); + + /* if we didn't get a logical palette, return NULL */ + if (!hLogPal) + return NULL; + + /* get a pointer to the logical palette */ + lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set some important fields */ + lpLogPal->palVersion = (WORD)PALVERSION; + lpLogPal->palNumEntries = (WORD)nColors; + + /* Copy the current system palette into our logical palette */ + + GetSystemPaletteEntries(hDC, 0, nColors, + (LPPALETTEENTRY)(lpLogPal->palPalEntry)); + + /* Go ahead and create the palette. Once it's created, + * we no longer need the LOGPALETTE, so free it. + */ + + hPal = CreatePalette(lpLogPal); + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + ReleaseDC(NULL, hDC); + + return hPal; +} + + +/************************************************************************* + * + * AllocRoomForDIB() + * + * Parameters: + * + * BITMAPINFOHEADER - bitmap info header stucture + * + * HBITMAP - handle to the bitmap + * + * Return Value: + * + * HDIB - handle to memory block + * + * Description: + * + * This routine takes a BITMAPINOHEADER, and returns a handle to global + * memory which can contain a DIB with that header. It also initializes + * the header portion of the global memory. GetDIBits() is used to determine + * the amount of room for the DIB's bits. The total amount of memory + * needed = sizeof(BITMAPINFOHEADER) + size of color table + size of bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/11/91 Patrick Schreiber Added header and some comments + * + ************************************************************************/ + +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap) +{ + DWORD dwLen; + HANDLE hDIB; + HDC hDC; + LPBITMAPINFOHEADER lpbi; + HANDLE hTemp; + + /* Figure out the size needed to hold the BITMAPINFO structure + * (which includes the BITMAPINFOHEADER and the color table). + */ + + dwLen = bi.biSize + PaletteSize((LPSTR) &bi); + hDIB = GlobalAlloc(GHND,dwLen); + + /* Check that DIB handle is valid */ + if (!hDIB) + return NULL; + + /* Set up the BITMAPINFOHEADER in the newly allocated global memory, + * then call GetDIBits() with lpBits = NULL to have it fill in the + * biSizeImage field for us. + */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + *lpbi = bi; + + hDC = GetDC(NULL); + GetDIBits(hDC, hBitmap, 0, (WORD) bi.biHeight, + NULL, (LPBITMAPINFO) lpbi, DIB_RGB_COLORS); + ReleaseDC(NULL, hDC); + + /* If the driver did not fill in the biSizeImage field, + * fill it in -- NOTE: this is a bug in the driver! + */ + if (lpbi->biSizeImage == 0) + lpbi->biSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) * + lpbi->biHeight; + + /* Get the size of the memory block we need */ + dwLen = lpbi->biSize + PaletteSize((LPSTR) &bi) + lpbi->biSizeImage; + + /* Unlock the memory block */ + GlobalUnlock(hDIB); + + /* ReAlloc the buffer big enough to hold all the bits */ + hTemp = GlobalReAlloc(hDIB,dwLen,0); + if (hTemp) + return hTemp; + else + { + /* Else free memory block and return failure */ + GlobalFree(hDIB); + return NULL; + } +} + + +/************************************************************************* + * + * ChangeDIBFormat() + * + * Parameter: + * + * HDIB - handle to packed-DIB in memory + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert the bits per pixel and/or the compression + * format of the specified DIB. Note: If the conversion was unsuccessful, + * we return NULL. The original DIB is left alone. Don't use code like the + * following: + * + * hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4); + * + * The conversion will fail, but hMyDIB will now be NULL and the original + * DIB will now hang around in memory. We could have returned the old + * DIB, but we wanted to allow the programmer to check whether this + * conversion succeeded or failed. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * + ************************************************************************/ + +HDIB FAR ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression) +{ + HDC hDC; // Handle to DC + HBITMAP hBitmap; // Handle to bitmap + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap info + HDIB hNewDIB = NULL; // Handle to new DIB + HPALETTE hPal, hOldPal; // Handle to palette, prev pal + WORD DIBBPP, NewBPP; // DIB bits per pixel, new bpp + DWORD DIBComp, NewComp;// DIB compression, new compression + + /* Check for a valid DIB handle */ + if (!hDIB) + return NULL; + + /* Get the old DIB's bits per pixel and compression format */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + DIBBPP = ((LPBITMAPINFOHEADER)lpbi)->biBitCount; + DIBComp = ((LPBITMAPINFOHEADER)lpbi)->biCompression; + GlobalUnlock(hDIB); + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure */ + if (wBitCount == 0) + { + NewBPP = DIBBPP; + if ((dwCompression == BI_RLE4 && NewBPP == 4) || + (dwCompression == BI_RLE8 && NewBPP == 8) || + (dwCompression == BI_RGB)) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Save the old DIB's palette */ + hPal = CreateDIBPalette(hDIB); + if (!hPal) + return NULL; + + /* Convert old DIB to a bitmap */ + hBitmap = DIBToBitmap(hDIB, hPal); + if (!hBitmap) + { + DeleteObject(hPal); + return NULL; + } + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* Get a DC and select/realize our palette in it */ + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + if (hNewDIB) + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + + DeleteObject(hBitmap); + DeleteObject(hPal); + + return hNewDIB; +} + + +/************************************************************************* + * + * ChangeBitmapFormat() + * + * Parameter: + * + * HBITMAP - handle to a bitmap + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * HPALETTE - handle to palette + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert a bitmap to the specified bits per pixel + * and compression format. The bitmap and it's palette will remain + * after calling this function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * 12/11/91 Patrick Schreiber Destroy old DIB if conversion was + * successful. + * 12/16/91 Patrick Schreiber Modified from converting DIB to new + * DIB to bitmap to new DIB. Added palette + * parameter. + * + ************************************************************************/ + +HDIB FAR ChangeBitmapFormat(HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal) +{ + HDC hDC; // Screen DC + HDIB hNewDIB=NULL; // Handle to new DIB + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info. header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap header + HPALETTE hOldPal=NULL; // Handle to palette + WORD NewBPP; // New bits per pixel + DWORD NewComp; // New compression format + + /* Check for a valid bitmap handle */ + if (!hBitmap) + return NULL; + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure + */ + if (wBitCount == 0) + { + NewComp = dwCompression; + if (NewComp == BI_RLE4) + NewBPP = 4; + else if (NewComp == BI_RLE8) + NewBPP = 8; + else /* Not enough info */ + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* If we have a palette, get a DC and select/realize it */ + if (hPal) + { + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + } + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + if (hOldPal) + { + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + } + + if (hNewDIB) + { + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + } + + return hNewDIB; +} +#endif \ No newline at end of file diff --git a/REDALERT/DIBUTIL.H b/REDALERT/DIBUTIL.H new file mode 100644 index 000000000..9b3dc60d7 --- /dev/null +++ b/REDALERT/DIBUTIL.H @@ -0,0 +1,38 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + * dibutil.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved. + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * AllocRoomForDIB() - Allocates memory for a DIB + * + */ + + +/* DIB constants */ +#define PALVERSION 0x300 + +/* DIB macros */ +#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER)) +#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left) +#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top) + +/* function prototypes */ +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap); diff --git a/REDALERT/DISPLAY.CPP b/REDALERT/DISPLAY.CPP new file mode 100644 index 000000000..c865e5aea --- /dev/null +++ b/REDALERT/DISPLAY.CPP @@ -0,0 +1,5138 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DISPLAY.CPP 3 3/09/97 8:04p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * DisplayClass::DisplayClass -- Default constructor for display class. * + * DisplayClass::Draw_It -- Draws the tactical map. * + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * DisplayClass::Init_Clear -- Clears the display to a known state. * + * DisplayClass::Init_IO -- Creates the map's button list * + * DisplayClass::Init_Theater -- Theater-specific initialization * + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * DisplayClass::Next_Object -- Searches for next object on display. * + * DisplayClass::One_Time -- Performs any special one time initializations. * + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/* +** These layer control elements are used to group the displayable objects +** so that proper overlap can be obtained. +*/ +LayerClass DisplayClass::Layer[LAYER_COUNT]; + +/* +** Fading tables +*/ +unsigned char DisplayClass::FadingBrighten[256]; +unsigned char DisplayClass::FadingShade[256]; +unsigned char DisplayClass::FadingWayDark[256]; +unsigned char DisplayClass::FadingLight[256]; +unsigned char DisplayClass::FadingGreen[256]; +unsigned char DisplayClass::FadingYellow[256]; +unsigned char DisplayClass::FadingRed[256]; +unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; +unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; +unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; +void const * DisplayClass::TransIconset; +unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::SpecialGhost[2*256]; + +void const * DisplayClass::ShadowShapes; +unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + +/* +** Bit array of cell redraw flags +*/ +BooleanVectorClass DisplayClass::CellRedraw; + +/* +** The main button that intercepts user input to the map +*/ +DisplayClass::TacticalClass DisplayClass::TacButton; + +// +// We need a way to bypass visible view checks when we are running in the context of GlyphX without using the +// internal C&C renderer. We shouldn't know or care what the user is actually looking at +// ST - 4/17/2019 9:01AM +// +bool DisplayClass::IgnoreViewConstraints = false; + + +static int const TEX_X = 0; +static int const TEX_Y = 6; +static int const TEX_W = 14; + +//Added for getting the input for special character keys from the client +// - 6/26/2019 JAS +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + + +/*********************************************************************************************** + * DisplayClass::DisplayClass -- Default constructor for display class. * + * * + * This constructor for the display class just initializes some of the display settings. * + * Most settings are initialized with the correct values at the time that the Init function * + * is called. There are some cases where default values are wise and this routine fills * + * those particular ones in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + *=============================================================================================*/ +DisplayClass::DisplayClass(void) : + TacticalCoord(0), + TacLeptonWidth(0), + TacLeptonHeight(0), + ZoneCell(0), + ZoneOffset(0), + CursorSize(0), + ProximityCheck(false), + PendingObjectPtr(0), + PendingObject(0), + PendingHouse(HOUSE_NONE), + TacPixelX(0), + TacPixelY(0), + DesiredTacticalCoord(0), + IsToRedraw(true), + IsRepairMode(false), + IsSellMode(false), + IsTargettingMode(SPC_NONE), + IsRubberBand(false), + IsTentative(false), + IsShadowPresent(false), + BandX(0), + BandY(0), + NewX(0), + NewY(0) +{ + ShadowShapes = 0; + TransIconset = 0; + + Set_View_Dimensions(0, 8, 320/CELL_PIXEL_W, 200/CELL_PIXEL_H); +} + + +/*********************************************************************************************** + * DisplayClass::One_Time -- Performs any special one time initializations. * + * * + * This routine is called from the game initialization process. It is to perform any one * + * time initializations necessary for the map display system. It allocates the staging * + * buffer needed for the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called ONCE and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Handles layer system now. * + * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * + *=============================================================================================*/ +void DisplayClass::One_Time(void) +{ + MapClass::One_Time(); + + /* + ** Init the CellRedraw bit array. Do not do this in the constructor, since the + ** BooleanVector may not have been constructed yet. + */ + CellRedraw.Resize(MAP_CELL_TOTAL); + + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].One_Time(); + } + + /* + ** Load the generic transparent icon set. + */ + TransIconset = MFCD::Retrieve("TRANS.ICN"); + + #ifndef NDEBUG + RawFileClass file("SHADOW.SHP"); + if (file.Is_Available()) { + ShadowShapes = Load_Alloc_Data(file); + } else { + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + } + #else + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + #endif + + //PG Set_View_Dimensions(0, 8 * RESFACTOR); + Set_View_Dimensions(0, 0); +} + + +/*********************************************************************************************** + * DisplayClass::Init_Clear -- clears the display to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Clear(void) +{ + MapClass::Init_Clear(); + + /* + ** Clear any object being placed + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + CursorSize = 0; + IsTargettingMode = SPC_NONE; + IsRepairMode = false; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; + + /* + ** Empty all the display's layers + */ + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].Init(); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_IO -- clears & re-builds the map's button list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_IO(void) +{ + MapClass::Init_IO(); + /* + ** Re-attach our buttons to the main map button list, only in non-edit mode. + */ + if (!Debug_Map) { + TacButton.Zap(); + Add_A_Button(TacButton); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * + * * + * INPUT: * + * theater new theater * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + * 05/07/1996 JLB : Added translucent tables. * + *=============================================================================================*/ +void DisplayClass::Init_Theater(TheaterType theater) +{ + char fullname[16]; + static TLucentType const MouseCols[4] = { + {BLACK, BLACK, 110, 0}, + {WHITE, WHITE, 110, 0}, + {LTGREY, LTGREY, 110, 0}, + {DKGREY, DKGREY, 110, 0} + }; + static TLucentType const MagicCols[MAGIC_COL_COUNT] = { + {32,32,110,0}, + {33,33,110,0}, + {34,34,110,0}, + {35,35,110,0}, + {36,36,110,0}, + {37,37,110,0}, + {38,38,110,0}, + {39,39,110,0}, + {BLACK, BLACK, 200, 0}, + {WHITE, BLACK, 40, 0}, + {LTGREY, BLACK, 80, 0}, + {DKGREY, BLACK, 140, 0}, + {LTGREEN, BLACK,130,0} + }; + static TLucentType const WhiteCols[1] = { + {1, WHITE, 80, 0} + }; + static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { + {WHITE+1, BLACK,130,0}, + {WHITE, BLACK,170,0}, + {LTGRAY, BLACK,250,0}, + {DKGRAY, BLACK,250,0} + }; + static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,130,0} + }; + static TLucentType const UShadowColsAir[USHADOW_COL_COUNT] = { + {LTGREEN, WHITE,0,0} + }; + static TLucentType const UShadowColsSnow[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,75,0} + }; + + /* + ** Invoke parent's init routine. + */ + MapClass::Init_Theater(theater); + + /* + ** Save the new theater value + */ + Scen.Theater = theater; + + /* + ** Unload old mixfiles, and cache the new ones + */ + sprintf(fullname, "%s.MIX", Theaters[theater].Root); + +#ifndef WIN32 +LastTheater = THEATER_NONE; +#endif + + if (Scen.Theater != LastTheater) { + if (TheaterData != NULL) { + delete TheaterData; + } + TheaterData = new MFCD(fullname, &FastKey); + assert(TheaterData != NULL); + + bool theaterload = TheaterData->Cache(TheaterBuffer); + assert(theaterload); +// LastTheater = Scen.Theater; + } + + /* + ** Load the custom palette associated with this theater. + ** The fading palettes will have to be generated as well. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + PaletteClass const * ptr = (PaletteClass *)MFCD::Retrieve(fullname); + + GamePalette = * ptr; + + OriginalPalette = GamePalette; + + Build_Fading_Table(GamePalette.Get_Data(), FadingGreen, GREEN, 110); + + Build_Fading_Table(GamePalette.Get_Data(), FadingYellow, YELLOW, 140); + + Build_Fading_Table(GamePalette.Get_Data(), FadingRed, RED, 140); + + Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); + + Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); + + Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); + + Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); + + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsAir[0], USHADOW_COL_COUNT, UnitShadowAir); + memcpy(&UnitShadowAir[256], ColorRemaps[PCOLOR_GOLD].RemapTable, sizeof(ColorRemaps[PCOLOR_GOLD].RemapTable)); + if (theater == THEATER_SNOW) { + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsSnow[0], USHADOW_COL_COUNT, UnitShadow); + } else { + Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); + } + + if (theater == THEATER_SNOW) { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 75); + } else { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 130); + } + + Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); + + /* + ** Create the shadow color used by aircraft. + */ + Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); + for (int index = 0; index < 256; index++) { + SpecialGhost[index] = 0; + } + + Make_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); + + Make_Fading_Table(GamePalette, FadingWayDark, DKGRAY, 192); + + /* + ** Adjust the palette according to the visual control option settings. + */ + Options.Fixup_Palette(); +} + + +/*********************************************************************************************** + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * * + * This routine is used to create an overlap list that specifies all the cells that are * + * covered by the specified text string. This overlap list is used to handle map refresh * + * logic. * + * * + * INPUT: text -- Pointer to the text that would appear on the map and must have an * + * overlap list generated. * + * * + * x,y -- The coordinates that the text would appear (upper left corner). * + * * + * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * + * if were displayed at the coordinates specified. The list is actually a series of * + * offsets from the display's upper left corner cell number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 12/07/1994 JLB : Sidebar fixup. * + * 08/13/1995 JLB : Optimized for variable sized help text. * + *=============================================================================================*/ +short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y) const +{ + static short _list[60]; + int count = ARRAY_SIZE(_list); + + if (text != NULL) { + short * ptr = &_list[0]; + int len = String_Pixel_Width(text)+CELL_PIXEL_W; + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); + + /* + ** If the help text would spill into the sidebar, then flag this fact, but + ** shorten the apparent length so that the icon list calculation will + ** function correctly. + */ + if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { + len = right-x; + *ptr++ = REFRESH_SIDEBAR; + count--; + } + + /* + ** Build the list of overlap cell offset values according to the text + ** coordinate and the length. + */ + if (x <= right) { + CELL ul = Click_Cell_Calc(x, y-1); + CELL lr = Click_Cell_Calc(x+len-1, Bound(y+24, TacPixelY, TacPixelY+Lepton_To_Pixel(TacLeptonHeight) - 1)); + + if (ul == -1) ul = Click_Cell_Calc(x, y); + + if (ul != -1 && lr != -1) { + for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { + for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { + *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); + count--; + if (count < 2) break; + } + if (count < 2) break; + } + } + } + + *ptr = REFRESH_EOL; + } + return(_list); +} + + +/*********************************************************************************************** + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * * + * Use this routine to set the tactical map screen coordinates and dimensions. This routine * + * is typically used when the screen size or position changes as a result of the sidebar * + * changing position or appearance. * + * * + * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * + * corner. * + * * + * width -- The width of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * height-- The height of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 06/27/1995 JLB : Adjusts tactical map position if necessary. * + *=============================================================================================*/ +void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) +{ + if (width == -1) { + TacLeptonWidth = Pixel_To_Lepton(SeenBuff.Get_Width()-x); + } else { + TacLeptonWidth = width * CELL_LEPTON_W; + } + + // ST - 3/1/2019 12:05PM + // Made the below code more consistent with the width calculation. This is needed if we aren't going to draw the tabs at the top of the screen + // + if (height == -1) { + TacLeptonHeight = Pixel_To_Lepton(SeenBuff.Get_Height() - y); + //height = (SeenBuff.Get_Height()-y) / CELL_PIXEL_H; + } + else { + TacLeptonHeight = height * CELL_LEPTON_H; + } + //TacLeptonHeight = height * CELL_LEPTON_H; + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = 0;// Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = 0;// Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight); + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * * + * This routine is used to set up the terrain cursor according to the size of the object * + * that is to be placed down. The terrain cursor looks like an arbitrary collection of * + * hatched square overlays. Typical use is when placing buildings. * + * * + * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * + * be marked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1994 JLB : Created. * + * 06/26/1995 JLB : Puts placement cursor into static buffer. * + *=============================================================================================*/ +void DisplayClass::Set_Cursor_Shape(short const * list) +{ + if (CursorSize) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + ZoneOffset = 0; + + if (list) { + int w,h; + static short _list[50]; + + memcpy(_list, list, sizeof(_list)); + CursorSize = _list; + Get_Occupy_Dimensions (w, h, CursorSize); + ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); + Cursor_Mark(ZoneCell+ZoneOffset, true); + } else { + CursorSize = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * house -- The house to base the proximity check upon. Typically this is the * + * player's house, but in multiplay, the computer needs to check for * + * proximity as well. * + * * + * list -- Pointer to the building's offset list. * + * * + * trycell -- The cell to base the offset list on. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + * 10/11/1994 BWG : Added IsProximate check for ore refineries * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const +{ + short const * ptr; + int retval = -1; + bool noradar = false; + //bool nomapped = false; // Not used. ST - 8/6/2019 10:51AM + bool shipyard = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = false; + } + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (list == NULL || trycell == 0) { + return(true); + } + + if (object == NULL || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + BuildingTypeClass const * building = (BuildingTypeClass const *)object; + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = list; +// ptr = CursorSize; + CELL cell = trycell; +// CELL cell = ZoneCell; + if (building->Adjacent == 1) { + while (*ptr != REFRESH_EOL && (retval == -1) ) { + cell = trycell + *ptr++; +// cell = ZoneCell + ZoneOffset + *ptr++; + + if (!In_Radar(cell)) { + retval = false; + noradar = true; + break; + } + + for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(newcell)) continue; + + // Code has no effect. ST - 8/6/2019 10:51AM + //if (!(*this)[newcell].IsMapped) { + // nomapped = true; + //} + BuildingClass * base = (*this)[newcell].Cell_Building(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + //BG: Modified so only walls can be placed next to walls - buildings can't. + //JLB: Except for bibs, in which case buildings can be placed next to these. + if (building->IsWall || + ((*this)[newcell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newcell].Smudge).IsBib)) { + + if ((*this)[newcell].Owner == house) { + retval = true; + break; + } + } + + // we've found a building... + if (base != NULL && base->House->Class->House == house && base->Class->IsBase) { + retval = true; + break; + } + + /* BG: modifications to allow buildings one cell away from other buildings. + ** This is done by scanning each cell that fails the check (hence getting + ** to this point) and looking at the n/s/e/w adjacent cells to see if they + ** have buildings in them. If they do, and they match us, then succeed. + */ + if (retval != -1) break; + + for (FacingType newface = FACING_N; newface < FACING_COUNT; newface++) { + CELL newercell = Adjacent_Cell(newcell, newface); + + if (building->IsWall || + ((*this)[newercell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newercell].Smudge).IsBib)) { + + if ((*this)[newercell].Owner == house) { + retval = true; + break; + } + } + + TechnoClass * newbase = (*this)[newercell].Cell_Techno(); + + // we've found a building... + if (newbase != NULL && newbase->What_Am_I() == RTTI_BUILDING && newbase->House->Class->House == house && ((BuildingClass const *)newbase)->Class->IsBase) { + retval = true; + break; + } + } + if (retval != -1) break; + } + } + } + + if (retval == -1) retval = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = (retval != false); + } + + /* + ** If this object has special dispensation to be placed further than one cell from + ** other regular buildings, then check for this case now. Only bother to check if + ** it hasn't already been given permission to be placed down. + */ + if (!retval && !noradar && object->What_Am_I() == RTTI_BUILDINGTYPE) { + + // For land mines, let's make it check proximity within 10 squares + if (building->Adjacent > 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj != NULL && !obj->IsInLimbo && obj->House->Class->House == house && obj->Class->IsBase) { + int centdist = ::Distance(obj->Center_Coord(), Cell_Coord(cell)); + centdist /= CELL_LEPTON_W; + centdist -= (obj->Class->Width() + obj->Class->Height()) / 2; + if (centdist <= building->Adjacent) { + retval = true; + break; + } + } + } + } + } + + return((bool)retval); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * * + * This routine controls the location, display, and animation of the * + * tactical map cursor. * + * * + * INPUT: pos -- Position to move the cursor do. If -1 is passed then * + * the cursor will just be hidden. If the position * + * passed is the same as the last position passed in, * + * then animation could occur (based on timers). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + * 06/08/1994 JLB : If position is -1, then follow mouse. * + * 02/28/1995 JLB : Forces placement cursor to fit on map. * + *=============================================================================================*/ +CELL DisplayClass::Set_Cursor_Pos(CELL pos) +{ + CELL prevpos; // Last position of cursor (for jump-back reasons). + + /* + ** Follow the mouse position if no cell number is provided. + */ + if (pos == -1) { + pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + } + + if (CursorSize == NULL) { + prevpos = ZoneCell; + ZoneCell = pos; + return(prevpos); + } + + /* + ** Adjusts the position so that the placement cursor is never part way off the + ** tactical map. + */ + int w,h; + Get_Occupy_Dimensions(w, h, CursorSize); + + int x = Cell_X(pos + ZoneOffset); + int y = Cell_Y(pos + ZoneOffset); + + if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); + if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); + if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; + if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) y = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; + pos = XY_Cell(x, y) - ZoneOffset; + + /* + ** This checks to see if NO animation or drawing is to occur and, if so, + ** exits. + */ + if (pos == ZoneCell) return(pos); + + prevpos = ZoneCell; + + /* + ** If the cursor is visible, then handle the graphic update. + ** Otherwise, just update the global position of the cursor. + */ + if (CursorSize != NULL) { + + /* + ** Erase the old cursor (if it exists) AND the cursor is moving. + */ + if (pos != ZoneCell && ZoneCell != -1) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + /* + ** Render the cursor (could just be animation). + */ + if (pos != -1) { + Cursor_Mark(pos+ZoneOffset, true); + } + } + ZoneCell = pos; + ProximityCheck = Passes_Proximity_Check(PendingObject, PendingHouse, CursorSize, ZoneCell+ZoneOffset); + + return(prevpos); +} + + +/*********************************************************************************************** + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * * + * INPUT: * + * w ptr to fill in with height * + * h ptr to fill in with width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/31/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const * list) const +{ + int min_x = MAP_CELL_W; + int max_x = -MAP_CELL_W; + int min_y = MAP_CELL_H; + int max_y = -MAP_CELL_H; + int x,y; + + w = 0; + h = 0; + + if (!list) { + /* + ** Loop through all cell offsets, accumulating max & min x- & y-coords + */ + while (*list != REFRESH_EOL) { + /* + ** Compute x & y coords of the current cell offset. We can't use Cell_X() + ** & Cell_Y(), because they use shifts to compute the values, and if the + ** offset is negative we'll get a bogus coordinate! + */ + x = (*list) % MAP_CELL_W; + y = (*list) / MAP_CELL_H; + + max_x = max(max_x, x); + min_x = min(min_x, x); + max_y = max(max_y, y); + min_y = min(min_y, y); + + list++; + } + + w = max(1, max_x - min_x + 1); + h = min(1, max_y - min_y + 1); + } +} + + +/*********************************************************************************************** + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * * + * This routine will clear or set the cursor display bits on the map. * + * If the bit is set, then the cursor will be rendered on that map * + * icon. * + * * + * INPUT: pos -- Position of the upper left corner of the cursor. * + * * + * on -- Should the bit be turned on? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that every call to set the bits is matched by a * + * corresponding call to clear the bits. * + * * + * HISTORY: * + * 09/04/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DisplayClass::Cursor_Mark(CELL pos, bool on) +{ + CELL const * ptr; + CellClass * cellptr; + + if (pos == -1) return; + + /* + ** For every cell in the CursorSize list, invoke its Redraw_Objects and + ** toggle its IsCursorHere flag + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + if (on) { + cellptr->IsCursorHere = true; + } else { + cellptr->IsCursorHere = false; + } + } + } + + /* + ** For every cell in the PendingObjectPtr's Overlap_List, invoke its + ** Redraw_Objects routine. + */ + if (PendingObjectPtr && PendingObjectPtr->IsActive) { + ptr = PendingObjectPtr->Overlap_List(); + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * * + * This routine is called once per game display frame (15 times per second). It handles * + * the mouse shape tracking and map scrolling as necessary. * + * * + * INPUT: input -- The next key just fetched from the input queue. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * + * set to 0. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1994 JLB : Created. * + * 06/02/1994 JLB : Filters mouse click input. * + * 06/07/1994 JLB : Fixed so template click will behave right. * + * 10/14/1994 JLB : Changing cursor shape over target. * + * 12/31/1994 JLB : Takes mouse coordinates as parameters. * + * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * + *=============================================================================================*/ +void DisplayClass::AI(KeyNumType & input, int x, int y) +{ + if ( + IsRubberBand && + (Get_Mouse_X() < TacPixelX || + Get_Mouse_Y() < TacPixelY || + Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || + Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { + Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); + } + + MapClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * * + * This routine is used to add an arbitrary (but tangible) game object to the map. It will * + * be rendered (made visible) once it is submitted to this function. This function builds * + * the list of game objects that get rendered each frame as necessary. It is possible to * + * submit the game object to different rendering layers. All objects in a layer get drawn * + * at the same time. Using this layer method it becomes possible to have objects "below" * + * other objects. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * layer -- The layer to add the object to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * + *=============================================================================================*/ +void DisplayClass::Submit(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Submit(object, (layer == LAYER_GROUND)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * * + * Every object that is to disappear from the map must be removed from the rendering * + * system. * + * * + * INPUT: object -- The object to remove. * + * * + * layer -- The layer to remove it from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + *=============================================================================================*/ +void DisplayClass::Remove(ObjectClass const * object, LayerType layer) +{ + assert(object != 0); + assert(object->IsActive); + + if (object) { + Layer[layer].Delete((ObjectClass *)object); + } +} + + +/*********************************************************************************************** + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * * + * This routine is used to determine the cell that is located at the * + * screen pixel coordinates given. Typical use is when the player * + * clicks with the mouse on the tactical map. * + * * + * INPUT: x,y -- Screen pixel coordinates. * + * * + * OUTPUT: Returns with cell that is under the coordinates specified. * + * If the coordinate specified is outside of the tactical * + * map, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL DisplayClass::Click_Cell_Calc(int x, int y) const +{ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/5/2019 11:56AM + if (IgnoreViewConstraints || (unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); + return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); + } + return(-1); +} + + +/*********************************************************************************************** + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * * + * This routine is used to scroll the tactical map view in the desired * + * direction. It can also be used to determine if scrolling would be * + * legal without actually performing any scrolling action. * + * * + * INPUT: facing -- The direction to scroll the tactical map. * + * * + * distance -- The distance in leptons to scroll the map. * + * * + * really -- Should the map actually be scrolled? If false, * + * then only the legality of a scroll is checked. * + * * + * OUTPUT: bool; Would scrolling in the desired direction be possible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/20/1994 JLB : Converted to member function. * + * 08/09/1995 JLB : Added distance parameter. * + * 08/10/1995 JLB : Any direction scrolling. * + *=============================================================================================*/ +bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + /* + ** If the distance is invalid then no further checking is required. Bail + ** with a no-can-do flag. + */ + if (distance == 0) return(false); + FacingType crude = Dir_Facing(facing); + + if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { + if (crude == FACING_SW) facing = DIR_S; + if (crude == FACING_NW) facing = DIR_N; + } + if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { + if (crude == FACING_NW) facing = DIR_W; + if (crude == FACING_NE) facing = DIR_E; + } + if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { + if (crude == FACING_NE) facing = DIR_N; + if (crude == FACING_SE) facing = DIR_S; + } + if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { + if (crude == FACING_SE) facing = DIR_E; + if (crude == FACING_SW) facing = DIR_W; + } + + /* + ** Determine the coordinate that it wants to scroll to. + */ + COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); + + /* + ** Clip the new coordinate to the edges of the game world. + */ + int xx = (int)(short)Coord_X(coord) - (short)Cell_To_Lepton(MapCellX); + int yy = (int)(short)Coord_Y(coord) - (short)Cell_To_Lepton(MapCellY); + bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + if (xx < 0) { + xx = 0; + shifted = true; + } + if (yy < 0) { + yy = 0; + shifted = true; + } + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + /* + ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately + ** reflect the actual distance moved. + */ + if (shifted) { + distance = Distance(TacticalCoord, coord); + } + + /* + ** If the new coordinate is the same as the old, then no scrolling would occur. + */ + if (!distance || coord == TacticalCoord) return(false); + + /* + ** Since the new coordinate is different than the old one, possibly adjust the real + ** tactical map accordingly. + */ + if (really) { + Set_Tactical_Position(coord); + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Scrolled map REQUIRES all top layer units to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * * + * This routine is used to flag all cells in the specified list for * + * redrawing. * + * * + * INPUT: cell -- The origin cell that the list is offset from. * + * * + * list -- Pointer to a list of offsets from the origin cell. * + * Each cell so specified is flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is rather slow (by definition). * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 08/01/1994 JLB : Simplified. * + *=============================================================================================*/ +void DisplayClass::Refresh_Cells(CELL cell, short const * list) +{ + short tlist[36]; + + if (*list == REFRESH_SIDEBAR) { + list++; + } + + List_Copy(list, ARRAY_SIZE(tlist), tlist); + short * tt = tlist; + int count = 0; + while (*tt != REFRESH_EOL) { + if (count >= ARRAY_SIZE(tlist)) { // Added overrun check. ST - 8/14/2019 3:14PM + break; + } + CELL newcell = cell + *tt++; + if (In_Radar(newcell)) { + (*this)[newcell].Redraw_Objects(); + } + count++; + } +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + * 08/05/2019 ST : Added house parameter so we can do this per player ** + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell, HouseClass *house) const +{ + static char const _shadow[256]={ + -1,33, 2, 2,34,37, 2, 2, + 4,26, 6, 6, 4,26, 6, 6, + 35,45,17,17,38,41,17,17, + 4,26, 6, 6, 4,26, 6, 6, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 32,36,25,25,44,40,25,25, + 19,30,20,20,19,30,20,20, + 39,43,29,29,42,46,29,29, + 19,30,20,20,19,30,20,20, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2 + }; + + int index = 0, value = -1; + + /* + ** Don't map cells that are at the edges. This solves + ** problem of accessing cells off the bounds of map and into + ** who-knows-what memory. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ((unsigned)(Cell_X(cell)-1) >= MAP_CELL_W-2) return(-1); + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); +#else + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); +#endif + //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + CellClass const * cellptr = &(*this)[cell]; + + /* + ** Presume solid black if that is what is here already. + */ + if (!cellptr->Is_Visible(house) && !cellptr->Is_Mapped(house)) value = -2; + + if (cellptr->Is_Mapped(house) /*&& !cellptr->IsVisible*/) { + /* + ** Build an index into the lookup table using all 8 surrounding cells. + ** We're mapping a revealed cell and we only care about the existence + ** of black cells. Bit numbering starts at the upper-right corner and + ** goes around the cell clockwise, so 0x80 = directly north. + */ + cell -= MAP_CELL_W + 1; cellptr -= MAP_CELL_W + 1; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x40; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x80; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x01; + cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x20; + cell += 2; cellptr += 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x02; + cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x10; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x08; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x04; + + value = _shadow[index]; + } + return(value); +} + + +#if (0) // Old code for reference. ST - 8/15/2019 10:25AM +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell) const +{ + static char const _shadow[256]={ + -1,33, 2, 2,34,37, 2, 2, + 4,26, 6, 6, 4,26, 6, 6, + 35,45,17,17,38,41,17,17, + 4,26, 6, 6, 4,26, 6, 6, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 32,36,25,25,44,40,25,25, + 19,30,20,20,19,30,20,20, + 39,43,29,29,42,46,29,29, + 19,30,20,20,19,30,20,20, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2 + }; + + int index = 0, value = -1; + + /* + ** Don't map cells that are at the top or bottom edge. This solves + ** problem of accessing cells off the top or bottom of the map and into + ** who-knows-what memory. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); +#else + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); +#endif + //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + CellClass const * cellptr = &(*this)[cell]; + + /* + ** Presume solid black if that is what is here already. + */ + if (!cellptr->IsVisible && !cellptr->IsMapped) value = -2; + + if (cellptr->IsMapped /*&& !cellptr->IsVisible*/) { + /* + ** Build an index into the lookup table using all 8 surrounding cells. + ** We're mapping a revealed cell and we only care about the existence + ** of black cells. Bit numbering starts at the upper-right corner and + ** goes around the cell clockwise, so 0x80 = directly north. + */ + cellptr-= MAP_CELL_W + 1; + if (!cellptr->IsMapped) index |= 0x40; + cellptr++; + if (!cellptr->IsMapped) index |= 0x80; + cellptr++; + if (!cellptr->IsMapped) index |= 0x01; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x20; + cellptr += 2; + if (!cellptr->IsMapped) index |= 0x02; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x10; + cellptr++; + if (!cellptr->IsMapped) index |= 0x08; + cellptr++; + if (!cellptr->IsMapped) index |= 0x04; + + value = _shadow[index]; + } + return(value); +} +#endif + +/*********************************************************************************************** + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * * + * This routine maps the specified cell. The cell must not already * + * have been mapped and the mapping player must be the human. * + * This routine will update any adjacent cell map icon as appropriate. * + * * + * INPUT: cell -- The cell to be mapped. * + * * + * house -- The player that is doing the mapping. * + * * + * OUTPUT: bool; Was action taken to map this cell? * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/24/1994 JLB : Takes pointer to HouseClass. * + * 02/20/1996 JLB : Allied units reveal the map for the player. * + * 08/05/2019 ST : Use per-player mapping so we can track the shroud for all players * + *=============================================================================================*/ +bool DisplayClass::Map_Cell(CELL cell, HouseClass * house, bool check_radar_spied, bool and_for_allies) +{ + // OK for house not to be PlayerPtr. ST - 8/6/2019 10:05AM + //if (house != PlayerPtr || !In_Radar(cell)) return(false); + if (house == NULL || !In_Radar(cell)) return(false); + + if (!house->IsHuman) { + if (!ShareAllyVisibility || !and_for_allies && !check_radar_spied) { + return false; + } + } + + /* + ** First check for the condition where we're spying on a house's radar + ** facility, to see if his mapping is applicable to us. + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + // Original code. ST - 8/15/2019 10:26AM + if (house && house != PlayerPtr) { + if (house->RadarSpied & (1<<(PlayerPtr->Class->House))) house = PlayerPtr; + if (Session.Type == GAME_NORMAL && house->Is_Ally(PlayerPtr)) house = PlayerPtr; + } + } else { + // Version to work with any human player, not just PlayerPtr + if (house && check_radar_spied) { + + for (int i=0 ; iPlayer.ID); + if (player_ptr->IsHuman) { + if (house->RadarSpied & (1<<(player_ptr->Class->House))) { + Map_Cell(cell, player_ptr, false, false); + } + } + } + } + } + + /* + ** Maybe also recurse to map for allies + */ + if (ShareAllyVisibility && and_for_allies && Session.Type == GAME_GLYPHX_MULTIPLAYER) { + + for (int i=0 ; iPlayer.ID); + if (player_ptr && player_ptr->IsActive && player_ptr->IsHuman) { + if (player_ptr != house && house->Is_Ally(player_ptr)) { + Map_Cell(cell, player_ptr, check_radar_spied, false); + } + } + } + } + + CellClass * cellptr = &(*this)[cell]; + + /* + ** Don't bother remapping this cell if it is already mapped. + */ +#if (1) + if (cellptr->Is_Mapped(house)) { + if (!cellptr->Is_Visible(house)) { + cellptr->Redraw_Objects(); + } + return(false); + } +#else + if (cellptr->IsMapped) { + if (!cellptr->IsVisible) { + cellptr->Redraw_Objects(); + } + return(false); + } +#endif + /* + ** Mark the cell as being mapped. This must be done first because + ** if the IsVisible flag must be set, then it might affect the + ** adjacent cell processing. + */ + // Set per player. ST - 8/6/2019 10:18AM + cellptr->Set_Mapped(house); + cellptr->Redraw_Objects(); + if (Cell_Shadow(cell, house) == -1) { + cellptr->Set_Visible(house); + } + + /* + ** Check out all adjacent cells to see if they need + ** to be mapped as well. This is necessary because of the + ** "unique" method of showing shadowed cells. Many combinations + ** are not allowed, and to fix this, just map the cells until + ** all is ok. + */ + int xx = Cell_X(cell); + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + int shadow; + CELL c; + int xdiff; + + c = Adjacent_Cell(cell, dir); + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)c >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(c) - xx; + xdiff = ABS(xdiff); + if (xdiff > 1) continue; + + CellClass * cptr = &(*this)[c]; + cptr->Redraw_Objects(); + +#if (1) + // New client/server friendly code + if (c != cell && !cptr->Is_Visible(house)) { + shadow = Cell_Shadow(c, house); + + if (shadow == -1) { + if (!cptr->Is_Mapped(house)) { + Map_Cell(c, house, check_radar_spied, false); + } else { + cptr->Set_Visible(house); + } + } else { + if (shadow != -2 && !cptr->Is_Mapped(house)) { + Map_Cell(c, house, check_radar_spied, false); + } + } + } +#else + // Old peer/peer code + if (c != cell && !cptr->IsVisible) { + shadow = Cell_Shadow(c); + + if (shadow == -1) { + if (!cptr->IsMapped) { + Map_Cell(c, house); + } else { + cptr->IsVisible = true; + } + } else { + if (shadow != -2 && !cptr->IsMapped) { + Map_Cell(c, house); + } + } + } +#endif + } + + TechnoClass * tech = (*this)[cell].Cell_Techno(); + if (tech) { + tech->Revealed(house); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * * + * This is the routine that figures out the location on the screen for * + * a specified coordinate. It is one of the fundamental routines * + * necessary for rendering the game objects. It performs some quick * + * tests to see if the coordinate is in a visible region and returns * + * this check as a boolean value. * + * * + * INPUT: coord -- The coordinate to check. * + * * + * x,y -- Reference to the pixel coordinates that this * + * coordinate would be when rendered. * + * * + * OUTPUT: bool; Is this coordinate in a visible portion of the map? * + * * + * WARNINGS: If the coordinate is not in a visible portion of the * + * map, then this X and Y parameters are not set. * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 12/15/1994 JLB : Converted to member function. * + * 01/07/1995 JLB : Uses inline functions to extract coord components. * + * 08/09/1995 JLB : Uses new coordinate system. * + *=============================================================================================*/ +#define EDGE_ZONE (CELL_LEPTON_W*2) +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const +{ + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + xoff = (xoff + EDGE_ZONE) - xtac; + + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + yoff = (yoff + EDGE_ZONE) - ytac; + + x = Lepton_To_Pixel(xoff) - CELL_PIXEL_W * 2; + y = Lepton_To_Pixel(yoff) - CELL_PIXEL_H * 2; + + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 4/17/2019 9:06AM + return(coord && (IgnoreViewConstraints || ((xoff <= TacLeptonWidth + EDGE_ZONE * 2) && (yoff <= TacLeptonHeight + EDGE_ZONE * 2)))); +} + +#if (0) //reference. ST - 5/8/2019 +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const +{ + if (coord) { + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + + xoff = (xoff+EDGE_ZONE) - xtac; + if ((unsigned)xoff <= TacLeptonWidth + EDGE_ZONE*2) { + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + + yoff = (yoff+EDGE_ZONE) - ytac; + if ((unsigned)yoff <= TacLeptonHeight + EDGE_ZONE*2) { + x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; + y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; + return(true); + } + } + } + return(false); +} +#endif + +/*********************************************************************************************** + * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * + * * + * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * + * be within the region bounded by TacMapX,Y - + TacMapW,H. * + * * + * INPUT: source, dest -- References to the coordinates to check. * + * * + * * + * OUTPUT: bool; Are these coordinates in a visible portion of the map? * + * Returns true if the pushed source & dest are visible, but if neither are * + * within the map, then it returns false. * + * * + * * + * HISTORY: * + * 03/27/1995 BWG : Created. * + *=============================================================================================*/ +bool DisplayClass::Push_Onto_TacMap(COORDINATE & source, COORDINATE & dest) +{ + if (!source || !dest) return(false); + + int x1 = Coord_X(source); + int y1 = Coord_Y(source); + int x2 = Coord_X(dest); + int y2 = Coord_Y(dest); + int left = Coord_X(TacticalCoord); + int right = Coord_X(TacticalCoord) + TacLeptonWidth; + int top = Coord_Y(TacticalCoord); + int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; + + if (x1 < left && x2 < left) return(false); + if (x1 > right && x2 > right) return(false); + if (y1 < top && y2 < top) return(false); + if (y1 > bottom && y2 > bottom) return(false); + + x1 = Bound(x1, left, right); + x2 = Bound(x2, left, right); + y1 = Bound(y1, top, bottom); + y2 = Bound(y2, top, bottom); + + source = XY_Coord(x1, y1); + dest = XY_Coord(x2, y2); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * * + * This routine is used to determine what the player has clicked on. * + * It is passed the cell that the click was on and it then examines * + * the cell and returns with a pointer to the object that is there. * + * * + * INPUT: cell -- The cell that has been clicked upon. * + * * + * x,y -- Optional offsets from the upper left corner of the cell to be used in * + * determining exactly which object in the cell is desired. * + * * + * OUTPUT: Returns with a pointer to the object that is "clickable" in * + * the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) const +{ + return(*this)[cell].Cell_Object(x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Draw_It -- Draws the tactical map. * + * * + * This will draw the tactical map at the recorded position. This * + * routine is used whenever the tactical map moves or needs to be * + * completely redrawn. It will handle making the necessary adjustments * + * to accomodate a moving cursor. * + * * + * INPUT: forced -- bool; force redraw of the entire display? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1991 JLB : Created. (benchmark = 292) * + * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * + * 04/15/1991 JLB : Added actual map reference for terrain (207) * + * 04/16/1991 JLB : _cell2meta converted to int (194) * + * 04/16/1991 JLB : References actual CellIcon[] array (204) * + * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * + * 04/17/1991 JLB : Cell based tactical map rendering (165) * + * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * + * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * + * 04/23/1991 JLB : Map active location cursor (334) * + * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * + * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * + * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * + * 05/12/1992 JLB : Destination page support. * + * 02/14/1994 JLB : Revamped. * + * 05/01/1994 JLB : Converted to member function. * + * 12/15/1994 JLB : Updated to work with display hierarchy. * + * 12/24/1994 JLB : Examines redraw bit intelligently. * + * 12/24/1994 JLB : Combined with old Refresh_Map() function. * + * 01/10/1995 JLB : Rubber band drawing. * + *=============================================================================================*/ + void DisplayClass::Draw_It(bool forced) +{ + int x,y; // Working cell index values. + + MapClass::Draw_It(forced); + + if (IsToRedraw || forced) { + BStart(BENCH_TACTICAL); + IsToRedraw = false; + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + Refresh_Band(); + + /* + ** Mark all cells under the vortex to be redrawn + */ + ChronalVortex.Set_Redraw(); + + + /* + ** If the multiplayer message system is displaying one or more messages, + ** flag all cells covered by the messages to redraw. This will prevent + ** messages from smearing the map if it scrolls. + */ + int num = Session.Messages.Num_Messages(); + if (num > 0) { + CELL cell; + for (cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + if (num > 1) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 2) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 3) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + } + + /* + ** Check for a movement of the tactical map. If there has been some + ** movement, then part (or all) of the icons must be redrawn. + */ + if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || + Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { + + int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); + int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); + + int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. + int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; + + int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. + int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. + + if (oldw < 1) forced = true; + if (oldh < 1) forced = true; + + +#ifdef WIN32 //For WIN32 only redraw the edges of the map that move into view + + /* + ** Work out which map edges need to be redrawn + */ + BOOL redraw_right = (oldx < 0) ? true : false; //Right hand edge + BOOL redraw_left = (oldx > 0) ? true : false; //Left hand edge + BOOL redraw_bottom= (oldy < 0) ? true : false; //Bottom edge + BOOL redraw_top = (oldy > 0) ? true : false; //Top edge + + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + /* + ** If hid page is in video memory then blit from the seen page to avoid blitting + ** an overlapped region. + */ + if (HidPage.Get_IsDirectDraw()) { + Hide_Mouse(); + SeenBuff.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + Show_Mouse(); + } else { + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } + + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** + ** Set the 'redraw stamp' bit for any cells that could not be copied. + ** + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + + if (abs(oldx) < 0x25 && abs(oldy) < 0x25) { + + /* + ** The width of the area we redraw depends on the scroll speed + */ + int extra_x = (abs(oldx)>=16) ? 2 : 1; + int extra_y = (abs(oldy)>=16) ? 2 : 1; + + /* + ** Flag the cells across the top of the visible area if required + */ + if (redraw_top) { + for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells across the bottom of the visible area if required + */ + if (redraw_bottom) { + for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the left of the visible area if required + */ + if (redraw_left) { + for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the right of the visible area if required + */ + if (redraw_right) { + for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + } else { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } + + +#else //WIN32 + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } +#endif + + /* + ** If the entire tactical map is forced to be redrawn, then set all the redraw flags + ** and let the normal processing take care of the rest. + */ + if (forced) { + CellRedraw.Set(); + } + + /* + ** The first order of business is to redraw all the underlying icons that are + ** flagged to be redrawn. + */ + if (HidPage.Lock()) { + Redraw_Icons(); + + /* + ** Draw the infantry bodies in this special layer. + */ +// for (int index = 0; index < Anims.Count(); index++) { +// AnimClass * anim = Anims.Ptr(index); +// if (*anim >= ANIM_CORPSE1 && *anim <= ANIM_CORPSE3) { +// anim->Render(forced); +// } +// } + +#ifdef SORTDRAW + /* + ** Draw the vortex effect over the terrain + */ + ChronalVortex.Render(); + + Redraw_OIcons(); +#endif + + HidPage.Unlock(); + } + +#ifndef WIN32 + /* + ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom + ** area one line below the screen. This causes the predator effect to work on any + ** shape drawn at the bottom of the screen. + */ + HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); +#endif + + if (HidPage.Lock()) { + +#ifndef SORTDRAW + /* + ** Draw the vortex effect over the terrain + */ + ChronalVortex.Render(); +#endif + + /* + ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer + ** first and then followed by all the layers in increasing altitude. + */ + BStart(BENCH_OBJECTS); + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + for (int index = 0; index < Layer[layer].Count(); index++) { + ObjectClass * ptr = Layer[layer][index]; + +#ifdef SORTDRAW + /* + ** Techno objects are drawn as part of the cell redraw process since techno + ** objects in the ground layer are handled by the Occupier and Overlapper + ** pointer lists. + */ + if (!Debug_Map && ptr->Is_Techno() && layer == LAYER_GROUND && ((TechnoClass*)ptr)->Visual_Character() == VISUAL_NORMAL) continue; +#endif + +// if (ptr->What_Am_I() == RTTI_ANIM && *((AnimClass*)ptr) >= ANIM_CORPSE1 && *((AnimClass*)ptr) <= ANIM_CORPSE3) { +// continue; +// } + assert(ptr->IsActive); + ptr->Render(forced); + } + } + BEnd(BENCH_OBJECTS); + + //ChronalVortex.Render(); + /* + ** Finally, redraw the shadow overlay as necessary. + */ + BStart(BENCH_SHROUD); + Redraw_Shadow(); + BEnd(BENCH_SHROUD); + } + HidPage.Unlock(); + +#ifdef SORTDRAW + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + Layer[LAYER_GROUND][index]->IsToDisplay = false; + } +#endif + + /* + ** Draw the rubber band over the top of it all. + */ + if (IsRubberBand) { + LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); + } + + /* + ** Clear the redraw flags so that normal redraw flag setting can resume. + */ + CellRedraw.Reset(); + +#ifdef SCENARIO_EDITOR + /* + ** If we're placing an object (PendingObject is non-NULL), and that object + ** is NOT an icon, smudge, or overlay, draw it here. + ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; + ** they're drawn at the upper left coord, so I have to AND the coord value + ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the + ** cell coordinates. + */ + if (Debug_Map && PendingObjectPtr) { + PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); + PendingObjectPtr->Render(true); + } +#endif + BEnd(BENCH_TACTICAL); + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * * + * This routine will redraw all of the terrain icons that are flagged * + * to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + * 06/20/1994 JLB : Uses cell drawing support function. * + * 12/06/1994 JLB : Scans tactical view in separate row/column loops * + * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * + *=============================================================================================*/ +void DisplayClass::Redraw_Icons(void) +{ + IsShadowPresent = false; + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + //if (cellptr->IsMapped || Debug_Unshroud) { + if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + cellptr->Draw_It(xpixel, ypixel); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + //if (!cellptr->IsVisible && !Debug_Unshroud) { + if (!cellptr->Is_Visible(PlayerPtr) && !Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + IsShadowPresent = true; + } + } + } + } + } +} + + +#ifdef SORTDRAW +void DisplayClass::Redraw_OIcons(void) +{ + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + //if (cellptr->IsMapped || Debug_Unshroud) { + if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + cellptr->Draw_It(xpixel, ypixel, true); + } + } + } + } + } +} +#endif + + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redrawn are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + //if (cellptr->IsVisible) continue; + if (cellptr->Is_Visible(PlayerPtr)) continue; // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + int shadow = -2; + //if (cellptr->IsMapped) { + if (cellptr->Is_Mapped(PlayerPtr)) { // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + shadow = Cell_Shadow(cell, PlayerPtr); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + } + if (shadow >= 0) { + CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); + } else { + if (shadow != -1) { + int ww = CELL_PIXEL_W; + int hh = CELL_PIXEL_H; + + if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { + LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); + } + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Next_Object -- Searches for next object on display. * + * * + * This utility routine is used to find the "next" object from the object specified. This * + * is typically used when is pressed and the current object shifts. * + * * + * INPUT: object -- The current object to base the "next" calculation off of. * + * * + * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * + * then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Next_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (unsigned uindex = 0; uindex < (unsigned)Layer[LAYER_GROUND].Count(); uindex++) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * * + * This routine will search for the previous object. Previous is defined as the one listed * + * before the specified object in the ground layer. If there is no specified object, then * + * the last object in the ground layer is returned. * + * * + * INPUT: object -- Pointer to the object that "previous" is to be defined from. * + * * + * OUTPUT: Returns with a pointer to the object previous to the specified one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * * + * INPUT: * + * x,y pixel coordinates to convert * + * * + * OUTPUT: * + * COORDINATE of pixel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/09/1994 BR : Created. * + * 12/06/1994 JLB : Uses map dimension variables in display class. * + * 12/10/1994 JLB : Uses union to speed building coordinate value. * + *=============================================================================================*/ +COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) const +{ + /* + ** Normalize the pixel coordinates to be relative to the upper left corner + ** of the tactical map. The coordinates are expressed in leptons. + */ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + /* + ** If pixel coordinate is over the tactical map, then translate it into a coordinate + ** value. If not, then just return with NULL. + */ + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/6/2019 10:47AM + //if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + if (IgnoreViewConstraints || ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight)) { + return(Coord_Add(TacticalCoord, XY_Coord(x, y))); + } + return(0); +} + + +/*********************************************************************************************** + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * * + * Find a cell meeting the specified requirements. This function is * + * used for scenario reinforcements. * + * * + * INPUT: dir -- Method of picking a map cell. * + * * + * waypoint -- Closest waypoint to use for finding appropriate map edge. * + * * + * cell -- Cell to find closest edge to if waypoint not specified. * + * * + * loco -- The locomotion of the reinforcements that are trying to enter. * + * * + * zonecheck -- Is zone checking required? * + * * + * mzone -- The movement zone type to check against (only if zone checking). * + * * + * OUTPUT: Returns with the calculated cell. If 0, then this indicates * + * that no legal cell was found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/11/1994 JLB : Revamped. * + * 05/18/1994 JLB : Converted to member function. * + * 12/18/1995 JLB : Handles edge preference scan. * + * 06/24/1996 JLB : Removed Dune II legacy code. * + * 06/25/1996 JLB : Rewrote and greatly simplified. * + * 10/05/1996 JLB : Checks for zone and crushable status. * + *=============================================================================================*/ +CELL DisplayClass::Calculated_Cell(SourceType dir, WAYPOINT waypoint, CELL cell, SpeedType loco, bool zonecheck, MZoneType mzone) const +{ + bool vert = false; + bool horz = false; + int x = 0; + int y = 0; + CELL punt = 0; // If all else fails, return this cell location. + int zone = -1; // Tentative zone for legality checking. + + /* + ** Waypoint edge detection for ground based reinforcements that have a waypoint origin are + ** determined by finding the closest map edge to the waypoint. Reinforcement location + ** scanning starts from that position. + */ + CELL trycell = -1; + if (waypoint != -1) { + trycell = Scen.Waypoint[waypoint]; + } + if (trycell == -1) { + trycell = cell; + } + + /* + ** If zone checking is requested, then find the correct zone to use. + */ + if (zonecheck && trycell != -1) { + zone = (*this)[trycell].Zones[mzone]; + } + + /* + ** If the cell or waypoint specified as been detected as legal, then set up the map edge + ** scanning values accordingly. + */ + if (trycell != -1) { + x = Cell_X(trycell) - MapCellX; + x = min(x, (-Cell_X(trycell) + (MapCellX+MapCellWidth))); + + y = Cell_Y(trycell) - MapCellY; + y = min(y, (-Cell_Y(trycell) + (MapCellY+MapCellHeight))); + + if (x < y) { + vert = true; + horz = false; + if ((Cell_X(trycell)-MapCellX) < MapCellWidth/2) { + x = -1; + } else { + x = MapCellWidth; + } + y = Cell_Y(trycell) - MapCellY; + + } else { + + vert = false; + horz = true; + if ((Cell_Y(trycell)-MapCellY) < MapCellHeight/2) { + y = -1; + } else { + y = MapCellHeight; + } + x = Cell_X(trycell) - MapCellX; + } + } + + /* + ** If no map edge can be inferred from the waypoint, then go with the + ** map edge specified by the edge parameter. + */ + if (!vert && !horz) { + switch (dir) { + default: + case SOURCE_NORTH: + horz = true; + y = -1; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_SOUTH: + horz = true; + y = MapCellHeight; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_EAST: + vert = true; + x = MapCellWidth; + y = Random_Pick(0, MapCellHeight-1); + break; + + case SOURCE_WEST: + vert = true; + x = -1; + y = Random_Pick(0, MapCellHeight-1); + break; + } + } + + /* + ** Determine the default reinforcement cell if all else fails. + */ + punt = XY_Cell(x + MapCellX, y + MapCellY); + + /* + ** Scan through the vertical and horizontal edges of the map looking for + ** a relatively clear cell for object placement. The cell scanned is + ** from the edge position specified by the X and Y variables. + */ + if (vert) { + int modifier = (x > MapCellX) ? -1 : 1; + + for (int index = 0; index < MapCellHeight; index++) { + CELL trycell = XY_Cell(x + MapCellX, ((y + index) % MapCellHeight) + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + if (horz) { + int modifier = (y > MapCellY) ? -MAP_CELL_W : MAP_CELL_W; + + for (int index = 0; index < MapCellWidth; index++) { + CELL trycell = XY_Cell(((x + index) % MapCellWidth) + MapCellX, y + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + /* + ** If there was no success in finding a suitable reinforcement edge cell, then return + ** with the default 'punt' cell location. + */ + return(punt); +} + + +/*********************************************************************************************** + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * * + * This routine will check the secified cell (given the specified conditions) and determine * + * if that is a good cell for reinforcement purposes. It checks for passability of the cell * + * as well as zone and whether blocking walls can be crushed. * + * * + * INPUT: outcell -- The cell that is just outside the edge of the map. * + * * + * incell -- The cell that is just inside the edge of the map. * + * * + * loco -- The locomotion type of the reinforcement. * + * * + * zone -- The zone that the eventual movement destination lies. A reinforcement * + * edge must fall within the same zone. * + * * + * mzone -- The zone check type to check against (if zone checking required) * + * * + * OUTPUT: bool; Is the specified cell good for reinforcement purposes? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const +{ + /* + ** If the map edge location is not clear for object placement, then this is not + ** a good cell for reinforcement purposes. + */ + if (!(*this)[outcell].Is_Clear_To_Move(loco, false, false)) { + return(false); + } + + /* + ** If it looks like the on-map cell cannot be driven on to, then return with + ** the failure code. + */ + if (!(*this)[incell].Is_Clear_To_Move(loco, false, false, zone, mzone)) { + return(false); + } + + /* + ** If the reinforcement cell is already occupied, then return a failure code. + */ + if ((*this)[outcell].Cell_Techno() != NULL) { + return(false); + } + if ((*this)[incell].Cell_Techno() != NULL) return(false); + + /* + ** All tests have passed, return with success code. + */ +//Mono_Printf("<%04X>\n", incell);Keyboard->Get(); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * * + * Use this routine to simultaneously select all objects within the coordinate region * + * specified. This routine is used by the multi-select rubber band handler. * + * * + * INPUT: coord1 -- Coordinate of one corner of the selection region. * + * * + * coord2 -- The opposite corner of the selection region. * + * * + * additive -- Does this add to the existing selection or replace it. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 04/25/1995 JLB : Limited to non-building type. * + * 03/06/1996 JLB : Allows selection of aircraft with bounding box. * + *=============================================================================================*/ +static bool should_exclude_from_selection(ObjectClass* obj) +{ + return (obj->What_Am_I() == RTTI_UNIT) && + (((UnitClass *)obj)->Class->IsToHarvest || + *((UnitClass *)obj) == UNIT_MCV); +} + +void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool additive) +{ + COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; + + coord1 = Coord_Add(tcoord, coord1); + coord2 = Coord_Add(tcoord, coord2); + int x1 = Coord_X(coord1); + int x2 = Coord_X(coord2); + int y1 = Coord_Y(coord1); + int y2 = Coord_Y(coord2); + + /* + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. + */ + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + /* + ** Sweep through all ground layer objects and select the ones within the + ** bounding box. + */ + if (!additive) { + Unselect_All(); + } + AllowVoice = true; + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Layer[LAYER_GROUND][index]; + COORDINATE ocoord = obj->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are allowed to be selected, and are within the bounding box. + */ + HouseClass * hptr = HouseClass::As_Pointer(obj->Owner()); + if ( obj->Class_Of().IsSelectable && + obj->What_Am_I() != RTTI_BUILDING && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = (hptr != NULL) && hptr->IsPlayerControl; + AllowVoice &= is_player_controlled; + if (obj->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** Select any aircraft with the bounding box. + */ + for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { + AircraftClass * aircraft = Aircraft.Ptr(air_index); + COORDINATE ocoord = aircraft->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are allowed to be selected, and are within the bounding box. + */ + if ( aircraft->Class->IsSelectable && + !aircraft->Is_Selected_By_Player() && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = aircraft->House->IsPlayerControl; + AllowVoice &= is_player_controlled; + if (aircraft->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** If a mix of player and non-player controlled units were selected, make sure non-player controlled units are de-selected + */ + bool player_controlled_units = false, non_player_controlled_units = false; + for (int i = 0; (i < CurrentObject.Count()) && (!player_controlled_units || !non_player_controlled_units); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (hptr->IsPlayerControl) { + player_controlled_units = true; + } + else { + non_player_controlled_units = true; + } + } + if (player_controlled_units && non_player_controlled_units) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (!hptr->IsPlayerControl) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + + /* + ** If player-controlled units are non-additively selected, remove harvesters and MCVs if they aren't the only types of units selected + */ + if (!additive && player_controlled_units) { + bool any_to_exclude = false, all_to_exclude = true; + for (int i = 0; i < CurrentObject.Count(); ++i) { + bool exclude = should_exclude_from_selection(CurrentObject[i]); + any_to_exclude |= exclude; + all_to_exclude &= exclude; + } + if (any_to_exclude && !all_to_exclude) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (should_exclude_from_selection(CurrentObject[i])) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + } + + AllowVoice = true; +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * * + * Use this routine to flag all cells that are covered in some fashion by the multi-unit * + * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * + * size or is being removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Refresh_Band(void) +{ + if (IsRubberBand) { + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + int x1 = BandX+TacPixelX; + int y1 = BandY+TacPixelY; + int x2 = NewX+TacPixelX; + int y2 = NewY+TacPixelY; + + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + CELL cell; + for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { + cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * * + * This routine handles the input directed at the tactical map. Since input, in this * + * regard, includes even the presence of the mouse over the tactical map, this routine * + * is called nearly every game frame. It handles adjusting the mouse shape as well as * + * giving orders to units. * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/17/1995 JLB : Created. * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + int x,y; // Sub cell pixel coordinates. + bool shadow; + ObjectClass * object = 0; + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1); + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + if (coord) { + + //shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:49AM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + if (object != NULL && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && (((TechnoClass *)object)->Cloak == CLOAKED || ((TechnoClass *)object)->Techno_Type_Class()->IsInvisible)) { + object = NULL; + } + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object != NULL) { + action = Best_Object_Action(object); + } else { + action = Best_Object_Action(cell); + } + + } else { + + if (object && object->Class_Of().IsSelectable) { + action = ACTION_SELECT; + } + + if (Map.IsRepairMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { + action = ACTION_REPAIR; + } else { + action = ACTION_NO_REPAIR; + } + } + + if (Map.IsSellMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { + if (object->What_Am_I() == RTTI_BUILDING) { + action = ACTION_SELL; + } else { + action = ACTION_SELL_UNIT; + } + } else { + + /* + ** Check to see if the cursor is over an owned wall. + */ + if (Map[cell].Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && + Map[cell].Owner == PlayerPtr->Class->House) { + action = ACTION_SELL; + } else { + action = ACTION_NO_SELL; + } + } + } + + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + action = ACTION_NUKE_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_BOMB) { + action = ACTION_PARA_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_INFANTRY) { + action = ACTION_PARA_INFANTRY; + } + + if (Map.IsTargettingMode == SPC_SPY_MISSION) { + action = ACTION_SPY_MISSION; + } + + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + action = ACTION_IRON_CURTAIN; + } + + if (Map.IsTargettingMode == SPC_CHRONOSPHERE) { + action = ACTION_CHRONOSPHERE; + } + + if (Map.IsTargettingMode == SPC_CHRONO2) { + action = ACTION_CHRONO2; + if (shadow) action = ACTION_NOMOVE; + ObjectClass const * tobject = As_Object(PlayerPtr->UnitToTeleport); + + /* + ** Determine if the object can be teleported to the destination cell. + */ + if (tobject != NULL && tobject->Is_Techno()) { + TechnoClass const * uobject = (TechnoClass const *)tobject; + if (!uobject->Can_Teleport_Here(cell)) { +// if (((UnitClass *)As_Object(PlayerPtr->UnitToTeleport))->Can_Enter_Cell(cell, FACING_NONE) != MOVE_OK) { + action = ACTION_NOMOVE; + } + + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else { // If the object is no longer valid, cancel targetting mode. + action = ACTION_NOMOVE; + Map.IsTargettingMode = SPC_NONE; + } +#endif + } + + if (Map.PendingObject) { + action = ACTION_NONE; + } + } + + /* + ** Move any cursor displayed. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** A right mouse button press cancels the current action or selection. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** Make sure that if the mouse button has been released and the map doesn't know about it, + ** then it must be informed. Do this by faking a mouse release event. + */ + if ((flags & LEFTUP) && Map.IsRubberBand) { + flags |= LEFTRELEASE; + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (!edge) { + if (flags & LEFTUP) { + Map.Mouse_Left_Up(cell, shadow, object, action); + } + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTRELEASE) { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + + /* + ** When the mouse is first pressed on the map, then record the mouse + ** position so that a proper check before going into rubber band + ** mode can be made. Rubber band mode starts when the mouse is + ** held down and moved a certain minimum distance. + */ + if (!edge && (flags & LEFTPRESS)) { + Map.Mouse_Left_Up(cell, shadow, object, action); + Map.Mouse_Left_Press(x, y); + } + + /* + ** While the mouse is being held down, determine if rubber band mode should + ** start. If rubber band mode is already active, then update the size + ** and flag the map to redraw it. + */ + if (flags & LEFTHELD) { + Map.Mouse_Left_Held(x, y); + } + } + + return(GadgetClass::Action(0, key)); +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Selection_At_Mouse -- Object selection * + * * + * Selects any objects at the current mouse position. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Selection_At_Mouse(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + if (coord) { + bool shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (object != nullptr) + { + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + if (shiftdown) + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_TOGGLE_SELECT); + } + else + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_SELECT); + } + + } + else + { + Unselect_All(); + } + } + + return 0; +} + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Command_Object -- Commanding Units * + * * + * Issues a command to the currently selected unit. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Command_Object(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + ActionType action = ACTION_NONE; + + if (coord) { + bool shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (CurrentObject.Count()) { + if (object) { + action = Best_Object_Action(object); + } + else { + action = Best_Object_Action(cell); + } + } + + if (action != ACTION_SELECT) + { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + } + return 0; +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * * + * This routine is called when the right mouse button is pressed. This action is supposed * + * to cancel whatever mode or process is active. If there is nothing to cancel, then it * + * will default to unselecting any units that might be currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Right_Press(void) +{ + if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { + //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + } else { + if (IsRepairMode) { + IsRepairMode = false; + } else { + if (IsSellMode) { + IsSellMode = false; + } else { + if (IsTargettingMode != SPC_NONE) { + IsTargettingMode = SPC_NONE; + } else { + Unselect_All(); + } + } + } + } + + // If it breaks... call 228. + Set_Default_Mouse(MOUSE_NORMAL, Map.IsSmall); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * * + * This routine is called continuously while the mouse is over the tactical map but there * + * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * + * help text. * + * * + * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * + * * + * object -- Pointer to the object that the mouse is currently over (may be NULL). * + * * + * action -- This is the action that the currently selected object (if any) will * + * perform if the left mouse button were clicked at this location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall) +{ + IsTentative = false; + + TARGET target = TARGET_NONE; + if (object != NULL) { + target = object->As_Target(); + } else { + if (cell != -1) { + target = As_Target(cell); + } + } + + /* + ** Don't allow selection of an object that is located in shadowed terrain. + ** In fact, just show the normal move cursor in order to keep the shadowed + ** terrain a mystery. + */ + if (shadow) { + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_NONE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_NO_SELL: + case ACTION_SELL: + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + case ACTION_NOMOVE: + if (CurrentObject.Count()) { + MouseType mouse_type = MOUSE_NO_MOVE; + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (CurrentObject[i]->What_Am_I() != RTTI_AIRCRAFT) { + mouse_type = MOUSE_CAN_MOVE; + break; + } + } + Set_Default_Mouse(mouse_type, wsmall); + break; + } + // Fall into next case for non aircraft object types. + + default: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + } + } else { + + /* + ** Change the mouse shape according to the default action that will occur + ** if the mouse button were clicked at this location. + */ + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_DAMAGE, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_GREPAIR, wsmall); + break; + + case ACTION_TOGGLE_SELECT: + case ACTION_SELECT: + Set_Default_Mouse(MOUSE_CAN_SELECT, wsmall); + break; + + case ACTION_MOVE: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_ATTACK: + if (Target_Legal(target) && CurrentObject.Count() == 1 && CurrentObject[0]->Is_Techno() && ((TechnoClass *)CurrentObject[0])->In_Range(target, 0)) { + Set_Default_Mouse(MOUSE_STAY_ATTACK, wsmall); + break; + } + // fall into next case. + + case ACTION_HARVEST: + Set_Default_Mouse(MOUSE_CAN_ATTACK, wsmall); + break; + + case ACTION_SABOTAGE: + Set_Default_Mouse(MOUSE_DEMOLITIONS, wsmall); + break; + + case ACTION_ENTER: + case ACTION_CAPTURE: + Set_Default_Mouse(MOUSE_ENTER, wsmall); + break; + + case ACTION_NOMOVE: + Set_Default_Mouse(MOUSE_NO_MOVE, wsmall); + break; + + case ACTION_NO_SELL: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_SELF: + Set_Default_Mouse(MOUSE_DEPLOY, wsmall); + break; + + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_REPAIR, wsmall); + break; + + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_SELL_UNIT, wsmall); + break; + + case ACTION_SELL: + Set_Default_Mouse(MOUSE_SELL_BACK, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + case ACTION_TOGGLE_PRIMARY: + Set_Default_Mouse(MOUSE_DEPLOY, wsmall); + break; + + default: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + } + } +#if 0 + /* + ** Never display help text if the mouse is held over the radar map. + */ + if (wsmall) { + return; + } + + /* + ** Give a generic help message when over shadow terrain. + */ + if (shadow) { +// if (Scen.Scenario < 4) { + Help_Text(TXT_SHADOW); +// } else { +// Help_Text(TXT_NONE); +// } + } else { + + /* + ** If the mouse is held over objects on the map, then help text may + ** pop up that tells what the object is. This call informs the help + ** system of the text name for the object under the mouse. + */ + if (object != NULL) { + int text; + int color = LTGREY; + + /* + ** Fetch the appropriate background color for help text. + */ + if (PlayerPtr->Is_Ally(object)) { + color = GREEN; + } else { + if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { + color = LTGREY; + } else { + color = PINK; + } + } + + /* + ** Fetch the name of the object. If it is an enemy object, then + ** the exact identity is glossed over with a generic text. + */ + text = object->Full_Name(); + if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) { + + if (!((TechnoClass *)object)->House->Is_Ally(PlayerPtr)) { +// if (!PlayerPtr->Is_Ally(object)) { + switch (object->What_Am_I()) { + case RTTI_INFANTRY: + text = TXT_ENEMY_SOLDIER; + break; + + case RTTI_UNIT: + text = TXT_ENEMY_VEHICLE; + break; + + case RTTI_BUILDING: + text = TXT_ENEMY_STRUCTURE; + break; + } + } + } + + if (/*Scen.Scenario > 3 ||*/ object->What_Am_I() != RTTI_TERRAIN) { + Help_Text(text, -1, -1, color); + } else { + Help_Text(TXT_NONE); + } + } else { + if ((*this)[cell].Land_Type() == LAND_TIBERIUM) { + Help_Text(TXT_MINERALS); + } else { + Help_Text(TXT_NONE); + } + } + } +#endif +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * * + * This routine is called when the left mouse button is released over the tactical map. * + * The release event is the workhorse of the game. Most actions occur at the moment of * + * mouse release. * + * * + * INPUT: cell -- The cell that the mouse is over. * + * * + * x,y -- The mouse pixel coordinate. * + * * + * object -- Pointer to the object that the mouse is over. * + * * + * action -- The action that the currently selected object (if any) will * + * perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 03/27/1995 JLB : Handles sell and repair actions. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall) +{ + if (PendingObjectPtr) { + /* + ** Try to place the pending object onto the map. + */ + if (ProximityCheck) { + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); + } else { + Speak(VOX_DEPLOY); + } + + } else { + + if (IsRubberBand) { + Refresh_Band(); + Select_These(XYP_Coord(BandX, BandY), XYP_Coord(x, y)); + + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + IsRubberBand = false; + IsTentative = false; + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + + } else { + + /* + ** Toggle the select state of the object. + */ + if (action == ACTION_TOGGLE_SELECT) { + if (!object || !CurrentObject.Count()) { + action = ACTION_SELECT; + } else { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } else { + object->Select(); + } + } + } + + /* + ** Selection of other object action. + */ + if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->Is_Selected_By_Player())) { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } + if (object->Select()) { + Unselect_All_Except(object); + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + } + } + + /* + ** If an action was detected as possible, then pass this action event + ** to all selected objects. + */ + if (action != ACTION_NONE && action != ACTION_SELECT && action != ACTION_TOGGLE_SELECT) { + + /* + ** Pass the action to all the selected objects. But first, redetermine + ** what action that object should perform. This, seemingly redundant + ** process, is necessary since multiple objects could be selected and each + ** might perform a different action when the click occurs. + */ + bool doflash = true; + AllowVoice = true; + FormMove = false; + FormSpeed = SPEED_WHEEL; + FormMaxSpeed = MPH_LIGHT_SPEED; + + if ( (action == ACTION_MOVE || action == ACTION_NOMOVE) && CurrentObject.Count()) { + + /* + ** Scan all units. If any are selected that shouldn't be, or aren't + ** selected but should be, then this is not a formation move. + */ + int group = 254; // init to invalid group # + + if (CurrentObject[0]->Is_Foot()) { + group = ((FootClass *)CurrentObject[0])->Group; + } + + /* + ** Presume this is a formation move unless something is detected + ** that will prevent it. + */ + FormMove = true; + + /* + ** First scan through all the selected units to make sure that they + ** are all of the same team and have been assigned a particular formation + */ + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tobject = CurrentObject[index]; + + /* + ** Only moveable (i.e., FootClass) objects can ever be in a formation + ** so if a selected object isn't of a FootClass type then it can't be + ** a formation move. + */ + if (tobject->Is_Foot() == false) { + FormMove = false; + break; + } + + /* + ** If the object is not part of the same team as the rest of the + ** selected group, or it just plain has never been assigned a + ** formation offset, then it can't be a formation move. + */ + FootClass const * foot = (FootClass *)tobject; + if (foot->Group != group || foot->XFormOffset == 0x80000000) { + FormMove = false; + break; + } + + /* + ** Determine the formation speed on the presumption that this + ** will turn out to be a formation move. + */ + MPHType maxspeed = foot->Techno_Type_Class()->MaxSpeed; + if (maxspeed < FormMaxSpeed) { + FormMaxSpeed = maxspeed; + FormSpeed = foot->Techno_Type_Class()->Speed; + } + } + + /* + ** Loop through all objects (that can theoretically be part of a team) and + ** if there are any that are part of the currently selected team, but + ** are not currently selected themselves, then this will force this move + ** to NOT be a formation move. + */ + if (FormMove) { + for (int index = 0; index < ::Logic.Count(); index++) { + ObjectClass const * obj = ::Logic[index]; + + /* + ** If the object is selected, then it has already been scanned + ** by the previous loop. + */ + if (obj->Is_Selected_By_Player()) continue; + + /* + ** Only consider footclass objects. + */ + if (!obj->Is_Foot()) continue; + + FootClass const * foot = (FootClass *)obj; + + /* + ** Only consider objects that are owned by the player. + */ + if (!foot->IsOwnedByPlayer) continue; + + /* + ** If another member of this team has been discovered and + ** it isn't selected, then the formation move cannot take + ** place. + */ + if (foot->Group == group) { + FormMove = false; + break; + } + } + } + } + + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass * tobject = CurrentObject[index]; + + if (object != NULL) { + tobject->Active_Click_With(tobject->What_Action(object), object); + } else { + + /* + ** Trap for formation moves: if this unit is part of a + ** formation (being part of a team qualifies) and they're + ** told to move, adjust the target destination so they stay + ** in formation when they arrive. + */ + CELL newmove = cell; + int whatami = tobject->What_Am_I(); + if (action == ACTION_MOVE && tobject->Is_Foot()) { + int oldisform; + FootClass * foot = (FootClass *)tobject; + oldisform = foot->IsFormationMove; + foot->IsFormationMove = FormMove; + if (FormMove && foot->Group != 255 ) { + newmove = foot->Adjust_Dest(cell); + } + foot->IsFormationMove = oldisform; + } + tobject->Active_Click_With(tobject->What_Action(cell), newmove); + } + AllowVoice = false; + } + AllowVoice = true; + FormMove = false; + + if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { + OutList.Add(EventClass(EventClass::REPAIR, TargetClass(object))); + } + if (action == ACTION_SELL_UNIT && object) { + switch (object->What_Am_I()) { + case RTTI_AIRCRAFT: + case RTTI_UNIT: + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + break; + + default: + break; + } + + } + if (action == ACTION_SELL) { + if (object) { + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + } else { + OutList.Add(EventClass(EventClass::SELLCELL, cell)); +// OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); + } + } + + if (action == ACTION_NUKE_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); + } + + if (action == ACTION_PARA_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_BOMB, cell)); + } + if (action == ACTION_PARA_INFANTRY) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_INFANTRY, cell)); + } + if (action == ACTION_SPY_MISSION) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SPY_MISSION, cell)); + } + if (action == ACTION_IRON_CURTAIN) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_IRON_CURTAIN, cell)); + } + if (action == ACTION_CHRONOSPHERE) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONOSPHERE, cell)); + } + if (action == ACTION_CHRONO2) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONO2, cell)); + } + } + + IsTentative = false; + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * * + * Handle the left mouse button press while over the tactical map. If it isn't is * + * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * + * mouse moves a sufficient distance from this recorded position, then rubber band mode * + * is officially started. * + * * + * INPUT: x,y -- The mouse coordinates at the time of the press. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Press(int x, int y) +{ + if (!IsRepairMode && !IsSellMode && IsTargettingMode == SPC_NONE && !PendingObject) { + IsTentative = true; + BandX = x; + BandY = y; + NewX = x; + NewY = y; + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * * + * This routine is called continuously while the left mouse button is held down over * + * the tactical map. This handles the rubber band mode detection and dragging. * + * * + * INPUT: x,y -- The mouse coordinate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Held(int x, int y) +{ + if (IsRubberBand) { + if (x != NewX || y != NewY) { + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + Refresh_Band(); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } else { + + /* + ** If the mouse is still held down while a tentative extended select is possible, then + ** check to see if the mouse has moved a sufficient distance in order to activate + ** extended select mode. + */ + if (IsTentative) { + + /* + ** The mouse must have moved a minimum distance before rubber band mode can be + ** initiated. + */ + if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { + IsRubberBand = true; + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + } + } + } +} + + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +extern int GlyphXClientSidebarWidthInLeptons; + +/*********************************************************************************************** + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * * + * This routine is used to set the tactical view position. The requested position is * + * clipped to the map dimensions as necessary. * + * * + * INPUT: coord -- The coordinate desired for the upper left corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Set_Tactical_Position(COORDINATE coord) +{ + /* + ** Bound the desired location to fit the legal map edges. + */ + int xx = 0;// (int)Coord_X(coord) - (int)Cell_To_Lepton(MapCellX); + int yy = 0;// (int)Coord_Y(coord) - (int)Cell_To_Lepton(MapCellY); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth) + GlyphXClientSidebarWidthInLeptons, Cell_To_Lepton(MapCellHeight)); // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +// Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + if (ScenarioInit) { + TacticalCoord = coord; + } + DesiredTacticalCoord = coord; + + IsToRedraw = true; + Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * * + * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * + * * + * INPUT: none * + * * + * OUTPUT: x, y -- Player starting location * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/28/1995 JLB : Commented. * + * 06/26/1995 JLB : Fixed building loop. * + * 10/20/1996 JLB : Doesn't wrap. * + *=============================================================================================*/ +void DisplayClass::Compute_Start_Pos(long& x, long& y) +{ + /* + ** Find the summation cell-x & cell-y for all the player's units, infantry, + ** and buildings. Buildings are weighted so that they count 16 times more + ** than units or infantry. + */ + x = 0; + y = 0; + long num = 0; + int i; + for (i = 0; i < Infantry.Count(); i++) { + InfantryClass * infp = Infantry.Ptr(i); + if (!infp->IsInLimbo && infp->Is_Owned_By_Player()) { + x += (long)Coord_XCell(infp->Coord); + y += (long)Coord_YCell(infp->Coord); + num++; + } + } + + for (i = 0; i < Units.Count(); i++) { + UnitClass * unitp = Units.Ptr(i); + if (!unitp->IsInLimbo && unitp->Is_Owned_By_Player()) { + x += (long)Coord_XCell(unitp->Coord); + y += (long)Coord_YCell(unitp->Coord); + num++; + } + } + + for (i = 0; i < Buildings.Count(); i++) { + BuildingClass * bldgp = Buildings.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { + x += (((long)Coord_XCell(bldgp->Coord)) * 16); + y += (((long)Coord_YCell(bldgp->Coord)) * 16); + num += 16; + } + } + + for (i = 0; i < Vessels.Count(); i++) { + VesselClass * bldgp = Vessels.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { + x += (((long)Coord_XCell(bldgp->Coord))); + y += (((long)Coord_YCell(bldgp->Coord))); + num++; + } + } + + /* + ** Divide each coord by 'num' to compute the average value + */ + if (num > 0) { + x /= num; + } else { + x = 0; + } + + if (num > 0) { + y /= num; + } else { + y = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * * + * This routine will control the sell mode for the player. * + * * + * INPUT: control -- The mode to set the sell state to. * + * 0 = Turn sell mode off. * + * 1 = Turn sell mode on. * + * -1 = Toggle sell mode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Sell_Mode_Control(int control) +{ + bool mode = IsSellMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsSellMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsSellMode && !PendingObject) { + IsRepairMode = false; + if (mode && PlayerPtr->BScan) { + IsSellMode = true; + Unselect_All(); + } else { + IsSellMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * * + * This routine is used to control the repair mode for the player. * + * * + * INPUT: control -- The mode to set the repair to. * + * 0 = Turn repair off. * + * 1 = Turn repair on. * + * -1= Toggle repair state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Repair_Mode_Control(int control) +{ + bool mode = IsRepairMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsRepairMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsRepairMode && !PendingObject) { + IsSellMode = false; + if (mode && PlayerPtr->BScan) { + IsRepairMode = true; + Unselect_All(); + } else { + IsRepairMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * * + * Use this routine to determine if the specified cell is visible on * + * the display. This is a useful fact, since many display operations * + * can be skipped if the cell is not visible. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: bool; Is this cell visible on the display? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DisplayClass::In_View(register CELL cell) const +{ + if (cell & 0xC000) return(false); + + COORDINATE coord = Coord_Whole(Cell_Coord(cell)); + COORDINATE tcoord = Coord_Whole(TacticalCoord); + + if ((Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+CELL_LEPTON_W-1) return(false); + if ((Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+CELL_LEPTON_H-1) return(false); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * * + * Use this routine to find the sub cell spot closest to the coordinate specified that is * + * free from occupation. Typical use of this is for infantry destination calculation. * + * * + * INPUT: coord -- The coordinate to use as the starting point when finding the closest * + * free spot. * + * * + * any -- Ignore occupation and just return the closest sub cell spot? * + * * + * OUTPUT: Returns with the coordinate of the closest free (possibly) sub cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + if (coord & HIGH_COORD_MASK) { + return(0x00800080); + } + return Map[coord].Closest_Free_Spot(coord, any); +} + + +/*********************************************************************************************** + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * * + * Use this routine to determine if the coordinate (rounded to the nearest sub cell * + * position) is free for placement. Typical use of this would be for infantry placement. * + * * + * INPUT: coord -- The coordinate to examine for "freeness". The coordinate is rounded to * + * the nearest free sub cell spot. * + * * + * OUTPUT: Is the sub spot indicated by the coordinate free from previous occupation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Is_Spot_Free(COORDINATE coord) const +{ + // This can't be right. Copy/paste error, maybe? ST - 5/8/2019 + //if (coord & HIGH_COORD_MASK) { + // return(0x00800080); + //} + return Map[coord].Is_Spot_Free(CellClass::Spot_Index(coord)); +} + + +/*********************************************************************************************** + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * * + * This routine will average the position of all the selected objects and then center * + * the map about those objects. * + * * + * INPUT: center -- The is an optional center about override coordinate. If specified, * + * then the map will be centered about that coordinate. Otherwise it * + * will center about the average location of all selected objects. * + * * + * OUTPUT: The center coordinate. * + * * + * WARNINGS: The map position changes by this routine. * + * * + * HISTORY: * + * 08/22/1995 JLB : Created. * + * 09/16/1996 JLB : Takes coordinate to center about (as override). * + *=============================================================================================*/ +COORDINATE DisplayClass::Center_Map(COORDINATE center) +{ + int x = 0; +// unsigned x = 0; + int y = 0; +// unsigned y = 0; + bool centerit = false; + + if (CurrentObject.Count()) { + + for (int index = 0; index < CurrentObject.Count(); index++) { + COORDINATE coord = CurrentObject[index]->Center_Coord(); + + x += Coord_X(coord); + y += Coord_Y(coord); + } + + x /= CurrentObject.Count(); + y /= CurrentObject.Count(); + centerit = true; + } + + if (center != 0L) { + x = Coord_X(center); + y = Coord_Y(center); + centerit = true; + } + + if (centerit) { + center = XY_Coord(x, y); + + x = x - (int)TacLeptonWidth/2; + if (x < Cell_To_Lepton(MapCellX)) x = Cell_To_Lepton(MapCellX); + + y = y - (int)TacLeptonHeight/2; + if (y < Cell_To_Lepton(MapCellY)) y = Cell_To_Lepton(MapCellY); + + Set_Tactical_Position(XY_Coord(x, y)); + + return center; + } + + return 0; +} + + +/*********************************************************************************************** + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * * + * This routine will cause the shadow to creep back by one cell. Multiple calls to this * + * routine will result in the shadow becoming more and more invasive until only the sight * + * range of player controlled units will keep the shadow pushed back. * + * * + * INPUT: none * + * house -- Player to apply shroud to * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 JLB : Created. * + * 08/06/2019 ST: Added house parameter for multiplayer * + *=============================================================================================*/ +void DisplayClass::Encroach_Shadow(HouseClass * house) +{ + CELL cell; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Is_Visible(house) || !cellptr->Is_Mapped(house)) continue; + + cellptr->IsToShroud = true; // IsToShroud isn't used outside this function. ST - 8/6/2019 2:28PM + } + + /* + ** Mark all shadow edge cells to be fully shrouded. All adjacent mapped + ** cell should become partially shrouded. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + if ((*this)[cell].IsToShroud) { + (*this)[cell].IsToShroud = false; + Shroud_Cell(cell, house); + } + } + + All_To_Look(house); + + Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * * + * This routine is called to add the shroud back to the cell specified. Typical of this * + * would be when the shroud is to regenerate. * + * * + * INPUT: cell -- The cell that the shroud is to be regenerated upon. * + * house -- Player to apply shroud to * + * * + * OUTPUT: none * + * * + * WARNINGS: Adjacent cells might be affected by this routine. The affect is determined * + * according to the legality of the partial shadow artwork. In the illegal cases * + * the adjacent cell might become shrouded as well. * + * * + * HISTORY: * + * 10/17/1995 JLB : Created. * + * 06/17/1996 JLB : Modified to handle the new shadow pieces. * + * 08/06/2019 ST: Added house parameter for multiplayer * + *=============================================================================================*/ +void DisplayClass::Shroud_Cell(CELL cell, HouseClass * house) +{ + if (house->IsGPSActive) { + if ( (*this)[cell].Is_Jamming(house) ) { + return; + } + } + if (!In_Radar(cell)) return; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Is_Mapped(house)) { + + cellptr->Set_Mapped(house, false); + cellptr->Set_Visible(house, false); + cellptr->Redraw_Objects(); + + /* + ** Check adjacent cells. There might be some weird combination of + ** shrouded cells such that more cells must be shrouded in order for + ** this to work. + */ + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + CELL c = Adjacent_Cell(cell, dir); + CellClass * cptr = &(*this)[c]; + + /* + ** If this adjacent cell must be completely shrouded as a result + ** of the map change, yet it isn't already shrouded, then recursively + ** shroud that cell. + */ + if (c != cell) { + cptr->Set_Visible(house, false); + } + + /* + ** Always redraw the cell because, more than likely, the shroud + ** edge will change shape because of the map change. + */ + cptr->Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * * + * This routine is used to read the map control data from the INI * + * file. * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The TriggerClass INI data must have been read before calling this function. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Read_INI(CCINIClass & ini) +{ + /* + ** Read the map dimensions. + */ + char const * const name = "Map"; + int x = ini.Get_Int(name, "X", 1); + int y = ini.Get_Int(name, "Y", 1); + int w = ini.Get_Int(name, "Width", MAP_CELL_W-2); + int h = ini.Get_Int(name, "Height", MAP_CELL_H-2); + +#ifndef FIXIT_VERSION_3 // Map size no longer restricted. + +#ifdef FIXIT_CSII // checked - ajw + if(Session.Type >= GAME_MODEM && Session.Type <= GAME_INTERNET && PlayingAgainstVersion < VERSION_AFTERMATH_CS) { + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + ** BG Note: only do this for multiplayer games against non-AfterMath. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } + } +#else + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } +#endif + +#endif // !FIXIT_VERSION_3 + + Set_Map_Dimensions( x, y, w, h ); + + /* + ** The theater is determined at this point. There is specific data that + ** is custom to this data. Load the custom data (as it related to terrain) + ** at this point. + */ + Scen.Theater = ini.Get_TheaterType(name, "Theater", THEATER_TEMPERATE); + if (Scen.Theater == THEATER_NONE) { + Scen.Theater = THEATER_TEMPERATE; + } + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 + if (Scen.Theater != LastTheater) { + Reset_Theater_Shapes(); + } +#endif //WIN32 + + /* + ** Now that the theater is known, init the entire map hierarchy + */ + Init(Scen.Theater); + + /* + ** Special initializations occur when the theater is known. + */ + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + /* + ** Read the Waypoint entries. + */ + for (int i = 0; i < WAYPT_COUNT; i++) { + char buf[20]; + sprintf(buf, "%d", i); + Scen.Waypoint[i] = ini.Get_Int("Waypoints", buf, -1); + + if (Scen.Waypoint[i] != -1) { + (*this)[Scen.Waypoint[i]].IsWaypoint = 1; + } + } + + /* + ** Set the starting position (do this after Init(), which clears the cells' + ** IsWaypoint flags). + */ + if (Scen.Waypoint[WAYPT_HOME] == -1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + 5*RESFACTOR, MapCellY + 4*RESFACTOR); + } + + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); + + /* + ** Loop through all CellTrigger entries. + */ + int len = ini.Entry_Count("CellTriggers"); + for (int index = 0; index < len; index++) { + + /* + ** Get a cell trigger and cell assignment. + */ + char const * cellentry = ini.Get_Entry("CellTriggers", index); + TriggerTypeClass * tp = ini.Get_TriggerType("CellTriggers", cellentry); + CELL cell = atoi(cellentry); + + if (tp != NULL && !(*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + tt->Cell = cell; + (*this)[cell].Trigger = tt; + } + } + } + + /* + ** Read the map template data. + */ + static char const * const MAPPACK = "MapPack"; + len = ini.Get_UUBlock(MAPPACK, _staging_buffer, sizeof(_staging_buffer)); + BufferStraw bstraw(_staging_buffer, len); + Map.Read_Binary(bstraw); + + LastTheater = Scen.Theater; +} + + +/*********************************************************************************************** + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * * + * This routine will output all the data of this map to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI handler to store the map data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: Any existing map data in the INI database will be replaced by this function. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Write_INI(CCINIClass & ini) +{ + char entry[20]; + + /* + ** Save the map parameters. + */ + static char const * const NAME = "Map"; + ini.Clear(NAME); + ini.Put_TheaterType(NAME, "Theater", Scen.Theater); + ini.Put_Int(NAME, "X", MapCellX); + ini.Put_Int(NAME, "Y", MapCellY); + ini.Put_Int(NAME, "Width", MapCellWidth); + ini.Put_Int(NAME, "Height", MapCellHeight); + + /* + ** Save the Waypoint entries. + */ + static char const * const WAYNAME = "Waypoints"; + ini.Clear(WAYNAME); + for (int i = 0; i < WAYPT_COUNT; i++) { + if (Scen.Waypoint[i] != -1) { + sprintf(entry, "%d", i); + ini.Put_Int(WAYNAME, entry, Scen.Waypoint[i]); + } + } + + /* + ** Save the cell's triggers. + */ + static char const * const CELLTRIG = "CellTriggers"; + ini.Clear(CELLTRIG); + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tp = (*this)[cell].Trigger; + if (tp != NULL) { + + /* + ** Generate entry name. + */ + sprintf(entry, "%d", cell); + + /* + ** Save entry. + */ + ini.Put_TriggerType(CELLTRIG, entry, tp->Class); + } + } + } + + /* + ** Write the map template data out to the ini file. + */ + static char const * const MAPPACK = "MapPack"; + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + int len = Map.Write_Binary(bpipe); + ini.Clear(MAPPACK); + if (len) { + ini.Put_UUBlock(MAPPACK, _staging_buffer, len); + } +} + + +/*********************************************************************************************** + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * * + * This routine will scan through all objects and tell them to look if they are supposed * + * to be able to reveal the map for the player. This routine may be necessary in cases * + * of gap generator reshroud logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 08/06/2019 ST : Added house parameter so it can work for multiple players * + *=============================================================================================*/ +void DisplayClass::All_To_Look(HouseClass *house, bool units_only) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (tech->House == house) { + if (tech->Is_Discovered_By_Player(house)) { + object->Look(); + } + } else { + //if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)) { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house)) { + tech->Look(); + } + } + } + } +} + + +/* +** Added house parameter for client/server multiplayer. ST - 8/12/2019 11:48AM +** +** +*/ +void DisplayClass::Constrained_Look(COORDINATE center, LEPTON distance, HouseClass *house) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + +// if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (tech->House->IsPlayerControl) { + if (tech->IsDiscoveredByPlayer && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + object->Look(); + } + } else { + + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr) && + Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + + tech->Look(); + } + } + } else { + + if (tech->House->IsHuman) { + + if (tech->House == house) { + if (tech->Is_Discovered_By_Player(house) && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + object->Look(); + } + } else { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house) && + Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + tech->Look(); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * * + * This will flag the cell to be redrawn. * + * * + * INPUT: cell -- The cell to be flagged. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Flag_Cell(CELL cell) +{ + Flag_To_Redraw(false); + IsToRedraw = true; + CellRedraw[cell] = true; +} + +static ActionType _priority_actions[] = { + ACTION_ATTACK, + ACTION_ENTER, + ACTION_HEAL, + ACTION_REPAIR, + ACTION_SABOTAGE, + ACTION_CAPTURE, + ACTION_MOVE +}; + +static int get_action_priority(ActionType action) +{ + for (int i = 0; i < ARRAY_LENGTH(_priority_actions); ++i) { + if (_priority_actions[i] == action) { + return i; + } + } + return INT_MAX; +} + +template +static int index_of(const DynamicVectorClass& list, T* object) +{ + for (int i = 0; i < list.Count(); i++) { + if (list[i] == object) { + return i; + } + } + return -1; +} + +template +static ObjectClass* Best_Object_With_ActionT(DynamicVectorClass& objects, T subject) +{ + DynamicVectorClass checked_types; + + if (objects.Count()) { + int best_priority = INT_MAX; + ObjectClass* best_object = objects[0]; + for (int i = 0; i < objects.Count(); ++i) { + ObjectClass* object = objects[i]; + const ObjectTypeClass* type = &object->Class_Of(); + if (index_of(checked_types, type) != -1) { + continue; + } + checked_types.Add(type); + ActionType action = object->What_Action(subject); + int priority = get_action_priority(action); + if (priority < best_priority) { + best_priority = priority; + best_object = object; + if (best_priority == 0) { + break; + } + } + } + return best_object; + } + return NULL; +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, const ObjectClass* object) +{ + return Best_Object_With_ActionT(objects, object); +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell) +{ + return Best_Object_With_ActionT(objects, cell); +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, const ObjectClass* object) +{ + ObjectClass* obj = Best_Object_With_Action(objects, object); + return (obj != NULL) ? obj->What_Action(object) : ACTION_NONE; +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell) +{ + ObjectClass* obj = Best_Object_With_Action(objects, cell); + return (obj != NULL) ? obj->What_Action(cell) : ACTION_NONE; +} + +ObjectClass* Best_Object_With_Action(const ObjectClass* object) +{ + return Best_Object_With_Action(CurrentObject.Raw(), object); +} + +ObjectClass* Best_Object_With_Action(CELL cell) +{ + return Best_Object_With_Action(CurrentObject.Raw(), cell); +} + +ActionType Best_Object_Action(const ObjectClass* object) +{ + return Best_Object_Action(CurrentObject.Raw(), object); +} + +ActionType Best_Object_Action(CELL cell) +{ + return Best_Object_Action(CurrentObject.Raw(), cell); +} \ No newline at end of file diff --git a/REDALERT/DISPLAY.H b/REDALERT/DISPLAY.H new file mode 100644 index 000000000..e2fae5b06 --- /dev/null +++ b/REDALERT/DISPLAY.H @@ -0,0 +1,337 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DISPLAY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 1, 1994 * + * * + * Last Update : May 1, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "map.h" +#include "layer.h" + + +#define ICON_PIXEL_W 24 +#define ICON_PIXEL_H 24 +#define ICON_LEPTON_W 256 +#define ICON_LEPTON_H 256 +#define CELL_PIXEL_W ICON_PIXEL_W +#define CELL_PIXEL_H ICON_PIXEL_H +#define CELL_LEPTON_W ICON_LEPTON_W +#define CELL_LEPTON_H ICON_LEPTON_H + +// ----------------------------------------------------------- +#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W) +#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H) + +#define SIDE_BAR_TAC_WIDTH 10 +#define SIDE_BAR_TAC_HEIGHT 8 + +extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2); + +class DisplayClass: public MapClass +{ + public: + friend class DLLExportClass; // ST - 5/13/2019 + + /* + ** The tactical map display position is indicated by the cell of the + ** upper left hand corner. These should not be altered directly. Use + ** the Set_Tactical_Position function instead. + */ + COORDINATE TacticalCoord; + + /* + ** The dimensions (in cells) of the visible window onto the game map. This tactical + ** map is how the player interacts and views the game world. + */ + LEPTON TacLeptonWidth; + LEPTON TacLeptonHeight; + + /* + ** These layer control elements are used to group the displayable objects + ** so that proper overlap can be obtained. + */ + static LayerClass Layer[LAYER_COUNT]; + + /* + ** This records the position and shape of a placement cursor to display + ** over the map. This cursor is used when placing buildings and also used + ** extensively by the scenario editor. + */ + CELL ZoneCell; + short ZoneOffset; + short const *CursorSize; + short CursorShapeSave[256]; // For save/load + bool ProximityCheck; // Is proximity check ok? + + /* + ** This holds the building type that is about to be placed upon the map. + ** It is only valid during the building placement state. The PendingLegal + ** flag is updated as the cursor moves and it reflects the legality of + ** placing the building at the desired location. + */ + ObjectClass * PendingObjectPtr; + ObjectTypeClass const * PendingObject; + HousesType PendingHouse; + + static unsigned char FadingBrighten[256]; + static unsigned char FadingShade[256]; + static unsigned char FadingWayDark[256]; + static unsigned char FadingLight[256]; + static unsigned char FadingGreen[256]; + static unsigned char FadingYellow[256]; + static unsigned char FadingRed[256]; + static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256]; + static unsigned char WhiteTranslucentTable[(1+1)*256]; + static unsigned char MouseTranslucentTable[(4+1)*256]; + static void const *TransIconset; + static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256]; + static unsigned char UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; + static unsigned char SpecialGhost[2*256]; + + //------------------------------------------------------------------------- + DisplayClass(void); + DisplayClass(NoInitClass const & x) : MapClass(x) {}; + + virtual void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** General display/map/interface support functionality. + */ + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + /* + ** Added functionality. + */ + void All_To_Look(HouseClass *house, bool units_only=false); // Added house parameter so it can work for multiple players. ST - 8/6/2019 2:30PM + void Constrained_Look(COORDINATE coord, LEPTON distance, HouseClass *house); // Added house parameter for client/server multiplayer. ST - 8/12/2019 3:25PM + void Shroud_Cell(CELL cell, HouseClass *house); + void Encroach_Shadow(HouseClass *house); + COORDINATE Center_Map(COORDINATE center=0L); + virtual bool Map_Cell(CELL cell, HouseClass *house, bool check_radar_spied = true, bool and_for_allies = true); // Added check_radar_spied parameter to prevent recursion. ST - 8/6/2019 10:16AM. Added and_for_allies ST - 10/31/2019 1:18PM + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false) {}; + virtual MouseType Get_Mouse_Shape(void) const = 0; + virtual bool Scroll_Map(DirType facing, int & distance, bool really); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1); + + /* + ** Pending object placement control. + */ + virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system. + void Cursor_Mark(CELL pos, bool on); + void Set_Cursor_Shape(short const * list); + CELL Set_Cursor_Pos(CELL pos = -1); + void Get_Occupy_Dimensions(int & w, int & h, short const *list) const; + + /* + ** Tactical map only functionality. + */ + virtual void Set_Tactical_Position(COORDINATE coord); + void Refresh_Band(void); + void Select_These(COORDINATE coord1, COORDINATE coord2, bool additive = false); + COORDINATE Pixel_To_Coord(int x, int y) const; + bool Coord_To_Pixel(COORDINATE coord, int & x, int & y) const; + bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest); + void Remove(ObjectClass const * object, LayerType layer); + void Submit(ObjectClass const * object, LayerType layer); + CELL Calculated_Cell(SourceType dir, WAYPOINT waypoint=-1, CELL cell=-1, SpeedType loco=SPEED_FOOT, bool zonecheck=true, MZoneType mzone=MZONE_NORMAL) const; + bool In_View(register CELL cell) const; + bool Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const; + ObjectClass * Cell_Object(CELL cell, int x=0, int y=0) const; + ObjectClass * Next_Object(ObjectClass * object) const; + ObjectClass * Prev_Object(ObjectClass * object) const; + int Cell_Shadow(CELL cell, HouseClass *house) const; + short const * Text_Overlap_List(char const * text, int x, int y) const; + bool Is_Spot_Free(COORDINATE coord) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + void Sell_Mode_Control(int control); + void Repair_Mode_Control(int control); + + virtual void Flag_Cell(CELL cell); + bool Is_Cell_Flagged(CELL cell) const {return CellRedraw.Is_True(cell);}; + + /* + ** Computes starting position based on player's units' Coords. + */ + void Compute_Start_Pos(long& x, long& y); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + virtual void Mouse_Right_Press(void); + virtual void Mouse_Left_Press(int x, int y); + virtual void Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall = false); + virtual void Mouse_Left_Held(int x, int y); + virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall = false); + + public: + /* + ** This is the pixel offset for the upper left corner of the tactical map. + */ + int TacPixelX; + int TacPixelY; + + /* + ** This is the coordinate that the tactical map should be in at next available opportunity. + */ + COORDINATE DesiredTacticalCoord; + + /* + ** If something in the tactical map is to be redrawn, this flag is set to true. + */ + unsigned IsToRedraw:1; + + /* + ** If the player is currently wielding a wrench (to select buildings for repair), + ** then this flag is true. In such a state, normal movement and combat orders + ** are preempted. + */ + unsigned IsRepairMode:1; + + /* + ** If the player is currently in "sell back" mode, then this flag will be + ** true. While in this mode, anything clicked on will be sold back to the + ** "factory". + */ + unsigned IsSellMode:1; + + /* + ** If the player is currently in ion cannon targeting mode, then this + ** flag will be true. While in this mode, anything clicked on will be + ** be destroyed by the ION cannon. + */ + SpecialWeaponType IsTargettingMode; + + protected: + + /* + ** If it is currently in rubber band mode (multi unit selection), then this + ** flag will be true. While in such a mode, normal input is preempted while + ** the extended selection is in progress. + */ + unsigned IsRubberBand:1; + + /* + ** The moment the mouse is held down, this flag gets set. If the mouse is dragged + ** a sufficient distance while held down, then true rubber band mode selection + ** can begin. Using a minimum distance prevents accidental rubber band selection + ** mode from being initiated. + */ + unsigned IsTentative:1; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + int Selection_At_Mouse(unsigned flags, KeyNumType & key); + int Command_Object(unsigned flags, KeyNumType & key); + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ +public: //ST - 1/21/2019 11:59AM + static TacticalClass TacButton; + + private: + + /* + ** This is a utility flag that is set during the icon draw process only if there + ** was at least one shadow icon detected that should be redrawn. When the shadow + ** drawing logic is to take place, but this flag is false, then the shadow drawing + ** will be skipped since it would perform no function. + */ + unsigned IsShadowPresent:1; + + /* + ** Rubber band mode consists of stretching a box from the anchor point (specified + ** here) to the current cursor position. + */ + int BandX,BandY; + int NewX,NewY; + + static void const *ShadowShapes; + static unsigned char ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + + void Redraw_Icons(void); + void Redraw_OIcons(void); + void Redraw_Shadow(void); + + /* + ** This bit array is used to flag cells to be redrawn. If the icon needs to + ** be redrawn for a cell, then the corresponding flag will be true. + */ + static BooleanVectorClass CellRedraw; + + bool Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const; + + // + // We need a way to bypass visible view checks when we are running in the context of GlyphX without using the + // internal C&C renderer. We shouldn't know or care what the user is actually looking at + // ST - 4/17/2019 9:01AM + // + static bool IgnoreViewConstraints; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; + +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/DLLInterface.cpp b/REDALERT/DLLInterface.cpp new file mode 100644 index 000000000..0842ddb31 --- /dev/null +++ b/REDALERT/DLLInterface.cpp @@ -0,0 +1,8605 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** DLLInterfac.cpp +** +** This is where we implement the API expected by the Instance Server. +** +** The Instance Server will pass in requests for loading and starting maps, control input from players, +** and requests for game simulation and rendering states. +** +** +*/ + +// Exception handling isn't enabled +#pragma warning (disable : 4530) //warning C4530: C++ exception handler used, but unwind semantics are not enabled. + +#include +#include +#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" + +#include + + + +/* +** Externs +*/ +extern int DLL_Startup(const char * command_line); +extern void Reallocate_Big_Shape_Buffer(void); +extern bool ProgEndCalled; +extern int Write_PCX_File(char* name, GraphicViewPortClass& pic, unsigned char* palette ); +extern void Color_Cycle(void); + +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum); +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags = 0, void const * ghostdata = NULL); + +typedef void (__cdecl* CNC_Event_Callback_Type)(const EventCallbackStruct &event); +typedef unsigned __int64 uint64; +typedef __int64 int64; + + + + +/* +** Audio defines +** +** +** +** +** +*/ +// For compatibility with Watcom in audio enums +#pragma warning (disable : 4091) + +// From RedAlert\Audio.cpp +enum ContextType; +extern struct SoundEffectNameStruct { + char const *Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT]; + +// From RedAlert\Audio.cpp +extern char const* Speech[VOX_COUNT]; + +// From RedAlert\Audio.cpp +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +}; + + + + +/* +** Misc defines +** +** +** +** +** +*/ +#define GAME_TO_PLAY Session.Type +#define MULTIPLAYER_COUNT Session.Players.Count() +#define KEYBOARD Keyboard + +#define RANDOM_START_POSITION 0x7f + + + + +/* +** DLL Interface +** +** +** +** +** +*/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in); +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback); +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules); +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, const char* scenario_name, int build_level, bool multiplayer); +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); +extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum mouse_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy); +extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players); +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty); +extern "C" __declspec(dllexport) void __cdecl CNC_Restore_Carryover_Objects(const CarryoverObjectStruct* objects); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time); + + + + +/* +** Class to implement the interface, and contain additional game state required by the conversion from peer/peer to client/server +** +** +** +** +** +*/ +class DLLExportClass { + public: + + static void Init(void); + static void Shutdown(void); + static void Config(const CNCRulesDataStruct& rules); + static void Add_Mod_Path(const char *mod_path); + static void Set_Home_Cell(int x, int y, uint64 player_id); + static void Set_Content_Directory(const char *dir); + + static bool Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Start_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Hold_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Start_Placement(uint64 player_id, int buildable_type, int buildable_id); + static BuildingClass *Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id); + static bool Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static void Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out); + static void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE); + static void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); + static bool Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); + static bool Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id); + static bool Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y); + static bool Create_Control_Group(unsigned char control_group_index); + static bool Add_To_Control_Group(unsigned char control_group_index); + static bool Toggle_Control_Group_Selection(unsigned char control_group_index); + static bool Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance); + static void Calculate_Start_Positions(void); + static void Computer_Message(bool last_player_taunt); + + static void Repair_Mode(uint64 player_id); + static void Repair(uint64 player_id, int object_id); + static void Sell_Mode(uint64 player_id); + static void Sell(uint64 player_id, int object_id); + static void Repair_Sell_Cancel(uint64 player_id); + + static void Scatter_Selected(uint64 player_id); + static void Select_Next_Unit(uint64 player_id); + static void Select_Previous_Unit(uint64 player_id); + static void Selected_Guard_Mode(uint64 player_id); + static void Selected_Stop(uint64 player_id); + static void Team_Units_Formation_Toggle_On(uint64 player_id); + static void Units_Queued_Movement_Toggle(uint64 player_id, bool toggle); + + static void Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output); + static bool Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + + + static void Set_Event_Callback(CNC_Event_Callback_Type event_callback) {EventCallback = event_callback;} + static void Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy = false); + static void Debug_Spawn_All(int x, int y); + static bool Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y); + static void Debug_Kill_Unit(int x, int y); + static void Debug_Heal_Unit(int x, int y); + + static void On_Play_Movie(const char * movie_name, ThemeType theme, bool immediate); + static void On_Display_Briefing_Text(); + + static void On_Sound_Effect(const HouseClass* player_ptr, int sound_effect_index, const char* extension, int variation, COORDINATE coord); + static void On_Speech(const HouseClass* player_ptr, int speech_index); + static void On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id); + static void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name); + static void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + static void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + + static void On_Game_Over(uint64 glyphx_player_id, bool player_wins); + static void On_Multiplayer_Game_Over(void); + + static void On_Debug_Output(const char *debug_text); + + static void On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); + + static void On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y); + + static void Glyphx_Queue_AI(); + + static void Store_Carryover_Objects(); + + static void Force_Human_Team_Wins(uint64 quitting_player_id); + + /* + ** Player context switching for input/output + */ + static bool Set_Player_Context(uint64 glyphx_player, bool force = false); + static void Reset_Player_Context(void); + static void Adjust_Internal_View(bool force_ignore_view_constraints = false); + static void Logic_Switch_Player_Context(ObjectClass *object); + static void Logic_Switch_Player_Context(HouseClass *house); + static void Refresh_Player_Control_Flags(void); + static __int64 Get_GlyphX_Player_ID(const HouseClass *house); + + static void Recalculate_Placement_Distances(); + + static void Reset_Sidebars(void); + + static SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr = NULL); + + static uint64 GlyphxPlayerIDs[MAX_PLAYERS]; + + + + static const void *Get_Shadow_Shapes(void) {return Map.ShadowShapes;} + static const unsigned char *Get_Shadow_Trans(void) {return &Map.ShadowTrans[0];} + + static bool Legacy_Render_Enabled(void); + + static bool Get_Input_Key_State(KeyNumType key); + + static void Set_Special_Key_Flags(unsigned char special_key_flags); + static void Clear_Special_Key_Flags(); + + static bool Load(Straw &file); + static bool Save(Pipe &file); + static void Code_Pointers(void); + static void Decode_Pointers(void); + + static bool Get_Game_Over() { return GameOver; } + + private: + static void Calculate_Single_Player_Score(EventCallbackStruct&); + + static int RA_Calculate_Leadership( HousesType player_house, int units_lost, int buildings_lost ); + static int RA_Calculate_Economy( long available_money, int stolen_buildings_credits, unsigned harvested_credits, long initial_credits ); + static int RA_Calculate_Score( int uspoints, int leadership, int economy ); + + static void Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type); + static void Convert_Special_Weapon_Type(SpecialWeaponType weapon_type, DllSuperweaponTypeEnum& dll_weapon_type, char* weapon_name); + static void Fill_Sidebar_Entry_From_Special_Weapon(CNCSidebarEntryStruct& sidebar_entry_out, SuperClass*& super_weapon_out, SpecialWeaponType weapon_type); + + static void Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance); + + static int CurrentDrawCount; + static int TotalObjectCount; + static int SortOrder; + static CNCObjectListStruct *ObjectList; + + static CNC_Event_Callback_Type EventCallback; + + + static int CurrentLocalPlayerIndex; + + static bool GameOver; + + static std::set MessagesSent; + + /* + ** Pseudo sidebars for players in multiplayer + */ + static SidebarGlyphxClass MultiplayerSidebars[MAX_PLAYERS]; + + static CELL MultiplayerStartPositions[MAX_PLAYERS]; + + static BuildingTypeClass *PlacementType[MAX_PLAYERS]; + + static unsigned char PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; + + static unsigned char SpecialKeyFlags[MAX_PLAYERS]; + + /* + ** Mod directories + */ + static DynamicVectorClass ModSearchPaths; + +}; + + + +/* +** DLLExportClass static data +** +** +** +** +** +*/ +int DLLExportClass::CurrentDrawCount = 0; +int DLLExportClass::TotalObjectCount = 0; +int DLLExportClass::SortOrder = 0; +CNCObjectListStruct *DLLExportClass::ObjectList = NULL; +SidebarGlyphxClass DLLExportClass::MultiplayerSidebars [MAX_PLAYERS]; +uint64 DLLExportClass::GlyphxPlayerIDs[MAX_PLAYERS] = {0xffffffffl}; +int DLLExportClass::CurrentLocalPlayerIndex = -1; +CELL DLLExportClass::MultiplayerStartPositions[MAX_PLAYERS]; +BuildingTypeClass *DLLExportClass::PlacementType[MAX_PLAYERS]; +unsigned char DLLExportClass::PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; +unsigned char DLLExportClass::SpecialKeyFlags[MAX_PLAYERS] = { 0U }; +DynamicVectorClass DLLExportClass::ModSearchPaths; +std::set DLLExportClass::MessagesSent; +bool DLLExportClass::GameOver = false; + + + +/* +** Global variables +** +** +** +** +** +*/ +int DLLForceMouseX = 0; +int DLLForceMouseY = 0; + +CNC_Event_Callback_Type DLLExportClass::EventCallback = NULL; + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +int GlyphXClientSidebarWidthInLeptons = 0; + +bool MPlayerIsHuman[MAX_PLAYERS]; +int MPlayerTeamIDs[MAX_PLAYERS]; +int MPlayerStartLocations[MAX_PLAYERS]; + +bool MPSuperWeaponDisable = false; +bool ShareAllyVisibility = true; +bool UseGlyphXStartLocations = true; + + +int GetRandSeed() +{ + using namespace std::chrono; + time_point time_since_epoch = system_clock::now(); + auto microseconds_since_epoch = floor(time_since_epoch); + + return abs( static_cast(microseconds_since_epoch.time_since_epoch().count()) ); +} + + + +void Play_Movie_GlyphX(const char * movie_name, ThemeType theme, bool immediate = false) +{ + if ((movie_name[0] == 'x' || movie_name[0] == 'X') && movie_name[1] == 0) { + return; + } + + DLLExportClass::On_Play_Movie(movie_name, theme, immediate); +} + +void Display_Briefing_Text_GlyphX() +{ + DLLExportClass::On_Display_Briefing_Text(); +} + + +void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house) +{ + // MBL 06.17.2019 + int voc = sound_index; + if (voc == VOC_NONE) + { + return; + } + + // MBL 06.17.2019 - Borrowed from RedAlert\AUDIO.CPP Sound_Effect() + // + #if 1 + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ""; // ".AUD"; + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** If there is no forced house, then use the current player + ** act like house. + */ + if (house == HOUSE_NONE) { + house = PlayerPtr->ActLike; + } + + /* + ** Change the extension based on the variation and house accent requested. + */ + if (((1 << house) & HOUSEF_ALLIES) != 0) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } else { + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".R00"; + } else { + ext = ".R02"; + } + } else { + if (variation % 2) { + ext = ".R01"; + } else { + ext = ".R03"; + } + } + } + } + #endif + // END MBL + + DLLExportClass::On_Sound_Effect(PlayerPtr, sound_index, ext, variation, coord); + + +#if 0 + // MBL 02.26.2019 + int voc = sound_index; + if (voc == VOC_NONE) + { + return; + } + + // MBL 02.26.2019 - Borrowed from AUDIO.CPP Sound_Effect() + // + char const * ext = ""; // ".AUD"; +#ifdef TIBERIAN_DAWN + if (Special.IsJuvenile && SoundEffectName[voc].Where == IN_JUV) { + ext = ".JUV"; + } else { +#endif + if (SoundEffectName[voc].Where == IN_VAR) { + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } +#ifdef TIBERIAN_DAWN + } +#endif + // END MBL + + DLLExportClass::On_Sound_Effect(PlayerPtr, sound_index, ext, variation, coord); +#endif +} + + +// void On_Speech(int speech_index) // MBL 02.06.2020 +void On_Speech(int speech_index, HouseClass *house) +{ + // DLLExportClass::On_Speech(PlayerPtr, speech_index); // MBL 02.06.2020 + if (house == NULL) { + DLLExportClass::On_Speech(PlayerPtr, speech_index); + } + else + { + DLLExportClass::On_Speech(house, speech_index); + } +} + + +void On_Message(const char* message, float timeout_seconds, int64 id) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_DIRECT, id); +} + +void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name) +{ + DLLExportClass::On_Update_Map_Cell(cell_x, cell_y, template_type_name); +} + +void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type) +{ + DLLExportClass::On_Special_Weapon_Targetting(player_ptr, weapon_type); +} + +void On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + DLLExportClass::On_Ping(player_ptr, coord); +} + +void On_Defeated_Message(const char* message, float timeout_seconds) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_PLAYER_DEFEATED, -1); +} + +void GlyphX_Debug_Print(const char *debug_text) +{ + DLLExportClass::On_Debug_Output(debug_text); +} + +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + DLLExportClass::On_Achievement(player_ptr, achievement_type, achievement_reason); +} + + + + + +/************************************************************************************************** +* CNC_Version -- Check DLL/Server version +* +* In: Version expected +* +* Out: Actual version +* +* +* +* History: 4/9/2020 2:12PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in) +{ + // Unreferenced, but potentially useful to know which version the server is expecting + version_in; + + return CNC_DLL_API_VERSION; +} + + + +/************************************************************************************************** +* CNC_Init -- Initialize the .DLL +* +* In: Command line +* +* Out: +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback) +{ + DLLExportClass::Set_Content_Directory(NULL); + + DLL_Startup(command_line); + + // MBL + DLLExportClass::Set_Event_Callback( event_callback ); + + DLLExportClass::Init(); +} + + + +/************************************************************************************************** +* DLL_Shutdown -- Shutdown the .DLL +* +* In: +* +* Out: +* +* +* +* History: 2/20/2020 1:58PM - ST +**************************************************************************************************/ +void DLL_Shutdown(void) +{ + DLLExportClass::Shutdown(); +} + + + + + +/************************************************************************************************** +* CNC_Config -- Configure the plugin +* +* In: Configuration data +* +* Out: +* +* +* +* History: 10/03/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules) +{ + DLLExportClass::Config(rules); +} + + + + +/************************************************************************************************** +* CNC_Add_Mod_Path -- Add a path to load mod files from +* +* In: Path to load mods from +* +* Out: +* +* +* +* History: 2/20/2020 2:04PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path) +{ + DLLExportClass::Add_Mod_Path(mod_path); +} + + + + + +/************************************************************************************************** +* CNC_Get_Visible_Page -- Get the screen buffer 'SeenBuff' from the game +* +* In: If buffer_in is null, just return info about page +* +* Out: false if not changed since last call +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height) +{ + if (!DLLExportClass::Legacy_Render_Enabled() || (buffer_in == NULL)) { + return false; + } + + /* + ** Assume the seen page viewport is the same size as the page + */ + + GraphicBufferClass *gbuffer = HidPage.Get_Graphic_Buffer(); + if (gbuffer == NULL) { + return false; + } + + int view_port_width = Map.MapCellWidth * CELL_PIXEL_W; + int view_port_height = Map.MapCellHeight * CELL_PIXEL_H; + + if (view_port_width == 0 || view_port_height == 0) { + return false; + } + + unsigned char *raw_buffer = (unsigned char*) gbuffer->Get_Buffer(); + long raw_size = gbuffer->Get_Size(); + if (raw_buffer == NULL || gbuffer->Get_Width() < view_port_width || gbuffer->Get_Height() < view_port_height) { + return false; + } + + width = view_port_width; + height = view_port_height; + + int pitch = gbuffer->Get_Width(); + for (int i = 0; i < view_port_height; ++i, buffer_in += view_port_width, raw_buffer += pitch) { + memcpy(buffer_in, raw_buffer, view_port_width); + } + + return true; +} + + + + +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]) +{ + memcpy(palette_in, CurrentPalette, sizeof(palette_in)); + return true; +} + + + + +/************************************************************************************************** +* CNC_Set_Multiplayer_Data -- Set up for a multiplayer match +* +* In: Multiplayer data +* +* Out: false if data is bad +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players) +{ + + if (num_players <= 0) { + return false; + } + + if (num_players > min(MAX_PLAYERS, max_players)) { + return false; + } + + DLLExportClass::Init(); + + Session.Options.Bases = game_options.MPlayerBases; // 1 = bases are on for this scenario + Session.Options.Credits = game_options.MPlayerCredits; // # credits everyone gets + Session.Options.Tiberium = game_options.MPlayerTiberium; // 1 = tiberium enabled for this scenario + Session.Options.Goodies = game_options.MPlayerGoodies; // 1 = goodies enabled for this scenario + Session.Options.Ghosts = game_options.MPlayerGhosts; // 1 = houses with no players will still play + //MPlayerSolo = game_options.MPlayerSolo; // 1 = allows a single-player net game + Session.Options.UnitCount = game_options.MPlayerUnitCount; // # units for non-base multiplayer scenarios + + Special.IsShadowGrow = game_options.MPlayerShadowRegrow; + Special.IsCaptureTheFlag = game_options.CaptureTheFlag; + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + Session.Options.ScenarioIndex = scenario_index; + + Special.IsMCVDeploy = game_options.IsMCVDeploy; + Special.UseMCVDeploy = true; + + MPSuperWeaponDisable = !game_options.EnableSuperweapons; // Are superweapons available + + //Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + + Special.IsEarlyWin = game_options.DestroyStructures; + + /* + ** Enable Counterstrike/Aftermath units + */ + OverrideNewUnitsEnabled = game_options.MPlayerAftermathUnits; + + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + for (int i=0 ; iName, player_info.Name, MPLAYER_NAME_MAX); + who->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated + who->Player.House = (HousesType)player_info.House; + who->Player.Color = (PlayerColorType) player_info.ColorIndex; + Session.Players.Add (who); + + /* + ** Player IDs are done differently in Red Alert vs. TD. + ** In TD, the ID is created from the house/color combination + ** In RA, the ID is HOUSE_MULTI1 + the index into the Session.Players vector + */ + DLLExportClass::GlyphxPlayerIDs[i] = player_info.GlyphxPlayerID; + + MPlayerIsHuman[i] = !player_info.IsAI; + MPlayerTeamIDs[i] = player_info.Team; + MPlayerStartLocations[i] = player_info.StartLocationIndex; + + /* + ** Temp fix for custom maps that don't have valid start positions set from matchmaking + */ + if (i > 0 && MPlayerStartLocations[i] == 0 && MPlayerStartLocations[0] == 0) { + MPlayerStartLocations[i] = i; + } + } + + /* + ** Force smart defense always on for multiplayer/skirmish + */ + Rule.IsSmartDefense = true; + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + Unselect_All(); + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + switch (object_type_id) + { + case INFANTRY: + { + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Infantry.ID((InfantryClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case UNIT: + { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Units.ID((UnitClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case AIRCRAFT: + { + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Aircraft.ID((AircraftClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case BUILDING: + { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Buildings.ID((BuildingClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case VESSEL: + { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Vessels.ID((VesselClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + } + + return false; +} + + +/************************************************************************************************** +* GlyphX_Assign_Houses -- Replacement for Assign_Houses in INI.CPP / SCNEARIO.CPP +* +* In: +* +* Out: +* +* +* +* History: 8/8/2019 12:37PM - ST +**************************************************************************************************/ +void GlyphX_Assign_Houses(void) +{ + /* + ** RA version + */ + extern int _build_tech[11]; + + int assigned[MAX_PLAYERS]; + bool preassigned; + int i,j,random_start_location; + HousesType house, house2; + HouseClass * housep, *housep2; + int lowest_color; + int index; + + srand(timeGetTime()); + + /* + ** Use pre-selected start locations as long as at least one has been defined, otherwise let the original code + ** in SCENARIO.CPP figure it out + */ + UseGlyphXStartLocations = false; + + /* + ** Assign random start positions if needed. + */ + int random_start_locations[26]; + int num_start_locations = 0; + int num_random_start_locations = 0; + for (i = 0; i < 26; i++) { + if (Scen.Waypoint[i] != -1) { + preassigned = false; + for (j = 0; !preassigned && (j < Session.Players.Count()); j++) { + if (MPlayerStartLocations[j] == num_start_locations) { + preassigned = true; + UseGlyphXStartLocations = true; + } + } + if (!preassigned) { + random_start_locations[num_random_start_locations] = num_start_locations; + num_random_start_locations++; + } + num_start_locations++; + } + } + + if (num_random_start_locations > 1) { + for (i = 0; i < num_random_start_locations - 1; i++) { + j = i + rand() / (RAND_MAX / (num_random_start_locations - i) + 1); + int t = random_start_locations[j]; + random_start_locations[j] = random_start_locations[i]; + random_start_locations[i] = t; + } + } + + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + for (i = 0; i < MAX_PLAYERS; i++) { + assigned[i] = 0; + } + + random_start_location = 0; + +// debugprint( "Assign_Houses()\n" ); + //------------------------------------------------------------------------ + // Assign each player in 'Players' to a multiplayer house. Players will + // be sorted by their chosen color value (this value must be unique among + // all the players). + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + + //..................................................................... + // Find the player with the lowest color index + //..................................................................... + index = 0; + lowest_color = 255; + for (j = 0; j < Session.Players.Count(); j++) { + //.................................................................. + // If we've already assigned this house, skip it. + //.................................................................. + if (assigned[j]) { + continue; + } + if (Session.Players[j]->Player.Color < lowest_color) { + lowest_color = Session.Players[j]->Player.Color; + index = j; + } + } + + //..................................................................... + // Mark this player as having been assigned. + //..................................................................... + assigned[index] = 1; + + //..................................................................... + // Assign the lowest-color'd player to the next available slot in the + // HouseClass array. + //..................................................................... + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#ifdef WOLAPI_INTEGRATION + // Make another copy of name, permanent throughout entire game. + strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#endif + housep->IsHuman = MPlayerIsHuman[index]; + housep->IsPlayerControl = housep->IsHuman; + + if (!housep->IsHuman) { + housep->IsStarted = true; + strncpy(housep->IniName, Text_String(TXT_COMPUTER), HOUSE_NAME_MAX); + housep->IQ = Rule.MaxIQ; + //housep->Control.TechLevel = _build_tech[BuildLevel]; + } + + + housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), + Session.Players[index]->Player.House, Session.Options.Credits); + + /* + ** Set the start location override + */ + if (MPlayerStartLocations[index] != RANDOM_START_POSITION) { + housep->StartLocationOverride = MPlayerStartLocations[index]; + } else { + if (random_start_location < num_random_start_locations) { + housep->StartLocationOverride = random_start_locations[random_start_location++]; + } else { + housep->StartLocationOverride = -1; + } + } + + if (index == 0) { + PlayerPtr = housep; + } + /* + ** Convert the build level into an actual tech level to assign to the house. + ** There isn't a one-to-one correspondence. + */ + housep->Control.TechLevel = _build_tech[BuildLevel]; + + housep->Assign_Handicap(Scen.Difficulty); + + //..................................................................... + // Record where we placed this player + //..................................................................... + Session.Players[index]->Player.ID = house; + +// debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); + } + + for (i = Session.Players.Count(); i < Rule.MaxPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (housep != NULL) { + housep->IsDefeated = true; + } + } + + for (i = 0; i < Session.Players.Count(); i++) { + + house = Session.Players[i]->Player.ID; + housep = HouseClass::As_Pointer(house); + + if (housep) { + + int team = MPlayerTeamIDs[i]; + + for (int j=0 ; j < Session.Players.Count(); j++) { + + if (i != j) { + + if (team == MPlayerTeamIDs[j]) { + + house2 = Session.Players[j]->Player.ID; + housep2 = HouseClass::As_Pointer(house2); + + if (housep2) { + housep->Make_Ally(house2); + } + } + } + } + } + } + +} + + +/************************************************************************************************** +* CNC_Set_Home_Cell -- Allows overriding the start position for the camera +* +* +* History: 2/14/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id) +{ + DLLExportClass::Set_Home_Cell(x, y, player_id); +} + + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map -> WITHOUT SPECIFYING A SCENARIO VARIATION (SCEN_VAR) +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* +* History: 7/10/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) +{ + return CNC_Start_Instance_Variation(scenario_index, (int)SCEN_VAR_NONE, (int)SCEN_DIR_EAST, build_level, faction, game_type, content_directory, sabotaged_structure, override_map_name); +} + + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* Renamed and modified to accept a scenario variation 7/10/2019 - LLL +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) +{ + if (game_type == NULL) { + return false; + } + + if (faction == NULL) { + return false; + } + + if (content_directory == NULL) { + return false; + } + + ScenarioPlayerType scen_player = SCEN_PLAYER_NONE; + + if (stricmp(faction, "SPAIN") == 0) { + scen_player = SCEN_PLAYER_SPAIN; + Whom = HOUSE_GOOD; + } + + if (stricmp(faction, "GREECE") == 0 || stricmp(faction, "ALLY") == 0) { + scen_player = SCEN_PLAYER_GREECE; + Whom = HOUSE_GOOD; + } + + if (stricmp(faction, "USSR") == 0) { + scen_player = SCEN_PLAYER_USSR; + Whom = HOUSE_BAD; + } + + + + DLLExportClass::Set_Content_Directory(content_directory); + + BuildLevel = build_level; + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GAME_TO_PLAY = GAME_NORMAL; + if (scen_player == SCEN_PLAYER_NONE) { + return false; + } + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + scen_player = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + Force_CD_Available(ALWAYS_RELOAD_CD); + + if (override_map_name && strlen(override_map_name)) { + strcpy(Scen.ScenarioName, override_map_name); + } else { + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + } + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + KEYBOARD->MouseQX = 100; + KEYBOARD->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + Seed = GetRandSeed(); + Scen.RandomNumber = Seed; + + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + + if (GAME_TO_PLAY == GAME_NORMAL) { + MPSuperWeaponDisable = false; + } else { + if (MPSuperWeaponDisable) { + /* + ** Write over the tecb level settings we just loaded from the Rules ini + */ + Rule.GPSTechLevel = 100; + Rule.ParaInfantryTechLevel = 100; + Rule.SpyPlaneTechLevel = 100; + Rule.ParaBombTechLevel = 100; + Rule.ChronoTechLevel = 100; + } + } + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } + + Map.Flag_To_Redraw(true); + + Set_Palette(GamePalette.Get_Data()); + Map.Render(); + + Set_Palette(GamePalette.Get_Data()); + + return true; +} + + +/************************************************************************************************** +* CNC_Read_INI -- Load an ini file into the supplied buffer +* +* In: Map initialization parameters +* +* Out: false if ini load failed +* +* +* History: 12/16/2019 11:44AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size) +{ + if (content_directory == NULL) { + return false; + } + + DLLExportClass::Set_Content_Directory(content_directory); + + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + ScenarioPlayerType scen_player = SCEN_PLAYER_MPLAYER; + + Force_CD_Available(ALWAYS_RELOAD_CD); + + if (override_map_name && strlen(override_map_name)) { + strcpy(Scen.ScenarioName, override_map_name); + } else { + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + } + + + if (_ini_buffer_size < _ShapeBufferSize) { + GlyphX_Debug_Print("INI file buffer may be too small"); + return false; + } + + if (!ini_buffer) { + GlyphX_Debug_Print("No INI file buffer"); + return false; + } + + memset(ini_buffer, _ini_buffer_size, 0); + + CCFileClass file(Scen.ScenarioName); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(Scen.ScenarioName); + return(false); + + } else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(Scen.ScenarioName); + + int bytes_read = file.Read(ini_buffer, _ini_buffer_size-1); + if (bytes_read == _ini_buffer_size - 1) { + GlyphX_Debug_Print("INI file buffer is too small"); + return false; + } + } + + /* + ** Ini buffer should be zero terminated + */ + if ((int) strlen(ini_buffer) >= _ini_buffer_size) { + GlyphX_Debug_Print("INI file buffer overrun"); + return false; + } + + return true; +} + + +/************************************************************************************************** +* CNC_Start_Custom_Instance -- Load and start a custom map +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* +* History: 2019/10/28 - JAS +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, const char* scenario_name, int build_level, bool multiplayer) +{ + + DLLExportClass::Set_Content_Directory(content_directory); + + char fullname[_MAX_FNAME + _MAX_EXT]; + + snprintf(fullname, _MAX_FNAME + _MAX_EXT, "%s%s.mpr", directory_path, scenario_name); + + + char name_buffer[128]; + char digest_buffer[32]; + + CCFileClass file(fullname); + + INIClass ini; + ini.Load(file); + + ini.Get_String("Basic", "Name", "No Name", name_buffer, sizeof(name_buffer)); + ini.Get_String("Digest", "1", "No Digest", digest_buffer, sizeof(digest_buffer)); + Session.Scenarios.Add(new MultiMission(fullname, name_buffer, digest_buffer, ini.Get_Bool("Basic", "Official", false), false)); + + + BuildLevel = build_level; + + ScenarioPlayerType scen_player; + strncpy(Scen.ScenarioName, fullname, _MAX_FNAME + _MAX_EXT); + + if (multiplayer) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + } else { + GAME_TO_PLAY = GAME_NORMAL; + } + scen_player = SCEN_PLAYER_MPLAYER; + + //Scen.Scenario = scenario_index; + //ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + + //if (stricmp(game_type, "GAME_NORMAL") == 0) { + // GAME_TO_PLAY = GAME_NORMAL; + //} + //else { + // if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + // GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + // scen_player = SCEN_PLAYER_MPLAYER; + // } + // else { + // return false; + // } + //} + + ///* + //** Load the scenario. Specify variation 'A' for the editor; for the game, + //** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + //** Skip this if we've already loaded a save-game. + //*/ + + //Force_CD_Available(ALWAYS_RELOAD_CD); + + //if (override_map_name && strlen(override_map_name)) { + // strcpy(Scen.ScenarioName, override_map_name); + //} + //else { + // Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + //} + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + KEYBOARD->MouseQX = 100; + KEYBOARD->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + Seed = GetRandSeed(); + Scen.RandomNumber = Seed; + + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + + if (GAME_TO_PLAY == GAME_NORMAL) { + MPSuperWeaponDisable = false; + } + else { + if (MPSuperWeaponDisable) { + /* + ** Write over the tecb level settings we just loaded from the Rules ini + */ + Rule.GPSTechLevel = 100; + Rule.ParaInfantryTechLevel = 100; + Rule.SpyPlaneTechLevel = 100; + Rule.ParaBombTechLevel = 100; + Rule.ChronoTechLevel = 100; + } + } + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } + Map.Flag_To_Redraw(true); + + Set_Palette(GamePalette.Get_Data()); + Map.Render(); + + Set_Palette(GamePalette.Get_Data()); + + return true; +} + + +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + CCFileClass file; + + if (type->ImageData != NULL) { + + sprintf(buffer, "%s_%d", type->IniName, shapenum); + _makepath(fullname, NULL, NULL, buffer, ".PCX"); + + return Debug_Write_Shape(fullname, type->ImageData, shapenum); + } + + return false; +} + + +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags, void const * ghostdata) +{ + /* + ** Build frame returns a pointer now instead of the shapes length + */ + char *shape_pointer = (char*) Build_Frame(shapefile , shapenum , _ShapeBuffer); + if (shape_pointer == NULL) { + return false;; + } + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + return false;; + } + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + + GraphicBufferClass temp_gbuffer(width, height); + GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, width, height); + + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = width; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = height; + + static const char _shape_trans = 0x40; + + if (flags == 0) { + Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, SHAPE_NORMAL|SHAPE_WIN_REL|_shape_trans); //, ghostdata, predoffset); + } else { + Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, flags, ghostdata); + } + Write_PCX_File((char*)file_name, temp_viewport, (unsigned char*)GamePalette.Get_Data()); + return true; +} + + + +/************************************************************************************************** +* CNC_Advance_Instance -- Process one logic frame +* +* In: +* +* Out: Is game still playing? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id) +{ + //DLLExportClass::Set_Event_Callback(event_callback); + + if (Frame <= 10) { // Don't spam forever, but useful to know that we actually started advancing + GlyphX_Debug_Print("CNC_Advance_Instance - RA"); + } + + /* + ** Shouldn't really need to do this, but I like the idea of always running the main loop in the context of the same player. + ** Might make tbe bugs more repeatable and consistent. ST - 3/15/2019 11:58AM + */ + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + } else { + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0]); + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + //if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + // Theme.Queue_Song(THEME_PICK_ANOTHER); + //} + + /* + ** Update the display, unless we're inside a dialog. + */ + //if (SpecialDialog == SDLG_NONE && GameInFocus) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + //Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + /* + ** The main loop passes these in uninitialized. ST - 2/7/2019 4:36PM + */ + KeyNumType input = KN_NONE; // Player input. + int x = 0; + int y = 0; + Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + /* + ** Process the sidebar. ST - 4/18/2019 11:59AM + */ + HouseClass *old_player_ptr = PlayerPtr; + for (int i=0 ; iPlayer.ID); + DLLExportClass::Logic_Switch_Player_Context(player_ptr); + Sidebar_Glyphx_AI(player_ptr, input); + } + DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); + } + + //} + + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will sync on different machines. + */ + Map.Layer[LAYER_GROUND].Sort(); + + /* + ** AI logic operations are performed here. + */ + //Skip this block of code on first update of single-player games. This helps prevents trigger generated messages on the first update from being lost during loading screen or movie. - LLL + static bool FirstUpdate = GAME_TO_PLAY != GAME_GLYPHX_MULTIPLAYER;; + if (!FirstUpdate) { + HouseClass *old_player_ptr = PlayerPtr; + Logic.Clear_Recently_Created_Bits(); + Logic.AI(); + DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); + } + FirstUpdate = false; + + TimeQuake = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!PendingTimeQuake) { + TimeQuakeCenter = 0; + } +#endif + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + /* + ** Process all commands that are ready to be processed. + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + Queue_AI(); + } else { + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::Glyphx_Queue_AI(); + + /* + ** Process the sidebar. ST - 3/22/2019 2:07PM + */ + for (int i=0 ; iPlayer.ID); + Sidebar_Glyphx_Recalc(player_ptr); + } + } + } + + /* + ** Keep track of elapsed time in the game. + */ + //Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + /* + ** Perform any win/lose code as indicated by the global control flags. + */ + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + GlyphX_Debug_Print("PlayerWins = true"); + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, true); + } + + //DLLExportClass::Set_Event_Callback(NULL); + return false; + } + if (PlayerLoses) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + GlyphX_Debug_Print("PlayerLoses = true"); + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, false); + } + + //DLLExportClass::Set_Event_Callback(NULL); + return false; + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Very rarely, the human players will get a message from the computer. + ** + ** This was disabled in the RA source code, so copying over the functionality from TD + */ + if (GAME_TO_PLAY != GAME_NORMAL && Session.Options.Ghosts && IRandom(0,10000) == 1) { + DLLExportClass::Computer_Message(false); + } + + if (ProgEndCalled) { + GlyphX_Debug_Print("ProgEndCalled - GameActive = false"); + GameActive = false; + } + + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + + //Sync_Delay(); + //DLLExportClass::Set_Event_Callback(NULL); + Color_Cycle(); + return(GameActive); +} + + + +/************************************************************************************************** +* CNC_Save_Load -- Process a save or load game action +* +* In: +* +* Out: Success? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type) +{ + bool result = false; + + if (save) { + result = Save_Game(file_path_and_name, "internal"); + } else { + + if (game_type == NULL) { + return false; + } + + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GAME_TO_PLAY = GAME_NORMAL; + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + //ScenPlayer = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + result = Load_Game(file_path_and_name); + + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true); + Set_Logic_Page(SeenBuff); + VisiblePage.Clear(); + Map.Flag_To_Redraw(true); + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + Set_Palette(GamePalette.Get_Data()); + } + + return result; +} + + +/************************************************************************************************** +* CNC_Set_Difficulty -- Set game difficulty +* +* In: +* +* Out: +* +* +* +* History: 10/02/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + Set_Scenario_Difficulty(difficulty); + } +} + + +/************************************************************************************************** +* CNC_Restore_Carryover_Objects +* +* History: 11/15/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Restore_Carryover_Objects(const CarryoverObjectStruct* objects) +{ + //Delete the list + while (Carryover) { + CarryoverClass* cptr = (CarryoverClass*)Carryover->Get_Next(); + delete Carryover; + Carryover = cptr; + } + + //Populate the list + const CarryoverObjectStruct* next_object = objects; + while (next_object) { + CarryoverClass* cptr = new CarryoverClass(); + + cptr->RTTI = (RTTIType)next_object->RTTI; + cptr->Type.Building = (StructType)next_object->Type; //This works regardless of what the RTTI-type and the enum type, because they're all enums. - LLL + cptr->Cell = (CELL)next_object->Cell; + cptr->House = (HousesType)next_object->House; + cptr->Strength = next_object->Strength; + + next_object = next_object->Next; + + if (Carryover == NULL) { + Carryover = cptr; + } + else { + cptr->Add_Tail(*Carryover); + } + } +} + + + +/************************************************************************************************** +* CNC_Handle_Player_Switch_To_AI -- Renamed 3/9/2020 - LLL +* CNC_Handle_Player_Disconnect -- Handle player disconnected during multiplayer game +* +* In: +* +* Out: +* +* +* +* History: 12/3/2019 1:46PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id) +{ + if (PlayerWins || PlayerLoses || DLLExportClass::Get_Game_Over()) { + return; + } + + HousesType house; + HouseClass *ptr; + + GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI"); + + if (GAME_TO_PLAY == GAME_NORMAL) { + return; + } + + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + + if (PlayerPtr) { + PlayerPtr->WasHuman = true; + PlayerPtr->IsHuman = false; + PlayerPtr->IQ = Rule.MaxIQ; + strcpy (PlayerPtr->IniName, Text_String(TXT_COMPUTER)); + + PlayerPtr->IsBaseBuilding = true; + + /* + ** Start the unload mission for MCVs + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (*obj == UNIT_MCV) { + obj->Assign_Mission(MISSION_GUARD); + obj->Assign_Target(TARGET_NONE); + obj->Assign_Destination(TARGET_NONE); + obj->Assign_Mission(MISSION_UNLOAD); + obj->Commence(); + } + } + } + + DLLExportClass::On_Message(PlayerPtr, "", 60.0f, MESSAGE_TYPE_PLAYER_DISCONNECTED, -1); + + /* + ** Send the disconnect taunt message + */ + int human_count = 0; + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MULTIPLAYER_COUNT); house++) { + ptr = HouseClass::As_Pointer(house); + + if (ptr && ptr->IsHuman && !ptr->IsDefeated) { + human_count++; + } + } + + if (human_count == 1) { + DLLExportClass::Computer_Message(true); + } + } + } +} + + +/************************************************************************************************** +* CNC_Handle_Human_Team_Wins +* +* History: 3/10/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 quitting_player_id) +{ + GlyphX_Debug_Print("CNC_Handle_Human_Team_Wins"); + DLLExportClass::Force_Human_Team_Wins(quitting_player_id); +} + + +/************************************************************************************************** +* CNC_Start_Mission_Timer +* +* History: 11/25/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time) +{ + if (GameActive) + { + Scen.MissionTimer = time; + + if (!Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Start(); + } + + Map.Redraw_Tab(); + } +} + + +/************************************************************************************************** +* DLLExportClass::Init -- Init the class +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Init(void) +{ + for (int i=0 ; i= VOC_FIRST && sound_effect_index < VOC_COUNT ) + { + strncpy( new_event.SoundEffect.SoundEffectName, SoundEffectName[ sound_effect_index ].Name , 16); + if ( extension != NULL ) + { + strncat( new_event.SoundEffect.SoundEffectName, extension , 16); + } + new_event.SoundEffect.SoundEffectPriority = SoundEffectName[ sound_effect_index ].Priority; + new_event.SoundEffect.SoundEffectContext = SoundEffectName[ sound_effect_index ].Where; + } + else + { + strncpy( new_event.SoundEffect.SoundEffectName, "BADINDEX", 16 ); + new_event.SoundEffect.SoundEffectPriority = -1; + new_event.SoundEffect.SoundEffectContext = -1; + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Speech -- Called when C&C wants to play a speech line +* +* In: +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Speech(const HouseClass* player_ptr, int speech_index) +{ + // player_ptr could be NULL + + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_SPEECH; + new_event.Speech.SpeechIndex = speech_index; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + if (speech_index >= VOX_FIRST && speech_index < VOX_COUNT) + { + strncpy(new_event.Speech.SpeechName, Speech[speech_index], 16); + } + else + { + strncpy(new_event.Speech.SpeechName, "BAD_SPEECH_INDEX", 16); + } + + EventCallback(new_event); +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Leadership -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Leadership( HousesType player_house, int units_lost, int buildings_lost ) +{ + int house = (player_house == HOUSE_USSR || player_house == HOUSE_UKRAINE); // 0 or 1 + + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass* object = Logic[index]; + HousesType owner = object->Owner(); + if ((house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE)) { + leadership++; + } + else { + if ((!house) && (object->Owner() == HOUSE_GREECE)) { + leadership++; + } + } + } + if (!leadership) leadership++; + leadership = 100 * fixed(leadership, (units_lost + buildings_lost + leadership)); + leadership = min(150, leadership); + + return leadership; +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Economy -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Economy( long available_money, int stolen_buildings_credits, unsigned harvested_credits, long initial_credits ) +{ + int economy = 100 * fixed((unsigned)available_money + 1 + stolen_buildings_credits, harvested_credits + (unsigned)initial_credits + 1); + economy = min(economy, 150); + + return economy; +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Score -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Score( int uspoints, int leadership, int economy ) +{ + int score_total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (score_total < -9999) score_total = -9999; + score_total = min(score_total, 99999); + + return score_total; +} + +/************************************************************************************************** +* DLLExportClass::CalculateScore* +* +* History: 10/16/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Calculate_Single_Player_Score(EventCallbackStruct& event) +{ + //Adapted from Red Alert SCORE.CPP Presentation() - LLL + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 + + int good_units_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; + int bad_units_lost = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; + int neutral_units_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; + int good_buildings_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; + int bad_buildings_lost = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; + int neutral_buildings_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; + int good_credits_harvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; + int bad_credits_harvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; + + int uspoints = 0; + + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass *hows = HouseClass::As_Pointer(hous); + if (hous == HOUSE_USSR || hous == HOUSE_BAD || hous == HOUSE_UKRAINE) { + bad_units_lost += hows->UnitsLost; + bad_buildings_lost += hows->BuildingsLost; + } + else { + good_units_lost += hows->UnitsLost; + good_buildings_lost += hows->BuildingsLost; + } + if (PlayerPtr->Is_Ally(hous)) { + uspoints += hows->PointTotal; + } + } + // if(uspoints < 0) uspoints = 0; + // uspoints += 1000; //BG 1000 bonus points for winning mission + + /* + ** Bias the base score upward according to the difficulty level. + */ + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + + /* + ** Determine leadership rating. + */ + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + HousesType owner = object->Owner(); + if ((house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE)) { + leadership++; + } + else { + if ((!house) && (object->Owner() == HOUSE_GREECE)) { + leadership++; + } + } + } + if (!leadership) leadership++; + leadership = 100 * fixed(leadership, (house ? bad_units_lost + bad_buildings_lost + leadership : good_units_lost + good_buildings_lost + leadership)); + leadership = min(150, leadership); + + /* + ** Determine economy rating. + */ + // int init = PlayerPtr->Control.InitialCredits; + // int cred = PlayerPtr->Available_Money(); + + int economy = 100 * fixed((unsigned)PlayerPtr->Available_Money() + 1 + PlayerPtr->StolenBuildingsCredits, PlayerPtr->HarvestedCredits + (unsigned)PlayerPtr->Control.InitialCredits + 1); + economy = min(economy, 150); + + int score_total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (score_total < -9999) score_total = -9999; + score_total = min(score_total, 99999); + + //Score Stats + event.GameOver.Leadership = leadership; + event.GameOver.Efficiency = economy; + event.GameOver.Score = score_total; + event.GameOver.CategoryTotal = uspoints; + event.GameOver.NODKilled = bad_units_lost; + event.GameOver.GDIKilled = good_units_lost; + event.GameOver.CiviliansKilled = neutral_units_lost; + event.GameOver.NODBuildingsDestroyed = bad_buildings_lost; + event.GameOver.GDIBuildingsDestroyed = good_buildings_lost; + event.GameOver.CiviliansBuildingsDestroyed = neutral_buildings_lost; + event.GameOver.RemainingCredits = PlayerPtr->Available_Money(); + + if (Scen.IsSkipScore) { + event.GameOver.Score = -1; + } +} + + +/************************************************************************************************** +* DLLExportClass::On_Message -- Called when the game wants to display a message (ex. tutorial text) +* +* In: +* +* Out: +* +* +* +* History: 10/16/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) +{ + if (EventCallback == NULL) + { + return; + } + + const char* p_msg = message; + + std::string localized_text_name; + if (message_id != -1) { + if (message_id == TXT_INSUFFICIENT_FUNDS) { + localized_text_name = "TEXT_INSUFFICIENT_FUNDS_MESSAGE"; + } + else if (message_id == TXT_LOW_POWER) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_001"; + } + else if (message_id == TXT_POWER_TESLA) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_002"; + } + else if (message_id == TXT_POWER_AAGUN) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_003"; + } + else { + if (MessagesSent.find(message_id) != MessagesSent.end()) { + return; + } + + MessagesSent.insert(message_id); + + localized_text_name = "TEXT_RA_TUTORIAL_MESSAGE_"; + localized_text_name += std::to_string(message_id); + } + p_msg = localized_text_name.c_str(); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_MESSAGE; + new_event.Message.Message = p_msg; + new_event.Message.TimeoutSeconds = timeout_seconds; + new_event.Message.MessageType = message_type; + new_event.Message.MessageParam1 = message_id; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Update_Map_Cell -- Called when an individual map cell template is updated +* +* +* History: 11/7/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name) +{ + if (EventCallback == NULL) + { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_UPDATE_MAP_CELL; + new_event.UpdateMapCell.CellX = cell_x; + new_event.UpdateMapCell.CellY = cell_y; + strncpy(new_event.UpdateMapCell.TemplateTypeName, template_type_name, 32); + new_event.UpdateMapCell.TemplateTypeName[31] = '\0'; + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Special_Weapon_Targetting -- Called when the server initiates targetting +* +* +* History: 11/19/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type) +{ + if (EventCallback == NULL) + { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_SPECIAL_WEAPON_TARGETTING; + new_event.SpecialWeaponTargetting.Type = RTTI_SPECIAL; + new_event.SpecialWeaponTargetting.ID = weapon_type; + Convert_Special_Weapon_Type(weapon_type, new_event.SpecialWeaponTargetting.WeaponType, new_event.SpecialWeaponTargetting.Name); + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Ping -- Called when a radar ping is needed +* +* +* History: 05/15/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_PING; + new_event.Ping.CoordX = Coord_X(coord); + new_event.Ping.CoordY = Coord_Y(coord); + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Game_Over -- Called when the C&C campaign game finishes +* +* +* History: 6/19/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::On_Game_Over(uint64 glyphx_Player_id, bool player_wins) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + SaveTanya = IsTanyaDead; + Scen.CarryOverTimer = Scen.MissionTimer; + // int timer = Scen.MissionTimer; + + Scen.CarryOverMoney = PlayerPtr->Credits; + + DLLExportClass::Store_Carryover_Objects(); + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_GAME_OVER; + new_event.GlyphXPlayerID = glyphx_Player_id; + new_event.GameOver.PlayerWins = player_wins; + new_event.GameOver.AfterScoreMovieName = ""; + new_event.GameOver.Multiplayer = false; + new_event.GameOver.MultiPlayerTotalPlayers = 0; + + Calculate_Single_Player_Score(new_event); + + VQType movie = player_wins ? Scen.WinMovie : Scen.LoseMovie; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName = VQName[movie]; + } else { + new_event.GameOver.MovieName = ""; + } + + movie = player_wins ? Scen.WinMovie2 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName2 = VQName[movie]; + } + else { + new_event.GameOver.MovieName2 = ""; + } + + movie = player_wins ? Scen.WinMovie3 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName3 = VQName[movie]; + } + else { + new_event.GameOver.MovieName3 = ""; + } + + movie = player_wins ? Scen.WinMovie4 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName4 = VQName[movie]; + } + else { + new_event.GameOver.MovieName4 = ""; + } + + //Campaign win & credits + if (Scen.IsEndOfGame) { + if (PlayerPtr->ActLike == HOUSE_USSR) { + new_event.GameOver.AfterScoreMovieName = VQName[VQ_SOVFINAL]; + } + else { + new_event.GameOver.AfterScoreMovieName = VQName[VQ_ALLYEND]; + } + } + + new_event.GameOver.SabotagedStructureType = -1; + new_event.GameOver.TimerRemaining = Scen.MissionTimer.Was_Started() ? Scen.MissionTimer.Value() : -1; + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Multiplayer_Game_Over -- Called when the C&C multiplayer game finishes +* +* +* History: 6/19/2019 - LLL +* History: 10/31/2019 - MBL - Adding the multi-player score stats support for debrief +**************************************************************************************************/ +void DLLExportClass::On_Multiplayer_Game_Over(void) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + EventCallbackStruct event; + + event.EventType = CALLBACK_EVENT_GAME_OVER; + + // Includes AI's for skirmish + int player_count = Session.Players.Count(); + + // Multiplayer players data for debrief stats + + event.GameOver.Multiplayer = true; + event.GameOver.MultiPlayerTotalPlayers = player_count; + + for ( int player_index = 0; player_index < player_count; player_index ++ ) + { + HouseClass* player_ptr = HouseClass::As_Pointer(Session.Players[ player_index ]->Player.ID); + if ( player_ptr != NULL ) + { + HousesType player_house = PlayerPtr->Class->House; + + int uspoints = 0; + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass* hows = HouseClass::As_Pointer(hous); + if (player_ptr->Is_Ally(hous)) { + uspoints += hows->PointTotal; + } + } + // if(uspoints < 0) uspoints = 0; + // uspoints += 1000; //BG 1000 bonus points for winning mission + + // N/A for multi-player + #if 0 + // Bias the base score upward according to the difficulty level. + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + #endif + + int leadership = RA_Calculate_Leadership( player_ptr->Class->House, player_ptr->UnitsLost, player_ptr->BuildingsLost ); + int economy = RA_Calculate_Economy( player_ptr->Available_Money(), player_ptr->StolenBuildingsCredits, player_ptr->HarvestedCredits, player_ptr->Control.InitialCredits ); + int total_score = RA_Calculate_Score( uspoints, leadership, economy ); + + int units_killed = 0; + int structures_killed = 0; + for ( unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++ ) + { + units_killed += player_ptr->UnitsKilled[ house_index ]; + structures_killed += player_ptr->BuildingsKilled[ house_index ]; + } + + // Populate and copy the multiplayer player data structure + + GameOverMultiPlayerStatsStruct multi_player_data; + + multi_player_data.GlyphXPlayerID = Get_GlyphX_Player_ID( player_ptr ); + multi_player_data.IsHuman = (player_ptr->IsHuman || player_ptr->WasHuman); + multi_player_data.WasHuman = player_ptr->WasHuman; + multi_player_data.IsWinner = !player_ptr->IsDefeated; + multi_player_data.Efficiency = economy; + multi_player_data.Score = total_score; + multi_player_data.ResourcesGathered = player_ptr->HarvestedCredits; + multi_player_data.TotalUnitsKilled = units_killed; + multi_player_data.TotalStructuresKilled = structures_killed; + + if ( player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ) + { + event.GameOver.MultiPlayerPlayersData[ player_index ] = multi_player_data; + } + } + } + for ( int player_index = player_count; player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED; player_index ++ ) + { + memset( &event.GameOver.MultiPlayerPlayersData[ player_index ], 0, sizeof( GameOverMultiPlayerStatsStruct ) ); + } + + // Single-player N/A stuff + + event.GameOver.MovieName = ""; + event.GameOver.MovieName2 = ""; + event.GameOver.MovieName3 = ""; + event.GameOver.MovieName4 = ""; + event.GameOver.AfterScoreMovieName = ""; + event.GameOver.Leadership = 0; + event.GameOver.Efficiency = 0; + event.GameOver.Score = 0; + event.GameOver.NODKilled = 0; + event.GameOver.GDIKilled = 0; + event.GameOver.CiviliansKilled = 0; + event.GameOver.NODBuildingsDestroyed = 0; + event.GameOver.GDIBuildingsDestroyed = 0; + event.GameOver.CiviliansBuildingsDestroyed = 0; + event.GameOver.RemainingCredits = 0; + event.GameOver.SabotagedStructureType = 0; + event.GameOver.TimerRemaining = -1; + + // Trigger an event for each human player + for (int i=0 ; iPlayer.ID); + if (player_ptr->IsHuman) { + event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + event.GameOver.PlayerWins = !player_ptr->IsDefeated; + event.GameOver.RemainingCredits = player_ptr->Available_Money(); + EventCallback(event); + } + } +} + + +/************************************************************************************************** +* DLLExportClass::On_Achievement -- Called when something achievement-related happens +* +* In: Type of achievement, reason this happened +* +* Out: +* +* +* +* History: 11/11/2019 11:37AM - ST +**************************************************************************************************/ +void DLLExportClass::On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_ACHIEVEMENT; + new_event.Achievement.AchievementType = achievement_type; + new_event.Achievement.AchievementReason = achievement_reason; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +void DLLExportClass::On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_CENTER_CAMERA; + new_event.CenterCamera.CoordX = coord_x; + new_event.CenterCamera.CoordY = coord_y; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Debug_Output -- Called when C&C wants to print debug output +* +* In: String to print to GlyphX log system +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Debug_Output(const char *debug_text) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_DEBUG_PRINT; + new_event.DebugPrint.PrintString = debug_text; + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::Force_Human_Team_Wins +* +* History: 3/10/2020 - LL +**************************************************************************************************/ +void DLLExportClass::Force_Human_Team_Wins(uint64 quitting_player_id) +{ + int winning_team = -1; + + //Find the first human's multiplayer team. + for (int i = 0; i < Session.Players.Count(); i++) + { + if (GlyphxPlayerIDs[i] != quitting_player_id) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class && house_class->IsHuman && !house_class->IsDefeated) { + winning_team = MPlayerTeamIDs[i]; + break; + } + } + } + + //Mark all players not on that team as defeated. + for (int i = 0; i < Session.Players.Count(); i++) + { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class) { + house_class->IsDefeated = MPlayerTeamIDs[i] != winning_team; + } + } + + PlayerWins = true; +} + + +/************************************************************************************************** +* CNC_Get_Game_State -- Get game state +* +* In: Type of state requested +* Player perspective +* Buffer to contain game state +* Size of buffer +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + bool got_state = false; + + switch (state_type) { + + case GAME_STATE_LAYERS: + { + got_state = DLLExportClass::Get_Layer_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_SIDEBAR: + { + got_state = DLLExportClass::Get_Sidebar_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_PLACEMENT: + { + got_state = DLLExportClass::Get_Placement_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_DYNAMIC_MAP: + got_state = DLLExportClass::Get_Dynamic_Map_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_SHROUD: + got_state = DLLExportClass::Get_Shroud_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_OCCUPIER: + got_state = DLLExportClass::Get_Occupier_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_PLAYER_INFO: + got_state = DLLExportClass::Get_Player_Info_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_STATIC_MAP: + { + if (buffer_size < sizeof(CNCMapDataStruct)) { + got_state = false; + break; + } + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + CNCMapDataStruct *map_data = (CNCMapDataStruct *)buffer_in; + + map_data->OriginalMapCellX = map_cell_x; + map_data->OriginalMapCellY = map_cell_y; + map_data->OriginalMapCellWidth = map_cell_width; + map_data->OriginalMapCellHeight = map_cell_height; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + map_data->MapCellX = map_cell_x; + map_data->MapCellY = map_cell_y; + map_data->MapCellWidth = map_cell_width; + map_data->MapCellHeight = map_cell_height; + + map_data->Theater = (CnCTheaterType) Scen.Theater; + + char* dot_ptr = strchr(Scen.ScenarioName, '.'); + const int count = (dot_ptr != nullptr) ? (dot_ptr - Scen.ScenarioName) : sizeof(Scen.ScenarioName); + memset(map_data->ScenarioName, 0, sizeof(map_data->ScenarioName)); + strncpy(map_data->ScenarioName, Scen.ScenarioName, min(sizeof(map_data->ScenarioName) - 1, count)); + + int cell_index = 0; + char cell_name[_MAX_PATH]; + char icon_number[32]; + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + CellClass * cellptr = &Map[cell]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) { + itoa(icon, icon_number, 10); + strncat(cell_name, "_i", 32); + strncat(cell_name, icon_number, 32); + strncat(cell_name, ".tga", 32); + + CNCStaticCellStruct &cell_info = map_data->StaticCells[cell_index++]; + strncpy(cell_info.TemplateTypeName, cell_name, 32); + cell_info.IconNumber = icon; + } + } + } + + got_state = true; + break; + } + + default: + { + got_state = false; + break; + } + } + + return got_state; +} + + +/************************************************************************************************** +* CNC_Handle_Game_Request +* +* Callback for when the requested movie is done playing. +* +* 7/23/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type) +{ + switch (request_type) + { + case INPUT_GAME_MOVIE_DONE: + break; + } +} + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode) +{ + if (!DLLExportClass::Legacy_Render_Enabled()) { + return; + } + + RulesClass::eHealthBarDisplayMode new_hb_mode = (RulesClass::eHealthBarDisplayMode)health_bar_display_mode; + if (new_hb_mode != Rule.HealthBarDisplayMode) { + Rule.HealthBarDisplayMode = new_hb_mode; + Map.Flag_To_Redraw(true); + } + + RulesClass::eResourceBarDisplayMode new_rb_mode = (RulesClass::eResourceBarDisplayMode)resource_bar_display_mode; + if (new_rb_mode != Rule.ResourceBarDisplayMode) { + Rule.ResourceBarDisplayMode = new_rb_mode; + Map.Flag_To_Redraw(true); + } +} + + + + + + +void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner) +{ + DLLExportClass::DLL_Draw_Intercept(shape_number, x, y, width, height, flags, object, rotation, scale, shape_file_name, override_owner); +} + + +void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + DLLExportClass::DLL_Draw_Pip_Intercept(object, pip); +} + + +void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner) +{ + CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount]; + Convert_Type(object, new_object); + if (new_object.Type == UNKNOWN) { + return; + } + + CNCObjectStruct* base_object = NULL; + char sub_object = 0; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + if (base_object == NULL) { + base_object = &draw_object; + } + sub_object++; + } + } + + new_object.CNCInternalObjectPointer = (void*)object; + new_object.OccupyListLength = 0; + new_object.SortOrder = SortOrder++; + + strncpy(new_object.TypeName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + + if (shape_file_name != NULL) { + strncpy(new_object.AssetName, shape_file_name, CNC_OBJECT_ASSET_NAME_LENGTH); + } + else { + strncpy(new_object.AssetName, object->Class_Of().Graphic_Name(), CNC_OBJECT_ASSET_NAME_LENGTH); + } + + new_object.Owner = (base_object != NULL) ? ((override_owner != HOUSE_NONE) ? override_owner : base_object->Owner) : (char)object->Owner(); + + HouseClass* owner_house = nullptr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && (hptr->Class->House == new_object.Owner)) { + owner_house = hptr; + break; + } + } + + new_object.RemapColor = (owner_house != nullptr) ? owner_house->RemapColor : -1; + + if (base_object == NULL) { + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + + if (new_object.Type == BUILDING) { + BuildingClass *building = (BuildingClass*)object; + if (building->BState == BSTATE_CONSTRUCTION) { + strncat(new_object.AssetName, "MAKE", CNC_OBJECT_ASSET_NAME_LENGTH); + } + const BuildingTypeClass *building_type = building->Class; + short const *occupy_list = building_type->Occupy_List(); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && new_object.OccupyListLength < MAX_OCCUPY_CELLS) { + new_object.OccupyList[new_object.OccupyListLength] = *occupy_list; + new_object.OccupyListLength++; + occupy_list++; + } + } + } + + COORDINATE coord = object->Render_Coord(); + CELL cell = Coord_Cell(coord); + int dimx, dimy; + object->Class_Of().Dimensions(dimx, dimy); + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = object->Height; + new_object.DrawFlags = flags; + new_object.SubObject = 0; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Rotation = (unsigned char)rotation; + new_object.Scale = scale; + new_object.FlashingFlags = 0; + new_object.Cloak = (CurrentDrawCount > 0) ? root_object.Cloak : UNCLOAKED; + new_object.VisibleFlags = CNCObjectStruct::VISIBLE_FLAGS_ALL; + new_object.SpiedByFlags = 0U; + + new_object.IsSelectable = object->Class_Of().IsSelectable; + new_object.IsSelectedMask = object->IsSelectedMask; + new_object.MaxStrength = object->Class_Of().MaxStrength; + new_object.Strength = object->Strength; + new_object.CellX = (CurrentDrawCount > 0) ? root_object.CellX : Cell_X(cell); + new_object.CellY = (CurrentDrawCount > 0) ? root_object.CellY : Cell_Y(cell); + new_object.CenterCoordX = Coord_X(object->Center_Coord()); + new_object.CenterCoordY = Coord_Y(object->Center_Coord()); + new_object.DimensionX = dimx; + new_object.DimensionY = dimy; + new_object.SimLeptonX = (CurrentDrawCount > 0) ? root_object.SimLeptonX : 0; + new_object.SimLeptonY = (CurrentDrawCount > 0) ? root_object.SimLeptonY : 0; + new_object.BaseObjectID = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING) && (root_object.Type != VESSEL)) ? root_object.ID : 0; + new_object.BaseObjectType = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING) && (root_object.Type != VESSEL)) ? root_object.Type : UNKNOWN; + new_object.NumLines = 0; + new_object.RecentlyCreated = object->IsRecentlyCreated; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.CanDemolish = object->Can_Demolish(); + new_object.CanDemolishUnit = object->Can_Demolish_Unit(); + new_object.CanRepair = object->Can_Repair(); + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + HousesType house = hptr->Class->House; + DynamicVectorClass& selected_objects = CurrentObject.Raw(house); + if (selected_objects.Count() > 0) { + Logic_Switch_Player_Context(hptr); + Convert_Action_Type(Best_Object_Action(selected_objects, object), (selected_objects.Count() == 1) ? selected_objects[0] : NULL, object->As_Target(), new_object.ActionWithSelected[house]); + } + } + } + Logic_Switch_Player_Context(old_player_ptr); + + RTTIType what_is_object = object->What_Am_I(); + + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = false; + new_object.IsFactory = false; + new_object.IsPrimaryFactory = false; + new_object.IsNominal = false; + new_object.IsDog = false; + new_object.IsIronCurtain = false; + new_object.IsAntiGround = false; + new_object.IsAntiAircraft = false; + new_object.IsSubSurface = false; + new_object.IsFake = false; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + + bool is_building = what_is_object == RTTI_BUILDING; + if (is_building) { + const BuildingClass* building = static_cast(object); + new_object.IsRepairing = building->IsRepairing; + new_object.IsFactory = building->Class->Is_Factory(); + new_object.IsPrimaryFactory = building->IsLeader; + new_object.IsFake = building->Class->IsFake; + } + + if (object->Is_Techno()) { + const TechnoClass* techno_object = static_cast(object); + const TechnoTypeClass *ttype = techno_object->Techno_Type_Class(); + + new_object.MaxSpeed = (unsigned char)ttype->MaxSpeed; + new_object.IsALoaner = techno_object->IsALoaner; + new_object.IsNominal = ttype->IsNominal; + new_object.MaxPips = ttype->Max_Pips(); + new_object.IsAntiGround = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsAntiGround; + new_object.IsAntiAircraft = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsAntiAircraft; + new_object.IsSubSurface = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsSubSurface; + new_object.IsIronCurtain = techno_object->IronCurtainCountDown > 0; + + int full_name = techno_object->Full_Name(); + if (full_name < 0) + { + new_object.OverrideDisplayName = Text_String(full_name); + } + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + Logic_Switch_Player_Context(hptr); + HousesType house = hptr->Class->House; + new_object.CanMove[house] = techno_object->Can_Player_Move(); + new_object.CanFire[house] = techno_object->Can_Player_Fire(); + } + } + Logic_Switch_Player_Context(old_player_ptr); + } + + new_object.ControlGroup = (unsigned char)(-1); + new_object.IsInFormation = false; + new_object.CanPlaceBombs = false; + + if (object->Is_Foot()) { + const FootClass* foot = static_cast(object); + new_object.ControlGroup = foot->Group; + new_object.IsInFormation = foot->XFormOffset != 0x80000000UL; + } + + bool is_infantry = what_is_object == RTTI_INFANTRY; + if (is_infantry) { + const InfantryClass* infantry = static_cast(object); + new_object.IsDog = infantry->Class->IsDog; + new_object.CanPlaceBombs = infantry->Class->IsBomber; + } + + new_object.CanHarvest = false; + bool is_unit = what_is_object == RTTI_UNIT; + if (is_unit) { + const UnitClass* unit = static_cast(object); + if (unit->Class->Type == UNIT_HARVESTER) + { + new_object.CanHarvest = true; + } + + new_object.IsDumping = unit->IsDumping; + } + + new_object.IsFixedWingedAircraft = false; + bool is_aircraft = what_is_object == RTTI_AIRCRAFT; + if (is_aircraft) { + const AircraftClass* aircraft = static_cast(object);; + new_object.IsFixedWingedAircraft = aircraft->Class->IsFixedWing; + } + + switch (what_is_object) + { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + case RTTI_UNIT: + case RTTI_UNITTYPE: + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + { + const TechnoClass* techno_object = static_cast(object); + new_object.FlashingFlags = techno_object->Get_Flashing_Flags(); + new_object.Cloak = techno_object->Cloak; + new_object.SpiedByFlags = techno_object->Spied_By(); + + if (techno_object->Techno_Type_Class()->IsInvisible) { + // Hide for enemy players + HouseClass* owner = HouseClass::As_Pointer(object->Owner()); + if (owner != nullptr) { + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && !owner->Is_Ally(hptr)) { + new_object.VisibleFlags &= ~(1 << hptr->Class->House); + } + } + } + } + } + break; + case RTTI_ANIM: + case RTTI_ANIMTYPE: + { + const AnimClass* anim_object = static_cast(object); + new_object.Owner = anim_object->OwnerHouse; + new_object.RemapColor = -1; + new_object.VisibleFlags = anim_object->Get_Visible_Flags(); + + const AnimTypeClass& anim_type = static_cast(anim_object->Class_Of()); + if (anim_type.VirtualName != NULL) { + strncpy(new_object.AssetName, anim_type.VirtualName, CNC_OBJECT_ASSET_NAME_LENGTH); + } + } + break; + case RTTI_TERRAIN: + case RTTI_TERRAINTYPE: + { + if (strncmp(new_object.AssetName, "MINE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0) { + strncpy(new_object.AssetName, "OREMINE", CNC_OBJECT_ASSET_NAME_LENGTH); + } + } + break; + } + + } + else { + new_object.MaxStrength = 0; + new_object.MaxSpeed = 0; + new_object.Strength = 0; + new_object.CellX = base_object->CellX; + new_object.CellY = base_object->CellY; + new_object.CenterCoordX = base_object->CenterCoordX; + new_object.CenterCoordY = base_object->CenterCoordY; + new_object.DimensionX = base_object->DimensionX; + new_object.DimensionY = base_object->DimensionY; + new_object.IsSelectable = false; + new_object.IsSelectedMask = 0U; + new_object.SimLeptonX = base_object->SimLeptonX; + new_object.SimLeptonY = base_object->SimLeptonY; + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = base_object->Altitude; + new_object.DrawFlags = flags; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Rotation = (unsigned char)rotation; + new_object.Scale = scale; + new_object.SubObject = sub_object; + new_object.BaseObjectID = base_object->ID; + new_object.BaseObjectType = base_object->Type; + + new_object.FlashingFlags = base_object->FlashingFlags; + new_object.Cloak = base_object->Cloak; + new_object.OccupyListLength = 0; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.NumLines = 0; + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = base_object->IsALoaner; + new_object.CanDemolish = base_object->CanDemolish; + new_object.CanDemolishUnit = base_object->CanDemolishUnit; + new_object.CanRepair = base_object->CanRepair; + new_object.RecentlyCreated = base_object->RecentlyCreated; + new_object.IsFactory = base_object->IsFactory; + new_object.IsPrimaryFactory = base_object->IsPrimaryFactory; + new_object.IsAntiGround = base_object->IsAntiGround; + new_object.IsAntiAircraft = base_object->IsAntiAircraft; + new_object.IsSubSurface = base_object->IsSubSurface; + new_object.IsNominal = base_object->IsNominal; + new_object.IsDog = base_object->IsDog; + new_object.IsIronCurtain = base_object->IsIronCurtain; + new_object.IsInFormation = false; + new_object.CanHarvest = base_object->CanHarvest; + new_object.CanPlaceBombs = base_object->CanPlaceBombs; + new_object.ControlGroup = base_object->ControlGroup; + new_object.VisibleFlags = base_object->VisibleFlags; + new_object.SpiedByFlags = base_object->SpiedByFlags; + new_object.IsFixedWingedAircraft = base_object->IsFixedWingedAircraft; + new_object.IsFake = base_object->IsFake; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + } + + CurrentDrawCount++; +} + + + +void DLLExportClass::DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + CNCObjectStruct* base_object = NULL; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + base_object = &draw_object; + break; + } + } + + if ((base_object != NULL) && (base_object->NumPips < MAX_OBJECT_PIPS)) { + base_object->Pips[base_object->NumPips] = pip; + base_object->NumPips++; + base_object->MaxPips = max(base_object->MaxPips, base_object->NumPips); + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get game objects from the layers +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + player_id; + + static int _export_count = 0; + + bool got_state = false; + + ObjectList = (CNCObjectListStruct*) buffer_in; + + TotalObjectCount = 0; + + /* + ** Get a reference draw coordinate for cells + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + if (map_cell_x > 0) { + map_cell_x--; + } + if (map_cell_y > 0) { + map_cell_y--; + } + + SortOrder = 0; + + /* + ** Get the ground layer first and then followed by all the layers in increasing altitude. + */ + for (int layer = 0; layer < DLL_LAYER_COUNT; layer++) { + + for (int index = 0; index < Map.Layer[layer].Count(); index++) { + + ObjectClass *object = Map.Layer[layer][index]; + if (object->IsActive) { + + unsigned int memory_needed = sizeof(CNCObjectListStruct); + memory_needed += (TotalObjectCount + 10) * sizeof(CNCObjectStruct); + if (memory_needed >= buffer_size) { + return false; + } + + if (object->Is_Techno()) { + /* + ** Skip units tethered to buildings, since the building will draw them itself + */ + TechnoClass* techno_object = static_cast(object); + TechnoClass* contact_object = techno_object->In_Radio_Contact() ? techno_object->Contact_With_Whom() : nullptr; + if ((object->What_Am_I() != RTTI_BUILDING) && (contact_object != nullptr) && (contact_object->What_Am_I() == RTTI_BUILDING) && contact_object->IsTethered && *((BuildingClass*)contact_object) == STRUCT_WEAP) { + continue; + } + + /* + ** Skip units tethered to vessels, since the vessel will draw them itself + */ + if ((contact_object != nullptr) && (contact_object->What_Am_I() == RTTI_VESSEL) && !contact_object->Is_Door_Closed() && contact_object->IsTethered && !techno_object->IsInLimbo) { + continue; + } + } + + if (Debug_Map || Debug_Unshroud || (object->IsDown && !object->IsInLimbo)) { + int x, y; + Map.Coord_To_Pixel(object->Render_Coord(), x, y); + + /* + ** Call to Draw_It can result in multiple callbacks to the draw intercept + */ + CurrentDrawCount = 0; + object->Draw_It(x, y, WINDOW_VIRTUAL); + + /* + ** If the root object is a factory, then the last base object is the object in production (rendered after infiltrated buildings when selected). + ** The root object is updated with the production asset name, but otherwise a separate object isn't created. + ** This only occurs in skirmish and multiplayer. + */ + if ((GAME_TO_PLAY != GAME_NORMAL) && (CurrentDrawCount > 0)) { + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + if (root_object.IsFactory) { + BuildingClass* building = (BuildingClass*)root_object.CNCInternalObjectPointer; + FactoryClass* factory = building->House->IsHuman ? building->House->Fetch_Factory(building->Class->ToBuild) : building->Factory; + if (factory != nullptr) { + for (int i = CurrentDrawCount - 1; i > 0; --i) { + CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + i]; + if (base_object.SubObject) { + continue; + } + strncpy(root_object.ProductionAssetName, base_object.TypeName, CNC_OBJECT_ASSET_NAME_LENGTH); + void* production_object = base_object.CNCInternalObjectPointer; + int new_draw_count = i; + for (int j = i + 1; j < CurrentDrawCount; ++j) { + CNCObjectStruct& cnc_object = ObjectList->Objects[TotalObjectCount + j]; + if (cnc_object.CNCInternalObjectPointer != production_object) { + memcpy(ObjectList->Objects + TotalObjectCount + new_draw_count, &cnc_object, sizeof(CNCObjectStruct)); + new_draw_count++; + } + } + memset(ObjectList->Objects + TotalObjectCount + new_draw_count, 0, (CurrentDrawCount - new_draw_count) * sizeof(CNCObjectStruct)); + CurrentDrawCount = new_draw_count; + break; + } + } + } + } + + /* + ** Shadows need to be rendered before the base object so they appear underneath, + ** even though they get drawn as sub-objects (after the base object) + */ + for (int i = 1; i < CurrentDrawCount; ++i) { + CNCObjectStruct& sub_object = ObjectList->Objects[TotalObjectCount + i]; + if (!sub_object.SubObject) { + continue; + } + static const int shadow_flags = SHAPE_PREDATOR | SHAPE_FADING; + if (((sub_object.DrawFlags & shadow_flags) == shadow_flags) || (strncmp(sub_object.AssetName, "WAKE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0)) { + if ((strncmp(sub_object.AssetName, "RROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0) && + (strncmp(sub_object.AssetName, "LROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0)) { + for (int j = i - 1; j >= 0; --j) { + CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + j]; + if (!base_object.SubObject && (base_object.CNCInternalObjectPointer == sub_object.CNCInternalObjectPointer)) { + int sort_order = base_object.SortOrder; + base_object.SortOrder = sub_object.SortOrder; + sub_object.SortOrder = sort_order; + break; + } + } + } + } + } + + TotalObjectCount += CurrentDrawCount; + } + } + } + } + + ObjectList->Count = TotalObjectCount; + + if (ObjectList->Count) { + _export_count++; + return true; + } + + return false; +} + + + + +void DLLExportClass::Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out) +{ + object_out.Type = UNKNOWN; + object_out.ID = -1; + + if (object == NULL) { + return; + } + + RTTIType type = object->What_Am_I(); + + switch (type) { + default: + break; + + case RTTI_VESSEL: + object_out.Type = VESSEL; + object_out.ID = Vessels.ID((VesselClass*)object); + break; + + case RTTI_INFANTRY: + object_out.Type = INFANTRY; + object_out.ID = Infantry.ID((InfantryClass*)object); + break; + + case RTTI_UNIT: + object_out.Type = UNIT; + object_out.ID = Units.ID((UnitClass*)object); + break; + + case RTTI_AIRCRAFT: + object_out.Type = AIRCRAFT; + object_out.ID = Aircraft.ID((AircraftClass*)object); + break; + + case RTTI_BUILDING: + object_out.Type = BUILDING; + object_out.ID = Buildings.ID((BuildingClass*)object); + break; + + case RTTI_BULLET: + object_out.Type = BULLET; + object_out.ID = Bullets.ID((BulletClass*)object); + break; + + case RTTI_ANIM: + object_out.Type = ANIM; + object_out.ID = Anims.ID((AnimClass*)object); + break; + + case RTTI_SMUDGE: + object_out.Type = SMUDGE; + object_out.ID = Smudges.ID((SmudgeClass*)object); + break; + + case RTTI_TERRAIN: + object_out.Type = TERRAIN; + object_out.ID = Terrains.ID((TerrainClass*)object); + break; + } +} + + + + + + +/************************************************************************************************** +* CNC_Handle_Input -- Process input to the game +* +* In: +* +* +* +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum input_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2) +{ + + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (input_event) { + + /* + ** Special keys have changed + */ + case INPUT_REQUEST_SPECIAL_KEYS: + { + DLLExportClass::Set_Special_Key_Flags(special_key_flags); + break; + } + + /* + ** The mouse is moving + */ + case INPUT_REQUEST_MOUSE_MOVE: + { + if (!DLLExportClass::Legacy_Render_Enabled()) { + break; + } + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + if (coord) { + //x -= Map.TacPixelX; + //y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + } + break; + } + + /* + ** Player left-clicked + */ + case INPUT_REQUEST_MOUSE_LEFT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right-clicked (on up) + */ + case INPUT_REQUEST_MOUSE_RIGHT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right button down + */ + case INPUT_REQUEST_MOUSE_RIGHT_DOWN: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, 100, 100); + } + break; + } + + + /* + ** Player drag selected + */ + case INPUT_REQUEST_MOUSE_AREA: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYP_Coord(x1, y1), XYP_Coord(x2, y2), false); + break; + } + + case INPUT_REQUEST_MOUSE_AREA_ADDITIVE: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYP_Coord(x1, y1), XYP_Coord(x2, y2), true); + break; + } + + case INPUT_REQUEST_SELL_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + PlayerPtr->Sell_Wall(cell); + + break; + } + + case INPUT_REQUEST_SELECT_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + Keyboard->MouseQX = x1; + Keyboard->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + DisplayClass::TacButton.Selection_At_Mouse(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + case INPUT_REQUEST_COMMAND_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + Keyboard->MouseQX = x1; + Keyboard->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + DisplayClass::TacButton.Command_Object(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + default: + break; + } +} + + + + +/************************************************************************************************** +* CNC_Handle_Structure_Request -- Process requests to repair and sell structures. +* +* In: +* +* +* Out: +* +* +* +* History: 4/29/2019 - LLL +**************************************************************************************************/ + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_STRUCTURE_REPAIR_START: + DLLExportClass::Repair_Mode(player_id); + break; + case INPUT_STRUCTURE_REPAIR: + DLLExportClass::Repair(player_id, object_id); + break; + case INPUT_STRUCTURE_SELL_START: + DLLExportClass::Sell_Mode(player_id); + break; + case INPUT_STRUCTURE_SELL: + DLLExportClass::Sell(player_id, object_id); + break; + case INPUT_STRUCTURE_CANCEL: + DLLExportClass::Repair_Sell_Cancel(player_id); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Unit_Request -- Process requests on selected units. +* +* In: +* +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_UNIT_SCATTER: + DLLExportClass::Scatter_Selected(player_id); + break; + case INPUT_UNIT_SELECT_NEXT: + DLLExportClass::Select_Next_Unit(player_id); + break; + case INPUT_UNIT_SELECT_PREVIOUS: + DLLExportClass::Select_Previous_Unit(player_id); + break; + case INPUT_UNIT_GUARD_MODE: + DLLExportClass::Selected_Guard_Mode(player_id); + break; + case INPUT_UNIT_STOP: + DLLExportClass::Selected_Stop(player_id); + break; + case INPUT_UNIT_FORMATION_TOGGLE: + DLLExportClass::Team_Units_Formation_Toggle_On(player_id); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_ON: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, true); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_OFF: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, false); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Sidebar_Request -- Process an input request to the sidebar +* +* In: +* +* +* Out: +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) { + + case SIDEBAR_REQUEST_START_CONSTRUCTION: + DLLExportClass::Start_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + DLLExportClass::Hold_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + DLLExportClass::Cancel_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_START_PLACEMENT: + DLLExportClass::Start_Placement(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_PLACE: + DLLExportClass::Place(player_id, buildable_type, buildable_id, cell_x, cell_y); + break; + + case SIDEBAR_CANCEL_PLACE: + DLLExportClass::Cancel_Placement(player_id, buildable_type, buildable_id); + break; + + default: + break; + } +} + +/************************************************************************************************** +* CNC_Handle_SuperWeapon_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON: + DLLExportClass::Place_Super_Weapon(player_id, buildable_type, buildable_id, x1, y1); + break; + } +} + +/************************************************************************************************** +* CNC_Handle_ControlGroup_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case CONTROL_GROUP_REQUEST_CREATE: + DLLExportClass::Create_Control_Group(control_group_index); + break; + case CONTROL_GROUP_REQUEST_TOGGLE: + DLLExportClass::Toggle_Control_Group_Selection(control_group_index); + break; + case CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION: + DLLExportClass::Add_To_Control_Group(control_group_index); + break; + + } +} + + + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get a snapshot of the sidebar state +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCSidebarStruct *sidebar = (CNCSidebarStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*sidebar); // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + sidebar->Credits = 0; + sidebar->CreditsCounter = 0; + sidebar->Tiberium = 0; + sidebar->MaxTiberium = 0; + sidebar->PowerProduced = 0; + sidebar->PowerDrained = 0; + sidebar->RepairBtnEnabled = false; + sidebar->SellBtnEnabled = false; + sidebar->RadarMapActive = false; + sidebar->MissionTimer = Scen.MissionTimer.Is_Active() ? (Scen.MissionTimer / TICKS_PER_SECOND) : -1; + + sidebar->UnitsKilled = 0; + sidebar->BuildingsKilled = 0; + sidebar->UnitsLost = 0; + sidebar->BuildingsLost = 0; + sidebar->TotalHarvestedCredits = 0; + + if (PlayerPtr) { + sidebar->Credits = PlayerPtr->Credits; + sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Current; // Timed display + // sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Credits; // Actual + sidebar->Tiberium = PlayerPtr->Tiberium; + sidebar->MaxTiberium = PlayerPtr->Capacity; + sidebar->PowerProduced = PlayerPtr->Power; + sidebar->PowerDrained = PlayerPtr->Drain; + + sidebar->RepairBtnEnabled = PlayerPtr->BScan > 0; + sidebar->SellBtnEnabled = PlayerPtr->BScan > 0; + sidebar->RadarMapActive = PlayerPtr->Radar == RADAR_ON; + + + // A. Get the DestroyedBuildings and DestroyedInfantry stats if they are available at this point + if (PlayerPtr->DestroyedBuildings) { + for ( int index = 0; index < PlayerPtr->DestroyedBuildings->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedBuildings->Get_Unit_Total( index ); + sidebar->BuildingsKilled += count; + } + } + if (PlayerPtr->DestroyedInfantry) { + for ( int index = 0; index < PlayerPtr->DestroyedInfantry->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedInfantry->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedUnits) { + for ( int index = 0; index < PlayerPtr->DestroyedUnits->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedUnits->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedAircraft) { + for ( int index = 0; index < PlayerPtr->DestroyedAircraft->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedAircraft->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + + // B. If the DestroyedBuildings and DestroyedInfantry stats seemed to be unvailable, this is another way to do it + // Note that we need to do both of these depending on which type of match we are running, as well as for Replays/Observer and live stats reporting + // We can't just do it this way for everything, as it does not work for all cases + if (sidebar->BuildingsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->BuildingsKilled += PlayerPtr->BuildingsKilled[ house_index ]; + } + } + if (sidebar->UnitsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->UnitsKilled += PlayerPtr->UnitsKilled[ house_index ]; // Includes Infantry, Vehicles, Aircraft + } + } + + + + sidebar->UnitsLost = PlayerPtr->UnitsLost; + sidebar->BuildingsLost = PlayerPtr->BuildingsLost; + sidebar->TotalHarvestedCredits = PlayerPtr->HarvestedCredits; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = Map.Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = Map.Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = Map.Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = Map.Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(Map.Column[c].Buildables[b].BuildableType, Map.Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60; + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + + bool isbusy = false; + + switch (Map.Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + isbusy |= Units.Avail() <= 0; + sidebar_entry.Type = UNIT_TYPE; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + isbusy |= Aircraft.Avail() <= 0; + sidebar_entry.Type = AIRCRAFT_TYPE; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + sidebar_entry.Fake = build_type->IsFake; + } + break; + + case RTTI_VESSELTYPE: + sidebar_entry.Type = VESSEL_TYPE; + isbusy = (PlayerPtr->VesselFactory != -1); + isbusy |= Vessels.Avail() <= 0; + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + Fill_Sidebar_Entry_From_Special_Weapon(sidebar_entry, super_weapon, (SpecialWeaponType)Map.Column[c].Buildables[b].BuildableID); + break; + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + int fnumber = Map.Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + + } else { + + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = context_sidebar->Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = context_sidebar->Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = context_sidebar->Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = context_sidebar->Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(context_sidebar->Column[c].Buildables[b].BuildableType, context_sidebar->Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60; + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + bool isbusy = false; + + switch (context_sidebar->Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + isbusy |= Units.Avail() <= 0; + sidebar_entry.Type = UNIT_TYPE; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + isbusy |= Aircraft.Avail() <= 0; + sidebar_entry.Type = AIRCRAFT_TYPE; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + sidebar_entry.Fake = build_type->IsFake; + break; + } + + case RTTI_VESSELTYPE: + isbusy = (PlayerPtr->VesselFactory != -1); + isbusy |= Vessels.Avail() <= 0; + sidebar_entry.Type = VESSEL_TYPE; + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + Fill_Sidebar_Entry_From_Special_Weapon(sidebar_entry, super_weapon, (SpecialWeaponType)sidebar_entry.BuildableID); + break; + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + } + } + + + + return true; +} + + +void DLLExportClass::Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type) +{ + switch (type) + { + case ACTION_NONE: + default: + dll_type = DAT_NONE; + break; + case ACTION_MOVE: + dll_type = DAT_MOVE; + break; + case ACTION_NOMOVE: + dll_type = DAT_NOMOVE; + break; + case ACTION_ENTER: + dll_type = DAT_ENTER; + break; + case ACTION_SELF: + dll_type = DAT_SELF; + break; + case ACTION_ATTACK: + if (Target_Legal(target) && (object != NULL) && object->Is_Techno() && ((TechnoClass*)object)->In_Range(target, 0)) { + dll_type = DAT_ATTACK; + } + else { + dll_type = DAT_ATTACK_OUT_OF_RANGE; + } + break; + case ACTION_GUARD_AREA: + dll_type = DAT_GUARD; + break; + case ACTION_HARVEST: + dll_type = DAT_ATTACK; + break; + case ACTION_SELECT: + case ACTION_TOGGLE_SELECT: + dll_type = DAT_SELECT; + break; + case ACTION_CAPTURE: + dll_type = DAT_CAPTURE; + break; + case ACTION_DAMAGE: + dll_type = DAT_DAMAGE; + break; + case ACTION_SABOTAGE: + dll_type = DAT_SABOTAGE; + break; + case ACTION_HEAL: + dll_type = DAT_HEAL; + break; + case ACTION_TOGGLE_PRIMARY: + dll_type = DAT_TOGGLE_PRIMARY; + break; + case ACTION_NO_DEPLOY: + dll_type = DAT_CANT_DEPLOY; + break; + case ACTION_GREPAIR: + dll_type = DAT_REPAIR; + break; + case ACTION_NO_GREPAIR: + dll_type = DAT_CANT_REPAIR; + break; + } +} + + +void DLLExportClass::Convert_Special_Weapon_Type(SpecialWeaponType weapon_type, DllSuperweaponTypeEnum& dll_weapon_type, char* weapon_name) +{ + switch (weapon_type) + { + case SPC_SONAR_PULSE: + dll_weapon_type = SW_SONAR_PULSE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_SonarPulse", 16); + } + break; + case SPC_NUCLEAR_BOMB: + dll_weapon_type = SW_NUKE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Nuke", 16); + } + break; + case SPC_CHRONOSPHERE: + dll_weapon_type = SW_CHRONOSPHERE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Chrono", 16); + } + break; + case SPC_PARA_BOMB: + dll_weapon_type = SW_PARA_BOMB; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_ParaBomb", 16); + } + break; + case SPC_PARA_INFANTRY: + dll_weapon_type = SW_PARA_INFANTRY; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_ParaInfantry", 16); + } + break; + case SPC_SPY_MISSION: + dll_weapon_type = SW_SPY_MISSION; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_SpyMission", 16); + } + break; + case SPC_IRON_CURTAIN: + dll_weapon_type = SW_IRON_CURTAIN; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_IronCurtain", 16); + } + break; + case SPC_GPS: + dll_weapon_type = SW_GPS; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_GPS", 16); + } + break; + case SPC_CHRONO2: + dll_weapon_type = SW_CHRONOSPHERE_DESTINATION; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Chrono2", 16); + } + break; + default: + dll_weapon_type = SW_UNKNOWN; + if (weapon_name != NULL) + { + weapon_name[0] = '\0'; + } + break; + } +} + + +void DLLExportClass::Fill_Sidebar_Entry_From_Special_Weapon(CNCSidebarEntryStruct& sidebar_entry_out, SuperClass*& super_weapon_out, SpecialWeaponType weapon_type) +{ + sidebar_entry_out.Type = SPECIAL; + + switch (weapon_type) + { + case SPC_SONAR_PULSE: + case SPC_NUCLEAR_BOMB: + case SPC_CHRONOSPHERE: + case SPC_PARA_BOMB: + case SPC_PARA_INFANTRY: + case SPC_SPY_MISSION: + case SPC_IRON_CURTAIN: + case SPC_GPS: + case SPC_CHRONO2: + Convert_Special_Weapon_Type(weapon_type, sidebar_entry_out.SuperWeaponType, sidebar_entry_out.AssetName); + break; + default: + sidebar_entry_out.SuperWeaponType = SW_UNKNOWN; + sidebar_entry_out.Type = UNKNOWN; + super_weapon_out = nullptr; + return; + } + + super_weapon_out = &(PlayerPtr->SuperWeapon[weapon_type]); +} + +static const int _map_width_shift_bits = 7; + +void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + static const FacingType _scan_facings[] = { + FACING_E, + FACING_S, + FACING_W, + FACING_N + }; + + memset(placement_distance, 255U, MAP_CELL_TOTAL); + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++) { + CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); + BuildingClass* base = (BuildingClass*)Map[cell].Cell_Find_Object(RTTI_BUILDING); + if ((base && base->House->Class->House == PlayerPtr->Class->House && base->Class->IsBase) || + ((placement_type->IsWall || ((Map[cell].Smudge != SMUDGE_NONE) && SmudgeTypeClass::As_Reference(Map[cell].Smudge).IsBib)) && + Map[cell].Owner == PlayerPtr->Class->House)) { + placement_distance[cell] = 0U; + CELL startcell = cell; + for (unsigned char distance = 1U; distance <= (placement_type->Adjacent + 1U); distance++) { + startcell = Adjacent_Cell(startcell, FACING_NW); + CELL scancell = startcell; + for (int i = 0; i < ARRAY_SIZE(_scan_facings); i++) { + CELL nextcell = scancell; + for (unsigned char scan = 0U; scan <= (distance * 2U); scan++) { + scancell = nextcell; + if (Map.In_Radar(scancell)) { + placement_distance[scancell] = min(placement_distance[scancell], distance); + } + nextcell = Adjacent_Cell(scancell, _scan_facings[i]); + } + } + } + } + } + } +} + +void Recalculate_Placement_Distances() +{ + DLLExportClass::Recalculate_Placement_Distances(); +} + +void DLLExportClass::Recalculate_Placement_Distances() +{ + if (PlacementType[CurrentLocalPlayerIndex] != NULL) { + Calculate_Placement_Distances(PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + } +} + +/************************************************************************************************** +* DLLExportClass::Get_Placement_State -- Get a snapshot of legal validity of placing a structure on all map cells +* +* In: +* +* Out: +* +* +* +* History: 2/4/2019 3:11PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (PlacementType[CurrentLocalPlayerIndex] == NULL) { + return false; + } + + CNCPlacementInfoStruct *placement_info = (CNCPlacementInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*placement_info); // Base amount needed. Will need more depending on how many entries there are + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + memory_needed += map_cell_width * map_cell_height * sizeof(CNCPlacementCellInfoStruct); + + if (memory_needed + 128 >= buffer_size) { + return false; + } + + placement_info->Count = map_cell_width * map_cell_height; + + int index = 0; + for (int y=0 ; y < map_cell_height ; y++) { + for (int x=0 ; x < map_cell_width ; x++) { + + CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); + + bool pass = Passes_Proximity_Check(cell, PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + + CellClass * cellptr = &Map[cell]; + bool clear = cellptr->Is_Clear_To_Build(PlacementType[CurrentLocalPlayerIndex]->Speed); + + CNCPlacementCellInfoStruct &placement_cell_info = placement_info->CellInfo[index++]; + placement_cell_info.PassesProximityCheck = pass; + placement_cell_info.GenerallyClear = clear; + } + } + + Map.ZoneOffset = 0; + + return true; +} + + +bool DLLExportClass::Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance) +{ + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + short const *occupy_list = placement_type->Occupy_List(true); + + while (*occupy_list != REFRESH_EOL) { + + CELL center_cell = cell_in + *occupy_list++; + + if (!Map.In_Radar(center_cell)) { + return false; + } + + if (placement_distance[center_cell] <= (placement_type->Adjacent + 1)) { + return true; + } + } + + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Start_Construction -- Start sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Hold_Construction -- Pause sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Hold_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Cancel_Construction -- Stop sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + return Cancel_Placement(player_id, buildable_type, buildable_id) && + ((GAME_TO_PLAY == GAME_NORMAL) ? + Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id) : + MP_Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id)); +} + + + +/************************************************************************************************** +* DLLExportClass::Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = PlayerPtr->Fetch_Factory((RTTIType)buildable_type); + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && factory != NULL) { + return(false); + } + + if (factory) { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + } + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + Queue_AI(); + + return true; + } + } + } + } + } + } + } + return false; +} + + + + +/************************************************************************************************** +* DLLExportClass::MP_Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 3/26/2019 1:02PM - ST +**************************************************************************************************/ +bool DLLExportClass::MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && genfactory != -1) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return(false); + } + + if (factory) { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + //Speak(VOX_NO_FACTORY); + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + if (DLLExportClass::Legacy_Render_Enabled()) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + Unselect_All(); + } + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + break; + } + + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** + */ + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + DLLExportClass::Glyphx_Queue_AI(); + + return true; + } + } + } + } + } + } + } + return false; +} + + + + + +/************************************************************************************************** +* DLLExportClass::Start_Placement -- Start placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + Recalculate_Placement_Distances(); + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + } + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Cancel_Placement -- Cancel placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 2/7/2019 10:52AM - ST +**************************************************************************************************/ +bool DLLExportClass::Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + PlacementType[CurrentLocalPlayerIndex] = NULL; + + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.IsTargettingMode = SPC_NONE; + Map.Set_Cursor_Shape(0); + + return true; +} + + + +/************************************************************************************************** +* DLLExportClass::Place -- Place a completed structure down +* +* In: +* +* Out: +* +* +* +* History: 2/6/2019 11:51AM - ST +**************************************************************************************************/ +bool DLLExportClass::Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + static const int _map_width_shift_bits = 7; + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + + /* + ** The cell coordinates passed in will be relative to the playable area that the client knows about + */ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << _map_width_shift_bits); + + /* + ** Call the place directly instead of queueing it, so we can evaluate the return code. + */ + if (PlayerPtr->Place_Object(building->What_Am_I(), cell + Map.ZoneOffset)) { + PlacementType[CurrentLocalPlayerIndex] = NULL; + } + } + } + return true; + +} + + + + +BuildingClass *DLLExportClass::Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + + } else { + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + } + } + return NULL; +} + +/************************************************************************************************** +* DLLExportClass::Place_Super_Weapon +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y) +{ + if (buildable_type != RTTI_SPECIAL) + { + return false; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + SpecialWeaponType weapon_type = (SpecialWeaponType)buildable_id; + + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, weapon_type, cell)); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Create_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Create_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 2); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Add_To_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Add_To_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 1); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Toggle_Control_Group_Selection +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Toggle_Control_Group_Selection(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 0); + + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Get_Shroud_State -- Get a snapshot of the shroud for the given player +* +* In: +* +* Out: +* +* +* +* History: 4/12/2019 3:44PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCShroudStruct *shroud = (CNCShroudStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*shroud) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++) { + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCShroudEntryStruct); + if (memory_needed >= buffer_size) { + return false; + } + + int xpixel; + int ypixel; + + Map.Coord_To_Pixel(coord, xpixel, ypixel); + + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + CNCShroudEntryStruct &shroud_entry = shroud->Entries[entry_index]; + + shroud_entry.IsVisible = cellptr->Is_Visible(PlayerPtr); + shroud_entry.IsMapped = cellptr->Is_Mapped(PlayerPtr); + shroud_entry.IsJamming = cellptr->Is_Jamming(PlayerPtr); + //shroud_entry.IsVisible = cellptr->IsVisible; + //shroud_entry.IsMapped = cellptr->IsMapped; + shroud_entry.ShadowIndex = -1; + + if (shroud_entry.IsMapped) { + if (!shroud_entry.IsVisible) { + shroud_entry.ShadowIndex = (char)Map.Cell_Shadow(cell, PlayerPtr); + } + } + + entry_index++; + } + } + + shroud->Count = entry_index; + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Occupier_State -- Get the occupier state for this player +* +* In: +* +* Out: +* +* +* +* History: 10/25/2019 - SKY +**************************************************************************************************/ +bool DLLExportClass::Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + UNREFERENCED_PARAMETER(player_id); + + CNCOccupierHeaderStruct* occupiers = (CNCOccupierHeaderStruct*)buffer_in; + CNCOccupierEntryHeaderStruct* entry = reinterpret_cast(occupiers + 1U); + occupiers->Count = 0; + + unsigned int memory_needed = sizeof(CNCOccupierHeaderStruct); + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++, occupiers->Count++) { + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + int occupier_count = 0; + ObjectClass* optr = cellptr->Cell_Occupier(); + while (optr != NULL) { + occupier_count++; + optr = optr->Next; + } + + memory_needed += sizeof(CNCOccupierEntryHeaderStruct) + (sizeof(CNCOccupierObjectStruct) * occupier_count); + if (memory_needed >= buffer_size) { + return false; + } + + CNCOccupierObjectStruct* occupier = reinterpret_cast(entry + 1U); + entry->Count = 0; + + optr = cellptr->Cell_Occupier(); + for (int i = 0; i < occupier_count; i++, occupier++, entry->Count++) { + CNCObjectStruct object; + Convert_Type(optr, object); + occupier->Type = object.Type; + occupier->ID = object.ID; + optr = optr->Next; + } + + entry = reinterpret_cast(occupier + 1U); + } + } + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Player_Info_State -- Get the multiplayer info for this player +* +* In: +* +* Out: +* +* +* +* History: 4/22/2019 10:33AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCPlayerInfoStruct *player_info = (CNCPlayerInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*player_info) + 32; // A little extra for no reason + + if (memory_needed >= buffer_size) { + return false; + } + + player_info->GlyphxPlayerID = 0; + + if (PlayerPtr == NULL) { + return false;; + } + + if (Session.Players.Count() > CurrentLocalPlayerIndex) { + strncpy(&player_info->Name[0], Session.Players[CurrentLocalPlayerIndex]->Name, MPLAYER_NAME_MAX); + } + + player_info->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated + player_info->House = PlayerPtr->Class->House; + player_info->AllyFlags = PlayerPtr->Get_Ally_Flags(); + + if (Session.Players.Count() > CurrentLocalPlayerIndex) { + player_info->ColorIndex = Session.Players[CurrentLocalPlayerIndex]->Player.Color; + } else { + player_info->ColorIndex = (PlayerPtr->Class->House == HOUSE_USSR) ? 2 : 1; // Fudge to a sensible color in campaign; 2 = red, 1 = blue + } + + player_info->GlyphxPlayerID = player_id; + player_info->HomeCellX = Cell_X(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->HomeCellY = Cell_Y(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->IsDefeated = PlayerPtr->IsDefeated; + + // Can see other players' power if ally (except for the player themself) or spying on a power plant + // Can see other players' money if spying on a resource building + player_info->SpiedPowerFlags = 0U; + player_info->SpiedMoneyFlags = 0U; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* house = Houses.Ptr(i); + if ((house != nullptr) && house->IsActive && (house != PlayerPtr) && house->Is_Ally(PlayerPtr)) { + player_info->SpiedPowerFlags |= 1U << house->Class->House; + } + } + for (int i = 0; i < Buildings.Count(); ++i) { + BuildingClass* building = Buildings.Ptr(i); + if ((building != nullptr) && building->IsActive && (building->Spied_By() & (1U << PlayerPtr->Class->House))) { + if ((*building == STRUCT_POWER) || (*building == STRUCT_ADVANCED_POWER)) { + player_info->SpiedPowerFlags |= 1U << building->House->Class->House; + } else if ((*building == STRUCT_REFINERY) || (*building == STRUCT_STORAGE)) { + player_info->SpiedMoneyFlags |= 1U << building->House->Class->House; + } + } + } + + // Populate spied data + for (char house = 0; house < MAX_HOUSES; ++house) { + HouseClass* hptr = HouseClass::As_Pointer((HousesType)house); + if ((hptr != nullptr) && hptr->IsActive) { + if (player_info->SpiedPowerFlags & (1U << house)) { + player_info->SpiedInfo[house].Power = hptr->Power; + player_info->SpiedInfo[house].Drain = hptr->Drain; + } + if (player_info->SpiedMoneyFlags & (1U << house)) { + player_info->SpiedInfo[house].Money = hptr->Available_Money(); + } + } + } + + // Populate selection info + if (CurrentObject.Count() > 0) { + CNCObjectStruct object; + Convert_Type(CurrentObject[0], object); + player_info->SelectedID = object.ID; + player_info->SelectedType = object.Type; + + const int left = Map.MapCellX; + const int right = Map.MapCellX + Map.MapCellWidth - 1; + const int top = Map.MapCellY; + const int bottom = Map.MapCellY + Map.MapCellHeight - 1; + + // Use first object with a weapon, or first object if none + ObjectClass* action_object = nullptr; + for (int i = 0; i < CurrentObject.Count(); ++i) { + ObjectClass* object = CurrentObject[i]; + if (object->Is_Techno()) { + TechnoClass* techno = (TechnoClass*)object; + if (techno->Techno_Type_Class()->PrimaryWeapon != NULL || techno->Techno_Type_Class()->SecondaryWeapon != NULL) { + action_object = object; + break; + } + } + } + if (action_object == nullptr) { + action_object = CurrentObject[0]; + } + + int index = 0; + for (int y = top; y <= bottom; ++y) { + for (int x = left; x <= right; ++x, ++index) { + Convert_Action_Type(action_object->What_Action(XY_Cell(x, y)), (CurrentObject.Count() == 1) ? action_object : NULL, As_Target(XY_Cell(x, y)), player_info->ActionWithSelected[index]); + } + } + + player_info->ActionWithSelectedCount = Map.MapCellWidth * Map.MapCellHeight; + } + else { + player_info->SelectedID = -1; + player_info->SelectedType = UNKNOWN; + player_info->ActionWithSelectedCount = 0U; + } + + // Screen shake + player_info->ScreenShake = PlayerPtr->ScreenShakeTime; + + // Radar jammed + player_info->IsRadarJammed = Map.Get_Jammed(PlayerPtr); + + return true; +}; + + + + +/************************************************************************************************** +* DLLExportClass::Get_Dynamic_Map_State -- Get a snapshot of the smudges and overlays on the terrain +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 10:45AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + player_id; + + static int _call_count = 0; + + CNCDynamicMapStruct *dynamic_map = (CNCDynamicMapStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*dynamic_map) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + int cell_index = 0; + + bool debug_output = false; + //if (_call_count == 20) { + //debug_output = true; + //} + + // Need to ignore view constraints for dynamic map updates, so the radar map + // has the latest tiberium state for cells outside the tactical view + DLLExportClass::Adjust_Internal_View(true); + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCDynamicMapEntryStruct) * 2; + if (memory_needed >= buffer_size) { + return false; + } + + /* + ** Only cells flagged to be redraw are examined. + */ + //if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Map.Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER || cellptr->IsMapped || Debug_Unshroud) { + Cell_Class_Draw_It(dynamic_map, entry_index, cellptr, xpixel, ypixel, debug_output); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + //if (!cellptr->IsMapped && !Debug_Unshroud) { + // IsShadowPresent = true; + //} + } + //} + } + } + + if (entry_index) { + _call_count++; + } + + dynamic_map->Count = entry_index; + + dynamic_map->VortexActive = ChronalVortex.Is_Active(); + dynamic_map->VortexX = Coord_X(ChronalVortex.Get_Position()); + dynamic_map->VortexY = Coord_Y(ChronalVortex.Get_Position()); + dynamic_map->VortexWidth = Pixel_To_Lepton(64); + dynamic_map->VortexHeight = Pixel_To_Lepton(64); + + return true; +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Cell_Class_Draw_It -- Go through the motions of drawing a cell to get the smudge and overlay info +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 11:09AM - ST +**************************************************************************************************/ +void DLLExportClass::Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output) +{ + /* + ** + ** Based on CellClass::Draw_It and SmudgeTypeClass::Draw_It + ** + ** + */ + + CELL cell = cell_ptr->Cell_Number(); + + /* + ** Redraw any smudge. + */ + if (cell_ptr->Smudge != SMUDGE_NONE) { + //SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + + const SmudgeTypeClass &smudge_type = SmudgeTypeClass::As_Reference(cell_ptr->Smudge); + + if (smudge_type.Get_Image_Data() != NULL) { + +if (debug_output) { + IsTheaterShape = true; + Debug_Write_Shape_Type(&smudge_type, 0); + IsTheaterShape = false; +} + + CNCDynamicMapEntryStruct &smudge_entry = dynamic_map->Entries[entry_index++]; + + strncpy(smudge_entry.AssetName, smudge_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + smudge_entry.Type = (short) cell_ptr->Smudge; + smudge_entry.Owner = (char)cell_ptr->Owner; + smudge_entry.DrawFlags = SHAPE_WIN_REL; // Looks like smudges are drawn top left + smudge_entry.PositionX = xpixel; + smudge_entry.PositionY = ypixel; + smudge_entry.Width = Get_Build_Frame_Width(smudge_type.Get_Image_Data()); + smudge_entry.Height = Get_Build_Frame_Height(smudge_type.Get_Image_Data()); + smudge_entry.CellX = Cell_X(cell); + smudge_entry.CellY = Cell_Y(cell); + smudge_entry.ShapeIndex = cell_ptr->SmudgeData; + smudge_entry.IsSmudge = true; + smudge_entry.IsOverlay = false; + smudge_entry.IsResource = false; + smudge_entry.IsSellable = false; + smudge_entry.IsTheaterShape = true; // Smudges are always theater-specific + smudge_entry.IsFlag = false; + } + } + + /* + ** Draw the overlay object. + */ + if (cell_ptr->Overlay != OVERLAY_NONE) { + //OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + //IsTheaterShape = (bool)otype.IsTheater; + //CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + //IsTheaterShape = false; + + const OverlayTypeClass &overlay_type = OverlayTypeClass::As_Reference(cell_ptr->Overlay); + + if (overlay_type.Get_Image_Data() != NULL) { + + CNCDynamicMapEntryStruct &overlay_entry = dynamic_map->Entries[entry_index++]; + + +if (debug_output) { + IsTheaterShape = (bool)overlay_type.IsTheater; + Debug_Write_Shape_Type(&overlay_type, 0); + IsTheaterShape = false; +} + + strncpy(overlay_entry.AssetName, overlay_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + overlay_entry.Type = (short)cell_ptr->Overlay; + overlay_entry.Owner = (char)cell_ptr->Owner; + overlay_entry.DrawFlags = SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST; // Looks like overlays are drawn centered and translucent + overlay_entry.PositionX = xpixel + (CELL_PIXEL_W>>1); + overlay_entry.PositionY = ypixel + (CELL_PIXEL_H>>1); + overlay_entry.Width = Get_Build_Frame_Width(overlay_type.Get_Image_Data()); + overlay_entry.Height = Get_Build_Frame_Height(overlay_type.Get_Image_Data()); + overlay_entry.CellX = Cell_X(cell); + overlay_entry.CellY = Cell_Y(cell); + overlay_entry.ShapeIndex = cell_ptr->OverlayData; + overlay_entry.IsSmudge = false; + overlay_entry.IsOverlay = true; + overlay_entry.IsResource = overlay_entry.Type >= OVERLAY_GOLD1 && overlay_entry.Type <= OVERLAY_GEMS4; + overlay_entry.IsSellable = (overlay_entry.Type >= OVERLAY_SANDBAG_WALL && overlay_entry.Type <= OVERLAY_WOOD_WALL) || overlay_entry.Type == OVERLAY_FENCE; + overlay_entry.IsTheaterShape = (bool)overlay_type.IsTheater; + overlay_entry.IsFlag = false; + } + } + + + if (cell_ptr->IsFlagged) { + + const void* image_data = MFCD::Retrieve("FLAGFLY.SHP"); + if (image_data != NULL) { + + CNCDynamicMapEntryStruct &flag_entry = dynamic_map->Entries[entry_index++]; + + strncpy(flag_entry.AssetName, "FLAGFLY", CNC_OBJECT_ASSET_NAME_LENGTH); + flag_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + flag_entry.Type = -1; + flag_entry.Owner = cell_ptr->Owner; + flag_entry.DrawFlags = SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING; + flag_entry.PositionX = xpixel + (ICON_PIXEL_W / 2); + flag_entry.PositionY = ypixel + (ICON_PIXEL_H / 2); + flag_entry.Width = Get_Build_Frame_Width(image_data); + flag_entry.Height = Get_Build_Frame_Height(image_data); + flag_entry.CellX = Cell_X(cell); + flag_entry.CellY = Cell_Y(cell); + flag_entry.ShapeIndex = Frame % 14; + flag_entry.IsSmudge = false; + flag_entry.IsOverlay = false; + flag_entry.IsResource = false; + flag_entry.IsSellable = false; + flag_entry.IsTheaterShape = false; + flag_entry.IsFlag = true; + } + + } + +} + + + + +/************************************************************************************************** +* DLLExportClass::Glyphx_Queue_AI -- Special queue processing for Glyphx multiplayer mode +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Glyphx_Queue_AI(void) +{ + + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + OutList.Next(); + } + + /* + ** Based on Execute_DoList in queue.cpp + ** + ** The events have the ID of the player encoded in them, so no special per-player processing should be needed. + ** When the event is created, the 'local player' is assumed to be the originator of the event, so PlayerPtr will need + ** to be swapped out to represent the real originating player prior to any events being created as a result of GlyphX input + ** + ** ST - 3/12/2019 10:51AM + */ + + for (int i = 0; i < MULTIPLAYER_COUNT; i++) { + + HousesType house; + HouseClass *housep; + + house = Session.Players[i]->Player.ID; + + housep= HouseClass::As_Pointer (house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!housep){ + continue; + } + + if (!housep->IsHuman){ + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (int j = 0; j < DoList.Count; j++) { + + if (!DoList[j].IsExecuted && (unsigned)Frame >= DoList[j].Frame) { + DoList[j].Execute(); + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + } + } + } + + + + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + while (DoList.Count) { + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + } + else { + break; + } + } + +} + +CarryoverClass Test_CC(CarryoverObjectStruct* cptr) +{ + CarryoverClass cc; + + cc.RTTI = (RTTIType)cptr->RTTI; + cc.Type.Building = (StructType)cptr->Type; //This works regardless of what the RTTI-type and the enum type, because they're all enums. - LLL + cc.Cell = (CELL)cptr->Cell; + cc.House = (HousesType)cptr->House; + cc.Strength = cptr->Strength; + + return cc; +} + + +/************************************************************************************************** +* DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars +* +* +* +* History: 11/8/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Store_Carryover_Objects() +{ + if (EventCallback == NULL || Scen.IsToCarryOver == false) { + return; + } + + CarryoverObjectStruct* carryover_list = 0; + CarryoverObjectStruct* carryover_list_tail = 0; + + /* + ** Record all objects, that are to be part of the carry over set, into the carry over list. + */ + for (int building_index = 0; building_index < Buildings.Count(); building_index++) { + BuildingClass * building = Buildings.Ptr(building_index); + + if (building && !building->IsInLimbo && building->Strength > 0) { + CarryoverClass carryover = CarryoverClass(building); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + UnitClass * unit = Units.Ptr(unit_index); + + if (unit && !unit->IsInLimbo && unit->Strength > 0) { + CarryoverClass carryover = CarryoverClass(unit); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Unit; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + InfantryClass * infantry = Infantry.Ptr(infantry_index); + + if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { + CarryoverClass carryover = CarryoverClass(infantry); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + VesselClass * vessel = Vessels.Ptr(vessel_index); + + if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { + CarryoverClass carryover = CarryoverClass(vessel); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + + //Make & Send Event + EventCallbackStruct event; + event.EventType = CALLBACK_EVENT_STORE_CARRYOVER_OBJECTS; + event.CarryoverObjects.CarryoverList = carryover_list; + EventCallback(event); + + //Delete the list + while (carryover_list) { + CarryoverObjectStruct* cptr = (CarryoverObjectStruct*)carryover_list->Next; + delete carryover_list; + carryover_list = cptr; + } +} + + +/************************************************************************************************** +* DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:10PM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Sidebars(void) +{ + for (int i=0 ; i= Session.Players.Count()) { + continue; + } + if (Session.Players[i] == NULL) { + continue; + } + HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + MultiplayerSidebars[i].Init_Clear(player_ptr); + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Set_Player_Context -- Switch the C&C local player context +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +bool DLLExportClass::Set_Player_Context(uint64 glyphx_player_id, bool force) +{ + /* + ** Context never needs to change in single player + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + if (PlayerPtr) { + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + } + return true; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + for (int i=0 ; iPlayer.ID); + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + CurrentLocalPlayerIndex = i; + + Refresh_Player_Control_Flags(); + + return true; + } + } + + return false; +} + + + +/************************************************************************************************** +* DLLExportClass::Reset_Player_Context -- Clear out old player context data +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 10:36AM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Player_Context(void) +{ + CurrentLocalPlayerIndex = 0; + CurrentObject.Clear_All(); +} + + + +/************************************************************************************************** +* DLLExportClass::Refresh_Player_Control_Flags -- Set the IsPlayerControl flags so that the player +* in context has IsPlayerControl +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 10:36AM - ST +**************************************************************************************************/ +void DLLExportClass::Refresh_Player_Control_Flags(void) +{ + for (int i=0 ; iPlayer.ID); + + if (player_ptr) { + + if (i == CurrentLocalPlayerIndex && player_ptr->IsHuman) { + player_ptr->IsPlayerControl = true; + } else { + player_ptr->IsPlayerControl = false; + } + } + } +} + + +/************************************************************************************************** +* Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void Logic_Switch_Player_Context(ObjectClass *object) +{ + DLLExportClass::Logic_Switch_Player_Context(object); +} + + +/************************************************************************************************** +* DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void DLLExportClass::Logic_Switch_Player_Context(ObjectClass *object) +{ + if (object == NULL) { + return; + } + + /* + ** If it's not a techno, it can't be owned. + */ + if (!object->Is_Techno()) { + return; + } + + TechnoClass *tech = static_cast(object); + + //HousesType house = tech->House->Class->House; + DLLExportClass::Logic_Switch_Player_Context(tech->House); +} + + + +/************************************************************************************************** +* Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void Logic_Switch_Player_Context(HouseClass *object) +{ + DLLExportClass::Logic_Switch_Player_Context(object); +} + + +/************************************************************************************************** +* DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void DLLExportClass::Logic_Switch_Player_Context(HouseClass *house) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + return; + } + + if (house == NULL) { + return; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + HousesType house_type = house->Class->House; + + for (int i=0 ; iPlayer.ID) { + + if (i == CurrentLocalPlayerIndex) { + return; + } + + PlayerPtr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + CurrentLocalPlayerIndex = i; + + Refresh_Player_Control_Flags(); + return; + } + } +} + + +/************************************************************************************************** +* DLLExportClass::Calculate_Start_Positions -- Calculate the initial view positions for the players +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 6/12/2019 3:00PM - ST +**************************************************************************************************/ +void DLLExportClass::Calculate_Start_Positions(void) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + MultiplayerStartPositions[0] = Scen.Views[0]; + return; + } + + HouseClass *player_ptr = PlayerPtr; + + ScenarioInit++; + COORDINATE old_tac = Map.TacticalCoord; + for (int i=0 ; i< MULTIPLAYER_COUNT; i++) { + PlayerPtr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (PlayerPtr) { + long x, y; + Map.Compute_Start_Pos(x, y); + MultiplayerStartPositions[i] = XY_Cell(x, y); + } + } + Map.TacticalCoord = old_tac; + ScenarioInit--; + + PlayerPtr = player_ptr; +} + + + +/************************************************************************************************** +* DLLExportClass::Get_GlyphX_Player_ID -- Get the external GlyphX player ID from the C&C house/player pointer +* Returns 0 in single player or if player ID isn't found +* +* In: +* +* Out: +* +* +* +* History: 4/22/2019 6:23PM - ST +**************************************************************************************************/ +__int64 DLLExportClass::Get_GlyphX_Player_ID(const HouseClass *house) +{ + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + if (GAME_TO_PLAY == GAME_NORMAL) { + return 0; + } + + if (house == NULL) { + return 0; + } + + HousesType house_type = house->Class->House; + + for (int i=0 ; iPlayer.ID) { + return GlyphxPlayerIDs[i]; + } + } + + /* + ** Failure case. + */ + return 0; +} + + + + +/************************************************************************************************** +* DLLExportClass::Adjust_Internal_View -- Set the internal tactical view to encompass the input coordinates +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 3:00PM - ST +**************************************************************************************************/ +void DLLExportClass::Adjust_Internal_View(bool force_ignore_view_constraints) +{ + /* + ** When legacy rendering is disabled (especially in multiplayer) we can get input coordinates that + ** fall outside the engine's tactical view. In this case, we need to adjust the tactical view before the + ** input will behave as expected. + */ + + if (!force_ignore_view_constraints && Legacy_Render_Enabled()) { + /* + ** Render view should already be tracking the player's local view + */ + DisplayClass::IgnoreViewConstraints = false; + return; + } + + DisplayClass::IgnoreViewConstraints = true; +} + + + + + +/************************************************************************************************** +* DLLExportClass::Get_Current_Context_Sidebar -- Get the sidebar data for the current player context +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +SidebarGlyphxClass *DLLExportClass::Get_Current_Context_Sidebar(HouseClass *player_ptr) +{ + if (player_ptr) { + + for (int i=0 ; iPlayer.ID)) { + return &MultiplayerSidebars[i]; + } + } + } + return &MultiplayerSidebars[CurrentLocalPlayerIndex]; +} + + +SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr) +{ + return DLLExportClass::Get_Current_Context_Sidebar(player_ptr); +} + + +/************************************************************************************************** +* DLLExportClass::Repair_Mode -- Starts the player's repair mode. All it does here is unselect all units. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + Unselect_All(); +} + +/************************************************************************************************** +* DLLExportClass::Repair -- Repairs a specific building +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair(uint64 player_id, int object_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + TARGET target = Build_Target(RTTI_BUILDING, object_id); + if (target != TARGET_NONE) + { + BuildingClass* building = As_Building(target); + if (building) { + if (!building->IsActive) { + GlyphX_Debug_Print("DLLExportClass::Repair -- trying to repair a non-active building"); + } else { + + if (building->Can_Repair() && building->House.Is_Valid() && building->House->Class->House == PlayerPtr->Class->House) + { + building->Repair(-1); + } + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Sell_Mode -- Starts the player's sell mode. All it does here is unselect all units. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + Unselect_All(); +} + +/************************************************************************************************** +* DLLExportClass::Sell -- Sell's a player's speceific building. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell(uint64 player_id, int object_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + TARGET target = Build_Target(RTTI_BUILDING, object_id); + if (target != TARGET_NONE) + { + BuildingClass* building = As_Building(target); + if (building) { + if (!building->IsActive) { + GlyphX_Debug_Print("DLLExportClass::Sell -- trying to sell a non-active building"); + } else { + if (building->Can_Demolish() && building->House.Is_Valid() && building->House->Class->House == PlayerPtr->Class->House) + { + building->Sell_Back(1); + } + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Repair_Sell_Cancel -- Ends the player's repair or sell mode. Doesn't do anything right now. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair_Sell_Cancel(uint64 player_id) +{ + //OutputDebugString("Repair_Sell_Cancel\n"); +} + +/************************************************************************************************** +* DLLExportClass::Scatter_Selected -- Scatter the selected units +* +* In: +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::Scatter_Selected(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Next_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Next_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Previous_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Previous_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Guard_Mode +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Guard_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Stop +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Stop(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // Copied from RedAlert/Conquer.cpp - Keyboard_Process() with Options.KeyStop (VK_S) + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Units_Queued_Movement_Toggle +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Units_Queued_Movement_Toggle(uint64 player_id, bool toggle) +{ + // Currently Red Alert only + + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (PlayerPtr != NULL) + { + PlayerPtr->IsQueuedMovementToggle = toggle; + } +} + +/************************************************************************************************** +* DLLExportClass::Team_Units_Formation_Toggle_On +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +// extern void Toggle_Formation(void); // Code\RedAlert\Conquer.cpp +extern char FormationEvent; // Code\RedAlert\Conquer.cpp +void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // + // MBL 03.23.2020: Code here copied and modified from Toggle_Formation(), since obj->IsSelected is not supported + // Replacing with ObjectClass::Is_Selected_By_Player(HouseClass *player); + // + + int team = MAX_TEAMS; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + if (team >= MAX_TEAMS) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + } + + if (team >= MAX_TEAMS) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + } + + if (team >= MAX_TEAMS) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +} + + +/************************************************************************************************** +* CNC_Handle_Debug_Request -- Process a debug input request +* +* In: +* +* +* Out: +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (debug_request_type) { + + case DEBUG_REQUEST_SPAWN_OBJECT: + DLLExportClass::Debug_Spawn_Unit(object_name, x, y, enemy); + break; + + case DEBUG_REQUEST_FORCE_CRASH: + Debug_Force_Crash = true; + break; + + case DEBUG_REQUEST_KILL_OBJECT: + if (strcmp(object_name, "HEAL") == 0) { + DLLExportClass::Debug_Heal_Unit(x, y); + } + else { + DLLExportClass::Debug_Kill_Unit(x, y); + } + break; + + case DEBUG_REQUEST_END_GAME: + { + bool win = true; + + const char lose[] = "LOSE"; + if (strcmp(lose, object_name) == 0) { + win = false; + } + + PlayerWins = win; + PlayerLoses = !win; + } + break; + + case DEBUG_REQUEST_UNSHROUD: + Debug_Unshroud = unshroud; + Map.Flag_To_Redraw(true); + break; + + case DEBUG_REQUEST_SUPERWEAPON_RECHARGE: + for (int i = 0; i < SPC_COUNT; ++i) + { + PlayerPtr->SuperWeapon[i].Forced_Charge(true); + } + break; + + case DEBUG_REQUEST_END_PRODUCTION: + { + for (int index = 0; index < Factories.Count(); index++) { + FactoryClass* factory = Factories.Ptr(index); + if (factory->Get_House()->IsHuman) { + Factories.Ptr(index)->Force_Complete(); + } + } + } + break; + + case DEBUG_REQUEST_ADD_RESOURCES: + { + if (object_name) { + int amount = atoi(object_name); + PlayerPtr->Credits += amount; + if (PlayerPtr->Credits < 0) { + PlayerPtr->Credits = 0; + } + } + } + break; + + case DEBUG_REQUEST_UNLOCK_BUILDABLES: + PlayerPtr->DebugUnlockBuildables = !PlayerPtr->DebugUnlockBuildables; + PlayerPtr->IsRecalcNeeded = true; + break; + + case DEBUG_REQUEST_SET_GLOBAL_FLAG: + Scen.Set_Global_To(x, true); + break; + + default: + break; + } +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: Object to unlimbo , x & y cell positions +* +* +* Out: True if unlimbo succeeded +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +bool DLLExportClass::Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y) +{ + if (techno) { + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_right = map_cell_x + Map.MapCellWidth; + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + map_cell_right = min(map_cell_right, cell_x + 26); // Generally try to prevent the objects from spawing off the right of the screen + + int try_x = cell_x; + int try_y = cell_y; + + while (try_y < map_cell_bottom) { + + CELL cell = XY_Cell(try_x, try_y); + + if (techno->Unlimbo(Cell_Coord(cell))) { + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + + cell_x = try_x; + cell_y = try_y; + return true; + } + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + } + + cell_x = try_x; + cell_y = try_y; + } + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: +* +* Out: +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_All(int x, int y) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + int origin_x = map_cell_x + 2; + int origin_y = map_cell_y + 2; + + if (x != 0 || y != 0) { + CELL screen_cell = Coord_Cell(Map.Pixel_To_Coord(x, y)); + origin_x = Cell_X(screen_cell); + origin_y = Cell_Y(screen_cell); + } + + int try_x = origin_x; + int try_y = origin_y; + + HousesType house = PlayerPtr->Class->House; + + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const & building_type = BuildingTypeClass::As_Reference(sindex); + + if (building_type.Get_Ownable() && building_type.Level != -1) { + + BuildingClass * building = new BuildingClass(building_type, house); + if (building) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(building, try_x, try_y)) { + break; + } + } + } + } + } + + + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const & unit_type = UnitTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (unit_type.Get_Ownable() && unit_type.Level != -1) { + + UnitClass * unit = (UnitClass*) unit_type.Create_One_Of(PlayerPtr); + if (unit) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(unit, try_x, try_y)) { + break; + } + } + } + } + } + + + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + InfantryTypeClass const &infantry_type = InfantryTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (infantry_type.Get_Ownable() && infantry_type.Level != -1) { + + InfantryClass * inf = (InfantryClass*) infantry_type.Create_One_Of(PlayerPtr); + if (inf) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(inf, try_x, try_y)) { + break; + } + } + } + } + } + + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + AircraftTypeClass const &aircraft_type = AircraftTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (aircraft_type.Get_Ownable() && aircraft_type.Level != -1) { + + AircraftClass * air = (AircraftClass*) aircraft_type.Create_One_Of(PlayerPtr); + if (air) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(air, try_x, try_y)) { + break; + } + } + } + } + } + + + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + VesselTypeClass const &vessel_type = VesselTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (vessel_type.Get_Ownable() && vessel_type.Level != -1) { + + VesselClass * boat = (VesselClass*) vessel_type.Create_One_Of(PlayerPtr); + if (boat) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(boat, try_x, try_y)) { + break; + } + } + } + } + } +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_Unit -- Debug spawn a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy) +{ + if (object_name == NULL) { + return; + } + + if (strlen(object_name) == 0) { + return; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + HousesType house = PlayerPtr->Class->House; + + /* + ** Place all? + */ + if (stricmp(object_name, "ALLOBJECTS") == 0) { + Debug_Spawn_All(x, y); + return; + } + + /* + ** If this is for the enemy, find the enemy with the most stuff + */ + + if (enemy) { + unsigned max_count = 0; + for (int i = 0; i < Houses.Count(); ++i) { + const HouseClass* player = Houses.Ptr(i); + const unsigned count = player->CurUnits + player->CurBuildings + player->CurInfantry + player->CurVessels + player->CurAircraft; + if (!PlayerPtr->Is_Ally(player) && (count >= max_count)) { + house = player->Class->House; + max_count = count; + } + } + } + + /* + ** What is this thing? + */ + + StructType structure_type = BuildingTypeClass::From_Name(object_name); + if (structure_type != STRUCT_NONE) { + + BuildingClass * building = new BuildingClass(structure_type, house); + if (building) { + if (!building->Unlimbo(Cell_Coord(cell))) { + delete building; + } + } + +#if (0) + Map.PendingObject = &BuildingTypeClass::As_Reference(structure_type); + Map.PendingHouse = PlayerPtr->ActLike; + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(PlayerPtr); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + + //OutList.Add(EventClass(EventClass::PLACE, RTTI_BUILDING, (CELL)(cell + Map.ZoneOffset))); + } +#endif + return; + } + + + UnitType unit_type = UnitTypeClass::From_Name(object_name); + if (unit_type != UNIT_NONE) { + + UnitClass * unit = new UnitClass(unit_type, house); + if (unit) { + unit->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + + return; + } + + + InfantryType infantry_type = InfantryTypeClass::From_Name(object_name); + if (infantry_type != INFANTRY_NONE) { + + InfantryClass * inf = new InfantryClass(infantry_type, house); + if (inf) { + inf->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + AircraftType aircraft_type = AircraftTypeClass::From_Name(object_name); + if (aircraft_type != AIRCRAFT_NONE) { + + AircraftClass * air = new AircraftClass(aircraft_type, house); + if (air) { + air->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + VesselType vessel_type = VesselTypeClass::From_Name(object_name); + if (vessel_type != VESSEL_NONE) { + + VesselClass *boat = new VesselClass(vessel_type, house); + if (boat != NULL) { + + if (boat->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N)) { + boat->Enter_Idle_Mode(); + } else { + delete boat; + } + } + } + + OverlayType overlay_type = OverlayTypeClass::From_Name(object_name); + if (overlay_type != OVERLAY_NONE) + { + new OverlayClass(overlay_type, cell); + + return; + } + +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Kill_Unit -- Kill a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 8/19/2019 3:09PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Kill_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + static const int debug_damage = 100; // 100 = Incremental damage + if (obj) { + int damage = debug_damage; + obj->Take_Damage(damage, 0, WARHEAD_HE, 0, true); + } else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + cellptr->Reduce_Tiberium(1); + } + if (optr->IsWall) { + Map[cell].Reduce_Wall(debug_damage); + } + } + if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B) { + Map.Destroy_Bridge_At(cell); + } + } + } +} + +void DLLExportClass::Debug_Heal_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + if (obj) { + obj->Strength = obj->Class_Of().MaxStrength; + } + else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + const int cellcount = (int)FACING_COUNT + 1; + CellClass* cells[cellcount]; + cells[0] = cellptr; + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + cells[(int)index + 1] = &cellptr->Adjacent_Cell(index); + } + + for (int index = 0; index < cellcount; index++) { + CellClass * newcell = cells[index]; + + if (newcell != NULL) { + if (newcell->Can_Tiberium_Germinate()) { + switch (cellptr->Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); + newcell->OverlayData = 0; + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + new OverlayClass(Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4), newcell->Cell_Number()); + newcell->OverlayData = 0; + break; + + default: + break; + } + } + else if (newcell->Land_Type() == LAND_TIBERIUM) { + switch (newcell->Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + newcell->OverlayData = MIN(newcell->OverlayData + 1, 11); + newcell->Recalc_Attributes(); + newcell->Redraw_Objects(); + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + newcell->OverlayData = MIN(newcell->OverlayData + 1, 2); + newcell->Recalc_Attributes(); + newcell->Redraw_Objects(); + break; + + default: + break; + } + } + } + } + } + } + } + } +} + + + + + +/************************************************************************************************** +* DLLExportClass::Legacy_Render_Enabled -- Is the legacy rendering enabled? +* +* In: +* +* Out: +* +* +* +* History: 4/15/2019 5:46PM - ST +**************************************************************************************************/ +bool DLLExportClass::Legacy_Render_Enabled(void) +{ + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + unsigned int num_humans = 0U; + for (int i = 0; i < MULTIPLAYER_COUNT; ++i) { + HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_ptr && player_ptr->IsHuman) { + if (++num_humans > 1) break; + } + } + return num_humans < 2; + } + + //return false; + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Computer_Message -- Replacement for original Computer_Message function +* +* In: +* +* Out: +* +* +* +* History: 1/27/2020 1:42PM - ST +**************************************************************************************************/ +void DLLExportClass::Computer_Message(bool last_player_taunt) +{ + HousesType house; + HouseClass *ptr; + + HouseClass *ai_players[MAX_PLAYERS]; + int ai_player_count = 0; + + /*------------------------------------------------------------------------ + Find the computer house that the message will be from + ------------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MULTIPLAYER_COUNT); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + ai_players[ai_player_count++] = ptr; + } + + if (ai_player_count) { + int ai_player_index = 0; + if (ai_player_count > 1) { + ai_player_index = IRandom(0, ai_player_count - 1); + } + + int taunt_index; + if (last_player_taunt) { + taunt_index = 13; + } else { + taunt_index = IRandom(0,12); + } + + On_Message(ai_players[ai_player_index], "", 15.0f, MESSAGE_TYPE_COMPUTER_TAUNT, taunt_index); + } +} + + +/************************************************************************************************** +* DLLExportClass::Set_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Set_Special_Key_Flags(unsigned char special_key_flags) +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = special_key_flags; +} + +/************************************************************************************************** +* DLLExportClass::Clear_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Clear_Special_Key_Flags() +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = 0; +} + +/************************************************************************************************** +* DLLExportClass::Get_Input_Key_State -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLLExportClass::Get_Input_Key_State(KeyNumType key) +{ + switch (key) + { + case KN_LCTRL: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_CTRL) != 0; + break; + case KN_LSHIFT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_SHIFT) != 0; + break; + case KN_LALT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_ALT) != 0; + break; + default: + break; + }; + + return false; + +} + +/************************************************************************************************** +* Get_Input_Key_State +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLL_Export_Get_Input_Key_State(KeyNumType key) +{ + return DLLExportClass::Get_Input_Key_State(key); +} + + + + +bool DLLSave(Pipe &file) +{ + return DLLExportClass::Save(file); +} + +bool DLLLoad(Straw &file) +{ + return DLLExportClass::Load(file); +} + + +/************************************************************************************************** +* DLLExportClass::Save -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +bool DLLExportClass::Save(Pipe & pipe) +{ + /* + ** Version first + */ + unsigned int version = CNC_DLL_API_VERSION; + pipe.Put(&version, sizeof(version)); + + pipe.Put(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)); + + pipe.Put(GlyphxPlayerIDs, sizeof(GlyphxPlayerIDs)); + + pipe.Put(&GlyphXClientSidebarWidthInLeptons, sizeof(GlyphXClientSidebarWidthInLeptons)); + + pipe.Put(MPlayerIsHuman, sizeof(MPlayerIsHuman)); + + pipe.Put(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)); + + pipe.Put(PlacementType, sizeof(PlacementType)); + + pipe.Put(&OverrideNewUnitsEnabled, sizeof(OverrideNewUnitsEnabled)); + + for (int i=0 ; iType; + } + } +} + + + +/************************************************************************************************** +* DLLExportClass::Decode_Pointers -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +void DLLExportClass::Decode_Pointers(void) +{ + for (int i=0 ; i(PlacementType[i]); + PlacementType[i] = NULL; + if (type >= STRUCT_FIRST && type < STRUCT_COUNT) { + + TechnoTypeClass const * tech = Fetch_Techno_Type(RTTI_BUILDINGTYPE, type); + if (tech) { + BuildingTypeClass* build_type = (BuildingTypeClass*)(tech); + if (build_type) { + PlacementType[i] = build_type; + } + } + } + } + } +} + + +void DLL_Code_Pointers(void) +{ + DLLExportClass::Code_Pointers(); +} + +void DLL_Decode_Pointers(void) +{ + DLLExportClass::Decode_Pointers(); +} \ No newline at end of file diff --git a/REDALERT/DLLInterface.h b/REDALERT/DLLInterface.h new file mode 100644 index 000000000..04cbd9ffd --- /dev/null +++ b/REDALERT/DLLInterface.h @@ -0,0 +1,924 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#pragma once + +#ifndef DLL_INTERFACE_H +#define DLL_INTERFACE_H + +struct CarryoverObjectStruct; + + + +/* +** DLL Interface version +** +** +** +*/ +#define CNC_DLL_API_VERSION 0x100 + + + + +#define MAX_EXPORT_CELLS (128 * 128) + +#ifdef TIBERIAN_DAWN +#define MAP_MAX_CELL_WIDTH 64 +#define MAP_MAX_CELL_HEIGHT 64 +#else +#define MAP_MAX_CELL_WIDTH 128 +#define MAP_MAX_CELL_HEIGHT 128 +#endif + + + + +/* +** Interface structs require stricter packing +** +** +*/ +#pragma pack(push) +#pragma pack(1) + + + + +/************************************************************************************** +** +** Game state request types +** +** +*/ +enum GameStateRequestEnum { + GAME_STATE_NONE, + GAME_STATE_STATIC_MAP, + GAME_STATE_DYNAMIC_MAP, + GAME_STATE_LAYERS, + GAME_STATE_SIDEBAR, + GAME_STATE_PLACEMENT, + GAME_STATE_SHROUD, + GAME_STATE_OCCUPIER, + GAME_STATE_PLAYER_INFO +}; + + + + +/************************************************************************************** +** +** Static map data (tiles) +** +** +*/ +struct CNCStaticCellStruct { + char TemplateTypeName[32]; + int IconNumber; +}; + + + +enum CnCTheaterType { + CNC_THEATER_NONE=-1, + CNC_THEATER_DESERT, + CNC_THEATER_JUNGLE, + CNC_THEATER_TEMPERATE, + CNC_THEATER_WINTER, + + CNC_THEATER_COUNT, + CNC_THEATER_FIRST=0 +}; + + +struct CNCMapDataStruct { + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + int OriginalMapCellX; + int OriginalMapCellY; + int OriginalMapCellWidth; + int OriginalMapCellHeight; + + CnCTheaterType Theater; + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + CNCStaticCellStruct StaticCells[MAX_EXPORT_CELLS]; +}; + + + + +/************************************************************************************** +** +** Object type enum +** +** +*/ +#define DLL_LAYER_COUNT 4 + +enum DllObjectTypeEnum { + UNKNOWN, + INFANTRY, + UNIT, + AIRCRAFT, + BUILDING, + TERRAIN, + ANIM, + BULLET, + OVERLAY, + SMUDGE, + OBJECT, + SPECIAL, + INFANTRY_TYPE, + UNIT_TYPE, + AIRCRAFT_TYPE, + BUILDING_TYPE, + VESSEL, + VESSEL_TYPE +}; + + + + + +/************************************************************************************** +** +** Object action types +** +** +*/ +enum DllActionTypeEnum : unsigned char { + DAT_NONE, + DAT_MOVE, + DAT_NOMOVE, + DAT_ENTER, + DAT_SELF, + DAT_ATTACK, + DAT_ATTACK_OUT_OF_RANGE, + DAT_GUARD, + DAT_SELECT, + DAT_CAPTURE, + DAT_SABOTAGE, + DAT_HEAL, + DAT_DAMAGE, + DAT_TOGGLE_PRIMARY, + DAT_CANT_DEPLOY, + DAT_REPAIR, + DAT_CANT_REPAIR +}; + + + + + +/************************************************************************************** +** +** Object state data +** +** +*/ + +#define MAX_OCCUPY_CELLS 36 +#define MAX_OBJECT_PIPS 18 +#define MAX_OBJECT_LINES 3 +#define MAX_HOUSES 32 + +struct CNCObjectLineStruct { + int X; + int Y; + int X1; + int Y1; + int Frame; + unsigned char Color; +}; + +#define CNC_OBJECT_ASSET_NAME_LENGTH 16 +struct CNCObjectStruct { + void *CNCInternalObjectPointer; + char TypeName[CNC_OBJECT_ASSET_NAME_LENGTH]; + char AssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + DllObjectTypeEnum Type; + int ID; + int BaseObjectID; + DllObjectTypeEnum BaseObjectType; + int PositionX; + int PositionY; + int Width; + int Height; + int Altitude; + int SortOrder; + int Scale; + int DrawFlags; + short MaxStrength; + short Strength; + unsigned short ShapeIndex; + unsigned short CellX; + unsigned short CellY; + unsigned short CenterCoordX; + unsigned short CenterCoordY; + short SimLeptonX; + short SimLeptonY; + unsigned char DimensionX; + unsigned char DimensionY; + unsigned char Rotation; + unsigned char MaxSpeed; + char Owner; + char RemapColor; + char SubObject; + bool IsSelectable; + unsigned int IsSelectedMask; + bool IsRepairing; + bool IsDumping; + bool IsTheaterSpecific; + unsigned int FlashingFlags; + unsigned char Cloak; + bool CanRepair; + bool CanDemolish; + bool CanDemolishUnit; + short OccupyList[MAX_OCCUPY_CELLS]; + int OccupyListLength; + int Pips[MAX_OBJECT_PIPS]; + int NumPips; + int MaxPips; + CNCObjectLineStruct Lines[MAX_OBJECT_LINES]; + int NumLines; + bool RecentlyCreated; + bool IsALoaner; + bool IsFactory; + bool IsPrimaryFactory; + bool IsDeployable; + bool IsAntiGround; + bool IsAntiAircraft; + bool IsSubSurface; + bool IsNominal; + bool IsDog; + bool IsIronCurtain; + bool IsInFormation; + bool CanMove[MAX_HOUSES]; + bool CanFire[MAX_HOUSES]; + bool CanDeploy; + bool CanHarvest; + bool CanPlaceBombs; + bool IsFixedWingedAircraft; + bool IsFake; + unsigned char ControlGroup; + unsigned int VisibleFlags; + unsigned int SpiedByFlags; + char ProductionAssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; + const char* OverrideDisplayName; + DllActionTypeEnum ActionWithSelected[MAX_HOUSES]; + + static const unsigned int VISIBLE_FLAGS_ALL = 0xffffffff; +}; + +struct CNCObjectListStruct { + int Count; + CNCObjectStruct Objects[1]; // Variable length +}; + + + + +/************************************************************************************** +** +** Placement validity data +** +** Used to pass back info about tructure placement validity +*/ +struct CNCPlacementCellInfoStruct { + bool PassesProximityCheck; // If the structure was placed in this cell, does that satisfy the proximity check for the whole structure? + bool GenerallyClear; // Is this cell generally clear of obstructions that would prevent placement? +}; + +struct CNCPlacementInfoStruct { + int Count; + CNCPlacementCellInfoStruct CellInfo[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Sidebar/construction state data +** +** +*/ +enum DllSuperweaponTypeEnum { + SW_NONE, + SW_UNKNOWN, + + //TD values + SW_NUKE, + SW_AIR_STRIKE, + SW_ION_CANNON, + + //RA values + SW_SONAR_PULSE, + SW_CHRONOSPHERE, + SW_PARA_BOMB, + SW_PARA_INFANTRY, + SW_SPY_MISSION, + SW_IRON_CURTAIN, + SW_GPS, + SW_CHRONOSPHERE_DESTINATION +}; + +struct CNCSidebarEntryStruct { + char AssetName[16]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + int BuildableType; // This is the original buildable type that should be passed back if we want to start/cancel construction + int BuildableID; // This is the original buildable id that should be passed back if we want to start/cancel construction + DllObjectTypeEnum Type; // Type converted to shared enum + DllSuperweaponTypeEnum SuperWeaponType; + int Cost; // Cost to construct + int PowerProvided; // Power cost to construct + int BuildTime; // Cost to construct + float Progress; // Construction progress (0.0 - 1.0) + short PlacementList[MAX_OCCUPY_CELLS]; // Which cells this structure occupies for placement (if structure) + int PlacementListLength; // How many cells + bool Completed; // Construction has completed + bool Constructing; // Is it currently constructing + bool ConstructionOnHold; // Is the current construction on hold + bool Busy; // Is the associated factory busy + bool BuildableViaCapture; // Is this buildable due to the capture of a structure of a different faction. This will be false for captured structures of the same faction (ActLike) + bool Fake; // Is this a fake structure? +}; + + +struct CNCSidebarStruct { + int EntryCount[2]; // Counts for the left and right columns + int Credits; // Amount of currency available (excluding Tiberium) + int CreditsCounter; // Visible credits to display in the sidebar (includes count up/down logic) + int Tiberium; // Amount of Tiberium in reserve + int MaxTiberium; // Maximum amount of Tiberium storage available + int PowerProduced; + int PowerDrained; + int MissionTimer; + unsigned int UnitsKilled; // Total count of enemy units killed by this player; Includes Infantry, Vehicles, Aircraft + unsigned int BuildingsKilled; // Total count of enemy structures killed by this player + unsigned int UnitsLost; // Total count player-owned units killed/lost + unsigned int BuildingsLost; // Total count player-owned structures killed/lost + unsigned int TotalHarvestedCredits; // Complete total of gained credits over the match (does not include starting credits) + bool RepairBtnEnabled; + bool SellBtnEnabled; + bool RadarMapActive; + + CNCSidebarEntryStruct Entries[1]; // Variable length column entries +}; + + + +enum SidebarRequestEnum { + SIDEBAR_REQUEST_START_CONSTRUCTION, + SIDEBAR_REQUEST_HOLD_CONSTRUCTION, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, + SIDEBAR_REQUEST_START_PLACEMENT, + SIDEBAR_REQUEST_PLACE, + SIDEBAR_CANCEL_PLACE, + SIDEBAR_CLICK_REPAIR, + SIDEBAR_REQUEST_ENABLE_QUEUE, + SIDEBAR_REQUEST_DISABLE_QUEUE, + SIDEBAR_REQUEST_START_CONSTRUCTION_MULTI, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION_MULTI +}; + +enum SuperWeaponRequestEnum { + SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON +}; +enum ControlGroupRequestEnum { + CONTROL_GROUP_REQUEST_CREATE, + CONTROL_GROUP_REQUEST_TOGGLE, + CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION, +}; + + +/************************************************************************************** +** +** Input events sent into the DLL +** +** +*/ +enum InputRequestEnum { + INPUT_REQUEST_NONE, + INPUT_REQUEST_MOUSE_MOVE, + INPUT_REQUEST_MOUSE_LEFT_CLICK, + INPUT_REQUEST_MOUSE_RIGHT_DOWN, + INPUT_REQUEST_MOUSE_RIGHT_CLICK, + INPUT_REQUEST_MOUSE_AREA, + INPUT_REQUEST_MOUSE_AREA_ADDITIVE, + INPUT_REQUEST_SELL_AT_POSITION, + INPUT_REQUEST_SELECT_AT_POSITION, + INPUT_REQUEST_COMMAND_AT_POSITION, + INPUT_REQUEST_SPECIAL_KEYS +}; + + +/************************************************************************************** +** +** Structure Requests Repair, Sell +** +** +*/ +enum StructureRequestEnum { + INPUT_STRUCTURE_NONE, + INPUT_STRUCTURE_REPAIR_START, + INPUT_STRUCTURE_REPAIR, + INPUT_STRUCTURE_SELL_START, + INPUT_STRUCTURE_SELL, + INPUT_STRUCTURE_CANCEL, +}; + + +/************************************************************************************** +** +** Unit Requests Scatter, Select Next, Select Previous, Guard Mode, Stop +** +** +*/ +enum UnitRequestEnum { + INPUT_UNIT_NONE, + INPUT_UNIT_SCATTER, + INPUT_UNIT_SELECT_NEXT, + INPUT_UNIT_SELECT_PREVIOUS, + INPUT_UNIT_GUARD_MODE, + INPUT_UNIT_STOP, + INPUT_UNIT_FORMATION_TOGGLE, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_ON, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_OFF, // RA Only +}; + + +/************************************************************************************** +** +** Game Action Requests +** +** +*/ +enum GameRequestEnum { + INPUT_GAME_MOVIE_DONE, + INPUT_GAME_LOADING_DONE, +}; + + +/************************************************************************************** +** +** Special Keys +** +** +*/ +enum SpecialKeyRequestEnum { + INPUT_SPECIAL_KEY_CTRL = 0b00000001, + INPUT_SPECIAL_KEY_ALT = 0b00000010, + INPUT_SPECIAL_KEY_SHIFT = 0b00000100, +}; + + + +/************************************************************************************** +** +** Non-static map data. +** +** Per-cell smudges and overlays. Smudges are used for things like craters and structure bibs that draw under units. +** Overlays are things like walls and tiberium that can't move from the cell but aren't flat like smudges. +** +** +*/ +struct CNCDynamicMapEntryStruct { + char AssetName[16]; + int PositionX; + int PositionY; + int Width; + int Height; + short Type; + char Owner; + int DrawFlags; + unsigned char CellX; + unsigned char CellY; + unsigned char ShapeIndex; + bool IsSmudge; + bool IsOverlay; + bool IsResource; + bool IsSellable; + bool IsTheaterShape; + bool IsFlag; +}; + +struct CNCDynamicMapStruct { + bool VortexActive; + int VortexX; + int VortexY; + int VortexWidth; + int VortexHeight; + int Count; + CNCDynamicMapEntryStruct Entries[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Event data +** +** Used to call back into the GlyphX engine for one-time events like sound effect triggers +** +** +*/ +enum EventCallbackType { + CALLBACK_EVENT_INVALID = -1, + CALLBACK_EVENT_SOUND_EFFECT = 0, + CALLBACK_EVENT_SPEECH, + CALLBACK_EVENT_GAME_OVER, + CALLBACK_EVENT_DEBUG_PRINT, + CALLBACK_EVENT_MOVIE, + CALLBACK_EVENT_MESSAGE, + CALLBACK_EVENT_UPDATE_MAP_CELL, + CALLBACK_EVENT_ACHIEVEMENT, + CALLBACK_EVENT_STORE_CARRYOVER_OBJECTS, + CALLBACK_EVENT_SPECIAL_WEAPON_TARGETTING, + CALLBACK_EVENT_BRIEFING_SCREEN, + CALLBACK_EVENT_CENTER_CAMERA, + CALLBACK_EVENT_PING +}; + + +struct GameOverMultiPlayerStatsStruct +{ + GameOverMultiPlayerStatsStruct() + : + GlyphXPlayerID( 0 ), + IsHuman( false ), + WasHuman( false ), + IsWinner( false ), + ResourcesGathered( 0 ), + TotalUnitsKilled( 0 ), + TotalStructuresKilled( 0 ), + Efficiency( 0 ), + Score( 0 ) + { + } + __int64 GlyphXPlayerID; + bool IsHuman; + bool WasHuman; + bool IsWinner; + int ResourcesGathered; + int TotalUnitsKilled; + int TotalStructuresKilled; + int Efficiency; // AKA Economy + int Score; +}; + +#define GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED 8 + + +enum EventCallbackMessageEnum { + MESSAGE_TYPE_DIRECT = 0, + MESSAGE_TYPE_PLAYER_DEFEATED, + MESSAGE_TYPE_COMPUTER_TAUNT, + MESSAGE_TYPE_PLAYER_DISCONNECTED +}; + +struct EventCallbackStruct { + + EventCallbackStruct::EventCallbackStruct(void) : EventType(CALLBACK_EVENT_INVALID), GlyphXPlayerID(0) { } + + EventCallbackType EventType; + + __int64 GlyphXPlayerID; + + union { + + struct SoundEffectEvent { + int SFXIndex; + int Variation; + int PixelX; + int PixelY; + int PlayerID; //TO_FIX + char SoundEffectName[ 16 ]; + int SoundEffectPriority; + int SoundEffectContext; + } SoundEffect; + + struct SpeechEvent { + int SpeechIndex; + int PlayerID; //TO_FIX + char SpeechName[ 16 ]; + } Speech; + + struct GameOverEvent { + bool Multiplayer; + // + // Single-player data + // + bool PlayerWins; //This should specify player id + const char* MovieName; + const char* MovieName2; + const char* MovieName3; + const char* MovieName4; + const char* AfterScoreMovieName; + int Score; + int Leadership; + int Efficiency; + int CategoryTotal; + int NODKilled; + int GDIKilled; + int CiviliansKilled; + int NODBuildingsDestroyed; + int GDIBuildingsDestroyed; + int CiviliansBuildingsDestroyed; + int RemainingCredits; + int SabotagedStructureType; + int TimerRemaining; + // + // Multi-player data + // + int MultiPlayerTotalPlayers; + GameOverMultiPlayerStatsStruct MultiPlayerPlayersData[ GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ]; + } GameOver; + + struct DebugPrintEvent { + const char *PrintString; + } DebugPrint; + + struct MovieEvent { + const char* MovieName; + int Theme; + bool Immediate; + } Movie; + + struct MessageEvent { + const char* Message; + float TimeoutSeconds; + EventCallbackMessageEnum MessageType; + __int64 MessageParam1; + } Message; + + struct UpdateMapCellEvent { + int CellX; + int CellY; + char TemplateTypeName[32]; + } UpdateMapCell; + + struct AchievementEvent { + const char* AchievementType; + const char* AchievementReason; + } Achievement; + + struct StoreCarryoverObjectsEvent { + const CarryoverObjectStruct* CarryoverList; + } CarryoverObjects; + + struct SpecialWeaponTargettingEvent { + int Type; + int ID; + char Name[16]; + DllSuperweaponTypeEnum WeaponType; + } SpecialWeaponTargetting; + + struct CenterCameraEvent { + int CoordX; + int CoordY; + } CenterCamera; + + struct PingEvent { + int CoordX; + int CoordY; + } Ping; + }; + +}; + + + + + + + + +/************************************************************************************** +** +** Multiplayer setup data +** +** Used to pass multiplayer setup info into the C&C code from the GlyphX engine +** +** +*/ + + + +struct CNCMultiplayerOptionsStruct { + //int MPlayerPrefColor; // preferred color index for this player + //int MPlayerColorIdx; // actual color index of this player + //CnCHousesType MPlayerHouse; // House of this player (GDI/NOD) + //unsigned char MPlayerLocalID; // ID of this player + int MPlayerCount; // # of human players in this game + int MPlayerBases; // 1 = bases are on for this scenario + int MPlayerCredits; // # credits everyone gets + int MPlayerTiberium; // 1 = tiberium enabled for this scenario + int MPlayerGoodies; // 1 = goodies enabled for this scenario + int MPlayerGhosts; // 1 = houses with no players will still play + int MPlayerSolo; // 1 = allows a single-player net game + int MPlayerUnitCount; // # units for non-base multiplayer scenarios + bool IsMCVDeploy; // MCV undeploys instead of selling + bool SpawnVisceroids; // Do visceroids spawn + bool EnableSuperweapons; // Are superweapons available + bool MPlayerShadowRegrow; + bool MPlayerAftermathUnits; + bool CaptureTheFlag; + bool DestroyStructures; // New early win condition via destroying all a player's structures +}; + + + +struct CNCSpiedInfoStruct { + int Power; + int Drain; + int Money; +}; + + +struct CNCPlayerInfoStruct { + char Name[64]; + unsigned char House; + int ColorIndex; + unsigned __int64 GlyphxPlayerID; + int Team; + int StartLocationIndex; + unsigned char HomeCellX; + unsigned char HomeCellY; + bool IsAI; + unsigned int AllyFlags; + bool IsDefeated; + unsigned int SpiedPowerFlags; + unsigned int SpiedMoneyFlags; + CNCSpiedInfoStruct SpiedInfo[MAX_HOUSES]; + int SelectedID; + DllObjectTypeEnum SelectedType; + DllActionTypeEnum ActionWithSelected[MAX_EXPORT_CELLS]; + unsigned int ActionWithSelectedCount; + unsigned int ScreenShake; + bool IsRadarJammed; +}; + + +// +enum GameRequestType { + GAME_REQUEST_MOVIE_DONE, +}; + + +/************************************************************************************** +** +** Rules configuration data +** +** +*/ +struct CNCDifficultyDataStruct +{ + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + + float RepairDelay; + float BuildDelay; + + bool IsBuildSlowdown; + bool IsWallDestroyer; + bool IsContentScan; +}; + +struct CNCRulesDataStruct +{ + CNCDifficultyDataStruct Difficulties[3]; +}; + + +/************************************************************************************** +** +** Debug input interface +** +** +*/ + +enum DebugRequestEnum { + DEBUG_REQUEST_SPAWN_OBJECT, + DEBUG_REQUEST_END_GAME, + DEBUG_REQUEST_UNSHROUD, + DEBUG_REQUEST_SUPERWEAPON_RECHARGE, + DEBUG_REQUEST_KILL_OBJECT, + DEBUG_REQUEST_END_PRODUCTION, + DEBUG_REQUEST_ADD_RESOURCES, + DEBUG_REQUEST_UNLOCK_BUILDABLES, + DEBUG_REQUEST_FORCE_CRASH, + DEBUG_REQUEST_SET_GLOBAL_FLAG, +}; + + + + + + +/************************************************************************************** +** +** Shroud data. +** +** Per-cell shroud info +** +** +*/ +struct CNCShroudEntryStruct { + char ShadowIndex; + bool IsVisible; + bool IsMapped; + bool IsJamming; +}; + +struct CNCShroudStruct { + int Count; + CNCShroudEntryStruct Entries[1]; // Variable length +}; + + + + + + +/************************************************************************************** +** +** Occupier data. +** +** Per-cell occupier info +** +** +*/ +struct CNCOccupierObjectStruct { + DllObjectTypeEnum Type; + int ID; +}; + +struct CNCOccupierEntryHeaderStruct { + int Count; +}; + +struct CNCOccupierHeaderStruct { + int Count; +}; + + + + +/************************************************************************************** +** +** Carryover object. +** +** Used to store object data that persists between missions +** +** +*/ +struct CarryoverObjectStruct +{ + CarryoverObjectStruct() : Next(0) {} + + CarryoverObjectStruct* Next; + + int RTTI; + int Type; + int Cell; + int Strength; + int House; +}; + + + + +/* +** End of strict structure packing +** +** +*/ +#pragma pack(pop) + + + +#endif //DLL_INTERFACE_H \ No newline at end of file diff --git a/REDALERT/DLLInterfaceEditor.cpp b/REDALERT/DLLInterfaceEditor.cpp new file mode 100644 index 000000000..99bde29a0 --- /dev/null +++ b/REDALERT/DLLInterfaceEditor.cpp @@ -0,0 +1,830 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//#include +#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" +#include "mixfile.h" +#include "ccini.H" + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map(char* cncdata_directory, char* house_name, int scenario_index, char* east_west, char* variant); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name(char* cncdata_directory, char* scenario_name); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points); + +int LoadScenario(); + +extern int DLL_Startup(const char * command_line); + +char EditorMapINIBuffer[SHAPE_BUFFER_SIZE]; +bool EditorMapInitialized = false; + +const static int EDITOR_COMMMAND_SUCCESS = 0; +const static int EDITOR_COMMMAND_FAILURE = 1; + +const char* CD1Path = "\\RED_ALERT\\CD1\\"; +const char* CD2Path = "\\RED_ALERT\\COUNTERSTRIKE\\"; +const char* CD3Path = "\\RED_ALERT\\AFTERMATH\\"; + +char RedAlertINI[_MAX_PATH]; + +/************************************************************************************************** +* CNC_Editor_Startup +* Initializes the system to allow map loading for the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup() +{ + /* + BlackPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + GamePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + OriginalPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + WhitePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + memset(WhitePalette, 63, 768); + + + Set_Palette(GamePalette); + + TheaterData = 0; + TheaterIcons = 0; + LowTheaterData = 0; + */ + + RunningFromEditor = true; + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Cleanup +* Cleans up systems initialized by the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup() +{ + /* + if (BlackPalette) + { + delete[] BlackPalette; + } + + if (GamePalette) + { + delete[] GamePalette; + } + + if (OriginalPalette) + { + delete[] OriginalPalette; + } + if (WhitePalette) + { + delete[] WhitePalette; + } + */ + + return EDITOR_COMMMAND_SUCCESS; +} +/************************************************************************************************** +* CNC_Editor_Load_Mix_Files +* Loads all the Mix files for Tiberian Dawn +**************************************************************************************************/ +void CNC_Editor_Load_Mix_Files() +{ + const char* MixFileNames[] = + { + "MAIN.MIX", + "REDALERT.MIX", + "EXPAND2.MIX", + "EXPAND.MIX", + "HIRES1.MIX", + //"LORES1.MIX" + + "GENERAL.MIX", + + "LOCAL.MIX", + "HIRES.MIX", + "NCHIRES.MIX", + "CONQUER.MIX", + + "RUSSIAN.MIX", + "ALLIES.MIX", + + "SNOW.MIX", + "TEMPERAT.MIX", + "INTERIOR.MIX", + }; + + int count = sizeof(MixFileNames) / sizeof(MixFileNames[0]); + + for (int i = count - 1; i >= 0; --i) + { + MFCD::Free(MixFileNames[i]); + } + + for (int i = 0; i < count; ++i) + { + MFCD* file = new MFCD(MixFileNames[i], &FastKey); + file->Cache(); + + } + +} + +/************************************************************************************************** +* CNC_Editor_Setup_Content_Directory +* Sets up where the system should load map data from. +* +* cncdata_directory: path of the base CNC data directory +* CD1: if true, consider this disc 1, otherwise consider this disc 2. +**************************************************************************************************/ +void CNC_Editor_Setup_Content_Directory(char* cncdata_directory, int CD, char (&content_directory)[_MAX_PATH]) +{ + + switch (CD) + { + default: + case 1: + sprintf(content_directory, "%s%s", cncdata_directory, CD1Path); + break; + case 2: + sprintf(content_directory, "%s%s", cncdata_directory, CD2Path); + break; + case 3: + sprintf(content_directory, "%s%s", cncdata_directory, CD3Path); + break; + } + + //Setup red alert path + sprintf(RedAlertINI, "%sREDALERT.INI",content_directory); + + if (strlen(content_directory) != 0) { + CCFileClass::Clear_Search_Drives(); + CCFileClass::Reset_Raw_Path(); + char *dll_dir = strdup(content_directory); + CCFileClass::Set_Search_Drives(dll_dir); + free(dll_dir); + } +} + +/************************************************************************************************** +* CNC_Editor_Load_Map +* Loads the map with the given parameters. +* +* cncdata_directory: path of the base CNC data directory +* faction: the name of the faction we are loading the map for +* scenario_index: int scenario index +* east_west: +* variant: +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map( + char* cncdata_directory, + char* house_name, + int scenario_index, + char* east_west, + char* variant) +{ + + CNC_Editor_Clear_Map(); + + ScenarioPlayerType scen_player; + int CD = 1; + if (stricmp(house_name, "SPAIN") == 0) { + scen_player = SCEN_PLAYER_SPAIN; + Whom = HOUSE_GOOD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "GREECE") == 0 || stricmp(house_name, "ALLY") == 0) { + scen_player = SCEN_PLAYER_GREECE; + Whom = HOUSE_GOOD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "USSR") == 0) { + scen_player = SCEN_PLAYER_USSR; + Whom = HOUSE_BAD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "MULTI") == 0) + { + scen_player = SCEN_PLAYER_MPLAYER; + Whom = HOUSE_MULTI1; + + if (scenario_index >= 25) + { + CD = 3; + } + + } + + if (stricmp(house_name, "JAPAN") == 0) + { + scen_player = SCEN_PLAYER_JP; + Whom = HOUSE_JP; + } + + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + + DLL_Startup(command_line); + + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir; + BuildLevel = 7; + + if (stricmp(east_west, "w") == 0) + { + scen_dir = SCEN_DIR_WEST; + } + else + { + scen_dir = SCEN_DIR_EAST; + } + + ScenarioVarType variant_enum; + + if (stricmp(variant, "b") == 0) + { + variant_enum = SCEN_VAR_B; + } + else if (stricmp(variant, "c") == 0) + { + variant_enum = SCEN_VAR_C; + } + else if (stricmp(variant, "d") == 0) + { + variant_enum = SCEN_VAR_D; + } + else + { + variant_enum = SCEN_VAR_A; + } + + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)variant_enum); + + return LoadScenario(); +} + +/************************************************************************************************** +* CNC_Editor_Load_Map_By_Scenario_Name +* Loads the map with the given parameters. +* +* cncdata_directory: path of the base CNC data directory +* scenario_name: name of the scnario to load +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name( + char* cncdata_directory, + char* scenario_name) +{ + int CD = 3; // Always use the aftermath cd + if (strnlen(scenario_name, _MAX_PATH) >= 3 && (scenario_name[2] == 'a' || scenario_name[2] == 'A')) + { + // Ant missions are CD 2 + CD = 2; + } + + CNC_Editor_Clear_Map(); + + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + + DLL_Startup(command_line); + + snprintf(Scen.ScenarioName, _MAX_FNAME + _MAX_EXT, "%s.ini", scenario_name); + + return LoadScenario(); +} + +int LoadScenario() +{ + CCFileClass file(Scen.ScenarioName); + + if (!file.Is_Available()) + { + return(EDITOR_COMMMAND_FAILURE); + } + + + CCINIClass ini; + int result = ini.Load(file,true); + if (result == 0) + { + return(EDITOR_COMMMAND_FAILURE); + } + + if (result == 2) + { + // if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { + /* + ** Make a special exception so that multiplayer maps from 1 through + ** 24 will not care if the message digest is in error. All other + ** maps will abort the scenario load. + */ + if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) + { + return(EDITOR_COMMMAND_FAILURE); + } + } + + const char * const BASIC = "Basic"; + NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); + + Map.One_Time(); + Map.Read_INI(ini); + + EditorMapInitialized = true; + + return EDITOR_COMMMAND_SUCCESS; +} + +/************************************************************************************************** +* CNC_Editor_Clear_Map +* Deletes the data for the currently loaded map. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map() +{ + if (EditorMapInitialized) + { + Map.Init_Clear(); + EditorMapInitialized = false; + + return EDITOR_COMMMAND_SUCCESS; + } + else + { + return EDITOR_COMMMAND_FAILURE; + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Map_Stats +* Gets the stats for the currently loaded map +* +* map_width: out parameter storing the width of the map +* map_height: out parameter storing the height of the map +* theater: out paramter storing the theater of the map +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater) +{ + if (EditorMapInitialized) + { + map_width = Map.MapCellWidth + 1; + map_height = Map.MapCellHeight + 1; + theater = Scen.Theater; + return EDITOR_COMMMAND_SUCCESS; + } + + map_width = -1; + map_height = -1; + theater = -1; + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data_By_Index +* Get the data from the given cell. +* +* cell_index: The index of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + CELL index = (CELL)cell_index; + + CellClass * cellptr = &Map[index]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + + char template_name[10]; + if (cellptr->Get_Template_Info(template_name, icon, image_data)) + { + snprintf(cell_name, cell_name_size, "%s-%04d", template_name, icon); + + template_type = cellptr->TType; + template_icon_index = icon; + + //TemplateTypeClass::As_Reference(ptr->TType). + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data +* Get the data from the given cell. +* +* x,y: The corrdinates of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + if (!EditorMapInitialized) + { + return EDITOR_COMMMAND_FAILURE; + } + + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + + return CNC_Editor_Get_Cell_Data_By_Index((int)cell, cell_name, cell_name_size, template_type, template_icon_index); +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Texture_Buffer +* +* x,y: +* out_width, out_height: dimensions of the outputed texture array +* out_texture_array: output array of unsigned chars storing the color data for the requested object, +* every 3 chars is a set of RGB values +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array) +{ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + char cell_name[_MAX_PATH]; + + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) + { + GraphicBufferClass temp_gbuffer(24, 24); + GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, 24, 24); + + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = 24; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = 24; + + temp_viewport.Draw_Stamp(image_data, icon, 0, 0, NULL, WINDOW_CUSTOM); + + out_width = temp_viewport.Get_Width(); + out_height = temp_viewport.Get_Height(); + + const int COLOR_SIZE = 3; + + SAFEARRAYBOUND Bound; + Bound.lLbound = 0; + Bound.cElements = out_width * out_height * COLOR_SIZE; + + out_texture_array = SafeArrayCreate(VT_UI1, 1, &Bound); + + unsigned char* out_buffer; + + HRESULT hr = SafeArrayAccessData(out_texture_array, (void **)&out_buffer); + if (SUCCEEDED(hr)) + { + GraphicBufferClass* Graphic_Buffer = temp_viewport.Get_Graphic_Buffer(); + + int VP_Scan_Line = temp_viewport.Get_Width() + temp_viewport.Get_XAdd(); + + char * start_ptr; + start_ptr = (char *)Graphic_Buffer->Get_Buffer(); + start_ptr += ((temp_viewport.Get_YPos() * VP_Scan_Line) + temp_viewport.Get_XPos()); + + for (int y = 0; y < out_height; ++y) + { + unsigned char* scanline_ptr = (unsigned char*)start_ptr + y * VP_Scan_Line; + unsigned char* out_buffer_y_ptr = out_buffer + (y * out_width * COLOR_SIZE); + for (int x = 0; x < out_width; ++x) + { + unsigned char* pallete_index_ptr = scanline_ptr + x; + unsigned char* out_buffer_ptr = out_buffer_y_ptr + (x * COLOR_SIZE); + + int palette_index = (*pallete_index_ptr); + out_buffer_ptr[0] = ((unsigned char)GamePalette[palette_index].Red_Component()); + out_buffer_ptr[1] = ((unsigned char)GamePalette[palette_index].Green_Component()); + out_buffer_ptr[2] = ((unsigned char)GamePalette[palette_index].Blue_Component()); + } + } + + SafeArrayUnaccessData(out_texture_array); + + return EDITOR_COMMMAND_SUCCESS; + } + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Template_Data +* Get the data from the given tile template type. +* +* template_type_index: The index of the template type to use. should come from the Get_Cell_Data function. +* template_positions: Out buffer to be filled with the list of positions of the tiles as offsets from the origin of the template. +* This data is store is an X, Y, X, Y, X, Y format. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points) +{ + if (template_type_index >= TEMPLATE_COUNT || template_type_index == TEMPLATE_NONE) + { + return EDITOR_COMMMAND_FAILURE; + } + + const TemplateTypeClass& template_type = TemplateTypeClass::As_Reference((TemplateType)template_type_index); + if (template_type.Get_Image_Data() == nullptr) + { + return EDITOR_COMMMAND_FAILURE; + } + + + short const * occupy_list = template_type.Occupy_List(); + + short const * counter = occupy_list; + while (counter && *counter != REFRESH_EOL) + { + counter++; + } + + int occupy_list_size = counter - occupy_list; + + + SAFEARRAYBOUND bounds; + bounds.lLbound = 0; + bounds.cElements = occupy_list_size * 2; + template_points = SafeArrayCreate(VT_I4, 1, &bounds); + + int *pData; + HRESULT hr = SafeArrayAccessData(template_points, (void **)&pData); + if (SUCCEEDED(hr)) + { + for (int i = 0; i < occupy_list_size; i++) + { + CELL cell = occupy_list[i]; + + int x = Cell_X(cell); + int y = Cell_Y(cell); + + pData[i * 2] = x; + pData[i * 2 + 1] = y; + } + + SafeArrayUnaccessData(template_points); + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory) +{ + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory,false, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + DLL_Startup(command_line); + + char team_ids[] = + { + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + }; + /* + { + 'g', + 'a', + 'u', + 'm' + }; + */ + const int team_count = sizeof(team_ids) / sizeof(char); + + char direction_ids[] = + { + 'e', + 'w', + }; + const int direction_count = sizeof(direction_ids) / sizeof(char); + + char variant_ids[] = + { + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + }; + /* + { + 'a', + 'b', + 'c', + 'd' + }; + */ + const int variant_count = sizeof(variant_ids) / sizeof(char); + + const int min_scenario_index = 1; + const int max_scenario_index = 99; + + char scenario_name[_MAX_FNAME + _MAX_EXT]; + char file_name[_MAX_FNAME + _MAX_EXT]; + + FILE * names_file = fopen("d:\\RA_Disk2.txt", "w+"); + + for (int team_index = 0; team_index < team_count; team_index++) + { + for (int scenario_index = min_scenario_index; scenario_index <= max_scenario_index; ++scenario_index) + { + for (int direction_index = 0; direction_index < direction_count; direction_index++) + { + for (int variant_index = 0; variant_index < variant_count; variant_index++) + { + sprintf(scenario_name, "sc%c%.2d%c%c", + team_ids[team_index], + scenario_index, + direction_ids[direction_index], + variant_ids[variant_index]); + + sprintf(file_name, "%s.INI", scenario_name); + CCFileClass file(file_name); + if (file.Is_Available()) + { + fprintf(names_file, "%s\n", scenario_name); + } + } + } + } + } + + fclose(names_file); + + return EDITOR_COMMMAND_SUCCESS; +} \ No newline at end of file diff --git a/REDALERT/DOOR.CPP b/REDALERT/DOOR.CPP new file mode 100644 index 000000000..00f771637 --- /dev/null +++ b/REDALERT/DOOR.CPP @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DOOR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 14, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DoorClass::AI -- Handles the door processing logic. * + * DoorClass::Close_Door -- Try to close the unit's door. * + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * DoorClass::Open_Door -- Opens the door for this unit. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * * + * This constructor sets the door to an initial closed state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +DoorClass::DoorClass(void) +{ + State = IS_CLOSED; + IsToRedraw = false; + Stages = 0; +} + + +/*********************************************************************************************** + * DoorClass::AI -- Handles the door processing logic. * + * * + * This routine should be called every game frame. It handles the door closing and opening * + * logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/13/1995 JLB : Created. * + *=============================================================================================*/ +void DoorClass::AI(void) +{ + if (Control.Graphic_Logic()) { + if (Control.Fetch_Stage() >= Stages) { + Control.Set_Rate(0); + switch (State) { + case IS_OPENING: + State = IS_OPEN; + break; + + case IS_CLOSING: + State = IS_CLOSED; + break; + } + } + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * DoorClass::Open_Door -- Opens the door for this unit. * + * * + * This routine will perform the door open operation for this unit. It will control vehicle * + * rotation if necessary. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Was action initiated to open the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Open_Door(int rate, int stages) +{ + switch (State) { + case IS_CLOSED: + case IS_CLOSING: + State = IS_OPENING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Close_Door -- Try to close the unit's door. * + * * + * This routine will attempt to close the unit's door. If the door is already closed or * + * in the process of closing, then no action is performed. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Action was initiated to close the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Close_Door(int rate, int stages) +{ + switch (State) { + case IS_OPEN: + case IS_OPENING: + State = IS_CLOSING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * * + * Use this routine to fetch the current door animation frame number. Frame zero is the * + * closed frame and frame 'N' is the open frame. If the door is in the process of opening * + * or closing, the appropriate frame number is used. 'N' is defined as the number of * + * stages in the animation minus 1 (e.g., a four frame animation will return a door stage * + * number between 0 and 3, inclusive). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the door animation frame number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int DoorClass::Door_Stage(void) const +{ + switch (State) { + case IS_CLOSING: + return((Stages-1) - Control.Fetch_Stage()); + + case IS_CLOSED: + return(0); + + case IS_OPENING: + return(Control.Fetch_Stage()); + + case IS_OPEN: + return(Stages-1); + } + return(0); +} diff --git a/REDALERT/DOOR.H b/REDALERT/DOOR.H new file mode 100644 index 000000000..39dd110bc --- /dev/null +++ b/REDALERT/DOOR.H @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DOOR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DOOR_H +#define DOOR_H + +class DoorClass +{ + private: + + /* + ** This is the animation control handler. + */ + StageClass Control; + + /* + ** This is the recorded number of stages of the current + ** door animation process. + */ + unsigned char Stages; + + /* + ** This is the door state. + */ + enum { + IS_CLOSED, // Door is closed. + IS_OPENING, // Door is in the process of opening. + IS_OPEN, // Door is fully open. + IS_CLOSING // Door is in the process of closing. + } State; + + /* + ** If the animation for this door indicates that the object it is + ** attached to should be redrawn, then this flag will be true. + */ + unsigned IsToRedraw:1; + + public: + DoorClass(void); + DoorClass(NoInitClass const & x) : Control(x) {}; + + bool Time_To_Redraw(void) {return(IsToRedraw);}; + void Clear_Redraw_Flag(void) {IsToRedraw = false;}; + void AI(void); + int Door_Stage(void) const; + bool Is_Door_Opening(void) const {return(State == IS_OPENING);}; + bool Is_Door_Closing(void) const {return(State == IS_CLOSING);}; + bool Open_Door(int rate, int stages); + bool Close_Door(int rate, int stages); + bool Is_Door_Open(void) const {return(State == IS_OPEN);}; + bool Is_Door_Closed(void) const {return(State == IS_CLOSED);}; + bool Is_Ready_To_Open(void) const; +}; + +#endif diff --git a/REDALERT/DPMI.CPP b/REDALERT/DPMI.CPP new file mode 100644 index 000000000..281f4a098 --- /dev/null +++ b/REDALERT/DPMI.CPP @@ -0,0 +1,167 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.cpv 4.41 04 Jul 1996 16:12:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) +#ifdef __FLAT__ +#pragma inline +#endif + +//#include "function.h" +#include "dpmi.h" + +#ifndef __FLAT__ + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + if (!size) return; + + unsigned short ssel = src.Selector; + unsigned short dsel = dest.Selector; + + asm { + push es + push ds + + mov si,soffset + mov di,doffset + mov cx,size + mov ax,ssel + mov dx,dsel + mov ds,ax + mov es,dx + } +again: + asm { + mov al,ds:[si] + mov ah,es:[di] + mov ds:[si],ah + mov es:[di],al + inc di + inc si + dec cx + jnz again + + pop ds + pop es + } +} +#endif + + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_swap(char *src, char *dest, int size); + + #pragma aux dss_swap = \ + "again: mov al,[esi]" \ + "mov ah,[edi]" \ + "mov [esi],ah" \ + "stosb" \ + "inc esi" \ + "loop again" \ + parm [esi] [edi] [ecx] \ + modify [ax]; + + if (!size) return; + dss_swap((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} + +#ifdef OBSOLETE +void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_copy(char *src, char *dest, int size); + #pragma aux dss_copy = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copskip1" \ + "rep movsd" \ +"copskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copskip2" \ + "rep movsb" \ +"copskip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + extern void dss_copy_to(void *src, (void *)dest, int size); + + #pragma aux dss_copy_to = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz cop2skip1" \ + "rep movsd" \ +"cop2skip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz cop2skip2" \ + "rep movsb" \ +"cop2skip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_to(src, (void *)(Selector + dest), size); + +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + extern void dss_copy_from(void *dest, (void *)source, int size); + + #pragma aux dss_copy_from = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copfskip1" \ + "rep movsd" \ +"copfskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copfskip2" \ + "rep movsb" \ +"copfskip2:" \ + parm [edi esi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_from(dest, (void *)(Selector + source), size); +} +#endif +#endif \ No newline at end of file diff --git a/REDALERT/DPMI.H b/REDALERT/DPMI.H new file mode 100644 index 000000000..2dab6c964 --- /dev/null +++ b/REDALERT/DPMI.H @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.h_v 4.43 05 Jul 1996 17:58:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef DPMI_Hx +#define DPMI_Hx +#include +#include +#include +//#include + + +extern void output(short port, short data); + + +class DOSSegmentClass { + /* + ** This is the selector/segment value. In real mode it is the segment, in protected + ** mode it is the selector (also 16 bits). This value is moved into DS or ES when + ** accessing memory. + ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h) + */ + unsigned int Selector; + + /* + ** These are C equivalents for pushing and popping the DS segment register. By using + ** these, it is possible to create very small code that uses a segment and + ** offset without damaging the DS register. These are especially useful in protected + ** mode, but they are legal in real mode as well. + */ + void Push_DS(void) {/*__emit__(0x1E);*/}; + void Pop_DS(void) {/*__emit__(0x1F);*/}; + + public: + DOSSegmentClass(void); + ~DOSSegmentClass(void); + DOSSegmentClass(unsigned short segment, long size=(1024L*64L)); + + unsigned int Get_Selector(void); + + /* + ** This routine is used to assign where the descriptor actually points to in + ** low DOS memory. In real mode, this is a simple segment assignment and the size + ** is always 64K regardless of what is specified. In protected mode, the segment + ** is used to update the selector and the size can be any length. + ** In Watcom flat mode, it sets Selector == segment<<4 + */ + void Assign(unsigned short segment, long size=(1024L*64L)); + + /* + ** These routines will move the data to/from regular memory and the segment/descriptor + ** memory. + */ + void Copy_To(void *source, int dest, int size); + void Copy_From(void *dest, int source, int size); + void Copy_Word_To(short data, int dest); + void Copy_Byte_To(char data, int dest); + void Copy_DWord_To(long data, int dest); + short Copy_Word_From(int source); + char Copy_Byte_From(int source); + long Copy_DWord_From(int source); + + /* + ** These routines move data around between sections of segmented (descriptor) memory. + ** Typically, this is used when accessing DOS memory in protected mode or when dealing + ** with hard memory areas such as the screen. + */ + static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); + static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); +}; + + +inline DOSSegmentClass::DOSSegmentClass(void) +{ + Selector = 0xB0000; +} + +inline DOSSegmentClass::~DOSSegmentClass(void) +{ +} + +inline void DOSSegmentClass::Copy_Word_To(short data, int dest) +{ + *(short *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_Byte_To(char data, int dest) +{ + *(char *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_DWord_To(long data, int dest) +{ + *(long *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Assign(unsigned short segment, long) +{ + Selector = (long)(segment)<<4L; +} + +inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long) +{ + Assign(segment); +} + +inline void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + memmove((void*)(Selector+dest), source, (unsigned)size); +} + +inline void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + memmove(dest, (void*)(Selector+source), (unsigned)size); +} + +inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) { + memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), (unsigned)size); +} + +inline short DOSSegmentClass::Copy_Word_From(int source) +{ + return *(short*)(Selector+source); +} + +inline char DOSSegmentClass::Copy_Byte_From(int source) +{ + return *(char*)(Selector+source); +} + +inline long DOSSegmentClass::Copy_DWord_From(int source) +{ + return *(long*)(Selector+source); +} + +inline unsigned int DOSSegmentClass::Get_Selector(void) +{ + return Selector; +} +#endif + + diff --git a/REDALERT/DRIVE.CPP b/REDALERT/DRIVE.CPP new file mode 100644 index 000000000..47698c124 --- /dev/null +++ b/REDALERT/DRIVE.CPP @@ -0,0 +1,2338 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DRIVE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DriveClass::AI -- Processes unit movement and rotation. * + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * DriveClass::DriveClass -- Constructor for drive class object. * + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * DriveClass::Teleport_To -- Teleport object to specified location. * + * DriveClass::While_Moving -- Processes unit movement. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef NEVER +void test(void) +{ + enum nums {one, two, three}; + + nums x; + nums *ptr; + + ptr = &x; +} +#endif + + +/*********************************************************************************************** + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * * + * This is the voice to play when the unit is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Select(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * * + * This plays the audio feedback when ordering this unit to move to a new destination. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Move(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM, + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * * + * This plays the audio feedback when ordering this unit to attack. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Attack(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * * + * This routine is called when the unit discovers that it should get out of the "hot seat" * + * and move to an adjacent cell. Since the safety of the adjacent cell is not determined * + * before the move begins, it will appear that the unit is just scattering (which it * + * should). * + * * + * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move * + * roughly away from the threat. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + * 09/27/1995 JLB : Revised to never scatter if already moving. * + * 07/09/1996 JLB : Moved to DriveClass so that ships will scatter too. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void DriveClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (MissionControl[Mission].IsParalyzed) return; + + if ((What_Am_I() != RTTI_UNIT || !((UnitClass *)this)->IsDumping) && (!Target_Legal(NavCom) || (nokidding && !IsRotating))) { + if (!Target_Legal(TarCom) || forced || Random_Pick(1, 4) == 1) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat != 0) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Destination(::As_Target(newcell)); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * * + * This routine removes the occupation bits for the vehicle and also handles cleaning up * + * any vehicle reservation bits. After this, it then proceeds with limboing the unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Limbo(void) +{ + if (!IsInLimbo) { + Stop_Driver(); + TrackNumber = -1; + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * * + * This routine will remove the "reservation" flag (if present) when the vehicle is * + * required to stop movement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Stop_Driver(void) +{ + assert(IsActive); + + /* + ** We only need to do something if the vehicle is actually going + ** somewhere. + */ + if (Head_To_Coord()) { + + /* + ** Safe off whether the vehicle is down or not so we know whether + ** we have to put it back down. + */ + int temp = IsDown; + + /* + ** If the vehicle is down, pick it up so it doesn't interfere with + ** our flags. + */ + if (temp) { + Mark(MARK_UP); + } + + /* + ** Call the drive class function which will let us release the + ** reserved track. + */ + Mark_Track(Head_To_Coord(), MARK_UP); + + /* + ** If it was down it should be down when we are done. + */ + if (temp) { + Mark(MARK_DOWN); + } + } + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * * + * This routine will set the vehicle to rotate to the direction specified. For tracked * + * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series * + * of short drives (three point turn) to face the desired direction. * + * * + * INPUT: dir -- The direction that this vehicle should face. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Do_Turn(DirType dir) +{ + assert(IsActive); + + if (dir != PrimaryFacing) { + +#ifdef TOFIX + /* + ** Special rotation track is needed for units that + ** cannot rotate in place. + */ + if (Special.IsThreePoint && TrackNumber == -1 && Techno_Type_Class()->Speed == SPEED_WHEEL) { + int facediff; // Signed difference between current and desired facing. + FacingType face; // Current facing (ordinal value). + + facediff = PrimaryFacing.Difference(dir) >> 5; + facediff = Bound(facediff, -2, 2); + if (facediff) { + face = Dir_Facing(PrimaryFacing); + + IsOnShortTrack = true; + Force_Track(face*FACING_COUNT + (face + facediff), Coord); + + Path[0] = FACING_NONE; + Set_Speed(0xFF); // Full speed. + } + } else { + PrimaryFacing.Set_Desired(dir); + } +#else + PrimaryFacing.Set_Desired(dir); +// IsRotating = true; +#endif + } +} + + +/*********************************************************************************************** + * DriveClass::Teleport_To -- Teleport object to specified location. * + * * + * This will teleport the object to the specified location or as close as possible to it * + * if the destination is blocked. * + * * + * INPUT: cell -- The desired destination cell to teleport to. * + * * + * OUTPUT: bool; Was the teleport successful? * + * * + * WARNINGS: All current activity of this object will be terminated by the teleport. It will * + * arrive at the destination in static guard mode. * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + * 10/31/1996 JLB : Handles flag teleport case. * + *=============================================================================================*/ +bool DriveClass::Teleport_To(CELL cell) +{ + /* + ** All cargo gets destroyed. + */ + if (Rule.IsChronoKill) { + Kill_Cargo(NULL); + } + + Stop_Driver(); + Force_Track(-1, 0); + PrimaryFacing.Set_Current(PrimaryFacing.Desired()); + Transmit_Message(RADIO_OVER_OUT); + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_NONE); + Commence(); + Mark(MARK_UP); + + /* + ** A teleported unit will drop the flag right where it's at. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(((UnitClass *)this)->Flagged)->Flag_Attach(Coord_Cell(Coord)); + } + + if (Can_Enter_Cell(cell) != MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } + Coord = Cell_Coord(cell); + Mark(MARK_DOWN); + Look(false); + Per_Cell_Process(PCP_END); + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * * + * This override (nuclear bomb) style routine is to be used when a unit needs to start * + * on a movement track but is outside the normal movement system. This occurs when a * + * harvester starts driving off of a refinery. * + * * + * INPUT: track -- The track number to start on. * + * * + * coord -- The coordinate that the unit will end up at when the movement track * + * is completed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Force_Track(int track, COORDINATE coord) +{ + assert(IsActive); + + TrackNumber = track; + TrackIndex = 0; + if (coord != 0) { + Start_Driver(coord); + } +} + + +/*********************************************************************************************** + * DriveClass::DriveClass -- Constructor for drive class object. * + * * + * This will initialize the drive class to its default state. It is called as a result * + * of creating a unit. * + * * + * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1994 JLB : Created. * + *=============================================================================================*/ +DriveClass::DriveClass(RTTIType rtti, int id, HousesType house) : + FootClass(rtti, id, house), + IsMoebius(false), + IsHarvesting(false), + IsTurretLockedDown(false), + IsOnShortTrack(false), + SpeedAccum(0), + MoebiusCountDown(0), + MoebiusCell(0), + TrackNumber(-1), + TrackIndex(0) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * * + * This debug utility function will display the status of the drive class to the mono * + * screen. It is through this information that bugs can be tracked down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(66, 14, 12, 1, IsMoebius ? MonoClass::INVERSE : MonoClass::NORMAL); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * * + * This routine calculates the new coordinate value needed for the * + * smooth turn logic. The adjustment and flag values must be * + * determined prior to entering this routine. * + * * + * INPUT: adj -- The adjustment coordinate as lifted from the * + * correct smooth turn table. * + * * + * dir -- Pointer to dir for possible modification * + * according to the flag bits. * + * * + * OUTPUT: Returns with the coordinate the unit should positioned to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1994 JLB : Created. * + * 07/13/1994 JLB : Converted to member function. * + *=============================================================================================*/ +COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType & dir) +{ + assert(IsActive); + + DirType workdir = dir; + int x,y; + int temp; + TrackControlType flags = TrackControl[TrackNumber].Flag; + + x = Coord_X(adj); + y = Coord_Y(adj); + + if (flags & F_T) { + temp = x; + x = y; + y = temp; + workdir = (DirType)(DIR_W - workdir); + } + + if (flags & F_X) { + x = -x; + workdir = (DirType)-workdir; + } + + if (flags & F_Y) { + y = -y; + workdir = (DirType)(DIR_S - workdir); + } + + dir = workdir; + + return(XY_Coord( (LEPTON)(Coord_X(Head_To_Coord()) + x), (LEPTON)(Coord_Y(Head_To_Coord()) + y))); +} + + +/*********************************************************************************************** + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * * + * This routine is used to set the unit's navigation computer to the * + * specified target. Once the navigation computer is set, the unit * + * will start planning and moving toward the destination. * + * * + * INPUT: target -- The destination target for the unit to head to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** For harvesting type vehicles, it might go into a dock and unload procedure + ** when the harvester is full and an empty refinery is selected as a target. + */ + BuildingClass * b = As_Building(target); + + /* + ** If the player clicked on refinery but it is not busy, then assign + ** it to unload at the refinery. + */ + if (b != NULL && *b == STRUCT_REFINERY && What_Am_I() == RTTI_UNIT && ((UnitTypeClass *)Techno_Type_Class())->IsToHarvest) { + if (Contact_With_Whom() != b && !b->In_Radio_Contact()) { + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + if (Mission != MISSION_ENTER && Mission != MISSION_HARVEST) { + Assign_Mission(MISSION_ENTER); + target = TARGET_NONE; + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } + + /* + ** Set the unit's navigation computer. + */ + FootClass::Assign_Destination(target); + + Path[0] = FACING_NONE; // Force recalculation of path. + if (!IsDriving && Mission != MISSION_UNLOAD) { + Start_Of_Move(); + } +} + + +/*********************************************************************************************** + * DriveClass::While_Moving -- Processes unit movement. * + * * + * This routine is used to process movement for the units as they move. * + * It is called many times for each cell's worth of movement. This * + * routine only applies after the next cell HeadTo has been determined. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DriveClass::While_Moving(void) +{ + assert(IsActive); + + /* + ** Perform quick legality checks. + */ + if (!IsDriving || TrackNumber == -1 || (IsRotating && !Techno_Type_Class()->IsTurretEquipped)) { + SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold. + return(false); + } + + /* + ** If enough movement has accumulated so that the unit can + ** visibly move on the map, then process accordingly. + ** Slow the unit down if he's carrying a flag. + */ + MPHType maxspeed = MPHType(min(Techno_Type_Class()->MaxSpeed * SpeedBias * House->GroundspeedBias, (int)MPH_LIGHT_SPEED)); + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + int actual; // Working movement addition value. + if (((UnitClass *)this)->Flagged != HOUSE_NONE) { + actual = SpeedAccum + ((int)maxspeed/2) * fixed(Speed, 256); + } else { + actual = SpeedAccum + maxspeed * fixed(Speed, 256); + } + + if (actual > PIXEL_LEPTON_W) { + TurnTrackType const * track; // Track control pointer. + TrackType const * ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + /* + ** Skip ahead the number of track steps required (limited only + ** by track length). Set the unit to the new position and + ** flag the unit accordingly. + */ + Mark(MARK_UP); + while (actual > PIXEL_LEPTON_W) { + COORDINATE offset; + DirType dir; + + actual -= PIXEL_LEPTON_W; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + Coord = Smooth_Turn(offset, dir); + + PrimaryFacing.Set(dir); + + /* + ** See if "per cell" processing is necessary. + */ + if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) { + Mark(MARK_DOWN); + Per_Cell_Process(PCP_DURING); + if (!IsActive) { + return(false); + } + Mark(MARK_UP); + } + + /* + ** The unit could "jump tracks". Check to see if the unit should + ** do so. + */ + if (/**this != UNIT_GUNBOAT &&*/ nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) { + TurnTrackType const * newtrack; // Proposed jump-to track. + int tnum; + + tnum = (int)(Dir_Facing(track->Facing) * FACING_COUNT) + (int)nextface; + newtrack = &TrackControl[tnum]; + if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) { + COORDINATE c = Head_To_Coord(); + int oldspeed = Speed; + + c = Adjacent_Cell(c, nextface); + + switch (Can_Enter_Cell(Coord_Cell(c), nextface)) { + case MOVE_OK: + IsOnShortTrack = false; // Shouldn't be necessary, but... + TrackNumber = tnum; + track = newtrack; + + tracknum = track->Track; + TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment. + ptr = RawTracks[tracknum-1].Track; + adj = false; + + Stop_Driver(); + IsDriving = true; + Per_Cell_Process(PCP_END); + IsDriving = false; + if (!IsActive) return(false); + if (Start_Driver(c)) { + Set_Speed(oldspeed); + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } else { + Path[0] = FACING_NONE; + TrackNumber = -1; + actual = 0; + } + break; + + case MOVE_CLOAK: + Map[c].Shimmer(); + break; + + case MOVE_TEMP: +#ifdef TOFIX + if (*this == UNIT_HARVESTER || !House->IsHuman) { +#else + if (!House->IsHuman) { +#endif + Map[c].Incoming(0, true, true); + } + break; + } + } + } + TrackIndex++; + + } else { + actual = 0; + Coord = Head_To_Coord(); + Stop_Driver(); + TrackNumber = -1; + TrackIndex = NULL; + + /* + ** Perform "per cell" activities. + */ + Mark(MARK_DOWN); + Per_Cell_Process(PCP_END); + if (!IsActive) return(false); + Mark(MARK_UP); + + break; + } + } + if (IsActive) { + Mark(MARK_DOWN); + } + } + + /* + ** Replace any remainder back into the unit's movement + ** accumulator to be processed next pass. + */ + SpeedAccum = actual; + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * * + * This routine is called when a unit has mostly or completely * + * entered a cell. The unit might be in the middle of a movement track * + * when this routine is called. It's primary purpose is to perform * + * sighting and other "per cell" activities. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1993 JLB : Created. * + * 03/30/1994 JLB : Revamped for track system. * + * 04/15/1994 JLB : Converted to member function. * + * 06/18/1994 JLB : Converted to virtual function. * + * 06/18/1994 JLB : Distinguishes between center and near-center conditions. * + *=============================================================================================*/ +void DriveClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** Check to see if it has reached its destination. If so, then clear the NavCom + ** regardless of the remaining path list. + */ + if (As_Cell(NavCom) == cell) { + IsTurretLockedDown = false; + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + + Lay_Track(); + } + + FootClass::Per_Cell_Process(why); +} + + +/*********************************************************************************************** + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * * + * This will try to start a unit advancing toward the cell it is * + * facing. It will check for and handle legality and reserving of the * + * necessary cell. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again because * + * initial start operation is temporarily delayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. * + * 03/16/1994 JLB : Revamped for track logic. * + * 04/15/1994 JLB : Converted to member function. * + * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. * + * 07/13/1995 JLB : Handles bumping into cloaked objects. * + * 09/22/1995 JLB : Breaks out of hopeless hunt mode. * + * 07/10/1996 JLB : Sets scan limit if necessary. * + *=============================================================================================*/ +bool DriveClass::Start_Of_Move(void) +{ + assert(IsActive); + + FacingType facing; // Direction movement will commence. + DirType dir; // Desired actual facing toward destination. + int facediff; // Difference between current and desired facing. + int speed; // Speed of unit. + CELL destcell; // Cell of destination. + LandType ground; // Ground unit is entering. + COORDINATE dest; // Destination coordinate. + + facing = Path[0]; + + if (!Target_Legal(NavCom) && facing == FACING_NONE) { + IsTurretLockedDown = false; + Stop_Driver(); + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + return(false); // Why is it calling this routine!?! + } + + /* + ** Reduce the path length if the target is a unit and the + ** range to the unit is less than the precalculated path steps. + */ + if (facing != FACING_NONE) { + int dist; + + if (Is_Target_Vessel(NavCom) || Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) { + dist = Lepton_To_Cell((LEPTON)Distance(NavCom)); + + if (dist < ARRAY_SIZE(Path)) { + Path[dist] = FACING_NONE; + facing = Path[0]; // Maybe needed. + } + } + } + + /* + ** If the path is invalid at this point, then generate one. If + ** generating a new path fails, then abort NavCom. + */ + if (facing == FACING_NONE) { + + /* + ** If after a path search, there is still no valid path, then set the + ** NavCom to null and let the script take care of assigning a new + ** navigation target. + */ + if (PathDelay != 0) { + return(false); + } + + if (!Basic_Path()) { + + /* + ** If the unit is close enough to the target then just stop + ** driving now. This prevents the fidgeting that would occur + ** if they mindlessly kept trying to get to the exact location + ** desired. This is quite necessary since it is typical to move + ** several units with the same mouse click. + */ + if (!Is_On_Priority_Mission() && Distance(NavCom) < Rule.CloseEnoughDistance && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + } else { + /* + ** If a basic path could not be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current()); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + if (TryTryAgain > 0) { + TryTryAgain--; + } else { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + + /* + ** Since the path was blocked, check to make sure that it was completely + ** blocked. If so and it has a valid TarCom and it is out of range of the + ** TarCom, then give this unit a range limit so that it might not pick + ** a "can't reach" target again. + */ + if (!Target_Legal(NavCom) && Target_Legal(TarCom) && !In_Range(TarCom)) { + IsScanLimited = true; + if (Team.Is_Valid()) Team->Scan_Limit(); + Assign_Target(TARGET_NONE); + } + + /* + ** Stop the movement, for now, and let the subsequent logic in later game + ** frames resume movement as appropriate. + */ + Stop_Driver(); + TrackNumber = -1; + IsTurretLockedDown = false; + return(false); + } + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + TryTryAgain = PATH_RETRY; + facing = Path[0]; + } + + /* + ** Determine the coordinate of the next cell to move into. + */ + dest = Adjacent_Cell(Coord, facing); + dir = Facing_Dir(facing); + + /* + ** Set the facing correctly if it isn't already correct. This + ** means starting a rotation track if necessary. + */ + facediff = PrimaryFacing.Difference(dir); + if (facediff) { + + /* + ** Request a change of facing. + */ + Do_Turn(dir); + return(true); + + } else { + + /* NOTE: Beyond this point, actual track assignment can begin. + ** + ** If the cell to move into is impassable (probably for some unexpected + ** reason), then abort the path list and set the speed to zero. The + ** next time this routine is called, a new path will be generated. + */ + destcell = Coord_Cell(dest); + Mark(MARK_UP); + MoveType cando = Can_Enter_Cell(destcell, facing); + Mark(MARK_DOWN); + + if (cando != MOVE_OK) { + + if (Mission == MISSION_MOVE /*KO&& House->IsHuman */&& Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false);//BG + } + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Stop_Driver(); + if (cando != MOVE_MOVING_BLOCK) { + Path[0] = FACING_NONE; // Path is blocked! + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (cando == MOVE_DESTROYABLE) { + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + } + IsNewNavCom = false; + TrackNumber = -1; + return(true); + } + + /* + ** Determine the speed that the unit can travel to the desired square. + */ + ground = Map[destcell].Land_Type(); + speed = Ground[ground].Cost[Techno_Type_Class()->Speed] * 255; + + /* change speed if it's related to a team move */ + if (IsFormationMove) speed = Ground[ground].Cost[FormationSpeed] * 255; + if (!speed) speed = 128; + +#ifdef NEVER + /* + ** Set the jiggle flag if the terrain would cause the unit + ** to jiggle when travelled over. + */ + BaseF &= ~BASEF_JIGGLE; + if (Ground[ground].Jiggle) { + BaseF |= BASEF_JIGGLE; + } +#endif + + /* + ** A damaged unit has a reduced speed. + */ + if (Health_Ratio() <= Rule.ConditionYellow /*(Techno_Type_Class()->MaxStrength>>1) > Strength*/) { + speed -= (speed/4); // Three quarters speed. + } + if ((speed != Speed)/* || !SpeedAdd*/) { + Set_Speed(speed); // Full speed. + } + + /* + ** Reserve the destination cell so that it won't become + ** occupied AS this unit is moving into it. + */ + if (cando != MOVE_OK) { + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + } else { + + Overrun_Square(Coord_Cell(dest), true); + + /* + ** Determine which track to use (based on recorded path). + */ + FacingType nextface = Path[1]; + if (nextface == FACING_NONE) nextface = facing; + + IsOnShortTrack = false; + TrackNumber = facing * FACING_COUNT + (int)nextface; + if (TrackControl[TrackNumber].Track == 0) { + Path[0] = FACING_NONE; + TrackNumber = -1; + return(true); + } else { + if (TrackControl[TrackNumber].Flag & F_D) { + /* + ** If the middle cell of a two cell track contains a crate, + ** the check for goodies before movement starts. + */ + if (!Map[destcell].Goodie_Check(this)) { + cando = MOVE_NO; + if (!IsActive) return(false); + } else { + if (!IsActive) return(false); + dest = Adjacent_Cell(dest, nextface); + destcell = Coord_Cell(dest); + cando = Can_Enter_Cell(destcell); + } + if (!IsActive) return(false); + + if (cando != MOVE_OK) { + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + if (cando == MOVE_DESTROYABLE) { + + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + IsNewNavCom = false; + TrackIndex = 0; + return(true); + } + } else { + memcpy((char*)&Path[0], (char*)&Path[2], CONQUER_PATH_MAX-2); + Path[CONQUER_PATH_MAX-2] = FACING_NONE; + IsPlanningToLook = true; + } + } else { + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + } + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } + } + + IsNewNavCom = false; + TrackIndex = 0; + if (!Start_Driver(dest)) { + TrackNumber = -1; + Path[0] = FACING_NONE; + Set_Speed(0); + } + } + return(false); +} + + +/*********************************************************************************************** + * DriveClass::AI -- Processes unit movement and rotation. * + * * + * This routine is used to process unit movement and rotation. It * + * functions autonomously from the script system. Thus, once a unit * + * is give rotation command or movement path, it will follow this * + * until specifically instructed to stop. The advantage of this * + * method is that it allows smooth movement of units, faster game * + * execution, and reduced script complexity (since actual movement * + * dynamics need not be controlled directly by the scripts). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine relies on the process control bits for the * + * specified unit (for speed reasons). Thus, only setting * + * movement, rotation, or path list will the unit perform * + * any physics. * + * * + * HISTORY: * + * 09/26/1993 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::AI(void) +{ + assert(IsActive); + + FootClass::AI(); + if (!IsActive || Height > 0) return; + + /* + ** Is this a unit that's been teleported using the chronosphere, and if so, + ** has his timer expired such that he needs to teleport back? + */ + if (IsMoebius) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() != RTTI_UNIT || ((UnitClass *)this)->Class->Type != UNIT_CHRONOTANK) { +#endif + if (MoebiusCountDown == 0) { + IsMoebius = false; + Teleport_To(MoebiusCell); + MoebiusCell = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + + /* + ** If the unit is following a track, then continue + ** to do so -- mindlessly. + */ + if (TrackNumber != -1) { + + /* + ** Perform the movement accumulation. + */ + While_Moving(); + if (!IsActive) return; + if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE) && (What_Am_I() != RTTI_UNIT || !((UnitClass*)this)->IsDumping)) { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + + } else { + + /* + ** For tracked units that are rotating in place, perform the rotation now. + */ +#ifdef TOFIX + if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } +#else + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Techno_Type_Class()->ROT * House->GroundspeedBias)) { + Mark(MARK_CHANGE_REDRAW); + } +#endif + if (!IsRotating) { + Per_Cell_Process(PCP_ROTATION); + if (!IsActive) return; + } + + } else { + + /* + ** The unit has no track to follow, but if there + ** is a navigation target or a remaining path, + ** then start on a new track. + */ + if ((Mission != MISSION_GUARD || Target_Legal(NavCom)) && Mission != MISSION_UNLOAD) { + if (Target_Legal(NavCom) || Path[0] != FACING_NONE) { + + /* + ** Double check to make sure that the movement destination is + ** in a zone that this unit can travel to. If not, then abort + ** the navigation target. Exception is to allow units to leave + ** impassable cells regardless of zone checks. + */ + LandType land = LAND_NONE; + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + land = Map[Center_Coord()].Land_Type(); + } + if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom)) && + land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) { + Stop_Driver(); + Assign_Destination(TARGET_NONE); + } else { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + } else { + Stop_Driver(); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * * + * This routine modifies the path of the specified unit so that it * + * will not start out with a rotation. This is necessary for those * + * vehicles that have difficulty with rotating in place. Typically, * + * this includes wheeled vehicles. * + * * + * INPUT: unit -- Pointer to the unit to adjust. * + * * + * path -- Pointer to path structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only units that require a fixup get modified. The * + * modification only occurs, if there is a legal path to * + * do so. * + * * + * HISTORY: * + * 04/03/1994 JLB : Created. * + * 04/06/1994 JLB : Uses path structure. * + * 04/10/1994 JLB : Diagonal smooth turn added. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Fixup_Path(PathType * path) +{ + assert(IsActive); + + FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements. + int facediff; // The facing difference value (0..4 | 0..-4). + static FacingType _path[4][6] = { + {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + static FacingType _dpath[4][6] = { + {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}, + {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + + int index; + int counter; // Path addition + FacingType * ptr; // Path list pointer. + FacingType * ptr2; // Copy of new path list pointer. + FacingType nextpath; // Next path value. + CELL cell; // Working cell value. + bool ok; + + /* + ** Verify that the unit is valid and there is a path problem to resolve. + */ + if (!path || path->Command[0] == FACING_NONE) { + return; + } + + /* + ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns. + */ +#ifdef TOFIX + if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) { +#else + if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) { +// if (What_Am_I() == RTTI_UNIT) { +#endif + return; + } + + /* + ** If the original path starts in the same direction as the unit, then + ** there is no problem to resolve -- abort. + */ + facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5; + + if (!facediff) return; + + if (Dir_Facing(PrimaryFacing) & FACING_NE) { + ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } else { + ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } + ptr2 = ptr; + + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Adjacent_Cell(cell, nextpath); + //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + + /* + ** If veering to the left was not successful, then try veering + ** to the right. This only makes sense if the vehicle is trying + ** to turn 180 degrees. + */ + if (!ok && ABS(facediff) == 4) { + ptr = ptr2; // Pointer to path adjust list. + facediff = -facediff; + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + } + + /* + ** If a legal path addition was created, then install it in place + ** of the first path value. The initial path entry is to be replaced + ** with a sequence of path entries that create smooth turning. + */ + if (ok) { + if (path->Length <= 1) { + memmove((char *)&stage[0], (char*)path->Command, max(counter, 1)); + path->Length = counter; + } else { + + /* + ** Optimize the transition path step from the smooth turn + ** first part as it joins with the rest of the normal + ** path. The normal prefix path steps are NOT to be optimized. + */ + if (counter) { + counter--; + path->Command[0] = stage[counter]; + Optimize_Moves(path, MOVE_OK); + } + + /* + ** If there is more than one prefix path element, then + ** insert the rest now. + */ + if (counter) { + memmove((char*)&path->Command[0], (char*)&path->Command[counter], 40-counter); + memmove((char*)&stage[0], (char*)&path->Command[0], counter); + path->Length += counter; + } + } + path->Command[path->Length] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * * + * This routine handles the track laying for the unit. This entails examining the unit's * + * current location as well as the direction and whether this unit is allowed to lay * + * tracks in the first place. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Lay_Track(void) +{ + assert(IsActive); + +#ifdef NEVER + static IconCommandType * _trackdirs[8] = { + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE, + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE + }; + + if (!(ClassF & CLASSF_TRACKS)) return; + + Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]); +#endif +} + + +/*********************************************************************************************** + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * * + * This routine will ensure that the midpoint (if any) of the track that the unit is * + * following, will be marked according to the mark type specified. * + * * + * INPUT: headto -- The head to coordinate. * + * * + * type -- The type of marking to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Mark_Track(COORDINATE headto, MarkType type) +{ + assert(IsActive); + + int value; + + if (type == MARK_UP) { + value = false; + } else { + value = true; + } + + if (headto) { + if (!IsOnShortTrack && TrackNumber != -1) { + + /* + ** If we have not passed the per cell process point we need + ** to deal with it. + */ + int tracknum = TrackControl[TrackNumber].Track; + if (tracknum) { + TrackType const * ptr = RawTracks[tracknum - 1].Track; + int cellidx = RawTracks[tracknum - 1].Cell; + if (cellidx > -1) { + DirType dir = ptr[cellidx].Facing; + + if (TrackIndex < cellidx && cellidx != -1) { + COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, dir); + Map[offset].Flag.Occupy.Vehicle = value; + } + } + } + } + Map[headto].Flag.Occupy.Vehicle = value; + } +} + + +/*********************************************************************************************** + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * * + * This routine is used to verify that this object is allowed to move. Some objects can * + * be temporarily occupied and thus cannot move until the situation permits. * + * * + * INPUT: direction -- The direction that movement would be desired. * + * * + * OUTPUT: Can the unit move in the direction specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Ok_To_Move(DirType ) const +{ + assert(IsActive); + + return true; +} + + +/*************************************************************************** +** Smooth turn track tables. These are coordinate offsets from the center +** of the destination cell. These are the raw tracks that are modified +** by negating the X and Y portions as necessary. Also for reverse travelling +** direction, the track list can be processed backward. +** +** Track 1 = N +** Track 2 = NE +** Track 3 = N->NE 45 deg (double path consumption) +** Track 4 = N->E 90 deg (double path consumption) +** Track 5 = NE->SE 90 deg (double path consumption) +** Track 6 = NE->N 45 deg (double path consumption) +** Track 7 = N->NE (facing change only) +** Track 8 = NE->E (facing change only) +** Track 9 = N->E (facing change only) +** Track 10= NE->SE (facing change only) +** Track 11= back up into refinery +** Track 12= drive out of refinery +*/ +//#pragma warn -ias +DriveClass::TrackType const DriveClass::Track1[24] = { + {0x00F50000L,(DirType)0}, + {0x00EA0000L,(DirType)0}, + {0x00DF0000L,(DirType)0}, + {0x00D40000L,(DirType)0}, + {0x00C90000L,(DirType)0}, + {0x00BE0000L,(DirType)0}, + {0x00B30000L,(DirType)0}, + {0x00A80000L,(DirType)0}, + {0x009D0000L,(DirType)0}, + {0x00920000L,(DirType)0}, + {0x00870000L,(DirType)0}, + {0x007C0000L,(DirType)0}, // Track jump check here. + {0x00710000L,(DirType)0}, + {0x00660000L,(DirType)0}, + {0x005B0000L,(DirType)0}, + {0x00500000L,(DirType)0}, + {0x00450000L,(DirType)0}, + {0x003A0000L,(DirType)0}, + {0x002F0000L,(DirType)0}, + {0x00240000L,(DirType)0}, + {0x00190000L,(DirType)0}, + {0x000E0000L,(DirType)0}, + {0x00030000L,(DirType)0}, + {0x00000000L,(DirType)0} +}; + +DriveClass::TrackType const DriveClass::Track2[] = { + {0x00F8FF08L,(DirType)32}, + {0x00F0FF10L,(DirType)32}, + {0x00E8FF18L,(DirType)32}, + {0x00E0FF20L,(DirType)32}, + {0x00D8FF28L,(DirType)32}, + {0x00D0FF30L,(DirType)32}, + {0x00C8FF38L,(DirType)32}, + {0x00C0FF40L,(DirType)32}, + {0x00B8FF48L,(DirType)32}, + {0x00B0FF50L,(DirType)32}, + {0x00A8FF58L,(DirType)32}, + {0x00A0FF60L,(DirType)32}, + {0x0098FF68L,(DirType)32}, + {0x0090FF70L,(DirType)32}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track3[] = { + {0x01F5FF00L,(DirType)0}, + {0x01EAFF00L,(DirType)0}, + {0x01DFFF00L,(DirType)0}, + {0x01D4FF00L,(DirType)0}, + {0x01C9FF00L,(DirType)0}, + {0x01BEFF00L,(DirType)0}, + {0x01B3FF00L,(DirType)0}, + {0x01A8FF00L,(DirType)0}, + {0x019DFF00L,(DirType)0}, + {0x0192FF00L,(DirType)0}, + {0x0187FF00L,(DirType)0}, + {0x0180FF00L,(DirType)0}, + {0x0175FF00L,(DirType)0}, // Jump entry point here. + {0x016BFF00L,(DirType)0}, + {0x0160FF02L,(DirType)1}, + {0x0155FF04L,(DirType)3}, + {0x014CFF06L,(DirType)4}, + {0x0141FF08L,(DirType)5}, + {0x0137FF0BL,(DirType)7}, + {0x012EFF0FL,(DirType)8}, + {0x0124FF13L,(DirType)9}, + {0x011AFF17L,(DirType)11}, + {0x0110FF1BL,(DirType)12}, + {0x0107FF1FL,(DirType)13}, // Center cell processing here. + {0x00FCFF24L,(DirType)15}, + {0x00F3FF28L,(DirType)16}, + {0x00ECFF2CL,(DirType)17}, + {0x00E0FF32L,(DirType)19}, + {0x00D7FF36L,(DirType)20}, + {0x00CFFF3DL,(DirType)21}, + {0x00C6FF42L,(DirType)23}, + {0x00BAFF49L,(DirType)24}, + {0x00B0FF4DL,(DirType)25}, + {0x00A8FF58L,(DirType)27}, + {0x00A0FF60L,(DirType)28}, + {0x0098FF68L,(DirType)29}, + {0x0090FF70L,(DirType)31}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track4[] = { + {0x00F5FF00L,(DirType)0}, + {0x00EBFF00L,(DirType)0}, + {0x00E0FF00L,(DirType)0}, + {0x00D5FF00L,(DirType)0}, + {0x00CBFF01L,(DirType)0}, + {0x00C0FF03L,(DirType)0}, + {0x00B5FF05L,(DirType)1}, + {0x00ABFF07L,(DirType)1}, + {0x00A0FF0AL,(DirType)2}, + {0x0095FF0DL,(DirType)3}, + {0x008BFF10L,(DirType)4}, + {0x0080FF14L,(DirType)5}, // Track entry here. + {0x0075FF18L,(DirType)8}, + {0x006DFF1CL,(DirType)12}, + {0x0063FF22L,(DirType)16}, + {0x005AFF25L,(DirType)20}, + {0x0052FF2BL,(DirType)23}, + {0x0048FF32L,(DirType)27}, + {0x0040FF37L,(DirType)32}, + {0x0038FF3DL,(DirType)36}, + {0x0030FF46L,(DirType)39}, + {0x002BFF4FL,(DirType)43}, + {0x0024FF58L,(DirType)47}, + {0x0020FF60L,(DirType)51}, + {0x001BFF6DL,(DirType)54}, + {0x0017FF79L,(DirType)57}, + {0x0014FF82L,(DirType)60}, // Track jump here. + {0x0011FF8FL,(DirType)62}, + {0x000DFF98L,(DirType)63}, + {0x0009FFA2L,(DirType)64}, + {0x0006FFACL,(DirType)64}, + {0x0004FFB5L,(DirType)66}, + {0x0003FFC0L,(DirType)64}, + {0x0002FFCBL,(DirType)64}, + {0x0001FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track5[] = { + {0xFFF8FE08L,(DirType)32}, + {0xFFF0FE10L,(DirType)32}, + {0xFFE8FE18L,(DirType)32}, + {0xFFE0FE20L,(DirType)32}, + {0xFFD8FE28L,(DirType)32}, + {0xFFD0FE30L,(DirType)32}, + {0xFFC8FE38L,(DirType)32}, + {0xFFC0FE40L,(DirType)32}, + {0xFFB8FE48L,(DirType)32}, + {0xFFB0FE50L,(DirType)32}, + {0xFFA8FE58L,(DirType)32}, + {0xFFA0FE60L,(DirType)32}, + {0xFF98FE68L,(DirType)32}, + {0xFF90FE70L,(DirType)32}, + {0xFF88FE78L,(DirType)32}, + {0xFF80FE80L,(DirType)32}, // Track entry here. + {0xFF78FE88L,(DirType)32}, + {0xFF71FE90L,(DirType)32}, + {0xFF6AFE97L,(DirType)32}, + {0xFF62FE9FL,(DirType)32}, + {0xFF5AFEA8L,(DirType)32}, + {0xFF53FEB0L,(DirType)35}, + {0xFF4BFEB7L,(DirType)38}, + {0xFF44FEBEL,(DirType)41}, + {0xFF3EFEC4L,(DirType)44}, + {0xFF39FECEL,(DirType)47}, + {0xFF34FED8L,(DirType)50}, + {0xFF30FEE0L,(DirType)53}, + {0xFF2DFEEBL,(DirType)56}, + {0xFF2CFEF5L,(DirType)59}, + {0xFF2BFF00L,(DirType)62}, + {0xFF2CFF0BL,(DirType)66}, + {0xFF2DFF15L,(DirType)69}, + {0xFF30FF1FL,(DirType)72}, + {0xFF34FF28L,(DirType)75}, + {0xFF39FF30L,(DirType)78}, + {0xFF3EFF3AL,(DirType)81}, + {0xFF44FF44L,(DirType)84}, + {0xFF4BFF4BL,(DirType)87}, + {0xFF53FF50L,(DirType)90}, + {0xFF5AFF58L,(DirType)93}, + {0xFF62FF60L,(DirType)96}, + {0xFF6AFF68L,(DirType)96}, + {0xFF71FF70L,(DirType)96}, + {0xFF78FF78L,(DirType)96}, + {0xFF80FF80L,(DirType)96}, // Track jump check here. + {0xFF88FF88L,(DirType)96}, + {0xFF90FF90L,(DirType)96}, + {0xFF98FF98L,(DirType)96}, + {0xFFA0FFA0L,(DirType)96}, + {0xFFA8FFA8L,(DirType)96}, + {0xFFB0FFB0L,(DirType)96}, + {0xFFB8FFB8L,(DirType)96}, + {0xFFC0FFC0L,(DirType)96}, + {0xFFC8FFC8L,(DirType)96}, + {0xFFD0FFD0L,(DirType)96}, + {0xFFD8FFD8L,(DirType)96}, + {0xFFE0FFE0L,(DirType)96}, + {0xFFE8FFE8L,(DirType)96}, + {0xFFF0FFF0L,(DirType)96}, + {0xFFF8FFF8L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track6[] = { + {0x0100FE00L,(DirType)32}, + {0x00F8FE08L,(DirType)32}, + {0x00F0FE10L,(DirType)32}, + {0x00E8FE18L,(DirType)32}, + {0x00E0FE20L,(DirType)32}, + {0x00D8FE28L,(DirType)32}, + {0x00D0FE30L,(DirType)32}, + {0x00C8FE38L,(DirType)32}, + {0x00C0FE40L,(DirType)32}, + {0x00B8FE48L,(DirType)32}, + {0x00B0FE50L,(DirType)32}, + {0x00A8FE58L,(DirType)32}, + {0x00A0FE60L,(DirType)32}, + {0x0098FE68L,(DirType)32}, + {0x0090FE70L,(DirType)32}, + {0x0088FE78L,(DirType)32}, + {0x0080FE80L,(DirType)32}, // Jump entry point here. + {0x0078FE88L,(DirType)32}, + {0x0070FE90L,(DirType)32}, + {0x0068FE98L,(DirType)32}, + {0x0060FEA0L,(DirType)32}, + {0x0058FEA8L,(DirType)32}, + {0x0055FEAEL,(DirType)32}, + {0x004EFEB8L,(DirType)35}, + {0x0048FEC0L,(DirType)37}, + {0x0042FEC9L,(DirType)40}, + {0x003BFED2L,(DirType)43}, + {0x0037FEDAL,(DirType)45}, + {0x0032FEE3L,(DirType)48}, + {0x002BFEEBL,(DirType)51}, + {0x0026FEF5L,(DirType)53}, + {0x0022FEFEL,(DirType)56}, + {0x001CFF08L,(DirType)59}, + {0x0019FF12L,(DirType)61}, + {0x0015FF1BL,(DirType)64}, + {0x0011FF26L,(DirType)64}, + {0x000EFF30L,(DirType)64}, + {0x000BFF39L,(DirType)64}, + {0x0009FF43L,(DirType)64}, + {0x0007FF4EL,(DirType)64}, + {0x0005FF57L,(DirType)64}, + {0x0003FF62L,(DirType)64}, + {0x0001FF6DL,(DirType)64}, + {0x0000FF77L,(DirType)64}, + {0x0000FF80L,(DirType)64}, // Track jump check here. + {0x0000FF8BL,(DirType)64}, + {0x0000FF95L,(DirType)64}, + {0x0000FFA0L,(DirType)64}, + {0x0000FFABL,(DirType)64}, + {0x0000FFB5L,(DirType)64}, + {0x0000FFC0L,(DirType)64}, + {0x0000FFCBL,(DirType)64}, + {0x0000FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track7[] = { + {0x0006FFFFL,(DirType)0}, + {0x000CFFFEL,(DirType)4}, + {0x0011FFFCL,(DirType)8}, + {0x0018FFFAL,(DirType)12}, + {0x001FFFF6L,(DirType)16}, + {0x0024FFF3L,(DirType)19}, + {0x002BFFF0L,(DirType)22}, + {0x0030FFFDL,(DirType)23}, + {0x0035FFEBL,(DirType)24}, + {0x0038FFE8L,(DirType)25}, + {0x003CFFE6L,(DirType)26}, + {0x0040FFE3L,(DirType)27}, + {0x0043FFE0L,(DirType)28}, + {0x0046FFDDL,(DirType)29}, + {0x0043FFDFL,(DirType)30}, + {0x0040FFE1L,(DirType)30}, + {0x003CFFE3L,(DirType)30}, + {0x0038FFE5L,(DirType)30}, + {0x0035FFE7L,(DirType)31}, + {0x0030FFE9L,(DirType)31}, + {0x002BFFEBL,(DirType)31}, + {0x0024FFEDL,(DirType)31}, + {0x001FFFF1L,(DirType)31}, + {0x0018FFF4L,(DirType)32}, + {0x0011FFF7L,(DirType)32}, + {0x000CFFFAL,(DirType)32}, + {0x0006FFFDL,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track8[] = { + {0x0003FFFCL,(DirType)32}, + {0x0006FFF7L,(DirType)36}, + {0x000AFFF1L,(DirType)40}, + {0x000CFFEBL,(DirType)44}, + {0x000DFFE4L,(DirType)46}, + {0x000EFFDCL,(DirType)48}, + {0x000FFFD5L,(DirType)50}, + {0x0010FFD0L,(DirType)52}, + {0x0011FFC9L,(DirType)54}, + {0x0012FFC2L,(DirType)56}, + {0x0011FFC0L,(DirType)58}, + {0x0010FFC2L,(DirType)60}, + {0x000EFFC9L,(DirType)62}, + {0x000CFFCFL,(DirType)64}, + {0x000AFFD5L,(DirType)64}, + {0x0008FFDAL,(DirType)64}, + {0x0006FFE2L,(DirType)64}, + {0x0004FFE9L,(DirType)64}, + {0x0002FFEFL,(DirType)64}, + {0x0001FFF5L,(DirType)64}, + {0x0000FFF9L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track9[] = { + {0xFFF50002L,(DirType)0}, + {0xFFEB0004L,(DirType)2}, + {0xFFE00006L,(DirType)4}, + {0xFFD50009L,(DirType)6}, + {0xFFCE000CL,(DirType)9}, + {0xFFC8000FL,(DirType)11}, + {0xFFC00012L,(DirType)13}, + {0xFFB80015L,(DirType)16}, + {0xFFC00012L,(DirType)18}, + {0xFFC8000EL,(DirType)20}, + {0xFFCE000AL,(DirType)22}, + {0xFFD50004L,(DirType)24}, + {0xFFDE0000L,(DirType)26}, + {0xFFE9FFF8L,(DirType)28}, + {0xFFEEFFF2L,(DirType)30}, + {0xFFF5FFEBL,(DirType)32}, + {0xFFFDFFE1L,(DirType)34}, + {0x0002FFD8L,(DirType)36}, + {0x0007FFD2L,(DirType)39}, + {0x000BFFCBL,(DirType)41}, + {0x0010FFC5L,(DirType)43}, + {0x0013FFBEL,(DirType)45}, + {0x0015FFB7L,(DirType)48}, + {0x0013FFBEL,(DirType)50}, + {0x0011FFC5L,(DirType)52}, + {0x000BFFCCL,(DirType)54}, + {0x0008FFD4L,(DirType)56}, + {0x0005FFDFL,(DirType)58}, + {0x0003FFEBL,(DirType)62}, + {0x0001FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track10[] = { + {0xFFF6000BL,(DirType)32}, + {0xFFF00015L,(DirType)37}, + {0xFFEB0020L,(DirType)42}, + {0xFFE9002BL,(DirType)47}, + {0xFFE50032L,(DirType)52}, + {0xFFE30038L,(DirType)57}, + {0xFFE00040L,(DirType)60}, + {0xFFE20038L,(DirType)62}, + {0xFFE40032L,(DirType)64}, + {0xFFE5002AL,(DirType)68}, + {0xFFE6001EL,(DirType)70}, + {0xFFE70015L,(DirType)72}, + {0xFFE8000BL,(DirType)74}, + {0xFFE90000L,(DirType)76}, + {0xFFE8FFF5L,(DirType)78}, + {0xFFE7FFEBL,(DirType)80}, + {0xFFE6FFE0L,(DirType)82}, + {0xFFE5FFD5L,(DirType)84}, + {0xFFE4FFCEL,(DirType)86}, + {0xFFE2FFC5L,(DirType)88}, + {0xFFE0FFC0L,(DirType)90}, + {0xFFE3FFC5L,(DirType)92}, + {0xFFE5FFCEL,(DirType)94}, + {0xFFE9FFD5L,(DirType)95}, + {0xFFEBFFE0L,(DirType)96}, + {0xFFF0FFEBL,(DirType)96}, + {0xFFF6FFF5L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track11[] = { + {0x01000000L,DIR_SW}, + {0x00F30008L,DIR_SW}, + {0x00E50010L,DIR_SW_X1}, + {0x00D60018L,DIR_SW_X1}, + {0x00C80020L,DIR_SW_X1}, + {0x00B90028L,DIR_SW_X1}, + {0x00AB0030L,DIR_SW_X2}, + {0x009C0038L,DIR_SW_X2}, + {0x008D0040L,DIR_SW_X2}, + {0x007F0048L,DIR_SW_X2}, + {0x00710050L,DIR_SW_X2}, + {0x00640058L,DIR_SW_X2}, + {0x00550060L,DIR_SW_X2}, + + {0x00000000L,DIR_SW_X2} +}; + +DriveClass::TrackType const DriveClass::Track12[] = { + {0xFF550060L,DIR_SW_X2}, + {0xFF640058L,DIR_SW_X2}, + {0xFF710050L,DIR_SW_X2}, + {0xFF7F0048L,DIR_SW_X2}, + {0xFF8D0040L,DIR_SW_X2}, + {0xFF9C0038L,DIR_SW_X2}, + {0xFFAB0030L,DIR_SW_X2}, + {0xFFB90028L,DIR_SW_X1}, + {0xFFC80020L,DIR_SW_X1}, + {0xFFD60018L,DIR_SW_X1}, + {0xFFE50010L,DIR_SW_X1}, + {0xFFF30008L,DIR_SW}, + + {0x00000000L,DIR_SW} +}; + +#if(1) +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(0,-35),DIR_S}, + {XYP_COORD(0,-34),DIR_S}, + {XYP_COORD(0,-33),DIR_S}, + {XYP_COORD(0,-32),DIR_S}, + {XYP_COORD(0,-31),DIR_S}, + {XYP_COORD(0,-30),DIR_S}, + {XYP_COORD(0,-29),DIR_S}, + {XYP_COORD(0,-28),DIR_S}, + {XYP_COORD(0,-27),DIR_S}, + {XYP_COORD(0,-26),DIR_S}, + {XYP_COORD(0,-25),DIR_S}, + {XYP_COORD(0,-24),DIR_S}, + {XYP_COORD(0,-23),DIR_S}, + {XYP_COORD(0,-22),DIR_S}, + {XYP_COORD(0,-21),DIR_S}, + {XYP_COORD(0,-20),DIR_S}, + {XYP_COORD(0,-19),DIR_S}, + {XYP_COORD(0,-18),DIR_S}, + {XYP_COORD(0,-17),DIR_S}, + {XYP_COORD(0,-16),DIR_S}, + {XYP_COORD(0,-15),DIR_S}, + {XYP_COORD(0,-14),DIR_S}, + {XYP_COORD(0,-13),DIR_S}, + {XYP_COORD(0,-12),DIR_S}, + {XYP_COORD(0,-11),DIR_S}, + {XYP_COORD(0,-10),DIR_S}, + {XYP_COORD(0,-9),DIR_S}, + {XYP_COORD(0,-8),DIR_S}, + {XYP_COORD(0,-7),DIR_S}, + {XYP_COORD(0,-6),DIR_S}, + {XYP_COORD(0,-5),DIR_S}, + {XYP_COORD(0,-4),DIR_S}, + {XYP_COORD(0,-3),DIR_S}, + {XYP_COORD(0,-2),DIR_S}, + {XYP_COORD(0,-1),DIR_S}, + + {0x00000000L,DIR_S} +}; +#else +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-17),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-16),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-15),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-14),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-13),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-12),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-11),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-10),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-9),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-8),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-7),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-6),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-5),(DirType)(DIR_SW-9)}, + {XYP_COORD(2,-4),(DirType)(DIR_SW-7)}, + {XYP_COORD(2,-3),(DirType)(DIR_SW-5)}, + {XYP_COORD(1,-2),(DirType)(DIR_SW-3)}, + {XYP_COORD(1,-1),(DirType)(DIR_SW-1)}, + + {0x00000000L,DIR_SW} +}; +#endif + +/* +** There are a limited basic number of tracks that a vehicle can follow. These +** are they. Each track can be interpreted differently but this is controlled +** by the TrackControl structure elaborated elsewhere. +*/ +DriveClass::RawTrackType const DriveClass::RawTracks[13] = { + {Track1, -1, 0, -1}, + {Track2, -1, 0, -1}, + {Track3, 37, 12, 22}, + {Track4, 26, 11, 19}, + {Track5, 45, 15, 31}, + {Track6, 44, 16, 27}, + {Track7, -1, 0, -1}, + {Track8, -1, 0, -1}, + {Track9, -1, 0, -1}, + {Track10, -1, 0, -1}, + {Track11, -1, 0, -1}, + {Track12, -1, 0, -1}, + {Track13, -1, 0, -1} +}; + + +/*************************************************************************** +** Smooth turning control table. Given two directions in a path list, this +** table determines which track to use and what modifying operations need +** be performed on the track data. +*/ +DriveClass::TurnTrackType const DriveClass::TrackControl[67] = { + {1, 0, DIR_N, F_}, // 0-0 + {3, 7, DIR_NE, F_D}, // 0-1 (raw chart) + {4, 9, DIR_E, F_D}, // 0-2 (raw chart) + {0, 0, DIR_SE, F_}, // 0-3 ! + {0, 0, DIR_S, F_}, // 0-4 ! + {0, 0, DIR_SW, F_}, // 0-5 ! + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0 + {2, 0, DIR_NE, F_}, // 1-1 (raw chart) + {6, 8, DIR_E, F_D}, // 1-2 (raw chart) + {5, 10, DIR_SE, F_D}, // 1-3 (raw chart) + {0, 0, DIR_S, F_}, // 1-4 ! + {0, 0, DIR_SW, F_}, // 1-5 ! + {0, 0, DIR_W, F_}, // 1-6 ! + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0 + {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1 + {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3 + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4 + {0, 0, DIR_SW, F_}, // 2-5 ! + {0, 0, DIR_W, F_}, // 2-6 ! + {0, 0, DIR_NW, F_}, // 2-7 ! + {0, 0, DIR_N, F_}, // 3-0 ! + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1 + {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2 + {2, 0, DIR_SE, F_Y}, // 3-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4 + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5 + {0, 0, DIR_W, F_}, // 3-6 ! + {0, 0, DIR_NW, F_}, // 3-7 ! + {0, 0, DIR_N, F_}, // 4-0 ! + {0, 0, DIR_NE, F_}, // 4-1 ! + {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3 + {1, 0, DIR_S, F_Y}, // 4-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5 + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6 + {0, 0, DIR_NW, F_}, // 4-7 ! + {0, 0, DIR_N, F_}, // 5-0 ! + {0, 0, DIR_NE, F_}, // 5-1 ! + {0, 0, DIR_E, F_}, // 5-2 ! + {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4 + {2, 0, DIR_SW, F_T}, // 5-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6 + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0 + {0, 0, DIR_NE, F_}, // 6-1 ! + {0, 0, DIR_E, F_}, // 6-2 ! + {0, 0, DIR_SE, F_}, // 6-3 ! + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5 + {1, 0, DIR_W, F_T}, // 6-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0 + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1 + {0, 0, DIR_E, F_}, // 7-2 ! + {0, 0, DIR_SE, F_}, // 7-3 ! + {0, 0, DIR_S, F_}, // 7-4 ! + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6 + {2, 0, DIR_NW, F_X}, // 7-7 + + {11, 11, DIR_SW, F_}, // Backup harvester into refinery. + {12, 12, DIR_SW_X2, F_}, // Drive back into refinery. + {13, 13, DIR_SW, F_} // Drive out of weapons factory. +}; + + + diff --git a/REDALERT/DRIVE.H b/REDALERT/DRIVE.H new file mode 100644 index 000000000..3cb332caf --- /dev/null +++ b/REDALERT/DRIVE.H @@ -0,0 +1,208 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DRIVE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DRIVE_H +#define DRIVE_H + +#include "foot.h" + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class DriveClass : public FootClass +{ + public: + /* + ** If this unit performing harvesting action, then this flag is true. The flag + ** is located here because the other bit flags here give it a free place to + ** reside. + */ + unsigned IsHarvesting:1; + + /* + ** This flag controls whether the unit has been moebius'd into a + ** different location, and whether the MoebiusCountDown timer should be + ** used to take him back where he belongs. + */ + unsigned IsMoebius:1; + + /* + ** This controls how long a unit can exist in its alternate location + ** before being pulled back by the chronosphere into its normal location. + */ + CDTimerClass MoebiusCountDown; + + /* + ** This is the coord the unit will be taken back to once its moebius + ** effect wears off. + */ + CELL MoebiusCell; + + /* + ** Some units must have their turret locked down to face their body direction. + ** When this flag is set, this condition is in effect. This flag is a more + ** accurate check than examining the TrackNumber since the turret may be + ** rotating into position so that a pending track may start. During this process + ** the track number does not indicate anything. + */ + unsigned IsTurretLockedDown:1; + + /* + ** This vehicle could be processing a "short track". A short track is one that + ** doesn't actually go anywhere. Kind of like turning in place. + */ + unsigned IsOnShortTrack:1; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + DriveClass(RTTIType rtti, int id, HousesType house); + DriveClass(NoInitClass const & x) : FootClass(x), MoebiusCountDown(x) {}; + virtual ~DriveClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Teleport_To(CELL cell); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + virtual bool Limbo(void); + void Do_Turn(DirType dir); + virtual void Overrun_Square(CELL , bool =true) {}; + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual bool Ok_To_Move(DirType ) const; + virtual void AI(void); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + void Force_Track(int track, COORDINATE coord); + virtual bool Stop_Driver(void); + + void Mark_Track(COORDINATE headto, MarkType type); + + /********************************************************************** + ** These enumerations are used as working constants that exist only + ** in the DriveClass namespace. + */ + enum DriveClassEnum { + BACKUP_INTO_REFINERY=64, // Track to backup into refinery. + OUT_OF_REFINERY, // Track to leave refinery. + OUT_OF_WEAPON_FACTORY // Track to leave weapons factory. + }; + + /**************************************************************************** + ** Smooth turning tracks are controlled by this structure and these + ** processing bits. + */ + typedef enum TrackControlType : unsigned char { + F_=0x00, // No translation necessary? + F_T=0x01, // Transpose X and Y components? + F_X=0x02, // Reverse X component sign? + F_Y=0x04, // Reverse Y component sign? + F_D=0x08 // Two cell consumption? + } TrackControlType; + + private: + + typedef struct { + char Track; // Which track to use. + char StartTrack; // Track when starting from stand-still. + DirType Facing; // Facing when track has been completed. + DriveClass::TrackControlType Flag; // List processing flag bits. + } TurnTrackType; + + typedef struct { + COORDINATE Offset; // Offset to origin coordinate. + DirType Facing; // Facing (primary track). + } TrackType; + + typedef struct { + TrackType const * Track; // Pointer to track list. + int Jump; // Index where track jumping is allowed. + int Entry; // Entry point if jumping to this track. + int Cell; // Per cell process should occur at this index. + } RawTrackType; + + /* + ** These speed values are used to accumulate movement and then + ** convert them into pixel "steps" that are then translated through + ** the currently running track so that the unit will move. + */ + int SpeedAccum; + + /* + ** This the track control logic (used for ground vehicles only). The 'Track' + ** variable holds the track being followed (0 == not following track). The + ** 'TrackIndex' variable holds the current index into the specified track + ** (starts at 0). + */ + int TrackNumber; + int TrackIndex; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual void Fixup_Path(PathType *path); + bool While_Moving(void); + bool Start_Of_Move(void); + void Lay_Track(void); + COORDINATE Smooth_Turn(COORDINATE adj, DirType & dir); + + static TurnTrackType const TrackControl[67]; + static RawTrackType const RawTracks[13]; + static TrackType const Track13[]; + static TrackType const Track12[]; + static TrackType const Track11[]; + static TrackType const Track10[]; + static TrackType const Track9[]; + static TrackType const Track8[]; + static TrackType const Track7[]; + static TrackType const Track6[]; + static TrackType const Track5[]; + static TrackType const Track4[]; + static TrackType const Track3[]; + static TrackType const Track2[]; + static TrackType const Track1[24]; +}; + +//PG inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType); +//PG inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType); +//PG inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType); + + +#endif diff --git a/REDALERT/DROP.CPP b/REDALERT/DROP.CPP new file mode 100644 index 000000000..ebd418555 --- /dev/null +++ b/REDALERT/DROP.CPP @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DROP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DROP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/24/96 * + * * + * Last Update : January 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "drop.h" + + +DropListClass::DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9*RESFACTOR, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + Fancy_Text_Print("", 0, 0, 0, 0, flags); + EditClass::Height = FontHeight+1; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + +DropListClass & DropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((DropListClass &)EditClass::Add(object)); +} + +DropListClass & DropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((DropListClass &)EditClass::Add_Tail(object)); +} + +DropListClass & DropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((DropListClass &)EditClass::Add_Head(object)); +} + +DropListClass * DropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((DropListClass *)EditClass::Remove()); +} + +int DropListClass::Add_Item(char const * text) +{ + strncpy(String, text, MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(text)); +} + +char const * DropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + +int DropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + +void DropListClass::Set_Selected_Index(int index) +{ + if (index < List.Count()) { + List.Set_Selected_Index(index); + strcpy(String, List.Get_Item(Current_Index())); + } else { + String[0] = '\0'; + } +} + + +void DropListClass::Clear_Focus(void) +{ + Collapse(); +} + +void DropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + +void DropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9*RESFACTOR; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + +void DropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +DropListClass & DropListClass::operator = (DropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +DropListClass::DropListClass(DropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +void DropListClass::Set_Selected_Index(char const * text) +{ + if (text) { + for (int index = 0; index < Count(); index++) { + if (stricmp(text, List.Get_Item(index)) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} + +#ifdef WOLAPI_INTEGRATION +void DropListClass::Flag_To_Redraw(void) +{ + if( IsDropped ) + List.Flag_To_Redraw(); + EditClass::Flag_To_Redraw(); +} +#endif diff --git a/REDALERT/DROP.H b/REDALERT/DROP.H new file mode 100644 index 000000000..7fcb25824 --- /dev/null +++ b/REDALERT/DROP.H @@ -0,0 +1,350 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DROP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DROP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DROP_H +#define DROP_H + +#include "list.h" +#include "edit.h" + +class DropListClass : public EditClass { + public: + DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + virtual ~DropListClass(void) {}; + + virtual DropListClass & Add(LinkClass & object); + virtual DropListClass & Add_Tail(LinkClass & object); + virtual DropListClass & Add_Head(LinkClass & object); + virtual DropListClass * Remove(void); + virtual void Zap(void); + + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual char const * Get_Item(int index) const {return(List.Get_Item(index));}; + +#ifdef WOLAPI_INTEGRATION + virtual void Flag_To_Redraw(void); +#endif + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + DropListClass & operator = (DropListClass const & list); + DropListClass(DropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + ListClass List; +}; + + + + +template +class TDropListClass : public EditClass { + public: + TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + TDropListClass(TDropListClass const & list); + virtual ~TDropListClass(void) {}; + + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual TDropListClass & Add(LinkClass & object); + virtual TDropListClass & Add_Tail(LinkClass & object); + virtual TDropListClass & Add_Head(LinkClass & object); + virtual TDropListClass * Remove(void); + void Zap(void); + + virtual int Add_Item(T text); + virtual T Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual T Get_Item(int index) const {return(List.Get_Item(index));}; + + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + TDropListClass & operator = (TDropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + TListClass List; +}; + + +template +TDropListClass::TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + + +template +TDropListClass & TDropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((TDropListClass &)EditClass::Add(object)); +} + + +template +TDropListClass & TDropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((TDropListClass &)EditClass::Add_Tail(object)); +} + + +template +TDropListClass & TDropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((TDropListClass &)EditClass::Add_Head(object)); +} + + +template +TDropListClass * TDropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((TDropListClass *)EditClass::Remove()); +} + + +template +int TDropListClass::Add_Item(T item) +{ + strncpy(String, item->Description(), MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(item)); +} + + +template +T TDropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + + +template +int TDropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + + +template +void TDropListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Set_Selected_Index(index); + strncpy(String, List.Get_Item(Current_Index())->Description(), MaxLength); + } else { + String[0] = '\0'; + } +} + + +template +void TDropListClass::Clear_Focus(void) +{ + Collapse(); +} + + +template +void TDropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item()->Description(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + + +template +void TDropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + + +template +void TDropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +template +TDropListClass & TDropListClass::operator = (TDropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +template +TDropListClass::TDropListClass(TDropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +template +void TDropListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == List.Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/REDALERT/DTABLE.CPP b/REDALERT/DTABLE.CPP new file mode 100644 index 000000000..4f7429189 --- /dev/null +++ b/REDALERT/DTABLE.CPP @@ -0,0 +1,1443 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +long DiffTable[] = { + 0, // Index = 0, Token = 0 + 1, // Index = 0, Token = 1 + 3, // Index = 0, Token = 2 + 4, // Index = 0, Token = 3 + 7, // Index = 0, Token = 4 + 8, // Index = 0, Token = 5 + 10, // Index = 0, Token = 6 + 11, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + -1, // Index = 0, Token = 9 + -3, // Index = 0, Token = 10 + -4, // Index = 0, Token = 11 + -7, // Index = 0, Token = 12 + -8, // Index = 0, Token = 13 + -10, // Index = 0, Token = 14 + -11, // Index = 0, Token = 15 + 1, // Index = 1, Token = 0 + 3, // Index = 1, Token = 1 + 5, // Index = 1, Token = 2 + 7, // Index = 1, Token = 3 + 9, // Index = 1, Token = 4 + 11, // Index = 1, Token = 5 + 13, // Index = 1, Token = 6 + 15, // Index = 1, Token = 7 + -1, // Index = 1, Token = 8 + -3, // Index = 1, Token = 9 + -5, // Index = 1, Token = 10 + -7, // Index = 1, Token = 11 + -9, // Index = 1, Token = 12 + -11, // Index = 1, Token = 13 + -13, // Index = 1, Token = 14 + -15, // Index = 1, Token = 15 + 1, // Index = 2, Token = 0 + 3, // Index = 2, Token = 1 + 5, // Index = 2, Token = 2 + 7, // Index = 2, Token = 3 + 10, // Index = 2, Token = 4 + 12, // Index = 2, Token = 5 + 14, // Index = 2, Token = 6 + 16, // Index = 2, Token = 7 + -1, // Index = 2, Token = 8 + -3, // Index = 2, Token = 9 + -5, // Index = 2, Token = 10 + -7, // Index = 2, Token = 11 + -10, // Index = 2, Token = 12 + -12, // Index = 2, Token = 13 + -14, // Index = 2, Token = 14 + -16, // Index = 2, Token = 15 + 1, // Index = 3, Token = 0 + 3, // Index = 3, Token = 1 + 6, // Index = 3, Token = 2 + 8, // Index = 3, Token = 3 + 11, // Index = 3, Token = 4 + 13, // Index = 3, Token = 5 + 16, // Index = 3, Token = 6 + 18, // Index = 3, Token = 7 + -1, // Index = 3, Token = 8 + -3, // Index = 3, Token = 9 + -6, // Index = 3, Token = 10 + -8, // Index = 3, Token = 11 + -11, // Index = 3, Token = 12 + -13, // Index = 3, Token = 13 + -16, // Index = 3, Token = 14 + -18, // Index = 3, Token = 15 + 1, // Index = 4, Token = 0 + 3, // Index = 4, Token = 1 + 6, // Index = 4, Token = 2 + 8, // Index = 4, Token = 3 + 12, // Index = 4, Token = 4 + 14, // Index = 4, Token = 5 + 17, // Index = 4, Token = 6 + 19, // Index = 4, Token = 7 + -1, // Index = 4, Token = 8 + -3, // Index = 4, Token = 9 + -6, // Index = 4, Token = 10 + -8, // Index = 4, Token = 11 + -12, // Index = 4, Token = 12 + -14, // Index = 4, Token = 13 + -17, // Index = 4, Token = 14 + -19, // Index = 4, Token = 15 + 1, // Index = 5, Token = 0 + 4, // Index = 5, Token = 1 + 7, // Index = 5, Token = 2 + 10, // Index = 5, Token = 3 + 13, // Index = 5, Token = 4 + 16, // Index = 5, Token = 5 + 19, // Index = 5, Token = 6 + 22, // Index = 5, Token = 7 + -1, // Index = 5, Token = 8 + -4, // Index = 5, Token = 9 + -7, // Index = 5, Token = 10 + -10, // Index = 5, Token = 11 + -13, // Index = 5, Token = 12 + -16, // Index = 5, Token = 13 + -19, // Index = 5, Token = 14 + -22, // Index = 5, Token = 15 + 1, // Index = 6, Token = 0 + 4, // Index = 6, Token = 1 + 7, // Index = 6, Token = 2 + 10, // Index = 6, Token = 3 + 14, // Index = 6, Token = 4 + 17, // Index = 6, Token = 5 + 20, // Index = 6, Token = 6 + 23, // Index = 6, Token = 7 + -1, // Index = 6, Token = 8 + -4, // Index = 6, Token = 9 + -7, // Index = 6, Token = 10 + -10, // Index = 6, Token = 11 + -14, // Index = 6, Token = 12 + -17, // Index = 6, Token = 13 + -20, // Index = 6, Token = 14 + -23, // Index = 6, Token = 15 + 1, // Index = 7, Token = 0 + 4, // Index = 7, Token = 1 + 8, // Index = 7, Token = 2 + 11, // Index = 7, Token = 3 + 15, // Index = 7, Token = 4 + 18, // Index = 7, Token = 5 + 22, // Index = 7, Token = 6 + 25, // Index = 7, Token = 7 + -1, // Index = 7, Token = 8 + -4, // Index = 7, Token = 9 + -8, // Index = 7, Token = 10 + -11, // Index = 7, Token = 11 + -15, // Index = 7, Token = 12 + -18, // Index = 7, Token = 13 + -22, // Index = 7, Token = 14 + -25, // Index = 7, Token = 15 + 2, // Index = 8, Token = 0 + 6, // Index = 8, Token = 1 + 10, // Index = 8, Token = 2 + 14, // Index = 8, Token = 3 + 18, // Index = 8, Token = 4 + 22, // Index = 8, Token = 5 + 26, // Index = 8, Token = 6 + 30, // Index = 8, Token = 7 + -2, // Index = 8, Token = 8 + -6, // Index = 8, Token = 9 + -10, // Index = 8, Token = 10 + -14, // Index = 8, Token = 11 + -18, // Index = 8, Token = 12 + -22, // Index = 8, Token = 13 + -26, // Index = 8, Token = 14 + -30, // Index = 8, Token = 15 + 2, // Index = 9, Token = 0 + 6, // Index = 9, Token = 1 + 10, // Index = 9, Token = 2 + 14, // Index = 9, Token = 3 + 19, // Index = 9, Token = 4 + 23, // Index = 9, Token = 5 + 27, // Index = 9, Token = 6 + 31, // Index = 9, Token = 7 + -2, // Index = 9, Token = 8 + -6, // Index = 9, Token = 9 + -10, // Index = 9, Token = 10 + -14, // Index = 9, Token = 11 + -19, // Index = 9, Token = 12 + -23, // Index = 9, Token = 13 + -27, // Index = 9, Token = 14 + -31, // Index = 9, Token = 15 + 2, // Index = 10, Token = 0 + 6, // Index = 10, Token = 1 + 11, // Index = 10, Token = 2 + 15, // Index = 10, Token = 3 + 21, // Index = 10, Token = 4 + 25, // Index = 10, Token = 5 + 30, // Index = 10, Token = 6 + 34, // Index = 10, Token = 7 + -2, // Index = 10, Token = 8 + -6, // Index = 10, Token = 9 + -11, // Index = 10, Token = 10 + -15, // Index = 10, Token = 11 + -21, // Index = 10, Token = 12 + -25, // Index = 10, Token = 13 + -30, // Index = 10, Token = 14 + -34, // Index = 10, Token = 15 + 2, // Index = 11, Token = 0 + 7, // Index = 11, Token = 1 + 12, // Index = 11, Token = 2 + 17, // Index = 11, Token = 3 + 23, // Index = 11, Token = 4 + 28, // Index = 11, Token = 5 + 33, // Index = 11, Token = 6 + 38, // Index = 11, Token = 7 + -2, // Index = 11, Token = 8 + -7, // Index = 11, Token = 9 + -12, // Index = 11, Token = 10 + -17, // Index = 11, Token = 11 + -23, // Index = 11, Token = 12 + -28, // Index = 11, Token = 13 + -33, // Index = 11, Token = 14 + -38, // Index = 11, Token = 15 + 2, // Index = 12, Token = 0 + 7, // Index = 12, Token = 1 + 13, // Index = 12, Token = 2 + 18, // Index = 12, Token = 3 + 25, // Index = 12, Token = 4 + 30, // Index = 12, Token = 5 + 36, // Index = 12, Token = 6 + 41, // Index = 12, Token = 7 + -2, // Index = 12, Token = 8 + -7, // Index = 12, Token = 9 + -13, // Index = 12, Token = 10 + -18, // Index = 12, Token = 11 + -25, // Index = 12, Token = 12 + -30, // Index = 12, Token = 13 + -36, // Index = 12, Token = 14 + -41, // Index = 12, Token = 15 + 3, // Index = 13, Token = 0 + 9, // Index = 13, Token = 1 + 15, // Index = 13, Token = 2 + 21, // Index = 13, Token = 3 + 28, // Index = 13, Token = 4 + 34, // Index = 13, Token = 5 + 40, // Index = 13, Token = 6 + 46, // Index = 13, Token = 7 + -3, // Index = 13, Token = 8 + -9, // Index = 13, Token = 9 + -15, // Index = 13, Token = 10 + -21, // Index = 13, Token = 11 + -28, // Index = 13, Token = 12 + -34, // Index = 13, Token = 13 + -40, // Index = 13, Token = 14 + -46, // Index = 13, Token = 15 + 3, // Index = 14, Token = 0 + 10, // Index = 14, Token = 1 + 17, // Index = 14, Token = 2 + 24, // Index = 14, Token = 3 + 31, // Index = 14, Token = 4 + 38, // Index = 14, Token = 5 + 45, // Index = 14, Token = 6 + 52, // Index = 14, Token = 7 + -3, // Index = 14, Token = 8 + -10, // Index = 14, Token = 9 + -17, // Index = 14, Token = 10 + -24, // Index = 14, Token = 11 + -31, // Index = 14, Token = 12 + -38, // Index = 14, Token = 13 + -45, // Index = 14, Token = 14 + -52, // Index = 14, Token = 15 + 3, // Index = 15, Token = 0 + 10, // Index = 15, Token = 1 + 18, // Index = 15, Token = 2 + 25, // Index = 15, Token = 3 + 34, // Index = 15, Token = 4 + 41, // Index = 15, Token = 5 + 49, // Index = 15, Token = 6 + 56, // Index = 15, Token = 7 + -3, // Index = 15, Token = 8 + -10, // Index = 15, Token = 9 + -18, // Index = 15, Token = 10 + -25, // Index = 15, Token = 11 + -34, // Index = 15, Token = 12 + -41, // Index = 15, Token = 13 + -49, // Index = 15, Token = 14 + -56, // Index = 15, Token = 15 + 4, // Index = 16, Token = 0 + 12, // Index = 16, Token = 1 + 21, // Index = 16, Token = 2 + 29, // Index = 16, Token = 3 + 38, // Index = 16, Token = 4 + 46, // Index = 16, Token = 5 + 55, // Index = 16, Token = 6 + 63, // Index = 16, Token = 7 + -4, // Index = 16, Token = 8 + -12, // Index = 16, Token = 9 + -21, // Index = 16, Token = 10 + -29, // Index = 16, Token = 11 + -38, // Index = 16, Token = 12 + -46, // Index = 16, Token = 13 + -55, // Index = 16, Token = 14 + -63, // Index = 16, Token = 15 + 4, // Index = 17, Token = 0 + 13, // Index = 17, Token = 1 + 22, // Index = 17, Token = 2 + 31, // Index = 17, Token = 3 + 41, // Index = 17, Token = 4 + 50, // Index = 17, Token = 5 + 59, // Index = 17, Token = 6 + 68, // Index = 17, Token = 7 + -4, // Index = 17, Token = 8 + -13, // Index = 17, Token = 9 + -22, // Index = 17, Token = 10 + -31, // Index = 17, Token = 11 + -41, // Index = 17, Token = 12 + -50, // Index = 17, Token = 13 + -59, // Index = 17, Token = 14 + -68, // Index = 17, Token = 15 + 5, // Index = 18, Token = 0 + 15, // Index = 18, Token = 1 + 25, // Index = 18, Token = 2 + 35, // Index = 18, Token = 3 + 46, // Index = 18, Token = 4 + 56, // Index = 18, Token = 5 + 66, // Index = 18, Token = 6 + 76, // Index = 18, Token = 7 + -5, // Index = 18, Token = 8 + -15, // Index = 18, Token = 9 + -25, // Index = 18, Token = 10 + -35, // Index = 18, Token = 11 + -46, // Index = 18, Token = 12 + -56, // Index = 18, Token = 13 + -66, // Index = 18, Token = 14 + -76, // Index = 18, Token = 15 + 5, // Index = 19, Token = 0 + 16, // Index = 19, Token = 1 + 27, // Index = 19, Token = 2 + 38, // Index = 19, Token = 3 + 50, // Index = 19, Token = 4 + 61, // Index = 19, Token = 5 + 72, // Index = 19, Token = 6 + 83, // Index = 19, Token = 7 + -5, // Index = 19, Token = 8 + -16, // Index = 19, Token = 9 + -27, // Index = 19, Token = 10 + -38, // Index = 19, Token = 11 + -50, // Index = 19, Token = 12 + -61, // Index = 19, Token = 13 + -72, // Index = 19, Token = 14 + -83, // Index = 19, Token = 15 + 6, // Index = 20, Token = 0 + 18, // Index = 20, Token = 1 + 31, // Index = 20, Token = 2 + 43, // Index = 20, Token = 3 + 56, // Index = 20, Token = 4 + 68, // Index = 20, Token = 5 + 81, // Index = 20, Token = 6 + 93, // Index = 20, Token = 7 + -6, // Index = 20, Token = 8 + -18, // Index = 20, Token = 9 + -31, // Index = 20, Token = 10 + -43, // Index = 20, Token = 11 + -56, // Index = 20, Token = 12 + -68, // Index = 20, Token = 13 + -81, // Index = 20, Token = 14 + -93, // Index = 20, Token = 15 + 6, // Index = 21, Token = 0 + 19, // Index = 21, Token = 1 + 33, // Index = 21, Token = 2 + 46, // Index = 21, Token = 3 + 61, // Index = 21, Token = 4 + 74, // Index = 21, Token = 5 + 88, // Index = 21, Token = 6 + 101, // Index = 21, Token = 7 + -6, // Index = 21, Token = 8 + -19, // Index = 21, Token = 9 + -33, // Index = 21, Token = 10 + -46, // Index = 21, Token = 11 + -61, // Index = 21, Token = 12 + -74, // Index = 21, Token = 13 + -88, // Index = 21, Token = 14 + -101, // Index = 21, Token = 15 + 7, // Index = 22, Token = 0 + 22, // Index = 22, Token = 1 + 37, // Index = 22, Token = 2 + 52, // Index = 22, Token = 3 + 67, // Index = 22, Token = 4 + 82, // Index = 22, Token = 5 + 97, // Index = 22, Token = 6 + 112, // Index = 22, Token = 7 + -7, // Index = 22, Token = 8 + -22, // Index = 22, Token = 9 + -37, // Index = 22, Token = 10 + -52, // Index = 22, Token = 11 + -67, // Index = 22, Token = 12 + -82, // Index = 22, Token = 13 + -97, // Index = 22, Token = 14 + -112, // Index = 22, Token = 15 + 8, // Index = 23, Token = 0 + 24, // Index = 23, Token = 1 + 41, // Index = 23, Token = 2 + 57, // Index = 23, Token = 3 + 74, // Index = 23, Token = 4 + 90, // Index = 23, Token = 5 + 107, // Index = 23, Token = 6 + 123, // Index = 23, Token = 7 + -8, // Index = 23, Token = 8 + -24, // Index = 23, Token = 9 + -41, // Index = 23, Token = 10 + -57, // Index = 23, Token = 11 + -74, // Index = 23, Token = 12 + -90, // Index = 23, Token = 13 + -107, // Index = 23, Token = 14 + -123, // Index = 23, Token = 15 + 9, // Index = 24, Token = 0 + 27, // Index = 24, Token = 1 + 45, // Index = 24, Token = 2 + 63, // Index = 24, Token = 3 + 82, // Index = 24, Token = 4 + 100, // Index = 24, Token = 5 + 118, // Index = 24, Token = 6 + 136, // Index = 24, Token = 7 + -9, // Index = 24, Token = 8 + -27, // Index = 24, Token = 9 + -45, // Index = 24, Token = 10 + -63, // Index = 24, Token = 11 + -82, // Index = 24, Token = 12 + -100, // Index = 24, Token = 13 + -118, // Index = 24, Token = 14 + -136, // Index = 24, Token = 15 + 10, // Index = 25, Token = 0 + 30, // Index = 25, Token = 1 + 50, // Index = 25, Token = 2 + 70, // Index = 25, Token = 3 + 90, // Index = 25, Token = 4 + 110, // Index = 25, Token = 5 + 130, // Index = 25, Token = 6 + 150, // Index = 25, Token = 7 + -10, // Index = 25, Token = 8 + -30, // Index = 25, Token = 9 + -50, // Index = 25, Token = 10 + -70, // Index = 25, Token = 11 + -90, // Index = 25, Token = 12 + -110, // Index = 25, Token = 13 + -130, // Index = 25, Token = 14 + -150, // Index = 25, Token = 15 + 11, // Index = 26, Token = 0 + 33, // Index = 26, Token = 1 + 55, // Index = 26, Token = 2 + 77, // Index = 26, Token = 3 + 99, // Index = 26, Token = 4 + 121, // Index = 26, Token = 5 + 143, // Index = 26, Token = 6 + 165, // Index = 26, Token = 7 + -11, // Index = 26, Token = 8 + -33, // Index = 26, Token = 9 + -55, // Index = 26, Token = 10 + -77, // Index = 26, Token = 11 + -99, // Index = 26, Token = 12 + -121, // Index = 26, Token = 13 + -143, // Index = 26, Token = 14 + -165, // Index = 26, Token = 15 + 12, // Index = 27, Token = 0 + 36, // Index = 27, Token = 1 + 60, // Index = 27, Token = 2 + 84, // Index = 27, Token = 3 + 109, // Index = 27, Token = 4 + 133, // Index = 27, Token = 5 + 157, // Index = 27, Token = 6 + 181, // Index = 27, Token = 7 + -12, // Index = 27, Token = 8 + -36, // Index = 27, Token = 9 + -60, // Index = 27, Token = 10 + -84, // Index = 27, Token = 11 + -109, // Index = 27, Token = 12 + -133, // Index = 27, Token = 13 + -157, // Index = 27, Token = 14 + -181, // Index = 27, Token = 15 + 13, // Index = 28, Token = 0 + 39, // Index = 28, Token = 1 + 66, // Index = 28, Token = 2 + 92, // Index = 28, Token = 3 + 120, // Index = 28, Token = 4 + 146, // Index = 28, Token = 5 + 173, // Index = 28, Token = 6 + 199, // Index = 28, Token = 7 + -13, // Index = 28, Token = 8 + -39, // Index = 28, Token = 9 + -66, // Index = 28, Token = 10 + -92, // Index = 28, Token = 11 + -120, // Index = 28, Token = 12 + -146, // Index = 28, Token = 13 + -173, // Index = 28, Token = 14 + -199, // Index = 28, Token = 15 + 14, // Index = 29, Token = 0 + 43, // Index = 29, Token = 1 + 73, // Index = 29, Token = 2 + 102, // Index = 29, Token = 3 + 132, // Index = 29, Token = 4 + 161, // Index = 29, Token = 5 + 191, // Index = 29, Token = 6 + 220, // Index = 29, Token = 7 + -14, // Index = 29, Token = 8 + -43, // Index = 29, Token = 9 + -73, // Index = 29, Token = 10 + -102, // Index = 29, Token = 11 + -132, // Index = 29, Token = 12 + -161, // Index = 29, Token = 13 + -191, // Index = 29, Token = 14 + -220, // Index = 29, Token = 15 + 16, // Index = 30, Token = 0 + 48, // Index = 30, Token = 1 + 81, // Index = 30, Token = 2 + 113, // Index = 30, Token = 3 + 146, // Index = 30, Token = 4 + 178, // Index = 30, Token = 5 + 211, // Index = 30, Token = 6 + 243, // Index = 30, Token = 7 + -16, // Index = 30, Token = 8 + -48, // Index = 30, Token = 9 + -81, // Index = 30, Token = 10 + -113, // Index = 30, Token = 11 + -146, // Index = 30, Token = 12 + -178, // Index = 30, Token = 13 + -211, // Index = 30, Token = 14 + -243, // Index = 30, Token = 15 + 17, // Index = 31, Token = 0 + 52, // Index = 31, Token = 1 + 88, // Index = 31, Token = 2 + 123, // Index = 31, Token = 3 + 160, // Index = 31, Token = 4 + 195, // Index = 31, Token = 5 + 231, // Index = 31, Token = 6 + 266, // Index = 31, Token = 7 + -17, // Index = 31, Token = 8 + -52, // Index = 31, Token = 9 + -88, // Index = 31, Token = 10 + -123, // Index = 31, Token = 11 + -160, // Index = 31, Token = 12 + -195, // Index = 31, Token = 13 + -231, // Index = 31, Token = 14 + -266, // Index = 31, Token = 15 + 19, // Index = 32, Token = 0 + 58, // Index = 32, Token = 1 + 97, // Index = 32, Token = 2 + 136, // Index = 32, Token = 3 + 176, // Index = 32, Token = 4 + 215, // Index = 32, Token = 5 + 254, // Index = 32, Token = 6 + 293, // Index = 32, Token = 7 + -19, // Index = 32, Token = 8 + -58, // Index = 32, Token = 9 + -97, // Index = 32, Token = 10 + -136, // Index = 32, Token = 11 + -176, // Index = 32, Token = 12 + -215, // Index = 32, Token = 13 + -254, // Index = 32, Token = 14 + -293, // Index = 32, Token = 15 + 21, // Index = 33, Token = 0 + 64, // Index = 33, Token = 1 + 107, // Index = 33, Token = 2 + 150, // Index = 33, Token = 3 + 194, // Index = 33, Token = 4 + 237, // Index = 33, Token = 5 + 280, // Index = 33, Token = 6 + 323, // Index = 33, Token = 7 + -21, // Index = 33, Token = 8 + -64, // Index = 33, Token = 9 + -107, // Index = 33, Token = 10 + -150, // Index = 33, Token = 11 + -194, // Index = 33, Token = 12 + -237, // Index = 33, Token = 13 + -280, // Index = 33, Token = 14 + -323, // Index = 33, Token = 15 + 23, // Index = 34, Token = 0 + 70, // Index = 34, Token = 1 + 118, // Index = 34, Token = 2 + 165, // Index = 34, Token = 3 + 213, // Index = 34, Token = 4 + 260, // Index = 34, Token = 5 + 308, // Index = 34, Token = 6 + 355, // Index = 34, Token = 7 + -23, // Index = 34, Token = 8 + -70, // Index = 34, Token = 9 + -118, // Index = 34, Token = 10 + -165, // Index = 34, Token = 11 + -213, // Index = 34, Token = 12 + -260, // Index = 34, Token = 13 + -308, // Index = 34, Token = 14 + -355, // Index = 34, Token = 15 + 26, // Index = 35, Token = 0 + 78, // Index = 35, Token = 1 + 130, // Index = 35, Token = 2 + 182, // Index = 35, Token = 3 + 235, // Index = 35, Token = 4 + 287, // Index = 35, Token = 5 + 339, // Index = 35, Token = 6 + 391, // Index = 35, Token = 7 + -26, // Index = 35, Token = 8 + -78, // Index = 35, Token = 9 + -130, // Index = 35, Token = 10 + -182, // Index = 35, Token = 11 + -235, // Index = 35, Token = 12 + -287, // Index = 35, Token = 13 + -339, // Index = 35, Token = 14 + -391, // Index = 35, Token = 15 + 28, // Index = 36, Token = 0 + 85, // Index = 36, Token = 1 + 143, // Index = 36, Token = 2 + 200, // Index = 36, Token = 3 + 258, // Index = 36, Token = 4 + 315, // Index = 36, Token = 5 + 373, // Index = 36, Token = 6 + 430, // Index = 36, Token = 7 + -28, // Index = 36, Token = 8 + -85, // Index = 36, Token = 9 + -143, // Index = 36, Token = 10 + -200, // Index = 36, Token = 11 + -258, // Index = 36, Token = 12 + -315, // Index = 36, Token = 13 + -373, // Index = 36, Token = 14 + -430, // Index = 36, Token = 15 + 31, // Index = 37, Token = 0 + 94, // Index = 37, Token = 1 + 157, // Index = 37, Token = 2 + 220, // Index = 37, Token = 3 + 284, // Index = 37, Token = 4 + 347, // Index = 37, Token = 5 + 410, // Index = 37, Token = 6 + 473, // Index = 37, Token = 7 + -31, // Index = 37, Token = 8 + -94, // Index = 37, Token = 9 + -157, // Index = 37, Token = 10 + -220, // Index = 37, Token = 11 + -284, // Index = 37, Token = 12 + -347, // Index = 37, Token = 13 + -410, // Index = 37, Token = 14 + -473, // Index = 37, Token = 15 + 34, // Index = 38, Token = 0 + 103, // Index = 38, Token = 1 + 173, // Index = 38, Token = 2 + 242, // Index = 38, Token = 3 + 313, // Index = 38, Token = 4 + 382, // Index = 38, Token = 5 + 452, // Index = 38, Token = 6 + 521, // Index = 38, Token = 7 + -34, // Index = 38, Token = 8 + -103, // Index = 38, Token = 9 + -173, // Index = 38, Token = 10 + -242, // Index = 38, Token = 11 + -313, // Index = 38, Token = 12 + -382, // Index = 38, Token = 13 + -452, // Index = 38, Token = 14 + -521, // Index = 38, Token = 15 + 38, // Index = 39, Token = 0 + 114, // Index = 39, Token = 1 + 191, // Index = 39, Token = 2 + 267, // Index = 39, Token = 3 + 345, // Index = 39, Token = 4 + 421, // Index = 39, Token = 5 + 498, // Index = 39, Token = 6 + 574, // Index = 39, Token = 7 + -38, // Index = 39, Token = 8 + -114, // Index = 39, Token = 9 + -191, // Index = 39, Token = 10 + -267, // Index = 39, Token = 11 + -345, // Index = 39, Token = 12 + -421, // Index = 39, Token = 13 + -498, // Index = 39, Token = 14 + -574, // Index = 39, Token = 15 + 42, // Index = 40, Token = 0 + 126, // Index = 40, Token = 1 + 210, // Index = 40, Token = 2 + 294, // Index = 40, Token = 3 + 379, // Index = 40, Token = 4 + 463, // Index = 40, Token = 5 + 547, // Index = 40, Token = 6 + 631, // Index = 40, Token = 7 + -42, // Index = 40, Token = 8 + -126, // Index = 40, Token = 9 + -210, // Index = 40, Token = 10 + -294, // Index = 40, Token = 11 + -379, // Index = 40, Token = 12 + -463, // Index = 40, Token = 13 + -547, // Index = 40, Token = 14 + -631, // Index = 40, Token = 15 + 46, // Index = 41, Token = 0 + 138, // Index = 41, Token = 1 + 231, // Index = 41, Token = 2 + 323, // Index = 41, Token = 3 + 417, // Index = 41, Token = 4 + 509, // Index = 41, Token = 5 + 602, // Index = 41, Token = 6 + 694, // Index = 41, Token = 7 + -46, // Index = 41, Token = 8 + -138, // Index = 41, Token = 9 + -231, // Index = 41, Token = 10 + -323, // Index = 41, Token = 11 + -417, // Index = 41, Token = 12 + -509, // Index = 41, Token = 13 + -602, // Index = 41, Token = 14 + -694, // Index = 41, Token = 15 + 51, // Index = 42, Token = 0 + 153, // Index = 42, Token = 1 + 255, // Index = 42, Token = 2 + 357, // Index = 42, Token = 3 + 459, // Index = 42, Token = 4 + 561, // Index = 42, Token = 5 + 663, // Index = 42, Token = 6 + 765, // Index = 42, Token = 7 + -51, // Index = 42, Token = 8 + -153, // Index = 42, Token = 9 + -255, // Index = 42, Token = 10 + -357, // Index = 42, Token = 11 + -459, // Index = 42, Token = 12 + -561, // Index = 42, Token = 13 + -663, // Index = 42, Token = 14 + -765, // Index = 42, Token = 15 + 56, // Index = 43, Token = 0 + 168, // Index = 43, Token = 1 + 280, // Index = 43, Token = 2 + 392, // Index = 43, Token = 3 + 505, // Index = 43, Token = 4 + 617, // Index = 43, Token = 5 + 729, // Index = 43, Token = 6 + 841, // Index = 43, Token = 7 + -56, // Index = 43, Token = 8 + -168, // Index = 43, Token = 9 + -280, // Index = 43, Token = 10 + -392, // Index = 43, Token = 11 + -505, // Index = 43, Token = 12 + -617, // Index = 43, Token = 13 + -729, // Index = 43, Token = 14 + -841, // Index = 43, Token = 15 + 61, // Index = 44, Token = 0 + 184, // Index = 44, Token = 1 + 308, // Index = 44, Token = 2 + 431, // Index = 44, Token = 3 + 555, // Index = 44, Token = 4 + 678, // Index = 44, Token = 5 + 802, // Index = 44, Token = 6 + 925, // Index = 44, Token = 7 + -61, // Index = 44, Token = 8 + -184, // Index = 44, Token = 9 + -308, // Index = 44, Token = 10 + -431, // Index = 44, Token = 11 + -555, // Index = 44, Token = 12 + -678, // Index = 44, Token = 13 + -802, // Index = 44, Token = 14 + -925, // Index = 44, Token = 15 + 68, // Index = 45, Token = 0 + 204, // Index = 45, Token = 1 + 340, // Index = 45, Token = 2 + 476, // Index = 45, Token = 3 + 612, // Index = 45, Token = 4 + 748, // Index = 45, Token = 5 + 884, // Index = 45, Token = 6 + 1020, // Index = 45, Token = 7 + -68, // Index = 45, Token = 8 + -204, // Index = 45, Token = 9 + -340, // Index = 45, Token = 10 + -476, // Index = 45, Token = 11 + -612, // Index = 45, Token = 12 + -748, // Index = 45, Token = 13 + -884, // Index = 45, Token = 14 + -1020, // Index = 45, Token = 15 + 74, // Index = 46, Token = 0 + 223, // Index = 46, Token = 1 + 373, // Index = 46, Token = 2 + 522, // Index = 46, Token = 3 + 672, // Index = 46, Token = 4 + 821, // Index = 46, Token = 5 + 971, // Index = 46, Token = 6 + 1120, // Index = 46, Token = 7 + -74, // Index = 46, Token = 8 + -223, // Index = 46, Token = 9 + -373, // Index = 46, Token = 10 + -522, // Index = 46, Token = 11 + -672, // Index = 46, Token = 12 + -821, // Index = 46, Token = 13 + -971, // Index = 46, Token = 14 + -1120, // Index = 46, Token = 15 + 82, // Index = 47, Token = 0 + 246, // Index = 47, Token = 1 + 411, // Index = 47, Token = 2 + 575, // Index = 47, Token = 3 + 740, // Index = 47, Token = 4 + 904, // Index = 47, Token = 5 + 1069, // Index = 47, Token = 6 + 1233, // Index = 47, Token = 7 + -82, // Index = 47, Token = 8 + -246, // Index = 47, Token = 9 + -411, // Index = 47, Token = 10 + -575, // Index = 47, Token = 11 + -740, // Index = 47, Token = 12 + -904, // Index = 47, Token = 13 + -1069, // Index = 47, Token = 14 + -1233, // Index = 47, Token = 15 + 90, // Index = 48, Token = 0 + 271, // Index = 48, Token = 1 + 452, // Index = 48, Token = 2 + 633, // Index = 48, Token = 3 + 814, // Index = 48, Token = 4 + 995, // Index = 48, Token = 5 + 1176, // Index = 48, Token = 6 + 1357, // Index = 48, Token = 7 + -90, // Index = 48, Token = 8 + -271, // Index = 48, Token = 9 + -452, // Index = 48, Token = 10 + -633, // Index = 48, Token = 11 + -814, // Index = 48, Token = 12 + -995, // Index = 48, Token = 13 + -1176, // Index = 48, Token = 14 + -1357, // Index = 48, Token = 15 + 99, // Index = 49, Token = 0 + 298, // Index = 49, Token = 1 + 497, // Index = 49, Token = 2 + 696, // Index = 49, Token = 3 + 895, // Index = 49, Token = 4 + 1094, // Index = 49, Token = 5 + 1293, // Index = 49, Token = 6 + 1492, // Index = 49, Token = 7 + -99, // Index = 49, Token = 8 + -298, // Index = 49, Token = 9 + -497, // Index = 49, Token = 10 + -696, // Index = 49, Token = 11 + -895, // Index = 49, Token = 12 + -1094, // Index = 49, Token = 13 + -1293, // Index = 49, Token = 14 + -1492, // Index = 49, Token = 15 + 109, // Index = 50, Token = 0 + 328, // Index = 50, Token = 1 + 547, // Index = 50, Token = 2 + 766, // Index = 50, Token = 3 + 985, // Index = 50, Token = 4 + 1204, // Index = 50, Token = 5 + 1423, // Index = 50, Token = 6 + 1642, // Index = 50, Token = 7 + -109, // Index = 50, Token = 8 + -328, // Index = 50, Token = 9 + -547, // Index = 50, Token = 10 + -766, // Index = 50, Token = 11 + -985, // Index = 50, Token = 12 + -1204, // Index = 50, Token = 13 + -1423, // Index = 50, Token = 14 + -1642, // Index = 50, Token = 15 + 120, // Index = 51, Token = 0 + 360, // Index = 51, Token = 1 + 601, // Index = 51, Token = 2 + 841, // Index = 51, Token = 3 + 1083, // Index = 51, Token = 4 + 1323, // Index = 51, Token = 5 + 1564, // Index = 51, Token = 6 + 1804, // Index = 51, Token = 7 + -120, // Index = 51, Token = 8 + -360, // Index = 51, Token = 9 + -601, // Index = 51, Token = 10 + -841, // Index = 51, Token = 11 + -1083, // Index = 51, Token = 12 + -1323, // Index = 51, Token = 13 + -1564, // Index = 51, Token = 14 + -1804, // Index = 51, Token = 15 + 132, // Index = 52, Token = 0 + 397, // Index = 52, Token = 1 + 662, // Index = 52, Token = 2 + 927, // Index = 52, Token = 3 + 1192, // Index = 52, Token = 4 + 1457, // Index = 52, Token = 5 + 1722, // Index = 52, Token = 6 + 1987, // Index = 52, Token = 7 + -132, // Index = 52, Token = 8 + -397, // Index = 52, Token = 9 + -662, // Index = 52, Token = 10 + -927, // Index = 52, Token = 11 + -1192, // Index = 52, Token = 12 + -1457, // Index = 52, Token = 13 + -1722, // Index = 52, Token = 14 + -1987, // Index = 52, Token = 15 + 145, // Index = 53, Token = 0 + 436, // Index = 53, Token = 1 + 728, // Index = 53, Token = 2 + 1019, // Index = 53, Token = 3 + 1311, // Index = 53, Token = 4 + 1602, // Index = 53, Token = 5 + 1894, // Index = 53, Token = 6 + 2185, // Index = 53, Token = 7 + -145, // Index = 53, Token = 8 + -436, // Index = 53, Token = 9 + -728, // Index = 53, Token = 10 + -1019, // Index = 53, Token = 11 + -1311, // Index = 53, Token = 12 + -1602, // Index = 53, Token = 13 + -1894, // Index = 53, Token = 14 + -2185, // Index = 53, Token = 15 + 160, // Index = 54, Token = 0 + 480, // Index = 54, Token = 1 + 801, // Index = 54, Token = 2 + 1121, // Index = 54, Token = 3 + 1442, // Index = 54, Token = 4 + 1762, // Index = 54, Token = 5 + 2083, // Index = 54, Token = 6 + 2403, // Index = 54, Token = 7 + -160, // Index = 54, Token = 8 + -480, // Index = 54, Token = 9 + -801, // Index = 54, Token = 10 + -1121, // Index = 54, Token = 11 + -1442, // Index = 54, Token = 12 + -1762, // Index = 54, Token = 13 + -2083, // Index = 54, Token = 14 + -2403, // Index = 54, Token = 15 + 176, // Index = 55, Token = 0 + 528, // Index = 55, Token = 1 + 881, // Index = 55, Token = 2 + 1233, // Index = 55, Token = 3 + 1587, // Index = 55, Token = 4 + 1939, // Index = 55, Token = 5 + 2292, // Index = 55, Token = 6 + 2644, // Index = 55, Token = 7 + -176, // Index = 55, Token = 8 + -528, // Index = 55, Token = 9 + -881, // Index = 55, Token = 10 + -1233, // Index = 55, Token = 11 + -1587, // Index = 55, Token = 12 + -1939, // Index = 55, Token = 13 + -2292, // Index = 55, Token = 14 + -2644, // Index = 55, Token = 15 + 194, // Index = 56, Token = 0 + 582, // Index = 56, Token = 1 + 970, // Index = 56, Token = 2 + 1358, // Index = 56, Token = 3 + 1746, // Index = 56, Token = 4 + 2134, // Index = 56, Token = 5 + 2522, // Index = 56, Token = 6 + 2910, // Index = 56, Token = 7 + -194, // Index = 56, Token = 8 + -582, // Index = 56, Token = 9 + -970, // Index = 56, Token = 10 + -1358, // Index = 56, Token = 11 + -1746, // Index = 56, Token = 12 + -2134, // Index = 56, Token = 13 + -2522, // Index = 56, Token = 14 + -2910, // Index = 56, Token = 15 + 213, // Index = 57, Token = 0 + 639, // Index = 57, Token = 1 + 1066, // Index = 57, Token = 2 + 1492, // Index = 57, Token = 3 + 1920, // Index = 57, Token = 4 + 2346, // Index = 57, Token = 5 + 2773, // Index = 57, Token = 6 + 3199, // Index = 57, Token = 7 + -213, // Index = 57, Token = 8 + -639, // Index = 57, Token = 9 + -1066, // Index = 57, Token = 10 + -1492, // Index = 57, Token = 11 + -1920, // Index = 57, Token = 12 + -2346, // Index = 57, Token = 13 + -2773, // Index = 57, Token = 14 + -3199, // Index = 57, Token = 15 + 234, // Index = 58, Token = 0 + 703, // Index = 58, Token = 1 + 1173, // Index = 58, Token = 2 + 1642, // Index = 58, Token = 3 + 2112, // Index = 58, Token = 4 + 2581, // Index = 58, Token = 5 + 3051, // Index = 58, Token = 6 + 3520, // Index = 58, Token = 7 + -234, // Index = 58, Token = 8 + -703, // Index = 58, Token = 9 + -1173, // Index = 58, Token = 10 + -1642, // Index = 58, Token = 11 + -2112, // Index = 58, Token = 12 + -2581, // Index = 58, Token = 13 + -3051, // Index = 58, Token = 14 + -3520, // Index = 58, Token = 15 + 258, // Index = 59, Token = 0 + 774, // Index = 59, Token = 1 + 1291, // Index = 59, Token = 2 + 1807, // Index = 59, Token = 3 + 2324, // Index = 59, Token = 4 + 2840, // Index = 59, Token = 5 + 3357, // Index = 59, Token = 6 + 3873, // Index = 59, Token = 7 + -258, // Index = 59, Token = 8 + -774, // Index = 59, Token = 9 + -1291, // Index = 59, Token = 10 + -1807, // Index = 59, Token = 11 + -2324, // Index = 59, Token = 12 + -2840, // Index = 59, Token = 13 + -3357, // Index = 59, Token = 14 + -3873, // Index = 59, Token = 15 + 284, // Index = 60, Token = 0 + 852, // Index = 60, Token = 1 + 1420, // Index = 60, Token = 2 + 1988, // Index = 60, Token = 3 + 2556, // Index = 60, Token = 4 + 3124, // Index = 60, Token = 5 + 3692, // Index = 60, Token = 6 + 4260, // Index = 60, Token = 7 + -284, // Index = 60, Token = 8 + -852, // Index = 60, Token = 9 + -1420, // Index = 60, Token = 10 + -1988, // Index = 60, Token = 11 + -2556, // Index = 60, Token = 12 + -3124, // Index = 60, Token = 13 + -3692, // Index = 60, Token = 14 + -4260, // Index = 60, Token = 15 + 312, // Index = 61, Token = 0 + 936, // Index = 61, Token = 1 + 1561, // Index = 61, Token = 2 + 2185, // Index = 61, Token = 3 + 2811, // Index = 61, Token = 4 + 3435, // Index = 61, Token = 5 + 4060, // Index = 61, Token = 6 + 4684, // Index = 61, Token = 7 + -312, // Index = 61, Token = 8 + -936, // Index = 61, Token = 9 + -1561, // Index = 61, Token = 10 + -2185, // Index = 61, Token = 11 + -2811, // Index = 61, Token = 12 + -3435, // Index = 61, Token = 13 + -4060, // Index = 61, Token = 14 + -4684, // Index = 61, Token = 15 + 343, // Index = 62, Token = 0 + 1030, // Index = 62, Token = 1 + 1717, // Index = 62, Token = 2 + 2404, // Index = 62, Token = 3 + 3092, // Index = 62, Token = 4 + 3779, // Index = 62, Token = 5 + 4466, // Index = 62, Token = 6 + 5153, // Index = 62, Token = 7 + -343, // Index = 62, Token = 8 + -1030, // Index = 62, Token = 9 + -1717, // Index = 62, Token = 10 + -2404, // Index = 62, Token = 11 + -3092, // Index = 62, Token = 12 + -3779, // Index = 62, Token = 13 + -4466, // Index = 62, Token = 14 + -5153, // Index = 62, Token = 15 + 378, // Index = 63, Token = 0 + 1134, // Index = 63, Token = 1 + 1890, // Index = 63, Token = 2 + 2646, // Index = 63, Token = 3 + 3402, // Index = 63, Token = 4 + 4158, // Index = 63, Token = 5 + 4914, // Index = 63, Token = 6 + 5670, // Index = 63, Token = 7 + -378, // Index = 63, Token = 8 + -1134, // Index = 63, Token = 9 + -1890, // Index = 63, Token = 10 + -2646, // Index = 63, Token = 11 + -3402, // Index = 63, Token = 12 + -4158, // Index = 63, Token = 13 + -4914, // Index = 63, Token = 14 + -5670, // Index = 63, Token = 15 + 415, // Index = 64, Token = 0 + 1246, // Index = 64, Token = 1 + 2078, // Index = 64, Token = 2 + 2909, // Index = 64, Token = 3 + 3742, // Index = 64, Token = 4 + 4573, // Index = 64, Token = 5 + 5405, // Index = 64, Token = 6 + 6236, // Index = 64, Token = 7 + -415, // Index = 64, Token = 8 + -1246, // Index = 64, Token = 9 + -2078, // Index = 64, Token = 10 + -2909, // Index = 64, Token = 11 + -3742, // Index = 64, Token = 12 + -4573, // Index = 64, Token = 13 + -5405, // Index = 64, Token = 14 + -6236, // Index = 64, Token = 15 + 457, // Index = 65, Token = 0 + 1372, // Index = 65, Token = 1 + 2287, // Index = 65, Token = 2 + 3202, // Index = 65, Token = 3 + 4117, // Index = 65, Token = 4 + 5032, // Index = 65, Token = 5 + 5947, // Index = 65, Token = 6 + 6862, // Index = 65, Token = 7 + -457, // Index = 65, Token = 8 + -1372, // Index = 65, Token = 9 + -2287, // Index = 65, Token = 10 + -3202, // Index = 65, Token = 11 + -4117, // Index = 65, Token = 12 + -5032, // Index = 65, Token = 13 + -5947, // Index = 65, Token = 14 + -6862, // Index = 65, Token = 15 + 503, // Index = 66, Token = 0 + 1509, // Index = 66, Token = 1 + 2516, // Index = 66, Token = 2 + 3522, // Index = 66, Token = 3 + 4529, // Index = 66, Token = 4 + 5535, // Index = 66, Token = 5 + 6542, // Index = 66, Token = 6 + 7548, // Index = 66, Token = 7 + -503, // Index = 66, Token = 8 + -1509, // Index = 66, Token = 9 + -2516, // Index = 66, Token = 10 + -3522, // Index = 66, Token = 11 + -4529, // Index = 66, Token = 12 + -5535, // Index = 66, Token = 13 + -6542, // Index = 66, Token = 14 + -7548, // Index = 66, Token = 15 + 553, // Index = 67, Token = 0 + 1660, // Index = 67, Token = 1 + 2767, // Index = 67, Token = 2 + 3874, // Index = 67, Token = 3 + 4981, // Index = 67, Token = 4 + 6088, // Index = 67, Token = 5 + 7195, // Index = 67, Token = 6 + 8302, // Index = 67, Token = 7 + -553, // Index = 67, Token = 8 + -1660, // Index = 67, Token = 9 + -2767, // Index = 67, Token = 10 + -3874, // Index = 67, Token = 11 + -4981, // Index = 67, Token = 12 + -6088, // Index = 67, Token = 13 + -7195, // Index = 67, Token = 14 + -8302, // Index = 67, Token = 15 + 608, // Index = 68, Token = 0 + 1825, // Index = 68, Token = 1 + 3043, // Index = 68, Token = 2 + 4260, // Index = 68, Token = 3 + 5479, // Index = 68, Token = 4 + 6696, // Index = 68, Token = 5 + 7914, // Index = 68, Token = 6 + 9131, // Index = 68, Token = 7 + -608, // Index = 68, Token = 8 + -1825, // Index = 68, Token = 9 + -3043, // Index = 68, Token = 10 + -4260, // Index = 68, Token = 11 + -5479, // Index = 68, Token = 12 + -6696, // Index = 68, Token = 13 + -7914, // Index = 68, Token = 14 + -9131, // Index = 68, Token = 15 + 669, // Index = 69, Token = 0 + 2008, // Index = 69, Token = 1 + 3348, // Index = 69, Token = 2 + 4687, // Index = 69, Token = 3 + 6027, // Index = 69, Token = 4 + 7366, // Index = 69, Token = 5 + 8706, // Index = 69, Token = 6 + 10045, // Index = 69, Token = 7 + -669, // Index = 69, Token = 8 + -2008, // Index = 69, Token = 9 + -3348, // Index = 69, Token = 10 + -4687, // Index = 69, Token = 11 + -6027, // Index = 69, Token = 12 + -7366, // Index = 69, Token = 13 + -8706, // Index = 69, Token = 14 + -10045, // Index = 69, Token = 15 + 736, // Index = 70, Token = 0 + 2209, // Index = 70, Token = 1 + 3683, // Index = 70, Token = 2 + 5156, // Index = 70, Token = 3 + 6630, // Index = 70, Token = 4 + 8103, // Index = 70, Token = 5 + 9577, // Index = 70, Token = 6 + 11050, // Index = 70, Token = 7 + -736, // Index = 70, Token = 8 + -2209, // Index = 70, Token = 9 + -3683, // Index = 70, Token = 10 + -5156, // Index = 70, Token = 11 + -6630, // Index = 70, Token = 12 + -8103, // Index = 70, Token = 13 + -9577, // Index = 70, Token = 14 + -11050, // Index = 70, Token = 15 + 810, // Index = 71, Token = 0 + 2431, // Index = 71, Token = 1 + 4052, // Index = 71, Token = 2 + 5673, // Index = 71, Token = 3 + 7294, // Index = 71, Token = 4 + 8915, // Index = 71, Token = 5 + 10536, // Index = 71, Token = 6 + 12157, // Index = 71, Token = 7 + -810, // Index = 71, Token = 8 + -2431, // Index = 71, Token = 9 + -4052, // Index = 71, Token = 10 + -5673, // Index = 71, Token = 11 + -7294, // Index = 71, Token = 12 + -8915, // Index = 71, Token = 13 + -10536, // Index = 71, Token = 14 + -12157, // Index = 71, Token = 15 + 891, // Index = 72, Token = 0 + 2674, // Index = 72, Token = 1 + 4457, // Index = 72, Token = 2 + 6240, // Index = 72, Token = 3 + 8023, // Index = 72, Token = 4 + 9806, // Index = 72, Token = 5 + 11589, // Index = 72, Token = 6 + 13372, // Index = 72, Token = 7 + -891, // Index = 72, Token = 8 + -2674, // Index = 72, Token = 9 + -4457, // Index = 72, Token = 10 + -6240, // Index = 72, Token = 11 + -8023, // Index = 72, Token = 12 + -9806, // Index = 72, Token = 13 + -11589, // Index = 72, Token = 14 + -13372, // Index = 72, Token = 15 + 980, // Index = 73, Token = 0 + 2941, // Index = 73, Token = 1 + 4902, // Index = 73, Token = 2 + 6863, // Index = 73, Token = 3 + 8825, // Index = 73, Token = 4 + 10786, // Index = 73, Token = 5 + 12747, // Index = 73, Token = 6 + 14708, // Index = 73, Token = 7 + -980, // Index = 73, Token = 8 + -2941, // Index = 73, Token = 9 + -4902, // Index = 73, Token = 10 + -6863, // Index = 73, Token = 11 + -8825, // Index = 73, Token = 12 + -10786, // Index = 73, Token = 13 + -12747, // Index = 73, Token = 14 + -14708, // Index = 73, Token = 15 + 1078, // Index = 74, Token = 0 + 3235, // Index = 74, Token = 1 + 5393, // Index = 74, Token = 2 + 7550, // Index = 74, Token = 3 + 9708, // Index = 74, Token = 4 + 11865, // Index = 74, Token = 5 + 14023, // Index = 74, Token = 6 + 16180, // Index = 74, Token = 7 + -1078, // Index = 74, Token = 8 + -3235, // Index = 74, Token = 9 + -5393, // Index = 74, Token = 10 + -7550, // Index = 74, Token = 11 + -9708, // Index = 74, Token = 12 + -11865, // Index = 74, Token = 13 + -14023, // Index = 74, Token = 14 + -16180, // Index = 74, Token = 15 + 1186, // Index = 75, Token = 0 + 3559, // Index = 75, Token = 1 + 5932, // Index = 75, Token = 2 + 8305, // Index = 75, Token = 3 + 10679, // Index = 75, Token = 4 + 13052, // Index = 75, Token = 5 + 15425, // Index = 75, Token = 6 + 17798, // Index = 75, Token = 7 + -1186, // Index = 75, Token = 8 + -3559, // Index = 75, Token = 9 + -5932, // Index = 75, Token = 10 + -8305, // Index = 75, Token = 11 + -10679, // Index = 75, Token = 12 + -13052, // Index = 75, Token = 13 + -15425, // Index = 75, Token = 14 + -17798, // Index = 75, Token = 15 + 1305, // Index = 76, Token = 0 + 3915, // Index = 76, Token = 1 + 6526, // Index = 76, Token = 2 + 9136, // Index = 76, Token = 3 + 11747, // Index = 76, Token = 4 + 14357, // Index = 76, Token = 5 + 16968, // Index = 76, Token = 6 + 19578, // Index = 76, Token = 7 + -1305, // Index = 76, Token = 8 + -3915, // Index = 76, Token = 9 + -6526, // Index = 76, Token = 10 + -9136, // Index = 76, Token = 11 + -11747, // Index = 76, Token = 12 + -14357, // Index = 76, Token = 13 + -16968, // Index = 76, Token = 14 + -19578, // Index = 76, Token = 15 + 1435, // Index = 77, Token = 0 + 4306, // Index = 77, Token = 1 + 7178, // Index = 77, Token = 2 + 10049, // Index = 77, Token = 3 + 12922, // Index = 77, Token = 4 + 15793, // Index = 77, Token = 5 + 18665, // Index = 77, Token = 6 + 21536, // Index = 77, Token = 7 + -1435, // Index = 77, Token = 8 + -4306, // Index = 77, Token = 9 + -7178, // Index = 77, Token = 10 + -10049, // Index = 77, Token = 11 + -12922, // Index = 77, Token = 12 + -15793, // Index = 77, Token = 13 + -18665, // Index = 77, Token = 14 + -21536, // Index = 77, Token = 15 + 1579, // Index = 78, Token = 0 + 4737, // Index = 78, Token = 1 + 7896, // Index = 78, Token = 2 + 11054, // Index = 78, Token = 3 + 14214, // Index = 78, Token = 4 + 17372, // Index = 78, Token = 5 + 20531, // Index = 78, Token = 6 + 23689, // Index = 78, Token = 7 + -1579, // Index = 78, Token = 8 + -4737, // Index = 78, Token = 9 + -7896, // Index = 78, Token = 10 + -11054, // Index = 78, Token = 11 + -14214, // Index = 78, Token = 12 + -17372, // Index = 78, Token = 13 + -20531, // Index = 78, Token = 14 + -23689, // Index = 78, Token = 15 + 1737, // Index = 79, Token = 0 + 5211, // Index = 79, Token = 1 + 8686, // Index = 79, Token = 2 + 12160, // Index = 79, Token = 3 + 15636, // Index = 79, Token = 4 + 19110, // Index = 79, Token = 5 + 22585, // Index = 79, Token = 6 + 26059, // Index = 79, Token = 7 + -1737, // Index = 79, Token = 8 + -5211, // Index = 79, Token = 9 + -8686, // Index = 79, Token = 10 + -12160, // Index = 79, Token = 11 + -15636, // Index = 79, Token = 12 + -19110, // Index = 79, Token = 13 + -22585, // Index = 79, Token = 14 + -26059, // Index = 79, Token = 15 + 1911, // Index = 80, Token = 0 + 5733, // Index = 80, Token = 1 + 9555, // Index = 80, Token = 2 + 13377, // Index = 80, Token = 3 + 17200, // Index = 80, Token = 4 + 21022, // Index = 80, Token = 5 + 24844, // Index = 80, Token = 6 + 28666, // Index = 80, Token = 7 + -1911, // Index = 80, Token = 8 + -5733, // Index = 80, Token = 9 + -9555, // Index = 80, Token = 10 + -13377, // Index = 80, Token = 11 + -17200, // Index = 80, Token = 12 + -21022, // Index = 80, Token = 13 + -24844, // Index = 80, Token = 14 + -28666, // Index = 80, Token = 15 + 2102, // Index = 81, Token = 0 + 6306, // Index = 81, Token = 1 + 10511, // Index = 81, Token = 2 + 14715, // Index = 81, Token = 3 + 18920, // Index = 81, Token = 4 + 23124, // Index = 81, Token = 5 + 27329, // Index = 81, Token = 6 + 31533, // Index = 81, Token = 7 + -2102, // Index = 81, Token = 8 + -6306, // Index = 81, Token = 9 + -10511, // Index = 81, Token = 10 + -14715, // Index = 81, Token = 11 + -18920, // Index = 81, Token = 12 + -23124, // Index = 81, Token = 13 + -27329, // Index = 81, Token = 14 + -31533, // Index = 81, Token = 15 + 2312, // Index = 82, Token = 0 + 6937, // Index = 82, Token = 1 + 11562, // Index = 82, Token = 2 + 16187, // Index = 82, Token = 3 + 20812, // Index = 82, Token = 4 + 25437, // Index = 82, Token = 5 + 30062, // Index = 82, Token = 6 + 34687, // Index = 82, Token = 7 + -2312, // Index = 82, Token = 8 + -6937, // Index = 82, Token = 9 + -11562, // Index = 82, Token = 10 + -16187, // Index = 82, Token = 11 + -20812, // Index = 82, Token = 12 + -25437, // Index = 82, Token = 13 + -30062, // Index = 82, Token = 14 + -34687, // Index = 82, Token = 15 + 2543, // Index = 83, Token = 0 + 7630, // Index = 83, Token = 1 + 12718, // Index = 83, Token = 2 + 17805, // Index = 83, Token = 3 + 22893, // Index = 83, Token = 4 + 27980, // Index = 83, Token = 5 + 33068, // Index = 83, Token = 6 + 38155, // Index = 83, Token = 7 + -2543, // Index = 83, Token = 8 + -7630, // Index = 83, Token = 9 + -12718, // Index = 83, Token = 10 + -17805, // Index = 83, Token = 11 + -22893, // Index = 83, Token = 12 + -27980, // Index = 83, Token = 13 + -33068, // Index = 83, Token = 14 + -38155, // Index = 83, Token = 15 + 2798, // Index = 84, Token = 0 + 8394, // Index = 84, Token = 1 + 13990, // Index = 84, Token = 2 + 19586, // Index = 84, Token = 3 + 25183, // Index = 84, Token = 4 + 30779, // Index = 84, Token = 5 + 36375, // Index = 84, Token = 6 + 41971, // Index = 84, Token = 7 + -2798, // Index = 84, Token = 8 + -8394, // Index = 84, Token = 9 + -13990, // Index = 84, Token = 10 + -19586, // Index = 84, Token = 11 + -25183, // Index = 84, Token = 12 + -30779, // Index = 84, Token = 13 + -36375, // Index = 84, Token = 14 + -41971, // Index = 84, Token = 15 + 3077, // Index = 85, Token = 0 + 9232, // Index = 85, Token = 1 + 15388, // Index = 85, Token = 2 + 21543, // Index = 85, Token = 3 + 27700, // Index = 85, Token = 4 + 33855, // Index = 85, Token = 5 + 40011, // Index = 85, Token = 6 + 46166, // Index = 85, Token = 7 + -3077, // Index = 85, Token = 8 + -9232, // Index = 85, Token = 9 + -15388, // Index = 85, Token = 10 + -21543, // Index = 85, Token = 11 + -27700, // Index = 85, Token = 12 + -33855, // Index = 85, Token = 13 + -40011, // Index = 85, Token = 14 + -46166, // Index = 85, Token = 15 + 3385, // Index = 86, Token = 0 + 10156, // Index = 86, Token = 1 + 16928, // Index = 86, Token = 2 + 23699, // Index = 86, Token = 3 + 30471, // Index = 86, Token = 4 + 37242, // Index = 86, Token = 5 + 44014, // Index = 86, Token = 6 + 50785, // Index = 86, Token = 7 + -3385, // Index = 86, Token = 8 + -10156, // Index = 86, Token = 9 + -16928, // Index = 86, Token = 10 + -23699, // Index = 86, Token = 11 + -30471, // Index = 86, Token = 12 + -37242, // Index = 86, Token = 13 + -44014, // Index = 86, Token = 14 + -50785, // Index = 86, Token = 15 + 3724, // Index = 87, Token = 0 + 11172, // Index = 87, Token = 1 + 18621, // Index = 87, Token = 2 + 26069, // Index = 87, Token = 3 + 33518, // Index = 87, Token = 4 + 40966, // Index = 87, Token = 5 + 48415, // Index = 87, Token = 6 + 55863, // Index = 87, Token = 7 + -3724, // Index = 87, Token = 8 + -11172, // Index = 87, Token = 9 + -18621, // Index = 87, Token = 10 + -26069, // Index = 87, Token = 11 + -33518, // Index = 87, Token = 12 + -40966, // Index = 87, Token = 13 + -48415, // Index = 87, Token = 14 + -55863, // Index = 87, Token = 15 + 4095, // Index = 88, Token = 0 + 12286, // Index = 88, Token = 1 + 20478, // Index = 88, Token = 2 + 28669, // Index = 88, Token = 3 + 36862, // Index = 88, Token = 4 + 45053, // Index = 88, Token = 5 + 53245, // Index = 88, Token = 6 + 61436, // Index = 88, Token = 7 + -4095, // Index = 88, Token = 8 + -12286, // Index = 88, Token = 9 + -20478, // Index = 88, Token = 10 + -28669, // Index = 88, Token = 11 + -36862, // Index = 88, Token = 12 + -45053, // Index = 88, Token = 13 + -53245, // Index = 88, Token = 14 + -61436 // Index = 88, Token = 15 +}; + diff --git a/REDALERT/DYNAVEC.CPP b/REDALERT/DYNAVEC.CPP new file mode 100644 index 000000000..6f8d677d3 --- /dev/null +++ b/REDALERT/DYNAVEC.CPP @@ -0,0 +1,300 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DYNAVEC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + * * + * Project Name : Red Alert * + * * + * File Name : DYNAVEC.CPP * + * * + * Programmer : Bill R Randolph * + * * + * Start Date : 09/18/96 * + * * + * Last Update : September 18, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Delete -- Deletes specified index from vector. * + * DynamicVectorClass::Delete -- Remove specified object from vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor * + * DynamicVectorClass::ID -- Find matching value in dynamic vector. * + * DynamicVectorClass::Resize -- Changes size of a dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) +#include "function.h" +#include "vector.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +//#include +#include + + + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previously allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previously and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * * + * This routine will add the specified element to the head of the vector. If necessary, * + * the vector will be expanded accordingly. * + * * + * INPUT: object -- Reference to the object to add to the head of this vector. * + * * + * OUTPUT: bool; Was the object added without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + return(Delete(ID(object))); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if ((unsigned)index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +/************************** end of dynavec.cpp *****************************/ +#endif \ No newline at end of file diff --git a/REDALERT/EDIT.CPP b/REDALERT/EDIT.CPP new file mode 100644 index 000000000..5de95fd5b --- /dev/null +++ b/REDALERT/EDIT.CPP @@ -0,0 +1,472 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EDIT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EditClass::Action -- Handles input events. * + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * EditClass::Draw_Text -- Draws the edit gadget text. * + * EditClass::EditClass -- Normal constructor for edit class object. * + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * EditClass::EditClass -- Normal constructor for edit class object. * + * * + * This is the normal constructor used to create an edit object. * + * * + * INPUT: id -- The ID number for this edit object. This is the ID number that will be * + * returned by the Input() function when the key is pressed if this * + * gadget has the keyboard input focus. * + * * + * text -- Reference to the text buffer that the edit gadget will modify as keyboard * + * input is processed. The value that this buffer contains is the default * + * text displayed. * + * * + * maxlen-- The maximum size of the text buffer specified. This length INCLUDES the * + * trailing null character so a simple sizeof() function call can be used. * + * * + * flags -- These are the text print control flags. It is used to control how the * + * text looks in the edit box. Use the normal TPF_??? flags. * + * * + * x,y -- The pixel coordinates of the upper left corner of the edit gadget area. * + * * + * w,h -- The pixel dimensions of the edit box. If either of these are no provided, * + * or set to -1, then the dimension is determined from the string itself. * + * * + * sytle -- This style flag parameter control what kind of characters are allowed in * + * the edit box. The initial string in the text buffer may contain illegal * + * characters, but they are NOT removed regardless of this parameter. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/05/1995 MML : Created. * + * 01/21/1995 JLB : Modified. * + *=============================================================================================*/ +EditClass::EditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + ControlClass (id, x, y, w, h, LEFTPRESS), String(text) +{ + TextFlags = flags & ~(TPF_CENTER); + EditFlags = style; + Set_Text(text, max_len); + Color = GadgetClass::Get_Color_Scheme(); + + if (w == -1 || h == -1) { + // PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + + if (h == -1) { + Height = FontHeight+1; + } + if (w == -1) { + if (strlen(String) > 0) { + Width = String_Pixel_Width(String) + 6; + } else { + Width = ((Char_Pixel_Width('X')+FontXSpacing) * (MaxLength+1)) + 2; + } + } + } + + IsReadOnly = 0; +} + + +/*********************************************************************************************** + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * * + * This default destructor removes the focus setting if it currently has it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/24/1995 JLB : Created. * + *=============================================================================================*/ +EditClass::~EditClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*********************************************************************************************** + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * * + * Use this routine to change the text that this edit gadget refers to. * + * * + * INPUT: text -- Reference to the character array that this edit gadget will be * + * modifying. * + * max_len -- The maximum size of the buffer that will be modified. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Set_Text(char * text, int max_len) +{ + String = text; + MaxLength = max_len-1; + Length = strlen(String); + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * * + * This routine will render the edit box. This will show the box outline as well as any * + * text it may contain. * + * * + * INPUT: forced -- Should the edit box be drawn even if it thinks it doesn't have to? * + * * + * OUTPUT: Was the edit box drawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Background(); + + /* + ** Display the text. + */ + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * EditClass::Action -- Handles input events. * + * * + * This routine will handle all mouse and keyboard events directed at this edit box * + * gadget. For keyboard events, this will insert the characters into the edit box. * + * * + * INPUT: flags -- The event flag that triggered this function call. * + * * + * key -- Reference to the keyboard/mouse event that triggered this function call. * + * * + * OUTPUT: Should the list be processed further? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Action(unsigned flags, KeyNumType & key) +{ + /* + ** If this is a read-only edit box, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** If the left mouse button is pressed over this gadget, then set the focus to + ** this gadget. The event flag is cleared so that no button ID number is returned. + */ + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + /* + ** Handle keyboard events here. Normally, the key is added to the string, but if the + ** RETURN key is pressed, then the button ID number is returned from the Input() + ** function. + */ + if ((flags & KEYBOARD) && Has_Focus()) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + key = (KeyNumType)(key & ~WWKEY_VK_BIT); + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + /* + ** Filter out all special keys except return and backspace + */ if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) + || key == KN_RETURN || key == KN_BACKSPACE) { + + + + if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * * + * This routine will redraw the edit gadget background. The overlaying text is handled by * + * a different routine. The mouse is guaranteed to be hidden when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Background(void) +{ + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); +} + + +/*********************************************************************************************** + * EditClass::Draw_Text -- Draws the edit gadget text. * + * * + * This routine is called when the edit gadget text needs to be drawn. The background has * + * already been drawn by the time this function is called. The mouse is guaranteed to be * + * hidden as well. * + * * + * INPUT: text -- The text to draw in the edit gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Text(char const * text) +{ + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && (int)strlen(text) < MaxLength && + ((int)String_Pixel_Width(text) + (int)String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + + +/*********************************************************************************************** + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * * + * This is the gruntwork routine that processes keyboard input to the edit gadget. This * + * routine will be called when keyboard input has been detected and this gadget has the * + * current focus. * + * * + * INPUT: ascii -- The ASCII key code that was fetched from the keyboard buffer. * + * * + * OUTPUT: bool; Should this keyboard input NOT cause the gadget ID number to be returned * + * from the controlling Input() routine? Typically, the return value would be * + * true unless the focus is lost due to the key being pressed. * + * * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool EditClass::Handle_Key(KeyASCIIType ascii) +{ + switch (ascii) { + /* + ** Handle the special case of a non-keyboard event. It is possible that this + ** key code might be passed to this routine if this routine has been overridden + ** and the key event was consumed. + */ + case 0: + break; + + /* + ** If the return key is pressed, then remove the focus from this edit + ** gadget but otherwise let the normal gadget processing proceed. This + ** causes the gadget ID number to be returned from the Input() function + ** so that the controlling program will know that the text can be + ** processed. + */ + case KA_RETURN: + Clear_Focus(); + return(false); + + /* + ** When the BACKSPACE key is pressed, remove the last character in the edit string. + */ + case KA_BACKSPACE: + if (Length) { + Length--; + String[Length] = '\0'; + Flag_To_Redraw(); + } + break; + + /* + ** If the keyboard event was not a recognized special key, then examine to see + ** if it can legally be added to the edit string and do so if possible. + */ + default: + + /* + ** Don't add a character if the length is greater than edit width. + */ + if (((int)String_Pixel_Width(String) + (int)Char_Pixel_Width(ascii) ) >= (Width-2)) { + break; + } + + /* + ** Don't add a character if the length is already at maximum. + */ + if (Length >= MaxLength) break; + + /* + ** Invisible characters are never added to the string. This is + ** especially true for spaces at the beginning of the string. + */ + if (!isgraph(ascii) && ascii != ' ') break; + if (ascii == ' ' && Length == 0) break; + + /* + ** If this is an upper case only edit gadget, then force the alphabetic + ** character to upper case. + */ + if ((EditFlags & UPPERCASE) && isalpha(ascii)) { + ascii = (KeyASCIIType)toupper(ascii); + } + + if ((!(EditFlags & NUMERIC) || !isdigit(ascii)) && + (!(EditFlags & ALPHA) || !isalpha(ascii)) && + (!(EditFlags & MISC) || isalnum(ascii)) && + ascii != ' ') { + break; + } + + /* + ** The character passed all legality checks, so add it to the edit string + ** and flag this gadget to be redrawn. The manual flag to redraw is needed + ** because the event flag has been cleared. This prevents the gadget's ID + ** number from being returned just because the gadget has been edited. + */ + String[Length++] = ascii; + String[Length] = '\0'; + Flag_To_Redraw(); + break; + } + return(true); +} + + +void EditClass::Set_Focus(void) +{ + Length = 0; + if (String) { + Length = strlen(String); + } + ControlClass::Set_Focus(); +} diff --git a/REDALERT/EDIT.H b/REDALERT/EDIT.H new file mode 100644 index 000000000..92168d96d --- /dev/null +++ b/REDALERT/EDIT.H @@ -0,0 +1,120 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EDIT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EDIT_H +#define EDIT_H + +class EditClass : public ControlClass +{ + public: + typedef enum EditStyle { + ALPHA =0x0001, // Edit accepts alphabetic characters. + NUMERIC =0x0002, // Edit accepts numbers. + MISC =0x0004, // Edit accepts misc graphic characters. + UPPERCASE =0x0008, // Force to upper case. + ALPHANUMERIC=(int)ALPHA|(int)NUMERIC|(int)MISC + } EditStyle; + + EditClass (int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1, EditStyle style=ALPHANUMERIC); + virtual ~EditClass(void); + + virtual void Set_Focus(void); + virtual int Draw_Me(int forced); + virtual void Set_Text(char * text, int max_len); + virtual char * Get_Text(void) {return(String);}; + void Set_Color (RemapControlType * color) { Color = color; } + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + + /* + ** These are the text size and style flags to be used when displaying the text + ** of the edit gadget. + */ + TextPrintType TextFlags; + + /* + ** Input flags that control what characters are allowed in the string. + */ + EditStyle EditFlags; + + /* + ** Pointer to text staging buffer and the maximum length of the string it + ** can contain. + */ + char *String; + int MaxLength; + + /* + ** This is the current length of the string. This length will never exceed the + ** MaxLength allowed. + */ + int Length; + + /* + ** This is the desired color of the edit control. + */ + RemapControlType * Color; + + virtual int Action (unsigned flags, KeyNumType &key); + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + virtual bool Handle_Key(KeyASCIIType ascii); + + private: + int IsReadOnly; +}; + +//PG inline EditClass::EditStyle operator |(EditClass::EditStyle, EditClass::EditStyle); +//PG inline EditClass::EditStyle operator &(EditClass::EditStyle, EditClass::EditStyle); +//PG inline EditClass::EditStyle operator ~(EditClass::EditStyle); + +inline EditClass::EditStyle operator|(EditClass::EditStyle a, EditClass::EditStyle b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline EditClass::EditStyle operator&(EditClass::EditStyle a, EditClass::EditStyle b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline EditClass::EditStyle operator~(EditClass::EditStyle a) +{ + return static_cast(~static_cast(a)); +} + + +#endif diff --git a/REDALERT/EGOS.CPP b/REDALERT/EGOS.CPP new file mode 100644 index 000000000..7e298a661 --- /dev/null +++ b/REDALERT/EGOS.CPP @@ -0,0 +1,986 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/EGOS.CPP 2 3/10/97 3:19p Steve_tall $ */ +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : EGOS.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if(0) +//PG_TO_FIX +#include "function.h" + +/* +** List of Ego Class instances +** There will be one instance for each line of text. +*/ +DynamicVectorClass EgoList; + +/* +** Number of slideshow pictures +*/ + +#define NUM_SLIDES 17 + +/* +** Length of time frame is displayed for +*/ +#define FRAME_DELAY 150 + +/* +** Number of frames that palete fade occurs over +*/ +#define FADE_DELAY 37 + +/* +** Names of slideshow pictures to play behind the text +*/ +char SlideNames[NUM_SLIDES][13]={ + + "aftr_hi.pcx", + "aly1.pcx", + "apc_hi.pcx", + "aphi0049.pcx", + "bnhi0020.pcx", + "dchi0040.pcx", + "frhi0166.pcx", + "lab.pcx", + "landsbrg.pcx", + "mahi0107.pcx", + "mig_hi.pcx", + "mtfacthi.pcx", + "needle.pcx", + "sov2.pcx", + "spy.pcx", + "stalin.pcx", + "tent.pcx" +}; + +/* +** Names of low res slideshow pictures to play behind the text +*/ +char LoresSlideNames[NUM_SLIDES][13]={ + "malo0107.cps", + "mig_lo.cps", + "mtfactlo.cps", + "needl-lo.cps", + "sov2-lo.cps", + "spy-lo.cps", + "staln-lo.cps", + "tent-lo.cps", + "aftr_lo.cps", + "aly1-lo.cps", + "apc_lo.cps", + "aplo0049.cps", + "bnlo0020.cps", + "dclo0040.cps", + "frlo0166.cps", + "lab-lo.cps", + "lands-lo.cps" +}; + +/* +** Array of all the palettes required for the slides +*/ +char SlidePals[NUM_SLIDES][256*3]; + +/* +** Array of graphic buffers containing the slides +*/ +GraphicBufferClass *SlideBuffers[NUM_SLIDES]; + +/* +** Original copy of slide (pref in video mem) that we use to undraw the text +*/ +GraphicBufferClass *BackgroundPage; + +/* +** This palette contains both the font palette entries and the slide +** palette. +*/ +PaletteClass ComboPalette; + +/* +** Ptr to the combo palette. +*/ +unsigned char *ComboPalPtr; + +/* +** Lookup table. If an entry is non-zero then it should be faded in/out when the slide changes. +*/ +char PaletteLUT[256]; + +/* +** Height of the strips that are blitted from the slides to the backgound and hid pages. +** We blit in several strips over several frames so as not to impact on the frame rate. +*/ +#define CHUNK_HEIGHT RESFACTOR * 50 + + + +#ifndef WIN32 +extern void Vsync(void); +#pragma aux Vsync modify [edx ebx eax] = \ + "mov edx,03DAh" \ + "mov ebx,[VertBlank]" \ + "and bl,001h" \ + "shl bl,3" \ + "in_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "je in_vbi" \ + "out_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "jne out_vbi" +#endif //WIN32 + + + +/*********************************************************************************************** + * EC::EgoClass -- EgoClass constructor * + * * + * * + * * + * INPUT: x position of text * + * y position of text * + * ptr to text string * + * flags to print text with * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:53PM ST : Created * + *=============================================================================================*/ +EgoClass::EgoClass (int x, int y, char *text, TextPrintType flags) +{ + XPos = x; + YPos = y; + Flags= flags; + Text = new char [strlen (text)+1]; + strcpy (Text, text); +} + + +/*********************************************************************************************** + * EC::~EgoClass -- EgoClass destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:54PM ST : Created * + *=============================================================================================*/ +EgoClass::~EgoClass(void) +{ + delete [] Text; +} + + +/*********************************************************************************************** + * EC::Scroll -- Apply the given distance to the y position of the text. * + * A positive distance scrolls up. * + * * + * * + * INPUT: distance in pixels to scroll up * + * * + * OUTPUT: true if text scrolled beyond the top of the screen * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:55PM ST : Created * + *=============================================================================================*/ +bool EgoClass::Scroll(int distance) +{ + YPos -= distance; + if (YPos < -20) { + return (true); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * EC::Render -- Draws the text to the logic page * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:57PM ST : Created * + *=============================================================================================*/ +void EgoClass::Render (void) +{ + if (YPos < LogicPage->Get_Height() && YPos > -16) { + Fancy_Text_Print(Text, XPos, YPos, GadgetClass::Get_Color_Scheme(), TBLACK, Flags); + } +} + + + +/*********************************************************************************************** + * EC::Wipe -- Wipes the previously rendered text by blitting a rectangle from the given * + * background screen. * + * * + * * + * INPUT: ptr to screen containing original background * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:58PM ST : Created * + *=============================================================================================*/ +void EgoClass::Wipe (GraphicBufferClass *background) +{ + int width = String_Pixel_Width (Text); + int x = XPos; + + if (Flags & TPF_RIGHT) { + x -= width; + }else{ + if (Flags & TPF_CENTER){ + x -= width/2; + } + } + + background->Blit(*LogicPage, x-1, YPos, x-1, YPos, width+2, 7 * RESFACTOR +1, false); +} + + + +/*********************************************************************************************** + * Set_Pal -- Low level palette set * + * * + * * + * * + * INPUT: ptr to palette * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:59PM ST : Created * + *=============================================================================================*/ +void Set_Pal(char *palette) +{ +//#ifndef WIN32 + //Vsync(); + //unsigned char *rgbptr = (unsigned char *) palette; + //outportb(0x03C8, 0); //Start from color 0 + + //for (int index = 0; index < 256; index++) { + // outrgb(rgbptr[index*3], rgbptr[index*3+1], rgbptr[index*3+2]); + //} +//#else //WIN32 + + Set_Palette((void*)palette); +//#endif +} + + +/*********************************************************************************************** + * Slide_Show -- Handles the blitting and fading of the background pictures. * + * * + * The picture frame number is used to trigger blitting and fading events * + * * + * * + * INPUT: picture number * + * frame * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:16AM ST : Created * + *=============================================================================================*/ +void Slide_Show (int slide, int frame) +{ + + /* + ** Temprary storage to save CCPalette to + */ + char save_palette[256*3]; + + + if (frame >= 1 && frame <=4){ + /* + ** Blit in a quarter of the new frame to the background page. + */ + SlideBuffers[slide]->Blit (*BackgroundPage, 0, (frame-1) * CHUNK_HEIGHT, + 0, (frame-1) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame >= 5 && frame <=8 ){ + /* + ** Blit in a quarter of the new frame to the hid page. + */ + BackgroundPage->Blit (HidPage, 0, (frame-5) * CHUNK_HEIGHT, + 0, (frame-5) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame ==9){ + /* + ** Create the combo palette from the font entries and the picture entries. + */ + for (int index = 0 ; index < 256 ; index++ ){ + if (PaletteLUT[index]) { + ComboPalPtr[index*3] = SlidePals[slide][index*3]; + ComboPalPtr[index*3+1] = SlidePals[slide][index*3+1]; + ComboPalPtr[index*3+2] = SlidePals[slide][index*3+2]; + } + } + return; + } + + + if (frame >10 && frame < FADE_DELAY+10){ + /* + ** Fade up the picture in the background. The text colors never fade. + */ + memcpy (save_palette, CCPalette, sizeof(save_palette)); + //CCPalette.Partial_Adjust (MIN (6*(frame-5), 255), ComboPalette, PaletteLUT); + CCPalette.Partial_Adjust (MIN ((255/FADE_DELAY)*(frame-10), 255), ComboPalette, PaletteLUT); + Set_Pal ( (char *) &CCPalette); + if (frame != 9+FADE_DELAY){ + memcpy (CCPalette, save_palette, sizeof(save_palette)); + }else{ + memcpy (CCPalette, CurrentPalette, sizeof (CCPalette)); + } + return; + } + + + if (frame >FRAME_DELAY && frame < FRAME_DELAY+FADE_DELAY){ + /* + ** Fade down the picture in the background. The text colors never fade. + */ + memcpy (save_palette, CCPalette, sizeof(save_palette)); + CCPalette.Partial_Adjust (MIN ((255/FADE_DELAY)*(frame-FRAME_DELAY), 255), PaletteLUT); + if (frame != FRAME_DELAY+FADE_DELAY-1){ + Set_Pal ( (char *) &CCPalette); + memcpy (CCPalette, save_palette, sizeof(save_palette)); + }else{ + /* + ** If this is the last fade down frame then zero the picture palette entries. + */ + unsigned char *ccpalptr = (unsigned char*)CCPalette; + for (int index = 0 ; index < 256 ; index++){ + if (PaletteLUT[index]){ + ccpalptr[index*3] = 0; + ccpalptr[index*3+1] = 0; + ccpalptr[index*3+2] = 0; + } + } + Set_Pal ( (char *) &CCPalette); + } + + } + +} + + + + +/*********************************************************************************************** + * Show_Who_Was_Responsible -- Main function to print the credits. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:20AM ST : Created * + *=============================================================================================*/ +void Show_Who_Was_Responsible (void) +{ + + int i; + int key; + + /* + ** Deault speed of credits scolling. This is the frame delay between pixel scrolls. + */ + static int speed = 3; + + /* + ** In DOS we need to scroll slower so we have a bool that lets us do it every other time + */ +#ifndef WIN32 + bool scroll_now = false; +#endif //WIN32 + + /* + ** Read in the credits file to be displayed + ** + ** Lines of text in CREDITS.TXT are treated as follows.... + ** + ** If the text starts and ends to the left of column 40 then text will be right justified. + ** If the text starts before column 40 and ends after it then it will be centered. + ** If the text starts after column 40 it will be right justified. + */ + CCFileClass creditsfile ("credits.txt"); + if ( !creditsfile.Is_Available()) return; + char *credits = new char [creditsfile.Size()+1]; + creditsfile.Read (credits, creditsfile.Size()); + + /* + ** Initialise the text printing system. + */ + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_GREEN]); + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + /* + ** Miscellaneous stuff for parsing the credits text file. + */ + int length = creditsfile.Size(); + int line = 0; + int column = 0; + char *cptr = credits; + char ch, lastchar, oldchar; + char *strstart, *strparse; + bool gotendstr; + int startcolumn, endcolumn, x; + int y=SeenBuff.Get_Height()+2; + EgoClass *ego; + TextPrintType flags; + + + /* + ** Search through the text file and extract the strings, using each string to create + ** a new EgoClass + */ + do { + /* + ** Search for text + */ + ch = *cptr++; + length --; + + /* + ** Look for a non whitespace character. + */ + switch ( ch ){ + + case 13: + /* + ** Char was carriage return. Go on to the next line starting at column 0. + */ + line++; + column = 0; + break; + + + case 10: + /* + ** Ignore line feed. CR does both. + */ + break; + + /* + ** Space character. Just advance the cursor and move on. + */ + case 32: + column++; + break; + + /* + ** Tab char. Advance to the next tab column. Tabs are every 8 columns. + */ + case 9: + column += 8; + column &= 0xfffffff8; + break; + + default: + /* + ** Found new string. Work out where it ends so we know how to treat it. + */ + lastchar = ch; + strstart = cptr-1; + strparse = cptr-1; + endcolumn = startcolumn = column; + gotendstr = false; + + do { + ch = *strparse++; + switch ( ch ){ + case 9: + case 10: + case 13: + gotendstr = true; + break; + + case 32: + if (lastchar == 32) gotendstr = true; + endcolumn++; + break; + + default: + endcolumn++; + } + if (strparse >= cptr+length) gotendstr = true; + + lastchar = ch; + }while (!gotendstr); + + + if (strparse >= cptr+length) break; + + /* + ** Strip off any trailing space. + */ + if (*(strparse-2) == 32){ + strparse--; + endcolumn -= 2; + } + + + /* + ** If string straddles the center column then center it. + ** + ** If string is on the left hand side then right justify it. + ** + ** If string is on the right hand side then left justify it. + */ + flags = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW; //TPF_NOSHADOW; + + if (startcolumn <40 && endcolumn >40){ + flags = flags | TPF_CENTER; + x = SeenBuff.Get_Width() / 2; + }else{ + if (startcolumn <40){ + flags = flags | TPF_RIGHT; + x = endcolumn *SeenBuff.Get_Width() /80; + }else{ + x = startcolumn * SeenBuff.Get_Width() / 80; + } + } + + /* + ** Temporarily terminate the string. + */ + oldchar = *(strparse-1); + *(strparse-1) = 0; + + /* + ** Create the new class and add it to our list. + */ + ego = new EgoClass (x, y+ line *8 *RESFACTOR, strstart, flags); + + EgoList.Add (ego); + + /* + ** Restore the character that was lost when we added the terminator. + */ + *(strparse-1) = oldchar; + + /* + ** Fix up our outer loop parsing variables. + */ + cptr = strparse; + column += strparse - strstart; + length -= strparse - strstart-1; + + if (ch == 13) { + line++; + column = 0; + }else{ + if (ch == 9){ + column += 7; + column &= 0xfffffff8; + } + } + break; + } + + } while ( length>0 ); + + + /* + ** Work out which palette entries the font needs so we dont fade those colors. + */ + memset (PaletteLUT, 1, sizeof (PaletteLUT)); + int pcolor = PCOLOR_GREEN; + + for (int index = 0; index < 6; index++) { + PaletteLUT[ColorRemaps[pcolor].FontRemap[10+index]] =0; + } + //PaletteLUT[ColorRemaps[pcolor].BrightColor] = 0; + PaletteLUT[ColorRemaps[pcolor].Color] = 0; + PaletteLUT[ColorRemaps[pcolor].Shadow] = 0; + PaletteLUT[ColorRemaps[pcolor].Background] = 0; + PaletteLUT[ColorRemaps[pcolor].Corners] = 0; + PaletteLUT[ColorRemaps[pcolor].Highlight] = 0; + PaletteLUT[ColorRemaps[pcolor].Bright] = 0; + PaletteLUT[ColorRemaps[pcolor].Underline] = 0; + PaletteLUT[ColorRemaps[pcolor].Bar] = 0; + PaletteLUT[ColorRemaps[pcolor].Box] = 0; + + + /* + ** Stop the music. + */ + Theme.Stop(); + + /* + ** Fade to black. + */ + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + + /* + ** Load the reference palette for the font. + */ + //Load_Title_Page(true); +//#ifdef WIN32 +// Load_Picture("EGOPAL.CPS", SysMemPage, SysMemPage, CCPalette, BM_DEFAULT); +//#else //WIN32 +// Load_Picture("EGOPAL.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); +//#endif //WIN32 + + + CCFileClass("EGOPAL.PAL").Read(&CCPalette, sizeof(CCPalette)); + + /* + ** Copy the font palette entries into the combo palette. + */ + PaletteClass credit_palette; + ComboPalPtr = (unsigned char *) &ComboPalette; + unsigned char *creditpal_ptr = (unsigned char *) &credit_palette; + memcpy (ComboPalette, CCPalette, sizeof (ComboPalette)); + + for (index = 0 ; index < 256 ; index++ ){ + if (PaletteLUT[index]) { + ComboPalPtr[index*3] = 0; + ComboPalPtr[index*3+1] = 0; + ComboPalPtr[index*3+2] = 0; + } + } + + /* + ** Clear the Seen Page since we will not be blitting to all of it. + */ + SeenBuff.Clear(); + HidPage.Clear(); + + /* + ** Set the font palette. + */ + memcpy ( CCPalette, ComboPalette, sizeof (ComboPalette) ); + CCPalette.Set(); + + /* + ** Loop through and load up all the slideshow pictures + */ + for (index = 0 ; index < NUM_SLIDES ; index++){ +#ifdef WIN32 + SlideBuffers[index] = new GraphicBufferClass; + SlideBuffers[index]->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); + Load_Title_Screen(&SlideNames[index][0], SlideBuffers[index], (unsigned char*) &SlidePals[index][0]); +#else //WIN32 + SlideBuffers[index] = new GraphicBufferClass (SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + Load_Picture(&LoresSlideNames[index][0], *SlideBuffers[index], *SlideBuffers[index], (unsigned char *)&SlidePals[index][0], BM_DEFAULT); +#endif //WIN32 + } + + /* + ** Create a new graphic buffer to restore the background from. Initialise it to black so + ** we can start scrolling before the first slideshow picture is blitted. + */ +#ifdef WIN32 + BackgroundPage = new GraphicBufferClass; + BackgroundPage->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)(GBC_VIDEOMEM)); +#else //WIN32 + BackgroundPage = new GraphicBufferClass (SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL ); +#endif //WIN32 + + SeenBuff.Blit(*BackgroundPage); + + /* + ** Go away nasty keyboard. + */ + Keyboard->Clear(); + + Set_Logic_Page(HidPage); + + /* + ** Start any old song. + */ + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Queue_Song(THEME_CREDITS); + + /* + ** Init misc timing variables. + */ + int time = TickCount; + int frame = 0; + int picture_frame = 0; + int slide_number = 0; + + Hide_Mouse(); + + /* + ** Save the priority of this process so we can change it back later + */ + //DWORD process_priority = GetPriorityClass(GetCurrentProcess()); + + /* + ** Main scrolling loop. + ** Keeps going until all the EgoClass objects are deleted or esc is pressed. + */ + while ( EgoList.Count() ){ + + frame++; + + /* + ** Once we have been running for a few frames, and Windows has time to do its virtual + ** memory stuff, increase our priority level. + */ + //if (frame == 30){ + // SetPriorityClass (GetCurrentProcess() , HIGH_PRIORITY_CLASS); + //} + + /* + ** Update the slideshow frame and switch to the next picture if its time. + */ + picture_frame++; + + if (picture_frame > FRAME_DELAY+50){ + if (slide_number =0 ; i--){ + EgoList[i]->Wipe(BackgroundPage); + if ( EgoList[i]->Scroll(1) ){ + EgoList.Delete(i); + break; + } + } +#ifndef WIN32 + } +#endif //WIN32 + /* + ** Render all the text strings in their new positions. + */ + if (LogicPage->Lock()){ + for (i=EgoList.Count()-1 ; i>=0 ; i--){ + EgoList[i]->Render(); + } + LogicPage->Unlock(); + } + + if (frame > 1000 && !Theme.Still_Playing()){ + Theme.Queue_Song(THEME_CREDITS); //NONE); + } + + /* + ** Stop calling Theme.AI after a while so a different song doesnt start playing + */ + Call_Back(); +// if (frame <1000 ){ +// Theme.AI(); +// }else{ +// Sound_Callback(); +// } + + /* + ** Kill any spare time before blitting the hid page forward. + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + /* + ** Blit all but the top and bottom of the hid page. This is beacuse the text print doesn't + ** clip vertically and looks ugly when it suddenly appears and disappears. + */ +#ifndef WIN32 + Wait_Vert_Blank(VertBlank); + //Vsync(); +#endif //WIN32 + HidPage.Blit(SeenBuff, 0, 8*RESFACTOR, 0, 8*RESFACTOR, SeenBuff.Get_Width(), SeenBuff.Get_Height() - 16*RESFACTOR, false); + + /* + ** Try and prevent Win95 from swapping out pictures we havnt used yet. + */ +#ifdef WIN32 + if (frame && 3 == 3){ + for (i=slide_number+1 ; iGet_IsDirectDraw() ){ + Force_VM_Page_In ((void*)SlideBuffers[i]->Get_Offset(), SeenBuff.Get_Width() * SeenBuff.Get_Height() ); + } + } + } +#endif //WIN32 + + /* + ** If user hits escape then break. + */ + key = KN_NONE; + if (Keyboard->Check()){ + key = Keyboard->Get(); + if (key == KN_ESC){ + break; + } +#if (0) + if (key == KN_Z){ + speed--; + if (speed <1 ) speed=1; + time = TickCount; + frame = 0; + } + if (key == KN_X){ + speed++; + time = TickCount; + frame = 0; + } +#endif //(0) + + } + + } + + if (key == KN_ESC){ + Theme.Fade_Out(); + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + }else{ + /* + ** Wait for the picture to fade down + */ + while (picture_frame <= FADE_DELAY+FRAME_DELAY){ + if (picture_frame < FRAME_DELAY && picture_frame > 10+FADE_DELAY){ + picture_frame = FRAME_DELAY; + } + frame++; + picture_frame++; + + Slide_Show (slide_number, picture_frame); + + Call_Back(); +// Sound_Callback(); //Theme.AI(); + + /* + ** Kill any spare time + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + } + } + + /* + ** Tidy up. + */ + //SetPriorityClass (GetCurrentProcess() , process_priority); + SeenBuff.Clear(); + + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + Theme.Stop(); + Options.Set_Score_Volume(oldvolume, false); + + for (index = 0 ; index < NUM_SLIDES ; index++){ + delete SlideBuffers[index]; + } + + delete BackgroundPage; + + delete [] credits; + + EgoList.Clear(); +} + +#endif + + + + + + + + + + + + diff --git a/REDALERT/EGOS.H b/REDALERT/EGOS.H new file mode 100644 index 000000000..c4681d45e --- /dev/null +++ b/REDALERT/EGOS.H @@ -0,0 +1,60 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : EGOS.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class EgoClass { + + public: + + EgoClass (int x, int y, char *text, TextPrintType flags); + ~EgoClass (); + + bool Scroll (int distance); + void Render (void); + void Wipe (GraphicBufferClass *background); + + + char *Text; + int XPos; + int YPos; + TextPrintType Flags; +}; + + diff --git a/REDALERT/ENDING.CPP b/REDALERT/ENDING.CPP new file mode 100644 index 000000000..8f5e10f6b --- /dev/null +++ b/REDALERT/ENDING.CPP @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ENDING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void GDI_Ending(void) +{ +#ifdef NEVER + if (TempleIoned) { + Play_Movie("GDIFINB"); + } else { + Play_Movie("GDIFINA"); + } + + Score.Presentation(); + + if (TempleIoned) { + Play_Movie("GDIEND2"); + } else { + Play_Movie("GDIEND1"); + } + Play_Movie("CC2TEASE"); +#endif +} + + +/*********************************************************************************************** + * Nod_Ending -- play ending movies for Nod players * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 7/10/1995 BWG : Created. * + *=============================================================================================*/ +void Nod_Ending(void) +{ +#ifdef NEVER + static char const _tanpal[]={0x0,0x0,0xED,0x0,0x2C,0x0,0xFB,0x0,0xFD,0x0,0x0,0x0,0x0,0x0,0x52,0x0}; + + char fname[12]; + char * satpic = new char[64000]; + int oldfontxspacing = FontXSpacing; + void const * oldfont; + + Score.Presentation(); + + oldfont = Set_Font(ScoreFontPtr); + + void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL")); + Load_Uncompress(CCFileClass("SATSEL.CPS"), HidPage, HidPage); + memcpy(satpic, HidPage.Get_Buffer(), 64000); + + void * kanefinl = Load_Sample("KANEFINL.AUD"); + void * loopie6m = Load_Sample("LOOPIE6M.AUD"); + + Play_Movie("NODFINAL", THEME_NONE, false); + + Hide_Mouse(); + Wait_Vert_Blank(VertBlank); + Set_Palette(localpal); + memcpy(SeenBuff.Get_Buffer(), satpic, 64000); + Show_Mouse(); + + Keyboard->Clear(); + Play_Sample(kanefinl, 255, 128); + Play_Sample(loopie6m, 255, 128); + + bool mouseshown = false; + bool done = false; + int selection = 1; + bool printedtext = false; + while (!done) { + if (!printedtext && !Is_Sample_Playing(kanefinl)) { + printedtext++; + Alloc_Object(new ScorePrintClass(Text_String(TXT_SEL_TARGET), 0, 180, _tanpal)); + mouseshown = true; + Show_Mouse(); + } + Call_Back_Delay(1); + if (!Keyboard->Check()) { + if (!Is_Sample_Playing(loopie6m)) Play_Sample(loopie6m, 255, 128); + } else { + if (Is_Sample_Playing(kanefinl)) { + Clear_KeyBuffer(); + } else { + int key = Keyboard->Get(); + if ((key & 0xFF) == KN_LMOUSE && !(key & KN_RLSE_BIT)) { + int mousex = MouseQX; + int mousey = MouseQY; + if (mousey >= 22 && mousey <= 177) { + done++; + if (mousex < 160 && mousey < 100) selection = 2; + if (mousex < 160 && mousey >= 100) selection = 3; + if (mousex >= 160 && mousey >= 100) selection = 4; + } + } + } + } + } + if (mouseshown) Hide_Mouse(); + delete satpic; + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + // erase the "choose a target" text + SeenBuff.Fill_Rect(0, 180, 319, 199, 0); + + Hide_Mouse(); + Keyboard->Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + Free_Sample(kanefinl); + Free_Sample(loopie6m); + + sprintf(fname, "NODEND%d", selection); + PreserveVQAScreen = 1; + Play_Movie(fname); + + Play_Movie("CC2TEASE"); +#endif +} diff --git a/REDALERT/ENDING.H b/REDALERT/ENDING.H new file mode 100644 index 000000000..173abe10b --- /dev/null +++ b/REDALERT/ENDING.H @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ENDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ENDING_H +#define ENDING_H + +void Nod_Ending(void); + +#endif diff --git a/REDALERT/EVENT.CPP b/REDALERT/EVENT.CPP new file mode 100644 index 000000000..19eca6f6f --- /dev/null +++ b/REDALERT/EVENT.CPP @@ -0,0 +1,1064 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EVENT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : November 10, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EventClass::EventClass -- Construct an id and cell based event. * + * EventClass::EventClass -- Construct simple target type event. * + * EventClass::EventClass -- Constructor for mission change events. * + * EventClass::EventClass -- Constructor for navigation computer events. * + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * EventClass::EventClass -- Constructor for sidebar build events. * + * EventClass::EventClass -- Constructs event to transfer special flags. * + * EventClass::EventClass -- Default constructor for event objects. * + * EventClass::EventClass -- Event for sequencing animations. * + * EventClass::EventClass -- Megamission assigned to unit. * + * EventClass::Execute -- Execute a queued command. * + * EventClass::EventClass -- construct a variable-sized event * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#endif //WIN32 + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +/*************************************************************************** +** Table of what data is really used in the EventClass struct for different +** events. This table must be kept current with the EventType enum. +*/ +unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { + 0, // EMPTY + size_of(EventClass, Data.General ), // ALLY + size_of(EventClass, Data.MegaMission ), // MEGAMISSION + size_of(EventClass, Data.MegaMission_F ), // MEGAMISSION_F + size_of(EventClass, Data.Target ), // IDLE + size_of(EventClass, Data.Target ), // SCATTER + 0, // DESTRUCT + 0, // DEPLOY + size_of(EventClass, Data.Place ), // PLACE + 0, // OPTIONS + size_of(EventClass, Data.General ), // GAMESPEED + size_of(EventClass, Data.Specific ), // PRODUCE + size_of(EventClass, Data.Specific.Type ), // SUSPEND + size_of(EventClass, Data.Specific.Type ), // ABANDON + size_of(EventClass, Data.Target ), // PRIMARY + size_of(EventClass, Data.Special ), // SPECIAL_PLACE + 0, // EXIT + size_of(EventClass, Data.Anim ), // ANIMATION + size_of(EventClass, Data.Target ), // REPAIR + size_of(EventClass, Data.Target ), // SELL + size_of(EventClass, Data.SellCell), // SELLCELL + size_of(EventClass, Data.Options ), // SPECIAL + 0, // FRAMESYNC + 0, // MESSAGE + size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME + size_of(EventClass, Data.FrameInfo ), // FRAMEINFO + 0, // SAVEGAME + size_of(EventClass, Data.NavCom ), // ARCHIVE + size_of(EventClass, Data.Variable.Size), // ADDPLAYER + size_of(EventClass, Data.Timing ), // TIMING + size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME +#ifdef FIXIT_VERSION_3 // Stalemate games. + 0, // PROPOSE_DRAW + 0, // RETRACT_DRAW +#endif +}; + +char * EventClass::EventNames[EventClass::LAST_EVENT] = { + "EMPTY", + "ALLY", + "MEGAMISSION", + "MEGAMISSION_F", + "IDLE", + "SCATTER", + "DESTRUCT", + "DEPLOY", + "PLACE", + "OPTIONS", + "GAMESPEED", + "PRODUCE", + "SUSPEND", + "ABANDON", + "PRIMARY", + "SPECIAL_PLACE", + "EXIT", + "ANIMATION", + "REPAIR", + "SELL", + "SELLCELL", + "SPECIAL", + "FRAMESYNC", + "MESSAGE", + "RESPONSE_TIME", + "FRAMEINFO", + "SAVEGAME", + "ARCHIVE", + "ADDPLAYER", + "TIMING", + "PROCESS_TIME", +#ifdef FIXIT_VERSION_3 // Stalemate games. + "PROPOSE_DRAW", + "RETRACT_DRAW", +#endif +}; + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructs event to transfer special flags. * + * * + * This constructs an event that will transfer the special flags. * + * * + * INPUT: data -- The special flags to be transported to all linked computers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(SpecialClass data) +{ + ID = PlayerPtr->ID; + Type = SPECIAL; + Frame = ::Frame; + Data.Options.Data = data; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct simple target type event. * + * * + * This will construct a generic event that needs only a target parameter. The actual * + * event and target values are specified as parameters. * + * * + * INPUT: type -- The event type to construct. * + * * + * target-- The target value that this event is to apply to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass target) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Target.Whom = target; +} + + +EventClass::EventClass(EventType type, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.SellCell.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Default constructor for event objects. * + * * + * This constructs a simple event object that requires no parameters other than the * + * type of event it is. * + * * + * INPUT: type -- The type of event to construct. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for general-purpose-data events. * + * * + * INPUT: type -- The type of event to construct. * + * val -- data value * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int val) +{ + ID = PlayerPtr->ID; + Type = type; + Data.General.Value = val; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for navigation computer events. * + * * + * Constructor for events that are used to assign the navigation computer. * + * * + * INPUT: type -- The type of event (this constructor can be used by other navigation * + * type events). * + * * + * src -- The object that the event should apply to. * + * * + * dest -- The destination (or target) that the event needs to complete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass src, TargetClass dest) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.NavCom.Whom = src; + Data.NavCom.Where = TargetClass(dest); +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Event for sequencing animations. * + * * + * This constructor is used for animations that must be created through the event system. * + * * + * INPUT: anim -- The animation that will be created. * + * * + * coord -- The location where the animation is to be created. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord) +{ + ID = PlayerPtr->ID; + Type = ANIMATION; + Frame = ::Frame; + Data.Anim.What = anim; + Data.Anim.Owner = owner; + Data.Anim.Where = coord; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION; + Frame = ::Frame; + Data.MegaMission.Whom = src; + Data.MegaMission.Mission = mission; + Data.MegaMission.Target = target; + Data.MegaMission.Destination = destination; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). This variation is used for formation moves. * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * speed -- The formation override speed for this move. * + * * + * maxspeed -- The formation override maximum speed for this move. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION_F; + Frame = ::Frame; + Data.MegaMission_F.Whom = src; + Data.MegaMission_F.Mission = mission; + Data.MegaMission_F.Target = TargetClass(target); + Data.MegaMission_F.Destination = TargetClass(destination); + Data.MegaMission_F.Speed = speed; + Data.MegaMission_F.MaxSpeed = maxspeed; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for sidebar build events. * + * * + * This constructor is used for events that deal with an object type and an object ID. * + * Typically, this is used exclusively by the sidebar. * + * * + * INPUT: type -- The event type of this object. * + * * + * object -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, int id) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Specific.Type = object; + Data.Specific.ID = id; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * * + * This constructor is used for those events that have an object type and associated cell. * + * Typically, this is for building placement after construction has completed. * + * * + * INPUT: type -- The event type for this object. * + * * + * object -- The object type number (actual object is probably inferred from the * + * sidebar data). * + * * + * cell -- The cell location where this event is to occur. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Place.Type = object; + Data.Place.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct an id and cell based event. * + * * + * This constructor is used for those events that require an ID number and a cell location. * + * * + * INPUT: type -- The event type this will be. * + * * + * id -- The arbitrary id number to assign. * + * * + * cell -- The location for this event. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int id, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Special.ID = id; + Data.Special.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- construct a variable-sized event * + * * + * INPUT: * + * ptr ptr to data associated with this event * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/10/1995 BRR : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, void * ptr, unsigned long size) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Variable.Pointer = ptr; + Data.Variable.Size = size; +} + + +/*********************************************************************************************** + * EventClass::Execute -- Execute a queued command. * + * * + * This routine executes an event. The even must already have been confirmed by any * + * remote machine before calling this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void EventClass::Execute(void) +{ + TechnoClass * techno; + AnimClass * anim = 0; + HouseClass * house = 0; +// CELL cell = 0; + char txt[80]; + bool formation = false; +// RTTIType rt; + + if (Debug_Print_Events) { + printf("(%d) Executing %s ID:%d Frame:%d ", + ::Frame, EventNames[Type], ID, Frame); + } + + switch (Type) { + + /* + ** Update the archive target for this building. + */ + case ARCHIVE: + techno = Data.NavCom.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->ArchiveTarget = Data.NavCom.Where.As_TARGET(); + } + break; + + /* + ** Make or break alliance. + */ + case ALLY: + house = Houses.Raw_Ptr(Data.General.Value); + if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { + Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); + } else { + Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); + } + break; + + /* + ** Special self destruct action requested. This is active in the multiplayer mode. + */ + case DESTRUCT: + Houses.Raw_Ptr(ID)->Flag_To_Die(); + break; + + /* + ** Update the special control flags. This is necessary so that in a multiplay + ** game, all machines will agree on the rules. If these options change during + ** game play, then all players are informed that options have changed. + */ + case SPECIAL: + { + Special = Data.Options.Data; + HouseClass * house = Houses.Raw_Ptr(ID); + + //sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); // Should be IniName? ST - 5/8/2019 + sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->IniName); + Session.Messages.Add_Message(NULL, 0, txt, + house->RemapColor, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200); + Map.Flag_To_Redraw(false); + } + break; + + /* + ** Starts or stops repair on the specified object. This event is triggered by the + ** player clicking the repair wrench on a building. + */ + case REPAIR: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->Repair(-1); + } + break; + + /* + ** Tells a building/unit to sell. This event is triggered by the player clicking the + ** sell animating cursor over the building or unit. + */ + case SELL: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { + if (techno->What_Am_I() == RTTI_BUILDING || (techno->What_Am_I() == RTTI_UNIT && Map[techno->Center_Coord()].Cell_Building() != 0)) { + techno->Sell_Back(-1); + } + } else { +// if (Is_Target_Cell(Data.Target.Whom)) { +// Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); +// } + } + break; + + /* + ** Tells the wall at the specified location to sell off. + */ + case SELLCELL: +// cell = Data.SellCell.Cell; + Houses.Raw_Ptr(ID)->Sell_Wall(Data.SellCell.Cell); + break; + + /* + ** This even is used to trigger an animation that is generated as a direct + ** result of player intervention. + */ + case ANIMATION: + anim = new AnimClass(Data.Anim.What, Data.Anim.Where); + if (anim) { + //2019/09/19 JAS - Visibility needs to be determined per player + if (Data.Anim.Owner == HOUSE_NONE || Data.Anim.What != ANIM_MOVE_FLASH) + { + anim->Set_Visible_Flags(0xffff); + } + else + { + anim->Set_Visible_Flags(1 << Data.Anim.Owner); + } + } + break; + + /* + ** This event will place the specified object at the specified location. + ** The event is used to place newly constructed buildings down on the map. The + ** object type is specified. From this object type, the house can determine the + ** exact factory and real object pointer to use. + */ + case PLACE: + Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); + break; + + /* + ** This event starts production of the specified object type. The house can + ** determine from the type and ID value, what object to begin production on and + ** what factory to use. + */ + case PRODUCE: + Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); + break; + + /* + ** This event is generated when the player puts production on hold. From the + ** object type, the factory can be inferred. + */ + case SUSPEND: + Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); + break; + + /* + ** This event is generated when the player cancels production of the specified + ** object type. From the object type, the exact factory can be inferred. + */ + case ABANDON: + Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); + break; + + /* + ** Toggles the primary factory state of the specified building. + */ + case PRIMARY: + { + BuildingClass * building = Data.Target.Whom.As_Building(); + if (building && building->IsActive) { + building->Toggle_Primary(); + } + } + break; + + /* + ** This is the general purpose mission control event. Most player + ** action routes through this event. It sets a unit's mission, TarCom, + ** and NavCom to the values specified. + */ + case MEGAMISSION_F: + techno = Data.MegaMission_F.Whom.As_Techno(); + if (techno && techno->IsActive && techno->Is_Foot()) { + ((FootClass *)techno)->IsFormationMove = true; + ((FootClass *)techno)->FormationSpeed = Data.MegaMission_F.Speed; + ((FootClass *)techno)->FormationMaxSpeed = Data.MegaMission_F.MaxSpeed; + FormMove = true; + FormSpeed = Data.MegaMission_F.Speed; + FormMaxSpeed = Data.MegaMission_F.MaxSpeed; + formation = true; + } + // Fall thru to next case... + + case MEGAMISSION: + if (Debug_Print_Events) { + printf("Whom:%x Tgt:%x Dest:%x ", + Data.MegaMission.Whom.As_TARGET(), + Data.MegaMission.Target.As_TARGET(), + Data.MegaMission.Destination.As_TARGET()); + } + techno = Data.MegaMission.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && techno->Strength > 0 && !techno->IsInLimbo) { + + /* + ** Fetch a pointer to the object of the mission. If there is an error with + ** this object, such as it is dead, then bail. + */ + ObjectClass * object = NULL; + if (Data.MegaMission.Target.Is_Valid()) { + object = Data.MegaMission.Target.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Target.Invalidate(); + } + } + + /* + ** If the destination target is invalid because the object is dead, then + ** bail from processing this mega mission. + */ + if (Data.MegaMission.Destination.Is_Valid()) { + object = Data.MegaMission.Destination.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Destination.Invalidate(); + } + } + + /* + ** Break any existing tether or team contact, since it is now invalid. + */ + if (!techno->IsTethered) { + techno->Transmit_Message(RADIO_OVER_OUT); + } + if (techno->Is_Foot()) { + if (!formation) ((FootClass *)techno)->IsFormationMove = false; + if (((FootClass *)techno)->Team) { + ((FootClass *)techno)->Team->Remove((FootClass *)techno); + } + } + + if (object != NULL) { + + // 2019/09/20 JAS - Added record of who clicked on the object + HouseClass* house = Houses.Raw_Ptr(ID); + bool is_allied = house != nullptr && house->Is_Ally(techno); + if (is_allied) { + object->Clicked_As_Target((HousesType)ID); + } + } + + /* + ** Test to see if the navigation target should really be queued rather + ** than assigned to the object. This would be the case if this is a + ** special queued move mission and there is already a valid navigation + ** target for this unit. + */ + bool q = (Data.MegaMission.Mission == MISSION_QMOVE); + + techno->Assign_Mission(Data.MegaMission.Mission); + + if (techno->Is_Foot()) { + ((FootClass*)techno)->SuspendedNavCom = TARGET_NONE; + } + techno->SuspendedTarCom = TARGET_NONE; + + /* + ** Guard area mode is handled with care. The specified target is actually + ** assigned as the location that should be guarded. In addition, the + ** movement destination is immediately set to this new location. + */ + if (Data.MegaMission.Mission == MISSION_GUARD_AREA && techno->Is_Foot()) { + techno->Assign_Target(TARGET_NONE); + techno->Assign_Destination(Data.MegaMission.Target.As_TARGET()); + techno->ArchiveTarget = Data.MegaMission.Target.As_TARGET(); + } else { + if (q && techno->Is_Foot()) { + ((FootClass *)techno)->Queue_Navigation_List(Data.MegaMission.Destination.As_TARGET()); + } else { + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + techno->Assign_Target(Data.MegaMission.Target.As_TARGET()); + techno->Assign_Destination(Data.MegaMission.Destination.As_TARGET()); + } + } + + // + // Special case for ship repairing: If the assigned mission is to + // move, and 'techno' is a Vessel: + // If the destination is a shipyard or sub pen, set the IsToSelfRepair flag + // Otherwise, clear both IsToSelfRepair and IsSelfRepairing + // + RTTIType rt = techno->What_Am_I(); +// rt = Data.MegaMission.Whom; + if (rt == RTTI_VESSEL && techno != NULL && techno->What_Am_I() == RTTI_VESSEL) { + VesselClass *ship = (VesselClass *)techno; + if (Data.MegaMission.Mission == MISSION_MOVE) { + if (object != NULL) { + if (object->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)object)->House->Is_Ally(techno) && +// if ((RTTIType)Data.MegaMission.Destination == RTTI_BUILDING && + (((BuildingClass *)object)->Class->Type == STRUCT_SHIP_YARD || + ((BuildingClass *)object)->Class->Type == STRUCT_SUB_PEN)) { + ship->IsToSelfRepair = true; + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } + +#ifdef NEVER + if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && + Data.MegaMission.Mission == MISSION_GUARD_AREA) { + + ((FootClass *)techno)->ArchiveTarget = Data.MegaMission.Destination; + } +#endif + } + break; + + /* + ** Request that the unit/infantry/aircraft go into idle mode. + */ + case IDLE: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered && techno->What_Am_I() != RTTI_BUILDING) { + techno->Transmit_Message(RADIO_OVER_OUT); + techno->Assign_Destination(TARGET_NONE); + techno->Assign_Target(TARGET_NONE); + techno->Enter_Idle_Mode(); + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + } + break; + + /* + ** Request that the unit/infantry/aircraft scatter from its current location. + */ + case SCATTER: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->Is_Foot() && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + ((FootClass *)techno)->IsScattering = true; + techno->Scatter(0, true, false); + } + break; + + /* + ** If we are placing down the ion cannon blast then lets take + ** care of it. + */ + case SPECIAL_PLACE: + Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); + break; + + /* + ** Exit the game. + ** Give parting message while palette is fading to black. + */ + case EXIT: + Theme.Queue_Song(THEME_NONE); + Stop_Speaking(); + Speak(VOX_CONTROL_EXIT); + while (Is_Speaking()) { + Call_Back(); + } + GameActive = false; + break; + + /* + ** Process the options menu, unless we're playing back a recording. + */ + case OPTIONS: + if (!Session.Play) { + SpecialDialog = SDLG_OPTIONS; + } + break; + + /* + ** Process the options Game Speed + */ + case GAMESPEED: + Options.GameSpeed = Data.General.Value; + break; + + /* + ** Adjust connection timing for multiplayer games + */ + case RESPONSE_TIME: + Session.MaxAhead = Data.FrameInfo.Delay; + break; + + /* + ** Save a multiplayer game (this event is only generated in multiplayer mode) + */ + case SAVEGAME: + /* + ** Show the user what's going on with a message box (but only if + ** we're not already inside a dialog box routine!) + */ + if (SpecialDialog == SDLG_NONE) { + CDTimerClass timer; + //timer.Start(); + timer = TICKS_PER_SECOND * 4; + + WWMessageBox().Process(TXT_SAVING_GAME, TXT_NONE); + + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + + while (timer > 0) { + Call_Back(); + } + + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + else { + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + } + break; + + /* + ** Add a new player to the game: + ** - Form a network connection to him + ** - Add his name, ID, House etc to our list of players + ** - Re-sort the ID array + ** - Place his units on the map + */ + case ADDPLAYER: + int i; + printf("ADDPLAYER EVENT!\n"); + for (i=0;i<(int)Data.Variable.Size;i++) { + printf("%d\n", ((char *)Data.Variable.Pointer)[i]); + } + if (ID != PlayerPtr->ID) { + delete [] Data.Variable.Pointer; + } + break; + + // + // This event tells all systems to use new timing values. It's like + // RESPONSE_TIME, only it works. It's only used with the + // COMM_MULTI_E_COMP protocol. + // + case TIMING: +#if(TIMING_FIX) + // + // If MaxAhead is about to increase, we're vulnerable to a Packet- + // Received-Too-Late error, if any system generates an event after + // this TIMING event, but before it executes. So, record the + // period of vulnerability's frame start & end values, so we + // can reschedule these events to execute after it's over. + // + if (Data.Timing.MaxAhead > Session.MaxAhead) { + NewMaxAheadFrame1 = Frame; + NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; + } +#endif + Session.DesiredFrameRate = Data.Timing.DesiredFrameRate; + Session.MaxAhead = Data.Timing.MaxAhead; + +#ifndef WOLAPI_INTEGRATION + /* + ** If spawned from WChat then we should be getting poked every minute. If not then + ** deliberately break the max ahead value + */ +#ifdef WIN32 + if (Special.IsFromWChat) { + Session.MaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + if (Debug_Print_Events) { + printf("DesiredFrameRate:%d MaxAhead:%d ", + Session.DesiredFrameRate, + Session.MaxAhead); + } + + break; + + // + // This event tells all systems what the other systems' process + // timing requirements are; it's used to compute a desired frame rate + // for the game. + // + case PROCESS_TIME: + for (i = 0; i < Session.Players.Count(); i++) { + if (ID == Session.Players[i]->Player.ID) { + Session.Players[i]->Player.ProcessTime = Data.ProcessTime.AverageTicks; + break; + } + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case PROPOSE_DRAW: + if( ID == PlayerPtr->ID ) + { + if( Scen.bOtherProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bLocalProposesDraw = true; + break; + } + Scen.bLocalProposesDraw = true; + //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_PROPOSED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + if( Scen.bLocalProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bOtherProposesDraw = true; + break; + } + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + //PG sprintf( szMessage, TXT_WOL_DRAW_PROPOSED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = true; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; + + case RETRACT_DRAW: + if( ID == PlayerPtr->ID ) + { + Scen.bLocalProposesDraw = false; + //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_RETRACTED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + //PG sprintf( szMessage, TXT_WOL_DRAW_RETRACTED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = false; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; +#endif + + /* + ** Default: do nothing. + */ + default: + break; + } + + if (Debug_Print_Events) { + printf("\n"); + } + +} diff --git a/REDALERT/EVENT.H b/REDALERT/EVENT.H new file mode 100644 index 000000000..647bff6c6 --- /dev/null +++ b/REDALERT/EVENT.H @@ -0,0 +1,257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EVENT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EVENT_H +#define EVENT_H + +/* +** This event class is used to contain all external game events (things that the player can +** do at any time) so that these events can be transported between linked computers. This +** encapsulation is required in order to ensure that each event affects all computers at the +** same time (same game frame). +** NOTE: If you add or remove an event type, you must also update the globals +** EventLength[] and EventNames[]. +*/ +class EventClass +{ + public: + + /* + ** All external events are identified by these labels. + */ + typedef enum EventType : unsigned char { + EMPTY, + + ALLY, // Make allie of specified house. + MEGAMISSION, // Full change of mission with target and destination. + MEGAMISSION_F, // Full change of mission with target and destination, and formation overrides. + IDLE, // Request to enter idle mode. + SCATTER, // Request to scatter from current location. + DESTRUCT, // Self destruct request (surrender action). + DEPLOY, // MCV is to deploy at current location. + PLACE, // Place building at location specified. + OPTIONS, // Bring up options screen. + GAMESPEED, // Set game speed + PRODUCE, // Start or Resume production. + SUSPEND, // Suspend production. + ABANDON, // Abandon production. + PRIMARY, // Primary factory selected. + SPECIAL_PLACE, // Special target location selected + EXIT, // Exit game. + ANIMATION, // Flash ground as movement feedback. + REPAIR, // Repair specified object. + SELL, // Sell specified object. + SELLCELL, // Sell wall at specified cell. + SPECIAL, // Special options control. + + // Private events. + FRAMESYNC, // Game-connection packet; includes Scenario CRC & sender's frame # + // Used to initiate game connection phase & to reconnect; + // When one of these is received, the receiver knows there are + // no associated commands in this packet. + MESSAGE, // Message to another player (The message is the 40 bytes + // after the event class). + RESPONSE_TIME, // use a new propagation delay value + FRAMEINFO, // Game-heartbeat packet; includes Game CRC & command count + // All packets sent for a frame are prefixed with one of these + SAVEGAME, // allows multiplayer games to save + ARCHIVE, // Updates archive target on specified object. + ADDPLAYER, // Add a new player + + TIMING, // new timing values for all systems to use + PROCESS_TIME, // a system's average processing time, in ticks per frame + +#ifdef FIXIT_VERSION_3 // Stalemate games. + PROPOSE_DRAW, // Players proposes that 2-player game be called a stalemate. + RETRACT_DRAW, // Player retracts proposed draw offer. +#endif + + LAST_EVENT, // one past the last event + } EventType; + + EventType Type; // Type of queue command object. + + /* + ** 'Frame' is the frame that the command should execute on. + ** 27 bits gives over 25 days of playing time without wrapping, + ** at 30 frames per second, so it should be plenty! + */ + unsigned Frame : 26; + + /* + ** House index of the player originating this event + */ + unsigned ID : 5; + + /* + ** This bit tells us if we've already executed this event. + */ + unsigned IsExecuted: 1; + + /* + ** This union contains the specific data that the event requires. + */ + union { + struct { + SpecialClass Data; // The special option flags. + } Options; + struct { + CELL Cell; // The cell to sell wall at. + } SellCell; + struct { + xTargetClass Whom; // The object to apply the event to. + } Target; + struct { + AnimType What; // The animation to create. + HousesType Owner; // The owner of the animation (when it matters). + COORDINATE Where; // The location to place the animation. + } Anim; + struct { + int Value; // general-purpose data + } General; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + } MegaMission; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + SpeedType Speed; // Formation override speed. + MPHType MaxSpeed; // Formation override maximum speed. + } MegaMission_F; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + } Mission; + struct { + xTargetClass Whom; // Whom to apply movement change to. + xTargetClass Where; // Where to set NavCom to. + } NavCom; + struct { + xTargetClass Whom; // Whom to apply attack change to. + xTargetClass Target; // What to set TarCom to. + } TarCom; + struct { + RTTIType Type; + int ID; + } Specific; + struct { + RTTIType Type; + CELL Cell; + } Place; + struct { + int ID; + CELL Cell; + } Special; + + /* + ** This structure is used for FRAMEINFO, FRAMESYNC, and RESPONSE_TIME + ** events; exactly one of these will be sent each frame, whether there's + ** data that frame or not. + ** CRC: the game CRC when this packet was generated; used to detect sync errors + ** CommandCount: # of commands the sender has sent; used to detect missed packets + ** Delay: sender's propagation delay value for this frame + */ + struct { + unsigned long CRC; + unsigned short CommandCount; // # commands sent so far + unsigned char Delay; // propagation delay used this frame + // (Frame - Delay = sender's current frame #) + } FrameInfo; + /* + ** This structure is used for the special variable-length event. This event + ** can be anything the application wants it to be. Its purpose is to allow + ** relatively large amounts of data to be associated with the event, without + ** bloating the size of this union (and thus all other event types). + */ + struct { + void * Pointer; + unsigned long Size; + } Variable; + + // + // This structure sets new timing values for all systems in a multiplayer + // game. This structure replaces the RESPONSE_TIME event for + // the COMM_MULTI_E_COMP protocol. + // + struct { + unsigned short DesiredFrameRate; + unsigned short MaxAhead; + } Timing; + + // + // This structure is transmitted by all systems, and is used to compute + // the "desired" frame rate for the game. + // + struct { + unsigned short AverageTicks; + } ProcessTime; + + } Data; + + //-------------- Constructors --------------------- + EventClass(void) {Type = EMPTY;}; + EventClass(SpecialClass data); + EventClass(EventType type, TargetClass target); + EventClass(EventType type); + EventClass(EventType type, int val); + EventClass(EventType type, CELL cell); + EventClass(EventType type, TargetClass src, TargetClass dest); + EventClass(TargetClass src, MissionType mission, TargetClass target=TARGET_NONE, TargetClass destination=TARGET_NONE); + + EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed); + + EventClass(EventType type, RTTIType object, int id); + EventClass(EventType type, RTTIType object, CELL cell); + EventClass(EventType type, int id, CELL cell); + EventClass(AnimType anim, HousesType owner, COORDINATE coord); + EventClass(void *ptr, unsigned long size); + EventClass(EventType type, void *ptr, unsigned long size); + + // Process the event. + void Execute(void); + + int operator == (EventClass & q) { + return memcmp(this, &q, sizeof(q)) == 0; + }; + + static unsigned char EventLength[LAST_EVENT]; + static char * EventNames[LAST_EVENT]; +}; + +#endif diff --git a/REDALERT/EXPAND.CPP b/REDALERT/EXPAND.CPP new file mode 100644 index 000000000..bec3242dc --- /dev/null +++ b/REDALERT/EXPAND.CPP @@ -0,0 +1,567 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EXPAND.CPP 7 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXPAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/03/95 * + * * + * Last Update : Mar 01, 1997 [V.Grippi] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +//#define CS_DEBUG + +#define ARRAYOFFSET 20 + + +/*********************************************************************************************** + * Expansion_CS_Present -- Is the Counterstrike expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if counterstrike installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/5/97 1:59PM ST : Fixed to check for EXPAND.MIX * + *=============================================================================================*/ +bool Expansion_CS_Present(void) +{ + // ajw 9/29/98 + return Is_Counterstrike_Installed(); +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + * Expansion_AM_Present -- Is the Aftermath expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if AfterMath is installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 7/9/97 1:59PM BG : Fixed to check for EXPAND2.MIX * + *=============================================================================================*/ +bool Expansion_AM_Present(void) +{ + // ajw 9/29/98 + return Is_Aftermath_Installed(); +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +} +#endif + + +const char* ExpandNames[] = { + "SCG20EA", + "SCG21EA", + "SCG22EA", + "SCG23EA", + "SCG24EA", + "SCG26EA", + "SCG27EA", + "SCG28EA", + "SCU31EA", + "SCU32EA", + "SCU33EA", + "SCU34EA", + "SCU35EA", + "SCU36EA", + "SCU37EA", + "SCU38EA", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "SCG43EA", // Harbor Reclamation + "SCG41EA", // In the nick of time + "SCG40EA", // Caught in the act + "SCG42EA", // Production Disruption + "SCG47EA", // Negotiations + "SCG45EA", // Monster Tank Madness + "SCG44EA", // Time Flies + "SCG48EA", // Absolut MADness + "SCG46EA", // Pawn + + "SCU43EA", // Testing Grounds + "SCU40EA", // Shock Therapy + "SCU42EA", // Let's Make a Steal + "SCU41EA", // Test Drive + "SCU45EA", // Don't Drink The Water + "SCU44EA", // Situation Critical + "SCU46EA", // Brothers in Arms + "SCU47EA", // Deus Ex Machina + "SCU48EA", // Grunyev Revolution +#endif + NULL + }; + +const char* TestNames2[] = { + "SCG01EA", + "SCG02EA", + "SCG03EA", + "SCG04EA", + "SCG05EA", + "SCG06EA", + "SCG07EA", + "SCG08EA", + "SCU01EA", + "SCU02EA", + "SCU03EA", + "SCU04EA", + "SCU05EA", + "SCU06EA", + "SCU07EA", + "SCU08EA", + "SCU09EA", + NULL +}; + +#ifdef GERMAN +const char* XlatNames[] = { + "Zusammenstoss", + "Unter Tage", + "Kontrollierte Verbrennung", + "Griechenland 1 - Stavros", + "Griechenland 2 - Evakuierung", + "Sibirien 1 - Frische Spuren", + "Sibirien 2 - In der Falle", + "Sibirien 3 - Wildnis", + "Das Feld der Ehre", + "Belagerung", + "Mausefalle", + "Teslas Erbe", + "Soldat Volkov", + "Die Spitze der Welt", + "Paradoxe Gleichung", + "Nukleare Eskalation", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "Ein sicherer Hafen", // "SCG43EA", // Harbor Reclamation + "Zeitkritische Routine", // "SCG41EA", // In the nick of time + "Auf frischer Tat ertappt", // "SCG40EA", // Caught in the act + "Drastischer Baustopp", // "SCG42EA", // Production Disruption + "Harte Verhandlungen", // "SCG47EA", // Negotiations + "Ferngelenktes Kriegsspielzeug",// "SCG45EA", // Monster Tank Madness + "Licht aus", // "SCG44EA", // Time Flies + "Molekulare Kriegsfhrung", // "SCG48EA", // Absolut MADness + "Bauernopfer", // "SCG46EA", // Pawn + + "Testgel„nde", // "SCU43EA", // Testing Grounds + "Schocktherapie", // "SCU40EA", // Shock Therapy + "Der Letzte seiner Art", // "SCU42EA", // Let's Make a Steal + "Probefahrt", // "SCU41EA", // Test Drive + "Schlaftrunk", // "SCU45EA", // Don't Drink The Water + "Der jngste Tag", // "SCU44EA", // Situation Critical + "Waffenbrder", // "SCU46EA", // Brothers in Arms + "Deus Ex Machina", // "SCU47EA", // Deus Ex Machina + "Die Replikanten von Grunyev", // "SCU48EA", // Grunyev Revolution + +#endif + NULL +}; + +#endif + +#ifdef FRENCH +const char* XlatNames[] = { + "Gaz Sarin 1: Ravitaillement Fatal", + "Gaz Sarin 2: En Sous-sol", + "Gaz Sarin 3: Attaque Chirurgicale", + "GrŠce Occup‚e 1: Guerre Priv‚e", + "GrŠce Occup‚e 2: Evacuation", + "Conflit Sib‚rien 1: Traces FraŒches", + "Conflit Sib‚rien 2: Pris au PiŠge", + "Conflit Sib‚rien 3: Terres de Glace", + "Mise … l'Epreuve", + "Assi‚g‚s", + "La SouriciŠre", + "L'H‚ritage de Tesla", + "Tandem de Choc", + "Jusqu'au Sommet du Monde", + "Effets Secondaires", + "Intensification nucl‚aire", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +"Le vieux port", // "SCG43EA", // Harbor Reclamation +"Juste … temps", // "SCG41EA", // In the nick of time +"La main dans le sac", // "SCG40EA", // Caught in the act +"Production interrompue", // "SCG42EA", // Production Disruption +"N‚gociations", // "SCG47EA", // Negotiations +"Tanks en folie!", // "SCG45EA", // Monster Tank Madness +"Le temps passe", // "SCG44EA", // Time Flies +"D‚mence absolue", // "SCG48EA", // Absolut MADness +"Le pion", // "SCG46EA", // Pawn + +"Terrains d'essais", // "SCU43EA", // Testing Grounds +"Th‚rapie de choc", // "SCU40EA", // Shock Therapy +"Au voleur!", // "SCU42EA", // Let's Make a Steal +"Essai de conduite", // "SCU41EA", // Test Drive +"Ne buvez pas la tasse", // "SCU45EA", // Don't Drink The Water +"Situation critique", // "SCU44EA", // Situation Critical +"FrŠres d'armes", // "SCU46EA", // Brothers in Arms +"Deus Ex Machina", // "SCU47EA", // Deus Ex Machina +"La R‚volution de Grunyev",// "SCU48EA", // Grunyev Revolution + +#endif + + NULL, +}; + +#endif + + + +#ifndef WIN32 //VG + + #define OPTION_WIDTH 236 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#error Can never again build without WIN32 defined. + #define OPTION_HEIGHT 162 +#else + #define OPTION_HEIGHT 162 +#endif + #define OPTION_X ((320 - OPTION_WIDTH) / 2) + #define OPTION_Y (200 - OPTION_HEIGHT) / 2 + +#else + + #define OPTION_WIDTH 560 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + #define OPTION_HEIGHT 332 +#else + #define OPTION_HEIGHT 300 +#endif + #define OPTION_X ((640 - OPTION_WIDTH) / 2) + #define OPTION_Y (400 - OPTION_HEIGHT) / 2 +#endif + +struct EObjectClass +{ + HousesType House; + int Scenario; + char Name[128]; + char FullName[128]; +}; + + +/* +** Derived from list class to handle expansion scenario listings. The listings +** are recorded as EObjectClass objects. The data contained specifies the scenario +** number, side, and text description. +*/ +class EListClass : public ListClass +{ + public: + EListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ListClass(id, x, y, w, h, flags, up, down) {}; + + virtual int Add_Object(EObjectClass * obj) { + return(ListClass::Add_Item((char const *)obj)); + } + virtual EObjectClass * Get_Object(int index) const { + return((EObjectClass *)ListClass::Get_Item(index)); + } + virtual EObjectClass * Current_Object(void) { + return((EObjectClass *)ListClass::Current_Item()); + } + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + virtual int Add_Item(char const * text) {return(ListClass::Add_Item(text));}; + virtual int Add_Item(int text) {return(ListClass::Add_Item(text));}; + virtual char const * Current_Item(void) const {return(ListClass::Current_Item());}; + virtual char const * Get_Item(int index) const {return(ListClass::Get_Item(index));}; +}; + + +/*********************************************************************************************** + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * * + * This overrides the normal list class draw action so that the scenario name will be * + * displayed along with the house name. * + * * + * INPUT: index -- The index of the entry that should be drawn. * + * * + * x,y -- Coordinate of upper left region to draw the entry into. * + * * + * width -- Width of region (pixels) to draw the entry. * + * * + * selected -- Is this entry considered selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1995 JLB : Created. * + *=============================================================================================*/ +void EListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + char buffer[128]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int text = TXT_NONE; + if (Get_Object(index)->House == HOUSE_GOOD) { + text = TXT_ALLIES; + } else { + text = TXT_SOVIET; + } + sprintf(buffer, "%s: %s", Text_String(text), Get_Object(index)->Name); + + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, 1); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + +#ifndef WIN32 + Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags & ~(TPF_CENTER), width, Tabs); +#else + Conquer_Clip_Text_Print(buffer, x + 100, y, scheme, TBLACK, flags & ~(TPF_CENTER), width, Tabs); +#endif +} + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ) // If not bCounterstrike, then this was called for Aftermath. +#else +bool Expansion_Dialog(void) +#endif +{ + GadgetClass * buttons = NULL; + +#ifndef WIN32 + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y+OPTION_HEIGHT-15); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y+OPTION_HEIGHT-15); +#else + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y + OPTION_HEIGHT - 50 ); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y + OPTION_HEIGHT - 50 ); +#endif + +#ifndef WIN32 + EListClass list(202, OPTION_X + 20, OPTION_Y+20, OPTION_WIDTH-40, OPTION_HEIGHT-40, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#else + EListClass list(202, OPTION_X+35, OPTION_Y + 30, OPTION_WIDTH-70, OPTION_HEIGHT - 85, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + +#endif + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + /* + ** Add in all the expansion scenarios. + */ + CCFileClass file; + char buffer[128], buffer2[128]; + char * sbuffer = (char*)_ShapeBuffer; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Though disgusted. + for (int index = 20; index < (36+18); index++) { +#else + for (int index = 20; index < 36; index++) { +#endif + +#ifndef CS_DEBUG + strcpy(buffer,ExpandNames[index - 20]); + strcpy(buffer2, ExpandNames[index - 20]); +#else + strcpy(buffer, TestNames2[index]); + strcpy(buffer2, TestNames2[index]); +#endif + if(buffer[0] == NULL) + break; + + strcat(buffer, ".INI"); + strcat(buffer2, ".INI"); + Scen.Set_Scenario_Name(buffer); + Scen.Scenario = index; + file.Set_Name(buffer); +#ifdef FIXIT_VERSION_3 + bool bOk; + if( index < 36 ) + bOk = bCounterstrike; + else + bOk = !bCounterstrike; + + if( bOk && file.Is_Available() ) + { +#else // FIXIT_VERSION_3 + if (file.Is_Available()) { +#endif // FIXIT_VERSION_3 + EObjectClass * obj = new EObjectClass; + switch(buffer[2]){ + + case 'G': + case 'g': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_GOOD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + case 'U': + case 'u': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_BAD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + + default: + break; + } + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + + + while (process) { + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } +#endif + + Call_Back(); + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); +#ifdef FIXIT_VERSION_3 + if (bCounterstrike) + { + //PG Draw_Caption( TXT_WOL_CS_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + } + else + { + //PG Draw_Caption( TXT_WOL_AM_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + } +#else + Draw_Caption(TXT_NEW_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); +#endif + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case 200|KN_BUTTON: + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + process = false; + okval = false; + break; + + case (KN_RETURN): + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (int index = 0; index < list.Count(); index++) { + delete list.Get_Object(index); + } + + return(okval); + +} + diff --git a/REDALERT/EXTERNS.H b/REDALERT/EXTERNS.H new file mode 100644 index 000000000..452644020 --- /dev/null +++ b/REDALERT/EXTERNS.H @@ -0,0 +1,508 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/EXTERNS.H 2 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EXTERNS_H +#define EXTERNS_H + +#include "cell.h" + +#ifdef SCENARIO_EDITOR +#include "mapedit.h" +#endif +#include "techno.h" +#include "type.h" +#include "building.h" +#include "unit.h" +#include "credits.h" +#include "goptions.h" +#include "options.h" +#include "infantry.H" +#include "DSOUND.H" + +extern char _staging_buffer[32000]; +extern "C" { +void _PRO(void); +} + +/* +** Convenient alias for MixFileClass object. This allows +** easier entry into the code and less clutter. +*/ +typedef MixFileClass MFCD; + +extern bool IsVQ640; +extern unsigned long GameVersion; +extern bool Debug_MotionCapture; +extern bool Debug_Rotate; +extern bool Debug_Quiet; +extern bool Debug_Cheat; +extern bool Debug_Remap; +extern bool Debug_Flag; +extern bool Debug_Lose; +extern bool Debug_Map; +extern bool Debug_Win; +extern bool Debug_Icon; +extern bool Debug_Passable; +extern bool Debug_Unshroud; +extern bool Debug_Threat; +extern bool Debug_Find_Path; +extern bool Debug_Check_Map; +extern bool Debug_Playtest; + +extern bool Debug_Heap_Dump; +extern bool Debug_Smart_Print; +extern bool Debug_Trap_Check_Heap; +extern bool Debug_Modem_Dump; +extern bool Debug_Print_Events; +extern bool Debug_Force_Crash; + +extern void const *LightningShapes; + +extern int NewINIFormat; + + +#ifdef FIXIT_ANTS +extern bool AntsEnabled; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool NewUnitsEnabled; +extern bool SecretUnitsEnabled; +extern int MTankDistance; +extern bool OverrideNewUnitsEnabled; // ST - 12/13/2019 12:19PM +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +extern int CarrierLaunchDelay; +#endif +#endif + +#ifdef FIXIT_NAME_OVERRIDE +extern char const * NameOverride[25]; +extern int NameIDOverride[25]; +#endif + +#ifdef WIN32 +extern bool GameInFocus; +extern unsigned char *InterpolatedPalettes[100]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; +extern int AllDone; +extern HANDLE hInstance; +extern bool InMovie; +extern WinTimerClass * WindowsTimer; +extern WWMouseClass * WWMouse; +extern GraphicBufferClass HiddenPage; +#define SeenPage SeenBuff +extern GraphicBufferClass VisiblePage; +extern GraphicViewPortClass SeenBuff; +extern GraphicBufferClass SysMemPage; +extern LPDIRECTSOUND SoundObject; +extern LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +extern int ScreenWidth; +extern int ScreenHeight; +extern GraphicBufferClass ModeXBuff; + +#else + +extern VideoBufferClass SeenPage; +extern GraphicBufferClass SeenBuff; +extern GraphicBufferClass & VisiblePage; +#endif + + +/* +** Dynamic global variables (these change or are initialized at run time). +*/ +extern MissionControlClass MissionControl[MISSION_COUNT]; +extern char const * TutorialText[225]; +extern Buffer * TheaterBuffer; +extern GetCDClass CDList; +extern CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern CCINIClass AftermathINI; +#endif +//extern Benchmark * Benches; +extern int MapTriggerID; +extern int LogicTriggerID; +extern PKey FastKey; +extern PKey SlowKey; +extern RulesClass Rule; +extern KeyboardClass * Keyboard; +extern RandomStraw CryptRandom; +extern RandomClass NonCriticalRandomNumber; +extern CarryoverClass * Carryover; +extern ScenarioClass Scen; +extern RemapControlType ColorRemaps[PCOLOR_COUNT]; +extern RemapControlType MetalScheme; +extern RemapControlType GreyScheme; +extern VersionClass VerNum; +extern bool SlowPalette; +extern bool ScoresPresent; +extern bool AllowVoice; +extern NewConfigType NewConfig; +extern VoxType SpeakQueue; +extern bool PlayerWins; +extern bool PlayerLoses; +extern bool PlayerRestarts; +extern long Frame; +extern VoxType SpeechRecord[2]; +extern void * SpeechBuffer[2]; +extern int PreserveVQAScreen; +extern bool BreakoutAllowed; +extern bool Brokeout; + +extern GameOptionsClass Options; + +extern LogicClass Logic; +#ifdef SCENARIO_EDITOR +extern MapEditClass Map; +#else +extern MouseClass Map; +#endif +extern ScoreClass Score; +extern MonoClass MonoArray[DMONO_COUNT]; +extern MFCD * TheaterData; +extern MFCD * MoviesMix; +extern MFCD * GeneralMix; +extern MFCD * ScoreMix; +extern MFCD * MainMix; +extern MFCD * ConquerMix; +extern ThemeClass Theme; +extern SpecialClass Special; + +/* +** Game object allocation and tracking classes. +*/ +extern TFixedIHeapClass Aircraft; +extern TFixedIHeapClass Anims; +extern TFixedIHeapClass Buildings; +extern TFixedIHeapClass Bullets; +extern TFixedIHeapClass Factories; +extern TFixedIHeapClass Houses; +extern TFixedIHeapClass Infantry; +extern TFixedIHeapClass Overlays; +extern TFixedIHeapClass Smudges; +extern TFixedIHeapClass Teams; +extern TFixedIHeapClass TeamTypes; +extern TFixedIHeapClass Templates; +extern TFixedIHeapClass Terrains; +extern TFixedIHeapClass Triggers; +extern TFixedIHeapClass Units; +extern TFixedIHeapClass Vessels; +extern TFixedIHeapClass TriggerTypes; + +extern TFixedIHeapClass HouseTypes; +extern TFixedIHeapClass BuildingTypes; +extern TFixedIHeapClass AircraftTypes; +extern TFixedIHeapClass InfantryTypes; +extern TFixedIHeapClass BulletTypes; +extern TFixedIHeapClass AnimTypes; +extern TFixedIHeapClass UnitTypes; +extern TFixedIHeapClass VesselTypes; +extern TFixedIHeapClass TemplateTypes; +extern TFixedIHeapClass TerrainTypes; +extern TFixedIHeapClass OverlayTypes; +extern TFixedIHeapClass SmudgeTypes; + +extern FixedIHeapClass * HeapPointers[RTTI_COUNT]; + +extern TFixedIHeapClass Weapons; +extern TFixedIHeapClass Warheads; + +extern QueueClass OutList; +extern QueueClass DoList; + +#ifdef MIRROR_QUEUE +extern QueueClass MirrorList; +#endif + +typedef DynamicVectorArrayClass SelectedObjectsType; +extern SelectedObjectsType CurrentObject; +extern DynamicVectorClass LogicTriggers; +extern DynamicVectorClass MapTriggers; +extern DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + +extern BaseClass Base; + +/* These variables are used to keep track of the slowest speed of a team */ +extern MPHType TeamMaxSpeed[MAX_TEAMS]; +extern SpeedType TeamSpeed[MAX_TEAMS]; +extern bool FormMove; +extern SpeedType FormSpeed; +extern MPHType FormMaxSpeed; + +extern bool IsTanyaDead; +extern bool SaveTanya; + +extern bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool PendingTimeQuake; +extern TARGET TimeQuakeCenter; +extern fixed QuakeUnitDamage; +extern fixed QuakeBuildingDamage; +extern int QuakeInfantryDamage; +extern int QuakeDelay; +extern fixed ChronoTankDuration; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +extern fixed EngineerDamage; // Amount of damage an engineer does +extern fixed EngineerCaptureLevel; // Building damage level before engineer can capture +#endif +#endif + +/* +** Loaded data file pointers. +*/ +extern void const * Metal12FontPtr; +extern void const * MapFontPtr; +extern void const * VCRFontPtr; +extern void const * TypeFontPtr; +extern void const * Font3Ptr; +extern void const * Font6Ptr; +extern void const * EditorFont; +extern void const * Font8Ptr; +extern void const * FontLEDPtr; +extern void const * ScoreFontPtr; +extern void const * GradFont6Ptr; +extern char const * SystemStrings; +extern char const * DebugStrings; + +/* +** Miscellaneous globals. +*/ +extern ChronalVortexClass ChronalVortex; +extern TTimerClass TickCount; +extern bool PassedProximity; // used in display.cpp +extern HousesType Whom; +//extern _VQAConfig AnimControl; +extern long SpareTicks; +extern long PathCount; +extern long CellCount; +extern long TargetScan; +extern long SidebarRedraws; +extern DMonoType MonoPage; +extern bool GameActive; +extern bool SpecialFlag; +extern int ScenarioInit; +extern HouseClass * PlayerPtr; +extern PaletteClass CCPalette; +extern PaletteClass BlackPalette; +extern PaletteClass WhitePalette; +extern PaletteClass GamePalette; +//extern PaletteClass InGamePalette; +#define InGamePalette GamePalette +extern PaletteClass OriginalPalette; +extern PaletteClass ScorePalette; +extern int BuildLevel; +extern unsigned long ScenarioCRC; + +#ifdef FIXIT_VERSION_3 +extern bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +extern unsigned long PlayingAgainstVersion; // Negotiated version number +extern bool Version107InMix; // Is there a v1.07 in the game +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +extern bool bAutoSonarPulse; +#endif + +#ifdef SCENARIO_EDITOR +extern CELL CurrentCell; +#endif + +extern SessionClass Session; +//extern NullModemClass NullModem; +extern IPXManagerClass Ipx; + +#if(TEN) +extern TenConnManClass *Ten; +#endif + +#if(MPATH) +extern MPlayerManClass *MPath; +#endif + +#if(TIMING_FIX) +extern int NewMaxAheadFrame1; +extern int NewMaxAheadFrame2; +#endif + +extern int Seed; +extern int CustomSeed; +extern GroundType Ground[LAND_COUNT]; + +/* +** Constant externs (data is not modified during game play). +*/ +extern char const * Missions[MISSION_COUNT]; +extern char const Keys[]; +extern char const * const VQName[VQ_COUNT]; +extern int CrateData[CRATE_COUNT]; +extern char const * const CrateNames[CRATE_COUNT]; +extern int CrateShares[CRATE_COUNT]; +extern AnimType CrateAnims[CRATE_COUNT]; +extern char const * const SpecialWeaponName[SPC_COUNT]; +extern int const SpecialWeaponHelp[SPC_COUNT]; +extern char const * const SpecialWeaponFile[SPC_COUNT]; +extern char const * const ArmorName[ARMOR_COUNT]; +extern char const * const QuarryName[QUARRY_COUNT]; +extern char const * const FormationName[FORMATION_COUNT]; +extern unsigned long const PlayCodes[]; +extern unsigned long const CheatCodes[]; +//extern char const * const ProjectileNames[]; +extern unsigned long const EditorCodes[]; +extern char const * const SourceName[SOURCE_COUNT]; +extern int const GroundColor[LAND_COUNT]; +extern int const SnowColor[LAND_COUNT]; +extern TheaterDataType const Theaters[THEATER_COUNT]; +extern unsigned char const Facing32[256]; +extern unsigned char const Facing16[256]; +extern signed char const Rotation16[256]; +extern unsigned char const Facing8[256]; +extern unsigned char const Pixel2Lepton[24]; +extern COORDINATE const StoppingCoordAbs[5]; +extern CELL const AdjacentCell[FACING_COUNT]; +extern COORDINATE const AdjacentCoord[FACING_COUNT]; +extern unsigned char const RemapCiv2[]; +extern unsigned char const RemapCiv4[]; +extern unsigned char const RemapCiv5[]; +extern unsigned char const RemapCiv6[]; +extern unsigned char const RemapCiv7[]; +extern unsigned char const RemapCiv8[]; +extern unsigned char const RemapCiv9[]; +extern unsigned char const RemapCiv10[]; +extern unsigned char const RemapEmber[]; + +extern int SoundOn; + +#ifdef WIN32 +extern GraphicViewPortClass HidPage; +#else +extern GraphicBufferClass HidPage; +#endif +extern int MenuList[][8]; +extern CDTimerClass FrameTimer; +extern CDTimerClass CountDownTimer; + +extern SpecialDialogType SpecialDialog; + +extern int RequiredCD; +extern int CurrentCD; +extern int MouseInstalled; + + +extern int LogLevel; +extern unsigned long LogLevelTime[ MAX_LOG_LEVEL ]; +extern unsigned long LogLastTime; + +extern class DynamicVectorClass test2; +extern class DynamicVectorClass test3; + +extern bool LogDump_Print; + +extern "C"{ + extern bool IsTheaterShape; +} + +extern void Reset_Theater_Shapes(void); +extern TheaterType LastTheater; +void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); +void Do_Vortex (int x, int y, int frame); + + +/************************************************************ +** Win32 specific externs +*/ +#ifdef WIN32 +extern int ReadyToQuit; //Are we about to exit cleanly +extern bool InDebugger; //Are we being run from a debugger +void Memory_Error_Handler(void); //Memory error handler function +void WWDebugString (char *string); +#else +extern bool IsTheaterShape; +#endif //WIN32 + + +/************************************************************* +** Internet specific externs +*/ +#ifdef WIN32 + +extern char PlanetWestwoodHandle[]; //Planet WW user name +extern char PlanetWestwoodPassword[]; //Planet WW password +extern char PlanetWestwoodIPAddress[]; //IP of server or other player +extern long PlanetWestwoodPortNumber; //Port number to send to +extern bool PlanetWestwoodIsHost; //Flag true if player has control of game options +extern unsigned long PlanetWestwoodGameID; //Game ID +extern HWND WChatHWND; //Handle to Wchat window. +extern bool GameStatisticsPacketSent; +extern bool ConnectionLost; +extern void *PacketLater; +extern bool SpawnedFromWChat; +extern int ShowCommand; +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +extern int UnitBuildPenalty; + + +#endif //WIN32 + +/* +** From SENDFILE.CPP - externs for scenario file transfers +*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, unsigned int crc, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); +bool Get_Scenario_File_From_Host(char *return_name, int gametype); +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official); + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +extern MCIMovie* MciMovie; +#endif + +#include "mpgset.h" +extern MPGSettings* MpgSettings; +#endif + +extern "C" bool MMXAvailable; + +extern bool ShareAllyVisibility; + +#endif diff --git a/REDALERT/FACE.CPP b/REDALERT/FACE.CPP new file mode 100644 index 000000000..96033646c --- /dev/null +++ b/REDALERT/FACE.CPP @@ -0,0 +1,224 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * * + * This routine will find the facing (compass direction) from one location to another. * + * Typical use of this is in find path and other 'monster' movement logic. * + * * + * INPUT: x1,y1 -- X and Y coordinates for the source location. The coordinate 0,0 is * + * presumed to be the northwest corner of the map. * + * * + * x2,y2 -- X and Y coordinates for the destination (target) location. * + * * + * OUTPUT: Returns with the facing from the first coordinate to the second coordinate. The * + * value returned will range from 0 being North, increasing clockwise until reaching * + * 255 which is just shy of North in a Westerly direction. * + * * + * WARNINGS: This routine is only accurate to the 8 primary compass directions. It is much * + * faster than the Desired_Facing256() function so it should be used where speed * + * is more important than accuracy. * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +/*********************************************************************************************** + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * * + * This routine will figure the facing from the source coordinate toward the destination * + * coordinate. Typically, this routine is used for movement and other 'monster' logic. It * + * is more accurate than the corresponding Desired_Facing8() function, but is slower. * + * * + * INPUT: srcx, srcy -- The source coordinate to determine the facing from. * + * * + * dstx, dsty -- The destination (or target) coordinate to determine the facing * + * toward. * + * * + * OUTPUT: Returns with the facing from the source coordinate toward the destination * + * coordinate with 0 being North increasing in a clockwise direction. 64 is East, * + * 128 is South, etc. * + * * + * WARNINGS: The coordinate 0,0 is presumed to be in the Northwest corner of the map. * + * Although this routine is fast, it is not as fast as Desired_Facing8(). * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + //lint -e414 Division by zero cannot occur here. + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} diff --git a/REDALERT/FACE.H b/REDALERT/FACE.H new file mode 100644 index 000000000..dee5f9e68 --- /dev/null +++ b/REDALERT/FACE.H @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACE_H +#define FACE_H + +// Enumerations of the facing values returned from Desired_Facing(). +typedef enum DirType : unsigned char { + DIR_MIN=0, + DIR_N=0, + DIR_NE=1<<5, + DIR_E=2<<5, + DIR_SE=3<<5, + DIR_S=4<<5, + DIR_SW=5<<5, + DIR_W=6<<5, + DIR_NW=7<<5, + DIR_MAX=255 +} DirType; + +// Operators that allow simple math with DirType. +inline DirType operator + (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator + (DirType f1, int f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, int f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} + + +// Function prototypes. +DirType Desired_Facing8(int x1, int y1, int x2, int y2); +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty); + +#endif + + + diff --git a/REDALERT/FACING.CPP b/REDALERT/FACING.CPP new file mode 100644 index 000000000..751be72ce --- /dev/null +++ b/REDALERT/FACING.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FacingClass::FacingClass -- Default constructor for the facing class. * + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * FacingClass::Set_Current -- Sets the current rotation value. * + * FacingClass::Set_Desired -- Sets the desired facing value. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "facing.h" + + +/*********************************************************************************************** + * FacingClass::FacingClass -- Default constructor for the facing class. * + * * + * This default constructor merely sets the desired and current facing values to be the * + * same (North). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +FacingClass::FacingClass(void) +{ + CurrentFacing = DIR_N; + DesiredFacing = DIR_N; +} + + +/*********************************************************************************************** + * FacingClass::Set_Desired -- Sets the desired facing value. * + * * + * This routine is used to set the desired facing value without altering the current * + * facing setting. Typical use of this routine is when a vehicle needs to face a * + * direction, but currently isn't facing the correct direction. After this routine is * + * called, it is presumed that subsequent calls to Rotation_Adjust() will result in the * + * eventual alignment of the current facing. * + * * + * INPUT: facing -- The new facing to assign to the desired value. * + * * + * OUTPUT: bool; Did the desired facing value actually change by this routine call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Desired(DirType facing) +{ + if (DesiredFacing != facing) { + DesiredFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Set_Current -- Sets the current rotation value. * + * * + * This routine will set the current rotation value. It is used to override the facing * + * value without adjusting the desired setting. * + * * + * INPUT: facing -- The new facing to assign to the current facing value. * + * * + * OUTPUT: bool; Was the current setting changed by this routine. Failure means that the * + * current setting was already at the value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Current(DirType facing) +{ + if (CurrentFacing != facing) { + CurrentFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * * + * This routine is used when the current and desired facings differ but the current * + * facing should be adjusted toward the desired facing. The amount of rotation to adjust * + * is provided as a rotation rate parameter. Typical use of this routine is for turrets * + * and other vehicle related rotating. * + * * + * INPUT: rate -- The rotation rate to use when adjusting the current facing toward the * + * desired facing. A value of 127 means instantaneous rotation. * + * * + * OUTPUT: bool; Did the rotation result in the current facing transitioning from one * + * 1/32 zone to another? If true, then the owning object most likely will * + * need to be redrawn to reflect the change. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Rotation_Adjust(int rate) +{ + /* + ** Only perform the rotation adjustment if the desired facing is not the + ** same as the current facing. + */ + if (Is_Rotating()) { + rate = min(rate, 127); + + DirType oldfacing = CurrentFacing; + int diff = Difference(); + + /* + ** If the allowed facing change is greater than the difference between + ** the current facing and the desired facing, then just snap the + ** facing to the new value. + */ + if (ABS(diff) < rate) { + CurrentFacing = DesiredFacing; + } else { + + /* + ** Adjust the current facing clockwise or counterclockwise depending + ** on the shortest distance to the desired facing from the current + ** facing. + */ + if (diff < 0) { + CurrentFacing = (DirType)(CurrentFacing - (DirType)rate); + } else { + CurrentFacing = (DirType)(CurrentFacing + (DirType)rate); + } + } + + /* + ** If this facing adjustment caused the current facing to rotate into a + ** new 1/32 rotation zone (likely to cause a redraw), then return + ** this fact with a true value. + */ + return(Dir_To_32(CurrentFacing) != Dir_To_32(oldfacing)); + } + return(false); +} diff --git a/REDALERT/FACING.H b/REDALERT/FACING.H new file mode 100644 index 000000000..d4532686e --- /dev/null +++ b/REDALERT/FACING.H @@ -0,0 +1,77 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACING_H +#define FACING_H + +/* +** This is a general facing handler class. It is used in those cases where facing needs to be +** kept track of, but there could also be an associated desired facing. The current facing +** is supposed to transition to the desired state over time. Using this class facilitates this +** processing as well as isolating the rest of the code from the internals. +*/ +class FacingClass +{ + public: + FacingClass(void); + FacingClass(DirType dir) : CurrentFacing(dir), DesiredFacing(dir) {}; + FacingClass(NoInitClass const & ) {}; + + operator DirType (void) const {return(CurrentFacing);}; + + DirType Current(void) const {return(CurrentFacing);}; + DirType Desired(void) const {return(DesiredFacing);}; + + int Set_Desired(DirType facing); + int Set_Current(DirType facing); + + void Set(DirType facing) { + Set_Current(facing); + Set_Desired(facing); + }; + + DirType Get(void) const { return CurrentFacing; } + + int Is_Rotating(void) const {return (DesiredFacing != CurrentFacing);}; + int Difference(void) const {return (int)(signed char)((int)DesiredFacing - (int)CurrentFacing);} + int Difference(DirType facing) const {return (int)(signed char)((int)facing - (int)CurrentFacing);} + int Rotation_Adjust(int rate); + + private: + DirType CurrentFacing; + DirType DesiredFacing; +}; + + +#endif diff --git a/REDALERT/FACTORY.CPP b/REDALERT/FACTORY.CPP new file mode 100644 index 000000000..8ba74e1a4 --- /dev/null +++ b/REDALERT/FACTORY.CPP @@ -0,0 +1,697 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACTORY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FactoryClass::AI -- Process factory production logic. * + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * FactoryClass::Get_Special_Item -- gets factory's spc prod item * + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * FactoryClass::Set -- Assigns a factory to produce an object. * + * FactoryClass::Set -- Force factory to "produce" special object. * + * FactoryClass::Start -- Resumes production after suspension or creation. * + * FactoryClass::Suspend -- Temporarily stop production. * + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * * + * This brings the factory into a null state. It is called when a factory object is * + * created. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::FactoryClass(void) : + RTTI(RTTI_FACTORY), + ID(Factories.ID(this)), + IsSuspended(false), + IsDifferent(false), + IsBlocked(false), + Balance(0), + OriginalBalance(0), + Object(0), + SpecialItem(SPC_NONE), + House(0) +{ + Set_Rate(0); + Set_Stage(0); +} + + +/*********************************************************************************************** + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * * + * This cleans up a factory object in preparation for deletion. If there is currently * + * an object in production, it is abandoned and money is refunded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::~FactoryClass(void) +{ + if (GameActive) { + Abandon(); + } +} + + +/*********************************************************************************************** + * FactoryClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the factory list and objects. This routine is typically * + * used in preparation for a new scenario load. All factory are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Init(void) +{ + Factories.Free_All(); +} + + +/*********************************************************************************************** + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * * + * This routine allocates a factory from the free factory pool. If there is no more room * + * to allocate a factory, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to the newly allocated factory object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void * FactoryClass::operator new(size_t) +{ + void * ptr = Factories.Allocate(); + if (ptr) { + ((FactoryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * * + * This returns the factory object back to the factory allocation pool. The factory is then * + * available to be allocated. * + * * + * INPUT: ptr -- Pointer to the factory object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::operator delete(void * ptr) +{ + if (ptr) { + ((FactoryClass *)ptr)->IsActive = false; + } + Factories.Free((FactoryClass *)ptr); +} + + +/*********************************************************************************************** + * FactoryClass::AI -- Process factory production logic. * + * * + * This routine should be called once per game tick. It handles the production process. * + * As production proceeds, money is deducted from the owner object's house. When production * + * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * + * required after that point. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 01/04/1995 JLB : Uses exact installment payment method. * + *=============================================================================================*/ +void FactoryClass::AI(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended && (Object != NULL || SpecialItem)) { + for (int index = 0; index < 1; index++) { + if (!Has_Completed() && Graphic_Logic() ) { + IsDifferent = true; + + int cost = Cost_Per_Tick(); + + cost = min(cost, Balance); + + /* + ** Enough time has expired so that another production step can occur. + ** If there is insufficient funds, then go back one production step and + ** continue the countdown. The idea being that by the time the next + ** production step occurs, there may be sufficient funds available. + */ + if (cost > House->Available_Money()) { + Set_Stage(Fetch_Stage()-1); + } else { + House->Spend_Money(cost); + Balance -= cost; + } + + /* + ** If the production has completed, then suspend further production. + */ + if (Fetch_Stage() == STEP_COUNT) { + IsSuspended = true; + Set_Rate(0); + House->Spend_Money(Balance); + Balance = 0; + } + } + } + } +} + + + +/*********************************************************************************************** + * FactoryClass::Force_Complete -- Force the factory to finish what it's building * + * * + * For debugging/testing support * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 8/23/2019 3:54PM ST : Created. * + *=============================================================================================*/ +void FactoryClass::Force_Complete(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended && (Object != NULL || SpecialItem)) { + Set_Stage(STEP_COUNT); + IsSuspended = true; + Set_Rate(0); + Balance = 0; + IsDifferent = true; + } +} + + +/*********************************************************************************************** + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * * + * Use this routine to determine if production has advanced at least one step. By using * + * this function, intelligent rendering may be performed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the production process advanced one step since the last time this * + * function was called? * + * * + * WARNINGS: This function clears the changed status flag as a side effect. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Changed(void) +{ + assert(Factories.ID(this) == ID); + + bool changed = IsDifferent; + IsDifferent = false; + return(changed); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Assigns a factory to produce an object. * + * * + * This routine initializes a factory to produce the object specified. The desired object * + * type is created and placed in suspended animation (limbo) until such time as production * + * completes. Production is not actually started by this routine. An explicit call to * + * Start() is required to begin production. * + * * + * INPUT: object -- Reference to the object type class that is to be produced. * + * * + * house -- Reference to the owner of the object to be produced. * + * * + * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * + * that the object could not be created. This is catastrophic and in such * + * cases, the factory object should be deleted. * + * * + * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * + * the factory means that the factory is useless and should be deleted. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) +{ + assert(Factories.ID(this) == ID); + + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + Object = (TechnoClass *)object.Create_One_Of(&house); + + /* + ** Buildings that are constructed, will default to rebuilding on so that + ** repair can commence and base rebuilding can occur. + */ + if (!house.IsHuman && Object != NULL && Object->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)Object)->IsToRebuild = true; + } + + if (Object) { + House = Object->House; + Balance = object.Cost_Of() * house.CostBias; + Object->PurchasePrice = Balance; + } + + /* + ** If all was set up successfully, then return true. + */ + return(Object != NULL); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Fills a factory with an already completed object. * + * * + * This routine is called when a produced object is in placement mode but then placement * + * is suspended. The object must then return to the factory as if it were newly completed * + * and awaiting removal. * + * * + * INPUT: object -- The object to return to the factory. * + * * + * OUTPUT: none * + * * + * WARNINGS: This will abandon any current object being produced at the factory in order * + * to set the new object into it. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Set(TechnoClass & object) +{ + assert(Factories.ID(this) == ID); + + Abandon(); + Object = &object; + House = Object->House; + Balance = 0; + Set_Rate(0); + Set_Stage(STEP_COUNT); + IsDifferent = true; + IsSuspended = true; +} + + +/*********************************************************************************************** + * FactoryClass::Suspend -- Temporarily stop production. * + * * + * This routine will suspend production until a subsequent call to Start() or Abandon(). * + * Typical use of this function is when the player puts production on hold or when there * + * is insufficient funds. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * + * factory was empty or production was already stopped (or never started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Suspend(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended) { + IsSuspended = true; + Set_Rate(0); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Start -- Resumes production after suspension or creation. * + * * + * This function will start the production process. It works for newly created factory * + * objects, as well as if production had been suspended previously. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production started? A false return value means that the factory is * + * empty or there is insufficient credits to begin production. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Start(void) +{ + assert(Factories.ID(this) == ID); + + if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { + if (House->IsHuman || House->Available_Money() >= Cost_Per_Tick()) { + int time; + + if (Object) { + time = Object->Time_To_Build(); +// } else { +// time = TICKS_PER_MINUTE * 5; + } + + /* + ** Adjust time according to IQ setting of computer controlled house. The + ** build time will range from double normal time at the slowest to + ** just normal time at the fastest. + */ + if (!House->IsHuman && Rule.Diff[House->Difficulty].IsBuildSlowdown) { + time = time * Inverse(fixed(House->IQ+Rule.MaxIQ, Rule.MaxIQ*2)); + } + + int rate = time / Bound(House->Power_Fraction(), fixed(1, 16), fixed(1)); +// int frac = House->Power_Fraction(); +// frac = Bound(frac, 0x0010, 0x0100); +// int rate = (time*256) / frac; + + rate /= STEP_COUNT; + rate = Bound(rate, 1, 255); + + Set_Rate(rate); + IsSuspended = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * * + * This routine is used when construction is to be abandoned and current money spend is * + * to be refunded. This function effectively clears out this factory of all record of the * + * producing object so that it may either be deleted or started anew with the Set() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * + * factory was not producing any object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Abandon(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + + if (Object) { + /* + ** Refund all money expended so far, back to the owner of the object under construction. + */ + int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; + House->Refund_Money(money - Balance); + Balance = 0; + + /* + ** Delete the object under construction. + */ + ScenarioInit++; + delete Object; + Object = NULL; + ScenarioInit--; + } + if (SpecialItem) { + SpecialItem = SPC_NONE; + } + + /* + ** Set the factory back to the idle and empty state. + */ + Set_Rate(0); + Set_Stage(0); + IsSuspended = true; + IsDifferent = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * * + * Use this routine to determine what animation (or completion step) the factory is * + * currently on. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a completion step number between 0 (uncompleted), to STEP_COUNT (completed)* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Completion(void) +{ + assert(Factories.ID(this) == ID); + + return(Fetch_Stage()); +} + + +/*********************************************************************************************** + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * * + * Use this routine to examine the factory object in order to determine if the associated * + * object has completed production and is awaiting placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + return(true); + } + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * * + * This routine gets the pointer to the currently constructing object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object undergoing construction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * FactoryClass::Get_Object(void) const +{ + assert(Factories.ID(this) == ID); + + return(Object); +} + + +/*************************************************************************** + * FactoryClass::Get_Special_Item -- gets factory spc prod item * + * * + * INPUT: none * + * * + * OUTPUT: int the item the factory is currently working on * + * * + * HISTORY: * + * 05/05/1995 PWG : Created. * + *=========================================================================*/ +int FactoryClass::Get_Special_Item(void) const +{ + assert(Factories.ID(this) == ID); + + return(SpecialItem); +} + + +/*********************************************************************************************** + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * * + * Use this routine to determine the cost per game "tick" to produce the object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of credits necessary to advance production one game tick. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Cost_Per_Tick(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + int steps = STEP_COUNT - Fetch_Stage(); + if (steps) { + return(Balance / steps); + } + return(Balance); + } + return(0); +} + + +/*********************************************************************************************** + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * * + * This routine is called after production completes, and the object produced has been * + * placed into the game. It resets the factory for deletion or starting of new production. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * + * any completed object. An immediate second call to this routine will also * + * yield false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + Object = NULL; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + SpecialItem = SPC_NONE; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + return(false); +} diff --git a/REDALERT/FACTORY.H b/REDALERT/FACTORY.H new file mode 100644 index 000000000..c2517003d --- /dev/null +++ b/REDALERT/FACTORY.H @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACTORY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : December 26, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "stage.h" + +class FactoryClass : private StageClass +{ + public: + RTTIType RTTI; + int ID; + + + FactoryClass(void); + FactoryClass(NoInitClass const & x) : StageClass(x) {}; + ~FactoryClass(void); + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + static void Init(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Abandon(void); + bool Completed(void); + bool Has_Changed(void); + bool Has_Completed(void); + bool Is_Building(void) const {return(Fetch_Rate() != 0);}; + bool Set(TechnoTypeClass const & object, HouseClass & house); + bool Set(int const & type, HouseClass & house); + bool Start(void); + bool Suspend(void); + int Completion(void); + TechnoClass * Get_Object(void) const; + int Get_Special_Item(void) const; + void AI(void); + void Set(TechnoClass & object); + HouseClass * Get_House(void) {return(House);}; + char const * Name(void) {return("Factory");} + bool Is_Blocked(void) {return IsBlocked;} + void Set_Is_Blocked(bool set) {IsBlocked = set;} + + /* + ** Added for debugging / testing. ST - 8/23/2019 3:52PM + */ + void Force_Complete(void); + + /* + ** This flag is used to maintain the pool of factory class objects. If the object has + ** been allocated, then this flag is true. Otherwise, the object is free to be + ** allocated. + */ + unsigned IsActive:1; + + enum StepCountEnum { + STEP_COUNT=54 // Number of steps to break production down into. + }; + protected: + + int Cost_Per_Tick(void); + + private: + + /* + ** If production is temporarily suspended, then this flag will be true. A factory + ** is suspended when it is first created, when production has completed, and when + ** explicitly instructed to Suspend() production. Suspended production is not + ** abandoned. It may be resumed with a call to Start(). + */ + unsigned IsSuspended:1; + + /* + ** If the AI process detected that the production process has advanced far enough + ** that a change in the building animation would occur, this flag will be true. + ** Examination of this flag (through the Has_Changed function) allows intelligent + ** updating of any production graphic. + */ + unsigned IsDifferent:1; + + /* + ** The exit from the factory is blocked by something, which means a unit is prevented from exiting after construction + ** has completed. ST - 2/25/2020 11:29AM + */ + unsigned IsBlocked:1; + + /* + ** This records the balance due on the current production item. This value will + ** be reduced as production proceeds. It will reach zero the moment production has + ** finished. Using this method ensures that the total production cost will be EXACT + ** regardless of the number of installment payments that are made. + */ + int Balance; + int OriginalBalance; + + /* + ** This is the object that is being produced. It is held in a state of limbo while + ** undergoing production. Since the object is created at the time production is + ** started, it is always available when production completes. + */ + TechnoClass * Object; + + /* + ** If the factory is not producing an object and is instead producing + ** a special item, then special item will be set. + */ + int SpecialItem; + + /* + ** The factory has to be doing production for one house or another. + ** The house pointer will point to whichever house it is being done + ** for. + */ + HouseClass * House; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/FAKESOCK.H b/REDALERT/FAKESOCK.H new file mode 100644 index 000000000..f36a7e3c8 --- /dev/null +++ b/REDALERT/FAKESOCK.H @@ -0,0 +1,50 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : FAKESOCK.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 6th, 1996 * + * * + * Last Update : August 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Stub replacement for the Winsock interface in DOS * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WIN32 + +class TcpipManagerClass { + + public: + inline BOOL Get_Connected(void) {return (FALSE);} + +}; + +extern TcpipManagerClass Winsock; +#endif diff --git a/REDALERT/FIELD.CPP b/REDALERT/FIELD.CPP new file mode 100644 index 000000000..8421ec835 --- /dev/null +++ b/REDALERT/FIELD.CPP @@ -0,0 +1,211 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include "field.h" + +FieldClass::FieldClass(char *id, char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, char *data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Convert the variables to host order. This needs to be converted so + // the switch statement does compares on the data that follows. + // + Size = ntohs(Size); + + DataType = ntohs(DataType); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} + diff --git a/REDALERT/FIELD.H b/REDALERT/FIELD.H new file mode 100644 index 000000000..d35b0c534 --- /dev/null +++ b/REDALERT/FIELD.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __FIELD_H +#define __FIELD_H + +#include +#include + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass { + + public: + friend class PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + void *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + +#endif diff --git a/REDALERT/FILEPCX.H b/REDALERT/FILEPCX.H new file mode 100644 index 000000000..0fa46958f --- /dev/null +++ b/REDALERT/FILEPCX.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/REDALERT/FINDPATH.CPP b/REDALERT/FINDPATH.CPP new file mode 100644 index 000000000..2a3107d16 --- /dev/null +++ b/REDALERT/FINDPATH.CPP @@ -0,0 +1,1307 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FINDPATH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FINDPATH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 25, 1995 [PWG] * + * * + * The path algorithm works by following a LOS path to the target. If it * + * collides with an impassable spot, it uses an Edge following routine to * + * get around it. The edge follower moves along the edge in a clockwise or * + * counter clockwise fashion until finding the destination spot. The * + * destination is determined by Find_Path. It is the first passable that * + * can be reached (so it will handle the doughnut case, where there is * + * a passable in the center of an unreachable area). * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Path_Overlap -- clears the path overlap list * + * Find_Path -- Find a path from point a to point b. * + * Find_Path_Cell -- Finds a given cell on a specified path * + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * Get_New_XY -- Get the new x,y based on current position and direction. * + * Optimize_Moves -- Optimize the move list. * + * Set_Path_Overlap -- Sets the overlap bit for given cell * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include + +/* +** When an edge search is started, it can be performed CLOCKwise or +** COUNTERCLOCKwise direction. +*/ +#define CLOCK (FacingType)1 // Clockwise. +#define COUNTERCLOCK (FacingType)-1 // Counterclockwise. + +/* +** If defined, diagonal moves are allowed, else no diagonals. +*/ +#define DIAGONAL + +/* +** This is the marker to signify the end of the path list. +*/ +#define END FACING_NONE + +/* +** "- 1" test for bit manipulation. +*/ +#define TEST + +/* +** If memory is more important than speed, set this define to +** true. It will then perform intermediate optimizations to get the most +** milage out of a limited movement list staging area. If this value +** is true then it figures paths a bit more intelligently. +*/ +#define SAVEMEM true + +/* +** Modify this macro so that given two cell values, it will return +** a value between 0 and 7, with 0 being North and moving +** clockwise (just like map degrees). +*/ +#define CELL_FACING(a, b) Dir_Facing(::Direction((a), (b))) + + +/*-------------------------------------------------------------------------*/ +/* +** Cells values are really indexes into the 'map'. The following value is +** the X width of the map. +*/ +#define MODULO MAP_CELL_W + +/* +** Maximum lookahead cells. Twice this value in bytes will be +** reserved on the stack. The smaller this number, the faster the processing. +*/ +#define MAX_MLIST_SIZE 300 +#define THREAT_THRESHOLD 5 + + +#define MAX_PATH_EDGE_FOLLOW 400 + +#ifdef NEVER +typedef enum { + FACING_N, // North + FACING_NE, // North-East + FACING_E, // East + FACING_SE, // South-East + FACING_S, // South + FACING_SW, // South-West + FACING_W, // West + FACING_NW, // North-West + + FACING_COUNT // Total of 8 directions (0..7). +} FacingType; +#endif + + +/*-------------------------------------------------------------------------*/ +//static bool DrawPath; + +inline FacingType Opposite(FacingType face) +{ + return( (FacingType) (face ^ 4)); +} + + +inline static FacingType Next_Direction(FacingType facing, FacingType dir) +{ + facing = facing + dir; + #ifndef DIAGONAL + facing = (FacingType)(facing & 0x06); + #endif + return(facing); +} + +/*=========================================================================*/ +/* Define a couple of variables which are private to the module they are */ +/* declared in. */ +/*=========================================================================*/ +static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path +static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path +static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path + + +//static CELL MoveMask = 0; +static CELL DestLocation; +static CELL StartLocation; + +/*************************************************************************** + * Point_Relative_To_Line -- Relation between a point and a line * + * * + * If a point is on a line then the following function holds true: * + * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * + * line (x1,z1),(x2,z2). * + * If the right side is > then the left side then the point is on one * + * side of the line and if the right side is < the the left side, then* + * the point is on the other side of the line. By subtracting one side* + * from the other we can determine on what side (if any) the point is on* + * by testing the side of the resulting subtraction. * + * * + * INPUT: * + * int x - x pos of point. * + * int z - z pos of point. * + * int x1 - x pos of first end of line segment. * + * int z1 - z pos of first end of line segment. * + * int x1 - x pos of second end of line segment. * + * int z1 - z pos of second end of line segment. * + * * + * OUTPUT: * + * Assuming (x1,z1) is north, (x2,z2) is south: * + * 0 : point is on line. * + * > 0 : point is east of line. * + * < 0 : point is west of line. * + * * + * WARNINGS: * + * Remember that int means that assumes 16 bits of precision. * + * * + * HISTORY: * + * 10/28/1994 SKB : Created. * + *=========================================================================*/ +int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) +{ + return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); +} + + +/*************************************************************************** + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * * + * While in the midst of the Follow Edge logic, it is possible (due to the * + * fact that we support diagonal movement) to begin looping around a * + * column of some type. The Unravel loop function will scan backward * + * through the list and fixup the path to try to prevent the loop. * + * * + * INPUT: path - pointer to the generated path so we can pull the * + * commands out of it. * + * cell - the cell we tried to enter that generated the * + * double overlap condition. * + * dir - the direction we tried to enter from when we * + * generated the double overlap condition * + * startx - the start x position of this path segment * + * starty - the start y position of this path segment * + * destx - the dest x position for this path segment * + * desty - the dest y position for this path segment * + * * + * OUTPUT: TRUE - loop has been successfully unravelled * + * FALSE - loop can not be unravelled so abort follow edge * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/25/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Unravel_Loop(PathType * path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) +{ + /* + ** Walk back to the actual cell before we advanced our position + */ + FacingType curr_dir = dir; + CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); + int idx = path->Length; // start at the last position + FacingType * list = &path->Command[idx-1]; // point to the last command + int checkx; + int checky; + int last_was_line = false; + + /* + ** loop backward through the list searching for a point that is + ** on the line. If the point was a diagonal move then adjust + ** it. + */ + while (idx) { + checkx = Cell_X(curr_pos); + checky = Cell_Y(curr_pos); + + if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { + + /* + ** We have now found a point on the line. Now we must check to see + ** if we left the line on a diagonal. If we did then we need to fix + ** it up. + */ + if (curr_dir & 1 && curr_pos != path->LastFixup) { + cell = curr_pos; + dir = *(list-1); + path->Length = idx; + path->LastFixup = curr_pos; + return(true); + } + + last_was_line = !last_was_line; + } + + /* + ** Since this cell will not be in the list, then pull out its cost + */ + path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); + + /* + ** Remove this cells flag from the overlap list for the path + */ +#ifdef TEST + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31))); +#else + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); +#endif + + /* + ** Adjust to the next list position and direction. + */ + curr_dir = *list--; + curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); + idx--; + } + + /* + ** If we can't modify the list to eliminate the problem, then we have + ** a larger problem in that we have deleted all of the cells in the + ** list. + */ + return(false); +} + + +/*************************************************************************** + * Register_Cell -- registers a cell on our path and check for backtrack * + * * + * This function adds a new cell to our path. If the cell has already * + * been recorded as part of our path, then this function moves back down * + * the list truncating it at the point we registered that cell. This * + * function will eliminate all backtracking from the list. * + * * + * INPUT: long * list - the list to set the overlap bit for * + * CELL cell - the cell to mark on the overlap list * + * * + * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * + * * + * HISTORY: * + * 05/23/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Register_Cell(PathType * path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType * list; + int pos = cell >> 5; +#ifdef TEST + int bit = (cell & 31); +#else + int bit = (cell & 31) - 1; +#endif + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + path->Length--; + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + int idx = 0; + list = path->Command; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words). + ** + ** PWG 8/16/95 - However there is no sense searching the list if + ** the cell we have overlapped on is the cell we + ** started in. + */ + + if (pos != cell) { + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + idx++; + list++; + } + newlen = idx; + } + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} + + +/*********************************************************************************************** + * Find_Path -- Find a path from point a to point b. * + * * + * INPUT: int source x,y, int destination x,y, char *final moves * + * array to store moves, int maximum moves we may attempt * + * * + * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * + * not reach the destination * + * * + * WARNINGS: This algorithm assumes that the target is NOT situated * + * inside an impassable. If this case may arise, the do-while * + * statement inside the inner while (true) must be changed * + * to include a check to se if the next_x,y is equal to the * + * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + *=============================================================================================*/ +PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, MoveType threshhold) +{ + CELL source = Coord_Cell(Coord); // Source expressed as cell + static PathType path; // Main path control. + CELL next; // Next cell to enter + CELL startcell; // Cell we started in + FacingType direction; // Working direction of look ahead. + FacingType newdir; // Tentative facing value. + + bool left=false, // Was leftward path legal? + right=false; // Was rightward path legal? + + int len; // Length of detour command list. + int unit_threat; // Calculated unit threat rating + int cost; // Cost to enter the square + FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. + moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. + PathType pleft,pright; // Path control structures. + PathType * which; // Which path to actually use. + int threat = 0; // + int threat_stage = 0; //These weren't initialized. ST - 1/8/2019 12:03PM + + + /* + ** If we have been provided an illegal place to store our final moves + ** then forget it. + */ + if (!final_moves) return(NULL); + + BStart(BENCH_FINDPATH); + + PathCount++; + + if (Team && Team->Class->IsRoundAbout) { + unit_threat = (Team) ? Team->Risk : Risk(); + threat_stage = 0; + threat = 0; + } else { + unit_threat = threat = -1; + } + + StartLocation = source; + DestLocation = dest; + + /* + ** Initialize the path structure so that we can keep track of the + ** path. + */ + path.Start = source; + path.Cost = 0; + path.Length = 0; + path.Command = final_moves; + path.Command[0] = END; + path.Overlap = MainOverlap; + path.LastOverlap = -1; + path.LastFixup = -1; + + memset(path.Overlap, 0, sizeof(MainOverlap)); + + /* + ** Clear the over lap list and then make sure that our starting position is marked + ** on the overlap list. (Otherwise the harvesters will drive in circles... ) + */ +#ifdef TEST + path.Overlap[source >> 5] |= (1 << ((source & 31))); +#else + path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); +#endif + + startcell = source; + + /* + ** Account for trailing end of list command, so reduce the maximum + ** allowed legal commands to reflect this. + */ + maxlen--; + + /* + ** As long as there is room to put commands in the movement command list, + ** then put commands in it. We build the path using the following + ** methodology. + ** + ** 1. Scan through the desired straight line path until we either hit an + ** impassable or have created a valid path. + ** + ** 2. If we have hit an impassable, walk through the impassable to make + ** sure that there is a passable on the other side. If there is not + ** and we can not change the impassable, then this list is dead. + ** + ** 3. Walk around the impassable on both the left and right edges and + ** take the shorter of the two paths. + ** + ** 4. Taking the new location as our start location start again with + ** step #1. + */ + while (path.Length < maxlen) { + +top_of_list: + /* + ** Have we reached the destination already? If so abort any further + ** command building. + */ + if (startcell == dest) { + break; + } + + /* + ** Find the absolute correct direction to reach the next straight + ** line cell and what cell it is. + */ + direction = CELL_FACING(startcell, dest); + next = Adjacent_Cell(startcell, direction); + + /* + ** If we can move here, then make this our next move. + */ + cost = Passable_Cell(next, direction, threat, threshhold); + if (cost) { + Register_Cell(&path, next, direction, cost, threshhold); + } else { + /* + ** If the impassable location is actually the destination, + ** then stop here and consider this "good enough". + */ + if (next == dest) break; + + /* + ** We could not move to the next cell, so follow through the + ** impassable until we find a passable spot that can be reached. + ** Once we find a passable, figure out the shortest path to it. + ** Since we have variable passable conditions this is not as + ** simple as it used to be. The limiter loop below allows us to + ** step through ten doughnuts before we give up. + */ + for (int limiter = 0; limiter < 5; limiter++) { + + /* + ** Get the next passable position by zipping through the + ** impassable positions until a passable position is found + ** or the destination is reached. + */ + for (;;) { + + /* + ** Move one step closer toward destination. + */ + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + + /* + ** If the cell is passable then we have been completely + ** successful. If the cell is not passable then continue. + */ + if (Passable_Cell(next, FACING_NONE, threat, threshhold)) { +// if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { + break; + } + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + } + + /* + ** Try to find a path to the passable position by following + ** the edge of the blocking object in both CLOCKwise and + ** COUNTERCLOCKwise fashions. + */ + int follow_len = maxlen + (maxlen >> 1); + + Mem_Copy(&path, &pleft, sizeof(PathType)); + pleft.Command = &moves_left[0]; + pleft.Overlap = LeftOverlap; + Mem_Copy(path.Command, pleft.Command, path.Length); + Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); + + // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; + // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; + // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn. + #if 0 + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left)/sizeof(moves_left[0]), threshhold); +// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); + #endif + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); + + + if (left) { + follow_len = min(maxlen, pleft.Length + (pleft.Length >> 1)); + } + + Mem_Copy(&path, &pright, sizeof(PathType)); + pright.Command = &moves_right[0]; + pright.Overlap = RightOverlap; + Mem_Copy(path.Command, pright.Command, path.Length); + Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); + + // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; + // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; + // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn. + #if 0 + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right)/sizeof(moves_right[0]), threshhold); +// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); + #endif + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); + + /* + ** If we could find a path, break from this loop. Otherwise this + ** means that we have found a "hole" of passable terrain that + ** cannot be reached by normal means. Scan forward looking for + ** the other side of the "doughnut". + */ + if (left || right) break; + + /* + ** If no path can be found to the intermediate cell, then + ** presume we have found a doughnut of some sort. Scan + ** forward until the next impassable is found and then + ** process this loop again. + */ + do { + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + } while (Passable_Cell(next, newdir, threat, threshhold)); + } + + if (!left && !right) break; + + /* + ** We found a path around the impassable locations, so figure out + ** which one was the smallest and copy those moves into the + ** path.Command array. + */ + which = &pleft; + if (right) { + which = &pright; + if (left) { + if (pleft.Length < pright.Length) { + which = &pleft; + } else { + which = &pright; + } + } + } + + /* + ** Record as much as possible of the shorter of the two + ** paths. The trailing EOL command is not copied because + ** this may not be the end of the find path logic. + */ + len = which->Length; + len = min(len, maxlen); + if (len > 0) { + memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); + memcpy(&path.Command[0], &which->Command[0], len * sizeof(FacingType)); + path.Length = len; + path.Cost = which->Cost; + path.LastOverlap = -1; + path.LastFixup = -1; + } else { + break; + } + } + startcell = next; + } + +end_of_list: + /* + ** Poke in the stop command. + */ + if (path.Length < maxlen) { + path.Command[path.Length++] = END; + } + + /* + ** Optimize the move list but only necessary if + ** diagonal moves are allowed. + */ + #ifdef DIAGONAL + Optimize_Moves(&path, threshhold); + #endif + + BEnd(BENCH_FINDPATH); + + return(&path); +} + + +/*********************************************************************************************** + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * * + * INPUT: start -- cell to head from * + * * + * target -- Target cell to head to. * + * * + * path -- Pointer to path list structure. * + * * + * search -- Direction of search (1=clock, -1=counterclock). * + * * + * olddir -- Facing impassible direction from start. * + * * + * callback -- Function pointer for determining if a cell is * + * passable or not. * + * * + * OUTPUT: bool: Could a path be found to the desired cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized & commented. * + *=============================================================================================*/ +bool FootClass::Follow_Edge(CELL start, CELL target, PathType * path, FacingType search, FacingType olddir, int threat, int , int max_cells, MoveType threshhold) +{ + FacingType newdir; // Direction of facing before surrounding cell check. + CELL oldcell, // Current cell. + newcell; // Tentative new cell. + int cost; // Working cost value. + int startx; + int starty; + int online=true; + int targetx; + int targety; + int oldval = 0; + int cellcount=0; + int forceout = false; + FacingType firstdir = (FacingType)-1; + CELL firstcell = -1; + bool stepped_off_line = false; + startx = Cell_X(start); + starty = Cell_Y(start); + targetx = Cell_X(target); + targety = Cell_Y(target); + + if (!path) return(false); + path->LastOverlap = -1; + path->LastFixup = -1; + + #ifndef DIAGONAL + /* + ** The edge following algorithm doesn't "do" diagonals. Force initial facing + ** to be an even 90 degree value. Adjust it in the direction it should be + ** rotating. + */ + if (olddir & 0x01) { + olddir = Next_Direction(olddir, search); + } + #endif + + newdir = Next_Direction(olddir, search); + oldcell = start; + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** Continue until we find our target, find our original starting spot, + ** or run out of moves. + */ + while (path->Length < max_cells) { + + /* + ** Look in all the adjacent cells to determine a passable one that + ** most closely matches the desired direction (working in the specified + ** direction). + */ + newdir = olddir; + for (;;) { + bool forcefail; // Is failure forced? + + forcefail = false; + + #ifdef DIAGONAL + /* + ** Rotate 45/90 degrees in desired direction. + */ + newdir = Next_Direction(newdir, search); + + /* + ** If facing a diagonal we must check the next 90 degree location + ** to make sure that we don't walk right by the destination. This + ** will happen if the destination it is at the corner edge of an + ** impassable that we are moving around. + */ + if (newdir & FACING_NE) { + CELL checkcell; // Non-diagonal check cell. + //int x,y; + + checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); + + if (checkcell == target) { + + /* + ** This only works if in fact, it is possible to move to the + ** cell from the current location. + */ + cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); + if (cost) { + /* + ** YES! The destination is at the corner of an impassable, so + ** set the direction to point directly at it and then the + ** scanning will terminate later. + */ + newdir = Next_Direction(newdir, search); + newcell = Adjacent_Cell(oldcell, newdir); + break; + } + } + + /* + ** Perform special diagonal check. If the edge follower would cross the + ** diagonal or fall on the diagonal line from the source, then consider + ** that cell impassible. Otherwise, the find path algorithm will fail + ** when there are two impassible locations located on a diagonal + ** that is lined up between the source and destination location. + ** + ** P.S. It might help if you check the right cell rather than using + ** the value that just happened to be in checkcell. + */ + + checkcell = Adjacent_Cell(oldcell, newdir); + + int checkx = Cell_X(checkcell); + int checky = Cell_Y(checkcell); + int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); + if (checkval && !online) { + forcefail = ((checkval ^ oldval) < 0); + } else { + forcefail = false; + } + /* + ** The only exception to the above is when we are directly backtracking + ** because we could be trying to escape from a culdesack! + */ + if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { + forcefail = false; + } + } + + #else + newdir = Next_Direction(newdir, search*2); + #endif + + /* + ** If we have just checked the same heading we started with, + ** we are surrounded by impassable characters and we exit. + */ + if (newdir == olddir) { + return(false); + } + + /* + ** Get the new cell. + */ + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** If we found a passable position, this is where we should move. + */ + if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { + break; + } else { + if (newcell == target) { + forceout = true; + break; + } + } + + } + + /* + ** Record the direction. + */ + if (!forceout) { + /* + ** Mark the cell because this is where we need to be. If register + ** cell fails then the list has been shortened and we need to adjust + ** the new direction. + */ + if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { + /* + ** The only reason we could not register a cell is that we are in + ** a looping situation. So we need to try and unravel the loop if + ** we can. + */ + if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { + return(false); + } + /* + ** Since we need to eliminate a diagonal we must pretend the upon + ** attaining this square, we were moving turned further in the + ** search direction then we really were. + */ + newdir = Next_Direction(newdir, (FacingType)(search*2)); + } + /* + ** Find out which side of the line this cell is on. If it is on + ** a side, then store off that side. + */ + int newx = Cell_X(newcell); + int newy = Cell_Y(newcell); + int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); + if (val) { + oldval = val; + online = false; + } else { + online = true; + } + cellcount++; + if (cellcount == MAX_PATH_EDGE_FOLLOW) { + return(false); + } + } + + /* + ** If we have found the target spot, we are done. + */ + if (newcell == target) { + path->Command[path->Length] = END; + return(true); + } + + /* + ** If we make a full circle back to our original spot, get out. + */ + if (newcell == firstcell && newdir == firstdir) { + return(false); + } + + if (firstcell == -1) { + firstcell = newcell; + firstdir = newdir; + } + + /* + ** Because we moved, our facing is now incorrect. We want to face toward + ** the impassable edge we are following (well, not actually toward, but + ** a little past so that we can turn corners). We have to turn 45/90 degrees + ** more than expected in anticipation of the pending 45/90 degree turn at + ** the start of this loop. + */ + #ifdef DIAGONAL + olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); + #else + olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); + #endif + oldcell = newcell; + } + + /* + ** The maximum search path is exhausted... abort with a failure. + */ + return(false); +} + + +/*********************************************************************************************** + * Optimize_Moves -- Optimize the move list. * + * * + * INPUT: char *moves to optimize * + * * + * OUTPUT: none (list is optimized) * + * * + * WARNINGS: EMPTY moves are used to hold the place of eliminated * + * commands. Also, NEVER call this routine with a list that * + * contains illegal commands. The list MUST be terminated * + * with a EOL command * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized and commented. * + *=============================================================================================*/ +#define EMPTY (FacingType)-2 +int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) +//int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) +{ + /* + ** Facing command pair adjustment table. Compare the facing difference between + ** the two commands. 0 means no optimization is possible. 3 means backtracking + ** so eliminate both commands. Any other value adjusts the first command facing. + */ +#ifdef DIAGONAL + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. +#else + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; +#endif + FacingType * cmd1, // Floating first command pointer. + * cmd2, // Floating second command pointer. + newcmd; // Calculated new optimized command. + FacingType newdir; // Tentative new direction for smoothing. + CELL cell; // Working cell (as it moves along path). + + /* + ** Abort if there is any illegal parameter. + */ + if (!path || !path->Command) return(0); + + /* + ** Optimization loop -- start scanning with the + ** first pair of commands (if there are at least two + ** in the command list). + */ + path->Command[path->Length] = END; // Force end of list. + + if (path->Length == 0) return(0); + + cell = path->Start; + if (path->Length > 1) { + cmd2 = path->Command + 1; + while (*cmd2 != END) { + + /* + ** Set the cmd1 pointer to point to the valid command closest, but + ** previous to cmd2. Be sure not to go previous to the head of the + ** command list. + */ + cmd1 = cmd2-1; + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + + /* + ** If there isn't any valid previous command, then bump the + ** cmd pointers to the next command pair and continue... + */ + if (*cmd1 == EMPTY) { + cmd2++; + continue; + } + + /* + ** Fetch precalculated command change value. 0 means leave + ** command set alone, 3 means backtrack and eliminate two + ** commands. Any other value is new direction and eliminate + ** one command. + */ + newcmd = (FacingType)(*cmd2 - *cmd1); + if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); + newcmd = _trans[newcmd]; + + /* + ** Check for backtracking. If this occurs, then eliminate the + ** two commands. This is the easiest optimization. + */ + if (newcmd == FACING_SE) { + *cmd1 = EMPTY; + *cmd2++ = EMPTY; + continue; + } + + /* + ** If an optimization code was found the process it. The command is a facing + ** offset to more directly travel toward the immediate destination cell. + */ + if (newcmd) { + + /* + ** Optimizations differ when dealing with diagonals. Especially when dealing + ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can + ** only be optimized if the intervening cell is passable. The distance travelled + ** is the same, but the path is less circuitous. + */ + if (*cmd1 & FACING_NE) { + + /* + ** Diagonal optimizations are always only 45 + ** degree adjustments. + */ + newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); + + /* + ** Diagonal 90 degree changes can be smoothed, although + ** the path isn't any shorter. + */ + if (ABS((int)newcmd) == 1) { + if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { + *cmd2 = newdir; + *cmd1 = newdir; + } + // BOB 16.12.92 + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + continue; + } + } else { + newdir = Next_Direction(*cmd1, newcmd); + } + + /* + ** Allow shortening turn only on right angle moves that are based on + ** 90 degrees. Always allow 135 degree optimizations. + */ + *cmd2 = newdir; + *cmd1 = EMPTY; + + /* + ** Backup what it thinks is the current cell. + */ + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + if (*cmd1 != EMPTY) { + cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); + } else { + cell = path->Start; + } + continue; + } + + /* + ** Since we could not make an optimization, we move our + ** head pointer forward. + */ + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + } + } + + /* + ** Pack the command list to remove any EMPTY command entries. + */ + cmd1 = path->Command; + cmd2 = path->Command; + cell = path->Start; + path->Cost = 0; + path->Length = 0; + while (*cmd2 != END) { + if (*cmd2 != EMPTY) { + cell = Adjacent_Cell(cell, *cmd2); + path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); + path->Length++; + *cmd1++ = *cmd2; + } + cmd2++; + } + path->Length++; + *cmd1 = END; + return(path->Length); +} + + +CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) +{ + FacingType dir; + CELL next; + int lp; + + dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; + + /* + ** Loop through the different acceptable distances. + */ + for (int dist = start; dist < max; dist ++) { + + /* + ** Move to the starting location. + */ + next = dst; + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir); + } + + if (dir & 1) { + /* + ** If our direction is diagonal than we need to check + ** only one side which is as long as both of the old sides + ** together. + */ + for (lp = 0; lp < dist << 1; lp ++) { + next = Adjacent_Cell(next, dir + 3); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } else { + /* + ** If our direction is not diagonal than we need to check two + ** sides so that we are checking a corner like location. + */ + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 2); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 4); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } + } + return(-1); +} + + + + +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +{ + MoveType move = Can_Enter_Cell(cell, face); + + if (move < MOVE_MOVING_BLOCK && Distance(Cell_Coord(cell)) > 0x0100) threshhold = MOVE_MOVING_BLOCK; + + if (move > threshhold) return(0); + + if (Session.Type == GAME_NORMAL) { + if (threat != -1) { + if (::Distance(Cell_Coord(cell), Cell_Coord(DestLocation)) > (THREAT_THRESHOLD * CELL_LEPTON_W)) { +// if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + } + + static int _value[MOVE_COUNT] = { + 1, // MOVE_OK + 1, // MOVE_CLOAK + 3, // MOVE_MOVING_BLOCK + 8, // MOVE_DESTROYABLE + 10, // MOVE_TEMP + 0 // MOVE_NO + }; + return(_value[move]); +} + diff --git a/REDALERT/FIXED.CPP b/REDALERT/FIXED.CPP new file mode 100644 index 000000000..e8add29a3 --- /dev/null +++ b/REDALERT/FIXED.CPP @@ -0,0 +1,245 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FIXED.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FIXED.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/20/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "fixed.h" +#include +#include +#include +#include + + +/* +** These are some handy fixed point constants. Using these constants instead of manually +** constructing them is not only faster, but more readable. +*/ +const fixed fixed::_1_2(1, 2); // 1/2 +const fixed fixed::_1_3(1, 3); // 1/3 +const fixed fixed::_1_4(1, 4); // 1/4 +const fixed fixed::_3_4(3, 4); // 3/4 +const fixed fixed::_2_3(2, 3); // 2/3 + + +fixed::fixed(int numerator, int denominator) +{ + if (denominator == 0) { + Data.Raw = 0U; + } else { + Data.Raw = (unsigned int)(((unsigned __int64)numerator * PRECISION) / denominator); + } +} + + +/*********************************************************************************************** + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * * + * This will parse the ASCII initialization string into a fixed point number. * + * The source string can be a conventional fixed point representation (e.g., "1.0", ".25") * + * or a percent value (e.g. "100%", "25%", "150%"). For percent values, the trailing "%" * + * is required. * + * * + * INPUT: ascii -- Pointer to the ascii source to translate into a fixed point number. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to specify an ASCII string that has more precision and * + * magnitude than can be represented by the fixed point number. In such a case, * + * the resulting value is undefined. * + * * + * HISTORY: * + * 06/20/1996 JLB : Created. * + *=============================================================================================*/ +fixed::fixed(char const * ascii) +{ + /* + ** If there is no valid pointer, then default to zero value. This takes care of any + ** compiler confusion that would call this routine when the programmer wanted the + ** integer parameter constructor to be called. + */ + if (ascii == NULL) { + Data.Raw = 0U; + return; + } + + /* + ** The whole part (if any) always starts with the first legal characters. + */ + char const * wholepart = ascii; + + /* + ** Skip any leading white space. + */ + while (isspace(*ascii)) { + ascii++; + } + + /* + ** Determine if the number is expressed as a percentage. Detect this by + ** seeing if there is a trailing "%" character. + */ + char const * tptr = ascii; + while (isdigit(*tptr)) { + tptr++; + } + + /* + ** Percentage value is specified as a whole number but is presumed to be + ** divided by 100 to get mathematical fixed point percentage value. + */ + if (*tptr == '%') { // Removed '/' preceding '%'. ST - 5/8/2019 + Data.Raw = (unsigned int)(((unsigned __int64)atoi(ascii) * PRECISION) / 100ULL); + } else { + + Data.Composite.Whole = Data.Composite.Fraction = 0U; + if (wholepart && *wholepart != '.') { + Data.Composite.Whole = (unsigned short)atoi(wholepart); + } + + const char * fracpart = strchr(ascii, '.'); + if (fracpart) fracpart++; + if (fracpart) { + unsigned int frac = (unsigned int)atoi(fracpart); + + int len = 0; + unsigned int base = 1; + char const * fptr = fracpart; + while (isdigit(*fptr)) { + fptr++; + len++; + base *= 10U; + } + + Data.Composite.Fraction = (unsigned short)(((unsigned __int64)frac * PRECISION) / base); + } + } +} + + +/*********************************************************************************************** + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * * + * Use this routine to convert this fixed point number into an ASCII null terminated * + * string. This is the counterpart to the fixed point constructor that takes an ASCII * + * string. * + * * + * INPUT: buffer -- Pointer to the buffer to hold the fixed point ASCII string. * + * * + * maxlen -- The length of the buffer. * + * * + * OUTPUT: Returns with the number of characters placed in the buffer. The trailing null is * + * not counted in this total. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int fixed::To_ASCII(char * buffer, int maxlen) const +{ + if (buffer == NULL) return(0); + + /* + ** Determine the whole and fractional parts of the number. The fractional + ** part number is the value in 1000ths. + */ + unsigned int whole = Data.Composite.Whole; + unsigned int frac = ((unsigned int)Data.Composite.Fraction * 1000U) / PRECISION; + char tbuffer[32]; + + /* + ** If there number consists only of a whole part, then the number is simply + ** printed into the buffer. If there is a fractional part, then there + ** will be a decimal place followed by up to three digits of accuracy for the + ** fractional component. + */ + if (frac == 0) { + sprintf(tbuffer, "%u", whole); + } else { + sprintf(tbuffer, "%u.%02u", whole, frac); + + char * ptr = &tbuffer[strlen(tbuffer)-1]; + while (*ptr == '0') { + *ptr = '\0'; + ptr--; + } + } + + /* + ** If no maximum length to the output buffer was specified, then presume the + ** output buffer is just long enough to store the number and the trailing + ** zero. + */ + if (maxlen == -1) { + maxlen = strlen(tbuffer)+1; + } + + /* + ** Fill the output buffer with the ASCII number. + */ + strncpy(buffer, tbuffer, maxlen); + + /* + ** Return with the number of ASCII characters placed into the output buffer. + */ + int len = strlen(tbuffer); + if (len < maxlen-1) return(len); + return(maxlen-1); +} + + +/*********************************************************************************************** + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * * + * This number will be converted into an ASCII string (using a static buffer) and the * + * string pointer will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the ASCII representation of this fixed point number. * + * * + * WARNINGS: As with all static return pointers, the pointer is valid only until such time * + * as this routine is called again. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +char const * fixed::As_ASCII(void) const +{ + static char buffer[32]; + + To_ASCII(buffer, sizeof(buffer)); + return(buffer); +} diff --git a/REDALERT/FIXED.H b/REDALERT/FIXED.H new file mode 100644 index 000000000..b141508c2 --- /dev/null +++ b/REDALERT/FIXED.H @@ -0,0 +1,234 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FIXED.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FIXED.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/19/96 * + * * + * Last Update : June 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef FIXED_H +#define FIXED_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +//#pragma warning 604 9 +//#pragma warning 595 9 + +/* +** This is a very simple fixed point class that functions like a regular integral type. However +** it is under certain restrictions. The whole part must not exceed 65535. The fractional part is +** limited to an accuracy of 1/65536. It cannot represent or properly handle negative values. It +** really isn't all that fast (if an FPU is guaranteed to be present than using "float" might be +** more efficient). It doesn't detect overflow or underflow in mathematical or bit-shift operations. +** +** Take careful note that the normal mathematical operators return integers and not fixed point +** values if either of the components is an integer. This is the normal C auto-upcasting rule +** as it would apply presuming that integers are considered to be of higher precision than +** fixed point numbers. This allows the result of these operators to generate values with greater +** magnitude than is normally possible if the result were coerced into a fixed point number. +** If the result should be fixed point, then ensure that both parameters are fixed point. +** +** Note that although integers are used as the parameters in the mathematical operators, this +** does not imply that negative parameters are supported. The use of integers is as a convenience +** to the programmer -- constant integers are presumed signed. If unsigned parameters were +** specified, then the compiler would have ambiguous conversion situation in the case of constant +** integers (e.g. 1, 10, 32, etc). This is most important for the constructor when dealing with the +** "0" parameter case. In that situation the compiler might interpret the "0" as a null pointer rather +** than an unsigned integer. There should be no adverse consequences of using signed integer parameters +** since the precision/magnitude of these integers far exceeds the fixed point component counterparts. +** +** Note that when integer values are returns from the arithmetic operators, the value is rounded +** to the nearest whole integer value. This differs from normal integer math that always rounds down. +*/ +class fixed +{ + static constexpr unsigned int PRECISION = 1 << 16; + + public: + // The default constructor must not touch the data members in any way. + fixed(void) {} + + // Copy constructor + fixed(fixed const & rvalue) {Data.Raw = rvalue.Data.Raw;} + + // Convenient constructor if numerator and denominator components are known. + fixed(int numerator, int denominator); + + // Conversion constructor to get fixed point from integer. + fixed(int value) {Data.Composite.Fraction = 0U;Data.Composite.Whole = (unsigned short)value;} + fixed(unsigned int value) {Data.Composite.Fraction = 0U;Data.Composite.Whole = (unsigned short)value;} + + // Conversion constructor to get fixed point from floating-point. + fixed(float value) {value += 1.0f/(PRECISION<<1);Data.Composite.Fraction = (unsigned short)((value - (unsigned short)value) * PRECISION);Data.Composite.Whole = (unsigned short)value;} + + // Constructor if ASCII image of number is known. + fixed(char const * ascii); + + // Convert to integer when implicitly required. + operator unsigned (void) const {return(unsigned)(((unsigned __int64)Data.Raw+(PRECISION>>1)) / PRECISION);} + + /* + ** The standard operators as they apply to in-place operation. + */ + fixed & operator *= (fixed const & rvalue) {Data.Raw = (unsigned int)(((unsigned __int64)Data.Raw * rvalue.Data.Raw) / PRECISION);return(*this);} + fixed & operator *= (int rvalue) {Data.Raw *= (unsigned int)rvalue;return(*this);} + fixed & operator /= (fixed const & rvalue) {if (rvalue.Data.Raw != 0U && rvalue.Data.Raw != PRECISION) Data.Raw = (unsigned int)((((unsigned __int64)Data.Raw * PRECISION)+(PRECISION>>1)) / rvalue.Data.Raw);return(*this);} + fixed & operator /= (int rvalue) {if (rvalue) Data.Raw /= (unsigned int)rvalue;return(*this);} + fixed & operator += (fixed const & rvalue) {Data.Raw += rvalue.Data.Raw;return(*this);} + fixed & operator -= (fixed const & rvalue) {Data.Raw -= rvalue.Data.Raw;return(*this);} + + /* + ** The standard "My Dear Aunt Sally" operators. The integer versions of multiply + ** and divide are more efficient than using the fixed point counterparts. + */ + const fixed operator * (fixed const & rvalue) const { return fixed(*this) *= rvalue; } + const int operator * (int rvalue) const { return fixed(*this) *= rvalue; } + const fixed operator / (fixed const & rvalue) const { return fixed(*this) /= rvalue; } + const int operator / (int rvalue) const { return fixed(*this) /= rvalue; } + const fixed operator + (fixed const & rvalue) const { return fixed(*this) += rvalue; } + const int operator + (int rvalue) const { return fixed(*this) += rvalue; } + const fixed operator - (fixed const & rvalue) const { return fixed(*this) -= rvalue; } + const int operator - (int rvalue) const { return fixed(*this) -= rvalue; } + + /* + ** The Shift operators are more efficient than using multiplies or divides by power-of-2 numbers. + */ + fixed & operator >>= (unsigned rvalue) {Data.Raw >>= rvalue;return(*this);} + fixed & operator <<= (unsigned rvalue) {Data.Raw <<= rvalue;return(*this);} + const fixed operator >> (unsigned rvalue) const {return fixed(*this) >>= rvalue;} + const fixed operator << (unsigned rvalue) const {return fixed(*this) <<= rvalue;} + + /* + ** The full set of comparison operators. + */ + bool operator == (fixed const & rvalue) const {return(Data.Raw == rvalue.Data.Raw);} + bool operator != (fixed const & rvalue) const {return(Data.Raw != rvalue.Data.Raw);} + bool operator < (fixed const & rvalue) const {return(Data.Raw < rvalue.Data.Raw);} + bool operator > (fixed const & rvalue) const {return(Data.Raw > rvalue.Data.Raw);} + bool operator <= (fixed const & rvalue) const {return(Data.Raw <= rvalue.Data.Raw);} + bool operator >= (fixed const & rvalue) const {return(Data.Raw >= rvalue.Data.Raw);} + bool operator ! (void) const {return(Data.Raw == 0U);} + + /* + ** Comparison to integers requires consideration of fractional component. + */ + bool operator < (int rvalue) const {return(Data.Raw < ((unsigned int)rvalue*PRECISION));} + bool operator > (int rvalue) const {return(Data.Raw > ((unsigned int)rvalue*PRECISION));} + bool operator <= (int rvalue) const {return(Data.Raw <= ((unsigned int)rvalue*PRECISION));} + bool operator >= (int rvalue) const {return(Data.Raw >= ((unsigned int)rvalue*PRECISION));} + bool operator == (int rvalue) const {return(Data.Raw == ((unsigned int)rvalue*PRECISION));} + bool operator != (int rvalue) const {return(Data.Raw != ((unsigned int)rvalue*PRECISION));} + + /* + ** Friend functions to handle the alternate positioning of fixed and integer parameters. + */ + friend const int operator * (int lvalue, fixed const & rvalue) { return fixed(lvalue) * rvalue; } + friend const int operator / (int lvalue, fixed const & rvalue) { return fixed(lvalue) / rvalue; } + friend const int operator + (int lvalue, fixed const & rvalue) { return fixed(lvalue) + rvalue; } + friend const int operator - (int lvalue, fixed const & rvalue) { return fixed(lvalue) - rvalue; } + friend bool operator < (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) < rvalue; } + friend bool operator > (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) > rvalue; } + friend bool operator <= (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) <= rvalue; } + friend bool operator >= (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) >= rvalue; } + friend bool operator == (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) == rvalue; } + friend bool operator != (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) != rvalue; } + friend int operator *= (int & lvalue, fixed const & rvalue) { lvalue = lvalue * rvalue; return(lvalue); } + friend int operator /= (int & lvalue, fixed const & rvalue) { lvalue = lvalue / rvalue; return(lvalue); } + friend int operator += (int & lvalue, fixed const & rvalue) { lvalue = lvalue + rvalue; return(lvalue); } + friend int operator -= (int & lvalue, fixed const & rvalue) { lvalue = lvalue - rvalue; return(lvalue); } + + /* + ** Helper functions to handle simple and common operations on fixed point numbers. + */ + void Round_Up(void) {Data.Raw += (PRECISION-1U);Data.Composite.Fraction = 0U;} + void Round_Down(void) {Data.Composite.Fraction = 0U;} + void Round(void) {if (Data.Composite.Fraction >= PRECISION>>1) Round_Up();Round_Down();} + void Saturate(unsigned capvalue) {if (Data.Raw > (capvalue*PRECISION)) Data.Raw = capvalue*PRECISION;} + void Saturate(fixed const & capvalue) {if (*this > capvalue) *this = capvalue;} + void Sub_Saturate(unsigned capvalue) {if (Data.Raw >= (capvalue*PRECISION)) Data.Raw = (capvalue*PRECISION)-1U;} + void Sub_Saturate(fixed const & capvalue) {if (*this >= capvalue) Data.Raw = capvalue.Data.Raw-1U;} + void Inverse(void) {*this = fixed(1) / *this;} + + /* + ** Friend helper functions that work in the typical C fashion of passing the object to + ** be processed as a parameter to the function. + */ + friend const fixed Round_Up(fixed const & value) {fixed temp = value; temp.Round_Up();return(temp);} + friend const fixed Round_Down(fixed const & value) {fixed temp = value; temp.Round_Down();return(temp);} + friend const fixed Round(fixed const & value) {fixed temp = value; temp.Round();return(temp);} + friend const fixed Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Inverse(fixed const & value) {fixed temp = value;temp.Inverse();return(temp);} + + /* + ** Conversion of the fixed point number into an ASCII string. + */ + int To_ASCII(char * buffer, int maxlen=-1) const; + char const * As_ASCII(void) const; + + /* + ** Helper constants that provide some convenient fixed point values. + */ + static const fixed _1_2; + static const fixed _1_3; + static const fixed _1_4; + static const fixed _3_4; + static const fixed _2_3; + + private: + union { + struct { +#ifdef BIG_ENDIAN + unsigned short Whole; + unsigned short Fraction; +#else + unsigned short Fraction; + unsigned short Whole; +#endif + } Composite; + unsigned int Raw; + } Data; +}; + + +#endif diff --git a/REDALERT/FLASHER.CPP b/REDALERT/FLASHER.CPP new file mode 100644 index 000000000..f0fee927f --- /dev/null +++ b/REDALERT/FLASHER.CPP @@ -0,0 +1,130 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLASHER.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * * + * This utility function will output the current status of the FlasherClass to the mono * + * screen. It is through this display that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void FlasherClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(50, 7); + mono->Printf("%2d", FlashCount); +} +#endif + + +/*********************************************************************************************** + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * * + * The ability for an object to flash is controlled by this logic processing routine. It * + * should be called once per game tick per unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the associated object be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/20/1994 JLB : Is now independent of object it represents. * + *=============================================================================================*/ +bool FlasherClass::Process(void) +{ + // 2019/09/20 JAS - Flashing info needs to exist per player + for (int i = 0; i < HOUSE_COUNT; i++) + { + if (FlashCountPerPlayer[i]) + { + FlashCountPerPlayer[i]--; + } + } + + if (FlashCount) { + FlashCount--; + IsBlushing = false; + + if (FlashCount & 0x01) { + IsBlushing = true; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FlasherClass::Get_Flashing_Flags -- * + * * + * Gets the flags tell which players this object should flash for. * + * * + * INPUT: none * + * * + * OUTPUT: unsigned int; Flag representing the players to flash for * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/20 JAS : Created. * + *=============================================================================================*/ +unsigned int FlasherClass::Get_Flashing_Flags() const +{ + unsigned flags = 0; + for (int i = 0; i < HOUSE_COUNT; ++i) + { + if (FlashCountPerPlayer[i] > 0) + { + flags |= (1 << i); + } + } + + return flags; +} \ No newline at end of file diff --git a/REDALERT/FLASHER.H b/REDALERT/FLASHER.H new file mode 100644 index 000000000..eeafe8d1b --- /dev/null +++ b/REDALERT/FLASHER.H @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLASHER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : May 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLASHER_H +#define FLASHER_H + +class FlasherClass { + public: + /* + ** When this object is targeted, it will flash a number of times. This is the + ** flash control number. It counts down to zero and then stops. Odd values + ** cause the object to be rendered in a lighter color. + */ + unsigned FlashCount:7; + + /* + ** When an object is targeted, it flashes several times to give visual feedback + ** to the player. Every other game "frame", this flag is true until the flashing + ** is determined to be completed. + */ + unsigned IsBlushing:1; + + // 2019/09/20 JAS - Flashing info needs to exist per player + unsigned int Get_Flashing_Flags() const; + unsigned int FlashCountPerPlayer[HOUSE_COUNT]; + + // 2019/09/20 JAS - Flashing info needs to exist per player + FlasherClass(void) { + FlashCount = 0; + IsBlushing = false; + + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = 0; + } + } + + FlasherClass(NoInitClass const & ) {}; + ~FlasherClass(void) {}; + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + bool Process(void); +}; + +#endif diff --git a/REDALERT/FLY.CPP b/REDALERT/FLY.CPP new file mode 100644 index 000000000..584c781ab --- /dev/null +++ b/REDALERT/FLY.CPP @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : June 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * FlyClass::Physics -- Performs vector physics (movement). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FlyClass::Physics -- Performs vector physics (movement). * + * * + * This routine performs movement (vector) physics. It takes the * + * specified location and moves it according to the facing and speed * + * of the vector. It returns the status of the move. * + * * + * INPUT: coord -- Reference to the coordinate that the vector will * + * be applied to. * + * * + * OUTPUT: Returns with the status of the vector physics. This could * + * range from no effect, to exiting the edge of the world. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + * 06/05/1995 JLB : Simplified to just do movement. * + *=============================================================================================*/ +ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing) +{ + if (SpeedAdd != MPH_IMMOBILE) { + int actual = (int)SpeedAdd + SpeedAccum; + div_t result = div(actual, PIXEL_LEPTON_W); + SpeedAccum = result.rem; + actual -= result.rem; + COORDINATE old = coord; + + /* + ** If movement occurred that is at least one + ** pixel, then check update the coordinate and + ** check for edge of world collision. + */ + if (result.quot) { + COORDINATE newcoord; // New working coordinate. + newcoord = Coord_Move(coord, facing, actual); + /* + ** If no movement occurred, then presume it hasn't moved at all + ** and return immediately with this indication. + */ + if (newcoord == coord) { + return(IMPACT_NONE); + } + + /* + ** Remember the new position. + */ + coord = newcoord; + + /* + ** If the new coordinate is off the edge of the world, then report + ** this. + */ + if (newcoord & HIGH_COORD_MASK /*|| !Map.In_Radar(Coord_Cell(newcoord))*/) { +// if (!Map.In_Radar(Coord_Cell(newcoord))) { + coord = old; + return(IMPACT_EDGE); + } + + return(IMPACT_NORMAL); + } + } + return(IMPACT_NONE); +} + + +/*********************************************************************************************** + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * * + * This sets the speed of the projectile. It basically functions like a throttle value * + * where 0 equals stop and 255 equals maximum speed (whatever that is for the particular * + * object). * + * * + * INPUT: speed -- Speed setting from 0 to 255. * + * * + * maximum -- The maximum speed of the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + * 07/26/1994 JLB : Added maximum speed as guiding value. * + *=============================================================================================*/ +void FlyClass::Fly_Speed(int speed, MPHType maximum) +{ + SpeedAdd = (MPHType)( maximum * fixed(speed, 256)); +// SpeedAdd = (MPHType)Fixed_To_Cardinal((int)maximum, speed); +} + + diff --git a/REDALERT/FLY.H b/REDALERT/FLY.H new file mode 100644 index 000000000..b02163e46 --- /dev/null +++ b/REDALERT/FLY.H @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLY_H +#define FLY_H + +typedef enum ImpactType : unsigned char { + IMPACT_NONE, // No movement (of significance) occurred. + IMPACT_NORMAL, // Some (non eventful) movement occurred. + IMPACT_EDGE // The edge of the world was reached. +} ImpactType; + + +/**************************************************************************** +** Flying objects are handled by this class definition. +*/ +class FlyClass { + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FlyClass(void) : SpeedAccum(0), SpeedAdd(MPH_IMMOBILE) {}; + FlyClass(NoInitClass const &) {}; + ~FlyClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Fly_Speed(int speed, MPHType maximum); + ImpactType Physics(COORDINATE &coord, DirType facing); + MPHType Get_Speed(void) const {return(SpeedAdd);}; + + private: + /* + ** Object movement consists of incrementing the accumulator until enough "distance" + ** has accumulated so that moving the object becomes reasonable. + */ + unsigned SpeedAccum; // Lepton accumulator. + MPHType SpeedAdd; // Lepton add (per frame). +}; + +#endif diff --git a/REDALERT/FOOT.CPP b/REDALERT/FOOT.CPP new file mode 100644 index 000000000..3ef0bdbc4 --- /dev/null +++ b/REDALERT/FOOT.CPP @@ -0,0 +1,2597 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FOOT.CPP 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FootClass::AI -- Handle general movement AI. * + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * FootClass::Body_Facing -- Set the body rotation/facing. * + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * FootClass::Death_Announcement -- Announces the death of a unit. * + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * FootClass::Detach -- Detaches a target from tracking systems. * + * FootClass::Detach_All -- Removes this object from the game system. * + * FootClass::Enters_Building -- When unit enters a building for some reason. * + * FootClass::FootClass -- Normal constructor for the foot class object. * + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority* + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * FootClass::Mark -- Unit interface to map rendering system. * + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * FootClass::Mission_Capture -- Handles the capture mission. * + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * FootClass::Override_Mission -- temporarily overrides a units mission * + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * FootClass::Restore_Mission -- Restores an overridden mission * + * FootClass::Sell_Back -- Causes this object to be sold back. * + * FootClass::Set_Speed -- Initiate unit movement physics. * + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * FootClass::Take_Damage -- Handles taking damage to this object. * + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FootClass::FootClass -- Default constructor for foot class objects. * + * * + * This is the default constructor for FootClass objects. It sets the foot class values to * + * their default starting settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/23/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(RTTIType rtti, int id, HousesType house) : + TechnoClass(rtti, id, house), + IsScanLimited(false), + IsInitiated(false), + IsNewNavCom(false), + IsPlanningToLook(false), + IsDeploying(false), + IsFiring(false), + IsRotating(false), + IsDriving(false), + IsUnloading(false), + IsFormationMove(false), + IsNavQueueLoop(false), + IsScattering(false), + IsMovingOntoBridge(false), + Speed(0), + SpeedBias(1), + XFormOffset(0x80000000), + YFormOffset(0x80000000), + NavCom(TARGET_NONE), + SuspendedNavCom(TARGET_NONE), + Team(0), + Group(255), + Member(0), + PathThreshhold(MOVE_CLOAK), + PathDelay(0), + TryTryAgain(PATH_RETRY), + BaseAttackTimer(0), + FormationSpeed(SPEED_FOOT), + FormationMaxSpeed(MPH_IMMOBILE), + HeadToCoord(0) +{ + Path[0] = FACING_NONE; + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * * + * This routine is used to output the current status of the foot class to the mono * + * monitor. Through this display bugs may be tracked down or eliminated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 07/04/1995 JLB : Handles aircraft special case. * + *=============================================================================================*/ +void FootClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(53, 13, 12, 1, IsInitiated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsPlanningToLook ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 14, 12, 1, IsDeploying ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 15, 12, 1, IsFiring ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 16, 12, 1, IsRotating ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 17, 12, 1, IsDriving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 18, 12, 1, IsUnloading ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsFormationMove ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(45, 1);mono->Printf("%02X", Speed); + if (NavCom) { + mono->Set_Cursor(29, 5); + mono->Printf("%08X", NavCom); + } + if (SuspendedNavCom) { + mono->Set_Cursor(38, 5); + mono->Printf("%08X", SuspendedNavCom); + } + + if (Team) Team->Debug_Dump(mono); + if (Group != 255) { + mono->Set_Cursor(59, 1);mono->Printf("%d", Group); + } + + static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; + for (int index = 0; index < min(12, ARRAY_SIZE(Path)); index++) { + mono->Set_Cursor(54+index, 3); + mono->Printf("%s", _p2c[((ABS((int)Path[index]+1)) % ARRAY_SIZE(_p2c))]); + } + mono->Set_Cursor(54, 5);mono->Printf("%2d", PathThreshhold); + mono->Set_Cursor(72, 3);mono->Printf("%4d", (long)PathDelay); + mono->Set_Cursor(67, 3);mono->Printf("%3d", TryTryAgain); + if (HeadToCoord) { + mono->Set_Cursor(60, 5);mono->Printf("%08X", HeadToCoord); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * FootClass::Set_Speed -- Initiate unit movement physics. * + * * + * This routine is used to set a unit's velocity control structure. * + * The game will then process the unit's movement during the momentum * + * physics calculation. * + * * + * INPUT: unit -- Pointer to the unit to alter. * + * * + * speed -- Throttle setting (0=stop, 255=full throttle). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 09/24/1993 JLB : Revised for faster speed. * + * 04/02/1994 JLB : Revised for new system. * + * 04/15/1994 JLB : Converted to member function. * + * 07/21/1994 JLB : Simplified. * + *=============================================================================================*/ +void FootClass::Set_Speed(int speed) +{ + assert(IsActive); + + speed &= 0xFF; + ((unsigned char &)Speed) = speed; +} + + +/*********************************************************************************************** + * FootClass::Mark -- Unit interface to map rendering system. * + * * + * This routine is the interface function for units as they relate to * + * the map rendering system. Whenever a unit's imagery changes, this * + * function is called. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Unit is removed. * + * MARK_CHANGE -- Unit alters image but doesn't move. * + * MARK_DOWN -- Unit is overlaid onto existing icons. * + * * + * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * + * down when it is already down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 12/23/1994 JLB : Performs low level check before processing. * + *=============================================================================================*/ +bool FootClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { +// short list[32]; + CELL cell = Coord_Cell(Coord); + +#ifndef PARTIAL + if (In_Which_Layer() != LAYER_GROUND && (mark == MARK_UP || mark == MARK_DOWN)) mark = MARK_CHANGE; +#endif + + /* + ** Inform the map of the refresh, occupation, and overlap + ** request. + */ + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List()); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * * + * This is a common routine used by both infantry and other ground travelling units. It * + * will fill in the unit's basic path to the NavCom destination. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * + * be found or the terrain prohibits the unit's movement. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Basic_Path(void) +{ + assert(IsActive); + + PathType * path; // Pointer to path control structure. + CELL cell; + int skip_path = false; + + Path[0] = FACING_NONE; + + if (Target_Legal(NavCom)) { + cell = As_Cell(NavCom); + + /* + ** When the navigation computer is set to a location that is impassible, then + ** find a nearby cell that can be entered and try to head toward that instead. + ** EXCEPT when that cell is very close -- then just bail. + */ + int dist = Distance(NavCom); + int checkdist = Team.Is_Valid() ? Rule.StrayDistance : Rule.CloseEnoughDistance; + if (Can_Enter_Cell(cell) > MOVE_CLOAK && dist > checkdist) { + CELL cell2 = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + if (cell2 != 0 && ::Distance(Cell_Coord(cell), Cell_Coord(cell2)) < dist) cell = cell2; + } + + if (What_Am_I() == RTTI_INFANTRY) { + CELL mycell = Coord_Cell(Center_Coord()); + ObjectClass * obj = Map[mycell].Cell_Occupier(); + while (obj) { + if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)obj; + if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { + if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { + Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); + } else { + Mem_Copy(inf->Path, Path, sizeof(Path)); + } + if (Path[0] != FACING_NONE) { + skip_path = true; + } + break; + } + } + obj = obj->Next; + } + } + + if (!skip_path) { + Mark(MARK_UP); + Path[0] = FACING_NONE; // Probably not necessary, but... + + /* + ** Try to find a path to the destination. If a failure occurs, then keep trying + ** with greater determination until either a complete failure occurs, or a decent + ** path was found. + */ + bool found1=false; // Found a best path yet? + PathType path1; + FacingType workpath1[200]; // Staging area for path list. +// FacingType workpath2[200]; // Staging area for path list. + MoveType maxtype = MOVE_TEMP; + if (!House->IsHuman) { + maxtype = MOVE_TEMP; +// maxtype = MOVE_DESTROYABLE; + } else { + + /* + ** For simple movement missions by the human player, then don't + ** consider friendly units as passable if close to the destination. + ** This will prevent a human controlled unit from just sitting next + ** to a destination just because there is another friendly unit + ** occupying the destination location. + */ + if (Mission == MISSION_MOVE && Distance(NavCom) < Rule.CloseEnoughDistance) { + maxtype = MOVE_DESTROYABLE; + } + } + + /* + ** Try to find a path to the destination. If there is a path + ** failure, then try a more severe path method until the + ** maximum severity is reached. + */ + for (;;) { + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), PathThreshhold); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + break; + } + + /* + ** A valid path was not found. Try the next greater path severity + ** level if the severity can be increased. If not, then consider this + ** a total failure. + */ + PathThreshhold++; + if (PathThreshhold > maxtype) break; + } + +#ifdef NEVER + /* + ** Determine if ANY path could be calculated by first examining the most + ** aggressive case. If this fails, then no path will succeed. Further + ** scanning is unnecessary. + */ + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + + /* + ** Scan for the best path possible. If this succeeds, then do a simple + ** comparison with the most aggressive path. If they are very close, then + ** go with the best (easiest) path method. + */ + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } else { + + /* + ** The easiest path method didn't result in a satisfactory path. Scan through + ** the rest of the path options, looking for the best one. + */ + for (MoveType move = (MoveType)(MOVE_CLOAK+1); move < (MoveType)(maxtype-1); move++) { +// for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype-1; move++) { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } +#endif + + /* + ** If a good path was found, then record it in the object's path + ** list. + */ + if (found1) { + Fixup_Path(&path1); + memcpy(&Path[0], &workpath1[0], min(path->Length, (int)sizeof(Path))); + } + + Mark(MARK_DOWN); + } + + PathDelay = Rule.PathDelay * TICKS_PER_MINUTE; + if (Path[0] != FACING_NONE) return(true); + + /* + ** If a basic path couldn't be determined, then abort the navigation process. + */ + Stop_Driver(); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * * + * This simple AI script handles moving the vehicle to its desired destination. Since * + * simple movement is handled directly by the engine, this routine merely waits until * + * the unit has reached its destination, and then causes the unit to enter idle mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 10/02/1996 JLB : Player controlled or human owned units don't scan for targets. * + *=============================================================================================*/ +int FootClass::Mission_Move(void) +{ + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + return(1); + } +// if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman) { + if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman && (!Team.Is_Valid() || !Team->Class->IsSuicide)) { + Target_Something_Nearby(THREAT_RANGE); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Capture -- Handles the capture mission. * + * * + * Capture missions are nearly the same as normal movement missions. The only difference * + * is that the final destination is handled in a special way so that it is not marked as * + * impassable. This allows the object (usually infantry) the ability to walk onto the * + * object and thus capture it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Capture(void) +{ + assert(IsActive); + + /* + ** If there is a valid TarCom but the NavCom isn't set, then set the NavCom accordingly. + */ + if (Is_Target_Building(TarCom) && !Target_Legal(NavCom) && What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber) { + Assign_Destination(TarCom); + } + + if (!Target_Legal(NavCom) /*&& !In_Radio_Contact()*/) { + Enter_Idle_Mode(); + if (Map[Center_Coord()].Cell_Building()) { + Scatter(0, true); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * * + * This AI routine handles heading to within range of the target and then firing upon * + * it until it is destroyed. If the target is destroyed, then the unit will change * + * missions to match its "idle mode" of operation (usually guarding). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Attack(void) +{ + assert(IsActive); + if (Target_Legal(TarCom)) { + Approach_Target(); + } else { + Enter_Idle_Mode(); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard -- Handles the AI for guarding in place. * + * * + * Units that are performing stationary guard duty use this AI process. They will sit * + * still and target any enemies that get within range. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Guard(void) +{ + assert(IsActive); + + /* + ** If this unit is on an impassable cell for any reason, it needs to scatter immediately + */ + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + LandType land = Map[Coord].Land_Type(); + if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + Scatter(0, true, true); + Shorten_Mission_Timer(); + } + } + + if (!Target_Something_Nearby(THREAT_RANGE)) { + Random_Animate(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_VESSEL) { + switch (((VesselClass *)this)->Class->Type) { + case VESSEL_DD: + case VESSEL_PT: + dtime = MissionControl[Mission].AA_Delay(); + break; + + case VESSEL_CA: + dtime *= 2; + break; + + default: + break; + } + } + if (What_Am_I() == RTTI_INFANTRY) { + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + } + + switch (((InfantryClass *)this)->Class->Type) { + case INFANTRY_E1: + case INFANTRY_E3: + dtime = MissionControl[Mission].AA_Delay(); + break; + + default: + break; + } + } + + return((Arm != 0) ? (int)Arm : (dtime+Random_Pick(0, 2))); +} + + +/*********************************************************************************************** + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * * + * This routine is the default hunt order for game objects. It handles searching for a * + * nearby object and heading toward it. The act of targeting will cause it to attack * + * the target it selects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the game tick delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Hunt(void) +{ + assert(IsActive); + if (!Target_Something_Nearby(THREAT_NORMAL)) { +#if(0) +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this == INFANTRY_GENERAL && House->Class->House==HOUSE_UKRAINE && Scen.Scenario==47) { + for(int index=0; index < Buildings.Count(); index++) { + if(Buildings.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Buildings.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Units.Count(); index++) { + if(Units.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Units.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Infantry.Count(); index++) { + if(Infantry.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Infantry.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Aircraft.Count(); index++) { + if(Aircraft.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Aircraft.Ptr(index)->As_Target()); + break; + } + } + } +#endif +#endif + Random_Animate(); + } else { + if (What_Am_I() == RTTI_INFANTRY && ( ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_RENOVATOR || ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_THIEF) ) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + } else { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber && Is_Target_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + } else { + Approach_Target(); + } + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * * + * This is the counterpart routine to the Start_Driver function. It clears the driving * + * status flags and destination coordinate record. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Greatly simplified. * + *=============================================================================================*/ +bool FootClass::Stop_Driver(void) +{ + assert(IsActive); + + if (HeadToCoord) { + HeadToCoord = NULL; + Set_Speed(0); + IsDriving = false; + IsMovingOntoBridge = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * * + * Before a unit can move it must be started by this routine. This routine handles * + * reserving the cell and setting the driving flag. * + * * + * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * + * away from the unit's current location. * + * * + * OUTPUT: bool; Was driving initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Uses simple spot index finder. * + *=============================================================================================*/ +bool FootClass::Start_Driver(COORDINATE &headto) +{ + assert(IsActive); + + Stop_Driver(); + if (headto) { + HeadToCoord = headto; + IsDriving = true; + + CellClass * cellptr = &Map[headto]; + TemplateType ttype = cellptr->TType; + IsMovingOntoBridge = (ttype >= TEMPLATE_BRIDGE1 && ttype <= TEMPLATE_BRIDGE2D) || (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3F); + + /* + ** Check for crate goodie finder here. + */ + if (Map[headto].Goodie_Check(this)) { + return(true); + } + if (!IsActive) return(false); + + HeadToCoord = NULL; + IsDriving = false; + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * * + * This routine will determine the sort coordinate for foot class object. This coordinate * + * is usually the coordinate of the object. The exception is if the object is tethered. * + * In this case (presumes offloading to the north), the sorting coordinate is adjusted * + * so that the object will be drawn on top of the transport unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * + *=============================================================================================*/ +COORDINATE FootClass::Sort_Y(void) const +{ + assert(IsActive); + + if (IsUnloading) { + return(Coord_Add(Coord, 0x01000000L)); + } + if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { + return(Coord_Add(Coord, 0x01000000L)); + } + return(Coord_Add(Coord, 0x00300000L)); +} + + +/*********************************************************************************************** + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * * + * This routine clears the units' navigation computer in preparation for removal from the * + * game. This is probably called as a result of unit destruction in combat. Clearing the * + * navigation computer ensures that the normal AI process won't start it moving again while * + * the object is undergoing any death animations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Stun(void) +{ + assert(IsActive); + + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + Stop_Driver(); + TechnoClass::Stun(); +} + + +/*********************************************************************************************** + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * * + * This routine will set the navigation computer to approach the target indicated by the * + * targeting computer. It is through this function that the unit nears the target so * + * that weapon firing may occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/13/1994 JLB : Made part of TechnoClass. * + * 12/22/1994 JLB : Enhanced search algorithm. * + * 05/20/1995 JLB : Always approaches if the object is off the map. * + *=============================================================================================*/ +void FootClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + /* + ** If the target is too far away then head toward it. + */ + int maxrange = Weapon_Range(primary); +// int maxrange = max(Weapon_Range(0), Weapon_Range(1)); + + if (!Target_Legal(NavCom) && (!In_Range(TarCom, primary) || !IsLocked)) { +// if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { + + /* + ** If the object that we are attacking is a building adjust the unit's + ** max range so that people can stand far away from the buildings and + ** hit them. + */ + BuildingClass * obj = As_Building(TarCom); + if (obj) { + maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + } + + /* + ** Adjust the max range of an infantry unit for where he is standing + ** in the room. + */ + maxrange -= 0x00B7; +#ifdef OBSOLETE + if (What_Am_I() == RTTI_INFANTRY) { + maxrange -= 0x0111; + } else { + maxrange -= 0x00B7; + } +#endif + maxrange = max(maxrange, 0); + + COORDINATE tcoord = ::As_Coord(TarCom); + COORDINATE trycoord = 0; + CELL tcell = Coord_Cell(tcoord); + CELL trycell = tcell; + DirType dir = Direction256(tcoord, Center_Coord()); + bool found = false; + + /* + ** Sweep through the cells between the target and the unit, looking for + ** a cell that the unit can enter but which is also within weapon range + ** of the target. If after a reasonable search, no appropriate cell could + ** be found, then the target will be assigned as the movement destination + ** and let "the chips fall where they may." + */ + for (int range = maxrange; range > 0x0080; range -= 0x0100) { + static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; + + for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { + trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); + + if (::Distance(trycoord, tcoord) < range) { + trycell = Coord_Cell(trycoord); + if (Map.In_Radar(trycell) && Map[trycell].Is_Clear_To_Move(Techno_Type_Class()->Speed, false, false, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)) { +// if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { + found = true; + break; + } + } + } + if (found) break; + } + + /* + ** If a suitable intermediate location was found, then head toward it. + ** Otherwise, head toward the enemy unit directly. + */ + if (found) { + Assign_Destination(::As_Target(trycell)); + } else { + + trycell = Map.Nearby_Location(trycell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + Assign_Destination(::As_Target(trycell)); +// Assign_Destination(TarCom); + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * * + * This mission routine causes the unit to scan for targets out to twice its weapon range * + * from the home point. If a target was found, then it will be attacked. The unit will * + * chase the target until it gets up to to its weapon range from the home position. * + * In that case, it will return to home position and start scanning for another target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with time delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 07/27/1995 JLB : Greatly simplified. * + *=============================================================================================*/ +int FootClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + /* + ** If this unit is on an impassable cell for any reason, it needs to scatter immediately + */ + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + LandType land = Map[Coord].Land_Type(); + if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + Scatter(0, true, true); + Shorten_Mission_Timer(); + } + } + + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { + Assign_Mission(MISSION_HARVEST); + return(1+Random_Pick(1, 10)); + } + + /* + ** Ensure that the archive target is valid. + */ + if (!Target_Legal(ArchiveTarget)) { + ArchiveTarget = ::As_Target(Coord); + } + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && What_Am_I() == RTTI_INFANTRY && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + /* + ** Make sure that the unit has not strayed too far from the home position. + ** If it has, then race back to it. + */ + int maxrange = Threat_Range(1)/2; + + if (!IsFiring && !Target_Legal(NavCom) && Distance(ArchiveTarget) > maxrange) { + Assign_Target(TARGET_NONE); + Assign_Destination(ArchiveTarget); + } + + if (!Target_Legal(TarCom)) { + COORDINATE old = Coord; + Coord = As_Coord(ArchiveTarget); + Target_Something_Nearby(THREAT_AREA); + Coord = old; + if (Target_Legal(TarCom)) { + return(1); + } + Random_Animate(); + } else { + Approach_Target(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_AIRCRAFT) { + dtime *= 2; + } + return(dtime + Random_Pick(1, 5)); +} + + +/*********************************************************************************************** + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * * + * This routine will make sure that the home position for the foot class object gets * + * reset. This is necessary since the home position may change depending on the unit's * + * transition between limbo and non-limbo condition. * + * * + * INPUT: coord -- The coordinate to unlimbo the unit at. * + * * + * dir -- The initial direction to give the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + /* + ** Try to unlimbo the unit. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Mobile units are always revealed to the house that owns them. + */ + Revealed(House); + + /* + ** Start in a still (non-moving) state. + */ + Path[0] = FACING_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Take_Damage -- Handles taking damage to this object. * + * * + * This routine intercepts the damage assigned to this object and if this object is * + * a member of a team, it informs the team that the damage has occurred. The team may * + * change it's priority or action based on this event. * + * * + * INPUT: damage -- The damage points inflicted on the unit. * + * * + * distance -- The distance from the point of damage to the unit itself. * + * * + * warhead -- The type of damage that is inflicted. * + * * + * source -- The perpetrator of the damage. By knowing who caused the damage, * + * the team know's who to "get even with". * + * * + * OUTPUT: Returns with the result type of the damage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(IsActive); + + ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + + if (result != RESULT_NONE && Team) { + + Team->Took_Damage(this, result, source); + + } else { + + if (result != RESULT_DESTROYED && result != RESULT_NONE) { + + /* + ** Determine if the target that is currently being attacked has a weapon that can + ** do harm to a ground based unit. This information is needed so that an appropriate + ** response will occur when damage is taken. + */ +// bool tweap = false; +// if (As_Techno(TarCom)) { +// tweap = (As_Techno(TarCom)->Techno_Type_Class()->PrimaryWeapon != NULL); +// } + + /* + ** This ensures that if a unit is in sticky mode, then it will snap out of + ** it when it takes damage. + */ + if (source != NULL && MissionControl[Mission].IsNoThreat && !MissionControl[Mission].IsZombie) { + Enter_Idle_Mode(); + } + + /* + ** If this object is not part of a team and it can retaliate for the damage, then have + ** it try to do so. This prevents it from just sitting there and taking damage. + */ + if (Is_Allowed_To_Retaliate(source)) { + + int primary = What_Weapon_Should_I_Use(source->As_Target()); + if (In_Range(source, primary) || !House->IsHuman) { + Assign_Target(source->As_Target()); + } + + if (Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + /* + ** Simple retaliation cannot occur because the source of the damage + ** is too far away. If scatter logic is enabled, then scatter now. + */ + if (!Target_Legal(TarCom) && !Target_Legal(NavCom) && Rule.IsScatter) { + Scatter(0, true); + } + + } else { + + /* + ** If this object isn't doing anything important, then scatter. + */ + if (MissionControl[Mission].IsScatter && !IsTethered && !IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && What_Am_I() != RTTI_AIRCRAFT && What_Am_I() != RTTI_VESSEL) { + if (!House->IsHuman || Rule.IsScatter) { + Scatter(0, true); + } + } + } + } + } + return(result); +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * * + * At this level, the object is known to have the ability to attack or move to the * + * target specified (in theory). Perform the attack or move as indicated. * + * * + * INPUT: target -- The target clicked upon that will precipitate action. * + * * + * OUTPUT: Returns with the type of action performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(IsActive); + assert(object != NULL); + + switch (action) { + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + if (What_Am_I() == RTTI_INFANTRY && + ((InfantryClass *)this)->Class->IsBomber && + object->What_Am_I() == RTTI_BUILDING && + !House->Is_Ally(object)) { + + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } else { + Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); + } + } + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD); + break; + + case ACTION_ATTACK: + if (Can_Player_Fire()) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + break; + + case ACTION_ENTER: + if (Can_Player_Move() && object && object->Is_Techno() /*&& !((RadioClass *)object)->In_Radio_Contact()*/) { + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_SABOTAGE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NOMOVE: + case ACTION_MOVE: + if (Can_Player_Move()) { + + TARGET targ = object->As_Target(); + + /* + ** If the destination object is not the same zone, then pick a nearby location. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT && Techno_Type_Class()->Speed != SPEED_WINGED && Map[Coord].Zones[Techno_Type_Class()->MZone] != Map[object->Center_Coord()].Zones[Techno_Type_Class()->MZone]) { + +#ifdef FIXIT_MINE_PASSABLE + // Fixes units not driving onto mines. + if (Can_Enter_Cell(Coord_Cell(object->Center_Coord())) > MOVE_OK) { + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); + } +#else + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, targ); + } + break; + + case ACTION_NO_DEPLOY: + Speak(VOX_DEPLOY); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * * + * This routine performs the action requested when the left mouse button was clicked over * + * a cell. Typically, this is just a move command. * + * * + * INPUT: action -- The predetermined action that should occur. * + * * + * cell -- The cell number that the action should occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(IsActive); + + action = What_Action(cell); + switch (action) { + case ACTION_HARVEST: + Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); + break; + + case ACTION_MOVE: + if (AllowVoice) { + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + // Fall into next case. + + case ACTION_NOMOVE: + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].Is_Visible(PlayerPtr)) { + + /* + ** Find the closest same-zoned cell to where the unit currently is. + ** This will allow the unit to come as close to the destination cell + ** as is reasonably possible, when clicking on an impassable cell + ** (as is likely when clicking in the shroud.) It looks for the + ** nearest cell using an expanding-radius box, and ignores cells + ** off the edge of the map. + */ + CellClass const * cellptr = &Map[::As_Cell(::As_Target(Center_Coord()))]; + if (What_Am_I() != RTTI_AIRCRAFT) { + + if (Can_Enter_Cell(Coord_Cell(Center_Coord())) == MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + } else { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } +#ifdef OBSOLETE + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + + /* + ** Engineer attempting to capture bridge to repair it + */ + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_SABOTAGE: + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, ::As_Target(cell) ); + break; + + // MBL 05.15.2020 - Adding support for CTRL+ALT clicking the ground to have units move to an area and guard it + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + Player_Assign_Mission(MISSION_GUARD_AREA, ::As_Target(cell)); + } + break; + // END MBL 05.15.2020 + } +} + + +/*********************************************************************************************** + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * * + * This routine is called as this object moves from cell to cell. When the center of the * + * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * + * the path to the distance to the target. This forces path recalculation in an effort to * + * avoid units passing each other. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/08/1995 JLB : Handles generic enter trigger event. * + * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * + *=============================================================================================*/ +void FootClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + + IsScattering = false; + + /* + ** Clear any unloading flag if necessary. + */ + IsUnloading = false; + + /* + ** If adjacent to an enemy techno that has the ability to reveal a sub, + ** then shimmer the cloaked object. + */ + if (Cloak == CLOAKED) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); + + if (Map.In_Radar(cell)) { + TechnoClass const * techno = Map[cell].Cell_Techno(); + + if (techno && !techno->House->Is_Ally(this) && techno->Techno_Type_Class()->IsScanner) { + Do_Shimmer(); + break; + } + } + } + } + + /* + ** Shorten the path if the target is now within weapon range of this + ** unit and this unit is on an attack type mission. But only if the target + ** is slow enough for leading to make sense. + */ + if (Target_Legal(TarCom) && (What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)this)->Class->IsDog)) { + int primary = What_Weapon_Should_I_Use(TarCom); + bool inrange = In_Range(TarCom, primary); + TechnoClass const * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Foot()) { + FootClass const * foot = (FootClass const *)techno; + MPHType speed = ((TechnoTypeClass const &)techno->Class_Of()).MaxSpeed; + COORDINATE rangecoord = (speed > MPH_SLOW) ? foot->Likely_Coord() : foot->Target_Coord(); + inrange = In_Range(rangecoord, primary); + } + + if ((Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + } + } + + /* + ** Trigger event associated with the player entering the cell. + */ + if (Cloak != CLOAKED) { + TriggerClass * trigger = Map[Coord].Trigger; + if (trigger != NULL) { + trigger->Spring(TEVENT_PLAYER_ENTERED, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + + /* + ** Check for horizontal trigger crossing. + */ + int x = Cell_X(Coord_Cell(Coord)); + int y = Cell_Y(Coord_Cell(Coord)); + int index; + for (index = 0; index < Map.MapCellWidth; index++) { + trigger = Map[XY_Cell(index+Map.MapCellX, y)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_HORIZONTAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_HORIZONTAL)) { + trigger->Spring(TEVENT_CROSS_HORIZONTAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for vertical trigger crossing. + */ + for (index = 0; index < Map.MapCellHeight; index++) { + trigger = Map[XY_Cell(x, index+Map.MapCellY)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_VERTICAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_VERTICAL)) { + trigger->Spring(TEVENT_CROSS_VERTICAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for zone entry trigger events. + */ + for (MapTriggerID = 0; MapTriggerID < MapTriggers.Count(); MapTriggerID++) { + trigger = MapTriggers[MapTriggerID]; + if (trigger->Class->Event1.Event == TEVENT_ENTERS_ZONE || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_ENTERS_ZONE)) { + if (Map[trigger->Cell].Zones[Techno_Type_Class()->MZone] == Map[Coord].Zones[Techno_Type_Class()->MZone]) { + trigger->Spring(TEVENT_ENTERS_ZONE, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** If any of these triggers cause this unit to be destroyed, then + ** stop all further processing for this unit. + */ + if (!IsActive) return; + } + +#ifdef OBSOLETE + /* + ** Flag any gap generators to re-draw + */ + for (int index = 0; index IsInLimbo && (HouseClass *)obj->House != PlayerPtr) { + int dist = Distance(obj) / CELL_LEPTON_W; + if (dist < (6 + Rule.GapShroudRadius) ) { + // if (dist < (6 + obj->Class->SightRange) ) { + obj->IsJamming = false; // lie so it'll re-jam now + } + } + } +#endif + + } + + TechnoClass::Per_Cell_Process(why); +} + + +/*************************************************************************** + * FootClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedNavCom = NavCom; + TechnoClass::Override_Mission(mission, tarcom, navcom); + + Assign_Destination(navcom); +} + + +/*************************************************************************** + * FootClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Restore_Mission(void) +{ + assert(IsActive); + + if (TechnoClass::Restore_Mission()) { + Assign_Destination(SuspendedNavCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * * + * This routine handles radio message that are related to movement. These are used for * + * complex coordinated maneuvers. * + * * + * INPUT: from -- Pointer to the originator of this radio message. * + * * + * message -- The radio message that is being received. * + * * + * param -- The optional parameter (could be a movement destination). * + * * + * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * + * response is RADIO_ROGER. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Answers if this object is located on top of a service depot. + */ + case RADIO_ON_DEPOT: + if (Map[Center_Coord()].Cell_Building() != NULL) { + BuildingClass const * building = Map[Center_Coord()].Cell_Building(); + if (*building == STRUCT_REPAIR) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** Intercept the repair request and if this object is moving, then no repair + ** is possible. + */ + case RADIO_REPAIR: + if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); + break; + + /* + ** Something bad has happened to the object in contact with. Abort any coordinated + ** activity with this object. Basically, ... run away! Run away! + */ + case RADIO_RUN_AWAY: + if (In_Radio_Contact()) { + if (NavCom == Contact_With_Whom()->As_Target()) { + Assign_Destination(TARGET_NONE); + } + } + if (Mission == MISSION_SLEEP) { + Assign_Mission(MISSION_GUARD); + Commence(); + } + if (Mission == MISSION_ENTER) { + Assign_Mission(MISSION_GUARD); + } + if (!IsRotating && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } + break; + + /* + ** Checks to see if this unit needs to move somewhere. If it is already in motion, + ** then it doesn't need further movement instructions. + */ + case RADIO_NEED_TO_MOVE: + param = (long)NavCom; + if (!Target_Legal(NavCom)) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Radio request to move to location specified. Typically this is used + ** for complex loading and unloading missions. + */ + case RADIO_MOVE_HERE: + if (NavCom != (TARGET)param) { + if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { + return(RADIO_YEA_NOW_WHAT); + } else { + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + Assign_Destination((TARGET)param); + Shorten_Mission_Timer(); + } + } + return(RADIO_ROGER); + + /* + ** Requests if this unit is trying to cooperatively load up. Typically, this occurs + ** for passengers and when vehicles need to be repaired. + */ + case RADIO_TRYING_TO_LOAD: + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + } + break; + } + return(TechnoClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * * + * This mission handler will cooperatively coordinate the object to maneuver into the * + * object it is in radio contact with. This is used by infantry when they wish to load * + * into an APC as well as by vehicles when they wish to enter a repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 JLB : Created. * + * 09/22/1995 JLB : Modified to handle the "on hold" condition. * + *=============================================================================================*/ +int FootClass::Mission_Enter(void) +{ + assert(IsActive); + + /* + ** Find out who to coordinate with. If in radio contact, then this the transporter is + ** defined. If not in radio contact, then try the archive target value to see if that + ** is suitable. + */ + TechnoClass * contact = Contact_With_Whom(); + if (contact == NULL) { + contact = As_Techno(ArchiveTarget); + } + + /* + ** If in contact, then let the transporter handle the movement coordination. + */ + if (contact != NULL) { + + /* + ** If the transport says to "bug off", then abort the enter mission. The transport may + ** likely say all is 'ok' with the "RADIO ROGER", then try again later. + */ + if (Transmit_Message(RADIO_DOCKING, contact) != RADIO_ROGER && !IsTethered) { + Transmit_Message(RADIO_OVER_OUT); + Enter_Idle_Mode(); + } + + } else { + + /* + ** Since there is no potential object to enter, then abort this + ** mission with some default standby mission. + */ + Enter_Idle_Mode(); + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * * + * This routine will assign the specified target to the navigation computer. No legality * + * checks are performed. * + * * + * INPUT: target -- The target value to assign to the navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + NavCom = target; + + /* + ** Presume that the easiest path is tried first. As the findpath proceeds, when + ** a failure occurs, this threshhold will be increased until path failure + ** cannot be prevent. At this point, all movement should cease. + */ + PathThreshhold = MOVE_CLOAK; +} + + +/*********************************************************************************************** + * FootClass::Detach_All -- Removes this object from the game system. * + * * + * This routine will remove this object from the game system. This routine is called when * + * this object is about to be deleted. All other objects should no longer reference this * + * object in that case. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach_All(bool all) +{ + assert(IsActive); + + if (Team && !ScenarioInit) { + Team->Remove(this); + Team = NULL; + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * * + * This routine is called when the house determines that it should attack the specified * + * target. This routine will determine if it can attack the target specified and if so, * + * the amount of power it can throw at it. This returned power value is used to allow * + * intelligent distribution of retaliation. * + * * + * INPUT: target -- The target that this object just might be assigned to attack and thus * + * how much power it can bring to bear should be returned. * + * * + * OUTPUT: Returns with the amount of power that this object can bring to bear against the * + * potential target specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Rescue_Mission(TARGET tarcom) +{ + assert(IsActive); + + /* + ** If the target specified is not legal, then it cannot be attacked. Always return + ** zero in this case. + */ + if (!Target_Legal(tarcom)) return(0); + + /* + ** If the unit is already assigned to destroy the tarcom then we need + ** to return a negative value which tells the computer to lower the + ** desired threat rating. + */ + if (TarCom == tarcom) { + return(-Risk()); + } + + /* + ** If the unit is currently attacking a target that has a weapon then we + ** cannot abandon it as it will destroy us if we return to base. + */ + if (Target_Legal(TarCom)) { + TechnoClass * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Weapon_Equipped()) { + return(0); + } + } + + /* + ** If the unit is in a harvest mission or is currently attacking + ** something, or is not very effective, then it will be of no help + ** at all. + */ + if (Team.Is_Valid() || Mission == MISSION_HARVEST || !Risk()) { + return(0); + } + + /* + ** Find the distance to the target modified by the range. If the + ** the distance is 0, then things are ok. + */ + int dist = Distance(tarcom) - Weapon_Range(0); + int threat = Risk() * 1024; + int speed = -1; + if (dist > 0) { + + /* + ** Next we need to figure out how fast the unit moves because this + ** decreases the distance penalty. + */ + speed = max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); + + int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; + + /* + ** Finally modify the threat by the distance the unit is away. + */ + threat = max(threat/ratio, 1); + } + return(threat); +} + + +/*********************************************************************************************** + * FootClass::Death_Announcement -- Announces the death of a unit. * + * * + * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * + * * + * INPUT: source -- The perpetrator of this death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Death_Announcement(TechnoClass const * ) const +{ + assert(IsActive); + + //if (IsOwnedByPlayer) { + if ((Session.Type == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) || (Session.Type != GAME_GLYPHX_MULTIPLAYER && IsOwnedByPlayer)) { + if (What_Am_I() == RTTI_VESSEL) { + // Speak(VOX_SHIP_LOST); // MBL 02.06.2020 + Speak(VOX_SHIP_LOST, House, Center_Coord()); + } else { + // Speak(VOX_UNIT_LOST); // MBL 02.06.2020 + Speak(VOX_UNIT_LOST, House, Center_Coord()); + } + } +} + + +/*********************************************************************************************** + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * * + * This routine will return with the greatest threat (best target) for this object. For * + * movable ground object, they won't automatically return ANY target if this object is * + * cloaked. Otherwise, cloaking is relatively useless. * + * * + * INPUT: method -- The request method (bit flags) to use when scanning for a target. * + * * + * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * + * TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 07/10/1996 JLB : Handles scan range limitation. * + *=============================================================================================*/ +TARGET FootClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + /* + ** If the scan is forced to be limited, then limit the scan now. + */ + if (IsScanLimited) { + method = method & ~THREAT_AREA; + method = method | THREAT_RANGE; + } + + /* + ** If this object can cloak, then it won't select a target automatically. + */ + if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { + return(TARGET_NONE); + } + + if (!(method & (THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_TIBERIUM|THREAT_BOATS|THREAT_CIVILIANS|THREAT_POWER|THREAT_FAKES|THREAT_FACTORIES|THREAT_BASE_DEFENSE))) { + if (What_Am_I() != RTTI_VESSEL) { + method = method | THREAT_GROUND; + } else { + method = method | THREAT_BOATS|THREAT_GROUND; + } + } + + /* + ** Perform the search for the target. + */ + TARGET target = TechnoClass::Greatest_Threat(method); + + /* + ** If no target could be located and this object is under scan range + ** restrictions, then this restriction must be lifted now. + */ + if (IsScanLimited && target == TARGET_NONE) { + const_cast(this)->IsScanLimited = false; // const_cast ST - 5/8/2019 + } + + /* + ** Return with final target found. + */ + return(target); +} + + +/*********************************************************************************************** + * FootClass::Detach -- Detaches a target from tracking systems. * + * * + * This routine will detach the specified target from the tracking systems of this object. * + * It will be removed from the navigation computer and any queued mission record. * + * * + * INPUT: target -- The target to be removed from this object. * + * * + * all -- Is the unit really about to be eliminated? If this is true then even * + * friendly contact (i.e., radio) must be eliminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 07/24/1996 JLB : Removes target from NavQueue list. * + *=============================================================================================*/ +void FootClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + + TechnoClass::Detach(target, all); + + if (!SpecialFlag) { + if (ArchiveTarget == target) { + ArchiveTarget = TARGET_NONE; + } + } + + if (SuspendedNavCom == target) { + SuspendedNavCom = TARGET_NONE; + SuspendedMission = MISSION_NONE; + } + + /* + ** If the navigation computer is assigned to the target, then the navigation + ** computer must be cleared. + */ + if (NavCom == target) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + Restore_Mission(); + } + + /* + ** Remove the target from the NavQueue list as well. + */ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == target) { + NavQueue[index] = TARGET_NONE; + if (index < ARRAY_SIZE(NavQueue)-1) { + memmove(&NavQueue[index], &NavQueue[index+1], ((ARRAY_SIZE(NavQueue)-index)-1) * sizeof(NavQueue[0])); + index--; + } + } + } + + /* + ** If targeting the specified object and this unit is obviously heading + ** toward the target to get within range, then abort the path. + */ + if (TarCom == target && House->IsHuman) { + Path[0] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * * + * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * + * from the object. This function is overridden for those objects that can contain * + * Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded from the object. * + * * + * WARNINGS: This routine must be called multiple times in order to completely offload the * + * Tiberium. When this routine return 0, all Tiberium has been offloaded. * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * * + * This routine examines the specified cell to see if the object can enter it. This * + * function is to be overridden for objects that could have the possibility of not being * + * allowed to enter the cell. Typical objects at the FootClass level always return * + * MOVE_OK. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The direction that this cell might be entered from. * + * * + * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * + * blockage. There are various other values that represent other blockage types. * + * The value returned will indicated the most severe reason why entry into the cell * + * is blocked. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const +{ + assert(IsActive); + + return MOVE_OK; +} + + +/*********************************************************************************************** + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * * + * This routine determines if it is legal to sell the object back. A foot class object can * + * only be sold back if it is sitting on a repair bay. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully sold back? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Can_Demolish(void) const +{ + assert(IsActive); + + StructType sell_struct = STRUCT_NONE; + switch (What_Am_I()) { + case RTTI_UNIT: + sell_struct = STRUCT_REPAIR; + break; + case RTTI_AIRCRAFT: + sell_struct = STRUCT_AIRSTRIP; + break; + default: + break; + } + if (sell_struct != STRUCT_NONE) { + if (In_Radio_Contact() && + Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && + *((BuildingClass *)Contact_With_Whom()) == sell_struct && + Distance(Contact_With_Whom()) < 0x0080) { + return(true); + } + } + return(TechnoClass::Can_Demolish()); +} + + +/*********************************************************************************************** + * FootClass::Sell_Back -- Causes this object to be sold back. * + * * + * When an object is sold back, a certain amount of money is refunded to the owner and then * + * the object is removed from the game system. * + * * + * INPUT: control -- The action to perform. The only supported action is "1", which means * + * to sell back. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Sell_Back(int control) +{ + assert(IsActive); + + if (control != 0) { + if (House == PlayerPtr) { + Speak(VOX_UNIT_SOLD); + Sound_Effect(VOC_CASHTURN); + } + House->Refund_Money(Refund_Amount()); + Stun(); + Limbo(); + delete this; + } +} + + +/*********************************************************************************************** + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * * + * This routine comes in handy when determining where a travelling object will be at * + * when considering the amount of time it would take for a normal unit to travel one cell. * + * Using this information, an intelligent "approach target" logic can be employed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate the object is at or soon will be. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE FootClass::Likely_Coord(void) const +{ + assert(IsActive); + + if (Head_To_Coord()) { + return(Head_To_Coord()); + } + return(Target_Coord()); +} + + +/*********************************************************************************************** + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * * + * This routine modify the specified cell if the unit is part of a formation. The * + * adjustment will take into consideration the formation relative offset from the * + * (presumed) center cell specified. * + * * + * INPUT: cell -- The cell to presume as the desired center point of the formation. * + * * + * OUTPUT: Returns with the cell that should be used as the actual destination. If this * + * object is part of a formation, then the cell location will be appropriately * + * adjusted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +CELL FootClass::Adjust_Dest(CELL cell) const +{ + assert(IsActive); + + if (IsFormationMove) { + int xdest = Cell_X(cell); + int ydest = Cell_Y(cell); + + int newx = Bound(XFormOffset + xdest, Map.MapCellX, Map.MapCellX + Map.MapCellWidth -1); + int newy = Bound(YFormOffset + ydest, Map.MapCellY, Map.MapCellY + Map.MapCellHeight -1); + + cell = XY_Cell(newx, newy); + } + return(cell); +} + + +/*********************************************************************************************** + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * * + * This routine will process the navigation queue. If the queue is present and valid and * + * there is currently no navigation target assigned to this object, then the first entry * + * of the queue will be assigned. The remaining entries will move down. If the queue is * + * to be processed as a circular list, then the first entry is appended to the end. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine might end up assigning a movement destination. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Handle_Navigation_List(void) +{ + /* + ** The navigation queue only needs to be processed if there is + ** currently no navigation target for this object. + */ + if (!Target_Legal(NavCom)) { + TARGET target = NavQueue[0]; + + /* + ** Check to see if the navigation queue even exists and + ** has at least one valid entry. If it does, then process it by + ** assigning the object's NavCom to the first entry on the list. + */ + if (Target_Legal(target)) { + Assign_Destination(target); + memmove(&NavQueue[0], &NavQueue[1], sizeof(NavQueue)-sizeof(NavQueue[0])); + NavQueue[ARRAY_SIZE(NavQueue)-1] = TARGET_NONE; + + /* + ** If the navigation queue is to loop (indefinately), then append the + ** target value from the first part to the end of the queue. + */ + if (IsNavQueueLoop) { + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == TARGET_NONE) { + NavQueue[index] = target; + break; + } + } + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * * + * This routine will append the destination target to the object's NavQueue list. After * + * doing so, if the object is not doing anything important, then it will be started on * + * that destination. This is functionally the same as Assign_Destination, but it stores * + * the target to the NavQueue first. * + * * + * INPUT: target -- The movement target destination to append the queue. * + * * + * OUTPUT: none * + * * + * WARNINGS: The queue is of finite size and any queue requests that would exceed that size * + * are ignored. If there are no queue entries pending and the unit is not * + * otherwise occupied, then the queue target might be carried directly into the * + * NavCom. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Queue_Navigation_List(TARGET target) +{ + if (Target_Legal(target)) { + int count; + for (count = 0; count < ARRAY_SIZE(NavQueue); count++) { + if (!Target_Legal(NavQueue[count])) break; + } + + /* + ** If the target is this object itself, then this indicates that the + ** queue list is to be processed as a loop. Otherwise, just tack the + ** navigation target to the end of the list. + */ + if (target == As_Target() && count > 0) { + IsNavQueueLoop = true; + } else { + if (count == 0) { + IsNavQueueLoop = false; + } + if (count < ARRAY_SIZE(NavQueue)) { + NavQueue[count] = target; + } + } + + /* + ** If this object isn't doing anything, then start acting on the + ** navigation queue now. + */ + if (!Target_Legal(NavCom) && Mission == MISSION_GUARD) { + Enter_Idle_Mode(); + } + } +} + + +/*********************************************************************************************** + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * * + * This routine will clear out any values in the navigation queue. This is the preferred * + * way of aborting a navigation queue for a unit. If the unit is already travelling, it * + * won't be interrupted by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This will clear the navigation list but not the navigation computer. Thus a * + * unit will still travel to its current immediate destination. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Clear_Navigation_List(void) +{ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * * + * This routine will determine if this object has permission to leave the map and thus * + * leave the game. Typical objects with this permission are transports used to drop of * + * reinforcements. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this object have permission to travel off the map edge and leave the * + * game? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Allowed_To_Leave_Map(void) const +{ + /* + ** If the unit hasn't entered the map yet, then don't allow leave the game. + */ + if (!IsLocked) return(false); + + /* + ** A unit that isn't marked as a loaner is a gift to the player. Such objects can never + ** leave the map unless they are part of a team that gives it special permision. + */ + if (!IsALoaner && Mission != MISSION_RETREAT && (!Team.Is_Valid() || !Team->Is_Leaving_Map())) return(false); + + return(true); +} + + +/*********************************************************************************************** + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * * + * This will examine this object to determine if it is suitable as a team recruit. Some * + * objects are disqualified if they are otherwise premptively occupied. * + * * + * INPUT: house -- Pointer to the house that is trying to recruit this object. * + * * + * OUTPUT: bool; Is this object suitable for recruitment by a team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Recruitable(HouseClass const * house) const +{ + /* + ** If not of the correct house presuasion, then recruitment is not allowed. + */ + if (house != NULL && house != House) { + return(false); + } + + /* + ** If the object is not a playing member of the game, then don't consider it available. + */ + if (IsInLimbo) { + return(false); + } + + /* + ** If it is already part of another team, then it is not available for + ** general recruitment. + */ + if (Team.Is_Valid()) { + return(false); + } + + /* + ** If it is currently in a mission the precludes recruitment into a team, then + ** return with this information. + */ + if (!Is_Recruitable_Mission(Mission)) { + return(false); + } + + /* + ** It was not disqualified for general team recruitment, so return that + ** it is available. + */ + return(true); +} + + +/*********************************************************************************************** + * FootClass::AI -- Handle general movement AI. * + * * + * This basically just sees if this object is within weapon range of the target and if * + * so, it will stop movement so that firing may commence. This prevents the occasional * + * case of an attacker driving right up to the defender before firing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::AI(void) +{ + TechnoClass::AI(); + +// FootClass::Per_Cell_Process does this function already. +#ifdef OBSOLETE + if (IsActive) { + if (!IsScattering && !IsTethered && !IsInLimbo && What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && In_Range(TarCom)) { + Assign_Destination(TARGET_NONE); + } + } +#endif +} + + +/*********************************************************************************************** + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority. * + * * + * Some objects are on an important mission that must succeed. If the object is on such * + * a mission, then it will be more aggressive in its movement action. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object on a priority mission? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_On_Priority_Mission(void) const +{ + if (Mission == MISSION_ENTER) return(true); + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * * + * This will try to make this mobile object leave the map. It does this by assigning a * + * movement destination that is located off the edge of the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Retreat(void) +{ + assert(IsActive); + + enum { + FIND_EDGE, + TRAVELLING + }; + + switch (Status) { + + /* + ** Find a suitable edge to travel to and then assign destination there. + */ + case FIND_EDGE: + if (Target_Legal(NavCom)) { + Status = TRAVELLING; + } else { + + CELL cell = 0; + + /* + ** If this is part of a team, then pick the edge where the team as likely + ** entered from. + */ + if (Team.Is_Valid() && Team->Class->Origin != -1) { + cell = Map.Calculated_Cell(House->Control.Edge, Team->Class->Origin, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + /* + ** If an edge hasn't been found, then try to find one that is not based on any + ** team information. + */ + if (cell == 0) { + cell = Map.Calculated_Cell(House->Control.Edge, -1, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + assert(cell == 0); // An edge cell must be found! + + Assign_Destination(::As_Target(cell)); + Status = TRAVELLING; + } + break; + + /* + ** While travelling, monitor that all is proceeding according to plan. + */ + case TRAVELLING: + if (!Target_Legal(NavCom)) { + Status = FIND_EDGE; + } + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} diff --git a/REDALERT/FOOT.H b/REDALERT/FOOT.H new file mode 100644 index 000000000..244833b8f --- /dev/null +++ b/REDALERT/FOOT.H @@ -0,0 +1,389 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FOOT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FOOT_H +#define FOOT_H + +#include "target.h" +#include "type.h" +#include "techno.h" +#include "ftimer.h" + +class UnitClass; +class BuildingClass; + + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class FootClass : public TechnoClass +{ + public: + /* + ** This flag controls whether a range limiting effect should be in place. If + ** true, then target scanning will be limited to the range of the object + ** regardless of what was requested from the target scanning logic. This value + ** is used for ships so that they won't permanently stick on a an attack mission + ** for a target they can never get within range of. This value will toggle when + ** a path cannot be generated and the target is not within range. It will also + ** toggle when path limiting is true, but there is not target found within + ** the limited range. + */ + unsigned IsScanLimited:1; + + /* + ** If this unit has officially joined the team's group, then this flag is + ** true. A newly assigned unit to a team is not considered part of the + ** team until it actually reaches the location where the team is. By + ** using this flag, it allows a team to continue to intelligently attack + ** a target without falling back to regroup the moment a distant member + ** joins. + */ + unsigned IsInitiated:1; + + /* + ** When the player gives this object a navigation target AND that target + ** does not result in any movement of the unit, then a beep should be + ** sounded. This typically occurs when selecting an invalid location for + ** movement. This flag is cleared if any movement was able to be performed. + ** It never gets set for computer controlled units. + */ + unsigned IsNewNavCom:1; + + /* + ** There are certain cases where a unit should perform a full scan rather than + ** the more efficient "ring scan". This situation occurs when a unit first + ** appears on the map or when it finishes a multiple cell movement track. + */ + unsigned IsPlanningToLook:1; + + /* + ** Certain units have the ability to metamorphize into a building. When this + ** operation begins, certain processes must occur. During these operations, this + ** flag will be true. This ensures that any necessary special case code gets + ** properly executed for this unit. + */ + unsigned IsDeploying:1; + + /* + ** This flag tells the system that the unit is doing a firing animation. This is + ** critical to the firing logic. + */ + unsigned IsFiring:1; + + /* + ** This unit could be either rotating its body or rotating its turret. During the + ** process of rotation, this flag is set. By examining this flag, unnecessary logic + ** can be avoided. + */ + unsigned IsRotating:1; + + /* + ** If this object is current driving to a short range destination, this flag is + ** true. A short range destination is either the next cell or the end of the + ** current "curvy" track. An object that is driving is not allowed to do anything + ** else until it reaches its destination. The exception is when infantry wish to + ** head to a different destination, they are allowed to start immediately. + */ + unsigned IsDriving:1; + + /* + ** If this object is unloading from a hover transport, then this flag will be + ** set to true. This handles the unusual case of an object disembarking from the + ** hover lander yet not necessarily tethered but still located in an overlapping + ** position. This flag will be cleared automatically when the object moves to the + ** center of a cell. + */ + unsigned IsUnloading:1; + + /* + ** If this object is part of a formation, this bit will be set. The + ** formation only occurs when every member of a team is selected, and + ** only those members of the team are the ones selected. + */ + unsigned IsFormationMove:1; + + /* + ** If the navigation movement queue is to be looped rather than consumed, then + ** this flag will be true. By looping, the unit will travel through the locations + ** in the queue indefinately. + */ + unsigned IsNavQueueLoop:1; + + /* + ** If this object is scattering, then this flag will be true. While true, the + ** NavCom should not be arbitrarily changed. This flag will automatcially be + ** cleared when the object moves one cell. + */ + unsigned IsScattering:1; + + /* + ** If this object is moving onto a bridge, then this flag will be true. + */ + unsigned IsMovingOntoBridge:1; + + /* + ** This is the "throttle setting" of the unit. It is a fractional value with 0 = stop + ** and 255 = full speed. + */ + int Speed; + + /* + ** This is the override speed adjuster. Normally, this is a fixed point + ** value of 0x0100, but it can be modified by crate powerups. + */ + fixed SpeedBias; + + /* + ** For units in a formation, these values represent the distance from + ** the target destination where the unit should move to. For example, + ** in a horizontal line formation, XFormOffset would be set to some + ** value, and YFormOffset would be zero. + */ + int XFormOffset; + int YFormOffset; + + /* + ** + ** This is the desired destination of the unit. The unit will attempt to head + ** toward this target (avoiding intervening obstacles). + */ + TARGET NavCom; + TARGET SuspendedNavCom; + + /* + ** A sequence of move destinations can be given to a unit. The sequence is + ** stores as an array of movement targets. The list is terminated with a + ** TARGET_NONE. + */ + TARGET NavQueue[10]; + + /* + ** This points to the team that "owns" this object. This pointer is used to + ** quickly process the team when this object is the source of the change. An + ** example would be if this object were to be destroyed, it would inform the + ** team of this fact by using this pointer. + */ + CCPtr Team; + + /* + ** If this object is part of a pseudo-team that the player is managing, then + ** this will be set to the team number (0 - 9). If it is not part of any + ** pseudo-team, then the number will be -1. + */ + unsigned char Group; + + /* + ** This points to the next member in the team that this object is part of. This + ** is used to quickly process each team member when the team class is the source + ** of the change. An example would be if the team decided that everyone is going + ** to move to a new location, it would inform each of the objects by chaining + ** through this pointer. + */ + FootClass * Member; + + /* + ** Since all objects derived from this class move according to a path list. + ** This is the path list. It specifies, as a simple list of facings, the + ** path that the object should follow in order to reach its destination. + ** This path list is limited in size, so it might require several generations + ** of path lists before the ultimate destination is reached. The game logic + ** handles regenerating the path list as necessary. + */ + FacingType Path[CONQUER_PATH_MAX]; + + /* + ** This value keeps track of how serious the unit is in trying to reach the + ** destination specified. As blockages arise, this threshold will rise + ** unit it reaches the point of complete failure. When that event occurs, the + ** unit will realize that it cannot get to its specified destination and will + ** try to perform some other action instead. + */ + MoveType PathThreshhold; + + /* + ** When there is a complete findpath failure, this timer is initialized so + ** that a findpath won't be calculated until this timer expires. + */ + CDTimerClass PathDelay; + enum {PATH_RETRY=10}; + int TryTryAgain; // Number of retry attempts remaining. + + /* + ** If the object has recently attacked a base, then this timer will not + ** have expired yet. It is used so a building does not keep calling + ** for help from the same attacker. + */ + CDTimerClass BaseAttackTimer; + + /* + ** For formation moves, this will be the override speed. + */ + SpeedType FormationSpeed; + + /* + ** For formation moves, this will be the override maximum speed. + */ + MPHType FormationMaxSpeed; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FootClass(NoInitClass const & x) : TechnoClass(x), Team(x), PathDelay(x), BaseAttackTimer(x) {}; + FootClass(RTTIType rtti, int id, HousesType house); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Basic_Path(void); + + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Can_Demolish(void) const; + bool Is_Recruitable(HouseClass const * house=NULL) const; + bool Is_On_Priority_Mission(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Likely_Coord(void) const; + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + COORDINATE Head_To_Coord(void) const {return (HeadToCoord);}; + virtual bool Start_Driver(COORDINATE &headto); + virtual bool Stop_Driver(void); + virtual void Assign_Destination(TARGET target); + bool Is_Allowed_To_Leave_Map(void) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Mark(MarkType mark=MARK_CHANGE); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Stun(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Death_Announcement(TechnoClass const * source=0) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual void Sell_Back(int control); + virtual int Offload_Tiberium_Bail(void); + virtual TARGET Greatest_Threat(ThreatType method) const; + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual int Mission_Retreat(void); + virtual int Mission_Enter(void); + virtual int Mission_Move(void); + virtual int Mission_Capture(void); + virtual int Mission_Attack(void); + virtual int Mission_Guard(void); + virtual int Mission_Hunt(void); + virtual int Mission_Guard_Area(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + void Handle_Navigation_List(void); + void Queue_Navigation_List(TARGET target); + void Clear_Navigation_List(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Approach_Target(void); + virtual void Fixup_Path(PathType *) {}; + virtual void Set_Speed(int speed); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + int Optimize_Moves(PathType *path, MoveType threshhold); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + CELL Adjust_Dest(CELL cell) const; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + CELL Safety_Point(CELL src, CELL dst, int start, int max); + int Rescue_Mission(TARGET tarcom); + + private: + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + void Debug_Draw_Map(char const * txt, CELL start, CELL dest, bool pause); + void Debug_Draw_Path(PathType *path); + bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); + bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); + bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); + + /* + ** This is the coordinate that the unit is heading to + ** as an immediate destination. This coordinate is never further + ** than once cell (or track) from the unit's location. When this coordinate + ** is reached, then the next location in the path list becomes the + ** next HeadTo coordinate. + */ + COORDINATE HeadToCoord; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; +}; + +#endif + diff --git a/REDALERT/FTIMER.H b/REDALERT/FTIMER.H new file mode 100644 index 000000000..e24d4cfd1 --- /dev/null +++ b/REDALERT/FTIMER.H @@ -0,0 +1,716 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FTIMER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FTIMER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/16/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * BasicTimerClass::operator () -- Function operator for timer object. * + * BasicTimerClass::operator long -- Conversion to long operator. * + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * TTimerClass::operator () -- Function operator for timer object. * + * TTimerClass::operator long -- Conversion operator for timer object. * + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * CDTimerClass::operator () -- Function operator for the count down timer. * + * CDTimerClass::operator long -- Conversion to long operator function. * + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * TTimerClass::Value -- Returns with the current value of the timer. * + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FTIMER_H +#define FTIMER_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +/* +** This is a timer class that watches a constant rate timer (specified by the parameter +** type class) and provides basic timer functionality. It is possible to set the start value +** WITHOUT damaging or otherwise affecting any other timer that may be built upon the same +** specified timer class object. Treat an object of this type as if it were a "magic" integral +** long that automatically advances at the speed of the timer class object controlling it. +*/ +// Let lint know that non-virtual destructor is OK for this class. +//lint -esym(1509,BasicTimerClass) +template +class BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + BasicTimerClass(unsigned long set=0); + BasicTimerClass(NoInitClass const & ); + + ~BasicTimerClass(void); + + // Fetch current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + protected: + T Timer; // Timer regulator (ticks at constant rate). + unsigned long Started; // Time started. +}; + + +template +inline BasicTimerClass::BasicTimerClass(NoInitClass const & ) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * * + * This is the constructor for the basic timer class object. It sets the timer counting * + * up from zero at the rate of the controlling timer class object. * + * * + * INPUT: set -- Alternate initial start value for the counter. If not specified, then * + * the timer is assumed to start at zero and count upwards. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +//lint -esym(1403,BasicTimerClass::Timer) +//lint -esym(1403,BasicTimerClass::Timer) +template +inline BasicTimerClass::BasicTimerClass(unsigned long set) : + Started(Timer()-set) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * * + * The destructor for the basic timer object doesn't have to do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::~BasicTimerClass(void) +{ +} + + +template +inline unsigned long BasicTimerClass::Value(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator long -- Conversion to long operator. * + * * + * This conversion operator allows the basic timer object to function in much the same * + * manner as the integral "long" type. One can assign a long with a timer object and the * + * actual value of the timer is extracted from the object and used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::operator unsigned long(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator allows the timer to also serve as the parameter type class for * + * additional timer objects. This allows one to instantiate a controlling timer class that * + * can control (e.g., turn on or off) all timers that are based upon it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long BasicTimerClass::operator () (void) const +{ + return(Timer()-Started); +} + + +/* +** This timer class functions similarly to the basic timer class. In addition to the +** normal timer operation, this class has the ability to be stopped and started at +** will. If you have no need to start or stop the timer, then use the basic timer +** class instead. +*/ +template +class TTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + TTimerClass(unsigned long set=0); + TTimerClass(NoInitClass const & x); + + ~TTimerClass(void) {}; + + // Fetches current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + private: + unsigned long Accumulated; // Total accumulated ticks. +}; + + +template +inline TTimerClass::TTimerClass(NoInitClass const & x) : + BasicTimerClass(x) +{ +} + + +/*********************************************************************************************** + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * * + * This is the constructor for the advanced timer class object. This object class can start * + * or stop the timer under user control. * + * * + * INPUT: set -- The initial value to set the timer to. If no value is specified, then * + * the timer is assumed to start from zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::TTimerClass(unsigned long set) : + BasicTimerClass(set), + Accumulated(0) +{ +} + + +/*********************************************************************************************** + * TTimerClass::Value -- Returns with the current value of the timer. * + * * + * This routine will return with the current value of the timer. It takes into account * + * whether the timer has stopped or not so as to always return the correct value regardless * + * of that condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current value of the timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::Value(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator long -- Conversion operator for timer object. * + * * + * This conversion operator allows this timer object to function as an "rvalue" of a "long" * + * type. This is consistent with the integral "long" value. It is possible to assign a * + * timer object to a long and have the long initialized with the current value of the * + * timer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::operator unsigned long(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator for the timer class allows this timer class to be used as the * + * template parameter for other timer class objects. With this ability, one can control * + * several timers (e.g., start or stop them) by using a single controlling timer class * + * that other timers are instantiated from. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::operator () (void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * * + * This routine will stop (pause) the timer from further increments. To cause the timer * + * to begin anew, call the Start() function. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + Accumulated += BasicTimerClass::operator unsigned long(); + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * * + * This routine will resume a timer that was previously stopped with the Stop() function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Start(void) +{ + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * * + * Since this timer can be paused, this routine is used to examine the timer to see if it * + * is currently paused or active. If the timer is active, then the return value will be * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this timer currently active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool TTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + + +/* +** This timer counts down from the specified (or constructed) value down towards zero. +** The countdown rate is controlled by the timer object specified. This timer object can +** be started or stopped. It can also be tested to see if it has expired or not. An expired +** count down timer is one that has value of zero. You can treat this class object as if it +** were an integral "magic" long that automatically counts down toward zero. +*/ +template +class CDTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + CDTimerClass(unsigned long set=0); + CDTimerClass(NoInitClass const & x); + + ~CDTimerClass(void); + + // Fetches current value of count down timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + bool Was_Started(void) const { return WasStarted; } + + private: + unsigned long DelayTime; // Ticks remaining before countdown timer expires. + bool WasStarted; +}; + + +template +inline CDTimerClass::CDTimerClass(NoInitClass const & x) : + BasicTimerClass(x), WasStarted(false) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * * + * This is the constructor for the count down timer object. The optional starting value * + * can be used to initiate the timer. Because of this constructor it is possible to assign * + * a long to a count down timer object in order to begin the countdown process. * + * * + * INPUT: set -- The initial starting value for the countdown timer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::CDTimerClass(unsigned long set) : + BasicTimerClass(0), + DelayTime(set), + WasStarted(false) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * * + * The destructor for the count down timer object does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::~CDTimerClass(void) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * * + * Use this routine to fetch the current value of the timer. It takes into consideration * + * whether the timer has been stopped or not. It returns the correct value regardless of * + * this condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the correct value of this count down timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::Value(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator long -- Conversion to long operator function. * + * * + * This conversion operator allows the count down timer object to be used as if it were * + * a "magic" long that automatically counted downward at the controller class tick rate. * + * The count down object can be used in any place that an rvalue long could be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::operator unsigned long(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator () -- Function operator for the count down timer. * + * * + * This is the function operator for the count down timer object. By supporting this * + * function operator, this class (or one derived from this class) could be used as the * + * controlling timer to the timer templates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::operator () (void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * * + * This routine is used to stop (pause) the count down timer object. A timer object paused * + * in this fashion will be resumed by a call to Start() or by assigning a new count down * + * value to the timer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + DelayTime = *this; + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * * + * This routine is used to start (resume) the count down timer that was previously stopped * + * with the Stop() function. The timer will also resume when a new timer value is assigned. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Start(void) +{ + WasStarted = true; + + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * * + * Because the timer object counting can be stopped, this routine is used to determine * + * if the timer is currently paused or active. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the timer currently active? * + * * + * WARNINGS: Note that if the timer has counted down to zero, then it may be active, but * + * the value will, naturally, not change. * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool CDTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + +#endif diff --git a/REDALERT/FUNCTION.H b/REDALERT/FUNCTION.H new file mode 100644 index 000000000..6d99d8c1d --- /dev/null +++ b/REDALERT/FUNCTION.H @@ -0,0 +1,1088 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUNCTION.H 2 3/13/97 2:05p Steve_tall $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +/* +** !!!DEFINE!!! "NDEBUG" if the assertion code is to be !!!REMOVED!!! from the project. +*/ +//#define NDEBUG // ST - 5/13/2019 + +//#pragma warn -hid + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÁÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + ÚÄÁÄÄÄÄÄÄÄÄÄ¿ + ³ ³ + ³ VesselClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ VesselTypeClass + ³ ³ + AircraftTypeClass InfantryTypeClass +#endif + + +#include "watcom.h" +#include "lint.h" + + +#ifdef WIN32 +//#define WIN32_LEAN_AND_MEAN +#include +#define WWFILE_H +#define RAWFILE_H +#define MONOC_H + +#define _MAX_NAME _MAX_FNAME + +#endif + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#if (0) +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +#endif + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +#define FILE_H +#define WWMEM_H + +#ifndef WIN32 +#define TIMER_H +#endif + +#ifdef WIN32 +#include "key.h" +#endif + +#include +#include "mpu.h" +#include "bench.h" +#include "rect.h" +#include "jshell.h" +#include "buff.h" +#include "face.h" +#include "random.h" +#include "crc.h" +#include "compat.h" +#include "fixed.h" +#include "base64.h" +#include "pipe.h" +#include "xpipe.h" +#include "ramfile.h" +#include "lcw.h" +#include "lzw.h" +#include "lcwpipe.h" +#include "lzwpipe.h" +#include "lzopipe.h" +#include "crcpipe.h" +#include "shapipe.h" +#include "b64pipe.h" +#include "straw.h" +#include "xstraw.h" +#include "b64straw.h" +#include "lcwstraw.h" +#include "lzwstraw.h" +#include "lzostraw.h" +#include "crcstraw.h" +#include "shastraw.h" +#include "rndstraw.h" + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +//#include + + + +#ifdef WIN32 +#define int386x(a,b,c,d) 0 +#define int386(a,b,c) 0 +#endif + + +/* +** VQ player specific includes. +*/ +//#include +//#include + +extern bool GameActive; +extern long LParam; + +#include +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" +#include "ccini.h" +#include "ccptr.h" +#include "bar.h" + +/* +** Greenleaf specific includes. +*/ +//#include +//#include + + +extern long Frame; +CELL Coord_Cell(COORDINATE coord); + +#include "utracker.h" +#include "crate.h" +#include "rules.h" +#include "ini.h" +#include "int.h" +#include "pk.h" +#include "pkpipe.h" +#include "pkstraw.h" +#include "sha.h" +#include "blowfish.h" +#include "blowpipe.h" +#include "blwstraw.h" +#include "language.h" +#include "hsv.h" +#include "rgb.h" +#include "palette.h" +#include "palettec.h" //ST 5/13/2019 +#include "version.h" +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "statbtn.h" +#include "slider.h" +#include "list.h" +#include "drop.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "loaddlg.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "warhead.h" +#include "weapon.h" +#include "trigtype.h" +#include "teamtype.h" // Team type objects. +#include "taction.h" +#include "tevent.h" +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // map editor class +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "vessel.h" // Sea unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "carry.h" +#include "scenario.h" +#include "msglist.h" // Multiplayer chat message system +#include "session.h" // Multiplayer session class +//#include "phone.h" // Phone list manager +#include "ipxmgr.h" // IPX connection manager +//#include "nullmgr.h" // Modem connection manager +#include "readline.h" +#include "vortex.h" +#include "egos.h" +#ifdef WIN32 +//#include "pcx.h" +#endif + +#if(TEN) +#include "tenmgr.h" +#endif + +#if(MPATH) +#ifdef WIN32 +#include "mpmgrw.h" +#else +#include "mpmgrd.h" +#endif +#endif + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +bool InitDDraw(void); +bool PlayMpegMovie(const char* name); +#endif + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + +#ifdef WIN32 + +/* +** For WIN32, replace the assert macro so we get an error on the debugger screen +** where we can see it. +** +*/ +#ifndef __BORLANDC__ +#ifdef assert +#undef assert +#endif //assert +void Assert_Failure (char *expression, int line, char *file); + +#ifdef NDEBUG + #define assert(__ignore) ((void)0) +#else + #define assert(expr) ((expr)?(void)0:Assert_Failure(#expr,__LINE__,__FILE__)) +#endif //NDEBUG + +#endif //__BORLANDC__ + + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); +extern void Rebuild_Interpolated_Palette(unsigned char *interpal); +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage ,int cap); +#endif + +/* +** ADATA.CPP +*/ +char const * Anim_Name(AnimType anim); + +/* +** AIRCRAFT.CPP +*/ +bool Building_Check(void); + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); +AnimType Anim_From_Name(char const * name); + +/* +** AUDIO.CPP +*/ +VocType Voc_From_Name(char const * name); +char const * Speech_Name(VoxType speech); +char const * Voc_Name(VocType voc); +int Sound_Effect(VocType voc, fixed volume=1, int variation=1, signed short panvalue=0, HousesType house=HOUSE_NONE); +// void Speak(VoxType voice); // MBL 02.06.2020 +void Speak(VoxType voice, HouseClass *house=NULL, COORDINATE coord=0); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord, int variation=1, HousesType house=HOUSE_NONE); +bool Is_Speaking(void); + +/* +** CDFILE.CPP +*/ +#ifdef WIN32 +int harderr_handler(unsigned, unsigned, unsigned *); +#else +int harderr_handler(unsigned, unsigned, unsigned __far *); +#endif + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead); +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land); +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int damage, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void List_Copy(short const * source, int len, short * dest); +int Get_CD_Index (int cd_drive, int timeout); +int Owner_From_Name(char const * text); +CrateType Crate_From_Name(char const * name); +Rect const Shape_Dimensions(void const * shapedata, int shapenum); +void IPX_Call_Back(void); +bool Is_Counterstrike_Installed (void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Aftermath_Installed (void); +#endif + + +#if(TEN) +void Ten_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +#define ALWAYS_RELOAD_CD 1000 + +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Unselect_All_Except(ObjectClass* object); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true, bool immediate = false); +void Play_Movie(VQType name, ThemeType theme=THEME_NONE, bool clrscrn=true, bool immediate=false); +bool Main_Loop(void); +TheaterType Theater_From_Name(char const * name); +void Main_Game(int argc, char * argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const * basename); +SourceType Source_From_Name(char const * name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor); +//void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, DirType rotation=DIR_N, long scale=0x0100); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N); + +// Added for draw intercept. ST - 1/17/2019 12:31PM +void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100); +void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100, char override_owner = HOUSE_NONE); + +// Added for pip draw intercept - SKY +void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N); + +void Go_Editor(bool flag); +//long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes); +char *CC_Get_Shape_Filename(void const * shapeptr ); +void CC_Add_Shape_To_Global(void const * shapeptr, char * filename, char code ); +void Bubba_Print(char * format,...); +void Heap_Dump_Check(char * string ); +void Dump_Heap_Pointers(void); +unsigned long Disk_Space_Available(void); +void * Hires_Load(char * name); +void Shake_The_Screen(int shakes, HousesType house = HOUSE_NONE); + +/* +** COORD.CPP +*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter=true); +void Normal_Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +int Distance(TARGET target1, TARGET target2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); +void Draw_Caption(char const * text, int x, int y, int w); +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, RemapControlType * fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, int width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag); +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Plain_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); + +/* +** DISPLAY.CPP +*/ +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, const ObjectClass* object); +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell); + +ActionType Best_Object_Action(DynamicVectorClass& objects, const ObjectClass* object); +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell); + +ObjectClass* Best_Object_With_Action(const ObjectClass* object); +ObjectClass* Best_Object_With_Action(CELL cell); + +ActionType Best_Object_Action(const ObjectClass* object); +ActionType Best_Object_Action(CELL cell); + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); +bool Expansion_CS_Present(void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Expansion_AM_Present(void); +#endif +/* +** FINDPATH.CPP +*/ +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ + +/* +** INI.CPP +*/ +void Write_Scenario_INI(char *root); +bool Read_Scenario_INI(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); +void Assign_Houses(void); + +/* +** INIBIN.CPP +*/ +unsigned long Ini_Binary_Version( void ); +bool Read_Scenario_INB( CCFileClass *file, char *root, bool fresh ); +bool Valid_Scenario_INB( CCFileClass *file ); + +/* +** INICODE.CPP +*/ +bool Read_Scenario_INI_Write_INB( char *root, bool fresh ); + +/* +** INIT.CPP +*/ +void Load_Title_Page(bool visible=false); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + +/* +** JSHELL.CPP +*/ +int Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette, PicturePlaneType format); +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void *dest, int color, int frac); +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac); + +/* +** KEYFBUFF.ASM +*/ +extern "C" { + long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +} + +/* +** KEYFRAME.CPP +*/ +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); +int Get_Last_Frame_Length(void); + + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Clear_Listbox(ListClass *list); +void Clear_Vector(DynamicVectorClass *vector); +void Computer_Message(void); +int Surrender_Dialog(int text); +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text); +bool Determine_If_Using_DVD(); +bool Using_DVD(); +#endif +int Abort_Dialog(void); + +#if(TEN) +int Read_TEN_Game_Options(void); +#endif // TEN + +#if(MPATH) +int Read_MPATH_Game_Options(void); +#endif // MPATH + +#if(TEN) +/* +** CCTEN.CPP +*/ +int Init_TEN(void); +void Shutdown_TEN(void); +void Connect_TEN(void); +void Destroy_TEN_Connection(int id, int error); +void Send_TEN_Win_Packet(void); +void Send_TEN_Alliance(char *whom, int ally); +void Send_TEN_Out_Of_Sync(void); +void Send_TEN_Packet_Too_Late(void); +#endif // TEN + +#if(MPATH) +/* +** CCMPATH.CPP +*/ +int Init_MPATH(void); +void Shutdown_MPATH(void); +void Connect_MPATH(void); +void Destroy_MPATH_Connection(int id, int error); +#endif // MPATH + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(bool skirmish=false); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); +void Log_Start_Time( char *string ); +void Log_End_Time( char *string ); +void Log_Time( char *string ); +void Log_Start_Nest_Time( char *string ); +void Log_End_Nest_Time( char *string ); + +/* +** OBJECT.CPP +*/ + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char const * profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char * profile); + +char *Read_Bin_Buffer( void ); +bool Read_Bin_Init( char *buffer, int length ); +int Read_Bin_Length( char *buffer ); +bool Read_Bin_Num( void *num, int length, char *buffer ); +int Read_Bin_Pos( char *buffer ); +int Read_Bin_PosSet( unsigned int pos, char *buffer ); +bool Read_Bin_String( char *string, char *buffer ); + +char *Write_Bin_Buffer( void ); +bool Write_Bin_Init( char *buffer, int length ); +int Write_Bin_Length( char *buffer ); +bool Write_Bin_Num( void *num, int length, char *buffer ); +int Write_Bin_Pos( char *buffer ); +int Write_Bin_PosSet( unsigned int pos, char *buffer ); +bool Write_Bin_String( char *string, int length, char *buffer ); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TargetClass whom, TARGET target); +bool Queue_Destination(TargetClass whom, TARGET target); +bool Queue_Mission(TargetClass whom, MissionType mission); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RANDOM.CPP +*/ + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass const * team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger=INFANTRY_NONE); + +/* +** ROTBMP.CPP +*/ +int Rotate_Bitmap(GraphicViewPortClass *srcvp,GraphicViewPortClass *destvp,int angle); + +/* +** RULES.CPP +*/ +bool Is_MCV_Deploy(); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(Straw & file); +bool Save_Misc_Values(Pipe & file); +bool Load_MPlayer_Values(Straw & file); +bool Save_MPlayer_Values(Pipe & file); +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep); +bool Load_Game(int id); +bool Load_Game(const char *file_name); +//bool Read_Object (void * ptr, int base_size, int class_size, FileClass & file, void * vtable); // Original Read_Object prototype. ST - 9/17/2019 12:50PM +bool Read_Object(void *ptr, int class_size, FileClass & file, bool has_vtable); +bool Save_Game(int id, char const * descr, bool bargraph=false); +bool Save_Game(const char *file_name, const char *descr); +bool Write_Object (void * ptr, int class_size, FileClass & file); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); + +/* +** SCENARIO.CPP +*/ +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var); +void Post_Load_Game(int load_net); +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +void Set_Scenario_Difficulty(int difficulty); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); +int BGMessageBox(char const *text, int button1, int button2); + +/* +** SCORE.CPP +*/ +char const * Map_Selection(void); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(bool simple=false); +char const * Fetch_Password(int caption, int message, int btext=TXT_OK); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath=false); +#else +int Fetch_Difficulty(void); +#endif + +/* +** STARTUP.CPP +*/ +void Print_Error_End_Exit(char *string); +void Emergency_Exit ( int code ); + +/* +** SUPPORT.ASM +*/ + +/* +** TARGET.CPP +*/ +TechnoTypeClass const * As_TechnoType(TARGET target); +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target, bool check_active = true); +AnimClass * As_Animation(TARGET target, bool check_active = true); +BuildingClass * As_Building(TARGET target, bool check_active = true); +BulletClass * As_Bullet(TARGET target, bool check_active = true); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target, bool check_active = true); +TeamClass * As_Team(TARGET target, bool check_active = true); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target, bool check_active = true); +TriggerClass * As_Trigger(TARGET target, bool check_active = true); +TriggerTypeClass * As_TriggerType(TARGET target); +UnitClass * As_Unit(TARGET target, bool check_active = true); +VesselClass * As_Vessel(TARGET target, bool check_active = true); +bool Target_Legal(TARGET target); +ObjectClass * As_Object(TARGET target, bool check_active = true); + +/* +** TEAMTYPE.CPP +*/ +NeedType TeamMission_Needs(TeamMissionType tmtype); + +/* +** TRACKER.CPP +*/ +void Detach_This_From_All(TARGET target, bool all=true); + +/* +** TRIGGER.CPP +*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** VERSION.CPP +*/ +char const * Version_Name(void); + +/* +** WEAPON.CPP +*/ +WeaponType Weapon_From_Name(char const * name); +ArmorType Armor_From_Name(char const * name); + +/* +** Winstub.cpp +*/ +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette); + +/* +** Egos.CPP +*/ +void Show_Who_Was_Responsible (void); + + +// +// We need to know when the visible page changes +// ST - 1/4/2019 10:31AM +// +void Blit_Hid_Page_To_Seen_Buff(void); +extern bool RunningAsDLL; +extern bool RunningFromEditor; + + +#include "inline.h" + +/* +** These declarations ensure that the templates will be expanded for these specified +** types. Doing this is required because some of the body functions for this template class +** are located in a .CPP module. Doing so results in faster compilation but requires declarations +** such as this for all types that will be required. There are no actual objects of with these +** names ever created, however there are other objects of this type (with different names) that +** are created. +*/ +extern CCPtr _wefwefy1; // Previus definition was function??? +extern CCPtr y2; +extern CCPtr y3; +extern CCPtr y4; +extern CCPtr y5; +extern CCPtr y6; +extern CCPtr y7; +extern CCPtr y8; +extern CCPtr y9; +extern CCPtr y10; +extern CCPtr y11; +extern CCPtr y12; +extern CCPtr y13; +extern CCPtr y14; +extern CCPtr y15; +extern CCPtr y16; +extern CCPtr y17; +extern CCPtr y18; +extern CCPtr y19; +extern CCPtr y20; +extern CCPtr y21; +extern CCPtr y22; +extern CCPtr y23; +extern CCPtr y24; +extern CCPtr y25; +extern CCPtr y26; +extern CCPtr y27; +template <> class DynamicVectorClass >; +extern DynamicVectorClass > y002; +template <>class DynamicVectorClass >; +extern DynamicVectorClass > y001; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx1; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx2; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx3; +template <>class DynamicVectorClass; +extern DynamicVectorClass whatever; + +/* +** Debug output. ST - 6/27/2019 10:00PM +*/ +void GlyphX_Debug_Print(const char *debug_text); + +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); + +/* +** Achievement event. ST - 11/11/2019 11:39AM +*/ +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); + +#endif \ No newline at end of file diff --git a/REDALERT/FUSE.CPP b/REDALERT/FUSE.CPP new file mode 100644 index 000000000..2536251bd --- /dev/null +++ b/REDALERT/FUSE.CPP @@ -0,0 +1,194 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUSE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FuseClass::FuseClass -- Constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 BRR : Created. Gosh, what a lotta work. * + *=============================================================================================*/ +FuseClass::FuseClass(void) +{ + Timer = 0; + Arming = 0; + HeadTo = 0; + Proximity = 0; +} + + +/*********************************************************************************************** + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * * + * This starts a fuse. Fuses are proximity detonation variety but * + * can be modified to have a minimum time to elapse before detonation * + * and a maximum time to exist before detonation. Typically, the * + * timing values are used for missiles that have a minimum arming * + * distance and a limited amount of fuel. * + * * + * INPUT: location -- The coordinate where the projectile start. This * + * is needed for proper proximity tracking. * + * * + * target -- The actual impact point. Fuses are based on real * + * word coordinates. * + * * + * time -- The maximum time that the fuse may work before * + * explosion is forced. * + * * + * arming -- The minimum time that must elapse before the * + * fuse may explode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Arm_Fuse(COORDINATE location, COORDINATE target, int timeto, int arming) +{ + timeto = max(timeto, arming); + Timer = min(timeto, 0xFF); + Arming = min(arming, 0xFF); + HeadTo = target; + Proximity = Distance(location, target); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * * + * This will process the fuse and update the internal clocks as well * + * as check to see if the fuse should trigger (explode) or not. * + * * + * INPUT: newlocation -- The new location of the fuse. This is needed * + * to determine proximity explosions. * + * * + * OUTPUT: bool; Was the fuse triggered to explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +bool FuseClass::Fuse_Checkup(COORDINATE newlocation) +{ + int proximity; + + /* + ** Always decrement the fuse timer. + */ + if (Timer) Timer--; + + /* + ** If the arming countdown has not expired, then do nothing. + */ + if (Arming) { + Arming--; + } else { + + /* + ** If the timer has run out, then the warhead explodes. + */ + if (!Timer) return(true); + + proximity = Distance(newlocation, HeadTo); + if (proximity < 0x0010) return(true); + if (proximity < ICON_LEPTON_W && proximity > Proximity) { + return(true); + } + Proximity = proximity; + } + return(false); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * * + * Use this routine to output the fuse class data to the save game file specified. * + * * + * INPUT: file -- The file to output the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Write(FileClass & file) +{ + file.Write(&Timer, sizeof(Timer)); + file.Write(&Arming, sizeof(Arming)); + file.Write(&HeadTo, sizeof(HeadTo)); + file.Write(&Proximity, sizeof(Proximity)); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * * + * Use this routine to input the fuse class data from the save game file specified. * + * * + * INPUT: file -- The file to input the data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Read(FileClass & file) +{ + file.Read(&Timer, sizeof(Timer)); + file.Read(&Arming, sizeof(Arming)); + file.Read(&HeadTo, sizeof(HeadTo)); + file.Read(&Proximity, sizeof(Proximity)); +} + + diff --git a/REDALERT/FUSE.H b/REDALERT/FUSE.H new file mode 100644 index 000000000..2a222e4bd --- /dev/null +++ b/REDALERT/FUSE.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUSE_H +#define FUSE_H + +/**************************************************************************** +** The fuse is used by projectiles to determine whether detonation should +** occur. This is usually determined by tracking the distance to the +** designated target reaches zero or when the timer expires. +*/ +class FuseClass { + public: + FuseClass(void); + FuseClass(NoInitClass const &) {}; + ~FuseClass(void) {}; + + void Arm_Fuse(COORDINATE location, COORDINATE target, int time=0xFF, int arming=0); + bool Fuse_Checkup(COORDINATE newlocation); + void Fuse_Write(FileClass & file); + void Fuse_Read(FileClass & file); + COORDINATE Fuse_Target(void); + + /* + ** Fuses can detonate if enough time has elapsed. This value counts + ** down. When it reaches zero, detonation occurs. + */ + unsigned char Timer; + + private: + + /* + ** Some fuses need a certain amount of time before detonation can + ** occur. This counts down and when it reaches zero, normal fuse + ** detonation checking can occur. + */ + unsigned char Arming; + + /* + ** This is the designated impact point of the projectile. The fuse + ** will trip when the closest point to this location has been reached. + */ + COORDINATE HeadTo; + + /* + ** This is the running proximity value to the impact point. This value + ** will progressively get smaller. Detonation occurs when it reaches + ** zero or when it starts to grow larger. + */ + short Proximity; +}; + +inline COORDINATE FuseClass::Fuse_Target(void) +{ + return(HeadTo); +} + +#endif diff --git a/REDALERT/GADGET.CPP b/REDALERT/GADGET.CPP new file mode 100644 index 000000000..d288a2565 --- /dev/null +++ b/REDALERT/GADGET.CPP @@ -0,0 +1,869 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GADGET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : 01/03/95 * + * * + * Last Update : August 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GadgetClass::Action -- Base action for gadget. * + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * GadgetClass::Disable -- Disables the gadget from input processing. * + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * GadgetClass::Enable -- Enables the gadget. * + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This records the current gadget the the gadget system is "stuck on". Such a +** gadget will be processed to the exclusion of all others until the mouse button +** is no longer pressed. +*/ +GadgetClass * GadgetClass::StuckOn = 0; + +/* +** This is a copy of a pointer to the last list used by the gadget input system. +** If a change of list is detected, then all gadgets are forced to be redrawn. +*/ +GadgetClass * GadgetClass::LastList = 0; + + +/* +** This points to the gadget that is intercepting all keyboard events. +*/ +GadgetClass * GadgetClass::Focused = 0; + + +/* +** This points to the current color scheme for drawing all gadgets. +*/ +static RemapControlType _GreyScheme = {15}; +RemapControlType * GadgetClass::ColorScheme = &_GreyScheme; + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * * + * This is the normal constructor for gadget objects. A gadget object is only concerned * + * with the region on the screen to considered "its own" as well as the flags that tell * + * what mouse action should be recognized when the mouse is over this screen area. * + * * + * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * + * will be "owned" by this gadget. * + * * + * w,h -- Width and height (in pixels) of this gadget's region. * + * * + * flags -- The flags (mouse conditions) that will cause this gadget's action * + * function to be called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) : + X(x), Y(y), Width(w), Height(h), IsToRepaint(false), IsSticky(sticky), IsDisabled(false), Flags(flags) +{ + if (IsSticky) { + Flags |= LEFTPRESS|LEFTRELEASE; + } +} + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * * + * This is the copy constructor for the gadget object. It will try to duplicate the * + * righthand gadget. * + * * + * INPUT: gadget -- Reference to the initilization gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(GadgetClass const & gadget) : + X(gadget.X), + Y(gadget.Y), + Width(gadget.Width), + Height(gadget.Height), + IsToRepaint(gadget.IsToRepaint), + IsSticky(gadget.IsSticky), + IsDisabled(gadget.IsDisabled), + Flags(gadget.Flags) +{ +} + + +/*********************************************************************************************** + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * * + * This is the destructor for the gadget object. It will clear the focus from this gadget * + * if this gadget currently has the focus. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass::~GadgetClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } + + if (this == StuckOn) { + StuckOn = NULL; + } + + if (this == LastList) { + LastList = NULL; + } + + if (this == Focused) { + Focused = NULL; + } +} + + +/*************************************************************************** + * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * + * area and the appropriate flag is set, then call Action(). * + * * + * INPUT: int key, int mousex, int mousey * + * * + * OUTPUT: true or false * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +{ + /* + ** Set flags to match only those events that occur AND are being looked for. If + ** the result is NULL, then we know that this button should be ignored. + */ + flags &= Flags; + + /* + ** If keyboard input should be processed by this "gadget" and keyboard input is + ** detected, then always call the action function. It is up to the action function + ** in this case to either ignore the keyboard input or not. + ** + ** For mouse actions, check to see if the mouse is in the region of the button + ** before calling the associated action function. This is the typical action for + ** buttons. + */ + if (this == StuckOn || + (flags & KEYBOARD) || + (flags && (mousex - X) < Width && (mousey - Y) < Height)) { + + return(Action(flags, key)); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Enable -- Enables the gadget. * + * * + * This function enables the gadget. An enabled gadget will be processed for input * + * purposes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Enable(void) +{ + IsDisabled = false; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Disable -- Disables the gadget from input processing. * + * * + * This routine will disable the gadget. A disabled gadget might be rendered, but is * + * ignored for input processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Disable(void) +{ + IsDisabled = true; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * * + * Use this routine if an individual gadget needs to be removed from the list of gadgets. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * + * gadget wasn't in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Remove(void) +{ + Clear_Focus(); + return(GadgetClass *)LinkClass::Remove(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * * + * This returns with the next gadget's pointer. It is identical to the base Get_Next() * + * function, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the next gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Next(void) const +{ + return(GadgetClass*)LinkClass::Get_Next(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * * + * This routine will return the previous gadget in the list. It is identical to the base * + * function Get_Prev, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Prev(void) const +{ + return(GadgetClass*)LinkClass::Get_Prev(); +} + + +/*********************************************************************************************** + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * * + * This function will delete all gadgets in the list. It is the counterpart to the * + * Create_One_Of functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Any references to these gadget become invalidated by this routine. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Delete_List(void) +{ + GadgetClass * g = this; + + /* + ** Move to head of the list. + */ + while (g->Get_Prev()) { + g = g->Get_Prev(); + } + + /* + ** First delete all the gadgets following the first one. The reason the first one + ** is kept around is that sometimes deleting one gadget will result in related gadgets + ** in the same list also being deleted. The first gadget will always contain the + ** correct gadget pointer. + */ + while (g) { + g->Clear_Focus(); + + GadgetClass * temp = g; + g = g->Get_Next(); + delete temp; + } +} + + +/*********************************************************************************************** + * GadgetClass::Action -- Base action for gadget. * + * * + * This handles the base level action that a gadget performs when a qualifying input event * + * is detected. This sets the redraw flag and returns true (to stop further processing). * + * If no qualifying input event was detected, but this routine was called anyway, then * + * don't perform any action. The call to this routine, in that case, must have been forced * + * for some other reason. * + * * + * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * + * no qualifying event occurred. * + * * + * OUTPUT: bool; Should further gadget list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Action(unsigned flags, KeyNumType &) +{ + /* + ** If any of the event flags are active, then this indicates that something probably + ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that + ** any sticky flags are cleared up. + */ + if (flags) { + IsToRepaint = true; + Sticky_Process(flags); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * * + * At this level, there is no actual rendering taking place with the call to Draw_Me, but * + * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * + * returns true, they will perform their custom rendering. * + * * + * INPUT: forced -- Is this redraw forced by outside circumstances? * + * * + * OUTPUT: bool; Should the gadget imagery be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Draw_Me(int forced) +{ + if (forced || IsToRepaint) { + IsToRepaint = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * * + * Use this function to cause all gadget in the list to be redrawn regardless of the state * + * of the IsToRepaint flag. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +void GadgetClass::Draw_All(bool forced) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + gadget->Draw_Me(forced); + gadget = gadget->Get_Next(); + } +} + + +/*************************************************************************** + * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* + * * + * INPUT: none. * + * * + * OUTPUT: key pressed. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +KeyNumType GadgetClass::Input(void) +{ + int mousex, mousey; + KeyNumType key; + unsigned flags; + int forced = false; + + /* + ** Record this list so that a forced redraw only occurs the FIRST time the + ** gadget list is passed to this routine. + */ + if (LastList != this) { + LastList = this; + forced = true; + StuckOn = NULL; + Focused = NULL; + } + + /* + ** Fetch any pending keyboard input. + */ + key = Keyboard->Check(); + if (key != 0) { + key = Keyboard->Get(); + } + +#ifdef WIN32 +#ifdef CHEAT_KEYS + if (key == KN_K && !Debug_Map && (Debug_Flag || Debug_Playtest)) { + /* + ** time to create a screen shot using the PCX code (if it works) + */ + if (!Debug_MotionCapture) { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + CDFileClass file; + char filename[30]; + +// Hide_Mouse(); + SeenBuff.Blit(temp_page); +// Show_Mouse(); + for (int lp = 0; lp < 99; lp ++) { + sprintf(filename, "scrsht%02d.pcx", lp); + file.Set_Name(filename); + if (!file.Is_Available()) break; + } + + file.Cache(200000); + Write_PCX_File(file, temp_page, & GamePalette); + Sound_Effect(VOC_BEEP); + } + } +#endif +#endif + + /* + ** For mouse button clicks, the mouse position is actually held in the MouseQ... + ** globals rather than their normal Mouse... globals. This is because we need to + ** know the position of the mouse at the exact instant when the click occurred + ** rather the the mouse position at the time we get around to this function. + */ + if (((key&0xFF) == KN_LMOUSE) || ((key&0xFF) == KN_RMOUSE)) { + mousex = Keyboard->MouseQX; + mousey = Keyboard->MouseQY; + } else { + mousex = Get_Mouse_X(); + mousey = Get_Mouse_Y(); + } + + /* + ** Set the mouse button state flags. These will be passed to the individual + ** buttons so that they can determine what action to perform (if any). + */ + flags = 0; + if (key) { + if (key == KN_LMOUSE) { + flags |= LEFTPRESS; + } + if (key == KN_RMOUSE) { + flags |= RIGHTPRESS; + } + if (key == (KN_LMOUSE | KN_RLSE_BIT)) { + flags |= LEFTRELEASE; + } + if (key == (KN_RMOUSE | KN_RLSE_BIT)) { + flags |= RIGHTRELEASE; + } + } + + /* + ** If the mouse wasn't responsible for this key code, then it must be from + ** the keyboard. Flag this fact. + */ + if (key && !flags) { + flags |= KEYBOARD; + } + + /* + ** Mouse button up or down action is ignored if there is a keyboard event. This + ** allows keyboard events to fall through normally even if the mouse is over a + ** gadget that is flagged for LEFTUP or RIGHTUP. + */ + if (!key) { + + /* + ** Check for the mouse being held down. We can't use the normal input system + ** for this, so we must examine the actual current state of the mouse + ** buttons. As a side note, if we determine that the mouse button isn't being + ** held down, then we automatically know that it must be up -- set the flag + ** accordingly. + */ + if (Keyboard->Down(KN_LMOUSE)) { + flags |= LEFTHELD; + } else { + flags |= LEFTUP; + } + if (Keyboard->Down(KN_RMOUSE)) { + flags |= RIGHTHELD; + } else { + flags |= RIGHTUP; + } + } + + /* + ** If "sticky" processing is active, then only process the stuck gadget. + */ + if (StuckOn) { + StuckOn->Draw_Me(false); + GadgetClass * oldstuck = StuckOn; + StuckOn->Clicked_On(key, flags, mousex, mousey); + if (StuckOn) { + StuckOn->Draw_Me(false); + } else { + oldstuck->Draw_Me(false); + } + } else { + + /* + ** If there is a gadget that has the keyboard focus, then route all keyboard + ** events to it. + */ + if (Focused && (flags & KEYBOARD)) { + Focused->Draw_Me(false); + Focused->Clicked_On(key, flags, mousex, mousey); + if (Focused) { + Focused->Draw_Me(false); + } + } else { + + /* + ** Sweep through all the buttons in the chain and pass the current button state + ** and keyboard input data to them. These routines will detect whether they should + ** perform some action and return a flag to this effect. They also have the option + ** of changing the key value so that an appropriate return value is use for this + ** processing routine. + */ + GadgetClass * next_button = this; + while (next_button != NULL) { + + /* + ** Maybe redraw the button if it needs to or is being forced to redraw. + */ + next_button->Draw_Me(forced); + + if (!next_button->IsDisabled) { + + /* + ** Process this button. If the button was recognized and action was + ** performed, then bail from further processing (speed reasons?). + */ + if (next_button->Clicked_On(key, flags, mousex, mousey)) { + + /* + ** Some buttons will require repainting when they perform some action. + ** Do so at this time. + */ + next_button->Draw_Me(false); + break; + } + } + + next_button = next_button->Get_Next(); + } + } + } + return(key); +} + + +/*********************************************************************************************** + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * * + * This examines the gadget list looking for on that has the same ID as specified. If that * + * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * + * or ones derived from it can have an ID value, we know that the returned pointer is at * + * least of the ControlClass type. * + * * + * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * + * a NULL will always be returned. * + * * + * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * + * If no matching gadget was found, then NULL is returned. * + * * + * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * + * will return a pointer to the first one only. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass * GadgetClass::Extract_Gadget(unsigned id) +{ + GadgetClass * g = this; + + if (id != 0) { + while (g != NULL) { + if (g->Get_ID() == id) { + return((ControlClass *)g); + } + g = g->Get_Next(); + } + } + return(0); +} + + +/*********************************************************************************************** + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * * + * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * + * Draw_Me function called at the next available opportunity. Usually, this is the next * + * time the Input() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Flag_To_Redraw(void) +{ + IsToRepaint = true; +} + + +/*********************************************************************************************** + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * * + * This function examines the event flags and handles any "sticky" processing required. * + * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * + * be processed to the exclusion of all other gadgets while the mouse button is held * + * down. * + * * + * INPUT: flags -- The event flags that triggered the call to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Sticky_Process(unsigned flags) +{ + if (IsSticky && (flags & LEFTPRESS)) { + StuckOn = this; + } + if (StuckOn == this && (flags & LEFTRELEASE)) { + StuckOn = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * * + * This will set the focus to this gadget regardless of any current focus setting. If there * + * is another gadget that has focus, it will have its focus cleared before this gadget will * + * get the focus. A focused gadget is one that has all keyboard input routed to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Focus(void) +{ + if (Focused) { + Focused->Flag_To_Redraw(); + Focused->Clear_Focus(); + } + Flags |= KEYBOARD; + Focused = this; +} + + +/*********************************************************************************************** + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * * + * Use this function to clear the focus for the gadget. If the gadget doesn't currently * + * have focus, then this routine will do nothing. For added functionality, overload this * + * virtual function so that gadget specific actions may be take when focus is lost. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Clear_Focus(void) +{ + if (Focused == this) { + Flags &= ~KEYBOARD; + Focused = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * * + * If this object has the keyboard focus, then this routine will return true. When the * + * gadget has keyboard focus, all keyboard events get routed to the gadget. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this gadget have the keyboard focus? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool GadgetClass::Has_Focus(void) +{ + return(this == Focused); +} + + +/*********************************************************************************************** + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * * + * This function is mostly for supporting HidPage drawing. If it returns true, it means * + * the application needs to re-blit the HidPage forward, after calling the list's Input(). * + * * + * INPUT: none * + * * + * OUTPUT: true = an item needs redrawing, false = no items need redrawing * + * * + * WARNINGS: It is assumed 'this' is the head of the list. * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +int GadgetClass::Is_List_To_Redraw(void) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + if (gadget->IsToRepaint) { + return (true); + } + gadget = gadget->Get_Next(); + } + return (false); +} + + +/*********************************************************************************************** + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * * + * This routine helps with moving a gadget's location. It will set the gadgets upper * + * left corner to the pixel location specified. * + * * + * INPUT: x,y -- The X and Y position to put the gadget's upper left corner to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Position(int x, int y) +{ + X = x; + Y = y; +} + diff --git a/REDALERT/GADGET.H b/REDALERT/GADGET.H new file mode 100644 index 000000000..1641b75f7 --- /dev/null +++ b/REDALERT/GADGET.H @@ -0,0 +1,273 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GADGET.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.H * + * * + * Programmer : Maria del Mar McCready Legg * + * * + * Start Date : January 3, 1995 * + * * + * Last Update : January 3, 1995 [MML] * + * * + * * + * LinkClass [This is the linked list manager class. It keeps a record * + * ³ of the next and previous gadget in the list. It is possible * + * ³ delete a gadget out of the middle of the list with this * + * ³ class.] * + * ³ * + * GadgetClass [The is the basic gadget class. It handles processing of * + * ³ input events and dispatching the appropriate functions. * + * ³ All gadgets must be derived from this class.] * + * ÃÄÄÄÄ¿ * + * ³ ³ * + * ³ ListClass [This list class functions like a list box does in Windows. It * + * ³ keeps track of a list of text strings. This list can be * + * ³ scrolled and an item selected. If the list becomes larger than * + * ³ can be completely displayed, it will automatically create a * + * ³ slider (at the right edge) to manage the scrolling.] * + * ³ * + * ControlClass [This class adds the concept of giving an ID number to the * + * ³ gadget. This ID can then be returned from the Input() * + * ³ function as if it were a pseudo-keystroke. Additionally, * + * ³ the ability to inform another button that this button has * + * ³ been actioned is allowed. This ability allows one button * + * ³ to watch what happens to another button. Example: a list * + * ³ box gadget can tell when an attached slider has been * + * ³ touched.] * + * ÚÄÄÄÄÄÄÄÅÄÄÄÄ¿ * + * ³ ³ ³ * + * ³ ³ GaugeClass [This class looks similar to Windows slider, but has * + * ³ ³ ³ a different controlling logic. There is no thumb and * + * ³ ³ ³ it serves as a simple variable control setting. This * + * ³ ³ ³ is analogous to a volume slider.] * + * ³ ³ ³ * + * ³ ³ SliderClass [The slider class is similar to the typical Windows slider. It * + * ³ ³ has a current setting, a thumb, and a controllable scale. This * + * ³ ³ is the object created to handle a scrolling list box.] * + * ³ ³ * + * ³ EditClass * + * ³ * + * ³ * + * ToggleClass [The toggle class is used for buttons that have an image and behave just * + * ³ like the buttons in Windows do. That is, they have a separate visual for * + * ³ when they are pressed and raised. They are officially triggered (return * + * ³ their ID number) when the mouse button is released while over the button. * + * ³ This class doesn't perform any rendering itself. It merely provides the * + * ³ logic so that the derived classes will function correctly.] * + * ÚÄÁÄÄÄÄ¿ * + * ³ ³ * + * ³ TextButtonClass [The text button functions like a normal Windows style button, but * + * ³ the imagery is based on text that is displayed on the button. A * + * ³ typical example would be the "OK" or "Cancel" buttons.] * + * ³ * + * ShapeButtonClass [The shape buttons is similar to the TextButton but instead of text * + * being used to give the button its imagery, an actual shape is used * + * instead. This allows graphic buttons. These are similar to the up/down * + * arrows seen in a Windows slider.] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GADGET_H +#define GADGET_H + +#include "link.h" + +class ControlClass; + +class GadgetClass : public LinkClass +{ + public: + friend class DLLExportClass; // ST - 5/13/2019 + + typedef enum FlagEnum { + LEFTPRESS = 0x0001, // Left mouse button press. + LEFTHELD = 0x0002, // Left mouse button is being held down. + LEFTRELEASE = 0x0004, // Left mouse button released. + LEFTUP = 0x0008, // Left mouse button is being held up. + RIGHTPRESS = 0x0010, // Right mouse button press. + RIGHTHELD = 0x0020, // Right mouse button is being held down. + RIGHTRELEASE = 0x0040, // Right mouse button released. + RIGHTUP = 0x0080, // Right mouse button is being held up. + KEYBOARD = 0x0100 // Keyboard input processing (maybe). + } FlagEnum; + + GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky=false); + GadgetClass(NoInitClass const & x) : LinkClass(x) {}; + GadgetClass(void) {}; + GadgetClass(GadgetClass const & gadget); + virtual ~GadgetClass(void); + + /* + ** Gadget list management functions. + */ + virtual KeyNumType Input(void); + virtual void Draw_All(bool forced=true); + virtual void Delete_List(void); + virtual ControlClass * Extract_Gadget(unsigned id); + virtual void Flag_List_To_Redraw(void) {LastList = 0;}; + virtual GadgetClass * Remove(void); + virtual GadgetClass * Get_Next(void) const; + virtual GadgetClass * Get_Prev(void) const; + + /* + ** Manages individual gadget states and actions. + */ + virtual void Disable(void); + virtual void Enable(void); + virtual unsigned Get_ID(void) const {return 0;}; + virtual void Flag_To_Redraw(void); + virtual void Peer_To_Peer(unsigned , KeyNumType & , ControlClass & ) {}; + virtual void Set_Focus(void); + virtual void Clear_Focus(void); + virtual bool Has_Focus(void); + virtual int Is_List_To_Redraw(void); + virtual bool Is_To_Redraw(void) {return (IsToRepaint);} + virtual void Set_Position(int x, int y); + + /* + ** General render function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** Sets the current color scheme + */ + static void Set_Color_Scheme(RemapControlType *scheme) + { ColorScheme = scheme; } + + static RemapControlType * Get_Color_Scheme(void) + { return (ColorScheme); } + + /* + ** This is the coordinates and dimensions of the gadget region. These are in + ** absolute screen pixel coordinates. + */ + int X; + int Y; + int Width; + int Height; + + protected: + + /* + ** Processes the event flags so that if this gadget needs to "stick" or + ** "unstick", it will be properly flagged. Call this function if you are + ** going to clear the button press flags before calling the base class + ** Action() function. Otherwise, calling this function manually, is + ** unnecessary since the base class Action() function already does so. + */ + virtual void Sticky_Process(unsigned flags); + + /* + ** This is the action function that will be called whenever the flags and mouse + ** input indicates. This is the main method by which this button performs a useful + ** function. + */ + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This is a record of the last list passed to the Input() function. If a list + ** different than the last recorded one is detected, then the draw function is + ** called for every gadget in the list. This causes all buttons to be redrawn the + ** fire time Input() is called without forced a manual call to Draw_All(). + */ + static GadgetClass * LastList; + + /* + ** This points to the gadget that has the keyboard focus. All keyboard only + ** events are fed to this gadget to the exclusion of all others. + */ + static GadgetClass * Focused; + + /* + ** This button should call the Draw_Me function because some graphic element needs + ** to be redrawn. This flag is set by default if the Action function is called. + */ + unsigned IsToRepaint:1; + + public: // HACK HACK HACK.. this is here because the sidebar buttons are static. + /* + ** A sticky button is one that is processed to the exclusion of all other buttons + ** IF the mouse was pressed down while over this button and the mouse continues + ** to remain pressed. This is the standard behavior for all normal Windows style + ** buttons. + */ + unsigned IsSticky:1; + + // ajw - Publicized StuckOn 7/30/98 (was protected) + /* + ** If there is a sticky button being processed, then this will point to it. A sticky + ** button is one that will ONLY be processed while the mouse button is being + ** held down. + */ + static GadgetClass * StuckOn; + + protected: + + /* + ** If the button is disabled, then it won't be processed by the input function. It will + ** have its Draw_Me function called as necessary. In order to not display the button + ** at all, the appropriate draw function should perform no action -- just return. Or, + ** just remove the button from the list. + */ + unsigned IsDisabled:1; + + /* + ** These are the action flags that are used to determine when the action function + ** should be called. Example: If this gadget only wants the action button called when + ** the left mouse button is pressed over the its region, then the flag will be set + ** to LEFTPRESS. + */ + unsigned Flags; + + /* + ** This is the current color scheme; it must be initialized by the app. + */ + static RemapControlType *ColorScheme; + + private: +public: //ST - 5/14/2019 + virtual int Clicked_On(KeyNumType & key, unsigned flags, int x, int y); +}; + +//PG +//inline GadgetClass::FlagEnum operator |(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator &(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator ~(GadgetClass::FlagEnum); + +inline GadgetClass::FlagEnum operator|(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline GadgetClass::FlagEnum operator&(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline GadgetClass::FlagEnum operator~(GadgetClass::FlagEnum a) +{ + return static_cast(~static_cast(a)); +} + + +#endif diff --git a/REDALERT/GAMEDLG.CPP b/REDALERT/GAMEDLG.CPP new file mode 100644 index 000000000..bd3564b5d --- /dev/null +++ b/REDALERT/GAMEDLG.CPP @@ -0,0 +1,517 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAMEDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "gamedlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#define GERMAN_OFFSET_Y 4 //VG + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); +#endif + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 12/31/1994 MML : Created. * + *=============================================================================================*/ +void GameControlsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 232 * RESFACTOR; // dialog width + int d_dialog_h = 141 * RESFACTOR; // dialog height + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + int d_top_margin = 25 * RESFACTOR; + + int d_txt6_h = (6 * RESFACTOR) + 1; // ht of 6-pt text + int d_margin1 = (5 * RESFACTOR); // large margin + int d_margin2 = (2 * RESFACTOR); // small margin + + int d_speed_w = d_dialog_w - (34 * RESFACTOR); + int d_speed_h = 6 * RESFACTOR; + int d_speed_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h; +#endif + + int d_scroll_w = d_dialog_w - (34 * RESFACTOR); + int d_scroll_h = 6 * RESFACTOR; + int d_scroll_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h; +#endif + + int d_visual_w = d_dialog_w - (40 * RESFACTOR); + int d_visual_h = 9 * RESFACTOR; + int d_visual_x = d_dialog_x + (20 * RESFACTOR); + int d_visual_y = d_scroll_y + d_scroll_h + d_txt6_h + (d_margin1 * 2); + + int d_sound_w = d_dialog_w - (40 * RESFACTOR); + int d_sound_h = (9 * RESFACTOR); + int d_sound_x = d_dialog_x + (20 * RESFACTOR); + int d_sound_y = d_visual_y + d_visual_h + d_margin1; + + int d_ok_w = 20 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_cx - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + +#ifdef WOLAPI_INTEGRATION + int d_wol_x = d_sound_x; + int d_wol_y = d_sound_y + d_sound_h + d_margin1; + int d_wol_w = d_sound_w; + int d_wol_h = d_sound_h; + + bool bShowWolapi = ( pWolapi && !pWolapi->bConnectionDown ); + if( bShowWolapi ) + { + // Enlarge dialog and shift ok button down. + d_dialog_h += d_wol_h + d_margin1; + d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + //d_ok_y += d_wol_h + d_margin1; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + } +#endif + + /* + ** Button Enumerations + */ +#ifdef WOLAPI_INTEGRATION + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_WOLAPI, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#else + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#endif + + /* + ** Dialog variables + */ + KeyNumType input; + + int gamespeed = Options.GameSpeed; + int scrollrate = Options.ScrollRate; + int selection; + bool pressed = false; + int curbutton = 0; + TextButtonClass *buttons[BUTTON_COUNT - BUTTON_FIRST]; + TextPrintType style; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands; // button list + + SliderClass gspeed_btn(BUTTON_SPEED, d_speed_x, d_speed_y, d_speed_w, d_speed_h, true); + SliderClass scrate_btn(BUTTON_SCROLLRATE, d_scroll_x, d_scroll_y, d_scroll_w, d_scroll_h, true); + TextButtonClass visual_btn(BUTTON_VISUAL, TXT_VISUAL_CONTROLS, TPF_BUTTON, d_visual_x, d_visual_y, d_visual_w, d_visual_h); + TextButtonClass sound_btn(BUTTON_SOUND, TXT_SOUND_CONTROLS, TPF_BUTTON, d_sound_x, d_sound_y, d_sound_w, d_sound_h); + TextButtonClass okbtn(BUTTON_OK, TXT_OPTIONS_MENU, TPF_BUTTON, d_ok_x, d_ok_y); + okbtn.X = (SeenBuff.Get_Width()-okbtn.Width)/2; + +#ifdef WOLAPI_INTEGRATION + TextButtonClass wol_btn( BUTTON_WOLAPI, TXT_WOL_OPTTITLE, TPF_BUTTON, d_wol_x, d_wol_y, d_wol_w, d_wol_h ); +#endif + + /* + ** Various Inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build button list + */ + commands = &okbtn; + gspeed_btn.Add_Tail(*commands); + scrate_btn.Add_Tail(*commands); + visual_btn.Add_Tail(*commands); + sound_btn.Add_Tail(*commands); +#ifdef WOLAPI_INTEGRATION + if( bShowWolapi ) + wol_btn.Add_Tail(*commands); +#endif + /* + ** Init button states + ** For sliders, the thumb ranges from 0 - (maxval-1), so to convert the + ** thumb value to a real-world value: + ** val = (MAX - slider.Get_Value()) - 1; + ** and, + ** slider.Set_Value(-(val + 1 - MAX)); + */ + gspeed_btn.Set_Maximum(OptionsClass::MAX_SPEED_SETTING); // varies from 0 - 7 + gspeed_btn.Set_Thumb_Size(1); + gspeed_btn.Set_Value((OptionsClass::MAX_SPEED_SETTING-1) - gamespeed); + + scrate_btn.Set_Maximum(OptionsClass::MAX_SCROLL_SETTING); // varies from 0 - 7 + scrate_btn.Set_Thumb_Size(1); + scrate_btn.Set_Value((OptionsClass::MAX_SCROLL_SETTING-1) - scrollrate); + + /* + ** Fill array of button ptrs. + */ + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = &visual_btn; + buttons[3] = &sound_btn; +#ifdef WOLAPI_INTEGRATION + buttons[4] = &wol_btn; + buttons[5] = &okbtn; +#else + buttons[4] = &okbtn; +#endif + /* + ** Processing loop. + */ + bool process = true; + bool display = true; + bool refresh = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + Map.Flag_To_Redraw(true); + Map.Render(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_GAME_CONTROLS, d_dialog_x, d_dialog_y, d_dialog_w); + Show_Mouse(); + display = false; + refresh = true; + } + + if (refresh) { + Hide_Mouse(); + + /* + ** Label the game speed slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SPEED, d_speed_x, d_speed_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print(TXT_SLOWER, d_speed_x, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_FASTER, d_speed_x + d_speed_w, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + /* + ** Label the scroll rate slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SCROLLRATE, d_scroll_x, d_scroll_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print (TXT_SLOWER, d_scroll_x, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print (TXT_FASTER, d_scroll_x + d_scroll_w, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + commands->Draw_All(); + + Show_Mouse(); + refresh = false; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + case (BUTTON_SPEED | KN_BUTTON): + curbutton = (BUTTON_SPEED - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_SCROLLRATE | KN_BUTTON): + curbutton = (BUTTON_SCROLLRATE - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_VISUAL | KN_BUTTON): + selection = BUTTON_VISUAL; + pressed = true; + break; + + case (BUTTON_SOUND | KN_BUTTON): + selection = BUTTON_SOUND; + pressed = true; + break; + + case (BUTTON_OK | KN_BUTTON): + selection = BUTTON_OK; + pressed = true; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOLAPI | KN_BUTTON): + selection = BUTTON_WOLAPI; + pressed = true; + break; +#endif + + case (KN_ESC): + process = false; + break; + + case (KN_LEFT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(1); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(1); + } + break; + + case (KN_RIGHT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(0); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(0); + } + break; + + case (KN_UP): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton--; // Skip over missing button. + } +#endif + if (curbutton < 0) { + curbutton = (BUTTON_COUNT - BUTTON_FIRST - 1); + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_DOWN): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton++; // Skip over missing button. + } +#endif + if (curbutton > (BUTTON_COUNT - BUTTON_FIRST - 1) ) { + curbutton = 0; + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_FIRST; + pressed = true; + break; + + default: + break; + } + + /* + ** Perform some action. Either to exit the dialog or bring up another. + */ + if (pressed) { + + /* + ** Record the new options slider settings. + ** The GameSpeed data member MUST NOT BE SET HERE! It will cause multiplayer + ** games to go out of sync. It's set by virtue of the event being executed. + */ + if (gamespeed != ((OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value()) ) { + gamespeed = (OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value(); + OutList.Add(EventClass(EventClass::GAMESPEED, gamespeed)); + } + + if (scrollrate != ((OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value()) ) { + scrollrate = (OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value(); + Options.ScrollRate = scrollrate; + } + process = false; + + /* + ** Save the settings in such a way that the GameSpeed is only set during + ** the save process; restore it when we're done, so multiplayer games don't + ** go out of sync. + */ + if (Session.Type == GAME_NORMAL) { + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + } else { + int old = Options.GameSpeed; // save orig value + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + Options.GameSpeed = old; // restore old value + } + + /* + ** Possibly launch into another dialog if so directed. + */ + switch (selection) { + case (BUTTON_VISUAL): + VisualControlsClass().Process(); + process = true; + display = true; + refresh = true; + break; + + case (BUTTON_SOUND): + if (!SoundType) { + WWMessageBox().Process(Text_String(TXT_NO_SOUND_CARD)); + process = true; + display = true; + refresh = true; + } else { + SoundControlsClass().Process(); + process = true; + display = true; + refresh = true; + } + break; + +#ifdef WOLAPI_INTEGRATION + case BUTTON_WOLAPI: + if( WOL_Options_Dialog( pWolapi, true ) ) + { + // The game ended while in this dialog. + process = false; + } + else + { + process = true; + display = true; + refresh = true; + } + break; +#endif + + case (BUTTON_OK): + break; + } + + pressed = false; + } + } +} + + diff --git a/REDALERT/GAMEDLG.H b/REDALERT/GAMEDLG.H new file mode 100644 index 000000000..0629fe4cc --- /dev/null +++ b/REDALERT/GAMEDLG.H @@ -0,0 +1,49 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAMEDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAMEDLG_H +#define GAMEDLG_H + +#include "gadget.h" + +class GameControlsClass +{ + public: + GameControlsClass(void) {}; + void Process(void); +}; + + +#endif + diff --git a/REDALERT/GAUGE.CPP b/REDALERT/GAUGE.CPP new file mode 100644 index 000000000..fe536e9d1 --- /dev/null +++ b/REDALERT/GAUGE.CPP @@ -0,0 +1,536 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAUGE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GaugeClass::Action -- Handles input events for the gauge. * + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * GaugeClass::Set_Value -- Set the value of the gauge. * + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * GaugeClass::GaugeClass -- class constructor * + * * + * INPUT: id -- button ID * + * * + * x,y -- upper-left corner, in pixels * + * * + * w,h -- width, height, in pixels * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +GaugeClass::GaugeClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTHELD|LEFTPRESS|LEFTRELEASE, true) +{ + Set_Maximum(255); + Set_Value(0); + + HasThumb = true; + IsHorizontal = (w > h); + IsColorized = true; + + ClickDiff = 0; +} + + +/*********************************************************************************************** + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * * + * This routine will set the maximum value for the gauge. This is the largest value that * + * the current setting may reach. The ability to change this allows the gauge to use and * + * return values that are convenient for the programmer's current needs. * + * * + * INPUT: value -- The value to use as the gauge maximum. * + * * + * OUTPUT: bool; Was the gauge maximum changed? A false indicates that the specified value * + * already matches the current maximum. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Maximum(int value) +{ + if (value != MaxValue) { + MaxValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Set_Value -- Set the value of the gauge. * + * * + * This routine will set the current value for the gauge. This value is clipped to the * + * limits of the gauge maximum. * + * * + * INPUT: value -- The value to set at the new current value. * + * * + * OUTPUT: bool; Was the current setting changed? A false indicates that the setting * + * specified is the same as what was already there. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Value(int value) +{ + value = Bound(value, 0, MaxValue); + if (value != CurValue) { + CurValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * * + * Use this routine to convert the specified pixel offset into a gauge value. This is used * + * in translating mouse clicks into a corresponding setting for the gauge. * + * * + * INPUT: pixel -- The pixel offset form the start of the gauge. * + * * + * OUTPUT: Returns with the setting value in gauge coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Pixel_To_Value(int pixel) +{ + int maximum; + + if (IsHorizontal) { + pixel -= X+1; + maximum = Width; + } else { + pixel -= Y+1; + maximum = Height; + } + maximum -= 2; + pixel = Bound(pixel, 0, maximum); + return(MaxValue * fixed(pixel, maximum)); +// return(Fixed_To_Cardinal(MaxValue, Cardinal_To_Fixed(maximum, pixel))); +} + + +/*********************************************************************************************** + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * * + * Use this routine to convert the specified gauge value into a pixel offset from the * + * star of the gauge. This is used for thumb positioning. * + * * + * INPUT: value -- The value to convert to a pixel offset. * + * * + * OUTPUT: Returns with the pixel offset of the specified value from the start of the * + * gauge. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Value_To_Pixel(int value) +{ + int maximum; + int start; + if (IsHorizontal) { + maximum = Width; + start = X; + } else { + maximum = Height; + start = Y; + } + maximum -= 2; + return(start + maximum * fixed(value, MaxValue)); +// return(start + Fixed_To_Cardinal(maximum, Cardinal_To_Fixed(MaxValue, value))); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * * + * OUTPUT: bool; Was the gauge redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + if (IsColorized) { + int middle = Value_To_Pixel(CurValue); + int color = GadgetClass::Get_Color_Scheme()->Bright; + if (IsHorizontal) { + if (middle >= (X + 1)) + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, color); + } else { + if (middle >= (Y + 1)) + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, color); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Action -- Handles input events for the gauge. * + * * + * This routine will handle input event processing for the gauge. It will adjust the * + * current setting of the gauge according to the mouse position. * + * * + * INPUT: flags -- The input event that is the reason for this function call. * + * key -- The key code that caused the event. * + * * + * OUTPUT: bool; Was the even recognized, processed, and no further gadget scanning is * + * desired (for this pass). * + * * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there's no thumb on this gauge, it's a display-only device. + */ + if (!HasThumb) { + return(false); + } + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + /* + ** If the thumb is currently being "dragged around", then update the slider + ** position according to the mouse position. In all other cases, ignore the + ** button being held down. + */ + if ((flags & LEFTPRESS) || ((flags & LEFTHELD) && StuckOn == this)) { + + /* + ** Compute the difference between where we clicked, and the edge of + ** the thumb (only if we clicked on the thumb.) + */ + if (flags & LEFTPRESS) { + int curpix = Value_To_Pixel(CurValue); + int clickpix = (IsHorizontal ? Get_Mouse_X() : Get_Mouse_Y()); + + if ( (clickpix > curpix) && (clickpix - curpix) < Thumb_Pixels()) { + ClickDiff = (clickpix - curpix); + } else { + ClickDiff = 0; + } + + int testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + + /* + ** Correct for round-down errors in Pixel_To_Value() and + ** Value_To_Pixel(); make ClickDiff exactly right so that + ** at this point, Get_Mouse_n() - ClickDiff converts to + ** CurValue. + */ + while (testval < CurValue && ClickDiff > 0) { + ClickDiff--; + testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + } + } + + /* + ** If no change occurred in the gauge, just call Control's Action routine, + ** but turn off the flags so it won't fill in 'key' with the button ID. + ** Thus, no button ID will be returned by Input. + */ + if (!Set_Value(Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff))) { + + flags &= ~(LEFTHELD|LEFTRELEASE|LEFTPRESS); + ControlClass::Action(0, key); + key = KN_NONE; + return(true); + } + + } else { + + /* + ** Ignore the left mouse button being held down if this gauge is not + ** currently in "sticky" mode. This allows processing of the LEFTPRESS + ** by any derived classes such that this gauge can be more closely + ** controlled. + */ + flags &= ~LEFTHELD; + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Thumb -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: none. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +void GaugeClass::Draw_Thumb(void) +{ + int x = Value_To_Pixel(CurValue); + + if ((x + 4) > Value_To_Pixel(MaxValue)) { + x = Value_To_Pixel(MaxValue) - 2; + } + + if (x < X) { + x = X; + } + + if (IsHorizontal) { + Draw_Box(x, Y, 4, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, x, Width, 4, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * TriColorGaugeClass::TriColorGaugeClass -- Constructor for 3 color (red\yellow\green) gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: See below. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +TriColorGaugeClass::TriColorGaugeClass(unsigned id, int x, int y, int w, int h) + : GaugeClass(id, x, y, w, h) +{ + RedLimit = 0; // maximum value for red + YellowLimit = 0; // maximum value for yellow +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Red_Limit -- Set the value for the red area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Red_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value > YellowLimit) { +// RedLimit = YellowLimit; +// YellowLimit = value; +// } else { + RedLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Yellow_Limit -- Set the value for the yellow area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Yellow_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value < RedLimit) { +// YellowLimit = RedLimit; +// RedLimit = value; +// } else { + YellowLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Draw_Me -- Draw the tri color gauge. * + * * + * INPUT: int forced -- draw or not? * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, (IsDisabled ? BOXSTYLE_RAISED : BOXSTYLE_DOWN), true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + int red = Value_To_Pixel(RedLimit); + int yellow = Value_To_Pixel(YellowLimit); + int middle = Value_To_Pixel(CurValue); + + if (CurValue <= RedLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, PINK); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, PINK); + } + } else if (CurValue > RedLimit && CurValue <= YellowLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, middle, Y+Height-2, YELLOW); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, middle, YELLOW); + } + } else if (CurValue > YellowLimit && CurValue <= MaxValue) { + + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, yellow, Y+Height-2, YELLOW); + LogicPage->Fill_Rect(yellow, Y+1, middle, Y+Height-2, GREEN); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, yellow, YELLOW); + LogicPage->Fill_Rect(X+1, yellow, X+Width-2, middle, GREEN); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + diff --git a/REDALERT/GAUGE.H b/REDALERT/GAUGE.H new file mode 100644 index 000000000..67763e437 --- /dev/null +++ b/REDALERT/GAUGE.H @@ -0,0 +1,106 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAUGE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.H * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAUGE_H +#define GAUGE_H + +class GaugeClass : public ControlClass +{ + public: + + GaugeClass(unsigned id, int x, int y, int w, int h); + + virtual int Draw_Me(int forced=false); + virtual int Set_Maximum(int value); + virtual int Set_Value(int value); + virtual int Get_Value(void) const {return (CurValue);}; + virtual void Use_Thumb(int value) { HasThumb = value ? true : false; }; + + virtual int Thumb_Pixels(void) { return (4);} + + /* + ** If this gauge has a color to the left of the current setting, then this + ** flag will be true. + */ + unsigned IsColorized:1; + + protected: + + /* + ** If a thumb is desired, set to true. + */ + unsigned HasThumb:1; + + /* + ** Is this a horizontal slider? + */ + unsigned IsHorizontal:1; + + int MaxValue; // maximum value (in application units) + int CurValue; // index of 1st displayed string in box + // (in application units) + + /* + ** This value records the difference between where the user clicked + ** and the edge of the thumb, so that the thumb follows the mouse + ** with the proper offset. + */ + int ClickDiff; + + protected: + virtual void Draw_Thumb(void); + virtual int Action(unsigned flags, KeyNumType &key); + virtual int Pixel_To_Value(int pixel); + virtual int Value_To_Pixel(int value); +}; + + + +class TriColorGaugeClass : public GaugeClass +{ + public: + TriColorGaugeClass(unsigned id, int x, int y, int w, int h); + virtual int Draw_Me(int forced); + virtual int Set_Red_Limit(int value); + virtual int Set_Yellow_Limit(int value); + + protected: + int RedLimit; // maximum value for red + int YellowLimit; // maximum value for yellow +}; + + + + +#endif diff --git a/REDALERT/GETCPU.CPP b/REDALERT/GETCPU.CPP new file mode 100644 index 000000000..8e5456b1e --- /dev/null +++ b/REDALERT/GETCPU.CPP @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GETCPU.CPP 1 3/03/97 10:24a Joe_bostic $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : GETCPU * + * * + * File Name : GETCPU.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 6/26/96 * + * * + * Last Update : June 26th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Example of interface to assembly language code to find CPU type * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Get_CPU_Type -- interface to ASM detection code * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//#define WIN32 +//#include +#include +#include + + +#define bool int + +/* +** Prototypes for linkage to assembly module +*/ + +extern "C" { + bool __cdecl Detect_MMX_Availability(void); + void __cdecl Init_MMX(void); + + extern char CPUType; + extern char VendorID; +} + + +/*********************************************************************************************** + * Get_CPU_Type -- Find out what kind of CPU we are running on * + * * + * * + * * + * INPUT: int - reference to cpu type * + * bool - reference to mmx availability flag * + * char* - ptr to buffer to receive chip vendor info * + * int - length of above buffer * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/26/96 10:15AM ST : Created * + *=============================================================================================*/ +void Get_CPU_Type(int & cpu_type, bool & mmx, char * vendor_id, int vendor_id_length) +{ + /* + ** Call the asm CPU detection code + */ + mmx = Detect_MMX_Availability(); + + /* + ** Return the promised results + */ + cpu_type = (int)CPUType; + char * vendor_ptr = &VendorID; + strncpy(vendor_id, vendor_ptr, vendor_id_length); +} + + + + + + + + + + diff --git a/REDALERT/GLOBALS.CPP b/REDALERT/GLOBALS.CPP new file mode 100644 index 000000000..9e8bb1440 --- /dev/null +++ b/REDALERT/GLOBALS.CPP @@ -0,0 +1,819 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/GLOBALS.CPP 2 3/10/97 6:22p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +bool IsVQ640 = false; +unsigned long GameVersion = 0; +bool Debug_MotionCapture = false; +bool Debug_Rotate = false; // Rotation algorithm control. +bool Debug_Quiet = false; +bool Debug_Cheat = false; +bool Debug_Remap = false; +bool Debug_Icon = false; +bool Debug_Flag = false; +bool Debug_Lose = false; +bool Debug_Win = false; +bool Debug_Map = false; // true = map editor mode +bool Debug_Passable = false; // true = show passable/impassable terrain +bool Debug_Unshroud = false; // true = hide the shroud +bool Debug_Threat = false; +bool Debug_Find_Path = false; +bool Debug_Check_Map = false; // true = validate the map each frame +bool Debug_Playtest = false; + +bool Debug_Heap_Dump = false; // true = print the Heap Dump +bool Debug_Smart_Print = false; // true = print everything that calls Smart_Printf +bool Debug_Trap_Check_Heap = false; // true = check the Heap +bool Debug_Modem_Dump = false; // true = print the Modem Stuff +bool Debug_Print_Events = false; // true = print event & packet processing +bool Debug_Force_Crash = false; + +TFixedIHeapClass Aircraft; +TFixedIHeapClass Anims; +TFixedIHeapClass Buildings; +TFixedIHeapClass Bullets; +TFixedIHeapClass Factories; +TFixedIHeapClass Houses; +TFixedIHeapClass Infantry; +TFixedIHeapClass Overlays; +TFixedIHeapClass Smudges; +TFixedIHeapClass Teams; +TFixedIHeapClass TeamTypes; +TFixedIHeapClass Templates; +TFixedIHeapClass Terrains; +TFixedIHeapClass Triggers; +TFixedIHeapClass Units; +TFixedIHeapClass Vessels; +TFixedIHeapClass TriggerTypes; + +TFixedIHeapClass HouseTypes; +TFixedIHeapClass BuildingTypes; +TFixedIHeapClass AircraftTypes; +TFixedIHeapClass InfantryTypes; +TFixedIHeapClass BulletTypes; +TFixedIHeapClass AnimTypes; +TFixedIHeapClass UnitTypes; +TFixedIHeapClass VesselTypes; +TFixedIHeapClass TemplateTypes; +TFixedIHeapClass TerrainTypes; +TFixedIHeapClass OverlayTypes; +TFixedIHeapClass SmudgeTypes; + + +/* +** These are the instantiate static heap pointers for the various +** CCPtr class objects that are allowed to exist. If the linker generates +** an error about a missing heap pointer, then this indicates that CCPtr objects +** for that type are not allowed. For every case of a TFixedIHeap manager of +** game objects, then a CCPtr can be instantiated for it. +*/ +#if (0) //Moved to runtime initialization. ST - 5/20/2019 +template<> FixedIHeapClass * CCPtr::Heap = &Aircraft; +template<> FixedIHeapClass * CCPtr::Heap = &Anims; +template<> FixedIHeapClass * CCPtr::Heap = &Buildings; +template<> FixedIHeapClass * CCPtr::Heap = &Bullets; +template<> FixedIHeapClass * CCPtr::Heap = &Factories; +template<> FixedIHeapClass * CCPtr::Heap = &Houses; +template<> FixedIHeapClass * CCPtr::Heap = &Infantry; +template<> FixedIHeapClass * CCPtr::Heap = &Overlays; +template<> FixedIHeapClass * CCPtr::Heap = &Smudges; +template<> FixedIHeapClass * CCPtr::Heap = &Teams; +template<> FixedIHeapClass * CCPtr::Heap = &TeamTypes; +template<> FixedIHeapClass * CCPtr::Heap = &Templates; +template<> FixedIHeapClass * CCPtr::Heap = &Terrains; +template<> FixedIHeapClass * CCPtr::Heap = &Triggers; +template<> FixedIHeapClass * CCPtr::Heap = &TriggerTypes; + +template<> FixedIHeapClass * CCPtr::Heap = &HouseTypes; +template<> FixedIHeapClass * CCPtr::Heap = &BuildingTypes; +template<> FixedIHeapClass * CCPtr::Heap = &AircraftTypes; +template<> FixedIHeapClass * CCPtr::Heap = &InfantryTypes; +template<> FixedIHeapClass * CCPtr::Heap = &BulletTypes; +template<> FixedIHeapClass * CCPtr::Heap = &AnimTypes; +template<> FixedIHeapClass * CCPtr::Heap = &UnitTypes; +template<> FixedIHeapClass * CCPtr::Heap = &VesselTypes; +template<> FixedIHeapClass * CCPtr::Heap = &TemplateTypes; +template<> FixedIHeapClass * CCPtr::Heap = &TerrainTypes; +template<> FixedIHeapClass * CCPtr::Heap = &OverlayTypes; +template<> FixedIHeapClass * CCPtr::Heap = &SmudgeTypes; +#endif + +/* These variables are used to keep track of the slowest speed of a team */ +MPHType TeamMaxSpeed[MAX_TEAMS]; +SpeedType TeamSpeed[MAX_TEAMS]; +bool FormMove; +SpeedType FormSpeed; +MPHType FormMaxSpeed; + + +char _staging_buffer[32000]; + +/* +** Global flag for the life of Tanya. If this flag is set, she is +** no longer available. +*/ +bool IsTanyaDead; +bool SaveTanya; + +#ifdef FIXIT_ANTS +bool AntsEnabled = false; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool NewUnitsEnabled = false; +bool SecretUnitsEnabled = false; +int MTankDistance = 15; +bool OverrideNewUnitsEnabled = false; // ST - 12/13/2019 12:21PM +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +int CarrierLaunchDelay = 60; +#endif +#endif + +int NewINIFormat = 0; + +bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool PendingTimeQuake; +TARGET TimeQuakeCenter; +fixed QuakeUnitDamage=0x300; +fixed QuakeBuildingDamage=0x300; +int QuakeInfantryDamage=25; +int QuakeDelay; +fixed ChronoTankDuration=0x300; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +fixed EngineerDamage=0x55; // Amount of damage an engineer does +fixed EngineerCaptureLevel=0x40; // Building damage level before engineer can capture +#endif +#endif + +#ifdef WIN32 +unsigned short Hard_Error_Occured = 0; +WWMouseClass * WWMouse = NULL; +GraphicBufferClass SysMemPage(DEFAULT_SCREEN_WIDTH, 200, (void*)NULL); +WinTimerClass * WindowsTimer=NULL; +int ScreenWidth=3072; +int ScreenHeight=3072; +GraphicBufferClass ModeXBuff; +bool InMovie = FALSE; //Are we currently playing a VQ movie? +HANDLE hInstance; +int AllDone; + + +/*************************************************************************** +** This is true if the game is the currently in focus windows app +** +*/ +bool GameInFocus = false; + +#endif + + +/*************************************************************************** +** Encryption keys. +*/ +PKey FastKey; +#ifdef CHEAT_KEYS +PKey SlowKey; +#endif + +#ifdef FIXIT_NAME_OVERRIDE +/*************************************************************************** +** This is where the name overrides for the units will reside. +*/ +char const * NameOverride[25]; +int NameIDOverride[25]; +#endif + +/*************************************************************************** +** These are the mission control structures. They hold the information about +** how the missions should behave in the system. +*/ +MissionControlClass MissionControl[MISSION_COUNT]; + + +/*************************************************************************** +** There are various tutorial messages that can appear in the game. These +** are called upon by number and pointed to by this array. +*/ +char const * TutorialText[225]; + + +/*************************************************************************** +** This holds the rules database. The rules database won't change during the +** program's run, but may need to be referenced intermitently. +*/ +CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +CCINIClass AftermathINI; +#endif + +/*************************************************************************** +** This points to the benchmark objects that are allocated only if the +** machine is running on a Pentium and this is a debug version. +*/ +//Benchmark * Benches; + + +/*************************************************************************** +** General rules that control the game. +*/ +RulesClass Rule; + + +/*************************************************************************** +** All keyboard input is routed through the object pointed to by this +** keyboard class pointer. +*/ +KeyboardClass * Keyboard; + + +/*************************************************************************** +** Remap control array. This is used to hold the remap +** tables for the various possible player colors, and the color schemes +** for dialogs. +*/ +RemapControlType ColorRemaps[PCOLOR_COUNT]; + +/* +** Special remap scheme for font that hs to print over metallic tabs +*/ +RemapControlType MetalScheme; + +/* +** This remap table is for special purposes. It consists of dark grey shades, +** and is used for dimming things out. +*/ +RemapControlType GreyScheme; + + +/*************************************************************************** +** This is the source of the random numbers used in the game. This controls +** the game logic and thus must be in sync with any networked machines. +*/ +RandomClass NonCriticalRandomNumber; +RandomStraw CryptRandom; + + +/*************************************************************************** +** This tracks all selected objects per house (for this map). +*/ +SelectedObjectsType CurrentObject; + + +/*************************************************************************** +** This is the game version. +*/ +//PG VersionClass VerNum; + + +/*************************************************************************** +** This is the VQ animation controller structure. It is filled in by reading +** the PLAYER.INI and overridden through program control. +*/ +//VQAConfig AnimControl; + +int PreserveVQAScreen; // Used for screen mode transition control. +bool BreakoutAllowed = true; // "true" if aborting of movies is allowed. +bool Brokeout; // Was the movie broken out of? +bool SlowPalette = false; // Slow palette flag set? + + +/*************************************************************************** +** These are the movie names to use for mission briefing, winning, and losing +** sequences. They are read from the INI file. +*/ +ScenarioClass Scen; + + +/*************************************************************************** +** This is the pending speech sample to play. This sample will be played +** at the first opportunity. +*/ +VoxType SpeakQueue = VOX_NONE; + + +/*************************************************************************** +** This records if the score (music) file is present. If not, then much of +** the streaming score system can be disabled. +*/ +bool ScoresPresent; + + +/*************************************************************************** +** This flag will control whether there is a response from game units. +** By carefully controlling this global, multiple responses are suppressed +** when a large group of infantry is given the movement order. +*/ +bool AllowVoice = true; + + +/*************************************************************************** +** This is the current frame number. This number is guaranteed to count +** upward at the rate of one per game logic process. The target rate is 15 +** per second. This value is saved and restored with the saved game. +*/ +long Frame = 0; + + +/*************************************************************************** +** These globals are constantly monitored to determine if the player +** has won or lost. They get set according to the trigger events associated +** with the scenario. +*/ +bool PlayerWins; +bool PlayerLoses; +bool PlayerRestarts; + +/* +** This flag is set if the player neither wins nor loses; it's mostly for +** multiplayer mode. +*/ +bool PlayerAborts; + + +/*************************************************************************** +** This is the pointer for the speech staging buffer. This buffer is used +** to hold the currently speaking voice data. Since only one speech sample +** is played at a time, this buffer is only as big as the largest speech +** sample that can be played. +*/ +void * SpeechBuffer[2]; +VoxType SpeechRecord[2]; + + +/*************************************************************************** +** The theater specific mixfiles are cached into the buffer pointed to by +** this global. +*/ +Buffer * TheaterBuffer; + + +/*************************************************************************** +** This is a running accumulation of the number of ticks that were unused. +** This accumulates into a useful value that contributes to a +** histogram of game performance. +*/ +long SpareTicks; +long PathCount; // Number of findpaths called. +long CellCount; // Number of cells redrawn. +long TargetScan; // Number of target scans. +long SidebarRedraws; // Number of sidebar redraws. + + +/*************************************************************************** +** This is the monochrome debug page array. The various monochrome data +** screens are located here. +*/ +MonoClass MonoArray[DMONO_COUNT]; +DMonoType MonoPage = DMONO_STRESS; // The current page. + + +/*************************************************************************** +** This holds the theater specific mixfiles. +*/ +MFCD * TheaterData = 0; +MFCD * MoviesMix = 0; +MFCD * GeneralMix = 0; +MFCD * ScoreMix = 0; +MFCD * MainMix = 0; +MFCD * ConquerMix = 0; + + +/*************************************************************************** +** This is the options control class. The options control such things as +** game speed, visual controls, and other user settings. +*/ +GameOptionsClass Options; + + +/*************************************************************************** +** Logic processing is controlled by this element. It handles both graphic +** and AI logic. +*/ +LogicClass Logic; + + +/*************************************************************************** +** This handles the background music. +*/ +ThemeClass Theme; + + +/*************************************************************************** +** This is the main control class for the map. +*/ +#ifdef SCENARIO_EDITOR +MapEditClass Map; +#else +MouseClass Map; +#endif + + +/************************************************************************** +** The running game score is handled by this class (and member functions). +*/ +ScoreClass Score; + + +/*************************************************************************** +** The running credit display is controlled by this class (and member +** functions. +*/ +CreditClass CreditDisplay; + + +/************************************************************************** +** This class records the special command override options that C&C +** supports. +*/ +SpecialClass Special; + + +bool PassedProximity; // used in display.cpp + + +/*************************************************************************** +** This is the scenario data for the currently loaded scenario. +** These variables should all be set together. +*/ +HousesType Whom; // Initial command line house choice. +int ScenarioInit; +bool SpecialFlag = false; + + +/*************************************************************************** +** This value tells the sidebar what items it's allowed to add. The +** lower the value, the simpler the sidebar will be. This value is the +** displayed value for tech level in the multiplay dialogs. It remaps to +** the in-game rules.ini tech levels. +*/ +int BuildLevel = 10; // Buildable level (1 = simplest) + + +/*************************************************************************** +** The various tutor and dialog messages are located in the data block +** referenced by this pointer. +*/ +char const * SystemStrings; +char const * DebugStrings; + + +/*************************************************************************** +** The game plays as long as this var is true. +*/ +bool GameActive; + + +/*************************************************************************** +** This is a scratch variable that is used to when a reference is needed to +** a long, but the value wasn't supplied to a function. This is used +** specifically for the default reference value. As such, it is not stable. +*/ +long LParam; + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** +** The currently-selected cell for the Scenario Editor +*/ +CELL CurrentCell = 0; +#endif + + +/*************************************************************************** +** Most of the text in the game will use the six point font. These are the +** pointers to the fonts. If it is NULL, then the font hasn't been loaded +** yet. +*/ +void const * Metal12FontPtr; //Font for use on in-game tabs in hires +void const * MapFontPtr; // Standard very small font. +void const * TypeFontPtr; // Teletype font for mission briefings. +void const * Font3Ptr; // Standard very small font. +void const * Font6Ptr; // Standard small font. +void const * EditorFont; // Font used for scenario editor. +void const * Font8Ptr; // 8 point proportional. +void const * FontLEDPtr; // LED fixed point font. +void const * VCRFontPtr; // VCR font pointer. +void const * ScoreFontPtr; // font for score & map selection screens +void const * GradFont6Ptr; // gradient 6 point font pointer. + + +/*************************************************************************** +** This is the house that the human player is currently playing. +*/ +HouseClass * PlayerPtr; + + +/*************************************************************************** +** Special palettes for MCGA mode goes here. These palette buffers are used +** for pictures that do not use the game palette or are used for fading to +** black. +*/ +PaletteClass CCPalette; +PaletteClass GamePalette; +//PaletteClass InGamePalette; +PaletteClass BlackPalette(RGBClass(0, 0, 0)); +PaletteClass WhitePalette(RGBClass(RGBClass::MAX_VALUE, RGBClass::MAX_VALUE, RGBClass::MAX_VALUE)); +PaletteClass OriginalPalette; +PaletteClass ScorePalette; + + +/*************************************************************************** +** These are the event queues. One is for holding events until they are ready to be +** sent to the remote computer for processing. The other list is for incoming events +** that need to be executed when the correct frame has been reached. +*/ +QueueClass OutList; +QueueClass DoList; + +#ifdef MIRROR_QUEUE +QueueClass MirrorList; +#endif + + +/*************************************************************************** +** These are arrays/lists of trigger pointers for each cell & the houses. +*/ +DynamicVectorClass HouseTriggers[HOUSE_COUNT]; +DynamicVectorClass MapTriggers; +int MapTriggerID; +DynamicVectorClass LogicTriggers; +int LogicTriggerID; + + +/*************************************************************************** +** This is the list of BuildingTypes that define the AI's base. +*/ +BaseClass Base; + + +/*************************************************************************** +** This is the list of carry over objects. These objects are part of the +** pseudo saved game that might be carried along with the current saved +** game. +*/ +CarryoverClass * Carryover; + + +/*************************************************************************** +** This value is computed every time a new scenario is loaded; it's a +** CRC of the INI and binary map files. +*/ +unsigned long ScenarioCRC; + + +/*************************************************************************** +** This class manages data specific to multiplayer games. +*/ +SessionClass Session; +#if(TIMING_FIX) +// +// These values store the min & max frame #'s for when MaxAhead >>increases<<. +// If MaxAhead increases, and the other systems free-run to the new MaxAhead +// value, they may miss an event generated after the MaxAhead event was sent, +// but before it executed, since it will have been scheduled with the older, +// shorter MaxAhead value. This will cause a Packet_Received_Too_Late error. +// The frames from the point where the new MaxAhead takes effect, up to that +// frame Plus the new MaxAhead, represent a "period of vulnerability"; any +// events received that are scheduled to execute during this period should +// be re-scheduled for after that period. +// +int NewMaxAheadFrame1; +int NewMaxAheadFrame2; +#endif + +#ifdef FIXIT_VERSION_3 +bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +unsigned long PlayingAgainstVersion; // Negotiated version number +bool Version107InMix; // Is there a v1.07 in the game +#endif + +/*************************************************************************** +** This is the null modem manager class. Declaring this class doesn't +** perform any allocations; +*/ +#if (0) +NullModemClass NullModem ( + 16, // number of send entries + 16, // number of receive entries + (MAX_SERIAL_PACKET_SIZE / sizeof(EventClass) ) * sizeof(EventClass) + sizeof( CommHeaderType ), + 0x1234); // Magic number must have each digit unique + // and different from the queue magic number +#endif + + +/*************************************************************************** +** This is the network IPX manager class. It handles multiple remote +** connections. Declaring this class doesn't perform any allocations; +** the class itself is 140 bytes. +*/ +//IPXManagerClass Ipx ( +// MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets +// ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), +// 10, // # entries in Global Queue +// 8, // # entries in Private Queues +// VIRGIN_SOCKET, // Socket ID # +// IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + +IPXManagerClass Ipx ( + MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets + ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), + 160, // # entries in Global Queue + 32, // # entries in Private Queues + VIRGIN_SOCKET, // Socket ID # + IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + + +#if(TEN) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - TEN connection ID's are equal to the HousesType for that player. +** - The TEN internal player ID is used to determine the player's color. +** - Ten's broadcast destination address -1 +*/ +TenConnManClass *Ten = NULL; + +#endif + + +#if(MPATH) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - MPATH connection ID's are equal to the HousesType for that player. +** - The player's color is read from the OPTIONS.INI file +** - MPath's broadcast destination address is 0 +*/ +MPlayerManClass *MPath = NULL; + +#endif + + +/*************************************************************************** +** This is the random-number seed; it's synchronized between systems for +** multiplayer games. +*/ +int Seed = 0; + + +/*************************************************************************** +** If this value is non-zero, use it as the random # seed instead; this should +** help reproduce some bugs. +*/ +int CustomSeed = 0; + +int WindowList[][9] = { +/* xbyte, ypixel, bytewid, pixelht, cursor color, bkgd color, cursor x, cursor y */ + + /* do not change the first 2 entries!! they are necc. to the system */ + + {0,0,40*8*RESFACTOR,200*RESFACTOR,WHITE,BLACK,0,0}, /* screen window */ + {1*8,75,38*8,100,WHITE,BLACK,0,0}, /* DOS Error window */ + + // Tactical map. + {0, 0, 40*8*RESFACTOR, 200*RESFACTOR, WHITE,LTGREY,0,0}, + + // Initial menu window. + {12*8, 199-42, 16*8, 42, LTGREY, DKGREY, 0, 0}, + + // Sidebar clipping window. + {0,0,0,0,0,0,0,0}, + + // Scenario editor window. + {5*8, 30, 30*8, 140, 0, 0, 0, 0}, + + // Partial object draw sub-window. + {0,0,0,0,WHITE,BLACK,0,0}, + + // Custom window. + {0, 0, 0, 0, 0, 0, 0, 0}, + + // Virtual window for external rendering. ST - 1/15/2019 3:02PM + {0, 0, 0, 0, 0, 0, 0, 0} + +}; + + +/* X,Y,Item Width,Items High,Selected,Norm Color,Sel Color,zero */ +int MenuList[][8]={ + {1, 3, 12, 3, 0, WHITE, PINK, 0}, +}; + + +#ifdef WIN32 +GraphicBufferClass VisiblePage; +GraphicBufferClass HiddenPage; +GraphicViewPortClass SeenBuff(&VisiblePage, 0, 0, 3072, 3072); +GraphicViewPortClass HidPage(&HiddenPage, 0, 0, 3072, 3072); +#else +GraphicBufferClass HidPage(DEFAULT_SCREEN_WIDTH, 201, (void*)NULL); +GraphicBufferClass SeenBuff(320, 200, (void *)0xA0000L); +VideoBufferClass SeenPage; +GraphicBufferClass & VisiblePage = SeenBuff; +#endif + + +#ifdef WIN32 +#else +#endif + +int SoundOn; +CDTimerClass FrameTimer; +CDTimerClass CountDownTimer; + +NewConfigType NewConfig; +TheaterType LastTheater = THEATER_NONE; //Lets us know when theater type changes. + + +/*************************************************************************** +** This flag is for popping up dialogs that call the main loop. +*/ +SpecialDialogType SpecialDialog = SDLG_NONE; + + +int RequiredCD = -1; +int CurrentCD = -1; +int MouseInstalled; + +// +// Variables for helping track how much time goes bye in routines +// +int LogLevel = 0; +unsigned long LogLevelTime[ MAX_LOG_LEVEL ] = { 0 }; +unsigned long LogLastTime = 0; +bool LogDump_Print = false; // true = print the Log time Stuff + + +/*************************************************************************** +** Tick Count global timer object. +*/ +TTimerClass TickCount = 0; + + +/*************************************************************************** +** Win32 specific globals +*/ +#ifdef WIN32 + +bool InDebugger = false; +int ReadyToQuit = 0; + +#else +bool IsTheaterShape = false; // must be defined only if not Win32 +#endif //WIN32 + +//PG GetCDClass CDList; +int UnitBuildPenalty = 100; + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +MCIMovie* MciMovie = NULL; +#endif + +#include "mpgset.h" +MPGSettings* MpgSettings = NULL; +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +bool bAutoSonarPulse = false; +#endif + +bool MMXAvailable = false; + + +// ST - 5/14/2019 +bool RunningAsDLL = false; +bool RunningFromEditor = false; diff --git a/REDALERT/GOPTIONS.CPP b/REDALERT/GOPTIONS.CPP new file mode 100644 index 000000000..018593a31 --- /dev/null +++ b/REDALERT/GOPTIONS.CPP @@ -0,0 +1,622 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/GOPTIONS.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "goptions.h" +#include "loaddlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#include "gamedlg.h" +#include "textbtn.h" +#include "descdlg.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +bool RedrawOptionsMenu; + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + * 06/23/1995 JLB : Handles restating the mission objective. * + * 07/27/1995 JLB : Adjusts menu for multiplay mode. * + *=============================================================================================*/ +void GameOptionsClass::Process(void) +{ + static struct { + int ID; // Button ID to use. + int Text; // Text number to use for this button. + bool Multiplay; // Allowed in multiplayer version? + } _constants[] = { + {BUTTON_LOAD, TXT_LOAD_MISSION, false}, +#ifdef FIXIT_MULTI_SAVE + {BUTTON_SAVE, TXT_SAVE_MISSION, true}, +#else + {BUTTON_SAVE, TXT_SAVE_MISSION, false}, +#endif + {BUTTON_DELETE, TXT_DELETE_MISSION, true}, + {BUTTON_GAME, TXT_GAME_CONTROLS, true}, + {BUTTON_QUIT, TXT_QUIT_MISSION, true}, +#ifdef FIXIT_VERSION_3 // Stalemate games. + {BUTTON_DRAW, TXT_OK, true}, +#endif + {BUTTON_RESUME, TXT_RESUME_MISSION, true}, + {BUTTON_RESTATE, TXT_RESTATE_MISSION, false}, + }; + + /* + ** Variables. + */ + TextButtonClass * buttons = 0; + int selection; + bool pressed; +#ifdef FIXIT_VERSION_3 // Stalemate games. + int curbutton = 7; +#else + int curbutton = 6; +#endif + int y; + TextButtonClass * buttonsel[ARRAY_SIZE(_constants)]; + static int num_buttons = sizeof(_constants)/sizeof(_constants[0]); + + + int num_players = 0; + int i; + + // + // Compute the number of real players in the game; only allow saves + // if there are more than 1. + // + for (i = 0; i < Session.Players.Count(); i++) { + if (!(HouseClass::As_Pointer(Session.Players[i]->Player.ID)->IsDefeated)) { + num_players++; + } + } + + + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list for all of the buttons for this dialog. + */ + int maxwidth = 0; + + for (int index = 0; index < num_buttons ; index++ ) { + int text = _constants[index].Text; + buttonsel[index] = NULL; + + if (Session.Type != GAME_NORMAL && !_constants[index].Multiplay) { + continue; + } + + if ( (Session.Type == GAME_SKIRMISH || + Session.Type == GAME_INTERNET) && text == TXT_SAVE_MISSION) { + continue; + } + +#ifdef FIXIT_VERSION_3 + if (Session.Type != GAME_NORMAL && ( num_players < 2 ) && + text == TXT_SAVE_MISSION) { + continue; + } +#else +#ifdef FIXIT_MULTI_SAVE + if (Session.Type != GAME_NORMAL && (num_players < 2 || PlayingAgainstVersion == VERSION_RED_ALERT_104) && + text == TXT_SAVE_MISSION) { + continue; + } +#endif //FIXIT_MULTI_SAVE +#endif + + if (Session.Type == GAME_SKIRMISH && text == TXT_DELETE_MISSION) { + continue; + } + + if (Session.Type != GAME_NORMAL && text == TXT_DELETE_MISSION) { + text = TXT_RESIGN; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if (index < 6) { +#else + if (index < 5) { +#endif + y = (SeenBuff.Get_Height() - OptionHeight)/2 + ButtonY + ((OButtonHeight+2) * index); + } else { + y = OptionY + ButtonResumeY; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + TextButtonClass* g; + if( _constants[index].ID == BUTTON_DRAW ) + { + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 ) + { + if( Scen.bLocalProposesDraw ) + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_RETRACT_DRAW, TPF_BUTTON, 0, y ); + else + continue; // Game will end now anyway. + } + else + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_PROPOSE_DRAW, TPF_BUTTON, 0, y ); + else + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_ACCEPT_DRAW, TPF_BUTTON, 0, y ); + } + } + else + continue; + } + else + g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#else + TextButtonClass * g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#endif + + if (g->Width > maxwidth) { + maxwidth = g->Width; + } + if (buttons == NULL) { + buttons = g; + } else { + g->Add_Tail(*buttons); + } + + buttonsel[index] = g; + } + + /* + ** BG: In skirmish mode, there is no 'restate' button, so we have to + ** backtrack through the list to find the last valid button. + */ + while(!buttonsel[curbutton-1]) curbutton--; + + buttonsel[curbutton-1]->Turn_On(); + + /* + ** Force all button lengths to match the maximum length of the widest button. + */ + GadgetClass * g = buttons; + while (g != NULL) { + g->Width = max(maxwidth, 90 * RESFACTOR); + g->X = OptionX+(OptionWidth-g->Width)/2; + g = g->Get_Next(); + } +//#ifdef FRENCH +// buttonsel[BUTTON_RESUME-1]->Width = 110 * RESFACTOR; +// buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR) - 5; +//#else + buttonsel[BUTTON_RESUME-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR); +//#endif + + if (Session.Type == GAME_NORMAL) { + buttonsel[BUTTON_RESTATE-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESTATE-1]->X = OptionX+OptionWidth-(buttonsel[BUTTON_RESTATE-1]->Width+(17 * RESFACTOR)); + } + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + (new GadgetClass(OptionX, OptionY, OptionWidth, OptionHeight, GadgetClass::LEFTPRESS))->Add_Tail(*buttons); + + /* + ** This cause a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to game button. + */ + (new ControlClass(BUTTON_RESUME, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS))->Add_Tail(*buttons); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display || RedrawOptionsMenu) { + + /* + ** Redraw the map. + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + /* + ** Reset up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OptionX, OptionY, OptionWidth, OptionHeight); + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(OptionX, OptionY, OptionWidth, OptionHeight); + + /* + ** Draw the arrows border if requested. + */ + Draw_Caption(TXT_OPTIONS, OptionX, OptionY, OptionWidth); + + /* + ** Display the version number at the bottom of the dialog box. + */ +#ifndef WIN32 + Fancy_Text_Print("%s\rV%s", + (OptionX+OptionWidth)-(17 * RESFACTOR), + OptionY+OptionHeight-((Session.Type == GAME_NORMAL) ? (32 * RESFACTOR) : (24 * RESFACTOR)), + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Scen.ScenarioName, + Version_Name()); + +#else +#if (0)//PG + Fancy_Text_Print("%s\rV%s", + (OptionX+OptionWidth)-(25 * RESFACTOR), + OptionY+OptionHeight-((Session.Type == GAME_NORMAL) ? (32 * RESFACTOR) : (24 * RESFACTOR)), + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Scen.ScenarioName, + Version_Name()); +#endif +#endif + + buttons->Draw_All(); + TabClass::Hilite_Tab(0); + Show_Mouse(); + display = false; + RedrawOptionsMenu = false; + } + + /* + ** Get user input. + */ + KeyNumType input = buttons->Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_RESTATE | KN_BUTTON): + selection = BUTTON_RESTATE; + pressed = true; + break; + + case (BUTTON_LOAD | KN_BUTTON): + selection = BUTTON_LOAD; + pressed = true; + break; + + case (BUTTON_SAVE | KN_BUTTON): + selection = BUTTON_SAVE; + pressed = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + selection = BUTTON_DELETE; + pressed = true; + break; + + case (BUTTON_QUIT | KN_BUTTON): + selection = BUTTON_QUIT; + pressed = true; + break; + + case (BUTTON_GAME | KN_BUTTON): + selection = BUTTON_GAME; + pressed = true; + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case (BUTTON_DRAW | KN_BUTTON): + selection = BUTTON_DRAW; + pressed = true; + break; +#endif + + case (KN_ESC): + case (BUTTON_RESUME | KN_BUTTON): + selection = BUTTON_RESUME; + pressed = true; + break; + + case (KN_UP): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton--; + if (curbutton < 1) curbutton = num_buttons; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton++; + if ( curbutton > num_buttons ) curbutton = 1; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + buttonsel[curbutton-1]->IsPressed = true; + buttonsel[curbutton-1]->Draw_Me(true); + selection = curbutton; + pressed = true; + Keyboard->Clear(); + break; + + default: + break; + } + + if (pressed) { + + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton = selection; + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + + switch (selection) { + case BUTTON_RESTATE: + display = true; + if (!Restate_Mission(Scen.ScenarioName, TXT_VIDEO, TXT_RESUME_MISSION/*KOTXT_OPTIONS*/)) { + BreakoutAllowed = true; + Play_Movie(Scen.BriefMovie); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + + Map.Flag_To_Redraw(true); + Theme.Queue_Song(THEME_PICK_ANOTHER); + process = false; + } else { + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + process = false; + } + break; + + case (BUTTON_LOAD): + display = true; + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + process = false; + } + break; + + case (BUTTON_SAVE): + display = true; + if (Session.Type == GAME_NORMAL) { + LoadOptionsClass(LoadOptionsClass::SAVE).Process(); + + } else { + OutList.Add(EventClass(EventClass::SAVEGAME)); + process = false; + } + break; + + case (BUTTON_DELETE): + display = true; + if (Session.Type != GAME_NORMAL) { + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + process = false; + } else { + LoadOptionsClass(LoadOptionsClass::WWDELETE).Process(); + } + break; + + case (BUTTON_QUIT): + if (Session.Type == GAME_NORMAL) { + switch (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_ABORT, TXT_CANCEL, TXT_RESTART)) { + case 1: + display = true; + break; + + case 0: + process = false; + Queue_Exit(); + break; + + case 2: + PlayerRestarts = true; + process = false; + break; + } + } else { + if (Surrender_Dialog(TXT_CONFIRM_EXIT)) { + process = false; + Queue_Exit(); + } else { + display = true; + } + //if (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_YES, TXT_NO) == 0) { + //process = false; + //Queue_Exit(); + //} else { + //display = true; + //} + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case BUTTON_DRAW: + if( Scen.bLocalProposesDraw ) + { + // Retract draw offer. + OutList.Add(EventClass(EventClass::RETRACT_DRAW)); + process = false; + } + else + { + if( !Scen.bOtherProposesDraw ) + { + // Propose a draw? + if( Surrender_Dialog( TXT_WOL_PROPOSE_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + else + { + // Accept a draw? + if( Surrender_Dialog( TXT_WOL_ACCEPT_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + } + break; +#endif + + case (BUTTON_GAME): + display = true; + GameControlsClass().Process(); + break; + + case (BUTTON_RESUME): + Save_Settings(); + process = false; + display = true; + break; + } + + pressed = false; + buttonsel[curbutton-1]->IsPressed = false; + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + } + } + + /* + ** Clean up and re-enter the game. + */ + buttons->Delete_List(); + + /* + ** Redraw the map. + */ + Keyboard->Clear(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + +void GameOptionsClass::Adjust_Variables_For_Resolution(void) +{ + OptionWidth = (216+8) * RESFACTOR; +#ifdef FIXIT_VERSION_3 // Stalemate games. + OptionHeight = 111 * RESFACTOR; +#else + OptionHeight = 100 * RESFACTOR; +#endif + OptionX = ((SeenBuff.Get_Width() - OptionWidth) / 2); + OptionY = ((SeenBuff.Get_Height() - OptionHeight) / 2); + ButtonWidth = 130 * RESFACTOR; + OButtonHeight = 9 * RESFACTOR; + CaptionYPos = 5 * RESFACTOR; + ButtonY = 21 * RESFACTOR; + Border1Len = 72 * RESFACTOR; + Border2Len = 16 * RESFACTOR; + ButtonResumeY = (OptionHeight - (19 * RESFACTOR)); +} diff --git a/REDALERT/GOPTIONS.H b/REDALERT/GOPTIONS.H new file mode 100644 index 000000000..06e72acfa --- /dev/null +++ b/REDALERT/GOPTIONS.H @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GOPTIONS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GOPTIONS_H +#define GOPTIONS_H + +#include "options.h" +#include "gadget.h" + + +class GameOptionsClass : public OptionsClass { + enum GameOptionsButtonEnum { + BUTTON_LOAD=1, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_GAME, + BUTTON_QUIT, +#ifdef FIXIT_VERSION_3 // Stalemate games. + BUTTON_DRAW, +#endif + BUTTON_RESUME, + BUTTON_RESTATE, + + BUTTON_COUNT + }; + + enum GameOptionsEnum { + OPTION_WIDTH=(216+8), + OPTION_HEIGHT=100, + OPTION_X=((320 - (216+8)) / 2), + OPTION_Y=((200 - 100) / 2), +#ifdef FRENCH + BUTTON_WIDTH=142, +#else + BUTTON_WIDTH=130, +#endif + NUMBER_OF_BUTTONS=6, // ajw Not used. + CAPTION_Y_POS=5, + BUTTON_Y=21, + BORDER1_LEN=72, + BORDER2_LEN=16, + BUTTON_RESUME_Y=(100-15) + }; + + public: + GameOptionsClass(void): OptionsClass () { }; + void Adjust_Variables_For_Resolution(void); + void Process(void); + + private: + int OptionWidth; + int OptionHeight; + int OptionX; + int OptionY; + int ButtonWidth; + int OButtonHeight; + int CaptionYPos; + int ButtonY; + int Border1Len; + int Border2Len; + int ButtonResumeY; + +}; + +#endif diff --git a/REDALERT/GSCREEN.CPP b/REDALERT/GSCREEN.CPP new file mode 100644 index 000000000..51ff07128 --- /dev/null +++ b/REDALERT/GSCREEN.CPP @@ -0,0 +1,484 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GSCREEN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * GScreenClass::Input -- Fetches input and processes gadgets. * + * GScreenClass::One_Time -- Handles one time class setups. * + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +GadgetClass * GScreenClass::Buttons = 0; + +GraphicBufferClass * GScreenClass::ShadowPage = 0; + + +/*********************************************************************************************** + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * * + * This constructor merely sets the display system, so that it will redraw the first time * + * the render function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +GScreenClass::GScreenClass(void) +{ + IsToUpdate = true; + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::One_Time -- Handles one time class setups. * + * * + * This routine (and all those that overload it) must perform truly one-time initialization. * + * Such init's would normally be done in the constructor, but other aspects of the game may * + * not have been initialized at the time the constructors are called (such as the file system, * + * the display, or other WWLIB subsystems), so many initializations should be deferred to the * + * One_Time init's. * + * * + * Any variables set in this routine should be declared as static, so they won't be modified * + * by the load/save process. Non-static variables will be over-written by a loaded game. * + * * + * This function allocates the shadow buffer that is used for quick screen updates. If * + * there were any data files to load, they would be loaded at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::One_Time(void) +{ + /* + ** Allocate the screen shadow page. This page is used to reduce access to the + ** actual screen memory. It contains a duplicate of what the SEENPAGE is. + */ + Buttons = 0; + ShadowPage = new GraphicBufferClass(320, 200); + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } +} + + +/*********************************************************************************************** + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * * + * This routine shouldn't be overloaded. It's the main map initialization routine, and will * + * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * + * routine results in calling every initialization routine in the entire map hierarchy. * + * * + * INPUT: * + * theater theater to initialize to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init(TheaterType theater) +{ + Init_Clear(); + Init_IO(); + Init_Theater(theater); +} + + +/*********************************************************************************************** + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * * + * This routine (and those that overload it) clears any buffers and variables to a known * + * state. It assumes that all buffers are allocated & valid. The map should be displayable * + * after calling this function, and should draw basically an empty display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Clear(void) +{ + /* + ** Clear the ShadowPage & HidPage to force a complete shadow blit. + */ + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } + + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * * + * This routine (and those that overload it) performs any theater-specific initializations * + * needed. This will include setting the palette, setting up remap tables, etc. This routine * + * only needs to be called when the theater has changed. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Theater(TheaterType ) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_IO(void) +{ + /* + ** Reset the button list. This means that any other elements of the map that need + ** buttons must attach them after this routine is called! + */ + Buttons = 0; +} + + +/*********************************************************************************************** + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * * + * This function is used to flag the display system whether any rendering is needed. The * + * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * + * has changed and the individual Draw_It functions must be called. When a sub system * + * determines that it needs to render something local to itself, it would call this routine * + * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * + * this routine will be called with a true parameter. * + * * + * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * + * * + * OUTPUT: none * + * * + * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * + * Render() function is called, the appropriate drawing steps will be performed. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Flag_To_Redraw(bool complete) +{ + IsToUpdate = true; + if (complete) { + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * GScreenClass::Input -- Fetches input and processes gadgets. * + * * + * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * + * system. * + * * + * INPUT: key -- Reference to the key code (for future examination). * + * * + * x,y -- Reference to mouse coordinates (for future examination). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Input(KeyNumType & key, int & x, int & y) +{ + key = Keyboard->Check(); + + x = Keyboard->Mouse_X(); + y = Keyboard->Mouse_Y(); + + if (Buttons != NULL) { + + /* + ** If any buttons need redrawing, they will do so in the Input routine, and + ** they should draw themselves to the HidPage. So, flag ourselves for a Blit + ** to show the newly drawn buttons. + */ + if (Buttons->Is_List_To_Redraw()) { + Flag_To_Redraw(false); + } + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); +#endif + + key = Buttons->Input(); + + Set_Logic_Page(oldpage); + + } else { + + if (key != 0) { + key = Keyboard->Get(); + } + } + + AI(key, x, y); +} + + +/*********************************************************************************************** + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * * + * This will add a gadget to the game input system. The gadget will be processed in * + * subsequent calls to the GScreenClass::Input() function. * + * * + * INPUT: gadget -- Reference to the gadget that will be added to the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Add_A_Button(GadgetClass & gadget) +{ + /* + ** If this gadget is already in the list, remove it before adding it in: + ** - If 1st gadget in list, use Remove_A_Button to remove it, to reset the + ** value of 'Buttons' appropriately + ** - Otherwise, just call the Remove function for that gadget to remove it + ** from any list it may be in + */ + if (Buttons == &gadget) { + Remove_A_Button(gadget); + } else { + gadget.Remove(); + } + + /* + ** Now add the gadget to our list: + ** - If there are not buttons, start the list with this one + ** - Otherwise, add it to the tail of the existing list + */ + if (Buttons) { + gadget.Add_Tail(*Buttons); + } else { + Buttons = &gadget; + } +} + + +/*********************************************************************************************** + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * * + * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * + * will be invalid! * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Remove_A_Button(GadgetClass & gadget) +{ + Buttons = gadget.Remove(); +} + + +/*********************************************************************************************** + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * * + * This routine should be called in the main game loop (once every game frame). It will * + * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * + * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * + * copied to the visible page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This actually updates the graphic display. As a result it can take quite a * + * while to perform. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Render(void) +{ + //This is unnessasary surely? ST - 10/16/96 2:30PM + //if (Buttons && Buttons->Is_List_To_Redraw()) { + // IsToRedraw = true; + //} + + + if (IsToUpdate || IsToRedraw) { + BStart(BENCH_GSCREEN_RENDER); + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + + if (IsToRedraw) { + Hide_Mouse(); + SeenPage.To_Buffer(0, 0, 320, 200, ShadowPage); + Show_Mouse(); + } +#endif + + Draw_It(IsToRedraw); + + if (Buttons) Buttons->Draw_All(false); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the Editor's buttons + */ + if (Debug_Map) { + if (Buttons) { + Buttons->Draw_All(); + } + } +#endif + /* + ** Draw the multiplayer message system to the Hidpage at this point. + ** This way, they'll Blit along with the rest of the map. + */ + if (Session.Messages.Num_Messages() > 0) { + Session.Messages.Set_Width( + Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); + } + Session.Messages.Draw(); + + //Blit_Display(); // 5/19/20 SKY - Skip copying to scene page, we can get the data directly from hidden page + IsToUpdate = false; + IsToRedraw = false; + + BEnd(BENCH_GSCREEN_RENDER); + Set_Logic_Page(oldpage); + } +} + + +/*********************************************************************************************** + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * * + * This routine is used to copy the correct display from the HIDPAGE * + * to the SEENPAGE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + *=============================================================================================*/ +extern "C" { + void ModeX_Blit (GraphicBufferClass * source); +} + +void GScreenClass::Blit_Display(void) +{ + BStart(BENCH_BLIT_DISPLAY); + #ifdef WIN32 + if (SeenBuff.Get_Width()!=320) { + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); + WWMouse->Erase_Mouse(&HidPage, FALSE); + } else { + //PG ModeX_Blit(&HiddenPage); + } + #else + Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ShadowPage->Get_Buffer()); + #endif + BEnd(BENCH_BLIT_DISPLAY); +} + + diff --git a/REDALERT/GSCREEN.H b/REDALERT/GSCREEN.H new file mode 100644 index 000000000..7a15ec144 --- /dev/null +++ b/REDALERT/GSCREEN.H @@ -0,0 +1,136 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GSCREEN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GSCREEN_H +#define GSCREEN_H + +#include "function.h" +#include "cell.h" + +class GScreenClass +{ + public: + + GScreenClass(void); + GScreenClass(NoInitClass const &) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time initializations + virtual void Init(TheaterType = THEATER_NONE); // Inits everything + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** Player I/O is routed through here. It is called every game tick. + */ + virtual void Input(KeyNumType & key, int & x, int & y); + virtual void AI(KeyNumType &, int, int) {}; + virtual void Add_A_Button(GadgetClass & gadget); + virtual void Remove_A_Button(GadgetClass & gadget); + + /* + ** Called when map needs complete updating. + */ + virtual void Flag_To_Redraw(bool complete=false); + + /* + ** Render maintenance routine (call every game tick). Probably no need + ** to override this in derived classes. + */ + virtual void Render(void); + + /* + ** Is called when actual drawing is required. This is the function to + ** override in derived classes. + */ + virtual void Draw_It(bool =false) {}; + + /* + ** This moves the hidpage up to the seenpage. + */ + virtual void Blit_Display(void); + + /* + ** Changes the mouse shape as indicated. + */ + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall) = 0; + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall) = 0; + virtual void Revert_Mouse_Shape(void) = 0; + virtual void Mouse_Small(bool wsmall) = 0; + + /* + ** Misc routines. + */ + virtual void * Shadow_Address(void) {return(ShadowPage);}; + + /* + ** This points to the buttons that are used for input. All of the derived classes will + ** attached their specific buttons to this list. + */ + static GadgetClass * Buttons; + + private: + + /* + ** If the entire map is required to redraw, then this flag is true. This flag + ** is set by the Flag_To_Redraw function. Typically, this occurs when the screen + ** has been trashed or is first created. + */ + unsigned IsToRedraw:1; + + /* + ** If only a sub-system of the map must be redrawn, then this flag will be set. + ** An example of something that would set this flag would be an animating icon + ** in the sidebar. In such a case, complete redrawing of the entire display is not + ** necessary, but the Draw_It function should still be called so that the appropriate + ** class can perform it's rendering. + */ + unsigned IsToUpdate:1; + + /* + ** Pointer to an exact copy of the visible graphic page. This copy is used to speed + ** display rendering by using an only-update-changed-pixels algorithm. + */ + static GraphicBufferClass * ShadowPage; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/HDATA.CPP b/REDALERT/HDATA.CPP new file mode 100644 index 000000000..2230f56e4 --- /dev/null +++ b/REDALERT/HDATA.CPP @@ -0,0 +1,523 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 22, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * HouseTypeClass::One_Time -- One-time initialization * + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static HouseTypeClass const HouseEngland( + HOUSE_ENGLAND, + "England", // NAME: House name. + TXT_ENGLAND, // FULLNAME: Translated house name. + "ENG", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'E' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGermany( + HOUSE_GERMANY, + "Germany", // NAME: House name. + TXT_GERMANY, // FULLNAME: Translated house name. + "GER", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseFrance( + HOUSE_FRANCE, + "France", // NAME: House name. + TXT_FRANCE, // FULLNAME: Translated house name. + "FRA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'F' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUkraine( + HOUSE_UKRAINE, + "Ukraine", // NAME: House name. + TXT_UKRAINE, // FULLNAME: Translated house name. + "UKA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'K' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUSSR( + HOUSE_USSR, + "USSR", // NAME: House name. + TXT_USSR, // FULLNAME: Translated house name. + "RED", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'U' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGreece( + HOUSE_GREECE, + "Greece", // NAME: House name. + TXT_GREECE, // FULLNAME: Translated house name. + "GRE", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseTurkey( + HOUSE_TURKEY, + "Turkey", // NAME: House name. + TXT_TURKEY, // FULLNAME: Translated house name. + "TRK", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'T' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseSpain( + HOUSE_SPAIN, + "Spain", // NAME: House name. + TXT_SPAIN, // FULLNAME: Translated house name. + "SPN", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'S' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGood( + HOUSE_GOOD, + "GoodGuy", // NAME: House name. + TXT_GOODGUY, // FULLNAME: Translated house name. + "GDI", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseBad( + HOUSE_BAD, + "BadGuy", // NAME: House name. + TXT_BADGUY, // FULLNAME: Translated house name. + "NOD", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'B' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseCivilian( + HOUSE_NEUTRAL, + "Neutral", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "CIV", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'C' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseJP( + HOUSE_JP, + "Special", // NAME: House name. + TXT_JP, // FULLNAME: Translated house name. + "JP", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'J' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti1( + HOUSE_MULTI1, + "Multi1", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP1", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti2( + HOUSE_MULTI2, + "Multi2", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP2", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti3( + HOUSE_MULTI3, + "Multi3", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP3", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti4( + HOUSE_MULTI4, + "Multi4", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP4", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti5( + HOUSE_MULTI5, + "Multi5", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP5", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti6( + HOUSE_MULTI6, + "Multi6", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP6", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti7( + HOUSE_MULTI7, + "Multi7", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP7", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti8( + HOUSE_MULTI8, + "Multi8", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP8", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +#ifdef OBSOLETE +HouseTypeClass const * const HouseTypeClass::Pointers[HOUSE_COUNT] = { + &HouseSpain, + &HouseGreece, + &HouseUSSR, + &HouseEngland, + &HouseUkraine, + &HouseGermany, + &HouseFrance, + &HouseTurkey, + &HouseGood, + &HouseBad, + &HouseCivilian, + &HouseJP, + &HouseMulti1, + &HouseMulti2, + &HouseMulti3, + &HouseMulti4, + &HouseMulti5, + &HouseMulti6, + &HouseMulti7, + &HouseMulti8, +}; +#endif + + +/*********************************************************************************************** + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * * + * This is the constructor for house type objects. This object holds the constant data * + * for the house type. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass::HouseTypeClass( + HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix) : + AbstractTypeClass(RTTI_HOUSETYPE, house, fullname, ini), +// RTTI(RTTI_HOUSETYPE), +// ID(house), + House(house), +// IniName(ini), +// FullName(fullname), + Lemon(lemon), + RemapColor(remapcolor), + Prefix(prefix), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1) +{ + strncpy(Suffix, ext, 3); + Suffix[3] = '\0'; +} + + +/*********************************************************************************************** + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * * + * This will allocate a house type object from the special heap that is used to maintain * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the newly allocated house type object. * + * * + * WARNINGS: If there is insufficient room, this routine may return NULL. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void * HouseTypeClass::operator new(size_t) +{ + return(HouseTypes.Alloc()); +} + + +/*********************************************************************************************** + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * * + * This will return the house type object specified back into the special heap that * + * is used to maintain house type objects. * + * * + * INPUT: ptr -- Pointer to the house type object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::operator delete(void * ptr) +{ + HouseTypes.Free((HouseTypeClass *)ptr); +} + + +/*********************************************************************************************** + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * * + * This will preallocate all the house types. They must be allocated in a particular order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called only once at the beginning of the game. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::Init_Heap(void) +{ + /* + ** These house type class objects must be allocated in the exact order that they + ** are specified in the HousesType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new HouseTypeClass(HouseSpain); + new HouseTypeClass(HouseGreece); + new HouseTypeClass(HouseUSSR); + new HouseTypeClass(HouseEngland); + new HouseTypeClass(HouseUkraine); + new HouseTypeClass(HouseGermany); + new HouseTypeClass(HouseFrance); + new HouseTypeClass(HouseTurkey); + new HouseTypeClass(HouseGood); + new HouseTypeClass(HouseBad); + new HouseTypeClass(HouseCivilian); + new HouseTypeClass(HouseJP); + new HouseTypeClass(HouseMulti1); + new HouseTypeClass(HouseMulti2); + new HouseTypeClass(HouseMulti3); + new HouseTypeClass(HouseMulti4); + new HouseTypeClass(HouseMulti5); + new HouseTypeClass(HouseMulti6); + new HouseTypeClass(HouseMulti7); + new HouseTypeClass(HouseMulti8); +} + + +/*********************************************************************************************** + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * * + * This routine will convert the ASCII house name specified into a * + * real house number. Typically, this is used when processing a * + * scenario INI file. * + * * + * INPUT: name -- ASCII name of house to process. * + * * + * OUTPUT: Returns with actual house number represented by the ASCII * + * name specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +HousesType HouseTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (stricmp(As_Reference(house).IniName, name) == 0) { +// if (stricmp(Pointers[house]->IniName, name) == 0) { + return(house); + } + } + } + return(HOUSE_NONE); +} + + +/*********************************************************************************************** + * HouseTypeClass::One_Time -- One-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/21/1994 JLB : Converted to member function. * + * 06/19/1996 JLB : Converted to regular heap class management. * + *=============================================================================================*/ +void HouseTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * * + * Use this routine to fetch a reference to the house number specified. * + * * + * INPUT: house -- The house number (HousesType) to look up. * + * * + * OUTPUT: Returns with a reference to the HouseTypeClass object that matches the house * + * number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass & HouseTypeClass::As_Reference(HousesType house) +{ + return(*HouseTypes.Ptr(house)); +} + + +/*********************************************************************************************** + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * * + * Use this routine to fetch the remap table assigned to this house. The remap table is * + * what gives the house's units/buildings their distinctive color. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +unsigned char const * HouseTypeClass::Remap_Table(void) const +{ + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * * + * This routine will fetch the rules controllable values for the house type from the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to fetch the house control values from. * + * * + * OUTPUT: bool; Was the house section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + FirepowerBias = ini.Get_Fixed(Name(), "Firepower", FirepowerBias); + GroundspeedBias = ini.Get_Fixed(Name(), "Groundspeed", GroundspeedBias); + AirspeedBias = ini.Get_Fixed(Name(), "Airspeed", AirspeedBias); + ArmorBias = ini.Get_Fixed(Name(), "Armor", ArmorBias); + ROFBias = ini.Get_Fixed(Name(), "ROF", ROFBias); + CostBias = ini.Get_Fixed(Name(), "Cost", CostBias); + BuildSpeedBias = ini.Get_Fixed(Name(), "BuildTime", BuildSpeedBias); + return(true); + } + return(false); +} diff --git a/REDALERT/HEAP.CPP b/REDALERT/HEAP.CPP new file mode 100644 index 000000000..e6fc5d936 --- /dev/null +++ b/REDALERT/HEAP.CPP @@ -0,0 +1,663 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HEAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : May 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * FixedIHeapClass::Free -- Frees an object in the heap. * + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * TFixedIHeapClass::Load -- Loads all active objects * + * TFixedIHeapClass::Save -- Saves all active objects * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "heap.h" +//#include +#include +#include +#include +#include + + +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; + + +/*********************************************************************************************** + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * * + * This is the normal constructor used for the heap manager class. This initializes * + * the class but doesn't yet assign actual heap memory to this manager. That is handled * + * by the Set_Heap() function. * + * * + * INPUT: size -- The size of the individual sub-blocks in this heap. This value is * + * typically the size of some class or structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: The heap must first be assigned a block of memory to manage before it can * + * be used. * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::FixedHeapClass(int size) : + IsAllocated(false), + Size(size), + TotalCount(0), + ActiveCount(0), + Buffer(0) +{ +} + + +/*********************************************************************************************** + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * * + * This is the default constructor for the heap manager class. It handles freeing the * + * memory assigned to this heap. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::~FixedHeapClass(void) +{ + FixedHeapClass::Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * * + * This routine is used to assign a memory heap to this object. A memory heap so assigned * + * will start with all sub-blocks unallocated. After this routine is called, normal * + * allocation and freeing may occur. This routine will allocate necessary memory if the * + * buffer parameter is NULL. * + * * + * INPUT: count -- The number of objects that this heap should manage. * + * * + * buffer -- Pointer to pre-allocated buffer that this manager will use. If this * + * parameter is NULL, then memory will be automatically allocated. * + * * + * OUTPUT: bool; Was the heap successfully initialized? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Set_Heap(int count, void * buffer) +{ + /* + ** Clear out the old heap data. + */ + Clear(); + + /* + ** If there is no size to the objects in the heap, then this block memory + ** handler can NEVER function. Return with a failure condition. + */ + if (!Size) return(false); + + /* + ** If there is no count specified, then this indicates that the heap should + ** be disabled. + */ + if (!count) return(true); + + /* + ** Initialize the free boolean vector and the buffer for the actual + ** allocation objects. + */ + if (FreeFlag.Resize(count)) { + if (!buffer) { + buffer = new char[count * Size]; + if (!buffer) { + FreeFlag.Clear(); + return(false); + } + IsAllocated = true; + } + Buffer = buffer; + TotalCount = count; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * * + * Finds the first available sub-block in the heap and returns a pointer to it. The sub- * + * block is marked as allocated by this routine. If there are no more sub-blocks * + * available, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated sub-block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedHeapClass::Allocate(void) +{ + if (ActiveCount < TotalCount) { + int index = FreeFlag.First_False(); + + if (index != -1) { + ActiveCount++; + FreeFlag[index] = true; + return((*this)[index]); + } + } + return(0); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * * + * Use this routine to free a previously allocated sub-block in the heap. * + * * + * INPUT: pointer -- A pointer to the sub-block to free. This is the same pointer that * + * was returned from the Allocate() function. * + * * + * OUTPUT: bool; Was the deallocation successful? Failure could indicate a pointer that * + * doesn't refer to this heap or a null pointer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free(void * pointer) +{ + if (pointer && ActiveCount) { + int index = ID(pointer); + + if (index < TotalCount) { + if (FreeFlag[index]) { + ActiveCount--; + FreeFlag[index] = false; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * * + * Use this routine to convert a pointer (returned by Allocate) into the sub-block * + * index number. This index number can be used as a form of identifier for the block. * + * * + * INPUT: pointer -- A pointer to the sub-block to convert into an ID number. * + * * + * OUTPUT: Returns with the index (ID) number for the sub-block specified. This number will * + * range between 0 and the sub-block max -1. If -1 is returned, then the pointer * + * was invalid. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::ID(void const * pointer) const +{ + if (pointer && Size) { + return((int)(((char *)pointer - (char *)Buffer) / Size)); + } + return(-1); +} + + +/*********************************************************************************************** + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * * + * This routine is used to bring the heap manager back into a non-functioning state. All * + * memory allocated by this manager is freed. Any previous pointers to allocated blocks * + * from this heap are now invalid. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedHeapClass::Clear(void) +{ + /* + ** Free the old buffer (if present). + */ + if (Buffer && IsAllocated) { + delete[] Buffer; + } + Buffer = 0; + IsAllocated = false; + ActiveCount = 0; + TotalCount = 0; + FreeFlag.Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * * + * This routine will free all previously allocated objects out of the heap. Use this * + * routine to ensure that the heap is empty. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of all objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free_All(void) +{ + ActiveCount = 0; + FreeFlag.Reset(); + return(true); +} + + +///////////////////////////////////////////////////////////////////// + + +/*********************************************************************************************** + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * * + * Use this routine to free all previously allocated objects in the heap. This routine will * + * also clear out the allocated object vector as well. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free_All(void) +{ + ActivePointers.Delete_All(); + return(FixedHeapClass::Free_All()); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * * + * This routine will clear the entire heap. All memory that was allocation, will be freed * + * by this routine. After calling this routine, the heap must either be resized or * + * a new heap memory block specifically attached, before it can be used again. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedIHeapClass::Clear(void) +{ + FixedHeapClass::Clear(); + ActivePointers.Clear(); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * * + * This routine will set the heap to use the buffer specified. Use this routine when a * + * pre-allocated buffer is to be used for the heap. A heap that is assigned in this * + * manner cannot be resized. * + * * + * INPUT: count -- The number of objects that the buffer pointer can be used to track. * + * * + * buffer -- Pointer to the buffer to use when keeping track of the objects. * + * * + * OUTPUT: Was the heap assigned successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Set_Heap(int count, void * buffer) +{ + Clear(); + if (FixedHeapClass::Set_Heap(count, buffer)) { + ActivePointers.Resize(count); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * * + * This routine will allocate an object located in the heap. If no free object space * + * could be found, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated object memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedIHeapClass::Allocate(void) +{ + void * ptr = FixedHeapClass::Allocate(); + if (ptr) { + ActivePointers.Add(ptr); + memset (ptr, 0, Size); + } + return(ptr); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Free -- Frees an object in the heap. * + * * + * This routine is used to free an object in the heap. Freeing is accomplished by marking * + * the object's memory as free to be reallocated. The object is also removed from the * + * allocated object pointer vector. * + * * + * INPUT: pointer -- Pointer to the object that is to be removed from the heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free(void * pointer) +{ + if (FixedHeapClass::Free(pointer)) { + ActivePointers.Delete(pointer); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * * + * Ths logical ID number of a memory block is the index number of the block as if the * + * heap consisted only of valid allocated blocks. This knowledge comes in handy when * + * the real index number must be anticipated before a memory block packing process. * + * * + * INPUT: pointer -- Pointer to an allocated block in the heap. * + * * + * OUTPUT: Returns with the logical index number of this block. The number returned must not * + * be used as a regular index into the heap until such time as the heap has been * + * compacted (by some means or another) without modifying the block order. * + * * + * WARNINGS: Runs in linear time. * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Logical_ID(void const * pointer) const +{ + if (pointer != NULL) { + for (int index = 0; index < Count(); index++) { + if (Active_Ptr(index) == pointer) { + return(index); + } + } + } + return(-1); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + * 03/12/1996 JLB : Uses in-place new operator for virtual table control. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(Pipe & file) const +{ + /* + ** Save the number of instances of this class + */ + file.Put(&ActiveCount, sizeof(ActiveCount)); + + /* + ** Save each instance of this class + */ + for (int i = 0; i < ActiveCount; i++) { + + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + int idx = ID(Ptr(i)); + file.Put(&idx, sizeof(idx)); + + /* + ** Save the object itself + */ + file.Put(Ptr(i), sizeof(T)); + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(Straw & file) +{ + int i; // loop counter + int idx; // object index + T * ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Get(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Get(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + file.Get(ptr, sizeof(T)); + new(ptr) T(NoInitClass()); +// if (!ptr->Load(file)) { +// return(false); +// } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} diff --git a/REDALERT/HEAP.H b/REDALERT/HEAP.H new file mode 100644 index 000000000..ac07df5f1 --- /dev/null +++ b/REDALERT/HEAP.H @@ -0,0 +1,195 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HEAP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : February 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HEAP_H +#define HEAP_H + +#include "vector.h" + +/************************************************************************** +** This is a block memory management handler. It is used when memory is to +** be treated as a series of blocks of fixed size. This is similar to an +** array of integral types, but unlike such an array, the memory blocks +** are anonymously. This facilitates the use of this class when overloading +** the new and delete operators for a normal class object. +*/ +class FixedHeapClass +{ + public: + FixedHeapClass(int size); + virtual ~FixedHeapClass(void); + + int Count(void) const {return ActiveCount;}; + int Length(void) const {return TotalCount;}; + int Avail(void) const {return TotalCount-ActiveCount;}; + + virtual int ID(void const * pointer) const; + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + void * operator[](int index) {return ((char *)Buffer) + (index * Size);}; + void const * operator[](int index) const {return ((char *)Buffer) + (index * Size);}; + + protected: + /* + ** If the memory block buffer was allocated by this class, then this flag + ** will be true. The block must be deallocated by this class if true. + */ + unsigned IsAllocated:1; + + /* + ** This is the size of each sub-block within the buffer. + */ + int Size; + + /* + ** This records the absolute number of sub-blocks in the buffer. + */ + int TotalCount; + + /* + ** This is the total blocks allocated out of the heap. This number + ** will never exceed Count. + */ + int ActiveCount; + + /* + ** Pointer to the heap's memory buffer. + */ + void * Buffer; + + /* + ** This is a boolean vector array of allocation flag bits. + */ + BooleanVectorClass FreeFlag; + + private: + // The assignment operator is not supported. + FixedHeapClass & operator = (FixedHeapClass const &); + + // The copy constructor is not supported. + FixedHeapClass(FixedHeapClass const &); +}; + + +/************************************************************************** +** This template serves only as an interface to the heap manager class. By +** using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedHeapClass : public FixedHeapClass +{ + public: + TFixedHeapClass(void) : FixedHeapClass(sizeof(T)) {}; + virtual ~TFixedHeapClass(void) {}; + + + virtual int ID(T const * pointer) const {return FixedHeapClass::ID(pointer);}; + virtual T * Alloc(void) {return (T*)FixedHeapClass::Allocate();}; + virtual int Free(T * pointer) {return(FixedHeapClass::Free(pointer));}; + + T & operator[](int index) {return *(T *)(((char *)Buffer) + (index * Size));}; + T const & operator[](int index) const {return *(T*)(((char *)Buffer) + (index * Size));}; +}; + + +/************************************************************************** +** This is a derivative of the fixed heap class. This class adds the +** ability to quickly iterate through the active (allocated) objects. Since the +** active array is a sequence of pointers, the overhead of this class +** is 4 bytes per potential allocated object (be warned). +*/ +class FixedIHeapClass : public FixedHeapClass +{ + public: + FixedIHeapClass(int size) : FixedHeapClass(size) {}; + virtual ~FixedIHeapClass(void) {}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + virtual int Logical_ID(void const * pointer) const; + virtual int Logical_ID(int id) const {return(Logical_ID((*this)[id]));} + + virtual void * Active_Ptr(int index) {return ActivePointers[index];}; + virtual void const * Active_Ptr(int index) const {return ActivePointers[index];}; + + /* + ** This is an array of pointers to allocated objects. Using this array + ** to control iteration through the objects ensures a minimum of processing. + ** It also allows access to this array so that custom sorting can be + ** performed. + */ + DynamicVectorClass ActivePointers; +}; + + +/************************************************************************** +** This template serves only as an interface to the iteratable heap manager +** class. By using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +class FileClass; +template +class TFixedIHeapClass : public FixedIHeapClass +{ + public: + TFixedIHeapClass(void) : FixedIHeapClass(sizeof(T)) {}; + virtual ~TFixedIHeapClass(void) {}; + + virtual int ID(T const * pointer) const {return FixedIHeapClass::ID(pointer);}; + virtual int Logical_ID(T const * pointer) const {return(FixedIHeapClass::Logical_ID(pointer));} + virtual int Logical_ID(int id) const {return(FixedIHeapClass::Logical_ID(id));} + virtual T * Alloc(void) {return (T*)FixedIHeapClass::Allocate();}; + virtual int Free(T * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Free(void * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Save(Pipe & file) const; + virtual int Load(Straw & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual T * Ptr(int index) const {return (T*)FixedIHeapClass::ActivePointers[index];}; + virtual T * Raw_Ptr(int index) {return (index >= 0 && index < Length()) ? (T*)((*this)[index]) : NULL;}; +}; + + +#endif + + diff --git a/REDALERT/HELP.CPP b/REDALERT/HELP.CPP new file mode 100644 index 000000000..08e4f203d --- /dev/null +++ b/REDALERT/HELP.CPP @@ -0,0 +1,414 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HELP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * HelpClass::HelpClass -- Default constructor for the help processor. * + * HelpClass::Help_AI -- Handles the help text logic. * + * HelpClass::Help_Text -- Assigns text as the current help text. * + * HelpClass::Init_Clear -- Sets help system to a known state. * + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the holding buffer for the text overlap list. This buffer must be in the near +** data segment. It will be filled in by the Set_Text() function. +*/ +//short const HelpClass::OverlapList[60] = { // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM +short HelpClass::OverlapList[60] = { + REFRESH_EOL +}; + +char const * HelpClass::HelpText; + + +/*********************************************************************************************** + * HelpClass::HelpClass -- Default constructor for the help processor. * + * * + * The help processor is initialized by this routine. It merely sets up the help engine * + * to the default state. The default state will not display any help text. Call the * + * Help_Text() function to enable help processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +HelpClass::HelpClass(void) : + HelpX(0), + HelpY(0), + HelpWidth(0), + IsRight(false), + Cost(0), + X(0), + Y(0), + DrawX(0), + DrawY(0), + Width(0), + Text(TXT_NONE), + Color(LTGREY), + CountDownTimer(0) +{ +} + + +/*********************************************************************************************** + * HelpClass::Init_Clear -- Sets help system to a known state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Init_Clear(void) +{ + TabClass::Init_Clear(); + + Set_Text(TXT_NONE); +} + + +/*********************************************************************************************** + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * * + * Use this routine to fetch an offset list for the cells under the text displayed. If * + * there is no text displayed, then the list will consist of just the terminator code. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the offset list for the help text overlap. The offset * + * list is based on the tactical map upper left corner cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +short const * HelpClass::Overlap_List(void) const +{ + if (Text == TXT_NONE || CountDownTimer) { + ((short &)(OverlapList[0])) = REFRESH_EOL; + } + return(OverlapList); +} + + +/*********************************************************************************************** + * HelpClass::Help_AI -- Handles the help text logic. * + * * + * This routine handles tracking the mouse position to see if the mouse remains stationary * + * for the required amount of time. If the time requirement has been met, then it flags * + * the help system to display the help text the next time the Draw_Help() function is * + * called. * + * * + * INPUT: key -- Keyboard input code. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called once and only once per game frame (15 times per * + * second). * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinates as passed in. * + *=============================================================================================*/ +void HelpClass::AI(KeyNumType &key, int x, int y) +{ + if (!CountDownTimer && !IsRight && (x != X || y != Y)) { + Help_Text(TXT_NONE); + } + + /* + ** Process the countdown timer only if it hasn't already expired and there is + ** a real help text message to display. + */ + if (CountDownTimer && !HelpText && Text != TXT_NONE) { + + /* + ** If the mouse has moved, then reset the timer since a moving mouse is not + ** supposed to bring up the help text. + */ + if (!IsRight && (X != x || Y != y)) { + X = x; + Y = y; + CountDownTimer = HELP_DELAY; + Help_Text(TXT_NONE); + } else { + + /* + ** If the delay has expired, then the text must be drawn. Build the help text + ** overlay list at this time. Better to do it now, when we KNOW it is needed, then + ** to do it earlier when it might not be needed. + */ + Set_Text(Text); + } + } + + TabClass::AI(key, x, y); +} + + +/*********************************************************************************************** + * HelpClass::Help_Text -- Assigns text as the current help text. * + * * + * Use this routine to change the help text that will pop up if the cursor isn't moved * + * for the help delay duration. Call this routine as often as desired. * + * * + * INPUT: text -- The text number for the help text to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Help_Text(int text, int x, int y, int color, bool quick) +{ + if (text != Text) { + + /* + ** If there is an existing text message, then flag the map to redraw the underlying + ** icons so that the text message is erased. + */ + if (Text != TXT_NONE) { + Refresh_Cells(Coord_Cell(TacticalCoord), &OverlapList[0]); + } + + /* + ** Record the position of the mouse. This recorded position will be used to determine + ** if the mouse has moved. A moving mouse prevents the help text from popping up. + */ + X = x; + if (x == -1) X = Get_Mouse_X(); + Y = y; + if (y == -1) Y = Get_Mouse_Y(); + IsRight = (y != -1) || (x != -1); + + if (quick) { + CountDownTimer = 1; + } else { + CountDownTimer = HELP_DELAY; + } + + /* + ** All help text prints in the same color for E3 + */ + //Color = color; + color = color; + Color = HELP_TEXT_COLOR; + Text = text; + Cost = 0; + } +} + + +/*********************************************************************************************** + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * * + * This function will print the help text if it thinks it should. The timer and text * + * message can control whether this occurs. If there is no help text or the countdown timer * + * has not expired, then no text will be printed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Draw_It(bool forced) +{ + TabClass::Draw_It(forced); + + forced = false; // TCTCTCTC + if (Text != TXT_NONE && (forced || !CountDownTimer)) { + + if (LogicPage->Lock()) { + Plain_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY-1, DrawX+Width+1, DrawY+FontHeight, Color); + + if (Cost) { + char buffer[15]; + sprintf(buffer, "$%d", Cost); + int width = String_Pixel_Width(buffer); + + Plain_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY+FontHeight, DrawX+width+1, DrawY+FontHeight+FontHeight-1, Color); + LogicPage->Draw_Line(DrawX, DrawY+FontHeight, DrawX+min(width+1, Width)-1, DrawY+FontHeight, BLACK); + } + LogicPage->Unlock(); + } + } +} + + +/*********************************************************************************************** + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * * + * This routine is used to build the overlap list -- used for icon refreshing. It also * + * determines if the text can fit on the screen and makes adjustments so that it will. * + * * + * INPUT: text -- The text number to set the help system to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/11/1994 JLB : Won't draw past tactical map edges. * + *=============================================================================================*/ +void HelpClass::Set_Text(int text) +{ + if (text != TXT_NONE) { + Text = text; + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_MAP|TPF_NOSHADOW); + Width = String_Pixel_Width(Text_String(Text)); + if (IsRight) { + DrawX = X - Width; + DrawY = Y; + } else { + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth) - 3*RESFACTOR; + int bottom = TacPixelY + Lepton_To_Pixel(TacLeptonHeight) - 1*RESFACTOR; + + DrawX = X+X_OFFSET; + DrawY = Y+Y_OFFSET; + if (DrawX + Width > right) { + DrawX -= (DrawX+Width) - right; + } + if (DrawY + 10*RESFACTOR > bottom) { + DrawY -= (DrawY+10*RESFACTOR) - bottom; + } + if (DrawX < TacPixelX+1) DrawX = TacPixelX+1; + if (DrawY < TacPixelY+1) DrawY = TacPixelY+1; + } + memcpy((void*)OverlapList, Text_Overlap_List(Text_String(Text), DrawX-1, DrawY), sizeof(OverlapList)); + *(short *)&OverlapList[ARRAY_SIZE(OverlapList)-1] = REFRESH_EOL; + } +} + + +/*********************************************************************************************** + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * * + * This routine intercepts the map scrolling request and then makes sure that if, in fact, * + * the map is going to scroll, then reset and erase the help text so that it doesn't * + * mess up the display. * + * * + * INPUT: facing -- The direction to scroll (unused by this routine). * + * * + * really -- If the scroll is actually going to occur, rather than just be examined * + * for legality, then this parameter will be true. If this parameter is * + * true, then the help text is reset. * + * * + * OUTPUT: Returns if it can, or did, scroll in the requested direction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +bool HelpClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (really) { + Help_Text(TXT_NONE); + } + return(TabClass::Scroll_Map(facing, distance, really)); +} + + +/*********************************************************************************************** + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * * + * Use this routine after the Help_Text() function to activate the second line. The second * + * line displays a cost. Typically, this is used by the sidebar to display the cost of the * + * specified item. * + * * + * INPUT: cost -- The cost to associate with this help text. If this value is zero, then * + * no second line is displayed, so don't pass in zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/09/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Cost(int cost) +{ + Cost = cost; +} + + +/*********************************************************************************************** + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * * + * This routine will set the position of the tactical map. At this class level, it merely * + * makes sure that the help text disappears when this happens. The lower level classes * + * actually change the map's position. * + * * + * INPUT: coord -- The new coordinate to make the upper left corner of the visible display. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Tactical_Position(COORDINATE coord) +{ + if (TacticalCoord != coord) { + Help_Text(TXT_NONE); + } + TabClass::Set_Tactical_Position(coord); +} diff --git a/REDALERT/HELP.H b/REDALERT/HELP.H new file mode 100644 index 000000000..83587990c --- /dev/null +++ b/REDALERT/HELP.H @@ -0,0 +1,140 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HELP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HELP_H +#define HELP_H + +#include "tab.h" + +#define HELP_TEXT_COLOR 80 //158 //Goldy/orange + +class HelpClass: public TabClass +{ + public: + HelpClass(void); + HelpClass(NoInitClass const & x) : TabClass(x) {}; + + /* + ** Initialization + */ + virtual void Init_Clear(void); // Clears all to known state + + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Scroll_Map(DirType facing, int &distance, bool really); + virtual void Set_Tactical_Position(COORDINATE coord); + + void Help_Text(int text, int x=-1, int y=-1, int color=LTGREY, bool quick=false); + void Set_Cost(int cost); + short const * Overlap_List(void) const; + + private: + + static char const *HelpText; + int HelpX; + int HelpY; + int HelpWidth; + + + void Set_Text(int text); + + /* + ** If the help text is right justified (as with the help text that pops up over the + ** sidebar icons), then this flag is set to true. + */ + unsigned IsRight:1; + + /* + ** If the optional second line of text that displays cost is desired, then this + ** value will be non-zero. Typically, this is true when the help text is associated + ** with one of the sidebar construction icons. + */ + int Cost; + + /* + ** This is the recorded position of the cursor at the time the help text + ** pops up. The help text is rendered as an offset from this pixel position. + */ + int X; + int Y; + + /* + ** This is the draw X and Y coordinate. This position is relative to the X and + ** Y coordinates but adjusted for screen edges as necessary. + */ + int DrawX; + int DrawY; + + /* + ** The width of the help text (in pixels) is stored here. This is a convenience + ** since calculating the width takes a bit of time. + */ + int Width; + + /* + ** The text number of the help text to display is held here. If no text is to be + ** displayed, then this value will be TXT_NONE. + */ + int Text; + + /* + ** This is the background color to use for the help text. It can change according + ** to the message displayed. + */ + int Color; + + /* + ** This countdown timer controls when the help text will pop up. If the mouse + ** remains stationary while this countdown timer expires, then the help text + ** will pop up. + */ + CDTimerClass CountDownTimer; + + /* + ** This is a calculated cell offset list (from the Map.TacticalCell) that indicates + ** which cells are under the help text and thus which cells need to be redrawn if + ** the help text is to be erased. + */ + //static short const OverlapList[60]; // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM + static short OverlapList[60]; + + enum HelpClassEnum { + HELP_DELAY=TIMER_SECOND*1, // The countdown timer delay before help text pops up. + Y_OFFSET=0, // The Y pixel offset from cursor for help text print. + X_OFFSET=12 // The X pixel offset from cursor for help text print. + }; +}; + + +#endif diff --git a/REDALERT/HOUSE.CPP b/REDALERT/HOUSE.CPP new file mode 100644 index 000000000..a228ab64b --- /dev/null +++ b/REDALERT/HOUSE.CPP @@ -0,0 +1,8173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : November 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseClass::AI -- Process house logic. * + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * HouseClass::AI_Attack -- Handles offensive attack logic. * + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * HouseClass::AI_Building -- Determines what building to build. * + * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. * + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * HouseClass::AI_Money_Check -- Handles money production logic. * + * HouseClass::AI_Power_Check -- Handle the power situation. * + * HouseClass::AI_Unit -- Determines what unit to build next. * + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * HouseClass::Attacked -- Lets player know if base is under attack. * + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * HouseClass::Blowup_All -- blows up everything * + * HouseClass::Can_Build -- General purpose build legality checker. * + * HouseClass::Clobber_All -- removes all objects for this house * + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * HouseClass::Expert_AI -- Handles expert AI processing. * + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * HouseClass::Find_Building -- Finds a building of specified type. * + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * HouseClass::HouseClass -- Constructor for a house object. * + * HouseClass::Init -- init's in preparation for new scenario * + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * HouseClass::Make_Ally -- Make the specified house an ally. * + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * HouseClass::Production_Begun -- Records that production has begun. * + * HouseClass::Read_INI -- Reads house specific data from INI. * + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * HouseClass::Spend_Money -- Removes money from the house. * + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * HouseClass::Tally_Score -- Fills in the score system for this round * + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * HouseClass::delete -- Deallocator function for a house object. * + * HouseClass::new -- Allocator for a house class. * + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * HouseClass::~HouseClass -- Default destructor for a house object. * + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +//#include "WolDebug.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + +TFixedIHeapClass HouseClass::BuildChoice; + +int TFixedIHeapClass::Save(Pipe &) const +{ + return(true); +} + +int TFixedIHeapClass::Load(Straw &) +{ + return(0); +} + +void TFixedIHeapClass::Code_Pointers(void) +{ +} + +void TFixedIHeapClass::Decode_Pointers(void) +{ +} + +extern bool RedrawOptionsMenu; + +/*********************************************************************************************** + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * * + * This operator will automatically convert from a houses class object into the HousesType * + * enumerated value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the object's HousesType value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass::operator HousesType(void) const +{ + assert(Houses.ID(this) == ID); + + return(Class->House); +} + + +/*********************************************************************************************** + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * * + * This will calculate the current tiberium (gold) load as a ratio of the maximum storage * + * capacity. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Tiberium_Fraction(void) const +{ + if (Tiberium == 0) { + return(0); + } + return(fixed(Tiberium, Capacity)); +} + + +/*********************************************************************************************** + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * * + * Use this routine to convert a house number into the house pointer that it represents. * + * A simple index into the Houses template array is not sufficient, since the array order * + * is arbitrary. An actual scan through the house object is required in order to find the * + * house object desired. * + * * + * INPUT: house -- The house type number to look up. * + * * + * OUTPUT: Returns with a pointer to the house object that the house number represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass * HouseClass::As_Pointer(HousesType house) +{ + if (house != HOUSE_NONE) { + for (int index = 0; index < Houses.Count(); index++) { + if (Houses.Ptr(index)->Class->House == house) { + return(Houses.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * * + * This basically calls the constructor for each of the houses in the game. All other * + * data specific to the house is initialized when the scenario is loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::One_Time(void) +{ + BuildChoice.Set_Heap(STRUCT_COUNT); +} + + +/*********************************************************************************************** + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * * + * The handicap rating will affect combat, movement, and production for the house. It can * + * either make it more or less difficult for the house (controlled by the handicap value). * + * * + * INPUT: handicap -- The handicap value to assign to this house. The default value for * + * a house is DIFF_NORMAL. * + * * + * OUTPUT: Returns with the old handicap value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + * 10/22/1996 JLB : Uses act like value for multiplay only. * + *=============================================================================================*/ +DiffType HouseClass::Assign_Handicap(DiffType handicap) +{ + DiffType old = Difficulty; + Difficulty = handicap; + + if (Session.Type != GAME_NORMAL) { + HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike); + FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias; + ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias; + CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } else { + FirepowerBias = Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = Rule.Diff[handicap].ArmorBias; + ROFBias = Rule.Diff[handicap].ROFBias; + CostBias = Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } + + return(old); +} + + + +#ifdef CHEAT_KEYS + +void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const +{ + mono->Set_Cursor(x, y); + mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense); +} + + +/*********************************************************************************************** + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * * + * This utility function will output the current status of the house class to the mono * + * screen. Through this information bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_HOUSE)); + + mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name()); + mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name()); + mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel); + mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty); + mono->Set_Cursor(52, 1);mono->Printf("%2d", State); + mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage); + mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ); + mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer); + + mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan); + mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name()); + mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft); + mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits); + mono->Set_Cursor(37, 3);mono->Printf("%5d", Power); + mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied); + mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal); + mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime); + mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime); + + mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan); + mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name()); + mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings); + mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium); + mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain); + mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]); + mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime); + mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime); + + mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan); + mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name()); + mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits); + mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits); + mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost); + mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies); + mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack); + + mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan); + mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name()); + mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry); + mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity); + mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost); + mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W); + mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer); + + mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan); + mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name()); + mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels); + mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center)); + mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime); + + + for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) { + mono->Set_Cursor(1+index, 15); + if (Scen.GlobalFlags[index] != 0) { + mono->Print("1"); + } else { + mono->Print("0"); + } + if (index >= 24) break; + } + if (Enemy != HOUSE_NONE) { + char const * name = ""; + name = HouseClass::As_Pointer(Enemy)->Name(); + mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name()); + } + + Print_Zone_Stats(27, 11, ZONE_NORTH, mono); + Print_Zone_Stats(27, 13, ZONE_CORE, mono); + Print_Zone_Stats(27, 15, ZONE_SOUTH, mono); + Print_Zone_Stats(1, 13, ZONE_WEST, mono); + Print_Zone_Stats(53, 13, ZONE_EAST, mono); + + mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * HouseClass::new -- Allocator for a house class. * + * * + * This is the allocator for a house class. Since there can be only * + * one of each type of house, this is allocator has restricted * + * functionality. Any attempt to allocate a house structure for a * + * house that already exists, just returns a pointer to the previously * + * allocated house. * + * * + * INPUT: house -- The house to allocate a class object for. * + * * + * OUTPUT: Returns with a pointer to the allocated class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void * HouseClass::operator new(size_t) +{ + void * ptr = Houses.Allocate(); + if (ptr) { + ((HouseClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * HouseClass::delete -- Deallocator function for a house object. * + * * + * This function marks the house object as "deallocated". Such a * + * house object is available for reallocation later. * + * * + * INPUT: ptr -- Pointer to the house object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::operator delete(void * ptr) +{ + if (ptr) { + ((HouseClass *)ptr)->IsActive = false; + } + Houses.Free((HouseClass *)ptr); +} + + +/*********************************************************************************************** + * HouseClass::HouseClass -- Constructor for a house object. * + * * + * This function is the constructor and it marks the house object * + * as being allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +#define VOX_NOT_READY VOX_NONE +HouseClass::HouseClass(HousesType house) : + RTTI(RTTI_HOUSE), + ID(Houses.ID(this)), + Class(HouseTypes.Ptr(house)), + Difficulty(Scen.CDifficulty), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1), + RepairDelay(0), + BuildDelay(0), + ActLike(Class->House), + IsHuman(false), + WasHuman(false), + IsPlayerControl(false), + IsStarted(false), + IsAlerted(false), + IsBaseBuilding(false), + IsDiscovered(false), + IsMaxedOut(false), + IsDefeated(false), + IsToDie(false), + IsToLose(false), + IsToWin(false), + IsCivEvacuated(false), + IsRecalcNeeded(true), + IsVisionary(false), + IsTiberiumShort(false), + IsSpied(false), + IsThieved(false), + IsGPSActive(false), + IsBuiltSomething(false), + IsResigner(false), + IsGiverUpper(false), + IsParanoid(false), + IsToLook(true), + IsQueuedMovementToggle(false), + DidRepair(false), + IQ(Control.IQ), + State(STATE_BUILDUP), + JustBuiltStructure(STRUCT_NONE), + JustBuiltInfantry(INFANTRY_NONE), + JustBuiltUnit(UNIT_NONE), + JustBuiltAircraft(AIRCRAFT_NONE), + JustBuiltVessel(VESSEL_NONE), + Blockage(0), + RepairTimer(0), + AlertTime(0), + BorrowedTime(0), + BScan(0), + ActiveBScan(0), + OldBScan(0), + UScan(0), + ActiveUScan(0), + OldUScan(0), + IScan(0), + ActiveIScan(0), + OldIScan(0), + AScan(0), + ActiveAScan(0), + OldAScan(0), + VScan(0), + ActiveVScan(0), + OldVScan(0), + CreditsSpent(0), + HarvestedCredits(0), + StolenBuildingsCredits(0), + CurUnits(0), + CurBuildings(0), + CurInfantry(0), + CurVessels(0), + CurAircraft(0), + Tiberium(0), + Credits(0), + Capacity(0), + AircraftTotals(NULL), + InfantryTotals(NULL), + UnitTotals(NULL), + BuildingTotals(NULL), + VesselTotals(NULL), + DestroyedAircraft(NULL), + DestroyedInfantry(NULL), + DestroyedUnits(NULL), + DestroyedBuildings(NULL), + DestroyedVessels(NULL), + CapturedBuildings(NULL), + TotalCrates(NULL), + AircraftFactories(0), + InfantryFactories(0), + UnitFactories(0), + BuildingFactories(0), + VesselFactories(0), + Power(0), + Drain(0), + AircraftFactory(-1), + InfantryFactory(-1), + UnitFactory(-1), + BuildingFactory(-1), + VesselFactory(-1), + Radar(RADAR_NONE), + FlagLocation(TARGET_NONE), + FlagHome(0), + UnitsLost(0), + BuildingsLost(0), + WhoLastHurtMe(house), + StartLocationOverride(-1), + Center(0), + Radius(0), + LATime(0), + LAType(RTTI_NONE), + LAZone(ZONE_NONE), + LAEnemy(HOUSE_NONE), + ToCapture(TARGET_NONE), + RadarSpied(0), + PointTotal(0), + PreferredTarget(QUARRY_ANYTHING), + ScreenShakeTime(0), + Attack(0), + Enemy(HOUSE_NONE), + AITimer(0), + UnitToTeleport(0), + BuildStructure(STRUCT_NONE), + BuildUnit(UNIT_NONE), + BuildInfantry(INFANTRY_NONE), + BuildAircraft(AIRCRAFT_NONE), + BuildVessel(VESSEL_NONE), + NukeDest(0), + Allies(0), + DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay), + TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay), + TriggerTime(0), + SpeakAttackDelay(1), + SpeakPowerDelay(1), + SpeakMoneyDelay(1), + SpeakMaxedDelay(1), + RemapColor(Class->RemapColor), + DebugUnlockBuildables(false) +{ + /* + ** Explicit in-place construction of the super weapons is + ** required here because the default constructor for super + ** weapons must serve as a no-initialization constructor (save/load reasons). + */ + new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + + memset(UnitsKilled, '\0', sizeof(UnitsKilled)); + memset(BuildingsKilled, '\0', sizeof(BuildingsKilled)); + memset(BQuantity, '\0', sizeof(BQuantity)); + memset(UQuantity, '\0', sizeof(UQuantity)); + memset(IQuantity, '\0', sizeof(IQuantity)); + memset(AQuantity, '\0', sizeof(AQuantity)); + memset(VQuantity, '\0', sizeof(VQuantity)); + strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name. + HouseTriggers[house].Clear(); + memset((void *)&Regions[0], 0x00, sizeof(Regions)); + Make_Ally(house); + Assign_Handicap(Scen.CDifficulty); + + /* + ** Set the time of the first AI attack. + */ + Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + + Init_Unit_Trackers(); +} + + +/*********************************************************************************************** + * HouseClass::~HouseClass -- House class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/6/96 4:48PM ST : Created * + *=============================================================================================*/ +HouseClass::~HouseClass (void) +{ + Class = 0; + + Free_Unit_Trackers(); +} + + +/*********************************************************************************************** + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * * + * This is the default constructor that initializes all the values to their default * + * settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +HouseStaticClass::HouseStaticClass(void) : + IQ(0), + TechLevel(1), + Allies(0), + MaxUnit(Rule.UnitMax/6), + MaxBuilding(Rule.BuildingMax/6), + MaxInfantry(Rule.InfantryMax/6), + MaxVessel(Rule.VesselMax/6), + MaxAircraft(Rule.UnitMax/6), + InitialCredits(0), + Edge(SOURCE_NORTH) +{ +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- General purpose build legality checker. * + * * + * This routine is called when it needs to be determined if the specified object type can * + * be built by this house. Production and sidebar maintenance use this routine heavily. * + * * + * INPUT: type -- Pointer to the type of object that legality is to be checked for. * + * * + * house -- This is the house to check for legality against. Note that this might * + * not be 'this' house since the check could be from a captured factory. * + * Captured factories build what the original owner of them could build. * + * * + * OUTPUT: Can the specified object be built? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * + * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. * + * 11/04/1996 JLB : Computer uses prerequisite record. * + *=============================================================================================*/ +bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const +{ + assert(Houses.ID(this) == ID); + assert(type != NULL); + + /* + ** An object with a prohibited tech level availability will never be allowed, regardless + ** of who requests it. + */ + if (((TechnoTypeClass const *)type)->Level == -1) return(false); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** If this is a CounterStrike II-only unit, and we're playing a multiplayer + ** game in 'downshifted' mode against CounterStrike or Red Alert, then + ** don't allow building this unit. + */ + if (!NewUnitsEnabled) { + switch(type->What_Am_I()) { + case RTTI_INFANTRYTYPE: + if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT) + return(false); + break; + case RTTI_UNITTYPE: + if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT) + return(false); + break; + case RTTI_VESSELTYPE: + if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT) + return(false); + break; + default: + break; + } + } +#endif + + /* + ** The computer can always build everything. + */ + if (!IsHuman && Session.Type == GAME_NORMAL) return(true); + + /* + ** Special hack to get certain objects to exist for both sides in the game. + */ + int own = type->Get_Ownable(); + + /* + ** Check to see if this owner can build the object type specified. + */ + if (((1L << house) & own) == 0) { + return(false); + } + + /* + ** Perform some equivalency fixups for the building existence flags. + */ + long flags = ActiveBScan; + + /* + ** The computer records prerequisite buildings because it can't relay on the + ** sidebar to keep track of this information. + */ + if (!IsHuman) { + flags = OldBScan; + } + + int pre = ((TechnoTypeClass const *)type)->Prerequisite; + + /* + ** Advanced power also serves as a prerequisite for normal power. + */ + if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; + + /* + ** Either tech center counts as a prerequisite. + */ + if (Session.Type != GAME_NORMAL) { + if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH; + } + + int level = Control.TechLevel; +#ifdef CHEAT_KEYS + if (Debug_Cheat) { + level = 98; + pre = 0; + } +#endif + + // ST - 8/23/2019 4:53PM + if (DebugUnlockBuildables) { + level = 98; + pre = 0; + } + + /* + ** See if the prerequisite requirements have been met. + */ + return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= (unsigned)level); +} + + +/*************************************************************************** + * HouseClass::Init -- init's in preparation for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 12/17/1994 JLB : Resets tracker bits. * + *=========================================================================*/ +void HouseClass::Init(void) +{ + Houses.Free_All(); + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseTriggers[index].Clear(); + } +} + +// Object selection list is switched with player context for GlyphX. ST - 8/7/2019 10:11AM +extern void Logic_Switch_Player_Context(HouseClass *house); +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * HouseClass::AI -- Process house logic. * + * * + * This handles the AI for the house object. It should be called once per house per game * + * tick. It processes all house global tasks such as low power damage accumulation and * + * house specific trigger events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * + *=============================================================================================*/ +extern void Recalculate_Placement_Distances(); + +void HouseClass::AI(void) +{ + assert(Houses.ID(this) == ID); + + // Set PlayerPtr to be this house. ST - 8/7/2019 10:12AM + Logic_Switch_Player_Context(this); + + /* + ** If base building has been turned on by a trigger, then force the house to begin + ** production and team creation as well. This is also true if the IQ is high enough to + ** being base building. + */ + if (IsBaseBuilding || IQ >= Rule.IQProduction) { + IsBaseBuilding = true; + IsStarted = true; + IsAlerted = true; + } + + /* + ** Check to see if the house wins. + */ + if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) { + IsToWin = false; + if (this == PlayerPtr) { + PlayerWins = true; + } else { + PlayerLoses = true; + } + } + + /* + ** Check to see if the house loses. + */ + if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) { + IsToLose = false; + if (this == PlayerPtr) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + } + + /* + ** Check to see if all objects of this house should be blown up. + */ + if (IsToDie && BorrowedTime == 0) { + IsToDie = false; + Blowup_All(); + } + + /* + ** Double check power values to correct illegal conditions. It is possible to + ** get a power output of negative (one usually) as a result of damage sustained + ** and the fixed point fractional math involved with power adjustments. If the + ** power rating drops below zero, then make it zero. + */ + Power = max(Power, 0); + Drain = max(Drain, 0); + + /* + ** If the base has been alerted to the enemy and should be attacking, then + ** see if the attack timer has expired. If it has, then create the attack + ** teams. + */ + if (IsAlerted && AlertTime == 0) { + + /* + ** Adjusted to reduce maximum number of teams created. + */ + int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1)); + for (int index = 0; index < maxteams; index++) { + TeamTypeClass const * ttype = Suggested_New_Team(true); + if (ttype != NULL) { + ScenarioInit++; + ttype->Create_One_Of(); + ScenarioInit--; + } + } + AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); +// int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2); +// int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2); +// AlertTime = Random_Pick(mintime, maxtime); + } + + /* + ** If this house's flag waypoint is a valid cell, see if there's + ** someone sitting on it. If so, make the scatter. + */ + if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) { + + TechnoClass * techno = Map[FlagHome].Cell_Techno(); + if (techno != NULL) { + bool moving = false; + if (techno->Is_Foot()) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + if (!moving) { + techno->Scatter(0, true, true); + } + } + } + + /* + ** Create teams for this house if necessary. + ** (Use the same timer for some extra capture-the-flag logic.) + */ + if (!IsAlerted && !TeamTime) { + + TeamTypeClass const * ttype = Suggested_New_Team(false); + if (ttype) { + ttype->Create_One_Of(); + } + + TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE; + } + + /* + ** If there is insufficient power, then all buildings that are above + ** half strength take a little bit of damage. + */ + if (DamageTime == 0) { + + /* + ** When the power is below required, then the buildings will take damage over + ** time. + */ + if (Power_Fraction() < 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass & b = *Buildings.Ptr(index); + + if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) { + // BG: Only damage buildings that require power, to keep the + // land mines from blowing up under low-power conditions + if (b.Class->Drain) { + int damage = 1; + b.Take_Damage(damage, 0, WARHEAD_AP, 0); + } + } + } + } + DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay; + } + + /* + ** If there are no more buildings to sell, then automatically cancel the + ** sell mode. + */ + if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) { + Map.Sell_Mode_Control(0); + } + + /* + ** Various base conditions may be announced to the player. Typically, this would be + ** low tiberium capacity or low power. + */ + if (PlayerPtr == this) { + + if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) { + Speak(VOX_NEED_MO_MONEY); + Map.Flash_Money(); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + + int text_id = TXT_INSUFFICIENT_FUNDS; + char const * text = Text_String(TXT_INSUFFICIENT_FUNDS); + if (text != NULL) { + Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + + if (SpeakMaxedDelay == 0 && IsMaxedOut) { + IsMaxedOut = false; + if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + } + } + if (SpeakPowerDelay == 0 && Power_Fraction() < 1) { + if (ActiveBScan & STRUCTF_CONST) { + Speak(VOX_LOW_POWER); + SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + Map.Flash_Power(); + + int text_id = -1; + char const * text = NULL; + if (BQuantity[STRUCT_AAGUN] > 0) { + text = Text_String(TXT_POWER_AAGUN); + text_id = TXT_POWER_AAGUN; + } + if (BQuantity[STRUCT_TESLA] > 0) { + text = Text_String(TXT_POWER_TESLA); + text_id = TXT_POWER_TESLA; + } + if (text == NULL) { + text = Text_String(TXT_LOW_POWER); + text_id = TXT_LOW_POWER; + } + if (text != NULL) { + Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + } + } + + /* + ** If there is a flag associated with this house, then mark it to be + ** redrawn. + */ + if (Target_Legal(FlagLocation)) { + UnitClass * unit = As_Unit(FlagLocation); + if (unit) { + unit->Mark(MARK_CHANGE); + } else { + CELL cell = As_Cell(FlagLocation); + Map[cell].Redraw_Objects(); + } + } + + bool is_time = false; + + /* + ** Triggers are only checked every so often. If the trigger timer has expired, + ** then set the trigger processing flag. + */ + if (TriggerTime == 0 || IsBuiltSomething) { + is_time = true; + TriggerTime = TICKS_PER_MINUTE/10; + IsBuiltSomething = false; + } + + /* + ** Process any super weapon logic required. + */ + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER || !MPSuperWeaponDisable) { + Super_Weapon_Handler(); + } + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Scen.AutoSonarTimer == 0 ) + { + // If house has nothing but subs left, do an automatic sonar pulse to reveal them. + if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw + { + int iCount = 0; + int i; + for( i = 0; i != STRUCT_COUNT-3; ++i ) + { + iCount += BQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != UNIT_RA_COUNT-3; ++i ) + { + iCount += UQuantity[ i ]; + } + if( !iCount ) + { + // ajw - Found bug - house's civilians are not removed from IQuantity when they die. + // Workaround... + for( i = 0; i <= INFANTRY_DOG; ++i ) + { + iCount += IQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != AIRCRAFT_COUNT; ++i ) + { + iCount += AQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != VESSEL_RA_COUNT; ++i ) + { + if( i != VESSEL_SS ) + iCount += VQuantity[ i ]; + } + if( !iCount ) + { + // Do the ping. + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + bAutoSonarPulse = true; + } + } + } + } + } + } + } +#endif + + if (Session.Type != GAME_NORMAL) { + Check_Pertinent_Structures(); + } + + /* + ** Special win/lose check for multiplayer games; by-passes the + ** trigger system. We must wait for non-zero frame, because init + ** may not properly set IScan etc for each house; you have to go + ** through each object's AI before it will be properly set. + */ + if (Session.Type != GAME_NORMAL && !IsDefeated && + !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) { + MPlayer_Defeated(); + } + + /* + ** Try to spring all events attached to this house. The triggers will check + ** for themselves if they actually need to be sprung or not. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + if (HouseTriggers[Class->House][index]->Spring() && index > 0) { + index--; + continue; + } + } + + /* + ** If a radar facility is not present, but the radar is active, then turn the radar off. + ** The radar also is turned off when the power gets below 100% capacity. + */ + if (PlayerPtr == this) { + bool jammed = true; + + /* + ** Find if there are any radar facilities, and if they're jammed or not + */ + + if (IsGPSActive) { + jammed = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); +#ifdef FIXIT_RADAR_JAMMED + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) { +#else + if (building && building->House == PlayerPtr) { +#endif + if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) { + if (!building->IsJammed) { + jammed = false; + break; + } + } + } + } + } + + Map.Set_Jammed(this, jammed); +// Need to add in here where we activate it when only GPS is active. + if (Map.Is_Radar_Active()) { + if (ActiveBScan & STRUCTF_RADAR) { + if (Power_Fraction() < 1 && !IsGPSActive) { + Map.Radar_Activate(0); + } + } else { + if (!IsGPSActive) { + Map.Radar_Activate(0); + } + } + + } else { + if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) { + if (Power_Fraction() >= 1 || IsGPSActive) { + Map.Radar_Activate(1); + } + } else { + if (Map.Is_Radar_Existing()) { + Map.Radar_Activate(4); + } + } + } + if (!IsGPSActive && !(ActiveBScan & STRUCTF_RADAR)) { + Radar = RADAR_NONE; + } else { + Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF; + } + } + + VisibleCredits.AI(false, this, true); + + /* + ** Perform any expert system AI processing. + */ + if (IsBaseBuilding && AITimer == 0) { + AITimer = Expert_AI(); + } + + if (!IsBaseBuilding && State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } + + AI_Building(); + AI_Unit(); + AI_Vessel(); + AI_Infantry(); + AI_Aircraft(); + + + /* + ** If the production possibilities need to be recalculated, then do so now. This must + ** occur after the scan bits have been properly updated. + */ + if (PlayerPtr == this && IsRecalcNeeded) { + IsRecalcNeeded = false; + Map.Recalc(); + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) { + + if (PlayerPtr == building->House) { + building->Update_Buildables(); + } + } + } + + Recalculate_Placement_Distances(); + Check_Pertinent_Structures(); + } + + /* + ** See if it's time to re-set the can-repair flag + */ + if (DidRepair && RepairTimer == 0) { + DidRepair = false; + } + + if (this == PlayerPtr && IsToLook) { + IsToLook = false; + Map.All_To_Look(PlayerPtr); + } +} + +/*********************************************************************************************** + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * * + * This handles any super weapons assigned to this house. It also performs any necessary * + * maintenance that the super weapons require. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Super_Weapon_Handler(void) +{ + /* + ** Perform all super weapon AI processing. This just checks to see if + ** the graphic needs changing for the special weapon and updates the + ** sidebar as necessary. + */ + for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) { + SuperClass * super = &SuperWeapon[special]; + + if (super->Is_Present()) { + + /* + ** Perform any charge-up logic for the super weapon. If the super + ** weapon is owned by the player and a graphic change is detected, then + ** flag the sidebar to be redrawn so the player will see the change. + */ + if (super->AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + + /* + ** Repeating super weapons that require power will be suspended if there + ** is insufficient power available. + */ + if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) { + super->Suspend(Power_Fraction() < 1); + } + } + } + + /* + ** Check to see if they have launched the GPS, but subsequently lost their + ** tech center. If so, remove the GPS, and shroud the map. + */ + if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) { + IsGPSActive = false; + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:32AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (IsPlayerControl) { + Map.Shroud_The_Map(PlayerPtr); + } + + } else { + + if (IsHuman) { + Map.Shroud_The_Map(this); + } + } + } + + /* + ** Check to see if the GPS Satellite should be removed from the sidebar + ** because of outside circumstances. The advanced technology facility + ** being destroyed is a good example of this. Having fired the satellite + ** is another good example, because it's a one-shot item. + */ + if (SuperWeapon[SPC_GPS].Is_Present()) { + if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) { + /* + ** Remove the missile capability when there is no advanced tech facility. + */ + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + /* + ** Auto-fire the GPS satellite if it's charged up. + */ + if (SuperWeapon[SPC_GPS].Is_Ready()) { + SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr); + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) { + bldg->HasFired = true; + bldg->Assign_Mission(MISSION_MISSILE); + break; + } + } + } + } + } else { + /* + ** If there is no GPS satellite present, but there is a GPS satellite + ** facility available, then make the GPS satellite available as well. + */ + if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 && + !IsGPSActive && + Control.TechLevel >= Rule.GPSTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + bool canfire = false; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) { + if (!bldg->HasFired) { + canfire = true; + break; + } + } + } + + if (canfire) { + SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_GPS, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_GPS); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + } + + /* + ** Check to see if the chronosphere should be removed from the sidebar + ** because of outside circumstances. The chronosphere facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the chronosphere when there is no chronosphere facility. + ** Note that this will not remove the one time created chronosphere. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) { + if (this == PlayerPtr) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) { + if (Map.IsTargettingMode == SPC_CHRONO2) { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + } else { + Map.IsTargettingMode = SPC_NONE; + } + } else { + Map.IsTargettingMode = SPC_NONE; + } + } +#else + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || + Map.IsTargettingMode == SPC_CHRONO2) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no chronosphere present, but there is a chronosphere + ** facility available, then make the chronosphere available as well. + */ + if ((ActiveBScan & STRUCTF_CHRONOSPHERE) && +// (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) && + (unsigned)Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level && +// Control.TechLevel >= Rule.ChronoTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_CHRONOSPHERE, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the iron curtain should be removed from the sidebar + ** because of outside circumstances. The iron curtain facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the iron curtain when there is no iron curtain facility. + ** Note that this will not remove the one time created iron curtain. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no iron curtain present, but there is an iron curtain + ** facility available, then make the iron curtain available as well. + */ + if ((ActiveBScan & STRUCTF_IRON_CURTAIN) && + (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_IRON_CURTAIN, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the sonar pulse should be removed from the sidebar + ** because of outside circumstances. The spied-upon enemy sub pen + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) { + int usspy = 1 << (Class->House); + bool present = false; + bool powered = false; + for (int q = 0; q < Buildings.Count() && !powered; q++) { + BuildingClass * bldg = Buildings.Ptr(q); + if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->Spied_By() & usspy) ) { + present = true; + powered = !(bldg->House->Power_Fraction() < 1); + } + } + if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the sonar pulse when there is no spied-upon enemy sub pen. + ** Note that this will not remove the one time created sonar pulse. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } + } + + /* + ** Check to see if the nuclear weapon should be removed from the sidebar + ** because of outside circumstances. The missile silos + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the nuke when there is no missile silo. + ** Note that this will not remove the one time created nuke. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } else { + /* + ** Allow the computer to fire the nuclear weapon when the weapon is + ** ready and the owner is the computer. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + } + } + + } else { + /* + ** If there is no nuclear missile present, but there is a missile + ** silo available, then make the missile available as well. + */ + if ((ActiveBScan & STRUCTF_MSLO) && + ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + + if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_SPY_MISSION].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Map.Column[1].Flag_To_Redraw(); + } + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_SPY_MISSION); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) { + SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SPY_MISSION, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_BOMB].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_BOMB); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) { + SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_INFANTRY); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) { + SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_INFANTRY, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Attacked -- Lets player know if base is under attack. * + * * + * Call this function whenever a building is attacked (with malice). This function will * + * then announce to the player that his base is under attack. It checks to make sure that * + * this is referring to the player's house rather than the enemy's. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Attacked(BuildingClass* source) +{ + assert(Houses.ID(this) == ID); + +#ifdef FIXIT_BASE_ANNOUNCE + if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) { +#else + if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) { +#endif + Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0); + SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + + /* + ** If there is a trigger event associated with being attacked, process it + ** now. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * * + * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * + * all storage capable buildings for the house. Harvested Tiberium adds to the credit * + * value of the house, but only up to the maximum storage capacity that the house can * + * currently maintain. * + * * + * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Harvested(unsigned tiberium) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + + Tiberium += tiberium; + if (Tiberium > Capacity) { + Tiberium = Capacity; + IsMaxedOut = true; + } + HarvestedCredits += tiberium; + Silo_Redraw_Check(oldtib, Capacity); +} + + +/*********************************************************************************************** + * HouseClass::Stole -- Accounts for the value of a captured building. * + * * + * Use this routine whenever a building is captured. It keeps track of the cost of the * + * building for use in the scoring routine, because you get an 'economy' boost for the * + * value of the stolen building (but you don't get the credit value for it.) * + * * + * INPUT: worth -- The worth of the building we captured (stole). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/05/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Stole(unsigned worth) +{ + assert(Houses.ID(this) == ID); + + StolenBuildingsCredits += worth; +} + + +/*********************************************************************************************** + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * * + * Use this routine to determine the total credit value of the house. This is the sum of * + * the harvested Tiberium in storage and the initial unspent cash reserves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total credit value of the house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +long HouseClass::Available_Money(void) const +{ + assert(Houses.ID(this) == ID); + + return(Tiberium + Credits); +} + + +/*********************************************************************************************** + * HouseClass::Spend_Money -- Removes money from the house. * + * * + * Use this routine to extract money from the house. Typically, this is a result of * + * production spending. The money is extracted from available cash reserves first. When * + * cash reserves are exhausted, then Tiberium is consumed. * + * * + * INPUT: money -- The amount of money to spend. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/20/1995 JLB : Spends Tiberium before spending cash. * + *=============================================================================================*/ +void HouseClass::Spend_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + if (money > (unsigned)Tiberium) { + money -= (unsigned)Tiberium; + Tiberium = 0; + Credits -= money; + } else { + Tiberium -= money; + } + Silo_Redraw_Check(oldtib, Capacity); + CreditsSpent += money; +} + + +/*********************************************************************************************** + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * * + * Use this routine when money needs to be refunded back to the house. This can occur when * + * construction is aborted. At this point, the exact breakdown of Tiberium or initial * + * credits used for the orignal purchase is lost. Presume as much of the money is in the * + * form of Tiberium as storage capacity will allow. * + * * + * INPUT: money -- The number of credits to refund back to the house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/01/1995 JLB : Refunded money is never lost * + *=============================================================================================*/ +void HouseClass::Refund_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + Credits += money; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * * + * Use this routine to adjust the maximum storage capacity for the house. This storage * + * capacity will limit the number of Tiberium credits that can be stored at any one time. * + * * + * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * + * * + * inanger -- Is this a forced adjustment to capacity due to some hostile event? * + * * + * OUTPUT: Returns with the number of Tiberium credits lost. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Adjust_Capacity(int adjust, bool inanger) +{ + assert(Houses.ID(this) == ID); + + long oldcap = Capacity; + int retval = 0; + + Capacity += adjust; + Capacity = max(Capacity, 0L); + if (Tiberium > Capacity) { + retval = Tiberium - Capacity; + Tiberium = Capacity; + if (!inanger) { + Refund_Money(retval); + retval = 0; + } else { + IsMaxedOut = true; + } + } + Silo_Redraw_Check(Tiberium, oldcap); + return(retval); +} + + +/*********************************************************************************************** + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * * + * Call this routine when either the capacity or tiberium levels change for a house. This * + * routine will determine if the aggregate tiberium storage level will result in the * + * silos changing their imagery. If this is detected, then all the silos for this house * + * are flagged to be redrawn. * + * * + * INPUT: oldtib -- Pre-change tiberium level. * + * * + * oldcap -- Pre-change tiberium storage capacity. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) +{ + assert(Houses.ID(this) == ID); + + int oldratio = 0; + if (oldcap) oldratio = (oldtib * 5) / oldcap; + int newratio = 0; + if (Capacity) newratio = (Tiberium * 5) / Capacity; + + if (oldratio != newratio) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { + b->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * * + * This routine will determine if the house number specified is a ally to this house. * + * * + * INPUT: house -- The house number to check to see if it is an ally. * + * * + * OUTPUT: Is the house an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(HousesType house) const +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE) { + return(((1<Class->House)); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * * + * This routine will examine the specified object and return whether it is an ally or not. * + * * + * INPUT: object -- The object to examine to see if it is an ally. * + * * + * OUTPUT: Is the specified object an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (object) { + return(Is_Ally(object->Owner())); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Make_Ally -- Make the specified house an ally. * + * * + * This routine will make the specified house an ally to this house. An allied house will * + * not be considered a threat or potential target. * + * * + * INPUT: house -- The house to make an ally of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 08/08/1995 JLB : Breaks off combat when ally commences. * + * 10/17/1995 JLB : Added reveal base when allied. * + *=============================================================================================*/ +void HouseClass::Make_Ally(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (Is_Allowed_To_Ally(house)) { + + Allies |= (1L << house); + + /* + ** Don't consider the newfound ally to be an enemy -- of course. + */ + if (Enemy == house) { + Enemy = HOUSE_NONE; + } + + if (ScenarioInit) { + Control.Allies |= (1L << house); + } + + if (Session.Type != GAME_NORMAL && !ScenarioInit) { + HouseClass * hptr = HouseClass::As_Pointer(house); + + /* + ** An alliance with another human player will cause the computer + ** players (if present) to become paranoid. + */ + if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) { +// if (hptr != NULL && hptr->IsHuman) { +// if (!hptr->IsHuman) { +// hptr->Make_Ally(Class->House); +// } + Computer_Paranoid(); + } + + char buffer[80]; + + /* + ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure + ** that fighting will most likely stop when the cease fire begins. + */ + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + + if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) { + TARGET target = ((TechnoClass *)object)->TarCom; + if (Target_Legal(target) && As_Techno(target) != NULL) { + if (Is_Ally(As_Techno(target))) { + ((TechnoClass *)object)->Assign_Target(TARGET_NONE); + } + } + } + } + + /* + ** Cause all structures to be revealed to the house that has been + ** allied with. + */ + if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b && !b->IsInLimbo && (HouseClass *)b->House == this) { + Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false); + } + } + } + + if (IsHuman) { + sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_HAS_ALLIED), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[((HouseClass::As_Pointer(house))->Class->House) - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + } + +#if(TEN) + // + // Notify the TEN server of the new alliance + // + if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(hptr->IniName, 1); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the new alliance + // + //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(hptr->IniName, 1); + //} +#endif // MPATH + + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * * + * This routine will flag the house specified so that it will be an enemy to this house. * + * Enemy houses are legal targets for attack. * + * * + * INPUT: house -- The house to make an enemy of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/27/1995 JLB : Making war is a bilateral action. * + *=============================================================================================*/ +void HouseClass::Make_Enemy(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE && Is_Ally(house)) { + HouseClass * enemy = HouseClass::As_Pointer(house); + Allies &= ~(1L << house); + + if (ScenarioInit) { + Control.Allies &= !(1L << house); + } + + /* + ** Breaking an alliance is a bilateral event. + */ + if (enemy != NULL && enemy->Is_Ally(this)) { + enemy->Allies &= ~(1L << Class->House); + + if (ScenarioInit) { + Control.Allies &= ~(1L << Class->House); + } + } + + if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) { + char buffer[80]; + + sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + Map.Flag_To_Redraw(false); + +#if(TEN) + // + // Notify the TEN server of the broken alliance + // + if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(enemy->IniName, 0); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the broken alliance + // + //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(enemy->IniName, 0); + //} +#endif // MPATH + + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * * + * This routine will return with the remap table to use when displaying an object owned * + * by this house. If the object is blushing (flashing), then the lightening remap table is * + * always used. The "unit" parameter allows proper remap selection for those houses that * + * have a different remap table for buildings or units. * + * * + * INPUT: blushing -- Is the object blushing (flashing)? * + * * + * remap -- The remap control value to use. * + * REMAP_NONE No remap pointer returned at all. * + * REMAP_NORMAL Return the remap pointer for this house. * + * REMAP_ALTERNATE (Nod solo play only -- forces red remap). * + * Multiplay returns same as REMAP_NORMAL * + * * + * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/25/1995 JLB : Uses remap control value. * + *=============================================================================================*/ +unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const +{ + assert(Houses.ID(this) == ID); + + if (blushing) return(&Map.FadingLight[0]); + + if (remap == REMAP_NONE) return(0); + + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * * + * This routine examines the house condition and returns with the team that it thinks * + * should be created. The units that are not currently a member of a team are examined * + * to determine the team needed. * + * * + * INPUT: alertcheck -- Select from the auto-create team list. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) +{ + assert(Houses.ID(this) == ID); + + return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck)); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * * + * This routine is called when the threat rating for a region needs to change. The region * + * and threat adjustment are provided. * + * * + * INPUT: region -- The region that adjustment is to occur on. * + * * + * threat -- The threat adjustment to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Threat(int region, int threat) +{ + assert(Houses.ID(this) == ID); + + static int _val[] = { + -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, + -1, 0, 1, + MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 + }; + static int _thr[] = { + 2, 1, 2, + 1, 0, 1, + 2, 1, 2 + }; + int neg; + int * val = &_val[0]; + int * thr = &_thr[0]; + + if (threat < 0) { + threat = -threat; + neg = true; + } else { + neg = false; + } + + for (int lp = 0; lp < 9; lp ++) { + Regions[region + *val].Adjust_Threat(threat >> *thr, neg); + val++; + thr++; + } +} + + +/*********************************************************************************************** + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * * + * This routine is called from the event system. It will start production for the object * + * type specified. This will be reflected in the sidebar as well as the house factory * + * tracking variables. * + * * + * INPUT: type -- The type of object to begin production on. * + * * + * id -- The subtype of object. * + * * + * OUTPUT: Returns with the reason why, or why not, production was started. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/21/1996 JLB : Handles max object case. * + *=============================================================================================*/ +ProdFailType HouseClass::Begin_Production(RTTIType type, int id) +{ + assert(Houses.ID(this) == ID); + int result = true; + bool initial_start = false; + FactoryClass * fptr; + TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); + + fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code, unless we are restarting production. + */ + if (fptr != NULL) { + if (fptr->Is_Building()) { + return(PROD_CANT); + } + } else { + fptr = new FactoryClass(); + if (!fptr) return(PROD_CANT); + Set_Factory(type, fptr); + result = fptr->Set(*tech, *this); + initial_start = true; + + /* + ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts. + ** ST - 3/17/2020 2:03PM + */ + if (!result) { + Set_Factory(type, NULL); + delete fptr; + fptr = NULL; + } + } + + if (result) { + fptr->Start(); + + /* + ** Link this factory to the sidebar so that proper graphic feedback + ** can take place. + */ + // Handle Glyphx multiplayer sidebar. ST - 8/14/2019 1:26PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Factory_Link(fptr->ID, type, id, this); + } + } else { + if (PlayerPtr == this) { + Map.Factory_Link(fptr->ID, type, id); + } + } + + return(PROD_OK); + } + + delete fptr; + return(PROD_CANT); +} + + +/*********************************************************************************************** + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * * + * This routine is called from the event system whenever the production of the specified * + * type needs to be suspended. The suspended production will be reflected in the sidebar * + * as well as in the house control structure. * + * * + * INPUT: type -- The type of object that production is being suspended for. * + * * + * OUTPUT: Returns why, or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Suspend_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Actually suspend the production. + */ + fptr->Suspend(); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.SidebarClass::IsToRedraw = true; + if (!RunningAsDLL) { // Don't force a redraw when running under GlyphX. PlayerPtr==this will always be true in this case, and we don't want to force a redraw even for AI players + Map.Flag_To_Redraw(false); + } + } + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * * + * This routine is called from the event system whenever production must be abandoned for * + * the type specified. This will remove the factory and pending object from the sidebar as * + * well as from the house factory record. * + * * + * INPUT: type -- The object type that production is being suspended for. * + * * + * OUTPUT: Returns the reason why or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Abandon_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If there is no factory to abandon, then return with a failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + // Handle Glyphx multiplayer sidebar. ST - 8/7/2019 10:18AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Abandon_Production(type, fptr->ID, this); + + // Need to clear pending object here if legacy renderer enabled + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + } else { + if (PlayerPtr == this) { + Map.Abandon_Production(type, fptr->ID); + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + } + + /* + ** Abandon production of the object. + */ + fptr->Abandon(); + Set_Factory(type, NULL); + delete fptr; + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * * + * This routine will pick a good target to fire the special weapon specified. * + * * + * INPUT: id -- The special weapon id to fire. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 PWG : Created. * + *=============================================================================================*/ +void HouseClass::Special_Weapon_AI(SpecialWeaponType id) +{ + assert(Houses.ID(this) == ID); + + /* + ** Loop through all of the building objects on the map + ** and see which ones are available. + */ + BuildingClass * bestptr = NULL; + int best = -1; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + /* + ** If the building is valid, not in limbo, not in the process of + ** being destroyed and not our ally, then we can consider it. + */ + if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { + if (Percent_Chance(90) && (b->Value() > best || best == -1)) { + best = b->Value(); + bestptr = b; + } + } + } + + if (bestptr) { + CELL cell = Coord_Cell(bestptr->Center_Coord()); + Place_Special_Blast(id, cell); + } +} + + +/*********************************************************************************************** + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * * + * This routine will create a blast effect at the cell specified. This is the result of * + * the special weapons. * + * * + * INPUT: id -- The special weapon id number. * + * * + * cell -- The location where the special weapon attack is to occur. * + * * + * OUTPUT: Was the special weapon successfully fired at the location specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented. * + * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * + * 07/28/1995 JLB : Revamped to use super weapon class controller. * + *=============================================================================================*/ +extern void Logic_Switch_Player_Context(ObjectClass *object); +extern void Logic_Switch_Player_Context(HouseClass *object); +extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + +bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) +{ + assert(Houses.ID(this) == ID); + + // Added. ST - 12/2/2019 11:26AM + bool fired = false; + const char *what = NULL; + + BuildingClass * launchsite = 0; + AnimClass * anim = 0; + switch (id) { + case SPC_SONAR_PULSE: + // Automatically discharge the sonar pulse and uncloak all subs. + if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) { + SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr); + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.Activate_Pulse(); + } + Sound_Effect(VOC_SONAR); + IsRecalcNeeded = true; + fired = true; + what = "SONAR"; + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { +#else + if (*sub == VESSEL_SS) { +#endif + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + } + break; + + case SPC_NUCLEAR_BOMB: + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) { + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(cell); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "NUKE"; + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.IsTargettingMode = SPC_NONE; + } + } + } else { + + /* + ** Search for a suitable launch site for this missile. + */ + launchsite = Find_Building(STRUCT_MSLO); + + /* + ** If a launch site was found, then proceed with the normal launch + ** sequence. + */ + if (launchsite) { + launchsite->Assign_Mission(MISSION_MISSILE); + launchsite->Commence(); + NukeDest = cell; + } + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "NUKE"; + } + } + break; + + case SPC_PARA_INFANTRY: + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) { + + TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF"); + if (ttype == NULL) { + ttype = new TeamTypeClass; + if (ttype != NULL) { + strcpy(ttype->IniName, "@PINF"); + ttype->IsTransient = true; + ttype->IsPrebuilt = false; + ttype->IsReinforcable = false; + ttype->Origin = WAYPT_SPECIAL; + ttype->MissionCount = 1; + ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT; + ttype->MissionList[0].Data.Value = WAYPT_SPECIAL; + ttype->ClassCount = 2; + ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers(); + ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + ttype->Members[1].Quantity = 1; + ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER); + } + } + + if (ttype != NULL) { + ttype->House = Class->House; + Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT); + Do_Reinforcements(ttype); + } + +// Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "PARA"; + } + break; + + case SPC_SPY_MISSION: + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell)); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "SPY"; + } + break; + + case SPC_PARA_BOMB: + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "PARABOMB"; + } + break; + + case SPC_IRON_CURTAIN: + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech) { + switch (tech->What_Am_I()) { + case RTTI_UNIT: + case RTTI_BUILDING: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND; + } +#endif + tech->Mark(MARK_CHANGE); + Sound_Effect(VOC_IRON1, tech->Center_Coord()); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr); + break; + default: + break; + } + } + + IsRecalcNeeded = true; + fired = true; + what = "IRON"; + } + break; + + case SPC_CHRONOSPHERE: + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech && Is_Ally(tech)) { + if (tech->What_Am_I() == RTTI_UNIT || + tech->What_Am_I() == RTTI_INFANTRY || +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) { +#else + (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) { +#endif + + if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + bool porthim = true; + if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) { + porthim = false; + } + if (porthim) { +#endif + HouseClass* old_player_ptr = PlayerPtr; + Logic_Switch_Player_Context(this); + Map.IsTargettingMode = SPC_CHRONO2; + On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode); + Logic_Switch_Player_Context(old_player_ptr); + UnitToTeleport = tech->As_Target(); + fired = true; + what = "CHRONO"; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + } + break; + + case SPC_CHRONO2: + { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + CELL oldcell = cell; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#else + if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#endif + /* + ** Destroy any infantryman that gets teleported + */ + if (tech->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)tech; + inf->Mark(MARK_UP); + inf->Coord = Cell_Coord(cell); + inf->Mark(MARK_DOWN); + int damage = inf->Strength; + inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->Assign_Target(tech->As_Target()); +#endif + } else { + /* + ** Warp the unit to the new location. + */ + DriveClass * drive = (DriveClass *)tech; + drive->MoebiusCell = Coord_Cell(drive->Coord); + oldcell = drive->MoebiusCell; + drive->Teleport_To(cell); + drive->IsMoebius = true; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->IsMoebius = false; + } + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + } +#else + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; +#endif + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONO, drive->Coord); + + /* + ** Set active animation on Chronospheres. + */ + for (int index = 0; index < Buildings.Count(); ++index) { + BuildingClass* building = Buildings.Ptr(index); + if (building != nullptr && building->IsActive && building->Owner() == Class->House && *building == STRUCT_CHRONOSPHERE) { + building->Begin_Mode(BSTATE_ACTIVE); + } + } + } + } + UnitToTeleport = TARGET_NONE; + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) { +#endif + SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + IsRecalcNeeded = true; + fired = true; + what = "CHRONO2"; + + /* + ** Now set a percentage chance that a time quake will occur. + */ + if (!TimeQuake) { + TimeQuake = Percent_Chance(Rule.QuakeChance * 100); + } + + /* + ** Now set a percentage chance that a chronal vortex will appear. It + ** might appear where the object teleported to or it might appear + ** where it teleported from -- random chance. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Don't allow a vortex if the teleportation was due to a chrono tank. + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) +#endif + if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) { + int x = Random_Pick(0, Map.MapCellWidth-1); + int y = Random_Pick(0, Map.MapCellHeight-1); + ChronalVortex.Appear(Cell_Coord(XY_Cell(Map.MapCellX + x, Map.MapCellY + y))); + +// if (Percent_Chance(50)) { +// ChronalVortex.Appear(Cell_Coord(oldcell)); +// } else { +// ChronalVortex.Appear(Cell_Coord(cell)); +// } + } + + break; + } + } + + /* + ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM + */ + if (IsHuman && fired && what) { + On_Achievement_Event(this, "SUPERWEAPON_FIRED", what); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * * + * This routine is called when a building has been produced and now must be placed on * + * the map. When the player clicks on the map, this routine is ultimately called when the * + * event passes through the event queue system. * + * * + * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * + * * + * * + * cell -- The location to place the object on the map. * + * * + * OUTPUT: Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + +bool HouseClass::Place_Object(RTTIType type, CELL cell) +{ + assert(Houses.ID(this) == ID); + + TechnoClass * tech = 0; + FactoryClass * factory = Fetch_Factory(type); + + /* + ** Only if there is a factory active for this type, can it be "placed". + ** In the case of a missing factory, then this request is completely bogus -- + ** ignore it. This might occur if, between two events to exit the same + ** object, the mouse was clicked on the sidebar to start building again. + ** The second placement event should NOT try to place the object that is + ** just starting construction. + */ + if (factory && factory->Has_Completed()) { + tech = factory->Get_Object(); + + if (cell == -1) { + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + +#ifdef FIXIT_HELI_LANDING + /* + ** Try to find a place for the object to appear from. For helicopters, it has the + ** option of finding a nearby helipad if no helipads are free. + */ + TechnoClass * builder = pending->Who_Can_Build_Me(false, false); + if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) { + builder = pending->Who_Can_Build_Me(true, false); + + } +#else + bool intheory = false; + if (pending->What_Am_I() == RTTI_AIRCRAFT) { + + /* + ** BG hack - helicopters don't need a specific building to + ** emerge from, in fact, they'll land next to a building if + ** need be. + */ + if( !((AircraftClass *)pending)->Class->IsFixedWing) { + intheory = true; + } + } + TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false); +#endif + TechnoTypeClass const *object_type = pending->Techno_Type_Class(); + if (builder != NULL && builder->Exit_Object(pending)) { + + /* + ** Since the object has left the factory under its own power, delete + ** the production manager tied to this slot in the sidebar. Its job + ** has been completed. + */ + factory->Set_Is_Blocked(false); + factory->Completed(); + Abandon_Production(type); + + /* + ** Could be tied to an achievement. ST - 11/11/2019 11:56AM + */ + if (IsHuman) { + if (object_type) { + On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName); + } + if (pending->IsActive) { + On_Ping(this, pending->Center_Coord()); + } + } + + switch (pending->What_Am_I()) { + case RTTI_UNIT: + JustBuiltUnit = ((UnitClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_VESSEL: + JustBuiltVessel = ((VesselClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + JustBuiltStructure = ((BuildingClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + } + } else { + /* + ** The object could not leave under it's own power. Just wait + ** until the player tries to place the object again. + */ + + /* + ** Flag that it's blocked so we can re-try the exit later. + ** This would have been a bad idea under the old peer-peer code since it would have pumped events into + ** the queue too often. ST - 2/25/2020 11:56AM + */ + factory->Set_Is_Blocked(true); + return(false); + } + } + + } else { + if (tech) { + TechnoClass * builder = tech->Who_Can_Build_Me(false, false); + if (builder) { + + builder->Transmit_Message(RADIO_HELLO, tech); + if (tech->Unlimbo(Cell_Coord(cell))) { + factory->Completed(); + Abandon_Production(type); + + if (PlayerPtr == this) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN); + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + } + return(true); + } else { + if (this == PlayerPtr) { + Speak(VOX_DEPLOY); + } + } + builder->Transmit_Message(RADIO_OVER_OUT); + } + return(false); + + } else { + + // Play a bad sound here? + return(false); + } + } + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * * + * This routine will inform the display system that building placement mode has begun. * + * The cursor will be created that matches the layout of the building shape. * + * * + * INPUT: builder -- The factory that is building this object. * + * * + * object -- The building that is going to be placed down on the map. * + * * + * OUTPUT: Was the building placement mode successfully initiated? * + * * + * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * + * is affected. * + * * + * HISTORY: * + * 05/04/1995 JLB : Created. * + * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * + *=============================================================================================*/ +bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) +{ + assert(Houses.ID(this) == ID); + + if (this == PlayerPtr && !Map.PendingObject && builder && object) { + /* + ** Ensures that object selection doesn't remain when + ** building placement takes place. + */ + Unselect_All(); + + Map.Repair_Mode_Control(0); + Map.Sell_Mode_Control(0); + + Map.PendingObject = object->Class; + Map.PendingObjectPtr = object; + Map.PendingHouse = Class->House; + + Map.Set_Cursor_Shape(object->Occupy_List(true)); + Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); + builder->Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*************************************************************************** + * HouseClass::Clobber_All -- removes all objects for this house * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine removes the house itself, so the multiplayer code * + * must not rely on there being "empty" houses lying around. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Clobber_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this) { + delete ::Aircraft.Ptr(i); + i--; + } + } + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this) { + delete ::Units.Ptr(i); + i--; + } + } + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this) { + delete ::Vessels.Ptr(i); + i--; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this) { + delete Infantry.Ptr(i); + i--; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this) { + delete Buildings.Ptr(i); + i--; + } + } + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->Class->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } + for (i = 0; i < TriggerTypes.Count(); i++) { + if (TriggerTypes.Ptr(i)->House == Class->House) { + delete TriggerTypes.Ptr(i); + i--; + } + } + + delete this; +} + + +/*********************************************************************************************** + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * * + * This routine is called when an object is to be removed from the game system. If the * + * specified object is part of the house tracking system, then it will be removed. * + * * + * INPUT: target -- The target value of the object that is to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Detach(TARGET target, bool ) +{ + assert(Houses.ID(this) == ID); + + if (ToCapture == target) { + ToCapture = TARGET_NONE; + } + + if (Is_Target_Trigger(target)) { + HouseTriggers[ID].Delete(As_Trigger(target)); + } +} + + +/*********************************************************************************************** + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * * + * This routine will examine the enemy houses and if there is a building owned by one * + * of those house, true will be returned. * + * * + * INPUT: btype -- The building type to check for. * + * * + * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const +{ + assert(Houses.ID(this) == ID); + + int bflag = 1L << btype; + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseClass * house = HouseClass::As_Pointer(index); + + if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * * + * This routine will examine the house status and return with a techno type pointer to * + * the object type that it thinks should be created. The type is restricted to match the * + * type specified. Typical use of this routine is by computer controlled factories. * + * * + * INPUT: objecttype -- The type of object to restrict the scan for. * + * * + * kennel -- Is this from a kennel? There are special hacks to ensure that only * + * dogs can be produced from a kennel. * + * * + * OUTPUT: Returns with a pointer to a techno type for the object type that should be * + * created. If no object should be created, then NULL is returned. * + * * + * WARNINGS: This is a time consuming routine. Only call when necessary. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const +{ + assert(Houses.ID(this) == ID); + + TechnoTypeClass const * techno = NULL; + + switch (objecttype) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (BuildAircraft != AIRCRAFT_NONE) { + return(&AircraftTypeClass::As_Reference(BuildAircraft)); + } + return(NULL); + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + if (BuildVessel != VESSEL_NONE) { + return(&VesselTypeClass::As_Reference(BuildVessel)); + } + return(NULL); + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (BuildUnit != UNIT_NONE) { + return(&UnitTypeClass::As_Reference(BuildUnit)); + } + return(NULL); + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (BuildInfantry != INFANTRY_NONE) { + if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL); + if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL); + return(&InfantryTypeClass::As_Reference(BuildInfantry)); + } + return(NULL); + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); + } + return(techno); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * * + * This routine will remove the flag attached to the specified target object or cell. * + * Call this routine before placing the object down. This is called inherently by the * + * the Flag_Attach() functions. * + * * + * INPUT: target -- The target that the flag was attached to but will be removed from. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully removed from the specified target? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Remove(TARGET target, bool set_home) +{ + assert(Houses.ID(this) == ID); + + bool rc = false; + + if (Target_Legal(target)) { + + /* + ** Remove the flag from a unit + */ + UnitClass * object = As_Unit(target); + if (object) { + rc = object->Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + + } else { + + /* + ** Remove the flag from a cell + */ + CELL cell = As_Cell(target); + if (Map.In_Radar(cell)) { + rc = Map[cell].Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + } + } + + /* + ** Handle the flag home cell: + ** If 'set_home' is set, clear the home value & the cell's overlay + */ + if (set_home) { + if (FlagHome != 0) { + Map[FlagHome].Overlay = OVERLAY_NONE; + Map.Flag_Cell(FlagHome); + FlagHome = 0; + } + } + } + return(rc); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * * + * This routine will attach the house flag to the location specified. If the location * + * cannot contain the flag, then a suitable nearby location will be selected. * + * * + * INPUT: cell -- The desired cell location to place the flag. * + * * + * set_home -- if true, resets the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully placed? * + * * + * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * + * Check the FlagLocation value to determine the final cell resting spot. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 10/08/1996 JLB : Uses map nearby cell scanning handler. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(CELL cell, bool set_home) +{ + assert(Houses.ID(this) == ID); + + bool rc; + bool clockwise; + + /* + ** Randomly decide if we're going to search cells clockwise or counter- + ** clockwise + */ + clockwise = Percent_Chance(50); + + /* + ** Only continue if this cell is a legal placement cell. + */ + if (Map.In_Radar(cell)) { + + /* + ** If the flag already exists, then it must be removed from the object + ** it is attached to. + */ + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the cell specified. If it can't be placed, then pick + ** a nearby cell where it can be placed. + */ + CELL newcell = cell; + rc = Map[newcell].Flag_Place(Class->House); + if (!rc) { + newcell = Map.Nearby_Location(cell, SPEED_TRACK, -1, MZONE_NORMAL, true); + if (newcell != 0) { + rc = Map[newcell].Flag_Place(Class->House); + } + +#ifdef OBSOLETE + /* + ** Loop for increasing distance from the desired cell. + ** For each distance, randomly pick a starting direction. Between + ** this and the clockwise/counterclockwise random value, the flag + ** should appear to be placed fairly randomly. + */ + for (int dist = 1; dist < 32; dist++) { + FacingType fcounter; + FacingType rot; + + /* + ** Clockwise search. + */ + if (clockwise) { + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot++; + if (rot > FACING_NW) rot = FACING_N; + } + } else { + + /* + ** Counter-clockwise search + */ + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot--; + if (rot < FACING_N) + rot = FACING_NW; + } + } + } +#endif + } + + /* + ** If we've found a spot for the flag, place the flag at the new cell. + ** if 'set_home' is set, OR this house has no current flag home cell, + ** mark that cell as this house's flag home cell. Otherwise fall back + ** on returning the flag to its home. + */ + if (rc) { + FlagLocation = As_Target(newcell); + + if (set_home || FlagHome == 0) { + Map[newcell].Overlay = OVERLAY_FLAG_SPOT; + Map[newcell].OverlayData = 0; + Map[newcell].Recalc_Attributes(); + FlagHome = newcell; + } + } + else if (FlagHome != 0) { + rc = Map[FlagHome].Flag_Place(Class->House); + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * * + * This routine will attach the house flag to the specified unit. This routine is called * + * when a unit drives over a cell containing a flag. * + * * + * INPUT: object -- Pointer to the object that the house flag is to be attached to. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag attached successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) +{ + assert(Houses.ID(this) == ID); + + if (object && !object->IsInLimbo) { + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the object. + */ + object->Flag_Attach(Class->House); + FlagLocation = object->As_Target(); + return(true); + } + return(false); +} + +extern void On_Defeated_Message(const char* message, float timeout_seconds); + +/*************************************************************************** + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/25/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::MPlayer_Defeated(void) +{ + assert(Houses.ID(this) == ID); + + char txt[80]; + int i,j; + unsigned char id; + HouseClass * hptr; + HouseClass * hptr2; + int num_alive; + int num_humans; + int all_allies; + + /* + ** Set the defeat flag for this house + */ + IsDefeated = true; + + /* + ** If this is a computer controlled house, then all computer controlled + ** houses become paranoid. + */ + if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) { + Computer_Paranoid(); + } + + /* + ** Remove this house's flag & flag home cell + */ + if (Special.IsCaptureTheFlag) { + if (FlagLocation) { + Flag_Remove(FlagLocation, true); + } else { + if (FlagHome != 0) { + Flag_Remove(FlagHome, true); + } + } + } + + /* + ** If this is me: + ** - Set MPlayerObiWan, so I can only send messages to all players, and + ** not just one (so I can't be obnoxiously omnipotent) + ** - Reveal the map + ** - Add my defeat message + */ + if (PlayerPtr == this) { + Session.ObiWan = 1; + Debug_Unshroud = true; + HidPage.Clear(); + Map.Flag_To_Redraw(true); + + /* + ** Pop up a message showing that I was defeated + */ + sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + if (Session.Type == GAME_NORMAL) { + Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + Map.Flag_To_Redraw(false); + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; + On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + } else { + + /* + ** If it wasn't me, find out who was defeated + */ + if (IsHuman) { + sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + + //Session.Messages.Add_Message(NULL, 0, txt, RemapColor, + // TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + RedrawOptionsMenu = true; + + int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; + On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); + Sound_Effect(VOC_INCOMING_MESSAGE); + } + } + + /* + ** Find out how many players are left alive. + */ + num_alive = 0; + num_humans = 0; + for (i = 0; i < Session.MaxPlayers; i++) { + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (hptr && !hptr->IsDefeated) { + if (hptr->IsHuman) { + num_humans++; + } + num_alive++; + } + } + + /* + ** If all the houses left alive are allied with each other, then in reality + ** there's only one player left: + */ + all_allies = 1; + for (i = 0; i < Session.MaxPlayers; i++) { + + /* + ** Get a pointer to this house + */ + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (!hptr || hptr->IsDefeated) + continue; + + /* + ** Loop through all houses; if there's one left alive that this house + ** isn't allied with, then all_allies will be false + */ + for (j = 0; j < Session.MaxPlayers; j++) { + hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); + if (!hptr2) { + continue; + } + + if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { + all_allies = 0; + break; + } + } + if (!all_allies) { + break; + } + } + + /* + ** If all houses left are allies, set 'num_alive' to 1; game over. + */ + if (all_allies) { + num_alive = 1; + } + + /* + ** If there's only one human player left or no humans left, the game is over: + ** - Determine whether this player wins or loses, based on the state of the + ** player's IsDefeated flag + ** - Find all players' indices in the Session.Score array + ** - Tally up scores for this game + */ + if (num_alive == 1 || num_humans == 0) { + if (PlayerPtr->IsDefeated) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + + /* + ** Add up the scores + */ + Tally_Score(); + + /* + ** Destroy all the IPX connections, since we have to go through the rest + ** of the Main_Loop() before we detect that the game is over, and we'll + ** end up waiting for frame sync packets from the other machines. + */ + if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) { + i = 0; + while (Ipx.Num_Connections() && (i++ < 1000) ) { + id = Ipx.Connection_ID(0); + Ipx.Delete_Connection(id); + } + Session.NumPlayers = 0; + } + +#if(TEN) + // + // Tell the TEN server who won + // + if (Session.Type == GAME_TEN) { + Send_TEN_Win_Packet(); + } +#endif // TEN + +#if(MPATH) + // + // Tell the MPATH server who won + // + if (Session.Type == GAME_MPATH) { + FILE *fp; + + fp = fopen("winner.txt","wt"); + if (fp) { + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + fprintf(fp,"%s\n",hptr->IniName); + } + } + fclose(fp); + } + } +#endif // MPATH + + } + + /* + ** Be sure our messages get displayed, even if we're about to exit. + */ + Map.Render(); +} + + +/*************************************************************************** + * HouseClass::Tally_Score -- Fills in the score system for this round * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::Tally_Score(void) +{ + HousesType house; + HousesType house2; + HouseClass * hptr; + int score_index; + int i,j,k; + int max_index; + int max_count; + int count; + + /* + ** Loop through all houses, tallying up each player's score + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + hptr = HouseClass::As_Pointer(house); + /* + ** Skip this house if it's not human. + */ + if (!hptr || !hptr->IsHuman) { + continue; + } + /* + ** Now find out where this player is in the score array + */ + score_index = -1; + for (i = 0; i < Session.NumScores; i++) { + if (!stricmp(hptr->IniName, Session.Score[i].Name)) { + score_index = i; + break; + } + } + + /* + ** If the index is still -1, the name wasn't found; add a new entry. + */ + if (score_index == -1) { + /* + ** Just add this player to the end of the array, if there's room + */ + if (Session.NumScores < MAX_MULTI_NAMES) { + score_index = Session.NumScores; + Session.NumScores++; + } + /* + ** If there's not room, we have to remove somebody. + ** For each player in the scores array, count the # of '-1' entries + ** from this game backwards; the one with the most is the one that + ** hasn't played the longest; replace him with this new guy. + */ + else { + max_index = 0; + max_count = 0; + for (j = 0; j < Session.NumScores; j++) { + count = 0; + for (k = Session.NumScores - 1; k >= 0; k--) { + if (Session.Score[j].Kills[k]==-1) { + count++; + } + else { + break; + } + } + if (count > max_count) { + max_count = count; + max_index = j; + } + } + score_index = max_index; + } + + /* + ** Initialize this new score entry + */ + Session.Score[score_index].Wins = 0; + strcpy (Session.Score[score_index].Name, hptr->IniName); + for (j = 0; j < MAX_MULTI_GAMES; j++) + Session.Score[score_index].Kills[j] = -1; + } + + /* + ** Init this player's Kills to 0 (-1 means he didn't play this round; + ** 0 means he played but got no kills). + */ + Session.Score[score_index].Kills[Session.CurGame] = 0; + + /* + ** Init this player's color to his last-used color index + */ + Session.Score[score_index].Color = hptr->RemapColor; + + /* + ** If this house was undefeated, it must have been the winner. + ** (If no human houses are undefeated, the computer won.) + */ + if (!hptr->IsDefeated) { + Session.Score[score_index].Wins++; + Session.Winner = score_index; + } + + /* + ** Tally up all kills for this player + */ + for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) { + Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2]; + Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2]; + } + } +} + + +/*************************************************************************** + * HouseClass::Blowup_All -- blows up everything * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + * 05/07/1996 JLB : Handles ships. * + *=========================================================================*/ +void HouseClass::Blowup_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + int damage; + UnitClass * uptr; + InfantryClass * iptr; + BuildingClass * bptr; + int count; + WarheadType warhead; + + /* + ** Find everything owned by this house & blast it with a huge amount of damage + ** at zero range. Do units before infantry, so the units' drivers are killed + ** too. Using Explosion_Damage is like dropping a big bomb right on the + ** object; it will also damage anything around it. + */ + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { + uptr = ::Units.Ptr(i); + + /* + ** Some units can't be killed with one shot, so keep damaging them until + ** they're gone. The unit will destroy itself, and put an infantry in + ** its place. When the unit destroys itself, decrement 'i' since + ** its pointer will be removed from the active pointer list. + */ + count = 0; + while (::Units.Ptr(i)==uptr && uptr->Strength) { + damage = uptr->Strength; + uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5 && uptr->IsActive) { + delete uptr; + break; + } + } + i--; + } + } + + /* + ** Destroy all aircraft owned by this house. + */ + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { + AircraftClass * aptr = ::Aircraft.Ptr(i); + + damage = aptr->Strength; + aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!aptr->IsActive) { + i--; + } + } + } + + /* + ** Destroy all vessels owned by this house. + */ + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) { + VesselClass * vptr = ::Vessels.Ptr(i); + + damage = vptr->Strength; + vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!vptr->IsActive) { + i--; + } + } + } + + /* + ** Buildings don't delete themselves when they die; they shake the screen + ** and begin a countdown, so don't decrement 'i' when it's destroyed. + */ + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { + bptr = Buildings.Ptr(i); + + count = 0; + while (Buildings.Ptr(i)==bptr && bptr->Strength) { + damage = bptr->Strength; + bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5) { + delete bptr; + break; + } + } + } + } + + /* + ** Infantry don't delete themselves when they die; they go into a death- + ** animation sequence, so there's no need to decrement 'i' when they die. + ** Infantry should die by different types of warheads, so their death + ** anims aren't all synchronized. + */ + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { + iptr = Infantry.Ptr(i); + + count = 0; + while (Infantry.Ptr(i)==iptr && iptr->Strength) { + damage = iptr->Strength; + warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE); + iptr->Take_Damage(damage, 0, warhead, NULL, true); + + count++; + if (count > 5) { + delete iptr; + break; + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * * + * When this routine is called, the house will blow up after a period of time. Typically * + * this is called when the flag is captured or the HQ destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to blow up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Die(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToDie = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToDie); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * * + * When this routine is called, the house will be declared the winner after a period of * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to win? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Win(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToWin = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToWin); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * * + * When this routine is called, it will spell the doom of this house. In a short while * + * all of the object owned by this house will explode. Typical use of this routine is when * + * the flag has been captured or the command vehicle has been destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Has the doom been initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Lose(void) +{ + assert(Houses.ID(this) == ID); + + IsToWin = false; + if (!IsToDie && !IsToLose) { + IsToLose = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToLose); +} + + +/*********************************************************************************************** + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * * + * This routine is called when initializing the color and remap data for this house. The * + * primary user of this routine is the multiplayer version of the game, especially for * + * saving & loading multiplayer games. * + * * + * INPUT: color -- The color of this house. * + * * + * house -- The house that this should act like. * + * * + * credits -- The initial credits to assign to this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +extern bool NowSavingGame; // TEMP MBL: Need to discuss better solution with Steve +void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) +{ + assert(Houses.ID(this) == ID); + + Credits = Control.InitialCredits = credits; + VisibleCredits.Current = Credits; + RemapColor = color; + ActLike = house; + + // MBL 03.20.2020 + // Attempt to fix Red Alert credit tick-up bug after saving a game that has had harvesting underway + // Note that this code gets called with both game loads and saves + // When this function is called, sometimes credits value has Tiberium (or HarvestedCredits?) variables applied, and sometimes now + // + if (NowSavingGame == true) + { + // At this point VisibleCredits.Current (set above) does not have harvested ore/tiberium applied, but VisibleCredits.Credits does + VisibleCredits.Current = VisibleCredits.Credits; + } +} + + +/*********************************************************************************************** + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * * + * Use this routine to fetch the current power output as a fixed point fraction. The * + * value 0x0100 is 100% power. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with power rating as a fixed pointer number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1995 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Power_Fraction(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power >= Drain || Drain == 0) return(1); + + if (Power) { + return(fixed(Power, Drain)); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * * + * This routine will try to sell the wall at the specified location. If there is a wall * + * present and it is owned by this house, then it can be sold. * + * * + * INPUT: cell -- The cell that wall selling is desired. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + * 11/02/1996 JLB : Checks unsellable bit for wall type. * + *=============================================================================================*/ +void HouseClass::Sell_Wall(CELL cell) +{ + assert(Houses.ID(this) == ID); + + if ((unsigned)cell > 0) { + OverlayType overlay = Map[cell].Overlay; + + if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { + OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); + + if (optr.IsWall) { + BuildingTypeClass const * btype = NULL; + switch (overlay) { + case OVERLAY_SANDBAG_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL); + break; + + case OVERLAY_CYCLONE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL); + break; + + case OVERLAY_BRICK_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL); + break; + + case OVERLAY_BARBWIRE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL); + break; + + case OVERLAY_WOOD_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL); + break; + + case OVERLAY_FENCE: + btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE); + break; + + default: + break; + } + if (btype != NULL && !btype->IsUnsellable) { + + if (PlayerPtr == this) { + Sound_Effect(VOC_CASHTURN); + } + + Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); + Map[cell].Overlay = OVERLAY_NONE; + Map[cell].OverlayData = 0; + Map[cell].Owner = HOUSE_NONE; + Map[cell].Wall_Update(); + Map[cell].Adjacent_Cell(FACING_N).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_W).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_S).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_E).Wall_Update(); + Map[cell].Recalc_Attributes(); + Map[cell].Redraw_Objects(); + Map.Radar_Pixel(cell); + Detach_This_From_All(::As_Target(cell), true); + + if (optr.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * * + * This routine is called when a construction yard needs to know what to build next. It * + * will either examine the prebuilt base list or try to figure out what to build next * + * based on the current game situation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building type class to build. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Building -- Finds a building of specified type. * + * * + * This routine is used to find a building of the specified type. This is particularly * + * useful for when some event requires a specific building instance. The nuclear missile * + * launch is a good example. * + * * + * INPUT: type -- The building type to scan for. * + * * + * zone -- The zone that the building must be located in. If no zone specific search * + * is desired, then pass ZONE_NONE. * + * * + * OUTPUT: Returns with a pointer to the building type requested. If there is no building * + * of the type requested, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + * 10/02/1995 JLB : Allows for zone specifics. * + *=============================================================================================*/ +BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Only scan if we KNOW there is at least one building of the type + ** requested. + */ + if (BQuantity[type] > 0) { + + /* + ** Search for a suitable launch site for this missile. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == type) { + if (zone == ZONE_NONE || Which_Zone(b) == zone) { + return(b); + } + } + } + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * * + * This routine is used to find a suitable building location for the building specified. * + * The auto base building logic uses this when building the base for the computer. * + * * + * INPUT: building -- Pointer to the building that needs to be placed down. * + * * + * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable * + * locations, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const +{ + assert(Houses.ID(this) == ID); + + int zonerating[ZONE_COUNT]; + struct { + int AntiAir; // Average air defense for the base. + int AntiArmor; // Average armor defense for the base. + int AntiInfantry; // Average infantry defense for the base. + } zoneinfo = {0,0,0}; + int antiair = building->Anti_Air(); + int antiarmor = building->Anti_Armor(); + int antiinfantry = building->Anti_Infantry(); + bool adj = true; + + /* + ** Never place combat buildings adjacent to each other. This is partly + ** because combat buildings don't have a bib and jamming will occur as well + ** as because spacing defensive buildings out will yield a better + ** defense. + */ + if (antiair || antiarmor || antiinfantry) { + adj = false; + } + + /* + ** Determine the average zone strengths for the base. This value is + ** used to determine what zones are considered under or over strength. + */ + ZoneType z; + for (z = ZONE_NORTH; z < ZONE_COUNT; z++) { + zoneinfo.AntiAir += ZoneInfo[z].AirDefense; + zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense; + zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense; + } + zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH; + + /* + ** Give each zone a rating for value. The higher the value the more desirable + ** to place the specified building in that zone. Factor the average value of + ** zone defense such that more weight is given to zones that are very under + ** defended. + */ + memset(&zonerating[0], '\0', sizeof(zonerating)); + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + int diff; + + diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiair, diff); + } + + diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiarmor, diff); + } + + diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiinfantry, diff); + } + } + + /* + ** Now that each zone has been given a desirability rating, find the zone + ** with the greatest value and try to place the building in that zone. + */ + ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST); + int largest = 0; + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + if (zonerating[z] > largest) { + zone = z; + largest = zonerating[z]; + } + } + + CELL zcell = Find_Cell_In_Zone(building, zone); + if (zcell) { + return(Cell_Coord(zcell)); + } + + /* + ** Could not build in preferred zone, so try building in any zone. + */ + static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST}; + int start = Random_Pick(0, ARRAY_SIZE(_zones)-1); + for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) { + ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)]; + zcell = Find_Cell_In_Zone(building, tryzone); + if (zcell) return(zcell); + } + + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * * + * This routine will average the location of the base and record the center point. The * + * recorded center point is used to determine such things as how far the base is spread * + * out and where to protect the most. This routine should be called whenever a building * + * is created or destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/28/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Center(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** First presume that there is no base. If there is a base, then these values will be + ** properly filled in below. + */ + Center = 0; + Radius = 0; + for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { + ZoneInfo[zone].AirDefense = 0; + ZoneInfo[zone].ArmorDefense = 0; + ZoneInfo[zone].InfantryDefense = 0; + } + + /* + ** Only process the center base size/position calculation if there are buildings to + ** consider. When no buildings for this house are present, then no processing need + ** occur. + */ + if (CurBuildings > 0) { + int x = 0; + int y = 0; + int count = 0; + int index; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + + /* + ** Give more "weight" to buildings that cost more. The presumption is that cheap + ** buildings don't affect the base disposition as much as the more expensive + ** buildings do. + */ + int weight = (b->Class->Cost_Of() / 1000)+1; + for (int i = 0; i < weight; i++) { + x += Coord_X(b->Center_Coord()); + y += Coord_Y(b->Center_Coord()); + count++; + } + } + } + + /* + ** This second check for quantity of buildings is necessary because the first + ** check against CurBuildings doesn't take into account if the building is in + ** limbo, but for base calculation, the limbo state disqualifies a building + ** from being processed. Thus, CurBuildings may indicate a base, but count may + ** not match. + */ + if (count > 0) { + x /= count; + y /= count; + +#ifdef NEVER + /* + ** Bias the center of the base away from the edges of the map. + */ + LEPTON left = Cell_To_Lepton(Map.MapCellX + 10); + LEPTON top = Cell_To_Lepton(Map.MapCellY + 10); + LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10); + LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10); + if (x < left) x = left; + if (x > right) x = right; + if (y < top) y = top; + if (y > bottom) y = bottom; +#endif + + Center = XY_Coord(x, y); + } + + /* + ** If there were any buildings discovered as legal to consider as part of the base, + ** then figure out the general average radius of the building disposition as it + ** relates to the center of the base. + */ + if (count > 1) { + int radius = 0; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + radius += Distance(Center, b->Center_Coord()); + } + } + Radius = max(radius / count, 2 * CELL_LEPTON_W); + + /* + ** Determine the relative strength of each base defense zone. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + ZoneType z = Which_Zone(b); + + if (z != ZONE_NONE) { + ZoneInfo[z].ArmorDefense += b->Anti_Armor(); + ZoneInfo[z].AirDefense += b->Anti_Air(); + ZoneInfo[z].InfantryDefense += b->Anti_Infantry(); + } + } + } + + } else { + Radius = 0x0200; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Expert_AI -- Handles expert AI processing. * + * * + * This routine is called when the computer should perform expert AI processing. This * + * method of AI is categorized as an "Expert System" process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This is relatively time consuming -- call periodically. * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Expert_AI(void) +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = 0; + bool stop = false; + int time = TICKS_PER_SECOND * 10; + + /* + ** If the current enemy no longer has a base or is defeated, then don't consider + ** that house a threat anymore. Clear out the enemy record and then try + ** to find a new enemy. + */ + if (Enemy != HOUSE_NONE) { + HouseClass * h = HouseClass::As_Pointer(Enemy); + + if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) { + Enemy = HOUSE_NONE; + } + } + + /* + ** If there is no enemy assigned to this house, then assign one now. The + ** enemy that is closest is picked. However, don't pick an enemy if the + ** base has not been established yet. + */ + if (ActiveBScan && Center && Attack == 0) { + int close = 0; + HousesType enemy = HOUSE_NONE; + int maxunit = 0; + int maxinfantry = 0; + int maxvessel = 0; + int maxaircraft = 0; + int maxbuilding = 0; + int enemycount = 0; + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) { + + /* + ** Perform a special restriction check to ensure that no enemy is chosen if + ** there is even one enemy that has not established a base yet. This will + ** ensure an accurate first pick for enemy since the distance to base + ** value can be determined. + */ + if (!h->IsStarted) { + enemy = HOUSE_NONE; + break; + } + + /* + ** Keep track of the number of buildings and units owned by the + ** enemy. This is used to bring up the maximum allowed to match. + */ + maxunit += h->CurUnits; + maxbuilding += h->CurBuildings; + maxinfantry += h->CurInfantry; + maxvessel += h->CurVessels; + maxaircraft += h->CurAircraft; + enemycount++; + + /* + ** Determine a priority value based on distance to the center of the + ** candidate base. The higher the value, the better the candidate house + ** is to becoming the preferred enemy for this house. + */ + int value = ((MAP_CELL_W*2)-Distance(Center, h->Center)); + value *= 2; + + /* + ** In addition to distance, record the number of kills directed + ** against this house. The enemy that does more damage might be + ** considered a greater threat. + */ + value += h->BuildingsKilled[Class->House]*5; + value += h->UnitsKilled[Class->House]; + + /* + ** Factor in the relative sizes of the bases. An enemy that has a + ** larger base will be considered a bigger threat. Conversely, a + ** smaller base is considered a lesser threat. + */ + value += h->CurUnits - CurUnits; + value += h->CurBuildings - CurBuildings; + value += (h->CurInfantry - CurInfantry)/4; + + /* + ** Whoever last attacked is given a little more priority as + ** a potential designated enemy. + */ + if (house == LAEnemy) { + value += 100; + } + +#ifdef OBSOLETE + /* + ** Human players are a given preference as the target. + */ + if (h->IsHuman) { + value *= 2; + } +#endif + + /* + ** Compare the calculated value for this candidate house and if it is + ** greater than the previously recorded maximum, record this house as + ** the prime candidate for enemy. + */ + if (value > close) { + enemy = house; + close = value; + } + } + } + + /* + ** Record this closest enemy base as the first enemy to attack. + */ + Enemy = enemy; + + /* + ** Up the maximum allowed units and buildings to match a rough average + ** of what the enemies are allowed. + */ + if (enemycount) { + maxunit /= enemycount; + maxbuilding /= enemycount; + maxinfantry /= enemycount; + maxvessel /= enemycount; + maxaircraft /= enemycount; + } + + if (Control.MaxBuilding < (unsigned)maxbuilding + 10) { + Control.MaxBuilding = maxbuilding + 10; + } + if (Control.MaxUnit < (unsigned)maxunit + 10) { + Control.MaxUnit = maxunit + 10; + } + if (Control.MaxInfantry < (unsigned)maxinfantry + 10) { + Control.MaxInfantry = maxinfantry + 10; + } + if (Control.MaxVessel < (unsigned)maxvessel + 10) { + Control.MaxVessel = maxvessel + 10; + } + if (Control.MaxAircraft < (unsigned)maxaircraft + 10) { + Control.MaxAircraft = maxaircraft + 10; + } + } + + /* + ** House state transition check occurs here. Transitions that occur here are ones + ** that relate to general base condition rather than specific combat events. + ** Typically, this is limited to transitions between normal buildup mode and + ** broke mode. + */ + if (State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } else { + if (State == STATE_BUILDUP) { + if (Available_Money() < 25) { + State = STATE_BROKE; + } + } + if (State == STATE_BROKE) { + if (Available_Money() >= 25) { + State = STATE_BUILDUP; + } + } + if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) { + State = STATE_BUILDUP; + } + if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) { + State = STATE_ATTACKED; + } + } + + /* + ** Records the urgency of all actions possible. + */ + UrgencyType urgency[STRATEGY_COUNT]; + StrategyType strat; + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + urgency[strat] = URGENCY_NONE; + + switch (strat) { + case STRATEGY_BUILD_POWER: + urgency[strat] = Check_Build_Power(); + break; + + case STRATEGY_BUILD_DEFENSE: + urgency[strat] = Check_Build_Defense(); + break; + + case STRATEGY_BUILD_INCOME: + urgency[strat] = Check_Build_Income(); + break; + + case STRATEGY_FIRE_SALE: + urgency[strat] = Check_Fire_Sale(); + break; + + case STRATEGY_BUILD_ENGINEER: + urgency[strat] = Check_Build_Engineer(); + break; + + case STRATEGY_BUILD_OFFENSE: + urgency[strat] = Check_Build_Offense(); + break; + + case STRATEGY_RAISE_MONEY: + urgency[strat] = Check_Raise_Money(); + break; + + case STRATEGY_RAISE_POWER: + urgency[strat] = Check_Raise_Power(); + break; + + case STRATEGY_LOWER_POWER: + urgency[strat] = Check_Lower_Power(); + break; + + case STRATEGY_ATTACK: + urgency[strat] = Check_Attack(); + break; + + default: + urgency[strat] = URGENCY_NONE; + break; + } + } + + /* + ** Performs the action required for each of the strategies that share + ** the most urgent category. Stop processing if any strategy at the + ** highest urgency performed any action. This is because higher urgency + ** actions tend to greatly affect the lower urgency actions. + */ + for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) { + bool acted = false; + + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + if (urgency[strat] == u) { + switch (strat) { + case STRATEGY_BUILD_POWER: + acted |= AI_Build_Power(u); + break; + + case STRATEGY_BUILD_DEFENSE: + acted |= AI_Build_Defense(u); + break; + + case STRATEGY_BUILD_INCOME: + acted |= AI_Build_Income(u); + break; + + case STRATEGY_FIRE_SALE: + acted |= AI_Fire_Sale(u); + break; + + case STRATEGY_BUILD_ENGINEER: + acted |= AI_Build_Engineer(u); + break; + + case STRATEGY_BUILD_OFFENSE: + acted |= AI_Build_Offense(u); + break; + + case STRATEGY_RAISE_MONEY: + acted |= AI_Raise_Money(u); + break; + + case STRATEGY_RAISE_POWER: + acted |= AI_Raise_Power(u); + break; + + case STRATEGY_LOWER_POWER: + acted |= AI_Lower_Power(u); + break; + + case STRATEGY_ATTACK: + acted |= AI_Attack(u); + break; + + default: + break; + } + } + } + } + + return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2)); +} + + +UrgencyType HouseClass::Check_Build_Power(void) const +{ + assert(Houses.ID(this) == ID); + + fixed frac = Power_Fraction(); + UrgencyType urgency = URGENCY_NONE; + + if (frac < 1 && Can_Make_Money()) { + urgency = URGENCY_LOW; + + /* + ** Very low power condition is considered a higher priority. + */ + if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM; + + /* + ** When under attack and there is a need for power in defense, + ** then consider power building a higher priority. + */ + if (State == STATE_THREATENED || State == STATE_ATTACKED) { + if (BScan | (STRUCTF_CHRONOSPHERE)) { + urgency = URGENCY_HIGH; + } + } + + } + return(urgency); +} + + +UrgencyType HouseClass::Check_Build_Defense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that base defense + ** should be given. The more vulnerable the base is, the higher + ** the urgency this routine should return. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Offense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that offensive + ** weaponry should be given. Surplus money or a very strong + ** defense will cause the offensive urgency to increase. + */ + return(URGENCY_NONE); +} + +/* +** Determines what the attack state of the base is. The higher the state, +** the greater the immediate threat to base defense is. +*/ +UrgencyType HouseClass::Check_Attack(void) const +{ + assert(Houses.ID(this) == ID); + + if (Frame > TICKS_PER_MINUTE && Attack == 0) { + if (State == STATE_ATTACKED) { + return(URGENCY_LOW); + } + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Income(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should determine if income processing buildings + ** should be constructed and at what urgency. The lower the money, + ** the lower the refineries, or recent harvester losses should + ** cause a greater urgency to be returned. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Fire_Sale(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** If there are no more factories at all, then sell everything off because the game + ** is basically over at this point. + */ + if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Engineer(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should check to see what urgency that the production of + ** engineers should be. If a friendly building has been captured or the + ** enemy has weak defenses, then building an engineer would be a priority. + */ + return(URGENCY_NONE); +} + + +/* +** Checks to see if money is critically low and something must be done +** to immediately raise cash. +*/ +UrgencyType HouseClass::Check_Raise_Money(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + if (Available_Money() < 100) { + urgency = URGENCY_LOW; + } + if (Available_Money() < 2000 && !Can_Make_Money()) { + urgency++; + } + + return(urgency); +} + +/* +** Checks to see if power is very low and if so, a greater urgency to +** build more power is returned. +*/ +UrgencyType HouseClass::Check_Lower_Power(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power > Drain+300) { + return(URGENCY_LOW); + } + return(URGENCY_NONE); +} + +/* +** This routine determines if there is a power emergency. Such an +** emergency might require selling of structures in order to free +** up power. This might occur if the base is being attacked and there +** are defenses that require power, but are just short of having +** enough. +*/ +UrgencyType HouseClass::Check_Raise_Power(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + + if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) { +// if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) { + urgency = URGENCY_MEDIUM; + if (State == STATE_ATTACKED) { + urgency++; + } + } + return(urgency); +} + + +bool HouseClass::AI_Attack(UrgencyType ) +{ + assert(Houses.ID(this) == ID); + + bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33)); + bool forced = (CurBuildings == 0); + int index; + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * a = Aircraft.Ptr(index); + + if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) { + if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + a->Assign_Mission(MISSION_HUNT); + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * u = Units.Ptr(index); + + if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) { + if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + u->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this unit is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) { + u->ArchiveTarget = ::As_Target(Where_To_Go(u)); + } + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * i = Infantry.Ptr(index); + + if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) { + if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) { + i->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this soldier is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) { + i->ArchiveTarget = ::As_Target(Where_To_Go(i)); + } + } + } + } + Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + return(true); +} + + +/* +** Given the specified urgency, build a power structure to meet +** this need. +*/ +bool HouseClass::AI_Build_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +/* +** Given the specified urgency, build base defensive structures +** according to need and according to existing base disposition. +*/ +bool HouseClass::AI_Build_Defense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build offensive units according +** to need and according to the opponents base defenses. +*/ +bool HouseClass::AI_Build_Offense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build income producing +** structures according to need. +*/ +bool HouseClass::AI_Build_Income(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +bool HouseClass::AI_Fire_Sale(UrgencyType urgency) +{ + assert(Houses.ID(this) == ID); + + if (CurBuildings && urgency == URGENCY_CRITICAL) { + Fire_Sale(); + Do_All_To_Hunt(); + return(true); + } + return(false); +} + +/* +** Given the specified urgency, build an engineer. +*/ +bool HouseClass::AI_Build_Engineer(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, sell of some power since +** there appears to be excess. +*/ +bool HouseClass::AI_Lower_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = Find_Building(STRUCT_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + + b = Find_Building(STRUCT_ADVANCED_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * * + * This routine is called when the computer needs to raise power by selling off buildings. * + * Usually this occurs because of some catastrophe that has lowered power levels to * + * the danger zone. * + * * + * INPUT: urgency -- The urgency that the power needs to be raised. This controls what * + * buildings will be sold. * + * * + * OUTPUT: bool; Was a building sold to raise power? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Power(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_TESLA, URGENCY_HIGH} + }; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + BuildingClass * b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * * + * This routine handles the situation where the computer desperately needs cash but cannot * + * wait for normal harvesting to raise it. Buildings must be sold. * + * * + * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, * + * the more important the buildings that can be sold become. * + * * + * OUTPUT: bool; Was a building sold to raise cash? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Money(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_STORAGE,URGENCY_LOW}, + {STRUCT_REPAIR,URGENCY_LOW}, + {STRUCT_TESLA,URGENCY_MEDIUM}, + {STRUCT_HELIPAD,URGENCY_MEDIUM}, + {STRUCT_POWER,URGENCY_HIGH}, + {STRUCT_AIRSTRIP,URGENCY_HIGH}, +// {STRUCT_WEAP,URGENCY_HIGH}, +// {STRUCT_BARRACKS,URGENCY_HIGH}, +// {STRUCT_TENT,URGENCY_HIGH}, + {STRUCT_CONST,URGENCY_CRITICAL} + }; + BuildingClass * b = 0; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +#ifdef NEVER + +/*********************************************************************************************** + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * * + * This logic is used to maintain a base defense. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Base_Defense(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** Check to find if any zone of the base is over defended. Such zones should have + ** some of their defenses sold off to make better use of the money. + */ + + /* + ** Make sure that the core defense is only about 1/2 of the perimeter defense average. + */ + int average = 0; + for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { + average += ZoneInfo[z].AirDefense; + average += ZoneInfo[z].ArmorDefense; + average += ZoneInfo[z].InfantryDefense; + } + average /= (ZONE_COUNT-ZONE_NORTH); + + /* + ** If the core value is greater than the average, then sell off some of the + ** inner defensive structures. + */ + int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense; + if (core >= average) { + static StructType _stype[] = { + STRUCT_GTOWER, + STRUCT_TURRET, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_TESLA, + STRUCT_SAM + }; + BuildingClass * b; + + for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) { + b = Find_Building(_stype[index], ZONE_CORE); + if (b) { + b->Sell_Back(1); + break; + } + } + } + + /* + ** If the enemy doesn't have any offensive air capability, then sell off any + ** SAM sites. Only do this when money is moderately low. + */ + if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) { + + /* + ** Scan to find if ANY human opponents have aircraft or a helipad. If one + ** is found then consider that opponent to have a valid air threat potential. + ** Don't sell off SAM sites in that case. + */ + bool nothreat = true; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * house = HouseClass::As_Pointer(h); + + if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) { + if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) { + nothreat = false; + break; + } + } + } + } + + return(TICKS_PER_SECOND*5); +} +#endif + + +/*********************************************************************************************** + * HouseClass::AI_Building -- Determines what building to build. * + * * + * This routine handles the general case of determining what building to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + * 11/03/1996 JLB : Tries to match aircraft of enemy * + *=============================================================================================*/ +int HouseClass::AI_Building(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL && Base.House == Class->House) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + BuildStructure = node->Type; + } + } + + if (IsBaseBuilding) { + /* + ** Don't suggest anything to build if the base is already big enough. + */ + unsigned int quant = 0; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass const * hptr = HouseClass::As_Pointer(h); + + if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) { + quant = hptr->CurBuildings; + } + } + quant += Rule.BaseSizeAdd; + +// TCTC -- Should multiply largest player base by some rational number. +// if (CurBuildings >= quant) return(TICKS_PER_SECOND); + + BuildChoice.Free_All(); + BuildChoiceClass * choiceptr; + StructType stype = STRUCT_NONE; + int money = Available_Money(); + int level = Control.TechLevel; + bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0); + BuildingTypeClass const * b = NULL; + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + level = Control.TechLevel; + + /* + ** Try to build a power plant if there is insufficient power and there is enough + ** money available. + */ + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a refinery if there isn't one already available. + */ + unsigned int current = BQuantity[STRUCT_REFINERY]; + if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY); + if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always make sure there is a barracks available, but only if there + ** will be sufficient money to train troopers. + */ + current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT]; + if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TENT); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** Try to build one dog house. + */ + current = BQuantity[STRUCT_KENNEL]; + if (current < 1 && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Try to build one gap generator. + */ + current = BQuantity[STRUCT_GAP]; + if (current < 1 && Power_Fraction() >= 1 && hasincome) { + b = &BuildingTypeClass::As_Reference(STRUCT_GAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** A source of combat vehicles is always needed, but only if there will + ** be sufficient money to build vehicles. + */ + current = BQuantity[STRUCT_WEAP]; + if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_WEAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always build up some base defense. + */ + current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET]; + if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + if (Percent_Chance(50)) { + b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Build some air defense. + */ + current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN]; + if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) { + + /* + ** Building air defense only makes sense if the opponent has aircraft + ** of some kind. + */ + bool airthreat = false; + int threat_quantity = 0; + if (enemy != NULL && enemy->AScan != 0) { + airthreat = true; + threat_quantity = enemy->CurAircraft; + } + if (!airthreat) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && !Is_Ally(house) && h->AScan != 0) { + airthreat = true; + break; + } + } + } + + if (airthreat) { + + if (BQuantity[STRUCT_RADAR] == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_RADAR); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type); + } + } + } + + b = &BuildingTypeClass::As_Reference(STRUCT_SAM); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Advanced base defense would be good. + */ + current = BQuantity[STRUCT_TESLA]; + if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_TESLA); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a tech center as soon as possible. + */ + current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH]; + if (current < 1) { + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** A helipad would be good. + */ + current = BQuantity[STRUCT_HELIPAD]; + if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** An airstrip would be good. + */ + current = BQuantity[STRUCT_AIRSTRIP]; + if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + +#ifdef OLD + /* + ** Build a repair bay if there isn't one already available. + */ + current = BQuantity[STRUCT_REPAIR]; + if (current == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR); + if (Can_Build(b, ActLike) && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } +#endif + + /* + ** Pick the choice that is the most urgent. + */ + UrgencyType best = URGENCY_NONE; + int bestindex; + for (int index = 0; index < BuildChoice.Count(); index++) { + if (BuildChoice.Ptr(index)->Urgency > best) { + bestindex = index; + best = BuildChoice.Ptr(index)->Urgency; + } + } + if (best != URGENCY_NONE) { + BuildStructure = BuildChoice.Ptr(bestindex)->Structure; + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Unit -- Determines what unit to build next. * + * * + * This routine handles the general case of determining what units to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of games frames to delay before calling this routine again.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Unit(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND); + if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND); + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. + */ + if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) { + if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) { + BuildUnit = UNIT_HARVESTER; + return(TICKS_PER_SECOND); + } + } + + if (Session.Type == GAME_NORMAL) { + + int counter[UNIT_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)memtype)->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)memtype)->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those objects that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildUnit = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + + int counter[UNIT_COUNT]; + int total = 0; + UnitType index; + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index); + if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) { + if (utype->PrimaryWeapon != NULL) { + counter[index] = 20; + } else { + counter[index] = 1; + } + } else { + counter[index] = 0; + } + total += counter[index]; + } + + if (total > 0) { + int choice = Random_Pick(0, total-1); + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (choice < counter[index]) { + BuildUnit = index; + break; + } + choice -= counter[index]; + } + } + } + + return(TICKS_PER_SECOND); +} + + +int HouseClass::AI_Vessel(void) +{ + assert(Houses.ID(this) == ID); + if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND); + + if (CurVessels >= Control.MaxVessel) { + return(TICKS_PER_SECOND); + } + + if (Session.Type == GAME_NORMAL) { + + int counter[VESSEL_COUNT]; + if (Session.Type == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int vindex = 0; vindex < Vessels.Count(); vindex++) { + VesselClass * unit = Vessels.Ptr(vindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + VesselType bestlist[VESSEL_COUNT]; + for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildVessel = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + BuildVessel = VESSEL_NONE; + } + + return(TICKS_PER_SECOND); +} + + + +/*********************************************************************************************** + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * * + * This routine handles the general case of determining what infantry unit to build * + * next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before being called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Infantry(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND); + if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL) { + TechnoTypeClass const * techno = 0; + int counter[INFANTRY_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0); + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type; +// counter[subtype] = 1; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = min(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + + if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) { + if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + int pick = Random_Pick(0, bestcount-1); + BuildInfantry = bestlist[pick]; + } + + } + + if (IsBaseBuilding) { + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + /* + ** This structure is used to keep track of the list of infantry types that should be + ** built. The infantry type and the value assigned to it is recorded. + */ + struct { + InfantryType Type; // Infantry type. + int Value; // Relative value assigned. + } typetrack[INFANTRY_COUNT]; + int count = 0; + int total = 0; + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + typetrack[count].Value = 0; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility. + int clipindex = index; + if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT; + if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#else + if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#endif + + switch (index) { + case INFANTRY_E1: + typetrack[count].Value = 3; + break; + + case INFANTRY_E2: + typetrack[count].Value = 5; + break; + + case INFANTRY_E3: + typetrack[count].Value = 2; + break; + + case INFANTRY_E4: + typetrack[count].Value = 5; + break; + + case INFANTRY_RENOVATOR: + if (CurInfantry > 5) { + typetrack[count].Value = 1 - max(IQuantity[index], 0); + } + break; + + case INFANTRY_TANYA: + typetrack[count].Value = 1 - max(IQuantity[index], 0); + break; + + default: + typetrack[count].Value = 0; + break; + } + } + + if (typetrack[count].Value > 0) { + typetrack[count].Type = index; + total += typetrack[count].Value; + count++; + } + } + } + + /* + ** If there is at least one choice, then pick it. The object picked + ** is influenced by the weight (value) assigned to it. This is accomplished + ** by picking a number between 0 and the total weight value. The appropriate + ** infantry object that matches the number picked is then selected to be built. + */ + if (count > 0) { + int pick = Random_Pick(0, total-1); + for (int index = 0; index < count; index++) { + if (pick < typetrack[index].Value) { + BuildInfantry = typetrack[index].Type; + break; + } + pick -= typetrack[index].Value; + } + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * * + * This routine is used to determine the general case of what aircraft to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frame to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Aircraft(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsHuman && IQ >= Rule.IQAircraft) { + if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND); + if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND); + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_LONGBOW; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_HIND; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_MIG; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_YAK; + return(TICKS_PER_SECOND); + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::Production_Begun -- Records that production has begun. * + * * + * This routine is used to inform the Expert System that production of the specified object * + * has begun. This allows the AI to proceed with picking another object to begin production * + * on. * + * * + * INPUT: product -- Pointer to the object that production has just begun on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Production_Begun(TechnoClass const * product) +{ + assert(Houses.ID(this) == ID); + + if (product != NULL) { + switch (product->What_Am_I()) { + case RTTI_UNIT: + if (*((UnitClass*)product) == BuildUnit) { + BuildUnit = UNIT_NONE; + } + break; + + case RTTI_VESSEL: + if (*((VesselClass*)product) == BuildVessel) { + BuildVessel = VESSEL_NONE; + } + break; + + case RTTI_INFANTRY: + if (*((InfantryClass*)product) == BuildInfantry) { + BuildInfantry = INFANTRY_NONE; + } + break; + + case RTTI_BUILDING: + if (*((BuildingClass*)product) == BuildStructure) { + BuildStructure = STRUCT_NONE; + } + break; + + case RTTI_AIRCRAFT: + if (*((AircraftClass*)product) == BuildAircraft) { + BuildAircraft = AIRCRAFT_NONE; + } + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * * + * This routine informs the Expert System that the specified object is no longer part of * + * this house's inventory. This occurs when the object is destroyed or captured. * + * * + * INPUT: techno -- Pointer to the object to remove from the tracking systems of this * + * house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Remove(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + int type; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings--; + BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_AIRCRAFT: + CurAircraft--; + AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_INFANTRY: + CurInfantry--; + if (!((InfantryClass *)techno)->IsTechnician) { + type = ((InfantryTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT; +#endif + IQuantity[type]--; + } + break; + + case RTTI_UNIT: + CurUnits--; + type = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT; +#endif + UQuantity[type]--; + break; + + case RTTI_VESSEL: + CurVessels--; + type = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT; +#endif + VQuantity[type]--; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * * + * This function is called when the specified object is now available as part of the house's* + * inventory. This occurs when the object is newly produced and also when it is captured * + * by this house. * + * * + * INPUT: techno -- Pointer to the object that is now part of the house inventory. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Add(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + StructType building; + AircraftType aircraft; + InfantryType infantry; + UnitType unit; + VesselType vessel; + int quant; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings++; + building = ((BuildingTypeClass const &)techno->Class_Of()).Type; + BQuantity[building]++; + BScan |= (1L << building); + if (Session.Type == GAME_INTERNET) { + BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_AIRCRAFT: + CurAircraft++; + aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type; + AQuantity[aircraft]++; + AScan |= (1L << aircraft); + if (Session.Type == GAME_INTERNET) { + AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_INFANTRY: + CurInfantry++; + infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type; + if (!((InfantryClass *)techno)->IsTechnician) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = infantry; + if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT; + IQuantity[quant]++; +#else + IQuantity[infantry]++; +#endif + if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) { + InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + IScan |= (1L << infantry); + } + break; + + case RTTI_UNIT: + CurUnits++; + unit = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = unit; + if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT; + UQuantity[quant]++; +#else + UQuantity[unit]++; +#endif + UScan |= (1L << unit); + if (Session.Type == GAME_INTERNET) { + UnitTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_VESSEL: + CurVessels++; + vessel = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = vessel; + if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT; + VQuantity[quant]++; +#else + VQuantity[vessel]++; +#endif + VScan |= (1L << vessel); + if (Session.Type == GAME_INTERNET) { + VesselTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * * + * Use this routine to fetch a pointer to the variable that holds the number of factories * + * that can produce the specified object type. This is a helper routine used when * + * examining the number of factories as well as adjusting their number. * + * * + * INPUT: rtti -- The RTTI of the object that could be produced. * + * * + * OUTPUT: Returns with the number of factories owned by this house that could produce the * + * object of the type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int * HouseClass::Factory_Counter(RTTIType rtti) +{ + switch (rtti) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitFactories); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselFactories); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftFactories); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryFactories); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingFactories); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * * + * This routine will recognize the specified object as having been removed from active * + * duty. * + * * + * INPUT: techno -- Pointer to the object to remove from active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Remove(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr - 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * * + * This routine will recognize the specified object as having entered active duty. Any * + * abilities granted to the house by that object are now available. * + * * + * INPUT: techno -- Pointer to the object that is entering active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Add(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr + 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * * + * This routine will determine what zone the specified coordinate lies in with respect to * + * this house's base. A location that is too distant from the base, even though it might * + * be a building, is not considered part of the base and returns ZONE_NONE. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * OUTPUT: Returns with the base zone that the specified coordinate lies in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + if (coord == 0) return(ZONE_NONE); + + int distance = Distance(Center, coord); + if (distance <= Radius) return(ZONE_CORE); + if (distance > Radius*4) return(ZONE_NONE); + + DirType facing = Direction(Center, coord); + if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH); + if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST); + if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH); + return(ZONE_WEST); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * * + * Use this routine to determine what zone the specified object lies in. * + * * + * INPUT: object -- Pointer to the object that will be checked for zone occupation. * + * * + * OUTPUT: Returns with the base zone that the object lies in. For objects that are too * + * distant from the center of the base, ZONE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (!object) return(ZONE_NONE); + return(Which_Zone(object->Center_Coord())); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * * + * This routine is used to determine what base zone the specified cell is in. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far * + * away. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(CELL cell) const +{ + assert(Houses.ID(this) == ID); + + return(Which_Zone(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * * + * This routine will go through all game objects and reset the existence bits for the * + * owning house. This method ensures that if the object exists, then the corresponding * + * existence bit is also set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Attributes(void) +{ + /* + ** Clear out all tracking values that will be filled in by this + ** routine. This allows the filling in process to not worry about + ** old existing values. + */ + int index; + for (index = 0; index < Houses.Count(); index++) { + HouseClass * house = Houses.Ptr(index); + + if (house != NULL) { + house->BScan = 0; + house->ActiveBScan = 0; + house->IScan = 0; + house->ActiveIScan = 0; + house->UScan = 0; + house->ActiveUScan = 0; + house->AScan = 0; + house->ActiveAScan = 0; + house->VScan = 0; + house->ActiveVScan = 0; + } + } + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + unit->House->UScan |= (1L << unit->Class->Type); + if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + if (!unit->IsInLimbo) { + unit->House->ActiveUScan |= (1L << unit->Class->Type); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + infantry->House->IScan |= (1L << infantry->Class->Type); + if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + if (!infantry->IsInLimbo) { + infantry->House->ActiveIScan |= (1L << infantry->Class->Type); + infantry->House->OldIScan |= (1L << infantry->Class->Type); + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + aircraft->House->AScan |= (1L << aircraft->Class->Type); + if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + if (!aircraft->IsInLimbo) { + aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type); + aircraft->House->OldAScan |= (1L << aircraft->Class->Type); + } + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->Class->Type < 32) { + building->House->BScan |= (1L << building->Class->Type); + if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + if (!building->IsInLimbo) { + building->House->ActiveBScan |= (1L << building->Class->Type); + building->House->OldBScan |= (1L << building->Class->Type); + } + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + vessel->House->VScan |= (1L << vessel->Class->Type); + if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) { + if (!vessel->IsInLimbo) { + vessel->House->ActiveVScan |= (1L << vessel->Class->Type); + vessel->House->OldVScan |= (1L << vessel->Class->Type); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * * + * This routine is used to find the cell that is closest to the center point of the * + * zone specified. Typical use of this routine is for building and unit placement so that * + * they can "cover" the specified zone. * + * * + * INPUT: zone -- The zone that the center point is to be returned. * + * * + * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Zone_Cell(ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + switch (zone) { + case ZONE_CORE: + return(Coord_Cell(Center)); + + case ZONE_NORTH: + return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3))); + + case ZONE_EAST: + return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3))); + + case ZONE_WEST: + return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3))); + + case ZONE_SOUTH: + return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * * + * This function is called for every new unit produced or delivered in order to determine * + * where the unit should "hang out" to await further orders. The best area for the * + * unit to loiter is returned as a cell location. * + * * + * INPUT: object -- Pointer to the object that needs to know where to go. * + * * + * OUTPUT: Returns with the cell that the unit should move to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + * 11/04/1996 JLB : Simplified to use helper functions * + *=============================================================================================*/ +CELL HouseClass::Where_To_Go(FootClass const * object) const +{ + assert(Houses.ID(this) == ID); + assert(object != NULL); + + ZoneType zone; // The zone that the object should go to. + if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) { + zone = ZONE_CORE; + } else { + zone = Random_Pick(ZONE_NORTH, ZONE_WEST); + } + + CELL cell = Random_Cell_In_Zone(zone); + assert(cell != 0); + + return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL)); +} + + +/*********************************************************************************************** + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * * + * This routine is used to find targets out in the field and away from base defense. * + * Typical of this would be the attack helicopters and the roving attack bands of * + * hunter killers. * + * * + * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. * + * * + * OUTPUT: Returns with a suitable target to attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + UnitClass * best = 0; + int value = 0; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) { + int val = Distance(coord, unit->Center_Coord()); + + if (unit->Anti_Air()) val *= 2; + + if (*unit == UNIT_HARVESTER) val /= 2; + + if (value == 0 || val < value) { + value = val; + best = unit; + } + } + } + if (best) { + return(best->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * * + * Call this routine to fetch the total quantity of aircraft of the type specified that is * + * owned by this house. * + * * + * INPUT: aircraft -- The aircraft type to check the quantity of. * + * * + * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this * + * house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(AircraftType aircraft) +{ + return(AQuantity[aircraft]); +} + + +/*********************************************************************************************** + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * * + * This is the counterpart to the Set_Factory function. It will return with a factory * + * pointer that is associated with the object type specified. * + * * + * INPUT: rtti -- The RTTI of the object type to find the factory for. * + * * + * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the * + * object type specified. * + * * + * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy * + * producing another unit of that category. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const +{ + int factory_index = -1; + + switch (rtti) { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = InfantryFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = AircraftFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = VesselFactory; + break; + + default: + factory_index = -1; + break; + } + + /* + ** Fetch the actual pointer to the factory object. If there is + ** no object factory that matches the specified rtti type, then + ** null is returned. + */ + if (factory_index != -1) { + return(Factories.Raw_Ptr(factory_index)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * * + * Call this routine when a factory has been created and it now must be passed on to the * + * house for tracking purposes. The house maintains several factory pointers and this * + * routine will ensure that the factory pointer gets stored correctly. * + * * + * INPUT: rtti -- The RTTI of the object the factory it to manufacture. * + * * + * factory -- The factory object pointer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory) +{ + int * factory_index = 0; + + assert(rtti != RTTI_NONE); + + switch (rtti) { + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = &UnitFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = &InfantryFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = &VesselFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = &BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = &AircraftFactory; + break; + } + + assert(factory_index != NULL); + + /* + ** Assign the factory to the appropriate slot. For the case of clearing + ** the factory out, then -1 is assigned. + */ + if (factory != NULL) { + *factory_index = factory->ID; + } else { + *factory_index = -1; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * * + * This routine will count the number of factories owned by this house that can build * + * objects of the specified type. * + * * + * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. * + * * + * OUTPUT: Returns with the number of factories that can build the object type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Factory_Count(RTTIType rtti) const +{ + int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti); + if (ptr != NULL) { + return(*ptr); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * * + * This will return the total number of buildings of that type owned by this house. * + * * + * INPUT: building -- The building type to check. * + * * + * OUTPUT: Returns with the number of buildings of that type owned by this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(StructType building) +{ + return(BQuantity[building]); +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +void HouseClass::Read_INI(CCINIClass & ini) +{ + HouseClass * p; // Pointer to current player data. + char const * hname; // Pointer to house name. + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + + p = new HouseClass(index); + p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario); + p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding); + p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit); + p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry); + p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel); + if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit; + p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100; + p->Credits = p->Control.InitialCredits; + + int iq = ini.Get_Int(hname, "IQ", 0); + if (iq > Rule.MaxIQ) iq = 1; + p->IQ = p->Control.IQ = iq; + + p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH); + p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false); + + int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL)); + p->Make_Ally(index); + p->Make_Ally(HOUSE_NEUTRAL); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if ((owners & (1 << h)) != 0) { + p->Make_Ally(h); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * * + * This routine will write out all data necessary to recreate it in anticipation of a * + * new scenario. All houses (that are active) will have their scenario type data written * + * out. * + * * + * INPUT: ini -- Reference to the INI database to write the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Write_INI(CCINIClass & ini) +{ + /* + ** The identity house control object. Only if the house value differs from the + ** identity, will the data be written out. + */ + HouseStaticClass control; + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p != NULL) { + char const * name = p->Class->IniName; + + ini.Clear(name); + if (i >= HOUSE_MULTI1) continue; + + if (p->Control.InitialCredits != control.InitialCredits) { + ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100)); + } + + if (p->Control.Edge != control.Edge) { + ini.Put_SourceType(name, "Edge", p->Control.Edge); + } + + if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) { + ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit); + } + + if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) { + ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry); + } + + if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) { + ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding); + } + + if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) { + ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel); + } + + if (p->Control.TechLevel != control.TechLevel) { + ini.Put_Int(name, "TechLevel", p->Control.TechLevel); + } + + if (p->Control.IQ != control.IQ) { + ini.Put_Int(name, "IQ", p->Control.IQ); + } + + if (p->IsPlayerControl != false && p != PlayerPtr) { + ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl); + } + + ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL))); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * * + * This routine will examine the current yak and mig situation verses airfields. If there * + * are equal aircraft to airfields, then this routine will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_No_YakMig(void) const +{ + int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG]; + + /* + ** Adjust the quantity down one if there is an aircraft in production. This will + ** allow production to resume after being held. + */ + FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT); + if (factory != NULL && factory->Get_Object() != NULL) { + AircraftClass const * air = (AircraftClass const *)factory->Get_Object(); + if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) { + quantity -= 1; + } + } + + if (quantity >= BQuantity[STRUCT_AIRSTRIP]) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * * + * This is a special hack check routine to see if the object type and id specified is * + * prevented from being produced. The Yak and the Mig are so prevented if there would be * + * insufficient airfields for them to land upon. * + * * + * INPUT: rtti -- The RTTI type of the value specified. * + * * + * value -- The type number (according to the RTTI type specified). * + * * + * OUTPUT: bool; Is production of this object prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const +{ + if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) { + return(Is_No_YakMig()); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * * + * This routine will sell back all buildings owned by this house. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a fire sale performed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Fire_Sale(void) +{ + if (CurBuildings > 0) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) { + b->Sell_Back(1); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * * + * This routine will cause all combatants of this house to go into hunt mode. The effect of * + * this is to throw everything this house has to muster at the enemies of this house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 10/02/1996 JLB : Handles aircraft too. * + *=============================================================================================*/ +void HouseClass::Do_All_To_Hunt(void) const +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == this && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + + if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) { + if (vessel->Team) vessel->Team->Remove(vessel); + vessel->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) { + if (aircraft->Team) aircraft->Team->Remove(aircraft); + aircraft->Assign_Mission(MISSION_HUNT); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * * + * Use this routine to determine if this house is legally allowed to ally with the * + * house specified. There are many reason why an alliance is not allowed. Typically, this * + * is when there would be no more opponents left to fight or if this house has been * + * defeated. * + * * + * INPUT: house -- The house that alliance with is desired. * + * * + * OUTPUT: bool; Is alliance with the house specified prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Allowed_To_Ally(HousesType house) const +{ + /* + ** Is not allowed to ally with a house that is patently invalid, such + ** as one that is illegally defined. + */ + if (house == HOUSE_NONE) { + return(false); + } + + /* + ** One cannot ally twice with the same house. + */ + if (Is_Ally(house)) { + return(false); + } + + /* + ** If the scenario is being set up, then alliances are always + ** allowed. No further checking is required. + */ + if (ScenarioInit) { + return(true); + } + + /* + ** Alliances (outside of scneario init time) are allowed only if + ** this is a multiplayer game. Otherwise, they are prohibited. + */ + if (Session.Type == GAME_NORMAL) { + return(false); + } + + /* + ** When the house is defeated, it can no longer make alliances. + */ + if (IsDefeated) { + return(false); + } + +#ifdef FIXIT_VERSION_3 + // Fix to prevent ally with computer. + if ( !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#else // FIXIT_VERSION_3 +#ifdef FIXIT_NO_COMP_ALLY + // Fix to prevent ally with computer. + if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#endif +#endif // FIXIT_VERSION_3 + + /* + ** Count the number of active houses in the game as well as the + ** number of existing allies with this house. + */ + int housecount = 0; + int allycount = 0; + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr = HouseClass::As_Pointer(house2); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) { + housecount++; + if (Is_Ally(hptr)) { + allycount++; + } + } + } + + /* + ** Alliance is not allowed if there wouldn't be any enemies left to + ** fight. + */ + if (housecount == allycount+1) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * * + * This routine will cause the computer players to become suspicious of the human * + * players and thus the computer players will band together in order to defeat the * + * human players. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Computer_Paranoid(void) +{ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019 + + /* + ** Loop through every computer controlled house and make allies with all other computer + ** controlled houses and then make enemies with all other human controlled houses. + */ + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) { + hptr->IsParanoid = true; + + /* + ** Break alliance with every human it is allied with and make friends with + ** any other computer players. + */ + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr2 = HouseClass::As_Pointer(house2); + if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) { + if (hptr2->IsHuman) { + hptr->Make_Enemy(house2); + } else { + hptr->Make_Ally(house2); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * * + * This routine will update the power output value of the house. It will cause any buildgins* + * that need to be redrawn to do so. * + * * + * INPUT: adjust -- The amount to adjust the power output value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Power(int adjust) +{ + Power += adjust; + + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * * + * This routine will update the drain value of the house. It will cause any buildings that * + * need to be redraw to do so. * + * * + * INPUT: adjust -- The amount to adjust the drain (positive means more drain). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Drain(int adjust) +{ + Drain += adjust; + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/11/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Update_Spied_Power_Plants(void) +{ + int count = CurrentObject.Count(); + if (count) { + for (int index = 0; index < count; index++) { + ObjectClass const * tech = CurrentObject[index]; + if (tech && tech->What_Am_I()==RTTI_BUILDING) { + BuildingClass *bldg = (BuildingClass *)tech; + if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) { + if ( bldg->Spied_By() & (1<<(PlayerPtr->Class->House)) ) { + bldg->Mark(MARK_CHANGE); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * * + * Use this routine to determine where the specified object should go if it were to go * + * some random (but legal) location within the zone specified. * + * * + * INPUT: techno -- The object that is desirous of going into the zone specified. * + * * + * zone -- The zone to find a location within. * + * * + * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If * + * no valid location could be found, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + * 11/04/1996 JLB : Not so strict on zone requirement. * + *=============================================================================================*/ +CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const +{ + if (techno == NULL) return(0); + + int bestval = -1; + int bestcell = 0; + TechnoTypeClass const * ttype = techno->Techno_Type_Class(); + + /* + ** Pick a random location within the zone specified. + */ + CELL trycell = Random_Cell_In_Zone(zone); + + short const * list = NULL; + if (techno->What_Am_I() == RTTI_BUILDING) { + list = techno->Occupy_List(true); + } + + /* + ** Find a legal placement position as close as possible to the picked location while still + ** remaining within the zone. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { +// if (Map.In_Radar(cell)) { + if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) { + bool ok = ttype->Legal_Placement(cell); + + /* + ** Another (adjacency) check is required for buildings. + */ + if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) { + ok = false; + } + + if (ok) { + int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell)); + if (bestval == -1 || dist < bestval) { + bestval = dist; + bestcell = cell; + } + } + } + } + + /* + ** Return the best location to move to. + */ + return(bestcell); +} + + +/*********************************************************************************************** + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * * + * This routine will pick a random cell within the zone specified. The pick will be * + * clipped to the map edge when necessary. * + * * + * INPUT: zone -- The zone to pick a cell from. * + * * + * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the * + * map, then a cell in the core zone is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/04/1996 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const +{ + COORDINATE coord = 0; + int maxdist = 0; + switch (zone) { + case ZONE_CORE: + coord = Coord_Scatter(Center, Random_Pick(0, Radius), true); + break; + + case ZONE_NORTH: + maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_EAST: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_SOUTH: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_WEST: + maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + } + + /* + ** Double check that the location is valid and if so, convert it into a cell + ** number. + */ + CELL cell; + if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) { + if (zone == ZONE_CORE) { + + /* + ** Finding a cell within the core failed, so just pick the center + ** cell. This cell is guaranteed to be valid. + */ + cell = Coord_Cell(Center); + } else { + + /* + ** If the edge fails, then try to find a cell within the core. + */ + cell = Random_Cell_In_Zone(ZONE_CORE); + } + } else { + cell = Coord_Cell(coord); + } + + /* + ** If the randomly picked location is not in the legal map area, then clip it to + ** the legal map area. + */ + if (!Map.In_Radar(cell)) { + int x = Cell_X(cell); + int y = Cell_Y(cell); + + if (x < Map.MapCellX) x = Map.MapCellX; + if (y < Map.MapCellY) y = Map.MapCellY; + if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1; + if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1; + cell = XY_Cell(x, y); + } + return(cell); +} + +/*********************************************************************************************** + * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the bit field storing which houses this house is allied with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/12/2019 JAS : Created. * + *=============================================================================================*/ +unsigned HouseClass::Get_Ally_Flags() +{ + return Allies; +} + + + +/*********************************************************************************************** + * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/31/2020 3:34PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Check_Pertinent_Structures(void) +{ + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + ** + ** Game is over when no pertinent structures remain + */ + + if (!Special.IsEarlyWin) { + return; + } + + if (IsToDie || IsToWin || IsToLose) { + return; + } + + bool any_good_buildings = false; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *b = Buildings.Ptr(index); + + if (b && b->IsActive && b->House == this) { + if (!b->Class->IsWall) { + if (!b->IsInLimbo && b->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) { + if (!unit->IsInLimbo && unit->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + Flag_To_Die(); + } +} + + + + + +/*********************************************************************************************** + * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Init_Unit_Trackers(void) +{ + if (Session.Type == GAME_INTERNET || Session.Type == GAME_GLYPHX_MULTIPLAYER) { + AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); + InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); + UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); + BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); + VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT); + + DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); + DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); + DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); + DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT); + + CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + TotalCrates = new UnitTrackerClass ( CRATE_COUNT ); + } else { + + AircraftTotals = NULL; + InfantryTotals = NULL; + UnitTotals = NULL; + BuildingTotals = NULL; + VesselTotals = NULL; + + DestroyedAircraft = NULL; + DestroyedInfantry = NULL; + DestroyedUnits = NULL; + DestroyedBuildings = NULL; + DestroyedVessels = NULL; + + CapturedBuildings = NULL; + TotalCrates = NULL; + } +} + + + +/*********************************************************************************************** + * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Free_Unit_Trackers(void) +{ + if (AircraftTotals) { + delete AircraftTotals; + AircraftTotals = NULL; + } + + if (InfantryTotals) { + delete InfantryTotals; + InfantryTotals = NULL; + } + + if (UnitTotals) { + delete UnitTotals; + UnitTotals = NULL; + } + + if (BuildingTotals) { + delete BuildingTotals; + BuildingTotals = NULL; + } + + if (VesselTotals) { + delete VesselTotals; + VesselTotals = NULL; + } + + if (DestroyedAircraft) { + delete DestroyedAircraft; + DestroyedAircraft = NULL; + } + + if (DestroyedInfantry) { + delete DestroyedInfantry; + DestroyedInfantry = NULL; + } + + if (DestroyedUnits) { + delete DestroyedUnits; + DestroyedUnits = NULL; + } + + if (DestroyedBuildings) { + delete DestroyedBuildings; + DestroyedBuildings = NULL; + } + + if (DestroyedVessels) { + delete DestroyedVessels; + DestroyedVessels = NULL; + } + + if (CapturedBuildings) { + delete CapturedBuildings; + CapturedBuildings = NULL; + } + + if (TotalCrates) { + delete TotalCrates; + TotalCrates = NULL; + } +} \ No newline at end of file diff --git a/REDALERT/HOUSE.H b/REDALERT/HOUSE.H new file mode 100644 index 000000000..9ae63e1af --- /dev/null +++ b/REDALERT/HOUSE.H @@ -0,0 +1,957 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HOUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : May 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HOUSE_H +#define HOUSE_H + +#include "type.h" +#include "region.h" +#include "vector.h" +#include "Credits.h" + +class TriggerClass; +class FootClass; +class FactoryClass; + +#define HOUSE_NAME_MAX 12 + + +/**************************************************************************** +** Certain aspects of the house "country" are initially set by the scenario +** control file. This information is static for the duration of the current +** scenario, but is dynamic between scenarios. As such, it can't be placed in +** the static HouseTypeClass structure, but is embedded into the house +** class instead. +*/ +class HouseStaticClass { + public: + HouseStaticClass(void); + HouseStaticClass(NoInitClass const & ) {}; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value is zero is presumed for human controlled houses. + */ + int IQ; + + /* + ** This is the buildable tech level for this house. This value is used + ** for when the computer is deciding what objects to build. + */ + int TechLevel; + + /* + ** This is the original ally specification to use at scenario + ** start. Various forces during play may adjust the ally state + ** of this house. + */ + int Allies; + + /* + ** This is the maximum number allowed to be built by this house. The + ** value depends on the scenario being played. + */ + unsigned MaxUnit; + unsigned MaxBuilding; + unsigned MaxInfantry; + unsigned MaxVessel; + unsigned MaxAircraft; + + /* + ** This records the initial credits assigned to this house when the scenario + ** was loaded. + */ + long InitialCredits; + + /* + ** For generic (unspecified) reinforcements, they arrive by a common method. This + ** specifies which method is to be used. + */ + SourceType Edge; +}; + + +/**************************************************************************** +** Player control structure. Each player (computer or human) has one of +** these structures associated. These are located in a global array. +*/ +class HouseClass { + public: + RTTIType RTTI; + int ID; + + /* + ** Pointer to the HouseTypeClass that this house is "owned" by. + ** All constant data for a house type is stored in that class. + */ + CCPtr Class; + + /* + ** This is the handicap (difficulty level) assigned to this house. + */ + DiffType Difficulty; + + /* + ** Override handicap control values. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + fixed RepairDelay; + fixed BuildDelay; + + /* + ** The initial house data as loaded from the scenario control file is + ** stored here. Although this data changes for each scenario, it remains + ** static for the duration of the current scenario. + */ + HouseStaticClass Control; + + /* + ** This is the house type that this house object should act like. This + ** value controls production choices and radar cover plate imagery. + */ + HousesType ActLike; + + /* + ** Is this player active? Usually that answer is true, but for civilians, it + ** might possibly be false. + */ + unsigned IsActive:1; + + /* + ** If this house is controlled by the player, then this flag will be true. The + ** computer controls all other active houses. + */ + unsigned IsHuman:1; + unsigned WasHuman:1; + + /* + ** If the player can control units of this house even if the player doesn't + ** own units of this house, then this flag will be true. + */ + unsigned IsPlayerControl:1; + + /* + ** This flag enables production. If the flag is false, production is disabled. + ** By timing when this flag gets set, the player can be given some breathing room. + */ + unsigned IsStarted:1; + + /* + ** When alerted, the house will create teams of the special "auto" type and + ** will generate appropriate units to fill those team types. + */ + unsigned IsAlerted:1; + + /* + ** If automatic base building is on, then this flag will be set to true. + */ + unsigned IsBaseBuilding:1; + + /* + ** If the house has been discovered, then this flag will be set + ** to true. However, the trigger even associated with discovery + ** will only be executed during the next house AI process. + */ + unsigned IsDiscovered:1; + + /* + ** If Tiberium storage is maxed out, then this flag will be set. At some point + ** the player is told of this fact and then this flag is cleared. This allows the + ** player to be told, but only occasionally rather than continuously. + */ + unsigned IsMaxedOut:1; + + /* + ** If this house is played by a human in a multiplayer game, this flag + ** keeps track of whether this house has been defeated or not. + */ + unsigned IsDefeated:1; + + /* + ** These flags are used in conjunction with the BorrowedTime timer. When + ** that timer expires and one of these flags are set, then that event is + ** applied to the house. This allows a dramatic pause between the event + ** trigger and the result. + */ + unsigned IsToDie:1; + unsigned IsToWin:1; + unsigned IsToLose:1; + + /* + ** This flag is set when a transport carrying a civilian has been + ** successfully evacuated. It is presumed that a possible trigger + ** event will be sprung by this event. + */ + unsigned IsCivEvacuated:1; + + /* + ** If potentially something changed that might affect the sidebar list of + ** buildable objects, then this flag indicates that at the first LEGAL opportunity, + ** the sidebar will be recalculated. + */ + unsigned IsRecalcNeeded:1; + + /* + ** If the map has been completely revealed to the player, then this flag + ** will be set to true. By examining this flag, a second "reveal all map" + ** crate won't be given to the player. + */ + unsigned IsVisionary:1; + + /* + ** This flag is set to true when the house has determined that + ** there is insufficient Tiberium to keep the harvesters busy. + ** In such a case, the further refinery/harvester production + ** should cease. This is one of the first signs that the endgame + ** has begun. + */ + unsigned IsTiberiumShort:1; + + /* + ** These flags are used for the general house trigger events of being + ** spied and thieved. The appropriate flag will be set when the event + ** occurs. + */ + unsigned IsSpied:1; + unsigned IsThieved:1; + + /* + ** This flag is used to control non-human repairing of buildings. Each + ** house gets to repair one building per loop, and this flag controls + ** whether this house has 'spent' its repair option this time through. + */ + unsigned DidRepair:1; + + /* + ** This flag is used to control whether or not this house has the GPS + ** satellite in orbit. If the satellite's there, they have unlimited + ** radar and the map is fully revealed. + */ + unsigned IsGPSActive:1; + + /* + ** If the JustBuilt??? variable has changed, then this flag will + ** be set to true. + */ + unsigned IsBuiltSomething:1; + + /* + ** Did this house lose via resignation? + */ + unsigned IsResigner:1; + + /* + ** Did this house lose because the player quit? + */ + unsigned IsGiverUpper:1; + + /* + ** If this computer controlled house has reason to be mad at humans, + ** then this flag will be true. Such a condition prevents alliances with + ** a human and encourages the computers players to ally amongst themselves. + */ + unsigned IsParanoid:1; + + /* + ** A gap generator shrouded cells and all units of this house must perform + ** a look just in case their look radius intersects the shroud area. + */ + unsigned IsToLook:1; + + /* + ** MBL 03.23.2020 - Support for queued movement mode (informed from the client) + */ + unsigned IsQueuedMovementToggle:1; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value of zero indicates that the player controls everything. + */ + int IQ; + + /* + ** This records the current state of the base. This state is used to control + ** what action the base will perform and directly affects production and + ** unit disposition. The state will change according to time and combat + ** events. + */ + StateType State; + + /* + ** These super weapon control objects are used to control the recharge + ** and availability of these special weapons to this house. + */ + SuperClass SuperWeapon[SPC_COUNT]; + + /* + ** This is a record of the last building that was built. For buildings that + ** were built as a part of scenario creation, it will be the last one + ** discovered. + */ + StructType JustBuiltStructure; + InfantryType JustBuiltInfantry; + UnitType JustBuiltUnit; + AircraftType JustBuiltAircraft; + VesselType JustBuiltVessel; + + /* + ** This records the number of triggers associated with this house that are + ** blocking a win condition. A win will only occur if all the blocking + ** triggers have been deleted. + */ + int Blockage; + + /* + ** For computer controlled houses, there is an artificial delay between + ** performing repair actions. This timer regulates that delay. If the + ** timer has not expired, then no repair initiation is allowed. + */ + CDTimerClass RepairTimer; + + /* + ** This timer controls the computer auto-attack logic. When this timer expires + ** and the house has been alerted, then it will create a set of attack + ** teams. + */ + CDTimerClass AlertTime; + + /* + ** This timer is used to handle the delay between some catastrophic + ** event trigger and when it is actually carried out. + */ + CDTimerClass BorrowedTime; + + /* + ** This is the last working scan bits for buildings. If a building is + ** active and owned by this house, it will have a bit set in this element + ** that corresponds to the building type number. Since this value is + ** accumulated over time, the "New" element contains the under-construction + ** version. + */ + unsigned long BScan; + unsigned long ActiveBScan; + unsigned long OldBScan; + + /* + ** This is the last working scan bits for units. For every existing unit + ** type owned by this house, a corresponding bit is set in this element. As + ** the scan bits are being constructed, they are built into the "New" element + ** and then duplicated into the regular element at the end of every logic cycle. + */ + unsigned long UScan; + unsigned long ActiveUScan; + unsigned long OldUScan; + + /* + ** Infantry type existence bits. Similar to unit and building bits. + */ + unsigned long IScan; + unsigned long ActiveIScan; + unsigned long OldIScan; + + /* + ** Aircraft type existence bits. Similar to unit and building bits. + */ + unsigned long AScan; + unsigned long ActiveAScan; + unsigned long OldAScan; + + /* + ** Vessel type existence bits. Similar to unit and building bits. + */ + unsigned long VScan; + unsigned long ActiveVScan; + unsigned long OldVScan; + + /* + ** Record of gains and losses for this house during the course of the + ** scenario. + */ + unsigned CreditsSpent; + unsigned HarvestedCredits; + int StolenBuildingsCredits; + + /* + ** This is the running count of the number of units owned by this house. This + ** value is used to keep track of ownership limits. + */ + unsigned CurUnits; + unsigned CurBuildings; + unsigned CurInfantry; + unsigned CurVessels; + unsigned CurAircraft; + + /* + ** This is the running total of the number of credits this house has accumulated. + */ + long Tiberium; + long Credits; + long Capacity; + + /* + ** Stuff to keep track of the total number of units built by this house. + */ + UnitTrackerClass * AircraftTotals; + UnitTrackerClass * InfantryTotals; + UnitTrackerClass * UnitTotals; + UnitTrackerClass * BuildingTotals; + UnitTrackerClass * VesselTotals; + + /* + ** Total number of units destroyed by this house + */ + UnitTrackerClass * DestroyedAircraft; + UnitTrackerClass * DestroyedInfantry; + UnitTrackerClass * DestroyedUnits; + UnitTrackerClass * DestroyedBuildings; + UnitTrackerClass * DestroyedVessels; + + /* + ** Total number of enemy buildings captured by this house + */ + UnitTrackerClass * CapturedBuildings; + + /* + ** Total number of crates found by this house + */ + UnitTrackerClass * TotalCrates; + + /* + ** Records the number of infantry and vehicle factories active. This value is + ** used to regulate the speed of production. + */ + int AircraftFactories; + int InfantryFactories; + int UnitFactories; + int VesselFactories; + int BuildingFactories; + + /* + ** This is the accumulation of the total power and drain factors. From these + ** values a ratio can be derived. This ratio is used to control the rate + ** of building decay. + */ + int Power; // Current power output. + int Drain; // Power consumption. + + /* + ** For human controlled houses, only one type of unit can be produced + ** at any one instant. These factory objects control this production. + */ + int AircraftFactory; + int InfantryFactory; + int UnitFactory; + int VesselFactory; + int BuildingFactory; + + /* + ** For human controlled houses, the current state of the radar map + */ + RadarEnum Radar; + + /* + ** This target value specifies where the flag is located. It might be a cell + ** or it might be an object. + */ + TARGET FlagLocation; + + /* + ** This is the flag-home-cell for this house. This is where we must bring + ** another house's flag back to, to defeat that house. + */ + CELL FlagHome; + + /* + ** For multiplayer games, each house needs to keep track of how many + ** objects of each other house they've killed. + */ + unsigned UnitsKilled[HOUSE_COUNT]; + unsigned UnitsLost; + unsigned BuildingsKilled[HOUSE_COUNT]; + unsigned BuildingsLost; + + /* + ** This keeps track of the last house to destroy one of my units. + ** It's used for scoring multiplayer games. + */ + HousesType WhoLastHurtMe; + + /* + ** Start location (waypoint index) passed in from GlyphX + */ + int StartLocationOverride; + + /* + ** This records information about the location and size of + ** the base. + */ + COORDINATE Center; // Center of the base. + int Radius; // Average building distance from center (leptons). + struct { + int AirDefense; + int ArmorDefense; + int InfantryDefense; + } ZoneInfo[ZONE_COUNT]; + + /* + ** This records information about the last time a building of this + ** side was attacked. This information is used to determine proper + ** response. + */ + int LATime; // Time of attack. + RTTIType LAType; // Type of attacker. + ZoneType LAZone; // Last zone that was attacked. + HousesType LAEnemy; // Owner of attacker. + + /* + ** This target value is the building that must be captured as soon as possible. + ** Typically, this will be one of the buildings of this house that has been + ** captured and needs to be recaptured. + */ + TARGET ToCapture; + + /* + ** This value shows who is spying on this house's radar facilities. + ** This is used for the other side to be able to update their radar + ** map based on the cells that this house's units reveal. + */ + int RadarSpied; + + /* + ** Running score, based on units destroyed and units lost. + */ + int PointTotal; + + /* + ** This is the targeting directions for when this house gets a + ** special weapon. + */ + QuarryType PreferredTarget; + + /* + ** Screen shake timer. + */ + CDTimerClass ScreenShakeTime; + + private: + /* + ** Tracks number of each building type owned by this house. Even if the + ** building is in construction, it will be reflected in this total. + */ +#ifdef FIXIT_ANTS + int BQuantity[STRUCT_COUNT-3]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int UQuantity[UNIT_RA_COUNT-3]; +#else + int UQuantity[UNIT_COUNT-3]; +#endif +#else + int BQuantity[STRUCT_COUNT]; + int UQuantity[UNIT_COUNT]; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int IQuantity[INFANTRY_RA_COUNT]; +#else + int IQuantity[INFANTRY_COUNT]; +#endif + int AQuantity[AIRCRAFT_COUNT]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int VQuantity[VESSEL_RA_COUNT]; +#else + int VQuantity[VESSEL_COUNT]; +#endif + + /* + ** This timer keeps track of when an all out attack should be performed. + ** When this timer expires, send most of this house's units in an + ** attack. + */ + CDTimerClass Attack; + + public: + /* + ** This records the overriding enemy that the computer will try to + ** destroy. Typically, this is the last house to attack, but can be + ** influenced by nearness. + */ + HousesType Enemy; + + /* + ** The house expert system is regulated by this timer. Each computer controlled + ** house will process the Expert System AI at intermittent intervals. Not only will + ** this distribute the overhead more evenly, but will add variety to play. + */ + CDTimerClass AITimer; + + /* + ** For the moebius effect, this is a pointer to the unit that we + ** selected to teleport. Only one teleporter should be active per house. + */ + TARGET UnitToTeleport; + + /* + ** This elaborates the suggested objects to construct. When the specified object + ** is constructed, then this corresponding value will be reset to nill state. The + ** expert system decides what should be produced, and then records the + ** recommendation in these variables. + */ + StructType BuildStructure; + UnitType BuildUnit; + InfantryType BuildInfantry; + AircraftType BuildAircraft; + VesselType BuildVessel; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + HouseClass(HousesType house); + HouseClass(NoInitClass const & x) : Class(x), Control(x), AlertTime(x), BorrowedTime(x), Attack(x), AITimer(x), DamageTime(x), TeamTime(x), TriggerTime(x), SpeakAttackDelay(x), SpeakPowerDelay(x), SpeakMoneyDelay(x), SpeakMaxedDelay(x) {}; + ~HouseClass(void); + operator HousesType(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + CELL Random_Cell_In_Zone(ZoneType zone) const; + static void Computer_Paranoid(void); + bool Is_Allowed_To_Ally(HousesType house) const; + void Do_All_To_Hunt(void) const; + void Super_Weapon_Handler(void); + int * Factory_Counter(RTTIType rtti); + int Factory_Count(RTTIType rtti) const; + DiffType Assign_Handicap(DiffType handicap); + TARGET Find_Juicy_Target(COORDINATE coord) const; + void Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const; + CELL Where_To_Go(FootClass const * object) const; + CELL Zone_Cell(ZoneType zone) const; + ZoneType Which_Zone(COORDINATE coord) const; + ZoneType Which_Zone(ObjectClass const * object) const; + ZoneType Which_Zone(CELL cell) const; + CELL Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const; + ProdFailType Begin_Production(RTTIType type, int id); + ProdFailType Suspend_Production(RTTIType type); + ProdFailType Abandon_Production(RTTIType type); + bool Place_Object(RTTIType type, CELL cell); + bool Manual_Place(BuildingClass * builder, BuildingClass * object); + void Special_Weapon_AI(SpecialWeaponType id); + bool Place_Special_Blast(SpecialWeaponType id, CELL cell); + bool Flag_Attach(CELL cell, bool set_home = false); + bool Flag_Attach(UnitClass * object, bool set_home = false); + bool Flag_Remove(TARGET target, bool set_home = false); + void Init_Data(PlayerColorType color, HousesType house, int credits); + COORDINATE Find_Build_Location(BuildingClass * building) const; + BuildingClass * Find_Building(StructType type, ZoneType zone=ZONE_NONE) const; + char const * Name(void) const {return(Class->Name());} + + // Added so the ally flags could be sent to client machines - 09 / 12 / 2019 JAS + unsigned Get_Ally_Flags(); + + bool Fire_Sale(void); + bool Is_Hack_Prevented(RTTIType rtti, int value) const; + bool Is_No_YakMig(void) const; + int Expert_AI(void); + void Production_Begun(TechnoClass const * rtti); + void Sell_Wall(CELL cell); + bool Flag_To_Die(void); + bool Flag_To_Win(void); + bool Flag_To_Lose(void); + void Make_Ally(HousesType house); + void Make_Ally(ObjectClass * object) {if (object) Make_Ally(object->Owner());}; + void Make_Enemy(HousesType house); + void Make_Enemy(ObjectClass * object) {if (object) Make_Enemy(object->Owner());}; + bool Is_Ally(HousesType house) const; + bool Is_Ally(HouseClass const * house) const; + bool Is_Ally(ObjectClass const * object) const; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void); + bool Can_Build(RTTIType rtti, int type, HousesType house) const; + + // Factory controls. + FactoryClass * Fetch_Factory(RTTIType rtti) const; + void Set_Factory(RTTIType rtti, FactoryClass * factory); + + bool Can_Build(ObjectTypeClass const * type, HousesType house) const; + + int Get_Quantity(AircraftType aircraft); + int Get_Quantity(StructType building); + unsigned char const * Remap_Table(bool blushing=false, RemapType remap=REMAP_NORMAL) const; + + TechnoTypeClass const * Suggest_New_Object(RTTIType objectype, bool kennel=false) const; + BuildingTypeClass const * Suggest_New_Building(void) const; + void Recalc_Center(void); + bool Does_Enemy_Building_Exist(StructType) const; + void Harvested(unsigned tiberium); + void Stole(unsigned worth); + long Available_Money(void) const; + void Spend_Money(unsigned money); + void Refund_Money(unsigned money); + void Attacked(BuildingClass* source); + void Adjust_Power(int adjust); + void Adjust_Drain(int adjust); + void Update_Spied_Power_Plants(void); + int Adjust_Capacity(int adjust, bool inanger=false); + fixed Power_Fraction(void) const; + fixed Tiberium_Fraction(void) const; + void Begin_Production(void) {IsStarted = true;}; + TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + void Adjust_Threat(int region, int threat); + void Tracking_Remove(TechnoClass const * techno); + void Tracking_Add(TechnoClass const * techno); + void Active_Remove(TechnoClass const * techno); + void Active_Add(TechnoClass const * techno); + + UrgencyType Check_Attack(void) const; + UrgencyType Check_Build_Power(void) const; + UrgencyType Check_Build_Defense(void) const; + UrgencyType Check_Build_Offense(void) const; + UrgencyType Check_Build_Income(void) const; + UrgencyType Check_Fire_Sale(void) const; + UrgencyType Check_Build_Engineer(void) const; + UrgencyType Check_Raise_Money(void) const; + UrgencyType Check_Raise_Power(void) const; + UrgencyType Check_Lower_Power(void) const; + + bool AI_Attack(UrgencyType urgency); + bool AI_Build_Power(UrgencyType urgency) const; + bool AI_Build_Defense(UrgencyType urgency) const; + bool AI_Build_Offense(UrgencyType urgency) const; + bool AI_Build_Income(UrgencyType urgency) const; + bool AI_Fire_Sale(UrgencyType urgency); + bool AI_Build_Engineer(UrgencyType urgency) const; + bool AI_Raise_Money(UrgencyType urgency) const; + bool AI_Raise_Power(UrgencyType urgency) const; + bool AI_Lower_Power(UrgencyType urgency) const; + + bool Can_Make_Money(void) const { + return(Available_Money() > 300 || (BScan & STRUCTF_REFINERY)); + }; + + static void Init(void); + static void One_Time(void); + static HouseClass * As_Pointer(HousesType house); + static void Recalc_Attributes(void); + + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + */ + void Check_Pertinent_Structures(void); + + void Init_Unit_Trackers(void); + void Free_Unit_Trackers(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static void Read_Flag_INI(char *buffer); + static void Write_Flag_INI(char *buffer); + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Special house actions. + */ + void Detach(TARGET target, bool all); + + /* + ** This vector holds the recorded status of the map regions. It is through + ** this region information that team paths are calculated. + */ + RegionClass Regions[MAP_TOTAL_REGIONS]; + + /* + ** This count down timer class decrements and then changes + ** the Atomic Bomb state. + */ + CELL NukeDest; + + /* + ** Per-house credits class to track the visible credits state for each house. Redundant in the original game, but needed + ** to preserve the exact credits count behavior in the GlyphX client. ST - 10/16/2019 2:31PM + */ + CreditClass VisibleCredits; + + bool DebugUnlockBuildables; + + /* + ** This routine completely removes this house & all its objects from the game. + */ + void Clobber_All(void); + + /* + ** This routine blows up everything in this house. Fun! + */ + void Blowup_All(void); + + /* + ** This routine gets called in multiplayer games when every unit, building, + ** and infantry for a house is destroyed. + */ + void MPlayer_Defeated(void); + + /* + ** When the game's over, this routine assigns everyone their score. + */ + void Tally_Score(void); + + friend class MapEditClass; + + private: + void Silo_Redraw_Check(long oldtib, long oldcap); + int AI_Building(void); + int AI_Unit(void); + int AI_Vessel(void); + int AI_Infantry(void); + int AI_Aircraft(void); + + /* + ** This is a bit field record of all the other houses that are allies with + ** this house. It is presumed that any house that isn't an ally, is therefore + ** an enemy. A house is always considered allied with itself. + */ + unsigned Allies; + + /* + ** General low-power related damaged is doled out whenever this timer + ** expires. + */ + CDTimerClass DamageTime; + + /* + ** Team creation is done whenever this timer expires. + */ + CDTimerClass TeamTime; + + /* + ** This controls the rate that the trigger time logic is processed. + */ + CDTimerClass TriggerTime; + + /* + ** At various times, the computer may announce the player's condition. The following + ** variables are used as countdown timers so that these announcements are paced + ** far enough apart to reduce annoyance. + */ + CDTimerClass SpeakAttackDelay; + CDTimerClass SpeakPowerDelay; + CDTimerClass SpeakMoneyDelay; + CDTimerClass SpeakMaxedDelay; + + /* + ** This structure is used to record a build request as determined by + ** the house AI processing. Higher priority build requests take precidence. + */ + struct BuildChoiceClass { + static void * operator new(size_t, void * ptr) {return(ptr);}; + UrgencyType Urgency; // The urgency of the build request. + StructType Structure; // The type of building to produce. + + BuildChoiceClass(UrgencyType u, StructType s) : Urgency(u), Structure(s) {}; + BuildChoiceClass(NoInitClass const & ) {}; + int Save(Pipe &) const {return(true);}; + int Load(Straw &) {return(true);}; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + }; + + static TFixedIHeapClass BuildChoice; + + + /* + ** These values are for multiplay only. + */ + public: + /* + ** For multiplayer games, each house instance has a remap table; the table + ** in the HousesTypeClass isn't used. This variable is set to the remap + ** table for the color the player wants to play. + */ + PlayerColorType RemapColor; + + /* + ** This is the name ("handle") the player has chosen for himself. + */ + char IniName[HOUSE_NAME_MAX]; + +#ifdef WOLAPI_INTEGRATION + // For Internet games only, unchanging name of player when game began. + // This name does not get changed to "Computer" if computer takes over for player. + char InitialName[HOUSE_NAME_MAX]; +#endif + + int QuantityB(int index) {return(BQuantity[index]);} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int QuantityU(int index) { + if(index >= UNIT_RA_COUNT) index -= UNIT_RA_COUNT; + return(UQuantity[index]); + } + int QuantityI(int index) { + if(index >= INFANTRY_RA_COUNT) index -= INFANTRY_RA_COUNT; + return(IQuantity[index]); + } + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) { + if(index >= VESSEL_RA_COUNT) index -= VESSEL_RA_COUNT; + return(VQuantity[index]); + } +#else + int QuantityU(int index) {return(UQuantity[index]);} + int QuantityI(int index) {return(IQuantity[index]);} + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) {return(VQuantity[index]);} +#endif + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[256]; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/HSV.CPP b/REDALERT/HSV.CPP new file mode 100644 index 000000000..e0862a4d8 --- /dev/null +++ b/REDALERT/HSV.CPP @@ -0,0 +1,208 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HSV.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HSV.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * HSVClass::Set -- Set the palette for this color object. * + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "hsv.h" +#include "rgb.h" + +HSVClass const HSVClass::BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * * + * This routine will adjust the HSV color object toward the color of the specified HSV * + * object. Typical users of this would be palette morphing or fading routines. * + * * + * INPUT: ratio -- The ratio to move the HSV object toward the color specified. A value * + * of zero means no movement at all. A value of 255 means move completely * + * toward the specified color (changed completely). * + * * + * hsv -- A reference to the color that the current HSV object is to change * + * toward. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Adjust(int ratio, HSVClass const & hsv) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = hsv.Value_Component() - Value_Component(); + Value = Value_Component() + (value * ratio) / 256; + + int saturation = hsv.Saturation_Component() - Saturation_Component(); + Saturation = Saturation_Component() + (saturation * ratio) / 256; + + int hue = hsv.Hue_Component() - Hue_Component(); + Hue = Hue_Component() + (hue * ratio) / 256; +} + + +/*********************************************************************************************** + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * * + * This routine will determine a color difference between two HSV objects. The difference * + * has no particular meaning other that larger numbers meaning greater difference. * + * * + * INPUT: hsv -- The other HSV object to compare this HSV object to. * + * * + * OUTPUT: Returns with a relative distance (in arbitrary units) between this HSV object and * + * the HSV object supplied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +int HSVClass::Difference(HSVClass const & hsv) const +{ + int hue = (int)Hue - (int)hsv.Hue; + if (hue < 0) hue = -hue; + + int saturation = (int)Saturation - (int)hsv.Saturation; + if (saturation < 0) saturation = -saturation; + + int value = (int)Value - (int)hsv.Value; + if (value < 0) value = -value; + + return(hue*hue + saturation*saturation + value*value); +} + + +/*********************************************************************************************** + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * * + * This conversion operator will convert the HSV object into an RGB object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implied) of the RGBClass object that most closely * + * matches this HSVClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +HSVClass::operator RGBClass (void) const +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Temporary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + int hue = Hue_Component(); + int saturation = Saturation_Component(); + int value = Value_Component(); + int red, green, blue; + + + hue *= 6; + f = hue % 255; + + // Set up possible red, green and blue values. + values[1] = + values[2] = value; + + // + // The following lines of code change + // values[3] = (v * (255 - ( (s * f) / 255) )) / 255; + // values[4] = values[5] = (v * (255 - s)) / 255; + // values[6] = (v * (255 - (s * (255 - f)) / 255)) / 255; + // so that the are rounded divides. + // + + tmp = (saturation * f) / 255; + values[3] = (value * (255 - tmp)) / 255; + + values[4] = + values[5] = (value * (255 - saturation)) / 255; + + tmp = 255 - (saturation * (255 - f)) / 255; + values[6] = (value * tmp) / 255; + + + // This should not be rounded. + i = hue / 255; + + i += (i > 4) ? -4 : 2; + red = values[i]; + + i += (i > 4) ? -4 : 2; + blue = values[i]; + + i += (i > 4) ? -4 : 2; + green = values[i]; + + return(RGBClass(red, green, blue)); +} + + +/*********************************************************************************************** + * HSVClass::Set -- Set the palette for this color object. * + * * + * The palette will be set for this color object. Use this routine to set an arbitrary * + * color index with the HSVClass object. * + * * + * INPUT: color -- The color index to change. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Set(int color) const +{ + RGBClass rgb = *this; + rgb.Set(color); +} diff --git a/REDALERT/HSV.H b/REDALERT/HSV.H new file mode 100644 index 000000000..17c4ccc69 --- /dev/null +++ b/REDALERT/HSV.H @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HSV.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HSV.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef HSV_H +#define HSV_H + +class RGBClass; +class HSVClass; + +/* +** Each color entry is represented by this class. It holds the values for the color +** attributes. The values are recorded in a range from 0 to 255 with 255 being the +** maximum. +*/ +class HSVClass +{ + private: + static HSVClass const BlackColor; + + public: + HSVClass(void) : Hue(0), Saturation(0), Value(0) {}; + HSVClass(unsigned char hue, unsigned char saturation, unsigned char value) : + Hue(hue), + Saturation(saturation), + Value(value) + {}; + operator RGBClass (void) const; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, HSVClass const & hsv); + int Difference(HSVClass const & hsv) const; + int Hue_Component(void) const {return(Hue);}; + int Saturation_Component(void) const {return(Saturation);}; + int Value_Component(void) const {return(Value);}; + void Set(int color) const; + + private: + unsigned char Hue; + unsigned char Saturation; + unsigned char Value; +}; + +#endif diff --git a/REDALERT/ICONLIST.CPP b/REDALERT/ICONLIST.CPP new file mode 100644 index 000000000..f34eaf431 --- /dev/null +++ b/REDALERT/ICONLIST.CPP @@ -0,0 +1,907 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Iconlist.cpp - created by ajw 07/07/98 + +// IconListClass is ListClass plus the option to include an icon on each line entry, +// the option to have the class maintain its own copies of strings passed to it +// for display, and the option to limit the maximum number of these strings that are +// kept (entries are removed from the top when this maximum is reached). +// Also added: multiple item selection capability. Note that the old selection code +// runs as normal, but it simply not used when it comes time to display. +// Also added: if mem. allocation is being done by this, the ability to break new items +// into multiple lines of text is enabled. +// Also added: extra data can be invisibly stored with each item, if memory allocation is +// being done by this. +// Extra data included 3 item preceding icons, 1 fixed position icon, an extra string, +// an extra void pointer, and a color remapping value. + +#include "iconlist.h" +#include "dibapi.h" + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +//*********************************************************************************************** +IconListClass::IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, + bool bResponsibleForStringAlloc, int iSelectionType, int iMaxItemsSaved ) : + ListClass( id, x, y, w, h, flags, up, down ) +{ + // If bResponsibleForStringAlloc, COPIES of strings are stored in the list. Deletion is + // handled by this class. Icons are different - the caller is responsible for what's on + // the other end of the pointer. + bDoAlloc = bResponsibleForStringAlloc; + // iSelectionType = 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + if( iSelectionType < 0 || iSelectionType > 2 ) iSelectionType = 1; + iSelectType = iSelectionType; + // If iMaxItemsSaved is 0, there is no limit to the number of text lines. The list can grow forever. + // Otherwise items are deleted from the head of the list when the maximum is passed. + // iMaxItemsSaved only applies when bResponsibleForStringAlloc. + iMaxItems = iMaxItemsSaved; +} + +//*********************************************************************************************** +IconListClass::~IconListClass( void ) +{ + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } +} + +/*********************************************************************************************** + * IconListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * pIcon -- Pointer to the shape to add. + * IconKind -- Indicates what type of image pIcon points to. + * szExtraDataString -- Extra string data that gets copied and stored along with item. + * szExtraDataPtr -- Extra data that gets stored along with item. + * pColorRemap -- Points to a color remapping used when drawing the item. + * + * OUTPUT: Returns new item index. * + * WARNINGS: none * + * HISTORY: 07/07/1998 ajw : Created. * + *=============================================================================================*/ +int IconListClass::Add_Item(char const * text) +{ + return Add_Item( text, NULL, NULL, ICON_SHAPE ); +} + +int IconListClass::Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + if( text ) + { + if( bDoAlloc ) + { + int iRetVal; + + char* szText = new char[ strlen( text ) + 51 ]; // 50 extra chars added for line breaks later. + strcpy( szText, text ); + + int iWidthMax, iHeight; + // Stupid usage of globals for font stuff... + if( TextFlags == TPF_TYPE ) + { + void* pFontBefore = Set_Font( TypeFontPtr ); + DWORD FontXSpacingBefore = FontXSpacing; + FontXSpacing = -2; + + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + + Set_Font( pFontBefore ); + FontXSpacing = FontXSpacingBefore; // Just in case it matters... Doubt it. + } + else + { + // Currently never called. Test well if you use IconList with a font other than TPF_TYPE, + // as the character spacing globals get set weirdly, I've found. + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + } + + // Each break character causes a line to be added to list. + char szBreakchars[] = "\r\n\v\f"; + char* szToken; + char* szNextChar = szText; + szToken = strtok( szText, szBreakchars ); + while( szToken ) + { + while( szNextChar < szToken ) + { + // We expected szToken to begin at szNextChar. Since it doesn't, extra break + // characters must have been removed by strtok as they were adjacent. We want + // a line break for every break character, so add lines for each space that + // szNextChar is off by. + szNextChar++; + Add_Item_Detail( " ", szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + } + iRetVal = Add_Item_Detail( szToken, szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + + // Expect next token two chars after the end of this one. + szNextChar = szToken + strlen( szToken ) + 1; + + // Get next token. + szToken = strtok( NULL, szBreakchars ); + } + delete [] szText; + return iRetVal; // Last value returned by ListClass::Add_Item + } + else + { + // Add one item to list. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + return ListClass::Add_Item( text ); + } + } + else + { + // (no text for new item) + if( pIcon0 || pIcon1 || pIcon2 ) + { + // Note: Cannot add an entry without text unless string allocation is being handled by me. + // Otherwise, because we want the icon to show up, create a blank entry for the ListClass. + if( bDoAlloc ) + { + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create new string, essentially blank. + char* szText = new char[2]; + strcpy( szText, " " ); + return ListClass::Add_Item( szText ); + } + else + // Cannot add entry, as text is blank and ListClass::Add_Item will do nothing. + // The Icon we want will not show up. + return List.Count() - 1; + } + else + return ListClass::Add_Item( text ); + } +} + +//*********************************************************************************************** +int IconListClass::Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, + void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ) +{ + // Broken out of above function as it is repeated. + + // Add one item to list. + // Too many entries? + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create icon entry. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; // ajw - Question: repeat the icon for each entry? make it optional? + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraData; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + // Create text entry. + // Copy text to new string. + char* szTextBit = new char[ strlen( szToken ) + 1 ]; + strcpy( szTextBit, szToken ); + return ListClass::Add_Item( szTextBit ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text ) +{ + return Add_Item( Text_String(text), NULL, NULL, ICON_SHAPE ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + return Add_Item( Text_String(text), szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, + pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( char const * text ) +{ + if( text ) + Remove_Item( List.ID(text) ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( int index ) +{ + if( (unsigned)index < List.Count() ) + { + delete (IconList_ItemExtras*)ExtrasList[ index ]; + ExtrasList.Delete( index ); + if( bDoAlloc ) + // Delete alloc'ed string. + delete [] (char*)List[index]; + ListClass::Remove_Item( index ); + + // I should probably put this in ListClass:Remove_Item(), as it seems clearly to be + // missing, but I want to only affect my own new code, to not introduce possible bugs. + // Shift the selected index if appropriate... + if( SelectedIndex >= index ) + { + SelectedIndex--; + if( SelectedIndex < 0 ) + SelectedIndex = 0; + } + } +} + +/*********************************************************************************************** + * IconListClass::Draw_Entry -- Calls ListClass::Draw_Entry, then adds icon. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 07/07/1998 ajw: Created. * + *=============================================================================================*/ + +#define PREICONGAP 1 +#define ICONTEXTGAP 2 + +void IconListClass::Draw_Entry( int index, int x, int y, int width, int selected ) +{ + IconList_ItemExtras* pExtras = (IconList_ItemExtras*)ExtrasList[ index ]; + + int xText = x; + // ajw If I end up needing to use SHAPEs for icons, figure out shape width here and offset x. + bool bIconsPresent = false; + for( int iIcon = 0; iIcon != 3; iIcon++ ) + if( pExtras->pIcon[ iIcon ] && pExtras->IconKind[ iIcon ] == ICON_DIB ) + { + // Push text over to accommodate icon. + int iWidthIcon = PREICONGAP + DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + xText += iWidthIcon; + width -= iWidthIcon; + bIconsPresent = true; + } + if( bIconsPresent ) + { + xText += ICONTEXTGAP; + width -= ICONTEXTGAP; + } + + RemapControlType* pRemap = pExtras->pColorRemap; + if( !pRemap ) + { + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + // See local version of this same hack, below. + int TempTabs; + const int* TabsSave; + if( Tabs ) + { + TempTabs = *Tabs - ( xText - x ); + TabsSave = Tabs; + Tabs = &TempTabs; + } + switch( iSelectType ) + { + case 0: + // Don't draw any items selected (even if they are, really, in ListClass). + ListClass::Draw_Entry( index, xText, y, width, false ); + break; + case 1: + ListClass::Draw_Entry( index, xText, y, width, selected ); + break; + case 2: + // Ignore 'selected' parameter. We use our own records. + ListClass::Draw_Entry( index, xText, y, width, pExtras->bMultiSelected ); + break; + } + // Restore Tabs. + if( Tabs ) + Tabs = TabsSave; + } + else + { + // Use different color remapping. + // This is largely copied straight from ListClass::Draw_Entry()... + + TextPrintType flags = TextFlags; + + bool bShowSelected; + + switch( iSelectType ) + { + case 0: + bShowSelected = false; + break; + case 1: + bShowSelected = selected; + break; + case 2: + bShowSelected = pExtras->bMultiSelected; + break; + } + + if( bShowSelected ) + { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect( xText, y, xText + width - 1, y + LineHeight - 1, pRemap->Shadow ); + } + else + { + if (!(flags & TPF_USE_GRAD_PAL)) + { + flags = flags | TPF_MEDIUM_COLOR; + } + } + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + if( Tabs ) + { + int tab = *Tabs - ( xText - x ); + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, &tab ); + } + else + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, NULL ); + } + + // Draw fixed position icon. + if( pExtras->FixedIcon.pIcon ) + { + if( pExtras->FixedIcon.IconKind == ICON_SHAPE ) + CC_Draw_Shape( pExtras->FixedIcon.pIcon, 0, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + CC_Draw_DIB( (char*)pExtras->FixedIcon.pIcon, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, pExtras->FixedIcon.iWidth, WINDOW_MAIN ); + } + + // Draw variable position left-of-text icons. + for( iIcon = 0; iIcon != 3; iIcon++ ) + { + if( pExtras->pIcon[ iIcon ] ) + { + x += PREICONGAP; + if( pExtras->IconKind[ iIcon ] == ICON_SHAPE ) + CC_Draw_Shape( pExtras->pIcon[ iIcon ], 0, x, y, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + { + CC_Draw_DIB( (char*)pExtras->pIcon[ iIcon ], x, y, 9999, WINDOW_MAIN ); + x += DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + } + } + } +} + +//*********************************************************************************************** +int IconListClass::Action(unsigned flags, KeyNumType & key) +{ + // Overriding of function is for the sake of MultiSelecting only. + if( iSelectType == 2 ) + { + if( !( flags & LEFTRELEASE ) ) + { + if( !( flags & KEYBOARD ) ) + { + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + int iSelected = CurrentTopIndex + index; + iSelected = min( iSelected, List.Count() - 1 ); + if( iSelected >= 0 ) + ((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected = + !((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected; + } + } + } + return ListClass::Action( flags, key ); +} + +//*********************************************************************************************** +// * IconListClass::Show_Last_Item -- Scrolls listbox down to ensure that last entry is visible. +// ajw 07/09/98 +void IconListClass::Show_Last_Item() +{ + int iItemLast = List.Count() - 1; + if( iItemLast - LineCount + 1 != CurrentTopIndex ) + { + Flag_To_Redraw(); + Set_View_Index( iItemLast - LineCount + 1 ); + } +} + +//*********************************************************************************************** +bool IconListClass::bItemIsMultiSelected( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected; + else + return false; +} + +//*********************************************************************************************** +void IconListClass::MultiSelect( int index, bool bSelect ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected = bSelect; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_ExtraDataString( int index ) const +{ + // Returns const pointer to the hidden "extra data" string that can be associated with each item. + // This is NULL if no extra data was assigned. + if( index < ExtrasList.Count() && index > -1 ) + { + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szExtraData; + } + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataString( int index, const char* szNewString ) +{ + if( index < ExtrasList.Count() && index > -1 ) + { + IconList_ItemExtras* pItemExtra = (IconList_ItemExtras*)ExtrasList[ index ]; + if( pItemExtra->szExtraData ) + { + // Delete the existing string. + delete [] pItemExtra->szExtraData; + } + if( szNewString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szNewString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szNewString ); + } + else + pItemExtra->szExtraData = NULL; + } +} + +//*********************************************************************************************** +void* IconListClass::Get_Item_ExtraDataPtr( int index ) const +{ + // Returns the hidden "extra data" void pointer that can be associated with each item. + // This is NULL if no value was assigned. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataPtr( int index, void* pNewValue ) +{ + // Sets the hidden "extra data" void pointer that can be associated with each item. + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData = pNewValue; +} + +//*********************************************************************************************** +const IconList_ItemExtras* IconListClass::Get_ItemExtras( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return (IconList_ItemExtras*)ExtrasList[ index ]; + else + return NULL; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_Help( int index ) const +{ + // Returns pointer to the string allocated for tooltip help. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szHelp; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Clear() +{ + // Removes all items from list. + + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + ExtrasList.Clear(); + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } + + List.Clear(); + Remove_Scroll_Bar(); + CurrentTopIndex = 0; +} + +//*********************************************************************************************** +RemapControlType* IconListClass::Get_Item_Color( int index ) +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_Color( int index, RemapControlType* pColorRemap ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap = pColorRemap; +} + +//*********************************************************************************************** +int IconListClass::Find( const char* szItemToFind ) +{ + // Returns -1 if szItemToFind is not found as the text BEGINNING one of the list entries, else index of item. + // Compare is case-sensitive. + for( int i = 0; i < List.Count(); i++ ) + { + if( strncmp( List[ i ], szItemToFind, strlen( szItemToFind ) ) == 0 ) + return i; + } + return -1; +} + +//*********************************************************************************************** +int IconListClass::FindColor( RemapControlType* pColorRemap ) +{ + // Returns -1 if no items of specified color are found, else first index. Assumes colorptr == colorptr is a valid equality test. + for( int i = 0; i < List.Count(); i++ ) + { + if( Get_Item_Color( i ) == pColorRemap ) + return i; + } + return -1; +} + +//*********************************************************************************************** +bool IconListClass::Set_Item( unsigned int index, const char* szText ) +{ + // Resets the text string allocated for an item. + if( !bDoAlloc || index >= List.Count() ) + return false; + + // Delete alloc'ed string. + delete [] (char*)List[ index ]; + + // Copy text to new string. + char* szTextNew = new char[ strlen( szText ) + 1 ]; + strcpy( szTextNew, szText ); + + // Reassign List's ptr. + List[ index ] = szTextNew; + + return true; +} + +//*********************************************************************************************** +bool IconListClass::Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ) +{ + if( index >= List.Count() ) + return false; + + // Sets one of the left-aligned icons. + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pIcon[ iIconNumber ] = pIcon; + ( (IconList_ItemExtras*)ExtrasList[ index ] )->IconKind[ iIconNumber ] = IconKind; + return true; +} + +//*********************************************************************************************** +int IconListClass::GetRealWidth() // sigh +{ + if( IsScrollActive ) + return Width + ScrollGadget.Width; + return Width; +} + +//*********************************************************************************************** +void IconListClass::Resize( int x, int y, int w, int h ) +{ + Remove_Scroll_Bar(); // If there is one. + + X = x; + Y = y; + Width = w; + Height = h; + + Set_Position( x, y ); + + LineCount = (h-1) / LineHeight; + + + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + Flag_To_Redraw(); +} + +//*********************************************************************************************** +int IconListClass::IndexUnderMouse() +{ + // Returns index of line that mouse is currently over, or -1 for mouse not hitting valid index. + // Assumes that x position of mouse is already known to be over the iconlist. + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight + CurrentTopIndex; + if( index > List.Count() - 1 || index < 0 ) + return -1; + return index; +} + +//*********************************************************************************************** +int IconListClass::OffsetToIndex( int iIndex, int y ) +{ + // Finds the current offset of item iIndex from the current top view index, in pixels, and add it to y. + return y + ( iIndex - CurrentTopIndex ) * LineHeight; +} + +//*********************************************************************************************** +//*********************************************************************************************** +// * Format_Window_String_New +// Functions like Format_Window_String except it fixes an infinite loop bug that occurred when strings +// lacked suitable break points, eliminates the '@' as an escape character, and operates differently +// in that it leaves the original string along, writing results instead to a second string parameter, +// that is iExtraChars longer than the original string. This is all a big hack so that I can insert +// extra break characters when a break in a long single word has to be made. +// Hey - it's better than an infinite loop that forces you to reset your machine, as in the original code... + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + // While the current line is less then the max length... + *szReturn = *string; + linelen += Char_Pixel_Width( *string ); + while ( linelen < maxlinelen && *string != '\r' && *string != '\0' ) + { + *++szReturn = *++string; + linelen += Char_Pixel_Width( *string ); + } + + // if the line is too long... + if (linelen >= maxlinelen) + { + /* + ** Back up to an appropriate location to break. + */ + const char* stringOverEnd = string; + while( linelen > 0 && *string != ' ' && *string != '\r' && *string != '\0' ) + { + linelen -= Char_Pixel_Width(*string--); + } + if( linelen <= 0 ) + { + // We could not find a nice break point. + // Go back one char from over-the-end point and add in a break there. + string = stringOverEnd - 1; + if( iExtraChars > 0 ) + iExtraChars--; // One less to make use of later. + else + // We've used up all our extras characters. + // Put in a break below by wiping out a valid char here. + szReturn--; + } + else + { + // Back up szReturn to same location. + szReturn -= ( stringOverEnd - string ); + } + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *szReturn++ = '\r'; + string++; + } + } + return(lines); +} + +//*********************************************************************************************** +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ) +{ + // A very basic DIB drawing routine. No clipping. No edge of window overrun checking. + // If iWidth is too large, default width of dib is used. + // If iWidth is negative, dib isn't drawn. + if( pDIB && iWidth >= 0 ) + { + + int iWidthDIB = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + const char* pBits = FindDIBBits( pDIB ); + + int iSrcPitch = ( iWidthDIB + 3 ) & ~3; + + if( iWidth > iWidthDIB ) + iWidth = iWidthDIB; + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iDestPitch = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineDest = (char*)draw_window.Get_Offset() + xDest + ( yDest + iHeight - 1 ) * iDestPitch; + + const char* pLineSrc = pBits; + for( int y = 0; y != iHeight; y++ ) + { + char* pDest = pLineDest; + const char* pSrc = pLineSrc; + for( int x = 0; x != iWidth; x++ ) + { + *pDest++ = *pSrc++; + } + pLineDest -= iDestPitch; + pLineSrc += iSrcPitch; + } + draw_window.Unlock(); + } + } +// else +// debugprint( "CC_Draw_DIB bad case ------------ pDib %i, iWidth %i\n", pDIB, iWidth ); +} + + +#endif diff --git a/REDALERT/ICONLIST.H b/REDALERT/ICONLIST.H new file mode 100644 index 000000000..5f5b08141 --- /dev/null +++ b/REDALERT/ICONLIST.H @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * IconListClass -- Exactly like ListClass, but displays an icon as well + * (actually a 'shape' image), left-aligned, covering + * any text that happens to be there... + * Also, I've added the option of making this class + * responsible for the mem alloc. of the strings, and + * an automatic limiting of entries to a set maximum. + * * + * HISTORY: 07/07/1998 ajw : Created, largely in hack mode. * + *=========================================================================*/ + +#ifndef ICONLIST_H +#define ICONLIST_H + +#include "function.h" +#include "vector.h" + +enum ICONKIND +{ + ICON_SHAPE = 0, // pIcon points to a shape. + ICON_DIB // pIcon points to DIBitmap data. +}; + +struct FIXEDICON // For putting icons in list entries at a specific fixed offset. +{ + void* pIcon; + ICONKIND IconKind; + int xOffset; + int yOffset; + int iWidth; +}; + +struct IconList_ItemExtras +{ + IconList_ItemExtras() : bMultiSelected( false ), szHelp( NULL ), szExtraData( NULL ), pvExtraData( NULL ) + { + pIcon[0] = NULL; + pIcon[1] = NULL; + pIcon[2] = NULL; + } + virtual ~IconList_ItemExtras() + { + delete [] szHelp; + delete [] szExtraData; + } + + bool bMultiSelected; // True if selected when bMultiSelect is on. + void* pIcon[3]; // Icon that appears before an item. + ICONKIND IconKind[3]; // Specifies what kind of image data pIcon points to. + char* szHelp; // Tooltip help string that can be associated with item. Allocated and deleted here. + char* szExtraData; // Extra string that can be associated with item. Allocated and deleted here. + void* pvExtraData; // Hidden pointer that can be associated with item. + RemapControlType* pColorRemap; // Pointer to a color remap, or null for default colored text. + FIXEDICON FixedIcon; +}; + +class IconListClass : public ListClass +{ + public: + IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, bool bResponsibleForStringAlloc = FALSE, int iSelectionType = 1, int iMaxItemsSaved = 0 ); +// IconListClass( const IconListClass& list ); + virtual ~IconListClass( void ); + + virtual int Add_Item( char const * text ); + virtual int Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + + virtual int Add_Item( int text ); + virtual int Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + +// virtual int Add_Scroll_Bar(void); +// virtual void Bump(int up); +// virtual int Count(void) const {return List.Count();}; +// virtual int Current_Index(void) const; +// virtual char const * Current_Item(void) const; +// virtual int Draw_Me(int forced); +// virtual char const * Get_Item(int index) const; +// virtual int Step_Selected_Index(int forward); +// virtual void Flag_To_Redraw(void); + +// virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item( char const * text ); + virtual void Remove_Item( int ); +// virtual int Remove_Scroll_Bar(void); +// virtual void Set_Selected_Index(int index); +// virtual void Set_Selected_Index(char const * text); +// virtual void Set_Tabs(int const * tabs); +// virtual int Set_View_Index(int index); +// virtual void Step(int up); +// virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ +// virtual LinkClass & Add(LinkClass & object); +// virtual LinkClass & Add_Tail(LinkClass & object); +// virtual LinkClass & Add_Head(LinkClass & object); +// virtual GadgetClass * Remove(void); + + virtual void Show_Last_Item(); + virtual bool bItemIsMultiSelected( int index ) const; + virtual void MultiSelect( int index, bool bSelect ); + virtual const char* Get_Item_ExtraDataString( int index ) const; + virtual void Set_Item_ExtraDataString( int index, const char* szNewString ); + virtual void* Get_Item_ExtraDataPtr( int index ) const; + virtual void Set_Item_ExtraDataPtr( int index, void* pNewValue ); + const char* Get_Item_Help( int index ) const; + virtual RemapControlType* Get_Item_Color( int index ); + virtual void Set_Item_Color( int index, RemapControlType* pColorRemap ); + virtual const IconList_ItemExtras* Get_ItemExtras( int index ) const; + virtual void Clear(); + virtual int Get_View_Index() { return CurrentTopIndex; } + bool bScrollBeingDragged() + { + // Returns true if the scroll bar of the list is being dragged by the user. + return ( GadgetClass::StuckOn == &ScrollGadget ); + } + + virtual int Find( const char* szItemToFind ); + virtual int FindColor( RemapControlType* pColorRemap ); + + virtual bool Set_Item( unsigned int index, const char* szText ); + virtual bool Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ); + + virtual int GetRealWidth(); + virtual void Resize( int x, int y, int w, int h ); + virtual int IndexUnderMouse(); + virtual int OffsetToIndex( int iIndex, int y ); + + virtual int SetSelectType( int iSelectTypeNew ) + { + // Provided to enable horrible hacks, mainly involved with dealing with ListClass's inability + // to have no item selected... + int iSelectTypeOld = iSelectType; + iSelectType = iSelectTypeNew; + return iSelectTypeOld; + } + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry( int index, int x, int y, int width, int selected ); + + virtual int Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ); + + // The list of Icons. + //DynamicVectorClass IconList; + //DynamicVectorClass< IconList_ItemExtras* > ExtrasList; ajw: creates hellacious linking problems + DynamicVectorClass< void* > ExtrasList; + + bool bDoAlloc; // True if I am responsible for mem. allocation/deletion of strings. +// bool bMultiSelect; // True if we are using the multiple item selection feature. + int iSelectType; // 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + int iMaxItems; // Number of items to limit list to, if bDoAlloc is true. +}; + +#endif + +#endif diff --git a/REDALERT/IDATA.CPP b/REDALERT/IDATA.CPP new file mode 100644 index 000000000..2efc287ce --- /dev/null +++ b/REDALERT/IDATA.CPP @@ -0,0 +1,1530 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IDATA.CPP 3 3/16/97 10:16p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * InfantryTypeClass::Get_Cameo_Data -- Fetches the small cameo shape for sidebar strip. * + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry obj* + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + +static DoInfoStruct DogDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // NA + {8, 6, 6}, // DO_WALK + {104, 14,14}, // DO_FIRE_WEAPON + {0, 0, 0}, // DO_LIE_DOWN // NA + {56, 6, 6}, // DO_CRAWL + {0, 0, 0}, // DO_GET_UP + {104, 14,14}, // DO_FIRE_PRONE + {216, 18,0}, // DO_IDLE1 + {216, 18,0}, // DO_IDLE2 + {235, 7, 0}, // DO_GUN_DEATH + {242, 9, 0}, // DO_EXPLOSION_DEATH + {242, 9, 0}, // DO_EXPLOSION2_DEATH + {242, 9, 0}, // DO_GRENADE_DEATH + {251, 14,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {106, 12,14}, // DO_DOG_MAUL +}; + + +// +// For the virtual do controls, we are using the TD infantry asset animation frame numbers, take out the -94 frame offsets that were added in RA +// + +static DoInfoStruct E1DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1, 8}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 6, 8}, // DO_FIRE_PRONE + {256, 16,0}, // DO_IDLE1 + {272, 16,0}, // DO_IDLE2 + {382, 8, 0}, // DO_GUN_DEATH + {398, 8, 0}, // DO_EXPLOSION_DEATH + {398, 8, 0}, // DO_EXPLOSION2_DEATH + {406, 12,0}, // DO_GRENADE_DEATH + {418, 18,0}, // DO_FIRE_DEATH + {436, 3, 3}, // DO_GESTURE1 + {460, 3, 3}, // DO_SALUTE1 + {484, 3, 3}, // DO_GESTURE2 + {508, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E2DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {288, 1, 12}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 20,20}, // DO_FIRE_WEAPON + {224, 2, 2}, // DO_LIE_DOWN + {240, 4, 4}, // DO_CRAWL + {272, 2, 2}, // DO_GET_UP + {288, 8, 12}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510, 8, 0}, // DO_GUN_DEATH + {526, 8, 0}, // DO_EXPLOSION_DEATH + {526, 8, 0}, // DO_EXPLOSION2_DEATH + {534, 12,0}, // DO_GRENADE_DEATH + {546, 18,0}, // DO_FIRE_DEATH + {564, 3, 3}, // DO_GESTURE1 + {588, 3, 3}, // DO_SALUTE1 + {612, 3, 3}, // DO_GESTURE2 + {636, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E3DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1,10}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 10,10}, // DO_FIRE_PRONE + {272, 16,0}, // DO_IDLE1 + {288, 16,0}, // DO_IDLE2 + {398, 8, 0}, // DO_GUN_DEATH + {414, 8, 0}, // DO_EXPLOSION_DEATH + {414, 8, 0}, // DO_EXPLOSION2_DEATH + {422, 12,0}, // DO_GRENADE_DEATH + {434, 18,0}, // DO_FIRE_DEATH + {452, 3, 3}, // DO_GESTURE1 + {476, 3, 3}, // DO_SALUTE1 + {500, 3, 3}, // DO_GESTURE2 + {524, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E4DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {256, 1,16}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 16,16}, // DO_FIRE_WEAPON + {192, 2, 2}, // DO_LIE_DOWN + {208, 4, 4}, // DO_CRAWL + {240, 2, 2}, // DO_GET_UP + {256, 16,16}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510, 8, 0}, // DO_GUN_DEATH + {526, 8, 0}, // DO_EXPLOSION_DEATH + {526, 8, 0}, // DO_EXPLOSION2_DEATH + {534, 12,0}, // DO_GRENADE_DEATH + {546, 18,0}, // DO_FIRE_DEATH + {564, 3, 3}, // DO_GESTURE1 + {588, 3, 3}, // DO_SALUTE1 + {612, 3, 3}, // DO_GESTURE2 + {636, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + + + +static DoInfoStruct E1DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1, 8}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 6, 8}, // DO_FIRE_PRONE + {256, 16,0}, // DO_IDLE1 + {272, 16,0}, // DO_IDLE2 + {382-94, 8, 0}, // DO_GUN_DEATH + {398-94, 8, 0}, // DO_EXPLOSION_DEATH + {398-94, 8, 0}, // DO_EXPLOSION2_DEATH + {406-94, 12,0}, // DO_GRENADE_DEATH + {418-94, 18,0}, // DO_FIRE_DEATH + {436-94, 3, 3}, // DO_GESTURE1 + {460-94, 3, 3}, // DO_SALUTE1 + {484-94, 3, 3}, // DO_GESTURE2 + {508-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E2DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {288, 1, 12}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 20,20}, // DO_FIRE_WEAPON + {224, 2, 2}, // DO_LIE_DOWN + {240, 4, 4}, // DO_CRAWL + {272, 2, 2}, // DO_GET_UP + {288, 8, 12}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E3DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1,10}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 10,10}, // DO_FIRE_PRONE + {272, 16,0}, // DO_IDLE1 + {288, 16,0}, // DO_IDLE2 + {398-94, 8, 0}, // DO_GUN_DEATH + {414-94, 8, 0}, // DO_EXPLOSION_DEATH + {414-94, 8, 0}, // DO_EXPLOSION2_DEATH + {422-94, 12,0}, // DO_GRENADE_DEATH + {434-94, 18,0}, // DO_FIRE_DEATH + {452-94, 3, 3}, // DO_GESTURE1 + {476-94, 3, 3}, // DO_SALUTE1 + {500-94, 3, 3}, // DO_GESTURE2 + {524-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E4DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {256, 1,16}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 16,16}, // DO_FIRE_WEAPON + {192, 2, 2}, // DO_LIE_DOWN + {208, 4, 4}, // DO_CRAWL + {240, 2, 2}, // DO_GET_UP + {256, 16,16}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + + +static DoInfoStruct E6DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {82, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {67, 2, 2}, // DO_LIE_DOWN + {82, 4, 4}, // DO_CRAWL + {114, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {130, 16,0}, // DO_IDLE1 + {130, 16,0}, // DO_IDLE2 + {146, 8, 0}, // DO_GUN_DEATH + {154, 8, 0}, // DO_EXPLOSION_DEATH + {162, 8, 0}, // DO_EXPLOSION2_DEATH + {170, 12,0}, // DO_GRENADE_DEATH + {182, 18,0}, // DO_FIRE_DEATH + {200, 3, 3}, // DO_GESTURE1 + {224, 3, 3}, // DO_SALUTE1 + {200, 3, 3}, // DO_GESTURE2 + {224, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E7DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {128, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 7, 7}, // DO_FIRE_WEAPON + {113, 2, 2}, // DO_LIE_DOWN + {128, 4, 4}, // DO_CRAWL + {161, 2, 2}, // DO_GET_UP + {176, 7, 7}, // DO_FIRE_PRONE + {232, 17,0}, // DO_IDLE1 + {249, 13,0}, // DO_IDLE2 + {262, 8, 0}, // DO_GUN_DEATH + {270, 8, 0}, // DO_EXPLOSION_DEATH + {278, 8, 0}, // DO_EXPLOSION2_DEATH + {286, 12,0}, // DO_GRENADE_DEATH + {298, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +//Spy +static DoInfoStruct SpyDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {144, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 8, 8}, // DO_FIRE_PRONE + {256, 14,0}, // DO_IDLE1 + {270, 18,0}, // DO_IDLE2 + {288, 8, 0}, // DO_GUN_DEATH + {296, 8, 0}, // DO_EXPLOSION_DEATH + {304, 8, 0}, // DO_EXPLOSION2_DEATH + {312, 12,0}, // DO_GRENADE_DEATH + {324, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E9DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {72, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {56, 2, 2}, // DO_LIE_DOWN + {72, 4, 4}, // DO_CRAWL + {108, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {120, 19,0}, // DO_IDLE1 + {120, 19,0}, // DO_IDLE2 + {139, 8, 0}, // DO_GUN_DEATH + {147, 8, 0}, // DO_EXPLOSION_DEATH + {155, 8, 0}, // DO_EXPLOSION2_DEATH + {163, 12,0}, // DO_GRENADE_DEATH + {175, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct MedicDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {130, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 28,0}, // DO_FIRE_WEAPON + {114, 2, 2}, // DO_LIE_DOWN + {130, 4, 4}, // DO_CRAWL + {162, 2, 2}, // DO_GET_UP + {56, 28,0}, // DO_FIRE_PRONE + {178, 15,0}, // DO_IDLE1 + {178, 15,0}, // DO_IDLE2 + {193, 8, 0}, // DO_GUN_DEATH + {210, 8, 0}, // DO_EXPLOSION_DEATH + {202, 8, 0}, // DO_EXPLOSION2_DEATH + {217, 12,0}, // DO_GRENADE_DEATH + {229, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct GeneralDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {104, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 4, 4}, // DO_FIRE_WEAPON + {88, 2, 2}, // DO_LIE_DOWN + {104, 4, 4}, // DO_CRAWL + {136, 2, 2}, // DO_GET_UP + {152, 4, 4}, // DO_FIRE_PRONE + {184, 26,0}, // DO_IDLE1 + {184, 26,0}, // DO_IDLE2 + {210, 8, 0}, // DO_GUN_DEATH + {226, 8, 0}, // DO_EXPLOSION_DEATH + {218, 8, 0}, // DO_EXPLOSION2_DEATH + {234, 12,0}, // DO_GRENADE_DEATH + {246, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct CivilianDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-85, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {205-85, 4, 4}, // DO_FIRE_PRONE + {189-85, 10,0}, // DO_IDLE1 + {199-85, 6, 0}, // DO_IDLE2 + {152, 8, 0}, // DO_GUN_DEATH + {160, 8, 0}, // DO_EXPLOSION_DEATH + {160, 8, 0}, // DO_EXPLOSION2_DEATH + {168, 12,0}, // DO_GRENADE_DEATH + {180, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct CivilianDoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {205, 4, 4}, // DO_FIRE_PRONE + {189, 10,0}, // DO_IDLE1 + {199, 6, 0}, // DO_IDLE2 + {329, 8, 0}, // DO_GUN_DEATH + {337, 8, 0}, // DO_EXPLOSION_DEATH + {337, 8, 0}, // DO_EXPLOSION2_DEATH + {345, 12,0}, // DO_GRENADE_DEATH + {357, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct EinsteinDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-92, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {0, 0, 0}, // DO_FIRE_PRONE + {104, 16,0}, // DO_IDLE1 + {104, 16,0}, // DO_IDLE2 + {212-92, 8, 0}, // DO_GUN_DEATH + {220-92, 8, 0}, // DO_EXPLOSION_DEATH + {228-92, 12,0}, // DO_EXPLOSION2_DEATH + {228-92, 12,0}, // DO_GRENADE_DEATH + {240-92, 17,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +// Attack dogs +static InfantryTypeClass const Dog( + INFANTRY_DOG, // Infantry type number. + TXT_GUARD_DOG, // Translate name number for infantry type. + "DOG", // INI name for infantry. + 0x0015, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + DogDoControls, + DogDoControls, + 1, // Frame of projectile launch. + 1, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Minigunners +static InfantryTypeClass const E1( + INFANTRY_E1, // Infantry type number. + TXT_E1, // Translate name number for infantry type. + "E1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E1DoControls, + E1DoControlsVirtual, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Grenadiers +static InfantryTypeClass const E2( + INFANTRY_E2, // Infantry type number. + TXT_E2, // Translate name number for infantry type. + "E2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E2DoControls, + E2DoControlsVirtual, + 14, // Frame of projectile launch. + 6, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Bazooka +static InfantryTypeClass const E3( + INFANTRY_E3, // Infantry type number. + TXT_E3, // Translate name number for infantry type. + "E3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E3DoControls, + E3DoControlsVirtual, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Flamethrower +static InfantryTypeClass const E4( + INFANTRY_E4, // Infantry type number. + TXT_E4, // Translate name number for infantry type. + "E4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + E4DoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Renovator +static InfantryTypeClass const E6( + INFANTRY_RENOVATOR, // Infantry type number. + TXT_E6, // Translate name number for infantry type. + "E6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E6DoControls, + E6DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Spy +static InfantryTypeClass const E8( + INFANTRY_SPY, // Infantry type number. + TXT_E8, // Translate name number for infantry type. + "SPY", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + SpyDoControls, + SpyDoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Thief +static InfantryTypeClass const E9( + INFANTRY_THIEF, // Infantry type number. + TXT_THIEF, // Translate name number for infantry type. + "THF", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E9DoControls, + E9DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Tanya +static InfantryTypeClass const E7( + INFANTRY_TANYA, // Infantry type number. + TXT_E7, // Translate name number for infantry type. + "E7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_COMMANDO, // Transport pip shape/color to use. + E7DoControls, + E7DoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Medic( + INFANTRY_MEDIC, // Infantry type number. + TXT_MEDIC, // Translate name number for infantry type. + "MEDI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const General( + INFANTRY_GENERAL, // Infantry type number. + TXT_GENERAL, // Translate name number for infantry type. + "GNRL", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + GeneralDoControls, + GeneralDoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Civilians +static InfantryTypeClass const C1( + INFANTRY_C1, // Infantry type number. + TXT_C1, // Translate name number for infantry type. + "C1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C2( + INFANTRY_C2, // Infantry type number. + TXT_C2, // Translate name number for infantry type. + "C2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv2 // pointer to override remap table +); + +static InfantryTypeClass const C3( + INFANTRY_C3, // Infantry type number. + TXT_C3, // Translate name number for infantry type. + "C3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C4( + INFANTRY_C4, // Infantry type number. + TXT_C4, // Translate name number for infantry type. + "C4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv4 // pointer to override remap table +); + +static InfantryTypeClass const C5( + INFANTRY_C5, // Infantry type number. + TXT_C5, // Translate name number for infantry type. + "C5", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv5 // pointer to override remap table +); + +static InfantryTypeClass const C6( + INFANTRY_C6, // Infantry type number. + TXT_C6, // Translate name number for infantry type. + "C6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv6 // pointer to override remap table +); + +static InfantryTypeClass const C7( + INFANTRY_C7, // Infantry type number. + TXT_C7, // Translate name number for infantry type. + "C7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv7 // pointer to override remap table +); + +static InfantryTypeClass const C8( + INFANTRY_C8, // Infantry type number. + TXT_C8, // Translate name number for infantry type. + "C8", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv8 // pointer to override remap table +); + +static InfantryTypeClass const C9( + INFANTRY_C9, // Infantry type number. + TXT_C9, // Translate name number for infantry type. + "C9", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv9 // pointer to override remap table +); + +// Nikoomba +static InfantryTypeClass const C10( + INFANTRY_C10, // Infantry type number. + TXT_C10, // Translate name number for infantry type. + "C10", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv10 // pointer to override remap table +); + +static InfantryTypeClass const Einstein( + INFANTRY_EINSTEIN, // Infantry type number. + TXT_EINSTEIN, // Translate name number for infantry type. + "EINSTEIN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + EinsteinDoControls, + 0, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Delphi( + INFANTRY_DELPHI, // Infantry type number. + TXT_DELPHI, // Translate name number for infantry type. + "DELPHI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const DrChan( + INFANTRY_CHAN, // Infantry type number. + TXT_CHAN, // Translate name number for infantry type. + "CHAN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + EinsteinDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + +// Shock Trooper +static InfantryTypeClass const ShockTrooper( + INFANTRY_SHOCK, // Infantry type number. + TXT_SHOCKTROOPER, // Translate name number for infantry type. + "SHOK", // INI name for infantry. + -0x0010, // Vertical offset. + 0x0038, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + E4DoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0, // pointer to override remap table + 0x0018 // Horizontal offset. +); + + +static InfantryTypeClass const Mechanic( + INFANTRY_MECHANIC, // Infantry type number. + TXT_MECHANIC, // Translate name number for infantry type. + "MECH", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); +#endif + +/*********************************************************************************************** + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * * + * This routine will construct the infantry type objects. It is use to create the static * + * infantry types that are used to give each of the infantry objects their characteristics. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 02/16/1996 JLB : Greatly simplified. * + *=============================================================================================*/ +InfantryTypeClass::InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * control, + DoInfoStruct const * virtual_control, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap, + int horizontaloffset) + : TechnoTypeClass(RTTI_INFANTRYTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + 0x0000, + 0x0000, + 0x0000, + is_nominal, + true, + true, + true, + false, + false, + is_theater, + false, + true, + true, + 8, + SPEED_FOOT, + horizontaloffset), + IsFemale(is_female), + IsCrawling(is_crawling), + IsCapture(false), + IsFraidyCat(false), + IsCivilian(is_civilian), + IsBomber(false), + IsDog(false), + IsRemapOverride(is_remap_override), + Type(type), + Pip(pip), + DoControls(control), + DoControlsVirtual(virtual_control), + FireLaunch(firelaunch), + ProneLaunch(pronelaunch), + OverrideRemap(override_remap) +{ + /* + ** Forced infantry overrides from the default. + */ + IsCrushable = true; + IsScanner = true; + IsRepairable = false; + IsCrew = false; + Speed = SPEED_FOOT; +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * * + * This will allocate an infantry type class object from the special memory pool of that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the infantry type class object allocated. If there was * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void * InfantryTypeClass::operator new(size_t) +{ + return(InfantryTypes.Alloc()); +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * * + * This will return a previously allocated infantry type class object back to the memory * + * pool from whence it came. * + * * + * INPUT: pointer -- The pointer to the infantry type class object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::operator delete(void * pointer) +{ + InfantryTypes.Free((InfantryTypeClass *)pointer); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * * + * This will pre-allocate all known infantry types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Init_Heap(void) +{ + /* + ** These infantry type class objects must be allocated in the exact order that they + ** are specified in the InfantryType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new InfantryTypeClass(E1); + new InfantryTypeClass(E2); + new InfantryTypeClass(E3); + new InfantryTypeClass(E4); + new InfantryTypeClass(E6); + new InfantryTypeClass(E7); + new InfantryTypeClass(E8); + new InfantryTypeClass(E9); + new InfantryTypeClass(Medic); + new InfantryTypeClass(General); + new InfantryTypeClass(Dog); + new InfantryTypeClass(C1); + new InfantryTypeClass(C2); + new InfantryTypeClass(C3); + new InfantryTypeClass(C4); + new InfantryTypeClass(C5); + new InfantryTypeClass(C6); + new InfantryTypeClass(C7); + new InfantryTypeClass(C8); + new InfantryTypeClass(C9); + new InfantryTypeClass(C10); + new InfantryTypeClass(Einstein); + new InfantryTypeClass(Delphi); + new InfantryTypeClass(DrChan); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new InfantryTypeClass(ShockTrooper); + new InfantryTypeClass(Mechanic); +#endif +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * * + * This creates an infantry object, but does not attempt to place it on the map. It is * + * typically used by the scenario editor when an object is needed, but the location has * + * not yet been specified for where it should appear on the map. * + * * + * INPUT: house -- The owner of the infantry object. * + * * + * OUTPUT: Returns with a pointer to the created infantry object. If an object could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * InfantryTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new InfantryClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * * + * This routine is used by the scenario editor to create and place an infantry object onto * + * the map at the location specified. * + * * + * INPUT: cell -- The cell location to place the infantry object at. * + * * + * house -- The owner of the infantry object. * + * * + * OUTPUT: bool; Was the infantry object successfully created and placed at the location * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + InfantryClass * i = new InfantryClass(Type, house); + if (i != NULL) { + COORDINATE coord = Map[cell].Closest_Free_Spot(Cell_Coord(cell)); + if (coord) { + return(i->Unlimbo(coord, DIR_E)); + } else { + delete i; + } + } + return(false); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * * + * This routine will return with a cell offset occupation list for a generic infantry * + * object. This is typically just a single cell since infantry are never bigger than one * + * cell and this routine presumes the infantry is located in the center of the cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a cell offset list for the infantry object as if it were located * + * in the center of a cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + + return(&_list[0]); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * * + * This routine is used by the scenario editor to display a generic representation of the * + * infantry object for the scenario editor. It simply draws a single (nice profile) view * + * of the infantry type. * + * * + * INPUT: x,y -- The display coordinates to render the infantry object at. * + * * + * window -- The window that the display coordinates are relative to. * + * * + * house -- The house colors to use when rendering this infantry object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + if (house != HOUSE_NONE) { + + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 2; + } + + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * * + * This routine will prepare the scenario editor so that the infantry objects appear on * + * the object list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Prep_For_Add(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + Map.Add_To_List(&As_Reference(index)); + } +} +#endif + + +/*********************************************************************************************** + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * * + * This routine is used to convert the infantry ASCII name as specified into an infantry * + * type number. This is called from the INI reader routine in the process if creating the * + * infantry objects needed for the scenario. * + * * + * INPUT: name -- The ASCII name to convert into an infantry type number. * + * * + * OUTPUT: Returns with the infantry type number that corresponds to the infantry ASCII name * + * specified. If no match could be found, then INFANTRY_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryType InfantryTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (InfantryType classid = INFANTRY_FIRST; classid < INFANTRY_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(INFANTRY_NONE); +} + + +/*********************************************************************************************** + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * * + * This routine will perform one time processing for the infantry type system. This is * + * generally restricted to loading of the infantry shape data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::One_Time(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + InfantryTypeClass const * uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass->Graphic_Name(), ".SHP"); + + #ifndef NDEBUG + RawFileClass sfile(fullname); + if (sfile.Is_Available()) { + ((void const *&)uclass->ImageData) = Load_Alloc_Data(sfile); + } else { + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + #endif + + /* + ** The small build image icon sized shapes are always generic. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%.4sICON", uclass->Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + + #ifndef NDEBUG + RawFileClass ifile(fullname); + if (ifile.Is_Available()) { + ((void const *&)uclass->CameoData) = Load_Alloc_Data(ifile); + } else { + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + #endif + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name text number for this infantry type. It examines * + * the special custom name flag to determine whether the custom name or the generic name * + * is to be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with text number for the name to give this infantry type object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryTypeClass::Full_Name(void) const +{ + if (Debug_Map || !IsNominal || Rule.IsNamed || Type == INFANTRY_C10 || Type == INFANTRY_DELPHI || Type == INFANTRY_EINSTEIN) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN); +} + + +/*********************************************************************************************** + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * * + * Use this routine to convert an infantry type number into a reference to the infantry * + * type class object it represents. * + * * + * INPUT: type -- The infantry type number to convert into a infantry type class object. * + * * + * OUTPUT: Returns with a reference to the infantry type class object specified. * + * * + * WARNINGS: Be sure that the type parameter is legal, otherwise the results are undefined. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +InfantryTypeClass & InfantryTypeClass::As_Reference(InfantryType type) +{ + return(*InfantryTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * * + * This routine will retrieve the override values for this infantry type class object from * + * the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to retrieve the data from. * + * * + * OUTPUT: bool; Was the infantry section for this type found and data retrieved from it? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsFraidyCat = ini.Get_Bool(Name(), "Fraidycat", IsFraidyCat); + IsCapture = ini.Get_Bool(Name(), "Infiltrate", IsCapture); + IsBomber = ini.Get_Bool(Name(), "C4", IsBomber); + IsDog = ini.Get_Bool(Name(), "IsCanine", IsDog); + if (IsBomber) IsCapture = true; + if (IsDog) IsLeader = false; + return(true); + } + return(false); +} + + +void InfantryTypeClass::Dimensions(int & width, int & height) const +{ +#ifdef WIN32 + width = 14; + height = 20; +#else + width = 12; + height = 16; +#endif +} \ No newline at end of file diff --git a/REDALERT/INFANTRY.CPP b/REDALERT/INFANTRY.CPP new file mode 100644 index 000000000..edcbf75ff --- /dev/null +++ b/REDALERT/INFANTRY.CPP @@ -0,0 +1,4335 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INFANTRY.CPP 2 3/03/97 10:35p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : October 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * InfantryClass::Draw_It -- Draws a unit object. * + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * InfantryClass::Init -- Initialize the infantry object system. * + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle* + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't o* + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + + +int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; + + +/*************************************************************************** +** This is the array of constant data associated with infantry maneuvers. It +** specifies the frame rate as well as if the animation can be aborted. +*/ +// interruptible, mobile, randomstart, rate +DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { + {true, false, false, 0}, // DO_STAND_READY + {true, false, false, 0}, // DO_STAND_GUARD + {true, false, false, 0}, // DO_PRONE + {true, true, true, 2}, // DO_WALK + {true, false, false, 1}, // DO_FIRE_WEAPON + {false, true, false, 2}, // DO_LIE_DOWN + {true, true, true, 2}, // DO_CRAWL + {false, false, false, 3}, // DO_GET_UP + {true, false, false, 1}, // DO_FIRE_PRONE + {true, false, false, 2}, // DO_IDLE1 + {true, false, false, 2}, // DO_IDLE2 + {false, false, false, 2}, // DO_GUN_DEATH + {false, false, false, 2}, // DO_EXPLOSION_DEATH + {false, false, false, 2}, // DO_EXPLOSION2_DEATH + {false, false, false, 2}, // DO_GRENADE_DEATH + {false, false, false, 2}, // DO_FIRE_DEATH + {false, false, false, 2}, // DO_GESTURE1 + {false, false, false, 2}, // DO_SALUTE1 + {false, false, false, 2}, // DO_GESTURE2 + {false, false, false, 2}, // DO_SALUTE2 + {false, false, false, 2}, // DO_DOG_MAUL +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * * + * This routine is used by the debug version to display pertinent information about the * + * infantry unit. * + * * + * INPUT: mono -- The monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Debug_Dump(MonoClass * mono) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + + mono->Print(Text_String(TXT_DEBUG_INFANTRY)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", Doing); + mono->Set_Cursor(8, 11);mono->Printf("%3d", Fear); + + mono->Fill_Attrib(66, 13, 12, 1, IsTechnician ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsStoked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsProne ? MonoClass::INVERSE : MonoClass::NORMAL); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * * + * This is the constructor used when creating an infantry unit. All values are required * + * except for facing and position. If these are absent, then the infantry is created in * + * a state of limbo -- not placed upon the map. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass::InfantryClass(InfantryType classid, HousesType house) : + FootClass(RTTI_INFANTRY, Infantry.ID(this), house), + Class(InfantryTypes.Ptr((int)classid)), + Doing(DO_NOTHING), + Comment(0), + IsTechnician(false), + IsStoked(false), + IsProne(false), + IsZoneCheat(false), + WasSelected(false), + Fear(FEAR_NONE), + StopDriverFrame(-1), + LookCell(0) +{ + House->Tracking_Add(this); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + IsCloakable = Class->IsCloakable; +#endif + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->Is_Two_Shooter()) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Strength = Class->MaxStrength; + + /* + ** Civilians carry much less ammo than soldiers do. + */ + Ammo = Class->MaxAmmo; +} + + +/*********************************************************************************************** + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * * + * This is the default destructor for infantry type units. It will put the infantry into * + * a limbo state if it isn't already in that state and the game is still active. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +InfantryClass::~InfantryClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * * + * This will allocate an infantry object from the infantry object free pool. If there is * + * no available slot, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * + * allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * InfantryClass::operator new(size_t) +{ + void * ptr = Infantry.Allocate(); + if (ptr != NULL) { + ((InfantryClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * * + * This routine is used return an infantry object back to the system. * + * * + * INPUT: ptr -- Pointer to the infantry object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((InfantryClass *)ptr)->IsActive = false; + } + Infantry.Free((InfantryClass *)ptr); +} + + +/*********************************************************************************************** + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * * + * This routine applies the damage specified to the infantry object. It is possible that * + * this routine will DESTROY the infantry unit in the process. * + * * + * INPUT: damage -- The damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The warhead type that is inflicting the damage. * + * * + * source -- Who is responsible for inflicting the damage. * + * * + * OUTPUT: bool; Was the infantry unit destroyed by this damage? * + * * + * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * + * for this in the code that follows the call to Take_Damage(). * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 03/31/1995 JLB : Revenge factor. * + *=============================================================================================*/ +ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Prone infantry take only half damage, but never below one damage point. + */ + if (IsProne && damage > 0) { + damage = damage * Rule.ProneDamageBias; + } + + /* + ** If we're taking damage from a dog, we have to decide if we're the + ** target of the dog. Dogs don't spill collateral damage onto anyone + ** else, so if we're the target of a valid dog, take full damage, but if + ** we're not the target, or the dog doesn't exist, then take no damage. + */ + if (source != NULL && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** hack for dog: if you're hit by a dog, and you're the target, your + ** damage gets upped to max. + */ + + if (res == RESULT_NONE) return(res); + + if (res == RESULT_DESTROYED) { + if (*this == INFANTRY_TANYA) { + IsTanyaDead = true; + } + Death_Announcement(source); + Stop_Driver(); + Stun(); + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + VocType sound; + VocType altsound; + sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM11); + altsound = VOC_YELL1; + if (*this == INFANTRY_TANYA) { + sound = altsound = VOC_TANYA_DIE; + } + if (Class->IsDog) { + sound = altsound = VOC_DOG_HURT; + } + + /* + ** The type of warhead determines the animation the infantry + ** will perform when killed. + */ + bool delthis = false; + TARGET us = As_Target(); + switch (WarheadTypeClass::As_Pointer(warhead)->InfantryDeath) { + default: + case 0: + delthis = true; + break; + + case 1: + Sound_Effect(sound, Coord); + Do_Action(DO_GUN_DEATH, true); + break; + + case 2: + Sound_Effect(sound, Coord); + Do_Action(DO_EXPLOSION_DEATH, true); + break; + + case 3: + Sound_Effect(sound, Coord); + Do_Action(DO_GRENADE_DEATH, true); + break; + + case 4: + Sound_Effect(altsound, Coord); + Do_Action(DO_FIRE_DEATH, true); + break; + + case 5: + Sound_Effect(sound, Coord); + AnimType anim = ANIM_ELECT_DIE; + if (Class->IsDog) anim = ANIM_DOG_ELECT_DIE; + new AnimClass(anim, Coord); + delthis = true; + break; + } + + if (delthis) { + delete this; + } + return(res); + } + + /* + ** When infantry gets hit, it gets scared. + */ + if (res != RESULT_DESTROYED) { + COORDINATE source_coord = (source) ? source->Coord : NULL; + + /* + ** If an engineer is damaged and it is just sitting there, then tell it + ** to go do something since it will definitely die if it doesn't. + */ + if (!House->IsHuman && *this == INFANTRY_RENOVATOR && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { + Assign_Mission(MISSION_HUNT); + } + + if (source != NULL) { + Scatter(source_coord); + } + + if (source != NULL && Fear < FEAR_SCARED) { + if (Class->IsFraidyCat) { + Fear = FEAR_PANIC; + } else { + Fear = FEAR_SCARED; + } + } else { + + /* + ** Increase the fear of the infantry by a bit. The fear increases more + ** quickly if the infantry is damaged. + */ + int morefear = FEAR_ANXIOUS; + if (Health_Ratio() > Rule.ConditionRed) morefear /= 2; + if (Health_Ratio() > Rule.ConditionYellow) morefear /= 2; + Fear = FearType(min((int)Fear + morefear, FEAR_MAXIMUM)); + } + } + return(res); +} + + +/*********************************************************************************************** + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * * + * This will determine the shape number to use for this infantry soldier. The shape number * + * is relative to the shape file associated with this infantry unit. * + * * + * INPUT: Window we will be drawing into * + * * + * OUTPUT: Returns with the shape number for this infantry object to be used when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 9/4/2019 1:45PM ST : Added window parameter * + *=============================================================================================*/ +int InfantryClass::Shape_Number(WindowNumberType window) const +{ + /* + ** Fetch the shape pointer to use for the infantry. This is controlled by what + ** choreograph sequence the infantry is performing, it's facing, and whether it + ** is prone. + */ + DoType doit = Doing; + if (doit == DO_NOTHING) doit = DO_STAND_READY; + + /* + ** Hold the walk pose for a couple of frames after we come to a stop to try and avoid the problem where a moving infantry + ** goes into the stand pose for a single frame when pausing in the assigned cell destination. ST - 9/4/2019 1:39PM + */ + if (doit == DO_STAND_READY) { + if (window == WINDOW_VIRTUAL) { + if (StopDriverFrame != -1) { + if (Frame - StopDriverFrame <= 2) { + if (Path[0] != FACING_NONE) { + doit = DO_WALK; + } + } + } + } + } + + /* + ** The animation frame numbers may be different when rendering in legacy mode vs. exporting for render in GlyphX. ST - 9/5/2019 12:34PM + */ + const DoInfoStruct *do_controls = (window == WINDOW_VIRTUAL) ? Class->DoControlsVirtual : Class->DoControls; + + /* + ** The infantry shape is always modulo the number of animation frames + ** of the action stage that the infantry is doing. + */ + int shapenum = Fetch_Stage() % max(do_controls[doit].Count, 1); + + /* + ** If facing makes a difference, then the shape number will be incremented + ** by the facing accordingly. + */ + if (do_controls[doit].Jump) { + shapenum += HumanShape[Dir_To_32(PrimaryFacing.Current())] * do_controls[doit].Jump; + } + + /* + ** Finally, the shape number is biased according to the starting frame number for + ** that action in the infantry shape file. + */ + shapenum += do_controls[doit].Frame; + + /* + ** Return with the final infantry shape number. + */ + return(shapenum); +} + + +/*********************************************************************************************** + * InfantryClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Converted to infantry support. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class by seeing if there is shape imagery for it. If + ** there is no shape image, then it certainly can't be drawn -- bail. + */ + void const * shapefile = Get_Image_Data(); + + if (shapefile == NULL) return; + + y += 4; + x -= 2; + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, Shape_Number(window), x, y, window); + + FootClass::Draw_It(x, y, window); +} + +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * * + * This routine will handle any special operations that need to be performed once each * + * cell travelled. This includes radioing a transport that it is now clear and the * + * transport is free to leave. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 03/01/1995 JLB : Capture building options. * + * 05/31/1995 JLB : Capture is always successful now. * + *=============================================================================================*/ +void InfantryClass::Per_Cell_Process(PCPType why) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + CellClass * cellptr = &Map[Coord]; + + if (why == PCP_END) { + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** capture, then capture it. + */ + if (Mission == MISSION_CAPTURE) { + TechnoClass * tech = cellptr->Cell_Building(); + + if (tech == NULL) tech = cellptr->Cell_Techno(); + if (tech != NULL && (tech->As_Target() == NavCom || tech->As_Target() == TarCom)) { + if (*this == INFANTRY_RENOVATOR) { + + /* + ** An engineer will either mega-repair a friendly or allied + ** building or it will damage/capture an enemy building. Whether + ** it damages or captures depends on how badly damaged the + ** enemy building is. + */ +#ifdef FIXIT_ENGINEER_CAPTURE + if (House->Is_Ally(tech)) { +#else + if (tech->House->Is_Ally(House)) { +#endif + tech->Renovate(); + } else { + bool iscapturable = false; + if (tech->What_Am_I() == RTTI_BUILDING) { + iscapturable = ((BuildingClass *)tech)->Class->IsCaptureable; + } +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) { +#else + if (tech->Health_Ratio() <= Rule.ConditionRed && iscapturable) { +#endif + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + tech->House->IsThieved = true; + tech->Captured(House); + } else { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + int damage = min( (short)((int)(tech->Techno_Type_Class()->MaxStrength) * EngineerDamage), tech->Strength-1); +#else + int damage = min( (tech->Techno_Type_Class()->MaxStrength) / 3, tech->Strength-1); +#endif + tech->Take_Damage(damage, 0, WARHEAD_HE, this, true); + } + BEnd(BENCH_PCP); + delete this; + return; + } + + } else { + if (*this != INFANTRY_SPY && tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + if (*this == INFANTRY_SPY) { + int housespy = (1 << (House->Class->House)); +// tech->House->IsSpied = true; + + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_SPIED, this); + } + + if (IsOwnedByPlayer) Speak(VOX_BUILDING_INFILTRATED); + + tech->Mark(MARK_OVERLAP_UP); + tech->SpiedBy |= housespy; + tech->Mark(MARK_OVERLAP_DOWN); + if (tech->What_Am_I() == RTTI_BUILDING) { + StructType build = *(BuildingClass *)tech; + if (build == STRUCT_RADAR /* || build == STRUCT_EYE */ ) { + tech->House->RadarSpied |= housespy; + } + + if (Session.Type == GAME_NORMAL || !MPSuperWeaponDisable) { + + // If they're spying on a sub pen, give 'em a sonar pulse + if (build == STRUCT_SUB_PEN) { + House->SuperWeapon[SPC_SONAR_PULSE].Enable(true, true, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, House); + } + } else { + if (IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + // If they're spying on an airfield, they get Parabombs + if (build == STRUCT_AIRSTRIP) { + House->SuperWeapon[SPC_PARA_BOMB].Enable(true, true, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, House); + } + } + else { + if (IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + } + + } else { + + if (*this == INFANTRY_THIEF) { // Thief just raided a storage facility + tech->House->IsThieved = true; + + if (tech->What_Am_I() == RTTI_BUILDING) { + BuildingClass * bldg = (BuildingClass *)tech; + if (bldg->Class->Capacity) { + + /* + ** If we just raided a storage facility (refinery or silo) + ** then give the thief up to half the capacity of the + ** storage facility. + */ + if (IsOwnedByPlayer || bldg->IsOwnedByPlayer) Speak(VOX_MONEY_STOLEN); +#ifdef OBSOLETE + long capacity = bldg->Class->Capacity * 256; + capacity /= (bldg->House->Tiberium+1); + int bldgcap = bldg->Class->Capacity; + + long cash = (bldgcap * 256) / (capacity+1); + if (cash > (bldgcap / 2)) cash = bldgcap / 2; +#else + long cash = bldg->House->Available_Money() / 2; +#endif + bldg->House->Spend_Money(cash); + House->Refund_Money(cash); + } + } + } + } + } + BEnd(BENCH_PCP); + delete this; + return; + + } else { + + #ifdef OBSOLETE + // are we trying to repair a bridge? + if (Is_Target_Cell(TarCom) ) { + CELL cell = Coord_Cell(Coord); + if (cell == ::As_Cell(NavCom)) { + TemplateType tt = cellptr->TType; + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + int h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D) { + new TemplateClass(TemplateType(cellptr->TType-1), cell); + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } else { + + // Trying to repair multi-segment bridge. Look for the + // start tile, then fix it, and determine the direction to + // go in and repair it all that way. + TemplateType newtt = TEMPLATE_BRIDGE_1A; + int xmov = -1; // coords to move to for next template + int ymov = 2; + bool valid = false; + switch (tt) { + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_1C: + valid = true; + break; + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_2C: + newtt = TEMPLATE_BRIDGE_2A; + xmov = 2; + ymov = -1; + valid = true; + break; + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + newtt = TEMPLATE_BRIDGE_3A; + valid = true; + break; + case TEMPLATE_BRIDGE_3E: + newtt = TEMPLATE_BRIDGE_3A; + xmov = 2; + ymov = -1; + valid = true; + break; + } + + // Did we find a valid repairable bridge piece? + if (valid) { + bool doing = true; + while (doing) { + new TemplateClass(TemplateType(newtt), cell); + cell += (MAP_CELL_W * ymov) + xmov; + if (xmov < 0) { + xmov = -1; + ymov = 1; + } else { + xmov = 1; + ymov = -1; + } + cellptr = &Map[cell]; + tt = cellptr->TType; + if ((tt >= TEMPLATE_BRIDGE_3B && tt <= TEMPLATE_BRIDGE_3F) || + tt == TEMPLATE_BRIDGE_1B || tt == TEMPLATE_BRIDGE_1C || + tt == TEMPLATE_BRIDGE_2B || tt == TEMPLATE_BRIDGE_2C ) { + + if (tt >= TEMPLATE_BRIDGE_3B) { + newtt = TEMPLATE_BRIDGE_3A; + } else { + if (tt < TEMPLATE_BRIDGE_2A) { + newtt = TEMPLATE_BRIDGE_1A; + } else { + newtt = TEMPLATE_BRIDGE_2A; + } + } + icon = cellptr->TIcon; + w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + } else { + doing = false; + } + } + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } + } + } + } else { + #endif + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + if (Map[Coord].Cell_Building()) { + Scatter(0, true); + } + } + #ifdef OBSOLETE + } + #endif + } + } + + /* + ** Infantry entering a transport vehicle will break radio contact + ** at attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno != NULL && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** sabotage, then sabotage it. + */ + if (Mission == MISSION_SABOTAGE) { + BuildingClass * building = cellptr->Cell_Building(); + if (building != NULL && building->As_Target() == NavCom) { + if (!building->IronCurtainCountDown && building->Mission != MISSION_DECONSTRUCTION) { + building->IsGoingToBlow = true; + building->Clicked_As_Target(PlayerPtr->Class->House, (Rule.C4Delay * TICKS_PER_MINUTE) / 2); // 2019/09/20 JAS - Added record of who clicked on the object + building->Clicked_As_Target(building->Owner(), (Rule.C4Delay * TICKS_PER_MINUTE) / 2); + building->CountDown = Rule.C4Delay * TICKS_PER_MINUTE; + building->WhomToRepay = As_Target(); + } + NavCom = TARGET_NONE; + Do_Uncloak(); + Arm = Rearm_Delay(true); + Scatter(building->Center_Coord(), true, true); // RUN AWAY! + BEnd(BENCH_PCP); + return; + } else { + if (::As_Target(Coord_Cell(Center_Coord())) == NavCom) { + Explosion_Damage(Coord, Rule.BridgeStrength, this, WARHEAD_HE); + + Stop_Driver(); + Scatter(Adjacent_Cell(Coord, PrimaryFacing), true, true); + Assign_Mission(MISSION_MOVE); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + if (!Target_Legal(NavCom) || Map[As_Cell(NavCom)].Land_Type() == LAND_WATER) { + Mark(MARK_DOWN); // Needed only so that Tanya will get destroyed by the explosion. + } + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + + Mark(MARK_DOWN); + } + } + } + + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (IsTethered) { + Transmit_Message(RADIO_UNLOADED); + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE) { + Do_Action(DO_GESTURE1); + } else { + Do_Action(DO_GESTURE2); + } + + /* + ** Special voice play. + */ + if (*this == INFANTRY_TANYA) { + Sound_Effect(VOC_TANYA_LAUGH, Coord); + } + + /* + ** If the cell is now full of infantry, tell them all to scatter + ** in order to make room for more. + */ + if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { + cellptr->Incoming(0, true, true); +// cellptr->Incoming(0, true); + } + } + + /* + ** When the infantry reaches the center of the cell, it may begin a new mission. + */ + if (MissionQueue == MISSION_NONE && !Target_Legal(NavCom) && !Target_Legal(TarCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + } + Commence(); + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = cellptr->Cell_Building(); + if (bldng != NULL && *bldng == STRUCT_APMINE) { + /* + ** Show the animation and get rid of the land mine + */ + COORDINATE blcoord = bldng->Center_Coord(); + new AnimClass(Combat_Anim(Rule.APMineDamage, WARHEAD_HE, cellptr->Land_Type()), blcoord); + delete bldng; + int damage; + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj != NULL && !obj->IsInLimbo) { + int dist = ::Distance(obj->Coord, blcoord); + if (dist <= 0xC0) { + damage = Rule.APMineDamage; + obj->Take_Damage(damage, 0, WARHEAD_HE); + } + } + } + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + + /* + ** If the last cell we looked from isn't adjacent to our current cell, + ** perform a full look. + */ + CELL cell = Coord_Cell(Coord); + if (::Distance(Cell_X(cell), Cell_Y(cell), Cell_X(LookCell), Cell_Y(LookCell)) > 1) { + Look(false); + } else { + Look(true); + } + +#if 1 +/* +** If after all is said and done, the unit finishes its move on an impassable cell, then +** it must presume that it is in the case of a unit driving onto a bridge that blows up +** before the unit completes it's move. In such a case the unit should have been destroyed +** anyway, so blow it up now. +*/ +LandType land = Map[Coord].Land_Type(); +if (!IsDriving && !Class->IsBomber && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; +} +#endif + + } + + if (IsActive) { + FootClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * * + * This is a support routine that removes the target specified from any targeting or * + * navigation computers. When a target is destroyed or removed from the game system, * + * the target must be removed from any tracking systems of the other units. This routine * + * handles removal for infantry units. * + * * + * INPUT: target -- The target to remove from the infantry unit's tracking systems. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Detach(TARGET target, bool all) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (TarCom == target) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + FootClass::Detach(target, all); +} + + +/*********************************************************************************************** + * InfantryClass::Init -- Initialize the infantry object system. * + * * + * This routine will force the infantry object system into its empty initial state. It * + * is called when the scenario needs to be cleared in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Init(void) +{ + Infantry.Free_All(); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * * + * This routine updates the infantry's navigation computer so that the infantry will * + * travel to the destination target specified. * + * * + * INPUT: target -- The target to have the infantry unit move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Destination(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Special flag so that infantry will start heading in the right direction immediately. + */ + if (IsDriving && !IsFormationMove && Target_Legal(target) && Map[Center_Coord()].Is_Clear_To_Move(Class->Speed, true, false)) { + Stop_Driver(); + } + + /* + ** When telling an infantry soldier to move to a location twice, then this + ** means that movement is more important than safety. Get up and run! + */ + if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat && !Class->IsDog) { + Do_Action(DO_GET_UP); + } + + /* + ** If telling a dog to attack a human, start the dog running + */ + TechnoClass * tech = As_Techno(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + FootClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * * + * This routine will update the infantry's targeting computer so that it will try to * + * attack the target specified. This might result in it moving to be within range and thus * + * also cause adjustment of the navigation computer. * + * * + * INPUT: target -- The target that this infantry should attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 06/30/1995 JLB : Tries to capture target if possible. * + *=============================================================================================*/ +void InfantryClass::Assign_Target(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Path[0] = FACING_NONE; + if (Class->IsDog) { + if (::As_Object(target) && ::As_Object(target)->What_Am_I() != RTTI_INFANTRY) { + target = TARGET_NONE; + } + } + FootClass::Assign_Target(target); + + /* + ** If this is an infantry that can only capture, then also assign its destination to the + ** target specified. + */ + if (!Target_Legal(NavCom) && Class->IsCapture && !Is_Weapon_Equipped()) { + BuildingClass const * building = As_Building(target); + if (building != NULL && building->Class->IsCaptureable) { + Assign_Destination(target); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * * + * This routine is used to handle the non-graphic AI processing the infantry requires. * + * Call this routine ONCE per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::AI(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + FootClass::AI(); + + if (!IsActive) { + return; + } + + if (IsUnloading) Mark(MARK_CHANGE_REDRAW); + + /* + ** Infantry that are not on the ground should always be redrawn. Such is + ** the case when they are parachuting to the ground. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(MARK_CHANGE); + } + + /* + ** Special hack to make sure that if this infantry is in firing animation, but the + ** stage class isn't set, then abort the firing flag. + */ + if (IsFiring && Fetch_Rate() == 0) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Do_Action(DO_STAND_READY); + Mark(MARK_OVERLAP_DOWN); + } + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsFiring && !IsFalling && !IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + + /* + ** Special hack to make sure the dog never attacks a cell. + */ + if (Class->IsDog && Target_Legal(TarCom) && Is_Target_Cell(TarCom)) { + Assign_Target(TARGET_NONE); + } + + /* + ** Handle any infantry fear logic or related actions. + */ + Fear_AI(); + + /* + ** Special victory dance action. + */ + if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment == 0) { + IsStoked = false; + Do_Action(Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2); + } + + /* + ** Determine if this infantry unit should fire off an + ** attack or not. + */ + Firing_AI(); + + /* + ** Handle the completion of the animation sequence. + */ + Doing_AI(); + + /* + ** Perform movement operations at this time. + */ + Movement_AI(); +} + + +/*********************************************************************************************** + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * * + * This routine is used to examine the cell specified and determine if the infantry is * + * allowed to enter it. It is used by the path finding algorithm. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the type of blockage in the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** If we are moving into an illegal cell, then we can't do that. + */ + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** If moving off the edge of the map, then consider that an illegal move. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + CellClass * cellptr = &Map[cell]; + + /* + ** Walls are considered impassable for infantry UNLESS the wall has a hole + ** in it. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (otype.IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (otype.IsWall) { + if ((cellptr->OverlayData / 16) != otype.DamageLevels) { + + /* + ** If the wall can be destroyed, then return this fact instead of + ** a complete failure to enter. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->Is_Wall_Destroyer()) { + return(MOVE_DESTROYABLE); + } + return(MOVE_NO); + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + MoveType retval = MOVE_OK; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** Always allow movement if the cell is the object to be captured or sabotaged. + */ + if (((Mission == MISSION_ENTER && In_Radio_Contact()) || Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && + (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { + + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target() && Is_Target_Unit(ArchiveTarget)) { + return(MOVE_NO); + } + + /* + ** If object is a land mine, allow movement + */ + if (obj->What_Am_I() == RTTI_BUILDING) { + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) { + obj = obj->Next; + continue; + } else { + if (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House)) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) { + obj = obj->Next; + continue; + } + } + } + } + + /* + ** Special case check so that a landed aircraft that is in radio contact, will not block + ** a capture attempt. It is presumed that this case happens when a helicopter is landed + ** at a helipad. + */ +// if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ +// if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT || obj->What_Am_I() == RTTI_UNIT) { +// if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { +// return(MOVE_OK); +// } +// } + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Allied objects block movement using different rules than for enemy + ** objects. + */ + if (House->Is_Ally(obj) || ScenarioInit) { + switch (obj->What_Am_I()) { + + /* + ** A unit blocks as either a moving blockage or a stationary temp blockage. + ** This depends on whether the unit is currently moving or not. + */ + case RTTI_UNIT: + if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + break; + + /* + ** Aircraft and buildings always block movement. If for some reason there is an + ** allied terrain object, that blocks movement as well. + */ + case RTTI_TERRAIN: + case RTTI_AIRCRAFT: + case RTTI_BUILDING: + return(MOVE_NO); + + default: + break; + } + + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** Any non-allied blockage is considered impassible if the infantry + ** is not equipped with a weapon. + */ + if (Combat_Damage() <= 0) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the infantry is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: +#ifdef OBSOLETE + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + case RTTI_INFANTRY: + if ( *(InfantryClass *)obj == INFANTRY_SPY && !Class->IsDog) { + retval = MOVE_TEMP; + break; + } + // otherwise, fall thru. + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } +// } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If foot soldiers cannot travel on the cell -- consider it impassable. + */ + if (retval == MOVE_OK && !IsTethered && Ground[cellptr->Land_Type()].Cost[SPEED_FOOT] == 0) { + +#ifdef OBSOLETE + /* + ** Special case - if it's an engineer, and the cell under consideration + ** is his NavCom, and his mission is mission_capture, then he's most + ** likely moving to his final destination to repair a bridge, so we + ** should let him. + */ + if (*this == INFANTRY_RENOVATOR && Is_Target_Cell(TarCom) && (cell == ::As_Cell(NavCom)) && (cellptr->TType == TEMPLATE_BRIDGE1D || cellptr->TType == TEMPLATE_BRIDGE2D || (cellptr->TType >= TEMPLATE_BRIDGE_1C && cellptr->TType <= TEMPLATE_BRIDGE_3E) ) ) { + return(MOVE_OK); + } +#endif + return(MOVE_NO); + } + + /* + ** if a unit has the cell reserved then we just can't go in there. + */ + if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { + return(MOVE_NO); + } + + /* + ** if a block of infantry has the cell reserved then there are two + ** possibilities... + */ + if (cellptr->InfType != HOUSE_NONE) { + if (House->Is_Ally(cellptr->InfType)) { + if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } + } else { + if (Combat_Damage() > 0) { + if (retval < MOVE_DESTROYABLE) { + retval = MOVE_DESTROYABLE; + } + } else { + return(MOVE_NO); + } + } + } + + /* + ** If it is still ok to move the infantry, then perform the last check + ** to see if the cell is already full of infantry. + */ + if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { + return(MOVE_NO); + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * * + * This is a rendering support routine that will return a pointer to a list of cell offsets * + * that specify the cells the infantry unit is currently overlapping (graphic wise) but * + * is not considered to occupy. This list is used to update the map display. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * + * occupy. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef PARTIAL +short const * InfantryClass::Overlap_List(bool redraw) const +#else +short const * InfantryClass::Overlap_List(bool ) const +#endif +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsDog) { + return(Coord_Spillage_List(Coord, 24 + (Doing == DO_DOG_MAUL ? 40 : 0) + (Doing >= DO_GUN_DEATH && Doing <= DO_FIRE_DEATH ? 40 : 0) )); + } else { + + /* + ** The default infantry rectangle will be as large as the largest shape the infantry + ** can be. + */ + +#ifdef PARTIAL + Rect rect(-16, -24, 32, 36); + + /* + ** If this is for a visual change redraw, then the overlap list will be based + ** on the actual dimensions of the shape data. If the dimensions have already + ** been calculated then use them, otherwise, use the default large rectangle + ** previously created. + */ + if (Height == 0 && !Is_Selected_By_Player() && redraw && Class->DimensionData != NULL) { + int shapenum = Shape_Number(); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + rect.Y += 4; + rect.X -= 2; + } + return(Coord_Spillage_List(Coord, rect, true)); +#else + + static Rect rect(-16, -24, 32, 36); + return(Coord_Spillage_List(Coord, rect, true)); +#endif + +// return(Coord_Spillage_List(Coord, 24 /*+ ((Doing > DO_WALK || IsSelected)?12:0)*/ )); + } +} + + +/*********************************************************************************************** + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * * + * Determines if the infantry unit can fire on the target. If it can't fire, then the * + * reason why is returned. * + * * + * INPUT: target -- The target to determine if the infantry can fire upon. * + * * + * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * + * can't, why not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 06/27/1995 JLB : Flame thrower can fire while prone now. * + *=============================================================================================*/ +FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Don't allow firing if the infantry is still firing on previous target. + */ +// if (IsFiring) return(FIRE_REARM); + + /* + ** If a medic is shooting at a healed target, let's declare the target + ** illegal so he won't be constantly healing healed infantrymen. + */ + if (Combat_Damage() < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TechnoClass * targ = As_Techno(target); +#else + InfantryClass * targ = As_Infantry(target); +#endif + if (targ == NULL || targ->Health_Ratio() >= Rule.ConditionGreen) { + return(FIRE_ILLEGAL); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (IsDriving || (Target_Legal(NavCom) && Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { + return(FIRE_MOVING); + } + + /* + ** Only one dog can fire on an infantry at a time + */ + if (Class->IsDog) { + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass *dog = Infantry.Ptr(index); + if (dog != this && dog->Class->IsDog && (dog->IsFiring || dog->IsInLimbo) && dog->TarCom == target) { + return(FIRE_ILLEGAL); + } + } + } + + return(FootClass::Can_Fire(target, which)); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * * + * This routine will determine the coordinate to use when this infantry fires. The * + * coordinate is the location where bullets appear (or fire effects appear) when the * + * object fires its weapon. * + * * + * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * + * weapon, 1 means secondary weapon. * + * * + * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * + * should appear. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/2019 SKY : Created. * + *=============================================================================================*/ +COORDINATE InfantryClass::Fire_Coord(int which) const +{ + COORDINATE coord = FootClass::Fire_Coord(which); + + /* + ** Since electric weapons draw a zap between start and end points, prone infantry that fire one + ** need to adjust their fire coordinate so the start point looks correct. + */ + if (IsProne) { + TechnoTypeClass const & tclass = *Techno_Type_Class(); + WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; + if (weapon && weapon->IsElectric) { + coord = Coord_Add(coord, XY_Coord(0, 48)); + } + } + + return coord; +} + + +/*********************************************************************************************** + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * * + * Use this routine when the infantry unit as accomplished its task and needs to find * + * something to do. The default behavior is to enter some idle state such as guarding. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Enter_Idle_Mode(bool ) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + } else { + + Handle_Navigation_List(); + + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsZombie || MissionControl[Mission].IsParalyzed) { + return; + } + + if (Class->IsDog) { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + ArchiveTarget = ::As_Target(Coord_Cell(Center_Coord())); + } + } else { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + if (Is_Weapon_Equipped()) { + order = MISSION_GUARD_AREA; + } else { + order = MISSION_GUARD; + } + } + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * * + * This routine is the random animator initiator for infantry units. This routine should * + * be called regularly. On occasion, it will cause the infantry to go into an idle * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 12/13/1994 JLB : Does random facing change. * + * 07/02/1995 JLB : Nikoomba special effects. * + *=============================================================================================*/ +bool InfantryClass::Random_Animate(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Is_Ready_To_Random_Animate()) { + IdleTimer = Random_Pick(Rule.RandomAnimateTime * (TICKS_PER_MINUTE/2), Rule.RandomAnimateTime * (TICKS_PER_MINUTE*2)); + + /* + ** Scared infantry will always follow the golden rule of civilians; + ** "When in darkness or in doubt, run in circles, scream, and shout!" + */ + if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { + Scatter(NULL, true); + return(true); + } + + switch (Random_Pick(0, 10)) { + case 0: + if (Class->IsDog) { + Do_Action(DO_IDLE1); + } + break; + + case 1: + Do_Action(DO_SALUTE1); + break; + + case 2: + Do_Action(DO_SALUTE2); + break; + + case 3: + Do_Action(DO_GESTURE1); + break; + + case 4: + Do_Action(DO_GESTURE2); + break; + + case 5: + Do_Action(DO_IDLE1); + break; + + case 6: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + break; + + case 7: + Do_Action(DO_IDLE2); + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!Is_Selected_By_Player() && IsOwnedByPlayer && *this == INFANTRY_TANYA && Sim_Random_Pick(0, 2) == 0) { + Sound_Effect(VOC_TANYA_SHAKE, Coord); + } + break; + + /* + ** On occasion, civilian types will wander about. + */ + case 8: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!House->IsHuman && Class->IsFraidyCat) { + Scatter(NULL, true); + } + break; + + case 9: + case 10: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * * + * This routine is used when the infantry should scatter to a nearby cell. Scattering * + * occurs as an occasional consequence of being fired upon. It is one of the features * + * that makes infantry so "charming". * + * * + * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * + * scatter. If the threat isn't from a particular direction, then this * + * parameter will be NULL. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/12/1994 JLB : Flame thrower infantry always scatter. * + * 08/02/1996 JLB : Added the nokidding parameter * + *=============================================================================================*/ +void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** A unit that is in the process of going somewhere will never scatter. + */ + if (IsDriving) forced = false; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + /* + ** If the infantry is currently engaged in legitimate combat, then don't + ** scatter unless forced to. + */ + if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; + + /* + ** Don't scatter if performing an action that can't be interrupted. + */ + if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; + + /* + ** For human players, don't scatter the infantry, if the special + ** flag has not been enabled that allows infantry scatter. + */ + if (!Rule.IsScatter && !nokidding && House->IsHuman && !forced && !Team.Is_Valid()) return; + + if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { + FacingType toface; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 4)-2); + } else { + COORDINATE coord = Coord_Fraction(Center_Coord()); + + if (coord != 0x00800080L) { + toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + toface = toface + FacingType(Random_Pick(0, 4)-2); + } + + CELL newcell = 0; + CELL altcell = 0; + FacingType face; + for (face = FACING_N; face < FACING_COUNT; face++) { + FacingType newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + if (altcell == 0) altcell = newcell; + if (!Map[newcell].Is_Bridge_Here()) break; +// Assign_Mission(MISSION_MOVE); +// Assign_Destination(::As_Target(newcell)); + } + } + if (face == FACING_COUNT) { + newcell = 0; + } + + if (newcell == 0) { + newcell = altcell; + } + + if (newcell != 0) { + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(newcell)); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Look -- Performs a look around (map reveal) action. * + * * + * This routine will reveal the map around this object. * + * * + * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * + * If it is absolutely known that the object has only moved one * + * cell from its previous location that it performed a Look() at, * + * then set this parameter to TRUE. It will only perform the look * + * check on the perimeter cells. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is slow, try to call it only when necessary. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Look(bool incremental) +{ + LookCell = Coord_Cell(Coord); + FootClass::Look(incremental); +} + + +/*********************************************************************************************** + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * * + * This starts the infantry into a choreographed animation sequence. These sequences can * + * be as simple as standing up or lying down, but can also be complex, such as dying or * + * performing some idle animation. * + * * + * INPUT: todo -- The choreographed sequence to start. * + * * + * force -- Force starting this animation even if the current animation is flagged * + * as uninterruptible. This is necessary for death animations. * + * * + * OUTPUT: bool; Was the animation started? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Do_Action(DoType todo, bool force) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (todo == DO_NOTHING || Class->DoControls[todo].Count == 0) { + return(false); + } + + if (*this == INFANTRY_SPY && todo >= DO_GESTURE1) { + todo = (DoType)(DO_IDLE1 + Random_Pick(0,1)); + } + + if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { + Mark(MARK_OVERLAP_UP); + Doing = todo; + Mark(MARK_OVERLAP_DOWN); + if (todo == DO_IDLE1 || todo == DO_IDLE2) { + Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); + } else { + Set_Rate(MasterDoControls[Doing].Rate); + } + Set_Stage(0); + + /* + ** Kludge to make sure that if infantry is in the dying animation, it isn't still + ** moving as well. + */ + if (Strength == 0) { + Stop_Driver(); + } + + /* + ** Make sure dogs don't try to go somewhere while they're mauling + */ + if (todo == DO_DOG_MAUL) { + Stop_Driver(); + Assign_Destination(TARGET_NONE); + } + + /* + ** Since the animation sequence might be interrupted. Set any flags + ** necessary so that if interrupted, the affect on the infantry is + ** still accomplished. + */ + switch (todo) { + case DO_LIE_DOWN: + IsProne = true; + break; + + case DO_GET_UP: + IsProne = false; + break; + + default: + break; + } + + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * * + * This is used to stop the infantry from animating in movement. This function will stop * + * the infantry moving and revert it to either a prone or standing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Stop_Driver(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Head_To_Coord()) { + + /* + ** Remove the "reservation" bit in the destination location. + */ + Clear_Occupy_Bit(Head_To_Coord()); + } + + /* + ** Set the occupation bit at the current location. + */ + Set_Occupy_Bit(Coord); + + if (Doing != DO_STAND_READY) { + StopDriverFrame = Frame; + } + + if (Class->IsDog) { + Do_Action(DO_STAND_READY); + } else { + if (IsProne) { + Do_Action(DO_PRONE); + } else { + Do_Action(DO_STAND_READY); + } + } + + if (Can_Enter_Cell(Coord_Cell(Coord)) == MOVE_OK) { + IsZoneCheat = false; + } else { + IsZoneCheat = true; + } + + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * * + * Use this routine to being the infantry moving toward the destination specified. The * + * destination is first checked to see if there is a free spot available. Then the infantry * + * reserves that spot and begins movement toward it. * + * * + * INPUT: headto -- The coordinate location desired for the infantry to head to. * + * * + * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * + * the specified destination could not contain the infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + * 05/14/1995 JLB : Tries to move to closest spot possible. * + * 05/15/1995 JLB : Uses closest spot if moving onto transport. * + *=============================================================================================*/ +bool InfantryClass::Start_Driver(COORDINATE & headto) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + COORDINATE old = headto; + + /* + ** Convert the head to coordinate to a legal sub-position location. + */ + headto = Map[headto].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); + if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { + headto = Map[old].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); + } + + /* + ** If the infantry started moving, then fixup the occupation bits. + */ + if (headto && FootClass::Start_Driver(headto)) { + if (!IsActive) return(false); + + /* + ** Remove the occupation bit from the infantry's current location. + */ + Clear_Occupy_Bit(Coord); + + /* + ** Set the occupation bit for the new headto location. + */ + Set_Occupy_Bit(headto); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * * + * This routine will clean up the infantry occupation bits (as necessary) as well as stop * + * the infantry movement process when it gets limboed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Limbo(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + Stop_Driver(); + + Clear_Occupy_Bit(Coord); + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * * + * Use this routine when the infantry unit wishes to fire a projectile. This routine * + * will launch the projectile and perform any other necessary infantry specific operations. * + * * + * INPUT: target -- The target of the attack. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * + * NULL is returned. If there is already the maximum bullet objects in play, then * + * this could happen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * InfantryClass::Fire_At(TARGET target, int which) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + + BulletClass * bullet = FootClass::Fire_At(target, which); + if (bullet != NULL && !IsInLimbo) { + + /* + ** For fraidycat infantry that run out of ammo, always go into + ** a maximum fear state at that time. + */ + if (Class->IsFraidyCat && !Ammo) { + Fear = FEAR_MAXIMUM; + if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { + Assign_Mission(MISSION_GUARD); + } + } + } + return(bullet); +} + + +/*********************************************************************************************** + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * * + * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * + * ensure that the coordinate is a legal subposition. * + * * + * INPUT: coord -- The coordinate to unlimbo the infantry at. * + * * + * facing -- The desired initial facing for the infantry unit. * + * * + * strength -- The desired initial strength for the infantry unit. * + * * + * mission -- The desired initial mission for the infantry unit. * + * * + * OUTPUT: bool; Was the infantry unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that the infantry start in a legal position on the map. + */ + coord = Map[coord].Closest_Free_Spot(coord, ScenarioInit); + if (coord == NULL) { + return(false); + } + + if (FootClass::Unlimbo(coord, facing)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->IScan |= (1L << Class->Type); + House->ActiveIScan |= (1L << Class->Type); + + /* + ** If there is no sight range, then this object isn't discovered by the player unless + ** it actually appears in a cell mapped by the player. + */ + if (Class->SightRange == 0) { + IsDiscoveredByPlayer = false; + } + + Set_Occupy_Bit(coord); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * * + * This routine intercepts the Greatest_Threat request and adds the appropriate target * + * types to search for. For regular infantry, this consists of all the ground types. For * + * rocket launching infantry, this also includes aircraft. * + * * + * INPUT: threat -- The basic threat control value. * + * * + * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * + * target could be found, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 09/28/1995 JLB : Engineers try to recapture buildings first. * + *=============================================================================================*/ +TARGET InfantryClass::Greatest_Threat(ThreatType threat) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Engineers consider only buildings that can be captured as being a threat. All others + ** are ignored. If there is a building that needs to be recaptured and it is nearby + ** then automatically head toward it to recapture it. + */ + if (!House->IsHuman && Class->IsCapture && !Is_Weapon_Equipped()) { + if (House->ToCapture != TARGET_NONE && Distance(House->ToCapture) < 0x0F00) { + return(House->ToCapture); + } + threat = threat | THREAT_CAPTURE; + } + + if (!Is_Weapon_Equipped()) { + if (!Class->IsCapture && *this != INFANTRY_RENOVATOR && *this != INFANTRY_SPY && *this != INFANTRY_THIEF) { + return(TARGET_NONE); + } + } + + /* + ** Special hack to make Tanya not auto-fire if controlled by a + ** human player. + */ + if (*this == INFANTRY_TANYA && House->IsHuman) { + return(TARGET_NONE); + } + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + + /* + ** Organic weapon types don't consider anything but infantry to be a threat. Such + ** weapon types would be the dog jaw and the medic first aid kit. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->WarheadPtr->IsOrganic) { + threat = threat & ~(THREAT_BUILDINGS|THREAT_VEHICLES|THREAT_BOATS|THREAT_AIR); + } + + /* + ** Human controlled infantry don't automatically fire upon buildings. + */ + if (Is_Weapon_Equipped() && House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + + /* + ** If this is a bomber type, then allow buildings to be considered a threat. + */ + if (Class->IsBomber && !House->IsHuman) { + threat = threat | THREAT_BUILDINGS; + } + + /* + ** Special hack: if it's a thief, then the only possible objects to + ** consider are tiberium-processing objects (silos & refineries). + */ + if (*this == INFANTRY_THIEF) { + threat = threat | THREAT_CAPTURE | THREAT_TIBERIUM; +// threat = (ThreatType)(THREAT_CAPTURE | THREAT_TIBERIUM); + } + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * * + * This routine handles playing an audio response as a result of the player selecting the * + * infantry unit. This occurs prior to giving it an order and may not be followed by any * + * order at all. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Select(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response = VOC_NONE; + if (Class->IsFemale) { + response = VOC_GIRL_YEAH; + } else { + response = VOC_GUY_YEAH; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_YES,VOC_ENG_ENG}; + static VocType _ein_response[] = {VOC_E_AH}; + static VocType _dog_response[] = {VOC_DOG_YES}; + static VocType _spy_response[] = {VOC_SPY_COMMANDER,VOC_SPY_YESSIR}; + static VocType _medic_response[] = {VOC_MED_REPORTING,VOC_MED_YESSIR}; + static VocType _tanya_response[] = {VOC_TANYA_YEA,VOC_TANYA_YES,VOC_TANYA_WHATS}; + static VocType _thief_response[] = {VOC_THIEF_YEA,VOC_THIEF_WHAT}; + static VocType _default_response[] = {VOC_ACKNOWL,VOC_REPORT,VOC_REPORT,VOC_YESSIR,VOC_YESSIR,VOC_READY,VOC_AWAIT}; + static VocType _stavros[] = {VOC_STAVCMDR,VOC_STAVYES}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic_response[] = {VOC_MECHHOWDY1,VOC_MECHHUH1,VOC_MECHLAFF1}; + static VocType _shock_response[] = {VOC_STYES1,VOC_STJUMP1,VOC_STJUICE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic_response; + size = ARRAY_SIZE(_mechanic_response); + break; + case INFANTRY_SHOCK: + response = _shock_response; + size = ARRAY_SIZE(_shock_response); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * * + * When the infantry is given the order to move, this routine handles the audio response * + * generated by the infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Move(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _dog_response[] = {VOC_DOG_BARK}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_ROCK}; +#else + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_GIVE}; +#endif + static VocType _thief_response[] = {VOC_THIEF_MOVEOUT,VOC_THIEF_OKAY,VOC_THIEF_AFFIRM}; + static VocType _default_response[] = {VOC_ROGER,VOC_RIGHT_AWAY,VOC_UGOTIT,VOC_AFFIRM,VOC_AFFIRM}; + static VocType _stavros[] = {VOC_STAVMOV,VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYES1,VOC_MECHRISE1,VOC_MECHHEAR1,VOC_MECHBOSS1}; + static VocType _shock[] = {VOC_STPOWER1,VOC_STDANCE1,VOC_STCHRGE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; + +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * * + * When the player gives an infantry unit the order to attack, this routine handles * + * the audio response by that unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _dog_response[] = {VOC_DOG_GROWL2}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH}; +#else + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH,VOC_TANYA_ROCK}; +#endif + static VocType _thief_response[] = {VOC_NONE}; + static VocType _default_response[] = {VOC_RIGHT_AWAY,VOC_AFFIRM,VOC_AFFIRM,VOC_UGOTIT,VOC_NO_PROB,VOC_YESSIR,VOC_YESSIR,VOC_YESSIR}; + static VocType _stavros[] = {VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYEEHAW1,VOC_MECHHOTDIG1,VOC_MECHWRENCH1}; + static VocType _shock[] = {VOC_STLIGHT1,VOC_STBURN1,VOC_STCRISP1,VOC_STSHOCK1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * * + * This routine checks to see if the infantry unit can capture the specified object rather * + * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * + * * + * INPUT: object -- The object that the mouse is currently over. * + * * + * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(ObjectClass const * object) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + assert(object != NULL); + + ActionType action = FootClass::What_Action(object); + + /* + ** If this is an engineer/renovator, we have to make some adjustments. + ** If the cursor is over an enemy building, return action-none. If it's + ** over a friendly building, we have to return action-capture so he can + ** renovate it. + ** However, abort the whole thing if the building is a barrel or mine. + */ + if (*this == INFANTRY_RENOVATOR && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + BuildingClass const * bldg = (BuildingClass *)object; + if (bldg->Class->IsRepairable) { + if (House->Is_Ally(bldg)) { + if (bldg->Health_Ratio() == 1) { + return(ACTION_NO_GREPAIR); + } + return(ACTION_GREPAIR); + } else { + + if (bldg->Class->IsCaptureable) { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (bldg->Health_Ratio() <= EngineerCaptureLevel) { +#else + if (bldg->Health_Ratio() <= Rule.ConditionRed) { +#endif + return(ACTION_CAPTURE); + } + return(ACTION_DAMAGE); + } + +// if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Class->IsCaptureable) { + } + } + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. In CSII, if this is a mechanic and the + ** cursor's over a friendly vehicle, execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (House->Is_Ally(object)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (object->What_Am_I() == RTTI_INFANTRY && object != this && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (object->What_Am_I() == RTTI_UNIT || object->What_Am_I() == RTTI_AIRCRAFT) ) ) { + + if (object->Health_Ratio() < Rule.ConditionGreen) { +// If it's a mechanic force-moving into an APC, don't try to heal it. + if(*this == INFANTRY_MECHANIC && object->What_Am_I() == RTTI_UNIT && *(UnitClass *)object == UNIT_APC && (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)) ) { + } else { + return(ACTION_HEAL); + } + } + } +#else + if(object->What_Am_I() == RTTI_INFANTRY && object != this) { + if (object->Health_Ratio() < Rule.ConditionGreen) { + return(ACTION_HEAL); + } + } +#endif + if(!object->Is_Techno() || !((TechnoClass *)object)->Techno_Type_Class()->Max_Passengers()) { + if (action == ACTION_GUARD_AREA || action == ACTION_MOVE) { + return(action); + } + return ((action == ACTION_TOGGLE_SELECT) ? ACTION_TOGGLE_SELECT : ACTION_SELECT); + } + } else { + return(ACTION_NOMOVE); + } + } + +#ifdef OBSOLETE + /* + ** See if it's a thief attacking an enemy vehicle, let him CAPTURE it. + */ + if (*this == INFANTRY_THIEF && object->What_Am_I() == RTTI_UNIT) { + if (((UnitClass *)object)->House != House) { + return(ACTION_CAPTURE); + } + } +#endif + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK && object->What_Am_I() != RTTI_INFANTRY) { + action = ACTION_NONE; + } + + /* + ** See if it's a commando, and if he's attacking a building, + ** have him return ACTION_SABOTAGE instead + */ + if (Class->IsBomber && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * obj = (BuildingClass *)object; + /* + ** Hack: Tanya should shoot barrels, bomb other structures. + */ + if (obj->Class->IsRepairable) { +// if (*obj != STRUCT_BARREL && *obj != STRUCT_BARREL3) { + return(ACTION_SABOTAGE); + } else { + return(ACTION_ATTACK); + } + } + + /* + ** See if this infantry is trying to move onto where a land mine is. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** There is no self-select action available for infantry types. + */ + if (action == ACTION_SELF) { + action = ACTION_NONE; + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (object->What_Am_I() != RTTI_VESSEL || *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + + if (Class->IsCapture && action == ACTION_ATTACK) { + if (!House->Is_Ally(object) && ( +//Disable capturing of helicopters (object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || + (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable) ) + ) { + + if (*this == INFANTRY_THIEF && (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->Capacity == 0)) { + action = ACTION_NONE; + } else { + + /* + ** If we're trying to capture a building, make sure we can get + ** to it. Find an adjacent cell that's the same zone as us. + ** The target circumstance is a naval yard that doesn't touch + ** the shore - a total island. In that case, we can't capture + ** it, so we shouldn't show the action-capture cursor. + */ + action = ACTION_CAPTURE; + if (object->What_Am_I() == RTTI_BUILDING) { + CELL cell = ::As_Cell(object->As_Target()); + int targzone = Map[::As_Cell(As_Target())].Zones[Class->MZone]; + short const *list = ((BuildingClass *)object)->Class->Occupy_List(false); + bool found = false; + while (*list != REFRESH_EOL && !found) { + CELL newcell = cell + *list++; + for (FacingType i=FACING_N; i < FACING_COUNT; i++) { + CELL adjcell = Adjacent_Cell(newcell, i); + if ((unsigned)adjcell >= MAP_CELL_TOTAL) continue; + if (Map[adjcell].Zones[Class->MZone] == targzone) { + found = true; + break; + } + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + } else { + if (!Is_Weapon_Equipped()) { + action = ACTION_NONE; + } + } + } + + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * * + * This routine is called when the player clicks over an object while this infantry soldier * + * is selected. Capture attempts are prohibited if the infantry cannot capture. The * + * command might respond if told to sabotage something. * + * * + * INPUT: action -- The action that is nominally to be performed. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_GREPAIR: + case ACTION_DAMAGE: + case ACTION_CAPTURE: + action = ACTION_CAPTURE; + break; + + case ACTION_HEAL: + action = ACTION_ATTACK; + break; + +// case ACTION_ENTER: +// action = ACTION_MOVE; +// break; + + case ACTION_SABOTAGE: + case ACTION_ATTACK: + case ACTION_GUARD_AREA: + case ACTION_MOVE: + action = action; + break; + + default: +// action = ACTION_NONE; + break; + } + + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * * + * INPUT: CELL - the cell we are setting the bit in * + * * + * int - the spot index we are setting the bit for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Set the occupy position for the spot that we passed in + */ + Map[cell].Flag.Composite |= (1 << spot_index); + + /* + ** Record the type of infantry that now owns the cell + */ + Map[cell].InfType = Owner(); +} + + +/*************************************************************************** + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Clear the occupy bit for the infantry in that cell + */ + Map[cell].Flag.Composite &= ~(1 << spot_index); + + /* + ** If he was the last infantry recorded in the cell then + ** remove the infantry ownership flag. + */ + if (!(Map[cell].Flag.Composite & 0x1F)) { + Map[cell].InfType = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * * + * This routine will return with the full name (as a text number) for this infantry * + * unit. Typically, this is the normal name, but in cases of civilian type survivors from * + * a building explosion, it might be a technician instead. In such a case, the special * + * technician name number is returned instead. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name to use for this infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/28/1996 JLB : Spy returns "enemy soldier" text name. * + *=============================================================================================*/ +int InfantryClass::Full_Name(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (IsTechnician) { + return(TXT_TECHNICIAN); + } + + if (*this == INFANTRY_SPY && !House->IsPlayerControl) { + return(TXT_E1); + } + + return(Class->Full_Name()); +} + + +/*********************************************************************************************** + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * * + * This routine intercepts the normal attack mission and if an engineer is detected and the * + * target is a building, then the engineer will be automatically assigned the capture * + * mission. In other cases, the normal attack logic will proceed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + * 04/15/1996 BWG : Engineers can only attack their own house's buildings now. * + * 05/29/1996 JLB : Engineers can now damage/capture enemy buildings. * + *=============================================================================================*/ +int InfantryClass::Mission_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsBomber && As_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + if (Class->IsCapture && As_Building(TarCom) != NULL && As_Building(TarCom)->Class->IsCaptureable) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + return(1); + } + + return(FootClass::Mission_Attack()); +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * * + * This routine will determine what action to perform if the mouse was clicked on the cell * + * specified. This is just a courier function since the lower level classes actually * + * perform the work. The need for this routine at this level is due to the existence of * + * a similarly named function at this level as well. C++ namespace rules require this * + * function courier to be in place or an error will result. * + * * + * INPUT: cell -- The cell that the mouse might be clicked upon. * + * * + * OUTPUT: Returns with the action that would be given to this infantry unit if the mouse * + * were clicked at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(CELL cell) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK) { + action = ACTION_NONE; + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (action == ACTION_ATTACK) { + action = ACTION_NOMOVE; + } + } + + /* + ** Demolitioners may destroy a bridge + */ + if (Class->IsBomber && action == ACTION_MOVE && !Special.IsCaptureTheFlag) { + switch (Map[cell].TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: +// case TEMPLATE_BRIDGE_3A: +// case TEMPLATE_BRIDGE_3B: + return(ACTION_SABOTAGE); + } + } + +#ifdef OBSOLETE + /* + ** Engineers may repair a destroyed bridge. + */ + if (*this == INFANTRY_RENOVATOR && action == ACTION_NOMOVE) { + /* + ** If they're pointing on the wrong side of the bridge, ignore it + ** 'cause we can't get there. + */ + TemplateType tt = Map[cell].TType; + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D || + tt == TEMPLATE_BRIDGE_1C || tt == TEMPLATE_BRIDGE_2C || + (tt >= TEMPLATE_BRIDGE_3C && tt <= TEMPLATE_BRIDGE_3E) ) { + /* + ** We know they're pointing at a destroyed bridge cell. If the cell + ** they're pointing at is surrounded by impassables, return this + ** cell as impassable. But, if any cell surrounding this cell is + ** passable, return that this is a capturable cell. + */ + if (Map[cell].Land_Type() == LAND_ROCK) { + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + int y = Cell_Y(cell); + if (y) { + LandType above = Map[(CELL)(cell-(MAP_CELL_W-1))].Land_Type(); + if (above == LAND_CLEAR || above == LAND_ROAD) { + if (Map[(CELL)(cell-(MAP_CELL_W-1))].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + if (y < MAP_CELL_H) { + LandType below = Map[(CELL)(cell + MAP_CELL_W-1)].Land_Type(); + if (below == LAND_CLEAR || below == LAND_ROAD) { + if (Map[(CELL)(cell+MAP_CELL_W-1)].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + } + return(ACTION_NOMOVE); + } + } +#endif + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * * + * This routine will return a reference to the infantry type class object that describes * + * this infantry's characteristics. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the InfantryTypeClass object associated with this * + * infantry object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & InfantryClass::Class_Of(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Read_INI(CCINIClass & ini) +{ + InfantryClass * infantry; // Working infantry pointer. + HousesType inhouse; // Infantry house. + InfantryType classid; // Infantry class. + char buf[128]; + char * validation; + DirType dir; + TriggerTypeClass * tp; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get an infantry entry + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); + if (inhouse != HOUSE_NONE) { + + /* + ** 2nd token: infantry type name. + */ + classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); + + if (classid != INFANTRY_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + infantry = new InfantryClass(classid, inhouse); + if (infantry != NULL) { + + /* + ** 3rd token: strength. + */ + int strength = atoi(strtok(NULL, ",\n\r")); + + /* + ** 4th token: cell #. + */ + CELL cell = atoi(strtok(NULL, ",\n\r")); + COORDINATE coord = Cell_Coord(cell); + + /* + ** 5th token: cell sub-location. + */ + int sub = atoi(strtok(NULL, ",")); + coord = Coord_Add(Coord_Whole(coord), StoppingCoordAbs[ sub ]); + + /* + ** Fetch the mission and facing. + */ + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + validation = strtok(NULL, ",\n\r"); + if (validation) { + dir = (DirType)atoi(validation); + validation = strtok(NULL, ",\n\r"); + if (validation) { + tp = TriggerTypeClass::From_Name(validation); + } else { + tp = NULL; + } + } else { + dir = (DirType)0; + tp = NULL; + } + + infantry->Trigger = NULL; + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + infantry->Trigger = tt; + } + } + + if (infantry->Unlimbo(coord, dir)) { + infantry->Strength = (int)infantry->Class_Of().MaxStrength * fixed(strength, 256); + if (infantry->Strength > infantry->Class->MaxStrength-3) infantry->Strength = infantry->Class->MaxStrength; + // infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); + if (Session.Type == GAME_NORMAL || infantry->House->IsHuman) { + infantry->Assign_Mission(mission); + infantry->Commence(); + } else { + infantry->Enter_Idle_Mode(); + } + } else { + + /* + ** If the infantry could not be unlimboed, then this is a big error. + ** Delete the infantry. + */ + delete infantry; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * * + * This will store all the infantry objects to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the infantry data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing infantry data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the infantry data out. + */ + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (!infantry->IsInLimbo) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", + infantry->House->Class->IniName, + infantry->Class->IniName, + infantry->Health_Ratio()*256, + Coord_Cell(infantry->Coord), + CellClass::Spot_Index(infantry->Coord), + MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? + infantry->MissionQueue : infantry->Mission), + infantry->PrimaryFacing.Current(), + infantry->Trigger.Is_Valid() ? infantry->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * * + * Use this routine to handle the fear logic for this infantry. It will slowly increase * + * the bravery of the infantry as well as cause it to stand up or lie down as appropriate. * + * It will even handle the special fraidy cat logic for civilian infantry. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once per game logic loop per infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Fear_AI(void) +{ + /* + ** After a time, the infantry will gain courage. + */ + if (Fear > 0) { + + Fear--; + + /* + ** When an armed civilian becomes unafraid, he will then reload + ** another clip into his pistol. + */ + if (Fear == 0 && Ammo == 0 && Is_Weapon_Equipped()) { + Ammo = Class->MaxAmmo; + } + + /* + ** Stand up if brave and lie down if afraid. + */ + if (IsProne) { + if (Fear < FEAR_ANXIOUS) { + Do_Action(DO_GET_UP); + } + } else { + + /* + ** Drop to the ground if anxious. Don't drop to the ground while moving + ** and the special elite flag is active. + */ + if (!Class->IsDog && Height == 0 && Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving))) { + Do_Action(DO_LIE_DOWN); + } + } + } + + /* + ** When in darkness or in doubt, + ** run in circles, scream, and shout. + */ + if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsFalling && !IsDriving && !Target_Legal(NavCom)) { + Scatter(0, true); + } +} + + +/*********************************************************************************************** + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * * + * This routine will detect when the infantry has left the edge of the world and will * + * delete it as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit deleted by this routine? * + * * + * WARNINGS: Be sure the check the return value and if true, abort any further processing * + * for this infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Edge_Of_World_AI(void) +{ + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Team.Is_Valid() && IsLocked) Team->IsLeaveMap = true; + + if (!Team.Is_Valid() && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * * + * This will examine the infantry and determine what firing action is required. It will * + * search for targets, starting firing animations, and launch bullets as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Firing_AI(void) +{ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + if (!IsFiring) { + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + if (Combat_Damage(primary) < 0) { + ObjectClass * targ= As_Object(TarCom); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (targ) { + if( (targ->What_Am_I() == RTTI_INFANTRY && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (targ->What_Am_I() == RTTI_AIRCRAFT || targ->What_Am_I() == RTTI_UNIT )) ) { + + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } + } else { + Assign_Target(TARGET_NONE); + } +#else + if (targ && targ->What_Am_I() == RTTI_INFANTRY) { + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } else { + Assign_Target(TARGET_NONE); + } +#endif + } else if (Class->IsDog) { + Assign_Target(TARGET_NONE); + } + break; + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + /* + ** Start firing animation. + */ + if (IsProne) { + Do_Action(DO_FIRE_PRONE); + } else { + Do_Action(DO_FIRE_WEAPON); + } + + Mark(MARK_OVERLAP_UP); + IsFiring = true; + Mark(MARK_OVERLAP_DOWN); + + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + + /* + ** If the target is in range, and the NavCom is the same, then just + ** stop and keep firing. + */ + if (TarCom == NavCom) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + break; + } + } + + /* + ** If in the middle of firing animation, then only + ** process that. Infantry cannot fire and move simultaneously. + ** At some point in the firing animation process, a projectile + ** will be launched. When the required animation frames have + ** been completed, the firing animation stops. + */ + int firestage = Class->FireLaunch; + if (IsProne) firestage = Class->ProneLaunch; + + if (IsFiring && Fetch_Stage() == firestage) { + + /* + ** Target might have changed during the firing animation + */ + if (Can_Fire(TarCom, primary) == FIRE_OK) { + Fire_At(TarCom, primary); + + /* + ** Run away from slowly approaching projectiles. + */ + if (Class->PrimaryWeapon->MaxSpeed < Rule.Incoming) { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** If it's a dog, get rid of him (he'll be re-created when he hits) + */ + if (Class->IsDog) { + WasSelected = IsSelected; + ScenarioInit++; + Limbo(); + ScenarioInit--; + } + } else { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + } + } else { + if (IsFiring) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * * + * Infantry can be in one of many different animation sequences. At the conclusion of each * + * sequence, the infantry will quite likely transition to a new animation state. This * + * routine handles detecting when that trasition should occur and starting the infantry * + * into its new state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Doing_AI(void) +{ + if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { + switch (Doing) { + default: + if (IsDriving) { + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } + } else { + if (Class->IsDog) { + Do_Action(DO_STAND_READY, true); + } else { + if (IsProne) { + Do_Action(DO_PRONE, true); + } else { + Do_Action(DO_STAND_READY, true); + } + } + } + break; + + case DO_DOG_MAUL: + Do_Action(DO_STAND_READY, true); + break; + + case DO_GUN_DEATH: + case DO_EXPLOSION_DEATH: + case DO_EXPLOSION2_DEATH: + case DO_GRENADE_DEATH: + case DO_FIRE_DEATH: + if (Fetch_Stage() >= Class->DoControls[Doing].Count) { + AnimClass* anim = NULL; + LandType land = Map[Center_Coord()].Land_Type(); + if (land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) { + if (Doing == DO_GUN_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_GRENADE_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-10, 3))); + } + if (Doing == DO_EXPLOSION_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE3, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_EXPLOSION2_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE2, Center_Coord()); + } + } + if (anim != NULL) { + anim->OwnerHouse = House->Class->House; + } + delete this; + return; + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * * + * It examines the infantry state and determines what movement action should be initiated * + * or processed. It handles the actual movement of the infantry as well as any path finding * + * or infantry startup logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Movement_AI(void) +{ + /* + ** Special hack check to ensure that infantry will never get stuck in a movement order if + ** there is no place to go. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } + + if (!IsFiring && !IsFalling && Doing != DO_DOG_MAUL) { + if (!IsDriving) { + + /* + ** When in guard mode, never allow a valid navcom. + */ + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { + Assign_Destination(TARGET_NONE); +// if (IsTethered) Scatter(0, true); + } + + /* + ** Double check to make sure it doesn't have a movement destination into a zone + ** that it can't travel to. In such a case, abort the movement process by clearing + ** the navigation computer. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && !IsDriving && !IsTethered && Target_Legal(NavCom) && IsLocked && Map[Coord].Zones[Class->MZone] != Map[As_Cell(NavCom)].Zones[Class->MZone]) { +// hack: if it's tanya, spy, or engineer, let 'em move there anyway. + if (!Class->IsCapture && Mission != MISSION_ENTER) { +// if (*this != INFANTRY_TANYA && *this != INFANTRY_SPY && *this != INFANTRY_RENOVATOR) { + Assign_Destination(TARGET_NONE); + } + } + + /* + ** A head to coordinate is needed. If there is no path + ** available, then create one. + */ + if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { + + /* + ** Determine if the next cell in the list is available + ** to be entered. If not, then abort the path and try + ** again. + */ + if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { + Path[0] = FACING_NONE; + } + + /* + ** Check to see if the target is closer than expected. This occurs + ** when heading toward a moving object and that object is heading + ** toward the unit. Shorten the precalculated path to be no longer + ** than the distance to the target. + */ + int d = Lepton_To_Cell(Distance(NavCom)); + if (d < CONQUER_PATH_MAX) { + Path[d] = FACING_NONE; + } + + /* + ** Find a path to follow if one isn't already calculated. + */ + if (Path[0] == FACING_NONE) { + + /* + ** Calculate the path from the current location to the + ** destination indicated by the navigation computer. If there + ** was a fundamental error with finding a path, then this + ** indicates that basic path & movement logic needs to be + ** aborted. + */ + if (PathDelay != 0) { + return; + } + if (!Basic_Path()) { + + /* + ** Check to ensure that if a computer controlled unit is in + ** hunt mode, but cannot reach the target it would like to, + ** abort the target tracking and let the normal hunt logic + ** assign a new one. + */ + if (!House->IsHuman && Mission == MISSION_HUNT) { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + } else { + + /* + ** If the infantry unit is close enough to the target, then + ** tell it to stop. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !IsTethered) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** Update the try try again counter so that this + ** infantry unit will try again at a later time. + */ + if (TryTryAgain) { + TryTryAgain--; + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + //If we're trying to enter a transport we need to fail so others can try to enter. - LLL 4/17/2020 + if (Mission == MISSION_ENTER) { + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Abort the target and destination process since the path + ** could not be found. In such a case, processing should stop + ** or else the game will bog down with repeated path failures. + ** Only perform the abort of the target is in a different zone. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && IsLocked && Target_Legal(NavCom) && Map[As_Cell(NavCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Destination(TARGET_NONE); + } + if (IsLocked && Target_Legal(TarCom) && Map[As_Cell(TarCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Target(TARGET_NONE); + } + } + } + } + Stop_Driver(); + return; + } + TryTryAgain = PATH_RETRY; + } + + /* + ** Determine the coordinate to head to based on the infantry's + ** current location and the next location in the path. + */ + COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); + CELL acell = Coord_Cell(acoord); + + if (Can_Enter_Cell(acell) != MOVE_OK) { + + if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered /*&& House->IsHuman*/ && Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { + if (Map[acell].Cell_Object()) { + if (!House->Is_Ally(Map[acell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); + } + } + } + } + + Path[0] = FACING_NONE; + Stop_Driver(); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + } else { + if (Start_Driver(acoord)) { + if (!IsActive) return; + PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); + if (IsFormationMove) { + Set_Speed(Ground[Map[Coord].Land_Type()].Cost[FormationSpeed] * 255); + } else { + Set_Speed(0xFF); + } + + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } + } + } + } + + } else { + + /* + ** The infantry knows where it should be headed, so head there. Check + ** to see if the infantry is "close enough" to the desired location that + ** it should just consider itself to have arrived. In this case, force + ** the infantry to the destination location and mark this path step + ** as complete. + */ + Mark(MARK_UP); + if (Distance(Head_To_Coord()) < 0x0010) { + + memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); + Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; + Coord = Head_To_Coord(); + Per_Cell_Process(PCP_END); + if (!IsActive || IsInLimbo) return; + + Stop_Driver(); + if (!IsActive || IsInLimbo) return; + + if (Coord_Cell(Coord) == As_Cell(NavCom)) { + NavCom = TARGET_NONE; + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + //Stop_Driver(); + Path[0] = FACING_NONE; + } + } else { + int movespeed = Speed; + + /* + ** When prone, the infantry moves at half speed or double + ** speed. This depends on whether the infantry actually has + ** prone animation stages. Civilians don't, and so they + ** run instead. + */ + if (Class->IsDog && Target_Legal(TarCom)) { + movespeed *= 2; + } + + if (IsProne && !Class->IsDog) { + if ((Class->IsFraidyCat && !Class->IsCrawling) ) { + movespeed = Speed*2; + } else { + movespeed = Speed/2; + } + } + + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + + /* + ** Advance the infantry as far as it should go. + */ + MPHType maxspeed = MPHType(min(Class->MaxSpeed * SpeedBias * House->GroundspeedBias, MPH_LIGHT_SPEED)); + + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + Coord = Coord_Move(Coord, Direction(Head_To_Coord()), maxspeed * fixed(movespeed, 256)); + } + Mark(MARK_DOWN); + } + IsNewNavCom = false; + } +} + + +/*********************************************************************************************** + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * * + * The image data for the infantry differs from normal if this is a spy. A spy always * + * appears like a minigunner to the non-owning players. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the image data to use for this infantry soldier. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * InfantryClass::Get_Image_Data(void) const +{ + if (!IsOwnedByPlayer && *this == INFANTRY_SPY) { + return(MFCD::Retrieve("E1.SHP")); + } + return(TechnoClass::Get_Image_Data()); +} + + +/*********************************************************************************************** + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle * + * * + * This routine will examine this infantry and determine if it is allowed and ready to * + * perform an idle animation. The conditions under which idle animations can be performed * + * are restrictive. Hence this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this infantry ready to do an idle animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Is_Ready_To_Random_Animate(void) const +{ + /* + ** See if the base classes (more rudimentary checking) determines that idle animations + ** cannot occur. If they cannot, then return with the failure code. + */ + if (!FootClass::Is_Ready_To_Random_Animate()) { + return(false); + } + + /* + ** While the infantry is in the air (such as when paradropping), it won't be allowed + ** to idle animate. + */ + if (Height > 0) { + return(false); + } + + /* + ** When the infantry is walking or otherwise engauged in travel, it won't idle animate. + */ + if (IsDriving) { + return(false); + } + + /* + ** When prone, idle animations cannot occur. This is primarily because there are no prone + ** idle animations. + */ + if (IsProne) { + return(false); + } + + /* + ** When firing, the infantry should not perform any idle animations. + */ + if (IsFiring) { + return(false); + } + + /* + ** Only if the infantry is in guard or ready stance is idle animations allowed. This is + ** because the idle animations start and end with these frames. + */ + if (Doing != DO_STAND_GUARD && Doing != DO_STAND_READY) { + return(false); + } + + /* + ** Since no reason was found to indicate it is not a good time to idle + ** animate, then it must be a good time to do so. + */ + return(true); +} + + +/*********************************************************************************************** + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * * + * This routine will paradrop this soldier at the location specified. It will cause the * + * soldier to hunt if controlled by the computer and to guard if controlledy by the * + * human. * + * * + * INPUT: coord -- The coordinate to paradrop the soldier to. * + * * + * OUTPUT: bool; Was the paradrop successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Paradrop(COORDINATE coord) +{ + if (FootClass::Paradrop(coord)) { + if (House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + return(true); + } + return(false); +} \ No newline at end of file diff --git a/REDALERT/INFANTRY.H b/REDALERT/INFANTRY.H new file mode 100644 index 000000000..332c746a1 --- /dev/null +++ b/REDALERT/INFANTRY.H @@ -0,0 +1,239 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INFANTRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INFANTRY_H +#define INFANTRY_H + + +class InfantryClass : public FootClass +{ + public: + CCPtr Class; + + /* + ** If the infantry is undergoing some choreographed animation sequence, then + ** this holds the particular sequence number. The frame of animation is kept + ** track of by the regular frame tracking system. When performing an animation + ** sequence, the infantry cannot perform anything else (even move). + */ + DoType Doing; + + /* + ** Certain infantry will either perform some comment or say something after an + ** amount of time has expired subsequent to an significant event. This is the + ** timer the counts down. + */ + CDTimerClass Comment; + + /* + ** If this civilian is actually a technician, then this flag will be true. + ** It should only be set for the civilian type infantry. Typically, the + ** technician appears after a building is destroyed. + */ + unsigned IsTechnician:1; + + /* + ** If the infantry just performed some feat, then it may respond with an action. + ** This flag will be true if an action is to be performed when the Comment timer + ** has expired. + */ + unsigned IsStoked:1; + + /* + ** This flag indicates if the infantry unit is prone. Prone infantry become that way + ** when they are fired upon. Infantry in the prone position are less vulnerable to + ** combat. + */ + unsigned IsProne:1; + + /* + ** If the infantry is allowed to move one cell from one zone to another, then this + ** flag will be true. It exists only so that when a bridge is destroyed, the bomb + ** placer is allowed to run from the destroyed bridge cell back onto a real cell. + */ + unsigned IsZoneCheat:1; + + /* + ** This flag is set for the dogs, when they launch into bullet mode. + ** it's to remember if the unit was selected, and if it was, then + ** when the dog is re-enabled, he'll reselect himself. + */ + unsigned WasSelected:1; + + /* + ** The fear rating of this infantry unit. The more afraid the infantry, the more + ** likely it is to panic and seek cover. + */ + FearType Fear; + + /* + ** Track when movement last stopped. + */ + long StopDriverFrame; + + /* + ** Track the last cell we looked from. + */ + CELL LookCell; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + InfantryClass(InfantryType classid, HousesType house); + InfantryClass(NoInitClass const & x) : FootClass(x), Class(x), Comment(x) {}; + virtual ~InfantryClass(void); + operator InfantryType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Destination(TARGET); + + /* + ** Query functions. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + void const * Get_Image_Data(void) const; + int Shape_Number(WindowNumberType window = WINDOW_MAIN) const; + virtual ObjectTypeClass const & Class_Of(void) const; + virtual int Full_Name(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType facing); + virtual bool Paradrop(COORDINATE coord); + virtual bool Limbo(void); + virtual void Detach(TARGET target, bool all); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw = false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell) {FootClass::Active_Click_With(action, cell);} + + /* + ** Combat related. + */ + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual BulletClass * Fire_At(TARGET target, int which); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual void Assign_Target(TARGET); + void Set_Occupy_Bit(COORDINATE coord) {Set_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Set_Occupy_Bit(CELL cell, int spot_index); + void Clear_Occupy_Bit(COORDINATE coord) {Clear_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Clear_Occupy_Bit(CELL cell, int spot_index); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual void AI(void); + void Fear_AI(void); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual int Mission_Attack(void); + bool Edge_Of_World_AI(void); + void Firing_AI(void); + void Doing_AI(void); + void Movement_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "INFANTRY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Movement and animation. + */ + virtual bool Do_Action(DoType todo, bool force=false); + virtual bool Random_Animate(void); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + virtual void Look(bool incremental=false); + + /* + ** Translation table to convert facing into infantry shape number. This special + ** table is needed since several facing stages are reused and flipped about the Y + ** axis. + */ + static int const HumanShape[32]; + + private: + + static DoStruct const MasterDoControls[DO_COUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/INI.CPP b/REDALERT/INI.CPP new file mode 100644 index 000000000..c195c32b9 --- /dev/null +++ b/REDALERT/INI.CPP @@ -0,0 +1,1287 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * INIClass::Find_Entry -- Find specified entry within section. * + * INIClass::Find_Section -- Find the specified section within the INI data. * + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name* + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * INIClass::Load -- Load INI data from the file specified. * + * INIClass::Load -- Load the INI data from the data stream (straw). * + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * INIClass::Put_PKey -- Stores the key to the INI database. * + * INIClass::Put_String -- Output a string to the section and entry specified. * + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * INIClass::Save -- Save the ini data to the file specified. * + * INIClass::Save -- Saves the INI data to a pipe stream. * + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * INIClass::~INIClass -- Destructor for INI handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include "ini.h" +#include "readline.h" +#include "xpipe.h" +#include "b64pipe.h" +#include "xstraw.h" +#include "b64straw.h" + + +#ifdef FIXIT_FAST_LOAD +#include "cstraw.h" +#endif + + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +//#pragma warning 665 9 + + +/*********************************************************************************************** + * INIClass::~INIClass -- Destructor for INI handler. * + * * + * This is the destructor for the INI class. It handles deleting all of the allocations * + * it might have done. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::~INIClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * * + * This routine is used to clear out the section specified. If no section is specified, * + * then the entire INI data is cleared out. Optionally, this routine can be used to clear * + * out just an individual entry in the specified section. * + * * + * INPUT: section -- Pointer to the section to clear out [pass NULL to clear all]. * + * * + * entry -- Pointer to optional entry specifier. If this parameter is specified, * + * then only this specific entry (if found) will be cleared. Otherwise, * + * the entire section specified will be cleared. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 08/21/1996 JLB : Optionally clears section too. * + * 11/02/1996 JLB : Updates the index list. * + *=============================================================================================*/ +bool INIClass::Clear(char const * section, char const * entry) +{ + if (section == NULL) { + SectionList.Delete(); + SectionIndex.Clear(); + } else { + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + if (entry != NULL) { + INIEntry * entptr = secptr->Find_Entry(entry); + if (entptr != NULL) { + /* + ** Remove the entry from the entry index list. + */ + secptr->EntryIndex.Remove_Index(entptr->Index_ID()); + + delete entptr; + } + } else { + /* + ** Remove this section index from the section index list. + */ + SectionIndex.Remove_Index(secptr->Index_ID()); + + delete secptr; + } + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load INI data from the file specified. * + * * + * Use this routine to load the INI class with the data from the specified file. * + * * + * INPUT: file -- Reference to the file that will be used to fill up this INI manager. * + * * + * OUTPUT: bool; Was the file loaded successfully? * + * * + * WARNINGS: This routine allocates memory. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Load(FileClass & file) +{ + return(Load(FileStraw(file))); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load the INI data from the data stream (straw). * + * * + * This will fetch data from the straw and build an INI database from it. * + * * + * INPUT: straw -- The straw that the data will be provided from. * + * * + * OUTPUT: bool; Was the database loaded ok? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_FAST_LOAD +bool INIClass::Load(Straw & ffile) +#else +bool INIClass::Load(Straw & file) +#endif +{ + bool end_of_file = false; + char buffer[MAX_LINE_LENGTH]; + +#ifdef FIXIT_FAST_LOAD + CacheStraw file; + file.Get_From(ffile); +#endif + + /* + ** Prescan until the first section is found. + */ + while (!end_of_file) { + Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (end_of_file) return(false); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + } + + /* + ** Process a section. The buffer is prefilled with the section name line. + */ + while (!end_of_file) { + + buffer[0] = ' '; + char * ptr = strchr(buffer, ']'); + if (ptr) *ptr = '\0'; + strtrim(buffer); + INISection * secptr = new INISection(strdup(buffer)); + if (secptr == NULL) { + Clear(); + return(false); + } + + /* + ** Read in the entries of this section. + */ + while (!end_of_file) { + + /* + ** If this line is the start of another section, then bail out + ** of the entry loop and let the outer section loop take + ** care of it. + */ + int len = Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + + /* + ** Determine if this line is a comment or blank line. Throw it out if it is. + */ + Strip_Comments(buffer); + if (len == 0 || buffer[0] == ';' || buffer[0] == '=') continue; + + /* + ** The line isn't an obvious comment. Make sure that there is the "=" character + ** at an appropriate spot. + */ + char * divider = strchr(buffer, '='); + if (!divider) continue; + + /* + ** Split the line into entry and value sections. Be sure to catch the + ** "=foobar" and "foobar=" cases. These lines are ignored. + */ + *divider++ = '\0'; + strtrim(buffer); + if (!strlen(buffer)) continue; + + strtrim(divider); + if (!strlen(divider)) continue; + + INIEntry * entryptr = new INIEntry(strdup(buffer), strdup(divider)); + if (entryptr == NULL) { + delete secptr; + Clear(); + return(false); + } + + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + secptr->EntryList.Add_Tail(entryptr); + } + + /* + ** All the entries for this section have been parsed. If this section is blank, then + ** don't bother storing it. + */ + if (secptr->EntryList.Is_Empty()) { + delete secptr; + } else { + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + SectionList.Add_Tail(secptr); + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Save -- Save the ini data to the file specified. * + * * + * Use this routine to save the ini data to the file specified. All existing data in the * + * file, if it was present, is replaced. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * OUTPUT: bool; Was the data written to the file? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(FileClass & file) const +{ + return(Save(FilePipe(file))); +} + + +/*********************************************************************************************** + * INIClass::Save -- Saves the INI data to a pipe stream. * + * * + * This routine will output the data of the INI file to a pipe stream. * + * * + * INPUT: pipe -- Reference to the pipe stream to pump the INI image to. * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(Pipe & pipe) const +{ + int total = 0; + + INISection * secptr = SectionList.First(); + while (secptr && secptr->Is_Valid()) { + + /* + ** Output the section identifier. + */ + total += pipe.Put("[", 1); + total += pipe.Put(secptr->Section, strlen(secptr->Section)); + total += pipe.Put("]", 1); + total += pipe.Put("\r\n", strlen("\r\n")); + + /* + ** Output all the entries and values in this section. + */ + INIEntry * entryptr = secptr->EntryList.First(); + while (entryptr && entryptr->Is_Valid()) { + total += pipe.Put(entryptr->Entry, strlen(entryptr->Entry)); + total += pipe.Put("=", 1); + total += pipe.Put(entryptr->Value, strlen(entryptr->Value)); + total += pipe.Put("\r\n", strlen("\r\n")); + + entryptr = entryptr->Next(); + } + + /* + ** After the last entry in this section, output an extra + ** blank line for readability purposes. + */ + total += pipe.Put("\r\n", strlen("\r\n")); + + secptr = secptr->Next(); + } + total += pipe.End(); + + return(total); +} + + +/*********************************************************************************************** + * INIClass::Find_Section -- Find the specified section within the INI data. * + * * + * This routine will scan through the INI data looking for the section specified. If the * + * section could be found, then a pointer to the section control data is returned. * + * * + * INPUT: section -- The name of the section to search for. Don't enclose the name in * + * brackets. Case is NOT sensitive in the search. * + * * + * OUTPUT: Returns with a pointer to the INI section control structure if the section was * + * found. Otherwise, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +INIClass::INISection * INIClass::Find_Section(char const * section) const +{ + if (section != NULL) { + + long crc = CRCEngine()(section, strlen(section)); + + if (SectionIndex.Is_Present(crc)) { + return(SectionIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * * + * This routine will scan through all the sections in the INI data and return a count * + * of the number it found. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of sections recorded in the INI data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Section_Count(void) const +{ + return(SectionIndex.Count()); +} + + +/*********************************************************************************************** + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * * + * This routine will examine the section specified and return with the number of entries * + * associated with it. * + * * + * INPUT: section -- Pointer to the section that will be examined. * + * * + * OUTPUT: Returns with the number entries in the specified section. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Entry_Count(char const * section) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->EntryIndex.Count()); + } + return(0); +} + + +/*********************************************************************************************** + * INIClass::Find_Entry -- Find specified entry within section. * + * * + * This support routine will find the specified entry in the specified section. If found, * + * a pointer to the entry control structure will be returned. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- Pointer to the entry name to search for. * + * * + * OUTPUT: If the entry was found, then a pointer to the entry control structure will be * + * returned. Otherwise, NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::Find_Entry(char const * section, char const * entry) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->Find_Entry(entry)); + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name. * + * * + * This will return the identifier name for the entry under the section specified. The * + * ordinal number specified is used to determine which entry to retrieve. The entry * + * identifier is the text that appears to the left of the "=" character. * + * * + * INPUT: section -- The section to use. * + * * + * index -- The ordinal number to use when fetching an entry name. * + * * + * OUTPUT: Returns with a pointer to the entry name. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +char const * INIClass::Get_Entry(char const * section, int index) const +{ + INISection * secptr = Find_Section(section); + + if (secptr != NULL && index < secptr->EntryIndex.Count()) { + INIEntry * entryptr = secptr->EntryList.First(); + + while (entryptr != NULL && entryptr->Is_Valid()) { + if (index == 0) return(entryptr->Entry); + index--; + entryptr = entryptr->Next(); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * * + * Use this routine to store an arbitrary length binary block of data into the INI database.* + * This routine will covert the data into displayable form and then break it into lines * + * that are stored in sequence to the section. A section used to store data in this * + * fashion can not be used for any other entries. * + * * + * INPUT: section -- The section identifier to place the data into. * + * * + * block -- Pointer to the block of binary data to store. * + * * + * len -- The length of the binary data. * + * * + * OUTPUT: bool; Was the data stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_UUBlock(char const * section, void const * block, int len) +{ + if (section == NULL || block == NULL || len < 1) return(false); + + Clear(section); + + BufferStraw straw(block, len); + Base64Straw bstraw(Base64Straw::ENCODE); + bstraw.Get_From(straw); + + int counter = 1; + + for (;;) { + char buffer[71]; + char sbuffer[32]; + + int length = bstraw.Get(buffer, sizeof(buffer)-1); + buffer[length] = '\0'; + if (length == 0) break; + + sprintf(sbuffer, "%d", counter); + Put_String(section, sbuffer, buffer); + counter++; + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * * + * This routine will take all the entries in the specified section and decompose them into * + * a binary block of data that will be stored into the buffer specified. By using this * + * routine [and the Put_UUBLock counterpart], arbitrary blocks of binary data may be * + * stored in the INI file. A section processed by this routine can contain no other * + * entries than those put there by a previous call to Put_UUBlock. * + * * + * INPUT: section -- The section name to process. * + * * + * block -- Pointer to the buffer that will hold the retrieved data. * + * * + * len -- The length of the buffer. The retrieved data will not fill past this * + * limit. * + * * + * OUTPUT: Returns with the number of bytes decoded into the buffer specified. * + * * + * WARNINGS: If the number of bytes retrieved exactly matches the length of the buffer * + * specified, then you might have a condition of buffer "overflow". * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_UUBlock(char const * section, void * block, int len) const +{ + if (section == NULL) return(0); + + Base64Pipe b64pipe(Base64Pipe::DECODE); + BufferPipe bpipe(block, len); + + b64pipe.Put_To(&bpipe); + + int total = 0; + int counter = Entry_Count(section); + for (int index = 0; index < counter; index++) { + char buffer[128]; + + int length = Get_String(section, Get_Entry(section, index), "=", buffer, sizeof(buffer)); + int outcount = b64pipe.Put(buffer, length); + total += outcount; + } + total += b64pipe.End(); + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * * + * This routine will take an arbitrarily long block of text and store it into the INI * + * database. The text is broken up into lines and each line is then stored as a numbered * + * entry in the specified section. A section used to store text in this way can not be used * + * to hold any other entries. The text is presumed to contain space characters scattered * + * throughout it and that one space between words and sentences is natural. * + * * + * INPUT: section -- The section to place the text block into. * + * * + * text -- Pointer to a null terminated text string that holds the block of * + * text. The length can be arbitrary. * + * * + * OUTPUT: bool; Was the text block placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_TextBlock(char const * section, char const * text) +{ + if (section == NULL) return(false); + + Clear(section); + + int index = 1; + while (text != NULL && *text != NULL) { + + char buffer[128]; + + strncpy(buffer, text, 75); + buffer[75] = '\0'; + + char b[32]; + sprintf(b, "%d", index); + + /* + ** Scan backward looking for a good break position. + */ + int count = strlen(buffer); + if (count > 0) { + if (count >= 75) { + while (count) { + char c = buffer[count]; + + if (isspace(c)) break; + count--; + } + + if (count == 0) { + break; + } else { + buffer[count] = '\0'; + } + } + + strtrim(buffer); + Put_String(section, b, buffer); + index++; + text = ((char *)text) + count; + } else { + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * * + * This will take all entries in the specified section and format them into a block of * + * normalized text. That is, text with single spaces between each concatenated line. All * + * entries in the specified section are processed by this routine. Use Put_TextBlock to * + * build the entries in the section. * + * * + * INPUT: section -- The section name to process. * + * * + * buffer -- Pointer to the buffer that will hold the complete text. * + * * + * len -- The length of the buffer specified. The text will, at most, fill this * + * buffer with the last character being forced to null. * + * * + * OUTPUT: Returns with the number of characters placed into the buffer. The trailing null * + * is not counted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_TextBlock(char const * section, char * buffer, int len) const +{ + if (len <= 0) return(0); + + buffer[0] = '\0'; + if (len <= 1) return(0); + + int elen = Entry_Count(section); + int total = 0; + for (int index = 0; index < elen; index++) { + + /* + ** Add spacers between lines of fetched text. + */ + if (index > 0) { + *buffer++ = ' '; + len--; + total++; + } + + Get_String(section, Get_Entry(section, index), "", buffer, len); + + int partial = strlen(buffer); + total += partial; + buffer += partial; + len -= partial; + if (len <= 1) break; + } + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * * + * Use this routine to store an integer value into the section and entry specified. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier used for the integer number. * + * * + * number -- The integer number to store in the database. * + * * + * format -- The format to store the integer. The format is generally only a * + * cosmetic affect. The Get_Int operation will interpret the value the * + * same regardless of what format was used to store the integer. * + * * + * 0 : plain decimal digit * + * 1 : hexadecimal digit (trailing "h") * + * 2 : hexadecimal digit (leading "$") * + * * + * OUTPUT: bool; Was the number stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +bool INIClass::Put_Int(char const * section, char const * entry, int number, int format) +{ + char buffer[MAX_LINE_LENGTH]; + + switch (format) { + default: + case 0: + sprintf(buffer, "%d", number); + break; + + case 1: + sprintf(buffer, "%Xh", number); + break; + + case 2: + sprintf(buffer, "$%X", number); + break; + } + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * * + * This routine will fetch an integer value from the entry and section specified. If no * + * entry could be found, then the default value will be returned instead. * + * * + * INPUT: section -- The section name to search under. * + * * + * entry -- The entry name to search for. * + * * + * defvalue -- The default value to use if the specified entry could not be found. * + * * + * OUTPUT: Returns with the integer value specified in the INI database or else returns the * + * default value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +int INIClass::Get_Int(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + + if (*entryptr->Value == '$') { + sscanf(entryptr->Value, "$%x", &defvalue); + } else { + if (tolower(entryptr->Value[strlen(entryptr->Value)-1]) == 'h') { + sscanf(entryptr->Value, "%xh", &defvalue); + } else { + defvalue = atoi(entryptr->Value); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * * + * This routine is similar to the Put_Int routine, but the number is stored as a hexadecimal* + * number. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier to tag to the integer number specified. * + * * + * number -- The number to assign the the specified entry and placed in the * + * specified section. * + * * + * OUTPUT: bool; Was the number placed into the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Hex(char const * section, char const * entry, int number) +{ + char buffer[MAX_LINE_LENGTH]; + + sprintf(buffer, "%X", number); + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. The * + * value is interpreted as a hexadecimal number and then returned. If no entry could be * + * found, then the default value is returned instead. * + * * + * INPUT: section -- The section identifier to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the integer value from the specified section and entry. If no entry * + * could be found, then the default value will be returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_Hex(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + sscanf(entryptr->Value, "%x", &defvalue); + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_String -- Output a string to the section and entry specified. * + * * + * This routine will put an arbitrary string to the section and entry specified. Any * + * previous matching entry will be replaced. * + * * + * INPUT: section -- The section identifier to place the string under. * + * * + * entry -- The entry identifier to identify this string [placed under the section]* + * * + * string -- Pointer to the string to assign to this entry. * + * * + * OUTPUT: bool; Was the entry assigned without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +bool INIClass::Put_String(char const * section, char const * entry, char const * string) +{ + if (section == NULL || entry == NULL) return(false); + + INISection * secptr = Find_Section(section); + + if (secptr == NULL) { + secptr = new INISection(strdup(section)); + if (secptr == NULL) return(false); + SectionList.Add_Tail(secptr); + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + } + + /* + ** Remove the old entry if found. + */ + INIEntry * entryptr = secptr->Find_Entry(entry); + if (entryptr != NULL) { + secptr->EntryIndex.Remove_Index(entryptr->Index_ID()); + delete entryptr; + } + + /* + ** Create and add the new entry. + */ + if (string != NULL && strlen(string) > 0) { + entryptr = new INIEntry(strdup(entry), strdup(string)); + + if (entryptr == NULL) { + return(false); + } + secptr->EntryList.Add_Tail(entryptr); + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * * + * This will retrieve the entire text to the right of the "=" character. The text is * + * found by finding a matching entry in the section specified. If no matching entry could * + * be found, then the default value will be stored in the output string buffer. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- If no entry could be found, then this text will be returned. * + * * + * buffer -- Output buffer to store the retrieved string into. * + * * + * size -- The size of the output buffer. The maximum string length that could * + * be retrieved will be one less than this length. This is due to the * + * forced trailing zero added to the end of the string. * + * * + * OUTPUT: Returns with the length of the string retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const +{ + /* + ** Verify that the parameters are nominally legal. + */ + if (section == NULL || entry == NULL) { + if (buffer != NULL && size > 0) { + buffer[0] = '\0'; + } + return(0); + } + + /* + ** Fetch the entry string if it is present. If not, then the normal default + ** value will be used as the entry value. + */ + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr) { + if (entryptr->Value) { + defvalue = entryptr->Value; + } + } + + /* + ** Fill in the buffer with the entry value and return with the length of the string. + */ + if (buffer == NULL || !size) { + return(0); + } else if (defvalue == NULL) { + buffer[0] = '\0'; + return(0); + } else if (buffer == defvalue) { + return(strlen(buffer)); + } else { + strncpy(buffer, defvalue, size); + buffer[size-1] = '\0'; + strtrim(buffer); + return(strlen(buffer)); + } +} + + +/*********************************************************************************************** + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * * + * Use this routine to place a boolean value into the INI database. The boolean value will * + * be stored as "yes" or "no". * + * * + * INPUT: section -- The section to place the entry and boolean value into. * + * * + * entry -- The entry identifier to tag to the boolean value. * + * * + * value -- The boolean value to place into the database. * + * * + * OUTPUT: bool; Was the boolean value placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Bool(char const * section, char const * entry, bool value) +{ + if (value) { + return(Put_String(section, entry, "yes")); + } else { + return(Put_String(section, entry, "no")); + } +} + + +/*********************************************************************************************** + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. If * + * one is found, the value is interpreted as a boolean value and then returned. In the case * + * of no matching entry, the default value will be returned instead. The boolean value * + * is interpreted using the standard boolean conventions. e.g., "Yes", "Y", "1", "True", * + * "T" are all consider to be a TRUE boolean value. * + * * + * INPUT: section -- The section to search under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if no matching entry could be located. * + * * + * OUTPUT: Returns with the boolean value of the specified section and entry. If no match * + * then the default boolean value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Get_Bool(char const * section, char const * entry, bool defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + switch (toupper(*entryptr->Value)) { + case 'Y': + case 'T': + case '1': + return(true); + + case 'N': + case 'F': + case '0': + return(false); + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * * + * This routine scans the supplied entry for the section specified. This is used for * + * internal database maintenance. * + * * + * INPUT: entry -- The entry to scan for. * + * * + * OUTPUT: Returns with a pointer to the entry control structure if the entry was found. * + * Otherwise it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::INISection::Find_Entry(char const * entry) const +{ + if (entry != NULL) { + int crc = CRCEngine()(entry, strlen(entry)); + if (EntryIndex.Is_Present(crc)) { + return(EntryIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_PKey -- Stores the key to the INI database. * + * * + * The key stored to the database will have both the exponent and modulus portions saved. * + * Since the fast key only requires the modulus, it is only necessary to save the slow * + * key to the database. However, storing the slow key stores the information necessary to * + * generate the fast and slow keys. Because public key encryption requires one key to be * + * completely secure, only store the fast key in situations where the INI database will * + * be made public. * + * * + * INPUT: key -- The key to store the INI database. * + * * + * OUTPUT: bool; Was the key stored to the database? * + * * + * WARNINGS: Store the fast key for public INI database availability. Store the slow key if * + * the INI database is secure. * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_PKey(PKey const & key) +{ + char buffer[512]; + + int len = key.Encode_Modulus(buffer); + Put_UUBlock("PublicKey", buffer, len); + + len = key.Encode_Exponent(buffer); + Put_UUBlock("PrivateKey", buffer, len); + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * * + * This routine will fetch the key from the INI database. The key fetched is controlled by * + * the parameter. There are two choices of key -- the fast or slow key. * + * * + * INPUT: fast -- Should the fast key be retrieved? The fast key has the advantage of * + * requiring only the modulus value. * + * * + * OUTPUT: Returns with the key retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey INIClass::Get_PKey(bool fast) const +{ + PKey key; + char buffer[512]; + + /* + ** When retrieving the fast key, the exponent is a known constant. Don't parse the + ** exponent from the database. + */ + if (fast) { + BigInt exp = PKey::Fast_Exponent(); + exp.DEREncode((unsigned char *)buffer); + key.Decode_Exponent(buffer); + } else { + Get_UUBlock("PrivateKey", buffer, sizeof(buffer)); + key.Decode_Exponent(buffer); + } + + Get_UUBlock("PublicKey", buffer, sizeof(buffer)); + key.Decode_Modulus(buffer); + + return(key); +} + + +/*********************************************************************************************** + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * * + * This routine will examine the section and entry specified and interpret the value * + * as if it were a fixed point number. The format of the fixed point number can be * + * percentage (e.g. 100%) or a floating point number (e.g., 1.7). * + * * + * INPUT: section -- Pointer to the section identifier to look under. * + * * + * entry -- Pointer to the entry identifier to examine. * + * * + * defvalue -- If the section and entry could not be found, then this value will * + * be returned. * + * * + * OUTPUT: Returns with the fixed point number that occurs at the section and entry * + * specified. If it could not be found, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +fixed INIClass::Get_Fixed(char const * section, char const * entry, fixed defvalue) const +{ + char buffer[128]; + fixed retval = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + retval = fixed(buffer); + } + return(retval); +} + + +/*********************************************************************************************** + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * * + * Use this routine to output a fixed point number to the database. The entry will be * + * placed in the section and entry specified. If there was any existing entry, it will * + * be replaced. * + * * + * INPUT: section -- Pointer to the section identifier. * + * * + * entry -- Pointer to the entry identifier to use for this value. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the data stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Fixed(char const * section, char const * entry, fixed value) +{ + return(Put_String(section, entry, value.As_ASCII())); +} + + +/*********************************************************************************************** + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * * + * This routine will scan the string (text line) supplied and if any comment portions are * + * found, they will be trimmed off. Leading and trailing blanks are also removed. * + * * + * INPUT: buffer -- Pointer to the null terminate string to be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void INIClass::Strip_Comments(char * buffer) +{ + if (buffer != NULL) { + char * comment = strchr(buffer, ';'); + if (comment) { + *comment = '\0'; + strtrim(buffer); + } + } +} diff --git a/REDALERT/INI.H b/REDALERT/INI.H new file mode 100644 index 000000000..50e04c895 --- /dev/null +++ b/REDALERT/INI.H @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/15/96 * + * * + * Last Update : May 15, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INI_H +#define INI_H + +#include +#include "listnode.h" +#include "pipe.h" +#include "wwfile.h" +#include "pk.h" +#include "fixed.h" +#include "crc.h" +#include "search.h" + +/* +** This is an INI database handler class. It handles a database with a disk format identical +** to the INI files commonly used by Windows. +*/ +class INIClass { + public: + INIClass(void) {} + ~INIClass(void); + + /* + ** Fetch and store INI data. + */ + bool Load(FileClass & file); + bool Load(Straw & file); + int Save(FileClass & file) const; + int Save(Pipe & file) const; + + /* + ** Erase all data within this INI file manager. + */ + bool Clear(char const * section = 0, char const * entry = 0); + + int Line_Count(char const * section) const; + bool Is_Loaded(void) const {return(!SectionList.Is_Empty());} + int Size(void) const; + bool Is_Present(char const * section, char const * entry = 0) const {if (entry == 0) return(Find_Section(section) != 0);return(Find_Entry(section, entry) != 0);} + + /* + ** Fetch the number of sections in the INI file or verify if a specific + ** section is present. + */ + int Section_Count(void) const; + bool Section_Present(char const * section) const {return(Find_Section(section) != NULL);} + + /* + ** Fetch the number of entries in a section or get a particular entry in a section. + */ + int Entry_Count(char const * section) const; + char const * Get_Entry(char const * section, int index) const; + + /* + ** Get the various data types from the section and entry specified. + */ + int Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const; + int Get_Int(char const * section, char const * entry, int defvalue=0) const; + int Get_Hex(char const * section, char const * entry, int defvalue=0) const; + bool Get_Bool(char const * section, char const * entry, bool defvalue=false) const; + int Get_TextBlock(char const * section, char * buffer, int len) const; + int Get_UUBlock(char const * section, void * buffer, int len) const; + PKey Get_PKey(bool fast) const; + fixed Get_Fixed(char const * section, char const * entry, fixed defvalue) const; + + /* + ** Put a data type to the section and entry specified. + */ + bool Put_Fixed(char const * section, char const * entry, fixed value); + bool Put_String(char const * section, char const * entry, char const * string); + bool Put_Hex(char const * section, char const * entry, int number); + bool Put_Int(char const * section, char const * entry, int number, int format=0); + bool Put_Bool(char const * section, char const * entry, bool value); + bool Put_TextBlock(char const * section, char const * text); + bool Put_UUBlock(char const * section, void const * block, int len); + bool Put_PKey(PKey const & key); + + protected: + enum {MAX_LINE_LENGTH=128}; + + /* + ** The value entries for the INI file are stored as objects of this type. + ** The entry identifier and value string are combined into this object. + */ + struct INIEntry : Node { + INIEntry(char * entry = 0, char * value = 0) : Entry(entry), Value(value) {} + ~INIEntry(void) {free(Entry);Entry = 0;free(Value);Value = 0;} + int Index_ID(void) const {return(CRCEngine()(Entry, strlen(Entry)));}; + + char * Entry; + char * Value; + }; + + /* + ** Each section (bracketed) is represented by an object of this type. All entries + ** subordinate to this section are attached. + */ + struct INISection : Node { + INISection(char * section) : Section(section) {} + ~INISection(void) {free(Section);Section = 0;EntryList.Delete();} + INIEntry * Find_Entry(char const * entry) const; + int Index_ID(void) const {return(CRCEngine()(Section, strlen(Section)));}; + + char * Section; + List EntryList; + IndexClassEntryIndex; + }; + + /* + ** Utility routines to help find the appropriate section and entry objects. + */ + INISection * Find_Section(char const * section) const; + INIEntry * Find_Entry(char const * section, char const * entry) const; + static void Strip_Comments(char * buffer); + + /* + ** This is the list of all sections within this INI file. + */ + List SectionList; + + IndexClass SectionIndex; +}; + + +#endif diff --git a/REDALERT/INIBIN.CPP b/REDALERT/INIBIN.CPP new file mode 100644 index 000000000..793ce6d08 --- /dev/null +++ b/REDALERT/INIBIN.CPP @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\inibin.cpv 4.40 04 Jul 1996 16:14:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIBIN.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : October 20, 1995 * + * * + * Last Update : November 7, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/REDALERT/INICODE.CPP b/REDALERT/INICODE.CPP new file mode 100644 index 000000000..705ab26c6 --- /dev/null +++ b/REDALERT/INICODE.CPP @@ -0,0 +1,363 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\inicode.cpv 4.38 03 Jul 1996 05:14:04 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INICODE.CPP * + * * + * Programmer : David R Dettmer * + * * + * Start Date : November 7, 1995 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef TOFIX + +void Get_Scenario_Digest(char * digest, char * buffer) +{ + char buf[128]; // Working string staging buffer. + char stage[sizeof(BigInt)*2]; + + char * stage_ptr = &stage[0]; + int len = strlen(buffer) + 2; + char * tbuffer = buffer + len; + + WWGetPrivateProfileString("DIGEST", NULL, NULL, tbuffer, sizeof(_staging_buffer)-len, buffer); + stage[0] = '\0'; + while (*tbuffer != '\0') { + + WWGetPrivateProfileString("DIGEST", tbuffer, NULL, buf, sizeof(buf)-1, buffer); + strcat(stage, buf); + tbuffer += strlen(tbuffer)+1; + } + + len = strlen(stage); + char * dbuffer = &stage[0]; + tbuffer = &stage[0]; + for (int index = 0; index < len/2; index++) { + int c; + if (isdigit(*tbuffer)) { + c = (*tbuffer) - '0'; + } else { + c = 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + c <<= 4; + if (isdigit(*tbuffer)) { + c |= (*tbuffer) - '0'; + } else { + c |= 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + *dbuffer++ = c; + } + + /* + ** Decode and decrypt the number. + */ + BigInt hash = 0; + hash.DERDecode((unsigned char*)stage); + + BigInt d; + d = d.Decode_ASCII(KEY_D); + BigInt n; + n = n.Decode_ASCII(KEY_N); + + hash = hash.exp_b_mod_c(d, n); + + memcpy(digest, &hash, 20); + + buffer = strstr(buffer, "[DIGEST]"); + if (buffer) { + *buffer = '\0'; + } +} + + +bool Read_Scenario_INI_Write_INB( char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char *binbuf; // Scenario.inb staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + char buf[256]; // Working string staging buffer. + char scenarioname[40]; + int len; + unsigned char val; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_staging_buffer; + memset(buffer, '\0', sizeof(_staging_buffer)); + + /* + ** Create scenario filename and read the file. + ** The previous routine verifies that the file is available. + */ + + sprintf(fname,"%s.INI",root); + CCFileClass file(fname); + + file.Read(buffer, sizeof(_staging_buffer)-1); + + /* + ** Fetch and slice off any message digest attached. + */ + char digest[20]; + Get_Scenario_Digest(digest, buffer); + + char real_digest[20]; + SHAEngine digest_engine; + digest_engine.Hash(buffer, strlen(buffer)); + digest_engine.Result(real_digest); + + if (memcmp(digest, real_digest, sizeof(real_digest)) != 0) { + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } + + sprintf(fname,"%s.INB",root); + file.Set_Name(fname); + file.Cache(16384); + file.Open(WRITE); + + unsigned long crc = Ini_Binary_Version(); + + file.Write( (char *)&crc, sizeof(crc) ); + + binbuf = (char *)Alloc( sizeof(_staging_buffer), MEM_NORMAL ); + + if (binbuf) { + Write_Bin_Init( binbuf, sizeof(_staging_buffer) ); + } else { + Print_Error_End_Exit( "Unable to alloc space for writing INB" ); + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Name", "", scenarioname, sizeof(scenarioname), buffer); + WWGetPrivateProfileString("Basic", "Intro", "x", Scen.IntroMovie, sizeof(Scen.IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", Scen.BriefMovie, sizeof(Scen.BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", Scen.WinMovie, sizeof(Scen.WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", Scen.LoseMovie, sizeof(Scen.LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", Scen.ActionMovie, sizeof(Scen.ActionMovie), buffer); + Scen.IsToCarryOver = WWGetPrivateProfileInt("Basic", "ToCarryOver", 0, buffer); + Scen.IsToInherit = WWGetPrivateProfileInt("Basic", "ToInherit", 0, buffer); + + Write_Bin_String( scenarioname, strlen(scenarioname), binbuf ); + Write_Bin_String( Scen.IntroMovie, strlen(Scen.IntroMovie), binbuf ); + Write_Bin_String( Scen.BriefMovie, strlen(Scen.BriefMovie), binbuf ); + Write_Bin_String( Scen.WinMovie, strlen(Scen.WinMovie), binbuf ); + Write_Bin_String( Scen.LoseMovie, strlen(Scen.LoseMovie), binbuf ); + Write_Bin_String( Scen.ActionMovie, strlen(Scen.ActionMovie), binbuf ); + + /* + ** Fetch the transition theme for this scenario. + */ + Scen.TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + Scen.TransitTheme = Theme.From_Name(buf); + + WWGetPrivateProfileString( "Basic", "Player", "Greece", buf, 127, buffer); + Scen.PlayerHouse = HouseTypeClass::From_Name(buf); + if (Scen.PlayerHouse >= HOUSE_MULTI1) { + Scen.PlayerHouse = HOUSE_GREECE; + } +// TCTC To Fix? +// Scen.CarryOverPercent = WWGetPrivateProfileInt( "Basic", "CarryOverMoney", 100, buffer); + Scen.CarryOverCap = WWGetPrivateProfileInt( "Basic", "CarryOverCap", -1, buffer); + Scen.Percent = WWGetPrivateProfileInt( "Basic", "Percent", 0, buffer); + + Write_Bin_Num( &Scen.TransitTheme, sizeof(Scen.TransitTheme), binbuf ); + Write_Bin_Num( &Scen.PlayerHouse, sizeof(Scen.PlayerHouse), binbuf ); + Write_Bin_Num( &Scen.CarryOverPercent, 1, binbuf ); + Write_Bin_Num( &Scen.CarryOverCap, 2, binbuf ); + Write_Bin_Num( &Scen.Percent, 1, binbuf ); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + Scen.CarryOverPercent = Cardinal_To_Fixed(100, Scen.CarryOverPercent); + + PlayerPtr = HouseClass::As_Pointer( Scen.PlayerHouse ); + assert(PlayerPtr != NULL); + PlayerPtr->IsHuman = true; + + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = MIN(Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent), (Scen.CarryOverCap * 100) ); + } else { + carryover = Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { + + Assign_Houses(); + } + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary(root, &ScenarioCRC)) { + return( false ); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + VesselClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &Scen.BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, 255, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + len = Write_Bin_Length( binbuf ); + if (len != -1) { + file.Write( binbuf, len ); + } + + Free( binbuf ); + file.Close(); + + return(true); +} + +#endif + + + + + + + + + diff --git a/REDALERT/INIT.CPP b/REDALERT/INIT.CPP new file mode 100644 index 000000000..b1ed212d6 --- /dev/null +++ b/REDALERT/INIT.CPP @@ -0,0 +1,3863 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INIT.CPP 8 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Bootstrap -- Perform the initial bootstrap procedure. * + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * Init_Color_Remaps -- Initialize the text remap tables. * + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * Init_Fonts -- Initialize all the game font pointers. * + * Init_Game -- Main game initialization routine. * + * Init_Heaps -- Initialize the game heaps and buffers. * + * Init_Keys -- Initialize the cryptographic keys. * + * Init_Mouse -- Initialize the mouse system. * + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * Init_Random -- Initializes the random-number generator * + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * Load_Recording_Values -- Loads recording values from recording file * + * Load_Title_Page -- Load the background art for the title page. * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#include "WSPIPX.h" +#include "internet.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX + +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include "ccdde.h" + +#include + +#ifdef DONGLE +#include "cbn_.h" +#endif + +#ifdef MPEGMOVIE // Denzil 6/25/98 +#include "mpgset.h" +#endif + +RemapControlType SidebarScheme; + +//#include "WolDebug.h" + +#ifdef CHEAT_KEYS +extern bool bNoMovies; +#endif + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool sequenced=false); +static void Init_Color_Remaps(void); +static void Init_Heaps(void); +static void Init_Expansion_Files(void); +static void Init_One_Time_Systems(void); +static void Init_Fonts(void); +static void Init_CDROM_Access(void); +static void Init_Bootstrap_Mixfiles(void); +static void Init_Secondary_Mixfiles(void); +static void Init_Mouse(void); +static void Bootstrap(void); +//static void Init_Authorization(void); +static void Init_Bulk_Data(void); +static void Init_Keys(void); + +extern int UnitBuildPenalty; + +extern "C" { +extern long RandNumb; +} +#ifndef WIN32 +static int UsePageFaultHandler = 1; // 1 = install PFH +#endif //WIN32 + +//extern int SimRandIndex; +void Init_Random(void); + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode + +bool Load_Recording_Values(CCFileClass & file); +bool Save_Recording_Values(CCFileClass & file); + +#ifdef WOLAPI_INTEGRATION +extern int WOL_Main(); +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ); +#endif + +extern bool Is_Mission_Counterstrike (char *file_name); + +/*********************************************************************************************** + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * * + * This loads and displays the prolog page that is displayed before the prolog movie * + * is played. This page is necessary because there is much loading that occurs before * + * the prolog movie is played and looking at a picture is better than looking at a blank * + * screen. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Load_Prolog_Page(void) +{ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen("PROLOG.PCX", &HidPage, (unsigned char*)CCPalette.Get_Data()); + HidPage.Blit(SeenPage); +#else + Load_Picture("PROLOG.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + HidPage.Blit(SeenPage); +#endif + CCPalette.Set(); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE! * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +#include "sha.h" +//#include +bool Init_Game(int , char * []) +{ + /* + ** Allocate the benchmark tracking objects only if the machine and + ** compile flags indicate. + */ + #ifdef CHEAT_KEYS + if (Processor() >= 2) { + Benches = new Benchmark [BENCH_COUNT]; + } + #endif + + /* + ** Initialize the encryption keys. + */ + Init_Keys(); + + /* + ** Bootstrap as much as possible before error-prone initializations are + ** performed. This bootstrap process will enable the error message + ** handler to function. + */ + Bootstrap(); + + //////////////////////////////////////// + // The editor needs to not start the mouse up. - 7/22/2019 JAS + if (!RunningFromEditor) + { + /* + ** Check for an initialize a working mouse pointer. Display error and bail if + ** no mouse driver is installed. + */ + Init_Mouse(); + } + /* + ** Initialize access to the CD-ROM and ensure that the CD is inserted. This can, and + ** most likely will, result in a visible prompt. + */ +#if (0) //PG + Init_CDROM_Access(); +#endif + + if (Special.IsFromInstall) { + Load_Prolog_Page(); + } + + /* + ** Register and cache any secondary mixfiles. + */ + Init_Secondary_Mixfiles(); + + /* + ** This is a special hack to initialize the heaps that must be in place before the + ** rules file is processed. These heaps should properly be allocated as a consequence + ** of processing the rules.ini file, but that is a bit beyond the capabilities of + ** the rule parser routine (currently). + */ + HouseTypes.Set_Heap(HOUSE_COUNT); + BuildingTypes.Set_Heap(STRUCT_COUNT); + AircraftTypes.Set_Heap(AIRCRAFT_COUNT); + InfantryTypes.Set_Heap(INFANTRY_COUNT); + BulletTypes.Set_Heap(BULLET_COUNT); + AnimTypes.Set_Heap(ANIM_COUNT); + UnitTypes.Set_Heap(UNIT_COUNT); + VesselTypes.Set_Heap(VESSEL_COUNT); + TemplateTypes.Set_Heap(TEMPLATE_COUNT); + TerrainTypes.Set_Heap(TERRAIN_COUNT); + OverlayTypes.Set_Heap(OVERLAY_COUNT); + SmudgeTypes.Set_Heap(SMUDGE_COUNT); + + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + // Heap init moved here from globals.cpp. ST - 5/20/2019 + CCPtr::Set_Heap(&Aircraft); + CCPtr::Set_Heap(&Anims); + CCPtr::Set_Heap(&Buildings); + CCPtr::Set_Heap(&Bullets); + CCPtr::Set_Heap(&Factories); + CCPtr::Set_Heap(&Houses); + CCPtr::Set_Heap(&Infantry); + CCPtr::Set_Heap(&Overlays); + CCPtr::Set_Heap(&Smudges); + CCPtr::Set_Heap(&Teams); + CCPtr::Set_Heap(&TeamTypes); + CCPtr::Set_Heap(&Templates); + CCPtr::Set_Heap(&Terrains); + CCPtr::Set_Heap(&Triggers); + CCPtr::Set_Heap(&TriggerTypes); + + CCPtr::Set_Heap(&HouseTypes); + CCPtr::Set_Heap(&BuildingTypes); + CCPtr::Set_Heap(&AircraftTypes); + CCPtr::Set_Heap(&InfantryTypes); + CCPtr::Set_Heap(&BulletTypes); + CCPtr::Set_Heap(&AnimTypes); + CCPtr::Set_Heap(&UnitTypes); + CCPtr::Set_Heap(&VesselTypes); + CCPtr::Set_Heap(&TemplateTypes); + CCPtr::Set_Heap(&TerrainTypes); + CCPtr::Set_Heap(&OverlayTypes); + CCPtr::Set_Heap(&SmudgeTypes); + + /* + ** Find and process any rules for this game. + */ + if (RuleINI.Load(CCFileClass("RULES.INI"), false)) { + Rule.Process(RuleINI); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Aftermath runtime change 9/29/98 + // This is safe to do, as only rules for aftermath units are included in this ini. + if (Is_Aftermath_Installed() == true) { + if (AftermathINI.Load(CCFileClass("AFTRMATH.INI"), false)) { + Rule.Process(AftermathINI); + } + } +#endif + + Session.MaxPlayers = Rule.MaxPlayers; + + /* + ** Initialize the game object heaps as well as other rules-dependant buffer allocations. + */ + Init_Heaps(); + + /* + ** Initialize the animation system. + */ + Anim_Init(); + +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } +#endif +#endif + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if( Using_DVD() ) + { + #ifdef MCIMPEG + MciMovie = new MCIMovie(MainWindow); + #endif + MpgSettings = new MPGSettings(NULL); //RawFileClass(CONFIG_FILE_NAME)); + } + #endif + + /* + ** Play the startup animation. + */ + if (!Special.IsFromInstall && !Special.IsFromWChat) { + VisiblePage.Clear(); +// Mono_Printf("Playing Intro\n"); + Play_Intro(); + memset(CurrentPalette, 0x01, 768); + WhitePalette.Set(); + } else { + memset(CurrentPalette, 0x01, 768); + } + + /* + ** Initialize the text remap tables. + */ + Init_Color_Remaps(); + + /* + ** Get authorization to access the game. + */ +// Init_Authorization(); +// Show_Mouse(); + + /* + ** Set the logic page to the seenpage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** If not automatically launching into the intro, then display the title + ** page while the bulk data is cached. + */ + if (!Special.IsFromInstall) { + Load_Title_Page(true); + + Hide_Mouse(); + Fancy_Text_Print(TXT_STAND_BY, 160*RESFACTOR, 120*RESFACTOR, &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + Show_Mouse(); + + CCPalette.Set(FADE_PALETTE_SLOW); + Call_Back(); + } + + /* + ** Initialize the bulk data. This takes the longest time and must be performed once + ** before the regular game starts. + */ + Init_Bulk_Data(); + + /* + ** Initialize the multiplayer score values + */ + Session.GamesPlayed = 0; + Session.NumScores = 0; + Session.CurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + Session.Score[i].Name[0] = '\0'; + Session.Score[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + Session.Score[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + GamePalette = CCPalette; +// InGamePalette = CCPalette; + OriginalPalette = CCPalette; + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + return(true); +} + +#ifdef WINSOCK_IPX // Steve Tall missed this one - ajw +extern bool Get_Broadcast_Addresses (void); +#endif + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +bool Select_Game(bool fade) +{ + // Enums in Select_Game() must match order of buttons in Main_Menu(). +#ifdef FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO_CS, // Expansion scenario to play. + SEL_NEW_SCENARIO_AM, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#else // FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + SEL_INTERNET, +#endif //WIN32 +//#if defined(MPEGMOVIE) // Denzil 6/25/98 +// SEL_MOVIESETTINGS, +//#endif + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#endif // FIXIT_VERSION_3 + + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + +#ifdef DONGLE + /* These where added by ColinM for the dongle checking */ + short iRet = 0; + unsigned short iPortNr = 1; /* automatic port scan enabled */ + unsigned char cSCodeSER[] = "\x41\x42"; + unsigned long ulIdRet = 0; + unsigned char cBoxName[]= "\x00\x00"; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int cdcheck = 0; + bool cs = Is_Counterstrike_Installed(); +#endif + +// #ifndef DVD // Denzil - We want the menu screen ajw No we don't +// if (Special.IsFromInstall) { +// display = false; +// } +// #endif + + Show_Mouse(); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; // Assume new units disabled, unless specifically .INI enabled or multiplayer negotiations enable it. +#endif + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + #ifdef MIRROR_QUEUE + MirrorList.Init(); + #endif + OutList.Init(); + Frame = 0; + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + PlayerWins = false; + PlayerLoses = false; + Session.ObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; + Session.DesiredFrameRate = 30; +#if(TIMING_FIX) + NewMaxAheadFrame1 = 0; + NewMaxAheadFrame2 = 0; +#endif + +/* ColinM added to check for dongle */ +#ifdef DONGLE + iRet = CbN_BoxReady( iPortNr , cBoxName); + if (cBoxName[0] != 0xc5 && cBoxName[0] != 0xc9) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } + + iRet = CbN_ReadSER( iPortNr, cSCodeSER, &ulIdRet); + if (ulIdRet != 0xa0095) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + Session.Score[i].Kills[Session.CurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (Session.Type == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + Theme.Queue_Song(THEME_CRUS); + + /* + ** If we're playing back a recording, load all pertinent values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (Session.Play && Session.RecordFile.Is_Available()) { + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else + Session.Play = false; + } + +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat) { + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } +#endif //WIN32 +#endif + + while (process) { + + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Page(); + GamePalette = CCPalette; + + HidPage.Blit(SeenPage); +// if (fade) { +// WhitePalette.Set(); +// CCPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// fade = false; +// } else { + CCPalette.Set(); +// } + + Set_Logic_Page(SeenBuff); + display = false; + Show_Mouse(); + } + else { + if (RunningAsDLL) { //PG + return true;; + } + } + + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall) selection = SEL_START_NEW_GAME; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat and our start game + ** packet has already arrived + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + } else { + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480) { + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } +#endif //WIN32 +#endif + +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + selection = SEL_MULTIPLAYER_GAME; // We are returning from a game. +#endif + + if (selection == SEL_NONE) { +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + + /* + ** Pick an expansion scenario. + */ +#ifdef FIXIT_VERSION_3 + case SEL_NEW_SCENARIO_CS: + case SEL_NEW_SCENARIO_AM: +#else // FIXIT_VERSION_3 + case SEL_NEW_SCENARIO: +#endif // FIXIT_VERSION_3 + Scen.CarryOverMoney = 0; + IsTanyaDead = false; + SaveTanya = false; + +#ifdef FIXIT_VERSION_3 + if( selection == SEL_NEW_SCENARIO_CS ) + { + if(!Force_CD_Available(2)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( true )){ + selection = SEL_NONE; + break; + } + } + else + { + if(!Force_CD_Available(3)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( false )){ + selection = SEL_NONE; + break; + } + } +#else // FIXIT_VERSION_3 + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cs) { + cdcheck = 2; + } + if (Is_Aftermath_Installed()) { + if (!cdcheck) { + cdcheck = 3; + } else { + cdcheck = 4; // special case: means check for 3 or 4 + } + } + if(!Force_CD_Available(cdcheck)) { + return(false); + } + #else + if(!Force_CD_Available(2)) { + return(false); + } + #endif + if(!Expansion_Dialog()){ + selection = SEL_NONE; + break; + } +#endif // FIXIT_VERSION_3 + +#ifdef FIXIT_DIFFICULTY + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch (Fetch_Difficulty(cdcheck >= 3)) { + #else + switch (Fetch_Difficulty()) { + #endif + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } +#endif + Theme.Fade_Out(); + Theme.Queue_Song(THEME_FIRST); + Session.Type = GAME_NORMAL; + process = false; + break; + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + if (Special.IsFromInstall) { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } else { + switch (Fetch_Difficulty()) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } + Scen.CarryOverMoney = 0; + BuildLevel = 10; + IsTanyaDead = false; + SaveTanya = false; + Whom = HOUSE_GOOD; + + if (!Special.IsFromInstall) { +#ifdef FIXIT_ANTS + if (AntsEnabled) { + Scen.Set_Scenario_Name("SCA01EA.INI"); + } else { +#endif +#ifdef FIXIT_VERSION_3 + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_CANCEL, TXT_SOVIET)) { + case 2: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + default: + selection = SEL_NONE; + continue; +#else + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_SOVIET)) { + case 1: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + default: +#endif + case 0: + Scen.Set_Scenario_Name("SCG01EA.INI"); + break; + + } +#ifdef FIXIT_ANTS + } +#endif + Theme.Fade_Out(); + Load_Title_Page(); + } else { + Theme.Fade_Out(); +#ifdef DVD // Denzil ajw Presumably a bug fix. + Choose_Side(); + Hide_Mouse(); +#else + Hide_Mouse(); + Choose_Side(); +#endif + if (CurrentCD == 0) { + Scen.Set_Scenario_Name("SCG01EA.INI"); + } else { + Scen.Set_Scenario_Name("SCU01EA.INI"); + } + } + + Session.Type = GAME_NORMAL; + process = false; + break; + +#ifndef FIXIT_VERSION_3 // Removed button from main menu. + #if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Internet game is requested + */ + case SEL_INTERNET: + /* + ** Only call up the internet menu code if we dont already have connect info from WChat + */ + if (!DDEServer.Get_MPlayer_Game_Info()) { + if (Do_The_Internet_Menu_Thang() && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + display = false; + Session.Type = GAME_INTERNET; + } else { + selection = SEL_NONE; + display = true; + } + } else { + Check_From_WChat(NULL); + display = false; + Session.Type = GAME_INTERNET; + selection = SEL_MULTIPLAYER_GAME; + } + break; + #endif //WIN32 +#endif + +// #if defined(MPEGMOVIE) // Denzil 6/25/98 +// case SEL_MOVIESETTINGS: +// MpgSettings->Dialog(); +// display = true; +// selection = SEL_NONE; +// break; +// #endif + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + Theme.Queue_Song(THEME_FIRST); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'Session.Type' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: +#ifdef WOLAPI_INTEGRATION + if( !pWolapi ) + { +#endif + switch (Session.Type) { + + /* + ** If 'Session.Type' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_SKIRMISH: +#if (0)//PG + if ( !Com_Scenario_Dialog(true) ) { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + else + { + // Ever hits? Session.Type set to GAME_SKIRMISH without user selecting in Select_MPlayer_Game()? +#ifdef FIXIT_VERSION_3 + // If mission is Counterstrike, CS CD will be required. But aftermath units require AM CD. + bAftermathMultiplayer = Is_Aftermath_Installed() && !Is_Mission_Counterstrike( Scen.ScenarioName ); + // ajw I'll bet this was needed before also... + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); +#endif + } +#endif//PG + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: +#if (0) + if ( Session.Type != GAME_SKIRMISH && NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((Session.Type == GAME_NULL_MODEM && + Session.ModemType == MODEM_NULL_HOST) || + (Session.Type == GAME_MODEM && + Session.ModemType == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } +#endif + break; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle being spawned from WChat. Internet play based on IPX code. + */ + case GAME_INTERNET: // ajw No longer hit. + if (Special.IsFromWChat) { + /* + ** Give myself focus. + */ + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + +#ifdef WINSOCK_IPX + + if (PacketTransport ) delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert ( PacketTransport != NULL); + + if (PacketTransport->Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + } else { + delete PacketTransport; + PacketTransport = NULL; + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + +#else //WINSOCK_IPX + + WWDebugString ("RA95 - About to initialise Winsock.\n"); + if (Winsock.Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + WWDebugString ("RA95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + if (Server) { + Winsock.Start_Server(); + } else { + Winsock.Start_Client(); + } + + + /* + ** Flush out any pending packets from a previous game. + */ + WWDebugString ("RA95 - About to flush packet queue.\n"); + WWDebugString ("RA95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + WWDebugString ("RA95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + WWDebugString ("RA95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)) { + + WWDebugString ("RA95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()) {}; + WWDebugString ("RA95 - Ready to check for more packets.\n"); + + } + WWDebugString ("RA95 - About to delete scrap memory.\n"); + delete temp_buffer; + + + + } else { + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()) { + WWDebugString ("RA95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + } else { + Read_Game_Options( "C&CSPAWN.INI" ); + } +#ifdef WINSOCK_IPX + WWDebugString ("RA95 - About to set addresses.\n"); + PacketTransport->Set_Broadcast_Address (PlanetWestwoodIPAddress); +#endif //WINSOCK_IPX + if (PlanetWestwoodIsHost) { + + WWDebugString ("RA95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()) { + WWDebugString ("RA95 - Server_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } else { + WWDebugString ("RA95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()) { + WWDebugString ("RA95 - Client_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } + + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + } +#ifdef WOLAPI_INTEGRATION + } // if( !pWolapi ) + + if( pWolapi ) + Session.Type = GAME_INTERNET; +#endif +//debugprint( "Session.Type = %i\n", Session.Type ); + switch (Session.Type) { + /* + ** Modem, Null-Modem or internet + */ + case GAME_MODEM: + case GAME_NULL_MODEM: +#ifndef WOLAPI_INTEGRATION + case GAME_INTERNET: +#endif + case GAME_SKIRMISH: + Theme.Fade_Out(); + process = false; +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + break; + +#ifdef WOLAPI_INTEGRATION // implies also WINSOCK_IPX + case GAME_INTERNET: + if( PacketTransport ) + delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert( PacketTransport != NULL ); + if( PacketTransport->Init() ) + { + switch( WOL_Main() ) + { + case 1: + // Start game. +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + break; + case 0: + // User cancelled. + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + break; + case -1: + // Patch was downloaded. Exit app. + Theme.Fade_Out(); + BlackPalette.Set( FADE_PALETTE_SLOW ); + return false; + } + } + else + { + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + } + break; +#endif + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + WWDebugString ("RA95 - Game type is IPX.\n"); + /* + ** Init network system & remote-connect + */ +#ifdef WINSOCK_IPX + if (PacketTransport ) delete PacketTransport; +// if (WWMessageBox().Process("Select a protocol to use for network play.", "UDP", "IPX")) { + PacketTransport = new IPXInterfaceClass; + assert ( PacketTransport != NULL); +// }else{ +// PacketTransport = new UDPInterfaceClass; //IPXInterfaceClass; +// assert ( PacketTransport != NULL); +// if (!Get_Broadcast_Addresses()) { +// Session.Type = GAME_NORMAL; +// display = true; +// selection = SEL_NONE; +// delete PacketTransport; +// PacketTransport = NULL; +// break; +// } +// } + +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + if (Session.Type == GAME_IPX && Init_Network() && Remote_Connect()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_NONE; +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#endif //WINSOCK_IPX + } + break; + +#if(TEN) + /* + ** TEN: jump straight into the game + */ + case GAME_TEN: + if (Init_TEN()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { + WWMessageBox().Process("Unable to initialize TEN!"); + //Prog_End(); + Emergency_Exit(1); + } + break; +#endif // TEN + +#if(MPATH) + /* + ** MPATH: jump straight into the game + */ + case GAME_MPATH: + if (Init_MPATH()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { + WWMessageBox().Process("Unable to initialize MPATH!"); + //Prog_End(); + Emergency_Exit(1); + } + break; +#endif // MPATH + + } + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + if (Debug_Flag) { + Play_Intro(Debug_Flag); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, true); // no transition picture to briefing + Keyboard->Clear(); + Play_Movie(VQ_SIZZLE, THEME_NONE, true); + Play_Movie(VQ_SIZZLE2, THEME_NONE, true); +// Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); // has transitino picture to briefing + } + Theme.Queue_Song(THEME_CRUS); + display = true; + fade = true; + selection = SEL_NONE; + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: + Theme.Fade_Out(); + BlackPalette.Set(FADE_PALETTE_SLOW); + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (Session.Attract && Session.RecordFile.Is_Available()) { + Session.Play = true; + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else { + Session.Play = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode to load scenario + */ + Scen.Set_Scenario_Name("SCG01EA.INI"); + } + + /* + ** Don't carry stray keystrokes into game. + */ + Keyboard->Clear(); + + /* + ** Initialize the random number generator(s) + */ + Init_Random(); + + /* + ** Save initialization values if we're recording this game. + */ + if (Session.Record) { + if (Session.RecordFile.Open(WRITE)) { + Save_Recording_Values(Session.RecordFile); + } else { + Session.Record = false; + } + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch(Session.Type) { + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: +#ifdef FIXIT_VERSION_3 + if( !bAftermathMultiplayer ) { +#else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS) { +#endif + NewUnitsEnabled = SecretUnitsEnabled = false; + } else { + NewUnitsEnabled = true; + } +// debugprint( "Non Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); + break; + case GAME_INTERNET: +#if (0) + if( !pWolapi ) + { +// debugprint( "pWolapi is null on internet game!" ); + Fatal( "pWolapi is null on internet game!" ); + } + //if( pWolapi->bEnableNewAftermathUnits ) + if( bAftermathMultiplayer ) + NewUnitsEnabled = true; + else + NewUnitsEnabled = SecretUnitsEnabled = false; +// debugprint( "Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); +#endif + break; + default: + break; + } +#endif + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded && !Session.LoadGame) { +// if (Debug_Map) { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, SCEN_VAR_A); +// } else { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir); +// } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + } + Show_Mouse(); +//Mono_Printf("About to call Start Scenario with %s\n", Scen.ScenarioName); + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + if (Special.IsFromInstall) Show_Mouse(); + Special.IsFromInstall = false; + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + Session.Messages.Init( + Map.TacPixelX, Map.TacPixelY, // x,y for messages + 6, // max # msgs + MAX_MESSAGE_LENGTH-14, // max msg length + 7 * RESFACTOR, // font height in pixels + -1, -1, // x,y for edit line (appears above msgs) + 0,//BG 1, // enable edit overflow + 20, // min, + MAX_MESSAGE_LENGTH - 14, // max for trimming overflow +#ifdef WIN32 + Lepton_To_Pixel(Map.TacLeptonWidth)); // Width in pixels of buffer +#else + (320-SIDEBAR_WID)); // Width in pixels of buffer +#endif + + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + !Session.Play) { + if (Session.Type == GAME_TEN) { +#if(TEN) + Session.Create_TEN_Connections(); +#endif // TEN + } else if (Session.Type == GAME_MPATH) { +#if(MPATH) + Session.Create_MPATH_Connections(); +#endif + } else { + Session.Create_Connections(); + } + } + + + /* + ** If this isnt an internet game that set the unit build rate to its default value + */ + if (Session.Type != GAME_INTERNET){ + UnitBuildPenalty = 100; + } + + /* + ** Hide the SeenPage; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + Call_Back(); + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + Show_Mouse(); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } +#endif //WIN32 + Map.Flag_To_Redraw(); + Call_Back(); + Map.Render(); + +#ifdef WOLAPI_INTEGRATION + + //ajw debugging only +// debugprint( "Debugging Session...\n" ); +// debugprint( "Session.Players count is %i.\n", Session.Players.Count() ); + for (i = 0; i < Session.Players.Count(); i++) + { + NetNumType net; + NetNodeType node; + Session.Players[i]->Address.Get_Address( net, node ); +// debugprint( "Player %i, %s, color %i, ip %i.%i.%i.%i.%i.%i\n", i, Session.Players[i]->Name, +// Session.Players[i]->Player.Color, node[0], node[1], node[2], node[3], node[4], node[5] ); + } +// debugprint( "PlanetWestwoodPortNumber is %i\n", PlanetWestwoodPortNumber ); + +#endif + + return(true); +} + + +/*********************************************************************************************** + * Play_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + * 05/08/1996 JLB : Modified for Red Alert and direction control. * + *=============================================================================================*/ +static void Play_Intro(bool sequenced) +{ + static VQType _counter = VQ_FIRST; + + Keyboard->Clear(); + if (sequenced) { + if (_counter <= VQ_FIRST) _counter = VQ_COUNT; + if (_counter == VQ_COUNT) _counter--; + if (_counter == VQ_REDINTRO) _counter--; + if (_counter == VQ_TITLE) _counter--; + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQType(_counter--), THEME_NONE); + +// Show_Mouse(); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef WIN32 + Play_Movie(VQ_REDINTRO, THEME_NONE, false); +#else + Play_Movie(VQ_TITLE, THEME_NONE, false); +#endif + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ + +#ifdef WIN32 +#ifdef MOVIE640 +//GraphicBufferClass VQ640(711, 400, (void *)NULL); +#endif +#endif +void Anim_Init(void) +{ +#if (0) +#ifdef WIN32 + + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; +//AnimControl.DrawFlags |= VQACFGF_NODRAW; +//BG - M. Grayford says turn this off AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); +#ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 711; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } +#endif + AnimControl.Vmode = 0; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } + AnimControl.SoundObject = SoundObject; + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#else //WIN32 + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = MCGA_MODE; + AnimControl.VBIBit = VertBlank; + AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_HMIINIT|VQAOPTF_CAPTIONS|VQAOPTF_EVA; +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + AnimControl.DigiCard = NewConfig.DigitCard; + AnimControl.HMIBufSize = 8192; + AnimControl.DigiHandle = Get_Digi_Handle(); + AnimControl.Volume = 0x00FF; + AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } + + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#endif //WIN32 +#endif +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char * argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + char arg_string[512]; + int str_len = strlen(argv[index]); + char *src = argv[index]; + char *dest = arg_string; + for (int i=0 ; i key during the first movie run. +// BreakoutAllowed = false; + break; + +#if(TEN) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + +#if(MPATH) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } + +#if (0) + /* + ** Build speed modifier + */ + if (strstr (string, "-UNITRATE:")){ + int unit_rate; + sscanf (string, "-UNITRATE:%d", &unit_rate); + UnitBuildPenalty = unit_rate; + } +#endif //(0) + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8, "."); + while (p) { + int x; + + sscanf(p, "%x", &x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL, "."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + Session.NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + Session.NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + Session.Attract = true; + continue; + } + + +#ifdef WIN32 + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string, "-480")) { + ScreenHeight = 480; + continue; + } + + /* + ** Check for spawn from WChat + */ +#ifndef FIXIT_VERSION_3 // WChat eliminated. + if (strstr(string,"-WCHAT")){ + SpawnedFromWChat = true; + } +#endif + +#endif + +#ifdef CHEAT_KEYS + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } + +#ifndef WIN32 + /* + ** Don't install Page Fault Handler (MUST use this for debugger) + */ + if (stricmp(string, "-NOPFS") == 0) { + UsePageFaultHandler = 0; + continue; + } +#endif + +#endif + + +#if(TEN) + /* + ** Enable TEN + */ + if (strstr(string, "TEN")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_TEN; + Special.IsFromInstall = false; + // + // Create the Ten network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + Ten = new TenConnManClass(); + Ten->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + Ten->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // TEN + +#if(MPATH) + /* + ** Enable MPATH + */ + if (strstr(string, "MPATH")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_MPATH; + Special.IsFromInstall = false; + // + // Create the MPath network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + MPath = new MPlayerManClass(); + MPath->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + MPath->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // MPATH + + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + + /* + ** look for passed-in video mode to default to + */ +#ifndef WIN32 + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif + +#ifdef CHEAT_KEYS + if (strstr(string,"-NOMOVIES")){ + bNoMovies = true; + } +#endif + + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + MonoClass::Enable(); + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; + + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + Session.Record = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + Session.Play = 1; + break; + + /* + ** Print lots of debug stuff about events & packets + */ + case 'P': + Debug_Print_Events = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + + default: + puts(TEXT_INVALID); + return(false); + } + + } + + continue; + } + } + return(true); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * This algorithm is cryptographically categorized as a "one way hash". * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cryptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[1024]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + int index; + for (index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + +/*********************************************************************************************** + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * * + * This routine is used to create a hash value from a data block. The algorithm is similar * + * to a CRC, but is faster. * + * * + * INPUT: buffer -- Pointer to a buffer of data to be 'hashed'. * + * * + * len -- The length of the buffer to compute the hash upon. * + * * + * OUTPUT: Returns with a 32bit hash value calculated from the specified buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +extern "C" { +long Calculate_CRC(void * buffer, long len) +{ + return(CRCEngine()(buffer, len)); +} +} + + +/*************************************************************************** + * Init_Random -- Initializes the random-number generator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +void Init_Random(void) +{ + #ifdef WIN32 + + /* + ** Gather some "random" bits from the system timer. Actually, only the + ** low order millisecond bits are secure. The other bits could be + ** easily guessed from the system clock (most clocks are fairly accurate + ** and thus predictable). + */ + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + CryptRandom.Seed_Bit(t.wSecond); + CryptRandom.Seed_Bit(t.wSecond>>1); + CryptRandom.Seed_Bit(t.wSecond>>2); + CryptRandom.Seed_Bit(t.wSecond>>3); + CryptRandom.Seed_Bit(t.wSecond>>4); + CryptRandom.Seed_Bit(t.wMinute); + CryptRandom.Seed_Bit(t.wMinute>>1); + CryptRandom.Seed_Bit(t.wMinute>>2); + CryptRandom.Seed_Bit(t.wMinute>>3); + CryptRandom.Seed_Bit(t.wMinute>>4); + CryptRandom.Seed_Bit(t.wHour); + CryptRandom.Seed_Bit(t.wDay); + CryptRandom.Seed_Bit(t.wDayOfWeek); + CryptRandom.Seed_Bit(t.wMonth); + CryptRandom.Seed_Bit(t.wYear); + #else + + /* + ** Gather some "random" bits from the DOS mode timer. + */ + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + CryptRandom.Seed_Byte(t.time); + #endif + +#ifdef FIXIT_MULTI_SAVE + // + // If we've loaded a multiplayer save game, return now; the random # + // class is loaded along with ScenarioClass. + // + if (Session.LoadGame) { + return; + } + + // + // If we're playing a recording, the Seed is loaded in + // Load_Recording_Values(). Just init the random # and return. + // + if (Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#else + /* + ** Do nothing if we've loaded a multiplayer game, or we're playing back + ** a recording; the random number generator is initialized by loading + ** the game. + */ + if (Session.LoadGame || Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#endif // FIXIT_MULTI_SAVE + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH && + !Session.Play) { + + /* + ** Set the optional user-specified seed + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } else { + srand(time(NULL)); + Seed = rand(); + } + } + + /* + ** Initialize the random-number generators + */ + Scen.RandomNumber = Seed; + RandNumb = Seed; +} + + +/*********************************************************************************************** + * Load_Title_Page -- Load the background art for the title page. * + * * + * This routine will load the background art in a machine independent format. There is * + * different art required for the hi-res and lo-res versions of the game. * + * * + * INPUT: visible -- Should the title page art be copied to the visible page by this * + * routine? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the mouse is hidden if the image is to be copied to the visible page. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +void Load_Title_Page(bool visible) +{ +#ifdef WIN32 + Load_Title_Screen("TITLE.PCX", &HidPage, (unsigned char*)CCPalette.Get_Data()); + if (visible) { + HidPage.Blit(SeenPage); + } +#else + Load_Picture("TITLE.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + if (visible) { + HidPage.Blit(SeenPage); + } +#endif +} + + +/*********************************************************************************************** + * Init_Color_Remaps -- Initialize the text remap tables. * + * * + * There are various color scheme remap tables that are dependant upon the color remap * + * information embedded within the palette control file. This routine will fetch that * + * data and build the text remap tables as indicated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + * 09/11/2019 ST : The default resolution doesn't have to match the size of the palette image* + *=============================================================================================*/ +static void Init_Color_Remaps(void) +{ + /* + ** Setup the remap tables. PALETTE.CPS contains a special set of pixels in + ** the upper-left corner. Each row of 16 pixels is one range of colors. The + ** first row represents unity (the default color units are drawn in); rows + ** after that are the remap colors. + */ + +#ifdef WIN32 + GraphicBufferClass temp_page(320, 200, (void*)NULL); + temp_page.Clear(); + Load_Picture("PALETTE.CPS", temp_page, temp_page, NULL, BM_DEFAULT); + temp_page.Blit(HidPage); +#else + Load_Picture("PALETTE.CPS", HidPage, HidPage, NULL, BM_DEFAULT); +#endif + for (PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++) { + + unsigned char * ptr = ColorRemaps[pcolor].RemapTable; + + for (int color = 0; color < 256; color++) { + ptr[color] = color; + } + + int index; + for (index = 0; index < 16; index++) { + ptr[HidPage.Get_Pixel(index, 0)] = HidPage.Get_Pixel(index, pcolor); + } + for (index = 0; index < 6; index++) { + ColorRemaps[pcolor].FontRemap[10+index] = HidPage.Get_Pixel(2+index, pcolor); + } + ColorRemaps[pcolor].BrightColor = WHITE; +// ColorRemaps[pcolor].BrightColor = HidPage.Get_Pixel(1, pcolor); + ColorRemaps[pcolor].Color = HidPage.Get_Pixel(4, pcolor); + + ColorRemaps[pcolor].Shadow = HidPage.Get_Pixel(10, pcolor); + ColorRemaps[pcolor].Background = HidPage.Get_Pixel(9, pcolor); + ColorRemaps[pcolor].Corners = HidPage.Get_Pixel(7, pcolor); + ColorRemaps[pcolor].Highlight = HidPage.Get_Pixel(4, pcolor); + ColorRemaps[pcolor].Bright = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Underline = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Bar = HidPage.Get_Pixel(6, pcolor); + + /* + ** This must grab from column 4 because the multiplayer color dialog palette counts + ** on this to be true. + */ + ColorRemaps[pcolor].Box = HidPage.Get_Pixel(4, pcolor); + } + + /* 12/9/2019 SKY - Swap Blue and Grey color remaps */ + { + RemapControlType temp; + memcpy(&temp, &ColorRemaps[PCOLOR_BLUE], sizeof(RemapControlType)); + memcpy(&ColorRemaps[PCOLOR_BLUE], &ColorRemaps[PCOLOR_GREY], sizeof(RemapControlType)); + memcpy(&ColorRemaps[PCOLOR_GREY], &temp, sizeof(RemapControlType)); + } + + /* + ** Now do the special dim grey scheme + */ + for (int color = 0; color < 256; color++) { + GreyScheme.RemapTable[color] = color; + } + for (int index = 0; index < 6; index++) { + GreyScheme.FontRemap[10+index] = HidPage.Get_Pixel(9+index, PCOLOR_GREY) & 0x00FF; + } + GreyScheme.BrightColor = HidPage.Get_Pixel(3, PCOLOR_GREY) & 0x00FF; + GreyScheme.Color = HidPage.Get_Pixel(7, PCOLOR_GREY) & 0x00FF; + + GreyScheme.Shadow = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(15, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Background = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(14, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Corners = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(13, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Highlight = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(9, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bright = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Underline = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bar = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Box = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + + /* + ** Set up the metallic remap table for the font that prints over the tabs + */ + memset ((void*)&MetalScheme, 4, sizeof(MetalScheme)); + for (int color_counter = 0; color_counter < 16; color_counter++) { + MetalScheme.FontRemap[color_counter] = color_counter; + } + MetalScheme.FontRemap[1] = 128; + MetalScheme.FontRemap[2] = 12; + MetalScheme.FontRemap[3] = 13; + MetalScheme.FontRemap[4] = 14; + MetalScheme.Color = 128; + MetalScheme.Background = 0; + MetalScheme.Underline = 128; + + /* + ** Set up the font remap table for the mission briefing font + */ + for (int colr = 0; colr < 16; colr++) { + ColorRemaps[PCOLOR_TYPE].FontRemap[colr] = HidPage.Get_Pixel(colr, PCOLOR_TYPE); + } + + ColorRemaps[PCOLOR_TYPE].Shadow = 11; + ColorRemaps[PCOLOR_TYPE].Background = 10; + ColorRemaps[PCOLOR_TYPE].Corners = 10; + ColorRemaps[PCOLOR_TYPE].Highlight = 9; + ColorRemaps[PCOLOR_TYPE].Bright = 15; + ColorRemaps[PCOLOR_TYPE].Underline = 11; + ColorRemaps[PCOLOR_TYPE].Bar = 11; + ColorRemaps[PCOLOR_TYPE].Box = 10; + ColorRemaps[PCOLOR_TYPE].BrightColor = 15; + ColorRemaps[PCOLOR_TYPE].Color = 9; + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); +// GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_BLUE]); +} + + +/*********************************************************************************************** + * Init_Heaps -- Initialize the game heaps and buffers. * + * * + * This routine will allocate the game heaps and buffers. The rules file has already been * + * processed by the time that this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Heaps(void) +{ + /* + ** Initialize the game object heaps. + */ + Vessels.Set_Heap(Rule.VesselMax); + Units.Set_Heap(Rule.UnitMax); + Factories.Set_Heap(Rule.FactoryMax); + Terrains.Set_Heap(Rule.TerrainMax); + Templates.Set_Heap(Rule.TemplateMax); + Smudges.Set_Heap(Rule.SmudgeMax); + Overlays.Set_Heap(Rule.OverlayMax); + Infantry.Set_Heap(Rule.InfantryMax); + Bullets.Set_Heap(Rule.BulletMax); + Buildings.Set_Heap(Rule.BuildingMax); + Anims.Set_Heap(Rule.AnimMax); + Aircraft.Set_Heap(Rule.AircraftMax); + Triggers.Set_Heap(Rule.TriggerMax); + TeamTypes.Set_Heap(Rule.TeamTypeMax); + Teams.Set_Heap(Rule.TeamMax); + Houses.Set_Heap(HOUSE_MAX); + TriggerTypes.Set_Heap(Rule.TrigTypeMax); +// Weapons.Set_Heap(Rule.WeaponMax); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + SpeechBuffer[index] = new char [SPEECH_BUFFER_SIZE]; + SpeechRecord[index] = VOX_NONE; + assert(SpeechBuffer[index] != NULL); + } + + /* + ** Allocate the theater buffer block. + */ + TheaterBuffer = new Buffer(THEATER_BUFFER_SIZE); + assert(TheaterBuffer != NULL); +} + + +/*********************************************************************************************** + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * * + * This routine will search for and register/cache any override mixfiles found. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Expansion_Files(void) +{ + /* + ** Need to search the search paths. ST - 3/15/2019 2:18PM + */ + const char *path = ".\\"; + char search_path[_MAX_PATH]; + char scan_path[_MAX_PATH]; + + for (int p = 0; p < 100; p++) { + + strcpy(search_path, path); + if (search_path[strlen(search_path) - 1] != '\\') { + strcat(search_path, "\\"); + } + + strcpy(scan_path, search_path); + strcat(scan_path, "SC*.MIX"); + + WIN32_FIND_DATA find_data; + memset(&find_data, 0, sizeof(find_data)); + HANDLE file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MFCD(ptr, &FastKey); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + memset(&find_data, 0, sizeof(find_data)); + strcpy(scan_path, search_path); + strcat(scan_path, "Ss*.MIX"); + file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MFCD(ptr, &FastKey); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + path = CDFileClass::Get_Search_Path(p); + + if (path == NULL) { + break; + } + } + +#if (0) + /* + ** Before all else, cache any additional mixfiles. + */ + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + MFCD::Cache(ptr); + } while (!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + } while (!_dos_findnext(&ff)); + } +#endif +} + + +/*********************************************************************************************** + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * * + * This performs the one-time processing required after the bulk data has been cached but * + * before the game actually starts. Typically, this routine extracts pointers to all the * + * embedded data sub-files within the main game data mixfile. This routine must be called * + * AFTER the bulk data has been cached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine AFTER the bulk data has been cached. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_One_Time_Systems(void) +{ + Call_Back(); + Map.One_Time(); + Logic.One_Time(); + Options.One_Time(); + Session.One_Time(); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + VesselTypeClass::One_Time(); + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); +} + + +/*********************************************************************************************** + * Init_Fonts -- Initialize all the game font pointers. * + * * + * This routine is used to fetch pointers to the game fonts. The mixfile containing these * + * fonts must have been previously cached. This routine is a necessary prerequisite to * + * displaying any dialogs or printing any text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Fonts(void) +{ + Metal12FontPtr = MFCD::Retrieve("12METFNT.FNT"); + MapFontPtr = MFCD::Retrieve("HELP.FNT"); + Font6Ptr = MFCD::Retrieve("6POINT.FNT"); + GradFont6Ptr = MFCD::Retrieve("GRAD6FNT.FNT"); + EditorFont = MFCD::Retrieve("EDITFNT.FNT"); + Font8Ptr = MFCD::Retrieve("8POINT.FNT"); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MFCD::Retrieve("3POINT.FNT"); + ScoreFontPtr = MFCD::Retrieve("SCOREFNT.FNT"); + FontLEDPtr = MFCD::Retrieve("LED.FNT"); + VCRFontPtr = MFCD::Retrieve("VCR.FNT"); + TypeFontPtr = MFCD::Retrieve("8POINT.FNT"); //("TYPE.FNT"); //VG 10/17/96 +} + + +/*********************************************************************************************** + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * * + * This routine is called to setup the CD-ROM access or emulation handler. It will ensure * + * that the appropriate CD-ROM is present (dependant on the RequiredCD global). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The fonts, palettes, and other bootstrap systems must have been initialized * + * prior to calling this routine since this routine will quite likely display * + * a dialog box requesting the appropriate CD be inserted. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_CDROM_Access(void) +{ + VisiblePage.Clear(); + HidPage.Clear(); + +#ifdef FIXIT_VERSION_3 + // Determine if we're going to be running from a DVD. + // The entire session will either require a DVD, or the regular CDs. Never both. + // Call Using_DVD() to determine which case it is. + // Here we set the value that Using_DVD() returns. + Determine_If_Using_DVD(); + // Force_CD_Available() is modified when Using_DVD() is true so that all requests become requests for the DVD. +#endif + + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** This call is needed because of a side effect of this function. It will examine the + ** CD-ROMs attached to this computer and set the appropriate status values. Without this + ** call, the "?:\\" could not be filled in correctly. + */ + Force_CD_Available(-1); + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + error = CCFileClass::Set_Search_Drives("?:\\"); + switch (error) { + case 1: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End("Init_CDROM_Access - CD_ERROR1", true); + Emergency_Exit(EXIT_FAILURE); + + case 2: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + if (WWMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + Prog_End("Init_CDROM_Access - CD_ERROR2", true); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + VisiblePage.Clear(); + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Init_CDROM_Access - Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + + RequiredCD = -1; + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +} + + +/*********************************************************************************************** + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * * + * This routine will register the initial mixfiles that are required to display error * + * messages and get input from the player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine before any dialogs would be displayed to the * + * player. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bootstrap_Mixfiles(void) +{ + int temp = RequiredCD; + RequiredCD = -2; + +#ifdef WOLAPI_INTEGRATION + CCFileClass fileWolapiMix( "WOLAPI.MIX" ); + if( fileWolapiMix.Is_Available() ) + { + new MFCD( "WOLAPI.MIX", &FastKey ); + MFCD::Cache( "WOLAPI.MIX" ); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + CCFileClass file2("EXPAND2.MIX"); + if (file2.Is_Available()) { + new MFCD("EXPAND2.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND2.MIX"); + assert(ok); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + bool ok1; + #if 0 + new MFCD("HIRES1.MIX", &FastKey); + ok1 = MFCD::Cache("HIRES1.MIX"); + assert(ok1); + #else + new MFCD("LORES1.MIX", &FastKey); + ok1 = MFCD::Cache("LORES1.MIX"); + assert(ok1); + #endif +#endif + +#ifdef FIXIT_ANTS // Ok. ajw + CCFileClass file("EXPAND.MIX"); + if (file.Is_Available()) { + new MFCD("EXPAND.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND.MIX"); + assert(ok); + } +#endif + + new MFCD("REDALERT.MIX", &FastKey); + + /* + ** Bootstrap enough of the system so that the error dialog box can successfully + ** be displayed. + */ + new MFCD("LOCAL.MIX", &FastKey); // Cached. + bool ok = MFCD::Cache("LOCAL.MIX"); + assert(ok); + +#if 0 + new MFCD("HIRES.MIX", &FastKey); + ok = MFCD::Cache("HIRES.MIX"); + assert(ok); + + new MFCD("NCHIRES.MIX", &FastKey); //Non-cached hires stuff incl VQ palettes +#else + new MFCD("LORES.MIX", &FastKey); + ok = MFCD::Cache("LORES.MIX"); + assert(ok); +#endif //WIN32 + + RequiredCD = temp; +} + + +/*********************************************************************************************** + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * * + * This routine is used to register the mixfiles that are needed for main menu processing. * + * Call this routine before the main menu is display and processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +//#define DENZIL_MIXEXTRACT +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outfile); +#endif + +static void Init_Secondary_Mixfiles(void) +{ + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + + //Denzil extract mixfile + #ifdef DENZIL_MIXEXTRACT + #if(0) + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES1.MIX", "o:\\projects\\radvd\\data\\extract\\movies1.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #else + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES2.MIX", "o:\\projects\\radvd\\data\\extract\\movies2.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #endif + #endif + + /* + ** Inform the file system of the various MIX files. + */ + ConquerMix = new MFCD("CONQUER.MIX", &FastKey); // Cached. +// new MFCD("TRANSIT.MIX", &FastKey); + + if (GeneralMix == NULL) GeneralMix = new MFCD("GENERAL.MIX", &FastKey); // Never cached. + + if (CCFileClass("MOVIES1.MIX").Is_Available()) { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); // Never cached. + } else { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); // Never cached. + } + assert(MoviesMix != NULL); + + /* + ** Register the score mixfile. + */ + ScoresPresent = true; + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + + /* + ** These are sound card specific, but the install program would have + ** copied the correct versions to the hard drive. + */ + new MFCD("SPEECH.MIX", &FastKey); // Never cached. + new MFCD("SOUNDS.MIX", &FastKey); // Cached. + new MFCD("RUSSIAN.MIX", &FastKey); // Cached. + new MFCD("ALLIES.MIX", &FastKey); // Cached. +} + + +/*********************************************************************************************** + * Bootstrap -- Perform the initial bootstrap procedure. * + * * + * This routine will load and initialize the game engine such that a dialog box could be * + * displayed. Because this is very critical, call this routine before any other game * + * initialization code. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Bootstrap(void) +{ + BlackPalette.Set(); + + /* + ** Be sure to short circuit the CD-ROM check if there is a CD-ROM override + ** path. + */ + if (CCFileClass::Is_There_Search_Drives()) { + RequiredCD = -2; + } + + /* + ** Process the message loop until we are in focus. We need to be in focus to read pixels from + ** the screen. + */ +#if (0) //PG + #ifdef WIN32 + do { + Keyboard->Check(); + } while (!GameInFocus); + AllSurfaces.SurfacesRestored = false; + #endif + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); +#endif + /* + ** Register and make resident all local mixfiles with particular emphasis + ** on the mixfiles that are necessary to display and error messages and + ** process further initialization. + */ + Init_Bootstrap_Mixfiles(); + + /* + ** Initialize the resident font pointers. + */ + Init_Fonts(); + +#ifndef WIN32 + /* + ** Install the hard error handler. + */ + _harderr(harderr_handler); // BG: Install hard error handler + + /* + ** Install a Page Fault handler + */ + if (UsePageFaultHandler) { + Install_Page_Fault_Handle(); + } +#endif + + /* + ** Setup the keyboard processor in preparation for the game. + */ + #ifdef WIN32 + Keyboard->Clear(); + #else + Keyboard_Attributes_Off(BREAKON | SCROLLLOCKON | TRACKEXT | PAUSEON | CTRLSON | CTRLCON | FILTERONLY | TASKSWITCHABLE); + Keyboard_Attributes_On(PASSBREAKS); + Keyboard->Clear(); + #endif + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ +#ifdef STEVES_LOAD_OVERRIDE + RawFileClass strings ("CONQUER.ENG"); + if (strings.Is_Available()){ + SystemStrings = new char [strings.Size()]; + strings.Read((void*)SystemStrings, strings.Size()); + }else{ + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); + } +#else + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); +#endif + DebugStrings = (char const *)MFCD::Retrieve("DEBUG.ENG"); + + /* + ** Default palette initialization. + */ + // PG_TO_FIX. This doesn't seem right. ST - 5/9/2019 + //memmove((unsigned char *)&GamePalette[0], (void *)MFCD::Retrieve("TEMPERAT.PAL"), 768L); + //WhitePalette[0] = BlackPalette[0]; +// GamePalette.Set(); + + /* + ** Initialize expansion files (if present). Expansion files must be located + ** in the current directory. + */ + Init_Expansion_Files(); + + SidebarScheme.Background = BLACK; + SidebarScheme.Corners = LTGREY; + SidebarScheme.Shadow = DKGREY; + SidebarScheme.Highlight = WHITE; + SidebarScheme.Color = LTGREY; + SidebarScheme.Bright = WHITE; + SidebarScheme.BrightColor = WHITE; + SidebarScheme.Box = LTGREY; + GadgetClass::Set_Color_Scheme(&SidebarScheme); +} + + +/*********************************************************************************************** + * Init_Mouse -- Initialize the mouse system. * + * * + * This routine will ensure that a valid mouse driver is present and a working mouse * + * pointer can be displayed. The mouse is hidden when this routine exits. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Mouse(void) +{ + /* + ** Since there is no mouse shape currently available we need + ** to set one of our own. + */ +#ifdef WIN32 + ShowCursor(false); +#endif + if (MouseInstalled) { + void const * temp_mouse_shapes = MFCD::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, Extract_Shape(temp_mouse_shapes, 0)); + while (Get_Mouse_State() > 1) Show_Mouse(); + } + } else { + char buffer[255]; + GamePalette.Set(); + GamePalette.Set(); + sprintf(buffer, TEXT_NO_MOUSE); + VisiblePage.Clear(); + WWMessageBox().Process(buffer, TXT_OK); + Prog_End("Init_Mouse", true); + Emergency_Exit(1); + } + + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); + while (Get_Mouse_State() > 1) Show_Mouse(); + Call_Back(); + Hide_Mouse(); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * * + * This is a development routine that restricts access to the game by way of passwords. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Authorization(void) +{ + if (Special.IsFromInstall) return; + + Load_Title_Page(); +#ifdef WIN32 + Wait_Vert_Blank(); +#else //WIN32 + Init_Delay(); + Wait_Vert_Blank(VertBlank); +#endif //WIN32 + + CCPalette.Set(); +// Set_Palette(Palette); + HidPage.Blit(SeenPage); + Show_Mouse(); + + /* + ** Fetch the type of game to be played. This will be either + ** C&C:Red Alert or C&C:Plus. + */ +//tryagain: + + bool ok = Debug_Flag; + int counter = 3; + + if (Debug_Flag) ok = true; + + /* + ** C&C:Red Alert requires a password for legal entry. Try (three times) to get a correct + ** password. If not found, then try again. + */ + bool skipper = false; +#ifdef NEVER + while (!ok && counter) { + SmartPtr str = Fetch_Password(TXT_PASSWORD_CAPTION, TXT_PASSWORD_MESSAGE, TXT_OK); + SmartPtr lptr = &CheatCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &EditorCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &PlayCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + skipper = true; + break; + } + } + + if (ok) break; + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Delay(TIMER_SECOND*(4-counter)*1); + if (WWMessageBox().Process(TXT_PASSWORD_ERROR, TXT_TRY_AGAIN, TXT_CANCEL)) { + goto tryagain; + } + + counter--; + if (counter == 0) goto tryagain; + } +#endif + + if (!skipper) { + CCPalette.Set(); + } + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Call_Back(); +} +#endif + + +/*********************************************************************************************** + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * * + * This routine is called to handle the time consuming process of game initialization. * + * The title page will be displayed when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will take a very long time. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bulk_Data(void) +{ + /* + ** Cache the main game data. This operation can take a very long time. + */ + MFCD::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MFCD::Cache("SOUNDS.MIX"); + MFCD::Cache("RUSSIAN.MIX"); + MFCD::Cache("ALLIES.MIX"); + } + Call_Back(); + + /* + ** Fetch the tutorial message data. + */ + INIClass ini; + ini.Load(CCFileClass("TUTORIAL.INI")); + for (int index = 0; index < ARRAY_SIZE(TutorialText); index++) { + TutorialText[index] = NULL; + + char buffer[128]; + char num[10]; + sprintf(num, "%d", index); + if (ini.Get_String("Tutorial", num, "", buffer, sizeof(buffer))) { + TutorialText[index] = strdup(buffer); + } + } + + /* + ** Perform one-time game system initializations. + */ + Init_One_Time_Systems(); +} + + +/*********************************************************************************************** + * Init_Keys -- Initialize the cryptographic keys. * + * * + * This routine will initialize the fast cryptographic key. It will also initialize the * + * slow one if this is a scenario editor version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Keys(void) +{ + RAMFileClass file((void*)Keys, strlen(Keys)); + INIClass ini; + ini.Load(file); + + FastKey = ini.Get_PKey(true); +#ifdef SCENARIO_EDITOR + SlowKey = ini.Get_PKey(false); +#endif +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_Recording_Values(CCFileClass & file) +{ + Session.Save(file); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Write(&Seed, sizeof(Seed)); + file.Write(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Write(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Write(&Whom, sizeof(Whom)); + file.Write(&Special, sizeof(SpecialClass)); + file.Write(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_Recording_Values(CCFileClass & file) +{ + Session.Load(file); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Read(&Seed, sizeof(Seed)); + file.Read(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Read(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Read(&Whom, sizeof(Whom)); + file.Read(&Special, sizeof(SpecialClass)); + file.Read(&Options, sizeof(GameOptionsClass)); + return (true); +} + +extern "C" { +void __PRO(void) { +// printf("_pro\n"); +} +} + +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outname) + { + CCFileClass inFile(filename); + CCFileClass outFile(outname); + + inFile.Open(); + outFile.Open(WRITE); + + void* buffer = malloc(32768); + + if (buffer) + { + unsigned long size = inFile.Size(); + unsigned long bytes; + + while (size > 0) + { + bytes = inFile.Read(buffer, 32768); + outFile.Write(buffer, bytes); + size -= bytes; + } + + free(buffer); + } + } +#endif + + +#ifdef FIXIT_VERSION_3 + +bool bUsingDVD = false; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +bool Is_DVD_Installed() +{ + bool bInstalled; + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "DVD", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + + return bInstalled; +} + +//*********************************************************************************************** +bool Determine_If_Using_DVD() +{ + // Determines if the user has a DVD currently available. If they do, we'll use it throughout the + // session. Else we won't check for it again and will always ask for CDs. + if( Is_DVD_Installed() ) + { + if( Force_CD_Available( 5 ) ) + { + bUsingDVD = true; + } + else + { + // User hit cancel. Allow things to progress normally. They will be prompted for + // a Red Alert disk as usual. + bUsingDVD = false; + } + } + else + bUsingDVD = false; + + return bUsingDVD; +} + +//*********************************************************************************************** +bool Using_DVD() +{ + return bUsingDVD; +} + +#endif + + + + +/*********************************************************************************************** + * Free_Heaps -- Clear out the heaps before exit * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/18/2019 11:59AM ST : Created. * + *=============================================================================================*/ +void Free_Heaps(void) +{ + HouseTypes.Clear(); + BuildingTypes.Clear(); + AircraftTypes.Clear(); + InfantryTypes.Clear(); + BulletTypes.Clear(); + AnimTypes.Clear(); + UnitTypes.Clear(); + VesselTypes.Clear(); + TemplateTypes.Clear(); + TerrainTypes.Clear(); + OverlayTypes.Clear(); + SmudgeTypes.Clear(); +#if (0) + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + // Heap init moved here from globals.cpp. ST - 5/20/2019 + CCPtr::Set_Heap(&Aircraft); + CCPtr::Set_Heap(&Anims); + CCPtr::Set_Heap(&Buildings); + CCPtr::Set_Heap(&Bullets); + CCPtr::Set_Heap(&Factories); + CCPtr::Set_Heap(&Houses); + CCPtr::Set_Heap(&Infantry); + CCPtr::Set_Heap(&Overlays); + CCPtr::Set_Heap(&Smudges); + CCPtr::Set_Heap(&Teams); + CCPtr::Set_Heap(&TeamTypes); + CCPtr::Set_Heap(&Templates); + CCPtr::Set_Heap(&Terrains); + CCPtr::Set_Heap(&Triggers); + CCPtr::Set_Heap(&TriggerTypes); + + CCPtr::Set_Heap(&HouseTypes); + CCPtr::Set_Heap(&BuildingTypes); + CCPtr::Set_Heap(&AircraftTypes); + CCPtr::Set_Heap(&InfantryTypes); + CCPtr::Set_Heap(&BulletTypes); + CCPtr::Set_Heap(&AnimTypes); + CCPtr::Set_Heap(&UnitTypes); + CCPtr::Set_Heap(&VesselTypes); + CCPtr::Set_Heap(&TemplateTypes); + CCPtr::Set_Heap(&TerrainTypes); + CCPtr::Set_Heap(&OverlayTypes); + CCPtr::Set_Heap(&SmudgeTypes); +#endif + + Vessels.Clear(); + Units.Clear(); + Factories.Clear(); + Terrains.Clear(); + Templates.Clear(); + Smudges.Clear(); + Overlays.Clear(); + Infantry.Clear(); + Bullets.Clear(); + Buildings.Clear(); + Anims.Clear(); + Aircraft.Clear(); + Triggers.Clear(); + TeamTypes.Clear(); + Teams.Clear(); + Houses.Clear(); + TriggerTypes.Clear(); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + if (SpeechBuffer[index]) { + delete [] SpeechBuffer[index]; + SpeechBuffer[index] = NULL; + } + } + + /* + ** Allocate the theater buffer block. + */ + if (TheaterBuffer) { + delete TheaterBuffer; + TheaterBuffer = NULL; + } +} \ No newline at end of file diff --git a/REDALERT/INLINE.H b/REDALERT/INLINE.H new file mode 100644 index 000000000..458e4e88b --- /dev/null +++ b/REDALERT/INLINE.H @@ -0,0 +1,1066 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INLINE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INLINE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/21/96 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Cell_Coord -- Convert a cell to a coordinate value. * + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * Cell_X -- Fetch the X cell component from the cell value. * + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * Coord_Add -- Adds coordinates together. * + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * Coord_Mid -- Finds the midpoint between two coordinates. * + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * Coord_Sub -- Subtracts one coordinate from another. * + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * Dir_Facing -- Convert a DirType into a FacingType value. * + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * Direction -- Calculates the DirType from one cell to another. * + * Direction -- Determines the facing value from one coordinate to another. * + * Direction256 -- Calculate the facing value from one coordinate to another. * + * Direction8 -- Fetches the direction from one coordinate to another. * + * Distance -- Finds the distance between two arbitrary points. * + * Facing_Dir -- Convert a FacingType into a DirType. * + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * Percent_Chance -- Calculate a percentage chance event. * + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * Random_Pick -- Pick a random number in a specified range. * + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * Text_String -- Convert a text number into a text pointer. * + * XYP_COORD -- Convert pixel components into a coordinate value. * + * XYP_Coord -- Combine pixel values into a coordinate. * + * XY_Cell -- Create a cell from X and Y cell components. * + * XY_Coord -- Convert X Y lepton components into a COORD. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INLINE_H +#define INLINE_H + + +/*********************************************************************************************** + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * * + * Use this routine to convert the specified lepton value into it's pixel corresponding * + * value. The pixel value returned will be the closest pixel value to the lepton value. It * + * will round up or down as necessary. * + * * + * INPUT: lepton -- The lepton value to convert into a pixel value. * + * * + * OUTPUT: Returns with the lepton value rounded to the nearest pixel component. * + * * + * WARNINGS: Precision is not maintained by this routine. Thus, if a value is converted to * + * pixel and then back to leptons, the value will probably not be the same. * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Pixel(LEPTON lepton) +{ + return (((int)(signed short)lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2) - ((lepton < 0) ? (ICON_LEPTON_W - 1) : 0)) / ICON_LEPTON_W; +} + + +/*********************************************************************************************** + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * * + * This routine will take the specified pixel value and convert it into lepton value. * + * * + * INPUT: pixel -- The pixel value to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the pixel value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Pixel_To_Lepton(int pixel) +{ + return (LEPTON)(((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2) - ((pixel < 0) ? (ICON_PIXEL_W - 1) : 0)) / ICON_PIXEL_W); +} + + +/*********************************************************************************************** + * XY_Coord -- Convert X Y lepton components into a COORD. * + * * + * This routine will take the specified X and Y lepton components and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y lepton components to combine. * + * * + * OUTPUT: Returns with a coordinate value that is created from the X and Y lepton components.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XY_Coord(LEPTON x, LEPTON y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = x; + coord.Sub.Y.Raw = y; + return(coord.Coord); +} + + +/*********************************************************************************************** + * XYP_COORD -- Convert pixel components into a coordinate value. * + * * + * This routine will take the specified pixel components and convert and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y pixel components to coerce into a coordinate value. * + * * + * OUTPUT: Returns with the coordinate value that matches the pixel values specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_COORD(int x, int y) +{ + return(XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y))); +} + + +/*********************************************************************************************** + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * * + * This routine will extract the X cell component from the coordinate value specified and * + * return the value. * + * * + * INPUT: coord -- The coordinate value to extract the X component from. * + * * + * OUTPUT: Returns with the X cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_XCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * * + * This routine will extract the Y cell component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate to extract the Y cell from. * + * * + * OUTPUT: Returns with just the Y cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_YCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell); +} + + +/*********************************************************************************************** + * XY_Cell -- Create a cell from X and Y cell components. * + * * + * This routine will construct a cell value by taking the X and Y cell value components * + * and combining them appropriately. * + * * + * INPUT: x,y -- The X and Y cell components to combine. * + * * + * OUTPUT: Returns with the CELL value created from the specified components. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL XY_Cell(int x, int y) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = x; + cell.Sub.Y = y; + return(cell.Cell); +} + + +/*********************************************************************************************** + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * * + * This routine will take the cell distance specified and convert it into a lepton distance.* + * * + * INPUT: cell_distance -- The distance in cells to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the cell distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Cell_To_Lepton(int cell_distance) +{ + LEPTON_COMPOSITE lepton; + lepton.Sub.Cell = (unsigned char)cell_distance; + lepton.Sub.Lepton = 0; + return(lepton.Raw); +} + + +/*********************************************************************************************** + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * * + * This routine will convert the specified lepton distance into the closest cell distance * + * possible. This might require rounding up or down as necessary. * + * * + * INPUT: lepton_distance -- The lepton distance to convert. * + * * + * OUTPUT: Returns with the cell distance that most closely corresponds to the lepton * + * distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Cell(LEPTON lepton_distance) +{ + if (((LEPTON_COMPOSITE &)lepton_distance).Sub.Lepton >= (CELL_LEPTON_W/2)) { + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell + 1); + } + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * * + * This routine will extract the X lepton component from the coordinate. * + * * + * INPUT: coord -- The coordinate to extract the X lepton equivalent from. * + * * + * OUTPUT: Returns with the X lepton portion of the coordinate value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_X(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Raw); +} + + +/*********************************************************************************************** + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * * + * This routine will extract the Y lepton component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate value to dissect. * + * * + * OUTPUT: Returns with the Y lepton component from the specified coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_Y(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Raw); +} + + +/*********************************************************************************************** + * Cell_X -- Fetch the X cell component from the cell value. * + * * + * This routine will extract the X cell component from the cell value specified. * + * * + * INPUT: cell -- The cell to extract. * + * * + * OUTPUT: Returns with the X cell component portion of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_X(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.X); +} + + +/*********************************************************************************************** + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * * + * This routine will extract the Y cell component from the cell value. * + * * + * INPUT: cell -- The cell value to extract from. * + * * + * OUTPUT: Returns with the Y cell component of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_Y(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.Y); +} + + +/*********************************************************************************************** + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * * + * This routine will extract just the X sub cell lepton component from the coordinate * + * specified. * + * * + * INPUT: coord -- The coordinate value to extract from. * + * * + * OUTPUT: Returns with the X lepton component of the coordinate that is part of the cell. * + * Thus, a coordinate that exactly lines up on the left edge of a cell would return * + * zero. One that exactly lines up on the right edge would return CELL_LEPTON_W. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_XLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton); +} + + +/*********************************************************************************************** + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * * + * This routine will extract the sub-cell Y lepton portion of the coordinate. * + * * + * INPUT: coord -- The coordinate to dissect. * + * * + * OUTPUT: Returns with just the Y lepton portion of the coordinate and only for the sub-cell * + * it refers to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_YLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton); +} + + +/*********************************************************************************************** + * XYP_Coord -- Combine pixel values into a coordinate. * + * * + * This will convert X and Y pixel values into a coordinate. Primarily this is used for * + * converting mouse clicks into coordinate values. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to convert. Origin is upper left corner. * + * * + * OUTPUT: Returns with the coordinate that most closely corresponds to the pixel values * + * specified. * + * * + * WARNINGS: The coordinate is relative to the upper left corner (0,0). To conver the * + * coordinate to a game relative one, it must be biased by the display coordinate * + * of the tactical map and the screen position of the tactical display. * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_Coord(int x, int y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = Pixel_To_Lepton(x); + coord.Sub.Y.Raw = Pixel_To_Lepton(y); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Cell_Coord -- Convert a cell to a coordinate value. * + * * + * This routine will convert the specified cell into a coordinat value. The coordinate * + * will refer to the center of the cell specified. * + * * + * INPUT: cell -- The cell to convert into a coordinate. * + * * + * OUTPUT: Returns with the coordinate that refers to the center of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Cell_Coord(CELL cell) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.X); + coord.Sub.X.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + coord.Sub.Y.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.Y); + coord.Sub.Y.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * * + * This routine will take the specified coordinate and force it to refer to the center of * + * the cell. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with the specified coordinate after it has been modified to refer to the * + * center of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Snap(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = CELL_LEPTON_W/2; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = CELL_LEPTON_W/2; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * * + * Use this routine to discard the cell components of the coordinate, leaving only the * + * sub-cell component. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with just the sub-cell components intact from the supplied coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Fraction(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * * + * This routine will discard the sub-cell components, leaving only the whole cell portion. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with only the whole cell components of the coordinate intact. The * + * resulting coordinate will refer to the upper left corner of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Whole(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Add -- Adds coordinates together. * + * * + * This routine will add one coordinate to another. Actually, it adds the X and Y components* + * separately (signed) and then recombines them back into a coordinate. * + * * + * INPUT: coord1 -- One coordinate to add. * + * * + * coord2 -- The other coordinate to add. * + * * + * OUTPUT: Returns with the logical add of the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Sub -- Subtracts one coordinate from another. * + * * + * This routine will subtract one coordinate from the other. The coordinates are broken * + * up into their X and Y components, the subtraction is performed, and then they are * + * recombined back into a coordinate to be returned. * + * * + * INPUT: coord1 -- The coordinate to be subtracted from. * + * * + * coord2 -- The coordinate to subtract. * + * * + * OUTPUT: Returns with the result of subtracting coord2 from coord1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Mid -- Finds the midpoint between two coordinates. * + * * + * This will find the coordinate that is exactly between the two coordinates specified. * + * * + * INPUT: coord1 -- The first coordinate. * + * * + * coord2 -- The second coordinate. * + * * + * OUTPUT: Returns with the midpoint between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.X.Raw) / 2); + coord.Sub.Y.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.Y.Raw) / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Dir_Facing -- Convert a DirType into a FacingType value. * + * * + * Use this routine to convert the specified DirType value into the closest FacingType * + * value that matches it. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with a FacingType value that most closely matches the DirType specified. * + * * + * WARNINGS: Precision of direction is lost by this transformation. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_Facing(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing+0x10)&0xFF)>>5); +} + + +/*********************************************************************************************** + * Facing_Dir -- Convert a FacingType into a DirType. * + * * + * This will conver the specified FacingType value into the DirType that exactly matches * + * it. * + * * + * INPUT: facing -- The FacingType to convert. * + * * + * OUTPUT: Returns with the DirType that exactly matches the facing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Facing_Dir(FacingType facing) +{ + return (DirType)((int)facing << 5); +} + + +/*********************************************************************************************** + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * * + * Use this routine to convert a DirType into a 0 through 15 value. * + * * + * INPUT: facing -- The DirType to convert into a 0..15 value. * + * * + * OUTPUT: Returns with the facing converted into a value where 0 equals North, 4 equals * + * East, 8 equals South, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_16(DirType facing) +{ + return Facing16[facing]; +} + + +/*********************************************************************************************** + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * * + * This will convert the DirType specified, into a 0 through 31 value where zero is North, * + * and rotates clockwise. The return value is baised to take into consideration the * + * distortion caused by 3D studio upon the game vehicle objects. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the facing converted into a value from zero to 31. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_32(DirType facing) +{ + return Facing32[facing]; +} + + +/*********************************************************************************************** + * Direction256 -- Calculate the facing value from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate that facing will be calcuated to. * + * * + * OUTPUT: Returns with the DirType that is the facing from coord1 to coord2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Determines the facing value from one coordinate to another. * + * * + * This will determine the DirType from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate to calculate facing to. * + * * + * OUTPUT: Returns with the DirType that represents the facing from coordinate 1 to coordinate* + * 2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction8 -- Fetches the direction from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. The return value * + * is of limited accuracy, but the calculation is fast. * + * * + * INPUT: coord1 -- The coordinate to calculate the facing from. * + * * + * coord2 -- The coordinate to figure the facing to. * + * * + * OUTPUT: Returns with the DirType to get from coordinate 1 to coordinate 2. * + * * + * WARNINGS: The return DirType is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Calculates the DirType from one cell to another. * + * * + * This routine will calculate the facing to get from one cell to another. Since dealing * + * with cells is much less precise than with coordinates, the return value is only * + * accurate to 8 facings. * + * * + * INPUT: cell1 -- The cell to calculate the DirType from. * + * * + * cell2 -- The cell to calculate the DirType to. * + * * + * OUTPUT: Returns with the DirType to get from the first cell to the second. * + * * + * WARNINGS: The return value is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(CELL cell1, CELL cell2) +{ + return (Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) +{ + return (Coord_Snap(Coord_Add(AdjacentCoord[(int)dir & 0x07], coord))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) +{ + return Adjacent_Cell(coord, Dir_Facing(dir)); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, FacingType dir) +{ + return (CELL)(cell + AdjacentCell[dir]); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, DirType dir) +{ + return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]); +} + + +/*********************************************************************************************** + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * * + * This routine will convert a DirType value into a facing number from 0 to 7. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the DirType converted to a number from 0 to 7 with 0 being North and * + * rotating clockwise. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_To_8(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing|0x10))>>5); +} + + +/*********************************************************************************************** + * Text_String -- Convert a text number into a text pointer. * + * * + * This routine will convert text numbers (as generated elsewhere) into an actual text * + * pointer that can be used for normal purposes. * + * * + * INPUT: string -- The text number to extract a pointer to. * + * * + * OUTPUT: Returns with a pointer to the text that represents the text number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline char const * Text_String(int string) +{ +#ifdef FIXIT_NAME_OVERRIDE + if (string < 0 && abs(string) < ARRAY_SIZE(NameOverride)) { + return(NameOverride[-(string+1)]); + } +#endif + + if (string < 1000) return(Extract_String(SystemStrings, string)); + return(Extract_String(DebugStrings, string-1000)); +} + + +/*********************************************************************************************** + * Random_Pick -- Pick a random number in a specified range. * + * * + * This routine is used to pick a game influencing random number between (inclusive) the * + * range specified. * + * * + * INPUT: a -- Low limit of range to pick from. * + * * + * b -- High limit of range to pick from. * + * * + * OUTPUT: Returns with a random number picked between (inclusive) the range of values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +template inline T Random_Pick(T a, T b) +{ + return T(Scen.RandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Percent_Chance -- Calculate a percentage chance event. * + * * + * This will calculate a percentage chance and return with 'true' as likely as the * + * chance value would occur (or less) on a random pick from 1 to 100. Thus a * + * Percent_Chance(50) would return 'true' 50 percent of the time. Percent_Chance(25) would * + * return 'true' 25% of the time, etc. * + * * + * INPUT: percent -- The percent value to calculate the chance upon. * + * * + * OUTPUT: Returns with 'true' in the same percentage as the percentage number supplied. * + * * + * WARNINGS: This affects the game syncronization random number generator and should be used * + * for those events that could affect the game engine. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Percent_Chance(int percent) +{ + return (Scen.RandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * * + * Use this routine to pick a random number such that it will be used so that it won't * + * actually affect the outcome of the game. It is critical to use this routine for any * + * random need that won't be needed on other machines in a multiplayer game. The result * + * can be freely used as long as it doesn't affect the outcome of the game. * + * * + * INPUT: a -- Low range of the random number to pick. * + * * + * b -- High range of the random number to pick. * + * * + * OUTPUT: Returns with a random number between (inclusive) the range limit values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +extern RandomClass NonCriticalRandomNumber; +template inline T Sim_Random_Pick(T a, T b) +{ + return T(NonCriticalRandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * * + * This routine is similar to the normal Percent_Chance() routine except that it doesn't * + * alter the main random number gerenator sequence. As such, this routine should be used * + * for those events that should have a random character, but will either not affect the * + * game engine or are only calculated on one machine in a multiplayer game. * + * * + * INPUT: percent -- The percent chance to calculate the possible return of 'true' on. * + * * + * OUTPUT: Returns 'true' with the same percentage chance as the percent number specified. * + * A percent value of 50 means 50%, 25 means 25%, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Sim_Percent_Chance(int percent) +{ + return (NonCriticalRandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Distance -- Finds the distance between two arbitrary points. * + * * + * This finds the (Dragon Strike) distance between two arbitrary points in flat space. * + * It does this by adding 1/2 the smaller absolute axis difference to the other absolute * + * axis distance. The result is rough but quick to calculate. * + * * + * INPUT: x1,y1 -- Coordinate location for point 1. * + * * + * x2,y2 -- Coordinate location for point 2. * + * * + * OUTPUT: Returns with the rough distance between the two points. The value returned is * + * expressed in the same units as the parameters were specified in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +inline int Distance(int x1, int y1, int x2, int y2) +{ + int diff1 = y1 - y2; + if (diff1 < 0) diff1 = -diff1; + int diff2 = x1 - x2; + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +#endif + diff --git a/REDALERT/INT.CPP b/REDALERT/INT.CPP new file mode 100644 index 000000000..9b8c0f94c --- /dev/null +++ b/REDALERT/INT.CPP @@ -0,0 +1,43 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "mp.h" +#include "int.h" + +int bignum::Error = 0; +bool bignum::Carry = false; +bool bignum::Borrow = false; +bignum bignum::Remainder; + diff --git a/REDALERT/INT.H b/REDALERT/INT.H new file mode 100644 index 000000000..e59c56af9 --- /dev/null +++ b/REDALERT/INT.H @@ -0,0 +1,301 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INT_H +#define INT_H + +#include +#include +#include +#include "mp.h" +#include "straw.h" + +//#pragma warn -inl + +template +class Int { + public: + + /* + ** Constructors and initializers. + */ + Int(void) {XMP_Init(®[0], 0, PRECISION);} + Int(unsigned long value) {XMP_Init(®[0], value, PRECISION);} + + void Randomize(Straw & rng, int bitcount) {XMP_Randomize(®[0], rng, bitcount, PRECISION);} + void Randomize(Straw & rng, const Int & minval, const Int & maxval) {XMP_Randomize(®[0], rng, minval, maxval, PRECISION); reg[0] |= 1;} + + /* + ** Convenient conversion operators to get at the underlying array of + ** integers. Big number math is basically manipulation of arbitrary + ** length arrays. + */ + operator digit * () {return & reg[0];} + operator const digit * () const {return & reg[0];} + + /* + ** Array access operator (references bit position). Bit 0 is the first bit. + */ + bool operator[](unsigned bit) const {return(XMP_Test_Bit(®[0], bit));} + + /* + ** Unary operators. + */ + Int & operator ++ (void) {XMP_Inc(®[0], PRECISION);return(*this);} + Int & operator -- (void) {XMP_Dec(®[0], PRECISION);return(*this);} + int operator ! (void) const {return(XMP_Test_Eq_Int(®[0], 0, PRECISION));} + Int operator ~ (void) {XMP_Not(®[0], PRECISION);return(*this);} + Int operator - (void) const {Int a = *this;a.Negate();return (a);} + + /* + ** Attribute query functions. + */ + int ByteCount(void) const {return(XMP_Count_Bytes(®[0], PRECISION));} + int BitCount(void) const {return(XMP_Count_Bits(®[0], PRECISION));} + bool Is_Negative(void) const {return(XMP_Is_Negative(®[0], PRECISION));} + unsigned MaxBitPrecision() const {return PRECISION*(sizeof(unsigned long)*CHAR_BIT);} + bool IsSmallPrime(void) const {return(XMP_Is_Small_Prime(®[0], PRECISION));} + bool SmallDivisorsTest(void) const {return(XMP_Small_Divisors_Test(®[0], PRECISION));} + bool FermatTest(unsigned rounds) const {return(XMP_Fermat_Test(®[0], rounds, PRECISION));} + bool IsPrime(void) const {return(XMP_Is_Prime(®[0], PRECISION));} + bool RabinMillerTest(Straw & rng, unsigned int rounds) const {return(XMP_Rabin_Miller_Test(rng, ®[0], rounds, PRECISION));} + + /* + ** 'in-place' binary operators. + */ + Int & operator += (const Int & number) {Carry = XMP_Add(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator -= (const Int & number) {Borrow = XMP_Sub(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator *= (const Int & multiplier) {Remainder = *this;Error=XMP_Signed_Mult(®[0], Remainder, multiplier, PRECISION);return(*this);} + Int & operator /= (const Int & t) {*this = (*this) / t;return *this;} + Int & operator %= (const Int & t) {*this = (*this) % t;return *this;} + Int & operator <<= (int bits) {XMP_Shift_Left_Bits(®[0], bits, PRECISION);return *this;} + Int & operator >>= (int bits) {XMP_Shift_Right_Bits(®[0], bits, PRECISION);return *this;} + + /* + ** Mathematical binary operators. + */ + Int operator + (const Int & number) const {Int term;Carry = XMP_Add(term, ®[0], number, 0, PRECISION);return(term);} + Int operator + (unsigned short b) const {Int result;Carry=XMP_Add_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator + (digit b, const Int & a) {return(Int(b) + a);} + Int operator - (const Int & number) const {Int term;Borrow = XMP_Sub(term, ®[0], number, 0, PRECISION);return(term);} + Int operator - (unsigned short b) const {Int result;Borrow = XMP_Sub_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator - (digit b, const Int & a) {return(Int(b) - a);} + Int operator * (const Int & multiplier) const {Int result;Error=XMP_Signed_Mult(result, ®[0], multiplier, PRECISION);return result;} + Int operator * (unsigned short b) const {Int result;Error=XMP_Unsigned_Mult_Int(result, ®[0], b, PRECISION);return(result);} +// friend Int operator * (digit b, const Int & a) {return(Int(b) * a);} + Int operator / (const Int & divisor) const {Int quotient = *this;XMP_Signed_Div(Remainder, quotient, ®[0], divisor, PRECISION);return (quotient);} + Int operator / (unsigned long b) const {return(*this / Int(b));} + Int operator / (unsigned short divisor) const {Int quotient;Error=XMP_Unsigned_Div_Int(quotient, ®[0], divisor, PRECISION);return(quotient);} +// friend Int operator / (digit a, const Int & b) {return(Int(a) / b);} + Int operator % (const Int & divisor) const {Int remainder;XMP_Signed_Div(remainder, Remainder, ®[0], divisor, PRECISION);return(remainder);} + Int operator % (unsigned long b) const {return(*this % Int(b));} + unsigned short operator % (unsigned short divisor) const {return(XMP_Unsigned_Div_Int(Remainder, ®[0], divisor, PRECISION));} +// friend Int operator % (digit a, const Int & b) {return(Int(a) % b);} + + /* + ** Bitwise binary operators. + */ + Int operator >> (int bits) const {Int result = *this; XMP_Shift_Right_Bits(result, bits, PRECISION);return result;} + Int operator << (int bits) const {Int result = *this; XMP_Shift_Left_Bits(result, bits, PRECISION);return result;} + + /* + ** Comparison binary operators. + */ + int operator == (const Int &b) const {return (memcmp(®[0], &b.reg[0], (MAX_BIT_PRECISION/CHAR_BIT))==0);} + int operator != (const Int& b) const {return !(*this == b);} + int operator > (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) > 0);} + int operator >= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) >= 0);} + int operator < (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) < 0);} + int operator <= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) <= 0);} + + /* + ** Misc. mathematical and logical functions. + */ + void Negate(void) {XMP_Neg(®[0], PRECISION);} + Int Abs(void) {XMP_Abs(®[0], PRECISION);return(*this);} + Int times_b_mod_c(Int const & multiplier, Int const & modulus) const { + Int result; + Error=xmp_stage_modulus(modulus, PRECISION); + Error=XMP_Mod_Mult(result, ®[0], multiplier, PRECISION); + XMP_Mod_Mult_Clear(PRECISION); + return result; + } + + Int exp_b_mod_c(const Int & e, const Int & m) const { + Int result; + Error=xmp_exponent_mod(result, ®[0], e, m, PRECISION); + return result; + } + + + static Int Unsigned_Mult(Int const & multiplicand, Int const & multiplier) {Int product;Error=XMP_Unsigned_Mult(&product.reg[0], &multiplicand.reg[0], &multiplier.reg[0], PRECISION);return(product);} + static void Unsigned_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {Error=XMP_Unsigned_Div(remainder, quotient, dividend, divisor, PRECISION);} + static void Signed_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {XMP_Signed_Div(remainder, quotient, dividend, divisor, PRECISION);} + Int Inverse(const Int & modulus) const {Int result;XMP_Inverse_A_Mod_B(result, ®[0], modulus, PRECISION);return(result);} + + static Int Decode_ASCII(char const * string) {Int result;XMP_Decode_ASCII(string, result, PRECISION);return(result);} + + // Number (sign independand) inserted into buffer. + int Encode(unsigned char *output) const {return(XMP_Encode(output, ®[0], PRECISION));} + int Encode(unsigned char * output, unsigned length) const {return(XMP_Encode(output, length, ®[0], PRECISION));} + void Signed_Decode(const unsigned char * from, int frombytes) {XMP_Signed_Decode(®[0], from, frombytes, PRECISION);} + void Unsigned_Decode(const unsigned char * from, int frombytes) {XMP_Unsigned_Decode(®[0], from, frombytes, PRECISION);} + + // encode Int using Distinguished Encoding Rules, returns size of output + int DEREncode(unsigned char * output) const {return(XMP_DER_Encode(®[0], output, PRECISION));} + void DERDecode(const unsigned char *input) {XMP_DER_Decode(®[0], input, PRECISION);} + + // Friend helper functions. + friend Int Generate_Prime(Straw & rng, int pbits, Int const * = 0); + friend Int Gcd(const Int & a, const Int & b); +// friend bool NextPrime(Int & p, const Int & max, bool blumInt=false); +// friend Int a_exp_b_mod_pq(const Int & a, const Int & ep, const Int & eq, const Int & p, const Int & q, const Int & u); + + static int Error; + + // Carry result from last addition. + static bool Carry; + + // Borrow result from last subtraction. + static bool Borrow; + + // Remainder value from the various division routines. + static Int Remainder; + + + private: + digit reg[PRECISION]; + + + struct RemainderTable + { + RemainderTable(const Int & p) : HasZeroEntry(false) + { + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] = p % primeTable[i]; + } + } + bool HasZero() const {return(HasZeroEntry);} + void Increment(unsigned short increment = 1) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += increment; + while (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + void Increment(const RemainderTable & rtQ) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += rtQ.table[i]; + if (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + + bool HasZeroEntry; + unsigned short table[ARRAY_SIZE(primeTable)]; + }; + +}; + + +template +T Gcd(const T & a, const T & n) +{ + T g[3]={n, a, 0UL}; + + unsigned int i = 1; + while (!!g[i%3]) { + g[(i+1)%3] = g[(i-1)%3] % g[i%3]; + i++; + } + return g[(i-1)%3]; +} + + + +//#pragma warning 604 9 +//#pragma warning 595 9 +template +T Generate_Prime(Straw & rng, int pbits, T const *) +{ + T minQ = (T(1UL) << (unsigned short)(pbits-(unsigned short)2)); + T maxQ = ((T(1UL) << (unsigned short)(pbits-(unsigned short)1)) - (unsigned short)1); + + T q; + T p; + + do { + q.Randomize(rng, minQ, maxQ); + p = (q*2) + (unsigned short)1; + + T::RemainderTable rtQ(q); + T::RemainderTable rtP(p); + + while (rtQ.HasZero() || rtP.HasZero() || !q.IsPrime() || !p.IsPrime()) { + q += 2; + p += 4; + if (q > maxQ) break; + + rtQ.Increment(2); + rtP.Increment(4); + } + } while (q > maxQ); + + return(p); +} + + + + + + +typedef Int bignum; +typedef Int BigInt; + + + +//BigInt Gcd(const BigInt & a, const BigInt & n); +//BigInt Generate_Prime(RandomNumberGenerator & rng, int pbits, BigInt const * dummy); + +#endif + diff --git a/REDALERT/INTERNET.CPP b/REDALERT/INTERNET.CPP new file mode 100644 index 000000000..b6b012886 --- /dev/null +++ b/REDALERT/INTERNET.CPP @@ -0,0 +1,975 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/INTERNET.CPP 6 3/17/97 1:05a Steve_tall $ */ +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : INTERNET.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Miscellaneous junk related to H2H internet connection. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * Check_From_WChat -- Interprets start game packet from WChat * + * Read_Game_Options -- Read the game setup options from the wchat packet * + * Is_User_WChat_Registered -- retrieve the users wchat entry from registry * + * Spawn_WChat -- spawns or switches focus to wchat * + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "ccdde.h" + + +extern bool SpawnedFromWChat; + +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(void); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +#endif + +/*************************************************************************** +** Internet specific globals +*/ +char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name +char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password +char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player +long PlanetWestwoodPortNumber = 1234; //Port number to send to +bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options +unsigned long PlanetWestwoodGameID; //Game ID +unsigned long PlanetWestwoodStartTime; //Time that game was started +HWND WChatHWND = 0; //Handle to Wchat window. +bool GameStatisticsPacketSent; //Flag that game stats have been sent to wchat +bool ConnectionLost; //Flag that the connection to the other player was lost +int WChatMaxAhead; +int WChatSendRate; +bool SpawnedFromWChat; +int ShowCommand; + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +/*********************************************************************************************** + * Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it. * + * C&CSPAWN.INI is now sent to us by WCHAT via DDE * + * * + * * + * INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 1:44PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +void Check_From_WChat(char *wchat_name) +{ + + char default_string[] = {"Error"}; + char key_string[256]; + char *ini_file; + RawFileClass wchat_file; + + /* + ** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from + ** the DDE server. + */ + if (wchat_name){ + ini_file = new char [8192]; + }else{ + ini_file = DDEServer.Get_MPlayer_Game_Info(); + +#ifdef NEVER + /* + ** Save it to disk as well so I can see it + */ + RawFileClass anotherfile ("FROMCHAT.TXT"); + anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length()); +#endif + + } + + if (wchat_name){ + wchat_file.Set_Name(wchat_name); + } + + if (!wchat_name || wchat_file.Is_Available()){ + + /* + ** Read the ini file from disk if we founf it there + */ + if (wchat_name){ + wchat_file.Read(ini_file, wchat_file.Size()); + } + + /* + ** Get the IP address + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Address", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + strcpy (PlanetWestwoodIPAddress, key_string); + + + + /* + ** Get the port number + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Port", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + PlanetWestwoodPortNumber = atol(key_string); + + + /* + ** Get host or client + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Host", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + if (strchr (key_string, '1')){ + PlanetWestwoodIsHost = true; + }else{ + PlanetWestwoodIsHost = false; + } + + + Special.IsFromWChat = true; + } + + if (wchat_name) delete ini_file; + +} +#endif // !WOLAPI_INTEGRATION + + +/*************************************************************************** + * Read_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * name of C&CSPAWN.INI file. Null if data should be got from DDE server* * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: \ * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(char *name) +{ + char *buffer; + + char filename[256] = {"INVALID.123"}; + + if (name){ + strcpy (filename, name); + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file (filename); + + if (name && !file.Is_Available()) { + return(0); + } else { + if (name){ + buffer = new char [8192]; // INI staging buffer pointer. + memset(buffer, '\0', 8192); + file.Read(buffer, 8192-1); + file.Close(); + }else{ + buffer = DDEServer.Get_MPlayer_Game_Info(); + } + } + + /*------------------------------------------------------------------------ + Get the player's name + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle), buffer); + strcpy(Session.GameName, Session.Handle); + Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); + Session.PrefColor = Session.ColorIdx; + int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); + Session.House = (HousesType) ((int)HOUSE_USSR + temp); + + Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); + Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); + Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); + Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); + Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); + BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); + Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); + Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); + Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + + PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + + Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription), + buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + + if (PlanetWestwoodIsHost){ + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed then force the CD + ** to be there. + ** + */ + if (Session.Options.ScenarioIndex == -1) { + //WWDebugString ("RA95I - Session.Options.ScenarioIndex == -1\n"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) { +#else + if ( Expansion_CS_Present() ) { +#endif + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index=Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + //WWDebugString ("RA95I - CounterStrike CD not in drive\n"); + + if (!Force_CD_Available (RequiredCD)){ + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } + //WWDebugString ("RA95I - Returned from Force_CD_Available()\n"); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + + /* + ** See if that scenario is available now. Its fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + //if (Session.Options.ScenarioIndex == -1) + // WWDebugString ("RA95I - Session.Options.ScenarioIndex is still -1\n"); + + } + } + + + Options.GameSpeed = 0; + + + //MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + + if (name) delete buffer; + return (1); + +} +#endif // !WOLAPI_INTEGRATION + + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + +void Just_Path(char *path, char *destpath) +{ + char *terminator = NULL; //He'll be back. + + strcpy (destpath, path); + terminator = strrchr (destpath, '\\'); + if (terminator){ + *terminator = 0; + } +} + + + + + +/*********************************************************************************************** + * Is_User_WChat_Registered -- retrieve the users wchat entry from the registry * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if users wchat entry was found in the registry * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:13PM ST : Created * + *=============================================================================================*/ +bool Is_User_WChat_Registered(char *buffer, int buffer_len) +{ + // ST - 5/13/2019 + buffer; buffer_len; + return false; + +#if (0) + HKEY key; + char user_handle[256]; + DWORD user_handle_size = sizeof (user_handle); + char user_pword[256]; + DWORD user_pword_size = sizeof (user_pword); + + + /* + ** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there + */ + key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE); + + if (key){ + key = Get_Registry_Sub_Key (key, "Nick1", TRUE); + if (key){ + + if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){ + + if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){ + + /* + ** If the first char of the users name is non-numberic and there is a password + ** then return success + */ + if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){ + RegCloseKey( key ); + return (TRUE); + } + } + } + } + + RegCloseKey ( key ); + } + + + + /* + ** Check HKEY_LOCAL_MACKINE/Software + */ + user_handle_size = sizeof (user_handle); + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + memcpy (buffer, user_handle, min((unsigned)buffer_len, user_handle_size)); + + /* + ** If the first char of the users name is non-numeric then return success + */ + if (user_handle[0] < '0' || user_handle[0] > '9'){ + return (TRUE); + }else{ + return (FALSE); + } +#endif +} + + + +/*********************************************************************************************** + * Spawn_WChat -- spawns or switches focus to wchat * + * * + * * + * * + * INPUT: can launch. If set then we are allowed to launch WChat if not already running * + * * + * OUTPUT: True if wchat was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Poke_WChat(void); +bool Spawn_WChat(bool can_launch) +{ + // ST - 5/13/2019 + can_launch; + return false; + +#if (0) + WWDebugString ("RA95 - In Spawn_WChat.\n"); + char packet[10] = {"Hello"}; + HWND chat_window = NULL; + + /* + ** See if WChat is already running... + */ + if (WChatHWND && IsWindow (WChatHWND) ){ + chat_window = WChatHWND; + }else{ + chat_window = FindWindow ( "OWL_Window", "Westwood Chat" ); + } + + if (chat_window){ + /* + ** WChat is already running. Minimize myself then try to give it focus. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + + /* + ** Send chat a tickle message so it knows to send the game stats to the server. + */ + if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) { + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + } + + //Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + /* + ** Give the focus to WChat + */ + SetForegroundWindow ( chat_window ); + ShowWindow ( chat_window, SW_RESTORE ); + return(true); + } + + + /* + ** Fail if we aren't allowed to launch wchat and we couldnt find its window. + */ + if (!can_launch) return (false); + + + /* + ** Find where WChat was installed to + */ + HKEY key; + char wchat_loc[256]; + DWORD wchat_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "WChat", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + char justpath [256]; + Just_Path(wchat_loc, justpath); + + /* + ** We found WChat in the registry. Minimize myself then try to spawn it. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + + if (success){ + return (true); + }else{ + ShowWindow (MainWindow, SW_RESTORE); + while ( Keyboard->Check() ) {}; + return (false); + } +#endif +} + +#endif //#ifndef WOLAPI_INTEGRATION + +/*********************************************************************************************** + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: True if app was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Spawn_Registration_App(void) +{ + // ST - 5/13/2019 + return false; +#if (0) + /* + ** Find where inetreg was installed to + */ + + HKEY key; + char inetreg_loc[256]; + DWORD inetreg_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + char justpath [256]; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + Just_Path(inetreg_loc, justpath); + + BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + if (success){ + //WaitForSingleObject (process_info.hProcess, 1000*10000); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_RESTORE ); + } + return (success); +#endif +} + +#endif //#ifndef WOLAPI_INTEGRATION + + +/*********************************************************************************************** + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 8:30PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Do_The_Internet_Menu_Thang(void) +{ +#if (0) //PG + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + char packet[10] = {"Hello"}; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int width; + int height; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //buttons = &cancelbtn; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + char users_name[256]; + int buffer_len = sizeof (users_name); + bool process; + bool display; + KeyNumType input; + + + if (!Special.IsFromWChat && !SpawnedFromWChat){ + /* + ** If the user is registered with Planet Westwood then spawn WChat. + */ + if (Is_User_WChat_Registered(users_name, buffer_len)){ + GameStatisticsPacketSent = false; + if (!Spawn_WChat(true)){ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + WWMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK); + LogicPage->Clear(); + return(false); + } + }else{ + /* + ** OK, whatever, just run WChat anyway. + */ + if (!Spawn_WChat (true)){ + + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + + //WWMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_CANCEL); + WWMessageBox().Process(TXT_NO_REG_APP, TXT_CANCEL); + } + Load_Title_Page(true); + return(false); + } + } + + /* + ** + ** User is registered and we spawned WChat. Wait for a game start message from WChat. + ** + */ + + process = true; + display = true; + + while (process){ + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored = FALSE; + display = true; + } + + if (display) { + Set_Logic_Page(SeenBuff); + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + //cancelbtn.Zap(); + //buttons = &cancelbtn; + + /* + .................... Rebuild the button list .................... + */ + //buttons->Draw_All(); + cancelbtn.Draw_Me(true); + + Show_Mouse(); + display = false; + } + + + /* + ** See if the game start packet has arrived from wchat yet. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + return(true); + } + + //input = buttons->Input(); + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + } + + } +#endif + return (false); +} + +#endif //#ifndef WOLAPI_INTEGRATION + + + +#endif //WIN32 diff --git a/REDALERT/INTERNET.H b/REDALERT/INTERNET.H new file mode 100644 index 000000000..458f8a793 --- /dev/null +++ b/REDALERT/INTERNET.H @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/Internet.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 7 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTERNET_H +#define INTERNET_H + +#define IP_ADDRESS_MAX 40 + + +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +int Read_Game_Options(char *name); + +extern char PlanetWestwoodIPAddress[IP_ADDRESS_MAX]; +extern long PlanetWestwoodPortNumber; +extern bool PlanetWestwoodIsHost; + +#endif diff --git a/REDALERT/INTERPAL.CPP b/REDALERT/INTERPAL.CPP new file mode 100644 index 000000000..226aa4a2d --- /dev/null +++ b/REDALERT/INTERPAL.CPP @@ -0,0 +1,465 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTERPAL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTERPAL.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : December 7th 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This module contains functions to allow use of old 320x200 animations on a 640x400 screen * + * * + * Functions: * + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * Write_Interpolation_Palette -- writes an interpolation palette to disk * + * Create_Palette_Interpolation_Table -- build the palette interpolation table * + * Increase_Palette_Luminance -- increase the contrast of a palette * + * Interpolate_2X_Scale -- Stretch a 320x200 graphic buffer into 640x400 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +BOOL InterpolationPaletteChanged = FALSE; +extern "C" { +extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Double (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} + +#define SIZE_OF_PALETTE 256 +extern "C"{ + unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + unsigned char * InterpolationPalette; +} + + + +/*********************************************************************************************** + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Read_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (palette_file.Is_Available()) { + palette_file.Open(READ); + palette_file.Read(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + InterpolationPaletteChanged = FALSE; + } +} + + +/*********************************************************************************************** + * Write_Interpolation_Palette -- writes an interpolation palette table to disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Write_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (!palette_file.Is_Available()) { + palette_file.Open(WRITE); + palette_file.Write(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + } +} + + + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + +// Asm_Create_Palette_Interpolation_Table(); + + #if (1) + + int i; + int j; + int p; + unsigned char * first_palette_ptr; + unsigned char * second_palette_ptr; + unsigned char * match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) InterpolationPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) InterpolationPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) InterpolationPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + PaletteInterpolationTable[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + #endif + InterpolationPaletteChanged = FALSE; + return; + +} + + + + + + + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * cap value for colours * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char * palette , int red_percentage , int green_percentage , int blue_percentage , int cap) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iGet_IsDirectDraw()) { + if (!source->Lock()) { + if (dest == &SeenBuff) Show_Mouse(); + return; + } + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()) { + if (!dest->Lock()) { + if (source_locked) { + source->Unlock(); + } + if (dest == &SeenBuff) Show_Mouse(); + return; + } + dest_locked = TRUE; + } + + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + /* + ** Call the appropriate assembly language copy routine + */ +#if (1) + switch (CopyType) { + case 0: + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 1: + Asm_Interpolate_Line_Double( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 2: + Asm_Interpolate_Line_Interpolate( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + } +#endif + +#if (0) + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = PaletteInterpolationTable[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + last_dest_ptr += dest_width; + dest_ptr = last_dest_ptr; + } + } + +#endif + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + if (dest == &SeenBuff) Show_Mouse(); + +//BG long *longptr = (long *)&PaletteInterpolationTable[0][0]; +//BG Mono_Printf("Clock cycles: %08x\n",*longptr); +#endif +} +#endif + + + + + diff --git a/REDALERT/INTRO.CPP b/REDALERT/INTRO.CPP new file mode 100644 index 000000000..639ba3659 --- /dev/null +++ b/REDALERT/INTRO.CPP @@ -0,0 +1,302 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTRO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#if (0) //PG +VQAHandle * Open_Movie(char * name); +VQAHandle * Open_Movie(char * name) +{ + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + VQAHandle * vqa = VQA_Alloc(); + if (vqa) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, name, &AnimControl) != 0) { + VQA_Free(vqa); + vqa = 0; + } + } + return(vqa); +} +#endif + +/*********************************************************************************************** + * Choose_Side -- play the introduction movies, select house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 5/08/1995 BWG : Created. * + *=============================================================================================*/ +void Choose_Side(void) // ajw - In RA, all this did was play a movie. Denzil is using it in its original sense. +{ + + Whom = HOUSE_GOOD; + +#if (0) //PG + if (Special.IsFromInstall) { + #ifdef DVD // Denzil + if( Using_DVD() ) + { + Hide_Mouse(); + Load_Title_Page(); + GamePalette = CCPalette; + HidPage.Blit(SeenPage); + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + Show_Mouse(); + + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_SOVIET)) + { + case 0: + CurrentCD = 0; + break; + + case 1: + CurrentCD = 1; + break; + } + + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_SLOW); + SeenPage.Clear(); + } + #endif + + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); + } +#endif +// Scen.ScenPlayer = SCEN_PLAYER_GREECE; + +#ifdef OBSOLETE + static char const _yellowpal[]={0x0,0x0,0xC9,0x0,0xBA,0x0,0x93,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0xEE,0x0}; + static char const _redpal[] ={0x0,0x0,0xA8,0x0,0xD9,0x0,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + static char const _graypal[] ={0x0,0x0,0x17,0x0,0x10,0x0,0x12,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + void * anim; + VQAHandle * gdibrief=0, * nodbrief=0; + void const * staticaud, * oldfont; + void const * speechg, * speechn, * speech; + int statichandle, speechhandle, speechplaying = 0; + int oldfontxspacing = FontXSpacing; + int setpalette = 0; + + int frame = 0, endframe = 255, selection = 0, lettersdone = 0; + + Hide_Mouse(); +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + + Call_Back(); + + staticaud = Load_Alloc_Data(CCFileClass("STRUGGLE.AUD")); + speechg = Load_Alloc_Data(CCFileClass("GDI_SLCT.AUD")); + speechn = Load_Alloc_Data(CCFileClass("NOD_SLCT.AUD")); + +// staticaud = MixFileClass::Retrieve("STRUGGLE.AUD"); +// speechg = MixFileClass::Retrieve("GDI_SLCT.AUD"); +// speechn = MixFileClass::Retrieve("NOD_SLCT.AUD"); + + anim = Open_Animation("CHOOSE.WSA", NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), Palette); + Call_Back(); + + nodbrief = Open_Movie("NOD1PRE.VQA"); + Call_Back(); + gdibrief = Open_Movie("GDI1.VQA"); + + if (Special.IsFromInstall) { + Set_Video_Mode(MCGA_MODE); + PreserveVQAScreen = 1; +// Hide_Mouse(); + Play_Movie("INTRO2", THEME_NONE, false); + Show_Mouse(); + } + + HidPage.Clear(); + if (!Special.IsFromInstall) { + SeenPage.Clear(); +// Set_Palette(Palette); + Palette.Set(); + } else { + setpalette = 1; + } + + statichandle = Play_Sample(staticaud, 255, 64); + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME, 0, 180, _yellowpal)); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME2, 0, 187, _yellowpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_NOD_NAME, 180, 180, _redpal)); + +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 57, 190, _graypal)); +#else +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 103, 194, _graypal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 103, 190, _graypal)); +#endif +#endif + Keyboard->Clear(); + + while (endframe != frame || (speechplaying && Is_Sample_Playing(speech)) ) { + Animate_Frame(anim, HidPage, frame++); + Hide_Mouse(); + if (setpalette) { + Wait_Vert_Blank(VertBlank); + //Set_Palette(Palette); + Palette.Set(); + setpalette = 0; + } + HidPage.Blit(SeenPage, 0, 22, 0, 22, 320, 156); + Show_Mouse(); + + if (!Is_Sample_Playing(staticaud)) statichandle = Play_Sample(staticaud, 255, 64); + Call_Back_Delay(3); // delay only if haven't clicked + + /* keep the mouse hidden until the letters are thru printing */ + if (!lettersdone) { + lettersdone = true; + for (int i=0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) lettersdone = 0; + if (lettersdone) { + Show_Mouse(); + } + } + if (frame >= Get_Animation_Frame_Count(anim)) frame = 0; + if (Keyboard->Check() && endframe == 255) { + if ((Keyboard->Get() & 0xFF) == KN_LMOUSE) { + if ((MouseQY > 48) && (MouseQY < 150)) { + if ((MouseQX > 18) && (MouseQX < 148)) { + + // Chose GDI + Whom = HOUSE_GOOD; + ScenPlayer = SCEN_PLAYER_GDI; + endframe = 0; + speechhandle = Play_Sample(speechg); + speechplaying = true; + speech = speechg; + } else if ((MouseQX > 160) && (MouseQX < 300)) { + // Chose Nod + selection = 1; + endframe = 14; + Whom = HOUSE_BAD; + ScenPlayer = SCEN_PLAYER_NOD; + speechhandle = Play_Sample(speechn); + speechplaying = true; + speech = speechn; + } + } + } + } + } + + Hide_Mouse(); + Close_Animation(anim); + + // erase the "choose side" text + SeenBuff.Fill_Rect(0, 180, 319, 199, 0); + + Keyboard->Clear(); + + /* play the scenario 1 briefing movie */ + if (Whom == HOUSE_GOOD) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + if (gdibrief) { +#ifdef CHEAT_KEYS +#else + VQA_Play(gdibrief, VQAMODE_RUN); +#endif + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + } else { + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + if (nodbrief) { +#ifdef CHEAT_KEYS +#else + VQA_Play(nodbrief, VQAMODE_RUN); +#endif + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + } + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + if (Whom == HOUSE_GOOD) { + /* + ** Make sure the screen's fully clear after the movie plays + */ + SeenPage.Clear(); + + BlackPalette.Adjust(WhitePalette, 0x08); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + +// memset(BlackPalette, 0x01, 768); +// Set_Palette(BlackPalette); +// memset(BlackPalette, 0x00, 768); + } else { + PreserveVQAScreen = 1; + } + Free(staticaud); + Free(speechg); + Free(speechn); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; +#endif +} diff --git a/REDALERT/INTRO.H b/REDALERT/INTRO.H new file mode 100644 index 000000000..7ebde0d17 --- /dev/null +++ b/REDALERT/INTRO.H @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTRO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTRO_H +#define INTRO_H + +void Choose_Side(void); + +#endif diff --git a/REDALERT/IOMAP.CPP b/REDALERT/IOMAP.CPP new file mode 100644 index 000000000..c46ff5d12 --- /dev/null +++ b/REDALERT/IOMAP.CPP @@ -0,0 +1,517 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IOMAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOMAP.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : March 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * All map-related loading/saving routines should go in this module, so it can be overlayed. * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * CellClass::Load -- Reads from a save game file. * + * CellClass::Save -- Write to a save game file. * + * CellClass::Should_Save -- Should the cell be written to disk? * + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Load -- Loads from a save game file. * + * MouseClass::Save -- Saves to a save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CellClass::Should_Save -- Should the cell be written to disk? * + * * + * This function will determine if the cell needs to be written to disk. Any cell that * + * contains special data should be written to disk. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should this cell's data be written to disk? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Should_Save(void) const +{ + static CellClass const _identity_cell; + + return(memcmp(&_identity_cell, this, sizeof(*this)) != 0); +} + + +/*********************************************************************************************** + * CellClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Load(Straw & file) +{ + file.Get(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Save(Pipe & file) const +{ + file.Put(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Code_Pointers(void) +{ + if (Cell_Occupier() != NULL) { + OccupierPtr = (ObjectClass *)OccupierPtr->As_Target(); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL && Overlapper[index]->IsActive) { + Overlapper[index] = (ObjectClass *)Overlapper[index]->As_Target(); + } else { + Overlapper[index] = NULL; + } + } +} + + +/*********************************************************************************************** + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Decode_Pointers(void) +{ + if (OccupierPtr != NULL) { + OccupierPtr = As_Object((TARGET)OccupierPtr, false); + assert(OccupierPtr != NULL); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + Overlapper[index] = As_Object((TARGET)Overlapper[index], false); + assert(Overlapper[index] != NULL); + } + } +} + + +/*********************************************************************************************** + * MouseClass::Load -- Loads from a save game file. * + * * + * Loading the map is very complicated. Here are the steps: * + * - Read the Theater for this save-game * + * - call Init_Theater to perform theater-specific inits * + * - call Free_Cells to free the cell array, because loading the map object will overwrite * + * the pointer to the cell array * + * - read the map object from disk * + * - call Alloc_Cells to re-allocate the cell array * + * - call Init_Cells to set the cells to a known state, because not every cell will be loaded * + * - read the cell objects into the cell array * + * - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be * + * called to restore the map's button list to the proper state. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool MouseClass::Load(Straw & file) +{ + /* + ** Load Theater: Even though this value is located in the DisplayClass, + ** it must be loaded first so initialization can be done before any other + ** map data is loaded. If initialization isn't done first, data read from + ** disk will be over-written when initialization occurs. This code must + ** go in the most-derived Map class. + */ + TheaterType theater; + if (file.Get(&theater, sizeof(theater)) != sizeof(theater)) { + return(false); + } + +#ifdef WIN32 +LastTheater = THEATER_NONE; +#endif + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 +// if (theater != LastTheater) { + Reset_Theater_Shapes(); +// } +#endif //WIN32 + + /* + ** Init display mixfiles + */ + Init_Theater(theater); + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + //LastTheater = Scen.Theater; + + /* + ** Free the cell array, because we're about to overwrite its pointers + */ + Free_Cells(); + + /* + ** Read the entire map object in. Only read in sizeof(MouseClass), so if we're + ** in editor mode, none of the map editor object is read in. + */ + file.Get(this, sizeof(*this)); +#ifdef SCENARIO_EDITOR + new(this) MapEditClass(NoInitClass()); +#else + new(this) MouseClass(NoInitClass()); +#endif + + /* + ** Reallocate the cell array + */ + Alloc_Cells(); + + /* + ** Init all cells to empty + */ + Init_Cells(); + + /* + ** Read # cells saved + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Read cells + */ + for (int index = 0; index < count; index++) { + CELL cell = 0; + if (file.Get(&cell, sizeof(cell)) != sizeof(cell)) { + return(false); + } + + if (!(*this)[cell].Load(file)) { + return(false); + } + } + + LastTheater = Scen.Theater; + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Save -- Save to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/26/1996 JLB : Cleaned up. * + *=============================================================================================*/ +bool MouseClass::Save(Pipe & file) const +{ + /* + ** Save Theater >first< + */ + TheaterType theater = Scen.Theater; + file.Put(&theater, sizeof(theater)); + + file.Put(this, sizeof(*this)); + + /* + ** Count how many cells will be saved. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + count++; + } + cellptr++; + } + + /* + ** write out count of the cells. + */ + file.Put(&count, sizeof(count)); + + /* + ** Save cells that need it + */ + cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + file.Put(&cell, sizeof(cell)); + cellptr->Save(file); + count--; + } + cellptr++; + } + + if (count != 0) return(false); + + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Code_Pointers(void) +{ + /* + ** Code PendingObjectPtr. + */ + if (PendingObjectPtr) { + PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target(); + } + + /* + ** Fix for saving game while in structure placement mode. ST - 4/15/2020 2:41PM + */ + memset(CursorShapeSave, 0, sizeof(CursorShapeSave)); + if (CursorSize && CursorSize != CursorShapeSave) { + + int save_buffer_element_size = sizeof(CursorShapeSave) / sizeof(CursorShapeSave[0]); + + int index = 0; + + while (index < save_buffer_element_size - 2 && CursorSize[index] != REFRESH_EOL) { + CursorShapeSave[index] = CursorSize[index]; + index++; + } + CursorShapeSave[index] = REFRESH_EOL; + } + + /* + ** Chain to parent. + */ + MapClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Decode_Pointers(void) +{ + /* + ** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd + ** have to reference PendingObjectPtr->Class_Of(), and the object that + ** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't + ** decode PendingObjectPtr, we can't set the placement cursor shape here + ** either. These have to be done as last-minute fixups. + */ + if (PendingObjectPtr) { + PendingObjectPtr = As_Object((TARGET)PendingObjectPtr, false); + assert(PendingObjectPtr != NULL); + } + + if (CursorSize) { + CursorSize = CursorShapeSave; + } + + /* + ** Chain to parent. + */ + MapClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Code_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Code_Pointers(); + cellptr++; + } +} + + +/*********************************************************************************************** + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Decode_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Decode_Pointers(); + cellptr++; + } +} + diff --git a/REDALERT/IOOBJ.CPP b/REDALERT/IOOBJ.CPP new file mode 100644 index 000000000..3cee98e26 --- /dev/null +++ b/REDALERT/IOOBJ.CPP @@ -0,0 +1,898 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IOOBJ.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOOBJ.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : May 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Load -- Reads from a save game file. * + * LayerClass::Save -- Write to a save game file. * + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * ReinforcementClass::Code_Pointers -- codes class's pointers for load/save * + * ReinforcementClass::Decode_Pointers -- decodes pointers for load/save * + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Code_Pointers(void) +{ + /* + ** Code the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = (TechnoTypeClass *)Members[i].Class->As_Target(); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Decode_Pointers(void) +{ + /* + ** Decode the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = As_TechnoType((TARGET)Members[i].Class); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 05/13/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Code_Pointers(void) +{ + /* + ** Code the 'Member' + */ + if (Member) { + Member = (FootClass *)Member->As_Target(); + } +} + + +/*********************************************************************************************** + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Decode_Pointers(void) +{ + /* + ** Decode the 'Member' + */ + if (Member) { + Member = (FootClass *)As_Techno((TARGET)Member, false); + assert(Member != NULL); + } +} + + +/*********************************************************************************************** + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Code_Pointers(void) +{ + Action1.Code_Pointers(); + Action2.Code_Pointers(); +} + + +/*********************************************************************************************** + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Decode_Pointers(void) +{ + Action1.Decode_Pointers(); + Action2.Decode_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Code_Pointers(void) +{ + /* + ** Code 'Payback' + */ + if (Payback) { + Payback = (TechnoClass *)Payback->As_Target(); + } + + /* + ** Chain to parent + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Decode_Pointers(void) +{ + /* + ** Decode 'Payback' + */ + if (Payback) { + Payback = As_Techno((TARGET)Payback, false); + assert(Payback != NULL); + } + + /* + ** Chain to parent + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Code_Pointers(void) +{ + if (Object) { + Object = (TechnoClass *)Object->As_Target(); + } + + ((HouseClass *&)House) = (HouseClass *)House->Class->House; +} + + +/*********************************************************************************************** + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Decode_Pointers(void) +{ + if (Object) { + Object = As_Techno((TARGET)Object, false); + assert(Object != NULL); + } + + unsigned int house_ptr_val = *((unsigned int*)&House); + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)house_ptr_val); + + assert(House != NULL); +} + + +/*********************************************************************************************** + * LayerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Load(Straw & file) +{ + /* + ** Read # elements in the layer + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Clear the array + */ + Clear(); + + /* + ** Read in all array elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr; + if (file.Get(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) { + return(false); + } + Add(ptr); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Save(Pipe & file) const +{ + /* + ** Save # array elements + */ + int count = Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save all elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr = (*this)[index]; + file.Put(&ptr, sizeof(ObjectClass *)); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Code_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + assert(obj != NULL); + (*this)[index] = (ObjectClass *)(obj->As_Target()); + } +} + + +/*********************************************************************************************** + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Decode_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + TARGET target = (TARGET)(*this)[index]; + (*this)[index] = (ObjectClass *)As_Object(target, false); + assert((*this)[index] != NULL); + } +} + + +/*********************************************************************************************** + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Decode_Pointers(void) +{ + /* + ** Re-assign the house's remap table (for multiplayer game loads) + ** Loading the house from disk will have over-written the house's RemapTable, so + ** Init_Data() is called to reset it to a valid pointer. + */ + + Init_Data(RemapColor, ActLike, Credits); +} + + +/*********************************************************************************************** + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Code_Pointers(void) +{ + RealTime.Stop(); +} + + +/*********************************************************************************************** + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Decode_Pointers(void) +{ + RealTime.Start(); +} + + +/*********************************************************************************************** + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Code_Pointers(void) +{ + if (Member != NULL && Member->IsActive) { + Member = (FootClass *)Member->As_Target(); + } else { + Member = TARGET_NONE; + } + + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Decode_Pointers(void) +{ + if ((TARGET)Member != TARGET_NONE) { + Member = (FootClass *)As_Techno((TARGET)Member, false); + assert(Member != NULL); + } + + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Code_Pointers(void) +{ + /* + ** Code 'Radio' + */ + if (Radio) { + Radio = (RadioClass *)Radio->As_Target(); + } + + MissionClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Decode_Pointers(void) +{ + /* + ** Decode 'Radio' + */ + if (Radio) { + Radio = As_Techno((TARGET)Radio, false); + assert(Radio != NULL); + } + + MissionClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Code_Pointers(void) +{ + CargoClass::Code_Pointers(); + RadioClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Decode_Pointers(void) +{ + CargoClass::Decode_Pointers(); + RadioClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Code_Pointers(void) +{ + /* + ** Code 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)CargoHold->As_Target(); + } +} + + +/*********************************************************************************************** + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Decode_Pointers(void) +{ + /* + ** Decode 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)As_Techno((TARGET)CargoHold, false); + assert(CargoHold != NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Code_Pointers(void) +{ + if (Next) { + Next = (ObjectClass *)Next->As_Target(); + } +} + + +/*********************************************************************************************** + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Decode_Pointers(void) +{ + if (Next) { + Next = As_Object((TARGET)Next, false); + assert(Next != NULL); + } +} diff --git a/REDALERT/IPX.CPP b/REDALERT/IPX.CPP new file mode 100644 index 000000000..c5ce6da2a --- /dev/null +++ b/REDALERT/IPX.CPP @@ -0,0 +1,1164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.CPP * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 15, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Pitfalls: * + * - Never try to use a closed socket; always check the return code from * + * IPX_Open_Socket(). * + * - Always give IPX an outstanding ECB for listening, before you send. * + * - It turns out that IPX is pretty bad about saving registers, so if * + * you have any register variables in your program, they may get * + * trashed. To circumvent this, all functions in this module save & * + * restore the registers before invoking any IPX or NETX function. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * IPX_Close_Socket -- closes an open socket * + * IPX_Get_Connection_Number -- gets local Connection Number * + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * IPX_Get_User_ID -- gets user ID from Connection Number * + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * IPX_Send_Packet -- commands IPX to send a packet * + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * IPX_Cancel_Event -- cancels an operation in progress * + * Let_IPX_Breath -- gives IPX some CPU time * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include +//#include +#include "ipx.h" + +#ifdef WIN32 +#include "ipx95.h" +#endif //WIN32 + + +// Turn off "expression is not meaningful". +//#pragma warning 628 9 + + +/*************************************************************************** + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1994 BR : Created. * + *=========================================================================*/ +int IPX_SPX_Installed(void) +{ + return false; +#ifdef WIN32 + +#ifdef TIBERIAN_SUN + return(false); +#else +#if (0)//PG + if ( Load_IPX_Dll () ){ + return ( IPX_Initialise() ); + }else{ + return(false); + } +#endif +#endif + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Init all registers to 0's + //------------------------------------------------------------------------ + memset (®s, 0, sizeof(regs)); + segread (&sregs); + memset (&rmi, 0, sizeof(rmi)); + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call, function 0x300 + //------------------------------------------------------------------------ + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x002f; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the real-mode interrupt handler. + // To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke + // interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, + // AL will be 0xff, and ES:DI will contain the IPX/SPX function address. + //------------------------------------------------------------------------ + rmi.eax = 0x00007a00; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // If IPX isn't there, return 0 + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) != 0xff) { + return(0); + } + + //------------------------------------------------------------------------ + // Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. + // If SPX is present, AL will be 0xff. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = 0x00000010; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // SPX is installed; return '2' + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) == 0xff) { + return(2); + } + + //------------------------------------------------------------------------ + // SPX is not installed; return '1' + //------------------------------------------------------------------------ + return(1); +#endif //WIN32 + +} /* end of IPX_SPX_Installed */ + + +/*************************************************************************** + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * * + * INPUT: * + * socket the socket number to open * + * * + * OUTPUT: * + * 0 = OK * + * -1 = IPX not installed * + * 0xfe = socket table is full * + * 0xff = socket is already open * + * * + * WARNINGS: * + * The application must define its socket number carefully. Use * + * values from 0x4000 to 0x8000 for custom socket numbers. The app * + * must know its own socket number as well as the socket number of * + * a destination workstation. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ + +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Open_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int rc; + + //------------------------------------------------------------------------ + // Open the socket: + // DX = socket number + // AL = 0 for short-lived socket, 0xff for long-lived socket + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF (&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_OPEN_SOCKET; // function code + rmi.edx = socket; // desired socket # + rmi.eax = 0x00ff; // make this a long-lived socket + + //........................................................................ + // call DPMI + //........................................................................ + int386x (DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0xff); + + return(rc); + +} /* end of IPX_Open_Socket */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Close_Socket -- closes an open socket * + * * + * INPUT: * + * socket socket number to close * + * * + * OUTPUT: * + * 0 = ok, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Close_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Close the socket: + // DX = socket number + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_CLOSE_SOCKET; + rmi.edx = socket; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(0); + +} /* end of IPX_Close_Socket */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_Connection_Number -- gets local Connection Number * + * * + * This Novell call will the return the user's local "Connection Number". * + * This value will be 0 if the user isn't logged into Novell, so this * + * routine can be used to detect if other calls (such as Get_Local_Target) * + * will be OK. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Connection Number, 0 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Connection_Number(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int num; + + //------------------------------------------------------------------------ + // Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local + // connection number into AL. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000dc00; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + num = rmi.eax & 0x00ff; + + return(num); + +} /* end of IPX_Get_Connection_Number */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * * + * This gets the Connection Number for the given User ID. Since a user * + * may be logged in more than once, this just returns the first connection * + * found and ignores the others. * + * * + * INPUT: * + * username name of the user to get the Connection Number for * + * * + * OUTPUT: * + * first-found Connection Number for that user, 0 if user not logged in * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_1st_Connection_Num (char * username) +{ + struct request_buffer { + unsigned short len; // username length + 5 + unsigned char buffer_type; // ConnectionNum = 0x15 + unsigned short object_type; // set ot 0x0100 + unsigned char name_len; // length of username + char name [48]; // copy of username + unsigned short reserved; + }; + struct reply_buffer { + unsigned short len; + unsigned char number_connections; // will be 0 - 100 + unsigned char connection_num [100]; // array of connection numbers + unsigned short reserved[2]; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + int num_conns; // # connections returned + int conn_num; // connection number + int rc; + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = (unsigned short)(strlen(username) + 5); + reqbuf->buffer_type = 0x15; + reqbuf->object_type = 0x0100; + reqbuf->name_len = (unsigned char) strlen(username); + strcpy(reqbuf->name, username); + reqbuf->reserved = reqbuf->reserved; // prevent compiler warning + replybuf->len = 101; + replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning + replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Stash the 1st connection number + //------------------------------------------------------------------------ + rc = (rmi.eax & 0x00ff); // if AL !=0, error + num_conns = replybuf->number_connections; // # times user is logged in + conn_num = (int )replybuf->connection_num[0]; // 1st connection # + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // Return error if function failed, or user not logged in + //------------------------------------------------------------------------ + if (rc != 0 || num_conns==0) { + return(0); + } + else { + return(conn_num); + } + +} /* end of IPX_Get_1st_Connection_Num */ + +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * * + * Once you've obtained a Connection Number from IPX_Get_Connection_Number * + * or IPX_Get_1st_Connection_Num, use this function to translate it into * + * a Network Number and Node Address; then, place those numbers in the * + * IPX header for outgoing packets. * + * * + * INPUT: * + * connection_number Connection Number to translate * + * network_number ptr: will hold Network Number * + * physical_node ptr: will hold Node Address * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * If connection_number is 0 and NETX isn't running, this routine * + * will just put garbage into the network_number and physical_node. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Internet_Address(int connection_number, + unsigned char * network_number, unsigned char * physical_node) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // Internet = 0x13 + unsigned char connection_number; // Conn. Number to translate + }; + struct reply_buffer { + unsigned short len; + unsigned char network_number [4]; // filled in by IPX + unsigned char physical_node [6]; // filled in by IPX + unsigned short server_socket; // filled in by IPX, but don't use! + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x13; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = 12; + replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning + replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning + replybuf->server_socket = replybuf->server_socket; // suppress warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + memcpy(network_number, replybuf->network_number, 4); + memcpy(physical_node, replybuf->physical_node, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_Internet_Address */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_User_ID -- gets user ID from Connection Number * + * * + * INPUT: * + * connection_number Connection Number to get User ID for * + * user_id ptr to buffer to put User ID into; * + * size must be >= 48 chars * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_User_ID(int connection_number, char * user_id) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // 0x16 = UserID buffer type + unsigned char connection_number; // Connection Number to get ID for + }; + struct reply_buffer { + unsigned short len; + unsigned char object_id[4]; + unsigned char object_type[2]; + char object_name[48]; + char login_time[7]; + unsigned short reserved; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x16; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = sizeof(struct reply_buffer) - 2; + replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings + replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings + replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings + replybuf->reserved = replybuf->reserved; // suppress warnings + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Fill in the caller's buffer with the user name + //------------------------------------------------------------------------ + strncpy(user_id, replybuf->object_name, 48); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_User_ID */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are "listening" on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas to * + * store the incoming data in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of data buffer, for the packet * + * Packet[1].Length: size of the data buffer * + * * + * When the packet is received, ECBType.CompletionCode will be 0 if * + * successful. Otherwise, some error occurred. * + * * + * You should initialize the ECB to 0's before filling it in. * + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * + * * + * OUTPUT: * + * 0 = OK, IPX error otherwise * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Listen_For_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_LISTEN_FOR_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(rmi.eax & 0x00ff); + +} /* end of IPX_Listen_For_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Send_Packet -- commands IPX to send a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are sending on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas the * + * outgoing data is stored in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of buffer containing data to send * + * Packet[1].Length: size of the data buffer * + * ImmediateAddress: must be filled in with the node address of * + * the bridge that will route the message; * + * fill this in by calling IPX_Get_Local_Target * + * * + * Also, you must fill in the IPXHeaderType with certain values: * + * PacketType: set to 4 for IPX * + * DestNetworkNumber: Network Number of the destination system * + * DestNetworkNode: Node Address of the destination system * + * DestNetworkSocket: the destination system's socket to send to; * + * this doesn't have to be the same as the * + * socket opened on the local machine. * + * * + * You should initialize the ECB & IPXHeader to 0's before filling them in.* + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB * + * * + * OUTPUT: * + * none. This function doesn't return anything; the caller must check * + * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void IPX_Send_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_SEND_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of IPX_Send_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * * + * Use this function to fill in the ECB's ImmediateAddress field when * + * sending a packet. The Immediate Address is the node address of the * + * bridge that must route the message. If there is no bridge, it's * + * filled in with the destination Node Address. In either case, it * + * will contain the proper value for sending. * + * * + * INPUT: * + * dest_network destination Network Number * + * dest_node destination Node Address * + * dest_socket destination Socket Number * + * bridge_address field to fill in with Immediate Address * + * * + * OUTPUT: * + * 0 = OK, error otherwise * + * * + * WARNINGS: * + * If the Connection Number is 0 (user not logged in), dest_network * + * and dest_node will be garbage, and this routine will probably crash. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + struct request_buffer { + unsigned char network_number [4]; + unsigned char physical_node [6]; + unsigned short socket; + }; + struct reply_buffer { + unsigned char local_target [6]; + }; + unsigned int rc; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + memcpy(reqbuf->network_number, dest_network, 4); + memcpy(reqbuf->physical_node, dest_node, 6); + reqbuf->socket = dest_socket; + + //------------------------------------------------------------------------ + // Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_GET_LOCAL_TARGET; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + memcpy(bridge_address, replybuf->local_target, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(rc); + +} /* end of IPX_Get_Local_Target */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Cancel_Event -- cancels an operation in progress * + * * + * INPUT: * + * ecb_ptr pointer to ECB event to cancel * + * * + * OUTPUT: * + * ??? * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Cancel_Event(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + unsigned int rc; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_CANCEL_EVENT; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + + return(rc); + +} /* end of IPX_Cancel_Event */ +#endif //WIN32 + +/*************************************************************************** + * Let_IPX_Breath -- gives IPX some CPU time * + * * + * Use this function if you're polling the ECB's InUse flag, waiting * + * for it to go to 0: * + * * + * while ECBType.InUse * + * Let_IPX_Breath(); * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void Let_IPX_Breath(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_RELINQUISH_CONTROL; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of Let_IPX_Breath */ +#endif //WIN32 +/**************************** end of ipx.cpp *******************************/ diff --git a/REDALERT/IPX.H b/REDALERT/IPX.H new file mode 100644 index 000000000..79edd2f29 --- /dev/null +++ b/REDALERT/IPX.H @@ -0,0 +1,188 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.H * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 14, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPX_H +#define IPX_H + +/* +******************************** Structures ********************************* +*/ +typedef unsigned char NetNumType[4]; +typedef unsigned char NetNodeType[6]; +typedef char UserID[48]; + +/*--------------------------------------------------------------------------- +This is the IPX Packet structure. It's followed by the data itself, which +can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +field; annotation of 'APP' means the application must set the field. +NOTE: All header fields are ordered high-byte,low-byte. +---------------------------------------------------------------------------*/ +typedef struct IPXHEADER { + unsigned short CheckSum; // IPX: Not used; always 0xffff + unsigned short Length; // IPX: Total size, incl header & data + unsigned char TransportControl; // IPX: # bridges message crossed + unsigned char PacketType; // APP: Set to 4 for IPX (5 for SPX) + unsigned char DestNetworkNumber[4]; // APP: destination Network Number + unsigned char DestNetworkNode[6]; // APP: destination Node Address + unsigned short DestNetworkSocket; // APP: destination Socket Number + unsigned char SourceNetworkNumber[4]; // IPX: source Network Number + unsigned char SourceNetworkNode[6]; // IPX: source Node Address + unsigned short SourceNetworkSocket; // IPX: source Socket Number +} IPXHeaderType; + +/*--------------------------------------------------------------------------- +This is the IPX Event Control Block. It serves as a communications area +between IPX and the application for a single IPX operation. You should set +up a separate ECB for each IPX operation you perform. +---------------------------------------------------------------------------*/ +typedef struct ECB { + void *Link_Address; + void (*Event_Service_Routine)(void); // APP: event handler (NULL=none) + unsigned char InUse; // IPX: 0 = event complete + unsigned char CompletionCode; // IPX: event's return code + unsigned short SocketNumber; // APP: socket to send data through + unsigned short ConnectionID; // returned by Listen (???) + unsigned short RestOfWorkspace; + unsigned char DriverWorkspace[12]; + unsigned char ImmediateAddress[6]; // returned by Get_Local_Target + unsigned short PacketCount; + struct { + void *Address; + unsigned short Length; + } Packet[2]; +} ECBType; + + +/*--------------------------------------------------------------------------- +This structure is used for calling DPMI function 0x300, Call-Real-Mode- +Interrupt. It passes register values to & from the interrupt handler. +---------------------------------------------------------------------------*/ +typedef struct { + long edi; + long esi; + long ebp; + long Reserved; + long ebx; + long edx; + long ecx; + long eax; + short Flags; + short es; + short ds; + short fs; + short gs; + short ip; + short cs; + short sp; + short ss; +} RMIType; + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +These defines are for the IPX functions. The function number is specified +by placing it in BX when IPX is called. There are two ways to invoke IPX: +use interrupt 0x7a, or use a function whose address is obtained by calling +interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +This is the preferred method, since other apps are known to use int 0x7a. +---------------------------------------------------------------------------*/ +#define IPX_OPEN_SOCKET 0x0000 +#define IPX_CLOSE_SOCKET 0x0001 +#define IPX_GET_LOCAL_TARGET 0x0002 +#define IPX_SEND_PACKET 0x0003 +#define IPX_LISTEN_FOR_PACKET 0x0004 +#define IPX_SCHEDULE_EVENT 0x0005 +#define IPX_CANCEL_EVENT 0x0006 +#define IPX_GET_INTERVAL_MARKER 0x0008 +#define IPX_GET_INTERNETWORK_ADDRESS 0x0009 +#define IPX_RELINQUISH_CONTROL 0x000A +#define IPX_DISCONNECT_FROM_TARGET 0x000B + +/*--------------------------------------------------------------------------- +These defines are for various IPX error codes: +---------------------------------------------------------------------------*/ +#define IPXERR_CONNECTION_SEVERED 0x00ec +#define IPXERR_CONNECTION_FAILED 0x00ed +#define IPXERR_NO_CONNECTION 0x00ee +#define IPXERR_CONNECTION_TABLE_FULL 0x00ef +#define IPXERR_NO_CANCEL_ECB 0x00f9 +#define IPXERR_NO_PATH 0x00fa +#define IPXERR_ECB_INACTIVE 0x00fc +#define IPXERR_INVALID_PACKET_LENGTH 0x00fd +#define IPXERR_SOCKET_TABLE_FULL 0x00fe +#define IPXERR_SOCKET_ERROR 0x00ff + +/*--------------------------------------------------------------------------- +These defines are for various interrupt vectors and DPMI functions: +---------------------------------------------------------------------------*/ +#define IPX_INT 0x007a +#define DPMI_INT 0x0031 +#define DPMI_ALLOC_DOS_MEM 0x0100 +#define DPMI_FREE_DOS_MEM 0x0101 +#define DPMI_CALL_REAL_INT 0x0300 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/* +******************************** Prototypes ********************************* +*/ +/*--------------------------------------------------------------------------- +NOTE: All routines return 0 for a success code and one of the above IPX +error codes if there's an error, EXCEPT: +- IPX_SPX_Installed (0 = not installed) +- Get_Connection_Number / Get_1st_Connection_Number (0 = no connection) +---------------------------------------------------------------------------*/ +/* +.................................. ipx.cpp .................................. +*/ +int IPX_SPX_Installed(void); +int IPX_Open_Socket(unsigned short socket); +int IPX_Close_Socket(unsigned short socket); +int IPX_Get_Connection_Number(void); +int IPX_Get_1st_Connection_Num (char *username); +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node); +int IPX_Get_User_ID(int connection_number, char *user_id); +int IPX_Listen_For_Packet(struct ECB *ecb_ptr); +void IPX_Send_Packet(struct ECB *ecb_ptr); +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address); +int IPX_Cancel_Event(struct ECB *ecb_ptr); +void Let_IPX_Breath(void); + +#endif + +/***************************** end of ipx.h ********************************/ + diff --git a/REDALERT/IPX95.CPP b/REDALERT/IPX95.CPP new file mode 100644 index 000000000..784f4a4bb --- /dev/null +++ b/REDALERT/IPX95.CPP @@ -0,0 +1,231 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : July 10th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Windows 95 equivelent functions from IPX.CPP * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#ifdef WIN32 + +#include "ipx95.h" + + // Stub in old IPX here ST - 12/20/2018 1:53PM +extern "C" { + extern BOOL __stdcall IPX_Initialise(void) { return 0; } + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer) { return 0; } + extern void __stdcall IPX_Shut_Down95(void) {} + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*) { return 0; } + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int) { return 0; } + extern BOOL __stdcall IPX_Start_Listening95(void) { return 0; } + extern int __stdcall IPX_Open_Socket95(int socket) { return 0; } + extern void __stdcall IPX_Close_Socket95(int socket) {} + extern int __stdcall IPX_Get_Connection_Number95(void) { return 0; } + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*) { return 0; } +} + +#if (0) +/* +** Instance handle for the THIPX32 .DLL +*/ +HINSTANCE IpxDllInstance = NULL; + +/* +** Function pointers +*/ +//extern "C" { + IPXInitialiseType IPX_Initialise = NULL; + IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95 = NULL; + IPXShutDown95Type IPX_Shut_Down95 = NULL; + IPXSendPacket95Type IPX_Send_Packet95 = NULL; + IPXBroadcastPacket95Type IPX_Broadcast_Packet95 = NULL; + IPXStartListening95Type IPX_Start_Listening95 = NULL; + IPXOpenSocket95Type IPX_Open_Socket95 = NULL; + IPXCloseSocket95Type IPX_Close_Socket95 = NULL; + IPXGetConnectionNumber95Type IPX_Get_Connection_Number95 = NULL; + IPXGetLocalTarget95 IPX_Get_Local_Target95 = NULL; +//} + +char const *FunctionNames[] = { + "_IPX_Initialise", + "_IPX_Get_Outstanding_Buffer95", + "_IPX_Shut_Down95", + "_IPX_Send_Packet95", + "_IPX_Broadcast_Packet95", + "_IPX_Start_Listening95", + "_IPX_Open_Socket95", + "_IPX_Close_Socket95", + "_IPX_Get_Connection_Number95", + "_IPX_Get_Local_Target95", + NULL +}; +#endif + +extern void Get_OS_Version (void); +bool WindowsNT = false; + + +/*********************************************************************************************** + * Load_IPX_Dll -- Loads the THIPX32 DLL * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if DLL loaded * + * * + * WARNINGS: This call will fail under NT due to a side effect of loading the THIPX32.DLL * + * which causes the 16 bit DLL THIPX16.DLL to load. * + * * + * HISTORY: * + * 4/1/97 11:40AM ST : Created * + *=============================================================================================*/ +bool Load_IPX_Dll (void) +{ + //ST 5/13/2019 + return false; + +#if (0) + Get_OS_Version(); + if (WindowsNT) return (false); + + SetErrorMode( SEM_NOOPENFILEERRORBOX ); + IpxDllInstance = LoadLibrary ( "THIPX32.DLL" ); + SetErrorMode ( 0 ); + + if ( IpxDllInstance ){ + + const char *function_name; + unsigned long *fptr = (unsigned long *) &IPX_Initialise; + int count = 0; + + do { + function_name = FunctionNames[count]; + if (function_name){ + *fptr = (unsigned long) GetProcAddress (IpxDllInstance, function_name); + assert (*fptr != NULL); + fptr++; + count++; + } + + } while ( function_name ); + + return (true); + + }else{ + return (false); + } +#endif +} + + + +/*********************************************************************************************** + * Unload_IPX_Dll -- Frees the THIPX32 library * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 2:37PM ST : Created * + *=============================================================================================*/ +void Unload_IPX_Dll (void) +{ + //ST 5/13/2019 + +#if (0) + if (IpxDllInstance){ + FreeLibrary (IpxDllInstance); + IpxDllInstance = NULL; + } +#endif +} + + +int IPX_Open_Socket(unsigned short socket) +{ + return -1; //ST 5/13/2019 + //return ( IPX_Open_Socket95((int)socket)); +} + +int IPX_Close_Socket(unsigned short socket) +{ + //ST 5/13/2019 IPX_Close_Socket95((int)socket); + return (0); +} + + +int IPX_Get_Connection_Number(void) +{ + return -1;//ST 5/13/2019 + //return (IPX_Get_Connection_Number95()); +} + + + +int IPX_Broadcast_Packet(unsigned char * buf, int buflen) +{ + return 0; //ST 5/13/2019 + //return(IPX_Broadcast_Packet95(buf, buflen)); +} + +#if (0) //ST 5/13/2019 +extern "C"{ + extern void __cdecl Int3(void); +} +#endif + +int IPX_Get_Local_Target(unsigned char * dest_network, unsigned char * dest_node, + unsigned short dest_socket, unsigned char * bridge_address) +{ + //Int3(); + + return 0; //ST 5/13/2019 + //return (IPX_Get_Local_Target95(dest_network, dest_node, dest_socket, bridge_address)); +} + + +#endif //WIN32 diff --git a/REDALERT/IPX95.H b/REDALERT/IPX95.H new file mode 100644 index 000000000..54bb5faa3 --- /dev/null +++ b/REDALERT/IPX95.H @@ -0,0 +1,101 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#if (0) + +/* +** Types for function pointers +*/ +typedef BOOL __stdcall (*IPXInitialiseType) (void); +typedef BOOL __stdcall (*IPXGetOutstandingBuffer95Type) (unsigned char*); +typedef void __stdcall (*IPXShutDown95Type) (void); +typedef int __stdcall (*IPXSendPacket95Type) (unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); +typedef int __stdcall (*IPXBroadcastPacket95Type) (unsigned char *, int); +typedef BOOL __stdcall (*IPXStartListening95Type) (void); +typedef int __stdcall (*IPXOpenSocket95Type) (int); +typedef void __stdcall (*IPXCloseSocket95Type) (int); +typedef int __stdcall (*IPXGetConnectionNumber95Type) (void); +typedef int __stdcall (*IPXGetLocalTarget95) (unsigned char *, unsigned char*, unsigned short, unsigned char*); + + + +/* +** Function pointers +*/ +//extern "C"{ + extern IPXInitialiseType IPX_Initialise; + extern IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95; + extern IPXShutDown95Type IPX_Shut_Down95; + extern IPXSendPacket95Type IPX_Send_Packet95; + extern IPXBroadcastPacket95Type IPX_Broadcast_Packet95; + extern IPXStartListening95Type IPX_Start_Listening95; + extern IPXOpenSocket95Type IPX_Open_Socket95; + extern IPXCloseSocket95Type IPX_Close_Socket95; + extern IPXGetConnectionNumber95Type IPX_Get_Connection_Number95; + extern IPXGetLocalTarget95 IPX_Get_Local_Target95; +//} + +/* +** Functions +*/ +bool Load_IPX_Dll (void); +void Unload_IPX_Dll (void); +#endif + +#if (1) +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void); + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); + extern void __stdcall IPX_Shut_Down95(void); + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); + extern BOOL __stdcall IPX_Start_Listening95(void); + extern int __stdcall IPX_Open_Socket95(int socket); + extern void __stdcall IPX_Close_Socket95(int socket); + extern int __stdcall IPX_Get_Connection_Number95(void); + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*); +} +#endif //(1) + +extern bool WindowsNT; + diff --git a/REDALERT/IPXADDR.CPP b/REDALERT/IPXADDR.CPP new file mode 100644 index 000000000..4cde126be --- /dev/null +++ b/REDALERT/IPXADDR.CPP @@ -0,0 +1,506 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXADDR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXAddressClass::IPXAddressClass -- class constructor * + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * IPXAddressClass::Set_Address -- sets the IPX address values * + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * IPXAddressClass::operator== -- overloaded comparison operator * + * IPXAddressClass::operator!= -- overloaded comparison operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include "ipxaddr.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#endif //WINSOCK_IPX + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor * + * * + * This default constructor generates a broadcast address. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(void) +{ + NetworkNumber[0] = 0xff; + NetworkNumber[1] = 0xff; + NetworkNumber[2] = 0xff; + NetworkNumber[3] = 0xff; + NodeAddress[0] = 0xff; + NodeAddress[1] = 0xff; + NodeAddress[2] = 0xff; + NodeAddress[3] = 0xff; + NodeAddress[4] = 0xff; + NodeAddress[5] = 0xff; + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber, net, 4); + memcpy(NodeAddress, node, 6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * * + * This form of the constructor takes an IPX header as an argument. It * + * extracts the address from the Source address fields in the header. * + * * + * INPUT: * + * header Header from which to extract the address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(IPXHeaderType *header) +{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * * + * This routine extracts the source addresses from the given IPX header. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(IPXHeaderType *header) +{ +#ifdef WINSOCK_IPX + ProtocolEnum protocol = PROTOCOL_IPX; + if ( PacketTransport ) protocol = PacketTransport->Get_Protocol(); + + switch ( protocol ) { + + case PROTOCOL_IPX: + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + break; + + case PROTOCOL_UDP: + unsigned char *addr = (unsigned char*) header; + memset (NodeAddress, 0, 6); + memcpy (NodeAddress, addr, 4); + memset (NetworkNumber, 0, 4); + break; + } +#else //WINSOCK_IPX + + if (header) { + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + } else { + /* + ** Address is meaningless when using winsock + */ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 1, 6); + } + +#endif //WINSOCK_IPX + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(NetNumType net, NetNodeType node) +{ + memcpy(net,NetworkNumber,4); + memcpy(node,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(IPXHeaderType *header) +{ + memcpy(header->DestNetworkNumber,NetworkNumber,4); + memcpy(header->DestNetworkNode,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::Is_Broadcast(void) +{ + if ( NetworkNumber[0] == 0xff && + NetworkNumber[1] == 0xff && + NetworkNumber[2] == 0xff && + NetworkNumber[3] == 0xff && + NodeAddress[0] == 0xff && + NodeAddress[1] == 0xff && + NodeAddress[2] == 0xff && + NodeAddress[3] == 0xff && + NodeAddress[4] == 0xff && + NodeAddress[5] == 0xff) { + return(1); + } + else { + return(0); + } + +} /* end of Is_Broadcast */ + + +/*************************************************************************** + * IPXAddressClass::operator== -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = not equal, 1 = equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator == (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(1); + } + else { + return(0); + } + + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(1); + } + else { + return(0); + } + } + +} /* end of operator== */ + + +/*************************************************************************** + * IPXAddressClass::operator!= -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = equal, 1 = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator != (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(0); + } + else { + return(1); + } + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(0); + } + else { + return(1); + } + } + +} /* end of operator!= */ + + +/*************************************************************************** + * IPXAddressClass::operator > -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator > (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) > 0); + +} /* end of operator> */ + + +/*************************************************************************** + * IPXAddressClass::operator < -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator < (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) < 0); + +} /* end of operator< */ + + +/*************************************************************************** + * IPXAddressClass::operator >= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator >= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) >= 0); + +} /* end of operator>= */ + + +/*************************************************************************** + * IPXAddressClass::operator <= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator <= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) <= 0); + +} /* end of operator<= */ + +/************************** end of ipxaddr.cpp *****************************/ diff --git a/REDALERT/IPXADDR.H b/REDALERT/IPXADDR.H new file mode 100644 index 000000000..e2748ec27 --- /dev/null +++ b/REDALERT/IPXADDR.H @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXADDR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + * This class is useful for any IPX-related code. It's just a utility * + * to help manage those annoying IPX address fields. This class lets you * + * compare addresses, copy addresses to & from the IPX header, etc. * + * * + * The class has no virtual functions, so you can treat this class just * + * like a data structure; it can be loaded & saved, and even transmitted * + * across the net. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXADDR_H +#define IPXADDR_H + +#include "ipx.h" // for NetNumType & NetNodeType + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXAddressClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructors: + .....................................................................*/ + IPXAddressClass(void); + IPXAddressClass(NetNumType net, NetNodeType node); + IPXAddressClass(IPXHeaderType *header); + + /*..................................................................... + Set the address from explicit variables, or from the SOURCE values + in an IPX packet header. + .....................................................................*/ + void Set_Address(NetNumType net, NetNodeType node); + void Set_Address(IPXHeaderType *header); + /*..................................................................... + Get the address values explicitly, or copy them into the DESTINATION + values in an IPX packet header. + .....................................................................*/ + void Get_Address (NetNumType net, NetNodeType node); + void Get_Address(IPXHeaderType *header); + + /*..................................................................... + Tells if this address is a broadcast address + .....................................................................*/ + int Is_Broadcast(void); + + /*..................................................................... + Overloaded operators: + .....................................................................*/ + int operator == (IPXAddressClass & addr); + int operator != (IPXAddressClass & addr); + int operator > (IPXAddressClass &addr); + int operator < (IPXAddressClass &addr); + int operator >= (IPXAddressClass &addr); + int operator <= (IPXAddressClass &addr); + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /* + --------------------------- Private Interface ---------------------------- + */ + private: + NetNumType NetworkNumber; + NetNodeType NodeAddress; +}; + +#endif + +/**************************** end of ipxaddr.h *****************************/ diff --git a/REDALERT/IPXCONN.CPP b/REDALERT/IPXCONN.CPP new file mode 100644 index 000000000..ea138ac10 --- /dev/null +++ b/REDALERT/IPXCONN.CPP @@ -0,0 +1,876 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXCONN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXConnClass::IPXConnClass -- class constructor * + * IPXConnClass::~IPXConnClass -- class destructor * + * IPXConnClass::Init -- hardware-specific initialization routine * + * IPXConnClass::Configure -- One-time initialization routine * + * IPXConnClass::Start_Listening -- commands IPX to listen * + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * IPXConnClass::Open_Socket -- opens communications socket * + * IPXConnClass::Close_Socket -- closes the socket * + * IPXConnClass::Send_To -- sends the packet to the given address * + * IPXConnClass::Broadcast -- broadcasts the given packet * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include +#include "ipxconn.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" + +#else + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 +#endif //WINSOCK_IPX + + + +/* +********************************* Globals *********************************** +*/ +unsigned short IPXConnClass::Socket; +int IPXConnClass::ConnectionNum; +ECBType * IPXConnClass::ListenECB; +IPXHeaderType * IPXConnClass::ListenHeader; +char * IPXConnClass::ListenBuf; +ECBType * IPXConnClass::SendECB; +IPXHeaderType * IPXConnClass::SendHeader; +char * IPXConnClass::SendBuf; +long IPXConnClass::Handler; +int IPXConnClass::Configured = 0; +int IPXConnClass::SocketOpen = 0; +int IPXConnClass::Listening = 0; +int IPXConnClass::PacketLen; + + +/*************************************************************************** + * IPXConnClass::IPXConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * address address of destination (NULL = no address) * + * id connection's unique numerical ID * + * name connection's name * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXConnClass::IPXConnClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 2, // retry delta + -1, // max retries + 60, // timeout + extralen) // (currently, this is only used by the Global Channel) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + if (address) + Address = (*address); + ID = id; + strcpy (Name, name); + +#ifdef WINSOCK_IPX + Address.Get_Address(net,node); + memcpy(ImmediateAddress,node,6); + Immed_Set = 0; +#else + if ( !Winsock.Get_Connected() ) { + /*------------------------------------------------------------------------ + If our Address field is an actual address (ie NULL wasn't passed to the + constructor), pre-compute the ImmediateAddress value for the SendECB. + This allows pre-computing of the ImmediateAddress for all connections + created after Configure() is called. + ------------------------------------------------------------------------*/ + if (!Address.Is_Broadcast() && Configured==1) { + Address.Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + if (IPX_Get_Local_Target (net, node, Socket, ImmediateAddress)!=0) { + memcpy(ImmediateAddress,node,6); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(ImmediateAddress,node,6); + } + + Immed_Set = 1; + } + else { + memset (ImmediateAddress, 0, 6); + Immed_Set = 0; + } + } +#endif //WINSOCK_IPX +} /* end of IPXConnClass */ + + +/*************************************************************************** + * IPXConnClass::Init -- hardware-specific initialization routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Init (void) +{ + /*------------------------------------------------------------------------ + Invoke the parent's Init routine + ------------------------------------------------------------------------*/ + ConnectionClass::Init(); + +} /* end of Init */ + + +/*************************************************************************** + * IPXConnClass::Configure -- One-time initialization routine * + * * + * This routine sets up static members that are shared by all IPX * + * connections (ie those variables used by the Send/Listen/Broadcast * + * routines). * + * * + * INPUT: * + * socket socket ID for sending & receiving * + * conn_num local IPX Connection Number (0 = not logged in) * + * listen_ecb ptr to ECBType for listening * + * send_ecb ptr to ECBType for sending * + * listen_header ptr to IPXHeaderType for listening * + * send_header ptr to IPXHeaderType for sending * + * listen_buf ptr to buffer for listening * + * send_buf ptr to buffer for sending * + * handler_rm_ptr REAL-MODE pointer to event service routine * + * (high word = segment, low word = offset) * + * maxpacketlen max packet size to listen for * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - All pointers must be protected-mode pointers, but must point to * + * DOS real-mode memory (except the Handler segment/offset) * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Configure (unsigned short socket, int conn_num, + ECBType *listen_ecb, ECBType *send_ecb, IPXHeaderType *listen_header, + IPXHeaderType *send_header, char *listen_buf, char *send_buf, + long handler_rm_ptr, int maxpacketlen) +{ + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + Socket = socket; + ConnectionNum = conn_num; + ListenECB = listen_ecb; + SendECB = send_ecb; + ListenHeader = listen_header; + SendHeader = send_header; + ListenBuf = listen_buf; + SendBuf = send_buf; + Handler = handler_rm_ptr; + PacketLen = maxpacketlen; + + Configured = 1; + +} /* end of Configure */ + + +/*************************************************************************** + * IPXConnClass::Start_Listening -- commands IPX to listen * + * * + * This routine may be used to start listening in polled mode (if the * + * ECB's Event_Service_Routine is NULL), or in interrupt mode; it's * + * up to the caller to fill the ECB in. If in polled mode, Listening * + * must be restarted every time a packet comes in. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - The ListenECB must have been properly filled in by the IPX Manager.* + * - Configure must be called before calling this routine. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Start_Listening(void) +{ +#ifdef WIN32 + +#ifdef WINSOCK_IPX + /* + ** Open the socket. + */ + if (!Open_Socket(Socket)) + return(false); + + /* + ** start listening on the socket. + */ + if ( PacketTransport->Start_Listening () ) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } + +#else + if (Winsock.Get_Connected ()) return (true); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + if (IPX_Start_Listening95()) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } +#endif //WINSOCK_IPX + +#else //WIN32 + + void *hdr_ptr; + unsigned long hdr_val; + void *buf_ptr; + unsigned long buf_val; + int rc; + + /*------------------------------------------------------------------------ + Don't do a thing unless we've been configured, and we're not listening. + ------------------------------------------------------------------------*/ + if (Configured==0 || Listening==1) { + return(0); + } + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) { + return(0); + } + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(ListenECB, 0, sizeof(ECBType)); + memset(ListenHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)ListenHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + + buf_val = (unsigned long)ListenBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in the ECB + ------------------------------------------------------------------------*/ + ListenECB->SocketNumber = Socket; + ListenECB->PacketCount = 2; + ListenECB->Packet[0].Address = hdr_ptr; + ListenECB->Packet[0].Length = sizeof(IPXHeaderType); + ListenECB->Packet[1].Address = buf_ptr; + ListenECB->Packet[1].Length = (unsigned short)PacketLen; + + ((long &)ListenECB->Event_Service_Routine) = Handler; + + /*------------------------------------------------------------------------ + Command IPX to listen + ------------------------------------------------------------------------*/ + rc = IPX_Listen_For_Packet(ListenECB); + if (rc!=0) { + Close_Socket(Socket); + return(0); + } + else { + Listening = 1; + return(1); + } + +#endif //WIN32 + +} /* end of Start_Listening */ + + +/*************************************************************************** + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - This routine MUST NOT be called if IPX is not listening already! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Stop_Listening(void) +{ +#ifdef WINSOCK_IPX + if ( PacketTransport ) PacketTransport->Stop_Listening(); + Listening = 0; + + // All done. + return(1); +#else + /*------------------------------------------------------------------------ + Don't do anything unless we're already Listening. + ------------------------------------------------------------------------*/ + if (Listening==0) { + return(0); + } + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Listening = 0; + return (true); + } else { + IPX_Shut_Down95(); + Close_Socket(Socket); + } + +#else //WIN32 + + /*------------------------------------------------------------------------ + Shut IPX down. + ------------------------------------------------------------------------*/ + IPX_Cancel_Event(ListenECB); + Close_Socket(Socket); + +#endif //WIN32 + + Listening = 0; + + /*------------------------------------------------------------------------ + All done. + ------------------------------------------------------------------------*/ + return(1); +#endif //WINSOCK_IPX + +} /* end of Stop_Listening */ + + +/*************************************************************************** + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send(char *buf, int buflen, void *, int) +{ + /*------------------------------------------------------------------------ + Invoke our own Send_To routine, filling in our Address as the destination. + ------------------------------------------------------------------------*/ + if (Immed_Set) { + return(Send_To (buf, buflen, &Address, ImmediateAddress)); + } + else { + return(Send_To (buf, buflen, &Address, NULL)); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXConnClass::Open_Socket -- opens communications socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Open_Socket(unsigned short socket) +{ + int rc; +#ifdef WINSOCK_IPX + rc = PacketTransport->Open_Socket(socket); + + SocketOpen = rc; + return ( rc ); + +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 1; + return (true); + } + + SocketOpen = 0; + + /*------------------------------------------------------------------------ + Try to open a listen socket. The socket may have been left open by + a previously-crashed program, so ignore the state of the SocketOpen + flag for this call; use IPX to determine if the socket was already open. + ------------------------------------------------------------------------*/ + rc = IPX_Open_Socket(socket); + if (rc) { + + /*..................................................................... + If already open, close & reopen it + .....................................................................*/ + if (rc==IPXERR_SOCKET_ERROR) { +#ifdef WIN32 + WWDebugString ("Error -- Specified socket is already open"); +#endif //WIN32 + IPX_Close_Socket(socket); + rc = IPX_Open_Socket(socket); + } + + /*.................................................................. + Still can't open: return error + ..................................................................*/ + if (rc) { + return(0); + } + } + + SocketOpen = 1; + + return(1); +#endif //WINSOCK_IPX + +} /* end of Open_Socket */ + + +/*************************************************************************** + * IPXConnClass::Close_Socket -- closes the socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Calling this routine when the sockets aren't open may crash! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Close_Socket(unsigned short socket) +{ +#ifdef WINSOCK_IPX + socket = socket; + PacketTransport->Close_Socket(); + SocketOpen = 0; +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 0; + return; + } + + /*------------------------------------------------------------------------ + Never, ever, ever, under any circumstances whatsoever, close a socket + that isn't open. You'll regret it forever (or until at least until + you're through rebooting, which, if you're on a Pentium is the same + thing). + ------------------------------------------------------------------------*/ + if (SocketOpen==1) { + IPX_Close_Socket(socket); + } + + SocketOpen = 0; +#endif //WINSOCK_IPX +} /* end of Close_Socket */ + + +/*************************************************************************** + * IPXConnClass::Send_To -- sends the packet to the given address * + * * + * The "ImmediateAddress" field of the SendECB must be filled in with the * + * address of a bridge, or the node address of the destination if there * + * is no bridge. The NETX call to find this address will always crash * + * if NETX isn't loaded (ConnectionNum is 0), so this case is trapped & * + * prevented. * + * Also, if the address of this IPX connection is known when the * + * constructor is called, and Configure has been called, Get_Local_Target * + * is called to precompute the ImmediateAddress; this case is detected & * + * if the value is already computed, it's just memcpy'd over. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address Address to send to * + * immed ImmediateAddress value, NULL if none * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed) +{ +#ifdef WINSOCK_IPX + + immed = immed; + assert ( immed == NULL ); + PacketTransport->WriteTo ( (void*)buf, buflen, (void*) address ); + return (true); + +#else //WINSOCK_IPX + NetNumType net; + NetNodeType node; + int rc; + +#ifdef WIN32 + + unsigned char send_address[6]; + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return (true); + } + + if (immed) { + memcpy(send_address, immed, 6); +#ifdef FIXIT_DESTNET + // fixes DESTNET + address->Get_Address(net,node); +#else + // breaks DESTNET + memcpy(node, immed, 6); + memset (net, 0, sizeof(net) ); +#endif + } else { + address->Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, &send_address[0]); + if (rc!=0) { + return(false); + } + } else { + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(send_address,node,6); + } + } + + return (IPX_Send_Packet95(&send_address[0], (unsigned char*)buf, buflen, (unsigned char*)net, (unsigned char*)node)); + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + + /*------------------------------------------------------------------------ + Get the bridge address + ------------------------------------------------------------------------*/ + if (immed) { + memcpy(SendECB->ImmediateAddress, immed, 6); + } + else { + address->Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, + SendECB->ImmediateAddress); + if (rc!=0) { + return(0); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(SendECB->ImmediateAddress,node,6); + } + } + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + address->Get_Address(SendHeader); // fill in header addresses + SendHeader->DestNetworkSocket = Socket; // destination socket id + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) + Let_IPX_Breath(); + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX + +} /* end of Send_To */ + + +/*************************************************************************** + * IPXConnClass::Broadcast -- broadcasts the given packet * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Broadcast(char *buf, int buflen) +{ +#ifdef WINSOCK_IPX + PacketTransport->Broadcast (buf, buflen); + return (true); + +#else //WINSOCK_IPX + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return(true); + } else { + return (IPX_Broadcast_Packet95((unsigned char*)buf, buflen)); + } + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + SendECB->ImmediateAddress[0] = 0xff; + SendECB->ImmediateAddress[1] = 0xff; + SendECB->ImmediateAddress[2] = 0xff; + SendECB->ImmediateAddress[3] = 0xff; + SendECB->ImmediateAddress[4] = 0xff; + SendECB->ImmediateAddress[5] = 0xff; + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + SendHeader->DestNetworkNumber[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNumber[1] = 0xff; + SendHeader->DestNetworkNumber[2] = 0xff; + SendHeader->DestNetworkNumber[3] = 0xff; + SendHeader->DestNetworkNode[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNode[1] = 0xff; + SendHeader->DestNetworkNode[2] = 0xff; + SendHeader->DestNetworkNode[3] = 0xff; + SendHeader->DestNetworkNode[4] = 0xff; + SendHeader->DestNetworkNode[5] = 0xff; + SendHeader->DestNetworkSocket = Socket; // destination socket # + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) { + Let_IPX_Breath(); + } + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX +} /* end of Broadcast */ + +/************************** end of ipxconn.cpp *****************************/ + diff --git a/REDALERT/IPXCONN.H b/REDALERT/IPXCONN.H new file mode 100644 index 000000000..3c96055e9 --- /dev/null +++ b/REDALERT/IPXCONN.H @@ -0,0 +1,203 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for IPX communications. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * SequencedConnClass. It guarantees order of delivery of packets. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXCONN_H +#define IPXCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "ipxaddr.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONN_NAME_MAX = 40 // max # chars allowed for connection name + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXConnClass(int numsend, int numrecieve, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen = 0); + virtual ~IPXConnClass () {}; + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + The Configure function is for configuring all connections at once. + It's static because it doesn't apply to any specific connection, but + all of them. + .....................................................................*/ + static void Configure(unsigned short socket, + int conn_num, ECBType *listen_ecb, ECBType *send_ecb, + IPXHeaderType *listen_header, IPXHeaderType *send_header, + char *listen_buf, char *send_buf, long handler_rm_ptr, + int maxpacketlen); + + /*..................................................................... + These routines tell IPX to start listening for packets, and to stop + listening for packets. They're static because they affect all + connections at once (there's no way to turn listening on for only one + connection; it's all or nothing). + .....................................................................*/ + static int Start_Listening (void); + static int Stop_Listening (void); + + /*..................................................................... + The Destination IPX Address for this connection + .....................................................................*/ + IPXAddressClass Address; + + /*..................................................................... + The "Immediate" (Bridge) address for this connection, and a flag + telling if the address has been precomputed. + .....................................................................*/ + NetNodeType ImmediateAddress; + int Immed_Set; + + /*..................................................................... + Each IPX Connection can have a Name & Unique numerical ID + .....................................................................*/ + int ID; + char Name[CONN_NAME_MAX]; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + /*..................................................................... + These are the routines that access IPX. Open_Socket & Close_Socket are + static because they're called by Start_Listening & Stop_Listening. + Send_To & Broadcast are static since they're direct interfaces to IPX, + and there's only one IPX instance running. + .....................................................................*/ + static int Open_Socket(unsigned short socket); + static void Close_Socket(unsigned short socket); + static int Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed); + static int Broadcast(char *buf, int buflen); + + /*..................................................................... + The socket ID for this connection + .....................................................................*/ + static unsigned short Socket; + + /*..................................................................... + User's local Connection # (0 = not logged in) + .....................................................................*/ + static int ConnectionNum; + + /*..................................................................... + This is a static version of MaxPacketLen, which is the size of the + app's packets, plus our internal CommHeaderType. It's used in the + Start_Listening routine. + .....................................................................*/ + static int PacketLen; + + /*..................................................................... + Variables for Listening (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *ListenECB; + static IPXHeaderType *ListenHeader; + static char *ListenBuf; + + /*..................................................................... + Variables for Sending (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *SendECB; + static IPXHeaderType *SendHeader; + static char *SendBuf; + + /*..................................................................... + This is a REAL-MODE pointer to the event-service-routine for IPX. + If it's 0, IPX will operate in polled mode. Otherwise, the high word + must contain the segment, and the low word must contain the offset. + CS will be the high word value when the routine is called. (Requiring + the segment/offset to be computed by the caller gives the caller + control over CS.) + .....................................................................*/ + static long Handler; + + /*..................................................................... + This status flag tells us if Configure() has been called or not. + .....................................................................*/ + static int Configured; + + /*..................................................................... + This status flag tells us if the socket has been opened or not. + .....................................................................*/ + static int SocketOpen; + + /*..................................................................... + This status flag tells us if Start_Listening() has been called or not. + .....................................................................*/ + static int Listening; +}; + +#endif + +/*************************** end of ipxconn.h ******************************/ diff --git a/REDALERT/IPXGCONN.CPP b/REDALERT/IPXGCONN.CPP new file mode 100644 index 000000000..4186cb101 --- /dev/null +++ b/REDALERT/IPXGCONN.CPP @@ -0,0 +1,531 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXGCONN.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : July 6, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* + * IPXGlobalConnClass::Send -- sends a packet * + * IPXGlobalConnClass::Service_Receive_Queue -- services receive queue * + * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include +#include "ipxgconn.h" + + +/*************************************************************************** + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * * + * This routine chains to the parent constructor, but it adjusts the size * + * of the packet by the added bytes in the GlobalHeaderType structure. * + * This forces the parent classes to allocate the proper sized PacketBuf * + * for outgoing packets, and to set MaxPacketLen to the proper value. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * product_id unique ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, + int maxlen, unsigned short product_id) : + IPXConnClass (numsend, numreceive, + maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), + GLOBAL_MAGICNUM, // magic number for this connection + NULL, // IPX Address (none) + 0, // Connection ID + "", // Connection Name + sizeof (IPXAddressClass)) // extra storage for the sender's address +{ + int i; + + ProductID = product_id; + IsBridge = 0; + + for (i = 0; i < 4; i++) { + LastPacketID[i] = 0xffffffff; + } + LastRXIndex = 0; + +} /* end of IPXGlobalConnClass */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a GlobalHeaderType and * + * queues the resulting packet into the Send Queue. The packet's * + * MagicNumber, Code, PacketID, destination Address and ProductID are set * + * here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address address to send the packet to (NULL = Broadcast) * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req) +{ + IPXAddressClass dest_addr; + + /*------------------------------------------------------------------------ + Store the packet's Magic Number + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + If this is a ACK-required packet, sent to a specific system, mark it as + ACK-required; otherwise, mark as no-ACK-required. + ------------------------------------------------------------------------*/ + if (ack_req && address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; + } + else { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; + } + + /*------------------------------------------------------------------------ + Fill in the packet ID. This will have very limited meaning; it only + allows us to determine if an ACK packet we receive later goes with this + packet; it doesn't let us detect re-sends of other systems' packets. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); + + /*------------------------------------------------------------------------ + Set the product ID for this packet. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; + + /*------------------------------------------------------------------------ + Set this packet's destination address. If no address is specified, use + a Broadcast address (which IPXAddressClass's default constructor creates). + ------------------------------------------------------------------------*/ + if (address != NULL) { + dest_addr = (*address); + } + + /*------------------------------------------------------------------------ + Copy the application's data + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Queue it, along with the destination address + ------------------------------------------------------------------------*/ + return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType), + &dest_addr, sizeof (IPXAddressClass))); + +} /* end of Send_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes GlobalHeaderType) * + * buflen length of buffer to process * + * address the address of the sender (the IPX Manager class must * + * extract this from the IPX Header of the received packet.) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, + IPXAddressClass *address) +{ + GlobalHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + GlobalHeaderType *entry_data; // ptr to queue entry data + GlobalHeaderType ackpacket; // ACK packet to send + int i; + int resend; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (GlobalHeaderType *)buf; + if (packet->Header.MagicNumber!=MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Header.Code) { + //..................................................................... + // DATA_ACK: Check for a resend by comparing the source address & + // ID of this packet with our last 4 received packets. + // Send an ACK for the packet, regardless of whether it's a resend + // or not. + //..................................................................... + case PACKET_DATA_ACK: + { + //.................................................................. + // Check for a resend + //.................................................................. + resend = 0; + for (i = 0; i < 4; i++) { + if ((unsigned int)i >= Queue->Receive_Total()) { + break; + } + if ((*address)==LastAddress[i] && + packet->Header.PacketID==LastPacketID[i]) { + resend = 1; + break; + } + } + + bool send_ack = true; + + //.................................................................. + // If it's not a resend, queue it; then, record the sender's address + // & the packet ID for future resend detection. + //.................................................................. + if (!resend) { + if (Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass))) { + LastAddress[LastRXIndex] = (*address); + LastPacketID[LastRXIndex] = packet->Header.PacketID; + LastRXIndex++; + if (LastRXIndex >= 4) { + LastRXIndex = 0; + } + }else{ + //.................................................................. + // Don't send an ack if we didn't have room to store the packet. + //.................................................................. + send_ack = false; + } + } + + + //.................................................................. + // Send an ACK for this packet + //.................................................................. + if (send_ack) { + ackpacket.Header.MagicNumber = MagicNum; + ackpacket.Header.Code = PACKET_ACK; + ackpacket.Header.PacketID = packet->Header.PacketID; + ackpacket.ProductID = ProductID; + Send ((char *)&ackpacket, sizeof(GlobalHeaderType), + address, sizeof(IPXAddressClass)); + } + + + break; + } + /*..................................................................... + DATA_NOACK: Queue this message, along with the sender's address. + Don't bother checking for a Re-Send, since the other system will only + send this packet once. + .....................................................................*/ + case PACKET_DATA_NOACK: + Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass)); + break; + + /*..................................................................... + ACK: If this ACK is for any of my packets, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: + for (i = 0; i < Queue->Num_Send(); i++) { + /*............................................................... + Get queue entry ptr + ...............................................................*/ + send_entry = Queue->Get_Send(i); + + /*............................................................... + If ptr is valid, get ptr to its data + ...............................................................*/ + entry_data = (GlobalHeaderType *)(send_entry->Buffer); + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->Header.PacketID==entry_data->Header.PacketID && + entry_data->Header.Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + } + + return(1); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * address filled in with sender's address * + * product_id filled in with sender's ProductID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet; + int packetlen; // size of received packet + + /*------------------------------------------------------------------------ + Return if nothing to do + ------------------------------------------------------------------------*/ + if (Queue->Num_Receive() == 0) { + return(0); + } + + /*------------------------------------------------------------------------ + Get ptr to the next available entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Get_Receive(0); + + /*------------------------------------------------------------------------ + Read it if it's un-read + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + + /*..................................................................... + Mark as read + .....................................................................*/ + rec_entry->IsRead = 1; + + /*..................................................................... + Copy data packet + .....................................................................*/ + packet = (GlobalHeaderType *)(rec_entry->Buffer); + packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); + } + (*buflen) = packetlen; + (*product_id) = packet->ProductID; + (*address) = (*((IPXAddressClass *)(rec_entry->ExtraBuffer))); + + return(1); + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send -- sends a packet * + * * + * This routine gets invoked by NonSequencedConn, when it's processing * + * the Send & Receive Queues. The buffer provided will already have the * + * GlobalHeaderType header embedded in it. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * extrabuf extra buffer to send * + * extralen length of extra buffer * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send(char *buf, int buflen, void *extrabuf, int ) +{ + IPXAddressClass *addr; + int rc; + + /*------------------------------------------------------------------------ + Extract the packet's embedded IPX address + ------------------------------------------------------------------------*/ + addr = (IPXAddressClass *)extrabuf; + + /*------------------------------------------------------------------------ + If it's a broadcast address, broadcast it + ------------------------------------------------------------------------*/ + if (addr->Is_Broadcast()) { + return(Broadcast (buf, buflen)); + } + + /*------------------------------------------------------------------------ + Otherwise, send it + ------------------------------------------------------------------------*/ + else { + if (IsBridge && !memcmp (addr, BridgeNet, 4)) { + rc = Send_To (buf, buflen, addr, BridgeNode); + } + else { + rc = Send_To (buf, buflen, addr, NULL); + } + return (rc); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXGlobalConnClass::Service_Receive_Queue -- services the receive queue * + * * + * This routine is necessary because the regular ConnectionClass checks * + * for sequential packet ID's before removing them from the receive queue; * + * this class cannot do that. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Service_Receive_Queue (void) +{ + int i; + ReceiveQueueType *rec_entry; // ptr to receive entry header + + //------------------------------------------------------------------------ + // Remove all dead packets: If a packet's been read, throw it away. + //------------------------------------------------------------------------ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + + return (1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * Set_Bridge -- Sets up connection to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) +{ +#ifdef WINSOCK_IPX + + if (Configured) { + bridge = bridge; + IsBridge = 0; + } + +#else //WINSOCK_IPX + if (Configured) { + memcpy (BridgeNet, bridge, 4); + memset (BridgeNode, 0xff, 6); + + if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { + IsBridge = 1; + } + else { + IsBridge = 0; + } + } +#endif //WINSOCK_IPX + +} /* end of Set_Bridge */ + + +/************************** end of ipxgconn.cpp ****************************/ diff --git a/REDALERT/IPXGCONN.H b/REDALERT/IPXGCONN.H new file mode 100644 index 000000000..7215cd69d --- /dev/null +++ b/REDALERT/IPXGCONN.H @@ -0,0 +1,203 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXGCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 11, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class is a special type of IPX Connection. It can talk to more * + * than one system at a time. It can Broadcast packets to all systems, * + * or send a packet to one individual system. The packets it sends to * + * individual systems can be DATA_NOACK or DATA_ACK packets, but the * + * packets broadcast have to be DATA_NOACK packets. This class is for * + * only the crudest "Who-are-you" type of network communications. Once * + * the IPX Address of another system is identified, a "real" IPX * + * Connection should be created, & further communications done through it. * + * * + * This means that the packet ID field no longer can be used to detect * + * resends, since the receive queue may receive a lot more packets than * + * we send out. So, re-sends cannot be detected; the application must be * + * designed so that it can handle multiple copies of the same packet. * + * * + * The class uses a slightly different header from the normal Connections; * + * this header includes the ProductID of the sender, so multiple * + * applications can share the same socket, but by using different product * + * ID's, can distinguish between their own packets & others. * + * * + * Because of this additional header, and because Receive ACK/Retry logic * + * is different (we can't detect resends), the following routines are * + * overloaded: * + * Send_Packet: must embed the product ID into the packet header * + * Receive_Packet: must detect resends via the LastAddress & * + * LastPacketID arrays. This class doesn't ACK * + * packets until Service_Receive_Queue is called; * + * the parent classes ACK the packet when it's * + * received. * + * Get_Packet: extracts the product ID from the header; * + * doesn't care about reading packets in order * + * Send is capable of broadcasting the packet, or sending * + * to a specific address * + * * + * This class also has the ability to cross a Novell Network Bridge. * + * You provide the class with the bridge address, and subsequent * + * broadcasts are sent across the bridge as well as to the local network. * + * Address-specific sends contain the destination network's address, * + * so they cross a bridge automatically; it's just the broadcasts * + * that need to know the bridge address. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXGLOBALCONN_H +#define IPXGLOBALCONN_H + + +#include "ipxconn.h" + + +/* +********************************** Defines ********************************** +*/ +//--------------------------------------------------------------------------- +// This is the header for Global Connection messages. It includes the usual +// "standard" header that the other connections do; but it also includes an +// IPX address field, so the application can get the address of the sender +// of this message. This address field must be provided in by the IPX +// Connection Manager class, when it calls this class's Receive_Packet +// function. +//--------------------------------------------------------------------------- +typedef struct { + CommHeaderType Header; + unsigned short ProductID; +} GlobalHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXGlobalConnClass : public IPXConnClass +{ + //------------------------------------------------------------------------ + // Public Interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Some useful enums: + //..................................................................... + enum GlobalConnectionEnum { + //.................................................................. + // This is the magic number for all Global Connections. Having the + // same magic number across products lets us ID different products + // on the net. If you change the fundamental connection protocol, + // you must use a different magic number. + //.................................................................. + //GLOBAL_MAGICNUM = 0x1234, // used for C&C 1 + GLOBAL_MAGICNUM = 0x1235, // used for C&C 0 + //.................................................................. + // These are the values used for the ProductID field in the Global + // Message structure. It also should be the Magic Number used for + // the private connections within that product. + // This list should be continually updated & kept current. Never + // ever ever use an old product ID for your product! + //.................................................................. + COMMAND_AND_CONQUER = 0xaa01, + COMMAND_AND_CONQUER0 = 0xaa00 + }; + + //..................................................................... + // Constructor/destructor. + //..................................................................... + IPXGlobalConnClass (int numsend, int numrecieve, int maxlen, + unsigned short product_id); + virtual ~IPXGlobalConnClass () {}; + + //..................................................................... + // Send/Receive routines. + //..................................................................... + virtual int Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req); + virtual int Receive_Packet (void * buf, int buflen, + IPXAddressClass *address); + virtual int Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id); + + //..................................................................... + // This is for telling the connection it can cross a bridge. + //..................................................................... + void Set_Bridge (NetNumType bridge); + + //..................................................................... + // The Product ID for this product. + //..................................................................... + unsigned short ProductID; + + //..................................................................... + // This describes the address of a bridge we have to cross. This class + // supports crossing only one bridge. Storing the bridge's network + // number allows us to obtain its local target address only once, then + // re-use it. + //..................................................................... + NetNumType BridgeNet; + NetNodeType BridgeNode; + int IsBridge; + + //------------------------------------------------------------------------ + // Protected Interface + //------------------------------------------------------------------------ + protected: + + //..................................................................... + // This is the overloaded Send routine declared in ConnectionClass, and + // used in SequencedConnClass. This special version sends to the address + // stored in the extra buffer within the Queue. + //..................................................................... + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + //..................................................................... + // This routine is overloaded from SequencedConnClass, because the + // Global Connection needs to ACK its packets differently from the + // other connections. + //..................................................................... + virtual int Service_Receive_Queue (void); + + private: + //..................................................................... + // Since we can't detect resends by using the PacketID (since we're + // receiving packets from a variety of sources, all using different + // ID's), we'll have to remember the last 'n' packet addresses & id's + // for comparison purposes. + // Note that, if network traffic is heavy, it's still possible for an + // app to receive the same packet twice! + //..................................................................... + IPXAddressClass LastAddress[4]; // array of last 4 addresses + unsigned long LastPacketID[4]; // array of last 4 packet ID's + int LastRXIndex; // index of next avail pos +}; + +#endif + +/*************************** end of ipxgconn.h *****************************/ diff --git a/REDALERT/IPXMGR.CPP b/REDALERT/IPXMGR.CPP new file mode 100644 index 000000000..34b895615 --- /dev/null +++ b/REDALERT/IPXMGR.CPP @@ -0,0 +1,2066 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXMGR.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 4, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXManagerClass::IPXManagerClass -- class constructor * + * IPXManagerClass::~IPXManagerClass -- class destructor * + * IPXManagerClass::Init -- initialization routine * + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * IPXManagerClass::Create_Connection -- creates a new connection * + * IPXManagerClass::Delete_Connection -- deletes a connection * + * IPXManagerClass::Num_Connections -- gets the # of connections * + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * IPXManagerClass::Connection_Name -- gets name for given connection * + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * IPXManagerClass::Connection_Index -- gets given connection's index * + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * IPXManagerClass::Get_Private_Message -- Polls Private Message queue * + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * IPXManagerClass::Global_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Global_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Private_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Private_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include PG +//#include PG +#include "ipxmgr.h" +#include "wwlib32.h" // to enable mono output + +#ifdef WINSOCK_IPX + +#include "WSProto.h" +#include "WSPIPX.h" + +#else //WINSOCK_IPX + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +// Turn off "expression is not meaningful". +//#pragma warning 628 9 + +//#include "WolDebug.h" + +/*************************************************************************** + * IPXManagerClass::IPXManagerClass -- class constructor * + * * + * INPUT: * + * glb_maxlen Global Channel maximum packet length * + * pvt_maxlen Private Channel maximum packet length * + * socket socket ID to use * + * product_id a unique numerical ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::IPXManagerClass (int glb_maxlen, int pvt_maxlen, + int glb_num_packets, int pvt_num_packets, unsigned short socket, + unsigned short product_id) +{ + int i; +#ifdef WINSOCK_IPX + /* + ** Find out if Packet protocol services are available through Winsock. + */ + if ( PacketTransport ) { + delete PacketTransport; + PacketTransport = NULL; + } + PacketTransport = new WinsockInterfaceClass; + assert ( PacketTransport != NULL ); + + if ( PacketTransport->Init() ){ + IPXStatus = 1; + }else{ + IPXStatus = 0; + } + delete PacketTransport; + PacketTransport = NULL; + +#else //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Initialize data members + //------------------------------------------------------------------------ + //........................................................................ + // IPXStatus = 1 if IPX is installed, 0 if not + //........................................................................ + if (IPX_SPX_Installed()==0) { + IPXStatus = 0; + } + else { + IPXStatus = 1; + } +#endif //WINSOCK_IPX + + //........................................................................ + // Set listening state flag to off + //........................................................................ + Listening = 0; + + //........................................................................ + // No memory has been alloc'd yet + //........................................................................ + RealMemAllocd = 0; + + //........................................................................ + // Set max packet sizes, for allocating real-mode memory + //........................................................................ + Glb_MaxPacketLen = glb_maxlen; + Glb_NumPackets = glb_num_packets; + Pvt_MaxPacketLen = pvt_maxlen; + Pvt_NumPackets = pvt_num_packets; + + //........................................................................ + // Save the app's product ID + //........................................................................ + ProductID = product_id; + + //........................................................................ + // Save our socket ID number + //........................................................................ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + + //------------------------------------------------------------------------ + // Get the user's IPX local connection number + //------------------------------------------------------------------------ + ConnectionNum = 0; +#ifndef WINSOCK_IPX + if (IPXStatus) { + ConnectionNum = IPX_Get_Connection_Number(); + } +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Init connection states + //------------------------------------------------------------------------ + NumConnections = 0; + CurConnection = 0; + for (i = 0; i < CONNECT_MAX; i++) { + Connection[i] = 0; + } + GlobalChannel = 0; + + SendOverflows = 0; + ReceiveOverflows = 0; + BadConnection = CONNECTION_NONE; + + //------------------------------------------------------------------------ + // Init timing parameters + //------------------------------------------------------------------------ + RetryDelta = 2; // 2 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 60; // report bad connection after 1 second + +} /* end of IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::~IPXManagerClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::~IPXManagerClass() +{ + int i; + + //------------------------------------------------------------------------ + // Stop all IPX events + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free all protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + //------------------------------------------------------------------------ + // Free all real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 +#ifndef WINSOCK_IPX +//PG Unload_IPX_Dll(); +#endif +#endif +#endif +} /* end of ~IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::Init -- initialization routine * + * * + * This routine allocates memory, & initializes variables * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Init() +{ + int i; + + if (Session.Type != GAME_INTERNET) { + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Stop Listening + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free Real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + + } else { + /* + ** Pretend IPX is available for Internet games whether it is or not + */ + IPXStatus = 1; + } + + //------------------------------------------------------------------------ + // Free protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + if (Session.Type != GAME_INTERNET) { + //------------------------------------------------------------------------ + // Allocate real-mode memory + //------------------------------------------------------------------------ + if (!Alloc_RealMode_Mem()) return(0); + RealMemAllocd = 1; + } + + //------------------------------------------------------------------------ + // Allocate the Global Channel + //------------------------------------------------------------------------ + GlobalChannel = new IPXGlobalConnClass (Glb_NumPackets, Glb_NumPackets, + Glb_MaxPacketLen, ProductID); + if (!GlobalChannel) { + return(0); + } + GlobalChannel->Init(); + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + + //------------------------------------------------------------------------ + // Configure the IPX Connections + //------------------------------------------------------------------------ + IPXConnClass::Configure(Socket, ConnectionNum, ListenECB, SendECB, + FirstHeaderBuf, SendHeader, FirstDataBuf, SendBuf, Handler, PacketLen); + + //------------------------------------------------------------------------ + // Start Listening + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (!IPXConnClass::Start_Listening()) return(0); + } + Listening = 1; + + return(1); + +} /* end of Init */ + + +/*************************************************************************** + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = IPX is installed; 0 = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Is_IPX(void) +{ + return(IPXStatus); + +} /* end of Is_IPX */ + + +/*************************************************************************** + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters for all existing connections, and * + * all connections created from now on. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + int i; + + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + if (GlobalChannel) { + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + } + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Set_Retry_Delta (RetryDelta); + Connection[i]->Set_Max_Retries (MaxRetries); + Connection[i]->Set_TimeOut (Timeout); + } + +} /* end of Set_Timing */ + + +/*************************************************************************** + * IPXManagerClass::Create_Connection -- creates a new connection * + * * + * INPUT: * + * id application-specific numerical ID for this connection * + * node ptr to IPXNodeIDType (name & address) * + * address IPX address for this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * Never create a connection with an 'id' of -1. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Create_Connection(int id, char *name, + IPXAddressClass *address) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no more room + //------------------------------------------------------------------------ + if (NumConnections==CONNECT_MAX) { + return(0); + } + + //------------------------------------------------------------------------ + // Create new connection + //------------------------------------------------------------------------ + Connection[NumConnections] = new IPXConnClass(Pvt_NumPackets, + Pvt_NumPackets, Pvt_MaxPacketLen, ProductID, address, id, name); + if (!Connection[NumConnections]) { + return(0); + } + + Connection[NumConnections]->Init (); + Connection[NumConnections]->Set_Retry_Delta (RetryDelta); + Connection[NumConnections]->Set_Max_Retries (MaxRetries); + Connection[NumConnections]->Set_TimeOut (Timeout); + + NumConnections++; + + return(1); + +} /* end of Create_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Delete_Connection -- deletes a connection * + * * + * INPUT: * + * id ID of connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Delete_Connection(int id) +{ + int i,j; + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no connections to delete + //------------------------------------------------------------------------ + if (NumConnections==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop through all connections + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + //..................................................................... + // If a match, delete it + //..................................................................... + if (Connection[i]->ID==id) { + delete Connection[i]; + + //.................................................................. + // Move array elements back one index + //.................................................................. + for (j = i; j < NumConnections - 1; j++) { + Connection[j] = Connection[j+1]; + } + + //.................................................................. + // Adjust counters + //.................................................................. + NumConnections--; + if (CurConnection >= NumConnections) + CurConnection = 0; + return(1); + } + } + + //------------------------------------------------------------------------ + // No match; error + //------------------------------------------------------------------------ + return(0); + +} /* end of Delete_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Num_Connections -- gets the # of connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of connections * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Num_Connections(void) +{ + return(NumConnections); + +} /* end of Num_Connections */ + + +/*************************************************************************** + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * * + * INPUT: * + * index index of connection to retrieve * + * * + * OUTPUT: * + * ID for that connection, CONNECTION_NONE if invalid index * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(Connection[index]->ID); + } + else { + return(CONNECTION_NONE); + } +} /* end of Connection_ID */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Name -- retrieves name for given connection * + * * + * INPUT: * + * id ID of connection to get name of * + * * + * OUTPUT: * + * ptr to connection's name, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +char *IPXManagerClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(Connection[i]->Name); + } + } + + return(NULL); + +} /* end of Connection_Name */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * * + * INPUT: * + * id ID of connection to get address for * + * * + * OUTPUT: * + * pointer to IXPAddressClass, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +IPXAddressClass * IPXManagerClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(&Connection[i]->Address); + } + } + + return(NULL); + +} /* end of Connection_Address */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Index -- gets given connection's index * + * * + * INPUT: * + * ID to retrieve index for * + * * + * OUTPUT: * + * index for this connection, CONNECTION_NONE if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(i); + } + } + + return(CONNECTION_NONE); + +} /* end of Connection_Index */ + + +/*************************************************************************** + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * * + * INPUT: * + * index connection index * + * id new connection ID * + * name new connection name * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Connection_Parms(int index, int id, char *name) +{ + if (index >= NumConnections) + return; + + Connection[index]->ID = id; + strcpy(Connection[index]->Name,name); + +} /* end of Set_Connection_Parms */ + + +/*************************************************************************** + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buf * + * ack_req 1 = ACK required; 0 = no ACK required * + * address address to send to; NULL = broadcast * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Global_Message(void *buf, int buflen, + int ack_req, IPXAddressClass *address) +{ + int rc; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + rc = GlobalChannel->Send_Packet (buf, buflen, address, ack_req); + if (!rc) { + SendOverflows++; + } + + return(rc); + +} /* end of Send_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * * + * INPUT: * + * buf buffer to store received packet * + * buflen length of data placed in 'buf' * + * address IPX address of sender * + * product_id product ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Global_Message(void *buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + return(GlobalChannel->Get_Packet (buf, buflen, address, product_id)); + +} /* end of Get_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of 'buf' * + * conn_id connection ID to send to (CONNECTION_NONE = all) * + * ack_req 1 = ACK required; 0 = no ACK required * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Private_Message(void *buf, int buflen, int ack_req, + int conn_id) +{ + int i; // loop counter + int connect_idx; // index of channel to send to, if specified + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Send the message to all connections + //------------------------------------------------------------------------ + if (conn_id==CONNECTION_NONE) { + //..................................................................... + // Check for room in all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() == + Connection[i]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + } + + //..................................................................... + // Send packet to all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + Connection[i]->Send_Packet (buf, buflen, ack_req); + } + return(1); + } + + //------------------------------------------------------------------------ + // Send the message to the specified connection + //------------------------------------------------------------------------ + else { + connect_idx = Connection_Index (conn_id); + if (connect_idx == CONNECTION_NONE) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Check for room in the connection + //..................................................................... + if (Connection[connect_idx]->Queue->Num_Send() == + Connection[connect_idx]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Send the packet to that connection + //..................................................................... + Connection[connect_idx]->Send_Packet (buf, buflen, ack_req); + return(1); + } + +} /* end of Send_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Private_Message -- Polls the Private Message queue * + * * + * INPUT: * + * buf buffer to store incoming packet * + * buflen length of data placed in 'buf' * + * conn_id filled in with connection ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Private_Message(void *buf, int *buflen, int *conn_id) +{ + int i; + int rc; + int c_id; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Safety check: ensure CurConnection is in range. + //------------------------------------------------------------------------ + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //------------------------------------------------------------------------ + // Scan all connections for a received packet, starting with 'CurConnection' + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + + //..................................................................... + // Check this connection for a packet + //..................................................................... + rc = Connection[CurConnection]->Get_Packet (buf, buflen); + c_id = Connection[CurConnection]->ID; + + //..................................................................... + // Increment CurConnection to the next connection index + //..................................................................... + CurConnection++; + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //..................................................................... + // If we got a packet, return the connection ID + //..................................................................... + if (rc) { + (*conn_id) = c_id; + return(1); + } + } + + return(0); + +} /* end of Get_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Service(void) +{ + int rc = 1; + int i; + CommHeaderType *packet; + int packetlen; + IPXAddressClass address; + +#ifdef WINSOCK_IPX + + + unsigned char temp_receive_buffer[1024]; + int temp_receive_buffer_len; + int temp_address_len; + + + char temp_address [128]; + + if ( PacketTransport ) { + + do { + temp_receive_buffer_len = sizeof (temp_receive_buffer); + temp_address_len = sizeof (temp_address); + packetlen = PacketTransport->Read ( temp_receive_buffer, temp_receive_buffer_len, temp_address, temp_address_len ); + if ( packetlen ) { + CurDataBuf = (char*)temp_receive_buffer; + address = *((IPXAddressClass*) temp_address); + + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + /* + ** Put the packet in the Global Queue + */ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + + /* + ** Find the Private Queue that this packet is for + */ + bool found_address = false; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + found_address = true; + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + if( Session.Type == GAME_INTERNET ) + { + /* + ** This packet came from an unknown source. If it looks like one of our players + ** packets then it might be from a player whos IP has changed. + */ + if (!found_address) { + if (packet->Code == ConnectionClass::PACKET_DATA_NOACK){ + /* + ** Magic number and packet code are valid. It's probably a C&C packet. + */ + EventClass *event = (EventClass*) (((char*) packet) + sizeof (CommHeaderType)); + + /* + ** If this is a framesync packet then grab the address and match it to an existing player. + */ + if (event->Type == EventClass::FRAMESYNC) { + int id = event->ID; + + assert (id != PlayerPtr->ID); + for ( int i=1 ; iPlayer.ID == id) { + + int iConnectionIndex = Connection_Index(id); + if( iConnectionIndex != CONNECTION_NONE ) // (else Create_Connections() has not yet been called) + { + /* + ** Found a likely candidate. Update his address. It should be OK to drop this + ** packet since it's a framesync packet and will will pick up the next one. + */ + Session.Players[i]->Address = address; + Connection[iConnectionIndex]->Address = address; + } + break; + } + } + } + } + } + } + } + } + } + + } while (packetlen); + + } + + + + +#else //WINSOCK_IPX + +#ifdef WIN32 + + unsigned char temp_receive_buffer[1024]; + int recv_length; + + if (Winsock.Get_Connected() || Special.IsFromWChat) { + + if (!Winsock.Get_Connected()) return (0); + + /* + ** This is an internet connection so get the packets from winsock + */ + while ((recv_length = Winsock.Read(temp_receive_buffer, 1024))!=0) { + + CurHeaderBuf = NULL; + CurDataBuf = (char*)&temp_receive_buffer[0]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length; + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + } else { +#if(0)//PG + while (IPX_Get_Outstanding_Buffer95(&temp_receive_buffer[0])) { + + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; + CurDataBuf = (char*)&temp_receive_buffer[sizeof(IPXHeaderType)]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + } +#endif + } + + +#else //WIN32 + + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop until there are no more packets to process. + //------------------------------------------------------------------------ + while (1) { + + //..................................................................... + // Check the BufferFlags for the "current" buffer; if it's empty, + // break; out of the loop. + //..................................................................... + if (BufferFlags[CurIndex]==0) { + break; + } + + //..................................................................... + // Compute the length of the packet (byte-swap the length in the IPX hdr) + //..................................................................... + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + //..................................................................... + // Extract the sender's address from the IPX header + //..................................................................... + address.Set_Address (CurHeaderBuf); + + //..................................................................... + // Examine the Magic Number of the received packet to determine if this + // packet goes into the Global Queue, or into one of the Private Queues + //..................................................................... + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + //.................................................................. + // Put the packet in the Global Queue + //.................................................................. + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) { + ReceiveOverflows++; + } + } + + //..................................................................... + // Find the Private Queue that this packet is for + //..................................................................... + else if (packet->MagicNumber == ProductID) { + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) { + ReceiveOverflows++; + } + break; + } + } + } + + //..................................................................... + // Set the current BufferFlags to 0 (since we've cleaned out this buffer) + //..................................................................... + BufferFlags[CurIndex] = 0; + + //..................................................................... + // Go to the next packet buffer + //..................................................................... + CurIndex++; + CurHeaderBuf = (IPXHeaderType *)(((char *)CurHeaderBuf) + FullPacketLen); + CurDataBuf = ((char *)CurDataBuf) + FullPacketLen; + if (CurIndex >= NumBufs) { + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + CurIndex = 0; + } + } + +#endif //WIN32 +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Service all connections. If a connection reports that it's gone "bad", + // report an error to the caller. If it's the Global Channel, un-queue the + // send entry that's holding things up. This will keep the Global Channel + // from being clogged by one un-ACK'd outgoing packet. + //------------------------------------------------------------------------ + if (GlobalChannel) { + if (!GlobalChannel->Service()) { + GlobalChannel->Queue->UnQueue_Send (NULL, NULL, 0); + rc = 0; + } + } + for (i = 0; i < NumConnections; i++) { + if (!Connection[i]->Service()) { + rc = 0; + BadConnection = Connection[i]->ID; + } + } + + if (rc) { + BadConnection = CONNECTION_NONE; + } + + return(rc); + +} /* end of Service */ + +/*************************************************************************** + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ID of bad connection; CONNECTION_NONE if none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Bad_Connection(void) +{ + return(BadConnection); + +} /* end of Get_Bad_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Send Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Send(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Send()); + +} /* end of Global_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Receive(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Receive()); + +} /* end of Global_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * # entries in the Private Send Queue * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Send(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Send()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() > maxnum) { + maxnum = Connection[i]->Queue->Num_Send(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * id ID of connection to query * + * * + * OUTPUT: * + * # entries in the Private Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Receive(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) + return(0); + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Receive()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Receive() > maxnum) { + maxnum = Connection[i]->Queue->Num_Receive(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * * + * INPUT: * + * socket new socket ID to use * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not call this function after communications have started; you * + * must call it before calling Init(). * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Socket(unsigned short socket) +{ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + +} /* end of Set_Socket */ + + +/*************************************************************************** + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * largest avg response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Response_Time(void) +{ + unsigned long resp; + unsigned long maxresp = 0; + int i; + + for (i = 0; i < NumConnections; i++) { + resp = Connection[i]->Queue->Avg_Response_Time(); + if (resp > maxresp) { + maxresp = resp; + } + } + + return(maxresp); + +} /* end of Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * avg global channel response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Global_Response_Time(void) +{ + if (GlobalChannel) { + return (GlobalChannel->Queue->Avg_Response_Time()); + } + else { + return (0); + } + +} /* end of Global_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Reset_Response_Time(void) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Queue->Reset_Response_Time(); + } + + if (GlobalChannel) + GlobalChannel->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * buf ptr * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void * IPXManagerClass::Oldest_Send(void) +{ + int i,j; + unsigned long time; + unsigned long mintime = 0xffffffff; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < NumConnections; i++) { + + send_entry = NULL; + + for (j = 0; j < Connection[i]->Queue->Num_Send(); j++) { + send_entry = Connection[i]->Queue->Get_Send(j); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && + send_entry->IsACK == 0) { + break; + } + else { + send_entry = NULL; + } + } + } + + if (send_entry!=NULL) { + + time = send_entry->FirstTime; + + if (time < mintime) { + mintime = time; + buf = send_entry->Buffer; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Bridge(NetNumType bridge) +{ + if (GlobalChannel) { + GlobalChannel->Set_Bridge(bridge); + } + +} /* end of Set_Bridge */ + + +/*************************************************************************** + * IPXManagerClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical value of 1st name in the array * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Configure_Debug(int index, int type_offset, + int type_size, char **names, int namestart, int namecount) +{ + if (index == -1) { + GlobalChannel->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + else if (Connection[index]) { + Connection[index]->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * * + * INPUT: * + * index index of connection to display (-1 = Global Channel) * + * refresh 1 = complete screen refresh * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Mono_Debug_Print(int index, int refresh) +{ +#ifdef WWLIB32_H + char txt[80]; + int i; + + if (index == -1) + GlobalChannel->Queue->Mono_Debug_Print (refresh); + + else if (Connection[index]) + Connection[index]->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (20,1); + Mono_Printf ("IPX Queue:"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (43,1); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,2); + Mono_Printf ("Receive Overflows:"); + + } + + Mono_Set_Cursor (32,1); + Mono_Printf ("%d",index); + + Mono_Set_Cursor (32,2); + if (index == -1) { + Mono_Printf ("%d ", GlobalChannel->Queue->Avg_Response_Time()); + } + else { + Mono_Printf ("%d ", Connection[index]->Queue->Avg_Response_Time()); + } + + Mono_Set_Cursor (59,1); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", ReceiveOverflows); + + for (i = 0; i < NumBufs; i++) { + if (BufferFlags[i]) { + txt[i] = 'X'; + } + else { + txt[i] = '_'; + } + } + txt[i] = 0; + Mono_Set_Cursor ((80-NumBufs)/2,3); + Mono_Printf ("%s",txt); + +#else + index = index; + refresh = refresh; +#endif + +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Alloc_RealMode_Mem(void) +{ + +#ifdef WIN32 + return (1); +#else + + union REGS regs; + struct SREGS sregs; + int size; // required size of allocation + unsigned char *realmode; // start addresses of real-mode data + int realmodelen; // length of real-mode data + unsigned long func_val; + char *p; // for parsing buffer + int i; + + //------------------------------------------------------------------------ + // Compute # of buffers we need to allocate, & the max size of each one + //------------------------------------------------------------------------ + NumBufs = Glb_NumPackets + (Pvt_NumPackets * CONNECT_MAX); + + PacketLen = Glb_MaxPacketLen + sizeof (GlobalHeaderType); + if (Pvt_MaxPacketLen + sizeof (CommHeaderType) > PacketLen) + PacketLen = Pvt_MaxPacketLen + sizeof (CommHeaderType); + + FullPacketLen = PacketLen + sizeof(IPXHeaderType); + + //------------------------------------------------------------------------ + // Compute the size of everything we'll ever need, allocate it in one big + // chunk. The memory is used as follows: + // - Real-mode assembly IPX callback routine, plus its data, + // (which includes the ListenECB) + // - Array of IPX Packet buffers (IPXHeader plus data buffer) + // - SendECB: ECB for sending + // - SendHeader: IPX Header for sending + // - SendBuf: Packet buffer for sending + // - BufferFlags: 1 byte for each incoming packet buffer; 1=in use, 0=free + //------------------------------------------------------------------------ + realmode = (unsigned char *)Get_RM_IPX_Address(); + realmodelen = Get_RM_IPX_Size(); + size = realmodelen + // assembly routine & its data + (FullPacketLen * NumBufs) + // array of packet buffers + sizeof(ECBType) + // SendECB + FullPacketLen + // SendHeader & SendBuf + NumBufs; // BufferFlags + if (size > 65535) { + return(0); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory for the ECB, IPXHeader & packet buffers: + // AX = 0x100 + // BX = # paragraphs to allocate + // - if Success, AX = real-mode segment, DX = selector + // - if Failure, carry flag is set + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = ((size + 15) >> 4); // # paragraphs to allocate + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Save the values of the returned segment & selector + //------------------------------------------------------------------------ + Selector = regs.w.dx; + Segment = regs.w.ax; + RealMemSize = size; + RealModeData = (RealModeDataType *)(((long)Segment) << 4); + + //------------------------------------------------------------------------ + // Lock the memory (since we're servicing interrupts with it) + // AX = 0x600 + // BX:CX = starting linear address of memory to lock + // SI:DI = size of region to lock (in bytes) + // - If Failure, carry flag is set. + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + return(0); + } + + //------------------------------------------------------------------------ + // Copy the Real-mode code into our memory buffer + //------------------------------------------------------------------------ + p = (char *)(((long)Segment) << 4); + memcpy (p,realmode,realmodelen); + p += realmodelen; + + //------------------------------------------------------------------------ + // Compute & save the entry point for the real-mode packet handler + //------------------------------------------------------------------------ + func_val = (unsigned long)RealModeData; + Handler = (((func_val & 0xffff0) << 12) | + ((func_val & 0x000f) + RealModeData->FuncOffset)); + + //------------------------------------------------------------------------ + // Fill in buffer pointers + //------------------------------------------------------------------------ + ListenECB = &(RealModeData->ListenECB); + + FirstHeaderBuf = (IPXHeaderType *)p; + FirstDataBuf = (((char *)FirstHeaderBuf) + sizeof(IPXHeaderType)); + CurIndex = 0; + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + p += FullPacketLen * NumBufs; + + SendECB = (ECBType *)p; + p += sizeof (ECBType); + + SendHeader = (IPXHeaderType *)p; + p += sizeof (IPXHeaderType); + + SendBuf = (char *)p; + p += PacketLen; + + BufferFlags = (char *)p; + + //------------------------------------------------------------------------ + // Fill in the real-mode routine's data (The ECB will be filled in when we + // command IPX to Listen). + //------------------------------------------------------------------------ + RealModeData->NumBufs = (short)NumBufs; + RealModeData->BufferFlags = (char *) + ((((long)BufferFlags & 0xffff0) << 12) | + ((long)BufferFlags & 0x0000f)); + RealModeData->PacketSize = (short)FullPacketLen; + RealModeData->FirstPacketBuf = (IPXHeaderType *) + ((((long)FirstHeaderBuf & 0xffff0) << 12) | + ((long)FirstHeaderBuf & 0x0000f)); + RealModeData->CurIndex = 0; + RealModeData->CurPacketBuf = RealModeData->FirstPacketBuf; + RealModeData->Semaphore = 0; + RealModeData->ReEntrantCount = 0; + + //------------------------------------------------------------------------ + // Init state of all buffers to empty + //------------------------------------------------------------------------ + for (i = 0; i < NumBufs; i++) { + BufferFlags[i] = 0; + } + + //------------------------------------------------------------------------ + // Check the start & end markers in the real-mode memory area + //------------------------------------------------------------------------ + if (RealModeData->Marker1 != 0x1111 || + RealModeData->Marker2 != 0x2222) { + Free_RealMode_Mem(); + return(0); + } + else { + return(1); + } +#endif //WIN32 +} /* end of Alloc_Realmode_Mem */ + + +/*************************************************************************** + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Free_RealMode_Mem(void) +{ + +#ifdef WIN32 + + return (1); + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + int rc = 1; + + //------------------------------------------------------------------------ + // Unlock the memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + rc = 0; + } + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + + return(rc); +#endif //WIN32 + +} /* end of Free_Realmode_Mem */ + +/*************************** end of ipxmgr.cpp *****************************/ diff --git a/REDALERT/IPXMGR.H b/REDALERT/IPXMGR.H new file mode 100644 index 000000000..6993e90a9 --- /dev/null +++ b/REDALERT/IPXMGR.H @@ -0,0 +1,394 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for IPX network communications. It * + * creates, manages, & orchestrates multiple IPX connections, as well as * + * the "global" connection ("Global Channel"), which can talk to any * + * system on the net. * + * * + * Use the Global Channel to query systems for their names, ID's, & * + * IPX addresses. Then, create a Private Connection with each system * + * that joins your game, and use the Private Channel to send game packets * + * (the private channel will perform somewhat faster, & gives you better * + * control than the Global Channel; it can detect retries, and the Global * + * Channel can't). * + * * + * HOW THIS CLASS WORKS: * + * This class has to set up an IPX Event Service Routine in low (DOS) * + * memory. So, it uses DPMI to allocate & lock a chunk of DOS memory; * + * this memory is used for all incoming packet buffers, the outgoing * + * packet buffer, and the actual code for the event handler. The real- * + * mode handler code & this class share a portion of memory that's mapped * + * into a "RealModeDataType" structure. As packets come in, the handler * + * points IPX to the next available packet buffer & restarts listening; * + * it sets a flag to tell this class that a packet is present at that * + * buffer slot. This class must read all the packets & determine which * + * connection they go with (the Global Channel, or one of the Private * + * Channels). This parsing is done in the Service routine for this class. * + * * + * Constructor: Just inits some variables, checks to see if IPX is there * + * Destructor: Complete shutdown; stops IPX listening, frees all memory * + * Init: Should only be called once (but can be called more); * + * allocates all memory, creates the Global Channel * + * connection, starts IPX listening. By not placing this * + * step in the constructor, the app can control when * + * listening actually starts; also, you don't get a bunch * + * of allocations just by declaring an IPXManagerClass * + * instance. You have to call Init() for the allocations * + * to occur. * + * Connection utilities: Create & manage Private Connections. Each * + * connection has its own IPX address, numerical ID, and * + * character name (presumably the name of the other * + * player). * + * Send/Get_Global_Message: adds a packet to the Global Connection queue, * + * or reads from the queue. The caller should check the * + * ProductID value from returned packets to be sure it's * + * talking to the right product. * + * Send/Get_Private_Message: adds a packet to a Private Connection queue, * + * or reads from the queue * + * Service: Checks the Real-Mode-Memory packet array to see if any * + * new packets have come in; if they have, it parses them * + * & distributes them to the right connection queue. The * + * queue's Service routine handles ACK'ing or Resending * + * packets. * + * * + * Here's a memory map of the Real-Mode memory block. 'N' is the number * + * of packet buffers allocated in low memory: * + * * + * ---------------------------------- * + * | Shared-memory data | * + * |--------------------------------| * + * | Real-mode event handler code | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 0 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 1 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 2 | * + * |--------------------------------| * + * | . . . | * + * |--------------------------------| * + * | IPX Header & Packet Buffer N | * + * |--------------------------------| * + * | Send Event Control Block | * + * |--------------------------------| * + * | Send IPX Header | * + * |--------------------------------| * + * | Send Packet Buffer | * + * |--------------------------------| * + * | Flags Array [N] | * + * ---------------------------------- * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXMANAGER_H +#define IPXMANAGER_H + + +/* +********************************* Includes ********************************** +*/ +#include "ipxconn.h" +#include "ipxgconn.h" +#include "ipxaddr.h" +#include "connmgr.h" + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is Virgin Interactive Entertainment's registered socket ID. +---------------------------------------------------------------------------*/ +#define VIRGIN_SOCKET 0x8813 + +/*--------------------------------------------------------------------------- +This is the maximum number of IPX connections supported. Just change this +value to support more. +---------------------------------------------------------------------------*/ +#define CONNECT_MAX 7 + +/*--------------------------------------------------------------------------- +These routines report the location & length of the real-mode routine, as +it's stored in protected-mode memory. +---------------------------------------------------------------------------*/ +extern "C" { + void * __cdecl Get_RM_IPX_Address(void); + long __cdecl Get_RM_IPX_Size(void); +} + +/* +***************************** Class Declaration ***************************** +*/ +class IPXManagerClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXManagerClass (int glb_maxlen, int pvt_maxlen, int glb_num_packets, + int pvt_num_packets, unsigned short socket, unsigned short product_id); + virtual ~IPXManagerClass (); // stop listening + + /*..................................................................... + Initialization routines. + .....................................................................*/ + int Init (void); + int Is_IPX(void); + virtual void Set_Timing (unsigned long retrydelta, unsigned long maxretries, + unsigned long timeout); + void Set_Bridge(NetNumType bridge); + + /*..................................................................... + These routines control creation of the "Connections" (data queues) for + each remote system. + .....................................................................*/ + int Create_Connection(int id, char *name, IPXAddressClass *address); + int Delete_Connection(int id); + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + char *Connection_Name(int id); + IPXAddressClass * Connection_Address(int id); + virtual int Connection_Index(int id); + void Set_Connection_Parms(int index, int id, char *name); + + /*..................................................................... + This is how the application sends & receives messages. + .....................................................................*/ + int Send_Global_Message (void *buf, int buflen, int ack_req = 0, + IPXAddressClass *address = NULL); + int Get_Global_Message (void *buf, int *buflen, IPXAddressClass *address, + unsigned short *product_id); + + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, int *conn_id); + + /*..................................................................... + The main polling routine; should be called as often as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine reports which connection has an error on it. + .....................................................................*/ + int Get_Bad_Connection(void); + + /*..................................................................... + Queue utility routines. The application can determine how many + messages are in the send/receive queues. + .....................................................................*/ + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + /*..................................................................... + This routine changes the socket ID assigned the IPX Manager when it + was constructed. Do not call this function after calling Init()! + The Socket ID should be known by both ends of the communications before + any packets are sent. + .....................................................................*/ + void Set_Socket(unsigned short socket); + + /*..................................................................... + Routines to return the largest average queue response time, and to + reset the response time for all queues. + .....................................................................*/ + virtual unsigned long Response_Time(void); + unsigned long Global_Response_Time(void); + virtual void Reset_Response_Time(void); + + /*..................................................................... + This routine returns a pointer to the oldest non-ACK'd buffer I've sent. + .....................................................................*/ + void * Oldest_Send(void); + + /*..................................................................... + Debug routines + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + These routines allocate & free the DOS Real-mode memory block. + .....................................................................*/ + int Alloc_RealMode_Mem(void); + int Free_RealMode_Mem(void); + + /*..................................................................... + Misc variables + .....................................................................*/ + unsigned int IPXStatus : 1; // 0 = no IPX, 1 = IPX found + unsigned int Listening : 1; // 1 = Listening is on + unsigned int RealMemAllocd : 1; // 1 = Real-mode memory has been alloc'd + + /*..................................................................... + Packet Sizes, used for allocating real-mode memory + .....................................................................*/ + int Glb_MaxPacketLen; // Global Channel maximum packet size + int Glb_NumPackets; // # Global send/receive packets + int Pvt_MaxPacketLen; // Private Channel maximum packet size + int Pvt_NumPackets; // # Private send/receive packets + + /*..................................................................... + The ProductID is used in the Global Channel's packet header, and it's + used for the Private Channels' Magic Number. + .....................................................................*/ + unsigned short ProductID; // product ID + + /*..................................................................... + The Socket ID, and local Novell Connection Number + .....................................................................*/ + unsigned short Socket; // Our socket ID for sending/receiving + int ConnectionNum; // local connection #, 0=not logged in + + /*..................................................................... + Array of connection queues + .....................................................................*/ + IPXConnClass * Connection[CONNECT_MAX]; // array of connection object ptrs + int NumConnections; // # connection objects in use + IPXGlobalConnClass *GlobalChannel; // the Global Channel + + /*..................................................................... + Current queue for polling for received packets + .....................................................................*/ + int CurConnection; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /*--------------------------------------------------------------------- + Real-mode memory pointers and such + ---------------------------------------------------------------------*/ + /*..................................................................... + This is a structure that mirrors data in real-mode memory: + .....................................................................*/ + typedef struct { + short Marker1; // the byte ID marker + ECBType ListenECB; // the Listening ECB + short NumBufs; // # of buffers we're giving to the handler + char *BufferFlags; // array of buffer-avail flags + short PacketSize; // size of packet including IPX header + IPXHeaderType *FirstPacketBuf; // ptr to 1st packet buffer + short CurIndex; // handler's current packet index + IPXHeaderType *CurPacketBuf; // handler's current packet buf + short FuncOffset; // contains offset of code + char Semaphore; // prevents re-entrancy + short ReEntrantCount; // times we've been called re-entrantly + short StackPtr; // real-mode stack pointer + short StackSeg; // real-mode stack segment + short StackPtr_int; // internal stack pointer + short StackSeg_int; // internal stack segment + short StackCheck; // stack check value (0x1234) + short Stack[256]; // actual stack space + short StackSpace; // label for top of stack + short Marker2; // the byte ID marker + } RealModeDataType; + + /*..................................................................... + The number & size of packet buffers in low memory + .....................................................................*/ + int NumBufs; // # packet buffers allocated + int PacketLen; // size of packet without IPX header + int FullPacketLen; // size of packet including IPX header + + /*..................................................................... + Selector & Segment of the DOS allocation; + Size of the allocation; + Ptr to the real-mode assembly data area + .....................................................................*/ + unsigned short Selector; // selector of DOS allocation pointer + unsigned short Segment; // real-mode segment of DOS allocation + int RealMemSize; // size of real mode memory allocated + RealModeDataType *RealModeData; // assembly routine & its data + + /*..................................................................... + This is a real-mode pointer to the address of the real-mode assembly + entry point. + .....................................................................*/ + long Handler; + + /*..................................................................... + Event Control Block for listening; contained within the real-mode + assembly routine's data area + .....................................................................*/ + ECBType *ListenECB; // ECB for listening + + /*..................................................................... + ptr to the 1st header & data buffers in the packet buffer array + .....................................................................*/ + IPXHeaderType *FirstHeaderBuf; // array of packet headers & buffers + char *FirstDataBuf; // 1st data buffer area + + /*..................................................................... + Current packet index & ptrs for parsing packets + .....................................................................*/ + int CurIndex; // Current packet index, for reading + IPXHeaderType *CurHeaderBuf; // Current packet ptr, for reading + char *CurDataBuf; // Current actual data ptr + + /*..................................................................... + ECB, header, & buffer for sending + .....................................................................*/ + ECBType *SendECB; // ECB for sending + IPXHeaderType *SendHeader; // Header for sending + char *SendBuf; // buffer for sending + + /*..................................................................... + Flags indicating whether a buffer contains data or not (1 = full) + The IPXManager must clear this flag; the real-mode routine will set it. + .....................................................................*/ + char *BufferFlags; // array of rx-buffer-avail flags + + /*..................................................................... + Various Statistics + .....................................................................*/ + int SendOverflows; + int ReceiveOverflows; + int BadConnection; +}; + +#endif + +/*************************** end of ipxmgr.h *******************************/ diff --git a/REDALERT/IPXPROT.ASM b/REDALERT/IPXPROT.ASM new file mode 100644 index 000000000..ab95251f6 --- /dev/null +++ b/REDALERT/IPXPROT.ASM @@ -0,0 +1,110 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;!!!!!!!!!!!!!!!!!!! lock the allocation + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +GLOBAL C Get_RM_IPX_Address:NEAR +GLOBAL C Get_RM_IPX_Size:NEAR + +;********************************* Data ************************************ + DATASEG + +LABEL RealBinStart BYTE +include "obj\win32\ipxreal.ibn" +LABEL RealBinEnd BYTE + +;********************************* Data ************************************ + CODESEG + +;*************************************************************************** +;* Get_RM_IPX_Address -- Return address of real mode code for copy. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* VOID * to the address of the real mode IPX code * +;* * +;* PROTO: * +;* VOID *Get_RM_IPX_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* Get_RM_IPX_Size -- return size of real mode IPX code. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* LONG size of the real mode IPX code * +;* * +;* PROTO: * +;* LONG Get_RM_IPX_Size(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + END + +;************************** End of handler.asm ***************************** diff --git a/REDALERT/IPXREAL.ASM b/REDALERT/IPXREAL.ASM new file mode 100644 index 000000000..657c11378 --- /dev/null +++ b/REDALERT/IPXREAL.ASM @@ -0,0 +1,316 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +MODEL LARGE +P386N +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +GLOBAL IPXHandler:FAR + + +;********************************* Code ************************************ + CODESEG + + +;--------------------------------------------------------------------------- +; The markers let the application verify that it's mapping this memory +; correctly. +;--------------------------------------------------------------------------- +Marker1 DW 1111h ; placeholder to find data start + +;--------------------------------------------------------------------------- +; This is the IPX Event Control Block: +;--------------------------------------------------------------------------- +ECB_LinkAddress DD ? +ECB_EventServiceRoutine DD ? ; Event Handler ptr +ECB_InUse DB ? ; 0 = event is complete +ECB_CompletionCode DB ? ; 0 = OK, IPX error otherwise +ECB_SocketNumber DW ? ; socket to listen/send on +ECB_ConnectionID DW ? +ECB_RestOfWorkspace DW ? +ECB_DriverWorkSpace DB 12 DUP (?) +ECB_ImmediateAddress DB 6 DUP (?) ; bridge address +ECB_PacketCount DW ? ; # data areas (2) +ECB_HeaderAddress DD ? ; ptr to IPX header buffer +ECB_HeaderLength DW ? ; length of IPX header buffer +ECB_PacketAddress DD ? ; ptr to packet buffer +ECB_PacketLength DW ? ; length of packet buffer + +;--------------------------------------------------------------------------- +; The rest of the variables are for telling IPX which buffer to store the +; next incoming packet in. They must be initialized by the application. +;--------------------------------------------------------------------------- +NumBufs DW 0 ; # buffers provided by app +BufferFlags DD 0 ; array of in-use flags (1 = in use) +PacketSize DW 0 ; total size of 1 buf (incl IPX hdr) +FirstPacketBuf DD 0 ; ptr to 1st packet buffer +CurIndex DW 0 ; current packet/flag index +CurPacketBuf DD 0 ; ptr to current packet buf +FuncOffset DW StartLabel ; offset of our routine + +;--------------------------------------------------------------------------- +; These values are for preventing re-entrancy; they're currently not used. +;--------------------------------------------------------------------------- +Semaphore DB 0 ; prevents re-entrancy +ReEntrantCount DW 0 ; times we've been called re-entrantly + +;--------------------------------------------------------------------------- +; Local stack space +;--------------------------------------------------------------------------- +StackPtr DW 0 ; saved copy of stack ptr +StackSeg DW 0 ; saved copy of stack seg +StackPtr_int DW 0 ; our internal stack ptr +StackSeg_int DW 0 ; our internal stack seg +StackCheck DW 1234h ; check for stack overflow + DW 256 DUP (0) ; stack storage space +StackSpace DW 0 ; label for our stack space + +;--------------------------------------------------------------------------- +; These bytes mark the end of the real-mode data area +;--------------------------------------------------------------------------- +Marker2 DW 2222h ; placeholder to find data end + + +;*************************************************************************** +;* IPXHandler -- IPX callback routine * +;* * +;* This routine is assembled as a stand-alone executable, then loaded * +;* into low DOS memory by a protected-mode application. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 04/07/1995 BRR : Created. * +;*=========================================================================* + label StartLabel + PROC IPXHandler C FAR USES + + ;................................................................... + ; Turn off interrupts; make sure memory copies go forward + ;................................................................... + pushf + cli + cld + + ;................................................................... + ; Set up segment registers to point DS to CS + ;................................................................... + push ds + push ax + mov ax,cs + mov ds,ax + + ;................................................................... + ; Set up our local stack; save SS & SP first. + ;................................................................... + mov [StackSeg],ss + mov [StackPtr],sp + mov [StackPtr_int], OFFSET StackSpace + mov [StackSeg_int], SEG StackSpace + lss sp, [DWORD PTR StackPtr_int] + + + ;................................................................... + ; Save all registers + ;................................................................... + pushad + push es + + ;................................................................... + ; If we've been called re-entrantly, just exit + ;................................................................... + cmp [Semaphore],0 + jz ??Start_Handler + add [ReEntrantCount],1 + jmp ??Exit_Handler + +??Start_Handler: + ;................................................................... + ; Set our semaphore + ;................................................................... + mov [Semaphore],1 + + ;------------------------------------------------------------------- + ; Set 'CurIndex' to the index of the next-available receive buffer, + ; and 'CurPacketBuf to the next-available packet buffer + ;------------------------------------------------------------------- + ;................................................................... + ; Get 'CurIndex' & increment it. Wrap to 0 if we reach 'NumBufs' + ; Since I'm treating 'CurPacketBuf' as a long integer (and not as + ; a segment:offset), the entire data area can't be larger than 64K. + ;................................................................... + mov dx,[CurIndex] ; DX = CurIndex + mov eax,[CurPacketBuf] ; EAX = current packet buffer addr + inc dx ; DX = next index + add ax,[PacketSize] ; EAX = next buffer ptr + cmp dx,[NumBufs] ; see if DX is past # buffers + jb ??Get_Flag + mov dx,0 ; wrap to 1st index + mov eax,[FirstPacketBuf] ; wrap to 1st packet buffer + +??Get_Flag: + ;................................................................... + ; Get the next buffer-in-use flag; if it's 0, load [CurIndex] with + ; the value of SI (the next index). If it's 1, skip the updating of + ; the index, flag & buffer ptr. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ;................................................................... + les di,[BufferFlags] ; ES:DI = BufferFlags address + mov bx,di ; BX = DI + new CurIndex + add bx,dx + + cmp [BYTE PTR es:bx],0 ; compare next flag to 0 (avail) + jne ??Set_ECB ; if not avail, skip setting new values + + ;................................................................... + ; The next buffer is available; so, set this buffer's In-Use flag + ; to 1, and move on to the next buffer. Do not set this buffer's + ; flag to 1 until we move on to the next buffer, to prevent the + ; application from reading the currently-in-use packet buffer. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ; ES:DI = BufferFlags address + ;................................................................... + mov bx,di ; BX = DI + old CurIndex + add bx,[CurIndex] + mov [BYTE PTR es:bx],1 ; set old BufferFlags value to in-use + + mov [CurIndex],dx ; save new index + mov [CurPacketBuf],eax ; save new packet address + + ;------------------------------------------------------------------- + ; Set up the Event Control Block to tell IPX to start listening. + ; The following entries are filled in by the app, and should be left + ; alone: + ; - EventServiceRoutine + ; - SocketNumber + ; The rest should be re-initialized. Note that EBX is now pointing + ; to an unavailable buffer if the next buffer was already in use; + ; so it must be reloaded with the correct buffer address from + ; [CurPacketBuf]. + ;------------------------------------------------------------------- +??Set_ECB: + mov [ECB_LinkAddress],0 ; default + mov [ECB_InUse],0 ; default + mov [ECB_CompletionCode],0 ; default + mov [ECB_ConnectionID],0 ; default + mov [ECB_RestOfWorkspace],0 ; default + mov [ECB_DriverWorkSpace],0 ; default + mov [ECB_ImmediateAddress],0 ; default + mov [ECB_PacketCount],2 ; use 2 data areas + mov ebx,[CurPacketBuf] ; get current buffer address + mov [ECB_HeaderAddress],ebx ; set header address + mov [ECB_HeaderLength],30 ; size of IPX header + add ebx,30 ; point to past the header + mov [ECB_PacketAddress],ebx ; set packet data address + mov ax,[PacketSize] ; get size of one buffer + sub ax,30 ; remove size of IPX header + mov [ECB_PacketLength],ax ; set size of packet data + + ;------------------------------------------------------------------- + ; Clear the IPX header for this packet + ;------------------------------------------------------------------- + les di,[ECB_HeaderAddress] ; ES:DI = IPX Listen Header + mov cx,30 ; (30 bytes = size of header) + mov al,0 + rep stosb ; clear to 0's + + ;------------------------------------------------------------------- + ; Command IPX to start listening again. + ;------------------------------------------------------------------- + mov bx,4 ; IPX code for Listen + mov ax,ds ; ES = segment of ListenECB + mov es,ax + mov ax,OFFSET ECB_LinkAddress + mov si,ax ; ES:SI = address of ECB + int 07ah ; call IPX interrupt + + ;................................................................... + ; Clear our semaphore + ;................................................................... + mov [Semaphore],0 + +??Exit_Handler: + ;................................................................... + ; Pop values from our local stack + ;................................................................... + pop es + popad + + ;................................................................... + ; Check our stack-check value; if the stack has overflowed, generate + ; a debugger break. + ;................................................................... + cmp [StackCheck],1234h + je ??Restore_Stack + int 3 + + ;................................................................... + ; Restore the stack to its previous value + ;................................................................... +??Restore_Stack: + lss sp, [DWORD PTR StackPtr] + + ;................................................................... + ; Pop the rest of the registers + ;................................................................... + pop ax + pop ds + + popf + + ret + +ENDP IPXHandler + +END + +;************************** End of handler.asm ***************************** diff --git a/REDALERT/ITABLE.CPP b/REDALERT/ITABLE.CPP new file mode 100644 index 000000000..2cf486599 --- /dev/null +++ b/REDALERT/ITABLE.CPP @@ -0,0 +1,1443 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +unsigned short IndexTable[] = { + 0, // Index = 0, Token = 0 + 0, // Index = 0, Token = 1 + 0, // Index = 0, Token = 2 + 0, // Index = 0, Token = 3 + 32, // Index = 0, Token = 4 + 64, // Index = 0, Token = 5 + 96, // Index = 0, Token = 6 + 128, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + 0, // Index = 0, Token = 9 + 0, // Index = 0, Token = 10 + 0, // Index = 0, Token = 11 + 32, // Index = 0, Token = 12 + 64, // Index = 0, Token = 13 + 96, // Index = 0, Token = 14 + 128, // Index = 0, Token = 15 + 0, // Index = 1, Token = 0 + 0, // Index = 1, Token = 1 + 0, // Index = 1, Token = 2 + 0, // Index = 1, Token = 3 + 48, // Index = 1, Token = 4 + 80, // Index = 1, Token = 5 + 112, // Index = 1, Token = 6 + 144, // Index = 1, Token = 7 + 0, // Index = 1, Token = 8 + 0, // Index = 1, Token = 9 + 0, // Index = 1, Token = 10 + 0, // Index = 1, Token = 11 + 48, // Index = 1, Token = 12 + 80, // Index = 1, Token = 13 + 112, // Index = 1, Token = 14 + 144, // Index = 1, Token = 15 + 16, // Index = 2, Token = 0 + 16, // Index = 2, Token = 1 + 16, // Index = 2, Token = 2 + 16, // Index = 2, Token = 3 + 64, // Index = 2, Token = 4 + 96, // Index = 2, Token = 5 + 128, // Index = 2, Token = 6 + 160, // Index = 2, Token = 7 + 16, // Index = 2, Token = 8 + 16, // Index = 2, Token = 9 + 16, // Index = 2, Token = 10 + 16, // Index = 2, Token = 11 + 64, // Index = 2, Token = 12 + 96, // Index = 2, Token = 13 + 128, // Index = 2, Token = 14 + 160, // Index = 2, Token = 15 + 32, // Index = 3, Token = 0 + 32, // Index = 3, Token = 1 + 32, // Index = 3, Token = 2 + 32, // Index = 3, Token = 3 + 80, // Index = 3, Token = 4 + 112, // Index = 3, Token = 5 + 144, // Index = 3, Token = 6 + 176, // Index = 3, Token = 7 + 32, // Index = 3, Token = 8 + 32, // Index = 3, Token = 9 + 32, // Index = 3, Token = 10 + 32, // Index = 3, Token = 11 + 80, // Index = 3, Token = 12 + 112, // Index = 3, Token = 13 + 144, // Index = 3, Token = 14 + 176, // Index = 3, Token = 15 + 48, // Index = 4, Token = 0 + 48, // Index = 4, Token = 1 + 48, // Index = 4, Token = 2 + 48, // Index = 4, Token = 3 + 96, // Index = 4, Token = 4 + 128, // Index = 4, Token = 5 + 160, // Index = 4, Token = 6 + 192, // Index = 4, Token = 7 + 48, // Index = 4, Token = 8 + 48, // Index = 4, Token = 9 + 48, // Index = 4, Token = 10 + 48, // Index = 4, Token = 11 + 96, // Index = 4, Token = 12 + 128, // Index = 4, Token = 13 + 160, // Index = 4, Token = 14 + 192, // Index = 4, Token = 15 + 64, // Index = 5, Token = 0 + 64, // Index = 5, Token = 1 + 64, // Index = 5, Token = 2 + 64, // Index = 5, Token = 3 + 112, // Index = 5, Token = 4 + 144, // Index = 5, Token = 5 + 176, // Index = 5, Token = 6 + 208, // Index = 5, Token = 7 + 64, // Index = 5, Token = 8 + 64, // Index = 5, Token = 9 + 64, // Index = 5, Token = 10 + 64, // Index = 5, Token = 11 + 112, // Index = 5, Token = 12 + 144, // Index = 5, Token = 13 + 176, // Index = 5, Token = 14 + 208, // Index = 5, Token = 15 + 80, // Index = 6, Token = 0 + 80, // Index = 6, Token = 1 + 80, // Index = 6, Token = 2 + 80, // Index = 6, Token = 3 + 128, // Index = 6, Token = 4 + 160, // Index = 6, Token = 5 + 192, // Index = 6, Token = 6 + 224, // Index = 6, Token = 7 + 80, // Index = 6, Token = 8 + 80, // Index = 6, Token = 9 + 80, // Index = 6, Token = 10 + 80, // Index = 6, Token = 11 + 128, // Index = 6, Token = 12 + 160, // Index = 6, Token = 13 + 192, // Index = 6, Token = 14 + 224, // Index = 6, Token = 15 + 96, // Index = 7, Token = 0 + 96, // Index = 7, Token = 1 + 96, // Index = 7, Token = 2 + 96, // Index = 7, Token = 3 + 144, // Index = 7, Token = 4 + 176, // Index = 7, Token = 5 + 208, // Index = 7, Token = 6 + 240, // Index = 7, Token = 7 + 96, // Index = 7, Token = 8 + 96, // Index = 7, Token = 9 + 96, // Index = 7, Token = 10 + 96, // Index = 7, Token = 11 + 144, // Index = 7, Token = 12 + 176, // Index = 7, Token = 13 + 208, // Index = 7, Token = 14 + 240, // Index = 7, Token = 15 + 112, // Index = 8, Token = 0 + 112, // Index = 8, Token = 1 + 112, // Index = 8, Token = 2 + 112, // Index = 8, Token = 3 + 160, // Index = 8, Token = 4 + 192, // Index = 8, Token = 5 + 224, // Index = 8, Token = 6 + 256, // Index = 8, Token = 7 + 112, // Index = 8, Token = 8 + 112, // Index = 8, Token = 9 + 112, // Index = 8, Token = 10 + 112, // Index = 8, Token = 11 + 160, // Index = 8, Token = 12 + 192, // Index = 8, Token = 13 + 224, // Index = 8, Token = 14 + 256, // Index = 8, Token = 15 + 128, // Index = 9, Token = 0 + 128, // Index = 9, Token = 1 + 128, // Index = 9, Token = 2 + 128, // Index = 9, Token = 3 + 176, // Index = 9, Token = 4 + 208, // Index = 9, Token = 5 + 240, // Index = 9, Token = 6 + 272, // Index = 9, Token = 7 + 128, // Index = 9, Token = 8 + 128, // Index = 9, Token = 9 + 128, // Index = 9, Token = 10 + 128, // Index = 9, Token = 11 + 176, // Index = 9, Token = 12 + 208, // Index = 9, Token = 13 + 240, // Index = 9, Token = 14 + 272, // Index = 9, Token = 15 + 144, // Index = 10, Token = 0 + 144, // Index = 10, Token = 1 + 144, // Index = 10, Token = 2 + 144, // Index = 10, Token = 3 + 192, // Index = 10, Token = 4 + 224, // Index = 10, Token = 5 + 256, // Index = 10, Token = 6 + 288, // Index = 10, Token = 7 + 144, // Index = 10, Token = 8 + 144, // Index = 10, Token = 9 + 144, // Index = 10, Token = 10 + 144, // Index = 10, Token = 11 + 192, // Index = 10, Token = 12 + 224, // Index = 10, Token = 13 + 256, // Index = 10, Token = 14 + 288, // Index = 10, Token = 15 + 160, // Index = 11, Token = 0 + 160, // Index = 11, Token = 1 + 160, // Index = 11, Token = 2 + 160, // Index = 11, Token = 3 + 208, // Index = 11, Token = 4 + 240, // Index = 11, Token = 5 + 272, // Index = 11, Token = 6 + 304, // Index = 11, Token = 7 + 160, // Index = 11, Token = 8 + 160, // Index = 11, Token = 9 + 160, // Index = 11, Token = 10 + 160, // Index = 11, Token = 11 + 208, // Index = 11, Token = 12 + 240, // Index = 11, Token = 13 + 272, // Index = 11, Token = 14 + 304, // Index = 11, Token = 15 + 176, // Index = 12, Token = 0 + 176, // Index = 12, Token = 1 + 176, // Index = 12, Token = 2 + 176, // Index = 12, Token = 3 + 224, // Index = 12, Token = 4 + 256, // Index = 12, Token = 5 + 288, // Index = 12, Token = 6 + 320, // Index = 12, Token = 7 + 176, // Index = 12, Token = 8 + 176, // Index = 12, Token = 9 + 176, // Index = 12, Token = 10 + 176, // Index = 12, Token = 11 + 224, // Index = 12, Token = 12 + 256, // Index = 12, Token = 13 + 288, // Index = 12, Token = 14 + 320, // Index = 12, Token = 15 + 192, // Index = 13, Token = 0 + 192, // Index = 13, Token = 1 + 192, // Index = 13, Token = 2 + 192, // Index = 13, Token = 3 + 240, // Index = 13, Token = 4 + 272, // Index = 13, Token = 5 + 304, // Index = 13, Token = 6 + 336, // Index = 13, Token = 7 + 192, // Index = 13, Token = 8 + 192, // Index = 13, Token = 9 + 192, // Index = 13, Token = 10 + 192, // Index = 13, Token = 11 + 240, // Index = 13, Token = 12 + 272, // Index = 13, Token = 13 + 304, // Index = 13, Token = 14 + 336, // Index = 13, Token = 15 + 208, // Index = 14, Token = 0 + 208, // Index = 14, Token = 1 + 208, // Index = 14, Token = 2 + 208, // Index = 14, Token = 3 + 256, // Index = 14, Token = 4 + 288, // Index = 14, Token = 5 + 320, // Index = 14, Token = 6 + 352, // Index = 14, Token = 7 + 208, // Index = 14, Token = 8 + 208, // Index = 14, Token = 9 + 208, // Index = 14, Token = 10 + 208, // Index = 14, Token = 11 + 256, // Index = 14, Token = 12 + 288, // Index = 14, Token = 13 + 320, // Index = 14, Token = 14 + 352, // Index = 14, Token = 15 + 224, // Index = 15, Token = 0 + 224, // Index = 15, Token = 1 + 224, // Index = 15, Token = 2 + 224, // Index = 15, Token = 3 + 272, // Index = 15, Token = 4 + 304, // Index = 15, Token = 5 + 336, // Index = 15, Token = 6 + 368, // Index = 15, Token = 7 + 224, // Index = 15, Token = 8 + 224, // Index = 15, Token = 9 + 224, // Index = 15, Token = 10 + 224, // Index = 15, Token = 11 + 272, // Index = 15, Token = 12 + 304, // Index = 15, Token = 13 + 336, // Index = 15, Token = 14 + 368, // Index = 15, Token = 15 + 240, // Index = 16, Token = 0 + 240, // Index = 16, Token = 1 + 240, // Index = 16, Token = 2 + 240, // Index = 16, Token = 3 + 288, // Index = 16, Token = 4 + 320, // Index = 16, Token = 5 + 352, // Index = 16, Token = 6 + 384, // Index = 16, Token = 7 + 240, // Index = 16, Token = 8 + 240, // Index = 16, Token = 9 + 240, // Index = 16, Token = 10 + 240, // Index = 16, Token = 11 + 288, // Index = 16, Token = 12 + 320, // Index = 16, Token = 13 + 352, // Index = 16, Token = 14 + 384, // Index = 16, Token = 15 + 256, // Index = 17, Token = 0 + 256, // Index = 17, Token = 1 + 256, // Index = 17, Token = 2 + 256, // Index = 17, Token = 3 + 304, // Index = 17, Token = 4 + 336, // Index = 17, Token = 5 + 368, // Index = 17, Token = 6 + 400, // Index = 17, Token = 7 + 256, // Index = 17, Token = 8 + 256, // Index = 17, Token = 9 + 256, // Index = 17, Token = 10 + 256, // Index = 17, Token = 11 + 304, // Index = 17, Token = 12 + 336, // Index = 17, Token = 13 + 368, // Index = 17, Token = 14 + 400, // Index = 17, Token = 15 + 272, // Index = 18, Token = 0 + 272, // Index = 18, Token = 1 + 272, // Index = 18, Token = 2 + 272, // Index = 18, Token = 3 + 320, // Index = 18, Token = 4 + 352, // Index = 18, Token = 5 + 384, // Index = 18, Token = 6 + 416, // Index = 18, Token = 7 + 272, // Index = 18, Token = 8 + 272, // Index = 18, Token = 9 + 272, // Index = 18, Token = 10 + 272, // Index = 18, Token = 11 + 320, // Index = 18, Token = 12 + 352, // Index = 18, Token = 13 + 384, // Index = 18, Token = 14 + 416, // Index = 18, Token = 15 + 288, // Index = 19, Token = 0 + 288, // Index = 19, Token = 1 + 288, // Index = 19, Token = 2 + 288, // Index = 19, Token = 3 + 336, // Index = 19, Token = 4 + 368, // Index = 19, Token = 5 + 400, // Index = 19, Token = 6 + 432, // Index = 19, Token = 7 + 288, // Index = 19, Token = 8 + 288, // Index = 19, Token = 9 + 288, // Index = 19, Token = 10 + 288, // Index = 19, Token = 11 + 336, // Index = 19, Token = 12 + 368, // Index = 19, Token = 13 + 400, // Index = 19, Token = 14 + 432, // Index = 19, Token = 15 + 304, // Index = 20, Token = 0 + 304, // Index = 20, Token = 1 + 304, // Index = 20, Token = 2 + 304, // Index = 20, Token = 3 + 352, // Index = 20, Token = 4 + 384, // Index = 20, Token = 5 + 416, // Index = 20, Token = 6 + 448, // Index = 20, Token = 7 + 304, // Index = 20, Token = 8 + 304, // Index = 20, Token = 9 + 304, // Index = 20, Token = 10 + 304, // Index = 20, Token = 11 + 352, // Index = 20, Token = 12 + 384, // Index = 20, Token = 13 + 416, // Index = 20, Token = 14 + 448, // Index = 20, Token = 15 + 320, // Index = 21, Token = 0 + 320, // Index = 21, Token = 1 + 320, // Index = 21, Token = 2 + 320, // Index = 21, Token = 3 + 368, // Index = 21, Token = 4 + 400, // Index = 21, Token = 5 + 432, // Index = 21, Token = 6 + 464, // Index = 21, Token = 7 + 320, // Index = 21, Token = 8 + 320, // Index = 21, Token = 9 + 320, // Index = 21, Token = 10 + 320, // Index = 21, Token = 11 + 368, // Index = 21, Token = 12 + 400, // Index = 21, Token = 13 + 432, // Index = 21, Token = 14 + 464, // Index = 21, Token = 15 + 336, // Index = 22, Token = 0 + 336, // Index = 22, Token = 1 + 336, // Index = 22, Token = 2 + 336, // Index = 22, Token = 3 + 384, // Index = 22, Token = 4 + 416, // Index = 22, Token = 5 + 448, // Index = 22, Token = 6 + 480, // Index = 22, Token = 7 + 336, // Index = 22, Token = 8 + 336, // Index = 22, Token = 9 + 336, // Index = 22, Token = 10 + 336, // Index = 22, Token = 11 + 384, // Index = 22, Token = 12 + 416, // Index = 22, Token = 13 + 448, // Index = 22, Token = 14 + 480, // Index = 22, Token = 15 + 352, // Index = 23, Token = 0 + 352, // Index = 23, Token = 1 + 352, // Index = 23, Token = 2 + 352, // Index = 23, Token = 3 + 400, // Index = 23, Token = 4 + 432, // Index = 23, Token = 5 + 464, // Index = 23, Token = 6 + 496, // Index = 23, Token = 7 + 352, // Index = 23, Token = 8 + 352, // Index = 23, Token = 9 + 352, // Index = 23, Token = 10 + 352, // Index = 23, Token = 11 + 400, // Index = 23, Token = 12 + 432, // Index = 23, Token = 13 + 464, // Index = 23, Token = 14 + 496, // Index = 23, Token = 15 + 368, // Index = 24, Token = 0 + 368, // Index = 24, Token = 1 + 368, // Index = 24, Token = 2 + 368, // Index = 24, Token = 3 + 416, // Index = 24, Token = 4 + 448, // Index = 24, Token = 5 + 480, // Index = 24, Token = 6 + 512, // Index = 24, Token = 7 + 368, // Index = 24, Token = 8 + 368, // Index = 24, Token = 9 + 368, // Index = 24, Token = 10 + 368, // Index = 24, Token = 11 + 416, // Index = 24, Token = 12 + 448, // Index = 24, Token = 13 + 480, // Index = 24, Token = 14 + 512, // Index = 24, Token = 15 + 384, // Index = 25, Token = 0 + 384, // Index = 25, Token = 1 + 384, // Index = 25, Token = 2 + 384, // Index = 25, Token = 3 + 432, // Index = 25, Token = 4 + 464, // Index = 25, Token = 5 + 496, // Index = 25, Token = 6 + 528, // Index = 25, Token = 7 + 384, // Index = 25, Token = 8 + 384, // Index = 25, Token = 9 + 384, // Index = 25, Token = 10 + 384, // Index = 25, Token = 11 + 432, // Index = 25, Token = 12 + 464, // Index = 25, Token = 13 + 496, // Index = 25, Token = 14 + 528, // Index = 25, Token = 15 + 400, // Index = 26, Token = 0 + 400, // Index = 26, Token = 1 + 400, // Index = 26, Token = 2 + 400, // Index = 26, Token = 3 + 448, // Index = 26, Token = 4 + 480, // Index = 26, Token = 5 + 512, // Index = 26, Token = 6 + 544, // Index = 26, Token = 7 + 400, // Index = 26, Token = 8 + 400, // Index = 26, Token = 9 + 400, // Index = 26, Token = 10 + 400, // Index = 26, Token = 11 + 448, // Index = 26, Token = 12 + 480, // Index = 26, Token = 13 + 512, // Index = 26, Token = 14 + 544, // Index = 26, Token = 15 + 416, // Index = 27, Token = 0 + 416, // Index = 27, Token = 1 + 416, // Index = 27, Token = 2 + 416, // Index = 27, Token = 3 + 464, // Index = 27, Token = 4 + 496, // Index = 27, Token = 5 + 528, // Index = 27, Token = 6 + 560, // Index = 27, Token = 7 + 416, // Index = 27, Token = 8 + 416, // Index = 27, Token = 9 + 416, // Index = 27, Token = 10 + 416, // Index = 27, Token = 11 + 464, // Index = 27, Token = 12 + 496, // Index = 27, Token = 13 + 528, // Index = 27, Token = 14 + 560, // Index = 27, Token = 15 + 432, // Index = 28, Token = 0 + 432, // Index = 28, Token = 1 + 432, // Index = 28, Token = 2 + 432, // Index = 28, Token = 3 + 480, // Index = 28, Token = 4 + 512, // Index = 28, Token = 5 + 544, // Index = 28, Token = 6 + 576, // Index = 28, Token = 7 + 432, // Index = 28, Token = 8 + 432, // Index = 28, Token = 9 + 432, // Index = 28, Token = 10 + 432, // Index = 28, Token = 11 + 480, // Index = 28, Token = 12 + 512, // Index = 28, Token = 13 + 544, // Index = 28, Token = 14 + 576, // Index = 28, Token = 15 + 448, // Index = 29, Token = 0 + 448, // Index = 29, Token = 1 + 448, // Index = 29, Token = 2 + 448, // Index = 29, Token = 3 + 496, // Index = 29, Token = 4 + 528, // Index = 29, Token = 5 + 560, // Index = 29, Token = 6 + 592, // Index = 29, Token = 7 + 448, // Index = 29, Token = 8 + 448, // Index = 29, Token = 9 + 448, // Index = 29, Token = 10 + 448, // Index = 29, Token = 11 + 496, // Index = 29, Token = 12 + 528, // Index = 29, Token = 13 + 560, // Index = 29, Token = 14 + 592, // Index = 29, Token = 15 + 464, // Index = 30, Token = 0 + 464, // Index = 30, Token = 1 + 464, // Index = 30, Token = 2 + 464, // Index = 30, Token = 3 + 512, // Index = 30, Token = 4 + 544, // Index = 30, Token = 5 + 576, // Index = 30, Token = 6 + 608, // Index = 30, Token = 7 + 464, // Index = 30, Token = 8 + 464, // Index = 30, Token = 9 + 464, // Index = 30, Token = 10 + 464, // Index = 30, Token = 11 + 512, // Index = 30, Token = 12 + 544, // Index = 30, Token = 13 + 576, // Index = 30, Token = 14 + 608, // Index = 30, Token = 15 + 480, // Index = 31, Token = 0 + 480, // Index = 31, Token = 1 + 480, // Index = 31, Token = 2 + 480, // Index = 31, Token = 3 + 528, // Index = 31, Token = 4 + 560, // Index = 31, Token = 5 + 592, // Index = 31, Token = 6 + 624, // Index = 31, Token = 7 + 480, // Index = 31, Token = 8 + 480, // Index = 31, Token = 9 + 480, // Index = 31, Token = 10 + 480, // Index = 31, Token = 11 + 528, // Index = 31, Token = 12 + 560, // Index = 31, Token = 13 + 592, // Index = 31, Token = 14 + 624, // Index = 31, Token = 15 + 496, // Index = 32, Token = 0 + 496, // Index = 32, Token = 1 + 496, // Index = 32, Token = 2 + 496, // Index = 32, Token = 3 + 544, // Index = 32, Token = 4 + 576, // Index = 32, Token = 5 + 608, // Index = 32, Token = 6 + 640, // Index = 32, Token = 7 + 496, // Index = 32, Token = 8 + 496, // Index = 32, Token = 9 + 496, // Index = 32, Token = 10 + 496, // Index = 32, Token = 11 + 544, // Index = 32, Token = 12 + 576, // Index = 32, Token = 13 + 608, // Index = 32, Token = 14 + 640, // Index = 32, Token = 15 + 512, // Index = 33, Token = 0 + 512, // Index = 33, Token = 1 + 512, // Index = 33, Token = 2 + 512, // Index = 33, Token = 3 + 560, // Index = 33, Token = 4 + 592, // Index = 33, Token = 5 + 624, // Index = 33, Token = 6 + 656, // Index = 33, Token = 7 + 512, // Index = 33, Token = 8 + 512, // Index = 33, Token = 9 + 512, // Index = 33, Token = 10 + 512, // Index = 33, Token = 11 + 560, // Index = 33, Token = 12 + 592, // Index = 33, Token = 13 + 624, // Index = 33, Token = 14 + 656, // Index = 33, Token = 15 + 528, // Index = 34, Token = 0 + 528, // Index = 34, Token = 1 + 528, // Index = 34, Token = 2 + 528, // Index = 34, Token = 3 + 576, // Index = 34, Token = 4 + 608, // Index = 34, Token = 5 + 640, // Index = 34, Token = 6 + 672, // Index = 34, Token = 7 + 528, // Index = 34, Token = 8 + 528, // Index = 34, Token = 9 + 528, // Index = 34, Token = 10 + 528, // Index = 34, Token = 11 + 576, // Index = 34, Token = 12 + 608, // Index = 34, Token = 13 + 640, // Index = 34, Token = 14 + 672, // Index = 34, Token = 15 + 544, // Index = 35, Token = 0 + 544, // Index = 35, Token = 1 + 544, // Index = 35, Token = 2 + 544, // Index = 35, Token = 3 + 592, // Index = 35, Token = 4 + 624, // Index = 35, Token = 5 + 656, // Index = 35, Token = 6 + 688, // Index = 35, Token = 7 + 544, // Index = 35, Token = 8 + 544, // Index = 35, Token = 9 + 544, // Index = 35, Token = 10 + 544, // Index = 35, Token = 11 + 592, // Index = 35, Token = 12 + 624, // Index = 35, Token = 13 + 656, // Index = 35, Token = 14 + 688, // Index = 35, Token = 15 + 560, // Index = 36, Token = 0 + 560, // Index = 36, Token = 1 + 560, // Index = 36, Token = 2 + 560, // Index = 36, Token = 3 + 608, // Index = 36, Token = 4 + 640, // Index = 36, Token = 5 + 672, // Index = 36, Token = 6 + 704, // Index = 36, Token = 7 + 560, // Index = 36, Token = 8 + 560, // Index = 36, Token = 9 + 560, // Index = 36, Token = 10 + 560, // Index = 36, Token = 11 + 608, // Index = 36, Token = 12 + 640, // Index = 36, Token = 13 + 672, // Index = 36, Token = 14 + 704, // Index = 36, Token = 15 + 576, // Index = 37, Token = 0 + 576, // Index = 37, Token = 1 + 576, // Index = 37, Token = 2 + 576, // Index = 37, Token = 3 + 624, // Index = 37, Token = 4 + 656, // Index = 37, Token = 5 + 688, // Index = 37, Token = 6 + 720, // Index = 37, Token = 7 + 576, // Index = 37, Token = 8 + 576, // Index = 37, Token = 9 + 576, // Index = 37, Token = 10 + 576, // Index = 37, Token = 11 + 624, // Index = 37, Token = 12 + 656, // Index = 37, Token = 13 + 688, // Index = 37, Token = 14 + 720, // Index = 37, Token = 15 + 592, // Index = 38, Token = 0 + 592, // Index = 38, Token = 1 + 592, // Index = 38, Token = 2 + 592, // Index = 38, Token = 3 + 640, // Index = 38, Token = 4 + 672, // Index = 38, Token = 5 + 704, // Index = 38, Token = 6 + 736, // Index = 38, Token = 7 + 592, // Index = 38, Token = 8 + 592, // Index = 38, Token = 9 + 592, // Index = 38, Token = 10 + 592, // Index = 38, Token = 11 + 640, // Index = 38, Token = 12 + 672, // Index = 38, Token = 13 + 704, // Index = 38, Token = 14 + 736, // Index = 38, Token = 15 + 608, // Index = 39, Token = 0 + 608, // Index = 39, Token = 1 + 608, // Index = 39, Token = 2 + 608, // Index = 39, Token = 3 + 656, // Index = 39, Token = 4 + 688, // Index = 39, Token = 5 + 720, // Index = 39, Token = 6 + 752, // Index = 39, Token = 7 + 608, // Index = 39, Token = 8 + 608, // Index = 39, Token = 9 + 608, // Index = 39, Token = 10 + 608, // Index = 39, Token = 11 + 656, // Index = 39, Token = 12 + 688, // Index = 39, Token = 13 + 720, // Index = 39, Token = 14 + 752, // Index = 39, Token = 15 + 624, // Index = 40, Token = 0 + 624, // Index = 40, Token = 1 + 624, // Index = 40, Token = 2 + 624, // Index = 40, Token = 3 + 672, // Index = 40, Token = 4 + 704, // Index = 40, Token = 5 + 736, // Index = 40, Token = 6 + 768, // Index = 40, Token = 7 + 624, // Index = 40, Token = 8 + 624, // Index = 40, Token = 9 + 624, // Index = 40, Token = 10 + 624, // Index = 40, Token = 11 + 672, // Index = 40, Token = 12 + 704, // Index = 40, Token = 13 + 736, // Index = 40, Token = 14 + 768, // Index = 40, Token = 15 + 640, // Index = 41, Token = 0 + 640, // Index = 41, Token = 1 + 640, // Index = 41, Token = 2 + 640, // Index = 41, Token = 3 + 688, // Index = 41, Token = 4 + 720, // Index = 41, Token = 5 + 752, // Index = 41, Token = 6 + 784, // Index = 41, Token = 7 + 640, // Index = 41, Token = 8 + 640, // Index = 41, Token = 9 + 640, // Index = 41, Token = 10 + 640, // Index = 41, Token = 11 + 688, // Index = 41, Token = 12 + 720, // Index = 41, Token = 13 + 752, // Index = 41, Token = 14 + 784, // Index = 41, Token = 15 + 656, // Index = 42, Token = 0 + 656, // Index = 42, Token = 1 + 656, // Index = 42, Token = 2 + 656, // Index = 42, Token = 3 + 704, // Index = 42, Token = 4 + 736, // Index = 42, Token = 5 + 768, // Index = 42, Token = 6 + 800, // Index = 42, Token = 7 + 656, // Index = 42, Token = 8 + 656, // Index = 42, Token = 9 + 656, // Index = 42, Token = 10 + 656, // Index = 42, Token = 11 + 704, // Index = 42, Token = 12 + 736, // Index = 42, Token = 13 + 768, // Index = 42, Token = 14 + 800, // Index = 42, Token = 15 + 672, // Index = 43, Token = 0 + 672, // Index = 43, Token = 1 + 672, // Index = 43, Token = 2 + 672, // Index = 43, Token = 3 + 720, // Index = 43, Token = 4 + 752, // Index = 43, Token = 5 + 784, // Index = 43, Token = 6 + 816, // Index = 43, Token = 7 + 672, // Index = 43, Token = 8 + 672, // Index = 43, Token = 9 + 672, // Index = 43, Token = 10 + 672, // Index = 43, Token = 11 + 720, // Index = 43, Token = 12 + 752, // Index = 43, Token = 13 + 784, // Index = 43, Token = 14 + 816, // Index = 43, Token = 15 + 688, // Index = 44, Token = 0 + 688, // Index = 44, Token = 1 + 688, // Index = 44, Token = 2 + 688, // Index = 44, Token = 3 + 736, // Index = 44, Token = 4 + 768, // Index = 44, Token = 5 + 800, // Index = 44, Token = 6 + 832, // Index = 44, Token = 7 + 688, // Index = 44, Token = 8 + 688, // Index = 44, Token = 9 + 688, // Index = 44, Token = 10 + 688, // Index = 44, Token = 11 + 736, // Index = 44, Token = 12 + 768, // Index = 44, Token = 13 + 800, // Index = 44, Token = 14 + 832, // Index = 44, Token = 15 + 704, // Index = 45, Token = 0 + 704, // Index = 45, Token = 1 + 704, // Index = 45, Token = 2 + 704, // Index = 45, Token = 3 + 752, // Index = 45, Token = 4 + 784, // Index = 45, Token = 5 + 816, // Index = 45, Token = 6 + 848, // Index = 45, Token = 7 + 704, // Index = 45, Token = 8 + 704, // Index = 45, Token = 9 + 704, // Index = 45, Token = 10 + 704, // Index = 45, Token = 11 + 752, // Index = 45, Token = 12 + 784, // Index = 45, Token = 13 + 816, // Index = 45, Token = 14 + 848, // Index = 45, Token = 15 + 720, // Index = 46, Token = 0 + 720, // Index = 46, Token = 1 + 720, // Index = 46, Token = 2 + 720, // Index = 46, Token = 3 + 768, // Index = 46, Token = 4 + 800, // Index = 46, Token = 5 + 832, // Index = 46, Token = 6 + 864, // Index = 46, Token = 7 + 720, // Index = 46, Token = 8 + 720, // Index = 46, Token = 9 + 720, // Index = 46, Token = 10 + 720, // Index = 46, Token = 11 + 768, // Index = 46, Token = 12 + 800, // Index = 46, Token = 13 + 832, // Index = 46, Token = 14 + 864, // Index = 46, Token = 15 + 736, // Index = 47, Token = 0 + 736, // Index = 47, Token = 1 + 736, // Index = 47, Token = 2 + 736, // Index = 47, Token = 3 + 784, // Index = 47, Token = 4 + 816, // Index = 47, Token = 5 + 848, // Index = 47, Token = 6 + 880, // Index = 47, Token = 7 + 736, // Index = 47, Token = 8 + 736, // Index = 47, Token = 9 + 736, // Index = 47, Token = 10 + 736, // Index = 47, Token = 11 + 784, // Index = 47, Token = 12 + 816, // Index = 47, Token = 13 + 848, // Index = 47, Token = 14 + 880, // Index = 47, Token = 15 + 752, // Index = 48, Token = 0 + 752, // Index = 48, Token = 1 + 752, // Index = 48, Token = 2 + 752, // Index = 48, Token = 3 + 800, // Index = 48, Token = 4 + 832, // Index = 48, Token = 5 + 864, // Index = 48, Token = 6 + 896, // Index = 48, Token = 7 + 752, // Index = 48, Token = 8 + 752, // Index = 48, Token = 9 + 752, // Index = 48, Token = 10 + 752, // Index = 48, Token = 11 + 800, // Index = 48, Token = 12 + 832, // Index = 48, Token = 13 + 864, // Index = 48, Token = 14 + 896, // Index = 48, Token = 15 + 768, // Index = 49, Token = 0 + 768, // Index = 49, Token = 1 + 768, // Index = 49, Token = 2 + 768, // Index = 49, Token = 3 + 816, // Index = 49, Token = 4 + 848, // Index = 49, Token = 5 + 880, // Index = 49, Token = 6 + 912, // Index = 49, Token = 7 + 768, // Index = 49, Token = 8 + 768, // Index = 49, Token = 9 + 768, // Index = 49, Token = 10 + 768, // Index = 49, Token = 11 + 816, // Index = 49, Token = 12 + 848, // Index = 49, Token = 13 + 880, // Index = 49, Token = 14 + 912, // Index = 49, Token = 15 + 784, // Index = 50, Token = 0 + 784, // Index = 50, Token = 1 + 784, // Index = 50, Token = 2 + 784, // Index = 50, Token = 3 + 832, // Index = 50, Token = 4 + 864, // Index = 50, Token = 5 + 896, // Index = 50, Token = 6 + 928, // Index = 50, Token = 7 + 784, // Index = 50, Token = 8 + 784, // Index = 50, Token = 9 + 784, // Index = 50, Token = 10 + 784, // Index = 50, Token = 11 + 832, // Index = 50, Token = 12 + 864, // Index = 50, Token = 13 + 896, // Index = 50, Token = 14 + 928, // Index = 50, Token = 15 + 800, // Index = 51, Token = 0 + 800, // Index = 51, Token = 1 + 800, // Index = 51, Token = 2 + 800, // Index = 51, Token = 3 + 848, // Index = 51, Token = 4 + 880, // Index = 51, Token = 5 + 912, // Index = 51, Token = 6 + 944, // Index = 51, Token = 7 + 800, // Index = 51, Token = 8 + 800, // Index = 51, Token = 9 + 800, // Index = 51, Token = 10 + 800, // Index = 51, Token = 11 + 848, // Index = 51, Token = 12 + 880, // Index = 51, Token = 13 + 912, // Index = 51, Token = 14 + 944, // Index = 51, Token = 15 + 816, // Index = 52, Token = 0 + 816, // Index = 52, Token = 1 + 816, // Index = 52, Token = 2 + 816, // Index = 52, Token = 3 + 864, // Index = 52, Token = 4 + 896, // Index = 52, Token = 5 + 928, // Index = 52, Token = 6 + 960, // Index = 52, Token = 7 + 816, // Index = 52, Token = 8 + 816, // Index = 52, Token = 9 + 816, // Index = 52, Token = 10 + 816, // Index = 52, Token = 11 + 864, // Index = 52, Token = 12 + 896, // Index = 52, Token = 13 + 928, // Index = 52, Token = 14 + 960, // Index = 52, Token = 15 + 832, // Index = 53, Token = 0 + 832, // Index = 53, Token = 1 + 832, // Index = 53, Token = 2 + 832, // Index = 53, Token = 3 + 880, // Index = 53, Token = 4 + 912, // Index = 53, Token = 5 + 944, // Index = 53, Token = 6 + 976, // Index = 53, Token = 7 + 832, // Index = 53, Token = 8 + 832, // Index = 53, Token = 9 + 832, // Index = 53, Token = 10 + 832, // Index = 53, Token = 11 + 880, // Index = 53, Token = 12 + 912, // Index = 53, Token = 13 + 944, // Index = 53, Token = 14 + 976, // Index = 53, Token = 15 + 848, // Index = 54, Token = 0 + 848, // Index = 54, Token = 1 + 848, // Index = 54, Token = 2 + 848, // Index = 54, Token = 3 + 896, // Index = 54, Token = 4 + 928, // Index = 54, Token = 5 + 960, // Index = 54, Token = 6 + 992, // Index = 54, Token = 7 + 848, // Index = 54, Token = 8 + 848, // Index = 54, Token = 9 + 848, // Index = 54, Token = 10 + 848, // Index = 54, Token = 11 + 896, // Index = 54, Token = 12 + 928, // Index = 54, Token = 13 + 960, // Index = 54, Token = 14 + 992, // Index = 54, Token = 15 + 864, // Index = 55, Token = 0 + 864, // Index = 55, Token = 1 + 864, // Index = 55, Token = 2 + 864, // Index = 55, Token = 3 + 912, // Index = 55, Token = 4 + 944, // Index = 55, Token = 5 + 976, // Index = 55, Token = 6 + 1008, // Index = 55, Token = 7 + 864, // Index = 55, Token = 8 + 864, // Index = 55, Token = 9 + 864, // Index = 55, Token = 10 + 864, // Index = 55, Token = 11 + 912, // Index = 55, Token = 12 + 944, // Index = 55, Token = 13 + 976, // Index = 55, Token = 14 + 1008, // Index = 55, Token = 15 + 880, // Index = 56, Token = 0 + 880, // Index = 56, Token = 1 + 880, // Index = 56, Token = 2 + 880, // Index = 56, Token = 3 + 928, // Index = 56, Token = 4 + 960, // Index = 56, Token = 5 + 992, // Index = 56, Token = 6 + 1024, // Index = 56, Token = 7 + 880, // Index = 56, Token = 8 + 880, // Index = 56, Token = 9 + 880, // Index = 56, Token = 10 + 880, // Index = 56, Token = 11 + 928, // Index = 56, Token = 12 + 960, // Index = 56, Token = 13 + 992, // Index = 56, Token = 14 + 1024, // Index = 56, Token = 15 + 896, // Index = 57, Token = 0 + 896, // Index = 57, Token = 1 + 896, // Index = 57, Token = 2 + 896, // Index = 57, Token = 3 + 944, // Index = 57, Token = 4 + 976, // Index = 57, Token = 5 + 1008, // Index = 57, Token = 6 + 1040, // Index = 57, Token = 7 + 896, // Index = 57, Token = 8 + 896, // Index = 57, Token = 9 + 896, // Index = 57, Token = 10 + 896, // Index = 57, Token = 11 + 944, // Index = 57, Token = 12 + 976, // Index = 57, Token = 13 + 1008, // Index = 57, Token = 14 + 1040, // Index = 57, Token = 15 + 912, // Index = 58, Token = 0 + 912, // Index = 58, Token = 1 + 912, // Index = 58, Token = 2 + 912, // Index = 58, Token = 3 + 960, // Index = 58, Token = 4 + 992, // Index = 58, Token = 5 + 1024, // Index = 58, Token = 6 + 1056, // Index = 58, Token = 7 + 912, // Index = 58, Token = 8 + 912, // Index = 58, Token = 9 + 912, // Index = 58, Token = 10 + 912, // Index = 58, Token = 11 + 960, // Index = 58, Token = 12 + 992, // Index = 58, Token = 13 + 1024, // Index = 58, Token = 14 + 1056, // Index = 58, Token = 15 + 928, // Index = 59, Token = 0 + 928, // Index = 59, Token = 1 + 928, // Index = 59, Token = 2 + 928, // Index = 59, Token = 3 + 976, // Index = 59, Token = 4 + 1008, // Index = 59, Token = 5 + 1040, // Index = 59, Token = 6 + 1072, // Index = 59, Token = 7 + 928, // Index = 59, Token = 8 + 928, // Index = 59, Token = 9 + 928, // Index = 59, Token = 10 + 928, // Index = 59, Token = 11 + 976, // Index = 59, Token = 12 + 1008, // Index = 59, Token = 13 + 1040, // Index = 59, Token = 14 + 1072, // Index = 59, Token = 15 + 944, // Index = 60, Token = 0 + 944, // Index = 60, Token = 1 + 944, // Index = 60, Token = 2 + 944, // Index = 60, Token = 3 + 992, // Index = 60, Token = 4 + 1024, // Index = 60, Token = 5 + 1056, // Index = 60, Token = 6 + 1088, // Index = 60, Token = 7 + 944, // Index = 60, Token = 8 + 944, // Index = 60, Token = 9 + 944, // Index = 60, Token = 10 + 944, // Index = 60, Token = 11 + 992, // Index = 60, Token = 12 + 1024, // Index = 60, Token = 13 + 1056, // Index = 60, Token = 14 + 1088, // Index = 60, Token = 15 + 960, // Index = 61, Token = 0 + 960, // Index = 61, Token = 1 + 960, // Index = 61, Token = 2 + 960, // Index = 61, Token = 3 + 1008, // Index = 61, Token = 4 + 1040, // Index = 61, Token = 5 + 1072, // Index = 61, Token = 6 + 1104, // Index = 61, Token = 7 + 960, // Index = 61, Token = 8 + 960, // Index = 61, Token = 9 + 960, // Index = 61, Token = 10 + 960, // Index = 61, Token = 11 + 1008, // Index = 61, Token = 12 + 1040, // Index = 61, Token = 13 + 1072, // Index = 61, Token = 14 + 1104, // Index = 61, Token = 15 + 976, // Index = 62, Token = 0 + 976, // Index = 62, Token = 1 + 976, // Index = 62, Token = 2 + 976, // Index = 62, Token = 3 + 1024, // Index = 62, Token = 4 + 1056, // Index = 62, Token = 5 + 1088, // Index = 62, Token = 6 + 1120, // Index = 62, Token = 7 + 976, // Index = 62, Token = 8 + 976, // Index = 62, Token = 9 + 976, // Index = 62, Token = 10 + 976, // Index = 62, Token = 11 + 1024, // Index = 62, Token = 12 + 1056, // Index = 62, Token = 13 + 1088, // Index = 62, Token = 14 + 1120, // Index = 62, Token = 15 + 992, // Index = 63, Token = 0 + 992, // Index = 63, Token = 1 + 992, // Index = 63, Token = 2 + 992, // Index = 63, Token = 3 + 1040, // Index = 63, Token = 4 + 1072, // Index = 63, Token = 5 + 1104, // Index = 63, Token = 6 + 1136, // Index = 63, Token = 7 + 992, // Index = 63, Token = 8 + 992, // Index = 63, Token = 9 + 992, // Index = 63, Token = 10 + 992, // Index = 63, Token = 11 + 1040, // Index = 63, Token = 12 + 1072, // Index = 63, Token = 13 + 1104, // Index = 63, Token = 14 + 1136, // Index = 63, Token = 15 + 1008, // Index = 64, Token = 0 + 1008, // Index = 64, Token = 1 + 1008, // Index = 64, Token = 2 + 1008, // Index = 64, Token = 3 + 1056, // Index = 64, Token = 4 + 1088, // Index = 64, Token = 5 + 1120, // Index = 64, Token = 6 + 1152, // Index = 64, Token = 7 + 1008, // Index = 64, Token = 8 + 1008, // Index = 64, Token = 9 + 1008, // Index = 64, Token = 10 + 1008, // Index = 64, Token = 11 + 1056, // Index = 64, Token = 12 + 1088, // Index = 64, Token = 13 + 1120, // Index = 64, Token = 14 + 1152, // Index = 64, Token = 15 + 1024, // Index = 65, Token = 0 + 1024, // Index = 65, Token = 1 + 1024, // Index = 65, Token = 2 + 1024, // Index = 65, Token = 3 + 1072, // Index = 65, Token = 4 + 1104, // Index = 65, Token = 5 + 1136, // Index = 65, Token = 6 + 1168, // Index = 65, Token = 7 + 1024, // Index = 65, Token = 8 + 1024, // Index = 65, Token = 9 + 1024, // Index = 65, Token = 10 + 1024, // Index = 65, Token = 11 + 1072, // Index = 65, Token = 12 + 1104, // Index = 65, Token = 13 + 1136, // Index = 65, Token = 14 + 1168, // Index = 65, Token = 15 + 1040, // Index = 66, Token = 0 + 1040, // Index = 66, Token = 1 + 1040, // Index = 66, Token = 2 + 1040, // Index = 66, Token = 3 + 1088, // Index = 66, Token = 4 + 1120, // Index = 66, Token = 5 + 1152, // Index = 66, Token = 6 + 1184, // Index = 66, Token = 7 + 1040, // Index = 66, Token = 8 + 1040, // Index = 66, Token = 9 + 1040, // Index = 66, Token = 10 + 1040, // Index = 66, Token = 11 + 1088, // Index = 66, Token = 12 + 1120, // Index = 66, Token = 13 + 1152, // Index = 66, Token = 14 + 1184, // Index = 66, Token = 15 + 1056, // Index = 67, Token = 0 + 1056, // Index = 67, Token = 1 + 1056, // Index = 67, Token = 2 + 1056, // Index = 67, Token = 3 + 1104, // Index = 67, Token = 4 + 1136, // Index = 67, Token = 5 + 1168, // Index = 67, Token = 6 + 1200, // Index = 67, Token = 7 + 1056, // Index = 67, Token = 8 + 1056, // Index = 67, Token = 9 + 1056, // Index = 67, Token = 10 + 1056, // Index = 67, Token = 11 + 1104, // Index = 67, Token = 12 + 1136, // Index = 67, Token = 13 + 1168, // Index = 67, Token = 14 + 1200, // Index = 67, Token = 15 + 1072, // Index = 68, Token = 0 + 1072, // Index = 68, Token = 1 + 1072, // Index = 68, Token = 2 + 1072, // Index = 68, Token = 3 + 1120, // Index = 68, Token = 4 + 1152, // Index = 68, Token = 5 + 1184, // Index = 68, Token = 6 + 1216, // Index = 68, Token = 7 + 1072, // Index = 68, Token = 8 + 1072, // Index = 68, Token = 9 + 1072, // Index = 68, Token = 10 + 1072, // Index = 68, Token = 11 + 1120, // Index = 68, Token = 12 + 1152, // Index = 68, Token = 13 + 1184, // Index = 68, Token = 14 + 1216, // Index = 68, Token = 15 + 1088, // Index = 69, Token = 0 + 1088, // Index = 69, Token = 1 + 1088, // Index = 69, Token = 2 + 1088, // Index = 69, Token = 3 + 1136, // Index = 69, Token = 4 + 1168, // Index = 69, Token = 5 + 1200, // Index = 69, Token = 6 + 1232, // Index = 69, Token = 7 + 1088, // Index = 69, Token = 8 + 1088, // Index = 69, Token = 9 + 1088, // Index = 69, Token = 10 + 1088, // Index = 69, Token = 11 + 1136, // Index = 69, Token = 12 + 1168, // Index = 69, Token = 13 + 1200, // Index = 69, Token = 14 + 1232, // Index = 69, Token = 15 + 1104, // Index = 70, Token = 0 + 1104, // Index = 70, Token = 1 + 1104, // Index = 70, Token = 2 + 1104, // Index = 70, Token = 3 + 1152, // Index = 70, Token = 4 + 1184, // Index = 70, Token = 5 + 1216, // Index = 70, Token = 6 + 1248, // Index = 70, Token = 7 + 1104, // Index = 70, Token = 8 + 1104, // Index = 70, Token = 9 + 1104, // Index = 70, Token = 10 + 1104, // Index = 70, Token = 11 + 1152, // Index = 70, Token = 12 + 1184, // Index = 70, Token = 13 + 1216, // Index = 70, Token = 14 + 1248, // Index = 70, Token = 15 + 1120, // Index = 71, Token = 0 + 1120, // Index = 71, Token = 1 + 1120, // Index = 71, Token = 2 + 1120, // Index = 71, Token = 3 + 1168, // Index = 71, Token = 4 + 1200, // Index = 71, Token = 5 + 1232, // Index = 71, Token = 6 + 1264, // Index = 71, Token = 7 + 1120, // Index = 71, Token = 8 + 1120, // Index = 71, Token = 9 + 1120, // Index = 71, Token = 10 + 1120, // Index = 71, Token = 11 + 1168, // Index = 71, Token = 12 + 1200, // Index = 71, Token = 13 + 1232, // Index = 71, Token = 14 + 1264, // Index = 71, Token = 15 + 1136, // Index = 72, Token = 0 + 1136, // Index = 72, Token = 1 + 1136, // Index = 72, Token = 2 + 1136, // Index = 72, Token = 3 + 1184, // Index = 72, Token = 4 + 1216, // Index = 72, Token = 5 + 1248, // Index = 72, Token = 6 + 1280, // Index = 72, Token = 7 + 1136, // Index = 72, Token = 8 + 1136, // Index = 72, Token = 9 + 1136, // Index = 72, Token = 10 + 1136, // Index = 72, Token = 11 + 1184, // Index = 72, Token = 12 + 1216, // Index = 72, Token = 13 + 1248, // Index = 72, Token = 14 + 1280, // Index = 72, Token = 15 + 1152, // Index = 73, Token = 0 + 1152, // Index = 73, Token = 1 + 1152, // Index = 73, Token = 2 + 1152, // Index = 73, Token = 3 + 1200, // Index = 73, Token = 4 + 1232, // Index = 73, Token = 5 + 1264, // Index = 73, Token = 6 + 1296, // Index = 73, Token = 7 + 1152, // Index = 73, Token = 8 + 1152, // Index = 73, Token = 9 + 1152, // Index = 73, Token = 10 + 1152, // Index = 73, Token = 11 + 1200, // Index = 73, Token = 12 + 1232, // Index = 73, Token = 13 + 1264, // Index = 73, Token = 14 + 1296, // Index = 73, Token = 15 + 1168, // Index = 74, Token = 0 + 1168, // Index = 74, Token = 1 + 1168, // Index = 74, Token = 2 + 1168, // Index = 74, Token = 3 + 1216, // Index = 74, Token = 4 + 1248, // Index = 74, Token = 5 + 1280, // Index = 74, Token = 6 + 1312, // Index = 74, Token = 7 + 1168, // Index = 74, Token = 8 + 1168, // Index = 74, Token = 9 + 1168, // Index = 74, Token = 10 + 1168, // Index = 74, Token = 11 + 1216, // Index = 74, Token = 12 + 1248, // Index = 74, Token = 13 + 1280, // Index = 74, Token = 14 + 1312, // Index = 74, Token = 15 + 1184, // Index = 75, Token = 0 + 1184, // Index = 75, Token = 1 + 1184, // Index = 75, Token = 2 + 1184, // Index = 75, Token = 3 + 1232, // Index = 75, Token = 4 + 1264, // Index = 75, Token = 5 + 1296, // Index = 75, Token = 6 + 1328, // Index = 75, Token = 7 + 1184, // Index = 75, Token = 8 + 1184, // Index = 75, Token = 9 + 1184, // Index = 75, Token = 10 + 1184, // Index = 75, Token = 11 + 1232, // Index = 75, Token = 12 + 1264, // Index = 75, Token = 13 + 1296, // Index = 75, Token = 14 + 1328, // Index = 75, Token = 15 + 1200, // Index = 76, Token = 0 + 1200, // Index = 76, Token = 1 + 1200, // Index = 76, Token = 2 + 1200, // Index = 76, Token = 3 + 1248, // Index = 76, Token = 4 + 1280, // Index = 76, Token = 5 + 1312, // Index = 76, Token = 6 + 1344, // Index = 76, Token = 7 + 1200, // Index = 76, Token = 8 + 1200, // Index = 76, Token = 9 + 1200, // Index = 76, Token = 10 + 1200, // Index = 76, Token = 11 + 1248, // Index = 76, Token = 12 + 1280, // Index = 76, Token = 13 + 1312, // Index = 76, Token = 14 + 1344, // Index = 76, Token = 15 + 1216, // Index = 77, Token = 0 + 1216, // Index = 77, Token = 1 + 1216, // Index = 77, Token = 2 + 1216, // Index = 77, Token = 3 + 1264, // Index = 77, Token = 4 + 1296, // Index = 77, Token = 5 + 1328, // Index = 77, Token = 6 + 1360, // Index = 77, Token = 7 + 1216, // Index = 77, Token = 8 + 1216, // Index = 77, Token = 9 + 1216, // Index = 77, Token = 10 + 1216, // Index = 77, Token = 11 + 1264, // Index = 77, Token = 12 + 1296, // Index = 77, Token = 13 + 1328, // Index = 77, Token = 14 + 1360, // Index = 77, Token = 15 + 1232, // Index = 78, Token = 0 + 1232, // Index = 78, Token = 1 + 1232, // Index = 78, Token = 2 + 1232, // Index = 78, Token = 3 + 1280, // Index = 78, Token = 4 + 1312, // Index = 78, Token = 5 + 1344, // Index = 78, Token = 6 + 1376, // Index = 78, Token = 7 + 1232, // Index = 78, Token = 8 + 1232, // Index = 78, Token = 9 + 1232, // Index = 78, Token = 10 + 1232, // Index = 78, Token = 11 + 1280, // Index = 78, Token = 12 + 1312, // Index = 78, Token = 13 + 1344, // Index = 78, Token = 14 + 1376, // Index = 78, Token = 15 + 1248, // Index = 79, Token = 0 + 1248, // Index = 79, Token = 1 + 1248, // Index = 79, Token = 2 + 1248, // Index = 79, Token = 3 + 1296, // Index = 79, Token = 4 + 1328, // Index = 79, Token = 5 + 1360, // Index = 79, Token = 6 + 1392, // Index = 79, Token = 7 + 1248, // Index = 79, Token = 8 + 1248, // Index = 79, Token = 9 + 1248, // Index = 79, Token = 10 + 1248, // Index = 79, Token = 11 + 1296, // Index = 79, Token = 12 + 1328, // Index = 79, Token = 13 + 1360, // Index = 79, Token = 14 + 1392, // Index = 79, Token = 15 + 1264, // Index = 80, Token = 0 + 1264, // Index = 80, Token = 1 + 1264, // Index = 80, Token = 2 + 1264, // Index = 80, Token = 3 + 1312, // Index = 80, Token = 4 + 1344, // Index = 80, Token = 5 + 1376, // Index = 80, Token = 6 + 1408, // Index = 80, Token = 7 + 1264, // Index = 80, Token = 8 + 1264, // Index = 80, Token = 9 + 1264, // Index = 80, Token = 10 + 1264, // Index = 80, Token = 11 + 1312, // Index = 80, Token = 12 + 1344, // Index = 80, Token = 13 + 1376, // Index = 80, Token = 14 + 1408, // Index = 80, Token = 15 + 1280, // Index = 81, Token = 0 + 1280, // Index = 81, Token = 1 + 1280, // Index = 81, Token = 2 + 1280, // Index = 81, Token = 3 + 1328, // Index = 81, Token = 4 + 1360, // Index = 81, Token = 5 + 1392, // Index = 81, Token = 6 + 1408, // Index = 81, Token = 7 + 1280, // Index = 81, Token = 8 + 1280, // Index = 81, Token = 9 + 1280, // Index = 81, Token = 10 + 1280, // Index = 81, Token = 11 + 1328, // Index = 81, Token = 12 + 1360, // Index = 81, Token = 13 + 1392, // Index = 81, Token = 14 + 1408, // Index = 81, Token = 15 + 1296, // Index = 82, Token = 0 + 1296, // Index = 82, Token = 1 + 1296, // Index = 82, Token = 2 + 1296, // Index = 82, Token = 3 + 1344, // Index = 82, Token = 4 + 1376, // Index = 82, Token = 5 + 1408, // Index = 82, Token = 6 + 1408, // Index = 82, Token = 7 + 1296, // Index = 82, Token = 8 + 1296, // Index = 82, Token = 9 + 1296, // Index = 82, Token = 10 + 1296, // Index = 82, Token = 11 + 1344, // Index = 82, Token = 12 + 1376, // Index = 82, Token = 13 + 1408, // Index = 82, Token = 14 + 1408, // Index = 82, Token = 15 + 1312, // Index = 83, Token = 0 + 1312, // Index = 83, Token = 1 + 1312, // Index = 83, Token = 2 + 1312, // Index = 83, Token = 3 + 1360, // Index = 83, Token = 4 + 1392, // Index = 83, Token = 5 + 1408, // Index = 83, Token = 6 + 1408, // Index = 83, Token = 7 + 1312, // Index = 83, Token = 8 + 1312, // Index = 83, Token = 9 + 1312, // Index = 83, Token = 10 + 1312, // Index = 83, Token = 11 + 1360, // Index = 83, Token = 12 + 1392, // Index = 83, Token = 13 + 1408, // Index = 83, Token = 14 + 1408, // Index = 83, Token = 15 + 1328, // Index = 84, Token = 0 + 1328, // Index = 84, Token = 1 + 1328, // Index = 84, Token = 2 + 1328, // Index = 84, Token = 3 + 1376, // Index = 84, Token = 4 + 1408, // Index = 84, Token = 5 + 1408, // Index = 84, Token = 6 + 1408, // Index = 84, Token = 7 + 1328, // Index = 84, Token = 8 + 1328, // Index = 84, Token = 9 + 1328, // Index = 84, Token = 10 + 1328, // Index = 84, Token = 11 + 1376, // Index = 84, Token = 12 + 1408, // Index = 84, Token = 13 + 1408, // Index = 84, Token = 14 + 1408, // Index = 84, Token = 15 + 1344, // Index = 85, Token = 0 + 1344, // Index = 85, Token = 1 + 1344, // Index = 85, Token = 2 + 1344, // Index = 85, Token = 3 + 1392, // Index = 85, Token = 4 + 1408, // Index = 85, Token = 5 + 1408, // Index = 85, Token = 6 + 1408, // Index = 85, Token = 7 + 1344, // Index = 85, Token = 8 + 1344, // Index = 85, Token = 9 + 1344, // Index = 85, Token = 10 + 1344, // Index = 85, Token = 11 + 1392, // Index = 85, Token = 12 + 1408, // Index = 85, Token = 13 + 1408, // Index = 85, Token = 14 + 1408, // Index = 85, Token = 15 + 1360, // Index = 86, Token = 0 + 1360, // Index = 86, Token = 1 + 1360, // Index = 86, Token = 2 + 1360, // Index = 86, Token = 3 + 1408, // Index = 86, Token = 4 + 1408, // Index = 86, Token = 5 + 1408, // Index = 86, Token = 6 + 1408, // Index = 86, Token = 7 + 1360, // Index = 86, Token = 8 + 1360, // Index = 86, Token = 9 + 1360, // Index = 86, Token = 10 + 1360, // Index = 86, Token = 11 + 1408, // Index = 86, Token = 12 + 1408, // Index = 86, Token = 13 + 1408, // Index = 86, Token = 14 + 1408, // Index = 86, Token = 15 + 1376, // Index = 87, Token = 0 + 1376, // Index = 87, Token = 1 + 1376, // Index = 87, Token = 2 + 1376, // Index = 87, Token = 3 + 1408, // Index = 87, Token = 4 + 1408, // Index = 87, Token = 5 + 1408, // Index = 87, Token = 6 + 1408, // Index = 87, Token = 7 + 1376, // Index = 87, Token = 8 + 1376, // Index = 87, Token = 9 + 1376, // Index = 87, Token = 10 + 1376, // Index = 87, Token = 11 + 1408, // Index = 87, Token = 12 + 1408, // Index = 87, Token = 13 + 1408, // Index = 87, Token = 14 + 1408, // Index = 87, Token = 15 + 1392, // Index = 88, Token = 0 + 1392, // Index = 88, Token = 1 + 1392, // Index = 88, Token = 2 + 1392, // Index = 88, Token = 3 + 1408, // Index = 88, Token = 4 + 1408, // Index = 88, Token = 5 + 1408, // Index = 88, Token = 6 + 1408, // Index = 88, Token = 7 + 1392, // Index = 88, Token = 8 + 1392, // Index = 88, Token = 9 + 1392, // Index = 88, Token = 10 + 1392, // Index = 88, Token = 11 + 1408, // Index = 88, Token = 12 + 1408, // Index = 88, Token = 13 + 1408, // Index = 88, Token = 14 + 1408 // Index = 88, Token = 15 +}; + diff --git a/REDALERT/JSHELL.CPP b/REDALERT/JSHELL.CPP new file mode 100644 index 000000000..9254ec382 --- /dev/null +++ b/REDALERT/JSHELL.CPP @@ -0,0 +1,659 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/JSHELL.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 2, 1994 * + * * + * Last Update : May 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Build_Translucent_Table -- Creates a translucent control table. * + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * Fatal -- General purpose fatal error handler. * + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * Set_Window -- Sets the window dimensions to that specified. * + * Small_Icon -- Create a small icon from a big one. * + * Translucent_Table_Size -- Determines the size of a translucent table. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wwfile.h" + + +/*********************************************************************************************** + * Small_Icon -- Create a small icon from a big one. * + * * + * This routine will extract the specified icon from the icon data file and convert that * + * icon into a small (3x3) representation. Typical use of this mini-icon is for the radar * + * map. * + * * + * INPUT: iconptr -- Pointer to the icon data file. * + * * + * iconnum -- The embedded icon number to convert into a small image. * + * * + * OUTPUT: Returns with a pointer to the small icon imagery. This is exactly 9 bytes long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + *=============================================================================================*/ +void * Small_Icon(void const * iconptr, int iconnum) +{ + static unsigned char _icon[9]; + IControl_Type const * iptr = (IControl_Type const *)iconptr; + unsigned char * data; + + if (iconptr) { + iconnum = ((char *)((char *)iptr + iptr->Map))[iconnum]; + data = &((unsigned char *)((unsigned char *)iptr + iptr->Icons))[iconnum*(24*24)]; +// data = &iptr->Icons[iconnum*(24*24)]; + + for (int index = 0; index < 9; index++) { + int _offsets[9] = { + 4+4*24, + 12+4*24, + 20+4*24, + 4+12*24, + 12+12*24, + 20+12*24, + 4+20*24, + 12+20*24, + 20+20*24 + }; + _icon[index] = data[_offsets[index]]; + } + } + + return(_icon); +} + + +/*********************************************************************************************** + * Set_Window -- Sets the window dimensions to that specified. * + * * + * Use this routine to set the windows dimensions to the coordinates and dimensions * + * specified. * + * * + * INPUT: x -- Window X pixel position. * + * * + * y -- Window Y pixel position. * + * * + * w -- Window width in pixels. * + * * + * h -- Window height in pixels. * + * * + * OUTPUT: none * + * * + * WARNINGS: The X and width values are truncated to an even 8 pixel boundary. This is * + * the same as stripping off the lower 3 bits. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void Set_Window(int window, int x, int y, int w, int h) +{ + WindowList[window][WINDOWWIDTH] = w; + WindowList[window][WINDOWHEIGHT] = h; + WindowList[window][WINDOWX] = x; + WindowList[window][WINDOWY] = y; +} + + +/*********************************************************************************************** + * Fatal -- General purpose fatal error handler. * + * * + * This is a very simple general purpose fatal error handler. It goes directly to text * + * mode, prints the error, and then aborts with a failure code. * + * * + * INPUT: message -- The text message to display. * + * * + * ... -- Any optional parameters that are used in formatting the message. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine never returns. The game exits immediately. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void Fatal(char const * message, ...) +{ + va_list va; + + va_start(va, message); + Prog_End(message, true); + vfprintf(stderr, message, va); + Mono_Printf(message); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } +} + + +#ifdef NEVER +void File_Fatal(char const * message) +{ + //Prog_End(); + perror(message); + Emergency_Exit(EXIT_FAILURE); +} +#endif + + + +/*********************************************************************************************** + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * * + * This is the C++ counterpart to the Load_Uncompress function. It will load the file * + * specified into the graphic buffer indicated and uncompress it. * + * * + * INPUT: file -- The file to load and uncompress. * + * * + * uncomp_buff -- The graphic buffer that initial loading will use. * + * * + * dest_buff -- The buffer that will hold the uncompressed data. * + * * + * reserved_data -- This is an optional pointer to a buffer that will hold any * + * reserved data the compressed file may contain. This is * + * typically a palette. * + * * + * OUTPUT: Returns with the size of the uncompressed data in the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void * reserved_data) +{ + unsigned short size; + void * sptr = uncomp_buff.Get_Buffer(); + void * dptr = dest_buff.Get_Buffer(); + int opened = false; + CompHeaderType header; + + /* + ** The file must be opened in order to be read from. If the file + ** isn't opened, then open it. Record this fact so that it can be + ** restored to its closed state at the end. + */ + if (!file.Is_Open()) { + if (!file.Open()) { + return(0); + } + opened = true; + } + + /* + ** Read in the size of the file (supposedly). + */ + file.Read(&size, sizeof(size)); + + /* + ** Read in the header block. This block contains the compression type + ** and skip data (among other things). + */ + file.Read(&header, sizeof(header)); + size -= sizeof(header); + + /* + ** If there are skip bytes then they must be processed. Either read + ** them into the buffer provided or skip past them. No check is made + ** to ensure that the reserved data buffer is big enough (watch out!). + */ + if (header.Skip) { + size -= header.Skip; + if (reserved_data) { + file.Read(reserved_data, header.Skip); + } else { + file.Seek(header.Skip, SEEK_CUR); + } + header.Skip = 0; + } + + /* + ** Determine where is the proper place to load the data. If both buffers + ** specified are identical, then the data should be loaded at the end of + ** the buffer and decompressed at the beginning. + */ + if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) { + sptr = (char *)sptr + uncomp_buff.Get_Size()-(size+sizeof(header)); + } + + /* + ** Read in the bulk of the data. + */ + Mem_Copy(&header, sptr, sizeof(header)); + file.Read((char *)sptr + sizeof(header), size); + + /* + ** Decompress the data. + */ + size = (unsigned int) Uncompress_Data(sptr, dptr); + + /* + ** Close the file if necessary. + */ + if (opened) { + file.Close(); + } + return((long)size); +} + + +int Load_Picture(char const * filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char * palette, PicturePlaneType ) +{ + return(Load_Uncompress(CCFileClass(filename), scratchbuf, destbuf, palette ) / 8000); +} + + +/*********************************************************************************************** + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * * + * This is the C++ replacement for the Load_Alloc_Data function. It will allocate the * + * memory big enough to hold the file and then read the file into it. * + * * + * INPUT: file -- The file to read. * + * * + * mem -- The memory system to use for allocation. * + * * + * OUTPUT: Returns with a pointer to the allocated and filled memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void * Load_Alloc_Data(FileClass &file) +{ + void * ptr = 0; + long size = file.Size(); + + ptr = new char [size]; + if (ptr) { + file.Read(ptr, size); + } + return(ptr); +} + + +/*********************************************************************************************** + * Translucent_Table_Size -- Determines the size of a translucent table. * + * * + * Use this routine to determine how big the translucent table needs * + * to be given the specified number of colors. This value is typically * + * used when allocating the buffer for the translucent table. * + * * + * INPUT: count -- The number of colors that are translucent. * + * * + * OUTPUT: Returns the size of the translucent table. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +long Translucent_Table_Size(int count) +{ + return(256L + (256L * count)); +} + + +/*********************************************************************************************** + * Build_Translucent_Table -- Creates a translucent control table. * + * * + * The table created by this routine is used by Draw_Shape (GHOST) to * + * achieve a translucent affect. The original color of the shape will * + * show through. This differs from the fading effect, since that * + * affect only alters the background color toward a single destination * + * color. * + * * + * INPUT: palette -- Pointer to the control palette. * + * * + * control -- Pointer to array of structures that control how * + * the translucent table will be built. * + * * + * count -- The number of entries in the control array. * + * * + * buffer -- Pointer to buffer to place the translucent table. * + * If NULL is passed in, then the buffer will be * + * allocated. * + * * + * OUTPUT: Returns with pointer to the translucent table. * + * * + * WARNINGS: This routine is exceedingly slow. Use sparingly. * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +void * Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control/* && palette*/) { // palette can't be NULL... ST - 5/9/2019 + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Build_Fading_Table(palette.Get_Data(), (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +/*********************************************************************************************** + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * * + * This routine will build a translucent (fading) table to remap colors into the shadow * + * color region of the palette. Shadow colors are not affected by this translucent table. * + * This means that a shape can be overlapped any number of times and the imagery will * + * remain deterministic (and constant). * + * * + * INPUT: palette -- Pointer to the palette to base the translucent process on. * + * * + * control -- Pointer to special control structure that specifies the * + * target color, and percentage of fade. * + * * + * count -- The number of colors to be remapped (entries in the control array). * + * * + * buffer -- Pointer to the staging buffer that will hold the translucent table * + * data. If this parameter is NULL, then an appropriate sized table * + * will be allocated. * + * * + * OUTPUT: Returns with a pointer to the translucent table data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +void * Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + + if (count && control) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (int index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + *ptr++ = palette.Closest_Color(trycolor); + } + } + return(dest); +} + + +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; +// HSVClass desthsv = palette[color]; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** If this color should not be remapped, then it will be stored as a remap + ** to itself. This is effectively no remap. + */ + if (index > PaletteClass::COLOR_COUNT-16 || index == 0) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + int best = -1; + int bvalue = 0; + for (int id = PaletteClass::COLOR_COUNT-16; id < PaletteClass::COLOR_COUNT-1; id++) { + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + *ptr++ = best; + } + } + } + return(dest); +} + + +#ifdef OBSOLETE +//int Desired_Facing8(int x1, int y1, int x2, int y2) +DirType xDesired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +//int Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +DirType xDesired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} +#endif diff --git a/REDALERT/JSHELL.H b/REDALERT/JSHELL.H new file mode 100644 index 000000000..b215b52ca --- /dev/null +++ b/REDALERT/JSHELL.H @@ -0,0 +1,478 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/JSHELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef JSHELL_H +#define JSHELL_H + + +#include + +#ifdef WIN32 +//#define getch Get_Key_Num +//#define kbhit Check_Key_Num +#include "key.h" +#else +#include +#endif + +/* +** Interface class to the keyboard. This insulates the game from library vagaries. Most +** notable being the return values are declared as "int" in the library whereas C&C +** expects it to be of KeyNumType. +*/ +#ifdef WIN32 +//#define KeyNumType int +//#define KeyASCIIType int + +//lint -esym(1725,KeyboardClass::MouseQX,KeyboardClass::MouseQY) +struct KeyboardClass : public WWKeyboardClass + +#else +struct KeyboardClass +#endif +{ + + /* + ** This flag is used to indicate whether the WW library has taken over + ** the keyboard or not. If not, then the normal console input + ** takes precedence. + */ + unsigned IsLibrary; + +#ifndef WIN32 + int &MouseQX; + int &MouseQY; + + KeyboardClass() : + IsLibrary(true), + MouseQX(::MouseQX), + MouseQY(::MouseQY) + {} + KeyNumType Get(void) {return (IsLibrary ? (KeyNumType)Get_Key_Num() : (KeyNumType)getch());}; + KeyNumType Check(void) {return (IsLibrary ? (KeyNumType)Check_Key_Num() : (KeyNumType)kbhit());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)KN_To_KA(key));}; + void Clear(void) {if (IsLibrary) Clear_KeyBuffer();}; + int Down(KeyNumType key) {return(Key_Down(key));}; +#else + KeyboardClass() : IsLibrary(true) {} + KeyNumType Get(void) {return ((KeyNumType)WWKeyboardClass::Get());}; + KeyNumType Check(void) {return ((KeyNumType)WWKeyboardClass::Check());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)WWKeyboardClass::To_ASCII(key));}; + void Clear(void) {WWKeyboardClass::Clear();}; + int Down(KeyNumType key) {return(WWKeyboardClass::Down(key));}; +#endif + + int Mouse_X(void) {return(Get_Mouse_X());}; + int Mouse_Y(void) {return(Get_Mouse_Y());}; +}; + + +/* +** These templates allow enumeration types to have simple bitwise +** arithmatic performed. The operators must be instatiated for the +** enumerated types desired. +*/ +template inline T operator ++(T & a) +{ + a = (T)((int)a + (int)1); + return(a); +} +template inline T operator ++(T & a, int) +{ + T aa = a; + a = (T)((int)a + (int)1); + return(aa); +} +template inline T operator --(T & a) +{ + a = (T)((int)a - (int)1); + return(a); +} +template inline T operator --(T & a, int) +{ + T aa = a; + a = (T)((int)a - (int)1); + return(aa); +} +template inline T operator |(T t1, T t2) +{ + return((T)((int)t1 | (int)t2)); +} +template inline T operator &(T t1, T t2) +{ + return((T)((int)t1 & (int)t2)); +} +template inline T operator ~(T t1) +{ + return((T)(~(int)t1)); +} + +#ifndef WIN32 +template inline T min(T value1, T value2) +{ + if (value1 < value2) { + return(value1); + } + return(value2); +} +int min(int, int); +long min(long, long); + +template inline T max(T value1, T value2) +{ + if (value1 > value2) { + return(value1); + } + return(value2); +} +int max(int, int); +long max(long, long); +#endif + +template inline void swap(T &value1, T &value2) +{ + T temp = value1; + value1 = value2; + value2 = temp; +} +int swap(int, int); +long swap(long, long); + +template inline +T Bound(T original, T minval, T maxval) +{ + if (original < minval) return(minval); + if (original > maxval) return(maxval); + return(original); +}; +int Bound(signed int, signed int, signed int); +unsigned Bound(unsigned, unsigned, unsigned); +long Bound(long, long, long); + +template +T _rotl(T X, int n) +{ + return((T)(( ( ( X ) << n ) | ( ( X ) >> ( (sizeof(T)*8) - n ) ) ))); +} + + +/* +** This macro serves as a general way to determine the number of elements +** within an array. +*/ +#define ARRAY_LENGTH(x) int(sizeof(x)/sizeof(x[0])) +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/* +** The shape flags are likely to be "or"ed together and other such bitwise +** manipulations. These instatiated operator templates allow this. +*/ +inline ShapeFlags_Type operator |(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator &(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator ~(ShapeFlags_Type); + + +void __cdecl Set_Bit(void * array, int bit, int value); +int __cdecl Get_Bit(void const * array, int bit); +int __cdecl First_True_Bit(void const * array); +int __cdecl First_False_Bit(void const * array); +int __cdecl Bound(int original, int min, int max); + +#if (0) +void Set_Bit(void * array, int bit, int value); +#pragma aux Set_Bit parm [esi] [ecx] [eax] \ + modify [esi ebx] = \ + "mov ebx,ecx" \ + "shr ebx,5" \ + "and ecx,01Fh" \ + "btr [esi+ebx*4],ecx" \ + "or eax,eax" \ + "jz ok" \ + "bts [esi+ebx*4],ecx" \ + "ok:" + +int Get_Bit(void const * array, int bit); +#pragma aux Get_Bit parm [esi] [eax] \ + modify [esi ebx] \ + value [eax] = \ + "mov ebx,eax" \ + "shr ebx,5" \ + "and eax,01Fh" \ + "bt [esi+ebx*4],eax" \ + "setc al" + +int First_True_Bit(void const * array); +#pragma aux First_True_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +int First_False_Bit(void const * array); +#pragma aux First_False_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "not ebx" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +#ifdef OBSOLETE +extern int Bound(int original, int min, int max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jl okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "jg okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jl okmax" \ + "mov eax,ecx" \ + "okmax:" + +extern unsigned Bound(unsigned original, unsigned min, unsigned max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jb okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "ja okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jb okmax" \ + "mov eax,ecx" \ + "okmax:" +#endif + + +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +#pragma aux Fixed_To_Cardinal parm [eax] [edx] \ + modify [edx] \ + value [eax] = \ + "mul edx" \ + "add eax,080h" \ + "test eax,0FF000000h" \ + "jz ok" \ + "mov eax,000FFFFFFh" \ + "ok:" \ + "shr eax,8" + + +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +#pragma aux Cardinal_To_Fixed parm [ebx] [eax] \ + modify [edx] \ + value [eax] = \ + "or ebx,ebx" \ + "jz fini" \ + "shl eax,8" \ + "xor edx,edx" \ + "div ebx" \ + "fini:" + + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + +#endif + +/* +** Timer objects that fetch the appropriate timer value according to +** the type of timer they are. +*/ +extern long Frame; +class FrameTimerClass +{ + public: + long operator () (void) const {return(Frame);}; + operator long (void) const {return(Frame);}; +}; + + +#ifndef WIN32 +extern bool TimerSystemOn; +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); +} +//bool Init_Timer_System(unsigned int freq, int partial=false); +bool Remove_Timer_System(void); +#else +extern WinTimerClass * WindowsTimer; +#endif + +#ifndef SYSTEM_TIMER_CLASS +#define SYSTEM_TIMER_CLASS +class SystemTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + #else + long operator () (void) const {return(Get_System_Tick_Count());}; + operator long (void) const {return(Get_System_Tick_Count());}; + #endif +}; +#endif + + +class UserTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + #else + long operator () (void) const {return(Get_User_Tick_Count());}; + operator long (void) const {return(Get_User_Tick_Count());}; + #endif +}; + + +template +void Bubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (array[index] > array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (*array[index] > *array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PNBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (stricmp(array[index]->Name(), array[index+1]->Name()) > 0) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +class SmartPtr +{ + public: + SmartPtr(NoInitClass const &) {} + SmartPtr(T * realptr = 0) : Pointer(realptr) {} + SmartPtr(SmartPtr const & rvalue) : Pointer(rvalue.Pointer) {} + ~SmartPtr(void) {Pointer = 0;} + + operator T * (void) const {return(Pointer);} + + operator long (void) const {return((long)Pointer);} + + SmartPtr operator ++ (int) {assert(Pointer != 0);SmartPtr temp = *this;++Pointer;return(temp);} + SmartPtr & operator ++ (void) {assert(Pointer != 0);++Pointer;return(*this);} + SmartPtr operator -- (int) {assert(Pointer != 0);SmartPtr temp = *this;--Pointer;return(temp);} + SmartPtr & operator -- (void) {assert(Pointer != 0);--Pointer;return(*this);} + + SmartPtr & operator = (SmartPtr const & rvalue) {Pointer = rvalue.Pointer;return(*this);} + T * operator -> (void) const {assert(Pointer != 0);return(Pointer);} + T & operator * (void) const {assert(Pointer != 0);return(*Pointer);} + + private: + T * Pointer; +}; + + +#endif diff --git a/REDALERT/KEY.CPP b/REDALERT/KEY.CPP new file mode 100644 index 000000000..6442f5077 --- /dev/null +++ b/REDALERT/KEY.CPP @@ -0,0 +1,758 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/KEY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages* + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboar* + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keybo* + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "key.h" + +#include "monoc.h" + +//void Message_Loop(void); + +//WWKeyboardClass * _Kbd = NULL; + + +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) : + MouseQX(0), + MouseQY(0), + Head(0), + Tail(0) +{ +// _Kbd = this; + + memset(KeyState, '\0', sizeof(KeyState)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + + unsigned short temp = Fetch_Element(); + if (Is_Mouse_Key(temp)) { + MouseQX = Fetch_Element(); + MouseQY = Fetch_Element(); + } + return(temp); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * * + * This checks the specified key code to see if it refers to the mouse buttons. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the key a mouse button key? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Mouse_Key(unsigned short key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + * 09/24/1996 JLB : Converted to new style keyboard system. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Check(void) const +{ + ((WWKeyboardClass *)this)->Fill_Buffer_From_System(); + if (Is_Buffer_Empty()) return(false); + return(Peek_Element()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Get(void) +{ + while (!Check()) {} // wait for key in buffer + return (Buff_Get()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put(unsigned short key) +{ + if (!Is_Buffer_Full()) { + Put_Element(key); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Key_Message(unsigned short vk_key, bool release) +{ + /* + ** Get the status of all of the different keyboard modifiers. Note, only pay attention + ** to numlock and caps lock if we are dealing with a key that is affected by them. Note + ** that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + ** would be incompatible with the dos version. + */ + if (!Is_Mouse_Key(vk_key)) { + if (((GetKeyState(VK_SHIFT) & 0x8000) != 0) || + ((GetKeyState(VK_CAPITAL) & 0x0008) != 0) || + ((GetKeyState(VK_NUMLOCK) & 0x0008) != 0)) { + + vk_key |= WWKEY_SHIFT_BIT; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + vk_key |= WWKEY_CTRL_BIT; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + vk_key |= WWKEY_ALT_BIT; + } + } + + if (release) { + vk_key |= WWKEY_RLS_BIT; + } + + /* + ** Finally use the put command to enter the key into the keyboard + ** system. + */ + return(Put(vk_key)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer. * + * * + * This routine will store the mouse type event into the keyboard buffer. It also checks * + * to ensure that there is enough room in the buffer so that partial mouse events won't * + * be recorded. * + * * + * INPUT: vk_key -- The mouse key message itself. * + * * + * x,y -- The mouse coordinates at the time of the event. * + * * + * release -- Is this a mouse button release? * + * * + * OUTPUT: bool; Was the event stored sucessfully into the keyboard buffer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release) +{ + if (Available_Buffer_Room() >= 3 && Is_Mouse_Key(vk_key)) { + Put_Key_Message(vk_key, release); + Put((unsigned short)x); + Put((unsigned short)y); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * * + * This routine will convert the key code specified into an ASCII value. This takes into * + * consideration the language and keyboard mapping of the host Windows system. * + * * + * INPUT: key -- The key code to convert into ASCII. * + * * + * OUTPUT: Returns with the key converted into ASCII. If the key has no ASCII equivalent, * + * then '\0' is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +char WWKeyboardClass::To_ASCII(unsigned short key) +{ + /* + ** Released keys never translate into an ASCII value. + */ + if (key & WWKEY_RLS_BIT) { + return('\0'); + } + + /* + ** Set the KeyState buffer to reflect the shift bits stored in the key value. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0x80; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0x80; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0x80; + } + + /* + ** Ask windows to translate the key into an ASCII equivalent. + */ + char buffer[10]; + int result = 1; + int scancode = 0; + + scancode = MapVirtualKey(key & 0xFF, 0); + result = ToAscii((UINT)(key & 0xFF), (UINT)scancode, (PBYTE)KeyState, (LPWORD)buffer, (UINT)0); + + /* + ** Restore the KeyState buffer back to pristine condition. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0; + } + + /* + ** If Windows could not perform the translation as expected, then + ** return with a null ASCII value. + */ + if (result != 1) { + return('\0'); + } + + return(buffer[0]); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * * + * This routine will examine the key specified to see if it is currently being held down. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the specified key currently being held down? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Down(unsigned short key) +{ + return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * * + * This routine will extract the next pending element in the keyboard queue. If there is * + * no element available, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the element extracted from the queue. An empty queue is signified * + * by a 0 return value. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Fetch_Element(void) +{ + unsigned short val = 0; + if (Head != Tail) { + val = Buffer[Head]; + + Head = (Head + 1) % ARRAY_SIZE(Buffer); + } + return(val); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * * + * This routine will examine and return with the next element in the keyboard buffer but * + * it will not alter or remove that element. Use this routine to see what is pending in * + * the keyboard queue. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next element in the keyboard queue. If the keyboard buffer is * + * empty, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Peek_Element(void) const +{ + if (!Is_Buffer_Empty()) { + return(Buffer[Head]); + } + return(0); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * * + * This will put one keyboard data element into the keyboard buffer. Typically, this data * + * is a key code, but it might be mouse coordinates. * + * * + * INPUT: val -- The data element to add to the keyboard buffer. * + * * + * OUTPUT: bool; Was the keyboard element added successfully? A failure would indicate that * + * the keyboard buffer is full. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Element(unsigned short val) +{ + if (!Is_Buffer_Full()) { + int temp = (Tail+1) % ARRAY_SIZE(Buffer); + Buffer[Tail] = val; + Tail = temp; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * * + * This routine will examine the keyboard buffer to determine if it is completely * + * full of queued keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer completely full? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Full(void) const +{ + if ((Tail + 1) % ARRAY_SIZE(Buffer) == Head) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * * + * This routine will examine the keyboard buffer to see if it contains no events at all. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer currently without any pending events queued? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Empty(void) const +{ + if (Head == Tail) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages. * + * * + * This routine will extract and process any windows messages in the windows message * + * queue. It is presumed that the normal message handler will call the keyboard * + * message processing function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Fill_Buffer_From_System(void) +{ + if (!Is_Buffer_Full()) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!GetMessage( &msg, NULL, 0, 0 )) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + + +/*********************************************************************************************** + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * * + * This routine will clear the keyboard buffer of all pending keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Clear(void) +{ + /* + ** Extract any windows pending keyboard message events and then clear out the keyboard + ** buffer. + */ + Fill_Buffer_From_System(); + Head = Tail; + + /* + ** Perform a second clear to handle the rare case of the keyboard buffer being full and there + ** still remains keyboard related events in the windows message queue. + */ + Fill_Buffer_From_System(); + Head = Tail; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboard * + * * + * This routine will examine the Windows message specified. If the message relates to an * + * event that the keyboard input system needs to process, then it will be processed * + * accordingly. * + * * + * INPUT: window -- Handle to the window receiving the message. * + * * + * message -- The message number of this event. * + * * + * wParam -- The windows specific word parameter (meaning depends on message). * + * * + * lParam -- The windows specific long word parameter (meaning is message dependant)* + * * + * OUTPUT: bool; Was this keyboard message recognized and processed? A 'false' return value * + * means that the message should be processed normally. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam) +{ +// ST - 5/13/2019 +#if (0) + bool processed = false; + + /* + ** Examine the message to see if it is one that should be processed. Only keyboard and + ** pertinant mouse messages are processed. + */ + switch (message) { + + /* + ** System key has been pressed. This is the normal keyboard event message. + */ + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if (wParam == VK_SCROLL) { + Stop_Execution(); + } else { + Put_Key_Message((unsigned short)wParam); + } + processed = true; + break; + + /* + ** The key has been released. This is the normal key release message. + */ + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message((unsigned short)wParam, true); + processed = true; + break; + + /* + ** Press of the left mouse button. + */ + case WM_LBUTTONDOWN: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the left mouse button. + */ + case WM_LBUTTONUP: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Double click of the left mouse button. Fake this into being + ** just a rapid click of the left button twice. + */ + case WM_LBUTTONDBLCLK: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Press of the middle mouse button. + */ + case WM_MBUTTONDOWN: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the middle mouse button. + */ + case WM_MBUTTONUP: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Middle button double click gets translated into two + ** regular middle button clicks. + */ + case WM_MBUTTONDBLCLK: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Right mouse button press. + */ + case WM_RBUTTONDOWN: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Right mouse button release. + */ + case WM_RBUTTONUP: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Translate a double click of the right button + ** into being just two regular right button clicks. + */ + case WM_RBUTTONDBLCLK: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** If the message is not pertinant to the keyboard system, + ** then do nothing. + */ + default: + break; + } + + /* + ** If this message has been processed, then pass it on to the system + ** directly. + */ + if (processed) { + DefWindowProc(window, message, wParam, lParam); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keyboa * + * * + * This examines the keyboard buffer queue and determine how many elements are available * + * for use before the buffer becomes full. Typical use of this would be when inserting * + * mouse events that require more than one element. Such an event must detect when there * + * would be insufficient room in the buffer and bail accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of elements that may be stored in to the keyboard buffer * + * before it becomes full and cannot accept any more elements. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Available_Buffer_Room(void) const +{ + int avail; + if (Head == Tail) { + avail = ARRAY_SIZE(Buffer); + } + if (Head < Tail) { + avail = Tail - Head; + } + if (Head > Tail) { + avail = (Tail + ARRAY_SIZE(Buffer)) - Head; + } + return(avail); +} diff --git a/REDALERT/KEY.H b/REDALERT/KEY.H new file mode 100644 index 000000000..5f1ac7313 --- /dev/null +++ b/REDALERT/KEY.H @@ -0,0 +1,656 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/KEY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#ifndef WIN32 +#include "..\wwflat32\include\keyboard.h" +#else + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + bool Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Put_Element(unsigned short val); + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + bool Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release = false); + int Available_Buffer_Room(void) const; + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + int Head; + int Tail; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +typedef enum KeyASCIIType { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +} KeyASCIIType; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0 = VK_0, + KN_1 = VK_1, + KN_2 = VK_2, + KN_3 = VK_3, + KN_4 = VK_4, + KN_5 = VK_5, + KN_6 = VK_6, + KN_7 = VK_7, + KN_8 = VK_8, + KN_9 = VK_9, + KN_A = VK_A, + KN_B = VK_B, + KN_BACKSLASH = VK_NONE_DC, + KN_BACKSPACE = VK_BACK, + KN_C = VK_C, + KN_CAPSLOCK = VK_CAPITAL, + KN_CENTER = VK_CLEAR, + KN_COMMA = VK_NONE_BC, + KN_D = VK_D, + KN_DELETE = VK_DELETE, + KN_DOWN = VK_DOWN, + KN_DOWNLEFT = VK_END, + KN_DOWNRIGHT = VK_NEXT, + KN_E = VK_E, + KN_END = VK_END, + KN_EQUAL = VK_NONE_BB, + KN_ESC = VK_ESCAPE, + KN_E_DELETE = VK_DELETE, + KN_E_DOWN = VK_NUMPAD2, + KN_E_END = VK_NUMPAD1, + KN_E_HOME = VK_NUMPAD7, + KN_E_INSERT = VK_INSERT, + KN_E_LEFT = VK_NUMPAD4, + KN_E_PGDN = VK_NUMPAD3, + KN_E_PGUP = VK_NUMPAD9, + KN_E_RIGHT = VK_NUMPAD6, + KN_E_UP = VK_NUMPAD8, + KN_F = VK_F, + KN_F1 = VK_F1, + KN_F10 = VK_F10, + KN_F11 = VK_F11, + KN_F12 = VK_F12, + KN_F2 = VK_F2, + KN_F3 = VK_F3, + KN_F4 = VK_F4, + KN_F5 = VK_F5, + KN_F6 = VK_F6, + KN_F7 = VK_F7, + KN_F8 = VK_F8, + KN_F9 = VK_F9, + KN_G = VK_G, + KN_GRAVE = VK_NONE_C0, + KN_H = VK_H, + KN_HOME = VK_HOME, + KN_I = VK_I, + KN_INSERT = VK_INSERT, + KN_J = VK_J, + KN_K = VK_K, + KN_KEYPAD_ASTERISK= VK_MULTIPLY, + KN_KEYPAD_MINUS = VK_SUBTRACT, + KN_KEYPAD_PLUS = VK_ADD, + KN_KEYPAD_RETURN = VK_RETURN, + KN_KEYPAD_SLASH = VK_DIVIDE, + KN_L = VK_L, + KN_LALT = VK_MENU, + KN_LBRACKET = VK_NONE_DB, + KN_LCTRL = VK_CONTROL, + KN_LEFT = VK_LEFT, + KN_LMOUSE = VK_LBUTTON, + KN_LSHIFT = VK_SHIFT, + KN_M = VK_M, + KN_MINUS = VK_NONE_BD, + KN_N = VK_N, + KN_NUMLOCK = VK_NUMLOCK, + KN_O = VK_O, + KN_P = VK_P, + KN_PAUSE = VK_PAUSE, + KN_PERIOD = VK_NONE_BE, + KN_PGDN = VK_NEXT, + KN_PGUP = VK_PRIOR, + KN_PRNTSCRN = VK_PRINT, + KN_Q = VK_Q, + KN_R = VK_R, + KN_RALT = VK_MENU, + KN_RBRACKET = VK_NONE_DD, + KN_RCTRL = VK_CONTROL, + KN_RETURN = VK_RETURN, + KN_RIGHT = VK_RIGHT, + KN_RMOUSE = VK_RBUTTON, + KN_RSHIFT = VK_SHIFT, + KN_S = VK_S, + KN_SCROLLLOCK = VK_SCROLL, + KN_SEMICOLON = VK_NONE_BA, + KN_SLASH = VK_NONE_BF, + KN_SPACE = VK_SPACE, + KN_SQUOTE = VK_NONE_DE, + KN_T = VK_T, + KN_TAB = VK_TAB, + KN_U = VK_U, + KN_UP = VK_UP, + KN_UPLEFT = VK_HOME, + KN_UPRIGHT = VK_PRIOR, + KN_V = VK_V, + KN_W = VK_W, + KN_X = VK_X, + KN_Y = VK_Y, + KN_Z = VK_Z, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +} KeyNumType; + + +//extern WWKeyboardClass *_Kbd; +#endif + +#endif diff --git a/REDALERT/KEYBOARD.CPP b/REDALERT/KEYBOARD.CPP new file mode 100644 index 000000000..116fee41b --- /dev/null +++ b/REDALERT/KEYBOARD.CPP @@ -0,0 +1,466 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.74 23 Sep 1996 12:36:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : September 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +#include "monoc.h" + +//void Message_Loop(void); + +WWKeyboardClass * _Kbd = NULL; + + +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) : + MouseQX(0), + MouseQY(0), + Head(0), + Tail(0) +// MState(0), +// Conditional(0), +// CurrentCursor(0) +{ + _Kbd = this; + + memset(KeyState, '\0', sizeof(KeyState)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + + unsigned short temp = Fetch_Element(); + if (Is_Mouse_Key(temp)) { + MouseQX = Fetch_Element(); + MouseQY = Fetch_Element(); + } + return(temp); +} + + +bool WWKeyboardClass::Is_Mouse_Key(unsigned short key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + * 09/24/1996 JLB : Converted to new style keyboard system. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Check(void) const +{ + ((WWKeyboardClass *)this)->Fill_Buffer_From_System(); + if (Is_Buffer_Empty()) return(false); + return(Peek_Element()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Get(void) +{ + while (!Check()) {} // wait for key in buffer + return (Buff_Get()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put(unsigned short key) +{ + if (!Is_Buffer_Full()) { + Put_Element(key); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Key_Message(unsigned short vk_key, bool release) +{ + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. Note + // that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + // would be incompatible with the dos version. + // + if (!Is_Mouse_Key(vk_key)) { + if (((GetKeyState(VK_SHIFT) & 0x8000) != 0) || + ((GetKeyState(VK_CAPITAL) & 0x0008) != 0) || + ((GetKeyState(VK_NUMLOCK) & 0x0008) != 0)) { + + vk_key |= WWKEY_SHIFT_BIT; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + vk_key |= WWKEY_CTRL_BIT; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + vk_key |= WWKEY_ALT_BIT; + } + } + + if (release) { + vk_key |= WWKEY_RLS_BIT; + } + + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key)); + + + +} + +#pragma warning (disable : 4065) +char WWKeyboardClass::To_ASCII(unsigned short key) +{ + /* + ** Released keys never translate into an ASCII value. + */ + if (key & WWKEY_RLS_BIT) { + return('\0'); + } + + /* + ** Set the KeyState buffer to reflect the shift bits stored in the key value. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0x80; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0x80; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0x80; + } + + /* + ** Ask windows to translate the key into an ASCII equivalent. + */ + char buffer[10]; + int result = 1; + int scancode = 0; + char override = '\0'; + + switch (key & 0xFF) { +// case KN_RETURN: +// override = KA_RETURN; +// break; + +// case KN_BACKSPACE: +// override = KA_BACKSPACE; +// break; + + default: + scancode = MapVirtualKey(key & 0xFF, 0); + result = ToAscii((UINT)(key & 0xFF), (UINT)scancode, (PBYTE)KeyState, (LPWORD)buffer, (UINT)0); + break; + } + + /* + ** Restore the KeyState buffer back to pristine condition. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0; + } + + /* + ** If Windows could not perform the translation as expected, then + ** return with a null ASCII value. + */ + if (result != 1) { + return('\0'); + } + + if (override != 0) { + return(override); + } + + return(buffer[0]); +} + + +bool WWKeyboardClass::Down(unsigned short key) +{ + return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + + + +unsigned short WWKeyboardClass::Fetch_Element(void) +{ + unsigned short val = 0; + if (Head != Tail) { + val = Buffer[Head]; + + Head = (Head + 1) % ARRAY_SIZE(Buffer); + } + return(val); +} + + +unsigned short WWKeyboardClass::Peek_Element(void) const +{ + if (!Is_Buffer_Empty()) { + return(Buffer[Head]); + } + return(0); +} + + +bool WWKeyboardClass::Put_Element(unsigned short val) +{ + if (!Is_Buffer_Full()) { + int temp = (Tail+1) % ARRAY_SIZE(Buffer); + Buffer[Tail] = val; + Tail = temp; + return(true); + } + return(false); +} + + +bool WWKeyboardClass::Is_Buffer_Full(void) const +{ + if ((Tail + 1) % ARRAY_SIZE(Buffer) == Head) { + return(true); + } + return(false); +} + + +bool WWKeyboardClass::Is_Buffer_Empty(void) const +{ + if (Head == Tail) { + return(true); + } + return(false); +} + + +void WWKeyboardClass::Fill_Buffer_From_System(void) +{ + if (!Is_Buffer_Full()) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!GetMessage( &msg, NULL, 0, 0 )) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + + +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message((unsigned short)wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message((unsigned short)wParam, true); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + +// case WM_MOUSEMOVE: +// if (CurrentCursor) +// SetCursor(CurrentCursor); +// break; + } +} diff --git a/REDALERT/KEYBOARD.H b/REDALERT/KEYBOARD.H new file mode 100644 index 000000000..33f6ddfa8 --- /dev/null +++ b/REDALERT/KEYBOARD.H @@ -0,0 +1,649 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.74 23 Sep 1996 12:36:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Put_Element(unsigned short val); + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + int Head; + int Tail; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +}; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0 = VK_0, + KN_1 = VK_1, + KN_2 = VK_2, + KN_3 = VK_3, + KN_4 = VK_4, + KN_5 = VK_5, + KN_6 = VK_6, + KN_7 = VK_7, + KN_8 = VK_8, + KN_9 = VK_9, + KN_A = VK_A, + KN_B = VK_B, + KN_BACKSLASH = VK_NONE_DC, + KN_BACKSPACE = VK_BACK, + KN_C = VK_C, + KN_CAPSLOCK = VK_CAPITAL, + KN_CENTER = VK_CLEAR, + KN_COMMA = VK_NONE_BC, + KN_D = VK_D, + KN_DELETE = VK_DELETE, + KN_DOWN = VK_DOWN, + KN_DOWNLEFT = VK_END, + KN_DOWNRIGHT = VK_NEXT, + KN_E = VK_E, + KN_END = VK_END, + KN_EQUAL = VK_NONE_BB, + KN_ESC = VK_ESCAPE, + KN_E_DELETE = VK_DELETE, + KN_E_DOWN = VK_NUMPAD2, + KN_E_END = VK_NUMPAD1, + KN_E_HOME = VK_NUMPAD7, + KN_E_INSERT = VK_INSERT, + KN_E_LEFT = VK_NUMPAD4, + KN_E_PGDN = VK_NUMPAD3, + KN_E_PGUP = VK_NUMPAD9, + KN_E_RIGHT = VK_NUMPAD6, + KN_E_UP = VK_NUMPAD8, + KN_F = VK_F, + KN_F1 = VK_F1, + KN_F10 = VK_F10, + KN_F11 = VK_F11, + KN_F12 = VK_F12, + KN_F2 = VK_F2, + KN_F3 = VK_F3, + KN_F4 = VK_F4, + KN_F5 = VK_F5, + KN_F6 = VK_F6, + KN_F7 = VK_F7, + KN_F8 = VK_F8, + KN_F9 = VK_F9, + KN_G = VK_G, + KN_GRAVE = VK_NONE_C0, + KN_H = VK_H, + KN_HOME = VK_HOME, + KN_I = VK_I, + KN_INSERT = VK_INSERT, + KN_J = VK_J, + KN_K = VK_K, + KN_KEYPAD_ASTERISK= VK_MULTIPLY, + KN_KEYPAD_MINUS = VK_SUBTRACT, + KN_KEYPAD_PLUS = VK_ADD, + KN_KEYPAD_RETURN = VK_RETURN, + KN_KEYPAD_SLASH = VK_DIVIDE, + KN_L = VK_L, + KN_LALT = VK_MENU, + KN_LBRACKET = VK_NONE_DB, + KN_LCTRL = VK_CONTROL, + KN_LEFT = VK_LEFT, + KN_LMOUSE = VK_LBUTTON, + KN_LSHIFT = VK_SHIFT, + KN_M = VK_M, + KN_MINUS = VK_NONE_BD, + KN_N = VK_N, + KN_NUMLOCK = VK_NUMLOCK, + KN_O = VK_O, + KN_P = VK_P, + KN_PAUSE = VK_PAUSE, + KN_PERIOD = VK_NONE_BE, + KN_PGDN = VK_NEXT, + KN_PGUP = VK_PRIOR, + KN_PRNTSCRN = VK_PRINT, + KN_Q = VK_Q, + KN_R = VK_R, + KN_RALT = VK_MENU, + KN_RBRACKET = VK_NONE_DD, + KN_RCTRL = VK_CONTROL, + KN_RETURN = VK_RETURN, + KN_RIGHT = VK_RIGHT, + KN_RMOUSE = VK_RBUTTON, + KN_RSHIFT = VK_SHIFT, + KN_S = VK_S, + KN_SCROLLLOCK = VK_SCROLL, + KN_SEMICOLON = VK_NONE_BA, + KN_SLASH = VK_NONE_BF, + KN_SPACE = VK_SPACE, + KN_SQUOTE = VK_NONE_DE, + KN_T = VK_T, + KN_TAB = VK_TAB, + KN_U = VK_U, + KN_UP = VK_UP, + KN_UPLEFT = VK_HOME, + KN_UPRIGHT = VK_PRIOR, + KN_V = VK_V, + KN_W = VK_W, + KN_X = VK_X, + KN_Y = VK_Y, + KN_Z = VK_Z, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +} KeyNumType; + + +extern WWKeyboardClass *_Kbd; + +#endif diff --git a/REDALERT/KEYFBUFF.ASM b/REDALERT/KEYFBUFF.ASM new file mode 100644 index 000000000..d6c3007cf --- /dev/null +++ b/REDALERT/KEYFBUFF.ASM @@ -0,0 +1,5210 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +;IDEAL +;P386 +;MODEL USE32 FLAT +;jumps + +.MODEL FLAT +;.386 + +;******************************** Includes ********************************* +;INCLUDE "gbuffer.inc" +;include "profile.inc" + +OPTIMAL_BYTE_COPY equ 14 + +GraphicViewPort STRUCT +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ENDS + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +USE MACRO func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +prologue macro +endm + + + +epilogue macro +endm + + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +.DATA + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + + + + + + extern C BigShapeBufferStart:dword + extern C UseBigShapeBuffer:dword + extern C TheaterShapeBufferStart:dword + extern C IsTheaterShape:dword + ;extern C Single_Line_Trans_Entry:near + ;extern C Next_Line:near + extern C MMX_Done:near + extern C MMXAvailable:dword + ;extern C EndNewShapeJumpTable:byte + ;extern C NewShapeJumpTable:dword + + +;********************************************************************************** +; +; Jump tables for new line header system +; +; Each line of shape data now has a header byte which describes the data on the line. +; + +; +; Header byte control bits +; +BLIT_TRANSPARENT =1 +BLIT_GHOST =2 +BLIT_FADING =4 +BLIT_PREDATOR =8 +BLIT_SKIP =16 +BLIT_ALL =BLIT_TRANSPARENT or BLIT_GHOST or BLIT_FADING or BLIT_PREDATOR or BLIT_SKIP + + +ShapeHeaderType STRUCT + + draw_flags dd ? + shape_data dd ? + shape_buffer dd ? + +ShapeHeaderType ENDS + + + +; +; Global definitions for routines that draw a single line of a shape +; + ;extern Short_Single_Line_Copy:near + ;extern Single_Line_Trans:near + ;extern Single_Line_Ghost:near + ;extern Single_Line_Ghost_Trans:near + ;extern Single_Line_Fading:near + ;extern Single_Line_Fading_Trans:near + ;extern Single_Line_Ghost_Fading:near + ;extern Single_Line_Ghost_Fading_Trans:near + ;extern Single_Line_Predator:near + ;extern Single_Line_Predator_Trans:near + ;extern Single_Line_Predator_Ghost:near + ;extern Single_Line_Predator_Ghost_Trans:near + ;extern Single_Line_Predator_Fading:near + ;extern Single_Line_Predator_Fading_Trans:near + ;extern Single_Line_Predator_Ghost_Fading:near + ;extern Single_Line_Predator_Ghost_Fading_Trans:near + ;extern Single_Line_Skip:near + + ;extern Single_Line_Single_Fade:near + ;extern Single_Line_Single_Fade_Trans:near + +;externdef AllFlagsJumpTable:dword +; +externdef NewShapeJumpTable:dword +externdef EndNewShapeJumpTable:byte +externdef CriticalFadeRedirections:dword +;externdef BufferFrameTable:dword + +;externdef CriticalFadeRedirections:dword +;externdef CriticalFadeRedirections:dword +;externdef CriticalFadeRedirections:dword +;externdef BF_Copy:far ptr +;externdef BF_Trans:dword +;externdef BF_Ghost:dword +;externdef BF_Ghost_Trans:dword +;externdef BF_Fading:dword +;externdef BF_Fading_Trans:dword +;externdef BF_Ghost_Fading:dword +;externdef BF_Ghost_Fading_Trans:dword +;externdef BF_Predator:dword +;externdef BF_Predator_Trans:dword +;externdef BF_Predator_Ghost:dword +;externdef BF_Predator_Ghost_Trans:dword +;externdef BF_Predator_Fading:dword +;externdef BF_Predator_Fading_Trans:dword +;externdef BF_Predator_Ghost_Fading:dword +;externdef BF_Predator_Ghost_Fading_Trans:dword + +externdef C Single_Line_Trans:near +externdef C Single_Line_Trans_Entry:near +externdef C Next_Line:near + + +.CODE + + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + + + +.DATA + +;NewShapeJumpTable label near ptr dword + +; +; Jumptable for shape line drawing with no flags set +; + +NewShapeJumpTable dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy +;CriticalFadeRedirections label dword +CriticalFadeRedirections dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + +; +; Jumptable for shape line drawing routines with fading, transparent and ghost flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + +; +; Jumptable for shape line drawing with predator flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent and predator flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + + +; +; Jumptable for shape line drawing routines with all flags set +; + +AllFlagsJumpTable label dword + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +EndNewShapeJumpTable db 0 + +.CODE + + + +;--------------------------------------------------------------------------- + + + + +;********************************************************************************************* +;* Set_Shape_Header -- create the line header bytes for a shape * +;* * +;* INPUT: Shape width * +;* Shape height * +;* ptr to raw shape data * +;* ptr to shape headers * +;* shape flags * +;* ptr to translucency table * +;* IsTranslucent * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/29/95 10:09AM ST : Created. * +;*===========================================================================================* + + Setup_Shape_Header proc C pixel_width:DWORD, pixel_height:DWORD, src:DWORD, headers:DWORD, flags:DWORD, Translucent:DWORD, IsTranslucent:DWORD + + ;ARG pixel_width :DWORD ; width of rectangle to blit + ;ARG pixel_height :DWORD ; height of rectangle to blit + ;ARG src :DWORD ; this is a member function + ;ARG headers :DWORD + ;ARG flags :DWORD + ;ARG Translucent :DWORD + ;ARG IsTranslucent :DWORD + LOCAL trans_count :DWORD + + pushad + + + mov esi,[src] ;ptr to raw shape data + mov edi,[headers] ;ptr to headers we are going to set up + mov eax,[flags] + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST + mov [edi].ShapeHeaderType.draw_flags,eax ;save old flags in header + add edi,size ShapeHeaderType + mov edx,[pixel_height] ;number of shape lines to scan + +outer_loop: mov ecx,[pixel_width] ;number of pixels in shape line + xor bl,bl ;flag the we dont know anything about this line yet + mov [trans_count],0 ;we havnt scanned any transparent pixels yet + +; +; Scan one shape line to see what kind of data it contains +; +inner_loop: xor eax,eax + mov al,[esi] + inc esi + +; +; Check for transparent pixel +; + test al,al + jnz not_transp + test [flags],SHAPE_TRANS + jz not_transp + or bl,BLIT_TRANSPARENT ;flag that pixel is transparent + inc [trans_count] ;keep count of the number of transparent pixels on the line + jmp end_lp + +; +; Check for predator effect on this line +; +not_transp: test [flags],SHAPE_PREDATOR + jz not_pred + or bl,BLIT_PREDATOR + +; +; Check for ghost effects +; +not_pred: test [flags],SHAPE_GHOST + jz not_ghost + push edi + mov edi,[IsTranslucent] + cmp byte ptr [edi+eax],-1 + pop edi + jz not_ghost + or bl,BLIT_GHOST + +; +; Check if fading is required +; +not_ghost: test [flags],SHAPE_FADING + jz end_lp + or bl,BLIT_FADING + +end_lp: dec ecx + jnz inner_loop + + +; +; Interpret the info we have collected and decide which routine will be +; used to draw this line +; + xor bh,bh + + test bl,BLIT_TRANSPARENT + jz no_transparencies + or bh,BLIT_TRANSPARENT + mov ecx,[pixel_width] + cmp ecx,[trans_count] + jnz not_all_trans + +; all pixels in the line were transparent so we dont need to draw it at all + mov bh,BLIT_SKIP + jmp got_line_type + +not_all_trans: +no_transparencies: + mov al,bl + and al,BLIT_PREDATOR + or bh,al + mov al,bl + and al,BLIT_GHOST + or bh,al + mov al,bl + and al,BLIT_FADING + or bh,al + +; +; Save the line header and do the next line +; +got_line_type:mov [edi],bh + inc edi + + dec edx + jnz outer_loop + + + popad + ret + +Setup_Shape_Header endp + + + + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; +next_line macro + + add edi , [ dest_adjust_width ] ;add in dest modulo + dec edx ;line counter + jz real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp dword ptr [eax] ;do the jump + + endm + + + + + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* +Buffer_Frame_To_Page proc C public USES eax ebx ecx edx esi edi x_pixel:DWORD, y_pixel:DWORD, pixel_width:DWORD, pixel_height:DWORD, src:DWORD, dest:DWORD, flags:DWORD + + ;USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ;ARG x_pixel :DWORD ; x pixel position in source + ;ARG y_pixel :DWORD ; y pixel position in source + ;ARG pixel_width :DWORD ; width of rectangle to blit + ;ARG pixel_height:DWORD ; height of rectangle to blit + ;ARG src :DWORD ; this is a member function + ;ARG dest :DWORD ; what are we blitting to + + ;ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + LOCAL header_pointer :DWORD + LOCAL use_old_draw :DWORD + LOCAL save_ecx :DWORD + LOCAL ShapeJumpTableAddress :DWORD + LOCAL shape_buffer_start :DWORD + + prologue + cmp [ src ] , 0 + jz real_out + + ; + ; Save the line attributes pointers and + ; Modify the src pointer to point to the actual image + ; + cmp [UseBigShapeBuffer],0 + jz do_args ;just use the old shape drawing system + + mov edi,[src] + mov [header_pointer],edi + + mov eax,[BigShapeBufferStart] + cmp [edi].ShapeHeaderType.shape_buffer,0 + jz is_ordinary_shape + mov eax,[TheaterShapeBufferStart] +is_ordinary_shape: + mov [shape_buffer_start],eax + + mov edi,[edi].ShapeHeaderType.shape_data + add edi,[shape_buffer_start] + mov [src],edi + mov [use_old_draw],0 + + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +check_trans: + test [ flags ] , SHAPE_TRANS + jz check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + + +check_fading: + ;______________________________________________________________________ + ; If this is the first time through for this shape then + ; set up the shape header + ;______________________________________________________________________ + pushad + + cmp [UseBigShapeBuffer],0 + jz new_shape + + mov edi,[header_pointer] + cmp [edi].ShapeHeaderType.draw_flags,-1 + jz setup_headers + mov eax,[flags] ;Redo the shape headers if this shape was + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST ;initially set up with different flags + cmp eax,[edi].ShapeHeaderType.draw_flags + jz no_header_setup +new_shape: + mov [use_old_draw],1 + jmp no_header_setup + +setup_headers: + push [IsTranslucent] + push [Translucent] + push [flags] + push [header_pointer] + push [src] + push [pixel_height] + push [pixel_width] + call Setup_Shape_Header + add esp,7*4 + mov [ShapeJumpTableAddress], offset AllFlagsJumpTable + jmp headers_set +no_header_setup: + + xor eax,eax + test [flags],SHAPE_PREDATOR + jz not_shape_predator + or al,BLIT_PREDATOR + +not_shape_predator: + test [flags],SHAPE_FADING + jz not_shape_fading + or al,BLIT_FADING + +not_shape_fading: + + test [flags],SHAPE_TRANS + jz not_shape_transparent + or al,BLIT_TRANSPARENT + +not_shape_transparent: + + test [flags],SHAPE_GHOST + jz not_shape_ghost + or al,BLIT_GHOST + +not_shape_ghost: + + + shl eax,7 + add eax, offset NewShapeJumpTable + mov [ShapeJumpTableAddress],eax + ;call Init_New_Shape_Jump_Table_Address + + +headers_set: + popad + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +set_fading: + mov [ FadingNum ] , eax + + mov ebx,[ShapeJumpTableAddress] + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx],offset Single_Line_Single_Fade + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx+4],offset Single_Line_Single_Fade_Trans + cmp eax,1 + jz single_fade + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx],offset Single_Line_Fading + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx+4],offset Single_Line_Fading_Trans + +single_fade: + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp pred_cont + +check_range: + and eax , PRED_MASK ; keep entries within bounds + +pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [esi].GraphicViewPort.GVPWidth ; add width + add eax , [esi].GraphicViewPort.GVPXAdd ; add x add + add eax , [esi].GraphicViewPort.GVPPitch ; extra pitch of DD surface ST - 9/29/95 1:08PM + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , dword ptr [ BufferFrameTable + ebx ] ; get table value + mov dword ptr [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi].GraphicViewPort.GVPWidth ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi].GraphicViewPort.GVPHeight ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + + or al , dl + jz do_blit + + mov [use_old_draw],1 + test cl , 1000b + jz dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +dest_left_ok: + test cl , 0010b + jz dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +dest_bottom_ok: + test dl , 0100b + jz dest_right_ok + + mov eax , [esi].GraphicViewPort.GVPWidth ; get width into register + mov [ x1_pixel ] , eax + +dest_right_ok: + test dl , 0001b + jz do_blit + + mov eax , [esi].GraphicViewPort.GVPHeight ; get width into register + mov [ y1_pixel ] , eax + +do_blit: + cld + mov eax , [esi].GraphicViewPort.GVPXAdd + add eax , [esi].GraphicViewPort.GVPPitch + add eax , [esi].GraphicViewPort.GVPWidth + mov edi , [esi].GraphicViewPort.GVPOffset + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + +; +; If the shape needs to be clipped then we cant handle it with the new header systen +; so draw it with the old shape drawer +; + cmp [use_old_draw],0 + jnz use_old_stuff + + add [header_pointer],size ShapeHeaderType + mov edx,[pixel_height] + mov ecx,[pixel_width] + mov eax,[header_pointer] + mov al,[eax] + mov [save_ecx],ecx + inc [header_pointer] + and eax,BLIT_ALL + shl eax,2 + add eax,[ShapeJumpTableAddress] + jmp dword ptr [eax] + + +use_old_stuff: + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle real_out + + sub eax , [ x_pixel ] + jle real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +real_out: + + cmp [MMXAvailable],0 + jz no_mmx_cleanup + call MMX_Done + +no_mmx_cleanup: + epilogue + + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** +;extern BF_Copy:near + +BF_Copy: + prologue + + cmp eax , 10 + jl forward_loop_bytes + +forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz forward_loop_dword + + ret + +forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + + epilogue + + ret + + +;******************************************************************** +;******************************************************************** + +; segment code page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +; +; Expand the 'next_line' macro so we can jump to it +; +; +; ST - 12/20/2018 3:48PM +Next_Line:: next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Trans:: + prologue + +Single_Line_Trans_Entry:: + +slt_mask_map_lp: ; Pentium pipeline usage + ;Pipe Cycles + mov al,[esi] ;U 1 + inc esi ;Vee 1 + + test al,al ;U 1 + jz slt_skip ;Vee 1/5 + +slt_not_trans:mov [edi],al ;u 1 + + inc edi ;vee 1 + dec ecx ;u 1 + + jnz slt_mask_map_lp ;vee (maybe) 1 + +slt_end_line: epilogue + next_line + + ;align 32 + +slt_skip: inc edi + dec ecx + jz slt_skip_end_line + + mov al,[esi] + inc esi + test al,al + jz slt_skip2 + mov [edi],al + inc edi + dec ecx + jnz slt_mask_map_lp + + epilogue + next_line + + ;align 32 + +slt_skip2: inc edi + dec ecx + jz slt_end_line + +; +; If we have hit two transparent pixels in a row then we go into +; the transparent optimised bit +; +slt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz slt_not_trans;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz slt_end_line ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp slt_round_again + + + +slt_skip_end_line: + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; +; We have to align the destination for cards that dont bankswitch correctly +; when you write non-aligned data. +; + ;align 32 +Long_Single_Line_Copy: + prologue + + rept 3 + test edi,3 + jz LSLC_aligned + movsb + dec ecx + endm + +LSLC_aligned: + mov ebx,ecx + + shr ecx,2 + rep movsd + and ebx,3 + jz proc_out + movsb + dec bl + jz proc_out + movsb + dec bl + jz proc_out + movsb +proc_out: epilogue + next_line + + + +;***************************************************************************** +; Draw a single short line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Short_Single_Line_Copy: + prologue + cmp ecx,16 + jge Long_Single_Line_Copy + mov ebx,ecx + rep movsb + mov ecx,ebx + epilogue + next_line + + +;***************************************************************************** +; Skip a line of source that is all transparent +; +; 11/29/95 10:21AM - ST +; + + ;align 32 +Single_Line_Skip: + prologue + add esi,ecx + add edi,ecx + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Single_Line_Ghost: + + prologue + xor eax,eax +slg_loop: mov al,[esi] + mov ebx,[IsTranslucent] + inc esi + mov bh,[eax+ebx] + cmp bh,-1 + jz slg_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +slg_store_pixel: + mov [edi],al + + inc edi + dec ecx + jnz slg_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Single_Line_Ghost_Trans: + prologue + xor eax,eax +; cmp ecx,3 +; ja slgt4 + +slgt_loop: mov al,[esi] + inc esi + test al,al + jz slgt_transparent + +slgt_not_transparent: + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt_store_pixel: + mov [edi],al + inc edi + dec ecx + jnz slgt_loop + epilogue + next_line + + + ;align 32 + +slgt_transparent: + inc edi ;1 + dec ecx ;2 + jz slgt_out ;1 (not pairable) + +slgt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz slgt_not_transparent ;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz slgt_out ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp slgt_round_again + +slgt_out: epilogue + next_line + + + +; +; Optimised video memory access version +; + ;align 32 + +slgt4: push edx + mov edx,[edi] + + rept 4 + local slgt4_store1 + local slgt4_trans1 + mov al,[esi] + inc esi + test al,al + jz slgt4_trans1 + + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt4_store1 + + and ebx,0ff00h + mov al,dl + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt4_store1: mov dl,al + +slgt4_trans1: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + lea ecx,[ecx+0fffffffch] + cmp ecx,3 + ja slgt4 + test ecx,ecx + jnz slgt_loop + + epilogue + next_line + + + + + + + + + + +;***************************************************************************** +; Draw a single line with fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Fading: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +slf_loop: mov al,[esi] + inc esi + + mov ebp,[esp] + +slf_fade_loop:mov al,[ebx+eax] + dec ebp + jnz slf_fade_loop + + mov [edi],al + inc edi + + dec ecx + jnz slf_loop + add esp,4 + pop ebp + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Fading_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +slft_loop: mov al,[esi] + inc esi + test al,al + jz slft_transparent + + mov ebp,[esp] + +slft_fade_loop: + mov al,[ebx+eax] + dec ebp + jnz slft_fade_loop + + mov [edi],al +slft_transparent: + inc edi + + dec ecx + jnz slft_loop + add esp,4 + pop ebp + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Single_Fade: + prologue + xor eax,eax + mov ebx,[FadingTable] + +slsf_loop: mov al,[esi] + mov al,[ebx+eax] + mov [edi],al + inc esi + inc edi + + dec ecx + jnz slsf_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Single_Fade_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + +slsft_loop: mov al,[esi] + inc esi + test al,al + jz slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz slsft_loop + epilogue + next_line + + ;align 32 + +slsft_transparent: + inc edi + + dec ecx + jz slsft_next_line + mov al,[esi] + inc esi + test al,al + jz slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz slsft_loop + +slsft_next_line: + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with ghosting and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Ghost_Fading: + + prologue + mov [StashECX],ecx + +SLGF_loop: xor eax,eax + mov al,[esi] + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgf_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +slgf_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slgf_fade_loop + + mov [edi],al + inc esi + inc edi + + dec [StashECX] + jnz SLGF_loop + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Ghost_Fading_Trans: + prologue + mov [StashECX],ecx + xor eax,eax + +; cmp ecx,3 +; ja slgft4 + +SLGFT_loop: mov al,[esi] + inc esi + test al,al + jz slgft_trans_pixel + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slgft_fade_loop + + mov [edi],al +slgft_trans_pixel: + inc edi + + dec [StashECX] + jnz SLGFT_loop + epilogue + next_line + + + ;align 32 + +slgft4: push edx + mov edx,[edi] + + rept 4 + local slgft4_fade + local slgft4_fade_lp + local slgft4_trans + mov al,[esi] + inc esi + test al,al + jz slgft4_trans + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft4_fade + + and ebx,0ff00h + + mov al,dl + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft4_fade: mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft4_fade_lp: mov al,[eax+ebx] + dec ecx + jnz slgft4_fade_lp + + mov dl,al + +slgft4_trans: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + sub [StashECX],4 + jz slgft4_out + cmp [StashECX],3 + ja slgft4 + jmp SLGFT_loop + +slgft4_out: epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator effect +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator: + + prologue + +slp_loop: mov al,[esi] + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz slp_get_pred + + mov [BFPartialCount] , ebx + jmp slp_skip_pixel + +slp_get_pred: xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add byte ptr [BFPredOffset],2 + mov eax, dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +slp_skip_pixel: + inc esi + inc edi + + dec ecx + jnz slp_loop + + epilogue + next_line + + + + +;***************************************************************************** +; Draw a single line with transparent pixels and predator effect +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Trans: + + prologue + +slpt_loop: mov al,[esi] + inc esi + test al,al + jz slpt_skip_pixel + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz slpt_get_pred + + mov [BFPartialCount] , ebx + jmp slpt_skip_pixel + +slpt_get_pred:xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset ] , PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +slpt_skip_pixel: + inc edi + + dec ecx + jnz slpt_loop + + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost: + + prologue + +slpg_loop: mov al,[esi] + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpg_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpg_check_ghost + +slpg_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax ] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +slpg_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpg_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpg_store_pixel: + mov [edi],al + inc esi + inc edi + + dec ecx + jnz slpg_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Trans: + prologue + +slpgt_loop: mov al,[esi] + inc esi + test al,al + jz slpgt_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpgt_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgt_check_ghost + +slpgt_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax ] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +slpgt_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgt_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgt_store_pixel: + mov [edi],al +slpgt_transparent: + inc edi + + dec ecx + jnz slpgt_loop + + pop ecx + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Fading: + + prologue + mov [StashECX],ecx + +slpf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh,bh + jnz slpf_get_pred + + mov [BFPartialCount],ebx + jmp slpf_do_fading + +slpf_get_pred:xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +slpf_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slpf_fade_loop + + mov [edi],al + inc edi + + dec [StashECX] + jnz slpf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, fading and predator +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Fading_Trans: + prologue + mov [StashECX],ecx + +slpft_loop: mov al,[esi] + inc esi + test al,al + jz slpft_transparent + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpft_get_pred + + mov [BFPartialCount],ebx + jmp slpft_do_fading + +slpft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +slpft_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slpft_fade_loop + + mov [edi],al +slpft_transparent: + inc edi + + dec [StashECX] + jnz slpft_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Fading: + + prologue + mov [StashECX],ecx + +slpgf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh , bh + jnz slpgf_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgf_check_ghost + +slpgf_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +slpgf_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgf_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgf_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpgf_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz slpgf_fade_loop + +slpgf_store_pixel: + mov [edi],al + inc edi + + dec [StashECX] + jnz slpgf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Fading_Trans: + + prologue + mov [StashECX],ecx + +slpgft_loop: mov al,[esi] + inc esi + test al,al + jz slpgft_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh , bh + jnz slpgft_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgft_check_ghost + +slpgft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +slpgft_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgft_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgft_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpgft_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz slpgft_fade_loop + +slpgft_store_pixel: + mov [edi],al +slpgft_transparent: + inc edi + + dec [StashECX] + jnz slpgft_loop + + epilogue + next_line + + + + +; ends ;end of strict alignment segment + +; codeseg + + + +;extern BF_Trans:near + +BF_Trans: + + prologue +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +trans_reference: + dec ecx + jge trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz trans_loop + epilogue + + ret + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost:near +BF_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx, [offset ghost_reference] + sub ecx, [offset ghost_line] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +ghost_reference: + dec ecx + jge ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Trans:near +BF_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_t_reference ] + sub ecx, [ offset ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +ghost_t_reference: + dec ecx + jge ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Fading:near +BF_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset fading_reference ] + sub ecx, [ offset fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz fading_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Fading_Trans:near +BF_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset fading_t_reference ] + sub ecx, [ offset fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz fading_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Fading:near +BF_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_f_reference ] + sub ecx, [ offset ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , byte ptr [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , byte ptr [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Fading_Trans:near +BF_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_f_t_reference ] + sub ecx, [ offset ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , byte ptr [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , byte ptr [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator:near +BF_Predator: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_reference ] + sub ecx, [offset predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +predator_reference: + dec ecx + jge predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Trans:near +BF_Predator_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_t_reference ] + sub ecx, [ offset predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_t_reference: + dec ecx + jge predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost:near +BF_Predator_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_reference ] + sub ecx, [ offset predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +predator_g_reference: + dec ecx + jge predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Trans:near +BF_Predator_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_t_reference ] + sub ecx, [ offset predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_g_t_reference: + dec ecx + jge predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Fading:near +BF_Predator_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_f_reference ] + sub ecx, [ offset predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Fading_Trans:near +BF_Predator_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_f_t_reference ] + sub ecx, [ offset predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Fading:near +BF_Predator_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_f_reference ] + sub ecx, [ offset predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Fading_Trans:near +BF_Predator_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_f_t_reference ] + sub ecx, [ offset predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + +Buffer_Frame_To_Page ENDP + + +end + +externdef C CPUType:byte + + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local local_cputype:byte + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [local_cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [local_cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [local_cputype],al + +@@end_get_cpu: mov al,[local_cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + +;********************************************************************************************* +;* Init_MMX -- Do any special inits required for MMX support * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Init_MMX proc C + + mov edi,offset NewShapeJumpTable + mov ecx,offset EndNewShapeJumpTable + sub ecx,edi + shr ecx,2 + mov eax,offset Single_Line_Trans + mov ebx,offset MMX_Single_Line_Trans + cld + + +@@patch_loop: repnz scasd + jnz @@done + mov [edi-4],ebx + test ecx,ecx + jnz @@patch_loop + +@@done: ret + +Init_MMX endp + + + + + + +;********************************************************************************************* +;* MMX_Done -- Restores floating point capability after MMX usage * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +MMX_Done proc C + + emms + ret + +MMX_Done endp + + + + + + + + code segment page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +;********************************************************************************************* +;* MMX_Single_Line_Trans -- draw a single line of transparent pixels using MMX technology * +;* * +;* * +;* INPUT: Esi - ptr to source data * +;* Edi - ptr to destination data * +;* Ecx - width to draw in bytes * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + + align 16 + +MMX_Single_Line_Trans proc near + +; +; If we are doing less than 8 bytes then dont use MMX +; + cmp ecx,8 + jge @@mmx_loop + push offset Single_Line_Trans_Entry + ret + +; +; Use MMX instructions to mask 8 bytes at once +; +; Creates a bitmask based on the source bytes equality with zero and then uses this to mask +; out the source bytes in the destination data. The advatage that MMX gives us is that there is +; no 'test for zero then jump' required to mask. +; + align 64 ;MMX instructions like 64 byte alignment! + +@@mmx_loop: + movq mm0,[esi] ; move 8 bytes of source into mm0 + pxor mm1,mm1 ; zero out mm1 + pcmpeqb mm1,mm0 ; compare mm0 with 0. Bits get set in mm1 + lea esi,[esi+8] ; adjust the source data pointer + pand mm1,[edi] ; and in the destination data to throw away the bytes which arent zero in the source + sub ecx,8 ; adjust the byte counter + por mm1,mm0 ; or in the source with the destination data + movq [edi],mm1 ; write back the destination data + lea edi,[edi+8] ; adjust the destination pointer + + cmp ecx,8 + jg @@mmx_loop + +; +; Jump to the approprite code for drawing the end of this line or going to the next one +; + push offset Next_Line + jcxz @@next_line + push offset Single_Line_Trans_Entry +@@next_line: ret + + +MMX_Single_Line_Trans endp + + +code ends + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + + + + + + + + + + +END + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 16, 1992 * +;* * +;* Last Update : October 2, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* Normal_Draw -- Function that writes a normal pixel line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; IDEAL +; P386 +;IDEAL_MODE EQU 1 +; INCLUDE "wwlib.i" + + ;------------------------------------------------------------------- + ; Extern all the library variables that this module requires + ;------------------------------------------------------------------- + + EXTRN C MaskPage:WORD + EXTRN C BackGroundPage:WORD + + ;------------------------------------------------------------------- + ; Define all the equates that this module requires + ;------------------------------------------------------------------- + +WIN_X EQU 0 ; offset for the x coordinate +WIN_Y EQU 2 ; offset for the y coordinate +WIN_WIDTH EQU 4 ; offset for the window width +WIN_HEIGHT EQU 6 ; offset for the window height +BYTESPERROW EQU 320 ; number of bytes per row + +FLAG_NORMAL EQU 0 ; flag for normal draw + +FLAG_GHOST EQU 1 ; This flag enables the ghost +FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent +FLAG_TRANS EQU 4 ; flag for transparent draw +FLAG_PRIORITY EQU 8 ; flag for priority draw + + ; fx on the above flags + +FLAG_MASK EQU 15 ; used to and of uneeded bits + +SHAPE_NORMAL EQU 0000h ; Standard shape. +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally. +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically. +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) + +SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point. +SHAPE_TRANS EQU 0040h ; has transparency + + +;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num). +;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect. +;SHAPE_COMPACT EQU 0400h ; Never use this bit. +SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing. + +SHAPE_GHOST EQU 1000h ; Transluscent table process. +;SHAPE_SHADOW EQU 2000h ; +;SHAPE_PARTIAL EQU 4000h ; +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table). + + +; MBL MOD 12.1.92 + +CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display +CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit +CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits +NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit + +; END MBL MOD + + + CODESEG + + ; 1 = GHOST (all odd entrys are prefixed with Ghost_) + ; 2 = BLAAAH + ; 4 = Trans (prfx) + ; 8 = Prior (prfx) + + +;--------------------------------------------------------------------------- +; Define the table of different line draw types +;--------------------------------------------------------------------------- + +LineTable DW WSA_Normal_Draw ;0 + DW Ghost_Normal_Draw ;1 + DW 0 ;2 + DW 0 ;3 + + DW Transparent_Draw ;4 + DW Ghost_Transparent_Draw ;5 + DW 0 ;6 + DW 0 ;7 + + DW Priority_Draw ;8 + DW Ghost_Priority_Draw ;9 + DW 0 ;10 + DW 0 ;11 + + DW Priority_Transparent_Draw ;12 + DW Ghost_Priority_Transparent_Draw ;13 + DW 0 ;14 + DW 0 ;15 + + + +;*************************************************************************** +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/16/1992 PWG : Created. * +;*=========================================================================* + PUBLIC C Buffer_Frame_To_LogicPage + PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi + + ;------------------------------------------------------------------- + ; Define the arguements that our program takes. + ;------------------------------------------------------------------- + + ARG x_pixel:WORD ; x pixel position to draw at + ARG y_pixel:WORD ; y pixel position to draw at + ARG pixel_w:WORD ; pixel width of draw region + ARG pixel_h:WORD ; pixel height of draw region + ARG win:WORD ; window to clip around + ARG flags:WORD ; flags that this routine will take + ARG buffer:DWORD ; pointer to the buffer with data + ARG args:WORD + + ;------------------------------------------------------------------- + ; Define the local variables that our program uses + ;------------------------------------------------------------------- + + LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table + LOCAL Translucent:DWORD ; ptr to the actual translucent table + + LOCAL win_x1:WORD ; clip window left x pixel position + LOCAL win_x2:WORD ; clip window right x pixel position + LOCAL win_y1:WORD ; clip window top y pixel position + LOCAL win_y2:WORD ; clip window bottom y pixel position + LOCAL clipleft:WORD ; number of pixels to clip on left + LOCAL clipright:WORD ; number of pixels to clip on right + LOCAL nextline:WORD ; offset to the next line + LOCAL putmiddle:WORD ; routine to call to put the middle + LOCAL maskpage:WORD ; location of the depth masks + LOCAL background:WORD ; location of the background data + LOCAL jflags:WORD ; location of the background data + + LOCAL priority:BYTE ; the priority level of the back + + push fs + + xor ecx,ecx + + ;-------------------------------------------------------------------- + ; Check to see if we have supplied any GHOST tables. + ;-------------------------------------------------------------------- + push di + + mov di,6 + mov [jflags],0 + +ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz short no_ghost ; if not then skip and do more + + or [jflags],FLAG_GHOST + + les ax,dword ptr [buffer + di] + + ; get the "are we really translucent?" table + mov [WORD PTR IsTranslucent],ax + mov [WORD PTR IsTranslucent + 2],es + add ax,0100h ; add to offset for tables + + ; get the "ok we are translucent!!" table + mov [WORD PTR Translucent],ax + mov [WORD PTR Translucent + 2],es + + add di,4 + +no_ghost: + + pop di + + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [flags],SHAPE_CENTER ; does this need to be centered? + je short no_centering ; if not the skip over this stuff + + mov ax,[pixel_w] + mov bx,[pixel_h] + sar ax,1 + sar bx,1 + sub [x_pixel],ax + sub [y_pixel],bx + +no_centering: + mov ax,[flags] + and ax,SHAPE_PRIORITY+SHAPE_TRANS + cmp ax,SHAPE_PRIORITY+SHAPE_TRANS + jne short test_trans + + or [jflags],FLAG_PRIORITY_TRANS + jmp short priority + + ;------------------------------------------------------------------- + ; Get the trans information if we need to get it + ;------------------------------------------------------------------- +test_trans: + test [flags],SHAPE_TRANS ; does this draw use transparencies? + je short test_priority ; if not the skip over this junk + + or [jflags],FLAG_TRANS + +test_priority: + ;------------------------------------------------------------------- + ; Get the priority information if we need to get it + ;------------------------------------------------------------------- + test [flags],SHAPE_PRIORITY ; does this draw use priorities? + je short no_priority ; if not the skip over this junk + + or [jflags],FLAG_PRIORITY + +priority: + mov ax,[BackGroundPage] ; get the background page from ds + mov [background],ax ; and store it on the stack + mov ax,[MaskPage] ; get the mask page from ds + mov [maskpage],ax ; and store it on the stack + mov ax,[WORD PTR buffer + 4]; get the priority level from args + mov [priority],al ; and store it in a local + + ;------------------------------------------------------------------- + ; Get the draw routine that we are going to draw with + ;------------------------------------------------------------------- +no_priority: +; mov bx,[flags] ; load in the current flags byte +; and bx,FLAG_MASK ; prevent lockup on bad value + mov bx,[jflags] ; load in the jump table flags + shl bx,1 + mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table + mov [putmiddle],ax ; store off the new offset + + ;------------------------------------------------------------------- + ; Get a pointer to the logic page to where we will draw our buffer + ;------------------------------------------------------------------- + push [LogicPage] ; push the current logic page + call FAR PTR Get_Page ; get the physical page address + add sp,2 ; pull the parameter from the stack + mov es,dx ; store the address in the dest + + ;-------------------------------------------------------------------- + ; Point DI to the beginning of the window that we need to look at. + ; that way we can access all of the info through di. + ;-------------------------------------------------------------------- + mov si,OFFSET WindowList ; get the offset of the window list + mov cl,4 ; shift 3 times = multiply by 16 + mov ax,[win] ; get the window number we are using + shl ax,cl ; each window is 8 words long + add si,ax ; add that into the offset of window + + ;-------------------------------------------------------------------- + ; Place all the clipping values on the stack so our function will + ; be truly re-entrant and will not need to shadow these values. + ;-------------------------------------------------------------------- + mov cl,3 ; to convert x to pixel mult by 8 + mov ax,[si + WIN_X] ; get the left clip position + shl ax,cl ; convert to a pixel x position + mov [win_x1],ax ; store the left edge of window + mov [win_x2],ax + + mov ax,[si + WIN_WIDTH] ; get the width of the window + shl ax,cl ; convert to a pixel width + add [win_x2],ax ; add to get the right window edge + + mov ax,[si + WIN_Y] ; get the win y coordinate to clip + mov [win_y1],ax ; and save it onto the stack + + add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord + mov [win_y2],ax ; and save it onto the stack + + test [flags],SHAPE_WIN_REL ; is this window relative? + je short get_buffer ; if not the skip over + + mov ax,[win_x1] ; get left edge of window + add [x_pixel],ax ; add to x pixel position + mov ax,[win_y1] ; get top edge of window + add [y_pixel],ax ; add to y pixel position + + ;-------------------------------------------------------------------- + ; Get a pointer to the source buffer so we can handle the clipping + ;-------------------------------------------------------------------- +get_buffer: + lds si,[buffer] ; get a pointer to the buffer + + ;-------------------------------------------------------------------- + ; Check the top of our shape and clip any lines that are necessary + ;-------------------------------------------------------------------- + mov ax,[y_pixel] ; get the y_pixel draw position + sub ax,[win_y1] ; subtract out the window y top + jns short check_bottom ; skip if y below window top + add ax,[pixel_h] ; add in the height of the region + jg short clip_top ; if positive then clip top lines + +jump_exit: + jmp proc_exit ; otherwise completely clipped + +clip_top: + xchg [pixel_h],ax + sub ax,[pixel_h] + add [y_pixel],ax + mul [pixel_w] ; convert to number of bytes to skip + add si,ax ; skip past the necessary bytes + + ;-------------------------------------------------------------------- + ; Check the bottom of our shape and clip it if necessary + ;-------------------------------------------------------------------- +check_bottom: + mov ax,[win_y2] ; get the bottom y of the window + sub ax,[y_pixel] ; subtract of the y to draw at + js jump_exit ; if its signed then nothing to draw + jz jump_exit ; if its zero then nothing to draw + + cmp ax,[pixel_h] ; if more room to draw then height + jae short clip_x_left ; then go check the left clip + mov [pixel_h],ax ; clip all but amount that will fit + +clip_x_left: + mov [clipleft],0 ; clear clip on left of region + mov ax,[x_pixel] ; get the pixel x of draw region + sub ax,[win_x1] ; pull out the window coordinate + jns short clip_x_right + neg ax ; negate to get amnt to skip in buf + mov [clipleft],ax ; store it in the left clip info + add [x_pixel],ax ; move to the edge of the window + sub [pixel_w],ax ; pull it out of the pixel width + +clip_x_right: + mov [clipright],0 ; clear clip on right of region + mov ax,[win_x2] ; get the window x of clip region + sub ax,[x_pixel] ; subtract the draw edge of region + js jump_exit ; if its negative then get out + jz jump_exit ; if its zero then get out + + cmp ax,[pixel_w] ; is space available larger than w + jae short draw_prep ; if so then go get drawing + + + xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax) + sub ax,[pixel_w] ; pull out the amount to draw + mov [clipright],ax ; this is the amount to clip on right + +draw_prep: + push si ; save off source pos in buffer + push ds ; both offset and segment + mov ax,@data + mov ds,ax + mov bx,[y_pixel] + shl bx,1 ; shift left by 1 for word table look + lds si,[YTable] ; get the address of the ytable + mov di,[ds:si+bx] ; look up the multiplied value + pop ds ; restore source pos in buffer + pop si ; both offset and segment + + add di,[x_pixel] ; add in the x pixel position + mov [nextline],di ; save it off in the next line + + ;-------------------------------------------------------------------- + ; Now determine the type of the shape and process it in the proper + ; way. + ;-------------------------------------------------------------------- + mov dx,[pixel_h] + + ; Check to see if the WSA is the screen width and there is no + ; clipping. In this case, then a special single call to the + ; line processing routine is possible. + mov ax,[clipleft] + add ax,[clipright] + jne short top_of_loop + cmp [pixel_w],BYTESPERROW + jne short top_of_loop + + ;------------------------------------ + ; The width of the WSA is the screen width, so just process as + ; one large WSA line. + mov ax,BYTESPERROW + imul dx + mov cx,ax + call [putmiddle] + jmp short proc_exit + + ;------------------------------------ + ; Process line by line. +top_of_loop: + add si,[clipleft] ; skip whats necessary on left edge + mov cx,[pixel_w] ; get the width we need to draw + + ; Copy the source to the destination as appropriate. This routine can + ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past + ; the end of the data. + call [putmiddle] + + add si,[clipright] ; skip past the left clip + add [nextline],BYTESPERROW + mov di,[nextline] + + dec dx + jnz top_of_loop + +proc_exit: + pop fs + ret + ENDP + + +;*************************************************************************** +;* NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;*=========================================================================* + + PROC NOLANGUAGE WSA_Normal_Draw NEAR + + IF 1 + ; This version is marginally faster than the later version. + mov ax,cx + shr cx,2 + rep movsd + and ax,011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + ret + + ELSE + + shr cx,1 ; convert to words (odd pix in carry) + rep movsw ; write out the needed words + adc cx,0 ; add the carry into cx + rep movsb ; write out the odd byte if any + ret + ENDIF + + ENDP + + +;*************************************************************************** +;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 10/02/1994 JLB : Optimized for 250% speed improvement. * +;*=========================================================================* + PROC NOLANGUAGE Transparent_Draw NEAR + + IF 1 + ; Preserve DX since it is used as a scratch register. + push dx + +loop: + ; Swap DS:SI and ES:DI back in preparation for the REP SCASB + ; instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Remember the bytes remaining in order to calculate the position + ; of the scan when it stops. + mov bx,cx + + ; Scan looking for a non-zero value in the source buffer. + xor al,al + repe scasb + + ; When the loop ends, if the EQ flag is set then the scanning is + ; complete. Jump to the end of the routine in order to fixup the + ; pointers. + je short fini + + ; Advance the destination pointer by the amount necessary to match + ; the source movement. DS:SI points to where data should be written. + add si,bx + inc cx ; SCASB leaves CX one too low, fix it. + dec di ; SCASB leaves DI one byte too far, fix it. + sub si,cx + + ; Scan for the duration of non-zero pixels. This yields a count which + ; is used to copy the source data to the destination. Preserve DI. + mov dx,di + mov bx,cx + repne scasb + mov di,dx + + ; Set BX to equal the number of bytes to copy from source to dest. + inc cx ; SCASB leaves CX one too low, fix it. + sub bx,cx + + ; Move the data from ES:DI to DS:SI for BX bytes. + xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining. + + ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Move the data from source to dest. First try to move double + ; words. Then copy the remainder bytes (if any). Putting jumps in + ; this section doesn't result in any savings -- oh well. + mov ax,cx + shr cx,2 + rep movsd + and ax,0011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + + ; Restore CX with the remaining bytes to process. + mov cx,bx + + ; If there are more bytes to process, then loop back. + or cx,cx + jne short loop + +fini: + ; Swap ES:DI and DS:SI back to original orientation. + mov ax,ds + mov bx,es + mov es,ax + mov ds,bx + xchg di,si + + ; Restore DX and return. + pop dx + ret + + ELSE + +loop_top: + lodsb + or al,al + jz short skip + + mov [es:di],al ; store the pixel to the screen +skip: + inc di + loop loop_top + ret + + ENDIF + + ENDP + + +;*************************************************************************** +;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of * +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +loop_top: + lodsb ; get the pixel to draw on the screen + + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +out_pixel: + stosb ; write the pixel and inc the DI + loop loop_top + ret + + ENDP + + +;*************************************************************************** +;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are * +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short write_back ; if it is go write background + + mov bl,[gs:di] ; get the mask byte for our pixel + + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short out_pixel ; if so then write the pixel + +write_back: + mov al,[fs:di] ; get the pixel to write +out_pixel: + stosb ; write the pixel + loop loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + + PROC NOLANGUAGE Ghost_Normal_Draw NEAR + +loop_top: + lodsb + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +skip: + inc di + loop loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Transparent_Draw NEAR + +loop_top: + lodsb + or al,al + jz short skip + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +skip: + inc di + loop loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron* +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +loop_top: + lodsb ; get the pixel to draw on the screen + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +out_pixel: + stosb ; write the pixel and inc the DI + loop loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they* +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short write_back ; if it is go write background + mov bl,[gs:di] ; get the mask byte for our pixel + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short out_pixel ; if so then write the pixel +write_back: + mov al,[fs:di] ; get the pixel to write +out_pixel: + stosb ; write the pixel + loop loop_top + + ret + + ENDP + + END + diff --git a/REDALERT/LANGUAGE.H b/REDALERT/LANGUAGE.H new file mode 100644 index 000000000..fc31a8b80 --- /dev/null +++ b/REDALERT/LANGUAGE.H @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifdef ENGLISH +#define TEXT_ERROR_TIMER "Error - Timer system failed to initialise due to system instability. You need to restart Windows." +#define TEXT_SHORT_TITLE "Red Alert" +#define TEXT_MEMORY_ERROR "Error - out of memory." +#define TEXT_ABORT "Abort" +#define TEXT_DDRAW_ERROR "Error - Unable to allocate primary video buffer - aborting." +#define TEXT_TITLE "Command & Conquer : Red Alert" +#define TEXT_VIDEO_ERROR "Error - Unable to set the video mode." +#define TEXT_INSUFFICIENT "Insufficient Disk Space to run Red Alert.\n" +#define TEXT_MUST_HAVE "You must have %d megabytes of free disk space." +#define TEXT_CRITICALLY_LOW "Warning - you are critically low on free disk space for saving games. Do you want to play Red Alert anyway?" +#define TEXT_NO_RAM "Insufficient RAM available.\n" +#define TEXT_USE_START_MENU "\n\rIf you have Windows 95 running you should start Red Alert\r\nDOS version via the Windows 95 Start Menu.\n\rPress any key.\n\r" +#define TEXT_SETUP_FIRST "Run SETUP program first.\n" +#define TEXT_NO_MOUSE "Red Alert is unable to detect your mouse driver." +#define TEXT_FILE_ERROR "FILE ERROR" +#define TEXT_PRESS_KEY "Press any key to retry." +#define TEXT_ESC_KEY "Press to exit program." +#define TEXT_TO_EXIT "Press any key to exit program." +#define TEXT_INVALID "Invalid option switch.\n" +#define TEXT_MAP_ERROR "Map Error!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continue" +#define TEXT_OPTIONS "Red Alert (c) 1996, Westwood Studios\r\n" \ + "Parameters:\r\n" \ + " -DESTNET = Specify Network Number of destination system\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Network Socket ID (0 - 16383)\n" \ + " -STEALTH = Hide multiplayer names (\"Boss mode\")\r\n" \ + " -MESSAGES = Allow messages from outside this game.\r\n" \ + "\r\n" +#endif + + +#ifdef GERMAN +#define TEXT_ERROR_TIMER "Fehler - das Timer-System konnte aufgrund einer Instabilit„t des Systems nicht initialisiert werden. Bitte starten Sie Windows neu." +#define TEXT_SHORT_TITLE "C&C:AR" +#define TEXT_MEMORY_ERROR "Fehler - Kein Speicher mehr." +#define TEXT_ABORT "Abbrechen" +#define TEXT_DDRAW_ERROR "Fehler - Kann prim„ren Videopuffer nicht finden - Abbruch." +#define TEXT_TITLE "Command & Conquer : Alarmstufe Rot" +#define TEXT_VIDEO_ERROR "Fehler - Kann Grafikmodus nicht einstellen." +#define TEXT_INSUFFICIENT "Nicht genug Festplattenplatz fr Command & Conquer:AR.\n" +#define TEXT_MUST_HAVE "Sie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_CRITICALLY_LOW "Nicht genug Festplattenplatz fr Command & Conquer:AR.\nSie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_NO_RAM "Zuwenig Hauptspeicher verfgbar.\n" +#define TEXT_USE_START_MENU "\n\rWenn auf Ihrem Rechner Windows 95 läuft,\n\rstarten Sie die DOS-Version von Alarmstufe Rot\n\rbitte über das Start-Menü von Windows 95.\n\rZum Weitermachen beliebige Taste drücken.\n\r" +#define TEXT_SETUP_FIRST "Bitte erst das SETUP-Programm starten.\n" +#define TEXT_NO_MOUSE "C&C:AR kann Ihren Maustreiber nicht finden..." +#define TEXT_FILE_ERROR "DATEIFEHLER" +#define TEXT_PRESS_KEY "Beliebige Taste drcken fr erneuten Versuch." +#define TEXT_ESC_KEY " drcken, um das Programm zu verlassen." +#define TEXT_TO_EXIT "Beliebige Taste drcken, um das Programm zu verlassen." +#define TEXT_INVALID "Ungltiger Parameter.\n" +#define TEXT_MAP_ERROR "Kartenfehler!" +#define TEXT_STOP "Halt" +#define TEXT_CONTINUE "Weiter" +#define TEXT_OPTIONS "C&C: Alarmstufe Rot (c) 1996, Westwood Studios\r\n" \ + "Parameter:\r\n" \ + " -DESTNET = Netzwerkkennung des Zielrechners festlegen\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Kennung des Netzwerk-Sockets (0 - 16383)\n" \ + " -STEALTH = Namen im Mehrspieler-Modus verstecken (\"Boss-Modus\")\r\n" \ + " -MESSAGES = Mitteilungen von ausserhalb des Spiels zulassen\r\n" \ + "\r\n" +#endif + + +#ifdef FRENCH +#define TEXT_ERROR_TIMER "Error - L'horloge système n'a pas pu s'initialiser en raison de l'instabilit‚ du sytème. Vous devez red‚marrer Windows." +#define TEXT_SHORT_TITLE "Alerte Rouge" +#define TEXT_MEMORY_ERROR "Erreur - Plus de m‚moire." +#define TEXT_ABORT "Interrompre" +#define TEXT_DDRAW_ERROR " Erreur - Impossible d'allouer le tampon vid‚o principal - Interruption." +#define TEXT_TITLE "Command & Conquer : Alerte Rouge" +#define TEXT_VIDEO_ERROR " Erreur - Impossible d'‚tablir le mode vid‚o." +#define TEXT_INSUFFICIENT "Espace disque insuffisant pour lancer Command & Conquer.\n" +#define TEXT_MUST_HAVE "Vous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_CRITICALLY_LOW "Espace disque insuffisant pour lancer Command & Conquer.\nVous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_NO_RAM "M‚moire vive (RAM) insuffisante.\n" +#define TEXT_USE_START_MENU "\n\rSi vous ˆtes dans Windows 95 vous devez lancer Alerte Rouge\r\nDOS … partir du menu D‚marrer de Windows 95.\n\rAppuyez sur n'importe quelle touche.\n\r" +#define TEXT_SETUP_FIRST "Lancez d'abord le programme de configuration SETUP.\n" +#define TEXT_NO_MOUSE "Alerte Rouge ne peut pas d‚tecter votre gestionnaire de souris." +#define TEXT_FILE_ERROR "ERREUR DE FICHIER" +#define TEXT_PRESS_KEY "Appuyez sur une touche pour recommencer." +#define TEXT_ESC_KEY "Appuyez sur Echap pour quitter le programme." +#define TEXT_TO_EXIT "Appuyez sur une touche pour quitter le programme." +#define TEXT_INVALID "Commande d'option invalide.\n" +#define TEXT_MAP_ERROR "Erreur de carte!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continuer" +#define TEXT_OPTIONS "Alerte Rouge (c) 1996, Westwood Studios\r\n" \ + "Paramètres:\r\n" \ + " -DESTNET = Sp‚cifier le num‚ro de r‚seau du systŠme de destination\r\n" \ + " (Syntaxe: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = ID Socket r‚seau (0  16383)\r\n" \ + " -STEALTH = Cacher les noms en mode multijoueurs (\"Mode Boss\")\r\n" \ + " -MESSAGES = Autorise les messages ext‚rieurs … ce jeu.\r\n" \ + "\r\n" +#endif + diff --git a/REDALERT/LAYER.CPP b/REDALERT/LAYER.CPP new file mode 100644 index 000000000..a0cff5efa --- /dev/null +++ b/REDALERT/LAYER.CPP @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LAYER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : March 10, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LayerClass::Sort -- Perform an incremental sort pass on the layer's objects. * + * LayerClass::Sorted_Add -- Adds object in sorted order to layer. * + * LayerClass::Submit -- Adds an object to a layer list. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "layer.h" + + +/*********************************************************************************************** + * LayerClass::Submit -- Adds an object to a layer list. * + * * + * This routine is used to add an object to the layer list. If the list is full, then the * + * object is not added. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Allows sorted insert. * + * 01/02/1995 JLB : Fixed to work with EMSListOf template. * + *=============================================================================================*/ +bool LayerClass::Submit(ObjectClass const * object, bool sort) +{ + /* + ** Add the object to the layer. Either at the end (if "sort" is false) or at the + ** appropriately sorted position. + */ + if (sort) { + return(Sorted_Add(object)); + } + return(Add((ObjectClass *)object)); +} + + +/*********************************************************************************************** + * LayerClass::Sort -- Handles sorting the objects in the layer. * + * * + * This routine is used if the layer objects must be sorted and sorting is to occur now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Don't call this routine too often since it does take a bit of time to * + * execute. It is a single pass binary sort and thus isn't horribly slow, * + * but it does take some time. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 03/10/1995 JLB : Uses comparison operator. * + *=============================================================================================*/ +void LayerClass::Sort(void) +{ + for (int index = 0; index < Count()-1; index++) { + if (*(*this)[index+1] < *(*this)[index]) { + ObjectClass * temp; + + temp = (*this)[index+1]; + (*this)[index+1] = (*this)[index]; + (*this)[index] = temp; + } + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Sorted_Add -- Adds object in sorted order to vector. * + * * + * Use this routine to add an object to the vector but it will be inserted in sorted * + * order. This depends on the ">" operator being defined for the vector object. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added to the vector successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +int LayerClass::Sorted_Add(ObjectClass const * const object) +{ + if ((unsigned)ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the right sorted position. + */ + int index; + for (index = 0; index < ActiveCount; index++) { + if ((*(*this)[index]) > (*object)) { + break; + } + } + + /* + ** Make room if the insertion spot is not at the end of the vector. + */ + for (int i = ActiveCount-1; i >= index; i--) { + (*this)[i+1] = (*this)[i]; + } + (*this)[index] = (ObjectClass *)object; + ActiveCount++; + return(true); +} + + diff --git a/REDALERT/LAYER.H b/REDALERT/LAYER.H new file mode 100644 index 000000000..f7bcbadb2 --- /dev/null +++ b/REDALERT/LAYER.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LAYER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : May 31, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LAYER_H +#define LAYER_H + +#include "vector.h" + +class ObjectClass; + +class LayerClass : public DynamicVectorClass +{ + public: + + //----------------------------------------------------------------- + void Sort(void); + bool Submit(ObjectClass const * object, bool sort=false); + int Sorted_Add(ObjectClass const * const object); + + + virtual void Init(void) {Clear();}; + virtual void One_Time(void) {}; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/REDALERT/LCW.CPP b/REDALERT/LCW.CPP new file mode 100644 index 000000000..e7fc70f3b --- /dev/null +++ b/REDALERT/LCW.CPP @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/*************************************************************************** + * LCW_Uncomp -- Decompress an LCW encoded data block. * + * * + * Uncompress data to the following codes in the format b = byte, w = word * + * n = byte code pulled from compressed data. * + * * + * Command code, n |Description * + * ------------------------------------------------------------------------* + * n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest * + * n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source* + * n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 * + * n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 * + * n=11111110,w1,b1 |long run of byte b1 for w1 bytes * + * n=10000000 |end of data reached * + * * + * * + * INPUT: * + * void * source ptr * + * void * destination ptr * + * unsigned long length of uncompressed data * + * * + * * + * OUTPUT: * + * unsigned long # of destination bytes written * + * * + * WARNINGS: * + * 3rd argument is dummy. It exists to provide cross-platform * + * compatibility. Note therefore that this implementation does not * + * check for corrupt source data by testing the uncompressed length. * + * * + * HISTORY: * + * 03/20/1995 IML : Created. * + *=========================================================================*/ +int LCW_Uncomp(void const * source, void * dest, unsigned long ) +{ + unsigned char * source_ptr, * dest_ptr, * copy_ptr, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* Read in the operation code. */ + op_code = *source_ptr++; + + if (!(op_code & 0x80)) { + + /* Do a short copy from destination. */ + count = (op_code >> 4) + 3; + copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8)); + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + if (!(op_code & 0x40)) { + + if (op_code == 0x80) { + + /* Return # of destination bytes written. */ + return ((unsigned long) (dest_ptr - (unsigned char*) dest)); + + } else { + + /* Do a medium copy from source. */ + count = op_code & 0x3f; + + while (count--) *dest_ptr++ = *source_ptr++; + } + + } else { + + if (op_code == 0xfe) { + + /* Do a long run. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + word_data = data = *(source_ptr + 2); + word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data; + source_ptr += 3; + + copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3); + count -= (copy_ptr - dest_ptr); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + word_dest_ptr = (unsigned*) dest_ptr; + + dest_ptr += (count & 0xfffffffc); + + while (word_dest_ptr < (unsigned*) dest_ptr) { + *word_dest_ptr = word_data; + *(word_dest_ptr + 1) = word_data; + word_dest_ptr += 2; + } + + copy_ptr = dest_ptr + (count & 0x3); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + } else { + + if (op_code == 0xff) { + + /* Do a long copy from destination. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8); + source_ptr += 4; + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + /* Do a medium copy from destination. */ + count = (op_code & 0x3f) + 3; + copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + source_ptr += 2; + + while (count--) *dest_ptr++ = *copy_ptr++; + } + } + } + } + } +} diff --git a/REDALERT/LCW.H b/REDALERT/LCW.H new file mode 100644 index 000000000..f8b97a431 --- /dev/null +++ b/REDALERT/LCW.H @@ -0,0 +1,46 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCW_H +#define LCW_H + + +int LCW_Uncomp(void const * source, void * dest, unsigned long length=0); + +extern "C" { +int __cdecl LCW_Comp(void const * source, void * dest, int length); +} + +#endif diff --git a/REDALERT/LCWCOMP.ASM b/REDALERT/LCWCOMP.ASM new file mode 100644 index 000000000..43aad6005 --- /dev/null +++ b/REDALERT/LCWCOMP.ASM @@ -0,0 +1,286 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: F:\projects\c&c0\vcs\code\lcwcomp.asv 5.0 11 Nov 1996 09:40:34 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : COMPRESS.ASM * +;* * +;* Programmer : Louis Castle * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT + +.MODEL FLAT + +;GLOBAL C LCW_Comp :NEAR +externdef C LCW_Comp:near + +;CODESEG +.code + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +;*********************************************************** +; +; ULONG LCW_Compress(BYTE *source, BYTE *dest, ULONG length) +; +; returns the size of the compressed data in bytes +; +;* +LCW_Comp proc C source:DWORD, dest:DWORD, datasize:DWORD + + ;USES ebx,ecx,edx,edi,esi + + ;ARG source:DWORD + ;ARG dest:DWORD + ;ARG datasize:DWORD + + LOCAL inlen:DWORD + LOCAL a1stdest:DWORD + LOCAL a1stsrc:DWORD + LOCAL lenoff:DWORD + LOCAL ndest:DWORD + LOCAL count:DWORD + LOCAL matchoff:DWORD + LOCAL end_of_data:DWORD + + pushad + + cld + mov edi,[dest] + mov esi,[source] + mov edx,[datasize] ; get length of data to compress + +; mov ax,ds +; mov es,ax + +; +; compress data to the following codes in the format b = byte, w = word +; n = byte code pulled from compressed data +; Bit field of n command description +; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 +; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes +; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 +; n=11111111,w1,w2 long run run w1 bytes from offset w2 +; n=10000000 end end of data reached +; + cld ; make sure all string commands are forward + mov ebx,esi + add ebx,edx + mov [end_of_data],ebx + mov [inlen],1 ; set the in-length flag + mov [a1stdest],edi ; save original dest offset for size calc + mov [a1stsrc],esi ; save offset of first byte of data + mov [lenoff],edi ; save the offset of the legth of this len + sub eax,eax + mov al,081h ; the first byte is always a len + stosb ; write out a len of 1 + lodsb ; get the byte + stosb ; save it +_loop: + mov [ndest],edi ; save offset of compressed data + mov edi,[a1stsrc] ; get the offset to the first byte of data + mov [count],1 ; set the count of run to 0 +searchloop: + sub eax,eax + mov al,[esi] ; get the current byte of data + cmp al,[esi+64] + jne short notrunlength + + mov ebx,edi + + mov edi,esi + mov ecx,[end_of_data] + sub ecx,edi + repe scasb + dec edi + mov ecx,edi + sub ecx,esi + cmp ecx,65 + jb short notlongenough + + mov [DWORD PTR inlen],0 ; clear the in-length flag + mov esi,edi + mov edi,[ndest] ; get the offset of our compressed data + + mov ah,al + mov al,0FEh + stosb + xchg ecx,eax + stosw + mov al,ch + stosb + + mov [ndest],edi ; save offset of compressed data + mov edi,ebx + jmp searchloop +notlongenough: + mov edi,ebx +notrunlength: + +oploop: + mov ecx,esi ; get the address of the last byte +1 + sub ecx,edi ; get the total number of bytes left to comp + jz short searchdone + + repne scasb ; look for a match + jne short searchdone ; if we don't find one we're done + + mov ebx,[count] + mov ah,[esi+ebx-1] + cmp ah,[edi+ebx-2] + + jne oploop + + mov edx,esi ; save this spot for the next search + mov ebx,edi ; save this spot for the length calc + dec edi ; back up one for compare + mov ecx,[end_of_data] ; get the end of data + sub ecx,esi ; sub current source for max len + + repe cmpsb ; see how many bytes match + +; start of change MH 9-24-91 + jne short notend ; if found mismatch then di - bx = match count + + inc edi ; else cx = 0 and di + 1 - bx = match count + +notend: +; end of change MH 9-24-91 + + mov esi,edx ; restore si + mov eax,edi ; get the dest + sub eax,ebx ; sub the start for total bytes that match + mov edi,ebx ; restore dest + cmp eax,[count] ; see if its better than before + jb searchloop ; if not keep looking + + mov [count],eax ; if so keep the count + dec ebx ; back it up for the actual match offset + mov [matchoff],ebx ; save the offset for later + jmp searchloop ; loop until we searched it all + +searchdone: + + mov ecx,[count] ; get the count of the longest run + mov edi,[ndest] ; get the offset of our compressed data + cmp ecx,2 ; see if its not enough run to matter + jbe short lenin ; if its 0,1, or 2 its too small + + cmp ecx,10 ; if not, see if it would fit in a short + ja short medrun ; if not, see if its a medium run + + mov eax,esi ; if its short get the current address + sub eax,[matchoff] ; sub the offset of the match + cmp eax,0FFFh ; if its less than 12 bits its a short + ja short medrun ; if its not, its a medium + +shortrun: + sub ebx,ebx + mov bl,cl ; get the length (3-10) + sub bl,3 ; sub 3 for a 3 bit number 0-7 + shl bl,4 ; shift it left 4 + add ah,bl ; add in the length for the high nibble + xchg ah,al ; reverse the bytes for a word store + jmp short srunnxt ; do the run fixup code + +medrun: + cmp ecx,64 ; see if its a short run + ja short longrun ; if not, oh well at least its long + + sub cl,3 ; back down 3 to keep it in 6 bits + or cl,0C0h ; the highest bits are always on + mov al,cl ; put it in al for the stosb + stosb ; store it + jmp short medrunnxt ; do the run fixup code + +lenin: + cmp [DWORD PTR inlen],0 ; is it doing a length? + jnz short len ; if so, skip code + +lenin1: + mov [lenoff],edi ; save the length code offset + mov al,80h ; set the length to 0 + stosb ; save it + +len: + mov ebx,[lenoff] ; get the offset of the length code + cmp BYTE PTR [ebx],0BFh ; see if its maxed out + je lenin1 ; if so put out a new len code + +stolen: + inc BYTE PTR [ebx] ; inc the count code + lodsb ; get the byte + stosb ; store it + mov DWORD PTR [inlen],1 ; we are now in a length so save it + jmp short nxt ; do the next code + +longrun: + mov al,0ffh ; its a long so set a code of FF + stosb ; store it + + mov eax,[count] ; send out the count + stosw ; store it +medrunnxt: + mov eax,[matchoff] ; get the offset + sub eax,[a1stsrc] ; make it relative tot he start of data +srunnxt: + stosw ; store it +; this code common to all runs + add esi,[count] ; add in the length of the run to the source + mov [DWORD PTR inlen],0 ; set the in leght flag to false + +;======================================================================= + +nxt: + cmp esi,[end_of_data] ; see if we did the whole pic + jae short _out ; if so, cool! were done + + jmp _loop + +_out: + mov ax,080h ; remember to send an end of data code + stosb ; store it + mov eax,edi ; get the last compressed address + sub eax,[a1stdest] ; sub the first for the compressed size + + popad + ret + +LCW_Comp endp + + +END diff --git a/REDALERT/LCWPIPE.CPP b/REDALERT/LCWPIPE.CPP new file mode 100644 index 000000000..50e84fbc3 --- /dev/null +++ b/REDALERT/LCWPIPE.CPP @@ -0,0 +1,310 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lcwpipe.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * * + * This will initialize the LCWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::LCWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::~LCWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LCW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : ((int)sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LCW_Uncomp(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LCW_Comp(Buffer, Buffer2, BlockSize); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LCW_Comp(source, Buffer2, BlockSize); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LCW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LCW_Comp(Buffer, Buffer2, Counter); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/REDALERT/LCWPIPE.H b/REDALERT/LCWPIPE.H new file mode 100644 index 000000000..fae2a16cc --- /dev/null +++ b/REDALERT/LCWPIPE.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWPIPE_H +#define LCWPIPE_H + +#include "pipe.h" + + +/* +** Performs LCW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LCWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWPipe(CompControl, int blocksize=1024*8); + virtual ~LCWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWPipe(LCWPipe & rvalue); + LCWPipe & operator = (LCWPipe const & pipe); +}; + + +#endif diff --git a/REDALERT/LCWSTRAW.CPP b/REDALERT/LCWSTRAW.CPP new file mode 100644 index 000000000..8d0ab9e51 --- /dev/null +++ b/REDALERT/LCWSTRAW.CPP @@ -0,0 +1,176 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWStraw::Get -- Fetch data through the LCW processor. * + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lcwstraw.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * * + * This will initialize the LCW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::LCWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::~LCWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWStraw::Get -- Fetch data through the LCW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LCW_Uncomp(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LCW_Comp(Buffer, &Buffer2[sizeof(BlockHeader)], BlockHeader.UncompCount); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/REDALERT/LCWSTRAW.H b/REDALERT/LCWSTRAW.H new file mode 100644 index 000000000..48b877ea2 --- /dev/null +++ b/REDALERT/LCWSTRAW.H @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWSTRAW_H +#define LCWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LCW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LCWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWStraw(CompControl control, int blocksize=1024*8); + virtual ~LCWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWStraw(LCWStraw & rvalue); + LCWStraw & operator = (LCWStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/LCWUNCMP.CPP b/REDALERT/LCWUNCMP.CPP new file mode 100644 index 000000000..be3906aa6 --- /dev/null +++ b/REDALERT/LCWUNCMP.CPP @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWUNCMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern "C" { + +/*************************************************************************** + * LCW_UNCOMPRESS -- Decompress an LCW encoded data block. * + * * + * Uncompress data to the following codes in the format b = byte, w = word * + * n = byte code pulled from compressed data. * + * * + * Command code, n |Description * + * ------------------------------------------------------------------------* + * n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest * + * n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source* + * n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 * + * n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 * + * n=11111110,w1,b1 |long run of byte b1 for w1 bytes * + * n=10000000 |end of data reached * + * * + * * + * INPUT: * + * void * source ptr * + * void * destination ptr * + * unsigned long length of uncompressed data * + * * + * * + * OUTPUT: * + * unsigned long # of destination bytes written * + * * + * WARNINGS: * + * 3rd argument is dummy. It exists to provide cross-platform * + * compatibility. Note therefore that this implementation does not * + * check for corrupt source data by testing the uncompressed length. * + * * + * HISTORY: * + * 03/20/1995 IML : Created. * + *=========================================================================*/ +unsigned long __cdecl LCW_Uncompress (void * source, void * dest, unsigned long ) +//unsigned long LCW_Uncompress (void * source, void * dest, unsigned long length) +{ + unsigned char * source_ptr, * dest_ptr, * copy_ptr, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* Read in the operation code. */ + op_code = *source_ptr++; + + if (!(op_code & 0x80)) { + + /* Do a short copy from destination. */ + count = (op_code >> 4) + 3; + copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8)); + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + if (!(op_code & 0x40)) { + + if (op_code == 0x80) { + + /* Return # of destination bytes written. */ + return ((unsigned long) (dest_ptr - (unsigned char*) dest)); + + } else { + + /* Do a medium copy from source. */ + count = op_code & 0x3f; + + while (count--) *dest_ptr++ = *source_ptr++; + } + + } else { + + if (op_code == 0xfe) { + + /* Do a long run. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + word_data = data = *(source_ptr + 2); + word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data; + source_ptr += 3; + + copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3); + count -= (copy_ptr - dest_ptr); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + word_dest_ptr = (unsigned*) dest_ptr; + + dest_ptr += (count & 0xfffffffc); + + while (word_dest_ptr < (unsigned*) dest_ptr) { + *word_dest_ptr = word_data; + *(word_dest_ptr + 1) = word_data; + word_dest_ptr += 2; + } + + copy_ptr = dest_ptr + (count & 0x3); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + } else { + + if (op_code == 0xff) { + + /* Do a long copy from destination. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8); + source_ptr += 4; + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + /* Do a medium copy from destination. */ + count = (op_code & 0x3f) + 3; + copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + source_ptr += 2; + + while (count--) *dest_ptr++ = *copy_ptr++; + } + } + } + } + } +} + +} diff --git a/REDALERT/LED.H b/REDALERT/LED.H new file mode 100644 index 000000000..90d4d0ddf --- /dev/null +++ b/REDALERT/LED.H @@ -0,0 +1,43 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\led.h_v 4.42 04 Jul 1996 16:10:40 JOE_BOSTIC $ */ + +#ifndef LED_H +#define LED_H + +class LEDClass +{ + public: + typedef enum ControlType { + LED_NOCHANGE, // Do nothing (just query). + LED_OFF, // Turn LED off. + LED_ON, // Turn LED on. + LED_TOGGLE // Toggle LED state. + } ControlType; + + protected: + static int Shift_Control(ControlType control, char bit); + + public: + static int Scroll_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x01);}; + static int Caps_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x02);}; + static int Num_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x04);}; + + private: + static void Send_To_Keyboard(unsigned char val); +}; + +#endif diff --git a/REDALERT/LINK.CPP b/REDALERT/LINK.CPP new file mode 100644 index 000000000..fb39adf01 --- /dev/null +++ b/REDALERT/LINK.CPP @@ -0,0 +1,398 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LinkClass::Add -- This object adds itself to the given list * + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * LinkClass::Get_Next -- Fetches the next object in list. * + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * LinkClass::Head_Of_List -- Finds the head of the list. * + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * LinkClass::Remove -- Removes the specified object from the list. * + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * LinkClass::Zap -- Forces the link pointers to NULL. * + * LinkClass::operator= -- Assignment operator for linked list class object. * + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "link.h" + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * * + * This copy constructor, unlike the assignment operator, doesn't have to deal with an * + * already initialized and legal link object to the left of the "=". It merely puts the * + * destination object into the same list as the source object. * + * * + * INPUT: link -- Reference to the object on the right of the "=". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(LinkClass const & link) : + Next(0), Prev(0) +{ + /* + ** Add this object to the same list that the copy object + ** resides in. + */ + Add((LinkClass &)link); +} + + +/*********************************************************************************************** + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * * + * This default destructor will remove the object from any linked list it may be part of. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::~LinkClass(void) +{ + Remove(); +} + + +/*********************************************************************************************** + * LinkClass::Zap -- Forces the link pointers to NULL. * + * * + * This routine will "zap" out the link pointers. This is usually necessary when the link * + * pointers start in an undefined state, but we KNOW that they aren't pointing to anything * + * valid. In such a case it becomes necessary to zap them so that when the object is added * + * to a list, it will be added correctly. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void LinkClass::Zap(void) +{ + Next = 0; + Prev = 0; +} + + +/*********************************************************************************************** + * LinkClass::operator= -- Assignment operator for linked list class object. * + * * + * The assignment operator makes sure that the previous and next pointers remain valid. * + * Because this class only consists of pointers, the assignment operator doesn't actually * + * transfer any data from the source object. It merely makes the destination object part * + * of the same list as the source object. In essence, this is transferring information * + * but not the actual values. * + * * + * If the destination object is already part of another list, it is removed from that list * + * before being added to the source object's list. This ensures that either list remains * + * in a valid condition. * + * * + * INPUT: link -- The object to the right of the "=" operator. * + * * + * OUTPUT: Returns a reference to the rightmost object -- per standard assignment rules. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::operator = (LinkClass const & link) +{ + if (&link == this) return(*this); + + Remove(); + Add((LinkClass &)link); + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Get_Next -- Fetches the next object in list. * + * * + * This routine will return with a pointer to the next object in the list. If there are * + * no more objects, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to next object in list or NULL if at end of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Next(void) const +{ + return(Next); +} + + +/*********************************************************************************************** + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * * + * Use this routine to get a pointer to the previous object in the linked list. If there * + * are no previous objects (such as at the head of the list), then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous object in the list or NULL if none. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Prev(void) const +{ + return(Prev); +} + + +/*********************************************************************************************** + * LinkClass::Head_Of_List -- Finds the head of the list. * + * * + * Use this routine to scan for and return a reference to the object at the head of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Head_Of_List(void) +{ + LinkClass * link = this; + while (link->Prev) { + link = link->Prev; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * * + * Use this routine to scan for and return a reference to the object at the end of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the end of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Tail_Of_List(void) +{ + LinkClass * link = this; + while (link->Next) { + link = link->Next; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Add -- This object adds itself to the given list * + * * + * Use this routine to add a link object to the list, but to be added right after the * + * given link. This allows inserting a link in the middle of the chain. A quite necessary * + * ability if the chain is order dependant (e.g., the gadget system). * + * * + * INPUT: list -- gadget object to add this one to * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Save ptr to next gadget. + */ + ptr = list.Next; + + /* + ** Link myself in after 'list'. + */ + list.Next = this; + Prev = &list; + + /* + ** Link myself to next gadget, if there is one. + */ + Next = ptr; + if (ptr) { + ptr->Prev = this; + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * * + * INPUT: list -- the list to make myself the head of * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Head(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Head_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Prev = this; + Next = ptr; + Prev = NULL; + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: the head of the list * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Tail(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Tail_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Next = this; + Prev = ptr; + Next = NULL; + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Remove(void) +{ + LinkClass * head = &Head_Of_List(); + LinkClass * tail = &Tail_Of_List(); + + if (Prev) { + Prev->Next = Next; + } + if (Next) { + Next->Prev = Prev; + } + Prev = 0; + Next = 0; + + if (head==this) { + if (tail==this) { + return(0); + } + return(&tail->Head_Of_List()); + } + return(head); +} + + diff --git a/REDALERT/LINK.H b/REDALERT/LINK.H new file mode 100644 index 000000000..a2981b93d --- /dev/null +++ b/REDALERT/LINK.H @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LINK_H +#define LINK_H + + +/* +** This implements a simple linked list. It is possible to add, remove, and traverse the +** list. Since this is a doubly linked list, it is possible to remove an entry from the +** middle of an existing list. +*/ +class LinkClass +{ + public: + LinkClass(NoInitClass const &) {}; + LinkClass(void) : Next(0), Prev(0) {}; + virtual ~LinkClass(void); + + virtual LinkClass * Get_Next(void) const; + virtual LinkClass * Get_Prev(void) const; + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual LinkClass & Head_Of_List(void); + virtual LinkClass & Tail_Of_List(void); + virtual void Zap(void); + virtual LinkClass * Remove(void); + + LinkClass & operator=(LinkClass const & link); // Assignment operator. + LinkClass(LinkClass const & link); // Copy constructor. + + private: + /* + ** Pointers to previous and next link objects in chain. + */ + LinkClass * Next; + LinkClass * Prev; +}; + +#endif diff --git a/REDALERT/LINT.H b/REDALERT/LINT.H new file mode 100644 index 000000000..c24b5126a --- /dev/null +++ b/REDALERT/LINT.H @@ -0,0 +1,185 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/28/96 * + * * + * Last Update : March 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// overloading data member (e.g., IsToRedraw) +//lint -e1516 + +// function distinguishing error (actually not an error when conversion operators are used). +//lint -e1053 + + +/* +** The current version of Lint doesn't know how to deal with conversion operators. It +** erroneously generates this error message as a result. Unfortunately, we have to throw +** out the baby with the bathwater on this one. +*/ +//lint -e58 -e56 + +/* +** Hiding a non virtual member function is generally ok. +*/ +//lint -e1511 + +/* +** The warning about not initializing a member during construction is +** valid. However, C&C takes advantage of this by using the in place +** new operator during the load game process. Disable the warning, but +** grudgingly. +*/ +//lint -e1401 + +/* +** Disable warning about non virtual base class destructor for these objects. +*/ +//lint -esym(1509,GraphicBufferClass,GraphicViewPortClass,BufferClass,VideoViewPortClass,GetCDClass) +//lint -esym(1509,BasicTimerClass,FlyClass,FuseClass,StageClass,FlasherClass,CargoClass,DoorClass) +//lint -esym(1509,AbstractTypeClass) + +/* +** The "unusual cast" warning is the result of derived casting. This is +** because there is no "dynamic_cast<>" implemented in Watcom (version 10.5). +*/ +//lint -e740 -e571 + +/* +** "function not previously declared inline" is probably not worth noting. +*/ +//lint -e1727 + +/* +** Allow sub-integer loss of precision. This will allow assigning ints to +** chars without warning. +*/ +//lint -e734 + +/* +** Shifting an integer left is always ok. +*/ +//lint -e701 -e703 + +/* +** Allow repeated include files. The body of the include file should be +** coded to ensure that it is processed only once. +*/ +//lint -e537 + +/* +** Implicitly converting an enum to an integer is almost always ok. +*/ +//lint -e641 + +/* +** It is possible to have template functions for "++" and "--" that +** don't require one of the parameters to be a class object. +*/ +//lint -e1042 + +/* +** Redundant declarations are ok. They are a bit harder to maintain, +** but they facilitate keeping modules less interdependant (include +** file wise). +*/ +//lint -e963 -e762 -e763 + +/* +** Not having a default constructor is ok. +*/ +//lint -e1712 + +/* +** Private constructors are ok. In fact, they are necessary if the +** class object must never be instantiated outside of the class +** itself. +*/ +//lint -e1704 + +/* +** Ignoring the return value from a function is ok. It is very +** common for certain side-effect type functions. +*/ +//lint -e534 + +/* +** Implicitly converting from a signed to an unsigned parameter (or +** visa versa) is ok. +*/ +//lint -e732 -e502 -e713 -e737 -eau + +/* +** Allow functions to overload and hide base functions. This is a +** technique of inheritance that handles function parameter changes. +*/ +//lint -e1411 + +/* +** If a switch statement doesn't have a case for every value (enums) +** but it does have a "default" case, then don't warn about it. +*/ +//lint -e788 + +/* +** If bitwize arithmetic is performed on compatible enumeration types, +** then don't complain. Many enums are used in this fashion. +*/ +//lint -e655 -e656 + +/* +** If a data member is not explicitly initialized in the initializer +** list, this is ok. +*/ +//lint -e1542 + +/* +** Calling "new" when not part of an assignment operation can be valid. This +** is true if the "new" operator has been overloaded and the class keeps +** track of itself. +*/ +//lint -e522 + +/* +** A class that is zero bytes long is ok. This is how method classes +** usually work. +*/ +//lint -e1501 + +/* +** Boolean passed to function is ok. +*/ +//lint -e730 + +/* +** Signed/unsigned mix with relational... ignore for now. +*/ +//lint -e574 + diff --git a/REDALERT/LIST.CPP b/REDALERT/LIST.CPP new file mode 100644 index 000000000..e5fa6f723 --- /dev/null +++ b/REDALERT/LIST.CPP @@ -0,0 +1,969 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LIST.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 23, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ListClass::Add -- This object adds itself to the given list * + * ListClass::Add_Head -- This gadget makes itself the head of the given list. * + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * ListClass::Add_Item -- Adds an item to the list box. * + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * ListClass::Add_Tail -- Add myself to the end of the given list. * + * ListClass::Bump -- Bumps the list box up/down one "page". * + * ListClass::Current_Index -- Fetches the current selected index. * + * ListClass::Current_Item -- Fetches pointer to current item string. * + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * ListClass::Draw_Me -- Draws the listbox. * + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * ListClass::Remove -- Removes the specified object from the list. * + * ListClass::Remove_Item -- Remove specified text from list box. * + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * ListClass::Step -- Moves the list view one line in direction specified. * + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * ListClass::~ListClass -- Destructor for list class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ListClass::ListClass -- class constructor * + * * + * INPUT: id button ID * + * * + * x,y upper-left corner, in pixels * + * * + * w,h width, height, in pixels * + * * + * list ptr to array of char strings to list* + * * + * flags, style flags for mouse, style of listbox * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ListClass::ListClass (int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + TextFlags = flags; + IsScrollActive = false; + Tabs = 0; + SelectedIndex = 0; + CurrentTopIndex = 0; + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + + +ListClass::ListClass(ListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + + +void ListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + + +/*********************************************************************************************** + * ListClass::~ListClass -- Destructor for list class objects. * + * * + * This is the destructor for list objects. It handles removing anything it might have * + * allocated. This is typically the scroll bar. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ListClass::~ListClass(void) +{ + Remove_Scroll_Bar(); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(char const * text) +{ + if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * * + * This will add the text as specified by the text number provided, to the list box. * + * The string is added to the end of the list. * + * * + * INPUT: text -- The text number for the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: Once a string is added to the list box in this fashion, there is no method of * + * retrieving the text number as it relates to any particular index in the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(int text) +{ + if (text != TXT_NONE) { + Add_Item(Text_String(text)); + } + return(List.Count() - 1); +} + + +void ListClass::Remove_Item(int index) +{ + if (index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + + +/*********************************************************************************************** + * ListClass::Remove_Item -- Remove specified text from list box. * + * * + * This routine will remove the specified text string from the list box. * + * * + * INPUT: text -- Pointer to the string to remove. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text pointer passed into this routine MUST be the same text pointer that * + * was used to add the string to the list. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Remove_Item(char const * text) +{ + if (text) { + Remove_Item(List.ID(text)); + } +} + + +/*************************************************************************** + * ListClass::Action -- If clicked on, do this! * + * * + * INPUT: int flags -- combination of mouse flags indicating * + * what action to take. * + * * + * OUTPUT: bool result. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +int ListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + if (SelectedIndex == -1) SelectedIndex = 0; + } + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ListClass::Draw_Me -- Draws the listbox. * + * * + * This routine will render the listbox. * + * * + * INPUT: forced -- Should the listbox be redrawn even if it already thinks it doesn't * + * need to be? This is true when something outside of the gadget system * + * has trashed the screen. * + * * + * OUTPUT: Was the listbox redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Bump -- Bumps the list box up/down one "page". * + * * + * Use this routine to adjust the "page" that is being viewed in the list box. The view * + * will move up or down (as specified) one page (screen full) of text strings. * + * * + * INPUT: up -- Should the adjustment be up? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step -- Moves the list view one line in direction specified. * + * * + * This routine will move the current view "page" one line in the direction specified. * + * * + * INPUT: up -- Should the view be moved upward? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * * + * This routine will fetch an item string from the list box. The item fetched can be any * + * one of the ones in the list. * + * * + * INPUT: index -- The index to examine and return the text pointer from. * + * * + * OUTPUT: Returns with the text pointer to the string at the index position specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Get_Item(int index) const +{ + if (List.Count() == 0) { + return NULL; + } + index = min(index, List.Count()-1); + return(List[index]); +} + + +/*********************************************************************************************** + * ListClass::Current_Item -- Fetches pointer to current item string. * + * * + * This routine will fetch a pointer to the currently selected item's text. * + * * + * INPUT: none * + * * + * OUTPUT: Return with pointer to currently selected text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Current_Item(void) const +{ + if (List.Count() <= SelectedIndex) { + return(0); + } + return(List[SelectedIndex]); +} + + +/*********************************************************************************************** + * ListClass::Current_Index -- Fetches the current selected index. * + * * + * This routine will fetch the index number for the currently selected line. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the index of the currently selected line. This ranges from zero to * + * the number of items in the list minus one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + + +/*********************************************************************************************** + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * * + * key -- The key value at the time of the event. * + * * + * whom -- Which gadget is being touched. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + + +/*********************************************************************************************** + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * * + * This routine is used to set the line that will be at the top of the list view. This is * + * how the view can be scrolled up and down. This does not affect the currently selected * + * item. * + * * + * INPUT: index -- The line (index) to move to the top of the list view. * + * * + * OUTPUT: bool; Was the view actually changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, max(0, List.Count() - LineCount)); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * * + * This routine will add a scroll bar (with matching arrows) to the list box. They are * + * added to the right edge and cause the interior of the list box to become narrower. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar added? * + * * + * WARNINGS: The list box becomes narrower when the scroll bar is added. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * * + * Use this routine to remove any attached scroll bar to this list box. If the scroll bar * + * is not present, then no action occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * * + * This sets the tab stop list to be used for text printing. It specifies a series of * + * pixel offsets for each tab stop. The offsets are from the starting pixel position that * + * the text begins at. * + * * + * INPUT: tabs -- Pointer to a list of tab pixel offsets. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only a pointer to the tabs is recorded by the ListClass object. Make sure that * + * the list remains intact for the duration of the existence of this object. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + + +/*********************************************************************************************** + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, scheme, TBLACK, flags, width, Tabs); + +} + + +/*********************************************************************************************** + * ListClass::Add -- Adds myself to list immediately after given object * + * * + * Adds the list box to the chain, immediately after the given object. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * * + * INPUT: object -- Pointer to the object to be added right after this one. * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Head -- Adds myself to head of the given list * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Tail -- Adds myself to tail of given list * + * * + * Adds the list box to the tail of the give chain. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: none * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * ListClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * ListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + + +/*********************************************************************************************** + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * * + * This routine will set the top line of the listbox to the index value specified. * + * * + * INPUT: index -- The index to set the top of the listbox to. * + * * + * OUTPUT: none * + * * + * WARNINGS: The requested index may be adjusted to fit within legal parameters. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 01/23/1996 JLB : Forces selected index to always be zero for a null list. * + *=============================================================================================*/ +void ListClass::Set_Selected_Index(int index) +{ + if (index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } else { + SelectedIndex = 0; + } +} + + +/*********************************************************************************************** + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * * + * This routine will scroll the top line of the listbox in the direction specified. * + * * + * INPUT: step -- The direction (and amount) to adjust the listbox. If negative value, then * + * the top line is scrolled upward. * + * * + * OUTPUT: Returns with the original top line index number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + + +void ListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + + +void ListClass::Set_Selected_Index(char const * text) +{ + if (text && List.Count() > 0) { + for (int index = 0; index < List.Count(); index++) { + if (stricmp(List[index], text) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} diff --git a/REDALERT/LIST.H b/REDALERT/LIST.H new file mode 100644 index 000000000..73b2a5408 --- /dev/null +++ b/REDALERT/LIST.H @@ -0,0 +1,799 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LIST.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LIST_H +#define LIST_H + +#include "control.h" +#include "shapebtn.h" +#include "slider.h" + + +/*************************************************************************** + * ListClass -- Like a Windows ListBox structure * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class ListClass : public ControlClass +{ + public: + ListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + ListClass(ListClass const & list); + virtual ~ListClass(void); + + virtual int Add_Item(char const * text); + virtual int Add_Item(int text); + virtual int Add_Scroll_Bar(void); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual char const * Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual char const * Get_Item(int index) const; + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const *Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. The pointers + ** are stored in EMS. The text that is pointed to may also be in EMS. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + + +template +class TListClass : public ControlClass +{ + public: + TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + TListClass(TListClass const & list); + virtual ~TListClass(void); + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual int Add_Item(T text); + virtual int Add_Scroll_Bar(void); + virtual void Insert_Item(T item); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual T Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + virtual T Get_Item(int index) const {return(List[index]);}; + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(T); + virtual void Remove_Index(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const * Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + +template +TListClass::TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true), + TextFlags(flags), + Tabs(0), + IsScrollActive(false), + SelectedIndex(0), + CurrentTopIndex(0) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + +template +TListClass::TListClass(TListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + +template +void TListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + +template +TListClass::~TListClass(void) +{ + Remove_Scroll_Bar(); +} + +template +void TListClass::Insert_Item(T item) +{ + if (Current_Index() >= Count()) { + List.Add(item); + } else { + List.Add(item); + + /* + ** Move all trailing items upward. + */ + for (int index = List.Count()-1; index >= Current_Index(); index--) { + List[index+1] = List[index]; + } + + /* + ** Insert the new item into the location at the current index. + */ + List[Current_Index()] = item; + } +} + + +template +int TListClass::Add_Item(T text) +{ +// if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } +// } + return(List.Count() - 1); +} + +template +void TListClass::Remove_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + +template +void TListClass::Remove_Item(T text) +{ + Remove_Index(List.ID(text)); +} + +template +int TListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + } + } + return(ControlClass::Action(flags, key)); +} + +template +int TListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box(X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + List[line]->Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// List[index].Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + +template +void TListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +template +void TListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +#ifdef NEVER +template +T TListClass::Get_Item(int index) const +{ + index = min(index, List.Count()); + return(List[index]); +} +#endif + +template +T TListClass::Current_Item(void) const +{ + static T _temp; + if (List.Count() <= SelectedIndex) { + return(_temp); + } + return(List[SelectedIndex]); +} + +template +int TListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + +template +void TListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + +template +int TListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, List.Count() - LineCount); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + +template +int TListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + +template +int TListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + +template +void TListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + +#ifdef NEVER +template +void TListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index], x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} +#endif + +template +LinkClass & TListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + +template +LinkClass & TListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + +template +LinkClass & TListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + +template +GadgetClass * TListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + +template +void TListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } +} + +template +int TListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + +template +void TListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + +template +void TListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/REDALERT/LISTNODE.H b/REDALERT/LISTNODE.H new file mode 100644 index 000000000..679f91da8 --- /dev/null +++ b/REDALERT/LISTNODE.H @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LISTNODE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LISTNODE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/16/96 * + * * + * Last Update : May 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LISTNODE_H +#define LISTNODE_H + +#include +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +//#pragma warn -inl + +/* +** This is a doubly linked list node. Typical use of this node is to derive +** objects from this node. The interface class for this node can be used for +** added convenience. +*/ +class GenericList; +class GenericNode { + public: + GenericNode(void) : NextNode(0), PrevNode(0) {} + ~GenericNode(void) {Unlink();} + GenericNode(GenericNode & node) {node.Link(this);} + GenericNode & operator = (GenericNode & node) { + if (&node != this) { + node.Link(this); + } + return(*this); + } + + void Unlink(void) { + if (Is_Valid()) { + PrevNode->NextNode = NextNode; + NextNode->PrevNode = PrevNode; + PrevNode = 0; + NextNode = 0; + } + } + + GenericList * Main_List(void) const { + GenericNode const * node = this; + while (node->PrevNode) { + node = PrevNode; + } + return((GenericList *)this); + } + void Link(GenericNode * node) { + assert(node != NULL); + node->Unlink(); + node->NextNode = NextNode; + node->PrevNode = this; + if (NextNode) NextNode->PrevNode = node; + NextNode = node; + } + + GenericNode * Next(void) const {return(NextNode);} + GenericNode * Prev(void) const {return(PrevNode);} + bool Is_Valid(void) const {return(this != NULL && NextNode != NULL && PrevNode != NULL);} + + protected: + GenericNode * NextNode; + GenericNode * PrevNode; +}; + + +/* +** This is a generic list handler. It manages N generic nodes. Use the interface class +** to the generic list for added convenience. +*/ +class GenericList { + public: + GenericList(void) { + FirstNode.Link(&LastNode); + } + + GenericNode * First(void) const {return(FirstNode.Next());} + GenericNode * Last(void) const {return(LastNode.Prev());} + bool Is_Empty(void) const {return(!FirstNode.Next()->Is_Valid());} + void Add_Head(GenericNode * node) {FirstNode.Link(node);} + void Add_Tail(GenericNode * node) {LastNode.Prev()->Link(node);} + void Delete(void) {while (FirstNode.Next()->Is_Valid()) delete FirstNode.Next();} + + protected: + GenericNode FirstNode; + GenericNode LastNode; + + private: + GenericList(GenericList & list); + GenericList & operator = (GenericList const &); +}; + + + +/* +** This node class serves only as an "interface class" for the normal node +** object. In order to use this interface class you absolutely must be sure +** that the node is the root base object of the "class T". If it is true that the +** address of the node is the same as the address of the "class T", then this +** interface class will work. You can usually ensure this by deriving the +** class T object from this node. +*/ +template class List; +template +class Node : public GenericNode { + public: + List * Main_List(void) const {return((List *)GenericNode::Main_List());} + T * Next(void) const {return((T *)GenericNode::Next());} + T * Prev(void) const {return((T *)GenericNode::Prev());} + bool Is_Valid(void) const {return(GenericNode::Is_Valid());} +}; + + +/* +** This is an "interface class" for a list of nodes. The rules for the class T object +** are the same as the requirements required of the node class. +*/ +template +class List : public GenericList { + public: + T * First(void) const {return((T*)GenericList::First());} + T * Last(void) const {return((T*)GenericList::Last());} +}; + + +#endif diff --git a/REDALERT/LOADDLG.CPP b/REDALERT/LOADDLG.CPP new file mode 100644 index 000000000..5d7ace181 --- /dev/null +++ b/REDALERT/LOADDLG.CPP @@ -0,0 +1,783 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOADDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.CPP * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * LoadOptionsClass::Process -- main processing routine * + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * LoadOptionsClass::Compare -- for qsort * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for unlink + + +/*********************************************************************************************** + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * * + * INPUT: * + * style style for this load/save dialog (LOAD/SAVE/DELETE) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::LoadOptionsClass(LoadStyleType style) +{ + Style = style; + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::~LoadOptionsClass() +{ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Process -- main processing routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * false = User cancelled, true = operation completed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 250 * RESFACTOR; // dialog width + int d_dialog_h = 156 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); // centered x-coord + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int d_list_w = d_dialog_w - (x_margin * 2); + int d_list_h = 104 * RESFACTOR; + int d_list_x = d_dialog_x + x_margin; + int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin; + + int d_edit_w = d_dialog_w - (x_margin * 2); + int d_edit_h = 13 * RESFACTOR; + int d_edit_x = d_dialog_x + x_margin; + int d_edit_y = d_list_y + d_list_h - (30 * RESFACTOR) + d_margin + d_txt8_h; + +#if (GERMAN | FRENCH) + int d_button_w = 50 * RESFACTOR; +#else + int d_button_w = 40 * RESFACTOR; +#endif + int d_button_h = 13 * RESFACTOR; + int d_button_x = d_dialog_cx - d_button_w - d_margin; + int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 60 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_LOAD = 100, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_CANCEL, + BUTTON_LIST, + BUTTON_EDIT, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + int list_ht = d_list_h; // adjusted list box height + + /* + ** Other Variables + */ + int btn_txt; // text on the 'OK' button + int btn_id; // ID of 'OK' button + int caption; // dialog caption + int game_idx = 0; // index of game to save/load/etc + int game_num = 0; // file number of game to load/save/etc + char game_descr[DESCRIP_MAX] = {0}; // save-game description + char fname[_MAX_NAME+_MAX_EXT]; // for generating filename to delete + int rc; // return code + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + switch (Style) { + case LOAD: + btn_txt = TXT_LOAD_BUTTON; + btn_id = BUTTON_LOAD; + caption = TXT_LOAD_MISSION; + break; + + case SAVE: + btn_txt = TXT_SAVE_BUTTON; + btn_id = BUTTON_SAVE; + caption = TXT_SAVE_MISSION; + list_ht -= 30; + break; + + default: + btn_txt = TXT_DELETE_BUTTON; + btn_id = BUTTON_DELETE; + caption = TXT_DELETE_MISSION; + break; + } + + TextButtonClass button (btn_id, btn_txt, TPF_BUTTON, d_button_x, d_button_y, d_button_w); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + + ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht, + TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + EditClass editbtn (BUTTON_EDIT, game_descr, sizeof(game_descr)-4, TPF_6PT_GRAD|TPF_NOSHADOW, d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + Fill_List(&listbtn); + + /* + ** Do nothing if list is empty. + */ + if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) { + Clear_List(&listbtn); + WWMessageBox().Process(TXT_NO_SAVES); + return(false); + } + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + listbtn.Add_Tail(*commands); + if (Style == SAVE) { + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + } + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + cancel = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w); + + if (Style == SAVE) { + Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx, + d_edit_y - d_txt8_h, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER); + } + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus if this is the save dialog. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime && Style == SAVE) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN || input == (BUTTON_EDIT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + switch (Style) { + case SAVE: + input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON); + cancelbtn.Turn_Off(); +// cancelbtn.IsOn = false; + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_SAVE); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case LOAD: + input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_LOAD); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case WWDELETE: + input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + } + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_LOAD | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (Files[game_idx]->Valid) { + + /* + ** Start a timer before we load the game + */ + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_LOADING, TXT_NONE); + Theme.Fade_Out(); + rc = Load_Game(game_num); + + /* + ** Make sure the message says on the screen at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + if (!rc) { + WWMessageBox().Process(TXT_ERROR_LOADING_GAME); + } else { + Speak(VOX_LOAD1); + while (Is_Speaking()) { + Call_Back(); + } + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); +// Set_Palette(GamePalette); + Show_Mouse(); + process = false; + } + } else { + WWMessageBox().Process(TXT_OBSOLETE_SAVEGAME); + } + break; + + /* + ** Save: Save the game & exit the dialog + */ + case (BUTTON_EDIT | KN_BUTTON): + + case (BUTTON_SAVE | KN_BUTTON): + if (!strlen(game_descr)) { + WWMessageBox().Process(TXT_MUSTENTER_DESCRIPTION); + firsttime = true; + display = true; + break; + } + game_idx = listbtn.Current_Index(); + if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) { + WWMessageBox().Process(TXT_SPACE_CANT_SAVE); + firsttime = true; + display = true; + break; + } + + game_num = Files[game_idx]->Num; + if (!Save_Game(game_num, game_descr)) { + WWMessageBox().Process(TXT_ERROR_SAVING_GAME); + } else { + Speak(VOX_SAVE1); + while (Is_Speaking()) { + Call_Back(); + } + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE); + + /* + ** Delay to let the user read the message + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + } + process = false; + break; + + /* + ** Delete: delete the file & stay in the dialog, to allow the user + ** to delete multiple files. + */ + case (BUTTON_DELETE | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (WWMessageBox().Process(TXT_DELETE_FILE_QUERY, TXT_YES, TXT_NO)==0) { + sprintf(fname, "SAVEGAME.%03d", game_num); + unlink(fname); + Clear_List(&listbtn); + Fill_List(&listbtn); + if (listbtn.Count() == 0) { + process = false; + } else { + ToggleClass * toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { +// toggle->IsOn = false; + toggle->Turn_Off(); + toggle->IsPressed = false; + toggle->Flag_To_Redraw(); + } + } + } + display = true; + break; + + /* + ** If the user clicks on the list, see if the there is a new current + ** item; if so, and if we're in SAVE mode, copy the list item into + ** the save-game description field. + */ + case (BUTTON_LIST | KN_BUTTON): + if (Style != SAVE) { + break; + } + + if (listbtn.Count() && listbtn.Current_Index() != game_idx) { + game_idx = listbtn.Current_Index(); + + /* + ** Copy the game's description, UNLESS it's the empty slot; if + ** it is, set the edit buffer to empty. + */ + if (game_idx != 0) { + strcpy(game_descr, listbtn.Get_Item(game_idx)); + + /* + ** Strip any leading parenthesis off of the description. + */ + if (game_descr[0] == '(') { + char * ptr = strchr(game_descr, ')'); + if (ptr != NULL) { + strcpy(game_descr, ptr+1); + strtrim(game_descr); + } + } + + } else { + game_descr[0] = 0; + } + editbtn.Set_Text(game_descr, 40); + } + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + Clear_List(&listbtn); + + if (cancel) return(false); + + return(true); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * * + * This step is essential, because it frees all the strings allocated for list items. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void LoadOptionsClass::Clear_List(ListClass * list) +{ + /* + ** For every item in the list, free its buffer & remove it from the list. + */ + int j = list->Count(); + for (int i = 0; i < j; i++) { + list->Remove_Item(list->Get_Item(0)); + } + + /* + ** Clear the array of game numbers + */ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 06/25/1995 JLB : Shows which saved games are "(old)". * + *=============================================================================================*/ +void LoadOptionsClass::Fill_List(ListClass * list) +{ +#if(0) //PG + FileEntryClass * fdata; // for adding entries to 'Files' + char descr[DESCRIP_MAX+32]; + unsigned scenario; // scenario # + HousesType house; // house + struct find_t ff; // for _dos_findfirst + int id; + + /* + ** Make sure the list is empty + */ + Clear_List(list); + + /* + ** Add the Empty Slot entry + */ + if (Style == SAVE) { + fdata = new FileEntryClass; + strcpy(fdata->Descr, Text_String(TXT_EMPTY_SLOT)); + fdata->DateTime = 0xffffffff; // will always be first + Files.Add(fdata); + } + + /* + ** Find all savegame files + */ + int rc = _dos_findfirst("SAVEGAME.*", _A_NORMAL, &ff); + + while (!rc) { + + if (stricmp(ff.name, NET_SAVE_FILE_NAME) != 0) { + + /* + ** Extract the game ID from the filename + */ + id = Num_From_Ext(ff.name); + + /* + ** get the game's info; if success, add it to the list + */ + bool ok = Get_Savefile_Info(id, descr, &scenario, &house); + + fdata = new FileEntryClass; + + fdata->Descr[0] = '\0'; + if (!ok) { + strcpy(fdata->Descr, Text_String(TXT_OLD_GAME)); + } else { + if (house == HOUSE_USSR || house == HOUSE_UKRAINE) { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_SOVIET)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_SOVIET)); +#endif + } else { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_ALLIES)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_ALLIES)); +#endif + } + } + strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1); + fdata->Valid = ok; + fdata->Scenario = scenario; + fdata->House = house; + fdata->Num = id; + fdata->DateTime = (((unsigned long)ff.wr_date) << 16) | (unsigned long)ff.wr_time; + Files.Add(fdata); + } + + /* + ** Find the next file + */ + rc = _dos_findnext(&ff); + } + + /* + ** If saving a game, determine a unique file ID for the empty slot + */ + if (Style == SAVE) { + /* + ** Find an un-used number to associate with the Empty Slot by looking in + ** GameNum for each number from 0 to 'N', where 'N' is the # of entries + ** in the list; if any number isn't found, use that number; otherwise, + ** use 'N + 1'. + */ + for (int i = 0; i < Files.Count(); i++) { // i = the # we're searching for + id = -1; // mark as 'not found' + for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's + if (Files[j]->Num==i) { // if found, mark as found + id = j; + break; + } + } + if (id == -1) break; // if ID not found, use this one + } + + Files[0]->Num = i; // set the empty slot's ID + } + + /* + ** Now sort the list in order of Date/Time (newest first, oldest last) + */ + qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare); + + /* + ** Now add every file's name to the list box + */ + for (int i = 0; i < Files.Count(); i++) { + list->Add_Item(Files[i]->Descr); + } +#endif +} + + +/*********************************************************************************************** + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * * + * INPUT: * + * fname filename to parse * + * * + * OUTPUT: * + * File number for this name. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Num_From_Ext(char * fname) +{ + char ext[_MAX_EXT]; + + _splitpath(fname, NULL, NULL, NULL, ext); + int num = atoi(ext + 1); // skip the '.' + return(num); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Compare(const void * p1, const void * p2) +{ + class FileEntryClass * fe1, * fe2; + + fe1 = *((class FileEntryClass **)p1); + fe2 = *((class FileEntryClass **)p2); + + if (fe1->DateTime > fe2->DateTime) return(-1); + if (fe1->DateTime < fe2->DateTime) return(1); + return(0); +} + diff --git a/REDALERT/LOADDLG.H b/REDALERT/LOADDLG.H new file mode 100644 index 000000000..ea2b68017 --- /dev/null +++ b/REDALERT/LOADDLG.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOADDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.H * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : March 19, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOADDLG_H +#define LOADDLG_H + +class FileEntryClass { + public: + char Descr[80]; // save-game description + unsigned Scenario; // scenario # + HousesType House; // house + int Num; // save file number (from the extension) + unsigned long DateTime; // date/time stamp of file + bool Valid; // Is the scenario valid? +}; + +class LoadOptionsClass +{ + public: + /* + ** This defines the style of the dialog + */ + typedef enum OperationModeEnum { + NONE = 0, + LOAD, + SAVE, + WWDELETE + } LoadStyleType; + + LoadOptionsClass (LoadStyleType style = LoadOptionsClass::NONE); + ~LoadOptionsClass (); + int Process (void); + + + protected: + /* + ** Internal routines + */ + void Clear_List (ListClass *list); // clears the list & game # array + void Fill_List (ListClass *list); // fills the list & game # array + int Num_From_Ext (char *fname); // translates filename to file # + static int Compare(const void *p1, const void *p2); // for qsort() + + /* + ** This is the requested style of the dialog + */ + LoadStyleType Style; + + /* + ** This is an array of pointers to FileEntryClass objects. These objects + ** are allocated on the fly as files are found, and pointers to them are + ** added to the vector list. Thus, all the objects must be free'd before + ** the vector list is cleared. This list is used for sorting the files + ** by date/time. + */ + DynamicVectorClass Files; +}; + + +#endif + diff --git a/REDALERT/LOGIC.CPP b/REDALERT/LOGIC.CPP new file mode 100644 index 000000000..ed4b6fa7e --- /dev/null +++ b/REDALERT/LOGIC.CPP @@ -0,0 +1,474 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOGIC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 27, 1993 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LogicClass::AI -- Handles AI logic processing for game objects. * + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "logic.h" +#include "vortex.h" + +static unsigned FramesPerSecond=0; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * * + * This is a debugging support routine. It displays the current state of the logic class * + * to the monochrome monitor. It assumes that it is being called once per second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per second. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 01/26/1996 JLB : Prints game time value. * + *=============================================================================================*/ +void LogicClass::Debug_Dump(MonoClass * mono) const +{ + #define RECORDCOUNT 40 + #define RECORDHEIGHT 21 + static int _framecounter = 0; + + static bool first = true; + if (first) { + first = false; + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_STRESS)); + } + +//mono->Set_Cursor(0,0);mono->Printf("%d", AllowVoice); + + + _framecounter++; + mono->Set_Cursor(1, 1);mono->Printf("%ld", (long)Scen.Timer); + mono->Set_Cursor(10, 1);mono->Printf("%3d", FramesPerSecond); + mono->Set_Cursor(1, 3);mono->Printf("%02d:%02d:%02d", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND); + + mono->Set_Cursor(1, 11);mono->Printf("%3d", Units.Count()); + mono->Set_Cursor(1, 12);mono->Printf("%3d", Infantry.Count()); + mono->Set_Cursor(1, 13);mono->Printf("%3d", Aircraft.Count()); + mono->Set_Cursor(1, 14);mono->Printf("%3d", Vessels.Count()); + mono->Set_Cursor(1, 15);mono->Printf("%3d", Buildings.Count()); + mono->Set_Cursor(1, 16);mono->Printf("%3d", Terrains.Count()); + mono->Set_Cursor(1, 17);mono->Printf("%3d", Bullets.Count()); + mono->Set_Cursor(1, 18);mono->Printf("%3d", Anims.Count()); + mono->Set_Cursor(1, 19);mono->Printf("%3d", Teams.Count()); + mono->Set_Cursor(1, 20);mono->Printf("%3d", Triggers.Count()); + mono->Set_Cursor(1, 21);mono->Printf("%3d", TriggerTypes.Count()); + mono->Set_Cursor(1, 22);mono->Printf("%3d", Factories.Count()); + + SpareTicks = min((long)SpareTicks, (long)TIMER_SECOND); + + /* + ** CPU utilization record. + */ + mono->Sub_Window(15, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%3d%%", ((TIMER_SECOND-SpareTicks)*100) / TIMER_SECOND); + + /* + ** Update the frame rate log. + */ + mono->Sub_Window(22, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", FramesPerSecond); + + /* + ** Update the findpath calc record. + */ + mono->Sub_Window(50, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", PathCount); + PathCount = 0; + + /* + ** Update the cell redraw record. + */ + mono->Sub_Window(29, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", CellCount); + CellCount = 0; + + /* + ** Update the target scan record. + */ + mono->Sub_Window(36, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", TargetScan); + TargetScan = 0; + + /* + ** Sidebar redraw record. + */ + mono->Sub_Window(43, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", SidebarRedraws); + SidebarRedraws = 0; + + /* + ** Update the CPU utilization chart. + */ + mono->Sub_Window(15, 13, 63, 10); + mono->Pan(1); + mono->Sub_Window(15, 13, 64, 10); + int graph = RECORDHEIGHT * fixed(TIMER_SECOND-SpareTicks, TIMER_SECOND); + for (int row = 1; row < RECORDHEIGHT; row += 2) { + static char _barchar[4] = {' ', 220, 0, 219}; + char str[2]; + int index = 0; + + index |= (graph >= row) ? 0x01 : 0x00; + index |= (graph >= row+1) ? 0x02: 0x00; + + str[1] = '\0'; + str[0] = _barchar[index]; + mono->Text_Print(str, 62, 9-(row/2)); + } + mono->Sub_Window(); + + + SpareTicks = 0; + FramesPerSecond = 0; +} +#endif + + +/*********************************************************************************************** + * LogicClass::AI -- Handles AI logic processing for game objects. * + * * + * This routine is used to perform the AI processing for all game objects. This includes * + * all houses, factories, objects, and teams. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * + * 12/23/1994 JLB : Ensures that no object gets skipped if it was deleted. * + *=============================================================================================*/ +void LogicClass::AI(void) +{ + int index; + + FramesPerSecond++; + + /* + ** Fading to B&W or color due to the chronosphere is handled here. + */ + Scen.Do_Fade_AI(); + + /* + ** Handle any general timer trigger events. + */ + for (LogicTriggerID = 0; LogicTriggerID < LogicTriggers.Count(); LogicTriggerID++) { + TriggerClass * trig = LogicTriggers[LogicTriggerID]; + + /* + ** Global changed trigger event might be triggered. + */ + if (Scen.IsGlobalChanged) { + if (trig->Spring(TEVENT_GLOBAL_SET)) continue; + if (trig->Spring(TEVENT_GLOBAL_CLEAR)) continue; + } + + /* + ** Bridge change event. + */ + if (Scen.IsBridgeChanged) { + if (trig->Spring(TEVENT_ALL_BRIDGES_DESTROYED)) continue; + } + + /* + ** General time expire trigger events can be sprung without warning. + */ + if (trig->Spring(TEVENT_TIME)) continue; + + /* + ** The mission timer expiration trigger event might spring if the timer is active + ** but at a value of zero. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + if (trig->Spring(TEVENT_MISSION_TIMER_EXPIRED)) continue; + } + } + + if (Scen.MissionTimer.Is_Active()) { + long secs = Scen.MissionTimer / TICKS_PER_SECOND; + long mins = secs / 60; + long hours = mins / 60; + secs %= 60; + mins %= 60; + + /* + ** Speak mission timer reminders. + */ + VoxType vox = VOX_NONE; + if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; + if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; + if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; + if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; + if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; + if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; + if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; + if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; + if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; + if (vox != VOX_NONE) { + Speak(vox); + Map.FlasherTimer = 7; + } + } + + /* + ** Clean up any status values that were maintained only for logic trigger + ** purposes. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + Scen.MissionTimer.Stop(); + Map.Flag_To_Redraw(true); // Used only to cause tabs to redraw in new state. + } + Scen.IsGlobalChanged = false; + Scen.IsBridgeChanged = false; + /* + ** Shadow creeping back over time is handled here. + */ + if (Special.IsShadowGrow && Rule.ShroudRate != 0 && Scen.ShroudTimer == 0) { + Scen.ShroudTimer = TICKS_PER_MINUTE * Rule.ShroudRate; + + /* + ** Do this for all players in Client/Server multiplayer. ST - 8/9/2019 10:23AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + Map.Encroach_Shadow(PlayerPtr); + } else { + for (int i=0 ; iPlayer.ID); + if (player_ptr && player_ptr->IsHuman) { + Map.Encroach_Shadow(player_ptr); + } + } + } + } + + /* + ** Team AI is processed. + */ + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->AI(); + } + + /* + ** If there's a time quake, handle it here. + */ + if (TimeQuake) { + Sound_Effect(VOC_KABOOM15); + Shake_The_Screen(8); + } + + ChronalVortex.AI(); + /* + ** AI for all sentient objects is processed. + */ + for (index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + + BStart(BENCH_AI); + obj->AI(); + BEnd(BENCH_AI); + + if (TimeQuake && obj != NULL && obj->IsActive && !obj->IsInLimbo && obj->Strength) { + int damage = (int)obj->Class_Of().MaxStrength * Rule.QuakeDamagePercent; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (TimeQuakeCenter) { + if(::Distance(obj->As_Target(),TimeQuakeCenter)/256 < MTankDistance) { + switch(obj->What_Am_I()) { + case RTTI_INFANTRY: + damage = QuakeInfantryDamage; + break; + case RTTI_BUILDING: + damage = QuakeBuildingDamage * (int)obj->Class_Of().MaxStrength; + break; + default: + damage = QuakeUnitDamage * (int)obj->Class_Of().MaxStrength; + break; + } + if (damage) { + obj->Clicked_As_Target(HOUSE_COUNT); // 2019/09/20 JAS - Added record of who clicked on the object, HOUSE_COUNT is used to mark for all houses + new AnimClass(ANIM_MINE_EXP1, obj->Center_Coord()); + } + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } else { + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } +#else + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); +#endif + } + /* + ** If the object was destroyed in the process of performing its AI, then + ** adjust the index so that no object gets skipped. + */ + if (obj != (*this)[index]) { + index--; + } + } + HouseClass::Recalc_Attributes(); + + /* + ** Map related logic is performed. + */ + Map.Logic(); + + /* + ** Factory processing is performed. + */ + for (index = 0; index < Factories.Count(); index++) { + Factories.Ptr(index)->AI(); + } + + /* + ** House processing is performed. + */ +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + { + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } + else + { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } +#else // AI() is called redundantly 12 times in multiplayer games here. ajw + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Session.Type != GAME_NORMAL && Scen.AutoSonarTimer == 0 ) + { + if( bAutoSonarPulse ) + { + Map.Activate_Pulse(); + Sound_Effect(VOC_SONAR); + bAutoSonarPulse = false; + } +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40; + Scen.AutoSonarTimer = AUTOSONAR_PERIOD; + } +#endif +} + + +/*********************************************************************************************** + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * * + * This routine is called when the specified target object is about to be removed from the * + * game system and all references to it must be severed. The only thing that the logic * + * system looks for in this case is to see if the target refers to a trigger and if so, * + * it scans through the trigger list and removes all references to it. * + * * + * INPUT: target -- The target to remove from the sytem. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void LogicClass::Detach(TARGET target, bool ) +{ + /* + ** Remove any triggers from the logic trigger list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + if (As_Trigger(target) == LogicTriggers[index]) { + LogicTriggers.Delete(index); + index--; + } + } + } +} + + +/*********************************************************************************************** + * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were * + * recently created * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 8/19/2019 5:47PM ST : Created. * + *=============================================================================================*/ +void LogicClass::Clear_Recently_Created_Bits(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + obj->IsRecentlyCreated = false; + } +} \ No newline at end of file diff --git a/REDALERT/LOGIC.H b/REDALERT/LOGIC.H new file mode 100644 index 000000000..c3ff05085 --- /dev/null +++ b/REDALERT/LOGIC.H @@ -0,0 +1,58 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOGIC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 29, 1994 * + * * + * Last Update : May 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "layer.h" + +/*********************************************************************************************** +** Game logic processing is controlled by this class. The graphic and AI logic is handled +** separately so that on slower machines, the graphic display is least affected. +*/ +class LogicClass : public LayerClass +{ + public: + void AI(void); + void Detach(TARGET target, bool all=true); + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Added. ST - 8/19/2019 5:46PM + */ + void Clear_Recently_Created_Bits(void); +}; +#endif \ No newline at end of file diff --git a/REDALERT/LZO.H b/REDALERT/LZO.H new file mode 100644 index 000000000..31e29c2cf --- /dev/null +++ b/REDALERT/LZO.H @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZO_H +#define _LZO_H + +#include "lzoconf.h" + +int lzo1x_1_compress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp wrkmem ); + + +int lzo1x_decompress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp ); + + + + + +#endif diff --git a/REDALERT/LZO1X.H b/REDALERT/LZO1X.H new file mode 100644 index 000000000..e7b0b60db --- /dev/null +++ b/REDALERT/LZO1X.H @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x.h -- public interface of the LZO1X compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZO1X_H +#define __LZO1X_H + +#include "lzoconf.h" + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// +************************************************************************/ + +/* Memory required for the wrkmem parameter. + * When the required size is 0, you can also pass a NULL pointer. + */ + +#define LZO1X_MEM_COMPRESS ((lzo_uint) (16384L * sizeof(lzo_byte *))) +#define LZO1X_MEM_DECOMPRESS (0) + + +/* fast decompression */ +LZO_EXTERN(int) +lzo1x_decompress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + +/* safe decompression with overrun testing */ +LZO_EXTERN(int) +lzo1x_decompress_x ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + + +/*********************************************************************** +// +************************************************************************/ + +LZO_EXTERN(int) +lzo1x_1_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/*********************************************************************** +// better compression ratio at the cost of more memory and time +************************************************************************/ + +#define LZO1X_999_MEM_COMPRESS ((lzo_uint) (14 * 16384L * sizeof(short))) + +LZO_EXTERN(int) +lzo1x_999_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/REDALERT/LZO1X_C.CPP b/REDALERT/LZO1X_C.CPP new file mode 100644 index 000000000..c541d7a7d --- /dev/null +++ b/REDALERT/LZO1X_C.CPP @@ -0,0 +1,348 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x_c.c -- standalone LZO1X-1 compressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#ifndef NDEBUG +#define NDEBUG +#endif +#include +#include "lzo_conf.h" + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define M1_MAX_OFFSET 0x0400 +#if defined(LZO1X) +#define M2_MAX_OFFSET 0x0800 +#elif defined(LZO1Y) +#define M2_MAX_OFFSET 0x0400 +#endif +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + + +#define _DV2(p,shift1,shift2) \ + (((( (lzo_uint)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0]) +#define DVAL_NEXT(dv,p) \ + dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint)(p[2]) << (2*5))) +#define _DV(p,shift) _DV2(p,shift,shift) +#define DVAL_FIRST(dv,p) dv = _DV((p),5) +#define _DINDEX(dv,p) ((40799u * (dv)) >> 5) +#define DINDEX(dv,p) (((_DINDEX(dv,p)) & 0x3fff) << 0) +#define UPDATE_D(dict,cycle,dv,p) dict[ DINDEX(dv,p) ] = (p) +#define UPDATE_I(dict,cycle,index,p) dict[index] = (p) + + +/*********************************************************************** +// compress a block of data. +************************************************************************/ + +static int do_compress(const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + register const lzo_byte *ip; + lzo_uint dv; + lzo_byte *op; + const lzo_byte * const in_end = in + in_len; + const lzo_byte * const ip_end = in + in_len - 9 - 4; + const lzo_byte *ii; + const lzo_bytepp const dict = (const lzo_bytepp) wrkmem; + + op = out; + ip = in; + ii = ip; + + DVAL_FIRST(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + + while (1) { + register const lzo_byte *m_pos; + lzo_uint m_len; + lzo_ptrdiff_t m_off; + lzo_uint lit; + + lzo_uint dindex = DINDEX(dv,ip); + m_pos = dict[dindex]; + UPDATE_I(dict,cycle,dindex,ip); + + + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) { + } +#if defined(LZO_UNALIGNED_OK_2) + else + if (* (unsigned short *) m_pos != * (unsigned short *) ip) +#else + else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } else { + if (m_pos[2] == ip[2]) { + lit = ip - ii; + m_pos += 3; + if (m_off <= M2_MAX_OFFSET) + goto match; + + /* better compression, but slower */ + if (lit == 3) { + assert(op - 2 > out); op[-2] |= LZO_BYTE(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + + if (*m_pos == ip[3]) { + goto match; + } + } else { + /* still need a better way for finding M1 matches */ + } + } + + + /* a literal */ + ++ip; + if (ip >= ip_end) { + break; + } + DVAL_NEXT(dv,ip); + continue; + + + /* a match */ + +match: + + /* store current literal run */ + if (lit > 0) { + register lzo_uint t = lit; + + if (t <= 3) { + assert(op - 2 > out); + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + + do { + *op++ = *ii++; + } while (--t > 0); + } + + + /* code the match */ +code_match: + assert(ii == ip); + ip += 3; + if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ || + *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) + { + --ip; + m_len = ip - ii; + assert(m_len >= 3); assert(m_len <= 8); + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); + } else { + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; + } + } + } else { + const lzo_byte *end; + end = in_end; + while (ip < end && *m_pos == *ip) { + m_pos++; + ip++; + } + m_len = (ip - ii); + assert(m_len >= 3); + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) { + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + } else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + if (m_len <= 9) { + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + } else { + m_len -= 9; + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11)); +m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = LZO_BYTE(m_len); + } + } + +m3_m4_offset: + *op++ = LZO_BYTE((m_off & 63) << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + + ii = ip; + if (ip >= ip_end) { + break; + } + DVAL_FIRST(dv,ip); + } + + /* store final literal run */ + if (in_end - ii > 0) { + register lzo_uint t = in_end - ii; + + if (op == out && t <= 238) { + *op++ = LZO_BYTE(17 + t); + } else { + if (t <= 3) { + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + } + do { + *op++ = *ii++; + } while (--t > 0); + } + + *out_len = op - out; + return LZO_E_OK; +} + + +/*********************************************************************** +// public entry point +************************************************************************/ + +int lzo1x_1_compress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + lzo_byte *op = out; + int r = LZO_E_OK; + + if (in_len <= 0) + *out_len = 0; + else if (in_len <= 9 + 4) + { + *op++ = LZO_BYTE(17 + in_len); + do *op++ = *in++; while (--in_len > 0); + *out_len = op - out; + } + else + r = do_compress(in,in_len,out,out_len,wrkmem); + + if (r == LZO_E_OK) + { + op = out + *out_len; + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + *out_len += 3; + } + + return r; +} + + +/* +vi:ts=4 +*/ diff --git a/REDALERT/LZO1X_D.CPP b/REDALERT/LZO1X_D.CPP new file mode 100644 index 000000000..39dbb5859 --- /dev/null +++ b/REDALERT/LZO1X_D.CPP @@ -0,0 +1,205 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x_d.c -- standalone LZO1X decompressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#ifndef NDEBUG +#define NDEBUG +#endif +#include + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + +#if 1 +# define TEST_IP 1 +#else +# define TEST_IP (ip < ip_end) +#endif + + +/*********************************************************************** +// decompress a block of data. +************************************************************************/ + +int lzo1x_decompress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp ) +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; + register const lzo_byte *m_pos; + const lzo_byte * const ip_end = in + in_len; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + goto first_literal_run; + } + + while (TEST_IP) + { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + if (t == 0) + { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + /* copy literals */ + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; +first_literal_run: + do *op++ = *ip++; while (--t > 0); + + + t = *ip++; + + if (t >= 16) + goto match; +#if defined(LZO1X) + m_pos = op - 1 - 0x800; +#elif defined(LZO1Y) + m_pos = op - 1 - 0x400; +#endif + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++; + goto match_done; + + + /* handle matches */ + while (TEST_IP) + { + if (t < 16) /* a M1 match */ + { + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; + } + else + { +match: + if (t >= 64) /* a M2 match */ + { + m_pos = op - 1; +#if defined(LZO1X) + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#endif + } + else if (t >= 32) /* a M3 match */ + { + t &= 31; + if (t == 0) + { + t = 31; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } + else /* a M4 match */ + { + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) + { + t = 7; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +match_done: + t = ip[-2] & 3; + if (t == 0) + break; + /* copy literals */ + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } + } + + /* ip == ip_end and no EOF code was found */ + + //Unreachable - ST 9/5/96 5:07PM + //*out_len = op - out; + //return (ip == ip_end ? LZO_E_EOF_NOT_FOUND : LZO_E_ERROR); + +eof_found: + assert(t == 1); + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : LZO_E_ERROR); +} + + +/* +vi:ts=4 +*/ diff --git a/REDALERT/LZOCONF.H b/REDALERT/LZOCONF.H new file mode 100644 index 000000000..953f83f83 --- /dev/null +++ b/REDALERT/LZOCONF.H @@ -0,0 +1,214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzoconf.h -- configuration for the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZOCONF_H +#define __LZOCONF_H + +#define LZO_VERSION 0x0200 +#define LZO_VERSION_STRING "0.20" +#define LZO_VERSION_DATE "11 Aug 1996" + + +#include /* CHAR_BIT, UINT_MAX, ULONG_MAX */ +#if !defined(CHAR_BIT) || (CHAR_BIT != 8) +# error invalid CHAR_BIT +#endif + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// defines +************************************************************************/ + +#if defined(__MSDOS__) || defined(MSDOS) +# define __LZO_MSDOS +# if (UINT_MAX < 0xffffffffL) +# define __LZO_MSDOS16 +# endif +#endif + + +/*********************************************************************** +// integral and pointer types +************************************************************************/ + +/* Unsigned type with 32 bits or more */ +#if (UINT_MAX >= 0xffffffffL) + typedef unsigned int lzo_uint; + typedef int lzo_int; +# define LZO_UINT_MAX UINT_MAX +#elif (ULONG_MAX >= 0xffffffffL) + typedef unsigned long lzo_uint; + typedef long lzo_int; +# define LZO_UINT_MAX ULONG_MAX +#else +# error lzo_uint +#endif + + +/* Memory model that allows to access memory at offsets of lzo_uint. + * Huge pointers (16 bit MSDOS) are somewhat slow, but they work + * fine and I really don't care about 16 bit compiler + * optimizations nowadays. + */ +#if (LZO_UINT_MAX <= UINT_MAX) +# define __LZO_MMODEL +#elif defined(__LZO_MSDOS16) +# define __LZO_MMODEL huge +# define __LZO_ENTRY __cdecl +#else +# error __LZO_MMODEL +#endif + + +/* no typedef here because of const-pointer issues */ +#define lzo_byte unsigned char __LZO_MMODEL +#define lzo_voidp void __LZO_MMODEL * +#define lzo_bytep unsigned char __LZO_MMODEL * +#define lzo_uintp lzo_uint __LZO_MMODEL * +#define lzo_intp lzo_int __LZO_MMODEL * +#define lzo_voidpp lzo_voidp __LZO_MMODEL * +#define lzo_bytepp lzo_bytep __LZO_MMODEL * + + +/* Unsigned type that can store all bits of a lzo_voidp */ +typedef unsigned long lzo_ptr_t; + +/* Align a pointer on a boundary that is a multiple of 'size' */ +#define LZO_ALIGN(ptr,size) \ + ((lzo_voidp) (((lzo_ptr_t)(ptr) + (size)-1) & ~((lzo_ptr_t)((size)-1)))) + + +/*********************************************************************** +// function types +************************************************************************/ + +//#ifdef __cplusplus +//# define LZO_EXTERN_C extern "C" +//#else +# define LZO_EXTERN_C extern +//#endif + + +#if !defined(__LZO_ENTRY) /* calling convention */ +# define __LZO_ENTRY +#endif +#if !defined(__LZO_EXPORT) /* DLL export (and maybe size) information */ +# define __LZO_EXPORT +#endif + +#if !defined(LZO_EXTERN) +# define LZO_EXTERN(_rettype) LZO_EXTERN_C _rettype __LZO_ENTRY __LZO_EXPORT +#endif + + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_compress_t) ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_decompress_t)( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/* a progress indicator callback function */ +typedef void __LZO_ENTRY +(__LZO_EXPORT *lzo_progress_callback_t)(lzo_uint,lzo_uint); + + +/*********************************************************************** +// error codes and prototypes +************************************************************************/ + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_NOT_COMPRESSIBLE (-2) /* not used right now */ +#define LZO_E_EOF_NOT_FOUND (-3) +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_OUT_OF_MEMORY (-7) /* not used right now */ + + +/* this should be the first function you call. Check the return code ! */ +LZO_EXTERN(int) lzo_init(void); + +/* version functions (useful for shared libraries) */ +LZO_EXTERN(unsigned) lzo_version(void); +LZO_EXTERN(const char *) lzo_version_string(void); + +/* string functions */ +LZO_EXTERN(int) +lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memset(lzo_voidp _s, int _c, lzo_uint _len); + +/* checksum functions */ +LZO_EXTERN(lzo_uint) +lzo_adler32(lzo_uint _adler, const lzo_byte *_buf, lzo_uint _len); + +/* misc. */ +LZO_EXTERN(int) lzo_assert(int _expr); +LZO_EXTERN(int) _lzo_config_check(void); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/REDALERT/LZOPIPE.CPP b/REDALERT/LZOPIPE.CPP new file mode 100644 index 000000000..85052d6f4 --- /dev/null +++ b/REDALERT/LZOPIPE.CPP @@ -0,0 +1,318 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZO processor pipe. * + * LZWPipe::Put -- Send some data through the LZO processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZO pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzopipe.h" +#include "lzo.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZOPipe::LZOPipe -- Constructor for the LZO processor pipe. * + * * + * This will initialize the LZOPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::LZOPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZOPipe::~LZOPipe -- Deconstructor for the LZO pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::~LZOPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOPipe::Put -- Send some data through the LZO processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZO processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + unsigned int length = sizeof (Buffer2); + lzo1x_decompress ((unsigned char*)Buffer, BlockHeader.CompCount, (unsigned char*)Buffer2, &length, NULL); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)source, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZOPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZO decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, Counter, (unsigned char *)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/REDALERT/LZOPIPE.H b/REDALERT/LZOPIPE.H new file mode 100644 index 000000000..84a0c74cf --- /dev/null +++ b/REDALERT/LZOPIPE.H @@ -0,0 +1,101 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOPIPE_H +#define LZOPIPE_H + +#include "pipe.h" + + +/* +** Performs LZO compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZOPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOPipe(CompControl, int blocksize=1024*8); + virtual ~LZOPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOPipe(LZOPipe & rvalue); + LZOPipe & operator = (LZOPipe const & pipe); +}; + + +#endif diff --git a/REDALERT/LZOSTRAW.CPP b/REDALERT/LZOSTRAW.CPP new file mode 100644 index 000000000..faa78df70 --- /dev/null +++ b/REDALERT/LZOSTRAW.CPP @@ -0,0 +1,181 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZOStraw::Get -- Fetch data through the LZO processor. * + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * * + * This will initialize the LZO straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::LZOStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::~LZOStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOStraw::Get -- Fetch data through the LZO processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *staging_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + unsigned int length = sizeof(Buffer); + lzo1x_decompress ((unsigned char*)staging_buffer, BlockHeader.CompCount, (unsigned char*)Buffer, &length, NULL); + delete [] staging_buffer; + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + char *dictionary = new char [64*1024]; + unsigned int length = sizeof (Buffer2) - sizeof (BlockHeader); + lzo1x_1_compress ((unsigned char*)Buffer, BlockHeader.UncompCount, (unsigned char*)(&Buffer2[sizeof(BlockHeader)]), &length, dictionary); + BlockHeader.CompCount = (unsigned short)length; + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/REDALERT/LZOSTRAW.H b/REDALERT/LZOSTRAW.H new file mode 100644 index 000000000..c1a5699e2 --- /dev/null +++ b/REDALERT/LZOSTRAW.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZoSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOSTRAW_H +#define LZOSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZO compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZOStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOStraw(CompControl control, int blocksize=1024*8); + virtual ~LZOStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOStraw(LZOStraw & rvalue); + LZOStraw & operator = (LZOStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/LZO_CONF.H b/REDALERT/LZO_CONF.H new file mode 100644 index 000000000..cd94fe4c2 --- /dev/null +++ b/REDALERT/LZO_CONF.H @@ -0,0 +1,283 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo_conf.h -- main internal configuration file for the the LZO library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the library and is subject + to change. + */ + + +#ifndef __LZO_CONF_H +#define __LZO_CONF_H + +#ifndef __LZOCONF_H +# include +#endif + + +/*********************************************************************** +// compiler specific defines +************************************************************************/ + +/* need Borland C 4.0 or above because of huge-pointer bugs */ +#if defined(__LZO_MSDOS16) && defined(__TURBOC__) +# if (__TURBOC__ < 0x452) +# error You need a newer compiler version +# endif +#endif + +#if defined(__LZO_MSDOS) || defined(__i386__) || defined(__386__) +# if !defined(__LZO_i386) +# define __LZO_i386 +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#include /* ptrdiff_t, size_t */ +#include /* memcpy, memmove, memcmp, memset */ + +#if 0 && !defined(assert) +# error not included +#endif + +#if defined(__BOUNDS_CHECKING_ON) +# include +#else +# define BOUNDS_CHECKING_OFF_DURING(stmt) stmt +# define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) +#endif + +/* ptrdiff_t */ +#if (UINT_MAX >= 0xffffffffL) + typedef ptrdiff_t lzo_ptrdiff_t; +#else + typedef long lzo_ptrdiff_t; +#endif + + +#ifdef __cplusplus +# define LZO_UNUSED(parm) +#else +# define LZO_UNUSED(parm) parm +#endif + + +#if !defined(__inline__) && !defined(__GNUC__) +# if defined(__cplusplus) +# define __inline__ inline +# else +# define __inline__ /* nothing */ +# endif +#endif + + +/*********************************************************************** +// compiler and architecture specific stuff +************************************************************************/ + +/* Some defines that indicate if memory can be accessed at unaligned + * addresses. You should also test that this is actually faster if + * it is allowed by your system. + */ + +#if 1 && defined(__LZO_i386) +# if !defined(LZO_UNALIGNED_OK_2) +# define LZO_UNALIGNED_OK_2 +# endif +# if !defined(LZO_UNALIGNED_OK_4) +# define LZO_UNALIGNED_OK_4 +# endif +#endif + + +#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK) +# define LZO_UNALIGNED_OK +# endif +#endif + + +/* Definitions for byte order, according to significance of bytes, from low + * addresses to high addresses. The value is what you get by putting '4' + * in the most significant byte, '3' in the second most significant byte, + * '2' in the second least significant byte, and '1' in the least + * significant byte. + */ + +#define LZO_LITTLE_ENDIAN 1234 +#define LZO_BIG_ENDIAN 4321 +#define LZO_PDP_ENDIAN 3412 + +/* The byte order is only needed if we use LZO_UNALIGNED_OK */ +#if !defined(LZO_BYTE_ORDER) +# if defined(__LZO_i386) +# define LZO_BYTE_ORDER LZO_LITTLE_ENDIAN +# elif defined(__mc68000__) +# define LZO_BYTE_ORDER LZO_BIG_ENDIAN +# elif defined(__BYTE_ORDER) +# define LZO_BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#if defined(LZO_UNALIGNED_OK) +# if !defined(LZO_BYTE_ORDER) +# error LZO_BYTE_ORDER is not defined +# elif (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \ + (LZO_BYTE_ORDER != LZO_BIG_ENDIAN) +# error invalid LZO_BYTE_ORDER +# endif +#endif + + +/*********************************************************************** +// optimization +************************************************************************/ + +/* gcc 2.6.3 and gcc 2.7.2 have a bug */ +#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY + +/* Help the optimizer with register allocation. + * Don't activate this macro for a fair comparision with other algorithms. + */ +#if 1 && defined(NDEBUG) && !defined(__BOUNDS_CHECKING_ON) +# if defined(__GNUC__) && defined(__i386__) +# if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY) +# define LZO_OPTIMIZE_GNUC_i386 +# endif +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define LZO_BYTE(x) ((unsigned char) (x)) + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) + +#define lzo_sizeof(x) ((lzo_uint) (sizeof(x))) + +#define LZO_HIGH(x) ((lzo_uint) (sizeof(x)/sizeof(*(x)))) + +/* this always fits into 16 bits */ +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_LSIZE(bits) (1ul << (bits)) +#define LZO_LMASK(bits) (LZO_LSIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + + +/*********************************************************************** +// ANSI C preprocessor macros +************************************************************************/ + +#define _LZO_STRINGIZE(x) #x +#define _LZO_MEXPAND(x) _LZO_STRINGIZE(x) + +/* concatenate */ +#define _LZO_CONCAT2(a,b) a ## b +#define _LZO_CONCAT3(a,b,c) a ## b ## c +#define _LZO_CONCAT4(a,b,c,d) a ## b ## c ## d +#define _LZO_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e + +/* expand and concatenate (by using one level of indirection) */ +#define _LZO_ECONCAT2(a,b) _LZO_CONCAT2(a,b) +#define _LZO_ECONCAT3(a,b,c) _LZO_CONCAT3(a,b,c) +#define _LZO_ECONCAT4(a,b,c,d) _LZO_CONCAT4(a,b,c,d) +#define _LZO_ECONCAT5(a,b,c,d,e) _LZO_CONCAT5(a,b,c,d,e) + + +/*********************************************************************** +// +************************************************************************/ + +/* Generate compressed data in a deterministic way. + * This is fully portable, and compression can be faster as well. + * A reason NOT to be deterministic is when the block size is + * very small (e.g. 8kB) or the dictionary is big, because + * then the initialization of the dictionary becomes a relevant + * magnitude for compression speed. + */ +#define LZO_DETERMINISTIC + + +/*********************************************************************** +// +************************************************************************/ + +#if 0 +/* This line causes problems on some architectures */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_off = ip - m_pos) > max_offset )) + +#else +/* This is the safe (but slower) version */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = ip - m_pos) > max_offset) +#endif + + +/* m_pos may point anywhere... + * This marco is probably a good candidate for architecture specific problems. + * Try casting the pointers to lzo_ptr_t before comparing them. + */ +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_pos < in || (m_off = ip - m_pos) <= 0 || m_off > max_offset) )) + + + +#endif /* already included */ + +/* +vi:ts=4 +*/ + diff --git a/REDALERT/LZW.CPP b/REDALERT/LZW.CPP new file mode 100644 index 000000000..241446c76 --- /dev/null +++ b/REDALERT/LZW.CPP @@ -0,0 +1,536 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Child_Node -- Find a matching dictionary entry. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include + +#include "xstraw.h" +#include "xpipe.h" +#include "buff.h" +#include "lzw.h" + + +LZWEngine::LZWEngine(void) +{ + Reset(); +} + + +void LZWEngine::Reset(void) +{ + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); + } +} + +int LZWEngine::Compress(Buffer const & input, Buffer const & output) +{ + BufferStraw instraw(input); + BufferPipe outpipe(output); + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + + string_code = 0; + if (instraw.Get(&string_code, sizeof(char)) == 0) { + string_code = END_OF_STREAM; + } + + for (;;) { + + /* + ** Fetch a character from the source data stream. If exhausted, + ** then break out of the process loop so that the final code + ** can be written out. + */ + unsigned char character; + if (instraw.Get(&character, sizeof(character)) == 0) break; + + /* + ** See if there is a match for the current code and current + ** character. A match indicates that there is already a + ** dictionary entry that fully represents the character + ** sequence. + */ + int index = Find_Child_Node(string_code, character); + + /* + ** If a code match was found, then set the current code + ** value to this code value that represents the concatenation + ** of the previous code value and the current character. + */ + if (index != -1 && dict[index].CodeValue != -1) { + string_code = dict[index].CodeValue; + } else { + + /* + ** Since no exact match was found, then create a new code + ** entry that represents the current code and character + ** value concatenated. This presumes there is room in the + ** code table. + */ + if (index != -1 && next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code, string_code, character); + next_code++; + } + + /* + ** Output the code to the compression stream and reset the + ** current code value to match the current character. This + ** has the effect of clearing out the current character + ** sequence scan in preparation for building a new one. It + ** also ensures that the character will be written out. + */ + outcount += outpipe.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + + outcount += outpipe.Put(&string_code, sizeof(string_code)); + if (string_code != END_OF_STREAM) { + string_code = END_OF_STREAM; + outcount += outpipe.Put(&string_code, sizeof(string_code)); + } + + return(outcount); +} + + +int LZWEngine::Uncompress(Buffer const & input, Buffer const & output) +{ + int outcount = 0; + BufferStraw instraw(input); + BufferPipe outpipe(output); + + CodeType old_code; + if (instraw.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + unsigned char character = (unsigned char)old_code; + outcount += outpipe.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (instraw.Get(&new_code, sizeof(new_code)) == 0) break; + + if (new_code == END_OF_STREAM) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += outpipe.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + + +int LZWEngine::Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +int LZWEngine::Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +int LZWEngine::Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Uncompress(inbuff, outbuff)); +} + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Compress(inbuff, outbuff)); +} + + + + + +#ifdef NEVER + + +/* + * Constants used throughout the program. BITS defines how many bits + * will be in a code. TABLE_SIZE defines the size of the dictionary + * table. + */ +#define BITS 12 +#define MAX_CODE ( ( 1 << BITS ) - 1 ) +#define TABLE_SIZE 5021 +#define END_OF_STREAM 256 +#define FIRST_CODE 257 +#define UNUSED -1 + +typedef unsigned short CodeType; + +/* + * This data structure defines the dictionary. Each entry in the dictionary + * has a code value. This is the code emitted by the compressor. Each + * code is actually made up of two pieces: a parent_code, and a + * character. Code values of less than 256 are actually plain + * text codes. + */ +struct CodeClass +{ + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} +}; +CodeClass dict[TABLE_SIZE]; + +char decode_stack[TABLE_SIZE]; + +inline int Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +/*********************************************************************************************** + * Find_Child_Node -- Find a matching dictionary entry. * + * * + * This hashing routine is responsible for finding the table location * + * for a string/character combination. The table index is created * + * by using an exclusive OR combination of the prefix and character. * + * This code also has to check for collisions, and handles them by * + * jumping around in the table. * + * * + * INPUT: parent_code -- The code of the parent string sequence. * + * * + * character -- The current character. * + * * + * OUTPUT: Returns with the index for the matching dictionary entry. If no matching index * + * could be found, then it returns with the index to an unused dictionary entry. If * + * there are also no unused entries in the dictionary, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/28/1996 JLB : Created. * + *=============================================================================================*/ +static int Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +/* + * This routine decodes a string from the dictionary, and stores it + * in the decode_stack data structure. It returns a count to the + * calling program of how many characters were placed in the stack. + */ +static int Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +/* + * The compressor is short and simple. It reads in new symbols one + * at a time from the input file. It then checks to see if the + * combination of the current symbol and the current code are already + * defined in the dictionary. If they are not, they are added to the + * dictionary, and we start over with a new one symbol code. If they + * are, the code for the combination of the code and character becomes + * our new code. + */ + +int LZW_Compress(Buffer & inbuff, Buffer & outbuff) +{ + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); +// dict[i].code_value = UNUSED; + } + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + for (;;) { + char character; + + if (input.Get(&character, sizeof(character)) == 0) break; + + int index = Find_Child_Node(string_code, character); + + if (index == -1) { + string_code = character; + outcount += output.Put(&string_code, sizeof(string_code)); + } else { + + if (dict[index].CodeValue != -1) { + string_code = dict[ index ].CodeValue; + } else { + if (next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code++, string_code, character); + } + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + } + + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = END_OF_STREAM; + outcount += output.Put(&string_code, sizeof(string_code)); + + return(outcount); +} + + +/* + * The file expander operates much like the encoder. It has to + * read in codes, the convert the codes to a string of characters. + * The only catch in the whole operation occurs when the encoder + * encounters a CHAR+STRING+CHAR+STRING+CHAR sequence. When this + * occurs, the encoder outputs a code that is not presently defined + * in the table. This is handled as an exception. + */ +int LZW_Uncompress(Buffer & inbuff, Buffer & outbuff) +{ + int outcount = 0; + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + CodeType old_code; + if (input.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + char character = (char)old_code; + outcount += output.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (input.Get(&new_code, sizeof(new_code)) == 0) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += output.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + +#endif diff --git a/REDALERT/LZW.H b/REDALERT/LZW.H new file mode 100644 index 000000000..2d69e2666 --- /dev/null +++ b/REDALERT/LZW.H @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZW_H +#define _LZW_H + +#include "buff.h" + +class LZWEngine +{ + public: + LZWEngine(void); + + int Compress(Buffer const & input, Buffer const & output); + int Uncompress(Buffer const & input, Buffer const & output); + + void Reset(void); + + private: + typedef short CodeType; + struct CodeClass { + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + enum {UNUSED=-1}; + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} + }; + + enum { + BITS=12, + MAX_CODE=((1 << BITS ) - 1), + FIRST_CODE=257, + END_OF_STREAM=256, + TABLE_SIZE=5021 + }; + CodeClass dict[TABLE_SIZE]; + + char decode_stack[TABLE_SIZE]; + + int Find_Child_Node(CodeType parent_code, char child_character); + int Decode_String(char * ptr, CodeType code); + static int Make_LZW_Hash(CodeType code, char character); +}; + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff); +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff); + +#endif diff --git a/REDALERT/LZWOTRAW.CPP b/REDALERT/LZWOTRAW.CPP new file mode 100644 index 000000000..672b00cf0 --- /dev/null +++ b/REDALERT/LZWOTRAW.CPP @@ -0,0 +1,181 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWOTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzwstraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *stageing_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + lz01x_decompress (ptr, BlockHeader.CompCount, Buffer, sizeof(Buffer), NULL); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + int len = sizeof (Buffer2) - sizeof (BlockHeader); + char *dictionary = new char [64*1024]; + lzo1x_1_compress (Buffer, BlockHeader.UncompCount, &Buffer2[sizeof(BlockHeader)], &BlockHeader.CompCount, dictionary); + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/REDALERT/LZWPIPE.CPP b/REDALERT/LZWPIPE.CPP new file mode 100644 index 000000000..5573c6eb0 --- /dev/null +++ b/REDALERT/LZWPIPE.CPP @@ -0,0 +1,312 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzwpipe.h" +#include "lzw.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * * + * This will initialize the LZWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::LZWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::~LZWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LZW_Uncompress(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LZW_Compress(::Buffer(Buffer, BlockSize), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LZW_Compress(::Buffer((void*)source, BlockSize), Buffer2); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LZW_Compress(::Buffer(Buffer, Counter), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/REDALERT/LZWPIPE.H b/REDALERT/LZWPIPE.H new file mode 100644 index 000000000..106870a0e --- /dev/null +++ b/REDALERT/LZWPIPE.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWPIPE_H +#define LZWPIPE_H + +#include "pipe.h" + + +/* +** Performs LZW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWPipe(CompControl, int blocksize=1024*8); + virtual ~LZWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWPipe(LZWPipe & rvalue); + LZWPipe & operator = (LZWPipe const & pipe); +}; + + +#endif diff --git a/REDALERT/LZWSTRAW.CPP b/REDALERT/LZWSTRAW.CPP new file mode 100644 index 000000000..8a8914f7a --- /dev/null +++ b/REDALERT/LZWSTRAW.CPP @@ -0,0 +1,177 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzwstraw.h" +#include "lzw.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LZW_Uncompress(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LZW_Compress(::Buffer(Buffer, BlockHeader.UncompCount), &Buffer2[sizeof(BlockHeader)]); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/REDALERT/LZWSTRAW.H b/REDALERT/LZWSTRAW.H new file mode 100644 index 000000000..bbfda3c65 --- /dev/null +++ b/REDALERT/LZWSTRAW.H @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWSTRAW_H +#define LZWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWStraw(CompControl control, int blocksize=1024*8); + virtual ~LZWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWStraw(LZWStraw & rvalue); + LZWStraw & operator = (LZWStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/License.txt b/REDALERT/License.txt new file mode 100644 index 000000000..a4adf6d01 --- /dev/null +++ b/REDALERT/License.txt @@ -0,0 +1,712 @@ +Electronic Arts Inc. released only TiberianDawn.dll and RedAlert.dll and their corresponding source code under the GPL V3 below, with additional terms at the bottom. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +ADDITIONAL TERMS per GNU GPL Section 7 +No trademark or publicity rights are granted. This license does NOT give you +any right, title or interest in "Command & Conquer" or any other Electronic Arts trademark. You may not distribute any +modification of this program using any Electronic Arts trademark or claim any affiliation or association with Electronic Arts Inc. +or its affiliates or their employees. + +Any propagation or conveyance of this program must include this copyright +notice and these terms. + +If you convey this program (or any modifications of it) and assume +contractual liability for the program to recipients of it, you agree to +indemnify Electronic Arts for any liability that those contractual +assumptions impose on Electronic Arts. + +You may not misrepresent the origins of this program; modified versions of +the program must be marked as such and not identified as the original program. + +This disclaimer supplements the one included in the General Public License. +TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS PROGRAM IS +PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY OF ANY KIND, AND +YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF SATISFACTORY QUALITY AND +PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS DISCLAIMS ANY AND ALL EXPRESS, +IMPLIED OR STATUTORY WARRANTIES, INCLUDING IMPLIED WARRANTIES OF +MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, +NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A +COURSE OF DEALING, USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT +AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL +MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE WITH THIRD PARTY SOFTWARE +OR THAT ANY ERRORS IN THE PROGRAM WILL BE CORRECTED. NO ORAL OR WRITTEN ADVICE +PROVIDED BY ELECTRONIC ARTS OR ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A +WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON +IMPLIED WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A +CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY +TO YOU. \ No newline at end of file diff --git a/REDALERT/MAP.CPP b/REDALERT/MAP.CPP new file mode 100644 index 000000000..43f1f5f74 --- /dev/null +++ b/REDALERT/MAP.CPP @@ -0,0 +1,2284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAP.CPP 3 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * MapClass::Detach -- Remove specified object from map references. * + * MapClass::In_Radar -- Is specified cell in the radar map? * + * MapClass::Init -- clears all cells * + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * MapClass::Logic -- Handles map related logic functions. * + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * MapClass::One_Time -- Performs special one time initializations for the map. * + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * MapClass::Pick_Up -- Removes specified object from the map. * + * MapClass::Place_Down -- Places the specified object onto the map. * + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * MapClass::Set_Map_Dimensions -- Initialize the map. * + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * MapClass::Validate -- validates every cell on the map * + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W +int const MapClass::RadiusOffset[] = { + /* 0 */ 0, + /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, + /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, + /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, + /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, + /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, + /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, + /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, + /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, + /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, + /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, + (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, +}; + +int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; + + +CellClass * BlubCell; + +/*********************************************************************************************** + * MapClass::One_Time -- Performs special one time initializations for the map. * + * * + * This routine is used by the game initialization function in order to perform any one * + * time initializations required for the map. This includes allocation of the map and * + * setting up its default dimensions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine MUST be called once and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/01/1994 BR : Added CellTriggers initialization * + *=============================================================================================*/ +void MapClass::One_Time(void) +{ + GScreenClass::One_Time(); + + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); +} + + +/*********************************************************************************************** + * MapClass::Init_Clear -- clears the map & buffers to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Clear(void) +{ + GScreenClass::Init_Clear(); + Init_Cells(); + TiberiumScan = 0; + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + Crates[index].Init(); + } +} + + +/*********************************************************************************************** + * MapClass::Alloc_Cells -- allocates the cell array * + * * + * This routine should be called at One_Time, and after loading the Map object from a save * + * game, but prior to loading the cell objects. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Alloc_Cells(void) +{ + /* + ** Assume that whatever the contents of the VectorClass are is garbage + ** (it may have been loaded from a save-game file), so zero it out first. + */ + new (&Array) VectorClass; + Array.Resize(Size); +} + + +/*********************************************************************************************** + * MapClass::Free_Cells -- frees the cell array * + * * + * This routine is used by the Load_Game routine to free the map's cell array before loading * + * the map object from disk; the array is then re-allocated & cleared before the cell objects * + * are loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Free_Cells(void) +{ + Array.Clear(); +} + + +/*********************************************************************************************** + * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * + * * + * This routine is used by Init_Clear to set the cells to a known state; it's also used by * + * the Load_Game routine to init all cells before loading a set of cells from disk, so it * + * needs to be called separately from the other Init_xxx() routines. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Cells(void) +{ + TotalValue = 0; + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + new (&Array[index]) CellClass; + } +} + + +/*********************************************************************************************** + * MapClass::Set_Map_Dimensions -- Set map dimensions. * + * * + * This routine is used to set the legal limits and position of the * + * map as it relates to the overall map array. Typically, this is * + * called by the scenario loading code. * + * * + * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * + * of the map. * + * * + * w,h -- The width and height of the legal map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + MapCellX = x; + MapCellY = y; + MapCellWidth = w; + MapCellHeight = h; +} + + +/*********************************************************************************************** + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * * + * This routine is used to reveal the cells around a specific location. * + * Typically, as a unit moves or is deployed, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the sighting originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * incremental-- Is this an incremental sighting. In other * + * words, has this function been called before where * + * the center coordinate is no more than one cell * + * distant from the last time? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1992 JLB : Created. * + * 03/08/1994 JLB : Updated to use sight table and incremental flag. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MapClass::Sight_From(CELL cell, int sightrange, HouseClass * house, bool incremental) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > 10) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + if (incremental) { + if (sightrange > 2) { + ptr += RadiusCount[sightrange-3]; + count -= RadiusCount[sightrange-3]; + } + } + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Map the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + //if (!(*this)[newcell].IsMapped) { // ST - 8/7/2019 10:31AM + Map.Map_Cell(newcell, house, true, true); + } +} + + +/*********************************************************************************************** + * MapClass::Shroud_From -- cloak a radius of cells * + * * + * This routine is used to shroud the cells around a specific location. * + * Typically, as a gap generator refreshes (when Encroach_Shadow() is called) this routine's* + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the shrouding originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 BWG : Created. * + * 08/09/2019 ST : Added house parameter * + *=============================================================================================*/ +void MapClass::Shroud_From(CELL cell, int sightrange, HouseClass *house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Shroud the cell. + */ + Map.Shroud_Cell(newcell, house); + } +} + + +/*********************************************************************************************** + * MapClass::Jam_From -- Mark as jammed the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::Jam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.Jam_Cell(newcell, house/*KO, false*/); + } + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (!house->IsPlayerControl) { + Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != house) { + Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, player_house); + } + } + } + +#ifdef OBSOLETE + /* + ** The objects on the map need to perform a manual look operation if they happen + ** to have their sight range overlap the gap radius. + */ + if (!house->IsPlayerControl) { +// if (house != PlayerPtr) { + + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Map.Layer[LAYER_GROUND][index]; + if (object && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->IsDiscoveredByPlayer && + (tech->Distance(As_Target(cell)) / CELL_LEPTON_W) <= tech->Techno_Type_Class()->SightRange + Rule.GapShroudRadius && + (tech->House->IsPlayerControl || + (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)))) { + + object->Look(); + } + } + } + } +#endif + +#ifdef OBSOLETE + /* + ** Here all the player's vehicles will perform a look if they're within + ** the shadow. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * unit = Infantry.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * unit = Vessels.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + } +#endif + +} + + +/*********************************************************************************************** + * MapClass::UnJam_From -- Remove jamming on the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::UnJam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.UnJam_Cell(newcell, house); + } +} + + +/*********************************************************************************************** + * MapClass::In_Radar -- Is specified cell in the radar map? * + * * + * This determines if the specified cell can be within the navigable * + * bounds of the map. Technically, this means, any cell that can be * + * scanned by radar. If a cell returns false from this function, then * + * the player could never move to or pass over this cell. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: bool; Is this cell possible to be displayed on radar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/01/1994 JLB : Speeded up. * + *=============================================================================================*/ +bool MapClass::In_Radar(CELL cell) const +{ + /* + ** If the cell value is WAY out of range, then it obviously can't be part of the game + ** playfield. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) return(false); + + /* + ** If the cell is off the left or right edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_X(cell) - MapCellX) >= (unsigned)MapCellWidth) return(false); + + /* + ** If the cell is off the top or bottom edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_Y(cell) - MapCellY) >= (unsigned)MapCellHeight) return(false); + + return(true); +} + + +/*********************************************************************************************** + * MapClass::Place_Down -- Places the specified object onto the map. * + * * + * This routine is used to place an object onto the map. It updates the "occupier" of the * + * cells that this object covers. The cells are determined from the Occupy_List function * + * provided by the object. Only one cell can have an occupier and this routine is the only * + * place that sets this condition. * + * * + * INPUT: cell -- The cell to base object occupation around. * + * * + * object -- The object to place onto the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Place_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Down(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Pick_Up -- Removes specified object from the map. * + * * + * The object specified is removed from the map by this routine. This will remove the * + * occupation flag for all the cells that the object covers. The cells that are covered * + * are determined from the Occupy_List function. * + * * + * INPUT: cell -- The cell that the object is centered about. * + * * + * object -- Pointer to the object that will be removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Pick_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Up(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * * + * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * * + * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * * + * This routine will clean up anything necessary with the presumption that the map has * + * been freshly created. Such things to clean up include various tiberium concentrations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the total credit value of the tiberium on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/13/1995 JLB : Returns total tiberium worth. * + * 02/15/1995 JLB : Optimal scan. * + *=============================================================================================*/ +long MapClass::Overpass(void) +{ + long value = 0; + + /* + ** Smooth out Tiberium. Cells that are not surrounded by other tiberium + ** will be reduced in density. + */ + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = (MapCellY+y) * MAP_CELL_W + (MapCellX+x); + value += (*this)[cell].Tiberium_Adjust(true); + (*this)[cell].Recalc_Attributes(); + } + } + return(value); +} + + +/*********************************************************************************************** + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * * + * This stores the template data from the map to the output pipe specified. The template * + * data consists of the template type number and template icon number for every cell on * + * the map. The output is organized in such a way so as to get maximum compression. * + * * + * INPUT: pipe -- Reference to the output pipe that will receive the map template data. * + * * + * OUTPUT: Returns with the total number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Write_Binary(Pipe & pipe) +{ + int total = 0; + + LCWPipe comp(LCWPipe::COMPRESS); + comp.Put_To(&pipe); + + CellClass * cellptr = &Array[0]; + for (int i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + + cellptr = &Array[0]; + for (int i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr++; + } + + return(total); +} + + +/*********************************************************************************************** + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * * + * This routine will retrieve the map template data from the straw specified. * + * * + * INPUT: straw -- Reference to the straw that will supply the map template data. * + * * + * OUTPUT: bool; Was the template data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Read_Binary(Straw & straw) +{ + LCWStraw decomp(LCWStraw::DECOMPRESS); + decomp.Get_From(&straw); + + CELL cell; + CellClass * cellptr; + switch (NewINIFormat) { + default: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + + case 0: + case 1: + case 2: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + } + return(true); +} + + +/*********************************************************************************************** + * MapClass::Logic -- Handles map related logic functions. * + * * + * Manages tiberium growth and spread. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + * 07/09/1995 JLB : Handles two directional scan. * + * 08/01/1995 JLB : Gives stronger weight to blossom trees. * + *=============================================================================================*/ +void MapClass::Logic(void) +{ + if (Debug_Force_Crash) { *((int *)0) = 1; } + /* + ** Crate regeneration is handled here. + */ + if (Session.Type != GAME_NORMAL && Session.Options.Goodies) { + + /* + ** Find any crate that has expired and then regenerate it at a new + ** spot. + */ + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Expired()) { + Crates[index].Remove_It(); + Place_Random_Crate(); + } + } + } + + /* + ** Bail early if there is no allowed growth or spread of Tiberium. + */ + if (!Rule.IsTGrowth && !Rule.IsTSpread) return; + + /* + ** Scan another block of the map in order to accumulate the potential + ** Tiberium cells that can grow or spread. + */ + int subcount = MAP_CELL_TOTAL / (Rule.GrowthRate * TICKS_PER_MINUTE); + subcount = max(subcount, 1); + int index; + for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { + CELL cell = index; + if (In_Radar(cell)) { + CellClass * ptr = &(*this)[cell]; + + /* + ** Tiberium cells can grow. + */ + if (ptr->Can_Tiberium_Grow()) { + + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumGrowthExcess) <= TiberiumGrowthCount) { + if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { + TiberiumGrowth[TiberiumGrowthCount++] = cell; + } else { + TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; + } + } + TiberiumGrowthExcess++; + } + + /* + ** Heavy Tiberium growth can spread. + */ + if (ptr->Can_Tiberium_Spread()) { + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumSpreadExcess) <= TiberiumSpreadCount) { + if (TiberiumSpreadCount < ARRAY_SIZE(TiberiumSpread)) { + TiberiumSpread[TiberiumSpreadCount++] = cell; + } else { + TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; + } + } + TiberiumSpreadExcess++; + } + } + + subcount--; + if (subcount == 0) break; + } + TiberiumScan = index; + + /* + ** When the entire map has been processed, proceed with tiberium (ore) growth + ** and spread action. + */ + if (TiberiumScan >= MAP_CELL_TOTAL) { + TiberiumScan = 0; + + /* + ** Growth logic. + */ + if (TiberiumGrowthCount) { + for (int i = 0; i < TiberiumGrowthCount; i++) { + CELL cell = TiberiumGrowth[i]; + CellClass * newcell = &(*this)[cell]; + newcell->Grow_Tiberium(); + } + } + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + + /* + ** Spread logic. + */ + if (TiberiumSpreadCount) { + for (int i = 0; i < TiberiumSpreadCount; i++) { + Map[TiberiumSpread[i]].Spread_Tiberium(); + } + } + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * * + * Use this routine to determine what region a particular cell lies in. * + * * + * INPUT: cell -- The cell number to examine. * + * * + * OUTPUT: Returns with the region that the specified cell occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 JLB : Created. * + *=============================================================================================*/ +int MapClass::Cell_Region(CELL cell) +{ + return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); +} + + +/*************************************************************************** + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * * + * INPUT: CELL cell - the cell number to check * + * HouseType house - the house to check * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1995 PWG : Created. * + *=========================================================================*/ +int MapClass::Cell_Threat(CELL cell, HousesType house) +{ + int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (!threat && Map[cell].Is_Visible(house)) { + threat = 1; + } + return(threat); +} + + +/*********************************************************************************************** + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * * + * This routine will place a crate at a random location on the map. This routine will only * + * make a limited number of attempts to place and if unsuccessful, it will not place any. * + * * + * INPUT: none * + * * + * OUTPUT: Was a crate successfully placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Place_Random_Crate(void) +{ + /* + ** Find a crate index that is free for assignment. If there are + ** no free slots, then return with failure to place crate. + */ + int crateindex = 0; + for (crateindex = 0; crateindex < ARRAY_SIZE(Crates); crateindex++) { + if (!Crates[crateindex].Is_Valid()) break; + } + if (crateindex == ARRAY_SIZE(Crates)) { + return(false); + } + + /* + ** Give a good effort to scan for and place a crate down on the map. + */ + for (int index = 0; index < 1000; index++) { + CELL cell = Map.Pick_Random_Location(); + + if (Crates[crateindex].Create_Crate(cell)) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * * + * This will examine the cell and remove any crates there. * + * * + * INPUT: cell -- The cell to examine for crates and remove from. * + * * + * OUTPUT: bool; Was a crate found at the location specified and was it removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Remove_Crate(CELL cell) +{ + if (Session.Type != GAME_NORMAL) { + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Here(cell)) { + return(Crates[index].Remove_It()); + } + } + } + +// if (Session.Type == GAME_NORMAL) { + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsCrate) { + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + return(true); + } +// } else { +// for (int index = 0; index < ARRAY_SIZE(Crates); index++) { +// if (Crates[index].Is_Here(cell)) { +// return(Crates[index].Remove_It()); +// } +// } +// } + return(false); +} + + +/*************************************************************************** + * MapClass::Validate -- validates every cell on the map * + * * + * This is a debugging routine, designed to detect memory trashers that * + * alter the map. This routine is slow, but thorough. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = map is OK, false = an error was found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +int MapClass::Validate(void) +{ + CELL cell; + TemplateType ttype; + unsigned char ticon; + TemplateTypeClass const *tclass; + unsigned char map[13*8]; + OverlayType overlay; + SmudgeType smudge; + ObjectClass * obj; + LandType land; + int i; + +BlubCell = &Array[797]; + +if (BlubCell->Overlapper[1]) { + obj = BlubCell->Overlapper[1]; + if (obj) { + if (obj->IsInLimbo) + obj = obj; + } +} + + /* + ** Check every cell on the map, even those that aren't displayed, + ** in the hopes of detecting a memory trasher. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + /* + ** Validate Template & Icon data + */ + ttype = (*this)[cell].TType; + ticon = (*this)[cell].TIcon; + if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) + return(false); + + /* + ** To validate the icon value, we have to get a copy of the template's + ** "icon map"; this map will have 0xff's in spots where there is no + ** icon. If the icon value is out of range or points to an invalid spot, + ** return an error. + */ + if (ttype != TEMPLATE_NONE) { + tclass = &TemplateTypeClass::As_Reference(ttype); + ticon = (*this)[cell].TIcon; + Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, tclass->Width * tclass->Height); + if (ticon < 0 || ticon >= (tclass->Width * tclass->Height) || map[ticon]==0xff) { + return (false); + } + } + + /* + ** Validate Overlay + */ + overlay = (*this)[cell].Overlay; + if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) { + return(false); + } + + /* + ** Validate Smudge + */ + smudge = (*this)[cell].Smudge; + if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) { + return(false); + } + + /* + ** Validate LandType + */ + land = (*this)[cell].Land_Type(); + if (land < LAND_CLEAR || land >= LAND_COUNT) { + return(false); + } + + /* + ** Validate Occupier + */ + obj = (*this)[cell].Cell_Occupier(); + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + + /* + ** Validate Overlappers + */ + for (i = 0; i < ARRAY_SIZE((*this)[cell].CellClass::Overlapper); i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + } + } + + return (true); +} + + +/*********************************************************************************************** + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * * + * This routine is used by the mouse input processing code to find a clickable object * + * close to coordinate specified. This is for targeting as well as selection determination. * + * * + * INPUT: coord -- The coordinate to scan for close object from. * + * * + * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * + * * + * WARNINGS: There could be a cloaked object at the location, but it won't be considered * + * if it is not owned by the player. * + * * + * HISTORY: * + * 08/20/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * MapClass::Close_Object(COORDINATE coord) const +{ + ObjectClass * object = 0; + int distance = 0; + CELL cell = Coord_Cell(coord); + + /* + ** Scan through current and adjacent cells, looking for the + ** closest object (within reason) to the specified coordinate. + */ + static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; + for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + + /* + ** Examine the cell for close object. Make sure that the cell actually is a + ** legal one. + */ + CELL newcell = cell + _offsets[index]; + if (In_Radar(newcell)) { + + /* + ** Search through all objects that occupy this cell and then + ** find the closest object. Check against any previously found object + ** to ensure that it is actually closer. + */ + ObjectClass * o = Array[newcell].Cell_Occupier(); + while (o != NULL) { + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + // Change for client/server multiplayer. ST - 8/7/2019 10:35AM + //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { + if (!o->Is_Techno() || ((TechnoClass *)o)->Is_Owned_By_Player() || ((TechnoClass *)o)->Cloak != CLOAKED) { + int d=-1; + if (o->What_Am_I() == RTTI_BUILDING) { + d = Distance(coord, Cell_Coord(newcell)); + if (d > 0x00C0) d = -1; + } else { + d = Distance(coord, o->Center_Coord()); + } + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = o; + } + } + o = o->Next; + } + } + } + + /* + ** Handle aircraft selection separately, since they aren't tracked in cells while flying + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->In_Which_Layer() != LAYER_GROUND) { + if (aircraft->Is_Owned_By_Player() || (aircraft->Cloak != CLOAKED)) { + int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -aircraft->Height))); + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = aircraft; + } + } + } + } + + /* + ** Only return the object if it is within 1/4 cell distance from the specified + ** coordinate. + */ + if (object && distance > 0xC0) { + object = 0; + } + return(object); +} + + +/*********************************************************************************************** + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * * + * This routine will rescan the map and fill in the zone values for each of the cells. * + * All cells that are contiguous are given the same zone number. * + * * + * INPUT: method -- The method to recalculate the zones upon. If 1 then recalc non * + * crushable zone. If 2 then recalc crushable zone. If 3, then * + * recalc both zones. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming routine. Call it as infrequently as possible. It must * + * be called whenever something that would affect contiguousness occurs. Example: * + * when a bridge is built or destroyed. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Zone_Reset(int method) +{ + /* + ** Zero out all zones to a null state. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (method & MZONEF_NORMAL) { + Array[index].Zones[MZONE_NORMAL] = 0; + } + if (method & MZONEF_CRUSHER) { + Array[index].Zones[MZONE_CRUSHER] = 0; + } + if (method & MZONEF_DESTROYER) { + Array[index].Zones[MZONE_DESTROYER] = 0; + } + if (method & MZONEF_WATER) { + Array[index].Zones[MZONE_WATER] = 0; + } + } + + /* + ** Normal zone recalculation. + */ + if (method & MZONEF_NORMAL) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_NORMAL)) { + zone++; + } + } + } + + /* + ** Crushable wall recalculation. + */ + if (method & MZONEF_CRUSHER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_CRUSHER)) { + zone++; + } + } + } + + /* + ** Wall destroyer zone recalculation. + */ + if (method & MZONEF_DESTROYER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_DESTROYER)) { + zone++; + } + } + } + + /* + ** Water based zone recalcuation. + */ + if (method & MZONEF_WATER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_WATER)) { + zone++; + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * * + * This routine is used to fill a zone value into the map. The map is "flood filled" from * + * the cell specified. All adjacent (8 connected) and generally passable terrain cells are * + * given the zone number specified. This routine checks for legality before filling * + * occurs. The routine is safe to call even if the legality of the cell is unknown at the * + * time of the call. * + * * + * INPUT: cell -- The cell to begin filling from. * + * * + * zone -- The zone number to assign to all adjacent cells. * + * * + * check -- The zone type to check against. * + * * + * OUTPUT: Returns with the number of cells marked by this routine. * + * * + * WARNINGS: This routine is slow and recursive. Only use when necessary. * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 10/05/1996 JLB : Examines crushable walls. * + *=============================================================================================*/ +int MapClass::Zone_Span(CELL cell, int zone, MZoneType check) +{ + int filled = 0; + int xbegin = Cell_X(cell); + int xend = xbegin; + int y = Cell_Y(cell); + + /* + ** Perform some preliminary legality checks. If the cell specified + ** is illegal, then no further processing is necessary. + */ + if (y < MapCellY || y >= MapCellY+MapCellHeight || xbegin < MapCellX || xbegin >= MapCellX+MapCellWidth) { + return(0); + } + + /* + ** Find the full extent of the current span by first scanning leftward + ** until a boundary is reached. + */ + for (; xbegin >= MapCellX; xbegin--) { + CellClass * cellptr = &(*this)[XY_Cell(xbegin, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + + /* + ** Special short circuit code to bail from this entire routine if + ** it was called for a cell that is not a legal candidate for + ** zone marking. This eliminates redundant processing and allows this + ** routine to be called for illegal cells without causing an error. + */ + if (xbegin == Cell_X(cell)) return(0); + + /* + ** Otherwise break out of the left scan since a boundary was discovered. + */ + xbegin++; + break; + } + } + xbegin = max(xbegin, MapCellX); + + /* + ** Scan rightward until a boundary is reached. This will then define the + ** extent of the current span. + */ + for (; xend < MapCellX+MapCellWidth; xend++) { + CellClass * cellptr = &(*this)[XY_Cell(xend, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + xend--; + break; + } + } + xend = min(xend, MapCellX+MapCellWidth-1); + + /* + ** At this point we know the bounds of the current span. Fill in the zone values + ** for the entire span. + */ + for (int x = xbegin; x <= xend; x++) { + (*this)[XY_Cell(x, y)].Zones[check] = zone; + filled++; + } + + /* + ** Now scan the upper and lower shadow rows. If any of these rows contain + ** candidate cells, then recursively call the span process for them. Take + ** note that the adjacent span scanning starts one cell wider on each + ** end of the scan. This is necessary because diagonals are considered + ** adjacent. + */ + for (int x = xbegin-1; x <= xend; x++) { + filled += Zone_Span(XY_Cell(x, y-1), zone, check); + filled += Zone_Span(XY_Cell(x, y+1), zone, check); + } + return(filled); +} + + +/*********************************************************************************************** + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * * + * This routine is used to find a location that probably will be ok to move to that is * + * located as close as possible to the specified cell. The computer uses this when it has * + * determined the ideal location for an object, but then needs to give a valid movement * + * destination to a unit. * + * * + * INPUT: cell -- The cell that scanning should radiate out from. * + * * + * zone -- The zone that must be matched to find a legal location (value of -1 means * + * any zone will do). * + * * + * * + * check -- The type of zone to check against. Only valid if a zone value is given. * + * * + * checkflagged -- Whether the cell's flagged status is checked (used when dropping). * + * * + * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * + * to the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check, bool checkflagged) const +{ + CELL topten[10]; + int count = 0; + int xx = Cell_X(cell); + int yy = Cell_Y(cell); + + /* + ** Determine the limits of the scanning in the four directions so that + ** it won't scan past the edge of the world. + */ + int left = MapCellX; + int right = MapCellX + MapCellWidth - 1; + int top = MapCellY; + int bottom = MapCellY + MapCellHeight - 1; + + /* + ** Radiate outward from the specified location, looking for the closest + ** location that is generally clear. + */ + for (int radius = 0; radius < MAP_CELL_W; radius++) { + CELL newcell; + CellClass const * cellptr; + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = xx-radius; x <= xx+radius; x++) { + if (x >= left && x <= right) { + int y = yy-radius; + if (y >= top) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + y = yy+radius; + if (y <= bottom) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count == ARRAY_SIZE(topten)) break; + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = yy-radius; y <= yy+radius; y++) { + if (y >= top && y <= bottom) { + int x = xx-radius; + if (x >= left) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + x = xx+radius; + if (x <= right) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count > 0) break; + } + + if (count > 0) { + return(topten[Frame % count]); + } + return(0); +} + + +/*********************************************************************************************** + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * * + * This routine is used to determine what base the specified cell is close to and what * + * zone of that base the cell lies in. This routine is particularly useful in letting the * + * computer know when the player targets a destination near a computer's base. * + * * + * INPUT: cell -- The cell that is to be checked. * + * * + * house -- Reference to the house type number. This value will be set if a base * + * was found nearby the specified cell. * + * * + * zone -- The zone that the cell is located in IF the cell is near a base. * + * * + * * + * OUTPUT: Was a base near the specified cell found? If not, then the 'house' and 'zone' * + * reference values are left in an undefined state and the return value will be * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Base_Region(CELL cell, HousesType & house, ZoneType & zone) const +{ + if ((unsigned)cell < MAP_CELL_TOTAL && In_Radar(cell)) { + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + + if (h && h->IsActive && !h->IsDefeated && h->Center) { + zone = h->Which_Zone(cell); + if (zone != ZONE_NONE) { + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * * + * This routine will destroy the bridge at the location specified. * + * * + * INPUT: cell -- A cell that can uniquely identify the bridge. * + * * + * OUTPUT: bool; Was the bridge destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ + +// Need to inform the server of the cell change so it can communicate with the clients - SKY +extern void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name); + +struct CellUpdateStruct +{ + const TemplateTypeClass* Type; + CELL Cell; +}; + +static const int MAX_UPDATES = 8; + +static void Add_Cell_Update(CellUpdateStruct* updates, int& count, TemplateType type, CELL cell) +{ + new TemplateClass(type, cell); + + assert(count < MAX_UPDATES); + if (count < MAX_UPDATES) + { + updates[count].Type = TemplateTypes.Ptr((int)type); + updates[count].Cell = cell; + count++; + } +} + +bool MapClass::Destroy_Bridge_At(CELL cell) +{ + bool destroyed = false; + if (In_Radar(cell) && !Special.IsCaptureTheFlag) { + CellClass * cellptr = &(*this)[cell]; + TemplateType ttype = cellptr->TType; + + CellUpdateStruct cell_updates[MAX_UPDATES]; + int update_count = 0; + + if (ttype == TEMPLATE_BRIDGE1 || ttype == TEMPLATE_BRIDGE2) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + + if (ttype == TEMPLATE_BRIDGE1) { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1H, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2H, cell); + } + + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + w/2 + (h/2)*MAP_CELL_W)); + } + + if (ttype == TEMPLATE_BRIDGE1H || ttype == TEMPLATE_BRIDGE2H) { + int icon = cellptr->TIcon; + int bridge_w = TemplateTypeClass::As_Reference(ttype).Width; + int bridge_h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % bridge_w; + cell -= MAP_CELL_W * (icon / bridge_w); + + if (ttype == TEMPLATE_BRIDGE1H) { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1D, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2D, cell); + } + + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + bridge_w/2 + (bridge_h/2)*MAP_CELL_W)); + Map.Zone_Reset(MZONEF_ALL); + + /* + ** Now, loop through all the bridge cells and find anyone standing + ** on a destroyed part (which is now river), and nuke them. + */ + CELL cell2 = cell; + for (int y = 0; y < bridge_h; y++) { + for (int x = 0; x < bridge_w; x++) { + CellClass * bridge_cell = &(*this)[cell2]; + if (bridge_cell->TType == ttype) { + + /* + ** Any unit that is firing on the bridge at this location, will stop + ** firing because the bridge has been destroyed. + */ + Detach_This_From_All(As_Target(cell2), true); + + ObjectClass * obj = bridge_cell->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + } + cell2++; + } + cell2 += MAP_CELL_W - bridge_w; + } + Shake_The_Screen(3); + + destroyed = true; + } else { + /* + ** All this code is for the multi-part bridges. + */ + if (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3E) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + switch (ttype) { + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + ttype++; + Add_Cell_Update(cell_updates, update_count, ttype, cell); + break; + } + + /* + ** If we were a middle piece that just got blown up, update the + ** adjoining pieces to make sure they're shaped properly. + */ + if (ttype == TEMPLATE_BRIDGE_3C) { + // check the template below us, at x-1, y+1 + CELL cell2 = cell + (MAP_CELL_W - 1); + CellClass * celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + // It was also destroyed. Update us and it. + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell); + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); + } + + // Now check the template above us, at x+1, y-1. + cell2 = cell - (MAP_CELL_W - 1); + celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + if (cellptr->TType == TEMPLATE_BRIDGE_3D) { + // if we're already one-sided, turn us to all water + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell); + } + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); + } + Map.Zone_Reset(MZONEF_ALL); + } + + /* + ** If we're an end bridge piece, update the adjoining piece to + ** be the proper shape. + */ + if (cellptr->TType == TEMPLATE_BRIDGE_1C) { + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + + // Point to the template below us, x-1, y+2 + CELL cell2 = cell + (MAP_CELL_W * 2) - 1; + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); + break; + case TEMPLATE_BRIDGE_3D: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); + break; + } + } else { + if (cellptr->TType == TEMPLATE_BRIDGE_2C) { + // Point to the template above us, x+2, y-1 + CELL cell2 = cell - (MAP_CELL_W - 2); + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); + break; + case TEMPLATE_BRIDGE_3E: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); + break; + } + } + } + if (cellptr->TType == TEMPLATE_BRIDGE_1C || + cellptr->TType == TEMPLATE_BRIDGE_2C || + (cellptr->TType >= TEMPLATE_BRIDGE_3C && cellptr->TType <= TEMPLATE_BRIDGE_3E)) { + int x, y, tdata = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + CellClass * ptr = &(*this)[(CELL)(cell + x)]; + if (ptr->TType == cellptr->TType || ptr->Land_Type() == LAND_RIVER || ptr->Land_Type() == LAND_WATER) { + Detach_This_From_All(As_Target((CELL)(cell+tdata)), true); + + ObjectClass * obj = ptr->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + + } + tdata++; + } + cell += MAP_CELL_W; + } + Map.Zone_Reset(MZONEF_ALL); + destroyed = true; + } + Shake_The_Screen(3); + } + } + + int cell_index = 0; + char cell_name[_MAX_PATH] = { 0 }; + char icon_number[32] = { 0 }; + int icon = 0; + void *image_data = 0; + for (int i = 0; i < update_count; i++) { + const TemplateTypeClass* type = cell_updates[i].Type; + CELL cell = cell_updates[i].Cell; + for (int y = 0; y < type->Height; y++) { + for (int x = 0; x < type->Width; x++) { + CELL newcell = cell + y * MAP_CELL_W + x; + if (Map.In_Radar(newcell)) { + CellClass * newcellptr = &Map[newcell]; + if (newcellptr->Get_Template_Info(cell_name, icon, image_data)) { + itoa(icon, icon_number, 10); + strncat(cell_name, "_i", 32); + strncat(cell_name, icon_number, 32); + strncat(cell_name, ".tga", 32); + On_Update_Map_Cell(Cell_X(newcell), Cell_Y(newcell), cell_name); + } + } + } + } + } + } + return(destroyed); +} + + +/*********************************************************************************************** + * MapClass::Detach -- Remove specified object from map references. * + * * + * This routine will take the object (represented by a target value) and remove all * + * references to it from the map. Typically, this is used to remove trigger reference. * + * * + * INPUT: target -- The target object to remove from the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +void MapClass::Detach(TARGET target, bool) +{ + /* + ** Remove this trigger from the map zone/line tracking list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < MapTriggers.Count(); index++) { + if (MapTriggers[index] == As_Trigger(target)) { + MapTriggers.Delete(index); + break; + } + } + + /* + ** Loop through all cells; remove any reference to this trigger + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger == As_Trigger(target)) { + (*this)[cell].Trigger = NULL; + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * * + * This will examine the entire map and return the number of bridges that are intact. An * + * intact bridge is one that units can travel over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of intact bridges on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Intact_Bridge_Count(void) const +{ + /* + ** Count all non-destroyed bridges on the map. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + switch (cellptr->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + if (cellptr->TIcon == 6) { + count++; + } + break; + + default: + break; + } + + cellptr++; + } + + return(count); +} + + +/*********************************************************************************************** + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * * + * This routine will pick a random location on the map. It performs no legality checking * + * other than forcing the cell to be on the map proper. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1996 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Pick_Random_Location(void) const +{ + int x = Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1); + int y = Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1); + + return(XY_Cell(x, y)); +} + + +#if (1) + +/*********************************************************************************************** + * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * + * * + * INPUT: House to shroud * + * * + * OUTPUT: None + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 BWG : Created. * + * 08/12/2019 ST : Updated for client/server multiplayer * + *=============================================================================================*/ +void MapClass::Shroud_The_Map(HouseClass *house) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->Is_Mapped(house) || cellptr->Is_Visible(house)) { + + cellptr->Redraw_Objects(); + + /* + ** BG: remove "ring of darkness" around edge of map. + */ + int x = Cell_X(cell); + int y = Cell_Y(cell); + if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && + y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { + cellptr->Set_Mapped(house, false); + cellptr->Set_Visible(house, false); + } + } + } + for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { + ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; + if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == house) { + layer_object->Look(); + } + } + Flag_To_Redraw(true); +} + + +#else + +// +// Old code for posterity. ST - 8/12/2019 11:34AM +// + +/*********************************************************************************************** + * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 BWG : Created. * + *=============================================================================================*/ +void MapClass::Shroud_The_Map(void) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->IsMapped || cellptr->IsVisible) { + cellptr->Redraw_Objects(); + /* + ** BG: remove "ring of darkness" around edge of map. + */ + int x = Cell_X(cell); + int y = Cell_Y(cell); + if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && + y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { + cellptr->IsMapped = false; + cellptr->IsVisible = false; + } + } + } + for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { + ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; + if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == PlayerPtr) { + layer_object->Look(); + } + } + Flag_To_Redraw(true); +} +#endif \ No newline at end of file diff --git a/REDALERT/MAP.H b/REDALERT/MAP.H new file mode 100644 index 000000000..58201873d --- /dev/null +++ b/REDALERT/MAP.H @@ -0,0 +1,183 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAP_H +#define MAP_H + +#include "gscreen.h" +#include "crate.h" + +class MapClass: public GScreenClass +{ + public: + + MapClass(void) {}; + MapClass(NoInitClass const & x) : GScreenClass(x), Array(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // Theater-specific inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Alloc_Cells(void); // Allocates buffers + virtual void Free_Cells(void); // Frees buffers + virtual void Init_Cells(void); // Frees buffers + + /*-------------------------------------------------------- + ** Main functions that deal with groupings of cells within the map or deals with the cell + ** as it relates to the map - not what the cell contains. + */ + CELL Pick_Random_Location(void) const; + int Intact_Bridge_Count(void) const; + bool Base_Region(CELL cell, HousesType & house, ZoneType & zone) const; + CELL Nearby_Location(CELL cell, SpeedType speed, int zone=-1, MZoneType check=MZONE_NORMAL, bool checkflagged=false) const; + ObjectClass * Close_Object(COORDINATE coord) const; + virtual void Detach(ObjectClass * ) {}; + int Cell_Region(CELL cell); + int Cell_Threat(CELL cell, HousesType house); + bool In_Radar(CELL cell) const; + void Sight_From(CELL cell, int sightrange, HouseClass *house, bool incremental=false); + void Jam_From(CELL cell, int jamrange, HouseClass *house); + void Shroud_From(CELL cell, int sightrange, HouseClass *house); + void UnJam_From(CELL cell, int jamrange, HouseClass *house); + void Place_Down(CELL cell, ObjectClass * object); + void Pick_Up(CELL cell, ObjectClass * object); + void Overlap_Down(CELL cell, ObjectClass * object); + void Overlap_Up(CELL cell, ObjectClass * object); + bool Read_Binary(Straw & straw); + int Write_Binary(Pipe & pipe); + bool Place_Random_Crate(void); + bool Remove_Crate(CELL cell); + bool Zone_Reset(int method); + bool Zone_Cell(CELL cell, int zone); + int Zone_Span(CELL cell, int zone, MZoneType check); + bool Destroy_Bridge_At(CELL cell); + void Detach(TARGET target, bool all=true); + void Shroud_The_Map(HouseClass *house); + + long Overpass(void); + + virtual void Logic(void); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Debug routine + */ + int Validate(void); + + /* + ** This is the dimensions and position of the sub section of the global map. + ** It is this region that appears on the radar map and constrains normal + ** movement. + */ + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + + /* + ** This is the total value of all harvestable Tiberium on the map. + */ + long TotalValue; + + CellClass & operator [] (COORDINATE coord) {return(Array[Coord_Cell(coord)]);}; + CellClass & operator [] (CELL cell) {return(Array[cell]);}; + CellClass const & operator [] (COORDINATE coord) const {return(Array[Coord_Cell(coord)]);}; + CellClass const & operator [] (CELL cell) const {return(Array[cell]);}; + int ID(CellClass const * ptr) {return(Array.ID(ptr));}; + int ID(CellClass const & ptr) {return(Array.ID(ptr));}; + + protected: + + /* + ** This is the array of cell objects. + */ + VectorClass Array; + + /* + ** These are the size dimensions of the underlying array of cell objects. + ** This is the dimensions of the "map" that the tactical view is + ** restricted to. + */ + int XSize; + int YSize; + int Size; + + static int const RadiusCount[11]; + static int const RadiusOffset[]; + + /* + ** This specifies the information for the various crates in the game. + */ + CrateClass Crates[256]; + + private: + friend class CellClass; + + /* + ** Tiberium growth potential cells are recorded here. + */ + CELL TiberiumGrowth[MAP_CELL_W/2]; + int TiberiumGrowthCount; + int TiberiumGrowthExcess; + + /* + ** List of cells that are full enough strength that they could spread + ** Tiberium to adjacent cells. + */ + CELL TiberiumSpread[MAP_CELL_W/2]; + int TiberiumSpreadCount; + int TiberiumSpreadExcess; + + /* + ** This is the current cell number in the incremental map scan process. + */ + CELL TiberiumScan; + + enum MapEnum {SCAN_AMOUNT=MAP_CELL_TOTAL}; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/MAPEDDLG.CPP b/REDALERT/MAPEDDLG.CPP new file mode 100644 index 000000000..f75e99046 --- /dev/null +++ b/REDALERT/MAPEDDLG.CPP @@ -0,0 +1,2705 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Map Editor dialogs & main menu options * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Handle_Triggers -- processes the trigger dialogs * + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * MapEditClass::New_Scenario -- creates a new scenario * + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * MapEditClass::Select_Trigger -- lets user select a trigger * + * MapEditClass::Size_Map -- lets user set size & location of map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::New_Scenario -- creates a new scenario * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Prompts user for map size * + * - Initializes the scenario by calling Clear_Scenario(), which calls * + * everybody's Init() routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = new scenario created, -1 = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::New_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + HousesType house; + + /* + ** Force the house save value to match the player house. + */ + if (PlayerPtr) { + switch (PlayerPtr->Class->House) { + case HOUSE_SPAIN: + player = SCEN_PLAYER_SPAIN; + break; + + case HOUSE_GREECE: + player = SCEN_PLAYER_GREECE; + break; + + default: + case HOUSE_USSR: + player = SCEN_PLAYER_USSR; + break; + } + } + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("New Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + ScenarioInit++; + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Create houses + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + new HouseClass(house); + } + + switch (player) { + case SCEN_PLAYER_MPLAYER: + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI1); + PlayerPtr->IsHuman = true; + LastHouse = HOUSE_MULTI1; + break; + + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Init the entire map + */ +// Init_Clear(); + Fill_In_Data(); + + /* + ** Prompt for map size + */ + Size_Map(-1, -1, 30, 30); + + /* + ** Set the Home & Reinforcement Cells to the center of the map + */ + Scen.Waypoint[WAYPT_REINF] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + (*this)[TacticalCoord].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + + Set_Tactical_Position(Cell_Coord(Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR) - (5 * RESFACTOR))); + ScenarioInit--; + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Loads the INI file for that scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Load_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + NodeNameType * who; // node to add to Players + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Load Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Read_Scenario_Ini() must be able to set PlayerPtr to the right house: + ** - Reading the INI will create the house objects + ** - PlayerPtr must be set before any Techno objects are created + ** - For GDI or NOD scenarios, PlayerPtr is set by reading the INI; + ** but for multiplayer, it's set via the Players vector; so, here we have + ** to set various multiplayer variables to fool the Assign_Houses() routine + ** into working properly. + */ + if (player == SCEN_PLAYER_MPLAYER) { + Clear_Vector(&Session.Players); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Session.NumPlayers = 1; + LastHouse = HOUSE_MULTI1; + } else { +#ifdef NEVER + if (ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } else { +#endif + LastHouse = HOUSE_GOOD; + } + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Read the INI + */ + if (Read_Scenario_INI(Scen.ScenarioName) == 0) { + if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'G'){ + WWMessageBox().Process("Please insert Red Alert CD1"); + }else if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'U') + WWMessageBox().Process("Please insert Red Alert CD2"); + else + WWMessageBox().Process("Unable to read scenario!"); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + Fill_In_Data(); + GamePalette.Set(); +// Set_Palette(GamePalette); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Saves the INI file for this scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = error/cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Save_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; +// FILE * fp; +// char fname[13]; + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Save Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Warning if scenario already exists + */ +// Scen.Set_Scenario_Name(scen_num, player, dir, var); +// fp = fopen(fname, "rb"); +// if (fp) { +// fclose(fp); +// rc = WWMessageBox().Process("File exists. Replace?", TXT_YES, TXT_NO); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// Render(); +// if (rc==1) { +// return(-1); +// } +// } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Player may have changed from GDI to NOD, so change playerptr accordingly + */ + switch (player) { + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Write the INI + */ + Write_Scenario_INI(Scen.ScenarioName); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * * + * Prompts user for: * + * - House (GDI, NOD) * + * - Scenario # * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Caption ³ * + * ³ ³ * + * ³ Scenario ___ ³ * + * ³ Version ___ ³ * + * ³ ³ * + * ³ [East] [West] ³ * + * ³ ³ * + * ³ [ GDI ] ³ * + * ³ [ NOD ] ³ * + * ³ [Multi-Player] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * caption string to use as a title * + * scen_nump output: ptr to scenario # * + * playerp output: ptr to player type * + * dirp output: ptr to direction * + * varp output: ptr to variation * + * multi 1 = allow to change single/multiplayer; 0 = not * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 09/04/1996 JLB : Simplified * + *=========================================================================*/ +int MapEditClass::Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 200, // dialog width + D_DIALOG_H = 164, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_SCEN_W = 45, // Scenario # width + D_SCEN_H = 9, // Scenario # height + D_SCEN_X = D_DIALOG_CX + 5, // Scenario # x + D_SCEN_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, // Scenario # y + + D_VARA_W = 13, // Version A width + D_VARA_H = 9, // Version A height + D_VARA_X = D_DIALOG_CX - (D_VARA_W * 5) / 2, // Version A x + D_VARA_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version A y + + D_VARB_W = 13, // Version B width + D_VARB_H = 9, // Version B height + D_VARB_X = D_VARA_X + D_VARA_W, // Version B x + D_VARB_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version B y + + D_VARC_W = 13, // Version C width + D_VARC_H = 9, // Version C height + D_VARC_X = D_VARB_X + D_VARB_W, // Version C x + D_VARC_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version C y + + D_VARD_W = 13, // Version D width + D_VARD_H = 9, // Version D height + D_VARD_X = D_VARC_X + D_VARC_W, // Version D x + D_VARD_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version D y + + D_VARLOSE_W = 13, // Version Lose width + D_VARLOSE_H = 9, // Version Lose height + D_VARLOSE_X = D_VARD_X + D_VARD_W, // Version Lose x + D_VARLOSE_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version Lose y + + D_EAST_W = 50, // EAST width + D_EAST_H = 9, // EAST height + D_EAST_X = D_DIALOG_CX - D_EAST_W - 5, // EAST x + D_EAST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_WEST_W = 50, // WEST width + D_WEST_H = 9, // WEST height + D_WEST_X = D_DIALOG_CX + 5, // WEST x + D_WEST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_GDI_W = 90, // GDI width + D_GDI_H = 9, // GDI height + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), // GDI x + D_GDI_Y = D_EAST_Y + D_EAST_H + D_MARGIN, // GDI y + + D_NOD_W = 90, // NOD width + D_NOD_H = 9, // NOD height + D_NOD_X = D_DIALOG_CX - (D_NOD_W / 2), // NOD x + D_NOD_Y = D_GDI_Y + D_GDI_H, // NOD y + + D_NEU_W = 90, // Neutral width + D_NEU_H = 9, // Neutral height + D_NEU_X = D_DIALOG_CX - (D_NOD_W / 2), // Neutral x + D_NEU_Y = D_NOD_Y + D_NOD_H, // Neutral y + + D_MPLAYER_W = 90, // Multi-Player width + D_MPLAYER_H = 9, // Multi-Player height + D_MPLAYER_X = D_DIALOG_CX - (D_MPLAYER_W / 2), // Multi-Player x + D_MPLAYER_Y = D_NEU_Y + D_NEU_H, // Multi-Player y + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN+15), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN+15), // Cancel y + + }; + + /* + ** Button enumerations + */ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_MPLAYER, + BUTTON_EAST, + BUTTON_WEST, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SCENARIO, + BUTTON_VAR_A, + BUTTON_VAR_B, + BUTTON_VAR_C, + BUTTON_VAR_D, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + + /* + ** Other Variables + */ + char scen_buf[10]={0}; // buffer for editing scenario # + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + EditClass editbtn (BUTTON_SCENARIO, scen_buf, 5, TPF_EFNT|TPF_NOSHADOW, D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::ALPHANUMERIC); +#else + EditClass editbtn (BUTTON_SCENARIO, scen_buf, 5, TPF_EFNT|TPF_NOSHADOW, D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::NUMERIC); +#endif + + TextButtonClass varabtn(BUTTON_VAR_A, "A", TPF_EBUTTON, D_VARA_X, D_VARA_Y, D_VARA_W, D_VARA_H); + TextButtonClass varbbtn(BUTTON_VAR_B, "B", TPF_EBUTTON, D_VARB_X, D_VARB_Y, D_VARB_W, D_VARB_H); + TextButtonClass varcbtn(BUTTON_VAR_C, "C", TPF_EBUTTON, D_VARC_X, D_VARC_Y, D_VARC_W, D_VARC_H); + TextButtonClass vardbtn(BUTTON_VAR_D, "D", TPF_EBUTTON, D_VARD_X, D_VARD_Y, D_VARD_W, D_VARD_H); + TextButtonClass gdibtn(BUTTON_GDI, "North (Spain)", TPF_EBUTTON, D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + TextButtonClass nodbtn(BUTTON_NOD, "South (Greece)", TPF_EBUTTON, D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + TextButtonClass neubtn(BUTTON_NEUTRAL, HouseTypeClass::As_Reference(HOUSE_USSR).IniName, TPF_EBUTTON, D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + TextButtonClass playermbtn(BUTTON_MPLAYER, "Multiplayer", TPF_EBUTTON, D_MPLAYER_X, D_MPLAYER_Y, D_MPLAYER_W, D_MPLAYER_H); + TextButtonClass eastbtn(BUTTON_EAST, "East", TPF_EBUTTON, D_EAST_X, D_EAST_Y, D_EAST_W, D_EAST_H); + TextButtonClass westbtn(BUTTON_WEST, "West", TPF_EBUTTON, D_WEST_X, D_WEST_Y, D_WEST_W, D_WEST_H); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (scen_nump < 100) { + sprintf(scen_buf, "%d", scen_nump); // init edit buffer + } else { + char first = scen_nump / 36; + char second = scen_nump % 36; + scen_buf[0] = first + 'A'; +//Mono_Printf("picking map, scen# = %d, first = %c, second = %d (numeric)\n",scen_nump, scen_buf[0],second);Keyboard->Get();Keyboard->Get(); + if (second < 10) { + scen_buf[1] = second + '0'; + } else { + scen_buf[1] = (second-10) + 'A'; + } + scen_buf[2] = 0; + } +#else + sprintf(scen_buf, "%d", scen_nump); // init edit buffer +#endif + editbtn.Set_Text(scen_buf, 5); + + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + switch (varp) { + case SCEN_VAR_A: + varabtn.Turn_On(); + break; + + case SCEN_VAR_B: + varbbtn.Turn_On(); + break; + + case SCEN_VAR_C: + varcbtn.Turn_On(); + break; + + case SCEN_VAR_D: + vardbtn.Turn_On(); + break; + } + + /* + ** Create the button list + */ + commands = &editbtn; + varabtn.Add_Tail(*commands); + varbbtn.Add_Tail(*commands); + varcbtn.Add_Tail(*commands); + vardbtn.Add_Tail(*commands); + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neubtn.Add_Tail(*commands); + playermbtn.Add_Tail(*commands); + eastbtn.Add_Tail(*commands); + westbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Init the button states + */ + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + neubtn.Turn_Off(); + playermbtn.Turn_Off(); + if (playerp == SCEN_PLAYER_MPLAYER) { + playermbtn.Turn_On(); + } else { + if (PlayerPtr) { + switch (PlayerPtr->Class->House) { + case HOUSE_SPAIN: + gdibtn.Turn_On(); + break; + + case HOUSE_GREECE: + nodbtn.Turn_On(); + break; + + case HOUSE_USSR: + neubtn.Turn_On(); + break; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } else { + switch (Scen.ScenarioName[2]) { + case 'G': + gdibtn.Turn_On(); + break; + + case 'U': + nodbtn.Turn_On(); + break; + + case 'M': + playermbtn.Turn_On(); + break; + } +#endif + } + } + + eastbtn.Turn_Off(); + westbtn.Turn_Off(); + if (dirp == SCEN_DIR_EAST) { + eastbtn.Turn_On(); + } else { + westbtn.Turn_On(); + } + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(caption, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + Fancy_Text_Print("Scenario", D_DIALOG_CX - 5, D_SCEN_Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); + commands->Draw_All(); + Show_Mouse(); + + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + + /* + ** Handle a click on one of the scenario variation group buttons. + */ + case (BUTTON_VAR_A | KN_BUTTON): + case (BUTTON_VAR_B | KN_BUTTON): + case (BUTTON_VAR_C | KN_BUTTON): + case (BUTTON_VAR_D | KN_BUTTON): + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + switch (input) { + case (BUTTON_VAR_A | KN_BUTTON): + varp = SCEN_VAR_A; + varabtn.Turn_On(); + break; + + case (BUTTON_VAR_B | KN_BUTTON): + varp = SCEN_VAR_B; + varbbtn.Turn_On(); + break; + + case (BUTTON_VAR_C | KN_BUTTON): + varp = SCEN_VAR_C; + varcbtn.Turn_On(); + break; + + case (BUTTON_VAR_D | KN_BUTTON): + varp = SCEN_VAR_D; + vardbtn.Turn_On(); + break; + } + break; + + /* + ** Handle a click on the east/west variation group. + */ + case (BUTTON_EAST | KN_BUTTON): + case (BUTTON_WEST | KN_BUTTON): + westbtn.Turn_Off(); + eastbtn.Turn_Off(); + switch (input) { + case (BUTTON_EAST | KN_BUTTON): + dirp = SCEN_DIR_EAST; + eastbtn.Turn_On(); + break; + + case (BUTTON_WEST | KN_BUTTON): + dirp = SCEN_DIR_WEST; + westbtn.Turn_On(); + break; + } + break; + + + /* + ** Handle a click on one of the player category + ** group buttons. + */ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MPLAYER | KN_BUTTON): + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + neubtn.Turn_Off(); + playermbtn.Turn_Off(); + switch (input) { + case (BUTTON_GDI | KN_BUTTON): + playerp = SCEN_PLAYER_SPAIN; + gdibtn.Turn_On(); + break; + + case (BUTTON_NOD | KN_BUTTON): + playerp = SCEN_PLAYER_GREECE; + nodbtn.Turn_On(); + break; + + case (BUTTON_NEUTRAL | KN_BUTTON): + playerp = SCEN_PLAYER_USSR; + neubtn.Turn_On(); + break; + + case (BUTTON_MPLAYER | KN_BUTTON): + playerp = SCEN_PLAYER_MPLAYER; + playermbtn.Turn_On(); + break; + } + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case (BUTTON_SCENARIO | KN_BUTTON): + break; + + default: + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Save selections & return + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (scen_buf[0] <= '9' && scen_buf[1] <= '9') { + scen_nump = atoi(scen_buf); + } else { + char first = scen_buf[0]; + char second = scen_buf[1]; + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scen_nump = (first * 36) + second; +//Mono_Printf("Converted to: %d, %d = %d\n",first, second, scen_nump);Keyboard->Get();Keyboard->Get(); + } +#else + scen_nump = atoi(scen_buf); +#endif + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Size_Map -- lets user set size & location of map * + * * + * Lets the user select a side of the map and expand/shrink it to the * + * desired size, or move the whole map around the available map area. * + * * + * The entire available map area is displayed, but the map is limited such * + * that there's always one blank cell around the map; this lets objects * + * properly exit the screen, since they have a blank undisplayed cell to * + * exit onto. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Clear Terrain ³ * + * ³ ³ ³ Water ³ * + * ³ ³ ³ Tiberium ³ * + * ³ ³ ³ Rock/Wall/Road ³ * + * ³ ³ (Map Area) ³ GDI Unit ³ * + * ³ ³ ³ NOD Unit ³ * + * ³ ³ ³ Neutral Unit ³ * + * ³ ³ ³ Terrain Object ³ * + * ³ ³ ³ Starting Cell ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ X Y Width Height ³ * + * ³ ## ## ## ## ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * x,y,w,h: initial size parameters (-1 = center the thing) * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Size_Map(int x, int y, int w, int h) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 350, // dialog width + D_DIALOG_H = 225, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_BORD_X1 = D_DIALOG_X + 45, +// D_BORD_X1 = D_DIALOG_X + (D_DIALOG_W / 2 - MAP_CELL_W) / 2, + D_BORD_Y1 = D_DIALOG_Y + 25, + D_BORD_X2 = D_BORD_X1 + MAP_CELL_W + 1, + D_BORD_Y2 = D_BORD_Y1 + MAP_CELL_H + 1, + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_X + 45, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN + 10), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (35 + D_CANCEL_W), // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN + 10), // Cancel y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_OK=100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MAP, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map board, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables: + */ + RedrawType display; // requested redraw level + bool cancel = false; // true = user cancels + KeyNumType input; // user input + int grabbed = 0; // 1=TLeft,2=TRight,3=BRight,4=BLeft + int map_x1; // map coords x1, pixel coords + int map_x2; // map coords x2, pixel coords + int map_y1; // map coords y1, pixel coords + int map_y2; // map coords y2, pixel coords + int delta1, delta2; // mouse-click proximity + int mx,my; // last-saved mouse coords +// char txt[40]; + int txt_x,txt_y; // for displaying text +// unsigned index; // for drawing map symbology + CELL cell; // for drawing map symbology + int color; // for drawing map symbology + ObjectClass * occupier; // cell's occupier + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass * commands = NULL; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Set up the actual map area relative to the map's border coords + */ + if (x==-1) { + map_x1 = D_BORD_X1 + (MAP_CELL_W - w) / 2 + 1; + } else { + map_x1 = D_BORD_X1 + x + 1; + } + + if (y==-1) { + map_y1 = D_BORD_Y1 + (MAP_CELL_H - h) / 2 + 1; + } else { + map_y1 = D_BORD_Y1 + y + 1; + } + + map_x2 = map_x1 + w - 1; + map_y2 = map_y1 + h - 1; + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main processing loop + */ + display = REDRAW_ALL; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Redraw the background, map border, key, and coord labels + */ + if (display >= REDRAW_BACKGROUND) { + + /* + ** Background + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_SIZE_MAP, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the map border + */ + if (LogicPage->Lock()) { + LogicPage->Draw_Rect(D_BORD_X1, D_BORD_Y1, D_BORD_X2, D_BORD_Y2, scheme->Shadow); +// for (index = D_BORD_X1; index < D_BORD_X2; +// index += (320/ICON_PIXEL_W)) { +// LogicPage->Put_Pixel(index, D_BORD_Y1-1, scheme->Shadow); +// LogicPage->Put_Pixel(index, D_BORD_Y2+1, scheme->Shadow); +// } +// for (index = D_BORD_Y1; index < D_BORD_Y2-8; +// index += (200/ICON_PIXEL_H)) { +// LogicPage->Put_Pixel(D_BORD_X1-1, index, scheme->Shadow); +// LogicPage->Put_Pixel(D_BORD_X2+1, index, scheme->Shadow); +// } + + /* + ** Draw the map "key" + */ + txt_x = D_BORD_X2 + 15; + txt_y = D_BORD_Y1; + Plain_Text_Print("Clear Terrain", txt_x, txt_y, GroundColor[LAND_CLEAR], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Water", txt_x, txt_y, GroundColor[LAND_WATER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Tiberium", txt_x, txt_y, GroundColor[LAND_TIBERIUM], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Rock", txt_x, txt_y, GroundColor[LAND_ROCK], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Wall", txt_x, txt_y, GroundColor[LAND_WALL], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Beach", txt_x, txt_y, GroundColor[LAND_BEACH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Rough", txt_x, txt_y, GroundColor[LAND_ROUGH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("River", txt_x, txt_y, GroundColor[LAND_RIVER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("GDI Unit", txt_x, txt_y, YELLOW, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Nod Unit", txt_x, txt_y, RED, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Neutral Unit", txt_x, txt_y, PURPLE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Terrain Object", txt_x, txt_y, DKGREEN, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Starting Cell", txt_x, txt_y, WHITE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + + /* + ** Draw the coordinate labels + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 43; + Fancy_Text_Print(" X", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Y", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Width", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Height", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + LogicPage->Unlock(); + } + + /* + ** Redraw the buttons + */ + commands->Flag_List_To_Redraw(); + } + + /* + ** Redraw the map symbology & location + */ + if (display >= REDRAW_MAP) { + + if (LogicPage->Lock()) { + + /* + ** Erase the map interior + */ + LogicPage->Fill_Rect(D_BORD_X1 + 1, D_BORD_Y1 + 1, D_BORD_X2 - 1, D_BORD_Y2 - 1, BLACK); + + /* + ** Draw Land map symbols (use color according to Ground[] array). + */ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier == NULL) { + color = GroundColor[(*this)[cell].Land_Type()]; + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ** Draw the actual map location + */ + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, WHITE); + switch (grabbed) { + case 1: + LogicPage->Draw_Line(map_x1, map_y1, map_x1 + 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x1, map_y1, map_x1, map_y1 + 5, BLUE); + break; + + case 2: + LogicPage->Draw_Line(map_x2, map_y1, map_x2 - 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x2, map_y1, map_x2, map_y1 + 5, BLUE); + break; + + case 3: + LogicPage->Draw_Line(map_x2, map_y2, map_x2 - 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x2, map_y2, map_x2, map_y2 - 5, BLUE); + break; + + case 4: + LogicPage->Draw_Line(map_x1, map_y2, map_x1 + 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x1, map_y2, map_x1, map_y2 - 5, BLUE); + break; + + case 5: + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, BLUE); + break; + + default: + break; + } + + /* + ** Draw Unit map symbols (Use the radar map color according to + ** that specified in the house type class object. + ** DKGREEN = terrain object + */ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier) { + color = DKGREEN; + if (occupier && occupier->Owner() != HOUSE_NONE) { + color = ColorRemaps[HouseClass::As_Pointer(occupier->Owner())->RemapColor].Color; + } + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ** Draw Home location + */ + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(Scen.Waypoint[WAYPT_HOME]) + 1, D_BORD_Y1 + Cell_Y(Scen.Waypoint[WAYPT_HOME]) + 1, WHITE); + + /* + ** Erase old coordinates + */ +// LogicPage->Fill_Rect( D_DIALOG_X + 7, +// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22, +// D_DIALOG_X + D_DIALOG_W - 7, +// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22 + 10, BLACK); + + /* + ** Draw the coordinates + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 32; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x1 - D_BORD_X1 - 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y1 - D_BORD_Y1 - 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x2 - map_x1 + 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y2 - map_y1 + 1); + + LogicPage->Unlock(); + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Process user input + */ + input = commands->Input(); + + /* + ** Normal button processing: This is done when the mouse button is NOT + ** being held down ('grabbed' is 0). + */ + if (grabbed == 0) { + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case KN_LMOUSE: + /* + ** Grab top left + */ + delta1 = abs(Keyboard->MouseQX - map_x1); + delta2 = abs(Keyboard->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 1; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab top right + */ + delta1 = abs(Keyboard->MouseQX - map_x2); + delta2 = abs(Keyboard->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 2; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab bottom right + */ + delta1 = abs(Keyboard->MouseQX - map_x2); + delta2 = abs(Keyboard->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 3; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab bottom left + */ + delta1 = abs(Keyboard->MouseQX - map_x1); + delta2 = abs(Keyboard->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 4; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab the whole map + */ + delta1 = abs(Keyboard->MouseQX - ((map_x1 + map_x2) / 2)); + delta2 = abs(Keyboard->MouseQY - ((map_y1 + map_y2) / 2)); + if (delta1 < (map_x2 - map_x1) / 4 && + delta2 < (map_y2 - map_y1) / 4) { + grabbed = 5; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + } + break; + + default: + break; + } + } else { + + /* + ** Mouse motion processing: This is done while the left mouse button IS + ** being held down. + ** - First, check for the button release; if detected, un-grab + ** - Then, handle mouse motion. WWLIB doesn't pass through a KN_MOUSE_MOVE + ** value while the button is being held down, so this case must be + ** trapped as a default. + */ + switch (input) { + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + grabbed = 0; + display = REDRAW_MAP; + break; + + default: + delta1 = Get_Mouse_X() - mx; + delta2 = Get_Mouse_Y() - my; + if (delta1==0 && delta2==0) { + break; + } + + /* + ** Move top left + */ + if (grabbed==1) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move top right + */ + if (grabbed==2) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move bottom right + */ + if (grabbed==3) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move bottom left + */ + if (grabbed==4) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move whole map + */ + if (grabbed==5) { + if (map_x1 + delta1 > D_BORD_X1 + 1 && map_x2 + delta1 < D_BORD_X2 - 1) { + map_x1 += delta1; + map_x2 += delta1; + } + + if (map_y1 + delta2 > D_BORD_Y1 + 1 && map_y2 + delta2 < D_BORD_Y2 - 1) { + map_y1 += delta2; + map_y2 += delta2; + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + break; + } + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Save selections + */ + MapCellX = map_x1 - D_BORD_X1 - 1; + MapCellY = map_y1 - D_BORD_Y1 - 1; + MapCellWidth = map_x2 - map_x1 + 1; + MapCellHeight = map_y2 - map_y1 + 1; + + /* + ** Clip Home Cell to new map size + */ + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) < MapCellX) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) > MapCellX + MapCellWidth - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth - 1, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) < MapCellY) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) > MapCellY + MapCellHeight - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY + MapCellHeight - 1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * * + * Edits the house specific and general scenario options. * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + * 02/13/1996 JLB : Revamped to new system. * + *=========================================================================*/ +int MapEditClass::Scenario_Dialog(void) +{ + TheaterType orig_theater = Scen.Theater; // original theater + HousesType house = PlayerPtr->Class->House; + HousesType newhouse = house; + HouseStaticClass hdata[HOUSE_COUNT]; + + /* + ** Fill in the house data for each house that exists. + */ + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr) { + hdata[h] = hptr->Control; + } + } + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 320 * RESFACTOR, + D_DIALOG_H = 200 * RESFACTOR, + D_DIALOG_X = ((320 * RESFACTOR - D_DIALOG_W) / 2), + D_DIALOG_Y = ((200 * RESFACTOR - D_DIALOG_H) / 2), + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + 15 * RESFACTOR, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 15 * RESFACTOR, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+15*RESFACTOR), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - 15*RESFACTOR + }; + + /* + ** Button enumerations: + */ + enum { + LIST_THEATER=100, + BUTTON_DESCRIPTION, + BUTTON_ALLIES, + BUTTON_CONTROL, + BUTTON_SMARTIES, + BUTTON_BASE, + BUTTON_NOSPYPLANE, + BUTTON_INHERIT, + BUTTON_TIMER, + BUTTON_THEME, + BUTTON_RECORD, + BUTTON_EVAC, + BUTTON_MONEYTIB, + BUTTON_TECH, + BUTTON_TRUCKCRATE, + BUTTON_ENDOFGAME, + BUTTON_SKIPSCORE, + BUTTON_ONETIME, + BUTTON_NOMAPSEL, + BUTTON_HOUSE, + BUTTON_CREDITS, + BUTTON_SOURCE, + BUTTON_MAXUNIT, + BUTTON_INTRO, + BUTTON_BRIEFING, + BUTTON_ACTION, + BUTTON_WIN, + BUTTON_LOSE, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + ControlClass * commands = NULL; // the button list + + /* + ** Theater choice drop down list. + */ + char theatertext[45] = ""; + DropListClass theaterbtn(LIST_THEATER, theatertext, sizeof(theatertext)-1, + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+30, 65, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TheaterType t = THEATER_FIRST; t < THEATER_COUNT; t++) { + theaterbtn.Add_Item(Theaters[t].Name); + } + theaterbtn.Set_Selected_Index(orig_theater); + + char description[DESCRIP_MAX] = ""; + strcpy(description, Scen.Description); + EditClass desc(BUTTON_DESCRIPTION, description, sizeof(description), TPF_EFNT|TPF_NOSHADOW, theaterbtn.X+theaterbtn.Width+15, theaterbtn.Y, 160); + + /* + ** Button that tells if this scenario should inherit buildings from the previous. + */ + CheckBoxClass inherit(BUTTON_INHERIT, theaterbtn.X+theaterbtn.Width+15+250, theaterbtn.Y); + if (Scen.IsToInherit) { + inherit.Turn_On(); + } else { + inherit.Turn_Off(); + } + + /* + ** Records scenario disposition into holding slot. + */ + CheckBoxClass record(BUTTON_RECORD, inherit.X, inherit.Y+8); + if (Scen.IsToCarryOver) { + record.Turn_On(); + } else { + record.Turn_Off(); + } + + /* + ** Should Tanya/civilian be automatically evacuated? + */ + CheckBoxClass tanya(BUTTON_EVAC, record.X, record.Y+8); + if (Scen.IsTanyaEvac) { + tanya.Turn_On(); + } else { + tanya.Turn_Off(); + } + + /* + ** End of game with with scenario? + */ + CheckBoxClass endofgame(BUTTON_ENDOFGAME, tanya.X, tanya.Y+8); + if (Scen.IsEndOfGame) { + endofgame.Turn_On(); + } else { + endofgame.Turn_Off(); + } + + /* + ** Timer inherit logic. + */ + CheckBoxClass timercarry(BUTTON_TIMER, endofgame.X, endofgame.Y+8); + if (Scen.IsInheritTimer) { + timercarry.Turn_On(); + } else { + timercarry.Turn_Off(); + } + + /* + ** Disable spy plane option? + */ + CheckBoxClass nospyplane(BUTTON_NOSPYPLANE, timercarry.X, timercarry.Y+8); + if (Scen.IsNoSpyPlane) { + nospyplane.Turn_On(); + } else { + nospyplane.Turn_Off(); + } + + /* + ** Skip the score screen? + */ + CheckBoxClass skipscore(BUTTON_SKIPSCORE, nospyplane.X, nospyplane.Y+8); + if (Scen.IsSkipScore) { + skipscore.Turn_On(); + } else { + skipscore.Turn_Off(); + } + + /* + ** Skip the map selection screen for next mission. Presume goes to + ** variation "B"? + */ + CheckBoxClass nomapsel(BUTTON_NOMAPSEL, skipscore.X, skipscore.Y+8); + if (Scen.IsNoMapSel) { + nomapsel.Turn_On(); + } else { + nomapsel.Turn_Off(); + } + + /* + ** Return to main menu after mission completes? + */ + CheckBoxClass onetime(BUTTON_ONETIME, nomapsel.X, nomapsel.Y+8); + if (Scen.IsOneTimeOnly) { + onetime.Turn_On(); + } else { + onetime.Turn_Off(); + } + + /* + ** Trucks carry a wood crate? + */ + CheckBoxClass truckcrate(BUTTON_TRUCKCRATE, onetime.X, onetime.Y+8); + if (Scen.IsTruckCrate) { + truckcrate.Turn_On(); + } else { + truckcrate.Turn_Off(); + } + + /* + ** Transfer credits into tiberium storage at scenario start? + */ + CheckBoxClass moneytib(BUTTON_MONEYTIB, truckcrate.X, truckcrate.Y+8); + if (Scen.IsMoneyTiberium) { + moneytib.Turn_On(); + } else { + moneytib.Turn_Off(); + } + + /* + ** Intro movie name. + */ + char introtext[_MAX_FNAME+_MAX_EXT]; + DropListClass intro(BUTTON_INTRO, introtext, sizeof(introtext), + TPF_EFNT|TPF_NOSHADOW, + theaterbtn.X, theaterbtn.Y+theaterbtn.Height+24, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + intro.Add_Item(""); + for (VQType v = VQ_FIRST; v < VQ_COUNT; v++) { + intro.Add_Item(VQName[v]); + } + intro.Set_Selected_Index((int)Scen.IntroMovie + 1); + + /* + ** Briefing movie name. + */ + char brieftext[_MAX_FNAME+_MAX_EXT]; + DropListClass briefing(BUTTON_BRIEFING, brieftext, sizeof(brieftext), + TPF_EFNT|TPF_NOSHADOW, + intro.X+intro.Width+10, intro.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + briefing.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + briefing.Add_Item(VQName[v]); + } + briefing.Set_Selected_Index((int)Scen.BriefMovie + 1); + + char actiontext[_MAX_FNAME+_MAX_EXT]; + DropListClass action(BUTTON_ACTION, actiontext, sizeof(actiontext), + TPF_EFNT|TPF_NOSHADOW, + briefing.X+briefing.Width+10, briefing.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + action.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + action.Add_Item(VQName[v]); + } + action.Set_Selected_Index((int)Scen.ActionMovie + 1); + + char wintext[_MAX_FNAME+_MAX_EXT]; + DropListClass win(BUTTON_WIN, wintext, sizeof(wintext), + TPF_EFNT|TPF_NOSHADOW, + action.X+action.Width+10, action.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + win.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + win.Add_Item(VQName[v]); + } + win.Set_Selected_Index((int)Scen.WinMovie + 1); + + char losetext[_MAX_FNAME+_MAX_EXT]; + DropListClass lose(BUTTON_LOSE, losetext, sizeof(losetext), + TPF_EFNT|TPF_NOSHADOW, + win.X+win.Width+10, win.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + lose.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + lose.Add_Item(VQName[v]); + } + lose.Set_Selected_Index((int)Scen.LoseMovie + 1); + + /* + ** House choice list. + */ + ListClass housebtn(BUTTON_HOUSE, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+105, 55, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + } + housebtn.Set_Selected_Index(PlayerPtr->Class->House); + + /* + ** Base house choice drop down list. + */ + char basetext[35]; + DropListClass basebtn(BUTTON_BASE, basetext, sizeof(basetext), + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+80, 65, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + basebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + } + if (Base.House != HOUSE_NONE) { + basebtn.Set_Selected_Index(Base.House); + } + + /* + ** Opening scenario theme. + */ + char themetext[65]; + DropListClass themebtn(BUTTON_THEME, themetext, sizeof(themetext), + TPF_EFNT|TPF_NOSHADOW, + basebtn.X+basebtn.Width+15*RESFACTOR, basebtn.Y, 85, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + themebtn.Add_Item(""); + for (ThemeType th = THEME_FIRST; th < THEME_COUNT; th++) { + themebtn.Add_Item(Theme.Full_Name(th)); + } + if (Scen.TransitTheme != THEME_NONE) { + themebtn.Set_Selected_Index(Scen.TransitTheme+1); + } else { + themebtn.Set_Selected_Index(0); + } + + /* + ** Build level (technology). + */ + SliderClass techlevel(BUTTON_TECH, housebtn.X+housebtn.Width+15, housebtn.Y, 100, 8); + techlevel.Set_Maximum(16); + + char statictechbuff[15]; + StaticButtonClass techstatic(0, "999", TPF_EFNT|TPF_NOSHADOW, techlevel.X+techlevel.Width-20, techlevel.Y-7); + + /* + ** Starting credits. + */ + SliderClass creditbtn(BUTTON_CREDITS, housebtn.X+housebtn.Width+15, techlevel.Y+20, 100, 8); + creditbtn.Set_Maximum(201); + + char staticcreditbuff[15]; + StaticButtonClass creditstatic(0, "999999999", TPF_EFNT|TPF_NOSHADOW, creditbtn.X+creditbtn.Width-50, creditbtn.Y-7); + + /* + ** Maximum unit/infantry slider. + */ + SliderClass maxunit(BUTTON_MAXUNIT, housebtn.X+housebtn.Width+15, creditbtn.Y+20, 100, 8); + maxunit.Set_Maximum(501); + + char staticmaxunitbuff[15]; + StaticButtonClass maxunitstatic(0, "999999", TPF_EFNT|TPF_NOSHADOW, maxunit.X+maxunit.Width-30, maxunit.Y-7); + + /* + ** Source of ground delivery reinforcements. + */ + char sourcetext[25] = ""; + ListClass sourcebtn(BUTTON_SOURCE, + housebtn.X+housebtn.Width+15, maxunit.Y+20, 100, 7*4, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SourceType source = SOURCE_FIRST; source <= SOURCE_WEST; source++) { + sourcebtn.Add_Item(SourceName[source]); + } + + /* + ** Smartness lider. + */ + SliderClass smarties(BUTTON_SMARTIES, sourcebtn.X, sourcebtn.Y+sourcebtn.Height+15, 35, 8); + smarties.Set_Maximum(Rule.MaxIQ+1); + + char staticsmartiesbuff[15]; + StaticButtonClass smartiesstatic(0, "9999", TPF_EFNT|TPF_NOSHADOW, smarties.X+smarties.Width-20, smarties.Y-7); + + /* + ** List box of who is allied with whom. + */ + CheckListClass allies(BUTTON_ALLIES, + techlevel.X+techlevel.Width+5, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (hdata[house].Allies & (1L << h)) { + allies.Check_Item(h, true); + } + } + allies.Set_Selected_Index(0); + + /* + ** List box of who the player can control. + */ + CheckListClass control(BUTTON_CONTROL, + allies.X+allies.Width+10, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + control.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (HouseClass::As_Pointer(h)->IsPlayerControl) { + control.Check_Item(h, true); + } + } + control.Set_Selected_Index(0); + + /* + ** Create the ubiquitous "ok" and "cancel" buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Create the list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + theaterbtn.Add_Tail(*commands); + themebtn.Add_Tail(*commands); + housebtn.Add_Tail(*commands); + techlevel.Add_Tail(*commands); + techstatic.Add_Tail(*commands); + sourcebtn.Add_Tail(*commands); + creditbtn.Add_Tail(*commands); + creditstatic.Add_Tail(*commands); + maxunitstatic.Add_Tail(*commands); + moneytib.Add_Tail(*commands); + smartiesstatic.Add_Tail(*commands); + allies.Add_Tail(*commands); + control.Add_Tail(*commands); + maxunit.Add_Tail(*commands); + nospyplane.Add_Tail(*commands); + skipscore.Add_Tail(*commands); + nomapsel.Add_Tail(*commands); + onetime.Add_Tail(*commands); + inherit.Add_Tail(*commands); + timercarry.Add_Tail(*commands); + tanya.Add_Tail(*commands); + record.Add_Tail(*commands); + truckcrate.Add_Tail(*commands); + endofgame.Add_Tail(*commands); + briefing.Add_Tail(*commands); + intro.Add_Tail(*commands); + action.Add_Tail(*commands); + win.Add_Tail(*commands); + lose.Add_Tail(*commands); + basebtn.Add_Tail(*commands); + smarties.Add_Tail(*commands); + desc.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool housechange = true; + bool display = true; + bool process = true; + bool cancel = false; // true = user cancels + bool dotext = true; // display the text. + bool fetch = false; // Fetch data from dialog into tracking structure. + //Set_Logic_Page(SeenBuff); + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + if (housechange) { + HouseStaticClass * hstatic = &hdata[newhouse]; + creditbtn.Set_Value(hstatic->InitialCredits/100); + techlevel.Set_Value(hstatic->TechLevel); + sourcebtn.Set_Selected_Index(hstatic->Edge); + maxunit.Set_Value(hstatic->MaxUnit + hstatic->MaxInfantry); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Check_Item(h, hstatic->Allies & (1L << h)); + } + smarties.Set_Value(hstatic->IQ); + + house = newhouse; + housechange = false; + display = true; + } + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_SCENARIO_OPTIONS, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Display the text that doesn't need drawing except when the entire dialog + ** needs to be redrawn. + */ + Fancy_Text_Print("Tech Level =", techlevel.X, techlevel.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Credits =", creditbtn.X, creditbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Unit Max =", maxunit.X, maxunit.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("IQ =", smarties.X, smarties.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Prebuild Base:", basebtn.X, basebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Theater:", theaterbtn.X, theaterbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Scenario Name:", desc.X, desc.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Country:", housebtn.X, housebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Home Edge:", sourcebtn.X, sourcebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Allies:", allies.X, allies.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Plyr Control:", control.X, control.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Theme:", themebtn.X, themebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Intro:", intro.X, intro.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Briefing:", briefing.X, briefing.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Action:", action.X, action.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Win:", win.X, win.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Lose:", lose.X, lose.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Store scenario?", record.X+10, record.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Inherit stored scenario?", inherit.X+10, inherit.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Auto evac. Tanya (civilian)?", tanya.X+10, tanya.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Last mission of game?", endofgame.X+10, endofgame.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Inherit mission timer from last scenario?", timercarry.X+10, timercarry.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Disable spy plane?", nospyplane.X+10, nospyplane.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Skip the score screen?", skipscore.X+10, skipscore.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("No map selection (force var 'B')?", nomapsel.X+10, nomapsel.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Return to main menu after scenario finishes?", onetime.X+10, onetime.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Truck carries wood crate?", truckcrate.X+10, truckcrate.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Initial money is transferred to silos?", moneytib.X+10, moneytib.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + + theaterbtn.Collapse(); + themebtn.Collapse(); + intro.Collapse(); + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + commands->Draw_All(true); + Show_Mouse(); + display = false; + dotext = true; + } + + /* + ** Display the text of the buttons that could change their text as a + ** result of slider interaction. + */ + if (dotext) { + dotext = false; + Hide_Mouse(); + + sprintf(statictechbuff, "%2d", techlevel.Get_Value()); + techstatic.Set_Text(statictechbuff); + techstatic.Draw_Me(); + + sprintf(staticcreditbuff, "$%-7d", creditbtn.Get_Value() * 100); + creditstatic.Set_Text(staticcreditbuff); + creditstatic.Draw_Me(); + + sprintf(staticmaxunitbuff, "%4d", maxunit.Get_Value()); + maxunitstatic.Set_Text(staticmaxunitbuff); + maxunitstatic.Draw_Me(); + + sprintf(staticsmartiesbuff, "%2d", smarties.Get_Value()); + smartiesstatic.Set_Text(staticsmartiesbuff); + smartiesstatic.Draw_Me(); + + Show_Mouse(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case BUTTON_ALLIES|KN_BUTTON: + allies.Check_Item(house, true); + break; + + case BUTTON_CONTROL|KN_BUTTON: + control.Check_Item(house, true); + break; + + case BUTTON_THEME|KN_BUTTON: + case BUTTON_INTRO|KN_BUTTON: + case BUTTON_BRIEFING|KN_BUTTON: + case BUTTON_ACTION|KN_BUTTON: + case BUTTON_WIN|KN_BUTTON: + case BUTTON_LOSE|KN_BUTTON: + case BUTTON_BASE|KN_BUTTON: + case LIST_THEATER|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + themebtn.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + display = true; + break; + + case BUTTON_SMARTIES|KN_BUTTON: + case BUTTON_MAXUNIT|KN_BUTTON: + case BUTTON_CREDITS|KN_BUTTON: + case BUTTON_TECH|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + themebtn.Collapse(); + theaterbtn.Collapse(); + dotext = true; + break; + + case BUTTON_HOUSE|KN_BUTTON: + newhouse = HousesType(housebtn.Current_Index()); + housechange = true; + briefing.Collapse(); + action.Collapse(); + themebtn.Collapse(); + win.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + fetch = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + fetch = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + if (fetch) { + fetch = false; + HouseStaticClass * hstatic = &hdata[house]; + + Base.House = HousesType(basebtn.Current_Index()); + hstatic->InitialCredits = creditbtn.Get_Value() * 100; + hstatic->Edge = SourceType(sourcebtn.Current_Index()); + hstatic->TechLevel = techlevel.Get_Value(); + hstatic->MaxUnit = maxunit.Get_Value()/2; + hstatic->MaxInfantry = maxunit.Get_Value()/2; + hstatic->IQ = smarties.Get_Value(); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if (allies.Is_Checked(h)) { + hstatic->Allies |= (1L << h); + } else { + hstatic->Allies &= ~(1L << h); + } + } + } + } + + /* + ** Redraw the map + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Copy the dialog data back into the appropriate game data locations. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr != NULL) { + hptr->Control = hdata[h]; + hptr->Allies = hdata[h].Allies; + + if (control.Is_Checked(h)) { + hptr->IsPlayerControl = true; + } else { + hptr->IsPlayerControl = false; + } + } + } + PlayerPtr->IsPlayerControl = true; + strncpy(Scen.Description, desc.Get_Text(), sizeof(Scen.Description)); + Scen.Description[sizeof(Scen.Description)-1] = '\0'; + Scen.IntroMovie = VQType(intro.Current_Index()-1); + Scen.BriefMovie = VQType(briefing.Current_Index()-1); + Scen.ActionMovie = VQType(action.Current_Index()-1); + Scen.WinMovie = VQType(win.Current_Index()-1); + Scen.LoseMovie = VQType(lose.Current_Index()-1); + Scen.IsToInherit = inherit.IsOn; + Scen.IsToCarryOver = record.IsOn; + Scen.IsTanyaEvac = tanya.IsOn; + Scen.IsEndOfGame = endofgame.IsOn; + Scen.IsInheritTimer = timercarry.IsOn; + Scen.IsNoSpyPlane = nospyplane.IsOn; + Scen.IsSkipScore = skipscore.IsOn; + Scen.IsNoMapSel = nomapsel.IsOn; + Scen.IsOneTimeOnly = onetime.IsOn; + Scen.IsTruckCrate = truckcrate.IsOn; + Scen.IsMoneyTiberium = moneytib.IsOn; + Scen.TransitTheme = ThemeType(themebtn.Current_Index()-1); + + /* + ** Change the theater: + ** - 1st set the Theater global + ** - scan all cells to check their TType for compatibility with the new + ** theater; if not compatible, set TType to TEMPLATE_NONE & TIcon to 0 + ** - Then, re-initialize the TypeClasses for the new Theater + */ + TheaterType theater = TheaterType(theaterbtn.Current_Index()); + if (theater != orig_theater) { + + unsigned char theater_mask; // template/terrain mask + TerrainClass * terrain; // cell's terrain pointer + + /* + ** Loop through all cells + */ + for (CELL i = 0; i < MAP_CELL_TOTAL; i++) { + + /* + ** If this cell has a template icon & that template isn't compatible + ** with this theater, set the icon to NONE + */ + if ((*this)[i].TType != TEMPLATE_NONE) { + theater_mask = TemplateTypeClass::As_Reference((*this)[i].TType).Theater; + if ( (theater_mask & (1 << theater))==0) { + (*this)[i].TType = TEMPLATE_NONE; + (*this)[i].TIcon = 0; + } + } + + /* + ** If this cell has terrain in it, and that terrain isn't compatible + ** with this theater, delete the terrain object. + */ + terrain = (*this)[i].Cell_Terrain(); + if (terrain != NULL) { + theater_mask = terrain->Class->Theater; + if ( (theater_mask & (1<Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + + /* + ** 'New' + */ + if (rc==2) { + + /* + ** Create a new trigger + */ + CurTrigger = new TriggerTypeClass(); + if (CurTrigger) { + + /* + ** delete it if user cancels + */ + if (!CurTrigger->Edit()) { + delete CurTrigger; + CurTrigger = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more triggers available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTrigger) { + Detach_This_From_All(CurTrigger->As_Target(), true); + delete CurTrigger; + //CurTrigger->Remove(); + CurTrigger = NULL; + Changed = 1; + } + } + } + + /* + ** Let the CurTrigger global exist if the trigger can be placed on the + ** ground or on a game object. + */ + if (CurTrigger && !(CurTrigger->Attaches_To() & (ATTACH_OBJECT|ATTACH_CELL))) { + CurTrigger = NULL; + } +} + + +/*************************************************************************** + * MapEditClass::Select_Trigger -- lets user select a trigger * + * * + * CurTrigger can be NULL when this function is called. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name Event Action House Team ³³ ³ * + * ³ ³ Name Event Action House Team ÃÄ´ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sort trigger list. * + *=========================================================================*/ +int MapEditClass::Select_Trigger(void) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_TXT8_H = 11, + D_MARGIN = 35, + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-10, + D_LIST_H = D_DIALOG_H-70, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 25, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + }; + + /* + ** Button enumerations: + */ + enum { + TRIGGER_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables: + */ + bool edit_trig = false; // true = user wants to edit + bool new_trig = false; // true = user wants to new + bool del_trig = false; // true = user wants to new + int i; // loop counter + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TListClass > triggerlist(TRIGGER_LIST, D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + TextButtonClass editbtn(BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + TextButtonClass newbtn(BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + TextButtonClass deletebtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in the list box + */ + for (i = 0; i < TriggerTypes.Count(); i++) { + triggerlist.Add_Item(TriggerTypes.Ptr(i)); + } + + PNBubble_Sort(&triggerlist[0], triggerlist.Count()); + + if (CurTrigger) { + triggerlist.Set_Selected_Index(CurTrigger); + } else { + triggerlist.Set_Selected_Index(0); + } + + /* + ** Set CurTrigger if it isn't + */ + if (TriggerTypes.Count()==0) { + CurTrigger = NULL; + } else { + CurTrigger = triggerlist.Current_Item(); +// if (!CurTrigger) { +// CurTrigger = &*triggerlist.Current_Item(); +// } + } + + /* + ** Create the list + */ + commands = &triggerlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if requested. + */ + if (display /*&& LogicPage->Lock()*/) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + commands->Flag_List_To_Redraw(); + commands->Draw_All(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + CurTrigger = &*triggerlist.Current_Item(); +// CurTrigger = (TriggerTypeClass *)&*triggerlist.Current_Item(); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTrigger) { // only allow if there's one selected + process = false; + edit_trig = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_trig = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_trig = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (edit_trig) return(1); + if (new_trig) return(2); + if (del_trig) return(3); + return(0); +} + + +#endif diff --git a/REDALERT/MAPEDIT.CPP b/REDALERT/MAPEDIT.CPP new file mode 100644 index 000000000..c064e7a55 --- /dev/null +++ b/REDALERT/MAPEDIT.CPP @@ -0,0 +1,2170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDIT.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor overloaded routines & utility routines * + *-------------------------------------------------------------------------* + * Map Editor modules: * + * (Yes, they're all one huge class.) * + * mapedit.cpp: overloaded routines, utility routines * + * mapeddlg.cpp: map editor dialogs, most of the main menu options * + * mapedplc.cpp: object-placing routines * + * mapedsel.cpp: object-selection & manipulation routines * + * mapedtm.cpp: team-editing routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::AI -- The map editor's main logic * + * MapEditClass::Read_INI -- overloaded Read_INI function * + * MapEditClass::AI_Menu -- menu of AI options * + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * MapEditClass::Clear_List -- clears the internal chooseable object list* + * MapEditClass::Cycle_House -- finds next valid house for object type * + * MapEditClass::Draw_It -- overloaded Redraw routine * + * MapEditClass::Fatal -- exits with error message * + * MapEditClass::Main_Menu -- main menu processor for map editor * + * MapEditClass::MapEditClass -- class constructor * + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * MapEditClass::One_Time -- one-time initialization * + * MapEditClass::Verify_House -- sees if given house can own given obj * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + +/* +** Array of all missions supported by the map editor +*/ +MissionType MapEditClass::MapEditMissions[] = { + MISSION_GUARD, + MISSION_STICKY, + MISSION_HARMLESS, + MISSION_HARVEST, + MISSION_GUARD_AREA, + MISSION_RETURN, + MISSION_AMBUSH, + MISSION_HUNT, + MISSION_SLEEP, +}; +#define NUM_EDIT_MISSIONS (sizeof(MapEditClass::MapEditMissions) / sizeof(MapEditClass::MapEditMissions[0])) + + +/* +** For menu processing +*/ +extern int UnknownKey; // in menus.cpp + +char MapEditClass::HealthBuf[20]; + + +/*************************************************************************** + * MapEditClass::MapEditClass -- class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +MapEditClass::MapEditClass(void) +{ + /* + ** Init data members. + */ +// ScenVar = SCEN_VAR_A; + ObjCount = 0; + LastChoice = 0; + LastHouse = HOUSE_GOOD; + GrabbedObject = 0; + for (int i=0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + TypeOffset[i] = 0; + } + Scen.Waypoint[WAYPT_HOME] = 0; + CurrentCell = 0; + CurTeam = NULL; + CurTrigger = NULL; + Changed = 0; + LMouseDown = 0; + BaseBuilding = false; +// BasePercent = 100; +} + + +/*************************************************************************** + * MapEditClass::One_Time -- one-time initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/02/1995 BR : Created. * + *=========================================================================*/ +void MapEditClass::One_Time(void) +{ + MouseClass::One_Time(); + + /* + ** The map: a single large "button" + */ +#ifdef WIN32 + MapArea = new ControlClass(MAP_AREA, 0, 8, 640-8, 400-8, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#else + MapArea = new ControlClass(MAP_AREA, 0, 8, 312, 192, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#endif + + /* + ** House buttons + */ + HouseList = new ListClass(POPUP_HOUSELIST, POPUP_HOUSE_X, POPUP_HOUSE_Y, POPUP_HOUSE_W, POPUP_HOUSE_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseList->Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + + /* + ** The mission list box + */ + MissionList = new ListClass(POPUP_MISSIONLIST, + POPUP_MISSION_X, POPUP_MISSION_Y, POPUP_MISSION_W, POPUP_MISSION_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int i = 0; i < NUM_EDIT_MISSIONS; i++) { + MissionList->Add_Item(MissionClass::Mission_Name(MapEditMissions[i])); + } + + /* + ** The health bar + */ + HealthGauge = new TriColorGaugeClass(POPUP_HEALTHGAUGE, + POPUP_HEALTH_X, POPUP_HEALTH_Y, POPUP_HEALTH_W, POPUP_HEALTH_H); + HealthGauge->Use_Thumb(true); + HealthGauge->Set_Maximum(0x100); + HealthGauge->Set_Red_Limit(0x3f - 1); + HealthGauge->Set_Yellow_Limit(0x7f - 1); + + /* + ** The health text label + */ + HealthBuf[0] = 0; + HealthText = new TextLabelClass(HealthBuf, + POPUP_HEALTH_X + POPUP_HEALTH_W / 2, + POPUP_HEALTH_Y + POPUP_HEALTH_H + 1, + GadgetClass::Get_Color_Scheme(), + TPF_CENTER | TPF_FULLSHADOW | TPF_EFNT); + + /* + ** Building attribute buttons. + */ + Sellable = new TextButtonClass(POPUP_SELLABLE, TXT_SELLABLE, TPF_EBUTTON, 320-65, 200-25, 60); + Rebuildable = new TextButtonClass(POPUP_REBUILDABLE, TXT_REBUILD, TPF_EBUTTON, 320-65, 200-15, 60); + + /* + ** The facing dial + */ + FacingDial = new Dial8Class(POPUP_FACINGDIAL, POPUP_FACEBOX_X, + POPUP_FACEBOX_Y, POPUP_FACEBOX_W, POPUP_FACEBOX_H, (DirType)0); + + /* + ** The base percent-built slider & its label + */ + BaseGauge = new GaugeClass(POPUP_BASEPERCENT, POPUP_BASE_X, POPUP_BASE_Y, POPUP_BASE_W, POPUP_BASE_H); + BaseLabel = new TextLabelClass ("Base:", POPUP_BASE_X - 3, POPUP_BASE_Y, GadgetClass::Get_Color_Scheme(), + TPF_RIGHT | TPF_NOSHADOW | TPF_EFNT); + BaseGauge->Set_Maximum(100); + BaseGauge->Set_Value(Scen.Percent); +} + + +/*********************************************************************************************** + * MapeditClass::Init_IO -- Reinitializes the radar map at scenario start. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Init_IO(void) +{ + /* + ** For normal game mode, jump to the parent's Init routine. + */ + if (!Debug_Map) { + + MouseClass::Init_IO(); + + } else { + + /* + ** For editor mode, add the map area to the button input list + */ + Buttons = 0; + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + } +} + + +/*************************************************************************** + * MapEditClass::Clear_List -- clears the internal chooseable object list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Clear_List(void) +{ + /* + ** Set # object type ptrs to 0, set NumType for each type to 0 + */ + ObjCount = 0; + for (int i = 0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + } +} + + +/*************************************************************************** + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * * + * Use this routine to add an object to the game object selection list. * + * This list is used by the Add_Object function. All items located in the * + * list will appear and be chooseable by that function. Make sure to * + * clear the list before adding a sequence of items to it. Clearing * + * the list is accomplished by the Clear_List() function. * + * * + * INPUT: * + * object ptr to ObjectTypeClass to add * + * * + * OUTPUT: * + * bool: was the object added to the list? A failure could occur if * + * NULL were passed in or the list is full. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/04/1994 JLB : Created. * + *=========================================================================*/ +bool MapEditClass::Add_To_List(ObjectTypeClass const * object) +{ + /* + ** Add the object if there's room. + */ + if (object && ObjCount < MAX_EDIT_OBJECTS) { + Objects[ObjCount++] = object; + + /* + ** Update type counters. + */ + switch (object->What_Am_I()) { + case RTTI_TEMPLATETYPE: + NumType[0]++; + break; + + case RTTI_OVERLAYTYPE: + NumType[1]++; + break; + + case RTTI_SMUDGETYPE: + NumType[2]++; + break; + + case RTTI_TERRAINTYPE: + NumType[3]++; + break; + + case RTTI_UNITTYPE: + NumType[4]++; + break; + + case RTTI_INFANTRYTYPE: + NumType[5]++; + break; + + case RTTI_VESSELTYPE: + NumType[6]++; + break; + + case RTTI_BUILDINGTYPE: + NumType[7]++; + break; + + case RTTI_AIRCRAFTTYPE: + NumType[8]++; + break; + } + return(true); + } + + return(false); +} + + +/*************************************************************************** + * MapEditClass::AI -- The map editor's main logic * + * * + * This routine overloads the parent's (DisplayClass) AI function. * + * It checks for any input specific to map editing, and calls the parent * + * AI routine to handle scrolling and other mainstream map stuff. * + * * + * If this detects one of its special input keys, it sets 'input' to 0 * + * before calling the parent AI routine; this prevents input conflict. * + * * + * SUPPORTED INPUT: * + * General: * + * F2/RMOUSE: main menu * + * F6: toggles show-passable mode * + * HOME: go to the Home Cell (scenario's start position)* + * SHIFT-HOME: set the Home Cell to the current TacticalCell* + * ESC: exits to DOS * + * Object Placement: * + * INSERT: go into placement mode * + * ESC: exit placement mode * + * LEFT/RIGHT: prev/next placement object * + * PGUP/PGDN: prev/next placement category * + * HOME: 1st placement object (clear template) * + * h/H: toggle house of placement object * + * LMOUSE: place the placement object * + * MOUSE MOTION: "paint" with the placement object * + * Object selection: * + * LMOUSE: select & "grab" current object * + * If no object is present where the mouse is * + * clicked, the current object is de-selected * + * If the same object is clicked on, it stays * + * selected. Also displays the object-editing * + * gadgets. * + * LMOUSE RLSE: release currently-grabbed object * + * MOUSE MOTION: if an object is grabbed, moves the object * + * SHIFT|ALT|ARROW: moves object in that direction * + * DELETE deletes currently-selected object * + * Object-editing controls: * + * POPUP_GDI: makes GDI the owner of this object * + * POPUP_NOD: makes NOD the owner of this object * + * POPUP_MISSIONLIST: sets that mission for this object * + * POPUP_HEALTHGAUGE: sets that health value for this object * + * POPUP_FACINGDIAL: sets the object's facing * + * * + * Changed is set when you: * + * - place an object * + * - move a grabbed object * + * - delete an object * + * - size the map * + * - create a new scenario * + * Changed is cleared when you: * + * - Save the scenario * + * - Load a scenario * + * - Play the scenario * + * * + * INPUT: * + * input KN_ value, 0 if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI(KeyNumType & input, int x, int y) +{ + int rc; + MissionType mission; + int strength; + CELL cell; + int i; + int found; // for removing a waypoint label + int waypt_idx; // for labelling a waypoint + BaseNodeClass * node; // for removing from an AI Base + HousesType house; + char wayname[4]; + + /* + ** Trap 'F2' regardless of whether we're in game or editor mode + */ + if (Debug_Flag) { + if ((input == KN_F2 && Session.Type == GAME_NORMAL) || input == (KN_F2 | KN_CTRL_BIT)) { + ScenarioInit = 0; + + /* + ** If we're in editor mode & Changed is set, prompt for saving changes + */ + if (Debug_Map && Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort game + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + } else { + Changed = 0; + Go_Editor(!Debug_Map); + } + } else { + + /* + ** User doesn't want to save + */ + Go_Editor(!Debug_Map); + } + } else { + /* + ** If we're in game mode, set Changed to 0 (so if we didn't save our + ** changes above, they won't keep coming back to haunt us with continual + ** Save Changes? prompts!) + */ + if (!Debug_Map) { + Changed = 0; + } + BaseGauge->Set_Value(Scen.Percent); + Go_Editor(!Debug_Map); + } + } + } + + /* + ** For normal game mode, jump to the parent's AI routine. + */ + if (!Debug_Map) { + MouseClass::AI(input, x, y); + return; + } + + ::Frame++; + + /* + ** Do special mouse processing if the mouse is over the map + */ + if (Get_Mouse_X() > TacPixelX && Get_Mouse_X() < + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() > TacPixelY && Get_Mouse_Y() < + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + /* + ** When the mouse moves over a scrolling edge, ScrollClass changes its + ** shape to the appropriate arrow or NO symbol; it's our job to change it + ** back to normal (or whatever the shape is set to by Set_Default_Mouse()) + ** when it re-enters the map area. + */ + if (CurTrigger) { + Override_Mouse_Shape(MOUSE_CAN_MOVE); + } else { + Override_Mouse_Shape(MOUSE_NORMAL); + } + } + + /* + ** Set 'ZoneCell' to track the mouse cursor around over the map. Do this + ** even if the map is scrolling. + */ + if (Get_Mouse_X() >= TacPixelX && Get_Mouse_X() <= + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() >= TacPixelY && Get_Mouse_Y() <= + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + cell = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + if (cell != -1) { + Set_Cursor_Pos(cell); + if (PendingObject) { + Flag_To_Redraw(true); + } + } + } + + /* + ** Check for mouse motion while left button is down. + */ + rc = Mouse_Moved(); + if (LMouseDown && rc) { + + /* + ** "Paint" mode: place current object, and restart placement + */ + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Move the currently-grabbed object + */ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } + } + } + } + + /* + ** Trap special editing keys; if one is detected, set 'input' to 0 to + ** prevent a conflict with parent's AI(). + */ + switch (input) { + /* + ** F2/RMOUSE = pop up main menu + */ + case KN_RMOUSE: + + /* + ** Turn off placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + } + + /* + ** Turn off trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + } + + /* + ** Unselect object & hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + Main_Menu(); + input = KN_NONE; + break; + + /* + ** F6 = toggle passable/impassable display + */ + case KN_F6: + Debug_Passable = (Debug_Passable == false); + HidPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /* + ** INSERT = go into object-placement mode + */ + case KN_INSERT: + if (!PendingObject) { + /* + ** Unselect current object, hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + + /* + ** Go into placement mode + */ + Start_Placement(); + } + input = KN_NONE; + break; + + /* + ** ESC = exit placement mode, or exit to DOS + */ + case KN_ESC: + + /* + ** Exit object placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + input = KN_NONE; + break; + } else { + + /* + ** Exit trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + input = KN_NONE; + break; + } else { + rc = WWMessageBox().Process("Exit Scenario Editor?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User doesn't want to exit; return to editor + */ + if (rc==1) { + input = KN_NONE; + break; + } + + /* + ** If changed, prompt for saving + */ + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort exit + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + break; + } else { + Changed = 0; + } + } + } + } + } + //Prog_End(); + Emergency_Exit(0); + break; + + /* + ** LEFT = go to previous placement object + */ + case KN_LEFT: + if (PendingObject) { + Place_Prev(); + } + input = KN_NONE; + break; + + /* + ** RIGHT = go to next placement object + */ + case KN_RIGHT: + if (PendingObject) { + Place_Next(); + } + input = KN_NONE; + break; + + /* + ** PGUP = go to previous placement category + */ + case KN_PGUP: + if (PendingObject) { + Place_Prev_Category(); + } + input = KN_NONE; + break; + + /* + ** PGDN = go to next placement category + */ + case KN_PGDN: + if (PendingObject) { + Place_Next_Category(); + } + input = KN_NONE; + break; + + /* + ** HOME = jump to first placement object, or go to Home Cell + */ + case KN_HOME: + if (PendingObject) { + Place_Home(); + } else { + + /* + ** Set map position + */ + ScenarioInit++; + Set_Tactical_Position(Scen.Waypoint[WAYPT_HOME]); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + input = KN_NONE; + break; + + /* + ** SHIFT-HOME: set new Home Cell position + */ + case ((int)KN_HOME | (int)KN_SHIFT_BIT): + if (CurrentCell != 0) { + + /* + ** Unflag the old Home Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_HOME]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_HOME && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + + /* + ** Now set the new Home cell + */ +// Scen.Waypoint[WAYPT_HOME] = Coord_Cell(TacticalCoord); +// (*this)[TacticalCoord].IsWaypoint = 1; +// Flag_Cell(Coord_Cell(TacticalCoord)); + Scen.Waypoint[WAYPT_HOME] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + + Changed = 1; + input = KN_NONE; +} + break; + + /* + ** SHIFT-R: set new Reinforcement Cell position. Don't allow setting + ** the Reinf. Cell to the same as the Home Cell (for display purposes.) + */ + case ((int)KN_R | (int)KN_SHIFT_BIT): + if (CurrentCell==0 || CurrentCell==Scen.Waypoint[WAYPT_HOME]) { + break; + } + + /* + ** Unflag the old Reinforcement Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_REINF]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_REINF && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + /* + ** Now set the new Reinforcement cell + */ + Scen.Waypoint[WAYPT_REINF] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + Changed = 1; + input = KN_NONE; + break; + + /* + ** ALT-Letter: Label a waypoint cell + */ + case ((int)KN_A | (int)KN_ALT_BIT): + case ((int)KN_B | (int)KN_ALT_BIT): + case ((int)KN_C | (int)KN_ALT_BIT): + case ((int)KN_D | (int)KN_ALT_BIT): + case ((int)KN_E | (int)KN_ALT_BIT): + case ((int)KN_F | (int)KN_ALT_BIT): + case ((int)KN_G | (int)KN_ALT_BIT): + case ((int)KN_H | (int)KN_ALT_BIT): + case ((int)KN_I | (int)KN_ALT_BIT): + case ((int)KN_J | (int)KN_ALT_BIT): + case ((int)KN_K | (int)KN_ALT_BIT): + case ((int)KN_L | (int)KN_ALT_BIT): + case ((int)KN_M | (int)KN_ALT_BIT): + case ((int)KN_N | (int)KN_ALT_BIT): + case ((int)KN_O | (int)KN_ALT_BIT): + case ((int)KN_P | (int)KN_ALT_BIT): + case ((int)KN_Q | (int)KN_ALT_BIT): + case ((int)KN_R | (int)KN_ALT_BIT): + case ((int)KN_S | (int)KN_ALT_BIT): + case ((int)KN_T | (int)KN_ALT_BIT): + case ((int)KN_U | (int)KN_ALT_BIT): + case ((int)KN_V | (int)KN_ALT_BIT): + case ((int)KN_W | (int)KN_ALT_BIT): + case ((int)KN_X | (int)KN_ALT_BIT): + case ((int)KN_Y | (int)KN_ALT_BIT): + case ((int)KN_Z | (int)KN_ALT_BIT): + if (CurrentCell != 0) { +#ifdef WIN32 + waypt_idx = (input & ~KN_ALT_BIT) - KN_A; +#else + waypt_idx = KN_To_KA(input & 0x00ff) - KA_a; +#endif + Update_Waypoint(waypt_idx); + } + input = KN_NONE; + break; + + /* + ** ALT-. : Designate an extended (2-letter) waypoint name + */ + case KN_PERIOD: + case ((int)KN_PERIOD | (int)KN_ALT_BIT): + if (CurrentCell != 0 && Get_Waypoint_Name(wayname)) { + int waynm = 0; + if (strlen(wayname)) { + wayname[0] = toupper(wayname[0]); + wayname[1] = toupper(wayname[1]); + if (wayname[0] >= 'A' && wayname[0] <= 'Z') { + waynm = wayname[0] - 'A'; + if (wayname[1] >= 'A' && wayname[1] <= 'Z') { + waynm = (waynm+1)*26 + (wayname[1] - 'A'); + } + if (waynm < WAYPT_HOME) Update_Waypoint(waynm); + } + } + } + input = KN_NONE; + break; + +#ifdef OBSOLETE + /* + ** ALT-1-4: Designate a cell as a capture-the-flag cell. + */ + case ((int)KN_1 | (int)KN_ALT_BIT): + case ((int)KN_2 | (int)KN_ALT_BIT): + case ((int)KN_3 | (int)KN_ALT_BIT): + case ((int)KN_4 | (int)KN_ALT_BIT): + + /* + ** If there's a current cell, place the flag & waypoint there. + */ + if (CurrentCell != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); +// waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house)) { + HouseClass::As_Pointer(house)->Flag_Attach(CurrentCell, true); + } + } else { + + /* + ** If there's a current object, attach the flag to it and clear the + ** waypoint. + */ + if (CurrentObject[0] != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house) && CurrentObject[0]->What_Am_I() == RTTI_UNIT) { + HouseClass::As_Pointer(house)->Flag_Attach((UnitClass *)CurrentObject[0], true); + } + } + } + input = KN_NONE; + break; +#endif + + /* + ** ALT-Space: Remove a waypoint designation + */ + case ((int)KN_SPACE | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + + /* + ** Loop through letter waypoints; if this cell is one of them, + ** clear that waypoint. + */ + for (i = 0 ; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i]==CurrentCell) + Scen.Waypoint[i] = -1; + } + + /* + ** Loop through flag home values; if this cell is one of them, clear + ** that waypoint. + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(HOUSE_MULTI1 + i); + if (HouseClass::As_Pointer(house) && + CurrentCell == HouseClass::As_Pointer(house)->FlagHome) + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(CurrentCell), true); + } + + /* + ** If there are no more waypoints on this cell, clear the cell's + ** waypoint designation. + */ + if (Scen.Waypoint[WAYPT_HOME]!=CurrentCell && + Scen.Waypoint[WAYPT_REINF]!=CurrentCell) + (*this)[CurrentCell].IsWaypoint = 0; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /* + ** 'H' = toggle current placement object's house + */ + case KN_H: + case ((int)KN_H | (int)KN_SHIFT_BIT): + if (PendingObject) { + Toggle_House(); + } + input = KN_NONE; + break; + + /* + ** Left-mouse click: + ** Button DOWN: + ** - Toggle LMouseDown + ** - If we're in placement mode, try to place the current object + ** - If success, re-enter placement mode + ** - Otherwise, try to select an object, and "grab" it if there is one + ** - If no object, then select that cell as the "current" cell + ** Button UP: + ** - Toggle LMouseDown + ** - release any grabbed object + */ + case ((int)MAP_AREA | (int)KN_BUTTON): + + /* + ** Left Button DOWN + */ + if (Keyboard->Down(KN_LMOUSE)) { + LMouseDown = 1; + + /* + ** Placement mode: place an object + */ + if (PendingObject) { + if (Place_Object()==0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Place a trigger + */ + if (CurTrigger) { + Place_Trigger(); + Changed = 1; + } else { + + /* + ** Select an object or a cell + ** Check for double-click + */ + if (CurrentObject.Count() && + ((TickCount - LastClickTime) < 15)) { + ; // stub + + } else { + /* + ** Single-click: select object + */ + if (Select_Object()==0) { + CurrentCell = 0; + Grab_Object(); + } else { + + /* + ** No object: select the cell + */ + CurrentCell = Click_Cell_Calc(Keyboard->MouseQX, Keyboard->MouseQY); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + } + } + LastClickTime = TickCount(); + input = KN_NONE; + } else { + + /* + ** Left Button UP + */ + LMouseDown = 0; + GrabbedObject = 0; + input = KN_NONE; + } + break; + + /* + ** SHIFT-ALT-Arrow: move the current object + */ + case (int)KN_UP | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_DOWN | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_LEFT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_RIGHT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + CurrentObject[0]->Move(KN_To_Facing(input)); + Changed = 1; + } + input = KN_NONE; + break; + + /* + ** DELETE: delete currently-selected object + */ + case KN_DELETE: + + /* + ** Delete currently-selected object's trigger, or the object + */ + if (CurrentObject.Count()) { + + /* + ** Delete trigger + */ + if (CurrentObject[0]->Trigger.Is_Valid()) { + CurrentObject[0]->Trigger = NULL; + } else { + /* + ** If the current object is part of the AI's Base, remove it + ** from the Base's Node list. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && + Base.Is_Node((BuildingClass *)CurrentObject[0])) { + node = Base.Get_Node((BuildingClass *)CurrentObject[0]); + Base.Nodes.Delete(*node); + } + + /* + ** Delete current object + */ + delete CurrentObject[0]; + + /* + ** Hide the popup controls + */ + Popup_Controls(); + } + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } else { + + /* + ** Remove trigger from current cell + */ + if (CurrentCell) { + if ((*this)[CurrentCell].Trigger.Is_Valid()) { + (*this)[CurrentCell].Trigger = NULL; +// CellTriggers[CurrentCell] = NULL; + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + } + input = KN_NONE; + break; + + /* + ** TAB: select next object on the map + */ + case KN_TAB: + Select_Next(); + input = KN_NONE; + break; + + /* + ** Object-Editing button: House Button + */ + case POPUP_HOUSELIST|KN_BUTTON: + /* + ** Determine the house desired by examining the currently + ** selected index in the house list gadget. + */ + house = HousesType(((ListClass *)Buttons->Extract_Gadget(POPUP_HOUSELIST))->Current_Index()); + + /* + ** If that house doesn't own this object, try to transfer it + */ + if (CurrentObject[0]->Owner() != house) { + if (Change_House(house)) { + Changed = 1; + } + } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); + HidPage.Clear(); + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + +// case (POPUP_GDI | KN_BUTTON): +// case (POPUP_NOD | KN_BUTTON): +// case (POPUP_NEUTRAL | KN_BUTTON): +// case (POPUP_MULTI1 | KN_BUTTON): +// case (POPUP_MULTI2 | KN_BUTTON): +// case (POPUP_MULTI3 | KN_BUTTON): +// case (POPUP_MULTI4 | KN_BUTTON): +// +// /* +// ** Convert input value into a house value; assume HOUSE_GOOD is 0 +// */ +// house = (HousesType)( (input & (~KN_BUTTON)) - POPUP_FIRST); +// +// /* +// ** If that house doesn't own this object, try to transfer it +// */ +// if (CurrentObject[0]->Owner()!=house) { +// if (Change_House(house)) { +// Changed = 1; +// } +// } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// input = KN_NONE; +// break; + + case POPUP_SELLABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsAllowedToSell = (building->IsAllowedToSell == false); + building->Mark(MARK_CHANGE); + } + if (building->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + } + break; + + case POPUP_REBUILDABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsToRebuild = (building->IsToRebuild == false); + building->Mark(MARK_CHANGE); + } + if (building->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + } + break; + + /* + ** Object-Editing button: Mission + */ + case (POPUP_MISSIONLIST | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ** Set new mission + */ + mission = MapEditMissions[MissionList->Current_Index()]; + if (CurrentObject[0]->Get_Mission() != mission) { + ((TechnoClass *)CurrentObject[0])->Set_Mission(mission); + Changed = 1; + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Health + */ + case (POPUP_HEALTHGAUGE | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Derive strength from current gauge reading + */ + strength = CurrentObject[0]->Class_Of().MaxStrength * fixed(HealthGauge->Get_Value(), 256); +// strength = Fixed_To_Cardinal((unsigned)CurrentObject[0]->Class_Of().MaxStrength, (unsigned)HealthGauge->Get_Value()); + + /* + ** Clip to 1 + */ + if (strength <= 0) { + strength = 1; + } + + /* + ** Set new strength + */ + if (strength != CurrentObject[0]->Strength) { + CurrentObject[0]->Strength = strength; + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + + /* + ** Update text label + */ + sprintf(HealthBuf, "%d", strength); + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_FACINGDIAL | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Set new facing + */ + if (FacingDial->Get_Direction() != + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Get()) { + + /* + ** Set body's facing + */ + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Set(FacingDial->Get_Direction()); + + /* + ** Set turret facing, if there is one + */ + if (CurrentObject[0]->What_Am_I()==RTTI_UNIT) { + ((UnitClass *)CurrentObject[0])->SecondaryFacing.Set(FacingDial->Get_Direction()); + } + + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_BASEPERCENT | KN_BUTTON): + if (BaseGauge->Get_Value() != Scen.Percent) { + Scen.Percent = BaseGauge->Get_Value(); + Build_Base_To(Scen.Percent); + HidPage.Clear(); + Flag_To_Redraw(true); + } + input = KN_NONE; + break; + + default: + break; + } + + /* + ** Call parent's AI routine + */ + MouseClass::AI(input, x, y); +} + + +/*************************************************************************** + * MapEditClass::Draw_It -- overloaded Redraw routine * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Draw_It(bool forced) +{ + char const * label; + char buf[40]; + char const * tptr; + + MouseClass::Draw_It(forced); + + if (!Debug_Map) { + return; + } + + /* + ** Display the total value of all Tiberium on the map. + */ + Fancy_Text_Print("Tiberium=%ld ", 0, 0, GadgetClass::Get_Color_Scheme(), + BLACK, TPF_EFNT | TPF_NOSHADOW, TotalValue); + + /* + ** If there are no object controls displayed, just invoke parent's Redraw + ** and return. + */ + if (!Buttons) { + return; + } + + /* + ** Otherwise, if 'display' is set, invoke the parent's Redraw to refresh + ** the HIDPAGE; then, update the buttons & text labels onto HIDPAGE; + ** then invoke the parent's Redraw to blit the HIDPAGE to SEENPAGE. + */ + if (forced) { + + /* + ** Update the text labels + */ + if (CurrentObject.Count()) { + /* + ** Display the object's name & ID + */ + label = Text_String(CurrentObject[0]->Full_Name()); + tptr = label; + sprintf(buf, "%s (%d)", tptr, CurrentObject[0]->As_Target()); + + /* + ** print the label + */ + Fancy_Text_Print (buf, 160, 0, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_EFNT); + } + } +} + + +/*************************************************************************** + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * * + * Reports whether the mouse has moved or not. This varies based on the * + * type of object currently selected. If there's an infantry object * + * selected, mouse motion counts even within a cell; for all other types,* + * mouse motion counts only if the mouse changes cells. * + * * + * The reason this routine is needed is to prevent Paint-Mode from putting* + * gobs of trees and such into the same cell if the mouse moves just * + * a little bit. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Mouse_Moved(void) +{ + static int old_mx = 0; + static int old_my = 0; + static CELL old_zonecell = 0; + const ObjectTypeClass * objtype = NULL; + bool retcode = false; + + /* + ** Return if no motion + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { + return(false); + } + + /* + ** Get a ptr to ObjectTypeClass + */ + if (PendingObject) { + objtype = PendingObject; + } else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } + + /* + ** Infantry: mouse moved if any motion at all + */ + if (objtype->What_Am_I() == RTTI_INFANTRYTYPE) { + retcode = true; + } else { + /* + ** Others: mouse moved only if cell changed + */ + if (old_zonecell!=ZoneCell) { + retcode = true; + } else { + retcode = false; + } + } + + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(retcode); +} + + +/*************************************************************************** + * MapEditClass::Main_Menu -- main menu processor for map editor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Main_Menu(void) +{ + char const * _menus[MAX_MAIN_MENU_NUM + 1]; + int selection; // option the user picks + bool process; // menu stays up while true + int rc; + + /* + ** Fill in menu items + */ + _menus[0] = "New Scenario"; + _menus[1] = "Load Scenario"; + _menus[2] = "Save Scenario"; + _menus[3] = "Size Map"; + _menus[4] = "Add Game Object"; + _menus[5] = "Scenario Options"; + _menus[6] = "AI Options"; + _menus[7] = "Play Scenario"; + _menus[8] = NULL; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + ** Process selection + */ + switch (selection) { + + /* + ** New scenario + */ + case 0: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (New_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 1; + } + process = false; + break; + + /* + ** Load scenario + */ + case 1: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (Load_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 0; + } + process = false; + break; + + /* + ** Save scenario + */ + case 2: + if (Save_Scenario() == 0) { + Changed = 0; + } + process = false; + break; + + /* + ** Edit map size + */ + case 3: + if (Size_Map(MapCellX, MapCellY, MapCellWidth, MapCellHeight)==0) { + process = false; + Changed = 1; + } + break; + + /* + ** Add an object + */ + case 4: + if (Placement_Dialog() == 0) { + Start_Placement(); + process = false; + } + break; + + /* + ** Scenario options + */ + case 5: + if (Scenario_Dialog() == 0) { + Changed = 1; + process = false; + } + break; + + /* + ** Other options + */ + case 6: + AI_Menu(); + process = false; + break; + + /* + ** Test-drive this scenario + */ + case 7: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO, TXT_CANCEL); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc == 2) return; + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + Changed = 0; + Debug_Map = false; + Start_Scenario(Scen.ScenarioName); + return; + } + } + + /* + ** Restore the display: + ** - Clear HIDPAGE to erase any spurious drawing done by the menu system + ** - Invoke Flag_To_Redraw to tell DisplayClass to re-render the whole screen + ** - Invoke Redraw() to update the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::AI_Menu -- menu of AI options * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + char const * _menus[MAX_AI_MENU_NUM + 1]; + + /* + ** Fill in menu strings + */ + _menus[0] = "Pre-Build a Base"; + _menus[1] = "Edit Triggers"; + _menus[2] = "Edit Teams"; + _menus[3] = NULL; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + ** Process selection + */ + switch (selection) { + /* + ** Pre-Build a Base + */ + case 0: + Start_Base_Building(); + process = false; + break; + + /* + ** Trigger Editing + */ + case 1: + Handle_Triggers(); + /* + ** Go into trigger placement mode + */ + if (CurTrigger) { + Start_Trigger_Placement(); + } + process = false; + break; + + /* + ** Team Editing + */ + case 2: + Handle_Teams("Teams"); + process = false; + break; + } + } +} + + +/*************************************************************************** + * MapEditClass::Verify_House -- is this objtype ownable by this house? * + * * + * INPUT: * + * house house to check * + * objtype ObjectTypeClass to check * + * * + * OUTPUT: * + * 0 = isn't ownable, 1 = it is * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Verify_House(HousesType house, ObjectTypeClass const * objtype) +{ + /* + ** Verify that new house can own this object + */ + return((objtype->Get_Ownable() & (1 << house)) != 0); +} + + +/*************************************************************************** + * MapEditClass::Cycle_House -- finds next valid house for object type * + * * + * INPUT: * + * objtype ObjectTypeClass ptr to get house for * + * curhouse current house value to start with * + * * + * OUTPUT: * + * HousesType that's valid for this object type * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +HousesType MapEditClass::Cycle_House(HousesType curhouse, ObjectTypeClass const *) +{ + HousesType count; // prevents an infinite loop + + /* + ** Loop through all house types, starting with the one after 'curhouse'; + ** return the first one that's valid + */ + count = HOUSE_NONE; + while (1) { + + /* + ** Go to next house + */ + curhouse++; + if (curhouse == HOUSE_COUNT) { + curhouse = HOUSE_FIRST; + } + + /* + ** Count # iterations; don't go forever + */ + count++; + if (count == HOUSE_COUNT) { + curhouse = HOUSE_NONE; + break; + } + + /* + ** Break if this is a valid house + */ +// if (HouseClass::As_Pointer(curhouse) && Verify_House(curhouse, objtype)) { + break; +// } + } + + return(curhouse); +} + + +/*************************************************************************** + * MapEditClass::Fatal -- exits with error message * + * * + * INPUT: * + * code tells which message to display; this minimizes the * + * use of character strings in the code. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Fatal(int txt) +{ + //Prog_End(); + printf("%s\n", txt); + Emergency_Exit(EXIT_FAILURE); +} + + +bool MapEditClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (Debug_Map) { + /* + ** The popup gadgets require the entire map to be redrawn if we scroll. + */ + if (really) { + Flag_To_Redraw(true); + } + } + return(MouseClass::Scroll_Map(facing, distance, really)); +} + + +#ifdef OBSOLETE +void MapEditClass::Flag_To_Redraw(bool complete) +{ + MouseClass::Flag_To_Redraw(complete); +} +#endif + + +void MapEditClass::Detach(ObjectClass * object) +{ + if (GrabbedObject == object) { + GrabbedObject = 0; + } +} + +bool MapEditClass::Get_Waypoint_Name(char wayptname[]) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 100, // dialog width + D_DIALOG_H = 56, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_EDIT_W = D_DIALOG_W - (D_MARGIN * 2), + D_EDIT_H = 13, + D_EDIT_X = D_DIALOG_X + D_MARGIN, + D_EDIT_Y = D_DIALOG_Y + 20, + + D_BUTTON_X = D_DIALOG_X + D_MARGIN, + D_BUTTON_Y = D_DIALOG_Y + 40, + D_BUTTON_W = 40, + D_BUTTON_H = 13, + + D_CANCEL_X = D_DIALOG_X + 53, + D_CANCEL_Y = D_DIALOG_Y + 40, + D_CANCEL_W = 40, + D_CANCEL_H = 13, + + }; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_EDIT, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + wayptname[0] = 0; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TextButtonClass button (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_BUTTON_X, D_BUTTON_Y, D_BUTTON_W); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W); + EditClass editbtn (BUTTON_EDIT, wayptname, 3, TPF_EFNT|TPF_NOSHADOW, D_EDIT_X, D_EDIT_Y, D_EDIT_W, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + + /* + ** Main Processing Loop. + */ + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else if (Main_Loop()) { + process = false; + cancel = true; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); +// Draw_Caption(caption, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN) { + input = (KeyNumType)(BUTTON_OK|KN_BUTTON); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_OK | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + process = false; + cancel = false; + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + cancel = true; + process = false; + break; + + default: + break; + } + } + + Map.Flag_To_Redraw(true); + if (cancel) return(false); + + return(true); +} + +void MapEditClass::Update_Waypoint(int waypt_idx) +{ + CELL cell; + + /* + ** Unflag cell for this waypoint if there is one + */ + cell = Scen.Waypoint[waypt_idx]; + if (cell != -1) { + if (Scen.Waypoint[WAYPT_HOME] != cell && Scen.Waypoint[WAYPT_REINF] != cell) { + (*this)[cell].IsWaypoint = 0; + } + Flag_Cell(cell); + } + Scen.Waypoint[waypt_idx] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Changed = 1; + Flag_Cell(CurrentCell); +} + + +/*************************************************************************** + * MapEditClass::Read_INI -- overloaded Read_INI function * + * * + * Overloading this function gives the map editor a chance to initialize * + * certain values every time a new INI is read. * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Read_INI(CCINIClass & ini) +{ + /* + ** Invoke parent's Read_INI + */ + Mono_Printf("We are in Read_INI\n"); + + MouseClass::Read_INI(ini); + BaseGauge->Set_Value(Scen.Percent); + + Mono_Clear_Screen(); + Mono_Printf("Scen.Percent = %d", Scen.Percent); + +// BaseGauge->Set_Value(Scen.Percent); +} + + +void MapEditClass::Write_INI(CCINIClass & ini) +{ + MouseClass::Write_INI(ini); +// ini.Put_Int("Basic", "Percent", Scen.Percent); +} + +#endif + +#include "mapedsel.cpp" + diff --git a/REDALERT/MAPEDIT.H b/REDALERT/MAPEDIT.H new file mode 100644 index 000000000..c2e343a6b --- /dev/null +++ b/REDALERT/MAPEDIT.H @@ -0,0 +1,341 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDIT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 14, 1994 * + * * + * Last Update : May 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * This class is derived from the normal display map class. It exists * + * only to allow editing and adding items to the map. * + *---------------------------------------------------------------------------------------------* + * House-setting functions: The editor contains several house maintenance routines: * + * Verify_House: tells if the given ObjectType can be owned by the given HousesType * + * Cycle_House: Finds the next valid house for the given ObjectType; used when a new object * + * can't be owned by the current editor HousesType. * + * Change_House: attempts to change the owner of the currently-selected object * + * Toggle_House: cycles the HousesType of a pending placement object * + * Set_House_Buttons: sets house buttons in accordance with the given HousesType * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAPEDIT_H +#define MAPEDIT_H + +#include "function.h" + +/* +** This is the maximum # of ObjectTypeClasses the editor has to deal with. +*/ +enum MapEdit1Enum { + MAX_EDIT_OBJECTS = // max # of ObjectTypeClasses allowed + (int)TEMPLATE_COUNT + + (int)OVERLAY_COUNT + + (int)SMUDGE_COUNT + + (int)TERRAIN_COUNT + + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)VESSEL_COUNT + + (int)STRUCT_COUNT, + + MAX_TEAM_CLASSES = // max # ObjectTypeClasses for a team + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT, + +// NUM_EDIT_MISSIONS = 6, // # missions that can be assigned an object + + NUM_EDIT_CLASSES = 9, // # different classes (templates, terrain, etc) + + MAX_MAIN_MENU_NUM = 8, + MAX_MAIN_MENU_LEN = 20, + + MAX_AI_MENU_NUM = 6, + MAX_AI_MENU_LEN = 20, + + POPUP_HOUSE_X=10, + POPUP_HOUSE_Y=100, + POPUP_HOUSE_W=60, + POPUP_HOUSE_H=(190-100), + +// POPUP_GDI_W = 50, +// POPUP_GDI_H = 9, +// POPUP_GDI_X = 10, +// POPUP_GDI_Y = 160, + +// POPUP_NOD_W = 50, +// POPUP_NOD_H = 9, +// POPUP_NOD_X = 10, +// POPUP_NOD_Y = 169, + +// POPUP_NEUTRAL_W = 50, +// POPUP_NEUTRAL_H = 9, +// POPUP_NEUTRAL_X = 10, +// POPUP_NEUTRAL_Y = 178, + +// POPUP_MULTI1_W = 25, +// POPUP_MULTI1_H = 9, +// POPUP_MULTI1_X = 10, +// POPUP_MULTI1_Y = 160, + +// POPUP_MULTI2_W = 25, +// POPUP_MULTI2_H = 9, +// POPUP_MULTI2_X = 35, +// POPUP_MULTI2_Y = 160, + +// POPUP_MULTI3_W = 25, +// POPUP_MULTI3_H = 9, +// POPUP_MULTI3_X = 10, +// POPUP_MULTI3_Y = 169, + +// POPUP_MULTI4_W = 25, +// POPUP_MULTI4_H = 9, +// POPUP_MULTI4_X = 35, +// POPUP_MULTI4_Y = 169, + + POPUP_MISSION_W = 80, + POPUP_MISSION_H = 40, + POPUP_MISSION_X = 70, + POPUP_MISSION_Y = 150, + + POPUP_FACEBOX_W = 30, + POPUP_FACEBOX_H = 30, + POPUP_FACEBOX_X = 160, + POPUP_FACEBOX_Y = 160, + + POPUP_HEALTH_W = 50, + POPUP_HEALTH_H = 10, + POPUP_HEALTH_X = 200, + POPUP_HEALTH_Y = 170, + + POPUP_BASE_W = 50, + POPUP_BASE_H = 8, + POPUP_BASE_X = 300 - 50, + POPUP_BASE_Y = 0 +}; + +/* +** These are the button ID's for the pop-up object-editing gizmos. +** The house button ID's must be sequential, with a 1-to-1 correspondence to +** the HousesType values. +*/ +enum MapEditButtonIDEnum{ + POPUP_SPAIN=500, + POPUP_FIRST=POPUP_SPAIN, + POPUP_GREECE, + POPUP_USSR, + POPUP_ENGLAND, + POPUP_ITALY, + POPUP_GERMANY, + POPUP_FRANCE, + POPUP_TURKEY, + POPUP_HOUSELIST, // House selection list. + POPUP_SELLABLE, // Allowed to sell. + POPUP_REBUILDABLE, // Allowed to rebuild. + POPUP_MISSIONLIST, // list box for missions + POPUP_HEALTHGAUGE, // health of object + POPUP_FACINGDIAL, // object's facing + POPUP_BASEPERCENT, // Base's percent-built slider + MAP_AREA, // map as a click-able thingy + BUTTON_FLAG=0x8000 +}; + + +class TeamTypeClass; + +class MapEditClass : public MouseClass +{ + /* + ** Public Interface + */ + public: + + /* + ** mapedit.cpp + */ + MapEditClass(void); + MapEditClass(NoInitClass const & x) : MouseClass(x) {}; + bool Get_Waypoint_Name(char wayptname[]); + void Update_Waypoint(int waypt_index); + + virtual void One_Time(void); // One-time init + virtual void Init_IO(void); // Inits button list + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool forced = true); + virtual bool Scroll_Map(DirType facing, int & distance, bool really=true); + virtual void Read_INI(CCINIClass & ini); + virtual void Write_INI(CCINIClass & ini); + virtual void Detach(ObjectClass * object); + void Detach(TARGET target, bool all=true) {MouseClass::Detach(target, all);} + void Clear_List(void); + bool Add_To_List(ObjectTypeClass const *object); + void Main_Menu(void); + void AI_Menu(void); + bool Mouse_Moved(void); + bool Verify_House(HousesType house, ObjectTypeClass const * objtype); + HousesType Cycle_House(HousesType curhouse, ObjectTypeClass const * objtype); +// int Trigger_Needs_Team(TriggerClass *trigger); + void Fatal(int txt); + + /* + ** mapeddlg.cpp + */ + int New_Scenario(void); + int Load_Scenario(void); + int Save_Scenario(void); + int Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp); + int Size_Map(int x, int y, int w, int h); + int Scenario_Dialog(void); + void Handle_Triggers(void); + int Select_Trigger(void); + + /* + ** mapedplc.cpp + */ + int Placement_Dialog(void); + void Start_Placement(void); + int Place_Object(void); + void Cancel_Placement(void); + void Place_Next(void); + void Place_Prev(void); + void Place_Next_Category(void); + void Place_Prev_Category(void); + void Place_Home(void); + void Toggle_House(void); + void Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id); + void Start_Trigger_Placement(void); + void Stop_Trigger_Placement(void); + void Place_Trigger(void); + void Start_Base_Building(void); + void Cancel_Base_Building(void); + void Build_Base_To(int percent); + + /* + ** mapedsel.cpp + */ + int Select_Object(void); + void Select_Next(void); + void Popup_Controls(void); + void Grab_Object(void); + int Move_Grabbed_Object(void); + bool Change_House(HousesType newhouse); + + /* + ** mapedtm.cpp + */ + void Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house); + void Handle_Teams(char const * caption); + int Select_Team(char const * caption); + int Team_Members(HousesType house); + + /* + ** Private Interface + */ + private: + /* + ** This is the last-requested variation of a loaded/saved/new scenario. + */ +// ScenarioVarType ScenVar; + + /* + ** Array of all TypeClasses the user can add to the map; cleared by + ** Clear_List(), added to by Add_To_List() + */ + ObjectTypeClass const * Objects[MAX_EDIT_OBJECTS]; + int ObjCount; // # of objects in the Objects array + + /* + ** Last-selected object to place, and last-selected house of object + */ + int LastChoice; // index of item user picked last + HousesType LastHouse; // house of last item picked + + /* + ** Variables for grabbing/moving objects + */ + ObjectClass * GrabbedObject; // object "grabbed" with mouse + CELL GrabOffset; // offset to grabbed obj's upper-left + unsigned long LastClickTime; // time of last LMOUSE click + + /* + ** Number of each type of object in Objects, so we can switch categories + */ + int NumType[NUM_EDIT_CLASSES]; // # of each type of class: + // 0 = Template + // 1 = Overlay + // 2 = Smudge + // 3 = Terrain + // 4 = Unit + // 5 = Infantry + // 6 = Vessels + // 7 = Building + // 8 = Aircraft + + /* + ** The offset of each type of object within the Objects[] array + */ + int TypeOffset[NUM_EDIT_CLASSES]; // offsets within Objects[] + + /* + ** The "current" trigger for point-and-click trigger setting + */ + TriggerTypeClass * CurTrigger; // current trigger + + /* + ** The "current" team type for editing & associating with a trigger + */ + TeamTypeClass * CurTeam; // current team + + /* + ** Bitfields for flags & such + */ + unsigned Changed : 1; // 1 = changes are unsaved + unsigned LMouseDown : 1; // 1 = left mouse is held down + unsigned BaseBuilding : 1; // 1 = we're in base-building mode + + /* + ** Variables for pre-building a base + */ +// int BasePercent; // Percentage the base will be built + + /* + ** Variables for supporting the object-editing controls at screen bottom + */ + ListClass * HouseList; + ListClass * MissionList; + TriColorGaugeClass *HealthGauge; + Dial8Class *FacingDial; + ControlClass *MapArea; + TextLabelClass *HealthText; + TextButtonClass * Sellable; + TextButtonClass * Rebuildable; + static char HealthBuf[20]; + GaugeClass *BaseGauge; + TextLabelClass *BaseLabel; + static MissionType MapEditMissions[]; +}; + +#endif diff --git a/REDALERT/MAPEDPLC.CPP b/REDALERT/MAPEDPLC.CPP new file mode 100644 index 000000000..41139b0ee --- /dev/null +++ b/REDALERT/MAPEDPLC.CPP @@ -0,0 +1,1821 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDPLC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDPLC.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-placement routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Build_Base_To -- builds the AI base to the given percent* + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * MapEditClass::Cancel_Placement -- cancels placement mode * + * MapEditClass::Place_Home -- homes the placement object * + * MapEditClass::Place_Next -- while placing object, goes to next * + * MapEditClass::Place_Next_Category -- places next object category * + * MapEditClass::Place_Object -- attempts to place the current object * + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * MapEditClass::Place_Prev_Category -- places previous object category * + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * MapEditClass::Start_Base_Building -- starts base-building mode * + * MapEditClass::Start_Placement -- enters placement mode * + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode* + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * MapEditClass::Toggle_House -- toggles current placement object's house* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * * + * This function sets LastChoice & LastHouse to the values chosen * + * by the user. It's up to the caller to call Start_Placement to enter * + * placement mode. * + * This routine does not modify PendingObject or PendingHouse. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ [GDI] [NOD] [Neutral] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ [Template] ³ * + * ³ ³ ³ [Overlay ] ³ * + * ³ ³ ³ [Smudge ] ³ * + * ³ ³ ³ [Terrain ] ³ * + * ³ ³ (Object picture) ³ [Unit ] ³ * + * ³ ³ ³ [Infantry] ³ * + * ³ ³ ³ [Aircraft] ³ * + * ³ ³ ³ [Building] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄ¿ ³ * + * ³ [<-] [->] ³(Grid)³ ³ * + * ³ ³ ³ ³ * + * ³ [OK] [Cancel] ÀÄÄÄÄÄÄÙ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 12/13/1995 JLB : Fixed house buttons to handle expanded house list. * + * 05/12/1996 JLB : Handles hi-res. * + *=========================================================================*/ +int MapEditClass::Placement_Dialog(void) +{ + HousesType house; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 180, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 11, + D_MARGIN = 7, + + D_PICTURE_W = 152, // must be divisible by 8! + D_PICTURE_H = 105, + D_PICTURE_X = D_DIALOG_X + 35, // must start on a byte boundary! + D_PICTURE_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + D_PICTURE_CX = D_PICTURE_X + D_PICTURE_W / 2, + + D_GDI_W = 65, + D_GDI_H = 9, + D_GDI_X = D_PICTURE_X+D_PICTURE_W+5, + D_GDI_Y = D_PICTURE_Y, + + D_LEFT_W = 45, + D_LEFT_H = 9, + D_LEFT_X = D_PICTURE_CX - 5 - D_LEFT_W, + D_LEFT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_RIGHT_W = 45, + D_RIGHT_H = 9, + D_RIGHT_X = D_PICTURE_CX + 5, + D_RIGHT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_TEMPLATE_W = 70, + D_TEMPLATE_H = 9, + D_TEMPLATE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TEMPLATE_W - 30, + D_TEMPLATE_Y = D_PICTURE_Y, + + D_OVERLAY_W = 70, + D_OVERLAY_H = 9, + D_OVERLAY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_OVERLAY_W - 30, + D_OVERLAY_Y = D_TEMPLATE_Y + D_TEMPLATE_H, + + D_SMUDGE_W = 70, + D_SMUDGE_H = 9, + D_SMUDGE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_SMUDGE_W - 30, + D_SMUDGE_Y = D_OVERLAY_Y + D_OVERLAY_H, + + D_TERRAIN_W = 70, + D_TERRAIN_H = 9, + D_TERRAIN_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TERRAIN_W - 30, + D_TERRAIN_Y = D_SMUDGE_Y + D_SMUDGE_H, + + D_UNIT_W = 70, + D_UNIT_H = 9, + D_UNIT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_UNIT_W - 30, + D_UNIT_Y = D_TERRAIN_Y + D_TERRAIN_H, + + D_INFANTRY_W = 70, + D_INFANTRY_H = 9, + D_INFANTRY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_INFANTRY_W - 30, + D_INFANTRY_Y = D_UNIT_Y + D_UNIT_H, + + D_AIRCRAFT_W = 70, + D_AIRCRAFT_H = 9, + D_AIRCRAFT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIRCRAFT_W - 30, + D_AIRCRAFT_Y = D_INFANTRY_Y + D_INFANTRY_H, + + D_BUILDING_W = 70, + D_BUILDING_H = 9, + D_BUILDING_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_BUILDING_W - 30, + D_BUILDING_Y = D_AIRCRAFT_Y + D_AIRCRAFT_H, + + D_AIR_W = 70, + D_AIR_H = 9, + D_AIR_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIR_W - 30, + D_AIR_Y = D_BUILDING_Y + D_BUILDING_H, + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_PICTURE_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN - 15, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_PICTURE_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN - 15, + + GRIDSIZE = 10, + GRIDBLOCK_W = 3, + GRIDBLOCK_H = 3, + D_GRID_X = D_DIALOG_X + D_DIALOG_W - (GRIDSIZE * GRIDBLOCK_W) - D_MARGIN - 35, + D_GRID_Y = D_DIALOG_Y + D_DIALOG_H - (GRIDSIZE * GRIDBLOCK_H) - D_MARGIN - 35, + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_GDI=100, + BUTTON_HOUSE, + BUTTON_NEXT, + BUTTON_PREV, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_TEMPLATE, + BUTTON_OVERLAY, + BUTTON_SMUDGE, + BUTTON_TERRAIN, + BUTTON_UNIT, + BUTTON_INFANTRY, + BUTTON_AIRCRAFT, + BUTTON_BUILDING, + BUTTON_AIR, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + const ObjectTypeClass * curobj; // Working object pointer. + int x,y; // for drawing the grid + KeyNumType input; // user input + short const * occupy; // ptr into object's OccupyList + int cell; // cell index for parsing OccupyList + int i; + int typeindex; // index of class type + + /* + ** Buttons + */ + ControlClass * commands; + + ListClass housebtn(BUTTON_HOUSE, + D_GDI_X, D_GDI_Y, 60, 8*16, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + house = HOUSE_FIRST; + + TextButtonClass nextbtn(BUTTON_NEXT, TXT_RIGHT, TPF_EBUTTON, D_RIGHT_X, D_RIGHT_Y, D_RIGHT_W, D_RIGHT_H); + TextButtonClass prevbtn(BUTTON_PREV, TXT_LEFT, TPF_EBUTTON, D_LEFT_X, D_LEFT_Y, D_LEFT_W, D_LEFT_H); + TextButtonClass okbtn(BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, "Cancel", TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + TextButtonClass templatebtn(BUTTON_TEMPLATE, "Template", TPF_EBUTTON, D_TEMPLATE_X, D_TEMPLATE_Y, D_TEMPLATE_W, D_TEMPLATE_H); + TextButtonClass overlaybtn(BUTTON_OVERLAY, "Overlay", TPF_EBUTTON, D_OVERLAY_X, D_OVERLAY_Y, D_OVERLAY_W, D_OVERLAY_H); + TextButtonClass smudgebtn(BUTTON_SMUDGE, "Smudge", TPF_EBUTTON, D_SMUDGE_X, D_SMUDGE_Y, D_SMUDGE_W, D_SMUDGE_H); + TextButtonClass terrainbtn(BUTTON_TERRAIN, "Terrain", TPF_EBUTTON, D_TERRAIN_X, D_TERRAIN_Y, D_TERRAIN_W, D_TERRAIN_H); + TextButtonClass unitbtn(BUTTON_UNIT, "Unit", TPF_EBUTTON, D_UNIT_X, D_UNIT_Y, D_UNIT_W, D_UNIT_H); + TextButtonClass infantrybtn(BUTTON_INFANTRY, "Infantry", TPF_EBUTTON, D_INFANTRY_X, D_INFANTRY_Y, D_INFANTRY_W, D_INFANTRY_H); + TextButtonClass aircraftbtn(BUTTON_AIRCRAFT, "Ships", TPF_EBUTTON, D_AIRCRAFT_X, D_AIRCRAFT_Y, D_AIRCRAFT_W, D_AIRCRAFT_H); + TextButtonClass buildingbtn(BUTTON_BUILDING, "Building", TPF_EBUTTON, D_BUILDING_X, D_BUILDING_Y, D_BUILDING_W, D_BUILDING_H); + TextButtonClass airbtn(BUTTON_AIR, "Aircraft", TPF_EBUTTON, D_AIR_X, D_AIR_Y, D_AIR_W, D_AIR_H); + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. (Skip aircraft, since they won't be used in the editor.) + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + /* + ** Compute offset of each class type in the Objects array + */ + TypeOffset[0] = 0; + for (i = 1; i < NUM_EDIT_CLASSES; i++) { + TypeOffset[i] = TypeOffset[i-1] + NumType[i-1]; + } + + /* + ** Return if no objects to place + */ + if (!ObjCount) { + return(-1); + } + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + if (LastChoice >= ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; // current object to choose + + commands = &nextbtn; + housebtn.Add_Tail(*commands); + prevbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + templatebtn.Add_Tail(*commands); + overlaybtn.Add_Tail(*commands); + smudgebtn.Add_Tail(*commands); + terrainbtn.Add_Tail(*commands); + unitbtn.Add_Tail(*commands); + infantrybtn.Add_Tail(*commands); + aircraftbtn.Add_Tail(*commands); + buildingbtn.Add_Tail(*commands); + airbtn.Add_Tail(*commands); + + /* + ** Make sure the recorded house selection matches the house list + ** box selection. + */ + LastHouse = HousesType(housebtn.Current_Index()); + + /* + ** Main processing loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_PLACE_OBJECT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Display the current object: + ** - save the current window dimensions + ** - adjust the window size to the actual drawable area + ** - draw the shape + ** - reset the window dimensions + */ + WindowList[WINDOW_EDITOR][WINDOWX] = D_PICTURE_X; + WindowList[WINDOW_EDITOR][WINDOWY] = D_PICTURE_Y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + Draw_Box(D_PICTURE_X, D_PICTURE_Y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, false); + curobj->Display(WinW/2, WinH>>1, WINDOW_EDITOR, LastHouse); +// curobj->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, LastHouse); + + /* + ** Erase the grid + */ + LogicPage->Fill_Rect(D_GRID_X - GRIDBLOCK_W * 2, D_GRID_Y, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, BLACK); + + /* + ** Draw a box for every cell occupied + */ + occupy = curobj->Occupy_List(); + while ( (*occupy) != REFRESH_EOL) { + cell = (*occupy); + occupy++; + x = D_GRID_X + ((cell % MAP_CELL_W) * GRIDBLOCK_W); + y = D_GRID_Y + ((cell / MAP_CELL_W) * GRIDBLOCK_H); + LogicPage->Fill_Rect(x, y, x + GRIDBLOCK_W - 1, y + GRIDBLOCK_H - 1, scheme->Bright); + } + + /* + ** Draw the grid itself + */ + for (y = 0; y <= GRIDSIZE; y++) { + for (x = 0; x <= GRIDSIZE; x++) { + LogicPage->Draw_Line(D_GRID_X + x * GRIDBLOCK_W, D_GRID_Y, + D_GRID_X + x * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, scheme->Shadow); + } + LogicPage->Draw_Line(D_GRID_X, D_GRID_Y + y * GRIDBLOCK_H, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, D_GRID_Y + y * GRIDBLOCK_H, + scheme->Shadow); + } + + /* + ** Print the object's label from the class's Full_Name(). + ** Warning: Text_String returns an EMS pointer, so standard string + ** functions won't work! + */ + Fancy_Text_Print (curobj->Full_Name(), + D_PICTURE_CX, D_PICTURE_Y + D_MARGIN, scheme, TBLACK, + TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw buttons + ** Figure out which class category we're in & highlight that button + ** This updates 'typeindex', which is used below, and it also updates + ** the category button states. + */ + i = 0; + for (typeindex = 0; typeindex < NUM_EDIT_CLASSES; typeindex++) { + i += NumType[typeindex]; + if (LastChoice < i) break; + } + templatebtn.Turn_Off(); + overlaybtn.Turn_Off(); + smudgebtn.Turn_Off(); + terrainbtn.Turn_Off(); + unitbtn.Turn_Off(); + infantrybtn.Turn_Off(); + aircraftbtn.Turn_Off(); + airbtn.Turn_Off(); + buildingbtn.Turn_Off(); + switch (typeindex + BUTTON_TEMPLATE) { + case BUTTON_TEMPLATE: + templatebtn.Turn_On(); + break; + + case BUTTON_OVERLAY: + overlaybtn.Turn_On(); + break; + + case BUTTON_SMUDGE: + smudgebtn.Turn_On(); + break; + + case BUTTON_TERRAIN: + terrainbtn.Turn_On(); + break; + + case BUTTON_UNIT: + unitbtn.Turn_On(); + break; + + case BUTTON_INFANTRY: + infantrybtn.Turn_On(); + break; + + case BUTTON_AIRCRAFT: + aircraftbtn.Turn_On(); + break; + + case BUTTON_AIR: + airbtn.Turn_On(); + break; + + case BUTTON_BUILDING: + buildingbtn.Turn_On(); + break; + } + + /* + ** Redraw buttons + */ + commands->Draw_All(); + Show_Mouse(); + display = false; + + } + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process user input + */ + switch (input) { + + /* + ** GDI House + */ + case (BUTTON_HOUSE | KN_BUTTON): + house = HousesType(housebtn.Current_Index()); + + /* + ** Set flags & buttons + */ + LastHouse = house; + display = true; + break; + + /* + ** Next in list + */ + case (KN_RIGHT): + case (BUTTON_NEXT | KN_BUTTON): + /* + ** Increment to next obj + */ + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; + + nextbtn.Turn_Off(); + display = true; + break; + + /* + ** Previous in list + */ + case (KN_LEFT): + case (BUTTON_PREV | KN_BUTTON): + + /* + ** Decrement to prev obj + */ + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount-1; + } + curobj = Objects[LastChoice]; + prevbtn.Turn_Off(); + display = true; + break; + + /* + ** Select a class type + */ + case (BUTTON_TEMPLATE | KN_BUTTON): + case (BUTTON_OVERLAY | KN_BUTTON): + case (BUTTON_SMUDGE | KN_BUTTON): + case (BUTTON_TERRAIN | KN_BUTTON): + case (BUTTON_UNIT | KN_BUTTON): + case (BUTTON_INFANTRY | KN_BUTTON): + case (BUTTON_AIRCRAFT | KN_BUTTON): + case (BUTTON_BUILDING | KN_BUTTON): + case (BUTTON_AIR | KN_BUTTON): + + /* + ** Find index of class + */ + typeindex = input - (BUTTON_TEMPLATE | KN_BUTTON); + + /* + ** If no objects of that type, do nothing + */ + if (NumType[typeindex]==0) { + display = true; + break; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Next category + */ + case KN_PGDN: + typeindex++; + if (typeindex==NUM_EDIT_CLASSES) { + typeindex = 0; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Previous category + */ + case KN_PGUP: + typeindex--; + if (typeindex < 0) { + typeindex = NUM_EDIT_CLASSES - 1; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Jump to 1st choice + */ + case KN_HOME: + LastChoice = 0; + + /* + ** Set current object + */ + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** OK + */ + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /* + ** Cancel + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Start_Placement -- enters placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Placement(void) +{ + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. These must be added in the same order expected by the + ** object selection dialog (same as button order). + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + /* + ** Compute offset of each class type in the Objects array + */ + TypeOffset[0] = 0; + for (int i = 1; i < NUM_EDIT_CLASSES; i++) { + TypeOffset[i] = TypeOffset[i-1] + NumType[i-1]; + } + + /* + ** Create the placement object: + ** - For normal placement mode, use the last-used index into Objects + ** (LastChoice), and the last-used house (LastHouse). + ** - For base-building mode, force the object to be a building, and use the + ** House specified in the Base object + */ + if (!BaseBuilding) { + if (LastChoice >= ObjCount) { + LastChoice = ObjCount - 1; + } + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } else { + if (LastChoice < TypeOffset[7]) { + LastChoice = TypeOffset[7]; + } + if (LastChoice >= ObjCount) { + LastChoice = ObjCount - 1; + } + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse = Base.House; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } + + /* + ** Error if no more objects available + */ + if (!PendingObjectPtr) { + WWMessageBox().Process("No more objects of this type available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + PendingObject = NULL; + if (BaseBuilding) { + Cancel_Base_Building(); + } + return; + } + + /* + ** Set the placement cursor + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(PendingObject->Occupy_List()); +} + + +/*************************************************************************** + * MapEditClass::Place_Object -- attempts to place the current object * + * * + * Placement of "real" objects is simply checked via their Unlimbo routine.* + * Placement of templates is more complex: * + * - for every cell in the template's OccupyList, check for objects * + * already in that cell by looking at the cell's OccupyList & * + * OverlapList * + * - "lift" all the objects in the cell by Mark'ing them * + * - temporarily place the template in that cell * + * - try to Unlimbo all the objects that were in the cell. If any * + * objects fail to Unlimbo onto that template, the template cannot * + * be placed here * + * * + * It is assumed that the object being placed is a "new" object; the * + * object's strength & mission are not set during Unlimbo. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = unable to place * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Place_Object(void) +{ + CELL template_cell; // cell being checked for template + COORDINATE obj_coord; // coord of occupier object + int okflag; // OK to place a template? + short const * occupy; // ptr into template's OccupyList + ObjectClass * occupier; // occupying object + TemplateType save_ttype; // for saving cell's TType + unsigned char save_ticon; // for saving cell's TIcon +// BaseNodeClass node; // for adding to an AI Base + + /* + ** Placing a template: + ** - first lift up any objects in the cell + ** - place the template, and try to replace the objects; if they won't go + ** back, the template can't go there + */ + //ScenarioInit++; + if (PendingObject->What_Am_I() == RTTI_TEMPLATETYPE) { + + /* + ** Loop through all cells this template will occupy + */ + okflag = true; + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Check this cell for an occupier + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + if ((*this)[template_cell].Cell_Occupier()) { + occupier = (*this)[template_cell].Cell_Occupier(); + + /* + ** Save object's coordinates + */ + obj_coord = occupier->Coord; + + /* + ** Place the object in limbo + */ + occupier->Mark(MARK_UP); + + /* + ** Set the cell's template values + */ + save_ttype = (*this)[template_cell].TType; + save_ticon = (*this)[template_cell].TIcon; + (*this)[template_cell].TType = + ((TemplateTypeClass *)PendingObject)->Type; + (*this)[template_cell].TIcon = Cell_X(*occupy) + Cell_Y(*occupy) * + ((TemplateTypeClass *)PendingObject)->Width; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Try to put the object back down + */ + if (occupier->Can_Enter_Cell(Coord_Cell(obj_coord)) != MOVE_OK) { + okflag = false; + } + + /* + ** Put everything back the way it was + */ + (*this)[template_cell].TType = save_ttype; + (*this)[template_cell].TIcon = save_ticon; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Major error if can't replace the object now + */ + occupier->Mark(MARK_DOWN); + } + occupy++; + } + + /* + ** If it's still OK after ALL THAT, place the template + */ + if (okflag) { + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Loop through all cells occupied by this template, and clear the + ** smudge & overlay. + */ + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Get cell for this occupy item + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + + /* + ** Clear smudge & overlay + */ + (*this)[template_cell].Overlay = OVERLAY_NONE; + (*this)[template_cell].OverlayData = 0; + (*this)[template_cell].Smudge = SMUDGE_NONE; + + /* + ** make adjacent cells recalc attrib's + */ + (*this)[template_cell].Recalc_Attributes(); + (*this)[template_cell].Wall_Update(); + (*this)[template_cell].Concrete_Calc(); + + occupy++; + } + + /* + ** Set flags etc + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + TotalValue = Overpass(); + Flag_To_Redraw(false); + return(0); + } + + /* + ** Failure to deploy results in a returned failure code. + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Not OK; return error + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Placing infantry: Infantry can go into cell sub-positions, so find the + ** sub-position closest to the mouse & put him there + */ + if (PendingObject->What_Am_I() == RTTI_INFANTRYTYPE) { + + /* + ** Find cell sub-position + */ + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + obj_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + } else { + obj_coord = NULL; + } + + /* + ** No free spots; don't place the object + */ + if (obj_coord == NULL) { + //ScenarioInit--; + return(-1); + } + + /* + ** Unlimbo the object + */ + if (PendingObjectPtr->Unlimbo(obj_coord)) { + ((InfantryClass *)PendingObjectPtr)->Set_Occupy_Bit(obj_coord); +// Map[obj_coord].Flag.Composite |= +// (1 << CellClass::Spot_Index(obj_coord)); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + //ScenarioInit--; + return(-1); + } + + /* + ** Placing an object + */ + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Update the Tiberium computation if we're placing an overlay + */ + if (PendingObject->What_Am_I() == RTTI_OVERLAYTYPE && + ((OverlayTypeClass *)PendingObject)->IsTiberium) { + TotalValue = Overpass(); + Flag_To_Redraw(false); + } + + /* + ** If we're building a base, add this building to the base's Node list. + */ + if (BaseBuilding && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE) { +// node.Type = ((BuildingTypeClass *)PendingObject)->Type; +// node.Cell = Coord_Cell(PendingObjectPtr->Coord); + Base.Nodes.Add(BaseNodeClass(((BuildingTypeClass *)PendingObject)->Type, Coord_Cell(PendingObjectPtr->Coord))); + } + + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + return(-1); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Placement -- cancels placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Placement(void) +{ + /* + ** Delete the placement object + */ + delete PendingObjectPtr; + PendingObject = 0; + PendingObjectPtr = 0; + PendingHouse = HOUSE_NONE; + + /* + ** Restore cursor shape + */ + Set_Cursor_Shape(0); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next -- while placing object, goes to next * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Increments LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * incrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + /* + ** Go to next object in Objects list + */ + LastChoice++; + if (LastChoice == ObjCount) { + + /* + ** If we're in normal placement mode, wrap to the 1st object; + ** if we're in base-building mode, wrap to the 1st building + */ + if (!BaseBuilding) { + LastChoice = 0; + } else { + LastChoice = TypeOffset[7]; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Decrements LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * decrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Go to prev object in Objects list + */ + LastChoice--; + + /* + ** If we're in normal placement mode, wrap at the 1st object. + ** If we're building a base, wrap at the 1st building. + */ + if (!BaseBuilding) { + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } else { + if (LastChoice < TypeOffset[7]) { + LastChoice = ObjCount - 1; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next_Category -- places next category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to next category in Objects list + */ + i = LastChoice; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i++; + if (i == ObjCount) { + i = 0; + } + } + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev_Category -- places previous category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to prev category in Objects list + */ + i = LastChoice; + + /* + ** Scan for start of this category + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + i--; + if (i < 0) i = ObjCount-1; + LastChoice = i; + + /* + ** Scan for the previous category + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + i++; + if (i >= ObjCount) i = 0; + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Home -- homes the placement object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Home(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + /* + ** Loop until we create a valid object + */ + LastChoice = 0; + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ + if (!Verify_House(LastHouse, Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Toggle_House -- toggles current placement object's house * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Toggle_House(void) +{ + TechnoClass *tp; + + /* + ** Don't allow this command if we're building a base; the only valid + ** house for base-building is the one assigned to the base. + */ + if (BaseBuilding) { + return; + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!PendingObjectPtr->Is_Techno()) { + return; + } + + /* + ** Select the house that will own this object + */ + LastHouse = Cycle_House(PendingObjectPtr->Owner(), PendingObject); + + /* + ** Change the house + */ + tp = (TechnoClass *)PendingObjectPtr; + tp->House = HouseClass::As_Pointer(LastHouse); + + /* + ** Set house variables to new house + */ + PendingHouse = LastHouse; +} + + +/*************************************************************************** + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * * + * Looks in the given button list for the given GDI, NOD & Neutral button * + * id's. Sets the On/Off state of the buttons based on the given house, * + * only if that button is found in the list. * + * * + * INPUT: * + * house house to set buttons to * + * btnlist ptr to button list to search * + * base_id button ID for GDI; assumes other id's are sequential* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + * 01/26/1996 JLB : Uses new house selection list method. * + *=========================================================================*/ +void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass *, int ) +//void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass * btnlist, int base_id) +{ + HouseList->Set_Selected_Index(house); + +#ifdef NEVER + HousesType h; + int id; + TextButtonClass * btn; + + /* + ** Loop through all houses, searching the button list for each one. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + + /* + ** Compute the desired button ID; get a pointer to the button + */ + id = (int)h + base_id; + btn = (TextButtonClass *)btnlist->Extract_Gadget(id); + if (btn) { + + /* + ** If this house value is the desired one, turn the button on; + ** otherwise, turn it off. + */ + if (h == house) { + btn->Turn_On(); + } else { + btn->Turn_Off(); + } + } + } +#endif +} + + +/*************************************************************************** + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Trigger_Placement(void) +{ + Set_Default_Mouse(MOUSE_CAN_MOVE); + Override_Mouse_Shape(MOUSE_CAN_MOVE); +} + + +/*************************************************************************** + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Stop_Trigger_Placement(void) +{ + CurTrigger = NULL; + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); +} + + +/*************************************************************************** + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Trigger(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** Assign trigger to an object + */ + AttachType a1 = CurTrigger->Attaches_To(); + if (object && (a1 & ATTACH_OBJECT) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + if (tt) { + object->Trigger = tt; + } + } + } else { + + /* + ** Assign trigger to a cell + */ + if ((a1 & ATTACH_CELL) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + Map[cell].Trigger = tt; + } +// CellTriggers[cell] = CurTrigger; + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Start_Base_Building -- starts base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Base_Building(void) +{ + /* + ** Fully build the base so the user can edit it + */ + Build_Base_To(100); + + /* + ** Start placement mode + */ + BaseBuilding = true; + Start_Placement(); + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Base_Building(void) +{ + /* + ** Build the base to the proper amount + */ + Build_Base_To(Scen.Percent); + + /* + ** Cancel placement mode + */ + Cancel_Placement(); + BaseBuilding = false; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Build_Base_To -- builds the AI base to the given percent * + * * + * INPUT: * + * percent percentage to build base to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Base_To(int percent) +{ + int i; + int num_buildings; + BuildingTypeClass const * objtype; + BuildingClass * obj; + + //ScenarioInit++; + + /* + ** Completely dismantle the base, so we start at a known point + */ + for (i = 0; i < Base.Nodes.Count(); i++) { + if (Base.Is_Built(i)) { + obj = Base.Get_Building(i); + delete obj; + } + } + + /* + ** Compute number of buildings to build + */ + num_buildings = (Base.Nodes.Count() * percent) / 100; + + /* + ** Build the base to the desired amount + */ + for (i = 0; i < num_buildings; i++) { + /* + ** Get a ptr to the type of building to build, create one, and unlimbo it. + */ + objtype = &BuildingTypeClass::As_Reference(Base.Nodes[i].Type); + obj = (BuildingClass *)objtype->Create_One_Of(HouseClass::As_Pointer(Base.House)); + + /* + ** If unlimbo fails, error out + */ + ScenarioInit++; + if (!obj->Unlimbo(Cell_Coord(Base.Nodes[i].Cell))) { + delete obj; + WWMessageBox().Process("Unable to build base!"); + ScenarioInit--; + return; + } + ScenarioInit--; + } + + //ScenarioInit--; +} + + +#endif diff --git a/REDALERT/MAPEDSEL.CPP b/REDALERT/MAPEDSEL.CPP new file mode 100644 index 000000000..78532a676 --- /dev/null +++ b/REDALERT/MAPEDSEL.CPP @@ -0,0 +1,585 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDSEL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : April 30, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-selection & manipulation routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Change_House -- changes CurrentObject's house * + * MapEditClass::Grab_Object -- grabs the current object * + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls* + * MapEditClass::Select_Next -- selects next object on the map * + * MapEditClass::Select_Object -- selects an object for processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * Select_Object -- selects an object for processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object selected, -1 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Object(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + int rc=0; + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** If no object, unselect the current one + */ + if (!object) { + if (CurrentObject.Count()) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + + /* + ** Turn off object controls + */ + Popup_Controls(); + } + rc = -1; + } else { + + /* + ** Select object only if it's different + */ + if (!CurrentObject.Count() || (CurrentObject.Count() && object != CurrentObject[0])) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + object->Select(); + + /* + ** Set mouse shape back to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show the popup controls + */ + Popup_Controls(); + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + + return(rc); +} + + +/*************************************************************************** + * MapEditClass::Select_Next -- selects next object on the map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Select_Next(void) +{ + ObjectClass * obj; + CELL obj_cell; + int smap_w; // screen map width in icons + int smap_h; // screen map height in icons + int cell_x; // cell-x of next object + int cell_y; // cell-y of next object + int tcell_x; // cell-x of TacticalCell + int tcell_y; // cell-y of TacticalCell + + /* + ** Get next object on the map + */ + obj = Map.Next_Object(CurrentObject[0]); + + if (obj) { + /* + ** Unselect current object if there is one + */ + Unselect_All(); + + /* + ** Select this object + */ + obj->Select(); + } + + /* + ** Restore mouse shape to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show pop-up controls + */ + Popup_Controls(); + + /* + ** Make sure object is shown on the screen + */ + /* + ** compute screen map dimensions + */ + smap_w = Lepton_To_Cell(TacLeptonWidth); + smap_h = Lepton_To_Cell(TacLeptonHeight); + + /* + ** compute x,y of object's cell + */ + obj_cell = Coord_Cell(CurrentObject[0]->Coord); + cell_x = Cell_X(obj_cell); + cell_y = Cell_Y(obj_cell); + tcell_x = Coord_XCell(TacticalCoord); + tcell_y = Coord_YCell(TacticalCoord); + + /* + ** If object is off-screen, move map + */ + if (cell_x < tcell_x) { + tcell_x = cell_x; + } else { + if (cell_x >= tcell_x + smap_w) { + tcell_x = cell_x - smap_w + 1; + } + } + + if (cell_y < tcell_y) { + tcell_y = cell_y; + } else { + if (cell_y >= tcell_y + smap_h) { + tcell_y = cell_y - smap_h + 1; + } + } + + ScenarioInit++; + Set_Tactical_Position(XY_Coord(Cell_To_Lepton(tcell_x), Cell_To_Lepton(tcell_y))); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls * + * * + * Call this routine whenever the CurrentObject changes. The routine will * + * selectively enable or disable the popup controls based on whether * + * CurrentObject is NULL, or if it's a Techno object, or what type of * + * Techno object it is. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + * 04/30/1996 JLB : Revamped for new buttons and stuff. * + *=========================================================================*/ +void MapEditClass::Popup_Controls(void) +{ + const TechnoTypeClass * objtype = NULL; + HousesType owner; // object's current owner + int mission_index; // object's current mission + int strength; // object's 0-255 strength value + int i; + + /* + ** Remove all buttons from GScreen's button list (so none of them provide + ** input any more); then, destroy the list by Zapping each button. Then, + ** we'll have to add at least the MapArea button back to the Input button + ** list before we return, plus any other buttons to process input for. We + ** always must add MapArea LAST in the list, so it doesn't intercept the + ** other buttons' input. + */ + Remove_A_Button(*HouseList); + Remove_A_Button(*MissionList); + Remove_A_Button(*HealthGauge); + Remove_A_Button(*HealthText); + Remove_A_Button(*FacingDial); + Remove_A_Button(*BaseGauge); + Remove_A_Button(*BaseLabel); + Remove_A_Button(*MapArea); + + Remove_A_Button(*Sellable); + Remove_A_Button(*Rebuildable); + + /* + ** If no current object, hide the list + */ + if (!CurrentObject.Count()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + /* + ** If not Techno, no need for editing buttons + */ + if (!CurrentObject[0]->Is_Techno()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + objtype = (TechnoTypeClass const *)&CurrentObject[0]->Class_Of(); + + /* + ** Get object's current values + */ + owner = CurrentObject[0]->Owner(); + mission_index = 0; + for (i = 0; i < NUM_EDIT_MISSIONS; i++) { + if (CurrentObject[0]->Get_Mission() == MapEditMissions[i]) { + mission_index = i; + } + } + strength = CurrentObject[0]->Health_Ratio()*256; + + switch (objtype->What_Am_I()) { + case RTTI_VESSELTYPE: + case RTTI_UNITTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_AIRCRAFTTYPE: + MissionList->Set_Selected_Index(mission_index); + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + FacingDial->Set_Direction(((TechnoClass *)CurrentObject[0])->PrimaryFacing); + + /* + ** Make the list. + */ + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*MissionList); + Add_A_Button(*HealthText); + Add_A_Button(*FacingDial); + break; + + case RTTI_BUILDINGTYPE: + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*HealthText); + + Add_A_Button(*Sellable); + if (((BuildingClass*)CurrentObject[0])->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + Add_A_Button(*Rebuildable); + if (((BuildingClass*)CurrentObject[0])->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + + if (objtype->IsTurretEquipped) { + FacingDial->Set_Direction(((TechnoClass *) CurrentObject[0])->PrimaryFacing); + Add_A_Button(*FacingDial); + } + break; + } + + /* + ** Add the map area last, so it's "underneath" the other buttons, and won't + ** intercept input for those buttons. + */ + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); +} + + +/*************************************************************************** + * MapEditClass::Grab_Object -- grabs the current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Grab_Object(void) +{ + CELL cell; + + if (CurrentObject.Count()) { + GrabbedObject = CurrentObject[0]; + + /* + ** Find out which cell 'ZoneCell' is in relation to the object's current cell + */ + cell = Coord_Cell(GrabbedObject->Coord); + GrabOffset = cell - ZoneCell; + } +} + + +/*************************************************************************** + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object moved, -1 = it didn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Move_Grabbed_Object(void) +{ + COORDINATE new_coord = 0; + int retval = -1; + + /* + ** Lift up the object + */ + GrabbedObject->Mark(MARK_UP); + + /* + ** If infantry, use a free spot in this cell + */ + if (GrabbedObject->Is_Infantry()) { + + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + new_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + + /* + ** Clear the occupied bit in this infantry's cell. + */ + ((InfantryClass *)GrabbedObject)->Clear_Occupy_Bit(GrabbedObject->Coord); + } else { + new_coord = NULL; + } + + } else { + + /* + ** Non-infantry: use cell's center coordinate + */ + new_coord = Cell_Coord(ZoneCell + GrabOffset); + + if (GrabbedObject->What_Am_I() == RTTI_BUILDING || + GrabbedObject->What_Am_I() == RTTI_TERRAIN) { + + new_coord = Coord_Whole(new_coord); + } + + /* + ** Try to place object at new coordinate + */ + if (GrabbedObject->Can_Enter_Cell(Coord_Cell(new_coord)) != MOVE_OK) { + new_coord = NULL; + } + } + if (new_coord != NULL) { + + /* + ** If this object is part of the AI's Base list, change the coordinate + ** in the Base's Node list. + */ + if (GrabbedObject->What_Am_I()==RTTI_BUILDING && + Base.Get_Node((BuildingClass *)GrabbedObject)) + Base.Get_Node((BuildingClass *)GrabbedObject)->Cell = Coord_Cell(new_coord); + + GrabbedObject->Coord = new_coord; + retval = 0; + } + GrabbedObject->Mark(MARK_DOWN); + + /* + ** For infantry, set the bit in its new cell marking that spot as occupied. + */ + if (GrabbedObject->Is_Infantry()) { + ((InfantryClass *)GrabbedObject)->Set_Occupy_Bit(new_coord); + } + + /* + ** Re-select the object, and reset the mouse pointer + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + Flag_To_Redraw(true); + + return(retval); +} + + +/*************************************************************************** + * MapEditClass::Change_House -- changes CurrentObject's house * + * * + * INPUT: * + * newhouse house to change to * + * * + * OUTPUT: * + * 1 = house was changed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Change_House(HousesType newhouse) +{ + TechnoClass *tp; + + /* + ** Return if no current object + */ + if (!CurrentObject.Count()) { + return(false); + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!CurrentObject[0]->Is_Techno()) { + return(false); + } + + /* + ** You can't change the house if the object is part of the AI's Base. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && Base.Is_Node((BuildingClass *)CurrentObject[0])) { + return(false); + } + + /* + ** Verify that the target house exists + */ + if (HouseClass::As_Pointer(newhouse)==NULL) { + return(false); + } + + /* + ** Verify that this is a valid owner + */ +// if (!Verify_House(newhouse, &CurrentObject[0]->Class_Of())) { +// return(false); +// } + + /* + ** Change the house + */ + tp = (TechnoClass *)CurrentObject[0]; + tp->House = HouseClass::As_Pointer(newhouse); + + tp->IsOwnedByPlayer = false; + if (tp->House == PlayerPtr) { + tp->IsOwnedByPlayer = true; + } + + return(true); +} + + +#endif diff --git a/REDALERT/MAPEDTM.CPP b/REDALERT/MAPEDTM.CPP new file mode 100644 index 000000000..1ff3699de --- /dev/null +++ b/REDALERT/MAPEDTM.CPP @@ -0,0 +1,942 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDTM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDTM.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : May 7, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * MapEditClass::Select_Team -- user selects a team from a list * + * MapEditClass::Team_Members -- user picks makeup of a team * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Handle_Teams(char const * caption) +{ + int rc; + + /* + ** Team dialog processing loop: + ** - Invoke the team selection dialog. If a team's selected, break + ** & return + ** - If user wants to edit the current team, do so + ** - If user wants to create new team, new a TeamTypeClass & edit it + ** - If user wants to delete team, delete the current team + ** - Keep looping until 'OK' + */ + for (;;) { + + /* + ** Select team + */ + rc = Select_Team(caption); + + /* + ** 'OK'; break + */ + if (rc == 0) { + break; + } else { + + /* + ** 'Edit' + */ + if (rc == 1 && CurTeam) { + if (CurTeam->Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** 'New' + */ + if (rc == 2) { + + /* + ** Create a new team + */ + CurTeam = new TeamTypeClass(); + if (CurTeam) { + /* + ** delete it if user cancels + */ + if (!CurTeam->Edit()) { + delete CurTeam; + CurTeam = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more teams available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } else { + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTeam) { + Detach_This_From_All(CurTeam->As_Target(), true); + delete CurTeam; + //CurTeam->Remove(); + CurTeam = NULL; + } + } + } + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Team -- user selects a team from a list * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sorted team list. * + *=========================================================================*/ +int MapEditClass::Select_Team(char const * ) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, // dialog width + D_DIALOG_H = 250, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 25, // margin width/height + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-20, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 20, + D_LIST_H = (D_DIALOG_H-50)-D_LIST_Y, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + + /* + ** Button enumerations: + */ + enum { + TEAM_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables + */ + bool edit_team = false; // true = user wants to edit + bool new_team = false; // true = user wants to new + bool del_team = false; // true = user wants to new + static int tabs[] = {35, 60, 80, 100}; // list box tab stops + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands = NULL; // the button list + + TListClass > teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W); + TextButtonClass newbtn (BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W); + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W); + TextButtonClass okbtn (BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in team names + */ + for (int index = 0; index < TeamTypes.Count(); index++) { + teamlist.Add_Item(TeamTypes.Ptr(index)); + } + + PNBubble_Sort(&teamlist[0], teamlist.Count()); + + if (!CurTeam || !CurTeam->IsActive) CurTeam = NULL; + + if (CurTeam) { + teamlist.Set_Selected_Index(CurTeam); + CurTeam = teamlist.Current_Item(); + } else { + teamlist.Set_Selected_Index(0); + if (TeamTypes.Count()) { + CurTeam = teamlist.Current_Item(); + } + } + + /* + ** Create the list + */ + commands = &teamlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ** Init tab stops for list + */ + teamlist.Set_Tabs(tabs); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + commands->Draw_All(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + CurTeam = teamlist.Current_Item(); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (teamlist.Count()) { + process = false; + edit_team = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_team = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_team = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (edit_team) return(1); + if (new_team) return(2); + if (del_team) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Team_Members -- user picks makeup of a team * + * * + * Team members are rendered in a 24 x 24 area; the Window coordinates * + * have to be set to this area when the object's 'Display()' routine is * + * called. Thus, the dialog's window coords have to be divisible by * + * 24. The height of the dialog is computed based on how many objects * + * there are in it. * + * * + * 10 pixels are left between rows of objects, so the # of that type of * + * object can be displayed underneath the object. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Members ³ * + * ³ ³ * + * ³ ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * house house to display objects for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine uses HIDBUFF for data storage. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +//#define TEENSY_WEENSY +/* +** Dialog & button dimensions +*/ +enum { + D_DIALOG_W = 640, + D_DIALOG_X = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT6_H = 7, + D_MARGIN = 7, + +#ifdef TEENSY_WEENSY + D_PICTURE_W = 32, + D_PICTURE_H = 24, +#else + D_PICTURE_W = 64, + D_PICTURE_H = 48, +#endif + D_ROW_H = (D_PICTURE_H + 3), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_CX - 5 - D_OK_W, + D_OK_Y = 0, + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = 0, + +}; + +int MapEditClass::Team_Members(HousesType house) +{ + /* + ** Button enumerations: + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + ** (highest enum is the lowest layer). Each section of the map checks + ** the requested redraw level to see if it's supposed to draw; if it's + ** >= its level, it redraws. + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + RedrawType display; // requested redraw level + bool process; // loop while true + + /* + ** Dialog variables + */ + KeyNumType input; // user input + bool cancel = false; // true = user cancels + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team display variables + */ + const TechnoTypeClass **teamclass; // array of team classes + int *teamcount; // array of class counts + int numcols; // # units displayed horizontally + int numrows; // # units displayed vertically + + /* + ** Dialog dimensions. + */ + int dlg_y; + int dlg_h; // dialog height + int msg_y; // y-coord for object names + + int curclass = -1; // current index into 'teamclass'; can be invalid! + // (is based on current mouse position) + int numclasses; // current # classes in the team (limited to <=5) + int maxclasses; // max # classes available + int i,j; + + /* + ** Values for timing when mouse held down. + */ + int lheld = 0; + int rheld = 0; + long tdelay[3] = {5, 20, 0}; + int tindex = 0; + long heldtime; + + /* + ** Buttons. + */ + ControlClass * commands; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Set up the team data arrays (ObjectTypeClass pointers & count) + */ +#ifdef WIN32 + teamclass = (const TechnoTypeClass **)SysMemPage.Get_Buffer(); + teamcount = (int *)SysMemPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#else + teamclass = (const TechnoTypeClass **)HidPage.Get_Buffer(); + teamcount = (int *)HidPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#endif + + /* + ** Fill in the ObjectTypeClass array with all available object type ptrs, + ** checking to be sure this house can own the object + */ + i = 0; + for (InfantryType i_id = INFANTRY_FIRST; i_id < INFANTRY_COUNT; i_id++) { + teamclass[i] = &InfantryTypeClass::As_Reference(i_id); + i++; + } + + for (AircraftType a_id = AIRCRAFT_FIRST; a_id < AIRCRAFT_COUNT; a_id++) { + teamclass[i] = &AircraftTypeClass::As_Reference(a_id); + i++; + } + + for (UnitType u_id = UNIT_FIRST; u_id < UNIT_COUNT; u_id++) { + teamclass[i] = &UnitTypeClass::As_Reference(u_id); + i++; + } + + for (VesselType v_id = VESSEL_FIRST; v_id < VESSEL_COUNT; v_id++) { + teamclass[i] = &VesselTypeClass::As_Reference(v_id); + i++; + } + + /* + ** Save max # classes. + */ + maxclasses = i; + + /* + ** Fill in the 'count' array with data from the current team: + ** - For every class in the current team, find that class type in the + ** 'teamclass' array & set its count value + */ + for (j = 0; j < maxclasses; j++) { + teamcount[j] = 0; + } + + /* + ** Loop through all classes in the team. + */ + for (i = 0; i < CurTeam->ClassCount; i++) { + + /* + ** Find this class in our array. + */ + for (j = 0; j < maxclasses; j++) { + + /* + ** Set the count; detect a match between the team's class & the + ** 'teamclass' array entry by comparing the actual pointers; typeid + ** won't work because E1 & E2 are the same type class. + */ + if (CurTeam->Members[i].Class == teamclass[j]) { + teamcount[j] = CurTeam->Members[i].Quantity; + break; + } + } + } + numclasses = CurTeam->ClassCount; + + /* + ** Set up the dialog dimensions based on number of classes we have to draw + ** + ** Compute picture rows & cols. + */ + numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + numrows = (maxclasses + numcols - 1) / numcols; + + /* + ** Dialog's height = top margin + label + picture rows + margin + label + margin + btn + */ + dlg_h = 400; + dlg_y = 0; + msg_y = dlg_y+dlg_h - 26 - 15; + + okbtn.Y = dlg_y + dlg_h - D_MARGIN - D_OK_H - 15; + cancelbtn.Y = dlg_y + dlg_h - D_MARGIN - D_CANCEL_H - 15; + + /* + ** Draw to SeenPage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Make sure 'house' is valid. + */ +// if (house!=HOUSE_GOOD && house!=HOUSE_BAD && house != HOUSE_MULTI1 && +// house != HOUSE_MULTI2 && house != HOUSE_MULTI3 && house != HOUSE_MULTI4 ) { +// if (Scen.ScenPlayer == SCEN_PLAYER_MPLAYER) { +// house = HOUSE_MULTI1; +// } else { +// house = HOUSE_GOOD; +// } +// } + + /* + ** Create the list. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ** Display the constant background of this dialog. + */ + Dialog_Box(D_DIALOG_X, dlg_y, D_DIALOG_W, dlg_h); + Draw_Caption(TXT_TEAM_MEMBERS, D_DIALOG_X, dlg_y, D_DIALOG_W); + + /* + ** Draw the objects. + */ + for (i = 0; i < maxclasses; i++) { + + /* + ** Display the object along with any count value for it. + */ + Draw_Member(teamclass[i], i, teamcount[i], house); + } + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + + /* + ** Mouse buttons set or clear 'held' values + */ + case (KN_LMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + lheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case (KN_RMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + rheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + lheld = 0; + break; + + case ((int)KN_RMOUSE | (int)KN_RLSE_BIT): + rheld = 0; + break; + + /* + ** OK: save values & return. + */ + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + /* + ** Cancel: abort & return. + */ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + /* + ** Compute new 'curclass' based on mouse position. + */ + i = (Get_Mouse_X() - 32 - D_DIALOG_X) / D_PICTURE_W + + ((Get_Mouse_Y() - (dlg_y+8+11)) / D_ROW_H) * numcols; + + /* + ** If it's changed, update class label. + */ + if (i != curclass) { + + curclass = i; + + /* + ** Clear out the previously printed name of the item. + */ + Hide_Mouse(); + LogicPage->Fill_Rect(D_DIALOG_X + 32, msg_y, D_DIALOG_X + D_DIALOG_W - 64, msg_y + D_TXT6_H, BLACK); + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, scheme, TBLACK, TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + + /* + ** Force buttons to not be held. + */ + lheld = 0; + rheld = 0; + Show_Mouse(); + } + break; + } + + /* + ** Check for a 'held' mouse button; if it's down, and the correct + ** amount of time has gone by, increment/decrement the count for the + ** current class. + */ + if (lheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + heldtime = TickCount; + if (tindex) { + tindex--; + } + + /* + ** Detect addition of a new class. + */ + if (teamcount[curclass]==0) { + + /* + ** Don't allow more classes than we can handle. + */ + if (numclasses == TeamTypeClass::MAX_TEAM_CLASSCOUNT) { + continue; + } + numclasses++; + } + teamcount[curclass]++; + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + + } else { + + if (rheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + if (tindex) { + tindex--; + } + heldtime = TickCount; + + if (teamcount[curclass] > 0) { + teamcount[curclass]--; + + /* + ** Detect removal of a class. + */ + if (teamcount[curclass] == 0) { + numclasses--; + } + } + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + } + } + } + + /* + ** Copy data into team. + */ + if (!cancel) { + CurTeam->ClassCount = numclasses; + i = 0; // current team class index + for (j = 0; j < maxclasses; j++) { + if (teamcount[j] > 0) { + CurTeam->Members[i].Quantity = teamcount[j]; + CurTeam->Members[i].Class = teamclass[j]; + i++; + } + } + } + + /* + ** Redraw the display. + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) return(-1); + return(0); +} + + +/*********************************************************************************************** + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * * + * This routine will display the cameo image of the potential team member. In the corner, * + * it will show the current quantity of this member for the current team being edited. * + * * + * INPUT: ptr -- Pointer to the member object type. * + * * + * index -- The index into the team dialog box array of selectable objects. This is * + * used to determine the correct X and Y offsets to draw. * + * * + * quant -- The quantity number to display in the corner of the image. * + * * + * house -- The owner of this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1995 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house) +{ + int numcols = (D_DIALOG_W - 64) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int dlg_y = 0; + int x = D_DIALOG_X + 32 + col * D_PICTURE_W; + int y = dlg_y + 8 + 13 + row * D_ROW_H; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Change the window to this box. + */ + WindowList[WINDOW_EDITOR][WINDOWX] = x; + WindowList[WINDOW_EDITOR][WINDOWY] = y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, true); + ptr->Display(WinW/2, WinH>>1, WINDOW_EDITOR, house); + if (quant > 0) { + Fancy_Text_Print("%d", x+1, y+1, scheme, TBLACK, TPF_8POINT|TPF_DROPSHADOW, quant); +// Fancy_Text_Print("%d", x+1, y+D_PICTURE_H-8, scheme, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_DROPSHADOW, quant); + } + Show_Mouse(); +} + + +#endif diff --git a/REDALERT/MAPSEL.CPP b/REDALERT/MAPSEL.CPP new file mode 100644 index 000000000..55ffa60dc --- /dev/null +++ b/REDALERT/MAPSEL.CPP @@ -0,0 +1,399 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPSEL.CPP * + * * + * Programmer : Barry W. Green * + * * + * Start Date : April 17, 1995 * + * * + * Last Update : April 27, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Bit_It_In -- Pixel fade graphic copy. * + * Map_Selection -- Starts the whole process of selecting next map to go to * + * Print_Statistics -- Prints statistics on country selected * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void Cycle_Call_Back_Delay(int time, PaletteClass &pal); +extern int ControlQ; + +int Mouse_Over_Spot(int house, int scenario); +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot); +//VG for ant mission progression +const char* antmission[] = {NULL, "SCA01EA.INI", "SCA02EA.INI", "SCA03EA.INI", "SCA04EA.INI"}; + +struct point { + int x; + int y; +} const MapCoords[2][14][3] = { + { + {{185,123},{ -1, -1},{ -1, -1}}, + {{173,112},{ -1, -1},{ -1, -1}}, + {{196,100},{200,112},{ -1, -1}}, + {{175,113},{ -1, -1},{ -1, -1}}, + {{187, 91},{202, 93},{206,105}}, + {{207,161},{212,172},{ -1, -1}}, + {{172, 92},{ -1, -1},{ -1, -1}}, + {{132,119},{146,125},{ -1, -1}}, + {{199, 73},{205, 86},{ -1, -1}}, + {{236,114},{ -1, -1},{ -1, -1}}, + {{219, 64},{225, 76},{ -1, -1}}, + {{256, 69},{ -1, -1},{ -1, -1}}, + {{262, 77},{ -1, -1},{ -1, -1}}, + {{249, 97},{ -1, -1},{ -1, -1}} + }, +// Soviet coords + { + {{178,105},{ -1, -1},{ -1, -1}}, + {{163,101},{163,113},{ -1, -1}}, + {{160, 89},{ -1, -1},{ -1, -1}}, + {{142,101},{142,117},{ -1, -1}}, + {{212,163},{ -1, -1},{ -1, -1}}, + {{155,133},{171,144},{ -1, -1}}, + {{216,103},{ -1, -1},{ -1, -1}}, + {{132,145},{154,154},{ -1, -1}}, + {{122,117},{ -1, -1},{ -1, -1}}, + {{117,130},{ -1, -1},{ -1, -1}}, + {{ 99,107},{109,146},{ -1, -1}}, + {{134,125},{ -1, -1},{ -1, -1}}, + {{ 32,156},{ 46,171},{ -1, -1}}, + {{108, 97},{ -1, -1},{ -1, -1}} + } +}; + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/*********************************************************************************************** + * Map_Selection -- Starts the whole process of selecting next map to go to * + * * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 BWG : Created. * + *=============================================================================================*/ +extern int CopyType; +#ifndef WIN32 +extern short StreamLowImpact; +#endif +char const * Map_Selection(void) +{ + return NULL; +#if (0)//PG + static char scenarioname[_MAX_FNAME+_MAX_EXT]; + +#ifdef FIXIT_ANTS + if (AntsEnabled) { + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + return(scenarioname); + } + +#endif + char _filename[]="MSAA.WSA"; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); + + _filename[2] = house ? 'S' : 'A'; + _filename[3] = Scen.Scenario + 'A'; + PaletteClass mappalette; + + int scenario = Scen.Scenario; + int selection; + static CDTimerClass timer; + int start = 0; + int count = 0; + int delay = 0; + int xspot = 0; + int yspot = 0; + + void const * appear1 = MFCD::Retrieve("MAPWIPE2.AUD"); + void const * bleep11 = MFCD::Retrieve("BLEEP11.AUD"); + void const * country4 = MFCD::Retrieve("MAPWIPE5.AUD"); + void const * toney7 = MFCD::Retrieve("TONEY7.AUD"); + void const * bleep17 = MFCD::Retrieve("BLEEP17.AUD"); + + void const * scold1 = MFCD::Retrieve("TONEY4.AUD"); + void const * country1 = MFCD::Retrieve("TONEY10.AUD"); + +#ifdef WIN32 + GraphicBufferClass *pseudoseenbuff = new GraphicBufferClass(320, 200, (void*)NULL); +#endif + +// fixed oldvolume = Options.ScoreVolume; +// Options.Set_Score_Volume(fixed(4, 10)); + Theme.Queue_Song(THEME_MAP); + + void *anim = Open_Animation(_filename, NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), mappalette); + + Keyboard->Clear(); + SeenPage.Clear(); + mappalette.Set(FADE_PALETTE_FAST, Call_Back); + +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); + for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + HidPage.Clear(); + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + + + int frame = 1; + StreamLowImpact = true; +#ifdef WIN32 + Play_Sample(appear1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(appear1, 255, Options.Normalize_Volume(55)); +#endif + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + CopyType = 1; + Animate_Frame(anim, *pseudoseenbuff, frame++); + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + switch(frame) { + case 16: +#ifdef WIN32 + Play_Sample(bleep11, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep11, 255, Options.Normalize_Volume(55)); +#endif + break; + case 30: +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(55)); +#endif + break; + case 51: +#ifdef WIN32 + Play_Sample(toney7, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(toney7, 255, Options.Normalize_Volume(55)); +#endif + break; + case 61: +#ifdef WIN32 + Play_Sample(bleep17, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep17, 255, Options.Normalize_Volume(55)); +#endif + break; + } + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); + Show_Mouse(); + Keyboard->Clear(); + + bool done = 0; + MouseType shape = MOUSE_NORMAL; + while (!done) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; + } +#endif + Cycle_Call_Back_Delay(1, mappalette); + int choice = Mouse_Over_Spot(house, scenario); + if (choice == -1) { + shape = MOUSE_NORMAL; + } else { + shape = MOUSE_CAN_ATTACK; + } + + Set_Mouse(shape, start, count, delay, xspot, yspot); + if (timer == 0) { + frame++; + frame %= count; + timer = delay; + Set_Mouse_Cursor(xspot, yspot, Extract_Shape(MouseClass::MouseShapes, start + frame)); + } + if (Keyboard->Check()) { + if ((Keyboard->Get() & 0x10FF) == KN_LMOUSE) { + if (choice != -1) { + done = 1; + selection = choice; +#ifdef WIN32 + Play_Sample(country1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country1, 255, Options.Normalize_Volume(50)); +#endif + } else { +#ifdef WIN32 + Play_Sample(scold1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(scold1, 255, Options.Normalize_Volume(50)); +#endif + } + } + } + } + + Hide_Mouse(); + + /* + ** Restore the mouse to normal shape before leaving this routine. + */ + Set_Mouse(MOUSE_NORMAL, start, count, delay, xspot, yspot); + Set_Mouse_Cursor(xspot, yspot, Extract_Shape(MouseClass::MouseShapes, start)); + + Keyboard->Clear(); +// BlackPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// SeenPage.Clear(); + + Fancy_Text_Print(TXT_STAND_BY, 160 * RESFACTOR, 190 * RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Create the new scenario filename from the selection. The filename is + ** derived from the previous filename but it has the scenario number + ** incremented and the chosen variation set. + */ + + //V.G. added so Ant Missions would progress + if(Scen.ScenarioName[2] == 'A'){ + int antnum = Scen.Scenario++; + if(antnum > 4) antnum = 1; + strcpy(scenarioname, antmission[antnum]); + } + else{ + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + scenarioname[6] = 'A' + selection; + } + Theme.Fade_Out(); +// Options.Set_Score_Volume(oldvolume); + +// Scen.ScenVar = (ScenarioVarType)selection; +//Mono_Printf("Chose variant %d \n", selection); + return(scenarioname); +#endif +} + +int Mouse_Over_Spot(int house, int scenario) +{ + int retval = -1; + for (int selection = 0; selection < 3 && MapCoords[house][scenario][selection].x != -1; selection++) { + int mousex = Get_Mouse_X() / RESFACTOR; + int mousey = Get_Mouse_Y() / RESFACTOR; + if (mousex >= MapCoords[house][scenario][selection].x && + mousey >= MapCoords[house][scenario][selection].y && + mousex <= MapCoords[house][scenario][selection].x+11 && + mousey <= MapCoords[house][scenario][selection].y+9) { + + retval = selection; + break; + } + } + return(retval); +} +void Cycle_Call_Back_Delay(int time, PaletteClass &pal) +{ + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + + while(time--) { + /* + ** Process the fading white color. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + pal[254] = GamePalette[WHITE]; + pal[254].Adjust(val, BlackColor); + + pal.Set(); + } + Call_Back_Delay(1); + } +} + +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot) +{ + switch(shape) { + case MOUSE_NORMAL: + start = 0; + count = 1; + delay = 0; + xspot = 0; + yspot = 0; + break; + default: + start = 21; + count = 8; + delay = 4; + xspot = 14; + yspot = 11; + break; + } +} diff --git a/REDALERT/MCI.CPP b/REDALERT/MCI.CPP new file mode 100644 index 000000000..c743c9e04 --- /dev/null +++ b/REDALERT/MCI.CPP @@ -0,0 +1,362 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* FILE +* MCI.cpp +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include "mci.h" + +/**************************************************************************** +* +* NAME +* GetDeviceCount() +* +* DESCRIPTION +* +* INPUTS +* NONE +* +* RESULT +* Count - Number of MCI device entries +* +****************************************************************************/ + +unsigned int MCI::GetDeviceCount(void) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + unsigned int count; + + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)&count; + sysInfo.dwRetSize = sizeof(count); + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_QUANTITY, (DWORD)&sysInfo); + + if (rc) + return 0; + + return count; + } + + +/**************************************************************************** +* +* NAME +* GetDeviceName(entry, name) +* +* DESCRIPTION +* +* INPUTS +* Entry - Entry number to get name for. +* Name - On return; device entry name +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::GetDeviceName(unsigned int item, char* buffer) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + + // Get device name + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)buffer; + sysInfo.dwRetSize = 63; + sysInfo.dwNumber = item; + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_NAME, (DWORD)&sysInfo); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetProductName(MCIDEVICEID id, char* buffer) + { + MCIERROR rc; + MCI_INFO_PARMS info; + + // Get device product name + memset(&info, 0, sizeof(info)); + info.lpstrReturn = (LPSTR)buffer; + info.dwRetSize = 63; + + rc = mciSendCommand(id, MCI_INFO, MCI_WAIT | MCI_INFO_PRODUCT, + (DWORD)&info); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* OpenDevice(name) +* +* DESCRIPTION +* +* INPUTS +* Name - Device name to open +* +* RESULT +* DeviceID - ID of opened device, 0 if error. +* +****************************************************************************/ + +MCIDEVICEID MCI::OpenDevice(const char* name) + { + MCIERROR rc; + MCI_OPEN_PARMS open; + + memset(&open, 0, sizeof(open)); + open.lpstrDeviceType = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE, (DWORD)&open); + + if (rc) + return 0; + + return (open.wDeviceID); + } + + +void MCI::CloseDevice(MCIDEVICEID id) + { + MCI_GENERIC_PARMS close; + + close.dwCallback = (DWORD)NULL; + + if (id) + mciSendCommand(id, MCI_CLOSE, MCI_WAIT, (DWORD)&close); + } + + +/**************************************************************************** +* +* NAME +* GetDeviceDescription +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetDeviceDescription(const char* name, MCIDevice* caps) + { + MCIDEVICEID id; + unsigned long result; + + // Copy the name + strncpy(caps->name, name, 63); + + if ((id = OpenDevice(name)) == 0) + return false; + + // Get device product name + GetProductName(id, caps->description); + + // Get device type + if (GetCapability(id, MCI_GETDEVCAPS_DEVICE_TYPE, &result)) + caps->type = result; + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_EJECT, &result)) + caps->canEject = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_PLAY, &result)) + caps->canPlay = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_RECORD, &result)) + caps->canRecord = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_SAVE, &result)) + caps->canSave = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_COMPOUND_DEVICE, &result)) + caps->usesDevElem = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_AUDIO, &result)) + caps->hasAudio = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_VIDEO, &result)) + caps->hasVideo = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_USES_FILES, &result)) + caps->reqElemFile = ((result) ? true : false); + + CloseDevice(id); + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result) + { + MCIERROR rc; + MCI_GETDEVCAPS_PARMS devCaps; + + memset(&devCaps, 0, sizeof(devCaps)); + devCaps.dwItem = capItem; + rc = mciSendCommand(id, MCI_GETDEVCAPS, MCI_WAIT|MCI_GETDEVCAPS_ITEM, + (DWORD)&devCaps); + + if (rc) + return false; + + *result = devCaps.dwReturn; + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +const char* MCI::GetDeviceTypeName(unsigned long type) + { + static struct _DeviceType {unsigned long typeID; const char* typeName;} + _deviceTypeNames[] = + { + {MCI_DEVTYPE_ANIMATION, "Animation"}, + {MCI_DEVTYPE_CD_AUDIO, "CD Audio"}, + {MCI_DEVTYPE_DAT, "DAT"}, + {MCI_DEVTYPE_DIGITAL_VIDEO, "Digital Video"}, + {MCI_DEVTYPE_OTHER, "Other"}, + {MCI_DEVTYPE_OVERLAY, "Overlay"}, + {MCI_DEVTYPE_SCANNER, "Scanner"}, + {MCI_DEVTYPE_SEQUENCER, "MIDI Sequencer"}, + {MCI_DEVTYPE_VCR, "VCR"}, + {MCI_DEVTYPE_VIDEODISC, "VideoDisc"}, + {MCI_DEVTYPE_WAVEFORM_AUDIO, "Wave Audio"}, + {0, NULL}, + }; + + int i = 0; + + while (_deviceTypeNames[i].typeID != 0) + { + if (_deviceTypeNames[i].typeID == type) + return _deviceTypeNames[i].typeName; + + i++; + } + + return NULL; + } + + +/**************************************************************************** +* +* NAME +* MCIEnumerate(callack, context) +* +* DESCRIPTION +* +* INPUTS +* Callback - +* Context - +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::EnumerateDevices(MCIEnumCB* callback, void* context) + { + DWORD count; + DWORD i; + char name[64]; + MCIDevice device; + + // Get the number of devices + count = GetDeviceCount(); + + // Do for each device + for (i = 1; i <= count; i++) + { + GetDeviceName(i, name); + memset(&device, 0, sizeof(device)); + + if (GetDeviceDescription(name, &device)) + { + if (!callback(&device, context)) + break; + } + } + + return true; + } +#endif // MCIMPEG + diff --git a/REDALERT/MCI.H b/REDALERT/MCI.H new file mode 100644 index 000000000..c936b02cd --- /dev/null +++ b/REDALERT/MCI.H @@ -0,0 +1,105 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MCI_H_ +#define _MCI_H_ +/**************************************************************************** +* +* FILE +* MCI.H +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include "watcom.h" + +/* MCIDevice - MCI device capabilities and description + * + * name - Name used to open device. + * description - Product description + * type - Device type + * canEject - Can eject media flag + * canPlay - Can playback media + * canRecord - Can record media + * canSave - Can save media + * usesDevElem - Uses device element + * hasAudio - Media supports audio + * hasVideo - Media supports video + * reqElemFile - Requires element file + */ +typedef struct _MCIDevice + { + char name[64]; + char description[64]; + unsigned long type; + bool canEject; + bool canPlay; + bool canRecord; + bool canSave; + bool usesDevElem; + bool hasAudio; + bool hasVideo; + bool reqElemFile; + } MCIDevice; + +/* MCI enumeration callback definition */ +typedef bool (MCIEnumCB)(MCIDevice* desc, void*); + +class MCI + { + public: + // Open MCI device + MCIDEVICEID OpenDevice(const char* name); + void CloseDevice(MCIDEVICEID id); + + // Enumerate devices + bool EnumerateDevices(MCIEnumCB* callback, void* context); + + // Get number of MCI devices name in registry or [MCI] section + // of system.ini + unsigned int GetDeviceCount(void); + + // Get device name from registry or [MCI] section of system.ini + bool GetDeviceName(unsigned int item, char* buffer); + + // Get general device description + bool GetDeviceDescription(const char* name, MCIDevice* caps); + + // Get type name (IE: Digital Video) from type ID (IE: MCI_DEVTYPE_DIGITAL_VIDEO) + const char* GetDeviceTypeName(unsigned long type); + + // Get device product name + bool GetProductName(MCIDEVICEID id, char* buffer); + + // Get device capability + bool GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result); + }; + +#endif // MCIMPEG +#endif // _MCI_H_ diff --git a/REDALERT/MCIMOVIE.CPP b/REDALERT/MCIMOVIE.CPP new file mode 100644 index 000000000..f5f9c8a62 --- /dev/null +++ b/REDALERT/MCIMOVIE.CPP @@ -0,0 +1,349 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "function.h" + +#ifdef MCIMPEG +#include "mcimovie.h" +#include + +/**************************************************************************** +* +* NAME +* MCIMovie - Constructor +* +* DESCRIPTION +* +* INPUTS +* HInstance - Application instance handle +* +* RESULT +* NONE +* +****************************************************************************/ + +MCIMovie::MCIMovie(HWND mainWindow) + : mMainWindow(mainWindow), mMCIWindow(NULL), mName(NULL), mDeviceID(0) + { + mWidth = mHeight = 0; + } + + +/**************************************************************************** +* +* NAME +* ~MCIMovie - Destructor +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +MCIMovie::~MCIMovie() + { + // Stop any playing movie + Close(); + + // Free name + if (mName != NULL) + free(mName); + } + + +/**************************************************************************** +* +* NAME +* Open() +* +* DESCRIPTION +* Open the media file in preparation for playback. +* +* INPUTS +* NONE +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Open(const char* name, const char* device) + { + MCIERROR rc; + MCI_DGV_RECT_PARMS rectParm; + MCI_BREAK_PARMS breakParm; + + // Stop any currently playing movie + Close(); + + // Copy the movie name for our use + if (mName != NULL) + free(mName); + + mName = strdup(name); + + if (device == NULL) + device = "mpegvideo"; + + // Setup open parameters + memset((void*)&mOpenParm, 0, sizeof(mOpenParm)); + mOpenParm.dwCallback = NULL; + mOpenParm.lpstrDeviceType = device; + mOpenParm.lpstrElementName = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | + MCI_OPEN_ELEMENT, (DWORD)&mOpenParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + // Set device ID + mDeviceID = mOpenParm.wDeviceID; + + // Retrieve movie dimensions + rectParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_WHERE, MCI_WAIT | MCI_DGV_WHERE_SOURCE, + (DWORD)&rectParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + mWidth = rectParm.rc.right - rectParm.rc.left; + mHeight = rectParm.rc.bottom - rectParm.rc.top; + + // Set break key to escape + breakParm.dwCallback = NULL; + breakParm.nVirtKey = VK_ESCAPE; + breakParm.hwndBreak = mMainWindow; + + rc = mciSendCommand(mDeviceID, MCI_BREAK, MCI_WAIT | MCI_BREAK_HWND | + MCI_BREAK_KEY, (DWORD)&breakParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Play - Play the specified movie. +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Play(HWND window) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + // Provide window for playback + if (AttachWindow(window)) + { + // Size the video area + if (SizeDestination()) + { + // Start playing + memset((void*)&mPlayParm, 0, sizeof(mPlayParm)); + mPlayParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_PLAY, MCI_WAIT, (DWORD)&mPlayParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + Close(); + } + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Pause +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Pause(void) + { + if (mDeviceID == 0) + return false; + + if (mciSendCommand(mDeviceID, MCI_PAUSE, 0, (DWORD)NULL)) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* Stop +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Close(void) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + rc = mciSendCommand(mDeviceID, MCI_CLOSE, 0, (DWORD)NULL); + mDeviceID = 0; + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* SizeDestination +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::SizeDestination(void) + { + MCIERROR rc; + MCI_DGV_PUT_PARMS putParm; + RECT rect; + + if (mMCIWindow == NULL) + return false; + + GetClientRect(mMCIWindow, &rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect + 1); + + putParm.dwCallback = NULL; + putParm.rc.left = rect.left; + putParm.rc.top = rect.top; + putParm.rc.right = rect.right; + putParm.rc.bottom = rect.bottom; + + rc = mciSendCommand(mDeviceID, MCI_PUT, MCI_WAIT | MCI_DGV_RECT | + MCI_DGV_PUT_DESTINATION, (DWORD)&putParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* AttachWindow +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::AttachWindow(HWND window) + { + MCIERROR rc; + MCI_DGV_WINDOW_PARMS winParm; + + mMCIWindow = window; + + memset((void*)&winParm, 0, sizeof(winParm)); + winParm.dwCallback = NULL; + winParm.hWnd = window; + winParm.nCmdShow = SW_SHOW; + + rc = mciSendCommand(mDeviceID, MCI_WINDOW, MCI_WAIT| MCI_DGV_WINDOW_HWND | + MCI_DGV_WINDOW_STATE, (DWORD)&winParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + +#endif // MCIMPEG + diff --git a/REDALERT/MCIMOVIE.H b/REDALERT/MCIMOVIE.H new file mode 100644 index 000000000..71e8a1299 --- /dev/null +++ b/REDALERT/MCIMOVIE.H @@ -0,0 +1,66 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MCIMOVIE_H_ +#define _MCIMOVIE_H_ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include +#include "watcom.h" + +class MCIMovie + { + public: + MCIMovie(HWND mainWindow); + ~MCIMovie(); + + bool Open(const char* name, const char* device); + bool Play(HWND window); + bool Pause(void); + bool Close(void); + + LONG GetWidth(void) + {return ((mDeviceID) ? mWidth : 0);} + + LONG GetHeight(void) + {return ((mDeviceID) ? mHeight : 0);} + + protected: + HWND mMainWindow; // Application window + HWND mMCIWindow; // Callback window + char *mName; + UINT mDeviceID; + MCI_OPEN_PARMS mOpenParm; + MCI_PLAY_PARMS mPlayParm; + + // Video stream dimension + LONG mWidth, mHeight; + + private: + bool SizeDestination(void); + bool AttachWindow(HWND window); + + static int mRegistered; + static WNDCLASS mWndClass; + static HINSTANCE mInstance; + }; + +#endif // MCIMPEG +#endif // _MCIMOVIE_H_ diff --git a/REDALERT/MEMCHECK.H b/REDALERT/MEMCHECK.H new file mode 100644 index 000000000..5a1b5376d --- /dev/null +++ b/REDALERT/MEMCHECK.H @@ -0,0 +1,2602 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# + + MemCheck 3.0 Professional for DOS + + Copyright (c) 1990-1994, StratosWare Corporation. + All rights reserved. + + 1-800-WE-DEBUG + + Note to Developers: + -------------------- + This file should be #included AFTER any other #includes in + each source file which is to be memory checked, and BEFORE + any code that performs any operations on allocated pointers. + If it isn't, MemCheck will not pick up source file and line + information for intercepted functions. + + The MCCONFIG.EXE utility distributed with MemCheck 3.0 + will do this safely and quickly for you. + + Most specifically, this header file MUST NOT come before + any prototypes of routines that MemCheck intercepts, like + malloc(), free(), strcpy(), and so on. + + The Final Cut: + --------------- + To ENTIRELY remove MemCheck from your code, just #define + the constant "NOMEMCHECK", or equivalently, "NOMC". + + This header file will then automatically 'evaporate' all + MemCheck 3.0 calls. This is MUCH PREFERABLE to placing + #if-#endif's around the header file's inclusion, as in + + #ifdef DEBUG // DON'T DO THIS! + #include + #endif + + Using the "#ifdef DEBUG" as above doesn't allow the + MemCheck header file to evaporate the MemCheck 3.0 API + calls you may have placed in your code, like mc_startcheck() + and mc_endcheck(). You would then have to surround + each MemCheck API call with additional #if-#endif's. + + Modification History + + WHO WHEN WHAT + KWB 07/28/93 Gussy for beta + KWB 09/11/93 Add new placement overload, NEW() C++ stuff + KWB 11/08/93 Final QA/QC for initial release + KWB 12/02/93 LINT -save added + KWB 02/19/94 Fixed function inlining holes, added macros + for underscore func variants under MSC 7+ + KWB 07/08/94 Added defines for BC extender (_CC_POWERPACK_) + KWB 07/09/94 Added special case for STACKTOP, END under PowerPack + KWB 08/04/94 Added cdecl modifier to stklen, atopsp etc. decls + KWB 08/11/94 Associated _CC32_ with _PROTECTED_ in ccdefs section; + Changed method of determining compiler model, + e.g. _CC_MSC6_ from if == to if >= + Associated DOSX286 with _PROTECTED_ for Phar Lap + Added MC_SET_EXCEPTF, mc_set/get_exceptf() + KWB 08/17/94 Added new[] support filtering (_CPP_ANSI20_) + KWB 08/18/94 Changed _MCFARCALL to _MCFARGLUE + KWB 09/13/94 Added erf_printf as alias for erf_stdout + KWB 09/14/94 Added endf_summary + KWB 09/21/94 Added MCCRITF and mc_set_critf() & related + KWB 10/10/94 Added #if !defined(setmem) etc. for BC DPMI32 + KWB 10/11/94 Added _CC_BORLANDx_ comp def, 'x' = major ver + +*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + + +/* Avoid multiple inclusions */ +#ifndef _MEMCHECK_H_ +#define _MEMCHECK_H_ + +/* Prevent inclusion in Windows(tm) compilations */ +#if defined(_Windows) || defined(WINDOWS) || defined(_WINDOWS) +# if !defined (__DPMI16__) && !defined (__DPMI32__) && !defined (DOSX286) +# error DOS version of MemCheck header file #included! +# endif +#endif + +/* Shorthand equivalence, V2.0 backwards compatibility... */ +#if defined (NOMC) || defined (NOMEMCHK) +# define NOMEMCHECK +#endif + +/* C++ new interception -- see note later and + read the MemCheck 3.0 User's Manual, section + "Integration With C++." Uncommenting the next line + and following simple instructions allows seamless + transmission of the exact file and line location + of new's in your source code. +*/ +/* #define NEW_OVERLOADED */ + + +/* *** Backwards compatibility with V2.0 *** */ + +#define mc_isactive mc_is_active /* standardize naming... */ +#define mc_getmode mc_get_mode +#define mc_errorstatus mc_error_flags +#define mc_verify_memory mc_check_buffers + +#define MC_USEDISK MC_USING_DISK +#define MC_USEMEM MC_USING_MEMORY + + +/* *** Backwards compatibility with V2.1 *** */ + +#define MCLINENO MCSL /* "MemCheck Source Line" */ +#define MFUNC ERF /* error reporting function */ + +#define mc_set_msgfunc mc_set_erf /* "Message funcs" are now */ +#define mc_get_msgfunc mc_get_erf /* universally referred to as */ +#define mc_error_status mc_error_flags /* "error reporting functions"*/ + +/* Maintain source code compatibility with version 2.1. + Buffer registration is simplified to + just calling "mc_register", regardless of model. + Same with buffer checking, e.g. "mc_check_far" + and "mc_check_near" are rolled into "mc_check". +*/ +#define mc_register_near(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_register_far(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_unregister_near(p) mc_unregister((void _MCFAR *)(p)) +#define mc_unregister_far(p) mc_unregister((void _MCFAR *)(p)) +#define mc_check_near(p) mc_check((void _MCFAR *)(p)) +#define mc_check_far(p) mc_check((void _MCFAR *)(p)) + +/* Error Number Definitions + Returned by mc_endcheck() and mc_error_flags(). + In MemCheck 3.0, there's now a global error number much + like the "errno" variable in standard C. +*/ +#define MCE_NO_ERROR 0 /* it's debugging time & all well */ +#define MCE_NULL_SOURCE 1 /* null source ptr on copy */ +#define MCE_NULL_DEST 2 /* null dest ptr on copy */ +#define MCE_UNDERWRITE 3 /* allocated buf underwritten (front) */ +#define MCE_OVERWRITE 4 /* allocated buf overwritten (end) */ +#define MCE_LEAK 5 /* forgot to free alloc'd memory */ +#define MCE_LEAKAGE MCE_LEAK +#define MCE_UNFREED_MEMORY MCE_LEAK +#define MCE_NULL_PTR_ASSIGN 6 /* assigned data through null ptr */ +#define MCE_BAD_STACK_PTR 7 /* bad stack pointer */ +#define MCE_STACK_OVERWRITE 8 /* copy trashes stack frame */ +#define MCE_INTERNAL 9 /* internal error msg */ +#define MCE_OVERLAPPING_COPY 10 /* source overlaps dest on copy */ +#define MCE_INVALID_PTR 11 /* bad ptr on free, realloc */ +#define MCE_DEST_OVERWRITE 12 /* copy too big for dest buffer */ +#define MCE_OUT_OF_MEMORY 13 /* out of memory */ +#define MCE_OOM MCE_OUT_OF_MEMORY +#define MCE_GPF_PTR 14 /* ptr caused GPF */ + + +/* MemCheck Error Flags + + The MemCheck error flag is just an unsigned long + (specifically, a MCEFLAGS typedef) with "sticky" bits + corresponding to the above MCE_... error numbers. + You can use the error flags macro MC_EFLAG(e) to + test the global MC_Errno double flag word. +*/ +/* Returns a long word with the e-th bit set */ +#define MC_EFLAG(e) ( (e) ? ((MCEFLAGS)1 << (e-1)) : (MCEFLAGS)0) + +#define EFLAG_NULL_PTR MC_EFLAG(MCE_NULL_DEST) +#define EFLAG_BAD_PTR MC_EFLAG(MCE_UNALLOCED_PTR) +#define EFLAG_FRONT_MAGIC MC_EFLAG(MCE_UNDERWRITE) +#define EFLAG_BACK_MAGIC MC_EFLAG(MCE_OVERWRITE) +#define EFLAG_PTRS_NOT_FREED MC_EFLAG(MCE_LEAK) +#define EFLAG_TRACK_FILE 0 /*obsolete in 3.0+*/ +#define EFLAG_NULL_ASSIGN MCE_FLAG(MCE_NULL_PTR_ASSIGN) + +/* *** End Compatibility Section *** */ + + +/* *** MemCheck Compiler Constant Definitions *** */ + +/* + Compiler Defines + -------- ------- + Microsoft _CC_MSC_, _CC_MSC_COMPATIBLE_ + V8.x _CC_MSC8_ + V7.x _CC_MSC7_ + V6.x _CC_MSC6_ + V5.x _CC_MSC5_ + Borland* _CC_BORLAND_, _CC_BCC_ + V3.x _CC_BORLAND3_ + V4.x _CC_BORLAND4_ + PowerPack/16 _CC_POWERPACK_, _CC_POWERPACK16_ + PowerPack/32 _CC_POWERPACK_, _CC_POWERPACK32_, _CC32_ + WATCOM _CC_WATCOM_, _CC_MSC_COMPATIBLE_ + WAT/386 _CC_WATCOM32_, _CC32_ + + * Borland has no good way of determining compiler + version. __BORLANDC__ returns some truly funky + hex constant that "will increase in future versions." + + Define Meaning + ------ -------- + _CC32_ * 32-bit compile + _PROTECTED_ 16- or 32-bit protected mode compile + LCODE Defined if large code model + LDATA Defined if large data model + STACKTOP Highest stack address (top) + STACKEND Lowest stack address + STACKLEN Stack length (top - end) + _CPP_ANSI20_ Compiler supports C++ 2.0, e.g. new[] + + * If _CC32_ is defined, _PROTECTED_ is also defined +*/ + + +#ifndef _CCDEFS_H_ +#define _CCDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* NOTE: Microsoft C 5.x users have to "#define _MSC_VER 500" + at the top of this file. +*/ +#if defined(_MSC_VER) +/* + _MSC_VER Microsoft C version; currently defined as 700. + M_I86 _M_I86 Member of the I86 processor family. + M_I86mM _M_I86mM Memory model type: + = T Tiny + S Small (default) + C Compact model + M Medium model + L Large model + H Huge model + Identifiers defined by /AT, /AS, /AC, /AM, + /AL, and /AH, respectively. + _MSDOS MS-DOS operating system. + _QC Microsoft QuickC Compiler. + _WINDLL Windows protected-mode dynamic-link library + is selected with /GD. + _WINDOWS Windows protected-mode is selected with /GA, + /Gn, /GW, /Mq, or /GD. +*/ +# define _CC_MSC_ +# define _CC_MSC_COMPATIBLE_ + +# if (_MSC_VER >= 800) +# define _CC_MSC8_ +# elif (_MSC_VER >= 700) +# define _CC_MSC7_ +# elif (_MSC_VER >= 600) +# define _CC_MSC6_ +# elif (_MSC_VER >= 500) /* see note above */ +# define _CC_MSC5_ +# else +# error MemCheck.h: unrecognized version of Microsoft compiler! +# endif + +#elif defined(__BORLANDC__) +# define _CC_BORLAND_ /* Borland product */ +# define _CC_BCC_ /* Borland C compiler */ + + /* Major compiler rev */ +# if (__BORLANDC__ >= 0x0450) +# define _CC_BORLAND4_ +# elif (__BORLANDC__ >= 0x400) +# define _CC_BORLAND3_ +# else + /* not needed */ +# endif + + /* Borland 4.0 PowerPack BCC.EXE defines (-WX): + __DPMI16__ _Windows + + With -WXD, -WXDE: + __DLL__ __DPMI16__ _Windows + + Borland 4.0 PowerPack BCC21.EXE defines (-WX): + __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + + With -WXD: + __DLL__ __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + */ +# if defined(__DPMI16__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK16_ +# define _PROTECTED_ +# endif +# if defined(__DPMI32__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK32_ +# define _CC32_ /* flat model */ +# endif + +/* Turbo C++ */ +#elif defined(MCTCP) /* homebrew */ +# define _CC_BORLAND_ /* Borland product */ +# define _CC_TCP_ /* Turbo C++ */ + +#elif defined(__TURBOC__) +/* + __TURBOC__ Gives the current Turbo C version + number, a hexadecimal number. Version + 1.0 id 0x1000; version 1.2 is 0x0102, etc. + __TINY__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__, __HUGE__ + Model defintions + __MSDOS__ Signals that we're not yet in the + twenty-first century +*/ +# define _CC_BORLAND_ /* Borland C product */ +# define _CC_TCC_ /* Turbo C/C++ compiler */ + + +#elif defined(_INTELC32_) +/* + _INTELC32_ has the value 1. + _ARCHITECTURE_ is 386 if the nomod486 pragma is ineffect, + 486 otherwise. +*/ +# define _CC_INTEL_ /* Intel Code Builder */ +# define _CC_MSC_COMPATIBLE_ +# define _CC32_ /* flat model */ + +#elif defined(__WATCOMC__) +/* + __WATCOMC__ Used to determine if the WATCOM C + or C/386 compiler is compiling + __386__ identifies the target machine as an + Intel 80386 under the WATCOM C/386 compiler + MSDOS Signals that we're not yet in the + twenty-first century + __FLAT__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__ + Model defintions (flat is default) + Also id's MSC-compatible model defines + + 8.5 and later: + __WINDOWS__ Identifies 16-bit Windows ("zw" or "zW" opts) + __WINDOWS_386__ 32-bit Microsoft Windows "zW" opt + __NETWARE_386__ Novell Netware 386, defined by the + Novell/Watcom C + __OS2__ IBM OS/2-hosted version of Watcom +*/ +# define _CC_WATCOM_ /* Watcom C product */ +# define _CC_MSC_COMPATIBLE_ +# ifdef __386__ +# define _CC32_ /* flat model */ +# define _CC_WATCOM32_ +# endif + + +#elif defined(__STDC__) /* Standard ANSI C */ +# define _CC_ANSI_ +# define _CC32_ /* no segmentation via far/near */ +# define far +# define near +# define huge +# define cdecl + +/* Avoids parameter mismatches from _far, etc. */ +# define _far +# define _near +# define _huge +# define _cdecl + +#else /* UNSUPPORTED / UNRECOGNIZED COMPILER */ + +#error MemCheck 3.0: unrecognized compiler +/* + If you're using Microsoft C5.1, you must + define the constant _MSC_VER, e.g. + + #define _MSC_VER 500 + + Place this statement at the top of this + header file or in your project header file + BEFORE the MemCheck header file is included. + + Microsoft C 5.0 is NOT supported (it's non-ANSI). +*/ + +#endif /* compiler constant definitions */ + +/* END of _CC..._ name setups; ripple & alias */ + +/* Sheer utility & avoidance of !_CC32_ */ +#ifndef _CC32_ +# define _CC16_ +#endif + +/* 32-bit compilers are always protected mode */ +#ifdef _CC32_ +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* CC32 */ + +/* Phar Lap support */ +#ifdef DOSX286 +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* DOSX286 */ + +/* C++ 2.0 compliance: _CPP_ANSI20_ */ +#if defined(_CC_BORLAND_) + /* Only BC++ 4.x and later, but Borland has + seriously mangled version info (__BORLANDC__) */ +# if defined (__BCPLUSPLUS__) +# if (__BCPLUSPLUS__ > 0x0310) /* after 3.1 */ +# define _CPP_ANSI20_ +# endif +# elif defined(_CC_POWERPACK_) +# define _CPP_ANSI20_ +# endif +#elif defined (_CC_MSC_) + /* Current release through 8.x doesn't support new[] */ +#elif defined (_CC_WATCOM_) +# if (__WATCOMC__ >= 1000) /* 10.x */ +# define _CPP_ANSI20_ +# endif +#endif + + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Code and Data Size Constants *** + LCODE not used by this header file. + LDATA *is* used, however. + + May be commented out if these constants are already defined. +*/ + +/* #ifndef LCODE */ +#if defined(M_I86MM) || defined(M_I86LM) || defined(M_I86HM) +# define LCODE 1 +#elif defined(__MEDIUM__) || defined(__LARGE__) || defined(__HUGE__) +# define LCODE 1 +#endif +/* #endif */ + +#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__) +# define LDATA 1 +#elif defined(M_I86CM) || defined(M_I86LM) || defined(M_I86HM) +# define LDATA 1 +#endif + +/* Macro "aliases" */ + +#ifndef _LCODE +# ifdef LCODE +# define _LCODE LCODE +# endif +#endif + +#ifndef _LDATA +# ifdef LDATA +# define _LDATA LDATA +# endif +#endif + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Physical Stack Address *** */ + +#if defined(_CC_BORLAND_) /* -------------------------- */ + + /* + BORLAND RTL Notes: + ;---------------------------------------------------------------------- + ; In large data models, the stack is in its own segment. The stack + ; starts at SS:__stklen and grows down to SS:0. + ; + ; In small data models, the stack is in the DGROUP, and grows down + ; to meet the heap. The end of the heap is marked by ___brklvl. + (KWB: Note that the brklvl is adjusted upwards until it meets + _stklen...) + ;---------------------------------------------------------------------- + */ + +# define STACKSLOP 256 + extern unsigned cdecl _stklen; + +# if defined(_CC_POWERPACK_) +# define STACKTOP (mc_stacktop()) +# define STACKEND (mc_stackend()) +# else /* not P-Pack */ +# ifdef LDATA + /* Compact, Large, Huge Models ... + + The stack starts at SS:_stklen and + grows downward to SS:0 + */ +# define STACKTOP ((unsigned) _stklen) +# define STACKEND ((unsigned) 0 + STACKSLOP) + +# else + /* Small, Medium Models ... + + The stack starts at SS:0xFFFE and grows + downwards _stklen bytes. + */ +# define STACKTOP ((unsigned) 0xFFFE) +# define STACKEND (STACKTOP - _stklen + STACKSLOP) +# endif +# endif /* not PowerPack */ + +#elif defined (_CC_MSC_) /* ------------------------------- */ + + extern char cdecl end; /* end of stack */ + extern unsigned cdecl _atopsp; /* top of stack */ + +# define STACKTOP _atopsp +# define STACKSLOP 256 + +# ifdef LDATA + /* If in far data, stack could be in its own + seg. tho not usually. see /FARSTACK */ +# define STACKEND ((unsigned) ((unsigned long)&end) + STACKSLOP) +# else + /* If near data, must be in DS; use near ptr */ +# define STACKEND ((unsigned)&end + STACKSLOP) +# endif + +#elif defined (_CC_WATCOM_) /* ------------------------------- */ + + extern unsigned _STACKLOW; /* end of stack */ + extern unsigned _STACKTOP; /* top of stack */ + +# define STACKTOP (_STACKTOP) +# define STACKSLOP 256 + +# ifdef LDATA +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# else +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# endif + +#else /* Unknown compiler ---------------- */ + +#error Unknown compiler at __FILE__(__LINE__) + +#endif /* defining stack top, end */ + +/*******************************************************************/ +/*******************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _ccdefs already #included */ + +/* End CCDEFS */ + + +#if !defined (NULL) /* pull in stdio.h if not already */ +# include +#endif + +/* Backup... sometimes NULL defined in other headers */ +#if !defined (_IOFBF) /* pull in stdio.h if not already */ +# include +#endif + + +/* *** MemCheck Constants *** */ + +/* Standard from MemCheck 3.0 onwards. + Access major version and revision + via mc_version() and mc_revision() macros. +*/ +#define _MC_VERSION 0x0300 /* welcome 3.0 the powerful */ + +#define mc_version() ((int)((_MC_VERSION & 0xFF00) >> 8)) +#define mc_revision() ((int)(_MC_VERSION & 0x00FF)) + +#if defined (_CC32_) /* 32-bit Intel target */ +#define PRT_FP "0x%p" +#else +#define PRT_FP "%Fp" +#endif + +/* *** MCID Macro *** */ + +/* Allows later flexibility in assigning mapping... + Also makes MCIDs formulaic +*/ +#define _MCID(f) (MCID)(__paste(MCID_,f)) + +/* + MemCheck Function ID's (MCID's) + + These are the indices used to retrieve information + about specific runtime library calls. +*/ +/* --- MEMCHECK INTERNAL FUNCTIONS --- */ + +#define _MCID_FIRST_INTERNAL 0 /* index of first internal func */ + +#define MCID_mc_startcheck 0 +#define MCID_mc_endcheck 1 +#define MCID_mc_check_buffers 2 +#define MCID_mc_check 3 +#define MCID_mc_register 4 +#define MCID_mc_unregister 5 +#define MCID_mc_set_alignsize 6 +#define MCID_mc_set_checkbytes 7 +#define MCID_mc_nullcheck 8 +#define MCID_mc_breakpoint 9 +#define MCID_mc_debug 10 +#define MCID_mc_set_location 11 +#define MCID_mc_stack_trace 12 +#define MCID_mc_report 13 + +#define _MCID_LAST_INTERNAL 14 /* Set = to last internal ID */ + + +/* *************** STANDARD C ROUTINES ******************* */ + +#define _MCID_FIRST_ANSI (_MCID_LAST_INTERNAL+1) + +#define MCID_calloc (_MCID_FIRST_ANSI + 0) +#define MCID_free (_MCID_FIRST_ANSI + 1) +#define MCID_malloc (_MCID_FIRST_ANSI + 2) +#define MCID_memcpy (_MCID_FIRST_ANSI + 3) +#define MCID_memmove (_MCID_FIRST_ANSI + 4) +#define MCID_memset (_MCID_FIRST_ANSI + 5) +#define MCID_realloc (_MCID_FIRST_ANSI + 6) +#define MCID_sprintf (_MCID_FIRST_ANSI + 7) +#define MCID_strcat (_MCID_FIRST_ANSI + 8) +#define MCID_strcpy (_MCID_FIRST_ANSI + 9) +#define MCID_strncat (_MCID_FIRST_ANSI + 10) +#define MCID_strncpy (_MCID_FIRST_ANSI + 11) +#define MCID_vsprintf (_MCID_FIRST_ANSI + 12) + +#define _MCID_LAST_ANSI MCID_vsprintf /* define to last ANSI */ + + +/* *************** MICROSOFT C ******************* */ + +#if defined(_CC_MSC_COMPATIBLE_) + +#define _MCID_FIRST_MSC (_MCID_LAST_ANSI + 1) + +# define MCID__expand (_MCID_FIRST_MSC + 0) +# define MCID__ffree (_MCID_FIRST_MSC + 1) +# define MCID__fcalloc (_MCID_FIRST_MSC + 2) +# define MCID__fmalloc (_MCID_FIRST_MSC + 3) +# define MCID__fmsize (_MCID_FIRST_MSC + 4) +# define MCID__frealloc (_MCID_FIRST_MSC + 5) +# define MCID__fexpand (_MCID_FIRST_MSC + 6) +# define MCID__msize (_MCID_FIRST_MSC + 7) + +# define MCID__fmemmove (_MCID_FIRST_MSC + 8) +# define MCID__fmemcpy (_MCID_FIRST_MSC + 9) +# define MCID__fmemset (_MCID_FIRST_MSC + 10) +# define MCID__fmemccpy (_MCID_FIRST_MSC + 11) +# define MCID__fstrcat (_MCID_FIRST_MSC + 12) +# define MCID__fstrncat (_MCID_FIRST_MSC + 13) +# define MCID__fstrcpy (_MCID_FIRST_MSC + 14) +# define MCID__fstrncpy (_MCID_FIRST_MSC + 15) +# define MCID__fstrdup (_MCID_FIRST_MSC + 16) +# define MCID__fstrset (_MCID_FIRST_MSC + 17) +# define MCID__fstrnset (_MCID_FIRST_MSC + 18) + +# define MCID__nfree (_MCID_FIRST_MSC + 19) +# define MCID__nmalloc (_MCID_FIRST_MSC + 20) +# define MCID__ncalloc (_MCID_FIRST_MSC + 21) +# define MCID__nrealloc (_MCID_FIRST_MSC + 22) +# define MCID__nexpand (_MCID_FIRST_MSC + 23) +# define MCID__nmsize (_MCID_FIRST_MSC + 24) +# define MCID__nstrdup (_MCID_FIRST_MSC + 25) + +# define MCID__dos_setvect (_MCID_FIRST_MSC + 26) +# define MCID__getdcwd (_MCID_FIRST_MSC + 27) + +/* Here starts the Great ANSI Divide. + MSC6 and earlier have no underscores; + MSC7 and later *have* underscores to emphasize + departure from ANSI... +*/ +#if defined(_CC_MSC_) && !defined (MSC6) /* not MSC6-compatible */ + +# define MCID__getcwd (_MCID_FIRST_MSC + 28) +# define MCID__cgets (_MCID_FIRST_MSC + 29) +# define MCID__halloc (_MCID_FIRST_MSC + 30) +# define MCID__hfree (_MCID_FIRST_MSC + 31) +# define MCID__memccpy (_MCID_FIRST_MSC + 32) +# define MCID__strdup (_MCID_FIRST_MSC + 33) +# define MCID__strnset (_MCID_FIRST_MSC + 34) +# define MCID__strset (_MCID_FIRST_MSC + 35) +# define MCID__swab (_MCID_FIRST_MSC + 36) +# define MCID__tempnam (_MCID_FIRST_MSC + 37) + +#else /*** MSC6 and before; WATCOM ***/ + +/* No leading underscores */ + +# define MCID_getcwd (_MCID_FIRST_MSC + 28) +# define MCID_cgets (_MCID_FIRST_MSC + 29) +# define MCID_halloc (_MCID_FIRST_MSC + 30) +# define MCID_hfree (_MCID_FIRST_MSC + 31) +# define MCID_memccpy (_MCID_FIRST_MSC + 32) +# define MCID_strdup (_MCID_FIRST_MSC + 33) +# define MCID_strnset (_MCID_FIRST_MSC + 34) +# define MCID_strset (_MCID_FIRST_MSC + 35) +# define MCID_swab (_MCID_FIRST_MSC + 36) +# define MCID_tempnam (_MCID_FIRST_MSC + 37) + +#endif /* MSC6 ANSI calls */ + +# define MCID_new (_MCID_FIRST_MSC + 38) +# define MCID_delete (_MCID_FIRST_MSC + 39) +# define MCID__fullpath (_MCID_FIRST_MSC + 40) + +#endif /* Microsoft C-compatible calls */ + + +/* *************** BORLAND C ******************* */ + +#if defined (_CC_BORLAND_) + +#define _MCID_FIRST_BC (_MCID_LAST_ANSI + 1) + +# define MCID__fmemmove (_MCID_FIRST_BC + 0) +# define MCID__fmemcpy (_MCID_FIRST_BC + 1) +# define MCID__fmemset (_MCID_FIRST_BC + 2) +# define MCID__fmemccpy (_MCID_FIRST_BC + 3) +# define MCID__fstrcat (_MCID_FIRST_BC + 4) +# define MCID__fstrncat (_MCID_FIRST_BC + 5) +# define MCID__fstrcpy (_MCID_FIRST_BC + 6) +# define MCID__fstrncpy (_MCID_FIRST_BC + 7) +# define MCID__fstrdup (_MCID_FIRST_BC + 8) +# define MCID__fstrset (_MCID_FIRST_BC + 9) +# define MCID__fstrnset (_MCID_FIRST_BC + 10) + +# define MCID__dos_setvect (_MCID_FIRST_BC + 11) +# define MCID__getdcwd (_MCID_FIRST_BC + 12) + +# define MCID_getcwd (_MCID_FIRST_BC + 13) +# define MCID_cgets (_MCID_FIRST_BC + 14) +# define MCID_memccpy (_MCID_FIRST_BC + 15) +# define MCID_strdup (_MCID_FIRST_BC + 16) +# define MCID_strnset (_MCID_FIRST_BC + 17) +# define MCID_strset (_MCID_FIRST_BC + 18) +# define MCID_swab (_MCID_FIRST_BC + 19) +# define MCID_tempnam (_MCID_FIRST_BC + 20) + +# define MCID_farmalloc (_MCID_FIRST_BC + 21) +# define MCID_farrealloc (_MCID_FIRST_BC + 22) +# define MCID_farfree (_MCID_FIRST_BC + 23) +# define MCID_farcalloc (_MCID_FIRST_BC + 24) +# define MCID_movmem (_MCID_FIRST_BC + 25) +# define MCID_setmem (_MCID_FIRST_BC + 26) +# define MCID_setvect (_MCID_FIRST_BC + 27) +# define MCID_stpcpy (_MCID_FIRST_BC + 28) +# define MCID__fmovmem (_MCID_FIRST_BC + 29) +# define MCID__fsetmem (_MCID_FIRST_BC + 30) +# define MCID_new (_MCID_FIRST_BC + 31) +# define MCID_delete (_MCID_FIRST_BC + 32) +# define MCID__fullpath (_MCID_FIRST_BC + 33) + +#endif + + +/* + 'TOUCH' macro so high warning levels don't generate + 'unreferenced variable' warnings, especially when + making Production libraries... All MemCheck code + compiles without a whymper. +*/ +#if defined (_CC_WATCOM_) +# define TOUCH(var) var = var +#elif defined (_CC_BORLAND4_) +# define TOUCH(var) var = var +#else +# define TOUCH(var) if (var) +#endif + + +/* Default log name used by stock erf_logfile() and variants... */ +#define MEMCHECK_LOG "MEMCHECK.LOG" + +#define MAX_MEMORY 1000 /* 1000K is more than ever possible */ + +/* User-Modifiable Defaults */ + +#define D_CheckByteCt sizeof(int) /* word size is default */ +#define D_AlignSize sizeof(int) /* align returned memory ptrs */ + +/* Number of bytes to copy from null segment (to determine null + pointer assignments) +*/ +#define D_NULLCHECK_BYTES_FAR 16 /* at 0000:0000 (far NULL) */ +#define D_NULLCHECK_BYTES_NEAR 16 /* at DS:0000 (near NULL) */ +#define MAX_NULLCHECK_BYTES_FAR 1024 /* extent of irupt vect tbl */ +#define MAX_NULLCHECK_BYTES_NEAR 66 /* reserved in DS */ + +/* Unroll the double-negative */ +/* + Debugging code specific to MemCheck can be + conditionally compiled by placing it within + #if-#endif sections: (NOTE that this is NOT + required when just using API functions) + + #ifdef MEMCHECK + + void _MCCALLBACK trackf_special (int op, MEMRECP memrecp) + { + (... your custom callback code ...) + } + + #endif + + instead of the more arcane + + #ifndef NOMEMCHECK + : + #endif + + (Both approaches work equally well, however...) +*/ +#ifndef NOMEMCHECK /* MemCheck active */ +#define MEMCHECK +#endif + + +/* *** Calling Conventions *** */ + +#if !defined (_CC_ANSI_) +#define _MCAPI pascal /* MemCheck API functions */ +#define _FASTAPI pascal /* speed-critical functions */ +#define _MCCDECL cdecl /* MemCheck varargs API */ +#define _MCCALLBACK cdecl /* callback functions */ +#define _MCVAR cdecl /* MemCheck global variable */ +#else +#define _MCAPI /* MemCheck API functions */ +#define _FASTAPI /* speed-critical functions */ +#define _MCCDECL /* MemCheck varargs API */ +#define _MCCALLBACK /* callback functions */ +#define _MCVAR /* MemCheck global variable */ +#endif + +#if !defined(_CC_WATCOM_) +# define _RTL _MCCDECL /* RTL calling conv */ +#else +# define _RTL /* RTL calling conv */ + +/* WATCOM C++ does not currently (2/17/94) + accept "cdecl" as a modifier on variables... +*/ +# undef _MCVAR +# define _MCVAR +#endif /* _CC_WATCOM_ */ + +/* 32-bit compiler-independent stuff */ +#if !defined(_CC32_) +#define _MCFAR far +#define _MCFARCALL far +#define _MCNEAR near +#define _MCNEARCALL near +#define _MCHUGE huge +#else +#define _MCFAR +#define _MCFARCALL +#define _MCNEAR +#define _MCNEARCALL +#define _MCHUGE +#endif /* _CC32_ */ + +/* + MSC declares the following routines as "far"... + So does Borland. WATCOM does not; define glue. + + _fstrset _fstrnset _fstrcpy + _fstrncpy _fstrcat _fstrncat + _fmemset _fmemmove _fmemccpy +*/ +#if !defined(_CC_WATCOM_) +# define _MCFARGLUE far +#else +# define _MCFARGLUE +#endif + + +/* Microsoft C7 and later will not have + have a malloc_mc, only _fmalloc_mc; likewise + with free_mc. + The RTLMALLOC and RTLFREE macros are used + to refer to a generically present back-end malloc + and free. +*/ +#if defined (_CC_MSC_) +# if defined (LDATA) +# define RTLMALLOC RTL(_fmalloc) +# define RTLFREE RTL(_ffree) +# else +# define RTLMALLOC RTL(_nmalloc) +# define RTLFREE RTL(_nfree) +# endif +#else /* non-MSC */ +# define RTLMALLOC RTL(malloc) +# define RTLFREE RTL(free) +#endif + + +/* WATCOM defines its atexit funcs as a "register", + which causes a param type mismatch. + _ATEXITFUNC calling convention smooths this out. +*/ +#if defined (_CC_WATCOM_) +# define _ATEXITFUNC +#else +# define _ATEXITFUNC _MCCDECL +#endif + + +/* MemCheck Tracking Mode + + Returned by mc_get_mode(). + Indicates whether information on each allocation + is being stored in memory or on disk. +*/ +#define MC_USING_MEMORY 1 +#define MC_USING_DISK 2 + + +/* Min, max orders for each node in the B-tree */ + +#define BT_ORDER_MIN 5 +#define BT_ORDER_MAX 255 /* maximum tree order */ +#define BT_ORDER_DEFAULT 19 /* default tree order */ + +/* + Returned by mc_get_speed(). + Pass as params to mc_set_speed(). +*/ +#define MC_RUN_NORMAL 1 +#define MC_RUN_FAST 2 + +/* For mc_report(): + "Flags" field of the MEMREC structure + is set to REPORT_START or REPORT_END + to indicate begin and end of report. + + NOTE: If REPORT_START or REPORT_END conflicts + with defines in your project, just comment + them out and use the MC_... variants instead. +*/ +#define REPORT_START (MRFLAGS)0xFE +#define REPORT_END (MRFLAGS)0xFD + +#define MC_REPORT_START (MRFLAGS)0xFE /* alternates in case of conflict */ +#define MC_REPORT_END (MRFLAGS)0xFD + + +/* + Maximum number of breakpoints that + can be set via mc_breakpoint(). +*/ +#define MC_MAX_BREAKS 50 + + +/* "Optype" parameter on Tracking function callback. */ +#define TRACKF_ADD 1 /* record being added to tree */ +#define TRACKF_DEL 2 /* record being deleted from tree */ + +/* Used for the mcflags field of MEMREC to indicate + whether file & line are exact or approximate +*/ +#define MRFLAG_EXACT_LOC ( (MRFLAGS) 0x01) + +/* + Set if the values for a MEMREC are already converted + to "user" values. +*/ +#define MRFLAG_USER_SPECS ( (MRFLAGS) 0x02) +#define MRFLAG_NO_CHECKBYTES ( (MRFLAGS) 0x04) + +/* Alternate name */ +#define mc_message mc_debug + +/* + Parameter to mc_check_transfer() that + specifies that the given data transfer function cannot + have overlapping source & destination. + (MCID's are unsigned bytes.) +*/ +#define MCF_NO_OVERLAP ((unsigned)0x8000) +#define NO_OVERLAP(mcid) ((mcid) | MCF_NO_OVERLAP) + +/* Parameter to mc_check_transfer indicating that + the found memory record is not needed */ +#define NO_MEMREC ((MEMRECP)NULL) +#define NOMEMREC NO_MEMREC + +/* Parameter to mc_check_transfer indicating that + there is no source pointer operand associated + with the data transfer being checked: e.g. memset. */ +#define NO_SOURCE ((void _MCFAR *)0xFFFFFFFA) + + +/* *** TYPEDEFS *** */ + +typedef char * MCSF; /* MemCheck source file */ +typedef unsigned int MCSL; /* MemCheck source line */ +typedef unsigned char MCID; /* MemCheck function ID */ + +typedef unsigned long MCEFLAGS; /* MemCheck error flags */ +typedef void _MCFAR * MCPTR; /* type of ptr stored in tree */ +typedef unsigned char MRFLAGS; /* flags in MEMRECORD */ +typedef unsigned long MCFLAGS; /* MemCheck settings flags */ + +/* MemCheck Rocket allocator prototypes */ +typedef void _MCFAR * (_MCFAR *ROCKETALLOCF) (size_t); +typedef void (_MCFAR *ROCKETFREEF) (void _MCFAR *); + +#pragma pack(1) +/* + Memory Tracking Structure (MEMREC) + + This is the data structure for buffers being + tracked by MemCheck. +*/ +typedef struct MemRecord + { + MCPTR ptr; /* heap/registered ptr */ + MCID mcid; /* MemCheck function ID */ + MRFLAGS flags; /* internal MC flags */ + unsigned long allocno; /* cardinality of allocation */ + unsigned long size; /* size of block */ + MCSF file; /* source file */ + MCSL line; /* source line */ + + } MEMREC, _MCFAR *MEMRECP; + + +/* *** SETTINGS *** */ +/* These are values that describe the life of a MemCheck run. */ + +typedef struct MCSETTINGS { + /* + Bit Flag What + --- --------------- ----------------------------------- + 0 MCF_ACTIVE MemCheck active or off + 1 MCF_FAST_MODE Fast mode or normal + 2 MCF_PROTECTED_MODE Protected mode or real + 3 MCF_FAR_NULL_CHECK Check for far NULL ptr assigns * + 4 MCF_NEAR_NULL_CHECK Check for far NULL ptr assigns * + 5 MCF_STANDARD_STACK Standard stack frame * + 6 MCF_AUTOINIT Start up automatically * + 7 MCF_CLEAR_ON_FREE Clear buffers when freed + 8 MCF_DISK_ROCKET Use DiskRocket options + 9 MCF_IDX_IN_MEMORY Use memory only for Rocket indexes * + (only if DiskRocket linked) + 10 MCF_SOURCE_ONLY Intercept in source code only + + 11 - 31 Reserved + */ + MCFLAGS Flags; /* Main settings flags */ + + unsigned short MaxMem; /* Max mem for tree usage, in K */ + unsigned short NearNullBytes; /* bytes to check in near null */ + unsigned short FarNullBytes; /* " " " " far " */ + unsigned char CheckByteCt; /* check byte count */ + unsigned char AlignSize; /* alignment boundary size */ + char TrackingDir[36]; /* Rocket stores temp files here */ + + } MCSETTINGS, *MCSETTINGSP; + + +/* Random 32-bit .CFG file sentinel */ +#define MC_CFG_FILE_SENTINEL ( (unsigned long) 0x10F23BC4 ) + +typedef struct MCCfgInfo { + + unsigned long sentinel; /* always MC_CFG_FILE_SENTINEL */ + MCSETTINGS MemCheckSettings; /* saved by user */ + + } MCCFGINFO, *MCCFGINFOP; + + +#ifndef _CC32_ + +/* 16-bit exception stack */ +typedef struct { + + unsigned xRetIP; + unsigned xRetCS; + unsigned xErr; + unsigned xIP; + unsigned xCS; + unsigned xFlags; + unsigned xSP; + unsigned xSS; + + } MCEXCEPTINFO; + +#else + +/* 32-bit exception stack */ +typedef struct { + + unsigned long xRetEIP; + unsigned short xRsvd1; + unsigned short xRetCS; + unsigned long xErr; + unsigned long xEIP; + unsigned short xRsvd2; + unsigned short xCS; + unsigned long xFlags; + unsigned long xESP; + unsigned short xRsvd3; + unsigned short xSS; + + } MCEXCEPTINFO; + +#endif /* _CC32_ */ + +/* Values for MCCRITSECT.action */ +#define MCCS_ENTER_SECTION 0 +#define MCCS_LEAVE_SECTION 1 + +#define MCCS_ACTION(pMCCS) (pMCCS->nAction) +#define MCCS_ENTER(pMCCS) ((*(pMCCS->pLocked))++) /* inc flag */ +#define MCCS_LEAVE(pMCCS) ((*(pMCCS->pLocked))--) /* dec flag */ + +/* + Critical section object - ptr to this passed to crit sect callback + WARNING: access these fields ONLY via the MCCS_...() macros. + To do otherwise subjects you to changes in implementation + of the underlying coordination of critical section locking. +*/ +typedef struct { + int nAction; /* MCCS_ENTER/LEAVE_SECTION */ + int * pLocked; /* # times entered */ + unsigned long ulRsvd; /* internal use */ + } MCCRITSECT; + +#pragma pack() + + +#define MC_FEXIT ( (MCID) 0xFF ) + + +/* Error Reporting Function typedef */ +#ifndef _ERF_DEFINED +#define _ERF_DEFINED +typedef void (_MCCALLBACK *ERF) (char *); +#endif + + +/* *** Callback Functions *** */ + +/* Interception callback (on every interception) */ +typedef void (_MCCALLBACK * GLOBALF) (void); + +/* Called whenever nodes added to or deleted from MC database */ +typedef void (_MCCALLBACK *TRACKF) (int, MEMRECP); + +/* User-definable check function to add to transfer checking */ +typedef void (_MCCALLBACK * CHECKF) ( + int , /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); + +/* Funcs called at startup or shutdown */ +typedef void (_MCCALLBACK *STARTF) (void); +typedef void (_MCCALLBACK *ENDF) (void); + +/* Report function type passed to mc_report() */ +typedef void (_MCCALLBACK *REPORTF) (MEMRECP); + +/* Additional heap pointer verification (troubleshoot only) */ +typedef int (_MCCALLBACK *VERIFYF) (void _MCFAR *); + +typedef void (*MCVOIDFP) (void); + +/* Exception handler */ +typedef void (_MCCALLBACK _MCFAR *MCEXCEPTF) (void); + +/* Multitasking enter/exit critical section callback. + Specify as param to mc_set_critf() at beginning of program; + callback function will be called with MCCS_ENTER_SECTION + on entry to MemCheck, or MCCS_LEAVE_SECTION on exit. + + NOT TO BE CONFUSED WITH A MEMCHECK "GLOBAL" FUNCTION. + Global functions (GLOBALF's) are used to perform any + actions on the interception of a runtime function; + CRITF's must be used ONLY to serialize access to MemCheck. +*/ +typedef void (_MCCALLBACK * MCCRITF) (MCCRITSECT *); + + +/* Stack Frame Handler + + You can define your own function to + record, analyze, or inspect each stack frame + when mc_stack_trace() is called. + + You *cannot* modify ANY of the values passed + in, as the "const" typing indicates. If you need to + modify a value, make a copy. See the MemCheck 3.0 + documentation for more details on stack frame handlers. +*/ + +typedef void (_MCFAR _MCCDECL *_SSFRAMEHANDLER ) ( + short const , /* AX: near/far/error flag */ + unsigned short const , /* CX: near (default) rtn CS */ + unsigned short const , /* ES: far rtn CS */ + unsigned const , /* DI: rtn offset from stack */ + short const /* DX: frame count */ + ); + +/* Values for "flag" constant parameter to a + stack frame handler. +*/ +#define TRACE_BAD_FRAME 0x00 /* couldn't recognize frame */ +#define TRACE_FAR_CALL 0x01 /* frame represents a far call */ +#define TRACE_NEAR_CALL 0x02 /* " " " near " */ +#define TRACE_BAD_CHAIN 0x03 /* frame BP chewed up */ +#define TRACE_BEGIN 0x80 /* signals begin walk */ +#define TRACE_END 0x81 /* signals end of walk */ + + +/* MC Settings Structure, "flags" member: */ +#define MCF_ACTIVE (MCFLAGS)(0x01) +#define MCF_FAST_MODE (MCFLAGS)(0x02) +#define MCF_PROTECTED_MODE (MCFLAGS)(0x04) +#define MCF_FAR_NULL_CHECK (MCFLAGS)(0x08) +#define MCF_NEAR_NULL_CHECK (MCFLAGS)(0x10) +#define MCF_STANDARD_STACK (MCFLAGS)(0x20) +#define MCF_AUTOINIT (MCFLAGS)(0x40) +#define MCF_CLEAR_ON_FREE (MCFLAGS)(0x80) +#define MCF_DISK_ROCKET (MCFLAGS)(0x100) +#define MCF_IDX_IN_MEMORY (MCFLAGS)(0x200) +#define MCF_SOURCE_ONLY (MCFLAGS)(0x400) + + +/* *** Conditional Compilation *** */ + +/* -------------------------------------------------------------- + If MEMCHECK is not being `compiled out' (via definition + of the constant NOMEMCHECK), include this section... +-------------------------------------------------------------- */ + +#if !defined(MEMCHECK) + +/* Include Section for `MemCheck Not Active' */ + +/* ***************************** + MemCheck Not Active Section + ***************************** + + This section completely removes or + "evaporates" all MemCheck function references + from your projects when you compile with + NOMEMCHECK #defined. + + There's no need to remove any MemCheck + headers or statements from your code + to produce a full production version + of your application. + + o + ooo + ooooooo + ooooooooo + ooooooooooo + ooo + ooo + ooo + ooo + */ + +#ifndef MEMCHECK_MODULE + +/* Evaporate all MemCheck 3.0 API + statements to do nothing, safely... */ + +# define mc_alloc_count() 0L +# define mc_blocks_allocated() 0L +# define mc_blocks_freed() 0L +# define mc_breakpoint(fi) 0 +# define mc_bytes_allocated() 0L +# define mc_bytes_freed() 0L +# define mc_check(p) 0 +# define mc_check_buffers() 0 +# define mc_cur_file() "No file" +# define mc_cur_line() 0 +# define mc_debug(s) +# define mc_debugf(_args) +# define mc_debug_on() +# define mc_debug_off() +# define mc_endcheck() (MCEFLAGS)0 +# define mc_errno() MCE_NO_ERROR +# define mc_error_flags() (MCEFLAGS)0 +# define mc_error_text(e) "MemCheck not active" +# define mc_except_text(e) "MemCheck not active" +# define mc_file() "No file" +# define mc_find_buffer(p,mr) 0 +# define mc_func() (MCID)0 +# define mc_func_name(mcid) ("") +# define mc_get_erf() (ERF)NULL +# define mc_get_mode() 0 +# define mc_get_speed() 0 +# define mc_in_source() 0 +# define mc_is_active() 0 +# define mc_line() 0 +# define mc_load_debugger() +# define mc_location_text() "MemCheck not active" +# define mc_memory_leaked() 0L +# define mc_memrec() (MEMRECP)NULL +# define mc_memrec_text() "MemCheck not active" +# define mc_msg_continued() 0 +# define mc_nullcheck() 0 +# define mc_null_snapshot() +# define mc_register(p,s) +# define mc_report(_f) +# define mc_set_alignsize(_s) +# define mc_set_breakfile(_f) +# define mc_set_checkbytes(_cb) +# define mc_set_checkf(_f) (CHECKF)NULL +# define mc_set_critf(_f) +# define mc_set_endf(erf) (ENDF)NULL +# define mc_set_erf(erf) (ERF)NULL +# define mc_set_globalf(_f) (GLOBALF)NULL +# define mc_set_globalexitf(_f) (GLOBALF)NULL +# define mc_set_speed(_s) +# define mc_set_location() +# define mc_set_trackf(_f) (TRACKF)NULL +# define mc_set_tracefile(_f) +# define mc_set_tree_order(_q) +# define mc_set_track_dir(_dir) +# define mc_source_ptr() (MCPTR)NULL +# define mc_stack_trace(_memo) 0 +# define mc_startcheck(_erf) +# define mc_unregister(p) +# define mc_set_exceptf(f) +# define mc_get_exceptf() ((MCEXCEPTF)NULL) + +/* *** Stock error reporting functions *** */ +# define erf_default(_msg) +# define erf_standard(_msg) +# define erf_logfile(_msg) +# define erf_log_only(_msg) +# define erf_trace(_msg) + +/* Internal Helpers */ +# define _direct_output(_s) +# define _mcsl(_f,_l) +# define _mcsl_delete(_f,_l) +# define _mcsl_new(_f,_l) +# define _mcslx(_f,_l,_s) +# define _mc_set_delflag() +# define _mc_set_location(_f,_l) +# define _mc_set_newflag() + +/* Link-time compileouts */ +# define MC_SET_AUTOINIT(_toggle) +# define MC_SET_CHECK_FREQ(_freq) +# define MC_SET_CHECKF(_f) +# define MC_SET_CRITF(_f) +# define MC_SET_ENDF(_f) +# define MC_SET_ENV_VAR(_envvarname) +# define MC_SET_ERF(_f) +# define MC_SET_GLOBALF(_f) +# define MC_SET_GLOBALEXITF(_f) +# define MC_SET_LOGFILE(_logfilename) +# define MC_SET_PANIC_BUFFERS(_q) +# define MC_SET_SSFH(_f) +# define MC_SET_STARTF(_f) +# define MC_SET_TRACKF(_f) +# define MC_SET_VERIFYF(_f) + +/* Back-end direct */ +#define RTL(_f) _f + +/* *** C++ *** */ +#ifdef __cplusplus + +#define NEW(_object) new _object +#define DELETE(_object) delete _object +#define DELETE_ARR(_arr) delete[] _arr + +#define cpp_malloc(_s) malloc(_s) +#define cpp_calloc(_n,_s) calloc(_n,_s) +#define cpp_free(_p) free(_p) + +/* Borland C++ */ +#define cpp_farmalloc(_s) farmalloc(_s) +#define cpp_farfree(_fp) farfree(_fp) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) _fmalloc(_s) +#define cpp__ffree(_fp) _ffree(_fp) + +#endif /* C++ */ + +#endif /* not MEMCHECK_MODULE */ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#else /* MEMCHECK is defined */ + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#pragma message ("MemCheck V3.0") + +/* + ************************* + MemCheck Active Section + ************************* + + The rest of this header file deals with + MemCheck's being compiled into an application. + + */ + +/* Specify that vars and funcs are straight C.. */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* *** ANSI Location Defines *** */ + +#define _MC_NO_FILE ((MCSF) 0) /* just in case... */ +#define _MC_NO_LINE ((MCSL) 0) + + +/* Allow for the possibility of _MCSF_ being + defined to reference a single, static module + filename. This prevents a multiplicity of + static filenames getting added to the DGROUP, e.g. + + static char *_thisfile = __FILE__; + #define _MCSF_ ((MCSF)thisfile) + #include + + This is only needed under MSC pre-VC++. + Borland has "-d" Merge duplicate strings. + VC++ has "/Gf" Elim duplicate strings. +*/ +#if !defined (_MCSF_) +# ifdef __FILE__ +# define _MCSF_ (MCSF)__FILE__ +# else +# define _MCSF_ _MC_NO_FILE +# endif +#endif + +#ifdef __LINE__ +# define _MCSL_ (MCSL)__LINE__ +#else +# define _MCSL_ _MC_NO_LINE +#endif + + +/* *** Standard ANSI C Includes *** + + For va_list function call args + Inclusion of this header won't change + the behavior of host code. +*/ +#if !defined (va_start) /* avoid multiple inclusion... */ +# include +#endif + + +/* *** Compiler-specific includes *** */ + +/*lint -save -e537 Repeated include files (if necessary) */ +#if defined(_CC_MSC_COMPATIBLE_) + +# if !defined (_INC_MALLOC) /* C7.x and later optimization */ +# include +# endif + +# if !defined (_INC_STRING) /* C7.x and later optimization */ +# include +# endif + +#elif defined(_CC_BORLAND_) + +# if !defined (__ALLOC_H) +# include +# endif + +/* String functions must be proto'd before pragmas */ +# if !defined (__STRING_H) +# include +# endif + +#endif /* Compiler-specific includes */ +/*lint -restore */ + +#if defined (_CC_POWERPACK32_) +extern void cdecl mcinitfp_startcheck (void); +extern void cdecl mcexitfp_endcheck (void); +#pragma startup mcinitfp_startcheck 16 +#pragma exit mcexitfp_endcheck 16 +#endif + +/***************************************/ +/* *** MemCheck 3.0 API Prototypes *** */ +/***************************************/ + +/* Internal helper macro - proto shorthand */ +#define _LOCP MCSF,MCSL + +extern unsigned long _MCAPI mc_alloc_count (void); +extern unsigned long _MCAPI mc_blocks_allocated (void); +extern unsigned long _MCAPI mc_blocks_freed (void); +extern unsigned long _MCAPI mc_bytes_allocated (void); +extern unsigned long _MCAPI mc_bytes_freed (void); +extern int _MCAPI mc_check (void _MCFAR *); +extern int _MCAPI mc_check_buffers (void); +extern MCSF _MCAPI mc_cur_file (void); +extern MCSL _MCAPI mc_cur_line (void); +extern void _MCCDECL mc_debugv (const char *, ...); +extern void _MCAPI mc_debug (const char *); +extern MCEFLAGS _MCAPI mc_endcheck (void); +extern MCEFLAGS _MCAPI mc_error_flags (void); +extern char * _MCAPI mc_error_text (int); +extern int _MCAPI mc_errno (void); +extern char * _MCAPI mc_except_text (unsigned); +extern MCSF _MCAPI mc_file (void); +extern int _MCAPI mc_find_buffer(void _MCFAR *realptr,MEMRECP memrecp); +extern MCID _MCAPI mc_func (void); +extern char * _MCAPI mc_func_name(MCID); +extern ERF _MCAPI mc_get_erf (void); +extern MCEXCEPTF _MCAPI mc_get_exceptf (void); +extern int _MCAPI mc_get_mode (void); +extern int _MCAPI mc_get_speed (void); +extern char * _MCAPI mc_get_tracefile (void); +extern int _MCAPI mc_in_source (void); +extern int _MCAPI mc_is_active (void); +extern MCSL _MCAPI mc_line (void); +extern char * _MCAPI mc_location_text (void); +#define mc_load_debugger() _asm int 3 +extern unsigned long _MCAPI mc_memory_leaked (void); +extern char * _MCAPI mc_memrec_text (MEMRECP); +extern MEMRECP _MCAPI mc_memrec (void); +extern int _MCAPI mc_msg_continued (void); +extern int _MCAPI mc_nullcheck (void); +extern void _MCAPI mc_null_snapshot (void); +extern void _MCAPI mc_register (void _MCFAR *, unsigned long); +extern void _MCAPI mc_report (REPORTF); +extern void _MCAPI mc_set_alignsize (unsigned int); +extern void _MCAPI mc_set_breakfile (char *); +extern void _MCAPI mc_set_checkbytes (unsigned int); +extern CHECKF _MCAPI mc_set_checkf (CHECKF); +extern void _MCAPI mc_set_critf (MCCRITF); +extern ENDF _MCAPI mc_set_endf (ENDF); +extern ERF _MCAPI mc_set_erf (ERF); +extern MCEXCEPTF _MCAPI mc_set_exceptf (MCEXCEPTF); +extern GLOBALF _MCAPI mc_set_globalf (GLOBALF); +extern GLOBALF _MCAPI mc_set_globalexitf (GLOBALF); +#define mc_set_location() _mc_set_location(_MCSF_,_MCSL_) +extern MCPTR _MCAPI mc_source_ptr (void); +extern void _MCAPI mc_set_speed (int); +extern void _MCAPI mc_set_tracefile (char *); +extern void _MCAPI mc_set_track_dir (char *); +extern TRACKF _MCAPI mc_set_trackf (TRACKF); +extern void _MCAPI mc_set_tree_order (int); +extern int _MCAPI mc_stack_trace (char *); +extern void _MCAPI mc_startcheck (_LOCP, ERF); +extern void _ATEXITFUNC mc_endcheck_at_exit (void); +extern void _MCAPI mc_unregister (void _MCFAR *); + +/* Debugging versions of the MemCheck library only */ +extern void _MCAPI mc_debug_on (void); +extern void _MCAPI mc_debug_off (void); +extern int _MCCALLBACK mc_search_heap (void _MCFAR *); + +/* *** INTERNAL API HELPERS *** */ +extern void _MCAPI _mc_set_location (_LOCP); +extern void _FASTAPI _mcsl (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcslx (MCSF,MCSL,size_t); /* location run-ahead */ +extern void _FASTAPI _mcsl_new (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcsl_delete (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mc_set_newflag (void); /* new's a'comin' */ +extern void _FASTAPI _mc_set_delflag (void); /* delete's a'comin' */ + +/* Misc - uses INT 9 to output directly to screen */ +#if !defined (_CC_WATCOM32_) +extern void _MCCDECL _MCFAR _direct_output (char _MCFAR *); +#else +#define _direct_output(s) printf ("%s\n", s) /* ALERT */ +#endif + +/* + mc_breakpoint() is now a MemCheck Tracking function (TRACKF). + Tracking functions get called every time + MemCheck adds or deletes from its database. +*/ +#define mc_breakpoint(_f) \ + mc_set_trackf( (mc_set_breakfile (_f), trackf_breakpoint) ) +#define mc_breakpoint_trace(_f) \ + mc_set_trackf( (mc_set_tracefile (_f), trackf_breakpoint_trace) ) + + +/* *** Advanced-user API extenders *** */ + +/* extern int _MCAPI mc_find_buffer(void _MCFAR *, MEMRECP); */ +extern int _MCAPI mc_check_transfer( + void _MCFAR *, + void _MCFAR *, + unsigned long, + unsigned, + unsigned, + MEMRECP); + +/* mc_get_settings + + Write your own "get settings" routine + to override the one shipped with MemCheck. + You can hard-wire any settings you like, e.g. + always ON for versions of your app shipped to + testers/QA stations, etc. +*/ +extern void _MCCALLBACK mc_get_settings (MCSETTINGS *); + + +/* *** Callbacks / Functionality Extenders *** + + Function Type Called... + -------------- ------------------------------ + Error reporting To handle each MemCheck error message + Global Interception On each MemCheck interception + Checking On every data transfer check + Tracking On every allocation/deallocation + Start On mc_startcheck or AutoInit + End At mc_endcheck or MemCheck shutdown + + Refer to your MemCheck 3.0 manual for further details. + + *** STOCK FUNCTIONS *** + These functions are available in the MemCheck + libraries as "ready-made" for your programming + pleasure in the categories above. +*/ + +/* *** Stock error reporting functions *** */ + +extern void _MCCALLBACK erf_default (char *); +extern void _MCCALLBACK erf_standard (char *); +extern void _MCCALLBACK erf_logfile (char *); +extern void _MCCALLBACK erf_log_only (char *); +extern void _MCCALLBACK erf_trace (char *); +extern void _MCCALLBACK erf_trace_all (char *); +extern void _MCCALLBACK erf_trace_obj (char *); +extern void _MCCALLBACK erf_stdout (char *); +extern void _MCCALLBACK erf_stderr (char *); +extern void _MCCALLBACK erf_find_leaks (char *); + +#define erf_printf erf_stdout /* alias*/ + +/* *** Stock Tracking Functions *** */ + +extern void _MCCALLBACK trackf_default (int, MEMRECP); +extern void _MCCALLBACK trackf_all (int, MEMRECP); +extern void _MCCALLBACK trackf_all_2 (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint_trace (int, MEMRECP); +extern void _MCCALLBACK trackf_big_alloc (int, MEMRECP); + +/* *** Stock End Functions *** */ + +extern void _MCCALLBACK endf_default (void); /* does nothing */ +extern void _MCCALLBACK endf_info (void); /* write run info to log */ +extern void _MCCALLBACK endf_alert (void); /* warn if run errs */ +extern void _MCCALLBACK endf_summary (void); /* warn if run errs */ + +/* *** Stock Start functions *** */ + +extern void _MCCALLBACK startf_default (void); /* does nothing */ +extern void _MCCALLBACK startf_info (void); /* write options to log */ + +/* *** Stock Check Functions *** */ + +extern void _MCCALLBACK checkf_default (int,void _MCFAR *,long); +extern void _MCCALLBACK checkf_dataseg ( + int, /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); +extern void _MCCALLBACK checkf_verify_heap (int,void _MCFAR *,long); + +/* *** Stock Global Interception Functions *** */ + +extern void _MCCALLBACK globalf_default (void); /* does nothing */ +extern void _MCCALLBACK globalf_supercheck (void); +extern void _MCCALLBACK globalf_check_buffers (void); +extern void _MCCALLBACK globalf_heapcheck (void); + +/* *** Stock Report Functions *** */ +extern void _MCCALLBACK reportf_default (MEMRECP); + +/* *** Stock Exception Handlers *** */ +extern void _MCCALLBACK _MCFAR exceptf_default (void); + +/* *** Stock Stack Frame Handlers *** */ +extern void _MCFAR _MCCDECL ssfh_info ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_fast ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + /* int const _flag, */ + +extern void _MCFAR _MCCDECL ssfh_standard ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_debug ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +/* */ +extern unsigned int _MCAPI mc_stacktop (void); /* high address */ +extern unsigned int _MCAPI mc_stackend (void); /* low address */ + + +/* Function external variables. + + These are used effectively with MemCheck 3.0's AutoInit + setting. Under AutoInit, MemCheck fires itself up automatically + on its first interception. Under these circumstances, + there's no chance to have changed any defaults (like the + ERF or error reporting function). These variables provide + a link-level method of setting these functions: + + #include + : + // Sets custom erf at link-time + MC_SET_ERF (erf_custom_startup); + : +*/ +/* *** GLOBALS *** */ + +extern ERF _MCVAR MC_ERF; /* error reporting func ptr */ +extern CHECKF _MCVAR MC_CheckF; /* transfer check func */ +extern MCCRITF _MCVAR MC_CritF; /* crit section enter/exit */ +extern GLOBALF _MCVAR MC_GlobalF; /* global interception callback */ +extern GLOBALF _MCVAR MC_GlobalExitF; /* called on exit interception */ +extern TRACKF _MCVAR MC_TrackF; /* alloc/dealloc callback */ +extern STARTF _MCVAR MC_StartF; /* startup callback */ +extern ENDF _MCVAR MC_EndF; /* shutdown callback */ + +extern VERIFYF _MCVAR MC_VerifyF; /* troubleshooting */ + +extern char * _MCVAR MC_LogFile; /* log file name used */ +extern char _MCVAR MC_UserAutoInit; +extern int _MCVAR MC_CheckFreq; /* for globalf_supercheck() et al */ +extern char * _MCVAR MC_EnvVar; /* Env var to detect 'active' */ +extern unsigned short _MCVAR MC_DataSeg; /* DS value */ + +extern int _MCVAR MC_MaxTraceDepth; +extern char * _MCVAR MCST_Desc; /* trace descrip to mc_..trc() */ + +extern MCSETTINGS _MCVAR MC_DefaultSettings; /* default settings */ +extern MCSETTINGS _MCVAR MC_Settings; /* real settings-- + USE WITH CARE!!! */ + +extern MCVOIDFP _MCVAR MC_PMMap1; /* p-mode func in map seg 1 */ + +/* Protected mode exception handling */ +extern unsigned char _MCVAR MC_ExceptList[]; /* exceptions to handle */ +extern MCEXCEPTINFO _MCVAR MC_ExceptInfo; /* in exception */ +extern MCEXCEPTF _MCVAR MC_ExceptF; /* installed hdler */ + +/* Rocket Guidance Systems */ +extern ROCKETALLOCF _MCVAR MC_RocketAllocF; +extern ROCKETFREEF _MCVAR MC_RocketFreeF; +extern unsigned char _MCVAR MC_PanicBufCount; /* anti-tree failure */ + +/* This char is used to fill freed buffers + if the "ClearOnFree" option in effect. + Default buffer clear char is 0. +*/ +extern unsigned char _MCVAR MC_FreedBufferFillChar; + +/* Link-time defaults. + + These macros are "covers" to insulate you, the developer, + from the underlying implementation, as well as to provide + such bennies as compiling clean out of your code when + NOMEMCHECK or NOMC is defined. + + Use instead of accessing vars directly! + + To use, place the following in ONE MODULE e.g. your main module + (any *one* module will work fine) after the MemCheck + header file has been included: + + #include + MC_SET_...(params); + + For example, to change the default log file that MemCheck + uses at runtime to C:\MYDEV\MYPROG.LOG: + + #include + MC_SET_LOGFILE ("C:\\MYDEV\\MPROG.LOG"); + + Most of these macros have runtime function equivalents, + such as mc_set_erf() for MC_SET_ERF(), etc. Notable + exceptions to this are the following values that + must generally have link-time initializations: + + MC_SET_LOGFILE() + MC_SET_AUTOINIT() + MC_SET_STARTF() +*/ +#define MC_SET_AUTOINIT(_toggle) \ + char _MCVAR MC_UserAutoInit = (_toggle); +#define MC_SET_CHECKF(_f) \ + CHECKF _MCVAR MC_CheckF = (_f) +#define MC_SET_CHECK_FREQ(_freq) \ + int _MCVAR MC_CheckFreq = (_freq) +#define MC_SET_CRITF(_f) \ + MCCRITF _MCVAR MC_CritF = (_f) +#define MC_SET_ENDF(_f) \ + ENDF _MCVAR MC_EndF = (_f) +#define MC_SET_ENV_VAR(_envvarname) \ + char * _MCVAR MC_EnvVar = (_envvarname) +#define MC_SET_ERF(_f) \ + ERF _MCVAR MC_ERF = (_f) +#define MC_SET_EXCEPTF(_f) \ + MCEXCEPTF _MCVAR MC_ExceptF = (_f) +#define MC_SET_GLOBALF(_f) \ + GLOBALF _MCVAR MC_GlobalF = (_f) +#define MC_SET_GLOBALEXITF(_f) \ + GLOBALF _MCVAR MC_GlobalExitF = (_f) +#define MC_SET_LOGFILE(_f) \ + char * _MCVAR MC_LogFile = (_f) +#define MC_SET_PANIC_BUFFERS(_q) \ + unsigned char _MCVAR MC_PanicBufCount = (_q) +#define MC_SET_SSFH(_f) \ + _SSFRAMEHANDLER _MCVAR near MC_SFrameHandler = (_f) +#define MC_SET_STARTF(_f) \ + STARTF _MCVAR MC_StartF = (_f) +#define MC_SET_TRACKF(_f) \ + TRACKF _MCVAR MC_TrackF = (_f) +#define MC_SET_VERIFYF(_f) \ + VERIFYF _MCVAR MC_VerifyF = (_f) + +/* Use the MC_BEGIN_EXCEPTLIST, MC_HANDLE_EXCEPTION, + and MC_END_EXCEPTLIST macros to change the exceptions + MemCheck handles in protected mode by default. + + Usage (exactly as typed): + #include + : + MC_BEGIN_EXCEPTLIST + MC_HANDLE_EXCEPTION (0x0) + MC_HANDLE_EXCEPTION (0xD) + MC_END_EXCEPTLIST + + NOTE: + To turn off MemCheck's exception handling completely, use + + MC_SET_EXCEPTF(NULL); + + instead of trying to define an empty EXCEPTLIST... +*/ +#define MC_BEGIN_EXCEPTLIST \ + unsigned char _MCVAR MC_ExceptList[] = { +#define MC_HANDLE_EXCEPTION(e) \ + (unsigned char)(e), +#define MC_END_EXCEPTLIST \ + (unsigned char)0xFF }; /* 0xFF MUST end list */ + +/* ------------- End MemCheck 3.0 Library Calls --------------- */ + +/* Formulaic rogue varargs interceptions; + most host-code-compatible method... + "Are you experienced?" + + "It is better to be mugged than + to live in fear." - Anon. +*/ +#define _VA_DEF(f,r,p) \ + typedef r (_RTL *p_##f) p; \ + extern p_##f _MCAPI _loc_##f (_LOCP); + +/* Declare sprintf helper function */ +_VA_DEF(sprintf,int,(char *, const char *, ...)) + + /* * * * * * * * * * * * * * * * * * * * * * * + ************************* + Back-End RTL + ************************* +*/ + +/* *** Back-end functions *** */ + +/* Macro to access true back-end RTL. + Used internally by the MemCheck API functions. +*/ +#define __paste(x,y) x ## y +#define RTL(func) __paste(func,_mc) + +/* Macro to encapsulate the declaration of + the renamed (zapped) back-end rtl +*/ +#define _RTLDECL(f,rctype,params) \ + extern rctype _RTL RTL(f) params + + +/* For the conversion that MSC underwent + from C 6 to 7, where non-ANSI calls + have underbars +*/ +#if defined (_CC_MSC_) && !defined (MSC6) +#if (_MSC_VER >= 700) +# define _C7A +#endif +#endif + +#ifdef _C7A +#define C7ANSI(func) _##func +#else +#define C7ANSI(func) func +#endif + +#undef _C7A + + +/* ---------------------------------------------- */ +/* These are the renamed ("zapped") RTL functions */ +/* ---------------------------------------------- */ + +/* *** ANSI *** */ + +_RTLDECL(malloc, void *, (size_t)); +_RTLDECL(calloc, void *, (size_t, size_t)); +_RTLDECL(realloc, void *, (void *, size_t)); +_RTLDECL(free, void, (void *)); +_RTLDECL(memcpy, void *, (void *,const void *,size_t)); +_RTLDECL(memmove, void *, (void *,const void *,size_t)); +_RTLDECL(memset, void *, (void *,int,size_t)); +_RTLDECL(strcpy, char *, (char *,const char *)); +_RTLDECL(strncpy, char *, (char *,const char *,size_t)); +_RTLDECL(strcat, char *, (char *,const char *)); +_RTLDECL(strncat, char *, (char *,const char *,size_t)); +_RTLDECL(vsprintf, int, (char *,const char *,va_list)); +_RTLDECL(sprintf, int, (char *,const char *,...)); + +#if !defined (_CC_ANSI_) +/* *** MSC *** */ + +/* WATCOM doesn't support these... */ +#if !defined(_CC32_) +_RTLDECL(_fmalloc, void far *, (size_t)); +_RTLDECL(_fcalloc, void far *, (size_t, size_t)); +_RTLDECL(_ffree, void, (void far *)); +_RTLDECL(_fmsize, size_t, (void far *)); +#endif + +_RTLDECL(_nmalloc, void _MCNEAR *,(size_t)); +_RTLDECL(_nfree, void, (void _MCNEAR *)); + +/* *** Borland *** */ + +#if !defined(_CC_POWERPACK32_) +_RTLDECL(farmalloc, void _MCFAR *, (unsigned long)); +_RTLDECL(farcalloc, void _MCFAR *, (unsigned long, unsigned long)); +_RTLDECL(farfree, void, (void _MCFAR *)); + +/* *** General Porpoise *** */ + +_RTLDECL(_fmemset, void far * _MCFARGLUE, (void far *,int,size_t)); +_RTLDECL(_fmemcpy, void far * _MCFARGLUE, (void far *,const void far *,size_t )); +_RTLDECL(_fstrcpy, char far * _MCFARGLUE, (char far *,const void far *)); +#endif /* not _CC_POWERPACK32_ */ + +#endif /* not STDC/ANSI */ + +/***************************************************************** + * -------- Function Call Interception Definitions --------- * + *****************************************************************/ + +#ifndef MEMCHECK_MODULE + +/* + This section targets user's code only +*/ + +/* Func interceptors... */ +#define _INTERCEPT(_f) (_mcsl(_MCSF_,_MCSL_),_f) +#define _VA_INTERCEPT(_f) (*_loc_##_f(_MCSF_,_MCSL_)) +#define _SETLOC(_f) (mc_set_location(),_f) + +/* NOTE near _mcsl with #if (_MCC_NEAR_INTERCEPT == 0) */ + +/* + MC_NO_TRANSFER_SIZE is used to eliminate errors or warnings + like "sizeof returns 0" or "Not allowed type in sizeof ". + These occur for unsized variables declared like + + extern unsigned char gHelpString[]; + + The optimal solution is to "size" the extern, e.g. + + extern unsigned char gHelpString[80]; + + but where this may not be practical, MC_NO_TRANSFER_SIZE may + be defined on a module-by-module OR project-wide basis. +*/ +#ifdef MC_NO_XFER_SIZE /* beta compat */ +# define MC_NO_TRANSFER_SIZE +#endif +#ifdef NO_TRANSFER_SIZE /* alternate */ +# define MC_NO_TRANSFER_SIZE +#endif + +#if defined (MC_NO_TRANSFER_SIZE) +# define _INTERCEPTX(_f,_d) _INTERCEPT(_f) +#else /* standard; transmit sizeof dest */ +# define _INTERCEPTX(_f,_d) (_mcslx(_MCSF_,_MCSL_,sizeof(_d)),_f) +#endif + + +/* Intrinsic Function Disabling + + It's important to disable function inlining for + all intercepted functions. +*/ + +#if defined(_CC_MSC_) + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros... +*/ +#pragma function(strcat) +#pragma function(strcpy) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(strset) + +#if defined(_MSC_VER) +#if (_MSC_VER >= 700) +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) +#pragma function(_fstrset) +#pragma function(_strset) +#endif +#endif /* defined _MSC_VER */ + +#elif defined(_CC_BORLAND_) + +/* Turbo C not like pragmae */ +#if !defined (_CC_TCC_) + +/* Eliminate duplicate strings. + This can save a bit of space in large + programs particularly, since each call to + MemCheck references an otherwise separate + copy of the current filename. +*/ +#pragma option -d + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros, for one... +*/ +#pragma intrinsic -strcat +#pragma intrinsic -strncat +#pragma intrinsic -strcpy +#pragma intrinsic -strncpy +#pragma intrinsic -stpcpy +#pragma intrinsic -strset +#pragma intrinsic -strnset +#pragma intrinsic -memcpy +#pragma intrinsic -memset + +#endif /* not Turbo C */ + +/* end Borland compiler intrinsics */ + +#elif defined (_CC_WATCOM_) + +/* NOTE: unfortunately, WATCOM C/C++ compilers + force inlining of the strcpy() function regardless + of whether you want it inlined or not, all the time. + So this pragma, while it should ensure that + strcpy() is a function call, does not... :{ + + So we take other measures below: see _mcwatcom_strcpy() +*/ +#pragma function(strcpy) + +#pragma function(strcat) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) + +#endif + +/* End disable function inlining */ + + +/*lint -save -e652 Define of symbol declared previously */ +#if defined (MC_NO_INTERCEPT) +#define NO_INTERCEPT +#endif + +#if !defined (NO_INTERCEPT) + +/* *** ANSI Standard C *** */ + +#define calloc(n,_sz) _INTERCEPT(calloc(n,_sz)) +#define malloc(_sz) _INTERCEPT(malloc(_sz)) +#define realloc(p,s) _INTERCEPT(realloc(p,s)) +#define free(p) _INTERCEPT(free(p)) + +#define memcpy(d,s,n) _INTERCEPTX(memcpy(d,s,n),d) +#define memmove(d,s,n) _INTERCEPTX(memmove(d,s,n),d) +#define memset(p,c,n) _INTERCEPTX(memset(p,c,n),p) +#define strcat(s1,s2) _INTERCEPTX(strcat(s1,s2),s1) +#if defined(_CC_WATCOM_) + /* WATCOM forces inlining of strcpy()... see note above */ +# define strcpy(d,s) _INTERCEPTX(_mcwatcom_strcpy(d,s),d) + extern char * _RTL _mcwatcom_strcpy (char *, const char *); +#else +# define strcpy(d,s) _INTERCEPTX(strcpy(d,s),d) +#endif +#define strncat(s1,s2,n) _INTERCEPTX(strncat(s1,s2,n),s1) +#define strncpy(d,s,n) _INTERCEPTX(strncpy(d,s,n),d) +#define vsprintf(s,f,a) _INTERCEPTX(vsprintf(s,f,a),s) + +/* #define sprintf _VA_INTERCEPT(sprintf) */ +#ifndef _lint +#define sprintf _INTERCEPT(sprintf) +#endif + +#if defined(_CC_MSC_COMPATIBLE_) /* *** Microsoft C *** */ + +#define _expand(_p,_s) _INTERCEPT(_expand(_p,_s)) +#define _fcalloc(n,_sz) _INTERCEPT(_fcalloc(n,_sz)) +#define _fexpand(_p,_s) _INTERCEPT(_fexpand(_p,_s)) +#define _ffree(p) _INTERCEPT(_ffree(p)) +#define _fmalloc(_sz) _INTERCEPT(_fmalloc(_sz)) +#define _frealloc(p,s) _INTERCEPT(_frealloc(p,s)) +#define _fmsize(p) _INTERCEPT(_fmsize(p)) +#define _msize(p) _INTERCEPT(_msize(p)) +#define _nfree(p) _INTERCEPT(_nfree(p)) +#define _nmalloc(_sz) _INTERCEPT(_nmalloc(_sz)) +#define _nrealloc(p,s) _INTERCEPT(_nrealloc(p,s)) +#define _ncalloc(n,_sz) _INTERCEPT(_ncalloc(n,_sz)) +#define _nexpand(_p,_s) _INTERCEPT(_nexpand(_p,_s)) +#define _nmsize(p) _INTERCEPT(_nmsize(p)) +#define _nstrdup(s) _INTERCEPT(_nstrdup(s)) +/* #define halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define _halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define hfree(p) _INTERCEPT(hfree(p)) */ +/* #define _hfree(p) _INTERCEPT(hfree(p)) */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define _cgets(s) _INTERCEPTX(_cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#define _memccpy(d,s,c,n) _INTERCEPTX(_memccpy(d,s,c,n),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define _strdup(s) _INTERCEPT(_strdup(s)) +#define _strnset(s,c,n) _INTERCEPTX(_strnset(s,c,n),s) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define _strset(s,c) _INTERCEPTX(_strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define _swab(s,d,n) _INTERCEPTX(_swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) +#define _tempnam(d,pfx) _INTERCEPT(_tempnam(d,pfx)) + +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) +#define _fullpath(b,p,n) _INTERCEPTX(_fullpath(b,p,n),b) + +/* ----- END Microsoft C/C++ interceptions ----- */ + +#elif defined (_CC_BORLAND_) /* *** Borland C/C++ *** */ + +#ifndef _CC_POWERPACK32_ +#define farfree(p) _INTERCEPT(farfree(p)) +#define farmalloc(s) _INTERCEPT(farmalloc(s)) +#define farcalloc(n,s) _INTERCEPT(farcalloc(n,s)) +#define farrealloc(p,s) _INTERCEPT(farrealloc(p,s)) +#endif /* not _CC_POWERPACK32_ */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#if !defined(movmem) +#define movmem(s,d,l) _INTERCEPTX(movmem(s,d,l),d) +#endif +#if !defined(setmem) +#define setmem(d,c,v) _INTERCEPTX(setmem(d,c,v),d) +#endif +#define setvect(i,v) _INTERCEPT(setvect(i,v)) +#define stpcpy(d,s) _INTERCEPTX(stpcpy(d,s),d) +#define _stpcpy(d,s) _INTERCEPTX(_stpcpy(d,s),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) + +#ifndef _CC_POWERPACK32_ +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fmovmem(s,d,l) _INTERCEPTX(_fmovmem(s,d,l),s) +#define _fsetmem(d,c,v) _INTERCEPTX(_fsetmem(d,c,v),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) +#endif /* not _CC_POWERPACK32_ */ + +/* +#define freemem(g) _INTERCEPT(freemem(g)) +#define vsscanf(d,f,a) _INTERCEPTX(vsscanf(d,f,a),d) +*/ + +/* ----- END Borland C/C++ interceptions ----- */ + +#else + +#error Unknown compiler in MemCheck.h + +#endif /* Compiler-specific Function Mapping Section */ + +/* Location Transmitters + + You can add any non-intercepted functions to + this bunch... Just updates MemCheck's file and line + information via mc_set_location(), which is thousands + of times faster than anything that does I/O. + The only time this section could be a problem is + if the header file is included before any other header + files which prototype these routines. + + Borland's TD (Turbo Debugger) also has problems here (see note). +*/ +#ifndef _lint /* LINT not like */ + +/* Borland's Turbo Debugger gets confoosed and executes + a `Run' instead of a `Step' when _SETLOC macro is used... +*/ +#if !defined (_CC_BORLAND_) +#if 1 /* Change this to '0' to omit this section */ + +#define printf _SETLOC(printf) + +#define fopen _SETLOC(fopen) +#define fprintf _SETLOC(fprintf) +#define fread _SETLOC(fread) +#define fwrite _SETLOC(fwrite) +#define fclose _SETLOC(fclose) + +#define system _SETLOC(system) +#define exec _SETLOC(exec) +#define spawnl _SETLOC(spawnl) +#define spawnlp _SETLOC(spawnlp) +#define spawnle _SETLOC(spawnle) +#define spawnlpe _SETLOC(spawnlpe) +#define spawnv _SETLOC(spawnv) +#define spawnvp _SETLOC(spawnvp) +#define spawnve _SETLOC(spawnve) +#define spawnvpe _SETLOC(spawnvpe) + +#endif /* end location transmission section */ +#endif /* not Borland C++ */ +#endif /* not def _lint */ + + +/* **** THIRD-PARTY MAPPINGS **** */ + +/* Vermont Views V3.xx + + The following code will transmit the exact file + and line of any mem_get() and mem_free() calls to + MemCheck, so that it can report on the location where + these functions are called, instead of the location of + the calloc() or free(). + + If you've used MCCONFIG to configure the Vermont Views source + code, you *must* either NOT include the MemCheck header file + in the MEM_GET.C and MEM_FREE.C modules, or, if you do, then + #define NO_INTERCEPT beforehand, e.g. + + Module MEM_GET.C ... + : + #define NO_INTERCEPT + #include + : + + MCCONFIG may be used to configure even the shrouded + Vermont Views source code. + + See also: TechNote "Using MemCheck 3.0 Professional + With Vermont Views", available on the StratosWare + BBS (313) 996-2993 as VIEWS.TXT, or by fax. +*/ +#if defined (VV_SYS) /* should do the trick */ +# define mem_get(s) _INTERCEPT(mem_get(s)) +# define mem_free(p) _INTERCEPT(mem_free(p)) +#endif + + +/* **** APPLICATION-SPECIFIC MAPPINGS **** */ + +/* + If your application uses allocation "cover" routines, + MemCheck will by default report errors and leaks by + the file and line of the malloc or free within the + cover module. To get MemCheck to report by file and + line where the cover function is actually called, follow + the instructtions in MemCheck TechNote "Transmitting File + and Line to MemCheck 3.0 Professional Through Cover Functions." + + This is where you can place the cover function macros. +*/ + + +/* end APPLICATION-SPECIFIC MAPPINGS */ + +#endif /* not NO_INTERCEPT */ + +/* Calls that xmit source file, line number if called in source */ +/* *** MemCheck API file & line transmittal *** */ +#define mc_startcheck(erf) mc_startcheck(_MCSF_,_MCSL_,erf) +#define mc_stack_trace(_memo) _INTERCEPT(mc_stack_trace(_memo)) +#define mc_debug(s) _INTERCEPT(mc_debug(s)) +#define mc_debugf(arglist) mc_debugv arglist +#define mc_debugv _mcsl(_MCSF_,_MCSL_),mc_debugv +#define mc_endcheck() _INTERCEPT(mc_endcheck()) +#define mc_check_buffers() _INTERCEPT(mc_check_buffers()) +#define mc_check(p) _INTERCEPT(mc_check(p)) +#define mc_register(p,s) _INTERCEPT(mc_register(p,s)) +#define mc_unregister(p) _INTERCEPT(mc_unregister(p)) +#define mc_nullcheck() _INTERCEPT(mc_nullcheck()) +#define mc_report(f) _INTERCEPT(mc_report(f)) + +/*lint -restore 652 Define of symbol declared prev */ + +#endif /* not MEMCHECK_MODULE, function interceptions */ + + +/* End "C" call wrapper */ +#ifdef __cplusplus +} + + +/* C++ MemCheck Class + + This class can be used as an alternative to + AutoInit, or to placing the mc_startcheck() and + mc_endcheck() calls in your main() program. + Just declaring an object of class 'MemCheck' + will start MemCheck up; usually you will place + this 'above' any other global or statically declared + C++ objects in your main module. + + Here are some examples of starting MemCheck up + via object mechanics: + + MemCheck On; + MemCheck Active; + MemCheck Rules; + + Use your imagination! Note that if AutoInit is ON, + any calls to mc_startcheck() and mc_endcheck() are + ignored. +*/ +#if !defined (NO_INTERCEPT) /* must not have this def'd */ + +/* This class def causes a warning under MSC if not used */ +#if !defined (_CC_MSC_) + +class MemCheck { +public: + MemCheck () { mc_startcheck (NULL); } + ~MemCheck () { mc_endcheck (); } +}; + +#endif + +#endif /* NO_INTERCEPT */ + + +/* *** For use in new and delete modules only *** */ +/* + Replace 'mallocs' with 'cpp_mallocs', etc. + In new and delete modules, NO_INTERCEPT should be #defined, e.g. + + #define NO_INTERCEPT + #include + : + void * operator new ( size_t size ) + { + if (!size) size = 1; + return (cpp_malloc (size)); + } + etc. +*/ +#define cpp_malloc(_s) (_mc_set_newflag(), malloc(_s)) +#define cpp_calloc(_n,_s) (_mc_set_newflag(), calloc(_n,_s)) +#define cpp_free(_p) (_mc_set_delflag(), free(_p)) + +/* Borland C++ */ +#define cpp_farmalloc(_s) (_mc_set_newflag(), farmalloc(_s)) +#define cpp_farfree(_fp) (_mc_set_delflag(), farfree(_fp)) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) (_mc_set_newflag(), _fmalloc(_s)) +#define cpp__ffree(_fp) (_mc_set_delflag(), _ffree(_fp)) + + +/* C++ */ +#if !defined (NO_INTERCEPT) +#if !defined (NO_CPP) + +/* + This method is off by default, because it + requires definition of a new operator like: + + void * new (size_t size, char *file, int lineno); + + Such a new operator is included in your SOURCE\CPP + directory. To have this method used for all modules, + #define NEW_OVERLOADED at the top of this header file + or in your project #include file, BEFORE the MemCheck + header file is #included. + + The substitutions for the new operator + may not work in all situations. To disable + MemCheck's interception of new on a module-by- + module basis by undefining NEW_OVERLOADED. +*/ +#if defined (NEW_OVERLOADED) + +/* Method 1: Placement Operators + + Use placement operators to trap file and line location transparently + on calls to new. + + Thanks for this tip to Dan Saks, + C and C++ writer, author, teacher, and columnist--buy his books. + He came through when no one else had a clue! + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with an overloaded new placement operator. +*/ + +/* Declare overloaded new with placement operators */ +void *operator new (size_t _sz, char *file, int lineno); +#if defined (_CPP_ANSI20_) +/* Array version; only under supporting compilers + COMMENT LINE OUT if it causes a compile error. +*/ +void *operator new[] (size_t _sz, char *file, int lineno); +#endif + +#if !defined (_CC_MSC_) +#define new new((char *)__FILE__,(int)__LINE__) +#else +#define new new(__FILE__,__LINE__) +#endif + +/* NOTE: + This placement operator interception syntax has been + known to cause syntax errors (in VC++ 1.0) for no apparent reason + on statements like + + Domain *d = new Domain (); + + Workaround is to change line (sorry!) to equivalent + + Domain *d = new Domain; +*/ + +/* Backwards compatibility with the V2.1 C++ macros */ +#ifndef NEW +#define NEW(_object) new _object +#endif +#ifdef DELETE +#define DELETE(_object) delete _object +#endif +#define DELETE_ARR(_arr) delete[] _arr + +#else /* !NEW_OVERLOADED - end of Placement Operator intercept */ + +/* New and Delete Interception, Method 2: NEW() and DELETE() + + The NEW() and DELETE() macros may be used to transmit file + and line of new and delete. These macros, which require + modification of source code, i.e. "NEW(object)" for "new object", + should probably be used only if the above overloaded new does + not work for your code base. + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with NEW() and DELETE(). + + If calling, please have your MemCheck serial number handy. +*/ +#ifndef NEW +#define NEW(_object) (_mcsl_new(_MCSF_,_MCSL_), new _object) +#endif +#ifndef DELETE /* WINNT.H under BC DPMI32 defines DELETE */ +#define DELETE(_object) (_mcsl_delete(_MCSF_,_MCSL_), delete _object) +#endif +#define DELETE_ARR(_arr) (_mcsl_delete(_MCSF_,_MCSL_), delete[] _arr) + +#endif /* !NEW_OVERLOADED */ + +#define delete _mcsl_delete(_MCSF_,_MCSL_), delete + + +/* *** FAILURES *** */ + +/* These macros failed in the purpose of + intercepting new transparently in some + situation or other. +*/ + +/* Failed on " * new expr " (TV) */ +/* #define new (mc_set_location(),0) ? NULL : new */ + +/* Failed on " x = new Object " (TV) */ +/* #define new ((mc_set_location(),0) ? NULL : new) */ +/* #define new new (mc_set_location(),0) ? NULL : */ + +#endif /* !NO_CPP */ +#endif /* NO_INTERCEPT */ +#endif /* cplusplus */ +/******** End C++ ************/ + + +#endif /* End of Section for MEMCHECK Defined */ + +/* -------------------------------------------------------------------------- */ + +#endif /* _MEMCHECK_H_ */ + + +/******************************** + * End of MemCheck 3.0 Header * + ********************************/ + diff --git a/REDALERT/MENUS.CPP b/REDALERT/MENUS.CPP new file mode 100644 index 000000000..0d1c4fa2a --- /dev/null +++ b/REDALERT/MENUS.CPP @@ -0,0 +1,1020 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MENUS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MENUS.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : Oct. 24, 1996 Victor Grippi * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Main_Menu -- Menu processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#else //WIN32 +#include +#endif + +/***************************** +** Function prototypes +******************************/ + +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2); +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index); +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc); + +int UnknownKey; + +PRIVATE int MenuUpdate=1; +PRIVATE int MenuSkip; + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +/*=========================================================================*/ +/* SELECT_TO_ENTRY: */ +/* */ +/* This routine converts a selection to the correct string entry. It */ +/* does this by search through a long bitfield starting at position index */ +/* until it finds the correct conversion to entries. */ +/* */ +/* INPUTS: int selection from menu, long the bit field to search, int */ +/* the starting index within the bit field. */ +/* RETURNS: int the index into the table of entries */ +/*=========================================================================*/ +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index) +{ + int placement; + + if (bitfield==0xFFFFFFFFL) /* if all bits are set */ + return(select); /* then it as is */ + + placement=0; /* current pos zero */ + while (select) { /* while still ones */ + if (bitfield & (1L<<(placement+index))) /* if this flagged then */ + select--; /* decrement counter */ + placement++; /* and we moved a place */ + } + while (!(bitfield & (1L<<(placement+index)))) { + placement++; + } + + return(placement); /* return the position */ +} + + +/*=========================================================================*/ +/* FLASH_LINE: */ +/* */ +/* This routine will flash the line at the desired location for the */ +/* menu routine. It is way cool awesome! */ +/* */ +/* INPUTS: char *text, int x position on line, int y position, char */ +/* normal foreground color, char hilight foreground color, char */ +/* background color */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc) +{ + int loop; + + for (loop=0;loop<3;loop++) { + Hide_Mouse(); + Plain_Text_Print(text, xpix, ypix, hfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Delay(2); + Plain_Text_Print(text, xpix, ypix, nfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); + Delay(2); + } +} + +/*=========================================================================*/ +/* COORDINATES_IN_REGION: */ +/* */ +/* Test to see if a given pair of coordinates are within the given */ +/* rectangular region. */ +/* */ +/* INPUTS: int x to be tested, int y to be tested, int left x pos, */ +/* int top y pos, int right x pos, int bottom y pos */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2) +{ + return((x>=inx1)&&(x<=inx2)&&(y>=iny1)&&(y<=iny2)); +} + +#ifdef NEVER +/*=========================================================================*/ +/* FIND_MENU_ITEMS: */ +/* */ +/* This routine finds the real total items in a menu when certain items */ +/* may be disabled by bit fields and the like. This is done by looping */ +/* through the fields, starting at the position passed in index and */ +/* counting the number of bits that are set. */ +/* */ +/* INPUTS: int the maximum number of items possible on the menu, long */ +/* the bit field of enabled and disabled items, char the index */ +/* point to start at within the list. */ +/* RETURNS: int the total number of items in the menu */ +/*=========================================================================*/ + int Find_Menu_Items(int maxitems, unsigned long field, char index) + { + int loop,ctr; + + if (field==0xFFFFFFFFL) /* if all bits are set */ + return(maxitems); /* then maxitems set */ + + for (loop=ctr=0;loopClear(); +} + + +/*=========================================================================*/ +/* CHECK_MENU: */ +/* */ +/* */ +/* */ +/* INPUTS: */ +/* RETURNS: */ +/*=========================================================================*/ +int Check_Menu(int menu, char const * text[], char *, long field, int index) +{ + int maxitem,select,key,menuy,menux; + int mx1,mx2,my1,my2,tempy; + int drawy,menuskip,halfskip; + int normcol,litcol,item,newitem,idx; + int * menuptr; + + //selection++; /* get rid of warning */ + + menuptr = &MenuList[menu][0]; /* get pointer to menu */ + maxitem = menuptr[ITEMSHIGH]-1; /* find max items */ + newitem = item = menuptr[MSELECTED]%(maxitem+1); /* find selected */ + select = -1; /* no selection made */ + menuskip = FontHeight+MenuSkip; /* calc new font height */ + halfskip = MenuSkip>>1; /* adjustment for menus */ + + menuy = WinY+menuptr[MENUY]; /* get the absolute */ + menux = (WinX+menuptr[MENUX]); /* coords of menu */ + normcol = menuptr[NORMCOL]; + litcol = menuptr[HILITE]; + + /* + ** Fetch a pending keystroke from the buffer if there is a keystroke + ** present. If no keystroke is pending then simple mouse tracking will + ** be done. + */ + key = 0; + UnknownKey = 0; + if (Keyboard->Check()) { +#ifdef WIN32 + key = (Keyboard->Get() & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT) ); /* mask off all but release bit */ +#else + key = (Keyboard->Get()&0x08FF); /* mask off all but release bit */ +#endif + } + + /* + ** if we are using the mouse and it is installed, then find the mouse + ** coordinates of the menu and if we are not somewhere on the menu get + ** the heck outta here. If we are somewhere on the menu, then figure + ** out the new selected item, and continue forward. + */ + mx1=(WinX)+(menuptr[MENUX]*FontWidth); /* get menu coords */ + my1=(WinY)+(menuptr[MENUY])-halfskip; /* from the menu */ + mx2=mx1+(menuptr[ITEMWIDTH]*FontWidth)-1; /* structure as */ + my2=my1+(menuptr[ITEMSHIGH]*menuskip)-1; /* necessary */ + + tempy=Get_Mouse_Y(); + if (Coordinates_In_Region(Get_Mouse_X(), tempy, mx1, my1, mx2, my2)&& MenuUpdate) { + newitem=(tempy-my1)/menuskip; + } + + switch (key) { + + case KN_UP: /* if the key moves up */ + newitem--; /* new item up one */ + if (newitem<0) /* if invalid new item */ + newitem=maxitem; /* put at list bottom */ + break; + case KN_DOWN: /* if key moves down */ + newitem++; /* new item down one */ + if (newitem>maxitem) /* if new item past */ + newitem=0; /* list end, clear */ + break; + case KN_HOME: /* if top of list key */ + case KN_PGUP: /* is selected then */ + newitem=0; /* new item = top */ + break; + case KN_END: /* if bottom of list is */ + case KN_PGDN: /* selected then */ + newitem=maxitem; /* new item = bottom */ + break; + + /* + ** Handle mouse button press. Set selection and then fall into the + ** normal menu item select logic. + */ + case KN_RMOUSE: + case KN_LMOUSE: + if (Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, mx1, my1, mx2, my2)) { + newitem = (Keyboard->MouseQY - my1) / menuskip; + } else { + UnknownKey = key; // Pass the unprocessed button click back. + break; + } + + /* + ** Normal menu item select logic. Will flash line and exit with menu + ** selection number. + */ + case KN_RETURN: /* if a selection is */ + case KN_SPACE: /* made with key */ + case KN_CENTER: + select=newitem; /* flag it made. */ + break; + + case 0: + break; + + /* + ** When no key was pressed or an unknown key was pressed, set the + ** global record of the key and exit normally. + ** EXCEPTION: If the key matches the first letter of any of the + ** menu entries, then presume it as a selection of + ** that entry. + */ + default: + for (idx = 0; idx < menuptr[ITEMSHIGH]; idx++) { + if (toupper(*(text[Select_To_Entry(idx, field, index)])) == toupper(Keyboard->To_ASCII((KeyNumType)(key&0x0FF)))) { + newitem = select = idx; + break; + } + } + UnknownKey = key; + break; + } + + if (newitem!=item) { + Hide_Mouse(); + idx=Select_To_Entry(item, field, index); + drawy=menuy+(item*menuskip); + Plain_Text_Print(text[idx], menux, drawy, normcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + idx=Select_To_Entry(newitem, field, index); + drawy=menuy+(newitem*menuskip); + Plain_Text_Print(text[idx], menux, drawy, litcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); /* resurrect the mouse */ + } + + if (select!=-1) { + idx=Select_To_Entry(select, field, index); + Hide_Mouse(); /* get rid of the mouse */ + drawy=menuy+(newitem*menuskip); + Flash_Line(text[idx], menux, drawy, normcol, litcol, TBLACK); + Show_Mouse(); + select=idx; + } + + menuptr[MSELECTED]=newitem; /* update menu select */ + + return(select); +} + + +/*************************************************************************** + * Do_Menu -- Generic menu processor. * + * * + * This helper function displays a menu of specified entries and waits * + * for the player to make a selection. If a selection is made, then * + * a whole number (starting at 0) is returned matching the entry * + * selected. If ESC is pressed, then -1 is returned. * + * * + * INPUT: strings -- A pointer to an array of pointers to text strings. * + * Each entry in the list will be a menu entry that * + * can be selected. * + * * + * blue -- Should the special blue color be used to display * + * the menu? * + * * + * OUTPUT: Returns with the cardinal number of the selected menu entry. * + * If ESC was pressed, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=========================================================================*/ +int Do_Menu(char const ** strings, bool ) +{ + int count; // Number of entries in this menu. + int length; // The width of the menu (in pixels). + char const ** ptr; // Working menu text pointer. + int selection; // Selection from user. + + if (!strings) return(-1); + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + + /* + ** Determine the number of entries in this string. + */ + ptr = strings; + count = 0; + while (*ptr++) { + count++; + } + MenuList[0][ITEMSHIGH] = count; + + /* + ** Determine the width of the menu by finding the length of the + ** longest menu entry. + */ + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_8POINT|TPF_DROPSHADOW); + length = 0; + ptr = strings; + while (*ptr) { + length = max(length, (int)String_Pixel_Width(*ptr)); + ptr++; + } + length += 7; + MenuList[0][ITEMWIDTH] = length >> 3; + + /* + ** Adjust the window values to match the size of the + ** specified menu. + */ + WindowList[WINDOW_MENU][WINDOWWIDTH] = (MenuList[0][ITEMWIDTH] + 2) * 8; + WindowList[WINDOW_MENU][WINDOWX] = (19 - (length >> 4)) * 8; + WindowList[WINDOW_MENU][WINDOWY] = 174 - (unsigned)(MenuList[0][ITEMSHIGH] * (FontHeight+FontYSpacing)); + WindowList[WINDOW_MENU][WINDOWHEIGHT] = MenuList[0][ITEMSHIGH] * FontHeight + 5 /*11*/; + + /* + ** Display the menu. + */ + Change_Window((int)WINDOW_MENU); + Show_Mouse(); + Window_Box(WINDOW_MENU, BOXSTYLE_RAISED); + Setup_Menu(0, strings, 0xFFFFL, 0, 0); + + Keyboard->Clear(); + selection = -1; + UnknownKey = 0; + while (selection == -1) { + Call_Back(); + selection = Check_Menu(0, strings, NULL, 0xFFL, 0); + if (UnknownKey != 0 || UnknownKey == KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) break; + } + Keyboard->Clear(); + Hide_Mouse(); + + HidPage.Blit(SeenPage); +//WindowList[WINDOW_MAIN][2] = SeenBuff.Get_Width();//BG + Change_Window((int)WINDOW_MAIN); + Map.Flag_To_Redraw(true); + return(selection); +} + + +/*************************************************************************** + * Main_Menu -- Menu processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * index of item selected, -1 if time out * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/17/1995 BRR : Created. * + *=========================================================================*/ +int Main_Menu(unsigned long ) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 152 * RESFACTOR; +#ifdef FIXIT_VERSION_3 + int d_dialog_h = 100 * RESFACTOR; +#else +//#ifdef WIN32 //Extra 'Internet' option on WIN32 menu +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + int d_dialog_h = 100 * RESFACTOR; +#else +// #if defined(MPEGMOVIE) // Denzil 6/25/98 - Video settings +// int d_dialog_h = 100 * RESFACTOR; +// #else + int d_dialog_h = 80 * RESFACTOR; +// #endif +#endif //WIN32 +#endif //FIXIT_VERSION_3 + int d_dialog_x = 85 * RESFACTOR; + int d_dialog_y = 75 * RESFACTOR; + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_start_w = 118 * RESFACTOR; + int d_start_h = 9 * RESFACTOR; + int d_start_x = 102 * RESFACTOR; +#ifndef FIXIT_VERSION_3 // Removed button from main menu. +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - no internet play + int d_internet_w = 118 * RESFACTOR; + int d_internet_h = 9 * RESFACTOR; + int d_internet_x = 102 * RESFACTOR; +#endif //WIN32 +#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// int d_movie_w = 118 * RESFACTOR; +// int d_movie_h = 9 * RESFACTOR; +// int d_movie_x = 102 * RESFACTOR; +//#endif + + int d_load_w = 118 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = 102 * RESFACTOR; + + int d_multi_w = 118 * RESFACTOR; + int d_multi_h = 9 * RESFACTOR; + int d_multi_x = 102 * RESFACTOR; + + int d_intro_w = 118 * RESFACTOR; + int d_intro_h = 9 * RESFACTOR; + int d_intro_x = 102 * RESFACTOR; + + int d_exit_w = 118 * RESFACTOR; //changed value to 118 V.Grippi + int d_exit_h = 9 * RESFACTOR; + int d_exit_x = 102 *RESFACTOR; //Added V.Grippi + + int starty = d_dialog_y + (12 * RESFACTOR); + +//#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play +//#ifndef FIXIT_VERSION_3 + static int max_buttons = 7; +//#else +// static int max_buttons = 6; +//#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// max_buttons++; +//#endif + /* + ** Button enumerations: + */ + // Enums in Select_Game() must match order of buttons in Main_Menu(). +#ifdef FIXIT_VERSION_3 + enum { + BUTTON_EXPAND=100, // (CS) + BUTTON_EXPAND_AM, + BUTTON_START, + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; +#else // FIXIT_VERSION_3 + enum { + BUTTON_EXPAND=100, + BUTTON_START, +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + BUTTON_INTERNET, +#endif //WIN32 +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// BUTTON_MOVIE, +//#endif + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; +#endif // FIXIT_VERSION_3 + + /* + ** Dialog variables: + */ +#ifdef FIXIT_VERSION_3 + bool bExpansionCS = Expansion_CS_Present(); + bool bExpansionAM = Expansion_AM_Present(); +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + bool expansions = Expansion_CS_Present() | Expansion_AM_Present(); +#else + bool expansions = Expansion_CS_Present(); +#endif +#endif + KeyNumType input; // input from user + int retval; // return value + int curbutton; + TextButtonClass * buttons[7]; + unsigned long starttime; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + +#ifdef FIXIT_VERSION_3 + int ystep = 14 * RESFACTOR; + if( bExpansionCS ) + { + if( bExpansionAM ) + ystep = 12 * RESFACTOR; + else + ystep = 13 * RESFACTOR; + } + else if( bExpansionAM ) + ystep = 13 * RESFACTOR; + + TextButtonClass expandbtnCS( BUTTON_EXPAND, TXT_WOL_CS_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h ); + if( bExpansionCS ) + starty += ystep; + TextButtonClass expandbtnAM( BUTTON_EXPAND_AM, TXT_WOL_AM_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h ); + if( bExpansionAM ) + starty += ystep; +#else + int ystep = 12 * RESFACTOR; + if (expansions) ystep = 10 * RESFACTOR; + + TextButtonClass expandbtn (BUTTON_EXPAND, TXT_NEW_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h); + if (expansions) starty += ystep; +#endif + + TextButtonClass startbtn(BUTTON_START, TXT_START_NEW_GAME, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h); + starty += ystep; +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - no internet play + TextButtonClass internetbutton (BUTTON_INTERNET, TXT_INTERNET, TPF_BUTTON, d_internet_x, starty, d_internet_w, d_internet_h); + starty += ystep; +#endif //WIN32 +#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// TextButtonClass moviebutton(BUTTON_MOVIE, "Movie Settings", TPF_BUTTON, d_movie_x, starty, d_movie_w, d_movie_h); +// starty += ystep; +//#endif //WIN32 + + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_MISSION, TPF_BUTTON, d_load_x, starty, d_load_w, d_load_h); + starty += ystep; + + TextButtonClass multibtn(BUTTON_MULTI, TXT_MULTIPLAYER_GAME, TPF_BUTTON, d_multi_x, starty, d_multi_w, d_multi_h); + starty += ystep; + + TextButtonClass introbtn(BUTTON_INTRO, TXT_INTRO, TPF_BUTTON, d_intro_x, starty, d_intro_w, d_intro_h); + starty += ystep; + + TextButtonClass exitbtn(BUTTON_EXIT, TXT_EXIT_GAME, TPF_BUTTON, + d_exit_x, starty, d_exit_w, d_exit_h); + starty += ystep; + + /* + ** Initialize + */ + if (RequiredCD != -2) { + RequiredCD = -1; + Force_CD_Available(RequiredCD); + } + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + starttime = TickCount; + + /* + ** Create the list + */ + commands = &startbtn; +#ifdef FIXIT_VERSION_3 + if( bExpansionCS ) + expandbtnCS.Add_Tail(*commands); + if( bExpansionAM ) + expandbtnAM.Add_Tail(*commands); +#else + if (expansions) { + expandbtn.Add_Tail(*commands); + } +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + internetbutton.Add_Tail(*commands); +#endif //WIN32 +#endif +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// moviebutton.Add_Tail(*commands); +//#endif + loadbtn.Add_Tail(*commands); + multibtn.Add_Tail(*commands); + introbtn.Add_Tail(*commands); + exitbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ +#ifdef FIXIT_VERSION_3 + curbutton = bExpansionCS ? 0 : ( bExpansionAM ? 1 : 2 ); + + buttons[0] = &expandbtnCS; + buttons[1] = &expandbtnAM; + buttons[2] = &startbtn; + buttons[3] = &loadbtn; + buttons[4] = &multibtn; + buttons[5] = &introbtn; + buttons[6] = &exitbtn; +#else + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + + buttons[0] = &expandbtn; + buttons[1] = &startbtn; + buttons[2] = &internetbutton; + buttons[3] = &loadbtn; + buttons[4] = &multibtn; + buttons[5] = &introbtn; + buttons[6] = &exitbtn; +#endif + + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Play_Song(THEME_INTRO); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + while (process) { + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } +#endif + + /* + ** If timeout expires, bail + */ +// if (timeout && TickCount - starttime > timeout) { +// retval = -1; +// process = false; +// } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + /* + ** Display the title and text overlay for the menu. + */ + Set_Logic_Page(HidPage); +// Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); +// Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + commands->Draw_All(); +#ifdef FIXIT_VERSION_3 +#if (0)//PG + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(5 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); +#endif +#else +#ifndef WIN32 + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(8 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); + +#else + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(11 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); + +#endif +#endif + + /* + ** Copy the menu to the visible page. + */ + Hide_Mouse(); + HidPage.Blit(SeenPage); + Show_Mouse(); + + Set_Logic_Page(SeenBuff); + display = false; + } + else { + if (RunningAsDLL) { //PG + retval = -1; + process = false; + } + } + + + /* + ** Get and process player input. + */ + input = commands->Input(); + +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No Internet play + /* + ** Check to see if WChat has told us to start playing an internet game + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + retval = BUTTON_INTERNET - BUTTON_EXPAND; + process = false; + input = KN_NONE; + } +#endif //WIN32 +#endif + + /* + ** If there is input, then take this opportunity to seed some bits + ** to the cryptographic random number generator. + */ + if (input != 0) { + #ifdef WIN32 + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + #else + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + #endif + } + + /* + ** Dispatch the input to be processed. + */ + switch (input) { + case (BUTTON_EXPAND | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifdef FIXIT_VERSION_3 + case (BUTTON_EXPAND_AM | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#endif + + case (BUTTON_START | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifndef FIXIT_VERSION_3 + #if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + case (BUTTON_INTERNET | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + #endif //WIN32 +#endif + +// #if defined(MPEGMOVIE) +// case (BUTTON_MOVIE | KN_BUTTON): +// retval = (input & 0x7FFF) - BUTTON_EXPAND; +// process = false; +// break; +// #endif + + case (BUTTON_LOAD | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_MULTI | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_INTRO | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_EXIT | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#if (0) + case KN_BACKSPACE: + Show_Who_Was_Responsible (); + display = true; + Theme.Play_Song(THEME_INTRO); + break; +#endif //(0) + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; +#ifdef FIXIT_VERSION_3 + switch( curbutton ) + { + case -1: + curbutton = max_buttons - 1; + break; + case 0: + if( !bExpansionCS ) + curbutton = max_buttons - 1; + break; + case 1: + if( !bExpansionAM ) + { + if( bExpansionCS ) + curbutton = 0; + else + curbutton = max_buttons - 1; + } + break; + } +#else + if (expansions) { + if (curbutton < 0) { + curbutton = max_buttons-1; + } + } else { + if (curbutton < 1) { + curbutton = max_buttons-1; + } + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; +#ifdef FIXIT_VERSION_3 + if( curbutton == max_buttons ) + { + if( bExpansionCS ) + curbutton = 0; + else if( bExpansionAM ) + curbutton = 1; + else + curbutton = 2; + } + else if( curbutton == 1 && !bExpansionAM ) + curbutton = 2; +#else + if (curbutton > (max_buttons - 1)) { + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + retval = curbutton; + process = false; + break; + + + + case KN_LMOUSE: +#if (0)//PG + if (Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, + + 9*RESFACTOR, 10*RESFACTOR, + 79*RESFACTOR, 24*RESFACTOR)){ + Show_Who_Was_Responsible(); + display = true; + Theme.Play_Song(THEME_INTRO); + + break; + } +#endif +#ifdef FIXIT_ANTS + #ifdef FIXIT_PATCH_108 + if (Is_Counterstrike_Installed() == true) + { + #endif + if ((Keyboard->Down(KN_LSHIFT) || Keyboard->Down(KN_RSHIFT)) && Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, 260*RESFACTOR, 0, 320*RESFACTOR, 50*RESFACTOR)) { + AntsEnabled = true; + process = false; +#ifdef FIXIT_VERSION_3 + retval = 2; // To match SEL_START_NEW_GAME +#else + retval = 1; +#endif + } + #ifdef FIXIT_PATCH_108 + } + #endif +#endif + + + default: + break; + } + } + + Options.Set_Score_Volume(oldvolume, false); + + return(retval); +} diff --git a/REDALERT/MESSAGE.H b/REDALERT/MESSAGE.H new file mode 100644 index 000000000..f498db91d --- /dev/null +++ b/REDALERT/MESSAGE.H @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define MESSAGE_NONE 0 // +#define MESSAGE_BUILD_WINDTRAP 1 // You must build a Windtrap +#define MESSAGE_STRUCT_CONCRETE 2 // Concrete: Use concrete to +#define MESSAGE_STRUCT_PALACE 3 // Palace: This is your +#define MESSAGE_STRUCT_LIGHT 4 // Light Factory: The Light +#define MESSAGE_STRUCT_HEAVY 5 // Heavy Factory: The Heavy +#define MESSAGE_STRUCT_HITECH 6 // Hi-Tech Factory: The +#define MESSAGE_STRUCT_IX 7 // House IX: The IX Research +#define MESSAGE_STRUCT_WOR 8 // WOR: Wor is used to train +#define MESSAGE_STRUCT_CONST 9 // Construction Facility: All +#define MESSAGE_STRUCT_WINDTRAP 10 // Windtrap: The windtrap +#define MESSAGE_STRUCT_BARRACKS 11 // Barracks: The Barracks is +#define MESSAGE_STRUCT_STARPORT 12 // Startport: The Starport is +#define MESSAGE_STRUCT_REFINERY 13 // Spice Refinery: The +#define MESSAGE_STRUCT_REPAIR 14 // Repair Facility: The Repair +#define MESSAGE_STRUCT_WALL 15 // Wall: The wall is used for +#define MESSAGE_STRUCT_TURRET 16 // Gun Turret: The cannon +#define MESSAGE_STRUCT_RTURRET 17 // Rocket Turret: The +#define MESSAGE_STRUCT_SILO 18 // Spice Silo: The Spice silo +#define MESSAGE_STRUCT_OUTPOST 19 // Outpost: The Outpost +#define MESSAGE_NEED_CONCRETE 20 // There isn't enough open +#define MESSAGE_SAND 21 // Sand: This is sand terrain. +#define MESSAGE_DUNE 22 // Sand Dunes: These are an +#define MESSAGE_ROCK 23 // Rock: This is rock terrain. +#define MESSAGE_MOUNT 24 // Mountain: Mountains on +#define MESSAGE_SPICE 25 // Spice Field: This is the +#define MESSAGE_ADJACENT 26 // Structures must be placed +#define MESSAGE_SEARCH4SPICE 27 // Search for spice fields to +#define MESSAGE_SANDWORM 28 // Warning: Sandworms diff --git a/REDALERT/MISSION.CPP b/REDALERT/MISSION.CPP new file mode 100644 index 000000000..0ea8e18ea --- /dev/null +++ b/REDALERT/MISSION.CPP @@ -0,0 +1,570 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MISSION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : September 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MissionClass::AI -- Processes order script. * + * MissionClass::Assign_Mission -- Give an order to a unit. * + * MissionClass::Commence -- Start script with new order. * + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * MissionClass::Restore_Mission -- Restores overridden mission * + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a te* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * * + * This is the default constructor for the mission class object. It sets the mission * + * handler into a default -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + * 03/01/1996 JLB : Uses initializer lists. * + *=============================================================================================*/ +MissionClass::MissionClass(RTTIType rtti, int id) : + ObjectClass(rtti, id), + Mission(MISSION_NONE), + SuspendedMission(MISSION_NONE), + MissionQueue(MISSION_NONE), + Status(0), + Timer(0) +{ +} + + +/*********************************************************************************************** + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * * + * These are the stub routines that handle the mission logic. They do nothing at this * + * level. Derived classes will override these routine as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this mission * + * handler again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +int MissionClass::Mission_Sleep(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Ambush(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Attack(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Capture(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard_Area(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Harvest(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Move(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Retreat(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Return(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Stop(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Unload(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Enter(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Construction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Deconstruction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Repair(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Missile(void) {return TICKS_PER_SECOND*30;}; + + +/*********************************************************************************************** + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * * + * Use this routine to set the current mission for this object. This routine will blast * + * over the current mission, bypassing the queue method. Call it when the mission needs * + * to be changed immediately. * + * * + * INPUT: mission -- The mission to set to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Set_Mission(MissionType mission) +{ + assert(IsActive); + + Mission = mission; + MissionQueue = MISSION_NONE; +} + + +/*********************************************************************************************** + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * * + * Use this routine to fetch the mission that this object is CURRENTLY acting under. The * + * mission queue may be filled with a imminent mission change, but this routine does not * + * consider that. It only returns the CURRENT mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the mission that this unit is currently following. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionType MissionClass::Get_Mission(void) const +{ + assert(IsActive); + + return(Mission == MISSION_NONE ? MissionQueue : Mission); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * * + * This is a debugging function that dumps this class' status to the monochrome screen * + * for review. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(1, 9);mono->Printf("%-14s", MissionClass::Mission_Name(Mission)); + mono->Set_Cursor(16, 9);mono->Printf("%-12s", MissionClass::Mission_Name(MissionQueue)); + mono->Set_Cursor(1, 7);mono->Printf("%3d", (long)Timer); + mono->Set_Cursor(6, 7);mono->Printf("%2d", Status); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * MissionClass::AI -- Processes order script. * + * * + * This routine will process the order script for as much time as * + * possible or until a script delay is detected. This routine should * + * be called for every unit once per game loop (if possible). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/25/1995 JLB : Added new missions. * + *=============================================================================================*/ +void MissionClass::AI(void) +{ + assert(IsActive); + + ObjectClass::AI(); + + /* + ** If this is the kind of object that is "paralyzed with fear" while it is above + ** ground level (such as when be paradropped), it will perform no mission AI + ** processing. + */ + if ((What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) && Height > 0) { + return; + } + + /* + ** This is the script AI equivalent processing. + */ + BStart(BENCH_MISSION); + if (Timer == 0 && Strength > 0) { + switch (Mission) { + default: + Timer = Mission_Sleep(); + break; + + case MISSION_HARMLESS: + case MISSION_SLEEP: + Timer = Mission_Sleep(); + break; + + case MISSION_STICKY: + case MISSION_GUARD: + Timer = Mission_Guard(); + break; + + case MISSION_ENTER: + Timer = Mission_Enter(); + break; + + case MISSION_CONSTRUCTION: + Timer = Mission_Construction(); + break; + + case MISSION_DECONSTRUCTION: + Timer = Mission_Deconstruction(); + break; + + case MISSION_CAPTURE: + case MISSION_SABOTAGE: + Timer = Mission_Capture(); + break; + + case MISSION_QMOVE: + case MISSION_MOVE: + Timer = Mission_Move(); + break; + + case MISSION_ATTACK: + Timer = Mission_Attack(); + break; + + case MISSION_RETREAT: + Timer = Mission_Retreat(); + break; + + case MISSION_HARVEST: + Timer = Mission_Harvest(); + break; + + case MISSION_GUARD_AREA: + Timer = Mission_Guard_Area(); + break; + + case MISSION_RETURN: + Timer = Mission_Return(); + break; + + case MISSION_STOP: + Timer = Mission_Stop(); + break; + + case MISSION_AMBUSH: + Timer = Mission_Ambush(); + break; + + case MISSION_HUNT: + case MISSION_RESCUE: + Timer = Mission_Hunt(); + break; + +// case MISSION_TIMED_HUNT: +// Timer = Mission_Timed_Hunt(); +// break; + + case MISSION_UNLOAD: + Timer = Mission_Unload(); + break; + + case MISSION_REPAIR: + Timer = Mission_Repair(); + break; + + case MISSION_MISSILE: + Timer = Mission_Missile(); + break; + } + } + BEnd(BENCH_MISSION); +} + + +/*********************************************************************************************** + * MissionClass::Commence -- Start script with new order. * + * * + * This routine will start script processing according to any queued * + * order it may have. If there is no queued order, then this routine * + * does nothing. Call this routine whenever the unit is in a good * + * position to change its order (such as when it is stopped). * + * * + * INPUT: none * + * * + * OUTPUT: Did the mission actually change? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 07/14/1994 JLB : Simplified. * + * 06/17/1995 JLB : Returns success flag. * + *=============================================================================================*/ +bool MissionClass::Commence(void) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + Mission = MissionQueue; + MissionQueue = MISSION_NONE; + + /* + ** Force immediate state machine processing at the first state machine state value. + */ + Timer = 0; + Status = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Assign_Mission -- Give an order to a unit. * + * * + * This routine will assign the specified mission to the mission queue for this object. * + * The actual mission logic will then be performed at the first available and legal * + * opportunity. * + * * + * INPUT: order -- Mission to give the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MissionClass::Assign_Mission(MissionType order) +{ + assert(IsActive); + + /* + ** Ensure that a MISSION_QMOVE is translated into a MISSION_MOVE. + */ + if (order == MISSION_QMOVE) order = MISSION_MOVE; + + if (order != MISSION_NONE && Mission != order) { + MissionQueue = order; + } +} + + +/*********************************************************************************************** + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * * + * This routine is used to convert an ASCII order name into the actual * + * order number it represents. Typically, this is used when processing * + * a scenario INI file. * + * * + * INPUT: name -- The ASCII order name to process. * + * * + * OUTPUT: Returns with the actual order number that the ASCII name * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/22/1994 JLB : Converted to static member function. * + *=============================================================================================*/ +MissionType MissionClass::Mission_From_Name(char const * name) +{ + MissionType order; + + if (name) { + for (order = MISSION_FIRST; order < MISSION_COUNT; order++) { + if (stricmp(Missions[order], name) == 0) { + return(order); + } + } + } + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * * + * Use this routine to convert a mission number into the ASCII string that represents * + * it. Typical use of this is when generating an INI file. * + * * + * INPUT: mission -- The mission number to convert. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that represents the mission type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +char const * MissionClass::Mission_Name(MissionType mission) +{ + if (mission != MISSION_NONE) { + return(Missions[mission]); + } + return("None"); +} + + +/*********************************************************************************************** + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +void MissionClass::Override_Mission(MissionType mission, TARGET, TARGET) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + SuspendedMission = MissionQueue; + } else { + SuspendedMission = Mission; + } + + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * MissionClass::Restore_Mission -- Restores overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +bool MissionClass::Restore_Mission(void) +{ + assert(IsActive); + + if (SuspendedMission != MISSION_NONE) { + Assign_Mission(SuspendedMission); + SuspendedMission= MISSION_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a tea * + * * + * Some missions preclude recruitment into a team. This routine will examine the mission * + * specified and if not allowed for a team, it will return false. * + * * + * INPUT: mission -- The mission type to examine. * + * * + * OUTPUT: bool; Is an object following this mission allowed to be recruited into a team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool MissionClass::Is_Recruitable_Mission(MissionType mission) +{ + if (mission == MISSION_NONE) { + return(true); + } + return(MissionControl[mission].IsRecruitable); +} + + + +MissionControlClass::MissionControlClass(void) : + Mission(MISSION_NONE), + IsNoThreat(false), + IsZombie(false), + IsRecruitable(true), + IsParalyzed(false), + IsRetaliate(true), + IsScatter(true), + Rate(".016"), + AARate(".016") +{ +} + + +char const * MissionControlClass::Name(void) const +{ + if (Mission == MISSION_NONE) { + return(""); + } + return(Missions[Mission]); +} + + + +bool MissionControlClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsNoThreat = ini.Get_Bool(Name(), "NoThreat", IsNoThreat); + IsZombie = ini.Get_Bool(Name(), "Zombie", IsZombie); + IsRecruitable = ini.Get_Bool(Name(), "Recruitable", IsRecruitable); + IsParalyzed = ini.Get_Bool(Name(), "Paralyzed", IsParalyzed); + IsRetaliate = ini.Get_Bool(Name(), "Retaliate", IsRetaliate); + IsScatter = ini.Get_Bool(Name(), "Scatter", IsScatter); + Rate = ini.Get_Fixed(Name(), "Rate", Rate); + AARate = ini.Get_Fixed(Name(), "AARate", 0); + if (AARate == 0) { + AARate = Rate; + } + return(true); + } + return(false); +} diff --git a/REDALERT/MISSION.H b/REDALERT/MISSION.H new file mode 100644 index 000000000..845cb52bd --- /dev/null +++ b/REDALERT/MISSION.H @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MISSION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISSION_H +#define MISSION_H + +#include "object.h" +#include "monoc.h" + +/**************************************************************************** +** This handles order assignment and tracking. The order is used to guide +** overall AI processing. +*/ +class MissionClass : public ObjectClass +{ + public: + + /* + ** This the tactical strategy to use. It is used by the unit script. This + ** is a general guide for unit AI processing. + */ + MissionType Mission; + MissionType SuspendedMission; + + /* + ** The order queue is used for orders that should take effect when the vehicle + ** has reached the center point of a cell. The queued order number is +1 when stored here + ** so that 0 will indicated there is no queued order. + */ + MissionType MissionQueue; + + int Status; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + MissionClass(RTTIType rtti, int id); + MissionClass(NoInitClass const & x) : ObjectClass(x), Timer(x) {}; + virtual ~MissionClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + void Shorten_Mission_Timer(void) {Timer = 0;} + virtual MissionType Get_Mission(void) const; + virtual void Assign_Mission(MissionType mission); + virtual bool Commence(void); + virtual void AI(void); + + /* + ** Support functions. + */ + virtual int Mission_Sleep(void); + virtual int Mission_Ambush(void); + virtual int Mission_Attack(void); + virtual int Mission_Capture(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); +// virtual int Mission_Timed_Hunt(void); + virtual int Mission_Move(void); + virtual int Mission_Retreat(void); + virtual int Mission_Return(void); + virtual int Mission_Stop(void); + virtual int Mission_Unload(void); + virtual int Mission_Enter(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Repair(void); + virtual int Mission_Missile(void); + virtual void Set_Mission(MissionType mission); + static bool Is_Recruitable_Mission(MissionType mission); + + static char const * Mission_Name(MissionType order); + static MissionType Mission_From_Name(char const *name); + virtual void Override_Mission(MissionType mission, TARGET, TARGET); + virtual bool Restore_Mission(void); + + private: + + /* + ** This the thread processing timer. When this value counts down to zero, then + ** more script processing may occur. + */ + CDTimerClass Timer; +}; + + +/**************************************************************************** +** This is the mission control (pun) that controls how each mission behaves +** when it comes to interacting with the game world. Example; some +** missions allow the object to scatter from threats, while others require +** the object to remain in place. This kind of characteristics are specfied +** by this class. +*/ +class MissionControlClass +{ + public: + MissionControlClass(void); + + bool Read_INI(CCINIClass & ini); + int Normal_Delay(void) const {return(TICKS_PER_MINUTE * Rate);} + int AA_Delay(void) const {return(TICKS_PER_MINUTE * AARate);} + + /* + ** This is the mission identifier that this mission represents. + */ + MissionType Mission; + + char const * Name(void) const; + + /* + ** If the object should not be considered a threat when it + ** comes to target scanning, then this will be true. + */ + unsigned IsNoThreat:1; + + /* + ** If objects in this mission should avoid targeting the enemy and + ** also avoid responding to the enemy, then this will be true. + */ + unsigned IsZombie:1; + + /* + ** An ojbect that can be recruited into a team must be on a mission + ** of this type. + */ + unsigned IsRecruitable:1; + + /* + ** If the object can behave normally except that it cannot + ** move to another location, then this flag will be true. + */ + unsigned IsParalyzed:1; + + /* + ** If an object on this mission is damaged, it is allowed to + ** retaliate? + */ + unsigned IsRetaliate:1; + + /* + ** Is the object allowed to scatter from immediate threats? + */ + unsigned IsScatter:1; + + /* + ** This specifies the time to delay between calls to the mission handler for those cases + ** where the delay could be indefinate. The exception would be when timing is critical. + ** Typical use of this would be to regulate the delay between mundane mission processing + ** in order to achieve less game overhead. + */ + fixed Rate; + + /* + ** Anti-Aircraft buildings (and units) in guard or guard area mode will use this override + ** delay interval instead of the normal "Rate" value. + */ + fixed AARate; +}; + + +#endif diff --git a/REDALERT/MIXFILE.CPP b/REDALERT/MIXFILE.CPP new file mode 100644 index 000000000..b23630d66 --- /dev/null +++ b/REDALERT/MIXFILE.CPP @@ -0,0 +1,598 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MIXFILE.CPP 2 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + * * + * Modified by Vic Grippi for WwXlat Tool 10/14/96 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * MixFileClass::Free -- Uncaches a cached mixfile. * + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.* + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "mixfile.h" + +#include "cdfile.h" +extern MFCD temp; + +//template int Compare(T const *obj1, T const *obj2) { +// if (*obj1 < *obj2) return(-1); +// if (*obj1 > *obj2) return(1); +// return(0); +//}; + + +/* +** This is the pointer to the first mixfile in the list of mixfiles registered +** with the mixfile system. +*/ +template +List > MixFileClass::List; + +template class MixFileClass; + +/*********************************************************************************************** + * MixFileClass::Free -- Uncaches a cached mixfile. * + * * + * Use this routine to uncache a mixfile that has been cached. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. * + * * + * OUTPUT: bool; Was the mixfile found and freed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Free(char const * filename) +{ + MixFileClass * ptr = Finder(filename); + + if (ptr) { + ptr->Free(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * * + * This destructor will free all memory allocated by this mixfile and will remove it from * + * the system. A mixfile removed in this fashion must be created anew in order to be * + * subsequent used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 01/06/1995 JLB : Puts mixfile header table into EMS. * + *=============================================================================================*/ +template +MixFileClass::~MixFileClass(void) +{ + /* + ** Deallocate any allocated memory. + */ + if (Filename) { + free((char *)Filename); + } + if (Data != NULL && IsAllocated) { + delete [] Data; + IsAllocated = false; + } + Data = NULL; + + if (HeaderBuffer != NULL) { + delete [] HeaderBuffer; + HeaderBuffer = NULL; + } + + /* + ** Unlink this mixfile object from the chain. + */ + Unlink(); +} + + +/*********************************************************************************************** + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * * + * This is the constructor for the mixfile object. It takes a filename and a memory * + * handler object and registers the mixfile object with the system. The index block is * + * allocated and loaded from disk by this routine. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles compressed file header. * + *=============================================================================================*/ +template +MixFileClass::MixFileClass(char const * filename, PKey const * key) : + IsDigest(false), + IsEncrypted(false), + IsAllocated(false), + Filename(0), + Count(0), + DataSize(0), + DataStart(0), + HeaderBuffer(0), + Data(0) +{ + if (filename == NULL) return; // ST - 5/9/2019 + + /* + ** Check to see if the file is available. If it isn't, then + ** no further processing is needed or possible. + */ + if (!Force_CD_Available(RequiredCD)) { + Prog_End("MixFileClass Force_CD_Available failed", true); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } + + T file(filename); // Working file object. + Filename = strdup(file.File_Name()); + FileStraw fstraw(file); + PKStraw pstraw(PKStraw::DECRYPT, CryptRandom); + Straw * straw = &fstraw; + + if (!file.Is_Available()) return; + + /* + ** Stuctures used to hold the various file headers. + */ + FileHeader fileheader; + struct { + short First; // Always zero for extended mixfile format. + short Second; // Bitfield of extensions to this mixfile. + } alternate; + + /* + ** Fetch the first bit of the file. From this bit, it is possible to detect + ** whether this is an extended mixfile format or the plain format. An + ** extended format may have extra options or data layout. + */ + int got = straw->Get(&alternate, sizeof(alternate)); + + /* + ** Detect if this is an extended mixfile. If so, then see if it is encrypted + ** and/or has a message digest attached. Otherwise, just retrieve the + ** plain mixfile header. + */ + if (alternate.First == 0) { + IsDigest = ((alternate.Second & 0x01) != 0); + IsEncrypted = ((alternate.Second & 0x02) != 0); + + if (IsEncrypted) { + pstraw.Key(key); + pstraw.Get_From(&fstraw); + straw = &pstraw; + } + straw->Get(&fileheader, sizeof(fileheader)); + + } else { + memmove(&fileheader, &alternate, sizeof(alternate)); + straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate)); + } + + Count = fileheader.count; + DataSize = fileheader.size; +//BGMono_Printf("Mixfileclass %s DataSize: %08x \n",filename,DataSize);Get_Key(); + /* + ** Load up the offset control array. If RAM is exhausted, then the mixfile is invalid. + */ + HeaderBuffer = new SubBlock [Count]; + if (HeaderBuffer == NULL) return; + straw->Get(HeaderBuffer, Count * sizeof(SubBlock)); + + /* + ** The start of the embedded mixfile data will be at the current file offset. + ** This should be true even if the file header has been encrypted because the file + ** header was cleverly written with just the sufficient number of padding bytes so + ** that this condition would be true. + */ + DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart; +// DataStart = file.Seek(0, SEEK_CUR); + + /* + ** Attach to list of mixfiles. + */ + List.Add_Tail(this); +} + + +/*********************************************************************************************** + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * * + * This routine will return with a pointer to the specified data file if the file resides * + * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident * + * file directly rather than going through the process of pseudo disk access. This will * + * save both time and RAM. * + * * + * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. * + * * + * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +template +void const * MixFileClass::Retrieve(char const * filename) +{ + void * ptr = 0; + Offset(filename, &ptr); + return(ptr); +}; + + +/*********************************************************************************************** + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * * + * This routine will scan through all registered mixfiles and return with a pointer to * + * the matching mixfile. If no mixfile could be found that matches the name specified, * + * then NULL is returned. * + * * + * INPUT: filename -- Pointer to the filename to search for. * + * * + * OUTPUT: Returns with a pointer to the matching mixfile -- if found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 06/08/1996 JLB : Only compares filename and extension. * + *=============================================================================================*/ +template +MixFileClass * MixFileClass::Finder(char const * filename) +{ + MixFileClass * ptr = List.First(); + while (ptr->Is_Valid()) { + char path[_MAX_PATH]; + char name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + /* + ** Strip the drive and path (if present) off of the filename + ** in the mixfile list. This enables a simple comparison to the + ** filename specified. The filename specified won't have a path attached and + ** the full pathname in the mixfile list WILL have a path attached. Hence, this + ** stripping of the path is necessary. + */ + _splitpath(ptr->Filename, NULL, NULL, name, ext); + _makepath(path, NULL, NULL, name, ext); + + if (stricmp(path, filename) == 0) { + return(ptr); + } + ptr = ptr->Next(); + } + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * * + * This routine will cache the mixfile, specified by name, into RAM. * + * * + * INPUT: filename -- The name of the mixfile that should be cached. * + * * + * OUTPUT: bool; Was the cache successful? * + * * + * WARNINGS: This routine could go to disk for a very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(char const * filename, Buffer const * buffer) +{ + MixFileClass * mixer = Finder(filename); + + if (mixer != NULL) { + return(mixer->Cache(buffer)); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * * + * This load the mixfile data into ram for this mixfile object. This is the counterpart * + * to the Free() function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles attached message digest. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(Buffer const * buffer) +{ + /* + ** If the mixfile is already cached, then no action needs to be performed. + */ + if (Data != NULL) return(true); + + /* + ** If a buffer was supplied (and it is big enough), then use it as the data block + ** pointer. Otherwise, the data block must be allocated. + */ + if (buffer != NULL) { + if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) { + Data = buffer->Get_Buffer(); + } + } else { + Data = new char [DataSize]; + IsAllocated = true; + } + + /* + ** If there is a data buffer to fill, then fill it now. + */ + if (Data != NULL) { + T file(Filename); + + FileStraw fstraw(file); + Straw * straw = &fstraw; + + /* + ** If a message digest is attached, then link a SHA straw segment to the data + ** stream so that the actual SHA can be compared with the attached one. + */ + SHAStraw sha; + if (IsDigest) { + sha.Get_From(fstraw); + straw = &sha; + } + + /* + ** Bias the file to the actual start of the data. This is necessary because the + ** real data starts some distance (not so easily determined) from the beginning of + ** the real file. + */ + file.Open(READ); + file.Bias(0); + file.Bias(DataStart); + + /* + ** Fetch the whole mixfile data in one step. If the number of bytes retrieved + ** does not equal that requested, then this indicates a serious error. + */ + long actual = straw->Get(Data, DataSize); + if (actual != DataSize) { + delete [] Data; + Data = NULL; + file.Error(EIO); + return(false); + } + + /* + ** If there is a digest attached to this mixfile, then read it in and + ** compare it to the generated digest. If they don't match, then + ** return with the "failure to cache" error code. + */ + if (IsDigest) { + char digest1[20]; + char digest2[20]; + sha.Result(digest2); + fstraw.Get(digest1, sizeof(digest1)); + if (memcmp(digest1, digest2, sizeof(digest1)) != 0) { + delete [] Data; + Data = NULL; + return(false); + } + } + + return(true); + } + IsAllocated = false; + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * * + * This routine will free the (presumably large) raw data block, but leave the index * + * block intact. By using this in conjunction with the Cache() function, one can maintain * + * tight control of memory usage. If the index block is desired to be freed, then the * + * mixfile object must be deleted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +void MixFileClass::Free(void) +{ + if (Data != NULL && IsAllocated) { + delete [] Data; + } + Data = NULL; + IsAllocated = false; +} + + +int compfunc(void const * ptr1, void const * ptr2) +{ + if (*(long const *)ptr1 < *(long const *)ptr2) return(-1); + if (*(long const *)ptr1 > *(long const *)ptr2) return(1); + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * * + * This routine will take the filename specified and search through the mixfile system * + * looking for it. If the file was found, then the mixfile it was found in, the offset * + * from the start of the mixfile, and the size of the embedded file will be returned. * + * Using this method it is possible for the CCFileClass system to process it as a normal * + * file. * + * * + * INPUT: filename -- The filename to search for. * + * * + * realptr -- Stores a pointer to the start of the file in memory here. If the * + * file is not in memory, then NULL is stored here. * + * * + * mixfile -- The pointer to the corresponding mixfile is placed here. If no * + * mixfile was found that contains the file, then NULL is stored here. * + * * + * offset -- The starting offset from the beginning of the parent mixfile is * + * stored here. * + * * + * size -- The size of the embedded file is stored here. * + * * + * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist * + * and can be opened. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) +{ + MixFileClass * ptr; + + if (filename == NULL) { +assert(filename != NULL);//BG + return(false); + } + + /* + ** Create the key block that will be used to binary search for the file. + */ + // Can't call strupr on a const string. ST - 5/20/2019 + //long crc = Calculate_CRC(strupr((char *)filename), strlen(filename)); + char filename_upper[_MAX_PATH]; + strcpy(filename_upper, filename); + strupr(filename_upper); + long crc = Calculate_CRC(strupr(filename_upper), strlen(filename_upper)); + SubBlock key; + key.CRC = crc; + + /* + ** Sweep through all registered mixfiles, trying to find the file in question. + */ + ptr = List.First(); + while (ptr->Is_Valid()) { + SubBlock * block; + + /* + ** Binary search for the file in this mixfile. If it is found, then extract the + ** appropriate information and store it in the locations provided and then return. + */ + block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc); + if (block != NULL) { + if (mixfile != NULL) *mixfile = ptr; + if (size != NULL) *size = block->Size; + if (realptr != NULL) *realptr = NULL; + if (offset != NULL) *offset = block->Offset; + if (realptr != NULL && ptr->Data != NULL) { + *realptr = (char *)ptr->Data + block->Offset; + } + if (ptr->Data == NULL && offset != NULL) { + *offset += ptr->DataStart; + } + return(true); + } + + /* + ** Advance to next mixfile. + */ + ptr = ptr->Next(); + } + + /* + ** All the mixfiles have been examined but no match was found. Return with the non success flag. + */ +assert(1);//BG + return(false); +} + + + +// ST - 12/18/2019 11:36AM +template +void MixFileClass::Free_All(void) +{ + MixFileClass * ptr = List.First(); + while (ptr->Is_Valid()) { + delete ptr; + ptr = List.First(); + } +} diff --git a/REDALERT/MIXFILE.H b/REDALERT/MIXFILE.H new file mode 100644 index 000000000..3e8a88e12 --- /dev/null +++ b/REDALERT/MIXFILE.H @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MIXFILE.H 1 3/03/97 10:25a Joe_bostic $ */ + +#ifndef MIXFILE_H +#define MIXFILE_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include +#include "listnode.h" +#include "pk.h" +#include "buff.h" + +template +class MixFileClass : public Node> +{ + public: + char const * Filename; // Filename of mixfile. + + + MixFileClass(char const *filename, PKey const * key); + ~MixFileClass(void); + + static bool Free(char const *filename); + void Free(void); + static void Free_All(void); // ST - 12/18/2019 11:35AM + bool Cache(Buffer const * buffer = NULL); + static bool Cache(char const *filename, Buffer const * buffer=NULL); + static bool Offset(char const *filename, void ** realptr = 0, MixFileClass ** mixfile = 0, long * offset = 0, long * size = 0); + static void const * Retrieve(char const *filename); + + struct SubBlock { + long CRC; // CRC code for embedded file. + long Offset; // Offset from start of data section. + long Size; // Size of data subfile. + + int operator < (SubBlock & two) const {return (CRC < two.CRC);}; + int operator > (SubBlock & two) const {return (CRC > two.CRC);}; + int operator == (SubBlock & two) const {return (CRC == two.CRC);}; + }; + + private: + static MixFileClass * Finder(char const * filename); + //long Offset(long crc, long * size = 0) const; // ST - 5/10/2019 + + /* + ** If this mixfile has an attached message digest, then this flag + ** will be true. The digest is checked only when the mixfile is + ** cached. + */ + unsigned IsDigest:1; + + /* + ** If the header to this mixfile has been encrypted, then this flag + ** will be true. Although the header of the mixfile may be encrypted, + ** the attached data files are not. + */ + unsigned IsEncrypted:1; + + /* + ** If the cached memory block was allocated by this routine, then this + ** flag will be true. + */ + unsigned IsAllocated:1; + + /* + ** This is the initial file header. It tells how many files are embedded + ** within this mixfile and the total size of all embedded files. + */ + typedef struct { + short count; + long size; + } FileHeader; + + /* + ** The number of files within the mixfile. + */ + int Count; + + /* + ** This is the total size of all the data file embedded within the mixfile. + ** It does not include the header or digest bytes. + */ + long DataSize; + + /* + ** Start of raw data in within the mixfile. + */ + long DataStart; + + /* + ** Points to the file header control block array. Each file in the mixfile will + ** have an entry in this table. The entries are sorted by their (signed) CRC value. + */ + SubBlock * HeaderBuffer; + + /* + ** If the mixfile has been cached, then this points to the cached data. + */ + void * Data; // Pointer to raw data. + + static List List; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/MMX.ASM b/REDALERT/MMX.ASM new file mode 100644 index 000000000..b349ca1a0 --- /dev/null +++ b/REDALERT/MMX.ASM @@ -0,0 +1,329 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : MMX.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : May 19th, 1996 * +;* * +;* Last Update : May 19th 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + ; include + + + .model flat + ;.686 + + + externdef C Detect_MMX_Availability:near + externdef C Single_Line_Trans_Entry:near + externdef C Next_Line:near + externdef C Init_MMX:near + externdef C MMX_Done:near + + externdef EndNewShapeJumpTable:byte + externdef NewShapeJumpTable:dword + externdef C Single_Line_Trans:near + externdef MMX_Single_Line_Trans:near + + + .code + +externdef C CPUType:byte +externdef C VendorID:byte + + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local local_cputype:byte + + ; MMX always available now. ST - 1/3/2019 1:31PM + mov [CPUType], 5 + mov eax, 1 + ret + + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [local_cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [local_cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [local_cputype],al + +@@end_get_cpu: mov al,[local_cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + +;********************************************************************************************* +;* Init_MMX -- Do any special inits required for MMX support * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Init_MMX proc C + + mov edi,offset NewShapeJumpTable + mov ecx,offset EndNewShapeJumpTable + sub ecx,edi + shr ecx,2 + mov eax,offset Single_Line_Trans + mov ebx,offset MMX_Single_Line_Trans + cld + + +@@patch_loop: repnz scasd + jnz @@done + mov [edi-4],ebx + test ecx,ecx + jnz @@patch_loop + +@@done: ret + +Init_MMX endp + + + + + + +;********************************************************************************************* +;* MMX_Done -- Restores floating point capability after MMX usage * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +MMX_Done proc C + + emms + ret + +MMX_Done endp + + + + + + + + code segment page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +;********************************************************************************************* +;* MMX_Single_Line_Trans -- draw a single line of transparent pixels using MMX technology * +;* * +;* * +;* INPUT: Esi - ptr to source data * +;* Edi - ptr to destination data * +;* Ecx - width to draw in bytes * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + + align 16 + +MMX_Single_Line_Trans proc near + +; +; If we are doing less than 8 bytes then dont use MMX +; + cmp ecx,8 + jge @@mmx_loop + push offset Single_Line_Trans_Entry + ret + +; +; Use MMX instructions to mask 8 bytes at once +; +; Creates a bitmask based on the source bytes equality with zero and then uses this to mask +; out the source bytes in the destination data. The advatage that MMX gives us is that there is +; no 'test for zero then jump' required to mask. +; + align 64 ;MMX instructions like 64 byte alignment! + +@@mmx_loop: + movq mm0,[esi] ; move 8 bytes of source into mm0 + pxor mm1,mm1 ; zero out mm1 + pcmpeqb mm1,mm0 ; compare mm0 with 0. Bits get set in mm1 + lea esi,[esi+8] ; adjust the source data pointer + pand mm1,[edi] ; and in the destination data to throw away the bytes which arent zero in the source + sub ecx,8 ; adjust the byte counter + por mm1,mm0 ; or in the source with the destination data + movq [edi],mm1 ; write back the destination data + lea edi,[edi+8] ; adjust the destination pointer + + cmp ecx,8 + jg @@mmx_loop + +; +; Jump to the approprite code for drawing the end of this line or going to the next one +; + push offset Next_Line + jcxz @@next_line + push offset Single_Line_Trans_Entry +@@next_line: ret + + +MMX_Single_Line_Trans endp + + +code ends + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + +end \ No newline at end of file diff --git a/REDALERT/MONOC.CPP b/REDALERT/MONOC.CPP new file mode 100644 index 000000000..36cf97f99 --- /dev/null +++ b/REDALERT/MONOC.CPP @@ -0,0 +1,1132 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MONOC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::Pan -- Scroll the window right or left. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Print -- Simple print of text number. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Printf -- Prints formatted text using text string number. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::Text_Print -- Simple text printing from text number. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * Mono_Print -- Prints simple text to monochrome screen. * + * Mono_Printf -- Prints formatted text to visible page. * + * Mono_Text_Print -- Prints text to location specified. * + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#if (0)//PG +//#include "watcom.h" +#include "monoc.h" + +#include "function.h" + +#include +#include +#include +//#include +#include +#include + + +//PG +//extern void output(short port, short data); +//#pragma aux output parm [dx] [ax] = \ +// "out dx,al" \ +// "inc dx" \ +// "mov al,ah" \ +// "out dx,al" + + +bool MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES]; +//MonoClass::MonoPageType * MonoClass::MonoRAM = (MonoClass::MonoPageType *) 0xB0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +#ifdef NEVER +template +T min(T a, T b) { + if (a < b) return(a); + return(b); +} + +template +T max(T a, T b) { + if (a > b) return(a); + return(b); +} +#endif + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) : + X(0), + Y(0), + Attrib(NORMAL), + Page(0), + SubX(0), + SubY(0), + SubW(COLUMNS), + SubH(LINES) +{ + int index; + + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Pan -- Scroll the window right or left. * + * * + * This routine will scroll the window to the right or left as indicated by the number of * + * rows. * + * * + * INPUT: cols -- The number of columns to pan the window. Positive numbers pan to the left * + * while negative numbers pan to the right. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Pan(int cols) +{ + if (cols == 0) return; + + if (abs(cols) >= SubW) { + Clear(); + return; + } + + CellType cell; + cell.Character = ' '; + cell.Attribute = Attrib; + + if (cols > 0) { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX], &Page_Ptr()->Data[index][SubX+cols], sizeof(CellType)*(SubW-cols)); + for (int cc = SubX+SubW-cols; cc < SubX+SubW; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } else { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX-cols], &Page_Ptr()->Data[index][SubX], sizeof(CellType)*(SubW+cols)); + for (int cc = SubX; cc < SubX-cols; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } + Set_Cursor(X-cols, Y); +} + + +/*********************************************************************************************** + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * * + * This routine is used to partition the monochrome screen so that only a sub-window will * + * be processed. By using this, a separate rectangle of the screen can be cleared, * + * scrolled, panned, or printed into. * + * * + * INPUT: x,y -- The upper left corner of the new sub-window. * + * * + * w,h -- Dimensions of the sub-window specified in characters. * + * * + * OUTPUT: none * + * * + * WARNINGS: The parameters are clipped as necessary. * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Sub_Window(int x, int y, int w, int h) +{ + /* + ** Undo any sub window adjustments to the cursor position. + */ + X += SubX; + Y += SubY; + + /* + ** Ensure parameters are legal. + */ + x = min(x, COLUMNS-1); + x = max(x, 0); + y = min(y, LINES-1); + y = max(y, 0); + if (w == -1) w = COLUMNS-x; + if (h == -1) h = LINES-y; + + /* + ** Assign the new sub-region. + */ + SubX = x; + SubY = y; + SubW = w; + SubH = h; + + /* + ** Move the cursor (if necessary) so that it falls within that region. + */ + int xx = X; + int yy = Y; + xx = min(xx, SubX+SubW-1); + xx = max(xx, SubX); + yy = min(yy, SubY+SubH-1); + yy = max(yy, SubY); + Set_Cursor(xx-SubX, yy-SubY); +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, MonoAttribute attrib, BoxStyleType thick) +{ + CellType cell; + MonoAttribute oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + w = min(w, SubW-x); + w = max(w, 1); + h = min(h, SubH-y); + h = max(h, 1); + + x += SubX; + y += SubY; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Page_Ptr()->Data[y][x+xpos+1] = cell; + cell.Character = CharData[thick].BottomEdge; + Page_Ptr()->Data[y+h-1][x+xpos+1] = cell; + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Page_Ptr()->Data[y+ypos+1][x] = cell; + cell.Character = CharData[thick].RightEdge; + Page_Ptr()->Data[y+ypos+1][x+w-1] = cell; + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Page_Ptr()->Data[y][x] = cell; + cell.Character = CharData[thick].UpperRight; + Page_Ptr()->Data[y][x+w-1] = cell; + cell.Character = CharData[thick].BottomRight; + Page_Ptr()->Data[y+h-1][x+w-1] = cell; + cell.Character = CharData[thick].BottomLeft; + Page_Ptr()->Data[y+h-1][x] = cell; + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + + X = x; + Y = y; +#if (0)//PG + if (!Enabled) return; + + /* + ** Update the visible cursor position only if the this mono page is the currently + ** visible one. + */ + int pos = ((y+SubY)*COLUMNS)+(x+SubX); + if (Page == 0) { + output(CONTROL_PORT, (short)(0x0E|(pos&0xFF00))); + output(CONTROL_PORT, (short)(0x0F|(pos<<8))); + } +#endif +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + if (!Enabled) return; + + Set_Cursor(0, 0); + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int rows = 0; rows < SubH; rows++) { + for (int cols = 0; cols < SubW; cols++) { + Page_Ptr()->Data[rows+SubX][cols+SubY] = cell; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * * + * This routine will give the specified attribute to the characters within the block * + * but will not change the characters themselves. You can use this routine to change the * + * underline, blink, or inverse characteristics of text. * + * * + * INPUT: x,y -- The upper left coordinate of the region to change. * + * * + * w,h -- The dimensions of the region to change (in characters). * + * * + * attrib -- The attribute to fill into the region specified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib) +{ + if (!w || !h || x >= SubW || h >= SubH || x+w > SubW || y+h > SubH) return; + + for (int rows = y; rows < y+h; rows++) { + for (int cols = x; cols < x+w; cols++) { + Page_Ptr()->Data[rows+SubY][cols+SubX].Attribute = attrib; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + if (!Enabled || lines <= 0) return; + + if (abs(lines) >= SubH) { + Clear(); + return; + } + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + if (lines > 0) { + for (int row = 0; row < SubH-lines; row++) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row+1][SubX], SubW*sizeof(CellType)); + } + for (int frow = SubH-lines; frow < SubH; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } else { + for (int row = SubH-1; row >= -lines; row--) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row-1][SubX], SubW*sizeof(CellType)); + } + for (int frow = 0; frow < -lines; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } + + Set_Cursor(X, Y-lines); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints formatted text using text string number. * + * * + * This routine will take the given text string number and print the formatted text to * + * the monochrome screen. * + * * + * INPUT: text -- The text number to convert into real text (by way of external function). * + * * + * ... -- Additional parameters as needed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(int text, ...) +{ + va_list va; + + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const * ptr) +{ + int startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; + while (*text) { + + cell.Character = *text; + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (cell.Character) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(startcol, Y+1); + break; + + /* + ** The TAB character is not currently handled. Convert it to + ** a space instead. + */ + case '\t': + cell.Character = ' '; + // fall into normal print case. + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge it is moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + Page_Ptr()->Data[SubY+Y][SubX+X] = cell; + + if (X < SubW-1) { + Set_Cursor(X+1, Y); + break; + } + // Fall into newline case. + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(0, Y+1); + break; + } + text++; + } +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Simple text printing from text number. * + * * + * This will print the text (represented by the text number) to the location on the * + * monochrome screen specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * x,y -- The coordinates to begin the printing at. * + * * + * attrib-- The character attribute to use while printing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(int text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + if (text != 0) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + + +/*********************************************************************************************** + * MonoClass::Print -- Simple print of text number. * + * * + * Prints text represented by the text number specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memmove(Page_Ptr(), src.Page_Ptr(), sizeof(MonoPageType)); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + MonoClass * displace = Get_Current(); + if (displace) { + for (int line = 0; line < LINES; line++) { + for (int col = 0; col < COLUMNS; col++) { + CellType temp = Page_Ptr()->Data[line][col]; + Page_Ptr()->Data[line][col] = Raw_Ptr(0)->Data[line][col]; + Raw_Ptr(0)->Data[line][col] = temp; + } + } + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memmove(Raw_Ptr(0), Page_Ptr(), sizeof(MonoPageType)); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + + +/*********************************************************************************************** + * Mono_Printf -- Prints formatted text to visible page. * + * * + * This routine will print formatted text (with parameters) to the visible monochrome page * + * at whatever the current cursor location is. * + * * + * INPUT: string -- The string to use as the main text and formatting control string. * + * * + * ... -- Any additional parameters required by the formatting string. * + * * + * OUTPUT: Returns with the number of characters written to the display. * + * * + * WARNINGS: The total size of the formatted text must not exceed 256 characters. * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Printf(char const * string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +/*********************************************************************************************** + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * * + * This routine will clear the currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + + +/*********************************************************************************************** + * Mono_Text_Print -- Prints text to location specified. * + * * + * This routine will print the specified text to the location indicated. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * x,y -- The coordinate to print the text at. * + * * + * attrib-- The attribute to use when printing the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (MonoClass::MonoAttribute)attrib); + } +} + + +/*********************************************************************************************** + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * * + * Use this routine to draw a rectangle to the monochrome screen. The dimensions, attribute,* + * and line style are controlled by parameters. * + * * + * INPUT: x,y -- Coordinate of upper left corner of the box to draw. * + * * + * w,h -- The width and height of the box to draw. * + * * + * attrib-- The attribute to use when drawing the box. * + * * + * thick -- The line drawing style to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (MonoClass::MonoAttribute)attrib, (MonoClass::BoxStyleType)thick); + } +} + + +/*********************************************************************************************** + * Mono_Print -- Prints simple text to monochrome screen. * + * * + * This is the non-formatting print to the monochrome screen. * + * * + * INPUT: text -- Pointer to the text that will be printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + + +/*********************************************************************************************** + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * * + * Use this routine to get the current cursor X position. This only applies to the * + * currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with X position of cursor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +/*********************************************************************************************** + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * * + * This routine will fetch the current Y cursor position for the monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Y position of the monochrome page. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + +//#endif \ No newline at end of file diff --git a/REDALERT/MONOC.H b/REDALERT/MONOC.H new file mode 100644 index 000000000..e08f6a40e --- /dev/null +++ b/REDALERT/MONOC.H @@ -0,0 +1,237 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MONOC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_Hx +#define MONOC_Hx + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class MonoClass { + public: + enum MonoClassPageEnums { + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + MAX_MONO_PAGES=16 // Maximum RAM pages on mono card. + }; + + typedef enum MonoAttribute { + INVISIBLE=0x00, // Black on black. + UNDERLINE=0x01, // Underline. + BLINKING=0x90, // Blinking white on black. + NORMAL=0x02, // White on black. + INVERSE=0x70, // Black on white. + } MonoAttribute; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = true;}; + static void Disable(void) {Enabled = false;}; + static bool Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Sub_Window(int x=0, int y=0, int w=-1, int h=-1); + void Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib); + void Draw_Box(int x, int y, int w, int h, MonoAttribute attrib=NORMAL, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(MonoAttribute attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, MonoAttribute attrib=NORMAL); + void Text_Print(int text, int x, int y, MonoAttribute attrib=NORMAL); + void View(void); + void Scroll(int lines=1); + void Pan(int cols=1); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + int Get_Width(void) const {return(SubW);}; + int Get_Height(void) const {return(SubH);}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + + /* + ** Cursor coordinate (relative to sub-window). + */ + int X; + int Y; + + /* + ** Default attribute to use when printing text. + */ + MonoAttribute Attrib; + + /* + ** The current physical page that this mono class object refers to. + */ + int Page; + + /* + ** Sub window coordinates. + */ + int SubX; + int SubY; + int SubW; + int SubH; + + /* + ** Pointer to the monochrome RAM. + */ +// static MonoPageType * MonoRAM; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + struct BoxDataType { + unsigned char UpperLeft; + unsigned char TopEdge; + unsigned char UpperRight; + unsigned char RightEdge; + unsigned char BottomRight; + unsigned char BottomEdge; + unsigned char BottomLeft; + unsigned char LeftEdge; + }; + static BoxDataType const CharData[4]; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + struct CellType { + unsigned char Character; // Character to display. + unsigned char Attribute; // Attribute. + }; + + struct MonoPageType { + CellType Data[LINES][COLUMNS]; + }; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + SIZE_OF_PAGE=(int)LINES*(int)COLUMNS*sizeof(CellType) // Entire page size. + }; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** Fetches pointers to the appropriate mono RAM. + */ + MonoPageType * Raw_Ptr(int page) const { + return &((MonoPageType *)0xB0000)[page]; + } + MonoPageType * Page_Ptr(void) const { + return(Raw_Ptr(Page)); + } + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static bool Enabled; +}; + +#ifndef WIN32 +int Mono_Printf(int string, ...); +#else +extern void Mono_Set_Cursor(int x, int y); +extern int Mono_Printf(int string, ...); +extern int Mono_Printf(char const * string, ...); +extern void Mono_Clear_Screen(void); +extern void Mono_Text_Print(void const *text, int x, int y, int attrib); +extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +extern void Mono_Print(void const *text); +extern int Mono_X(void); +extern int Mono_Y(void); +#endif + +#endif + diff --git a/REDALERT/MOUSE.CPP b/REDALERT/MOUSE.CPP new file mode 100644 index 000000000..9a1157162 --- /dev/null +++ b/REDALERT/MOUSE.CPP @@ -0,0 +1,389 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MOUSE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MouseClass::AI -- Process player input as it relates to the mouse * + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This points to the loaded mouse shapes. +*/ +void const * MouseClass::MouseShapes; + +/* +** This is the timer that controls the mouse animation. It is always at a fixed +** rate so it uses the constant system timer. +*/ +CDTimerClass MouseClass::Timer = 0; + + +/*********************************************************************************************** + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * * + * This is the default constructor for the mouse handling class. It merely sets up the * + * mouse system to its default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +MouseClass::MouseClass(void) : + IsSmall(false), + CurrentMouseShape(MOUSE_NORMAL), + NormalMouseShape(MOUSE_NORMAL), + Frame(0) +{ +} + + +/*********************************************************************************************** + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * * + * This routine is used to inform the display system as to which mouse shape is desired. * + * * + * INPUT: mouse -- The mouse shape number to set the mouse to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Set_Default_Mouse(MouseType mouse, bool size) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + NormalMouseShape = mouse; + Override_Mouse_Shape(mouse, size); +} + + +/*********************************************************************************************** + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * * + * Use this routine to cancel the effects of Override_Mouse_Shape(). It will revert the * + * mouse back to the original shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/27/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Revert_Mouse_Shape(void) +{ + Override_Mouse_Shape(NormalMouseShape, false); +} + + +/*********************************************************************************************** + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * * + * This routine is called to change the mouse sizing override. If the mouse can change * + * size to that specified, then the mouse imagery will be changed. If a change of imagery * + * cannot occur (due to lack of appropriate artwork), then no action will be performed. * + * * + * INPUT: small -- Should the mouse be made small? If not, then it will be made large. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Mouse_Small(bool wsmall) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (IsSmall == wsmall) { + return; + } + + IsSmall = wsmall; + + if (wsmall) { + if (control->SmallFrame != -1) { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->SmallFrame + Frame/4)); + } else { + Set_Mouse_Cursor(MouseControl[MOUSE_NORMAL].X, MouseControl[MOUSE_NORMAL].Y, Extract_Shape(MouseShapes, MOUSE_NORMAL)); + } + } else { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->StartFrame + Frame/4)); + } +} + + +/*********************************************************************************************** + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * * + * This routine is used to alter the shape of the mouse as needed. * + * Typical mouse shape change occurs when scrolling the map or * + * selecting targets. * + * * + * INPUT: mouse -- The mouse shape number to use. * + * * + * OUTPUT: bool; Was the mouse shape changed? * + * * + * WARNINGS: This is not intended to be used as a means to hide the * + * mouse. Nor will it work correctly if the mouse shape * + * file hasn't been loaded. * + * * + * HISTORY: * + * 03/10/1994 JLB : Created. * + * 06/03/1994 JLB : Made into member function. * + * 12/24/1994 JLB : Added small control parameter. * + *=============================================================================================*/ +#ifdef WIN32 +void Block_Mouse(GraphicBufferClass *buffer); +void Unblock_Mouse(GraphicBufferClass *buffer); +#endif + +bool MouseClass::Override_Mouse_Shape(MouseType mouse, bool wsmall) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + MouseStruct const * control = &MouseControl[mouse]; + static bool startup = false; + int baseshp; + + /* + ** Only certain mouse shapes have a small counterpart. If the requested mouse + ** shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wsmall = false; + } + + /* + ** If the mouse shape is going to change, then inform the mouse driver of the + ** change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wsmall != IsSmall)))) { + startup = true; + + Timer = control->FrameRate; + Frame = 0; + + baseshp = (wsmall) ? control->SmallFrame : control->StartFrame; + if (baseshp == -1) { + baseshp = control->StartFrame; + } + + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseshp)); + CurrentMouseShape = mouse; + IsSmall = wsmall; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MouseClass::AI -- Process player input as it relates to the mouse * + * * + * This routine will is to be called once per game tick and is passed the player keyboard * + * or mouse input code. It processes this code and updates the mouse shape as appropriate. * + * * + * INPUT: input -- The player input code as returned from Keyboard->Get(). * + * * + * x,y -- The mouse coordinate values to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 03/27/1995 JLB : New animation control. * + * 05/28/1995 JLB : Moderates animation so is more steady regardless of speed. * + * 06/30/1995 JLB : Uses constant timer system. * + *=============================================================================================*/ +void MouseClass::AI(KeyNumType &input, int x, int y) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (control->FrameRate && Timer == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer = control->FrameRate; + + if (!IsSmall || control->SmallFrame != -1) { + int baseframe = (IsSmall) ? control->SmallFrame : control->StartFrame; + if (baseframe == -1) baseframe = control->StartFrame; + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseframe + Frame)); + } + } + + ScrollClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * * + * Use this routine to load the mouse data file and perform any other necessary one time * + * preparations for the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine ONCE. * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::One_Time(void) +{ + ScrollClass::One_Time(); + + /* + ** Override the mouse shape file with the one in the current directory, but only if there + ** is an override file available. + */ + #ifndef NDEBUG + RawFileClass file("MOUSE.SHP"); + if (file.Is_Available()) { + MouseShapes = Load_Alloc_Data(file); + } else { + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + } + #else + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + #endif +} + + +/*********************************************************************************************** + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * * + * This routine will reset the mouse handling system. Typically, this routine is called * + * when preparing for the beginning of a new scenario. * + * * + * INPUT: theater -- The theater that the scenario will take place. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Init_Clear(void) +{ + ScrollClass::Init_Clear(); + IsSmall = false; + NormalMouseShape = MOUSE_NORMAL; +} + + +/* +** This array of structures is used to control the mouse animation +** sequences. +*/ +//#ifdef WIN32 +//#define WD 45 +//#define HT 36 +//#else +#define WD 29 +#define HT 23 +//#endif + +MouseClass::MouseStruct MouseClass::MouseControl[MOUSE_COUNT] = { + {0, 1, 0, 80, 0, 0}, // MOUSE_NORMAL + {1, 1, 0, -1, WD/2, 0}, // MOUSE_N + {2, 1, 0, -1, WD, 0}, // MOUSE_NE + {3, 1, 0, -1, WD, HT/2}, // MOUSE_E + {4, 1, 0, -1, WD, HT}, // MOUSE_SE + {5, 1, 0, -1, WD/2, HT}, // MOUSE_S + {6, 1, 0, -1, 0, HT}, // MOUSE_SW + {7, 1, 0, -1, 0, HT/2}, // MOUSE_W + {8, 1, 0, -1, 0, 0}, // MOUSE_NW + + {124, 1, 0, -1, WD/2, 0}, // MOUSE_NO_N + {125, 1, 0, -1, WD, 0}, // MOUSE_NO_NE + {126, 1, 0, -1, WD, HT/2}, // MOUSE_NO_E + {127, 1, 0, -1, WD, HT}, // MOUSE_NO_SE + {128, 1, 0, -1, WD/2, HT}, // MOUSE_NO_S + {129, 1, 0, -1, 0, HT}, // MOUSE_NO_SW + {130, 1, 0, -1, 0, HT/2}, // MOUSE_NO_W + {131, 1, 0, -1, 0, 0}, // MOUSE_NO_NW + + {14, 1, 0, 33, WD/2, HT/2}, // MOUSE_NO_MOVE + {10, 4, 4, 29, WD/2, HT/2}, // MOUSE_CAN_MOVE + {113, 3, 4, 142, WD/2, HT/2}, // MOUSE_ENTER + {59, 9, 4, -1, WD/2, HT/2}, // MOUSE_DEPLOY + {15, 6, 4, -1, WD/2, HT/2}, // MOUSE_CAN_SELECT + {21, 8, 4, 134, WD/2, HT/2}, // MOUSE_CAN_ATTACK + {68, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_BACK + {148, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_UNIT + {35, 24, 2, -1, WD/2, HT/2}, // MOUSE_REPAIR + {120, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + {119, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_SELL_BACK + {81, 1, 0, 145, WD/2, HT/2}, // MOUSE_RADAR_CURSOR + {90, 7, 4, -1, WD/2, HT/2}, // MOUSE_NUCLEAR_BOMB + {82, 8, 2, 213, WD/2, HT/2}, // MOUSE_AIR_STRIKE + {116, 3, 4, 121, WD/2, HT/2}, // MOUSE_DEMOLITIONS + {147, 1, 0, 146, WD/2, HT/2}, // MOUSE_AREA_GUARD + {160, 4, 4, 194, WD/2, HT/2}, // MOUSE_HEAL + {164, 3, 4, 167, WD/2, HT/2}, // MOUSE_DAMAGE + {170, 24, 2, -1, WD/2, HT/2}, // MOUSE_GREPAIR + {195, 8, 4, 203, WD/2, HT/2}, // MOUSE_STAY_ATTACK + {211, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_DEPLOY + {212, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_ENTER + {213, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + + {97, 8, 3, -1, WD/2, HT/2}, // MOUSE_CHRONO_SELECT + {105, 8, 2, -1, WD/2, HT/2}, // MOUSE_CHRONO_DEST + +}; diff --git a/REDALERT/MOUSE.H b/REDALERT/MOUSE.H new file mode 100644 index 000000000..ab2bffb50 --- /dev/null +++ b/REDALERT/MOUSE.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MOUSE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "stage.h" +#include "scroll.h" + +class MouseClass: public ScrollClass +{ + public: + MouseClass(void); + MouseClass(NoInitClass const & x) : ScrollClass(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall=false); + virtual void Revert_Mouse_Shape(void); + virtual MouseType Get_Mouse_Shape(void) const {return NormalMouseShape;}; + virtual void Mouse_Small(bool wsmall); + + /* + ** File I/O. + */ + virtual bool Load(Straw & file); + virtual bool Save(Pipe & file) const; + + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall = false); + + /* + ** This allows the tactical map input gadget access to change the + ** mouse shapes. + */ + friend class TacticalClass; + + /* + ** This points to the loaded mouse shapes. + */ + static void const * MouseShapes; + + private: + + /* + ** This type is used to control the frames and rates of the mouse + ** pointer. Some mouse pointers are actually looping animations. + */ + typedef struct MouseStruct + { + int StartFrame; // Starting frame number. + int FrameCount; // Number of animation frames. + int FrameRate; // Frame delay between changing frames. + int SmallFrame; // Start frame number for small version (if any). + int X,Y; // Hotspot X and Y offset. + } MouseStruct; + + /* + ** The control frames and rates for the various mouse pointers are stored + ** in this static array. + */ + static MouseStruct MouseControl[MOUSE_COUNT]; + + public: + /* + ** If the small representation of the mouse is active, then this flag is true. + */ + unsigned IsSmall:1; + + + private: + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseType CurrentMouseShape; + MouseType NormalMouseShape; + + /* + ** For animating mouse shapes, this controls the frame and animation rate. + */ + static CDTimerClass Timer; + int Frame; +}; + + +#endif diff --git a/REDALERT/MOVIE.H b/REDALERT/MOVIE.H new file mode 100644 index 000000000..888e53333 --- /dev/null +++ b/REDALERT/MOVIE.H @@ -0,0 +1,70 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MPGMOVIE_H_ +#define _MPGMOVIE_H_ +/**************************************************************************** +* +* FILE +* MpgMovie.h +* +* DESCRIPTION +* Movie playback using DirectShow Multimedia streaming and DirectDraw +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 27, 1998 +* +****************************************************************************/ + +#include +#include + +#ifdef MPGEXPORT +#define DLLCALL __declspec(dllexport) +#else +#define DLLCALL __declspec(dllimport) +#endif + +typedef enum + { + MPGCMD_ERROR = -1, + MPGCMD_INIT = 0, + MPGCMD_CLEANUP, + MPGCMD_PALETTE, + MPGCMD_UPDATE + } MPG_CMD; + +typedef enum + { + MPGRES_QUIT = -1, + MPGRES_CONTINUE = 0, + MPGRES_LOSTFOCUS, + } MPG_RESPONSE; + +typedef MPG_RESPONSE (far __stdcall *LPMPGCALLBACK)(MPG_CMD cmd, LPVOID data, LPVOID user); + +extern "C" + { + DLLCALL void __stdcall MpgPlay(const char* name, IDirectDraw* dd, + IDirectDrawSurface* surface, RECT* dstRect); + DLLCALL void __stdcall MpgPause(void); + DLLCALL void __stdcall MpgResume(void); + DLLCALL void __stdcall MpgSetCallback(LPMPGCALLBACK callback, LPVOID user); + } + +#endif // _MPGMOVIE_H_ diff --git a/REDALERT/MP.CPP b/REDALERT/MP.CPP new file mode 100644 index 000000000..663e09240 --- /dev/null +++ b/REDALERT/MP.CPP @@ -0,0 +1,2729 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * memrev -- Reverse the byte order of the buffer specified. * + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * XMP_Add -- Add two MP numbers with a carry option. * + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * XMP_Compare -- Compare one MP number with another. * + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * XMP_Dec -- Decrement the MP number by one. * + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * XMP_Double_Mul -- Double precision MP multiply. * + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * XMP_Inc -- Increment an MP number by one. * + * XMP_Init -- Initialize an MP number to a starting value. * + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * XMP_Move -- Assign one MP number to another. * + * XMP_Neg -- Negate the specified MP number. * + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * XMP_Randomize -- Generate a random MP number. * + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include "mp.h" + + +#ifndef __BORLANDC__ +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define _USERENTRY +#endif + + +/*********************************************************************************************** + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * * + * This utility routine will determine the number of precision bytes exist in the long * + * integer specified. There are some optimizations that can occur if the byte precision * + * is known. * + * * + * INPUT: value -- The value of the long integer that the byte precision will be calculated * + * for. * + * * + * OUTPUT: Returns with the number of bytes that the long integer requires (at a minimum) * + * to cover the precision of the integer. The minimum value will be 1, the maximum * + * will be 4. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +static int _Byte_Precision(unsigned long value) +{ + int byte_count; + for (byte_count = sizeof(value); byte_count; byte_count--) { + if (value >> ((byte_count-1)*8)) break; + } + return(byte_count); +} + + +/*********************************************************************************************** + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * * + * This routine will output the length of the block using Distinguished Encoding Rules. * + * The rest of the block must be filled in as appropriate. For data blocks that are less * + * than 128 bytes long, the header consists of only one byte. Longer buffers lengths * + * can consume up to 5 bytes (depends on magnitude of the length value). * + * * + * INPUT: length -- The length of the data block to be output. * + * * + * output -- Pointer to the memory block that will be set up. * + * * + * OUTPUT: Returns with the number of bytes (header) that was used to store the length * + * value. Subsequent data must be placed after the header. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output) +{ + assert(output != NULL); + + int header_length = 0; + + if (length <= SCHAR_MAX) { + output[header_length++] = (unsigned char)length; + } else { + output[header_length++] = (unsigned char)(_Byte_Precision(length) | 0x80); + for (int byte_counter = _Byte_Precision(length); byte_counter; --byte_counter) { + output[header_length++] = (unsigned char)(length >> ((byte_counter-1)*8)); + } + } + return(header_length); +} + + +/*********************************************************************************************** + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * * + * This routine is used to encode a number into a buffer using Distinguished Encoding * + * Rules. The number of bytes used will be, typically, two bytes more than the number of * + * precision bytes in the number. * + * * + * INPUT: from -- Pointer to the multi-precision number. * + * * + * output -- Pointer to the buffer that will hold the DER encoded number. * + * * + * precision-- The precision of the multi-precision number. * + * * + * OUTPUT: Returns with the number of bytes used in the output buffer. * + * * + * WARNINGS: Make sure the buffer is big enough to hold the DER encoded number. For safety * + * make sure it is precision+6 bytes long. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision) +{ + assert(from != NULL); + assert(output != NULL); + assert(precision > 0); + + unsigned char buffer[MAX_UNIT_PRECISION*sizeof(digit)+1]; + int header_count = 0; + + unsigned number_count = XMP_Encode(buffer, from, precision); + + output[header_count++] = 0x02; + header_count += XMP_DER_Length_Encode(number_count, &output[header_count]); + memcpy(&output[header_count], buffer, number_count); + + return(header_count+number_count); +} + + +/*********************************************************************************************** + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * * + * Use this routine to decode a Distinguished Encoding Rules number into a multi-precision * + * number. This is the counterpart function to the XMP_DER_Encode() function. * + * * + * INPUT: result -- The buffer the hold the result MP number. * + * * + * input -- Pointer to the DER encoded number. * + * * + * precision -- The precision of the MP number. This is the maximum precision the * + * DER number can be. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision) +{ + assert(result != NULL); + assert(input != NULL); + assert(precision > 0); + + if (*input++ == 0x02) { + unsigned byte_count; + + if ((*input & 0x80) == 0) { + byte_count = *input++; + } else { + int length = *input++ & 0x7f; + if (length > 2) return; + byte_count = *input++; + if (length > 1) byte_count = (byte_count << 8) | *input++; + } + if (byte_count <= (precision * sizeof(digit))) { + XMP_Signed_Decode(result, input, byte_count, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer. * + * * + * This routine will encode an multi-precision number into a buffer of specified length. * + * The number of stored in "big endian" format with appropriate sign extension. * + * * + * INPUT: to -- Pointer to the buffer to place the number. * + * * + * tobytes -- The number of bytes to use in the destination buffer. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes placed into the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(tobytes > 0); + assert(precision > 0); + + unsigned frombytes = precision * sizeof(digit); + unsigned char filler = (unsigned char)(XMP_Is_Negative(from, precision) ? 0xff : 0); + + int index; + for (index = 0; index < (int)(tobytes-frombytes); index++) { + *to++ = filler; + } + + const unsigned char * fptr = ((const unsigned char *)from) + min(tobytes, frombytes); + for (index = 0; index < (int)min(tobytes, frombytes); index++) { + *to++ = *--fptr; + } + + return(tobytes); +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * * + * This routine will encode the MP number into the specified buffer. The number will be * + * encoded using the least number of bytes possible. * + * * + * INPUT: to -- The buffer to encode the MP number into. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes used in the destination buffer to hold the * + * encoded number. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the encoded MP number. * + * A safe size would be the precision plus one. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +//#pragma warning 364 9 +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(precision > 0); + + bool is_negative = XMP_Is_Negative(from, precision); + unsigned char filler = (unsigned char)(is_negative ? 0xff : 0); + unsigned char * number_ptr; + + unsigned char * const end = (unsigned char *)from; + for (number_ptr = (unsigned char *)end + precision - 1; number_ptr > (unsigned char *)end; number_ptr--) { + if (*number_ptr != filler) break; + } + + unsigned index = 0; + if (((*number_ptr & 0x80) && !is_negative) || (!(*number_ptr & 0x80) && is_negative)) { + to[index++] = filler; + } + + to[index++] = *number_ptr; + + while (number_ptr != end) { + to[index++] = *--number_ptr; + } + return(index); +} + + +/*********************************************************************************************** + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * * + * Use this routine to convert a coded number back into an MP number. The coded number * + * is presumed to be signed. * + * * + * INPUT: result -- Pointer to the buffer that will hold the decoded MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes consumed by the encoded MP number. * + * * + * precision -- The precision of the MP number (maximum) of the result. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the precision is sufficient to hold the decoded MP number. * + * Otherwise, the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + unsigned char filler = (unsigned char)((*from & 0x80) ? 0xff : 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = filler; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * * + * Use this routine to decode a MP number and treat it as if it were unsigned. * + * * + * INPUT: result -- Pointer to the buffer to hold the result MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes in the encoded number. * + * * + * precision-- The precision of the result MP number -- maximum precision. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the result MP precision is sufficient to hold the decoded number or * + * else the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = '\0'; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * * + * This routine will return with the precision of the MP number expressed as bytes. The * + * MP number is presumed unsigned. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The precision of the MP number to examine. * + * * + * OUTPUT: Returns with the minimum number of bytes consumed by this MP number. * + * * + * WARNINGS: Passing a signed MP number to this routine will return an artificially greater * + * precision than it really is. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Significance(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + number += precision; + do { + if (*(--number)) break; + } while (--precision); + return(precision); +} + + +/*********************************************************************************************** + * XMP_Inc -- Increment an MP number by one. * + * * + * This will increment the MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to increment. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps around the maximum precision, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inc(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + if (++(*number)) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Dec -- Decrement the MP number by one. * + * * + * Use this routine to decrement the specified MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to decrement. * + * * + * precision-- The precision of the MP number to decrement. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps below zero, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Dec(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + *number -= 1; + if ((*number) != ~(digit)0) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Neg -- Negate the specified MP number. * + * * + * This routine will negate (reverse sign) of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to negate. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Neg(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + XMP_Not(number, precision); + XMP_Inc(number, precision); +} + + +/*********************************************************************************************** + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * * + * This will perform the absolute value function on the specified MP number. That is, if * + * the MP number is negative, it will be transformed into a positive number. If the number * + * is already positive, then it will be left alone. * + * * + * INPUT: number -- Pointer to the MP number to ABS. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Abs(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + if (XMP_Is_Negative(number, precision)) { + XMP_Neg(number, precision); + } +} + + +/*********************************************************************************************** + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * * + * Use this routine to perform a right shift of the MP number by the number of bits * + * specified. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift upon. * + * * + * bits -- The number of bits to shift. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is an unsigned shift. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Right_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + number += precision; + digit carry = 0; + while (precision--) { + number--; + digit temp = *number; + *number = carry; + carry = temp; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + number += precision; + digit carry = 0; + digit bitmask = (1L << bits) - 1; + int unbits = UNITSIZE - bits; + + while (precision--) { + number--; + digit temp = *number & bitmask; + *number >>= bits; + *number |= carry << unbits; + carry = temp; + } + return; + } + + /* + ** General purpose slow right. + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number + digits_to_shift) >> bits_to_shift) | (*(number + (digits_to_shift + 1)) << (UNITSIZE - bits_to_shift)); + number++; + } + + if (digits_to_shift < precision) { + *number = (*(number + digits_to_shift) >> bits_to_shift); + number++; + } + + for (index= 0; index < min(digits_to_shift, precision); index++) { + *number++ = 0; + } +} + + +/*********************************************************************************************** + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * * + * Use this routine to perform a left shift of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift operation on. * + * * + * bits -- The number of bits to shift the MP number leftward. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Left_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + digit carry = 0; + while (precision--) { + digit temp = *number; + *number = carry; + carry = temp; + number++; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + digit carry = 0; + digit bitmask = ~(((digit)-1) >> bits); + int unbits = UNITSIZE - bits; /* shift bits must be <= UNITSIZE */ + + while (precision--) { + digit temp = *number & bitmask; + *number = (*number << bits) | (carry >> unbits); + carry = temp; + number++; + } + return; + } + + /* + ** General purpose slow left; + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + number += precision-1; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number - digits_to_shift) << bits_to_shift) | (*(number - (digits_to_shift + 1)) >> (UNITSIZE - bits_to_shift)); + number--; + } + + if (digits_to_shift < precision) { + *number = (*(number - digits_to_shift) << bits_to_shift); + number--; + } + + for (index = 0; index < min(digits_to_shift, precision); index++) { + *number-- = 0; + } +} + + +/*********************************************************************************************** + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * * + * This routine will rotate the MP number to the left by one bit. The rotation passes bits * + * through a "carry" bit position. The initial value of this "carry" bit is passed to the * + * routine and the final value is returned as the result. * + * * + * INPUT: number -- Pointer to the MP number to perform the left rotate upon. * + * * + * carry -- The initial value of the "carry" bit. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: Returns with the final value of the carry bit. This is the the bit value of the * + * upper most bit of the MP number prior to the rotate operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rotate_Left(digit * number, bool carry, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + while (precision--) { + bool temp = ((*number & UPPER_MOST_BIT) != 0); + *number = (*number << 1); + if (carry) *number = *number + 1; + carry = temp; + number++; + } + return carry; +} + + +/*********************************************************************************************** + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * * + * Perform a bitwise NOT operation. * + * * + * INPUT: number -- Pointer to the MP number to operate on. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Not(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + for (int index = 0; index < precision; index++) { + *number = ~(*number); + number++; + } +} + + +/*********************************************************************************************** + * XMP_Init -- Initialize an MP number to a starting value. * + * * + * This will initialize (assign) a number to an MP number. The initial value is limited * + * to the precision allowed by a DIGIT type. * + * * + * INPUT: number -- Pointer to the MP number to initialize. * + * * + * value -- Initial integer value to assign to the MP number. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Init(digit * number, digit value, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + memset(number, '\0', precision * sizeof(digit)); + *number = value; +} + + +/*********************************************************************************************** + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * * + * This routine will count the maximum number of bits used by this MP number. The result * + * could be referred to as the "bit precision" of the MP number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The (digit) precision of the MP number. * + * * + * OUTPUT: Returns with the number of significant bits in the MP number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Count_Bits(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + int sub_precision = XMP_Significance(number, precision); + if (!sub_precision) return(0); + int total_bit_count = XMP_Digits_To_Bits(sub_precision); + number += sub_precision-1; + digit high_bit_mask = UPPER_MOST_BIT; + + while (!((*number) & high_bit_mask)) { + high_bit_mask >>= 1; + total_bit_count--; + } + + return(total_bit_count); +} + + +/*********************************************************************************************** + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * * + * This routine will scan the MP number and determine the number of bytes needed to * + * represent the MP number. Consider the result the "byte precision" of the number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- Precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes required to represent the precision of the number.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Count_Bytes(const digit * number, int precision) +{ + unsigned char * ptr = (unsigned char *)number; + int count = 0; + for (unsigned index = 0; index < precision*sizeof(digit); index++) { + if (!*ptr) break; + count++; + ptr++; + } + return(count); +} + + +/*********************************************************************************************** + * XMP_Move -- Assign one MP number to another. * + * * + * This will move one MP number over the top of another. * + * * + * INPUT: dest -- Destination MP number (will get clobbered). * + * * + * source -- Source MP number. * + * * + * precision-- Precision of both MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Both MP numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Move(digit * dest, digit const * source, int precision) +{ + memcpy(dest, source, precision * sizeof(digit)); +} + + +/*********************************************************************************************** + * XMP_Compare -- Compare one MP number with another. * + * * + * This routine will compare two MP numbers. It will return a value indicating which MP * + * number is greater or if they are equal. * + * * + * INPUT: left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns -1 if the left_number is less than the right_number. * + * Returns 1 if the left_number is greater than the right number. * + * Returns 0 if both numbers are identical. * + * * + * WARNINGS: Both numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Compare(const digit * left_number, const digit * right_number, int precision) +{ + left_number += precision-1; + right_number += precision-1; + do { + if (*left_number < *right_number) return -1; + if (*left_number > *right_number) return 1; + left_number--; + right_number--; + } while (--precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Add -- Add two MP numbers with a carry option. * + * * + * Use this routine to add one MP number to another. There is an optional "carry" value * + * that (when true) will add an additional 1 to the result. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. This can be the * + * same value as the left_number or right_number pointers. * + * * + * left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * carry -- Optional carry flag (typically this will be false). * + * * + * precision -- The precision of the numbers involved. * + * * + * OUTPUT: Returns with the carry flag after the addition. If the value is true then an * + * overflow occurred. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add(digit * result, const digit * left_number, const digit * right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + *right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number++; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * * + * This routine will add an integer number to an MP number. There is an optional carry * + * parameter. If the carry flag is true, and additional 1 will be added to the result. * + * This routine is much faster than adding two MP numbers together. * + * * + * INPUT: result -- Pointer to the result MP number. This pointer can be the same as * + * the left_number parameter. * + * * + * left_number -- Pointer to the left hand MP number. * + * * + * right_number-- The integer number to add to the left hand number. * + * * + * carry -- Input carry flag. If this is true, then an additional one will be * + * added to the result. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns with the result carry flag. A true value means the addition overflowed. * + * * + * WARNINGS: All MP numbers must share the same precision. Negative numbers are not * + * supported. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add_Int(digit * result, const digit * left_number, digit right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number = 0; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * * + * This routine will subtract one MP number from another. There is an optional borrow * + * flag that can be specified. * + * * + * INPUT: result -- Pointer to the MP number that will hold the result. This pointer * + * can be the same as the left_number or right_number parameters. * + * * + * left_number -- The left hand number (value will be subtracted from this). * + * * + * right_number-- The right hand number (the value to subtract from the left number) * + * * + * borrow -- The optional borrow flag. If this flag is true, the an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow result flag. If the value is true, then an underflow * + * occurred during subtraction. * + * * + * WARNINGS: All MP numbers must share the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub(digit * result, const digit * left_number, const digit * right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + const unsigned short * right_number_ptr = (const unsigned short *)right_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - (digit) *right_number_ptr - (digit) borrow; + right_number_ptr++; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * * + * This will subtract an integer from the specified MP number. There is an optional borrow * + * flag available. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * left_number -- Pointer to the MP number that will be subtracted FROM. * + * * + * right_number-- The integer to subtract from the left hand number. * + * * + * borrow -- The optional borrow flag. If this value is true, then an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow flag of the result. If this value is true, then an * + * underflow occurred during subtraction. * + * * + * WARNINGS: The precision must be identical between the MP numbers involved. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub_Int(digit * result, const digit * left_number, unsigned short right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - right_number - borrow; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + + right_number = 0; + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * * + * This routine will multiply two MP numbers together. The result will have the sum of the * + * significance of the two. * + * * + * INPUT: prod -- Pointer to the product MP buffer that will hold the result. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product will fit within the precision of the result. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Init(prod, 0, precision); + + /* + ** Multiplying by zero is always a zero product. + */ + if (XMP_Test_Eq_Int(multiplicand, 0, precision) || XMP_Test_Eq_Int(multiplier, 0, precision)) { + return 0; + } + + int total_bit_count = XMP_Count_Bits(multiplier, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + multiplier += sub_precision; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(prod, 1, precision); + + if ((*(multiplier-1)) & high_bit_mask) { + XMP_Add(prod, prod, multiplicand, 0, precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + multiplier--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * * + * This is a very fast multiply since the multiplier is just an integer integral. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that is the multiplicand. * + * * + * multiplier -- The integral integer that is the multiplier. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fit in a signed integer (although it isn't a signed value). * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision) +{ + const unsigned short * m2 = (const unsigned short *)multiplicand; + unsigned short * pr = (unsigned short *)prod; + unsigned long carry = 0; + for (int i = 0; i < precision*2; ++i) { + unsigned long p = (((unsigned long)multiplier) * *m2) + carry;; + *pr = (unsigned short) p; + carry = p >> 16; + m2++; + pr++; + } + + /* Add carry to the next higher word of product / dividend */ +// *pr += (unsigned short)carry; + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * * + * This will multiply the specified integer with the MP number. It is a much faster * + * multiply than when multiplying two MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that serves as the multiplicand. * + * * + * multiplier -- The simple integral integer used as the multiplier. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fist within a signed short integer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + } else { + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * * + * This routine will perform a multiply between two signed MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is not a very fast routine. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, abs_multiplicand, abs_multiplier, precision); + } else { + XMP_Unsigned_Mult(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, multiplicand, abs_multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * * + * This routine performs a fast divide of the specified MP dividend by a simple * + * short integer. The division is an UNSIGNED division however. * + * * + * INPUT: quotient -- Pointer to the MP number buffer where the quotient will go. * + * * + * dividend -- Pointer to the MP number that serves as the dividend. * + * * + * divisor -- The simple signed short integer that serves as the divisor. * + * * + * precision -- The precision that is used by the MP numbers involved. * + * * + * OUTPUT: Returns with the remainder of the division. * + * * + * WARNINGS: This is an UNSIGNED divide even. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision) +{ + if (!divisor) return 0; /* zero divisor means divide error */ + + unsigned short remainder = 0; + + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + digit const * dividend_ptr = dividend + (digit_precision-1); + + if (!digit_precision) return(0); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + digit * quotient_ptr = quotient + (digit_precision-1); + + while (total_bit_count--) { + remainder <<= 1; + + if ((*dividend_ptr) & high_bit_mask) remainder++; + + if (remainder >= divisor) { + remainder -= divisor; + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + --dividend_ptr; + --quotient_ptr; + } + } + + return(remainder); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * * + * This will perform the (dog slow) divide of one MP number into another. Because of the * + * slowness of this routine, both the quotient and the remainder are available as a * + * result of the operation. * + * * + * INPUT: remainder -- Pointer to the MP buffer that will hold the remainder of the divide.* + * * + * quotient -- Pointer to the MP buffer that will hold the quotient of the divide. * + * * + * dividend -- The MP dividend (numerator) number. * + * * + * divisor -- The MP divisor (denominator) number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + // check for divide by zero. + if (XMP_Test_Eq_Int(divisor, 0, precision)) return(-1); + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + if (!digit_precision) return(0); + + digit const * dividend_ptr = dividend + (digit_precision-1); + digit * quotient_ptr = quotient + (digit_precision-1); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + + if (((*dividend_ptr) & high_bit_mask) != 0) { + XMP_Inc(remainder, precision); + } + + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + dividend_ptr--; + quotient_ptr--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * * + * This will perform a signed divide (very very slow) of one MP number into another. * + * Because of the slow nature of this routine, both the quotient and the remainder are * + * available as results. * + * * + * INPUT: remainder -- Pointer to the buffer that will hold the remainder of the divide. * + * * + * quotient -- Pointer to the buffer that will hold the quotient of the divide. * + * * + * dividend -- The dividend (numerator) MP number. * + * * + * divisor -- The divisor (denominator) MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + bool negative = false; + + digit scratch_dividend[MAX_UNIT_PRECISION]; + XMP_Move(scratch_dividend, dividend, precision); + + digit scratch_divisor[MAX_UNIT_PRECISION]; + XMP_Move(scratch_divisor, divisor, precision); + + if (XMP_Is_Negative(scratch_dividend, precision)) { + XMP_Neg(scratch_dividend, precision); + negative = !negative; + } + + if (XMP_Is_Negative(scratch_divisor, precision)) { + XMP_Neg(scratch_divisor, precision); + negative = !negative; + } + + XMP_Unsigned_Div(remainder, quotient, scratch_dividend, scratch_divisor, precision); + + if (negative) { + XMP_Neg(quotient, precision); + if (!XMP_Test_Eq_Int(remainder, 0, precision)) { + XMP_Dec(quotient, precision); + XMP_Neg(remainder, precision); + XMP_Add(remainder, remainder, scratch_divisor, 0, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * * + * This is a utility routine that will perform an inverse on the MP number and then * + * perform a modulus of that number by another MP number. There are some algorithms that * + * require this process. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * number -- The MP number that will be inverted then modulo-ized. * + * * + * modulus -- The MP number to modulus the first number by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision) +{ + digit g[3][MAX_UNIT_PRECISION]; + XMP_Move(g[0], modulus, precision); + XMP_Move(g[1], number, precision); + + digit v[3][MAX_UNIT_PRECISION]; + XMP_Init(v[0], 0, precision); + XMP_Init(v[1], 1, precision); + + digit y[MAX_UNIT_PRECISION]; + + int i; + for (i = 1; !XMP_Test_Eq_Int(g[i%3], 0, precision); i++) { + XMP_Unsigned_Div(g[(i+1)%3], y, g[(i-1)%3], g[i%3], precision); + + XMP_Unsigned_Mult(result, v[i%3], y, precision); + XMP_Sub(v[(i+1)%3], v[(i-1)%3], result, 0, precision); + } + + if (XMP_Is_Negative(v[(i-1)%3], precision)) { + XMP_Add(v[(i-1)%3], v[(i-1)%3], modulus, 0, precision); + } + + XMP_Move(result, v[(i-1)%3], precision); +} + + +/*********************************************************************************************** + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * * + * Use this routine to determine the inverse of the specified MP number. The inverse is * + * defined as 1/number. * + * * + * INPUT: result -- Pointer to the result MP number buffer. * + * * + * number -- The number to be inverted. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision) +{ + digit remainder[MAX_UNIT_PRECISION]; + + if (XMP_Test_Eq_Int(divisor, 0, precision)) return -1; /* zero divisor means divide error */ + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + /* normalize and compute number of bits in quotient first */ + unsigned total_bit_count = XMP_Count_Bits(divisor, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count + 1); /* bitmask within a single digit */ + int sub_precision = XMP_Bits_To_Digits(total_bit_count + 1); + + XMP_Set_Bit(remainder, total_bit_count - 1); + + /* rescale quotient to precision of divisor bits */ + quotient += sub_precision-1; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + quotient--; + } + } + + XMP_Init(remainder, 0, precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * * + * This routine will convert a supplied ASCII string into an MP number. * + * * + * INPUT: str -- Pointer to the ASCII string that will be converted. * + * * + * mpn -- Pointer to the MP number buffer that will be initialized. * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision) +{ + /* + ** Initialize the multiprecision number to zero. From this point + ** onward, this object can be manipulated as a regular number. + ** This is, in fact, what is done as the ascii string is parsed + ** into a working number. + */ + XMP_Init(mpn, 0, precision); + + /* + ** No string or zero length is considered '0'. + */ + if (!str) return; + int i = strlen(str); + if (i == 0) return; + + unsigned short radix; /* base 2-16 */ + switch (toupper(str[i-1])) { /* classify radix select suffix character */ + case '.': + radix = 10; + break; + + case 'H': + radix = 16; + break; + + case 'O': + radix = 8; + break; + + case 'B': /* caution! 'b' is a hex digit! */ + radix = 2; + break; + + default: + radix = 10; + break; + } + + bool minus = (*str == '-'); + if (minus) str++; + + digit c; + while ((c = (unsigned char)*str++) != 0) { + if (c == ',') continue; /* allow commas in number */ + + /* + ** If not a hexadecimal (highest base) digit then it is + ** clearly the end of the processable string. Bail out + ** of the scan loop. + */ + if (!isxdigit((char)c)) break; + + /* + ** Convert the character into an integer number 0 through 15. + */ + if (isdigit((char)c)) { + c -= '0'; + } else { + c = (unsigned char)(toupper((char)c) - 'A') + 10; + } + + /* + ** If the integer digit is greater than the radix, then we + ** know that further processing should stop. This is the + ** end of the number string. + */ + if (c >= radix) break; /* scan terminated by any non-digit */ + + + XMP_Unsigned_Mult_Int(mpn, mpn, radix, precision); + XMP_Add_Int(mpn, mpn, c, 0, precision); + } + if (minus) { + XMP_Neg(mpn, precision); + } +} + + +/*********************************************************************************************** + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * * + * Multiply the single-word multiplier times the multiprecision integer * + * in multiplicand, accumulating result in prod. The resulting * + * multiprecision prod will be 1 word longer than the multiplicand. * + * multiplicand is double precision words long. We add into prod, so caller * + * should zero it out first. For best results, this time-critical * + * function should be implemented in assembly. * + * NOTE: Unlike other functions in the multiprecision arithmetic * + * library, both multiplicand and prod are pointing at the LSB, * + * regardless of byte order of the machine. On an 80x86, this makes * + * no difference. But if this assembly function is implemented * + * on a 680x0, it becomes important. * + * * + * Note that this has been modified from the previous version to allow * + * better support for Smith's modmult: * + * The final carry bit is added to the existing product * + * array, rather than simply stored. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand -- Pointer to the multiplicand MP number. * + * * + * multiplier -- The short integer used as the multiplier. * + * * + * precision -- The precision of the MP number used. * + * * + * OUTPUT: none * + * * + * WARNINGS: The carry (if any) is added into the integer one beyond the end of the * + * product buffer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Hybrid_Mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision) +{ + unsigned long carry = 0; + for (int i = 0; i < precision; ++i) { + unsigned long p = (unsigned long)multiplier * *multiplicand++; + p += *prod + carry; + *prod++ = (unsigned short) p; + carry = p >> 16; + } + + /* Add carry to the next higher word of product / dividend */ + *prod += (unsigned short) carry; +} + + +/*********************************************************************************************** + * XMP_Double_Mul -- Double precision MP multiply. * + * * + * This will perform a double precision multiply of MP numbers. This means that the product * + * will be twice the precision of the components. * + * * + * INPUT: prod -- Pointer to the result buffer. This buffer must be able to hold * + * double the precision specified. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier number. * + * * + * precision -- The precision of the two component MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product buffer can hold a double precision number. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + /* + ** Clear out the double precision product buffer. + */ + XMP_Init(prod, 0, precision*2); + + const unsigned short * multiplier_ptr = (const unsigned short *) multiplier; + unsigned short * product_ptr = (unsigned short *) prod; + + // Multiply multiplicand by each word in multiplier, accumulating prod. + for (int i = 0; i < precision*2; ++i) { + XMP_Hybrid_Mul(product_ptr++, (unsigned short *)multiplicand, *multiplier_ptr++, precision*2); + } +} + + + +static int _modulus_shift; // number of bits for recip scaling +static unsigned short _reciprical_high_digit; // MSdigit of scaled recip +static unsigned short _reciprical_low_digit; // LSdigit of scaled recip + +static int _modulus_sub_precision; // length of modulus in MULTUNITs +static int _modulus_bit_count; // number of modulus significant bits +static digit _scratch_modulus[MAX_UNIT_PRECISION]; // modulus + +// The double precision modulus staging buffer. +static digit _double_staging_number[MAX_UNIT_PRECISION * 2 + 2]; + +// most significant digits of modulus. +static digit _mod_quotient[4]; +static digit _mod_divisor[4]; + + +/*********************************************************************************************** + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * * + * Calculate the reciprocal of modulus with a precision of two MULTUNITs. * + * Assumes that precision has already been adjusted to the * + * size of the modulus, plus SLOP_BITS. * + * * + * Note: This routine was designed to work with large values and * + * doesn't have the necessary testing or handling to work with a * + * modulus having less than three significant digits. For such cases, * + * the separate multiply and modulus routines can be used. * + * * + * INPUT: modulus -- Pointer to the modulus number. * + * * + * precision-- The precision of the modulus number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Prepare_Modulus(const digit * n_modulus, int precision) +{ + XMP_Move(_scratch_modulus, n_modulus, precision); + + _modulus_bit_count = XMP_Count_Bits(_scratch_modulus, precision); + _modulus_sub_precision = (_modulus_bit_count + 16 - 1) / 16; + + /* + ** Keep 2*16 bits in _mod_divisor. + ** This will (normally) result in a reciprocal of 2*16+1 bits. + */ + int sub_precision = XMP_Significance(_scratch_modulus, precision); // significant digits in modulus + XMP_Move(_mod_divisor, &_scratch_modulus[sub_precision-2], 2); + _modulus_shift = XMP_Count_Bits(_mod_divisor, 2) - 2 * 16; + XMP_Shift_Right_Bits(_mod_divisor, _modulus_shift, 2); + + XMP_Reciprocal(_mod_quotient, _mod_divisor, 2); + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + + /* Reduce to: 0 < _modulus_shift <= 16 */ + _modulus_shift = ((_modulus_shift + (16 - 1)) % 16) + 1; + + /* round up */ + XMP_Inc(_mod_quotient, 2); + if (XMP_Count_Bits(_mod_quotient, 2) > 2 * 16) { + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + _modulus_shift--; /* now 0 <= _modulus_shift <= 16 */ + } + unsigned short * mpm = (unsigned short *) _mod_quotient; + _reciprical_low_digit = *mpm++; + _reciprical_high_digit = *mpm; + + return 0; +} + + +/*********************************************************************************************** + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * * + * This routine will combine a multiply and a modulus operation. This takes advantage of * + * a tremendous speed advantage possible if these two processes are combined rather than * + * being performed separately. * + * * + * INPUT: prod -- Pointer to the MP buffer that will hold the result. * + * * + * multiplicand-- The number to multiply. * + * * + * multiplier -- The number to multiply by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The modulus must already have been prepared by the routine XMP_Prepare_Modulus. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Double_Mul(_double_staging_number, multiplicand, multiplier, precision); + + int double_precision = precision * 2 + 1; + + _double_staging_number[double_precision - 1] = 0; /* leading 0 digit */ + + /* + ** We now start working with MULTUNITs. + ** Determine the most significant MULTUNIT of the product so we don't + ** have to process leading zeros in our divide loop. + */ + int dmi = XMP_Significance(_double_staging_number, double_precision) * 2; // number of significant MULTUNITs in product + + if (dmi >= _modulus_sub_precision) { + + /* Make dividend negative. This allows the use of mp_single_mul to + ** "subtract" the product of the modulus and the trial divisor + ** by actually adding to a negative dividend. + ** The one's complement of the dividend is used, since it causes + ** a zero value to be represented as all ones. This facilitates + ** testing the result for possible overflow, since a sign bit + ** indicates that no adjustment is necessary, and we should not + ** attempt to adjust if the result of the addition is zero. + */ + XMP_Inc(_double_staging_number, double_precision); + XMP_Neg(_double_staging_number, double_precision); + + int nqd = dmi + 1 - _modulus_sub_precision; // number of quotient digits remaining to be generated + + /* Set msb, lsb, and normal ptrs of dividend */ + unsigned short * dmph = ((unsigned short *)_double_staging_number) + dmi + 1; // points to one higher than precision would indicate + unsigned short * dmpl = dmph - _modulus_sub_precision; + + /* + ** Divide loop. + ** Each iteration computes the next quotient MULTUNIT digit, then + ** multiplies the divisor (modulus) by the quotient digit and adds + ** it to the one's complement of the dividend (equivalent to + ** subtracting). If the product was greater than the remaining dividend, + ** we get a non-negative result, in which case we subtract off the + ** modulus to get the proper negative remainder. + */ + for (; nqd; nqd--) { + --dmph; + --dmpl; + + unsigned short q = mp_quo_digit(dmph); // trial quotient digit + if (q > 0) { + XMP_Hybrid_Mul(dmpl, (unsigned short *)_scratch_modulus, q, precision*2); + + /* Perform correction if q too large. + ** This rarely occurs. + */ + if (!(*dmph & SEMI_UPPER_MOST_BIT)) { + unsigned short * dmp = dmpl; + if (XMP_Sub((unsigned long *)dmp, (unsigned long *)dmp, _scratch_modulus, false, precision)) { + (*dmph)--; + } + } + } + } + + /* d contains the one's complement of the remainder. */ + XMP_Neg(_double_staging_number, precision); + XMP_Dec(_double_staging_number, precision); + } + + XMP_Move(prod, _double_staging_number, precision); + return (0); +} + + +/*********************************************************************************************** + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * * + * Smith's mp_modmult function leaves some internal arrays in memory, * + * so we have to call modmult_burn() at the end of mp_exponent_mod. * + * This is so that no cryptographically sensitive data is left in memory * + * after the program exits. * + * * + * INPUT: precision -- The precision of the numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Mod_Mult_Clear(int precision) +{ + XMP_Init(_scratch_modulus, 0, precision); + XMP_Init(_double_staging_number, 0, precision); + XMP_Init(_mod_quotient, 0, ARRAY_SIZE(_mod_quotient)); + XMP_Init(_mod_divisor, 0, ARRAY_SIZE(_mod_divisor)); + _modulus_shift = _modulus_bit_count = 0; + _reciprical_high_digit = _reciprical_low_digit = 0; + _modulus_sub_precision = /*mutemp =*/ 0; +} + + +/* +** The function mp_quo_digit is the heart of Smith's modulo reduction, +** which uses a form of long division. It computes a trial quotient +** "digit" (MULTUNIT-sized digit) by multiplying the three most +** significant MULTUNITs of the dividend by the two most significant +** MULTUNITs of the reciprocal of the modulus. Note that this function +** requires that 16 * 2 <= sizeof(unsigned long). +** +** An important part of this technique is that the quotient never be +** too small, although it may occasionally be too large. This was +** done to eliminate the need to check and correct for a remainder +** exceeding the divisor. It is easier to check for a negative +** remainder. The following technique rarely needs correction for +** MULTUNITs of at least 16 bits. +** +** The following routine has two implementations: +** +** Parameter: dividend - points to the most significant MULTUNIT +** of the dividend. Note that dividend actually contains the +** one's complement of the actual dividend value (see comments for +** XMP_Mod_Mult). +** +** Return: the trial quotient digit resulting from dividing the first +** three MULTUNITs at dividend by the upper two MULTUNITs of the +** modulus. +*/ +unsigned short mp_quo_digit(unsigned short * dividend) +{ + unsigned long q, q0, q1, q2; + + /* + * Compute the least significant product group. + * The last terms of q1 and q2 perform upward rounding, which is + * needed to guarantee that the result not be too small. + */ + q1 = (dividend[-2] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit + _reciprical_high_digit; + q2 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit + (1L << 16); + q0 = (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the middle significant product group. */ + q1 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit; + q2 = (dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit; + q = (q0 >> 16) + (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the most significant term and add in the others */ + q = (q >> (16 - 2)) + (((dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit) << 1); + q >>= _modulus_shift; + + /* Prevent overflow and then wipe out the intermediate results. */ + return (unsigned short) min(q, (unsigned long)(1L << 16) - 1); +} + + +/* +** Russian peasant combined exponentiation/modulo algorithm. +** Calls modmult instead of mult. +** Computes: expout = (expin**exponent) mod modulus +** WARNING: All the arguments must be less than the modulus! +*/ +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision) +{ + digit product[MAX_UNIT_PRECISION]; + + XMP_Init(expout, 1, precision); + if (XMP_Test_Eq_Int(exponent_ptr, 0, precision)) { + if (XMP_Test_Eq_Int(expin, 0, precision)) { + return -1; /* 0 to the 0th power means return error */ + } + return 0; /* otherwise, zero exponent means expout is 1 */ + } + + if (XMP_Test_Eq_Int(modulus, 0, precision)) { + return -2; /* zero modulus means error */ + } + + if (XMP_Compare(expin, modulus, precision) >= 0) { + return -3; /* if expin >= modulus, return error */ + } + + if (XMP_Compare(exponent_ptr, modulus, precision) >= 0) { + return -4; /* if exponent >= modulus, return error */ + } + + /* set smallest optimum precision for this modulus */ + int limited_precision = XMP_Significance(modulus, precision); + + if (XMP_Prepare_Modulus(modulus, limited_precision)) { + return -5; /* unstageable modulus (STEWART algorithm) */ + } + + /* normalize and compute number of bits in exponent first */ +// int exp_precision = XMP_Significance(exponent_ptr, limited_precision); +// if (!exp_precision) return(0); +// int bits = XMP_Digits_To_Bits(exp_precision); +// exponent_ptr += (exp_precision-1); +// digit high_bit_mask = UPPER_MOST_BIT; +// while (! ((*exponent_ptr) & high_bit_mask)) { +// high_bit_mask >>= 1; +// bits--; +// } + + int total_bit_count = XMP_Count_Bits(exponent_ptr, limited_precision); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + exponent_ptr += (sub_precision-1); + + /* We can "optimize out" the first modsquare and modmult: */ + total_bit_count--; /* We know for sure at this point that bits>0 */ + + XMP_Move(expout, expin, limited_precision); + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + while (total_bit_count--) { + XMP_Mod_Mult(product, expout, expout, limited_precision); + + if (((*exponent_ptr) & high_bit_mask)) { + XMP_Mod_Mult(expout, product, expin, limited_precision); + } else { + XMP_Move(expout, product, limited_precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + } + + XMP_Init(product, 0, limited_precision); + XMP_Mod_Mult_Clear(limited_precision); /* ask mp_modmult to also burn its own evidence */ + + return 0; +} + + +/*********************************************************************************************** + * memrev -- Reverse the byte order of the buffer specified. * + * * + * This routine will reverse the byte order in the buffer specified. * + * * + * INPUT: buffer -- Pointer to the buffer that will be reversed. * + * * + * length -- The length of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void memrev(char * buffer, size_t length) +{ + char * r2 = &(buffer[length - 1]); + while (buffer < r2) { + char b = *buffer; + *buffer++ = *r2; + *r2-- = b; + } +} + + +int _USERENTRY pfunc(const void * pkey, const void * base) +{ + if (*(unsigned short *)pkey < *(unsigned short *)base) return(-1); + if (*(unsigned short *)pkey > *(unsigned short *)base) return(1); + return(0); +} + + +/*********************************************************************************************** + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * * + * This routine will compare the MP number against all known small prime numbers. It will * + * return true if a match was found. * + * * + * INPUT: candidate -- Pointer to MP number that is to be tested. * + * * + * precision -- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the MP number a member of the small prime community? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Small_Prime(const digit * candidate, int precision) +{ + /* + ** If the number is too large for comparison to the known small primes table, then + ** bail immediately. + */ + if (XMP_Significance(candidate, precision) > 1) return(false); + if (*candidate > primeTable[ARRAY_SIZE(primeTable)-1]) return false; + + unsigned long * ptr = (unsigned long *)bsearch(&candidate, &primeTable[0], ARRAY_SIZE(primeTable), sizeof(primeTable[0]), pfunc); + return(ptr != NULL); +} + + +/*********************************************************************************************** + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * * + * This test for primality will divide an MP number by the set of small primes. If any of * + * these numbers divides evenly into the candidate number, then it is known that the * + * candidate is NOT prime. * + * * + * INPUT: candidate -- Pointer to the MP number that is to be tested. * + * * + * precision -- The precision of the MP number/ * + * * + * OUTPUT: bool; Did the MP number pass the small divisors test? * + * * + * WARNINGS: If the MP number passes, it doesn't mean that it is prime, just that is hasn't * + * yet been proven to be not prime. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Small_Divisors_Test(const digit * candidate, int precision) +{ + digit quotient[MAX_UNIT_PRECISION]; + + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + if (XMP_Unsigned_Div_Int(quotient, candidate, primeTable[i], precision) == 0) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * * + * This is a more expensive but thorough test for primality. The aggressiveness of this * + * test can be controlled by the number of rounds specified. Four rounds is usually * + * sufficient. * + * * + * INPUT: candidate -- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of rounds to test the MP number (keep it small). * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: bool; Was the number not proven to be not prime. A FALSE means that it is not * + * prime. A TRUE means that it might be prime. * + * * + * WARNINGS: This takes a bit of time. The time it takes is directly controlled by the * + * number of rounds specified. Keep the number of rounds as small as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision) +{ + assert(rounds < ARRAY_SIZE(primeTable)); + + digit term[MAX_UNIT_PRECISION]; + XMP_Move(term, candidate_prime, precision); + XMP_Dec(term, precision); + + for (unsigned i = 0; i < rounds; i++) { + // if ((x**(p-1)) mod p) != 1, then p is not prime + digit result[MAX_UNIT_PRECISION]; + + digit small_prime[MAX_UNIT_PRECISION]; + XMP_Init(small_prime, primeTable[i], precision); + + xmp_exponent_mod(result, small_prime, term, candidate_prime, precision); + + if (!XMP_Test_Eq_Int(result, 1, precision)) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * * + * This test for primality is even more expensive the Fermat's Little Theorem. It doesn't * + * prove that a number is prime, but it can prove that it is not prime. * + * * + * INPUT: rng -- Reference to to a random number generator. * + * * + * candidate-- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of test rounds to perform. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? A FALSE means that the number is * + * not prime. A TRUE means that it might be. * + * * + * WARNINGS: This routine takes a long time. Use as few rounds as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision) +{ + digit wminus1[MAX_UNIT_PRECISION]; + XMP_Sub_Int(wminus1, w, 1, 0, precision); + + unsigned maxbitprecision = precision * sizeof(digit) * 8; + unsigned a; + for (a = 0; a < maxbitprecision; a++) { + if (XMP_Test_Bit(wminus1, a)) { + break; + } + } + + digit m[MAX_UNIT_PRECISION]; + XMP_Move(m, wminus1, precision); + XMP_Shift_Right_Bits(wminus1, a, precision); + + for (int i = 0; i < rounds; i++) { + digit b[MAX_UNIT_PRECISION]; + digit temp[MAX_UNIT_PRECISION]; + XMP_Init(temp, 2, precision); + XMP_Randomize(b, rng, temp, wminus1, precision); + + digit z[MAX_UNIT_PRECISION]; + xmp_exponent_mod(z, b, m, w, precision); + + if (XMP_Test_Eq_Int(z, 1, precision) || XMP_Compare(z, wminus1, precision) == 0) { + continue; // passes this round + } + + int j; + for (j = 1; j < (int)a; j++) { + digit t2[MAX_UNIT_PRECISION]; + xmp_exponent_mod(t2, z, temp, w, precision); + + if (XMP_Compare(t2, wminus1, precision) == 0) { + break; // passed this round + } + if (XMP_Test_Eq_Int(z, 1, precision)) { + return false; + } + } + if (j == a) { + return false; + } + } + return true; +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number. * + * * + * This routine will generate a random MP number with the number of bits precision * + * specified. This is the starting point for generating large random prime numbers. It is * + * very important that the random number generated is truly random. * + * * + * INPUT: result -- Pointer to the buffer that will hold the MP number. * + * * + * rng -- Reference to a random number generator. * + * * + * total_bits-- The number of bits precision that the MP number must have. * + * * + * precision-- The precision of the MP number to be generated (maximum) * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, int total_bits, int precision) +{ + assert(XMP_Bits_To_Digits(total_bits) <= MAX_UNIT_PRECISION); + + total_bits = min(total_bits, precision * 32); + + unsigned nbytes = total_bits/8 + 1; + + XMP_Init(result, 0, precision); + rng.Get(result, nbytes); + + ((unsigned char *)result)[nbytes-1] &= (unsigned char)(~((~0) << (total_bits % 8))); +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * * + * This routine will generate a random MP number but it will be bounded by the minimum * + * and maximum MP numbers specified. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * rng -- Reference to a random number generator to use. * + * * + * minval -- Minimum value allowed. * + * * + * maxval -- Maximum value allowed. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, digit const * minval, digit const * maxval, int precision) +{ + digit range[MAX_UNIT_PRECISION]; + XMP_Sub(range, maxval, minval, 0, precision); + unsigned int bit_count = XMP_Count_Bits(range, precision); + do { + XMP_Randomize(result, rng, bit_count, precision); + } while (XMP_Compare(result, range, precision) > 0); + + XMP_Add(result, result, minval, 0, precision); +} + + +/*********************************************************************************************** + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * * + * This routine will perform some checks to try and determine if the specified MP number * + * is a prime number. The result of this test is not 100% conclusive, but it is pretty * + * darn close. * + * * + * INPUT: prime -- Pointer to a candidate number to test for primality. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? If FALSE, then the number is * + * not prime. If TRUE, then it might be. * + * * + * WARNINGS: This can take a very very very very very long time. Especially for the larger * + * numbers. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Prime(digit const * prime, int precision) +{ + /* + ** Even numbers are ALWAYS not prime. + */ + if (!(*prime & 0x01)) return(false); + + /* + ** Compare the prime number against the exhaustive list of prime + ** numbers below 14 bits in size. If it finds a match, then + ** the number is a known prime. + */ + if (XMP_Is_Small_Prime(prime, precision)) return(true); + + /* + ** Perform the small divisors test. This is not exhaustive, but + ** will weed out a large percentage of non-prime numbers. + */ + if (!XMP_Small_Divisors_Test(prime, precision)) return(false); + + /* + ** Perform Fermat's Little Theorum on the candidate prime. Run + ** the theorum for several rounds to ensure a high degree of + ** confidence. + */ + if (!XMP_Fermat_Test(prime, 2, precision)) return(false); + + /* + ** If all of the above tests have not confirmed primality nor + ** confirmed non-primality, presume that the number must be prime. + */ + return(true); +} + + +/* +** Complete list of all prime numbers that are less than 32719 (inclusive). +*/ +unsigned short primeTable[3511] = { + 0x0002,0x0003,0x0005,0x0007,0x000B,0x000D,0x0011,0x0013,0x0017,0x001D,0x001F,0x0025,0x0029,0x002B,0x002F,0x0035, + 0x003B,0x003D,0x0043,0x0047,0x0049,0x004F,0x0053,0x0059,0x0061,0x0065,0x0067,0x006B,0x006D,0x0071,0x007F,0x0083, + 0x0089,0x008B,0x0095,0x0097,0x009D,0x00A3,0x00A7,0x00AD,0x00B3,0x00B5,0x00BF,0x00C1,0x00C5,0x00C7,0x00D3,0x00DF, + 0x00E3,0x00E5,0x00E9,0x00EF,0x00F1,0x00FB,0x0101,0x0107,0x010D,0x010F,0x0115,0x0119,0x011B,0x0125,0x0133,0x0137, + 0x0139,0x013D,0x014B,0x0151,0x015B,0x015D,0x0161,0x0167,0x016F,0x0175,0x017B,0x017F,0x0185,0x018D,0x0191,0x0199, + 0x01A3,0x01A5,0x01AF,0x01B1,0x01B7,0x01BB,0x01C1,0x01C9,0x01CD,0x01CF,0x01D3,0x01DF,0x01E7,0x01EB,0x01F3,0x01F7, + 0x01FD,0x0209,0x020B,0x021D,0x0223,0x022D,0x0233,0x0239,0x023B,0x0241,0x024B,0x0251,0x0257,0x0259,0x025F,0x0265, + 0x0269,0x026B,0x0277,0x0281,0x0283,0x0287,0x028D,0x0293,0x0295,0x02A1,0x02A5,0x02AB,0x02B3,0x02BD,0x02C5,0x02CF, + 0x02D7,0x02DD,0x02E3,0x02E7,0x02EF,0x02F5,0x02F9,0x0301,0x0305,0x0313,0x031D,0x0329,0x032B,0x0335,0x0337,0x033B, + 0x033D,0x0347,0x0355,0x0359,0x035B,0x035F,0x036D,0x0371,0x0373,0x0377,0x038B,0x038F,0x0397,0x03A1,0x03A9,0x03AD, + 0x03B3,0x03B9,0x03C7,0x03CB,0x03D1,0x03D7,0x03DF,0x03E5,0x03F1,0x03F5,0x03FB,0x03FD,0x0407,0x0409,0x040F,0x0419, + 0x041B,0x0425,0x0427,0x042D,0x043F,0x0443,0x0445,0x0449,0x044F,0x0455,0x045D,0x0463,0x0469,0x047F,0x0481,0x048B, + 0x0493,0x049D,0x04A3,0x04A9,0x04B1,0x04BD,0x04C1,0x04C7,0x04CD,0x04CF,0x04D5,0x04E1,0x04EB,0x04FD,0x04FF,0x0503, + 0x0509,0x050B,0x0511,0x0515,0x0517,0x051B,0x0527,0x0529,0x052F,0x0551,0x0557,0x055D,0x0565,0x0577,0x0581,0x058F, + 0x0593,0x0595,0x0599,0x059F,0x05A7,0x05AB,0x05AD,0x05B3,0x05BF,0x05C9,0x05CB,0x05CF,0x05D1,0x05D5,0x05DB,0x05E7, + 0x05F3,0x05FB,0x0607,0x060D,0x0611,0x0617,0x061F,0x0623,0x062B,0x062F,0x063D,0x0641,0x0647,0x0649,0x064D,0x0653, + 0x0655,0x065B,0x0665,0x0679,0x067F,0x0683,0x0685,0x069D,0x06A1,0x06A3,0x06AD,0x06B9,0x06BB,0x06C5,0x06CD,0x06D3, + 0x06D9,0x06DF,0x06F1,0x06F7,0x06FB,0x06FD,0x0709,0x0713,0x071F,0x0727,0x0737,0x0745,0x074B,0x074F,0x0751,0x0755, + 0x0757,0x0761,0x076D,0x0773,0x0779,0x078B,0x078D,0x079D,0x079F,0x07B5,0x07BB,0x07C3,0x07C9,0x07CD,0x07CF,0x07D3, + 0x07DB,0x07E1,0x07EB,0x07ED,0x07F7,0x0805,0x080F,0x0815,0x0821,0x0823,0x0827,0x0829,0x0833,0x083F,0x0841,0x0851, + 0x0853,0x0859,0x085D,0x085F,0x0869,0x0871,0x0883,0x089B,0x089F,0x08A5,0x08AD,0x08BD,0x08BF,0x08C3,0x08CB,0x08DB, + 0x08DD,0x08E1,0x08E9,0x08EF,0x08F5,0x08F9,0x0905,0x0907,0x091D,0x0923,0x0925,0x092B,0x092F,0x0935,0x0943,0x0949, + 0x094D,0x094F,0x0955,0x0959,0x095F,0x096B,0x0971,0x0977,0x0985,0x0989,0x098F,0x099B,0x09A3,0x09A9,0x09AD,0x09C7, + 0x09D9,0x09E3,0x09EB,0x09EF,0x09F5,0x09F7,0x09FD,0x0A13,0x0A1F,0x0A21,0x0A31,0x0A39,0x0A3D,0x0A49,0x0A57,0x0A61, + 0x0A63,0x0A67,0x0A6F,0x0A75,0x0A7B,0x0A7F,0x0A81,0x0A85,0x0A8B,0x0A93,0x0A97,0x0A99,0x0A9F,0x0AA9,0x0AAB,0x0AB5, + 0x0ABD,0x0AC1,0x0ACF,0x0AD9,0x0AE5,0x0AE7,0x0AED,0x0AF1,0x0AF3,0x0B03,0x0B11,0x0B15,0x0B1B,0x0B23,0x0B29,0x0B2D, + 0x0B3F,0x0B47,0x0B51,0x0B57,0x0B5D,0x0B65,0x0B6F,0x0B7B,0x0B89,0x0B8D,0x0B93,0x0B99,0x0B9B,0x0BB7,0x0BB9,0x0BC3, + 0x0BCB,0x0BCF,0x0BDD,0x0BE1,0x0BE9,0x0BF5,0x0BFB,0x0C07,0x0C0B,0x0C11,0x0C25,0x0C2F,0x0C31,0x0C41,0x0C5B,0x0C5F, + 0x0C61,0x0C6D,0x0C73,0x0C77,0x0C83,0x0C89,0x0C91,0x0C95,0x0C9D,0x0CB3,0x0CB5,0x0CB9,0x0CBB,0x0CC7,0x0CE3,0x0CE5, + 0x0CEB,0x0CF1,0x0CF7,0x0CFB,0x0D01,0x0D03,0x0D0F,0x0D13,0x0D1F,0x0D21,0x0D2B,0x0D2D,0x0D3D,0x0D3F,0x0D4F,0x0D55, + 0x0D69,0x0D79,0x0D81,0x0D85,0x0D87,0x0D8B,0x0D8D,0x0DA3,0x0DAB,0x0DB7,0x0DBD,0x0DC7,0x0DC9,0x0DCD,0x0DD3,0x0DD5, + 0x0DDB,0x0DE5,0x0DE7,0x0DF3,0x0DFD,0x0DFF,0x0E09,0x0E17,0x0E1D,0x0E21,0x0E27,0x0E2F,0x0E35,0x0E3B,0x0E4B,0x0E57, + 0x0E59,0x0E5D,0x0E6B,0x0E71,0x0E75,0x0E7D,0x0E87,0x0E8F,0x0E95,0x0E9B,0x0EB1,0x0EB7,0x0EB9,0x0EC3,0x0ED1,0x0ED5, + 0x0EDB,0x0EED,0x0EEF,0x0EF9,0x0F07,0x0F0B,0x0F0D,0x0F17,0x0F25,0x0F29,0x0F31,0x0F43,0x0F47,0x0F4D,0x0F4F,0x0F53, + 0x0F59,0x0F5B,0x0F67,0x0F6B,0x0F7F,0x0F95,0x0FA1,0x0FA3,0x0FA7,0x0FAD,0x0FB3,0x0FB5,0x0FBB,0x0FD1,0x0FD3,0x0FD9, + 0x0FE9,0x0FEF,0x0FFB,0x0FFD,0x1003,0x100F,0x101F,0x1021,0x1025,0x102B,0x1039,0x103D,0x103F,0x1051,0x1069,0x1073, + 0x1079,0x107B,0x1085,0x1087,0x1091,0x1093,0x109D,0x10A3,0x10A5,0x10AF,0x10B1,0x10BB,0x10C1,0x10C9,0x10E7,0x10F1, + 0x10F3,0x10FD,0x1105,0x110B,0x1115,0x1127,0x112D,0x1139,0x1145,0x1147,0x1159,0x115F,0x1163,0x1169,0x116F,0x1181, + 0x1183,0x118D,0x119B,0x11A1,0x11A5,0x11A7,0x11AB,0x11C3,0x11C5,0x11D1,0x11D7,0x11E7,0x11EF,0x11F5,0x11FB,0x120D, + 0x121D,0x121F,0x1223,0x1229,0x122B,0x1231,0x1237,0x1241,0x1247,0x1253,0x125F,0x1271,0x1273,0x1279,0x127D,0x128F, + 0x1297,0x12AF,0x12B3,0x12B5,0x12B9,0x12BF,0x12C1,0x12CD,0x12D1,0x12DF,0x12FD,0x1307,0x130D,0x1319,0x1327,0x132D, + 0x1337,0x1343,0x1345,0x1349,0x134F,0x1357,0x135D,0x1367,0x1369,0x136D,0x137B,0x1381,0x1387,0x138B,0x1391,0x1393, + 0x139D,0x139F,0x13AF,0x13BB,0x13C3,0x13D5,0x13D9,0x13DF,0x13EB,0x13ED,0x13F3,0x13F9,0x13FF,0x141B,0x1421,0x142F, + 0x1433,0x143B,0x1445,0x144D,0x1459,0x146B,0x146F,0x1471,0x1475,0x148D,0x1499,0x149F,0x14A1,0x14B1,0x14B7,0x14BD, + 0x14CB,0x14D5,0x14E3,0x14E7,0x1505,0x150B,0x1511,0x1517,0x151F,0x1525,0x1529,0x152B,0x1537,0x153D,0x1541,0x1543, + 0x1549,0x155F,0x1565,0x1567,0x156B,0x157D,0x157F,0x1583,0x158F,0x1591,0x1597,0x159B,0x15B5,0x15BB,0x15C1,0x15C5, + 0x15CD,0x15D7,0x15F7,0x1607,0x1609,0x160F,0x1613,0x1615,0x1619,0x161B,0x1625,0x1633,0x1639,0x163D,0x1645,0x164F, + 0x1655,0x1669,0x166D,0x166F,0x1675,0x1693,0x1697,0x169F,0x16A9,0x16AF,0x16B5,0x16BD,0x16C3,0x16CF,0x16D3,0x16D9, + 0x16DB,0x16E1,0x16E5,0x16EB,0x16ED,0x16F7,0x16F9,0x1709,0x170F,0x1723,0x1727,0x1733,0x1741,0x175D,0x1763,0x1777, + 0x177B,0x178D,0x1795,0x179B,0x179F,0x17A5,0x17B3,0x17B9,0x17BF,0x17C9,0x17CB,0x17D5,0x17E1,0x17E9,0x17F3,0x17F5, + 0x17FF,0x1807,0x1813,0x181D,0x1835,0x1837,0x183B,0x1843,0x1849,0x184D,0x1855,0x1867,0x1871,0x1877,0x187D,0x187F, + 0x1885,0x188F,0x189B,0x189D,0x18A7,0x18AD,0x18B3,0x18B9,0x18C1,0x18C7,0x18D1,0x18D7,0x18D9,0x18DF,0x18E5,0x18EB, + 0x18F5,0x18FD,0x1915,0x191B,0x1931,0x1933,0x1945,0x1949,0x1951,0x195B,0x1979,0x1981,0x1993,0x1997,0x1999,0x19A3, + 0x19A9,0x19AB,0x19B1,0x19B5,0x19C7,0x19CF,0x19DB,0x19ED,0x19FD,0x1A03,0x1A05,0x1A11,0x1A17,0x1A21,0x1A23,0x1A2D, + 0x1A2F,0x1A35,0x1A3F,0x1A4D,0x1A51,0x1A69,0x1A6B,0x1A7B,0x1A7D,0x1A87,0x1A89,0x1A93,0x1AA7,0x1AAB,0x1AAD,0x1AB1, + 0x1AB9,0x1AC9,0x1ACF,0x1AD5,0x1AD7,0x1AE3,0x1AF3,0x1AFB,0x1AFF,0x1B05,0x1B23,0x1B25,0x1B2F,0x1B31,0x1B37,0x1B3B, + 0x1B41,0x1B47,0x1B4F,0x1B55,0x1B59,0x1B65,0x1B6B,0x1B73,0x1B7F,0x1B83,0x1B91,0x1B9D,0x1BA7,0x1BBF,0x1BC5,0x1BD1, + 0x1BD7,0x1BD9,0x1BEF,0x1BF7,0x1C09,0x1C13,0x1C19,0x1C27,0x1C2B,0x1C2D,0x1C33,0x1C3D,0x1C45,0x1C4B,0x1C4F,0x1C55, + 0x1C73,0x1C81,0x1C8B,0x1C8D,0x1C99,0x1CA3,0x1CA5,0x1CB5,0x1CB7,0x1CC9,0x1CE1,0x1CF3,0x1CF9,0x1D09,0x1D1B,0x1D21, + 0x1D23,0x1D35,0x1D39,0x1D3F,0x1D41,0x1D4B,0x1D53,0x1D5D,0x1D63,0x1D69,0x1D71,0x1D75,0x1D7B,0x1D7D,0x1D87,0x1D89, + 0x1D95,0x1D99,0x1D9F,0x1DA5,0x1DA7,0x1DB3,0x1DB7,0x1DC5,0x1DD7,0x1DDB,0x1DE1,0x1DF5,0x1DF9,0x1E01,0x1E07,0x1E0B, + 0x1E13,0x1E17,0x1E25,0x1E2B,0x1E2F,0x1E3D,0x1E49,0x1E4D,0x1E4F,0x1E6D,0x1E71,0x1E89,0x1E8F,0x1E95,0x1EA1,0x1EAD, + 0x1EBB,0x1EC1,0x1EC5,0x1EC7,0x1ECB,0x1EDD,0x1EE3,0x1EEF,0x1EF7,0x1EFD,0x1F01,0x1F0D,0x1F0F,0x1F1B,0x1F39,0x1F49, + 0x1F4B,0x1F51,0x1F67,0x1F75,0x1F7B,0x1F85,0x1F91,0x1F97,0x1F99,0x1F9D,0x1FA5,0x1FAF,0x1FB5,0x1FBB,0x1FD3,0x1FE1, + 0x1FE7,0x1FEB,0x1FF3,0x1FFF,0x2011,0x201B,0x201D,0x2027,0x2029,0x202D,0x2033,0x2047,0x204D,0x2051,0x205F,0x2063, + 0x2065,0x2069,0x2077,0x207D,0x2089,0x20A1,0x20AB,0x20B1,0x20B9,0x20C3,0x20C5,0x20E3,0x20E7,0x20ED,0x20EF,0x20FB, + 0x20FF,0x210D,0x2113,0x2135,0x2141,0x2149,0x214F,0x2159,0x215B,0x215F,0x2173,0x217D,0x2185,0x2195,0x2197,0x21A1, + 0x21AF,0x21B3,0x21B5,0x21C1,0x21C7,0x21D7,0x21DD,0x21E5,0x21E9,0x21F1,0x21F5,0x21FB,0x2203,0x2209,0x220F,0x221B, + 0x2221,0x2225,0x222B,0x2231,0x2239,0x224B,0x224F,0x2263,0x2267,0x2273,0x2275,0x227F,0x2285,0x2287,0x2291,0x229D, + 0x229F,0x22A3,0x22B7,0x22BD,0x22DB,0x22E1,0x22E5,0x22ED,0x22F7,0x2303,0x2309,0x230B,0x2327,0x2329,0x232F,0x2333, + 0x2335,0x2345,0x2351,0x2353,0x2359,0x2363,0x236B,0x2383,0x238F,0x2395,0x23A7,0x23AD,0x23B1,0x23BF,0x23C5,0x23C9, + 0x23D5,0x23DD,0x23E3,0x23EF,0x23F3,0x23F9,0x2405,0x240B,0x2417,0x2419,0x2429,0x243D,0x2441,0x2443,0x244D,0x245F, + 0x2467,0x246B,0x2479,0x247D,0x247F,0x2485,0x249B,0x24A1,0x24AF,0x24B5,0x24BB,0x24C5,0x24CB,0x24CD,0x24D7,0x24D9, + 0x24DD,0x24DF,0x24F5,0x24F7,0x24FB,0x2501,0x2507,0x2513,0x2519,0x2527,0x2531,0x253D,0x2543,0x254B,0x254F,0x2573, + 0x2581,0x258D,0x2593,0x2597,0x259D,0x259F,0x25AB,0x25B1,0x25BD,0x25CD,0x25CF,0x25D9,0x25E1,0x25F7,0x25F9,0x2605, + 0x260B,0x260F,0x2615,0x2627,0x2629,0x2635,0x263B,0x263F,0x264B,0x2653,0x2659,0x2665,0x2669,0x266F,0x267B,0x2681, + 0x2683,0x268F,0x269B,0x269F,0x26AD,0x26B3,0x26C3,0x26C9,0x26CB,0x26D5,0x26DD,0x26EF,0x26F5,0x2717,0x2719,0x2735, + 0x2737,0x274D,0x2753,0x2755,0x275F,0x276B,0x276D,0x2773,0x2777,0x277F,0x2795,0x279B,0x279D,0x27A7,0x27AF,0x27B3, + 0x27B9,0x27C1,0x27C5,0x27D1,0x27E3,0x27EF,0x2803,0x2807,0x280D,0x2813,0x281B,0x281F,0x2821,0x2831,0x283D,0x283F, + 0x2849,0x2851,0x285B,0x285D,0x2861,0x2867,0x2875,0x2881,0x2897,0x289F,0x28BB,0x28BD,0x28C1,0x28D5,0x28D9,0x28DB, + 0x28DF,0x28ED,0x28F7,0x2903,0x2905,0x2911,0x2921,0x2923,0x293F,0x2947,0x295D,0x2965,0x2969,0x296F,0x2975,0x2983, + 0x2987,0x298F,0x299B,0x29A1,0x29A7,0x29AB,0x29BF,0x29C3,0x29D5,0x29D7,0x29E3,0x29E9,0x29ED,0x29F3,0x2A01,0x2A13, + 0x2A1D,0x2A25,0x2A2F,0x2A4F,0x2A55,0x2A5F,0x2A65,0x2A6B,0x2A6D,0x2A73,0x2A83,0x2A89,0x2A8B,0x2A97,0x2A9D,0x2AB9, + 0x2ABB,0x2AC5,0x2ACD,0x2ADD,0x2AE3,0x2AEB,0x2AF1,0x2AFB,0x2B13,0x2B27,0x2B31,0x2B33,0x2B3D,0x2B3F,0x2B4B,0x2B4F, + 0x2B55,0x2B69,0x2B6D,0x2B6F,0x2B7B,0x2B8D,0x2B97,0x2B99,0x2BA3,0x2BA5,0x2BA9,0x2BBD,0x2BCD,0x2BE7,0x2BEB,0x2BF3, + 0x2BF9,0x2BFD,0x2C09,0x2C0F,0x2C17,0x2C23,0x2C2F,0x2C35,0x2C39,0x2C41,0x2C57,0x2C59,0x2C69,0x2C77,0x2C81,0x2C87, + 0x2C93,0x2C9F,0x2CAD,0x2CB3,0x2CB7,0x2CCB,0x2CCF,0x2CDB,0x2CE1,0x2CE3,0x2CE9,0x2CEF,0x2CFF,0x2D07,0x2D1D,0x2D1F, + 0x2D3B,0x2D43,0x2D49,0x2D4D,0x2D61,0x2D65,0x2D71,0x2D89,0x2D9D,0x2DA1,0x2DA9,0x2DB3,0x2DB5,0x2DC5,0x2DC7,0x2DD3, + 0x2DDF,0x2E01,0x2E03,0x2E07,0x2E0D,0x2E19,0x2E1F,0x2E25,0x2E2D,0x2E33,0x2E37,0x2E39,0x2E3F,0x2E57,0x2E5B,0x2E6F, + 0x2E79,0x2E7F,0x2E85,0x2E93,0x2E97,0x2E9D,0x2EA3,0x2EA5,0x2EB1,0x2EB7,0x2EC1,0x2EC3,0x2ECD,0x2ED3,0x2EE7,0x2EEB, + 0x2F05,0x2F09,0x2F0B,0x2F11,0x2F27,0x2F29,0x2F41,0x2F45,0x2F4B,0x2F4D,0x2F51,0x2F57,0x2F6F,0x2F75,0x2F7D,0x2F81, + 0x2F83,0x2FA5,0x2FAB,0x2FB3,0x2FC3,0x2FCF,0x2FD1,0x2FDB,0x2FDD,0x2FE7,0x2FED,0x2FF5,0x2FF9,0x3001,0x300D,0x3023, + 0x3029,0x3037,0x303B,0x3055,0x3059,0x305B,0x3067,0x3071,0x3079,0x307D,0x3085,0x3091,0x3095,0x30A3,0x30A9,0x30B9, + 0x30BF,0x30C7,0x30CB,0x30D1,0x30D7,0x30DF,0x30E5,0x30EF,0x30FB,0x30FD,0x3103,0x3109,0x3119,0x3121,0x3127,0x312D, + 0x3139,0x3143,0x3145,0x314B,0x315D,0x3161,0x3167,0x316D,0x3173,0x317F,0x3191,0x3199,0x319F,0x31A9,0x31B1,0x31C3, + 0x31C7,0x31D5,0x31DB,0x31ED,0x31F7,0x31FF,0x3209,0x3215,0x3217,0x321D,0x3229,0x3235,0x3259,0x325D,0x3263,0x326B, + 0x326F,0x3275,0x3277,0x327B,0x328D,0x3299,0x329F,0x32A7,0x32AD,0x32B3,0x32B7,0x32C9,0x32CB,0x32CF,0x32D1,0x32E9, + 0x32ED,0x32F3,0x32F9,0x3307,0x3325,0x332B,0x332F,0x3335,0x3341,0x3347,0x335B,0x335F,0x3367,0x336B,0x3373,0x3379, + 0x337F,0x3383,0x33A1,0x33A3,0x33AD,0x33B9,0x33C1,0x33CB,0x33D3,0x33EB,0x33F1,0x33FD,0x3401,0x340F,0x3413,0x3419, + 0x341B,0x3437,0x3445,0x3455,0x3457,0x3463,0x3469,0x346D,0x3481,0x348B,0x3491,0x3497,0x349D,0x34A5,0x34AF,0x34BB, + 0x34C9,0x34D3,0x34E1,0x34F1,0x34FF,0x3509,0x3517,0x351D,0x352D,0x3533,0x353B,0x3541,0x3551,0x3565,0x356F,0x3571, + 0x3577,0x357B,0x357D,0x3581,0x358D,0x358F,0x3599,0x359B,0x35A1,0x35B7,0x35BD,0x35BF,0x35C3,0x35D5,0x35DD,0x35E7, + 0x35EF,0x3605,0x3607,0x3611,0x3623,0x3631,0x3635,0x3637,0x363B,0x364D,0x364F,0x3653,0x3659,0x3661,0x366B,0x366D, + 0x368B,0x368F,0x36AD,0x36AF,0x36B9,0x36BB,0x36CD,0x36D1,0x36E3,0x36E9,0x36F7,0x3701,0x3703,0x3707,0x371B,0x373F, + 0x3745,0x3749,0x374F,0x375D,0x3761,0x3775,0x377F,0x378D,0x37A3,0x37A9,0x37AB,0x37C9,0x37D5,0x37DF,0x37F1,0x37F3, + 0x37F7,0x3805,0x380B,0x3821,0x3833,0x3835,0x3841,0x3847,0x384B,0x3853,0x3857,0x385F,0x3865,0x386F,0x3871,0x387D, + 0x388F,0x3899,0x38A7,0x38B7,0x38C5,0x38C9,0x38CF,0x38D5,0x38D7,0x38DD,0x38E1,0x38E3,0x38FF,0x3901,0x391D,0x3923, + 0x3925,0x3929,0x392F,0x393D,0x3941,0x394D,0x395B,0x396B,0x3979,0x397D,0x3983,0x398B,0x3991,0x3995,0x399B,0x39A1, + 0x39A7,0x39AF,0x39B3,0x39BB,0x39BF,0x39CD,0x39DD,0x39E5,0x39EB,0x39EF,0x39FB,0x3A03,0x3A13,0x3A15,0x3A1F,0x3A27, + 0x3A2B,0x3A31,0x3A4B,0x3A51,0x3A5B,0x3A63,0x3A67,0x3A6D,0x3A79,0x3A87,0x3AA5,0x3AA9,0x3AB7,0x3ACD,0x3AD5,0x3AE1, + 0x3AE5,0x3AEB,0x3AF3,0x3AFD,0x3B03,0x3B11,0x3B1B,0x3B21,0x3B23,0x3B2D,0x3B39,0x3B45,0x3B53,0x3B59,0x3B5F,0x3B71, + 0x3B7B,0x3B81,0x3B89,0x3B9B,0x3B9F,0x3BA5,0x3BA7,0x3BAD,0x3BB7,0x3BB9,0x3BC3,0x3BCB,0x3BD1,0x3BD7,0x3BE1,0x3BE3, + 0x3BF5,0x3BFF,0x3C01,0x3C0D,0x3C11,0x3C17,0x3C1F,0x3C29,0x3C35,0x3C43,0x3C4F,0x3C53,0x3C5B,0x3C65,0x3C6B,0x3C71, + 0x3C85,0x3C89,0x3C97,0x3CA7,0x3CB5,0x3CBF,0x3CC7,0x3CD1,0x3CDD,0x3CDF,0x3CF1,0x3CF7,0x3D03,0x3D0D,0x3D19,0x3D1B, + 0x3D1F,0x3D21,0x3D2D,0x3D33,0x3D37,0x3D3F,0x3D43,0x3D6F,0x3D73,0x3D75,0x3D79,0x3D7B,0x3D85,0x3D91,0x3D97,0x3D9D, + 0x3DAB,0x3DAF,0x3DB5,0x3DBB,0x3DC1,0x3DC9,0x3DCF,0x3DF3,0x3E05,0x3E09,0x3E0F,0x3E11,0x3E1D,0x3E23,0x3E29,0x3E2F, + 0x3E33,0x3E41,0x3E57,0x3E63,0x3E65,0x3E77,0x3E81,0x3E87,0x3EA1,0x3EB9,0x3EBD,0x3EBF,0x3EC3,0x3EC5,0x3EC9,0x3ED7, + 0x3EDB,0x3EE1,0x3EE7,0x3EEF,0x3EFF,0x3F0B,0x3F0D,0x3F37,0x3F3B,0x3F3D,0x3F41,0x3F59,0x3F5F,0x3F65,0x3F67,0x3F79, + 0x3F7D,0x3F8B,0x3F91,0x3FAD,0x3FBF,0x3FCD,0x3FD3,0x3FDD,0x3FE9,0x3FEB,0x3FF1,0x3FFD,0x401B,0x4021,0x4025,0x402B, + 0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9,0x40B1,0x40B7,0x40BD,0x40DB, + 0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B,0x413F,0x4159,0x4165,0x416B, + 0x4177,0x417B,0x4193,0x41AB,0x41B7,0x41BD,0x41BF,0x41CB,0x41E7,0x41EF,0x41F3,0x41F9,0x4205,0x4207,0x4219,0x421F, + 0x4223,0x4229,0x422F,0x4243,0x4253,0x4255,0x425B,0x4261,0x4273,0x427D,0x4283,0x4285,0x4289,0x4291,0x4297,0x429D, + 0x42B5,0x42C5,0x42CB,0x42D3,0x42DD,0x42E3,0x42F1,0x4307,0x430F,0x431F,0x4325,0x4327,0x4333,0x4337,0x4339,0x434F, + 0x4357,0x4369,0x438B,0x438D,0x4393,0x43A5,0x43A9,0x43AF,0x43B5,0x43BD,0x43C7,0x43CF,0x43E1,0x43E7,0x43EB,0x43ED, + 0x43F1,0x43F9,0x4409,0x440B,0x4417,0x4423,0x4429,0x443B,0x443F,0x4445,0x444B,0x4451,0x4453,0x4459,0x4465,0x446F, + 0x4483,0x448F,0x44A1,0x44A5,0x44AB,0x44AD,0x44BD,0x44BF,0x44C9,0x44D7,0x44DB,0x44F9,0x44FB,0x4505,0x4511,0x4513, + 0x452B,0x4531,0x4541,0x4549,0x4553,0x4555,0x4561,0x4577,0x457D,0x457F,0x458F,0x45A3,0x45AD,0x45AF,0x45BB,0x45C7, + 0x45D9,0x45E3,0x45EF,0x45F5,0x45F7,0x4601,0x4603,0x4609,0x4613,0x4625,0x4627,0x4633,0x4639,0x463D,0x4643,0x4645, + 0x465D,0x4679,0x467B,0x467F,0x4681,0x468B,0x468D,0x469D,0x46A9,0x46B1,0x46C7,0x46C9,0x46CF,0x46D3,0x46D5,0x46DF, + 0x46E5,0x46F9,0x4705,0x470F,0x4717,0x4723,0x4729,0x472F,0x4735,0x4739,0x474B,0x474D,0x4751,0x475D,0x476F,0x4771, + 0x477D,0x4783,0x4787,0x4789,0x4799,0x47A5,0x47B1,0x47BF,0x47C3,0x47CB,0x47DD,0x47E1,0x47ED,0x47FB,0x4801,0x4807, + 0x480B,0x4813,0x4819,0x481D,0x4831,0x483D,0x4847,0x4855,0x4859,0x485B,0x486B,0x486D,0x4879,0x4897,0x489B,0x48A1, + 0x48B9,0x48CD,0x48E5,0x48EF,0x48F7,0x4903,0x490D,0x4919,0x491F,0x492B,0x4937,0x493D,0x4945,0x4955,0x4963,0x4969, + 0x496D,0x4973,0x4997,0x49AB,0x49B5,0x49D3,0x49DF,0x49E1,0x49E5,0x49E7,0x4A03,0x4A0F,0x4A1D,0x4A23,0x4A39,0x4A41, + 0x4A45,0x4A57,0x4A5D,0x4A6B,0x4A7D,0x4A81,0x4A87,0x4A89,0x4A8F,0x4AB1,0x4AC3,0x4AC5,0x4AD5,0x4ADB,0x4AED,0x4AEF, + 0x4B07,0x4B0B,0x4B0D,0x4B13,0x4B1F,0x4B25,0x4B31,0x4B3B,0x4B43,0x4B49,0x4B59,0x4B65,0x4B6D,0x4B77,0x4B85,0x4BAD, + 0x4BB3,0x4BB5,0x4BBB,0x4BBF,0x4BCB,0x4BD9,0x4BDD,0x4BDF,0x4BE3,0x4BE5,0x4BE9,0x4BF1,0x4BF7,0x4C01,0x4C07,0x4C0D, + 0x4C0F,0x4C15,0x4C1B,0x4C21,0x4C2D,0x4C33,0x4C4B,0x4C55,0x4C57,0x4C61,0x4C67,0x4C73,0x4C79,0x4C7F,0x4C8D,0x4C93, + 0x4C99,0x4CCD,0x4CE1,0x4CE7,0x4CF1,0x4CF3,0x4CFD,0x4D05,0x4D0F,0x4D1B,0x4D27,0x4D29,0x4D2F,0x4D33,0x4D41,0x4D51, + 0x4D59,0x4D65,0x4D6B,0x4D81,0x4D83,0x4D8D,0x4D95,0x4D9B,0x4DB1,0x4DB3,0x4DC9,0x4DCF,0x4DD7,0x4DE1,0x4DED,0x4DF9, + 0x4DFB,0x4E05,0x4E0B,0x4E17,0x4E19,0x4E1D,0x4E2B,0x4E35,0x4E37,0x4E3D,0x4E4F,0x4E53,0x4E5F,0x4E67,0x4E79,0x4E85, + 0x4E8B,0x4E91,0x4E95,0x4E9B,0x4EA1,0x4EAF,0x4EB3,0x4EB5,0x4EC1,0x4ECD,0x4ED1,0x4ED7,0x4EE9,0x4EFB,0x4F07,0x4F09, + 0x4F19,0x4F25,0x4F2D,0x4F3F,0x4F49,0x4F63,0x4F67,0x4F6D,0x4F75,0x4F7B,0x4F81,0x4F85,0x4F87,0x4F91,0x4FA5,0x4FA9, + 0x4FAF,0x4FB7,0x4FBB,0x4FCF,0x4FD9,0x4FDB,0x4FFD,0x4FFF,0x5003,0x501B,0x501D,0x5029,0x5035,0x503F,0x5045,0x5047, + 0x5053,0x5071,0x5077,0x5083,0x5093,0x509F,0x50A1,0x50B7,0x50C9,0x50D5,0x50E3,0x50ED,0x50EF,0x50FB,0x5107,0x510B, + 0x510D,0x5111,0x5117,0x5123,0x5125,0x5135,0x5147,0x5149,0x5171,0x5179,0x5189,0x518F,0x5197,0x51A1,0x51A3,0x51A7, + 0x51B9,0x51C1,0x51CB,0x51D3,0x51DF,0x51E3,0x51F5,0x51F7,0x5209,0x5213,0x5215,0x5219,0x521B,0x521F,0x5227,0x5243, + 0x5245,0x524B,0x5261,0x526D,0x5273,0x5281,0x5293,0x5297,0x529D,0x52A5,0x52AB,0x52B1,0x52BB,0x52C3,0x52C7,0x52C9, + 0x52DB,0x52E5,0x52EB,0x52FF,0x5315,0x531D,0x5323,0x5341,0x5345,0x5347,0x534B,0x535D,0x5363,0x5381,0x5383,0x5387, + 0x538F,0x5395,0x5399,0x539F,0x53AB,0x53B9,0x53DB,0x53E9,0x53EF,0x53F3,0x53F5,0x53FB,0x53FF,0x540D,0x5411,0x5413, + 0x5419,0x5435,0x5437,0x543B,0x5441,0x5449,0x5453,0x5455,0x545F,0x5461,0x546B,0x546D,0x5471,0x548F,0x5491,0x549D, + 0x54A9,0x54B3,0x54C5,0x54D1,0x54DF,0x54E9,0x54EB,0x54F7,0x54FD,0x5507,0x550D,0x551B,0x5527,0x552B,0x5539,0x553D, + 0x554F,0x5551,0x555B,0x5563,0x5567,0x556F,0x5579,0x5585,0x5597,0x55A9,0x55B1,0x55B7,0x55C9,0x55D9,0x55E7,0x55ED, + 0x55F3,0x55FD,0x560B,0x560F,0x5615,0x5617,0x5623,0x562F,0x5633,0x5639,0x563F,0x564B,0x564D,0x565D,0x565F,0x566B, + 0x5671,0x5675,0x5683,0x5689,0x568D,0x568F,0x569B,0x56AD,0x56B1,0x56D5,0x56E7,0x56F3,0x56FF,0x5701,0x5705,0x5707, + 0x570B,0x5713,0x571F,0x5723,0x5747,0x574D,0x575F,0x5761,0x576D,0x5777,0x577D,0x5789,0x57A1,0x57A9,0x57AF,0x57B5, + 0x57C5,0x57D1,0x57D3,0x57E5,0x57EF,0x5803,0x580D,0x580F,0x5815,0x5827,0x582B,0x582D,0x5855,0x585B,0x585D,0x586D, + 0x586F,0x5873,0x587B,0x588D,0x5897,0x58A3,0x58A9,0x58AB,0x58B5,0x58BD,0x58C1,0x58C7,0x58D3,0x58D5,0x58DF,0x58F1, + 0x58F9,0x58FF,0x5903,0x5917,0x591B,0x5921,0x5945,0x594B,0x594D,0x5957,0x595D,0x5975,0x597B,0x5989,0x5999,0x599F, + 0x59B1,0x59B3,0x59BD,0x59D1,0x59DB,0x59E3,0x59E9,0x59ED,0x59F3,0x59F5,0x59FF,0x5A01,0x5A0D,0x5A11,0x5A13,0x5A17, + 0x5A1F,0x5A29,0x5A2F,0x5A3B,0x5A4D,0x5A5B,0x5A67,0x5A77,0x5A7F,0x5A85,0x5A95,0x5A9D,0x5AA1,0x5AA3,0x5AA9,0x5ABB, + 0x5AD3,0x5AE5,0x5AEF,0x5AFB,0x5AFD,0x5B01,0x5B0F,0x5B19,0x5B1F,0x5B25,0x5B2B,0x5B3D,0x5B49,0x5B4B,0x5B67,0x5B79, + 0x5B87,0x5B97,0x5BA3,0x5BB1,0x5BC9,0x5BD5,0x5BEB,0x5BF1,0x5BF3,0x5BFD,0x5C05,0x5C09,0x5C0B,0x5C0F,0x5C1D,0x5C29, + 0x5C2F,0x5C33,0x5C39,0x5C47,0x5C4B,0x5C4D,0x5C51,0x5C6F,0x5C75,0x5C77,0x5C7D,0x5C87,0x5C89,0x5CA7,0x5CBD,0x5CBF, + 0x5CC3,0x5CC9,0x5CD1,0x5CD7,0x5CDD,0x5CED,0x5CF9,0x5D05,0x5D0B,0x5D13,0x5D17,0x5D19,0x5D31,0x5D3D,0x5D41,0x5D47, + 0x5D4F,0x5D55,0x5D5B,0x5D65,0x5D67,0x5D6D,0x5D79,0x5D95,0x5DA3,0x5DA9,0x5DAD,0x5DB9,0x5DC1,0x5DC7,0x5DD3,0x5DD7, + 0x5DDD,0x5DEB,0x5DF1,0x5DFD,0x5E07,0x5E0D,0x5E13,0x5E1B,0x5E21,0x5E27,0x5E2B,0x5E2D,0x5E31,0x5E39,0x5E45,0x5E49, + 0x5E57,0x5E69,0x5E73,0x5E75,0x5E85,0x5E8B,0x5E9F,0x5EA5,0x5EAF,0x5EB7,0x5EBB,0x5ED9,0x5EFD,0x5F09,0x5F11,0x5F27, + 0x5F33,0x5F35,0x5F3B,0x5F47,0x5F57,0x5F5D,0x5F63,0x5F65,0x5F77,0x5F7B,0x5F95,0x5F99,0x5FA1,0x5FB3,0x5FBD,0x5FC5, + 0x5FCF,0x5FD5,0x5FE3,0x5FE7,0x5FFB,0x6011,0x6023,0x602F,0x6037,0x6053,0x605F,0x6065,0x606B,0x6073,0x6079,0x6085, + 0x609D,0x60AD,0x60BB,0x60BF,0x60CD,0x60D9,0x60DF,0x60E9,0x60F5,0x6109,0x610F,0x6113,0x611B,0x612D,0x6139,0x614B, + 0x6155,0x6157,0x615B,0x616F,0x6179,0x6187,0x618B,0x6191,0x6193,0x619D,0x61B5,0x61C7,0x61C9,0x61CD,0x61E1,0x61F1, + 0x61FF,0x6209,0x6217,0x621D,0x6221,0x6227,0x623B,0x6241,0x624B,0x6251,0x6253,0x625F,0x6265,0x6283,0x628D,0x6295, + 0x629B,0x629F,0x62A5,0x62AD,0x62D5,0x62D7,0x62DB,0x62DD,0x62E9,0x62FB,0x62FF,0x6305,0x630D,0x6317,0x631D,0x632F, + 0x6341,0x6343,0x634F,0x635F,0x6367,0x636D,0x6371,0x6377,0x637D,0x637F,0x63B3,0x63C1,0x63C5,0x63D9,0x63E9,0x63EB, + 0x63EF,0x63F5,0x6401,0x6403,0x6409,0x6415,0x6421,0x6427,0x642B,0x6439,0x6443,0x6449,0x644F,0x645D,0x6467,0x6475, + 0x6485,0x648D,0x6493,0x649F,0x64A3,0x64AB,0x64C1,0x64C7,0x64C9,0x64DB,0x64F1,0x64F7,0x64F9,0x650B,0x6511,0x6521, + 0x652F,0x6539,0x653F,0x654B,0x654D,0x6553,0x6557,0x655F,0x6571,0x657D,0x658D,0x658F,0x6593,0x65A1,0x65A5,0x65AD, + 0x65B9,0x65C5,0x65E3,0x65F3,0x65FB,0x65FF,0x6601,0x6607,0x661D,0x6629,0x6631,0x663B,0x6641,0x6647,0x664D,0x665B, + 0x6661,0x6673,0x667D,0x6689,0x668B,0x6695,0x6697,0x669B,0x66B5,0x66B9,0x66C5,0x66CD,0x66D1,0x66E3,0x66EB,0x66F5, + 0x6703,0x6713,0x6719,0x671F,0x6727,0x6731,0x6737,0x673F,0x6745,0x6751,0x675B,0x676F,0x6779,0x6781,0x6785,0x6791, + 0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839,0x683B,0x683F,0x6845,0x684B, + 0x684D,0x6857,0x6859,0x685D,0x6863,0x6869,0x686B,0x6871,0x6887,0x6899,0x689F,0x68B1,0x68BD,0x68C5,0x68D1,0x68D7, + 0x68E1,0x68ED,0x68EF,0x68FF,0x6901,0x690B,0x690D,0x6917,0x6929,0x692F,0x6943,0x6947,0x6949,0x694F,0x6965,0x696B, + 0x6971,0x6983,0x6989,0x6997,0x69A3,0x69B3,0x69B5,0x69BB,0x69C1,0x69C5,0x69D3,0x69DF,0x69E3,0x69E5,0x69F7,0x6A07, + 0x6A2B,0x6A37,0x6A3D,0x6A4B,0x6A67,0x6A69,0x6A75,0x6A7B,0x6A87,0x6A8D,0x6A91,0x6A93,0x6AA3,0x6AC1,0x6AC9,0x6AE1, + 0x6AE7,0x6B05,0x6B0F,0x6B11,0x6B23,0x6B27,0x6B2D,0x6B39,0x6B41,0x6B57,0x6B59,0x6B5F,0x6B75,0x6B87,0x6B89,0x6B93, + 0x6B95,0x6B9F,0x6BBD,0x6BBF,0x6BDB,0x6BE1,0x6BEF,0x6BFF,0x6C05,0x6C19,0x6C29,0x6C2B,0x6C31,0x6C35,0x6C55,0x6C59, + 0x6C5B,0x6C5F,0x6C65,0x6C67,0x6C73,0x6C77,0x6C7D,0x6C83,0x6C8F,0x6C91,0x6C97,0x6C9B,0x6CA1,0x6CA9,0x6CAF,0x6CB3, + 0x6CC7,0x6CCB,0x6CEB,0x6CF5,0x6CFD,0x6D0D,0x6D0F,0x6D25,0x6D27,0x6D2B,0x6D31,0x6D39,0x6D3F,0x6D4F,0x6D5D,0x6D61, + 0x6D73,0x6D7B,0x6D7F,0x6D93,0x6D99,0x6DA5,0x6DB1,0x6DB7,0x6DC1,0x6DC3,0x6DCD,0x6DCF,0x6DDB,0x6DF7,0x6E03,0x6E15, + 0x6E17,0x6E29,0x6E33,0x6E3B,0x6E45,0x6E75,0x6E77,0x6E7B,0x6E81,0x6E89,0x6E93,0x6E95,0x6E9F,0x6EBD,0x6EBF,0x6EE3, + 0x6EE9,0x6EF3,0x6EF9,0x6EFB,0x6F0D,0x6F11,0x6F17,0x6F1F,0x6F2F,0x6F3D,0x6F4D,0x6F53,0x6F61,0x6F65,0x6F79,0x6F7D, + 0x6F83,0x6F85,0x6F8F,0x6F9B,0x6F9D,0x6FA3,0x6FAF,0x6FB5,0x6FBB,0x6FBF,0x6FCB,0x6FCD,0x6FD3,0x6FD7,0x6FE3,0x6FE9, + 0x6FF1,0x6FF5,0x6FF7,0x6FFD,0x700F,0x7019,0x701F,0x7027,0x7033,0x7039,0x704F,0x7051,0x7057,0x7063,0x7075,0x7079, + 0x7087,0x708D,0x7091,0x70A5,0x70AB,0x70BB,0x70C3,0x70C7,0x70CF,0x70E5,0x70ED,0x70F9,0x70FF,0x7105,0x7115,0x7121, + 0x7133,0x7151,0x7159,0x715D,0x715F,0x7163,0x7169,0x7183,0x7187,0x7195,0x71AD,0x71C3,0x71C9,0x71CB,0x71D1,0x71DB, + 0x71E1,0x71EF,0x71F5,0x71FB,0x7207,0x7211,0x7217,0x7219,0x7225,0x722F,0x723B,0x7243,0x7255,0x7267,0x7271,0x7277, + 0x727F,0x728F,0x7295,0x729B,0x72A3,0x72B3,0x72C7,0x72CB,0x72CD,0x72D7,0x72D9,0x72E3,0x72EF,0x72F5,0x72FD,0x7303, + 0x730D,0x7321,0x732B,0x733D,0x7357,0x735B,0x7361,0x737F,0x7381,0x7385,0x738D,0x7393,0x739F,0x73AB,0x73BD,0x73C1, + 0x73C9,0x73DF,0x73E5,0x73E7,0x73F3,0x7415,0x741B,0x742D,0x7439,0x743F,0x7441,0x745D,0x746B,0x747B,0x7489,0x748D, + 0x749B,0x74A7,0x74AB,0x74B1,0x74B7,0x74B9,0x74DD,0x74E1,0x74E7,0x74FB,0x7507,0x751F,0x7525,0x753B,0x753D,0x754D, + 0x755F,0x756B,0x7577,0x7589,0x758B,0x7591,0x7597,0x759D,0x75A1,0x75A7,0x75B5,0x75B9,0x75BB,0x75D1,0x75D9,0x75E5, + 0x75EB,0x75F5,0x75FB,0x7603,0x760F,0x7621,0x762D,0x7633,0x763D,0x763F,0x7655,0x7663,0x7669,0x766F,0x7673,0x7685, + 0x768B,0x769F,0x76B5,0x76B7,0x76C3,0x76DB,0x76DF,0x76F1,0x7703,0x7705,0x771B,0x771D,0x7721,0x772D,0x7735,0x7741, + 0x774B,0x7759,0x775D,0x775F,0x7771,0x7781,0x77A7,0x77AD,0x77B3,0x77B9,0x77C5,0x77CF,0x77D5,0x77E1,0x77E9,0x77EF, + 0x77F3,0x77F9,0x7807,0x7825,0x782B,0x7835,0x783D,0x7853,0x7859,0x7861,0x786D,0x7877,0x7879,0x7883,0x7885,0x788B, + 0x7895,0x7897,0x78A1,0x78AD,0x78BF,0x78D3,0x78D9,0x78DD,0x78E5,0x78FB,0x7901,0x7907,0x7925,0x792B,0x7939,0x793F, + 0x794B,0x7957,0x795D,0x7967,0x7969,0x7973,0x7991,0x7993,0x79A3,0x79AB,0x79AF,0x79B1,0x79B7,0x79C9,0x79CD,0x79CF, + 0x79D5,0x79D9,0x79F3,0x79F7,0x79FF,0x7A05,0x7A0F,0x7A11,0x7A15,0x7A1B,0x7A23,0x7A27,0x7A2D,0x7A4B,0x7A57,0x7A59, + 0x7A5F,0x7A65,0x7A69,0x7A7D,0x7A93,0x7A9B,0x7A9F,0x7AA1,0x7AA5,0x7AED,0x7AF5,0x7AF9,0x7B01,0x7B17,0x7B19,0x7B1D, + 0x7B2B,0x7B35,0x7B37,0x7B3B,0x7B4F,0x7B55,0x7B5F,0x7B71,0x7B77,0x7B8B,0x7B9B,0x7BA1,0x7BA9,0x7BAF,0x7BB3,0x7BC7, + 0x7BD3,0x7BE9,0x7BEB,0x7BEF,0x7BF1,0x7BFD,0x7C07,0x7C19,0x7C1B,0x7C31,0x7C37,0x7C49,0x7C67,0x7C69,0x7C73,0x7C81, + 0x7C8B,0x7C93,0x7CA3,0x7CD5,0x7CDB,0x7CE5,0x7CED,0x7CF7,0x7D03,0x7D09,0x7D1B,0x7D1D,0x7D33,0x7D39,0x7D3B,0x7D3F, + 0x7D45,0x7D4D,0x7D53,0x7D59,0x7D63,0x7D75,0x7D77,0x7D8D,0x7D8F,0x7D9F,0x7DAD,0x7DB7,0x7DBD,0x7DBF,0x7DCB,0x7DD5, + 0x7DE9,0x7DED,0x7DFB,0x7E01,0x7E05,0x7E29,0x7E2B,0x7E2F,0x7E35,0x7E41,0x7E43,0x7E47,0x7E55,0x7E61,0x7E67,0x7E6B, + 0x7E71,0x7E73,0x7E79,0x7E7D,0x7E91,0x7E9B,0x7E9D,0x7EA7,0x7EAD,0x7EB9,0x7EBB,0x7ED3,0x7EDF,0x7EEB,0x7EF1,0x7EF7, + 0x7EFB,0x7F13,0x7F15,0x7F19,0x7F31,0x7F33,0x7F39,0x7F3D,0x7F43,0x7F4B,0x7F5B,0x7F61,0x7F63,0x7F6D,0x7F79,0x7F87, + 0x7F8D,0x7FAF,0x7FB5,0x7FC3,0x7FC9,0x7FCD,0x7FCF +}; diff --git a/REDALERT/MP.H b/REDALERT/MP.H new file mode 100644 index 000000000..675f75863 --- /dev/null +++ b/REDALERT/MP.H @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MP_H +#define MP_H + +//lint -e740 -e534 -e537 -e760 + +//lint -d_LINT=1 +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include "straw.h" +#include + +extern unsigned short primeTable[3511]; + + +#define digit unsigned long +#define signeddigit signed long +#define LOG_UNITSIZE 5 +#define UNITSIZE 32 +#define UPPER_MOST_BIT 0x80000000L +#define SEMI_UPPER_MOST_BIT 0x8000 +#define SEMI_MASK ((unsigned short)~0) +#define MAX_BIT_PRECISION 2048 +#define MAX_UNIT_PRECISION (MAX_BIT_PRECISION/UNITSIZE) +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + + +int XMP_Significance(const digit * r, int precision); +void XMP_Inc(digit * r, int precision); +void XMP_Dec(digit * r, int precision); +void XMP_Neg(digit * r, int precision); +void XMP_Abs(digit * r, int precision); +void XMP_Shift_Right_Bits(digit * r1, int bits, int precision); +void XMP_Shift_Left_Bits(digit * r1, int bits, int precision); +bool XMP_Rotate_Left(digit * r1, bool carry, int precision); +void XMP_Not(digit * digit_ptr, int precision); +void XMP_Init(digit * r, digit value, int precision); +unsigned XMP_Count_Bits(const digit * r, int precision); +int XMP_Count_Bytes(const digit * r, int precision); +void XMP_Move(digit * dest, digit const * source, int precision); +int XMP_Compare(const digit * r1, const digit * r2, int precision); +bool XMP_Add(digit * result, const digit * r1, const digit * r2, bool carry, int precision); +bool XMP_Add_Int(digit * result, const digit * r1, digit r2, bool carry, int precision); +bool XMP_Sub(digit * result, const digit * r1, const digit * r2, bool borrow, int precision); +bool XMP_Sub_Int(digit * result, const digit * r1, unsigned short r2, bool borrow, int precision); +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision); +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision); +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision); +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision); +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision); +void xmp_single_mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision); +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int xmp_stage_modulus(const digit * n_modulus, int precision); +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +void XMP_Mod_Mult_Clear(int precision); +unsigned short mp_quo_digit(unsigned short * dividend); +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision); +bool XMP_Is_Small_Prime(const digit * candidate, int precision); +bool XMP_Small_Divisors_Test(const digit * candidate, int precision); +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision); +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision); +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision); +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision); +void XMP_Randomize(digit * result, Straw & rng, int nbits, int precision); +void XMP_Randomize(digit * result, Straw & rng, digit const * min, digit const * max, int precision); +bool XMP_Is_Prime(digit const * prime, int precision); +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision); +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output); +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision); +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision); + + + +inline int XMP_Digits_To_Bits(int digits) +{ + return(digits << LOG_UNITSIZE); +} + + +inline int XMP_Bits_To_Digits(int bits) +{ + return ((bits + (UNITSIZE-1)) / UNITSIZE); +} + + +inline digit XMP_Bits_To_Mask(int bits) +{ + if (!bits) return(0); + return(1 << ((bits-1) % UNITSIZE)); +} + + +inline bool XMP_Is_Negative(const digit * r, int precision) +{ + return((signeddigit) *(r + (precision-1)) < 0); +} + + +inline bool XMP_Test_Eq_Int(digit const * r, int i, int p) +{ + return( (*r == i ) && XMP_Significance(r,p) <= 1 ); +} + + +inline void XMP_Set_Bit(digit * r, unsigned bit) +{ + r[bit >> LOG_UNITSIZE] |= ((digit)1 << (bit & (UNITSIZE-1))); +} + +inline bool XMP_Test_Bit(const digit * r, unsigned bit) +{ + return (r[bit >> LOG_UNITSIZE] & ((digit)1 << (bit & (UNITSIZE-1)))); +} + + + +// Misc functions. +void memrev(char * buffer, size_t length); + +#endif diff --git a/REDALERT/MPGSET.CPP b/REDALERT/MPGSET.CPP new file mode 100644 index 000000000..38b24a7cc --- /dev/null +++ b/REDALERT/MPGSET.CPP @@ -0,0 +1,606 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* FILE +* MpgSet.cpp +* +* DESCRIPTION +* Mpeg movie settings manager +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* June 30, 1998 +* +****************************************************************************/ + +#include "function.h" + +#ifdef DVD +#include "mpgset.h" + +#ifdef MCIMPEG +bool EnumMCI(MCIDevice* desc, void* context); +#endif + +/**************************************************************************** +* +* NAME +* MPGSettings(DeviceName) +* +* DESCRIPTION +* Default constructor +* +* INPUTS +* DeviceName - Initial device to use for MPG playback (NULL = DXMedia) +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::MPGSettings(const char* deviceName) + : mDeviceName(NULL) + { + SetDeviceName(deviceName); + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + #endif + } + + +MPGSettings::MPGSettings(FileClass& file) + : mDeviceName(NULL) + { + INIClass ini; + char buffer[256]; + char* device = NULL; + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + // Enumerate all the MCI devices that can play a movie + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + + #endif + + // Retrieve the user specified device from the config file + buffer[0] = '\0'; + + if (ini.Load(file)) + { + ini.Get_String("MovieSettings", "Device", "Default", buffer, sizeof(buffer)); + } + + // If there is a specification in the config and it isn't the default + if ((strlen(buffer) != 0) && (stricmp(buffer, "Default") != 0)) + { + #ifdef MCIMPEG + // Search for selection + for (int i = 0; i < mCount; i++) + { + if (stricmp(buffer, mMCIDevices[i].name) == 0) + { + device = mMCIDevices[i].name; + break; + } + } + #endif + } + + SetDeviceName(device); + } + + +/**************************************************************************** +* +* NAME +* ~MPGSettings +* +* DESCRIPTION +* Destructor +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::~MPGSettings(void) + { + if (mDeviceName) + free(mDeviceName); + + #ifdef MCIMPEG + if (mMCIDevices) + delete[] mMCIDevices; + #endif + } + + +/**************************************************************************** +* +* NAME +* SetDeviceName(DeviceName) +* +* DESCRIPTION +* Change current device used for mpeg playback +* +* INPUTS +* DeviceName - Device name type (IE: mpegvideo) +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::SetDeviceName(const char* deviceName) + { + if (mDeviceName) + free(mDeviceName); + + mDeviceName = NULL; + + if (deviceName) + mDeviceName = strdup(deviceName); + } + + +bool MPGSettings::Save(FileClass& file) + { + INIClass ini; + + if (ini.Load(file)) + { + const char* device = GetDeviceName(); + + if (device) + { + ini.Put_String("MovieSettings", "Device", device); + } + else + { + ini.Put_String("MovieSettings", "Device", "Default"); + } + + ini.Save(file); + return true; + } + + return false; + } + + +/**************************************************************************** +* +* NAME +* Dialog() +* +* DESCRIPTION +* Mpeg playback settings dialog +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::Dialog(void) + { + // Dialog & button dimensions + int d_dialog_w = 200 *RESFACTOR; + int d_dialog_h = 100 *RESFACTOR; + int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = 70 * RESFACTOR; + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 7 *RESFACTOR; + int d_margin = 7 *RESFACTOR; + + int d_okay_w = 40 *RESFACTOR; + int d_okay_h = 9 *RESFACTOR; + int d_okay_x = d_dialog_x + d_margin + 20; + int d_okay_y = ((d_dialog_y + d_dialog_h) - (d_okay_h + 20)); + + int d_test_w = 40 *RESFACTOR; + int d_test_h = 9 *RESFACTOR; + int d_test_x = (d_dialog_cx - (d_test_w / 2)); + int d_test_y = ((d_dialog_y + d_dialog_h) - (d_test_h + 20)); + + int d_cancel_w = 40 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = ((d_dialog_x + d_dialog_w) - (d_cancel_w + d_margin + 20)); + int d_cancel_y = ((d_dialog_y + d_dialog_h) - (d_cancel_h + 20)); + + int d_method_w = 165 * RESFACTOR; + int d_method_h = 50 * RESFACTOR; + int d_method_x = (d_dialog_cx - (d_method_w / 2)); + int d_method_y = (d_dialog_y + 40); + + // Button enumerations: + enum + { + BUTTON_OKAY = 100, + BUTTON_TEST, + BUTTON_CANCEL, + BUTTON_METHOD, + NUM_OF_BUTTONS = 4, + }; + + int num_of_buttons = NUM_OF_BUTTONS; + + // Redraw values: in order from "top" to "bottom" layer of the dialog + typedef enum + { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + // Dialog variables: + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + int i; + char* origDevice = NULL; + + // Buttons + ControlClass * commands = NULL; // the button list + + TextButtonClass okaybtn(BUTTON_OKAY, TXT_OK, TPF_BUTTON, + d_okay_x, d_okay_y, d_okay_w, d_okay_h); + + TextButtonClass testbtn(BUTTON_TEST, "Test", TPF_BUTTON, + d_test_x, d_test_y, d_test_w, d_test_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ListClass method(BUTTON_METHOD, d_method_x, d_method_y, d_method_w, + d_method_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + // Initialize + Set_Logic_Page(SeenBuff); + + // Create the list + commands = &okaybtn; + testbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + method.Add_Tail(*commands); + + // Fill array of button ptrs + curbutton = 0; + buttons[0] = &okaybtn; + buttons[1] = &testbtn; + buttons[2] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + // Add device to device control + method.Add_Item("DirectX Media (Default)"); + + #ifdef MCIMPEG + for (i = 0; i < mCount; i++) + { + if (mMCIDevices[i].description != NULL) + { + method.Add_Item(mMCIDevices[i].description); + } + } + #endif + + method.Set_Selected_Index(0); + + #ifdef MCIMPEG + // Search for current selection + if (GetDeviceName()) + { + for (i = 0; i < mCount; i++) + { + if (stricmp(GetDeviceName(), mMCIDevices[i].name) == 0) + { + method.Set_Selected_Index(i + 1); + break; + } + } + } + #endif + + // Save original device selection + if (GetDeviceName()) + origDevice = strdup(GetDeviceName()); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + + while (process) + { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) + { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + // Invoke game callback + Call_Back(); + + // Refresh display if needed + if (display) + { + Hide_Mouse(); + + if (display >= REDRAW_BACKGROUND) + { + // Refresh the backdrop + Load_Title_Page(true); + CCPalette.Set(); + + // Draw the background + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption("Movie Settings", d_dialog_x, d_dialog_y, d_dialog_w); + } + + // Redraw buttons + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Get user input + input = commands->Input(); + + // Process input + switch (input) + { + case (BUTTON_OKAY | KN_BUTTON): + selection = BUTTON_OKAY; + pressed = true; + break; + + case (BUTTON_TEST | KN_BUTTON): + selection = BUTTON_TEST; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + + if (curbutton > (num_of_buttons - 1)) + curbutton = 0; + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_OKAY; + pressed = true; + break; + + default: + break; + } + + if (pressed) + { + // to make sure the selection is correct in case they used the mouse + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton = selection - BUTTON_OKAY; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) + { + case (BUTTON_TEST): + buttons[curbutton]->IsPressed = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + Theme.Fade_Out(); + Hide_Mouse(); + VisiblePage.Clear(); + //PlayMpegMovie("acrop"); //PG + Keyboard->Clear(); + Show_Mouse(); + Theme.Queue_Song(THEME_CRUS); + display = REDRAW_ALL; + + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + buttons[0]->Turn_On(); + curbutton = 0; + break; + + case (BUTTON_OKAY): + process = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + { + RawFileClass file(CONFIG_FILE_NAME); + Save(file); + } + break; + + case (BUTTON_CANCEL): + process = false; + SetDeviceName(origDevice); + break; + } + + pressed = false; + } + } + + if (origDevice) + free(origDevice); + } + + +#ifdef MCIMPEG +/**************************************************************************** +* +* NAME +* EnumMCI(DeviceDesc, Context) +* +* DESCRIPTION +* MCI device enumeration callback +* +* INPUTS +* DeviceDesc - MCI device description +* Context - User defined context variable +* +* RESULT +* Continue - Continue with next device flag +* +****************************************************************************/ + +bool EnumMCI(MCIDevice* desc, void* context) + { + MPGSettings* mpgset = (MPGSettings*)context; + + // Digital video device type? + if (desc->type == MCI_DEVTYPE_DIGITAL_VIDEO) + { + if (MciMovie) + { + CCFileClass file; + const char* filename; + + filename = file.Set_Name("movies\\acrop.mpg"); + + if (!file.Is_Available()) + { + char buffer[256]; + sprintf(buffer, "Couldn't test MCI device %s\n", desc->name); + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(buffer); + Hide_Mouse(); + VisiblePage.Clear(); + return true; + } + + if (MciMovie->Open(filename, desc->name)) + { + MciMovie->Close(); + memcpy((void*)&mpgset->mMCIDevices[mpgset->mCount], (void*)desc, + sizeof(MCIDevice)); + mpgset->mCount++; + } + } + } + + return true; + } +#endif +#endif diff --git a/REDALERT/MPGSET.H b/REDALERT/MPGSET.H new file mode 100644 index 000000000..aae36bba7 --- /dev/null +++ b/REDALERT/MPGSET.H @@ -0,0 +1,48 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MPGSETTINGS_H_ +#define _MPGSETTINGS_H_ + +#include "function.h" + +#ifdef DVD +#include "mci.h" +#include "rawfile.h" + +class MPGSettings + { + public: + MPGSettings(const char* deviceName); + MPGSettings(FileClass& file); + virtual ~MPGSettings(void); + + void SetDeviceName(const char* device); + const char* GetDeviceName(void) const + {return mDeviceName;} + bool Save(FileClass& file); + void Dialog(void); + + char* mDeviceName; + + #ifdef MCIMPEG + MCI mMCI; + unsigned int mCount; + MCIDevice* mMCIDevices; + #endif + }; + +#endif // DVD +#endif // _MPGSETTINGS_H_ diff --git a/REDALERT/MPLAYER.CPP b/REDALERT/MPLAYER.CPP new file mode 100644 index 000000000..c6c2c0e1e --- /dev/null +++ b/REDALERT/MPLAYER.CPP @@ -0,0 +1,1248 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPLAYER.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPLAYER.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 14, 1995 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * Clear_Listbox -- clears the given list box * + * Clear_Vector -- clears the given NodeNameType vector * + * Computer_Message -- "sends" a message from the computer * + * Garble_Message -- "garbles" a message * + * Surrender_Dialog -- Prompts user for surrendering * + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#endif + +/*********************************************************************************************** + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_NORMAL, GAME_MODEM, etc. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +GameType Select_MPlayer_Game (void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 190 *RESFACTOR; +#ifdef WOLAPI_INTEGRATION + int d_dialog_h = 89 * RESFACTOR; // ajw + int d_dialog_y = (((255 * RESFACTOR) - d_dialog_h) / 2); +#else + int d_dialog_h = 78 *RESFACTOR; + int d_dialog_y = 90 * RESFACTOR; +#endif + int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 7 *RESFACTOR; + int d_margin = 7 *RESFACTOR; + + int d_modemserial_w = 80 *RESFACTOR; + int d_modemserial_h = 9 *RESFACTOR; + int d_modemserial_x = d_dialog_cx - d_modemserial_w / 2; + int d_modemserial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_skirmish_w = 80 *RESFACTOR; + int d_skirmish_h = 9 *RESFACTOR; + int d_skirmish_x = d_dialog_cx - d_skirmish_w / 2; + int d_skirmish_y = d_modemserial_y + d_modemserial_h + 2*RESFACTOR; + + int d_ipx_w = 80 *RESFACTOR; + int d_ipx_h = 9 *RESFACTOR; + int d_ipx_x = d_dialog_cx - d_ipx_w / 2; + int d_ipx_y = d_skirmish_y + d_skirmish_h + 2*RESFACTOR; + +#ifdef WOLAPI_INTEGRATION + // ajw 7/2/98 - added button + int d_wol_w = 80 * RESFACTOR; + int d_wol_h = 9 * RESFACTOR; + int d_wol_x = d_dialog_cx - d_wol_w / 2; + int d_wol_y = d_ipx_y + d_ipx_h + 2*RESFACTOR; +#endif + + int d_cancel_w = 60 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; +#ifdef WOLAPI_INTEGRATION + int d_cancel_y = d_wol_y + d_wol_h + d_margin; +#else + int d_cancel_y = d_ipx_y + d_ipx_h + d_margin; +#endif + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + //------------------------------------------------------------------------ + // Button enumerations: + //------------------------------------------------------------------------ + enum { + BUTTON_MODEMSERIAL = 100, + BUTTON_SKIRMISH, + BUTTON_IPX, +#ifdef WOLAPI_INTEGRATION + BUTTON_WOL, // ajw +#endif + BUTTON_CANCEL, + +#ifdef WOLAPI_INTEGRATION + NUM_OF_BUTTONS = 5, // ajw +#else + NUM_OF_BUTTONS = 4, +#endif + }; + + int num_of_buttons = NUM_OF_BUTTONS - (Ipx.Is_IPX() ? 0 : 1); + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables: + //------------------------------------------------------------------------ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + GameType retval; // return value + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + + //------------------------------------------------------------------------ + // If IPX not active then do only the modem serial dialog + //------------------------------------------------------------------------ +// if ( !Ipx.Is_IPX() ) { +// return( Select_Serial_Dialog() ); +// } + + + TextButtonClass modemserialbtn (BUTTON_MODEMSERIAL, TXT_MODEM_SERIAL, TPF_BUTTON, + d_modemserial_x, d_modemserial_y, d_modemserial_w, d_modemserial_h); + + TextButtonClass skirmishbtn (BUTTON_SKIRMISH, TXT_SKIRMISH, TPF_BUTTON, + d_skirmish_x, d_skirmish_y, d_skirmish_w, d_skirmish_h); + + TextButtonClass ipxbtn (BUTTON_IPX, TXT_NETWORK, TPF_BUTTON, + d_ipx_x, d_ipx_y, d_ipx_w, d_ipx_h); + +#ifdef WOLAPI_INTEGRATION + // ajw + TextButtonClass wolbtn(BUTTON_WOL, TXT_WOL_INTERNETBUTTON, TPF_BUTTON, + d_wol_x, d_wol_y, d_wol_w, d_wol_h); +#endif + + if(!Ipx.Is_IPX()) { + d_cancel_y = d_ipx_y; + d_dialog_h -= d_cancel_h; + } + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + VisiblePage.Blit(seen_buff_save); +#endif + //------------------------------------------------------------------------ + // Create the list + //------------------------------------------------------------------------ + commands = &modemserialbtn; + skirmishbtn.Add_Tail(*commands); + if(Ipx.Is_IPX()) { + ipxbtn.Add_Tail(*commands); + } +#ifdef WOLAPI_INTEGRATION + wolbtn.Add_Tail(*commands); // ajw +#endif + cancelbtn.Add_Tail(*commands); + + //------------------------------------------------------------------------ + // Fill array of button ptrs + //------------------------------------------------------------------------ + curbutton = 0; + buttons[0] = &modemserialbtn; + buttons[1] = &skirmishbtn; + if(Ipx.Is_IPX()) { + buttons[2] = &ipxbtn; +#ifdef WOLAPI_INTEGRATION + buttons[3] = &wolbtn; // ajw + buttons[4] = &cancelbtn; +#else + buttons[3] = &cancelbtn; +#endif + } else { +#ifdef WOLAPI_INTEGRATION + buttons[2] = &wolbtn; // ajw + buttons[3] = &cancelbtn; +#else + buttons[2] = &cancelbtn; +#endif + } + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + seen_buff_save.Blit(VisiblePage); + display = REDRAW_ALL; + } + #endif + + //..................................................................... + // Invoke game callback + //..................................................................... + Call_Back(); + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + //............................................................... + // Refresh the backdrop + //............................................................... + Load_Title_Page(true); + CCPalette.Set(); + + //............................................................... + // Draw the background + //............................................................... + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_SELECT_MPLAYER_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_MODEMSERIAL | KN_BUTTON): + selection = BUTTON_MODEMSERIAL; + pressed = true; + break; + + case (BUTTON_SKIRMISH | KN_BUTTON): + selection = BUTTON_SKIRMISH; + pressed = true; + break; + + case (BUTTON_IPX | KN_BUTTON): + selection = BUTTON_IPX; + pressed = true; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOL | KN_BUTTON): // ajw + selection = BUTTON_WOL; + pressed = true; + break; +#endif + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (num_of_buttons - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_MODEMSERIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + + //.................................................................. + // to make sure the selection is correct in case they used the mouse + //.................................................................. + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_MODEMSERIAL; + if(selection == BUTTON_CANCEL && !Ipx.Is_IPX()) curbutton--; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_MODEMSERIAL): + + //............................................................ + // Pop up the modem/serial/com port dialog + //............................................................ + retval = Select_Serial_Dialog(); + + if (retval != GAME_NORMAL) { + process = false; + } else { + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + } + break; + + case (BUTTON_SKIRMISH): + Session.Type = GAME_SKIRMISH; +#if (0) //PG + if (Com_Scenario_Dialog(true)) { + retval = GAME_SKIRMISH; + process = false; +#ifdef FIXIT_VERSION_3 + bAftermathMultiplayer = Is_Aftermath_Installed(); + // ajw I'll bet this was needed before also... + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); +#endif + } else { + buttons[curbutton]->IsPressed = false; + Session.Type = GAME_NORMAL; + display = REDRAW_ALL; + } +#endif + break; + + case (BUTTON_IPX): + retval = GAME_IPX; + process = false; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOL): // ajw + retval = GAME_INTERNET; + process = false; + break; +#endif + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } + return(retval); + +} /* end of Select_MPlayer_Game */ + + +/*************************************************************************** + * Clear_Listbox -- clears the given list box * + * * + * This routine assumes the items in the given list box are character * + * buffers; it deletes each item in the list, then clears the list. * + * * + * INPUT: * + * list ptr to listbox * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Listbox(ListClass * list) +{ + char * item; + + //------------------------------------------------------------------------ + // Clear the list box + //------------------------------------------------------------------------ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + list->Flag_To_Redraw(); + +} // end of Clear_Listbox + + +/*************************************************************************** + * Clear_Vector -- clears the given NodeNameType vector * + * * + * INPUT: * + * vector ptr to vector to clear * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Vector(DynamicVectorClass * vector) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the 'Players' Vector + //------------------------------------------------------------------------ + for (i = 0; i < vector->Count(); i++) { + delete (*vector)[i]; + } + vector->Clear(); + +} // end of Clear_Vector + + +/*************************************************************************** + * Computer_Message -- "sends" a message from the computer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +void Computer_Message(void) +{ +#ifdef NEVER + int color; + HousesType house; + HouseClass * ptr; + + //------------------------------------------------------------------------ + // Find the computer house that the message will be from + //------------------------------------------------------------------------ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + //..................................................................... + // Decode this house's color + //..................................................................... + color = ptr->RemapColor; + + //..................................................................... + // We now have a 1/4 chance of echoing one of the human players' + // messages back. + //..................................................................... + if (Percent_Chance(25)) { + + //.................................................................. + // Now we have a 1/3 chance of garbling the human message. + //.................................................................. + if (Percent_Chance(33)) { + Garble_Message(Session.LastMessage); + } + + //.................................................................. + // Only add the message if there is one to add. + //.................................................................. + if (strlen(Session.LastMessage)) { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Session.LastMessage, + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + else { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Text_String(TXT_COMP_MSG1 + Random_Pick(0, 12)), + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return; + } +#endif +} /* end of Computer_Message */ + + +#ifdef NEVER +/*************************************************************************** + * Garble_Message -- "garbles" a message * + * * + * INPUT: * + * buf buffer to garble; stores output message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +static void Garble_Message(char * buf) +{ + char txt[80]; + char punct[20]; // for punctuation + char * p; // working ptr + int numwords; // # words in the phrase + char * words[40]; // ptrs to various words in the phrase + int i,j; + + //------------------------------------------------------------------------ + // Pull off any trailing punctuation + //------------------------------------------------------------------------ + p = buf + strlen(buf) - 1; + while (1) { + if (p < buf) + break; + if (p[0]=='!' || p[0]=='.' || p[0]=='?') { + p--; + } + else { + p++; + break; + } + if (strlen(p) >= (sizeof(punct) - 1) ) { + break; + } + } + strcpy (punct, p); + p[0] = 0; + + for (i = 0; i < 40; i++) { + words[i] = NULL; + } + + //------------------------------------------------------------------------ + // Copy the original buffer + //------------------------------------------------------------------------ + strcpy(txt, buf); + + //------------------------------------------------------------------------ + // Split it up into words + //------------------------------------------------------------------------ + p = strtok (txt, " "); + numwords = 0; + while (p) { + words[numwords] = p; + numwords++; + p = strtok (NULL, " "); + } + + //------------------------------------------------------------------------ + // Now randomly put the words back. Don't use the real random-number + // generator, since different machines will have different LastMessage's, + // and will go out of sync. + //------------------------------------------------------------------------ + buf[0] = 0; + for (i = 0; i < numwords; i++) { + j = Sim_IRandom(0, numwords); + if (words[j] == NULL) { // this word has been used already + i--; + continue; + } + strcat(buf, words[j]); + words[j] = NULL; + if (i < numwords-1) + strcat(buf, " "); + } + strcat(buf, punct); + +} /* end of Garble_Message */ +#endif + + +/*************************************************************************** + * Surrender_Dialog -- Prompts user for surrendering * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = user cancels, 1 = user wants to surrender. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(int text) +{ + return Surrender_Dialog( Text_String( text ) ); +} +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text) +#else +int Surrender_Dialog(int text) +#endif +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 240*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + D_DIALOG_X = ((320*RESFACTOR - D_DIALOG_W) / 2),// centered x-coord + D_DIALOG_Y = ((200*RESFACTOR - D_DIALOG_H) / 2),// centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_OK_W = 45*RESFACTOR, // OK width + D_OK_H = 9*RESFACTOR, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5*RESFACTOR, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN*2, // OK y + + D_CANCEL_W = 45*RESFACTOR, // Cancel width + D_CANCEL_H = 9*RESFACTOR, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5*RESFACTOR, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN*2, // Cancel y + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + buttons[0] = &okbtn; + buttons[1] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... +#ifdef FIXIT_VERSION_3 // Stalemate games. + Fancy_Text_Print(text, + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(Text_String(text), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_OK | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_CANCEL | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +/*************************************************************************** + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = user confirms abort, 0 = user cancels * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +int Abort_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 170*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + D_DIALOG_X = ((320*RESFACTOR - D_DIALOG_W) / 2),// centered x-coord + D_DIALOG_Y = ((200*RESFACTOR - D_DIALOG_H) / 2),// centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_YES_W = 45*RESFACTOR, // YES width + D_YES_H = 9*RESFACTOR, // YES height + D_YES_X = D_DIALOG_CX - D_YES_W - 5*RESFACTOR, // YES x + D_YES_Y = D_DIALOG_Y + D_DIALOG_H - D_YES_H - D_MARGIN*2, // YES y + + D_NO_W = 45*RESFACTOR, // Cancel width + D_NO_H = 9*RESFACTOR, // Cancel height + D_NO_X = D_DIALOG_CX + 5*RESFACTOR, // Cancel x + D_NO_Y = D_DIALOG_Y + D_DIALOG_H - D_NO_H - D_MARGIN*2, // Cancel y + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_YES = 100, + BUTTON_NO, + }; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, TPF_BUTTON, D_YES_X, D_YES_Y, D_YES_W, D_YES_H); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, TPF_BUTTON, D_NO_X, D_NO_Y, D_NO_W, D_NO_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &yesbtn; + nobtn.Add_Tail(*commands); + + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... + Fancy_Text_Print(Text_String(TXT_CONFIRM_EXIT), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_YES | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_NO | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +#if(TEN) +/*************************************************************************** + * Read_TEN_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_TEN_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + if (Session.TenPlayerID == -1) { + return (0); + } + Session.ColorIdx = (PlayerColorType)Session.TenPlayerID; + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + Session.NumPlayers = ini.Get_Int("Options", "Players", 2); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * Read_MPATH_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_MPATH_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + Session.ColorIdx = (PlayerColorType)ini.Get_Int("Options", "Color", 0); + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // MPATH + +/************************** end of mplayer.cpp *****************************/ diff --git a/REDALERT/MPLIB.CPP b/REDALERT/MPLIB.CPP new file mode 100644 index 000000000..f46128b46 --- /dev/null +++ b/REDALERT/MPLIB.CPP @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "types.h" +#include "mgenord.h" +#include "magic.h" +#include "rtq.h" +#include +#include +#include +#include "mplib.h" + +#define CHUNNEL_INT 0x48 + +typedef union REGS REGISTERS; + +void +Yield(void) +{ + REGISTERS regs; + + regs.w.ax = 0x1680; + int386(0x2f, ®s, ®s); +} + +void +PostWindowsMessage(void) +{ + REGISTERS regs; + + regs.x.eax = DPMIAPI_POST_WINDOWS_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = 0; + regs.x.ecx = 0; + int386(CHUNNEL_INT, ®s, ®s); +} + +int MGenGetQueueCtr(int qNo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETQUEUECTR_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qNo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +RTQ_NODE *MGenMoveTo(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MOVENODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetNode(int q) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = q; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetMasterNode(unsigned *size) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MASTERNODE_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + *size = regs.x.ecx; + + return (RTQ_NODE *) regs.x.eax; +} + +int MGenFlushNodes(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_FLUSHNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenMCount(unsigned lowerOrderBits, unsigned upperOrderBits) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MCOUNT_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = lowerOrderBits; + regs.x.ecx = upperOrderBits; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenSanityCheck(void) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_SANITYCHECK_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + \ No newline at end of file diff --git a/REDALERT/MPMGRD.CPP b/REDALERT/MPMGRD.CPP new file mode 100644 index 000000000..33254e27c --- /dev/null +++ b/REDALERT/MPMGRD.CPP @@ -0,0 +1,363 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "mpmgrd.h" + +extern "C" { +#include +#include +#include +#include +#include "types.h" +#include "rtq.h" +#include "services.h" +} +#include "mplib.h" +#include "mplpc.h" + + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +typedef struct { + DWORD address; + char Data[1]; +} packet; + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +#define FREEQUEUE 0 +#define DOSWORKQUEUE 1 +#define WINWORKQUEUE 2 +#define WINSENDQUEUE 3 +#define DOSPENDINGQUEUE 4 +#define WINWORKQUEUE2 5 + +// 6, 7, 8, taken up by LPC services + +#define GDOSWORKQUEUE 14 +#define GWINWORKQUEUE 15 +#define GWINSENDQUEUE 16 +#define GDOSPENDINGQUEUE 17 +#define GWINWORKQUEUE2 18 + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + unsigned size; + + MGenGetMasterNode(&size); + if (size != sizeof(RTQ_NODE)) { + exit(-234); + } + _myAddr = LPCGetMPAddr(); + _nConnections = 0; + + for (int i = 0; i < CONNECT_MAX; i++) { + _Connections[i] = 0; + strcpy(_Names[i], ""); + } +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int /* ack_req */, + int conn_id) +{ + RTQ_NODE *n; + int idx = Connection_Index(conn_id); + + if (_nConnections == 0) { + return (STATUS_OK); + } + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (conn_id == CONNECTION_NONE) { + p->address = BROADCAST_ADDR; + } else { + p->address = _Connections[idx]; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, WINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + RTQ_NODE *n; + int i; + + if ((n = MGenMoveTo(DOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + *conn_id = CONNECTION_NONE; + for (i = 0; i < _nConnections; i++) { + if (p->address == _Connections[i]) { + (*conn_id) = _ID[i]; + break; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int /*ack_req*/, + int address) +{ + RTQ_NODE *n; + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (address == 0) { + p->address = BROADCAST_ADDR; + } else { + p->address = address; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, GWINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + RTQ_NODE *n; + + if ((n = MGenMoveTo(GDOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + if (address) { + if (p->address == BROADCAST_ADDR) { + *address = 0; + } else { + *address = p->address; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + _Connections[_nConnections] = address; + _ID[_nConnections] = id; + strcpy(_Names[_nConnections], name); + _nConnections++; + return STATUS_OK; +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == -1) + return 0; + + for (i = idx; i < _nConnections - 1; i++) { + _Connections[i] = _Connections[i+1]; + _ID[i] = _ID[i+1]; + strcpy (_Names[i], _Names[i+1]); + } + _nConnections--; + return STATUS_OK; +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (NULL); + } + + return _Names[idx]; +} + +int +MPlayerManClass::Connection_Address(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (0); + } + + return _Connections[idx]; +} + +int +MPlayerManClass::Num_Connections(void) +{ + return _nConnections; +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return _ID[index]; +} + +int +MPlayerManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < _nConnections; i++) { + if (_ID[i] == id) { + return i; + } + } + + return -1; +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return MGenGetQueueCtr(DOSPENDINGQUEUE); +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return STATUS_OK; +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + TGAMEDEF game_def; + int sz = sizeof(game_def); + + GetGameDef(&game_def, &sz); + + return (game_def.numPlayers - 1); + +} + + +void MPlayerManClass::Flush_All(void) +{ + MGenFlushNodes(DOSPENDINGQUEUE, FREEQUEUE); + MGenFlushNodes(GDOSPENDINGQUEUE, FREEQUEUE); +} + diff --git a/REDALERT/MPMGRD.H b/REDALERT/MPMGRD.H new file mode 100644 index 000000000..cb8c34a3b --- /dev/null +++ b/REDALERT/MPMGRD.H @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + int _myAddr; + int _Connections[CONNECT_MAX]; + int _ID[CONNECT_MAX]; + char _Names[CONNECT_MAX][MAX_NAME_LEN]; + int _nConnections; +}; + +#endif // mpmgr_h + + \ No newline at end of file diff --git a/REDALERT/MPMGRW.CPP b/REDALERT/MPMGRW.CPP new file mode 100644 index 000000000..5a7ee023c --- /dev/null +++ b/REDALERT/MPMGRW.CPP @@ -0,0 +1,297 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#if (0)//PG +#ifndef MP_LOAD_DLL_DYNAMICALLY +#define MP_LOAD_DLL_DYNAMICALLY +#endif + +#include "mpmgrw.h" + +extern "C" { +#include +#include +#include +#include +} + +#include + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +#define PRIVATE 0 +#define PUBLIC 1 + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef void __cdecl (*MPlayerInit_Type)(void); +typedef void __cdecl (*MPlayerDestroy_Type)(void); +typedef int __cdecl (*Send_Private_Message_Type)(void *buf, + int buflen, + int ack_req, + int conn_id); +typedef int __cdecl (*Get_Private_Message_Type)(void *buf, int *buflen, + int *conn_id); +typedef int __cdecl (*Send_Global_Message_Type)(void *buf, int buflen, int ack_req, + int addr); +typedef int __cdecl (*Get_Global_Message_Type)(void *buf, int *buflen, int *address); +typedef int __cdecl (*Create_Connection_Type)(int id, char *name, int address); +typedef int __cdecl (*Delete_Connection_Type)(int id); +typedef char * __cdecl (*Connection_Name_Type)(int id); +typedef int __cdecl (*Connection_Address_Type)(int id); +typedef int __cdecl (*Num_Connections_Type)(void); +typedef int __cdecl (*Connection_ID_Type)(int id); +typedef int __cdecl (*Connection_Index_Type)(int id); +typedef int __cdecl (*Init_Type)(void); +typedef int __cdecl (*Find_Num_Connections_Type)(void); + +MPlayerInit_Type MPlayerManCreate; +MPlayerDestroy_Type MPlayerManDestroy; +Send_Private_Message_Type Send_Private_Message_DLL; +Get_Private_Message_Type Get_Private_Message_DLL; +Send_Global_Message_Type Send_Global_Message_DLL; +Get_Global_Message_Type Get_Global_Message_DLL; +Create_Connection_Type Create_Connection_DLL; +Delete_Connection_Type Delete_Connection_DLL; +Connection_Name_Type Connection_Name_DLL; +Connection_Address_Type Connection_Address_DLL; +Num_Connections_Type Num_Connections_DLL; +Connection_ID_Type Connection_ID_DLL; +Connection_Index_Type Connection_Index_DLL; +Init_Type Init_DLL; +Find_Num_Connections_Type Find_Num_Connections_DLL; + + +FARPROC MPGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + FARPROC ret=GetProcAddress(hModule, lpProcName); + if (!ret) { + char msg[255]; + sprintf(msg, "Unable to load function %s from %s", + lpProcName, "RA95MP.DLL"); + MessageBox(0, msg, "Warning", MB_OK); + } + return ret; +} + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + HMODULE lib; + + lib = LoadLibrary("ra95mp.dll"); + if (lib != 0) { + MPlayerManCreate = (MPlayerInit_Type) MPGetProcAddress(lib, + "MPlayerManCreate"); + MPlayerManDestroy = (MPlayerDestroy_Type) MPGetProcAddress(lib, + "MPlayerManDestroy"); + Send_Private_Message_DLL = (Send_Private_Message_Type) MPGetProcAddress(lib, + "Send_Private_Message"); + Get_Private_Message_DLL = (Get_Private_Message_Type) MPGetProcAddress(lib, + "Get_Private_Message"); + Send_Global_Message_DLL = (Send_Global_Message_Type) MPGetProcAddress(lib, + "Send_Global_Message"); + Get_Global_Message_DLL = (Get_Global_Message_Type) MPGetProcAddress(lib, + "Get_Global_Message"); + Create_Connection_DLL = (Create_Connection_Type) MPGetProcAddress(lib, + "Create_Connection"); + Delete_Connection_DLL = (Delete_Connection_Type) MPGetProcAddress(lib, + "Delete_Connection"); + Connection_Name_DLL = (Connection_Name_Type) MPGetProcAddress(lib, + "Connection_Name"); + Connection_Address_DLL = (Connection_Address_Type) MPGetProcAddress(lib, + "Connection_Address"); + Num_Connections_DLL = (Num_Connections_Type) MPGetProcAddress(lib, + "Num_Connections"); + Connection_ID_DLL = (Connection_ID_Type) MPGetProcAddress(lib, + "Connection_ID"); + Connection_Index_DLL = (Connection_Index_Type) MPGetProcAddress(lib, + "Connection_Index"); + Init_DLL = (Init_Type) MPGetProcAddress(lib, + "Init"); + Find_Num_Connections_DLL = (Find_Num_Connections_Type) MPGetProcAddress(lib, + "Find_Num_Connections"); + } else { + MessageBox(0, "RA95MP.DLL not found!", "Warning", MB_OK); + exit(0); + } + + if (MPlayerManCreate != 0) { + MPlayerManCreate(); + } +} + +MPlayerManClass::~MPlayerManClass() +{ + MPlayerManDestroy(); +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int ack_req, + int conn_id) +{ + return Send_Private_Message_DLL(buf, buflen, ack_req, conn_id); +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + return Get_Private_Message_DLL(buf, buflen, conn_id); +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int ack_req, + int addr) +{ + return Send_Global_Message_DLL(buf, buflen, ack_req, addr); +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + return Get_Global_Message_DLL(buf, buflen, address); +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + return Create_Connection_DLL(id, name, address); +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + return Delete_Connection_DLL(id); +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + return Connection_Name_DLL(id); +} + +int +MPlayerManClass::Connection_Address(int id) +{ + return Connection_Address_DLL(id); +} + +int +MPlayerManClass::Num_Connections(void) +{ + return Num_Connections_DLL(); +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return Connection_ID_DLL(index); +} + +int +MPlayerManClass::Connection_Index(int id) +{ + return Connection_Index_DLL(id); +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return 0; +// return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return 0; +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return Init_DLL(); +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + return Find_Num_Connections_DLL(); +} + + +void MPlayerManClass::Flush_All(void) +{ +} + +#endif \ No newline at end of file diff --git a/REDALERT/MPMGRW.H b/REDALERT/MPMGRW.H new file mode 100644 index 000000000..bfe7e6dc9 --- /dev/null +++ b/REDALERT/MPMGRW.H @@ -0,0 +1,85 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" +#include "mplayer.h" +#include + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + ~MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + //HGULP _gulp; + //HGULP _pgulp; + //GAMEDEF _gameDef; + //int _myAddr; + //int _Connections[CONNECT_MAX]; + //int _ID[CONNECT_MAX]; + //char _Names[CONNECT_MAX][MAX_NAME_LEN]; + //int _nConnections; +}; + +#endif // mpmgr_h + diff --git a/REDALERT/MPU.CPP b/REDALERT/MPU.CPP new file mode 100644 index 000000000..9e101b20a --- /dev/null +++ b/REDALERT/MPU.CPP @@ -0,0 +1,45 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPU.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPU.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mpu.h" + + +#ifdef __BORLANDC__ +unsigned long __cdecl Get_CPU_Clock(unsigned long & high) +{ + __asm db 0fh,031h + __asm mov [high],edx + return(_EAX); +} +#endif diff --git a/REDALERT/MPU.H b/REDALERT/MPU.H new file mode 100644 index 000000000..fb5e0536f --- /dev/null +++ b/REDALERT/MPU.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPU.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPU.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/15/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MPU_H +#define MPU_H + + +/*********************************************************************************************** + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * * + * This routine will return the internal Pentium clock accumulator. This accumulator is * + * incremented every clock tick. Since this clock value can get very very large, the value * + * returned is in 64 bits. The low half is returned directly, the high half is stored in * + * location specified. * + * * + * INPUT: high -- Reference to the high value of the 64 bit clock number. * + * * + * OUTPUT: Returns with the low half of the CPU clock value. * + * * + * WARNINGS: This instruction is only available on Pentium or later processors. * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +#if (0) +unsigned long Get_CPU_Clock(unsigned long & high); +#ifndef __BORLANDC__ +#pragma aux Get_CPU_Clock parm [esi] \ + modify [edx] \ + value [eax] = \ + "db 0fh,031h" \ + "mov [esi],edx" +#endif +#endif + +#endif diff --git a/REDALERT/MSGBOX.CPP b/REDALERT/MSGBOX.CPP new file mode 100644 index 000000000..a84f03c5a --- /dev/null +++ b/REDALERT/MSGBOX.CPP @@ -0,0 +1,517 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGBOX.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMessageBox::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + + +#ifdef FIXIT_VERSION_3 +bool cancel_current_msgbox = false; +#endif + +/*********************************************************************************************** + * WWMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int WWMessageBox::Process(const char * msg, const char * b1txt, const char * b2txt, const char * b3txt, bool preserve) +{ +#define BUFFSIZE (511) + char buffer[BUFFSIZE]; + int retval; + bool process; // loop while true + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + void * back; + BOOL display; // display level + int realval[5]; + +#ifndef WIN32 + int preservex,preservey,preservew,preserveh; +#endif + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + if (b1txt != NULL && *b1txt == '\0') b1txt = NULL; + if (b2txt != NULL && *b2txt == '\0') b2txt = NULL; + if (b3txt != NULL && *b3txt == '\0') b3txt = NULL; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt != NULL) { + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2 * RESFACTOR); + bwidth = max((String_Pixel_Width(b1txt) + (8 * RESFACTOR)), (30 * RESFACTOR)); + + if (b2txt != NULL) { + numbuttons = 2; + bwidth = max(((int)String_Pixel_Width( b2txt ) + (8 * RESFACTOR)), bwidth); + + if (b3txt != NULL) { + numbuttons = 3; + } + + } else { + numbuttons = 1; + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-1); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int width; + int height; + int lines = Format_Window_String(buffer, 255 * RESFACTOR, width, height); + TextPrintType tpf = TPF_TEXT; + + width = max(width, (90 * RESFACTOR)); + width += 40 * RESFACTOR; + height += (numbuttons == 0) ? (40 * RESFACTOR) : (60 * RESFACTOR); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + int printx = x + (20 * RESFACTOR); + + /* + ** Special hack to center a one line dialog box text. + */ + if (lines == 1) { + printx = x + width/2; + tpf = tpf | TPF_CENTER; + } + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + #ifdef WIN32 + VisiblePage.Blit(seen_buff_save); + #endif + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + //DOS BUILD GERMAN BUTTONS NEED TO ONE ON TOP OF THE OTHER VG 11/6/96 + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Center button. + */ + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Right button. + */ + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, 0, y + height - (bheight + (15 * RESFACTOR))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + memset(buttons, '\0', sizeof(buttons)); + if (numbuttons > 0) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else { + if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { +#ifndef WIN32 + preservex = max(0, x-4); + preservey = max(0, y-4); + preservew = min(width+8, 320-preservex); + preserveh = min(height+8, 200-preservey); + back = new char[preservew * preserveh]; + SeenBuff.To_Buffer(preservex, preservey, preservew, preserveh, back, preservew * preserveh); +#else + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); +#endif + } + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + seen_buff_save.Blit(VisiblePage); + display = true; + } + #endif + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); +#ifdef FIXIT_VERSION_3 + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = KN_ESC; + } +#endif + switch (input) { + case (KN_ESC): + selection = realval[numbuttons-1]; + pressed = true; + +#ifdef NEVER + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } +#endif + break; + + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (BUTTON_2|BUTTON_FLAG): + if (numbuttons > 2) { + selection = realval[2]; + } else { + selection = realval[1]; + } + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = realval[curbutton]; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + break; + } + + if (pressed) { + + TextButtonClass * toggle; + /* + ** Turn all the buttons off. + */ + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_1); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_2); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_3); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + + /* + ** Turn on and depress the button that was selected. + */ + if (selection == BUTTON_1 || selection == BUTTON_2 || selection == BUTTON_3) { + TextButtonClass * toggle = (TextButtonClass *)buttonlist->Extract_Gadget(selection); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + } + Hide_Mouse(); + buttonlist->Draw_All(true); + Show_Mouse(); + + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()) { + #ifdef WIN32 + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + #else + MCGA_Buffer_To_Page(preservex, preservey, preservew, preserveh, back, &SeenBuff); + #endif + } + SeenBuff.Unlock(); + + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int WWMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int WWMessageBox::Process(char const * msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/REDALERT/MSGBOX.H b/REDALERT/MSGBOX.H new file mode 100644 index 000000000..cb4c720e3 --- /dev/null +++ b/REDALERT/MSGBOX.H @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGBOX.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGBOX_H +#define MSGBOX_H + +#include "jshell.h" + +class WWMessageBox +{ + int Caption; + + public: + WWMessageBox(int caption=TXT_NONE) {Caption = caption;}; + int Process(const char *msg, const char *b1txt, const char *b2txt=NULL, const char *b3txt=NULL, bool preserve=false); + int Process(int msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); + int Process(char const *msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); +}; + +#endif + diff --git a/REDALERT/MSGLIST.CPP b/REDALERT/MSGLIST.CPP new file mode 100644 index 000000000..d8f1dea53 --- /dev/null +++ b/REDALERT/MSGLIST.CPP @@ -0,0 +1,1408 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGLIST.CPP 2 3/04/97 2:52p Joe_bostic $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : March 4, 1997 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MessageListClass::MessageListClass -- constructor * + * MessageListClass::~MessageListClass -- destructor * + * MessageListClass::Init -- Inits message system, sets options * + * MessageListClass::Add_Message -- displays the given message * + * MessageListClass::Get_Message -- retrieves given message * + * MessageListClass::Get_Label -- retrieves given text label * + * MessageListClass::Concat_Message -- concats the given message * + * MessageListClass::Add_Edit -- Adds editable string to message list * + * MessageListClass::Remove_Edit -- removes the edit field * + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * MessageListClass::Manage -- Manages multiplayer messages * + * MessageListClass::Input -- Handles input for sending messages * + * MessageListClass::Draw -- Draws the messages * + * MessageListClass::Num_Messages -- returns # messages in the list * + * MessageListClass::Set_Width -- sets allowable width of messages * + * MessageListClass::Trim_Message -- trims chars off start of message * + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * MessageListClass::Reset -- Reset so no messages are visible. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/**************************** Globals **************************************/ + + +/*************************************************************************** + * MessageListClass::MessageListClass -- constructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::MessageListClass(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init all data members + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = 0; + MessageY = 0; + MaxMessages = 0; + MaxChars = 0; + Height = 0; + + EnableOverflow = 0; + AdjustEdit = 0; + IsEdit = 0; + EditX = 0; + EditY = 0; + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + OverflowStart = 0; + OverflowEnd = 0; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + +} // end of MessageListClass + + +/*************************************************************************** + * MessageListClass::~MessageListClass -- destructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::~MessageListClass() +{ + Init(0,0,0,0,0,0,0,0,0,0); + +} // end of ~MessageListClass + + +/*************************************************************************** + * MessageListClass::Init -- Inits message system, sets options * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, NOT including edit message * + * maxchars max # characters allowed per message * + * height pixel height of a line of text * + * edit_x x-coord of edit field; -1 = put at the top of the * + * other messages * + * edit_y y-coord of edit field; -1 = put at the top of the * + * other messages * + * overflow_on true = enable the overflow typing feature * + * over_start start index for overflow processing * + * over_end end index for overflow processing * + * width pixel width of message buffer * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Init(int x, int y, int max_msg, int maxchars, + int height, int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width) +{ + TextLabelClass * txtlabel; + int i; + + Width = width; + + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = x; + MessageY = y; + + MaxMessages = max_msg; + if (MaxMessages > MAX_NUM_MESSAGES) + MaxMessages = MAX_NUM_MESSAGES; + + MaxChars = maxchars; + if (MaxChars > MAX_MESSAGE_LENGTH) + MaxChars = MAX_MESSAGE_LENGTH; + + Height = height; + + //------------------------------------------------------------------------ + // Init the edit field variables. If edit_x or edit_y is -1, place the + // edit field above the other messages; otherwise, place it at the desired + // coords. + //------------------------------------------------------------------------ + EnableOverflow = overflow_on; + IsEdit = 0; + if (edit_x == -1 || edit_y == -1) { + AdjustEdit = 1; + EditX = x; + EditY = y; + } + else { + AdjustEdit = 0; + EditX = edit_x; + EditY = edit_y; + } + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + + //------------------------------------------------------------------------ + // Init the overflow processing indices + //------------------------------------------------------------------------ + OverflowStart = over_start; + OverflowEnd = over_end; + if (OverflowEnd >= MaxChars) { + OverflowEnd = MaxChars - 1; + } + if (OverflowStart >= OverflowEnd) { + OverflowStart = OverflowEnd - 1; + } + +} // end of Init + + +/*********************************************************************************************** + * MessageListClass::Reset -- Reset so no messages are visible. * + * * + * This routine will reset the message list tracker so that any displayed messages are * + * cleared. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/04/1997 JLB : Created. * + *=============================================================================================*/ +void MessageListClass::Reset(void) +{ + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + TextLabelClass * txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (int index = 0; index < MAX_NUM_MESSAGES; index++) { + BufferAvail[index] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + EditLabel = 0; + IsEdit = 0; +} + +extern void On_Message(const char* message, float timeout_seconds, long long message_id); + +/*************************************************************************** + * MessageListClass::Add_Message -- displays the given message * + * * + * INPUT: * + * name name of sender, NULL = none * + * id numerical ID for this message * + * txt text to display * + * color color to draw text in * + * style style to use * + * timeout # of ticks the thing is supposed to last (-1 = forever) * + * * + * OUTPUT: * + * ptr to new TextLabelClass object. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + * 10/16/1996 JLB : Audio feedback added. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Message(char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout) +{ + TextLabelClass * txtlabel = NULL; + char message[MAX_MESSAGE_LENGTH + 30]; + + //------------------------------------------------------------------------ + // Combine the name & message text, if there's a name given + //------------------------------------------------------------------------ + if (name) { + sprintf(message, "%s:%s", name, txt); + id = -1; + } else { + strcpy(message, txt); + } + +#if (0) + + int i; + int found; + char temp[MAX_MESSAGE_LENGTH + 30]; + int print_this_pass; + char save = 0; + int mess_start; + + //------------------------------------------------------------------------ + // Check that printing this wont overrun the width of the print area on screen + //------------------------------------------------------------------------ + + print_this_pass = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[color], TBLACK, style); + int wid = String_Pixel_Width(message); + if (wid >= Width-8) { + //------------------------------------------------------------------------ + // Bugger. Its too long. Loop through and find out how many chars we can print + //------------------------------------------------------------------------ + if (name) { + sprintf (temp, "%s:", name); + mess_start = strlen (name)+1; + } else { + mess_start = 0; + } + for (int i=1 ; i<(int)strlen(txt) ; i++) { + strncpy (&temp[mess_start], txt, i); + temp [mess_start + i] = 0; + wid = String_Pixel_Width(temp); + if (wid >= Width-8) { + print_this_pass = mess_start + i-1; + break; + } + } + + //------------------------------------------------------------------------ + // Prematurely terminate the string so it doesn't all print. + // We will re-enter at the end to print the rest. + //------------------------------------------------------------------------ + if (print_this_pass) { + save = message [print_this_pass]; + message [print_this_pass] = 0; + } + } + + + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if ( (MaxMessages > 0) && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + + if (txtlabel==NULL) + return(NULL); + + //..................................................................... + // Remove this message from the list; mark its buffer as being available. + //..................................................................... + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // Create the message + //------------------------------------------------------------------------ + txtlabel = new TextLabelClass (message, MessageX, MessageY, + &ColorRemaps[color], style); + if (timeout==-1) { + txtlabel->UserData1 = 0; + } + else { + txtlabel->UserData1 = TickCount + timeout; + } + txtlabel->UserData2 = id; + + //------------------------------------------------------------------------ + // Find a buffer to store our message in; if there are none, don't add the + // message. + //------------------------------------------------------------------------ + found = 0; + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (BufferAvail[i]) { + BufferAvail[i] = 0; + memset (MessageBuffers[i],0,MAX_MESSAGE_LENGTH + 30); + strcpy (MessageBuffers[i],message); + txtlabel->Text = MessageBuffers[i]; + found = 1; + break; + } + } + if (!found) { + delete txtlabel; + return (NULL); + } + +#endif + + On_Message(message, timeout * 60.0f / TICKS_PER_MINUTE, id); + //Sound_Effect(VOC_INCOMING_MESSAGE); + +#if (0) + + //------------------------------------------------------------------------ + // Attach the message to our list + //------------------------------------------------------------------------ + if (MessageList) { + txtlabel->Add_Tail (*MessageList); + } + else { + MessageList = txtlabel; + } + + //------------------------------------------------------------------------ + // Recompute all messages' y-coordinate values + //------------------------------------------------------------------------ + Compute_Y(); + + //------------------------------------------------------------------------ + // If we terminated the string before the end then we need to reenter to + // add a new message with the rest of the string. + //------------------------------------------------------------------------ + if (save) { + message [print_this_pass] = save; + Add_Message (name, id, &message [print_this_pass], color, style, timeout); + } + +#endif + + return(txtlabel); + +} // end of Add_Message + + +/*************************************************************************** + * MessageListClass::Get_Message -- retrieves given message * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Message(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg->Text); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Message + + +/*************************************************************************** + * MessageListClass::Get_Label -- retrieves given text label * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Get_Label(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Label + + +/*************************************************************************** + * MessageListClass::Concat_Message -- concats the given message * + * * + * INPUT: * + * name name of sender; NULL = none * + * id ID of message to concatenate to * + * txt text to concatenate onto existing message * + * timeout new timeout for message * + * * + * OUTPUT: * + * 1 = OK, 0 = error (id or name not found) * + * * + * WARNINGS: * + * If the required message doesn't exist, this routine does nothing. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Concat_Message(char const * name, int id, char const * txt, int timeout) +{ + int min_chars; + int max_chars; + char * msg; + TextLabelClass * tlabel; + int found; + + //------------------------------------------------------------------------ + // If no name is given, or the concatenation feature is turned off, + // don't concatenate the message + //------------------------------------------------------------------------ + if (!name || !EnableOverflow) { + return (0); + } + + //------------------------------------------------------------------------ + // Scan through all active messages, searching for one with a matching + // name & ID + //------------------------------------------------------------------------ + found = 0; + if (MessageList) { + tlabel = MessageList; + while (tlabel) { + if (tlabel->UserData2 == id && + !memcmp(tlabel->Text,name,strlen(name))) { + found = 1; + break; + } + tlabel = (TextLabelClass *)tlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // name and ID not found; return + //------------------------------------------------------------------------ + if (!found) { + return (0); + } + + //------------------------------------------------------------------------ + // set a pointer to the text string, plus the name and colon + //------------------------------------------------------------------------ + msg = tlabel->Text + strlen(name) + 1; + + //------------------------------------------------------------------------ + // If there's room enough in the message, just add the given string + //------------------------------------------------------------------------ + if ( (int)(strlen(msg) + strlen(txt)) < MaxChars) { + + //--------------------------------------------------------------------- + // We need to trim the message if there is no room to draw it + //--------------------------------------------------------------------- + char *concat_test = new char [MaxChars+1]; + Fancy_Text_Print(TXT_NONE, 0, 0, tlabel->Color, TBLACK, tlabel->Style); + int name_width = String_Pixel_Width(tlabel->Text) - String_Pixel_Width(msg); + int width; + + strcpy (concat_test, msg); + strcat (concat_test, txt); + width = String_Pixel_Width(concat_test) + name_width; + min_chars = 10; + + while (width >= Width-8){ + + max_chars = strlen (msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + + Trim_Message (NULL, msg, min_chars, max_chars, 0); + + strcpy (concat_test, msg); + strcat (concat_test, txt); + + width = String_Pixel_Width(concat_test) + name_width; + }; + + delete [] concat_test; + + strcat (msg,txt); + } + + //------------------------------------------------------------------------ + // Otherwise, trim off some characters from the beginning of the + // message. Trim off at least enough to leave room for the new text. + // Trim from left to right to remove the minimum required text. + //------------------------------------------------------------------------ + else { + min_chars = (strlen(msg) + strlen(txt)) - MaxChars; + max_chars = strlen(msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + Trim_Message (NULL, msg, min_chars, max_chars, 0); + strcat (msg, txt); + } + + //------------------------------------------------------------------------ + // Set the new timeout value for the message + //------------------------------------------------------------------------ + if (timeout==-1) { + tlabel->UserData1 = 0; + } + else { + tlabel->UserData1 = TickCount + timeout; + } + + return (1); + +} // end of Concat_Message + + + +/*********************************************************************************************** + * MessageListClass::Set_Edit_Focus -- Give the gadget system focus to the edit box * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +void MessageListClass::Set_Edit_Focus (void) +{ + if (IsEdit) EditLabel->Set_Focus(); +} + + +/*********************************************************************************************** + * MessageListClass::Has_Edit_Focus -- Find out if the edit box has the input focus * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +bool MessageListClass::Has_Edit_Focus (void) +{ + if (IsEdit){ + return (EditLabel->Has_Focus()); + }else{ + return(false); + } +} + + + +/*************************************************************************** + * MessageListClass::Add_Edit -- Adds editable string to message list * + * * + * INPUT: * + * color color of edit message * + * style style of edit message * + * to string: who to send to; NULL = none * + * cursor character to use as a cursor; 0 = none * + * * + * OUTPUT: * + * ptr to new TextLabelClass * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Edit(PlayerColorType color, + TextPrintType style, char * to, char cursor, int width) +{ + int i; + TextLabelClass * txtlabel; + + //------------------------------------------------------------------------ + // Do nothing if we're already in "edit" mode + //------------------------------------------------------------------------ + if (IsEdit) { + EditLabel->Set_Focus(); + return(NULL); + } + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if (AdjustEdit && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // If no 'to' field was passed in, ignore it + //------------------------------------------------------------------------ + if (!to) { + to = ""; + } + + //------------------------------------------------------------------------ + // Set the cursor character + //------------------------------------------------------------------------ + CursorChar = cursor; + + //------------------------------------------------------------------------ + // Initialize the buffer positions; create a new text label object + //------------------------------------------------------------------------ + memset (EditBuf, 0, sizeof(EditBuf)); + strcpy (EditBuf, to); + OverflowBuf[0] = 0; + EditCurPos = EditInitPos = strlen(to); + EditLabel = new TextLabelClass (EditBuf, EditX, EditY, + &ColorRemaps[color], style); + + Width = width; + + if (EditLabel) { + IsEdit = 1; + EditLabel->Set_Focus(); + } + else { + IsEdit = 0; + } + + //------------------------------------------------------------------------ + // If the edit field appears over the message list, recompute the y-value + // for all messages. Also, adjust MaxMessages down by one, since there + // is now one less slot available. + //------------------------------------------------------------------------ + if (AdjustEdit) { + Compute_Y(); + MaxMessages--; + } + + return(EditLabel); + +} // end of Add_Edit + + +/*************************************************************************** + * MessageListClass::Remove_Edit -- removes the edit field * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/06/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Remove_Edit(void) +{ + //------------------------------------------------------------------------ + // If the edit field is active, delete it + //------------------------------------------------------------------------ + if (IsEdit) { + IsEdit = 0; + delete EditLabel; + + //..................................................................... + // If the edit field appears over the message list, recompute the + // y-value for all messages. Adjust MaxMessages back up, since there + // is now a new available slot. + //..................................................................... + if (AdjustEdit) { + Compute_Y(); + MaxMessages++; + } + } + +} // end if Remove_Edit + + +/*************************************************************************** + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to edit buffer, minus the "To:" header * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Edit_Buf(void) +{ + return(EditBuf + EditInitPos); + +} // end of Get_Edit_Buf + + +/*************************************************************************** + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * * + * INPUT: * + * color color to set edit label to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Edit_Color(PlayerColorType color) +{ + if (IsEdit) { + EditLabel->Color = &ColorRemaps[color]; + } + +} // end of Set_Edit_Color + + +/*************************************************************************** + * MessageListClass::Manage -- Manages multiplayer messages * + * * + * If this routine returns TRUE, the caller should update the display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 0 = no change has occurred, 1 = changed * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Manage (void) +{ + TextLabelClass * txtlabel; + TextLabelClass * next; + int changed = 0; + int i; + + //------------------------------------------------------------------------ + // Loop through all messages + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + + //..................................................................... + // If this message's time is up, remove it from the list + //..................................................................... + if (txtlabel->UserData1 != 0 && TickCount > txtlabel->UserData1) { + + //.................................................................. + // Save the next ptr in the list; remove this entry + //.................................................................. + next = (TextLabelClass *)txtlabel->Get_Next(); + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) { + BufferAvail[i] = 1; + } + } + delete txtlabel; + changed = 1; + txtlabel = next; + } + else { + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // If a changed has been made, recompute the y-coord of all messages + //------------------------------------------------------------------------ + if (changed) { + Compute_Y(); + } + + return(changed); + +} // end of Manage + + +/*************************************************************************** + * MessageListClass::Input -- Handles input for sending messages * + * * + * INPUT: * + * input key value to process * + * * + * OUTPUT: * + * 1 = caller should redraw the message list (no need to complete * + * refresh, though) * + * 2 = caller should completely refresh the display. * + * 3 = caller should send the edit message. * + * (sets 'input' to 0 if it processes it.) * + * 4 = caller should send the Overflow buffer * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Input(KeyNumType &input) +{ + KeyASCIIType ascii; + int retcode = 0; + int numchars; + + //------------------------------------------------------------------------ + // Do nothing if nothing to do. + //------------------------------------------------------------------------ + if (input == KN_NONE) { + return(0); + } + + //------------------------------------------------------------------------ + // Leave mouse events alone. + //------------------------------------------------------------------------ + if ( (input & (~KN_RLSE_BIT))==KN_LMOUSE || + (input & (~KN_RLSE_BIT))==KN_RMOUSE) { + return(0); + } + + //------------------------------------------------------------------------ + // If we're in 'edit mode', handle keys + //------------------------------------------------------------------------ + if (IsEdit) { + + + ascii = (KeyASCIIType)(Keyboard->To_ASCII(input) & 0x00ff); + +#ifdef WIN32 + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((input & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + input = (KeyNumType)(input & ~WWKEY_VK_BIT); + + } else { + /* + ** Filter out all special keys except return, escape and backspace + */ + if ((!(input & WWKEY_VK_BIT) && !(input & KN_BUTTON) + && ascii >= ' ' && ascii <= 127) + || (input & 0xff)== (KN_RETURN & 0xff) + || (input & 0xff)== (KN_BACKSPACE & 0xff) + || (input & 0xff)== (KN_ESC & 0xff) ) { + + //ascii = (KeyASCIIType)(Keyboard->To_ASCII(input)); + } else { + input = KN_NONE; + return (0); + } + } +#endif //WIN32 + + + + switch (ascii) { + //.................................................................. + // ESC = abort message + //.................................................................. + case KA_ESC & 0xff: + Remove_Edit(); + retcode = 2; + input = KN_NONE; + break; + + //.................................................................. + // RETURN = send the message. + // Add a space to the end, in case another message gets concatenated + // onto this one after we send it; then, they won't be mushed + // together. + //.................................................................. + case KA_RETURN & 0xff: + if (EditCurPos == EditInitPos) { + retcode = 0; + input = KN_NONE; + break; + } + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + EditBuf[EditCurPos] = ' '; + EditCurPos++; + EditBuf[EditCurPos] = 0; + } + Remove_Edit(); + retcode = 3; + input = KN_NONE; + break; + + //.................................................................. + // BACKSPACE = remove a character + //.................................................................. + case KA_BACKSPACE & 0xff: + if (EditCurPos > EditInitPos) { + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 2; + } + input = KN_NONE; + EditLabel->Set_Focus(); + break; + + //.................................................................. + // default: add a character. Reserve the last buffer position for + // null. (EditCurPos - EditInitPos) is the buffer index # of the + // next character, after the "To:" prefix. + //.................................................................. + default: + EditLabel->Set_Focus(); + bool overflowed = false; + if (ascii >= ' ' && ascii <= 127) { + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 1; + + /* + ** Verify that the additional character would not overrun the on screen edit box. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, EditLabel->Color, TBLACK, EditLabel->Style); + int width = String_Pixel_Width(EditBuf); + if (width >= Width-10) { + overflowed = true; + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 0; + } + } else { + //............................................................ + // If there's no room in the buffer, and overflow is enabled, + // trim the extra characters off (from right to left, to + // remove the max possible characters), and then add the new + // character in. + //............................................................ + overflowed = true; + } + + if (/*BGEnableOverflow &&*/ overflowed) { + numchars = Trim_Message (OverflowBuf, EditBuf + EditInitPos, + OverflowStart,OverflowEnd, 1); + EditCurPos -= numchars; + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 4; + } + } + input = KN_NONE; + break; + } + } + + return(retcode); + +} // end of Input + + +/*************************************************************************** + * MessageListClass::Draw -- draws messages * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Draw(void) +{ + char txt[2] = {0,0}; + + if (IsEdit) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + EditLabel->Draw_Me(true); + + if (CursorChar && (EditCurPos - EditInitPos) < (MaxChars - 1) && EditLabel->Has_Focus()) { + txt[0] = CursorChar; + Fancy_Text_Print(txt, + EditLabel->X + String_Pixel_Width(EditLabel->Text), + EditLabel->Y, + EditLabel->Color, + TBLACK, + EditLabel->Style); + } + + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + if (MessageList) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + MessageList->Draw_All(); + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + +} // end of Draw + + +/*************************************************************************** + * MessageListClass::Num_Messages -- returns # messages in the list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of messages, including the edit field if it's above the messages * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Num_Messages(void) +{ + GadgetClass * gadg; + int num; + + num = 0; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + num++; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit && AdjustEdit) { + num++; + } + + return (num); + +} // end of Num_Messages + + +/*************************************************************************** + * MessageListClass::Set_Width -- sets allowable width of messages * + * * + * INPUT: * + * width pixel width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Width(int width) +{ + GadgetClass * gadg; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + ((TextLabelClass *)gadg)->PixWidth = width; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit) { + EditLabel->PixWidth = width; + } + +} // end of Set_Width + + +/*************************************************************************** + * MessageListClass::Trim_Message -- trims chars off start of message * + * * + * INPUT: * + * dest buffer to store removed characters in; NULL = none * + * src text buffer to trim * + * min_chars min # chars that must be trimmed off * + * max_chars max # chars allowed to trim * + * scandir 0 = left-to-right, 1 = right-to-left * + * * + * OUTPUT: * + * # characters removed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Trim_Message(char * dest, char * src, int min_chars, + int max_chars, int scandir) +{ + int i; + int len; + int found; + + //------------------------------------------------------------------------ + // validate parameters + //------------------------------------------------------------------------ + if (min_chars <= 0) { + return(0); + } + + len = strlen (src); + if (max_chars > len) { + max_chars = len; + } + + //------------------------------------------------------------------------ + // find 1st available white space; if there is none, just trim off + // 'min_chars' characters. 'i' will be the number of chars to trim. + // The chars removed will include the white space. + //------------------------------------------------------------------------ + found = 0; + //........................................................................ + // scan from left to right + //........................................................................ + if (scandir == 0) { + for (i = min_chars; i <= max_chars; i++) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // scan from right to left + //........................................................................ + else { + for (i = max_chars; i >= min_chars; i--) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // If no whitespace was found, just set 'i' to the min # characters + //........................................................................ + if (!found) { + i = min_chars; + } + + //------------------------------------------------------------------------ + // Save trimmed characters in the dest buffer, if there is one + //------------------------------------------------------------------------ + if (dest) { + memcpy (dest, src, i); + dest[i] ='\0'; + } + + //------------------------------------------------------------------------ + // Shift characters over in the source buffer + //------------------------------------------------------------------------ + memmove (src, src + i, len - i + 1); + + return (i); + +} // end of Trim_Message + + +/*************************************************************************** + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Compute_Y(void) +{ + GadgetClass * gadg; + int y; + + //------------------------------------------------------------------------ + // If the editable message is attached to the message list, 'AdjustEdit' + // will be set; so, adjust all y-values downward one line. Otherwise, + // the editable message has its own screen coordinates. + //------------------------------------------------------------------------ + if (IsEdit && AdjustEdit) { + y = MessageY + Height; + } + else { + y = MessageY; + } + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + +} // end of Compute_Y + + +/*************************** end of msglist.cpp ****************************/ diff --git a/REDALERT/MSGLIST.H b/REDALERT/MSGLIST.H new file mode 100644 index 000000000..1e5d68882 --- /dev/null +++ b/REDALERT/MSGLIST.H @@ -0,0 +1,211 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGLIST.H 2 3/04/97 2:53p Joe_bostic $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : May 22, 1995 [BRR] * + * * + * Initializing: * + * Call Init(), giving it the coords of the upper-left corner to display * + * the messages, the coordinates to display the editable message, max # * + * messages allowed, max # chars per message, and the pixel height of * + * the font. MaxChars should be less than or equal to the define * + * MAX_MESSAGE_LENGTH. * + * * + * Displaying a message: * + * Call Add_Message(); the buffer you pass in is copied into a static * + * buffer in this class. Each message is given a timeout; after this * + * time, the message is removed from the displayed list. * + * Each message also has a 2-byte ID, which can be used to identify the + * sender. + * * + * Editing a message: * + * Call Add_Edit(), giving it the color & style to print in. An * + * optional "To xxx" prefix is allowed. The edit message can appear in * + * the message list itself (it's placed first), or at a separate screen * + * location, depending on the values passed to Init(). * + * * + * Updating messages: * + * The Input() routine passes new keys to the editable message; this * + * routine's return code tells the app whether to redraw the messages, * + * and whether to send it the edit message. As the user types * + * characters into the edit field, if he overflows the max storage * + * for the edit field, the field is parsed & the first few words * + * removed; these words are put into the Overflow buffer, and the app * + * is told so; the app should send this buffer across to the destination.* + * * + * - MessageList is a gadget list of all current messages * + * - MessageX & Y are the upper left corner of the 1st message * + * - MaxMessages is the max # of messages allowed, including the editable * + * message; 0 = no limit. * + * - MaxChars is the max # of chars allowed per message * + * - Height is the pixel height of a single line * + * - EditLabel points to the textmessage gadget for the current editable * + * field. EditBuf points to the char buffer being edited. EditInitPos * + * & EditCurPos define buffer index positions. * + * - EditSendAddress is the IPX Address to send the message to when RETURN * + * is pressed. * + * * + * The low word in the UserData field in the TextLabelClass tells what the * + * timeout for each message is (0 = none); the high byte of the UserData * + * field is used for the text message ID. * + * When a message's timeout expires, it's deleted. When a new message * + * is added, the top message is deleted if MPlayerMaxMessages is exceeded. * + * * + * The Edit-able message is never deleted until ESC or RETURN is pressed. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGLIST_H +#define MSGLIST_H + +//*************************************************************************** +// Defines +//*************************************************************************** +//--------------------------------------------------------------------------- +// Max length of inter-player message buffers. Messages.Init() should specify +// a value <= this. For editing messages, the "to" field length is added to +// this length to generate the entire editable message length. For displayed +// messages, a "From" prefix length should be added to this value to generate +// the entire max displayable message length. +//--------------------------------------------------------------------------- +#define MAX_MESSAGE_LENGTH 120 + +//--------------------------------------------------------------------------- +// Max # of allowed messages at one time +//--------------------------------------------------------------------------- +#define MAX_NUM_MESSAGES 14 + +//*************************************************************************** +// Forward declarations +//*************************************************************************** +class TextLabelClass; + +//*************************************************************************** +// Class declaration +//*************************************************************************** +class MessageListClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + MessageListClass (void); + ~MessageListClass (); + + //..................................................................... + // Initialization + //..................................................................... + void Reset(void); + void Init (int x, int y, int max_msg, int maxchars, int height, + int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width = 640); + TextLabelClass * Add_Message (char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout); + int Concat_Message (char const * name, int id, char const * txt, int timeout); + + //..................................................................... + // Message access utility routines + //..................................................................... + char * Get_Message (int id); + TextLabelClass * Get_Label (int id); + + //..................................................................... + // Message-editing support routines + //..................................................................... + TextLabelClass * Add_Edit(PlayerColorType color, TextPrintType style, + char *to, char cursor = 0, int width = 640); + void Remove_Edit (void); + char * Get_Edit_Buf (void); + char * Get_Overflow_Buf (void) {return (OverflowBuf);} + void Clear_Overflow_Buf (void) {OverflowBuf[0] = 0;} + int Is_Edit(void) {return (IsEdit);} + void Set_Edit_Color(PlayerColorType color); + + //..................................................................... + // Maintenance routines + //..................................................................... + int Manage (void); + int Input (KeyNumType &input); + void Draw(void); + int Num_Messages(void); + void Set_Width(int width); + void Set_Edit_Focus(void); + bool Has_Edit_Focus(void); + + private: + + //..................................................................... + // Message parsing + //..................................................................... + int Trim_Message(char *dest, char *src, int min_chars, int max_chars, + int scandir); + + //..................................................................... + // Compute the y-coord of the message list + //..................................................................... + void Compute_Y(void); + + //..................................................................... + // Private Data + //..................................................................... + TextLabelClass * MessageList; // list of messages + int MessageX; // x-coord of upper-left + int MessageY; // y-coord of upper-left + int MaxMessages; // max messages allowed + int MaxChars; // max allowed chars per message + int Height; // height in pixels + + //..................................................................... + // Data for the edit field: the edit field will either appear at + // exact coordinates specified by the application, or it will appear + // vertically above the other messages. + //..................................................................... + unsigned EnableOverflow : 1; // 1 = enable overflow feature + unsigned IsEdit : 1; // 1 = there's an edit field + unsigned AdjustEdit : 1; // 1 = edit field appears over msgs + int EditX; // x-coord of edit field + int EditY; // y-coord of edit field + TextLabelClass *EditLabel; // ptr to current edit label + char EditBuf[MAX_MESSAGE_LENGTH + 30]; // buffer for editable message + char OverflowBuf[MAX_MESSAGE_LENGTH + 30]; // overflow area + int EditCurPos; // current edit position + int EditInitPos; // initial edit position + char CursorChar; // character to use a cursor + int OverflowStart; // 1st index for overflow trimming + int OverflowEnd; // last index for overflow trimming + int Width; // Maximum width in pixels of editable string + + //..................................................................... + // Buffers provided for messages. They must be long enough for + // both the message, and for the "To" prefix on edited messages, or + // the "Name:" prefix on received messages. + //..................................................................... + char MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; + char BufferAvail[MAX_NUM_MESSAGES]; +}; + +#endif +/**************************** end of msglist.h *****************************/ + diff --git a/REDALERT/MiscAsm.cpp b/REDALERT/MiscAsm.cpp new file mode 100644 index 000000000..cd8e6397c --- /dev/null +++ b/REDALERT/MiscAsm.cpp @@ -0,0 +1,1572 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** +** Misc. assembly code moved from headers +** +** +** +** +** +*/ + +#include "FUNCTION.H" + + + +extern "C" void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy) +{ + memcpy(dest, source, bytes_to_copy); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2) +{ + __asm { + mov eax,[coord1] + mov ebx,[coord2] + mov dx,ax + sub dx,bx + jg okx + neg dx + okx: + shr eax,16 + shr ebx,16 + sub ax,bx + jg oky + neg ax +oky: + cmp ax,dx + jg ok + xchg ax,dx +ok: + shr dx,1 + add ax,dx + } +} + + + + +/* +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ + +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + static const char _new_facing16[] = { + 3, 2, 4,-1, 1, 2,0,-1, + 13,14,12,-1,15,14,0,-1, + 5, 6, 4,-1, 7, 6,8,-1, + 11,10,12,-1, 9,10,8,-1 + }; + + + __asm { + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[_new_facing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ + +int __cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) +{ + + __asm { + xor ebx,ebx //; Facing number. + + ////; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short xnotneg + neg ecx + mov ebx,11000000b //; Set bit 7 and 6 for leftward. +xnotneg: + + //; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ynotneg + xor ebx,01000000b //; Complement bit 6 for downward. + neg eax +ynotneg: + + //; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + //; Determine if the direction is closer to the Y axis and make sure that + //; CX holds the larger of the two deltas. This is in preparation for the + //; divide. + cmp eax,ecx + jb short gotaxis + xchg eax,ecx + xor edx,01000000b //; Closer to Y axis so make DX=64 for quad 0 and 2. +gotaxis: + + //; If closer to the X axis then add 64 for quadrants 0 and 2. If + //; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + //; add value is in DX and save on stack. + push edx + + //; Make sure that the division won't overflow. Reduce precision until + //; the larger number is less than 256 if it appears that an overflow + //; will occur. If the high byte of the divisor is not zero, then this + //; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short nooverflow +again: + test ecx,0FFFFFF00h + jz short nooverflow + shr ecx,1 + shr eax,1 + jmp short again +nooverflow: + + //; Make sure that the division won't underflow (divide by zero). If + //; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short nounderflow + mov eax,0FFFFFFFFh + jmp short divcomplete + + //; Derive a pseudo angle number for the octant. The angle is based + //; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +nounderflow: + xor edx,edx + shld edx,eax,8 //; shift high byte of eax into dl + shl eax,8 + div ecx +divcomplete: + + //; Integrate the 5 most significant bits into the angle index. If DX + //; is not zero, then it is 64. This means that the dividend must be negated + //; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short noneg + dec edx + neg eax +noneg: + add eax,edx + add eax,ebx + and eax,0FFH +// ret + } +} + + + + + + + + + + + + +/* + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +//NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + +// CODESEG +*/ + +/* +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +*/ +int __cdecl Desired_Facing8(long x1, long y1, long x2, long y2) +{ + + static const char _new_facing8[] = {1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4}; + + __asm { + + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 //; Close to major axis bit. + + xor eax,eax + mov al,[_new_facing8+ebx] + + //; Normalize to 0..FF range. + shl eax,5 + +// ret + + } + +} + + + +#if (0) + +/* + ; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + + __asm { + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + +#if (0) + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END +#endif +#endif + + + + + + + + + + + + + + + + +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/65536 as the lowest and up to * +;* 65536 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal) +{ + __asm { + + mov eax, 0FFFFFFFFh //; establish default return value + + mov ebx,[base] + or ebx, ebx + jz retneg1 //; if base==0, return 4294967295 + + mov eax,[cardinal] //; otherwise, return (cardinal*65536)/base + shl eax,16 + xor edx,edx + div ebx + +retneg1: + //ret + + + } +} + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed) +{ +// PROC Fixed_To_Cardinal C near +// USES edx + +// ARG base:DWORD +// ARG fixed:DWORD + + __asm { + mov eax,[base] + mul [fixed] + add eax,08000h //; eax = (base * fixed) + 0x8000 + + shr eax,16 //; return eax/65536 + //ret + } + + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + +} + + + + + + + + + + + + + + + +void __cdecl Set_Bit(void * array, int bit, int value) +{ + __asm { + mov ecx, [bit] + mov eax, [value] + mov esi, [array] + mov ebx,ecx + shr ebx,5 + and ecx,01Fh + btr [esi+ebx*4],ecx + or eax,eax + jz ok + bts [esi+ebx*4],ecx +ok: + } +} + + +int __cdecl Get_Bit(void const * array, int bit) +{ + __asm { + mov eax, [bit] + mov esi, [array] + mov ebx,eax + shr ebx,5 + and eax,01Fh + bt [esi+ebx*4],eax + setc al + } +} + +int __cdecl First_True_Bit(void const * array) +{ + __asm { + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + + +int __cdecl First_False_Bit(void const * array) +{ + __asm { + + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + not ebx + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + +int __cdecl Bound(int original, int min, int max) +{ + __asm { + mov eax,[original] + mov ebx,[min] + mov ecx,[max] + cmp ebx,ecx + jl okorder + xchg ebx,ecx +okorder: cmp eax,ebx + jg okmin + mov eax,ebx +okmin: cmp eax,ecx + jl okmax + mov eax,ecx +okmax: + } +} + + + + + + + +/* + +CELL __cdecl Coord_Cell(COORDINATE coord) +{ + __asm { + mov eax, coord + mov ebx,eax + shr eax,010h + xor al,al + shr eax,2 + or al,bh + } + +} + + + +*/ + + + + + +/* +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* +*/ +void __cdecl Shake_Screen(int shakes) +{ + // PG_TO_FIX + // Need a different solution for shaking the screen + shakes; +} + + + +#if (0) +GLOBAL C Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + ret + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END + +#endif + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ + +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac) +{ + /* + global C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + */ + +#define ALLOWED_COUNT 16 +#define ALLOWED_START 256-ALLOWED_COUNT + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini1 + cmp [dest],0 + je fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START + innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT + fillerloop: + inc bl + mov al,bl + stosb + loop fillerloop + + fini1: + mov esi,[dest] + mov eax,esi + + //ret + } +} + + + + + + +extern "C" long __cdecl Reverse_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + xchg al,ah + ror eax,16 + xchg al,ah + } +} + + +extern "C" short __cdecl Reverse_Short(short number) +{ + __asm { + mov ax,[number] + xchg ah,al + } +} + + + +extern "C" long __cdecl Swap_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + ror eax,16 + } +} + + + + + + + + +/* + + + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + global C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. +*/ +void __cdecl strtrim(char *buffer) +{ + __asm { + cmp [buffer],0 + je short fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. + looper: + lodsb + cmp al,20h ; Space + je short looper + cmp al,9 ; TAB + je short looper + stosb + + ; Copy the rest of the string. + gruntloop: + lodsb + stosb + or al,al + jnz short gruntloop + dec edi + ; Strip the white space from the end of the string. + looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short looper2 + cmp ah,9 + je short looper2 + + fini: + //ret + } +} + + +/* +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + global C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto +*/ + +void __cdecl Fat_Put_Pixel(int x, int y, int color, int siz, GraphicViewPortClass &gpage) +{ + __asm { + + cmp [siz],0 + je short exit_label + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[ebx]GraphicViewPortClass.Height ;YPIXEL_MAX + jae short exit_label + mov ecx,[ebx]GraphicViewPortClass.Width + add ecx,[ebx]GraphicViewPortClass.XAdd + add ecx,[ebx]GraphicViewPortClass.Pitch + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[ebx]GraphicViewPortClass.Width + cmp edx,[x] + mov edx,ecx + jbe short exit_label + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] + again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short again + + exit_label: + //ret + } +} + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Calculate_CRC :NEAR + + CODESEG +*/ +/* +extern "C" long __cdecl Calculate_CRC(void *buffer, long length) +{ + unsigned long crc; + + unsigned long local_length = (unsigned long) length; + + __asm { + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + //; Fetch the length of the data block to CRC. + + mov ecx,[local_length] + + jecxz short fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short remainder2 + accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop accumloop + + ; Handle the remainder bytes. + remainder2: + or dl,dl + jz short fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx + nextbyte: + lodsb + ror eax,8 + loop nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + + ;nextbyte: + ; shl eax,8 + ; lodsb + ; loop nextbyte + rol ebx,1 + add ebx,eax + + fini: + mov [crc],ebx + popad + mov eax,[crc] + //ret + } +} + + +*/ + + + +extern "C" void __cdecl Set_Palette_Range(void *palette) +{ + if (palette == NULL) { + return; + } + + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); +} \ No newline at end of file diff --git a/REDALERT/NETDLG.CPP b/REDALERT/NETDLG.CPP new file mode 100644 index 000000000..ab0cadd82 --- /dev/null +++ b/REDALERT/NETDLG.CPP @@ -0,0 +1,7844 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NETDLG.CPP 13 10/13/97 2:20p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : December 12, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_CHAT_ANNOUNCE * + * Chat: unique id of the local node, so I can tell * + * if this chat announcement is from myself * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house, color, & version range * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * Reject.Why: tells why we got rejected * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Init_Network -- initializes network stuff * + * Shutdown_Network -- shuts down network stuff * + * Process_Global_Packet -- responds to remote queries * + * Destroy_Connection -- destroys the given connection * + * Remote_Connect -- handles connecting this user to others * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Unjoin_Game -- Cancels joining a game * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Net_New_Dialog -- lets user start a new game * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// Warning - Most disgusting cpp file of all time. ajw + +#include "function.h" + +//PG Stubs +void Destroy_Connection(int, int) {} +bool Process_Global_Packet(GlobalPacketType *, IPXAddressClass *) { return false; } +bool Client_Remote_Connect(void) { return false; } +bool Server_Remote_Connect(void) { return false; } +bool Init_Network(void) { return false; } +bool Remote_Connect(void) { return false; } + +#if (0) //PG + + +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#include "ccdde.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 + +#include +//#include + +//#include "WolDebug.h" + +#define SHOW_MONO 0 +//#define OLDWAY 1 + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +#endif +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +#include "COORDA.h" + +//--------------------------------------------------------------------------- +// The possible states of the join-game dialog +//--------------------------------------------------------------------------- +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting + JOIN_GAME_START_LOAD, // the game we've joined is starting; load saved game +} JoinStateType; + +//--------------------------------------------------------------------------- +// The possible return codes from Get_Join_Responses() +//--------------------------------------------------------------------------- +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game formed, or is now open + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +//--------------------------------------------------------------------------- +// The possible reasons we're rejected from joining a game +//--------------------------------------------------------------------------- +typedef enum { + REJECT_DUPLICATE_NAME, // player's name is a duplicate + REJECT_GAME_FULL, // game is full + REJECT_VERSION_TOO_OLD, // joiner's version is too old + REJECT_VERSION_TOO_NEW, // joiner's version is too new + REJECT_BY_OWNER, // game owner clicked "reject" + REJECT_DISBANDED, // game was disbanded + REJECT_MISMATCH, // "rules.ini" file mismatch. +} RejectType; + +#ifdef ENGLISH +char const *EngMisStr[] = { + "Coastal Influence (Med)", + "Middle Mayhem (Sm)", + "Equal Opportunity (Sm)", + "Marooned II (Med)", + "Keep off the Grass (Sm)", + "Isle of Fury (Lg)", + "Ivory Wastelands (Sm)", + "Shallow Grave (Med)", + "North By Northwest (Lg)", + "First Come, First Serve (Sm)", + "Island Hoppers (Sm)", + "Raraku (Lg)", + "Central Conflict (Lg)", + "Combat Alley (Med)", + "Island Wars (Lg)", + "Desolation (Lg)", + "No Escape (Med)", + "No Man's Land (Med)", + "Normandy (Med)", + "Pond Skirmish (Med)", + "Ridge War (Med)", + "A Path Beyond (Lg)", + "Dugout Isle (Med)", + "Treasure Isle (Med)", + + "Africa (Lg)", + "Alaska Anarchy (Lg)", + "All that Glitters... (Lg)", + "Apre's Peace (Lg)", + "Antartica (Lg)", + "Armourgarden (Lg)", + "Austraila (Med)", + "Barrier to Entry (Lg)", + "Bavarian Blast (Med)", + "Be Shore (Med)", + "Bearing Straits (Med)", + "Blow Holes (Lg)", + "Bonsai (Sm)", + "Brother Stalin (Lg)", + "Bullseye (Lg)", + "C&C (Med)", + "Camos Canyon (Med)", + "Camos Coves (Lg)", + "Camos Cross (Lg)", + "Camos Crossing (Sm)", + "Central Arena (Lg)", + "Canyon River (Med)", + "Crossroads (Sm)", + "Czech Mate (Lg)", + "Dday (Med)", + "Disaster Central (Lg)", + "Docklands (Med)", + "East Coast (Med)", + "Eastern Seaboard (Lg)", + "Finger Lake (Lg)", + "Fjords (Med)", + "Floodlands (Lg)", + "Forest under fire (Lg)", + "Four Corners (Lg)", + "Frostbit Fjords (Lg)", + "Glenboig (Sm)", + "Hell Frozen Over (Lg)", + "India (Lg)", + "Indirect Fire (Lg)", + "Island Wars II (Lg)", + "Italy (Lg)", + "Kabalo (Lg)", + "King of the Hills (Lg)", + "Lake Divide (Med)", + "Lakelands (Lg)", + "Land Ladder (Lg)", + "Lotsa Lakes (Lg)", + "Lunar Battlefield (Lg Special)", + "Malibu Fields (Med)", + "Marshland (Med)", + "MyLai Delta (Med)", + "Natural Harbor (Med)", + "No Way Out (Lg)", + "Normandy Landing (Lg)", + "Ore Wars (Med)", + "Oz (Lg)", + "Pilgrim Fathers II (Lg)", + "Pip's Ice Tea (Med)", + "Polar Panic (Lg)", + "Ponds (Med)", + "Putney (Lg)", + "Return to Zion (Lg)", + "Ring of Land (Lg)", + "River Basin (Lg)", + "River Delta (Med)", + "River Islands (Med)", + "River Maze (Sm)", + "Rivers (Sm)", + "Run the Gauntlet (Med)", + "Scappa Flow (Lg)", + "Siberian Slaughter (Lg)", + "Sleepy Valley (Sm)", + "Snake River (Lg)", + "Snow Wars (Lg)", + "Snowball fight (Lg)", + "Snowy Island (Lg)", + "So Near So Far (Sm)", + "South America (Lg)", + "Spring Line (Lg)", + "Star (Lg)", + "Straighter & Narrower (Sm)", + "TerrainSpotting (Sm)", + "The Bay (Lg)", + "The Garden (Lg)", + "The Great Lakes (Med)", + "The Ice Arena (Lg)", + "The Lake District (Lg)", + "The Linked lands (Lg)", + "The Mississippi (Med)", + "The Sticky Bit (Lg)", + "The Valley (Med)", + "The Woods Today (Lg)", + "Things to Come (Lg)", + "Tiger Core (Sm)", + "To the Core (Sm)", + "Tournament Hills (Lg)", + "Tropical Storm (Med)", + "Tundra Trouble (Lg)", + "Uk (Med)", + "Undiscovered Country (Sm)", + "United States (Med)", + "Volcano (Sm)", + "Wastelands (Lg)", + "Water Works (Sm)", + "World Map (Med)", + "Zambezi (Lg)", + + "A Pattern of Islands (Lg 8 players)", + "Arena Valley Extreme (Mega 8 players)", + "Around the Rim (Sm 4 players)", + "Ashes to Ashes (Lg 6 players)", + "Artic Wasteland (Mega 8 players)", + "Badajoz (Med 4 players)", + "Baptism of Fire (Lg 6 players)", + "Big Fish, Small Pond (Lg 6 players)", + "Blue Lakes (Lg 8 players)", + "Booby Traps (Mega 8 players)", + "Bridgehead (Lg 6 players)", + "Butterfly Bay (Lg 6 players)", + "Central Conflict Extreme (Mega 8 players)", + "Circles of Death (Mega 8 players)", + "Cold Front (Med 6 players)", + "Cold Pass (Med 4 players)", + "Combat Zones (Mega 8 players)", + "Conflict Cove (Sm 4 players)", + "Culloden Moor (Med 8 players)", + "Damnation Alley (Mega 8 players)", + "Death Valley (Mega 8 players)", + "Deep Six (Mega 8 players)", + "Destruction Derby (Mega 8 players)", + "Diamonds Aren't Forever (Mega 8 players)", + "Elysium (Sm 4 players)", + "Equal Shares (Lg 4 players)", + "Frost Bitten (Mega 8 players)", + "Frozen Valley (Med 6 players)", + "Gettysburg (Sm 4 players)", + "Glacial Valley (Sm 4 players)", + "Gold Coast (Med 6 players)", + "Gold Rush (Lg 4 players)", + "Habitat (Lg 4 players)", + "Hades Frozen Over (Sm 4 players)", + "Hamburger Hill (Mega 8 players)", + "Hastings (Sm 4 players)", + "Hell's Pass (Med 6 players)", + "Holy Grounds (Mega 8 players)", + "Ice Bergs (Med 6 players)", + "Ice Station (Lg 6 players)", + "Ice Queen (Lg 4 players)", + "In the Sun (Med 6 players)", + "Innocents? (Mega 8 players)", + "Islands (Med 8 players)", + "Island Plateau (Lg 4 players)", + "Island Wars Extreme (Mega 8 players)", + "Kananga (Med 6 players)", + "King of the Hills Extreme (Mega 8 players)", + "Lake Land (Lg 8 players)", + "Land Locked (Lg 8 players)", + "Lanes (Med 8 players)", + "Leipzip (Sm 4 players)", + "Meander (Lg 8 players)", + "Mekong (Med 8 players)", + "Middle Ground (Med 8 players)", + "Naval Conquests (Mega 8 players)", + "On your Marks (Med 4 players)", + "Open Warfare (Mega 8 players)", + "Ore Gardens (Lg 8 players)", + "Potholes (Mega 8 players)", + "Puddles (Med 4 players)", + "Random Violence (Mega 8 players)", + "Revenge (Med 8 players)", + "Rias (Med 8 players)", + "River Crossing (Sm 4 players)", + "River Rampage (Mega 8 players)", + "River Rapids (Lg 6 players)", + "Rivers Wild (Mega 8 players)", + "Rorkes Drift (Lg 4 players)", + "Seaside (Med 4 players)", + "Shades (Med 8 players)", + "Smuggler's Cove (Lg 6 players)", + "Snow Garden (Sm 2 players)", + "Stalingrad (Sm 4 players)", + "Sticks & Stones (Med 4 players)", + "Strathearn Valley (Lg 6 players)", + "Super Bridgehead (Mega 8 players)", + "Super Mekong (Mega 8 players)", + "Super Ore Gardens (Mega 8 players)", + "Switch (Med 4 players)", + "The Berg (Mega 8 players)", + "The Boyne (Med 4 players)", + "The Bulge (Sm 4 players)", + "The Cauldron (Lg 6 players)", + "The Finger (Lg 6 players)", + "The Hills Have Eyes (Mega 8 players)", + "The Keyes (Med 6 players)", + "The Lakes (Med 8 players)", + "The Neck (Med 6 players)", + "The Web (Lg 6 players)", + "To the Core (Lg 4 players)", + "Trafalgar (Lg 4 players)", + "Twin Rivers (Sm 4 players)", + "Umtumbo Gorge (Lg 4 players)", + "Watch Your Step Extreme (Mega 8 players)", + "Waterfalls (Lg 8 players)", + "Waterloo Revisited (Lg 6 players)", + "Water Werks (Mega 8 players)", + "Warlord's Lake (Sm 4 players)", + "Zama (Sm 4 players)", + + NULL +}; +#endif + +#ifdef GERMAN +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Weg ins Jenseits (Gr)", + "Central Conflict (Lg)", "Der zentrale Konflikt (Gr)", + "Coastal Influence (Med)", "Sturm an der Kste (Mit)", + "Combat Alley (Med)", "Boulevard der Schlachten (Mit)", + "Desolation (Lg)", "Verwstung (Gr)", + "Dugout Isle (Med)", "Buddelschiff (Mit)", + "Equal Opportunity (Sm)", "Gleiche Chancen (Kl)", + "First Come, First Serve (Sm)", "Wer zuerst kommt... (Kl)", + "Island Hoppers (Sm)", "Inselspringen (Kl)", + "Island Wars (Lg)", "Der Krieg der Eilande (Gr)", + "Isle of Fury (Lg)", "Insel des Zorns (Gr)", + "Ivory Wastelands (Sm)", "Elfenbeinwste (Kl)", + "Keep off the Grass (Sm)", "Rasen betreten verboten (Kl)", + "Marooned II (Med)", "Gestrandet (Mit)", + "Middle Mayhem (Sm)", "Mittelsmann (Kl)", + "No Escape (Med)", "Kein Entrinnen (Mit)", + "No Man's Land (Med)", "Niemandsland (Mit)", + "Normandy (Med)", "Normandie (Mit)", + "North By Northwest (Lg)", "Nord auf Nordwest (Gr)", + "Pond Skirmish (Med)", "Teichgepl„nkel (Mit)", + "Raraku (Lg)", "Raraku (Gr)", + "Ridge War (Med)", "Das Tal der Cyborgs (Mit)", + "Shallow Grave (Med)", "Ein enges Grab (Mit)", + "Treasure Isle (Med)", "Die Schatzinsel (Mit)", + + "Africa (Lg)", "Afrika (Gr)", + "Alaska Anarchy (Lg)", "Anarchie in Alaska (Gr)", + "All that Glitters... (Lg)", "Alles was gl„nzt... (Gr)", + "Apre's Peace (Lg)", "Apres Frieden (Gr)", + "Antartica (Lg)", "Antarktica (Gr)", + "Armourgarden (Lg)", "Garten der Panzer (Gr)", + "Austraila (Med)", "Koalaland (Mit)", + "Barrier to Entry (Lg)", "Zutritt verboten (Gr)", + "Bavarian Blast (Med)", "Bayrische Blasmusik (Mit)", + "Be Shore (Med)", "Strandl„ufer (Mit)", + "Bearing Straits (Med)", "Die Heringstrasse (Mit)", + "Blow Holes (Lg)", "L”cheriger K„se (Gr)", + "Bonsai (Sm)", "Bonsai (Kl)", + "Brother Stalin (Lg)", "Brderchen Stalin (Gr)", + "Bullseye (Lg)", "Bullseye (Gr)", + "C&C (Med)", "C&C (Mit)", + "Camos Canyon (Med)", "Camos-Canyon (Mit)", + "Camos Coves (Lg)", "Camos-Grotte (Gr)", + "Camos Cross (Lg)", "Camos-Kreuz (Gr)", + "Camos Crossing (Sm)", "Camos-Kreuzweg (Kl)", + "Central Arena (Lg)", "Spielplatz des Teufels (Gr)", + "Canyon River (Med)", "Canyonfluss (Mit)", + "Crossroads (Sm)", "Kreuzung (Kl)", + "Czech Mate (Lg)", "Tschechische Er”ffnung (Gr)", + "Dday (Med)", "D-Day (Mit)", + "Disaster Central (Lg)", "Endstation Schweinebucht (Gr)", + "Docklands (Med)", "Docklands (Mit)", + "East Coast (Med)", "Ostkste (Mit)", + "Eastern Seaboard (Lg)", "Die Passage nach Osten (Gr)", + "Finger Lake (Lg)", "Fingersee (Gr)", + "Fjords (Med)", "Fjorde (Mit)", + "Floodlands (Lg)", "Land unter! (Gr)", + "Forest under fire (Lg)", "Waldsterben im Feuer (Gr)", + "Four Corners (Lg)", "Viereck (Gr)", + "Frostbit Fjords (Lg)", "Frostbeulenfjord (Gr)", + "Glenboig (Sm)", "Glenboig (Kl)", + "Hell Frozen Over (Lg)", "Winter in der H”lle (Gr)", + "India (Lg)", "Indien (Gr)", + "Indirect Fire (Lg)", "Indirekter Beschuss (Gr)", + "Island Wars II (Lg)", "Krieg der Inseln (Gr)", + "Italy (Lg)", "Italien (Gr)", + "Kabalo (Lg)", "Kabalo (Gr)", + "King of the Hills (Lg)", "K”nig des Maulwurfshgels (Gr)", + "Lake Divide (Med)", "Wasserscheide (Mit)", + "Lakelands (Lg)", "Seenplatte (Gr)", + "Land Ladder (Lg)", "Das Leiterspiel (Gr)", + "Lotsa Lakes (Lg)", "Mehr Seen (Gr)", + "Lunar Battlefield (Lg Special)", "Schlachtfeld Mond (Gr Spezial)", + "Malibu Fields (Med)", "Malibu (Mit)", + "Marshland (Med)", "Schlammschlacht (Mit)", + "MyLai Delta (Med)", "Das Delta von My Lai (Mit)", + "Natural Harbor (Med)", "Natrlicher Hafen (Mit)", + "No Way Out (Lg)", "Kein Entkommen (Gr)", + "Normandy Landing (Lg)", "Landung in der Normandie (Gr)", + "Ore Wars (Med)", "Die Erz-Kriege (Mit)", + "Oz (Lg)", "Das Land Oz (Gr)", + "Pilgrim Fathers II (Lg)", "Die Grnderv„ter (Gr)", + "Pip's Ice Tea (Med)", "Pips Eistee (Mit)", + "Polar Panic (Lg)", "Panik am Pol (Gr)", + "Ponds (Med)", "Tmpelspringer (Mit)", + "Putney (Lg)", "Putney (Gr)", + "Return to Zion (Lg)", "Rckkehr nach Zion (Gr)", + "Ring of Land (Lg)", "Der Landring (Gr)", + "River Basin (Lg)", "Flusslauf (Gr)", + "River Delta (Med)", "Flussdelta (Mit)", + "River Islands (Med)", "Flussinsel (Mit)", + "River Maze (Sm)", "Flussgewirr (Kl)", + "Rivers (Sm)", "Flsse (Kl)", + "Run the Gauntlet (Med)", "Spiessrutenlauf (Mit)", + "Scappa Flow (Lg)", "Scapa Flow (Gr)", + "Siberian Slaughter (Lg)", "Sibirisches Gemetzel (Gr)", + "Sleepy Valley (Sm)", "Tal der Ahnungslosen (Kl)", + "Snake River (Lg)", "Am Schlangenfluss (Gr)", + "Snow Wars (Lg)", "Krieg der Flocken (Gr)", + "Snowball fight (Lg)", "Schneeballschlacht (Gr)", + "Snowy Island (Lg)", "Schneeinsel (Gr)", + "So Near So Far (Sm)", "So nah und doch so fern (Kl)", + "South America (Lg)", "Sdamerika (Gr)", + "Spring Line (Lg)", "Frhlingsgefhle (Gr)", + "Star (Lg)", "Stern (Gr)", + "Straighter & Narrower (Sm)", "Enger & schmaler (Kl)", + "TerrainSpotting (Sm)", "TerrainSpotting (Kl)", + "The Bay (Lg)", "Die Bucht (Gr)", + "The Garden (Lg)", "Der Garten (Gr)", + "The Great Lakes (Med)", "Die Grossen Seen (Mit)", + "The Ice Arena (Lg)", "Eisarena (Gr)", + "The Lake District (Lg)", "Kalte Seenplatte (Gr)", + "The Linked lands (Lg)", "Die verbundenen L„nder (Gr)", + "The Mississippi (Med)", "Grsse von Tom Sawyer (Mit)", + "The Sticky Bit (Lg)", "Der klebrige Teil (Gr)", + "The Valley (Med)", "Das Tal (Mit)", + "The Woods Today (Lg)", "Waldl„ufer (Gr)", + "Things to Come (Lg)", "Was die Zukunft bringt (Gr)", + "Tiger Core (Sm)", "Das Herz des Tigers (Kl)", + "To the Core (Sm)", "Mitten ins Herz (Kl)", + "Tournament Hills (Lg)", "Hgel der Entscheidung (Gr)", + "Tropical Storm (Med)", "Tropenstrme (Mit)", + "Tundra Trouble (Lg)", "Tauziehen in der Tundra (Gr)", + "Uk (Med)", "GB (Mit)", + "Undiscovered Country (Sm)", "Unentdecktes Land (Kl)", + "United States (Med)", "US (Mit)", + "Volcano (Sm)", "Vulkan (Kl)", + "Wastelands (Lg)", "Wstenei (Gr)", + "Water Works (Sm)", "Wasserwerke (Kl)", + "World Map (Med)", "Weltkarte (Kl)", + "Zambezi (Lg)", "Sambesi (Gr)", + +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Inselmuster (gross, 8 Spieler)", + "Arena Valley Extreme (Mega 8 players)", "Arenatal (sehr gross, 8 Spieler)", + "Around the Rim (Sm 4 players)", "Um die Kante (klein, 4 Spieler)", + "Ashes to Ashes (Lg 6 players)", "Asche zu Asche (gross, 6 Spieler)", + "Artic Wasteland (Mega 8 players)", "Arktische Wste (sehr gross, 8 Spieler)", + "Badajoz (Med 4 players)", "Badjoz (mittelgross, 4 Spieler)", + "Baptism of Fire (Lg 6 players)", "Feuertaufe (gross, 6 Spieler)", + "Big Fish, Small Pond (Lg 6 players)", "Grosser Fisch im kleinen Teich (gross, 6 Spieler)", + "Blue Lakes (Lg 8 players)", "Die blauen Seen (gross, 8 Spieler)", + "Booby Traps (Mega 8 players)", "Vorsicht, Falle! (sehr gross, 8 Spieler)", + "Bridgehead (Lg 6 players)", "Brckenkopf im Niemandsland (gross, 6 Spieler)", + "Butterfly Bay (Lg 6 players)", "Schmetterlingsbucht (gross, 6 Spieler)", + "Central Conflict Extreme (Mega 8 players)", "Zentraler Konflikt fr K”nner (sehr gross, 8 Spieler)", + "Circles of Death (Mega 8 players)", "Todeskreise (sehr gross, 8 Spieler)", + "Cold Front (Med 6 players)", "Kaltfront ( mittelgross, 6 Spieler)", + "Cold Pass (Med 4 players)", "Cooler Pass (mittelgross, 4 Spieler)", + "Combat Zones (Mega 8 players)", "Kampfgebiete (sehr gross, 8 Spieler)", + "Conflict Cove (Sm 4 players)", "H”hlenkonflikt (klein, 4 Spieler)", + "Culloden Moor (Med 8 players)", "Culloden-Moor (mittelgross, 8 Spieler)", + "Damnation Alley (Mega 8 players)", "Strasse der Verdammten (sehr gross, 8 Spieler)", + "Death Valley (Mega 8 players)", "Tal des Todes (sehr gross, 8 Spieler)", + "Deep Six (Mega 8 players)", "Tiefe Sechs (sehr gross, 8 Spieler)", + "Destruction Derby (Mega 8 players)", "Destruction Derby (sehr gross, 8 Spieler)", + "Diamonds Aren't Forever (Mega 8 players)", "Verg„ngliche Diamanten (sehr gross, 8 Spieler)", + "Elysium (Sm 4 players)", "Elysium (klein, 4 Spieler)", + "Equal Shares (Lg 4 players)", "Gleiche Anteile (gross, 4 Spieler)", + "Frost Bitten (Mega 8 players)", "Frostbrand (sehr gross, 8 Spieler)", + "Frozen Valley (Med 6 players)", "Eisiges Tal (mittelgross, 6 Spieler)", + "Gettysburg (Sm 4 players)", "Gettysburg (klein, 4 Spieler)", + "Glacial Valley (Sm 4 players)", "Gletschertal (klein, 4 Spieler)", + "Gold Coast (Med 6 players)", "Goldkste (mittelgross, 6 Spieler)", + "Gold Rush (Lg 4 players)", "Goldrausch (gross, 4 Spieler)", + "Habitat (Lg 4 players)", "Habitat (gross, 4 Spieler)", + "Hades Frozen Over (Sm 4 players)", "Frostschutz fr die H”lle (klein, 4 Spieler)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (sehr gross, 8 Spieler)", + "Hastings (Sm 4 players)", "Hastings (klein, 4 Spieler)", + "Hell's Pass (Med 6 players)", "H”llenpass (mittelgross, 6 Spieler)", + "Holy Grounds (Mega 8 players)", "Heiliger Boden (sehr gross, 8 Spieler)", + "Ice Bergs (Med 6 players)", "Eisberge (mittelgross, 6 Spieler)", + "Ice Station (Lg 6 players)", "Eisstation (gross, 6 Spieler)", + "Ice Queen (Lg 4 players)", "Eisk”nigin (gross, 4 Spieler)", + "In the Sun (Med 6 players)", "Unter der Sonne (mittelgross, 6 Spieler)", + "Innocents? (Mega 8 players)", "Unschuldig? Wer? (sehr gross, 8 Spieler)", + "Islands (Med 8 players)", "Inseln im Nebel (mittelgross, 8 Spieler)", + "Island Plateau (Lg 4 players)", "Inselplateau (gross, 4 Spieler)", + "Island Wars Extreme (Mega 8 players)", "Extremes Inselspringen (sehr gross, 8 Spieler)", + "Kananga (Med 6 players)", "Kananga (mittelgross, 6 Spieler)", + "King of the Hills Extreme (Mega 8 players)", "K”nig des Maulwurfshgels (sehr gross, 8 Spieler)", + "Lake Land (Lg 8 players)", "Seenland (gross, 8 Spieler)", + "Land Locked (Lg 8 players)", "Das Verschlossene Land (gross, 8 Spieler)", + "Lanes (Med 8 players)", "Gassenjungen (mittelgross, 8 Spieler)", + "Leipzip (Sm 4 players)", "Leipzig (klein, 4 Spieler)", + "Meander (Lg 8 players)", "M„ander (gross, 8 Spieler)", + "Mekong (Med 8 players)", "Mekong (mittelgross, 8 Spieler)", + "Middle Ground (Med 8 players)", "Mittelsmann (mittelgross, 8 Spieler)", + "Naval Conquests (Mega 8 players)", "Kommt zur Marine, haben sie gesagt (sehr gross, 8 Spieler)", + "On your Marks (Med 4 players)", "Auf die Pl„tze (mittelgross, 4 Spieler)", + "Open Warfare (Mega 8 players)", "Offener Schlagabtausch (sehr gross, 8 Spieler)", + "Ore Gardens (Lg 8 players)", "Erzparadies (gross, 8 Spieler)", + "Potholes (Mega 8 players)", "Schlagl”cher (sehr gross, 8 Spieler)", + "Puddles (Med 4 players)", "Pftzen (mittelgross, 4 Spieler)", + "Random Violence (Mega 8 players)", "Unberechenbare Gewalt (sehr gross, 8 Spieler)", + "Revenge (Med 8 players)", "Rache (mittelgross, 8 Spieler)", + "Rias (Med 8 players)", "Kabul (mittelgross, 8 Spieler)", + "River Crossing (Sm 4 players)", "Die Furt (klein, 4 Spieler)", + "River Rampage (Mega 8 players)", "Flussfahrt (sehr gross, 8 Spieler)", + "River Rapids (Lg 6 players)", "Stromschnellen (gross, 6 Spieler)", + "Rivers Wild (Mega 8 players)", "Wildwasser (sehr gross, 8 Spieler)", + "Rorkes Drift (Lg 4 players)", "Rorkes Drift (gross, 4 Spieler)", + "Seaside (Med 4 players)", "Strandleben (mittelgross, 4 Spieler)", + "Shades (Med 8 players)", "Schattenreich (mittelgross, 8 Spieler)", + "Smuggler's Cove (Lg 6 players)", "Schmugglerh”hle (gross, 6 Spieler)", + "Snow Garden (Sm 2 players)", "Schneegest”ber (klein, 2 Spieler)", + "Stalingrad (Sm 4 players)", "Stalingrad (klein, 4 Spieler)", + "Sticks & Stones (Med 4 players)", "Holz und Steine (mittelgross, 4 Spieler)", + "Strathearn Valley (Lg 6 players)", "Das Tal von Strathearn (gross, 6 Spieler)", + "Super Bridgehead (Mega 8 players)", "Super-Brckenkopf (sehr gross, 8 Spieler)", + "Super Mekong (Mega 8 players)", "Super-Mekong (sehr gross, 8 Spieler)", + "Super Ore Gardens (Mega 8 players)", "Super-Erzparadies (sehr gross, 8 Spieler)", + "Switch (Med 4 players)", "Schalter (mittelgross, 4 Spieler)", + "The Berg (Mega 8 players)", "Der Berg (sehr gross, 8 Spieler)", + "The Boyne (Med 4 players)", "Boyne (mittelgross, 4 Spieler)", + "The Bulge (Sm 4 players)", "Die W”lbung (klein, 4 Spieler)", + "The Cauldron (Lg 6 players)", "Der Kessel (gross, 6 Spieler)", + "The Finger (Lg 6 players)", "Der Finger (gross, 6 Spieler)", + "The Hills Have Eyes (Mega 8 players)", "Die Hgel haben Augen (sehr gross, 8 Spieler)", + "The Keyes (Med 6 players)", "Ein Sumpf (mittelgross, 6 Spieler)", + "The Lakes (Med 8 players)", "Die Seen (mittelgross, 8 Spieler)", + "The Neck (Med 6 players)", "Der Hals (mittelgross, 6 Spieler)", + "The Web (Lg 6 players)", "Das Netz (gross, 6 Spieler)", + "To the Core (Lg 4 players)", "Mitten ins Herz (gross, 4 Spieler)", + "Trafalgar (Lg 4 players)", "Trafalgar (gross, 4 Spieler)", + "Twin Rivers (Sm 4 players)", "Zwillingsstr”me (klein, 4 Spieler)", + "Umtumbo Gorge (Lg 4 players)", "Die Umtumbo-Schlucht (gross, 4 Spieler)", + "Watch Your Step Extreme (Mega 8 players)", "Vorsicht, Lebensgefahr (sehr gross, 8 Spieler)", + "Waterfalls (Lg 8 players)", "Wasserfall (gross, 8 Spieler)", + "Waterloo Revisited (Lg 6 players)", "Zu Besuch in Waterloo (gross, 6 Spieler)", + "Water Werks (Mega 8 players)", "Wasserwerk (sehr gross, 8 Spieler)", + "Warlord's Lake (Sm 4 players)", "Der See des Kriegsgottes (klein, 4 Spieler)", + "Zama (Sm 4 players)", "Zama (klein, 4 Spieler)", +//#endif + NULL +}; +#endif +#ifdef FRENCH +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Le Passage (Max)", + "Central Conflict (Lg)", "Conflit Central (Max)", + "Coastal Influence (Med)", "Le Chant des Canons (Moy)", + "Combat Alley (Med)", "Aux Armes! (Moy)", + "Desolation (Lg)", "D‚solation (Max)", + "Dugout Isle (Med)", "L'Ile Maudite (Moy)", + "Equal Opportunity (Sm)", "A Chances Egales (Min)", + "First Come, First Serve (Sm)", "La Loi du Plus Fort (Min)", + "Island Hoppers (Sm)", "D'une Ile … l'autre (Min)", + "Island Wars (Lg)", "Guerres Insulaires (Max)", + "Isle of Fury (Lg)", "L'Ile de la Furie(Max)", + "Ivory Wastelands (Sm)", "Terres d'Ivoire (Min)", + "Keep off the Grass (Sm)", "Hors de mon Chemin (Min)", + "Marooned II (Med)", "Isolement II (Moy)", + "Middle Mayhem (Sm)", "Chaos Interne (Min)", + "No Escape (Med)", "Le PiŠge (Moy)", + "No Man's Land (Med)", "No Man's Land (Moy)", + "Normandy (Med)", "Normandie (Moy)", + "North By Northwest (Lg)", "Nord, Nord-Ouest (Max)", + "Pond Skirmish (Med)", "Bain de Sang (Moy)", + "Raraku (Lg)", "Raraku (Max)", + "Ridge War (Med)", "Guerre au Sommet (Moy)", + "Shallow Grave (Med)", "La Saveur de la Mort (Moy)", + "Treasure Isle (Med)", "L'Ile au Tr‚sor (Moy)", + + "Africa (Lg)", "Afrique (Max)", + "Alaska Anarchy (Lg)", "Anarchie en Alaska (Max)", + "All that Glitters... (Lg)", "Tout ce qui brille... (Max)", + "Apre's Peace (Lg)", "Une Paix Durement N‚goci‚e... (Max)", + "Antartica (Lg)", "Antarctique (Max)", + "Armourgarden (Lg)", "La Guerre des Blind‚s (Max)", + "Austraila (Med)", "Australie (Moy)", + "Barrier to Entry (Lg)", "BarriŠre … l'Entr‚e (Max)", + "Bavarian Blast (Med)", "Tonnerre Bavarois (Moy)", + "Be Shore (Med)", "Plages Menac‚es (Moy)", + "Bearing Straits (Med)", "Droit Devant ! (Moy)", + "Blow Holes (Lg)", "CratŠres (Max)", + "Bonsai (Sm)", "Bonsa‹ (Min)", + "Brother Stalin (Lg)", "FrŠre Staline (Max)", + "Bullseye (Lg)", "L'oeil du Taureau (Max)", + "C&C (Med)", "C&C (Moy)", + "Camos Canyon (Med)", "Le Canyon (Moy)", + "Camos Coves (Lg)", "Criques (Max)", + "Camos Cross (Lg)", "La Croix de Guerre (Max)", + "Camos Crossing (Sm)", "La Crois‚e des Chemins (Min)", + "Central Arena (Lg)", "L'ArŠne Diabolique (Max)", + "Canyon River (Med)", "Au Milieu Coule Une RiviŠre (Moy)", + "Crossroads (Sm)", "Carrefours (Min)", + "Czech Mate (Lg)", "TchŠque et Mat (Max)", + "Dday (Med)", "Le Jour J (Moy)", + "Disaster Central (Lg)", "D‚sastre Central (Max)", + "Docklands (Med)", "L'Enfer des Docks (Moy)", + "East Coast (Med)", "C“te Est (Moy)", + "Eastern Seaboard (Lg)", "Rivages de l'Est (Max)", + "Finger Lake (Lg)", "Le Lac de tous les Dangers (Max)", + "Fjords (Med)", "Fjords (Moy)", + "Floodlands (Lg)", "Campagne Lacustre (Max)", + "Forest under fire (Lg)", "Forˆt en flammes (Max)", + "Four Corners (Lg)", "4 Coins (Max)", + "Frostbit Fjords (Lg)", "Fjords Gel‚s (Max)", + "Glenboig (Sm)", "Glenboig (Min)", + "Hell Frozen Over (Lg)", "Enfer de Glace Max)", + "India (Lg)", "Inde (Max)", + "Indirect Fire (Lg)", "Attaque Indirecte (Max)", + "Island Wars II (Lg)", "Guerres Insulaires II (Max)", + "Italy (Lg)", "Italie (Max)", + "Kabalo (Lg)", "Kabalo (Max)", + "King of the Hills (Lg)", "Le Roi des Montagnes (Max)", + "Lake Divide (Med)", "La Guerre du Lac (Moy)", + "Lakelands (Lg)", "Terres Submerg‚es (Max)", + "Land Ladder (Lg)", "Jusqu'au Sommet (Max)", + "Lotsa Lakes (Lg)", "Terres de Lacs (Max)", + "Lunar Battlefield (Lg Special)", "Combat Lunaire (Max Sp‚cial)", + "Malibu Fields (Med)", "Les Champs de Malibu (Moy)", + "Marshland (Med)", "Mar‚cages (Moy)", + "MyLai Delta (Med)", "Le Delta Mylai (Moy)", + "Natural Harbor (Med)", "Port Naturel (Moy)", + "No Way Out (Lg)", "Sans Issue (Max)", + "Normandy Landing (Lg)", "Le D‚barquement (Max)", + "Ore Wars (Med)", "La Guerre du Minerai (Moy)", + "Oz (Lg)", "Oz (Max)", + "Pilgrim Fathers II (Lg)", "Les PŠlerins 2 (Max)", + "Pip's Ice Tea (Med)", "Les Tranch‚es de Glace (Moy)", + "Polar Panic (Lg)", "Panique Polaire (Max)", + "Ponds (Med)", "Les Etangs (Moy)", + "Putney (Lg)", "La Meilleure D‚fense... (Max)", + "Return to Zion (Lg)", "Retour … Sion (Max)", + "Ring of Land (Lg)", "Le Cycle Infernal (Max)", + "River Basin (Lg)", "Confrontation Navale (Max)", + "River Delta (Med)", "Le Delta (Moy)", + "River Islands (Med)", "C“tes … Surveiller de PrŠs (Moy)", + "River Maze (Sm)", "Labyrinthe Fluvial (Min)", + "Rivers (Sm)", "RiviŠres (Min)", + "Run the Gauntlet (Med)", "Relevons le D‚fi ! (Moy)", + "Scappa Flow (Lg)", "Combats Sanglants (Max)", + "Siberian Slaughter (Lg)", "Carnage Sib‚rien (Max)", + "Sleepy Valley (Sm)", "La Vall‚e Endormie (Min)", + "Snake River (Lg)", "La RiviŠre aux Serpents (Max)", + "Snow Wars (Lg)", "Guerres de Neige (Max)", + "Snowball fight (Lg)", "Bataille de Boules de Neige (Max)", + "Snowy Island (Lg)", "L'Ile sous la Neige (Max)", + "So Near So Far (Sm)", "Si Loin, Si Proche (Min)", + "South America (Lg)", "Am‚rique du Sud (Max)", + "Spring Line (Lg)", "Ligne de Front (Max)", + "Star (Lg)", "Etoile (Max)", + "Straighter & Narrower (Sm)", "L'Entonnoir (Min)", + "TerrainSpotting (Sm)", "TerrainSpotting (Min)", + "The Bay (Lg)", "La Baie (Max)", + "The Garden (Lg)", "Le Jardin (Max)", + "The Great Lakes (Med)", "Les Grands Lacs (Moy)", + "The Ice Arena (Lg)", "L'ArŠne de Glace (Max)", + "The Lake District (Lg)", "Un Lac Imprenable (Max)", + "The Linked lands (Lg)", "Passages … Gu‚ (Max)", + "The Mississippi (Med)", "Mississippi (Moy)", + "The Sticky Bit (Lg)", "Marasme (Max)", + "The Valley (Med)", "La Vall‚e (Moy)", + "The Woods Today (Lg)", "Aujoud'hui: la Mort ! (Max)", + "Things to Come (Lg)", "D‚nouement Incertain (Max)", + "Tiger Core (Sm)", "Le Coeur du Tigre (Min)", + "To the Core (Sm)", "Le Coeur du Conflit (Min)", + "Tournament Hills (Lg)", "Combat en Altitude (Max)", + "Tropical Storm (Med)", "Ouragan Tropical (Moy)", + "Tundra Trouble (Lg)", "La Toundra (Max)", + "Uk (Med)", "Royaume Uni (Moy)", + "Undiscovered Country (Sm)", "Terre Inconnue (Min)", + "United States (Med)", "Etats Unis (Moy)", + "Volcano (Sm)", "Le Volcan (Min)", + "Wastelands (Lg)", "Terres D‚sol‚es (Max)", + "Water Works (Sm)", "Jeux d'Eau (Min)", + "World Map (Med)", "Carte du Monde (Moy)", + "Zambezi (Lg)", "ZambŠze (Max)", +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Archipel (Max. 8 joueurs)", + "Arena Valley Extreme (Mega 8 players)", "La Vall‚e de l'arŠne (XL 8 joueurs)", + "Around the Rim (Sm 4 players)", "Autour de la crˆte (Min. 4 joueurs)", + "Ashes to Ashes (Lg 6 players)", "R‚duit en cendres (Max. 6 joueurs)", + "Artic Wasteland (Mega 8 players)", "D‚solation arctique (XL 8 joueurs)", + "Badajoz (Med 4 players)", "Badjoz (Moy. 4 joueurs)", + "Baptism of Fire (Lg 6 players)", "Baptˆme du feu (Max. 6 joueurs)", + "Big Fish, Small Pond (Lg 6 players)", "Gros poisson, Min. Mare (Max. 6 joueurs)", + "Blue Lakes (Lg 8 players)", "Lacs bleus (Max. 8 joueurs)", + "Booby Traps (Mega 8 players)", "PiŠges (XL 8 joueurs)", + "Bridgehead (Lg 6 players)", "Tˆte de pont (Max. 6 joueurs)", + "Butterfly Bay (Lg 6 players)", "La baie du papillon (Max. 6 joueurs)", + "Central Conflict Extreme (Mega 8 players)", "Conflit central extrˆme (XL 8 joueurs)", + "Circles of Death (Mega 8 players)", "Les cercles de la mort (XL 8 joueurs)", + "Cold Front (Med 6 players)", "Front froid ( Moy. 6 joueurs)", + "Cold Pass (Med 4 players)", "La Passe Glac‚e (Moy. 4 joueurs)", + "Combat Zones (Mega 8 players)", "Zones de combat (XL 8 joueurs)", + "Conflict Cove (Sm 4 players)", "La Crique du conflit (Min. 4 joueurs)", + "Culloden Moor (Med 8 players)", "La Lande de Culloden (Moy. 8 joueurs)", + "Damnation Alley (Mega 8 players)", "Le chemin de la damnation (XL 8 joueurs)", + "Death Valley (Mega 8 players)", "La vall‚e de la mort (XL 8 joueurs)", + "Deep Six (Mega 8 players)", "Six de profondeur (XL 8 joueurs)", + "Destruction Derby (Mega 8 players)", "Stock car (XL 8 joueurs)", + "Diamonds Aren't Forever (Mega 8 players)", "Les diamants ne sont pas ‚ternels (XL 8 joueurs)", + "Elysium (Sm 4 players)", "Elys‚e (Min. 4 joueurs)", + "Equal Shares (Lg 4 players)", "Parts ‚gales (Max. 4 joueurs)", + "Frost Bitten (Mega 8 players)", "Engelures (XL 8 joueurs)", + "Frozen Valley (Med 6 players)", "La Vall‚e glac‚e (Moy. 6 joueurs)", + "Gettysburg (Sm 4 players)", "Gettysburg (Min. 4 joueurs)", + "Glacial Valley (Sm 4 players)", "Vall‚e de glace (Min. 4 joueurs)", + "Gold Coast (Med 6 players)", "La c“te dor‚e (Moy. 6 joueurs)", + "Gold Rush (Lg 4 players)", "La ru‚e vers l'or (Max. 4 joueurs)", + "Habitat (Lg 4 players)", "Habitat (Max. 4 joueurs)", + "Hades Frozen Over (Sm 4 players)", "Les enfers glac‚s (Min. 4 joueurs)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (XL 8 joueurs)", + "Hastings (Sm 4 players)", "Hastings (Min. 4 joueurs)", + "Hell's Pass (Med 6 players)", "La route de l'enfer (Moy. 6 joueurs)", + "Holy Grounds (Mega 8 players)", "Terres saintes (XL 8 joueurs)", + "Ice Bergs (Med 6 players)", "Icebergs (Moy. 6 joueurs)", + "Ice Station (Lg 6 players)", "Station glac‚e (Max. 6 joueurs)", + "Ice Queen (Lg 4 players)", "Reine des glaces (Max. 4 joueurs)", + "In the Sun (Med 6 players)", "Sous le soleil (Moy. 6 joueurs)", + "Innocents? (Mega 8 players)", "Innocents ? (XL 8 joueurs)", + "Islands (Med 8 players)", "Iles (Moy. 8 joueurs)", + "Island Plateau (Lg 4 players)", "Plateau des Œles (Max. 4 joueurs)", + "Island Wars Extreme (Mega 8 players)", "Guerres insulaires extrˆme (XL 8 joueurs)", + "Kananga (Med 6 players)", "Kananga (Moy. 6 joueurs)", + "King of the Hills Extreme (Mega 8 players)", "Roi des collines extrˆme (XL 8 joueurs)", + "Lake Land (Lg 8 players)", "Paysage lacustre (Max. 8 joueurs)", + "Land Locked (Lg 8 players)", "Enclave (Max. 8 joueurs)", + "Lanes (Med 8 players)", "Le parcours du combattant (Moy. 8 joueurs)", + "Leipzip (Sm 4 players)", "Leipzig (Min. 4 joueurs)", + "Meander (Lg 8 players)", "M‚andre (Max. 8 joueurs)", + "Mekong (Med 8 players)", "M‚kong (Moy. 8 joueurs)", + "Middle Ground (Med 8 players)", "Plateau m‚dian (Moy. 8 joueurs)", + "Naval Conquests (Mega 8 players)", "Conquˆtes navales (XL 8 joueurs)", + "On your Marks (Med 4 players)", "A vos marques (Moy. 4 joueurs)", + "Open Warfare (Mega 8 players)", "Guerre ouverte (XL 8 joueurs)", + "Ore Gardens (Lg 8 players)", "Jardins de minerai (Max. 8 joueurs)", + "Potholes (Mega 8 players)", "Nids de poules (XL 8 joueurs)", + "Puddles (Med 4 players)", "Flaques (Moy. 4 joueurs)", + "Random Violence (Mega 8 players)", "Violence al‚atoire (XL 8 joueurs)", + "Revenge (Med 8 players)", "Vengeance (Moy. 8 joueurs)", + "Rias (Med 8 players)", "Rias (Moy. 8 joueurs)", + "River Crossing (Sm 4 players)", "Passage … gu‚ (Min. 4 joueurs)", + "River Rampage (Mega 8 players)", "RiviŠre d‚chaŒn‚e (XL 8 joueurs)", + "River Rapids (Lg 6 players)", "Rapides (Max. 6 joueurs)", + "Rivers Wild (Mega 8 players)", "RiviŠres sauvages (XL 8 joueurs)", + "Rorkes Drift (Lg 4 players)", "L'Exode de Rorkes (Max. 4 joueurs)", + "Seaside (Med 4 players)", "C“te (Moy. 4 joueurs)", + "Shades (Med 8 players)", "Ombres (Moy. 8 joueurs)", + "Smuggler's Cove (Lg 6 players)", "La Crique du contrebandier (Max. 6 joueurs)", + "Snow Garden (Sm 2 players)", "Jardin de neige (Min. 2 joueurs)", + "Stalingrad (Sm 4 players)", "Stalingrad (Min. 4 joueurs)", + "Sticks & Stones (Med 4 players)", "Bƒton & Roches (Moy. 4 joueurs)", + "Strathearn Valley (Lg 6 players)", "La Vall‚e de Strathearn (Max. 6 joueurs)", + "Super Bridgehead (Mega 8 players)", "Super tˆte de pont (XL 8 joueurs)", + "Super Mekong (Mega 8 players)", "Super M‚kong (XL 8 joueurs)", + "Super Ore Gardens (Mega 8 players)", "Super jardin de minerai (XL 8 joueurs)", + "Switch (Med 4 players)", "Permutation (Moy. 4 joueurs)", + "The Berg (Mega 8 players)", "Le Berg (XL 8 joueurs)", + "The Boyne (Med 4 players)", "Le Boyne (Moy. 4 joueurs)", + "The Bulge (Sm 4 players)", "Le bombement (Min. 4 joueurs)", + "The Cauldron (Lg 6 players)", "Le chaudron (Max. 6 joueurs)", + "The Finger (Lg 6 players)", "Le doigt (Max. 6 joueurs)", + "The Hills Have Eyes (Mega 8 players)", "Les collines ont des yeux (XL 8 joueurs)", + "The Keyes (Med 6 players)", "Les Keyes (Moy. 6 joueurs)", + "The Lakes (Med 8 players)", "Les lacs (Moy. 8 joueurs)", + "The Neck (Med 6 players)", "Le goulot (Moy. 6 joueurs)", + "The Web (Lg 6 players)", "La toile (Max. 6 joueurs)", + "To the Core (Lg 4 players)", "Jusqu'au cour (Max. 4 joueurs)", + "Trafalgar (Lg 4 players)", "Trafalgar (Max. 4 joueurs)", + "Twin Rivers (Sm 4 players)", "Les deux riviŠres (Min. 4 joueurs)", + "Umtumbo Gorge (Lg 4 players)", "La Gorge de Umtumbo (Max. 4 joueurs)", + "Watch Your Step Extreme (Mega 8 players)", "Pas-…-pas extrˆme (XL 8 joueurs)", + "Waterfalls (Lg 8 players)", "Chutes d'eau (Max. 8 joueurs)", + "Waterloo Revisited (Lg 6 players)", "Waterloo II (Max. 6 joueurs)", + "Water Werks (Mega 8 players)", "Jeux d'eau (XL 8 joueurs)", + "Warlord's Lake (Sm 4 players)", "Le lac du guerrier (Min. 4 joueurs)", + "Zama (Sm 4 players)", "Zama (Min. 4 joueurs)", +//#endif + NULL +}; +#endif + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color); +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len); +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init = 0); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, + ListClass *gamelist, ColorListClass *playerlist, int join_index, + char *my_name, RejectType *why); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used); +void Start_WWChat(ColorListClass *playerlist); +int Update_WWChat(void); + + +#define PCOLOR_BROWN PCOLOR_GREY + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; +#ifdef WINSOCK_IPX + assert ( PacketTransport != NULL ); +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // This call allocates all necessary queue buffers, allocates Real-mode + // memory, and commands IPX to start listening on the Global Channel. + //------------------------------------------------------------------------ + if (!Ipx.Init()) { + return(false); + } + + //------------------------------------------------------------------------ + // Set up the IPX manager to cross a bridge + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (Session.IsBridge) { + Session.BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ +// +// Note: The thought behind this section of code was that if the program +// terminates early, without an EventClass::EXIT event, it still needs to +// tell the other systems that it's gone, so it would send a SIGN_OFF packet. +// BUT, this causes a sync bug if the systems are running slow and this system +// is running ahead of the others; it will send the NET_SIGN_OFF >>before<< +// the other system execute their EventClass::EXIT event, and the other systems +// will kill the connection at some random Frame # & turn my stuff over to +// the computer possibly at different times. +// BRR, 10/29/96 +// +#if 0 + //------------------------------------------------------------------------ + // If the Players vector contains at least one name, send a sign-off + // packet. If 'Players' is empty, I have no name, so there's no point + // in sending a sign-off. + //------------------------------------------------------------------------ + if (Session.Players.Count()) { + //..................................................................... + // Build a sign-off packet & send it + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + } + + //..................................................................... + // Wait for the packets to finish going out (or the Global Channel + // times out) + //..................................................................... + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + } +#endif + + //------------------------------------------------------------------------ + // If I was in a game, I'm not now, so clear the game name + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + +} /* end of Shutdown_Network */ + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * Session.GameName must have been filled in before this function can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + //------------------------------------------------------------------------ + // If our Players vector is empty, just return. + //------------------------------------------------------------------------ + if (Session.Players.Count()==0) { + return (true); + } + + //------------------------------------------------------------------------ + // Another system asking what game this is + //------------------------------------------------------------------------ + if (packet->Command==NET_QUERY_GAME && Session.NetStealth==0) { + + //..................................................................... + // If the game is closed, let every player respond, and let the sender of + // the query sort it all out. This way, if the game's host exits the game, + // the game still shows up on other players' dialogs. + // If the game is open, only the game owner may respond. + //..................................................................... + if (strlen(Session.GameName) > 0 && ((!Session.NetOpen) || + (Session.NetOpen && + !strcmp(Session.Players[0]->Name,Session.GameName)))) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, Session.GameName); + mypacket.GameInfo.IsOpen = Session.NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } + + //------------------------------------------------------------------------ + // Another system asking what player I am + //------------------------------------------------------------------------ + else if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, Session.GameName) && + (strlen(Session.GameName) > 0) && Session.NetStealth==0) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); // changed DRD 9/26 + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, Session.Players[0]->Name); + mypacket.PlayerInfo.House = Session.House; + mypacket.PlayerInfo.Color = Session.ColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(Session.GameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + + return(false); + +} /* end of Process_Global_Packet */ + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player * + * on this connection * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + if (Debug_Print_Events) { + printf("Destroying connection for house %d (%s)\n", + id,HouseClass::As_Pointer((HousesType)id)->IniName); + } + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + //------------------------------------------------------------------------ + // Create a message to display to the user + //------------------------------------------------------------------------ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + //------------------------------------------------------------------------ + // Delete the IPX connection + //------------------------------------------------------------------------ + Ipx.Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + //------------------------------------------------------------------------ + // If we're the last player left, tell the user. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // Init my game name to 0-length, since I haven't joined any game yet. + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Keep looping until something useful happens. + //------------------------------------------------------------------------ + while (1) { + //..................................................................... + // Pop up the network Join/New dialog + //..................................................................... + rc = Net_Join_Dialog(); + + //..................................................................... + // -1 = user selected Cancel + //..................................................................... + if (rc==-1) { + Session.NetStealth = stealth; + Session.NetOpen = 0; + return(false); + } + + //..................................................................... + // 0 = user has joined an existing game; save values & return + //..................................................................... + else if (rc==0) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } + + //..................................................................... + // 1 = user requests New Network Game + //..................................................................... + else if (rc==1) { + //.................................................................. + // Pop up the New Network Game dialog; if user selects OK, return + // 'true'; otherwise, return to the Join Dialog. + //.................................................................. + if (Net_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + +} /* end of Remote_Connect */ + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 17 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_name_w = 70 *RESFACTOR; + int d_name_h = 9 *RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + (2*RESFACTOR); + +#ifdef OLDWAY + int d_gdi_w = 40 *RESFACTOR; + int d_gdi_h = 9 *RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 *RESFACTOR; + int d_nod_h = 9 *RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 *RESFACTOR; + int d_color_h = 9 *RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_gamelist_w = 155 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 - 2*RESFACTOR; + int d_gamelist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +//BG int d_playerlist_w = 113 *RESFACTOR; + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_playerlist_w - 2*RESFACTOR); + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_count_w = 25 *RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + d_margin2; + + int d_level_w = 25 *RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 *RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 *RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_level_h; + + int d_options_w = d_playerlist_w; + int d_options_h = ((5 * 6) + 4) *RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin2 - (2*RESFACTOR); + + int d_message1_w = d_dialog_w - (d_margin1 * 2) + 4*RESFACTOR; + int d_message1_h = (14 * d_txt6_h) +3*RESFACTOR; + int d_message1_x = d_dialog_x + (d_dialog_w-d_message1_w)/2; + int d_message1_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message1_h); + + int d_message2_w = d_message1_w; + int d_message2_h = (8 * d_txt6_h) + 3*RESFACTOR; + int d_message2_x = d_message1_x; + int d_message2_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message2_h); + +#ifdef FRENCH //VG2 + int d_join_w = 60 *RESFACTOR; +#else + int d_join_w = 40 *RESFACTOR; +#endif + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_dialog_y + d_dialog_h - d_join_h - 8*RESFACTOR; + + int d_cancel_w = 50 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_join_y; + +#ifdef FRENCH + int d_new_w = 60 *RESFACTOR; +#else + int d_new_w = 40 *RESFACTOR; +#endif + int d_new_h = 9 *RESFACTOR; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_join_y; + + int d_send_w = d_message1_w; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_message1_x; + int d_send_y = d_message1_y + d_message1_h; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {71 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + char txt[128]; + char const *p; + int parms_received = 0; // 1 = game options received + int found; + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + int goto_lobby; + bool messages_have_focus = true; // Gadget focus starts on the message system + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w); +#else + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, TPF_BUTTON, d_new_x, d_new_y, d_new_w); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + StaticButtonClass descrip(0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + 16*RESFACTOR, d_name_y, d_dialog_w - 32*RESFACTOR, d_txt6_h+1); + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color(&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color(&ColorRemaps[Session.ColorIdx]); + } + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Rule.IsMPBasesOn); + optionlist.Check_Item(1, Rule.IsMPTiberiumGrow); + optionlist.Check_Item(2, Rule.IsMPCrates); + optionlist.Check_Item(3, Rule.IsMPCaptureTheFlag); + optionlist.Check_Item(4, Rule.IsMPShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { +// for (HousesType house = HOUSE_FIRST; house <= HOUSE_TURKEY; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Session.Options.AIPlayers); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif +#ifdef WIN32 +//char *fred; +#endif + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + } + } + + /* + ** Collapse the country list if we are going to redraw the game list + */ + if (gamelist.Is_To_Redraw() && housebtn.IsDropped) { + housebtn.Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + housebtn.Collapse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + //............................................................... + // For game-browsing, label the name, side, & color buttons: + //............................................................... + if (joinstate < JOIN_CONFIRMED) { + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x + d_gdi_w, + d_gdi_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, + d_house_x + (d_house_w / 2), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + Fancy_Text_Print(TXT_COLOR_COLON, + d_dialog_x + ((d_dialog_w / 4) * 3), + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + } else { + //............................................................... + // If we're joined to a game, just print the player's name & side. + //............................................................... +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (txt, Text_String(TXT_S_PLAYING_S), namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + Fancy_Text_Print(txt,d_dialog_cx, d_dialog_y + d_margin2 + (1*RESFACTOR), + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx], + TBLACK, TPF_CENTER | TPF_TEXT); + } + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); +#ifdef OLDWAY + gdibtn.Zap(); + nodbtn.Zap(); +#else + housebtn.Zap(); +#endif + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + countgauge.Zap(); + levelgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + staticcount.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + //............................................................... + // Only add the name edit field, the House, Join & New buttons if + // we're doing nothing, or we've just been rejected. + //............................................................... + if (joinstate < JOIN_CONFIRMED) { +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } else { + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + staticcount.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + descrip.Add_Tail(*commands); + } + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS && joinstate < JOIN_CONFIRMED) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the message system; erase old messages first + //.................................................................. + if (display >= REDRAW_MESSAGE) { + if (joinstate==JOIN_CONFIRMED) { + Draw_Box(d_message2_x, d_message2_y, d_message2_w, d_message2_h, BOXSTYLE_BOX, true); + } else { + Draw_Box(d_message1_x, d_message1_y, d_message1_w, d_message1_h, BOXSTYLE_BOX, true); + } + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received && joinstate >= JOIN_CONFIRMED) { + + //............................................................... + // Scenario title + //............................................................... +// LogicPage->Fill_Rect(d_dialog_x + 16 *RESFACTOR, d_name_y, d_dialog_x + d_dialog_w - 16 *RESFACTOR, d_name_y + d_txt6_h, BLACK); + + p = Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { + + // EW - Scenario language translation goes here!!!!!!!! VG + int ii; + for (ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[ii])) { + #if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[ii+1]); + #else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + descrip.Set_Text(txt); + +// sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +// descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, scheme, BLACK, TPF_TEXT | TPF_CENTER, p, Session.Options.ScenarioDescription); + } else { + sprintf(txt, "%s %s", p, Text_String(TXT_NOT_FOUND)); + descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER, p, Text_String(TXT_NOT_FOUND)); + } + //............................................................... + // Unit count, tech level, credits, ai players + //............................................................... +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, d_count_x + d_count_w + 35 *RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + Fancy_Text_Print(TXT_COUNT, d_count_x - 2 *RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticcount.Set_Text(txt); + staticcount.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2 *RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2 *RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2 *RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2 *RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2 *RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + if (input & KN_BUTTON) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // Mouse Click: + // If we're joined to a game, display an error if the user tries to + // modify a read-only control. + // If user clicks on a color button: + // - If we've joined a game, don't allow a new color selection + // - otherwise, select that color + // - Change the color of the user's name & message field to match + // the newly-selected color. + //.................................................................. + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) { + if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + break; + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color (&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + } + name_edt.Flag_To_Redraw(); + + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + + display = REDRAW_COLORS; + } + break; + + //.................................................................. + // User clicks on the game list: + //.................................................................. + case (BUTTON_GAMELIST | KN_BUTTON): + //............................................................... + // Handle a double-click + //............................................................... + if (lastclick_timer < 30 && gamelist.Current_Index() == lastclick_idx) { + + //............................................................ + // If we're in a game, & the item clicked on is a different + // game, un-join the game we're in. + //............................................................ + if ((joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) && + lastclick_idx != game_index) { + if (gamelist.Current_Index() == 0) { + goto_lobby = 1; + } else { + goto_lobby = 0; + } + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, goto_lobby, d_message1_x, d_message1_y, d_txt6_h, + d_send_x, d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + + //......................................................... + // Clear the Player vector & the player list box, since + // our game_index has changed. + //......................................................... + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + } + //............................................................ + // If we clicked on another game, join that game. + //............................................................ + if (joinstate != JOIN_CONFIRMED && + joinstate != JOIN_WAIT_CONFIRM && lastclick_idx > 0) { + gamelist.Set_Selected_Index(lastclick_idx); + game_index = lastclick_idx; + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + //............................................................ + // Otherwise, we must have joined the lobby + //............................................................ + if (game_index == 0) { + Clear_Listbox (&playerlist); + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + display = REDRAW_ALL; + } + } else { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = gamelist.Current_Index(); + + //............................................................ + // If we've joined a game, don't allow the selected item to + // change + //............................................................ + if (joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) { + gamelist.Set_Selected_Index(game_index); + } + + //............................................................ + // If we're not in a game, and the user clicks on a different + // entry, clear the player list & send a player query; + // init the click timer, to detect a double-click of this item. + //............................................................ + else if (gamelist.Current_Index() != game_index) { + + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + break; + +#ifdef OLDWAY + //.................................................................. + // House Buttons: set the player's desired House + //.................................................................. + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; +#else +#endif + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_MESSAGE; + } + break; + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + } + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, d_message1_x, d_message1_y, d_txt6_h, d_send_x, + d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } else { + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + process = false; + rc = -1; + } + break; + + //.................................................................. + // NEW: bail out with return code 1 + //.................................................................. + case (BUTTON_NEW | KN_BUTTON): + //............................................................... + // Force user to enter a name + //............................................................... + if (strlen(namebuf)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + + //............................................................... + // Ensure name is unique + //............................................................... + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!stricmp(Session.Games[i]->Name, namebuf)) { + found = 1; + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAMENAME_MUSTBE_UNIQUE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + } + + if (found) { + break; + } + + //............................................................... + // Save player & game name + //............................................................... + strcpy(Session.Handle,namebuf); + strcpy(Session.GameName,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Service keyboard input for any message being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + // (We have to redraw the edit line, to erase the cursor.) + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + // Rather than setting 'display', which would redraw all msgs, + // we only need to erase & redraw the edit box here. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'Input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, namebuf); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // If we're joined in a game, send the message to every player + // in our player list. Skip the local system (index 0). + //............................................................ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + } else { + //............................................................ + // Otherwise, send the message to all players in our chat list. + //............................................................ + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + if (Obfuscate(Session.GPacket.Message.Buf) == 0x72A47EF6) { + Session.WWChat = 1; + Clear_Listbox (&playerlist); + Start_WWChat(&playerlist); + } + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + display = REDRAW_MESSAGE; + } + +#ifdef OBSOLETE + // + // This is for the old drop-down list of houses, not used any more. + // + if (housebtn.IsDropped) { + isdropped = 1; + } else if (isdropped) { + display = REDRAW_ALL; + } +#endif + + break; + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) { + load_game = 1; + } + + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + + bool ready_packet_was_sent = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + + /* + ** If the scenario that the host wants to play doesn't exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I don't have. Request that the host sends the + ** scenario to me provided it's not an official scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + + Ipx.Set_Timing (30, (unsigned long) -1, 600); + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + rc = 0; + process = false; + + } else if (joinstate==JOIN_CONFIRMED) { + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (d_message2_x + 1 *RESFACTOR, d_message2_y + 1 *RESFACTOR, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + } else if (joinstate==JOIN_REJECTED) { + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_REQUEST_DENIED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + + item = NULL; + if (why==REJECT_DUPLICATE_NAME) { + item = (char *)Text_String(TXT_NAME_MUSTBE_UNIQUE); + } + else if (why==REJECT_GAME_FULL) { + item = (char *)Text_String(TXT_GAME_FULL); + } + else if (why==REJECT_VERSION_TOO_OLD) { + item = (char *)Text_String(TXT_YOURGAME_OUTDATED); + } + else if (why==REJECT_VERSION_TOO_NEW) { + item = (char *)Text_String(TXT_DESTGAME_OUTDATED); + } + else if (why==REJECT_MISMATCH) { + item = (char *)Text_String(TXT_MISMATCH); + } + else if (why==REJECT_DISBANDED) { + item = (char *)Text_String(TXT_GAME_CANCELLED); + } + if (item) { + Session.Messages.Add_Message(NULL, 0, item, PCOLOR_BROWN, TPF_TEXT, 1200); + } + + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + display = REDRAW_ALL; + + } + } else if (event == EV_GAME_OPTIONS) { + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } else { + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + optionlist.Check_Item(0,Session.Options.Bases); + optionlist.Check_Item(1,Session.Options.Tiberium); + optionlist.Check_Item(2,Session.Options.Goodies); + optionlist.Check_Item(3,Special.IsCaptureTheFlag); + optionlist.Check_Item(4,Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } else if (event == EV_MESSAGE) { + //..................................................................... + // Draw an incoming message + //..................................................................... + display = REDRAW_MESSAGE; + Sound_Effect(VOC_INCOMING_MESSAGE); + } else if (event == EV_NEW_GAME) { + //..................................................................... + // If a new game has formed, or an existing game has changed state + // (from open to closed or closed to open), redraw the message system. + //..................................................................... + display = REDRAW_MESSAGE; + } else if (event == EV_NEW_PLAYER || event == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + } else if (event == EV_GAME_SIGNOFF) { + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + gamelist.Flag_To_Redraw(); + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + + //..................................................................... + // If I've changed my name or color, make sure those changes go into + // the Chat vector. + //..................................................................... + strcpy(Session.Chat[0]->Name, namebuf); + Session.Chat[0]->Chat.Color = Session.ColorIdx; + if (Session.Chat[0]->Chat.Color == PCOLOR_DIALOG_BLUE) { + Session.Chat[0]->Chat.Color = PCOLOR_REALLY_BLUE; + } + + //..................................................................... + // Clean out the chat vector. If we find a node that we haven't heard + // from in 6 seconds, delete that node. + // If we haven't heard from a node in 5 seconds, send him a request + // for a chat announcement; he then has 1 second to reply. + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + if (TickCount - Session.Chat[i]->Chat.LastTime > 360) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + } else if (TickCount - Session.Chat[i]->Chat.LastTime > 300 && + Session.Chat[i]->Chat.LastChance == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Name[0] = 0; + Session.GPacket.Command = NET_CHAT_REQUEST; + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &(Session.Chat[i]->Address)); + Ipx.Service(); + Session.Chat[i]->Chat.LastChance = 1; + } + } + + //..................................................................... + // Manage the Lobby list: + // If the user has selected the 1st Game ("Lobby"), the names of all + // users in the Chat area show up in the Players list box. + // Users can be changing their names and their colors at any time, so + // we scan the Chat list each time to see if anything's changed; if + // so, we redraw the player list. + // (If WWChat is on, the Chat list is ignored, and the playerlist + // contains custom names.) + //..................................................................... + if (game_index == 0) { + if (!Session.WWChat) { + while (Session.Chat.Count() > playerlist.Count()) { + item = new char [MPLAYER_NAME_MAX]; + item[0] = 0; + playerlist.Add_Item(item); + playerlist.Flag_To_Redraw(); + } + while (playerlist.Count() > Session.Chat.Count()) { + item = (char *)playerlist.Get_Item(0); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + for (i = 0; i < Session.Chat.Count(); i++) { + if (stricmp(Session.Chat[i]->Name,playerlist.Get_Item(i)) || + &ColorRemaps[ (Session.Chat[i]->Chat.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.Chat[i]->Chat.Color] != + playerlist.Colors[i]) { + + playerlist.Colors[i] = + &ColorRemaps[Session.Chat[i]->Chat.Color]; + if (playerlist.Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist.Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + strcpy((char *)playerlist.Get_Item(i), Session.Chat[i]->Name); + playerlist.Flag_To_Redraw(); + } + } + } else { + if (stricmp(Session.Chat[0]->Name,playerlist.Get_Item(0)) || + &ColorRemaps[Session.Chat[0]->Chat.Color] != + playerlist.Colors[0]) { + playerlist.Colors[0] = &ColorRemaps[Session.Chat[0]->Chat.Color]; + strcpy((char *)playerlist.Get_Item(0), Session.Chat[0]->Name); + playerlist.Flag_To_Redraw(); + } + if (Update_WWChat()) { + display = REDRAW_MESSAGE; + } + } + } + + } + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + starttime = TickCount; + while (TickCount - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ +// Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, +// Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Remove the chat edit box + //------------------------------------------------------------------------ + Session.Messages.Remove_Edit(); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +} /* end of Net_Join_Dialog */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color) +{ + //------------------------------------------------------------------------ + // Validate join_index + //------------------------------------------------------------------------ + if (join_index < 1) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_MUST_SELECT_GAME), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + if ( (Session.Games.Count()<=1) || join_index > Session.Games.Count()) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NOTHING_TO_JOIN), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // Force user to enter a name + //------------------------------------------------------------------------ + if (strlen(playername)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // The game must be open + //------------------------------------------------------------------------ + if (!Session.Games[join_index]->Game.IsOpen) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAME_IS_CLOSED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return (false); + } + + //------------------------------------------------------------------------ + // Send packet to game's owner + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_JOIN; + strcpy (Session.GPacket.Name, playername); + Session.GPacket.PlayerInfo.House = house; + Session.GPacket.PlayerInfo.Color = color; +#ifdef FIXIT_VERSION_3 + // Guest sends host his version. + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + if( Is_Aftermath_Installed() ) + { +// debugprint( "Guest tells host 'I have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version() | 0x80000000; + } + else + { +// debugprint( "Guest tells host 'I don't have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); + } +#else + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); +#endif + Session.GPacket.PlayerInfo.MaxVersion = VerNum.Max_Version(); + Session.GPacket.PlayerInfo.CheatCheck = RuleINI.Get_Unique_ID(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[join_index]->Address)); + + return(true); + +} /* end of Request_To_Join */ + + +/*************************************************************************** + * Unjoin_Game -- Cancels joining a game * + * * + * INPUT: * + * namebuf current player name * + * joinstate current join state * + * gamelist ListBox of game names * + * playerlist ListBox of player names * + * game_index index in 'gamelist' of game we're leaving * + * goto_lobby true = we're going to the lobby * + * msg_x message system x-coord * + * msg_y message system y-coord * + * msg_h message system char height * + * send_x message system send x-coord * + * send_y message system send y-coord * + * msg_len message system max msg length * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/12/1995 BRR : Created. * + *=========================================================================*/ +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len) +{ + int i; + char *item; + + //------------------------------------------------------------------------ + // Fill in a SIGN_OFF packet + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + + //------------------------------------------------------------------------ + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //------------------------------------------------------------------------ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + if (joinstate == JOIN_WAIT_CONFIRM || joinstate == JOIN_CONFIRMED) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[game_index]->Address)); + } + + //------------------------------------------------------------------------ + // Re-init the message system to its new larger size + //------------------------------------------------------------------------ + Session.Messages.Init (msg_x + 1, msg_y + 1, 14, + msg_len, msg_h, send_x + 1, send_y + 1, 1, + 20, msg_len - 5); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_'); + + //------------------------------------------------------------------------ + // Remove myself from the player list, and reset my game name + //------------------------------------------------------------------------ + if (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + } + + if (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // Highlight "Lobby" on the Game list, Announce I'm ready to chat + //------------------------------------------------------------------------ + if (goto_lobby) { + gamelist->Set_Selected_Index(0); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + } + +} // end of Unjoin_Game + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * joinstate our current joinstate * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * chatnow if 1, will immediately send the chat announcement * + * myname user's name * + * init initialize the timers * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init) +{ + //........................................................................ + // These values control the timeouts for sending various types of packets; + // they're designed such that they'll rarely occur simultaneously. + //........................................................................ + enum { + GAME_QUERY_TIME = 120, + PLAYER_QUERY_TIME = 35, + CHAT_ANNOUNCE_TIME = 83, + }; + static CDTimerClass game_timer; // time between NET_QUERY_GAME's + static CDTimerClass player_timer; // time between NET_QUERY_PLAYERS's + static CDTimerClass chat_timer; // time between NET_CHAT_ANNOUNCE's + + + //------------------------------------------------------------------------ + // Initialize timers + //------------------------------------------------------------------------ + if (init) { + game_timer = GAME_QUERY_TIME; + player_timer = PLAYER_QUERY_TIME; + chat_timer = CHAT_ANNOUNCE_TIME; + } + + //------------------------------------------------------------------------ + // Send the game-name query if the time has expired, or we're told to do + // it right now + //------------------------------------------------------------------------ + if (!game_timer || gamenow) { + + game_timer = GAME_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the player query for the game currently clicked on, if the time has + // expired and there is a currently-selected game, or we're told to do it + // right now + //------------------------------------------------------------------------ + if ( ((curgame > 0) && (curgame < Session.Games.Count()) && + !player_timer) || playernow) { + + player_timer = PLAYER_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_PLAYER; + strcpy (Session.GPacket.Name, Session.Games[curgame]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the chat announcement + //------------------------------------------------------------------------ + if ((!chat_timer && joinstate!=JOIN_CONFIRMED) || chatnow) { + + chat_timer = CHAT_ANNOUNCE_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy (Session.GPacket.Name, myname); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * Session.House (from NET_CONFIRM_JOIN) * + * Session.ColorIdx (from NET_CONFIRM_JOIN) * + * Session.Options.Bases (from NET_GAME_OPTIONS) * + * Session.Options.Tiberium (from NET_GAME_OPTIONS) * + * Session.Options.Goodies (from NET_GAME_OPTIONS) * + * Session.Options.Ghosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * my_name name of local system * + * why ptr: filled in with reason for rejection from a game * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index, char *my_name, RejectType *why) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) + return(EV_NONE); + + //------------------------------------------------------------------------ + // If we're joined in a game, handle the packet in a standard way; otherwise, + // don't answer standard queries. + //------------------------------------------------------------------------ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&Session.GPacket,&Session.GAddress)!=0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // NET_ANSWER_GAME: Another system is answering our GAME query, so add that + // system to our list box if it's new. + //------------------------------------------------------------------------ + if (Session.GPacket.Command==NET_ANSWER_GAME) { + + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name)) { + found = 1; + + //............................................................... + // If name was found, update the node's time stamp & IsOpen flag. + //............................................................... + Session.Games[i]->Game.LastTime = TickCount; + if (Session.Games[i]->Game.IsOpen != Session.GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME), + Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + Session.Games[i]->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + + //............................................................ + // If this game has gone from closed to open, copy the + // responder's address into our Game slot, since the guy + // responding to this must be game owner. + //............................................................ + if (Session.Games[i]->Game.IsOpen) { + Session.Games[i]->Address = Session.GAddress; + } + + //............................................................ + // If we're in chat mode, print a message that the state of + // this game has changed. + //............................................................ + if (*joinstate < JOIN_CONFIRMED) { + if (Session.Games[i]->Game.IsOpen) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_FORMING); + } + else { + sprintf(txt,Text_String(TXT_GAME_NOW_IN_PROGRESS), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_CLOSED); + } + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + retcode = EV_NEW_GAME; + } + } + break; + } + } + + //..................................................................... + // name not found (or addresses are different); add it to 'Games' + //..................................................................... + if (found==0) { + + //.................................................................. + // Create a new node structure, fill it in, add it to 'Games' + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount; + Session.Games.Add (who); + + //.................................................................. + // Create a string for "xxx's Game", leaving room for brackets around + // the string if it's a closed game + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + gamelist->Add_Item(item); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this game is open, display a message stating that it's + // now available. + //.................................................................. + if (Session.GPacket.GameInfo.IsOpen && (*joinstate) < JOIN_CONFIRMED) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.GPacket.Name); + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_GAME_FORMING); + } + + retcode = EV_NEW_GAME; + } + } + + //------------------------------------------------------------------------ + // NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add + // it to our player list box & the Player Vector if it's new + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_ANSWER_PLAYER) { + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // If the address is already present, re-copy their name, color & + // house into the existing entry, in case they've changed it without + // our knowledge; set the 'found' flag so we won't create a new entry. + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + strcpy(Session.Players[i]->Name, Session.GPacket.Name); + Session.Players[i]->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players[i]->Player.Color = Session.GPacket.PlayerInfo.Color; + + playerlist->Colors[i] = + &ColorRemaps[Session.GPacket.PlayerInfo.Color]; + + if (playerlist->Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist->Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + + found = 1; + break; + } + } + + //..................................................................... + // Don't add this player if he's not part of the game that's selected. + //..................................................................... + i = gamelist->Current_Index(); + if (Session.Games.Count() && Session.GPacket.PlayerInfo.NameCRC != + Compute_Name_CRC(Session.Games[i]->Name)) { + found = 1; + } + + //..................................................................... + // Don't add this player if it's myself. (We must check the name + // since the address of myself in 'Players' won't be valid.) + //..................................................................... + if (!strcmp (my_name,Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // name not found, or address didn't match; add to player list box + // & Players Vector + //..................................................................... + if (found==0) { + //.................................................................. + // Create & add a node to the Vector + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + who->Player.Color = Session.GPacket.PlayerInfo.Color; + Session.Players.Add (who); + + //.................................................................. + // Create & add a string to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this player has joined our game, play a special sound. + //.................................................................. + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_JOINED); + } + + retcode = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us + // as being confirmed, and start answering queries from other systems + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (Session.GameName, Session.GPacket.Name); + Session.House = Session.GPacket.PlayerInfo.House; + Session.ColorIdx = Session.GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + // the dialog state to its first pop-up state. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_REJECT_JOIN) { + //..................................................................... + // If we're confirmed in a game, broadcast a sign-off to tell all other + // systems that I'm no longer a part of any game; this way, I'll be + // properly removed from their dialogs. + //..................................................................... + if ( (*joinstate) == JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name,my_name); + + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Session.GameName[0] = 0; + + //.................................................................. + // remove myself from the player list + //.................................................................. + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + + (*joinstate) = JOIN_REJECTED; + (*why) = REJECT_BY_OWNER; + retcode = EV_STATE_CHANGE; + } + //..................................................................... + // If we're waiting for confirmation & got rejected, tell the user why + //..................................................................... + else if ((*joinstate) == JOIN_WAIT_CONFIRM) { + (*why) = (RejectType)Session.GPacket.Reject.Why; + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_GAME_OPTIONS: The game owner has changed the game options & is + // sending us the new values. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED || (*joinstate)==JOIN_WAIT_CONFIRM) { + Session.Options.Credits = Session.GPacket.ScenarioInfo.Credits; + Session.Options.Bases = Session.GPacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = Session.GPacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = Session.GPacket.ScenarioInfo.IsGoodies; +// Session.Options.Ghosts = Session.GPacket.ScenarioInfo.IsGhosties; + Session.Options.AIPlayers = Session.GPacket.ScenarioInfo.AIPlayers; + BuildLevel = Session.GPacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = Session.GPacket.ScenarioInfo.UnitCount; + Seed = Session.GPacket.ScenarioInfo.Seed; + Special = Session.GPacket.ScenarioInfo.Special; + Options.GameSpeed = Session.GPacket.ScenarioInfo.GameSpeed; + +#ifdef FIXIT_VERSION_3 + // Guest receives game version number from host. + // Added to the transmitted version number is a bit indicating presence of Aftermath expansion. + unsigned long lVersion = Session.GPacket.ScenarioInfo.Version & ~0x80000000; // Actual version number. + Session.CommProtocol = VerNum.Version_Protocol( lVersion ); + bAftermathMultiplayer = Session.GPacket.ScenarioInfo.Version & 0x80000000; +// if( bAftermathMultiplayer ) +// debugprint( "Guest hears host say 'This is an Aftermath game'\n" ); +// else +// debugprint( "Guest hears host say 'This is NOT an Aftermath game'\n" ); +#else + Session.CommProtocol = VerNum.Version_Protocol(Session.GPacket.ScenarioInfo.Version); + PlayingAgainstVersion = Session.GPacket.ScenarioInfo.Version; +#endif + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Rule.IsTGrowth = 1; + Special.IsTSpread = 1; + Rule.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Rule.IsTGrowth = 0; + Special.IsTSpread = 0; + Rule.IsTSpread = 0; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, Session.GPacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, Session.GPacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest, sizeof (Session.GPacket.ScenarioInfo.FileDigest)); +#else + strcpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = Session.GPacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = Session.GPacket.ScenarioInfo.FileLength; + + retcode = EV_GAME_OPTIONS; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // both the game list & player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + //..................................................................... + // Remove this name from the list of games + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name) && + Session.Games[i]->Address==Session.GAddress) { + + //............................................................... + // If the system signing off is the currently-selected list + // item, clear the player list since that game is no longer + // forming. + //............................................................... + if (i==gamelist->Current_Index()) { + Clear_Listbox (playerlist); + Clear_Vector (&Session.Players); + } + + //............................................................... + // If the system signing off was the owner of our game, mark + // ourselves as rejected + //............................................................... + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + (*why) = REJECT_DISBANDED; + } + + //............................................................... + // Set my return code + //............................................................... + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } + else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + //............................................................... + // Remove game name from game list + //............................................................... + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + + //..................................................................... + // Remove this name from the list of players + //..................................................................... + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + playerlist->Flag_To_Redraw(); + + //............................................................... + // If this player has left our game, play a special sound. + //............................................................... + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_LEFT); + } + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + //..................................................................... + // Remove this name from the chat list + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Chat[i]->Address==Session.GAddress) { + + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + } + + //------------------------------------------------------------------------ + // NET_GO: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; + Session.HostAddress = Session.GAddress; + } + } + + //------------------------------------------------------------------------ + // NET_LOADGAME: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_LOADGAME) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START_LOAD; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_ANNOUNCE: Someone is ready to chat; add them to our list, if + // they aren't already on it, and it's not myself. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_ANNOUNCE) { + found = 0; + //..................................................................... + // If this packet is from myself, don't add it to the list + //..................................................................... + if (Session.GPacket.Chat.ID == Session.UniqueID) { + found = 1; + } + //..................................................................... + // Otherwise, see if we already have this address stored in our list + // If so, update that node's time values & name (in case the user + // changed it), and return. + //..................................................................... + else { + for (i = 0; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + strcpy (Session.Chat[i]->Name, Session.GPacket.Name); + Session.Chat[i]->Chat.LastTime = TickCount; + Session.Chat[i]->Chat.LastChance = 0; + Session.Chat[i]->Chat.Color = Session.GPacket.Chat.Color; + found = 1; + break; + } + } + } + //..................................................................... + // Add a new node to the list + //..................................................................... + if (!found) { + who = new NodeNameType; + strcpy (who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Chat.LastTime = TickCount; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_REQUEST: Someone is requesting a CHAT_ANNOUNCE from us; send + // one to him directly. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_REQUEST) { + if ((*joinstate) != JOIN_WAIT_CONFIRM && (*joinstate) != JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy(Session.GPacket.Name, my_name); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &Session.GAddress); + + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + //..................................................................... + // If we're in a game, the sender must be in our game. + //..................................................................... + if ( (*joinstate)==JOIN_CONFIRMED) { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + } + //..................................................................... + // Otherwise, we're in the chat room; display any old message. + //..................................................................... + else { + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + + retcode = EV_MESSAGE; + } + + //------------------------------------------------------------------------ + // NET_PING: Someone is pinging me to get a response time measure (will only + // happen after I've joined a game). Do nothing; the IPX Manager will handle + // sending an ACK, and updating the response time measurements. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + //------------------------------------------------------------------------ + // Default case: nothing happened. (This case will be hit every time I + // receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + //------------------------------------------------------------------------ + else { + retcode = EV_NONE; + } + + return(retcode); + +} /* end of Get_Join_Responses */ + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ + return 0; //PG +#if (0) + typedef enum { + NUM_MESSAGES = 10 + } NumMessagesType; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + +//BG int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_w = 124*RESFACTOR; + int d_playerlist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR; + + int d_scenariolist_w = 162*RESFACTOR; + int d_scenariolist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - (5*RESFACTOR); + int d_scenariolist_y = d_playerlist_y; + + int d_reject_w = 55*RESFACTOR; + int d_reject_h = 9*RESFACTOR; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_count_y = d_reject_y + d_reject_h /*KO+ d_margin2*/; + + int d_level_w = 25*RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25*RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106*RESFACTOR; + int d_options_h = ((5 * 6) + 4)*RESFACTOR; + int d_options_x = d_scenariolist_x + ((d_scenariolist_w - d_options_w) / 2); + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (NUM_MESSAGES * d_txt6_h) + 3*RESFACTOR; // 10 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message_h); +// int d_message_y = d_options_y + d_options_h + d_margin1; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9*RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 50*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + int d_cancel_w = 50*RESFACTOR; + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, 60*RESFACTOR); + + StaticButtonClass staticunit(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + if (loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE +// loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init dialog values, only the first time through + //------------------------------------------------------------------------ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = + (SessionClass::CountMax[Session.Options.Bases] + + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = 0; + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = 0; + + //------------------------------------------------------------------------ + // Init scenario description list box + //------------------------------------------------------------------------ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + + break; + } + } + if ( EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + { + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected +#ifdef FIXIT_VERSION_3 + bAftermathMultiplayer = Is_Aftermath_Installed(); +// debugprint( "Host decides that, initially, bAftermathMultiplayer is %i\n", bAftermathMultiplayer ); +#else + PlayingAgainstVersion = VerNum.Version_Number(); +#endif + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the message display system + //------------------------------------------------------------------------ + Session.Messages.Init(d_message_x + 1*RESFACTOR, d_message_y + 1*RESFACTOR, NUM_MESSAGES, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1*RESFACTOR, d_send_y + 1*RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + /* + ** Zap, Zap, Zap + */ + playerlist.Zap(); + scenariolist.Zap(); + rejectbtn.Zap(); + staticunit.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + countgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + levelgauge.Zap(); + optionlist.Zap(); + okbtn.Zap(); + cancelbtn.Zap(); + loadbtn.Zap(); + + + /* + ** Hack hack, hack + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) { +#else + if (loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { +#endif +#ifdef FIXIT_MULTI_SAVE + loadbtn.Add_Tail(*commands); +#endif + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2*RESFACTOR, d_count_y, d_count_x + d_count_w + 35*RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticunit.Set_Text(txt); + staticunit.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2*RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2*RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // New Scenario selected. + //.................................................................. +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + break; + +#else // FIXIT_VERSION_3 + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + }else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + } else { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + } + break; +#endif // FIXIT_VERSION_3 + + //.................................................................. + // Reject the currently-selected player (don't allow rejecting myself, + // who will be the first entry in the list) + //.................................................................. + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + + if (index == 0) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_CANT_REJECT_SELF), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + + } else if (index < 0 || index >= playerlist.Count()) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_SELECT_PLAYER_REJECT), + PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 1, + &(Session.Players[index]->Address)); + break; + + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case (BUTTON_OPTIONS | KN_BUTTON): + if (Special.IsCaptureTheFlag != optionlist.Is_Checked(3) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != (optionlist.Is_Checked(0) ? 1 : 0)) { + Session.Options.Bases = (optionlist.Is_Checked(0) ? 1 : 0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + optionlist.Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsCaptureTheFlag = optionlist.Is_Checked(3); + Special.IsShadowGrow = optionlist.Is_Checked(4); + + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // OK: exit loop with TRUE status + //.................................................................. + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + while ((int)TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Players.Count() > 1 ) { +// if (Session.Players.Count() + Session.Options.AIPlayers > 1 ) { + rc = TRUE; + process = FALSE; + } else { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_ONE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) { + load_game = 1; + } else { + load_game = 0; + } + break; + + //.................................................................. + // CANCEL: send a SIGN_OFF, bail out with error code + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // Broadcast my sign-off over a bridged network if there is one + //............................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Re-draw the messages & service keyboard input for any message + // being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Handle); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // Send the message to every player in our player list, except + // myself. + //............................................................ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + // If there's no message with this ID already displayed, just + // add a new message; if there is one, concatenate it. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + display = REDRAW_MESSAGE; + } + break; + } + + //..................................................................... + // Process incoming packets + //..................................................................... +#ifndef FIXIT_VERSION_3 + int oldversion = PlayingAgainstVersion; +#endif + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } +#ifdef FIXIT_VERSION_3 // All scenarios now allowable for download, regardless of if CS scen. or 126x126 scen. + if (display < REDRAW_PARMS) display = REDRAW_PARMS; +#else // FIXIT_VERSION_3 + if (oldversion == PlayingAgainstVersion){ + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + }else{ + + /* + ** If a CS scenario was selected and we now have a red alert only player + ** in the mix then deselect the cs scenario. + */ + #ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && + #else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && + #endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #ifdef FIXIT_CSII // checked - ajw + /* + ** If an AM mega scenario was selected and we now have a non-AM + ** player in the mix then deselect the mega scenario. + */ + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #endif + display = REDRAW_BACKGROUND; + } +#endif // FIXIT_VERSION_3 + + transmit = 1; + } else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else if (whahoppa == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + display = REDRAW_PARMS; + } + transmit = 1; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; +#ifdef FIXIT_VERSION_3 + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + // Host encodes whether or not this is an Aftermath game in the highest bit. + if( bAftermathMultiplayer ) + { +// debugprint( "Host tells guests 'This is an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version() | 0x80000000; + } + else + { +// debugprint( "Host tells guests 'This is NOT an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + } +#else + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); +#endif + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = max( (Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response."); +#endif + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*10; // Wait for 10 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); +#endif +} /* end of Net_New_Dialog */ + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * color_used array of color-used flags * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_PLAYER_SIGNOFF = a player has left * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + unsigned long version; // version # to use + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // Try to handle the packet in a standard way + //------------------------------------------------------------------------ + if (Process_Global_Packet(&Session.GPacket,&Session.GAddress) != 0) { + return(EV_NONE); + } else if (Session.GPacket.Command==NET_QUERY_JOIN) { + //------------------------------------------------------------------------ + // NET_QUERY_JOIN: + //------------------------------------------------------------------------ + //..................................................................... + // See if this name is unique: + // - If the name matches, but the address is different, reject this player + // - If the name & address match, this packet must be a re-send of a + // previous request; in this case, do nothing. The other player must have + // received my CONFIRM_JOIN packet (since it was sent with an ACK + // required), so we can ignore this resend. + //..................................................................... + found = 0; + resend = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (!strcmp(Session.Players[i]->Name,Session.GPacket.Name)) { + if (Session.Players[i]->Address != Session.GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + //..................................................................... + // If his name is the same as mine, treat it like a duplicate name + //..................................................................... + if (!strcmp (Session.Players[0]->Name, Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // Reject if name is a duplicate + //..................................................................... + if (found) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_DUPLICATE_NAME; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // Reject if there are too many players + //..................................................................... + else if ( (Session.Players.Count() >= Session.MaxPlayers) && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_GAME_FULL; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + /* + ** Don't allow joining if the rules.ini file doesn't appear to match. + */ + else if (Session.GPacket.PlayerInfo.CheatCheck != RuleINI.Get_Unique_ID() && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_MISMATCH; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // If this packet is NOT a resend, accept the player. Grant him the + // requested color if possible. + //..................................................................... + else if (!resend) { + //.................................................................. + // Check the player's version range against our own, to see if + // there's an overlap region + //.................................................................. +#ifdef FIXIT_VERSION_3 + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + bool bGuestHasAftermath = Session.GPacket.PlayerInfo.MinVersion & 0x80000000; + if( bGuestHasAftermath ) +// debugprint( "Host hears guest say 'I have Aftermath'\n" ); + ; + else + { +// debugprint( "Host hears guest say 'I don't have Aftermath'\n" ); + if( bAftermathMultiplayer ) + { +// debugprint( "Host decides this is no longer an Aftermath game!\n" ); + bAftermathMultiplayer = false; + } + } + + Session.GPacket.PlayerInfo.MinVersion &= ~0x80000000; // Strip special bit. +#endif + version = VerNum.Clip_Version (Session.GPacket.PlayerInfo.MinVersion, + Session.GPacket.PlayerInfo.MaxVersion); + +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + +// TCTCTC save off version number + + //.................................................................. + // Reject player if his version is too old + //.................................................................. + if (version == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_OLD; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //.................................................................. + // Reject player if his version is too new + //.................................................................. + else if (version == 0xffffffff) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_NEW; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + //.................................................................. + // If the player is accepted, our mutually-accepted version may be + // different; set the CommProtocol accordingly. + //.................................................................. + else { + Session.CommProtocol = VerNum.Version_Protocol(version); + } + + //.................................................................. + // Add node to the Vector list + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players.Add (who); + + //.................................................................. + // Set player's color; if requested color isn't used, give it to him; + // otherwise, give him the 1st available color. Mark the color we + // give him as used. + //.................................................................. + if (color_used[Session.GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = Session.GPacket.PlayerInfo.Color; + } + else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (color_used[i]==0) { + who->Player.Color = (PlayerColorType)i; + break; + } + } + } + color_used[who->Player.Color] = 1; + + //.................................................................. + // Add player name to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, + Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // Send a confirmation packet + //.................................................................. + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CONFIRM_JOIN; + strcpy(Session.GPacket.Name,Session.Handle); + Session.GPacket.PlayerInfo.House = who->Player.House; + Session.GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + + //.................................................................. + // Play a special sound. + //.................................................................. + Sound_Effect(VOC_PLAYER_JOINED); + + retval = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // the player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (!strcmp (Session.Players[i]->Name, Session.GPacket.Name) && + Session.Players[i]->Address==Session.GAddress) { + + //............................................................... + // Remove from the list box + //............................................................... + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + + //............................................................... + // Mark his color as available + //............................................................... + color_used[Session.Players[i]->Player.Color] = 0; + + //............................................................... + // Delete from the Vector list + //............................................................... + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + //............................................................... + // Play a special sound. + //............................................................... + Sound_Effect(VOC_PLAYER_LEFT); + + retval = EV_PLAYER_SIGNOFF; + + break; + } + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + retval = EV_MESSAGE; + } + + return(retval); + +} /* end of Get_NewGame_Responses */ + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < (int)strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); + +} /* end of Compute_Name_CRC */ + + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + reconn; fresh; oldest_index; timeval; +#if (0)//PG + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int d_txt6_h = 6 * RESFACTOR+1; + int d_margin = 5 * RESFACTOR; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + fresh = true; + } +#endif + + //------------------------------------------------------------------------ + // Draw the dialog from scratch + //------------------------------------------------------------------------ + if (fresh) { + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + switch ( Session.Type) { + case GAME_IPX: + case GAME_INTERNET: + case GAME_MODEM: + case GAME_NULL_MODEM: + + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + break; + +#if(TEN) + case GAME_TEN: + if (reconn) { + id = Ten->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ten->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // TEN + +#if(MPATH) + case GAME_MPATH: + if (reconn) { + id = MPath->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + MPath->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // MPATH + } + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = max(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + +#ifdef FIXIT_VERSION_3 + char szNewCancelMessage[ 300 ]; + sprintf( szNewCancelMessage, "%s%s", buf3, TXT_WOL_CANCELMEANSFORFEIT ); + + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + { + w = max(String_Pixel_Width(szNewCancelMessage), w); // * RESFACTOR; why was it ever multiplied by this!!!? + w += (d_margin * 12); + } + else + { + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); + } +#else + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); +#endif + + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*RESFACTOR - (w / 2); + y = 100*RESFACTOR - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*RESFACTOR, y + (d_margin * 2), scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + Fancy_Text_Print (szNewCancelMessage, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + + Show_Mouse(); + } + //------------------------------------------------------------------------ + // Just update the timeout value on the dialog + //------------------------------------------------------------------------ + else { + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + + int fillx = 160*RESFACTOR - (String_Pixel_Width (buf2) / 2) -6; + LogicPage->Fill_Rect (fillx, y + (d_margin * 2) + d_txt6_h + d_margin, + fillx + String_Pixel_Width (buf2) + 12, + y + (d_margin * 2) + d_txt6_h + d_margin + d_txt6_h +1*RESFACTOR, + BLACK); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, BLACK, TPF_CENTER | TPF_TEXT); + + Show_Mouse(); + } +#endif +} /* end of Net_Reconnect_Dialog */ + +#define MAX_CHAT_NAME 12 +#define MAX_CHAT_PHRASE 45 + +struct WWPerson { + char Name[MAX_CHAT_NAME]; + char Phrase[MAX_CHAT_PHRASE]; + PlayerColorType Color; + unsigned long LastTime; +}; + +struct WWPerson WWPersons[] = { + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,105,115,110,39,116,32,116,104,105,115,32,99,111,111,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,119,97,110,116,32,115,111,109,101,32,115,101,97,102,111,111,100,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {71,114,101,97,116,46,32,74,117,115,116,32,103,114,101,97,116,46,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,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {87,111,110,100,101,114,102,117,108,46,32,80,101,114,102,101,99,116,44,32,105,110,32,102,97,99,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {89,111,117,32,99,97,108,108,32,116,104,105,115,32,65,73,63,32,32,83,104,101,101,115,104,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {66,105,108,108,115,32,114,117,108,101,33,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,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {84,97,107,101,32,116,104,105,115,32,111,117,116,44,32,110,111,119,33,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,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {87,104,121,32,99,97,110,39,116,32,121,111,117,32,102,105,116,32,97,110,121,32,109,111,114,101,32,108,101,116,116,101,114,115,32,111,110,32,116,104,101,32,108,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,73,32,119,97,110,116,32,116,111,32,116,97,108,107,32,116,111,32,121,111,117,32,97,98,111,117,116,32,116,104,105,115,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,99,111,109,101,32,116,111,32,109,121,32,111,102,102,105,99,101,44,32,110,111,119,33,32,32,91,99,114,97,99,107,33,93,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,115,116,97,112,108,101,114,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,116,104,101,115,97,117,114,117,115,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,112,101,110,99,105,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {72,101,121,32,83,116,101,118,101,44,32,99,97,110,32,119,101,32,109,101,101,116,32,105,110,32,121,111,117,114,32,111,102,102,105,99,101,63,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {83,116,101,118,101,44,32,105,116,39,108,108,32,111,110,108,121,32,116,97,107,101,32,97,32,109,105,110,117,116,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,111,98,32,83,0,0,0,0,0,0,0,}, + {77,105,108,111,44,32,100,105,97,108,32,50,52,57,46,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,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,119,105,116,104,32,97,110,32,69,45,76,45,83,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,115,98,97,114,102,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,102,98,117,116,116,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,115,111,111,111,32,119,101,105,114,100,33,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,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {68,117,104,33,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,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {72,101,121,44,32,105,116,32,119,111,114,107,101,100,32,111,110,32,109,121,32,99,111,109,112,117,116,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,32,105,115,32,99,108,101,97,114,108,121,32,97,32,119,97,115,116,101,32,111,102,32,109,101,109,111,114,121,46,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {72,109,109,44,32,73,32,115,101,101,33,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,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {65,104,104,44,32,121,101,115,46,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,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {78,111,116,32,97,110,111,116,104,101,114,32,105,110,115,116,97,108,108,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {72,65,32,72,65,32,72,65,32,72,65,32,72,65,33,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,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,114,101,97,108,108,121,32,99,111,111,108,33,32,32,71,111,115,104,32,103,117,121,115,33,32,32,87,111,119,33,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {87,111,119,33,32,32,89,111,117,32,103,117,121,115,32,97,114,101,32,116,104,101,32,103,114,101,97,116,101,115,116,33,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {71,114,97,121,102,105,115,104,32,102,111,114,32,108,117,110,99,104,32,97,103,97,105,110,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,108,97,109,101,46,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,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,39,115,32,98,117,103,103,101,100,46,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,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {83,104,105,112,32,105,116,33,33,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,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,87,0,0,0,0,0,}, + {79,75,32,101,118,101,114,121,111,110,101,44,32,111,117,116,32,111,102,32,109,121,32,111,102,102,105,99,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,99,104,97,116,32,112,114,111,103,114,97,109,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,110,105,110,106,97,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {73,32,119,101,97,114,32,100,101,115,105,103,110,101,114,32,106,101,97,110,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {72,101,121,32,66,105,108,108,44,32,116,104,105,115,32,116,104,105,110,103,32,107,101,101,112,115,32,99,114,97,25,26,24,104,105,110,103,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,98,101,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,119,111,114,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,78,0,0,0,0,0,0,}, + {83,111,117,110,100,115,32,108,105,107,101,32,97,32,100,114,105,118,101,114,32,112,114,111,98,108,101,109,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,99,111,116,116,32,66,0,0,0,0,0,}, + {73,32,110,101,101,100,32,116,104,105,115,32,102,111,114,32,76,79,76,50,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,84,0,0,0,0,0,}, + {84,104,105,115,32,115,101,101,109,115,32,114,97,116,104,101,114,32,115,105,108,108,121,44,32,97,99,116,117,97,108,108,121,46,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,117,108,32,77,0,0,0,0,0,0,}, + {78,111,32,46,46,46,46,32,32,87,32,65,32,89,33,33,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,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,80,0,0,0,0,0,0,}, + {65,72,32,72,65,32,72,65,32,72,65,33,33,32,32,72,65,72,65,33,33,32,32,65,72,72,32,72,65,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {83,116,97,108,105,110,32,107,105,108,108,101,100,32,109,121,32,102,97,116,104,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {73,32,104,111,112,101,32,121,111,117,39,114,101,32,112,114,111,117,100,32,111,102,32,121,111,117,114,115,101,108,102,44,32,66,105,108,108,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,103,32,87,105,108,108,121,0,0,0,}, + {65,110,121,98,111,100,121,32,102,101,114,32,97,32,115,112,105,116,116,105,110,39,32,99,111,110,116,101,115,116,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,99,32,87,0,0,0,0,0,0,}, + {75,97,114,97,116,101,32,105,115,32,103,111,111,100,32,98,117,116,32,121,111,117,32,117,115,101,32,105,116,32,102,111,114,32,101,118,105,108,33,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {84,101,100,32,77,0,0,0,0,0,0,0,}, + {72,97,46,32,32,70,117,110,110,121,46,32,32,84,104,105,115,32,105,115,32,102,117,110,110,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,97,109,111,110,32,82,0,0,0,0,0,}, + {73,32,98,108,97,109,101,32,116,104,101,32,70,114,101,110,99,104,46,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,},PCOLOR_GREEN,0}, + { {68,119,105,103,104,116,32,79,0,0,0,0,}, + {79,104,44,32,109,97,110,33,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,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {73,111,110,32,99,97,110,110,111,110,32,114,101,97,100,121,46,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,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {83,101,108,101,99,116,32,116,97,114,103,101,116,46,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,},PCOLOR_GREEN,0}, + { {66,114,117,99,101,32,74,0,0,0,0,0,}, + {73,32,97,109,32,116,104,101,32,71,101,110,105,101,32,111,102,32,116,104,101,32,108,97,109,112,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {76,97,117,114,97,32,87,0,0,0,0,0,}, + {71,111,32,97,119,97,121,44,32,66,105,108,108,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,},PCOLOR_GREEN,0}, + { {68,97,118,105,100,32,68,0,0,0,0,0,}, + {72,109,109,109,46,46,46,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,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,105,0,0,0,0,0,}, + {67,97,108,108,32,109,101,32,78,97,116,101,46,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,},PCOLOR_GREEN,0}, + { {68,101,110,122,105,108,32,76,0,0,0,0,}, + {84,104,105,115,32,119,111,117,108,100,32,98,101,32,98,101,116,116,101,114,32,111,110,32,116,104,101,32,77,97,99,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,114,101,103,32,72,0,0,0,0,0,0,}, + {66,117,116,32,100,111,101,115,32,105,116,32,102,105,116,32,105,110,116,111,32,50,32,77,66,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,110,97,116,104,97,110,32,76,0,0,}, + {73,32,116,104,105,110,107,32,73,32,110,101,101,100,32,97,32,104,97,105,114,99,117,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {65,100,97,109,32,73,0,0,0,0,0,0,}, + {87,104,97,116,101,118,101,114,46,32,32,66,101,101,102,33,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,},PCOLOR_GREEN,0}, + { {75,117,114,116,32,79,0,0,0,0,0,0,}, + {70,105,120,32,105,116,32,121,111,117,114,115,101,108,102,46,32,32,73,39,109,32,98,117,115,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,115,101,112,104,32,72,0,0,0,0,}, + {60,73,99,121,32,103,108,97,114,101,62,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,},PCOLOR_GREEN,0}, + { {77,97,116,116,32,72,0,0,0,0,0,0,}, + {73,39,109,32,110,101,118,101,114,32,103,111,111,100,32,119,105,116,104,32,99,108,101,118,101,114,32,111,110,32,116,104,101,32,115,112,111,116,46,46,46,0,0,0,0,},PCOLOR_GREEN,0}, + { {67,104,114,105,115,32,82,0,0,0,0,0,}, + {73,32,108,111,118,101,32,109,121,32,106,111,98,46,32,32,89,101,97,104,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,116,32,67,0,0,0,0,0,0,0,}, + {73,39,109,32,97,32,112,114,111,102,101,115,115,105,111,110,97,108,32,121,111,100,101,108,108,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {73,97,110,32,76,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,},PCOLOR_GREEN,0}, + { {74,97,99,107,32,77,0,0,0,0,0,0,}, + {73,32,108,105,118,101,32,97,98,111,117,116,32,97,32,98,108,111,99,107,32,102,114,111,109,32,116,104,101,32,112,114,111,112,111,115,101,100,32,115,105,116,101,46,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,66,0,0,0,0,0,0,}, + {84,104,101,121,32,97,108,108,32,108,111,111,107,32,108,105,107,101,32,97,110,116,115,32,102,114,111,109,32,117,112,32,104,101,114,101,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, +}; + +CDTimerClass wwperson_timer; + +void Start_WWChat(ColorListClass *playerlist) +{ + char *item; + int i; + HousesType house; + + //------------------------------------------------------------------------ + // Ensure a different sequence each time + //------------------------------------------------------------------------ + Scen.RandomNumber = rand(); + + //------------------------------------------------------------------------ + // Add myself to the player list + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + //------------------------------------------------------------------------ + // Add everyone else to the list + //------------------------------------------------------------------------ + for (i = 0; i < sizeof(WWPersons) / sizeof(struct WWPerson); i++) { + //..................................................................... + // Add the 1st entry to the list no matter what; for entries after the + // 1st, only add the name if it's different from the previous name. + //..................................................................... + if (i==0 || strcmp(WWPersons[i].Name, WWPersons[i-1].Name)) { + WWPersons[i].Color = (PlayerColorType)(Random_Pick(0,(int)(PCOLOR_LAST - 1))); + if (Percent_Chance(50)) { + house = HOUSE_GREECE; + } else { + house = HOUSE_USSR; + } +// house = (HousesType)Random_Pick((int)HOUSE_GOOD,(int)HOUSE_BAD); + item = new char [MPLAYER_NAME_MAX + 64]; + if (house != HOUSE_USSR && house != HOUSE_UKRAINE) { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_SOVIET)); + } + playerlist->Add_Item(item, + (WWPersons[i].Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[WWPersons[i].Color]); + } + //..................................................................... + // If this entry's name is the same as the previous, copy the color + // value from the previous entry. + //..................................................................... + else if (i > 0) { + WWPersons[i].Color = WWPersons[i-1].Color; + } + + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + +} // end of Start_WWChat + + +int Update_WWChat(void) +{ + int i; + int j; + + //------------------------------------------------------------------------ + // Do nothing if it's too soon. + //------------------------------------------------------------------------ + if (wwperson_timer > 0) { + return(0); + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + + //------------------------------------------------------------------------ + // Even after the timer's gone off, there's only a 1/8 chance we'll + // print a message. + //------------------------------------------------------------------------ + if (Percent_Chance(12)) { + return(0); + } + + //------------------------------------------------------------------------ + // Randomly select a message to "send"; ensure we pick a message that + // hasn't been recently displayed. + //------------------------------------------------------------------------ + j = sizeof(WWPersons) / sizeof(struct WWPerson); + i = Random_Pick (0, j - 1); + if ((TickCount - WWPersons[i].LastTime) < 1800 && + WWPersons[i].LastTime != 0) { + return(0); + } + + Session.Messages.Add_Message (WWPersons[i].Name, + 0, + WWPersons[i].Phrase, WWPersons[i].Color, + TPF_TEXT, -1); + WWPersons[i].LastTime = TickCount; + + return (1); + +} // end of Update_WWChat + + +#if(0) +/*****************************************************************************/ +void Start_Logging(void) +{ + FILE *fp; + static char *ColorNames[6] = { + "Yellow", + "Red", + "BlueGreen", + "Orange", + "Green", + "Blue", + }; + int i; + int id; + HousesType house; + char *housenames[] = { + "ALLIES", + "SOVIET", + "Neutral", + "Special", + "Multi1", + "Multi2", + "Multi3", + "Multi4", + }; + time_t t; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + /* + ** Print game header + */ + t = time(NULL); + fprintf (fp,"==============================================================\n"); + fprintf (fp,"Date: %s",ctime(&t)); + fprintf (fp,"Scenario: %s\n", + Session.Scenarios[Session.Options.ScenarioIndex].Description()); + fprintf (fp,"Total # players: %d\n",Session.NumPlayers); + fprintf (fp,"==============================================================\n"); + + /* + ** Print connection info + */ + + /* + ** Print player data + */ + fprintf(fp,"Local Player:\n"); + fprintf (fp," Name:%s\n", Session.Handle); + fprintf (fp," Color:%s\n", ColorNames[Session.ColorIdx]); + fprintf (fp," House:%s\n", housenames[Session.House]); + fprintf (fp,"\n"); + + for (i = 0; i < Ipx.Num_Connections(); i++) { + id = Ipx.Connection_ID(i); + house = (HouseClass)id; + fprintf(fp,"Connection %d:\n",i); + fprintf (fp," Name:%s\n", Ipx.Connection_Name(id)); + fprintf (fp," Color:%s\n", + ColorNames[HouseClass::As_Pointer(house)->RemapColor); + fprintf (fp," House:%s\n", housenames[house]); + fprintf (fp,"\n"); + } + + /* + ** Print game options + */ + fprintf(fp,"Game Options:\n"); + fprintf(fp," Bases:%d\n",Session.Options.Bases); + fprintf(fp," Credits:%d\n",Session.Options.Credits); + fprintf(fp," Tiberium:%d\n",Session.Options.Tiberium); + fprintf(fp," Crates:%d\n",Session.Options.Goodies); + fprintf(fp," AI Players:%d\n",Session.Options.AIPlayers); + fprintf(fp," Build Level:%d\n",BuildLevel); + fprintf(fp," Unit Count:%d\n",Session.Options.UnitCount); + fprintf(fp," Seed:%d\n",Seed); + fprintf (fp,"\n"); + + fclose(fp); + +} /* end of Start_Logging */ + + +void Log_Message(char *msg) +{ + FILE *fp; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + fprintf(fp,"%s (Frame:%d)\n",msg,Frame); + fclose(fp); + flushall(); + +} /* end of Log_Message */ +#endif + + + + + +#ifndef WOLAPI_INTEGRATION // Rest of file ifdeffed out. + + + + + +extern bool Spawn_WChat(bool can_launch); + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³.³ ³ Green Acres ³.³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Brown Sewers ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + + int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3)*RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i; + char *item; + int tabs[] = {77}; // tabs for player list box + int optiontabs[] = {8}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + JoinEventType whahoppa; // event generated by received packets + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int width; + int height; + + bool player_joined = false; + CountDownTimerClass join_timer; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Rule.IsTGrowth = Special.IsTGrowth; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Special.IsTSpread; + transmit = 0; + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ +// randomize(); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Players.Count() > 1) { + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined) { + player_joined = true; + join_timer.Set (3*60, true); + break; + } else { + if (join_timer.Time()) break; + } + + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = MAX((int)Ipx.Global_Response_Time() * 2, 60*2); + while ((int)TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Solo || Session.Players.Count() > 1 || Session.Options.Ghosts) { + rc = TRUE; + process = FALSE; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + Sound_Effect(VOC_SYS_ERROR); + } + //if (input==(BUTTON_LOAD | KN_BUTTON)) + // load_game = 1; + break; + } + break; + + } + + //..................................................................... + // Process incoming packets + //..................................................................... + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + transmit = 1; + } + else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + // ajw - I don't understand why this check is done here and not later. + if ( !file.Is_Available() ) { + + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed and + ** the file name matches a counterstrike map then force the CD + ** to be there. + ** + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ( Expansion_CS_Present() || Expansion_AM_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName) && index!=2 && index!=3) { + needcd = true; + RequiredCD = 2; + } + if (Is_Mission_Aftermath(Session.ScenarioFileName) && index!=3) { + needcd = true; + RequiredCD = 3; + } + if (needcd) { +#else + if ( Expansion_CS_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + int scen_num; + sscanf ( Session.ScenarioFileName, "SCM%02d", &scen_num ); + + int current_drive = CCFileClass::Get_CD_Drive(); + if ( scen_num>24 && Get_CD_Index(current_drive, 1*60) != 2){ + RequiredCD = 2; +#endif + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + /* + ** See if that file is available now. Its fatal if it isnt. + */ + if ( !file.Is_Available() ) { + + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Clear the Players vector if we're not starting a game. + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + return (false); + } + } + + + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + //Session.GPacket.ScenarioInfo.IsGhosties = Session.Options.Ghosts; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = MAX( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = MAX( ((int)Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); + + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*30; // Wait for 30 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + + WWDebugString ("RA95 - Exited response wait loop.\n"); + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 +} /* end of Net_Fake_New_Dialog */ + + + + + + + + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³.³ ³ Peter Parker GDI ³.³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_Join_Dialog(void) +{ + +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_gamelist_w = 160 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high +// int d_gamelist_x = d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_x = 500*RESFACTOR; //d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_y = 50 + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = 50+ d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_GAMELIST = 100, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {77 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + int parms_received = 0; // 1 = game options received + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + int ready_to_go = 0; + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + int width; + int height; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, 0, 0, d_txt6_h, 0, + 0, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + else { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + process = false; + rc = -1; + } + break; + + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + default: + if (joinstate == JOIN_NOTHING && gamelist.Count() >1 ) { + gamelist.Set_Selected_Index(1); //lastclick_idx); + game_index = 1; + strcpy (Session.Handle,namebuf); + //Session.House = (HousesType)housebtn.Current_Index(); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) load_game = 1; + + bool ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + CCFileClass testfile ( Session.ScenarioFileName ); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** See if that scenario is available now. Its pretty fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + } + } + } + + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided its not an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + + rc = 0; + process = false; + } + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + else if (joinstate==JOIN_CONFIRMED) { + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 12]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); +#if (0) + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (0,0, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, 0, 0, 1, + 20, MAX_MESSAGE_LENGTH - 5); + Session.Messages.Add_Edit(Session.ColorIdx, TPF_TEXT | TPF_BRIGHT_COLOR, NULL, '_'); +#endif //(0) + } + + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + else if (joinstate==JOIN_REJECTED) { + Sound_Effect(VOC_SYS_ERROR); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + + } + } // end of state change + + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + else if (event == EV_GAME_OPTIONS) { + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } + + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + else if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + } + +#if (0) + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + if (ready_to_go && Session.Players.Count() == 2) { + rc = 0; + process = false; + } +#endif //(0) + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + else { + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); +///* +//** Hack to fake a scenario name as if it had been sent over the connection. +//*/ +//sprintf(Scen.ScenarioName, "SCM%02dEA.INI", Session.Options.ScenarioIndex+1); + +// Scen.Scenario = Session.Options.ScenarioIndex; + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = MAX((int)Ipx.Global_Response_Time() * 2, 60*2); + starttime = TickCount; + while (TickCount - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ + //Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + // Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 + +} /* end of Net_Join_Dialog */ + + + + + + + + + + +bool Server_Remote_Connect(void) +{ + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + if (!Net_Fake_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + return (false); + } + + Session.NetOpen = 0; + Session.NetStealth = stealth; + Session.Write_MultiPlayer_Settings (); + return (true); + +} /* end of Server_Remote_Connect */ + + + + + + + +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + rc = Net_Fake_Join_Dialog(); + Session.Write_MultiPlayer_Settings (); + + Session.NetStealth = stealth; + Session.NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } + +} /* end of Client_Remote_Connect */ + + + +/*************************** end of netdlg.cpp *****************************/ + +#endif //#ifndef WOLAPI_INTEGRATION +#endif \ No newline at end of file diff --git a/REDALERT/NOSEQCON.CPP b/REDALERT/NOSEQCON.CPP new file mode 100644 index 000000000..a414bbd5f --- /dev/null +++ b/REDALERT/NOSEQCON.CPP @@ -0,0 +1,688 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.cpv 1.10 01 Mar 1996 18:08:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * NonSequencedConnClass::Receive_Packet -- adds packet to receive queue * + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "NOSEQCON.H" + + +/*************************************************************************** + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::NonSequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen); +} + + +/*************************************************************************** + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::~NonSequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NonSequencedConnClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); +} + + +/*************************************************************************** + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { +// Smart_Printf( "Packet ack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendAck++; + } else { +// Smart_Printf( "Packet noack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendNoAck++; + } + return(true); + } else { +// Smart_Printf( "Packet not Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + return(false); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { +// Smart_Printf( "Bad Magic Number\n" ); + return(false); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Get_Send(i); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Received ACK for %d \n", packet->PacketID ); + send_entry->IsACK = 1; + break; + } + } + } + +//{ +// if (i == Queue->Num_Send() ) { +// Smart_Printf( "Received bad ACK for %d \n", packet->PacketID ); +// } +//} + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*--------------------------------------------------------------------- + If there's only one slot left, don't tie up the queue with this packet + ---------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { +// Smart_Printf( "Only one slot left don't tie up with DATA NOACK packet %d \n", packet->PacketID ); + return(false); + } + + /*--------------------------------------------------------------------- + Error if we can't queue the packet + ---------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "Can't Queue the packet %d \n", packet->PacketID ); + return(false); + } + +// Smart_Printf( "Queued DATA NOACK for %d \n", packet->PacketID ); + NumRecNoAck++; + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Looking at ID %d, LastSeqID=%d \n", packet->PacketID, LastSeqID ); + /*.................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + ....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { +// Smart_Printf( "Older than oldest\n" ); + save_packet = 0; + } + /*.................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + ....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { +// Smart_Printf( "It's a resend\n" ); + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*--------------------------------------------------------------------- + Queue the packet & update our LastSeqID value. + ---------------------------------------------------------------------*/ + if (save_packet) { + /*------------------------------------------------------------------ + If there's only one slot left, make sure we only put a packet in it if + this packet will let us increment our LastSeqID; otherwise, we'll get + stuck, forever unable to increment LastSeqID. + ------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { +// Smart_Printf( "One slot left not what we looking for max=%d,num=%d \n", +// Queue->Max_Receive(), Queue->Num_Receive() ); + return(0); + } + } + + /*------------------------------------------------------------------ + If we can't queue the packet, return; don't send an ACK. + ------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "unable to queue packet\n" ); + return(0); + } + + NumRecAck++; + + /*------------------------------------------------------------------ + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ------------------------------------------------------------------*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................ + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*--------------------------------------------------------------------- + Send an ACK, regardless of whether this was a resend or not. + ---------------------------------------------------------------------*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; +// Smart_Printf( "Sending ACK for %d \n", packet->PacketID ); + Send ((char *)&ackpacket, sizeof(CommHeaderType)); + + return(true); + + } else { +// Smart_Printf( "invalid packet type %d \n", packet->Code ); + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + } + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ------------------------- Get this queue entry ------------------------ + */ + send_entry = Queue->Get_Send(i); + /* + ---------------- If ACK has been received, unqueue it ----------------- + */ + if (send_entry->IsACK) { + /* + ................ Update this queue's response time ................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + /* + ....................... unqueue the packet ......................... + */ + Queue->UnQueue_Send(NULL,NULL,i); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ +#if(0) +{ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (send_entry->SendCount) { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Resending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Resending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } else { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Sending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Sending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } +} +#endif + Send (send_entry->Buffer, send_entry->BufLen); + + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { +// Smart_Printf( "Max Retries!!! %d !!! \n", MaxRetries ); + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { +// Smart_Printf( "Timed out!!! Time %d, Timeout %d, buflen %d !!! \n", +// (send_entry->LastTime - send_entry->FirstTime), Timeout, +// send_entry->BufLen ); + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { +// Smart_Printf( "Connection going bad!!! \n" ); + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } else { + if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } + } + } + } + + return(true); +} + + diff --git a/REDALERT/NOSEQCON.H b/REDALERT/NOSEQCON.H new file mode 100644 index 000000000..4a1f00ec1 --- /dev/null +++ b/REDALERT/NOSEQCON.H @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.h_v 1.13 01 Mar 1996 17:32:42 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NOSEQCON.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Non-Sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * however, the performance is better than the Sequenced approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NONSEQCONN_H +#define NONSEQCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "combuf.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class NonSequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NonSequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~NonSequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; +}; + +#endif + + diff --git a/REDALERT/NULLCONN.CPP b/REDALERT/NULLCONN.CPP new file mode 100644 index 000000000..1f63610b8 --- /dev/null +++ b/REDALERT/NULLCONN.CPP @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLCONN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : April 20, 1995 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemConnClass::NullModemConnClass -- class constructor * + * NullModemConnClass::~NullModemConnClass -- class destructor * + * NullModemConnClass::Init -- hardware-dependent initialization * + * NullModemConnClass::Send -- hardware-dependent packet sending * + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#endif //WIN32 +#include +//#include +#include "nullconn.h" + + +/*************************************************************************** + * NullModemConnClass::NullModemConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # send queue entries * + * numreceive desired # send receive entries * + * maxlen max length of application's packets * + * magicnum application-defined magic # for the packets * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::NullModemConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 60, // Retry Delta Time + -1, // Max Retries (-1 means ignore this timeout parameter) + 1200) // Timeout: 20 seconds +{ + /*------------------------------------------------------------------------ + Pre-set the port value to NULL, so Send won't send until we've been Init'd + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + /*------------------------------------------------------------------------ + Allocate the Send Buffer; the parent constructor has set MaxPacketLen, + so we can use it in our computation. + ------------------------------------------------------------------------*/ + SendBuf = new char [ Actual_Max_Packet() ]; + +} /* end of NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::~NullModemConnClass -- class destructor * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::~NullModemConnClass () +{ + delete [] SendBuf; + +} /* end of ~NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::Init -- hardware-dependent initialization * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +#ifdef WIN32 +void NullModemConnClass::Init (HANDLE port_handle) +{ + ConnectionClass::Init(); + PortHandle = port_handle; +} +#else //WIN32 +void NullModemConnClass::Init (PORT *port) +{ + ConnectionClass::Init(); + Port = port; +} /* end of Init */ +#endif //WIN32 + +/*************************************************************************** + * NullModemConnClass::Send -- hardware-dependent packet sending * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 1 = OK, 0 = error * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Send (char *buf, int buflen, void *, int ) +{ + return 0; +#if (0)//PG + int *ibuf; + SerialHeaderType *header; + unsigned long sendlen; + + /*------------------------------------------------------------------------ + Error if we haven't been properly initialized + ------------------------------------------------------------------------*/ +#ifdef WIN32 + if ( PortHandle == NULL ) return(false); + +#else //WIN32 + int status; + if ( Port == NULL ) { + return(0); + } +#endif //WIN32 + + /*------------------------------------------------------------------------ + Package the data into the Send Buffer + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *) SendBuf; + header->MagicNumber = PACKET_SERIAL_START; + header->Length = (short) buflen; + header->MagicNumber2 = PACKET_SERIAL_VERIFY; + + sendlen = sizeof( SerialHeaderType ); + memcpy (SendBuf + sendlen, buf, buflen); + sendlen += buflen; + ibuf = (int *)(SendBuf + sendlen); + *ibuf = Compute_CRC( buf, buflen ); + sendlen += sizeof( int ); + + *(SendBuf + sendlen) = '\r'; + sendlen += 1; + + /*------------------------------------------------------------------------ + Send the data + ------------------------------------------------------------------------*/ +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); + return (true); + +#else //WIN32 + status = WriteBuffer( Port, SendBuf, sendlen ); + if ( status == ASSUCCESS ) { + return(1); + } + else { + return(0); + } +#endif //WIN32 +#endif//PG +} /* end of Send */ + + +/*************************************************************************** + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * * + * INPUT: * + * buf buffer to compute CRC for * + * buflen length of buffer in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Compute_CRC (char *buf, int buflen) +{ + unsigned int sum, hibit; + + sum = 0; + for (int i = 0; i < buflen; i++) { + if ( sum & 0x80000000 ) { // check hi bit to rotate into low bit + hibit = 1; + } + else { + hibit = 0; + } + + sum <<= 1; + sum += (hibit + (unsigned char)buf[i]); + } + + return((int)sum); + +} /* end of Compute_CRC */ + + +/*************************************************************************** + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * number of bytes used for communications only. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/20/1995 DRD : Created. * + *=========================================================================*/ +int NullModemConnClass::Packet_Overhead_Size ( void ) +{ + /*------------------------------------------------------------------------ + short for Null Modem Magic Number + short for Null Modem length of packet + int for Null Modem CRC check + CommHeaderType for Queued packets + ------------------------------------------------------------------------*/ + return( (PACKET_SERIAL_OVERHEAD_SIZE + sizeof(CommHeaderType)) ); + +} /* end of Packet_Overhead_Size */ + +/************************** end of nullconn.cpp ****************************/ diff --git a/REDALERT/NULLCONN.H b/REDALERT/NULLCONN.H new file mode 100644 index 000000000..f21873d6a --- /dev/null +++ b/REDALERT/NULLCONN.H @@ -0,0 +1,147 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLCONN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for a NULL-Modem connection. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the non-sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * ConnectionClass. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLCONN_H +#define NULLCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +//#include "commlib.h" + +/* +********************************** Defines ********************************** +*/ +#define PACKET_SERIAL_START 0xDABD +#define PACKET_SERIAL_VERIFY 0xDEAF + +#define PACKET_SERIAL_OVERHEAD_SIZE (sizeof( SerialHeaderType ) + sizeof( SerialCRCType )) + +typedef struct { + unsigned short MagicNumber; + unsigned short Length; + unsigned short MagicNumber2; +} SerialHeaderType; + +typedef struct { + int SerialCRC; +} SerialCRCType; + + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NullModemConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum); + virtual ~NullModemConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ +#ifdef WIN32 + void Init (HANDLE port_handle); +#else //WIN32 + void Init (PORT *port); +#endif //WIN32 + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned long Actual_Max_Packet (void) { return (MaxPacketLen + + (sizeof(SerialHeaderType)) + sizeof(int) + sizeof (char)); } + + /*..................................................................... + This routine computes a CRC value for the given buffer. + .....................................................................*/ + static int Compute_CRC(char *buf, int buflen); + + /*..................................................................... + This routine returns the number of bytes extra added the packet + for communication. + .....................................................................*/ + static int Packet_Overhead_Size( void ); + + /* + --------------------------- Private Interface ---------------------------- + */ + protected: + /*..................................................................... + This routine actually performs a hardware-dependent data send. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); +#ifdef WIN32 + /* + ** This is the winsoze port handle + */ + HANDLE PortHandle; +#else //WIN32 + /*..................................................................... + This is the PORT value used by the GreenLeaf calls. + .....................................................................*/ + PORT *Port; +#endif //WIN32 + + /*..................................................................... + This buffer is a staging area for data sent out; it includes the + packet sent by the parent class (which includes the application's + packet, plus the CommHeaderType header), plus: + - 2-byte buffer start ID + - 2-byte length + - 4-byte CRC value (at the end of the buffer) + This is the actual packet that gets sent across the serial line. + .....................................................................*/ + char *SendBuf; +}; + +#endif + +/************************** end of nullconn.h ******************************/ + diff --git a/REDALERT/NULLDLG.CPP b/REDALERT/NULLDLG.CPP new file mode 100644 index 000000000..acfb90110 --- /dev/null +++ b/REDALERT/NULLDLG.CPP @@ -0,0 +1,7835 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/NULLDLG.CPP 14 3/17/97 1:05a Steve_tall $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : Jan. 21, 1997 [V.Grippi] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Destroy_Null_Connection -- destroys the given connection * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * Init_Null_Modem -- Initializes Null Modem communications * + * Init_String_Compare -- for qsort * + * Phone_Compare -- for qsort * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Reconnect_Null_Modem -- allows user to reconnect * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +//PG +GameType Select_Serial_Dialog(void) { return GAME_NORMAL; } + + + + + +#if (0)//PG +#ifdef FIXIT_RANDOM_GAME +#include "time.h" +#endif +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +ModemRegistryEntryClass *ModemRegistry = NULL; //Ptr to modem registry data +#endif //WIN32 +#include "COORDA.h" +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath( char* file_name ); +#endif + +//#include "WolDebug.h" + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +//extern char const *ForMisStr[]; +extern char const *EngMisStr[]; + + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +PlayerColorType TheirColor; +HousesType TheirHouse; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#define PCOLOR_BROWN PCOLOR_GREY + + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 0, 8, 1, + settings->HardwareFlowControl ) ) { + return (true); + } else { + return (false); + } + +#else //WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 'N', 8, 1, + settings->HardwareFlowControl) ) { + NullModem.Change_IRQ_Priority( settings->IRQ ); + + return(true); + } else { + return(false); + } +#endif //WIN32 +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!Session.Play) { + if (Session.Type == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + NullModem.Change_IRQ_Priority( 0 ); // reset priority of interrupts + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!Session.Play) { + /* + ** Send a sign-off packet + */ + memset (&event, 0, sizeof(EventClass)); + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount; + while ( (TickCount - starttime) < 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + Load_Title_Page(true); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20 * RESFACTOR, y + 25 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + +#ifdef WIN32 + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()) {} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); +#endif //WIN32 + + + /* + ** Check for a packet. If we detect one, the other system has already been + ** started. Wait 1/2 sec for him to receive my ACK, then exit with success. + ** Note: The initial time must be a little longer than the resend delay. + ** Just in case we just missed the packet. + */ + starttime = TickCount; + while ( TickCount - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /* + ** Send a packet across. As long as Num_Send() is non-zero, the other system + ** hasn't received it yet. + */ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.ScenarioInfo.Seed = TickCount; + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while (TickCount - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + break; + } + } + } + } + + starttime = TickCount; + + /* + ** Main Processing Loop + */ + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = 0; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (Session.ModemType) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + Show_Mouse(); + + /* + ** Main Processing Loop + */ + starttime = lastmsgtime = TickCount; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + + /* + ** Resend our message if it's time + */ + if (TickCount - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount; + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /* + ** Check for an incoming message + */ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + if (ReceivePacket.Command == SERIAL_CONNECT) { + + // are we getting our own packets back?? + + if (ReceivePacket.ID == Session.ColorIdx) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /* + ** OK, we got our message; now we have to make certain the other + ** guy gets his, so send him one with an ACK required. + */ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player being * + * "destroyed". * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + + if ( Session.NumPlayers == 1 ) { + return; + } + + /* + ** Do nothing if the house isn't human. + */ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /* + ** Create a message to display to the user + */ + txt[0] = '\0'; + switch (error) { + case 1: + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + break; + + case 0: + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + break; + + case -1: + NullModem.Delete_Connection(); + break; + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + /* + ** Remove this player from the Players vector + */ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /* + ** If we're the last player left, tell the user. + */ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 160 * RESFACTOR; // dialog width + int d_dialog_h = 94 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = 80*RESFACTOR; +// int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_dial_w = 90 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 * RESFACTOR; + int d_answer_h = 9 * RESFACTOR; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 * RESFACTOR; + int d_nullmodem_h = 9 * RESFACTOR; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 * RESFACTOR; + int d_settings_h = 9 * RESFACTOR; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /* + ** Button Enumerations + */ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*RESFACTOR}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, TPF_BUTTON, d_answer_x, d_answer_y, d_answer_w, d_answer_h); + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, TPF_BUTTON, d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, TPF_BUTTON, d_settings_x, d_settings_y, d_settings_w, d_settings_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + if (Session.SerialDefaults.Port == 0 || + Session.SerialDefaults.IRQ == -1 || + Session.SerialDefaults.Baud == -1) { + selectsettings = true; + + } else if ( NullModem.Detect_Port( &Session.SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + + /* + ** Create the list + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Main Processing Loop + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ** Refresh the backdrop + */ + Load_Title_Page(true); + /* + ** Draw the background + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + /* + ** Draw the labels + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + commands->Draw_All(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Remote-connect + */ + } else if ( Phone_Dialog() ) { + if (Session.PhoneBook[Session.CurPhoneIdx]->Settings.Port == 0) { + settings = &Session.SerialDefaults; + } else { + settings = &(Session.PhoneBook[Session.CurPhoneIdx]->Settings); + } +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + Session.CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, Session.PhoneBook[ Session.CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + Session.ModemType = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &Session.SerialDefaults; +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + Session.ModemType = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Otherwise, remote-connect; save values if we're recording + */ + } else if ( Init_Null_Modem( &Session.SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + Session.ModemType = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + Session.ModemType = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + WWMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &Session.SerialDefaults ) ) { + Session.Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (Session.SerialDefaults.Port != 0 && + Session.SerialDefaults.IRQ != -1 && + Session.SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &Session.SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + + return( retval ); +} + + + + + + +#ifdef WIN32 +/*********************************************************************************************** + * Advanced_Modem_Settings -- Allows to user to set additional modem settings * + * * + * * + * * + * INPUT: current settings * + * * + * OUTPUT: modified settings * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/16/96 2:29PM ST : Created * + *=============================================================================================*/ +Advanced_Modem_Settings (SerialSettingsType *settings) +{ + + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +#if (FRENCH | GERMAN) + int d_dialog_w = 440; // dialog width +#else + int d_dialog_w = 340; // dialog width +#endif + + int d_dialog_h = 200; // dialog height + int d_dialog_x = 320 - d_dialog_w/2; // dialog x-coord + int d_dialog_y = 200 - d_dialog_h/ 4; // dialog y-coord + + + int d_compression_w = 50; + int d_compression_h = 18; + int d_compression_x = d_dialog_x + d_dialog_w/2 +40; + int d_compression_y = d_dialog_y + 40; + + int d_errorcorrection_w = 50; + int d_errorcorrection_h = 18; + int d_errorcorrection_x = d_dialog_x + d_dialog_w/2 +40; + int d_errorcorrection_y = d_dialog_y + 65; + + int d_hardwareflowcontrol_w = 50; + int d_hardwareflowcontrol_h = 18; + int d_hardwareflowcontrol_x = d_dialog_x + d_dialog_w/2 +40; + int d_hardwareflowcontrol_y = d_dialog_y + 90; + + int d_default_w = 100; + int d_default_h = 18; + int d_default_x = d_dialog_x + d_dialog_w / 2 - d_default_w / 2; + int d_default_y = d_dialog_y + d_dialog_h - 70; + + int d_ok_w = 100; + int d_ok_h = 18; + int d_ok_x = d_dialog_x + d_dialog_w/2 - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - 40; + + enum { + BUTTON_COMPRESSION = 100, + BUTTON_ERROR_CORRECTION, + BUTTON_HARDWARE_FLOW_CONTROL, + BUTTON_DEFAULT, + BUTTON_OK, + }; + + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND, + } RedrawType; + + /* + ** Yes/No strings + */ + char compress_text [16]; + char correction_text [16]; + char flowcontrol_text[16]; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Initialise the button text + */ + strcpy (compress_text, settings->Compression ? Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + + /* + ** Create the buttons + */ + TextButtonClass compressionbutton(BUTTON_COMPRESSION, compress_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_compression_x, d_compression_y, d_compression_w, d_compression_h); + + TextButtonClass errorcorrectionbutton(BUTTON_ERROR_CORRECTION, correction_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_errorcorrection_x, d_errorcorrection_y, d_errorcorrection_w, d_errorcorrection_h); + + TextButtonClass hardwareflowcontrolbutton(BUTTON_HARDWARE_FLOW_CONTROL, flowcontrol_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_hardwareflowcontrol_x, d_hardwareflowcontrol_y, d_hardwareflowcontrol_w, d_hardwareflowcontrol_h); + + TextButtonClass defaultbutton(BUTTON_DEFAULT, TXT_DEFAULT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass okbutton(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + + /* + ** Misc. variables. + */ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + GadgetClass *commands; // button list + + + commands = &okbutton; + defaultbutton.Add_Tail(*commands); + compressionbutton.Add_Tail(*commands); + errorcorrectionbutton.Add_Tail(*commands); + hardwareflowcontrolbutton.Add_Tail(*commands); + + + /* + ** Main process loop + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_MODEM_INITIALISATION, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_DATA_COMPRESSION, + d_compression_x - 26, d_compression_y + 2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_ERROR_CORRECTION, + d_errorcorrection_x - 26, d_errorcorrection_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_HARDWARE_FLOW_CONTROL, + d_hardwareflowcontrol_x -26, d_hardwareflowcontrol_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + compressionbutton.Flag_To_Redraw(); + errorcorrectionbutton.Flag_To_Redraw(); + hardwareflowcontrolbutton.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_COMPRESSION | KN_BUTTON): + settings->Compression = settings->Compression ^ 1; + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_ERROR_CORRECTION | KN_BUTTON): + settings->ErrorCorrection = settings->ErrorCorrection ^ 1; + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_HARDWARE_FLOW_CONTROL | KN_BUTTON): + settings->HardwareFlowControl = settings->HardwareFlowControl ^ 1; + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_DEFAULT | KN_BUTTON): + settings->Compression = false; + settings->ErrorCorrection = false; + settings->HardwareFlowControl = true; + + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } +} +#endif //WIN32 + + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 5 * RESFACTOR; // margin width/height + + int d_portlist_w = 80 * RESFACTOR + 80*(RESFACTOR-1); //Port list wider in hires + int d_portlist_h = 33 * RESFACTOR; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); +#ifdef WIN32 + d_portlist_x = 0x45; +#endif + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 * RESFACTOR; + +#ifdef WIN32 + int d_port_w = d_portlist_w; + int d_port_x = 0x45; +#else + int d_port_w = (5 * 6 * RESFACTOR) + 3 * RESFACTOR; + +// int d_port_x = d_portlist_x + 29 * RESFACTOR; +#ifdef FRENCH //VG2 + int d_port_x = (d_portlist_x + 29 * RESFACTOR) + 5; +#else + int d_port_x = d_portlist_x + 29 * RESFACTOR; +#endif + +#endif //WIN32 + int d_port_h = 9 * RESFACTOR; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_irqlist_w = 80 * RESFACTOR; + int d_irqlist_h = 33 * RESFACTOR; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_irq_h = 9 * RESFACTOR; + int d_irq_x = d_irqlist_x + 25 * RESFACTOR; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 * RESFACTOR; + int d_baudlist_h = 33 * RESFACTOR; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); +#ifdef WIN32 + d_baudlist_x -= 32; +#endif + int d_baudlist_y = d_irqlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_baud_h = 9 * RESFACTOR; + int d_baud_x = d_baudlist_x + 29 * RESFACTOR; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 8 * RESFACTOR + 3 * RESFACTOR; + int d_initstrlist_h = 21 * RESFACTOR; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_portlist_y + d_portlist_h + ((d_margin + d_txt6_h) * 2) + d_margin + 4; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_initstr_h = 9 * RESFACTOR; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + + int d_add_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_add_x = d_dialog_cx - (d_add_w / 2); +#else + int d_add_x = (d_dialog_cx - (d_add_w / 2)) + 30; +#endif + int d_add_h = 9 * RESFACTOR; + int d_add_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_delete_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_delete_x = (d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2)) + 10; +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_h = 9 * RESFACTOR; + int d_delete_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstrlist_h = 27 * RESFACTOR; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstr_h = 9 * RESFACTOR; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 * RESFACTOR; + int d_tone_h = 9 * RESFACTOR; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 * RESFACTOR; + int d_pulse_h = 9 * RESFACTOR; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + +#ifdef FRENCH + int d_save_w = 80 * RESFACTOR; +#else + int d_save_w = 40 * RESFACTOR; +#endif + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_x + (d_dialog_w / 5) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 4*RESFACTOR; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 4) / 5) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 4*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_advanced_w = 50*RESFACTOR; +#else + int d_advanced_w = 40*RESFACTOR; +#endif + int d_advanced_h = 9*RESFACTOR; + int d_advanced_x = d_dialog_x + ((d_dialog_w) / 2) - (d_advanced_w / 2); + int d_advanced_y = d_dialog_y + d_dialog_h - d_advanced_h - d_margin - 4 *RESFACTOR; + + /* + ** Button Enumerations + */ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_ADVANCED, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[4] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8", + }; + + static char custom_port[10 + MODEM_NAME_MAX] = {"CUSTOM - ????"}; + + + +#ifndef WIN32 // No IRQ dialog in Win version + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; + +#endif //WIN32 + +#ifdef WIN32 + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char modemnames[10][MODEM_NAME_MAX]; + +#else //WIN32 + static char *baudname[5] = { + "9600", + "14400", + "19200", + "28800", + "38400" + }; +#endif //WIN32 + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) + int port_custom_index = 4; //index of custom entry in port list + int irq_index = 1; // index of currently-selected irq (default = 3) + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + bool firsttime = true; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, portbuf, PORTBUF_MAX, + TPF_TEXT, d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#ifndef WIN32 + EditClass irq_edt (BUTTON_IRQ, irqbuf, IRQBUF_MAX, TPF_TEXT, d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#endif //WIN32 + + EditClass baud_edt (BUTTON_BAUD, baudbuf, BAUDBUF_MAX, TPF_TEXT, d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + ListClass baudlist(BUTTON_BAUDLIST, d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + EditClass initstr_edt (BUTTON_INITSTR, initstrbuf, INITSTRBUF_MAX, TPF_TEXT, d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + ListClass initstrlist(BUTTON_INITSTRLIST, d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + EditClass cwaitstr_edt (BUTTON_CWAITSTR, cwaitstrbuf, CWAITSTRBUF_MAX, TPF_TEXT, d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, TPF_BUTTON, d_tone_x, d_tone_y, d_tone_w, d_tone_h); + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, TPF_BUTTON, d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#ifdef WIN32 + TextButtonClass advancedbutton(BUTTON_ADVANCED, TXT_ADVANCED, TPF_BUTTON, d_advanced_x, d_advanced_y, d_advanced_w, d_advanced_h); +#endif //WIN32 + /* + ** Various Inits + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { +#ifdef WIN32 + tempsettings.Baud = 19200; +#else //WIN32 + tempsettings.Baud = 9600; +#endif //WIN32 + } + + + /* + ** Set the current indices + */ +#ifndef WIN32 + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //WIN32 + +#ifdef WIN32 + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else if (tempsettings.Baud == 19200) { + baud_index = 1; + } else if (tempsettings.Baud == 28800) { + baud_index = 2; + } else if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } +#else //WIN32 + if (tempsettings.Baud == 9600) { + baud_index = 0; + } else if (tempsettings.Baud == 14400) { + baud_index = 1; + } else if (tempsettings.Baud == 19200) { + baud_index = 2; + } else if (tempsettings.Baud == 28800) { + baud_index = 3; + } else { + baud_index = 4; + } +#endif //WIN32 + sprintf (baudbuf, "%d", tempsettings.Baud); + + /* + ** Set up the port list box & edit box + */ + for (i = 0; i < 4; i++) { + portlist.Add_Item( portname[ i ] ); + } + +#ifdef WIN32 + /* + ** Loop through the first 10 possible modem entries in the registry. Frankly, its just + ** tough luck if the user has more than 10 modems attached! + */ + if (ModemRegistry) { + delete ModemRegistry; + } + int modems_found = 0; + for (i=0 ; i<10 ; i++) { + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()) { + strncpy (modemnames[modems_found], ModemRegistry->Get_Modem_Name(), MODEM_NAME_MAX); + portlist.Add_Item( modemnames [modems_found++] ); + port_custom_index ++; + } + delete ModemRegistry; + } + ModemRegistry = NULL; + +#endif //WIN32 + + portlist.Add_Item ( custom_port ); + + + /* + ** Work out the current port index + */ + port_index = -1; +#ifdef WIN32 + if (tempsettings.ModemName[0]) { + for ( i=0 ; i= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Dialog & Field labels + */ + Draw_Caption(TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PORT_COLON, d_port_x - 3 * RESFACTOR, d_port_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#ifndef WIN32 + Fancy_Text_Print(TXT_IRQ_COLON, d_irq_x - 3 * RESFACTOR, d_irq_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#endif //WIN32 + Fancy_Text_Print(TXT_BAUD_COLON, d_baud_x - 3 * RESFACTOR, d_baud_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + + Fancy_Text_Print(TXT_INIT_STRING, d_initstr_x, d_initstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CWAIT_STRING, d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + /* + ** Redraw buttons + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif //WIN32 + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); +#ifdef WIN32 + advancedbutton.Flag_To_Redraw(); +#endif //WIN32 + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + if ( firsttime ) { +// port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ** Process input + */ + switch (input) { + +#ifdef WIN32 + case (BUTTON_ADVANCED | KN_BUTTON): + Advanced_Modem_Settings (&tempsettings); + display = REDRAW_ALL; + break; +#endif //WIN32 + + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //WIN32 + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + + } else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: +#ifdef WIN32 + if (portbuf[3] <= '9' && portbuf[3] >'0') { + portbuf[4] = 0; + port_index = port_custom_index; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + break; + } +#endif //WIN32 + WWMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } +#ifndef WIN32 + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //WIN32 + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + port_edt.Clear_Focus(); + + // auto select the irq for port +#ifndef WIN32 + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //WIN32 + } else { + if (port_index == port_custom_index) { + /* + ** This is the custom entry + */ + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + } else { + /* + ** Must be a modem name entry so just copy iy + */ + strncpy (portbuf, item, PORTBUF_MAX); + } + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + } else if (port_index < port_custom_index) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + +#ifndef WIN32 + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; +#endif //WIN32 + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( Session.InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /* + ** Add a new InitString entry + */ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + Session.InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + if (item == Session.InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, Session.InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( Session.InitStrings.Count() && initstr_index != -1) { + Session.InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + tempsettings.ModemName[0] = 0; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + tempsettings.ModemName[0] = 0; + break; + + default: + if (port_index == port_custom_index) { +#ifdef WIN32 + strncpy ( tempsettings.ModemName, portbuf, MODEM_NAME_MAX ); + tempsettings.Port = 1; +#else //WIN32 + sscanf( portbuf, "%x", &tempsettings.Port ); + tempsettings.ModemName[0] = 0; +#endif //WIN32 + } else { + /* + ** Must be a modem name index + */ + strcpy (tempsettings.ModemName, portlist.Current_Item()); + tempsettings.Port = 1; + } + break; + } + + +#ifndef WIN32 + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //WIN32 + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = Session.CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + + } else if (dpstatus == PORT_INVALID) { +#ifdef WIN32 + WWMessageBox().Process( TXT_UNABLE_TO_OPEN_PORT ); +#else //WIN32 + WWMessageBox().Process( TXT_INVALID_SETTINGS ); +#endif //WIN32 + firsttime = true; + display = REDRAW_ALL; + + } else if (dpstatus == PORT_IRQ_INUSE) { + WWMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = true; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&Session.InitStrings[0]), Session.InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, Session.InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else if (curidx >= list->Count() ) { + curidx = 0; + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, Session.InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. + * 01/21/97 V.Grippi added check for CS before sending scenario file * + *=============================================================================================*/ +int Com_Scenario_Dialog(bool skirmish) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 7 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + 1 * RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h*/; + + int d_scenariolist_w = 162 * RESFACTOR; + int d_scenariolist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + + if (skirmish) { + d_scenariolist_h *= 2; + } + + + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - 5*RESFACTOR; + int d_scenariolist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR; + + if (skirmish) { + d_scenariolist_x = d_dialog_x + (d_dialog_w-d_scenariolist_w)/2; + } + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_count_y = d_playerlist_y + d_playerlist_h + (d_margin1 * 2) - 2 * RESFACTOR; + + if (skirmish) { + d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + } + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_dialog_x + d_dialog_w - 149 * RESFACTOR; + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (8 * d_txt6_h) + 3 * RESFACTOR; // 4 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + 2*RESFACTOR; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 45 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - RESFACTOR*6; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - RESFACTOR*6; + + int d_load_w = 45 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - RESFACTOR*6; + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_PLAYERLIST, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + BUTTON_DIFFICULTY, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int playertabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + bool transmit; // 1 = re-transmit new game options + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + unsigned long version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static bool first_time = true; + bool oppscorescreen = false; + bool gameoptions = Session.Type == GAME_SKIRMISH; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + CCFileClass loadfile ("SAVEGAME.NET"); + bool load_game = false; // 1 = load a saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + bool messages_have_focus = true; // Gadget focus starts on the message system + + Set_Logic_Page(SeenBuff); + + CDTimerClass kludge_timer; // Timer to allow a wait after client joins + // game before game can start + bool ok_button_added = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass * commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, d_load_w, d_load_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + SliderClass difficulty(BUTTON_DIFFICULTY, d_name_x, optionlist.Y + optionlist.Height + d_margin1 + d_margin1, d_dialog_w - (d_name_x-d_dialog_x)*2, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + difficulty.Set_Maximum(5); + difficulty.Set_Value(2); + } else { + difficulty.Set_Maximum(3); + difficulty.Set_Value(1); + } + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + if (!skirmish) { + playerlist.Add_Tail(*commands); + } else { + difficulty.Add_Tail(*commands); + } + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + if (Session.Type == GAME_SKIRMISH){ + okbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + if (!skirmish && loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE + //Load button added only when other player has arrived + //loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Special.IsShadowGrow = Rule.IsMPShadowGrow; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = false; + } + + /*........................................................................ + Init button states + ........................................................................*/ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + if (!skirmish) { + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + } + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsShadowGrow); + if (!skirmish) { + optionlist.Check_Item(4, Special.IsCaptureTheFlag); + } + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + int maxp = Rule.MaxPlayers - 2; +// int maxp = Rule.MaxPlayers - (skirmish ? 1 : 2); + aiplayersgauge.Set_Maximum(maxp); + + if (skirmish) { + if ( Session.Options.AIPlayers > 7 ) { + Session.Options.AIPlayers = 7; + } + Session.Options.AIPlayers = max(Session.Options.AIPlayers, 1); + }else{ + if ( Session.Options.AIPlayers > 6 ) { + Session.Options.AIPlayers = 6; + } + } + + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = //Session.Options.Tiberium; + Rule.IsTGrowth = //Session.Options.Tiberium; + Special.IsTSpread = //Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (int j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + break; + } + } + if (EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ +#ifdef FIXIT_RANDOM_GAME + srand(time(NULL)); + Seed = rand(); +#else +// randomize(); +// Seed = rand(); +#endif + + /*........................................................................ + Init the message display system + ........................................................................*/ + if (!skirmish) { + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1, d_send_y + 1, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + } + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + TPF_TEXT, -1); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + } + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + if (!skirmish) { + NullModem.Reset_Response_Time(); // clear response time + } + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount; + + + /* + ** Those easily offended should avert their eyes from the following line. And whatever + ** you do, dont search for 'goto'. + */ +oh_dear_its_a_label: + + while (process) { + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Mono_Debug_Print(0); + } + #endif + + if (!skirmish){ + if (!ok_button_added && gameoptions && kludge_timer == 0){ + okbtn.Add_Tail(*commands); + ok_button_added = true; +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) + { + loadbtn.Add_Tail( *commands ); + } +#else +#ifdef FIXIT_MULTI_SAVE + if ( loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { + loadbtn.Add_Tail ( *commands ); + } +#endif +#endif + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + } + } + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (!skirmish) { + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + if (!skirmish) { + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + Fancy_Text_Print(TXT_EASY, difficulty.X, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_HARD, difficulty.X + difficulty.Width, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_RIGHT|TPF_TEXT); + Fancy_Text_Print(TXT_NORMAL, difficulty.X + difficulty.Width/2, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_CENTER|TPF_TEXT); + } + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE && !skirmish) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { + +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2, d_count_y, d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); +// Fancy_Text_Print("%d ", d_count_x + d_count_w + 3 * RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT, Session.Options.UnitCount); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 3 * RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); +// Fancy_Text_Print("%d", d_credits_x + d_credits_w + 2 * RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT, Session.Options.Credits); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); +// Fancy_Text_Print("%d", d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT, Session.Options.AIPlayers); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (!skirmish) { + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + display = REDRAW_MESSAGE; + } + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + if (input & KN_BUTTON) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + + Session.PrefColor = (PlayerColorType)((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx); + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + display = REDRAW_BACKGROUND; + transmit = true; + break; +#endif + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; + +#else // FIXIT_VERSION_3 Whoever duplicated Netdlg into Nulldlg should be shot. Wasn't it enough? + + Abandon all hope ye who hit enter here. + + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ( !skirmish && (PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if ( !skirmish && PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + } else + if ( !skirmish && PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + }else{ + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; +#endif + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + SessionClass::CountMin[Session.Options.Bases]; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = ((Session.Options.Credits + 250) / 500) * 500; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + { + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + int humans = 2; // Two humans. + if (skirmish) { + Session.Options.AIPlayers += 1; // Always one forced AI player. + humans = 1; // One human. + // if (Session.Options.AIPlayers == 0) { + // Session.Options.AIPlayers = 1; + // aiplayersgauge.Set_Value(0); + // } + } + if (Session.Options.AIPlayers+humans >= Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - humans; + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + } + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + break; + } + + //------------------------------------------------------------------ + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //------------------------------------------------------------------ + case (BUTTON_OPTIONS | KN_BUTTON): + if (!skirmish && Special.IsCaptureTheFlag != optionlist.Is_Checked(4) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != optionlist.Is_Checked(0)) { + Session.Options.Bases = optionlist.Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + if (!skirmish) optionlist.Check_Item(4, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsShadowGrow = optionlist.Is_Checked(3); + if (!skirmish) { + Special.IsCaptureTheFlag = optionlist.Is_Checked(4); + } + + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + rc = true; + process = false; + + // force transmitting of game options packet one last time + + transmit = true; + transmittime = 0; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) + load_game = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!skirmish) { + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + } + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (skirmish) { + transmit = false; + } + + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ScenarioInfo.Credits = Session.Options.Credits; + SendPacket.ScenarioInfo.IsBases = Session.Options.Bases; + SendPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + SendPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + SendPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + SendPacket.ScenarioInfo.BuildLevel = BuildLevel; + SendPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + SendPacket.ScenarioInfo.Seed = Seed; + SendPacket.ScenarioInfo.Special = Special; + SendPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + SendPacket.ID = Session.ModemType; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (SendPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + SendPacket.ScenarioInfo.FileLength = file.Size(); + +#ifdef WOLAPI_INTEGRATION + strcpy( SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(SendPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)SendPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof SendPacket.ScenarioInfo.FileDigest); + SendPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if (!skirmish && (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (!skirmish && NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + kludge_timer = 2*60; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + transmit = true; + + parms_received = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process (TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + } else { + + + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists + .........................................................*/ + if (playerlist.Count()==0) { + //...................................................... + // Add two strings to the player list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[TheirColor]); + } + + //......................................................... + // Ensure the player list has the latest, greatest copy of + // our names & colors. Do this every time we receive an + // options packet. + //......................................................... + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : TheirColor]; + + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ScenarioInfo.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if (!skirmish && (TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + if (!skirmish) { + NullModem.Service(); + } + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = skirmish ? 1 : 2; + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + /* + ** Fetch the difficulty setting when in skirmish mode. + */ + if (skirmish) { + int diff = difficulty.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2); + switch (diff) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } else { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } + + if (!skirmish) { + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + TheirName[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(TheirName, "2"); + } + } + #endif + + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + } + + /*..................................................................... + Send all players a GO packet. + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + if (load_game) { + SendPacket.Command = SERIAL_LOADGAME; + } else { + SendPacket.Command = SERIAL_GO; + } + + if (!skirmish) { + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { + ; + } else if (SendPacket.ScenarioInfo.ResponseTime < theirresponsetime) { + SendPacket.ScenarioInfo.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + if (!skirmish) { + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((SendPacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) + ); + } else { + Session.MaxAhead = max( (SendPacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + SendPacket.ID = Session.ModemType; + + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + /* + ** Wait for the go response. This will be either a 'GO' reply, a + ** request for the scenario to be sent or a reply to say that the scenario + ** cant be played. + */ +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); +#endif + do { + NullModem.Service(); + + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + +#ifdef FIXIT_VERSION_3 + if (ReceivePacket.Command == SERIAL_READY_TO_GO) + { + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } + break; + } +#else + if (ReceivePacket.Command == SERIAL_READY_TO_GO) break; +#endif + + if (ReceivePacket.Command == SERIAL_NO_SCENARIO) { + WWMessageBox().Process(TXT_NO_EXPANSION_SCENARIO, TXT_CANCEL); + /* + ** We have to recover from this somehow so.... + */ + process = true; + display = REDRAW_ALL; + lastmsgtime = TickCount; + goto oh_dear_its_a_label; + } + + if (ReceivePacket.Command == SERIAL_REQ_SCENARIO) { +#ifdef WIN32 + WWDebugString ("RA95 - About to call 'Send_Remote_File'.\n"); + +#endif + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + Send_Remote_File (Scen.ScenarioName, 0); + + break; + } + } + + } while ( !Keyboard->Check() ); + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + } + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + if (!skirmish) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + } + + if (!skirmish) Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings(); + } + + if (load_game && !skirmish) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); +} + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Find_Local_Scenario -- finds the file name of the scenario with matching attributes * + * * + * * + * * + * INPUT: ptr to Scenario description * + * ptr to Scenario filename to fix up * + * length of file for trivial rejection of scenario files * + * ptr to digest. Digests must match. * + * * + * * + * OUTPUT: true if scenario is available locally * + * * + * WARNINGS: We need to reject files that don't match exactly because scenarios with the same * + * description can exist on both machines but have different contents. For example * + * there will be lots of scenarios called 'my map' and 'aaaaaa'. * + * * + * HISTORY: * + * 8/23/96 12:36PM ST : Created * + *=============================================================================================*/ +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official) +{ +//FILE *fp; +//fp = fopen("findscen.txt","wt"); +//debugprint("looking for local scenario: description = %s, name=%s, length=%d, digest=%s, official=%d\n", description, filename, length, digest, official); + + char digest_buffer[32]; + /* + ** Scan through the scenario list looking for scenarios with matching descriptions. + */ + for (int index = 0; index < Session.Scenarios.Count(); index++) { + +//debugprint( "Checking against scenario: %s\n", Session.Scenarios[index]->Description()); + if (!strcmp (Session.Scenarios[index]->Description(), description)) { +//debugprint("found matching description.\n"); + CCFileClass file (Session.Scenarios[index]->Get_Filename()); + + /* + ** Possible rejection on the basis of availability. + */ + if (file.Is_Available()) { +//debugprint("file is available.\n"); + /* + ** Possible rejection on the basis of size. + */ + if (file.Size() == length) { +//debugprint("length matches.\n"); + /* + ** We don't know the digest for 'official' scenarios so assume its correct + */ + if (!official) { +//debugprint("!official.\n"); + /* + ** Possible rejection on the basis of digest + */ + INIClass ini; + ini.Load(file); + ini.Get_String ("Digest", "1", "No digest here mate. Nope.", digest_buffer, sizeof (digest_buffer) ); + } +//debugprint("digest = %s, digest_buffer = %s.\n", digest, digest_buffer); +#ifdef FIXIT_CSII // checked - ajw 9/28/98. But don't know why this happens. Because of autodownload? + /* + ** If this is an aftermath scenario then ignore the digest and return success. + */ + if ( Is_Mission_Aftermath ((char*)Session.Scenarios[index]->Get_Filename()) ) { +//debugprint("a 1match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } +#endif + + /* + ** This must be the same scenario. Copy the name and return true. + */ + if (official || !strcmp (digest, digest_buffer)) { +//debugprint("a match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } + } + } +// else +// debugprint("file not available '%s'.\n", Session.Scenarios[index]->Get_Filename()); + + } + } +//debugprint("failed match.\n"); + /* + ** Couldnt find the scenario locally. Return failure. + */ + return (false); +} + + + + + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * The 'Players' vector is filled in by this routine, when the game starts; this * + * is for the Assign_Houses routine, which expects this vector to contain all * + * players' names & houses & colors. Other than that, the Players vector, Games * + * vector, and Chat vector aren't used at all by this routine. The Game & Players * + * list boxes are filled in manually in the processing loop. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 2 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin1 + d_margin2 + d_txt6_h + 1*RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; + +#else //OLDWAY + + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; + +#endif //OLDWAY + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + int d_color_y = d_name_y; + + int d_scenario_y = d_name_y + d_name_h + d_margin2; + + int d_gamelist_w = 160 * RESFACTOR; + int d_gamelist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_gamelist_y = d_scenario_y + d_txt6_h + d_margin2 + d_txt6_h + d_margin2; + +//BG int d_playerlist_w = 112 * RESFACTOR; + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w - 5*RESFACTOR; + int d_playerlist_y = d_gamelist_y; + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + (d_margin1 * 2) - d_margin2; + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 * RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 112 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin1 - d_margin2; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (7 * d_txt6_h) + 3 * RESFACTOR; // 7 rows high + int d_message_x = d_gamelist_x;//d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + d_margin2/*KO + d_margin1*/; + + int d_send_w = d_message_w; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_message_x; + int d_send_y = d_message_y + d_message_h; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_send_y + d_send_h/*KO + d_margin2*/; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else //OLDWAY + BUTTON_HOUSE, +#endif //OLDWAY + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name +//BG int playertabs[] = {77}; // tabs for player list box + int playertabs[] = {71*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for options list box + bool transmit; // 1 = re-transmit new game options + bool first; // 1 = no packets received yet + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + unsigned long version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + bool load_game = false; // 1 = load saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + char *p; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + bool messages_have_focus = true; + bool ready_packet_was_sent = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else //OLDWAY + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif //OLDWAY + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else //OLDWAY + housebtn.Add_Tail(*commands); +#endif //OLDWAY + + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else //OLDWAY + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif //OLDWAY + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-2); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + transmit = true; + first = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 7, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 * RESFACTOR, d_send_y + 1 * RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + Session.WWChat = 0; + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + TPF_TEXT, -1); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount; + + bool process = true; // process while true + while (process) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print (TXT_COUNT, d_count_x - 2 * RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2 * RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_CREDITS_COLON, d_credits_x - 2 * RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1 * RESFACTOR, d_color_y + 1 * RESFACTOR, + cbox_x[i] + 1 * RESFACTOR + d_color_w - 2 * RESFACTOR, d_color_y + 1 * RESFACTOR + d_color_h - 2 * RESFACTOR, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print(txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + /*............................................................ + Scenario description + ............................................................*/ + //LogicPage->Fill_Rect(d_dialog_x + 16*RESFACTOR, d_scenario_y, + // d_dialog_x + d_dialog_w - 16*RESFACTOR, d_scenario_y + d_txt6_h, BLACK); + + p = (char *)Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { +// sprintf(txt,"%s %s",p, Session.Options.ScenarioDescription); +// Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + // EW - Scenario language translation goes here!!!!!!!! VG + for (i = 0; EngMisStr[i] != NULL; i++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[i])) { +#if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[i+1]); +#else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +#endif + break; + } + } + if (EngMisStr[i] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER); + } + + //......................................................... + // Unit count, tech level, credits + //......................................................... + //LogicPage->Fill_Rect(d_count_x + d_count_w + 2 * RESFACTOR, d_count_y, + // d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, + // BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + KeyNumType input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + changed = true; + + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (Session.PrefColor == TheirColor) + break; + } + Session.ColorIdx = Session.PrefColor; + + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + strcpy (Session.Handle, namebuf); + transmit = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } else if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else //OLDWAY + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + transmit = true; + //display = REDRAW_BACKGROUND; + break; +#endif //OLDWAY + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if ((TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message(&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while ( (TickCount - starttime) < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (Session.ColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = true; + transmittime = 0; + + Session.ColorIdx = (PlayerColorType)(TheirColor + 1); + if (Session.ColorIdx >= 6) { + Session.ColorIdx = PCOLOR_FIRST; + } + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + Session.Options.Credits = ReceivePacket.ScenarioInfo.Credits; + Session.Options.Bases = ReceivePacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = ReceivePacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = ReceivePacket.ScenarioInfo.IsGoodies; + Session.Options.AIPlayers = ReceivePacket.ScenarioInfo.AIPlayers; + BuildLevel = ReceivePacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = ReceivePacket.ScenarioInfo.UnitCount; + Seed = ReceivePacket.ScenarioInfo.Seed; + Special = ReceivePacket.ScenarioInfo.Special; + Options.GameSpeed = ReceivePacket.ScenarioInfo.GameSpeed; + + if (Session.Options.Tiberium) { + Special.IsTGrowth = true; + Rule.IsTGrowth = true; + Special.IsTSpread = true; + Rule.IsTSpread = true; + } else { + Special.IsTGrowth = false; + Rule.IsTGrowth = false; + Special.IsTSpread = false; + Rule.IsTSpread = false; + } + + //......................................................... + // Adjust the gauges + //......................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //......................................................... + // Update the options list box + //......................................................... + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + /* + ** If the scenario name changed then we need to redraw the whole lot. + */ + if (strcmp (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario)) { + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, ReceivePacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest, sizeof( ReceivePacket.ScenarioInfo.FileDigest )); +#else + strcpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = ReceivePacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = ReceivePacket.ScenarioInfo.FileLength; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process(TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists, then transmit our options + to him. + .........................................................*/ + if (first) { + //...................................................... + // Add a string to the game list, and two to the player + // list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; + gamelist.Add_Item(item); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]); + + first = false; + transmit = true; + transmittime = 0; + } + + //......................................................... + // Ensure the game list & player list have the latest, + // greatest copy of our names & colors. Do this every time + // we receive an options packet. + //......................................................... + item = (char *)gamelist.Get_Item(0); + sprintf(item,Text_String(TXT_THATGUYS_GAME), + TheirName); + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); + +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]; + + gamelist.Flag_To_Redraw(); + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_LOADGAME): + load_game = true; + case (SERIAL_GO): + + ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + #ifdef WIN32 + WWDebugString ("RA95 - Counterstrike CD is not in drive\n"); + #endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** Make sure we dont time out because of the disk swap + */ + lastmsgtime = TickCount; + } + } + } + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if (!ready_packet_was_sent) { + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided it isnt an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + /* + ** We dont have the scenario and we dont want to request that it gets + ** sent because its an official one. + ** Print up a message saying we cant play this scenario and reply to the + ** host, telling him to select another. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_NO_SCENARIO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + NullModem.Service(); + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + display = REDRAW_ALL; + lastmsgtime = TickCount; + break; + } else { +#endif +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 0)) { + rc = false; + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + lastmsgtime = TickCount; + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + + /* + ** Fall through here... + */ + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((ReceivePacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } else { + Session.MaxAhead = max( (ReceivePacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + + process = false; + rc = true; + if (ReceivePacket.Command == SERIAL_LOADGAME) + load_game = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ((TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = 2; + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + namebuf[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(namebuf, "2"); + } + } + #endif + + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + who = new NodeNameType; + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ +// while (optionlist.Count()>0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + while (gamelist.Count()>0) { + item = (char *)gamelist.Get_Item(0); + delete [] item; + gamelist.Remove_Item(item); + } + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); +//#ifdef WIN32 +// Load_Uncompress(CCFileClass("TITLE.CPS"), SysMemPage, SysMemPage, CCPalette); +// SysMemPage.Scale(SeenPage); +//#else +// Load_Uncompress(CCFileClass("TITLE.CPS"), HidPage, HidPage, CCPalette); +// HidPage.Blit(SeenPage); +//#endif + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 * RESFACTOR; // dialog width + int d_dialog_h = 160 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_phonelist_w = 248 * RESFACTOR; + int d_phonelist_h = 87 * RESFACTOR; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11 * RESFACTOR; + + int d_add_w = 45 * RESFACTOR; + int d_add_h = 9 * RESFACTOR; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45 * RESFACTOR; + int d_edit_h = 9 * RESFACTOR; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45 * RESFACTOR; + int d_delete_h = 9 * RESFACTOR; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_numedit_h = 9 * RESFACTOR; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*RESFACTOR, 207*RESFACTOR}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + bool changed = false; // 1 = save changes to INI file + bool firsttime = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ListClass phonelist(BUTTON_PHONELIST, d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, TPF_BUTTON, d_edit_x, d_edit_y, d_edit_w, d_edit_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + EditClass numedit (BUTTON_NUMEDIT, phone_num, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + firsttime = true; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + phonelist.Draw_Me (true); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + #ifdef FIXIT_PHONELIST_CRASH + if (Session.CurPhoneIdx != -1) { + #endif + if (phonelist.Current_Index() != Session.CurPhoneIdx) { + Session.CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = true; + } + #ifdef FIXIT_PHONELIST_CRASH + } + #endif + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*Session.PhoneBook[Session.CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*Session.PhoneBook[Session.CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (Session.PhoneBook[Session.CurPhoneIdx] == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + Session.PhoneBook.Delete (Session.CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = true; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (Session.CurPhoneIdx == -1 || + strcmp( Session.PhoneBook[Session.CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = true; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + } + } + changed = true; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&Session.PhoneBook[0]), Session.PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( Session.PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, Session.PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( Session.PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( Session.PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, Session.PhoneBook[i]->Number ); + } else { + strncpy( phonenum, Session.PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (Session.PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + Session.PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || Session.CurPhoneIdx < -1) { + Session.CurPhoneIdx = -1; + } else { + if (Session.CurPhoneIdx >= list->Count() ) { + Session.CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (Session.CurPhoneIdx > -1) { + strcpy (buf, Session.PhoneBook[Session.CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( Session.CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 * RESFACTOR; // dialog width + int d_dialog_h = 110 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 * RESFACTOR; + int d_name_y = d_dialog_y + 25 * RESFACTOR; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_number_h = 9 * RESFACTOR; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 * RESFACTOR; + int d_number_y = d_name_y + d_name_h + d_margin; + + int d_default_w = 130 * RESFACTOR; + int d_default_h = 9 * RESFACTOR; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + + int d_custom_w = 130 * RESFACTOR; + int d_custom_h = 9 * RESFACTOR; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + + int d_save_w = 55 * RESFACTOR; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h - 5*RESFACTOR; + + int d_cancel_w = 55 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h - 5*RESFACTOR; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + bool custom = false; + bool firsttime = true; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit(BUTTON_NAME, namebuf, PhoneEntryClass::PHONE_MAX_NAME, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + EditClass numedit(BUTTON_NUMBER, numbuf, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, TPF_BUTTON, d_default_x, d_default_y, d_default_w, d_default_h); + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, TPF_BUTTON, d_custom_x, d_custom_y, d_custom_w, d_custom_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = Session.SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + bool process = true; // process while true + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print (TXT_NAME_COLON, d_name_x - 5 * RESFACTOR, d_name_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + Fancy_Text_Print (TXT_NUMBER_COLON, d_number_x - 5 * RESFACTOR, d_number_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + KeyNumType input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = false; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = true; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + + + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); +// WWMessageBox().Process( "Error in the InitString." ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + + /* + ** Completely disable audio. This is required for MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + WWMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + WWMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + Session.ModemService = false; + WWMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Completely disable audio. This is required for some MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + +// case DIAL_BUSY: +// WWMessageBox().Process(TXT_LINE_BUSY); +// connected = false; +// break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_CANCELED: + WWMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s",buf); + } else { + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + if (Debug_Modem_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + +void Log_Start_Time( char *string ) +{ +// LogDump_Print = true; + + LogLevel = 0; + LogLevelTime[ LogLevel ] = LogLastTime = TickCount; + + Smart_Printf( "start tick=%d, %s \n", LogLastTime, string ); +} + + +void Log_End_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + while (LogLevel >= 0) { + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel-- ]; + Smart_Printf( "end tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + } + + LogDump_Print = false; +} + + +void Log_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + + Smart_Printf( "tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + LogLastTime = currtime; +} + + +void Log_Start_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + Smart_Printf( "start ntick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel >= (MAX_LOG_LEVEL - 1) ) { + Smart_Printf( "Could not start another nesting Maxed at %d,%d!-! \n", + LogLevel, (MAX_LOG_LEVEL - 1) ); + } else { + LogLevelTime[ ++LogLevel ] = currtime; + } + + LogLastTime = currtime; +} + + +void Log_End_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel <= 0) { + Smart_Printf( "Could not end another nesting Mined at %d,%d!-! \n", + LogLevel, 0 ); + LogLevel = 0; + } + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel ]; + Smart_Printf( "end ntick=%d, ticks=%d, secs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel) { + LogLevel--; + } + + LogLastTime = currtime; +} + +#endif \ No newline at end of file diff --git a/REDALERT/NULLMGR.CPP b/REDALERT/NULLMGR.CPP new file mode 100644 index 000000000..92da4fb84 --- /dev/null +++ b/REDALERT/NULLMGR.CPP @@ -0,0 +1,2618 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/NULLMGR.CPP 2 3/07/97 6:40p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : May 1, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemClass::NullModemClass -- class constructor * + * NullModemClass::~NullModemClass -- class destructor * + * NullModemClass::Init -- initialization * + * NullModemClass::Send_Message -- sends a message * + * NullModemClass::Get_Message -- polls the Queue for a message * + * NullModemClass::Service -- main polling routine * + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * NullModemClass::Reset_Response_Time -- Resets response time computatio* + * NullModemClass::Oldest_Send -- Returns ptr to oldest unACK'd send buf * + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * NullModemClass::Dial_Modem -- dials a number passed * + * NullModemClass::Answer_Modem -- waits for call and answers * + * NullModemClass::Hangup_Modem -- hangs up the modem * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +extern ModemRegistryEntryClass *ModemRegistry; +#endif //WIN32 +#include "i86.h" + + +// Turn off "expression is not meaningful". +#pragma warning 628 9 + + +// the following line was taken from Greenleaf's +// because of other define conflicts + +#define ESC 27 +#define NOKEY 0xffff +#define INIT_COMMAND_RETRIES 2 + +// this time is in milliseconds + +#define DEFAULT_TIMEOUT 1500 + +// +// the following is for a fix around a greenleaf bug +// where they do not check for the value of abortkey +// to determine whether or not they call the abort modem function. +// +extern "C" { + extern void (*_AbortModemFunctionPtr)(int); +} + +static void (*NullModemClass::OrigAbortModemFunc)(int); + +static KeyNumType NullModemClass::Input; +static GadgetClass *NullModemClass::Commands; // button list + +/* +** Ugly hack: this string stores the string received from the modem +*/ +char ModemRXString[80]; + + +/*************************************************************************** + * NullModemClass::NullModemClass -- class constructor * + * * + * INPUT: * + * numsend # desired entries for the send queue * + * numreceive # desired entries for the receive queue * + * maxlen application's max packet length * + * magicnum application-specific magic # (so we don't * + * accidentally end up talking to another one of our own * + * products using the same protocol) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::NullModemClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum) : ConnManClass() +{ + /*------------------------------------------------------------------------ + Init Port to NULL; we haven't opened Greenleaf yet. + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + + Connection = NULL; + + NumSend = numsend; + NumReceive = numreceive; + MaxLen = maxlen; + MagicNum = magicnum; + + RXBuf = 0; + BuildBuf = 0; + + EchoSize = 500; + EchoBuf = 0; + + OldIRQPri = -1; + + ModemVerboseOn = false; // default true + ModemEchoOn = false; // default true + ModemWaitCarrier = 50000; // default 50 * 1000ms = 50 secs + ModemCarrierDetect = 600; // default 6 * 100ms = .6 secs + ModemCarrierLoss = 1400; // default 14 * 100ms = 1.4 secs + ModemHangupDelay = 20000; // default 20 * 1000ms = 20 secs + ModemGuardTime = 1000; // default 50 * 20ms = 1 sec + ModemEscapeCode = '+'; // default ASCII 43 + + SendOverflows = 0; + ReceiveOverflows = 0; + CRCErrors = 0; + + NumConnections = 0; + + /*------------------------------------------------------------------------ + Init timing parameters + ------------------------------------------------------------------------*/ + RetryDelta = 60; // 60 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 1200; // report bad connection after 20 seconds + +} /* end of NullModemClass */ + + +/*************************************************************************** + * NullModemClass::~NullModemClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::~NullModemClass () +{ + Delete_Connection(); + +} /* end of ~NullModemClass */ + + +/*************************************************************************** + * NullModemClass::Init -- initialization * + * * + * INPUT: * + * port address * + * irq 2-15 * + * dev_name name of communications device (win32 only) * + * baud 300, 1200, 9600, etc * + * parity 'O' (odd), 'E' (even), 'N' (none), 'S' (space), * + * 'M' (mark) * + * wordlength 5, 6, 7, or 8 * + * stopbits 1 or 2 * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 10/9/1996 ST : Modified to take device name in win32 * + *=========================================================================*/ +int NullModemClass::Init (int port, int irq, char *dev_name, int baud, char parity, int wordlen, int stopbits, int flowcontrol) +{ + int com; + +#ifdef WIN32 + + /* + ** Get rid of the 'no reference to' warning + */ + irq = irq; + + /* + ** Make sure the port is closed before we start + */ + if (PortHandle) { + CloseHandle (PortHandle); + PortHandle = NULL; + } + + if (!Connection) { + +#else //WIN32 + + int irqnum; + int address; + int status; + + /* + ** Get rid of the 'no reference to' warning + */ + dev_name = dev_name; + + if (Port) { + PortClose(Port); + Port = NULL; + }else { + +#endif //WIN32 + + /*------------------------------------------------------------------------ + Init our Connection + ------------------------------------------------------------------------*/ + Connection = new NullModemConnClass (NumSend, NumReceive, MaxLen, + MagicNum); + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + + /*--------------------------------------------------------------------- + Allocate our packet parsing buffer; make it the same # of packets as the + # of receive queue entries the application has requested. Use the + "Actual" maximum packet size, given from the connection; this allows for + both headers that get added to the packet. + ---------------------------------------------------------------------*/ + RXSize = Connection->Actual_Max_Packet() * NumReceive; + RXBuf = new char [RXSize]; + + BuildBuf = new char [MaxLen]; + + EchoBuf = new char [ EchoSize ]; + } + + RXCount = 0; + EchoCount = 0; + + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch (port) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + +#ifdef WIN32 + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + } + + /* + ** Shift up the baud rate to sensible values + */ +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + char *device; + + switch ( port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = dev_name; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++ ){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (dev_name, ModemRegistry->Get_Modem_Name() )){ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + device = NULL; + } + + + + /* + ** Open the com port + */ + PortHandle = SerialPort->Serial_Port_Open (device, baud, parity, wordlen, stopbits, flowcontrol); + if (PortHandle == INVALID_HANDLE_VALUE) { + Shutdown(); + return(false); + } + + Connection->Init (PortHandle); + +#else //WIN32 + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (port != address || irq != irqnum) { + + status = FastSetPortHardware( com, irq, port ); + + if (status < ASSUCCESS) { + Mono_Printf( "Unable to set Com port status %d\n", status ); + Mono_Printf( "Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } else { + Mono_Printf( "Changed Com port number %d to address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "No changes to Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "Com port number %d\n", com + 1 ); + } + + if (status != ASSUCCESS) { + Delete_Connection(); + return(false); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, baud, parity, wordlen, stopbits); + + if (Port->status != ASSUCCESS) { + Shutdown(); + return(false); + } + +// UseRtsCts( Port, 1 ); // use RTS CTS hardware flow control + + /*------------------------------------------------------------------------ + Init the Connection + ------------------------------------------------------------------------*/ + Connection->Init(Port); + //because Watcom is so stupid + flowcontrol = flowcontrol; + +#endif //WIN32 + + NumConnections = 1; + + return(true); +} + + +/*********************************************************************************************** + * NMC::Num_Connections -- returns NumConnections member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: NumConnections * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Num_Connections( void ) +{ + return(NumConnections); +} + + +/*********************************************************************************************** + * NMC::Delete_Connection -- removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Delete_Connection( void ) +{ + if (Connection) { + delete Connection; + Connection = NULL; + } + + if (RXBuf) { + delete [] RXBuf; + RXBuf = NULL; + } + + if (BuildBuf) { + delete [] BuildBuf; + BuildBuf = NULL; + } + + if (EchoBuf) { + delete [] EchoBuf; + EchoBuf = NULL; + } + + NumConnections = 0; + + return( true ); +} /* end of Delete_Connection */ + + + +/*********************************************************************************************** + * NMC::Init_Send_Queue -- Initialises the connections send queue * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:46AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Init_Send_Queue( void ) +{ + + /*------------------------------------------------------------------------ + Init the send queue + ------------------------------------------------------------------------*/ + if ( Connection ) { + Connection->Queue->Init_Send_Queue(); + } + + return(true); +} + + + +/*********************************************************************************************** + * NMC::Detect_Port -- Checks that the specified com port exists * + * * + * * + * * + * INPUT: ptr to SerialSettingsType * + * * + * OUTPUT: true if port is valid * + * * + * WARNINGS: Win32 version always returns true as win95 shouldnt allow us to open the * + * port if it doesnt exist or is in use by the mouse * + * * + * HISTORY: * + * 8/2/96 11:47AM ST : Documented / Win32 support * + *=============================================================================================*/ +DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + }else{ + SerialPort->Serial_Port_Close(); + } + + /* + ** Shift up the baud rate to sensible values + */ + int baud = settings->Baud; +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + + /* + ** Translate the port address into a usable device name + */ + char *device; + + switch ( settings->Port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = settings->ModemName; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (device, ModemRegistry->Get_Modem_Name() )){ + /* + ** Got a match. Break out leaving the registry info intact. + */ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + return (PORT_INVALID); + } + + /* + ** Open the com port + */ + HANDLE porthandle = SerialPort->Serial_Port_Open (device, baud, 0, 8, 1, settings->HardwareFlowControl); + + if (porthandle == INVALID_HANDLE_VALUE){ + return (PORT_INVALID); + } + + SerialPort->Serial_Port_Close(); + return (PORT_VALID); + + + +#else //WIN32 + + union REGS regs ; + int com; + int irqnum; + int address; + int status; + + + // shutdown previous port + + Shutdown(); + + if (settings->IRQ > 0xf) { + return( PORT_INVALID ); + } + + // + // check if the IRQ is the same as the mouse + // call mouse func to get mouse IRQ number + // + + regs.x.eax = 0x0024; + int386( 0x33, ®s, ®s ); + + // + // check for error + // + if (regs.w.ax != 0xffff) { + // + // is the mouse IRQ the same as that they selected + // + if (regs.h.cl == 0) { // PS/2 IRQ 0xc + if (settings->IRQ == 0xc) { + return( PORT_IRQ_INUSE ); + } + } else if (regs.h.cl == (unsigned char)(settings->IRQ)) { + return( PORT_IRQ_INUSE ); + } + } + + if (settings->IRQ < 2 // 0 timer, 1 keyboard + || settings->IRQ == 6 // floppy disk + || settings->IRQ == 8 // CMOS real-time clock + || settings->IRQ == 0xd // math coprocessor error + || settings->IRQ == 0xe) { // hard disk + return( PORT_IRQ_INUSE ); + } + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch ( settings->Port ) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (settings->Port != address || settings->IRQ != irqnum) { + status = FastSetPortHardware( com, settings->IRQ, settings->Port ); + } + } + + if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, settings->Baud, 'N', 8, 1); + + status = Port->status; + + PortClose(Port); + Port = NULL; + + if (status == ASIRQINUSE) { + return( PORT_IRQ_INUSE ); + } else if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + return( PORT_VALID ); +#endif //WIN32 +} + + +/*********************************************************************************************** + * NullModemClass::ShutDown -- Closes serial port and removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:43AM ST : Documented / Win32 support * + *=============================================================================================*/ +void NullModemClass::Shutdown ( void ) +{ + +#ifdef WIN32 + + if (PortHandle && SerialPort) { + SerialPort->Serial_Port_Close(); + delete SerialPort; + SerialPort = NULL; + PortHandle = NULL; + Delete_Connection(); + } + +#else //WIN32 + if (Port) { + PortClose(Port); + Port = NULL; + Delete_Connection(); + } + +#endif //WIN32 + +} /* end of Shutdown */ + + +/*************************************************************************** + * NullModemClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/07/1995 DRD : Created. * + *=========================================================================*/ +void NullModemClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + +} /* end of Set_Timing */ + + +/*************************************************************************** + * NullModemClass::Send_Message -- sends a message * + * * + * For clarity's sake, here's what happens to the buffer passed in: * + * - It gets passed to the Connection's Send_Packet() routine * + * - The CommHeaderType header gets tacked onto it * + * - The resulting buffer gets added to the Connection's Send Queue * + * - When Service() determines that it needs to send the data, it * + * copies the entire packet (CommHeaderType and all) into its local * + * SendBuf, adds the packet start ID, length, and CRC, then sends it out.* + * * + * The ack_req argument will almost always be '1' (the default). The only * + * reason to use 0 is if you don't know whether the other system is * + * ready or not, so you have to periodically send out a query packet, * + * and wait for a response. (Using the connection's built-in retry * + * system would just blast out useless data if the other system isn't * + * even there.) * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required; 0 = not * + * * + * OUTPUT: * + * 1 = OK; 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Send_Message (void *buf, int buflen, int ack_req) +{ + int rc; + + if (NumConnections == 0) { + return( false ); + } + + rc = Connection->Send_Packet(buf,buflen,ack_req); + if (!rc) + SendOverflows++; + + return(rc); + +} /* end of Send_Message */ + + +/*************************************************************************** + * NullModemClass::Get_Message -- polls the Queue for a message * + * * + * INPUT: * + * buf buffer to store message in * + * buflen ptr filled in with length of message * + * * + * OUTPUT: * + * 1 = message was received; 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Get_Message (void *buf, int *buflen) +{ + if (NumConnections == 0) { + return( false ); + } + return( Connection->Get_Packet( buf, buflen ) ); +} + + +/*************************************************************************** + * NullModemClass::Service -- main polling routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = connection has gone bad * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +int NullModemClass::Service (void) +{ + int pos; // current position in RXBuf + int i; // loop counter + unsigned short length; + SerialHeaderType *header; // decoded packet start, length + SerialCRCType *crc; // decoded packet CRC + char moredata = 0; + + if (NumConnections == 0) { + return( false ); + } + +#ifdef WIN32 + + RXCount += SerialPort->Read_From_Serial_Port((unsigned char*)(RXBuf + RXCount), int(RXSize - RXCount) ); + +#else //WIN32 + + int status; + static unsigned long last_time = 0; + + /*------------------------------------------------------------------------ + First, copy all the bytes we can from the Greenleaf RX buffer to our + own buffer. + (For Win95, only call GetLineStatus() 60 times a second at most. + Otherwise, we don't receive any packets!) + ------------------------------------------------------------------------*/ + if ( (TickCount - last_time) > 0) { + if ( GetLineStatus( Port ) ) { + Mono_Set_Cursor(0,0); + ClearLineStatus( Port ); + } + last_time = TickCount; + } + + status = SpaceUsedInRXBuffer( Port ); + + if ( status < ASSUCCESS) { +// Smart_Printf( "SpaceUsedInRXBuffer status = %d, port status = %d \n", status, Port->status ); + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + } else if (status > 0) { + status = ReadBuffer( Port, RXBuf + RXCount, RXSize - RXCount ); + +#if (CONN_DEBUG) + printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); +#endif +// Smart_Printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); + if (status < ASSUCCESS && status != ASBUFREMPTY) { +// Smart_Printf( "ReadBuffer ERRRRRRORRRRRR! \n" ); + } else { + moredata = 1; + } + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + RXCount += Port->count; + } + + +#endif //WIN32 + + // minimum packet size + + if ( RXCount < (PACKET_SERIAL_OVERHEAD_SIZE + 1) ) { + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Now scan the buffer for the start of a packet. + ------------------------------------------------------------------------*/ + pos = -1; + for (i = 0; i <= RXCount - sizeof( short ); i++) { + if ( *((unsigned short *)(RXBuf + i)) == PACKET_SERIAL_START ) { + pos = i; + break; + } + } + + /*------------------------------------------------------------------------ + No start code was found; throw away all bytes except the last few, and + return. + ------------------------------------------------------------------------*/ + if (pos==-1) { +// Smart_Printf( "No magic number found \n" ); + /*..................................................................... + move the remaining, un-checked bytes to the start of the buffer + .....................................................................*/ + memmove (RXBuf, RXBuf + i, sizeof( short ) - 1); + RXCount = sizeof( short ) - 1; + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Check to see if there are enough bytes for the header to be decoded + ------------------------------------------------------------------------*/ + if ( (RXCount - pos) < sizeof( SerialHeaderType ) ) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + A start code was found; check the packet's length & CRC + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *)(RXBuf + pos); + + /*------------------------------------------------------------------------ + If we lost a byte in the length, we may end up waiting a very long time + for the buffer to get to the right length; check the verify value to + make sure this didn't happen. + ------------------------------------------------------------------------*/ + if ( header->MagicNumber2 != PACKET_SERIAL_VERIFY ) { +// Smart_Printf( "Verify failed\n"); +// Hex_Dump_Data( (RXBuf + pos), PACKET_SERIAL_OVERHEAD_SIZE ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + length = header->Length; + + /*------------------------------------------------------------------------ + Special case: if the length comes out too long for us to process: + - Assume the packet is bad + - Throw away the bogus packet-start code + - Return; we'll search for another packet-start code next time. + ------------------------------------------------------------------------*/ + if (length > MaxLen) { +#if (CONN_DEBUG) + printf( "length too lonnng\n" ); +#endif +// Smart_Printf( "length too lonnng %d, max %d \n", length, MaxLen ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + If the entire packet isn't stored in our buffer, copy the remaining bytes + to the front of the buffer & return. + ------------------------------------------------------------------------*/ + if ( (pos + length + PACKET_SERIAL_OVERHEAD_SIZE) > RXCount) { + + if ( moredata ) { +// Smart_Printf( "waiting for more data %d, pos = %d \n", ((length + PACKET_SERIAL_OVERHEAD_SIZE) - (RXCount - pos)), pos ); + } + + if (pos) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + } + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + Now grab the CRC value in the packet, & compare it to the CRC value + computed from the actual data. If they don't match, throw away the bogus + start-code, move the rest to the front of the buffer, & return. + We'll continue parsing this data when we're called next time. + ------------------------------------------------------------------------*/ + crc = (SerialCRCType *)(RXBuf + pos + sizeof( SerialHeaderType ) + length); + if (NullModemConnClass::Compute_CRC(RXBuf + pos + + sizeof( SerialHeaderType ), length) != crc->SerialCRC) { + + CRCErrors++; + +#if (CONN_DEBUG) + printf( "CRC check failed\n" ); +#endif +// Smart_Printf( "CRC check failed for packet of length %d \n", length ); + +// if (length < 100) { +// Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +// } + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + +#if(0) +// Mono_Printf( "received %d bytes \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received tick=%d, Protocol=%d \n", TickCount, Session.CommProtocol ); + Hex_Dump_Data( (RXBuf + pos), (sizeof( SerialHeaderType) + length) ); + Debug_Modem_Dump = false; +#endif + + /*------------------------------------------------------------------------ + Give the new packet to the Connection to process. + ------------------------------------------------------------------------*/ + if (!Connection->Receive_Packet(RXBuf + pos + sizeof( SerialHeaderType ), length)) { + ReceiveOverflows++; +// Smart_Printf( "Received overflows %d \n", ReceiveOverflows ); + } + +#if(0) + else { +// Mono_Printf( "added packet \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received Packet \n" ); + Debug_Modem_Dump = false; + } +#endif + +#if (0) + Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +#endif + + /*------------------------------------------------------------------------ + Move all data past this packet to the front of the buffer. + ------------------------------------------------------------------------*/ + pos += (PACKET_SERIAL_OVERHEAD_SIZE + length); + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + + /*------------------------------------------------------------------------ + Now, service the connection's Queue's; this will handle ACK & Retries. + ------------------------------------------------------------------------*/ + return(Connection->Service()); + +} /* end of Service */ + + +/*************************************************************************** + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Send(void) +{ + if (Connection) + return( Connection->Queue->Num_Send() ); + else + return (0); + +} /* end of Num_Send */ + + +/*************************************************************************** + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Receive(void) +{ + if (Connection) + return( Connection->Queue->Num_Receive() ); + else + return (0); + +} /* end of Num_Receive */ + + +/*************************************************************************** + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +unsigned long NullModemClass::Response_Time(void) +{ + if (Connection) + return( Connection->Queue->Avg_Response_Time() ); + else + return (0); + +} /* end of Response_Time */ + + +/*************************************************************************** + * NullModemClass::Reset_Response_Time -- Resets response time computation * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Reset_Response_Time(void) +{ + if (Connection) + Connection->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * Oldest_Send -- Returns ptr to oldest unACK'd send buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void * NullModemClass::Oldest_Send(void) +{ + int i; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < Connection->Queue->Num_Send(); i++) { + send_entry = Connection->Queue->Get_Send(i); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + buf = send_entry->Buffer; + break; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * NullModemClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index (not used) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical start of the 1st name value * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Configure_Debug(int, int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + if (Connection) + Connection->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); +} + +#ifdef CHEAT_KEYS +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * INPUT: * + * index (not used) * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Mono_Debug_Print(int,int refresh) +{ + if (!Connection) + return; + + Connection->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (31,1); + Mono_Printf ("Serial Port Queues"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (20,3); + Mono_Printf ("CRC Errors:"); + + Mono_Set_Cursor (43,2); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,3); + Mono_Printf ("Receive Overflows:"); + } + + Mono_Set_Cursor (32,2); + Mono_Printf ("%d ", Connection->Queue->Avg_Response_Time()); + + Mono_Set_Cursor (32,3); + Mono_Printf ("%d ", CRCErrors); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,3); + Mono_Printf ("%d ", ReceiveOverflows); + + Mono_Set_Cursor (2,5); + Mono_Printf ("%d ", Num_Send()); + + Mono_Set_Cursor (41,5); + Mono_Printf ("%d ", Num_Receive()); + +} /* end of Mono_Debug_Print */ +#endif + + +/*************************************************************************** + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * -1 init string invalid * + * 0 no modem found * + * 1 modem found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +int NullModemClass::Detect_Modem( SerialSettingsType *settings, bool reconnect ) +{ + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + int status; + int error_count = 0; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Get resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + strcpy( buffer, Text_String( TXT_INITIALIZING_MODEM ) ); + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int lines = Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 40 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + if (lines == 1) { + Fancy_Text_Print(buffer, x + width/2, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_CENTER); + } else { + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + } + + Show_Mouse(); + + + HMWaitForOK( 0, NULL ); + + + /* + ** OK, lets not mess about any more. Just turn on echo, verbose, and result codes + ** before we even begin. At least this way when we get an error later on we have already + ** removed all the steps we use to try and recover. + ** The timeouts need to be quite small in case the modem is turned off. + */ + + /* + ** Turn on result codes. + */ + Send_Modem_Command ( "ATQ0", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Make result codes verbose. + */ + Send_Modem_Command ( "ATV1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Turn on echo. + */ + Send_Modem_Command ( "ATE1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + ModemVerboseOn = true; + ModemEchoOn = true; + + + + + + /* + ** Try sending a plain old AT command to the modem. Now that we have theoretically + ** turned on verbose result codes we should get an 'OK' back. + ** + */ + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 2 ); + + if (status < ASSUCCESS) { +#ifdef FIXIT_MULTI_SAVE + return(false); +#else + if (WWMessageBox().Process(TXT_ERROR_NO_RESP, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; +#endif + } + + /* + ** Send the user supplied modem init string + */ + if (settings->InitStringIndex == -1) { + status = Send_Modem_Command( "", '\r', buffer, 81, 300, 1 ); + } else { + /* + ** Split up the init string into seperate strings if it contains one or more '|' characters. + ** This character acts as a carriage return/pause. + */ + char *istr = new char [2 + strlen( Session.InitStrings [settings->InitStringIndex] )]; + char *tokenptr; + strcpy (istr, Session.InitStrings [settings->InitStringIndex] ); + + /* + ** Tokenise the string and send it in chunks + */ + tokenptr = strtok ( istr, "|" ); + while ( tokenptr ) { + + status = Send_Modem_Command( tokenptr, '\r', buffer, 81, 3000, 1 ); + /* + ** Handle error case. + */ + if (status < ASSUCCESS) { + if (WWMessageBox().Process(TXT_ERROR_NO_INIT, TXT_IGNORE, TXT_CANCEL)) { + delete istr; + return( false ); + } +#ifdef WIN32 + error_count++; +#endif //WIN32 + break; + } + + tokenptr = strtok ( NULL, "|"); + + } + } + +#ifdef WIN32 + + if (settings->Port == 1 && ModemRegistry) { + /* + ** Send the init strings from the registry if available + */ + char send_string[256] = {"AT"}; + + /* + ** Send the init string for hardware flow control + */ + if (settings->HardwareFlowControl){ + if (ModemRegistry->Get_Modem_Hardware_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Hardware_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, 300, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + /* + ** Send the init string for no flow control + */ + if (ModemRegistry->Get_Modem_No_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_No_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for data compresseion + */ + if (settings->Compression){ + + if (ModemRegistry->Get_Modem_Compression_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + + if (ModemRegistry->Get_Modem_Compression_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for error correction + */ + if (settings->ErrorCorrection){ + + if (ModemRegistry->Get_Modem_Error_Correction_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + if (ModemRegistry->Get_Modem_Error_Correction_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + } + +#endif //WIN32 + + + /* + ** We require that auto-answer be disabled so turn it off now. + */ + status = Send_Modem_Command( "ATS0=0", '\r', buffer, 81, DEFAULT_TIMEOUT, INIT_COMMAND_RETRIES ); + if (status != MODEM_CMD_OK) { + if (WWMessageBox().Process(TXT_ERROR_NO_DISABLE, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; + } + + /* + ** If we had an unreasonable number of ignored errors then return failure + */ + if (error_count >= 3) { + WWMessageBox().Process(TXT_ERROR_TOO_MANY, TXT_OK); + return (false); + } + + return( true ); +} + + +/*************************************************************************** + * NullModemClass::Dial_Modem -- dials a number passed * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Dial_Modem( char *string, DialMethodType method, bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( buffer, Text_String( TXT_MODEM_CONNERR_REDIALING ) ); + } else { + strcpy( buffer, Text_String( TXT_DIALING ) ); + } + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + int text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 *RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + Show_Mouse(); + + /* + ** Start waiting for connection response + */ +#ifdef WIN32 + SerialPort->Set_Modem_Dial_Type((WinCommDialMethodType) method); + /* + ** Clear out any old modem results that might be hanging around + */ + SerialPort->Get_Modem_Result(60, buffer, 81); + /* + ** Dial that sucker + */ + SerialPort->Dial_Modem(string); + +#else //WIN32 + HMSetDialingMethod( Port, (int)method ); + status = HMDial( Port, string ); + +#endif //WIN32 + + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = ModemWaitCarrier; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + Commands->Draw_All(); + } + + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + /*..................................................................... + Process input + .....................................................................*/ + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } else if ( strncmp( buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + process = false; + } + } + + if (delay <= 0) { + process = false; + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Dial_Modem */ + + +/*************************************************************************** + * NullModemClass::Answer_Modem -- waits for call and answers * + * * + * INPUT: * + * reconnect whether this is to reconnect * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Answer_Modem( bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Redraw values: in order from "top" to "bottom" layer of the dialog + ------------------------------------------------------------------------*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + bool ring = false; + + int x,y,width,height; // dialog dimensions + int text_width; + char text_buffer[80*3]; + char comm_buffer[80*3]; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( text_buffer, Text_String( TXT_MODEM_CONNERR_WAITING ) ); + } else { + strcpy( text_buffer, Text_String( TXT_WAITING_FOR_CALL ) ); + } + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + + Input = KN_NONE; + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack() and Input(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = 60000; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } +#endif //WIN32 + + /*..................................................................... + Refresh display if needed + .....................................................................*/ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /*............................................................... + Refresh the backdrop + ...............................................................*/ + if ( !reconnect ) { + Load_Title_Page(true); + } + + /*............................................................... + Draw the background + ...............................................................*/ + Dialog_Box(x, y, width, height); + + /*............................................................... + Draw the labels + ...............................................................*/ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 * RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, comm_buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, comm_buffer, 81 ); +#endif //WIN32 + + + /*..................................................................... + Process input + .....................................................................*/ + if (!Input) Input = Commands->Input(); + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( comm_buffer, "RING", 4 ) == 0 ) { + + strcpy( text_buffer, Text_String( TXT_ANSWERING ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATA\r", strlen("ATA\r")); +#else //WIN32 + PortKillTime( Port, 100 ); + HMWaitForOK( 0, NULL ); + status = HMAnswer( Port ); +#endif //WIN32 + + ring = true; + delay = ModemWaitCarrier; + display = REDRAW_ALL; + } else if ( strncmp( comm_buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, comm_buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( comm_buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( comm_buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( comm_buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_ERROR, TXT_OK); + process = false; + } + } + +#ifdef WIN32 + + if (delay <= 0) { + if (ring) { + if (SerialPort->Get_Modem_Status() & CD_SET) { + sprintf(ModemRXString, "%s", "Connected"); + dialstatus = DIAL_CONNECTED; + } else { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_TIMEOUT, TXT_OK); + } + process = false; + } else { + delay = 60000; + } + } +#else //WIN32 + + if (delay <= 0) { + if (ring) { + process = false; + } else { + delay = 60000; + } + } +#endif //WIN32 + + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Answer_Modem */ + + +/*************************************************************************** + * NullModemClass::Hangup_Modem -- hangs up the modem * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * status successful or not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 : Added Win32 support * + *=========================================================================*/ +bool NullModemClass::Hangup_Modem( void ) +{ + int status; + int delay; + char buffer[81]; + char escape[4]; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + /* + ** Toggle DTR low then high + */ +#ifdef WIN32 + SerialPort->Set_Serial_DTR(FALSE); + Delay(3200/60); + SerialPort->Set_Serial_DTR(TRUE); +#else //WIN32 + + SetDtr( Port, 0 ); + PortKillTime( Port, 3200 ); + SetDtr( Port, 1 ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + } + + /* + ** Send modem break commmand + */ + escape[0] = ModemEscapeCode; + escape[1] = ModemEscapeCode; + escape[2] = ModemEscapeCode; + escape[3] = 0; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char*)escape, 3); +#else //WIN32 + status = HMSendStringNoWait( Port, escape, -1 ); +#endif //WIN32 + + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + if ( strncmp( buffer, "OK", 2 ) == 0 ) { + break; + } + } + + /* + ** Send the hangup command + */ + status = Send_Modem_Command( "ATH", '\r', buffer, 81, ModemHangupDelay, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + Session.ModemService = true; + return( false ); + } + + /* + ** Send spurious ATZ command for no apparent reason + */ + status = Send_Modem_Command( "ATZ", '\r', buffer, 81, 5000, 1 ); + + if (status != MODEM_CMD_OK) { + Session.ModemService = true; + return( false ); + } + + Session.ModemService = true; + return( true ); + +} /* end of Hangup_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Modem_Echo -- Sets the echo callback function pointer * + * * + * * + * * + * INPUT: Ptr to callback function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:48PM ST : Documented and added WIn32 support * + *=============================================================================================*/ +void NullModemClass::Setup_Modem_Echo( void ( *func )( char c) ) +{ +#ifdef WIN32 + SerialPort->Set_Echo_Function(func); +#else //WIN32 + HMSetUpEchoRoutine( func ); +#endif //WIN32 + +} /* end of Setup_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Remove_Modem_Echo -- Set the echo function callback pointer to null * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:50PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Modem_Echo( void ) +{ +// Smart_Printf( "Remove Echo modem code\n" ); + HMSetUpEchoRoutine( NULL ); + +} /* end of Remove_Modem_Echo */ + + + +/*********************************************************************************************** + * NMC::Print_EchoBuf -- Print out the contents of the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Print_EchoBuf( void ) +{ + for (int i = 0; i < strlen(NullModem.EchoBuf); i++) { + if (NullModem.EchoBuf[i] == '\r') { + NullModem.EchoBuf[i] = 1; + } else { + if (NullModem.EchoBuf[i] == '\n') { + NullModem.EchoBuf[i] = 2; + } + } + } +// Smart_Printf( "Echo buffer length %d (%s)\n", NullModem.EchoCount, NullModem.EchoBuf ); +} + + + +/*********************************************************************************************** + * NMC::Reset_EchoBuf -- Empties the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Reset_EchoBuf( void ) +{ + *EchoBuf = 0; + EchoCount = 0; +} + + +/*********************************************************************************************** + * NMC::Abort_Modem -- Checks for user input so that modem operations can be aborted * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ASUSERABORT if abort key pressed. ASSUCESS otherwise. * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:52PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Abort_Modem( PORT * ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + Input = Commands->Input(); + + switch ( Input ) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + return( ASUSERABORT ); + } + + return( ASSUCCESS ); + +} /* end of Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Abort_Modem -- sets the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 2:59PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Setup_Abort_Modem( void ) +{ + +#ifdef WIN32 + + SerialPort->Set_Abort_Function((int(*)(void))Abort_Modem); + +#else //WIN32 + /* + ** save off original abort modem function to later restore + */ + OrigAbortModemFunc = _AbortModemFunctionPtr; + HMSetUpAbortKey( ESC ); + SetAbortModemFunctionPtr( Abort_Modem ); +#endif //WIN32 + +} /* end of Setup_Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Remove_Abort_Modem -- Removes the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:01PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Abort_Modem( void ) +{ + +#ifdef WIN32 + SerialPort->Set_Abort_Function(NULL); + +#else //WIN32 + + HMSetUpAbortKey( NOKEY ); + + /* + ** Restore the original abort modem function + */ + _AbortModemFunctionPtr = OrigAbortModemFunc; +#endif //WIN32 + +} /* end of Remove_Abort_Modem */ + + + +/*********************************************************************************************** + * NMC::Change_IRQ_Priority -- Increases the priority of the serial interrupt * + * * + * * + * * + * INPUT: Interrupt request number * + * * + * OUTPUT: ASSUCCESS if changed * + * * + * WARNINGS: The Win32 version of this function does nothing. * + * Priorities are controlled by windoze * + * * + * HISTORY: * + * 8/2/96 3:03PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Change_IRQ_Priority( int irq ) +{ +#ifdef WIN32 + + irq = irq; + return ( ASSUCCESS ); + +#else //WIN32 + + if (irq != OldIRQPri) { + OldIRQPri = irq; + return( Change8259Priority( irq ) ); + } else { + return( ASSUCCESS ); + } + +#endif //WIN32 + +} /* end of Change_IRQ_Priority */ + + + +/*********************************************************************************************** + * NMC::Get_Modem_Status -- returns status of modem control bits * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:06PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Get_Modem_Status( void ) +{ + int modemstatus; + int status; + char buffer[81]; + +#ifdef WIN32 + modemstatus = SerialPort->Get_Modem_Status(); +#else //WIN32 + modemstatus = GetModemStatus( Port ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + modemstatus &= (~CD_SET); + } + + return( modemstatus ); + +} /* end of Get_Modem_Status */ + + +/*********************************************************************************************** + * NMC::Send_Modem_Command -- Sends an 'AT' command to the modem and gets the response * + * * + * * + * * + * INPUT: command to send to modem. e.g. 'ATZ' * + * terminator byte for command string * + * buffer to put modem response into * + * length of above buffer * + * delay to wait for response * + * number of times to retry when modem doesnt respond * + * * + * OUTPUT: input delay less the time it took the modem to respond * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:09PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ) +{ + +#ifdef WIN32 + return (SerialPort->Send_Command_To_Modem(command, terminator, buffer, buflen, delay, retries)); +#else //WIN32 + + int status, tmpdelay, retry; + char tempbuf[81]; + + + *buffer = 0; + + for ( retry = 0; retry < retries; retry++ ) { + + PortKillTime( Port, 100 ); + + status = HMSendStringNoWait( Port, command, terminator ); + + + if (status < ASSUCCESS) { + return( status ); + } + + tmpdelay = delay; + while ( tmpdelay > 0 ) { + + tmpdelay = HMInputLine( Port, tmpdelay, tempbuf, 81 ); + + if ( strcmp( tempbuf, "OK" ) == 0 ) { + return( MODEM_CMD_OK ); + } else if ( strcmp( tempbuf, "0" ) == 0 ) { + return( MODEM_CMD_0 ); + } else if ( strcmp( tempbuf, "ERROR" ) == 0 ) { + return( MODEM_CMD_ERROR ); + } else if ( tempbuf[0] != 0 ) { + strncpy( buffer, tempbuf, buflen ); + } + } + + PortKillTime( Port, 100 ); + + /* + ** Send and extra return to help clear out any problems with the modem (if any). + */ + HMWaitForOK( 300, NULL ); + status = HMSendString( Port, "" ); + HMWaitForOK( 0, NULL ); + } + + return( tmpdelay ); + +#endif //WIN32 +} + + +/*********************************************************************************************** + * NMC::Verify_And_Convert_To_Int -- converts a text string of numbers to an int * + * * + * * + * * + * INPUT: ptr to buffer * + * * + * OUTPUT: value of text number in buffer * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:13PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Verify_And_Convert_To_Int( char *buffer ) +{ + int value = 0; + int len = strlen(buffer); + + + for (int i = 0; i < len; i++) { + if ( !isdigit( *(buffer + i) ) ) { + value = -1; + break; + } + } + + if (value == 0) { + value = atoi( buffer ); + } + + return( value ); + +} /* end of Verify_And_Convert_To_Int */ + +/*************************** end of nullmgr.cpp ****************************/ + +#endif \ No newline at end of file diff --git a/REDALERT/NULLMGR.H b/REDALERT/NULLMGR.H new file mode 100644 index 000000000..8f350ead1 --- /dev/null +++ b/REDALERT/NULLMGR.H @@ -0,0 +1,223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLMGR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for a NULL-Modem connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLMODEM_H +#define NULLMODEM_H + + +/* +********************************* Includes ********************************** +*/ +#include "nullconn.h" +#include "connmgr.h" +#include "commlib.h" + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + char *BuildBuf; + int MaxLen; + + char *EchoBuf; + int EchoSize; + int EchoCount; + + int OldIRQPri; + + int ModemVerboseOn; + int ModemEchoOn; + int ModemWaitCarrier; + int ModemCarrierDetect; + int ModemCarrierLoss; + int ModemHangupDelay; + int ModemGuardTime; + char ModemEscapeCode; + + static void (*OrigAbortModemFunc)(int); + static KeyNumType Input; + static GadgetClass *Commands; // button list + + /* + ** Constructor/destructor. + */ + NullModemClass (int numsend, int numreceive, int maxlen, unsigned short magicnum); + virtual ~NullModemClass (); + + /* + ** This is the main initialization routine. + */ + int Init( int port, int irq, char *dev_name, int baud, char parity, int wordlength, int stopbits, int flowcontrol ); + int Delete_Connection( void ); + virtual int Num_Connections(void); + virtual int Connection_ID(int ) {return (0);} + virtual int Connection_Index(int ) {return (0);} + int Init_Send_Queue( void ); + void Shutdown( void ); + + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + /* + ** This is how the application sends & receives messages. + */ + int Send_Message (void *buf, int buflen, int ack_req = 1); + int Get_Message (void *buf, int *buflen); + + /* + ** These are for compatibility + */ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int = CONNECTION_NONE) + {return (Send_Message(buf,buflen,ack_req));} + virtual int Get_Private_Message (void *buf, int *buflen, int *) + {return (Get_Message(buf,buflen));} + + /* + ** The main polling routine; should be called as often as possible. + */ + virtual int Service (void); + + /* + ** Queue utility routines. The application can determine how many + ** messages are in the send/receive queues, and the queue's average + ** response time (in clock ticks). + */ + int Num_Send(void); + int Num_Receive(void); + virtual unsigned long Response_Time(void); + virtual void Reset_Response_Time(void); + void * Oldest_Send(void); + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh = 0); +#endif + + /* + ** These are for compatibility + */ + virtual int Global_Num_Send(void) {return (Num_Send());} + virtual int Global_Num_Receive(void) {return (Num_Receive());} + virtual int Private_Num_Send(int = CONNECTION_NONE) + {return (Num_Send());} + virtual int Private_Num_Receive(int = CONNECTION_NONE) + {return (Num_Receive());} + + DetectPortType Detect_Port( SerialSettingsType *settings ); + int Detect_Modem( SerialSettingsType *settings, int reconnect = 0 ); + DialStatusType Dial_Modem(char *string, DialMethodType method, int reconnect = 0); + DialStatusType Answer_Modem(int reconnect = 0); + int Hangup_Modem(void); + void Setup_Modem_Echo(void (*func)(char c)); + void Remove_Modem_Echo(void); + void Print_EchoBuf(void); + void Reset_EchoBuf(void); + static int Abort_Modem(PORT *); + void Setup_Abort_Modem(void); + void Remove_Abort_Modem(void); + + int Change_IRQ_Priority(int irq); + int Get_Modem_Status(void); + int Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ); + int Verify_And_Convert_To_Int( char *buffer ); + + /* + ** Private Interface. + */ + private: + + /* + ** This is a pointer to the NULL-Modem Connection object. + */ + NullModemConnClass *Connection; + int NumConnections; // # connection objects in use + +#ifdef WIN32 + /* + ** This is the Win95 port handle + */ + HANDLE PortHandle; +#else //WIN32 + /* + ** This is the Greenleaf port handle. + */ + PORT *Port; +#endif //WIN32 + + int NumSend; + int NumReceive; + unsigned short MagicNum; + + /* + ** This is the staging buffer for parsing incoming packets. + ** RXSize is the allocated size of the RX buffer. + ** RXCount is the # of characters we currently have in our buffer. + */ + char *RXBuf; + int RXSize; + int RXCount; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /* + ** Various Statistics + */ + int SendOverflows; + int ReceiveOverflows; + int CRCErrors; +}; + +#endif + +/*************************** end of nullmgr.h ******************************/ diff --git a/REDALERT/OBJECT.CPP b/REDALERT/OBJECT.CPP new file mode 100644 index 000000000..1322c4a92 --- /dev/null +++ b/REDALERT/OBJECT.CPP @@ -0,0 +1,2488 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OBJECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ObjectClass::AI -- Handles generic object AI processing. * + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * ObjectClass::Detach -- Detach the specified target from this object. * + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * ObjectClass::Init -- Initializes the basic object system. * + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * ObjectClass::Mark -- Handles basic marking logic. * + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * ObjectClass::Name -- Fetches the identification name of this object. * + * ObjectClass::ObjectClass -- Default constructor for objects. * + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * ObjectClass::Passive_Click_With -- Right mouse button click process. * + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * ObjectClass::Render -- Displays the object onto the map. * + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * ObjectClass::Repair -- Handles object repair control. * + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * ObjectClass::Scatter -- Tries to scatter this object. * + * ObjectClass::Select -- Try to make this object the "selected" object. * + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * ObjectClass::Take_Damage -- Applies damage to the object. * + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * ObjectClass::Value -- Fetches the target value of this object. * + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object* + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** Selected objects have a special marking box around them. This is the shapes that are +** used for this purpose. +*/ +void const * ObjectTypeClass::SelectShapes = 0; + +void const * ObjectTypeClass::PipShapes = 0; + + + +/*********************************************************************************************** + * ObjectClass::ObjectClass -- Default constructor for objects. * + * * + * This is the default constructor for objects. It is called as an inherent part of the * + * construction process for all the normal game objects instantiated. It serves merely to * + * initialize the object values to a common (default) state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Objects always start in a state of limbo. They must be Unlimbo()ed before they * + * can be used. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass::ObjectClass(RTTIType rtti, int id) : + AbstractClass(rtti, id), + IsDown(false), + IsToDamage(false), + IsToDisplay(false), + IsInLimbo(true), + IsSelected(false), + IsAnimAttached(false), + IsFalling(false), + Riser(0), + Next(0), + Trigger(NULL), + Strength(255), + IsSelectedMask(0) // Mask showing who has selected this object +{ +} + + +/*********************************************************************************************** + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * * + * This routine will return with a pointer to the image data that should be used when * + * this object is drawn. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * ObjectClass::Get_Image_Data(void) const +{ + return(Class_Of().Get_Image_Data()); +} + + +/*********************************************************************************************** + * ObjectClass::Name -- Fetches the identification name of this object. * + * * + * This routine will return a pointer to the identifier name for this object. This name * + * is usually short and is used in the INI files to identify this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text identifier name of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +char const * ObjectClass::Name(void) const +{ + return(Class_Of().Name()); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * * + * Ths exit coordinate is the location that a piggy back or newly produced object will * + * appear at when it exits this object. Transports and factory buildings will utilize this * + * routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that an object will appear at when exiting this * + * object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Exit_Coord(void) const +{ + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::AI -- Handles generic object AI processing. * + * * + * This routine is used to handle the AI processing that occurs for all object types. * + * Typically, this isn't much, but there is the concept of falling that all objects can * + * be subjected to (e.g., grenades). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::AI(void) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Falling logic is handled here. + */ + if (IsFalling) { + LayerType layer = In_Which_Layer(); + + Height += Riser; + if (Height <= 0) { + Height = 0; + IsFalling = false; + Per_Cell_Process(PCP_END); + + Shorten_Attached_Anims(this); + } + if (IsAnimAttached) { + Riser -= 1; + Riser = max(Riser, -3); + } else { + Riser -= Rule.Gravity; +// Riser -= GRAVITY; + Riser = max(Riser, -100); + } + + if (layer != In_Which_Layer()) { + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + if (Class_Of().IsFootprint) { + if (In_Which_Layer() == LAYER_GROUND) { + Map.Place_Down(Coord_Cell(Center_Coord()), this); + } else { + Map.Pick_Up(Coord_Cell(Center_Coord()), this); + } + } + } + } +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * * + * This routine will return that action that this object could perform if the mouse were * + * clicked over the object specified. * + * * + * INPUT: object -- Pointer to the object to check this object against when determining * + * the action to perform. * + * * + * OUTPUT: It returns that action that will be performed if the mouse were clicked over the * + * object. Since non-derived objects cannot do anything, and cannot even be * + * instantiated, this routine will always return ACTION_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(ObjectClass const *) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * * + * This routine is called when information on a potential action if the mouse were clicked * + * on the cell specified. This routine merely serves as a virtual placeholder so that * + * object types that can actually perform some action will override this routine to provide * + * true functionality. * + * * + * INPUT: cell -- The cell that the mouse is over and might be clicked on. * + * * + * OUTPUT: Returns with the action that this object would try to perform if the mouse were * + * clicked. Since objects at this level have no ability to do anything, this routine * + * will always returns ACTION_NONE unless it is overridden. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(CELL) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * * + * The default layer for object location is the LAYER_GROUND. Aircraft will override this * + * routine and make adjustments as necessary according to the aircraft's altitude. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this object is located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +LayerType ObjectClass::In_Which_Layer(void) const +{ + assert(this != 0); + assert(IsActive); + + if (Height < (FLIGHT_LEVEL - (FLIGHT_LEVEL/3))) { + return(LAYER_GROUND); + } + return(LAYER_TOP); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * * + * This routine will return the ownable bits for this object. Objects at this level can't * + * really be owned by anyone, but return the full spectrum of legality just to be safe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable flags (as a combined bitfield) for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Get_Ownable(void) const +{ + assert(this != 0); + assert(IsActive); + + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * * + * Most objects cannot be repaired. This routine defaults to returning "false", but is * + * overridden by derived functions defined by object types that can support repair. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be repaired? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Repair(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * * + * This routine is used to determine if this object can be sold. Most objects cannot be * + * but for those objects that can, this routine will be overridden as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be sold back? Typically, the answer is no. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Demolish(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +bool ObjectClass::Can_Demolish_Unit(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * * + * This routine is used to determine if attacking is an option under player control with * + * respect to this unit. This routine will be overridden as necessary for those objects * + * that have the ability to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given an attack order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Fire(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * * + * This routine is used to determine if the player has the ability to command this object * + * with a movement mission. This routine will be overridden as necessary to support this * + * ability. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given a movement mission by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Move(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * * + * When the coordinate to use when firing at this object is needed, this routine will * + * provide it. Normal objects just use the center of the object for this, but there are * + * some more sophisticated objects that are not fired upon the center. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire at if this object is a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Target_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Center_Coord())); +// return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * * + * This routine will return the center coordinate for the object. The center coordinate is * + * typically the coordinate recorded in the object structure. Exceptions to this include * + * the trees and other terrain elements. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate that is considered the center point of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Center_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * * + * This routine will return the coordinate to base the drawing of this object's graphic * + * at. This is adjusted according to the nature of the graphic associated with this * + * object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate to draw the graphic of this object at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Render_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * * + * This routine returns the coordinate that a potential docking object should home in on. * + * Typically, this the center of the object, but in certain cases it is adjusted off center * + * according to the object type. An example of this would be the airfield. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that a docking object should head for. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Docking_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * * + * This routine will return the value to be used for object sorting. The sorting ensures * + * that the object are rendered from a top to bottom order. Certain object use a sorting * + * value different from their center coordinate. This is true if the object "touches the * + * ground" at a point that is different from the object's center point. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value to use as the Y sorting value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * * + * For those objects that fire, the coordinate that the projectile it launches will appear * + * at the location specified by the return value from this function. * + * * + * INPUT: which -- Which weapon to consider when determining fire coordinate? * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the coordinate that a launched projectile will appear at if this * + * object were to fire the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Fire_Coord(int ) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * * + * This routine is called when this object is killed. If the source of the death is known, * + * then a pointer to the responsible object is provided as a parameter. * + * * + * INPUT: source -- Pointer to the cause of this unit's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Record_The_Kill(TechnoClass * ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * * + * When an object is cloaked, there are several conditions that would cause it to shimmer * + * and thus reveal itself. When such a condition arrises, this function is called. If the * + * object is cloaked, then it will shimmer. At this derivation level, cloaking is * + * undefined. Objects that can cloak will override this function as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Do_Shimmer(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * * + * This routine is called, typically, by a transport building type that requires an object * + * to leave it. This routine will place the object at a suitable location or return * + * a value indicating why not. * + * * + * INPUT: object -- Pointer to the object that wishes to leave this object. * + * * + * OUTPUT: Returns the success value of the attempt: * + * 0: Object could not be placed -- ever * + * 1: Object placement is temporarily delayed -- try again later. * + * 2: Object placement proceeded normally * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Exit_Object(TechnoClass *) +{ + assert(this != 0); + assert(IsActive); + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * * + * This routine is called when the object becomes hidden from the player. It can result in * + * lost targeting and tracking abilities with respect to the hidden object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Hidden(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * * + * This routine is called when the object needs to look around the terrain. For player * + * owned objects, the terrain is revealed. For non-player objects, not effect occurs. * + * * + * INPUT: incremental -- If true, then the looking algorithm will only examine the edges * + * of the sight range. This is more efficient and work well if the * + * object has only moved one cell since the last time it has performed * + * the look operation. * + * * + * OUTPUT: none * + * * + * WARNINGS: This can be a time consuming operation. Call only when necessary. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Look(bool ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * * + * This routine is called when this object is selected and the mouse was clicked on the * + * tactical map. An action is required from the object. The object that the mouse was * + * over and the tentative action to perform are provided as parameters. * + * * + * INPUT: action -- The requested action to perform with the object specified. * + * * + * object -- The object that the action should be performed on. This object is * + * what the mouse was over when the click occurred. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , ObjectClass *) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * * + * This routine will dispatch the action requested upon the cell specified. It is called * + * when the mouse is clicked over a cell while this object is selected. * + * * + * INPUT: action -- The action to perform. * + * * + * cell -- The location (cell) to perform this action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , CELL ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * * + * This routine is called when this object is the target of some player click action. * + * For more sophisticated object, this will trigger the object to begin flashing a few * + * times. At this level, no action is performed. * + * * + * INPUT: flashes -- The requested number of times to flash this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Clicked_As_Target(HousesType house, int) // 2019/09/20 JAS - Added record of who clicked on the object +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * * + * This routine will determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to check to see if it is within weapon range. * + * * + * which -- The weapon to check against. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: bool; Is the specified coordinate within weapon range for the weapon type * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::In_Range(COORDINATE , int) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * * + * This routine will return the weapon range according to the type of weapon specified. * + * * + * INPUT: which -- The weapon to fetch the range from. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the range (in leptons) of the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Weapon_Range(int) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Scatter -- Tries to scatter this object. * + * * + * This routine is used when the object should scatter from its current location. It * + * applies to units that have the ability to move. * + * * + * INPUT: coord -- The source of the threat that is causing the scatter. * + * * + * forced-- Whether this scatter attempt is serious and scattering should occur * + * regardless of what is doing now. * + * * + * OUTPUT: none * + * * + * WARNINGS: This may or may not cause the object to scatter. It is merely a request to the * + * object that it would be good if it were to scatter. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Scatter(COORDINATE , bool, bool) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * * + * This routine is called when an animation is attached to this object. It might be a * + * fire animation (hence the name), but it might also be smoke or any other animation * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object caught on fire by this routine? Actually, this is really * + * the answer to this question; "Is this animation attaching to this object * + * that doesn't already have an animation attached?" * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Catch_Fire(void) +{ + assert(this != 0); + assert(IsActive); + + return false; +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * * + * This routine is called if there is an attached animation on this object and that * + * animation has finished. Typically, this is necessary for when trees are on fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Fire_Out(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Value -- Fetches the target value of this object. * + * * + * This routine will return the target value of this object. The higher the number, the * + * better the object will be as a target. This routine is called when searching for * + * targets. Generic objects have no target potential, and this routine returns zero to * + * reflect that. Other object types will override this routine to return the appropriate * + * target value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value of this object as a target. Higher values mean better * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Value(void) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * * + * Generic objects don't have a mission, so this routine will just return MISSION_NONE. * + * However, techno objects do have a mission and this routine is overloaded to handle * + * those objects in order to return the correct mission value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current mission being followed by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +MissionType ObjectClass::Get_Mission(void) const +{ + assert(this != 0); + assert(IsActive); + + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::Repair -- Handles object repair control. * + * * + * This routine will control object repair mode. At the object level, no repair is * + * possible, so it is expected that any object that can repair will override this function * + * as necessary. * + * * + * INPUT: control -- The repair control parameter. * + * 0 = turn repair off * + * 1 = turn repair on * + * -1 = toggle repair state * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Repair(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * * + * This routine is called to sell back the object. Override this routine for the more * + * sophisticated objects that can actually be sold back. Normal objects can't be sold and * + * this routine does nothing as a consequence. * + * * + * INPUT: control -- How to control the sell state of this object. * + * 0 = stop selling. * + * 1 = start selling. * + * -1 = toggle selling state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Sell_Back(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * * + * This routine will instantly move the object one cell in the specified direction. It * + * moves the object by force. This is typically ONLY used by the scenario editor * + * process. * + * * + * INPUT: facing -- The direction to move the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: Naturally, this can cause illegal placement situations -- use with caution. * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Move(FacingType facing) +{ + assert(this != 0); + assert(IsActive); + + COORDINATE coord; + + Mark(MARK_UP); + coord = Adjacent_Cell(Coord, facing); + if (Can_Enter_Cell(Coord_Cell(coord)) == MOVE_OK) { + Coord = coord; + } + Mark(MARK_DOWN); +} + + +// Object selection list is switched with player context for GlyphX. ST - 4/17/2019 9:42AM +extern void Logic_Switch_Player_Context(ObjectClass *object); + +/*********************************************************************************************** + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * * + * This routine brings a currently selected object into an unselected state. This is * + * needed when another object becomes selected as well as if the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect(void) +{ + assert(this != 0); + assert(IsActive); + + //if (IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (Is_Selected_By_Player()) { + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + + IsSelected = false; + + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Unselected_By_Player(); + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + } +} + + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players -- This will un-select the object if it was selected * + * from all players * + * * + * This routine brings a currently selected object into an unselected state for all players.* + * This is needed when the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players(void) +{ + CurrentObject.Delete_All(this); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + IsSelected = false; + IsSelectedMask = 0; + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players_Except_Owner -- This will un-select the object if it was * + * selected for all players except for the object's owner * + * * + * This routine brings a currently selected object into an unselected state for all players.* + * This is needed when the object cloaks. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players_Except_Owner(void) +{ + CurrentObject.Delete_All_Except(this, Owner()); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + int owner_mask = 1 << Owner(); + if (IsSelectedMask & owner_mask) + { + IsSelected = true; + IsSelectedMask = owner_mask; + } + else + { + IsSelected = false; + IsSelectedMask = 0; + } + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + +/*********************************************************************************************** + * ObjectClass::Select -- Try to make this object the "selected" object. * + * * + * This routine is used to make this object into the one that is "selected". A selected * + * object usually displays a floating bar graph and is available to be given orders from * + * the player's I/O. * + * * + * INPUT: allow_mixed -- Allow a mix of player and non-player controlled units? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + * 06/12/1995 JLB : Cannot select a loaner object. * + * 07/23/1995 JLB : Adds to head or tail depending on leader type flag. * + *=============================================================================================*/ +bool ObjectClass::Select(bool allow_mixed) +{ + assert(this != 0); + assert(IsActive); + + //if (!Debug_Map && (IsSelected || !Class_Of().IsSelectable)) { + // return(false); + //} + // Updated to function for multiplayer - 6/26/2019 JAS + if (!Debug_Map && (Is_Selected_By_Player() || !Class_Of().IsSelectable)) { + return(false); + } + + if (!Debug_Map && Can_Player_Move() && Is_Techno() && ((TechnoClass *)this)->IsALoaner) { + return(false); + } + + /* + ** Don't allow selection if the object is still in the air. + */ + if (Height > 0 && (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL || What_Am_I() == RTTI_INFANTRY)) { + return(false); + } + + /* + ** Don't allow selection of object when in building placement mode. + */ + if (Map.PendingObject) { + return(false); + } + + if (!allow_mixed) { + /* + ** If selecting an object of a different house than the player's, make sure that + ** the entire selection list is cleared. + */ + for (int i = 0; i < CurrentObject.Count(); i++) { + HouseClass * tryhptr = HouseClass::As_Pointer(Owner()); + HouseClass * oldhptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + // if (Owner() != CurrentObject[0]->Owner() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + if (oldhptr->IsPlayerControl != tryhptr->IsPlayerControl) { + Unselect_All(); + break; + } + } + } + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + + //IsSelected = true; + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Selected_By_Player(); + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + + return(true); +} + + +/*********************************************************************************************** + * ObjectClass::Render -- Displays the object onto the map. * + * * + * This routine will determine the location of the object and if it is roughly on the * + * visible screen, it will display it. Not displaying objects that are not on the screen * + * will save valuable time. * + * * + * INPUT: bool; Should the render be forced regardless of whether the object is flagged to * + * be redrawn? * + * * + * OUTPUT: bool; Was the draw code called for this object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Render(bool forced) const +{ + assert(this != 0); + assert(IsActive); + + int x, y; + COORDINATE coord = Render_Coord(); + CELL cell = Coord_Cell(coord); + + if (Debug_Map || Debug_Unshroud || ((forced || IsToDisplay) && IsDown && !IsInLimbo)) { + const_cast(this)->IsToDisplay = false; // added const_cast ST - 5/9/2019 + + if (Map.Coord_To_Pixel(coord, x, y)) { + + /* + ** Draw the object itself + */ + Draw_It(x, y, WINDOW_TACTICAL); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the trigger attached to the object. Draw_It is window- + ** relative, so add the window's x-coord to 'x'. + */ + if (Debug_Map && Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, + x + (WinX), y, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_6POINT); + } +#endif + + return(true); + } + } + return(false); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * * + * This routine is used to display the current status of the object class to the mono * + * monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 1);mono->Printf("%-18.18s", Text_String(Full_Name())); + if (Next != NULL) { + mono->Set_Cursor(20, 5);mono->Printf("%08X", Next->As_Target()); + } + if (Trigger.Is_Valid()) { + mono->Text_Print(Trigger->Class->IniName, 11, 3); + } + mono->Set_Cursor(34, 1);mono->Printf("%3d", Strength); + + mono->Fill_Attrib(1, 13, 12, 1, IsDown ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 14, 12, 1, IsToDamage ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 15, 12, 1, IsToDisplay ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 16, 12, 1, IsInLimbo ? MonoClass::INVERSE : MonoClass::NORMAL); + // Updated to function for multiplayer - 6/26/2019 JAS + mono->Fill_Attrib(1, 17, 12, 1, Is_Selected_By_Player() ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 13, 12, 1, IsAnimAttached ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(23, 14);mono->Printf("%d", Riser); + mono->Fill_Attrib(14, 12, 14, 1, IsFalling ? MonoClass::INVERSE : MonoClass::NORMAL); + + AbstractClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * * + * This routine will mark the object and inform the display system * + * that appropriate rendering is needed. Whenever it is determined * + * that an object needs to be redrawn, call this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a subordinate function to the function Mark(). If an object needs to * + * be redrawn it is probably better to call the function Mark(MARK_CHANGE) rather * + * than this function. This function does not inform the map system that * + * overlapping objects are to be redrawn and thus unless you are really sure that * + * this routine should be called, don't. * + * * + * HISTORY: * + * 05/08/1994 JLB : Created. * + * 12/23/1994 JLB : Flags map and flags unit only. * + *=============================================================================================*/ +void ObjectClass::Mark_For_Redraw(void) +{ + assert(this != 0); + assert(IsActive); + + if (!IsToDisplay) { + IsToDisplay = true; + + /* + ** This tells the map rendering logic to "go through the motions" and call the + ** rendering function. In the rendering function, it will sort out what gets + ** rendered and what doesn't. + */ + Map.Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * * + * An object brought into a state of limbo by this routine can be safely deleted. This * + * routine will remove the object from all game lists and tracking systems. It is called * + * prior to deleting the object or placing the object "on ice". * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully placed in limbo? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Limbo(void) +{ + assert(this != 0); + assert(IsActive); + + if (GameActive && !IsInLimbo) { + + //Unselect(); + // Updated to function for multiplayer - 6/26/2019 JAS + Unselect_All_Players(); + + Detach_All(); + Mark(MARK_UP); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Remove the object from the logic processing list. + */ + if (Class_Of().IsSentient) { + Logic.Delete(this); + } + + Hidden(); + IsInLimbo = true; + IsToDisplay = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * * + * This routine will place the object into the game tracking and display systems. It is * + * called as a consequence of creating the object. Every game object must be unlimboed at * + * some point. * + * * + * INPUT: coord -- The coordinate to place the object into the game system. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the game object successfully unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Sets object strength. * + *=============================================================================================*/ +bool ObjectClass::Unlimbo(COORDINATE coord, DirType ) +{ + assert(this != 0); + assert(IsActive); + if (GameActive && IsInLimbo && !IsDown) { + if (ScenarioInit || Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + IsInLimbo = false; + IsToDisplay = false; + Coord = Class_Of().Coord_Fixup(coord); + + if (Mark(MARK_DOWN)) { + if (IsActive) { + + /* + ** Add the object to the appropriate map layer. This layer is used + ** for rendering purposes. + */ + if (In_Which_Layer() != LAYER_NONE) { + Map.Submit(this, In_Which_Layer()); + } + + if (Class_Of().IsSentient) { + Logic.Submit(this); + } + } + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Detach -- Detach the specified target from this object. * + * * + * This routine is called when the object (as specified) is to be removed from the game * + * engine and thus, all references to it must be severed. Typically, the only thing * + * checked for at this level is the attached trigger. * + * * + * INPUT: target -- The target that will be removed from the game system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach(TARGET target, bool ) +{ + if (Trigger.Is_Valid() && Is_Target_Trigger(target) && Trigger->As_Target() == target) { + Attach_Trigger(NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * * + * This routine will take the object and see that it is removed from all miscellaneous * + * tracking systems in the game. This operation is vital when deleting an object. It is * + * necessary so that when the object is removed from the game, existing game objects won't * + * be referencing a now invalid game object. This typically affects the targeting * + * and navigation computers of other game objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_All(bool all) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Unselect this object if it was selected. + */ + //if (all || Owner() != PlayerPtr->Class->House) { + // Unselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + if (all) { + //Unselect(); + // Updated to function for multiplayer - 6/28/2019 JAS + Unselect_All_Players(); + } + else + { + Unselect_All_Players_Except_Owner(); + } + //End of change - JAS 6/28/2019 + + Map.Detach(this); + + /* + ** Remove from targeting computers. + */ + Detach_This_From_All(As_Target(), all); +} + + +/*********************************************************************************************** + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * * + * Any radio message received that applies to objects in general are handled by this * + * routine. Typically, this is the "redraw" message, which occurs when another object is * + * loading or unloading and thus overlapping. * + * * + * INPUT: message -- The message received. * + * * + * OUTPUT: Returns with the appropriate radio response. If the message was recognized, then * + * RADIO_ROGER is returned, otherwise, just RADIO_STATIC is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType ObjectClass::Receive_Message(RadioClass *, RadioMessageType message, long & ) +{ + assert(this != 0); + assert(IsActive); + + switch (message) { + + /* + ** This message serves as a rendering convenience. It lets the system + ** know that there might be a visual conflict and the unit in radio + ** contact should be redrawn. This typically occurs when a vehicle + ** is being unloaded from a hover lander. + */ + case RADIO_REDRAW: + Mark(MARK_CHANGE); + return(RADIO_ROGER); + + default: + break; + } + return(RADIO_STATIC); +} + + +/*********************************************************************************************** + * ObjectClass::Take_Damage -- Applies damage to the object. * + * * + * This routine applies damage to the object according to the damage parameters. It handles * + * reducing the strength of the object and also returns the result of that damage. The * + * result value can be examined to determine if the object was destroyed, greatly damaged, * + * or other results. * + * * + * INPUT: damage -- Reference to the damage number to apply. This number will be adjusted * + * according to defensive armor and distance. Examine this value after * + * the call to determine the actual amount of damage applied. * + * * + * distance -- The distance (in leptons) from the center of the damage causing * + * explosion to the object itself. * + * * + * warhead -- The warhead type that is causing the damage. * + * * + * source -- The perpetrator of this damage. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: Returns the ResultType that indicates what the affect of the damage was. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 12/27/1994 JLB : Trigger event processing for attacked or destroyed. * + * 01/01/1995 JLB : Reduces damage greatly depending on range. * + *=============================================================================================*/ +ResultType ObjectClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(this != 0); + assert(IsActive); + + ResultType result = RESULT_NONE; + int oldstrength = Strength; + + if (oldstrength && damage != 0 && (forced || !Class_Of().IsImmune)) { + int maxstrength = Class_Of().MaxStrength; + + /* + ** Modify damage based on the warhead type and the armor of the object. This results + ** in a reduced damage value, but never below 1 damage point. Unless + ** it's forced damage, in which case we want full damage. + */ + if (!forced /*&& damage > 0*/) { + damage = Modify_Damage(damage, warhead, Class_Of().Armor, distance); + + /* + ** Special hack to ensure that dogs only do damage to intended victim and no + ** damage to others. + */ + if (source && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + } + if (damage == 0) return(RESULT_NONE); + + /* + ** Are we healing/repairing? If so, add strength, but in + ** any case, return that no damage was done. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_AIRCRAFT) { +#else + if (What_Am_I() == RTTI_INFANTRY) { +#endif + Clicked_As_Target(PlayerPtr->Class->House, 7); // 2019/09/20 JAS - Added record of who clicked on the object + Strength -= damage; + if (Strength > maxstrength) { + Strength = maxstrength; + } + } + return(RESULT_NONE); + } + + /* + ** At this point, we KNOW that at least light damage has occurred. + */ + result = RESULT_LIGHT; + + /* + ** A non-fatal blow has occurred. Check to see if the object transitioned to below + ** half strength or if it is now down to one hit point. + */ + if (oldstrength > damage) { + + if (oldstrength >= (maxstrength >> 1) && (oldstrength-damage) < (maxstrength >> 1)) { + result = RESULT_HALF; + } + } else { + + /* + ** When an object is damaged to destruction, it will instead stop at one + ** damage point. This will prolong the damage state as well as + ** give greater satisfaction when it is finally destroyed. + */ + damage = oldstrength; + } + + /* + ** Apply the damage to the object. + */ + Strength = oldstrength - damage; + + /* + ** Check to see if the object is majorly damaged or destroyed. + */ + switch (Strength) { + case 0: + Record_The_Kill(source); + result = RESULT_DESTROYED; + if (this->Is_Techno()) { + if (this == ::As_Object(((TechnoClass *)this)->House->UnitToTeleport)) ((TechnoClass *)this)->House->UnitToTeleport = 0; + } + Detach_All(); + break; + + case 1: + result = RESULT_MAJOR; + break; + + default: + break; + } + + /* + ** Handle any trigger event associated with this object. + */ + if (source && Trigger.Is_Valid() && result != RESULT_DESTROYED) { + Trigger->Spring(TEVENT_ATTACKED, this); + } + + /* + ** If any damage was assessed and this object is selected, then flag + ** the object to be redrawn so that the health bar will be updated. + */ + //if (result != RESULT_NONE && IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (result != RESULT_NONE && Is_Selected_By_Player()) { + Mark(MARK_CHANGE); + } + } + + /* + ** Return with the result of the damage taken. + */ + return(result); +} + + +/*********************************************************************************************** + * ObjectClass::Mark -- Handles basic marking logic. * + * * + * This routine handles the base logic for marking an object up or down on the map. It * + * manages the IsDown flag as well as flagging the object to be redrawn if necessary. * + * Whenever an object is to be marked, it should call this base class function first. If * + * this function returns true, then the higher level function should proceed with its own * + * logic. * + * * + * INPUT: mark -- The marking method to use for this object. It can be either MARK_DOWN, * + * MARK_UP, or MARK_CHANGE. * + * * + * OUTPUT: bool; Was the object marked successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (!IsInLimbo && IsActive) { + + /* + ** A mark for change is always successful UNLESS the object + ** is not placed down or has already been flagged as changed + ** this game frame. + */ + if (mark == MARK_CHANGE || mark == MARK_CHANGE_REDRAW) { + if (IsToDisplay && mark != MARK_CHANGE_REDRAW) return(false); + if (IsDown) { + Mark_For_Redraw(); + return(true); + } + return(false); + } + + /* + ** Handle adding or removing the object in the cells' overlap lists + */ + if (mark == MARK_OVERLAP_UP) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Up(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + if (mark == MARK_OVERLAP_DOWN) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Down(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + + /* + ** It is important to know whether the object is a techno class + ** or not to see if we have to adjust the regional threat ratings + */ + int threat = 0; + HousesType house = HOUSE_NONE; + CELL cell = 0; + TechnoClass * tech; + if (Is_Techno()) { + tech = (TechnoClass *)this; + threat = tech->Risk(); + house = tech->Owner(); + cell = Coord_Cell(Coord); + } else { + tech = NULL; + } + + /* + ** Marking down is only successful if the object isn't already + ** placed down. + */ + if (mark == MARK_DOWN && !IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, threat); + } + IsDown = true; + Mark_For_Redraw(); + return(true); + } + + /* + ** Lifting up is only successful if the object isn't already + ** lifted up from the map. + */ + if (mark == MARK_UP && IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, -threat); + } + if (Class_Of().IsFootprint) { + Map.Overlap_Up(Coord_Cell(Coord), this); + } + IsDown = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Init -- Initializes the basic object system. * + * * + * This routine should be called when the basic object system needs to be initialized. This * + * occurs when the scenario is about to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Init(void) +{ + CurrentObject.Clear_All(); +} + + +/*********************************************************************************************** + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * * + * This routine is called when this object gets revealed to the house specified. * + * * + * INPUT: house -- Pointer to the house that this object is being revealed to. * + * * + * OUTPUT: Was this object revealed for the first time to this house? Generic objects always * + * return true unless an invalid house pointer was specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Revealed(HouseClass * house) +{ + assert(this != 0); + assert(IsActive); + + return(house != NULL); +} + + + + +/*********************************************************************************************** + * ObjectClass::Set_Selected_By_Player -- Set this object as selected by the given player or * + * the default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Selected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + if (((TechnoTypeClass const &)Class_Of()).IsLeader) { + CurrentObject.Add_Head(house, this); + } + else { + CurrentObject.Add(house, this); + } + + int shift = (int)house; + IsSelectedMask |= (1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsSelected = true; + } +} + +/*********************************************************************************************** + * ObjectClass::Set_Unselected_By_Player -- Set this object as unselected by the given player * + * orthe default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Unselected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + CurrentObject.Delete(house, this); + + int shift = (int)house; + IsSelectedMask &= ~(1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsSelected = false; + } +} + +/*********************************************************************************************** + * ObjectClass::Is_Selected_By_Player -- Has this object been selected by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if selected by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +bool ObjectClass::Is_Selected_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int)player->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + else { + int shift = (int)PlayerPtr->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + return false; +} + + + + +/*********************************************************************************************** + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * * + * Call this routine as a replacement for Unlimbo() if the object is to be paradropped onto * + * the playing field. * + * * + * INPUT: coord -- The desired landing coordinate to give the dropping unit. * + * * + * OUTPUT: bool; Was the object successfully unlimboed and has begun paradropping? * + * * + * WARNINGS: The unit may not be successful in paradropping if the desired destination * + * location cannot be occupied by the object. * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Paradrop(COORDINATE coord) +{ + assert(this != 0); + assert(IsActive); + + Height = FLIGHT_LEVEL; + IsFalling = true; + if (Unlimbo(coord, DIR_S)) { + AnimClass * anim = NULL; + + if (What_Am_I() == RTTI_BULLET) { + anim = new AnimClass(ANIM_PARA_BOMB, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } else { + anim = new AnimClass(ANIM_PARACHUTE, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } + + /* + ** If the animation was created, then attach it to this object. + */ + if (anim != NULL) { + anim->Attach_To(this); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * * + * This routine is used to attach the specified trigger to the object. * + * * + * INPUT: trigger -- Pointer to the trigger to attach. If any existing trigger is desired * + * to be detached, then pass NULL to this routine. * + * * + * OUTPUT: bool; Was the trigger attached? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Attach_Trigger(TriggerClass * trigger) +{ + if (Trigger.Is_Valid()) { + TriggerClass * tptr = Trigger; + tptr->AttachCount--; + Trigger = NULL; + } + + if (trigger) { + Trigger = trigger; + trigger->AttachCount++; + return(true); + } + return(false); +} + + +// These can't be made inline (for various reasons). +short const * ObjectClass::Occupy_List(bool placement) const {return(Class_Of().Occupy_List(placement));}; +short const * ObjectClass::Overlap_List(bool ) const {return(Class_Of().Overlap_List());}; +BuildingClass * ObjectClass::Who_Can_Build_Me(bool intheory, bool legal) const {return(Class_Of().Who_Can_Build_Me(intheory, legal, Owner()));}; +fixed ObjectClass::Health_Ratio(void) const {return(fixed(Strength, Class_Of().MaxStrength));}; +int ObjectClass::Full_Name(void) const {return Class_Of().Full_Name();}; + + +//********************************************************************************************** +// MODULE SEPARATION -- ObjectTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * * + * This is the base constructor that is used when constructing the object type classes. * + * Every tangible game piece type calls this constructor for the ObjectTypeClass. This * + * class holds static information that is common to objects in general. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/23/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass::ObjectTypeClass( + RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int name, + char const * ini) : + AbstractTypeClass(rtti, id, name, ini), + IsCrushable(false), + IsStealthy(is_stealthy), + IsSelectable(is_selectable), + IsLegalTarget(is_legal_target), + IsInsignificant(is_insignificant), + IsImmune(is_immune), + IsSentient(is_sentient), + IsFootprint(is_footprint), + Armor(ARMOR_NONE), + MaxStrength(0), + ImageData(0), + RadarIcon(0) +{ + /* + ** Init the DimensionData rect. Not sure how this was ever working before without being allocated. It was just trashing + ** memory later on when the pointer was being dereferenced and written to without being initialized. ST - 8/14/2019 3:15PM + */ + DimensionData = NULL; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * * + * This routine will return the maximum number of pips that can be displayed for this * + * object. When dealing with generic objects, this value is always zero. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pip boxes (empty or otherwise) to display. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Max_Pips(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * * + * This routine will fetch the dimensions of this object expressed as pixels width and * + * pixels height. This information can be used to intelligently update the clipping * + * rectangles. * + * * + * INPUT: width -- Reference to the width variable that will be filled in. * + * * + * height -- Reference to the height variable that will be filled in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::Dimensions(int &width, int &height) const +{ + width = 10; + height = 10; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * * + * This routine will return the cost to purchase this unit. This routine is expected to be * + * overridden by the objects that can actually be purchased. All other object types can * + * simply return zero since this value won't be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the cost of the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Cost_Of(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * * + * This routine will fetch the time in takes to construct this object. Objects that can * + * be constructed will override this routine in order to return a useful value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time units (arbitrary) that it takes to construct this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Time_To_Build(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * * + * This routine will return with the cameo data pointer for this object type. It is * + * expected that objects that can appear on the sidebar will override this routine in order * + * to provide proper cameo data pointer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo shape data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void const * ObjectTypeClass::Get_Cameo_Data(void) const +{ + return(NULL); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * * + * This routine returns a pointer to a simple occupation list for this object. Since at * + * this tier of the object class chain, the exact shape of the object is indeterminate, * + * this function merely returns a single cell occupation list. This actually works for * + * most vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to a simple occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * * + * This function returns a pointer to an overlap list for the object. An overlap list is * + * the offsets from the object's cell to get the cells the imagery overlaps, but is object * + * is not considered to occupy. Since at this stage, the overlap information is not * + * available, this function merely returns a pointer to an empty list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the generic overlap list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Overlap_List(void) const +{ + static short const _list[] = {REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * * + * This routine is used to handle the once per game processing required for object types. * + * This consists of loading any data and initializing any data tables the game requires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk. * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::One_Time(void) +{ + SelectShapes = MFCD::Retrieve("SELECT.SHP"); + + #ifndef NDEBUG + RawFileClass file("PIPS.SHP"); + if (file.Is_Available()) { + PipShapes = Load_Alloc_Data(file); + } else { + PipShapes = MFCD::Retrieve("PIPS.SHP"); + } + #else + PipShapes = MFCD::Retrieve("PIPS.SHP"); + #endif +} + + +/*********************************************************************************************** + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * * + * This routine will scan through all available factory buildings and determine which * + * is capable of building this object type. The scan can be controlled to scan for only * + * factory buildings that are free to produce now or those that could produce this * + * object type if conditions permit. * + * * + * INPUT: intheory -- Should the general (when conditions permit) case be examined to see * + * if a building could build this object type "in theory" even though it * + * might currently be otherwise occupied? * + * * + * legal -- Check for building prerequisite and technology level rules? Usually * + * this would be 'true' for human controlled requests and 'false' for * + * the computer. This is because the computer is usually not under * + * the normal restrictions that the player is under. * + * * + * OUTPUT: Returns with a pointer to the building that can produce the object of this * + * type. If no suitable factory building could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * ObjectTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + if (!intheory && What_Am_I() == RTTI_AIRCRAFTTYPE && ((AircraftTypeClass*)this)->IsFixedWing) { + int num_builders = 0, num_fixed_wings = 0; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI && + building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Get_Ownable()) && + (!legal || building->House->Can_Build(this, building->ActLike))) { + num_builders++; + } + } + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + assert(aircraft != NULL); + + if ( !aircraft->IsInLimbo && + aircraft->House->Class->House == house && + aircraft->Class->IsFixedWing) { + num_fixed_wings++; + } + } + if (num_fixed_wings >= num_builders) { + return NULL; + } + } + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI && + building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Get_Ownable()) && + (!legal || building->House->Can_Build(this, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + // BG: Hack so only kennels can build dogs, and no other, and barracks can + // only build humans and no other. + if (What_Am_I() == RTTI_INFANTRYTYPE) { + InfantryTypeClass * me = (InfantryTypeClass *)this; + if (me->IsDog) { + if (*building == STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } else { + if (*building != STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } else { + + /* + ** HACK ALERT: Helipads can build aircraft and airstrips can build + ** fixed wing craft only. + */ + if (What_Am_I() == RTTI_AIRCRAFTTYPE) { + AircraftTypeClass * air = (AircraftTypeClass *)this; + if ((*building == STRUCT_HELIPAD && !air->IsFixedWing) || (*building == STRUCT_AIRSTRIP && air->IsFixedWing)) { + if (building->IsLeader) return(building); + anybuilding = building; + } + + } else { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } + } + return(anybuilding); +} \ No newline at end of file diff --git a/REDALERT/OBJECT.H b/REDALERT/OBJECT.H new file mode 100644 index 000000000..289f04c81 --- /dev/null +++ b/REDALERT/OBJECT.H @@ -0,0 +1,272 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OBJECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OBJECT_H +#define OBJECT_H + +#include "abstract.h" + +class ObjectClass; +class TechnoClass; +class ObjectTypeClass; +class HouseClass; +class BuildingClass; +class RadioClass; +class TriggerClass; + + +/********************************************************************** +** Every game object (that can exist on the map) is ultimately derived from this object +** class. It holds the common information between all objects. This is primarily the +** object unique ID number and its location in the world. All common operations between +** game objects are represented by virtual functions in this class. +*/ +class ObjectClass : public AbstractClass +{ + public: + /* + ** The object can be in one of two states -- placed down on the map, or not. If the + ** object is placed down on the map, then this flag will be true. + */ + unsigned IsDown:1; + + /* + ** This is a support flag that is only used while building a list of objects to + ** be damaged by a proximity affect (explosion). When this flag is set, this object + ** will not be added to the list of units to damage. When damage is applied to the + ** object, this flag is cleared again. This process ensures that an object is never + ** subject to "double jeopardy". + */ + unsigned IsToDamage:1; + + /* + ** Is this object flagged to be displayed during the next rendering process? This + ** flag could be set by many different circumstances. It is automatically cleared + ** when the object is rerendered. + */ + unsigned IsToDisplay:1; + + /* + ** An object in the game may be valid yet held in a state of "limbo". Units are in such + ** a state if they are being transported or are otherwise "inside" another unit. They can + ** also be in limbo if they have been created but are being held until the proper time + ** for delivery. + */ + unsigned IsInLimbo:1; + + /* + ** When an object is "selected" it is given a floating bar graph or other graphic imagery + ** to display this fact. When the player performs I/O, the actions may have a direct + ** bearing on the actions of the currently selected object. For quick checking purposes, + ** if this object is the one that is "selected", this flag will be true. + */ + unsigned IsSelected:1; + + //Added a mask instead of bool for selecting players. This is because we must now support multiplayer. + // - 6/26/2019 + // Needs more than 16 bits since RA has more than 16 factions. ST - 8/14/2019 12:15PM + unsigned int IsSelectedMask; + + /* + ** If an animation is attached to this object, then this flag will be true. + */ + unsigned IsAnimAttached:1; + + /* + ** If this object should process falling logic, then this flag will be true. Such + ** objects might be ballistic projectiles, grenades, or parachuters. + */ + unsigned IsFalling:1; + int Riser; + + /* + ** Several objects could exist in the same cell list. This is a pointer to the + ** next object in the cell list. The objects in this list are not in any + ** significant order. + */ + SmartPtr Next; + + /* + ** Every object can be assigned a trigger; the same trigger can be assigned + ** to multiple objects. + */ + CCPtr Trigger; + + /* + ** This is the current strength of this object. + */ + short Strength; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + ObjectClass(RTTIType rtti, int id); + ObjectClass(NoInitClass const & x) : AbstractClass(x), Next(x), Trigger(x) {}; + virtual ~ObjectClass(void) {Next = 0;}; + int operator < (ObjectClass const & object) const {return Sort_Y() < object.Sort_Y();}; + int operator > (ObjectClass const & object) const {return Sort_Y() > object.Sort_Y();}; + + /* + ** Object selection control. + */ + static void Init(void); + + /* + ** Query functions. + */ + virtual bool Is_Players_Army(void) const {return(false);} + virtual void const * Get_Image_Data(void) const; + virtual ActionType What_Action(ObjectClass const *) const; + virtual ActionType What_Action(CELL) const; + virtual LayerType In_Which_Layer(void) const; + bool Is_Infantry(void) const {return(RTTI == RTTI_INFANTRY);}; + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRY || RTTI == RTTI_UNIT || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + bool Is_Techno(void) const {return(RTTI == RTTI_BUILDING || RTTI == RTTI_UNIT || RTTI == RTTI_INFANTRY || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + virtual int Get_Ownable(void) const; + virtual ObjectTypeClass const & Class_Of(void) const = 0; + virtual char const * Name(void) const; + virtual int Full_Name(void) const; + virtual bool Can_Repair(void) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual void Detach(TARGET target, bool all=true); + virtual void Detach_All(bool all=true); + virtual void Record_The_Kill(TechnoClass * ); + virtual bool Paradrop(COORDINATE coord); + bool Attach_Trigger(TriggerClass * trigger); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Do_Shimmer(void); + virtual int Exit_Object(TechnoClass *); + virtual bool Render(bool forced) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual fixed Health_Ratio(void) const; + virtual void Draw_It(int x, int y, WindowNumberType ) const = 0; + virtual void Hidden(void); + virtual void Look(bool incremental=false); + virtual bool Mark(MarkType=MARK_CHANGE); + + private: + virtual void Mark_For_Redraw(void); + + public: + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType , ObjectClass *); + virtual void Active_Click_With(ActionType , CELL ); + virtual void Clicked_As_Target(HousesType house, int = 7); // 2019/09/20 JAS - Added record of who clicked on the object + virtual bool Select(bool allow_mixed = false); + virtual void Unselect(void); + + //These selection functions were added to handle the fact that we now need to support + //client-server multiplayer. - JAS 6/26/2019 + virtual void Unselect_All_Players(void); + virtual void Unselect_All_Players_Except_Owner(void); + virtual bool Is_Selected_By_Player(HouseClass *player = NULL) const; + virtual void Set_Selected_By_Player(HouseClass *player = NULL); + virtual void Set_Unselected_By_Player(HouseClass *player = NULL); + + /* + ** Combat related. + */ + virtual bool In_Range(COORDINATE , int=0) const; + virtual int Weapon_Range(int =0) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Scatter(COORDINATE , bool forced=false, bool nokidding=false); + virtual bool Catch_Fire(void); + virtual void Fire_Out(void); + virtual int Value(void) const; + virtual MissionType Get_Mission(void) const; + + /* + ** AI. + */ + virtual void Per_Cell_Process(PCPType) {} + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int ); + virtual void Sell_Back(int ); + virtual void AI(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void Move(FacingType); + + enum {FLIGHT_LEVEL=256}; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/OCIDL.H b/REDALERT/OCIDL.H new file mode 100644 index 000000000..4fdd6df08 --- /dev/null +++ b/REDALERT/OCIDL.H @@ -0,0 +1,8026 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + +/* File created by MIDL compiler version 3.00.39 */ +/* at Sat Jul 13 21:56:58 1996 + */ +/* Compiler settings for c:\oa\src\idl\ocidl.idl: + Oic (OptLev=i1), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data +*/ +//@@MIDL_FILE_HEADING( ) +#include "rpc.h" +#include "rpcndr.h" +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __ocidl_h__ +#define __ocidl_h__ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* Forward Declarations */ + +#ifndef __IEnumConnections_FWD_DEFINED__ +#define __IEnumConnections_FWD_DEFINED__ +typedef interface IEnumConnections IEnumConnections; +#endif /* __IEnumConnections_FWD_DEFINED__ */ + + +#ifndef __IConnectionPoint_FWD_DEFINED__ +#define __IConnectionPoint_FWD_DEFINED__ +typedef interface IConnectionPoint IConnectionPoint; +#endif /* __IConnectionPoint_FWD_DEFINED__ */ + + +#ifndef __IEnumConnectionPoints_FWD_DEFINED__ +#define __IEnumConnectionPoints_FWD_DEFINED__ +typedef interface IEnumConnectionPoints IEnumConnectionPoints; +#endif /* __IEnumConnectionPoints_FWD_DEFINED__ */ + + +#ifndef __IConnectionPointContainer_FWD_DEFINED__ +#define __IConnectionPointContainer_FWD_DEFINED__ +typedef interface IConnectionPointContainer IConnectionPointContainer; +#endif /* __IConnectionPointContainer_FWD_DEFINED__ */ + + +#ifndef __IClassFactory2_FWD_DEFINED__ +#define __IClassFactory2_FWD_DEFINED__ +typedef interface IClassFactory2 IClassFactory2; +#endif /* __IClassFactory2_FWD_DEFINED__ */ + + +#ifndef __IProvideClassInfo_FWD_DEFINED__ +#define __IProvideClassInfo_FWD_DEFINED__ +typedef interface IProvideClassInfo IProvideClassInfo; +#endif /* __IProvideClassInfo_FWD_DEFINED__ */ + + +#ifndef __IProvideClassInfo2_FWD_DEFINED__ +#define __IProvideClassInfo2_FWD_DEFINED__ +typedef interface IProvideClassInfo2 IProvideClassInfo2; +#endif /* __IProvideClassInfo2_FWD_DEFINED__ */ + + +#ifndef __IOleControl_FWD_DEFINED__ +#define __IOleControl_FWD_DEFINED__ +typedef interface IOleControl IOleControl; +#endif /* __IOleControl_FWD_DEFINED__ */ + + +#ifndef __IOleControlSite_FWD_DEFINED__ +#define __IOleControlSite_FWD_DEFINED__ +typedef interface IOleControlSite IOleControlSite; +#endif /* __IOleControlSite_FWD_DEFINED__ */ + + +#ifndef __IPropertyPage_FWD_DEFINED__ +#define __IPropertyPage_FWD_DEFINED__ +typedef interface IPropertyPage IPropertyPage; +#endif /* __IPropertyPage_FWD_DEFINED__ */ + + +#ifndef __IPropertyPage2_FWD_DEFINED__ +#define __IPropertyPage2_FWD_DEFINED__ +typedef interface IPropertyPage2 IPropertyPage2; +#endif /* __IPropertyPage2_FWD_DEFINED__ */ + + +#ifndef __IPropertyPageSite_FWD_DEFINED__ +#define __IPropertyPageSite_FWD_DEFINED__ +typedef interface IPropertyPageSite IPropertyPageSite; +#endif /* __IPropertyPageSite_FWD_DEFINED__ */ + + +#ifndef __IPropertyNotifySink_FWD_DEFINED__ +#define __IPropertyNotifySink_FWD_DEFINED__ +typedef interface IPropertyNotifySink IPropertyNotifySink; +#endif /* __IPropertyNotifySink_FWD_DEFINED__ */ + + +#ifndef __ISpecifyPropertyPages_FWD_DEFINED__ +#define __ISpecifyPropertyPages_FWD_DEFINED__ +typedef interface ISpecifyPropertyPages ISpecifyPropertyPages; +#endif /* __ISpecifyPropertyPages_FWD_DEFINED__ */ + + +#ifndef __IPersistMemory_FWD_DEFINED__ +#define __IPersistMemory_FWD_DEFINED__ +typedef interface IPersistMemory IPersistMemory; +#endif /* __IPersistMemory_FWD_DEFINED__ */ + + +#ifndef __IPersistStreamInit_FWD_DEFINED__ +#define __IPersistStreamInit_FWD_DEFINED__ +typedef interface IPersistStreamInit IPersistStreamInit; +#endif /* __IPersistStreamInit_FWD_DEFINED__ */ + + +#ifndef __IPersistPropertyBag_FWD_DEFINED__ +#define __IPersistPropertyBag_FWD_DEFINED__ +typedef interface IPersistPropertyBag IPersistPropertyBag; +#endif /* __IPersistPropertyBag_FWD_DEFINED__ */ + + +#ifndef __ISimpleFrameSite_FWD_DEFINED__ +#define __ISimpleFrameSite_FWD_DEFINED__ +typedef interface ISimpleFrameSite ISimpleFrameSite; +#endif /* __ISimpleFrameSite_FWD_DEFINED__ */ + + +#ifndef __IFont_FWD_DEFINED__ +#define __IFont_FWD_DEFINED__ +typedef interface IFont IFont; +#endif /* __IFont_FWD_DEFINED__ */ + + +#ifndef __IPicture_FWD_DEFINED__ +#define __IPicture_FWD_DEFINED__ +typedef interface IPicture IPicture; +#endif /* __IPicture_FWD_DEFINED__ */ + + +#ifndef __IFontDisp_FWD_DEFINED__ +#define __IFontDisp_FWD_DEFINED__ +typedef interface IFontDisp IFontDisp; +#endif /* __IFontDisp_FWD_DEFINED__ */ + + +#ifndef __IPictureDisp_FWD_DEFINED__ +#define __IPictureDisp_FWD_DEFINED__ +typedef interface IPictureDisp IPictureDisp; +#endif /* __IPictureDisp_FWD_DEFINED__ */ + + +#ifndef __IAdviseSinkEx_FWD_DEFINED__ +#define __IAdviseSinkEx_FWD_DEFINED__ +typedef interface IAdviseSinkEx IAdviseSinkEx; +#endif /* __IAdviseSinkEx_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceObjectWindowless_FWD_DEFINED__ +#define __IOleInPlaceObjectWindowless_FWD_DEFINED__ +typedef interface IOleInPlaceObjectWindowless IOleInPlaceObjectWindowless; +#endif /* __IOleInPlaceObjectWindowless_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteEx_FWD_DEFINED__ +#define __IOleInPlaceSiteEx_FWD_DEFINED__ +typedef interface IOleInPlaceSiteEx IOleInPlaceSiteEx; +#endif /* __IOleInPlaceSiteEx_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteWindowless_FWD_DEFINED__ +#define __IOleInPlaceSiteWindowless_FWD_DEFINED__ +typedef interface IOleInPlaceSiteWindowless IOleInPlaceSiteWindowless; +#endif /* __IOleInPlaceSiteWindowless_FWD_DEFINED__ */ + + +#ifndef __IViewObjectEx_FWD_DEFINED__ +#define __IViewObjectEx_FWD_DEFINED__ +typedef interface IViewObjectEx IViewObjectEx; +#endif /* __IViewObjectEx_FWD_DEFINED__ */ + + +#ifndef __IOleUndoUnit_FWD_DEFINED__ +#define __IOleUndoUnit_FWD_DEFINED__ +typedef interface IOleUndoUnit IOleUndoUnit; +#endif /* __IOleUndoUnit_FWD_DEFINED__ */ + + +#ifndef __IOleParentUndoUnit_FWD_DEFINED__ +#define __IOleParentUndoUnit_FWD_DEFINED__ +typedef interface IOleParentUndoUnit IOleParentUndoUnit; +#endif /* __IOleParentUndoUnit_FWD_DEFINED__ */ + + +#ifndef __IEnumOleUndoUnits_FWD_DEFINED__ +#define __IEnumOleUndoUnits_FWD_DEFINED__ +typedef interface IEnumOleUndoUnits IEnumOleUndoUnits; +#endif /* __IEnumOleUndoUnits_FWD_DEFINED__ */ + + +#ifndef __IOleUndoManager_FWD_DEFINED__ +#define __IOleUndoManager_FWD_DEFINED__ +typedef interface IOleUndoManager IOleUndoManager; +#endif /* __IOleUndoManager_FWD_DEFINED__ */ + + +#ifndef __IQuickActivate_FWD_DEFINED__ +#define __IQuickActivate_FWD_DEFINED__ +typedef interface IQuickActivate IQuickActivate; +#endif /* __IQuickActivate_FWD_DEFINED__ */ + + +#ifndef __IPointerInactive_FWD_DEFINED__ +#define __IPointerInactive_FWD_DEFINED__ +typedef interface IPointerInactive IPointerInactive; +#endif /* __IPointerInactive_FWD_DEFINED__ */ + + +#ifndef __IObjectWithSite_FWD_DEFINED__ +#define __IObjectWithSite_FWD_DEFINED__ +typedef interface IObjectWithSite IObjectWithSite; +#endif /* __IObjectWithSite_FWD_DEFINED__ */ + + +#ifndef __IErrorLog_FWD_DEFINED__ +#define __IErrorLog_FWD_DEFINED__ +typedef interface IErrorLog IErrorLog; +#endif /* __IErrorLog_FWD_DEFINED__ */ + + +#ifndef __IPropertyBag_FWD_DEFINED__ +#define __IPropertyBag_FWD_DEFINED__ +typedef interface IPropertyBag IPropertyBag; +#endif /* __IPropertyBag_FWD_DEFINED__ */ + + +#ifndef __IPerPropertyBrowsing_FWD_DEFINED__ +#define __IPerPropertyBrowsing_FWD_DEFINED__ +typedef interface IPerPropertyBrowsing IPerPropertyBrowsing; +#endif /* __IPerPropertyBrowsing_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oleidl.h" +#include "oaidl.h" + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +/**************************************** + * Generated header for interface: __MIDL__intf_0000 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [local] */ + + +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 - 1996. +// +//-------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec; + +#ifndef __IOleControlTypes_INTERFACE_DEFINED__ +#define __IOleControlTypes_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControlTypes + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [auto_handle][unique][version] */ + + +typedef /* [v1_enum] */ +enum tagUASFLAGS + { UAS_NORMAL = 0, + UAS_BLOCKED = 0x1, + UAS_NOPARENTENABLE = 0x2, + UAS_MASK = 0x3 + } UASFLAGS; + +/* State values for the DISPID_READYSTATE property */ +typedef /* [v1_enum] */ +enum tagREADYSTATE + { READYSTATE_UNINITIALIZED = 0, + READYSTATE_LOADING = 1, + READYSTATE_LOADED = 2, + READYSTATE_INTERACTIVE = 3, + READYSTATE_COMPLETE = 4 + } READYSTATE; + +typedef /* [represent_as] */ struct tagUserHWND + { + wireHWND pRemHwnd; + } UserHWND; + +typedef /* [represent_as] */ struct tagUserHWND UserHACCEL; + +typedef /* [represent_as] */ struct tagUserHWND UserHDC; + +typedef /* [represent_as] */ struct tagUserHWND UserHFONT; + +typedef /* [represent_as] */ struct tagUserMSG + { + wireHWND pRemHwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + DWORD time; + POINT pt; + } UserMSG; + +typedef /* [represent_as] */ struct tagUserBSTR + { + wireBSTR pBstrBlob; + } UserBSTR; + +typedef struct tagVARIANT_BLOB + { + DWORD clSize; + DWORD rpcReserved; + /* [size_is] */ ULONGLONG ahData[ 1 ]; + } __RPC_FAR *wireVARIANT_BLOB; + +typedef /* [represent_as] */ struct tagUserVARIANT + { + wireVARIANT_BLOB pVarBlob; + } UserVARIANT; + +typedef /* [represent_as] */ struct tagUserEXCEPINFO + { + WORD wCode; + WORD wReserved; + wireBSTR bstrSource; + wireBSTR bstrDescription; + wireBSTR bstrHelpFile; + DWORD dwHelpContext; + ULONG pvReserved; + ULONG pfnDeferredFillIn; + SCODE scode; + } UserEXCEPINFO; + + + +extern RPC_IF_HANDLE IOleControlTypes_v1_0_c_ifspec; +extern RPC_IF_HANDLE IOleControlTypes_v1_0_s_ifspec; +#endif /* __IOleControlTypes_INTERFACE_DEFINED__ */ + +#ifndef __IEnumConnections_INTERFACE_DEFINED__ +#define __IEnumConnections_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumConnections + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IEnumConnections __RPC_FAR *PENUMCONNECTIONS; + +typedef IEnumConnections __RPC_FAR *LPENUMCONNECTIONS; + +typedef struct tagCONNECTDATA + { + IUnknown __RPC_FAR *pUnk; + DWORD dwCookie; + } CONNECTDATA; + +typedef struct tagCONNECTDATA __RPC_FAR *PCONNECTDATA; + +typedef struct tagCONNECTDATA __RPC_FAR *LPCONNECTDATA; + + +EXTERN_C const IID IID_IEnumConnections; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumConnections : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cConnections) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumConnectionsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumConnections __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumConnections __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumConnections __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumConnections __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumConnections __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumConnectionsVtbl; + + interface IEnumConnections + { + CONST_VTBL struct IEnumConnectionsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumConnections_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumConnections_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumConnections_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumConnections_Next(This,cConnections,rgcd,pcFetched) \ + (This)->lpVtbl -> Next(This,cConnections,rgcd,pcFetched) + +#define IEnumConnections_Skip(This,cConnections) \ + (This)->lpVtbl -> Skip(This,cConnections) + +#define IEnumConnections_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumConnections_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnections_RemoteNext_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +void __RPC_STUB IEnumConnections_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Skip_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + +void __RPC_STUB IEnumConnections_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Reset_Proxy( + IEnumConnections __RPC_FAR * This); + + +void __RPC_STUB IEnumConnections_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Clone_Proxy( + IEnumConnections __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumConnections_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumConnections_INTERFACE_DEFINED__ */ + + +#ifndef __IConnectionPoint_INTERFACE_DEFINED__ +#define __IConnectionPoint_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IConnectionPoint + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IConnectionPoint __RPC_FAR *PCONNECTIONPOINT; + +typedef IConnectionPoint __RPC_FAR *LPCONNECTIONPOINT; + + +EXTERN_C const IID IID_IConnectionPoint; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IConnectionPoint : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface( + /* [out] */ IID __RPC_FAR *pIID) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer( + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC) = 0; + + virtual HRESULT STDMETHODCALLTYPE Advise( + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE Unadvise( + /* [in] */ DWORD dwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumConnections( + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IConnectionPointVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IConnectionPoint __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IConnectionPoint __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetConnectionInterface )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IID __RPC_FAR *pIID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetConnectionPointContainer )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Advise )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unadvise )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ DWORD dwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumConnections )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IConnectionPointVtbl; + + interface IConnectionPoint + { + CONST_VTBL struct IConnectionPointVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IConnectionPoint_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IConnectionPoint_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IConnectionPoint_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IConnectionPoint_GetConnectionInterface(This,pIID) \ + (This)->lpVtbl -> GetConnectionInterface(This,pIID) + +#define IConnectionPoint_GetConnectionPointContainer(This,ppCPC) \ + (This)->lpVtbl -> GetConnectionPointContainer(This,ppCPC) + +#define IConnectionPoint_Advise(This,pUnkSink,pdwCookie) \ + (This)->lpVtbl -> Advise(This,pUnkSink,pdwCookie) + +#define IConnectionPoint_Unadvise(This,dwCookie) \ + (This)->lpVtbl -> Unadvise(This,dwCookie) + +#define IConnectionPoint_EnumConnections(This,ppEnum) \ + (This)->lpVtbl -> EnumConnections(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_GetConnectionInterface_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IID __RPC_FAR *pIID); + + +void __RPC_STUB IConnectionPoint_GetConnectionInterface_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_GetConnectionPointContainer_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC); + + +void __RPC_STUB IConnectionPoint_GetConnectionPointContainer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_Advise_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + +void __RPC_STUB IConnectionPoint_Advise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_Unadvise_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [in] */ DWORD dwCookie); + + +void __RPC_STUB IConnectionPoint_Unadvise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_EnumConnections_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IConnectionPoint_EnumConnections_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IConnectionPoint_INTERFACE_DEFINED__ */ + + +#ifndef __IEnumConnectionPoints_INTERFACE_DEFINED__ +#define __IEnumConnectionPoints_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumConnectionPoints + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IEnumConnectionPoints __RPC_FAR *PENUMCONNECTIONPOINTS; + +typedef IEnumConnectionPoints __RPC_FAR *LPENUMCONNECTIONPOINTS; + + +EXTERN_C const IID IID_IEnumConnectionPoints; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumConnectionPoints : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cConnections) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumConnectionPointsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumConnectionPoints __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumConnectionPoints __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumConnectionPoints __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumConnectionPoints __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumConnectionPointsVtbl; + + interface IEnumConnectionPoints + { + CONST_VTBL struct IEnumConnectionPointsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumConnectionPoints_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumConnectionPoints_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumConnectionPoints_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumConnectionPoints_Next(This,cConnections,ppCP,pcFetched) \ + (This)->lpVtbl -> Next(This,cConnections,ppCP,pcFetched) + +#define IEnumConnectionPoints_Skip(This,cConnections) \ + (This)->lpVtbl -> Skip(This,cConnections) + +#define IEnumConnectionPoints_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumConnectionPoints_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_RemoteNext_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +void __RPC_STUB IEnumConnectionPoints_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Skip_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + +void __RPC_STUB IEnumConnectionPoints_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Reset_Proxy( + IEnumConnectionPoints __RPC_FAR * This); + + +void __RPC_STUB IEnumConnectionPoints_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Clone_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumConnectionPoints_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumConnectionPoints_INTERFACE_DEFINED__ */ + + +#ifndef __IConnectionPointContainer_INTERFACE_DEFINED__ +#define __IConnectionPointContainer_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IConnectionPointContainer + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IConnectionPointContainer __RPC_FAR *PCONNECTIONPOINTCONTAINER; + +typedef IConnectionPointContainer __RPC_FAR *LPCONNECTIONPOINTCONTAINER; + + +EXTERN_C const IID IID_IConnectionPointContainer; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IConnectionPointContainer : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints( + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint( + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP) = 0; + + }; + +#else /* C style interface */ + + typedef struct IConnectionPointContainerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IConnectionPointContainer __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IConnectionPointContainer __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumConnectionPoints )( + IConnectionPointContainer __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindConnectionPoint )( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP); + + END_INTERFACE + } IConnectionPointContainerVtbl; + + interface IConnectionPointContainer + { + CONST_VTBL struct IConnectionPointContainerVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IConnectionPointContainer_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IConnectionPointContainer_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IConnectionPointContainer_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IConnectionPointContainer_EnumConnectionPoints(This,ppEnum) \ + (This)->lpVtbl -> EnumConnectionPoints(This,ppEnum) + +#define IConnectionPointContainer_FindConnectionPoint(This,riid,ppCP) \ + (This)->lpVtbl -> FindConnectionPoint(This,riid,ppCP) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IConnectionPointContainer_EnumConnectionPoints_Proxy( + IConnectionPointContainer __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IConnectionPointContainer_EnumConnectionPoints_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPointContainer_FindConnectionPoint_Proxy( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP); + + +void __RPC_STUB IConnectionPointContainer_FindConnectionPoint_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IConnectionPointContainer_INTERFACE_DEFINED__ */ + + +#ifndef __IClassFactory2_INTERFACE_DEFINED__ +#define __IClassFactory2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IClassFactory2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IClassFactory2 __RPC_FAR *LPCLASSFACTORY2; + +typedef struct tagLICINFO + { + LONG cbLicInfo; + BOOL fRuntimeKeyAvail; + BOOL fLicVerified; + } LICINFO; + +typedef struct tagLICINFO __RPC_FAR *LPLICINFO; + + +EXTERN_C const IID IID_IClassFactory2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IClassFactory2 : public IClassFactory + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLicInfo( + /* [out] */ LICINFO __RPC_FAR *pLicInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE RequestLicKey( + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstanceLic( + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj) = 0; + + }; + +#else /* C style interface */ + + typedef struct IClassFactory2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IClassFactory2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IClassFactory2 __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateInstance )( + IClassFactory2 __RPC_FAR * This, + /* [unique][in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockServer )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ BOOL fLock); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLicInfo )( + IClassFactory2 __RPC_FAR * This, + /* [out] */ LICINFO __RPC_FAR *pLicInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLicKey )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateInstanceLic )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj); + + END_INTERFACE + } IClassFactory2Vtbl; + + interface IClassFactory2 + { + CONST_VTBL struct IClassFactory2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IClassFactory2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IClassFactory2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IClassFactory2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IClassFactory2_CreateInstance(This,pUnkOuter,riid,ppvObject) \ + (This)->lpVtbl -> CreateInstance(This,pUnkOuter,riid,ppvObject) + +#define IClassFactory2_LockServer(This,fLock) \ + (This)->lpVtbl -> LockServer(This,fLock) + + +#define IClassFactory2_GetLicInfo(This,pLicInfo) \ + (This)->lpVtbl -> GetLicInfo(This,pLicInfo) + +#define IClassFactory2_RequestLicKey(This,dwReserved,pBstrKey) \ + (This)->lpVtbl -> RequestLicKey(This,dwReserved,pBstrKey) + +#define IClassFactory2_CreateInstanceLic(This,pUnkOuter,pUnkReserved,riid,bstrKey,ppvObj) \ + (This)->lpVtbl -> CreateInstanceLic(This,pUnkOuter,pUnkReserved,riid,bstrKey,ppvObj) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IClassFactory2_GetLicInfo_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [out] */ LICINFO __RPC_FAR *pLicInfo); + + +void __RPC_STUB IClassFactory2_GetLicInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IClassFactory2_RequestLicKey_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey); + + +void __RPC_STUB IClassFactory2_RequestLicKey_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IClassFactory2_RemoteCreateInstanceLic_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObj); + + +void __RPC_STUB IClassFactory2_RemoteCreateInstanceLic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IClassFactory2_INTERFACE_DEFINED__ */ + + +#ifndef __IProvideClassInfo_INTERFACE_DEFINED__ +#define __IProvideClassInfo_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IProvideClassInfo + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IProvideClassInfo __RPC_FAR *LPPROVIDECLASSINFO; + + +EXTERN_C const IID IID_IProvideClassInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IProvideClassInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetClassInfo( + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI) = 0; + + }; + +#else /* C style interface */ + + typedef struct IProvideClassInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IProvideClassInfo __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IProvideClassInfo __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IProvideClassInfo __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassInfo )( + IProvideClassInfo __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + END_INTERFACE + } IProvideClassInfoVtbl; + + interface IProvideClassInfo + { + CONST_VTBL struct IProvideClassInfoVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IProvideClassInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IProvideClassInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IProvideClassInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IProvideClassInfo_GetClassInfo(This,ppTI) \ + (This)->lpVtbl -> GetClassInfo(This,ppTI) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IProvideClassInfo_GetClassInfo_Proxy( + IProvideClassInfo __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + +void __RPC_STUB IProvideClassInfo_GetClassInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IProvideClassInfo_INTERFACE_DEFINED__ */ + + +#ifndef __IProvideClassInfo2_INTERFACE_DEFINED__ +#define __IProvideClassInfo2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IProvideClassInfo2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IProvideClassInfo2 __RPC_FAR *LPPROVIDECLASSINFO2; + +typedef +enum tagGUIDKIND + { GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1 + } GUIDKIND; + + +EXTERN_C const IID IID_IProvideClassInfo2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IProvideClassInfo2 : public IProvideClassInfo + { + public: + virtual HRESULT STDMETHODCALLTYPE GetGUID( + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IProvideClassInfo2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IProvideClassInfo2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IProvideClassInfo2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassInfo )( + IProvideClassInfo2 __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetGUID )( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID); + + END_INTERFACE + } IProvideClassInfo2Vtbl; + + interface IProvideClassInfo2 + { + CONST_VTBL struct IProvideClassInfo2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IProvideClassInfo2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IProvideClassInfo2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IProvideClassInfo2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IProvideClassInfo2_GetClassInfo(This,ppTI) \ + (This)->lpVtbl -> GetClassInfo(This,ppTI) + + +#define IProvideClassInfo2_GetGUID(This,dwGuidKind,pGUID) \ + (This)->lpVtbl -> GetGUID(This,dwGuidKind,pGUID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IProvideClassInfo2_GetGUID_Proxy( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID); + + +void __RPC_STUB IProvideClassInfo2_GetGUID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IProvideClassInfo2_INTERFACE_DEFINED__ */ + + +#ifndef __IOleControl_INTERFACE_DEFINED__ +#define __IOleControl_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControl + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IOleControl __RPC_FAR *LPOLECONTROL; + +typedef struct tagCONTROLINFO + { + ULONG cb; + HACCEL hAccel; + USHORT cAccel; + DWORD dwFlags; + } CONTROLINFO; + +typedef struct tagCONTROLINFO __RPC_FAR *LPCONTROLINFO; + +typedef +enum tagCTRLINFO + { CTRLINFO_EATS_RETURN = 1, + CTRLINFO_EATS_ESCAPE = 2 + } CTRLINFO; + + +EXTERN_C const IID IID_IOleControl; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleControl : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetControlInfo( + /* [out] */ CONTROLINFO __RPC_FAR *pCI) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnMnemonic( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnAmbientPropertyChange( + /* [in] */ DISPID dispID) = 0; + + virtual HRESULT STDMETHODCALLTYPE FreezeEvents( + /* [in] */ BOOL bFreeze) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleControlVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleControl __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleControl __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleControl __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetControlInfo )( + IOleControl __RPC_FAR * This, + /* [out] */ CONTROLINFO __RPC_FAR *pCI); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnMnemonic )( + IOleControl __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnAmbientPropertyChange )( + IOleControl __RPC_FAR * This, + /* [in] */ DISPID dispID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FreezeEvents )( + IOleControl __RPC_FAR * This, + /* [in] */ BOOL bFreeze); + + END_INTERFACE + } IOleControlVtbl; + + interface IOleControl + { + CONST_VTBL struct IOleControlVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleControl_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleControl_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleControl_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleControl_GetControlInfo(This,pCI) \ + (This)->lpVtbl -> GetControlInfo(This,pCI) + +#define IOleControl_OnMnemonic(This,pMsg) \ + (This)->lpVtbl -> OnMnemonic(This,pMsg) + +#define IOleControl_OnAmbientPropertyChange(This,dispID) \ + (This)->lpVtbl -> OnAmbientPropertyChange(This,dispID) + +#define IOleControl_FreezeEvents(This,bFreeze) \ + (This)->lpVtbl -> FreezeEvents(This,bFreeze) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleControl_GetControlInfo_Proxy( + IOleControl __RPC_FAR * This, + /* [out] */ CONTROLINFO __RPC_FAR *pCI); + + +void __RPC_STUB IOleControl_GetControlInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_OnMnemonic_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IOleControl_OnMnemonic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_OnAmbientPropertyChange_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IOleControl_OnAmbientPropertyChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_FreezeEvents_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ BOOL bFreeze); + + +void __RPC_STUB IOleControl_FreezeEvents_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleControl_INTERFACE_DEFINED__ */ + + +#ifndef __IOleControlSite_INTERFACE_DEFINED__ +#define __IOleControlSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControlSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IOleControlSite __RPC_FAR *LPOLECONTROLSITE; + +typedef struct tagPOINTF + { + FLOAT x; + FLOAT y; + } POINTF; + +typedef struct tagPOINTF __RPC_FAR *LPPOINTF; + +typedef +enum tagXFORMCOORDS + { XFORMCOORDS_POSITION = 0x1, + XFORMCOORDS_SIZE = 0x2, + XFORMCOORDS_HIMETRICTOCONTAINER = 0x4, + XFORMCOORDS_CONTAINERTOHIMETRIC = 0x8 + } XFORMCOORDS; + + +EXTERN_C const IID IID_IOleControlSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleControlSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnControlInfoChanged( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE LockInPlaceActive( + /* [in] */ BOOL fLock) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetExtendedControl( + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp) = 0; + + virtual HRESULT STDMETHODCALLTYPE TransformCoords( + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnFocus( + /* [in] */ BOOL fGotFocus) = 0; + + virtual HRESULT STDMETHODCALLTYPE ShowPropertyFrame( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleControlSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleControlSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleControlSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleControlSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnControlInfoChanged )( + IOleControlSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockInPlaceActive )( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fLock); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetExtendedControl )( + IOleControlSite __RPC_FAR * This, + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TransformCoords )( + IOleControlSite __RPC_FAR * This, + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IOleControlSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnFocus )( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fGotFocus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ShowPropertyFrame )( + IOleControlSite __RPC_FAR * This); + + END_INTERFACE + } IOleControlSiteVtbl; + + interface IOleControlSite + { + CONST_VTBL struct IOleControlSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleControlSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleControlSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleControlSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleControlSite_OnControlInfoChanged(This) \ + (This)->lpVtbl -> OnControlInfoChanged(This) + +#define IOleControlSite_LockInPlaceActive(This,fLock) \ + (This)->lpVtbl -> LockInPlaceActive(This,fLock) + +#define IOleControlSite_GetExtendedControl(This,ppDisp) \ + (This)->lpVtbl -> GetExtendedControl(This,ppDisp) + +#define IOleControlSite_TransformCoords(This,pPtlHimetric,pPtfContainer,dwFlags) \ + (This)->lpVtbl -> TransformCoords(This,pPtlHimetric,pPtfContainer,dwFlags) + +#define IOleControlSite_TranslateAccelerator(This,pMsg,grfModifiers) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg,grfModifiers) + +#define IOleControlSite_OnFocus(This,fGotFocus) \ + (This)->lpVtbl -> OnFocus(This,fGotFocus) + +#define IOleControlSite_ShowPropertyFrame(This) \ + (This)->lpVtbl -> ShowPropertyFrame(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleControlSite_OnControlInfoChanged_Proxy( + IOleControlSite __RPC_FAR * This); + + +void __RPC_STUB IOleControlSite_OnControlInfoChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_LockInPlaceActive_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fLock); + + +void __RPC_STUB IOleControlSite_LockInPlaceActive_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_GetExtendedControl_Proxy( + IOleControlSite __RPC_FAR * This, + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp); + + +void __RPC_STUB IOleControlSite_GetExtendedControl_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_TransformCoords_Proxy( + IOleControlSite __RPC_FAR * This, + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IOleControlSite_TransformCoords_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_TranslateAccelerator_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers); + + +void __RPC_STUB IOleControlSite_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_OnFocus_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fGotFocus); + + +void __RPC_STUB IOleControlSite_OnFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_ShowPropertyFrame_Proxy( + IOleControlSite __RPC_FAR * This); + + +void __RPC_STUB IOleControlSite_ShowPropertyFrame_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleControlSite_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPage_INTERFACE_DEFINED__ +#define __IPropertyPage_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPage + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPage __RPC_FAR *LPPROPERTYPAGE; + +typedef struct tagPROPPAGEINFO + { + ULONG cb; + LPOLESTR pszTitle; + SIZE size; + LPOLESTR pszDocString; + LPOLESTR pszHelpFile; + DWORD dwHelpContext; + } PROPPAGEINFO; + +typedef struct tagPROPPAGEINFO __RPC_FAR *LPPROPPAGEINFO; + + +EXTERN_C const IID IID_IPropertyPage; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPage : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetPageSite( + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite) = 0; + + virtual HRESULT STDMETHODCALLTYPE Activate( + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal) = 0; + + virtual HRESULT STDMETHODCALLTYPE Deactivate( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPageInfo( + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetObjects( + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0; + + virtual HRESULT STDMETHODCALLTYPE Show( + /* [in] */ UINT nCmdShow) = 0; + + virtual HRESULT STDMETHODCALLTYPE Move( + /* [in] */ LPCRECT pRect) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsPageDirty( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Apply( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Help( + /* [in] */ LPCOLESTR pszHelpDir) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPageVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPage __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPage __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetPageSite )( + IPropertyPage __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Activate )( + IPropertyPage __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Deactivate )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageInfo )( + IPropertyPage __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjects )( + IPropertyPage __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Show )( + IPropertyPage __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Move )( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsPageDirty )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Apply )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Help )( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPage __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + END_INTERFACE + } IPropertyPageVtbl; + + interface IPropertyPage + { + CONST_VTBL struct IPropertyPageVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPage_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPage_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPage_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPage_SetPageSite(This,pPageSite) \ + (This)->lpVtbl -> SetPageSite(This,pPageSite) + +#define IPropertyPage_Activate(This,hWndParent,pRect,bModal) \ + (This)->lpVtbl -> Activate(This,hWndParent,pRect,bModal) + +#define IPropertyPage_Deactivate(This) \ + (This)->lpVtbl -> Deactivate(This) + +#define IPropertyPage_GetPageInfo(This,pPageInfo) \ + (This)->lpVtbl -> GetPageInfo(This,pPageInfo) + +#define IPropertyPage_SetObjects(This,cObjects,ppUnk) \ + (This)->lpVtbl -> SetObjects(This,cObjects,ppUnk) + +#define IPropertyPage_Show(This,nCmdShow) \ + (This)->lpVtbl -> Show(This,nCmdShow) + +#define IPropertyPage_Move(This,pRect) \ + (This)->lpVtbl -> Move(This,pRect) + +#define IPropertyPage_IsPageDirty(This) \ + (This)->lpVtbl -> IsPageDirty(This) + +#define IPropertyPage_Apply(This) \ + (This)->lpVtbl -> Apply(This) + +#define IPropertyPage_Help(This,pszHelpDir) \ + (This)->lpVtbl -> Help(This,pszHelpDir) + +#define IPropertyPage_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPage_SetPageSite_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + +void __RPC_STUB IPropertyPage_SetPageSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Activate_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + +void __RPC_STUB IPropertyPage_Activate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Deactivate_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_Deactivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_GetPageInfo_Proxy( + IPropertyPage __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + +void __RPC_STUB IPropertyPage_GetPageInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_SetObjects_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + +void __RPC_STUB IPropertyPage_SetObjects_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Show_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + +void __RPC_STUB IPropertyPage_Show_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Move_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + +void __RPC_STUB IPropertyPage_Move_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_IsPageDirty_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_IsPageDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Apply_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_Apply_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Help_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + +void __RPC_STUB IPropertyPage_Help_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_TranslateAccelerator_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IPropertyPage_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPage_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPage2_INTERFACE_DEFINED__ +#define __IPropertyPage2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPage2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPage2 __RPC_FAR *LPPROPERTYPAGE2; + + +EXTERN_C const IID IID_IPropertyPage2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPage2 : public IPropertyPage + { + public: + virtual HRESULT STDMETHODCALLTYPE EditProperty( + /* [in] */ DISPID dispID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPage2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPage2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetPageSite )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Activate )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Deactivate )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageInfo )( + IPropertyPage2 __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjects )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Show )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Move )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsPageDirty )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Apply )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Help )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EditProperty )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ DISPID dispID); + + END_INTERFACE + } IPropertyPage2Vtbl; + + interface IPropertyPage2 + { + CONST_VTBL struct IPropertyPage2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPage2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPage2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPage2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPage2_SetPageSite(This,pPageSite) \ + (This)->lpVtbl -> SetPageSite(This,pPageSite) + +#define IPropertyPage2_Activate(This,hWndParent,pRect,bModal) \ + (This)->lpVtbl -> Activate(This,hWndParent,pRect,bModal) + +#define IPropertyPage2_Deactivate(This) \ + (This)->lpVtbl -> Deactivate(This) + +#define IPropertyPage2_GetPageInfo(This,pPageInfo) \ + (This)->lpVtbl -> GetPageInfo(This,pPageInfo) + +#define IPropertyPage2_SetObjects(This,cObjects,ppUnk) \ + (This)->lpVtbl -> SetObjects(This,cObjects,ppUnk) + +#define IPropertyPage2_Show(This,nCmdShow) \ + (This)->lpVtbl -> Show(This,nCmdShow) + +#define IPropertyPage2_Move(This,pRect) \ + (This)->lpVtbl -> Move(This,pRect) + +#define IPropertyPage2_IsPageDirty(This) \ + (This)->lpVtbl -> IsPageDirty(This) + +#define IPropertyPage2_Apply(This) \ + (This)->lpVtbl -> Apply(This) + +#define IPropertyPage2_Help(This,pszHelpDir) \ + (This)->lpVtbl -> Help(This,pszHelpDir) + +#define IPropertyPage2_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + + +#define IPropertyPage2_EditProperty(This,dispID) \ + (This)->lpVtbl -> EditProperty(This,dispID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPage2_EditProperty_Proxy( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyPage2_EditProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPage2_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPageSite_INTERFACE_DEFINED__ +#define __IPropertyPageSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPageSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPageSite __RPC_FAR *LPPROPERTYPAGESITE; + +typedef +enum tagPROPPAGESTATUS + { PROPPAGESTATUS_DIRTY = 0x1, + PROPPAGESTATUS_VALIDATE = 0x2, + PROPPAGESTATUS_CLEAN = 0x4 + } PROPPAGESTATUS; + + +EXTERN_C const IID IID_IPropertyPageSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPageSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnStatusChange( + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLocaleID( + /* [out] */ LCID __RPC_FAR *pLocaleID) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPageContainer( + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPageSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPageSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPageSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnStatusChange )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLocaleID )( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ LCID __RPC_FAR *pLocaleID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageContainer )( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + END_INTERFACE + } IPropertyPageSiteVtbl; + + interface IPropertyPageSite + { + CONST_VTBL struct IPropertyPageSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPageSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPageSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPageSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPageSite_OnStatusChange(This,dwFlags) \ + (This)->lpVtbl -> OnStatusChange(This,dwFlags) + +#define IPropertyPageSite_GetLocaleID(This,pLocaleID) \ + (This)->lpVtbl -> GetLocaleID(This,pLocaleID) + +#define IPropertyPageSite_GetPageContainer(This,ppUnk) \ + (This)->lpVtbl -> GetPageContainer(This,ppUnk) + +#define IPropertyPageSite_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_OnStatusChange_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IPropertyPageSite_OnStatusChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_GetLocaleID_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ LCID __RPC_FAR *pLocaleID); + + +void __RPC_STUB IPropertyPageSite_GetLocaleID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_GetPageContainer_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + +void __RPC_STUB IPropertyPageSite_GetPageContainer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_TranslateAccelerator_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IPropertyPageSite_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPageSite_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyNotifySink_INTERFACE_DEFINED__ +#define __IPropertyNotifySink_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyNotifySink + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyNotifySink __RPC_FAR *LPPROPERTYNOTIFYSINK; + + +EXTERN_C const IID IID_IPropertyNotifySink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyNotifySink : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnChanged( + /* [in] */ DISPID dispID) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnRequestEdit( + /* [in] */ DISPID dispID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyNotifySinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyNotifySink __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyNotifySink __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChanged )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnRequestEdit )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + END_INTERFACE + } IPropertyNotifySinkVtbl; + + interface IPropertyNotifySink + { + CONST_VTBL struct IPropertyNotifySinkVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyNotifySink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyNotifySink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyNotifySink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyNotifySink_OnChanged(This,dispID) \ + (This)->lpVtbl -> OnChanged(This,dispID) + +#define IPropertyNotifySink_OnRequestEdit(This,dispID) \ + (This)->lpVtbl -> OnRequestEdit(This,dispID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyNotifySink_OnChanged_Proxy( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyNotifySink_OnChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyNotifySink_OnRequestEdit_Proxy( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyNotifySink_OnRequestEdit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyNotifySink_INTERFACE_DEFINED__ */ + + +#ifndef __ISpecifyPropertyPages_INTERFACE_DEFINED__ +#define __ISpecifyPropertyPages_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: ISpecifyPropertyPages + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef ISpecifyPropertyPages __RPC_FAR *LPSPECIFYPROPERTYPAGES; + +typedef struct tagCAUUID + { + ULONG cElems; + /* [size_is] */ GUID __RPC_FAR *pElems; + } CAUUID; + +typedef struct tagCAUUID __RPC_FAR *LPCAUUID; + + +EXTERN_C const IID IID_ISpecifyPropertyPages; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface ISpecifyPropertyPages : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPages( + /* [out] */ CAUUID __RPC_FAR *pPages) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISpecifyPropertyPagesVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + ISpecifyPropertyPages __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + ISpecifyPropertyPages __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + ISpecifyPropertyPages __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPages )( + ISpecifyPropertyPages __RPC_FAR * This, + /* [out] */ CAUUID __RPC_FAR *pPages); + + END_INTERFACE + } ISpecifyPropertyPagesVtbl; + + interface ISpecifyPropertyPages + { + CONST_VTBL struct ISpecifyPropertyPagesVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISpecifyPropertyPages_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISpecifyPropertyPages_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISpecifyPropertyPages_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISpecifyPropertyPages_GetPages(This,pPages) \ + (This)->lpVtbl -> GetPages(This,pPages) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISpecifyPropertyPages_GetPages_Proxy( + ISpecifyPropertyPages __RPC_FAR * This, + /* [out] */ CAUUID __RPC_FAR *pPages); + + +void __RPC_STUB ISpecifyPropertyPages_GetPages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISpecifyPropertyPages_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistMemory_INTERFACE_DEFINED__ +#define __IPersistMemory_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistMemory + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistMemory __RPC_FAR *LPPERSISTMEMORY; + + +EXTERN_C const IID IID_IPersistMemory; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistMemory : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Load( + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Save( + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSizeMax( + /* [out] */ ULONG __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistMemoryVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistMemory __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistMemory __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistMemory __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistMemory __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsDirty )( + IPersistMemory __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSizeMax )( + IPersistMemory __RPC_FAR * This, + /* [out] */ ULONG __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistMemory __RPC_FAR * This); + + END_INTERFACE + } IPersistMemoryVtbl; + + interface IPersistMemory + { + CONST_VTBL struct IPersistMemoryVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistMemory_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistMemory_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistMemory_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistMemory_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistMemory_IsDirty(This) \ + (This)->lpVtbl -> IsDirty(This) + +#define IPersistMemory_Load(This,pMem,cbSize) \ + (This)->lpVtbl -> Load(This,pMem,cbSize) + +#define IPersistMemory_Save(This,pMem,fClearDirty,cbSize) \ + (This)->lpVtbl -> Save(This,pMem,fClearDirty,cbSize) + +#define IPersistMemory_GetSizeMax(This,pCbSize) \ + (This)->lpVtbl -> GetSizeMax(This,pCbSize) + +#define IPersistMemory_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistMemory_IsDirty_Proxy( + IPersistMemory __RPC_FAR * This); + + +void __RPC_STUB IPersistMemory_IsDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_RemoteLoad_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ ULONG cbSize); + + +void __RPC_STUB IPersistMemory_RemoteLoad_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_RemoteSave_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + +void __RPC_STUB IPersistMemory_RemoteSave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistMemory_GetSizeMax_Proxy( + IPersistMemory __RPC_FAR * This, + /* [out] */ ULONG __RPC_FAR *pCbSize); + + +void __RPC_STUB IPersistMemory_GetSizeMax_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistMemory_InitNew_Proxy( + IPersistMemory __RPC_FAR * This); + + +void __RPC_STUB IPersistMemory_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistMemory_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistStreamInit_INTERFACE_DEFINED__ +#define __IPersistStreamInit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistStreamInit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistStreamInit __RPC_FAR *LPPERSISTSTREAMINIT; + + +EXTERN_C const IID IID_IPersistStreamInit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistStreamInit : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Load( + /* [in] */ LPSTREAM pStm) = 0; + + virtual HRESULT STDMETHODCALLTYPE Save( + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSizeMax( + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistStreamInitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistStreamInit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistStreamInit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsDirty )( + IPersistStreamInit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSizeMax )( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistStreamInit __RPC_FAR * This); + + END_INTERFACE + } IPersistStreamInitVtbl; + + interface IPersistStreamInit + { + CONST_VTBL struct IPersistStreamInitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistStreamInit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistStreamInit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistStreamInit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistStreamInit_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistStreamInit_IsDirty(This) \ + (This)->lpVtbl -> IsDirty(This) + +#define IPersistStreamInit_Load(This,pStm) \ + (This)->lpVtbl -> Load(This,pStm) + +#define IPersistStreamInit_Save(This,pStm,fClearDirty) \ + (This)->lpVtbl -> Save(This,pStm,fClearDirty) + +#define IPersistStreamInit_GetSizeMax(This,pCbSize) \ + (This)->lpVtbl -> GetSizeMax(This,pCbSize) + +#define IPersistStreamInit_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_IsDirty_Proxy( + IPersistStreamInit __RPC_FAR * This); + + +void __RPC_STUB IPersistStreamInit_IsDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_Load_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm); + + +void __RPC_STUB IPersistStreamInit_Load_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_Save_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty); + + +void __RPC_STUB IPersistStreamInit_Save_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_GetSizeMax_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize); + + +void __RPC_STUB IPersistStreamInit_GetSizeMax_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_InitNew_Proxy( + IPersistStreamInit __RPC_FAR * This); + + +void __RPC_STUB IPersistStreamInit_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistStreamInit_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistPropertyBag_INTERFACE_DEFINED__ +#define __IPersistPropertyBag_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistPropertyBag + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistPropertyBag __RPC_FAR *LPPERSISTPROPERTYBAG; + + +EXTERN_C const IID IID_IPersistPropertyBag; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistPropertyBag : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Load( + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog) = 0; + + virtual HRESULT STDMETHODCALLTYPE Save( + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistPropertyBagVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistPropertyBag __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistPropertyBag __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistPropertyBag __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistPropertyBag __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties); + + END_INTERFACE + } IPersistPropertyBagVtbl; + + interface IPersistPropertyBag + { + CONST_VTBL struct IPersistPropertyBagVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistPropertyBag_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistPropertyBag_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistPropertyBag_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistPropertyBag_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistPropertyBag_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#define IPersistPropertyBag_Load(This,pPropBag,pErrorLog) \ + (This)->lpVtbl -> Load(This,pPropBag,pErrorLog) + +#define IPersistPropertyBag_Save(This,pPropBag,fClearDirty,fSaveAllProperties) \ + (This)->lpVtbl -> Save(This,pPropBag,fClearDirty,fSaveAllProperties) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_InitNew_Proxy( + IPersistPropertyBag __RPC_FAR * This); + + +void __RPC_STUB IPersistPropertyBag_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_Load_Proxy( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + +void __RPC_STUB IPersistPropertyBag_Load_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_Save_Proxy( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties); + + +void __RPC_STUB IPersistPropertyBag_Save_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistPropertyBag_INTERFACE_DEFINED__ */ + + +#ifndef __ISimpleFrameSite_INTERFACE_DEFINED__ +#define __ISimpleFrameSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: ISimpleFrameSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef ISimpleFrameSite __RPC_FAR *LPSIMPLEFRAMESITE; + + +EXTERN_C const IID IID_ISimpleFrameSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface ISimpleFrameSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE PreMessageFilter( + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE PostMessageFilter( + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISimpleFrameSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + ISimpleFrameSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + ISimpleFrameSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PreMessageFilter )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PostMessageFilter )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie); + + END_INTERFACE + } ISimpleFrameSiteVtbl; + + interface ISimpleFrameSite + { + CONST_VTBL struct ISimpleFrameSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISimpleFrameSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISimpleFrameSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISimpleFrameSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISimpleFrameSite_PreMessageFilter(This,hWnd,msg,wp,lp,plResult,pdwCookie) \ + (This)->lpVtbl -> PreMessageFilter(This,hWnd,msg,wp,lp,plResult,pdwCookie) + +#define ISimpleFrameSite_PostMessageFilter(This,hWnd,msg,wp,lp,plResult,dwCookie) \ + (This)->lpVtbl -> PostMessageFilter(This,hWnd,msg,wp,lp,plResult,dwCookie) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISimpleFrameSite_PreMessageFilter_Proxy( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + +void __RPC_STUB ISimpleFrameSite_PreMessageFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISimpleFrameSite_PostMessageFilter_Proxy( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie); + + +void __RPC_STUB ISimpleFrameSite_PostMessageFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISimpleFrameSite_INTERFACE_DEFINED__ */ + + +#ifndef __IFont_INTERFACE_DEFINED__ +#define __IFont_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IFont + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IFont __RPC_FAR *LPFONT; + +#if defined(_WIN32) && !defined(OLE2ANSI) +typedef TEXTMETRICW TEXTMETRICOLE; + +#else +typedef TEXTMETRIC TEXTMETRICOLE; +#endif +typedef TEXTMETRICOLE __RPC_FAR *LPTEXTMETRICOLE; + + +EXTERN_C const IID IID_IFont; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IFont : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE get_Name( + /* [out] */ BSTR __RPC_FAR *pName) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Name( + /* [in] */ BSTR name) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Size( + /* [out] */ CY __RPC_FAR *pSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Size( + /* [in] */ CY size) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Bold( + /* [out] */ BOOL __RPC_FAR *pBold) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Bold( + /* [in] */ BOOL bold) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Italic( + /* [out] */ BOOL __RPC_FAR *pItalic) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Italic( + /* [in] */ BOOL italic) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Underline( + /* [out] */ BOOL __RPC_FAR *pUnderline) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Underline( + /* [in] */ BOOL underline) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Strikethrough( + /* [out] */ BOOL __RPC_FAR *pStrikethrough) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Strikethrough( + /* [in] */ BOOL strikethrough) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Weight( + /* [out] */ SHORT __RPC_FAR *pWeight) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Weight( + /* [in] */ SHORT weight) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Charset( + /* [out] */ SHORT __RPC_FAR *pCharset) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Charset( + /* [in] */ SHORT charset) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_hFont( + /* [out] */ HFONT __RPC_FAR *phFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsEqual( + /* [in] */ IFont __RPC_FAR *pFontOther) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRatio( + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryTextMetrics( + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddRefHfont( + /* [in] */ HFONT hFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReleaseHfont( + /* [in] */ HFONT hFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetHdc( + /* [in] */ HDC hDC) = 0; + + }; + +#else /* C style interface */ + + typedef struct IFontVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IFont __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IFont __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IFont __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )( + IFont __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pName); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Name )( + IFont __RPC_FAR * This, + /* [in] */ BSTR name); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Size )( + IFont __RPC_FAR * This, + /* [out] */ CY __RPC_FAR *pSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Size )( + IFont __RPC_FAR * This, + /* [in] */ CY size); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Bold )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pBold); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Bold )( + IFont __RPC_FAR * This, + /* [in] */ BOOL bold); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Italic )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pItalic); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Italic )( + IFont __RPC_FAR * This, + /* [in] */ BOOL italic); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Underline )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pUnderline); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Underline )( + IFont __RPC_FAR * This, + /* [in] */ BOOL underline); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Strikethrough )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pStrikethrough); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Strikethrough )( + IFont __RPC_FAR * This, + /* [in] */ BOOL strikethrough); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Weight )( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pWeight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Weight )( + IFont __RPC_FAR * This, + /* [in] */ SHORT weight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Charset )( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pCharset); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Charset )( + IFont __RPC_FAR * This, + /* [in] */ SHORT charset); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_hFont )( + IFont __RPC_FAR * This, + /* [out] */ HFONT __RPC_FAR *phFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IFont __RPC_FAR * This, + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsEqual )( + IFont __RPC_FAR * This, + /* [in] */ IFont __RPC_FAR *pFontOther); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetRatio )( + IFont __RPC_FAR * This, + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryTextMetrics )( + IFont __RPC_FAR * This, + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefHfont )( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseHfont )( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetHdc )( + IFont __RPC_FAR * This, + /* [in] */ HDC hDC); + + END_INTERFACE + } IFontVtbl; + + interface IFont + { + CONST_VTBL struct IFontVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IFont_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IFont_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IFont_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IFont_get_Name(This,pName) \ + (This)->lpVtbl -> get_Name(This,pName) + +#define IFont_put_Name(This,name) \ + (This)->lpVtbl -> put_Name(This,name) + +#define IFont_get_Size(This,pSize) \ + (This)->lpVtbl -> get_Size(This,pSize) + +#define IFont_put_Size(This,size) \ + (This)->lpVtbl -> put_Size(This,size) + +#define IFont_get_Bold(This,pBold) \ + (This)->lpVtbl -> get_Bold(This,pBold) + +#define IFont_put_Bold(This,bold) \ + (This)->lpVtbl -> put_Bold(This,bold) + +#define IFont_get_Italic(This,pItalic) \ + (This)->lpVtbl -> get_Italic(This,pItalic) + +#define IFont_put_Italic(This,italic) \ + (This)->lpVtbl -> put_Italic(This,italic) + +#define IFont_get_Underline(This,pUnderline) \ + (This)->lpVtbl -> get_Underline(This,pUnderline) + +#define IFont_put_Underline(This,underline) \ + (This)->lpVtbl -> put_Underline(This,underline) + +#define IFont_get_Strikethrough(This,pStrikethrough) \ + (This)->lpVtbl -> get_Strikethrough(This,pStrikethrough) + +#define IFont_put_Strikethrough(This,strikethrough) \ + (This)->lpVtbl -> put_Strikethrough(This,strikethrough) + +#define IFont_get_Weight(This,pWeight) \ + (This)->lpVtbl -> get_Weight(This,pWeight) + +#define IFont_put_Weight(This,weight) \ + (This)->lpVtbl -> put_Weight(This,weight) + +#define IFont_get_Charset(This,pCharset) \ + (This)->lpVtbl -> get_Charset(This,pCharset) + +#define IFont_put_Charset(This,charset) \ + (This)->lpVtbl -> put_Charset(This,charset) + +#define IFont_get_hFont(This,phFont) \ + (This)->lpVtbl -> get_hFont(This,phFont) + +#define IFont_Clone(This,ppFont) \ + (This)->lpVtbl -> Clone(This,ppFont) + +#define IFont_IsEqual(This,pFontOther) \ + (This)->lpVtbl -> IsEqual(This,pFontOther) + +#define IFont_SetRatio(This,cyLogical,cyHimetric) \ + (This)->lpVtbl -> SetRatio(This,cyLogical,cyHimetric) + +#define IFont_QueryTextMetrics(This,pTM) \ + (This)->lpVtbl -> QueryTextMetrics(This,pTM) + +#define IFont_AddRefHfont(This,hFont) \ + (This)->lpVtbl -> AddRefHfont(This,hFont) + +#define IFont_ReleaseHfont(This,hFont) \ + (This)->lpVtbl -> ReleaseHfont(This,hFont) + +#define IFont_SetHdc(This,hDC) \ + (This)->lpVtbl -> SetHdc(This,hDC) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IFont_get_Name_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pName); + + +void __RPC_STUB IFont_get_Name_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Name_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BSTR name); + + +void __RPC_STUB IFont_put_Name_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Size_Proxy( + IFont __RPC_FAR * This, + /* [out] */ CY __RPC_FAR *pSize); + + +void __RPC_STUB IFont_get_Size_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Size_Proxy( + IFont __RPC_FAR * This, + /* [in] */ CY size); + + +void __RPC_STUB IFont_put_Size_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Bold_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pBold); + + +void __RPC_STUB IFont_get_Bold_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Bold_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL bold); + + +void __RPC_STUB IFont_put_Bold_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Italic_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pItalic); + + +void __RPC_STUB IFont_get_Italic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Italic_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL italic); + + +void __RPC_STUB IFont_put_Italic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Underline_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pUnderline); + + +void __RPC_STUB IFont_get_Underline_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Underline_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL underline); + + +void __RPC_STUB IFont_put_Underline_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Strikethrough_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pStrikethrough); + + +void __RPC_STUB IFont_get_Strikethrough_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Strikethrough_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL strikethrough); + + +void __RPC_STUB IFont_put_Strikethrough_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Weight_Proxy( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pWeight); + + +void __RPC_STUB IFont_get_Weight_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Weight_Proxy( + IFont __RPC_FAR * This, + /* [in] */ SHORT weight); + + +void __RPC_STUB IFont_put_Weight_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Charset_Proxy( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pCharset); + + +void __RPC_STUB IFont_get_Charset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Charset_Proxy( + IFont __RPC_FAR * This, + /* [in] */ SHORT charset); + + +void __RPC_STUB IFont_put_Charset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_hFont_Proxy( + IFont __RPC_FAR * This, + /* [out] */ HFONT __RPC_FAR *phFont); + + +void __RPC_STUB IFont_get_hFont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_Clone_Proxy( + IFont __RPC_FAR * This, + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont); + + +void __RPC_STUB IFont_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_IsEqual_Proxy( + IFont __RPC_FAR * This, + /* [in] */ IFont __RPC_FAR *pFontOther); + + +void __RPC_STUB IFont_IsEqual_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_SetRatio_Proxy( + IFont __RPC_FAR * This, + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric); + + +void __RPC_STUB IFont_SetRatio_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_QueryTextMetrics_Proxy( + IFont __RPC_FAR * This, + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM); + + +void __RPC_STUB IFont_QueryTextMetrics_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_AddRefHfont_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + +void __RPC_STUB IFont_AddRefHfont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_ReleaseHfont_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + +void __RPC_STUB IFont_ReleaseHfont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_SetHdc_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HDC hDC); + + +void __RPC_STUB IFont_SetHdc_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IFont_INTERFACE_DEFINED__ */ + + +#ifndef __IPicture_INTERFACE_DEFINED__ +#define __IPicture_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPicture + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPicture __RPC_FAR *LPPICTURE; + +typedef +enum tagPictureAttributes + { PICTURE_SCALABLE = 0x1, + PICTURE_TRANSPARENT = 0x2 + } PICTUREATTRIBUTES; + +typedef UINT OLE_HANDLE; + +typedef LONG OLE_XPOS_HIMETRIC; + +typedef LONG OLE_YPOS_HIMETRIC; + +typedef LONG OLE_XSIZE_HIMETRIC; + +typedef LONG OLE_YSIZE_HIMETRIC; + + +EXTERN_C const IID IID_IPicture; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPicture : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE get_Handle( + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_hPal( + /* [out] */ OLE_HANDLE __RPC_FAR *phPal) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Type( + /* [out] */ SHORT __RPC_FAR *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Width( + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Height( + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight) = 0; + + virtual HRESULT STDMETHODCALLTYPE Render( + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds) = 0; + + virtual HRESULT STDMETHODCALLTYPE set_hPal( + /* [in] */ OLE_HANDLE hPal) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_CurDC( + /* [out] */ HDC __RPC_FAR *phDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE SelectPicture( + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_KeepOriginalFormat( + /* [out] */ BOOL __RPC_FAR *pKeep) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_KeepOriginalFormat( + /* [in] */ BOOL keep) = 0; + + virtual HRESULT STDMETHODCALLTYPE PictureChanged( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SaveAsFile( + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Attributes( + /* [out] */ DWORD __RPC_FAR *pDwAttr) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPictureVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPicture __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPicture __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPicture __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Handle )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_hPal )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *phPal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Type )( + IPicture __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pType); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Width )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Height )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Render )( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *set_hPal )( + IPicture __RPC_FAR * This, + /* [in] */ OLE_HANDLE hPal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CurDC )( + IPicture __RPC_FAR * This, + /* [out] */ HDC __RPC_FAR *phDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SelectPicture )( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_KeepOriginalFormat )( + IPicture __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pKeep); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_KeepOriginalFormat )( + IPicture __RPC_FAR * This, + /* [in] */ BOOL keep); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PictureChanged )( + IPicture __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SaveAsFile )( + IPicture __RPC_FAR * This, + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )( + IPicture __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pDwAttr); + + END_INTERFACE + } IPictureVtbl; + + interface IPicture + { + CONST_VTBL struct IPictureVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPicture_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPicture_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPicture_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPicture_get_Handle(This,pHandle) \ + (This)->lpVtbl -> get_Handle(This,pHandle) + +#define IPicture_get_hPal(This,phPal) \ + (This)->lpVtbl -> get_hPal(This,phPal) + +#define IPicture_get_Type(This,pType) \ + (This)->lpVtbl -> get_Type(This,pType) + +#define IPicture_get_Width(This,pWidth) \ + (This)->lpVtbl -> get_Width(This,pWidth) + +#define IPicture_get_Height(This,pHeight) \ + (This)->lpVtbl -> get_Height(This,pHeight) + +#define IPicture_Render(This,hDC,x,y,cx,cy,xSrc,ySrc,cxSrc,cySrc,pRcWBounds) \ + (This)->lpVtbl -> Render(This,hDC,x,y,cx,cy,xSrc,ySrc,cxSrc,cySrc,pRcWBounds) + +#define IPicture_set_hPal(This,hPal) \ + (This)->lpVtbl -> set_hPal(This,hPal) + +#define IPicture_get_CurDC(This,phDC) \ + (This)->lpVtbl -> get_CurDC(This,phDC) + +#define IPicture_SelectPicture(This,hDCIn,phDCOut,phBmpOut) \ + (This)->lpVtbl -> SelectPicture(This,hDCIn,phDCOut,phBmpOut) + +#define IPicture_get_KeepOriginalFormat(This,pKeep) \ + (This)->lpVtbl -> get_KeepOriginalFormat(This,pKeep) + +#define IPicture_put_KeepOriginalFormat(This,keep) \ + (This)->lpVtbl -> put_KeepOriginalFormat(This,keep) + +#define IPicture_PictureChanged(This) \ + (This)->lpVtbl -> PictureChanged(This) + +#define IPicture_SaveAsFile(This,pStream,fSaveMemCopy,pCbSize) \ + (This)->lpVtbl -> SaveAsFile(This,pStream,fSaveMemCopy,pCbSize) + +#define IPicture_get_Attributes(This,pDwAttr) \ + (This)->lpVtbl -> get_Attributes(This,pDwAttr) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPicture_get_Handle_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle); + + +void __RPC_STUB IPicture_get_Handle_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_hPal_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *phPal); + + +void __RPC_STUB IPicture_get_hPal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Type_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pType); + + +void __RPC_STUB IPicture_get_Type_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Width_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth); + + +void __RPC_STUB IPicture_get_Width_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Height_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight); + + +void __RPC_STUB IPicture_get_Height_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_Render_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds); + + +void __RPC_STUB IPicture_Render_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_set_hPal_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ OLE_HANDLE hPal); + + +void __RPC_STUB IPicture_set_hPal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_CurDC_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ HDC __RPC_FAR *phDC); + + +void __RPC_STUB IPicture_get_CurDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_SelectPicture_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut); + + +void __RPC_STUB IPicture_SelectPicture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_KeepOriginalFormat_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pKeep); + + +void __RPC_STUB IPicture_get_KeepOriginalFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_put_KeepOriginalFormat_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ BOOL keep); + + +void __RPC_STUB IPicture_put_KeepOriginalFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_PictureChanged_Proxy( + IPicture __RPC_FAR * This); + + +void __RPC_STUB IPicture_PictureChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_SaveAsFile_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize); + + +void __RPC_STUB IPicture_SaveAsFile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Attributes_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pDwAttr); + + +void __RPC_STUB IPicture_get_Attributes_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPicture_INTERFACE_DEFINED__ */ + + +#ifndef __IFontDisp_INTERFACE_DEFINED__ +#define __IFontDisp_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IFontDisp + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IFontDisp __RPC_FAR *LPFONTDISP; + + +EXTERN_C const IID IID_IFontDisp; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IFontDisp : public IDispatch + { + public: + }; + +#else /* C style interface */ + + typedef struct IFontDispVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IFontDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IFontDisp __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IFontDisp __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IFontDisp __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IFontDisp __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IFontDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IFontDisp __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + END_INTERFACE + } IFontDispVtbl; + + interface IFontDisp + { + CONST_VTBL struct IFontDispVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IFontDisp_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IFontDisp_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IFontDisp_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IFontDisp_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IFontDisp_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IFontDisp_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IFontDisp_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IFontDisp_INTERFACE_DEFINED__ */ + + +#ifndef __IPictureDisp_INTERFACE_DEFINED__ +#define __IPictureDisp_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPictureDisp + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPictureDisp __RPC_FAR *LPPICTUREDISP; + + +EXTERN_C const IID IID_IPictureDisp; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPictureDisp : public IDispatch + { + public: + }; + +#else /* C style interface */ + + typedef struct IPictureDispVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPictureDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPictureDisp __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPictureDisp __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IPictureDisp __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IPictureDisp __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IPictureDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IPictureDisp __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + END_INTERFACE + } IPictureDispVtbl; + + interface IPictureDisp + { + CONST_VTBL struct IPictureDispVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPictureDisp_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPictureDisp_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPictureDisp_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPictureDisp_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IPictureDisp_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IPictureDisp_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IPictureDisp_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IPictureDisp_INTERFACE_DEFINED__ */ + + +#ifndef __IAdviseSinkEx_INTERFACE_DEFINED__ +#define __IAdviseSinkEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IAdviseSinkEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IAdviseSinkEx __RPC_FAR *LPADVISESINKEX; + + +EXTERN_C const IID IID_IAdviseSinkEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IAdviseSinkEx : public IAdviseSink + { + public: + virtual /* [local] */ void STDMETHODCALLTYPE OnViewStatusChange( + /* [in] */ DWORD dwViewStatus) = 0; + + }; + +#else /* C style interface */ + + typedef struct IAdviseSinkExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IAdviseSinkEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnDataChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [unique][in] */ FORMATETC __RPC_FAR *pFormatetc, + /* [unique][in] */ STGMEDIUM __RPC_FAR *pStgmed); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnViewChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnRename )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ IMoniker __RPC_FAR *pmk); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnSave )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnClose )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnViewStatusChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + END_INTERFACE + } IAdviseSinkExVtbl; + + interface IAdviseSinkEx + { + CONST_VTBL struct IAdviseSinkExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IAdviseSinkEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IAdviseSinkEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IAdviseSinkEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IAdviseSinkEx_OnDataChange(This,pFormatetc,pStgmed) \ + (This)->lpVtbl -> OnDataChange(This,pFormatetc,pStgmed) + +#define IAdviseSinkEx_OnViewChange(This,dwAspect,lindex) \ + (This)->lpVtbl -> OnViewChange(This,dwAspect,lindex) + +#define IAdviseSinkEx_OnRename(This,pmk) \ + (This)->lpVtbl -> OnRename(This,pmk) + +#define IAdviseSinkEx_OnSave(This) \ + (This)->lpVtbl -> OnSave(This) + +#define IAdviseSinkEx_OnClose(This) \ + (This)->lpVtbl -> OnClose(This) + + +#define IAdviseSinkEx_OnViewStatusChange(This,dwViewStatus) \ + (This)->lpVtbl -> OnViewStatusChange(This,dwViewStatus) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [async][call_as] */ void STDMETHODCALLTYPE IAdviseSinkEx_RemoteOnViewStatusChange_Proxy( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + +void __RPC_STUB IAdviseSinkEx_RemoteOnViewStatusChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IAdviseSinkEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ +#define __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceObjectWindowless + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IOleInPlaceObjectWindowless __RPC_FAR *LPOLEINPLACEOBJECTWINDOWLESS; + + +EXTERN_C const IID IID_IOleInPlaceObjectWindowless; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceObjectWindowless : public IOleInPlaceObject + { + public: + virtual HRESULT STDMETHODCALLTYPE OnWindowMessage( + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDropTarget( + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceObjectWindowlessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InPlaceDeactivate )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UIDeactivate )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjectRects )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect, + /* [in] */ LPCRECT lprcClipRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReactivateAndUndo )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnWindowMessage )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDropTarget )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); + + END_INTERFACE + } IOleInPlaceObjectWindowlessVtbl; + + interface IOleInPlaceObjectWindowless + { + CONST_VTBL struct IOleInPlaceObjectWindowlessVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceObjectWindowless_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceObjectWindowless_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceObjectWindowless_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceObjectWindowless_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceObjectWindowless_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceObjectWindowless_InPlaceDeactivate(This) \ + (This)->lpVtbl -> InPlaceDeactivate(This) + +#define IOleInPlaceObjectWindowless_UIDeactivate(This) \ + (This)->lpVtbl -> UIDeactivate(This) + +#define IOleInPlaceObjectWindowless_SetObjectRects(This,lprcPosRect,lprcClipRect) \ + (This)->lpVtbl -> SetObjectRects(This,lprcPosRect,lprcClipRect) + +#define IOleInPlaceObjectWindowless_ReactivateAndUndo(This) \ + (This)->lpVtbl -> ReactivateAndUndo(This) + + +#define IOleInPlaceObjectWindowless_OnWindowMessage(This,msg,wParam,lParam,plResult) \ + (This)->lpVtbl -> OnWindowMessage(This,msg,wParam,lParam,plResult) + +#define IOleInPlaceObjectWindowless_GetDropTarget(This,ppDropTarget) \ + (This)->lpVtbl -> GetDropTarget(This,ppDropTarget) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceObjectWindowless_OnWindowMessage_Proxy( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + +void __RPC_STUB IOleInPlaceObjectWindowless_OnWindowMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceObjectWindowless_GetDropTarget_Proxy( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); + + +void __RPC_STUB IOleInPlaceObjectWindowless_GetDropTarget_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteEx_INTERFACE_DEFINED__ +#define __IOleInPlaceSiteEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceSiteEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleInPlaceSiteEx __RPC_FAR *LPOLEINPLACESITEEX; + +typedef /* [v1_enum] */ +enum tagACTIVATEFLAGS + { ACTIVATE_WINDOWLESS = 1 + } ACTIVATEFLAGS; + + +EXTERN_C const IID IID_IOleInPlaceSiteEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceSiteEx : public IOleInPlaceSite + { + public: + virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivateEx( + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivateEx( + /* [in] */ BOOL fNoRedraw) = 0; + + virtual HRESULT STDMETHODCALLTYPE RequestUIActivate( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceSiteExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceSiteEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceSiteEx __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanInPlaceActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindowContext )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ IOleInPlaceFrame __RPC_FAR *__RPC_FAR *ppFrame, + /* [out] */ IOleInPlaceUIWindow __RPC_FAR *__RPC_FAR *ppDoc, + /* [out] */ LPRECT lprcPosRect, + /* [out] */ LPRECT lprcClipRect, + /* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Scroll )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ SIZE scrollExtant); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIDeactivate )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fUndoable); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardUndoState )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeactivateAndUndo )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPosRectChange )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivateEx )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivateEx )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUIActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + END_INTERFACE + } IOleInPlaceSiteExVtbl; + + interface IOleInPlaceSiteEx + { + CONST_VTBL struct IOleInPlaceSiteExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceSiteEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceSiteEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceSiteEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceSiteEx_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceSiteEx_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceSiteEx_CanInPlaceActivate(This) \ + (This)->lpVtbl -> CanInPlaceActivate(This) + +#define IOleInPlaceSiteEx_OnInPlaceActivate(This) \ + (This)->lpVtbl -> OnInPlaceActivate(This) + +#define IOleInPlaceSiteEx_OnUIActivate(This) \ + (This)->lpVtbl -> OnUIActivate(This) + +#define IOleInPlaceSiteEx_GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) \ + (This)->lpVtbl -> GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) + +#define IOleInPlaceSiteEx_Scroll(This,scrollExtant) \ + (This)->lpVtbl -> Scroll(This,scrollExtant) + +#define IOleInPlaceSiteEx_OnUIDeactivate(This,fUndoable) \ + (This)->lpVtbl -> OnUIDeactivate(This,fUndoable) + +#define IOleInPlaceSiteEx_OnInPlaceDeactivate(This) \ + (This)->lpVtbl -> OnInPlaceDeactivate(This) + +#define IOleInPlaceSiteEx_DiscardUndoState(This) \ + (This)->lpVtbl -> DiscardUndoState(This) + +#define IOleInPlaceSiteEx_DeactivateAndUndo(This) \ + (This)->lpVtbl -> DeactivateAndUndo(This) + +#define IOleInPlaceSiteEx_OnPosRectChange(This,lprcPosRect) \ + (This)->lpVtbl -> OnPosRectChange(This,lprcPosRect) + + +#define IOleInPlaceSiteEx_OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) \ + (This)->lpVtbl -> OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) + +#define IOleInPlaceSiteEx_OnInPlaceDeactivateEx(This,fNoRedraw) \ + (This)->lpVtbl -> OnInPlaceDeactivateEx(This,fNoRedraw) + +#define IOleInPlaceSiteEx_RequestUIActivate(This) \ + (This)->lpVtbl -> RequestUIActivate(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_OnInPlaceActivateEx_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IOleInPlaceSiteEx_OnInPlaceActivateEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_OnInPlaceDeactivateEx_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + +void __RPC_STUB IOleInPlaceSiteEx_OnInPlaceDeactivateEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_RequestUIActivate_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteEx_RequestUIActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceSiteEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ +#define __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceSiteWindowless + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IOleInPlaceSiteWindowless __RPC_FAR *LPOLEINPLACESITEWINDOWLESS; + +typedef /* [v1_enum] */ +enum tagOLEDCFLAGS + { OLEDC_NODRAW = 0x1, + OLEDC_PAINTBKGND = 0x2, + OLEDC_OFFSCREEN = 0x4 + } OLEDCFLAGS; + + +EXTERN_C const IID IID_IOleInPlaceSiteWindowless; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceSiteWindowless : public IOleInPlaceSiteEx + { + public: + virtual HRESULT STDMETHODCALLTYPE CanWindowlessActivate( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCapture( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCapture( + /* [in] */ BOOL fCapture) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFocus( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetFocus( + /* [in] */ BOOL fFocus) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDC( + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReleaseDC( + /* [in] */ HDC hDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE InvalidateRect( + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase) = 0; + + virtual HRESULT STDMETHODCALLTYPE InvalidateRgn( + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase) = 0; + + virtual HRESULT STDMETHODCALLTYPE ScrollRect( + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip) = 0; + + virtual HRESULT STDMETHODCALLTYPE AdjustRect( + /* [out][in] */ LPRECT prc) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnDefWindowMessage( + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceSiteWindowlessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanInPlaceActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindowContext )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ IOleInPlaceFrame __RPC_FAR *__RPC_FAR *ppFrame, + /* [out] */ IOleInPlaceUIWindow __RPC_FAR *__RPC_FAR *ppDoc, + /* [out] */ LPRECT lprcPosRect, + /* [out] */ LPRECT lprcClipRect, + /* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Scroll )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ SIZE scrollExtant); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIDeactivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fUndoable); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardUndoState )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeactivateAndUndo )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPosRectChange )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivateEx )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivateEx )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUIActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanWindowlessActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCapture )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCapture )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fCapture); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetFocus )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetFocus )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fFocus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDC )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseDC )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HDC hDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InvalidateRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InvalidateRgn )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ScrollRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AdjustRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out][in] */ LPRECT prc); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnDefWindowMessage )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + END_INTERFACE + } IOleInPlaceSiteWindowlessVtbl; + + interface IOleInPlaceSiteWindowless + { + CONST_VTBL struct IOleInPlaceSiteWindowlessVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceSiteWindowless_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceSiteWindowless_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceSiteWindowless_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceSiteWindowless_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceSiteWindowless_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceSiteWindowless_CanInPlaceActivate(This) \ + (This)->lpVtbl -> CanInPlaceActivate(This) + +#define IOleInPlaceSiteWindowless_OnInPlaceActivate(This) \ + (This)->lpVtbl -> OnInPlaceActivate(This) + +#define IOleInPlaceSiteWindowless_OnUIActivate(This) \ + (This)->lpVtbl -> OnUIActivate(This) + +#define IOleInPlaceSiteWindowless_GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) \ + (This)->lpVtbl -> GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) + +#define IOleInPlaceSiteWindowless_Scroll(This,scrollExtant) \ + (This)->lpVtbl -> Scroll(This,scrollExtant) + +#define IOleInPlaceSiteWindowless_OnUIDeactivate(This,fUndoable) \ + (This)->lpVtbl -> OnUIDeactivate(This,fUndoable) + +#define IOleInPlaceSiteWindowless_OnInPlaceDeactivate(This) \ + (This)->lpVtbl -> OnInPlaceDeactivate(This) + +#define IOleInPlaceSiteWindowless_DiscardUndoState(This) \ + (This)->lpVtbl -> DiscardUndoState(This) + +#define IOleInPlaceSiteWindowless_DeactivateAndUndo(This) \ + (This)->lpVtbl -> DeactivateAndUndo(This) + +#define IOleInPlaceSiteWindowless_OnPosRectChange(This,lprcPosRect) \ + (This)->lpVtbl -> OnPosRectChange(This,lprcPosRect) + + +#define IOleInPlaceSiteWindowless_OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) \ + (This)->lpVtbl -> OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) + +#define IOleInPlaceSiteWindowless_OnInPlaceDeactivateEx(This,fNoRedraw) \ + (This)->lpVtbl -> OnInPlaceDeactivateEx(This,fNoRedraw) + +#define IOleInPlaceSiteWindowless_RequestUIActivate(This) \ + (This)->lpVtbl -> RequestUIActivate(This) + + +#define IOleInPlaceSiteWindowless_CanWindowlessActivate(This) \ + (This)->lpVtbl -> CanWindowlessActivate(This) + +#define IOleInPlaceSiteWindowless_GetCapture(This) \ + (This)->lpVtbl -> GetCapture(This) + +#define IOleInPlaceSiteWindowless_SetCapture(This,fCapture) \ + (This)->lpVtbl -> SetCapture(This,fCapture) + +#define IOleInPlaceSiteWindowless_GetFocus(This) \ + (This)->lpVtbl -> GetFocus(This) + +#define IOleInPlaceSiteWindowless_SetFocus(This,fFocus) \ + (This)->lpVtbl -> SetFocus(This,fFocus) + +#define IOleInPlaceSiteWindowless_GetDC(This,pRect,grfFlags,phDC) \ + (This)->lpVtbl -> GetDC(This,pRect,grfFlags,phDC) + +#define IOleInPlaceSiteWindowless_ReleaseDC(This,hDC) \ + (This)->lpVtbl -> ReleaseDC(This,hDC) + +#define IOleInPlaceSiteWindowless_InvalidateRect(This,pRect,fErase) \ + (This)->lpVtbl -> InvalidateRect(This,pRect,fErase) + +#define IOleInPlaceSiteWindowless_InvalidateRgn(This,hRGN,fErase) \ + (This)->lpVtbl -> InvalidateRgn(This,hRGN,fErase) + +#define IOleInPlaceSiteWindowless_ScrollRect(This,dx,dy,pRectScroll,pRectClip) \ + (This)->lpVtbl -> ScrollRect(This,dx,dy,pRectScroll,pRectClip) + +#define IOleInPlaceSiteWindowless_AdjustRect(This,prc) \ + (This)->lpVtbl -> AdjustRect(This,prc) + +#define IOleInPlaceSiteWindowless_OnDefWindowMessage(This,msg,wParam,lParam,plResult) \ + (This)->lpVtbl -> OnDefWindowMessage(This,msg,wParam,lParam,plResult) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_CanWindowlessActivate_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_CanWindowlessActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetCapture_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_SetCapture_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fCapture); + + +void __RPC_STUB IOleInPlaceSiteWindowless_SetCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetFocus_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_SetFocus_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fFocus); + + +void __RPC_STUB IOleInPlaceSiteWindowless_SetFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetDC_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_ReleaseDC_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HDC hDC); + + +void __RPC_STUB IOleInPlaceSiteWindowless_ReleaseDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_InvalidateRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase); + + +void __RPC_STUB IOleInPlaceSiteWindowless_InvalidateRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_InvalidateRgn_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase); + + +void __RPC_STUB IOleInPlaceSiteWindowless_InvalidateRgn_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_ScrollRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip); + + +void __RPC_STUB IOleInPlaceSiteWindowless_ScrollRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_AdjustRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out][in] */ LPRECT prc); + + +void __RPC_STUB IOleInPlaceSiteWindowless_AdjustRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_OnDefWindowMessage_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + +void __RPC_STUB IOleInPlaceSiteWindowless_OnDefWindowMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ */ + + +#ifndef __IViewObjectEx_INTERFACE_DEFINED__ +#define __IViewObjectEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IViewObjectEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IViewObjectEx __RPC_FAR *LPVIEWOBJECTEX; + +typedef /* [v1_enum] */ +enum tagVIEWSTATUS + { VIEWSTATUS_OPAQUE = 1, + VIEWSTATUS_SOLIDBKGND = 2, + VIEWSTATUS_DVASPECTOPAQUE = 4, + VIEWSTATUS_DVASPECTTRANSPARENT = 8 + } VIEWSTATUS; + +typedef /* [v1_enum] */ +enum tagHITRESULT + { HITRESULT_OUTSIDE = 0, + HITRESULT_TRANSPARENT = 1, + HITRESULT_CLOSE = 2, + HITRESULT_HIT = 3 + } HITRESULT; + +typedef /* [v1_enum] */ +enum tagDVASPECT2 + { DVASPECT_OPAQUE = 16, + DVASPECT_TRANSPARENT = 32 + } DVASPECT2; + +typedef struct tagExtentInfo + { + ULONG cb; + DWORD dwExtentMode; + SIZEL sizelProposed; + } DVEXTENTINFO; + +typedef /* [v1_enum] */ +enum tagExtentMode + { DVEXTENT_CONTENT = 0, + DVEXTENT_INTEGRAL = DVEXTENT_CONTENT + 1 + } DVEXTENTMODE; + +typedef /* [v1_enum] */ +enum tagAspectInfoFlag + { DVASPECTINFOFLAG_CANOPTIMIZE = 1 + } DVASPECTINFOFLAG; + +typedef struct tagAspectInfo + { + ULONG cb; + DWORD dwFlags; + } DVASPECTINFO; + + +EXTERN_C const IID IID_IViewObjectEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IViewObjectEx : public IViewObject2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetRect( + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetViewStatus( + /* [out] */ DWORD __RPC_FAR *pdwStatus) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryHitPoint( + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryHitRect( + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNaturalExtent( + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel) = 0; + + }; + +#else /* C style interface */ + + typedef struct IViewObjectExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IViewObjectEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IViewObjectEx __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Draw )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hdcTargetDev, + /* [in] */ HDC hdcDraw, + /* [in] */ LPCRECTL lprcBounds, + /* [unique][in] */ LPCRECTL lprcWBounds, + /* [in] */ BOOL ( STDMETHODCALLTYPE __RPC_FAR *pfnContinue )( + DWORD dwContinue), + /* [in] */ DWORD dwContinue); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetColorSet )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [out] */ LOGPALETTE __RPC_FAR *__RPC_FAR *ppColorSet); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Freeze )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [out] */ DWORD __RPC_FAR *pdwFreeze); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unfreeze )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwFreeze); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetAdvise )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD aspects, + /* [in] */ DWORD advf, + /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAdvise )( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pAspects, + /* [out] */ DWORD __RPC_FAR *pAdvf, + /* [out] */ IAdviseSink __RPC_FAR *__RPC_FAR *ppAdvSink); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetExtent )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [out] */ LPSIZEL lpsizel); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRect )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetViewStatus )( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwStatus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryHitPoint )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryHitRect )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNaturalExtent )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel); + + END_INTERFACE + } IViewObjectExVtbl; + + interface IViewObjectEx + { + CONST_VTBL struct IViewObjectExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IViewObjectEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IViewObjectEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IViewObjectEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IViewObjectEx_Draw(This,dwDrawAspect,lindex,pvAspect,ptd,hdcTargetDev,hdcDraw,lprcBounds,lprcWBounds,pfnContinue,dwContinue) \ + (This)->lpVtbl -> Draw(This,dwDrawAspect,lindex,pvAspect,ptd,hdcTargetDev,hdcDraw,lprcBounds,lprcWBounds,pfnContinue,dwContinue) + +#define IViewObjectEx_GetColorSet(This,dwDrawAspect,lindex,pvAspect,ptd,hicTargetDev,ppColorSet) \ + (This)->lpVtbl -> GetColorSet(This,dwDrawAspect,lindex,pvAspect,ptd,hicTargetDev,ppColorSet) + +#define IViewObjectEx_Freeze(This,dwDrawAspect,lindex,pvAspect,pdwFreeze) \ + (This)->lpVtbl -> Freeze(This,dwDrawAspect,lindex,pvAspect,pdwFreeze) + +#define IViewObjectEx_Unfreeze(This,dwFreeze) \ + (This)->lpVtbl -> Unfreeze(This,dwFreeze) + +#define IViewObjectEx_SetAdvise(This,aspects,advf,pAdvSink) \ + (This)->lpVtbl -> SetAdvise(This,aspects,advf,pAdvSink) + +#define IViewObjectEx_GetAdvise(This,pAspects,pAdvf,ppAdvSink) \ + (This)->lpVtbl -> GetAdvise(This,pAspects,pAdvf,ppAdvSink) + + +#define IViewObjectEx_GetExtent(This,dwDrawAspect,lindex,ptd,lpsizel) \ + (This)->lpVtbl -> GetExtent(This,dwDrawAspect,lindex,ptd,lpsizel) + + +#define IViewObjectEx_GetRect(This,dwAspect,pRect) \ + (This)->lpVtbl -> GetRect(This,dwAspect,pRect) + +#define IViewObjectEx_GetViewStatus(This,pdwStatus) \ + (This)->lpVtbl -> GetViewStatus(This,pdwStatus) + +#define IViewObjectEx_QueryHitPoint(This,dwAspect,pRectBounds,ptlLoc,lCloseHint,pHitResult) \ + (This)->lpVtbl -> QueryHitPoint(This,dwAspect,pRectBounds,ptlLoc,lCloseHint,pHitResult) + +#define IViewObjectEx_QueryHitRect(This,dwAspect,pRectBounds,pRectLoc,lCloseHint,pHitResult) \ + (This)->lpVtbl -> QueryHitRect(This,dwAspect,pRectBounds,pRectLoc,lCloseHint,pHitResult) + +#define IViewObjectEx_GetNaturalExtent(This,dwAspect,lindex,ptd,hicTargetDev,pExtentInfo,pSizel) \ + (This)->lpVtbl -> GetNaturalExtent(This,dwAspect,lindex,ptd,hicTargetDev,pExtentInfo,pSizel) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetRect_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect); + + +void __RPC_STUB IViewObjectEx_GetRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetViewStatus_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwStatus); + + +void __RPC_STUB IViewObjectEx_GetViewStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_QueryHitPoint_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + +void __RPC_STUB IViewObjectEx_QueryHitPoint_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_QueryHitRect_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + +void __RPC_STUB IViewObjectEx_QueryHitRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetNaturalExtent_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel); + + +void __RPC_STUB IViewObjectEx_GetNaturalExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IViewObjectEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleUndoUnit_INTERFACE_DEFINED__ +#define __IOleUndoUnit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleUndoUnit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleUndoUnit __RPC_FAR *LPOLEUNDOUNIT; + + +EXTERN_C const IID IID_IOleUndoUnit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleUndoUnit : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Do( + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetUnitType( + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnNextAdd( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleUndoUnitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleUndoUnit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Do )( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDescription )( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetUnitType )( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNextAdd )( + IOleUndoUnit __RPC_FAR * This); + + END_INTERFACE + } IOleUndoUnitVtbl; + + interface IOleUndoUnit + { + CONST_VTBL struct IOleUndoUnitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleUndoUnit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleUndoUnit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleUndoUnit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleUndoUnit_Do(This,pUndoManager) \ + (This)->lpVtbl -> Do(This,pUndoManager) + +#define IOleUndoUnit_GetDescription(This,pBstr) \ + (This)->lpVtbl -> GetDescription(This,pBstr) + +#define IOleUndoUnit_GetUnitType(This,pClsid,plID) \ + (This)->lpVtbl -> GetUnitType(This,pClsid,plID) + +#define IOleUndoUnit_OnNextAdd(This) \ + (This)->lpVtbl -> OnNextAdd(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_Do_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + +void __RPC_STUB IOleUndoUnit_Do_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_GetDescription_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoUnit_GetDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_GetUnitType_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + +void __RPC_STUB IOleUndoUnit_GetUnitType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_OnNextAdd_Proxy( + IOleUndoUnit __RPC_FAR * This); + + +void __RPC_STUB IOleUndoUnit_OnNextAdd_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleUndoUnit_INTERFACE_DEFINED__ */ + + +#ifndef __IOleParentUndoUnit_INTERFACE_DEFINED__ +#define __IOleParentUndoUnit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleParentUndoUnit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleParentUndoUnit __RPC_FAR *LPOLEPARENTUNDOUNIT; + + +EXTERN_C const IID IID_IOleParentUndoUnit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleParentUndoUnit : public IOleUndoUnit + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit) = 0; + + virtual HRESULT STDMETHODCALLTYPE Add( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindUnit( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetParentState( + /* [out] */ DWORD __RPC_FAR *pdwState) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleParentUndoUnitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleParentUndoUnit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleParentUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Do )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDescription )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetUnitType )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNextAdd )( + IOleParentUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Add )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindUnit )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetParentState )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + END_INTERFACE + } IOleParentUndoUnitVtbl; + + interface IOleParentUndoUnit + { + CONST_VTBL struct IOleParentUndoUnitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleParentUndoUnit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleParentUndoUnit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleParentUndoUnit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleParentUndoUnit_Do(This,pUndoManager) \ + (This)->lpVtbl -> Do(This,pUndoManager) + +#define IOleParentUndoUnit_GetDescription(This,pBstr) \ + (This)->lpVtbl -> GetDescription(This,pBstr) + +#define IOleParentUndoUnit_GetUnitType(This,pClsid,plID) \ + (This)->lpVtbl -> GetUnitType(This,pClsid,plID) + +#define IOleParentUndoUnit_OnNextAdd(This) \ + (This)->lpVtbl -> OnNextAdd(This) + + +#define IOleParentUndoUnit_Open(This,pPUU) \ + (This)->lpVtbl -> Open(This,pPUU) + +#define IOleParentUndoUnit_Close(This,pPUU,fCommit) \ + (This)->lpVtbl -> Close(This,pPUU,fCommit) + +#define IOleParentUndoUnit_Add(This,pUU) \ + (This)->lpVtbl -> Add(This,pUU) + +#define IOleParentUndoUnit_FindUnit(This,pUU) \ + (This)->lpVtbl -> FindUnit(This,pUU) + +#define IOleParentUndoUnit_GetParentState(This,pdwState) \ + (This)->lpVtbl -> GetParentState(This,pdwState) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Open_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + +void __RPC_STUB IOleParentUndoUnit_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Close_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + +void __RPC_STUB IOleParentUndoUnit_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Add_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleParentUndoUnit_Add_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_FindUnit_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleParentUndoUnit_FindUnit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_GetParentState_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + +void __RPC_STUB IOleParentUndoUnit_GetParentState_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleParentUndoUnit_INTERFACE_DEFINED__ */ + + +#ifndef __IEnumOleUndoUnits_INTERFACE_DEFINED__ +#define __IEnumOleUndoUnits_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumOleUndoUnits + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IEnumOleUndoUnits __RPC_FAR *LPENUMOLEUNDOUNITS; + + +EXTERN_C const IID IID_IEnumOleUndoUnits; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumOleUndoUnits : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cElt) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumOleUndoUnitsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumOleUndoUnits __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumOleUndoUnits __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumOleUndoUnits __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumOleUndoUnitsVtbl; + + interface IEnumOleUndoUnits + { + CONST_VTBL struct IEnumOleUndoUnitsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumOleUndoUnits_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumOleUndoUnits_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumOleUndoUnits_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumOleUndoUnits_Next(This,cElt,rgElt,pcEltFetched) \ + (This)->lpVtbl -> Next(This,cElt,rgElt,pcEltFetched) + +#define IEnumOleUndoUnits_Skip(This,cElt) \ + (This)->lpVtbl -> Skip(This,cElt) + +#define IEnumOleUndoUnits_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumOleUndoUnits_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_RemoteNext_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + +void __RPC_STUB IEnumOleUndoUnits_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Skip_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt); + + +void __RPC_STUB IEnumOleUndoUnits_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Reset_Proxy( + IEnumOleUndoUnits __RPC_FAR * This); + + +void __RPC_STUB IEnumOleUndoUnits_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Clone_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumOleUndoUnits_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumOleUndoUnits_INTERFACE_DEFINED__ */ + + +#ifndef __IOleUndoManager_INTERFACE_DEFINED__ +#define __IOleUndoManager_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleUndoManager + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +#define SID_SOleUndoManager IID_IOleUndoManager; +typedef IOleUndoManager __RPC_FAR *LPOLEUNDOMANAGER; + + +EXTERN_C const IID IID_IOleUndoManager; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleUndoManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit) = 0; + + virtual HRESULT STDMETHODCALLTYPE Add( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOpenParentState( + /* [out] */ DWORD __RPC_FAR *pdwState) = 0; + + virtual HRESULT STDMETHODCALLTYPE DiscardFrom( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE UndoTo( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE RedoTo( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumUndoable( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumRedoable( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLastUndoDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLastRedoDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE Enable( + /* [in] */ BOOL fEnable) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleUndoManagerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleUndoManager __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleUndoManager __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Add )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOpenParentState )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardFrom )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UndoTo )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RedoTo )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumUndoable )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumRedoable )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLastUndoDescription )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLastRedoDescription )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Enable )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ BOOL fEnable); + + END_INTERFACE + } IOleUndoManagerVtbl; + + interface IOleUndoManager + { + CONST_VTBL struct IOleUndoManagerVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleUndoManager_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleUndoManager_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleUndoManager_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleUndoManager_Open(This,pPUU) \ + (This)->lpVtbl -> Open(This,pPUU) + +#define IOleUndoManager_Close(This,pPUU,fCommit) \ + (This)->lpVtbl -> Close(This,pPUU,fCommit) + +#define IOleUndoManager_Add(This,pUU) \ + (This)->lpVtbl -> Add(This,pUU) + +#define IOleUndoManager_GetOpenParentState(This,pdwState) \ + (This)->lpVtbl -> GetOpenParentState(This,pdwState) + +#define IOleUndoManager_DiscardFrom(This,pUU) \ + (This)->lpVtbl -> DiscardFrom(This,pUU) + +#define IOleUndoManager_UndoTo(This,pUU) \ + (This)->lpVtbl -> UndoTo(This,pUU) + +#define IOleUndoManager_RedoTo(This,pUU) \ + (This)->lpVtbl -> RedoTo(This,pUU) + +#define IOleUndoManager_EnumUndoable(This,ppEnum) \ + (This)->lpVtbl -> EnumUndoable(This,ppEnum) + +#define IOleUndoManager_EnumRedoable(This,ppEnum) \ + (This)->lpVtbl -> EnumRedoable(This,ppEnum) + +#define IOleUndoManager_GetLastUndoDescription(This,pBstr) \ + (This)->lpVtbl -> GetLastUndoDescription(This,pBstr) + +#define IOleUndoManager_GetLastRedoDescription(This,pBstr) \ + (This)->lpVtbl -> GetLastRedoDescription(This,pBstr) + +#define IOleUndoManager_Enable(This,fEnable) \ + (This)->lpVtbl -> Enable(This,fEnable) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Open_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + +void __RPC_STUB IOleUndoManager_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Close_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + +void __RPC_STUB IOleUndoManager_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Add_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_Add_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetOpenParentState_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + +void __RPC_STUB IOleUndoManager_GetOpenParentState_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_DiscardFrom_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_DiscardFrom_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_UndoTo_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_UndoTo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_RedoTo_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_RedoTo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_EnumUndoable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IOleUndoManager_EnumUndoable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_EnumRedoable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IOleUndoManager_EnumRedoable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetLastUndoDescription_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoManager_GetLastUndoDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetLastRedoDescription_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoManager_GetLastRedoDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Enable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ BOOL fEnable); + + +void __RPC_STUB IOleUndoManager_Enable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleUndoManager_INTERFACE_DEFINED__ */ + + +#ifndef __IQuickActivate_INTERFACE_DEFINED__ +#define __IQuickActivate_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IQuickActivate + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IQuickActivate __RPC_FAR *LPQUICKACTIVATE; + +typedef /* [v1_enum] */ +enum tagQACONTAINERFLAGS + { QACONTAINER_SHOWHATCHING = 0x1, + QACONTAINER_SHOWGRABHANDLES = 0x2, + QACONTAINER_USERMODE = 0x4, + QACONTAINER_DISPLAYASDEFAULT = 0x8, + QACONTAINER_UIDEAD = 0x10, + QACONTAINER_AUTOCLIP = 0x20, + QACONTAINER_MESSAGEREFLECT = 0x40, + QACONTAINER_SUPPORTSMNEMONICS = 0x80 + } QACONTAINERFLAGS; + +typedef DWORD OLE_COLOR; + +typedef struct tagQACONTAINER + { + ULONG cbSize; + IOleClientSite __RPC_FAR *pClientSite; + IAdviseSinkEx __RPC_FAR *pAdviseSink; + IPropertyNotifySink __RPC_FAR *pPropertyNotifySink; + IUnknown __RPC_FAR *pUnkEventSink; + DWORD dwAmbientFlags; + OLE_COLOR colorFore; + OLE_COLOR colorBack; + IFont __RPC_FAR *pFont; + IOleUndoManager __RPC_FAR *pUndoMgr; + DWORD dwAppearance; + LONG lcid; + HPALETTE hpal; + struct IBindHost __RPC_FAR *pBindHost; + } QACONTAINER; + +typedef struct tagQACONTROL + { + ULONG cbSize; + DWORD dwMiscStatus; + DWORD dwViewStatus; + DWORD dwEventCookie; + DWORD dwPropNotifyCookie; + DWORD dwPointerActivationPolicy; + } QACONTROL; + + +EXTERN_C const IID IID_IQuickActivate; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IQuickActivate : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE QuickActivate( + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetContentExtent( + LPSIZEL pSizel) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetContentExtent( + LPSIZEL pSizel) = 0; + + }; + +#else /* C style interface */ + + typedef struct IQuickActivateVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IQuickActivate __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IQuickActivate __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IQuickActivate __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QuickActivate )( + IQuickActivate __RPC_FAR * This, + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetContentExtent )( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetContentExtent )( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + END_INTERFACE + } IQuickActivateVtbl; + + interface IQuickActivate + { + CONST_VTBL struct IQuickActivateVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IQuickActivate_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IQuickActivate_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IQuickActivate_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IQuickActivate_QuickActivate(This,pQaContainer,pQaControl) \ + (This)->lpVtbl -> QuickActivate(This,pQaContainer,pQaControl) + +#define IQuickActivate_SetContentExtent(This,pSizel) \ + (This)->lpVtbl -> SetContentExtent(This,pSizel) + +#define IQuickActivate_GetContentExtent(This,pSizel) \ + (This)->lpVtbl -> GetContentExtent(This,pSizel) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IQuickActivate_QuickActivate_Proxy( + IQuickActivate __RPC_FAR * This, + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl); + + +void __RPC_STUB IQuickActivate_QuickActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IQuickActivate_SetContentExtent_Proxy( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + +void __RPC_STUB IQuickActivate_SetContentExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IQuickActivate_GetContentExtent_Proxy( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + +void __RPC_STUB IQuickActivate_GetContentExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IQuickActivate_INTERFACE_DEFINED__ */ + + +#ifndef __IPointerInactive_INTERFACE_DEFINED__ +#define __IPointerInactive_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPointerInactive + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IPointerInactive __RPC_FAR *LPPOINTERINACTIVE; + +typedef /* [v1_enum] */ +enum tagPOINTERINACTIVE + { POINTERINACTIVE_ACTIVATEONENTRY = 1, + POINTERINACTIVE_DEACTIVATEONLEAVE = 2, + POINTERINACTIVE_ACTIVATEONDRAG = 4 + } POINTERINACTIVE; + + +EXTERN_C const IID IID_IPointerInactive; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPointerInactive : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetActivationPolicy( + /* [out] */ DWORD __RPC_FAR *pdwPolicy) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInactiveMouseMove( + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInactiveSetCursor( + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPointerInactiveVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPointerInactive __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPointerInactive __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPointerInactive __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetActivationPolicy )( + IPointerInactive __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwPolicy); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInactiveMouseMove )( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInactiveSetCursor )( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways); + + END_INTERFACE + } IPointerInactiveVtbl; + + interface IPointerInactive + { + CONST_VTBL struct IPointerInactiveVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPointerInactive_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPointerInactive_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPointerInactive_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPointerInactive_GetActivationPolicy(This,pdwPolicy) \ + (This)->lpVtbl -> GetActivationPolicy(This,pdwPolicy) + +#define IPointerInactive_OnInactiveMouseMove(This,pRectBounds,x,y,grfKeyState) \ + (This)->lpVtbl -> OnInactiveMouseMove(This,pRectBounds,x,y,grfKeyState) + +#define IPointerInactive_OnInactiveSetCursor(This,pRectBounds,x,y,dwMouseMsg,fSetAlways) \ + (This)->lpVtbl -> OnInactiveSetCursor(This,pRectBounds,x,y,dwMouseMsg,fSetAlways) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPointerInactive_GetActivationPolicy_Proxy( + IPointerInactive __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwPolicy); + + +void __RPC_STUB IPointerInactive_GetActivationPolicy_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPointerInactive_OnInactiveMouseMove_Proxy( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState); + + +void __RPC_STUB IPointerInactive_OnInactiveMouseMove_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPointerInactive_OnInactiveSetCursor_Proxy( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways); + + +void __RPC_STUB IPointerInactive_OnInactiveSetCursor_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPointerInactive_INTERFACE_DEFINED__ */ + + +#ifndef __IObjectWithSite_INTERFACE_DEFINED__ +#define __IObjectWithSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IObjectWithSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IObjectWithSite __RPC_FAR *LPOBJECTWITHSITE; + + +EXTERN_C const IID IID_IObjectWithSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IObjectWithSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetSite( + /* [in] */ IUnknown __RPC_FAR *pUnkSite) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSite( + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite) = 0; + + }; + +#else /* C style interface */ + + typedef struct IObjectWithSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IObjectWithSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IObjectWithSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetSite )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSite )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite); + + END_INTERFACE + } IObjectWithSiteVtbl; + + interface IObjectWithSite + { + CONST_VTBL struct IObjectWithSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IObjectWithSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IObjectWithSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IObjectWithSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IObjectWithSite_SetSite(This,pUnkSite) \ + (This)->lpVtbl -> SetSite(This,pUnkSite) + +#define IObjectWithSite_GetSite(This,riid,ppvSite) \ + (This)->lpVtbl -> GetSite(This,riid,ppvSite) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IObjectWithSite_SetSite_Proxy( + IObjectWithSite __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSite); + + +void __RPC_STUB IObjectWithSite_SetSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IObjectWithSite_GetSite_Proxy( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite); + + +void __RPC_STUB IObjectWithSite_GetSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IObjectWithSite_INTERFACE_DEFINED__ */ + + +#ifndef __IErrorLog_INTERFACE_DEFINED__ +#define __IErrorLog_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IErrorLog + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IErrorLog __RPC_FAR *LPERRORLOG; + + +EXTERN_C const IID IID_IErrorLog; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IErrorLog : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AddError( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo) = 0; + + }; + +#else /* C style interface */ + + typedef struct IErrorLogVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IErrorLog __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IErrorLog __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IErrorLog __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddError )( + IErrorLog __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo); + + END_INTERFACE + } IErrorLogVtbl; + + interface IErrorLog + { + CONST_VTBL struct IErrorLogVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IErrorLog_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IErrorLog_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IErrorLog_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IErrorLog_AddError(This,pszPropName,pExcepInfo) \ + (This)->lpVtbl -> AddError(This,pszPropName,pExcepInfo) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IErrorLog_AddError_Proxy( + IErrorLog __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo); + + +void __RPC_STUB IErrorLog_AddError_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IErrorLog_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyBag_INTERFACE_DEFINED__ +#define __IPropertyBag_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyBag + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyBag __RPC_FAR *LPPROPERTYBAG; + + +EXTERN_C const IID IID_IPropertyBag; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyBag : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read( + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog) = 0; + + virtual HRESULT STDMETHODCALLTYPE Write( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyBagVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyBag __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyBag __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyBag __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Read )( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Write )( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar); + + END_INTERFACE + } IPropertyBagVtbl; + + interface IPropertyBag + { + CONST_VTBL struct IPropertyBagVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyBag_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyBag_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyBag_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyBag_Read(This,pszPropName,pVar,pErrorLog) \ + (This)->lpVtbl -> Read(This,pszPropName,pVar,pErrorLog) + +#define IPropertyBag_Write(This,pszPropName,pVar) \ + (This)->lpVtbl -> Write(This,pszPropName,pVar) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPropertyBag_RemoteRead_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog, + /* [in] */ DWORD varType, + /* [in] */ IUnknown __RPC_FAR *pUnkObj); + + +void __RPC_STUB IPropertyBag_RemoteRead_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyBag_Write_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar); + + +void __RPC_STUB IPropertyBag_Write_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyBag_INTERFACE_DEFINED__ */ + + +#ifndef __IPerPropertyBrowsing_INTERFACE_DEFINED__ +#define __IPerPropertyBrowsing_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPerPropertyBrowsing + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPerPropertyBrowsing __RPC_FAR *LPPERPROPERTYBROWSING; + +typedef struct tagCALPOLESTR + { + ULONG cElems; + /* [size_is] */ LPOLESTR __RPC_FAR *pElems; + } CALPOLESTR; + +typedef struct tagCALPOLESTR __RPC_FAR *LPCALPOLESTR; + +typedef struct tagCADWORD + { + ULONG cElems; + /* [size_is] */ DWORD __RPC_FAR *pElems; + } CADWORD; + +typedef struct tagCADWORD __RPC_FAR *LPCADWORD; + + +EXTERN_C const IID IID_IPerPropertyBrowsing; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPerPropertyBrowsing : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetDisplayString( + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE MapPropertyToPage( + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPredefinedStrings( + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPredefinedValue( + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPerPropertyBrowsingVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPerPropertyBrowsing __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPerPropertyBrowsing __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDisplayString )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MapPropertyToPage )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPredefinedStrings )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPredefinedValue )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut); + + END_INTERFACE + } IPerPropertyBrowsingVtbl; + + interface IPerPropertyBrowsing + { + CONST_VTBL struct IPerPropertyBrowsingVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPerPropertyBrowsing_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPerPropertyBrowsing_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPerPropertyBrowsing_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPerPropertyBrowsing_GetDisplayString(This,dispID,pBstr) \ + (This)->lpVtbl -> GetDisplayString(This,dispID,pBstr) + +#define IPerPropertyBrowsing_MapPropertyToPage(This,dispID,pClsid) \ + (This)->lpVtbl -> MapPropertyToPage(This,dispID,pClsid) + +#define IPerPropertyBrowsing_GetPredefinedStrings(This,dispID,pCaStringsOut,pCaCookiesOut) \ + (This)->lpVtbl -> GetPredefinedStrings(This,dispID,pCaStringsOut,pCaCookiesOut) + +#define IPerPropertyBrowsing_GetPredefinedValue(This,dispID,dwCookie,pVarOut) \ + (This)->lpVtbl -> GetPredefinedValue(This,dispID,dwCookie,pVarOut) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetDisplayString_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IPerPropertyBrowsing_GetDisplayString_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_MapPropertyToPage_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid); + + +void __RPC_STUB IPerPropertyBrowsing_MapPropertyToPage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetPredefinedStrings_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut); + + +void __RPC_STUB IPerPropertyBrowsing_GetPredefinedStrings_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetPredefinedValue_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut); + + +void __RPC_STUB IPerPropertyBrowsing_GetPredefinedValue_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPerPropertyBrowsing_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + + +void __RPC_USER UserHWND_from_local( HWND __RPC_FAR *, UserHWND __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHWND_to_local( UserHWND __RPC_FAR *, HWND __RPC_FAR * ); + void __RPC_USER UserHWND_free_inst( UserHWND __RPC_FAR * ); +void __RPC_USER UserHWND_free_local( HWND __RPC_FAR * ); + +void __RPC_USER UserHACCEL_from_local( HACCEL __RPC_FAR *, UserHACCEL __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHACCEL_to_local( UserHACCEL __RPC_FAR *, HACCEL __RPC_FAR * ); + void __RPC_USER UserHACCEL_free_inst( UserHACCEL __RPC_FAR * ); +void __RPC_USER UserHACCEL_free_local( HACCEL __RPC_FAR * ); + +void __RPC_USER UserHDC_from_local( HDC __RPC_FAR *, UserHDC __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHDC_to_local( UserHDC __RPC_FAR *, HDC __RPC_FAR * ); + void __RPC_USER UserHDC_free_inst( UserHDC __RPC_FAR * ); +void __RPC_USER UserHDC_free_local( HDC __RPC_FAR * ); + +void __RPC_USER UserHFONT_from_local( HFONT __RPC_FAR *, UserHFONT __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHFONT_to_local( UserHFONT __RPC_FAR *, HFONT __RPC_FAR * ); + void __RPC_USER UserHFONT_free_inst( UserHFONT __RPC_FAR * ); +void __RPC_USER UserHFONT_free_local( HFONT __RPC_FAR * ); + +void __RPC_USER UserMSG_from_local( MSG __RPC_FAR *, UserMSG __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserMSG_to_local( UserMSG __RPC_FAR *, MSG __RPC_FAR * ); + void __RPC_USER UserMSG_free_inst( UserMSG __RPC_FAR * ); +void __RPC_USER UserMSG_free_local( MSG __RPC_FAR * ); + +void __RPC_USER UserBSTR_from_local( BSTR __RPC_FAR *, UserBSTR __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserBSTR_to_local( UserBSTR __RPC_FAR *, BSTR __RPC_FAR * ); + void __RPC_USER UserBSTR_free_inst( UserBSTR __RPC_FAR * ); +void __RPC_USER UserBSTR_free_local( BSTR __RPC_FAR * ); + +void __RPC_USER UserVARIANT_from_local( VARIANT __RPC_FAR *, UserVARIANT __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserVARIANT_to_local( UserVARIANT __RPC_FAR *, VARIANT __RPC_FAR * ); + void __RPC_USER UserVARIANT_free_inst( UserVARIANT __RPC_FAR * ); +void __RPC_USER UserVARIANT_free_local( VARIANT __RPC_FAR * ); + +void __RPC_USER UserEXCEPINFO_from_local( EXCEPINFO __RPC_FAR *, UserEXCEPINFO __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserEXCEPINFO_to_local( UserEXCEPINFO __RPC_FAR *, EXCEPINFO __RPC_FAR * ); + void __RPC_USER UserEXCEPINFO_free_inst( UserEXCEPINFO __RPC_FAR * ); +void __RPC_USER UserEXCEPINFO_free_local( EXCEPINFO __RPC_FAR * ); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumConnections_Next_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnections_Next_Stub( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Next_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Next_Stub( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IClassFactory2_CreateInstanceLic_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IClassFactory2_CreateInstanceLic_Stub( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObj); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Load_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Load_Stub( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ ULONG cbSize); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Save_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Save_Stub( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + +/* [local] */ void STDMETHODCALLTYPE IAdviseSinkEx_OnViewStatusChange_Proxy( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + +/* [async][call_as] */ void STDMETHODCALLTYPE IAdviseSinkEx_OnViewStatusChange_Stub( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Next_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Next_Stub( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPropertyBag_Read_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPropertyBag_Read_Stub( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog, + /* [in] */ DWORD varType, + /* [in] */ IUnknown __RPC_FAR *pUnkObj); + + + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/REDALERT/ODATA.CPP b/REDALERT/ODATA.CPP new file mode 100644 index 000000000..7c10d41e6 --- /dev/null +++ b/REDALERT/ODATA.CPP @@ -0,0 +1,951 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ODATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ODATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : August 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static OverlayTypeClass const Sandbag( + OVERLAY_SANDBAG_WALL, // Overlay type number. + "SBAG", // INI name of overlay. + TXT_SANDBAG_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 20, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Cyclone( + OVERLAY_CYCLONE_WALL, // Overlay type number. + "CYCL", // INI name of overlay. + TXT_CYCLONE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Brick( + OVERLAY_BRICK_WALL, // Overlay type number. + "BRIK", // INI name of overlay. + TXT_BRICK_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 3, // If this is a wall, how many damage levels? + 70, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + true, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Barbwire( + OVERLAY_BARBWIRE_WALL, // Overlay type number. + "BARB", // INI name of overlay. + TXT_BARBWIRE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Wood( + OVERLAY_WOOD_WALL, // Overlay type number. + "WOOD", // INI name of overlay. + TXT_WOOD_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + true, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Fence( + OVERLAY_FENCE, // Overlay type number. + "FENC", // INI name of overlay. + TXT_FENCE, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gold1( + OVERLAY_GOLD1, // Overlay type number. + "GOLD01", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold2( + OVERLAY_GOLD2, // Overlay type number. + "GOLD02", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold3( + OVERLAY_GOLD3, // Overlay type number. + "GOLD03", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold4( + OVERLAY_GOLD4, // Overlay type number. + "GOLD04", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gems1( + OVERLAY_GEMS1, // Overlay type number. + "GEM01", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems2( + OVERLAY_GEMS2, // Overlay type number. + "GEM02", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems3( + OVERLAY_GEMS3, // Overlay type number. + "GEM03", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems4( + OVERLAY_GEMS4, // Overlay type number. + "GEM04", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V12( + OVERLAY_V12, // Overlay type number. + "V12", // INI name of overlay. + TXT_CIV12, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V13( + OVERLAY_V13, // Overlay type number. + "V13", // INI name of overlay. + TXT_CIV13, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V14( + OVERLAY_V14, // Overlay type number. + "V14", // INI name of overlay. + TXT_CIV14, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V15( + OVERLAY_V15, // Overlay type number. + "V15", // INI name of overlay. + TXT_CIV15, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V16( + OVERLAY_V16, // Overlay type number. + "V16", // INI name of overlay. + TXT_CIV16, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V17( + OVERLAY_V17, // Overlay type number. + "V17", // INI name of overlay. + TXT_CIV17, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V18( + OVERLAY_V18, // Overlay type number. + "V18", // INI name of overlay. + TXT_CIV18, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const FlagSpot( + OVERLAY_FLAG_SPOT, // Overlay type number. + "FPLS", // INI name of overlay. + TXT_FLAG_SPOT, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const WoodCrate( + OVERLAY_WOOD_CRATE, // Overlay type number. + "WCRATE", // INI name of overlay. + TXT_WOOD_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const WaterCrate( + OVERLAY_WATER_CRATE, // Overlay type number. + "WWCRATE", // INI name of overlay. + TXT_WATER_CRATE, // Full name of overlay. + LAND_WATER, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const SteelCrate( + OVERLAY_STEEL_CRATE, // Overlay type number. + "SCRATE", // INI name of overlay. + TXT_STEEL_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); + + +/*********************************************************************************************** + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * * + * This is the constructor for the overlay types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass::OverlayTypeClass( + OverlayType iconset, + char const * ininame, + int fullname, + LandType ground, + int damagelevels, + int damagepoints, + bool isradarvisible, + bool iswooden, + bool istarget, + bool iscrushable, + bool istiberium, + bool high, + bool theater, + bool walltype, + bool iscrate) : + ObjectTypeClass(RTTI_OVERLAYTYPE, + int(iconset), + false, + true, + false, + istarget, + true, + false, + false, + fullname, + ininame), + Type(iconset), + Land(ground), + DamageLevels(damagelevels), + DamagePoints(damagepoints), + IsTheater(theater), + IsWall(walltype), + IsHigh(high), + IsTiberium(istiberium), + IsWooden(iswooden), + IsCrate(iscrate), + IsRadarVisible(isradarvisible) +{ + IsCrushable = iscrushable; +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * * + * This will allocate an overlay type class object from the special memory pool that is * + * for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlay type class object allocated. If there is * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * OverlayTypeClass::operator new(size_t) +{ + return(OverlayTypes.Alloc()); +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * * + * This will return a previously allcoated overaly type object to the special memory * + * pool that it was allocated from. * + * * + * INPUT: pointer -- Pointer to the overlay type class object to return the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::operator delete(void * pointer) +{ + OverlayTypes.Free((OverlayTypeClass *)pointer); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * * + * This will initialize the overlay type heap by pre-allocated all overlay types known * + * to exist. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: It should be called once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init_Heap(void) +{ + /* + ** These overlay type class objects must be allocated in the exact order that they + ** are specified in the OverlayType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new OverlayTypeClass(Sandbag); // OVERLAY_SANDBAG_WALL + new OverlayTypeClass(Cyclone); // OVERLAY_CYCLONE_WALL + new OverlayTypeClass(Brick); // OVERLAY_BRICK_WALL + new OverlayTypeClass(Barbwire); // OVERLAY_BARBWIRE_WALL + new OverlayTypeClass(Wood); // OVERLAY_WOOD_WALL + new OverlayTypeClass(Gold1); // OVERLAY_GOLD1 + new OverlayTypeClass(Gold2); // OVERLAY_GOLD2 + new OverlayTypeClass(Gold3); // OVERLAY_GOLD3 + new OverlayTypeClass(Gold4); // OVERLAY_GOLD4 + new OverlayTypeClass(Gems1); // OVERLAY_GEMS1 + new OverlayTypeClass(Gems2); // OVERLAY_GEMS2 + new OverlayTypeClass(Gems3); // OVERLAY_GEMS3 + new OverlayTypeClass(Gems4); // OVERLAY_GEMS4 + new OverlayTypeClass(V12); // OVERLAY_V12 + new OverlayTypeClass(V13); // OVERLAY_V13 + new OverlayTypeClass(V14); // OVERLAY_V14 + new OverlayTypeClass(V15); // OVERLAY_V15 + new OverlayTypeClass(V16); // OVERLAY_V16 + new OverlayTypeClass(V17); // OVERLAY_V17 + new OverlayTypeClass(V18); // OVERLAY_V18 + new OverlayTypeClass(FlagSpot); // OVERLAY_FLAG_SPOT + new OverlayTypeClass(WoodCrate); // OVERLAY_WOOD_CRATE + new OverlayTypeClass(SteelCrate); // OVERLAY_STEEL_CRATE + new OverlayTypeClass(Fence); // OVERLAY_FENCE + new OverlayTypeClass(WaterCrate); // OVERLAY_WATER_CRATE +} + + +/*********************************************************************************************** + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * * + * This routine should be called once when the game first starts. It will establish * + * pointers to the graphic data of the overlay objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * * + * This routine is used to determine the overlay number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the overlay. * + * * + * OUTPUT: Returns with the overlay number. If the name had no match, * + * then returns with OVERLAY_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +OverlayType OverlayTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(OVERLAY_NONE); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the overlay map and build an * + * occupation list. This list is used to render a overlay cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the overlay occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * OverlayTypeClass::Occupy_List(bool) const +{ + static short _simple[] = {0, REFRESH_EOL}; + + return(_simple); +} + + +/*************************************************************************** + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +unsigned char * OverlayTypeClass::Radar_Icon(int data) const +{ + unsigned char * icon = (unsigned char *)Get_Radar_Data(); // Get pointer to radar icons + if (icon != NULL) icon += (data * 9) + 2; // move icon ptr to correct icon + return(icon); // Return the correct icon +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * * + * This routine is used to display a generic view of the overlay * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + if (Get_Image_Data() != NULL) { + int frame = 0; + + if (IsTiberium) { + frame = 7; + } + if (Type == OVERLAY_GEMS1 || Type == OVERLAY_GEMS2 || Type == OVERLAY_GEMS3 || Type == OVERLAY_GEMS4) { + frame = 2; + } + + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), frame, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * * + * This routine prepares a list of overlay objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a overlay object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created * + *=============================================================================================*/ +void OverlayTypeClass::Prep_For_Add(void) +{ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + if (overlay.Get_Image_Data() != NULL && + !overlay.IsWall && + (!overlay.IsTiberium || index == OVERLAY_GOLD1 || index == OVERLAY_GEMS1)) { + + Map.Add_To_List(&overlay); + } + } +} +#endif + + +/*********************************************************************************************** + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * * + * This support routine is used by the scenario editor to add a overlay object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the overlay object. * + * * + * OUTPUT: bool; Was the overlay object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new OverlayClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * * + * This routine will create an object of this type. For certain overlay objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a overlay at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this overlay type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * OverlayTypeClass::Create_One_Of(HouseClass *) const +{ + return(new OverlayClass(Type, -1)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * * + * This routine will draw the overlay shape at the coordinates specified. It is presumed * + * that all the underlying layers have already been rendered by the time this routine is * + * called. * + * * + * INPUT: x, y -- Coordinate (upper left) of cell where overlay image is to be drawn. * + * * + * data -- Cell specific data that controls the imagery of the overlay. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Draw_It(int x, int y, int data) const +{ + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), data, Map.TacPixelX+x+(CELL_PIXEL_W>>1), Map.TacPixelY+y+(CELL_PIXEL_H>>1), WINDOW_MAIN, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * * + * This routine will update the overlay graphic data according to the theater specified. * + * It is typically called when the scenario is first loaded (theater change). * + * * + * INPUT: theater -- The theater to load specific data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass & overlay = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + + if (overlay.IsTheater) { + _makepath(fullname, NULL, NULL, overlay.IniName, Theaters[theater].Suffix); + } else { + _makepath(fullname, NULL, NULL, overlay.IniName, ".SHP"); + } + overlay.ImageData = MFCD::Retrieve(fullname); + + IsTheaterShape = overlay.IsTheater; //Tell Build_Frame if this is a theater specific shape + if (overlay.RadarIcon != NULL) delete[] (char *)overlay.RadarIcon; + overlay.RadarIcon = Get_Radar_Icon(overlay.Get_Image_Data(), 0, -1, 3); + IsTheaterShape = false; + } + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * * + * Use this routine to get a reference that corresponds to the overlay type. * + * * + * INPUT: type -- The overlay type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the overlay type specified. * + * * + * WARNINGS: Be sure that the overlay type specified is legal. Illegal type value will * + * result in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass & OverlayTypeClass::As_Reference(OverlayType type) +{ + return(*OverlayTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * * + * This will adjust the coordinate specified so that it will be of legal format to * + * assign as the coordinate of an overlay. Overlays are always relative to the upper left * + * corner of the cell, so this routine drops the fractional cell components. * + * * + * INPUT: coord -- The coordinate to fixup to be legal for assignment. * + * * + * OUTPUT: Returns with a properly fixed up coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE OverlayTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/REDALERT/OPTIONS.CPP b/REDALERT/OPTIONS.CPP new file mode 100644 index 000000000..cff3732f9 --- /dev/null +++ b/REDALERT/OPTIONS.CPP @@ -0,0 +1,920 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OPTIONS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * OptionsClass::Get_Game_Speed -- Fetches the current game speed setting. * + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * OptionsClass::Get_Scroll_Rate -- Fetches the current scroll rate setting. * + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * OptionsClass::One_Time -- This performs any one time initialization for the options class.* + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * OptionsClass::Process -- Handles all the options graphic interface. * + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * OptionsClass::Set -- Sets options based on current settings * + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * OptionsClass::Set_Game_Speed -- Sets the game speed as specified. * + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * OptionsClass::Set_Scroll_Rate -- Sets the scroll rate as specified. * + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * OptionsClass::Set_Tint -- Sets the tint setting. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "options.h" + + +#ifdef WIN32 +char const * const OptionsClass::HotkeyName = "WinHotkeys"; +#else +char const * const OptionsClass::HotkeyName = "DOSHotkeys"; +#endif + + +/*********************************************************************************************** + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * * + * This is the constructor for the options class. It handles setting up all the globals * + * necessary for the options. This includes setting them to their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +OptionsClass::OptionsClass(void) : + GameSpeed(3), + ScrollRate(3), +#ifdef WIN32 + Volume(".40"), // was .295 + ScoreVolume(".25"), +#ifdef FIXIT_VERSION_3 + MultiScoreVolume("0"), +#endif +#else + Volume(".8"), + ScoreVolume(".6"), +#endif + Brightness(fixed::_1_2), + Tint(fixed::_1_2), + Saturation(fixed::_1_2), + Contrast(fixed::_1_2), + AutoScroll(true), + IsScoreRepeat(false), + IsScoreShuffle(false), + IsPaletteScroll(true), + + KeyForceMove1(KN_LALT), + KeyForceMove2(KN_RALT), + KeyForceAttack1(KN_LCTRL), + KeyForceAttack2(KN_RCTRL), + KeySelect1(KN_LSHIFT), + KeySelect2(KN_RSHIFT), + KeyScatter(KN_X), + KeyStop(KN_S), + KeyGuard(KN_G), + KeyNext(KN_N), + KeyPrevious(KN_B), + KeyFormation(KN_F), + KeyHome1(KN_HOME), + KeyHome2(KN_E_HOME), + KeyBase(KN_H), + KeyResign(KN_R), + KeyAlliance(KN_A), + KeyBookmark1(KN_F9), + KeyBookmark2(KN_F10), + KeyBookmark3(KN_F11), + KeyBookmark4(KN_F12), + KeySelectView(KN_E), + KeyRepair(KN_T), + KeyRepairOn(KN_NONE), + KeyRepairOff(KN_NONE), + KeySell(KN_Y), + KeySellOn(KN_NONE), + KeySellOff(KN_NONE), + KeyMap(KN_U), + KeySidebarUp(KN_UP), + KeySidebarDown(KN_DOWN), + KeyOption1(KN_ESC), + KeyOption2(KN_SPACE), + KeyScrollLeft(KN_NONE), + KeyScrollRight(KN_NONE), + KeyScrollUp(KN_NONE), + KeyScrollDown(KN_NONE), + KeyQueueMove1(KN_Q), + KeyQueueMove2(KN_Q), + KeyTeam1(KN_1), + KeyTeam2(KN_2), + KeyTeam3(KN_3), + KeyTeam4(KN_4), + KeyTeam5(KN_5), + KeyTeam6(KN_6), + KeyTeam7(KN_7), + KeyTeam8(KN_8), + KeyTeam9(KN_9), + KeyTeam10(KN_0) +{ +} + + +/*********************************************************************************************** + * OptionsClass::One_Time -- This performs any one time initialization for the options class. * + * * + * This routine should be called only once and it will perform any initializations for the * + * options class that is needed. This may include things like file loading and memory * + * allocation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::One_Time(void) +{ + Set_Score_Vol(ScoreVolume * 256); +} + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Process(void) +{ +} + + +/*********************************************************************************************** + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * * + * This routine will control the score shuffle flag. The setting to use is provided as * + * a parameter. When shuffling is on, the score play order is scrambled. * + * * + * INPUT: on -- Should the shuffle option be activated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Shuffle(int on) +{ + IsScoreShuffle = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * * + * This routine is used to control whether scores repeat or not. The setting to use for * + * the repeat flag is provided as a parameter. * + * * + * INPUT: on -- Should the scores repeat? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Repeat(int on) +{ + IsScoreRepeat = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * * + * This routine will set the global score volume to the value specified. The value ranges * + * from zero to 255. * + * * + * INPUT: volume -- The new volume setting to use for scores. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Score_Volume(fixed volume, bool feedback) +{ + ScoreVolume = Sub_Saturate(volume, 1); + Set_Score_Vol(ScoreVolume * 256); + if (feedback && !Theme.Still_Playing()) { + Sound_Effect(VOC_BEEP, ScoreVolume); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * * + * This routine will set the sound effect volume level as indicated. It can generate a * + * sound effect for feedback purposes if desired. The volume setting can range from zero * + * to 255. The value of 255 is the loudest. * + * * + * INPUT: volume -- The volume setting to use for the new value. 0 to 255. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Sound_Volume(fixed volume, bool feedback) +{ + Volume = Sub_Saturate(volume, 1); + if (feedback) { + Sound_Effect(VOC_BEEP); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * * + * This routine will set the current brightness level to the value specified. This value * + * can range from zero to 255, with 128 being the normal (default) brightness level. * + * * + * INPUT: brightness -- The brightness level to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Brightness(fixed brightness) +{ + Brightness = fixed::_1_4 + (fixed::_1_2 * brightness); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * * + * This routine will fetch the current setting for the brightness level. The value ranges * + * from zero to 255, with 128 being the normal (default) value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current brightness setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Brightness(void) const +{ + return((Brightness - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * * + * This routine will set the color value to that specified. The value specified can range * + * from zero to 255. The value of 128 is the normal default color setting. * + * * + * INPUT: color -- The new color value to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Saturation(fixed color) +{ + Saturation = color; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * * + * This routine will fetch the current color setting. This value ranges from zero to * + * 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current color setting. The value of 128 is the normal (default) * + * color setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Saturation(void) const +{ + return(Saturation); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * * + * This routine will set the contrast to the setting specified. This setting ranges from * + * zero to 255. The value o 128 is the normal default value. * + * * + * INPUT: contrast -- The contrast setting to make as the current setting. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Contrast(fixed contrast) +{ + Contrast = fixed::_1_4 + (fixed::_1_2 * contrast); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * * + * This routine will get the current contrast setting. The value returned is in the range * + * of zero to 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current contrast setting. A setting of 128 is the normal default value.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Contrast(void) const +{ + return((Contrast - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Tint -- Sets the tint setting. * + * * + * This routine will change the current tint setting according to the value specified. * + * * + * INPUT: tint -- The desired tint setting. This value ranges from zero to 255. * + * * + * OUTPUT: none * + * * + * WARNINGS: The value of 128 is the default (normal) tint setting. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Tint(fixed tint) +{ + Tint = tint; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * * + * This fetches the current tint setting. The value is returned as a number between * + * zero and 255. This has been adjusted for the valid range allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current tint setting. Normal tint setting is 128. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Tint(void) const +{ + return(Tint); +} + + +/*********************************************************************************************** + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * * + * This routine is used to adjust the palette according to the settings provided. It is * + * used by the options class to monkey with the palette. * + * * + * INPUT: oldpal -- Pointer to the original (unmodified) palette. * + * * + * newpal -- The new palette to create according to the settings provided. * + * * + * brightness -- The brightness level (0..255). * + * * + * color -- The color level (0..255). * + * * + * tint -- The tint (hue) level (0..255). * + * * + * contrast -- The contrast level (0..255). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + * 12/02/1995 JLB : Uses palette class objects. * + *=============================================================================================*/ +void OptionsClass::Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const +{ + //if (!oldpal || !newpal) return; Can't be NULL... ST - 5/9/2019 + + /* + ** Adjust for palette. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + if (index == CC_MOUSE_COLOR) { + newpal[index] = oldpal[index]; + } else { + /* + ** Convert the working palette entry into an HSV format for + ** manipulation. + */ + HSVClass hsv = oldpal[index]; + + /* + ** Adjust contrast by moving the value toward the center according to the + ** percentage indicated. + */ + int temp; + temp = (hsv.Value_Component() * (brightness * 256)) / 0x80; // Brightness + temp = Bound(temp, 0, 0xFF); + int v = temp; + temp = (((((int)v) - 0x80) * (contrast * 256)) / 0x80) + 0x80; // Contrast + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (hsv.Saturation_Component() * (color * 256)) / 0x80; // Color + temp = Bound(temp, 0, 0xFF); + int s = temp; + temp = (hsv.Hue_Component() * (tint * 256)) / 0x80; // Tint + temp = Bound(temp, 0, 0xFF); + int h = temp; + + /* + ** Replace the working palette entry according to the newly calculated + ** hue, saturation, and value. + */ + newpal[index] = HSVClass(h, s, v); + } + } +} + + +/*********************************************************************************************** + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Reworked to use new INI handler. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Load_Settings(void) +{ + /* + ** Create filename and read the file. + */ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + ini.Load(file); + + /* + ** Read in the Options values + */ + static char const * const OPTIONS = "Options"; + GameSpeed = ini.Get_Int(OPTIONS, "GameSpeed", GameSpeed); + ScrollRate = ini.Get_Int(OPTIONS, "ScrollRate", ScrollRate); + Set_Brightness(ini.Get_Fixed(OPTIONS, "Brightness", Brightness)); + Set_Sound_Volume(ini.Get_Fixed(OPTIONS, "Volume", Volume), false); + Set_Score_Volume(ini.Get_Fixed(OPTIONS, "ScoreVolume", ScoreVolume), false); +#ifdef FIXIT_VERSION_3 + MultiScoreVolume = ini.Get_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + Set_Contrast(ini.Get_Fixed(OPTIONS, "Contrast", Contrast)); + Set_Saturation(ini.Get_Fixed(OPTIONS, "Color", Saturation)); + Set_Tint(ini.Get_Fixed(OPTIONS, "Tint", Tint)); + AutoScroll = ini.Get_Bool(OPTIONS, "AutoScroll", AutoScroll); + Set_Repeat(ini.Get_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat)); + Set_Shuffle(ini.Get_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle)); + SlowPalette = ini.Get_Bool(OPTIONS, "SlowPalette", SlowPalette); + IsPaletteScroll = ini.Get_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + KeyForceMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + KeyForceMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + KeyForceAttack1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + KeyForceAttack2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + KeySelect1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect1", KeySelect1); + KeySelect2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect2", KeySelect2); + KeyScatter = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScatter", KeyScatter); + KeyStop = (KeyNumType)ini.Get_Int(HotkeyName, "KeyStop", KeyStop); + KeyGuard = (KeyNumType)ini.Get_Int(HotkeyName, "KeyGuard", KeyGuard); + KeyNext = (KeyNumType)ini.Get_Int(HotkeyName, "KeyNext", KeyNext); + KeyPrevious = (KeyNumType)ini.Get_Int(HotkeyName, "KeyPrevious", KeyPrevious); + KeyFormation = (KeyNumType)ini.Get_Int(HotkeyName, "KeyFormation", KeyFormation); + KeyHome1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome1", KeyHome1); + KeyHome2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome2", KeyHome2); + KeyBase = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBase", KeyBase); + KeyResign = (KeyNumType)ini.Get_Int(HotkeyName, "KeyResign", KeyResign); + KeyAlliance = (KeyNumType)ini.Get_Int(HotkeyName, "KeyAlliance", KeyAlliance); + KeyBookmark1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + KeyBookmark2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + KeyBookmark3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + KeyBookmark4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + KeySelectView = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelectView", KeySelectView); + KeyRepair = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + KeyRepairOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + KeyRepairOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + KeySell = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellToggle", KeySell); + KeySellOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOn", KeySellOn); + KeySellOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOff", KeySellOff); + KeyMap = (KeyNumType)ini.Get_Int(HotkeyName, "KeyMapToggle", KeyMap); + KeySidebarUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + KeySidebarDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + KeyOption1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption1", KeyOption1); + KeyOption2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption2", KeyOption2); + KeyScrollLeft = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + KeyScrollRight = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + KeyScrollUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + KeyScrollDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + KeyQueueMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + KeyQueueMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + KeyTeam1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam1", KeyTeam1); + KeyTeam2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam2", KeyTeam2); + KeyTeam3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam3", KeyTeam3); + KeyTeam4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam4", KeyTeam4); + KeyTeam5 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam5", KeyTeam5); + KeyTeam6 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam6", KeyTeam6); + KeyTeam7 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam7", KeyTeam7); + KeyTeam8 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam8", KeyTeam8); + KeyTeam9 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam9", KeyTeam9); + KeyTeam10 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + +#ifdef WIN32 + KeyForceMove1 = (KeyNumType)(KeyForceMove1 & ~WWKEY_VK_BIT); + KeyForceMove2 = (KeyNumType)(KeyForceMove2 & ~WWKEY_VK_BIT); + KeyForceAttack1 = (KeyNumType)(KeyForceAttack1 & ~WWKEY_VK_BIT); + KeyForceAttack2 = (KeyNumType)(KeyForceAttack2 & ~WWKEY_VK_BIT); + KeySelect1 = (KeyNumType)(KeySelect1 & ~WWKEY_VK_BIT); + KeySelect2 = (KeyNumType)(KeySelect2 & ~WWKEY_VK_BIT); + KeyScatter = (KeyNumType)(KeyScatter & ~WWKEY_VK_BIT); + KeyStop = (KeyNumType)(KeyStop & ~WWKEY_VK_BIT); + KeyGuard = (KeyNumType)(KeyGuard & ~WWKEY_VK_BIT); + KeyNext = (KeyNumType)(KeyNext & ~WWKEY_VK_BIT); + KeyPrevious = (KeyNumType)(KeyPrevious & ~WWKEY_VK_BIT); + KeyFormation = (KeyNumType)(KeyFormation & ~WWKEY_VK_BIT); + KeyHome1 = (KeyNumType)(KeyHome1 & ~WWKEY_VK_BIT); + KeyHome2 = (KeyNumType)(KeyHome2 & ~WWKEY_VK_BIT); + KeyBase = (KeyNumType)(KeyBase & ~WWKEY_VK_BIT); + KeyResign = (KeyNumType)(KeyResign & ~WWKEY_VK_BIT); + KeyAlliance = (KeyNumType)(KeyAlliance & ~WWKEY_VK_BIT); + KeyBookmark1 = (KeyNumType)(KeyBookmark1 & ~WWKEY_VK_BIT); + KeyBookmark2 = (KeyNumType)(KeyBookmark2 & ~WWKEY_VK_BIT); + KeyBookmark3 = (KeyNumType)(KeyBookmark3 & ~WWKEY_VK_BIT); + KeyBookmark4 = (KeyNumType)(KeyBookmark4 & ~WWKEY_VK_BIT); + KeySelectView = (KeyNumType)(KeySelectView & ~WWKEY_VK_BIT); + KeyRepair = (KeyNumType)(KeyRepair & ~WWKEY_VK_BIT); + KeyRepairOn = (KeyNumType)(KeyRepairOn & ~WWKEY_VK_BIT); + KeyRepairOff = (KeyNumType)(KeyRepairOff & ~WWKEY_VK_BIT); + KeySell = (KeyNumType)(KeySell & ~WWKEY_VK_BIT); + KeySellOn = (KeyNumType)(KeySellOn & ~WWKEY_VK_BIT); + KeySellOff = (KeyNumType)(KeySellOff & ~WWKEY_VK_BIT); + KeyMap = (KeyNumType)(KeyMap & ~WWKEY_VK_BIT); + KeySidebarUp = (KeyNumType)(KeySidebarUp & ~WWKEY_VK_BIT); + KeySidebarDown = (KeyNumType)(KeySidebarDown & ~WWKEY_VK_BIT); + KeyOption1 = (KeyNumType)(KeyOption1 & ~WWKEY_VK_BIT); + KeyOption2 = (KeyNumType)(KeyOption2 & ~WWKEY_VK_BIT); + KeyScrollLeft = (KeyNumType)(KeyScrollLeft & ~WWKEY_VK_BIT); + KeyScrollRight = (KeyNumType)(KeyScrollRight & ~WWKEY_VK_BIT); + KeyScrollUp = (KeyNumType)(KeyScrollUp & ~WWKEY_VK_BIT); + KeyScrollDown = (KeyNumType)(KeyScrollDown & ~WWKEY_VK_BIT); + KeyQueueMove1 = (KeyNumType)(KeyQueueMove1 & ~WWKEY_VK_BIT); + KeyQueueMove2 = (KeyNumType)(KeyQueueMove2 & ~WWKEY_VK_BIT); + KeyTeam1 = (KeyNumType)(KeyTeam1 & ~WWKEY_VK_BIT); + KeyTeam2 = (KeyNumType)(KeyTeam2 & ~WWKEY_VK_BIT); + KeyTeam3 = (KeyNumType)(KeyTeam3 & ~WWKEY_VK_BIT); + KeyTeam4 = (KeyNumType)(KeyTeam4 & ~WWKEY_VK_BIT); + KeyTeam5 = (KeyNumType)(KeyTeam5 & ~WWKEY_VK_BIT); + KeyTeam6 = (KeyNumType)(KeyTeam6 & ~WWKEY_VK_BIT); + KeyTeam7 = (KeyNumType)(KeyTeam7 & ~WWKEY_VK_BIT); + KeyTeam8 = (KeyNumType)(KeyTeam8 & ~WWKEY_VK_BIT); + KeyTeam9 = (KeyNumType)(KeyTeam9 & ~WWKEY_VK_BIT); + KeyTeam10 = (KeyNumType)(KeyTeam10 & ~WWKEY_VK_BIT); +#endif +} + + +/*********************************************************************************************** + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Revamped and tightened up. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Save_Settings (void) +{ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + + /* + ** Load any existing options file because it may contain entries that won't be + ** explicitly written out by this routine. By preloading the database, these entries + ** will be carried over. + */ + if (file.Is_Available()) { + ini.Load(file); + } + + /* + ** Save Options settings + */ + static char const * const OPTIONS = "Options"; + ini.Put_Int(OPTIONS, "GameSpeed", GameSpeed); + ini.Put_Int(OPTIONS, "ScrollRate", ScrollRate); + ini.Put_Fixed(OPTIONS, "Brightness", Brightness); + ini.Put_Fixed(OPTIONS, "Volume", Volume); +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_NORMAL ) // Save only when non-multiplayer. + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#else + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#endif +#ifdef FIXIT_VERSION_3 + ini.Put_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + ini.Put_Fixed(OPTIONS, "Contrast", Contrast); + ini.Put_Fixed(OPTIONS, "Color", Saturation); + ini.Put_Fixed(OPTIONS, "Tint", Tint); + ini.Put_Bool(OPTIONS, "AutoScroll", AutoScroll); + ini.Put_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat); + ini.Put_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle); + ini.Put_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + ini.Put_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + ini.Put_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + ini.Put_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + ini.Put_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + ini.Put_Int(HotkeyName, "KeySelect1", KeySelect1); + ini.Put_Int(HotkeyName, "KeySelect2", KeySelect2); + ini.Put_Int(HotkeyName, "KeyScatter", KeyScatter); + ini.Put_Int(HotkeyName, "KeyStop", KeyStop); + ini.Put_Int(HotkeyName, "KeyGuard", KeyGuard); + ini.Put_Int(HotkeyName, "KeyNext", KeyNext); + ini.Put_Int(HotkeyName, "KeyPrevious", KeyPrevious); + ini.Put_Int(HotkeyName, "KeyFormation", KeyFormation); + ini.Put_Int(HotkeyName, "KeyHome1", KeyHome1); + ini.Put_Int(HotkeyName, "KeyHome2", KeyHome2); + ini.Put_Int(HotkeyName, "KeyBase", KeyBase); + ini.Put_Int(HotkeyName, "KeyResign", KeyResign); + ini.Put_Int(HotkeyName, "KeyAlliance", KeyAlliance); + ini.Put_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + ini.Put_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + ini.Put_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + ini.Put_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + ini.Put_Int(HotkeyName, "KeySelectView", KeySelectView); + ini.Put_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + ini.Put_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + ini.Put_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + ini.Put_Int(HotkeyName, "KeySellToggle", KeySell); + ini.Put_Int(HotkeyName, "KeySellOn", KeySellOn); + ini.Put_Int(HotkeyName, "KeySellOff", KeySellOff); + ini.Put_Int(HotkeyName, "KeyMapToggle", KeyMap); + ini.Put_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + ini.Put_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + ini.Put_Int(HotkeyName, "KeyOption1", KeyOption1); + ini.Put_Int(HotkeyName, "KeyOption2", KeyOption2); + ini.Put_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + ini.Put_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + ini.Put_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + ini.Put_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + ini.Put_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + ini.Put_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + ini.Put_Int(HotkeyName, "KeyTeam1", KeyTeam1); + ini.Put_Int(HotkeyName, "KeyTeam2", KeyTeam2); + ini.Put_Int(HotkeyName, "KeyTeam3", KeyTeam3); + ini.Put_Int(HotkeyName, "KeyTeam4", KeyTeam4); + ini.Put_Int(HotkeyName, "KeyTeam5", KeyTeam5); + ini.Put_Int(HotkeyName, "KeyTeam6", KeyTeam6); + ini.Put_Int(HotkeyName, "KeyTeam7", KeyTeam7); + ini.Put_Int(HotkeyName, "KeyTeam8", KeyTeam8); + ini.Put_Int(HotkeyName, "KeyTeam9", KeyTeam9); + ini.Put_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + /* + ** Write the INI data out to a file. + */ + ini.Save(file); +} + + +/*********************************************************************************************** + * OptionsClass::Set -- Sets options based on current settings * + * * + * Use this routine to adjust the palette or sound settings after a fresh scenario load. * + * It assumes the values needed are already loaded into OptionsClass. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void OptionsClass::Set(void) +{ + Set_Brightness(Brightness); + Set_Contrast(Contrast); + Set_Saturation(Saturation); + Set_Tint(Tint); + Set_Sound_Volume(Volume, false); + Set_Score_Volume(ScoreVolume, false); + Set_Repeat(IsScoreRepeat); + Set_Shuffle(IsScoreShuffle); +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * * + * This routine is used to adjust delay factors that MUST be synchronized on all machines * + * but should maintain a speed as close to constant as possible. Building animations are * + * a good example of this. * + * * + * INPUT: delay -- The normal delay factor. * + * * + * OUTPUT: Returns with the delay to use that has been modified so that a reasonably constant * + * rate will result. * + * * + * WARNINGS: This calculation is crude due to the coarse resolution that a 1/15 second timer * + * allows. * + * * + * Use of this routine ASSUMES that the GameSpeed is synchronized on all machines. * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + * 06/30/1995 JLB : Handles low values in a more consistent manner. * + *=============================================================================================*/ +int OptionsClass::Normalize_Delay(int delay) const +{ + static int _adjust[][8] = { + {2,2,1,1,1,1,1,1}, + {3,3,3,2,2,2,1,1}, + {5,4,4,3,3,2,2,1}, + {7,6,5,4,4,4,3,2} + }; + if (delay) { + if (delay < 5) { + delay = _adjust[delay-1][GameSpeed]; + } else { + delay = ((delay * 8) / (GameSpeed+1)); + } + } + return(delay); +} + + +/*********************************************************************************************** + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * * + * This routine is used to adjust the real palette to match the values for the palette * + * control. The adjusted palette is placed into the palette buffer specified. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The palette is not actually set by this routine. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Fixup_Palette(void) const +{ + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + CCPalette = InGamePalette; +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * * + * This routine will take a relative volume value and convert it to the real volume value * + * to use. This allows all the game volumes to be corrected to the correct global volume. * + * * + * INPUT: volume -- Requested volume level. * + * * + * OUTPUT: Returns with the actual volume level to use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Normalize_Volume(int volume) const +{ + return(volume * Volume); +} diff --git a/REDALERT/OPTIONS.H b/REDALERT/OPTIONS.H new file mode 100644 index 000000000..1e08bb29c --- /dev/null +++ b/REDALERT/OPTIONS.H @@ -0,0 +1,157 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OPTIONS.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +class OptionsClass { + public: + enum { + MAX_SCROLL_SETTING=7, + MAX_SPEED_SETTING=7 + }; + + OptionsClass(void); + + void One_Time(void); + void Process(void); + + void Fixup_Palette(void) const; + void Set_Shuffle(int on); + void Set_Repeat(int on); + void Set_Score_Volume(fixed volume, bool feedback); + void Set_Sound_Volume(fixed volume, bool feedback); + void Set_Brightness(fixed brightness); + fixed Get_Brightness(void) const; + void Set_Saturation(fixed color); + fixed Get_Saturation(void) const; + void Set_Contrast(fixed contrast); + fixed Get_Contrast(void) const; + void Set_Tint(fixed tint); + fixed Get_Tint(void) const; + int Normalize_Delay(int delay) const; + int Normalize_Volume(int volume) const; + + /* + ** File I/O routines + */ + void Load_Settings(void); + void Save_Settings(void); + + void Set(void); + + /* + ** This is actually the delay between game frames expressed as 1/60 of + ** a second. The default value is 4 (1/15 second). + */ + unsigned int GameSpeed; + + int ScrollRate; // Distance to scroll. + fixed Volume; // Volume for sound effects. + fixed ScoreVolume; // Volume for scores. +#ifdef FIXIT_VERSION_3 + fixed MultiScoreVolume; // Volume for scores during multiplayer games. +#endif + fixed Brightness; // Brightness. + fixed Tint; // Hue + fixed Saturation; // Saturation + fixed Contrast; // Value + unsigned AutoScroll:1; // Does map autoscroll? + unsigned IsScoreRepeat:1; // Score should repeat? + unsigned IsScoreShuffle:1; // Score list should shuffle? + unsigned IsPaletteScroll:1;// Allow palette scrolling? + + /* + ** These are the hotkeys used for keyboard control. + */ + KeyNumType KeyForceMove1; + KeyNumType KeyForceMove2; + KeyNumType KeyForceAttack1; + KeyNumType KeyForceAttack2; + KeyNumType KeySelect1; + KeyNumType KeySelect2; + KeyNumType KeyScatter; + KeyNumType KeyStop; + KeyNumType KeyGuard; + KeyNumType KeyNext; + KeyNumType KeyPrevious; + KeyNumType KeyFormation; + KeyNumType KeyHome1; + KeyNumType KeyHome2; + KeyNumType KeyBase; + KeyNumType KeyResign; + KeyNumType KeyAlliance; + KeyNumType KeyBookmark1; + KeyNumType KeyBookmark2; + KeyNumType KeyBookmark3; + KeyNumType KeyBookmark4; + KeyNumType KeySelectView; + KeyNumType KeyRepair; + KeyNumType KeyRepairOn; + KeyNumType KeyRepairOff; + KeyNumType KeySell; + KeyNumType KeySellOn; + KeyNumType KeySellOff; + KeyNumType KeyMap; + KeyNumType KeySidebarUp; + KeyNumType KeySidebarDown; + KeyNumType KeyOption1; + KeyNumType KeyOption2; + KeyNumType KeyScrollLeft; + KeyNumType KeyScrollRight; + KeyNumType KeyScrollUp; + KeyNumType KeyScrollDown; + KeyNumType KeyQueueMove1; + KeyNumType KeyQueueMove2; + KeyNumType KeyTeam1; + KeyNumType KeyTeam2; + KeyNumType KeyTeam3; + KeyNumType KeyTeam4; + KeyNumType KeyTeam5; + KeyNumType KeyTeam6; + KeyNumType KeyTeam7; + KeyNumType KeyTeam8; + KeyNumType KeyTeam9; + KeyNumType KeyTeam10; + + void Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const; + protected: + + private: + + static char const * const HotkeyName; +}; + + +#endif diff --git a/REDALERT/OVERLAY.CPP b/REDALERT/OVERLAY.CPP new file mode 100644 index 000000000..faacbd989 --- /dev/null +++ b/REDALERT/OVERLAY.CPP @@ -0,0 +1,381 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OVERLAY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * OverlayClass::Init -- Resets the overlay object system. * + * OverlayClass::Mark -- Marks the overlay down on the map. * + * OverlayClass::OverlayClass -- Overlay object constructor. * + * OverlayClass::delete -- Returns a overlay object to the pool. * + * OverlayClass::new -- Allocates a overlay object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "overlay.h" + + +HousesType OverlayClass::ToOwn = HOUSE_NONE; + + + +/*********************************************************************************************** + * OverlayClass::Init -- Resets the overlay object system. * + * * + * This routine resets the overlay object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Init(void) +{ + Overlays.Free_All(); +} + + +/*********************************************************************************************** + * OverlayClass::new -- Allocates a overlay object from pool * + * * + * This routine is used to allocate a overlay object from the * + * overlay object pool. * + * * + * INPUT: size -- The size of a overlay object (not used). * + * * + * OUTPUT: Returns with a pointer to an available overlay object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * OverlayClass::operator new(size_t ) +{ + void * ptr = Overlays.Allocate(); + if (ptr) { + ((OverlayClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * OverlayClass::delete -- Returns a overlay object to the pool. * + * * + * This routine will return a overlay object to the overlay object * + * pool. A overlay so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::operator delete(void * ptr) +{ + if (ptr) { + ((OverlayClass *)ptr)->IsActive = false; + } + Overlays.Free((OverlayClass *)ptr); +} + + +/*********************************************************************************************** + * OverlayClass::OverlayClass -- Overlay object constructor. * + * * + * This is the constructor for a overlay object. * + * * + * INPUT: type -- The overlay object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +OverlayClass::OverlayClass(OverlayType type, CELL pos, HousesType house) : + ObjectClass(RTTI_OVERLAY, Overlays.ID(this)), + Class(OverlayTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + Unlimbo(Cell_Coord(pos)); + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * OverlayClass::Mark -- Marks the overlay down on the map. * + * * + * This routine will place the overlay onto the map. The overlay object is deleted by this * + * operation. The map is updated to reflect the presence of the overlay. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the overlay successfully marked? Failure occurs if it is not being * + * marked down. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool OverlayClass::Mark(MarkType mark) +{ + assert(Overlays.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL cell = Coord_Cell(Coord); + CellClass * cellptr = &Map[cell]; + + /* + ** Walls have special logic when they are marked down. + */ + if (Class->IsWall) { + if (cellptr->Is_Clear_To_Build()) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + cellptr->Wall_Update(); + Map.Zone_Reset(Class->IsCrushable ? MZONE_NORMAL : MZONE_NORMAL|MZONE_CRUSHER); + + /* + ** Flag ownership of the cell if the 'global' ownership flag indicates that this + ** is necessary for the overlay. + */ + if (ToOwn != HOUSE_NONE) { + cellptr->Owner = ToOwn; + } + + } else { + delete this; + return(false); + } + } else { + + bool clear = false; + if (!ScenarioInit) { + if (Class->Type == OVERLAY_WATER_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_FLOAT, false, false); + } else { + if (Class->Type == OVERLAY_STEEL_CRATE || Class->Type == OVERLAY_WOOD_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false); + } else { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true); + } + } + } else { + clear = true; + } + + if ((ScenarioInit || cellptr->Overlay == OVERLAY_NONE) && clear) { + + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + + cellptr->Redraw_Objects(); + if (Class->Land == LAND_TIBERIUM) { + cellptr->OverlayData = 1; + cellptr->Tiberium_Adjust(); + } + } + } + + /* + ** ***** Is this really needed? + */ + cellptr->Recalc_Attributes(); + + /* + ** Remove the overlay and make sure the system thinks it was never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * * + * This routine is used to load a scenario's overlay data. The overlay objects are read * + * from the INI file and then created on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: Requires that all the buildings be placed first, so the scan for assigning wall * + * ownership to the nearest building will work. * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Specifically forbid manual crates in multiplayer scenarios. * + *=============================================================================================*/ +void OverlayClass::Read_INI(CCINIClass & ini) +{ + if (NewINIFormat > 1) { + int len = ini.Get_UUBlock("OverlayPack", _staging_buffer, sizeof(_staging_buffer)); + + if (len > 0) { + BufferStraw bpipe(_staging_buffer, len); + LCWStraw uncomp(LCWStraw::DECOMPRESS); + uncomp.Get_From(&bpipe); + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + OverlayType classid; + + uncomp.Get(&classid, sizeof(classid)); + + if (classid != OVERLAY_NONE) { + + if (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } + } + } + + if (NewINIFormat < 2 || ini.Is_Present("Overlay")) { + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + CELL cell = atoi(entry); + OverlayType classid = ini.Get_OverlayType(INI_Name(), entry, OVERLAY_NONE); + + /* + ** Don't allow placement of crates in the multiplayer scenarios. + */ + if (classid != OVERLAY_NONE && (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate)) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } +} + + +void OverlayClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + ini.Clear("OverlayPack"); + + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + LCWPipe comppipe(LCWPipe::COMPRESS); + + comppipe.Put_To(&bpipe); + + int total = 0; + CellClass * cellptr = &Map[(CELL)0]; + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + total += comppipe.Put(&cellptr->Overlay, sizeof(cellptr->Overlay)); + cellptr++; + } + if (total) { + ini.Put_UUBlock("OverlayPack", _staging_buffer, total); + } + +// for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { +// CellClass * cellptr = &Map[index]; +// if (cellptr->Overlay != OVERLAY_NONE) { +// char uname[10]; +// sprintf(uname, "%d", index); +// ini.Put_Overlay(INI_Name(), uname, cellptr->Overlay); +// } +// } +} \ No newline at end of file diff --git a/REDALERT/OVERLAY.H b/REDALERT/OVERLAY.H new file mode 100644 index 000000000..64c8f49e4 --- /dev/null +++ b/REDALERT/OVERLAY.H @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OVERLAY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the overlay object. Overlay objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class OverlayClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the overlay object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + OverlayClass(OverlayType type, CELL pos=-1, HousesType = HOUSE_NONE); + OverlayClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~OverlayClass(void) {if (GameActive) OverlayClass::Limbo();Class=0;}; + operator OverlayType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "OVERLAY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Virtual support functionality. + */ + virtual bool Mark(MarkType); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + private: + /* + ** This is used to control the marking process of the overlay. If this is + ** set to a valid house number, then the cell that the overlay is marked down + ** upon will be flagged as being owned by the specified house. + */ + static HousesType ToOwn; + + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/PACKET.CPP b/REDALERT/PACKET.CPP new file mode 100644 index 000000000..7b63cb889 --- /dev/null +++ b/REDALERT/PACKET.CPP @@ -0,0 +1,463 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) //PG + +#include +#include +#include + +enum {false=0,true=1}; +typedef int bool; + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass(void) +{ + FieldClass *current; + FieldClass *next; + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete current; + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *(unsigned short *)curbuf; + curbuf += sizeof (unsigned short); + Size = ntohs(Size); + ID = *(short *)curbuf; + curbuf += sizeof (short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *(unsigned short *)curbuf = (unsigned short)htons((unsigned short)size); + curbuf += sizeof (unsigned short); + *(short *)curbuf = htons(ID); + curbuf += sizeof (short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + + // + // If there is any pad left over, make sure you memset it + // to zeros, so it looks like a pad. + // + if (pad) { + memset(curbuf, 0, pad); + curbuf += pad; + } + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, min(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} +#endif \ No newline at end of file diff --git a/REDALERT/PACKET.H b/REDALERT/PACKET.H new file mode 100644 index 000000000..367d80262 --- /dev/null +++ b/REDALERT/PACKET.H @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __PACKET_H +#define __PACKET_H + + +#include "field.h" + +class PacketClass { + public: + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(void); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + bool Get_Field(char *id, char &data); + bool Get_Field(char *id, unsigned char &data); + bool Get_Field(char *id, short &data); + bool Get_Field(char *id, unsigned short &data); + bool Get_Field(char *id, long &data); + bool Get_Field(char *id, unsigned long &data); + bool Get_Field(char *id, char *data); + bool Get_Field(char *id, void *data, int &length); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + + +#endif diff --git a/REDALERT/PALETTEC.CPP b/REDALERT/PALETTEC.CPP new file mode 100644 index 000000000..093b28f5f --- /dev/null +++ b/REDALERT/PALETTEC.CPP @@ -0,0 +1,450 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/palette.cpp 2 9/23/97 11:00p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PALETTE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PaletteClass::Adjust -- Adjusts the palette toward another palette. * + * PaletteClass::Adjust -- Adjusts this palette toward black. * + * PaletteClass::Closest_Color -- Finds closest match to color specified. * + * PaletteClass::Set -- Fade the display palette to this palette. * + * PaletteClass::PaletteClass -- Constructor that fills palette with color specified. * + * PaletteClass::operator = -- Assignment operator for palette objects. * + * PaletteClass::operator == -- Equality operator for palette objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + +void __cdecl Set_Palette(void * palette); + + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +#include "function.h" +#include "watcom.h" +#include "palette.h" +#include "palettec.h" +#include "ftimer.h" +//#define TIMER_H +#include "wwlib32.h" +//#include "timer.h" +#include + +#ifndef SYSTEM_TIMER_CLASS +#define SYSTEM_TIMER_CLASS + +#ifdef WIN32 +extern WinTimerClass * WindowsTimer; +#endif + +class SystemTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + #else + long operator () (void) const {return(Get_System_Tick_Count());}; + operator long (void) const {return(Get_System_Tick_Count());}; + #endif +}; +#endif + + +//PaletteClass const PaletteClass::CurrentPalette; +extern "C" unsigned char CurrentPalette[]; + +PaletteClass const & PaletteClass::CurrentPalette = *(PaletteClass *)&::CurrentPalette[0]; + + +/*********************************************************************************************** + * PaletteClass::PaletteClass -- Constructor that fills palette with color specified. * + * * + * This constructor will fill the palette with the color specified. * + * * + * INPUT: rgb -- Reference to the color to fill the entire palette with. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +PaletteClass::PaletteClass(RGBClass const & rgb) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + Palette[index] = rgb; + } +} + + +/*********************************************************************************************** + * PaletteClass::operator == -- Equality operator for palette objects. * + * * + * This is the comparison for equality operator. It will compare palette objects to * + * determine if they are identical. * + * * + * INPUT: palette -- Reference to the palette to compare to this palette. * + * * + * OUTPUT: Are the two palettes identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int PaletteClass::operator == (PaletteClass const & palette) const +{ + if (this == &palette) return(true); + return(memcmp(&Palette[0], &palette.Palette[0], sizeof(Palette)) == 0); +} + + +/*********************************************************************************************** + * PaletteClass::operator = -- Assignment operator for palette objects. * + * * + * This is the assignment operator for palette objects. Although the default C++ generated * + * assignment operator would function correctly, it would not check for self-assignment * + * and thus this routine can be faster. * + * * + * INPUT: palette -- Reference to that palette that will be copied into this palette. * + * * + * OUTPUT: Returns with a reference to the newly copied to palette. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +PaletteClass & PaletteClass::operator = (PaletteClass const & palette) +{ + if (this == &palette) return(*this); + + memcpy(&Palette[0], &palette.Palette[0], sizeof(Palette)); + return(*this); +} + + +/*********************************************************************************************** + * PaletteClass::Adjust -- Adjusts this palette toward black. * + * * + * This routine is used to adjust this palette toward black. Typical use of this routine * + * is when fading the palette to black. * + * * + * INPUT: ratio -- The ratio to fade this palette to black. 0 means no fading at all. 255 * + * means 100% faded to black. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't actually set the palette to the video card. Use the Set() * + * function to achieve that purpose. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Adjust(int ratio) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + Palette[index].Adjust(ratio, BlackColor); + } +} + + +/*********************************************************************************************** + * PaletteClass::Adjust -- Adjusts the palette toward another palette. * + * * + * This routine is used to adjust a palette toward a destination palette by the ratio * + * specified. This is primarily used by the palette fading routines. * + * * + * INPUT: palette -- Reference to the destination palette. * + * * + * ratio -- The ratio to adjust this palette toward the destination palette. A * + * value of 0 means no adjustment at all. A value of 255 means 100% * + * adjustment. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Adjust(int ratio, PaletteClass const & palette) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + +//if (index == 1) { +// Mono_Printf("From R=%d,G=%d,B=%d ", Palette[index].Red_Component(), Palette[index].Green_Component(), Palette[index].Blue_Component()); +// Mono_Printf("To R=%d,G=%d,B=%d [%d] ", palette[index].Red_Component(), palette[index].Green_Component(), palette[index].Blue_Component(), ratio); +//} + Palette[index].Adjust(ratio, palette[index]); + +//if (index == 1) { +// Mono_Printf("Equals R=%d,G=%d,B=%d.\n", Palette[index].Red_Component(), Palette[index].Green_Component(), Palette[index].Blue_Component()); +//} + + } +} + + +/*********************************************************************************************** + * PaletteClass::Partial_Adjust -- Adjusts the specified parts of this palette toward black. * + * * + * This routine is used to adjust this palette toward black. Typical use of this routine * + * is when fading the palette to black. The input lookup table is used to determine * + * which entries should fade and which should stay the same * + * * + * INPUT: ratio -- The ratio to fade this palette to black. 0 means no fading at all. 255 * + * means 100% faded to black. * + * * + * lookup -- ptr to lookup table * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't actually set the palette to the video card. Use the Set() * + * function to achieve that purpose. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Partial_Adjust(int ratio, char *lut) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + if (lut[index]) { + Palette[index].Adjust(ratio, BlackColor); + } + } +} + + +/*********************************************************************************************** + * PaletteClass::Partial_Adjust -- Adjusts the palette toward another palette. * + * * + * This routine is used to adjust a palette toward a destination palette by the ratio * + * specified. This is primarily used by the palette fading routines. The input lookup * + * table is used to determine which entries should fade and which should stay the same * + * * + * * + * INPUT: palette -- Reference to the destination palette. * + * * + * ratio -- The ratio to adjust this palette toward the destination palette. A * + * value of 0 means no adjustment at all. A value of 255 means 100% * + * adjustment. * + * * + * lookup -- ptr to lookup table * + * * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Partial_Adjust(int ratio, PaletteClass const & palette, char *lut) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + if (lut[index]) { + Palette[index].Adjust(ratio, palette[index]); + } + } +} + + +/*********************************************************************************************** + * PaletteClass::Closest_Color -- Finds closest match to color specified. * + * * + * This routine will examine the palette and return with the color index number for the * + * color that most closely matches the color specified. Remap operations rely heavily on * + * this routine to allow working with a constant palette. * + * * + * INPUT: rgb -- Reference to a color to search for in the current palette. * + * * + * OUTPUT: Returns with a color index value to most closely matches the specified color. * + * * + * WARNINGS: This routine will quite likely not find an exact match. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int PaletteClass::Closest_Color(RGBClass const & rgb) const +{ + int closest = 0; + int value = -1; + + RGBClass const * ptr = &Palette[0]; + for (int index = 0; index < COLOR_COUNT; index++) { + int difference = rgb.Difference(*ptr++); + if (value == -1 || difference < value) { + value = difference; + closest = index; + } + } + return(closest); +} + + +#ifndef WIN32 +extern void Vsync(void); +#pragma aux Vsync modify [edx ebx eax] = \ + "mov edx,03DAh" \ + "mov ebx,[VertBlank]" \ + "and bl,001h" \ + "shl bl,3" \ + "in_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "je in_vbi" \ + "out_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "jne out_vbi" +#endif //WIN32 + + +/*********************************************************************************************** + * PaletteClass::Set -- Fade the display palette to this palette. * + * * + * This routine will fade the display palette to match this palette over the time period * + * specified. For smooth palette transitions, this is the routine to call. * + * * + * INPUT: time -- The time period (in system tick increments) to fade the display palette * + * to match this palette. * + * * + * callback -- Optional pointer to callback function that, if non-null, will be * + * called as often as possible during the fading process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will not return until the palette is completely faded to the * + * destination palette. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + * 02/05/1996 JLB : Uses new timer system. * + *=============================================================================================*/ +void PaletteClass::Set(int time, void (* callback)(void)) const +{ + CDTimerClass timer = time; + PaletteClass original = CurrentPalette; + + while (timer) { + + /* + ** Build an intermediate palette that is as close to the destination palette + ** as the current time is proportional to the ending time. + */ + PaletteClass palette = original; + int adjust = ((time - timer) * 256) / time; + palette.Adjust(adjust, *this); + + /* + ** Remember the current time so that multiple palette sets within the same game + ** time tick won't occur. This is probably unnecessary since the palette setting + ** code, at the time of this writing, delays at least one game tick in the process + ** of setting the palette. + */ + long holdtime = timer; + + /* + ** Set the palette to this intermediate palette and then loop back + ** to calculate and set a new intermediate palette. + */ +#ifdef WIN32 + Set_Palette((void*)&palette[0]); +#else + palette.Set(); +#endif //WIN32 + + /* + ** If the callback routine was specified, then call it once per palette + ** setting loop. + */ + if (callback) { + callback(); + } + + /* + ** This loop ensures that the palette won't be set more than once per game tick. Setting + ** the palette more than once per game tick will have no effect since the calculation will + ** result in the same intermediate palette that was previously calculated. + */ + while (timer == holdtime && holdtime != 0) { + if (callback) callback(); + } + } + + /* + ** Ensure that the final palette exactly matches the requested + ** palette before exiting the fading routine. + */ +#ifndef WIN32 + Vsync(); + RGBClass const * rgbptr = &Palette[0]; + RGBClass::Raw_Color_Prep(0); + for (int index = 0; index < COLOR_COUNT; index++) { + rgbptr->Raw_Set(); + rgbptr++; + } + ((PaletteClass &)CurrentPalette) = *this; +#else //WIN32 + Set_Palette((void*)&Palette[0]); +#endif +} diff --git a/REDALERT/PALETTEC.H b/REDALERT/PALETTEC.H new file mode 100644 index 000000000..d8d7c9ce4 --- /dev/null +++ b/REDALERT/PALETTEC.H @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PALETTE.H 2 9/23/97 11:00p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PALETTE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PALETTE_Hx +#define PALETTE_Hx + +#include "rgb.h" + +/* +** The palette class is used to manipulate a palette as a whole. All 256 colors are +** represented by the palette class object. +*/ +class PaletteClass +{ + public: + enum { + COLOR_COUNT=256 // Number of color indices on the palette. + }; + + PaletteClass(void) {}; + PaletteClass(RGBClass const & rgb); + + RGBClass & operator[] (unsigned index) {return(Palette[index % COLOR_COUNT]);}; + const RGBClass & operator[] (unsigned index) const {return(Palette[index % COLOR_COUNT]);}; + int operator == (PaletteClass const & palette) const; + int operator != (PaletteClass const & palette) const {return(!(operator ==(palette)));}; + PaletteClass & operator = (PaletteClass const & palette); + + // Removed these and replaced with function for clarity. ST - 5/9/2019 + //operator const unsigned char * (void) const {return((const unsigned char *)&Palette[0]);}; + //operator unsigned char * (void) {return((unsigned char *)&Palette[0]);}; + const void *Get_Data(void) const { return &Palette[0]; } + void *Get_Data(void) { return &Palette[0]; } + + void Adjust(int ratio); + void Adjust(int ratio, PaletteClass const & palette); + void Partial_Adjust(int ratio, char *lut); + void Partial_Adjust(int ratio, PaletteClass const & palette, char *lut); + void Set(int time = 0, void (*callback)(void) = 0) const; + int Closest_Color(RGBClass const & rgb) const; + + static PaletteClass const & CurrentPalette; + + protected: + RGBClass Palette[COLOR_COUNT]; +}; + + +#endif diff --git a/REDALERT/PIPE.CPP b/REDALERT/PIPE.CPP new file mode 100644 index 000000000..382e6d906 --- /dev/null +++ b/REDALERT/PIPE.CPP @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * Pipe::Flush -- Flush all pending data out the pipe. * + * Pipe::Put -- Feed some data through the pipe. * + * Pipe::~Pipe -- Destructor for pipe class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pipe.h" +#include +#include + + +/*********************************************************************************************** + * Pipe::~Pipe -- Destructor for pipe class object. * + * * + * This destructor will unlink itself from any other pipes that it may be chained to. In * + * the process, it will flush any output it may have pending. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Pipe::~Pipe(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Put_To(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * * + * This routine will link two pipes together. The specified pipe will be fed data from * + * this pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that data will flow to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Pipe::Put_To(Pipe * pipe) +{ + if (ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + ChainTo->Flush(); + } + + ChainTo = pipe; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Pipe::Put -- Feed some data through the pipe. * + * * + * Use this to force feed data through the pipe. It is guaranteed to accept data as fast * + * as you can supply it. * + * * + * INPUT: source -- Pointer to the data to feed to this routine. * + * * + * length -- The number of bytes of data to submit. * + * * + * OUTPUT: Returns with the number of bytes actually output at the other far distant final * + * end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Put(void const * source, int length) +{ + if (ChainTo != NULL) { + return(ChainTo->Put(source, length)); + } + return(length); +} + + +/*********************************************************************************************** + * Pipe::Flush -- Flush all pending data out the pipe. * + * * + * Then the pipe needs to be flushed, this routine will be called. Since pipe segments * + * might have internal staging buffer for the data, this routine is necessary to force * + * all staging buffers to be clear. This routine is called when the pipe is being * + * destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Flush(void) +{ + if (ChainTo != NULL) { + return(ChainTo->Flush()); + } + return(0); +} + + diff --git a/REDALERT/PIPE.H b/REDALERT/PIPE.H new file mode 100644 index 000000000..cc1971ece --- /dev/null +++ b/REDALERT/PIPE.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PIPE_H +#define PIPE_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** A "push through" pipe interface abstract class used for such purposes as compression +** and translation of data. In STL terms, this is functionally similar to an output +** iterator but with a few enhancements. A pipe class object that is not derived into +** another useful class serves only as a pseudo null-pipe. It will accept data but +** just throw it away but pretend that it sent it somewhere. +*/ +class Pipe +{ + public: + Pipe(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Pipe(void); + + virtual int Flush(void); + virtual int End(void) {return(Flush());} + virtual void Put_To(Pipe * pipe); + void Put_To(Pipe & pipe) {Put_To(&pipe);} + virtual int Put(void const * source, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Pipe * ChainTo; + Pipe * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Pipe(Pipe & rvalue); + Pipe & operator = (Pipe const & pipe); +}; + +#endif diff --git a/REDALERT/PK.CPP b/REDALERT/PK.CPP new file mode 100644 index 000000000..f935d0e83 --- /dev/null +++ b/REDALERT/PK.CPP @@ -0,0 +1,360 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * PKey::Generate -- Generate a public and private key. * + * PKey::PKey -- Construct a key using encoded strings. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "pk.h" +#include "rndstraw.h" + + +/*********************************************************************************************** + * PKey::PKey -- Construct a key using encoded strings. * + * * + * This constructor will construct a key based on the encoded strings supplied. * + * * + * INPUT: exponent -- The encoded string for the exponent portion of the key. * + * * + * modulus -- The encoded string for the modulus portion of the key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey::PKey(void const * exponent, void const * modulus) +{ + Modulus.DERDecode((unsigned char *)modulus); + Exponent.DERDecode((unsigned char *)exponent); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * * + * This will store the modulus portion of the key into a buffer. The number of bytes * + * stored into the buffer depends on the value of the key. * + * * + * INPUT: buffer -- Pointer to the buffer that will hold the encoded modulus value. * + * * + * OUTPUT: Returns with the number of bytes stored to the buffer. * + * * + * WARNINGS: Be sure that the buffer can hold the encoded bytes. This is normally around the * + * same size as the Crypt_Block_Size() (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Modulus(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Modulus.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * * + * This routine will encode the exponent portion of the key. This is only necessary for the * + * slow key since the fast key always has an exponent of 65537. * + * * + * INPUT: buffer -- Pointer to the buffer that will be filled with the encoded exponent. * + * * + * OUTPUT: Returns with the nuber of bytes stored into the buffer. * + * * + * WARNINGS: Be sure the buffer is big enough to hold the encoded exponent. Usually this is * + * about the same size as the Crypt_Block_Size (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Exponent(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Exponent.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * * + * This is the counterpart to the Encode_Modulus() function. It will initialize the * + * modulus portion of the key with the encoded data supplied. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the previously encoded modulus value. * + * * + * OUTPUT: void * + * * + * WARNINGS: void * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Modulus(void * buffer) +{ + Modulus.DERDecode((unsigned char *)buffer); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * * + * This is the counterpart to the Encode_Exponent function. It will decode a previously * + * encoded exponent portion back into the key. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the encoded exponent value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Exponent(void * buffer) +{ + Exponent.DERDecode((unsigned char *)buffer); +} + + +/*********************************************************************************************** + * PKey::Generate -- Generate a public and private key. * + * * + * Public key cryptography relies on having two paired keys. The key used to encrypt * + * data must be decrypted by using the other key. Which key designated as the public or * + * private key is arbitrary. However, one is faster than the other. Use the faster key for * + * the more common operation. * + * * + * INPUT: random -- Reference to a source of random data. * + * * + * bits -- The number of bits to use for key generation. Use a number greater * + * than 16 but less than 2048. The ideal bit size is one that is evenly * + * divisible by 8 and then add one. Practical numbers range from 65 to * + * 1025 bits. * + * * + * fastkey -- Reference to the key that has fast encryption/decryption properties. * + * * + * slowkey -- Reference to the mate key of the other. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can take a very long time. It can take MINUTES to generate a * + * 1024 bit key (even on a Pentium Pro 200Mghz machine). * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + * 07/10/1996 JLB : Must supply source of random data. * + *=============================================================================================*/ +void PKey::Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey) +{ + //PG_TO_FIX + fastkey; + slowkey; +#if (0) + /* + ** Key generation consists of create a key pair and then testing the key + ** pair. If the test fails, then repeat the process. The test and repeat + ** method is required since the prime number generating process can't + ** guarantee the generation of a prime number -- it can only generate a + ** highly likely prime number. + */ + for (;;) { + /* + ** Generate the two random prime numbers. This is the longest + ** step. + */ + BigInt p = Generate_Prime(random, bits, &p); + BigInt q = Generate_Prime(random, bits, &q); + + /* + ** The exponent factors are easy to calculate from the prime numbers. + */ + BigInt e = Fast_Exponent(); + BigInt n = p * q; + BigInt pqmin = (p-(unsigned short)1)*(q-(unsigned short)1); + BigInt d = e.Inverse(pqmin); + + /* + ** Store the data into the key objects. Notice that the modulus is the + ** same for both the fast and slow keys. Also notice that the exponent for + ** the fast key is ALWAYS 65537. Given this, it is possible to economize the + ** fast key into being just the modulus and the slow key to being just the + ** exponent (presuming the slow key also has access to the fast key so that + ** it can get the modulus). + */ + fastkey.Exponent = e; + fastkey.Modulus = n; + fastkey.BitPrecision = n.BitCount()-1; + + slowkey.Exponent = d; + slowkey.Modulus = n; + slowkey.BitPrecision = fastkey.BitPrecision; + + /* + ** Test the keys by encrypting a block of random bytes. If it decrypts + ** correctly, then a valid key pair has been generated -- bail. + */ + char before[256]; + char after[256]; + + for (int index = 0; index < fastkey.Plain_Block_Size(); index++) { + before[index] = (char)rand(); + } + fastkey.Encrypt(before, fastkey.Plain_Block_Size(), after); + slowkey.Decrypt(after, slowkey.Crypt_Block_Size(), after); + + /* + ** Compare the pre and post processing buffer. A match indicates + ** a valid key pair. + */ + if (memcmp(before, after, fastkey.Plain_Block_Size()) == 0) break; + } +#endif +} + + +/*********************************************************************************************** + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * * + * This routine will encrypt the supplied plaintext into cyphertext by processing the input * + * in block. The source is processed in whole blocks. Partial blocks are not supported by * + * public key cryptography. * + * * + * INPUT: source -- Pointer to the source plaintext that will be encrypted. * + * * + * length -- The length of the plaintext to encrypt. * + * * + * dest -- Pointer to the buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the number of cypher text bytes placed into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the output. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + + /* + ** Encrypt the source data in full blocks. Partial blocks are not processed and are not + ** copied to the destination buffer. + */ + while (slen >= Plain_Block_Size()) { + + /* + ** Perform the encryption of the block. + */ + BigInt temp = 0; + memmove(&temp, source, Plain_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Crypt_Block_Size()); + slen -= Plain_Block_Size(); + source = (char *)source + Plain_Block_Size(); + dest = (char *)dest + Crypt_Block_Size(); + total += Crypt_Block_Size(); + } + + return(total); +} + + +/*********************************************************************************************** + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * * + * This routine will process the supplied cyphertext by breaking it up into blocks and * + * then decrypting each block in turn. The block size is dependant upon the key. By NOT * + * embedding this information into the cypher data, it makes the encryption more secure. * + * * + * INPUT: source -- Pointer to the cypher text to be decrypted. * + * * + * length -- The number of cypher text bytes supplied to this routine. * + * * + * dest -- Pointer to the buffer to hold the plaintext. * + * * + * OUTPUT: Returns with the number of plaintext bytes output to the destination buffer. * + * * + * WARNINGS: Only whole blocks are processed. If the source has any partial block sized * + * data, then it will be left unprocessed. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Decrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + BigInt temp; + + /* + ** Decrypt the source data in full blocks. Partial blocks are not processed in any way. + */ + while (slen >= Crypt_Block_Size()) { + + /* + ** Perform the encryption. + */ + temp = 0; + memmove(&temp, source, Crypt_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Plain_Block_Size()); + slen -= Crypt_Block_Size(); + source = (char *)source + Crypt_Block_Size(); + dest = (char *)dest + Plain_Block_Size(); + total += Plain_Block_Size(); + } + + return(total); +} diff --git a/REDALERT/PK.H b/REDALERT/PK.H new file mode 100644 index 000000000..f19742403 --- /dev/null +++ b/REDALERT/PK.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PK_H +#define PK_H + +#include "int.h" + +/* +** This class holds a public or private key used in Public Key Cryptography. It also serves +** as the conduit for encrypting/decrypting data using that key. Cryptography, using this +** method, has a couple of characteristics that affect how it is used. One, the process of +** encrypting/decrypting is very slow. This limits the effective quantity of data that can +** be processed. Two, the ciphertext is larger than the plaintext. This property generally +** limits its use to streaming data as opposed to random access data. The data is processed +** in blocks. The size of the ciphertext and plaintext blocks can be determined only from +** the key itself. +** +** A reasonable use of this technology would be to encrypt only critical data such as the +** password for a fast general purpose cryptographic algorithm. +*/ +class PKey +{ + public: + PKey(void) : Modulus(0), Exponent(0), BitPrecision(0) {} + PKey(void const * exponent, void const * modulus); // DER initialization. + + int Encrypt(void const * source, int slen, void * dest) const; + int Decrypt(void const * source, int slen, void * dest) const; + + static void Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey); + + int Plain_Block_Size(void) const {return((BitPrecision-1)/8);} + int Crypt_Block_Size(void) const {return(Plain_Block_Size()+1);} + int Block_Count(int plaintext_length) const {return((((plaintext_length-1)/Plain_Block_Size())+1));} + + int Encode_Modulus(void * buffer) const; + int Encode_Exponent(void * buffer) const; + + void Decode_Modulus(void * buffer); + void Decode_Exponent(void * buffer); + + static long Fast_Exponent(void) {return(65537L);} + + private: + + // p*q + BigInt Modulus; + + // 65537 or + // inverse of (p-1)(q-1). + BigInt Exponent; + + // Maximum bits allowed for block. + int BitPrecision; +}; + + +#endif diff --git a/REDALERT/PKPIPE.CPP b/REDALERT/PKPIPE.CPP new file mode 100644 index 000000000..2562872cf --- /dev/null +++ b/REDALERT/PKPIPE.CPP @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * Project Name : Command & Conquer * + * * + * * + * File Name : PKPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/07/96 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * PKPipe::Put -- Submit data to the pipe for processing. * + * PKPipe::Put_To -- Chains one pipe to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkpipe.h" + + +/*********************************************************************************************** + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * * + * This will construct the public key pipe object. * + * * + * INPUT: control -- The method used to process the data flow (encrypt or decrypt). * + * * + * rnd -- Reference to a random number generate used to create the internal * + * blowfish key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKPipe::PKPipe(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowPipe::ENCRYPT : BlowPipe::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ +} + + +/*********************************************************************************************** + * PKPipe::Put_To -- Chains one pipe to another. * + * * + * This handles linking of one pipe to this pipe. Data will flow from this PKPipe to the * + * pipe segment specified. Special handling is done so that piping actually flows to the * + * embedded blowfish pipe and then flows to the designated pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that this pipe segment is to send data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Put_To(Pipe * pipe) +{ + if (BF.ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + BF.ChainTo = pipe; + if (pipe != NULL) { + pipe->ChainFrom = &BF; + } + BF.ChainFrom = this; + ChainTo = &BF; + } +} + + +/*********************************************************************************************** + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * * + * This routine must be called with a valid key pointer in order for encryption/description * + * to be performed on the data stream. Prior to calling this routine or after calling this * + * routine with a NULL pointer, the data stream will pass through this pipe without * + * modification. * + * * + * INPUT: key -- Pointer to the key to use for processing. Pass NULL if process is to be * + * terminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Key(PKey const * key) +{ + if (key == NULL) { + Flush(); + IsGettingKey = false; + } + CipherKey = key; + + if (CipherKey != NULL) { + IsGettingKey = true; + if (Control == DECRYPT) { + Counter = BytesLeft = Encrypted_Key_Length(); + } + } +} + + +/*********************************************************************************************** + * PKPipe::Put -- Submit data to the pipe for processing. * + * * + * This routine (if processing as been enabled by a previous key submission) will * + * encrypt or decrypt the data stream that passes through it. When encrypting, the data * + * stream will increase in size by about 10% (bit it varies according to the key used). * + * * + * INPUT: source -- Pointer to the data to be submitted to the pipe stream. * + * * + * length -- The number of bytes submitted. * + * * + * OUTPUT: Returns with the actual number of byte output at the final end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Put(void const * source, int length) +{ + /* + ** If the parameter seem illegal, then pass the pipe request to the + ** next pipe in the chain and let them deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Pipe::Put(source, length)); + } + + int total = 0; + + /* + ** Perform a special process if the this is the first part of the data flow. The special + ** key must be processed first. After this initial key processing, the rest of the data flow + ** is processed by the blowfish pipe and ignored by the PKPipe. + */ + if (IsGettingKey) { + + /* + ** When encrypting, first make the key block and then pass the data through the + ** normal blowfish processor. + */ + if (Control == ENCRYPT) { + + /* + ** Generate the largest blowfish key possible. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + int didput = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + total += Pipe::Put(Buffer, didput); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + + } else { + + /* + ** First try to accumulate a full key. + */ + int toget = (BytesLeft < length) ? BytesLeft : length; + memmove(&Buffer[Counter-BytesLeft], source, toget); + length -= toget; + BytesLeft -= toget; + source = (char *)source + toget; + + /* + ** If a full key has been accumulated, then decrypt it and feed the + ** key to the blowfish engine. + */ + if (BytesLeft == 0) { + char buffer[MAX_KEY_BLOCK_SIZE]; + CipherKey->Decrypt(Buffer, Counter, buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + } + } + } + + /* + ** If there are any remaining bytes to pipe through, then + ** pipe them through now -- they will be processed by the + ** blowfish engine. + */ + total += Pipe::Put(source, length); + return(total); +} + + +/*********************************************************************************************** + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/REDALERT/PKPIPE.H b/REDALERT/PKPIPE.H new file mode 100644 index 000000000..3f9160812 --- /dev/null +++ b/REDALERT/PKPIPE.H @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/06/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PKPIPE_H +#define PKPIPE_H + +#include "pipe.h" +#include "pk.h" +#include "rndstraw.h" +#include "blowpipe.h" + + +/* +** This pipe will encrypt/decrypt the data stream. The data is encrypted by generating a +** symetric key that is then encrypted using the public key system. This symetric key is then +** used to encrypt the remaining data. +*/ +class PKPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKPipe(CryptControl control, RandomStraw & rnd); + + virtual void Put_To(Pipe * pipe); + virtual void Put_To(Pipe & pipe) {Put_To(&pipe);} + + // Feed data through for processing. + virtual int Put(void const * source, int length); + + // Submit key for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowPipe BF; + + /* + ** Controls the method of processing the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption/decryption. The actual process + ** performed is controlled by the Control member. A key can be used for + ** either encryption or decryption -- it makes no difference. However, whichever + ** process is performed, the opposite process must be performed using the + ** other key. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key (whichever is greater). + */ + char Buffer[MAX_KEY_BLOCK_SIZE]; + + /* + ** The working counter that holds the number of bytes in the staging buffer. + */ + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKPipe(PKPipe & rvalue); + PKPipe & operator = (PKPipe const & pipe); +}; + + +#endif diff --git a/REDALERT/PKSTRAW.CPP b/REDALERT/PKSTRAW.CPP new file mode 100644 index 000000000..910e4f4ac --- /dev/null +++ b/REDALERT/PKSTRAW.CPP @@ -0,0 +1,301 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKStraw::Get -- Fetch data and process it accordingly. * + * PKStraw::Get_From -- Chains one straw to another. * + * PKStraw::Key -- Assign a key to the cipher process straw. * + * PKStraw::PKStraw -- Initialize the public key straw object. * + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + + +/*********************************************************************************************** + * PKStraw::PKStraw -- Initialize the public key straw object. * + * * + * This constructs the public key straw object. The operation to perform (encrypt or * + * decrypt) as well as a random number generator must be provided. * + * * + * INPUT: control -- What operation to perform on the data. Pass in either ENCRYPT or * + * DECRYPT. * + * * + * rnd -- Reference to a random number straw that is used internally to * + * generate the sub-key. The final strength of the cipher depends on * + * quality of this random number generator. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKStraw::PKStraw(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowStraw::ENCRYPT : BlowStraw::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ + Straw::Get_From(BF); +} + + +/*********************************************************************************************** + * PKStraw::Get_From -- Chains one straw to another. * + * * + * This routine handles the special case of this straw object in that there is an * + * embedded blowfish straw segment. It must be chained on correctly. * + * * + * INPUT: straw -- Pointer to the straw segment that this segment is to receive data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Get_From(Straw * straw) +{ + if (BF.ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + + BF.ChainTo = straw; + BF.ChainFrom = this; + ChainTo = &BF; + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = this; + } + } +} + + + +/*********************************************************************************************** + * PKStraw::Key -- Assign a key to the cipher process straw. * + * * + * This routine will assign the key (or NULL if the current key is to be removed) to the * + * cipher stream process. When a key has been assigned, encryption or decryption will * + * take place. In the absence (NULL key pointer) of a key, the data passes through * + * unchanged. * + * * + * INPUT: key -- Pointer to the key to assign to the stream. If the key pointer is NULL, * + * then this causes the cipher stream to stop processing the data and will * + * pass the data through unchanged. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the key passed to this routine is the opposite key to that used * + * to process the stream originally (when decrypting). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Key(PKey const * key) +{ + CipherKey = key; + if (key != NULL) { + IsGettingKey = true; + } + Counter = 0; + BytesLeft = 0; +} + + +/*********************************************************************************************** + * PKStraw::Get -- Fetch data and process it accordingly. * + * * + * This routine will fetch the number of bytes requested. If a valid key has been assigned * + * to this stream, then the data will be processed as it passes through. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of data bytes stored to the destination buffer. If * + * this number is less than that requested, then it indicates that the data source * + * has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Get(void * source, int length) +{ + /* + ** If the parameters seem invalid, then pass the request on so that someone + ** else can deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Straw::Get(source, length)); + } + + int total = 0; + + /* + ** The first part of the data flow must process the special key. After the special + ** key has been processed, the data flows through this straw without direct + ** modification (the blowfish straw will process the data). + */ + if (IsGettingKey) { + + if (Control == DECRYPT) { + + /* + ** Retrieve the pk encrypted blowfish key block. + */ + char cbuffer[MAX_KEY_BLOCK_SIZE]; + int got = Straw::Get(cbuffer, Encrypted_Key_Length()); + + /* + ** If the entire key block could not be retrieved, then this indicates + ** a major data flow error -- just return with no action performed. + */ + if (got != Encrypted_Key_Length()) return(0); + + /* + ** Decrypt the blowfish key and then activate the blowfish straw + ** with that key. + */ + CipherKey->Decrypt(cbuffer, got, Buffer); + BF.Key(Buffer, BLOWFISH_KEY_SIZE); + + } else { + + /* + ** Generate the blowfish key by using random numbers. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + Counter = BytesLeft = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + } + + /* + ** The first phase of getting the special key has been accomplished. Now, all + ** subsequent data is passed (unmodified) though this straw segment. The blowfish + ** straw takes over the compression/decompression from this point forward. + */ + IsGettingKey = false; + } + + /* + ** If there are any pending bytes in the buffer, then pass + ** these on first. The only time this should be is when the blowfish + ** key has first been generated. + */ + if (BytesLeft > 0) { + int tocopy = (length < BytesLeft) ? length : BytesLeft; + memmove(source, &Buffer[Counter-BytesLeft], tocopy); + source = (char *)source + tocopy; + BytesLeft -= tocopy; + length -= tocopy; + total += tocopy; + } + + /* + ** Any requested bytes that haven't been satisfied are copied over now by + ** drawing the data through the blowfish engine. The blowfish engine happens + ** to be linked to the chain so a normal Get() operation is sufficient. + */ + total += Straw::Get(source, length); + + return(total); +} + + +/*********************************************************************************************** + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/REDALERT/PKSTRAW.H b/REDALERT/PKSTRAW.H new file mode 100644 index 000000000..f4695663e --- /dev/null +++ b/REDALERT/PKSTRAW.H @@ -0,0 +1,122 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PKSTRAW_H +#define PKSTRAW_H + +#include "pk.h" +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + +class PKStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKStraw(CryptControl control, RandomStraw & rnd); + + virtual void Get_From(Straw * straw); + virtual void Get_From(Straw & straw) {Get_From(&straw);} + + virtual int Get(void * source, int slen); + + // Submit key to be used for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowStraw BF; + + /* + ** This control member tells what method (encryption or decryption) that should + ** be performed on the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption or decryption. If this pointer is NULL, then + ** the data passing through this segment will not be modified. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key size (whichever is + ** greater). + */ + char Buffer[256]; + + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKStraw(PKStraw & rvalue); + PKStraw & operator = (PKStraw const & straw); +}; + +#endif diff --git a/REDALERT/POWER.CPP b/REDALERT/POWER.CPP new file mode 100644 index 000000000..1efabb025 --- /dev/null +++ b/REDALERT/POWER.CPP @@ -0,0 +1,475 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/POWER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PowerClass::AI -- Process the power bar logic. * + * PowerClass::Draw_It -- Renders the power bar graphic. * + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * PowerClass::One_Time -- One time processing for the power bar. * + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Points to the shape to use for the "desired" power level indicator. +*/ +void const * PowerClass::PowerShape; +void const * PowerClass::PowerBarShape; + +PowerClass::PowerButtonClass PowerClass::PowerButton; + + +/*********************************************************************************************** + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * * + * This is the default constructor for the power bar class. It doesn't really do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +PowerClass::PowerClass(void) : + IsToRedraw(false), + IsActive(false), + FlashTimer(0), + RecordedDrain(-1), + RecordedPower(-1), + DesiredDrainHeight(0), + DesiredPowerHeight(0), + DrainHeight(0), + PowerHeight(0), + DrainBounce(0), + PowerBounce(0), + PowerDir(0), + DrainDir(0) +{ +} + + +/*********************************************************************************************** + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * * + * This routine is called in preparation for the start of a scenario. The power bar is * + * initialized into the null state by this routine. As soon as the scenario starts, the * + * power bar will rise to reflect the actual power output and drain. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Init_Clear(void) +{ + RadarClass::Init_Clear(); + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; + FlashTimer = 0; +} + + +/*********************************************************************************************** + * PowerClass::One_Time -- One time processing for the power bar. * + * * + * This routine is for code that truly only needs to be done once per game run. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void PowerClass::One_Time(void) +{ + RadarClass::One_Time(); + PowerButton.X = POWER_X * RESFACTOR; + PowerButton.Y = POWER_Y * RESFACTOR; + PowerButton.Width = (POWER_WIDTH * RESFACTOR)-1; + PowerButton.Height = POWER_HEIGHT * RESFACTOR; + PowerShape = MFCD::Retrieve("POWER.SHP"); + PowerBarShape = MFCD::Retrieve("POWERBAR.SHP"); +} + + +/*********************************************************************************************** + * PowerClass::Draw_It -- Renders the power bar graphic. * + * * + * This routine will draw the power bar graphic to the LogicPage. * + * * + * INPUT: complete -- Should the power bar be redrawn even if it isn't specifically flagged * + * to do so? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/27/1994 JLB : Changes power bar color depending on amount of power. * + *=============================================================================================*/ +void PowerClass::Draw_It(bool complete) +{ + static int _modtable[]={ + 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 + }; + + if (complete || IsToRedraw) { + BStart(BENCH_POWER); + + if (LogicPage->Lock()) { + if (Map.IsSidebarActive) { + IsToRedraw = false; + ShapeFlags_Type flags = SHAPE_NORMAL; + void const * remap = NULL; + + if (FlashTimer > 1 && ((FlashTimer % 3) & 0x01) != 0) { + flags = flags | SHAPE_FADING; + remap = Map.FadingRed; + } + +// LogicPage->Fill_Rect(POWER_X, POWER_Y, POWER_X+POWER_WIDTH-1, POWER_Y+POWER_HEIGHT-1, LTGREY); + CC_Draw_Shape(PowerBarShape, 0, 240 * RESFACTOR, 88 * RESFACTOR, WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); + +#ifdef WIN32 + /* + ** Hires power strip is too big to fit into a shape so it is in two parts + */ + CC_Draw_Shape(PowerBarShape, 1, 240 * RESFACTOR, (88 * RESFACTOR) + (56*RESFACTOR), WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); +#endif + /* + ** Determine how much the power production exceeds or falls short + ** of power demands. + */ + int bottom = (POWER_Y + POWER_HEIGHT - 1) * RESFACTOR; + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + power_height = Bound(power_height, 0, POWER_HEIGHT - 2); + drain_height = Bound(drain_height, 0, POWER_HEIGHT - 2); + + /* + ** Draw the power output graphic on top of the power bar framework. + */ + if (power_height) { + int color1 = 3; + int color2 = 4; + + if (PlayerPtr->Drain > PlayerPtr->Power) { + color1 = 214; + color2 = 211; + } + if (PlayerPtr->Drain > (PlayerPtr->Power * 2)) { + color1 = 235; + color2 = 230; + } + + /* + ** New power bar is in slightly different place + ** + ** Old power bar was 107 pixels high. New bar is 153 pixels high. + ** + ** ST - 5/2/96 11:23AM + */ +#ifdef WIN32 + power_height = (power_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); + drain_height = (drain_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); +#endif + bottom = (175*RESFACTOR)+1; + + LogicPage->Fill_Rect(245*RESFACTOR, bottom-power_height, 245*RESFACTOR+1, bottom, color2); + LogicPage->Fill_Rect(246*RESFACTOR, bottom-power_height, 246*RESFACTOR+1, bottom, color1); + } + + /* + ** Draw the power drain threshold marker. + */ + CC_Draw_Shape(PowerShape, 0, (POWER_X * RESFACTOR)+RESFACTOR, bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + } + LogicPage->Unlock(); + } + BEnd(BENCH_POWER); + } + RadarClass::Draw_It(complete); +} + + +/*********************************************************************************************** + * PowerClass::AI -- Process the power bar logic. * + * * + * Use this routine to process the power bar logic. This consists of animation effects. * + * * + * INPUT: input -- The player input value to be consumed or ignored as appropriate. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void PowerClass::AI(KeyNumType &input, int x, int y) +{ + if (Map.IsSidebarActive /*IsActive*/) { + int olddrain = DrainHeight; + int oldpower = PowerHeight; + + + /* + ** If the recorded power value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Power != RecordedPower) { + DesiredPowerHeight = Power_Height(PlayerPtr->Power); + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; + if (PowerHeight > DesiredPowerHeight) { + PowerDir = -1; + } else if (PowerHeight < DesiredPowerHeight) { + PowerDir = 1; + } else { + PowerBounce = 0; + } + } + + /* + ** If the recorded drain value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Drain != RecordedDrain) { + DesiredDrainHeight = Power_Height(PlayerPtr->Drain); + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; + if (DrainHeight > DesiredDrainHeight) { + DrainDir = -1; + } else if (DrainHeight < DesiredDrainHeight) { + DrainDir = 1; + } else { + DrainBounce = 0; + } + } + + if (DrainBounce && DrainHeight == DesiredDrainHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + DrainBounce--; + } else { + /* + ** If we need to move the drain height then do so. + */ + if (DrainHeight != DesiredDrainHeight) { + DrainHeight += DrainDir; + } + } + + if (PowerBounce && PowerHeight == DesiredPowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + PowerBounce--; + } else { + /* + ** If we need to move the power height then do so. + */ + if (PowerHeight != DesiredPowerHeight) { + PowerHeight += PowerDir; + } + } + + if (olddrain != DrainHeight || oldpower != PowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Flag to redraw if the power bar flash effect has expired. + */ +// if (FlashTimer == 1) { + if (FlashTimer > 0) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + RadarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * * + * This routine will examine a refresh list request and determine if the sidebar would be * + * affect. If so, it will flag the sidebar to be redrawn. * + * * + * INPUT: cell -- The cell that the offset list is base on. * + * * + * list -- The list of cell offset used to flag for redraw. If the special sidebar * + * affecting cell magic offset number is detected, the sidebar is flagged * + * for redraw and the magic offset is removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + RadarClass::Refresh_Cells(cell, list); +} + + +/*************************************************************************** + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * * + * INPUT: int value - the value we are testing * + * * + * OUTPUT: int the height of the point that this value is on graph * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 PWG : Created. * + *=========================================================================*/ +int PowerClass::Power_Height(int value) +{ + int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power + + /* + ** Loop through the different hundreds figuring out the fractional piece + ** of each. + */ + for (int lp = 0; lp < num; lp ++) { + retval = retval + (((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; + } + + /* + ** Adjust the retval to factor in the remainder + */ + if (value) { + retval = retval + (((((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); + } + + retval = Bound(retval, 0, POWER_HEIGHT-2); + return(retval); +} + + +/*********************************************************************************************** + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * * + * This routine handles input on the power bar area. Since no input is used for the power * + * bar, this routine just pops up appropriate help text for the power bar. * + * * + * INPUT: flags -- The event flags that triggered this action call. * + * * + * key -- The key code (if any) associated with the trigger event. * + * * + * OUTPUT: Should further button processing be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +{ + if (!Map.IsSidebarActive) { + return(false); + } + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + Map.Override_Mouse_Shape(MOUSE_NORMAL); + if (PlayerPtr->Power_Fraction() < 1 && PlayerPtr->Power > 0) { + Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } else { + Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } + GadgetClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * * + * This will cause the power bar to display with a flash so as to draw attention to * + * itself. Typical use of this effect is when power is low. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/14/1996 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Flash_Power(void) +{ + FlashTimer = TICKS_PER_SECOND; + IsToRedraw = true; + Flag_To_Redraw(false); +} diff --git a/REDALERT/POWER.H b/REDALERT/POWER.H new file mode 100644 index 000000000..c10442684 --- /dev/null +++ b/REDALERT/POWER.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/POWER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef POWER_H +#define POWER_H + +#include "radar.h" + +class PowerClass : public RadarClass +{ + public: + PowerClass(void); + PowerClass(NoInitClass const & x) : RadarClass(x), FlashTimer(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + + virtual void Init_Clear(void); // Clears all to known state + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Refresh_Cells(CELL cell, short const *list); + void Flash_Power(void); + + unsigned IsToRedraw:1; + + protected: + /* + ** This gadget is used to capture mouse input on the power bar. + */ + class PowerButtonClass : public GadgetClass { + public: + PowerButtonClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class PowerClass; + }; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static PowerButtonClass PowerButton; + + enum PowerEnums { + POWER_X=10*ICON_PIXEL_W, +#ifdef WIN32 + POWER_Y= (7+70+13), + POWER_HEIGHT=(200-(7+70+13)), +#else + POWER_Y= (88+9), + POWER_HEIGHT=80, +#endif + POWER_WIDTH=8, + POWER_LINE_SPACE=5, + POWER_LINE_WIDTH=3, + POWER_STEP_LEVEL=100, + POWER_STEP_FACTOR=5 + }; + + private: + int Power_Height(int value); + + unsigned IsActive:1; + + /* + ** If the power bar should be rendered with some flash effect then + ** this specifies the duration that the flash will occur. + */ + CDTimerClass FlashTimer; + + int RecordedDrain; + int RecordedPower; + int DesiredDrainHeight; + int DesiredPowerHeight; + int DrainHeight; + int PowerHeight; + int DrainBounce; + int PowerBounce; + short PowerDir; + short DrainDir; + + /* + ** Points to the shape to use for the "desired" power level indicator. + */ + static void const * PowerShape; + static void const * PowerBarShape; +}; + +#endif diff --git a/REDALERT/PROFILE.CPP b/REDALERT/PROFILE.CPP new file mode 100644 index 000000000..3c6b996d1 --- /dev/null +++ b/REDALERT/PROFILE.CPP @@ -0,0 +1,867 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PROFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWGetPrivateProfileInt -- Fetches integer value from INI. * + * WWGetPrivateProfileString -- Fetch string from INI. * + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static char * WriteBinBuffer = NULL; +static int WriteBinBufferLen = 0; +static int WriteBinBufferPos = 0; +static int WriteBinBufferMax = 0; +static char * ReadBinBuffer = NULL; +static int ReadBinBufferLen = 0; +static int ReadBinBufferPos = 0; +static int ReadBinBufferMax = 0; + + +/*************************************************************************** + * Read_Private_Config_Struct -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config) +{ + INIClass ini; + ini.Load(file); + + config->DigitCard = ini.Get_Hex("Sound", "Card", 0); + config->IRQ = ini.Get_Int("Sound", "IRQ", 0); + config->DMA = ini.Get_Int("Sound", "DMA", 0); + config->Port = ini.Get_Hex("Sound", "Port", 0); + config->BitsPerSample= ini.Get_Int("Sound", "BitsPerSample", 0); + config->Channels = ini.Get_Int("Sound", "Channels", 0); + config->Reverse = ini.Get_Int("Sound", "Reverse", 0); + config->Speed = ini.Get_Int("Sound", "Speed", 0); + ini.Get_String("Language", "Language", NULL, config->Language, sizeof(config->Language)); + + +// config->DigitCard = WWGetPrivateProfileHex("Sound", "Card", profile); +// config->IRQ = WWGetPrivateProfileInt("Sound", "IRQ", 0,profile); +// config->DMA = WWGetPrivateProfileInt("Sound", "DMA", 0,profile); +// config->Port = WWGetPrivateProfileHex("Sound", "Port", profile); +// config->BitsPerSample= WWGetPrivateProfileInt("Sound", "BitsPerSample",0,profile); +// config->Channels = WWGetPrivateProfileInt("Sound", "Channels", 0,profile); +// config->Reverse = WWGetPrivateProfileInt("Sound", "Reverse", 0,profile); +// config->Speed = WWGetPrivateProfileInt("Sound", "Speed", 0,profile); +// WWGetPrivateProfileString("Language", "Language", NULL, config->Language, 3, profile); + + return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); +} + + +/*************************************************************************** + * Get_Private_Profile_Hex -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 MML : Created. * + *=========================================================================*/ +unsigned WWGetPrivateProfileHex (char const * section, char const * entry, char * profile) +{ + char buffer[16]; // Integer staging buffer. + unsigned card; + + memset (buffer, '0', sizeof(buffer)); // MAX_ENTRY_SIZE = 15 + buffer[sizeof(buffer)-1] = '\0'; + + WWGetPrivateProfileString(section, entry, "0", buffer, sizeof(buffer), profile); + + if (strlen (buffer) > 0) { + sscanf (buffer, "%x", &card); + } else { + card = 0; + } + + return(card); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileInt -- Fetches integer value. * + * * + * INPUT: * + * section section to read from * + * * + * entry name of entry to read * + * * + * def default value, if entry isn't found * + * * + * profile buffer containing INI data * + * * + * OUTPUT: * + * integer requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +int WWGetPrivateProfileInt(char const * section, char const * entry, int def, char * profile) +{ + char buffer[16]; // Integer staging buffer. + + /* + ** Store the default in the buffer. + */ + sprintf(buffer, "%d", def); + + /* + ** Get the buffer; use itself as the default. + */ + WWGetPrivateProfileString(section, entry, buffer, buffer, sizeof(buffer)-1, profile); + + /* + ** Convert to int & return. + */ + return(atoi(buffer)); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * * + * entry name of entry to write; if NULL, the entire section is deleted * + * * + * value value to write * + * * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileInt(char const * section, char const * entry, int value, char * profile) +{ + char buffer[250]; // Working section buffer. + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Generate string to save. + */ + sprintf(buffer, "%d", value); + + /* + ** Save the string. + */ + return(WWWritePrivateProfileString(section, entry, buffer, profile)); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileString -- Fetch game override string. * + * * + * INPUT: * + * section section name to read from * + * * + * entry name of entry to read; if NULL, all entry names are returned * + * * + * def default string to use if not found; can be NULL * + * * + * retbuffer buffer to store result in * + * * + * retlen max length of return buffer * + * * + * profile INI buffer * + * * + * OUTPUT: * + * ptr to entry found in INI buf; NULL if not found * + * * + * WARNINGS: * + * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * + * to disk. This routine must take this into consideration, by searching * + * for \n when scanning backward, and for \r when scanning forward. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +char * WWGetPrivateProfileString(char const * section, char const * entry, char const * def, char * retbuffer, int retlen, char const * profile) +{ + char const * workptr; // Working pointer into profile block. + char * altworkptr; // Alternate work pointer. + char sec[50]; // Working section buffer. + char const * retval; // Start of section or entry pointer. + char * next; // Pointer to start of next section (or EOF). + char c,c2; // Working character values. + int len; // Working substring length value. + int entrylen; // Byte length of specified entry. + char * orig_retbuf; // original retbuffer ptr + +// if (!retlen) return(NULL); + + /* + ** Fill in the default value just in case the entry could not be found. + */ + if (retbuffer) { + retbuffer[0] = '\0'; + if (retlen > 1 || retlen == 0) retbuffer[1] = '\0'; + if (def) { + strncpy(retbuffer, def, retlen); + } + retbuffer[retlen-1] = '\0'; + orig_retbuf = retbuffer; + } + + /* + ** Make sure a profile string was passed in + */ + if (!profile || !section) { + return(retbuffer); + } + + /* + ** Build section string to match file image. + */ + sprintf(sec, "[%s]", section); // sec = section name including []'s + strupr(sec); + len = strlen(sec); // len = section name length, incl []'s + + /* + ** Scan for a matching section + */ + retval = profile; + workptr = profile; + for (;;) { + + /* + ** 'workptr' = start of next section + */ + workptr = strchr(workptr, '['); + + /* + ** If the end has been reached without finding the desired section + ** then abort with a failure flag. + */ + if (!workptr) { + return(NULL); + } + + /* + ** 'c' = character just before the '[' + */ + if (workptr==profile) { + c = '\n'; + } else { + c = *(workptr-1); + } + + /* + ** If this is the section name & the character before is a newline, + ** process this section + */ + if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { + + /* + ** Skip work pointer to start of first valid entry. + */ + workptr += len; + while (isspace(*workptr)) { + workptr++; + } + + /* + ** If the section name is empty, we will have stepped onto the start + ** of the next section name; inserting new entries here will leave + ** a blank line between this section's name & 1st entry. So, check + ** for 2 newlines in a row & step backward. + */ + if (workptr - profile > 4) { + if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') + workptr -= 2; + } + + /* + ** 'next = end of section or end of file. + */ + next = (char*)strchr(workptr, '['); + for (;;) { + if (next) { + + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if (*(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = (char*)workptr + strlen(workptr)-1; + break; + } + } + + /* + ** If a specific entry was specified then return with the associated + ** string. + */ + if (entry) { + retval = workptr; + entrylen = strlen(entry); + + for (;;) { + /* + ** Search for the 1st character of the entry + */ + workptr = strchr(workptr, *entry); + + /* + ** If the end of the file has been reached or we have spilled + ** into the next section, then abort + */ + if (!workptr || workptr >= next) { + return(NULL); + } + + /* + ** 'c' = character before possible entry; must be a newline + ** 'c2' = character after possible entry; must be '=' or space + */ + c = *(workptr-1); + c2 = *(workptr+entrylen); + + /* + ** Entry found; extract it + */ + if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && + (c2 == '=' || isspace(c2))) { + retval = workptr; + workptr += entrylen; // skip entry name + workptr = strchr(workptr, '='); // find '=' + + /* + ** 'altworkptr' = next newline; \r is used here since we're + ** scanning forward! + */ + if (workptr) { + altworkptr = (char*)strchr(workptr, '\r'); // find next newline + } + + /* + ** Return if there was no '=', or if the newline is before + ** the next '=' + */ + if (workptr == NULL || altworkptr < workptr) { + return((char *)retval); + } + + /* + ** Skip any white space after the '=' and before the first + ** valid character of the parameter. + */ + workptr++; // Skip the '='. + while (isspace(*workptr)) { + + /* + ** Just return if there's no entry past the '='. + */ + if (workptr >= altworkptr) + return((char*)retval); + + workptr++; // Skip the whitespace + } + + /* + ** Copy the entry into the return buffer. + */ + len = (int)(altworkptr - workptr); + if (len > retlen-1) { + len = retlen-1; + } + + if (retbuffer) { + memcpy(retbuffer, workptr, len); + *(retbuffer + len) = '\0'; // Insert trailing null. + strtrim(retbuffer); + } + return((char*)retval); + } + + /* + ** Entry was not found; go to the next one + */ + workptr++; + } + } else { + + /* + ** No entry was specified, so build a list of all entries. + ** 'workptr' is at 1st entry after section name + ** 'next' is next bracket, or end of file + */ + retval = workptr; + + if (retbuffer) { + + /* + ** Keep accumulating the identifier strings in the retbuffer. + */ + while (workptr && workptr < next) { + altworkptr = (char*)strchr(workptr, '='); // find '=' + + if (altworkptr && altworkptr < next) { + int length; // Length of ID string. + + length = (int)(altworkptr - workptr); + + /* + ** Make sure we don't write past the end of the retbuffer; + ** add '3' for the 3 NULL's at the end + */ + if (retbuffer - orig_retbuf + length + 3 < retlen) { + memcpy(retbuffer, workptr, length); // copy entry name + *(retbuffer+length) = '\0'; // NULL-terminate it + strtrim(retbuffer); // trim spaces + retbuffer += strlen(retbuffer)+1; // next pos in dest buf + } else { + break; + } + + /* + ** Advance the work pointer to the start of the next line + ** by skipping the end of line character. + */ + workptr = strchr(altworkptr, '\n'); + if (!workptr) { + break; + } + workptr++; + } else { + + /* + ** If no '=', break out of loop + */ + break; + } + } + + /* + ** Final trailing terminator. Make double sure the double + ** trailing null is added. + */ + *retbuffer++ = '\0'; + *retbuffer++ = '\0'; + } + break; + } + } else { + + /* + ** Section name not found; go to the next bracket & try again + ** Advance past '[' and keep scanning. + */ + workptr++; + } + } + + return((char*)retval); +} + + +/*********************************************************************************************** + * WritePrivateProfileString -- Write a string to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * entry name of entry to write; if NULL, the section is deleted * + * string string to write; if NULL, the entry is deleted * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * This function has to translate newlines into \r\n sequences. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileString(char const * section, char const * entry, char const * string, char * profile) +{ + char buffer[250]; // Working section buffer + char const * offset; + char const * next; // ptr to next section + char c; // Working character value + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Try to find the section. WWGetPrivateProfileString with NULL entry name + ** will return all entry names in the given buffer, truncated to the given + ** buffer length. 'offset' will point to 1st entry in the section, NULL if + ** section not found. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + + /* + ** If the section could not be found, then add it to the end. Don't add + ** anything if a removal of an entry is requested (it is obviously already + ** non-existent). Make sure two newlines precede the section name. + */ + if (!offset && entry) { + sprintf(buffer, "\r\n[%s]\r\n", section); + strcat(profile, buffer); + } + + /* + ** If the section is there and 'entry' is NULL, remove the entire section + */ + if (offset && !entry) { + + /* + ** 'next = end of section or end of file. + */ + next = strchr(offset, '['); + for (;;) { + if (next) { + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if ( *(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = offset + strlen(offset); + break; + } + } + + /* + ** Remove the section + */ + strcpy((char*)offset, (char*)next); + + return(true); + } + + /* + ** Find the matching entry within the desired section. A NULL return buffer + ** with 0 length will just return the offset of the found entry, NULL if + ** entry not found. + */ + offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); + + /* + ** Remove any existing entry + */ + if (offset) { + int eol; // Working EOL offset. + + /* + ** Get # characters up to newline; \n is used since we're after the end + ** of this line + */ + eol = strcspn(offset, "\n"); + + /* + ** Erase the entry by strcpy'ing the entire INI file over this entry + */ + if (eol) { + strcpy((char*)offset, offset + eol + 1); + } + } else { + + /* + ** Entry doesn't exist, so point 'offset' to the 1st entry position in + ** the section. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + } + + /* + ** Add the desired entry. + */ + if (entry && string) { + + /* + ** Generate entry string. + */ + sprintf(buffer, "%s=%s\r\n", entry, string); + + /* + ** Make room for new entry. + */ + memmove((char*)offset+strlen(buffer), offset, strlen(offset)+1); + + /* + ** Copy the entry into the INI buffer. + */ + memcpy((char*)offset, buffer, strlen(buffer)); + } + + return(true); +} + + +char * Read_Bin_Buffer( void ) +{ + return( ReadBinBuffer ); +} + + +bool Read_Bin_Init( char * buffer, int length ) +{ + ReadBinBuffer = buffer; + ReadBinBufferLen = length; + ReadBinBufferPos = 0; + ReadBinBufferMax = 0; + return( true ); +} + + +int Read_Bin_Length( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferMax ); + } +} + + +bool Read_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != ReadBinBuffer || length <= 0 || length > 4 || + (ReadBinBufferPos + length) >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + memcpy( num, ptr, length ); + ReadBinBufferPos += length; + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } +} + + +int Read_Bin_Pos( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferPos ); + } +} + + +int Read_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + ReadBinBufferPos = pos; + return( ReadBinBufferPos ); + } +} + + +bool Read_Bin_String( char * string, char * buffer ) +{ + char * ptr; + unsigned char length; + + if (buffer != ReadBinBuffer || + ReadBinBufferPos >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + length = (unsigned char)*ptr++; + if ( (ReadBinBufferPos + length + 2) <= ReadBinBufferLen) { + memcpy( string, ptr, (unsigned int)(length + 1) ); + ReadBinBufferPos += (length + 2); + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } else { + return( false ); + } + } +} + + +char * Write_Bin_Buffer( void ) +{ + return( WriteBinBuffer ); +} + + +bool Write_Bin_Init( char * buffer, int length ) +{ + WriteBinBuffer = buffer; + WriteBinBufferLen = length; + WriteBinBufferPos = 0; + WriteBinBufferMax = 0; + return( true ); +} + + +int Write_Bin_Length( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferMax ); + } +} + + +bool Write_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length <= 0 || length > 4 || + (WriteBinBufferPos + length) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + memcpy( ptr, num, length ); + WriteBinBufferPos += length; + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + + +int Write_Bin_Pos( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferPos ); + } +} + + +int Write_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + WriteBinBufferPos = pos; + return( WriteBinBufferPos ); + } +} + + +bool Write_Bin_String( char * string, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length < 0 || length > 255 || + (WriteBinBufferPos + length + 2) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + *ptr++ = (char)length; + memcpy( ptr, string, (length + 1) ); + WriteBinBufferPos += (length + 2); + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + diff --git a/REDALERT/QUEUE.CPP b/REDALERT/QUEUE.CPP new file mode 100644 index 000000000..1556d0174 --- /dev/null +++ b/REDALERT/QUEUE.CPP @@ -0,0 +1,4561 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/QUEUE.CPP 6 3/14/97 5:12p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/28/95 * + * * + * Last Update : October 14, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions for Queueing Events: * + * Queue_Mission -- Queue a mega mission event. * + * Queue_Options -- Queue the options event. * + * Queue_Exit -- Add the exit game event to the queue. * + * * + * Functions for processing Queued Events: * + * Queue_AI -- Process all queued events. * + * Queue_AI_Normal -- Process all queued events. * + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * Main Multiplayer Queue Logic: * + * Wait_For_Players -- Waits for other systems to come on-line * + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * Process_Send_Period -- timing for sending packets every 'n' frames * + * Send_Packets -- sends out events from the OutList * + * Send_FrameSync -- Sends a FRAMESYNC packet * + * Process_Receive_Packet -- processes an incoming packet * + * Process_Serial_Packet -- Handles an incoming serial packet * + * Can_Advance -- determines if it's OK to advance to the next frame * + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * Handle_Timeout -- attempts to reconnect; if fails, bails. * + * Stop_Game -- stops the game * + * * + * Packet Compression / Decompression: * + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * Add_Compressed_Events -- adds compressed events to a packet * + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * Extract_Uncompressed_Events -- extracts events from a packet * + * Extract_Compressed_Events -- extracts events from a packet * + * * + * DoList Management: * + * Execute_DoList -- Executes commands from the DoList * + * Clean_DoList -- Cleans out old events from the DoList * + * Queue_Record -- Records the DoList to disk * + * Queue_Playback -- plays back queue entries from a record file * + * * + * Debugging: * + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * Add_CRC -- Adds a value to a CRC * + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * Init_Queue_Mono -- inits mono display * + * Update_Queue_Mono -- updates mono display * + * Print_Framesync_Values -- displays frame-sync variables * + * Check_Mirror -- Checks mirror memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +bool bReconnectDialogCancelled; +#endif + + +/********************************** Defines *********************************/ +#define SHOW_MONO 0 + + +/********************************** Globals *********************************/ +//--------------------------------------------------------------------------- +// GameCRC is the current computed CRC value for this frame. +// CRC[] is a record of our last 32 game CRC's. +// ColorNames is for debug output in Print_CRCs +//--------------------------------------------------------------------------- +static unsigned long GameCRC; +static unsigned long CRC[32] = + {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}; +static char *ColorNames[8] = { + "Yellow", + "LtBlue", + "Red", + "Green", + "Orange", + "Grey", + "Blue", + "Brown" +}; + +//........................................................................... +// Mono debugging variables: +// NetMonoMode: 0 = show connection output, 1 = flowcount output +// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen +// IsMono: used for taking control of Mono screen away from the engine +//........................................................................... +int NetMonoMode = 1; +int NewMonoMode = 1; +static int IsMono = 0; + +//--------------------------------------------------------------------------- +// Several routines return various codes; here's an enum for all of them. +//--------------------------------------------------------------------------- +typedef enum RetcodeEnum { + RC_NORMAL, // no news is good news + RC_PLAYER_READY, // a new player has been heard from + RC_SCENARIO_MISMATCH, // scenario mismatch + RC_DOLIST_FULL, // DoList is full + RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed + RC_PLAYER_LEFT, // modem: other player left the game + RC_HUNG_UP, // modem has hung up + RC_NOT_RESPONDING, // other player not responding (timeout/hung up) + RC_CANCEL, // user cancelled +} RetcodeType; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern void Enable_Secret_Units(void); +#endif + +/********************************* Prototypes *******************************/ +//........................................................................... +// Main multiplayer queue logic +//........................................................................... +static void Queue_AI_Normal(void); +static void Queue_AI_Multiplayer(void); +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv); +static void Generate_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Process_Time_Event(ConnManClass *net); +static int Process_Send_Period(ConnManClass *net); //, int init); +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent); +static void Send_FrameSync(ConnManClass *net, int cmd_count); +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time); +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh); +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static void Stop_Game(void); + +//........................................................................... +// Packet compression/decompression: +//........................................................................... +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap); +int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +static int Breakup_Receive_Packet(void *buf, int bufsize ); +int Extract_Uncompressed_Events(void *buf, int bufsize); +int Extract_Compressed_Events(void *buf, int bufsize); + +//........................................................................... +// DoList management: +//........................................................................... +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, +// ConnManClass *net, TCountDownTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +static void Clean_DoList(ConnManClass *net); +static void Queue_Record(void); +static void Queue_Playback(void); + +//........................................................................... +// Debugging: +//........................................................................... +static void Compute_Game_CRC(void); +void Add_CRC(unsigned long *crc, unsigned long val); +static void Print_CRCs(EventClass *ev); +static void Init_Queue_Mono(ConnManClass *net); +static void Update_Queue_Mono(ConnManClass *net, int flow_index); +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent); + +extern void Keyboard_Process(KeyNumType &input); +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +void Check_Mirror(void); + + +/*************************************************************************** + * Queue_Mission -- Queue a mega mission event. * + * * + * This routine is called when the player causes a change to a game unit. * + * The event that initiates the change is queued to as a result of a call * + * to this routine. * + * * + * INPUT: * + * whom Whom this mission request applies to (a friendly unit). * + * mission The mission to assign to this object. * + * target The target of this mission (if any). * + * dest The movement destination for this mission (if any). * + * * + * OUTPUT: * + * Was the mission request queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) { + return(false); + } else { + return(true); + } +} + + +/*********************************************************************************************** + * Queue_Mission -- Queue a mega mission event, formation override for common speed. * + * * + * This routine is called when the player causes a change to a game unit. The event that * + * initiates the change is queued to as a result of a call to this routine. * + * * + * INPUT: whom -- Whom this mission request applies to (a friendly unit). * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target of this mission (if any). * + * * + * dest -- The movement destination for this mission (if any). * + * * + * speed -- The override speed for this unit. * + * * + * maxspeed -- The override maximum speed for this unit. * + * * + * OUTPUT: Was the mission request queued successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) { + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * Queue_Options -- Queue the options event. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the options screen event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Options(void) +{ + if (! OutList.Add(EventClass(EventClass::OPTIONS))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Options */ + + +/*************************************************************************** + * Queue_Exit -- Add the exit game event to the queue. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the exit event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Exit(void) +{ + if (! OutList.Add(EventClass(EventClass::EXIT))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Exit */ + + +/*************************************************************************** + * Queue_AI -- Process all queued events. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +void Queue_AI(void) +{ + if (Session.Play) { + Queue_Playback(); + } + + else { + + switch (Session.Type) { + + case GAME_SKIRMISH: + case GAME_NORMAL: + Queue_AI_Normal(); + break; + + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: + case GAME_INTERNET: + case GAME_TEN: + case GAME_MPATH: + Queue_AI_Multiplayer(); + break; + } + } + +} /* end of Queue_AI */ + + +/*************************************************************************** + * Queue_AI_Normal -- Process all queued events. * + * * + * This is the "normal" version of the queue management routine. It does * + * the following: * + * - Transfers items in the OutList to the DoList * + * - Executes any commands in the DoList that are supposed to be done on * + * this frame # * + * - Cleans out the DoList * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +static void Queue_AI_Normal(void) +{ + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + OutList.Next(); + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(1, PlayerPtr->Class->House, NULL, NULL, NULL, + NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_AI_Normal */ + + +/*************************************************************************** + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * This is the network version of the queue management routine. It does * + * the following: * + * - If this is the 1st frame, waits for other systems to signal ready * + * - Generates a timing event, to allow the connection time to be dynamic * + * - Handles timing related to sending packets every 'n' frames * + * - Sends outgoing events * + * - Frame-syncs to the other systems (see below) * + * - Executes & cleans out the DoList * + * * + * The Frame-Sync'ing logic is the heart & soul of network play. It works * + * by ensuring that any system won't out-run the other system by more than * + * 'Session.MaxAhead' frames; this in turn ensures that a packet's * + * execution frame # won't have been passed by the time that packet is * + * received by all systems. * + * * + * To achieve this, the system must keep track of all other system's * + * current frame #'s; these are stored in an array called 'their_frame[]'. * + * However, because current frame #'s are sent in FRAMEINFO packets, which * + * don't require an ACK, and command packets are sent in packets requiring * + * an ACK, it's possible for a command packet to get lost, and the next * + * frame's FRAMEINFO packet to not get lost; the other system may then * + * advance past the frame # the command is to execute on! So, to prevent * + * this, all FRAMEINFO packets include a CommandCount field. This value * + * tells the other system how many events it should have received by this * + * time. This system can therefore keep track of how many commands it's * + * actually received, and compare it to the CommandCount field, to see if * + * it's missed an event packet. The # of events we've received from each * + * system is stored in 'their_recv[]', and the # events they say they've * + * sent is stored in 'their_sent[]'. * + * * + * Thus, two conditions must be met in order to advance to the next frame: * + * - Our current frame # must be < their_frame + Session.MaxAhead * + * - their_recv[i] must be >= their_sent[i] * + * * + * 'their_frame[] is updated by Process_Receive_Packet() * + * 'their_recv[] is updated by Process_Receive_Packet() * + * 'their_sent[] is updated by Process_Receive_Packet() * + * 'my_sent' is updated by this routine. * + * * + * The order of the arrays their_frame[] etc is the same order the * + * connections are created in. The Sender's ID is passed to * + * Connection_Index() to obtain the array index. * + * * + * The only routines allowed to pop up dialogs are: * + * Wait_For_Players() (only pops up the reconnect dialog) * + * Execute_DoList() (tells if out of sync, or packet recv'd too late) * + * * + * Sign-off's are detected by: * + * - Timing out while waiting for a packet * + * - Detecting that the other player is now at the score screen or * + * connection dialog (serial) * + * - If we see an EventClass::EXIT event on the private channel * + * * + * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has * + * the following properties: * + * - It compresses packets, so that the minimum number of bytes are * + * transmitted. Packets are compressed by extracting all info common to * + * the events into the packet header, and then sending only the bytes * + * relevant to each type of event. For instance, if 100 infantry guys * + * are told to move to the same location, the command itself & the * + * location will be included in the 1st movement command only; after * + * that, there will be a rep count then 99 infantry TARGET numbers, * + * identifying all the infantry told to move. * + * - The protocol also only sends packets out every 'n' frames. This cuts * + * the data rate dramatically. It means that 'Session.MaxAhead' must be * + * divisible by 'n'; also, the minimum value for 'Session.MaxAhead' is * + * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO * + * packet gets missed. * + * * + * Note: For synchronization-waiting loops (like waiting to hear from all * + * other players, waiting to advance to the next frame, etc), use * + * Net.Num_Connections() rather than Session.NumPlayers; this reflects the * + * actual # of connections, and can be "faked" into playing even when * + * there aren't any other players actually there. A typical example of * + * this is playing back a recorded game. For command-execution loops, use * + * Session.NumPlayers. This ensures all commands get executed, even if * + * there isn't a human generating those commands. * + * * + * The modem works a little differently from the network in this respect: * + * - The connection has to stay "alive" even if the other player exits to * + * the join dialog. This prevents each system from timing out & hanging * + * the modem up. Thus, packets are sent back & forth & just thrown away,* + * but each system knows the other is still there. Messages may be sent * + * between systems, though. * + * - Destroy_Null_Connection doesn't hang up the modem, so * + * Num_Connections() still reports a value of 1 even though the other * + * player has left. * + * - Any waits on Num_Connections() must also check for * + * Session.NumPlayers > 1, to keep from waiting forever if the other * + * guy has left * + * - Packets sent to a player who's left require no ACK * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_AI_Multiplayer(void) +{ + if(Session.Type == GAME_SKIRMISH) return; + + return; +#if (0)//PG + //........................................................................ + // Enums: + //........................................................................ + enum { + MIXFILE_RESEND_DELTA = 120, // ticks b/w resends + MIXFILE_TIMEOUT = 3600*2, // timeout waiting for mixfiles. + FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog + FRAMESYNC_TIMEOUT = (15*60), // timeout waiting for frame sync packet + }; + + int timeout_factor = (Session.Type == GAME_INTERNET) ? 6 : 1; + + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + ConnManClass *net; // ptr to access all multiplayer functions + EventClass packet; // for sending single frame-sync's + char *multi_packet_buf; // buffer for sending/receiving + int multi_packet_max; // max length of multi_packet_buf + + //........................................................................ + // Frame-sync'ing variables + //........................................................................ + static long + their_frame[MAX_PLAYERS - 1]; // other players' frame #'s + static unsigned short + their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent + static unsigned short + their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others + static unsigned short + my_sent; // # cmds I've sent out + + //........................................................................ + // Timing variables + //........................................................................ + static CDTimerClass skip_crc; // to delay the CRC check +// static TCountDownTimerClass skip_crc; // to delay the CRC check + + //........................................................................ + // Other misc variables + //........................................................................ + int i; + RetcodeType rc; + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //------------------------------------------------------------------------ + // Initialize the packet buffer pointer & its max size + //------------------------------------------------------------------------ +#if (0)//PG + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + multi_packet_buf = NullModem.BuildBuf; + multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType); + net = &NullModem; + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + multi_packet_buf = Session.MetaPacket; + multi_packet_max = Session.MetaSize; + net = &Ipx; + } +#endif +#if(TEN) + else if (Session.Type == GAME_TEN) { + multi_packet_buf = Session.TenPacket; + multi_packet_max = Session.TenSize; + net = Ten; + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + multi_packet_buf = Session.MPathPacket; + multi_packet_max = Session.MPathSize; + net = MPath; + } +#endif + + //------------------------------------------------------------------------ + // Debug stuff + //------------------------------------------------------------------------ + Init_Queue_Mono(net); + Update_Queue_Mono (net, 0); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've just started a game, or loaded a multiplayer game, we must + // wait for all other systems to signal ready. + //------------------------------------------------------------------------ + if (Frame==0 || Session.LoadGame) { + //..................................................................... + // Initialize static locals + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) { + their_frame[i] = -1; + their_sent[i] = 0; + their_recv[i] = 0; + } + my_sent = 0; +#ifdef FIXIT_MULTI_SAVE + skip_crc = 32; +#else + skip_crc = Frame + 32; +#endif // FIXIT_MULTI_SAVE + for (i = 0; i < 32; i++) + CRC[i] = 0; + + //..................................................................... + // If we've loaded a saved game: + // - If this game was saved as the result of a lost connection, clear + // the CRC value so it will always match the other system's + // - Otherwise, use the GameCRC value, so we'll compare save-game files + // rather than scenario INI files + //..................................................................... + if (Session.LoadGame) { + if (Session.EmergencySave) + ScenarioCRC = 0; + else + ScenarioCRC = GameCRC; + } + + //..................................................................... + // Send our initial FRAMESYNC packet + //..................................................................... + Send_FrameSync(net, my_sent); + + //..................................................................... + // Wait for the other guys + //..................................................................... + rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor, + MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //..................................................................... + // Re-initialize frame numbers (in case somebody signed off while I was + // waiting for MIX files to load; we would have fallen through, but + // their frame # would still be -1). + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) + their_frame[i] = 0; + + //..................................................................... + // Reset the network response time computation, now that we're both + // sending data again (loading MIX files will have introduced + // deceptively large values). + //..................................................................... + net->Reset_Response_Time(); + + //..................................................................... + // Initialize the frame timers + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Process_Send_Period(net);//, 1); + } + + //..................................................................... + // Turn off our special load-game flags + //..................................................................... + if (Session.LoadGame) { + Session.EmergencySave = false; + Session.LoadGame = false; + } + + } // end of Frame 0 wait + + //------------------------------------------------------------------------ + // Adjust connection timing parameters every 128 frames. + //------------------------------------------------------------------------ + + else if ( (Frame & 0x007f) == 0) { + // + // If we're using the new spiffy protocol, do proper timing handling. + // If we're the net "master", compute our desired frame rate & new + // 'MaxAhead' value. + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // All systems will transmit their required process time. + // + Generate_Process_Time_Event(net); + + // + // The game "host" will transmit timing adjustment events. + // + if (Session.Am_I_Master()) { + Generate_Real_Timing_Event(net, my_sent); + } + } else { + // + // For the older protocols, do the old broken timing handling. + // + Generate_Timing_Event(net, my_sent); + } + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (!Process_Send_Period(net)) { //, 0)) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + } + + //------------------------------------------------------------------------ + // Send our data packet(s); update my command-sent counter + //------------------------------------------------------------------------ + my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max, + Session.MaxAhead, my_sent); + + //------------------------------------------------------------------------ + // If this is our first time through, we're done. + //------------------------------------------------------------------------ + if (Frame==0) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + + //------------------------------------------------------------------------ + // Frame-sync'ing: wait until it's OK to advance to the next frame. + //------------------------------------------------------------------------ +#ifdef FIXIT_VERSION_3 + int iFramesyncTimeout; + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 ) + // Shortened resync timeout for non-2 player games. + iFramesyncTimeout = 5 * 60; // One minute. + else + iFramesyncTimeout = FRAMESYNC_TIMEOUT; + + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + iFramesyncTimeout * (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#else + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + FRAMESYNC_TIMEOUT* (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#endif + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); +#ifdef WOLAPI_INTEGRATION + // New rule - if you cancel a waiting to reconnect dialog, you lose. + bReconnectDialogCancelled = ( rc == RC_CANCEL ); +#endif + } +#endif //WIN32 + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc, + their_frame, their_sent, their_recv)) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(net); + + if (IsMono) { + MonoClass::Disable(); + } +#endif +} // end of Queue_AI_Multiplayer + + +/*************************************************************************** + * Wait_For_Players -- Waits for other systems to come on-line * + * * + * This routine performs the most critical logic in multiplayer; that of * + * synchronizing my frame number with those of the other systems. * + * * + * INPUT: * + * first_time 1 = 1st time this routine is called * + * net ptr to connection manager * + * resend_delta time (ticks) between FRAMESYNC resends * + * dialog_time time (ticks) until pop up a reconnect dialog * + * timeout time (ticks) until we give up the ghost * + * multi_packet_buf buffer to store packets in * + * my_sent # commands I've sent so far * + * their_frame array of their frame #'s * + * their_sent array of their CommandCount values * + * their_recv array of # cmds I've received from them * + * * + * OUTPUT: * + * RC_NORMAL OK to advance to the next frame * + * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg * + * RC_NOT_RESPONDING other player(s) not responding * + * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) * + * RC_DOLIST_FULL DoList was full * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv) +{ + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + EventClass *event; // event ptr for parsing incoming packets + int packetlen; // size of meta-packet sent, & received + int id; // id of other player + int messages_this_loop; // to limit # messages processed each loop + int message_limit; // max # messages we'll read each frame + + //........................................................................ + // Variables used only if 'first_time': + //........................................................................ + int num_ready; // # players signalling ready + + //........................................................................ + // Timing variables + //........................................................................ + CDTimerClass retry_timer; // time between FRAMESYNC packet resends + CDTimerClass dialog_timer; // time to pop up a dialog + CDTimerClass timeout_timer; // general-purpose timeout + + //........................................................................ + // Dialog variables + //........................................................................ + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //........................................................................ + // Other misc variables + //........................................................................ + KeyNumType input; // for user input + int x,y; // for map input + RetcodeType rc; + + //------------------------------------------------------------------------ + // Wait to hear from all other players + //------------------------------------------------------------------------ + num_ready = 0; + retry_timer = resend_delta; // time to retry + dialog_timer = dialog_time; // time to show dlg + timeout_timer = timeout; // time to bail out + + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 2); + + //--------------------------------------------------------------------- + // Resend a frame-sync packet if longer than one propagation delay goes + // by; this prevents a "deadlock". If he's waiting for me to advance, + // but has missed my last few FRAMEINFO packets, I may be waiting for + // him to advance. Resending a FRAMESYNC ensures he knows what frame + // number I'm on. + //--------------------------------------------------------------------- + if (!retry_timer) { + retry_timer = resend_delta; // time to retry + Update_Queue_Mono (net, 3); + Send_FrameSync(net, my_sent); + } + + //--------------------------------------------------------------------- + // Service the connections + //--------------------------------------------------------------------- + net->Service(); + + //--------------------------------------------------------------------- + // Pop up a reconnect dialog if enough time goes by + //--------------------------------------------------------------------- + if (!dialog_timer && SpecialDialog==SDLG_NONE) { + if (reconnect_dlg == 0 && first_time == 0) { + FILE *fp; + int i; + HouseClass *housep; + + fp = fopen("recon.txt","wt"); + if (fp) { + fprintf(fp,"# Connections: %d\n",net->Num_Connections()); + fprintf(fp," My Frame #: %d\n",Frame); + for (i = 0; i < net->Num_Connections(); i++) { + housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i))); + fprintf(fp,"%15s: Their Sent:%d Their Recv:%d Their Frame:%d\n", + housep->IniName, their_sent[i], their_recv[i], their_frame[i]); + } + fclose(fp); + } + +#ifdef WOLAPI_INTEGRATION + // "Reconnecting" dialog is about to be shown. + // At this point, begin wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + pWolapi->Init_DisconnectPinging(); +#endif + } + + if (Process_Reconnect_Dialog(&timeout_timer, their_frame, // (Returns immediately.) + net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) { + return (RC_CANCEL); + } + reconnect_dlg = 1; + +#ifdef WOLAPI_INTEGRATION + // Continue wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging ) + pWolapi->Pump_DisconnectPinging(); +#endif + } + + //--------------------------------------------------------------------- + // Exit if too much time goes by (the other system has crashed or + // bailed) + //--------------------------------------------------------------------- + if (!timeout_timer) { + //.................................................................. + // For the first-time run, just give up; something's wrong. + //.................................................................. + if (first_time) { + return (RC_NOT_RESPONDING); + } + //.................................................................. + // Otherwise, we're in the middle of a game; so, the modem & + // network must deal with a timeout differently. + //.................................................................. + else { + Update_Queue_Mono (net, 4); + + if (Handle_Timeout(net, their_frame, their_sent, their_recv)) { + Map.Flag_To_Redraw(true); // erase modem reconnect dialog + Map.Render(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + } +#ifdef FIXIT_MULTI_SAVE +#ifdef FIXIT_VERSION_3 + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) { +#else + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) && + PlayingAgainstVersion != VERSION_RED_ALERT_104) { +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; +//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); +//Print_CRCs(NULL); +//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); +//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); + } +#endif // FIXIT_MULTI_SAVE + else { + return (RC_NOT_RESPONDING); + } + } + } + + //--------------------------------------------------------------------- + // Check for an incoming message. We must still process commands + // even if 'first_time' is set, in case the other system got my 1st + // FRAMESYNC, but I didn't get his; he'll be at the next frame, and + // may be sending commands. + // We have to limit the number of incoming messages we handle; it's + // possible to go into an infinite loop processing modem messages. + // (This feature is disabled for Ten; we need to keep the TCP buffers + // clear, so we read all the packets we can every time.) + //--------------------------------------------------------------------- + messages_this_loop = 0; + message_limit = 5; + + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + message_limit = 9999; + } + + while ( (messages_this_loop++ < message_limit) && + net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) { + + Keyboard->Check(); + + Update_Queue_Mono (net, 5); + + /*.................................................................. + Get an event ptr to the incoming message + ..................................................................*/ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------ + // Special processing for a modem game: process SERIAL packets + //------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + rc = Process_Serial_Packet(multi_packet_buf, first_time); + //............................................................... + // SERIAL packet received & processed + //............................................................... + if (rc == RC_SERIAL_PROCESSED) { + net->Service(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + continue; + } + //............................................................... + // other player has left the game + //............................................................... + else if (rc == RC_PLAYER_LEFT) { + if (first_time) { + num_ready++; + } + break; + } + //............................................................... + // Connection was lost + //............................................................... + else if (rc == RC_HUNG_UP) { +#ifdef FIXIT_MULTI_SAVE +#ifndef FIXIT_VERSION_3 + if (PlayingAgainstVersion != VERSION_RED_ALERT_104){ +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; + //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); + //Print_CRCs(NULL); + //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); +#ifndef FIXIT_VERSION_3 + }else{ + return (RC_NOT_RESPONDING); + } +#endif + +#else + return (RC_NOT_RESPONDING); +#endif // FIXIT_MULTI_SAVE + } + //............................................................... + // If it was any other type of serial packet, break + //............................................................... + else if (rc != RC_NORMAL) { + break; + } + } + + //------------------------------------------------------------------ + // Process the incoming packet + //------------------------------------------------------------------ + rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen, + their_frame, their_sent, their_recv); + //.................................................................. + // New player heard from + //.................................................................. + if (rc == RC_PLAYER_READY) { + num_ready++; + } + //.................................................................. + // Scenario's don't match + //.................................................................. + else if (rc == RC_SCENARIO_MISMATCH) { + return (RC_SCENARIO_MISMATCH); + } + //.................................................................. + // DoList was full + //.................................................................. + else if (rc == RC_DOLIST_FULL) { + return (RC_DOLIST_FULL); + } + + //.................................................................. + // Service the connection, to clean out the receive queues + //.................................................................. + net->Service(); + } + + //--------------------------------------------------------------------- + // Debug output + //--------------------------------------------------------------------- + Print_Framesync_Values(Frame, Session.MaxAhead, net->Num_Connections(), + their_recv, their_sent, my_sent); + + //--------------------------------------------------------------------- + // Attempt to advance to the next frame. + //--------------------------------------------------------------------- + //..................................................................... + // For the first-time run, just check to see if we've heard from + // everyone. + //..................................................................... + if (first_time) { + if (num_ready >= net->Num_Connections()) { + break; + } + } + //..................................................................... + // For in-game processing, we have to check their_sent, their_recv, + // their_frame, etc. + //..................................................................... + else { + if (Can_Advance(net, Session.MaxAhead, their_frame, their_sent, + their_recv)) { + break; + } + } + + //--------------------------------------------------------------------- + // Service game stuff. Servicing the map's input, and rendering the + // map, allows the map to scroll even though we're hung up waiting for + // packets. Don't do this if 'first_time' is set, since users could be + // waiting a very long time for all systems to load the scenario, and + // it gets frustrating being able to scroll around without doing + // anything. + //--------------------------------------------------------------------- + Call_Back(); + if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + Map.Input(input, x, y); + if (input) + Keyboard_Process(input); + Map.Render(); + } + + } /* end of while */ + + //------------------------------------------------------------------------ + // If the reconnect dialog was shown, force the map to redraw. + //------------------------------------------------------------------------ + if (reconnect_dlg) { + Map.Flag_To_Redraw(true); + Map.Render(); + } + + return (RC_NORMAL); + +} // end of Wait_For_Players + + +/*************************************************************************** + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * * + * This routine adjusts the connection timing on the local system; it also * + * optionally generates a RESPONSE_TIME event, to tell all systems to * + * dynamically adjust the current MaxAhead value. This allows both the * + * MaxAhead & the connection retry logic to have dynamic timing, to adjust * + * to varying line conditions. * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Generate_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + + // + // For now, TEN & MPATH don't measure the net's response time, so there's + // no point in adjusting our timing. Do nothing. + // + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + return; + } + + //------------------------------------------------------------------------ + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, divide again by 4, assuming a game rate of 15 fps. + //------------------------------------------------------------------------ + resp_time = net->Response_Time(); + + //------------------------------------------------------------------------ + // Adjust my connection retry timing; only do this if I've sent out more + // than 5 commands, so I know I have a measure of the response time. + //------------------------------------------------------------------------ + if (my_sent > 5) { + + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + //..................................................................... + // If I'm the network "master", I'm also responsible for updating the + // MaxAhead value on all systems, so do that here too. + //..................................................................... + if (Session.Am_I_Master()) { + ev.Type = EventClass::RESPONSE_TIME; + //.................................................................. + // For multi-frame compressed events, the MaxAhead must be an even + // multiple of the FrameSendRate. + //.................................................................. + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + //.................................................................. + // For sending packets every frame, just use the 1-way connection + // response time. + //.................................................................. + else { + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + NETWORK_MIN_MAX_AHEAD ); + } + else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + OutList.Add(ev); + } + } + +} // end of Generate_Timing_Event + + +/*************************************************************************** + * Generate_Real_Timing_Event -- Generates a TIMING event * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + int highest_ticks; + int i; + int specified_frame_rate; + int maxahead; + + // + // If we haven't sent out at least 5 guaranteed-delivery packets, don't + // bother trying to measure our connection response time; just return. + // + if (my_sent < 5) { + return; + } + + // + // Find the highest processing time we have stored + // + highest_ticks = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + // + // If we haven't heard from all systems yet, bail out. + // + if (Session.Players[i]->Player.ProcessTime == -1) { + return; + } + if (Session.Players[i]->Player.ProcessTime > highest_ticks) { + highest_ticks = Session.Players[i]->Player.ProcessTime; + } + } + + // + // Compute our "desired" frame rate as the lower of: + // - What the user has dialed into the options screen + // - What we're really able to run at + // + if (highest_ticks == 0) { + Session.DesiredFrameRate = 60; + } else { + Session.DesiredFrameRate = 60 / highest_ticks; + } + + if (Options.GameSpeed == 0) { + specified_frame_rate = 60; + } else { + specified_frame_rate = 60 / Options.GameSpeed; + } + + Session.DesiredFrameRate = MIN (Session.DesiredFrameRate, specified_frame_rate); + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Compute our new 'MaxAhead' value, based upon the response time of our + // connection and our desired frame rate. + // 'MaxAhead' in frames is: + // + // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec) + // + // resp_time is divided by 2 because, as reported, it represents a round- + // trip, and we only want to use a one-way trip. + // + maxahead = (resp_time * Session.DesiredFrameRate) / (2 * 60); + + // + // Now, we have to round 'maxahead' so it's an even multiple of our + // send rate. It also must be at least thrice the FrameSendRate. + // (Isn't "thrice" a cool word?) + // + maxahead = ((maxahead + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate; + maxahead = MAX (maxahead, (int)Session.FrameSendRate * 3); + + ev.Type = EventClass::TIMING; + ev.Data.Timing.DesiredFrameRate = Session.DesiredFrameRate; + ev.Data.Timing.MaxAhead = maxahead; + + OutList.Add(ev); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + +} + + +/*************************************************************************** + * Generate_Process_Time_Event -- Generates a PROCESS_TIME event * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Process_Time_Event(ConnManClass *net) +{ + EventClass ev; + int avgticks; + unsigned long resp_time; // connection response time, in ticks + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + + + if (IsMono) { + MonoClass::Enable(); + Mono_Set_Cursor(0,23); + Mono_Printf("Processing Ticks:%03d Frames:%03d\n", Session.ProcessTicks,Session.ProcessFrames); + MonoClass::Disable(); + } + + avgticks = Session.ProcessTicks / Session.ProcessFrames; + + ev.Type = EventClass::PROCESS_TIME; + ev.Data.ProcessTime.AverageTicks = avgticks; + OutList.Add(ev); + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; +} + + +/*************************************************************************** + * Process_Send_Period -- timing for sending packets every 'n' frames * + * * + * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. * + * It determines if it's time to send a packet or not. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * 1 = it's time to send a packet; 0 = don't send a packet this frame. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Send_Period(ConnManClass *net) //, int init) +{ + //------------------------------------------------------------------------ + // If the current frame # is not an even multiple of 'FrameSendRate', then + // it's not time to send a packet; just return. + //------------------------------------------------------------------------ + if (Frame != (((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate) ) { + + net->Service(); + + if (IsMono) { + MonoClass::Disable(); + } + + return (0); + } + + return (1); + + +} // end of Process_Send_Period + + +/*************************************************************************** + * Send_Packets -- sends out events from the OutList * + * * + * This routine computes how many events can be sent this frame, and then * + * builds the "meta-packet" & sends it. * + * * + * The 'cap' value is the max # of events we can send. Ideally, it should * + * be based upon the bandwidth of our connection. Currently, it's just * + * hardcoded to prevent the modem from having to resend "too much" data, * + * which is about 200 bytes per frame. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer to store packets in * + * multi_packet_max max size of multi_packet_buf * + * max_ahead current game MaxAhead value * + * my_sent # commands I've sent this game * + * * + * OUTPUT: * + * # events sent, NOT including the FRAMEINFO event * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent) +{ + int cap; // max # events to send, NOT including FRAMEINFO event + int do_once; // true: only go through packet loop once + int ack_req; // 0 = no ack required on outgoing packet + int packetlen; // size of meta-packet sent + + //------------------------------------------------------------------------ + // Determine how many events it's OK to send this frame. + //------------------------------------------------------------------------ + //........................................................................ + // If we have 4 or more packets queued for sending, don't add any more + // this frame. + //........................................................................ + if (net->Private_Num_Send() >= 4) { + cap = 0; + do_once = 1; + } + //........................................................................ + // If there are 2 or more packets queued, the entire packet we send must + // fit within a single ComQueue buffer, so limit # events to 5. + // (The Modem connection manager has a max buffer size of 200 bytes, which + // is large enough for 6 uncompressed events, which leaves room for 5 + // events plus a FRAMEINFO.) + //........................................................................ + else if (net->Private_Num_Send() >= 2) { + cap = 5; + do_once = 1; + + } + //........................................................................ + // Otherwise, just send all events in the OutList + //........................................................................ + else { + cap = OutList.Count; + do_once = 0; + } + + //........................................................................ + // Make sure we aren't sending more events than are in the OutList + //........................................................................ + if (cap > OutList.Count) { + cap = OutList.Count; + } + + //........................................................................ + // Make sure we don't send so many events that our DoList fills up + //........................................................................ + if (cap > (MAX_EVENTS * 64) - DoList.Count) { + cap = (MAX_EVENTS * 64) - DoList.Count; + } + + // + // 10/21/96 5:12PM - ST + // + if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){ + cap = OutList.Count; + do_once = 0; + } + + //------------------------------------------------------------------------ + // Build our meta-packet & transmit it. + //------------------------------------------------------------------------ + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 1); + + //..................................................................... + // If there are no commands this frame, we'll just be sending a FRAMEINFO + // packet; no ack is required. For the modem's sake, check + // Session.NumPlayers; no ACK is needed if we're just sending to someone + // who's left the game. + //..................................................................... + if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 1) { + ack_req = 0; + } + else { + ack_req = 1; + } + + //..................................................................... + // Build & send out our message + //..................................................................... + packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max, + max_ahead, my_sent, cap); + net->Send_Private_Message (multi_packet_buf, packetlen, ack_req); + + //..................................................................... + // Call Service() to actually send the packet + //..................................................................... + net->Service(); + + //..................................................................... + // Stop if there's no more data to send, or if our send queue is + // filling up. + //..................................................................... + if (OutList.Count == 0 || do_once) { + break; + } + } + + return (cap); + +} // end of Send_Packets + + +/*************************************************************************** + * Send_FrameSync -- Sends a FRAMESYNC packet * + * * + * This routine is used to periodically remind the other systems that * + * we're still here, and to tell them what frame # we're on, in case * + * they've missed my FRAMEINFO packets. * + * * + * INPUT: * + * net ptr to connection manager * + * cmd_count # commands I've sent so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Send_FrameSync(ConnManClass *net, int cmd_count) +{ + EventClass packet; + + //------------------------------------------------------------------------ + // Build a frame-sync event to send. FRAMESYNC packets contain a + // scenario-based CRC rather than a game-state-based CRC, to let the + // games compare scenario CRC's on startup. + //------------------------------------------------------------------------ + memset (&packet, 0, sizeof(EventClass)); + packet.Type = EventClass::FRAMESYNC; + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + packet.Frame = Frame + Session.MaxAhead; + } + packet.ID = PlayerPtr->ID; + packet.Data.FrameInfo.CRC = ScenarioCRC; + packet.Data.FrameInfo.CommandCount = cmd_count; + packet.Data.FrameInfo.Delay = Session.MaxAhead; + + //------------------------------------------------------------------------ + // Send the event. For modem, this just sends to the other player; + // for network, it sends to everyone we're connected to. + //------------------------------------------------------------------------ + + net->Send_Private_Message (&packet, (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)), 0 ); + + return; + +} // end of Send_FrameSync + + +/*************************************************************************** + * Process_Receive_Packet -- processes an incoming packet * + * * + * This routine receives a packet from another system, adds it to our * + * execution queue (the DoList), and updates my arrays of their frame #, * + * their commands-sent, and their commands-received. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer containing packet(s) to parse * + * id id of sender * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * RC_NORMAL: nothing unusual happened, although * + * their_sent or their_recv may have been * + * altered * + * RC_PLAYER_READY: player has been heard from for the 1st time; * + * this presumes that his original * + * 'their_frame[]' value was -1 when this * + * routine was called * + * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; * + * normally only applies after loading a new * + * scenario or save-game * + * RC_DOLIST_FULL: fatal error; unable to add events to DoList * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + EventClass *event; + int index; + RetcodeType retcode = RC_NORMAL; + int i; + + //------------------------------------------------------------------------ + // Get an event ptr to the incoming message + //------------------------------------------------------------------------ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------------ + // Get the index of the sender + //------------------------------------------------------------------------ + index = net->Connection_Index(id); + + //------------------------------------------------------------------------ + // Compute the other player's frame # (at the time this packet was sent) + //------------------------------------------------------------------------ + if (their_frame[index] < + (int)(event->Frame - event->Data.FrameInfo.Delay)) { + + //..................................................................... + // If the original frame # for this player is -1, it means we've heard + // from this player for the 1st time; return the appropriate value. + //..................................................................... + if (their_frame[index]==-1) { + retcode = RC_PLAYER_READY; + } + + their_frame[index] = event->Frame - event->Data.FrameInfo.Delay; + } + + //------------------------------------------------------------------------ + // Extract the other player's CommandCount. This count will include + // the commands in this packet, if there are any. + //------------------------------------------------------------------------ + if (event->Data.FrameInfo.CommandCount > their_sent[index]) { + + if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) { + FILE *fp; + fp = fopen("badcount.txt","wt"); + if (fp) { + fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame:%d ID:%d IsExec:%d\n", + event->Frame, + event->ID, + event->IsExecuted); + if (event->Type != EventClass::FRAMEINFO) { + fprintf(fp,"Wrong Event Type!\n"); + } else { + fprintf(fp,"CRC:%x CommandCount:%d Delay:%d\n", + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + } + } + + their_sent[index] = event->Data.FrameInfo.CommandCount; + } + + if (Debug_Print_Events) { + if (event->Type == EventClass::FRAMESYNC) { + printf("(%d) Received FRAMESYNC: ", Frame); + } else { + printf("(%d) Received FRAMEINFO: ", Frame); + } + printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n", + event->Frame, + event->ID, + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + + //------------------------------------------------------------------------ + // If this packet was not a FRAMESYNC packet: + // - Add the events in it to our DoList + // - Increment our commands-received counter by the number of non- + // FRAMEINFO packets received + //------------------------------------------------------------------------ + if (event->Type != EventClass::FRAMESYNC) { + //..................................................................... + // Break up the packet into its component events. A returned packet + // count of -1 indicates a fatal queue-full error. + //..................................................................... + i = Breakup_Receive_Packet( multi_packet_buf, packetlen); + if (i==-1) { + return (RC_DOLIST_FULL); + } + //..................................................................... + // Compute the actual # commands in the packet by subtracting off the + // FRAMEINFO event + //..................................................................... + if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) { + i--; + } + + their_recv[index] += i; + } + + //------------------------------------------------------------------------ + // If the event was a FRAMESYNC packet, there will be no commands to add, + // but we must check the ScenarioCRC value. + //------------------------------------------------------------------------ + else if (event->Data.FrameInfo.CRC != ScenarioCRC) { + return (RC_SCENARIO_MISMATCH); + } + + return (retcode); + +} // end of Process_Receive_Packet + + +/*************************************************************************** + * Process_Serial_Packet -- Handles an incoming serial packet * + * * + * This routine is needed because the modem classes don't support a * + * "global channel" like the network classes do, but that functionality is * + * still needed for modem communications. Specifically, the modem dialogs * + * transmit their own special packets back & forth, and messages are sent * + * using a special packet type. Thus, we have to call this routine when * + * we receive a modem packet, to allow it to process messages & dialog * + * packets. * + * * + * INPUT: * + * multi_packet_buf packet buffer to process * + * first_time 1 = this is the 1st game frame * + * * + * OUTPUT: * + * RC_NORMAL: this wasn't a SERIAL-type packet * + * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was * + * processed; the other player is still connected, * + * even if he's not in the game. * + * RC_PLAYER_LEFT: other player has left the game * + * RC_HUNG_UP: we're getting our own packets back; thus, the * + * modem is mirroring our packets, which means the * + * modem hung up! * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time) +{ + multi_packet_buf; + first_time; + return (RC_NORMAL); +#if (0) // ST - 5/13/2019 + SerialPacketType *serial_packet; // for parsing serial packets + int player_gone; + EventClass *event; + + //------------------------------------------------------------------------ + // Determine if this packet means that the other player has left the game + //------------------------------------------------------------------------ + serial_packet = (SerialPacketType *)multi_packet_buf; + player_gone = 0; + //........................................................................ + // On Frame 0, only a SIGN_OFF means the other player left; the other + // packet types may be left over from a previous session. + //........................................................................ + if (first_time) { + if (serial_packet->Command == SERIAL_SIGN_OFF) { + player_gone = 1; + } + } + //........................................................................ + // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means + // the other player is gone. + //........................................................................ + else { + if (serial_packet->Command == SERIAL_SIGN_OFF || + serial_packet->Command == SERIAL_TIMING || + serial_packet->Command == SERIAL_SCORE_SCREEN ) { + player_gone = 1; + } + } + if (player_gone) { + Destroy_Null_Connection(serial_packet->ScenarioInfo.Color, 0); + return (RC_PLAYER_LEFT); + } + + //------------------------------------------------------------------------ + // Process an incoming message + //------------------------------------------------------------------------ + if (serial_packet->Command == SERIAL_MESSAGE) { + if (!Session.Messages.Concat_Message(serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Appears to do nothing + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, + (PlayerColorType)serial_packet->ID, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + //..................................................................... + // Save this message in our last-message buffer + //..................................................................... + if (strlen (serial_packet->Message.Message)) { + strcpy (Session.LastMessage, serial_packet->Message.Message); + } + + //..................................................................... + // Tell the map to do a partial update (just to force the + // messages to redraw). + //..................................................................... + //Map.Flag_To_Redraw(false); + Map.Flag_To_Redraw(true); + return (RC_SERIAL_PROCESSED); + } + + //------------------------------------------------------------------------ + // Any other SERIAL-type packet means the other player is still there; + // throw them away, but let the caller know the connection is OK. + //------------------------------------------------------------------------ + if ( (serial_packet->Command >= SERIAL_CONNECT && + serial_packet->Command < SERIAL_LAST_COMMAND) || + (serial_packet->Command >= SERIAL_REQ_SCENARIO && + serial_packet->Command <= SERIAL_NO_SCENARIO) || + Session.NumPlayers == 1) { + return (RC_SERIAL_PROCESSED); + } + + //........................................................................ + // are we getting our own packets back?? + //........................................................................ + event = (EventClass *)multi_packet_buf; + + if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED); + + if (event->ID == PlayerPtr->ID) { + return (RC_HUNG_UP); + } + + return (RC_NORMAL); +#endif +} // end of Process_Serial_Packet + + +/*************************************************************************** + * Can_Advance -- determines if it's OK to advance to the next frame * + * * + * This routine uses the current values stored in their_frame[], * + * their_send[], and their_recv[] to see if it's OK to advance to the next * + * game frame. We must not advance if: * + * - If our frame # would be too far ahead of the slowest player (the * + * lowest their_frame[] value). "Too far" means * + * (Frame >= their_frame + MaxAhead). * + * - our current command count doesn't match the sent command count of one * + * other player (meaning that we've missed a command packet from that * + * player, and thus the frame # we're receiving from him may be due to a * + * FRAMEINFO packet sent later than the command, so we shouldn't use * + * this frame # to see if we should advance; we should wait until we * + * have all the commands before we advance. * + * * + * Of course, this routine assumes the values in their_frame[] etc are * + * kept current by the caller. * + * * + * INPUT: * + * net ptr to connection manager * + * max_ahead max frames ahead * + * their_frame array of their frame #'s * + * their_sent array of their sent command count * + * their_recv array of their # received commands * + * * + * OUTPUT: * + * 1 = OK to advance; 0 = not OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + long their_oldest_frame; // other players' oldest frame # + int count_ok; // true = my cmd count matches theirs + int i; + + //------------------------------------------------------------------------ + // Special case for modem: if the other player has left, go ahead and + // advance to the next frame; don't wait on him. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + return (1); + } + + //------------------------------------------------------------------------ + // Find the oldest frame # in 'their_frame' + //------------------------------------------------------------------------ + their_oldest_frame = Frame + 1000; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < their_oldest_frame) + their_oldest_frame = their_frame[i]; + } + + //------------------------------------------------------------------------ + // I can advance to the next frame IF: + // 1) I'm less than a one-way propagation delay ahead of the other + // players' frame numbers, AND + // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands + // the other players have sent so far). + //------------------------------------------------------------------------ + count_ok = 1; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_recv[i] < their_sent[i]) { + count_ok = 0; + break; + } + } + if (count_ok && (Frame < (their_oldest_frame + max_ahead))) { + return (1); + } + + return (0); + +} // end of Can_Advance + + +/*************************************************************************** + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * * + * This routine [re]draws the reconnection dialog; if 'reconn' is set, * + * it tells the user who we're trying to reconnect to; otherwise, is just * + * says something generic like "Waiting for connections". * + * * + * INPUT: * + * timeout_timer ptr to count down timer, showing time remaining * + * their_frame array of other players' frame #'s * + * num_conn # connections in 'their_frame' * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * * + * OUTPUT: * + * 1 = user wants to cancel, 0 = not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh) +{ + static int displayed_time = 0; // time value currently displayed + int new_time; + int oldest_index; // index of person requiring a reconnect + int i,j; + + //------------------------------------------------------------------------ + // Convert the timer to seconds + //------------------------------------------------------------------------ + new_time = *timeout_timer / 60; + + //------------------------------------------------------------------------ + // If the timer has changed, or 'fresh' is set, redraw the dialog + //------------------------------------------------------------------------ + if (fresh || (new_time != displayed_time)) { + //..................................................................... + // Find the index of the person we're trying to reconnect to + //..................................................................... + if (reconn) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < num_conn; i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + } + Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time); + } + displayed_time = new_time; + + //........................................................................ + // If user hits ESC, bail out + //........................................................................ + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + return (1); + } + } + + return (0); + +} // end of Process_Reconnect_Dialog + + +/*************************************************************************** + * Handle_Timeout -- handles a timeout in the wait-for-players loop * + * * + * This routine "gracefully" handles a timeout in the frame-sync loop. * + * The timeout must be handled differently by a modem game or network * + * game. * + * * + * The modem game must detect if the other player is still connected * + * physically, even if he's not playing the game any more; if so, this * + * routine returns an OK status. If the other player isn't even * + * physically connected, an error is returned. * + * * + * The network game must find the connection that's causing the timeout, * + * and destroy it. The game continues, even if there are no more human * + * players left. * + * * + * INPUT: * + * net ptr to connection manager * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * 1 = it's OK; reset timeout timers & keep processing * + * 0 = game over, man * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + int oldest_index; // index of person requiring a reconnect + int i,j; + int id; + + //------------------------------------------------------------------------ + // For modem, attempt to reconnect; if that fails, save the game & bail. + //------------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + if ( net->Num_Connections() ) { + if (!Reconnect_Modem()) { +#ifndef FIXIT_MULTI_SAVE + //............................................................... + // Set 'Session.EmergencySave', so when this game is loaded, we + // won't check the CRC of the game state (this system & the + // other may be on different frames, in which case the CRC + // won't match). + //............................................................... + Session.EmergencySave = 1; + //Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + Session.EmergencySave = 0; +#endif // FIXIT_MULTI_SAVE + return (0); + } else { + return (1); + } + } + } + + //------------------------------------------------------------------------ + // For network, destroy the oldest connection + //------------------------------------------------------------------------ + else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + + id = net->Connection_ID(oldest_index); +#ifdef WIN32 + /* + ** Send the game statistics packet now if the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + ConnectionLost = true; + Send_Statistics_Packet(); // Disconnect, and I'll be the only one left. + } +#endif //WIN32 + + if (id != ConnManClass::CONNECTION_NONE) { + for (i = oldest_index; i < net->Num_Connections() - 1; i++) { + their_frame[i] = their_frame[i+1]; + their_sent[i] = their_sent[i+1]; + their_recv[i] = their_recv[i+1]; + } + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + Destroy_Connection(id,1); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(id,1); + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(id,1); + } +#endif + } + } + + return (1); + +} // end of Handle_Timeout + + +/*************************************************************************** + * Stop_Game -- stops the game * + * * + * This routine clears any global flags that need it, in preparation for * + * halting the game prematurely. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1995 BRR : Created. * + *=========================================================================*/ +static void Stop_Game(void) +{ + Session.LoadGame = false; + Session.EmergencySave = false; + GameActive = 0; + if (IsMono) { + MonoClass::Disable(); + } +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + ConnectionLost = true; + Send_Statistics_Packet(); // Stop_Game() + } +#endif //WIN32 + + return; + +} // end of Stop_Game + + +/*************************************************************************** + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * * + * This routine takes events from the OutList, and puts them into a * + * "meta-packet", which is transmitted to all systems we're connected to. * + * Also, these events are added to our own DoList. * + * * + * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this * + * tells the other systems what frame we're on, as well as serving as a * + * standard packet header. * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * num_cmds value to use for the CommandCount field * + * cap max # events to send * + * * + * OUTPUT: * + * new size of packet * + * * + * WARNINGS: * + * 'num_cmds' should be the total of of commands, including all those sent * + * this frame! * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap) +{ + int size = 0; + EventClass *finfo; + + //------------------------------------------------------------------------ + // All events start with a FRAMEINFO event; fill this part in. + //------------------------------------------------------------------------ + //........................................................................ + // Set the event type + //........................................................................ + finfo = (EventClass *)buf; + finfo->Type = EventClass::FRAMEINFO; + //........................................................................ + // Set the frame to execute this event on; this is protocol-specific + //........................................................................ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + finfo->Frame = Frame + frame_delay; + } + //........................................................................ + // Fill in the rest of the event + //........................................................................ + finfo->ID = PlayerPtr->ID; + finfo->Data.FrameInfo.CRC = GameCRC; + finfo->Data.FrameInfo.CommandCount = num_cmds; + finfo->Data.FrameInfo.Delay = frame_delay; + + //------------------------------------------------------------------------ + // Initialize the # of bytes processed; this is protocol-specific + //------------------------------------------------------------------------ + if (Session.CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) { + size += sizeof(EventClass); + } + else { + size += (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)); + } + + //------------------------------------------------------------------------ + // Transfer all events from the OutList into the DoList, building our + // packet while we go. + //------------------------------------------------------------------------ + switch (Session.CommProtocol) { + //..................................................................... + // COMM_PROTOCOL_SINGLE_NO_COMP: + // We'll send at least a FRAMEINFO every single frame, no compression + //..................................................................... + case (COMM_PROTOCOL_SINGLE_NO_COMP): + size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // COMM_PROTOCOL_SINGLE_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every frame. + // COMM_PROTOCOL_MULTI_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every 'n' frames. + //..................................................................... + case (COMM_PROTOCOL_SINGLE_E_COMP): + case (COMM_PROTOCOL_MULTI_E_COMP): + size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // Default: We have no idea what to do, so do nothing. + //..................................................................... + default: + size = 0; + break; + } + + return( size ); + +} /* end of Build_Send_Packet */ + + +/*************************************************************************** + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + int ev_size; // size of event we're adding + + //------------------------------------------------------------------------ + // Loop until there are no more events, or we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + if (OutList.First().Type==EventClass::ADDPLAYER) { + ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size; + } + else { + ev_size = sizeof(EventClass); + } + //..................................................................... + // Will the next event exceed the size of the buffer? If so, break. + //..................................................................... + if ( (size + ev_size) > bufsize ) { + return (size); + } + + //..................................................................... + // Set the event's frame delay + //..................................................................... + OutList.First().Frame = Frame + frame_delay; + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList + // event. If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if (!DoList.Add(OutList.First())) { + return (size); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //..................................................................... + // Add event to the send packet + //..................................................................... + if (OutList.First().Type==EventClass::ADDPLAYER) { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + } + else { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + } + + //..................................................................... + // Increment our event counter; delete the last event from the queue + //..................................................................... + num++; + OutList.Next(); + } + + return (size); + +} // end of Add_Uncompressed_Events + + +/*************************************************************************** + * Add_Compressed_Events -- adds an compressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size reference to current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + EventClass::EventType eventtype; // type of event being compressed + EventClass prevevent; // last event processed + int datasize; // size of element plucked from event union + int storedsize; // actual # bytes stored from event + unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count + unsigned char numunits = 0; // megamission rep count value + bool missiondup = false; // flag: is this event a megamission repeat? + + //------------------------------------------------------------------------ + // clear previous event + //------------------------------------------------------------------------ + memset (&prevevent, 0, sizeof(EventClass)); + + if (Debug_Print_Events) { + printf("\n(%d) Building Send Packet\n", Frame); + } + + //------------------------------------------------------------------------ + // Loop until there are no more events, we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + eventtype = OutList.First().Type; + datasize = EventClass::EventLength[ eventtype ]; + //..................................................................... + // For a variable-sized event, pull the size from the event; otherwise, + // the size will be the data element size plus the event type value. + // (The other data elements in the event, Frame, ID, etc, are stored + // in the packet header.) + //..................................................................... + if (eventtype==EventClass::ADDPLAYER) { + storedsize = datasize + sizeof (EventClass::EventType) + + OutList.First().Data.Variable.Size; + } + else { + storedsize = datasize + sizeof (EventClass::EventType); + } + + //..................................................................... + // MegaMission compression: MegaMissions are stored as: + // EventType + // Rep Count + // MegaMission structure (event # 1 only) + // Whom #2 + // Whom #3 + // Whom #4 + // ... + // Whom #n + //..................................................................... + if (prevevent.Type == EventClass::MEGAMISSION) { + //.................................................................. + // If previous & current events are both MegaMissions: + //.................................................................. + if (eventtype == EventClass::MEGAMISSION) { + //............................................................... + // If the Mission, Target, & Destination are the same, compress + // the events into one: + // - Change datasize to the size of the 'Whom' field only + // - set total # bytes to store to the size of the 'Whom' only + // - increment the MegaMission rep count + // - set the MegaMission rep flag + //............................................................... + if (OutList.First().Data.MegaMission.Mission == + prevevent.Data.MegaMission.Mission && + OutList.First().Data.MegaMission.Target == + prevevent.Data.MegaMission.Target && + OutList.First().Data.MegaMission.Destination == + prevevent.Data.MegaMission.Destination) { +#if (0)//PG + if (Debug_Print_Events) { + printf(" adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n", + OutList.First().Data.MegaMission.Whom.As_TARGET(), + OutList.First().Data.MegaMission.Whom, + MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission), + OutList.First().Data.MegaMission.Target.As_TARGET(), + OutList.First().Data.MegaMission.Target, + OutList.First().Data.MegaMission.Destination.As_TARGET(), + OutList.First().Data.MegaMission.Destination); + } +#endif + datasize = sizeof(prevevent.Data.MegaMission.Whom); + storedsize = datasize; + numunits++; + missiondup = true; + } + //............................................................... + // Data doesn't match; start a new run of MegaMissions: + // - Store previous MegaMission rep count + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //............................................................... + else { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + *unitsptr = numunits; + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + } + //.................................................................. + // Previous event was a MegaMission, but this one isn't: end the + // run of MegaMissions: + // - Store previous MegaMission rep count + // - Clear variables + //.................................................................. + else { + *unitsptr = numunits; // save # events in our run + unitsptr = NULL; // init other values + numunits = 0; + missiondup = false; + } + } + + //..................................................................... + // The previous event is not a MEGAMISSION but the current event is: + // Set up a new run of MegaMissions: + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //..................................................................... + else if (eventtype == EventClass::MEGAMISSION) { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + + //..................................................................... + // Will the next event exceed the size of the buffer? If so, + // stop compressing. + //..................................................................... + if ( (size + storedsize) > bufsize ) + break; + + //..................................................................... + // Set the event's frame delay (this is protocol-dependent) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + OutList.First().Frame = ((Frame + frame_delay + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate; + } + else { + OutList.First().Frame = Frame + frame_delay; + } + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList event. + // If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if ( !DoList.Add( OutList.First() ) ) { + break; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //--------------------------------------------------------------------- + // Compress the event into the send packet buffer + //--------------------------------------------------------------------- + switch ( eventtype ) { + //.................................................................. + // RESPONSE_TIME: just use the Delay field of the FrameInfo union + //.................................................................. + case (EventClass::RESPONSE_TIME): + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.FrameInfo.Delay, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + break; + + //.................................................................. + // MEGAMISSION: + //.................................................................. + case (EventClass::MEGAMISSION): + //............................................................... + // Repeated mission in a run: + // - Update the rep count (in case we break out) + // - Copy the Whom field only + //............................................................... + if (missiondup) { + *unitsptr = numunits; + + memcpy ( ((char *)buf) + size, + &OutList.First().Data.MegaMission.Whom, datasize ); + + size += datasize; + } + //............................................................... + // 1st mission in a run: + // - Init the rep count (in case we break out) + // - Set the EventType + // - Copy the MegaMission structure, leaving room for 'numunits' + //............................................................... + else { + *unitsptr = numunits; + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + + sizeof(EventClass::EventType) + sizeof(numunits), + &OutList.First().Data.MegaMission, datasize ); + + size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits)); + } + break; + + //.................................................................. + // Variable-sized packets: Copy the packet Size & the buffer + //.................................................................. + case (EventClass::ADDPLAYER): + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.Variable.Size, datasize ); + size += (datasize + sizeof(EventClass::EventType)); + + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + + break; + + //.................................................................. + // Default case: Just copy over the data field from the union + //.................................................................. + default: + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + + break; + } + + //--------------------------------------------------------------------- + // update # events processed + //--------------------------------------------------------------------- + num++; + + //--------------------------------------------------------------------- + // Update 'prevevent' + //--------------------------------------------------------------------- + memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) ); + + //--------------------------------------------------------------------- + // Go to the next event to process + //--------------------------------------------------------------------- + OutList.Next(); + } + + if (Debug_Print_Events) { + printf("\n"); + } + + return (size); + +} // end of Add_Compressed_Events + + +/*************************************************************************** + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * * + * INPUT: * + * buf buffer to break up * + * bufsize length of buffer * + * * + * OUTPUT: * + * # events added to queue, -1 if fatal error (queue is full) * + * (return value includes any FRAMEINFO packets encountered; * + * FRAMESYNC's are ignored) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Breakup_Receive_Packet(void *buf, int bufsize ) +{ + int count = 0; + + /* + ** is there enough leftover for another record + */ + switch (Session.CommProtocol) { + case (COMM_PROTOCOL_SINGLE_NO_COMP): + count = Extract_Uncompressed_Events(buf, bufsize); + break; + + default: + count = Extract_Compressed_Events(buf, bufsize); + break; + } + + return (count); + +} /* end of Breakup_Receive_Packet */ + + +/*************************************************************************** + * Extract_Uncompressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Uncompressed_Events(void *buf, int bufsize) +{ + int count = 0; + int pos = 0; + int leftover = bufsize; + EventClass *event; + + //------------------------------------------------------------------------ + // Loop until there are no more events in the packet + //------------------------------------------------------------------------ + while (leftover >= sizeof(EventClass) ) { + + Keyboard->Check(); + + event = (EventClass *)(((char *)buf) + pos); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + event->IsExecuted = 0; + + //.................................................................. + // Special processing for variable-sized events + //.................................................................. + if (event->Type == EventClass::ADDPLAYER) { + event->Data.Variable.Pointer = new char[event->Data.Variable.Size]; + memcpy (event->Data.Variable.Pointer, + ((char *)buf) + sizeof(EventClass), + event->Data.Variable.Size); + + pos += event->Data.Variable.Size; + leftover -= event->Data.Variable.Size; + } + + if (!DoList.Add( *event )) { + if (event->Type == EventClass::ADDPLAYER) { + delete [] event->Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(*event); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + } + + //..................................................................... + // Point to the next position in the buffer; decrement our 'leftover' + //..................................................................... + pos += sizeof(EventClass); + leftover -= sizeof(EventClass); + } + + return (count); + +} // end of Extract_Uncompressed_Events + + +/*************************************************************************** + * Extract_Compressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Compressed_Events(void *buf, int bufsize) +{ + int pos = 0; // current buffer parsing position + int leftover = bufsize; // # bytes left to process + EventClass *event; // event ptr for parsing buffer + int count = 0; // # events processed + int datasize = 0; // size of data to copy + EventClass eventdata; // stores Frame, ID, etc + unsigned char numunits = 0; // # units stored in compressed MegaMissions + + //------------------------------------------------------------------------ + // Clear work event structure + //------------------------------------------------------------------------ + memset (&eventdata, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Assume the first event is a FRAMEINFO event + // Init 'datasize' to the amount of data to copy, minus the EventType value + // For the 1st packet only, this will include all info before the Data + // union, plus the size of the FrameInfo structure, minus the EventType size. + //------------------------------------------------------------------------ + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType); + event = (EventClass *)(((char *)buf) + pos); + + while (leftover >= (datasize + (int)sizeof(EventClass::EventType)) ) { + + Keyboard->Check(); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + //.................................................................. + // initialize the common data from the FRAMEINFO event + // keeping IsExecuted 0 + //.................................................................. + if (event->Type == EventClass::FRAMEINFO) { + eventdata.Frame = event->Frame; + eventdata.ID = event->ID; + + //............................................................... + // Adjust position past the common data + //............................................................... + pos += (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + leftover -= (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + } + //.................................................................. + // if MEGAMISSION event get the number of units (events to generate) + //.................................................................. + else if (event->Type == EventClass::MEGAMISSION) { + numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type)); + pos += sizeof(numunits); + leftover -= sizeof(numunits); + } + + //.................................................................. + // clear the union data portion of the event + //.................................................................. + memset (&eventdata.Data, 0, sizeof(eventdata.Data)); + eventdata.Type = event->Type; + datasize = EventClass::EventLength[ eventdata.Type ]; + + switch (eventdata.Type) { + case (EventClass::RESPONSE_TIME): + memcpy ( &eventdata.Data.FrameInfo.Delay, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + + case (EventClass::ADDPLAYER): + + memcpy ( &eventdata.Data.Variable.Size, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + eventdata.Data.Variable.Pointer = + new char[eventdata.Data.Variable.Size]; + memcpy (eventdata.Data.Variable.Pointer, + ((char *)buf) + pos + sizeof(EventClass::EventType) + datasize, + eventdata.Data.Variable.Size); + + pos += eventdata.Data.Variable.Size; + leftover -= eventdata.Data.Variable.Size; + + break; + + case (EventClass::MEGAMISSION): + memcpy ( &eventdata.Data.MegaMission, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + if (numunits > 1) { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + datasize = sizeof(eventdata.Data.MegaMission.Whom); + + while (numunits) { + + Keyboard->Check(); + + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //...................................................... + // Keep count of how many events we add to the queue + //...................................................... + count++; + numunits--; + memcpy ( &eventdata.Data.MegaMission.Whom, + ((char *)buf) + pos, datasize ); + + //...................................................... + // if one unit left fall thru to normal code + //...................................................... + if (numunits == 1) { + datasize -= sizeof(EventClass::EventType); + break; + } + else { + pos += datasize; + leftover -= datasize; + } + } + } + break; + + default: + memcpy ( &eventdata.Data, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + } + + if ( !DoList.Add( eventdata ) ) { + if (eventdata.Type == EventClass::ADDPLAYER) { + delete [] eventdata.Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + + if (leftover) { + event = (EventClass *)(((char *)buf) + pos); + datasize = EventClass::EventLength[ event->Type ]; + if (event->Type == EventClass::MEGAMISSION) { + datasize += sizeof(numunits); + } + } + } + //..................................................................... + // FRAMESYNC event: This >should< be the only event in the buffer, + // and it will be uncompressed. + //..................................................................... + else { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + event = (EventClass *)(((char *)buf) + pos); + + //.................................................................. + // size of FRAMESYNC event - EventType size + //.................................................................. + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - + sizeof(EventClass::EventType); + } + } + + return (count); + +} // end of Extract_Compressed_Events + + +/*************************************************************************** + * Execute_DoList -- Executes commands from the DoList * + * * + * This routine executes any events in the DoList that need to be executed * + * on the current game frame. The events must be executed in a special * + * order, so that all systems execute all events in exactly the same * + * order. * + * * + * This routine also handles checking the Game CRC sent by other systems * + * against my own, to be sure we're still in sync. * + * * + * INPUT: * + * max_houses # houses to execute commands for * + * base_house HousesType to start with * + * net ptr to connection manager; NULL if none * + * skip_crc a frame-based countdown timer; if it's non-zero, the * + * CRC check will be skipped. Ignored if NULL. * + * their_frame array of their frame #'s * + * their_sent array of # commands they've sent * + * their_recv array of # commands I've received from them * + * * + * (their_xxx are ignored if 'net' is NULL.) * + * * + * OUTPUT: * + * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + HousesType house; + HouseClass *hptr; + int i,j,k; + int index; + int check_crc; + + Check_Mirror(); + +#if(TIMING_FIX) + // + // If MPlayerMaxAhead is recomputed such that it increases, the systems + // may try to free-run to the new MaxAhead value. If so, they may miss + // an event that was generated after the TIMING event was created, but + // before it executed; this event will be scheduled with the older, + // shorter MaxAhead value. If a system doesn't receive this event, it + // may execute past the frame it's scheduled to execute on, creating + // a Packet-Recieved-Too-Late error. To prevent this, find any events + // that are scheduled to execute during this "period of vulnerability", + // and re-schedule for the end of that period. + // + for (j = 0; j < DoList.Count; j++) { + if (DoList[j].Type != EventClass::FRAMEINFO && + DoList[j].Frame > (unsigned)NewMaxAheadFrame1 && + DoList[j].Frame < (unsigned)NewMaxAheadFrame2) { + DoList[j].Frame = (unsigned)NewMaxAheadFrame2; + #ifdef MIRROR_QUEUE + MirrorList[j].Frame = NewMaxAheadFrame2; + #endif + } + } +#endif + + //------------------------------------------------------------------------ + // Execute the DoList. Events must be executed in the same order on all + // systems; so, execute them in the order of the HouseClass array. This + // array is stored in the same order on all systems. + //------------------------------------------------------------------------ + for (i = 0; i < max_houses; i++) { + //..................................................................... + // Convert our index into a HousesType value + //..................................................................... + house = (HousesType)(i + base_house); + hptr = HouseClass::As_Pointer(house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!hptr) { + continue; + } + if (!hptr->IsHuman) { + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (j = 0; j < DoList.Count; j++) { + + if (net) + Update_Queue_Mono (net, 6); + + //.................................................................. + // If this event was from the currently-executing player ID, and it's + // time to execute it, execute it. + //.................................................................. + if (DoList[j].ID == hptr->ID && (unsigned) Frame >= DoList[j].Frame && + !DoList[j].IsExecuted) { + + //............................................................... + // Error if it's too late to execute this packet! + // (Hack: disable this check for solo or skirmish mode.) + //............................................................... + if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type != + EventClass::FRAMEINFO && Session.Type != GAME_NORMAL && + Session.Type != GAME_SKIRMISH) { + +#if(TEN) + Send_TEN_Packet_Too_Late(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Packet_Too_Late(); +#endif // MPATH + + Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame, + their_sent, their_recv); + WWMessageBox().Process (TXT_PACKET_TOO_LATE); + return (0); + } + + //............................................................... + // Only execute EXIT & OPTIONS commands if they're from myself. + //............................................................... + if (DoList[j].Type==EventClass::EXIT || + DoList[j].Type==EventClass::OPTIONS) { + +#ifdef WIN32 + if (DoList[j].Type==EventClass::EXIT) { + /* + ** Flag that this house lost because it quit. + */ + HousesType quithouse; + HouseClass *quithptr; + + for (int player = 0; player < max_houses ; player++) { + quithouse = (HousesType)(player + base_house); + quithptr = HouseClass::As_Pointer(quithouse); + if (!quithptr) { + continue; + } + if (quithptr->ID == DoList[j].ID) { + quithptr->IsGiverUpper = true; + break; + } + } + + /* + ** Send the game statistics packet now since the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Event - player aborted, and there were only 2 left. + } + } +#endif //WIN32 + + if (Debug_Print_Events) { + if (DoList[j].Type==EventClass::EXIT) { + printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n", + Frame, + DoList[j].ID, + (HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName, + DoList[j].Frame); + } + } + + if (DoList[j].ID == PlayerPtr->ID) { + DoList[j].Execute(); + } else if (DoList[j].Type==EventClass::EXIT) { + //............................................................ + // If this EXIT event isn't from myself, destroy the connection + // for that player. The HousesType for this event is the + // connection ID. + //............................................................ + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + //PG Destroy_Null_Connection( house, 0 ); + } else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || + Session.Type == GAME_MPATH) && net) { + index = net->Connection_Index (house); + if (index != -1) { + for (k = index; k < net->Num_Connections() - 1; k++) { + their_frame[k] = their_frame[k+1]; + their_sent[k] = their_sent[k+1]; + their_recv[k] = their_recv[k+1]; + } + if (Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) { + Destroy_Connection(house,0); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(house,0); + } +#endif // TEN +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(house,0); + } +#endif // MPATH + } + } + // + // Special case for recording playback: turn the house over + // to the computer. + // + if (Session.Play && DoList[j].Type==EventClass::EXIT) { + hptr->IsHuman = false; + hptr->IQ = Rule.MaxIQ; + hptr->Computer_Paranoid(); + strcpy (hptr->IniName,Text_String(TXT_COMPUTER)); + Session.NumPlayers--; + } + } + } + + //............................................................... + // For a FRAMEINFO event, check the CRC value. + //............................................................... + else if (DoList[j].Type == EventClass::FRAMEINFO) { + //............................................................ + // Skip the CRC check if we're less than 32 frames into the game; + // this will prevent a newly-loaded modem game from instantly + // going out of sync, if the games were saved at different + // frame numbers. + //............................................................ + if (!skip_crc || *skip_crc == 0) { + check_crc = 1; + } + else { + check_crc = 0; + } + if (check_crc + && DoList[j].Frame == Frame + && DoList[j].Data.FrameInfo.Delay < 32) { + index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) & + 0x001f); + if (CRC[index] != DoList[j].Data.FrameInfo.CRC) { + Print_CRCs(&DoList[j]); + +#if(TEN) + Send_TEN_Out_Of_Sync(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Out_Of_Sync(); +#endif // MPATH +#if (0)//PG + if (WWMessageBox().Process (TXT_OUT_OF_SYNC, + TXT_CONTINUE, TXT_STOP) == 0) { + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + //PG Destroy_Null_Connection( house, -1 ); + Shutdown_Modem(); + Session.Type = GAME_NORMAL; + } + else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) && net) { + while (net->Num_Connections()) { + Keyboard->Check(); + Destroy_Connection (net->Connection_ID(0), -1); + } + } +#if(TEN) + else if (Session.Type == GAME_TEN && net) { + while (net->Num_Connections()) { + Destroy_TEN_Connection (net->Connection_ID(0), -1); + } + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH && net) { + while (net->Num_Connections()) { + Destroy_MPATH_Connection (net->Connection_ID(0), -1); + } + } +#endif + Map.Flag_To_Redraw(true); + } + else { + return (0); + } +#endif + return (1); + } + } + } + //............................................................... + // Execute other commands + //............................................................... + else { + DoList[j].Execute(); + } + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + #ifdef MIRROR_QUEUE + MirrorList[j].IsExecuted = 1; + #endif + } + } + } + + return (1); + +} // end of Execute_DoList + + +/*************************************************************************** + * Clean_DoList -- Cleans out old events from the DoList * + * * + * Currently, an event can only be removed from the DoList if it's at the * + * head of the list; and event can't be removed from the middle. So, * + * this routine loops as long as the next event in the DoList has been * + * executed, it's removed. * + * * + * INPUT: * + * net ptr to connection manager; ignored if NULL * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Clean_DoList(ConnManClass *net) +{ + while (DoList.Count) { + + Keyboard->Check(); + + if (net) + Update_Queue_Mono (net, 7); + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + #ifdef MIRROR_QUEUE + MirrorList.Next(); + #endif + } + else { + break; + } + } + +} // end of Clean_DoList + + +/*************************************************************************** + * Queue_Record -- Records the DoList to disk * + * * + * This routine just saves any events in the DoList to disk; we can later * + * "play back" the recording just be pulling events from disk rather than * + * from the network! * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/14/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Record(void) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # of events to save this frame + //------------------------------------------------------------------------ + j = 0; + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + j++; + } + } + + //------------------------------------------------------------------------ + // Save the # of events, then all events. + //------------------------------------------------------------------------ + Session.RecordFile.Write (&j,sizeof(j)); + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + Session.RecordFile.Write (&DoList[i],sizeof (EventClass)); + j--; + } + } + +} /* end of Queue_Record */ + + +/*************************************************************************** + * Queue_Playback -- plays back queue entries from a record file * + * * + * This routine reads events from disk, putting them into the DoList; * + * it then executes the DoList just like the network version does. The * + * result is that the game "plays back" like a recording. * + * * + * This routine detects mouse motion and stops playback, so it can work * + * like an "attract" mode, showing a demo of the game itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Playback(void) +{ + int numevents; + EventClass event; + int i; + int ok; + static int mx,my; + int max_houses; + HousesType base_house; + int key; + int testframe; + + //------------------------------------------------------------------------ + // If the user hits ESC, stop the playback + //------------------------------------------------------------------------ + if (Keyboard->Check()) { + key = Keyboard->Get(); + if (key == KA_ESC || Session.Attract) { + GameActive = 0; + return; + } + } + + //------------------------------------------------------------------------ + // If we're in "Attract" mode, and the user moves the mouse, stop the + // playback. + //------------------------------------------------------------------------ + if (Session.Attract && Frame > 0 && + (mx != Get_Mouse_X() || my != Get_Mouse_Y())) { + GameActive = 0; + return; + } + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've reached the CRC print frame, do so & exit + //------------------------------------------------------------------------ + if (Frame >= Session.TrapPrintCRC) { + Print_CRCs(NULL); + Prog_End("Queue_Playback reached CRC print frame", true); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Don't read anything the first time through (since the Queue_AI_Network + // routine didn't write anything the first time through); do this after the + // CRC is computed, since we'll still need a CRC for Frame 0. + //------------------------------------------------------------------------ + if (Frame==0 && Session.Type!=GAME_NORMAL) { + return; + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + testframe = ((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (Frame != testframe) { + return; + } + } + + //------------------------------------------------------------------------ + // Read the DoList from disk + //------------------------------------------------------------------------ + ok = 1; + if (Session.RecordFile.Read (&numevents, sizeof(numevents)) == + sizeof(numevents)) { + for (i = 0; i < numevents; i++) { + if (Session.RecordFile.Read (&event, sizeof(EventClass)) == + sizeof(EventClass)) { + event.IsExecuted = 0; + DoList.Add (event); + #ifdef MIRROR_QUEUE + MirrorList.Add(event); + #endif + } + else { + ok = 0; + break; + } + } + } + else { + ok = 0; + } + + if (!ok) { + GameActive = 0; + return; + } + + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (Session.Type == GAME_NORMAL) { + max_houses = 1; + base_house = PlayerPtr->Class->House; + } + else { + max_houses = Session.MaxPlayers; + base_house = HOUSE_MULTI1; + } + if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_Playback */ + + +/*************************************************************************** + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Compute_Game_CRC(void) +{ + int i,j; + VesselClass *vessp; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + ObjectClass *objp; + HouseClass *housep; + + GameCRC = 0; + + //------------------------------------------------------------------------ + // Infantry + //------------------------------------------------------------------------ + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + } + + //------------------------------------------------------------------------ + // Units + //------------------------------------------------------------------------ + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + } + + //------------------------------------------------------------------------ + // Shippies + //------------------------------------------------------------------------ + for (i = 0; i < Vessels.Count(); i++) { + vessp = (VesselClass *)Vessels.Active_Ptr(i); + Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom); + Add_CRC (&GameCRC, (int)vessp->Strength); + Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom); + } + + //------------------------------------------------------------------------ + // Buildings + //------------------------------------------------------------------------ + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + } + + //------------------------------------------------------------------------ + // Houses + //------------------------------------------------------------------------ + for (i = 0; i < Houses.Count(); i++) { + housep = (HouseClass *)Houses.Active_Ptr(i); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + for (i = 0; i < LAYER_COUNT; i++) { + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + } + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + + //------------------------------------------------------------------------ + // A random # + //------------------------------------------------------------------------ +// Add_CRC(&GameCRC, Scen.RandomNumber.Seed); + Add_CRC(&GameCRC, Scen.RandomNumber); + +} /* end of Compute_Game_CRC */ + + +/*************************************************************************** + * Add_CRC -- Adds a value to a CRC * + * * + * INPUT: * + * crc ptr to crc * + * val value to add * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Add_CRC(unsigned long *crc, unsigned long val) +{ + int hibit; + + if ( (*crc) & 0x80000000) { + hibit = 1; + } + else { + hibit = 0; + } + + (*crc) <<= 1; + (*crc) += val; + (*crc) += hibit; + +} /* end of Add_CRC */ + +/*************************************************************************** + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * * + * INPUT: * + * ev -- event to display * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Print_CRCs(EventClass *ev) +{ + int i,j; + InfantryClass *infp; + UnitClass *unitp; + VesselClass *vesselp; + BuildingClass *bldgp; + ObjectClass *objp; + FILE *fp; + HouseClass *housep; + HousesType house; + int color; + + Mono_Clear_Screen(); + Mono_Set_Cursor (0,0); + fp = fopen("OUT.TXT","wt"); + if (fp==NULL) { + return; + } + + for (i = 0; i < 32; i++) { + fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]); + } + + // + // Houses + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + GameCRC = 0; + housep = HouseClass::As_Pointer (house); + if (housep) { + HousesType actlike = housep->ActLike; + color = housep->RemapColor; + fprintf(fp,"%s: IsHuman:%d Color:%s ID:%d ActLike:%s\n", + housep->IniName, + housep->IsHuman, + ColorNames[color], + housep->ID, + HouseClass::As_Pointer(actlike)->Class->Name()); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Infantry + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Infantry -------------------\n", + housep->Class->Name()); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==house) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x Speed:%d NavCom:%x\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom); + } + } + Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Units + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Units -------------------\n", + housep->Class->Name()); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==house) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d Tgt:%x\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type, unitp->As_Target()); + } + } + Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Vessels + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Vessels -------------------\n", + housep->Class->Name()); + for (i = 0; i < Vessels.Count(); i++) { + vesselp = (VesselClass *)Vessels.Active_Ptr(i); + if (vesselp->Owner()==house) { + Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom); + Add_CRC (&GameCRC, (int)vesselp->Strength); + Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom); + fprintf(fp, + "COORD:%x Facing:%d Mission:%d Strength:%d Type:%d Tgt:%x\n", + vesselp->Coord,(int)vesselp->PrimaryFacing, + vesselp->Get_Mission(), vesselp->Strength, + vesselp->Class->Type, vesselp->As_Target()); + } + } + Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Buildings + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Buildings -------------------\n", + housep->Class->Name()); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==house) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type, bldgp->As_Target()); + } + } + Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Animations + // + AnimClass *animp; + fprintf(fp,"-------------------- Animations -------------------\n"); + for (i = 0; i < Anims.Count(); i++) { + animp = (AnimClass *)Anims.Active_Ptr(i); + fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n", + animp->xObject, + animp->OwnerHouse, + animp->Loops); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + GameCRC = 0; + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,">>>> MAP LAYER %d <<<<\n",i); + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",j,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + else if (objp->What_Am_I() == RTTI_VESSEL) + fprintf(fp,"Vessel (Type:%d) ", + (VesselType)(*((VesselClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + } + Mono_Printf("Map Layers:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + GameCRC = 0; + fprintf(fp,">>>> LOGIC LAYER <<<<\n"); + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",i,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + Mono_Printf("Logic:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Random # generator, frame # + //------------------------------------------------------------------------ + Mono_Printf("Random Number:%x \n",Scen.RandomNumber.Seed); +#ifdef RANDOM_COUNT + fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n", + Scen.RandomNumber.Seed, + Scen.RandomNumber.Count1, + Scen.RandomNumber.Count2); +#else + fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed); +#endif + + Mono_Printf("My Frame:%d \n",Frame); + fprintf(fp,"My Frame:%d\n",Frame); + + if (ev) { + fprintf(fp,"\n"); + fprintf(fp,"Offending event:\n"); + fprintf(fp," Type: %d\n",ev->Type); + fprintf(fp," Frame: %d\n",ev->Frame); + fprintf(fp," ID: %x\n",ev->ID); + fprintf(fp," CRC: %x\n",ev->Data.FrameInfo.CRC); + fprintf(fp," CommandCount: %d\n",ev->Data.FrameInfo.CommandCount); + fprintf(fp," Delay: %d\n",ev->Data.FrameInfo.Delay); + } + + fclose(fp); + +} /* end of Print_CRCs */ + + +/*************************************************************************** + * Init_Queue_Mono -- inits mono display * + * * + * This routine steals control of the mono screen away from the rest of * + * the engine, by setting the global IsMono; if IsMono is set, the other * + * routines in this module turn off the Mono display when they're done * + * with it, so the rest of the engine won't over-write what we're writing. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Init_Queue_Mono(ConnManClass *net) +{ +#if(SHOW_MONO) + //------------------------------------------------------------------------ + // Set 'IsMono' so we can steal the mono screen from the engine + //------------------------------------------------------------------------ + if ((Frame==0 || Session.LoadGame) && MonoClass::Is_Enabled()) { + IsMono = true; + } + + //------------------------------------------------------------------------ + // Enable mono output for our stuff; we must Disable it before we return + // control to the engine. + //------------------------------------------------------------------------ + if (IsMono) + MonoClass::Enable(); + + if (net->Num_Connections() > 0) { + //..................................................................... + // Network mono debugging screen + //..................................................................... + if (NetMonoMode==0) { + if (Frame==0 || Session.LoadGame || NewMonoMode) { + net->Configure_Debug (0, sizeof (CommHeaderType), + sizeof(EventClass::EventType), EventClass::EventNames, 0, 27); + net->Mono_Debug_Print (0,1); + NewMonoMode = 0; + } + else { + net->Mono_Debug_Print (0,0); + } + } + //..................................................................... + // Flow control debugging output + //..................................................................... + else { + if (NewMonoMode) { + Mono_Clear_Screen(); + Mono_Printf(" Queue AI:\n"); // flowcount[0] + Mono_Printf(" Build Packet Loop:\n"); // flowcount[1] + Mono_Printf(" Frame Sync:\n"); // flowcount[2] + Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3] + Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4] + Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5] + Mono_Printf(" DoList Execution:\n"); // flowcount[6] + Mono_Printf(" DoList Cleaning:\n"); // flowcount[7] + Mono_Printf("\n"); + Mono_Printf(" Frame:\n"); + Mono_Printf(" Session.MaxAhead:\n"); + Mono_Printf(" their_recv:\n"); + Mono_Printf(" their_sent:\n"); + Mono_Printf(" my_sent:\n"); + NewMonoMode = 0; + } + } + } +#else + net = net; +#endif +} // end of Init_Queue_Mono + + +/*************************************************************************** + * Update_Queue_Mono -- updates mono display * + * * + * INPUT: * + * net ptr to connection manager * + * flow_index index # for flow-count updates * + * -1: display * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Update_Queue_Mono(ConnManClass *net, int flow_index) +{ +#if(SHOW_MONO) + static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + //------------------------------------------------------------------------ + // If 'NetMonoMode' is 1, display flowcount info + //------------------------------------------------------------------------ + if (NetMonoMode==1) { + if (flow_index >= 0 && flow_index < 20) { + Mono_Set_Cursor(35,flow_index); + flowcount[flow_index]++; + Mono_Printf("%d",flowcount[flow_index]); + } + } + //------------------------------------------------------------------------ + // Otherwise, display the connection debug screen + //------------------------------------------------------------------------ + else { + net->Mono_Debug_Print (0,0); + } + +#else + flow_index = flow_index; + net = net; +#endif + +} // end of Update_Queue_Mono + + +/*************************************************************************** + * Print_Framesync_Values -- displays frame-sync variables * + * * + * INPUT: * + * curframe current game Frame # * + * max_ahead max-ahead value * + * num_connections # connections * + * their_recv # commands I've received from my connections * + * their_sent # commands each connection claims to have sent * + * my_sent # commands I've sent * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent) +{ +#if(SHOW_MONO) + int i; + + if (NetMonoMode==1) { + Mono_Set_Cursor(35,9); + Mono_Printf("%d",curframe); + + Mono_Set_Cursor(35,10); + Mono_Printf("%d",max_ahead); + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,11); + Mono_Printf("%4d",(int)their_recv[i]); + } + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,12); + Mono_Printf("%4d",(int)their_sent[i]); + } + + Mono_Set_Cursor(35,13); + Mono_Printf("%4d",(int)my_sent); + } +#else + curframe = curframe; + max_ahead = max_ahead; + num_connections = num_connections; + their_recv = their_recv; + their_sent = their_sent; + my_sent = my_sent; +#endif +} // end of Print_Framesync_Values + + +/*************************************************************************** + * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk * + * * + * INPUT: * + * event ptr to event to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/28/1996 BRR : Created. * + *=========================================================================*/ +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + FILE *fp; + int i; + HousesType house; + + fp = fopen("toolate.txt", "wt"); + if (!fp) { + return; + } + fprintf(fp,"----------------- Event data: ----------------------\n"); + fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame: %d\n",event->Frame); + fprintf(fp,"ID: %d\n",event->ID); + + for (i = 0; i < Session.Players.Count(); i++) { + if (event->ID == Session.Players[i]->Player.ID) { + fprintf(fp,"Player's Name: %s",Session.Players[i]->Name); + } + } + fprintf(fp,"\n"); + + fprintf(fp,"--------------------- My data: ---------------------\n"); + fprintf(fp,"My Frame:%d\n",Frame); + fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead); + + if (net) { + fprintf(fp,"-------------------- Frame Stats: ------------------\n"); + fprintf(fp,"Name ID TheirFrame TheirSent TheirRecv\n"); + for (i = 0; i < net->Num_Connections(); i++) { + house = (HousesType)(net->Connection_ID(i)); + fprintf(fp,"%12s %2d %6d %6d %6d\n", + (HouseClass::As_Pointer(house))->IniName, + net->Connection_ID(i), + their_frame[i], + their_sent[i], + their_recv[i]); + } + } + + fclose(fp); +} + +/*************************************************************************** + * Check_Mirror -- Checks mirror memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/14/1996 BRR : Created. * + *=========================================================================*/ +void Check_Mirror(void) +{ +#ifdef MIRROR_QUEUE + int i; + char txt[80]; + unsigned long *ptr; + int found_5s = 0; + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + for (i = 0; i < DoList.Count; i++) { + if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) { + sprintf(txt,"Queue Memory Trashed! Head:%d Tail:%d, Addr:%p or %p", + DoList.Get_Head(), + DoList.Get_Tail(), + DoList.Get_Array() + i, + MirrorList.Get_Array() + i); + WWMessageBox().Process (txt); + Prog_End("Check_Mirror", true); + Emergency_Exit(0); + } + } + + if (found_5s) { + //Prog_End(); + Emergency_Exit(0); + } + +#endif +} // end of Check_Mirror + + +/*************************** end of queue.cpp ******************************/ \ No newline at end of file diff --git a/REDALERT/QUEUE.H b/REDALERT/QUEUE.H new file mode 100644 index 000000000..3206ae545 --- /dev/null +++ b/REDALERT/QUEUE.H @@ -0,0 +1,292 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/QUEUE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/08/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * QueueClass::Add -- Add object to queue. * + * QueueClass::First -- Fetches reference to first object in list. * + * QueueClass::Init -- Initializes queue to empty state. * + * QueueClass::Next -- Throws out the head of the line. * + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include "mission.h" +#include "target.h" +#include "defines.h" + +//#pragma warn -inl + +/* +** This class implements a classic FIFO queue (also known as - standing in line). Objects +** are added to the end (tail) of the line. Objects are removed from the start (first) of +** the line. A keyboard buffer is a good example of a common computer use of a queue. There +** is no provision for "taking cuts" or leaving the line once an object has been added. +** +** The implementation uses a circular list of objects. This allows adding and deleting of +** elements without any maintenance moves of remaining objects that would otherwise be +** necessary in a simple array storage method. A side effect of this means that accessing the +** internal array directly is not allowed. Supporting functions are provided for this purpose. +** +** WARNING WARNING WARNING WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +** The size parameter MUST be an exact power of two (2, 4, 8, 16, etc.) otherwise the internal +** indexing algorithm will fail. +*/ +template +class QueueClass +{ + public: + /* + ** This is the count of the number of objects in the queue. If this count is zero, + ** then the operator[], First(), and Next() functions are undefined. Check this + ** value BEFORE calling these functions. + */ + const int Count; + + //-------------- Functions -------------------- + QueueClass(void); // Default constructor. + + /* + ** The bracket subscript operator functions similarly to the way a normal subscript + ** operator works except that entry [0] matches the first-in-line and entry + ** [Count-1] matches the last-in-line. This is ensured regardless of the actual position + ** of the object in the circular internal list. + */ + T & operator[](int); + + /* + ** This function will return a reference to the "head of the line" object. + */ + T & First(void); + + /* + ** This function clears the list of objects. + */ + void Init(void); + + /* + ** This function discards the head-of-the-line object and advances all the remaining + ** objects up by one. Mnemonic: Imagine a broadway audition and the director yells + ** "NEXT!" + */ + int Next(void); + + /* + ** This will add an object to the tail of the line. If there is no more room to add + ** the object, then false will be returned. + */ + int Add(T const &); + + int Get_Head(void); + int Get_Tail(void); + T * Get_Array(void); + + private: + int Head; // Index of element in list the longest. + int Tail; // Index where next new addition will go. + + T Array[size]; // Raw array of objects. +}; + + +/*********************************************************************************************** + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * * + * This default constructor for QueueClass objects initializes the queue to an empty * + * state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline QueueClass::QueueClass(void) : Count(0) +{ + Init(); +} + + +/*********************************************************************************************** + * QueueClass::Init -- Initializes queue to empty state. * + * * + * This function resets the queue to an empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline void QueueClass::Init(void) +{ + ((int &)Count) = 0; + Head = 0; + Tail = 0; +} + + +/*********************************************************************************************** + * QueueClass::Add -- Add object to queue. * + * * + * This function is used to add an object to the tail of the line. If the queue cannot * + * accept any more entries, then the object won't be added and false will be returned. * + * * + * INPUT: object -- The object that is to be added to the queue. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: If the queue is full, then the object won't be added. Be sure to check for this.* + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Add(T const &q) +{ + if (Count < size) { + Array[Tail] = q; + Tail = (Tail + 1) & (size-1); + ((int &)Count) = Count + 1; + return(true); + } + return (false); +} + + +/*********************************************************************************************** + * QueueClass::Next -- Throws out the head of the line. * + * * + * This routine is used to discard the object at the head of the line. All remaining * + * objects "move up" one. No actual movement occurs, merely the index is adjusted, but * + * the affect is that the next oldest object in the queue will now be returned with the * + * next call to the First() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of object remaining in the queue. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Next(void) +{ + if (Count) { + Head = (Head + 1) & (size-1); + ((int &)Count) = Count - 1; + } + return (Count); +} + + +/*********************************************************************************************** + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * * + * Use this routine to examine individual objects within the queue. The oldest object in * + * the queue is referenced by an index value of zero. The newest object in the queue is * + * referenced by a value of Count-1. If there are no objects in the queue, then this * + * operator is undefined. Although this operator allows examination of the queue, there is * + * no corresponding ability to insert or delete objects from the middle of the queue. * + * * + * INPUT: index -- The index into the queue of objects. Valid values range from zero to * + * Count-1. All other values return an undefined reference! * + * * + * OUTPUT: Returns with a reference to the object indicated by the index. * + * * + * WARNINGS: Check to make sure that Count is not zero before using this operator. Failure * + * to do so will return a reference to an undefined object. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::operator[](int index) +{ + return Array[(Head + index) & (size-1)]; +} + + +/*********************************************************************************************** + * QueueClass::First -- Fetches reference to first object in list. * + * * + * This routine is used to fetch the first object in the list (head of the line). This * + * object is the oldest in the list. Typical use of this function is to get and process * + * the first object so that it may be discarded with the Next() function in order to bring * + * subsequent objects to the first position. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the oldest object in the queue. * + * * + * WARNINGS: If there are no objects in the queue, then this function returns an undefined * + * reference. Be sure to check Count against zero before calling this function. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::First(void) +{ + return Array[Head]; +} + +template +inline int QueueClass::Get_Head(void) +{ + return Head; +} + +template +inline int QueueClass::Get_Tail(void) +{ + return Tail; +} + +template +inline T * QueueClass::Get_Array(void) +{ + return Array; +} + +#endif diff --git a/REDALERT/RADAR.CPP b/REDALERT/RADAR.CPP new file mode 100644 index 000000000..f540bc7ee --- /dev/null +++ b/REDALERT/RADAR.CPP @@ -0,0 +1,2683 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADAR.CPP 3 3/12/97 2:35p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Multi_Color -- Get the multi color offset number * + * RadarClass::AI -- Processes radar input (non-tactical). * + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * RadarClass::Click_In_Radar -- Check to see if a click is in radar map * + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * RadarClass::Draw_Names -- draws players' names on the radar map * + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * RadarClass::Init_Clear -- Sets the radar map to a known state * + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * RadarClass::Radar_Activate -- Controls radar activation. * + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell.* + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to* + * RadarClass::Zoom_Mode(void) -- Handles toggling zoom on the map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +//void const * RadarClass::CoverShape; +RadarClass::RTacticalClass RadarClass::RadarButton; + +void const * RadarClass::RadarAnim = NULL; +void const * RadarClass::RadarPulse = NULL; +void const * RadarClass::RadarFrame = NULL; + +static bool FullRedraw = false; + +static GraphicBufferClass _IconStage(3,3); +static GraphicBufferClass _TileStage(24,24); + + +/*********************************************************************************************** + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * * + * This default constructor merely sets the radar specific values to default settings. The * + * radar must be deliberately activated in order for it to be displayed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/16/1994 JLB : Created. * + *=============================================================================================*/ +RadarClass::RadarClass(void) : + IsToRedraw(false), + RadarCursorRedraw(false), + IsPulseActive(false), + RadarPulseFrame(0), + DoesRadarExist(false), + IsRadarActive(false), + IsRadarActivating(false), + IsRadarDeactivating(false), + IsRadarJammedByPlayerMask(0U), + SpecialRadarFrame(0), + RadarAnimFrame(0), + RadarX(0), + RadarY(0), + RadarCellWidth(0), + RadarCellHeight(0), + RadarCell(0), + BaseX(0), + BaseY(0), + RadarWidth(0), + RadarHeight(0), + IsZoomed(true), + ZoomFactor(0), + IsPlayerNames(false), + IsHouseSpy(false), + SpyingOn(HOUSE_SPAIN), + PixelPtr(0) +{ +} + + +/*********************************************************************************************** + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * * + * This routine handles any one time processing required in order for the radar map to * + * function. This actually only requires an allocation of the radar staging buffer. This * + * buffer is needed for those cases where the radar area of the page is being destroyed * + * and it needs to be destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine only ONCE. * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::One_Time(void) +{ + RadWidth = 80 * RESFACTOR; + RadHeight = 70 * RESFACTOR; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = 7 * RESFACTOR; + RadPWidth = 64 * RESFACTOR; + RadPHeight = 64 * RESFACTOR; + #ifdef WIN32 + RadOffX = 6; + RadOffY = 7; + RadIWidth = 128+18;//************ + RadIHeight = 128+2;//************ + #else + RadOffX = 4; + RadOffY = 1; + RadIWidth = 72; + RadIHeight = 69; + #endif + + DisplayClass::One_Time(); +#ifdef OBSOLETE + RadarButton.X = RadX+RadOffX; + RadarButton.Y = RadY+RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +#else + RadarButton.X = RadX; + RadarButton.Y = RadY; + RadarButton.Width = RadWidth; + RadarButton.Height = RadHeight; +#endif +} + + +/*********************************************************************************************** + * RadarClass::Init_Clear -- Sets the radar map to a known state. * + * * + * This routine is used to initialize the radar map at the start of the scenario. It * + * sets the radar map position and starts it in the disabled state. * + * * + * INPUT: theater -- The theater that the scenario is starting (unused by this routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Init_Clear(void) +{ + DisplayClass::Init_Clear(); + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; + + /* + ** If we have a valid map lets make sure that we set it correctly + */ + if (MapCellWidth || MapCellHeight) { +#ifdef WIN32 + IsZoomed = false; +#else + IsZoomed = true; +#endif + Zoom_Mode(Coord_Cell(Map.TacticalCoord)); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Activate -- Controls radar activation. * + * * + * Use this routine to turn the radar map on or off. * + * * + * INPUT: control -- What to do with the radar map: * + * 0 = Turn radar off. * + * 1 = Turn radar on. * + * 2 = Remove Radar Gadgets * + * 3 = Add Radar Gadgets * + * 4 = Remove radar. * + * -1= Toggle radar on or off. * + * * + * OUTPUT: bool; Was the radar map already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Radar_Activate(int control) +{ + bool old = IsRadarActive; + + switch (control) { + + /* + ** Toggle the state of the radar map on or off. + */ + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; + } + } + } else { + Radar_Activate(2); + } + return(old); + + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } else { + RadarAnimFrame = 0; + } + } + } + } else { + Radar_Activate(3); + } + return(old); + + case 2: + if (Session.Type==GAME_NORMAL) { + SidebarClass::Zoom.Disable(); + } else { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 3: + if (Session.Type == GAME_NORMAL && Is_Zoomable()) { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; + + default: + break; + } + + if (IsRadarActive != old) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + FullRedraw = IsRadarActive; + return(old); +} + + +/*********************************************************************************************** + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * * + * This is used to display the radar map that appears in the lower * + * right corner. The main changes to this map are the vehicles and * + * structure pixels. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1991 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Draw_It(bool forced) +{ + DisplayClass::Draw_It(forced); + +#if (0) // Legacy radar rendering not used. ST - 2/26/2020 3:53PM + + static char * _hiresradarnames[]={ + "natoradr.shp", //HOUSE_SPAIN, + "natoradr.shp", //HOUSE_GREECE, + "ussrradr.shp", //HOUSE_USSR, + "natoradr.shp", //HOUSE_ENGLAND, + "ussrradr.shp", //HOUSE_UKRAINE, + "natoradr.shp", //HOUSE_GERMANY, + "natoradr.shp", //HOUSE_FRANCE, + "natoradr.shp", //HOUSE_TURKEY, + "natoradr.shp", //HOUSE_GOOD + "ussrradr.shp", //HOUSE_BAD + }; + static char * _frames[]={ + "nradrfrm.shp", //HOUSE_SPAIN, + "nradrfrm.shp", //HOUSE_GREECE, + "uradrfrm.shp", //HOUSE_USSR, + "nradrfrm.shp", //HOUSE_ENGLAND, + "uradrfrm.shp", //HOUSE_UKRAINE, + "nradrfrm.shp", //HOUSE_GERMANY, + "nradrfrm.shp", //HOUSE_FRANCE, + "nradrfrm.shp", //HOUSE_TURKEY, + "nradrfrm.shp", //HOUSE_GOOD + "uradrfrm.shp", //HOUSE_BAD + }; + + int radarforced = 0; + + /* + ** Don't perform any rendering if none is requested. + */ + if (!forced && !IsToRedraw && !FullRedraw) return; + + BStart(BENCH_RADAR); + + static HousesType _house = HOUSE_NONE; + + if (PlayerPtr->ActLike != _house) { + char name[_MAX_FNAME + _MAX_EXT]; + +// strcpy(name, "NATORADR.SHP" ); +// if (Session.Type == GAME_NORMAL) { + strcpy(name, _hiresradarnames[PlayerPtr->ActLike]); +// } + #ifndef NDEBUG + RawFileClass file(name); + if (file.Is_Available()) { + RadarAnim = Load_Alloc_Data(file); + } else { + RadarAnim = MFCD::Retrieve(name); + } + strcpy(name, "PULSE.SHP"); + RawFileClass file2(name); + if (file2.Is_Available()) { + RadarPulse = Load_Alloc_Data(file2); + } else { + RadarPulse = MFCD::Retrieve(name); + } + strcpy(name, _frames[PlayerPtr->ActLike]); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarFrame = Load_Alloc_Data(file3); + } else { + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + } + #else + RadarAnim = MFCD::Retrieve(name); + strcpy(name, "PULSE.SHP"); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarPulse = Load_Alloc_Data(file3); + } else { + RadarPulse = MFCD::Retrieve(name); + } + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + #endif + _house = PlayerPtr->ActLike; + } + + /* + ** If in player name mode, just draw player names + */ + if (IsPlayerNames) { + Draw_Names(); + IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + /* + ** If in spy-on-radar facility mode, draw the appropriate info. + */ + if (IsHouseSpy) { + IsToRedraw = false; + if (Draw_House_Info()) { + BEnd(BENCH_RADAR); + return; + } + } + + if (IsRadarActivating || IsRadarDeactivating || IsRadarJammed) { + Radar_Anim(); + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + if (Map.IsSidebarActive) { + if (IsRadarActive) { + + if (RunningAsDLL) { + BEnd(BENCH_RADAR); + return; + } + + /* + ** If only a few of the radar pixels need to be redrawn, then find and redraw + ** only these. + */ + if (!forced && IsToRedraw && !FullRedraw && !IsPulseActive) { + IsToRedraw = false; + + if (PixelPtr) { + + /* + ** Render all pixels in the "to redraw" stack. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < (int)PixelPtr; index++) { + CELL cell = PixelStack[index]; + if (Cell_On_Radar(cell)) { + (*this)[cell].IsPlot = false; + Plot_Radar_Pixel(cell); + RadarCursorRedraw |= (*this)[cell].IsRadarCursor; + } + } + LogicPage->Unlock(); + } + + /* + ** Refill the stack if there is pending pixels yet to be plotted. + ** This should only process in sections for speed reasons + */ + if (PixelPtr == PIXELSTACK) { + PixelPtr = 0; + + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = XY_Cell(MapCellX + x, MapCellY + y); + if (Cell_On_Radar(cell)) { + + if ((*this)[cell].IsPlot) { + PixelStack[PixelPtr++] = cell; + IsToRedraw = true; + if (PixelPtr == PIXELSTACK) break; + } + } + } + if (PixelPtr == PIXELSTACK) break; + } + } else { + PixelPtr = 0; + } + } + + Radar_Cursor(RadarCursorRedraw); + + } else { + +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); +#endif + + CC_Draw_Shape(RadarFrame, 1, RadX, RadY+(1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + if (BaseX || BaseY) { + + if (!IsZoomed && BaseX && BaseY && (int)RadarWidth< (RadIWidth-1) && (int)RadarHeight < (RadIHeight-1)) { +#ifdef WIN32 + LogicPage->Draw_Rect(RadX + RadOffX + BaseX -1, + RadY + RadOffY + BaseY -1, + RadX + RadOffX + BaseX + RadarWidth, +// RadX + RadOffX + BaseX + RadarWidth +1, + RadY + RadOffY + BaseY + RadarHeight, +// RadY + RadOffY + BaseY + RadarHeight +1, + WHITE); +#endif + } + } else { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + } + + /* + ** Draw the entire radar map. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (In_Radar(index) && Cell_On_Radar(index)) { + Plot_Radar_Pixel(index); + } + } + if (IsPulseActive) { + CC_Draw_Shape(RadarPulse, RadarPulseFrame++, RadX + RadOffX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + } + LogicPage->Unlock(); + } + + Radar_Cursor(true); + FullRedraw = false; + IsToRedraw = false; + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + if (oldpage == &SeenBuff) { + Hide_Mouse(); + LogicPage->Blit(SeenBuff, RadX, RadY, RadX, RadY, RadWidth, RadHeight); + Show_Mouse(); + } + + Set_Logic_Page(oldpage); + } + + } else { + + /* + ** If the radar is not active, then only draw the cover plate if forced to do so. + */ + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + CC_Draw_Shape(RadarAnim, val, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + FullRedraw = false; + IsToRedraw = false; + + if (RunningAsDLL) { + BEnd(BENCH_RADAR); + return; + } + + /* + ** Display the country name on the cover plate when in multi play only. + */ + if (Session.Type != GAME_NORMAL) { + Fancy_Text_Print(Text_String(HouseTypeClass::As_Reference(PlayerPtr->ActLike).Full_Name()), RadX+RadWidth/2, RadY+RadHeight-10*RESFACTOR, &ColorRemaps[PlayerPtr->RemapColor], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + } + + } + BEnd(BENCH_RADAR); +#endif +} + + +/*************************************************************************** + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) +{ + TerrainClass * list[4] = {0,0,0,0}; + int listidx = 0; + int lp,lp2; + + + ObjectClass * obj = Map[cell].Cell_Occupier(); + + /* + ** If the cell is occupied by a terrain type, add it to the sortable + ** list. + */ + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + + /* + ** Now loop through all the occupiers and add them to the list if they + ** are terrain type. + */ + for (lp = 0; lp < ARRAY_SIZE(Map[cell].Overlapper); lp ++) { + obj = Map[cell].Overlapper[lp]; + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + } + + /* + ** If there are no entries in our list then just get out. + */ + if (!listidx) return; + + /* + ** If there is terrain in this cell then draw a dark pixel to + ** represent it. + */ + if (size == 1) { + LogicPage->Put_Pixel(x, y, 21); +// LogicPage->Put_Pixel(x, y, 60); + return; + } + + /* + ** Sort the list by its sort Y value so that we can render in the proper + ** order. + */ + for (lp = 0; lp < listidx - 1; lp ++) { + for (lp2 = lp + 1; lp2 < listidx; lp2++) { + if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { + TerrainClass * terrain = list[lp]; + list[lp] = list[lp2]; + list[lp2] = terrain; + } + } + } + + /* + ** loop through the list and take care of rendering the correct icon. + */ + for (lp = 0; lp < listidx; lp ++) { + unsigned char * icon = list[lp]->Radar_Icon(cell); + if (!icon) continue; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char *)&FadingBrighten[0]); +#else + for (int lpy = 0; lpy < 3; lpy++) { + for (int lpx = 0; lpx < 3; lpx++) { + if (*icon) { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + icon++; + } + } +#endif //WIN32 + } +} + + +/*********************************************************************************************** + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * * + * This routine will display an object imagery at the location specified according to the * + * condition of the specified cell. * + * * + * INPUT: cell -- The cell to use as reference when drawing the radar pixel. * + * * + * x,y -- The pixel coordinate to render the radar "pixel" at. * + * * + * size -- The size of the "pixel". When zoomed in, this value will be "3". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) +{ + ObjectClass * obj; + + obj = (ObjectClass *)Map[cell].Cell_Occupier(); + while (obj) { + if (obj->Is_Techno() && ((TechnoClass *)obj)->Is_Visible_On_Radar()) { + int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].Bar; +// int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].BrightColor; + int xoff; + int yoff; + int subsize = max(1, size/3); + + switch (obj->What_Am_I()) { + case RTTI_INFANTRY: + xoff = (Coord_XLepton(obj->Coord) / (CELL_LEPTON_W/(size+1))) - subsize/2; + xoff = max(xoff, 0); + xoff = min(xoff, size-subsize); + yoff = (Coord_YLepton(obj->Coord) / (CELL_LEPTON_H/(size+1))) - subsize/2; + yoff = max(yoff, 0); + yoff = min(yoff, size-subsize); + + /* + ** Draw the infantryman's pixel. If he's a spy, draw in my house color + */ + if (*(InfantryClass *)obj == INFANTRY_SPY) { + color = ColorRemaps[PlayerPtr->RemapColor].Bar; +// color = ColorRemaps[PlayerPtr->RemapColor].BrightColor; + } + LogicPage->Fill_Rect(x+xoff, y+yoff, x+xoff+(subsize-1), y+yoff+(subsize-1), color); + break; + + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + LogicPage->Fill_Rect(x, y, x+size-1, y+size-1, color); + break; + + default: + break; + } + } + obj = obj->Next; + } + +} + + +/*************************************************************************** + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) +{ + //int lpx,lpy; + + OverlayType overlay = (*this)[cell].Overlay; + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const * otype = &OverlayTypeClass::As_Reference(overlay); + + if (otype->IsRadarVisible) { + unsigned char * icon = otype->Radar_Icon((*this)[cell].OverlayData); + if (!icon) return; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x, y, DKGREY); + +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingShade[0]); + } else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingYellow[0]); + } +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); +// } else { +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + } + +#else + for (int lpy = 0; lpy < size; lpy++) { + for (int lpx = 0; lpx < size; lpx++) { + if (size == 1) icon+=4; + if (*icon) { + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x + lpx, y + lpy, DKGREY); +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingShade[*icon]); + } else { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingYellow[*icon]); + } +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingGreen[*icon]); +// } else { +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + } + if (size == 1) { + icon+=5; + } else { + icon++; + } + icon++; + } + } +#endif //WIN32 + } + } +} + + +/*************************************************************************** + * RadarClass::Zoom_Mode -- Handles toggling zoom on the map * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/29/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Zoom_Mode(CELL cell) +{ + int map_c_width; + int map_c_height; + + /* + ** Set all of the initial zoom mode variables to the correct + ** setting. + */ +#ifdef WIN32 + if (Is_Zoomable()) { + IsZoomed = !IsZoomed; + } else { + IsZoomed = true; + } +#else + IsZoomed = false; +#endif + BaseX = 0; + BaseY = 0; + + /* + ** Figure out exactly what size we need to zoom the map to. + */ + if (!IsZoomed) { + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = max(min(xfactor, yfactor) , 1); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } else { + ZoomFactor = 3; +// ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; + } + + /* + ** Make sure we do not show more cells than are on the map. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + map_c_width = min(map_c_width, RadIWidth); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, RadIHeight); + map_c_height = min(map_c_height, MapCellHeight); +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif + + /* + ** Find the amount of remainder because this will let us calculate + ** how to center the thing. + */ + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); + + /* + ** Finally mark the map so it shows just as much as it is supposed + ** to. + */ + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIHeight - rem_y; + + /* + ** Set the radar position to the current cell. + */ + Set_Radar_Position(cell); + + /* + ** When zoom mode changes then we need to redraw the radar + ** area. + */ + IsToRedraw = true; + + /* + ** Notify the map that we need to redraw a portion + */ + Flag_To_Redraw(false); + + /* + ** Since we have made a vast change we must redraw everything + */ + FullRedraw = true; +} + + +/*********************************************************************************************** + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * * + * This will check to see if the zoomed mode of the map would be just the same size as * + * the non-zoomed mode. If this is true, then zooming would have no effect, so return * + * false indicating that zooming is not allowed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is zooming allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Zoomable(void) const +{ + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + int factor = max(min(xfactor, yfactor) , 1); + if (factor == 3) { + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * * + * This will update the radar map with a pixel. It is used to display * + * vehicle positions on the radar map. * + * * + * INPUT: unit -- Pointer to unit to render at the given position. If * + * NULL is passed in, then the underlying terrain is * + * displayed instead. * + * * + * pos -- Position on the map to update. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine does NOT hide the mouse. It is up to you to * + * do so. * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 06/21/1991 JLB : Large blips for units & buildings. * + * 02/14/1994 JLB : Revamped. * + * 04/17/1995 PWG : Created. * + * 04/18/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Plot_Radar_Pixel(CELL cell) +{ + if (cell == -1) cell = 1; + + int x,y; // Coordinate of cell location. + + /* + ** Perform any clipping on the cell coordinate. + */ + if (!IsRadarActive || (unsigned)cell > MAP_CELL_TOTAL) { + return; + } + + if (!In_Radar(cell) || !Cell_On_Radar(cell)) { + return; + } + + /* + ** If we are zoomed in then calculate the pixel based off of the portion + ** of the map the radar is viewing. + */ + x = Cell_X(cell) - RadarX; + y = Cell_Y(cell) - RadarY; + if ((unsigned)x >= RadarCellWidth || (unsigned)y >= RadarCellHeight) { + return; + } + + bool usjamming = false; + if (LogicPage->Lock()) { + CellClass * cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); + + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color=TBLACK; // Color of the pixel to plot. + int housebit = (1 << PlayerPtr->Class->House); + int celljammed = (*this)[cell].Jammed; + int jammed = celljammed & (0xFFFF - housebit); + if (!jammed && ((*this)[cell].IsMapped || Debug_Unshroud)) { +// if (!jammed && ((*this)[cell].IsVisible || Debug_Unshroud)) { + color = cellptr->Cell_Color(true); + if ( (celljammed & housebit) && (color == TBLACK) ) { + color = BLACK;//FadingWayDark[color]; + usjamming = true; + } + } else { + color = BLACK; + } + + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ + if (color == TBLACK) { + if (ZoomFactor > 1) { + void const * ptr = NULL; + int icon; + + /* + ** Fetch the template pointer and template icon number for the + ** specified cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != 255) { + ptr = TemplateTypeClass::As_Reference(cellptr->TType).Get_Image_Data(); + icon = cellptr->TIcon; + } + + /* + ** If the template pointer is still NULL, then this means either a clear + ** template or an illegal one. Setup for a clear template. + */ + if (ptr == NULL) { + ptr = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Get_Image_Data(); + icon = cellptr->Clear_Icon(); + } + + IconsetClass const * iconset = (IconsetClass const *)ptr; + unsigned char const * icondata = iconset->Icon_Data(); + + + /* + ** Convert the logical icon number into the actual icon number. + */ + icon &= 0x00FF; + icon = *(iconset->Map_Data() + icon); + + unsigned char * data = (unsigned char *)icondata + icon*(24*24); + Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + _TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); + } else { +// LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, cellptr->Cell_Color(false)); +/*BG*/ LogicPage->Put_Pixel(x, y, cellptr->Cell_Color(false)); + } + } else { + LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, color); +///*BG*/ LogicPage->Put_Pixel(x, y, color); + } + if (color != BLACK) { + Render_Overlay(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); + Render_Infantry(cell, x, y, ZoomFactor); + } else { + if(usjamming) { + Render_Infantry(cell, x, y, ZoomFactor); + } + } + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * * + * This routine is used to inform the system that a pixel needs to be * + * rerendered on the radar map. The pixel(s) will be rendered the * + * next time the map is refreshed. * + * * + * INPUT: cell -- The map cell to be rerendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1992 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Radar_Pixel(CELL cell) +{ + if (IsRadarActive && Map.IsSidebarActive && Cell_On_Radar(cell)) { + IsToRedraw = true; + (*this)[cell].IsPlot = true; + if (PixelPtr < PIXELSTACK) { + PixelStack[PixelPtr++] = cell; + } + } +} + + +/*********************************************************************************************** + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * * + * This routine will examine the X and Y coordinate and convert them into the X and Y * + * cell coordinate value that corresponds to the location. * + * * + * INPUT: x,y -- The X and Y mouse coordinate already normalized to the radar upper left * + * corner. * + * * + * OUTPUT: Returns with success rating in addition, the X and Y values will now hold the * + * cell coordinates of the cell the pixel offsets indicated. * + * Result 1 = click was in radar region * + * Result 0 = click was outside radar region completely * + * Result-1 = click in radar area but not on clickable region of radar. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1995 PWG : Created. * + * 07/16/1995 JLB : Recognizes when sidebar is closed now. * + *=============================================================================================*/ +int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) const +{ + int x = ptr_x; + int y = ptr_y; + + /* + ** If radar is not active the click could have been on a radar point + */ + if (!IsRadarActive || !Map.IsSidebarActive) return(0); + + x -= (RadX + RadOffX); + y -= (RadY + RadOffY); + if (x < RadIWidth && y < RadIHeight) { + x -= BaseX; + y -= BaseY; + + if ((unsigned)x < RadarWidth + (ZoomFactor-1) && (unsigned)y < RadarHeight + (ZoomFactor-1)) { +// if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + x = RadarX + (x / ZoomFactor); + y = RadarY + (y / ZoomFactor); + if (change) { + ptr_x = x; + ptr_y = y; + } + return(1); + } + return(-1); + } + return(0); +} + + +/*********************************************************************************************** + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * * + * This routine will examine the pixel coordinate provided and determine what cell it * + * represents. If the radar map is not active or the coordinates are not positioned over * + * the radar map, then it will fall into the base class corresponding routine. * + * * + * INPUT: x,y -- The pixel coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that the coordinate is over or -1 if not over any * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Click_Cell_Calc(int x, int y) const +{ + int result = Click_In_Radar(x, y, true); + switch (result) { + case 1: + return(XY_Cell(x, y)); + + case -1: + return(-1); + + default: + break; + } + return(DisplayClass::Click_Cell_Calc(x, y)); +} + + +/*********************************************************************************************** + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * * + * This routine will update the radar map if a cell becomes mapped. * + * * + * INPUT: cell -- The cell that is being mapped. * + * * + * house -- The house that is doing the mapping. * + * * + * OUTPUT: bool; Was the cell mapped (for the first time) by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Map_Cell(CELL cell, HouseClass * house, bool check_radar_spied, bool and_for_allies) +{ + if (DisplayClass::Map_Cell(cell, house, check_radar_spied, and_for_allies)) { + Radar_Pixel(cell); + return(true); + } + return(false); +} + + + +void RadarClass::Cursor_Cell(CELL cell, int value) +{ + /* + ** If this cell is not on the radar don't bother doing anything. + */ + if (Cell_On_Radar(cell)) { + + int temp = (*this)[cell].IsRadarCursor; + + if (temp != value) { + + /* + ** Record the new state of this cell. + */ + (*this)[cell].IsRadarCursor = value; + + /* + ** If we are erasing then erase the cell. + */ + if (value == FALSE) { + Plot_Radar_Pixel(cell); + } + } + } +} + + +void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen) +{ + int x, y; + /* + ** First step is to convert pixel coordinates back to a CellX and CellY. + */ + x1 = RadarX + (x1 / ZoomFactor); + y1 = RadarY + (y1 / ZoomFactor); + x2 = RadarX + (x2 / ZoomFactor); + y2 = RadarY + (y2 / ZoomFactor); + + /* + ** Now we need to convert the Pixel length to a cell length. + */ + barlen = (barlen / ZoomFactor) + 1; + + /* + ** Now lets loop through and mark the map with the proper value. + */ + for (int lp = 0; lp <= barlen; lp++) { + /* + ** Do Horizontal action to upper and lower left corners. + */ + x = x1 + lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Horizontal Action to upper and lower right corners + */ + x = x2 - lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Vertical Action to left and right upper corners + */ + y = y1 + lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + + /* + ** Do Vertical action to left and right lower corners. + */ + y = y2 - lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + } +} + + + +/*********************************************************************************************** + * RadarClass::Cell_XY_To_Radar_Pixel-- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) +{ + x = (cellx - RadarX) * ZoomFactor; + y = (celly - RadarY) * ZoomFactor; +} + + +/*********************************************************************************************** + * RadarClass::Jam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Jam_Cell(CELL cell, HouseClass * house/*KO, bool shadeit*/) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Jammed |= jam; + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:00AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (house != PlayerPtr) Shroud_Cell(cell, PlayerPtr/*KO, shadeit*/); + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != house) { + Shroud_Cell(cell, player_house); + } + } + } + + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::UnJam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::UnJam_Cell(CELL cell, HouseClass * house) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Redraw_Objects(); + (*this)[cell].Jammed &= (0xFFFF - jam); + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +//#pragma argsused +void RadarClass::Radar_Cursor(int forced) +{ + static int _last_pos = -1; + static int _last_frame = -1; +#ifdef WIN32 + GraphicViewPortClass * oldpage; +#else + GraphicBufferClass * oldpage; +#endif + int x1, y1, x2, y2; + + /* + ** figure out these function calls as we will need to call them multiple times. + */ + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; + + /* + ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then + ** just skip the redraw process. + */ + if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; + + if ( _last_pos != -1 ) { + /* + ** The first thing we need to do is take care of erasing the last radar cell position. We do this + ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and + ** finally taking care of calling the erase procedure which will convert the pixel coordinates back + ** to the cells that need to be redraw. + **/ + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); + + Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the current coordinates based on the last animation frame. + */ + x1-= _last_frame; + y1-= _last_frame; + x2+= _last_frame; + y2+= _last_frame; + + /* + ** Finally mark the map (actually remove the marks that indicate the radar cursor was there + */ + Mark_Radar(x1, y1, x2, y2, FALSE, barlen); + } + + + /* + ** Find the upper left and lower right corners of the radar cursor. + ** Remember to adjust x2 and y2 back by one pixel as they will not be + ** pointing to the right value otherwise. They point one cell ahead + ** of where they should. + */ + Cell_XY_To_Radar_Pixel(tac_cell_x, tac_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(tac_cell_x + Lepton_To_Cell(TacLeptonWidth), tac_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the coordinates based on the current frame of radar animation. + */ + x1-= SpecialRadarFrame; + y1-= SpecialRadarFrame; + x2+= SpecialRadarFrame; + y2+= SpecialRadarFrame; + + Mark_Radar(x1, y1, x2, y2, TRUE, barlen); + + /* + ** setup a graphic view port class so we can write all the pixels relative + ** to 0,0 rather than relative to full screen coordinates. + */ + oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); + + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + + // Draw upper right hand corner + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + + // Draw lower left hand corner + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + + // Draw lower right hand corner + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); + + Set_Logic_Page(oldpage); + _last_pos = tac_cell; + _last_frame = SpecialRadarFrame; + RadarCursorRedraw = FALSE; +} + + +/*************************************************************************** + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Radar_Anim(void) +{ + /* + ** Do nothing if we're in player-name mode + */ + if (IsPlayerNames) + return; + + if (!Map.IsSidebarActive) return; + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage, + RadX + RadOffX, + RadY + RadOffY, + RadIWidth, + RadIHeight-2); +#endif +//Mono_Set_Cursor(0,0); +#ifdef WIN32 + Draw_Box(RadX+RadOffX-1, RadY+RadOffY-1, RadIWidth+2, RadIHeight+2, BOXSTYLE_RAISED, true); +#endif + draw_window.Clear(); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + Flag_To_Redraw(false); + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * RadarClass::AI -- Processes radar input (non-tactical). * + * * + * This routine intercepts any player input that concerns the radar map, but not those * + * areas that represent the tactical map. These are handled by the tactical map AI * + * processor. Primarily, this routine handles the little buttons that border the radar * + * map. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 12/26/1994 JLB : Moves tactical map with click or drag. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void RadarClass::AI(KeyNumType & input, int x, int y) +{ + /* + ** Check to see if we need to animate the radar cursor + */ + if (IsRadarActive && Map.IsSidebarActive && SpecialRadarFrame) { + SpecialRadarFrame--; + RadarCursorRedraw = TRUE; + IsToRedraw = TRUE; + Flag_To_Redraw(FALSE); + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarActivating) { + if (!DoesRadarExist) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + DoesRadarExist = true; + Radar_Activate(3); + } + } else { + RadarAnimFrame--; + if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + Radar_Activate(3); + } + } + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame == MAX_RADAR_FRAMES) { + IsRadarDeactivating = false; + } else { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + + /* + ** Check here to see if radar is being jammed, so we can update the + ** animation with snow. + */ + if (!IsRadarActivating && !IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + if (RadarAnimFrame > (3 + RADAR_ACTIVATED_FRAME)) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Check here to see if the sonar pulse is active, and if it is, flag the + ** radar to redraw so the pulse ping will display. + */ + if (IsPulseActive) { + Flag_To_Redraw(true); + IsToRedraw = true; + if (RadarPulseFrame >= 8) { + RadarPulseFrame = 0; + IsPulseActive = false; + } + } + + DisplayClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * * + * This is the main action function for handling player I/O on the radar map. It processes * + * mouse clicks as well as mouse moves. * + * * + * INPUT: flags -- The event flags that trigger this function call. * + * * + * key -- Reference the keyboard event that applies to the trigger event. * + * * + * OUTPUT: Should further processing of the input list be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) +{ + CELL cell; // cell num click happened over + int x,y; // Sub cell pixel coordinates. + int cellx,celly; // Sub cell pixel coordinates. + bool shadow; // is the cell in shadow or not + ObjectClass * object = 0; // what object is in the cell + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + if (Map.IsSidebarActive) { + Map.Help_Text(TXT_NONE); + } + + if (!Map.IsRadarActive) { + if (Map.IsSidebarActive) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); +// Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + return(false); + } + + /* + ** Disable processing if the player names are up + */ + if (Map.Is_Player_Names()) { + GadgetClass::Action(0, key); + return(true); + } + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + + /* + ** See if the mouse is over the radar general area, but not yet + ** over the active region of the radar map. In such a case, the + ** mouse is overridden to be the normal cursor and no other + ** action is performed. + */ + if (x < Map.RadX+Map.RadOffX || x >= Map.RadX+Map.RadIWidth || y < Map.RadY+Map.RadOffY || y >= Map.RadY+Map.RadIHeight) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); + return(false); + } + + int result = Map.RadarClass::Click_In_Radar(x, y, false); + + if (result == 1) { + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1 && Map.In_Radar(cell)) { + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); +// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + cellx = 12; + celly = 12; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Cell_Object(cell, cellx, celly); + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + + /* + ** If this is not a valid radar map action then we are not going to do + ** anything. + */ + switch (action) { + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; + + default: + action = ACTION_NONE; + object = NULL; + break; + } + + /* + ** On the radar map the only reason we would want the normal cursor to + ** appear is if we were over one of our own selected units. Otherwise + ** we can't move there. + **/ + if (action == ACTION_NONE) { + if (object && object->Is_Selected_By_Player()) { + object = NULL; + } else { + action = ACTION_NOMOVE; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (flags & LEFTUP) { + Map.Mouse_Left_Up(-1, shadow, object, action, true); + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTPRESS) { + Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); + } + + } else { + + Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); + + if (flags & LEFTPRESS) { + + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + int cellx = Cell_X(cell); + int celly = Cell_Y(cell); + cellx -= Lepton_To_Cell(Map.TacLeptonWidth) / 2; + cellx = max(cellx, Map.MapCellX); + celly -= Lepton_To_Cell(Map.TacLeptonHeight) / 2; + celly = max(celly, Map.MapCellY); + cell = XY_Cell(cellx, celly); + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); +// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + Map.Set_Tactical_Position(Cell_Coord(cell)); + cell = Coord_Cell(Map.DesiredTacticalCoord); + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(true); + Map.SpecialRadarFrame = 4; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Zoom_Mode(cell); + } + } + } + } + if (result == -1) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + GadgetClass::Action(0, key); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * * + * This routine intercepts the refresh cells request and if it detects that the sidebar * + * should be rerendered, it flags the radar map to redraw during the next draw operation. * + * * + * INPUT: cell -- The origin cell that the refresh cell offset list is based upon. * + * * + * list -- Pointer to the list of offsets from the origin cell that specifies the * + * cells to be flagged for redraw. If the list starts with the special * + * code to refresh the sidebar, then this routine recognizes it and flags * + * the radar map to be redrawn accordingly. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + DisplayClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell. * + * * + * This routine will try to center the radar map around the cell position specified. * + * * + * INPUT: cell -- The cell to try and position the radar map around. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Radar_Position(CELL cell) +{ +#ifdef WIN32 + int oldx, oldy; + int newx, newy; + int newcell; + + if (ZoomFactor != 1) { + oldx = (Cell_X(cell) - MapCellX); + oldy = (Cell_Y(cell) - MapCellY); + } else { + oldx = 0; + oldy = 0; + } + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); + + if (RadarCell != newcell) { + int forced = FALSE; + int xmod = newx; + int ymod = newy; + + int radx = (Cell_X(RadarCell)) - xmod; + int rady = (Cell_Y(RadarCell)) - ymod; + + RadarX = newx; + RadarY = newy; + RadarCell = newcell; + + if (Map.IsSidebarActive && Map.IsRadarActive) { + int radw = RadarCellWidth-ABS(radx); // Replicable width. + int radh = RadarCellHeight-ABS(rady); // Replicable height. + + if (radw < 1) forced = true; + if (radh < 1) forced = true; + + if (!forced && (radw != RadarWidth || radh != RadarHeight)) { + /* + ** Blit the section that is actually overlapping. + ** + ** If the video card isnt able to blit overlapped regions then we have + ** to do the blit in two stages via an intermediate buffer. The test to allow + ** overlapped blits is done in the library at the time of setting the video mode. + */ + if (OverlappedVideoBlits || !HidPage.Get_IsDirectDraw()) { + + /* + ** Overlapped blits are OK or we dont have a video memory hid page so blits are + ** always done in software by the library anyway. + */ + HidPage.Blit(HidPage, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + + } else { + /* + ** Create a temporary intermediate surface + */ + GraphicBufferClass temp_surface; + temp_surface.Init((RadarWidth + 16) & 0xfffffff0, + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum) GBC_VIDEOMEM); + + /* + ** Do the blit in 2 stages. + */ + HidPage.Blit(temp_surface, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); + + temp_surface.Blit(HidPage, + 0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + } + + /* + ** Now we need to flag the section of the map that is going to redraw. + */ + if ( radx != 0 ) { + int min; + int max; + if ( radx < 0 ) { // this mean regen the right edge + min = radw; + max = radw+ABS(radx); + } else { // this mean regen the left edge + min = 0; + max = radx; + } + for (int x = min; x < max; x++ ) { + for (int y = 0; y < (int)RadarCellHeight; y++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + if ( newy != 0 ) { + int min; + int max; + if ( rady < 0 ) { // this mean regen the bottom edge + min = radh; + max = radh+ABS(rady); + } else { // this mean regen the top edge + min = 0; + max = rady; + } + for (int y = min; y < max; y++ ) { + for ( int x = 0; x < (int)RadarCellWidth; x++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + } + } + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + if (ZoomFactor > 4) { + FullRedraw = forced; + } + } else { + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + } +#else + + if (cell != RadarCell) { + int oldx = RadarX; + int oldy = RadarY; + CELL oldcell = RadarCell; + int x = Cell_X(cell); + int y = Cell_Y(cell); + + /* + ** If the new radar position is not too close to the edge of the + ** current radar display, then don't bother to change the radar position. + */ + if ((unsigned)(x - (RadarX+10)) > RadarCellWidth-20) { + oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; + } else { + oldx = Cell_X(RadarCell)-MapCellX; + } + if ((unsigned)(y - (RadarY+10)) > RadarCellHeight-20) { + oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; + } else { + oldy = Cell_Y(RadarCell)-MapCellY; + } + +#ifdef NEVER + if ((unsigned)(x - (RadarX+10)) > RadarWidth-20 || (unsigned)(y - (RadarY+10)) > RadarHeight-20) { + oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; + oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; + } else { + oldx = Cell_X(RadarCell)-MapCellX; + oldy = Cell_Y(RadarCell)-MapCellY; + } +#endif + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + RadarX = oldx + MapCellX; + RadarY = oldy + MapCellY; + RadarCell = XY_Cell(RadarX, RadarY); + IsToRedraw = true; + Flag_To_Redraw(false); + if (oldcell != RadarCell) { + FullRedraw = IsRadarActive; + } + } +#endif +} + + +/*********************************************************************************************** + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * * + * This returns the cell number of the upper left corner of the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the radar map upper left corner cell position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Radar_Position(void) +{ + return(RadarCell); +} + + +/*********************************************************************************************** + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * * + * This routine is called when the tactical map changes its dimensions. This occurs when * + * the tactical map moves and when the sidebar pops on or off. * + * * + * INPUT: x,y -- The cell coordinate of the upper left corner of the tactical map. * + * * + * w,y -- The cell width and height of the tactical map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + Set_Radar_Position(XY_Cell(x, y)); + DisplayClass::Set_Map_Dimensions(x, y, w, h); +} + + +/*********************************************************************************************** + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to * + * * + * This routine is called when the tactical map is to change position. The radar map might * + * be adjusted as well by this routine. * + * * + * INPUT: coord -- The new coordinate to use for the upper left corner of the tactical * + * map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Tactical_Position(COORDINATE coord) +{ + DisplayClass::Set_Tactical_Position(coord); + Set_Radar_Position(Coord_Cell(TacticalCoord)); +} + + +/*********************************************************************************************** + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * * + * This routine will examine the specified cell number and return whether it is visible * + * on the radar map. This depends on the radar map position. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: Is the specified cell visible on the radar map currently? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Cell_On_Radar(CELL cell) +{ + if ((unsigned)cell > MAP_CELL_TOTAL) + return(false); + + if (!IsZoomed) { + return(true); + } + return(!(((Cell_X(cell) - RadarX) > RadarCellWidth) || ((Cell_Y(cell) - RadarY) > RadarCellHeight))); +} + + +/*********************************************************************************************** + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * * + * INPUT: * + * on true = turn on; false = turn off * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Player_Names(bool on) +{ + IsPlayerNames = on; + IsToRedraw = true; + if (on) { + Flag_To_Redraw(true); +// Flag_To_Redraw(false); + } else { + Flag_To_Redraw(true); // force drawing of the plate + } +} + + +/*********************************************************************************************** + * RadarClass::Spy_Next_House -- advances to the next house we're spying on, or returns NULL * + * * + * INPUT: * + * * + * OUTPUT: * + * 0 = no house to spy on, 1 = found house to spy on * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Spy_Next_House(void) +{ + bool tospy = false; + int spiedby = (1<<(PlayerPtr->Class->House)); + + IsPlayerNames = false; + IsToRedraw = true; + + HousesType maxhouse; + HousesType firsthouse; + HousesType house; + + if (Session.Type == GAME_NORMAL) { + firsthouse = HOUSE_SPAIN; + maxhouse = HOUSE_GOOD; + } else { + firsthouse = HOUSE_MULTI1; + maxhouse = HOUSE_COUNT; + } + + if (IsHouseSpy) { + house = (HousesType)(SpyingOn+1); + } else { + house = firsthouse; + } + + if (house < firsthouse) house = firsthouse; + + while (house < maxhouse && !tospy) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive && hptr != PlayerPtr) { + if (hptr->RadarSpied & spiedby) { + tospy = true; + SpyingOn = house; + break; + } + } + house++; + } + + IsHouseSpy = tospy; + + Flag_To_Redraw(true); // force drawing of the plate + return(tospy); +} + + +/*********************************************************************************************** + * Draw_House_Info -- Print house statistics on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Draw_House_Info(void) +{ + int y; + char txt[40]; + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return false; + } + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + y = RadY + RadOffY + (2*RESFACTOR); + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + Fancy_Text_Print (TXT_SPY_INFO, RadX + RadOffX + (6 * RESFACTOR), y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += 7*RESFACTOR; + + HouseClass * ptr = HouseClass::As_Pointer(SpyingOn); + if (ptr && (ptr->RadarSpied & (1<<(PlayerPtr->Class->House))) ) { + PlayerColorType c_idx; + RemapControlType * color; + TextPrintType style; + + c_idx = ptr->RemapColor; + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + + /* + ** Print house's name below 'spy report' + */ + txt[0] = 0; + sprintf(txt, "%s", ptr->IniName);//Text_String(ptr->Class->FullName)); +// sprintf(txt, "%s", ptr->Name());//Text_String(ptr->Class->FullName)); + if (strlen(txt)) { + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } + Fancy_Text_Print (txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + } else { + strcpy(txt,"________"); + } + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_BUILDNGS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + +// count & print buildings + itoa(ptr->CurBuildings, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_UNITS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print units + itoa(ptr->CurUnits, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + 6 * RESFACTOR, y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_INFANTRY, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print infantry + itoa(ptr->CurInfantry, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); +#if(0) + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_AIRCRAFT, RADAR_X + RADAR_OFF_X + 6, y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print aircraft + for (i = AIRCRAFT_NONE+1, count = 0; i < AIRCRAFT_COUNT; i++) { + count += ptr->AQuantity[i]; + } + itoa(count, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX, y, + color, BLACK, style); +#endif + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * Draw_Names -- draws players' names on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Draw_Names(void) +{ + PlayerColorType c_idx; + HousesType house; + HouseClass * ptr; + int y; + char txt[40]; + HousesType h; + int kills; + RemapControlType * color; + TextPrintType style; + + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return; + } + +// CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RADAR_X, RADAR_Y+1, +// WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + + y = RadY + RadOffY+(2*RESFACTOR); + + Fancy_Text_Print (TXT_NAME_COLON, RadX + RadOffX, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Fancy_Text_Print (TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW); + y += 6*RESFACTOR+1; + + LogicPage->Draw_Line(RadX + RadOffX, y, RadX + RadOffX + RadIWidth - 1, y, LTGREY); + y += 2*RESFACTOR; + + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr) continue; + + /* + ** Decode this house's color + */ + c_idx = ptr->RemapColor; + + if (ptr->IsDefeated) { + color = &GreyScheme; + style = TPF_6PT_GRAD | TPF_NOSHADOW; + } else { + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + } + + /* + ** Initialize our message + */ + txt[0] = 0; +// sprintf(txt, "%s", ptr->Name()); + sprintf(txt, "%s", ptr->IsHuman ? ptr->IniName : Text_String(TXT_COMPUTER)); + + if (strlen(txt) == 0) { + strcpy(txt,"________"); + } + + /* + ** Print the player name, and the # of kills + */ +#ifdef WIN32 + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } +#else + if (strlen(txt) > 8) { + txt[8] = '.'; + txt[9] = '\0'; + } +#endif + Fancy_Text_Print (txt, RadX + RadOffX, y, color, TBLACK, style); + + kills = 0; + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + kills += ptr->UnitsKilled[h]; + kills += ptr->BuildingsKilled[h]; + } + sprintf(txt, "%2d", kills); + Fancy_Text_Print (txt, RadX + RadOffX + RadIWidth - 2, y, color, TBLACK, style | TPF_RIGHT); + + y += 6*RESFACTOR+1; + + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); +} + + +void RadarClass::Activate_Pulse(void) +{ + if (IsRadarActive || PlayerPtr->IsGPSActive) { + IsPulseActive = true; + RadarPulseFrame = 0; + } +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * * + * Determines if the radar map is currently being displayed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map currently being displayed as active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Active(void) +{ + return(IsRadarActive || PlayerPtr->IsGPSActive); +// return IsRadarActive || PlayerPtr->IsGPSActive; +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Activating -- Determines if the radar map is being activated. * + * * + * Determines if the radar map is being activated. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map currently being activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/2019 SKY : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Activating(void) +{ + return IsRadarActivating; +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * * + * This will determine if the radar map is available. If available, the radar will show * + * representations of terrain, units, and buildings. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map available to be displayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Existing(void) +{ + return(DoesRadarExist || PlayerPtr->IsGPSActive); +} + + +/*********************************************************************************************** + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * * + * This will fetch the current state of the radar jamming for the player. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar currently jammed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Get_Jammed(HousesType house) const +{ + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* player = Houses.Ptr(i); + if (player && player->Class->House == house) { + return Get_Jammed(player); + } + } + return false; +} + +bool RadarClass::Get_Jammed(HouseClass *player) const +{ + assert(player); + if (player->IsGPSActive) return(false); + int shift = (int)player->Class->House; + return (IsRadarJammedByPlayerMask & (1 << shift)) ? true : false; +} + +void RadarClass::Set_Jammed(HousesType house, bool jam) +{ + int shift = (int)house; + if (jam) { + IsRadarJammedByPlayerMask |= (1 << shift); + } + else { + IsRadarJammedByPlayerMask &= ~(1 << shift); + } +} + +void RadarClass::Set_Jammed(HouseClass *player, bool jam) +{ + if (player && player->Class) { + Set_Jammed(player->Class->House, jam); + } +} + +void RadarClass::Flag_Cell(CELL cell) +{ +// Radar_Pixel(cell); + DisplayClass::Flag_Cell(cell); +} \ No newline at end of file diff --git a/REDALERT/RADAR.H b/REDALERT/RADAR.H new file mode 100644 index 000000000..47bb60335 --- /dev/null +++ b/REDALERT/RADAR.H @@ -0,0 +1,250 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADAR_H +#define RADAR_H + +#include "display.h" + +class RadarClass: public DisplayClass +{ + public: + RadarClass(void); + RadarClass(NoInitClass const & x) : DisplayClass(x) {}; + + /* + ** The dimensions and coordinates of the radar map. + */ + int RadX; + int RadOffX; + int RadY; + int RadOffY; + int RadWidth; + int RadHeight; + int RadIWidth; + int RadIHeight; + int RadPWidth; + int RadPHeight; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void Flag_Cell(CELL cell); + virtual bool Map_Cell(CELL cell, HouseClass *house, bool check_radar_spied = true, bool and_for_allies = true); // Added check_radar_spied parameter to prevent recursion. ST - 8/6/2019 10:16AM. Added and_for_allies ST - 10/31/2019 1:18PM + virtual bool Jam_Cell(CELL cell, HouseClass * house); + virtual bool UnJam_Cell(CELL cell, HouseClass * house); + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + virtual void Set_Tactical_Position(COORDINATE coord); + void Zoom_Mode(CELL cell); + int Click_In_Radar(int &x, int &y, bool change=false) const; + void Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y); + + bool Is_Zoomable(void) const; + void Set_Radar_Position(CELL cell); + CELL Radar_Position(void); + bool Radar_Activate(int control); + void Plot_Radar_Pixel(CELL cell); + void Radar_Pixel(CELL cell); + void Coord_To_Radar_Pixel(COORDINATE coord, int &x, int &y); + void Cursor_Cell(CELL cell, int value); + void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen); + void Radar_Cursor(int forced = false); + void Render_Terrain(CELL cell, int x, int y, int size); + bool Cell_On_Radar(CELL cell); + void Render_Infantry(CELL cell, int x, int y, int size); + void Render_Overlay(CELL cell, int x, int y, int size); + void Radar_Anim(void); + bool Is_Radar_Active(void); + bool Is_Radar_Activating(void); + bool Is_Radar_Existing(void); + + /* + ** Toggles player names on & off + */ + void Player_Names(bool on); + int Is_Player_Names(void) {return IsPlayerNames;} + bool Spying_On_House(void) {return IsHouseSpy;} + void Draw_Names(void); + bool Draw_House_Info(void); + int Is_Zoomed(void) {return IsZoomed;} + bool Get_Jammed(HousesType house) const; + bool Get_Jammed(HouseClass *player) const; + void Set_Jammed(HousesType house, bool jam); + void Set_Jammed(HouseClass *player, bool jam); + bool Spy_Next_House(void); + void Activate_Pulse(void); + + protected: + + /* + ** Radar map constant values. + */ + enum RadarClassEnums { + RADAR_ACTIVATED_FRAME=22, + MAX_RADAR_FRAMES = 41 + }; + + /* + ** If the radar map must be completely redrawn, then this flag will be true. + ** Typical causes of this would be when the radar first appears, or when the + ** screen has been damaged. + */ + unsigned IsToRedraw:1; + unsigned RadarCursorRedraw:1; + + /* + ** If the radar map is visible then this flag is true. + */ + unsigned DoesRadarExist:1; + unsigned IsRadarActive:1; + unsigned IsRadarActivating:1; + unsigned IsRadarDeactivating:1; + + /* + ** Per-player radar jammed flag. + */ + unsigned int IsRadarJammedByPlayerMask; + + /* + ** Flag to tell whether sonar pulse should be displayed on radar map + */ + unsigned IsPulseActive:1; + int RadarPulseFrame; + + /* + ** Special radar frame is set when a new location is selected on the + ** radar map. It counts down through the special radar cursors until + ** either the radar cursor becomes normal or the radar cursor is moved + ** again. + */ + int SpecialRadarFrame; + int RadarAnimFrame; + + static void const * RadarAnim; + static void const * RadarPulse; + static void const * RadarFrame; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class RTacticalClass : public GadgetClass { + public: + RTacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class RadarClass; + }; + friend class RTacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static RTacticalClass RadarButton; + + private: + + /* + ** The current radar position as the upper left corner cell for the + ** radar map display. The width and height is controlled by the + ** actual dimensions of the radar map display box (in pixels). + */ + unsigned RadarX; + unsigned RadarY; + unsigned RadarCellWidth; + unsigned RadarCellHeight; + unsigned RadarCell; + + /* + ** This is the origin (pixel offsets) for the upper left corner + ** of the radar map within the full radar map area of the screen. + ** This is biased so that the radar map, when smaller than full + ** size will appear centered. + */ + unsigned BaseX; + unsigned BaseY; + + unsigned RadarWidth; + unsigned RadarHeight; + + /* + ** If the radar map is in zoom mode, then this value will be true. + */ + unsigned IsZoomed:1; + + /* + ** This flag is true if the radar map is in its special show-the-player + ** names mode. + */ + unsigned IsPlayerNames:1; + + /* + ** This flag is true if the radar map is in its special show-the-units + ** of-another-house mode. + */ + unsigned IsHouseSpy:1; + + /* + ** This is the zoom factor to use. This value is the number of pixels wide + ** each cell will occupy on the radar map. Completely zoomed out would be a + ** value of 1. + */ + int ZoomFactor; + + /* + ** If we're spying on a house's radar facility, this field shows the + ** name of the house we're spying on. + */ + HousesType SpyingOn; + + /* + ** This is the list of radar pixels that need to be updated. Only a partial + ** list is maintained for maximum speed. + */ + unsigned PixelPtr; + enum PixelStackEnums {PIXELSTACK=400}; + CELL PixelStack[PIXELSTACK]; +}; + + +#endif diff --git a/REDALERT/RADIO.CPP b/REDALERT/RADIO.CPP new file mode 100644 index 000000000..8c8291072 --- /dev/null +++ b/REDALERT/RADIO.CPP @@ -0,0 +1,290 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADIO.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** These are the text representations of the radio messages that can be transmitted. +*/ +char const * RadioClass::Messages[RADIO_COUNT] = { + "static (no message)", + "Roger.", + "Come in.", + "Over and out.", + "Requesting transport.", + "Attach to transport.", + "I've got a delivery for you.", + "I'm performing load/unload maneuver. Be careful.", + "I'm clear.", + "You are clear to unload. Driving away now.", + "Am unable to comply.", + "I'm starting construction now... act busy.", + "I've finished construction. You are free.", + "We bumped, redraw yourself please.", + "I'm trying to load up now.", + "May I become a passenger?", + "Are you ready to receive shipment?", + "Are you trying to become a passenger?", + "Move to location X.", + "Do you need to move?", + "All right already. Now what?", + "I'm a passenger now.", + "Backup into refinery now.", + "Run away!", + "Tether established.", + "Tether broken.", + "Repair one step.", + "Are you prepared to fight?", + "Attack this target please.", + "Reload one step.", + "Circumstances prevent success.", + "All done with the request.", + "Do you need service depot work?", + "Are you sitting on service depot?" +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * * + * This displays the radio connection value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void RadioClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(29, 7);mono->Printf("0-%-47s", Messages[Old[0]]); + mono->Set_Cursor(29, 8);mono->Printf("1-%-47s", Messages[Old[1]]); + mono->Set_Cursor(29, 9);mono->Printf("2-%-47s", Messages[Old[2]]); + if (Radio) { + mono->Set_Cursor(20, 7);mono->Printf("%08X", Radio->As_Target()); + } + MissionClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * * + * This is the base version of what should happen when a radio message is received. It * + * turns the radio off when the "OVER_OUT" message is received. All other messages are * + * merely acknowledged with a "ROGER". * + * * + * INPUT: from -- The object that is initiating this radio message (always valid). * + * * + * message -- The radio message received. * + * * + * param -- Reference to optional value that might be used to return more * + * information than can be conveyed in the simple radio response * + * messages. * + * * + * OUTPUT: Returns with the response radio message. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 09/24/1994 JLB : Streamlined to be only a communications carrier. * + * 05/22/1995 JLB : Recognized who is sending the message * + * 06/05/1996 JLB : Radio message history tracking. * + *=============================================================================================*/ +RadioMessageType RadioClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + /* + ** Keep a record of the last message received by this radio. + */ + if (message != Old[0]) { + Old[2] = Old[1]; + Old[1] = Old[0]; + Old[0] = message; + } + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + ** This only applies if the message is coming from the object that + ** has an established conversation with this object. + */ + if (from == Radio && message == RADIO_OVER_OUT) { + MissionClass::Receive_Message(from, message, param); + Radio_Off(); + return(RADIO_ROGER); + } + + /* + ** The "hello" message is an attempt to establish contact. If this radio + ** is already in an established conversation with another object, then + ** return with "negative". If all is well, return with "roger". + */ + if (message == RADIO_HELLO && Strength) { + if (Radio == from || Radio == NULL) { + Radio = from; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(MissionClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * * + * This routine is used to transmit a radio message from this object to another. Most * + * inter object coordination is handled through this mechanism. * + * * + * INPUT: to -- Pointer to the object that will receive the radio message. * + * * + * message -- The message itself (see RadioType). * + * * + * param -- Optional reference to parameter that might be used to pass or * + * receive additional information. * + * * + * OUTPUT: Returns with the response radio message from the receiving object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, long & param, RadioClass * to) +{ + assert(IsActive); + + if (to == NULL) { + to = Contact_With_Whom(); + } + + /* + ** If there is no target for the radio message, then always return static. + */ + if (to == NULL) return(RADIO_STATIC); + + /* + ** Handle some special case processing that occurs when certain messages + ** are transmitted. + */ + if (to == Radio && message == RADIO_OVER_OUT) { + Radio = 0; + } + + /* + ** If this object is not in radio contact but the message + ** indicates that radio contact should be established, then + ** try to do so. If the other party agrees then contact + ** is established. + */ + if (message == RADIO_HELLO) { + Transmit_Message(RADIO_OVER_OUT); + if (to->Receive_Message(this, message, param) == RADIO_ROGER) { + Radio = to; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(to->Receive_Message(this, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * * + * This routine will break radio contact as the object is entering limbo state. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool RadioClass::Limbo(void) +{ + assert(IsActive); + + if (!IsInLimbo) { + Transmit_Message(RADIO_OVER_OUT); + } + return(MissionClass::Limbo()); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * * + * This routine will transmit the specified message to the object. This routine differs * + * from the normal Transmit_Message in that the LParam value is "faked" into the * + * parameter list. It is presumed that the message sent with this function does not * + * require the LParam. * + * * + * INPUT: message -- The message to transmit. * + * * + * to -- The requested receiver of this message. * + * * + * OUTPUT: Returns with the radio response from the receiver. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, RadioClass * to) +{ + assert(IsActive); + + return(Transmit_Message(message, LParam, to)); +} diff --git a/REDALERT/RADIO.H b/REDALERT/RADIO.H new file mode 100644 index 000000000..011d8c6a4 --- /dev/null +++ b/REDALERT/RADIO.H @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADIO_H +#define RADIO_H + +#include "mission.h" + +class ObjectClass; +class TechnoClass; + + +/**************************************************************************** +** Radio contact is controlled by this class. It handles the mundane chore +** of keeping the radio contact alive as well as broadcasting messages +** to the receiving radio. Radio contact is primarily used when one object +** is in "command" of another. +*/ +class RadioClass : public MissionClass { + private: + + /* + ** This is a record of the last message received by this receiver. + */ + RadioMessageType Old[3]; + + /* + ** This is the object that radio communication has been established + ** with. Although is is only a one-way reference, it is required that + ** the receiving radio is also tuned to the object that contains this + ** radio set. + */ + RadioClass * Radio; + + /* + ** This is a text representation of all the possible radio messages. This + ** text is used for monochrome debug printing. + */ + static char const * Messages[RADIO_COUNT]; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + RadioClass(RTTIType rtti, int id) : MissionClass(rtti, id), Radio(0) {}; + RadioClass(NoInitClass const & x) : MissionClass(x) {}; + virtual ~RadioClass(void) {Radio=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool In_Radio_Contact(void) const {return (Radio != 0);}; + void Radio_Off(void) {Radio = 0;}; + TechnoClass * Contact_With_Whom(void) const {return (TechnoClass *)Radio;}; + + // Inherited from base class(es). + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual RadioMessageType Transmit_Message(RadioMessageType message, long & param=LParam, RadioClass * to=NULL); + virtual RadioMessageType Transmit_Message(RadioMessageType message, RadioClass * to); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Limbo(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/REDALERT/RAMFILE.CPP b/REDALERT/RAMFILE.CPP new file mode 100644 index 000000000..7a6ff0e49 --- /dev/null +++ b/REDALERT/RAMFILE.CPP @@ -0,0 +1,472 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAMFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RAMFileClass::Close -- This will 'close' the ram file. * + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * RAMFileClass::Is_Open -- Is the file open? * + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * RAMFileClass::Open -- Opens the RAM based file. * + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * RAMFileClass::Read -- Read data from the file. * + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * RAMFileClass::Size -- Returns with the size of the ram file. * + * RAMFileClass::Write -- Copies data to the ram file. * + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "FUNCTION.H" +#include "ramfile.h" +#include + + +/*********************************************************************************************** + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * * + * This routine will construct a "file" object that actually is just a front end processor * + * for a buffer. Access to the buffer will appear as if it was accessing a file. This * + * is different from the caching ability of the buffered file class in that this file * + * class has no real file counterpart. Typical use of this is for algorithms that were * + * originally designed for file processing, but are now desired to work with a buffer. * + * * + * INPUT: buffer -- Pointer to the buffer to use for this file. The buffer will already * + * contain data if the file is opened for READ. It will be considered * + * a scratch buffer if opened for WRITE. If the buffer pointer is NULL * + * but the length parameter is not, then a buffer will be allocated * + * of the specified length. This case is only useful for opening the * + * file for WRITE. * + * * + * length -- The length of the buffer submitted to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::RAMFileClass(void * buffer, int len) : + Buffer((char *)buffer), + MaxLength(len), + Length(len), + Offset(0), + Access(READ), + IsOpen(false), + IsAllocated(false) +{ + if (buffer == NULL && len > 0) { + Buffer = new char[len]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * * + * The destructor will deallocate any buffer that it allocated. Otherwise it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::~RAMFileClass(void) +{ + Close(); + if (IsAllocated) { + delete [] Buffer; + Buffer = NULL; + IsAllocated = false; + } +} + + +/*********************************************************************************************** + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Create(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Delete(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * * + * RAM files are always available. * + * * + * INPUT: none * + * * + * OUTPUT: TRUE * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Available(int ) +{ + return(true); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Open -- Is the file open? * + * * + * This answers the question whether the file is open or not. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Open(void) const +{ + return(IsOpen); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * * + * This routine will open the ram file. The name is meaningless so that parameter is * + * ignored. If the access mode is for write, then the pseudo-file can be written until the * + * buffer is full. If the file is opened for read, then the buffer is presumed to be full * + * of the data to be read. * + * * + * INPUT: name -- ignored. * + * * + * access-- The access method to use for the data buffer -- either READ or WRITE. * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(char const * , int access) +{ + return(Open(access)); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens the RAM based file. * + * * + * This will open the ram based file for read or write. If the file is opened for write, * + * the the 'file' can be written up to the limit of the buffer's size. If the file is * + * opened for read, then the buffer is presumed to hold the data to be read. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(int access) +{ + if (Buffer == NULL || Is_Open()) { + return(false); + } + + Offset = 0; + Access = access; + IsOpen = true; + + switch (access) { + default: + case READ: + break; + + case WRITE: + Length = 0; + break; + + case READ|WRITE: + break; + } + + return(Is_Open()); +} + + +/*********************************************************************************************** + * RAMFileClass::Read -- Read data from the file. * + * * + * Use this routine just like a normal file read. It will copy the bytes from the ram * + * buffer to the destination specified. When the ram buffer is exhausted, less bytes than * + * requested will be read. * + * * + * INPUT: buffer -- Pointer to the buffer to store the data to. * + * * + * size -- The number of bytes to 'read' into the specified buffer. * + * * + * OUTPUT: Returns with the number of bytes copied to the destination buffer. If the number * + * of bytes returned is less than requested, then this indicates that the source * + * buffer is exhausted. * + * * + * WARNINGS: The read function only applies to ram 'files' opened for read access. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Read(void * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(READ); + hasopened = true; + } else { + if ((Access & READ) == 0) { + return(0); + } + } + + int tocopy = (size < (Length-Offset)) ? size : (Length-Offset); + memmove(buffer, &Buffer[Offset], tocopy); + Offset += tocopy; + + if (hasopened) { + Close(); + } + + return(tocopy); +} + + +/*********************************************************************************************** + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * * + * This routine will move the read/write position of the ram file to the location specified * + * by the offset and direction parameters. It functions similarly to the regular file * + * seek method. * + * * + * INPUT: pos -- The signed offset from the home position specified by the "dir" * + * parameter. * + * * + * dir -- The home position to base the position offset on. This will either be * + * the start of the file, the end of the file, or the current read/write * + * position. * + * * + * OUTPUT: Returns with the new file position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Seek(long pos, int dir) +{ + if (Buffer == NULL || !Is_Open()) { + return(Offset); + } + + int maxoffset = Length; + if ((Access & WRITE) != 0) { + maxoffset = MaxLength; + } + + switch (dir) { + case SEEK_CUR: + Offset += pos; + break; + + case SEEK_SET: + Offset = 0 + pos; + break; + + case SEEK_END: + Offset = maxoffset + pos; + break; + } + + if (Offset < 0) Offset = 0; + if (Offset > maxoffset) Offset = maxoffset; + + if (Offset > Length) { + Length = Offset; + } + + return(Offset); +} + + +/*********************************************************************************************** + * RAMFileClass::Size -- Returns with the size of the ram file. * + * * + * This will return the size of the 'real' data in the ram file. The real data is either * + * the entire buffer, if opened for READ, or just the written data if opened for WRITE. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the ram file system considers to be valid * + * data of the 'file'. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Size(void) +{ + return(Length); +} + + +/*********************************************************************************************** + * RAMFileClass::Write -- Copies data to the ram file. * + * * + * This function similarly to the regular write operation supported for files. It copies * + * the data specified to the current write position in the ram file. * + * * + * INPUT: buffer -- Pointer to the data to be written. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the actual number of bytes written. This will be less than requested * + * if the buffer is exhausted of space prematurely. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Write(void const * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(WRITE); + hasopened = true; + } else { + if ((Access & WRITE) == 0) { + return(0); + } + } + + int maxwrite = MaxLength - Offset; + int towrite = (size < maxwrite) ? size : maxwrite; + memmove(&Buffer[Offset], buffer, towrite); + Offset += towrite; + + if (Offset > Length) { + Length = Offset; + } + + if (hasopened) { + Close(); + } + + return(towrite); +} + + +/*********************************************************************************************** + * RAMFileClass::Close -- This will 'close' the ram file. * + * * + * Closing a ram file actually does nothing but record that it is now closed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void RAMFileClass::Close(void) +{ + IsOpen = false; +} diff --git a/REDALERT/RAMFILE.H b/REDALERT/RAMFILE.H new file mode 100644 index 000000000..b05efdcbb --- /dev/null +++ b/REDALERT/RAMFILE.H @@ -0,0 +1,110 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAMFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAMFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RAMFILE_H +#define RAMFILE_H + + +#include "wwfile.h" + + +class RAMFileClass : public FileClass +{ + public: + RAMFileClass(void * buffer, int len); + virtual ~RAMFileClass(void); + + virtual char const * File_Name(void) const {return("UNKNOWN");} + virtual char const * Set_Name(char const * ) {return(File_Name());} + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int access=READ); + virtual int Open(int access=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(true);} + virtual void Error(int , int = false, char const * =NULL) {} + + operator char const * () {return File_Name();} + + private: + + /* + ** Pointer to the buffer that the "file" will reside in. + */ + char * Buffer; + + /* + ** The maximum size of the buffer. The file occupying the buffer + ** may be smaller than this size. + */ + int MaxLength; + + /* + ** The number of bytes in the sub-file occupying the buffer. + */ + int Length; + + /* + ** The current file position offset within the buffer. + */ + int Offset; + + /* + ** The file was opened with this access mode. + */ + int Access; + + /* + ** Is the file currently open? + */ + bool IsOpen; + + /* + ** Was the file buffer allocated during construction of this object? + */ + bool IsAllocated; +}; + + + + +#endif diff --git a/REDALERT/RAND.CPP b/REDALERT/RAND.CPP new file mode 100644 index 000000000..d46bfddbe --- /dev/null +++ b/REDALERT/RAND.CPP @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\rand.cpv 4.35 04 Jul 1996 16:14:56 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/25/95 * + * * + * Last Update : June 17, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Sim_IRandom -- Returns minval to maxval, inclusive * + * Sim_Random -- Returns 0 - 255 * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "COORDA.h" +int SimRandIndex = 0; + +/*************************************************************************** + * Sim_Random -- Returns 0 - 255 * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 - 255, at random * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Takes advantage of character math wrap. * + *=========================================================================*/ +int Sim_Random(void) +{ + static unsigned char _randvals[] = { + 0x47, 0xce, 0xc6, 0x6e, 0xd7, 0x9f, 0x98, 0x29, 0x92, 0x0c, 0x74, 0xa2, + 0x65, 0x20, 0x4b, 0x4f, 0x1e, 0xed, 0x3a, 0xdf, 0xa5, 0x7d, 0xb5, 0xc8, + 0x86, 0x01, 0x81, 0xca, 0xf1, 0x17, 0xd6, 0x23, 0xe1, 0xbd, 0x0e, 0xe4, + 0x62, 0xfa, 0xd9, 0x5c, 0x68, 0xf5, 0x7f, 0xdc, 0xe7, 0xb9, 0xc4, 0xb3, + 0x7a, 0xd8, 0x06, 0x3e, 0xeb, 0x09, 0x1a, 0x31, 0x3f, 0x46, 0x28, 0x12, + 0xf0, 0x10, 0x84, 0x76, 0x3b, 0xc5, 0x53, 0x18, 0x14, 0x73, 0x7e, 0x59, + 0x48, 0x93, 0xaa, 0x1d, 0x5d, 0x79, 0x24, 0x61, 0x1b, 0xfd, 0x2b, 0xa8, + 0xc2, 0xdb, 0xe8, 0x2a, 0xb0, 0x25, 0x95, 0xab, 0x96, 0x83, 0xfc, 0x5f, + 0x9c, 0x32, 0x78, 0x9a, 0x9e, 0xe2, 0x8e, 0x35, 0x4c, 0x41, 0xa1, 0x69, + 0x5a, 0xfe, 0xa7, 0xa4, 0xf6, 0x6d, 0xc1, 0x58, 0x0a, 0xcf, 0xea, 0xc3, + 0xba, 0x85, 0x99, 0x8d, 0x36, 0xb6, 0xdd, 0xd3, 0x04, 0xe6, 0x45, 0x0d, + 0x60, 0xae, 0xa3, 0x22, 0x4d, 0xe9, 0xc9, 0x9b, 0xb7, 0x0f, 0x02, 0x42, + 0xf9, 0x0b, 0x8f, 0x43, 0x44, 0x87, 0x70, 0xbe, 0xe3, 0xf8, 0xee, 0xa9, + 0xbc, 0xc0, 0x67, 0x33, 0x16, 0x37, 0x57, 0xad, 0x5e, 0x9d, 0x64, 0x40, + 0x54, 0x05, 0x2c, 0xe0, 0xb2, 0x97, 0x08, 0xaf, 0x75, 0x8a, 0x5b, 0xfb, + 0x4e, 0xbf, 0x91, 0xf3, 0xcb, 0x7c, 0x63, 0xef, 0x89, 0x52, 0x6c, 0x2f, + 0x21, 0x4a, 0xf7, 0xcd, 0x2e, 0xf4, 0xc7, 0x6f, 0x19, 0xb1, 0x66, 0xcc, + 0x90, 0x8c, 0x50, 0x51, 0x26, 0x7b, 0xda, 0x49, 0x80, 0x30, 0x55, 0x1f, + 0xd2, 0xb4, 0xd1, 0xd5, 0x6b, 0xf2, 0x72, 0xbb, 0x13, 0x3d, 0xff, 0x15, + 0x38, 0xe5, 0xd4, 0xde, 0x2d, 0x27, 0x94, 0xa0, 0xd0, 0x39, 0x82, 0x8b, + 0x03, 0xac, 0x3c, 0x34, 0x77, 0xb8, 0xec, 0x00, 0x07, 0x1c, 0x88, 0xa6, + 0x56, 0x11, 0x71, 0x6a, + }; + + ((unsigned char&)SimRandIndex)++; +// SimRandIndex &= 0xff; + return(_randvals[SimRandIndex]); +} + + +/*************************************************************************** + * Sim_IRandom -- returns minval to maxval, inclusive * + * * + * INPUT: * + * minval,maxval range limits * + * * + * OUTPUT: * + * random value * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Uses fixed point math helper routine. * + *=========================================================================*/ +int Sim_IRandom(int minval, int maxval) +{ + return(Fixed_To_Cardinal((maxval-minval), Sim_Random()) + minval); +} + + +/* +** This routine can be used to create the _RandVals[] table. It will be +** different every time it's run. +*/ +#if 0 +#include +#include +#include +#include + +void main(void); + +void main(void) +{ + int i; + FILE *fp; + int r; + char is_used[256]; + char rand_val[256]; + + srand ( time ( NULL ) ) ; + + for (i = 0; i < 256; i++) { + is_used[i] = 0; + } + + /* + ** Store the digits 0 to 255 in a random order within the 'rand_val' array + ** This ensures our random number sequence represents every value. + */ + for (i = 0; i < 256; i++) { + if (kbhit()!=0) { + printf("ABORTED!\n"); + break; + } + r = (rand() * 256) / RAND_MAX; // r is the index into array + if (is_used[r]==0) { + is_used[r] = 1; + rand_val[r] = i; + } else { + i--; + } + } + + fp = fopen("rand.cpp","wt"); + if (fp==NULL) { + printf("File error\n"); + exit(0); + } + + fprintf(fp, "unsigned char _RandVals[] = {\n"); + for (i = 0; i < 256; i++) { + fprintf(fp,"0x%02x,\n",rand_val[i]); + } + fprintf(fp,"};\n"); + fclose(fp); + +} + +#endif + diff --git a/REDALERT/RANDOM.CPP b/REDALERT/RANDOM.CPP new file mode 100644 index 000000000..337c41237 --- /dev/null +++ b/REDALERT/RANDOM.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RANDOM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/28/96 * + * * + * Last Update : February 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomClass::RandomClass -- Constructor for the random number class. * + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * RandomClass::operator() -- Ranged random number generator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "random.h" +#ifdef RANDOM_COUNT +#include +extern long Frame; +#endif + +/*********************************************************************************************** + * RandomClass::RandomClass -- Constructor for the random number class. * + * * + * This constructor can take an integer as a parameter. This allows the class to be * + * constructed by assigning an integer to an existing object. The compiler creates a * + * temporary with the constructor and then performs a copy constructor operation. * + * * + * INPUT: seed -- The optional starting seed value to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +RandomClass::RandomClass(unsigned seed) : + Seed(seed) +{ +#ifdef RANDOM_COUNT + Count1 = 0; + Count2 = 0; +#endif +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * * + * This routine will fetch the next random number in the sequence. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next random number. * + * * + * WARNINGS: This routine modifies the seed value so that subsequent calls will not return * + * the same value. Take note that this routine only returns 15 bits of * + * random number. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator ()(void) +{ +#ifdef RANDOM_COUNT + Count1++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Transform the seed value into the next number in the sequence. + */ + Seed = (Seed * MULT_CONSTANT) + ADD_CONSTANT; + + /* + ** Extract the 'random' bits from the seed and return that value as the + ** random number result. + */ + return((Seed >> THROW_AWAY_BITS) & (~((~0) << SIGNIFICANT_BITS))); +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Ranged random number generator. * + * * + * This function will return with a random number within the range specified. This replaces * + * the functionality of IRandom() in the old library. * + * * + * INPUT: minval -- The minimum value to return from the function. * + * * + * maxval -- The maximum value to return from the function. * + * * + * OUTPUT: Returns with a random number that falls between the minval and maxval (inclusive). * + * * + * WARNINGS: The range specified must fall within the maximum bit significance of the * + * random number algorithm (15 bits), otherwise the value returned will be * + * decidedly non-random. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator() (int minval, int maxval) +{ +#ifdef RANDOM_COUNT + Count2++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Test for shortcut case where the range is null and thus + ** the number to return is actually implicit from the + ** parameters. + */ + if (minval == maxval) return(minval); + + /* + ** Ensure that the min and max range values are in proper order. + */ + if (minval > maxval) { + int temp = minval; + minval = maxval; + maxval = temp; + } + + /* + ** Find the highest bit that fits within the magnitude of the + ** range of random numbers desired. Notice that the scan is + ** limited to the range of significant bits returned by the + ** random number algorithm. + */ + int magnitude = maxval - minval; + int highbit = SIGNIFICANT_BITS-1; + while ((magnitude & (1 << highbit)) == 0 && highbit > 0) { + highbit--; + } + + /* + ** Create a full bit mask pattern that has all bits set that just + ** barely covers the magnitude of the number range desired. + */ + int mask = ~( (~0L) << (highbit+1) ); + + /* + ** Keep picking random numbers until it fits within the magnitude desired. + */ + int pick = magnitude+1; + while (pick > magnitude) { + pick = operator()() & mask; + } + + /* + ** Finally, bias the random number pick to the start of the range + ** requested. + */ + return(pick + minval); +} + diff --git a/REDALERT/RANDOM.H b/REDALERT/RANDOM.H new file mode 100644 index 000000000..f13c5420d --- /dev/null +++ b/REDALERT/RANDOM.H @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RANDOM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RANDOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/96 * + * * + * Last Update : February 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RANDOM_H +#define RANDOM_H + +/********************************************************************** +** Enable this define to turn on the random # counters. This must not +** be defined for the release version, or saved games won't work! +*/ +//#define RANDOM_COUNT + + +/* +** This class functions like a 'magic' int value that returns a random number +** every time it is read. To set the "random seed" for this, just assign a number +** to the object (just as you would if it were a true 'int' value). Take note that although +** this class will return an 'int', the actual significance of the random number is +** limited to 15 bits (0..32767). +*/ +class RandomClass { + public: + RandomClass(unsigned seed=0); + + operator int(void) {return(operator()());}; + int operator() (void); + int operator() (int minval, int maxval); + + enum { + SIGNIFICANT_BITS=15 // Random number bit significance. + }; + + unsigned long Seed; + +#ifdef RANDOM_COUNT + unsigned long Count1; + unsigned long Count2; +#endif + + protected: + /* + ** Internal working constants that are used to generate the next + ** random number. + */ + enum { + MULT_CONSTANT=0x41C64E6D, // K multiplier value. + ADD_CONSTANT=0x00003039, // K additive value. + THROW_AWAY_BITS=10 // Low bits to throw away. + }; +}; + +#endif diff --git a/REDALERT/RAWFILE.CPP b/REDALERT/RAWFILE.CPP new file mode 100644 index 000000000..8e9629d5f --- /dev/null +++ b/REDALERT/RAWFILE.CPP @@ -0,0 +1,1345 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include + +#include "rawfile.h" + +#ifndef WIN32 +#include +#include +#include +extern short Hard_Error_Occured; +#endif + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Error(int , int , char const * ) +{ +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const * filename) : + Rights(0), + BiasStart(0), + BiasLength(-1), + Handle(NULL_HANDLE), + Filename(filename), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const * filename) +{ + if (Filename != NULL && Allocated) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (filename == NULL) return(NULL); + + Bias(0); + + ((char *&)Filename) = strdup(filename); + if (Filename == NULL) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (Filename == NULL) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetitively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ + #ifndef WIN32 + Hard_Error_Occured = 0; + #endif + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + #endif + break; + + case WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_creat(Filename, 0, &Handle); + #endif + break; + + case READ|WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); + #endif + break; + } + + /* + ** Biased files must be positioned past the bias start position. + */ + if (BiasStart != 0 || BiasLength != -1) { + Seek(0, SEEK_SET); + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == NULL_HANDLE) { + +#ifdef WIN32 +// return(false); + Error(GetLastError(), false, Filename); +// continue; +#else + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } + continue; +#endif + } + break; + } + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ +#ifndef WIN32 + bool open_failed; +#endif + + if (Filename == NULL) return(false); + + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +#ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (Handle == NULL_HANDLE) { + return(false); + } + break; +#else + + Hard_Error_Occured = 0; + open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } + } + if (!open_failed) break; +#endif + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ +#ifdef WIN32 + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + if (_dos_close(Handle)) { + Error(errno, false, Filename); + } +#endif + Handle = NULL_HANDLE; + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + +#ifdef WIN32 + /* + ** Try to close the file. If there was an error (who knows what that could be), then + ** call the error routine. + */ + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ + Hard_Error_Occured = 0; + if (_dos_close(Handle)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + break; + } + +#endif + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = NULL_HANDLE; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void * buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** A biased file has the requested read length limited to the bias length of + ** the file. + */ + if (BiasLength != -1) { + int remainder = BiasLength - Seek(0); + size = size < remainder ? size : remainder; + } + +#ifdef WIN32 + long total = 0; + while (size > 0) { + bytesread = 0; + + SetErrorMode(SEM_FAILCRITICALERRORS); + if (!ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + size -= bytesread; + total += bytesread; + Error(GetLastError(), true, Filename); + SetErrorMode(0); + continue; + } + SetErrorMode(0); + size -= bytesread; + total += bytesread; + if (bytesread == 0) break; + } + bytesread = total; + +#else + + int readresult; + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size < 32000L ? size : 32000L; + + Hard_Error_Occured = 0; + readresult = _dos_read(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + } + } + } +#endif //WIN32 + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const * buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + +#ifdef WIN32 + if (!WriteFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + Error(GetLastError(), false, Filename); + } + +#else + + int writeresult; + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + + Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = _dos_write(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } + } + } +#endif //WIN32 + + /* + ** Fixup the bias length if necessary. + */ + if (BiasLength != -1) { + if (Raw_Seek(0) > BiasStart+BiasLength) { + BiasLength = Raw_Seek(0) - BiasStart; + } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + + /* + ** A file that is biased will have a seek operation modified so that the file appears to + ** exist only within the bias range. All bytes outside of this range appear to be + ** non-existant. + */ + if (BiasLength != -1) { + switch (dir) { + case SEEK_SET: + if (pos > BiasLength) { + pos = BiasLength; + } + pos += BiasStart; + break; + + case SEEK_CUR: + break; + + case SEEK_END: + dir = SEEK_SET; + pos += BiasStart + BiasLength; +// pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; +// pos = (pos >= BiasStart) ? pos : BiasStart; + break; + } + + /* + ** Perform the modified raw seek into the file. + */ + long newpos = Raw_Seek(pos, dir) - BiasStart; + + /* + ** Perform a final double check to make sure the file position fits with the bias range. + */ + if (newpos < 0) { + newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; + } + if (newpos > BiasLength) { + newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; + } + return(newpos); + } + + /* + ** If the file is not biased in any fashion, then the normal seek logic will + ** work just fine. + */ + return(Raw_Seek(pos, dir)); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + + /* + ** A biased file already has its length determined. + */ + if (BiasLength != -1) { + return(BiasLength); + } + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + +#ifdef WIN32 + size = GetFileSize(Handle, NULL); + + /* + ** If there was in internal error, then call the error function. + */ + if (size == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { + Hard_Error_Occured = 0; + size = filelength(Handle); + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (size == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + + BiasLength = size-BiasStart; + return(BiasLength); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + + /* + ** A biased file must be at least as long as the bias offset. Seeking to the + ** appropriate start offset has the effect of lengthening the file to the + ** correct length. + */ + if (BiasLength != -1) { + Seek(0, SEEK_SET); + } + + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +#ifdef WIN32 + if (!DeleteFile(Filename)) { + Error(GetLastError(), false, Filename); + return(false); + } +#else + Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } +#endif + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win32 method. * + *=============================================================================================*/ +unsigned long RawFileClass::Get_Date_Time(void) +{ +#ifdef WIN32 + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + WORD dosdate; + WORD dostime; + FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); + return((dosdate << 16) | dostime); + } + return(0); +#else + unsigned short time; + unsigned short date; + unsigned long datetime = 0; + + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + if ( _dos_getftime( Handle, &date, &time ) ) { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + if ( _dos_getftime( Handle, &date, &time ) ) { + RawFileClass::Close(); + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + + RawFileClass::Close(); + } else { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } + + // + // combine the date and time as a long + // + datetime = (date << 16) + time; + + return( datetime ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win 32 method * + *=============================================================================================*/ +bool RawFileClass::Set_Date_Time(unsigned long datetime) +{ +#ifdef WIN32 + if (RawFileClass::Is_Open()) { + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + FILETIME filetime; + if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { + return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime)); + } + } + } + return(false); +#else + unsigned short time; + unsigned short date; + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + // + // only set the date and time if open for READ only + // + if ( Rights == READ ) { + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + // + // return true indicating success + // + return( true ); + } + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + RawFileClass::Close(); + // + // return true indicating success + // + return( true ); + } + + RawFileClass::Close(); + } + } + + // + // return false indicating error + // + return( false ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * * + * This will bias a file by giving it an artificial starting position and length. By * + * using this routine, it is possible to 'fool' the file into ignoring a header and * + * trailing extra data. An example of this would be a file inside of a mixfile. * + * * + * INPUT: start -- The starting offset that will now be considered the start of the * + * file. * + * * + * length -- The forced length of the file. For files that are opened for write, * + * this serves as the artificial constraint on the file's length. For * + * files opened for read, this limits the usable file size. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1996 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Bias(int start, int length) +{ + if (start == 0) { + BiasStart = 0; + BiasLength = -1; + return; + } + + BiasLength = RawFileClass::Size(); + BiasStart += start; + if (length != -1) { + BiasLength = BiasLength < length ? BiasLength : length; + } + BiasLength = BiasLength > 0 ? BiasLength : 0; + + /* + ** Move the current file offset to a legal position if necessary and the + ** file was open. + */ + if (Is_Open()) { + RawFileClass::Seek(0, SEEK_SET); + } +} + + +/*********************************************************************************************** + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * * + * This will perform a seek on the file as if it were unbiased. This is in spite of any * + * bias setting the file may have. The ability to perform a raw seek in this fasion is * + * necessary to maintain the bias ability. * + * * + * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * + * * + * dir -- The origin of the seek operation. * + * * + * OUTPUT: Returns with the new position of the seek operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Raw_Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + +#ifdef WIN32 + switch (dir) { + case SEEK_SET: + dir = FILE_BEGIN; + break; + + case SEEK_CUR: + dir = FILE_CURRENT; + break; + + case SEEK_END: + dir = FILE_END; + break; + } + + pos = SetFilePointer(Handle, pos, NULL, dir); + + /* + ** If there was an error in the seek, then bail with an error condition. + */ + if (pos == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ + Hard_Error_Occured = 0; + pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} diff --git a/REDALERT/RAWFILE.H b/REDALERT/RAWFILE.H new file mode 100644 index 000000000..e33b535b7 --- /dev/null +++ b/REDALERT/RAWFILE.H @@ -0,0 +1,332 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_Hx +#define RAWFILE_Hx + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +#define NULL_HANDLE INVALID_HANDLE_VALUE +#define HANDLE_TYPE HANDLE +#else +#define NULL_HANDLE -1 +#define HANDLE_TYPE int +#endif + +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +#ifndef WWERROR +#define WWERROR -1 +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void); + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + void Bias(int start, int length=-1); + + HANDLE_TYPE Get_File_Handle(void) { return (Handle); }; + + /* + ** These bias values enable a sub-portion of a file to appear as if it + ** were the whole file. This comes in very handy for multi-part files such as + ** mixfiles. + */ + int BiasStart; + int BiasLength; + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + long Raw_Seek(long pos, int dir=SEEK_CUR); + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + HANDLE_TYPE Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + // + // file date and time are in the following formats: + // + // date bits 0-4 day (0-31) + // bits 5-8 month (1-12) + // bits 9-15 year (0-119 representing 1980-2099) + // + // time bits 0-4 second/2 (0-29) + // bits 5-10 minutes (0-59) + // bits 11-15 hours (0-23) + // + unsigned short Date; + unsigned short Time; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : + Rights(READ), + BiasStart(0), + BiasLength(-1), + #ifdef WIN32 + Handle(INVALID_HANDLE_VALUE), + #else + Handle(-1), + #endif + Filename(0), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::~RawFileClass(void) +{ + Close(); + if (Allocated && Filename) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } +} + + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ +#ifdef WIN32 + return(Handle != INVALID_HANDLE_VALUE); +#else + return (Handle >= 0); +#endif +} + +#endif diff --git a/REDALERT/RAWOLAPI.CPP b/REDALERT/RAWOLAPI.CPP new file mode 100644 index 000000000..a62a1f702 --- /dev/null +++ b/REDALERT/RAWOLAPI.CPP @@ -0,0 +1,2099 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// rawolapi.cpp - Core WOLAPI interface functions stuff. +// Definitions for RAChatEventSink, RADownloadEventSink, RANetUtilEventSink. +// ajw 07/10/98 + +#include "RAWolapi.h" +#define IID_DEFINED +#include "wolapi\wolapi_i.c" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "Wol_gsup.h" +#include "wolapi\netutildefs.h" + +#include "WolDebug.h" + +bool operator<( const User& u1, const User& u2 ); + +const char* Game_Registry_Key(); + +// The definitions of QueryInterface, AddRef, and Release are needed because we are not including +// files that ordinarily (under MSVC) would define these for us, as part of CComObjectRoot, I believe. +// This Watcom has no equivalent we can use, so we do it manually... + +//*********************************************************************************************** +RAChatEventSink::RAChatEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + bRequestServerListWait( false ), + pOwner( pOwnerIn ), + pServer( NULL ), + bConnected( false ), + hresRequestConnectionError( 0 ), + pChannelList( NULL ), + pUserList( NULL ), + pUserTail( NULL ), + szMotd( NULL ), + bJoined( false ), + bGotKickedTrigger( false ), + bIgnoreChannelLists( false ), + bRequestChannelListForLobbiesWait( false ), + pGameUserList( NULL ), + bRequestGameStartWait( false ), + pUserIPList( NULL ), + pUserIPListTail( NULL ), + iGameID( 0 ) +{ +} + +//*********************************************************************************************** +RAChatEventSink::~RAChatEventSink() +{ +// debugprint( "RAChatEventSink destructor\n" ); + delete pServer; + delete [] szMotd; + DeleteChannelList(); + DeleteUserList(); + DeleteUserIPList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RAChatEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RAChatEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_IChatEvent)) + { + *ppv = (IChatEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RAChatEventSink::AddRef() +{ +// debugprint( "RAChatEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RAChatEventSink::Release() +{ +// debugprint( "RAChatEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerList( HRESULT hRes, Server* pServerHead ) +{ + //strcpy( szLadderServerHost, "games.westwood.com" ); + //iLadderServerPort = 3840; + //strcpy( szGameResServerHost, "games.westwood.com" ); + +// debugprint( ">>> OnServerList got: %i ", hRes ); + DebugChatDef( hRes ); + + if( pServer ) + { + delete pServer; + pServer = NULL; + } + + if( SUCCEEDED( hRes ) ) + { + while( pServerHead ) + { + // Copy the first IRC Server to use in the RequestConnection() call. + if( !pServer && ( strcmp( (char*)pServerHead->connlabel, "IRC" ) == 0 ) ) + { + pServer = new Server; + *pServer = *pServerHead; + } + else if( !*pOwner->szLadderServerHost && ( strcmp( (char*)pServerHead->connlabel, "LAD" ) == 0 ) ) + { +// debugprint( "Scanning '%s'\n", (char*)pServerHead->conndata ); + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szLadderServerHost, token ); + token = strtok( NULL, ";" ); + pOwner->iLadderServerPort = atoi( token ); +// debugprint( "Ladder is at: %s, port %i\n", pOwner->szLadderServerHost, pOwner->iLadderServerPort ); + } + else if( !*pOwner->szGameResServerHost1 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Red Alert game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost1, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort1 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + else if( !*pOwner->szGameResServerHost2 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Aftermath game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost2, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort2 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + pServerHead = pServerHead->next; + } + } + + bRequestServerListWait = false; + return(S_OK); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPageSend( HRESULT hRes ) +{ +// debugprint( ">>> OnPageSend got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_PAGE_NOTHERE && hRes != CHAT_S_PAGE_OFF && hRes != S_OK ) + hRes = E_FAIL; + + hresRequestPageResult = hRes; + + bRequestPageWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPaged( HRESULT, User* pUser, LPCSTR szMessage ) +{ +// debugprint( ">>> OnPaged got: %s ", szMessage ); + + char* szPrint = new char[ strlen( (char*)pUser->name ) + strlen( szMessage ) + strlen( TXT_WOL_ONPAGE ) ]; + sprintf( szPrint, TXT_WOL_ONPAGE, (char*)pUser->name, szMessage ); + if( !pOwner->bInGame ) + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PAGE ); + else + { + Session.Messages.Add_Message( NULL, 0, szPrint, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + if( !pOwner->bFreezeExternalPager ) + strcpy( pOwner->szExternalPager, (char*)pUser->name ); + Map.Flag_To_Redraw(true); + } + + delete [] szPrint; + + Sound_Effect( WOLSOUND_ONPAGE ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnFind( HRESULT hRes, Channel* pChannel ) +{ +// debugprint( ">>> OnFind got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_FIND_NOTHERE && hRes != CHAT_S_FIND_NOCHAN && hRes != CHAT_S_FIND_OFF && hRes != S_OK ) + hRes = E_FAIL; + + if( hRes == S_OK ) + OnFindChannel = *pChannel; + + hresRequestFindResult = hRes; + + bRequestFindWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnLogout( HRESULT hRes, User* pUser ) +{ +// debugprint( ">>> OnLogout got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone has been logged out by the chat server due to inactivity. + // Fake a call to OnChannelLeave(), as the processing is identical. +// debugprint( "OnLogout calling OnChannelLeave for %s, owner=%i\n", (char*)pUser->name, ( pUser->flags & CHAT_USER_CHANNELOWNER ) ); + OnChannelLeave( S_OK, NULL, pUser ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnBusy(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnIdle(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnConnection( HRESULT hRes, LPCSTR motd ) +{ +// debugprint( ">>> OnConnection got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + + // Prepare a new string for a modified version of motd. + szMotd = new char[ strlen( motd ) + 1 ]; + // Replace single line breaks with a space. + // Replace double line breaks with double carriage returns. + + bool bJustDidBreak = false; + const char* szIn = motd; + char* szOut = szMotd; + + while( *szIn ) + { + if( *szIn == '\r' && *( szIn + 1 ) == '\n' ) + { + if( !bJustDidBreak ) + { + *szOut++ = ' '; + bJustDidBreak = true; + } + else + { + szOut--; + *szOut++ = '\r'; + *szOut++ = '\r'; + bJustDidBreak = false; +// debugprint( "^" ); + } + szIn += 2; + } + else + { + *szOut++ = *szIn++; + bJustDidBreak = false; + } +// debugprint( "%c", *( szOut - 1 ) ); + } + *szOut = 0; // Null-terminate. +// debugprint( "\n" ); + +// pOwner->PrintMessage( szMotd ); + + bConnected = true; + } + else + { + hresRequestConnectionError = hRes; + + char szError[150]; + ChatDefAsText( szError, hRes ); + strcat( szError, " (Connect Error)" ); +// debugprint( szError ); + } + + bRequestConnectionWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelCreate( HRESULT hRes, Channel* ) +{ +// debugprint( ">>> OnChannelCreate got: %i ", hRes ); + DebugChatDef( hRes ); + +// if( bJoined ) +// { +// WWMessageBox().Process( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// Fatal( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// } + + if( SUCCEEDED( hRes ) ) + { + bJoined = true; + } + bRequestChannelCreateWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelModify(HRESULT, Channel *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelJoin( HRESULT hRes, Channel* /*pChannel*/, User* pUser ) +{ +// if( SUCCEEDED( hRes ) ) +// debugprint( ">>> OnChannelJoin got: channel '%s', user '%s', %i ", (char*)pChannel->name, (char*)pUser->name, hRes ); +// else +// debugprint( ">>> OnChannelJoin got: %i ", hRes ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelJoin when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring join.\n" ); +// return S_OK; +// } + + hresRequestJoinResult = hRes; + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = true; + bRequestChannelJoinWait = false; + if( pUserList ) + { + // Should never happen. +// debugprint( "pUserList should be NULL (as I am the joiner)!!! Deleting user list...\n" ); + DeleteUserList(); + } + } + else + { + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We can ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + // Add user to our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelJoin - ignoring %s join... \n", (char*)pUser->name ); + return S_OK; + } +// if( !pUserList ) +// { +// // There has to be at least one user there - you. +// debugprint( "pUserList is null in OnChannelJoin!!! users: %s\n", (char*)pUser->name ); +// Fatal( "pUserList is null in OnChannelJoin!!!\n" ); +// } + + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + + // Insert user into list alphabetically. + InsertUserSorted( pUserNew ); + + // Update the shown list. + pOwner->ListChannelUsers(); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + _ASSERTE( pOwner->pGSupDlg ); + pOwner->pGSupDlg->OnGuestJoin( pUser ); + + // Ask for this player's IP address. + pOwner->RequestIPs( (char*)pUser->name ); + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL || pOwner->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Request ladder results for new user. + pOwner->RequestLadders( (char*)pUser->name ); + } + } + } + else + { + bRequestChannelJoinWait = false; + } + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::InsertUserSorted( User* pUserNew ) +{ + if( !pUserList ) + { + pUserList = pUserNew; + pUserTail = pUserNew; + } + else + { + if( *pUserNew < *pUserList ) + { + // Insert user at beginning. + pUserNew->next = pUserList; + pUserList = pUserNew; + } + else + { + User* pUserCheck = pUserList; + User* pUserInsertAfter = NULL; + while( pUserCheck->next ) + { + if( *pUserNew < *pUserCheck->next ) + { + pUserInsertAfter = pUserCheck; + break; + } + pUserCheck = pUserCheck->next; + } + if( pUserInsertAfter ) + { + pUserNew->next = pUserInsertAfter->next; + pUserInsertAfter->next = pUserNew; + } + else + { + // Add user to end. + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + } + } +} + +//*********************************************************************************************** +bool operator<( const User& u1, const User& u2 ) +{ + if( u1.flags & CHAT_USER_CHANNELOWNER && !( u2.flags & CHAT_USER_CHANNELOWNER ) ) + return true; + if( !( u1.flags & CHAT_USER_CHANNELOWNER ) && u2.flags & CHAT_USER_CHANNELOWNER ) + return false; + if( u1.flags & CHAT_USER_VOICE && !( u2.flags & CHAT_USER_VOICE ) ) + return true; + if( !( u1.flags & CHAT_USER_VOICE ) && u2.flags & CHAT_USER_VOICE ) + return false; + return ( _stricmp( (char*)u1.name, (char*)u2.name ) < 0 ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelLeave( HRESULT hRes, Channel*, User* pUser ) +{ + // Note: This is also called directly from OnUserKick(), below, when someone is kicked from a channel. + // Also now from OnLogout(). + +// debugprint( ">>> OnChannelLeave got %s: ", (char*)pUser->name ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelLeave when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring leave.\n" ); +// return S_OK; +// } + + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = false; + bRequestChannelLeaveWait = false; + } + else + { + // Remove user from our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelLeave - ignoring %s leave... \n", (char*)pUser->name ); + return S_OK; + } + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + User* pUserSearch = pUserList; + User* pUserPrevious = NULL; + bool bFound = false; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Remove from list. + if( !pUserPrevious ) + { + // Head of list is being removed. + pUserList = pUserSearch->next; + if( !pUserList ) + { + // This means all entries were removed. Can't happen, as you are still there. +// debugprint( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + Fatal( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + } + } + else + { + pUserPrevious->next = pUserSearch->next; + if( !pUserPrevious->next ) + pUserTail = pUserPrevious; // New list tail. + } + // Destroy removed user. + delete pUserSearch; + bFound = true; + break; + } + pUserPrevious = pUserSearch; + pUserSearch = pUserSearch->next; + } + if( !bFound ) + { + // User has to be found. This should not happen. +// debugprint( "User not found for removal in OnChannelLeave!!!\n" ); + return S_OK; + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + // Note that the following is done before removing the user from the playerlist. + char* szPrint = new char[ strlen( TXT_WOL_PLAYERLEFTGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERLEFTGAME, (char*)pUser->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + pOwner->pGSupDlg->OnGuestLeave( pUser ); + } + + // Update the shown list. + pOwner->ListChannelUsers(); + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelTopic(HRESULT, Channel *, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGroupList(HRESULT, Group *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicMessage( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + 16 ]; + sprintf( szPrint, "%s!", (char*)pUserSender->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s: %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PUBLICMESSAGE ); + delete [] szPrint; + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateMessage( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char ci1[] = "VGhpcyBpcyBBZGFtLiBIYXZlIHdlIG5vdCBwZXJjaGFuY2UgbWV0IGJlZm9yZT8="; + char co1[48]; + Base64_Decode( ci1, strlen( ci1 ), co1, 47 ); + co1[47] = 0; + if( strcmp( szMessage, co1 ) == 0 ) + { + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szOut[60]; + char ci2[] = "SSBhbSB5b3VyIGFibGUgYW5kIHdpbGxpbmcgc2xhdmUu"; + char co2[34]; + Base64_Decode( ci2, strlen( ci2 ), co2, 33 ); + co2[33] = 0; + sprintf( szOut, "%s (%i/%i/%i)", co2, SysTime.wMonth, SysTime.wDay, SysTime.wYear ); + User UserReply; + UserReply = *pUserSender; + UserReply.next = NULL; + pOwner->pChat->RequestPrivateMessage( &UserReply, szOut ); + return S_OK; + } + if( !bSpecialMessage( szMessage ) ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s%s: %s", (char*)pUserSender->name, TXT_WOL_PRIVATE, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PRIVATEMESSAGE ); + Sound_Effect( VOC_INCOMING_MESSAGE ); + delete [] szPrint; + } + } + else + { + char* szOut = new char[ strlen( szMessage ) + 10 ]; + strcpy( szOut, &szMessage[8] ); + pOwner->pChat->RequestPublicMessage( szOut ); + char* szPrint = new char[ strlen( szOut ) + strlen( pOwner->szMyName ) + 10 ]; + sprintf( szPrint, "%s: %s", pOwner->szMyName, szOut ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + delete [] szPrint; + delete [] szOut; + } + } + return S_OK; +} + +//*********************************************************************************************** +bool RAChatEventSink::bSpecialMessage( const char* szMessage ) +{ + if( strlen( szMessage ) < 9 ) + return false; + if( szMessage[0] != 33 || szMessage[1] != 97 || szMessage[2] != 106 || szMessage[3] != 119 ) + return false; + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szCode[5]; + memcpy( (void*)szCode, (void*)&szMessage[4], 4 ); + szCode[4] = 0; + int iCode = atoi( szCode ); + return ( iCode == ( ( SysTime.wMonth * 99 ^ SysTime.wDay * 33 ) ^ SysTime.wYear ) ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnSystemMessage(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnNetStatus( HRESULT hRes ) +{ +// debugprint( ">>> OnNetStatus got: " ); + DebugChatDef( hRes ); + + if( !SUCCEEDED( hRes ) ) + { + // If we are waiting for a server list, this error might indicate that we're not going to + // get one, so bail out of waiting for it. + bRequestServerListWait = false; + // Same for logout. + bRequestLogoutWait = false; + } + + if( hRes == CHAT_S_CON_DISCONNECTED ) + { + if( bRequestLogoutWait || !bConnected ) + { + // Ok. We are waiting to logout or already have. + bRequestLogoutWait = false; + } + else + { + // Uh oh. We got disconnected unexpectedly. + if( pOwner->bInGame ) + // Set flag for wolapi destruction if connection is lost during game. + pOwner->bConnectionDown = true; + else + { + if( !pOwner->bSelfDestruct ) + { + // Set flag for wolapi destruction. + WWMessageBox().Process( TXT_WOL_WOLAPIGONE ); + pOwner->bSelfDestruct = true; + } + } + } + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelList( HRESULT, Channel* pChannelListIn ) +{ + if( bIgnoreChannelLists ) // Response to channel lists has been temporarily turned off. + { +// debugprint( ">>> IGNORED OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + return S_OK; + } + + // Special case for modal GetLobbyChannels(). Because we want to be sure this OnChannelList is one that was caused + // by a Request for gametype 0, and not one arriving from an earlier Request for games. + // This OnChannelList might not actually match the Request in GetLobbyChannels(), but as long as it's type 0 it'll do. + if( bRequestChannelListForLobbiesWait ) + { + if( pChannelListIn && pChannelListIn->type != 0 ) + { +// debugprint( ">>> IGNORED OnChannelList, bRequestChannelListForLobbiesWait if\n" ); + return S_OK; + } + // Note: if no channels in list, can't tell what kind of Request call gave us this list. + // (In our case assume it was the one asking for lobbies and allow to fail later naturally due to no lobbies available.) + } + + DeleteChannelList(); +// debugprint( ">>> OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + + int iLobbyCur = iChannelLobbyNumber( (unsigned char*)pOwner->szChannelNameCurrent ); + + Channel* pChannelListTail = NULL; + + // Copy channel list to our own list. + while( pChannelListIn ) + { +// debugprint( "OnChannelList got %s\n", pChannelListIn->name ); + switch( ChannelFilter ) + { + case CHANNELFILTER_OFFICIAL: + if( pChannelListIn->official != 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_UNOFFICIAL: + if( pChannelListIn->official == 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_LOBBIES: + { + int iLobby = iChannelLobbyNumber( pChannelListIn->name ); + if( iLobby == -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + } + case CHANNELFILTER_LOCALLOBBYGAMES: + // We are listing games of our type, and may have to filter out non-local-lobby games. + if( !pOwner->bAllGamesShown ) + { + int iGameSourceLobby = pChannelListIn->reserved & 0x00FFFFFF; + if( iLobbyCur == -1 || iGameSourceLobby != iLobbyCur ) + { + pChannelListIn = pChannelListIn->next; + continue; + } + } + break; + } + Channel* pChannelNew = new Channel; + *pChannelNew = *pChannelListIn; + pChannelNew->next = NULL; // (We don't want the value that was just copied!) + if( !pChannelListTail ) + { + // First channel in list. + pChannelList = pChannelNew; // This is the head of our channel list. + pChannelListTail = pChannelNew; + } + else + { + pChannelListTail->next = pChannelNew; + pChannelListTail = pChannelNew; + } + pChannelListIn = pChannelListIn->next; + } + +// bRequestChannelListWait = false; + + if( bRequestChannelListForLobbiesWait ) + bRequestChannelListForLobbiesWait = false; + else + pOwner->OnChannelList(); + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteChannelList() +{ + // Delete all channels allocated on the heap. + // pChannelList points to the head element of a linked list of channels, copied during OnChannelList(). +// debugprint( "DeleteChannelList\n" ); + while( pChannelList ) + { + Channel* pChannelHead = pChannelList; + pChannelList = pChannelHead->next; + delete pChannelHead; + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserList(HRESULT, Channel*, User* pUserListIn ) +{ + // Maintenance of users list is like that for channels list. +// debugprint( ">>> OnUserList\n" ); + DeleteUserList(); + + // Copy channel list to our own list. + while( pUserListIn ) + { +// debugprint( "OnUserList got %s\n", pUserListIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserListIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserList = pUserNew; // This is the head of our User list. + pUserTail = pUserNew; + } + else + { + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + pUserListIn = pUserListIn->next; + } + +// bRequestUserListWait = false; + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserList() +{ + // Delete all Users allocated on the heap. + // pUserList points to the head element of a linked list of Users, copied during OnUserList(). +// debugprint( "DeleteUserList\n" ); + while( pUserList ) + { + User* pUserHead = pUserList; + pUserList = pUserHead->next; + delete pUserHead; + } + pUserTail = NULL; +} + + +//*********************************************************************************************** +// We got a list of updates to apply +// +STDMETHODIMP RAChatEventSink::OnUpdateList( HRESULT hRes, Update* pUpdateList ) +{ +// debugprint( ">>> OnUpdateList got: " ); + DebugChatDef( hRes ); + + if( !pUpdateList ) // Shouldn't happen. + return S_OK; + + // Count the updates. + int iUpdates = 0; + Update* pUpdate = pUpdateList; + + while( pUpdate != NULL ) + { + pUpdate = pUpdate->next; + ++iUpdates; + } +// debugprint( "%i updates\n", iUpdates ); + + if( WWMessageBox().Process( TXT_WOL_PATCHQUESTION, TXT_YES, TXT_NO ) == 0 ) + { + // Get the updates. (I ignore the concept of "optional" downloads here.) + if( DownloadUpdates( pUpdateList, iUpdates ) ) + pOwner->hresPatchResults = PATCHDOWNLOADED; + else + pOwner->hresPatchResults = PATCHAVOIDED; + } + else + // User says don't do the download. + // Set flag to tell WolapiObject what has happened. + pOwner->hresPatchResults = PATCHAVOIDED; + + return S_OK; +} + +extern bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ); +//*********************************************************************************************** +bool RAChatEventSink::DownloadUpdates( Update* pUpdateList, int iUpdates ) +{ + // First we create a Download and Download Sink interface object, like Chat and ChatSink. + bool bReturn = true; + // This is all like WolapiObject::bSetupCOMStuff(). +//debugprint( "Do all the COM stuff.\n" ); + IDownload* pDownload; + CoCreateInstance( CLSID_Download, NULL, CLSCTX_INPROC_SERVER, IID_IDownload, (void**)&pDownload ); + _ASSERTE( pDownload ); + RADownloadEventSink* pDownloadSink = new RADownloadEventSink(); + pDownloadSink->AddRef(); + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + HRESULT hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + DWORD dwDownloadAdvise; + hRes = pConnectionPoint->Advise( (IDownloadEvent*)pDownloadSink, &dwDownloadAdvise ); + _ASSERTE(SUCCEEDED(hRes)); + // Presumably the above calls will succeed, because they did so when we did bSetupComStuff(). + + pContainer->Release(); + pConnectionPoint->Release(); + + Update* pUpdate = pUpdateList; + int iUpdateCurrent = 0; + // Save current directory. + char szCurDirSave[_MAX_PATH]; + ::GetCurrentDirectory( _MAX_PATH, szCurDirSave ); + while( pUpdate ) + { + ++iUpdateCurrent; + char szTitle[ 120 ]; + sprintf( szTitle, TXT_WOL_DOWNLOADING, iUpdateCurrent, iUpdates ); + char fullpath[ _MAX_PATH ]; + sprintf( fullpath, "%s\\%s", pUpdate->patchpath, pUpdate->patchfile ); + // Downloading in WOLAPI is in a state of disarray somewhat. + // Make sure the destination directory exists, and make it the current directory during the download. +//debugprint( "Switching to %s dir.\n", (char*)pUpdate->localpath ); + if( !::SetCurrentDirectory( (char*)pUpdate->localpath ) ) + { + // Create the destination directory. +// debugprint( "Creating dir.\n" ); + ::CreateDirectory( (char*)pUpdate->localpath, NULL ); + ::SetCurrentDirectory( (char*)pUpdate->localpath ); + } + // Note: Unknown what the reg key value is actually used for... +//debugprint( "Asking to download %s to %s. Server '%s', login '%s', password '%s'\n", fullpath, (char*)pUpdate->patchfile, +// (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password ); + pDownload->DownloadFile( (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password, fullpath, + (char*)pUpdate->patchfile, Game_Registry_Key() ); +// debugprint( "Call WOL_Download_Dialog()\n" ); + if( !WOL_Download_Dialog( pDownload, pDownloadSink, szTitle ) ) + { + bReturn = false; + break; + } + pUpdate = pUpdate->next; + } + ::SetCurrentDirectory( szCurDirSave ); + + // Undo all the COM stuff. +//debugprint( "Undo all the COM stuff.\n" ); + pConnectionPoint = NULL; + pContainer = NULL; + hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + pConnectionPoint->Unadvise( dwDownloadAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pDownload->Release(); + pDownloadSink->Release(); // This results in pDownloadSink deleting itself for us. + + return bReturn; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError( HRESULT hRes ) +{ +// debugprint( ">>> OnServerError got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnMessageOfTheDay(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::ActionEggSound( const char* szMessage ) +{ + // Easter egg related. + if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) ) + { + int i = rand() % 30; + if( i == 0 ) + Sound_Effect( VOC_DOG_HURT ); + else if( i == 1 ) + Sound_Effect( VOC_ANTDIE ); + else + Sound_Effect( (VocType)( VOC_SCREAM1 + rand() % 9 ) ); + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) + || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_WHINE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 6 ) + { + case 0: Sound_Effect( VOC_CANNON1 ); break; + case 1: Sound_Effect( VOC_CANNON2 ); break; + case 2: Sound_Effect( VOC_GUN_RIFLE ); break; + case 3: Sound_Effect( VOC_SILENCER ); break; + case 4: Sound_Effect( VOC_CANNON6 ); break; + case 5: Sound_Effect( VOC_CANNON8 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_KABOOM1 ); break; + case 1: Sound_Effect( VOC_KABOOM12 ); break; + case 2: Sound_Effect( VOC_KABOOM15 ); break; + case 3: Sound_Effect( VOC_KABOOM30 ); break; + case 4: Sound_Effect( VOC_KABOOM25 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 8 ) + { + case 0: Sound_Effect( VOC_E_AH ); break; + case 1: Sound_Effect( VOC_E_YES ); break; + case 2: Sound_Effect( VOC_THIEF_YEA ); break; + case 3: Sound_Effect( VOC_SPY_YESSIR ); break; + case 4: Sound_Effect( VOC_SPY_INDEED ); break; + case 5: Sound_Effect( VOC_ENG_YES ); break; + case 6: Sound_Effect( VOC_MED_YESSIR ); break; + case 7: Sound_Effect( VOC_MED_AFFIRM ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_E_OK ); + else if( strstr( szMessage, "<>" )|| strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_SPY_ONWAY ); break; + case 1: Sound_Effect( VOC_ENG_MOVEOUT ); break; + case 2: Sound_Effect( VOC_SPY_KING ); break; + case 3: Sound_Effect( VOC_MED_MOVEOUT ); break; + case 4: Sound_Effect( VOC_THIEF_MOVEOUT ); break; + } + } + else if( strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_SPLASH ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_POWER_UP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_ZAP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TORPEDO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_SUBSHOW ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_BARK ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_GROWL2 ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CHRONO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CRUMBLE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CASHTURN ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_HEAL ); + else if( strstr( szMessage, "<>" ) ) + { + switch( rand() % 3 ) + { + case 0: Sound_Effect( VOC_MISSILE_1 ); break; + case 1: Sound_Effect( VOC_MISSILE_2 ); break; + case 2: Sound_Effect( VOC_MISSILE_3 ); break; + } + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateAction( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATE, (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicAction( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateGameOptions( HRESULT, User* pUser, LPCSTR szRequest ) +{ +// debugprint( ">>> OnPrivateGameOptions\n" ); +// DebugChatDef( hRes ); + + char szRequestCopy[ 600 ]; + strcpy( szRequestCopy, szRequest ); + + if( pOwner->pGSupDlg ) + { + if( pOwner->pGSupDlg->bHost ) + pOwner->pGSupDlg->ProcessGuestRequest( pUser, szRequestCopy ); + else + pOwner->pGSupDlg->ProcessInform( szRequestCopy ); // Must be private message to guest from game host. + } +// else +// debugprint( "OnPrivateGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicGameOptions( HRESULT, Channel*, User*, LPCSTR szInform ) +{ +// debugprint( ">>> OnPublicGameOptions: %s\n", szInform ); + + char szInformCopy[ 600 ]; + strcpy( szInformCopy, szInform ); + + if( pOwner->pGSupDlg ) + { + pOwner->pGSupDlg->ProcessInform( szInformCopy ); + } +// else +// debugprint( "OnPublicGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGameStart( HRESULT hRes, Channel*, User* pUserIn, int iGameID ) +{ + // Note: All players receive this, not just the host that requested it. + +// debugprint( ">>> OnGameStart got: " ); + DebugChatDef( hRes ); + +// if( bRequestGameStartWait ) // Implies user is the host that did RequestGameStart(). +// { + + // Create the list of users that are actually involved in a game. + // Most likely will always match pUserList, but there is a chance of someone leaving or joining + // at the wrong moment, so from this point on, the pGameUserList is used. + + // Note: pUserIPList was added later, for pre-start pinging. It duplicates pGameUserList ip information, + // strictly speaking. + + // Delete any existing list. + while( pGameUserList ) + { + User* pGameUserHead = pGameUserList; + pGameUserList = pGameUserList->next; + delete pGameUserHead; + } + // Copy incoming user list. + User* pGameUserListTail = NULL; + while( pUserIn ) + { +// debugprint( "OnGameStart got %s\n", (char*)pUserIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pGameUserListTail ) + { + // First User in list. + pGameUserList = pUserNew; // This is the head of our User list. + pGameUserListTail = pUserNew; + } + else + { + pGameUserListTail->next = pUserNew; + pGameUserListTail = pUserNew; + } + pUserIn = pUserIn->next; + } + + bRequestGameStartWait = false; +// } + +// debugprint( "iGameID is %i\n", iGameID ); + this->iGameID = iGameID; + + return S_OK; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetPlayerGameIP( const char* szPlayerName ) const +{ + // Returns ipaddr value of player if found in pGameUserList, else 0. + User* pUser = pGameUserList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szPlayerName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserKick( HRESULT hRes, Channel*, User* pUserKicked, User* pUserKicker ) +{ +// debugprint( ">>> OnUserKick got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone was kicked. + // Fake a call to OnChannelLeave(), as the processing is identical. + OnChannelLeave( S_OK, NULL, pUserKicked ); + if( pUserKicked->flags & CHAT_USER_MYSELF ) + { + // Trigger a channel exit later on, when we have left this callback. + bGotKickedTrigger = true; + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( TXT_WOL_USERKICKEDYOU ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDYOU, (char*)pUserKicker->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + // Ensure that the bGotKickedTrigger is acted upon immediately... + pOwner->dwTimeNextWolapiPump = ::timeGetTime(); + } + else + { + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( (char*)pUserKicked->name ) + + strlen( TXT_WOL_USERKICKEDUSER ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDUSER, (char*)pUserKicker->name, (char*)pUserKicked->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + switch( rand() % 4 ) + { + case 0: + Sound_Effect( VOC_TANYA_CHEW ); + break; + case 1: + Sound_Effect( VOC_TANYA_LAUGH ); + break; + case 2: + Sound_Effect( VOC_TANYA_CHING ); + break; + case 3: + Sound_Effect( VOC_TANYA_KISS ); + break; + } + } + else + { + // You tried to kick someone, but the user wasn't found. + // Ignore. +// debugprint( "OnUserKick non S_OK value\n" ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserIP( HRESULT hRes, User* pUser ) +{ + // A list of users is kept, separate from other user lists, to preserve the ipaddr's we've found through this + // callback. OnUserList (for some dumb reason) doesn't hold valid ipaddr's, so we have to go through + // all this rigamarole... + // (List is cleared when entering game channel. Users are added initially and on joins, not removed on leaves.) +// debugprint( ">>> OnUserIP got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { + // Look for user in our current users list. + User* pUserSearch = pUserIPList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Found matching user. Replace it's ipaddr value, in case it changed.(?) + pUserSearch->ipaddr = pUser->ipaddr; + return S_OK; + } + pUserSearch = pUserSearch->next; + } + // User not found in current list. Add. + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserIPListTail ) + { + // First user in list. + pUserIPList = pUserNew; // This is the head of our list. + pUserIPListTail = pUserNew; + } + else + { + pUserIPListTail->next = pUserNew; + pUserIPListTail = pUserNew; + } + } + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserIPList() +{ + // Same as DeleteUserList but for pUserIPList. +// debugprint( "DeleteUserIPList\n" ); + while( pUserIPList ) + { + User* pUserHead = pUserIPList; + pUserIPList = pUserHead->next; + delete pUserHead; + } + pUserIPListTail = NULL; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetUserIP( const char* szName ) const +{ + // Looks in pUserIPList for the ipaddr of user with name szName. + // This is used only while in game channels. + // This is for step 2 in acquiring fellow player ping times. To get the IP addresses into pUserIPList + // we had to go through request/callbacks. Now pings are requested on these addresses, and the results + // tallied in NetUtilSink for our retrieval later. + // Returns 0 if not found. + + // Find szName in list. + User* pUser = pUserIPList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError(HRESULT , LPCSTR ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerBannedYou(HRESULT , time_t ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserFlags( HRESULT hRes, LPCSTR name, unsigned int flags, unsigned int ) +{ +// debugprint( ">>> OnUserFlags got: " ); + DebugChatDef( hRes ); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring OnUserFlags because game is starting.\n" ); // (Shouldn't ever happen.) + return S_OK; + } + } + + // Find user in our current users list. + User* pUserPrior = NULL; + User* pUserSearch = pUserList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, name ) == 0 ) + { + // Set user's flags to new value. + pUserSearch->flags = flags; + + // Remove user from userlist and reinsert appropriately sorted. + if( !pUserPrior ) + { + // User was head of list. + pUserList = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was also tail of list. + pUserTail = NULL; + else + pUserSearch->next = NULL; + } + else + { + // User was not head of list. + pUserPrior->next = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was tail of list. + pUserTail = pUserPrior; + else + pUserSearch->next = NULL; + } + InsertUserSorted( pUserSearch ); + + // Update shown list. + pOwner->ListChannelUsers(); + break; + } + pUserPrior = pUserSearch; + pUserSearch = pUserSearch->next; + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelBan( HRESULT , LPCSTR name, int banned ) +{ + if( banned && strcmp( name, "*" ) != 0 ) + { + char* szPrint = new char[ strlen( name ) + strlen( TXT_WOL_USERWASBANNED ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERWASBANNED, name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + + return S_OK; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RADownloadEventSink::RADownloadEventSink() : + bFlagEnd( false ), + bFlagError( false ), + bFlagProgressUpdate( false ), + bFlagStatusUpdate( false ), + bFlagQueryResume( false ) +{ + m_cRef=0; // Ref counter +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RADownloadEventSink::QueryInterface(const IID& iid, void** ppv) +{ + if ((iid == IID_IUnknown) ||(iid == IID_IDownloadEvent)) + { + *ppv = (IDownloadEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RADownloadEventSink::AddRef() +{ + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RADownloadEventSink::Release() +{ + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnEnd(void) +{ +// debugprint( ">>> OnEnd\n" ); + bFlagEnd = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnError( int /*iCode*/ ) +{ +// debugprint( ">>> OnError got: %i\n", iCode ); +//#define DOWNLOADEVENT_NOSUCHSERVER 1 +//#define DOWNLOADEVENT_COULDNOTCONNECT 2 +//#define DOWNLOADEVENT_LOGINFAILED 3 +//#define DOWNLOADEVENT_NOSUCHFILE 4 +//#define DOWNLOADEVENT_LOCALFILEOPENFAILED 5 +//#define DOWNLOADEVENT_TCPERROR 6 +//#define DOWNLOADEVENT_DISCONNECTERROR 7 + + bFlagError = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnProgressUpdate( int bytesread, int totalsize, int timetaken, int timeleft ) +{ +// debugprint( ">>> OnProgressUpdate\n" ); + bFlagProgressUpdate = true; + + iBytesRead = bytesread; + iTotalSize = totalsize; + iTimeTaken = timetaken; + iTimeLeft = timeleft; + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnStatusUpdate( int status ) +{ +// debugprint( ">>> OnStatusUpdate, status = %i\n", status ); + bFlagStatusUpdate = true; + + iStatus = status; + + return S_OK; +} + +//*********************************************************************************************** +// Just tell the FTP module to go ahead and resume +// +STDMETHODIMP RADownloadEventSink::OnQueryResume() +{ +// debugprint( ">>> OnQueryResume\n" ); + bFlagQueryResume = true; + + bResumed = true; + + return DOWNLOADEVENT_RESUME; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RANetUtilEventSink::RANetUtilEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + pOwner( pOwnerIn ), + pLadderList( NULL ), + pLadderTail( NULL ), + pLadderListAM( NULL ), + pLadderTailAM( NULL ) +{ +// debugprint( "RANetUtilEventSink constructor\n" ); +} + +//*********************************************************************************************** +RANetUtilEventSink::~RANetUtilEventSink() +{ +// debugprint( "RANetUtilEventSink destructor\n" ); + DeleteLadderList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RANetUtilEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RANetUtilEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_INetUtilEvent)) + { + *ppv = (INetUtilEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RANetUtilEventSink::AddRef() +{ +// debugprint( "RANetUtilEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RANetUtilEventSink::Release() +{ +// debugprint( "RANetUtilEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnGameresSent( HRESULT hRes ) +{ +// debugprint( ">>> OnGameresSent got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnLadderList( HRESULT hRes, Ladder* pLadderListIn, int /*totalCount*/, long /*timeStamp*/, int /*keyRung*/ ) +{ + // Maintenance of ladders list is like that for channels list above. + // DeleteLadderList(); -> This is done once, before a set of RequestLadderList() calls are made. +// debugprint( ">>> OnLadderList got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { +// debugprint( "(SUCCEEDED)\n" ); + // Copy ladder list to our own list. + while( pLadderListIn ) + { +// debugprint( "OnLadderList got %s, rung %u\n", pLadderListIn->login_name, pLadderListIn->rung ); + if( *pLadderListIn->login_name != 0 && pLadderListIn->rung != -1 ) + { + Ladder* pLadderNew = new Ladder; + *pLadderNew = *pLadderListIn; + pLadderNew->next = NULL; // (We don't want the value that was just copied!) + if( pLadderNew->sku == LADDER_CODE_RA ) + { + if( !pLadderTail ) + { + // First Ladder in list. + pLadderList = pLadderNew; // This is the head of our Ladder list. + pLadderTail = pLadderNew; + } + else + { + pLadderTail->next = pLadderNew; + pLadderTail = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string. + sprintf( pOwner->szMyRecord, TXT_WOL_PERSONALWINLOSSRECORD, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + else // sku must be LADDER_CODE_AM + { + if( !pLadderTailAM ) + { + // First Ladder in list. + pLadderListAM = pLadderNew; // This is the head of our Ladder list. + pLadderTailAM = pLadderNew; + } + else + { + pLadderTailAM->next = pLadderNew; + pLadderTailAM = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string for Aftermath. + sprintf( pOwner->szMyRecordAM, TXT_WOL_PERSONALWINLOSSRECORDAM, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + } + pLadderListIn = pLadderListIn->next; + } + // Update shown list. + pOwner->ListChannelUsers(); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnPing( HRESULT hRes, int time, unsigned long ip, int /*handle*/ ) +{ + if( pOwner->bDoingDisconnectPinging ) + { +// debugprint( ">>> OnPing got : ip %i, time %i, ", ip, time ); + DebugChatDef( hRes ); + + if( ip == pOwner->TournamentOpponentIP ) + { + // This is the result of the opponent ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Opponent\n", pOwner->iDisconnectPingCurrent ); + } + else + { + // This is the result of the game server ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Server\n", pOwner->iDisconnectPingCurrent ); + } + } + return S_OK; +} + +//*********************************************************************************************** +void RANetUtilEventSink::DeleteLadderList() +{ +// debugprint( "DeleteLadderList()\n" ); + // Delete all Ladders allocated on the heap. + while( pLadderList ) + { + Ladder* pLadderHead = pLadderList; + pLadderList = pLadderHead->next; + delete pLadderHead; + } + pLadderTail = NULL; +} + +//*********************************************************************************************** +unsigned int RANetUtilEventSink::GetUserRank( const char* szName, bool bRankRA ) +{ + // Searches for szName in ladder list, returns player rank if found, else 0. + // Slow linear search. + // If bRankRA, returns RA rank, else returns AM rank. +// debugprint( "GetUserRank: Asked for %s, ", szName ); + Ladder* pLad; + if( bRankRA ) + pLad = pLadderList; + else + pLad = pLadderListAM; + + while( pLad ) + { +// debugprint( " comparing %s\n", (char*)pLad->login_name ); + if( _stricmp( (char*)pLad->login_name, szName ) == 0 ) + { +// debugprint( "found rung value %u\n", pLad->rung ); + return pLad->rung; + } + pLad = pLad->next; + } +// debugprint( "couldn't find in my ladder list.\n", szName ); + + return 0; +} + +//*********************************************************************************************** +//*********************************************************************************************** +void ChatDefAsText( char* szDesc, HRESULT hRes ) +{ + // Sets szDesc to the text meaning of hRes. + // Make sure szDesc is as long as the longest of the below. + switch( hRes ) + { + case CHAT_E_NICKINUSE : + sprintf( szDesc, "Your nick is still logged into chat" ); + break; + case CHAT_E_BADPASS : + sprintf( szDesc, "Your password is incorrect during login" ); + break; + case CHAT_E_NONESUCH : + sprintf( szDesc, "Reference made to non-existant user or channel" ); + break; + case CHAT_E_CON_NETDOWN : + sprintf( szDesc, "The network layer is down or cannot be initialized for some reason" ); + break; + case CHAT_E_CON_LOOKUP_FAILED : + sprintf( szDesc, "Name lookup (e.g DNS) failed for some reason" ); + break; + case CHAT_E_CON_ERROR : + sprintf( szDesc, "Some fatal error occured with the net connection" ); + break; + case CHAT_E_TIMEOUT : + sprintf( szDesc, "General request timeout for a request" ); + break; + case CHAT_E_MUSTPATCH : + sprintf( szDesc, "Must patch before continuing" ); + break; + case CHAT_E_STATUSERROR : + sprintf( szDesc, "Miscellaneous internal status error" ); + break; + case CHAT_E_UNKNOWNRESPONSE : + sprintf( szDesc, "Server has returned something we don't recognise" ); + break; + case CHAT_E_CHANNELFULL : + sprintf( szDesc, "Tried to join a channel that has enough players already" ); + break; + case CHAT_E_CHANNELEXISTS : + sprintf( szDesc, "Tried to create a channel that already exists" ); + break; + case CHAT_E_CHANNELDOESNOTEXIST : + sprintf( szDesc, "Tried to join a channel that does not exist" ); + break; + case CHAT_E_BADCHANNELPASSWORD : + sprintf( szDesc, "Tried to join a channel with the wrong password" ); + break; + case CHAT_E_BANNED : + sprintf( szDesc, "You've been banned from the server / channel" ); + break; + case CHAT_E_NOT_OPER : + sprintf( szDesc, "You tried to do something that required operator status" ); + break; + + case CHAT_S_CON_CONNECTING : + sprintf( szDesc, "A network connection is underway" ); + break; + case CHAT_S_CON_CONNECTED : + sprintf( szDesc, "A network connection is complete" ); + break; + case CHAT_S_CON_DISCONNECTING : + sprintf( szDesc, "A network connection is going down" ); + break; + case CHAT_S_CON_DISCONNECTED : + sprintf( szDesc, "A network connection is closed" ); + break; + case CHAT_S_FIND_NOTHERE : + sprintf( szDesc, "Find - Nick not in system" ); + break; + case CHAT_S_FIND_NOCHAN : + sprintf( szDesc, "Find - Not in any channels" ); + break; + case CHAT_S_FIND_OFF : + sprintf( szDesc, "Find - user has find turned off" ); + break; + case CHAT_S_PAGE_NOTHERE : + sprintf( szDesc, "Page - Nick not in system" ); + break; + case CHAT_S_PAGE_OFF : + sprintf( szDesc, "Page - user has page turned off" ); + break; + case CHAT_E_NOTCONNECTED : + sprintf( szDesc, "You are not connected to the chat server" ); + break; + case CHAT_E_NOCHANNEL : + sprintf( szDesc, "You are not in a channel" ); + break; + case CHAT_E_NOTIMPLEMENTED : + sprintf( szDesc, "Feature is not implemented" ); + break; + case CHAT_E_PENDINGREQUEST : + sprintf( szDesc, "The request was made while while a conflicting request was still pending" ); + break; + case CHAT_E_PARAMERROR : + sprintf( szDesc, "Invalid parameter passed - usually a NULL pointer" ); + break; + case CHAT_E_LEAVECHANNEL : + sprintf( szDesc, "Tried to create or join a channel before leaving the previous one" ); + break; + case CHAT_E_JOINCHANNEL : + sprintf( szDesc, "Tried to send something to a channel when not a member of any channel" ); + break; + case CHAT_E_UNKNOWNCHANNEL : + sprintf( szDesc, "Tried to join a non-existant channel" ); + break; + case S_OK: + sprintf( szDesc, "S_OK" ); + break; + case E_FAIL: + sprintf( szDesc, "E_FAIL" ); + break; + default: + sprintf( szDesc, "ERROR - Value not recognized!" ); + break; + } + // Append NetUtil errors. + switch( hRes ) + { + case NETUTIL_E_ERROR: + strcat( szDesc, " NetUtil: NETUTIL_E_ERROR" ); + break; + case NETUTIL_E_BUSY: + strcat( szDesc, " NetUtil: NETUTIL_E_BUSY" ); + break; + case NETUTIL_S_FINISHED: + strcat( szDesc, " NetUtil: NETUTIL_S_FINISHED" ); + break; + } +} + +//*********************************************************************************************** +void DebugChatDef( HRESULT hRes ) +{ + char szText[200]; + ChatDefAsText( szText, hRes ); +// debugprint( "%s\n", szText ); +} + +//*********************************************************************************************** +int iChannelLobbyNumber( const unsigned char* szChannelName ) +{ + // Returns lobby number of channel, or -1 for "channel is not a lobby". + if( strncmp( (char*)szChannelName, LOB_PREFIX, strlen( LOB_PREFIX ) ) == 0 ) + { + char szNum[10]; + strcpy( szNum, (char*)szChannelName + strlen( LOB_PREFIX ) ); +// debugprint( " ^ iChannelLobbyNumber returning atoi of %s\n", szNum ); + return atoi( szNum ); + } + else + return -1; +} + +//*********************************************************************************************** +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ) +{ + // Hard-coded translation of lobby number to apparent lobby name. + switch( iLobby ) + { + case 0: + strcpy( szLobbyNameToSet, "Combat Alley" ); + break; + case 1: + strcpy( szLobbyNameToSet, "No Man's Land" ); + break; + case 2: + strcpy( szLobbyNameToSet, "Hell's Pass" ); + break; + case 3: + strcpy( szLobbyNameToSet, "Lost Vegas" ); + break; + case 4: + strcpy( szLobbyNameToSet, "Death Valley" ); + break; + case 5: + strcpy( szLobbyNameToSet, "The Wastelands" ); + break; + case 6: + strcpy( szLobbyNameToSet, "Isle of Fury" ); + break; + case 7: + strcpy( szLobbyNameToSet, "Armourgarden" ); + break; + case 8: + strcpy( szLobbyNameToSet, "The Hive" ); + break; + case 9: + strcpy( szLobbyNameToSet, "North by Northwest" ); + break; + case 10: + strcpy( szLobbyNameToSet, "Decatur High" ); + break; + case 11: + strcpy( szLobbyNameToSet, "Damnation Alley" ); + break; + default: + sprintf( szLobbyNameToSet, "%ith Division", iLobby ); + break; + } +} + +#endif \ No newline at end of file diff --git a/REDALERT/RAWOLAPI.H b/REDALERT/RAWOLAPI.H new file mode 100644 index 000000000..6e026fa73 --- /dev/null +++ b/REDALERT/RAWOLAPI.H @@ -0,0 +1,337 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +#ifndef WIN32 +#error WOLAPI_INTEGRATION can't be specified for non WIN32 version! +#endif + +// rawolapi.h - WOLAPI sinks declarations. +// ajw 07/10/98 + +// Based somewhat on Neal's Borlandized version, "chatapi.h". + +#ifndef RAWOLAPI_H +#define RAWOLAPI_H + +#include "function.h" + +//#include "cominit.h" +#include + +// From OBJBASE.H +#define interface struct + +// From RPCNDR.H +#define DECLSPEC_UUID(x) + +#include + +//namespace WOL // namespace is workaround due to the use of "Server" as a global in Red Alert. +// ajw - Can't use namespaces in Watcom 10.5 it seems... +//{ + #include "wolapi\wolapi.h" + #define IID_DEFINED + //#include "wlib/wdebug.h" + #include "wolapi\chatdefs.h" + #include "wolapi\downloaddefs.h" + #include "wolapi\ftpdefs.h" +//}; +//using namespace WOL; +#include +//#include +#include + +//*********************************************************************************************** +// For debugging chat defined hresults... +void ChatDefAsText( char* szDesc, HRESULT hRes ); +void DebugChatDef( HRESULT hRes ); + +int iChannelLobbyNumber( const unsigned char* szChannelName ); +#define REASONABLELOBBYINTERPRETEDNAMELEN 50 +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ); + +class WolapiObject; + +#define MAXCHATSENDLENGTH 71 // Mainly aesthetic, and because of the length of edit line. + +enum CHANNELFILTER +{ + CHANNELFILTER_NO, + CHANNELFILTER_OFFICIAL, + CHANNELFILTER_UNOFFICIAL, + CHANNELFILTER_LOBBIES, + CHANNELFILTER_LOCALLOBBYGAMES, +}; + +#define WOLCOLORREMAP_ACTION PCOLOR_GREY +#define WOLCOLORREMAP_SELFSPEAKING PCOLOR_RED +#define WOLCOLORREMAP_LOCALMACHINEMESS PCOLOR_REALLY_BLUE // Color of system messages that originate locally. +#define WOLCOLORREMAP_PAGE PCOLOR_GOLD +#define WOLCOLORREMAP_KICKORBAN PCOLOR_GREEN //LTBLUE +#define WOLCOLORREMAP_PUBLICMESSAGE PCOLOR_NONE +#define WOLCOLORREMAP_PRIVATEMESSAGE PCOLOR_ORANGE + +#define WOLSOUND_ERROR VOC_SYS_ERROR +#define WOLSOUND_LOGIN VOC_RADAR_ON +#define WOLSOUND_LOGOUT VOC_RADAR_OFF +#define WOLSOUND_ENTERCHAN VOC_PLAYER_JOINED +#define WOLSOUND_EXITCHAN VOC_PLAYER_LEFT +#define WOLSOUND_ONPAGE VOC_INCOMING_MESSAGE +#define WOLSOUND_KICKORBAN VOC_TANYA_KISS +#define WOLSOUND_ENTERGAME VOC_INVULNERABLE +#define WOLSOUND_EXITGAME VOC_DOOR + +enum DISCONNECT_PING_STATUS +{ + PING_UNSTARTED, + PING_WAITING, + PING_GOOD, + PING_BAD, +}; +#define DISCONNECT_PING_COUNT 5 + +//*********************************************************************************************** +class RAChatEventSink : /////public CComObjectRoot, /////public IConnectionPoint, + public IChatEvent +{ + public: + RAChatEventSink( WolapiObject* pOwner ); + virtual ~RAChatEventSink(); + +// BEGIN_COM_MAP(RAChatEventSink) +// COM_INTERFACE_ENTRY(IChatEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IChatEvent + STDMETHOD(OnServerList)(HRESULT res, Server* servers); + STDMETHOD(OnLogout)(HRESULT r, User *user); + STDMETHOD(OnBusy)(HRESULT r); + STDMETHOD(OnIdle)(HRESULT r); + STDMETHOD(OnPageSend)(HRESULT r); + STDMETHOD(OnPaged)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnFind)(HRESULT r, Channel *); + STDMETHOD(OnConnection)(HRESULT r,LPCSTR motd); + STDMETHOD(OnChannelCreate)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelModify)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelJoin)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelLeave)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelTopic)(HRESULT r, Channel *channel, LPCSTR topic); + STDMETHOD(OnGroupList)(HRESULT r, Group *); + STDMETHOD(OnPublicMessage)(HRESULT r, Channel *channel, User *user, LPCSTR text); + STDMETHOD(OnPrivateMessage)(HRESULT r, User *user,LPCSTR text); + STDMETHOD(OnSystemMessage)(HRESULT r, LPCSTR); + STDMETHOD(OnNetStatus)(HRESULT r); + STDMETHOD(OnChannelList)(HRESULT r, Channel* channels); + STDMETHOD(OnUserList)(HRESULT r, Channel* channel, User* users); + STDMETHOD(OnUpdateList)(HRESULT res, Update *); + STDMETHOD(OnServerError)(HRESULT res); + STDMETHOD(OnMessageOfTheDay)(HRESULT res, LPCSTR); + STDMETHOD(OnPrivateAction)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicAction)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnPrivateGameOptions)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicGameOptions)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnGameStart)(HRESULT r, Channel *, User *, int); + STDMETHOD(OnUserKick)(HRESULT r, Channel *, User *, User *); + STDMETHOD(OnUserIP)(HRESULT r, User *); + STDMETHOD(OnServerError)(HRESULT res, LPCSTR ircmsg); + STDMETHOD(OnServerBannedYou)(HRESULT r, time_t bannedTill); + STDMETHOD(OnUserFlags)(HRESULT r, LPCSTR name, unsigned int flags, unsigned int mask); + STDMETHOD(OnChannelBan)(HRESULT r, LPCSTR name, int banned); + + unsigned long GetPlayerGameIP( const char* szPlayerName ) const; + void DeleteUserList(); // Deletes from heap all users pointed to through pUserList. + void DeleteUserIPList(); + unsigned long GetUserIP( const char* szName ) const; + + void ActionEggSound( const char* szMessage ); + +public: + // These vars are rather hackish. Basically, they are set before a callback is expected to be fired, and + // then checked immediately afterwards. The rest of the time, their values are meaningless. + // The idea is to force wolapi act in a modal way. In many places I "block" until a callback response to a + // wolapi request has been received. + bool bRequestServerListWait; + bool bRequestConnectionWait; + bool bRequestLogoutWait; +// bool bRequestChannelListWait; + bool bRequestChannelJoinWait; + bool bRequestChannelLeaveWait; + bool bRequestUserListWait; + bool bRequestChannelCreateWait; + bool bRequestFindWait; + bool bRequestPageWait; + + bool bRequestChannelListForLobbiesWait; + + bool bIgnoreChannelLists; // Used to temporarily turn off response to channel lists, when we are in the midst + // of some processing that depends on pChannelList remaining constant. + + bool bRequestGameStartWait; + + Server* pServer; // Server to connect to, acquired from OnServerList. + bool bConnected; // True when user is logged in to chat server. + bool bJoined; // True when user has joined a channel. + + Channel* pChannelList; // First element of channel list, or null. + CHANNELFILTER ChannelFilter; // Affects what channels are included in channel list when built. + + User* pUserList; // First element of user list, or null. + User* pUserTail; // Last element of user list, or null. + + char* szMotd; // Message of the day. + HRESULT hresRequestConnectionError; // Used to pass error hresult. + + HRESULT hresRequestFindResult; // Used to pass hresult. + Channel OnFindChannel; + + HRESULT hresRequestPageResult; // Used to pass hresult. + + HRESULT hresRequestJoinResult; // Used to pass hresult. + + bool bGotKickedTrigger; // Special flag meaning do some more processing after callback has exited. + + User* pGameUserList; // First element of start game user list, or null. + int iGameID; // WW Online game id received from OnGameStart. + // Is also a flag indicating "OnGameStart() called, TriggerGameStart() not yet called". + + User* pUserIPList; // List that holds user IP's, used for pinging in game channel. + User* pUserIPListTail; + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + + void DeleteChannelList(); // Deletes from heap all channels pointed to through pChannelList. + bool DownloadUpdates( Update* pUpdateList, int iUpdates ); + bool bSpecialMessage( const char* szMessage ); + void InsertUserSorted( User* pUserNew ); + +private: + long m_cRef; // Reference Count + +}; + +//*********************************************************************************************** +class RADownloadEventSink : +/////////// public CComObjectRoot, + public IDownloadEvent +{ +public: + RADownloadEventSink(); + virtual ~RADownloadEventSink() {}; + +// BEGIN_COM_MAP(RADownloadEventSink) +// COM_INTERFACE_ENTRY(IDownloadEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IDownloadEvent + STDMETHOD(OnEnd)(void); + STDMETHOD(OnError)(int error); + STDMETHOD(OnProgressUpdate)(int bytesread, int totalsize, int timetaken, int timeleft); + STDMETHOD(OnStatusUpdate)(int status); + STDMETHOD(OnQueryResume)(void); + +public: + bool bFlagEnd; + bool bFlagError; + bool bFlagProgressUpdate; + bool bFlagStatusUpdate; + bool bFlagQueryResume; + int iBytesRead; + int iTotalSize; + int iTimeTaken; + int iTimeLeft; + int iStatus; + bool bResumed; + +private: + long m_cRef; // Ref count +}; + +//*********************************************************************************************** +class RANetUtilEventSink : +// public CComObjectRoot, + public INetUtilEvent +{ +public: + RANetUtilEventSink( WolapiObject* pOwner ); + virtual ~RANetUtilEventSink(); + +//BEGIN_COM_MAP(CNetUtilEventSink) +// COM_INTERFACE_ENTRY(INetUtilEvent) +//END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // INetUtilEvent + + STDMETHOD(OnGameresSent)(HRESULT res); + STDMETHOD(OnLadderList)(HRESULT res, Ladder *list, int totalCount, long timeStamp, int keyRung); + STDMETHOD(OnPing)(HRESULT res, int time, unsigned long ip, int handle); + + + void DeleteLadderList(); // Deletes from heap all users pointed to through pUserList. + unsigned int GetUserRank( const char* szName, bool bRankRA ); + + Ladder* pLadderList; // First element of Ladder list, or null. + Ladder* pLadderTail; // Last element of Ladder list, or null. + Ladder* pLadderListAM; // First element of Aftermath Ladder list, or null. + Ladder* pLadderTailAM; // Last element of Aftermath Ladder list, or null. + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + +private: + long m_cRef; // Reference Count +}; + +//*********************************************************************************************** + +// SKU, reported to WOLAPI for the purpose of finding patches. +#ifdef ENGLISH +#define GAME_SKU 0x1500 +#else +#ifdef GERMAN +#define GAME_SKU 0x1502 +#else +#define GAME_SKU 0x1503 +#endif +#endif + +#define GAME_VERSION 0x00030003 +#define GAME_TYPE 21 +#define LOB_PREFIX "Lob_21_" + +// Sent to gameres server in order to receive Red Alert or Aftermath ladder rankings. (Sent in RequestLadderList.) +#define LADDER_CODE_RA 1005 +#define LADDER_CODE_AM 500 + +#endif + +#endif diff --git a/REDALERT/READLINE.CPP b/REDALERT/READLINE.CPP new file mode 100644 index 000000000..62f0b604e --- /dev/null +++ b/REDALERT/READLINE.CPP @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "function.h" +#include +#include +#include "wwfile.h" +#include "xstraw.h" +#include "readline.h" + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +//#pragma warning 665 9 + + +void strtrimcpp(char * buffer) +{ + if (buffer) { + + /* + ** Strip leading white space from the string. + */ + char * source = buffer; + while (isspace(*source)) { + source++; + } + if (source != buffer) { + strcpy(buffer, source); + } + + /* + ** Clip trailing white space from the string. + */ + for (int index = strlen(buffer)-1; index >= 0; index--) { + if (isspace(buffer[index])) { + buffer[index] = '\0'; + } else { + break; + } + } + } +} + + + +int Read_Line(FileClass & file, char * buffer, int len, bool & eof) +{ + return(Read_Line(FileStraw(file), buffer, len, eof)); +} + + +int Read_Line(Straw & file, char * buffer, int len, bool & eof) +{ + if (len == 0 || buffer == NULL) return(0); + + int count = 0; + for (;;) { + char c; + if (file.Get(&c, sizeof(c)) != sizeof(c)) { + eof = true; + buffer[0] = '\0'; + break; + } + + if (c == '\x0A') break; + if (c != '\x0D' && count+1 < len) { + buffer[count++] = c; + } + } + buffer[count] = '\0'; + + strtrimcpp(buffer); + return(strlen(buffer)); +} diff --git a/REDALERT/READLINE.H b/REDALERT/READLINE.H new file mode 100644 index 000000000..b0a8281fd --- /dev/null +++ b/REDALERT/READLINE.H @@ -0,0 +1,23 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "wwfile.h" +#include "straw.h" + +void strtrim(char * buffer); +int Read_Line(FileClass & file, char * buffer, int len, bool & eof); +int Read_Line(Straw & file, char * buffer, int len, bool & eof); + diff --git a/REDALERT/RECT.CPP b/REDALERT/RECT.CPP new file mode 100644 index 000000000..d51f42a8b --- /dev/null +++ b/REDALERT/RECT.CPP @@ -0,0 +1,197 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/22/96 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Rect::Rect -- Constructs a rectangle object. * + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * Rect::Intersect -- Find the intersection between two rectangles. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "rect.h" + + +/*********************************************************************************************** + * Rect::Rect -- Constructs a rectangle object. * + * * + * This will construct a rectangle object according to the parameters specified. * + * * + * INPUT: x,y -- The X and Y values of the upper left corner of the rectangle. * + * * + * w,h -- The width and height values of the rectangle. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect::Rect(int x, int y, int w, int h) : + X(x), + Y(y), + Width(w), + Height(h) +{ +} + + +/*********************************************************************************************** + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * * + * An invalid rectangle has values that do not make any sense. This is a useful state since * + * this can be used to determine if a rectangle has been initialized correctly or for * + * detecting an error return condition for rectangle manipulation routines. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this rectangle appear valid? * + * * + * WARNINGS: An invalid rectangle is one that has a width or height of less than one. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +bool Rect::Is_Valid(void) const +{ + return(Width > 0 && Height > 0); +} + + +/*********************************************************************************************** + * Rect::Intersect -- Find the intersection between two rectangles. * + * * + * This routine will take the specified rectangle and use it like a "cookie cutter" on this * + * rectangle. The intersection of these two rectangles is returned. An optional X and * + * Y parameter is supplied so that an absolute coordinate can be maintained to the new * + * rectangle. * + * * + * INPUT: rectangle -- Reference to the rectangle to use as a cookie cutter. * + * * + * x,y -- Optional pointer to a coordinate that will be adjusted to stay * + * in an absolute position in coordinates even though it is a * + * relative offset. * + * * + * OUTPUT: Returns with the rectangle that is the intersection of the one specified and * + * this rectangle. * + * * + * WARNINGS: The rectangle returned may be invalid. This can occur if there is no legal * + * intersection between the rectangles. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Rect::Intersect(Rect const & rectangle, int * x, int * y) const +{ + Rect rect(0, 0, 0, 0); // Dummy (illegal) rectangle. + Rect r = rectangle; // Working rectangle. + + /* + ** Both rectangles must be valid or else no intersection can occur. In such + ** a case, return an illegal rectangle. + */ + if (!Is_Valid() || !rectangle.Is_Valid()) return(rect); + + /* + ** The rectangle spills past the left edge. + */ + if (r.X < X) { + r.Width -= X - r.X; + r.X = X; + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past top edge. + */ + if (r.Y < Y) { + r.Height -= Y - r.Y; + r.Y = Y; + } + if (r.Height < 1) return(rect); + + /* + ** The rectangle spills past the right edge. + */ + if (r.X + r.Width > X + Width) { + r.Width -= (r.X + r.Width) - (X + Width); + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past the bottom edge. + */ + if (r.Y + r.Height > Y + Height) { + r.Height -= (r.Y + r.Height) - (Y + Height); + } + if (r.Height < 1) return(rect); + + /* + ** Adjust Height relative draw position according to Height new rectangle + ** union. + */ + if (x != NULL) { + *x -= (r.X-X); + } + if (y != NULL) { + *y -= (r.Y-Y); + } + + return(r); +} + + +Rect const Union(Rect const & rect1, Rect const & rect2) +{ + if (rect1.Is_Valid()) { + if (rect2.Is_Valid()) { + Rect result = rect1; + + if (result.X > rect2.X) { + result.Width += result.X-rect2.X; + result.X = rect2.X; + } + if (result.Y > rect2.Y) { + result.Height += result.Y-rect2.Y; + result.Y = rect2.Y; + } + if (result.X+result.Width < rect2.X+rect2.Width) { + result.Width = ((rect2.X+rect2.Width)-result.X)+1; + } + if (result.Y+result.Height < rect2.Y+rect2.Height) { + result.Height = ((rect2.Y+rect2.Height)-result.Y)+1; + } + return(result); + } + return(rect1); + } + return(rect2); +} diff --git a/REDALERT/RECT.H b/REDALERT/RECT.H new file mode 100644 index 000000000..8f1a77b47 --- /dev/null +++ b/REDALERT/RECT.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/21/96 * + * * + * Last Update : July 21, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RECT_H +#define RECT_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include + +class Rect +{ + public: + Rect(int x=0, int y=0, int w=0, int h=0); + + Rect const Intersect(Rect const & rectangle, int * x=NULL, int * y=NULL) const; + friend Rect const Union(Rect const & rect1, Rect const & rect2); + + bool Is_Valid(void) const; + int Size(void) const {return(Width*Height);} + +// private: + int X; + int Y; + int Width; + int Height; +}; + + +#endif + diff --git a/REDALERT/REGION.H b/REDALERT/REGION.H new file mode 100644 index 000000000..33fdba6f9 --- /dev/null +++ b/REDALERT/REGION.H @@ -0,0 +1,56 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/REGION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REGION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/09/95 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef REGION_H +#define REGION_H + + +class RegionClass { + public: + RegionClass(void) {Threat = 0;}; + ~RegionClass(void) {}; + int operator != (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass));}; + int operator == (RegionClass const & region) {return !memcmp(this, ®ion, sizeof(RegionClass));}; + int operator > (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) > 0;}; + int operator < (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) < 0;}; + + void Reset_Threat(void) {Threat = 0;}; + void Adjust_Threat(int threat, int neg) {if (neg) Threat -= threat; else Threat+= threat;}; + int Threat_Value(void) const {return Threat;}; + + protected: + long Threat; +}; + +#endif diff --git a/REDALERT/REINF.CPP b/REDALERT/REINF.CPP new file mode 100644 index 000000000..bdd2cb120 --- /dev/null +++ b/REDALERT/REINF.CPP @@ -0,0 +1,748 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/REINF.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REINF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 24, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * Do_Reinforcements -- Create and place a reinforcement team. * + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * _Create_Group -- Create a group given team specification. * + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * * + * This routine will cause the group to pop out of the object specified. * + * * + * INPUT: group -- Pointer to the first object in the group to be popped out. * + * * + * object -- Pointer to the object that the group is to pop out of. * + * * + * OUTPUT: bool; Was the group popped out of the specified object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Pop_Group_Out_Of_Object(FootClass * group, TechnoClass * object) +{ + assert(group != NULL && object != NULL); + int quantity = 0; + + /* + ** Take every infantry member of this group and detach it from the group list + ** and then make it pop out of the candidate source. + */ + while (group != NULL) { + TechnoClass * todo = group; + group = (FootClass *)(ObjectClass *)group->Next; + todo->Next = NULL; + + switch (object->What_Am_I()) { + + /* + ** The infantry just walks out of a building. + */ + case RTTI_BUILDING: + if (object->Exit_Object(todo) != 2) { + delete todo; + } else { + ++quantity; + } + break; + + /* + ** Infantry get attached to transport vehicles and then unload. + */ + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + object->Attach((FootClass *)todo); + object->Assign_Mission(MISSION_UNLOAD); + ++quantity; + break; + + default: + delete todo; + break; + } + } + + return (quantity != 0); +} + + +/*********************************************************************************************** + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * * + * The unit is examined and if the owning house needs to confiscate it, then this routine * + * will return TRUE. In other cases, the unit should be left to its own devices. * + * * + * INPUT: unit -- Pointer to the object to examine. * + * * + * OUTPUT: bool; Should the object be confiscated by the player so that it becomes one of * + * his normal game objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool _Need_To_Take(AircraftClass const * air) +{ + if (*air == AIRCRAFT_YAK || *air == AIRCRAFT_MIG) { + int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP); +// int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP) - (air->House->Get_Quantity(AIRCRAFT_YAK)+air->House->Get_Quantity(AIRCRAFT_MIG)); + + /* + ** Loop through all aircraft and subtract all the ones that are NOT loaners. + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * airptr = Aircraft.Ptr(index); + if ((*airptr == AIRCRAFT_YAK || *airptr == AIRCRAFT_MIG) && airptr->IsOwnedByPlayer && !airptr->IsALoaner && airptr != air) { + deficit -= 1; + if (deficit == 0) break; + } + } + + if (deficit > 0) return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Create_Group -- Create a group given team specification. * + * * + * This routine will create all members of the group as specified by the team type. * + * * + * INPUT: teamtype -- Pointer to the team type that specifies what objects should be * + * created in this group. * + * * + * OUTPUT: Returns with a pointer to the first member of the group created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static FootClass * _Create_Group(TeamTypeClass const * teamtype) +{ + assert(teamtype != NULL); + + TeamClass * team = new TeamClass(teamtype); + if (team != NULL) { + team->Force_Active(); + } + + bool hasunload = false; + for (int tm = 0; tm < teamtype->MissionCount; tm++) { + if (teamtype->MissionList[tm].Mission == TMISSION_UNLOAD) { + hasunload = true; + break; + } + } + + /* + ** Now that the official source for the reinforcement has been determined, the + ** objects themselves must be created. + */ + FootClass * transport = NULL; + FootClass * object = NULL; + for (int index = 0; index < teamtype->ClassCount; index++) { + TechnoTypeClass const * tclass = teamtype->Members[index].Class; + + for (int sub = 0; sub < teamtype->Members[index].Quantity; sub++) { + ScenarioInit++; + FootClass * temp = (FootClass *)tclass->Create_One_Of(HouseClass::As_Pointer(teamtype->House)); + ScenarioInit--; + + if (temp != NULL) { + + /* + ** Add the member to the team. + */ + if (team != NULL) { + ScenarioInit++; + bool ok = team->Add(temp); +//Mono_Printf("Added to team = %d.\n", ok);Keyboard->Get(); + + ScenarioInit--; + temp->IsInitiated = true; + } + + if (temp->What_Am_I() == RTTI_AIRCRAFT && !_Need_To_Take((AircraftClass const *)temp)) { + temp->IsALoaner = true; + } + + /* + ** Build the list of transporters and passengers. + */ + if (tclass->Max_Passengers() > 0) { + + /* + ** Link to the list of transports. + */ + temp->Next = transport; + transport = temp; + + } else { + + /* + ** Link to the list of normal objects. + */ + temp->Next = object; + object = temp; + } + } + } + } + + /* + ** If the group consists of transports and normal objects, then assign the normal + ** objects to be passengers on the transport. + */ + if (transport != NULL && object != NULL) { + transport->Attach(object); + + /* + ** HACK ALERT! If the this team has an unload mission, then flag the transport + ** as a loaner so that it will exit from the map when the unload process is + ** complete, but only if the transport is an aircraft type. + */ + if (hasunload && (transport->What_Am_I() == RTTI_AIRCRAFT || transport->What_Am_I() == RTTI_VESSEL)) { + transport->IsALoaner = true; + } + } + + /* + ** For JUST transport helicopters, consider the loaner a gift if there are + ** no passengers. + */ + if (transport != NULL && object == NULL && transport->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)transport) == AIRCRAFT_TRANSPORT) { + transport->IsALoaner = false; + } + + if (transport == 0 && object == 0) { + if (team != NULL) delete team; + return(NULL); + } + + /* + ** If this group consists only of non-transport object, then just return with a pointer + ** to the first member of the group. + */ + if (transport == NULL) { + return(object); + } + + return(transport); +} + + +/*********************************************************************************************** + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * * + * Use this to determine if the specified group only contains infantry. Such a reinforcement* + * group is a candidate for popping out of a building or transport vehicle rather than * + * driving/walking/sailing/flying onto the map under its own power. * + * * + * INPUT: first -- Pointer to the first object in the group to examine. * + * * + * OUTPUT: bool; Is the entire group composed of infantry type units? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Consists_Only_Of_Infantry(FootClass const * first) +{ + while (first != NULL) { + if (first->What_Am_I() != RTTI_INFANTRY) { + return(false); + } + first = (FootClass const *)((ObjectClass *)first->Next); + } + return(true); +} + + +/*********************************************************************************************** + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * * + * This routine is used to scan nearby locations to determine if there is a suitable host * + * for these reinforcements to "pop out of" (apologies to Aliens). Typical hosts include * + * buildings and transport vehicles (of any kind). * + * * + * INPUT: origin -- The cell that should be scanned from. Only this location and immediate * + * adjacent locations will be scanned. * + * * + * OUTPUT: Returns with a pointer to a suitable host. If none could be found then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static TechnoClass * _Who_Can_Pop_Out_Of(CELL origin) +{ + CellClass * cellptr = &Map[origin]; + TechnoClass * candidate = NULL; + + for (int f = -1; f < 8; f++) { + CellClass * ptr = cellptr; + if (f != -1) { + ptr = &ptr->Adjacent_Cell(FacingType(f)); + } + + BuildingClass * building = ptr->Cell_Building(); + if (building && building->Strength > 0) { + candidate = building; + } + + UnitClass * unit = ptr->Cell_Unit(); + if (unit && unit->Strength && unit->Class->Max_Passengers() > 0) { + return(unit); + } + } + return(candidate); +} + + +/*********************************************************************************************** + * Do_Reinforcements -- Create and place a reinforcement team. * + * * + * This routine is called when a reinforcement team must be created and placed on the map. * + * It will create all members of the team and place them at the location determined from * + * the team composition. The reinforcement team should follow team orders until overridden * + * by AI or player intervention. * + * * + * INPUT: teamtype -- Pointer to the team type to create as a reinforcement. * + * * + * OUTPUT: Was the reinforcement successfully created and placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/18/1995 JLB : Returns success or failure condition. * + * 06/19/1995 JLB : Announces reinforcements. * + * 02/15/1996 JLB : Recognizes team reinforcement location. * + *=============================================================================================*/ +bool Do_Reinforcements(TeamTypeClass const * teamtype) +{ + assert(teamtype != 0); + + /* + ** perform some preliminary checks for validity. + */ + if (!teamtype || !teamtype->ClassCount) return(false); + + /* + ** HACK ALERT! + ** Give this team an attack waypoint mission that will attack the waypoint location of this + ** team if there are no team missions previously assigned. + */ + if (teamtype->MissionCount == 0) { + TeamTypeClass * tt = (TeamTypeClass *)teamtype; + tt->MissionCount = 1; + tt->MissionList[0].Mission = TMISSION_ATT_WAYPT; + tt->MissionList[0].Data.Value = teamtype->Origin; + } + + FootClass * object = _Create_Group(teamtype); + + +//Mono_Printf("%d-%s (object=%p, team=%d).\n", __LINE__, __FILE__, object, object->Team.Is_Valid());Keyboard->Get(); + + + /* + ** Bail on this reinforcement if no reinforcements could be created. + ** This is probably because the object maximum was reached. + */ + if (!object) { + return(false); + } + + /* + ** Special case code to handle infantry types that run from a building. This presumes + ** that infantry are never a transport (which is safe to do). + */ + if (object != NULL && teamtype->Origin != -1 && _Consists_Only_Of_Infantry(object)) { + + /* + ** Search for an object that these infantry can pop out of. + */ + TechnoClass * candidate = _Who_Can_Pop_Out_Of(Scen.Waypoint[teamtype->Origin]); + + if (candidate != NULL) { + return(_Pop_Group_Out_Of_Object(object, candidate)); + } + } + + /* + ** The reinforcements must be delivered the old fashioned way -- by moving onto the + ** map using their own power. First order of business is to determine where they + ** should arrive from. + */ + SourceType source = HouseClass::As_Pointer(teamtype->House)->Control.Edge; + if (source == SOURCE_NONE) { + source = SOURCE_NORTH; + } + + /* + ** Pick the location where the reinforcements appear and then place + ** them there. + */ + bool placed = false; + + FacingType eface = (FacingType)(source << 1); // Facing to enter map. + + CELL cell = Map.Calculated_Cell(source, teamtype->Origin, -1, object->Techno_Type_Class()->Speed); +#ifdef FIXIT_ANTS + /* + ** For the ants, they will pop out of the ant hill directly. + */ + if (teamtype->Origin != -1 && object->What_Am_I() == RTTI_UNIT && + (*((UnitClass*)object) == UNIT_ANT1 || + *((UnitClass*)object) == UNIT_ANT2 || + *((UnitClass*)object) == UNIT_ANT3)) { + CELL newcell = Scen.Waypoint[teamtype->Origin]; + if (newcell != -1) { + if (Map[newcell].TType == TEMPLATE_HILL01) { + cell = newcell; + } + } + } +#endif + + CELL newcell = cell; + + FootClass * o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + bool okvoice = false; + while (newcell > 0 && object != NULL) { + DirType desiredfacing = Facing_Dir(eface); + if (object->What_Am_I() == RTTI_AIRCRAFT) { + desiredfacing = Random_Pick(DIR_N, DIR_MAX); + } + + ScenarioInit++; + if (object->Unlimbo(Cell_Coord(newcell), desiredfacing)) { + okvoice = true; + + /* + ** If this object is part of a team, then the mission for this + ** object will be guard. The team handler will assign the proper + ** mission that it should follow. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT) { + object->Assign_Mission(MISSION_GUARD); + object->Commence(); + } + + } else { + + /* + ** Could not unlimbo at location specified so find an adjacent location that it can + ** be unlimboed at. If this fails, then abort the whole placement process. + */ + FacingType adj; + for (adj = FACING_N; adj < FACING_COUNT; adj++) { + CELL trycell = Adjacent_Cell(newcell, adj); + if (!Map.In_Radar(trycell) && object->Can_Enter_Cell(trycell, adj) == MOVE_OK) { + newcell = trycell; + break; + } + } + if (adj < FACING_COUNT) continue; + newcell = 0; + } + ScenarioInit--; + + object = o; + if (object != NULL) { + o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + } + } + + /* + ** If there are still objects that could not be placed, then delete them. + */ + if (o != NULL) { + while (o != NULL) { + FootClass * old = o; + o = (FootClass *)(ObjectClass *)o->Next; + old->Next = 0; + + delete old; + } + } + + /* + ** Announce when the reinforcements have arrived. + */ + if (okvoice && teamtype->House == PlayerPtr->Class->House) { + Speak(VOX_REINFORCEMENTS, NULL, newcell ? Cell_Coord(newcell) : 0); + } + + return(true); +} + + +/*********************************************************************************************** + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * * + * Use this routine to bring on a reinforcement that hasn't been anticipated by the trigger * + * system. An example of this would be replacement harvesters or airfield ordered units. * + * The appropriate transport is created (if necessary) and a mission is assigned such that * + * the object will legally bring itself onto the playing field. * + * * + * INPUT: house -- The owner of this reinforcement. * + * * + * type -- The object to bring on. * + * * + * another -- This is reserved for the transport class in those cases where the * + * transport MUST be forced to a specific type. * + * * + * mission -- The mission to assign this reinforcement team. * + * * + * argument -- Optional team mission argument (usually a waypoint). * + * * + * OUTPUT: Was the special reinforcement created without error? * + * * + * WARNINGS: This routine will fail if a team type cannot be created. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission, int argument) +{ + assert(house != 0); + assert(type != 0); + + if (house && type) { + TeamTypeClass * team = new TeamTypeClass(); + + if (team) { + + /* + ** If there is no overridden mission assign to this special reinforcement, then + ** we must assign something. If not, the reinforcement will just sit at the edge + ** of the map. + */ + if (!another && mission == TMISSION_NONE) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(house->Control.Edge); + } + + /* + ** Fill in the team characteristics. + */ + strcpy((char *)&team->IniName[0], "TEMP"); + team->IsReinforcable = false; + team->IsTransient = true; + team->ClassCount = 1; + team->Members[0].Class = type; + team->Members[0].Quantity = 1; + team->MissionCount = 1; + if (mission == TMISSION_NONE) { + team->MissionList[0].Mission = TMISSION_UNLOAD; + team->MissionList[0].Data.Value = WAYPT_REINF; + } else { + team->MissionList[0].Mission = mission; + team->MissionList[0].Data.Value = argument; + } + team->House = house->Class->House; + if (another) { + team->ClassCount++; + team->Members[1].Class = another; + team->Members[1].Quantity = 1; + } + + bool ok = Do_Reinforcements(team); + if (!ok) delete team; + return(ok); + } + } + return(false); +} + + +/*********************************************************************************************** + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * * + * This routine is used to launch an airstrike. It will create the necessary aircraft and * + * assign them to attack the target specified. This routine bypasses the normal * + * reinforcement logic since it doesn't need the sophistication of unloading and following * + * team mission lists. * + * * + * INPUT: house -- The perpetrator of this air strike. * + * * + * air -- The type of aircraft to make up this airstrike. * + * * + * number -- The number of aircraft in this airstrike. * + * * + * mission -- The mission to assign the aircraft. * + * * + * tarcom -- The target to assign these aircraft. * + * * + * navcom -- The navigation target to assign (if necessary). * + * * + * OUTPUT: Returns the number of aircraft created for this airstrike. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int Create_Air_Reinforcement(HouseClass * house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger) +{ + assert(house != 0); + assert((unsigned)air < AIRCRAFT_COUNT); + assert(number != 0); + assert((unsigned)mission < MISSION_COUNT); + /* + ** Get a pointer to the class of the object that we are going to create. + */ + TechnoTypeClass const * type = (TechnoTypeClass *)&AircraftTypeClass::As_Reference(air); + + /* + ** Abort the airstrike if Tanya is the passenger and she's dead. + */ + if (passenger == INFANTRY_TANYA && IsTanyaDead) { + number = 0; + } + + /* + ** Loop through the number of objects we are supposed to create and + ** create and place them on the map. + */ + int sub; + for (sub = 0; sub < number; sub++) { + + /* + ** Create one of the required objects. If this fails we could have + ** a real problem. + */ + ScenarioInit++; + TechnoClass * obj = (TechnoClass *)type->Create_One_Of(house); + ScenarioInit--; + if (!obj) return(sub); + + /* + ** Flying objects always have the IsALoaner bit set. + */ + obj->IsALoaner = true; + + /* + ** Find a cell for the object to come in on. This is stolen from the + ** the code that handles a SOURCE_AIR in the normal logic. + */ + SourceType source = house->Control.Edge; + switch (source) { + case SOURCE_NORTH: + case SOURCE_EAST: + case SOURCE_SOUTH: + case SOURCE_WEST: + break; + + default: + source = SOURCE_NORTH; + break; + } + CELL newcell = Map.Calculated_Cell(source, -1, -1, SPEED_WINGED); + + /* + ** Try and place the object onto the map. + */ + ScenarioInit++; + int placed = obj->Unlimbo(Cell_Coord(newcell), DIR_N); + ScenarioInit--; + if (placed) { + + /* + ** If we succeeded in placing the obj onto the map then + ** now we need to give it a mission and destination. + */ + obj->Assign_Mission(mission); + + /* + ** If a navcom was specified then set it. + */ + if (navcom != TARGET_NONE) { + obj->Assign_Destination(navcom); + } + + /* + ** If a tarcom was specified then set it. + */ + if (tarcom != TARGET_NONE) { + obj->Assign_Target(tarcom); + } + + /* + ** Assign generic passenger value here. This value is used to determine + ** if this aircraft should drop parachute reinforcements. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass * aircraft = (AircraftClass *)obj; + if (passenger != INFANTRY_NONE) { + aircraft->Passenger = passenger; + } +// if (Passenger == INFANTRY_TANYA) { +// aircraft->Ammo = 1; + //aircraft->AttacksRemaining = 1; +// } + } + + /* + ** Start the object into action. + */ + obj->Commence(); + } else { + delete obj; + sub--; + return(sub); + } + } + return(sub); +} diff --git a/REDALERT/RESOURCE/RedAlert.rc b/REDALERT/RESOURCE/RedAlert.rc new file mode 100644 index 000000000..1caeb6b8f --- /dev/null +++ b/REDALERT/RESOURCE/RedAlert.rc @@ -0,0 +1,100 @@ +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Petroglyph Games Inc." + VALUE "FileDescription", "Command & Conquer(TM) Remastered Collection RedAlert DLL" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Command & Conquer(TM) Remastered Collection" + VALUE "LegalCopyright", "(C) 2020 Electronic Arts Inc. All rights reserved." + VALUE "OriginalFilename", "RedAlert.DLL" + VALUE "ProductName", "Command & Conquer(TM) Remastered Collection" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/REDALERT/RESOURCE/resource.h b/REDALERT/RESOURCE/resource.h new file mode 100644 index 000000000..d0c028e56 --- /dev/null +++ b/REDALERT/RESOURCE/resource.h @@ -0,0 +1,30 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by RedAlert.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/REDALERT/RGB.CPP b/REDALERT/RGB.CPP new file mode 100644 index 000000000..265d47d1d --- /dev/null +++ b/REDALERT/RGB.CPP @@ -0,0 +1,246 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RGB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RGB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * RGBClass::Difference -- Determines the "distance" between two colors. * + * RGBClass::Set -- Set the color index to the RGB object specified. * + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "rgb.h" +#include "hsv.h" +#include "palettec.h" + +RGBClass const BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * * + * This routine is used to modify an RGB value to proportionately match another RGB value * + * according to the ratio parameter specified. Typical use of this routine is in palette * + * fading from one palette to another or to black. * + * * + * INPUT: ratio -- The ration of transformation. This value is in the form of 0 to 255, * + * with 0 being no change, and 255 being 100% transformed into the * + * destination color. * + * * + * rgb -- Reference to the destination RGB color to transform this color into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Adjust(int ratio, RGBClass const & rgb) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = (int)rgb.Red - (int)Red; + Red = (int)Red + (value * ratio) / 256; + + value = (int)rgb.Green - (int)Green; + Green = (int)Green + (value * ratio) / 256; + + value = (int)rgb.Blue - (int)Blue; + Blue = (int)Blue + (value * ratio) / 256; +} + + +/*********************************************************************************************** + * RGBClass::Difference -- Determines the "distance" between two colors. * + * * + * This routine is used to calculate a relative distance between two colors. The value is * + * relative only to itself and thus is useful only for determining the magnitude of * + * color difference rather than the nature of the color difference. Palette remapping * + * code uses this routine to find closest matches for colors. * + * * + * INPUT: rgb -- Reference to the color to be compared to this color. * + * * + * OUTPUT: Returns the difference between the two colors. The value returned is zero if the * + * colors exactly match. The greater the positive value the greater the difference * + * between the colors. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int RGBClass::Difference(RGBClass const & rgb) const +{ + int r = (int)Red - (int)rgb.Red; + if (r < 0) r = -r; + + int g = (int)Green - (int)rgb.Green; + if (g < 0) g = -g; + + int b = (int)Blue - (int)rgb.Blue; + if (b < 0) b = -b; + + return(r*r + g*g + b*b); +} + + +/*********************************************************************************************** + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * * + * This conversion operator will convert an RGBClass object into an HSVClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implicit) to the HSVClass object that most closely * + * represents the RGBClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +RGBClass::operator HSVClass (void) const +{ + int hue; + int saturation; + int value; + + /* + ** Fetch working component values for the color guns. + */ + int red = Red_Component(); + int green = Green_Component(); + int blue = Blue_Component(); + + /* + ** The hue defaults to none. Only if there is a saturation value will the + ** hue be calculated. + */ + hue = 0; + + /* + ** Set the value (brightness) to match the brightest color gun. + */ + value = (red > green) ? red : green; + if (blue > value) value = blue; + + /* + ** Determine the amount of true white present in the color. This is the + ** minimum color gun value. The white component is used to determine + ** color saturation. + */ + int white = (red < green) ? red : green; + if (blue < white) white = blue; + + /* + ** Determine the saturation (intensity) of the color by comparing the + ** ratio of true white as a component of the overall color. The more + ** white component, the less saturation. + */ + saturation = 0; + if (value) { + saturation = ((value - white) * 255) / value; + } + + /* + ** If there is any saturation at all, then the hue must be calculated. The + ** hue is based on a six sided color wheel. + */ + if (saturation != 0) { + unsigned int tmp = value - white; + unsigned int r1 = ((value - red) * 255) / tmp; + unsigned int g1 = ((value - green) * 255) / tmp; + unsigned int b1 = ((value - blue) * 255) / tmp; + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if (value == red) { + if (white == green) { + tmp = 5 * 256 + b1; + } else { + tmp = 1 * 256 - g1; + } + } else { + if (value == green) { + if (white == blue) { + tmp = 1 * 256 + r1; + } else { + tmp = 3 * 256 - b1; + } + } else { + if (white == red) { + tmp = 3 * 256 + g1; + } else { + tmp = 5 * 256 - r1; + } + } + } + + // Divide by six and round. + hue = tmp / 6; + } + + return(HSVClass(hue, saturation, value)); +} + + +/*********************************************************************************************** + * RGBClass::Set -- Set the color index to the RGB object specified. * + * * + * This routine will set the specified color index with this RGBClass object. Use this * + * routine to set one color. * + * * + * INPUT: color -- The color index to set with this RGBClass object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Set(int color) const +{ + Raw_Color_Prep(color); + Raw_Set(); + + ((RGBClass &)PaletteClass::CurrentPalette[color]) = *this; +} + diff --git a/REDALERT/RGB.H b/REDALERT/RGB.H new file mode 100644 index 000000000..a91f2d1d5 --- /dev/null +++ b/REDALERT/RGB.H @@ -0,0 +1,140 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RGB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RGB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RGB_H +#define RGB_H + +class PaletteClass; +class HSVClass; + +#if (0) + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + +extern void outrgb(unsigned char red, unsigned char green, unsigned char blue); +#pragma aux outrgb parm [al] [bl] [cl] \ + modify [dx al] = \ + "mov dx,03C9h" \ + "out dx,al" \ + "jmp e1" \ + "e1:" \ + "mov al,bl" \ + "out dx,al" \ + "jmp e2" \ + "e2:" \ + "mov al,cl" \ + "out dx,al" \ + "jmp e3" \ + "e3:" + +#endif + +/* +** Each color entry is represented by this class. It holds the values for the color +** guns. The gun values are recorded in device dependant format, but the interface +** uses gun values from 0 to 255. +*/ +class RGBClass +{ +// static RGBClass const BlackColor; + + public: + RGBClass(void) : Red(0), Green(0), Blue(0) {}; + RGBClass(unsigned char red, unsigned char green, unsigned char blue) : + Red((unsigned char)(red>>2)), + Green((unsigned char)(green>>2)), + Blue((unsigned char)(blue>>2)) + {}; + operator HSVClass (void) const; + RGBClass & operator = (RGBClass const & rgb) { + if (this == &rgb) return(*this); + + Red = rgb.Red; + Green = rgb.Green; + Blue = rgb.Blue; + return(*this); + }; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, RGBClass const & rgb); +// void Adjust(int ratio, RGBClass const & rgb = BlackColor); + int Difference(RGBClass const & rgb) const; + int Red_Component(void) const {return ((Red << 2) | (Red >> 6));}; + int Green_Component(void) const {return((Green << 2) | (Green >> 6));}; + int Blue_Component(void) const {return((Blue << 2) | (Blue >> 6));}; + void Set(int color) const; + + private: + + friend class PaletteClass; + + void Raw_Set(void) const { + //outrgb(Red, Green, Blue); + +// outportb(0x03C9, Red); +// outportb(0x03C9, Green); +// outportb(0x03C9, Blue); + }; + + static void Raw_Color_Prep(unsigned char color) { + //outportb(0x03C8, color); + }; + + /* + ** These hold the actual color gun values in machine dependant scale. This + ** means the values range from 0 to 63. + */ + unsigned char Red; + unsigned char Green; + unsigned char Blue; +}; + +extern RGBClass const BlackColor; + +#endif diff --git a/REDALERT/RNDSTRAW.CPP b/REDALERT/RNDSTRAW.CPP new file mode 100644 index 000000000..c9dc7bce5 --- /dev/null +++ b/REDALERT/RNDSTRAW.CPP @@ -0,0 +1,317 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNDSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNDSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomStraw::Get -- Fetch random data. * + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * RandomStraw::Reset -- Reset the data to known initial state. * + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "rndstraw.h" +#include "sha.h" + + +/*********************************************************************************************** + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * * + * This will initialize the random straw into a known state. The initial state is useless * + * for cryptographic purposes. It must be seeded before it should be used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::RandomStraw(void) : + SeedBits(0), + Current(0) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * * + * This destructor will clear out the seed data so that there won't be any sensitive * + * information left over in the memory this object occupied. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::~RandomStraw(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::Reset -- Reset the data to known initial state. * + * * + * This routine is functionally equivalent to performing an placement new on the object. It * + * clears out all the seed data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must reseed it before fetching random numbers. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Reset(void) +{ + SeedBits = 0; + Current = 0; + memset(Random, '\0', sizeof(Random)); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * * + * This routine is used when seeding the random straw. Keep feeding random bits to this * + * class until the Seed_Bits_Needed value returns zero. Random bits should be gathered from * + * sources external to the immediate program. Examples would be the system clock (good for * + * a few bits), the delay between player keystrokes (good for a few bits per keystroke), * + * etc. When gathering random bits from integers, use the low order bits (since they * + * characteristically are less predictable and more 'random' than the others). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bits required in order to fully seed this random * + * number generator. * + * * + * WARNINGS: Even if this routine returns zero, you are still allowed and encouraged to feed * + * more random bits when the opportunity arrises. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +int RandomStraw::Seed_Bits_Needed(void) const +{ + const int total = sizeof(Random) * CHAR_BIT; + if (SeedBits < total) { + return(total - SeedBits); + } + return(0); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * * + * This routine should be called to feed a single random bit to the random straw object. * + * You must feed as many bits as requested by the Seed_Bits_Needed() function. Submitting * + * additional bits is not only allowed, but encouraged. * + * * + * INPUT: seed -- The bit (lowest order bit) to feed to the random number seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Bit(int seed) +{ + char * ptr = ((char *)&Random[0]) + ((SeedBits / CHAR_BIT) % sizeof(Random)); + char frac = (char)(1 << (SeedBits & (CHAR_BIT-1))); + + if (seed & 0x01) { + *ptr ^= frac; + } + SeedBits++; + + if (SeedBits == (sizeof(Random) * CHAR_BIT)) { + Scramble_Seed(); + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * * + * This will submit 8 bits (as specified) to the random number seed. * + * * + * INPUT: seed -- The seed bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Byte(char seed) +{ + for (int index = 0; index < CHAR_BIT; index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * * + * This will submit 16 bits to the accumulated seed. * + * * + * INPUT: seed -- The 16 bits to submit to the seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Short(short seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * * + * This will submit a full 32 bits to the accumulated seed value. * + * * + * INPUT: seed -- The 32 bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Long(long seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * * + * This routine is called when a full set of seed bits has been accumulated. It will take * + * the existing seed and scramble the bits. An effective way of doing this is to use the * + * Secure Hash Algorithm. It is necessary to have this routine because there might be * + * some coorelation in the seed bits. Even more important is that this routine will result * + * in every bit of the seed having a scrambling effect on every other bit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Scramble_Seed(void) +{ + SHAEngine sha; + + for (int index = 0; index < sizeof(Random); index++) { + char digest[20]; + + sha.Hash(&Random[0], sizeof(Random)); + sha.Result(digest); + + int tocopy = sizeof(digest) < (sizeof(Random)-index) ? sizeof(digest) : (sizeof(Random)-index); + memmove(((char *)&Random[0]) + index, digest, tocopy); + } +} + + +/*********************************************************************************************** + * RandomStraw::Get -- Fetch random data. * + * * + * This routine will fetch random data and store it into the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to fill with random data. * + * * + * length -- The number of bytes to store into the buffer. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. This will always * + * be the number of bytes requested since random numbers are unexhaustible. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + * 07/10/1996 JLB : Revamped to make cryptographically secure. * + *=============================================================================================*/ +int RandomStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Straw::Get(source, slen)); + } + + int total = 0; + while (slen > 0) { + *(char *)source = (char)Random[Current++]; + Current = Current % (sizeof(Random) / sizeof(Random[0])); + source = (char*)source + sizeof(char); + slen--; + total++; + } + return(total); +} diff --git a/REDALERT/RNDSTRAW.H b/REDALERT/RNDSTRAW.H new file mode 100644 index 000000000..c19f678a4 --- /dev/null +++ b/REDALERT/RNDSTRAW.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNDSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNDSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RNDSTRAW_H +#define RNDSTRAW_H + + +#include "straw.h" +#include "random.h" + +/* +** This is a straw terminator class. It will generate random numbers to fill the data request. +** Unlike regular straw terminators, this class will never run out of "data". +*/ +class RandomStraw : public Straw +{ + public: + RandomStraw(void); + virtual ~RandomStraw(void); + + virtual int Get(void * source, int slen); + + void Reset(void); + void Seed_Bit(int seed); + void Seed_Byte(char seed); + void Seed_Short(short seed); + void Seed_Long(long seed); + + int Seed_Bits_Needed(void) const; + + private: + /* + ** Counter of the number of seed bits stored to this random number + ** generator. + */ + int SeedBits; + + /* + ** The current random generator to use when fetching the next random + ** byte of data. + */ + int Current; + + /* + ** Array of generators. There must be at least 448 bits of random number seed + ** in order to be reasonably secure, however, using 1024 bits would be best. + */ + RandomClass Random[32]; + + void Scramble_Seed(void); + + RandomStraw(RandomStraw & rvalue); + RandomStraw & operator = (RandomStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/RNG.H b/REDALERT/RNG.H new file mode 100644 index 000000000..50161696f --- /dev/null +++ b/REDALERT/RNG.H @@ -0,0 +1,51 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNG.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RNG_H +#define RNG_H + +/* +** This is an abstract interface class for a random number generator. It serves only to +** provide random numbers. +*/ +class RandomNumberGenerator { + public: + virtual ~RandomNumberGenerator() {} + + virtual void Get_Block(void * output, unsigned int size) = 0; +}; + + +#endif + diff --git a/REDALERT/ROTBMP.CPP b/REDALERT/ROTBMP.CPP new file mode 100644 index 000000000..10ac8cef6 --- /dev/null +++ b/REDALERT/ROTBMP.CPP @@ -0,0 +1,414 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ROTBMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ROTBMP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0)//PG +//#include "function.h" +#include "watcom.h" +#include "rotbmp.h" +#define FILE_H +#define WWMEM_H +#include + + +int Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int angle); + +struct WPPOINT { + int x; + int y; +}; + +/*************************************************************************** + * Rotate_bitmap -- rotate a bitmap from srcvp to destvp * + * * + * INPUT: note that angles are 0 - 255 (best if used in increments of 4!) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * destvp should be a square. width and height should be equal to : * + * MAX(srcvp->width,srcvp->height) * 1.7 (square root of 2!!) * + * * + * * + * HISTORY: * + * 01/02/1996 BP : Created. * + *=========================================================================*/ +Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int a) +{ + int shift = 7; + int fixpoint1 = 1 << shift; // this is a fixed point 1 + int Deltax; + int Deltay; + int sx,sy,dx,dy; + int Error = 0; + int Decimal = 0; + // this is used if I walk in Y + int buffwidth = srcvp->Get_Width() + srcvp->Get_XAdd(); + int buffwid2 = destvp->Get_Width() + destvp->Get_XAdd(); + char * dest; + char * src; + int sa,ca; + int t; + int x,y; + int rx,ry; + int w,h; + int dw = destvp->Get_Width(); + int dh = destvp->Get_Height(); + WPPOINT sp[4]; + WPPOINT dp[4]; + sa = Sin256[a]; + ca = Cos256[a]; + // get rectangle size + x = 0; + y = 0; + w = srcvp->Get_Width(); + h = srcvp->Get_Height(); + + int halfws = w >> 1; + int halfhs = h >> 1; + int halfwd = dw >> 1; + int halfhd = dh >> 1; + + + // make the src rectangle + sp[0].x = x; + sp[0].y = y; + + sp[1].x = x + w; + sp[1].y = y; + + // now calculate the rotated rectangle + dp[0].x = ( ((sp[0].x - halfws) * ca) - ((sp[0].y - halfhs) * sa) ) >> shift; + dp[0].x += halfwd; + + dp[0].y = ( ((sp[0].x - halfws) * sa) + ((sp[0].y - halfhs) * ca) ) >> shift; + dp[0].y += halfhd; + + dp[1].x = ( ((sp[1].x - halfws) * ca) - ((sp[1].y - halfhs) * sa) ) >> shift; + dp[1].x += halfwd; + + dp[1].y = ( ((sp[1].x - halfws) * sa) + ((sp[1].y - halfhs) * ca) ) >> shift; + dp[1].y += halfhd; + + rx = dp[0].x; + ry = dp[0].y; + // now calculate slope + + // diff from new to old x + + Deltax = (dp[1].x - dp[0].x); + + // diff from new to old y + + Deltay = (dp[1].y - dp[0].y); + + // handle the easy cases + + // no change in x + int r; + if (!Deltax) { // must be 90 or 270 degree transpose! + if (Deltay < 0) { + // walk across source in the x + dir + // walk across dest in the y - dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + dx, ry - dy); + for (r = 0; r< w; r++) { + // transparency + if (* src) + *dest = *src; + src++; + dest -= buffwid2; + } + y++; + dx++; + } + } else { + // walk across source in the x + dir + // walk across dest in the y + dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - dx, ry + dy); + for (r = 0; r< w; r++) { + // transparency + if (*src) + *dest = *src; + src++; + dest += buffwid2; + } + y++; + dx++; + } + } + return 0; + } + // no change in y + + if (!Deltay) { // must be 0 or 180 degree transpose ! + if (Deltax < 0) { + y = 0; + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - x , ry - y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest--; + src++; + } + } + } else { + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + x, ry + y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest++; + src++; + } + } + } + return 0; + } + + + // ok now the hard part + + // make them 16.16 + if ( ABS(Deltax) < ABS(Deltay)) { // ok this means we want to walk in y + + // walk in + x in the src and + // walk in + y in the dest + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + int yinc1 = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + } + Decimal = ( Deltax << shift) / Deltay ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratio between the source height and the dest height + // as the rectangle rotates the height and width change + int DeltaH = (w << shift) / Deltay; + int Error2 = 0; + sy = 0; + for (int r = 0; r< h ;r++) { + // now we walk across the top calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start in dest + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + + // this is the end point of the line + + int y2 = ( ( ((w) - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + y2 += halfhd; + + // length of line + + int diff = ABS(y2 - ry); + + // if walking backwards reveres diff to reflect sign + + src = MAKE_PTR(srcvp, x, y + sy); + + dest = MAKE_PTR(destvp, rx, ry); + + Error = 0; + Error2 = 0; + char * baseptr = src; + // while walking line + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaH; + dest += buffwid2; + Error += Decimal; + src = baseptr + (Error2 >> shift) ; + // this is time to inc x in src y in dest + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += xinc; + } + } + sy += yinc; + } + return 0; + } else { // else we walk in X + int lasterror = 0; + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + sy = sy + h - 1; + yinc = -1; + } + + + Decimal = ( Deltay << shift) / Deltax ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratios between the source width and the dest width + // as the rectangle rotates the actual size changes! + + int DeltaW = (w << shift) / Deltax; + int Error2 = 0; + for (int r = 0; r< h ;r++) { + sx = 0; + + // now we walk across the side calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start + + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + // this is the other side of the box + + int x2 = ( ( ((sx + w) - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + + x2 += halfwd; + + int diff = x2 - rx; + + + dx = 0; + dy = 0; + + if (xinc == -1) { + diff = -diff; + } + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + Error = 0; + Error2 = 0; + char * baseptr = src; + + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaW; + rx++; + dest += xinc; + Error += Decimal; + src = baseptr + (Error2 >> shift); + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += buffwid2; + } + } + sy += yinc; + } + } + return 0; +} +#endif \ No newline at end of file diff --git a/REDALERT/ROTBMP.H b/REDALERT/ROTBMP.H new file mode 100644 index 000000000..88158d483 --- /dev/null +++ b/REDALERT/ROTBMP.H @@ -0,0 +1,112 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// list of 256 sines + +#define MAKE_PTR(vp,x,y) (char *) (vp->Get_Offset() + ((y) * (vp->Get_Width() + vp->Get_XAdd())) + (x)) +#define MAKE_PTR2(vp,x,y) (char *) (vp->Get_Offset() + (((y)<<1) * (vp->Get_Width() + vp->Get_XAdd())) + ((x)<<1)) + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +long Cos256[] = { + 128, 127, 127, 127, 127, 127, 126, 126, + 125, 124, 124, 123, 122, 121, 120, 119, + 118, 116, 115, 114, 112, 111, 109, 107, + 106, 104, 102, 100, 98, 96, 94, 92, + 90, 87, 85, 83, 80, 78, 75, 73, + 70, 68, 65, 62, 59, 57, 54, 51, + 48, 45, 42, 39, 36, 33, 30, 27, + 24, 21, 18, 14, 11, 8, 5, 2, + 0, -3, -7, -10, -13, -16, -19, -22, + -25, -28, -31, -35, -38, -41, -44, -46, + -49, -52, -55, -58, -61, -63, -66, -69, + -72, -74, -77, -79, -82, -84, -86, -89, + -91, -93, -95, -97, -99, -101, -103, -105, + -107, -108, -110, -112, -113, -114, -116, -117, + -118, -119, -120, -121, -122, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -116, -114, -113, -112, -110, -108, -107, + -105, -103, -101, -99, -97, -95, -93, -91, + -89, -86, -84, -82, -79, -77, -74, -72, + -69, -66, -64, -61, -58, -55, -52, -49, + -46, -44, -41, -38, -35, -31, -28, -25, + -22, -19, -16, -13, -10, -7, -3, 0, + 2, 5, 8, 11, 14, 18, 21, 24, + 27, 30, 33, 36, 39, 42, 45, 48, + 51, 54, 57, 59, 62, 65, 68, 70, + 73, 75, 78, 80, 83, 85, 87, 90, + 92, 94, 96, 98, 100, 102, 104, 106, + 107, 109, 111, 112, 114, 115, 116, 118, + 119, 120, 121, 122, 123, 124, 124, 125, + 126, 126, 127, 127, 127, 127, 127, 127, +}; +long Sin256[] = { + 0, 3, 6, 9, 12, 15, 18, 21, + 25, 28, 31, 34, 37, 40, 43, 46, + 49, 52, 54, 57, 60, 63, 66, 68, + 71, 73, 76, 79, 81, 83, 86, 88, + 90, 92, 95, 97, 99, 101, 103, 104, + 106, 108, 110, 111, 113, 114, 115, 117, + 118, 119, 120, 121, 122, 123, 124, 125, + 125, 126, 126, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 126, 126, 125, + 125, 124, 123, 123, 122, 121, 120, 119, + 117, 116, 115, 113, 112, 110, 109, 107, + 105, 104, 102, 100, 98, 96, 94, 91, + 89, 87, 85, 82, 80, 77, 75, 72, + 70, 67, 64, 61, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, + 23, 20, 17, 14, 11, 7, 4, 1, + -1, -4, -7, -11, -14, -17, -20, -23, + -26, -29, -32, -35, -38, -41, -44, -47, + -50, -53, -56, -59, -61, -64, -67, -70, + -72, -75, -77, -80, -82, -85, -87, -89, + -91, -94, -96, -98, -100, -102, -104, -105, + -107, -109, -110, -112, -113, -115, -116, -117, + -119, -120, -121, -122, -123, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -115, -114, -113, -111, -110, -108, -106, + -104, -103, -101, -99, -97, -95, -92, -90, + -88, -86, -83, -81, -79, -76, -73, -71, + -68, -66, -63, -60, -57, -54, -52, -49, + -46, -43, -40, -37, -34, -31, -28, -25, + -21, -18, -15, -12, -9, -6, -3, 0, +}; diff --git a/REDALERT/RULES.CPP b/REDALERT/RULES.CPP new file mode 100644 index 000000000..68d36ac5c --- /dev/null +++ b/REDALERT/RULES.CPP @@ -0,0 +1,1074 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Difficulty_Get -- Fetch the difficulty bias values. * + * RulesClass::AI -- Processes the AI control constants from the database. * + * RulesClass::General -- Process the general main game rules. * + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * RulesClass::Land_Types -- Inits the land type values. * + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * RulesClass::Powerups -- Process the powerup values from the database. * + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * RulesClass::RulesClass -- Default constructor for rules class object. * + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * Techno_Get -- Get rule data common for all techno type objects. * + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * RulesClass::Objects -- Fetch all the object characteristic values. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "vortex.h" + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = fixed(100, 256) * val; + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * RulesClass::RulesClass -- Default constructor for rules class object. * + * * + * This is the default constructor for the rules class object. Although it initializes the * + * rule data with default values, it is expected that they will all be overridden by the * + * rules control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +RulesClass::RulesClass(void) : + TurboBoost("1.5"), + AttackInterval(3), + AttackDelay(5), + PowerEmergencyFraction(fixed::_3_4), + BadgerBombCount(1), + AirstripRatio(".12"), + AirstripLimit(5), + HelipadRatio(".12"), + HelipadLimit(5), + TeslaRatio(".16"), + TeslaLimit(10), + AARatio(".14"), + AALimit(10), + DefenseRatio(".5"), + DefenseLimit(40), + WarRatio(".1"), + WarLimit(2), + BarracksRatio(".16"), + BarracksLimit(2), + RefineryLimit(4), + RefineryRatio(".16"), + BaseSizeAdd(3), + PowerSurplus(50), + InfantryReserve(2000), + InfantryBaseMult(2), + ChronoDuration(3), + WaterCrateChance(".2"), + SoloCrateMoney(2000), + GPSTechLevel(0), + UnitCrateType(UNIT_NONE), + PatrolTime(".016"), + TeamDelay(".6"), + CloakDelay(0), + GameSpeedBias(1), + NervousBias(1), + VortexRange(10*CELL_LEPTON_W), + VortexSpeed((MPHType)10), + VortexDamage(200), + VortexChance(".2"), + ExplosionSpread(fixed::_1_2), + SupressRadius(CELL_LEPTON_W), + ParaInfantryTechLevel(10), + SpyPlaneTechLevel(10), + ParaBombTechLevel(10), + MaxIQ(5), + IQSuperWeapons(4), + IQProduction(5), + IQGuardArea(4), + IQRepairSell(3), + IQCrush(2), + IQScatter(3), + IQContentScan(4), + IQAircraft(4), + IQHarvester(3), + IQSellBack(2), + SilverCrate(CRATE_HEAL_BASE), + WoodCrate(CRATE_MONEY), + WaterCrate(CRATE_MONEY), + CrateMinimum(1), + CrateMaximum(255), + LZScanRadius(16*CELL_LEPTON_W), + MPDefaultMoney(3000), + MPMaxMoney(10000), + IsMPShadowGrow(true), + IsMPBasesOn(true), + IsMPTiberiumGrow(true), + IsMPCrates(true), + IsMPAIPlayers(false), + IsMPCaptureTheFlag(false), + DropZoneRadius(4*CELL_LEPTON_W), + MessageDelay(".6"), + SavourDelay(".03"), + AVMineDamage(1200), + APMineDamage(1000), + MaxPlayers(8), + BaseDefenseDelay(fixed::_1_4), + SuspendPriority(20), + SuspendDelay(2), + SurvivorFraction(fixed::_1_2), + ReloadRate(".05"), + AutocreateTime(5), + BuildupTime(".05"), + OreDumpRate(2), + AtomDamage(1000), + IsComputerParanoid(true), + IsCurleyShuffle(false), + IsFlashLowPower(true), + IsCompEasyBonus(true), + IsFineDifficulty(false), + IsExplosiveHarvester(false), + IsMCVDeploy(false), + IsAllyReveal(true), + IsSeparate(false), + IsTreeTarget(false), + IsMineAware(true), + IsTGrowth(true), + IsTSpread(true), + IsNamed(false), + IsAutoCrush(false), + IsSmartDefense(false), + IsScatter(false), + IsChronoKill(true), + ProneDamageBias(fixed::_1_2), + QuakeDamagePercent(".33"), + QuakeChance(".2"), + GrowthRate(2), + ShroudRate(4), + CrateTime(10), + TimerWarning(2), + ChronoTechLevel(1), + SonarTime(14), + ChronoTime(3), + ParaBombTime(14), + ParaInfantryTime(2), + ParaSaboteurTime(14), + SpyTime(2), + IronCurtainTime(14), + GPSTime(1), + NukeTime(14), + SpeakDelay(2), + DamageDelay(1), + Gravity(3), + GapShroudRadius(10), + GapRegenInterval(".1"), + RadarJamRadius(10*CELL_LEPTON_W), + Incoming(MPH_IMMOBILE), + MinDamage(1), + MaxDamage(1000), + RepairStep(5), + RepairPercent(fixed::_1_4), + URepairStep(5), + URepairPercent(fixed::_1_4), + RepairRate(".016"), + ConditionGreen(1), + ConditionYellow(fixed::_1_2), + ConditionRed(fixed::_1_4), + RandomAnimateTime(".083"), + BailCount(28), + GoldValue(35), + GemValue(110), + AircraftMax(100), + AnimMax(100), + BuildingMax(500), + BulletMax(40), + FactoryMax(20), + InfantryMax(500), + OverlayMax(1), + SmudgeMax(1), + TeamMax(60), + TeamTypeMax(60), + TemplateMax(1), + TerrainMax(500), + TriggerMax(60), + UnitMax(500), + VesselMax(100), + ProjectileMax(20), + WeaponMax(20), + WarheadMax(20), + TrigTypeMax(80), + CloseEnoughDistance(0x0280), + StrayDistance(0x0200), + CrushDistance(0x0180), + CrateRadius(0x0280), + HomingScatter(0x0200), + BallisticScatter(0x0100), + RefundPercent(fixed::_1_2), + IronCurtainDuration(fixed::_1_2), + BridgeStrength(1000), + BuildSpeedBias(1), + C4Delay(".03"), + RepairThreshhold(1000), + PathDelay(".016"), + MovieTime(fixed::_1_4), + TiberiumShortScan(0x0600), + TiberiumLongScan(0x2000), + HealthBarDisplayMode(HB_SELECTED), + ResourceBarDisplayMode(RB_SELECTED) +{ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; + MTankDistance = 30; + QuakeUnitDamage = 0x080; + QuakeBuildingDamage = 0x040; + QuakeInfantryDamage = 0; + QuakeDelay = 120; + ChronoTankDuration = 0x300; +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + EngineerDamage=(fixed)1 / (fixed)3; // Amount of damage an engineer does + EngineerCaptureLevel=ConditionRed; // Building damage level before engineer can capture +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = 60; +#endif +#endif +} + + +/*********************************************************************************************** + * Difficulty_Get -- Fetch the difficulty bias values. * + * * + * This will fetch the difficulty bias values for the section specified. * + * * + * INPUT: ini -- Reference the INI database to fetch the values from. * + * * + * diff -- Reference to the difficulty class object to fill in with the values. * + * * + * section -- The section identifier to lift the values from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +static void Difficulty_Get(CCINIClass & ini, DifficultyClass & diff, char const * section) +{ + if (ini.Is_Present(section)) { + diff.FirepowerBias = ini.Get_Fixed(section, "FirePower", 1); + diff.GroundspeedBias = ini.Get_Fixed(section, "Groundspeed", 1); + diff.AirspeedBias = ini.Get_Fixed(section, "Airspeed", 1); + diff.ArmorBias = ini.Get_Fixed(section, "Armor", 1); + diff.ROFBias = ini.Get_Fixed(section, "ROF", 1); + diff.CostBias = ini.Get_Fixed(section, "Cost", 1); + diff.RepairDelay = ini.Get_Fixed(section, "RepairDelay", ".02"); + diff.BuildDelay = ini.Get_Fixed(section, "BuildDelay", ".03"); + diff.IsBuildSlowdown = ini.Get_Bool(section, "BuildSlowdown", false); + diff.BuildSpeedBias = ini.Get_Fixed(section, "BuildTime", 1); + diff.IsWallDestroyer = ini.Get_Bool(section, "DestroyWalls", true); + diff.IsContentScan = ini.Get_Bool(section, "ContentScan", false); + } +} + + +/*********************************************************************************************** + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * * + * This routine will fetch the rule data from the control file. * + * * + * INPUT: file -- Reference to the rule file to process. * + * * + * OUTPUT: bool; Was the rule file processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Process(CCINIClass & ini) +{ + BStart(BENCH_RULES); + + General(ini); + MPlayer(ini); + Recharge(ini); + Heap_Maximums(ini); + AI(ini); + Powerups(ini); + Land_Types(ini); + Themes(ini); + IQ(ini); + Objects(ini); + Difficulty(ini); + + BEnd(BENCH_RULES); + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::General -- Process the general main game rules. * + * * + * This fetches the control constants uses for regular game processing. Any game behavior * + * controlling values that don't properly fit in any of the other catagories will be * + * stored here. * + * * + * INPUT: ini -- Reference to the database to fetch the values from. * + * * + * OUTPUT: bool; Was the general section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::General(CCINIClass & ini) +{ + static char const * const GENERAL = "General"; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static char const * const AFTERMATH = "Aftermath"; + + if(ini.Is_Present(AFTERMATH)) { +//debugprint( "NewUnitsEnabled previously %i\n", NewUnitsEnabled ); + NewUnitsEnabled = ini.Get_Int(AFTERMATH, "NewUnitsEnabled", 0); +//debugprint( "NewUnitsEnabled set to %i by Rules\n", NewUnitsEnabled ); + MTankDistance = ini.Get_Int(AFTERMATH,"MTankDistance",MTankDistance); + QuakeUnitDamage = ini.Get_Fixed(AFTERMATH, "QuakeUnitDamage", QuakeUnitDamage); + QuakeBuildingDamage = ini.Get_Fixed(AFTERMATH, "QuakeBuildingDamage", QuakeBuildingDamage); + QuakeInfantryDamage = ini.Get_Int(AFTERMATH,"QuakeInfantryDamage",QuakeInfantryDamage); + QuakeDelay = ini.Get_Int(AFTERMATH,"QuakeDelay",QuakeDelay); + ChronoTankDuration = ini.Get_Fixed(AFTERMATH, "ChronoTankDuration", ChronoTankDuration); +//Mono_Set_Cursor(0,0);Mono_Printf("Chrono duration: %08x \n",ChronoTankDuration);Keyboard->Get();Keyboard->Get(); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = ini.Get_Int(AFTERMATH,"CarrierLaunchDelay",120); +#endif +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + // Engineer changing fields were specifically left out of Aftrmath.ini, thus these values are not found to set. ajw + // Implies interesting security hole if user creates a separate Aftrmath.ini file! + EngineerDamage = ini.Get_Fixed(AFTERMATH, "EngineerDamage", EngineerDamage); // Amount of damage an engineer does + EngineerCaptureLevel = ini.Get_Fixed(AFTERMATH, "EngineerCaptureLevel", EngineerCaptureLevel); // Building damage level before engineer can capture +#endif + } + +#endif + + if (ini.Is_Present(GENERAL)) { + TurboBoost = ini.Get_Fixed(GENERAL, "TurboBoost", TurboBoost); + BadgerBombCount = ini.Get_Int(GENERAL, "BadgerBombCount", BadgerBombCount); + IsCurleyShuffle = ini.Get_Bool(GENERAL, "CurleyShuffle", IsCurleyShuffle); + IsFlashLowPower = ini.Get_Bool(GENERAL, "FlashLowPower", IsFlashLowPower); + IsChronoKill = ini.Get_Bool(GENERAL, "ChronoKillCargo", IsChronoKill); + ChronoDuration = ini.Get_Fixed(GENERAL, "ChronoDuration", ChronoDuration); + IsFineDifficulty = ini.Get_Bool(GENERAL, "FineDiffControl", IsFineDifficulty); + WaterCrateChance = ini.Get_Fixed(GENERAL, "WaterCrateChance", WaterCrateChance); + SoloCrateMoney = ini.Get_Int(GENERAL, "SoloCrateMoney", SoloCrateMoney); + ParaBombTechLevel = ini.Get_Int(GENERAL, "ParabombTech", ParaBombTechLevel); + GPSTechLevel = ini.Get_Int(GENERAL, "GPSTechLevel", GPSTechLevel); + UnitCrateType = ini.Get_UnitType(GENERAL, "UnitCrateType", UnitCrateType); + IsExplosiveHarvester = ini.Get_Fixed(GENERAL, "OreExplosive", IsExplosiveHarvester); + GapRegenInterval = ini.Get_Fixed(GENERAL, "GapRegenInterval", GapRegenInterval); + TeamDelay = ini.Get_Fixed(GENERAL, "TeamDelay", TeamDelay); + CloakDelay = ini.Get_Fixed(GENERAL, "SubmergeDelay", CloakDelay); + GameSpeedBias = ini.Get_Fixed(GENERAL, "GameSpeedBias", GameSpeedBias); + NervousBias = ini.Get_Fixed(GENERAL, "BaseBias", NervousBias); + ExplosionSpread = ini.Get_Fixed(GENERAL, "ExpSpread", ExplosionSpread); + SupressRadius = ini.Get_Lepton(GENERAL, "FireSupress", SupressRadius); + ParaInfantryTechLevel = ini.Get_Int(GENERAL, "ParaTech", ParaInfantryTechLevel); + SpyPlaneTechLevel = ini.Get_Int(GENERAL, "SpyPlaneTech", SpyPlaneTechLevel); + SilverCrate = ini.Get_CrateType(GENERAL, "SilverCrate", SilverCrate); + WoodCrate = ini.Get_CrateType(GENERAL, "WoodCrate", WoodCrate); + WaterCrate = ini.Get_CrateType(GENERAL, "WaterCrate", WaterCrate); + CrateMinimum = ini.Get_Int(GENERAL, "CrateMinimum", CrateMinimum); + CrateMaximum = ini.Get_Int(GENERAL, "CrateMaximum", CrateMaximum); + IsScatter = ini.Get_Bool(GENERAL, "PlayerScatter", IsScatter); + IsSmartDefense = ini.Get_Bool(GENERAL, "PlayerReturnFire", IsSmartDefense); + IsAutoCrush = ini.Get_Bool(GENERAL, "PlayerAutoCrush", IsAutoCrush); + IsNamed = ini.Get_Bool(GENERAL, "NamedCivilians", IsNamed); + IsTGrowth = ini.Get_Bool(GENERAL, "OreGrows", IsTGrowth); + IsTSpread = ini.Get_Bool(GENERAL, "OreSpreads", IsTSpread); + IsMineAware = ini.Get_Bool(GENERAL, "MineAware", IsMineAware); + IsTreeTarget = ini.Get_Bool(GENERAL, "TreeTargeting", IsTreeTarget); + IsSeparate = ini.Get_Bool(GENERAL, "SeparateAircraft", IsSeparate); + DropZoneRadius = ini.Get_Lepton(GENERAL, "DropZoneRadius", DropZoneRadius); + MessageDelay = ini.Get_Fixed(GENERAL, "MessageDelay", MessageDelay); + SavourDelay = ini.Get_Fixed(GENERAL, "SavourDelay", SavourDelay); + AVMineDamage = ini.Get_Int(GENERAL, "AVMineDamage", AVMineDamage); + APMineDamage = ini.Get_Int(GENERAL, "APMineDamage", APMineDamage); + BaseDefenseDelay = ini.Get_Fixed(GENERAL, "BaseDefenseDelay", BaseDefenseDelay); + SuspendPriority = ini.Get_Int(GENERAL, "SuspendPriority", SuspendPriority); + SuspendDelay = ini.Get_Fixed(GENERAL, "SuspendDelay", SuspendDelay); + SurvivorFraction = ini.Get_Fixed(GENERAL, "SurvivorRate", SurvivorFraction); + RadarJamRadius = ini.Get_Lepton(GENERAL, "RadarJamRadius", RadarJamRadius); + ReloadRate = ini.Get_Fixed(GENERAL, "ReloadRate", ReloadRate); + RandomAnimateTime = ini.Get_Fixed(GENERAL, "IdleActionFrequency", RandomAnimateTime); + BuildupTime = ini.Get_Fixed(GENERAL, "BuildupTime", BuildupTime); + OreDumpRate = ini.Get_Int(GENERAL, "OreTruckRate", OreDumpRate); + AtomDamage = ini.Get_Int(GENERAL, "AtomDamage", AtomDamage); + BailCount = ini.Get_Int(GENERAL, "BailCount", BailCount); + BallisticScatter = ini.Get_Lepton(GENERAL, "BallisticScatter", BallisticScatter); + BridgeStrength = ini.Get_Int(GENERAL, "BridgeStrength", BridgeStrength); + BuildSpeedBias = ini.Get_Fixed(GENERAL, "BuildSpeed", BuildSpeedBias); + ConditionGreen = 1; + ConditionRed = ini.Get_Fixed(GENERAL, "ConditionRed", ConditionRed); + ConditionYellow = ini.Get_Fixed(GENERAL, "ConditionYellow", ConditionYellow); + CrateRadius = ini.Get_Lepton(GENERAL, "CrateRadius", CrateRadius); + CrushDistance = ini.Get_Lepton(GENERAL, "Crush", CrushDistance); + DamageDelay = ini.Get_Fixed(GENERAL, "DamageDelay", DamageDelay); + GapShroudRadius = ini.Get_Int(GENERAL, "GapRadius", GapShroudRadius); + GemValue = ini.Get_Int(GENERAL, "GemValue", GemValue); + GoldValue = ini.Get_Int(GENERAL, "GoldValue", GoldValue); + Gravity = ini.Get_Int(GENERAL, "Gravity", Gravity); + GrowthRate = ini.Get_Fixed(GENERAL, "GrowthRate", GrowthRate); + HomingScatter = ini.Get_Lepton(GENERAL, "HomingScatter", HomingScatter); + Incoming = ini.Get_MPHType(GENERAL, "Incoming", MPH_IMMOBILE); + IronCurtainDuration = ini.Get_Fixed(GENERAL, "IronCurtain", IronCurtainDuration); + IsAllyReveal = ini.Get_Bool(GENERAL, "AllyReveal", IsAllyReveal); + IsMCVDeploy = ini.Get_Bool(GENERAL, "MCVUndeploy", IsMCVDeploy); + MaxDamage = ini.Get_Int(GENERAL, "MaxDamage", MaxDamage); + MinDamage = ini.Get_Int(GENERAL, "MinDamage", MinDamage); + ProneDamageBias = ini.Get_Fixed(GENERAL, "ProneDamage", ProneDamageBias); + QuakeDamagePercent = ini.Get_Fixed(GENERAL, "QuakeDamage", QuakeDamagePercent); + QuakeChance = ini.Get_Fixed(GENERAL, "QuakeChance", QuakeChance); + RefundPercent = ini.Get_Fixed(GENERAL, "RefundPercent", RefundPercent); + RepairPercent = ini.Get_Fixed(GENERAL, "RepairPercent", RepairPercent); + RepairStep = ini.Get_Int(GENERAL, "RepairStep", RepairStep); + URepairPercent = ini.Get_Fixed(GENERAL, "URepairPercent", URepairPercent); + URepairStep = ini.Get_Int(GENERAL, "URepairStep", URepairStep); + RepairRate = ini.Get_Fixed(GENERAL, "RepairRate", RepairRate); + ShroudRate = ini.Get_Fixed(GENERAL, "ShroudRate", ShroudRate); + SpeakDelay = ini.Get_Fixed(GENERAL, "SpeakDelay", SpeakDelay); + StrayDistance = ini.Get_Lepton(GENERAL, "Stray", StrayDistance); + CloseEnoughDistance = ini.Get_Lepton(GENERAL, "CloseEnough", CloseEnoughDistance); + TimerWarning = ini.Get_Fixed(GENERAL, "TimerWarning", TimerWarning); + MovieTime = ini.Get_Fixed(GENERAL, "MovieTime", MovieTime); + C4Delay = ini.Get_Fixed(GENERAL, "C4Delay", C4Delay); + ChronoTechLevel = ini.Get_Int(GENERAL, "ChronoTechLevel", ChronoTechLevel); + CrateTime = ini.Get_Fixed(GENERAL, "CrateRegen", CrateTime); + VortexRange = ini.Get_Lepton(GENERAL, "VortexRange", VortexRange); + VortexSpeed = MPHType(_Scale_To_256(ini.Get_Int(GENERAL, "VortexSpeed", VortexSpeed))); + VortexDamage = ini.Get_Int(GENERAL, "VortexDamage", VortexDamage); + VortexChance = ini.Get_Fixed(GENERAL, "VortexChance", VortexChance); + + ChronalVortex.Set_Range(VortexRange / CELL_LEPTON_W); + ChronalVortex.Set_Speed(VortexSpeed); + ChronalVortex.Set_Damage(VortexDamage); + + //ChronalVortex.Set_Range ( ini.Get_Int (GENERAL, "VortexRange", ChronalVortex.Get_Range() ) ); + //ChronalVortex.Set_Speed ( ini.Get_Int (GENERAL, "VortexSpeed", ChronalVortex.Get_Speed() ) ); + //ChronalVortex.Set_Damage ( ini.Get_Int (GENERAL, "VortexDamage", ChronalVortex.Get_Damage() ) ); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * * + * This is used to set the default settings for the multiplayer system. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the multiplayer default override section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::MPlayer(CCINIClass & ini) +{ + static char const * const MPLAYER = "MultiplayerDefaults"; + if (ini.Is_Present(MPLAYER)) { + MPDefaultMoney = ini.Get_Int(MPLAYER, "Money", MPDefaultMoney); + MPMaxMoney = ini.Get_Int(MPLAYER, "MaxMoney", MPMaxMoney); + IsMPShadowGrow = ini.Get_Bool(MPLAYER, "ShadowGrow", IsMPShadowGrow); + IsMPBasesOn = ini.Get_Bool(MPLAYER, "Bases", IsMPBasesOn); + IsMPTiberiumGrow = ini.Get_Bool(MPLAYER, "OreGrows", IsMPTiberiumGrow); + IsMPCrates = ini.Get_Bool(MPLAYER, "Crates", IsMPCrates); + IsMPCaptureTheFlag = ini.Get_Bool(MPLAYER, "CaptureTheFlag", IsMPCaptureTheFlag); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * * + * Use this to set the recharge times for the various super weapons available. * + * * + * INPUT: ini -- Reference to the database. * + * * + * OUTPUT: bool; Was the recharge section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Recharge(CCINIClass & ini) +{ + static char const * const RECHARGE = "Recharge"; + if (ini.Is_Present(RECHARGE)) { + SonarTime = ini.Get_Fixed(RECHARGE, "Sonar", SonarTime); + ChronoTime = ini.Get_Fixed(RECHARGE, "Chrono", ChronoTime); + ParaBombTime = ini.Get_Fixed(RECHARGE, "ParaBomb", ParaBombTime); + ParaInfantryTime = ini.Get_Fixed(RECHARGE, "Paratrooper", ParaInfantryTime); + SpyTime = ini.Get_Fixed(RECHARGE, "SpyPlane", SpyTime); + IronCurtainTime = ini.Get_Fixed(RECHARGE, "IronCurtain", IronCurtainTime); + GPSTime = ini.Get_Fixed(RECHARGE, "GPS", GPSTime); + NukeTime = ini.Get_Fixed(RECHARGE, "Nuke", NukeTime); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * * + * This fetches the maximum heap sizes from the database specified. The heaps will be * + * initialized by this routine as indicated. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the maximum section found and processed? * + * * + * WARNINGS: This process is catastrophic to any data currently existing in the heaps * + * modified. This should only be processed during the game initialization stage. * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Heap_Maximums(CCINIClass & ini) +{ + /* + ** Heap maximum values. + */ + static char const * const MAXIMUMS = "Maximums"; + if (ini.Is_Present(MAXIMUMS)) { + MaxPlayers = ini.Get_Int(MAXIMUMS, "Players", MaxPlayers); + AircraftMax = ini.Get_Int(MAXIMUMS, "Aircraft", AircraftMax); + AnimMax = ini.Get_Int(MAXIMUMS, "Anim", AnimMax); + BuildingMax = ini.Get_Int(MAXIMUMS, "Building", BuildingMax); + BulletMax = ini.Get_Int(MAXIMUMS, "Bullet", BulletMax); + FactoryMax = ini.Get_Int(MAXIMUMS, "Factory", FactoryMax); + InfantryMax = ini.Get_Int(MAXIMUMS, "Infantry", InfantryMax); + OverlayMax = ini.Get_Int(MAXIMUMS, "Overlay", OverlayMax); + SmudgeMax = ini.Get_Int(MAXIMUMS, "Smudge", SmudgeMax); + TeamMax = ini.Get_Int(MAXIMUMS, "Team", TeamMax); + TeamTypeMax = ini.Get_Int(MAXIMUMS, "TeamType", TeamTypeMax); + TemplateMax = ini.Get_Int(MAXIMUMS, "Template", TemplateMax); + TerrainMax = ini.Get_Int(MAXIMUMS, "Terrain", TerrainMax); + TriggerMax = ini.Get_Int(MAXIMUMS, "Trigger", TriggerMax); + UnitMax = ini.Get_Int(MAXIMUMS, "Unit", UnitMax); + VesselMax = ini.Get_Int(MAXIMUMS, "Vessel", VesselMax); + ProjectileMax = ini.Get_Int(MAXIMUMS, "Projectile", ProjectileMax); + WeaponMax = ini.Get_Int(MAXIMUMS, "Weapon", WeaponMax); + WarheadMax = ini.Get_Int(MAXIMUMS, "Warhead", WarheadMax); + TrigTypeMax = ini.Get_Int(MAXIMUMS, "TrigType", TrigTypeMax); + } + + /* + ** Special case: double maximum animations to accomodate lots of action + */ + AnimMax = max(AnimMax, 200); + + /* + ** Any heaps that use the maximums that were just loaded, must + ** be initialized as necessary. + */ + Warheads.Set_Heap(WarheadMax); + new WarheadTypeClass("SA"); + new WarheadTypeClass("HE"); + new WarheadTypeClass("AP"); + new WarheadTypeClass("Fire"); + new WarheadTypeClass("HollowPoint"); + new WarheadTypeClass("Super"); + new WarheadTypeClass("Organic"); + new WarheadTypeClass("Nuke"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WarheadTypeClass("Mechanical"); +#endif + + Weapons.Set_Heap(WeaponMax); + new WeaponTypeClass("Colt45"); + new WeaponTypeClass("ZSU-23"); + new WeaponTypeClass("Vulcan"); + new WeaponTypeClass("Maverick"); + new WeaponTypeClass("Camera"); + new WeaponTypeClass("FireballLauncher"); + new WeaponTypeClass("Sniper"); + new WeaponTypeClass("ChainGun"); + new WeaponTypeClass("Pistol"); + new WeaponTypeClass("M1Carbine"); + new WeaponTypeClass("Dragon"); + new WeaponTypeClass("Hellfire"); + new WeaponTypeClass("Grenade"); + new WeaponTypeClass("75mm"); + new WeaponTypeClass("90mm"); + new WeaponTypeClass("105mm"); + new WeaponTypeClass("120mm"); + new WeaponTypeClass("TurretGun"); + new WeaponTypeClass("MammothTusk"); + new WeaponTypeClass("155mm"); + new WeaponTypeClass("M60mg"); + new WeaponTypeClass("Napalm"); + new WeaponTypeClass("TeslaZap"); + new WeaponTypeClass("Nike"); + new WeaponTypeClass("8Inch"); + new WeaponTypeClass("Stinger"); + new WeaponTypeClass("TorpTube"); + new WeaponTypeClass("2Inch"); + new WeaponTypeClass("DepthCharge"); + new WeaponTypeClass("ParaBomb"); + new WeaponTypeClass("DogJaw"); + new WeaponTypeClass("Heal"); + new WeaponTypeClass("SCUD"); + new WeaponTypeClass("Flamer"); + new WeaponTypeClass("RedEye"); + +#ifdef FIXIT_ANTS + new WeaponTypeClass("Mandible"); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WeaponTypeClass("PortaTesla"); + new WeaponTypeClass("GoodWrench"); + new WeaponTypeClass("SubSCUD"); + new WeaponTypeClass("TTankZap"); + new WeaponTypeClass("APTusk"); + new WeaponTypeClass("Democharge"); +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new WeaponTypeClass("AirAssault"); +#endif + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::AI -- Processes the AI control constants from the database. * + * * + * This will examine the database specified and set the AI override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database that holds the AI overrides. * + * * + * OUTPUT: bool; Was the AI section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::AI(CCINIClass & ini) +{ + static char const * const AI = "AI"; + if (ini.Is_Present(AI)) { + AttackInterval = ini.Get_Fixed(AI, "AttackInterval", AttackInterval); + AttackDelay = ini.Get_Fixed(AI, "AttackDelay", AttackDelay); + PatrolTime = ini.Get_Fixed(AI, "PatrolScan", PatrolTime); + RepairThreshhold = ini.Get_Int(AI, "CreditReserve", RepairThreshhold); + PathDelay = ini.Get_Fixed(AI, "PathDelay", PathDelay); + TiberiumShortScan = ini.Get_Lepton(AI, "OreNearScan", TiberiumShortScan); + TiberiumLongScan = ini.Get_Lepton(AI, "OreFarScan", TiberiumLongScan); + AutocreateTime = ini.Get_Fixed(AI, "AutocreateTime", AutocreateTime); + InfantryReserve = ini.Get_Int(AI, "InfantryReserve", InfantryReserve); + InfantryBaseMult = ini.Get_Int(AI, "InfantryBaseMult", InfantryBaseMult); + PowerSurplus = ini.Get_Int(AI, "PowerSurplus", PowerSurplus); + BaseSizeAdd = ini.Get_Int(AI, "BaseSizeAdd", BaseSizeAdd); + RefineryRatio = ini.Get_Fixed(AI, "RefineryRatio", RefineryRatio); + RefineryLimit = ini.Get_Int(AI, "RefineryLimit", RefineryLimit); + BarracksRatio = ini.Get_Fixed(AI, "BarracksRatio", BarracksRatio); + BarracksLimit = ini.Get_Int(AI, "BarracksLimit", BarracksLimit); + WarRatio = ini.Get_Fixed(AI, "WarRatio", WarRatio); + WarLimit = ini.Get_Int(AI, "WarLimit", WarLimit); + DefenseRatio = ini.Get_Fixed(AI, "DefenseRatio", DefenseRatio); + DefenseLimit = ini.Get_Int(AI, "DefenseLimit", DefenseLimit); + AARatio = ini.Get_Fixed(AI, "AARatio", AARatio); + AALimit = ini.Get_Int(AI, "AALimit", AALimit); + TeslaRatio = ini.Get_Fixed(AI, "TeslaRatio", TeslaRatio); + TeslaLimit = ini.Get_Int(AI, "TeslaLimit", TeslaLimit); + HelipadRatio = ini.Get_Fixed(AI, "HelipadRatio", HelipadRatio); + HelipadLimit = ini.Get_Int(AI, "HelipadLimit", HelipadLimit); + AirstripRatio = ini.Get_Fixed(AI, "AirstripRatio", AirstripRatio); + AirstripLimit = ini.Get_Int(AI, "AirstripLimit", AirstripLimit); + IsCompEasyBonus = ini.Get_Bool(AI, "CompEasyBonus", IsCompEasyBonus); + IsComputerParanoid = ini.Get_Bool(AI, "Paranoid", IsComputerParanoid); + PowerEmergencyFraction = ini.Get_Fixed(AI, "PowerEmergency", PowerEmergencyFraction); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Powerups -- Process the powerup values from the database. * + * * + * This will examine the database and initialize the powerup override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database the the powerup values are to be * + * initialized from. * + * * + * OUTPUT: bool; Was the powerup section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Powerups(CCINIClass & ini) +{ + static char const * const POWERUPS = "Powerups"; + if (ini.Is_Present(POWERUPS)) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + char buffer[128]; + if (ini.Get_String(POWERUPS, CrateNames[crate], "0,NONE", buffer, sizeof(buffer))) { + + /* + ** Share odds. + */ + char * token = strtok(buffer, ","); + if (token) { + strtrim(token); + CrateShares[crate] = atoi(token); + } + + /* + ** Animation to use. + */ + token = strtok(NULL, ","); + if (token) { + strtrim(token); + CrateAnims[crate] = Anim_From_Name(token); + } + + /* + ** Optional data number. + */ + token = strtok(NULL, ","); + if (token != NULL) { + if (strpbrk(token, ".%") != NULL) { + CrateData[crate] = fixed(token) * 256; + } else { + strtrim(token); + CrateData[crate] = atoi(token); + } + } + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Land_Types -- Inits the land type values. * + * * + * This will set the land movement attributes from the database specified. * + * * + * INPUT: ini -- Reference to the database that has the land value overrides. * + * * + * OUTPUT: bool; Was the land type sections found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Land_Types(CCINIClass & ini) +{ + /* + ** Fetch the movement characteristic data for terrain types. + */ + for (LandType land = LAND_FIRST; land < LAND_COUNT; land++) { + static char const * _lands[LAND_COUNT] = { + "Clear", + "Road", + "Water", + "Rock", + "Wall", + "Ore", + "Beach", + "Rough", + "River" + }; + + GroundType * gptr = &Ground[land]; + + if (ini.Is_Present(_lands[land])) { + gptr->Cost[SPEED_FOOT] = ini.Get_Fixed(_lands[land], "Foot", 1); + gptr->Cost[SPEED_TRACK] = ini.Get_Fixed(_lands[land], "Track", 1); + gptr->Cost[SPEED_WHEEL] = ini.Get_Fixed(_lands[land], "Wheel", 1); + gptr->Cost[SPEED_WINGED] = fixed(1); + gptr->Cost[SPEED_FLOAT] = ini.Get_Fixed(_lands[land], "Float", 1); + gptr->Build = ini.Get_Bool(_lands[land], "Buildable", false); + } + } + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * * + * The musical theme availability is controlled by the scenario and the player's house * + * choice. These controls can be specified in the theme control section of the INI * + * database. * + * * + * INPUT: ini -- Reference to the INI database to process. * + * * + * OUTPUT: bool; Was the theme section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Themes(CCINIClass & ini) +{ + static char const * const THEMECONTROL = "ThemeControl"; + + if (ini.Is_Present(THEMECONTROL)) { + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (ini.Is_Present(THEMECONTROL, Theme.Base_Name(theme))) { + + char buffer[128]; + int scen = 1; + int owners = HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS; + + ini.Get_String(THEMECONTROL, Theme.Base_Name(theme), "", buffer, sizeof(buffer)); + char const * token = strtok(buffer, ","); + if (token != NULL) { + scen = atoi(token); + } + + token = strtok(NULL, ","); + if (token != NULL) { + owners = Owner_From_Name(token); + } + + Theme.Set_Theme_Data(theme, scen, owners); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * * + * This will scan the database specified and retrieve the IQ control values from it. These * + * IQ control values are what gives the IQ rating meaning. It fundimentally controls how * + * the computer behaves. * + * * + * INPUT: ini -- Reference to the INI database to read the IQ controls from. * + * * + * OUTPUT: bool; Was the IQ section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::IQ(CCINIClass & ini) +{ + static char const * const IQCONTROL = "IQ"; + if (ini.Is_Present(IQCONTROL)) { + MaxIQ = ini.Get_Int(IQCONTROL, "MaxIQLevels", MaxIQ); + IQSuperWeapons = ini.Get_Int(IQCONTROL, "SuperWeapons", IQSuperWeapons); + IQProduction = ini.Get_Int(IQCONTROL, "Production", IQProduction); + IQGuardArea = ini.Get_Int(IQCONTROL, "GuardArea", IQGuardArea); + IQRepairSell = ini.Get_Int(IQCONTROL, "RepairSell", IQRepairSell); + IQCrush = ini.Get_Int(IQCONTROL, "AutoCrush", IQCrush); + IQScatter = ini.Get_Int(IQCONTROL, "Scatter", IQScatter); + IQContentScan = ini.Get_Int(IQCONTROL, "ContentScan", IQContentScan); + IQAircraft = ini.Get_Int(IQCONTROL, "Aircraft", IQAircraft); + IQHarvester = ini.Get_Int(IQCONTROL, "Harvester", IQHarvester); + IQSellBack = ini.Get_Int(IQCONTROL, "SellBack", IQSellBack); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Objects -- Fetch all the object characteristic values. * + * * + * This will parse the specified INI database and fetch all the object characteristic * + * values specified therein. * + * * + * INPUT: ini -- Reference to the ini database to scan. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Objects(CCINIClass & ini) +{ + /* + ** Fetch the game object values from the rules file. + */ + for (int index = 0; index < Warheads.Count(); index++) { + Warheads.Ptr(index)->Read_INI(ini); + } + + for (int proj = 0; proj < BulletTypes.Count(); proj++) { + BulletTypes.Ptr(proj)->Read_INI(ini); + } + + for (int windex = 0; windex < Weapons.Count(); windex++) { + Weapons.Ptr(windex)->Read_INI(ini); + } + + for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { + UnitTypes.Ptr(uindex)->Read_INI(ini); + } + + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypes.Ptr(iindex)->Read_INI(ini); + } + + for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { + VesselTypes.Ptr(vindex)->Read_INI(ini); + } + + for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { + AircraftTypes.Ptr(aindex)->Read_INI(ini); + } + + for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { + BuildingTypes.Ptr(bindex)->Read_INI(ini); + } + + /* + ** Fetch the house attribute override values. + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTypeClass::As_Reference(house).Read_INI(ini); + } + + /* + ** Fetch the mission control values. + */ + for (MissionType mission = MISSION_FIRST; mission < MISSION_COUNT; mission++) { + MissionControlClass * miss = &MissionControl[mission]; + miss->Mission = mission; + miss->Read_INI(ini); + } + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * * + * This routine is used to fetch the various group settings for the difficulty levels. * + * * + * INPUT: ini -- Reference to the INI database that has the difficulty setting values. * + * * + * OUTPUT: bool; Was the difficulty section found and processed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Difficulty(CCINIClass & ini) +{ +#if 0 + Difficulty_Get(ini, Diff[DIFF_EASY], "Easy"); + Difficulty_Get(ini, Diff[DIFF_NORMAL], "Normal"); + Difficulty_Get(ini, Diff[DIFF_HARD], "Difficult"); +#endif + return(true); +} + + +/*********************************************************************************************** + * Is_MCV_Deploy -- Check if MCV can be deployed. * + * * + * This routine is used to check if the Construction Yard can revert back into an MCV. * + * It allows the special variables to override anything set by the rules. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the Construction Yard revert back into an MCV. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/24/2019 SKY : Created. * + *=============================================================================================*/ +bool Is_MCV_Deploy() +{ + return Special.UseMCVDeploy ? Special.IsMCVDeploy : Rule.IsMCVDeploy; +} \ No newline at end of file diff --git a/REDALERT/RULES.H b/REDALERT/RULES.H new file mode 100644 index 000000000..b4ce1c074 --- /dev/null +++ b/REDALERT/RULES.H @@ -0,0 +1,896 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RULES_H +#define RULES_H + +#include "ccini.h" + +class DifficultyClass +{ + public: + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + fixed RepairDelay; + fixed BuildDelay; + + unsigned IsBuildSlowdown:1; + unsigned IsWallDestroyer:1; + unsigned IsContentScan:1; +}; + +class RulesClass { + public: + + RulesClass(void); + + bool Process(CCINIClass & file); + bool General(CCINIClass & ini); + bool MPlayer(CCINIClass & ini); + bool Recharge(CCINIClass & ini); + bool Heap_Maximums(CCINIClass & ini); + bool AI(CCINIClass & ini); + bool Powerups(CCINIClass & ini); + bool Land_Types(CCINIClass & ini); + bool Themes(CCINIClass & ini); + bool IQ(CCINIClass & ini); + bool Objects(CCINIClass & ini); + bool Difficulty(CCINIClass & ini); + + /* + ** This specifies the turbo boost speed for missiles when they are fired upon + ** aircraft and the weapon is specified as having a turbo boost bonus. + */ + fixed TurboBoost; + + /* + ** This specifies the average number of minutes between each computer attack. + */ + fixed AttackInterval; + + /* + ** This specifies the average minutes delay before the computer will begin + ** its first attack upon the player. The duration is also modified by the + ** difficulty level. + */ + fixed AttackDelay; + + /* + ** If the power ratio falls below this percentage, then a power emergency is + ** in effect. At such times, the computer might decide to sell off some + ** power hungry buildings in order to alleviate the situation. + */ + fixed PowerEmergencyFraction; + + /* + ** The number of badgers that arrive when the parabomb option is used. + */ + int BadgerBombCount; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of airstrips. + */ + fixed AirstripRatio; + + /* + ** Limit the number of airstrips to this amount. + */ + int AirstripLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of helipads. + */ + fixed HelipadRatio; + + /* + ** Limit the number of helipads to this amount. + */ + int HelipadLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of Tesla Coils. + */ + fixed TeslaRatio; + + /* + ** Limit tesla coil production to this maximum. + */ + int TeslaLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of anti-aircraft defense. + */ + fixed AARatio; + + /* + ** Limit anti-aircraft building quantity to this amount. + */ + int AALimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of defensive structures. + */ + fixed DefenseRatio; + + /* + ** This is the limit to the number of defensive building that can be built. + */ + int DefenseLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of war factories. + */ + fixed WarRatio; + + /* + ** War factories are limited to this quantity for the computer controlled player. + */ + int WarLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of infantry producing structures. + */ + fixed BarracksRatio; + + /* + ** No more than this many barracks can be built. + */ + int BarracksLimit; + + /* + ** Refinery building is limited to this many refineries. + */ + int RefineryLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of refineries. + */ + fixed RefineryRatio; + + /* + ** The computer is limited in the size of the base it can build. It is limited to the + ** size of the largest human opponent base plus this surplus count. + */ + int BaseSizeAdd; + + /* + ** If the power surplus is less than this amount, then the computer will + ** build power plants. + */ + int PowerSurplus; + + /* + ** The computer will build infantry if their cash reserve is greater than this amount. + */ + int InfantryReserve; + + /* + ** This factor is multiplied by the number of buildings in the computer's base and infantry + ** are always built until it matches that number. + */ + int InfantryBaseMult; + + /* + ** This specifies the duration that a unit will remain chronoshifted before it + ** will be returned to its starting location. + */ + fixed ChronoDuration; + + /* + ** Percent chance that a water crate will be generated instead of a land + ** crate when crates are on and in a multiplay game. + */ + fixed WaterCrateChance; + + /* + ** Solo play has money crate amount fixed according to this rule value. + */ + int SoloCrateMoney; + + /* + ** GPS tech level control. + */ + int GPSTechLevel; + + /* + ** If a unit type is specified here, then the unit crate will generate + ** a unit of this type (always). + */ + UnitType UnitCrateType; + + /* + ** This is the time to delay between patrol-to-waypoint target scanning. + */ + fixed PatrolTime; + + /* + ** This is the time interval that checking to create teams will span. The + ** smaller this number, the more often checking for team creation will occur. + */ + fixed TeamDelay; + + /* + ** This is the arbitrary delay to make all cloaking objects remain uncloaked + ** before having it recloak. + */ + fixed CloakDelay; + + /* + ** This is an overall game apparent speed bias to use for object + ** movement purposes. + */ + fixed GameSpeedBias; + + /* + ** If a potential target is close to the base then increase + ** the likelyhood of attacking it by this bias factor. + */ + fixed NervousBias; + + /* + ** Controls the Chronal vortex characteristics. + */ + LEPTON VortexRange; + MPHType VortexSpeed; + int VortexDamage; + fixed VortexChance; + + /* + ** When an explosive object explodes, the damage will spread out + ** by this factor. The value represents the number of cells radius + ** that the damage will spread for every 100 points of raw damage at + ** the explosion center point. + */ + fixed ExplosionSpread; + + /* + ** For weapons specially marked to check for nearby friendly buildings + ** when scanning for good targets, this indicates the scan radius. Such + ** weapons will supress firing on enemies if they are in close proximity + ** to allied buildings. + */ + LEPTON SupressRadius; + + /* + ** This is the tech level that para infantry are granted free to the owner + ** of an airstrip. + */ + int ParaInfantryTechLevel; + + /* + ** This is the tech level that spy planes are granted free to the owner of + ** an airstrip. + */ + int SpyPlaneTechLevel; + + /* + ** This is the tech level that the parabombs are granted free to the owner + ** of an airstrip. + */ + int ParaBombTechLevel; + + /* + ** This is the maximum number of IQ settings available. The human player is + ** presumed to be at IQ level zero. + */ + int MaxIQ; + + /* + ** The IQ level at which super weapons will be automatically fired by the computer. + */ + int IQSuperWeapons; + + /* + ** The IQ level at which production is automatically controlled by the computer. + */ + int IQProduction; + + /* + ** The IQ level at which newly produced units start out in guard area mode instead + ** of normal guard mode. + */ + int IQGuardArea; + + /* + ** The IQ level at which the computer will be able to decide what gets repaired + ** or sold. + */ + int IQRepairSell; + + /* + ** At this IQ level or higher, a unit is allowed to automatically try to crush + ** an atagonist if possible. + */ + int IQCrush; + + /* + ** The unit/infantry will try to scatter if an incoming threat + ** is detected. + */ + int IQScatter; + + /* + ** Tech level at which the computer will scan the contents of a transport + ** in order to pick the best target to fire upon. + */ + int IQContentScan; + + /* + ** Aircraft replacement production occurs at this IQ level or higher. + */ + int IQAircraft; + + /* + ** Checks for and replaces lost harvesters. + */ + int IQHarvester; + + /* + ** Is allowed to sell a structure being damaged. + */ + int IQSellBack; + + /* + ** The silver and wood crates in solo play will have these powerups. + */ + CrateType SilverCrate; + CrateType WoodCrate; + CrateType WaterCrate; + + /* + ** This specifies the minimum number of crates to place on the map in spite + ** of the number of actual human players. + */ + int CrateMinimum; + + /* + ** This specifies the crate maximum quantity to use. + */ + int CrateMaximum; + + /* + ** Landing zone maximum alternate zone scan radius. + */ + LEPTON LZScanRadius; + + /* + ** Multiplayer default settings. + */ + int MPDefaultMoney; + int MPMaxMoney; + unsigned IsMPShadowGrow:1; + unsigned IsMPBasesOn:1; + unsigned IsMPTiberiumGrow:1; + unsigned IsMPCrates:1; + unsigned IsMPAIPlayers:1; + unsigned IsMPCaptureTheFlag:1; + + /* + ** Drop zone reveal radius. + */ + LEPTON DropZoneRadius; + + /* + ** This is the delay that multiplayer messages will remain on the screen. + */ + fixed MessageDelay; + + /* + ** Savour delay between when scenario detects end and the actual + ** end of the play. + */ + fixed SavourDelay; + + /* + ** This specifies the damage to inflict for two differnt styles of + ** land mine. + */ + int AVMineDamage; + int APMineDamage; + + /* + ** This is the maximum number of multiplayers allowed. + */ + int MaxPlayers; + + /* + ** This is the delay between 'panic attacks' when the computer's base is under + ** attack. This delay gives the previously assigned units a chance to affect the + ** attacker before the computer sends more. + */ + fixed BaseDefenseDelay; + + /* + ** These values control the team suspension logic for dealing with immedate base threats. + ** When the base is attacked, all teams with less than the specified priority will be + ** temporarily put on hold for the number of minutes specified. + */ + int SuspendPriority; + fixed SuspendDelay; + + /* + ** This serves as the fraction of a building's original cost that is converted + ** into survivors (of some fashion). There are rounding and other marginal + ** fudge effects, but this value is the greatest control over the survivor rate. + */ + fixed SurvivorFraction; + + /* + ** This is the aircraft reload rate expressed in minutes per ammo load. + */ + fixed ReloadRate; + + /* + ** The average time (in minutes) between the computer autocreating a team + ** from the team's autocreate list. + */ + fixed AutocreateTime; + + /* + ** Build up time for buildings (minutes). + */ + fixed BuildupTime; + + /* + ** Ore truck speed for dumping. + */ + int OreDumpRate; + + /* + ** This is the amount of damage done by the atom bomb in solo missions. The + ** damage done during multiplay will be 1/5th this value. + */ + int AtomDamage; + + /* + ** This array controls the difficulty affects on the game. There is one + ** difficulty class object for each difficulty level. + */ + DifficultyClass Diff[DIFF_COUNT]; + + /* + ** Is the computer paranoid? If so, then it will band together with other computer + ** paranoid players when the situation looks rough. + */ + bool IsComputerParanoid:1; + + /* + ** Should helicopters shuffle their position between firing on their + ** target? + */ + bool IsCurleyShuffle:1; + + /* + ** Flash the power bar when the power goes below 100%. + */ + bool IsFlashLowPower:1; + + /* + ** If the computer players will go to easy mode if there is more + ** than one human player, this flag will be true. + */ + bool IsCompEasyBonus:1; + + /* + ** If fine control of difficulty settings is desired, then set this value to true. + ** Fine control allows 5 settings. The coarse control only allows three settings. + */ + bool IsFineDifficulty:1; + + /* + ** If the harvester is to explode more violently than normal + ** if it is carrying cargo, then this flag will be true. + */ + unsigned IsExplosiveHarvester:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + */ + unsigned IsMCVDeploy:1; + + /* + ** If the base is to be revealed to a new ally, then this + ** flag will be true. + */ + unsigned IsAllyReveal:1; + + /* + ** Can the helipad (and airfield) be purchased separately from the associated + ** aircraft. + */ + unsigned IsSeparate:1; + + /* + ** Give target cursor for trees? Doing this will make targetting of trees easier. + */ + unsigned IsTreeTarget:1; + + /* + ** Are friendly units automatically aware of mines so that they can avoid them? + */ + unsigned IsMineAware:1; + + /* + ** If Tiberium is allowed to grow, then this flag will be true. + */ + unsigned IsTGrowth:1; + + /* + ** If Tiberium is allowed to spread, then this flag will be true. + */ + unsigned IsTSpread:1; + + /* + ** Should civilan buildings and civilians display their true name rather than + ** the generic "Civilian Building" and "Civilain"? + */ + unsigned IsNamed:1; + + /* + ** Should player controlled vehicles automatically try to crush nearby infantry + ** instead of required the player to manually direct them to crush. + */ + unsigned IsAutoCrush:1; + + /* + ** Should the player controlled buildings and units automatically return fire when + ** fired upon? + */ + unsigned IsSmartDefense:1; + + /* + ** Should player controlled units try to scatter more easily in order to + ** avoid damage or threats? + */ + unsigned IsScatter:1; + + /* + ** If the chronoshift effect should kill all cargo, then this flag will + ** be set to true. + */ + unsigned IsChronoKill:1; + + /* + ** When infantry are prone or when civilians are running around like crazy, + ** they are less prone to damage. This specifies the multiplier to the damage + ** (as a fixed point number). + */ + fixed ProneDamageBias; + + /* + ** The time quake will do this percentage of damage to all units and buildings + ** in the game. The number is expressed as a fixed point percentage. + */ + fixed QuakeDamagePercent; + + /* + ** Percentage chance that a time quake will occur with each chronoshift use. + */ + fixed QuakeChance; + + /* + ** Ore (Tiberium) growth rate. The value is the number of minutes between + ** growth steps. + */ + fixed GrowthRate; + + /* + ** This specifies the number of minutes between each shroud regrowth process. + */ + fixed ShroudRate; + + /* + ** This is the average minutes between each generation of a random crate + ** to be placed on the map if generating of random crates is indicated. + */ + fixed CrateTime; + + /* + ** This specifies the number of minutes remaining before that if the mission timer + ** gets to this level or below, it will be displayed in red. + */ + fixed TimerWarning; + + /* + ** This specifies the minutes of delay between recharges for these + ** special weapon types. + */ + fixed SonarTime; + fixed ChronoTime; + fixed ParaBombTime; + fixed ParaInfantryTime; + fixed ParaSaboteurTime; + fixed SpyTime; + fixed IronCurtainTime; + fixed GPSTime; + fixed NukeTime; + + /* + ** Other miscellaneous delay times. + */ + fixed SpeakDelay; + fixed DamageDelay; + + /* + ** This is the gravity constant used to control the arcing and descent of ballistic + ** object such as grenades and artillery. + */ + int Gravity; + + /* + ** Gap generators have a shroud radius of this many cells. + */ + int GapShroudRadius; + + /* + ** This is the minute interval between the gap generators refreshing + ** their zones of gapping. + */ + fixed GapRegenInterval; + + /* + ** Mobile radar jammer radius of effect. + */ + LEPTON RadarJamRadius; + + /* + ** The speed at which a projectile that travels at or slower will cause + ** objects in the target location to scatter. This simulates the ability + ** of targets to run for cover if the projectile gives them enough time + ** to react. + */ + MPHType Incoming; + + /* + ** Minimum and maximum damage allowed per shot. + */ + int MinDamage; + int MaxDamage; + + /* + ** This is the rate of repair for units and buildings. The rate is the + ** number of strength points repaired per repair clock tick. The cost of + ** repair is the (fixed point) fractional cost to repair the object based + ** on the full price of the object. Example; a value of 50% means that to + ** repair the object from 1 damage point to full strength would cost 50% of + ** the cost to build it from scratch. + */ + int RepairStep; + fixed RepairPercent; + int URepairStep; + fixed URepairPercent; + + /* + ** This is the rate that objects with self healing will heal. They will repair a bit + ** every 'this' number of minutes. + */ + fixed RepairRate; + + /* + ** These fixed point values are used to determine the status (health bar + ** color) of the game objects. Objects in the 'yellow' are in a cautionary + ** state. Object in the 'red' are in a danger state. + */ + fixed ConditionGreen; + fixed ConditionYellow; + fixed ConditionRed; + + /* + ** Average number of minutes between infantry random idle animations. + */ + fixed RandomAnimateTime; + + /* + ** These control the capacity and value of the ore types that a harvester + ** may carry. The harvester carries a maximum discrete number of 'bails'. + ** The value of each bail depends on the ore it is composed of. + */ + int BailCount; // was STEP_COUNT + int GoldValue; // was GOLD_WORTH + int GemValue; // was GEM_WORTH + + /* + ** This specifies the heap maximum for the various game objects. + */ + int AircraftMax; + int AnimMax; + int BuildingMax; + int BulletMax; + int FactoryMax; + int InfantryMax; + int OverlayMax; + int SmudgeMax; + int TeamMax; + int TeamTypeMax; + int TemplateMax; + int TerrainMax; + int TriggerMax; + int UnitMax; + int VesselMax; + int ProjectileMax; + int WeaponMax; + int WarheadMax; + int TrigTypeMax; + + /* + ** Close enough distance that is used to determine if the object should + ** stop movement when blocked. If the distance to the desired destination + ** is equal to this distance or less, but the path is blocked, then consider + ** the object to have gotten "close enough" to the destination to stop. + */ + LEPTON CloseEnoughDistance; + + /* + ** Stray distance to group team members within. The larger the distance, + ** the looser the teams will move. + */ + LEPTON StrayDistance; + + /* + ** If a vehicle is closer than this range to a target that it can crush + ** by driving over it, then it will try to drive over it instead of firing + ** upon it. The larger the value, the greater the 'bigfoot crush syndrome' is + ** has. + */ + LEPTON CrushDistance; + + /* + ** For area effect crate bonus items will affect all objects within this radius. + */ + LEPTON CrateRadius; + + /* + ** Maximum scatter distances for homing and non-homing projectiles. + */ + LEPTON HomingScatter; + LEPTON BallisticScatter; + + /* + ** This is the refund percentage when selling off buildings and units + ** on the repair pad (service depot). + */ + fixed RefundPercent; + + /* + ** The Iron Curtain invulnerability effect lasts for this many minutes. + */ + fixed IronCurtainDuration; + + /* + ** The strength of bridges is held here. By corollary, the strength of the + ** demolition charge carried by Tanya is equal to this value as well. + */ + int BridgeStrength; + + /* + ** This is the overall build speed bias. Multiply this value by the normal build + ** delay to get the effective build delay. + */ + fixed BuildSpeedBias; + + /* + ** Weapon type array pointer should go here. Dynamic type. + */ + + + /* + ** Warhead type class array pointer should go here. Dynamic type. + */ + + + /* + ** Ground type and speed affect data should go here. + */ + + + /* + ** This is the delay between the time a C4 bomb is planted and the time it will + ** explode. The longer the delay, the greater safety margin for a demolitioner + ** type. The short the delay, the less time the victim has to sell the building + ** off. + */ + fixed C4Delay; + + /* + ** The computer will only repair a structure if it has spare money greater than this + ** amount. The thinking is that this will prevent the computer from frittering away + ** all it's cash on repairing and thus leaving nothing for production of defenses. + */ + int RepairThreshhold; + + /* + ** This is the delay (in minutes) between retries of a failed path. The longer the + ** delay the faster the system, but the longer the units take to react to a blocked + ** terrain event. + */ + fixed PathDelay; + + /* + ** This is the special (debug version only) movie recorder timeout value. Each second + ** results in about 2-3 megabytes. + */ + fixed MovieTime; + + /* + ** This is the level at or above which the chronosphere facility can + ** actually produce the chronosphere effect. Below this tech level, + ** the facility is merely a showpiece and has no effect. + */ + int ChronoTechLevel; + + /* + ** These are the Tiberium scan distances. The short range scan is used to determine if the + ** current field has been exhausted. The long range scan is used when finding a Tiberium + ** field to harvest. Keep these ranges as small as possible. + */ + LEPTON TiberiumShortScan; + LEPTON TiberiumLongScan; + + /* + ** Health bar display mode + */ + enum eHealthBarDisplayMode + { + HB_DAMAGED = 0, + HB_ALWAYS, + HB_SELECTED + } HealthBarDisplayMode; + + /* + ** Resource bar display mode + */ + enum eResourceBarDisplayMode + { + RB_SELECTED = 0, + RB_ALWAYS, + } ResourceBarDisplayMode; +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/RedAlert.vcxproj b/REDALERT/RedAlert.vcxproj new file mode 100644 index 000000000..7161a6d7f --- /dev/null +++ b/REDALERT/RedAlert.vcxproj @@ -0,0 +1,787 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {DA948ED9-EF67-4813-94B7-995BE956786E} + RedAlert + 8.1 + SAK + SAK + SAK + SAK + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + ..\bin\$(PlatformName)\ + $(PlatformName)\$(Configuration)\ + false + + + false + ..\bin\$(PlatformName)\ + $(PlatformName)\$(Configuration)\ + $(ProjectName)I + + + + Level3 + MaxSpeed + true + true + false + ./win32lib + true + false + TRUE_FALSE_DEFINED;ENGLISH;WIN32;NDEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) + 1Byte + false + MultiThreaded + 4800;4244;4996 + false + true + + + true + true + winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + DebugFull + Windows + false + false + false + + + + + Level3 + Disabled + false + ./win32lib + false + true + ProgramDatabase + TRUE_FALSE_DEFINED;ENGLISH;WIN32;_DEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) + false + MultiThreadedDebug + 1Byte + 4800;4244;4996 + true + + + winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + DebugFull + Windows + $(OutDir)$(TargetName).lib + false + false + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + Document + true + 1 + true + 1 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/REDALERT/RedAlert.vcxproj.filters b/REDALERT/RedAlert.vcxproj.filters new file mode 100644 index 000000000..39e5841b6 --- /dev/null +++ b/REDALERT/RedAlert.vcxproj.filters @@ -0,0 +1,1842 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {fd6d77c9-ab70-415f-80a5-2f3e9fafc730} + + + {f4599de7-e877-410c-943f-afc4bef70eb6} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Resource + + + + + Source Files + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + Source Files\win32lib + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files\win32lib + + + Source Files + + + Source Files\win32lib + + + + + Source Files\Resource + + + \ No newline at end of file diff --git a/REDALERT/SAVEDLG.H b/REDALERT/SAVEDLG.H new file mode 100644 index 000000000..a8e2991e3 --- /dev/null +++ b/REDALERT/SAVEDLG.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SAVEDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SAVEDLG_H +#define SAVEDLG_H + +#include "gadget.h" + +class SaveOptionsClass +{ + private: + + enum SaveOptionsClassEnums { + BUTTON_CANCEL=200, + BUTTON_SAVE, + OPTION_WIDTH=216, + OPTION_HEIGHT=122, + OPTION_X=((320 - OPTION_WIDTH) / 2) & ~7, + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + NUMBER_OF_BUTTONS=2, + CAPTION_Y_POS=5, + BORDER1_LEN=49, + BUTTON_CANCEL_X=90, + BUTTON_CANCEL_Y=103, + LISTBOX_X=40, + LISTBOX_Y=24, + LISTBOX_W=136, + LISTBOX_H=72 + }; + + public: + SaveOptionsClass (void) { }; + void Process (void); +}; + + +#endif diff --git a/REDALERT/SAVELOAD.CPP b/REDALERT/SAVELOAD.CPP new file mode 100644 index 000000000..5e4d8365a --- /dev/null +++ b/REDALERT/SAVELOAD.CPP @@ -0,0 +1,1654 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SAVELOAD.CPP 9 3/17/97 1:04a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVELOAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 23, 1994 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Code_All_Pointers -- Code all pointers. * + * Decode_All_Pointers -- Decodes all pointers. * + * Get_Savefile_Info -- gets description, scenario #, house * + * Load_Game -- loads a saved game * + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * Load_Misc_Values -- loads miscellaneous variables * + * MPlayer_Save_Message -- pops up a "saving..." message * + * Put_All -- Store all save game data to the pipe. * + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * Save_Game -- saves a game to disk * + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * Save_Misc_Values -- saves miscellaneous variables * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +//#include "WolDebug.h" + +extern bool DLLSave(Pipe &file); +extern bool DLLLoad(Straw &file); + +extern bool SpawnedFromWChat; +#endif + +//#define SAVE_BLOCK_SIZE 512 +#define SAVE_BLOCK_SIZE 4096 +//#define SAVE_BLOCK_SIZE 1024 + +/* +********************************** Defines ********************************** +*/ +#define SAVEGAME_VERSION (DESCRIP_MAX + \ + 0x01000006 + ( \ + sizeof(AircraftClass) + \ + sizeof(AircraftTypeClass) + \ + sizeof(AnimClass) + \ + sizeof(AnimTypeClass) + \ + sizeof(BaseClass) + \ + sizeof(BuildingClass) + \ + sizeof(BuildingTypeClass) + \ + sizeof(BulletClass) + \ + sizeof(BulletTypeClass) + \ + sizeof(CellClass) + \ + sizeof(FactoryClass) + \ + sizeof(HouseClass) + \ + sizeof(HouseTypeClass) + \ + sizeof(InfantryClass) + \ + sizeof(InfantryTypeClass) + \ + sizeof(LayerClass) + \ + sizeof(MouseClass) + \ + sizeof(OverlayClass) + \ + sizeof(OverlayTypeClass) + \ + sizeof(SmudgeClass) + \ + sizeof(SmudgeTypeClass) + \ + sizeof(TeamClass) + \ + sizeof(TeamTypeClass) + \ + sizeof(TemplateClass) + \ + sizeof(TemplateTypeClass) + \ + sizeof(TerrainClass) + \ + sizeof(TerrainTypeClass) + \ + sizeof(TriggerClass) + \ + sizeof(TriggerTypeClass) + \ + sizeof(UnitClass) + \ + sizeof(UnitTypeClass) + \ + sizeof(VesselClass) + \ + sizeof(ScenarioClass) + \ + sizeof(ChronalVortexClass))) +// sizeof(Waypoint))) + + +static int Reconcile_Players(void); +extern bool Is_Mission_Counterstrike (char *file_name); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Put_All -- Store all save game data to the pipe. * + * * + * This is the bulk processor of the game related save game data. All the game object * + * and state data is stored to the pipe specified. * + * * + * INPUT: pipe -- Reference to the pipe that will receive the save game data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Put_All(Pipe & pipe, int save_net) +{ + /* + ** Save the scenario global information. + */ + pipe.Put(&Scen, sizeof(Scen)); + + /* + ** Save the map. The map must be saved first, since it saves the Theater. + */ + if (!save_net) Call_Back(); + Map.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save all game objects. This code saves every object that's stored in a + ** TFixedIHeap class. + */ + Houses.Save(pipe); + if (!save_net) Call_Back(); + TeamTypes.Save(pipe); + if (!save_net) Call_Back(); + Teams.Save(pipe); + if (!save_net) Call_Back(); + TriggerTypes.Save(pipe); + if (!save_net) Call_Back(); + Triggers.Save(pipe); + if (!save_net) Call_Back(); + Aircraft.Save(pipe); + if (!save_net) Call_Back(); + Anims.Save(pipe); + + if (!save_net) Call_Back(); + + Buildings.Save(pipe); + if (!save_net) Call_Back(); + Bullets.Save(pipe); + if (!save_net) Call_Back(); + Infantry.Save(pipe); + if (!save_net) Call_Back(); + Overlays.Save(pipe); + if (!save_net) Call_Back(); + Smudges.Save(pipe); + if (!save_net) Call_Back(); + Templates.Save(pipe); + if (!save_net) Call_Back(); + Terrains.Save(pipe); + if (!save_net) Call_Back(); + Units.Save(pipe); + if (!save_net) Call_Back(); + Factories.Save(pipe); + if (!save_net) Call_Back(); + Vessels.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save the Logic & Map layers + */ + Logic.Save(pipe); + + int count = MapTriggers.Count(); + pipe.Put(&count, sizeof(count)); + int index; + for (int index = 0; index < MapTriggers.Count(); index++) { + TARGET target = MapTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + count = LogicTriggers.Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < LogicTriggers.Count(); index++) { + TARGET target = LogicTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + count = HouseTriggers[h].Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < HouseTriggers[h].Count(); index++) { + TARGET target = HouseTriggers[h][index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + } + if (!save_net) Call_Back(); + + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Save(pipe); + } + + if (!save_net) Call_Back(); + + /* + ** Save the Score + */ + pipe.Put(&Score, sizeof(Score)); + if (!save_net) Call_Back(); + + /* + ** Save the AI Base + */ + Base.Save(pipe); + if (!save_net) Call_Back(); + + /* + ** Save out the carry over list (if present). First see how + ** many carry over objects are in the list. + */ + int carry_count = 0; + CarryoverClass const * cptr = Carryover; + while (cptr != NULL) { + carry_count++; + cptr = (CarryoverClass const *)cptr->Get_Next(); + } + + if (!save_net) Call_Back(); + + /* + ** Save out the number of objects in the list. + */ + pipe.Put(&carry_count, sizeof(carry_count)); + if (!save_net) Call_Back(); + + /* + ** Now write out the objects themselves. + */ + CarryoverClass const * object_to_write = Carryover; + while (object_to_write != NULL) { + pipe.Put(object_to_write, sizeof(*object_to_write)); + object_to_write = (CarryoverClass const *)object_to_write->Get_Next(); + } + if (!save_net) Call_Back(); + + /* + ** Save miscellaneous variables. + */ + Save_Misc_Values(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save multiplayer values + */ + pipe.Put(&save_net, sizeof(save_net)); // Write out whether we saved the net values so we know if we have to load them again. ST - 10/22/2019 2:10PM + if (save_net) { + Save_MPlayer_Values(pipe); + } + + pipe.Flush(); +} + + +/*************************************************************************** + * Save_Game -- saves a game to disk * + * * + * Saving the Map: * + * DisplayClass::Save() invokes CellClass's Write() for every cell * + * that needs to be saved. A cell needs to be saved if it contains * + * any special data at all, such as a TIcon, or an Occupier. * + * The cell saves its own CellTrigger pointer, converted to a TARGET. * + * * + * Saving game objects: * + * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* + * Save() routine invokes each object's Write() routine, if that * + * object's IsActive is set. * + * * + * Saving the layers: * + * The Map's Layers (Ground, Air, etc) of things that are on the map, * + * and the Logic's Layer of things to process both need to be saved. * + * LayerClass::Save() writes the entire layer array to disk * + * * + * Saving the houses: * + * Each house needs to be saved, to record its Credits, Power, etc. * + * * + * Saving miscellaneous data: * + * There are a lot of miscellaneous variables to save, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 02/27/1996 JLB : Uses simpler game control value save operation. * + *=========================================================================*/ +bool Save_Game(int id, char const * descr, bool ) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to save. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id==-1) { + strcpy(name, NET_SAVE_FILE_NAME); + //save_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + + return Save_Game(name, descr); +} + + + +/* +** Version that takes file name. ST - 9/9/2019 11:10AM +*/ +bool NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve +bool Save_Game(const char *file_name, const char *descr) +{ + NowSavingGame = true; // TEMP MBL: Need to discuss better solution with Steve + + int save_net = 0; // 1 = save network/modem game + + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + save_net = 1; + } + + unsigned scenario; + HousesType house; + + scenario = Scen.Scenario; // get current scenario # + house = PlayerPtr->Class->House; // get current house + + /* + ** Code everybody's pointers + */ + Code_All_Pointers(); + + /* + ** Open the file + */ + BufferIOFileClass file(file_name); + + FilePipe fpipe(&file); + + /* + ** Save the DLLs variables first, so we can do a version check in the DLL when we begin the load + */ + if (RunningAsDLL) { + DLLSave(fpipe); + } + + /* + ** Save the description, scenario #, and house + ** (scenario # & house are saved separately from the actual Scenario & + ** PlayerPtr globals for convenience; we can quickly find out which + ** house & scenario this save-game file is for by reading these values. + ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), + ** which may or may not be a HousesType number; so, saving 'house' + ** here ensures we can always pull out the house for this file.) + */ + char descr_buf[DESCRIP_MAX]; + memset(descr_buf, '\0', sizeof(descr_buf)); + sprintf(descr_buf, "%s\r\n", descr); // put CR-LF after text + //descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL + fpipe.Put(descr_buf, DESCRIP_MAX); + + fpipe.Put(&scenario, sizeof(scenario)); + + fpipe.Put(&house, sizeof(house)); + + /* + ** Save the save-game version, for loading verification + */ + unsigned long version = SAVEGAME_VERSION; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + version++; +#endif + fpipe.Put(&version, sizeof(version)); + + int pos = file.Seek(0, SEEK_CUR); + + /* + ** Store a dummy message digest. + */ + char digest[20]; + fpipe.Put(digest, sizeof(digest)); + + + /* + ** Dump the save game data to the file. The data is compressed + ** and then encrypted. The message digest is calculated in the + ** process by using the data just as it is written to disk. + */ + SHAPipe sha; + BlowPipe bpipe(BlowPipe::ENCRYPT); + LZOPipe pipe(LZOPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LZWPipe pipe(LZWPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LCWPipe pipe(LCWPipe::COMPRESS, SAVE_BLOCK_SIZE); + bpipe.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + + sha.Put_To(fpipe); + bpipe.Put_To(sha); + pipe.Put_To(bpipe); + Put_All(pipe, save_net); + + /* + ** Output the real final message digest. This is the one that is of + ** the data image as it exists on the disk. + */ + pipe.Flush(); + file.Seek(pos, SEEK_SET); + sha.Result(digest); + fpipe.Put(digest, sizeof(digest)); + + pipe.End(); + + Decode_All_Pointers(); + + NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve + + return(true); +} + + +/*************************************************************************** + * Load_Game -- loads a saved game * + * * + * This routine loads the data in the same way it was saved out. * + * * + * Loading the Map: * + * - DisplayClass::Load() invokes CellClass's Load() for every cell * + * that was saved. * + * - The cell loads its own CellTrigger pointer. * + * * + * Loading game objects: * + * - IHeap's Load() routine loads the # of objects stored, and loads * + * each object. * + * - Triggers: Add themselves to the HouseTriggers if they're associated * + * with a house * + * * + * Loading the layers: * + * LayerClass::Load() reads the entire layer array to disk * + * * + * Loading the houses: * + * Each house is loaded in its entirety. * + * * + * Loading miscellaneous data: * + * There are a lot of miscellaneous variables to load, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * If this routine returns false, the entire game will be in an * + * unknown state, so the scenario will have to be re-initialized. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 1/20/97 V.Grippi Added expansion CD check * + *=========================================================================*/ +bool Load_Game(int id) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to load. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id == -1) { + strcpy(name, NET_SAVE_FILE_NAME); + //load_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + return Load_Game(name); +} + + +/* +** Version that takes a file name instead. ST - 9/9/2019 11:13AM +*/ +bool Load_Game(const char *file_name) +{ + int i; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + int load_net = 0; // 1 = save network/modem game + + /* + ** Open the file + */ + RawFileClass file(file_name); + if (!file.Is_Available()) { + return(false); + } + + FileStraw fstraw(file); + + Call_Back(); + + /* + ** Load the DLLs variables first, in case we need to do something different based on version + */ + if (RunningAsDLL) { + DLLLoad(fstraw); + } + + /* + ** Read & discard the save-game's header info + */ + if (fstraw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + if (fstraw.Get(&scenario, sizeof(scenario)) != sizeof(scenario)) { + return(false); + } + + if (fstraw.Get(&house, sizeof(house)) != sizeof(house)) { + return(false); + } + + /* + ** Read in & verify the save-game ID code + */ + unsigned long version; + if (fstraw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } + GameVersion = version; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1) != SAVEGAME_VERSION) ) { + return(false); + } +#else + if (version != SAVEGAME_VERSION /*&& version != 0x0100616D*/){ + return(false); + } +#endif + /* + ** Get the message digest that is embedded in the file. + */ + char digest[20]; + fstraw.Get(digest, sizeof(digest)); + + /* + ** Remember the file position since we must seek back here to + ** perform the real saved game read. + */ + long pos = file.Seek(0, SEEK_CUR); + + /* + ** Pass the rest of the file through the hash straw so that + ** the digest can be compaired to the one in the file. + */ + SHAStraw sha; + sha.Get_From(fstraw); + for (;;) { + if (sha.Get(_staging_buffer, sizeof(_staging_buffer)) != sizeof(_staging_buffer)) break; + } + char actual[20]; + sha.Result(actual); + sha.Get_From(NULL); + + Call_Back(); + + /* + ** Compare the two digests. If they differ then return a failure condition + ** before any damage could be done. + */ + if (memcmp(actual, digest, sizeof(digest)) != 0) { + return(false); + } + + /* + ** Set up the pipe so that the scenario data can be read. + */ + file.Seek(pos, SEEK_SET); + BlowStraw bstraw(BlowStraw::DECRYPT); + LZOStraw straw(LZOStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LZWStraw straw(LZWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LCWStraw straw(LCWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); + + bstraw.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + bstraw.Get_From(fstraw); + straw.Get_From(bstraw); + + /* + ** Clear the scenario so we start fresh; this calls the Init_Clear() routine + ** for the Map, and all object arrays. It has the following important + ** effects: + ** - Every cell is cleared to 0's, via MapClass::Init_Clear() + ** - All heap elements' are cleared + ** - The Houses are Initialized, which also clears their HouseTriggers + ** array + ** - The map's Layers & Logic Layer are cleared to empty + ** - The list of currently-selected objects is cleared + */ + Clear_Scenario(); + + /* + ** Load the scenario global information. + */ + straw.Get(&Scen, sizeof(Scen)); + + /* + ** Fixup the Sessionclass scenario info so we can work out which + ** CD to request later + */ + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + if ( !scenario_file.Is_Available() ){ + + int cd = -1; + if (Is_Mission_Counterstrike (Scen.ScenarioName)) { + cd = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Expansion_AM_Present()) { + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + if (index == 3) cd = 3; + } +#endif + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Mission_Aftermath (Scen.ScenarioName)) { + cd = 3; +#ifdef BOGUSCD + cd = -1; +#endif + } +#endif + RequiredCD = cd; + if (!Force_CD_Available (RequiredCD)) { + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + } else { + /* + ** The scenario is available so set RequiredCD to whatever is currently + ** in the drive. + */ + int current_drive = CCFileClass::Get_CD_Drive(); + RequiredCD = Get_CD_Index(current_drive, 1*60); + } + } + + /* + ** Load the map. The map comes first, since it loads the Theater & init's + ** mixfiles. The map calls all the type-class's Init routines, telling them + ** what the Theater is; this must be done before any objects are created, so + ** they'll be properly created. + */ + Map.Load(straw); + + Call_Back(); + + /* + ** Load the object data. + */ + Houses.Load(straw); + TeamTypes.Load(straw); + Teams.Load(straw); + TriggerTypes.Load(straw); + Triggers.Load(straw); + Aircraft.Load(straw); + Anims.Load(straw); + Buildings.Load(straw); + Bullets.Load(straw); + + Call_Back(); + + Infantry.Load(straw); + Overlays.Load(straw); + Smudges.Load(straw); + Templates.Load(straw); + Terrains.Load(straw); + Units.Load(straw); + Factories.Load(straw); + Vessels.Load(straw); + + /* + ** Load the Logic & Map Layers + */ + Logic.Load(straw); + + int count; + straw.Get(&count, sizeof(count)); + MapTriggers.Clear(); + int index; + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + MapTriggers.Add(As_Trigger(target)); + } + + straw.Get(&count, sizeof(count)); + LogicTriggers.Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + LogicTriggers.Add(As_Trigger(target)); + } + + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + straw.Get(&count, sizeof(count)); + HouseTriggers[h].Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + HouseTriggers[h].Add(As_Trigger(target)); + } + } + + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Load(straw); + } + + Call_Back(); + + /* + ** Load the Score + */ + straw.Get(&Score, sizeof(Score)); + new(&Score) ScoreClass(NoInitClass()); + + /* + ** Load the AI Base + */ + Base.Load(straw); + + /* + ** Delete any carryover pseudo-saved game list. + */ + while (Carryover != NULL) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Load any carryover pseudo-saved game list. + */ + int carry_count = 0; + straw.Get(&carry_count, sizeof(carry_count)); + while (carry_count) { + CarryoverClass * cptr = new CarryoverClass; + assert(cptr != NULL); + + straw.Get(cptr, sizeof(CarryoverClass)); + new (cptr) CarryoverClass(NoInitClass()); + cptr->Zap(); + + if (!Carryover) { + Carryover = cptr; + } else { + cptr->Add_Tail(*Carryover); + } + carry_count--; + } + + Call_Back(); + + /* + ** Load miscellaneous variables, including the map size & the Theater + */ + Load_Misc_Values(straw); + + /* + ** Load multiplayer values + */ + straw.Get(&load_net, sizeof(load_net)); + if (load_net) { + Load_MPlayer_Values(straw); + } + + file.Close(); + Decode_All_Pointers(); + Map.Init_IO(); + Map.Flag_To_Redraw(true); + + /* + ** Fixup any expediency data that can be inferred from the physical + ** data loaded. + */ + Post_Load_Game(load_net); + + /* + ** Re-init unit trackers. They will be garbage pointers after the load + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->Init_Unit_Trackers(); + } + } + + Call_Back(); + + /* + ** Set the required CD to be in the drive according to the scenario + ** loaded. + */ + if (RequiredCD != -2 && !load_net) { + +#ifdef FIXIT_ANTS + /* + ** Determines if this an ant mission. Since the ant mission looks no different from + ** a regular mission, examining of the scenario name is the only way to tell. + */ + if (toupper(Scen.ScenarioName[0]) == 'S' && + toupper(Scen.ScenarioName[1]) == 'C' && + toupper(Scen.ScenarioName[2]) == 'A' && + toupper(Scen.ScenarioName[3]) == '0' && + toupper(Scen.ScenarioName[5]) == 'E' && + toupper(Scen.ScenarioName[6]) == 'A') { + + AntsEnabled = true; + } else{ + AntsEnabled = false; + } +#endif + + if (Scen.Scenario == 1) { + RequiredCD = -1; + } else { +#ifdef FIXIT_ANTS + if (Scen.Scenario > 19 || AntsEnabled) { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(Scen.Scenario >= 36) { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { +#endif //FIXIT_ANTS + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } +#ifdef FIXIT_ANTS + } +#endif //FIXIT_ANTS + } + + }else{ + + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + + /* + ** Fix up the session class variables + */ + for ( int s=0 ; sDescription() == Scen.Description){ + + memcpy (Session.Options.ScenarioDescription, Scen.Description, + sizeof (Session.Options.ScenarioDescription)); + memcpy (Session.ScenarioFileName, Scen.ScenarioName, + sizeof (Session.ScenarioFileName)); + Session.ScenarioFileLength = scenario_file.Size(); + memcpy (Session.ScenarioDigest, Session.Scenarios[s]->Get_Digest(), + sizeof (Session.ScenarioDigest)); + Session.ScenarioIsOfficial = Session.Scenarios[s]->Get_Official(); + Scen.Scenario = s; + Session.Options.ScenarioIndex = s; + break; + } + } + } + } + + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Load_Game Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + + ScenarioInit = 0; + + if (load_net) { + + // Removed as this is ensured by the GlyphX & DLL save/load code. ST - 10/22/2019 5:20PM + //if (!Reconcile_Players()) { // (must do after Decode pointers) + // return(false); + //} + + //!!!!!!!!!! put Fixup_Player_Units() here + Session.LoadGame = true; + } + + Map.Reload_Sidebar(); // re-load sidebar art. + + /* + ** Rescan the scenario file for any rules updates. + */ + CCINIClass ini; + int result = ini.Load(CCFileClass(Scen.ScenarioName), true); + + /* + ** Reset the rules values to their initial settings. + */ + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - But does this incorporate *changes*? - NO. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); \ + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode. + if (load_net) { + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: + readini = Is_Aftermath_Installed(); + break; + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + if (!Force_CD_Available(3)) { // force Aftermath CD in drive. + Prog_End("Load_Game Force_CD_Available(3) failed", true); +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 +if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); +} +#endif +#endif + Emergency_Exit(EXIT_FAILURE); + } + } + CCINIClass mpini; + if (mpini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(mpini); + Rule.Recharge(mpini); + Rule.AI(mpini); + Rule.Powerups(mpini); + Rule.Land_Types(mpini); + Rule.Themes(mpini); + Rule.IQ(mpini); + Rule.Objects(mpini); + Rule.Difficulty(mpini); + } + } + + } +#endif + + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } else { + Theme.Queue_Song(Scen.TransitTheme); + } + + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Rule.IsSmartDefense = true; + } + + return(true); +} + + +/*************************************************************************** + * Save_Misc_Values -- saves miscellaneous variables * + * * + * INPUT: * + * file file to use for writing * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/29/1994 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=========================================================================*/ +bool Save_Misc_Values(Pipe & file) +{ + int i, j; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for saving 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x = PlayerPtr->Class->House; + file.Put(&x, sizeof(x)); + + /* + ** Save frame #. + */ + file.Put(&Frame, sizeof(Frame)); + + /* + ** Save currently-selected objects list. + ** Save the # of ptrs in the list. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + count = selection.Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save the pointers. + */ + for (j = 0; j < count; j++) { + ptr = selection[j]; + file.Put(&ptr, sizeof(ptr)); + } + } + + /* + ** Save the chronal vortex + */ + ChronalVortex.Save(file); + + /* + ** Save Tanya flags. + */ + file.Put(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Put(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*********************************************************************************************** + * Load_Misc_Values -- Loads miscellaneous variables. * + * * + * INPUT: file -- The file to load the misc values from. * + * * + * OUTPUT: Was the misc load process successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool Load_Misc_Values(Straw & file) +{ + ObjectClass * ptr; // for loading 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x; + file.Get(&x, sizeof(x)); +// file.Get(&PlayerPtr, sizeof(PlayerPtr)); + PlayerPtr = HouseClass::As_Pointer((HousesType)x); + + /* + ** Load frame #. + */ + file.Get(&Frame, sizeof(Frame)); + + for (int i = 0; i < SelectedObjectsType::COUNT; i++) { + /* + ** Load currently-selected objects list. + ** Load the # of ptrs in the list. + */ + DynamicVectorClass& selection = CurrentObject.Raw(i); + int count; // # ptrs in 'CurrentObject' + file.Get(&count, sizeof(count)); + + /* + ** Load the pointers. + */ + for (int j = 0; j < count; j++) { + file.Get(&ptr, sizeof(ptr)); + selection.Add(ptr); // add to the list + } + } + + /* + ** Load the chronal vortex + */ + ChronalVortex.Load(file); + + /* + ** Save Tanya flags. + */ + file.Get(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Get(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*************************************************************************** + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_MPlayer_Values(Pipe & file) +{ + Session.Save(file); + file.Put(&BuildLevel, sizeof(BuildLevel)); + file.Put(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Put(&Seed, sizeof(Seed)); + file.Put(&Whom, sizeof(Whom)); + file.Put(&Special, sizeof(SpecialClass)); + file.Put(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_MPlayer_Values(Straw & file) +{ + Session.Load(file); + file.Get(&BuildLevel, sizeof(BuildLevel)); + file.Get(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Get(&Seed, sizeof(Seed)); + file.Get(&Whom, sizeof(Whom)); + file.Get(&Special, sizeof(SpecialClass)); + file.Get(&Options, sizeof(GameOptionsClass)); + + return (true); +} + +/* +** ST - 9/26/2019 11:43AM +*/ +extern void DLL_Code_Pointers(void); +extern void DLL_Decode_Pointers(void); + +/*********************************************************************************************** + * Code_All_Pointers -- Code all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Code_All_Pointers(void) +{ + int i, j; + + /* + ** The Map. + */ + Map.Code_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Code_Pointers(); + Teams.Code_Pointers(); + Triggers.Code_Pointers(); + Aircraft.Code_Pointers(); + Anims.Code_Pointers(); + Buildings.Code_Pointers(); + Bullets.Code_Pointers(); + Infantry.Code_Pointers(); + Overlays.Code_Pointers(); + Smudges.Code_Pointers(); + Templates.Code_Pointers(); + Terrains.Code_Pointers(); + Units.Code_Pointers(); + Factories.Code_Pointers(); + Vessels.Code_Pointers(); + + /* + ** The Layers. + */ + Logic.Code_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Code_Pointers(); + } + + /* + ** The Score. + */ + Score.Code_Pointers(); + + /* + ** The Base. + */ + Base.Code_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + for (j = 0; j < selection.Count(); j++) { + selection[j] = (ObjectClass *)selection[j]->As_Target(); + } + } + + /* + ** DLL data + */ + DLL_Code_Pointers(); + + /* + ** Houses must be coded last, because the Class->House member of the HouseClass + ** is used to code HouseClass pointers for all other objects, and if Class is + ** coded, it will point to a meaningless value. + */ + Houses.Code_Pointers(); +} + + +/*********************************************************************************************** + * Decode_All_Pointers -- Decodes all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Decode_All_Pointers(void) +{ + /* + ** The Map. + */ + Map.Decode_Pointers(); + + /* + ** Decode houses first, so we can properly decode all other objects' + ** House pointers + */ + Houses.Decode_Pointers(); + + /* + ** DLL data + */ + DLL_Decode_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Decode_Pointers(); + Teams.Decode_Pointers(); + Triggers.Decode_Pointers(); + Aircraft.Decode_Pointers(); + Anims.Decode_Pointers(); + Buildings.Decode_Pointers(); + Bullets.Decode_Pointers(); + Infantry.Decode_Pointers(); + Overlays.Decode_Pointers(); + Smudges.Decode_Pointers(); + Templates.Decode_Pointers(); + Terrains.Decode_Pointers(); + Units.Decode_Pointers(); + Factories.Decode_Pointers(); + Vessels.Decode_Pointers(); + + /* + ** The Layers. + */ + Logic.Decode_Pointers(); + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Decode_Pointers(); + } + + /* + ** The Score. + */ + Score.Decode_Pointers(); + + /* + ** The Base. + */ + Base.Decode_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = HouseClass::As_Pointer((HousesType)PlayerPtr); + Whom = PlayerPtr->Class->House; + assert(PlayerPtr != NULL); + + /* + ** Currently-selected objects. + */ + for (int index = 0; index < SelectedObjectsType::COUNT; index++) { + DynamicVectorClass& selection = CurrentObject.Raw(index); + for (int j = 0; j < selection.Count(); j++) { + selection[j] = As_Object((TARGET)selection[j]); + assert(selection[j] != NULL); + } + } + + /* + ** Last-Minute Fixups; to resolve these pointers properly requires all other + ** pointers to be loaded & decoded. + */ + if (Map.PendingObjectPtr) { + Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); + assert(Map.PendingObject != NULL); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); +#ifdef BG + Map.Set_Placement_List(Map.PendingObject->Placement_List(true)); +#endif + } else { + Map.PendingObject = 0; + Map.Set_Cursor_Shape(0); + } +} + + +/*************************************************************************** + * Get_Savefile_Info -- gets description, scenario #, house * + * * + * INPUT: * + * id numerical ID, for the file extension * + * buf buffer to store description in * + * scenp ptr to variable to hold scenario * + * housep ptr to variable to hold house * + * * + * OUTPUT: * + * true = OK, false = error (save-game file invalid) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep) +{ + char name[_MAX_FNAME+_MAX_EXT]; + unsigned long version; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + BufferIOFileClass file(name); + + FileStraw straw(file); + + /* + ** Read in the description, scenario #, and the house + */ + if (straw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF + strcpy(buf, descr_buf); + + if (straw.Get(scenp, sizeof(unsigned)) != sizeof(unsigned)) { + return(false); + } + + if (straw.Get(housep, sizeof(HousesType)) != sizeof(HousesType)) { + return(false); + } + + /* + ** Read & verify the save-game version # + */ + if (straw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1 != SAVEGAME_VERSION)) ) { +#else + if (version != SAVEGAME_VERSION) { +#endif + return(false); + } + return(true); +} + + +/*************************************************************************** + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * * + * This function is for supporting loading a saved multiplayer game. * + * When the game is loaded, we have to figure out which house goes with * + * which entry in the Players vector. We also have to figure out if * + * everyone who was originally in the game is still with us, and if not, * + * turn their stuff over to the computer. * + * * + * So, this function does the following: * + * - For every name in 'Players', makes sure that name is in the House * + * array; if not, it's a fatal error. * + * - For every human-controlled house, makes sure there's a player * + * with that name; if not, it turns that house over to the computer. * + * - Fills in the Player's house ID * + * * + * This assumes that each player MUST keep their name the same as it was * + * when the game was saved! It's also assumed that the network * + * connections have not been formed yet, since Player[i]->Player.ID will * + * be invalid until this routine has been called. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconcile_Players(void) +{ + int i; + int found; + HousesType house; + HouseClass * housep; + + /* + ** If there are no players, there's nothing to do. + */ + if (Session.Players.Count()==0) + return (true); + + /* + ** Make sure every name we're connected to can be found in a House + */ + for (i = 0; i < Session.Players.Count(); i++) { + found = 0; + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + break; + } + } + if (!found) + return (false); + } + + // + // Loop through all Houses; if we find a human-owned house that we're + // not connected to, turn it over to the computer. + // + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + // + // Skip this house if it wasn't human to start with. + // + if (!housep->IsHuman) { + continue; + } + + // + // Try to find this name in the Players vector; if it's found, set + // its ID to this house. + // + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + Session.Players[i]->Player.ID = house; + break; + } + } + + /* + ** If this name wasn't found, remove it + */ + if (!found) { + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; + housep->IsStarted = true; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName, Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + } + } + + // + // If all went well, our Session.NumPlayers value should now equal the value + // from the saved game, minus any players we removed. + // + if (Session.NumPlayers == Session.Players.Count()) { + return (true); + } else { + return (false); + } +} + + +/*************************************************************************** + * MPlayer_Save_Message -- pops up a "saving..." message * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +void MPlayer_Save_Message(void) +{ + //char *txt = Text_String( +} \ No newline at end of file diff --git a/REDALERT/SCENARIO.CPP b/REDALERT/SCENARIO.CPP new file mode 100644 index 000000000..429563a91 --- /dev/null +++ b/REDALERT/SCENARIO.CPP @@ -0,0 +1,3739 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//Mono_Printf("%d %s\n",__LINE__,__FILE__); +/* $Header: /CounterStrike/SCENARIO.CPP 15 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 21, 1996 [JLB] * + * * + * This module handles the scenario reading and writing. Scenario related * + * code that is executed between scenario play can also be here. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Houses -- Assigns multiplayer houses to various players * + * Clear_Flag_Spots -- Clears flag overlays off the map * + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * Clip_Move -- moves in given direction from given cell; clips to map * + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * Do_Lose -- Display losing comments. * + * Do_Restart -- Handle the restart mission process. * + * Do_Win -- Display winning congratulations. * + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * Post_Load_Game -- Fill in an inferred data from the game state. * + * Read_Scenario -- Reads a scenario from disk. * + * Read_Scenario_INI -- Read specified scenario INI file. * + * Remove_AI_Players -- Removes the computer AI houses & their units * + * Restate_Mission -- Handles restating the mission objective. * + * Scan_Place_Object -- places an object >near< the given cell * + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * Set_Scenario_Name -- Creates the INI scenario name string. * + * Start_Scenario -- Starts the scenario. * + * Write_Scenario_INI -- Write the scenario INI file. * + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +extern bool SpawnedFromWChat; +#endif +extern int PreserveVQAScreen; + +void Display_Briefing_Text_GlyphX(); + +extern void GlyphX_Assign_Houses(void); //ST - 8/8/2019 12:35PM +extern bool UseGlyphXStartLocations; //ST - 3/31/2020 9:54AM + +//#include "WolDebug.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +static void Remove_AI_Players(void); +static void Create_Units(bool official); +static CELL Clip_Scatter(CELL cell, int maxdist); +static CELL Clip_Move(CELL cell, FacingType facing, int dist); + +// Made this non-static so we can access it from the updated assign players function. ST - 8/9/2019 10:35AM +int _build_tech[11] = { + 2,2, // Tech level 0 and 1 are the same (tech 0 is never used). + 4, + 5, + 7, + 8, + 9, + 10, + 11, + 12, + 13 +}; + + +#ifdef FRENCH +#define TXT_HACKHACK "Accomplie" +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_ACCOMPLISHED) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Counterstrike (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * * + * This constructs the default scenario control object. Normally, all the default values * + * are meaningless since the act of starting a scenario will fill in all of the values with * + * settings retrieved from the scenario control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ScenarioClass::ScenarioClass(void) : + Difficulty(DIFF_NORMAL), + CDifficulty(DIFF_NORMAL), + Timer(0), + MissionTimer(0), + ShroudTimer(TICKS_PER_MINUTE * Rule.ShroudRate), + Scenario(1), + Theater(THEATER_TEMPERATE), + IntroMovie(VQ_NONE), + BriefMovie(VQ_NONE), + WinMovie(VQ_NONE), + WinMovie2(VQ_NONE), + WinMovie3(VQ_NONE), + WinMovie4(VQ_NONE), + LoseMovie(VQ_NONE), + ActionMovie(VQ_NONE), + TransitTheme(THEME_NONE), + PlayerHouse(HOUSE_GREECE), + CarryOverPercent(0), + CarryOverMoney(0), + CarryOverCap(0), + Percent(0), + BridgeCount(0), + CarryOverTimer(0), + IsBridgeChanged(false), + IsGlobalChanged(false), + IsToCarryOver(false), + IsToInherit(false), + IsTanyaEvac(false), + IsFadingBW(false), + IsFadingColor(false), + IsEndOfGame(false), + IsInheritTimer(false), + IsNoSpyPlane(false), + IsSkipScore(false), + IsOneTimeOnly(false), + IsNoMapSel(false), + IsTruckCrate(false), + IsMoneyTiberium(false), +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40 + AutoSonarTimer( AUTOSONAR_PERIOD ), +#endif + FadeTimer(0) +{ + for (int index = 0; index < ARRAY_SIZE(Waypoint); index++) { + Waypoint[index] = -1; + } + strcpy(Description, ""); + strcpy(ScenarioName, ""); + strcpy(BriefingText, ""); + memset(GlobalFlags, '\0', sizeof(GlobalFlags)); + memset(Views, '\0', sizeof(Views)); +} + + +/*********************************************************************************************** + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * * + * This routine will start the palette to fade to B/W for a brief moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_BW_Fade(void) +{ + IsFadingBW = true; + IsFadingColor = false; + FadeTimer = GRAYFADETIME; +} + + +/*********************************************************************************************** + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * * + * This routine will handle the maintenance of the palette fading effect. It should be * + * called once per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_Fade_AI(void) +{ + if (IsFadingColor) { + if (FadeTimer == 0) { + IsFadingColor = false; + } + fixed newsat = Options.Get_Saturation() * fixed(GRAYFADETIME-FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + } + if (IsFadingBW) { + if (FadeTimer == 0) { + IsFadingBW = false; + } + fixed newsat = Options.Get_Saturation() * fixed(FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + if (!IsFadingBW) { + IsFadingColor = true; + FadeTimer = GRAYFADETIME; + } + } +} + + +/*********************************************************************************************** + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * * + * This routine will set the global flag to the falue (true/false) specified. It will * + * also scan for and spring any triggers that are dependant upon that global. * + * * + * INPUT: global -- The global flag to change. * + * * + * value -- The value to change the global flag to. * + * * + * OUTPUT: Returns with the previous value of the flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool ScenarioClass::Set_Global_To(int global, bool value) +{ + if ((unsigned)global < ARRAY_SIZE(Scen.GlobalFlags)) { + + bool previous = GlobalFlags[global]; + if (previous != value) { + GlobalFlags[global] = value; + IsGlobalChanged = true; + + /* + ** Special case to scan through all triggers and if any are found that depend on this + ** global being set/cleared, then if there is an elapsed time event associated, it + ** will be reset at this time. + */ + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + if ((tp->Class->Event1.Event == TEVENT_GLOBAL_SET || tp->Class->Event1.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event1.Data.Value == global) { + tp->Class->Event2.Reset(tp->Event1); + } + if ((tp->Class->Event2.Event == TEVENT_GLOBAL_SET || tp->Class->Event2.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event2.Data.Value == global) { + tp->Class->Event1.Reset(tp->Event1); + } + } + } + return(previous); + } + return(false); +} + + +/*********************************************************************************************** + * Start_Scenario -- Starts the scenario. * + * * + * This routine will start the scenario. In addition to loading the scenario data, it will * + * play the briefing and action movies. * + * * + * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * + * * + * briefing -- Should the briefing be played? Normally this is true except when the * + * scenario is restarting. * + * * + * OUTPUT: Was the scenario started without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Start_Scenario(char * name, bool briefing) +{ +//BG Theme.Queue_Song(THEME_QUIET); +Theme.Stop(); + IsTanyaDead = SaveTanya; + if (!Read_Scenario(name)) { + return(false); + } + + /* + ** Play the winning movie and then start the next scenario. + */ + RequiredCD = -1; +// if (RequiredCD != -2 && Session.Type == GAME_NORMAL) { +// if (Scen.Scenario == 1) +// RequiredCD = -1; +// else { +// if((Scen.Scenario >= 20 && Scen.ScenarioName[2] == 'G' || Scen.ScenarioName[2] == 'U') || Scen.ScenarioName[2] == 'A' +// || (Scen.ScenarioName[2] == 'M' && Scen.Scenario >= 25)) +// RequiredCD = 2; +// else if(Scen.ScenarioName[2] == 'U') +// RequiredCD = 1; +// else if(Scen.ScenarioName[2] == 'G') +// RequiredCD = 0; +// } +// +//#ifdef FIXIT_FORCE_CD +// Forces the CD to be inserted according to the scenario being loaded. +//Hide_Mouse(); +//VisiblePage.Clear(); +//Show_Mouse(); +//GamePalette.Set(); +//if (!Force_CD_Available(RequiredCD)) { +// Prog_End(); +// exit(EXIT_FAILURE); +//} +//#endif + + + +// } + Theme.Stop(); + + if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.IntroMovie); + Play_Movie(Scen.BriefMovie); + } + + /* + ** If there's no briefing movie, restate the mission at the beginning. + */ +#if 1 // 12/04/2019 - LLL + if (Session.Type == GAME_NORMAL && Scen.BriefMovie == VQ_NONE) { + Display_Briefing_Text_GlyphX(); + } +#else + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + if (Session.Type == GAME_NORMAL && Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { + /* + ** Make sure the mouse is visible before showing the restatement. + */ + while(Get_Mouse_State()) { + Show_Mouse(); + } + Restate_Mission(Scen.ScenarioName, TXT_OK, TXT_NONE); + } +#endif + + if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.ActionMovie, Scen.TransitTheme); + } + + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } + + /* + ** Set the options values, since the palette has been initialized by Read_Scenario + */ + Options.Set(); + + return(true); +} + + +/*********************************************************************************************** + * Set_Scenario_Difficulty -- Sets the difficulty of the scenario. * + * * + * Updates the player's difficulty in single-player mode. * + * * + * INPUT: difficulty -- Scenario difficulty * + * * + * OUTPUT: none * + * * + * WARNINGS: Only works in single-player. * + * Must call Start_Scenario first to initialize the player. * + * * + * HISTORY: * + * 10/02/2019 SKY : Created. * + *=============================================================================================*/ +void Set_Scenario_Difficulty(int difficulty) +{ + if (Session.Type == GAME_NORMAL) { + switch (difficulty) { + case 0: + PlayerPtr->Assign_Handicap(DIFF_EASY); + break; + case 1: + PlayerPtr->Assign_Handicap(DIFF_NORMAL); + break; + case 2: + PlayerPtr->Assign_Handicap(DIFF_HARD); + break; + default: + break; + } + } +} + + +/*********************************************************************************************** + * Read_Scenario -- Reads a scenario from disk. * + * * + * This will read a scenario from disk. Use this to begin a scenario. * + * It doesn't perform any rendering, it merely sets up the system * + * with the proper data. Setting of the right game state will start * + * the scenario running. * + * * + * INPUT: root -- Scenario root filename * + * * + * OUTPUT: none * + * * + * WARNINGS: You must clear out the system variables before calling * + * this function. Use the Clear_Scenario() function. * + * It is assumed that Scenario is set to the current scenario number. * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 02/03/1992 JLB : Uses house identification. * + *=============================================================================================*/ +bool Read_Scenario(char * name) +{ + BStart(BENCH_SCENARIO); + Clear_Scenario(); + ScenarioInit++; + if (Read_Scenario_INI(name)) { +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode case. + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#endif + break; + case GAME_INTERNET: +#ifndef FIXIT_VERSION_3 // Loading of Aftermath rules depends on bAftermathMultiplayer now. + if (Is_Mission_Counterstrike(name)) { + readini = false; // Don't allow AM units on a CS map in WChat + break; + } +#endif // ( Note lack of break; ) + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != 3 ) { +#else + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { +#endif + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + RequiredCD = 3; + if (!Force_CD_Available(RequiredCD)) { // force Aftermath CD in drive. +#ifndef WOLAPI_INTEGRATION + #ifdef WIN32 + if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + } + #endif +#endif + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } + } + CCINIClass ini; + if (ini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + } + } +#endif + Fill_In_Data(); + Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); + } else { + +#if (1) + char message[200]; + if (name) { + sprintf(message, "Failed to load scenario %s", name); + GlyphX_Debug_Print(message); + } else { + GlyphX_Debug_Print("Failed to load scenario"); + } +#else + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); + WWMessageBox().Process(TXT_UNABLE_READ_SCENARIO); + Hide_Mouse(); +#endif + + BEnd(BENCH_SCENARIO); + return(false); + } + ScenarioInit--; + BEnd(BENCH_SCENARIO); + return(true); +} + + +/*********************************************************************************************** + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * * + * This routine is called after the INI file for the scenario has been processed. It will * + * infer the game state from the scenario INI data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Fill_In_Data(void) +{ + /* + ** The basic scenario data load does not contain the full set of + ** game data. We now must fill in the missing pieces. + */ + ScenarioInit++; + int index; + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + + Map.Flag_To_Redraw(true); + + /* + ** Reset the movement zones according to the terrain passability. + */ + Map.Zone_Reset(MZONEF_ALL); + + +#ifdef WIN32 + /* + ** Since the sidebar starts up activated, adjust the home start position so that + ** the right edge of the map will still be visible. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); +// if (Session.Type == GAME_NORMAL) { + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Map.Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); +// } + } +#endif + + /* + ** Handle any data resetting that can be safely inferred from the actual + ** data that has been loaded. + */ + /* + ** Distribute the trigger pointers to the appropriate working lists. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + + assert(tp != NULL); + + if (tp->Attaches_To() & ATTACH_MAP) { + MapTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_GENERAL) { + LogicTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_HOUSE) { + HouseTriggers[tp->House].Add(Find_Or_Make(tp)); + } + } + + ScenarioInit--; + + /* + ** Now go through and set all the cells ringing the map to be visible, so + ** we won't get the wall of shadow at the edge of the map. + */ + int x,y; + for (x = Map.MapCellX-1; x < ((Map.MapCellX + Map.MapCellWidth + 1)); x++) { + Map[XY_Cell(x, Map.MapCellY-1)].IsVisible = + Map[XY_Cell(x, Map.MapCellY-1)].IsMapped = true; + + Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsVisible = + Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsMapped = true; + } + for (y = Map.MapCellY; y < (Map.MapCellY + Map.MapCellHeight); y++) { + Map[XY_Cell(Map.MapCellX-1, y)].IsVisible = + Map[XY_Cell(Map.MapCellX-1, y)].IsMapped = true; + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsVisible = + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsMapped = true; + } + + /* + ** If inheriting from a previous scenario was indicated, then create the carry over + ** objects at this time. + */ + if (Scen.IsToInherit) { + CarryoverClass * cptr = Carryover; + while (cptr != NULL) { + cptr->Create(); + cptr = (CarryoverClass *)cptr->Get_Next(); + } + } + + /* + ** The "allow win" action is a special case that is handled here. The total number + ** of triggers that have this action must be recorded. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + if (tp->Action1.Action == TACTION_ALLOWWIN || + (tp->ActionControl != MULTI_ONLY && tp->Action2.Action == TACTION_ALLOWWIN)) { + + HouseClass::As_Pointer(tp->House)->Blockage++; + } + } + + /* + ** Move available money to silos, if the scenario flag so indicates. + */ + if (Scen.IsMoneyTiberium) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL) { + int tomove = hptr->Capacity - hptr->Tiberium; + hptr->Credits -= tomove; + hptr->Tiberium += tomove; + } + } + } + + /* + ** Count all non-destroyed bridges on the map. + */ + Scen.BridgeCount = Map.Intact_Bridge_Count(); + + Map.All_To_Look(PlayerPtr, true); +} + + +/*********************************************************************************************** + * Post_Load_Game -- Fill in an inferred data from the game state. * + * * + * This routine is typically called after a game has been loaded. Some working data lists * + * can be rebuild from the game state. This working data is rebuilt rather than being * + * stored with the game data file. * + * * + * INPUT: load_multi -- true if we're loading a multiplayer game * + * * + * OUTPUT: none * + * * + * WARNINGS: Although it is safe to call this routine whenever, it is only needed after a * + * game load. * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +void Post_Load_Game(int load_multi) +{ + Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); + // + // Do NOT call Overpass if we're loading a multiplayer game; it calls the + // random # generator, which throws the games out of sync if they were + // saved on different frame #'s. + // + if (!load_multi) { + Map.Overpass(); + } + Scen.BridgeCount = Map.Intact_Bridge_Count(); + Map.Zone_Reset(MZONEF_ALL); +} + + +/*********************************************************************************************** + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * * + * This routine will clear out all data specific to a scenario in * + * preparation for a subsequent scenario data load. This will free * + * all units, animations, and icon maps. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * + * 07/13/1995 JLB : End count down moved here. * + *=============================================================================================*/ +void Clear_Scenario(void) +{ +// TCTCTC -- possibly just use in-place new of scenario object? + + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.Timer = 0; + Scen.ShroudTimer = 0; + Scen.IntroMovie = VQ_NONE; + Scen.BriefMovie = VQ_NONE; + Scen.WinMovie = VQ_NONE; + Scen.WinMovie2 = VQ_NONE; + Scen.WinMovie3 = VQ_NONE; + Scen.WinMovie4 = VQ_NONE; + Scen.LoseMovie = VQ_NONE; + Scen.ActionMovie = VQ_NONE; + Scen.IsNoSpyPlane = false; + Scen.IsTanyaEvac = false; + Scen.IsEndOfGame = false; + Scen.IsInheritTimer = false; + Scen.IsToCarryOver = false; + Scen.IsSkipScore = false; + Scen.IsOneTimeOnly = false; + Scen.IsTruckCrate = false; + Scen.IsMoneyTiberium = false; + Scen.IsNoMapSel = false; + Scen.CarryOverCap = 0; + Scen.CarryOverPercent = 0; + Scen.TransitTheme = THEME_NONE; + Scen.Percent = 0; + + memset(Scen.GlobalFlags, 0, sizeof(Scen.GlobalFlags)); + + MapTriggers.Clear(); + LogicTriggers.Clear(); + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTriggers[house].Clear(); + } + + /* + ** Call everyone's Init routine, except the Map's; for the Map, only call + ** MapClass::Init, which clears the Cell array. The Display::Init requires + ** a Theater argument, and the theater is not known at this point; also, it + ** would reload MixFiles, which isn't desired. Display::Read_INI calls its + ** own Init, which will Init the entire Map hierarchy. + */ + Map.Init_Clear(); + Score.Init(); + Logic.Init(); + + HouseClass::Init(); + ObjectClass::Init(); + TeamTypeClass::Init(); + TeamClass::Init(); + TriggerClass::Init(); + TriggerTypeClass::Init(); + AircraftClass::Init(); + AnimClass::Init(); + BuildingClass::Init(); + BulletClass::Init(); + InfantryClass::Init(); + OverlayClass::Init(); + SmudgeClass::Init(); + TemplateClass::Init(); + TerrainClass::Init(); + UnitClass::Init(); + VesselClass::Init(); + + FactoryClass::Init(); + + Base.Init(); + + CurrentObject.Clear_All(); + + for (int index = 0; index < WAYPT_COUNT; index++) { + Scen.Waypoint[index] = -1; + } + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + bAutoSonarPulse = false; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + Scen.bLocalProposesDraw = false; + Scen.bOtherProposesDraw = false; +#endif + +} + + +/*********************************************************************************************** + * Do_Win -- Display winning congratulations. * + * * + * Perform the win the mission process. This will display any winning movies and the score * + * screen. Followed by the map selection screen and then the load of the new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 01/01/1995 JLB : Carries money forward into next scenario. * + *=============================================================================================*/ +void Do_Win(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ +#ifdef FIXIT_ANTS + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore || AntsEnabled) { +#else + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore ) { +#endif //FIXIT_ANTS + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw (true); + Map.Render(); +#ifdef WIN32 + Fancy_Text_Print(TXT_SCENARIO_WON, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#else + Fancy_Text_Print(TXT_MISSION, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_HACKHACK, x, 110*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#endif + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_ACCOMPLISHED); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.WinMovie); + Play_Movie(Scen.WinMovie2); + Play_Movie(Scen.WinMovie3); + Play_Movie(Scen.WinMovie4); + + Keyboard->Clear(); + + SaveTanya = IsTanyaDead; + Scen.CarryOverTimer = Scen.MissionTimer; +// int timer = Scen.MissionTimer; + + /* + ** Do the ending screens only if not playing back a recorded game. + */ + if (!Session.Play) { + /* + ** If the score presentation should be performed, then do + ** so now. + */ + Keyboard->Clear(); + if (!Scen.IsSkipScore) { + Score.Presentation(); + } + + + if (Scen.IsOneTimeOnly) { + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +// Mono_Printf("Scenario.cpp one time only antsenabled is false\n"); +#endif + return; + } + + /* + ** If this scenario is flagged as ending the game then print the credits and exit. + */ +#if (0)//PG + if (Scen.IsEndOfGame) { + if (PlayerPtr->ActLike == HOUSE_USSR) { + Play_Movie(VQ_SOVFINAL); + } else { + Play_Movie(VQ_ALLYEND); + } + Show_Who_Was_Responsible(); + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + return; + } +#endif + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ + if (Scen.IsNoMapSel) { + // force it to play the second half of scenario 10 +#ifdef FIXIT_ANTS + if (AntsEnabled) { + char scenarioname[24]; + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + Scen.Scenario++; + sprintf(buf, "%02d", Scen.Scenario); + memcpy(&scenarioname[3], buf, 2); + Scen.Set_Scenario_Name(scenarioname); + } else { + Scen.ScenarioName[6] = 'B'; + } + +#else + Scen.ScenarioName[6] = 'B'; +#endif + + } else { + Scen.Set_Scenario_Name(Map_Selection()); + } + + Keyboard->Clear(); + } + + Scen.CarryOverMoney = PlayerPtr->Credits; + + /* + ** If requested, record the scenario's objects in the carry over list + ** for possible use in a future scenario. + */ + if (Scen.IsToCarryOver) { + + /* + ** First delete any existing carry over list. Any old list will be + ** blasted over by the new list -- there is only one logic carryover + ** list to be maintained. + */ + while (Carryover) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Record all objects, that are to be part of the carry over set, into + ** the carry over list. + */ + for (int building_index = 0; building_index < Buildings.Count(); building_index++) { + BuildingClass * building = Buildings.Ptr(building_index); + + if (building && !building->IsInLimbo && building->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(building); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + UnitClass * unit = Units.Ptr(unit_index); + + if (unit && !unit->IsInLimbo && unit->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(unit); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + InfantryClass * infantry = Infantry.Ptr(infantry_index); + + if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(infantry); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + VesselClass * vessel = Vessels.Ptr(vessel_index); + + if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(vessel); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + } + + /* + ** Generate a new scenario filename + */ +// Scen.Set_Scenario_Name(Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, Scen.ScenVar); + Start_Scenario(Scen.ScenarioName); + + /* + ** If the mission timer is to be inheriteded from the previous scenario then do it now. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + +// PlayerPtr->NukePieces = nukes; + + Map.Render(); + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Lose -- Display losing comments. * + * * + * Performs the lose mission processing. This will generally display a "would you like * + * to replay" dialog and then either reload the scenario or set flags such that the main * + * menu will appear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +void Do_Lose(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_SCENARIO_LOST, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_FAIL); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef CHEAT_KEYS +// Mono_Printf("Trying to play lose movie\n"); +#endif //CHEAT_KEYS + Play_Movie(Scen.LoseMovie); + + /* + ** Start same scenario again + */ + GamePalette.Set(); + Show_Mouse(); + if (!Session.Play && !WWMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { + Hide_Mouse(); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + Map.Render(); + } else { + Hide_Mouse(); + GameActive = 0; + } + + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + +#ifdef FIXIT_VERSION_3 // Stalemate games. +/*********************************************************************************************** + * Do_Draw -- Parallels Do_Win and Do_Lose, for multiplayer games that end in a draw. + *=============================================================================================*/ +void Do_Draw(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_WOL_DRAW, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_CONTROL_EXIT); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); +} +#endif + +/*********************************************************************************************** + * Do_Restart -- Handle the restart mission process. * + * * + * This routine is called in the main game loop when the mission must be restarted. This * + * routine will throw away the current game and reload the appropriate mission. The * + * game will "resume" at the start of the mission. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +void Do_Restart(void) +{ + /* + ** Start a timer going, before we restart the scenario + */ + CDTimerClass timer; + timer = TICKS_PER_SECOND * 4; + Theme.Queue_Song(THEME_QUIET); + + WWMessageBox().Process(TXT_RESTARTING, TXT_NONE); + + Map.Set_Default_Mouse(MOUSE_NORMAL); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + /* + ** Make sure the message stays displayed for at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + Map.Render(); +} + + +/*********************************************************************************************** + * Restate_Mission -- Handles restating the mission objective. * + * * + * This routine will display the mission objective (as text). It will also give the * + * option to redisplay the mission briefing video. * + * * + * INPUT: name -- The scenario name. This is the unique identifier for the scenario * + * briefing text as it appears in the "MISSION.INI" file. * + * * + * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * + * requested, or 0 if the return to game options button was selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + * 08/06/1995 JLB : Uses preloaded briefing text. * + *=============================================================================================*/ +bool Restate_Mission(char const * name, int button1, int button2) +{ + if (name) { + + bool brief = true; + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + + if (Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { + button2 = TXT_OK; + button1 = TXT_NONE; + brief = false; + } + + /* + ** If mission object text was found, then display it. + */ + if (strlen(Scen.BriefingText)) { + strcpy(_ShapeBuffer, Scen.BriefingText); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + if (BGMessageBox(_ShapeBuffer, button2, button1)) { + return(true); + } + if (!brief) return(true); + return(false); + } + } + return(false); +} + + +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int BGMessageBox(char const * msg, int btn1, int btn2) +{ +#define BUFFSIZE 511 + char buffer[BUFFSIZE]; + int retval; + bool process; // loop while true + KeyNumType input; // user input + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + BOOL display; // display level + int realval[5]; + int morebutton = 3; // which button says "more": 2 or 3? + + const char * b1txt = Text_String(btn1); + const char * b2txt = Text_String(btn2); +#ifdef FRENCH + const char * b3txt = "SUITE"; +#else +#ifdef GERMAN + const char * b3txt = "MEHR"; +#else + const char * b3txt = "MORE"; +#endif +#endif + + const void *briefsnd = MFCD::Retrieve("BRIEFING.AUD"); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_TYPE]); + + /* + ** If the message won't be needing the 'more' button, get rid of it. + */ + if (strlen(msg) <= BUFFSIZE-1) { + b3txt = ""; + } + +#ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); +#endif + + /* + ** If there's no text for button one, zero it out. + */ + if (*b1txt == '\0') { + b1txt = b2txt; + b2txt = ""; + if(*b1txt == '\0') { + b1txt=0; + } + } + + /* + ** If there's no text for button two, zero it out. However, if there + ** is text for button three, move its text (always "MORE") to button two, + ** and set the morebutton flag to point to button two. Then, clear out + ** button 3. + */ + if (*b2txt == '\0') { + b2txt = 0; + if (*b3txt != '\0') { + b2txt = b3txt; + b3txt = ""; + morebutton = 1; + } + } + + /* + ** If there's no text for button three, zero it out. + */ + if (*b3txt == '\0') b3txt = 0; + + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + char b1char, b2char, b3char; // 1st char of each string + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt) { + b1char = toupper(b1txt[0]); + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max((String_Pixel_Width(b1txt) + 8), 80); + if (b2txt) { + numbuttons = 2; + b2char = toupper(b2txt[0]); + bwidth = max(((int)String_Pixel_Width( b2txt ) + 8), bwidth); +// b1x = x + 10; // left side + + if (b3txt) { + numbuttons = 3; + b3char = toupper(b3txt[0]); + bwidth = max(((int)String_Pixel_Width( b3txt ) + 8), bwidth); + } + + } else { + numbuttons = 1; +// b1x = x + ((width - bwidth) >> 1); // centered + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + int buffend = BUFFSIZE-1; + strncpy(buffer, msg, BUFFSIZE-1); + /* + ** Scan through the string to see if it got clipped, and if so, we'll + ** trim it back to the last space so it'll clip on a word. + */ + if (strlen(buffer) != strlen(msg)) { + while (buffer[buffend] != ' ') buffend--; + buffer[buffend]=0; + } + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + int width; + int height; + Format_Window_String(buffer, 300, width, height); + height += (numbuttons == 0) ? 30 : 60; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, + 0, y + height - (bheight + 5)); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + if (numbuttons) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + + PaletteClass temp; +#ifdef WIN32 + char *filename = "SOVPAPER.PCX"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.PCX"; + } + Load_Title_Screen(filename, &HidPage, (unsigned char*)temp.Get_Data()); +#else + char *filename = "SOVPAPER.CPS"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.CPS"; + } + Load_Uncompress(CCFileClass(filename), HidPage, HidPage, temp); +#endif + HidPage.Blit(SeenPage); + + #ifdef WIN32 + VisiblePage.Blit(seen_buff_save); + #endif + + static unsigned char _scorepal[]={0,1,12,13,4,5,6,7,8,9,10,255,252,253,14,248}; + Set_Font_Palette(_scorepal); + temp.Set(FADE_PALETTE_MEDIUM, Call_Back); + + /* + ** Main Processing Loop. + */ + + int bufindex = 0; + + Keyboard->Clear(); + + Set_Font_Palette(_scorepal); + int xprint = x + 20; + int yprint = y + 25; + do { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + char bufprint[2]; + bufprint[1]=0; + bufprint[0] = buffer[bufindex]; + if (bufprint[0] == '\r' || bufprint[0] == '@') { + xprint = x + 20; + yprint += FontHeight + FontYSpacing; + + } else { + if (bufprint[0] != 20) { + SeenPage.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#ifdef WIN32 + seen_buff_save.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#endif + xprint += Char_Pixel_Width(bufprint[0]); + } + } + if (bufprint[0] == '\r' || bufprint[0] == '@') { +#ifdef WIN32 + Play_Sample(briefsnd, 255, Options.Normalize_Volume(135)); +#else + Play_Sample(briefsnd, 255, Options.Normalize_Volume(45)); +#endif + CDTimerClass cd; + cd = 5; + do { + Call_Back(); + } while(!Keyboard->Check() && cd); + } + } while (buffer[++bufindex]); + + Show_Mouse(); + Keyboard->Clear(); + + if (buttonlist) { + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + switch (input) { + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (KN_ESC): + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } + break; + + case (BUTTON_2|BUTTON_FLAG): + selection = BUTTON_2; + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_1; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + if (b1char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_1; + pressed = true; + } else if (b2txt!=NULL && + b2char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_2; + pressed = true; + } else if (b3txt!=NULL && + b3char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_3; + pressed = true; + } + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_1): + retval = 1; + process = false; + break; + + case (BUTTON_2): + retval = 0; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + if (retval == (morebutton-1) && strlen(msg) > BUFFSIZE-1) { + retval = BGMessageBox(msg + buffend + 1, btn1, btn2); + } + /* + ** Restore the screen. + */ + Hide_Mouse(); + /* + ** Now set the palette, depending on if we're going to show the video or + ** go back to the main menu. + */ + switch (retval) { + case 0: +// BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// SeenPage.Clear(); +//// CCPalette.Set(); +// break; + case 1: + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + SeenPage.Clear(); + break; + default: + break; + } + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + return(retval); +} + + +/*********************************************************************************************** + * Set_Scenario_Name -- Creates the INI scenario name string. * + * * + * This routine is used by the scenario loading and saving code. It generates the scenario * + * INI root file name for the specified scenario parameters. * + * * + * INPUT: * + * buf buffer to store filename in; must be long enough for root.ext * + * scenario scenario number * + * player player type for this game (GDI, NOD, multi-player, ...) * + * dir directional parameter for this game (East/West) * + * var variation of this game (Lose, A/B/C/D, etc) * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * + *=============================================================================================*/ +void ScenarioClass::Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) +{ + Scenario = scenario; +// ScenPlayer = player; +// ScenDir = dir; +// ScenVar = var; + + char c_player; // character representing player type + char c_dir; // character representing direction type + char c_var; // character representing variation type + ScenarioVarType i; + char fname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Set the player-type value. + */ + switch (player) { + case SCEN_PLAYER_SPAIN: + c_player = HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix; + break; + + case SCEN_PLAYER_GREECE: + c_player = HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix; + break; + + case SCEN_PLAYER_USSR: + c_player = HouseTypeClass::As_Reference(HOUSE_USSR).Prefix; + break; + + case SCEN_PLAYER_JP: + c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; + break; + + /* + ** Multi player scenario. + */ + default: + c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; + break; + } + + /* + ** Set the directional character value. + ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' + */ + switch (dir) { + case SCEN_DIR_EAST: + c_dir = 'E'; + break; + + case SCEN_DIR_WEST: + c_dir = 'W'; + break; + + default: + case SCEN_DIR_NONE: + c_dir = Percent_Chance(50) ? 'W' : 'E'; + break; + } + + /* + ** Set the variation value. + */ + if (var == SCEN_VAR_NONE) { + + /* + ** Find which variations are available for this scenario + */ + for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { + sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); + if (!CCFileClass(fname).Is_Available()) { + break; + } + } + + if (i==SCEN_VAR_FIRST) { + c_var = 'X'; // indicates an error + } else { + c_var = 'A' + Random_Pick(0, i-1); +// ScenVar = (ScenarioVarType)i; + } + } else { + switch (var) { + case SCEN_VAR_A: + c_var = 'A'; + break; + + case SCEN_VAR_B: + c_var = 'B'; + break; + + case SCEN_VAR_C: + c_var = 'C'; + break; + + case SCEN_VAR_D: + c_var = 'D'; + break; + + default: + c_var = 'L'; + break; + + } + } + + /* + ** generate the filename + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +//Mono_Printf("In set_scenario_name, scenario # = %d\n",scenario);Keyboard->Get();Keyboard->Get(); + if (scenario < 100) { + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); + } else { + char first = (scenario / 36) + 'A'; + char second = scenario % 36; + + if (second < 10) { + second += '0'; + } else { + second = (second - 10) + 'A'; + } + + sprintf(ScenarioName, "SC%c%c%c%c%c.INI", c_player, first, second, c_dir, c_var); + } +#else + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); +#endif +} + + +void ScenarioClass::Set_Scenario_Name(char const * name) +{ + if (name != NULL) { + strncpy(ScenarioName, name, sizeof(ScenarioName)); + ScenarioName[ARRAY_SIZE(ScenarioName)-1] = '\0'; + + char buf[3]; + memcpy(buf, &ScenarioName[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (buf[0] > '9' || buf[1] > '9') { + char first = buf[0]; + char second = buf[1]; + if (first <= '9') { + first -= '0'; + } else { + first -= 'A'; + } + + if (second <= '9') { + second -= '0'; + } else { + second = (second - 'A') + 10; + } + + Scenario = (36 * first) + second; + } else { + Scenario = atoi(buf); + } +#else + Scenario = atoi(buf); +#endif + } +} + + + +/*********************************************************************************************** + * Read_Scenario_INI -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. V.Grippi added CS check 2/5/97 * + *=============================================================================================*/ +bool Read_Scenario_INI(char * fname, bool ) +{ +// char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + + + ScenarioInit++; + + Clear_Scenario(); +#ifdef OBSOLETE + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + RequiredCD = -1; + } +#endif + + /* + ** Only force a CD check if this is a single player game or if its + ** a multiplayer game on an official scenario. If its non-official + ** (a user scenario) then we dont care which CD is in because the + ** scenario is stored locally on the hard drive. In this case, we + ** have already verified its existance. ST 3/1/97 4:52PM. + */ +#ifdef FIXIT_VERSION_3 // Avoid CD check if official scenario was downloaded. + if( ( Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial ) && _stricmp( Scen.ScenarioName, "download.tmp" ) ){ +#else + if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial){ +#endif + + /* + ** If this is scenario 1 then it should be on all CDs unless its an ant scenario + */ + if (Scen.Scenario == 1 && Scen.ScenarioName[2] != 'A') { + RequiredCD = -1; + } else { +// Mono_Printf("Read_SCen_INI scenario is: %s\n", Scen.ScenarioName); + /* + ** If this is a multiplayer scenario we need to find out if its a counterstrike + ** scenario. If so then we need CD 2. The original multiplayer scenarios are on + ** all CDs. + */ + if (Session.Type != GAME_NORMAL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + RequiredCD = -1; // default that any CD will do. +// If it's a counterstrike mission, require the counterstrike CD, unless the +// Aftermath CD is already in the drive, in which case, leave it there. +// Note, this works because this section only tests for multiplayer scenarios. + if (Is_Mission_Counterstrike(Scen.ScenarioName)) { + RequiredCD = 2; + if( Is_Aftermath_Installed() || Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) == 3 ) + { + RequiredCD = 3; + } + } + if(Is_Mission_Aftermath(Scen.ScenarioName)) { + RequiredCD = 3; + } +#else + if (Scen.Scenario > 24) { + RequiredCD = 2; + } else { + RequiredCD = -1; + } +#endif + } else { + + /* + ** This is a solo game. If the scenario number is >= 20 or its an ant mission + ** then we need the counterstrike CD (2) + */ + if (Scen.Scenario >= 20 || Scen.ScenarioName[2] == 'A') { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Scen.Scenario >= 36 && Scen.ScenarioName[2] != 'A') { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { + + /* + ** This is a solo mission from the original Red Alert. Choose the Soviet or + ** allied CD depending on the scenario name. + */ + if (Scen.ScenarioName[2] == 'U') { + RequiredCD = 1; + } else { + if (Scen.ScenarioName[2] == 'G') { +// Mono_Printf("We are setting REquiredCD to 0"); + RequiredCD = 0; + + } + } + } + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're asking for a CD swap, check to see if we need to set the palette +// to avoid a black screen. If this is a normal RA game, and the CD being +// requested is an RA CD, then don't set the palette, leave the map screen up. + +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != RequiredCD ) { +#else + if (Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != RequiredCD) { +#endif + if ((RequiredCD == 0 || RequiredCD == 1) && Session.Type == GAME_NORMAL) { + SeenPage.Clear(); + } + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + } +#endif + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Read_Scenario_INI Force_CD_Available failed", true); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } + } else { + /* + ** This is a user scenario so any old CD will do. + */ + RequiredCD = -1; + } + + + /* + ** Create scenario filename and read the file. + */ +// sprintf(fname, "%s.INI", root); + CCINIClass ini; + CCFileClass file(fname); +// file.Cache(); + + int result = ini.Load(file, true); + if (result == 0) { +// Mono_Printf("ini.Load failed"); + GlyphX_Debug_Print("Failed to load scenario file"); + GlyphX_Debug_Print(fname); + return(false); + } + + GlyphX_Debug_Print("Loaded scenario file"); + GlyphX_Debug_Print(file); + + /* + ** If the scenario digest is wrong then the return code will be a 2. + */ + if (result == 2) { +// if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { + /* + ** Make a special exception so that multiplayer maps from 1 through + ** 24 will not care if the message digest is in error. All other + ** maps will abort the scenario load. + */ + if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) { +#if (1) + GlyphX_Debug_Print("Scenario digest is wrong"); +#else + GamePalette.Set(); + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); +#endif +#ifdef RELEASE_VERSION + return(false); +#endif + } +// } + } + + /* + ** Reset the rules values to their initial settings. + */ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameOverride[index] != NULL) free((void*)NameOverride[index]); + NameOverride[index] = NULL; + NameIDOverride[index] = 0; + } + if (Session.Type == GAME_NORMAL) { + Special.IsShadowGrow = false; + } +#endif + +#ifdef FIXIT_ANTS + Session.Messages.Reset(); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); + WeaponTypeClass::As_Pointer(WEAPON_FLAMER)->Sound = VOC_NONE; + InfantryTypeClass::As_Reference(INFANTRY_THIEF).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_E4).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_SPY).PrimaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_SPY).SecondaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_GENERAL).IsBomber = false; + UnitTypeClass::As_Reference(UNIT_HARVESTER).IsExploding = false; + UnitTypeClass::As_Reference(UNIT_ANT1).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT2).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT3).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_QUEEN).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA1).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA2).Level = -1; +#endif + + + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Except does this _change_ any rules, or just add to them? - Just adds. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + + /* + ** For civilians, remove the graphics name override from the base rules (can still be overridden in scenario-specific INI). + */ + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypeClass* itype = InfantryTypes.Ptr(iindex); + if (itype->IsCivilian) { + itype->GraphicName[0] = '\0'; + } + } + + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + + /* + ** Fix a legacy bug with England and France country bonuses + */ + if (Session.Type != GAME_NORMAL) { + HouseTypeClass::As_Reference(HOUSE_ENGLAND).ArmorBias = fixed(9, 10); + HouseTypeClass::As_Reference(HOUSE_FRANCE).ROFBias = fixed(9, 10); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; +#ifdef TOFIX + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } +#endif + + /* + ** Fetch the appropriate movie names from the INI file. + */ + const char * const BASIC = "Basic"; + ini.Get_String(BASIC, "Name", "", Scen.Description, sizeof(Scen.Description)); + Scen.IntroMovie = ini.Get_VQType(BASIC, "Intro", Scen.IntroMovie); + Scen.BriefMovie = ini.Get_VQType(BASIC, "Brief", Scen.BriefMovie); + Scen.WinMovie = ini.Get_VQType(BASIC, "Win", Scen.WinMovie); + Scen.WinMovie2 = ini.Get_VQType(BASIC, "Win2", Scen.WinMovie2); + Scen.WinMovie3 = ini.Get_VQType(BASIC, "Win3", Scen.WinMovie3); + Scen.WinMovie4 = ini.Get_VQType(BASIC, "Win4", Scen.WinMovie4); + Scen.LoseMovie = ini.Get_VQType(BASIC, "Lose", Scen.LoseMovie); + Scen.ActionMovie = ini.Get_VQType(BASIC, "Action", Scen.ActionMovie); + Scen.IsToCarryOver = ini.Get_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + Scen.IsToInherit = ini.Get_Bool(BASIC, "ToInherit", Scen.IsToInherit); + Scen.IsInheritTimer = ini.Get_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + Scen.IsEndOfGame = ini.Get_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + Scen.IsTanyaEvac = ini.Get_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + Scen.TransitTheme = ini.Get_ThemeType(BASIC, "Theme", THEME_NONE); + NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); + Scen.CarryOverPercent = ini.Get_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + Scen.CarryOverPercent = Saturate(Scen.CarryOverPercent, 1); + Scen.CarryOverCap = ini.Get_Int(BASIC, "CarryOverCap", Scen.CarryOverCap); + Scen.IsNoSpyPlane = ini.Get_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + Scen.IsSkipScore = ini.Get_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + Scen.IsOneTimeOnly = ini.Get_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + Scen.IsNoMapSel = ini.Get_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + Scen.IsTruckCrate = ini.Get_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + Scen.IsMoneyTiberium = ini.Get_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + Scen.Percent = ini.Get_Int(BASIC, "Percent", Scen.Percent); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerTypeClass::Read_INI(ini); + Call_Back(); + + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(ini); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + PlayerPtr = HouseClass::As_Pointer(ini.Get_HousesType(BASIC, "Player", HOUSE_GREECE)); + PlayerPtr->Assign_Handicap(Scen.Difficulty); + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = min(Scen.CarryOverMoney * Scen.CarryOverPercent, Scen.CarryOverCap); + } else { + carryover = Scen.CarryOverMoney * Scen.CarryOverPercent; + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { + //Call the new Assign_Houses function. ST - 8/8/2019 12:35PM + //Assign_Houses(); + GlyphX_Assign_Houses(); + } + PlayerPtr->IsHuman = true; + PlayerPtr->IsPlayerControl = true; + +// if (NewINIFormat < 2 || !ini.Is_Present("MapPack")) { +// Map.Read_Binary(root, &ScenarioCRC); +// } + + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(ini); + Call_Back(); + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(ini); + Call_Back(); + + VesselClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(ini); + Call_Back(); + + + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(ini); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(ini); + Call_Back(); + + /* Moved above ini.Get_TextBlock(...) so Xlat mission.ini could be loaded + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. VG 10/17/96 + */ + INIClass mini; + mini.Load(CCFileClass("MISSION.INI")); + mini.Get_TextBlock(fname, Scen.BriefingText, sizeof(Scen.BriefingText)); + + /* + ** Read in any briefing text. + */ + if (Scen.BriefingText[0] == '\0') { + ini.Get_TextBlock("Briefing", Scen.BriefingText, sizeof(Scen.BriefingText)); + } + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Special cases: + ** Gold Rush multiplayer map cell 9033 - LAND_ROCK + ** USSR mission 13 - fixup trigger action + ** Allied mission 9A - fail mission if tech center is destroyed before being spied + ** Aftermath: Brother in Arms - have transports move to separate waypoints + ** Aftermath: Let's Make a Steal - Make the pillboxes un-capturable + ** Counterstrike: Soviet Soldier Volkov and Chitzkoi / Deus Ex Machina - Sniper burst fix + */ + if (_stricmp(Scen.ScenarioName, "scmh8ea.ini") == 0) { + Map[(CELL)9033].Override_Land_Type(LAND_ROCK); + } + + if (_stricmp(Scen.ScenarioName, "scu13ea.ini") == 0) { + TriggerTypeClass* trigger = TriggerTypes.Ptr(11); + trigger->Action1.Trigger.Set_Raw(39); + } + + if (_stricmp(Scen.ScenarioName, "scg09ea.ini") == 0) { + TriggerTypeClass* spyd_trigger = TriggerTypeClass::From_Name("Spyd"); + assert(spyd_trigger != NULL); + + TriggerTypeClass* kos_trigger = new TriggerTypeClass(); + kos_trigger->IsPersistant = spyd_trigger->IsPersistant; + kos_trigger->House = spyd_trigger->House; + kos_trigger->EventControl = MULTI_LINKED; + kos_trigger->ActionControl = MULTI_ONLY; + kos_trigger->Event1.Event = spyd_trigger->Event1.Event; + kos_trigger->Event1.Data = spyd_trigger->Event1.Data; + kos_trigger->Event2.Event = TEVENT_DESTROYED; + kos_trigger->Event2.Data.Value = 0; + kos_trigger->Action1.Action = TACTION_SET_GLOBAL; + kos_trigger->Action1.Data.Value = 22; + kos_trigger->Action2.Action = TACTION_SET_GLOBAL; + kos_trigger->Action2.Data.Value = 23; + + spyd_trigger->Event1.Event = TEVENT_GLOBAL_SET; + spyd_trigger->Event1.Data.Value = 22; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass* building = Buildings.Ptr(index); + if (building->Trigger.Is_Valid() && (building->Trigger->Class == spyd_trigger)) { + building->Attach_Trigger(Find_Or_Make(kos_trigger)); + } + } + + TriggerTypeClass* los3_trigger = new TriggerTypeClass(); + los3_trigger->IsPersistant = spyd_trigger->IsPersistant; + los3_trigger->House = spyd_trigger->House; + los3_trigger->EventControl = MULTI_AND; + los3_trigger->ActionControl = MULTI_AND; + los3_trigger->Event1.Event = TEVENT_GLOBAL_SET; + los3_trigger->Event1.Data.Value = 23; + los3_trigger->Event2.Event = TEVENT_GLOBAL_CLEAR; + los3_trigger->Event2.Data.Value = 22; + los3_trigger->Action1.Action = TACTION_LOSE; + los3_trigger->Action1.Data.Value = -255; + los3_trigger->Action2.Action = TACTION_TEXT_TRIGGER; + los3_trigger->Action2.Data.Value = 54; + } + + if (_stricmp(Scen.ScenarioName, "scu46ea.ini") == 0) { + Scen.Waypoint[20] = 9915; + Scen.Waypoint[21] = 9919; + Map[Scen.Waypoint[20]].IsWaypoint = 1; + Map[Scen.Waypoint[21]].IsWaypoint = 1; + + TeamTypeClass* rnf1_team = TeamTypeClass::From_Name("rnf1"); + assert(rnf1_team != NULL); + rnf1_team->MissionList[0].Data.Value = 20; + + TeamTypeClass* rnf2_team = TeamTypeClass::From_Name("rnf2"); + assert(rnf2_team != NULL); + rnf2_team->MissionList[0].Data.Value = 21; + } + + if (_stricmp(Scen.ScenarioName, "scu42ea.ini") == 0) { + BuildingTypeClass::As_Reference(STRUCT_PILLBOX).IsCaptureable = false; + } + + if ((_stricmp(Scen.ScenarioName, "scu35ea.ini") == 0) || (_stricmp(Scen.ScenarioName, "scu47ea.ini") == 0)) { + WeaponTypeClass::As_Pointer(Weapon_From_Name("Sniper"))->Burst = 2; + } + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (Session.Type != GAME_NORMAL /*|| Scen.ScenPlayer == SCEN_PLAYER_2PLAYER || Scen.ScenPlayer == SCEN_PLAYER_MPLAYER*/) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if ( (Session.Options.AIPlayers + Session.Players.Count() < Rule.MaxPlayers) && !Debug_Map) { + Remove_AI_Players(); + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. Session.Options.UnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(ini.Get_Bool("Basic", "Official", false)); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if random crates are enabled for + ** this scenario. + */ + if (Session.Options.Goodies) { + int count = max(Rule.CrateMinimum, Session.NumPlayers); + count = min(count, Rule.CrateMaximum); + for (int index = 0; index < count; index++) { + Map.Place_Random_Crate(); + } + } + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Added runtime check. + if( Is_Aftermath_Installed() ) + { + if (Session.Type == GAME_SKIRMISH || Session.Type == GAME_GLYPHX_MULTIPLAYER) { + bAftermathMultiplayer = NewUnitsEnabled = OverrideNewUnitsEnabled; + } + } +#endif + ScenarioInit--; + return(true); +} + + +/*********************************************************************************************** + * Write_Scenario_INI -- Write the scenario INI file. * + * * + * INPUT: * + * root root filename for the scenario * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/11/1995 JLB : Updates movie data. * + *=============================================================================================*/ +void Write_Scenario_INI(char * fname) +{ +#ifndef CHEAT_KEYS + fname = fname; +#else +// CCFileClass file(fname); + + CCINIClass ini; + + /* + ** Preload the old scenario if it is present because there may + ** be some fields in the INI that are processed but not written + ** out. Preloading the scenario will preserve these manually + ** maintained entries. + */ + if (CCFileClass(fname).Is_Available()) { + ini.Load(CCFileClass(fname), true); + } + + static char const * const BASIC = "Basic"; + ini.Clear(BASIC); + ini.Put_String(BASIC, "Name", Scen.Description); + ini.Put_VQType(BASIC, "Intro", Scen.IntroMovie); + ini.Put_VQType(BASIC, "Brief", Scen.BriefMovie); + ini.Put_VQType(BASIC, "Win", Scen.WinMovie); + ini.Put_VQType(BASIC, "Lose", Scen.LoseMovie); + ini.Put_VQType(BASIC, "Action", Scen.ActionMovie); + ini.Put_HousesType(BASIC, "Player", PlayerPtr->Class->House); + ini.Put_ThemeType(BASIC, "Theme", Scen.TransitTheme); + ini.Put_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + ini.Put_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + ini.Put_Bool(BASIC, "ToInherit", Scen.IsToInherit); + ini.Put_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + ini.Put_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + ini.Put_Int(BASIC, "NewINIFormat", 3); + ini.Put_Int(BASIC, "CarryOverCap", Scen.CarryOverCap/100); + ini.Put_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + ini.Put_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + ini.Put_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + ini.Put_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + ini.Put_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + ini.Put_Bool(BASIC, "Official", true); + ini.Put_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + ini.Put_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + ini.Put_Int(BASIC, "Percent", Scen.Percent); + + HouseClass::Write_INI(ini); + TeamTypeClass::Write_INI(ini); + TriggerTypeClass::Write_INI(ini); + Map.Write_INI(ini); + TerrainClass::Write_INI(ini); + UnitClass::Write_INI(ini); + VesselClass::Write_INI(ini); + InfantryClass::Write_INI(ini); + BuildingClass::Write_INI(ini); + Base.Write_INI(ini); + OverlayClass::Write_INI(ini); + SmudgeClass::Write_INI(ini); + + if (strlen(Scen.BriefingText)) { + ini.Put_TextBlock("Briefing", Scen.BriefingText); + } +// sprintf(fname, "%s.INI", root); + RawFileClass rawfile(fname); + ini.Save(rawfile, true); +#endif +} + + +/*********************************************************************************************** + * Assign_Houses -- Assigns multiplayer houses to various players * + * * + * This routine assigns all players to a multiplayer house slot; it forms network connections * + * to each player. The Connection ID used is the value for that player's HousesType. * + * * + * PlayerPtr is also set here. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine assumes the 'Players' vector has been properly filled in with players' * + * names, addresses, color, etc. * + * Also, it's assumed that the HouseClass's have all been created & initialized. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + * 07/14/1995 JLB : Records name of player in house structure. * + *=============================================================================================*/ +void Assign_Houses(void) +{ + int assigned[MAX_PLAYERS]; + int color_used[8]; + int i,j; + HousesType house; + HouseClass * housep; + int lowest_color; + int index; + HousesType pref_house; + int color; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + for (i = 0; i < MAX_PLAYERS; i++) { + assigned[i] = 0; + color_used[i] = 0; + } + +// debugprint( "Assign_Houses()\n" ); + //------------------------------------------------------------------------ + // Assign each player in 'Players' to a multiplayer house. Players will + // be sorted by their chosen color value (this value must be unique among + // all the players). + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + + //..................................................................... + // Find the player with the lowest color index + //..................................................................... + index = 0; + lowest_color = 255; + for (j = 0; j < Session.Players.Count(); j++) { + //.................................................................. + // If we've already assigned this house, skip it. + //.................................................................. + if (assigned[j]) { + continue; + } + if (Session.Players[j]->Player.Color < lowest_color) { + lowest_color = Session.Players[j]->Player.Color; + index = j; + } + } + + //..................................................................... + // Mark this player as having been assigned. + //..................................................................... + assigned[index] = 1; + color_used[Session.Players[index]->Player.Color] = 1; + + //..................................................................... + // Assign the lowest-color'd player to the next available slot in the + // HouseClass array. + //..................................................................... + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#ifdef WOLAPI_INTEGRATION + // Make another copy of name, permanent throughout entire game. + strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#endif + housep->IsHuman = true; + housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), + Session.Players[index]->Player.House, Session.Options.Credits); + if (index == 0) { + PlayerPtr = housep; + } + /* + ** Convert the build level into an actual tech level to assign to the house. + ** There isn't a one-to-one correspondence. + */ + housep->Control.TechLevel = _build_tech[BuildLevel]; + + housep->Assign_Handicap(Scen.Difficulty); + + //..................................................................... + // Record where we placed this player + //..................................................................... + Session.Players[index]->Player.ID = house; + +// debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); + } + + //------------------------------------------------------------------------ + // Now assign computer players to the remaining houses. + //------------------------------------------------------------------------ + for (i = Session.Players.Count(); i < Session.Players.Count() + Session.Options.AIPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (Percent_Chance(50)) { + pref_house = HOUSE_GREECE; + } else { + pref_house = HOUSE_USSR; + } + + //..................................................................... + // Pick a color for this house; keep looping until we find one. + //..................................................................... + while (1) { + color = Random_Pick(0, 7); + if (color_used[color] == false) { + break; + } + } + color_used[color] = true; + + //..................................................................... + // Set up the house + //..................................................................... +// housep->Control.MaxUnit = 80; +// housep->Control.MaxInfantry = 60; +// housep->Control.MaxBuilding = 60; +// housep->Control.MaxVessel = 60; + housep->IsHuman = false; + housep->IsStarted = true; + + strcpy(housep->IniName, Text_String(TXT_COMPUTER)); + + if (Session.Type != GAME_NORMAL) { + housep->IQ = Rule.MaxIQ; + } + + housep->Init_Data((PlayerColorType)color, pref_house, Session.Options.Credits); + housep->Control.TechLevel = _build_tech[BuildLevel]; +// housep->Control.TechLevel = BuildLevel; + + DiffType difficulty = Scen.CDifficulty; + + if (Session.Players.Count() > 1 && Rule.IsCompEasyBonus && difficulty > DIFF_EASY) { + difficulty = (DiffType)(difficulty - 1); + } + housep->Assign_Handicap(difficulty); + } + + for (i = Session.Players.Count()+Session.Options.AIPlayers; i < Rule.MaxPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (housep != NULL) { + housep->IsDefeated = true; + } + } +} + + +/*********************************************************************************************** + * Remove_AI_Players -- Removes the computer AI houses & their units * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Remove_AI_Players(void) +{ + int i; + int aicount = 0; + HousesType house; + HouseClass * housep; + +#if (0) + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + aicount++; + if(aicount > Session.Options.AIPlayers) { + housep->Clobber_All(); + } + } + } +#else + /* + ** AI players are set up like human players now. ST - 8/13/2019 1:32PM + */ + for (i = 0; i < MAX_PLAYERS; i++) { + if (i >= Session.Players.Count()) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + housep->Clobber_All(); + } + } + } + +#endif +} + + +#define USE_GLYPHX_START_LOCATIONS 1 + + +/*********************************************************************************************** + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * * + * This routine uses data tables to determine which units to create for either * + * a GDI or NOD house, and how many of each. * + * * + * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * + * as that house's "home" cell. * + * * + * INPUT: official -- Directs the placement logic to use the full set of waypoints rather * + * than biasing toward the first four. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static int ReserveInfantryIndex = 0; +static void Reserve_Infantry() +{ + if (Infantry.Count() == Infantry.Length()) { + delete Infantry.Ptr(ReserveInfantryIndex); + ReserveInfantryIndex = (ReserveInfantryIndex + 1) % Infantry.Length(); + } +} + +static int ReserveUnitIndex = 0; +static void Reserve_Unit() +{ + if (Units.Count() == Units.Length()) { + delete Units.Ptr(ReserveUnitIndex); + ReserveUnitIndex = (ReserveUnitIndex + 1) % Units.Length(); + } +} + +static void Create_Units(bool official) +{ + static struct { + int MinLevel; + UnitType AllyType[2]; + UnitType SovietType[2]; + } utable[] = { + {4, {UNIT_MTANK2, UNIT_LTANK}, {UNIT_MTANK, UNIT_NONE}}, + {5, {UNIT_APC, UNIT_NONE}, {UNIT_V2_LAUNCHER, UNIT_NONE}}, + {8, {UNIT_ARTY, UNIT_JEEP}, {UNIT_MTANK, UNIT_NONE}}, + {10, {UNIT_MTANK2, UNIT_MTANK2}, {UNIT_HTANK, UNIT_NONE}} + }; + static int num_units[ARRAY_SIZE(utable)]; // # of each type of unit to create + int tot_units; // total # units to create + + static struct { + int MinLevel; + int AllyCount; + InfantryType AllyType; + int SovietCount; + InfantryType SovietType; + } itable[] = { + {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, + {2, 1,INFANTRY_E3, 1,INFANTRY_E2}, + {4, 1,INFANTRY_E3, 1,INFANTRY_E4}, + +// removed because of bug B478 (inappropriate infantry given in a bases off scenario). +// {5, 1,INFANTRY_RENOVATOR, 1,INFANTRY_RENOVATOR}, +// {6, 1,INFANTRY_SPY, 1,INFANTRY_DOG}, +// {10, 1,INFANTRY_THIEF, 1,INFANTRY_DOG}, +// {12, 1,INFANTRY_MEDIC, 2,INFANTRY_DOG} + }; + static int num_infantry[ARRAY_SIZE(itable)];// # of each type of infantry to create + int tot_infantry; // total # infantry to create + + + CELL centroid; // centroid of this house's stuff + CELL centerpt; // centroid for a category of objects, as a CELL + + int u_limit=0; // last allowable index of units for this BuildLevel + int i_limit=0; // last allowable index of infantry for this BuildLevel + TechnoClass * obj; // newly-created object + int i,j,k; // loop counters + int scaleval; // value to scale # units or infantry + + ReserveInfantryIndex = ReserveUnitIndex = 0; + + /* + ** For the current BuildLevel, find the max allowable index into the tables + */ + for (i = 0; i < ARRAY_SIZE(utable); i++) { + if (PlayerPtr->Control.TechLevel >= utable[i].MinLevel) { + u_limit = i+1; + } + } + for (i = 0; i < ARRAY_SIZE(itable); i++) { + if (PlayerPtr->Control.TechLevel >= itable[i].MinLevel) { + i_limit = i+1; + } + } + + /* + ** Compute how many of each buildable category to create + */ + /* + ** Compute allowed # units + */ + tot_units = (Session.Options.UnitCount * 2) / 3; + if (u_limit == 0) tot_units = 0; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < u_limit; i++) { + num_units[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all units + */ + j = 0; + for (i = 0; i < tot_units; i++) { + num_units[j]++; + j++; + if (j >= u_limit) { + j = 0; + } + } + + /* + ** Compute allowed # infantry + */ + tot_infantry = Session.Options.UnitCount - tot_units; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < i_limit; i++) { + num_infantry[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all infantry + */ + j = 0; + for (i = 0; i < tot_infantry; i++) { + num_infantry[j]++; + j++; + if (j >= i_limit) { + j = 0; + } + } + + /* + ** Build a list of the valid waypoints. This normally shouldn't be + ** necessary because the scenario level designer should have assigned + ** valid locations to the first N waypoints, but just in case, this + ** loop verifies that. + */ + + const unsigned int MAX_STORED_WAYPOINTS = 26; + + bool taken[MAX_STORED_WAYPOINTS]; + CELL waypts[MAX_STORED_WAYPOINTS]; + assert(Rule.MaxPlayers < ARRAY_SIZE(waypts)); + int num_waypts = 0; + + /* + ** Calculate the number of waypoints (as a minimum) that will be lifted from the + ** mission file. Bias this number so that only the first 4 waypoints are used + ** if there are 4 or fewer players. Unofficial maps will pick from all the + ** available waypoints. + */ +#ifndef USE_GLYPHX_START_LOCATIONS + int look_for = max(4, Session.Players.Count()+Session.Options.AIPlayers); + if (!official) { + look_for = 8; + } +#else + /* + ** We allow the users to choose from all available start positions, even on official maps. ST - 1/15/2020 9:19AM + */ + int look_for = Session.Players.Count(); +#endif + + for (int waycount = 0; waycount < 26; waycount++) { +// for (int waycount = 0; waycount < max(4, Session.Players.Count()+Session.Options.AIPlayers); waycount++) { + if (Scen.Waypoint[waycount] != -1) { + waypts[num_waypts] = Scen.Waypoint[waycount]; + taken[num_waypts] = false; + num_waypts++; + + if (num_waypts >= MAX_STORED_WAYPOINTS) + { + break; + } + } + } + + /* + ** If there are insufficient waypoints to account for all players, then randomly assign + ** starting points until there is enough. + */ + int deficiency = look_for - num_waypts; +// int deficiency = (Session.Players.Count() + Session.Options.AIPlayers) - num_waypts; + if (deficiency > 0) { + for (int index = 0; index < deficiency; index++) { + CELL trycell = XY_Cell(Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1), Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1)); + + trycell = Map.Nearby_Location(trycell, SPEED_TRACK); + waypts[num_waypts] = trycell; + taken[num_waypts] = false; + num_waypts++; + } + } + + /* + ** Loop through all houses. Computer-controlled houses, with Session.Options.Bases + ** ON, are treated as though bases are OFF (since we have no base-building + ** AI logic.) + */ + int numtaken = 0; + for (HousesType house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + + /* + ** Get a pointer to this house; if there is none, go to the next house + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr == NULL) { + continue; + } + + /* + ** Pick the starting location for this house. The first house just picks + ** one of the valid locations at random. The other houses pick the furthest + ** wapoint from the existing houses. + */ + if (!UseGlyphXStartLocations) { + + if (numtaken == 0) { + int pick = Random_Pick(0, num_waypts-1); + centroid = waypts[pick]; + taken[pick] = true; + numtaken++; + } else { + + /* + ** Set all waypoints to have a score of zero in preparation for giving + ** a distance score to all waypoints. + */ + int score[26]; + memset(score, '\0', sizeof(score)); + + /* + ** Scan through all waypoints and give a score as a value of the sum + ** of the distances from this waypoint to all taken waypoints. + */ + for (int index = 0; index < num_waypts; index++) { + + /* + ** If this waypoint has not already been taken, then accumulate the + ** sum of the distance between this waypoint and all other taken + ** waypoints. + */ + if (!taken[index]) { + for (int trypoint = 0; trypoint < num_waypts; trypoint++) { + + if (taken[trypoint]) { + score[index] += Distance(Cell_Coord(waypts[index]), Cell_Coord(waypts[trypoint])); + } + } + } + } + + /* + ** Now find the waypoint with the largest score. This waypoint is the one + ** that is furthest from all other taken waypoints. + */ + int best = 0; + int bestvalue = 0; + for (int searchindex = 0; searchindex < num_waypts; searchindex++) { + if (score[searchindex] > bestvalue || bestvalue == 0) { + bestvalue = score[searchindex]; + best = searchindex; + } + } + + /* + ** Assign this best position to the house. + */ + centroid = waypts[best]; + taken[best] = true; + numtaken++; + } + } else { + + /* + ** New code that respects the start locations passed in from GlyphX. + ** + ** ST - 1/8/2020 3:39PM + */ + centroid = waypts[hptr->StartLocationOverride]; + } + + /* + ** Assign the center of this house to the waypoint location. + */ + hptr->Center = Cell_Coord(centroid); + + /* + ** If Bases are ON, human & computer houses are treated differently + */ + if (Session.Options.Bases) { + + /* + ** - For a human-controlled house: + ** - Set 'scaleval' to 1 + ** - Create an MCV + ** - Attach a flag to it for capture-the-flag mode + */ + scaleval = 1; + Reserve_Unit(); + obj = new UnitClass (UNIT_MCV, house); + if (!obj->Unlimbo(Cell_Coord(centroid), DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj != NULL) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj, true); + } + } + } else { + + /* + ** If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for + ** capture-the-flag mode. + */ + scaleval = 1; +#ifdef TOFIX + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_TRUCK, house); + obj->Unlimbo(Cell_Coord(centroid), DIR_N); + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } +#endif + } + + /* + ** Create units for this house + */ + for (i = 0; i < u_limit; i++) { + + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_units[i] * scaleval; j++) { + + /* + ** Create an Ally unit + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < 2; k++) if(utable[i].AllyType[k] != UNIT_NONE) { + Reserve_Unit(); + obj = new UnitClass (utable[i].AllyType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create a Soviet unit + */ + for (k = 0; k < 2; k++) if(utable[i].SovietType[k] != UNIT_NONE) { + Reserve_Unit(); + obj = new UnitClass (utable[i].SovietType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + + /* + ** Create infantry + */ + for (i = 0; i < i_limit; i++) { + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_infantry[i] * scaleval; j++) { + + /* + ** Create Ally infantry (Note: Unlimbo calls Enter_Idle_Mode(), which + ** assigns the infantry to HUNT; we must use Set_Mission() to override + ** this state.) + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < itable[i].AllyCount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].AllyType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create Soviet infantry + */ + for (k = 0; k < itable[i].SovietCount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].SovietType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Scan_Place_Object -- places an object >near< the given cell * + * * + * INPUT: * + * obj ptr to object to Unlimbo * + * cell center of search area * + * * + * OUTPUT: * + * true = object was placed; false = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +int Scan_Place_Object(ObjectClass * obj, CELL cell) +{ + int dist; // for object placement + FacingType rot; // for object placement + FacingType fcounter; // for object placement + int tryval; + CELL newcell; + TechnoClass * techno; + int skipit; + + /* + ** First try to unlimbo the object in the given cell. + */ + if (Map.In_Radar(cell)) { + techno = Map[cell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(cell), DIR_N)) { + return(true); + } + } + } + + /* + ** Loop through distances from the given center cell; skip the center cell. + ** For each distance, try placing the object along each rotational direction; + ** if none are available, try each direction with a random scatter value. + ** If that fails, go to the next distance. + ** This ensures that the closest coordinates are filled first. + */ + for (dist = 1; dist < 32; dist++) { + + /* + ** Pick a random starting direction + */ + rot = Random_Pick(FACING_N, FACING_NW); + + /* + ** Try all directions twice + */ + for (tryval = 0 ; tryval < 2; tryval++) { + + /* + ** Loop through all directions, at this distance. + */ + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + + skipit = false; + + /* + ** Pick a coordinate along this directional axis + */ + newcell = Clip_Move(cell, rot, dist); + + /* + ** If this is our second try at this distance, add a random scatter + ** to the desired cell, so our units aren't all aligned along spokes. + */ + if (tryval > 0) { + newcell = Clip_Scatter (newcell, 1); + } + + /* + ** If, by randomly scattering, we've chosen the exact center, skip + ** it & try another direction. + */ + if (newcell==cell) { + skipit = true; + } + + if (!skipit) { + /* + ** Only attempt to Unlimbo the object if: + ** - there is no techno in the cell + ** - the techno in the cell & the object are both infantry + */ + techno = Map[newcell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(newcell), DIR_N)) { + return(true); + } + } + } + + rot++; + if (rot > FACING_NW) { + rot = FACING_N; + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * * + * INPUT: * + * cell cell to scatter from * + * maxdist max distance to scatter * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Scatter(CELL cell, int maxdist) +{ + int x,y; + int xdist; + int ydist; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + xdist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + x += xdist; + if (x > xmax) { + x = xmax; + } + } else { + x -= xdist; + if (x < xmin) { + x = xmin; + } + } + + /* + ** Adjust the y-coordinate + */ + ydist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + y += ydist; + if (y > ymax) { + y = ymax; + } + } else { + y -= ydist; + if (y < ymin) { + y = ymin; + } + } + + return (XY_Cell(x, y)); +} + + +/*********************************************************************************************** + * Clip_Move -- moves in given direction from given cell; clips to map * + * * + * INPUT: * + * cell cell to start from * + * facing direction to move * + * dist distance to move * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Move(CELL cell, FacingType facing, int dist) +{ + int x,y; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + switch (facing) { + case FACING_N: + y -= dist; + break; + + case FACING_NE: + x += dist; + y -= dist; + break; + + case FACING_E: + x += dist; + break; + + case FACING_SE: + x += dist; + y += dist; + break; + + case FACING_S: + y += dist; + break; + + case FACING_SW: + x -= dist; + y += dist; + break; + + case FACING_W: + x -= dist; + break; + + case FACING_NW: + x -= dist; + y -= dist; + break; + } + + /* + ** Clip to the map + */ + if (x > xmax) + x = xmax; + if (x < xmin) + x = xmin; + + if (y > ymax) + y = ymax; + if (y < ymin) + y = ymin; + + return (XY_Cell(x, y)); +} + + +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var) +{ + if (name == NULL) return; + + /* + ** Fetch the scenario number. + */ + char buf[3]; + memcpy(buf, &name[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char first = buf[0]; + char second = buf[1]; + if (first <= '9' && second <= '9') { + scenario = atoi(buf); + } else { + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scenario = (36 * first) + second; + } +#else + scenario = atoi(buf); +#endif + + /* + ** Fetch the scenario player (side). + */ + player = SCEN_PLAYER_GREECE; + if (name[2] == HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix) { + player = SCEN_PLAYER_SPAIN; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix) { + player = SCEN_PLAYER_GREECE; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_USSR).Prefix) { + player = SCEN_PLAYER_USSR; + } + + /* + ** Fetch the direction. + */ + dir = SCEN_DIR_EAST; + if (name[5] == 'E') { + dir = SCEN_DIR_EAST; + } else { + dir = SCEN_DIR_WEST; + } + + /* + ** Fetch the variation. + */ + var = SCEN_VAR_A; + var = ScenarioVarType((name[6] - 'A') + SCEN_VAR_A); +} \ No newline at end of file diff --git a/REDALERT/SCENARIO.H b/REDALERT/SCENARIO.H new file mode 100644 index 000000000..3bf0d917d --- /dev/null +++ b/REDALERT/SCENARIO.H @@ -0,0 +1,333 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCENARIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SCENARIO_H +#define SCENARIO_H + + +/* +** This class holds the information about the current game being played. This information is +** global to the scenario and is generally of a similar nature to the information that was held +** in the controlling scenario INI file. It is safe to write this structure out as a whole since +** it doesn't contain any embedded pointers. +*/ +class ScenarioClass { + public: + + // Constructor. + ScenarioClass(void); + void Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); + void Set_Scenario_Name(char const * name); + + bool Set_Global_To(int global, bool value); + void Do_BW_Fade(void); + void Do_Fade_AI(void); + + /* + ** This is the source of the random numbers used in the game. This controls + ** the game logic and thus must be in sync with any networked machines. + */ + RandomClass RandomNumber; + + /* + ** This is the difficulty setting of the game. + */ + DiffType Difficulty; // For human player. + DiffType CDifficulty; // For computer players. + + /* + ** This is the main mission timer. This is the timer that is reset at the + ** start of the mission. It, effectively, holds the elapsed time of the + ** mission. + */ + TTimerClass Timer; + + /* + ** This is an array of waypoints; each waypoint corresponds to a letter of + ** the alphabet, and points to a cell number. -1 means unassigned. + ** The CellClass has a bit that tells if that cell has a waypoint attached to + ** it; the only way to find which waypoint it is, is to scan this array. This + ** shouldn't be needed often; usually, you know the waypoint & you want the CELL. + */ + CELL Waypoint[WAYPT_COUNT]; + + /* + ** This holds the system wide mission countdown timer. Time based missions + ** are governed by this timer. Various trigger events can modify and examine + ** this timer. The current value of this timer will display on the game + ** screen. + */ + CDTimerClass MissionTimer; + + /* + ** The shroud regrowth (if enabled) is regulated by this timer. When the + ** timer expires, the shroud will regrow one step. + */ + CDTimerClass ShroudTimer; + + /* + ** The scenario number. + */ + int Scenario; + + /* + ** The theater of the current scenario. + */ + TheaterType Theater; + + /* + ** The full name of the scenario (as it exists on disk). + */ + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + /* + ** Description of the scenario. + */ + char Description[DESCRIP_MAX]; + + /* + ** The filename of the introduction movie. + */ + VQType IntroMovie; + + /* + ** The filename of the briefing movie. + */ + VQType BriefMovie; + + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie2; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie3; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie4; + + /* + ** The filename of the movie to play if the scenario is lost. + */ + VQType LoseMovie; + + /* + ** The filename of the movie to play right after the briefing and + ** just before the game. + */ + VQType ActionMovie; + + /* + ** This is the full text of the briefing. This text will be + ** displayed when the player commands the "restate mission + ** objectives" operation. + */ + char BriefingText[1024]; + + /* + ** This is the theme to start playing at the beginning of the action + ** movie. A score started in this fashion will continue to play as + ** the game progresses. + */ + ThemeType TransitTheme; + + /* + ** The house that the player is to be (obsolete). + */ + HousesType PlayerHouse; + + /* + ** The percentage of money that is allowed to be carried over into the + ** following scenario. + */ + fixed CarryOverPercent; + + /* + ** This is the amount of money that was left over in the previous + ** scenario. + */ + int CarryOverMoney; + + /* + ** This specifies the maximum amount of money that is allowed to be + ** carried over from the previous scenario. This limits the amount + ** regardless of what the carry over percentage is set to. + */ + int CarryOverCap; + + /* + ** This is the percent that the computer controlled base is to be + ** built up to at the scenario start. + */ + int Percent; + + /* + ** Global flags that are used in the trigger system and are persistent + ** over the course of the game. + */ + bool GlobalFlags[30]; + + /* + ** This records the bookmark view locations the player has recorded. + */ + CELL Views[4]; + + /* + ** This is the number of active passable bridges in the current game. + */ + int BridgeCount; + + /* + ** This records the carry over timer value that is used when the mission + ** starts (presuming the appropriate flag is set) and also used when the + ** scenario restarts. + */ + int CarryOverTimer; + + /* + ** If a bridge has been destroyed, then this flag will be set to true. + ** If there is a trigger that depends on this, it might be triggered. + */ + unsigned IsBridgeChanged:1; + + /* + ** If a global has changed and global change trigger events must be + ** processed, then this flag will be set to true. + */ + unsigned IsGlobalChanged:1; + + /* + ** Are the buildings and units in this scenario to carry over into + ** some (unspecified) later scenario and thus have to be recorded + ** at the end? + */ + unsigned IsToCarryOver:1; + + /* + ** Is this scenario to begin by taking the previously recorded + ** carryover objects and creating them onto the map? + */ + unsigned IsToInherit:1; + + /* + ** If Tanya or a civilian is to be automatically evacuated when they enter + ** a transport vehicle, then this flag will be true. + */ + unsigned IsTanyaEvac:1; + + /* + ** These variables are assigned to the chronosphere effect, and control + ** whether the palette should be fading towards b&w or towards color. + */ + unsigned IsFadingBW:1; + unsigned IsFadingColor:1; + + /* + ** If this scenario is to be the last mission of the game (for this side), then + ** this flag will be true. + */ + unsigned IsEndOfGame:1; + + /* + ** If the mission countdown timer is to be inherited from the previous + ** scenario, then this flag will be set to true. + */ + unsigned IsInheritTimer:1; + + /* + ** If the spy plane is to be disabled in this scenario even though circumstances + ** might otherwise indicate that it should appear, then this flag will be true. + */ + unsigned IsNoSpyPlane:1; + + /* + ** If the score screen (and "mission accomplished" voice) is to be skipped when + ** this scenario is finished, then this flag will be true. + */ + unsigned IsSkipScore:1; + + /* + ** If this is to be a one time only mission such that when it is completed, the game + ** will return to the main menu, then this flag will be set to true. + */ + unsigned IsOneTimeOnly:1; + + /* + ** If the map selection is to be skipped then this flag will be true. If this + ** ins't a one time only scenario, then the next scenario will have the same + ** name as the current one but will be for variation "B". + */ + unsigned IsNoMapSel:1; + + /* + ** If trucks are supposed to drop wood crates when they explode, then this flag + ** will be set to true. + */ + unsigned IsTruckCrate:1; + + /* + ** If the initial money is to be assigned as ore in available silos, then + ** this flag will be set to true. + */ + unsigned IsMoneyTiberium:1; + + /* + ** This is the fading countdown timer. As this timer counts down, the + ** fading to b&w or color will progress. This timer represents a + ** percentage of the Options.Get_Saturation() to fade towards. + */ + CDTimerClass FadeTimer; + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + // Timer to set the period for checking if an auto-sonar pulse should be performed. + // This will take place if a player has nothing but subs left in the game. + CDTimerClass AutoSonarTimer; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + bool bLocalProposesDraw; // True if the local player in a 2-player game has a draw offer extended. + bool bOtherProposesDraw; // True if the other player in a 2-player game has a draw offer extended. +#endif + +}; + + +#endif diff --git a/REDALERT/SCORE.CPP b/REDALERT/SCORE.CPP new file mode 100644 index 000000000..4314a196d --- /dev/null +++ b/REDALERT/SCORE.CPP @@ -0,0 +1,1942 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SCORE.CPP 3 3/14/97 12:02a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : May 3, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * Draw_Infantrymen -- Draw all the guys on the score screen * + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen * + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly * + * ScoreClass::DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen * + * ScoreClass::Delay -- Pauses waiting for keypress. * + * ScoreClass::Presentation -- Main routine to display score screen. * + * ScoreClass::Print_Graph_Title -- Prints title on score screen. * + * ScoreClass::Print_Minutes -- Print out hours/minutes up to max * + * ScoreClass::Pulse_Bar_Graph -- Pulses the bargraph color. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WIN32 +extern short StreamLowImpact; +#endif + +#include "function.h" + +#define SCORETEXT_X 184 +#define SCORETEXT_Y 8 +#define CASUALTY_Y 88 +#define BUILDING_X 256 +#define BUILDING_Y 128 +#define BARGRAPH_X 266 +#define MAX_BAR_X 318 // max possible is 319 because of bar's right shadow +#define SIZEGBAR 118 +#define HALLFAME_X 11 +#define HALLFAME_Y 120 + +#define MULTISCOREX 30 + +#define TEDIT_FAME 1 +#define NUMINFANTRYMEN 10 +#define NUMFAMENAMES 7 +#define MAX_FAMENAME_LENGTH 11 + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +GraphicBufferClass *PseudoSeenBuff; + +struct InfantryAnim { + int xpos; + int ypos; + void const *shapefile; + void const *remap; + int anim; + int stage; + char delay; + InfantryTypeClass const *Class; +} InfantryMan[NUMINFANTRYMEN]; +void Draw_InfantryMen(void); +void Draw_InfantryMan(int index); +void New_Infantry_Anim(int index, int anim); +void Draw_Bar_Graphs(int i, int gkilled, int nkilled); +void Animate_Cursor(int pos, int ypos); +void Animate_Score_Objs(void); +void Cycle_Wait_Click(bool cycle=true); + +#ifdef FIXIT_SCORE_CRASH +//void Disable_Uncompressed_Shapes (void); +//void Enable_Uncompressed_Shapes (void); +#endif //FIXIT + +void const * Beepy6; +int ControlQ; // cheat key to skip past score/mapsel screens +bool StillUpdating; + +#ifdef WIN32 +char *ScreenNames[2]={"ALIBACKH.PCX", "SOVBACKH.PCX"}; +#else +char *ScreenNames[2]={"ALI-TRAN.WSA", "SOV-TRAN.WSA"}; +#endif + +//#ifdef WIN32 +//TextBlitClass BlitList; +//#endif + + +struct Fame { + char name[MAX_FAMENAME_LENGTH]; + int score; + int level; + int side; +}; + +ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + + +ScoreAnimClass::ScoreAnimClass(int x, int y, void const * data) +{ + XPos = x * RESFACTOR; + YPos = y * RESFACTOR; + Timer = 0; + DataPtr = data; +} + + +ScoreTimeClass::ScoreTimeClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; +} + +void ScoreTimeClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + +ScoreCredsClass::ScoreCredsClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; + Clock1 = MFCD::Retrieve("CLOCK1.AUD"); + CashTurn = MFCD::Retrieve("CASHTURN.AUD"); +} + + +void ScoreCredsClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(Clock1, 255, Options.Normalize_Volume(130)); +#else + Play_Sample(Clock1, 255, Options.Normalize_Volume(50)); +#endif + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + + +ScorePrintClass::ScorePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +ScorePrintClass::ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void ScorePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + delete this; + return; + } + +#ifdef WIN32 + StillUpdating = true; +#endif + if (!Timer) { + Timer = 1; + + int pos = XPos+(Stage*(6*RESFACTOR)); +// print the letter properly + if (Stage) { + Set_Font_Palette(PrimaryPalette); + localstr[0]=((char *)DataPtr)[Stage-1]; + HidPage.Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); + PseudoSeenBuff->Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); +#endif + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + SeenPage.Print(localstr, pos, YPos-1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos, YPos+1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(localstr, pos, YPos-1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos, YPos+1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#endif + } + Stage++; + } +} + + +ScoreScaleClass::ScoreScaleClass(void const * string, int xpos, int ypos, char const palette[]) : + ScoreAnimClass(xpos, ypos, string) +{ + Palette = &palette[0]; +#ifdef WIN32 + Stage = 0; +#else + Stage = 5; +#endif +} + + +void ScoreScaleClass::Update(void) +{ + static int _destx[]={0,80,107,134,180,228}; + static int _destw[]={6,20, 30, 40, 60, 80}; + + /* + ** Restore the background for the scaled-up letter + */ + if (!Timer) { + Timer = 1; +#ifndef WIN32 + if (Stage != 5) { + int destx = _destx[Stage+1]*RESFACTOR; + int destw = _destw[Stage+1]*RESFACTOR; + HidPage.Blit(SeenPage, destx, YPos, destx, YPos, (destx + destw) <= 320 * RESFACTOR ? destw : (320 * RESFACTOR) - destx, (YPos + destw) <= 200 * RESFACTOR ? destw : (200 * RESFACTOR) - YPos); + } +#endif + if (Stage) { + Set_Font_Palette(Palette); + HidPage.Fill_Rect(0, 0, 7*RESFACTOR, 7*RESFACTOR, TBLACK); + HidPage.Print((char *)DataPtr, 0, 0, TBLACK, TBLACK); + HidPage.Scale(SeenPage, 0, 0, _destx[Stage]*RESFACTOR, YPos, 5*RESFACTOR, 6*RESFACTOR, _destw[Stage]*RESFACTOR, _destw[Stage]*RESFACTOR, true); + Stage--; + } else { + Set_Font_Palette(Palette); + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]==this) ScoreObjs[i] = 0; + } + HidPage.Print((char *)DataPtr, XPos, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#endif + delete this; + return; + } + } +} + +int Alloc_Object(ScoreAnimClass *obj) +{ + int i,ret; + + for (i = ret = 0; i < MAXSCOREOBJS; i++) { + if (!ScoreObjs[i]) { + ScoreObjs[i] = obj; + ret = i; + break; + } + } + return(ret); +} + + + +/*********************************************************************************************** + * ScoreClass::Presentation -- Main routine to display score screen. * + * * + * This is the main routine that displays the score screen graphics. * + * It gets called at the end of each scenario and is used to present * + * the results and a rating of the player's battle. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 : Created. * + *=============================================================================================*/ +static unsigned char const _bluepal[]={0xC0,0xC1,0xC1,0xC3,0xC2,0xC5,0xC3,0xC7,0xC4,0xC9,0xCA,0xCB,0xCC,0xCD,0xC0,0xCF}; +static unsigned char const _greenpal[]={0x70,0x71,0x7C,0x73,0x7D,0x75,0x7E,0x77,0x7F,0x79,0x7A,0x7B,0x7C,0x7D,0x7C,0x7F}; +static unsigned char const _redpal[]={0xD0,0xD1,0xD7,0xD3,0xD9,0xD5,0xDA,0xD7,0xDB,0xD9,0xDA,0xDB,0xDC,0xDD,0xD6,0xDF}; +static unsigned char const _yellowpal[]={0x0,0x0,0xEC,0x0,0xEB,0x0,0xEA,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; +void ScoreClass::Presentation(void) +{ +#if (0)//PG +#ifdef WIN32 +// if (Keyboard != NULL) return; +#endif + static int const _casuax[2]={144,150}; + static int const _casuay[2]={ 78, 78}; + static int const _gditxy[2]={ 90, 90}; + +#if defined(FRENCH) || defined(GERMAN) + static int const _gditxx[2]={130,150}; + static int const _nodtxx[2]={130,150}; +#else + static int const _gditxx[2]={135,150}; + static int const _nodtxx[2]={135,150}; +#endif + static int const _nodtxy[2]={102,102}; + static int const _bldggy[2]={138,138}; + static int const _bldgny[2]={150,150}; + +#ifdef WIN32 +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Disable_Uncompressed_Shapes(); +#endif //FIXIT + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + int i; + void const * yellowptr; + void const * redptr; + CCFileClass file(FAME_FILE_NAME); + struct Fame hallfame[NUMFAMENAMES]; + void *oldfont; + int oldfontxspacing = FontXSpacing; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 +#ifdef WIN32 + char inter_pal[15]; + sprintf(inter_pal, "SCORPAL1.PAL"); +#endif + + ControlQ = 0; + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_SCORE); + +#ifdef WIN32 + VisiblePage.Clear(); + SysMemPage.Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + Set_Logic_Page(SysMemPage); +#else + SeenPage.Clear(); + HidPage.Clear(); + Set_Logic_Page(HidPage); +#endif + BlackPalette.Set(); + + + void const * country4 = MFCD::Retrieve("COUNTRY4.AUD"); + void const * sfx4 = MFCD::Retrieve("SFX4.AUD"); + Beepy6 = MFCD::Retrieve("BEEPY6.AUD"); + + /* + ** Load the background for the score screen + */ +#ifndef WIN32 + void *anim = Open_Animation(ScreenNames[house], NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); +#endif + + unsigned minutes = (unsigned)((ElapsedTime / (long)TIMER_MINUTE))+1; + +// Load up the shapes for the Nod score screen +#ifdef WIN32 + yellowptr = MFCD::Retrieve("BAR3BHR.SHP"); + redptr = MFCD::Retrieve("BAR3RHR.SHP"); +#else + if (!house) { + yellowptr = MFCD::Retrieve("BAR3BLU.SHP"); + redptr = MFCD::Retrieve("BAR3RED.SHP"); + } +#endif + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + +/* --- Now display the background animation --- */ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen(ScreenNames[house], &HidPage, ScorePalette); + Increase_Palette_Luminance (ScorePalette , 30, 30, 30, 63); + HidPage.Blit(SeenPage); + HidPage.Blit(*PseudoSeenBuff); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(60)); +#endif + +#ifndef WIN32 + int frame = 1; + StreamLowImpact = true; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, SeenPage, frame++); + Call_Back_Delay(2); + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); +#endif + + /* + ** Background's up, so now load various shapes and animations + */ +#ifdef WIN32 + void const * timeshape = MFCD::Retrieve("TIMEHR.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISC1-HR.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISC2-HR.SHP"); +#else + void const * timeshape = MFCD::Retrieve("TIME.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISCORE1.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISCORE2.SHP"); +#endif + ScoreObjs[0] = new ScoreTimeClass(238, 2, timeshape, 30, 4); + ScoreObjs[1] = new ScoreTimeClass(4, 89, hiscore1shape, 10, 4); + ScoreObjs[2] = new ScoreTimeClass(4, 180, hiscore2shape, 10, 4); + + /* Now display the stuff */ +#ifdef WIN32 +#else + SeenPage.Blit(HidPage); +#endif + Set_Logic_Page(SeenBuff); + + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 198, 9, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 204, 9, _greenpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_LEAD, 164, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_EFFI, 164, 38, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOTA, 164, 50, _greenpal)); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(13); + + int scorecounter = 0; + + Keyboard->Clear(); + + /* + ** Determine leadership rating. + */ + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + HousesType owner = object->Owner(); + if ( (house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE) ) { + leadership++; + } else { + if ( (!house) && (object->Owner() == HOUSE_GREECE) ) { + leadership++; + } + } + } + int uspoints = 0; + + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass *hows = HouseClass::As_Pointer(hous); + if (hous == HOUSE_USSR || hous == HOUSE_BAD || hous == HOUSE_UKRAINE) { + NKilled += hows->UnitsLost; + NBKilled += hows->BuildingsLost; + } else { + GKilled += hows->UnitsLost; + GBKilled += hows->BuildingsLost; + } + if (PlayerPtr->Is_Ally(hous) ) { + uspoints += hows->PointTotal; + } + } +// if(uspoints < 0) uspoints = 0; +// uspoints += 1000; //BG 1000 bonus points for winning mission + + /* + ** Bias the base score upward according to the difficulty level. + */ + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + + + if (!leadership) leadership++; + leadership = 100*fixed(leadership, (house ? NKilled+NBKilled+leadership : GKilled+GBKilled+leadership)); + leadership = min(150,leadership); + + /* + ** Determine economy rating. + */ + int init = PlayerPtr->Control.InitialCredits; + int cred = PlayerPtr->Available_Money(); + + int economy = 100*fixed((unsigned)PlayerPtr->Available_Money()+1+PlayerPtr->StolenBuildingsCredits, PlayerPtr->HarvestedCredits + (unsigned)PlayerPtr->Control.InitialCredits+1); + economy=min(economy,150); + + int total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (total < -9999) total = -9999; + total = min(total, 99999); + +Keyboard->Clear(); + for (i = 0; i <= 130; i++) { + Set_Font_Palette(_greenpal); + int lead = (leadership * i) / 100; + Count_Up_Print("%3d%%", lead, leadership, 244, 26); + if (i>=30) { + int econo = (economy * (i-30)) / 100; + Count_Up_Print("%3d%%", econo, economy, 244, 38); + } + Print_Minutes(minutes); + Call_Back_Delay(1); +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(100)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(40)); +#endif + if ( (i >= 30) && (i >= leadership) && ((i-30) >= economy) ) break; +//BG if (Keyboard->Check()) break; + } + Count_Up_Print("%3d%%", leadership, leadership, 244, 26); + Count_Up_Print("%3d%%", economy, economy, 244, 38); + + char buffer[16]; + sprintf(buffer, "x %5d",uspoints); + Alloc_Object(new ScorePrintClass(buffer, 274, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(buffer, 274, 38, _greenpal)); + Call_Back_Delay(8); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, WHITE); + Call_Back_Delay(1); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, GREEN); + + sprintf(buffer,"%5d", total); + Alloc_Object(new ScorePrintClass(buffer, 286, 50, _greenpal)); + +//BG if (!Keyboard->Check()) { + Call_Back_Delay(60); +//BG } + + if (house) Show_Credits(house, _greenpal); + +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(60); + + /* + ** Show stats on # of units killed + */ + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + int indx = house; +#ifdef WIN32 + indx = 0; +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_CASU, _casuax[indx], _casuay[indx], _greenpal)); + Call_Back_Delay(9); + if (house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _gditxy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _nodtxy[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _gditxy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _nodtxy[indx], _redpal)); + } + Call_Back_Delay(6); + + Set_Font_Palette(_redpal); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); +#else + if (house) { + Do_Nod_Casualties_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); + } +#endif + + Set_Logic_Page(SeenBuff); + + /* + ** Print out stats on buildings destroyed + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif +#ifdef WIN32 + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); +#else + if (!house) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); + } else { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL1, 150, 118, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL2, 150, 126, _greenpal)); + Call_Back_Delay(13); + } +#endif + if(house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldggy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldgny[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldggy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldgny[indx], _redpal)); + } + Call_Back_Delay(7); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); +#else + if (house) { + Call_Back_Delay(6); + Set_Font_Palette(_greenpal); + Do_Nod_Buildings_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); + } +#endif + +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + + Keyboard->Clear(); + + if (!house) Show_Credits(house, _greenpal); + /* + ** Hall of fame display and processing + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 28, 110, _greenpal)); + Call_Back_Delay(9); + + /* + ** First check for the existence of the file, and if there isn't one, + ** make a new one filled with blanks. + */ + if (!file.Is_Available()) { + + // hall of fame doesn't exist, so blank it out & write it + file.Open(WRITE); + + for (i = 0; i < NUMFAMENAMES; i++) { + hallfame[i].name[0] = + hallfame[i].score = + hallfame[i].level = 0; + hallfame[i].side = 0; + file.Write(&hallfame[i], sizeof(struct Fame)); + } + + file.Close(); + } + + file.Open(READ); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Read(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + + /* + ** If the player's score is good enough to bump someone off the list, + ** remove their data, move everyone down a notch, and set index = where + ** their info goes + */ + if (hallfame[NUMFAMENAMES-1].score >= total) + hallfame[NUMFAMENAMES-1].score = 0; + int index; + for (index = 0; index < NUMFAMENAMES; index++) { + if (total > hallfame[index].score) { + if (index < (NUMFAMENAMES-1)) for (i = (NUMFAMENAMES-1); i > index; i--) hallfame[i] = hallfame[i-1]; + hallfame[index].score = total; + hallfame[index].level = Scen.Scenario; + hallfame[index].name[0] = 0; // blank out the name + hallfame[index].side = house; + break; + } + } + + /* + ** Now display the hall of fame + */ + Set_Logic_Page(SeenBuff); + +#ifdef WIN32 + char maststr[NUMFAMENAMES*32]; +#endif + char const *pal; + for (i = 0; i < NUMFAMENAMES; i++) { + pal = hallfame[i].side ? _redpal : _bluepal; + Alloc_Object(new ScorePrintClass(hallfame[i].name, HALLFAME_X, HALLFAME_Y + (i*8), pal)); + if (hallfame[i].score) { +#ifdef WIN32 + char *str = maststr + i*32; +#else + char *str = (char *)(HidPage.Get_Buffer()) + i*32; +#endif + sprintf(str, "%d", hallfame[i].score); + Alloc_Object(new ScorePrintClass(str, HALLFAME_X+(6*14), HALLFAME_Y + (i*8), pal, BLACK)); + if (hallfame[i].level < 20) { + sprintf(str+16, "%d", hallfame[i].level); + } else { + strcpy(str+16, "**"); + } + Alloc_Object(new ScorePrintClass(str+16, HALLFAME_X+(6*11), HALLFAME_Y + (i*8), pal, BLACK)); + Call_Back_Delay(13); + } + } +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + /* + ** If the player's on the hall of fame, have him enter his name now + */ + Keyboard->Clear(); + + if (index < NUMFAMENAMES) { + pal = hallfame[index].side ? _redpal : _bluepal; + Input_Name(hallfame[index].name, HALLFAME_X, HALLFAME_Y + (index*8), pal); + + file.Open(WRITE); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Write(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + } else { + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 149, 190, _yellowpal)); + ControlQ = false; + Cycle_Wait_Click(); + } + + Keyboard->Clear(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + Show_Mouse(); +// Map_Selection(); +// Scen.ScenVar = SCEN_VAR_A; +// Scen.ScenDir = SCEN_DIR_EAST; + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + GamePalette.Set(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + +#ifdef WIN32 + delete PseudoSeenBuff; +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Enable_Uncompressed_Shapes(); +#endif //FIXIT + +#endif +#endif +} + + +void Cycle_Wait_Click(bool cycle) +{ + int counter = 0; + int minclicks = 20; + unsigned long timingtime = TickCount; + //PG SerialPacketType sendpacket; + //PG SerialPacketType receivepacket; + //PG int packetlen; + + + Keyboard->Clear(); + while (minclicks || (!Keyboard->Check() && !ControlQ) ) { +#if (0) //PG + if (Session.Type == GAME_NULL_MODEM || + Session.Type == GAME_MODEM) { + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&sendpacket, 0, sizeof(SerialPacketType)); + sendpacket.Command = SERIAL_SCORE_SCREEN; + sendpacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + sendpacket.ID = Session.ModemType; + + NullModem.Send_Message (&sendpacket, sizeof(sendpacket), 0); + timingtime = TickCount; + } + + if (NullModem.Get_Message (&receivepacket, &packetlen) > 0) { + // throw packet away + packetlen = packetlen; + } + + NullModem.Service(); + } +#endif + Call_Back_Delay(1); + if (minclicks) { + minclicks--; + Keyboard->Clear(); + } + + if(cycle) { + counter = ((++counter) & 7); + if (counter == 0 && Options.IsPaletteScroll) { + RGBClass rgb = ScorePalette[233]; + for (int i = 233; i < 237; i++) { + ScorePalette[i] = ScorePalette[i+1]; + } + ScorePalette[237] = rgb; + ScorePalette.Set(); + } + } + } + Keyboard->Clear(); +} + +void ScoreClass::Do_Nod_Buildings_Graph(void) +{ + int shapenum; + InfantryTypeClass const *ramboclass; + + void const * factptr = MFCD::Retrieve("POWR.SHP"); + void const * rmboptr = MFCD::Retrieve("E7.SHP"); + void const * fball1ptr = MFCD::Retrieve("FBALL1.SHP"); + ramboclass = &InfantryTypeClass::As_Reference(INFANTRY_TANYA); + + /* + ** Print the # of buildings on the hidpage so we only need to do it once + */ + SeenPage.Blit(HidPage); + Set_Logic_Page(HidPage); + Call_Back_Delay(30); + Set_Font_Palette(_redpal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 10, TBLACK, TBLACK); + Set_Font_Palette(_bluepal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 22, TBLACK, TBLACK); + + /* + ** Here's the animation/draw loop for blowing up the factory + */ + int i; + for (i=0; i<98; i++) { + HidPage.Blit(HidPage, BUILDING_X, BUILDING_Y, 0, 0, 320-BUILDING_X, 48); + shapenum = 0; // no damage + if (i >= 60) { + shapenum = Extract_Shape_Count(factptr) - 2; // some damage + if (i == 60) { + Shake_The_Screen(6); + Sound_Effect(VOC_CRUMBLE); + } + if (i > 65) { + shapenum = Extract_Shape_Count(factptr) - 1; // mega damage + } + } + + /* + ** Draw the building before Rambo + */ + if (i < 68) { + CC_Draw_Shape(factptr, shapenum, 0, 0, WINDOW_MAIN, + SHAPE_GHOST|SHAPE_FADING|SHAPE_WIN_REL, ColorRemaps[PCOLOR_GOLD].RemapTable, DisplayClass::UnitShadow); + + } + + /* + ** Now draw some fires, if appropriate + */ + if (i >= 61) { + int firecount = Extract_Shape_Count(fball1ptr); + int shapeindex = (i-61) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 10, 10, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + if (i > 64) { + shapeindex = (i-64) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 50, 30, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + } + } + /* + ** Draw the Tanya character running away from the building + */ + CC_Draw_Shape(rmboptr, (ramboclass->DoControls[DO_WALK].Frame + ramboclass->DoControls[DO_WALK].Jump*6) + ((unsigned(i)>>1)%ramboclass->DoControls[DO_WALK].Count), + i+32, 40, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + ColorRemaps[PCOLOR_RED].RemapTable, DisplayClass::UnitShadow); + HidPage.Blit(SeenPage, 0, 0, BUILDING_X, BUILDING_Y, 320-BUILDING_X, 48); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(1); + } + + i = max(GBKilled, NBKilled); + for (int q = 0; q <= i; q++) { + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", q, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", q, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(1); +//BG } + } + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", NBKilled, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", GBKilled, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +} + + +/*************************************************************************** + * DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen* + * * + * * + * * + * INPUT: yellowptr, redptr = pointers to shape file for graphs * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 BWG : Created. * + *=========================================================================*/ + +void ScoreClass::Do_GDI_Graph(void const * yellowptr, void const * redptr, int gkilled, int nkilled, int ypos) +{ + int i, maxval; +#ifdef WIN32 + int xpos = 174; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 + if(house) { + int temp = gkilled; + gkilled = nkilled; + nkilled = temp; + void const *tempptr = yellowptr; + yellowptr = redptr; + redptr = tempptr; + } +#else + int xpos = 173; +#endif + int gdikilled = gkilled, nodkilled=nkilled; + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + gdikilled = (gdikilled * SIZEGBAR) / maxval; + nodkilled = (nodkilled * SIZEGBAR) / maxval; + if (maxval < 20) { + gdikilled = gkilled * 5; + nodkilled = nkilled * 5; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + // Draw the white-flash shape on the hidpage + Set_Logic_Page(HidPage); + HidPage.Fill_Rect(0, 0, 124*RESFACTOR, 9*RESFACTOR, TBLACK); + CC_Draw_Shape(redptr, 119, 0, 0, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Set_Font_Palette(house ? _redpal : _bluepal); +#else + Set_Font_Palette(_bluepal); +#endif + + for (i = 1; i <= gdikilled; i++) { + if (i != gdikilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*gkilled) / maxval, gkilled, 297, ypos+2); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + Count_Up_Print("%d", gkilled, gkilled, 297, ypos+ 2); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); + +#ifdef WIN32 + Set_Font_Palette(house ? _bluepal : _redpal); +#else + Set_Font_Palette(_redpal); +#endif + for (i = 1; i <= nodkilled; i++) { + if (i != nodkilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*nkilled) / maxval, nkilled, 297, ypos+14); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + +// if (Keyboard::Check()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", nkilled, nkilled, 297, ypos+14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); +} + + +void ScoreClass::Do_Nod_Casualties_Graph(void) +{ + int i, gdikilled, nodkilled, maxval; + + void const * e1ptr = MFCD::Retrieve("E1.SHP"); + + gdikilled = GKilled; + nodkilled = NKilled; + maxval = max(gdikilled, nodkilled); + + if (!maxval) maxval=1; + if ((gdikilled > (MAX_BAR_X - BARGRAPH_X)) || (nodkilled > (MAX_BAR_X - BARGRAPH_X)) ) { + gdikilled = (gdikilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + nodkilled = (nodkilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + /* + ** Initialize a bunch of objects for the infantrymen who pose for the bar + ** graphs of casualties. + */ + int r = NUMINFANTRYMEN/2; + for (i = 0; i < NUMINFANTRYMEN/2; i++) { + InfantryMan[i+0].xpos = + InfantryMan[i+r].xpos = (i*10) + 7; + InfantryMan[i+0].ypos = 11; + InfantryMan[i+r].ypos = 21; + InfantryMan[i+0].shapefile = + InfantryMan[i+r].shapefile = e1ptr; + InfantryMan[i+0].remap = ColorRemaps[PCOLOR_RED].RemapTable; + InfantryMan[i+r].remap = ColorRemaps[PCOLOR_BLUE].RemapTable; + InfantryMan[i+0].anim = + InfantryMan[i+r].anim = 0; + InfantryMan[i+0].stage = + InfantryMan[i+r].stage = 0; + InfantryMan[i+0].delay = + InfantryMan[i+r].delay = NonCriticalRandomNumber & 0x1F; + InfantryMan[i+0].Class = + InfantryMan[i+r].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + } + + /* + ** Draw the infantrymen and pause briefly before running the graph + */ + Draw_InfantryMen(); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(40); + + for (i = 1; i <= maxval; i++) { + // Draw & update infantrymen 3 times for every tick on the graph (i) + for (int index = 0; index < 3; index++) { + Draw_InfantryMen(); + Draw_Bar_Graphs(i, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + + Set_Font_Palette(_redpal); + Count_Up_Print("%d", (i*NKilled) / maxval, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", (i*GKilled) / maxval, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(3); + } +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + } +//BG if (Keyboard->Check()) Keyboard->Clear(); + + /* + ** Make sure accurate count is printed at end + */ + Set_Font_Palette(_redpal); + Count_Up_Print("%d", NKilled, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", GKilled, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + + /* + ** Finish up death animations, if there are any active + */ + int k = 1; + while (k) { + for (i=k=0; i= DO_GUN_DEATH) { + k=1; + } + } + if (k) { + Draw_InfantryMen(); + } + Draw_Bar_Graphs(maxval, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(1); + } +} + + +void ScoreClass::Show_Credits(int house, char const pal[]) +{ + static int _credsx[2]={276,276}; + static int _credsy[2]={173,58}; + static int _credpx[2]={228,236}; +#ifdef GERMAN + static int _credpy[2]={181, 74}; + static int _credtx[2]={162,162}; + static int _credty[2]={173, 62}; +#else + static int _credpy[2]={189-12, 74}; + static int _credtx[2]={182,182}; + static int _credty[2]={179-12, 62}; +#endif + + int credobj,i; + int minval,add; + +#ifdef WIN32 + void const * credshape = MFCD::Retrieve(house ? "CREDSUHR.SHP" : "CREDSAHR.SHP"); +#else + void const * credshape = MFCD::Retrieve(house ? "CREDSU.SHP" : "CREDSA.SHP"); +#endif + + Alloc_Object(new ScorePrintClass(TXT_SCORE_ENDCRED, _credtx[house], _credty[house], pal)); + Call_Back_Delay(15); + + credobj = Alloc_Object(new ScoreCredsClass(_credsx[house], _credsy[house], credshape, 32, 2)); + minval = PlayerPtr->Available_Money() / 100; + + /* + ** Print out total credits left at end of scenario + */ + i = -50; + + do { + add = 5; + if ((PlayerPtr->Available_Money() - i) > 100 ) add += 15; + if ((PlayerPtr->Available_Money() - i) > 500 ) add += 30; + if ((PlayerPtr->Available_Money() - i) > 1000) add += PlayerPtr->Available_Money() / 40; + if (add < minval) add = minval; + i += add; + + if (i < 0) i=0; + + Set_Font_Palette(pal); + Count_Up_Print("%d", i, PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Call_Back_Delay(2); +/*BG if (Keyboard->Check()) { + Count_Up_Print("%d", PlayerPtr->Available_Money(), PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Keyboard->Clear(); + break; + }*/ + } while (i < PlayerPtr->Available_Money()) ; + + delete ScoreObjs[credobj]; + ScoreObjs[credobj] = 0; +} + + +/*************************************************************************** + * SCORECLASS::PRINT_MINUTES -- Print out hours/minutes up to max * + * * + * Same as count-up-print, but for the time * + * * + * INPUT: current minute count and maximum * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void ScoreClass::Print_Minutes(int minutes) +{ + char str[20]; + if (minutes >= 60) { + if ((minutes/60) > 9) minutes = (9*60 + 59); + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT1), (minutes / 60), (minutes % 60)); + } else { + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT2), minutes); + } + SeenPage.Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly. * + * * + * This routine prints out a number (like 70) or its maximum number, into a string, onto * + * the screen, on a clean section of the screen, and blits it forward to the seenpage so you* + * can print without flashing and can print over something (to count up %'s). * + * * + * INPUT: str = string to print into * + * percent = # to print * + * max = # to print if percent > max * + * xpos = x pixel coord * + * ypos = y pixel coord * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/07/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Count_Up_Print(char *str, int percent, int maxval, int xpos, int ypos) +{ + char destbuf[64]; + + sprintf(destbuf, str, percent <= maxval ? percent : maxval); + SeenPage.Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#ifdef WIN32 + PseudoSeenBuff->Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Input_Name -- Gets the name from the keyboard * + * * + * This routine handles keyboard input, and does a nifty zooming letter effect too. * + * * + * INPUT: str = string to put user's typing into * + * xpos = x pixel coord * + * ypos = y pixel coord * + * pal = text remapping palette to print using * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Input_Name(char str[], int xpos, int ypos, char const pal[]) +{ + int key = 0; + int ascii, index=0; + + void const * keystrok = MFCD::Retrieve("KEYSTROK.AUD"); + + /* + ** Ready the hidpage so it can restore background under zoomed letters + */ + SeenPage.Blit(HidPage); + + /* + ** Put a copy of the high score area on a spare area of the hidpage, so + ** we can use it to restore the letter's background instead of filling + ** with black. + */ + HidPage.Blit(HidPage, 0, 100*RESFACTOR, 0, 0, 100*RESFACTOR, 100*RESFACTOR); + + do { + Call_Back(); + Animate_Score_Objs(); + Animate_Cursor(index, ypos); + if (Keyboard->Check()) { + key = Keyboard->To_ASCII(Keyboard->Get()) & 0xFF; + Call_Back(); + + if (index == MAX_FAMENAME_LENGTH-2) { + while (Keyboard->Check()) { + Keyboard->Get(); + } + } + + /* + ** If they hit 'backspace' when they're on the last letter, + ** turn it into a space instead. + */ + if ((key == KA_BACKSPACE) && (index == MAX_FAMENAME_LENGTH-2) ) { + if (str[index] && str[index]!=32) key = 32; + } + if (key == KA_BACKSPACE) { //if (key == KN_BACKSPACE) { + if (index) { + str[--index] = 0; + + int xposindex6 = (xpos+(index*6))*RESFACTOR; + HidPage.Blit(SeenPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #endif + HidPage.Blit(HidPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + } + + } else if (key != KA_RETURN) { //else if (key != KN_RETURN && key!=KN_KEYPAD_RETURN) { + ascii = key; //ascii = KN_To_KA(key); + if (ascii >= 'a' && ascii <= 'z') ascii -= ('a' - 'A'); + if ( (ascii >= '!' && ascii <= KA_TILDA) || ascii == ' ') { + HidPage.Blit(SeenPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #endif + HidPage.Blit(HidPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + str[index] = ascii; + str[index+1] = 0; + + int objindex; +#ifdef WIN32 + Play_Sample(keystrok, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(keystrok, 255, Options.Normalize_Volume(105)); +#endif + objindex = Alloc_Object(new ScoreScaleClass(str+index, xpos+(index*6), ypos, pal)); + while (ScoreObjs[objindex]) Call_Back_Delay(1); + + if (index < (MAX_FAMENAME_LENGTH-2) ) index++; + } + } + } + } while (key != KA_RETURN); // } while(key != KN_RETURN && key!=KN_KEYPAD_RETURN); +} + + +void Animate_Cursor(int pos, int ypos) +{ + static int _lastpos = 0, _state; + static CDTimerClass _timer; + + ypos += 6; // move cursor to bottom of letter + + ypos *= RESFACTOR; + + // If they moved the cursor, erase old one and force state=0, to make green draw right away + if (pos != _lastpos) { + HidPage.Blit(SeenPage, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#endif + _lastpos = pos; + _state = 0; + } + SeenBuff.Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#endif + /* + ** Toggle the color of the cursor, green or black, if it's time to do so. + */ + if (!_timer) { + _state ^= 1; + _timer = 5; + } +} + + +/*************************************************************************** + * Draw_InfantryMen -- Draw all the guys on the score screen * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMen() +{ + int k; + +// Only draw the infantrymen if we're playing USSR... Allies wouldn't execute +// people like that. + + /* + ** First restore the background + */ + HidPage.Blit(HidPage, BARGRAPH_X, CASUALTY_Y, 0, 0, 320-BARGRAPH_X, 34); + Set_Logic_Page(HidPage); + + /* + ** Then draw all the infantrymen on the clean hidpage + */ + for (k = 0; k < NUMINFANTRYMEN; k++) Draw_InfantryMan(k); + /* + ** They'll all be blitted over to the seenpage after the graphs are drawn + */ +} + +/*************************************************************************** + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * * + * This routine draws one of the infantrymen in the "Casualties" area * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMan(int index) +{ + int stage; + + /* If the infantryman's dead, just abort this function */ + if (InfantryMan[index].anim == -1) return; + + stage = InfantryMan[index].stage + InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Frame; + + CC_Draw_Shape(InfantryMan[index].shapefile, + stage, + InfantryMan[index].xpos, + InfantryMan[index].ypos, + WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + InfantryMan[index].remap, + DisplayClass::UnitShadow); + /* + ** see if it's time to run a new anim + */ + if (--InfantryMan[index].delay <= 0) { + InfantryMan[index].delay = 3; + if (++InfantryMan[index].stage >= InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Count) { + + /* + ** was he playing a death anim? If so, and it's done, erase him + */ + if (InfantryMan[index].anim >= DO_GUN_DEATH) { + InfantryMan[index].anim = -1; + } else { + New_Infantry_Anim(index, DO_STAND_READY); + } + } + } +} + + +/*************************************************************************** + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen* + * * + * * + * * + * INPUT: index: which of the 30 infantrymen to affect * + * anim: which animation sequence to start him into * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void New_Infantry_Anim(int index, int anim) +{ + InfantryMan[index].anim = anim; + InfantryMan[index].stage = 0; + if (anim >= DO_GUN_DEATH) { + InfantryMan[index].delay = 1; // start right away + } else { + InfantryMan[index].delay = NonCriticalRandomNumber & 15; + } +} + + +/*************************************************************************** + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * * + * * + * * + * INPUT: i = current count of how far to draw graph * + * gkilled = # of GDI forces killed (adjusted to fit in space) * + * nkilled = # of Nod forces killed (adjusted to fit in space) * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + * 07/02/1996 BWG : Removed references to civilians. * + *=========================================================================*/ +void Draw_Bar_Graphs(int i, int gkilled, int nkilled) +{ + + if (gkilled) { + LogicPage->Fill_Rect(0, 0+4*RESFACTOR, 0+min(i, gkilled)*RESFACTOR, 0+5*RESFACTOR, RED); + LogicPage->Draw_Line(0+1*RESFACTOR, 0+6*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+6*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, TBLACK); + if (i <= gkilled) { + int anim = InfantryMan[i/11].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(i/11, DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim(i/11, DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + if (nkilled) { + LogicPage->Fill_Rect( 0, 0+16*RESFACTOR, 0+min(i, nkilled)*RESFACTOR, 0+17*RESFACTOR, LTCYAN); + LogicPage->Draw_Line( 0+1*RESFACTOR, 0+18*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+18*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, TBLACK); + if (i <= nkilled) { + int anim = InfantryMan[(NUMINFANTRYMEN/2)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } +} + + +/*************************************************************************** + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * * + * This is just to cut down on code size and typing a little. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Call_Back_Delay(int time) +{ + time; +#if (0)//PG + if (time < 0 ) time = 0; + if (time > 60) time = 60; + CDTimerClass cd; + CDTimerClass callbackcd = 0; + + if (!ControlQ) { + if (Keyboard->Down(KN_LCTRL) && Keyboard->Down(KN_Q)) { + ControlQ = 1; + Keyboard->Clear(); + } + } + if (ControlQ) time=0; + + cd = time; + StreamLowImpact = true; + do { + if (callbackcd == 0) { + Call_Back(); + callbackcd = TIMER_SECOND/4; + } + Animate_Score_Objs(); + } while (cd); + StreamLowImpact = false; +#endif +} + + +void Animate_Score_Objs() +{ +#ifdef WIN32 + StillUpdating = false; + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + PseudoSeenBuff->Blit(SeenPage); + } +#endif + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]) { + ScoreObjs[i]->Update(); + } + } +} + +char *Int_Print(int a) +{ + static char str[10]; + + sprintf(str, "%d", a); + return str; +} + + +/*********************************************************************************************** + * Multi_Score_Presentation -- Multiplayer routine to display score screen. * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 BWG: Created. * + *=============================================================================================*/ +extern int CopyType; + +void Multi_Score_Presentation(void) +{ +#if (0)//PG + char remap[16]; +#ifdef WIN32 + GraphicBufferClass *pseudoseenbuff = new GraphicBufferClass(320, 200, (void*)NULL); + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + + int i,k; + void *oldfont; + int oldfontxspacing = FontXSpacing; + + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); +// Theme.Queue_Song(THEME_WIN); + + BlackPalette.Set(); + SeenPage.Clear(); + HidPage.Clear(); + Hide_Mouse(); + void *anim = Open_Animation("MLTIPLYR.WSA", NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); + /* + ** Display the background animation + */ +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); +for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); +CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); + + int frame = 1; + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + Animate_Frame(anim, *pseudoseenbuff, frame++); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + } + Close_Animation(anim); + +#ifdef WIN32 + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , PseudoSeenBuff , NULL); + CopyType = 0; +#endif + + /* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + + Set_Logic_Page(SeenBuff); + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 113, 13, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 130, 13, _greenpal)); +#endif + Call_Back_Delay(5); + Alloc_Object(new ScorePrintClass(TXT_COMMANDER, 27, 31, _greenpal)); + Call_Back_Delay(10); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 113, 31, _greenpal)); +#endif +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 118, 31, _greenpal)); +#endif +#ifdef ENGLISH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 126, 31, _greenpal)); +#endif + Call_Back_Delay(13); + Alloc_Object(new ScorePrintClass(TXT_KILLS_COLON, 249, 31, _greenpal)); + Call_Back_Delay(6); + + /* + ** Move all the scores over a notch if there's more games than can be + ** shown (which is known by Session.CurGame == MAX_MULTI_GAMES-1); + */ + if (Session.CurGame == MAX_MULTI_GAMES-1) { + for (i = 0; i < MAX_MULTI_NAMES; i++) { + for (k = 0; k < MAX_MULTI_GAMES-1; k++) { + Session.Score[i].Kills[k] = Session.Score[i].Kills[k+1]; + } + } + } + + int y = 41; + for (i = 0; i < MAX_MULTI_NAMES; i++) { + if (strlen(Session.Score[i].Name)) { + int color = Session.Score[i].Color; + remap[ 8] = ColorRemaps[color].FontRemap[11]; + remap[ 6] = ColorRemaps[color].FontRemap[12]; + remap[ 4] = ColorRemaps[color].FontRemap[13]; + remap[ 2] = ColorRemaps[color].FontRemap[14]; + remap[14] = ColorRemaps[color].FontRemap[15]; + + Alloc_Object(new ScorePrintClass(Session.Score[i].Name, 15, y, remap)); + Call_Back_Delay(20); + + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Wins), 118, y, remap)); + Call_Back_Delay(6); + + for (k = 0; k <= min(Session.CurGame, MAX_MULTI_GAMES-2); k++) { + if (Session.Score[i].Kills[k] >= 0) { + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Kills[k]), 225+(24*k), y, remap)); + Call_Back_Delay(6); + } + } + y += 12; + } + } + +#if defined(GERMAN) || defined(FRENCH) + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 95 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 109 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#endif + Cycle_Wait_Click(false); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); + SeenPage.Clear(); + GamePalette.Set(); +#ifdef WIN32 + delete PseudoSeenBuff; +#endif + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + Show_Mouse(); +#endif +} + +void ScoreClass::Init(void) +{ + Score = 0; + NKilled = 0; + GKilled = 0; + CKilled = 0; + NBKilled = 0; + GBKilled = 0; + CBKilled = 0; + NHarvested = 0; + GHarvested = 0; + CHarvested = 0; + ElapsedTime = 0; + RealTime = 0; + ChangingGun = 0; +} diff --git a/REDALERT/SCORE.H b/REDALERT/SCORE.H new file mode 100644 index 000000000..e5d6f9534 --- /dev/null +++ b/REDALERT/SCORE.H @@ -0,0 +1,145 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCORE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCORE_H +#define SCORE_H + +#include "unit.h" +#include "building.h" + +class ScoreClass { + public: + ScoreClass(void) {}; + ScoreClass(NoInitClass const &) {}; + + int Score; + int NKilled; + int GKilled; + int CKilled; + int NBKilled; + int GBKilled; + int CBKilled; + int NHarvested; + int GHarvested; + int CHarvested; + unsigned long ElapsedTime; + TTimerClass RealTime; + + void Init(void); + void Presentation(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + unsigned char *ChangingGun; + + void ScoreDelay(int ticks); + void Pulse_Bar_Graph(void); + void Print_Graph_Title(int,int); + void Print_Minutes(int minutes); + void Count_Up_Print(char *str, int percent, int max, int xpos, int ypos); + void Show_Credits(int house, char const pal[]); + void Do_GDI_Graph(void const * yellowptr, void const * redptr, int gdikilled, int nodkilled, int ypos); + void Do_Nod_Casualties_Graph(void); + void Do_Nod_Buildings_Graph(void); + void Input_Name(char str[], int xpos, int ypos, char const pal[]); +}; + +class ScoreAnimClass { + public: + ScoreAnimClass(int x, int y, void const * data); + int XPos; + int YPos; + CDTimerClass Timer; + void const * DataPtr; + virtual void Update(void) {} ; + virtual ~ScoreAnimClass(void) {DataPtr=0;} ; +}; + +class ScoreCredsClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + void const * CashTurn; + void const * Clock1; + + virtual void Update(void); + ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreCredsClass(void) {CashTurn=0;Clock1=0;}; +}; + +class ScoreTimeClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + virtual void Update(void); + ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreTimeClass(void) {}; +}; + +class ScorePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + ScorePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~ScorePrintClass(void) {PrimaryPalette=0;}; +}; + +class ScoreScaleClass : public ScoreAnimClass { + public: + int Stage; + char const * Palette; + virtual void Update(void); + ScoreScaleClass(void const * data, int xpos, int ypos, char const pal[]); + virtual ~ScoreScaleClass(void) {Palette=0;}; + +}; + +#define MAXSCOREOBJS 8 +extern ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + +void Multi_Score_Presentation(void); + +#endif diff --git a/REDALERT/SCREEN.H b/REDALERT/SCREEN.H new file mode 100644 index 000000000..991364021 --- /dev/null +++ b/REDALERT/SCREEN.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCREEN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 2, 1994 * + * * + * Last Update : June 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCREEN_H +#define SCREEN_H + + +class ScreenClass +{ + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseShapeType CurrentMouseShape; + MouseShapeType NormalMouseShape; + + public: + + ScreenClass(void) { + CurrentMouseShape = SHP_NONE; + NormalMouseShape = SHP_MOUSE; + }; + + + Init(void); + Set_Default_Mouse(MouseShapeType mouse); + Force_Mouse_Shape(MouseShapeType mouse); + + unsigned char *GamePalette; + unsigned char *BlackPalette; +}; + +#endif diff --git a/REDALERT/SCROLL.CPP b/REDALERT/SCROLL.CPP new file mode 100644 index 000000000..977b08ff0 --- /dev/null +++ b/REDALERT/SCROLL.CPP @@ -0,0 +1,267 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCROLL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/08/95 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ScrollClass::AI -- Handles scroll AI processing. * + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define SCROLL_DELAY 1 +#else +#define SCROLL_DELAY 2 +#endif + +CDTimerClass ScrollClass::Counter; + + +/*********************************************************************************************** + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * * + * This is the constructor for the scroll class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +ScrollClass::ScrollClass(void) : + IsAutoScroll(true) +{ + Counter = SCROLL_DELAY; + Inertia = 0; +} + + +/*********************************************************************************************** + * ScrollClass::AI -- Handles scroll AI processing. * + * * + * This routine is called every game frame for purposes of input processing. * + * * + * INPUT: input -- Reference to the keyboard/mouse event that just occurred. * + * * + * x,y -- The mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + * 08/10/1995 JLB : Revamped for free smooth scrolling. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +void ScrollClass::AI(KeyNumType &input, int x, int y) +{ +#if 0 + bool player_scrolled=false; + static DirType direction; + int rate; + + + /* + ** If rubber band mode is in progress, then don't allow scrolling of the tactical map. + */ + if (!IsRubberBand /*&& !IsTentative*/) { + + /* + ** Special check to not scroll within the special no-scroll regions. + */ + bool noscroll = false; + + if (!noscroll) { + bool at_screen_edge = (y == 0 || x == 0 || x >= SeenBuff.Get_Width()-1 || y >= SeenBuff.Get_Height()-1); + + /* + ** Verify that the mouse is over a scroll region. + */ + if (Inertia || at_screen_edge) { + if (at_screen_edge) { + + player_scrolled=true; + + /* + ** Adjust the mouse coordinates to emphasize the + ** cardinal directions over the diagonals. + */ + int altx = x; + if (altx < 50 * RESFACTOR) altx -= ((50 * RESFACTOR)-altx); + altx = max(altx, 0); + if (altx > ((320-50) * RESFACTOR)) altx += altx-((320-50) * RESFACTOR); + altx = min(altx, (320 * RESFACTOR)); + if (altx > (50 * RESFACTOR) && altx < ((320-50) * RESFACTOR)) { + altx += (((320/2) * RESFACTOR)-altx)/2; + } + + int alty = y; + if (alty < (50 * RESFACTOR)) alty -= (50 * RESFACTOR)-alty; + alty = max(alty, 0); + if (alty > (150 * RESFACTOR)) alty += alty-(150 * RESFACTOR); + alty = min(alty, 200 * RESFACTOR); + + direction = (DirType)Desired_Facing256((320/2) * RESFACTOR, (200/2) * RESFACTOR, altx, alty); + } + + int control = Dir_Facing(direction); + + /* + ** The mouse is over a scroll region so set the mouse shape accordingly if the map + ** can be scrolled in the direction indicated. + */ + static int _rate[9] = { + 0x00E0*RESFACTOR, + 0x00C0*RESFACTOR, + 0x00A0*RESFACTOR, + 0x0080*RESFACTOR, + 0x0060*RESFACTOR, + 0x0040*RESFACTOR, + 0x0020*RESFACTOR, + 0x0010*RESFACTOR, + 0x0008*RESFACTOR + }; + if (Debug_Map) { + rate = Options.ScrollRate+1; + } else { + rate = 8-Inertia; + } + + if (rate < Options.ScrollRate+1) { + rate = Options.ScrollRate+1; + Inertia = 8-rate; + } + + /* + ** Increase the scroll rate if the mouse button is held down. + */ + // if (Keyboard->Down(KN_LMOUSE)) { + // rate = Bound(rate-3, 0, 4); + // } + if (Keyboard->Down(KN_RMOUSE)) { + rate = Bound(rate+1, 4, (int)(sizeof(_rate)/sizeof(_rate[0]))-1); + } + + /* + ** If options indicate that scrolling should be forced to + ** one of the 8 facings, then adjust the direction value + ** accordingly. + */ + direction = Facing_Dir(Dir_Facing(direction)); + + int distance = _rate[rate]/2; + + if (!Scroll_Map(direction, distance, false)) { + Override_Mouse_Shape((MouseType)(MOUSE_NO_N+control), false); + } else { + Override_Mouse_Shape((MouseType)(MOUSE_N+control), false); + + /* + ** If the mouse button is pressed or auto scrolling is active, then scroll + ** the map if the delay counter indicates. + */ + if (Keyboard->Down(KN_LMOUSE) || IsAutoScroll) { + distance = _rate[rate]; + + if (Debug_Map) { + Scroll_Map(direction, distance, true); + Counter = SCROLL_DELAY; + } else { + distance = _rate[rate]; + Scroll_Map(direction, distance, true); + + if (Counter == 0 && player_scrolled) { + Counter = SCROLL_DELAY; + Inertia++; + } + } + } + } + + } + + if (!Debug_Map && !player_scrolled) { + if (!Counter) { + Inertia--; + if (Inertia<0) Inertia++; + Counter = SCROLL_DELAY; + } + } + + } + } +#endif + HelpClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * * + * This routine controls the autoscrolling setting. Autoscroll, when active, will cause the * + * map to scroll if the mouse is held over the scroll region. This is regardless of whether * + * any mouse button is held down or not. * + * * + * INPUT: control -- Should the autoscroll be turned on? * + * 0 = turn off * + * 1 = turn on * + * -1 = toggle current setting * + * * + * OUTPUT: Returns with the old setting of the autoscroll flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +bool ScrollClass::Set_Autoscroll(int control) +{ + bool old = IsAutoScroll; + + switch (control) { + case -1: + IsAutoScroll = !IsAutoScroll; + break; + + default: + IsAutoScroll = control; + break; + } + return(old); +} + + diff --git a/REDALERT/SCROLL.H b/REDALERT/SCROLL.H new file mode 100644 index 000000000..b3634936c --- /dev/null +++ b/REDALERT/SCROLL.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCROLL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCROLL_H +#define SCROLL_H + +#include "help.h" + + +class ScrollClass: public HelpClass +{ + /* + ** If map scrolling is automatic, then this flag is true. Automatic scrolling will + ** cause the map to scroll if the mouse is in the scroll region, regardless of + ** whether or not the mouse button is held down. + */ + unsigned IsAutoScroll:1; + + /* + ** Scroll speed is regulated by this count down timer. When this value reaches zero, + ** scroll the map in the direction required and reset this timer. + */ + static CDTimerClass Counter; + + /* + ** Inertia control for scrolling + */ + int Inertia; + + public: + ScrollClass(void); + ScrollClass(NoInitClass const & x) : HelpClass(x) {}; + + bool Set_Autoscroll(int control); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Init_IO(void) {Counter = 0;HelpClass::Init_IO();}; +}; + +#endif diff --git a/REDALERT/SDATA.CPP b/REDALERT/SDATA.CPP new file mode 100644 index 000000000..65488c400 --- /dev/null +++ b/REDALERT/SDATA.CPP @@ -0,0 +1,568 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SDATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static SmudgeTypeClass const Crater1 ( + SMUDGE_CRATER1, + "CR1", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater2 ( + SMUDGE_CRATER2, + "CR2", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater3 ( + SMUDGE_CRATER3, + "CR3", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater4 ( + SMUDGE_CRATER4, + "CR4", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater5 ( + SMUDGE_CRATER5, + "CR5", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater6 ( + SMUDGE_CRATER6, + "CR6", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch1 ( + SMUDGE_SCORCH1, + "SC1", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch2 ( + SMUDGE_SCORCH2, + "SC2", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch3 ( + SMUDGE_SCORCH3, + "SC3", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch4 ( + SMUDGE_SCORCH4, + "SC4", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch5 ( + SMUDGE_SCORCH5, + "SC5", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch6 ( + SMUDGE_SCORCH6, + "SC6", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); + +static SmudgeTypeClass const Bibx1 ( + SMUDGE_BIB1, + "BIB1", + TXT_BIB, + 4,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx2 ( + SMUDGE_BIB2, + "BIB2", + TXT_BIB, + 3,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx3 ( + SMUDGE_BIB3, + "BIB3", + TXT_BIB, + 2,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); + + +/*********************************************************************************************** + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * * + * This constructor is used to create the smudge type objects. These type objects contain * + * static information about the various smudge types supported in the game. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass::SmudgeTypeClass( + SmudgeType smudge, + char const * ininame, + int fullname, + int width, + int height, + bool isbib, + bool iscrater) : + ObjectTypeClass( + RTTI_SMUDGETYPE, + int(smudge), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(smudge), + Width(width), + Height(height), + IsCrater(iscrater), + IsBib(isbib) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * * + * This will allocate a smudge type class object from the special memory pool for that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the newly allocated smudge type class object. If there is insufficient* + * memory in the pool to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * SmudgeTypeClass::operator new(size_t) +{ + return(SmudgeTypes.Alloc()); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * * + * This will return the smudge type class object back to the memory pool from whence it * + * was originally allocated. * + * * + * INPUT: pointer -- Pointer to the smudge type class object to return the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::operator delete(void * pointer) +{ + SmudgeTypes.Free((SmudgeTypeClass *)pointer); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * * + * This will initialize the special heap for smudge type class objects, by pre-allocated * + * all known smudge types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init_Heap(void) +{ + /* + ** These smudge type class objects must be allocated in the exact order that they + ** are specified in the SmudgeType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new SmudgeTypeClass(Crater1); // SMUDGE_CRATER1 + new SmudgeTypeClass(Crater2); // SMUDGE_CRATER2 + new SmudgeTypeClass(Crater3); // SMUDGE_CRATER3 + new SmudgeTypeClass(Crater4); // SMUDGE_CRATER4 + new SmudgeTypeClass(Crater5); // SMUDGE_CRATER5 + new SmudgeTypeClass(Crater6); // SMUDGE_CRATER6 + new SmudgeTypeClass(Scorch1); // SMUDGE_SCORCH1 + new SmudgeTypeClass(Scorch2); // SMUDGE_SCORCH2 + new SmudgeTypeClass(Scorch3); // SMUDGE_SCORCH3 + new SmudgeTypeClass(Scorch4); // SMUDGE_SCORCH4 + new SmudgeTypeClass(Scorch5); // SMUDGE_SCORCH5 + new SmudgeTypeClass(Scorch6); // SMUDGE_SCORCH6 + new SmudgeTypeClass(Bibx1); // SMUDGE_BIB1 + new SmudgeTypeClass(Bibx2); // SMUDGE_BIB2 + new SmudgeTypeClass(Bibx3); // SMUDGE_BIB3 +} + + +/*********************************************************************************************** + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * * + * This converts an ASCII name into a smudge type number. This is typically necessary * + * when processing scenario INI files and not used otherwise. * + * * + * INPUT: name -- Pointer to the name to convert. * + * * + * OUTPUT: Returns with the SmudgeType number that matches the name supplied. If no match * + * was found, then SMUDGE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeType SmudgeTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(SMUDGE_NONE); +} + + +/*********************************************************************************************** + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * * + * Smudges are always only one icon in dimension, so this routine always returns a cell * + * occupation offset list of the center cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns occupation list specifying all the cells that the overlay occupies. This * + * is just the center cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +short const * SmudgeTypeClass::Occupy_List(bool) const +{ + static short _occupy[4*4]; + short * ptr = &_occupy[0]; + + for (int x = 0; x < Width; x++) { + for (int y = 0; y < Height; y++) { + *ptr++ = x + (y*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + return(_occupy); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * * + * Smudge object imagery varies between theaters. This routine will load the appropriate * + * imagery for the theater specified. * + * * + * INPUT: theater -- The theater to prepare for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + SmudgeTypeClass const & smudge = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed smudge data set name. + + _makepath(fullname, NULL, NULL, smudge.IniName, Theaters[theater].Suffix); + ((void const *&)smudge.ImageData) = MFCD::Retrieve(fullname); + } + } +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * * + * The scenario object editor will call this routine to display a typical imagery of this * + * smudge object for graphical identification purposes. * + * * + * INPUT: x,y -- Coordinate to render the smudge at. * + * * + * window-- The window to base the coordinate rendering upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const * ptr = Get_Image_Data(); + + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IsTheaterShape = true; // Smudges are theater specific + if (ptr != NULL) { + for (int w = 0; w < Width; w++) { + for (int h = 0; h < Height; h++) { + CC_Draw_Shape(ptr, w + (h*Width), x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, WINDOW_TACTICAL, SHAPE_WIN_REL); + } + } + } + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * * + * This routine adds smudge objects to the list of objects that the scenario editor can * + * place upon the ground. It is only called from the scenario editor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Prep_For_Add(void) +{ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * * + * This routine will, in one motion, create a smudge object and place it upon the map. * + * Since placing a smudge on the map will destroy the object, this routine will leave the * + * smudge object count unchanged. Typically, this routine is used by the scenario editor * + * for creating smudges and placing them on the map. * + * * + * INPUT: cell -- The cell to place the smudge object. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new SmudgeClass(Type, Cell_Coord(cell))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * * + * This routine will create a smudge object of the appropriate type. Smudge objects are * + * transitory in nature. They exist only from the point of creation until they are given * + * a spot on the map to reside. At that time the map data is updated and the smudge * + * object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a created smudge object. If none could be created, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * SmudgeTypeClass::Create_One_Of(HouseClass *) const +{ + return(new SmudgeClass(Type, -1)); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * * + * This routine will draw the smudge overlay image at the coordinate (upper left) * + * specified. The underlying terrain icon is presumed to have already been rendered. * + * * + * INPUT: x,y -- Coordinate of the upper left corner of icon to render the smudge object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Draw_It(int x, int y, int data) const +{ + void const * ptr = Get_Image_Data(); + if (ptr != NULL) { + IsTheaterShape = true; // Smudges are theater specific + CC_Draw_Shape(ptr, data, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * * + * Use this routine to get a reference to the smudge type class object when given just * + * the smudge type identifier. * + * * + * INPUT: type -- The smudge type identifier to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the smudge type class object. * + * * + * WARNINGS: Be sure that the smudge type specified is legal. An illegal type value will * + * produce undefined results. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass & SmudgeTypeClass::As_Reference(SmudgeType type) +{ + return(*SmudgeTypes.Ptr(type)); +} diff --git a/REDALERT/SEARCH.H b/REDALERT/SEARCH.H new file mode 100644 index 000000000..083f9cc1f --- /dev/null +++ b/REDALERT/SEARCH.H @@ -0,0 +1,686 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SEARCH.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEARCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/02/96 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * IndexClass::Add_Index -- Add element to index tracking system. * + * IndexClass::Clear -- Clear index handler to empty state. * + * IndexClass::Count -- Fetch the number of index entries recorded. * + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * IndexClass::IndexClass -- Constructor for index handler. * + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * IndexClass::Is_Present -- Checks for presense of index entry. * + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * IndexClass::Sort_Nodes -- Sorts nodes in preparation for a binary search. * + * IndexClass::~IndexClass -- Destructor for index handler object. * + * compfunc -- Support function for bsearch and bsort. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SEARCH_H +#define SEARCH_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#ifndef __BORLANDC__ +#define _USERENTRY +#endif + +/* +** This class is used to create and maintain an index. It does this by assigning a unique +** identifier number to the type objects that it is indexing. The regular binary sort and search +** function are used for speedy index retreival. Typical use of this would be to index pointers to +** normal data objects, but it can be used to hold the data objects themselves. Keep in mind that +** the data object "T" is copied around by this routine in the internal tables so not only must +** it have a valid copy constructor, it must also be efficient. The internal algorithm will +** create an arbitrary number of default constructed objects of type "T". Make sure it has a +** default constructor that is effecient. The constructor need not perform any actual +** initialization since this class has prior knowledge about the legality of these temporary +** objects and doesn't use them until after the copy constructor is used to initialize them. +*/ + +template +class IndexClass +{ + public: + IndexClass(void); + ~IndexClass(void); + + /* + ** Add element to index table. + */ + bool Add_Index(int id, T data); + + /* + ** Removes an index entry from the index table. + */ + bool Remove_Index(int id); + + /* + ** Check to see if index is present. + */ + bool Is_Present(int id) const; + + /* + ** Fetch number of indexes in the table. + */ + int Count(void) const; + + /* + ** Actually a fetch an index data element from the table. + */ + T Fetch_Index(int id) const; + + /* + ** Clear out the index table to null (empty) state. + */ + void Clear(void); + + private: + /* + ** This node object is used to keep track of the connection between the data + ** object and the index identifier number. + */ + struct NodeElement { + int ID; // ID number (must be first element in this structure). + T Data; // Data element assigned to this ID number. + }; + + /* + ** This is the pointer to the allocated index table. It contains all valid nodes in + ** a sorted order. + */ + NodeElement * IndexTable; + + /* + ** This records the number of valid nodes within the index table. + */ + int IndexCount; + + /* + ** The total size (in nodes) of the index table is recorded here. If adding a node + ** would cause the index count to exceed this value, the index table must be resized + ** to make room. + */ + int IndexSize; + + /* + ** If the index table is sorted and ready for searching, this flag will be true. Sorting + ** of the table only occurs when absolutely necessary. + */ + bool IsSorted; + + /* + ** This records a pointer to the last element found by the Is_Present() function. Using + ** this last recorded value can allow quick fetches of data whenever possible. + */ + NodeElement const * Archive; + + //------------------------------------------------------------------------------------- + IndexClass(IndexClass const & rvalue); + IndexClass * operator = (IndexClass const & rvalue); + + /* + ** Increase size of internal index table by amount specified. + */ + bool Increase_Table_Size(int amount); + + /* + ** Check if archive pointer is the same as that requested. + */ + bool Is_Archive_Same(int id) const; + + /* + ** Invalidate the archive pointer. + */ + void Invalidate_Archive(void); + + /* + ** Set archive to specified value. + */ + void Set_Archive(NodeElement const * node); + + /* + ** Search for the node in the index table. + */ + NodeElement const * Search_For_Node(int id) const; + + static int _USERENTRY search_compfunc(void const * ptr, void const * ptr2); +}; + + +/*********************************************************************************************** + * IndexClass::IndexClass -- Constructor for index handler. * + * * + * This constructs an empty index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::IndexClass(void) : + IndexTable(0), + IndexCount(0), + IndexSize(0), + IsSorted(false), + Archive(0) +{ + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::~IndexClass -- Destructor for index handler object. * + * * + * This will destruct and free any resources managed by this index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::~IndexClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * IndexClass::Clear -- Clear index handler to empty state. * + * * + * This routine will free all internal resources and bring the index handler into a * + * known empty state. After this routine, the index handler is free to be reused. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Clear(void) +{ + delete [] IndexTable; + IndexTable = 0; + IndexCount = 0; + IndexSize = 0; + IsSorted = false; + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * * + * This helper function will increase the capacity of the internal index table without * + * performing any alterations to the index data. Use this routine prior to adding a new * + * element if the existing capacity is insufficient. * + * * + * INPUT: amount -- The number of index element spaces to add to its capacity. * + * * + * OUTPUT: bool; Was the internal capacity increased without error? * + * * + * WARNINGS: If there is insufficient RAM, then this routine will fail. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Increase_Table_Size(int amount) +{ + /* + ** Check size increase parameter for legality. + */ + if (amount < 0) return(false); + + NodeElement * table = new NodeElement[IndexSize + amount]; + if (table != NULL) { + + /* + ** Copy all valid nodes into the new table. + */ + for (int index = 0; index < IndexCount; index++) { + table[index] = IndexTable[index]; + } + + /* + ** Make the new table the current one (and delete the old one). + */ + delete [] IndexTable; + IndexTable = table; + IndexSize += amount; + Invalidate_Archive(); + + /* + ** Return with success flag. + */ + return(true); + } + + /* + ** Failure to allocate the memory results in a failure to increase + ** the size of the index table. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Count -- Fetch the number of index entries recorded. * + * * + * This will return the quantity of index entries that have been recored by this index * + * handler. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with number of recored indecies present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +int IndexClass::Count(void) const +{ + return(IndexCount); +} + + +/*********************************************************************************************** + * IndexClass::Is_Present -- Checks for presense of index entry. * + * * + * This routine will scan for the specified index entry. If it was found, then 'true' is * + * returned. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: bool; Was the index entry found in this index handler object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Present(int id) const +{ + /* + ** If there are no data elements in the index table, then it can + ** never find the specified index. Check for and return failure + ** in this case. + */ + if (IndexCount == 0) { + return(false); + } + + /* + ** Check to see if this same index element was previously searched for. If + ** so and it was previously found, then there is no need to search for it + ** again -- just return true. + */ + if (Is_Archive_Same(id)) { + return(true); + } + + /* + ** Perform a binary search on the index nodes in order to look for a + ** matching index value. + */ + NodeElement const * nodeptr = Search_For_Node(id); + + /* + ** If a matching index was found, then record it for future reference and return success. + */ + if (nodeptr != 0) { + ((IndexClass *)this)->Set_Archive(nodeptr); + return(true); + } + + /* + ** Could not find element so return failure condition. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * * + * This routine will find the specified index and return the data value associated with it. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with the data value associated with the index value. * + * * + * WARNINGS: This routine presumes that the index exists. If it doesn't exist, then the * + * default constructed object "T" is returned instead. To avoid this problem, * + * always verfiy the existance of the index by calling Is_Present() first. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +T IndexClass::Fetch_Index(int id) const +{ + if (Is_Present(id)) { + + /* + ** Count on the fact that the archive pointer is always valid after a call to Is_Present + ** that returns "true". + */ + return(Archive->Data); + } + return(T()); +} + + +/*********************************************************************************************** + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * * + * This routine compares the specified index ID with the previously recorded index archive * + * pointer in order to determine if they match. * + * * + * INPUT: id -- The index ID to compare to the archive index object pointer. * + * * + * OUTPUT: bool; Does the specified index match the archive pointer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Archive_Same(int id) const +{ + if (Archive != 0 && Archive->ID == id) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * * + * This routine will set the archive pointer to an invalid state. This must be performed * + * if ever the archive pointer would become illegal (such as when the element it refers to * + * is deleted). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Invalidate_Archive(void) +{ + Archive = 0; +} + + +/*********************************************************************************************** + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * * + * This routine records the specified node pointer. Use this routine when there is a * + * good chance that the specified node will be requested in the immediate future. * + * * + * INPUT: node -- Pointer to the node to assign to the archive. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Set_Archive(NodeElement const * node) +{ + Archive = node; +} + + +/*********************************************************************************************** + * IndexClass::Add_Index -- Add element to index tracking system. * + * * + * This routine will record the index information into this index manager object. It * + * associates the index number with the data specified. The data is copied to an internal * + * storage location. * + * * + * INPUT: id -- The ID number to associate with the data. * + * * + * data -- The data to store. * + * * + * OUTPUT: bool; Was the element added without error? Failure indicates that RAM has been * + * exhausted. * + * * + * WARNINGS: The data is COPIED to internal storage. This means that the data object must * + * have a functional and efficient copy constructor and assignment operator. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Add_Index(int id, T data) +{ + /* + ** Ensure that there is enough room to add this index. If not, then increase the + ** capacity of the internal index table. + */ + if (IndexCount + 1 > IndexSize) { + if (!Increase_Table_Size(IndexSize == 0 ? 10 : IndexSize)) { + + /* + ** Failure to increase the size of the index table means failure to add + ** the index element. + */ + return(false); + } + } + + /* + ** Add the data to the end of the index data and then sort the index table. + */ + IndexTable[IndexCount].ID = id; + IndexTable[IndexCount].Data = data; + IndexCount++; + IsSorted = false; + + return(true); +} + + +/*********************************************************************************************** + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * * + * This will scan through the previously added index elements and if a match was found, it * + * will be removed. * + * * + * INPUT: id -- The index ID to search for and remove. * + * * + * OUTPUT: bool; Was the index element found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Remove_Index(int id) +{ + /* + ** Find the array index into the table that matches the specified id value. + */ + int found_index = -1; + for (int index = 0; index < IndexCount; index++) { + if (IndexTable[index].ID == id) { + found_index = index; + break; + } + } + + /* + ** If the array index was found, then copy all higher index entries + ** downward to fill the vacated location. We cannot use memcpy here because the type + ** object may not support raw copies. C++ defines the assignment operator to deal + ** with this, so that is what we use. + */ + if (found_index != -1) { + + for (int index = found_index+1; index < IndexCount; index++) { + IndexTable[index-1] = IndexTable[index]; + } + IndexCount--; + + NodeElement fake; + fake.ID = 0; + fake.Data = T(); + IndexTable[IndexCount] = fake; // zap last (now unused) element + + Invalidate_Archive(); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * compfunc -- Support function for bsearch and bsort. * + * * + * This compare function presumes that its parameters are pointing to NodeElements and that * + * the first "int" in the node is the index ID number to be used for comparison. * + * * + * INPUT: ptr1 -- Pointer to first node. * + * * + * ptr2 -- Pointer to second node. * + * * + * OUTPUT: Returns with the comparision value between the two nodes. * + * * + * WARNINGS: This is highly dependant upon the layout of the NodeElement structure. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +int _USERENTRY IndexClass::search_compfunc(void const * ptr1, void const * ptr2) +{ + if (*(int const *)ptr1 == *(int const *)ptr2) { + return(0); + } + if (*(int const *)ptr1 < *(int const *)ptr2) { + return(-1); + } + return(1); +} + + +/*********************************************************************************************** + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * * + * This routine will perform a binary search on the previously recorded index values and * + * if a match was found, it will return a pointer to the NodeElement. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with a pointer to the NodeElement that matches the index ID specified. If * + * no matching index could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +typename IndexClass::NodeElement const * IndexClass::Search_For_Node(int id) const +{ + /* + ** If there are no elements in the list, then it certainly can't find any matches. + */ + if (IndexCount == 0) { + return(0); + } + + /* + ** If the list has not yet been sorted, then do so now. Binary searching requires + ** the list to be sorted. + */ + if (!IsSorted) { + qsort(&IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc); + ((IndexClass *)this)->Invalidate_Archive(); + ((IndexClass *)this)->IsSorted = true; + } + + /* + ** This list is sorted and ready to perform a binary search upon it. + */ + NodeElement node; + node.ID = id; + return((NodeElement const *)bsearch(&node, &IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc)); +} + + +#endif + + diff --git a/REDALERT/SEDITDLG.CPP b/REDALERT/SEDITDLG.CPP new file mode 100644 index 000000000..6b90c0b06 --- /dev/null +++ b/REDALERT/SEDITDLG.CPP @@ -0,0 +1,383 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.cpp - "SimpleEditDlgClass": An ok/cancel type dialog with 1 or 2 edit boxes. +// Mostly a hack for what I need right now - not necessarily very flexible. +// Still - I can't believe there isn't a set of dialog classes in here already. +// ajw 07/21/98 + +#include "SEditDlg.h" +#include "WOLEdit.h" + +extern bool cancel_current_msgbox; +bool disable_current_msgbox = false; + +//*********************************************************************************************** +SimpleEditDlgClass::SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 /* = NULL */, int iEditCharsAccept2 /* = 0 */ ) + : iDialogWidth( iDialogWidth ), iEditCharsAccept( iEditCharsAccept ), iEditCharsAccept2( iEditCharsAccept2 ) +{ + // Copy strings. + if( szTitle ) + { + this->szTitle = new char[ strlen( szTitle ) + 1 ]; + strcpy( this->szTitle, szTitle ); + } + else + this->szTitle = NULL; + + if( szPrompt ) + { + this->szPrompt = new char[ strlen( szPrompt ) + 1 ]; + strcpy( this->szPrompt, szPrompt ); + } + else + this->szPrompt = NULL; // I wouldn't try this ... not totally implemented. + + if( szPrompt2 ) + { + this->szPrompt2 = new char[ strlen( szPrompt2 ) + 1 ]; + strcpy( this->szPrompt2, szPrompt2 ); + } + else + this->szPrompt2 = NULL; // This is the flag for whether or not there is a second edit box. + + *szEdit = 0; + *szEdit2 = 0; + + szOkButton = Text_String( TXT_OK ); + szCancelButton = Text_String( TXT_CANCEL ); + szMiddleButton = NULL; +} + +//*********************************************************************************************** +SimpleEditDlgClass::~SimpleEditDlgClass() +{ + delete [] szTitle; + delete [] szPrompt; + delete [] szPrompt2; +} + +//*********************************************************************************************** +void SimpleEditDlgClass::SetButtons( const char* szOk, const char* szCancel, const char* szMiddle /*= NULL*/ ) +{ + szOkButton = szOk; + szCancelButton = szCancel; + szMiddleButton = szMiddle; +} + +//*********************************************************************************************** +const char* SimpleEditDlgClass::Show() +{ + // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int x_margin = 18 * RESFACTOR; // margin width/height + int y_margin = 10 * RESFACTOR; // margin width/height + int d_gap_y = 5 * RESFACTOR; + + int d_dialog_w = iDialogWidth; + int d_dialog_h = szPrompt2 ? ( 29 * RESFACTOR ) + 2 * d_gap_y + 2 * y_margin : ( 19 * RESFACTOR ) + d_gap_y + 2 * y_margin; + if( szTitle ) + d_dialog_h += 10 * RESFACTOR + 2 * d_gap_y; + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + +/* + if( szTitle ) + { + d_title_w = String_Pixel_Width( szTitle ); + d_title_h = 10 * RESFACTOR; + d_title_x = d_dialog_cx - d_title_w / 2; + d_title_y = d_dialog_y + d_gap_y; + } +*/ + + int d_prompt_w = String_Pixel_Width( szPrompt ); + int d_prompt_h = 10 * RESFACTOR; + int d_prompt_x = d_dialog_x + x_margin; + int d_prompt_y = szTitle ? ( d_dialog_y + 3 * d_gap_y + 10 * RESFACTOR ) : ( d_dialog_y + d_gap_y ); + + int d_edit_w = d_dialog_w - d_prompt_w - 2 * x_margin; + int d_edit_h = 10 * RESFACTOR; + int d_edit_x = d_dialog_x + d_prompt_w + x_margin; + int d_edit_y = d_prompt_y; + + int d_prompt2_w = szPrompt2 ? String_Pixel_Width( szPrompt2 ) : 0; + int d_prompt2_h = 10 * RESFACTOR; + int d_prompt2_x = d_dialog_x + x_margin; + int d_prompt2_y = d_prompt_y + d_prompt2_h + d_gap_y; + + int d_edit2_w = d_dialog_w - d_prompt2_w - 2 * x_margin; + int d_edit2_h = 10 * RESFACTOR; + int d_edit2_x = d_dialog_x + d_prompt2_w + x_margin; + int d_edit2_y = d_prompt2_y; + + int d_ok_w, d_ok_h, d_ok_x, d_ok_y, d_cancel_w, d_cancel_h, d_cancel_x, d_cancel_y, d_mid_x, d_mid_y, d_mid_w, d_mid_h; + + if( !szMiddleButton ) + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 10 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 10 * RESFACTOR; + d_cancel_y = d_ok_y; + } + else + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 30 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_mid_w = 40 * RESFACTOR; + d_mid_h = 9 * RESFACTOR; + d_mid_x = d_dialog_cx - ( d_mid_w / 2 ); + d_mid_y = d_ok_y; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 30 * RESFACTOR; + d_cancel_y = d_ok_y; + } + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_MIDDLE, + BUTTON_EDIT, + BUTTON_EDIT2 + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + const char* szReturn = NULL; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, szOkButton, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, szCancelButton, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + TextButtonClass MiddleBtn( BUTTON_MIDDLE, szMiddleButton, TPF_BUTTON, d_mid_x, d_mid_y, d_mid_w ); + + WOLEditClass EditBox( BUTTON_EDIT, szEdit, min( sizeof(szEdit), iEditCharsAccept ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC ); + WOLEditClass EditBox2( BUTTON_EDIT2, szEdit2, min( sizeof(szEdit2), iEditCharsAccept2 ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit2_x, d_edit2_y, d_edit2_w, -1, EditClass::ALPHANUMERIC ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + if( szMiddleButton ) + MiddleBtn.Add_Tail(*commands); + EditBox.Add_Tail(*commands); + if( szPrompt2 ) + EditBox2.Add_Tail(*commands); + EditBox.Set_Focus(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + if( szTitle ) + Draw_Caption( szTitle, d_dialog_x, d_dialog_y, d_dialog_w ); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( szPrompt, d_prompt_x, d_prompt_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + if( szPrompt2 ) + Fancy_Text_Print( szPrompt2, d_prompt2_x, d_prompt2_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + EditBox.Set_Focus(); + EditBox.Flag_To_Redraw(); + } + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + } + + if( disable_current_msgbox ) + { + disable_current_msgbox = false; + EditBox.Disable(); + // These do not actually draw. I am actually clearing the "draw" flag! + // Problem is Disable sets them to redraw, and I don't want to, and there is no Flag_To_Redraw( false ). + EditBox.GadgetClass::Draw_Me( true ); + if( szPrompt2 ) + { + EditBox2.Disable(); + EditBox2.GadgetClass::Draw_Me( true ); + } + OkBtn.Disable(); + OkBtn.GadgetClass::Draw_Me( true ); + CancelBtn.Disable(); + CancelBtn.GadgetClass::Draw_Me( true ); + if( szMiddleButton ) + { + MiddleBtn.Disable(); + MiddleBtn.GadgetClass::Draw_Me( true ); + } + } + + /* + ** Process input. + */ + switch( input ) + { +// case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + szReturn = szCancelButton; + process = false; + break; + +// case KN_RETURN: + case ( BUTTON_EDIT | KN_BUTTON ): // (Return pressed while on edit.) + case ( BUTTON_OK | KN_BUTTON ): + szReturn = szOkButton; + process = false; + break; + + case ( BUTTON_MIDDLE | KN_BUTTON ): + szReturn = szMiddleButton; + process = false; + break; + + default: + break; + } + } + + return szReturn; +} + +#endif diff --git a/REDALERT/SEDITDLG.H b/REDALERT/SEDITDLG.H new file mode 100644 index 000000000..7db7d7c6b --- /dev/null +++ b/REDALERT/SEDITDLG.H @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.h - "SimpleEditDlgClass": An ok/cancel type dialog with a single edit box. +// ajw 07/21/98 + +#include "function.h" + +class SimpleEditDlgClass +{ +public: + SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 = NULL, int iEditCharsAccept2 = 0 ); + virtual ~SimpleEditDlgClass(); + + const char* Show(); // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + void SetButtons( const char* szOk, const char* szCancel, const char* szMiddle = NULL ); + + char szEdit[ 300 ]; // iEditCharsAccept upper limit. + char szEdit2[ 300 ]; + +protected: + int iDialogWidth; // X pixels width of entire dialog. + char* szTitle; // Title of dialog, or NULL for no title. + + char* szPrompt; // Text appearing to the left of edit box. + int iEditCharsAccept; // Max length of string allowed in edit, includes null-terminator. + + char* szPrompt2; + int iEditCharsAccept2; + + const char* szOkButton; // Text of button that acts like an Ok button. Appears on left. + const char* szCancelButton; // Text of button that acts like an Cancel button. Appears on right. + const char* szMiddleButton; // Optional middle button text. Null = no middle button. +}; + +#endif diff --git a/REDALERT/SENDFILE.CPP b/REDALERT/SENDFILE.CPP new file mode 100644 index 000000000..d5913f088 --- /dev/null +++ b/REDALERT/SENDFILE.CPP @@ -0,0 +1,877 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : SENDFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : Audust 20th, 1996 * + * * + * Last Update : August 20th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for scenario file transfer between machines * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0)//PG +#include "function.h" + +//#include "WolDebug.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else + +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +#define RESPONSE_TIMEOUT 60*60 + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +/*********************************************************************************************** + * Get_Scenario_File_From_Host -- Initiates download of scenario file from game host * + * * + * * + * * + * INPUT: ptr to buffer to copy file name into * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file sucessfully downloaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:06PM ST : Created * + *=============================================================================================*/ +bool Get_Scenario_File_From_Host(char *return_name, int gametype) +{ + //WWDebugString ("RA95 - In Get_Scenario_From_Host\n"); + + unsigned int file_length; + + SerialPacketType send_packet; + SerialPacketType receive_packet; + GlobalPacketType net_send_packet; + GlobalPacketType net_receive_packet; + unsigned int packet_len; + unsigned short product_id; + + IPXAddressClass sender_address; + + CDTimerClass response_timer; // timeout timer for waiting for responses + + /* + ** Send the scenario request using guaranteed delivery. + */ + if (!gametype) { + memset ((void*)&send_packet, 0, sizeof (send_packet)); + send_packet.Command = SERIAL_REQ_SCENARIO; + NullModem.Send_Message (&send_packet, sizeof(send_packet), 1); + } else { + memset ((void*)&net_send_packet, 0, sizeof (net_send_packet)); + net_send_packet.Command = NET_REQ_SCENARIO; + Ipx.Send_Global_Message (&net_send_packet, sizeof (net_send_packet), + 1, &(Session.HostAddress) ); + } + + + + //WWDebugString ("RA95 - Waiting for response from host\n"); + + /* + ** Wait for host to respond with a file info packet + */ + response_timer = RESPONSE_TIMEOUT; + if (!gametype){ + do { + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == SERIAL_FILE_INFO){ + strcpy (return_name, receive_packet.ScenarioInfo.ShortFileName); + file_length = receive_packet.ScenarioInfo.FileLength; + break; + } + } + } while ( response_timer ); + }else{ + do { + Ipx.Service(); + int receive_packet_length = sizeof (net_receive_packet); + if (Ipx.Get_Global_Message (&net_receive_packet, &receive_packet_length, + &sender_address, &product_id)){ + +//WWDebugString ("RA95 - Got packet from host\n"); +#ifdef WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && sender_address == Session.HostAddress) { +#else //WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + strcpy (return_name, net_receive_packet.ScenarioInfo.ShortFileName); + file_length = net_receive_packet.ScenarioInfo.FileLength; +//WWDebugString ("RA95 - Got file info packet from host\n"); + break; + } + } + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + } while ( response_timer ); + } + +//char rt[80]; +//sprintf (rt, "RA95 - response_timer = %d\n", response_timer ); +//WWDebugString (rt); + + /* + ** If we timed out then something horrible has happened to the other player so just + ** return failure. + */ + if (!response_timer) return (false); + +// debugprint( "about to download '%s'\n", return_name ); + + /* + ** Receive the file from the host + */ + return (Receive_Remote_File ( return_name, file_length, gametype)); +} + + + + + + + +/*********************************************************************************************** + * Receive_Remote_File -- Handles incoming file download packets from the game host * + * * + * * + * * + * INPUT: file name to save as * + * length of file to expect * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file downloaded was completed * + * * + * WARNINGS: This fuction can modify the file name passed in * + * * + * HISTORY: * + * 8/22/96 3:07PM ST : Created * + *=============================================================================================*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype) +{ + + //WWDebugString ("RA95 - In Receive_Remote_File\n"); + unsigned short product_id; + IPXAddressClass sender_address; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200 *RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_RECEIVING_SCENARIO); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + + RemoteFileTransferType receive_packet; + + int last_received_block = -1; //No blocks received yet + unsigned int total_length = 0; + unsigned int packet_len; + + /* + ** If the file name is already in use, use the temp file name + */ + CCFileClass test_file (file_name); + if (test_file.Is_Available()){ + strcpy (file_name, "DOWNLOAD.TMP"); + } + + RawFileClass save_file (file_name); + + /* + ** If the file already exists then delete it and re-create it. + */ + if (save_file.Is_Available()) save_file.Delete(); + + /* + ** Open the file for write + */ + save_file.Open ( WRITE ); + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + /* + ** Wait for all the blocks to arrive + */ + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == NET_FILE_CHUNK){ + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + }else{ + Ipx.Service(); + + int receive_packet_len = sizeof (receive_packet); + if (Ipx.Get_Global_Message (&receive_packet, &receive_packet_len, + &sender_address, &product_id)){ + +#ifdef WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && sender_address == Session.HostAddress){ +#else //WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + + } while ( process ); + + save_file.Close(); + + /* + ** Update the internal list of scenarios to include the downloaded one so we know about it + ** for the next game. + */ + Session.Read_Scenario_Descriptions(); + + return (return_code); +} + + + + + + +/*********************************************************************************************** + * Send_Remote_File -- Sends a file to game clients * + * * + * * + * * + * INPUT: File name * + * * + * OUTPUT: true if file transfer was successfully completed * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:09PM ST : Created * + *=============================================================================================*/ +bool Send_Remote_File ( char *file_name, int gametype ) +{ + //WWDebugString ("RA95 - In Send_Remote_File\n"); + + /* + ** Dialog & button dimensions + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 240 *factor; // dialog width + int d_dialog_h = 90*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int d_progress_w = 100*factor; + int d_progress_h = 10*factor; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*factor; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_SENDING_SCENARIO); + + CDTimerClass response_timer; // timeout timer for waiting for responses + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + int file_length; + int block_number; + int max_chunk_size; + int total_blocks; + int bytes_left; + + void *read_ptr; + + RemoteFileTransferType send_packet; + SerialPacketType file_info; + GlobalPacketType net_file_info; + + + CCFileClass send_file (file_name); + + if ( !send_file.Is_Available() ){ + //WWDebugString ("RA95 - Error - could not find file to send to client\n"); +// debugprint("RA95 - Error - could not find file to send to client\n"); + return (false); + } + file_length = send_file.Size(); + + response_timer = RESPONSE_TIMEOUT; + + /* + ** Send the file info to the remote machine(s) + */ + if (!gametype){ + file_info.Command = SERIAL_FILE_INFO; + strcpy (&file_info.ScenarioInfo.ShortFileName[0], file_name); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#endif +#endif + file_info.ScenarioInfo.FileLength = file_length; + NullModem.Send_Message (&file_info, sizeof (file_info), 1); + while (NullModem.Num_Send() > 0 && response_timer){ + NullModem.Service(); + } + } else { + net_file_info.Command = NET_FILE_INFO; + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], file_name); +// debugprint( "Uploading '%s'\n", file_name ); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + // There was a bug here: s/b net_file_info. This means that players that don't have Aftermath could have been + // accumulating Aftermath maps all this time!!! (File wasn't getting renamed to "DOWNLOAD.TMP".) + } +#endif +#endif +// debugprint( "ShortFileName is '%s'\n", net_file_info.ScenarioInfo.ShortFileName ); + net_file_info.ScenarioInfo.FileLength = file_length; + + for (int i=0 ; iAddress) ); + } + + while (Ipx.Global_Num_Send() > 0 && response_timer) { + Ipx.Service(); + } + } + + + max_chunk_size = MAX_SEND_FILE_PACKET_SIZE; + total_blocks = (file_length + max_chunk_size-1) / max_chunk_size; + bytes_left = file_length; + + send_file.Open ( READ ); + + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + + block_number = 0; + + while ( process ){ + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + + if (block_number < total_blocks){ + + if ( NullModem.Num_Send() <2 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + NullModem.Send_Message ((void*)&send_packet, sizeof(send_packet), 1); + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (NullModem.Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + + } + } + + + }else{ + Ipx.Service(); + + if (block_number < total_blocks){ + + if ( Ipx.Global_Num_Send() == 0 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + for (int i=0 ; iAddress) ); + } + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (Ipx.Global_Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + + + + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + } + + return (return_code); +} +#endif \ No newline at end of file diff --git a/REDALERT/SEQCONN.CPP b/REDALERT/SEQCONN.CPP new file mode 100644 index 000000000..496d6b15e --- /dev/null +++ b/REDALERT/SEQCONN.CPP @@ -0,0 +1,560 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.10 01 Mar 1996 18:29:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * SequencedConnClass::SequencedConnClass -- class constructor * + * SequencedConnClass::~SequencedConnClass -- class destructor * + * SequencedConnClass::Init -- Initializes connection queue to empty * + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * SequencedConnClass::Receive_Packet -- adds packet to receive queue * + * SequencedConnClass::Get_Packet -- gets a packet from receive queue * + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * SequencedConnClass::Service_Receive_Queue -- services recieve queue * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "function.h" + +#include "WolDebug.h" + +/*************************************************************************** + * SequencedConnClass::SequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::SequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); + +} /* end of SequencedConnClass */ + + +/*************************************************************************** + * SequencedConnClass::~SequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::~SequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * SequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void SequencedConnClass::Init (void) +{ + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { + NumSendAck++; + } else { + NumSendNoAck++; + } + return(true); + + } else { + return(false); + } +} + + +/*************************************************************************** + * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *entry_data; // ptr to queue entry data + int save_packet = 1; // 0 = this is a resend, or + // out-of-order packet + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber!=MagicNum) + return(false); + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Code) { + /*..................................................................... + DATA: If this is a No-Ack packet, always save it. Otherwise, only + save it if it's received in the proper sequence. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + if (packet->Code == PACKET_DATA_NOACK) { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); +#endif + save_packet = 1; + } else { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); +#endif + if ((packet->PacketID == NumRecAck)) { + save_packet = 1; + } else { + save_packet = 0; + /*............................................................... + If this is a resend of our next-available received message, it + means the other app didn't get our ACK, so mark it as + non-acknowledged to tell Service_Receive_Queue to send an ACK. + ...............................................................*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + if (entry_data->PacketID==packet->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + rec_entry->IsACK = 0; +#ifdef DEBUG_SEQ +printf("(Resend)\n"); +#endif + } + } + } + } + + /* + ...................... queue this packet ........................ + */ + if (save_packet) { + if (!Queue->Queue_Receive(buf, buflen)) { + return(false); + } + if (packet->Code == PACKET_DATA_ACK) { + NumRecAck++; + } else { + NumRecNoAck++; + } + } + break; + + /*..................................................................... + ACK: If this ACK is for the latest-sent packet, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: +#ifdef DEBUG_SEQ +printf("ACK received (%d)\n",packet->PacketID); +#endif + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Next_Send(); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry!=NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Next_Receive(); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + + return(false); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Send_Queue (void) +{ + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + + /*------------------------------------------------------------------------ + - If the next packet is ACK'd remove it from the queue + - If the next packet isn't ACK'd, [re-]send it + ------------------------------------------------------------------------*/ + /* + ......................... Get ptr to data to send ........................ + */ + send_entry = Queue->Next_Send(); + if (send_entry==NULL) + return(true); + + /* + ------------------ If ACK has been received, unqueue it ------------------ + */ + if (send_entry->IsACK) { + + /* + .................. Update this queue's response time .................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /* + ......................... unqueue the packet .......................... + */ +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Send(NULL,NULL); + } else { + + /* + ----------------- ACK not received yet, [re-]send packet ----------------- + */ + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ + Send (send_entry->Buffer, send_entry->BufLen); + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) + send_entry->IsACK = 1; + } + +#ifdef DEBUG_SEQ +packet_hdr = (CommHeaderType *)send_entry->Buffer; +if (packet_hdr->Code == PACKET_DATA_NOACK) { + printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); +} else { + printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); +} +#endif + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) + return(false); + if (Timeout != -1 && send_entry->LastTime - + send_entry->FirstTime > Timeout) + return(false); + + } + } + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Receive_Queue (void) +{ + CommHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry==NULL) + return(true); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { +#ifdef DEBUG_SEQ +printf("Sending ACK (%d)\n",packet_hdr->PacketID); +#endif + ackpacket.MagicNumber = MagicNum; + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet_hdr->PacketID; + + Send((char *)&ackpacket, sizeof(CommHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) { +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Receive(NULL,NULL); + } + + return(true); +} + +#endif \ No newline at end of file diff --git a/REDALERT/SEQCONN.H b/REDALERT/SEQCONN.H new file mode 100644 index 000000000..6280358c5 --- /dev/null +++ b/REDALERT/SEQCONN.H @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.h_v 1.13 01 Mar 1996 17:45:24 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Sequenced" ACK/Retry approach to packet * + * transmission. It waits until the last packet has been ACK'd before * + * sending another packet. Thus, it guarantees order of delivery of * + * packets, but its performance will be slower than the Non-Sequenced * + * approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SEQCONN_H +#define SEQCONN_H + +#include "connect.h" +#include "comqueue.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class SequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + SequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~SequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet queue. + .....................................................................*/ + CommQueueClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + +}; + +#endif +/*************************** end of seqconn.h ******************************/ diff --git a/REDALERT/SESSION.CPP b/REDALERT/SESSION.CPP new file mode 100644 index 000000000..909eac9f4 --- /dev/null +++ b/REDALERT/SESSION.CPP @@ -0,0 +1,1875 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SESSION.CPP 3 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SessionClass::SessionClass -- Constructor * + * SessionClass::~SessionClass -- Destructor * + * SessionClass::One_Time -- one-time initializations * + * SessionClass::Init -- Initializes all values * + * SessionClass::Create_Connections -- forms connections to other players * + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * SessionClass::Save -- Saves this class to a file * + * SessionClass::Load -- Loads this class from a file * + * SessionClass::Read_MultiPlayer_Settings -- reads settings from INI * + * SessionClass::Write_MultiPlayer_Settings -- writes settings to INI * + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * SessionClass::Trap_Object -- searches for an object, for debugging * + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for station ID computation +#include // for station ID computation + +//#include "WolDebug.h" + +/***************************** Globals *************************************/ +//--------------------------------------------------------------------------- +// This is the array of remap colors. Each player in a network game is +// assigned one of these colors. The 'G' is for graphics drawing; the 'T' +// is for text printing (indicates a remap table for the font to use). +//--------------------------------------------------------------------------- +//int SessionClass::GColors[MAX_MPLAYER_COLORS] = { + //5, // Yellow + //127, // Red + //135, // BlueGreen + //26, // Orange + //4, // Green + //202 // Blue-Grey +//}; + +//int SessionClass::TColors[MAX_MPLAYER_COLORS] = { + //CC_GDI_COLOR, // Yellow + //CC_NOD_COLOR, // Red + //CC_BLUE_GREEN, // BlueGreen + //CC_ORANGE, // Orange + //CC_GREEN, // Green + //CC_BLUE_GREY, // Blue +//}; + +/*--------------------------------------------------------------------------- +Min & Max unit count values; index0 = bases OFF, index1 = bases ON +---------------------------------------------------------------------------*/ +int SessionClass::CountMin[2] = {1,0}; +int SessionClass::CountMax[2] = {50,12}; + +//--------------------------------------------------------------------------- +// This is a list of all the names of the multiplayer scenarios +//--------------------------------------------------------------------------- +char SessionClass::Descriptions[100][40]; + +//--------------------------------------------------------------------------- +// These values are used purely for the Mono debug display. They show the +// names of the Global Channel packet types, and the event types. +//--------------------------------------------------------------------------- +char * SessionClass::GlobalPacketNames[] = { + "Game?", + "Game!", + "Player?", + "Player!", + "Join?", + "Join!", + "Reject", + "GameOptions", + "Sign Off", + "GO!", + "Message", + "Ping", + "Load" +}; + +char * SessionClass::SerialPacketNames[] = { + "CONNECT", + "GAME_OPTIONS", + "SIGN_OFF", + "GO", + "MESSAGE", + "TIMING", + "SCORE_SCREEN", + "LOADGAME", + "LAST_COMMAND", +}; + + +char * SessionClass::DialMethodCheck[ DIAL_METHODS ] = { + "T", + "P" +}; + +char *SessionClass::CallWaitStrings[ CALL_WAIT_STRINGS_NUM ] = { + "*70,", + "70#,", + "1170,", + "CUSTOM - " +}; + +/*************************************************************************** + * SessionClass::SessionClass -- Constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::SessionClass(void) +{ + Type = GAME_NORMAL; + CommProtocol = DEFAULT_COMM_PROTOCOL; + + Options.ScenarioIndex = 0; + Options.Bases = 0; + Options.Credits = 0; + Options.Tiberium = 0; + Options.Goodies = 0; + Options.Ghosts = 0; + Options.UnitCount = 0; + + UniqueID = 0; + + Handle[0] = 0; + PrefColor = PCOLOR_FIRST; + ColorIdx = PCOLOR_FIRST; + House = HOUSE_GOOD; + ObiWan = 0; + Solo = 0; + + MaxPlayers = 8; + NumPlayers = 0; + + MaxAhead = 5; + FrameSendRate = DEFAULT_FRAME_SEND_RATE; + + LoadGame = 0; + EmergencySave = 0; + + LastMessage[0] = 0; + WWChat = 0; + + RecordFile.Set_Name("RECORD.BIN"); // always uses this name + Record= 0; // set via command line + Play = 0; // set via command line + Attract = 0; // set via command line + + IsBridge = 0; + NetStealth = 0; + NetProtect = 1; + NetOpen = 0; + GameName[0] = 0; + GProductID = 0; + + MetaSize = MAX_IPX_PACKET_SIZE; + + ModemService = true; + CurPhoneIdx = 0; // set from INI file + SerialDefaults.Port = 0x2f8; // set from INI file + SerialDefaults.IRQ = 3; // set from INI file + SerialDefaults.Baud = 9600; // set from INI file + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; // set from INI file + SerialDefaults.InitStringIndex = 0; // set from INI file + SerialDefaults.CallWaitStringIndex = 0; // set from INI file + strcpy(SerialDefaults.CallWaitString,""); + ModemType = MODEM_NULL_HOST; // set from INI file + + TrapFrame = 0x7fffffff; // frame to start trapping object values at + TrapObjType = RTTI_NONE; // type of object to trap + TrapObject.Ptr.All = NULL; // ptr to object being trapped + TrapCoord = 0; // COORDINATE of object to trap + TrapTarget = TARGET_NONE; // TARGET value of object to trap + TrapCell = NULL; // for trapping a cell + TrapCheckHeap = 0; // start checking the Heap + TrapPrintCRC = 0; // output CRC file + +#if(TEN) + TenPacket = NULL; + TenSize = 200; + TenPlayerID = -1; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // TEN + +#if(MPATH) + MPathPacket = NULL; + MPathSize = 200; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // MPATH + +} // end of SessionClass + + +/*************************************************************************** + * SessionClass::~SessionClass -- Destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::~SessionClass(void) +{ +} // end of ~SessionClass + + +/*************************************************************************** + * SessionClass::One_Time -- one-time initializations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::One_Time(void) +{ + Read_MultiPlayer_Settings(); + Read_Scenario_Descriptions(); + + UniqueID = Compute_Unique_ID(); + +} // end of One_Time + + +/*************************************************************************** + * SessionClass::Init -- Initializes all values * + * * + * This function should be called for every new game played; it only sets * + * those variables that should be set for a new game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Init(void) +{ + +} // end of Init + + +/*************************************************************************** + * SessionClass::Create_Connections -- forms connections to other players * + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_Connections(void) +{ +#if (0)//PG + int i; + + if (Session.Type != GAME_IPX && Session.Type != GAME_INTERNET) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ipx.Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + &(Players[i]->Address) ); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } +#endif + return (1); + +} // end of Create_Connections + + +#if(TEN) +/*************************************************************************** + * SessionClass::Create_TEN_Connections -- forms connections to TEN players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_TEN_Connections(void) +{ + int i; + + if (Session.Type != GAME_TEN) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ten->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->TenAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_TEN_Connections + +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * SessionClass::Create_MPATH_Connections -- forms connections to MPATH players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_MPATH_Connections(void) +{ + int i; + + if (Session.Type != GAME_MPATH) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + MPath->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->MPathAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_MPATH_Connections + +#endif // MPATH + + +/*************************************************************************** + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +bool SessionClass::Am_I_Master(void) +{ + int i; + HousesType house; + HouseClass *hptr; + + //------------------------------------------------------------------------ + // Check every house; if PlayerPtr points to the first human house, we're + // the master. + //------------------------------------------------------------------------ + for (i = 0; i < Session.MaxPlayers; i++) { + house = (HousesType)((int)HOUSE_MULTI1 + i); + hptr = HouseClass::As_Pointer(house); + if (hptr->IsHuman) { + if (PlayerPtr == hptr) { + return (true); + } + else { + return (false); + } + } + } + + return (false); + +} // end of Am_I_Master + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Save(Pipe & file) const +{ +#ifdef FIXIT_MULTI_SAVE + file.Put(&CommProtocol, sizeof(CommProtocol)); + file.Put(&MaxAhead, sizeof(MaxAhead)); + file.Put(&FrameSendRate, sizeof(FrameSendRate)); + file.Put(&DesiredFrameRate, sizeof(DesiredFrameRate)); +#endif // FIXIT_MULTI_SAVE + file.Put(&PrefColor, sizeof(PrefColor)); + file.Put(&ColorIdx, sizeof(ColorIdx)); + file.Put(&House, sizeof(House)); + file.Put(&NumPlayers, sizeof(NumPlayers)); + file.Put(&Options.Bases, sizeof(Options.Bases)); + file.Put(&Options.Credits, sizeof(Options.Credits)); + file.Put(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Put(&Options.Goodies, sizeof(Options.Goodies)); + file.Put(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Put(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Put(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Put(&ObiWan, sizeof(ObiWan)); + file.Put(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Load(Straw & file) +{ +#ifdef FIXIT_MULTI_SAVE +// if(GameVersion != 0x0100616D){ + file.Get(&CommProtocol, sizeof(CommProtocol)); + file.Get(&MaxAhead, sizeof(MaxAhead)); + file.Get(&FrameSendRate, sizeof(FrameSendRate)); + file.Get(&DesiredFrameRate, sizeof(DesiredFrameRate)); +// } +#endif // FIXIT_MULTI_SAVE + file.Get(&PrefColor, sizeof(PrefColor)); + file.Get(&ColorIdx, sizeof(ColorIdx)); + file.Get(&House, sizeof(House)); + file.Get(&NumPlayers, sizeof(NumPlayers)); + file.Get(&Options.Bases, sizeof(Options.Bases)); + file.Get(&Options.Credits, sizeof(Options.Credits)); + file.Get(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Get(&Options.Goodies, sizeof(Options.Goodies)); + file.Get(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Get(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Get(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Get(&ObiWan, sizeof(ObiWan)); + file.Get(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Save(CCFileClass & file) +{ + int i; + + file.Write(&Type, sizeof(Type)); + file.Write(&CommProtocol, sizeof(CommProtocol)); + file.Write(&FrameSendRate, sizeof(FrameSendRate)); + file.Write(&PrefColor, sizeof(PrefColor)); + file.Write(&ColorIdx, sizeof(ColorIdx)); + file.Write(&House, sizeof(House)); + file.Write(&NumPlayers, sizeof(NumPlayers)); + file.Write(&Options.Bases, sizeof(Options.Bases)); + file.Write(&Options.Credits, sizeof(Options.Credits)); + file.Write(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Write(&Options.Goodies, sizeof(Options.Goodies)); + file.Write(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Write(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Write(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Write(&ObiWan, sizeof(ObiWan)); + file.Write(&EmergencySave, sizeof(EmergencySave)); + + i = Players.Count(); + file.Write(&i, sizeof(i)); + for (i = 0; i < Players.Count(); i++) { + file.Write(Players[i], sizeof(NodeNameType)); + } + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Load(CCFileClass & file) +{ + int count; + int i; + NodeNameType *node; + + file.Read(&Type, sizeof(Type)); + file.Read(&CommProtocol, sizeof(CommProtocol)); + file.Read(&FrameSendRate, sizeof(FrameSendRate)); + file.Read(&PrefColor, sizeof(PrefColor)); + file.Read(&ColorIdx, sizeof(ColorIdx)); + file.Read(&House, sizeof(House)); + file.Read(&NumPlayers, sizeof(NumPlayers)); + file.Read(&Options.Bases, sizeof(Options.Bases)); + file.Read(&Options.Credits, sizeof(Options.Credits)); + file.Read(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Read(&Options.Goodies, sizeof(Options.Goodies)); + file.Read(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Read(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Read(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Read(&ObiWan, sizeof(ObiWan)); + file.Read(&EmergencySave, sizeof(EmergencySave)); + + file.Read(&count, sizeof(count)); + for (i = 0; i < count; i++) { + node = new NodeNameType; + file.Read(node, sizeof(NodeNameType)); + Players.Add(node); + } + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Read_MultiPlayer_Settings -- reads settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Read_MultiPlayer_Settings (void) +{ +#if (0)//PG + char *tokenptr; // ptr to token + PhoneEntryClass *phone; // a phone book entry + char *entry; // a phone book entry + char buf[128]; // buffer for parsing INI entry + int i; + CELL cell; + + +// CCFileClass file (CONFIG_FILE_NAME); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete[] InitStrings[i]; + } + InitStrings.Clear(); + + // Clear the dialing entries + for (i = 0; i < PhoneBook.Count(); i++) { + delete[] PhoneBook[i]; + } + PhoneBook.Clear(); + + // Create filename and read the file. + INIClass ini; + if (ini.Load(RawFileClass(CONFIG_FILE_NAME))) { + + // Get the player's last-used Handle + ini.Get_String("MultiPlayer", "Handle", "Noname", Handle, sizeof(Handle)); + + // Get the player's last-used Color + PrefColor = (PlayerColorType)ini.Get_Int("MultiPlayer", "Color", 0); +#ifdef FIXIT_VERSION_3 + int iSide = ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); + iSide = max( 2, min( 6, iSide ) ); + House = (HousesType)iSide; +#else + House = (HousesType)ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); +#endif + CurPhoneIdx = ini.Get_Int("MultiPlayer", "PhoneIndex", -1); + TrapCheckHeap = ini.Get_Int("MultiPlayer", "CheckHeap", 0); + + // Read in default serial settings + ini.Get_String("SerialDefaults", "ModemName", "NoName", SerialDefaults.ModemName, MODEM_NAME_MAX); + if (!strcmp ( SerialDefaults.ModemName, "NoName")) { + SerialDefaults.ModemName[0] = 0; + } + SerialDefaults.Port = ini.Get_Int("SerialDefaults", "Port", 0); + SerialDefaults.IRQ = ini.Get_Int("SerialDefaults", "IRQ", -1); + SerialDefaults.Baud = ini.Get_Int("SerialDefaults", "Baud", -1); + SerialDefaults.Compression = ini.Get_Int ("SerialDefaults", "Compression", 0); + SerialDefaults.ErrorCorrection = ini.Get_Int ("SerialDefaults", "ErrorCorrection", 0); + SerialDefaults.HardwareFlowControl = ini.Get_Int ("SerialDefaults", "HardwareFlowControl", 1); + + ini.Get_String("SerialDefaults", "DialMethod", "T", buf, 2); + +#ifndef WIN32 + /* + ** Ignore any modem name in DOS. This should only be nessasary if the user + ** previously set up the modem in the windows version. + */ + if (SerialDefaults.ModemName[0] && SerialDefaults.Port == 1) { + SerialDefaults.Port = 0x3F8; + SerialDefaults.ModemName[0] = 0; + } +#endif //WIN32 + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + SerialDefaults.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; + } + + SerialDefaults.InitStringIndex = ini.Get_Int("SerialDefaults", "InitStringIndex", 0); + + SerialDefaults.CallWaitStringIndex = ini.Get_Int("SerialDefaults", "CallWaitStringIndex", CALL_WAIT_CUSTOM); + + ini.Get_String("SerialDefaults", "CallWaitString", "", SerialDefaults.CallWaitString, CWAITSTRBUF_MAX); + + if (SerialDefaults.IRQ == 0 || SerialDefaults.Baud == 0) { + SerialDefaults.Port = 0; + SerialDefaults.IRQ = -1; + SerialDefaults.Baud = -1; + } + + int initcount = ini.Entry_Count("InitStrings"); + for (int index = 0; index < initcount; index++) { + entry = new char[ INITSTRBUF_MAX ]; + entry[0] = 0; + ini.Get_String("InitStrings", ini.Get_Entry("InitStrings", index), NULL, entry, INITSTRBUF_MAX); + strupr( entry ); + InitStrings.Add( entry ); + } + + // if no entries then have at least one + if (initcount == 0) { + entry = new char[ INITSTRBUF_MAX ]; + strcpy( entry, "ATZ" ); + InitStrings.Add( entry ); + SerialDefaults.InitStringIndex = 0; + } + + // Read the entry names in + int phonecount = ini.Entry_Count("PhoneBook"); + for (int index = 0; index < phonecount; index++) { + // Create a new phone book entry + phone = new PhoneEntryClass(); + + // Read the entire entry in + ini.Get_String("PhoneBook", ini.Get_Entry("PhoneBook", index), NULL, buf, sizeof(buf)); + + // Extract name, phone # & serial port settings + tokenptr = strtok( buf, "|" ); + if (tokenptr) { + strcpy( phone->Name, tokenptr ); + strupr( phone->Name ); + } else { + phone->Name[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( phone->Number, tokenptr ); + strupr( phone->Number ); + } else { + phone->Number[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + sscanf( tokenptr, "%x", &phone->Settings.Port ); + } else { + phone->Settings.Port = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.IRQ = atoi( tokenptr ); + } else { + phone->Settings.IRQ = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Baud = atoi( tokenptr ); + } else { + phone->Settings.Baud = -1; + } + + phone->Settings.Compression = 0; + phone->Settings.ErrorCorrection = 0; + phone->Settings.HardwareFlowControl = 1; + + /* + ** Find out if this phonebook entry has the new settings included. If not + ** then we need to skip this section. + */ + tokenptr = strtok( NULL, "|" ); + if (tokenptr){ + strcpy( buf, tokenptr ); + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + /* + ** This must be an old phonebook entry + */ + break; + } + } + + /* + ** Method wasnt found - assume its a new phonebook entry so get the extra settings + */ + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + + phone->Settings.Compression = atoi( tokenptr ); + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.ErrorCorrection = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.HardwareFlowControl = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + } + } + + + if (tokenptr) { + strcpy( buf, tokenptr ); + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + phone->Settings.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + } else { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.InitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.InitStringIndex = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.CallWaitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy (phone->Settings.CallWaitString, tokenptr); + } else { + phone->Settings.CallWaitString[0] = 0; + } + + // Add it to our list + PhoneBook.Add(phone); + } + + // Read special recording playback values, to help find sync bugs + TrapFrame = ini.Get_Int("SyncBug", "Frame", 0x7fffffff); + + ini.Get_String("SyncBug", "Type", "NONE", buf, 80); + + if (!stricmp(buf,"AIRCRAFT")) + TrapObjType = RTTI_AIRCRAFT; + else if (!stricmp(buf,"ANIM")) + TrapObjType = RTTI_ANIM; + else if (!stricmp(buf,"BUILDING")) + TrapObjType = RTTI_BUILDING; + else if (!stricmp(buf,"BULLET")) + TrapObjType = RTTI_BULLET; + else if (!stricmp(buf,"INFANTRY")) + TrapObjType = RTTI_INFANTRY; + else if (!stricmp(buf,"UNIT")) + TrapObjType = RTTI_UNIT; + else { + TrapObjType = RTTI_NONE; + } + + ini.Get_String("SyncBug", "Coord", "0", buf, 80); + sscanf(buf,"%x",&TrapCoord); + + ini.Get_String("SyncBug", "Target", "0", buf, 80); + sscanf(buf,"%x",&TrapTarget); + + ini.Get_String("SyncBug", "Cell", "0", buf, 80); + cell = atoi(buf); + if (cell) { + TrapCell = &(Map[cell]); + } + + TrapPrintCRC = ini.Get_Int("SyncBug", "PrintCRC", 0x7fffffff); + } +#endif +} + + +/*************************************************************************** + * SessionClass::Write_MultiPlayer_Settings -- writes settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Write_MultiPlayer_Settings (void) +{ +#ifdef NEVER + char * buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char entrytext[4]; + char buf[128]; // buffer for parsing INI entry + + //------------------------------------------------------------------------ + // Get a working pointer to the INI staging buffer. Make sure that the + // buffer starts cleared out of any data. + //------------------------------------------------------------------------ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name(CONFIG_FILE_NAME); + if (file.Is_Available()) { + file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + } + + //------------------------------------------------------------------------ + // Save the player's last-used Handle & Color + //------------------------------------------------------------------------ + WWWritePrivateProfileInt("MultiPlayer", "PhoneIndex", CurPhoneIdx, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Color", (int)PrefColor, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Side", House, buffer); + WWWritePrivateProfileString("MultiPlayer", "Handle", Handle, buffer); + + //------------------------------------------------------------------------ + // Clear all existing Settings.SerialDefault entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("SerialDefaults", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save default serial settings in opposite order you want to see them + //------------------------------------------------------------------------ + WWWritePrivateProfileString("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex, buffer); + WWWritePrivateProfileString("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ], buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Baud", SerialDefaults.Baud, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "IRQ", SerialDefaults.IRQ, buffer); + sprintf(buf, "%x", SerialDefaults.Port); + WWWritePrivateProfileString("SerialDefaults", "Port", buf, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Compression", SerialDefaults.Compression , buffer); + WWWritePrivateProfileInt ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl, buffer); + + //------------------------------------------------------------------------ + // Clear all existing InitString entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("InitStrings", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all InitString entries. In descending order so they come out in + // ascending order. + //------------------------------------------------------------------------ + for (i = (InitStrings.Count() - 1); i >= 0; i--) { + sprintf( buf, "%03d", i); + WWWritePrivateProfileString ("InitStrings", buf, InitStrings[i], buffer); + } + + //------------------------------------------------------------------------ + // Clear all existing Phone Book entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("PhoneBook", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + //------------------------------------------------------------------------ + for (i = (PhoneBook.Count() - 1); i >= 0; i--) { + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + WWWritePrivateProfileString ("PhoneBook", entrytext, buf, buffer); + } + + //------------------------------------------------------------------------ + // Write the INI data out to a file. + //------------------------------------------------------------------------ + file.Open(WRITE); + file.Write(buffer,strlen(buffer)); + file.Close(); +#endif + +#if (0)//PG + INIClass ini; + RawFileClass file(CONFIG_FILE_NAME); + if (ini.Load(file)) { + + // Save the player's last-used Handle & Color + ini.Put_Int("MultiPlayer", "PhoneIndex", CurPhoneIdx); + ini.Put_Int("MultiPlayer", "Color", (int)PrefColor); + ini.Put_Int("MultiPlayer", "Side", House); + ini.Put_String("MultiPlayer", "Handle", Handle); + + // Clear all existing Settings.SerialDefault entries. + ini.Clear("SerialDefaults"); + + // Save default serial settings in opposite order you want to see them + ini.Put_String("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString); + ini.Put_Int("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex); + ini.Put_Int("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex); + ini.Put_String("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ]); + ini.Put_Int("SerialDefaults", "Baud", SerialDefaults.Baud); + ini.Put_Int("SerialDefaults", "IRQ", SerialDefaults.IRQ); + ini.Put_Int("SerialDefaults", "Port", SerialDefaults.Port, 1); + ini.Put_String("SerialDefaults", "ModemName", SerialDefaults.ModemName); + ini.Put_Int ("SerialDefaults", "Compression", SerialDefaults.Compression ); + ini.Put_Int ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection ); + ini.Put_Int ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl ); + + // Clear all existing InitString entries. + ini.Clear("InitStrings"); + + // Save all InitString entries. + for (int index = 0; index < InitStrings.Count(); index++) { + char buf[10]; + sprintf( buf, "%03d", index); + ini.Put_String("InitStrings", buf, InitStrings[index]); + } + + // Clear all existing Phone Book entries. + ini.Clear("PhoneBook"); + + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + for (int i = (PhoneBook.Count() - 1); i >= 0; i--) { + char buf[128]; + char entrytext[10]; + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + ini.Put_String("PhoneBook", entrytext, buf); + } + + // Write the INI data out to a file. + ini.Save(file); + } +#endif +} + + +// Determine if a mission is from counterstrike or aftermath, or either. +// Multiplayer maps >24, with a numerical name, are Counterstrike. +// Multiplayer maps with an alphabetical name, like SCMJGEA.INI, are Aftermath. + +bool Is_Mission_Counterstrike (char *file_name) +{ + int scenario_number = 0; + + if ( isdigit ( file_name[5] )){ + sscanf (file_name, "SCM%03d", &scenario_number); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!isdigit(file_name[3]) || !isdigit(file_name[4])) { + return(false); + } +#endif + sscanf (file_name, "SCM%02d", &scenario_number); + } + return ( scenario_number > 24 ); +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Aftermath (char *file_name) +{ + // ajw added + // Must start with "scm". + char szCopy[ _MAX_PATH + 1 ]; + strcpy( szCopy, file_name ); + _strlwr( szCopy ); + if( strstr( szCopy, "scm" ) != szCopy ) + return false; + + if (isdigit(file_name[5])) { + return(false); + } + + if ( !isdigit(file_name[3]) || !isdigit(file_name[4]) ) { + return (true); + } + return (false); +} + +/* +** Certain missions are 126x126 size, and those can't be downloaded to a +** non-Aftermath player, so this function checks to see if the map in +** question is one of those. We'll know that by the file name: if it's +** K0 -> M9, it's 126x126. +*/ +bool Is_Mission_126x126 (char *file_name) // This is no longer used. ajw +{ + if (isdigit(file_name[5])) { + return(false); + } + + if ( (file_name[3] >= 'k' && file_name[3] <= 'm') || + (file_name[3] >= 'K' && file_name[3] <= 'M') ) { + return (true); + } + return (false); +} + +#endif + + + + + +/*************************************************************************** + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 09/10/1996 JLB : Searches using different method. * + *=========================================================================*/ +void SessionClass::Read_Scenario_Descriptions (void) +{ + + // Clear the scenario description lists + Scenarios.Clear(); + + /* + ** Fetch the main multiplayer scenario packet data. + */ + CCFileClass file("MISSIONS.PKT"); + if (file.Is_Available()) { + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in Missions.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "msns_pkt.txt" ); + file.Seek( 0, SEEK_SET ); + long lSize = file.Size(); + char* pData = new char[ lSize + 1 ]; + file.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } + + /* + ** Fetch any scenario packet lists and apply them first. + */ +#ifdef WIN32 + WIN32_FIND_DATA block; + HANDLE handle = FindFirstFile("*.PKT", &block); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + char const * name = &block.cAlternateFileName[0]; + if (*name == '\0') name = &block.cFileName[0]; +//Mono_Printf("Found file '%s'.\n", block.cAlternateFileName); +//Mono_Printf("Found file '%s'.\n", block.cFileName); +//debugprint("Found file '%s'.\n", block.cAlternateFileName); +//debugprint("Found file '%s'.\n", block.cFileName); +//debugprint( "Found alternate PKT file.\n" ); + CCFileClass file(name); + INIClass ini; + ini.Load(file); + + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } + } + + if (FindNextFile(handle, &block) == 0) break; + } + + + #ifdef FIXIT_CSII // checked - ajw + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +#ifdef FIXIT_VERSION_3 + if( Is_Counterstrike_Installed() ) + { +#endif + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in cstrike.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "cs_pkt.txt" ); + file2.Seek( 0, SEEK_SET ); + long lSize = file2.Size(); + char* pData = new char[ lSize + 1 ]; + file2.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } +#ifdef FIXIT_VERSION_3 + } +#endif + #endif + +#ifdef FIXIT_VERSION_3 // Aftermath scenarios are now in their own pkt file. + if( Is_Aftermath_Installed() ) + { + CCFileClass file2("AFTMATH.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in aftmath.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); + } + } + } +#endif + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + char const * file_name; + char name_buffer[128]; + char digest_buffer[32]; + + handle = FindFirstFile ( "*.MPR" , &block ); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + file_name = &block.cAlternateFileName[0]; + if (*file_name == '\0') file_name = &block.cFileName[0]; +//debugprint( "Found MPR '%s'\n", file_name ); + CCFileClass file(file_name); + INIClass ini; + ini.Load(file); + + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + Scenarios.Add (new MultiMission (file_name, name_buffer, digest_buffer,ini.Get_Bool("Basic", "Official", false), false )); + } + + if (FindNextFile(handle, &block) == 0) break; + } + +#else //WIN32 + +#error What? You think you can still build the DOS version after all this time? + + char name_buffer[128]; + char digest_buffer[32]; + + struct find_t block; + if (_dos_findfirst("*.PKT", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_CSII + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } + + } while(_dos_findnext(&block) == 0); + } + + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + if (_dos_findfirst("*.MPR", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + bool official = ini.Get_Bool("Basic", "Official", false); + Scenarios.Add (new MultiMission (block.name, name_buffer, digest_buffer, official, false )); + } while(_dos_findnext(&block) == 0); + } + + #ifdef FIXIT_CSII + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +// if (Is_Counterstrike_Installed()) { + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); + } + } +// } + #endif + +#endif //WIN32 +} + + +/*************************************************************************** + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Free_Scenario_Descriptions(void) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the scenario descriptions & filenames + //------------------------------------------------------------------------ + for (int index = 0; index < Scenarios.Count(); index++) { + delete Scenarios[index]; + } + Scenarios.Clear(); +// Filenum.Clear(); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete InitStrings[i]; + } + InitStrings.Clear(); +#if (0)//PG + //------------------------------------------------------------------------ + // Clear the dialing entries + //------------------------------------------------------------------------ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); +#endif +} /* end of Free_Scenario_Descriptions */ + + +/*************************************************************************** + * SessionClass::Trap_Object -- searches for an object, for debugging * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Trap_Object(void) +{ + int i; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + TrapObject.Ptr.All = NULL; + + //------------------------------------------------------------------------ + // Search for the object based upon its type, then its coordinate or + // 'this' pointer value. + //------------------------------------------------------------------------ + switch (TrapObjType) { + case RTTI_AIRCRAFT: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Ptr(i)->Coord == TrapCoord || + Aircraft.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); + break; + } + } + break; + + case RTTI_ANIM: + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Ptr(i)->Coord == TrapCoord || + Anims.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Ptr(i); + break; + } + } + break; + + case RTTI_BUILDING: + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->Coord == TrapCoord || + Buildings.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Ptr(i); + break; + } + } + break; + + case RTTI_BULLET: + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Ptr(i)->Coord == TrapCoord || + Bullets.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Ptr(i); + break; + } + } + break; + + case RTTI_INFANTRY: + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->Coord == TrapCoord || + Infantry.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Ptr(i); + break; + } + } + break; + + case RTTI_UNIT: + for (i = 0; i < Units.Count(); i++) { + if (Units.Ptr(i)->Coord == TrapCoord || + Units.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Ptr(i); + break; + } + } + break; + + //..................................................................... + // Last-ditch find-the-object-right-now-darnit loop + //..................................................................... + case RTTI_NONE: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || + Aircraft.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); + TrapObjType = RTTI_AIRCRAFT; + return; + } + } + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Raw_Ptr(i)->Coord == TrapCoord || + Anims.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); + TrapObjType = RTTI_ANIM; + return; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || + Buildings.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); + TrapObjType = RTTI_BUILDING; + return; + } + } + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || + Bullets.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); + TrapObjType = RTTI_BULLET; + return; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || + Infantry.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); + TrapObjType = RTTI_INFANTRY; + return; + } + } + for (i = 0; i < Units.Count(); i++) { + if (Units.Raw_Ptr(i)->Coord == TrapCoord || + Units.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Raw_Ptr(i); + TrapObjType = RTTI_UNIT; + return; + } + } + + default: + break; + } +} + + +/*************************************************************************** + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1995 BRR : Created. * + *=========================================================================*/ +unsigned long SessionClass::Compute_Unique_ID(void) +{ + return 1;//PG +#if (0) //PG + time_t tm; + unsigned long id; + struct diskfree_t dtable; + char *path; + int i; + + //------------------------------------------------------------------------ + // Start with the seconds since Jan 1, 1970 (system local time) + //------------------------------------------------------------------------ + time(&tm); + id = (unsigned long)tm; + + //------------------------------------------------------------------------ + // Now add in the free space on the hard drive + //------------------------------------------------------------------------ + if (_dos_getdiskfree(3, &dtable) == 0) { + Add_CRC(&id, (unsigned long)dtable.avail_clusters); + Add_CRC(&id, (unsigned long)dtable.total_clusters); + Add_CRC(&id, (unsigned long)dtable.bytes_per_sector); + Add_CRC(&id, (unsigned long)dtable.sectors_per_cluster); + } + + //------------------------------------------------------------------------ + // Add in every byte in the user's path environment variable + //------------------------------------------------------------------------ + path = getenv("PATH"); + if (path) { + for (i = 0; i < strlen(path); i++) { + Add_CRC(&id, (unsigned long)path[i]); + } + } + + return (id); +#endif +} // end of Compute_Unique_ID + + + + +MultiMission::MultiMission(char const * filename, char const * description, char const * digest, bool official, bool expansion) +{ + Set_Filename(filename); + Set_Description(description); + Set_Digest(digest); + Set_Official(official); + Set_Expansion(expansion); +} + + +void MultiMission::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(ScenarioDescription, x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(ScenarioDescription, x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +void MultiMission::Set_Description(char const * description) +{ + if (description != NULL) { + strncpy(ScenarioDescription, description, ARRAY_SIZE(ScenarioDescription)); + ScenarioDescription[ARRAY_SIZE(ScenarioDescription)-1] = '\0'; + } +} + + +void MultiMission::Set_Filename(char const * filename) +{ + if (filename != NULL) { + strncpy(Filename, filename, ARRAY_SIZE(Filename)); + Filename[ARRAY_SIZE(Filename)-1] = '\0'; + } +} + +void MultiMission::Set_Digest(char const * digest) +{ + if (digest != NULL) { + strncpy(Digest, digest, ARRAY_SIZE(Digest)); + Digest[ARRAY_SIZE(Digest)-1] = '\0'; + } + else + { + strcpy( Digest, "NODIGEST" ); + } +} + +void MultiMission::Set_Official (bool official) +{ + IsOfficial = official; +} + +void MultiMission::Set_Expansion (bool expansion) +{ + IsExpansion = expansion; +} + + +/************************** end of session.cpp *****************************/ diff --git a/REDALERT/SESSION.H b/REDALERT/SESSION.H new file mode 100644 index 000000000..7a373c359 --- /dev/null +++ b/REDALERT/SESSION.H @@ -0,0 +1,703 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SESSION.H 4 3/10/97 6:23p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + * The purpose of this class is to contain those variables & routines * + * specifically related to a multiplayer game. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SESSION_H +#define SESSION_H + +#include "ipxaddr.h" +#include "msglist.h" +#include "connect.h" + +//--------------------------------------------------------------------------- +// Forward declarations +//--------------------------------------------------------------------------- +class AircraftClass; +class AnimClass; +class BuildingClass; +class BulletClass; +class InfantryClass; +class UnitClass; +class PhoneEntryClass; +class CellClass; + +//--------------------------------------------------------------------------- +// Defines +//--------------------------------------------------------------------------- +//........................................................................... +// Various limiting values +//........................................................................... +#define MPLAYER_BUILD_LEVEL_MAX 10 // max build level in multiplay +#define MAX_MPLAYER_COLORS 8 // max # of colors + +//........................................................................... +// Max sizes of packets we want to send +// The IPX packet's size is IPX's max size (546), rounded down to accommodate +// the max number of events possible. +//........................................................................... +#define MAX_IPX_PACKET_SIZE (((546 - sizeof(CommHeaderType)) / \ + sizeof(EventClass) ) * sizeof(EventClass)) +#define MAX_SERIAL_PACKET_SIZE 256 + +//........................................................................... +// Max length of player names fields; attempt to use the constant for the +// HouseClass, if it's been defined; otherwise, define it myself. +//........................................................................... +#ifdef HOUSE_NAME_MAX +#define MPLAYER_NAME_MAX HOUSE_NAME_MAX +#else +#define MPLAYER_NAME_MAX 12 // max length of a player's name +#endif + +//........................................................................... +// Values to control the multiplayer score screen +//........................................................................... +#define MAX_MULTI_NAMES 8 // max # names (rows) on the score screen +#define MAX_MULTI_GAMES 4 // max # games (columns) on the score screen + +//........................................................................... +// Min value for MaxAhead, for both net & modem; only applies for +// COMM_PROTOCOL_MULTI_E_COMP. +//........................................................................... +#define MODEM_MIN_MAX_AHEAD 5 +#define NETWORK_MIN_MAX_AHEAD 2 + +//........................................................................... +// Send period (in frames) for COMM_PROTOCOL_MULTI_E_COMP and above +//........................................................................... +#define DEFAULT_FRAME_SEND_RATE 3 + +//........................................................................... +// Modem-specific constants +//........................................................................... +#define PORTBUF_MAX 64 // dialog field sizes +#define IRQBUF_MAX 3 +#define BAUDBUF_MAX 7 +#define INITSTRBUF_MAX 41 +#define CWAITSTRBUF_MAX 16 +#define CREDITSBUF_MAX 5 +#define PACKET_TIMING_TIMEOUT 40 // ticks b/w sending a timing packet +#define MODEM_NAME_MAX PORTBUF_MAX - 1 // Max length of modem name in list box + +//--------------------------------------------------------------------------- +// Enums +//--------------------------------------------------------------------------- +//........................................................................... +// Types of games; used to tell which protocol we're using +//........................................................................... +typedef enum GameEnum { + GAME_NORMAL, // not multiplayer + GAME_MODEM, // modem game + GAME_NULL_MODEM, // NULL-modem + GAME_IPX, // IPX Network game + GAME_INTERNET, // Internet H2H + GAME_SKIRMISH, // 1 plr vs. AI's + GAME_TEN, // TEN Network game + GAME_MPATH, // MPath Network game + GAME_GLYPHX_MULTIPLAYER // Multiplayer game controlled by the GLYPHX engine. ST - 5/14/2019 11:41AM +} GameType; + +//........................................................................... +// Various Modem-specific enums +//........................................................................... +typedef enum DetectPortType { + PORT_VALID = 0, + PORT_INVALID, + PORT_IRQ_INUSE +} DetectPortType; + +typedef enum DialStatusType { + DIAL_CONNECTED = 0, + DIAL_NO_CARRIER, + DIAL_BUSY, + DIAL_ERROR, + DIAL_NO_DIAL_TONE, + DIAL_CANCELED +} DialStatusType; + +typedef enum DialMethodType { + DIAL_TOUCH_TONE = 0, + DIAL_PULSE, + DIAL_METHODS +} DialMethodType; + +typedef enum CallWaitStringType { + CALL_WAIT_TONE_1 = 0, + CALL_WAIT_TONE_2, + CALL_WAIT_PULSE, + CALL_WAIT_CUSTOM, + CALL_WAIT_STRINGS_NUM +} CallWaitStringType; + +typedef enum ModemGameType { + MODEM_NULL_HOST = 0, + MODEM_NULL_JOIN, + MODEM_DIALER, + MODEM_ANSWERER +} ModemGameType; + +//........................................................................... +// Commands sent over the serial Global Channel +//........................................................................... +typedef enum SerialCommandType { + SERIAL_CONNECT = 100, // Are you there? Hello? McFly? + SERIAL_GAME_OPTIONS = 101, // Hey, dudes, here's some new game options + SERIAL_SIGN_OFF = 102, // Bogus, dudes, my boss is coming; I'm outta here! + SERIAL_GO = 103, // OK, dudes, jump into the game loop! + SERIAL_MESSAGE = 104, // Here's a message + SERIAL_TIMING = 105, // timimg packet + SERIAL_SCORE_SCREEN = 106, // player at score screen + SERIAL_LOADGAME = 107, // Start the game, loading a saved game first + SERIAL_LAST_COMMAND, // last command + SERIAL_REQ_SCENARIO = 1000, // Reqest that host sends the scenario file to the other players. + SERIAL_FILE_INFO = 1001, // Info about the file that is going to be transferred + SERIAL_FILE_CHUNK = 1002, // A chunk of scenario + SERIAL_READY_TO_GO = 1003, // Sent in response to a 'GO' command + SERIAL_NO_SCENARIO = 1004 // Scenario isnt available on remote machine so we cant play +} SerialCommandType; + +//........................................................................... +// Commands sent over the network Global Channel +//........................................................................... +typedef enum NetCommandType { + NET_QUERY_GAME, // Hey, what games are out there? + NET_ANSWER_GAME, // Yo, Here's my game's name! + NET_QUERY_PLAYER, // Hey, what players are in this game? + NET_ANSWER_PLAYER, // Yo, I'm in that game! + NET_CHAT_ANNOUNCE, // I'm at the chat screen + NET_CHAT_REQUEST, // Respond with a CHAT_ANNOUNCE, please. + NET_QUERY_JOIN, // Hey guys, can I play too? + NET_CONFIRM_JOIN, // Well, OK, if you really want to. + NET_REJECT_JOIN, // No, you can't join; sorry, dude. + NET_GAME_OPTIONS, // Hey, dudes, here's some new game options + NET_SIGN_OFF, // Bogus, dudes, my boss is coming; I'm outta here! + NET_GO, // OK, jump into the game loop! + NET_MESSAGE, // Here's a message + NET_PING, // I'm pinging you to take a time measurement + NET_LOADGAME, // start a game by loading a saved game + NET_REQ_SCENARIO =1000,// Reqest that host sends the scenario file to the other players. + NET_FILE_INFO =1001,// Info about the file that is going to be transferred + NET_FILE_CHUNK =1002,// A chunk of scenario + NET_READY_TO_GO =1003,// Sent in response to a 'GO' command + NET_NO_SCENARIO =1004 // Scenario isnt available on remote machine so we cant play +} NetCommandType; + +//--------------------------------------------------------------------------- +// Structures +//--------------------------------------------------------------------------- +//........................................................................... +// An entry on the score screen is defined by this structure +//........................................................................... +typedef struct { + char Name[MPLAYER_NAME_MAX]; + int Wins; + int Kills[MAX_MULTI_GAMES]; + PlayerColorType Color; +} MPlayerScoreType; + +//........................................................................... +// Settings for the serial port +//........................................................................... +typedef struct { + int Port; + int IRQ; + int Baud; + DialMethodType DialMethod; + int InitStringIndex; + int CallWaitStringIndex; + char CallWaitString[ CWAITSTRBUF_MAX ]; + bool Compression; + bool ErrorCorrection; + bool HardwareFlowControl; + char ModemName [ MODEM_NAME_MAX ]; +} SerialSettingsType; + +//........................................................................... +// This is a "node", used for the lists of available games & players. The +// 'Game' structure is used for games; the 'Player' structure for players. +//........................................................................... +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; // player or game name + IPXAddressClass Address; +#if(TEN) + int TenAddress; +#endif +#if(MPATH) + int MPathAddress; +#endif + union { + struct { + unsigned char IsOpen; // is the game open? + unsigned long LastTime; // last time we heard from this guy + } Game; + struct { + HousesType House; // "ActLike" House of this player + PlayerColorType Color; // Color of this player + HousesType ID; // Actual House of this player + int ProcessTime; // Length of time to process players main loop + } Player; + struct { + unsigned long LastTime; // last time we heard from this guy + unsigned char LastChance; // we're about to remove him from the list + PlayerColorType Color; // chat player's color + } Chat; + }; +} NodeNameType; + + +//........................................................................... +// Packet sent over the serial Global Channel +//........................................................................... +typedef struct { + SerialCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + unsigned char ID; // unique ID of sender of message + union { + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color or SIGNOFF ID + unsigned long MinVersion; // min version this game supports + unsigned long MaxVersion; // max version this game supports + char Scenario[DESCRIP_MAX]; // Scenario name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? + int CheatCheck; // Unique ID of "rules.ini" file. + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long ResponseTime; // packet response time + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Message[MAX_MESSAGE_LENGTH]; // inter-player message + PlayerColorType Color; // player's color or SIGNOFF ID + } Message; + struct { + PlayerColorType Color; // player's color or SIGNOFF ID + } Chat; + }; +} SerialPacketType; + +//........................................................................... +// Other packet sent over the serial global channel (for file transfers) +//........................................................................... +#define MAX_SEND_FILE_PACKET_SIZE MAX_SERIAL_PACKET_SIZE - 64 +typedef struct { + SerialCommandType Command; // Enum defined above. Should be a file transfer enum. + unsigned short BlockNumber; // Index position of this file chunk in the file + unsigned short BlockLength; // Length of data in the RawData buffer + unsigned char RawData [MAX_SEND_FILE_PACKET_SIZE]; +} RemoteFileTransferType; + + +//........................................................................... +// Packet sent over the network Global Channel +//........................................................................... +typedef struct GlobalPacketType { + NetCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + union { + struct { + unsigned int IsOpen : 1; // 1 = game is open for joining + } GameInfo; + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color + unsigned long NameCRC; // CRC of player's game's name + unsigned long MinVersion; // game's min supported version + unsigned long MaxVersion; // game's max supported version + int CheatCheck; // Unique ID of "rules.ini" file. + } PlayerInfo; + struct { + char Scenario[DESCRIP_MAX]; // Scenario Name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario :1;// Is this scenario an official Westwood one? + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long Version; // version # common to all players + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Buf[MAX_MESSAGE_LENGTH]; // inter-user message + PlayerColorType Color; // color of sender of message + unsigned long NameCRC; // CRC of sender's Game Name + } Message; + struct { + int OneWay; // one-way response time + } ResponseTime; + struct { + int Why; // why were we rejected from the game? + } Reject; + struct { + unsigned long ID; // unique ID for this chat node + PlayerColorType Color; // my color + } Chat; + }; +} GlobalPacketType; + + +//........................................................................... +// For finding sync bugs; filled in by the engine when certain conditions +// are met; the pointers allow examination of objects in the debugger. +//........................................................................... +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +/* +** This is the identifier for a multiplayer mission. This can be used to +** identify the filename of the mission as well as display the mission in a +** mission selection list. +*/ +class MultiMission +{ + public: + MultiMission(char const * filename = NULL, char const * description = NULL, char const *digest = NULL, bool official = true, bool expansion = false); + + void Set_Description(char const * description); + void Set_Filename(char const * filename); + void Set_Digest(char const * digest); + void Set_Official(bool official); + void Set_Expansion(bool expansion); + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Description(void) const {return(ScenarioDescription);} + char const * Get_Filename(void) const {return(Filename);} + char const * Get_Digest(void) const {return(Digest);} + bool Get_Official(void) { return (IsOfficial); } + bool Get_Expansion(void) { return (IsExpansion); } // Implied "IsCounterstrike". No longer used. -ajw + + private: + char ScenarioDescription[DESCRIP_MAX]; + char Filename[_MAX_FNAME+_MAX_EXT]; + char Digest[32]; + bool IsOfficial; + bool IsExpansion; +}; + + +typedef struct { + int ScenarioIndex; //Used on host machine only as index into scenario list + int Bases; + int Credits; + int Tiberium; + int Goodies; + int Ghosts; + int UnitCount; + int AIPlayers; // # of AI players allowed to be built + char ScenarioDescription [DESCRIP_MAX]; //Used on client machines only +} GameOptionsType; + +//--------------------------------------------------------------------------- +// Class Definition +//--------------------------------------------------------------------------- +class SessionClass +{ + //------------------------------------------------------------------------ + // Public interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + SessionClass(void); + ~SessionClass(void); + + //..................................................................... + // Initialization + //..................................................................... + void One_Time(void); + void Init(void); + + //..................................................................... + // Reads/writes to the INI file + //..................................................................... + void Read_MultiPlayer_Settings (void); + void Write_MultiPlayer_Settings (void); + void Read_Scenario_Descriptions (void); + void Free_Scenario_Descriptions(void); + + //..................................................................... + // Utility functions + //..................................................................... + int Create_Connections(void); + bool Am_I_Master(void); + unsigned long Compute_Unique_ID(void); + +#if(TEN) + int Create_TEN_Connections(void); +#endif // TEN + +#if(MPATH) + int Create_MPATH_Connections(void); +#endif // MPATH + + //..................................................................... + // File I/O + //..................................................................... + int Save(Pipe & file) const; + int Load(Straw & file); + int Save(CCFileClass & file); + int Load(CCFileClass & file); + + //..................................................................... + // Debugging / Sync Bugs + //..................................................................... + void Trap_Object(void); + + //--------------------------------------------------------------------- + // Public Data + //--------------------------------------------------------------------- + //..................................................................... + // The type of session being played + //..................................................................... + GameType Type; + + //..................................................................... + // The current communications protocol + //..................................................................... + CommProtocolType CommProtocol; + + //..................................................................... + // Game options + //..................................................................... + GameOptionsType Options; + + //..................................................................... + // Unique workstation ID, for detecting my own packets + //..................................................................... + unsigned long UniqueID; + + //..................................................................... + // Player's local options + //..................................................................... + char Handle[MPLAYER_NAME_MAX]; // player name + PlayerColorType PrefColor; // preferred color index + PlayerColorType ColorIdx; // actual color index + HousesType House; // GDI / NOD + int ObiWan; // 1 = player can see all + int Solo; // 1 = player can play alone + + //..................................................................... + // Max allowable # of players & actual # of (human) players + //..................................................................... + int MaxPlayers; + int NumPlayers; + + //..................................................................... + // Frame-sync'ing timing variables + // 'MaxAhead' is the number of frames ahead of this one to execute + // a given packet. It's set by the RESPONSE_TIME event. + // 'FrameSendRate' is the # frames between data packets + //..................................................................... + unsigned long MaxAhead; + unsigned long FrameSendRate; + + int DesiredFrameRate; + + int ProcessTimer; + int ProcessTicks; + int ProcessFrames; + + //..................................................................... + // This flag is set when we've loaded a multiplayer game. + //..................................................................... + int LoadGame; + + //..................................................................... + // This flag is set when the modem game saves the game due to a lost + // connection. + //..................................................................... + int EmergencySave; + + //..................................................................... + // List of scenarios & their file numbers + //..................................................................... + DynamicVectorClass Scenarios; +// DynamicVectorClass Scenarios; +// DynamicVectorClass Filenum; + + char ScenarioFileName[_MAX_FNAME+_MAX_EXT+1]; //File name of scenario to load + + char ScenarioDigest [32]; //Digest of scenario to load + unsigned int ScenarioFileLength; + bool ScenarioIsOfficial; + + char ScenarioRequests[20]; //Which players requested scenario files + int RequestCount; + IPXAddressClass HostAddress; + + //..................................................................... + // This is the multiplayer messaging system + //..................................................................... + MessageListClass Messages; + IPXAddressClass MessageAddress; + char LastMessage[MAX_MESSAGE_LENGTH]; + unsigned WWChat : 1; // 1 = go into special WW Chat mode + + //..................................................................... + // This is the multiplayer scorekeeping system + //..................................................................... + MPlayerScoreType Score[MAX_MULTI_NAMES]; + int GamesPlayed; // # games played this run + int NumScores; // # active entries in MPlayerScore + int Winner; // index of winner of last game + int CurGame; // index of current game being played + + //..................................................................... + // Static arrays + //..................................................................... + static char Descriptions[100][40]; + static int CountMin[2]; + static int CountMax[2]; + static char * GlobalPacketNames[]; + static char * SerialPacketNames[]; + + //..................................................................... + // For Recording & Playing back a file + //..................................................................... + CCFileClass RecordFile; + unsigned Record : 1; + unsigned Play : 1; + unsigned Attract : 1; + + //..................................................................... + // IPX-specific variables + //..................................................................... + int IsBridge; // 1 = we're crossing a bridge + IPXAddressClass BridgeNet; // address of bridge + bool NetStealth; // makes us invisible + bool NetProtect; // keeps others from messaging us + bool NetOpen; // 1 = game is open for joining + char GameName[MPLAYER_NAME_MAX]; // game's name + GlobalPacketType GPacket; // global packet + int GPacketlen; // global packet length + IPXAddressClass GAddress; // address of sender + unsigned short GProductID; // product ID of sender + char MetaPacket[MAX_IPX_PACKET_SIZE]; // packet building buffer + int MetaSize; // size of MetaPacket + DynamicVectorClass Games; // list of games + DynamicVectorClass Players; // list of players + DynamicVectorClass Chat; // list of chat nodes + + //..................................................................... + // Modem-specific variables + //..................................................................... + unsigned ModemService : 1; // 1 = service modem in Call_Back + int CurPhoneIdx; // phone listing index + SerialSettingsType SerialDefaults; // default serial settings + ModemGameType ModemType; // caller or answerer? + + DynamicVectorClass PhoneBook; + DynamicVectorClass InitStrings; + static char * DialMethodCheck[ DIAL_METHODS ]; + static char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + + //..................................................................... + // For finding Sync Bugs + //..................................................................... + long TrapFrame; // frame # to start trapping 'TrapObject' + RTTIType TrapObjType; // type of object to trap + TrapObjectType TrapObject; // ptr to object to trap (watch) + COORDINATE TrapCoord; // coord of object, 0 = ignore + TARGET TrapTarget; // Target # of object, 0 = ignore + CellClass * TrapCell; // Ptr to cell to trap (watch) + int TrapCheckHeap; // true = check the heap as of TrapFrame + long TrapPrintCRC; // Frame # to print CRC state file + +#if(TEN) + // + // TEN-specific variables + // + char *TenPacket; + int TenSize; + int TenMessageAddress; + int TenAddress; + int TenPlayerID; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // TEN + +#if(MPATH) + // + // MPATH-specific variables + // + char *MPathPacket; + int MPathSize; + int MPathMessageAddress; + int MPathAddress; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // MPATH +}; + +#endif // SESSION_H + +/*************************** end of session.h ******************************/ diff --git a/REDALERT/SHA.CPP b/REDALERT/SHA.CPP new file mode 100644 index 000000000..aedbf5aed --- /dev/null +++ b/REDALERT/SHA.CPP @@ -0,0 +1,314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAEngine::Result -- Fetch the current digest. * + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data blo* + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +//#include +#include "sha.h" + + + +#if !defined(__BORLANDC__) && !defined(min) +#define min(a, b) ((a)<(b))?(a):(b) +#endif + + +/*********************************************************************************************** + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data bloc * + * * + * This routine will see if there is a partial block already accumulated in the holding * + * buffer. If so, then the data is fetched from the source such that a full buffer is * + * accumulated and then processed. If there is insufficient data to fill the buffer, then * + * it accumulates what data it can and then returns so that this routine can be called * + * again later. * + * * + * INPUT: data -- Reference to a pointer to the data. This pointer will be modified if * + * this routine consumes any of the data in the buffer. * + * * + * length-- Reference to the length of the data available. If this routine consumes * + * any of the data, then this length value will be modified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Partial(void const * & data, long & length) +{ + if (length == 0 || data == NULL) return; + + /* + ** If there is no partial buffer and the source is greater than + ** a source block size, then partial processing is unnecessary. + ** Bail out in this case. + */ + if (PartialCount == 0 && length >= SRC_BLOCK_SIZE) return; + + /* + ** Attach as many bytes as possible from the source data into + ** the staging buffer. + */ + int add_count = min((int)length, SRC_BLOCK_SIZE - PartialCount); + memcpy(&Partial[PartialCount], data, add_count); + data = ((char const *&)data) + add_count; + PartialCount += add_count; + length -= add_count; + + /* + ** If a full staging buffer has been accumulated, then process + ** the staging buffer and then bail. + */ + if (PartialCount == SRC_BLOCK_SIZE) { + Process_Block(&Partial[0], Acc); + Length += (long)SRC_BLOCK_SIZE; + PartialCount = 0; + } +} + + +/*********************************************************************************************** + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * * + * This is the main access routine to the SHA engine. It will take the arbitrarily long * + * data block and process it. The hash value is accumulated with any previous calls to * + * this routine. * + * * + * INPUT: data -- Pointer to the data block to process. * + * * + * length -- The number of bytes to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Hash(void const * data, long length) +{ + IsCached = false; + + /* + ** Check for and handle any smaller-than-512bit blocks. This can + ** result in all of the source data submitted to this routine to be + ** consumed at this point. + */ + Process_Partial(data, length); + + /* + ** If there is no more source data to process, then bail. Speed reasons. + */ + if (length == 0) return; + + /* + ** First process all the whole blocks available in the source data. + */ + long blocks = (length / SRC_BLOCK_SIZE); + long const * source = (long const *)data; + for (int bcount = 0; bcount < blocks; bcount++) { + Process_Block(source, Acc); + Length += (long)SRC_BLOCK_SIZE; + source += SRC_BLOCK_SIZE/sizeof(long); + length -= (long)SRC_BLOCK_SIZE; + } + + /* + ** Process any remainder bytes. This data is stored in the source + ** accumulator buffer for future processing. + */ + data = source; + Process_Partial(data, length); +} + + +#define Reverse_LONG(a) ((a>>24)&0x000000FFL) | ((a>>8)&0x0000FF00L) | ((a<<8)&0x00FF0000L) | ((a<<24)&0xFF000000L) + + +/*********************************************************************************************** + * SHAEngine::Result -- Fetch the current digest. * + * * + * This routine will return the digest as it currently stands. * + * * + * INPUT: pointer -- Pointer to the buffer that will hold the digest -- 20 bytes. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will always be * + * 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAEngine::Result(void * result) const +{ + /* + ** If the final hash result has already been calculated for the + ** current data state, then immediately return with the precalculated + ** value. + */ + if (IsCached) { + memcpy(result, &FinalResult, sizeof(FinalResult)); + } + + long length = Length + PartialCount; + int partialcount = PartialCount; + char partial[SRC_BLOCK_SIZE]; + memcpy(partial, Partial, sizeof(Partial)); + + /* + ** Cap the end of the source data stream with a 1 bit. + */ + partial[partialcount] = (char)0x80; + + /* + ** Determine if there is insufficient room to append the + ** data length number to the hash source. If not, then + ** fill out the rest of the accumulator and flush it to + ** the hash so that there will be room for the final + ** count value. + */ + SHADigest acc = Acc; + if ((SRC_BLOCK_SIZE - partialcount) < 9) { + if (partialcount+1 < SRC_BLOCK_SIZE) { + memset(&partial[partialcount+1], '\0', SRC_BLOCK_SIZE - (partialcount+1)); + } + Process_Block(&partial[0], acc); + partialcount = 0; + } else { + partialcount++; + } + + /* + ** Put the length of the source data as a 64 bit integer in the + ** last 8 bytes of the pseudo-source data. + */ + memset(&partial[partialcount], '\0', SRC_BLOCK_SIZE - partialcount); + *(long *)(&partial[SRC_BLOCK_SIZE-4]) = Reverse_LONG((length*8)); + Process_Block(&partial[0], acc); + + memcpy((char *)&FinalResult, &acc, sizeof(acc)); + for (int index = 0; index < sizeof(FinalResult)/sizeof(long); index++) { +// for (int index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + (long &)FinalResult.Long[index] = Reverse_LONG(FinalResult.Long[index]); + } + (bool&)IsCached = true; + memcpy(result, &FinalResult, sizeof(FinalResult)); + return(sizeof(FinalResult)); +} + +/* +** This pragma to turn off the warning "Conversion may lose significant digits" is to +** work around a bug within the Borland compiler. It will give this warning when the +** _rotl() function is called but will NOT give the warning when the _lrotl() function +** is called even though they both have the same parameters and declaration attributes. +*/ +//#pragma warn -sig +template +T _rotl(T X, int n) +{ + return(T)( ( X << n ) | ( (unsigned)X >> ((sizeof(T)*8) - n) ) ); +} +//unsigned long _RTLENTRY _rotl(unsigned long X, int n) +//{ +// return(unsigned long)( (unsigned long)( (unsigned long)( (unsigned long)X ) << (int)n ) | (unsigned long)( ((unsigned long) X ) >> ( (int)((int)(sizeof(long)*(long)8) - (long)n) ) ) ); +//} +void memrev(char * buffer, size_t length); + + +/*********************************************************************************************** + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * * + * This helper routine is called when a full block of data is available for processing * + * into the hash. * + * * + * INPUT: source -- Pointer to the block of data to process. * + * * + * acc -- Reference to the hash accumulator that this hash step will be * + * accumulated into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Block(void const * source, SHADigest & acc) const +{ + /* + ** The hash is generated by performing operations on a + ** block of generated/seeded data. + */ + long block[PROC_BLOCK_SIZE/sizeof(long)]; + + /* + ** Expand the source data into a large 80 * 32bit buffer. This is the working + ** data that will be transformed by the secure hash algorithm. + */ + long const * data = (long const *)source; + int index; + for (index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + block[index] = Reverse_LONG(data[index]); + } + + for (index = SRC_BLOCK_SIZE/sizeof(long); index < PROC_BLOCK_SIZE/sizeof(long); index++) { +// block[index] = _rotl(block[(index-3)&15] ^ block[(index-8)&15] ^ block[(index-14)&15] ^ block[(index-16)&15], 1); + block[index] = _rotl(block[index-3] ^ block[index-8] ^ block[index-14] ^ block[index-16], 1); + } + + /* + ** This is the core algorithm of the Secure Hash Algorithm. It is a block + ** transformation of 512 bit source data with a 2560 bit intermediate buffer. + */ + SHADigest alt = acc; + for (index = 0; index < PROC_BLOCK_SIZE/sizeof(long); index++) { + long temp = _rotl(alt.Long[0], 5) + Do_Function(index, alt.Long[1], alt.Long[2], alt.Long[3]) + alt.Long[4] + block[index] + Get_Constant(index); + alt.Long[4] = alt.Long[3]; + alt.Long[3] = alt.Long[2]; + alt.Long[2] = _rotl(alt.Long[1], 30); + alt.Long[1] = alt.Long[0]; + alt.Long[0] = temp; + } + acc.Long[0] += alt.Long[0]; + acc.Long[1] += alt.Long[1]; + acc.Long[2] += alt.Long[2]; + acc.Long[3] += alt.Long[3]; + acc.Long[4] += alt.Long[4]; +} + diff --git a/REDALERT/SHA.H b/REDALERT/SHA.H new file mode 100644 index 000000000..e75dfb276 --- /dev/null +++ b/REDALERT/SHA.H @@ -0,0 +1,201 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHA.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHA.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHA_H +#define SHA_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include +#include +#include +#include + + +/* +** This implements the Secure Hash Algorithm. It is a cryptographically +** secure hash with no known weaknesses. It generates a 160 bit hash +** result given an arbitrary length data source. +*/ +class SHAEngine +{ + public: + SHAEngine(void) : IsCached(false), Length(0), PartialCount(0) { + Acc.Long[0] = SA; + Acc.Long[1] = SB; + Acc.Long[2] = SC; + Acc.Long[3] = SD; + Acc.Long[4] = SE; + }; + + void Init(void) { + new ((void*)this) SHAEngine; + }; + + // Fetch result as if source data were to stop now. + int Result(void * result) const; + + void Hash(void const * data, long length); + + static int Digest_Size(void) {return(sizeof(SHADigest));} + + private: + + typedef union { + unsigned long Long[5]; + unsigned char Char[20]; + } SHADigest; + + /* + ** This holds the calculated final result. It is cached + ** here to avoid the overhead of recalculating it over + ** multiple sequential requests. + */ + bool IsCached; + SHADigest FinalResult; + + enum { + // These are the initial seeds to the block accumulators. + SA=0x67452301L, + SB=0xefcdab89L, + SC=0x98badcfeL, + SD=0x10325476L, + SE=0xc3d2e1f0L, + + // These are the constants used in the block transformation. + K1=0x5a827999L, // t=0..19 2^(1/2)/4 + K2=0x6ed9eba1L, // t=20..39 3^(1/2)/4 + K3=0x8f1bbcdcL, // t=40..59 5^(1/2)/4 + K4=0xca62c1d6L, // t=60..79 10^(1/2)/4 + + // Source data is grouped into blocks of this size. + SRC_BLOCK_SIZE=16*sizeof(long), + + // Internal processing data is grouped into blocks this size. + PROC_BLOCK_SIZE=80*sizeof(long) + }; + + long Get_Constant(int index) const { + if (index < 20) return K1; + if (index < 40) return K2; + if (index < 60) return K3; + return K4; + }; + + // Used for 0..19 + long Function1(long X, long Y, long Z) const { + return(Z ^ ( X & ( Y ^ Z ) ) ); + }; + + // Used for 20..39 + long Function2(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + // Used for 40..59 + long Function3(long X, long Y, long Z) const { + return( (X & Y) | (Z & (X | Y) ) ); + }; + + // Used for 60..79 + long Function4(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + long Do_Function(int index, long X, long Y, long Z) const { + if (index < 20) return Function1(X, Y, Z); + if (index < 40) return Function2(X, Y, Z); + if (index < 60) return Function3(X, Y, Z); + return Function4(X, Y, Z); + }; + + // Process a full source data block. + void Process_Block(void const * source, SHADigest & acc) const; + + // Processes a partially filled source accumulator buffer. + void Process_Partial(void const * & data, long & length); + + /* + ** This is the running accumulator values. These values + ** are updated by a block processing step that occurs + ** every 512 bits of source data. + */ + SHADigest Acc; + + /* + ** This is the running length of the source data + ** processed so far. This total is used to modify the + ** resulting hash value as if it were appended to the end + ** of the source data. + */ + long Length; + + /* + ** This holds any partial source block. Partial source blocks are + ** a consequence of submitting less than block sized data chunks + ** to the SHA Engine. + */ + int PartialCount; + char Partial[SRC_BLOCK_SIZE]; +}; + + +#define SHA_SOURCE1 "abc" +#define SHA_DIGEST1a "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78\x50\xC2\x6C\x9C\xD0\xD8\x9D" +#define SHA_DIGEST1b "\x01\x64\xB8\xA9\x14\xCD\x2A\x5E\x74\xC4\xF7\xFF\x08\x2C\x4D\x97\xF1\xED\xF8\x80" + + +#define SHA_SOURCE2 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +#define SHA_DIGEST2a "\x84\x98\x3E\x44\x1C\x3B\xD2\x6E\xBA\xAE\x4A\xA1\xF9\x51\x29\xE5\xE5\x46\x70\xF1" +#define SHA_DIGEST2b "\xD2\x51\x6E\xE1\xAC\xFA\x5B\xAF\x33\xDF\xC1\xC4\x71\xE4\x38\x44\x9E\xF1\x34\xC8" + +#define SHA_SOURCE3 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define SHA_DIGEST3a "\x34\xAA\x97\x3C\xD4\xC4\xDA\xA4\xF6\x1E\xEB\x2B\xDB\xAD\x27\x31\x65\x34\x01\x6F" +#define SHA_DIGEST3b "\x32\x32\xAF\xFA\x48\x62\x8A\x26\x65\x3B\x5A\xAA\x44\x54\x1F\xD9\x0D\x69\x06\x03" + +#endif diff --git a/REDALERT/SHAPEBTN.CPP b/REDALERT/SHAPEBTN.CPP new file mode 100644 index 000000000..ea07f16d4 --- /dev/null +++ b/REDALERT/SHAPEBTN.CPP @@ -0,0 +1,179 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPEBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "shapebtn.h" + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Default Constructor for a shape type button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must call Set_Shape() before using a button constructed with this function, * + * and you must set X & Y, and ID. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0), + ReflectButtonState(false) +{ +} + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * * + * This is the normal constructor for a shape type button. Shape buttons are ones that * + * have their imagery controlled by a shape file. The various states of the button are * + * given a visual form as one of these shapes. Button dimensions are controlled by the * + * first shape. * + * * + * INPUT: id -- The button ID. * + * * + * shape -- Pointer to the shape file that controls the button's display. * + * * + * x,y -- The pixel coordinate of the upper left corner of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The width and height of the shape is controlled by the first shape in the * + * shape file provided. This means that all the shapes in the shape file must be * + * the same size. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(unsigned id, void const * shape, int x, int y) : + ToggleClass(id, x, y, 0, 0), + ReflectButtonState(false) +{ +// Width = 0; +// Height = 0; + Set_Shape(shape); +} + + +/*********************************************************************************************** + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * * + * This routine will assign the specified shape to this shape object. * + * * + * INPUT: data -- Pointer to the shape to assign. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void ShapeButtonClass::Set_Shape(void const * data) +{ + ShapeData = data; + if (ShapeData) { + Width = Get_Build_Frame_Width(ShapeData); + Height = Get_Build_Frame_Height(ShapeData); + } +} + + +/*********************************************************************************************** + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * * + * This function is called when the button detects that it must be redrawn. The actual * + * shape to use is controled by the button's state and the shape file provided when then * + * button was constructed. * + * * + * INPUT: forced -- Should the button be redrawn regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the shape redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ShapeButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced) && ShapeData) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the body & set text color. + */ + int shapenum = 0; + if (IsDisabled) { + shapenum = DISABLED_SHAPE; + } else { + + if (!ReflectButtonState) { + + if (IsPressed) { + shapenum = DOWN_SHAPE; + } else { + shapenum = UP_SHAPE; + } + } else { + shapenum = IsOn; + } + + } + CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + + diff --git a/REDALERT/SHAPEBTN.H b/REDALERT/SHAPEBTN.H new file mode 100644 index 000000000..4265e7e85 --- /dev/null +++ b/REDALERT/SHAPEBTN.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPEBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SHAPEBTN_H +#define SHAPEBTN_H + +#include "toggle.h" + +class ShapeButtonClass : public ToggleClass +{ + public: + ShapeButtonClass(void); + ShapeButtonClass(unsigned id, void const * shapes, int x, int y); + virtual int Draw_Me(int forced=false); + virtual void Set_Shape(void const * data); + void const * Get_Shape_Data(void) {return(ShapeData);}; + + enum ShapeButtonClassEnums { + UP_SHAPE, // Shape to use when button is "up". + DOWN_SHAPE, // Shape to use when button is "down". + DISABLED_SHAPE // Shape to use when button is disabled. + }; + + unsigned ReflectButtonState:1; + + protected: + + /* + ** This points to the shape data file. This file contains the appropriate shapes + ** for this button in the offsets specified above. + */ + void const * ShapeData; +}; +#endif diff --git a/REDALERT/SHAPIPE.CPP b/REDALERT/SHAPIPE.CPP new file mode 100644 index 000000000..99cd87d7d --- /dev/null +++ b/REDALERT/SHAPIPE.CPP @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAPipe::Result -- Fetches the current SHA value. * + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shapipe.h" + + +/*********************************************************************************************** + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * * + * This pipe segment will not modify the data, but it will examine the data and use it when * + * building a SHA digest. * + * * + * INPUT: source -- Pointer to the data to flow through the pipe. * + * * + * length -- The number of bytes to submit. * + * * + * OUTPUT: Returns with the actual number of bytes output at the distant final end of the * + * pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Put(void const * source, int slen) +{ + SHA.Hash(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * SHAPipe::Result -- Fetches the current SHA value. * + * * + * This routine will return the SHA digest for the data that has passed through this * + * link in the pipe chain. It is a non-destructive read. * + * * + * INPUT: result -- Pointer to the buffer to hold the SHA digest. This buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Result(void * result) const +{ + return(SHA.Result(result)); +} + + diff --git a/REDALERT/SHAPIPE.H b/REDALERT/SHAPIPE.H new file mode 100644 index 000000000..9d904179c --- /dev/null +++ b/REDALERT/SHAPIPE.H @@ -0,0 +1,63 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHAPIPE_H +#define SHAPIPE_H + +#include "pipe.h" +#include "sha.h" + +/* +** This class serves as a pipe that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAPipe : public Pipe +{ + public: + SHAPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAPipe(SHAPipe & rvalue); + SHAPipe & operator = (SHAPipe const & pipe); +}; + +#endif diff --git a/REDALERT/SHASTRAW.CPP b/REDALERT/SHASTRAW.CPP new file mode 100644 index 000000000..634f52fd9 --- /dev/null +++ b/REDALERT/SHASTRAW.CPP @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHASTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHASTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * SHAStraw::Result -- Fetches the current SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shastraw.h" + + +/*********************************************************************************************** + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * * + * This routine will fetch the requested data and as it passes through this straw it will * + * submit it to the SHA processor. The data that passes through is unmodified by this * + * straw segment. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The length of the data requested. * + * * + * OUTPUT: Returns with the number of bytes stored in the buffer. If this number is less * + * than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + SHA.Hash(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * SHAStraw::Result -- Fetches the current SHA digest. * + * * + * Use this routine to fetch the current SHA digest from the straw. It will return the * + * digest of the data that has passed through this straw segment. * + * * + * INPUT: result -- Pointer to the buffer to hold the message digest. The buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes stored into the digest buffer. This will always * + * be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Result(void * result) const +{ + return(SHA.Result(result)); +} diff --git a/REDALERT/SHASTRAW.H b/REDALERT/SHASTRAW.H new file mode 100644 index 000000000..54c25ce2b --- /dev/null +++ b/REDALERT/SHASTRAW.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHASTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHASTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHASTRAW_H +#define SHASTRAW_H + + +#include "straw.h" +#include "sha.h" + +/* +** This class serves as a straw that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAStraw : public Straw +{ + public: + SHAStraw(void) {} + virtual int Get(void * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAStraw(SHAStraw & rvalue); + SHAStraw & operator = (SHAStraw const & straw); +}; + + +#endif diff --git a/REDALERT/SIDEBAR.CPP b/REDALERT/SIDEBAR.CPP new file mode 100644 index 000000000..db58daa64 --- /dev/null +++ b/REDALERT/SIDEBAR.CPP @@ -0,0 +1,2478 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SIDEBAR.CPP 2 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * SidebarClass::Activate -- Controls the sidebar activation. * + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * SidebarClass::Draw_It -- Renders the sidebar display. * + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void * SidebarClass::SidebarShape = NULL; +void * SidebarClass::SidebarMiddleShape = NULL; +void * SidebarClass::SidebarBottomShape = NULL; + + +/*************************************************************************** +** This holds the translucent table for use with the construction clock +** animation. +*/ +char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; + + +/*************************************************************************** +** This points to the main sidebar shapes. These include the upgrade and +** repair buttons. +*/ +//TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; + +typedef enum ButtonNumberType { + BUTTON_RADAR = 100, + BUTTON_REPAIR, + BUTTON_DEMOLISH, + BUTTON_UPGRADE, + BUTTON_SELECT, + BUTTON_ZOOM +} ButtonNumberType; + +/* +** Sidebar buttons +*/ +SidebarClass::SBGadgetClass SidebarClass::Background; +ShapeButtonClass SidebarClass::Repair; +ShapeButtonClass SidebarClass::Upgrade; +ShapeButtonClass SidebarClass::Zoom; +ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; +ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; +SidebarClass::StripClass::SelectClass +SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; + +/* +** Shape data pointers +*/ +void * SidebarClass::StripClass::LogoShapes = NULL; +void const * SidebarClass::StripClass::ClockShapes; +void const * SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(void) : + IsSidebarActive(false), + IsToRedraw(true), + IsRepairActive(false), + IsUpgradeActive(false), + IsDemolishActive(false) +{ + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = (SIDE_X+8); + WindowList[WINDOW_SIDEBAR][WINDOWY] = SIDE_Y + 1 + TOP_HEIGHT; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = SIDE_WIDTH; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT; +// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ + new (&Column[0]) StripClass(InitClass()); + new (&Column[1]) StripClass(InitClass()); + + Column[0].X = COLUMN_ONE_X * RESFACTOR; + Column[0].Y = COLUMN_ONE_Y * RESFACTOR; + Column[1].X = COLUMN_TWO_X * RESFACTOR; + Column[1].Y = COLUMN_TWO_Y * RESFACTOR; +} + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * * + * Unlike the normal constructor, this one doesn't do any initialization. There is one * + * exception to this. The stip classes can't call an explicit NoInitClass constructor * + * since they are an array. Since the default constructor is called for these strips, we * + * must reset the X and Y location to what we know they should be. * + * * + * INPUT: flag to indicate that this is a no initialization constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(NoInitClass const & x) : PowerClass(x) +{ + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; +} + + +/*********************************************************************************************** + * SidebarClass::One_Time -- Handles the one time game initializations. * + * * + * This routine is used to load the graphic data that is needed by the sidebar display. It * + * should only be called ONCE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once when the game first starts. * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::One_Time(void) +{ + PowerClass::One_Time(); + + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X+8)) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWY] = (SIDE_Y + 1 + TOP_HEIGHT) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT) * RESFACTOR; +// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; + + /* + ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM + */ + WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1*RESFACTOR; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; + Column[0].One_Time(0); + Column[1].One_Time(1); + + /* + ** Load the sidebar shape in at this time. (Hi-Res sidebar is theater dependant) + */ + if (SidebarShape == NULL) { + SidebarShape = (void*)MFCD::Retrieve("SIDEBAR.SHP"); + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Clear(void) +{ + + PowerClass::Init_Clear(); + + IsToRedraw = true; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + Activate(false); +} + + +/*********************************************************************************************** + * SidebarClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_IO(void) +{ + PowerClass::Init_IO(); + + /* + ** Add the sidebar's buttons only if we're not in editor mode. + */ + if (!Debug_Map) { + + Repair.IsSticky = true; + Repair.ID = BUTTON_REPAIR; + Repair.X = (0x1f2/2)*RESFACTOR; + Repair.Y = (0x96/2)*RESFACTOR; + Repair.IsPressed = false; + Repair.IsToggleType = true; + Repair.ReflectButtonState = true; + Repair.Set_Shape(MFCD::Retrieve("REPAIR.SHP")); + + Upgrade.IsSticky = true; + Upgrade.ID = BUTTON_UPGRADE; +#ifdef WIN32 + Upgrade.X = 0x21f; +#else + Upgrade.X = ((0x21f/2)+1)*RESFACTOR; +#endif + Upgrade.Y = (0x96/2)*RESFACTOR; + Upgrade.IsPressed = false; + Upgrade.IsToggleType = true; + Upgrade.ReflectButtonState = true; + Upgrade.Set_Shape(MFCD::Retrieve("SELL.SHP")); + + Zoom.IsSticky = true; + Zoom.ID = BUTTON_ZOOM; + Zoom.X = (0x24c/2)*RESFACTOR; + Zoom.Y = (0x96/2)*RESFACTOR; + Zoom.IsPressed = false; + Zoom.Set_Shape(MFCD::Retrieve("MAP.SHP")); + + if ((IsRadarActive && Is_Zoomable()) || Session.Type != GAME_NORMAL) { + Zoom.Enable(); + } else { + Zoom.Disable(); + } + Column[0].Init_IO(0); + Column[1].Init_IO(1); + + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + if (IsSidebarActive) { + IsSidebarActive = false; + Activate(1); +// Background.Zap(); +// Add_A_Button(Background); + } + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * + * the data that is loaded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Theater(TheaterType theater) +{ + Reload_Sidebar(); + + PowerClass::Init_Theater(theater); + + Column[0].Init_Theater(theater); + Column[1].Init_Theater(theater); +} + +/*********************************************************************************************** + * SidebarClass::Reload_Sidebar -- Loads appropriate sidebar shapes depending on house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/18/1996 BWG : Created. * + *=============================================================================================*/ +void SidebarClass::Reload_Sidebar(void) +{ + static char * sidebarnames[]={ + "SIDE?NA.SHP", //NATO + "SIDE?NA.SHP", + "SIDE?US.SHP", //USSR + "SIDE?NA.SHP", + "SIDE?US.SHP", //UKRAINE + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", //HOUSE_GOOD + "SIDE?US.SHP" //HOUSE_BAD + }; + int houseloaded = 0; + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } + + /* Don't have write access to the static char array. ST - 5/20/2019 */ +#if (0) + char * sidename = sidebarnames[houseloaded]; + *(sidename+4) = '1'; + SidebarShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '2'; + SidebarMiddleShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '3'; + SidebarBottomShape = (void*)MFCD::Retrieve(sidename); +#else + char sb_name[16]; + strcpy(sb_name, sidebarnames[houseloaded]); + sb_name[4] = '1'; + SidebarShape = (void*)MFCD::Retrieve(sb_name); + sb_name[4] = '2'; + SidebarMiddleShape = (void*)MFCD::Retrieve(sb_name); + sb_name[4] = '3'; + SidebarBottomShape = (void*)MFCD::Retrieve(sb_name); + +#endif + + + + + Column[0].Reload_LogoShapes(); + Column[1].Reload_LogoShapes(); +} + +/*********************************************************************************************** + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) +{ + assert((unsigned)type < RTTI_COUNT); + assert(id >= 0); + + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + +/*********************************************************************************************** + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * * + * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * + * to be refreshed as well. If the special code to refresh the sidebar was found, it * + * flags the sidebar to be redrawn and then removes the code from the list. * + * * + * INPUT: cell -- The cell to base the refresh list on. * + * * + * list -- Pointer to the cell offset list that elaborates all the cells that * + * need to be flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Column[0].IsToRedraw = true; + Column[1].IsToRedraw = true; + Flag_To_Redraw(false); + } + PowerClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * * + * Use this routine to turn the repair sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure is friendly and damaged. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Repair(int control) +{ + bool old = IsRepairActive; + + if (control == -1) { + control = IsRepairActive ? 0 : 1; + } + switch (control) { + case 1: + IsRepairActive = true; + break; + + default: + case 0: + IsRepairActive = false; + break; + } + if (old != IsRepairActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + + if (!IsRepairActive) { + Help_Text(TXT_NONE); + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * * + * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure can be upgraded and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Upgrade(int control) +{ + bool old = IsUpgradeActive; + if (control == -1) { + control = IsUpgradeActive ? 0 : 1; + } + switch (control) { + case 1: + IsUpgradeActive = true; + break; + + default: + case 0: + IsUpgradeActive = false; + break; + } + if (old != IsUpgradeActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsUpgradeActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * * + * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * + * the button is enabled when a friendly building is selected and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Demolish(int control) +{ + bool old = IsDemolishActive; + + if (control == -1) { + control = IsDemolishActive ? 0 : 1; + } + switch (control) { + case 1: + IsDemolishActive = true; + break; + + default: + case 0: + IsDemolishActive = false; + break; + } + if (old != IsDemolishActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsDemolishActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::Add(RTTIType type, int id, bool via_capture) +{ + assert((unsigned)type < RTTI_COUNT); + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + int column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + Activate(1); + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * * + * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * + * a building is selected that can produce units. If the number of units to produce is * + * greater than what the sidebar can hold, this routine is used to scroll the other object * + * into view so they can be selected. * + * * + * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * + * later in the list of objects. * + * * + * OUTPUT: bool; Did scrolling occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Scroll(bool up, int column) +{ + if (column == -1) { + bool scr = false; + scr |= Column[0].Scroll(up); + scr |= Column[1].Scroll(up); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + if (scr) { + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + if (Column[column].Scroll(up)) { + // No need to redraw the whole sidebar juts because we scrolled a strip is there? ST - 10/15/96 7:29PM + //IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Draw_It -- Renders the sidebar display. * + * * + * This routine performs the actual drawing of the sidebar display. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the sidebar imagery changed at all? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * + *=============================================================================================*/ +void SidebarClass::Draw_It(bool complete) +{ + PowerClass::Draw_It(complete); + + BStart(BENCH_SIDEBAR); + + if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { + IsToRedraw = false; + + if (LogicPage->Lock()) { + /* + ** Draw the outline box around the sidebar buttons. + */ + int shape = complete ? 0 : 1; + + /* + ** The sidebar shape is too big in 640x400 so it needs to be drawn in three chunks. + */ + CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, (8+80+50)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + LogicPage->Unlock(); + } + } + + /* + ** Draw the side strip elements by calling their respective draw functions. + */ + if (IsSidebarActive) { + Column[0].Draw_It(complete); + Column[1].Draw_It(complete); + + if (complete || IsToRedraw) { + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + } + } + IsToRedraw = false; + + BEnd(BENCH_SIDEBAR); +} + + +/*********************************************************************************************** + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarClass::AI(KeyNumType & input, int x, int y) +{ + bool redraw = false; + + // + // We need to process the sidebar differently in multiplayer. ST - 8/7/2019 10:48AM + // + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + PowerClass::AI(input, x, y); + return; + } + + /* + ** Toggle the sidebar in and out with the key. + */ +#ifndef WIN32 + if (input == KN_TAB) { + Activate(-1); + } +#else + if (!Debug_Map) { + Activate(1); // Force the sidebar always on in Win95 mode + } +#endif //WIN32 + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } + +#ifdef NEVER + if (IsSidebarActive && !Debug_Map) { + + if (input == KN_DOWN) { + int scr = 0; + scr |= Column[0].Scroll(false); + scr |= Column[1].Scroll(false); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + if (input == KN_UP) { + int scr = 0; + scr |= Column[0].Scroll(true); + scr |= Column[1].Scroll(true); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + } +#endif + + + if (IsSidebarActive) { + + /* + ** If there are any buildings in the payer's inventory, then allow the repair + ** option. + */ + if (PlayerPtr->BScan) { + Activate_Repair(true); + } else { + Activate_Repair(false); + } + + if (input == (BUTTON_REPAIR|KN_BUTTON)) { + Repair_Mode_Control(-1); + } + + if (input == (BUTTON_ZOOM|KN_BUTTON)) { + Zoom_Mode_Control(); + } + + if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + Sell_Mode_Control(-1); + } + + if (redraw) { + //IsToRedraw = true; + Column[0].Flag_To_Redraw(); + Column[1].Flag_To_Redraw(); + + Flag_To_Redraw(false); + } + } + + if ((!IsRepairMode) && Repair.IsOn) { + Repair.Turn_Off(); + } + + if ((!IsSellMode) && Upgrade.IsOn) { + Upgrade.Turn_Off(); + } + + PowerClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Recalc(void) +{ + bool redraw = false; + + // Done elsewhere for new multiplayer. ST - 8/7/2019 10:49AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + return; + } + + redraw |= Column[0].Recalc(); + redraw |= Column[1].Recalc(); + + if (redraw) { + IsToRedraw = true; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * SidebarClass::Activate -- Controls the sidebar activation. * + * * + * Use this routine to turn the sidebar on or off. This routine handles updating the * + * necessary flags. * + * * + * INPUT: control -- Tells what to do with the sidebar according to the following: * + * 0 = Turn sidebar off. * + * 1 = Turn sidebar on. * + * -1= Toggle sidebar on or off. * + * * + * OUTPUT: bool; Was the sidebar already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate(int control) +{ + // + // We don't want the original sidebar to be visible. ST - 1/31/2019 11:28AM + // + if (control < 100) { + return IsSidebarActive; + } + + bool old = IsSidebarActive; + + if (Session.Play) + return (old); + + /* + ** Determine the new state of the sidebar. + */ + switch (control) { + case -1: + IsSidebarActive = IsSidebarActive == false; + break; + + case 1: + IsSidebarActive = true; + break; + + default: + case 0: + IsSidebarActive = false; + break; + } + + /* + ** Only if there is a change in the state of the sidebar will anything + ** be done to change it. + */ + if (IsSidebarActive != old) { + + /* + ** If the sidebar is activated but was on the right side of the screen, then + ** activate it on the left side of the screen. + */ + if (IsSidebarActive /*&& X*/) { + Set_View_Dimensions(0, 8 * RESFACTOR, ((320-SIDE_WIDTH)/ICON_PIXEL_W) * RESFACTOR); + IsToRedraw = true; + Help_Text(TXT_NONE); + Repair.Zap(); + Add_A_Button(Repair); + Upgrade.Zap(); + Add_A_Button(Upgrade); + Zoom.Zap(); + Add_A_Button(Zoom); + Column[0].Activate(); + Column[1].Activate(); + Background.Zap(); + Add_A_Button(Background); + Map.RadarButton.Zap(); + Add_A_Button(Map.RadarButton); + Map.PowerButton.Zap(); + Add_A_Button(Map.PowerButton); + } else { + Help_Text(TXT_NONE); + Set_View_Dimensions(0, 8 * RESFACTOR); + Remove_A_Button(Repair); + Remove_A_Button(Upgrade); + Remove_A_Button(Zoom); + Remove_A_Button(Background); + Column[0].Deactivate(); + Column[1].Deactivate(); + Remove_A_Button(Map.RadarButton); + Remove_A_Button(Map.PowerButton); + } + + /* + ** Since the sidebar status has changed, update the map so that the graphics + ** will be rendered correctly. + */ + Flag_To_Redraw(true); + } + + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::StripClass(InitClass const & ) : + X(0), + Y(0), + ID(0), + IsToRedraw(true), + IsBuilding(false), + IsScrollingDown(false), + IsScrolling(false), + Flasher(-1), + TopIndex(0), + Scroller(0), + Slid(0), + BuildableCount(0) +{ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * + * * + * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * + * the shape files it needs for rendering. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::One_Time(int ) +{ + /* + ** Sidebar is player team specific in Hires + */ + ClockShapes = MFCD::Retrieve("CLOCK.SHP"); + + for (SpecialWeaponType lp = SPC_FIRST; lp < SPC_COUNT; lp++) { + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", SpecialWeaponFile[lp]); + + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + SpecialShapes[lp] = MFCD::Retrieve(fullname); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * * + * This routine will return with a pointer to the cameo data for the special objects that * + * can appear on the sidebar (e.g., nuclear bomb). * + * * + * INPUT: type -- The special type to fetch the cameo imagery for. * + * * + * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : commented * + *=============================================================================================*/ +void const * SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) +{ + if ((unsigned)type < SPC_COUNT) { + return(SpecialShapes[type]); + } + return(0); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Clear(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; + Flasher = -1; + TopIndex = 0; + Slid = 0; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * + * * + * This routine doesn't actually add any buttons to the list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_IO(int id) +{ + ID = id; + + UpButton[ID].IsSticky = true; + UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].X = X+(UP_X_OFFSET * RESFACTOR); + UpButton[ID].Y = Y+(UP_Y_OFFSET * RESFACTOR); + +#if (FRENCH) +#ifdef WIN32 + UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); +#else + UpButton[ID].Set_Shape(MFCD::Retrieve("STUP_FIX.SHP")); +#endif +#else //FRENCH + UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); +#endif //FRENCH + + DownButton[ID].IsSticky = true; + DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].X = X+(DOWN_X_OFFSET * RESFACTOR); + DownButton[ID].Y = Y+(DOWN_Y_OFFSET * RESFACTOR); + + /* + ** Buttons are in a slightly different position in the new sidebar + */ + UpButton[ID].Y--; + DownButton[ID].Y--; + + DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectClass & g = SelectButton[ID][index]; + g.ID = BUTTON_SELECT; + g.X = X; + g.Y = Y + ((OBJECT_HEIGHT*index) * RESFACTOR); + g.Width = OBJECT_WIDTH * RESFACTOR; + g.Height = OBJECT_HEIGHT * RESFACTOR; + g.Set_Owner(*this, index); + } + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Theater(TheaterType theater) +{ + + Reload_LogoShapes(); + + if ( (theater != THEATER_NONE) && (theater != ::LastTheater)) { + + static TLucentType const ClockCols[1] = { + {GREEN, BLACK, 100, 0} +// {GREEN, LTGREY, 180, 0} + }; + + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + PaletteClass pal = OriginalPalette; + memset(&pal[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + Build_Translucent_Table(pal, &ClockCols[0], 1, (void*)ClockTranslucentTable); + +// Mem_Copy(GamePalette, OriginalPalette, 768); +// memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + + /* + ** Create the translucent table used for the sidebar. + */ +// Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); +// GamePalette = OriginalPalette; + + Conquer_Build_Fading_Table(GamePalette, &ClockTranslucentTable[256], BLACK, 100); + } +} + +void SidebarClass::StripClass::Reload_LogoShapes(void) +{ + /* + ** Load hi-res strip art here since it is player side specific + */ + static char * stripnames[]={ + "stripna.shp", //Nato + "stripna.shp", + "stripus.shp", //USSR + "stripna.shp", + "stripus.shp", //UKRAINE + "stripna.shp", + "stripna.shp", + "stripna.shp", + "stripna.shp", //HOUSE_GOOD + "stripus.shp", //HOUSE_BAD + }; + int houseloaded = 0; + + /* + ** Sidebar art is dependent on the side of the player + */ + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } + LogoShapes = (void*)MFCD::Retrieve(stripnames[houseloaded]); +} + +/*********************************************************************************************** + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * * + * This routine will add the side strip buttons to the map's input system. This routine * + * should be called once when the sidebar activates. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine a second time without first calling Deactivate(). * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Activate(void) +{ + UpButton[ID].Zap(); + Map.Add_A_Button(UpButton[ID]); + + DownButton[ID].Zap(); + Map.Add_A_Button(DownButton[ID]); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectButton[ID][index].Zap(); + Map.Add_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * + * * + * Call this routine to remove all the buttons on the side strip from the map's input * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine unless the Activate() function was previously called. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Deactivate(void) +{ + Map.Remove_A_Button(UpButton[ID]); + Map.Remove_A_Button(DownButton[ID]); + for (int index = 0; index < MAX_VISIBLE; index++) { + Map.Remove_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + IsToRedraw = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * * + * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * + * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * + * the AI function and the Draw_It function are required to properly give the appearance * + * of scrolling. * + * * + * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * + * * + * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 07/29/1995 JLB : Simplified scrolling logic. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Scroll(bool up) +{ + if (up) { + if (!TopIndex) return(false); + Scroller--; + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) return(false); + Scroller++; + } + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * * + * This utility routine is called when something changes on the sidebar and it must be * + * reflected the next time drawing is performed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Flag_To_Redraw(void) +{ + IsToRedraw = true; + //Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +{ + bool redraw = false; + + /* + ** If this is scroll button for this side strip, then scroll the strip as + ** indicated. + */ + if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + UpButton[ID].IsPressed = false; + if (!Scroll(true)) { + Sound_Effect(VOC_SCOLD); + } + } + if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + DownButton[ID].IsPressed = false; + if (!Scroll(false)) { + Sound_Effect(VOC_SCOLD); + } + } + + /* + ** Reflect the scroll desired direction/value into the scroll + ** logic handler. This might result in up or down scrolling. + */ + if (!IsScrolling && Scroller) { + if (BuildableCount <= MAX_VISIBLE) { + Scroller = 0; + } else { + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (Scroller < 0) { + if (!TopIndex) { + Scroller = 0; + } else { + Scroller++; + IsScrollingDown = false; + IsScrolling = true; + TopIndex--; + Slid = 0; + } + + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) { + Scroller = 0; + } else { + Scroller--; + Slid = OBJECT_HEIGHT; + IsScrollingDown = true; + IsScrolling = true; + } + } + } + } + + /* + ** Scroll logic is handled here. + */ + if (IsScrolling) { + if (IsScrollingDown) { + Slid -= SCROLL_RATE; + if (Slid <= 0) { + IsScrolling = false; + Slid = 0; + TopIndex++; + } + } else { + Slid += SCROLL_RATE; + if (Slid >= OBJECT_HEIGHT) { + IsScrolling = false; + Slid = 0; + } + } + redraw = true; + } + + /* + ** Handle any flashing logic. Flashing occurs when the player selects an object + ** and provides the visual feedback of a recognized and legal selection. + */ + if (Flasher != -1) { + if (Graphic_Logic()) { + redraw = true; + if (Fetch_Stage() >= 7) { + Set_Rate(0); + Set_Stage(0); + Flasher = -1; + } + } + } + + /* + ** Handle any building clock animation logic. + */ + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + redraw = true; + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + switch (pending->What_Am_I()) { + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + + case RTTI_BUILDING: + Speak(VOX_CONSTRUCTION); + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + /* + ** If any of the logic determined that this side strip needs to be redrawn, then + ** set the redraw flag for this side strip. + */ + if (redraw) { + Flag_To_Redraw(); + } + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * * + * Use this routine to render the sidebar display. It checks to see if it needs to be * + * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * + * will force redraw the entire strip. * + * * + * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * + * flag. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 08/06/1995 JLB : Handles multi factory tracking in same strip. * + *=============================================================================================*/ +void SidebarClass::StripClass::Draw_It(bool complete) +{ + if (IsToRedraw || complete) { + IsToRedraw = false; + + if (RunningAsDLL) { + return; + } + + SidebarRedraws++; + + /* + ** Fills the background to the side strip. We shouldnt need to do this if the strip + ** has a full complement of icons. + */ + /* + ** New sidebar needs to be drawn not filled + */ + if (BuildableCount < MAX_VISIBLE) { + CC_Draw_Shape(LogoShapes, ID, X+(2*RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + } + + /* + ** Redraw the scroll buttons. + */ + UpButton[ID].Draw_Me(true); + DownButton[ID].Draw_Me(true); + + /* + ** Loop through all the buildable objects that are visible in the strip and render + ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. + */ + for (int i = 0; i < MAX_VISIBLE + (IsScrolling ? 1 : 0); i++) { + bool production; + bool completed; + int stage; + bool darken = false; + void const * shapefile = 0; + int shapenum = 0; + void const * remapper = 0; + FactoryClass * factory = 0; + int index = i+TopIndex; + int x = X; + int y = Y + (i*OBJECT_HEIGHT * RESFACTOR); + + /* + ** If the strip is scrolling, then the offset is adjusted accordingly. + */ + if (IsScrolling) { + y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; +// y -= OBJECT_HEIGHT - Slid; + } + + /* + ** Fetch the shape number for the object type located at this current working + ** slot. This shape pointer is used to draw the underlying graphic there. + */ + if (index < BuildableCount) { + ObjectTypeClass const * obj = NULL; + SpecialWeaponType spc = SPC_NONE; + + if (Buildables[index].BuildableType != RTTI_SPECIAL) { + + obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (obj != NULL) { + + /* + ** Fetch the remap table that is appropriate for this object + ** type. + */ + remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const *)obj)->Remap); + + /* + ** If there is already a factory producing this kind of object, then all + ** objects of this type are displays in a disabled state. + */ + bool isbusy = (PlayerPtr->Fetch_Factory(Buildables[index].BuildableType) != NULL); + if (!isbusy && PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID)) { + isbusy = true; + } + + /* + ** Infantry don't get remapped in the sidebar (special case). + */ + if (Buildables[index].BuildableType == RTTI_INFANTRYTYPE) { + remapper = 0; + } + + shapefile = obj->Get_Cameo_Data(); + shapenum = 0; + if (Buildables[index].Factory != -1) { + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } else { + production = false; +// darken = IsBuilding; + + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ + darken = isbusy; + } + } else { + darken = PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID); + } + + } else { + + spc = SpecialWeaponType(Buildables[index].BuildableID); + shapefile = Get_Special_Cameo(spc); + shapenum = 0; + + production = true; + completed = PlayerPtr->SuperWeapon[spc].Is_Ready(); + stage = PlayerPtr->SuperWeapon[spc].Anim_Stage(); + darken = false; + } + + if (obj != NULL || spc != SPC_NONE) { + /* + ** If this item is flashing then take care of it. + ** + */ + if (Flasher == index && (Fetch_Stage() & 0x01)) { + remapper = Map.FadingLight; + } + + } else { + shapefile = LogoShapes; + if (!darken) { + shapenum = SB_BLANK; + } + } + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; + } + + remapper = 0; + /* + ** Now that the shape of the object at the current working slot has been found, + ** draw it and any graphic overlays as necessary. + ** + ** Don't draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM + */ + if (shapenum != SB_BLANK || shapefile != LogoShapes) { + CC_Draw_Shape(shapefile, shapenum, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + remapper); + + /* + ** Darken this object because it cannot be produced or is otherwise + ** unavailable. + */ + if (darken) { + CC_Draw_Shape(ClockShapes, 0, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + } + } + + /* + ** Draw the overlapping clock shape if this is object is being constructed. + ** If the object is completed, then display "Ready" with no clock shape. + */ + if (production) { + if (completed) { + + /* + ** Display text showing that the object is ready to place. + */ + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]))+(LEFT_EDGE_OFFSET+15) * RESFACTOR, + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } else { + + CC_Draw_Shape(ClockShapes, stage+1, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + + /* + ** Display text showing that the construction is temporarily on hold. + */ + if (factory && !factory->Is_Building()) { + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET+15) * RESFACTOR), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } + } + } + + } + + LastSlid = Slid; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + bool redraw = false; + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech != NULL) { + ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; + } else { + if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { + ok = PlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); + } else { + ok = false; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + TopIndex = 0; + IsToRedraw = true; + redraw = true; + BuildableCount--; + index--; + } + } + +#ifdef NEVER + /* + ** If there are no more buildable objects to display, make the sidebar go away. + */ + if (!BuildableCount) { + Map.SidebarClass::Activate(0); + } +#endif + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * * + * This is the default constructor for the button that controls the buildable cameos on * + * the sidebar strip. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * + * correct values before this button will function. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::SelectClass::SelectClass(void) : + ControlClass(0, 0, 0, (OBJECT_WIDTH-1) * RESFACTOR, OBJECT_HEIGHT * RESFACTOR, LEFTPRESS|RIGHTPRESS|LEFTUP), + Strip(0), + Index(0) +{ +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * + * * + * Use this routine to set custom buildable vars for this particular select button. It * + * uses this information to properly know what buildable object to start or stop production * + * on. * + * * + * INPUT: strip -- Reference to the strip that owns this buildable button. * + * * + * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * + * the owning strip know what index this button refers to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +{ + Strip = &strip; + Index = index; + X = strip.X; + Y = strip.Y + ((index * OBJECT_HEIGHT) * RESFACTOR); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * + * * + * This function is called when the buildable icon (cameo) is clicked on. It handles * + * starting and stopping production as indicated. * + * * + * INPUT: flags -- The input event that triggered the call. * + * * + * key -- The keyboard value at the time of the input. * + * * + * OUTPUT: Returns with whether the input list should be scanned further. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 10/09/1996 JLB : Sonar pulse converted to regular event type. * + *=============================================================================================*/ +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +{ + int index = Strip->TopIndex + Index; + RTTIType otype = Strip->Buildables[index].BuildableType; + int oid = Strip->Buildables[index].BuildableID; + int fnumber = Strip->Buildables[index].Factory; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + ObjectTypeClass const * choice = NULL; + SpecialWeaponType spc = SPC_NONE; + + /* + ** Determine the factory number that would apply to objects of the type + ** the mouse is currently addressing. This doesn't mean that the factory number + ** fetched is actually producing the indicated object, merely that that particular + ** kind of factory is specified by the "genfactory" value. This can be used to see + ** if the factory type is currently busy or not. + */ + FactoryClass * factory = PlayerPtr->Fetch_Factory(otype); + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + + if (index < Strip->BuildableCount) { + if (otype != RTTI_SPECIAL) { + choice = Fetch_Techno_Type(otype, oid); + } else { + spc = SpecialWeaponType(oid); + } + + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + } else { + Map.Help_Text(TXT_NONE); + } + + if (spc != SPC_NONE) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(SpecialWeaponHelp[spc], X, Y, scheme->Color, true); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". If we are in targeting + ** mode then we don't want to be any more. + */ + if (flags & RIGHTPRESS) { + Map.IsTargettingMode = SPC_NONE; + } + /* + ** A left mouse press signal "activate". If our weapon type is + ** available then we should activate it. + */ + if (flags & LEFTPRESS) { + + if ((unsigned)spc < SPC_COUNT) { + if (PlayerPtr->SuperWeapon[spc].Is_Ready()) { + if (spc != SPC_SONAR_PULSE) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SONAR_PULSE, 0)); + } + } else { + PlayerPtr->SuperWeapon[spc].Impatient_Click(); + } + } + } + + } else { + + if (choice != NULL) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(choice->Full_Name(), X, Y, scheme->Color, true); + Map.Set_Cost(choice->Cost_Of() * PlayerPtr->CostBias); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". + */ + if (flags & RIGHTPRESS) { + + /* + ** If production is in progress, put it on hold. If production is already + ** on hold, then abandon it. Money will be refunded, the factory + ** manager deleted, and the object under construction is returned to + ** the free pool. + */ + if (factory != NULL) { + + /* + ** Cancels placement mode if the sidebar factory is abandoned or + ** suspended. + */ + if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + + if (!factory->Is_Building()) { + Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + } else { + Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); + Map.Column[0].IsToRedraw = true; + Map.Column[1].IsToRedraw = true; + } + } + } + + if (flags & LEFTPRESS) { + + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && factory != NULL) { + Speak(VOX_NO_FACTORY); + ControlClass::Action(flags, key); + return(true); + } + + if (factory != NULL) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + Map.IsTargettingMode = SPC_ANY; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + } else { + flags = 0; + } + } + + ControlClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * + * * + * This routine is called whenever the mouse is over the sidebar. It makes sure that the * + * mouse is always the normal shape while over the sidebar. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- Reference the keyboard code that may be present. * + * * + * OUTPUT: Returns that no further keyboard processing is necessary. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/28/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +{ + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + /* + ** Flag that all the icons on this strip need to be redrawn + */ + Flag_To_Redraw(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there was a change to the strip, then flag the strip to be redrawn. + */ + if (abandon) { + Flag_To_Redraw(); + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + + +/*********************************************************************************************** + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * * + * This is the function that is called when the map button is pressed. It will toggle * + * between the different modes that the radar map can assume. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Zoom_Mode_Control(void) +{ +#ifdef WIN32 + /* + ** If radar is active, cycle as follows: + ** Zoomed => not zoomed + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => zoomed + */ + if (IsRadarActive) { + if (Is_Zoomed() || Session.Type==GAME_NORMAL) { + if (Is_Zoomed() || !Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#else + /* + ** If radar is active, cycle as follows: + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => not zoomed + */ + if (IsRadarActive) { + if (Session.Type==GAME_NORMAL) { + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#endif +} \ No newline at end of file diff --git a/REDALERT/SIDEBAR.H b/REDALERT/SIDEBAR.H new file mode 100644 index 000000000..c599c70b3 --- /dev/null +++ b/REDALERT/SIDEBAR.H @@ -0,0 +1,397 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SIDEBAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "function.h" +#include "power.h" +#include "factory.h" + +class InitClass {}; + +class SidebarClass: public PowerClass +{ + public: + /* + ** These constants are used to control the sidebar rendering. They are instantiated + ** as enumerations since C++ cannot use "const" in this context. + */ + enum SideBarClassEnums { + BUTTON_ACTIVATOR=100, // Button ID for the activator. + SIDE_X=320-80, // The X position of sidebar upper left corner. + SIDE_Y=7+70, // The Y position of sidebar upper left corner. + SIDE_WIDTH=SIDEBAR_WID, // Width of the entire sidebar (in pixels). + SIDE_HEIGHT=200-(7+70), // Height of the entire sidebar (in pixels). + TOP_HEIGHT=13, // Height of top section (with repair/sell buttons). + COLUMN_ONE_X=(320-80)+8, // Sidestrip upper left coordinates... + COLUMN_ONE_Y=int(SIDE_Y)+int(TOP_HEIGHT), + COLUMN_TWO_X=(320-80)+8+((80-16)/2)+3, + COLUMN_TWO_Y=7+70+13, + +//BGA: changes to all buttons +#ifdef GERMAN + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef FRENCH + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef ENGLISH + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=(int)SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=(int)SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=(int)SIDE_X+36, // Right button X coordinate. + BUTTON_TWO_Y=(int)SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=(int)SIDE_X+58, // Right button X coordinate. + BUTTON_THREE_Y=(int)SIDE_Y+2, // Right button Y coordinate. +#endif + + COLUMNS=2 // Number of side strips on sidebar. + }; + + static void * SidebarShape; + static void * SidebarMiddleShape; //Only used in Win95 version + static void * SidebarBottomShape; //Only used in Win95 version + + SidebarClass(void); + SidebarClass(NoInitClass const & x); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + void Reload_Sidebar(void); // Loads house-specific sidebar art + + virtual void AI(KeyNumType & input, int x, int y); + virtual void Draw_It(bool complete); + virtual void Refresh_Cells(CELL cell, short const *list); + + void Zoom_Mode_Control(void); + bool Abandon_Production(RTTIType type, int factory); + bool Activate(int control); + bool Add(RTTIType type, int ID, bool via_capture = false); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM ); + bool Sidebar_Click(KeyNumType & input, int x, int y); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + SelectClass(NoInitClass const & x) : ControlClass(x) {}; + + void Set_Owner(StripClass & strip, int index); + + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + StripClass(void) {} + StripClass(InitClass const &); + StripClass(NoInitClass const & ) {}; + + bool Add(RTTIType type, int ID, bool via_capture); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM ); + bool Abandon_Production(int factory); + bool Scroll(bool up); + bool AI(KeyNumType & input, int x, int y); + void Draw_It(bool complete); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + void Reload_LogoShapes(void); + bool Recalc(void); + void Activate(void); + void Deactivate(void); + void Flag_To_Redraw(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(SpecialWeaponType type); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + BUTTON_UP=200, + BUTTON_DOWN=210, + BUTTON_SELECT=220, + MAX_BUILDABLES=75, // Maximum number of object types in sidebar. + OBJECT_HEIGHT=24, // Pixel height of each buildable object. + OBJECT_WIDTH=32, // Pixel width of each buildable object. + STRIP_WIDTH=35, // Width of strip (not counting border lines). + MAX_VISIBLE=4, // Number of object slots visible at any one time. +#ifdef WIN32 + SCROLL_RATE=12, // The pixel jump while scrolling (larger is faster). +#else + SCROLL_RATE=8, // The pixel jump while scrolling (larger is faster). +#endif + UP_X_OFFSET=2, // Scroll up arrow coordinates. +#ifdef WIN32 + UP_Y_OFFSET=int(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, +#else + UP_Y_OFFSET=int(MAX_VISIBLE)*int(OBJECT_HEIGHT)+2, +#endif + DOWN_X_OFFSET=18, // Scroll down arrow coordinates. + DOWN_Y_OFFSET=UP_Y_OFFSET,//BGint(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, + SBUTTON_WIDTH=16, // Width of the mini-scroll button. + SBUTTON_HEIGHT=12, // Height of the mini-scroll button. + LEFT_EDGE_OFFSET=2, // Offset from left edge for building shapes. + TEXT_X_OFFSET=18, // X offset to print "ready" text. + TEXT_Y_OFFSET=15, // Y offset to print "ready" text. + TEXT_COLOR=255 // Color to use for the "Ready" text. + }; + + /* + ** This is the coordinate of the upper left corner that this side strip + ** uses for rendering. + */ + int X,Y; + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** Shape numbers for the shapes in the STRIP.SHP file. + */ + enum SideBarStipShapeEnums { + SB_BLANK, // The blank rectangle to use if there are no objects present. + SB_FRAME + }; + + /* + ** If this particular side strip needs to be redrawn, then this flag + ** will be true. + */ + unsigned IsToRedraw:1; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This controls the sidebar slide direction. If this is true, then the sidebar + ** will scroll downward -- revealing previous objects. + */ + unsigned IsScrollingDown:1; + + /* + ** If the sidebar is scrolling, then this flag is true. Otherwise it is false. + */ + unsigned IsScrolling:1; + + /* + ** This is the object (sidebar slot) that is flashing. Only one slot can be flashing + ** at any one instant. This is usually the result of a click on the slot and construction + ** has commenced. + */ + int Flasher; + + /* + ** As the sidebar scrolls up and down, this variable holds the index for the topmost + ** visible sidebar slot. + */ + int TopIndex; + + /* + ** This is the queued scroll direction and amount. The sidebar + ** will scroll the number of slots indicated by this value. This + ** value is set according to the scroll buttons. + */ + int Scroller; + + /* + ** The sidebar has smooth scrolling. This is the number of pixels the sidebar + ** has slide down. Thus, if this value were 5, then there would be 5 pixels of + ** the TopIndex-1 sidebar object visible. When the Slid value reaches 24, then + ** the value resets to zero and the TopIndex is decremented. For sliding in the + ** opposite direction, change the IsScrollingDown flag. + */ + int Slid; + + /* + ** The value of Slid the last time we rendered the sidebar. + */ + int LastSlid; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + /* + ** Pointer to the shape data for small versions of the logos. These are used as + ** placeholder pieces on the side bar. + */ + static void * LogoShapes; + + /* + ** This points to the animation sequence of frames used to mark the passage of time + ** as an object is undergoing construction. + */ + static void const * ClockShapes; + + /* + ** This points to the animation sequence which deals with special + ** shapes which handle non-production based icons. + */ + static void const * SpecialShapes[SPC_COUNT]; + + /* + ** This is the last theater that the special palette remap table was loaded + ** for. If the current theater differs from this recorded value, then the + ** remap tables are reloaded. + */ +// static TheaterType LastTheater; + + static ShapeButtonClass UpButton[COLUMNS]; + static ShapeButtonClass DownButton[COLUMNS]; + static SelectClass SelectButton[COLUMNS][MAX_VISIBLE]; + + /* + ** This points to the shapes that are used for the clock overlay. This displays + ** progress of construction. + */ + static char ClockTranslucentTable[(1+1)*256]; + + } Column[COLUMNS]; + + + /* + ** If the sidebar is active then this flag is true. + */ + unsigned IsSidebarActive:1; + + /* + ** This flag tells the rendering system that the sidebar needs to be redrawn. + */ + unsigned IsToRedraw:1; + + class SBGadgetClass: public GadgetClass { + public: +//#ifdef WIN32 + SBGadgetClass(void) : GadgetClass((int)((int)SIDE_X+8)*RESFACTOR, (int)SIDE_Y*RESFACTOR, (int)((int)SIDE_WIDTH-1)*RESFACTOR-1, (int)((int)SIDE_HEIGHT-1)*RESFACTOR, LEFTUP) {}; +//#else +// SBGadgetClass(void) : GadgetClass((int)SIDE_X+8, (int)SIDE_Y, (int)SIDE_WIDTH-1, (int)SIDE_HEIGHT-1, LEFTUP) {}; +//#endif + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + /* + ** This is the button that is used to collapse and expand the sidebar. + ** These buttons must be available to derived classes, for Save/Load. + */ + static ShapeButtonClass Repair; + static ShapeButtonClass Upgrade; + static ShapeButtonClass Zoom; + static SBGadgetClass Background; + + bool Scroll(bool up, int column); + + private: + bool Activate_Repair(int control); + bool Activate_Upgrade(int control); + bool Activate_Demolish(int control); + int Which_Column(RTTIType type); + + unsigned IsRepairActive:1; + unsigned IsUpgradeActive:1; + unsigned IsDemolishActive:1; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/SIDEBARGlyphx.CPP b/REDALERT/SIDEBARGlyphx.CPP new file mode 100644 index 000000000..1889862e1 --- /dev/null +++ b/REDALERT/SIDEBARGlyphx.CPP @@ -0,0 +1,821 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.cpv 2.13 02 Aug 1995 17:03:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBARGlyphx.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 14th, 2019 * + * * + * Last Update : March 14th, 2019 * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "SidebarGlyphx.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + +/*********************************************************************************************** + * SidebarGlyphxClass::SidebarGlyphxClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::SidebarGlyphxClass(void) +{ + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_Clear(HouseClass *player_ptr) +{ + SidebarPlayerPtr = player_ptr; + + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + + //Activate(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_IO(void) +{ + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + //if (IsSidebarActive) { + // IsSidebarActive = false; + // Activate(1); + //} +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarGlyphxClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Factory_Link(int factory, RTTIType type, int id) +{ + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Add(RTTIType type, int id, bool via_capture) +{ + int column; + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + //Activate(1); + return(true); + } + return(false); + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarGlyphxClass::AI(KeyNumType & input, int x, int y) +{ + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Recalc(void) +{ + Column[0].Recalc(); + Column[1].Recalc(); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::StripClass::StripClass(void) +{ + IsBuilding = false; + BuildableCount = 0; + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; + } + ParentSidebar = NULL; +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::StripClass::Init_Clear(void) +{ + IsBuilding = false; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; + } +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + return(true); + } + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::AI(KeyNumType & input, int , int ) +{ + /* + ** This is needed as it's where units get queued for structure exit. ST -3/14/2019 12:03PM + */ + + + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending) { + switch (pending->What_Am_I()) { + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + + case RTTI_BUILDING: + if (!factory->Is_Blocked()) { + Speak(VOX_CONSTRUCTION); + } + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + return(false); +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech) { + ok = tech->Who_Can_Build_Me(true, false, ParentSidebar->SidebarPlayerPtr->Class->House) != NULL; + } else { + + if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { + ok = ParentSidebar->SidebarPlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); + } else { + ok = false; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + BuildableCount--; + index--; + + Buildables[BuildableCount].BuildableID = 0; + Buildables[BuildableCount].BuildableType = RTTI_NONE; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = false; + } + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + +/*********************************************************************************************** + * SidebarGlyphxClass::Code_Pointers -- Converts classes pointers to savable representation * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Code_Pointers(void) +{ + if (SidebarPlayerPtr) { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)SidebarPlayerPtr->Class->House; + } else { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Decode_Pointers -- Converts classes savable representation to run-time * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Decode_Pointers(void) +{ + if (*((HousesType*)&SidebarPlayerPtr) == HOUSE_NONE) { + SidebarPlayerPtr = NULL; + } else { + ((HouseClass *&)SidebarPlayerPtr) = HouseClass::As_Pointer(*((HousesType*)&SidebarPlayerPtr)); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Load(Straw &file) +{ + ::new (this) SidebarGlyphxClass(); + + //bool ok = Read_Object(this, sizeof(*this), file, false); + if (file.Get(this, sizeof(*this)) != sizeof(*this)) { + return false; + } + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + return true; +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Save(Pipe &file) +{ + //return(Write_Object(this, sizeof(*this), file)); + + file.Put(this, sizeof(*this)); + + return true; +} + + +extern SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr); + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_Clear(player_ptr); + } +} + +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_IO(); + } +} + +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Abandon_Production(type, factory); + } + + return false; +} + +bool Sidebar_Glyphx_Add(RTTIType type, int id, HouseClass *player_ptr, bool via_capture) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Add(type, id, via_capture); + } + + return false; +} + +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Recalc(); + } +} + +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->AI(input, 0, 0); + } +} + +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Factory_Link(factory, type, id); + } + + return false; +} + +bool Sidebar_Glyphx_Save(Pipe &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Save(file); + } + return false; +} + +bool Sidebar_Glyphx_Load(Straw &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Load(file); + } + return false; +} + +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Code_Pointers(); + } +} + +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Decode_Pointers(); + } +} \ No newline at end of file diff --git a/REDALERT/SIDEBARGlyphx.H b/REDALERT/SIDEBARGlyphx.H new file mode 100644 index 000000000..2e085a10a --- /dev/null +++ b/REDALERT/SIDEBARGlyphx.H @@ -0,0 +1,209 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.h_v 2.18 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_GLYPHX_H +#define SIDEBAR_GLYPHX_H + +#pragma once + +#include "function.h" +#include "power.h" +#include "factory.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + + +class SidebarGlyphxClass +{ + public: + + enum SideBarClassEnums { + COLUMNS=2 // Number of side strips on sidebar. + }; + + SidebarGlyphxClass(void); + + /* + ** Initialization + */ + void Init_Clear(HouseClass *player_ptr); // Clears all to known state + void Init_IO(void); // Inits button list + + void AI(KeyNumType & input, int x, int y); + + bool Abandon_Production(RTTIType type, int factory); + bool Add(RTTIType type, int ID, bool via_capture = false); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + bool Load(Straw &file); + bool Save(Pipe &pipe); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + + void Set_Owner(StripClass & strip, int index); + StripClass * Strip; + int Index; + + protected: + int Action(unsigned flags, KeyNumType & key); + }; + + public: + + StripClass(void); + bool Add(RTTIType type, int ID, bool via_capture); + bool Abandon_Production(int factory); + bool AI(KeyNumType & input, int x, int y); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + bool Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(int type); + + void Set_Parent_Sidebar(SidebarGlyphxClass *parent) {ParentSidebar = parent;} + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + //void Code_Pointers(void); + //void Decode_Pointers(void); + + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + MAX_BUILDABLES = 75 // Maximum number of object types in sidebar. + }; + + SidebarGlyphxClass *ParentSidebar; + + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + } Column[COLUMNS]; + + + private: + int Which_Column(RTTIType type); + + HouseClass *SidebarPlayerPtr; +}; + + + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr = NULL); // Inits button list +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Add(RTTIType type, int ID, HouseClass *player_ptr = NULL, bool via_capture = false); +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input); +bool Sidebar_Glyphx_Save(Pipe &file, SidebarGlyphxClass *sidebar); +bool Sidebar_Glyphx_Load(Straw &file, SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar); + + + + + +#endif //SIDEBAR_GLYPHX_H \ No newline at end of file diff --git a/REDALERT/SLIDER.CPP b/REDALERT/SLIDER.CPP new file mode 100644 index 000000000..fa96ad3d2 --- /dev/null +++ b/REDALERT/SLIDER.CPP @@ -0,0 +1,413 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SLIDER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SliderClass::Action -- Handles input processing for the slider. * + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * SliderClass::Step -- Steps the slider one value up or down. * + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * SliderClass::~SliderClass -- Destructor for slider object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "slider.h" + + +/*********************************************************************************************** + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * * + * This is the normal constructor for the slider gadget. * + * * + * INPUT: id -- The ID number to assign to this gadget. * + * x,y -- The pixel coordinate of the upper left corner for this gadget. * + * w,h -- The width and height of the slider gadget. The slider automatically * + * adapts for horizontal or vertical operation depending on which of these * + * dimensions is greater. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list) + : GaugeClass(id, x, y, w, h) +{ + BelongToList = belong_to_list ? true : false; + + PlusGadget = 0; + MinusGadget = 0; + if (!BelongToList) { + PlusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-PLUS.SHP"), X+Width+2, Y); + MinusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-MINS.SHP"), X-6, Y); + + if (PlusGadget) { + PlusGadget->Make_Peer(*this); + PlusGadget->Add(*this); + PlusGadget->Flag_To_Redraw(); + } + if (MinusGadget) { + MinusGadget->Make_Peer(*this); + MinusGadget->Add(*this); + MinusGadget->Flag_To_Redraw(); + } + } + Set_Thumb_Size(1); + Recalc_Thumb(); + + /* + ** Gauges have at least 2 colors, but sliders should only have one. + */ + IsColorized = 0; +} + + +/*********************************************************************************************** + * SliderClass::~SliderClass -- Destructor for slider object. * + * * + * This cleans up the slider object in preparation for deletion. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::~SliderClass(void) +{ + if (PlusGadget) { + delete PlusGadget; + PlusGadget = 0; + } + if (MinusGadget) { + delete MinusGadget; + MinusGadget = 0; + } +} + + +/*********************************************************************************************** + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * * + * This sets the maximum value that the slider can be set at. The maximum value controls * + * the size of the thumb and the resolution of the thumb's movement. * + * * + * INPUT: value -- The value to set for the slider's maximum. * + * OUTPUT: bool; Was the maximum value changed? A false indicates a set to the value it * + * is currently set to already. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Maximum(int value) +{ + if (GaugeClass::Set_Maximum(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * * + * This routine will set the size of the thumb as it relates to the maximum value the * + * slider can achieve. This serves to display a proportionally sized thumb as well as * + * control how the slider "bumps" up or down. * + * * + * INPUT: value -- The new value of the thumb. It should never be larger than the slider * + * maximum. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Set_Thumb_Size(int value) +{ + Thumb = min(value, MaxValue); + Thumb = max(Thumb, 1); + Flag_To_Redraw(); + Recalc_Thumb(); +} + + +/*********************************************************************************************** + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * * + * This routine will set the thumb position for the slider. * + * * + * INPUT: value -- The position to set the slider. This position is relative to the maximum * + * value for the slider. * + * * + * OUTPUT: bool; Was the slider thumb position changed at all? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Value(int value) +{ + value = min(value, MaxValue-Thumb); + + if (GaugeClass::Set_Value(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * * + * This takes the current thumb logical size and starting value and calculates the pixel * + * size and starting offset accordingly. This function should be called whenever one of * + * these elements has changed. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Recalc_Thumb(void) +{ + int length = IsHorizontal ? Width : Height; + int size = length * fixed(Thumb, MaxValue); +// int size = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, Thumb)); + ThumbSize = max(size, 4); + int start = length * fixed(CurValue, MaxValue); +// int start = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, CurValue)); + ThumbStart = min(start, length-ThumbSize); +} + + +/*********************************************************************************************** + * SliderClass::Action -- Handles input processing for the slider. * + * * + * This routine is called when a qualifying input event has occurred. This routine will * + * process that event and make any adjustments to the slider as necessary. * + * * + * INPUT: flags -- Flag bits that tell the input event that caused this function to * + * be called. * + * key -- Reference to the key that caused the input event. * + * OUTPUT: bool; Was the event consumed and further processing of the gadget list should be * + * aborted? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** Handle the mouse click in a special way. If the click was not on the thumb, then + ** jump the thumb position one "step" in the appropriate direction. Otherwise, let normal + ** processing take place -- the slider then "sticks" and the thumb moves according to + ** mouse position. + */ + if (flags & LEFTPRESS) { + int mouse; // Mouse pixel position. + int edge; // Edge of slider. + + if (IsHorizontal) { + mouse = Get_Mouse_X(); + edge = X; + } else { + mouse = Get_Mouse_Y(); + edge = Y; + } + edge += 1; + + /* + ** Clicking outside the thumb: invoke parent's Action to process flags etc, + ** but turn off the event & return true so processing stops at this button. + */ + if (mouse < edge+ThumbStart) { + Bump(true); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + if (mouse > edge+ThumbStart+ThumbSize) { + Bump(false); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + GaugeClass::Action(flags, key); + key = KN_NONE; + return(true); + } + } + } + + /* + ** CHANGE GAUGECLASS::ACTION -- REMOVE (LEFTRELEASE) FROM IF STMT + */ + return(GaugeClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * * + * This support function will bump the slider one "step" or the size of the thumb up or * + * down as specified. It is typically called when the slider is clicked outside of the * + * thumb region but still inside of the slider. * + * * + * INPUT: up -- Should the bump be to increase the current position? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Bump(int up) +{ + if (up) { + return(Set_Value(CurValue - Thumb)); + } + return(Set_Value(CurValue + Thumb)); +} + + +/*********************************************************************************************** + * SliderClass::Step -- Steps the slider one value up or down. * + * * + * This routine will move the slider thumb one step in the direction specified. * + * * + * INPUT: up -- Should the step be up (i.e., forward)? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Step(int up) +{ + if (up) { + return(Set_Value(CurValue - 1)); + } + return(Set_Value(CurValue + 1)); +} + + +/*********************************************************************************************** + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * * + * This will draw the thumb graphic for this slider. Sometimes the thumb requires special * + * drawing, thus the need for this function separate from the normal Draw_Me function. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: The mouse is guaranteed to be hidden when this routine is called. * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Draw_Thumb(void) +{ + if (IsHorizontal) { + Draw_Box(X+ThumbStart, Y, ThumbSize, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, Y+ThumbStart, Width, ThumbSize, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * SliderClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * OUTPUT: bool; Was the gauge redrawn? * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Draw_Me(int forced) +{ + if (BelongToList) { + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + Draw_Thumb(); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + } + + /* + ** If it does not belong to a listbox... + */ + return(GaugeClass::Draw_Me(forced)); +} + + +/*********************************************************************************************** + * SliderClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * key -- The key value at the time of the event. * + * whom -- Which gadget is being touched. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Peer_To_Peer(unsigned flags, KeyNumType & , ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == PlusGadget) { + Step(false); + } + if (&whom == MinusGadget) { + Step(true); + } + } +} + + diff --git a/REDALERT/SLIDER.H b/REDALERT/SLIDER.H new file mode 100644 index 000000000..e0f125ad3 --- /dev/null +++ b/REDALERT/SLIDER.H @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SLIDER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "gauge.h" +#include "shapebtn.h" + + +/*************************************************************************** + * SliderClass -- Like a Windows ListBox structure * + * * + * INPUT: int id-- id of gadget * + * int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * int belong_to_list -- does this slider go with a listclass? * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class SliderClass : public GaugeClass +{ + public: + SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + virtual ~SliderClass(void); + + virtual void Set_Thumb_Size(int value); + virtual int Set_Maximum(int value); + virtual int Set_Value(int); + virtual int Bump(int up); + virtual int Step(int up); + virtual int Draw_Me(int forced); + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + + virtual int Thumb_Pixels(void) { return (ThumbSize);} + + protected: + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + ShapeButtonClass * PlusGadget; + ShapeButtonClass * MinusGadget; + + /* + ** If I belong to a listbox, I have to draw myself differently... + **/ + unsigned BelongToList:1; + + /* + ** This is the logical size of the thumb. This value is used when drawing + ** the thumb imagery. It is also the amount that is bumped when the + ** Bump() function is called. (This value is in application units.) + */ + int Thumb; + + /* + ** This is the current thumb pixel size and starting offset from beginning + ** of slider region. (These values are in pixels.) + */ + int ThumbSize; + int ThumbStart; // x or y position for the thumb + + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Thumb(void); + + private: + void Recalc_Thumb(void); +}; + +#endif diff --git a/REDALERT/SMUDGE.CPP b/REDALERT/SMUDGE.CPP new file mode 100644 index 000000000..ef2f8eb61 --- /dev/null +++ b/REDALERT/SMUDGE.CPP @@ -0,0 +1,361 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SMUDGE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * SmudgeClass::operator new -- Creator of smudge objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "smudge.h" + + + +HousesType SmudgeClass::ToOwn = HOUSE_NONE; + + +/*********************************************************************************************** + * SmudgeClass::operator new -- Creator of smudge objects. * + * * + * This routine will allocate a smudge object from the smudge tracking pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a newly allocated smudge object. If one couldn't be * + * found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * SmudgeClass::operator new(size_t ) +{ + void * ptr = Smudges.Allocate(); + if (ptr != NULL) { + ((SmudgeClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * * + * This routine is used to remove the smudge from the tracking system. * + * * + * INPUT: ptr -- Pointer to the smudge to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((SmudgeClass *)ptr)->IsActive = false; + } + Smudges.Free((SmudgeClass *)ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * * + * This is the typical constructor for smudge objects. If the position to place the * + * smudge is not given, then the smudge will be initialized in a limbo state. If the * + * smudge is placed on the map, then this operation causes the smudge object itself to be * + * deleted and special map values updated to reflect the presence of a smudge. * + * * + * INPUT: type -- The type of smudge to construct. * + * * + * pos -- The position to place the smudge. If -1, then the smudge is initialized * + * into a limbo state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeClass::SmudgeClass(SmudgeType type, COORDINATE pos, HousesType house) : + ObjectClass(RTTI_SMUDGE, Smudges.ID(this)), + Class(SmudgeTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + if (!Unlimbo(pos)) { + delete this; + } else { + ToOwn = HOUSE_NONE; + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * * + * This routine is used during the scenario clearing process to initialize the smudge * + * object tracking system to a null state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Init(void) +{ + Smudges.Free_All(); +} + + +/*********************************************************************************************** + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * * + * This routine will place the smudge on the map. If the map cell allows. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the smudge marked successfully? Failure occurs if the smudge isn't * + * marked DOWN. * + * * + * WARNINGS: The smudge object is DELETED by this routine. * + * * + * HISTORY: * + * 09/22/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool SmudgeClass::Mark(MarkType mark) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL origin = Coord_Cell(Coord); + + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CELL newcell = origin + w + (h*MAP_CELL_W); + if (Map.In_Radar(newcell)) { + CellClass * cell = &Map[newcell]; + + if (Class->IsBib) { + cell->Smudge = Class->Type; + cell->SmudgeData = w + (h*Class->Width); + cell->Owner = ToOwn; + } else { + if (cell->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + if (Class->IsCrater && cell->Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(cell->Smudge).IsCrater) { + cell->SmudgeData++; + cell->SmudgeData = (int)min((int)cell->SmudgeData, (int)4); + } + + if (cell->Smudge == SMUDGE_NONE) { + + /* + ** Special selection of a crater that starts as close to the + ** specified coordinate as possible. + */ + if (Class->IsCrater) { + cell->Smudge = (SmudgeType)(SMUDGE_CRATER1 + CellClass::Spot_Index(Coord)); + } else { + cell->Smudge = Class->Type; + } + cell->SmudgeData = 0; + } + } + } + + /* + ** Flag everything that might be overlapping this cell to redraw itself. + */ + cell->Redraw_Objects(); + } + } + } + + /* + ** Whether it was successful in placing, or not, delete the smudge object. It isn't + ** needed once the map has been updated with the proper smudge data. Fake this object + ** as if it were never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * * + * This routine is used when a building is removed from the game. If there was any bib * + * attached, this routine will be called to disown the cells and remove the bib artwork. * + * * + * INPUT: cell -- The origin cell for this bib removal. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is actually working on a temporary bib object. It is created for the sole * + * purpose of calling this routine. It will be deleted immediately afterward. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Disown(CELL cell) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (Class->IsBib) { + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CellClass & cellptr = Map[(CELL)(cell + w + (h*MAP_CELL_W))]; + + if (cellptr.Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr.Overlay).IsWall) { + cellptr.Smudge = SMUDGE_NONE; + cellptr.SmudgeData = 0; + if (!cellptr.IsFlagged) { + cellptr.Owner = HOUSE_NONE; + } + cellptr.Redraw_Objects(); + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * * + * This routine is used by the scenario loader to read the smudge data in an INI file and * + * create the appropriate smudge objects on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Sets the smudge data value as well. * + *=============================================================================================*/ +void SmudgeClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; // Working string staging buffer. + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + SmudgeType smudge; // Smudge type. + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + smudge = SmudgeTypeClass::From_Name(strtok(buf, ",")); + if (smudge != SMUDGE_NONE) { + char * ptr = strtok(NULL, ","); + if (ptr != NULL) { + int data = 0; + CELL cell = atoi(ptr); + ptr = strtok(NULL, ","); + if (ptr != NULL) data = atoi(ptr); + new SmudgeClass(smudge, Cell_Coord(cell)); + if (Map[cell].Smudge == smudge && data != 0) { + Map[cell].SmudgeData = data; + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * * + * This routine will output all the smudge data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing template data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Find all templates and write them to the file. + */ + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass const * stype = &SmudgeTypeClass::As_Reference(ptr->Smudge); + if (!stype->IsBib) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%d,%d", stype->IniName, index, ptr->SmudgeData); + ini.Put_String(INI_Name(), uname, buf); + } + } + } +} \ No newline at end of file diff --git a/REDALERT/SMUDGE.H b/REDALERT/SMUDGE.H new file mode 100644 index 000000000..d75b4b86f --- /dev/null +++ b/REDALERT/SMUDGE.H @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SMUDGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SMUDGE_H +#define SMUDGE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This is the transitory form for smudges. They exist as independent objects +** only in the transition stage from creation to placement upon the map. Once +** they are placed on the map, they merely become 'smudges' in the cell data. This +** object is then destroyed. +*/ +class SmudgeClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + SmudgeClass(SmudgeType type, COORDINATE pos=0xFFFFFFFFUL, HousesType house = HOUSE_NONE); + SmudgeClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + operator SmudgeType(void) const {return Class->Type;}; + virtual ~SmudgeClass(void) {if (GameActive) SmudgeClass::Limbo();Class=0;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "SMUDGE";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual bool Mark(MarkType); + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + void Disown(CELL cell); + + private: + + static HousesType ToOwn; + + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/SOUNDDLG.CPP b/REDALERT/SOUNDDLG.CPP new file mode 100644 index 000000000..c46a67f82 --- /dev/null +++ b/REDALERT/SOUNDDLG.CPP @@ -0,0 +1,455 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SOUNDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SOUNDDLG.CPP * + * * + * Programmer : Maria del Mar McCready-Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "sounddlg.h" + +class MusicListClass : public ListClass +{ + public: + MusicListClass(int id, int x, int y, int w, int h) : + ListClass(id, x, y, w, h, TPF_6PT_GRAD|TPF_NOSHADOW, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")) + {}; + virtual ~MusicListClass(void) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; + + +/*********************************************************************************************** + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void SoundControlsClass::Process(void) +{ + + /* + ** Adjust dialog controls for resolution + */ + int option_width= OPTION_WIDTH * RESFACTOR; + int option_height= OPTION_HEIGHT * RESFACTOR; + + int option_x= OPTION_X * RESFACTOR; + int option_y= OPTION_Y * RESFACTOR; + + int listbox_x= LISTBOX_X * RESFACTOR; + int listbox_y= LISTBOX_Y * RESFACTOR; + int listbox_w= LISTBOX_W * RESFACTOR; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + int listbox_h= (LISTBOX_H * RESFACTOR)+2; +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif + + int button_width= BUTTON_WIDTH * RESFACTOR; + int button_x= BUTTON_X * RESFACTOR; + int button_y= BUTTON_Y * RESFACTOR; + + int stop_x= STOP_X * RESFACTOR; + int stop_y= STOP_Y * RESFACTOR; + + int play_x= PLAY_X * RESFACTOR; + int play_y= PLAY_Y * RESFACTOR; + + int onoff_width= ONOFF_WIDTH * RESFACTOR; + int shuffle_x= SHUFFLE_X * RESFACTOR; + int shuffle_y= SHUFFLE_Y * RESFACTOR; + int repeat_x= REPEAT_X * RESFACTOR; + int repeat_y= REPEAT_Y * RESFACTOR; + + int mslider_x= MSLIDER_X * RESFACTOR; + int mslider_y= MSLIDER_Y * RESFACTOR; + int mslider_w= MSLIDER_W * RESFACTOR; + int mslider_height= MSLIDER_HEIGHT * RESFACTOR; + + int fxslider_x= FXSLIDER_X * RESFACTOR; + int fxslider_y= FXSLIDER_Y * RESFACTOR; + int fxslider_w= FXSLIDER_W * RESFACTOR; + int fxslider_height= FXSLIDER_HEIGHT * RESFACTOR; + + int button_stop= BUTTON_STOP; + int button_play= BUTTON_PLAY; + int button_shuffle= BUTTON_SHUFFLE; + int button_repeat= BUTTON_REPEAT; + int button_options= BUTTON_OPTIONS; + int slider_music= SLIDER_MUSIC; + int slider_sound= SLIDER_SOUND; + int button_listbox= BUTTON_LISTBOX; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); +// ThemeType theme; + + /* + ** List box that holds the score text strings. + */ + MusicListClass listbox(0, option_x+listbox_x, option_y+listbox_y, listbox_w, listbox_h); + + /* + ** Return to options menu button. + */ + TextButtonClass returnto(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, option_x+button_x, option_y+button_y, button_width); +// TextButtonClass returnto(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, + + /* + ** Stop playing button. + */ + ShapeButtonClass stopbtn(BUTTON_STOP, MFCD::Retrieve("BTN-ST.SHP"), option_x+stop_x, option_y+stop_y); + + /* + ** Start playing button. + */ + ShapeButtonClass playbtn(BUTTON_PLAY, MFCD::Retrieve("BTN-PL.SHP"), option_x+play_x, option_y+play_y); + + /* + ** Shuffle control. + */ + TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, onoff_width); +// TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, ONOFF_WIDTH); + + /* + ** Repeat control. + */ + TextButtonClass repeatbtn(BUTTON_REPEAT, TXT_OFF, TPF_BUTTON, option_x+repeat_x, option_y+repeat_y, onoff_width); + + /* + ** Music volume slider. + */ + SliderClass music(SLIDER_MUSIC, option_x+mslider_x, option_y+mslider_y, mslider_w, mslider_height, true); + + /* + ** Sound volume slider. + */ + SliderClass sound(SLIDER_SOUND, option_x+fxslider_x, option_y+fxslider_y, fxslider_w, fxslider_height, true); + + /* + ** Causes left mouse clicks inside the dialog area, but not on any + ** particular button, to be ignored. + */ + GadgetClass area(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + + /* + ** Causes right clicks anywhere or left clicks outside of the dialog + ** box area to be the same a clicking the return to game options button. + */ + ControlClass ctrl(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::RIGHTPRESS|GadgetClass::LEFTPRESS); + + /* + ** The repeat and shuffle buttons are of the toggle type. They toggle + ** between saying "on" and "off". + */ + shufflebtn.IsToggleType = true; + if (Options.IsScoreShuffle) { + shufflebtn.Turn_On(); + } else { + shufflebtn.Turn_Off(); + } + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + + repeatbtn.IsToggleType = true; + if (Options.IsScoreRepeat) { + repeatbtn.Turn_On(); + } else { + repeatbtn.Turn_Off(); + } + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + + /* + ** Set the initial values of the sliders. + */ + music.Set_Maximum(255); + music.Set_Thumb_Size(16); + music.Set_Value(Options.ScoreVolume * 256); + sound.Set_Maximum(255); + sound.Set_Thumb_Size(16); + sound.Set_Value(Options.Volume * 256); + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. + */ + GadgetClass * optionsbtn = &returnto; + listbox.Add_Tail(*optionsbtn); + stopbtn.Add_Tail(*optionsbtn); + playbtn.Add_Tail(*optionsbtn); + shufflebtn.Add_Tail(*optionsbtn); + repeatbtn.Add_Tail(*optionsbtn); + music.Add_Tail(*optionsbtn); + sound.Add_Tail(*optionsbtn); + area.Add_Tail(*optionsbtn); + ctrl.Add_Tail(*optionsbtn); + + /* + ** Add all the themes to the list box. The list box entries are constructed + ** and then stored into allocated EMS memory blocks. + */ + for (ThemeType index = THEME_FIRST; index < Theme.Max_Themes(); index++) { + if (Theme.Is_Allowed(index)) { + char buffer[100]; + int length = Theme.Track_Length(index); + char const * fullname = Theme.Full_Name(index); + + void * ptr = new char [sizeof(buffer)]; + if (ptr) { + sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s", index, listbox.Count()+1, length / 60, length % 60, fullname); + listbox.Add_Item((char const *)ptr); + } + + if (Theme.What_Is_Playing() == index) { + listbox.Set_Selected_Index(listbox.Count()-1); + } + } + } + static int _tabs[] = { + 55 * RESFACTOR, 72 * RESFACTOR, 90 * RESFACTOR + }; + listbox.Set_Tabs(_tabs); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(option_x, option_y, option_width, option_height); + + Draw_Caption(TXT_SOUND_CONTROLS, option_x, option_y, option_width); + + /* + ** Draw the Music, Speech & Sound titles. + */ + Fancy_Text_Print(TXT_MUSIC_VOLUME, option_x+mslider_x-(5 * RESFACTOR), option_y+mslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + Fancy_Text_Print(TXT_SOUND_VOLUME, option_x+fxslider_x-(5 * RESFACTOR), option_y+fxslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + +#if defined(GERMAN) || defined(FRENCH) + Fancy_Text_Print(TXT_SHUFFLE, option_x+4+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#else + Fancy_Text_Print(TXT_SHUFFLE, option_x+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#endif + Fancy_Text_Print(TXT_REPEAT, option_x+repeat_x-(5 * RESFACTOR), option_y+repeat_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + optionsbtn->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = optionsbtn->Input(); + + /* + ** Process Input. + */ + switch (input) { + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + process = false; + break; + + /* + ** Control music volume. + */ + case SLIDER_MUSIC|KN_BUTTON: + Options.Set_Score_Volume(fixed(music.Get_Value(), 256), true); +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + Options.MultiScoreVolume = Options.ScoreVolume; +#endif + break; + + /* + ** Control sound volume. + */ + case SLIDER_SOUND|KN_BUTTON: + Options.Set_Sound_Volume(fixed(sound.Get_Value(), 256), true); + break; + + case BUTTON_LISTBOX|KN_BUTTON: + break; + + /* + ** Stop all themes from playing. + */ + case BUTTON_STOP|KN_BUTTON: + Theme.Stop(); + Theme.Queue_Song(THEME_QUIET); +// Theme.Queue_Song(THEME_NONE); + break; + + /* + ** Start the currently selected theme to play. + */ + case KN_SPACE: + case BUTTON_PLAY|KN_BUTTON: + Theme.Queue_Song( (ThemeType)*((unsigned char *)listbox.Current_Item()) ); + break; + + /* + ** Toggle the shuffle button. + */ + case BUTTON_SHUFFLE|KN_BUTTON: + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Shuffle(shufflebtn.IsOn); + break; + + /* + ** Toggle the repeat button. + */ + case BUTTON_REPEAT|KN_BUTTON: + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Repeat(repeatbtn.IsOn); + break; + } + } + + /* + ** If the score volume was turned all the way down, then actually + ** stop the scores from being played. + */ + if (Options.ScoreVolume == 0) { + Theme.Stop(); + } + + /* + ** Free the items from the list box. + */ + while (listbox.Count()) { + char const * ptr = listbox.Get_Item(0); + listbox.Remove_Item(ptr); + delete [] (void*)ptr; + } +} + + +/*********************************************************************************************** + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * * + * This routine will display the score line in a list box. It overrides the list box * + * handler for line drawing. * + * * + * INPUT: index -- The index within the list box that is being drawn. * + * * + * x,y -- The pixel coordinates of the upper left position of the line. * + * * + * width -- The width of the line that drawing is allowed to use. * + * * + * selected-- Is the current line selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void MusicListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, + GadgetClass::Get_Color_Scheme()->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, scheme, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, TextFlags, width, Tabs); + } +} diff --git a/REDALERT/SOUNDDLG.H b/REDALERT/SOUNDDLG.H new file mode 100644 index 000000000..f7c800b00 --- /dev/null +++ b/REDALERT/SOUNDDLG.H @@ -0,0 +1,112 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SOUNDDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUNDDLG_H +#define SOUNDDLG_H + +#include "gadget.h" + +class SoundControlsClass +{ + enum SoundControlsClassEnums { +#ifdef FRENCH + OPTION_WIDTH=308, +#else + OPTION_WIDTH=292, +#endif + OPTION_HEIGHT=146, + + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + + LISTBOX_X=17, + LISTBOX_Y=54, + LISTBOX_W=OPTION_WIDTH-(LISTBOX_X*2), + LISTBOX_H=72, + + BUTTON_WIDTH=70, + BUTTON_X=OPTION_WIDTH-(BUTTON_WIDTH+17), // Options button x pos + BUTTON_Y=128, // Options button y pos + + STOP_X=17, // Stop button X. + STOP_Y=128, // Stop button Y. + + PLAY_X=35, + PLAY_Y=128, + + ONOFF_WIDTH=25, +#ifdef GERMAN + SHUFFLE_X=79,//BGA:91, +#else +#ifdef FRENCH + SHUFFLE_X=99, +#else + SHUFFLE_X=97, +#endif +#endif + SHUFFLE_Y=128, + +#ifdef FRENCH + REPEAT_X=169, +#else + REPEAT_X=164, +#endif + REPEAT_Y=128, + + MSLIDER_X=147, + MSLIDER_Y=28, + MSLIDER_W=108, + MSLIDER_HEIGHT=5, + + FXSLIDER_X=147, + FXSLIDER_Y=40, + FXSLIDER_W=108, + FXSLIDER_HEIGHT=5, + + BUTTON_STOP = 605, + BUTTON_PLAY, + BUTTON_SHUFFLE, + BUTTON_REPEAT, + BUTTON_OPTIONS, + SLIDER_MUSIC, + SLIDER_SOUND, + BUTTON_LISTBOX, + }; + + public: + SoundControlsClass(void) {} + void Process(void); +}; + +#endif diff --git a/REDALERT/SPECIAL.CPP b/REDALERT/SPECIAL.CPP new file mode 100644 index 000000000..42163b6dc --- /dev/null +++ b/REDALERT/SPECIAL.CPP @@ -0,0 +1,598 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPECIAL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/27/95 * + * * + * Last Update : August 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * Fetch_Password -- Prompts for a password entry from client. * + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * Special_Dialog -- Handles the special options dialog. * + * SpecialClass::Init -- Initialize the special class of options. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define OPTION_WIDTH 236*2 +#define OPTION_HEIGHT 162*2 +#define OPTION_X ((640 - OPTION_WIDTH) / 2) +#define OPTION_Y (400 - OPTION_HEIGHT) / 2 +#else +#define OPTION_WIDTH 236 +#define OPTION_HEIGHT 162 +#define OPTION_X ((320 - OPTION_WIDTH) / 2) +#define OPTION_Y (200 - OPTION_HEIGHT) / 2 +#endif + + +/*********************************************************************************************** + * SpecialClass::Init -- Initialize the special class of options. * + * * + * This initialization function is required (as opposed to using a constructor) because * + * the SpecialClass is declared as part of a union. A union cannot have a member with a * + * constructor. Other than this anomoly, the function serves the same purpose as a * + * normal constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/20/1996 JLB : Created. * + *=============================================================================================*/ +void SpecialClass::Init(void) +{ + IsShadowGrow = false; + IsSpeedBuild = false; + IsFromInstall = false; + IsCaptureTheFlag = false; + IsInert = false; + IsThreePoint = false; + IsTGrowth = true; + IsTSpread = true; + UseMCVDeploy = false; + IsMCVDeploy = false; + IsEarlyWin = false; +} + + +/*********************************************************************************************** + * Special_Dialog -- Handles the special options dialog. * + * * + * This dialog is used when setting the special game options. It does not appear in the * + * final version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void Special_Dialog(bool simple) +{ + simple; +#if (0)//PG + SpecialClass oldspecial = Special; + GadgetClass * buttons = NULL; + static struct { + int Description; + int Setting; + CheckBoxClass * Button; + } _options[] = { + {TXT_THREE_POINT, 0, 0}, + {TXT_SPEED_BUILD, 0, 0}, + }; + + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+15*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-60*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + buttons = &ok; + cancel.Add(*buttons); + + for (int index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + _options[index].Button = new CheckBoxClass(100+index, OPTION_X+17*RESFACTOR, OPTION_Y+20*RESFACTOR+(index*10*RESFACTOR)); + if (_options[index].Button) { + _options[index].Button->Add(*buttons); + + bool value = false; + switch (_options[index].Description) { + case TXT_THREE_POINT: + value = Special.IsThreePoint; + break; + + case TXT_SPEED_BUILD: + value = Special.IsSpeedBuild; + break; + } + + _options[index].Setting = value; + if (value) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + while (process) { + + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Draw_Caption(TXT_SPECIAL_OPTIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + Fancy_Text_Print(_options[index].Description, _options[index].Button->X+10*RESFACTOR, _options[index].Button->Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_ESC: + case 200|KN_BUTTON: + process = false; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + bool setting = _options[index].Setting; + switch (_options[index].Description) { + case TXT_THREE_POINT: + oldspecial.IsThreePoint = setting; + break; + + case TXT_SPEED_BUILD: + oldspecial.IsSpeedBuild = setting; + break; + } + } + if (!simple) { + OutList.Add(EventClass(oldspecial)); + } else { + Special = oldspecial; + } + break; + + case 201|KN_BUTTON: + process = false; + break; + + case KN_NONE: + break; + + default: + index = (input & ~KN_BUTTON) - 100; + if ((unsigned)index < sizeof(_options)/sizeof(_options[0])) { + _options[index].Setting = _options[index].Button->IsOn; + } + break; + } + } + + if (!simple) { + Map.Revert_Mouse_Shape(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +#endif +} + + +/* +** Derived from the edit class, this class allows entry of passwords style text +** as an edit box. This style is characterized by "*" being displayed for every +** real character entered. +*/ +class PWEditClass : public EditClass +{ + public: + PWEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1) : + EditClass(id, text, max_len, flags, x, y, w, h, ALPHANUMERIC) {}; + + protected: + virtual void Draw_Text(char const * text); +}; + + +/*********************************************************************************************** + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * * + * This routine is used by the password style edit box in order to display the entered * + * text. The text will be displayed as asterisks instead of the actual characters the * + * edit box may contain. This is necessary to obscure the password entry from glancing * + * eyes. * + * * + * INPUT: text -- Pointer to the text that is to be rendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +void PWEditClass::Draw_Text(char const * text) +{ + char buffer[80]; + + memset(buffer, '\0', sizeof(buffer)); + memset(buffer, '*', strlen(text)); + + if (FontPtr == GradFont6Ptr) { + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && (int)strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(buffer), Y+1, Color, TBLACK, TextFlags | flags); + } + } else { + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Has_Focus() ? + &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY], + TBLACK, TextFlags, Width-2); + + if (Has_Focus() && (int)strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print("_", X+1+String_Pixel_Width(buffer), Y+1, + &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TextFlags); + } + } +} + + +/*********************************************************************************************** + * Fetch_Password -- Prompts for a password entry from client. * + * * + * This routine will prompt for and return a password entry from the player. * + * * + * INPUT: caption -- The caption to use for the top of the prompt dialog. * + * * + * message -- The body of the message to display in the dialog. * + * * + * btext -- The button text to use to finish the dialog box entry. * + * * + * OUTPUT: Returns with a pointer to the password text entered. This pointer is valid * + * only until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +#define BUFFSIZE (511) +char const * Fetch_Password(int caption, int message, int btext) +{ + char buffer[BUFFSIZE]; + bool process; // loop while true + KeyNumType input; // user input + bool pressed; + int curbutton; + TextButtonClass ok; + + if (btext == TXT_NONE) btext = TXT_OK; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2*RESFACTOR; + bwidth = max((String_Pixel_Width(Text_String(btext)) + 8*RESFACTOR), 30*RESFACTOR); + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, Text_String(message), BUFFSIZE-1); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, 255, width, height); + + width = max(width, 50*RESFACTOR); + width += 40*RESFACTOR; + height += (60+25)*RESFACTOR; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Create the "ok" and password edit buttons. + */ + TextButtonClass button1(1, btext, TPF_BUTTON, + x + ((width - bwidth) >> 1), y + height - (bheight + 5*RESFACTOR), bwidth); + + static char pbuffer[45]; + memset(pbuffer, '\0', sizeof(pbuffer)); + int editx = x+26*RESFACTOR; + int editwidth = (SeenBuff.Get_Width()/2 - editx) * 2; + PWEditClass button2(2, &pbuffer[0], sizeof(pbuffer), TPF_6PT_GRAD|TPF_NOSHADOW, editx, (y+height)-35*RESFACTOR, editwidth, 10*RESFACTOR); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + buttonlist = &button1; + button2.Add(*buttonlist); + + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, width, height); + Draw_Caption(caption, x, y, width); + + /* + ** Draw the body of the message box. + */ + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + process = true; + pressed = false; + bool first = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + process = false; + break; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + if (first) { + button2.Set_Focus(); + button2.Flag_To_Redraw(); + first = false; + } + switch (input) { + case (1|BUTTON_FLAG): + process = false; + break; + + case (KN_ESC): + case (2|BUTTON_FLAG): + process = false; + break; + + case (KN_RETURN): + process = false; + break; + + default: + break; + } + } + + return(pbuffer); +} + + +/*********************************************************************************************** + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * * + * This will display a dialog box that requests the player to specify a difficulty * + * setting. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a difficulty setting of 0 for easiest and 4 for hardest. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath) +#else +int Fetch_Difficulty(void) +#endif +{ + int const w = 250 * RESFACTOR; + int const h = 80 * RESFACTOR; + int const x = ((320 * RESFACTOR)/2) - w/2; + int const y = ((200 * RESFACTOR)/2) - h/2; + int const bwidth = 30 * RESFACTOR; + + /* + ** Fill the description buffer with the description text. Break + ** the text into appropriate spacing. + */ + char buffer[512]; + strncpy(buffer, Text_String(TXT_DIFFICULTY), sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If it's an aftermath mission, trim the sentence to get rid of the campaign stuff. + if (amath) { + int index=0; + while (buffer[index] && buffer[index]!='.') index++; + if (buffer[index]=='.') { + buffer[index+1]=0; + } + } +#endif + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, w-60*RESFACTOR, width, height); + + /* + ** Create the OK button. + */ + TextButtonClass okbutton(1, TXT_OK, TPF_BUTTON, (x+w) - (bwidth+20*RESFACTOR) , (y+h) - (18*RESFACTOR), bwidth); + GadgetClass * buttonlist = &okbutton; + + /* + ** Create the slider button. + */ + SliderClass slider(2, x+20*RESFACTOR, y+h - 29*RESFACTOR, w - 40*RESFACTOR, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + slider.Set_Maximum(5); + slider.Set_Value(2); + } else { + slider.Set_Maximum(3); + slider.Set_Value(1); + } + slider.Add(*buttonlist); + + /* + ** Main Processing Loop. + */ + Set_Logic_Page(SeenBuff); + bool redraw = true; + bool process = true; + while (process) { + + if (redraw) { + redraw = false; + + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Dialog_Box(x, y, w, h); + + /* + ** Draw the body of the message. + */ +// Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Display the descripton of the slider range. + */ + Fancy_Text_Print(TXT_HARD, slider.X+slider.Width, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_RIGHT|TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_EASY, slider.X, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_NORMAL, slider.X + (slider.Width/2), slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + redraw = true; + continue; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); + + switch (input) { + case KN_RETURN: + case (1|BUTTON_FLAG): + process = false; + break; + + default: + break; + } + } + + return(slider.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2)); +} diff --git a/REDALERT/SPECIAL.H b/REDALERT/SPECIAL.H new file mode 100644 index 000000000..f080f762e --- /dev/null +++ b/REDALERT/SPECIAL.H @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPECIAL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/95 * + * * + * Last Update : February 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +class SpecialClass +{ + public: + + /* + ** This initializes all members just like a constructor. A constructor + ** cannot be used for this class because it is part of a union. + */ + void Init(void); + + /* + ** If the shroud should regenerated, then this flag will be true. + */ + unsigned IsShadowGrow:1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild:1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall:1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag:1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert:1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint:1; + + /* + ** If Tiberium is allowed to spread and grow, then these flags will be true. + ** These are duplicated from the rules.ini file and also controlled by the + ** multiplayer dialog box. + */ + unsigned IsTGrowth:1; + unsigned IsTSpread:1; + + /* + ** Flag that we were spawned from WChat. + */ + unsigned IsFromWChat:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + ** Used to override the rules setting. + */ + unsigned UseMCVDeploy:1; + unsigned IsMCVDeploy:1; + + /* + ** New anti-griefing early win mode. ST - 1/31/2020 3:42PM + */ + unsigned IsEarlyWin:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[128]; +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/SPRITE.CPP b/REDALERT/SPRITE.CPP new file mode 100644 index 000000000..045a10284 --- /dev/null +++ b/REDALERT/SPRITE.CPP @@ -0,0 +1,326 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPRITE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/********************************************************************** + + Sprite.cpp + + Dec 28,1995 + + GraphicBufferClass member functions for blitting, scaling, + and rotating bitmaps + +**********************************************************************/ + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +//#include "function.h" +#define FILE_H +#define RAWFILE_H +#define WWMEM_H +#define WWFILE_Hx +#include +#include +//#include "gbuffer.h" +//#include "math.h" + + +long _SineTab[256] = { +0 ,6 ,12 , 18 , 25 , 31 , 37 , 43 , 49 , 56 , 62 , 68 , 74 , 80 , 86 , +92 , 97 , 103 , 109 , 115 , 120 , 126 , 131 , 136 , 142 , 147 , 152 , +157 , 162 , 167 , 171 , 176 , 180 , 185 , 189 , 193 , 197 , 201 , 205 , +209 , 212 , 216 , 219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , +243 , 244 , 246 , 248 , 249 , 251 , 252 , 253 , 254 , 254 , 255 , 255 , +255 , 255 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , +246 , 245 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , +216 , 213 , 209 , 205 , 201 , 198 , 194 , 189 , 185 , 181 , 176 , 172 , +167 , 162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , +104 , 98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 44 , 37 , 31 , 25 , +19 , 12 , 6 , 0 , -5 , -12 , -18 , -24 , -30 , -37 , -43 , -49 , -55 , +-61 , -67 , -73 , -79 , -85 , -91 , -97 , -103 , -109 , -114 , -120 , +-125 , -131 , -136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , +-176 , -180 , -185 , -189 , -193 , -197 , -201 , -205 , -208 , -212 , +-215 , -219 , -222 , -225 , -228 , -231 , -233 , -236 , -238 , -240 , +-242 , -244 , -246 , -248 , -249 , -250 , -252 , -253 , -254 , -254 , +-255 , -255 , -255 , -255 , -255 , -255 , -255 , -254 , -254 , -253 , +-252 , -251 , -249 , -248 , -246 , -245 , -243 , -241 , -239 , -236 , +-234 , -231 , -228 , -226 , -223 , -219 , -216 , -213 , -209 , -206 , +-202 , -198 , -194 , -190 , -185 , -181 , -177 , -172 , -167 , -162 , +-158 , -153 , -148 , -142 , -137 , -132 , -126 , -121 , -115 , -110 , +-104 , -98 , -92 , -86 , -81 , -75 , -69 , -62 , -56 , -50 , -44 , +-38 , -32 , -25 , -19 , -13 , -7 , +}; + +long _CosineTab[256] = { +256 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , 246 , +244 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , 216 , +212 , 209 , 205 , 201 , 197 , 193 , 189 , 185 , 181 , 176 , 171 , 167 , +162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , 103 , +98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 43 , 37 , 31 , 25 , 19 , +12 , 6 , 0 , -6 , -12 , -18 , -24 , -31 , -37 , -43 , -49 , -55 , -61 , +-68 , -74 , -80 , -86 , -91 , -97 , -103 , -109 , -114 , -120 , -125 , -131 , +-136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , -176 , -180 , +-185 , -189 , -193 , -197 , -201 , -205 , -209 , -212 , -216 , -219 , -222 , +-225 , -228 , -231 , -233 , -236 , -238 , -240 , -242 , -244 , -246 , +-248 , -249 , -251 , -252 , -253 , -254 , -254 , -255 , -255 , -255 , +-255 , -255 , -255 , -255 , -254 , -254 , -253 , -252 , -251 , -249 , -248 , +-246 , -245 , -243 , -241 , -239 , -236 , -234 , -231 , -228 , -225 , +-222 , -219 , -216 , -213 , -209 , -205 , -202 , -198 , -194 , -190 , -185 , +-181 , -176 , -172 , -167 , -162 , -157 , -152 , -147 , -142 , -137 , -132 , +-126 , -121 , -115 , -109 , -104 , -98 , -92 , -86 , -80 , -74 , -68 , +-62 , -56 , -50 , -44 , -38 , -31 , -25 , -19 , -13 , -6 , 0 , 5 , 11 , +18 , 24 , 30 , 36 , 43 , 49 , 55 , 61 , 67 , 73 , 79 , 85 , 91 , 97 , 103 , +108 , 114 , 120 , 125 , 131 , 136 , 141 , 146 , 151 , 156 , 161 , 166 , +171 , 176 , 180 , 184 , 189 , 193 , 197 , 201 , 205 , 208 , 212 , 215 , +219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , 242 , 244 , 246 , +248 , 249 , 250 , 252 , 253 , 253 , 254 , 255 , 255 , 255 , +}; + + + +/*************************************************************** +* +* Scale_Rotate +* +* FUNCTION: +* +* Using Bi-Linear Interpolation, draws a scaled and rotated +* bitmap onto the buffer. No clipping is performed so beware. +* +* INPUTS +* +* bmp - bitmap to draw +* pt - desired position of the center +* scale - 24.8 fixed point scale factor +* angle - 8bit angle (0=0deg, 255=360deg) +* +***************************************************************/ + +void GraphicBufferClass::Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle) +{ + unsigned int scrpos; + unsigned int temp; + + int i,j; // counter vars + int pxerror =0; // these three vars will be used in an + int pyerror =0; // integer difference alg to keep track + int pixpos =0; // of what pixel to draw. + unsigned char pixel; + + TPoint2D p0; // "upper left" corner of the rectangle + TPoint2D p1; // "upper right" corner of the rectangle + TPoint2D p2; // "lower left" corner of the rectangle + + /*------------------------------------------------- + Compute three corner points of the rectangle + -------------------------------------------------*/ + { + angle &= 0x0FF; + long c = _CosineTab[angle]; + long s = _SineTab[angle]; + long W = (scale*bmp.Width)>>1; + long L = (scale*bmp.Height)>>1; + + p0.x = (pt.x + ((( (L*c) >> 8)-((W*s) >> 8)) >> 8)); + p0.y = (pt.y + (((-(L*s) >> 8)-((W*c) >> 8)) >> 8)); + p1.x = (pt.x + ((( (L*c) >> 8)+((W*s) >> 8)) >> 8)); + p1.y = (pt.y + (((-(L*s) >> 8)+((W*c) >> 8)) >> 8)); + p2.x = (pt.x + (((-(L*c) >> 8)-((W*s) >> 8)) >> 8)); + p2.y = (pt.y + ((( (L*s) >> 8)-((W*c) >> 8)) >> 8)); + } + + /*----------------------------------- + Initialize Breshnam constants + -----------------------------------*/ + + // This breshnam line goes across the FRONT of the rectangle + // In the bitmap, this will step from left to right + + int f_deltax =(p1.x-p0.x); + int f_deltay =(p1.y-p0.y); + int f_error =0; + int f_xstep =1; + int f_ystep =Width; + + // This breshnam line goes down the SIDE of the rectangle + // In the bitmap, this line will step from top to bottom + + int s_deltax =(p2.x-p0.x); + int s_deltay =(p2.y-p0.y); + int s_error =0; + int s_xstep =1; + int s_ystep =Width; + + /*-------------------------------- + fixup deltas and step values + --------------------------------*/ + + if (f_deltay<0) { + f_deltay=-f_deltay; + f_ystep=-Width; + }; + + if (f_deltax<0) { + f_deltax=-f_deltax; + f_xstep=-1; + }; + + if (s_deltay<0) { + s_deltay=-s_deltay; + s_ystep=-Width; + }; + + if (s_deltax<0) { + s_deltax=-s_deltax; + s_xstep=-1; + }; + + scrpos=p0.x+Width*p0.y; //address of initial screen pos. + temp=scrpos; + + /*--------------------------------------------------------------------- + Now all of the differences, errors, and steps are set up so we can + begin drawing the bitmap... + + There are two cases here, + 1 - the "Front" line has a slope of < 1.0 (45 degrees) + 2 - the "Front" line has a slope of >= 1.0 + + For case 1, we step along the X direction, for case 2, step in y + ---------------------------------------------------------------------*/ + + if (f_deltax>f_deltay) { //CASE 1, step front in X, side in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltax) + { + pixpos++; + pxerror-=f_deltax; + }; + scrpos+=f_xstep; //step to next screen pos + f_error+=f_deltay; + if (f_error>f_deltax) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltax; + scrpos+=f_ystep; + }; + }; + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + + while (pyerror>s_deltay) + { + pixpos+=bmp.Width; + pyerror-=s_deltay; + }; + + f_error=0; + scrpos=temp; + scrpos+=s_ystep; + s_error+=s_deltax; + + if (s_error>s_deltay) + { + s_error-=s_deltay; + scrpos+=s_xstep; + }; + } + + } else { // CASE 2, Step front line in X, side line in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltay) + { + pixpos++; + pxerror-=f_deltay; + }; + + scrpos+=f_ystep; //step to next screen pos + f_error+=f_deltax; + if (f_error>f_deltay) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltay; + scrpos+=f_xstep; + }; + }; + + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + while (pyerror>s_deltax) + { + pixpos+=bmp.Width; + pyerror-=s_deltax; + }; + + scrpos=temp; + scrpos+=s_xstep; + s_error+=s_deltay; + f_error=0; + if (s_error>s_deltax) + { + s_error-=s_deltax; + scrpos+=s_ystep; + }; + } + } +} \ No newline at end of file diff --git a/REDALERT/STAGE.H b/REDALERT/STAGE.H new file mode 100644 index 000000000..1103c39a8 --- /dev/null +++ b/REDALERT/STAGE.H @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STAGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STAGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 17, 1994 * + * * + * Last Update : June 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STAGE_H +#define STAGE_H + +class StageClass { + + /* + ** This handles the animation stage of the object. This includes smoke, walking, + ** flapping, and rocket flames. + */ + unsigned Stage; + + /* + ** This is the countdown timer for stage animation. When this counts down + ** to zero, then the stage increments by one and the time cycle starts + ** over again. + */ + CDTimerClass Timer; + + /* + ** This is the value to assign the StageTimer whenever it needs to be reset. Thus, + ** this value is the control of how fast the stage value increments. + */ + int Rate; + + public: + StageClass(void) : Stage(0), Timer(0), Rate(0) {}; + StageClass(NoInitClass const & x) : Timer(x) {}; + + int Fetch_Stage(void) const {return(Stage);}; + int Fetch_Rate(void) const {return(Rate);}; + void Set_Stage(int stage) {Stage = stage;}; + void Set_Rate(int rate) {Timer = rate; Rate = rate;}; + void AI(void) {}; + bool About_To_Change(void) const {return(Timer == 0 && Rate != 0);} + bool Graphic_Logic(void) { + if (About_To_Change()) { + Stage++; + Timer = Rate; + return(true); + } + return(false); + }; +}; + + +#endif diff --git a/REDALERT/STARTUP.CPP b/REDALERT/STARTUP.CPP new file mode 100644 index 000000000..3fdf51626 --- /dev/null +++ b/REDALERT/STARTUP.CPP @@ -0,0 +1,1213 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/STARTUP.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 3, 1994 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Prog_End -- Cleans up library systems in prep for game exit. * + * main -- Initial startup routine (preps library systems). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +#ifdef WIN32 +#include "ccdde.h" +#include "ipx95.h" +#endif //WIN32 + +#ifdef MCIMPEG // Denzil 6/15/98 +#include "mcimovie.h" +#endif + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#endif + +extern char RedAlertINI[_MAX_PATH]; + +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config); +void Print_Error_End_Exit(char * string); +void Print_Error_Exit(char * string); + +#ifdef WIN32 +//WinTimerClass * WinTimer; +extern void Create_Main_Window ( HANDLE instance , int command_show , int width , int height); +extern bool RA95AlreadyRunning; +HINSTANCE ProgramInstance; +void Check_Use_Compressed_Shapes (void); +void Read_Setup_Options(RawFileClass *config_file); +bool VideoBackBufferAllowed = true; +#else +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); +#endif //WIN32 + +const char* Game_Registry_Key(); + +#if (ENGLISH) +#define WINDOW_NAME "Red Alert" +#endif + +#if (FRENCH) +#define WINDOW_NAME "Alerte Rouge" +#endif + +#if (GERMAN) +#define WINDOW_NAME "Alarmstufe Rot" +#endif + +// Added. ST - 5/14/2019 +bool ProgEndCalled = false; + +extern "C"{ + extern char *BigShapeBufferStart; + extern char *TheaterShapeBufferStart; + extern BOOL UseBigShapeBuffer; + extern bool IsTheaterShape; +} + +extern void Free_Heaps(void); +extern void DLL_Shutdown(void); + +BOOL WINAPI DllMain(HINSTANCE instance, unsigned int fdwReason, void *lpvReserved) +{ + lpvReserved; + + switch (fdwReason) { + + case DLL_PROCESS_ATTACH: + ProgramInstance = instance; + break; + + case DLL_PROCESS_DETACH: + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + + /* + ** Red Alert doesn't clean up memory. Do some of that here. + */ + MFCD::Free_All(); + + if (BigShapeBufferStart) { + Free(BigShapeBufferStart); + BigShapeBufferStart = NULL; + } + + if (TheaterShapeBufferStart) { + Free(TheaterShapeBufferStart); + TheaterShapeBufferStart = NULL; + } + + if (_ShapeBuffer) { + delete [] _ShapeBuffer; + _ShapeBuffer = NULL; + } + + DLL_Shutdown(); + Free_Heaps(); + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + return true; +} + + + +/*********************************************************************************************** + * main -- Initial startup routine (preps library systems). * + * * + * This is the routine that is first called when the program starts up. It basically * + * handles the command line parsing and setting up library systems. * + * * + * INPUT: argc -- Number of command line arguments. * + * * + * argv -- Pointer to array of command line argument strings. * + * * + * OUTPUT: Returns with execution failure code (if any). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +//int PASCAL WinMain(HINSTANCE, HINSTANCE, char *, int ) +//PG int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char * command_line , int command_show ) +int DLL_Startup(const char * command_line_in) + +#else //WIN32 +int main(int argc, char * argv[]) +#endif //WIN32 + +{ +#ifdef WIN32 + + RunningAsDLL = true; + int command_show = SW_HIDE; + HINSTANCE instance = ProgramInstance; + char command_line[1024]; + strcpy(command_line, command_line_in); + + + + #ifndef MPEGMOVIE // Denzil 6/10/98 + //PG DDSCAPS surface_capabilities; + #endif + + /* + ** First thing to do is check if there is another instance of Red Alert already running + ** and if so, switch to the existing instance and terminate ourselves. + */ + SpawnedFromWChat = false; +#if (0)//PG + if (RA95AlreadyRunning) { //Set in the DDEServer constructor + //MessageBox (NULL, "Error - attempt to restart Red Alert 95 when already running.", "Red Alert", MB_ICONEXCLAMATION|MB_OK); + + HWND ccwindow; + ccwindow = FindWindow (WINDOW_NAME, WINDOW_NAME); + if (ccwindow) { + SetForegroundWindow ( ccwindow ); + ShowWindow ( ccwindow, SW_RESTORE ); + } + + return (EXIT_SUCCESS); + } +#endif + +#endif +//printf("in program.\n");getch(); +//printf("ram free = %ld\n",Ram_Free(MEM_NORMAL));getch(); +#ifdef WIN32 + if (Ram_Free(MEM_NORMAL) < 7000000) { +#else + + void *temp_mem = malloc (13*1024*1024); + if (temp_mem) { + free (temp_mem); + }else{ + + //if (Ram_Free(MEM_NORMAL) < 13000000) { +// if (Ram_Free(MEM_NORMAL) < 3500000) { +#endif + printf (TEXT_NO_RAM); + +#ifndef WIN32 + printf (TEXT_USE_START_MENU); + getch(); +#endif + +#if (0) + + /* + ** Take a stab at finding out how much memory there is available. + */ + + for ( int mem = 13*1024*1024 ; mem >0 ; mem -=1024 ) { + temp_mem = malloc (mem); + if (temp_mem){ + free (temp_mem); + printf ("Memory available: %d", mem); + break; + } + } + + getch(); +#endif //(0) + return(EXIT_FAILURE); + } + +#ifdef WIN32 + + if (strstr(command_line, "f:\\projects\\c&c0") != NULL || + strstr(command_line, "F:\\PROJECTS\\C&C0") != NULL) { + MessageBox(0, "Playing off of the network is not allowed.", "Red Alert", MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } + + int argc; //Command line argument count + unsigned command_scan; + char command_char; + char * argv[20]; //Pointers to command line arguments + char path_to_exe[132]; + + ProgramInstance = instance; + + /* + ** Get the full path to the .EXE + */ + GetModuleFileName (instance, &path_to_exe[0], 132); + + /* + ** First argument is supposed to be a pointer to the .EXE that is running + ** + */ + argc=1; //Set argument count to 1 + argv[0]=&path_to_exe[0]; //Set 1st command line argument to point to full path + + /* + ** Get pointers to command line arguments just like if we were in DOS + ** + ** The command line we get is cr/zero? terminated. + ** + */ + + command_scan=0; + + do { + /* + ** Scan for non-space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ) { + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + bool in_quotes = false; + do { + command_char = *( command_line+command_scan++ ); + if (command_char == '"') { + in_quotes = !in_quotes; + } + } while ( (in_quotes || command_char!=' ') && command_char != 0 && command_char!=13 ); + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + +#endif //WIN32 + + /* + ** Remember the current working directory and drive. + */ +#if (0)//PG + unsigned olddrive; + char oldpath[MAX_PATH]; + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + /* + ** Change directory to the where the executable is located. Handle the + ** case where there is no path attached to argv[0]. + */ + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + _splitpath(argv[0], drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = ('A' + olddrive)-1; + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); +#endif + +#ifdef WOLAPI_INTEGRATION + // Look for special wolapi install program, used after the patch to version 3, to install "Shared Internet Components". + WIN32_FIND_DATA wfd; + HANDLE hWOLSetupFile = FindFirstFile( "wolsetup.exe", &wfd ); + bool bWOLSetupFile = ( hWOLSetupFile != INVALID_HANDLE_VALUE ); +// if( bWOLSetupFile ) +// debugprint( "Found wolsetup.exe\n" ); + FindClose( hWOLSetupFile ); + // Look for special registry entry that tells us when the setup exe has done its thing. + HKEY hKey; + RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ); + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WolapiInstallComplete", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) == ERROR_SUCCESS ) + { +// debugprint( "Found WolapiInstallComplete in registry\n" ); + // Setup has finished. Delete the setup exe and remove reg key. + if( bWOLSetupFile ) + { + if( DeleteFile( "wolsetup.exe" ) ) + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + else + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + RegCloseKey( hKey ); + + // I've been having problems getting the patch to delete "conquer.eng", which is present in the game + // directory for 1.08, but which must NOT be present for this version (Aftermath mix files provide the + // string overrides that the 1.08 separate conquer.eng did before Aftermath). + // Delete conquer.eng if it's found. + if( FindFirstFile( "conquer.eng", &wfd ) != INVALID_HANDLE_VALUE ) + DeleteFile( "conquer.eng" ); + +#endif + + if (Parse_Command_Line(argc, argv)) { + +#if(TEN) + // + // Only allow the TEN version of the game to run if the TEN + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_TEN) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for TEN\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for TEN\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // TEN + +#if(MPATH) + // + // Only allow the MPATH version of the game to run if the MPATH + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_MPATH) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for MPATH\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for MPATH\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // MPATH + +#ifdef WIN32 + + WindowsTimer = new WinTimerClass(60, FALSE); +#if (0)//PG + int time_test = WindowsTimer->Get_System_Tick_Count(); + Sleep (1000); + if (WindowsTimer->Get_System_Tick_Count() == time_test){ + MessageBox(0, TEXT_ERROR_TIMER, TEXT_SHORT_TITLE, MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } +#endif +#else //WIN32 + Init_Timer_System(60, true); +#endif //WIN32 + + //////////////////////////////////////// + // The editor needs to load the Red Alert ini file from a different location than the real game. - 7/18/2019 JAS + char* red_alert_file_path = nullptr; + if (RunningFromEditor) + { + red_alert_file_path = RedAlertINI; + } + else + { + red_alert_file_path = CONFIG_FILE_NAME; + } + + RawFileClass cfile(red_alert_file_path); + //RawFileClass cfile(CONFIG_FILE_NAME); + // end of change - 7/18/2019 JAS + //////////////////////////////////////// + +#ifndef WIN32 + Install_Keyboard_Interrupt(Get_RM_Keyboard_Address(), Get_RM_Keyboard_Size()); +#endif //WIN32 + +#ifdef NEVER + Keyboard->IsLibrary = true; + if (Debug_Flag) { + Keyboard_Attributes_On(DEBUGINT); + } +#endif + + Keyboard = new KeyboardClass(); + +#ifdef WIN32 + /* + ** If there is loads of memory then use uncompressed shapes + */ + Check_Use_Compressed_Shapes(); +#endif + + /* + ** If there is not enough disk space free, don't allow the product to run. + */ + if (Disk_Space_Available() < INIT_FREE_DISK_SPACE) { + +#ifdef WIN32 +#if (0)//PG + char disk_space_message [512]; + sprintf(disk_space_message, TEXT_CRITICALLY_LOW); //PG , (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + int reply = MessageBox(NULL, disk_space_message, TEXT_SHORT_TITLE, MB_ICONQUESTION|MB_YESNO); + if (reply == IDNO) { + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); + } +#endif +#else + printf(TEXT_INSUFFICIENT); + printf(TEXT_MUST_HAVE, INIT_FREE_DISK_SPACE / (1024 * 1024)); + printf("\n"); + if (Keyboard) Keyboard->Get(); +// Keyboard::IsLibrary = false; + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); + return(EXIT_FAILURE); +#endif + } + + if (cfile.Is_Available()) { + + Read_Private_Config_Struct(cfile, &NewConfig); + +#ifdef WIN32 + + /* + ** Set the options as requested by the ccsetup program + */ + Read_Setup_Options( &cfile ); + + Create_Main_Window( instance , command_show , ScreenWidth , ScreenHeight ); + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); +#else //WIN32 + if (!Debug_Quiet) { + Audio_Init(NewConfig.DigitCard, + NewConfig.Port, + NewConfig.IRQ, + NewConfig.DMA, + PLAYBACK_RATE_NORMAL, +// (NewConfig.Speed) ? PLAYBACK_RATE_SLOW : PLAYBACK_RATE_NORMAL, + NewConfig.BitsPerSample, +// 4, + (Get_CPU() < 5) ? 3 : 5, +// (NewConfig.Speed) ? 3 : 5, + NewConfig.Reverse); + SoundOn = true; + } else { + Audio_Init(0, -1, -1, -1, PLAYBACK_RATE_NORMAL, 8, 5, false); + } +#endif //WIN32 + + +#ifdef WIN32 + #ifdef MPEGMOVIE // Denzil 6/10/98 + if (!InitDDraw()) + return (EXIT_FAILURE); + #else + BOOL video_success = FALSE; + /* + ** Set 640x400 video mode. If its not available then try for 640x480 + */ + if (ScreenHeight == 400) { + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)) { + video_success = TRUE; + } else { + if (Set_Video_Mode (MainWindow, ScreenWidth, 480, 8)) { + video_success = TRUE; + ScreenHeight = 480; + } + } + } else { + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)) { + video_success = TRUE; + } + } + + if (!video_success) { + MessageBox(MainWindow, TEXT_VIDEO_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + //if (Palette) delete Palette; + return (EXIT_FAILURE); + } + + if (ScreenWidth==320) { + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + ModeXBuff.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + } else { + +#if (1) //ST - 1/3/2019 2:11PM + + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + +#else + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + + /* + ** Check that we really got a video memory page. Failure is fatal. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) { + /* + ** Aaaarrgghh! + */ + WWDebugString(TEXT_DDRAW_ERROR);WWDebugString("\n"); + MessageBox(MainWindow, TEXT_DDRAW_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + return (EXIT_FAILURE); + } + + /* + ** If we have enough left then put the hidpage in video memory unless... + ** + ** If there is no blitter then we will get better performance with a system + ** memory hidpage + ** + ** Use a system memory page if the user has specified it via the ccsetup program. + */ + unsigned video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + if (video_memory < (unsigned int)(ScreenWidth*ScreenHeight) || + (! (video_capabilities & VIDEO_BLITTER)) || + (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) || + !VideoBackBufferAllowed) { + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + //HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)GBC_VIDEOMEM); + + /* + ** Make sure we really got a video memory hid page. If we didnt then things + ** will run very slowly. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) { + + /* + ** Oh dear, big trub. This must be an IBM Aptiva or something similarly cruddy. + ** We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } +#endif + } + + ScreenHeight = 3072; + + if (VisiblePage.Get_Height() == 480) { + SeenBuff.Attach(&VisiblePage, 0, 40, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 40, 3072, 3072); + } else { + SeenBuff.Attach(&VisiblePage, 0, 0, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 0, 3072, 3072); + } + #endif // MPEGMOVIE - Denzil 6/10/98 + + Options.Adjust_Variables_For_Resolution(); + + /* + ** Install the memory error handler + */ + Memory_Error = &Memory_Error_Handler; + + WindowList[0][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[0][WINDOWHEIGHT] = SeenBuff.Get_Height(); + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[WINDOW_EDITOR][WINDOWHEIGHT]= SeenBuff.Get_Height(); + + //////////////////////////////////////// + // The editor needs to not start the mouse up. - 7/22/2019 JAS + if (!RunningFromEditor) + { + WWMouse = new WWMouseClass(&SeenBuff, 48, 48); + MouseInstalled = TRUE; + } + + //PG CDFileClass::Set_CD_Drive (CDList.Get_First_CD_Drive()); + +#else //WIN32 + + Options.Adjust_Variables_For_Resolution(); + if (!Special.IsFromInstall) { + BlackPalette.Set(); +// Set_Palette(Palette); + Set_Video_Mode(MCGA_MODE); + } + MouseInstalled = Install_Mouse(32, 24, 320, 200); +#endif //WIN32 + + /* + ** See if we should run the intro + */ + INIClass ini; + ini.Load(cfile); + + /* + ** Check for forced intro movie run disabling. If the conquer + ** configuration file says "no", then don't run the intro. + ** Don't do this for TEN & MPath. + */ + if (!Special.IsFromInstall && Session.Type != GAME_TEN && + Session.Type != GAME_MPATH) { + Special.IsFromInstall = ini.Get_Bool("Intro", "PlayIntro", true); + } + SlowPalette = ini.Get_Bool("Options", "SlowPalette", false); + + /* + ** Regardless of whether we should run it or not, here we're + ** gonna change it to say "no" in the future. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + ini.Put_Bool("Intro", "PlayIntro", false); + ini.Save(cfile); + } + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** If WChat has been trying to send us a game start packet then receive it and + ** flag that we were spawned from WCHat so we dont play the into movie. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + }else{ + //Check_From_WChat("C&CSPAWN.INI"); + //if (Special.IsFromWChat){ + // DDEServer.Disable(); + //} + } +#endif //WIN32 +#endif + /* + ** If the intro is being run for the first time, then don't + ** allow breaking out of it with the key. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + } + + Memory_Error_Exit = Print_Error_End_Exit; + + Main_Game(argc, argv); + + if (RunningAsDLL) { //PG + return (EXIT_SUCCESS); + } + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if (MpgSettings != NULL) + delete MpgSettings; + + #ifdef MCIMPEG + if (MciMovie != NULL) + delete MciMovie; + #endif + #endif + +#ifdef WIN32 + VisiblePage.Clear(); + HiddenPage.Clear(); +#else //WIN32 + SeenPage.Clear(); + Set_Video_Mode(RESET_MODE); +#endif //WIN32 + Memory_Error_Exit = Print_Error_Exit; + +#ifdef WIN32 + /* + ** Flag that this is a clean shutdown (not killed with Ctrl-Alt-Del) + */ + ReadyToQuit = 1; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + }while (ReadyToQuit == 1); + + return (EXIT_SUCCESS); + +#else //WIN32 + Remove_Mouse(); + Sound_End(); +#endif //WIN32 + } else { + if (!RunningFromEditor) + { + puts(TEXT_SETUP_FIRST); + Keyboard->Get(); + } + } + +#ifdef WIN32 + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + +#else //WIN32 + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); +#endif //WIN32 + } + /* + ** Restore the current drive and directory. + */ +#ifndef WIN32 + _dos_setdrive(olddrive, &drivecount); + chdir(oldpath); +#endif //WIN32 + return(EXIT_SUCCESS); +} + + +/* Initialize DirectDraw and surfaces */ +bool InitDDraw(void) + { + + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + VisiblePage.Attach_DD_Surface(&HiddenPage); + SeenBuff.Attach(&VisiblePage, 0, 0, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 0, 3072, 3072); + +#if (0)//PG + DDSCAPS surface_capabilities; + BOOL video_success = FALSE; + + /* Set 640x400 video mode. If its not available then try for 640x480 */ + if (ScreenHeight == 400) + { + if (Set_Video_Mode(MainWindow, ScreenWidth, ScreenHeight, 8)) + { + video_success = TRUE; + } + else + { + if (Set_Video_Mode(MainWindow, ScreenWidth, 480, 8)) + { + video_success = TRUE; + ScreenHeight = 480; + } + } + } + else + { + if (Set_Video_Mode(MainWindow, ScreenWidth, ScreenHeight, 8)) + { + video_success = TRUE; + } + } + + if (!video_success) + { + MessageBox(MainWindow, TEXT_VIDEO_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + + if (WindowsTimer) + delete WindowsTimer; + + return false; + } + + if (ScreenWidth == 320) + { + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + ModeXBuff.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + } + else + { + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + + /* Check that we really got a video memory page. Failure is fatal. */ + memset(&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + /* Aaaarrgghh! */ + WWDebugString(TEXT_DDRAW_ERROR);WWDebugString("\n"); + MessageBox(MainWindow, TEXT_DDRAW_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + + if (WindowsTimer) + delete WindowsTimer; + + return false; + } + + /* If we have enough left then put the hidpage in video memory unless... + * + * If there is no blitter then we will get better performance with a system + * memory hidpage + * + * Use a system memory page if the user has specified it via the ccsetup program. + */ + unsigned video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + + if (video_memory < ScreenWidth * ScreenHeight + || (!(video_capabilities & VIDEO_BLITTER)) + || (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) + || !VideoBackBufferAllowed) + { + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + } + else + { + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + /* Make sure we really got a video memory hid page. If we didnt then things + * will run very slowly. + */ + memset(&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + /* Oh dear, big trub. This must be an IBM Aptiva or something similarly cruddy. + * We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + } + else + { + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } + } + + ScreenHeight = 400; + + if (VisiblePage.Get_Height() == 480) + { + SeenBuff.Attach(&VisiblePage, 0, 40, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 40, 3072, 3072); + } + else + { + SeenBuff.Attach(&VisiblePage, 0, 0, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 0, 3072, 3072); + } +#endif + return true; + } + + +/*********************************************************************************************** + * Prog_End -- Cleans up library systems in prep for game exit. * + * * + * This routine should be called before the game terminates. It handles cleaning up * + * library systems so that a graceful return to the host operating system is achieved. * + * * + * INPUT: why - text description of exit reason * + * fatal - indicates a fatal error * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + * 08/07/2019 ST : Added why and fatal params * + *=============================================================================================*/ +#ifdef WIN32 +void __cdecl Prog_End(const char *why, bool fatal) +{ + GlyphX_Debug_Print("Prog_End()"); + + if (why) { + GlyphX_Debug_Print(why); + } + if (fatal) { + *((int*)0) = 0; + } + + Sound_End(); + if (WWMouse) { + delete WWMouse; + WWMouse = NULL; + } + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + + ProgEndCalled = true; +} +#else //WIN32 + +void Prog_End(void) +{ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + NullModem.Change_IRQ_Priority(0); + } + + Set_Video_Mode(RESET_MODE); + Remove_Keyboard_Interrupt(); + Remove_Mouse(); + Sound_End(); + Remove_Timer_System(); +} +#endif //WIN32 + + +void Print_Error_End_Exit(char * string) +{ + Prog_End(string, true); + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + +void Print_Error_Exit(char * string) +{ + GlyphX_Debug_Print(string); + + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + + + + + + +/*********************************************************************************************** + * Emergency_Exit -- Function to call when we want to exit unexpectedly. * + * Use this function instead of exit(n) so everything is properly cleaned up.* + * * + * * + * INPUT: Code to return to the OS * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/13/97 1:32AM ST : Created * + *=============================================================================================*/ +void Emergency_Exit ( int code ) +{ + if (RunningAsDLL) { //PG + return; + } + +#ifdef WIN32 + + /* + ** Clear out the video buffers so we dont glitch when we lose focus + */ + VisiblePage.Clear(); + HiddenPage.Clear(); + BlackPalette.Set(); + Memory_Error_Exit = Print_Error_Exit; + + /* + ** Flag that this is an emergency shut down - not a clean shutdown but + ** not killed with Ctrl-Alt-Del either. + */ + ReadyToQuit = 3; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + }while (ReadyToQuit == 3); + + +#else //WIN32 + /* + ** Do the DOS end + */ + Prog_End(); + +#endif //WIN32 + + exit ( code ); +} + + + + + + + + + + + +#ifdef WIN32 + +/*********************************************************************************************** + * Read_Setup_Options -- Read stuff in from the INI file that we need to know sooner * + * * + * * + * * + * INPUT: Ptr to config file class * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 4:09PM ST : Created * + * 09/30/1996 JLB : Uses INI class. * + *=============================================================================================*/ +void Read_Setup_Options( RawFileClass *config_file ) +{ + if (config_file->Is_Available()) { + + INIClass ini; + + ini.Load(*config_file); + + /* + ** Read in the boolean options + */ + VideoBackBufferAllowed = ini.Get_Bool("Options", "VideoBackBuffer", true); + AllowHardwareBlitFills = ini.Get_Bool("Options", "HardwareFills", true); + ScreenHeight = ini.Get_Bool("Options", "Resolution", false) ? 3072 : 3072; + + /* + ** See if an alternative socket number has been specified + */ + int socket = ini.Get_Int("Options", "Socket", 0); + if (socket >0 ) { + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + } + + /* + ** See if a destination network has been specified + */ + char netbuf [512]; + memset(netbuf, 0, sizeof(netbuf)); + char * netptr = netbuf; + bool found = ini.Get_String("Options", "DestNet", NULL, netbuf, sizeof(netbuf)); + + if (found && netptr != NULL && strlen(netbuf)) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the string, pulling off each address piece + */ + int i = 0; + char * p = strtok(netbuf,"."); + int x; + while (p != NULL) { + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + } + } +} + +void Get_OS_Version (void) +{ + //PG + //WindowsNT = ((GetVersion() & 0x80000000) == 0) ? true : false; + +#if (0) + OSVERSIONINFO osversion; + if ( GetVersionEx (&osversion) ){ + WindowsNT = (osversion.dwPlatformId == VER_PLATFORM_WIN32_NT) ? true : false; + OutputDebugString ("RA95 - Got OS version\n"); + }else{ + OutputDebugString ("RA95 - Failed to get OS version\n"); + char debugstr [128]; + sprintf (debugstr, "RA95 - Error code is %d\n", GetLastError()); + OutputDebugString (debugstr); + + } +#endif //(0) + +} + + +#endif //WIN32 \ No newline at end of file diff --git a/REDALERT/STATBTN.CPP b/REDALERT/STATBTN.CPP new file mode 100644 index 000000000..94ce2fddc --- /dev/null +++ b/REDALERT/STATBTN.CPP @@ -0,0 +1,270 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STATBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "statbtn.h" + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(unsigned , char const * text, TextPrintType style, int x, int y, int w, int h) : + GadgetClass(x, y, w, h, FlagEnum(0)), + String(NULL), + PrintFlags(style) +{ + /* + ** Make a duplicate of the string to display. + */ + Set_Text(text, false); + + if (w == -1 || h == -1) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String); + } + if (h == -1) { + Height = FontHeight; + } + } +} + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(void) : + GadgetClass(0, 0, 0, 0, FlagEnum(0)), + String(NULL), + PrintFlags(TPF_8POINT) +{ +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int StaticButtonClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Set_Text(char const * text, bool resize) +{ + if (String != NULL) { + delete [] String; + String = NULL; + } + + if (text != NULL) { + String = new char[strlen(text)+1]; + if (String != NULL) { + strcpy(String, text); + } + } + + Flag_To_Redraw(); + if (resize && String != NULL) { + Draw_Background(); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String); + Height = FontHeight + FontYSpacing; + Background = Buffer(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Background(void) +{ + /* + ** If the background hasn't been recorded from the buffer, then + ** allocate and record the background image now. + */ + if (Background.Get_Buffer() == NULL && Width > 0 && Height > 0) { + new(&Background) Buffer(Width*Height); + if (Background.Get_Buffer() != NULL) { + LogicPage->To_Buffer(X, Y, Width, Height, Background, Background.Get_Size()); + } + } + + /* + ** If there is a background image present, then restore it to the buffer now. + */ + if (Background.Get_Buffer() != NULL && LogicPage->Lock()) { + Buffer_To_Page(X, Y, Width, Height, Background, *LogicPage); + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Text(char const * text) +{ + /* + ** Display the text. + */ + if (String != NULL) { + int x = X; + + if (PrintFlags & TPF_CENTER) { + x += Width/2; + } + if (PrintFlags & TPF_RIGHT) { + x += Width-1; + } + + Fancy_Text_Print(text, x, Y, GadgetClass::Get_Color_Scheme(), TBLACK, PrintFlags); + } +} diff --git a/REDALERT/STATBTN.H b/REDALERT/STATBTN.H new file mode 100644 index 000000000..d793d90ad --- /dev/null +++ b/REDALERT/STATBTN.H @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STATBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STATBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STATBTN_H +#define STATBTN_H + +#include "gadget.h" +#include "buff.h" + +class StaticButtonClass : public GadgetClass +{ + public: + StaticButtonClass(void); + StaticButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const * text, bool resize = false); + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + /* + ** If a background is to be preserved for this button, then this will point to + ** a buffer that holds a pristine background image. + */ + Buffer Background; + + /* + ** This points to a copy of the string that is used for the button's text. + */ + char * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/REDALERT/STATS.CPP b/REDALERT/STATS.CPP new file mode 100644 index 000000000..536e7a4cc --- /dev/null +++ b/REDALERT/STATS.CPP @@ -0,0 +1,911 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 05/29/1996 * + * * + * Last Update : May 29th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Internet game statistics to collect and upload to the server * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "packet.h" +#include "ccdde.h" + +#define FIELD_PACKET_TYPE "TYPE" +#define FIELD_GAME_ID "IDNO" +#define FIELD_START_CREDITS "CRED" +#define FIELD_BASES "BASE" +#define FIELD_TIBERIUM "TIBR" +#define FIELD_CRATES "CRAT" +#define FIELD_AI_PLAYERS "AIPL" +#define FIELD_CAPTURE_THE_FLAG "FLAG" +#define FIELD_START_UNIT_COUNT "UNIT" +#define FIELD_TECH_LEVEL "TECH" +#define FIELD_SCENARIO "SCEN" +#define FIELD_COMPLETION "CMPL" +#define FIELD_START_TIME "TIME" +#define FIELD_GAME_DURATION "DURA" +#define FIELD_FRAME_RATE "AFPS" +#define FIELD_SPEED_SETTING "SPED" +#define FIELD_GAME_VERSION "VERS" +#define FIELD_GAME_BUILD_DATE "DATE" +#define FIELD_COVERT_PRESENT "COVT" +#define FIELD_CPU_TYPE "PROC" +#define FIELD_MEMORY "MEMO" +#define FIELD_VIDEO_MEMORY "VIDM" +#define FIELD_SHADOW_REGROWS "SHAD" + +#ifdef WOLAPI_INTEGRATION +#define FIELD_HOSTORNOT "SDFX" +#define FIELD_TOURNAMENT "TRNY" +#define FIELD_NUM_INITIAL_PLAYERS "NUMP" +#define FIELD_NUM_REMAINING_PLAYERS "REMN" +#define FIELD_DISCONNECT_PINGS "PING" +#define FIELD_COMPUTERTOOKOVER "QUIT" +//#define FIELD_HARDWARE_GUID "GUID" +#define FIELD_PLAYER1_IP "ADR1" +#define FIELD_PLAYER2_IP "ADR2" +#endif + +// ajw The following were never used (thank god). +#define FIELD_PLAYER1_HANDLE "NAM1" +#define FIELD_PLAYER2_HANDLE "NAM2" +#define FIELD_PLAYER1_TEAM "SID1" +#define FIELD_PLAYER2_TEAM "SID2" +#define FIELD_PLAYER1_COLOR "COL1" +#define FIELD_PLAYER2_COLOR "COL2" +#define FIELD_PLAYER1_CREDITS "CRD1" +#define FIELD_PLAYER2_CREDITS "CRD2" + +#define FIELD_PLAYER1_UNITS_LEFT "UNL1" +#define FIELD_PLAYER2_UNITS_LEFT "UNL2" +#define FIELD_PLAYER1_INFANTRY_LEFT "INL1" +#define FIELD_PLAYER2_INFANTRY_LEFT "INL2" +#define FIELD_PLAYER1_PLANES_LEFT "PLL1" +#define FIELD_PLAYER2_PLANES_LEFT "PLL2" +#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1" +#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2" +#define FIELD_PLAYER1_VESSELS_LEFT "VSL1" +#define FIELD_PLAYER2_VESSELS_LEFT "VSL2" + +#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1" +#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2" +#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1" +#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2" +#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1" +#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2" +#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1" +#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2" +#define FIELD_PLAYER1_VESSELS_BOUGHT "VSB1" +#define FIELD_PLAYER2_VESSELS_BOUGHT "VSB2" + +#define FIELD_PLAYER1_UNITS_KILLED "UNK1" +#define FIELD_PLAYER2_UNITS_KILLED "UNK2" +#define FIELD_PLAYER1_INFANTRY_KILLED "INK1" +#define FIELD_PLAYER2_INFANTRY_KILLED "INK2" +#define FIELD_PLAYER1_PLANES_KILLED "PLK1" +#define FIELD_PLAYER2_PLANES_KILLED "PLK2" +#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1" +#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2" +#define FIELD_PLAYER1_VESSELS_KILLED "VSK1" +#define FIELD_PLAYER2_VESSELS_KILLED "VSK2" + +#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1" +#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2" + +#define FIELD_PLAYER1_CRATES_FOUND "CRA1" +#define FIELD_PLAYER2_CRATES_FOUND "CRA2" +#define FIELD_PLAYER1_HARVESTED "HRV1" +#define FIELD_PLAYER2_HARVESTED "HRV2" + + +#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50 +#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51 + +// Note: These enums match those in the game results server code. +enum { + COMPLETION_CONNECTION_LOST, + COMPLETION_PLAYER_1_WON, + COMPLETION_PLAYER_1_WON_BY_RESIGNATION, + COMPLETION_PLAYER_1_WON_BY_DISCONNECTION, + COMPLETION_PLAYER_2_WON, + COMPLETION_PLAYER_2_WON_BY_RESIGNATION, + COMPLETION_PLAYER_2_WON_BY_DISCONNECTION, +#ifdef FIXIT_VERSION_3 // Stalemate games. + COMPLETION_WASH = 64, +#endif +}; + + +extern unsigned long PlanetWestwoodGameID; +extern HINSTANCE ProgramInstance; +extern unsigned long PlanetWestwoodStartTime; + +extern "C" char CPUType; + + +bool GameTimerInUse = false; +TimerClass GameTimer; +long GameEndTime; +void *PacketLater = NULL; + +//#include "WolDebug.h" + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +extern bool bReconnectDialogCancelled; +#endif + +/*********************************************************************************************** + * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/29/96 12:38PM ST : Created * + *=============================================================================================*/ + +void Send_Statistics_Packet(void) +{ +#if (0)//PG +// debugprint( "Stats: Send_Statistics_Packet() called.\n" ); +#ifndef INTERNET_OFF // Denzil 5/4/98 + +#ifdef WOLAPI_INTEGRATION + if( !pWolapi ) // Should no longer ever happen. + return; +#endif + + PacketClass stats; + HouseClass *player; + static int packet_size; + int index; + bool packet_later = false; // Should the packet be sent later + void *packet; + + static char field_player_handle[5] = { "NAM?" }; + static char field_player_team[5] = { "SID?" }; + static char field_player_color[5] = { "COL?" }; + static char field_player_credits[5] = { "CRD?" }; + static char field_player_units_left[5] = { "UNL?" }; + static char field_player_infantry_left[5] = { "INL?" }; + static char field_player_planes_left[5] = { "PLL?" }; + static char field_player_buildings_left[5] = { "BLL?" }; + static char field_player_vessels_left[5] = { "VSL?" }; + static char field_player_units_bought[5] = { "UNB?" }; + static char field_player_infantry_bought[5] = { "INB?" }; + static char field_player_planes_bought[5] = { "PLB?" }; + static char field_player_buildings_bought[5] = { "BLB?" }; + static char field_player_vessels_bought[5] = { "VSB?" }; + static char field_player_units_killed[5] = { "UNK?" }; + static char field_player_infantry_killed[5] = { "INK?" }; + static char field_player_planes_killed[5] = { "PLK?" }; + static char field_player_buildings_killed[5] = { "BLK?" }; + static char field_player_vessels_killed[5] = { "VSK?" }; + static char field_player_buildings_captured[5] = { "BLC?" }; + static char field_player_crates_found[5] = { "CRA?" }; + static char field_player_harvested[5] = { "HRV?" }; + + static char *houses[] = { + "SPA", + "GRE", + "USS", + "ENG", + "ITA", + "GER", + "FRA", + "TKY", + "GUD", + "BAD", + "CIV", + "JP ", + "M01", + "M02", + "M03", + "M04", + "M05", + "M06", + "M07", + "M08" + }; + + if (!PacketLater){ + + /* + ** Field to identify this as C&C 95 internet game statistics packet + */ +#ifdef WOLAPI_INTEGRATION + if( pWolapi->bGameServer ) + { + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)0 ); // Reversed meaning of this for Neal. + }else{ + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)1 ); + } +#else + if (Server){ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO); + }else{ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO); + } +#endif + + /* + ** Game ID. A unique game identifier assigned by WChat. + */ + stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID); + +#ifdef WOLAPI_INTEGRATION + + // Number of players initially in game. + stats.Add_Field( FIELD_NUM_INITIAL_PLAYERS, (unsigned long)pWolapi->GameInfoCurrent.iPlayerCount ); +//debugprint( "Stats: number of initial players is %i\n", pWolapi->GameInfoCurrent.iPlayerCount ); + + // Number of players remaining in game. Not sure of what use this will be statistically... + stats.Add_Field( FIELD_NUM_REMAINING_PLAYERS, (unsigned long)Session.Players.Count() ); +//debugprint( "Stats: number of remaining players is %i\n", Session.Players.Count() ); + + // Whether or not this was a tournament game. + stats.Add_Field( FIELD_TOURNAMENT, (unsigned char)( pWolapi->GameInfoCurrent.bTournament ? 1 : 0 ) ); + +// ajw This is now in WOLAPI... +// // A unique value that identifies the machine that the game was played on. +// HW_PROFILE_INFO hwinfo; +// ::GetCurrentHwProfile( &hwinfo ); +// stats.Add_Field( FIELD_HARDWARE_GUID, hwinfo.szHwProfileGuid ); +#endif + + /* + ** Start credits. + */ + stats.Add_Field(FIELD_START_CREDITS, (unsigned long)Session.Options.Credits); + + /* + ** Bases (On/Off) + */ + stats.Add_Field(FIELD_BASES, Session.Options.Bases ? "ON" : "OFF"); + + /* + ** Tiberium (On/Off) + */ + stats.Add_Field(FIELD_TIBERIUM, Session.Options.Tiberium ? "ON" : "OFF"); + + /* + ** Crates (On/Off) + */ + stats.Add_Field(FIELD_CRATES, Session.Options.Goodies ? "ON" : "OFF"); + + /* + ** AI Players (On/Off) + */ + stats.Add_Field(FIELD_AI_PLAYERS, (unsigned long) Session.Options.AIPlayers); + + /* + ** Shadow regrowth enabled + */ + stats.Add_Field(FIELD_SHADOW_REGROWS, Special.IsShadowGrow ? "ON" : "OFF" ); + + /* + ** Capture the flag mode (On/Off) + */ + stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF"); + + /* + ** Start unit count + */ + stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)Session.Options.UnitCount); + + /* + ** Tech level. + */ + stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel); + + /* + ** Scenario + */ +#if (1) + + stats.Add_Field(FIELD_SCENARIO, Session.Options.ScenarioDescription); + +#else //(1) + char fname[128]; + char namebuffer[40]; + char *abuffer = (char *)_ShapeBuffer; + memset(abuffer, '\0', _ShapeBufferSize); + sprintf(fname,"%s.INI",Scen.ScenarioName); + CCFileClass fileo; + fileo.Set_Name (fname); + fileo.Read(abuffer, _ShapeBufferSize-1); + fileo.Close(); + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer); + stats.Add_Field(FIELD_SCENARIO, namebuffer); + //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]); +#endif //(1) + +#ifdef WOLAPI_INTEGRATION + // Completion status is set for Tournament games only - ajw. + if( pWolapi->GameInfoCurrent.bTournament ) + { +#endif + + /* + ** Game completion status. + ** + ** Connection lost. + ** Player 1 won + ** Player 2 won + ** Player 1 won by resignation + ** Player 2 won by resignation + ** Player 1 aborted + ** Player 2 aborted + ** + ** Game was a draw + */ + HouseClass *player1 = NULL; + HouseClass *player2 = NULL; + for (int h=0 ; hIsHuman){ + if (player1){ + player2 = ptr; + break; + }else{ + player1 = ptr; + } + } + } + + int completion = -1; + + if (player1 && player2){ // Can this ever fail? ajw +#ifdef FIXIT_VERSION_3 + // Send IP addresses of both players. + NetNumType net; + NetNodeType node; + char szIPAddress[ 30 ]; + Session.Players[ 0 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + if( strcmp( szIPAddress, "255.255.255.255" ) == 0 ) + { + + // Ok. It's not set. Let's try to get it ourselves... + char szHostName[ 512 ]; + int iRes = gethostname( szHostName, 512 ); + if( iRes != SOCKET_ERROR ) // else forget about trying + { +// debugprint( "gethostname got me %s\n", szHostName ); + struct hostent* pHostent = gethostbyname( szHostName ); + if( pHostent ) // else forget about trying + { + int i = 0; + int* piAddress = (int*)pHostent->h_addr_list[ i ]; + while( piAddress ) + { + // There is a non-null value for this h_addr_list entry. + char szAsciiIP[ 30 ]; + strcpy( szAsciiIP, inet_ntoa( *( (struct in_addr*)piAddress ) ) ); + // We have an address in the right form. + // Now, is it an address in a private network? If so we should ignore it. + unsigned char q1 = ( (char*)piAddress )[ 0 ]; // First digit. + unsigned char q2 = ( (char*)piAddress )[ 1 ]; // Second digit. +// debugprint( "ip: %s\n", szAsciiIP ); + if( q1 == 10 || ( q1 == 172 && ( q2 >= 16 && q2 <= 31 ) ) || ( q1 == 192 && q2 == 168 ) ) + { + // This is a private network address - ignore it and go on to next. + } + else + { + strcpy( szIPAddress, szAsciiIP ); + break; + } + piAddress = (int*)pHostent->h_addr_list[ ++i ]; + } + } +// else +// debugprint( "gethostbyname failed. Error %i\n", WSAGetLastError() ); + } +// else +// debugprint( "gethostname failed with %i, error %i\n", iRes, WSAGetLastError() ); + } + stats.Add_Field( FIELD_PLAYER1_IP, (char*)szIPAddress ); + Session.Players[ 1 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + stats.Add_Field( FIELD_PLAYER2_IP, (char*)szIPAddress ); +#endif +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + completion = COMPLETION_WASH; + } + else + { +#endif + if (ConnectionLost){ +#ifdef WOLAPI_INTEGRATION + if( bReconnectDialogCancelled ) + { + if( Session.Players[ 0 ]->Player.ID == HOUSE_MULTI1 ) + // I am player1. + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + else + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + else + { + completion = COMPLETION_CONNECTION_LOST; + if( pWolapi->bDisconnectPingingCompleted ) + { + char szPingResult[ 8 ]; // Format is "x/y a/b", e.g., "3/5 4/5" + pWolapi->DisconnectPingResultsString( szPingResult ); + stats.Add_Field( FIELD_DISCONNECT_PINGS, (char*)szPingResult ); + } +// else +// debugprint( "Stats: bDisconnectPingingCompleted is false! Should be finished!!!!!!!!!!!!!!!\n" ); + } +#else + completion = COMPLETION_CONNECTION_LOST; +#endif + }else{ + + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + + + if (player2->IsDefeated){ + /* + ** Player 1 won. Find out how. + */ + completion = COMPLETION_PLAYER_1_WON; + if (player2->IsResigner){ + completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION; + }else{ + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + } + + + }else{ + + if (player1->IsDefeated){ + /* + ** Player 2 won. Find out how. + */ + completion = COMPLETION_PLAYER_2_WON; + if (player1->IsResigner){ + completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION; + }else{ + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + } + } + } + } +#ifdef FIXIT_VERSION_3 // Stalemate games. + } +#endif + } + + stats.Add_Field (FIELD_COMPLETION, (char) completion); +//debugprint( "Stats: Tournament game completion value: %i\n", completion ); + +#ifdef WOLAPI_INTEGRATION + } +#endif + + + /* + ** Game start time (GMT or Pacific?) + ** + ** Passed from WChat + */ + stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime); + + /* + ** Game duration (seconds). + */ + stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60); + + /* + ** Avg. frame rate. + */ +#ifdef FIXIT_IP_CRASH + long divisor = GameEndTime / 60; + if (divisor != 0) { + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); + } else { + stats.Add_Field (FIELD_FRAME_RATE, 0l); + } +#else + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); +#endif + + + /* + ** CPU type + */ + stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType); + + /* + ** Memory + */ + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys); + + /* + ** Video memory + */ + DDCAPS video_capabilities; + long video_memory; + + if (DirectDrawObject){ + video_capabilities.dwSize = sizeof (video_capabilities); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + video_memory = video_capabilities.dwVidMemTotal; + video_memory += 1024*1024 -1; + video_memory &= 0xfff00000; + stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory); + } + } + + /* + ** Game speed setting. + */ + stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed); + + /* + ** Red Alert version/build date + */ + char version[128]; + sprintf (version, "V%s", VerNum.Version_Name() ); + stats.Add_Field (FIELD_GAME_VERSION, (char*)version); + + char path_to_exe[280]; + FILETIME write_time; //File time is 64 bits + + GetModuleFileName (ProgramInstance, path_to_exe, 280); + RawFileClass file; + file.Set_Name(path_to_exe); + file.Open(); + HANDLE handle = file.Get_File_Handle(); + + if (handle != INVALID_HANDLE_VALUE){ + if (GetFileTime (handle, NULL, NULL, &write_time)){ + write_time.dwLowDateTime = htonl (write_time.dwLowDateTime); + write_time.dwHighDateTime = htonl (write_time.dwHighDateTime); + stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time)); + } + } + + /* + ** Covert installed? (Yes/No) + */ + //stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present()); + + /* + ** Build the player specific statistics + ** + */ +#ifdef WOLAPI_INTEGRATION + for (int house = 0 ; house < 8 ; house++){ +#else + for (int house = 0 ; house < 2 ; house++){ +#endif + player = HouseClass::As_Pointer((HousesType) (house + HOUSE_MULTI1)); + +#ifdef WOLAPI_INTEGRATION + if( !player ) + continue; +#endif + + /* + ** Player handle. + */ + field_player_handle[3] = '1' + (char)house; +#ifdef WOLAPI_INTEGRATION + stats.Add_Field (field_player_handle, (char*) player->InitialName); +//debugprint( "Stats: Player %i name %s\n", house, (char*) player->InitialName ); +//debugprint( "Stats: Player %i ending name %s\n", house, (char*) player->IniName ); +#else + stats.Add_Field (field_player_handle, (char*) player->IniName); +#endif + +#ifdef WOLAPI_INTEGRATION + // Whether or not this player was taken over by the computer, due to his quitting the game. + if( strcmp( player->IniName, player->InitialName ) ) + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)1 ); + else + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)0 ); +#endif + /* + ** Player team. (NOD or GDI) + */ + field_player_team[3] = '1' + (char)house; + stats.Add_Field (field_player_team, houses[player->ActLike]); + + /* + ** Player color + */ + field_player_color[3] = '1' + (char)house; + stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1)); + + /* + ** Player end credits. + */ + field_player_credits[3] = '1' + (char)house; + stats.Add_Field (field_player_credits, player->Credits + player->Tiberium); + + /* + ** Number of each unit/building type built + */ + field_player_infantry_bought[3] = '1' + (char)house; + field_player_units_bought[3] = '1' + (char)house; + field_player_planes_bought[3] = '1' + (char)house; + field_player_buildings_bought[3] = '1' + (char)house; + field_player_vessels_bought[3] = '1' + (char)house; + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_bought, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + player->InfantryTotals->To_PC_Format(); + player->UnitTotals->To_PC_Format(); + player->AircraftTotals->To_PC_Format(); + player->BuildingTotals->To_PC_Format(); + + /* + ** Clear out the counts and use the space to count up the current number of units/buildings + */ + player->InfantryTotals->Clear_Unit_Total(); + player->AircraftTotals->Clear_Unit_Total(); + player->UnitTotals->Clear_Unit_Total(); + player->BuildingTotals->Clear_Unit_Total(); + player->VesselTotals->Clear_Unit_Total(); + + /* + ** Number of units remaining to player + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (player == unit->House){ + player->UnitTotals->Increment_Unit_Total (unit->Class->Type); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (player == infantry->House && !infantry->Class->IsCivilian){ + player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (player == aircraft->House){ // && aircraft->Class->Type != AIRCRAFT_CARGO){ + player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type); + } + } + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (player == building->House){ + player->BuildingTotals->Increment_Unit_Total (building->Class->Type); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + if (player == vessel->House){ + player->VesselTotals->Increment_Unit_Total (vessel->Class->Type); + } + } + + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + field_player_infantry_left[3] = '1' + (char)house; + field_player_units_left[3] = '1' + (char)house; + field_player_planes_left[3] = '1' + (char)house; + field_player_buildings_left[3] = '1' + (char)house; +#ifdef FIXIT_IP_STATS + field_player_vessels_left[3] = '1' + (char)house; +#endif + stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_left, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + + /* + ** Number of enemy units/buildings of each type destroyed. + */ + + player->DestroyedInfantry->To_Network_Format(); + player->DestroyedUnits->To_Network_Format(); + player->DestroyedAircraft->To_Network_Format(); + player->DestroyedBuildings->To_Network_Format(); + player->DestroyedVessels->To_Network_Format(); + + field_player_infantry_killed[3] = '1' + (char)house; + field_player_units_killed[3] = '1' + (char)house; + field_player_planes_killed[3] = '1' + (char)house; + field_player_buildings_killed[3] = '1' + (char)house; + field_player_vessels_killed[3] = '1' + (char)house; + + stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#ifdef FIXIT_VERSION_3 + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedVessels->Get_Unit_Count()*4); +#else + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#endif + + + /* + ** Number and type of enemy buildings captured + */ + field_player_buildings_captured[3] = '1' + (char)house; + player->CapturedBuildings->To_Network_Format(); + stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4); + + /* + ** Number of crates discovered and their contents + */ + field_player_crates_found[3] = '1' + (char)house; + player->TotalCrates->To_Network_Format(); + stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4); + + /* + ** Amount of tiberium turned into credits + */ + field_player_harvested[3] = '1' + (char)house; + stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits); + + } + + /* + ** Create the comms packet to be sent + */ + packet = stats.Create_Comms_Packet(packet_size); + +#ifndef WOLAPI_INTEGRATION // ajw - 'PacketLater' is no longer ever used. + /* + ** If a player disconnected then dont send the packet at this time - save it for later + */ + if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION + || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){ + PacketLater = packet; + return; + } +#endif + + }else{ //else for if (!PacketLater) + + /* + ** Send the packet we calculated earlier when the disconnect occurred + */ + packet = PacketLater; + PacketLater = NULL; + } + + /* + ** Send it..... + */ + const char* szGameResServer; + int iPort; + if( pWolapi->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + szGameResServer = pWolapi->szGameResServerHost2; + iPort = pWolapi->iGameResServerPort2; + } + else + { + szGameResServer = pWolapi->szGameResServerHost1; + iPort = pWolapi->iGameResServerPort1; + } + + if( *szGameResServer ) + { + if( pWolapi->pNetUtil->RequestGameresSend( szGameResServer, iPort, (unsigned char*)packet, packet_size ) != S_OK ) + //debugprint( "RequestGameresSend( %s, %i ) failed!!!\n", szGameResServer, iPort ); + ; + } + + /* + ** Save it to disk as well so I can see it + */ +// RawFileClass anotherfile ("packet.net"); +// anotherfile.Write(packet, packet_size); +//debugprint( "Wrote out packet.net\n" ); + + /* + ** Tidy up + */ + delete [] packet; + + GameStatisticsPacketSent = true; +#endif // INTERNET_OFF +#endif +} + + + +void Register_Game_Start_Time(void) +{ + + GameTimer.Set (0, true); + GameTimerInUse = true; +} + +extern void Register_Game_End_Time(void) +{ + GameEndTime = GameTimer.Time(); + GameTimerInUse = false; +} + + +#endif //WIN32 diff --git a/REDALERT/STRAW.CPP b/REDALERT/STRAW.CPP new file mode 100644 index 000000000..9dd67832c --- /dev/null +++ b/REDALERT/STRAW.CPP @@ -0,0 +1,138 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Straw::Get_From -- Connect one straw segment to another. * + * Straw::Get -- Fetch some data from the straw chain. * + * Straw::~Straw -- Destructor for a straw segment. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "straw.h" +#include +#include + + +/*********************************************************************************************** + * Straw::~Straw -- Destructor for a straw segment. * + * * + * This destructor will remove this segment from the straw chain. If there were any * + * connected segments to either side, they will be joined so that the straw chain will * + * still remain linked. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Straw::~Straw(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Get_From(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Straw::Get_From -- Connect one straw segment to another. * + * * + * Use this routine to connect straw segments together. The straw segment specified as the * + * parameter will be linked to the chain such that when data is requested, it will be * + * fetched from the specified link before being processed by this link. * + * * + * INPUT: straw -- Pointer to the straw segment that data will be fetched from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Straw::Get_From(Straw * straw) +{ + if (ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + } + + ChainTo = straw; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Straw::Get -- Fetch some data from the straw chain. * + * * + * Use this routine to fetch some data from the straw. It is presumed that this routine * + * will be overridden to provide some useful functionality. At this level, the straw chain * + * merely passes the request on to the next straw in the chain. * + * * + * INPUT: source -- Pointer to the buffer to hold the requested data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number returned * + * is less than the number requested, then this indicates that the straw data has * + * been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Straw::Get(void * source, int slen) +{ + if (ChainTo != NULL) { + return(ChainTo->Get(source, slen)); + } + return(0); +} + + diff --git a/REDALERT/STRAW.H b/REDALERT/STRAW.H new file mode 100644 index 000000000..9e5b068de --- /dev/null +++ b/REDALERT/STRAW.H @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STRAW_H +#define STRAW_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This is a demand driven data carrier. It will retrieve the byte request by passing +** the request down the chain (possibly processing on the way) in order to fulfill the +** data request. Without being derived, this class merely passes the data through. Derived +** versions are presumed to modify the data in some useful way or monitor the data +** flow. +*/ +class Straw +{ + public: + Straw(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Straw(void); + + virtual void Get_From(Straw * pipe); + void Get_From(Straw & pipe) {Get_From(&pipe);} + virtual int Get(void * buffer, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Straw * ChainTo; + Straw * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Straw(Straw & rvalue); + Straw & operator = (Straw const & pipe); +}; + + +#endif diff --git a/REDALERT/STUB.CPP b/REDALERT/STUB.CPP new file mode 100644 index 000000000..9768e8550 --- /dev/null +++ b/REDALERT/STUB.CPP @@ -0,0 +1,22 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include +#include + +void main(void) +{ + printf("Run C&C.COM.\n"); +} diff --git a/REDALERT/STYLE.H b/REDALERT/STYLE.H new file mode 100644 index 000000000..dd770d0a9 --- /dev/null +++ b/REDALERT/STYLE.H @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + STYLE GUIDE FOR COMMAND & CONQUER : TIBERIAN SUN + +In addition to the standard Westwood programmer's style guide, the following +guidelines apply. + +> Use every feature the compiler has that will help catch bugs and encourage +solid portable coding practices. i.e., turn on all warnings and treat all warnings +as errors. Use helper programs such as Lint, BoundsChecker, or CodeGuard whenever +possible. + +> Keep related items together. Examples: Declare variables right before they are +used. Keep functions that work on the same subsystem, within the same module. + +> Use consistent commenting and spacing style. Examples: see existing Red Alert +code. Creative freedom does not extend to formatting. + +> Use descriptive variable names. Examples: Use "index" rather than "i". Use +"unit_index" rather than "index". + +> Use a consistent variable and function naming convention. Examples; boolean +variables should begin with "Is" (e.g., "IsActive", "IsFiring"). Functions that +exist solely to return a similar boolean query should begin with "Is_". + +> If you have a variable that only serves a boolean function, then declare it +as "bool" rather than as "int" and assign it "true" or "false" rather than "1" +or "0". + +> Choose function and variable names that are precise, descriptive, and concise (in that +order). + +> Hide data where possible: Examples: If a global is only used in one module, make +it static to that module. If a helper function is only used in one module, make it +static as well. Hide class members in the private section if they aren't needed +public. Most variables aren't needed as public. + +> Keep function bodies short. It is often times, more easy to understand and maintain +if a very long function is broken up into helper functions with descriptive names. +A clue if a function is too long is if you can page down 5 times and still be somewhere +in the middle of the function. If you can page down 10 times and still be in the +function, you've got a serious function size problem. + +> Compare pointers to NULL and integer types to zero (NULL) instead of using the +"if (x)" or "if (!x)" format. This short format should only be used for boolean +values and expressions. It is just as efficient and it makes it more clear to the +reader that the variable is a pointer and not a simple boolean value. + +> Use "const" when declaring any member functions that do not modify data. Corollary -- +make sure any function that appears like it will not modify data will, in fact, NOT +modify any data. Example; A function called Is_Able_To_Move() shouldn't modify the +object nor should it actually cause something to start moving! The use of "const" is a +variation on style guide rule #1 (use the compiler to help ensure proper code). + +> Shun using assembly language except in extreme cases. Such would be if a +function can only be performed by using assembly (requires special opcodes), or +if performance would be PROVEABLY dramatically improved. Don't code in assembly for +performance reasons if there isn't benchmarking code in place to analyze the results. +As a corollary, design a better algorithm as the first recourse. + +> Keep class interfaces small and simple. + +> Organize algorithms to use as few special case "if" statements as possible. Data +tables or data files are preferred. This follows the generally good guideline of offloading +as much processing as possible to compile-time rather than run-time. + +*/ diff --git a/REDALERT/SUPER.CPP b/REDALERT/SUPER.CPP new file mode 100644 index 000000000..f3ac6977a --- /dev/null +++ b/REDALERT/SUPER.CPP @@ -0,0 +1,388 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SUPER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : October 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SuperClass::AI -- Process the super weapon AI. * + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * SuperClass::Discharged -- Handles discharged action for special super weapon. * + * SuperClass::Enable -- Enable this super special weapon. * + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * SuperClass::Remove -- Removes super weapon availability. * + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * * + * This is the constructor for the super weapons. * + * * + * INPUT: recharge -- The recharge delay time (in game frames). * + * * + * charging -- Voice to announce that the weapon is charging. * + * * + * ready -- Voice to announce that the weapon is fully charged. * + * * + * impatient -- Voice to announce current charging state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +SuperClass::SuperClass(int recharge, bool powered, VoxType charging, VoxType ready, VoxType impatient, VoxType suspend) : + IsPowered(powered), + IsPresent(false), + IsOneTime(false), + IsReady(false), + Control(0), + OldStage(-1), + VoxRecharge(ready), + VoxCharging(charging), + VoxImpatient(impatient), + VoxSuspend(suspend), + RechargeTime(recharge) +{ +} + + +/*********************************************************************************************** + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * * + * This will temporarily put on hold the charging of the special weapon. This might be the * + * result of insufficient power. * + * * + * INPUT: on -- Should the weapon charging be suspended? Else, it will unsuspend. * + * * + * OUTPUT: Was the weapon suspend state changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Suspend(bool on) +{ + if (IsPresent && !IsReady && !IsOneTime && on == Control.Is_Active()) { + if (!on) { + Control.Start(); + } else { + Control.Stop(); + } +// if (on != IsSuspended) { +// if (on) { +// SuspendTime = Control; +// } else { +// Control = SuspendTime; +// } +// IsSuspended = on; + return(true); +// } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Enable -- Enable this super special weapon. * + * * + * This routine is called when the special weapon needs to be activated. This is used for * + * both the normal super weapons and the special one-time super weapons (from crates). * + * * + * INPUT: onetime -- Is this a special one time super weapon? * + * * + * player -- Is this weapon for the player? If true, then there might be a voice * + * announcement of this weapon's availability. * + * * + * quiet -- Request that the weapon start in suspended state (quiet mode). * + * * + * OUTPUT: Was the special super weapon enabled? Failure might indicate that the weapon was * + * already available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Enable(bool onetime, bool player, bool quiet) +{ + if (!IsPresent) { + IsPresent = true; + IsOneTime = onetime; + bool retval = Recharge(player && !quiet); + if (quiet) Suspend(true); + return(retval); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Remove -- Removes super weapon availability. * + * * + * Call this routine when the super weapon should be removed because of some outside * + * force. For one time special super weapons, they can never be removed except as the * + * result of discharging them. * + * * + * INPUT: none * + * * + * OUTPUT: Was the special weapon removed and disabled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Remove(void) +{ + if (IsPresent && !IsOneTime) { + IsReady = false; + IsPresent = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * * + * This routine is called when the special weapon is allowed to recharge. Suspension will * + * be disabled and the animation process will begin. * + * * + * INPUT: player -- Is this for a player owned super weapon? If so, then a voice * + * announcement might be in order. * + * * + * OUTPUT: Was the super weapon begun charging up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Recharge(bool player) +{ + if (IsPresent && !IsReady) { +// IsSuspended = false; + OldStage = -1; + Control.Start(); + Control = RechargeTime; + +#ifdef CHEAT_KEYS + if (Special.IsSpeedBuild) { + Control = 1; + } +#endif + + if (player) { + Speak(VoxCharging); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Superclass::Discharged -- Handles discharged action for special super weapon. * + * * + * This routine should be called when the special super weapon has been discharged. The * + * weapon will either begin charging anew or will be removed entirely -- depends on the * + * one time flag for the weapon. * + * * + * INPUT: player -- Is this special weapon for the player? If so, then there might be a * + * voice announcement. * + * * + * OUTPUT: Should the sidebar be reprocessed because the special weapon has been eliminated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Discharged(bool player) +{ + if (Control.Is_Active() && IsPresent && IsReady) { + IsReady = false; + if (IsOneTime) { + IsOneTime = false; + return(Remove()); + } else { + Recharge(player); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::AI -- Process the super weapon AI. * + * * + * This routine will process the charge up AI for this super weapon object. If the weapon * + * has advanced far enough to change any sidebar graphic that might represent it, then * + * "true" will be returned. Use this return value to intelligently update the sidebar. * + * * + * INPUT: player -- Is this for the player? If it is and the weapon is now fully charged, * + * then this fully charged state will be announced to the player. * + * * + * OUTPUT: Was the weapon's state changed such that a sidebar graphic update will be * + * necessary? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::AI(bool player) +{ + if (IsPresent && !IsReady) { + if (!Control.Is_Active()) { + if (OldStage != -1) { + OldStage = -1; + return(true); + } + } else { + if (Control == 0) { + IsReady = true; + if (player) { + Speak(VoxRecharge); + } + return(true); + } else { + if (Anim_Stage() != OldStage) { + OldStage = Anim_Stage(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * * + * This will return the current animation stage for this super weapon. The value will be * + * between zero (uncharged) to ANIMATION_STAGES (fully charged). Use this value to render * + * the appropriate graphic on the sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current animation stage for this special super weapon powerup. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + * 10/11/1996 JLB : Doesn't show complete until really complete. * + *=============================================================================================*/ +int SuperClass::Anim_Stage(void) const +{ + if (IsPresent) { + if (IsReady) { + return(ANIMATION_STAGES); + } +// int time = Control; +// if (IsSuspended) { +// time = SuspendTime; +// } + + int stage = ANIMATION_STAGES * fixed(RechargeTime-Control.Value(), RechargeTime); + stage = min(stage, ANIMATION_STAGES-1); + return(stage); + } + return(0); +} + + +/*********************************************************************************************** + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * * + * This routine is called when the player clicks on the super weapon icon on the sidebar * + * when the super weapon is not ready yet. This results in a voice message feedback to the * + * player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Impatient_Click(void) const +{ + if (!Control.Is_Active()) { + Speak(VoxSuspend); + } else { + Speak(VoxImpatient); + } +} + + +/*********************************************************************************************** + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * * + * This routine will force the special weapon to full charge state. Call it when the weapon * + * needs to be instantly charged. The airstrike (when it first becomes available) is a * + * good example. * + * * + * INPUT: player -- Is this for the player? If true, then the full charge state will be * + * announced. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Forced_Charge(bool player) +{ + if (IsPresent) { + IsReady = true; + Control.Start(); + Control = 0; +// IsSuspended = false; + if (player) { + Speak(VoxRecharge); + } + } +} diff --git a/REDALERT/SUPER.H b/REDALERT/SUPER.H new file mode 100644 index 000000000..b9838bc5d --- /dev/null +++ b/REDALERT/SUPER.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SUPER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SUPER_H +#define SUPER_H + +#include "ftimer.h" + +class SuperClass { + public: + SuperClass(NoInitClass const & x) : Control(x) {}; + SuperClass(void) : Control(NoInitClass()) {}; + SuperClass(int recharge, bool powered, VoxType charging=VOX_NONE, VoxType ready=VOX_NONE, VoxType impatient=VOX_NONE, VoxType suspend=VOX_NONE); + + bool Suspend(bool on); + bool Enable(bool onetime = false, bool player=false, bool quiet=false); + void Forced_Charge(bool player=false); + bool AI(bool player=false); + bool Remove(void); + void Impatient_Click(void) const; + int Anim_Stage(void) const; + bool Discharged(bool player); + bool Is_Ready(void) const {return(IsReady);} + bool Is_Present(void) const {return(IsPresent);} + bool Is_One_Time(void) const {return(IsOneTime && IsPresent);} + bool Is_Powered(void) const {return(IsPowered);} + + //Needed access to recharge times for tooltips - 2019/08/14 Jason Scott + int Get_Recharge_Time() const { return(RechargeTime); }; + + private: + bool Recharge(bool player=false); + + unsigned IsPowered:1; + unsigned IsPresent:1; + unsigned IsOneTime:1; + unsigned IsReady:1; + + CDTimerClass Control; + int OldStage; + + VoxType VoxRecharge; + VoxType VoxCharging; + VoxType VoxImpatient; + VoxType VoxSuspend; + int RechargeTime; + + //Needed to make ANIMATION_STAGES public so the animation frame numbers could be turned into progress + //percentages - 2019/08/07 Jason Scott +public: + enum { + ANIMATION_STAGES=54 + }; +}; + + + +#endif diff --git a/REDALERT/SUPPORT.ASM b/REDALERT/SUPPORT.ASM new file mode 100644 index 000000000..561f1481e --- /dev/null +++ b/REDALERT/SUPPORT.ASM @@ -0,0 +1,556 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: F:\projects\c&c0\vcs\code\support.asv 5.0 11 Nov 1996 09:40:30 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + GLOBAL C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mul [(GraphicViewPort ebx).GVPWidth] + add edi,eax + + ; Verify the the X pixel offset is legal. + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +if 0 + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + GLOBAL C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + GLOBAL C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + GLOBAL C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + GLOBAL C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX +endif + + + DATASEG + +TabA DD 6949350 + DD 4913933 + DD 3474675 + DD 2456966 + DD 1737338 + DD 1228483 + DD 868669 + DD 614242 + DD 434334 + DD 307121 + DD 217167 + DD 153560 + DD 108584 + DD 76780 + DD 54292 + DD 38390 + DD 27146 + DD 19195 + DD 13573 + DD 9598 + DD 6786 + DD 4799 + DD 3393 + DD 2399 + DD 1697 + DD 1200 + DD 848 + DD 600 + DD 424 + DD 300 + DD 212 + DD 150 + DD 106 + +TabB DD 154 + DD 218 + DD 309 + DD 437 + DD 618 + DD 874 + DD 1236 + DD 1748 + DD 2472 + DD 3496 + DD 4944 + DD 6992 + DD 9888 + DD 13983 + DD 19775 + DD 27967 + DD 39551 + DD 55933 + DD 79101 + DD 111866 + DD 158203 + DD 223732 + DD 316405 + DD 447465 + DD 632811 + DD 894929 + DD 1265621 + DD 1789859 + DD 2531243 + DD 3579718 + DD 5062486 + DD 7159436 + DD 10124971 + + CODESEG + +;*********************************************************************************************** +;* Square_Root -- Finds the square root of the fixed pointer parameter. * +;* * +;* INPUT: val -- The fixed point (16:16) value to find the square root of. * +;* * +;* OUTPUT: Returns with the square root of the fixed pointer parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/04/1995 JLB : Adapted. * +;*=============================================================================================*/ +;unsigned Square_Root(unsigned val); + GLOBAL C Square_Root :NEAR + PROC Square_Root C near + USES ebx,edx + + bsr ebx,eax + jz ??zero + + mul [DWORD 4*ebx + OFFSET TabA] + shrd eax,edx,10h + add eax, [4*ebx + OFFSET TabB] +??zero: + ret + + ENDP Square_Root + +;---------------------------------------------------------------------------- + + END + diff --git a/REDALERT/SURFACE.CPP b/REDALERT/SURFACE.CPP new file mode 100644 index 000000000..aeaa859b6 --- /dev/null +++ b/REDALERT/SURFACE.CPP @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SURFACE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SURFACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "FUNCTION.H" +#include "surface.h" + + +Surface::Surface(int w, int h, Buffer const * buffer, int pitch) : + Width(w), + Height(h), + Pitch(pitch) +{ + /* + ** If a buffer was specified, then this means that the surface will use the buffer memory + ** and thus not allocate and manage its own memory. + */ + if (buffer != NULL) { + SurfaceData = *buffer; + + /* + ** Reduce the dimensions if the buffer is not big enough. This size bounding is only + ** possible if the buffer size is known. Otherwise, presume that it is big enough. + */ + if (buffer->Get_Size() > 0 && Get_Size() > buffer->Get_Size()) { + + Height = buffer->Get_Size() / (Width+Pitch); + if (Height == 0) { + Height = 1; + Width = buffer->Get_Size(); + } + } + } else { + /* + ** A new buffer without existing memory specified, will allocate and manage its + ** own memory for the surface. + */ + new(&SurfaceData) Buffer(Logical_Size()); + } +} + + +Surface::Surface(Surface const & surface, int x, int y, int w, int h) : + Width(w), + Height(h), + Pitch(surface.Bytes_Per_Line() % w) +{ + new(&SurfaceData) Buffer((char*)surface.Get_Buffer() + y*surface.Bytes_Per_Line() + x); +} + + +void Surface::Copy_To(Buffer & buffer, int x, int y, int w, int h) const +{ + assert(buffer.Is_Valid()); + + /* + ** Determine the width of the region to copy from this surface. + */ + int width = w; + if (width == -1) { + width = Width; + } + + /* + ** Determine the height of the region to copy from this surface. + */ + int height = h; + if (height == -1) { + height = Height; + } + + Copy_To(Rect(x, y, width, height), buffer); +} + + +void Surface::Copy_To(Rect const & fromrect, Buffer & tobuffer) const +{ + assert(fromrect.Is_Valid()); + assert(tobuffer.Is_Valid()); + + /* + ** Determine the copy-from rectangle. The size is bounded to the source + ** size of the surface, regardless of what was specified as the source + ** rectangle. + */ + Rect rect = fromrect.Intersect(Rect(0, 0, Width, Height)); + + /* + ** Determine the number of bytes to copy. If this number would be + ** larger than the size of the destination buffer (presuming the size + ** of the destination buffer is known), then limit the copy size + ** to the buffer size. + */ + int tocopy = rect.Size(); + if (tobuffer.Get_Size() > 0 && tobuffer.Get_Size() > tocopy) { + tocopy = tobuffer.Get_Size(); + } + + /* + ** Determine the source starting byte pointer. + */ + char * source = (char*)Get_Buffer() + rect.Y*Bytes_Per_Line() + rect.X; + + /* + ** Determine the destination buffer pointer. This will always be the + ** start of the destination buffer object specified. + */ + char * dest = tobuffer; + + /* + ** Determine the working pitch value to use. For full width surface + ** copies on surfaces that have no inherent pitch, then a very fast + ** copy-in-one-fast-step operation can be performed. + */ + int pitch = Bytes_Per_Line() % rect.Width; + if (pitch == 0) { + memmove(dest, source, tocopy); + } else { + + /* + ** Copy the source to the destination in line segments. + */ + for (int y = 0; y < rect.Height; y++) { + memmove(dest, source, width); + dest += rect.Width; + source += rect.Width + pitch; + } + } +} +#endif \ No newline at end of file diff --git a/REDALERT/SURFACE.H b/REDALERT/SURFACE.H new file mode 100644 index 000000000..389970440 --- /dev/null +++ b/REDALERT/SURFACE.H @@ -0,0 +1,113 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SURFACE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SURFACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SURFACE_H +#define SURFACE_H + +#include "buff.h" +#include "rect.h" + +/* +** This class is used to represent an arbitrary rectangle in a graphic memory space. Typically, +** this represents what could be called a graphic buffer or even a portion of a graphic buffer. +** It can also represent a piece of artwork (such as a shape image) to be used to construct a +** display image. This wide range of use is possible, because this class is a very minimal +** representation of the graphic data. It holds only the information is absolutely needs and +** has the minimum member functions possible. +*/ +class Surface +{ + public: + Surface(void) : Width(0), Height(0), Pitch(0) {} + Surface(int w, int h, Buffer const * buffer=NULL, int pitch=0); + Surface(Surface const & surface, int x, int y, int w, int h); + virtual ~Surface(void) {} + + /* + ** Basic manipulation routines for copying entire surfaces or + ** a sub-region. + */ + void Copy_To(Rect const & fromrect, Surface & tosurface, Rect const & torect) const; + void Copy_To(Rect const & fromrect, Buffer & tobuffer) const; + + /* + ** Convenience routines that make presumptions on what is desired so that a + ** minimum of parameters can be specified. These routine ultimately map to the + ** basic copy routines. + */ + void Copy_To(Buffer & buffer, int x=0, int y=0, int w=-1, int h=-1) const; + void Copy_From(Buffer const & buffer, int x=0, int y=0, int w=-1, int h=-1); + + /* + ** Basic query functions. Support routines that will manipulate the underlying + ** image data will require access to this information. + */ + void * Get_Buffer(void) const {return(SurfaceData.Get_Buffer());} + int Get_Size(void) const {return(Bytes_Per_Line() * Height);} + int Get_Width(void) const {return(Width);} + int Get_Height(void) const {return(Height);} + int Get_Pitch(void) const {return(Pitch);} + + protected: + int Bytes_Per_Line(void) const {return(Width+Pitch);} + + /* + ** This is the pointer to the surface memory. Sometimes this could be allocated memory + ** managed by this class but more likely, it is memory that is merely referred to by + ** this class. + */ + Buffer SurfaceData; + + /* + ** This is the width (columns) of the surface (in pixels). + */ + int Width; + + /* + ** This ithe height (rows) of the surface (in pixels). + */ + int Height; + + /* + ** This is the modulus of the surface underlying memory 'display surface' width + ** divided by this surface's width. For a surface that is full width, this value + ** will be 0. This value will be non-zero if this surface is referring to a + ** sub-region within another surface. + */ + int Pitch; +}; + + +#endif diff --git a/REDALERT/Shape.cpp b/REDALERT/Shape.cpp new file mode 100644 index 000000000..cca1b42e8 --- /dev/null +++ b/REDALERT/Shape.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** +** Replacement for Shape asm files +** +** ST - 12/19/2018 10:15AM +** +** +** +*/ + + +extern "C" unsigned char *_ShapeBuffer = 0; +extern "C" long _ShapeBufferSize = 0; + + + +/* +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +*/ +extern "C" void __cdecl Set_Shape_Buffer(void const *buffer, int size) +{ + _ShapeBuffer = (unsigned char *)buffer; + _ShapeBufferSize = size; +} + + + +#if (0) +global C ShapeBuffer :dword +global C ShapeBufferSize :dword +global C _ShapeBuffer :dword +global C _ShapeBufferSize :dword +global C Set_Shape_Buffer :near + +DATASEG +label ShapeBuffer dword +_ShapeBuffer dd 0 + +label ShapeBufferSize dword +_ShapeBufferSize dd 0 + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL C Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + +#endif \ No newline at end of file diff --git a/REDALERT/TAB.CPP b/REDALERT/TAB.CPP new file mode 100644 index 000000000..5b1ac5a8c --- /dev/null +++ b/REDALERT/TAB.CPP @@ -0,0 +1,318 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TAB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TabClass::AI -- Handles player I/O with the tab buttons. * + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * TabClass::TabClass -- Default construct for the tab button class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * TabClass::TabShape = NULL; + + +/*********************************************************************************************** + * TabClass::TabClass -- Default construct for the tab button class. * + * * + * The default constructor merely sets the tab buttons to default non-selected state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +TabClass::TabClass(void) : + FlasherTimer(0), + IsToRedraw(false), + MoneyFlashTimer(0) +{ +} + + +/*********************************************************************************************** + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * * + * This routine is called whenever the display is being redrawn (in some fashion). The * + * parameter can be used to force the tab buttons to redraw completely. The default action * + * is to only redraw if the tab buttons have been explicitly flagged to be redraw. The * + * result of this is the elimination of unnecessary redraws. * + * * + * INPUT: complete -- bool; Force redraw of the entire tab button graphics? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 05/19/1995 JLB : New EVA style. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +#define TAB_HEIGHT 8 +void TabClass::Draw_It(bool complete) +{ + SidebarClass::Draw_It(complete); + + if (Debug_Map) { + return; + } + +// Disable tab drawing for menu, credits buttons etc. ST - 5/27/2019 +#if (0) + /* + ** Redraw the top bar imagery if flagged to do so or if the entire display needs + ** to be redrawn. + */ + if ((complete || IsToRedraw) && LogicPage->Lock()) { + + int width = SeenBuff.Get_Width(); + int rightx = width - 1; + int tab_height = TAB_HEIGHT * RESFACTOR; + + LogicPage->Fill_Rect(0, 0, rightx, tab_height-1, BLACK); +// LogicPage->Fill_Rect(0, 0, rightx, tab_height-(2 * RESFACTOR), BLACK); + +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 0, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, 2, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + Draw_Credits_Tab(); + LogicPage->Draw_Line(0, tab_height-(1* RESFACTOR), rightx, tab_height-(1 * RESFACTOR), BLACK); +#ifdef WIN32 + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); +#else +// Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +#endif //WIN32 + if (IsSidebarActive) { +#ifndef WIN32 + TabClass::Hilite_Tab(1); +#endif //WIN32 + } else { + CC_Draw_Shape(TabShape, 0, width-(EVA_WIDTH * RESFACTOR), 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_SIDEBAR, width-((EVA_WIDTH/2) * RESFACTOR), 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); + } + + LogicPage->Unlock(); + } + Credits.Graphic_Logic(complete || IsToRedraw); +#endif + IsToRedraw = false; +} + + +void TabClass::Draw_Credits_Tab(void) +{ +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, Map.MoneyFlashTimer > 1 ? 8 : 6, (320-EVA_WIDTH) * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, 4, (320-(EVA_WIDTH*2)) * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + + if (Scen.MissionTimer.Is_Active()) { + bool light = ((int)Scen.MissionTimer < TICKS_PER_MINUTE * Rule.TimerWarning) || Map.FlasherTimer > 0; +#ifdef WIN32 + CC_Draw_Shape(TabShape, light ? 4 : 2, 320, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, light ? 6 : 5, EVA_WIDTH * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + } +} + + +void TabClass::Hilite_Tab(int tab) +{ + int xpos = 0; + int text = TXT_TAB_BUTTON_CONTROLS; + int textx = (EVA_WIDTH/2) * RESFACTOR; + + if (tab) { + xpos = (320-EVA_WIDTH) * RESFACTOR; + text = TXT_TAB_SIDEBAR; + textx = (320-(EVA_WIDTH/2)) * RESFACTOR; + } + +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 1, xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + MetalScheme.Color = 128+6; + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); + MetalScheme.Color = 128; +#else + CC_Draw_Shape(TabShape, 1 + (tab ? 0 : 2), xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(text, textx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +// Fancy_Text_Print(text, textx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +#endif +} + + +/*********************************************************************************************** + * TabClass::AI -- Handles player I/O with the tab buttons. * + * * + * This routine is called every game tick and passed whatever key the player has supplied. * + * If the input selects a tab button, then the graphic gets updated accordingly. * + * * + * INPUT: input -- The player's input character (might be mouse click). * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 05/31/1995 JLB : Fixed to handle mouse shape properly. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +void TabClass::AI(KeyNumType &input, int x, int y) +{ + if (y >= 0 && y < (TAB_HEIGHT * RESFACTOR) && x < (SeenBuff.Get_Width() - 1) && x > 0) { + + bool ok = false; + int width = SeenBuff.Get_Width(); + + /* + ** If the mouse is at the top of the screen, then the tab bars only work + ** in certain areas. If the special scroll modification is not active, then + ** the tabs never work when the mouse is at the top of the screen. + */ + if (y > 0) { + ok = true; + } + + if (ok) { + if (input == KN_LMOUSE) { + int sel = -1; + if (x < EVA_WIDTH * RESFACTOR) sel = 0; +#ifndef WIN32 // No Sidebar tab in hires - sidebar is always active. + if (x > (320-80) * RESFACTOR) sel = 1; +#endif //WIN32 + if (sel >= 0) { + Set_Active(sel); + input = KN_NONE; + } + } + + Override_Mouse_Shape(MOUSE_NORMAL, false); + } + } + + if (MoneyFlashTimer == 1) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + Credits.AI(); + + SidebarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * * + * This function is used to activate one of the file folder tab buttons that appear at the * + * top edge of the screen. * + * * + * INPUT: select -- The button to activate. 0 = left button, 1=next button, etc. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void TabClass::Set_Active(int select) +{ + switch (select) { + case 0: + Queue_Options(); + break; + + case 1: + Map.SidebarClass::Activate(-1); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * * + * This routine will perform any one time initializations of the tab handler class. This * + * typically includes the loading of the shapes that appear on it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void TabClass::One_Time(void) +{ + SidebarClass::One_Time(); + RawFileClass file("tabs.shp"); + TabShape = MFCD::Retrieve("TABS.SHP"); +} + + +void TabClass::Flash_Money(void) +{ + IsToRedraw = true; + Flag_To_Redraw(false); + MoneyFlashTimer = 7; +} diff --git a/REDALERT/TAB.H b/REDALERT/TAB.H new file mode 100644 index 000000000..053c9c03c --- /dev/null +++ b/REDALERT/TAB.H @@ -0,0 +1,76 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TAB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TAB_H +#define TAB_H + +#include "sidebar.h" +#include "credits.h" + +class TabClass: public SidebarClass +{ + public: + TabClass(void); + TabClass(NoInitClass const & x) : SidebarClass(x), Credits(x), FlasherTimer(x), MoneyFlashTimer(x) {}; + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + static void Draw_Credits_Tab(void); + static void Hilite_Tab(int tab); + void Flash_Money(void); + + virtual void One_Time(void); // One-time inits + void Redraw_Tab(void) {IsToRedraw = true;Flag_To_Redraw(false);}; + + CreditClass Credits; + + CDTimerClass FlasherTimer; + + protected: + + /* + ** If the tab graphic is to be redrawn, then this flag is true. + */ + unsigned IsToRedraw:1; + + private: + void Set_Active(int select); + + CDTimerClass MoneyFlashTimer; + + static void const * TabShape; +}; + + +#endif diff --git a/REDALERT/TACTION.CPP b/REDALERT/TACTION.CPP new file mode 100644 index 000000000..98f5468b5 --- /dev/null +++ b/REDALERT/TACTION.CPP @@ -0,0 +1,902 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TACTION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ACTION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Action_From_Name -- retrieves ActionType for given name * + * Action_Needs -- Figures out what data an action object needs. * + * Name_From_Action -- retrieves name for ActionType * + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * TActionClass::Detach -- Removes any attachment from associated action. * + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * TActionClass::operator -- Performs the action that this object does. * + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + +/* +** These are the text names for the various actions. If the action name ends with "..." then +** this means that additional data is probably required. +*/ +static const char * ActionText[TACTION_COUNT] = { + "-No Action-", + "Winner is...", + "Loser is...", + "Production Begins", + "Create Team...", + "Destroy All Teams", + "All to Hunt...", + "Reinforcement (team)...", + "Drop Zone Flare (waypoint)...", + "Fire Sale...", + "Play Movie...", + "Text Trigger (ID num)...", + "Destroy Trigger...", + "Autocreate Begins...", + "~don't use~", + "Allow Win", + "Reveal all map", + "Reveal around waypoint...", + "Reveal zone of waypoint...", + "Play sound effect...", + "Play music theme...", + "Play speech...", + "Force Trigger...", + "Timer Start", + "Timer Stop", + "Timer Extend (1/10th min)...", + "Timer Shorten (1/10th min)...", + "Timer Set (1/10th min)...", + "Global Set...", + "Global Clear...", + "Auto Base Building...", + "Grow shroud one 'step'", + "Destroy attached building", + "Add 1-time special weapon...", + "Add repeating special weapon...", + "Preferred target...", + "Launch Nukes" +}; + + +ActionChoiceClass ActionChoices[TACTION_COUNT] = { + {TACTION_NONE}, + {TACTION_WIN}, + {TACTION_LOSE}, + {TACTION_BEGIN_PRODUCTION}, + {TACTION_CREATE_TEAM}, + {TACTION_DESTROY_TEAM}, + {TACTION_ALL_HUNT}, + {TACTION_REINFORCEMENTS}, + {TACTION_DZ}, + {TACTION_FIRE_SALE}, + {TACTION_PLAY_MOVIE}, + {TACTION_TEXT_TRIGGER}, + {TACTION_DESTROY_TRIGGER}, + {TACTION_AUTOCREATE}, + {TACTION_WINLOSE}, + {TACTION_ALLOWWIN}, + {TACTION_REVEAL_ALL}, + {TACTION_REVEAL_SOME}, + {TACTION_REVEAL_ZONE}, + {TACTION_PLAY_SOUND}, + {TACTION_PLAY_MUSIC}, + {TACTION_PLAY_SPEECH}, + {TACTION_FORCE_TRIGGER}, + {TACTION_START_TIMER}, + {TACTION_STOP_TIMER}, + {TACTION_ADD_TIMER}, + {TACTION_SUB_TIMER}, + {TACTION_SET_TIMER}, + {TACTION_SET_GLOBAL}, + {TACTION_CLEAR_GLOBAL}, + {TACTION_BASE_BUILDING}, + {TACTION_CREEP_SHADOW}, + {TACTION_DESTROY_OBJECT}, + {TACTION_1_SPECIAL}, + {TACTION_FULL_SPECIAL}, + {TACTION_PREFERRED_TARGET}, + {TACTION_LAUNCH_NUKES} +}; + + +/*********************************************************************************************** + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * * + * This is a support routine only used to display this object when it is part of a list * + * box. * + * * + * INPUT: x,y -- The coordinate (upper left) to display the description. * + * * + * width,height -- Dimensions of the area to display the description. * + * * + * selected -- Is this item highlighted? * + * * + * flags -- The text print flags to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void ActionChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TActionClass::Detach -- Removes any attachment from associated action. * + * * + * This routine will remove any action reference to the team type specified. This routine * + * is called when the team type is being destroyed. All references to that team type must * + * also be severed. This routine does that with respect to trigger actions. * + * * + * INPUT: target-- The target object or type to remove from this taction object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Detach(TARGET target) +{ + if (Is_Target_TeamType(target) && Team == As_TeamType(target)) { + Team = NULL; + } + if (Is_Target_TriggerType(target) && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*********************************************************************************************** + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * * + * This routine will build the text (INI entry) format for the data of this trigger * + * action object. Typical use of this is when the INI file is being written. * + * * + * INPUT: ptr -- Pointer to the location to build the INI text to. The buffer is presumed * + * to be big enough. * + * * + * OUTPUT: none * + * * + * WARNINGS: The buffer passed to this routine must be big enough to hold the largest * + * text that will be created into it. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d,%d", Action, TeamTypes.Logical_ID(Team), TriggerTypes.Logical_ID(Trigger), Data.Value); +} + + +/*********************************************************************************************** + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * * + * This routine will convert INI data into the right values within this trigger action * + * object. Typical use of this routine is when the INI file is being read. It is the * + * counterpart to the Build_INI_Entry function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Read_INI(void) +{ + switch (NewINIFormat) { + default: + Action = TActionType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + case 0: + Action = TActionType(atoi(strtok(NULL, ","))); + + char const * ptr = strtok(NULL, ","); + Team = TeamTypeClass::From_Name(ptr); + assert(Action_Needs(Action) != NEED_TEAM || Team.Is_Valid()); + + /* + ** Since triggers refer to other triggers, only record a copy of the trigger text + ** name. This will be fixed up later. + */ + Trigger.Set_Raw((long)strdup(strtok(NULL, ","))); + + Data.Value = atoi(strtok(NULL, ",")); + break; + } +} + + +/*********************************************************************************************** + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * * + * This routine is called prior to saving the game. It will convert any pointers into a * + * format that is safe for persistent storage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine makes the object unfit for use. The Decode_Pointers() routine * + * must be called prior to using this object. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * * + * This routine is called after a game has been loaded. The encoded pointers will be * + * converted back into usable format by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will NOT work unless the pointer are, in fact, coded. There is * + * no prevention check to protect against calling this routine twice. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::operator -- Performs the action that this object does. * + * * + * This routine is called when the action associated with this action object must be * + * performed. Typically, this occurs when a trigger has "sprung" and now it must take * + * effect. The action object is what carries out this effect. * + * * + * INPUT: house -- The owner of this action. This information is necessary since some * + * actions depend on who the trigger was owned by. * + * * + * object-- Pointer to the object that the springing trigger was attached to. If * + * this parameter is null, then the trigger wasn't attached to any object. * + * * + * id -- Trigger ID (only if forced) otherwise -1. * + * * + * cell -- The cell this trigger is attached to (if any). * + * * + * OUTPUT: bool; Was this action able to perform what it needed to do? Failure could be * + * because a reinforcement couldn't be generated, for example. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + * 04/10/1996 JLB : Added the ID parameter. * + *=============================================================================================*/ +bool TActionClass::operator() (HousesType house, ObjectClass * object, int id, CELL cell) +{ + /* + ** Otherwise, take an appropriate action. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + TriggerClass * trig = NULL; + if (id != -1) { + trig = Triggers.Raw_Ptr(id); + } + bool success = true; +// TeamTypeClass * ttype = Team; + + /* + ** Ensure that the specified object is not actually dead. A dead object could + ** be passed to this routine in the case of a multiple event trigger that + ** had the first event kill the object. + */ + if (object && !object->IsActive) { + object = 0; + } + + switch (Action) { + /* + ** Display a text message overlayed onto the tactical map. + */ + case TACTION_TEXT_TRIGGER: + Session.Messages.Add_Message(NULL, Data.Value, (char *)TutorialText[Data.Value], PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + break; + + /* + ** Launch nuclear missiles (duds) from all mslo's + */ + case TACTION_LAUNCH_NUKES: + { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_MSLO) { + bldg->Assign_Mission(MISSION_MISSILE); + } + } + break; + } + + /* + ** Set the preferred target for the house. + */ + case TACTION_PREFERRED_TARGET: + if (hptr) { + hptr->PreferredTarget = Data.Quarry; + } + break; + + /* + ** Initiate (or disable) the computer AI. When active, the computer will + ** build bases and units. + */ + case TACTION_BASE_BUILDING: + if (Data.Bool) { + hptr->IsBaseBuilding = true; + } else { + hptr->IsBaseBuilding = false; + } + break; + + /* + ** Cause the shadow to creep back one step. + */ + case TACTION_CREEP_SHADOW: + Map.Encroach_Shadow(PlayerPtr); + break; + + /* + ** Set a scenario global. + */ + case TACTION_SET_GLOBAL: + Scen.Set_Global_To(Data.Value, true); + break; + + /* + ** Clear a scenario global. + */ + case TACTION_CLEAR_GLOBAL: + Scen.Set_Global_To(Data.Value, false); + break; + + /* + ** Reveal the map around the area specified. + */ + case TACTION_REVEAL_SOME: + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[Data.Value], Rule.GapShroudRadius, PlayerPtr, false); + } + break; + + /* + ** Reveal all cells of the zone that the specified waypoint is located + ** in. This can be used to reveal whole islands or bodies of water + */ + case TACTION_REVEAL_ZONE: + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[Data.Value]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + } + break; + + /* + ** Reveal the entire map. + */ + case TACTION_REVEAL_ALL: + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + break; + + /* + ** Star the mission timer. + */ + case TACTION_START_TIMER: + if (!Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + } + break; + + /* + ** Stop the mission timer. This will really just + ** suspend the timer. + */ + case TACTION_STOP_TIMER: + if (Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Stop(); + Map.Redraw_Tab(); + } + break; + + /* + ** Add time to the mission timer. + */ + case TACTION_ADD_TIMER: + Scen.MissionTimer = Scen.MissionTimer + (Data.Value * (TICKS_PER_MINUTE/10)); + Map.Redraw_Tab(); + break; + + /* + ** Remove time from the mission timer. + */ + case TACTION_SUB_TIMER: + if ((int)Scen.MissionTimer <= Data.Value * (TICKS_PER_MINUTE/10)) { + Scen.MissionTimer = 0; + } else { + Scen.MissionTimer = Scen.MissionTimer - (Data.Value * (TICKS_PER_MINUTE/10)); + } + Map.Redraw_Tab(); + break; + + /* + ** Set the mission timer to the value specified. + */ + case TACTION_SET_TIMER: + Scen.MissionTimer = Data.Value * (TICKS_PER_MINUTE/10); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + break; + + /* + ** Play a movie immediately. The game is temporarily + ** suspended while the movie plays. + */ + case TACTION_PLAY_MOVIE: + Hide_Mouse(); + SeenPage.Clear(); + Play_Movie(VQType(char(Data.Movie)), THEME_NONE, true, true); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + Show_Mouse(); + break; + + /* + ** Play a sound effect. + */ + case TACTION_PLAY_SOUND: + Sound_Effect(Data.Sound); + break; + + /* + ** Play a musical theme. + */ + case TACTION_PLAY_MUSIC: + Theme.Queue_Song(Data.Theme); + break; + + /* + ** Play the speech data specified. + */ + case TACTION_PLAY_SPEECH: + Speak(Data.Speech); + break; + + /* + ** Give the special weapon to the house. + */ + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + hptr->SuperWeapon[Data.Special].Enable(Action==TACTION_1_SPECIAL, false); +// hptr->SuperWeapon[Data.Special].Forced_Charge(PlayerPtr == hptr); + + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (hptr->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, Data.Special, hptr); + } + } + else { + if (PlayerPtr == hptr) { + Map.Add(RTTI_SPECIAL, Data.Special); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** Destroying a trigger means that all triggers of that type will be destroyed. + */ + case TACTION_DESTROY_TRIGGER: + if (Trigger.Is_Valid()) { + for (int index = 0; index < Triggers.Count(); index++) { + if (Triggers.Ptr(index)->Class == Trigger) { + Detach_This_From_All(Triggers.Ptr(index)->As_Target()); + delete Triggers.Ptr(index); + index--; + } + } + } + break; + + /* + ** A forced trigger will force an existing trigger of that type or + ** will create a trigger of that type and then force it to be sprung. + */ + case TACTION_FORCE_TRIGGER: + if (Trigger.Is_Valid()) { + Find_Or_Make(Trigger)->Spring(TEVENT_ANY, 0, 0, true); + } + break; + + /* + ** Place a smoke marker at the waypoint specified. + */ + case TACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Scen.Waypoint[Data.Value])); + break; + + /* + ** Flag the house specified as the winner. Really the house value + ** is only used to determine if it is the player or the computer. + */ + case TACTION_WIN: + if (Data.House == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** Flag the house specified as the loser. The house parameter is only + ** used to determine if it refers to the player or the computer. + */ + case TACTION_LOSE: + if (Data.House != PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** This will enable production to begin for the house specified. + */ + case TACTION_BEGIN_PRODUCTION: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->Begin_Production(); + } + break; + + /* + ** Cause all buildings to be sold and all units to go into + ** hunt mode. + */ + case TACTION_FIRE_SALE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->State = STATE_ENDGAME; + } + break; + + /* + ** Begin the team autocreate logic for the house specified. + */ + case TACTION_AUTOCREATE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->IsAlerted = true; + } + break; + + /* + ** Manually create the team specified. + */ + case TACTION_CREATE_TEAM: + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + break; + + /* + ** Destroy all teams of the type specified. + */ + case TACTION_DESTROY_TEAM: + Team->Destroy_All_Of(); + break; + + /* + ** Create a reinforcement of the team specified. + */ + case TACTION_REINFORCEMENTS: + success = Do_Reinforcements(&*Team); + break; + + /* + ** Force all units of the house specified to go into + ** hunt mode. + */ + case TACTION_ALL_HUNT: + HouseClass::As_Pointer(Data.House)->Do_All_To_Hunt(); + break; + + /* + ** This will destroy all objects that this trigger is + ** attached to. + */ + case TACTION_DESTROY_OBJECT: + if (object) { + int damage = object->Strength; + object->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } else { + success = false; + } + + /* + ** If the trigger is attached to a bridge, then the bridge + ** gets destroyed regardless of whether the trigger was a + ** forced or natural spring event. + */ + if (cell != 0) { + Map.Destroy_Bridge_At(cell); + } + + /* + ** Loop through and destroy all objects that have this trigger + ** attached to them. + */ + if (trig) { + for (int u_index = 0; u_index < Units.Count(); u_index++) { + UnitClass * unit = Units.Ptr(u_index); + + if (unit && unit->Trigger == trig) { + unit->Trigger = NULL; + int damage = unit->Strength; + unit->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int i_index = 0; i_index < Infantry.Count(); i_index++) { + InfantryClass * infantry = Infantry.Ptr(i_index); + + if (infantry && infantry->Trigger == trig) { + infantry->Trigger = NULL; + int damage = infantry->Strength; + infantry->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { + AircraftClass * aircraft = Aircraft.Ptr(a_index); + + if (aircraft && aircraft->Trigger == trig) { + aircraft->Trigger = NULL; + int damage = aircraft->Strength; + aircraft->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass * building = Buildings.Ptr(b_index); + + if (building && building->Trigger == trig) { + building->Trigger = NULL; + int damage = building->Strength; + building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + } + break; + + /* + ** Do no action at all. + */ + case TACTION_NONE: + default: + break; + } + return(success); +} + + +/*********************************************************************************************** + * Action_From_Name -- retrieves ActionType for given name * + * * + * INPUT: * + * name name to get ActionType for * + * * + * OUTPUT: * + * ActionType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TActionType Action_From_Name (char const * name) +{ + if (name == NULL) { + return(TACTION_NONE); + } + + for (TActionType i = TACTION_NONE; i < TACTION_COUNT; i++) { + if (!stricmp(name, ActionText[i])) { + return(i); + } + } + + return(TACTION_NONE); +} + + +/*********************************************************************************************** + * Name_From_Action -- retrieves name for ActionType * + * * + * INPUT: * + * action ActionType to get name for * + * * + * OUTPUT: * + * name of ActionType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Action(TActionType action) +{ + return(ActionText[action]); +} + + +/*********************************************************************************************** + * Action_Needs -- Figures out what data an action object needs. * + * * + * Use this routine to determine what extra data is needed for the specified action. This * + * data will be prompted for in the scenario editor. * + * * + * INPUT: action -- The action that is to be queried. * + * * + * OUTPUT: Returns with the data type (enumeration) needed for this action type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +NeedType Action_Needs(TActionType action) +{ + switch (action) { + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + return(NEED_SPECIAL); + + case TACTION_FIRE_SALE: + case TACTION_WIN: + case TACTION_LOSE: + case TACTION_ALL_HUNT: + case TACTION_BEGIN_PRODUCTION: + case TACTION_AUTOCREATE: + return(NEED_HOUSE); + + case TACTION_BASE_BUILDING: + return(NEED_BOOL); + + case TACTION_CREATE_TEAM: + case TACTION_DESTROY_TEAM: + case TACTION_REINFORCEMENTS: + return(NEED_TEAM); + + case TACTION_FORCE_TRIGGER: + case TACTION_DESTROY_TRIGGER: + return(NEED_TRIGGER); + + case TACTION_DZ: + return(NEED_WAYPOINT); + + case TACTION_REVEAL_SOME: + case TACTION_REVEAL_ZONE: + return(NEED_WAYPOINT); + + case TACTION_PLAY_MUSIC: + return(NEED_THEME); + + case TACTION_PLAY_MOVIE: + return(NEED_MOVIE); + + case TACTION_PLAY_SOUND: + return(NEED_SOUND); + + case TACTION_PLAY_SPEECH: + return(NEED_SPEECH); + + case TACTION_TEXT_TRIGGER: + case TACTION_ADD_TIMER: + case TACTION_SUB_TIMER: + case TACTION_SET_TIMER: + case TACTION_SET_GLOBAL: + case TACTION_CLEAR_GLOBAL: + return(NEED_NUMBER); + + case TACTION_PREFERRED_TARGET: + return(NEED_QUARRY); + + default: + break; + } + return(NEED_NONE); +} + + diff --git a/REDALERT/TACTION.H b/REDALERT/TACTION.H new file mode 100644 index 000000000..0cdaf5f1e --- /dev/null +++ b/REDALERT/TACTION.H @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TACTION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ACTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef ACTION_H +#define ACTION_H + +typedef enum TActionType : unsigned char { + TACTION_NONE, + + TACTION_WIN, // player wins! + TACTION_LOSE, // player loses. + TACTION_BEGIN_PRODUCTION, // computer begins factory production. + TACTION_CREATE_TEAM, // computer creates a certain type of team + TACTION_DESTROY_TEAM, + TACTION_ALL_HUNT, // all enemy units go into hunt mode (teams destroyed). + TACTION_REINFORCEMENTS, // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + TACTION_DZ, // Deploy drop zone smoke. + TACTION_FIRE_SALE, // Sell all buildings and go on rampage. + TACTION_PLAY_MOVIE, // Play movie (temporarily suspend game). + TACTION_TEXT_TRIGGER, // Triggers a text message display. + TACTION_DESTROY_TRIGGER, // Destroy specified trigger. + TACTION_AUTOCREATE, // Computer to autocreate teams. + TACTION_WINLOSE, // Win if captured, lose if destroyed. + TACTION_ALLOWWIN, // Allows winning if triggered. + + TACTION_REVEAL_ALL, // Reveal the entire map. + TACTION_REVEAL_SOME, // Reveal map around cell #. + TACTION_REVEAL_ZONE, // Reveal all of specified zone. + TACTION_PLAY_SOUND, // Play sound effect. + TACTION_PLAY_MUSIC, // Play musical score. + TACTION_PLAY_SPEECH, // Play EVA speech. + TACTION_FORCE_TRIGGER, // Force trigger to activate. + TACTION_START_TIMER, // Start mission timer. + TACTION_STOP_TIMER, // Stop mission timer. + TACTION_ADD_TIMER, // Increase mission timer time. + TACTION_SUB_TIMER, // Decrease mission timer time. + TACTION_SET_TIMER, // Set and start the mission timer. + TACTION_SET_GLOBAL, // Set global variable. + TACTION_CLEAR_GLOBAL, // Clear global variable. + TACTION_BASE_BUILDING, // Automated base building. + TACTION_CREEP_SHADOW, // Shadow grows back one 'step'. + + TACTION_DESTROY_OBJECT, // Destroys the building this trigger is attached to. + TACTION_1_SPECIAL, // Add a one-time special weapon ability to house. + TACTION_FULL_SPECIAL, // Add a repeating special weapon ability to house. + + TACTION_PREFERRED_TARGET, // Designates preferred target for house. + TACTION_LAUNCH_NUKES, // Launch fake nuclear missiles from all silos + + TACTION_COUNT, + TACTION_FIRST=0 +} TActionType; + +TActionType Action_From_Name(char const * name); +char const * Name_From_Action(TActionType action); +NeedType Action_Needs(TActionType action); + +class TriggerTypeClass; +class TeamTypeClass; + +/* +** This elaborates the information necessary to carry out +** a trigger's action. +*/ +struct TActionClass { + TActionType Action; // Action to perform. + + CCPtr Team; // Team type pointer for this action (if needed). + + CCPtr Trigger; // Trigger type pointer for this action (if needed). + + union { + ThemeType Theme; // Musical theme. + VocType Sound; // Sound effect. + VoxType Speech; // Speech identifier. + HousesType House; // House to be affected. + SpecialWeaponType Special; // Special weapon ability. + QuarryType Quarry; // Preferred target for attack. + VQType Movie; // The movie to play. + bool Bool; // Boolean value. + long Value; + } Data; + + TActionClass(void) : Action(TACTION_NONE) { + Data.Theme = THEME_NONE; + Data.Value = -1; + }; + TActionClass(NoInitClass const & x) : Team(x), Trigger(x) {}; + + void Detach(TARGET target); + void Code_Pointers(void); + void Decode_Pointers(void); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; + + bool operator() (HousesType house, ObjectClass * object, int id, CELL cell); +}; + + +class ActionChoiceClass { + public: + ActionChoiceClass(TActionType event=TACTION_NONE) : Action(event) {} + + operator TActionType (void) const {return(Action);} + bool operator == (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action);} + bool operator != (ActionChoiceClass const & rvalue) const {return(Action != rvalue.Action);} + bool operator > (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Action(Action));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TActionType Action; +}; + + +extern ActionChoiceClass ActionChoices[TACTION_COUNT]; + +#endif diff --git a/REDALERT/TARGET.CPP b/REDALERT/TARGET.CPP new file mode 100644 index 000000000..4dc1adc59 --- /dev/null +++ b/REDALERT/TARGET.CPP @@ -0,0 +1,941 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TARGET.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * As_Animation -- Converts target value into animation pointer. * + * As_Building -- Converts a target value into a building object pointer. * + * As_Bullet -- Converts the target into a bullet pointer. * + * As_Cell -- Converts a target value into a cell number. * + * As_Coord -- Converts a target value into a coordinate value. * + * As_Infantry -- If the target is infantry, return a pointer to it. * + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * As_Object -- Converts a target value into an object pointer. * + * As_Target -- Converts a cell into a target value. * + * As_Target -- Converts a coordinate into a target value. * + * As_Team -- Converts a target number into a team pointer. * + * As_TeamType -- Converts a target into a team type pointer. * + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * As_Trigger -- Converts specified target into a trigger pointer. * + * As_TriggerType -- Convert the specified target into a trigger type. * + * As_Unit -- Converts a target value into a unit pointer. * + * As_Vessel -- Converts a target number into a vessel pointer. * + * TClass::TClass -- Constructor for target from object pointer. * + * TargetClass::As_Object -- Converts a target into an object pointer. * + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * Target_Legal -- Determines if the specified target is legal. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "target.h" + + +TargetClass::TargetClass(TARGET target) +{ + Target.Target = target; +} + + +TargetClass::TargetClass(AbstractClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(AbstractTypeClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(CellClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +CellClass * xTargetClass::As_Cell(void) const +{ + if (Target.Sub.Exponent == RTTI_CELL) { + return(&Map[(CELL)Target.Sub.Mantissa]); + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Trigger -- Converts specified target into a trigger pointer. * + * * + * This routine will convert the specified target number into a trigger pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the trigger pointer that the specified target number represents. If * + * it doesn't represent a legal trigger object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass * As_Trigger(TARGET target, bool check_active) +{ + TriggerClass* trigger = Is_Target_Trigger(target) ? Triggers.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && trigger != NULL && !trigger->IsActive) { + trigger = NULL; + } + return(trigger); +} + + +/*********************************************************************************************** + * As_Team -- Converts a target number into a team pointer. * + * * + * This routine will convert the specified target number into a team pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the team object that the specified target number represents. If it * + * doesn't represent a legal team then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * As_Team(TARGET target, bool check_active) +{ + TeamClass* team = Is_Target_Team(target) ? Teams.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && team != NULL && !team->IsActive) { + team = NULL; + } + return(team); +} + + +/*********************************************************************************************** + * As_TeamType -- Converts a target into a team type pointer. * + * * + * This routine will convert the specified target number into a team type pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the team type represented by the target number. If the * + * target number doesn't represent a legal team type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * As_TeamType(TARGET target) +{ + return(Is_Target_TeamType(target) ? TeamTypes.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Animation -- Converts target value into animation pointer. * + * * + * This routine will convert the specified target number into an animation pointer. * + * * + * INPUT: target -- The target number to convert into an animation pointer. * + * * + * OUTPUT: Returns with a pointer to the legal animation that this target represents. If it * + * doesn't represent a legal animation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +AnimClass * As_Animation(TARGET target, bool check_active) +{ + AnimClass* anim = Is_Target_Animation(target) ? Anims.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && anim != NULL && !anim->IsActive) { + anim = NULL; + } + return(anim); +} + + +/*********************************************************************************************** + * As_Bullet -- Converts the target into a bullet pointer. * + * * + * This routine will convert the specified target number into a bullet pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the bullet it specifies. If the target doesn't refer to * + * a legal bullet, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * As_Bullet(TARGET target, bool check_active) +{ + BulletClass* bullet = Is_Target_Bullet(target) ? Bullets.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && bullet != NULL && !bullet->IsActive) { + bullet = NULL; + } + return(bullet); +} + + +/*********************************************************************************************** + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * * + * This routine will convert the specified target value into an aircraft object pointer. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with a pointer to the aircraft that this target value represents. If the * + * specified target value doesn't represent an aircraft, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass * As_Aircraft(TARGET target, bool check_active) +{ + AircraftClass* aircraft = Is_Target_Aircraft(target) ? Aircraft.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && aircraft != NULL && !aircraft->IsActive) { + aircraft = NULL; + } + return(aircraft); +} + + +/*********************************************************************************************** + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * * + * This routine will take the target value specified and convert it into a TechnoClass * + * pointer if the target represents an object that has a TechnoClass. * + * * + * INPUT: target -- The target value to convert into a TechnoClass pointer. * + * * + * OUTPUT: Returns with a pointer to the associated object's TechnoClass. If the target * + * cannot be converted into a TechnoClass pointer, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * As_Techno(TARGET target, bool check_active) +{ + ObjectClass * obj = As_Object(target, check_active); + + if (obj && obj->Is_Techno()) { + return(TechnoClass *)obj; + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Object -- Converts a target value into an object pointer. * + * * + * This routine is used to convert the target value specified into an object pointer. If * + * the target doesn't represent an object or the target value is illegal, then NULL is * + * returned. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the object it represent, or NULL if not an object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * As_Object(TARGET target, bool check_active) +{ + int val = Target_Value(target); + ObjectClass * object = NULL; + switch (Target_Kind(target)) { + case RTTI_INFANTRY: + object = Infantry.Raw_Ptr(val); + break; + + case RTTI_UNIT: + object = Units.Raw_Ptr(val); + break; + + case RTTI_VESSEL: + object = Vessels.Raw_Ptr(val); + break; + + case RTTI_BUILDING: + object = Buildings.Raw_Ptr(val); + break; + + case RTTI_AIRCRAFT: + object = Aircraft.Raw_Ptr(val); + break; + + case RTTI_TERRAIN: + object = Terrains.Raw_Ptr(val); + break; + + case RTTI_BULLET: + object = Bullets.Raw_Ptr(val); + break; + + case RTTI_ANIM: + object = Anims.Raw_Ptr(val); + break; + + default: + break; + } + + /* + ** Special check to ensure that a target value that references an + ** invalid object will not be converted back into an object pointer. + ** This condition is rare, but could occur in a network game if the + ** object it refers to is destroyed between the time an event message + ** is sent and when it is received. + */ + if (check_active && object != NULL && !object->IsActive) { + object = NULL; + } + + return(object); +} + + +/*********************************************************************************************** + * As_Unit -- Converts a target value into a unit pointer. * + * * + * This routine is used to convert the target value specified into a pointer to a unit * + * object. * + * * + * INPUT: target -- The target value to convert into a unit pointer. * + * * + * OUTPUT: Returns with a pointer to the unit the target value represents or NULL if not * + * a unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * As_Unit(TARGET target, bool check_active) +{ + UnitClass* unit = Is_Target_Unit(target) ? Units.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && unit != NULL && !unit->IsActive) { + unit = NULL; + } + return(unit); +} + + +/*********************************************************************************************** + * As_Vessel -- Converts a target number into a vessel pointer. * + * * + * Use this routine to conver the specified target number into a pointer to a vessel object * + * that it represents. * + * * + * INPUT: target -- The target number to convert to a vessel pointer. * + * * + * OUTPUT: Returns with a pointer to the vessel object that this target value represents. If * + * the target number does not represent a vessel, then null is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * As_Vessel(TARGET target, bool check_active) +{ + VesselClass* vessel = Is_Target_Vessel(target) ? Vessels.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && vessel != NULL && !vessel->IsActive) { + vessel = NULL; + } + return(vessel); +} + + +/*********************************************************************************************** + * As_Infantry -- If the target is infantry, return a pointer to it. * + * * + * This routine will translate the specified target value into an infantry pointer if the * + * target actually represents an infantry object. * + * * + * INPUT: target -- The target to convert to a pointer. * + * * + * OUTPUT: Returns a pointer to the infantry object that this target value represents. If * + * the target doesn't represent an infantry object, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * As_Infantry(TARGET target, bool check_active) +{ + InfantryClass* infantry = Is_Target_Infantry(target) ? Infantry.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && infantry != NULL && !infantry->IsActive) { + infantry = NULL; + } + return(infantry); +} + + +/*********************************************************************************************** + * As_Building -- Converts a target value into a building object pointer. * + * * + * This routine is used to convert the target value specified into a building pointer. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the building object that the target value represents. * + * If it doesn't represent a building, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * As_Building(TARGET target, bool check_active) +{ + BuildingClass* building = Is_Target_Building(target) ? Buildings.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && building != NULL && !building->IsActive) { + building = NULL; + } + return(building); +} + + +/*********************************************************************************************** + * Target_Legal -- Determines if the specified target is legal. * + * * + * This routine is used to check for the legality of the target value specified. It is * + * necessary to call this routine if there is doubt about the the legality of the target. * + * It is possible for the unit that a target value represents to be eliminated and thus * + * rendering the target value invalid. * + * * + * INPUT: target -- The target value to check. * + * * + * OUTPUT: bool; Is the target value legal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +bool Target_Legal(TARGET target) +{ + if (target == TARGET_NONE) return(false); + + ObjectClass * obj = As_Object(target, false); + if (obj) { + return(obj->IsActive); + } + return(true); +} + + +/*********************************************************************************************** + * As_Cell -- Converts a target value into a cell number. * + * * + * This routine is used to convert the target value specified, into a cell value. This is * + * necessary for find path and other procedures that need a cell value. * + * * + * INPUT: target -- The target value to convert to a cell value. * + * * + * OUTPUT: Returns with the target value expressed as a cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL As_Cell(TARGET target) +{ + return(Coord_Cell(As_Coord(target))); +} + + +/*********************************************************************************************** + * As_Coord -- Converts a target value into a coordinate value. * + * * + * This routine is used to convert the target value specified into a coordinate value. It * + * is necessary for those procedures that require a coordinate value. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with the target expressed as a COORDINATE value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + * 11/16/1994 JLB : Simplified. * + *=============================================================================================*/ +COORDINATE As_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + int v = Target_Value(target); + + int x = ((v & 0x0FFF) << 4) + 0x0008; + int y = (((v>>12) & 0x0FFF) << 4) + 0x0008; + return(XY_Coord(x, y)); + +// return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + assert(obj->IsActive); + return(obj->Target_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * * + * This routine will convert the specified target into a coordinate location. This location * + * is used when moving to the target specified. For cells, this is the center of the cell. * + * For special buildings that allow docking, it is the center location of the docking * + * bay. * + * * + * INPUT: target -- The target to convert into a coordinate value. * + * * + * OUTPUT: Returns with the docking coordinate of the target value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE As_Movement_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Docking_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * TargetClass::As_Object -- Converts a target into an object pointer. * + * * + * If the target represents an object of some type, then this routine will return a * + * pointer to the object. Otherwise it will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object that this target represents or NULL if it * + * doesn't represent a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +AbstractClass * xTargetClass::As_Abstract(bool check_active) const +{ + AbstractClass* abst = NULL; + switch ((RTTIType)*this) { + case RTTI_TEAM: + abst = Teams.Raw_Ptr(Value()); + break; + + case RTTI_BULLET: + abst = Bullets.Raw_Ptr(Value()); + break; + + case RTTI_OVERLAY: + abst = Overlays.Raw_Ptr(Value()); + break; + + case RTTI_SMUDGE: + abst = Smudges.Raw_Ptr(Value()); + break; + + case RTTI_UNIT: + abst = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + abst = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + abst = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + abst = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + abst = Aircraft.Raw_Ptr(Value()); + break; + + case RTTI_TERRAIN: + abst = Terrains.Raw_Ptr(Value()); + break; + + case RTTI_ANIM: + abst = Anims.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && abst != NULL && !abst->IsActive) { + abst = NULL; + } + return(abst); +} + + +AbstractTypeClass * xTargetClass::As_TypeClass(void) const +{ + switch ((RTTIType)*this) { + case RTTI_TEAMTYPE: + return(TeamTypes.Raw_Ptr(Value())); + + case RTTI_TRIGGERTYPE: + return(TriggerTypes.Raw_Ptr(Value())); + + case RTTI_BULLETTYPE: + return((BulletTypeClass *)&BulletTypeClass::As_Reference(BulletType(Value()))); + + case RTTI_OVERLAY: + return((OverlayTypeClass *)&OverlayTypeClass::As_Reference(OverlayType(Value()))); + + case RTTI_SMUDGE: + return((SmudgeTypeClass *)&SmudgeTypeClass::As_Reference(SmudgeType(Value()))); + + case RTTI_UNIT: + return((UnitTypeClass *)&UnitTypeClass::As_Reference(UnitType(Value()))); + + case RTTI_VESSEL: + return((VesselTypeClass *)&VesselTypeClass::As_Reference(VesselType(Value()))); + + case RTTI_BUILDING: + return((BuildingTypeClass *)&BuildingTypeClass::As_Reference(StructType(Value()))); + + case RTTI_INFANTRY: + return((InfantryTypeClass *)&InfantryTypeClass::As_Reference(InfantryType(Value()))); + + case RTTI_AIRCRAFT: + return((AircraftTypeClass *)&AircraftTypeClass::As_Reference(AircraftType(Value()))); + + case RTTI_TERRAIN: + return((TerrainTypeClass *)&TerrainTypeClass::As_Reference(TerrainType(Value()))); + + case RTTI_ANIM: + return((AnimTypeClass *)&AnimTypeClass::As_Reference(AnimType(Value()))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * * + * This routine is used to convert the target object into a pointer to a techno class * + * object. If the target doesn't specify a techno class object, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the techno class object that this target represents or * + * else it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TechnoClass * xTargetClass::As_Techno(bool check_active) const +{ + TechnoClass* techno = NULL; + switch ((RTTIType)*this) { + case RTTI_UNIT: + techno = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + techno = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + techno = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + techno = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + techno = Aircraft.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && techno != NULL && !techno->IsActive) { + techno = NULL; + } + return(techno); +} + + +ObjectClass * xTargetClass::As_Object(bool check_active) const +{ + ObjectClass* object = NULL; + switch ((RTTIType)*this) { + case RTTI_TERRAIN: + object = Terrains.Raw_Ptr(Value()); + break; + + case RTTI_SMUDGE: + object = Smudges.Raw_Ptr(Value()); + break; + + case RTTI_OVERLAY: + object = Overlays.Raw_Ptr(Value()); + break; + + case RTTI_BULLET: + object = Bullets.Raw_Ptr(Value()); + break; + + case RTTI_ANIM: + object = Anims.Raw_Ptr(Value()); + break; + + case RTTI_UNIT: + object = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + object = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + object = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + object = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + object = Aircraft.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && object != NULL && !object->IsActive) { + object = NULL; + } + return(object); +} + + + + +/*********************************************************************************************** + * As_Target -- Converts a cell into a target value. * + * * + * This routine will convert a cell into a target value. * + * * + * INPUT: cell -- The cell number that will be coerced into a target value. * + * * + * OUTPUT: Returns with the target value that this cell represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(CELL cell) +{ + int x = Cell_X(cell); + int y = Cell_Y(cell); + + x <<= 4; + y <<= 4; + + x += 0x0008; + y += 0x0008; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_Target -- Converts a coordinate into a target value. * + * * + * This routine is used to convert the specified coordinate into a target value. * + * * + * INPUT: coord -- The coordinate that is to be converted into a target value. * + * * + * OUTPUT: Returns with the target value that represents the coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(COORDINATE coord) +{ + int x = Coord_X(coord); + int y = Coord_Y(coord); + + x >>= 4; + y >>= 4; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * * + * This routine will conver the specified target number into a pointer to the techno * + * type class that it represents. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the TechnoTypeClass object that the target number * + * represents. If it doesn't represent that kind of object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * As_TechnoType(TARGET target) +{ + int val = Target_Value(target); + switch (Target_Kind(target)) { + case RTTI_INFANTRYTYPE: + return(&InfantryTypeClass::As_Reference(InfantryType(val))); + + case RTTI_UNITTYPE: + return(&UnitTypeClass::As_Reference(UnitType(val))); + + case RTTI_VESSELTYPE: + return(&VesselTypeClass::As_Reference(VesselType(val))); + + case RTTI_AIRCRAFTTYPE: + return(&AircraftTypeClass::As_Reference(AircraftType(val))); + + case RTTI_BUILDINGTYPE: + return(&BuildingTypeClass::As_Reference(StructType(val))); + + } + return(NULL); +} + + +/*********************************************************************************************** + * As_TriggerType -- Convert the specified target into a trigger type. * + * * + * This routine will conver the target number into a pointer to the trigger type it * + * represents. * + * * + * INPUT: target -- The target value to convert into a trigger type pointer. * + * * + * OUTPUT: Returns with a pointer to the trigger type object that the specified target value * + * represents. If it doesn't represent a trigger type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * As_TriggerType(TARGET target) +{ + if (Target_Kind(target) == RTTI_TRIGGERTYPE) { + return(TriggerTypes.Raw_Ptr(Target_Value(target))); + } + return(NULL); +} \ No newline at end of file diff --git a/REDALERT/TARGET.H b/REDALERT/TARGET.H new file mode 100644 index 000000000..cb9970fa6 --- /dev/null +++ b/REDALERT/TARGET.H @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TARGET.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARGET_H +#define TARGET_H + + +inline RTTIType Target_Kind(TARGET a) +{ + return(RTTIType(((TARGET_COMPOSITE &)a).Sub.Exponent)); +} + +inline unsigned Target_Value(TARGET a) +{ + return(((TARGET_COMPOSITE &)a).Sub.Mantissa); +} + +inline bool Is_Target_Team(TARGET a) {return (Target_Kind(a) == RTTI_TEAM);} +inline bool Is_Target_TeamType(TARGET a) {return (Target_Kind(a) == RTTI_TEAMTYPE);} +inline bool Is_Target_Trigger(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGER);} +inline bool Is_Target_TriggerType(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGERTYPE);} +inline bool Is_Target_Infantry(TARGET a) {return (Target_Kind(a) == RTTI_INFANTRY);} +inline bool Is_Target_Bullet(TARGET a) {return (Target_Kind(a) == RTTI_BULLET);} +inline bool Is_Target_Terrain(TARGET a) {return (Target_Kind(a) == RTTI_TERRAIN);} +inline bool Is_Target_Cell(TARGET a) {return (Target_Kind(a) == RTTI_CELL);} +inline bool Is_Target_Unit(TARGET a) {return (Target_Kind(a) == RTTI_UNIT);} +inline bool Is_Target_Vessel(TARGET a) {return (Target_Kind(a) == RTTI_VESSEL);} +inline bool Is_Target_Building(TARGET a) {return (Target_Kind(a) == RTTI_BUILDING);} +inline bool Is_Target_Template(TARGET a) {return (Target_Kind(a) == RTTI_TEMPLATE);} +inline bool Is_Target_Aircraft(TARGET a) {return (Target_Kind(a) == RTTI_AIRCRAFT);} +inline bool Is_Target_Animation(TARGET a) {return (Target_Kind(a) == RTTI_ANIM);} +inline bool Is_Target_Object(TARGET a) +{ + return (Target_Kind(a) == RTTI_TERRAIN || + Target_Kind(a) == RTTI_UNIT || + Target_Kind(a) == RTTI_VESSEL || + Target_Kind(a) == RTTI_INFANTRY || + Target_Kind(a) == RTTI_BUILDING || + Target_Kind(a) == RTTI_AIRCRAFT); +} + + +TARGET As_Target(CELL cell); +TARGET As_Target(COORDINATE coord); +//inline TARGET As_Target(CELL cell) {return (TARGET)(((unsigned)RTTI_CELL << TARGET_MANTISSA) | cell);} + +class UnitClass; +class BuildingClass; +class TechnoClass; +class TerrainClass; +class ObjectClass; +class InfantryClass; +class BulletClass; +class TriggerClass; +class TeamClass; +class TeamTypeClass; +class AnimClass; +class AircraftClass; +class VesselClass; +class CellClass; +class TriggerTypeClass; + +/* +** Must not have a constructor since Watcom cannot handle a class that has a constructor if +** that class object is in a union. Don't use this class for normal purposes. Use the TargetClass +** instead. The xTargetClass is only used in one module for a special reason -- keep it that way. +*/ +class xTargetClass +{ + protected: + + TARGET_COMPOSITE Target; + + public: + + // conversion operator to RTTIType + operator RTTIType (void) const {return(RTTIType(Target.Sub.Exponent));} + + // comparison operator + int operator == (xTargetClass & tgt) {return (tgt.Target.Target==Target.Target ? 1 : 0);} + + // conversion operator to regular TARGET type + TARGET As_TARGET(void) const {return(Target.Target);} + + unsigned Value(void) const {return(Target.Sub.Mantissa);}; + + void Invalidate(void) {Target.Sub.Exponent = RTTI_NONE;Target.Sub.Mantissa = -1;} + bool Is_Valid(void) const {return (Target.Sub.Exponent != RTTI_NONE);} + + TARGET As_Target(void) const {return(Target.Target);} + AbstractTypeClass * As_TypeClass(void) const; + AbstractClass * As_Abstract(bool check_active = true) const; + TechnoClass * As_Techno(bool check_active = true) const; + ObjectClass * As_Object(bool check_active = true) const; + CellClass * As_Cell(void) const; + + /* + ** Helper routines to combine testing for, and fetching a pointer to, the + ** type of object indicated. + */ + TriggerTypeClass * As_TriggerType(void) const {if (*this == RTTI_TRIGGERTYPE) return((TriggerTypeClass *)As_TypeClass());return(0);} + TeamTypeClass * As_TeamType(void) const {if (*this == RTTI_TEAMTYPE) return((TeamTypeClass *)As_TypeClass());return(0);} + TerrainClass * As_Terrain(bool check_active = true) const {if (*this == RTTI_TERRAIN) return((TerrainClass *)As_Abstract(check_active));return(0);} + BulletClass * As_Bullet(bool check_active = true) const {if (*this == RTTI_BULLET) return((BulletClass *)As_Abstract(check_active));return(0);} + AnimClass * As_Anim(bool check_active = true) const {if (*this == RTTI_ANIM) return((AnimClass *)As_Abstract(check_active));return(0);} + TeamClass * As_Team(bool check_active = true) const {if (*this == RTTI_TEAM) return((TeamClass *)As_Abstract(check_active));return(0);} + InfantryClass * As_Infantry(bool check_active = true) const {if (*this == RTTI_INFANTRY) return((InfantryClass *)As_Techno(check_active));return(0);} + UnitClass * As_Unit(bool check_active = true) const {if (*this == RTTI_UNIT) return((UnitClass *)As_Techno(check_active));return(0);} + BuildingClass * As_Building(bool check_active = true) const {if (*this == RTTI_BUILDING) return((BuildingClass *)As_Techno(check_active));return(0);} + AircraftClass * As_Aircraft(bool check_active = true) const {if (*this == RTTI_AIRCRAFT) return((AircraftClass *)As_Techno(check_active));return(0);} + VesselClass * As_Vessel(bool check_active = true) const {if (*this == RTTI_VESSEL) return((VesselClass *)As_Techno(check_active));return(0);} +}; + +/* +** This class only serves as a wrapper to the xTargetClass. This class must not define any members except +** for the constructors. This is because the xTargetClass is used in a union and this target object is +** used as its initializer. If this class had any extra members they would not be properly copied and +** communicated to the other machines in a network/modem game. Combining this class with xTargetClass would +** be more efficient, but Watcom doesn't allow class objects that have a constructor to be part of a union [even +** if the class object has a default constructor!]. +*/ +class TargetClass : public xTargetClass +{ + public: + + TargetClass(void) {Invalidate();} + TargetClass(NoInitClass const &) {} + TargetClass(RTTIType rtti, int id) { + Target.Sub.Exponent = rtti; + Target.Sub.Mantissa = id; + } + TargetClass(CELL cell) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = cell; + } + TargetClass(TARGET target); + TargetClass(AbstractClass const * ptr); + TargetClass(AbstractTypeClass const * ptr); + TargetClass(CellClass const * ptr); +}; + +#endif diff --git a/REDALERT/TCPIP.CPP b/REDALERT/TCPIP.CPP new file mode 100644 index 000000000..b6515ff48 --- /dev/null +++ b/REDALERT/TCPIP.CPP @@ -0,0 +1,906 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 20th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Member functions of the TcpipManagerClass which provides the Winsock * + * interface for C&C * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * TMC::Close -- restores any currently in use Winsock resources * + * TMC::Init -- Initialised Winsock for use. * + * TMC::Start_Server -- Initialise connection and start listening. * + * TMC::Read -- read any pending input from the stream socket * + * TMC::Write -- Send data via the Winsock streaming socket * + * TMC::Add_Client -- A client has requested to connect. * + * TMC::Message_Handler -- Message handler for Winsock. * + * TMC::Set_Host_Address -- Set the address of the host * + * TMC::Start_Client -- Start trying to connect to a game host * + * TMC::Close_Socket -- Close an opened Winsock socket. * + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" + + +/* +** Nasty globals +*/ +#ifndef WOLAPI_INTEGRATION +bool Server; //Is this player acting as client or server +#endif +TcpipManagerClass Winsock; //The object for interfacing with Winsock + + + +/*********************************************************************************************** + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +TcpipManagerClass::TcpipManagerClass(void) +{ + WinsockInitialised = FALSE; + Connected = FALSE; + UseUDP = TRUE; + SocketReceiveBuffer = 4096; + SocketSendBuffer = 4096; +} + + +/*********************************************************************************************** + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +TcpipManagerClass::~TcpipManagerClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * TMC::Close -- restores any currently in use Winsock resources * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + if (Async){ + WSACancelAsyncRequest(Async); + } + + /* + ** Close any open sockets + */ + if (ConnectSocket != INVALID_SOCKET){ + Close_Socket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + if (ListenSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + ListenSocket = INVALID_SOCKET; + } + + if (UDPSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + UDPSocket = INVALID_SOCKET; + } + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = FALSE; + Connected = FALSE; +} + + + +/*********************************************************************************************** + * TMC::Init -- Initialised Winsock for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (TRUE); + + /* + ** Initialise sockets to null + */ + ListenSocket = INVALID_SOCKET; + ConnectSocket =INVALID_SOCKET; + UDPSocket = INVALID_SOCKET; + + /* + ** Start WinSock, and fill in our WinSockData + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, &WinsockInfo); + if (rc != 0) { + return (FALSE); + } + + /* + ** Check the Winsock version number + */ + if ((WinsockInfo.wVersion & 0x00ff) != (version & 0x00ff) || + (WinsockInfo.wVersion >> 8) != (version >> 8)) { + return (FALSE); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = TRUE; + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Start_Server -- initialise out connection as the server. Start listening for clients. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:56PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Server(void) +{ + int i; + //struct sockaddr_in addr; + + Start_Client(); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; iCheck(); + + /* + ** Copy any outstanding incoming data to the buffer provided + */ + if (ReceiveBuffers[RXBufferTail].InUse){ + memcpy (buffer, ReceiveBuffers[RXBufferTail].Buffer, + MIN(ReceiveBuffers[RXBufferTail].DataLength, buffer_len)); + ReceiveBuffers[RXBufferTail].InUse = false; + + bytes_copied = MIN(ReceiveBuffers[RXBufferTail++].DataLength, buffer_len); + + RXBufferTail &= WS_NUM_RX_BUFFERS-1; + } + + return (bytes_copied); +} + + + +/*********************************************************************************************** + * TMC::Write -- Send data via the Winsock UDP socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Write(void *buffer, int buffer_len) +{ + char *source_buf = (char*) buffer; + + /* + ** Copy the data to one of the classes internal buffers + */ + if (!TransmitBuffers[TXBufferHead].InUse){ + memcpy (TransmitBuffers[TXBufferHead].Buffer, + buffer, + MIN (buffer_len, WS_INTERNET_BUFFER_LEN)); + TransmitBuffers[TXBufferHead].InUse = true; + TransmitBuffers[TXBufferHead++].DataLength = MIN(buffer_len, WS_INTERNET_BUFFER_LEN); + TXBufferHead &= WS_NUM_TX_BUFFERS-1; + } + + /* + ** Send a message to ourselves to start off the event + */ + if (UseUDP){ + SendMessage(MainWindow, WM_UDPASYNCEVENT, 0, (LONG)FD_WRITE); + }else{ + SendMessage(MainWindow, WM_ASYNCEVENT, 0, (LONG)FD_WRITE); + } + /* + ** Make sure the message loop gets called because all the Winsock notifications + ** are done via messages. + */ + Keyboard->Check(); +} + + + + + + +/*********************************************************************************************** + * TMC::Add_Client -- a client has requested to connect. Make the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if client was successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:02PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Add_Client(void) +{ + struct sockaddr_in addr; + int addrsize; + bool delay = TRUE; + + /* + ** Accept the connection. If there is an error then dont do anything else + */ + addrsize = sizeof(addr); + ConnectSocket = accept (ListenSocket, (LPSOCKADDR)&addr, &addrsize); + if (ConnectSocket == INVALID_SOCKET) { + //Show_Error("accept", WSAGetLastError()); + return(FALSE); + } + + /* + ** Set options for this socket + */ + setsockopt (ConnectSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&delay, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Save the clients address + */ + memcpy(&ClientIPAddress, &addr.sin_addr.s_addr,4); + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr,4); + + /* + ** Initiate an asynchronous host lookup by address. Our window will receive notification + ** when this is complete or when it times out. + */ + Async = WSAAsyncGetHostByAddr (MainWindow, WM_HOSTBYADDRESS, + (char const *)&addr.sin_addr, 4, PF_INET, &HostBuff[0], + MAXGETHOSTSTRUCT); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, + FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (ConnectSocket); + return(FALSE); + } + + /* + ** Create our UDP socket + */ + UDPSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (UDPSocket == INVALID_SOCKET) { + return (FALSE); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(UDPSocket, (LPSOCKADDR)&addr, sizeof(addr)) == + SOCKET_ERROR) { + Close_Socket(UDPSocket); + ConnectStatus = NOT_CONNECTING; + return(FALSE); + } + + /* + ** Set options for the UDP socket + */ + setsockopt (UDPSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (UDPSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (UDPSocket, MainWindow, WM_UDPASYNCEVENT, + FD_READ | FD_WRITE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (UDPSocket); + Close_Socket (ConnectSocket); + return(FALSE); + } + + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Message_Handler(HWND, UINT message, UINT , LONG lParam) +{ + struct hostent *hentry; + struct sockaddr_in addr; + int event; + int rc; + int addr_len; + + switch (message){ + + /* + ** Handle the GetHostByAddress result + */ + case WM_HOSTBYADDRESS: + + if (IsServer){ + /* + ** We are the server + */ + ConnectStatus = CONNECTING; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (&ClientName[0], hentry->h_name); + } + Async = 0; + return; + + }else{ + /* + ** We are the client + */ + ConnectStatus = CONTACTING_SERVER; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (Server.Name, hentry->h_name); + } + else { + Server.Name[0] = 0; + } + Async = 0; + return; + } + + + /* + ** Retrieve host by name: Start connecting now that we have the + ** address. + */ + case WM_HOSTBYNAME: + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + memcpy (&(Server.Addr.s_addr), hentry->h_addr, 4); + memcpy(&UDPIPAddress, hentry->h_addr, 4); + strcpy (Server.DotAddr, inet_ntoa(Server.Addr)); + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + Server.Name[0] = 0; + strcpy (Server.DotAddr, "????"); + ConnectStatus = SERVER_ADDRESS_LOOKUP_FAILED; + } + Async = 0; + return; + + + /* + ** Connection is ready - accept the client + */ + case WM_ACCEPT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + return; + } + if (Add_Client()) { + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + } + return; + + + + /* + ** Handle UDP packet events + */ + case WM_UDPASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + + case FD_READ: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + addr_len = sizeof(addr); + rc = recvfrom(UDPSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0, + (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(UDPSocket); + return; + } + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr, 4); + Copy_To_In_Buffer(rc); + return; + + + case FD_WRITE: + if (UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, &UDPIPAddress, 4); + + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (TransmitBuffers[TXBufferTail].InUse){ + rc = sendto(UDPSocket, + TransmitBuffers[TXBufferTail].Buffer, + TransmitBuffers[TXBufferTail].DataLength, + 0, + (LPSOCKADDR)&addr, + sizeof (addr)); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(UDPSocket); + } + break; + } + TransmitBuffers[TXBufferTail++].InUse = false; + TXBufferTail &= WS_NUM_TX_BUFFERS-1; + } + return; + } + } + + + + /* + ** Handle the asynchronous event callbacks + */ + case WM_ASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + /* + ** FD_CLOSE: the client has gone away. Remove the client from our system. + */ + case FD_CLOSE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0 && rc != WSAECONNRESET) { + ConnectStatus = CONNECTION_LOST; + return; + } + if (Async != 0) { + WSACancelAsyncRequest(Async); + } + WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, 0); + Close_Socket (ConnectSocket); + ConnectSocket = INVALID_SOCKET; + //Connected = FALSE; + ConnectStatus = CONNECTION_LOST; + break; + + /* + ** FD_CONNECT: A connection was made, or an error occurred. + */ + case FD_CONNECT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_CONNECT; + return; + } + + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + return; + + } + } +} + + + +/*********************************************************************************************** + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer to our internal buffer * + * * + * * + * * + * INPUT: bytes to copy * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:17PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Copy_To_In_Buffer(int bytes) +{ + if (!ReceiveBuffers[RXBufferHead].InUse){ + memcpy (ReceiveBuffers[RXBufferHead].Buffer, ReceiveBuffer, MIN(bytes, WS_INTERNET_BUFFER_LEN)); + ReceiveBuffers[RXBufferHead].InUse = true; + ReceiveBuffers[RXBufferHead++].DataLength = MIN(bytes, WS_INTERNET_BUFFER_LEN); + RXBufferHead &= WS_NUM_RX_BUFFERS-1; + } +} + + + +/*********************************************************************************************** + * TMC::Set_Host_Address -- Set the address of the host game we want to connect to * + * * + * * + * * + * INPUT: ptr to address string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Set_Host_Address(char *address) +{ + strcpy(HostAddress, address); +} + + + +/*********************************************************************************************** + * TMC::Start_Client -- Start trying to connect to a game host * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Client(void) +{ + struct sockaddr_in addr; + bool delay = true; + int i; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; iIsActive || object->Strength == 0) return(false); + + /* + ** If the object is in limbo, then it isn't an active team member either. However, if the + ** scenario init flag is on, then it is probably a reinforcement issue or scenario + ** creation situation. In such a case, the members are considered active because they need to + ** be given special orders and treatment. + */ + if (!ScenarioInit && object->IsInLimbo) return(false); + + /* + ** Nothing eliminated this object from being considered an active member of the team (i.e., + ** "breathing"), then return that it is ok. + */ + return(true); +} + + +/*********************************************************************************************** + * _Is_It_Playing -- Determines if unit is active and an initiated team member. * + * * + * Use this routine to determine if the specified unit is an active participant of the * + * team. When a unit is first recruited to the team, it must travel to the team's location * + * before it can become an active player. Call this routine to determine if the specified * + * unit can be considered an active player. * + * * + * INPUT: object -- Pointer to the object that is to be checked to see if it is an * + * active player. * + * * + * OUTPUT: bool; Is the specified unit an active, living, initiated member of the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +static inline bool _Is_It_Playing(FootClass const * object) +{ + /* + ** If the object is not active, then it certainly can be a participating member of the + ** team. + */ + if (!_Is_It_Breathing(object)) return(false); + + /* + ** Only members that have been "Initiated" are considered "playing" participants of the + ** team. This results in the team members that are racing to regroup with the team (i.e., + ** not initiated), will continue to catch up to the team even while the initiated team members + ** carry out their team specific orders. + */ + if (!object->IsInitiated && object->What_Am_I() != RTTI_AIRCRAFT) return(false); + + /* + ** If it reaches this point, then nothing appears to disqualify the specified object from + ** being considered an active playing member of the team. In this case, return that + ** information. + */ + return(true); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TeamClass::Debug_Dump -- Displays debug information about the team. * + * * + * This routine will display information about the team. This is useful for debugging * + * purposes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the debugging information will * + * be displayed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 20);mono->Printf("%8.8s", Class->IniName); + mono->Set_Cursor(10, 20);mono->Printf("%3d", Total); + mono->Set_Cursor(17, 20);mono->Printf("%3d", Quantity[Class->ID]); + if (CurrentMission != -1) { + mono->Set_Cursor(1, 22); + mono->Printf("%-29s", Class->MissionList[CurrentMission].Description(CurrentMission)); + } + mono->Set_Cursor(40, 20);mono->Printf("%-10s", FormationName[Formation]); + mono->Set_Cursor(22, 20);mono->Printf("%08X", Zone); + mono->Set_Cursor(31, 20);mono->Printf("%08X", Target); + + mono->Fill_Attrib(53, 20, 12, 1, IsUnderStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 21, 12, 1, IsFullStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 22, 12, 1, IsHasBeen ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(66, 20, 12, 1, IsMoving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 21, 12, 1, IsForcedActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 22, 12, 1, IsReforming ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * * + * This routine clears out the team object array in preparation for starting a new * + * scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Init(void) +{ + Teams.Free_All(); +} + + +/*********************************************************************************************** + * TeamClass::operator new -- Allocates a team object. * + * * + * This routine will allocate a team object from the team object pool. * + * * + * INPUT: size -- The size of the requested allocation. * + * * + * OUTPUT: Returns with a pointer to the freshly allocated team object. If an allocation * + * could not be made, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * TeamClass::operator new(size_t) +{ + void * ptr = Teams.Allocate(); + if (ptr != NULL) { + ((TeamClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TeamClass::operator delete -- Deallocates a team object. * + * * + * This routine will return a team object to the team object pool. * + * * + * INPUT: ptr -- Pointer to the team object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((TeamClass *)ptr)->IsActive = false; + } + Teams.Free((TeamClass *)ptr); +} + + +/*********************************************************************************************** + * TeamClass::~TeamClass -- Team object destructor. * + * * + * This routine is called when a team object is destroyed. It handles updating the total * + * number count for this team object type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 07/04/1996 JLB : Keeps trigger if trigger still attached to objects. * + *=============================================================================================*/ +TeamClass::~TeamClass(void) +{ + if (GameActive && Class.Is_Valid()) { + while (Member != NULL) { + Remove(Member); + } + Class->Number--; + + /* + ** When the team dies, any trigger associated with it, dies as well. This will only occur + ** if there are no other objects linked to this trigger. Only player reinforcement + ** members that broke off of the team earlier will have this occur. + */ + if (Trigger.Is_Valid()) { + if (Trigger->AttachCount == 0) { + delete (TriggerClass *)Trigger; + } + Trigger = NULL; + } + } +} + + +/*********************************************************************************************** + * TeamClass::TeamClass -- Constructor for the team object type. * + * * + * This routine is called when the team object is created. * + * * + * INPUT: type -- Pointer to the team type to make this team object from. * + * * + * owner -- The owner of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : + AbstractClass(RTTI_TEAM, Teams.ID(this)), + Class((TeamTypeClass *)type), + House(owner), + IsForcedActive(false), + IsHasBeen(false), + IsFullStrength(false), + IsUnderStrength(true), + IsReforming(false), + IsLagging(false), + IsAltered(true), + JustAltered(false), + IsMoving(false), + IsNextMission(true), + IsLeaveMap(false), + Suspended(false), + Trigger(NULL), + Zone(TARGET_NONE), + ClosestMember(TARGET_NONE), + MissionTarget(TARGET_NONE), + Target(TARGET_NONE), + Total(0), + Risk(0), + Formation(FORMATION_NONE), + SuspendTimer(0), + CurrentMission(-1), + TimeOut(0), + Member(0) +{ + assert(Class); + assert(Class->IsActive); + assert(Class->ClassCount > 0); + + if (owner == NULL) { + House = HouseClass::As_Pointer(Class->House); + } + + memset(Quantity, 0, sizeof(Quantity)); + if (Class->Origin != -1) { + Zone = ::As_Target(Scen.Waypoint[Class->Origin]); + } + Class->Number++; + + /* + ** If there is a trigger tightly associated with this team, then + ** create an instance of that trigger and attach it to the team. + */ + if (Class->Trigger.Is_Valid()) { + Trigger = new TriggerClass(Class->Trigger); + } +} + + +/*************************************************************************** + * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Assign_Mission_Target(TARGET new_target) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** First go through and find anyone who is currently targeting + ** the old mission target and clear their Tarcom. + */ + FootClass * unit = Member; + if (MissionTarget != TARGET_NONE) { + while (unit != NULL) { + bool tar = (unit->TarCom == MissionTarget); + bool nav = (unit->NavCom == MissionTarget); + if (tar || nav) { + + /* + ** If the unit was doing something related to the team mission + ** then we kick him into guard mode so that he is easy to change + ** missions for. + */ + unit->Assign_Mission(MISSION_GUARD); + + /* + ** If the unit's tarcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (nav) { + unit->Assign_Destination(TARGET_NONE); + } + + /* + ** If the unit's navcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (tar) { + unit->Assign_Target(TARGET_NONE); + } + } + unit = unit->Member; + } + } + + /* + ** If there is not currently an override on the current mission target + ** then assign both MissionTarget and Target to the new target. If + ** there is an override, allow the team to keep fighting the override but + ** make sure they pick up on the new mission when they are ready. + */ + if (Target == MissionTarget || !Target_Legal(Target)) { + MissionTarget = Target = new_target; + } else { + MissionTarget = new_target; + } +} + + +/*********************************************************************************************** + * TeamClass::AI -- Process team logic. * + * * + * General purpose team logic is handled by this routine. It should be called once per * + * active team per game tick. This routine handles recruitment and assigning orders to * + * member units. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/06/1995 JLB : Choreographed gesture. * + *=============================================================================================*/ +void TeamClass::AI(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + int desired = 0; + int old_under = IsUnderStrength; + int old_full = IsFullStrength; + + /* + ** If the team has been suspended then we need to check if it's time for + ** us to reactivate the team. If not, no team logic will be processed + ** for this team. + */ + if (Suspended) { + if (SuspendTimer != 0) { + return; + } + Suspended = false; + } + + /* + ** If this team senses that its composition has been altered, then it should + ** recalculate the under strength and full strength flags. + */ + if (IsAltered) { + + /* + ** Figure out the total number of objects that this team type requires. + */ + for (int index = 0; index < Class->ClassCount; index++) { + desired += Class->Members[index].Quantity; + } + assert(desired != 0); + + if (Total) { + IsFullStrength = (Total == desired); + if (IsFullStrength) { + IsHasBeen = true; + } + + /* + ** Reinforceable teams will revert (or snap out of) the under strength + ** mode when the members transition the magic 1/3 strength threshold. + */ + if (Class->IsReinforcable) { + if (desired > 2) { + IsUnderStrength = (Total <= desired / 3); + } else { + IsUnderStrength = (Total < desired); + } + } else { + + /* + ** Teams that are not flagged as reinforceable are never considered under + ** strength if the team has already started its main mission. This + ** ensures that once the team has started, it won't dally to pick up + ** new members. + */ + IsUnderStrength = !IsHasBeen; + } + + IsAltered = JustAltered = false; + } else { + IsUnderStrength = true; + IsFullStrength = false; + Zone = TARGET_NONE; + + /* + ** A team that exists on the player's side is automatically destroyed + ** when there are no team members left. This team was created as a + ** result of reinforcement logic and no longer needs to exist when there + ** are no more team members. + */ + if (IsHasBeen || Session.Type != GAME_NORMAL) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + } + + /* + ** If the team has gone from under strength to no longer under + ** strength than the team needs to reform. + */ + if (old_under != IsUnderStrength) { + IsReforming = true; + } + } + + /* + ** If the team is under strength, then flag it to regroup. + */ + if (IsMoving && IsUnderStrength) { + IsMoving = false; + CurrentMission = -1; + if (Total) { + Calc_Center(Zone, ClosestMember); + + /* + ** When a team is badly damaged and needs to regroup it should + ** pick a friendly building to go and regroup at. Its first preference + ** should be somewhere near repair factory. If it cannot find a repair + ** factory then it should pick another structure that is friendly to + ** its side. + */ + CELL dest = As_Cell(Zone); + int max = 0x7FFFFFFF; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == House && b->Class->PrimaryWeapon == NULL) { + CELL cell = Coord_Cell(b->Center_Coord()); + int dist = ::Distance(b->Center_Coord(), As_Coord(Zone)) * (Map.Cell_Threat(cell, House->Class->House) + 1); + + if (*b == STRUCT_REPAIR) { + dist /= 2; + } + if (dist < max) { + cell = Fetch_A_Leader()->Safety_Point(As_Cell(Zone), cell, 2, 4); +// cell = Member->Safety_Point(As_Cell(Zone), cell, 2, 4); + if (cell != -1) { + max = dist; + dest = cell; + } + } + } + } + + // Should calculate a regroup location. + Target = ::As_Target(dest); + Coordinate_Move(); + return; + } else { + Zone = TARGET_NONE; + } + } + + /* + ** Flag this team into action when it gets to full strength. Human owned teams are only + ** used for reinforcement purposes -- always consider them at full strength. + */ + if (!IsMoving && (IsFullStrength || IsForcedActive)) { + IsMoving = true; + IsHasBeen = true; + IsUnderStrength = false; + + /* + ** Infantry can do a gesture when they start their mission. Pick + ** a gesture at random. + */ + FootClass * techno = Member; + DoType doaction = Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2; + while (techno) { + if (_Is_It_Breathing(techno) && techno->What_Am_I() == RTTI_INFANTRY) { + ((InfantryClass *)techno)->Do_Action(doaction); + } + + if (IsReforming || IsForcedActive) { + techno->IsInitiated = true; + } + + techno = techno->Member; + } + CurrentMission = -1; + IsNextMission = true; +// IsForcedActive = false; + } + + /* + ** If the team is moving or if there is no center position for + ** the team, then the center position must be recalculated. + */ + if (IsReforming || IsMoving || Zone == TARGET_NONE || ClosestMember == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Try to recruit members if there is room to do so for this team. + ** Only try to recruit members for a non player controlled team. + */ + if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((!House->IsHuman || !IsHasBeen) && Session.Type == GAME_NORMAL)) { +// if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((/*!House->IsHuman ||*/ !IsHasBeen) && Session.Type == GAME_NORMAL)) { + for (int index = 0; index < Class->ClassCount; index++) { + if (Quantity[index] < Class->Members[index].Quantity) { + Recruit(index); + } + } + } + + /* + ** If there are no members of the team and the team has reached + ** full strength at one time, then delete the team. + */ + if (Member == NULL && IsHasBeen) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + + /* + ** If the mission should be advanced to the next entry, then do so at + ** this time. Various events may cause the mission to advance, but it + ** all boils down to the following change-mission code. + */ + if (IsMoving && !IsReforming && IsNextMission) { + IsNextMission = false; + CurrentMission++; + if (CurrentMission < Class->MissionCount) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + TimeOut = mission->Data.Value * (TICKS_PER_MINUTE/10); + Target = TARGET_NONE; + switch (mission->Mission) { + + case TMISSION_MOVECELL: + Assign_Mission_Target(::As_Target((CELL)(mission->Data.Value))); + break; + + case TMISSION_MOVE: + if ((unsigned)mission->Data.Value < WAYPT_COUNT && Member != NULL) { + FootClass * leader = Fetch_A_Leader(); + CELL movecell = Scen.Waypoint[mission->Data.Value]; + if (!Is_Leaving_Map()) { + if (leader->Can_Enter_Cell(movecell) != MOVE_OK) { + movecell = Map.Nearby_Location(movecell, leader->Techno_Type_Class()->Speed); + } + } + Assign_Mission_Target(::As_Target(movecell)); + Target = ::As_Target(movecell); + } + break; + + case TMISSION_ATT_WAYPT: + case TMISSION_PATROL: + case TMISSION_SPY: + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + break; + + case TMISSION_ATTACKTARCOM: + Assign_Mission_Target(mission->Data.Value); + break; + + case TMISSION_UNLOAD: + default: + Assign_Mission_Target(TARGET_NONE); + break; + } + } else { + delete this; + return; + } + } + + /* + ** Perform mission of the team. This depends on the mission list. + */ + if (Member != NULL && IsMoving && !IsReforming && !IsUnderStrength) { + + /* + ** If the current Target has been dealt with but the mission target + ** has not, then the current target needs to be reset to the mission + ** target. + */ + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** If the current mission is one that times out, then check for + ** this case. If it has timed out then advance to the next + ** mission in the list or disband the team. + */ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; +// FootClass * member = Member; + + switch (mission->Mission) { + case TMISSION_PATROL: + TMission_Patrol(); + break; + + case TMISSION_FORMATION: + TMission_Formation(); + break; + + case TMISSION_ATTACKTARCOM: + case TMISSION_ATTACK: + TMission_Attack(); + break; + + case TMISSION_LOAD: + TMission_Load(); + break; + + case TMISSION_DEPLOY: + TMission_Deploy(); + break; + + case TMISSION_UNLOAD: + TMission_Unload(); + break; + + case TMISSION_MOVE: + case TMISSION_MOVECELL: + Coordinate_Move(); + break; + + /* + ** All members of this team become invulnerable as if by magic. + */ + case TMISSION_INVULNERABLE: + TMission_Invulnerable(); + break; + + case TMISSION_GUARD: + Coordinate_Regroup(); + break; + + case TMISSION_DO: + Coordinate_Do(); + break; + + case TMISSION_SET_GLOBAL: + TMission_Set_Global(); + break; + + case TMISSION_ATT_WAYPT: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } else { + Coordinate_Attack(); + } + break; + + case TMISSION_SPY: + TMission_Spy(); + break; + + case TMISSION_HOUND_DOG: + TMission_Follow(); + break; + + case TMISSION_LOOP: + TMission_Loop(); + break; + } + + /* + ** Check for mission time out condition. If the mission does in fact time out, then + ** flag it so that the team mission list will advance. + */ + switch (mission->Mission) { +// case TMISSION_UNLOAD: + case TMISSION_GUARD: + if (TimeOut == 0) { + IsNextMission = true; + } + break; + } + + } else { + + if (IsMoving) { + IsReforming = !Coordinate_Regroup(); + } else { + Coordinate_Move(); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Add -- Adds specified object to team. * + * * + * Use this routine to add the specified object to the team. The object is checked to make * + * sure that it can be assigned to the team. If it can't, then the object will be left * + * alone and false will be returned. * + * * + * INPUT: obj -- Pointer to the object that is to be assigned to this team. * + * * + * OUTPUT: bool; Was the unit added to the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation flag setup. * + * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * + *=============================================================================================*/ +bool TeamClass::Add(FootClass * obj) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + if (!obj) return(false); + + int typeindex; + if (!Can_Add(obj, typeindex)) return(false); + + /* + ** All is ok to add the object to the team, but if the object is already part of + ** another team, then it must be removed from that team first. + */ + if (obj->Team.Is_Valid()) { + obj->Team->Remove(obj); + } + + /* + ** Actually add the object to the team. + */ + Quantity[typeindex]++; + obj->IsInitiated = (Member == NULL); + obj->Member = Member; + Member = obj; + obj->Team = this; + + /* + ** If a common trigger is designated for this team type, then attach the + ** trigger to this team member. + */ + if (Trigger.Is_Valid()) { + obj->Attach_Trigger(Trigger); + } + + Total++; + Risk += obj->Risk(); + if (Zone == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Return with success, since the object was added to the team. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Can_Add -- Determines if the specified object can be added to team. * + * * + * This routine will examine the team and determine if the specified object can be * + * properly added to this team. This is a security check to filter out those objects that * + * should not be added because of conflicting priorities or other restrictions. * + * * + * INPUT: obj -- Pointer to the candidate object that is being checked for legal * + * adding to this team. * + * * + * typeindex-- The class index number (according to the team type's class array) that * + * the candidate object is classified as. The routine processes much * + * faster if you can provide this information, but if you don't, the * + * routine will figure it out. * + * * + * OUTPUT: bool; Can the specified object be added to this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Can_Add(FootClass * obj, int & typeindex) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Trying to add the team member to itself is an error condition. + */ + if (obj->Team == this) { + return(false); + } + + /* + ** The object must be active, a member of this house. A special dispensation is given to + ** units that are in radio contact. It is presumed that they are very busy and should + ** not be disturbed. + */ + if (!_Is_It_Breathing(obj) || obj->In_Radio_Contact() || obj->House != House) { + return(false); + } + + /* + ** If the object is doing some mission that precludes it from joining + ** a team then don't add it. + */ + if (obj->Mission != MISSION_NONE && !MissionClass::Is_Recruitable_Mission(obj->Mission)) { + return(false); + } + + /* + ** If this object is part of another team, then check to make sure that it + ** is permitted to leave the other team in order to join this one. If not, + ** then no further processing is allowed -- bail. + */ + if (obj->Team.Is_Valid() && (obj->Team->Class->RecruitPriority >= Class->RecruitPriority)) { + return(false); + } + + /* + ** Aircraft that have no ammo for their weapons cannot be recruited into a team. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT && obj->Techno_Type_Class()->PrimaryWeapon != NULL && !obj->Ammo) { + return(false); + } + + /* + ** Search for the exact member index that the candidate object matches. + ** If no match could be found, then adding the object to the team cannot + ** occur. + */ + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + if (typeindex == Class->ClassCount) { + return(false); + } + + /* + ** If the team is already full of this type, then adding the object is not allowed. + ** Return with a failure flag in this case. + */ + if (Quantity[typeindex] >= Class->Members[typeindex].Quantity) { + return(false); + } + + return(true); +} + + + +/*********************************************************************************************** + * TeamClass::Remove -- Removes the specified object from the team. * + * * + * Use this routine to remove an object from a team. Objects removed from the team are * + * then available to be recruited by other teams, or even by the same team at a later time. * + * * + * INPUT: obj -- Pointer to the object that is to be removed from this team. * + * * + * typeindex-- Optional index of where this object type is specified in the type * + * type class. This parameter can be omitted. It only serves to make * + * the removal process faster. * + * * + * OUTPUT: bool; Was the object removed from this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation tracking and team captain selection. * + *=============================================================================================*/ +bool TeamClass::Remove(FootClass * obj, int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Make sure that the object is in fact a member of this team. If not, then it can't + ** be removed. Return success because the end result is the same. + */ + if (this != obj->Team) { + return(true); + } + + /* + ** Detach the common trigger for this team type. Only current and active members of the + ** team have that trigger attached. The exception is for player team members that + ** get removed from a reinforcement team. + */ + if (obj->Trigger == Trigger && !obj->House->IsPlayerControl) { + obj->Attach_Trigger(NULL); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. The + ** team type class will not be set if the appropriate type could not be found + ** for this object. This indicates that the object was illegally added. Continue to + ** process however, since removing this object from the team is a good idea. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + } + + /* + ** Decrement the counter for the team class. There is now one less of this object type. + */ + if (typeindex < Class->ClassCount) { + Quantity[typeindex]--; + } + + /* + ** Actually remove the object from the team. Scan through the team members + ** looking for the one that matches the one specified. If it is found, it + ** is unlinked from the member chain. During this scan, a check is made to + ** ensure that at least one remaining member is still initiated. If not, then + ** a new team captain must be chosen. + */ + bool initiated = false; + FootClass * prev = 0; + FootClass * curr = Member; + bool found = false; + while (curr != NULL && (!found || !initiated)) { + if (curr == obj) { + if (prev != NULL) { + prev->Member = curr->Member; + } else { + Member = curr->Member; + } + FootClass * temp = curr->Member; + curr->Member = 0; + curr->Team = 0; + curr->SuspendedMission = MISSION_NONE; + curr->SuspendedNavCom = TARGET_NONE; + curr->SuspendedTarCom = TARGET_NONE; + curr = temp; + Total--; + found = true; + Risk -= obj->Risk(); + continue; + } + + /* + ** If this (remaining) member is initiated, then keep a record of this. + */ + initiated |= curr->IsInitiated; + + prev = curr; + curr = curr->Member; + } + + /* + ** A unit that breaks off of a team will enter idle mode. + */ + obj->Enter_Idle_Mode(); + + /* + ** If, after removing the team member, there are no initiated members left + ** in the team, then just make the first remaining member of the team the + ** team captain. Mark the center location of the team as invalid so that + ** it will be centered around the captain. + */ + if (!initiated && Member != NULL) { + Member->IsInitiated = true; + Zone = TARGET_NONE; + } + + /* + ** Must record that the team composition has changed. At the next opportunity, + ** the team members will be counted and appropriate AI adjustments made. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * * + * This routine will take the given index ID and scan for available objects of that type * + * to recruit to the team. Recruiting will continue until that object type has either * + * been exhausted or if the team's requirement for that type has been filled. * + * * + * INPUT: typeindex -- The index for the object type to recruit. The index is used to * + * look into the type type's array of object types that make up this * + * team. * + * * + * OUTPUT: Returns with the number of objects added to this team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 04/10/1995 JLB : Scans for units too. * + *=============================================================================================*/ +int TeamClass::Recruit(int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + COORDINATE center = As_Coord(Zone); + + if (Class->Origin != -1) { + center = Cell_Coord(Scen.Waypoint[Class->Origin]); + } + + int added = 0; // Total number added to team. + + /* + ** Quick check to see if recruiting is really allowed for this index or not. + */ + if (Class->Members[typeindex].Quantity > Quantity[typeindex]) { + switch (Class->Members[typeindex].Class->What_Am_I()) { + + /* + ** For infantry objects, sweep through the infantry in the game looking for + ** ones owned by the house that owns the team. When found, try to add. + */ + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + { + InfantryClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + int d = infantry->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(infantry, typeindex)) { + best = infantry; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + { + AircraftClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + int d = aircraft->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(aircraft, typeindex)) { + best = aircraft; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_UNITTYPE: + case RTTI_UNIT: + { + UnitClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + int d = unit->Distance(center); + + if (unit->House == House && unit->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(unit, typeindex)) { + best = unit; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + { + VesselClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + int d = vessel->Distance(center); + + if (vessel->House == House && vessel->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(vessel, typeindex)) { + best = vessel; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + } + } + return(added); +} + + +/*********************************************************************************************** + * TeamClass::Detach -- Removes specified target from team tracking. * + * * + * When a target object is about to be removed from the game (e.g., it was killed), then * + * any team that is looking at that target must abort from that target. * + * * + * INPUT: target -- The target object that is going to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Detach(TARGET target, bool ) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** If the target to detach matches the target of this team, then remove + ** the target from this team's Tar/Nav com and let the chips fall + ** where they may. + */ + if (Target == target) { + Target = TARGET_NONE; + } + if (MissionTarget == target) { + MissionTarget = TARGET_NONE; + } + if (Trigger.Is_Valid() && Trigger->As_Target() == target) { + Trigger = 0; + } +} + + +/*********************************************************************************************** + * TeamClass::Calc_Center -- Determines average location of team members. * + * * + * Use this routine to calculate the "center" location of the team. This is the average * + * position of all members of the team. Using this center value it is possible to tell * + * if a team member is too far away and where to head to in order to group up. * + * * + * INPUT: center -- Average center target location of the team. Only initiated members * + * will be considered. * + * * + * close_member--Location (as target) of the unit that is closest to the team's * + * target. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Calc_Center(TARGET & center, TARGET & close_member) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Presume there is no center. This will be confirmed in the following scanning + ** operation. + */ + close_member = TARGET_NONE; + center = TARGET_NONE; + + FootClass const * team_member = Member; // Working team member pointer. + + /* + ** If there are no members of the team, then there can be no center point of the team. + */ + if (team_member == NULL) return; + + /* + ** If the team is supposed to follow a nearby friendly unit, then the + ** team's "center" will actually be that unit. Otherwise, calculated the + ** average center location for the team. + */ + if (Class->MissionList[CurrentMission].Mission == TMISSION_HOUND_DOG) { + + /* + ** First pick a member of the team. The closest friendly object to that member + ** will be picked. + */ + if (!team_member) return; + + FootClass const * closest = NULL; // Current closest friendly object. + int distance = -1; // Record of last closest distance calc. + + /* + ** Scan through all vehicles. + */ + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + FootClass const * trial_unit = Units.Ptr(unit_index); + + if (_Is_It_Breathing(trial_unit) && trial_unit->House->Is_Ally(House) && trial_unit->Team != this) { + int trial_distance = team_member->Distance(trial_unit); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_unit; + } + } + } + + /* + ** Scan through all infantry. + */ + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + FootClass const * trial_infantry = Infantry.Ptr(infantry_index); + + if (_Is_It_Breathing(trial_infantry) && trial_infantry->House->Is_Ally(House) && trial_infantry->Team != this) { + int trial_distance = team_member->Distance(trial_infantry); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_infantry; + } + } + } + + /* + ** Scan through all vessels. + */ + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + FootClass const * trial_vessel = Vessels.Ptr(vessel_index); + + if (_Is_It_Breathing(trial_vessel) && trial_vessel->House->Is_Ally(House) && trial_vessel->Team != this) { + int trial_distance = team_member->Distance(trial_vessel); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_vessel; + } + } + } + + /* + ** Set the center location as actually the friendly object that is closest. If there + ** is no friendly object, then don't set any center location at all. + */ + if (closest) { + center = closest->As_Target(); + close_member = Member->As_Target(); + } + + } else { + + long x = 0; // Accumulated X coordinate. + long y = 0; // Accumulated Y coordinate. + int dist = 0; // Closest recorded distance to team target. + int quantity = 0; // Number of team members counted. + FootClass const * closest = 0; // Closest member to target. + + /* + ** Scan through all team members and accumulate the X and Y component of their + ** location. Only team members that are active will be considered. Also keep + ** track of the team member that is closest to the team's target. + */ + while (team_member != NULL) { + if (_Is_It_Playing(team_member)) { + + /* + ** Accumulate X and Y components of qualified team members. + */ + x += Coord_X(team_member->Coord); + y += Coord_Y(team_member->Coord); + quantity++; + + /* + ** Keep a record of the team member that is nearest to the team's + ** target. + */ + int try_dist = team_member->Distance(Target); + if (!dist || try_dist < dist) { + dist = try_dist; + closest = team_member; + } + } + + team_member = team_member->Member; + } + + /* + ** If there were any qualifying members, then the team's center point can be + ** determined. + */ + if (quantity) { + x /= quantity; + y /= quantity; + COORDINATE coord = XY_Coord((int)x, (int)y); + center = ::As_Target(coord); + + + /* + ** If the center location is impassable, then just pick the location of + ** one of the team members. + */ + if (!closest->Can_Enter_Cell(As_Cell(center))) { +// if (Class->Origin != -1) { +// center = ::As_Target(Scen.Waypoint[Class->Origin]); +// } else { + center = ::As_Target(Coord_Cell(closest->Center_Coord())); +// } + } + } + + /* + ** Record the position of the closest member to the team's target and + ** that will be used as the regroup point. + */ + if (closest != NULL) { + close_member = ::As_Target(Coord_Cell(closest->Center_Coord())); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * * + * This routine is used when a team member takes damage. Usually the team will react in * + * some fashion to the attack. This reaction can range from running away to assigning this * + * new target as the team's target. * + * * + * INPUT: obj -- The team member that was damaged. * + * * + * result -- The severity of the damage taken. * + * * + * source -- The perpetrator of the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if ((result != RESULT_NONE) && (!Class->IsSuicide)) { + if (!IsMoving) { + + // TCTCTC + // Should run to a better hiding place or disband into a group of hunting units. + + } else { + /* + ** Respond to the attack, but not if we're an aircraft or a LST. + */ + if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT && (Member->What_Am_I() != RTTI_VESSEL || *(VesselClass *)((FootClass *)Member) != VESSEL_TRANSPORT)) { + if (Target != source->As_Target()) { + + /* + ** Don't change target if the team's target is one that can fire as well. There is + ** no point in endlessly shuffling between targets that have firepower. + */ + if (Target_Legal(Target)) { + TechnoClass * techno = As_Techno(Target); + + if (techno && ((TechnoTypeClass const &)techno->Class_Of()).PrimaryWeapon != NULL) { + if (techno->In_Range(As_Coord(Zone), 0)) { + return; + } + } + } + + /* + ** Don't change target to aggressor if the aggressor cannot normally be attacked. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT || (source->What_Am_I() == RTTI_VESSEL && (Member->What_Am_I() == RTTI_UNIT || Member->What_Am_I() == RTTI_INFANTRY))) { + return; + } + + Target = source->As_Target(); + } + } + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * * + * This function is called when the team knows what it should attack. This routine will * + * give the necessary orders to the members of the team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Attack(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** Check if they're attacking a cell. If the contents of the cell are + ** a bridge or a building/unit/techno, then it's a valid target. Otherwise, + ** the target is invalid. This only applies to non-aircraft teams. An aircraft team + ** can "attack" an empty cell and this is perfectly ok (paratrooper drop and parabombs + ** are prime examples). + */ + if (Is_Target_Cell(Target) && Member != NULL && Fetch_A_Leader()->What_Am_I() != RTTI_AIRCRAFT) { + CellClass *cellptr = &Map[As_Cell(Target)]; + TemplateType tt = cellptr->TType; + if (cellptr->Cell_Object()) { + Target = cellptr->Cell_Object()->As_Target(); + } else { + if (tt != TEMPLATE_BRIDGE1 && tt != TEMPLATE_BRIDGE2 && + tt != TEMPLATE_BRIDGE1H && tt != TEMPLATE_BRIDGE2H && + tt != TEMPLATE_BRIDGE_1A && tt != TEMPLATE_BRIDGE_1B && + tt != TEMPLATE_BRIDGE_2A && tt != TEMPLATE_BRIDGE_2B && + tt != TEMPLATE_BRIDGE_3A && tt != TEMPLATE_BRIDGE_3B ) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + FootClass *unit = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if(unit->What_Am_I() != RTTI_UNIT || + *(UnitClass *)unit != UNIT_CHRONOTANK || + mission->Mission != TMISSION_SPY) +#endif + Target = 0; // invalidize the target so it'll go to next mission. + } + } + } + + if (!Target_Legal(Target)) { + IsNextMission = true; + + } else { + + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + FootClass * unit = Member; + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)unit == INFANTRY_SPY) { + unit->Assign_Mission(MISSION_CAPTURE); + unit->Assign_Target(Target); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_CHRONOTANK) { + UnitClass *tank = (UnitClass *)unit; + tank->Teleport_To(::As_Cell(Target)); + tank->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONOTANK1, unit->Coord); + tank->Assign_Target(TARGET_NONE); + tank->Assign_Mission(MISSION_GUARD); + } else { +#endif + if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { + unit->Transmit_Message(RADIO_OVER_OUT); + unit->Assign_Mission(MISSION_ATTACK); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + if (unit->TarCom != Target) { + unit->Assign_Target(Target); + } + } + + unit = unit->Member; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * * + * This routine is called when the team must delay at its current location. Team members * + * are grouped together by this function. It is called when the team needs to sit and * + * wait. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the team completely regrouped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Regroup(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool retval = true; + + /* + ** Regroup default logic. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->Distance(Zone) > Rule.StrayDistance && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { + if (!Target_Legal(unit->NavCom)) { +// TCTCTC +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, Zone) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + + retval = false; + if (!unit->IsFormationMove) { + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + } else { + retval = true; // formations are always considered regrouped. + } + } + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (unit->Mission != MISSION_GUARD_AREA) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } + return(retval); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * + * * + * This will assign the specified mission to the team. If there are team members that are * + * too far away from the center of the team, then they will be told to move to the team's * + * location. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This only works if the special mission the team members are to perform does not * + * require extra parameters. The ATTACK and MOVE missions are particularly bad. * + * * + * HISTORY: * + * 05/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Do(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + MissionType do_mission = Class->MissionList[CurrentMission].Data.Mission; + + /* + ** For each unit either head it back to the team center or give it the main + ** team mission order as appropriate. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Distance(Zone) > Rule.StrayDistance * 2) { + + /* + ** Only if the unit isn't already heading to regroup with the team, will it + ** be given orders to do so. + */ + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Mission != do_mission) { + unit->ArchiveTarget = TARGET_NONE; + unit->Assign_Mission(do_mission); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * * + * This routine is called when the team must move to a new location. Movement and grouping * + * commands associated with this task are initiated here. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Move(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + bool found = false; + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (Target_Legal(Target)) { + + if (!Lagging_Units()) { + + while (unit != NULL) { + + /* + ** Tell the unit, if necessary, that it should regroup + ** with the main team location. If the unit is regrouping, then + ** the team should continue NOT qualify as fully reaching the desired + ** location. + */ + if (Coordinate_Conscript(unit)) { + finished = false; + } + + if (unit->Mission == MISSION_UNLOAD || unit->MissionQueue == MISSION_UNLOAD) { + finished = false; + } + + if (_Is_It_Playing(unit) && unit->Mission != MISSION_UNLOAD && unit->MissionQueue != MISSION_UNLOAD) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + if (unit->What_Am_I() == RTTI_INFANTRY && ((InfantryClass const *)unit)->Class->IsDog) { + if (Target_Legal(unit->TarCom)) stray = unit->Techno_Type_Class()->ThreatRange; + if (Target_Legal(unit->TarCom) && unit->Distance(unit->TarCom) > stray) { + unit->Assign_Target(TARGET_NONE); + } + } + found = true; + + int dist = unit->Distance(Target); + if (unit->IsFormationMove) { + if (::As_Target(Coord_Cell(unit->Coord)) != unit->NavCom) { + dist = Rule.StrayDistance + 1; // formation moves must be exact. + } + } + + if (dist > stray || + (unit->What_Am_I() == RTTI_AIRCRAFT && +// (unit->In_Which_Layer() == LAYER_TOP && + ((AircraftClass *)unit)->Height > 0 && + Coord_Cell(unit->Center_Coord()) != As_Cell(Target) && + !((AircraftClass *)unit)->Class->IsFixedWing && + Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { + + bool wasform = false; + + + if (unit->Mission != MISSION_MOVE) { + unit->Assign_Mission(MISSION_MOVE); + } + + if (unit->NavCom != Target) { + + /* + ** Check if this destination should be adjusted for + ** a formation move + */ + if (Is_Target_Cell(Target) && unit->IsFormationMove) { + CELL newcell = unit->Adjust_Dest(As_Cell(Target)); + if (Coord_Cell(unit->Coord) != newcell) { + unit->Assign_Destination(::As_Target(newcell)); + } else { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + wasform = true; + } + } else { + unit->Assign_Destination(Target); + } + } + + if (!wasform) { + finished = false; + } + + } else { + if (unit->Mission == MISSION_MOVE && (!Target_Legal(unit->NavCom) || Distance(unit->NavCom) < CELL_LEPTON_W)) { + unit->Assign_Destination(TARGET_NONE); + unit->Enter_Idle_Mode(); + } + } + + /* + ** If any member still has a valid NavCom then consider this + ** movement mission to still be in progress. This will ensure + ** that the members come to a complete stop before the next + ** mission commences. Without this, the team will prematurely + ** start on the next mission even when all members aren't yet + ** in their proper spot. + */ + if (Target_Legal(unit->NavCom)) { + finished = false; + } + } + + unit = unit->Member; + } + } else { + finished = false; + } + } + + /* + ** If there are no initiated members to this team, then it certainly + ** could not have managed to move to the target destination. + */ + if (!found) { + finished = false; + } + + /* + ** If all the team members are close enough to the desired destination, then + ** move to the next mission. + */ + if (finished && IsMoving) { + IsNextMission = true; + } +} + + +/*********************************************************************************************** + * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * + * * + * This routine will examine the team and find any lagging units. The units are then * + * ordered to catch up to the team member that is closest to the team's destination. This * + * routine will not do anything unless lagging members are suspected. This fact is * + * indicated by setting the IsLagging flag. The flag is set by some outside agent. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to set IsLagging for the team if a lagging member is suspected. * + * * + * HISTORY: * + * 08/01/1995 PWG : Created. * + * 04/11/1996 JLB : Modified. * + *=============================================================================================*/ +bool TeamClass::Lagging_Units(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool lag = false; + + //BG: HACK - if it's in a formation move, then disable the check for + // VG added NULL check laggers, 'cause they're all moving simultaneously. + if (unit != NULL && unit->IsFormationMove) IsLagging = false; + + /* + ** If the IsLagging bit is not set, then obviously there are no lagging + ** units. + */ + if (!IsLagging) return(false); + + /* + ** Scan through all of the units, searching for units who are having + ** trouble keeping up with the pack. + */ + while (unit != NULL) { + + if (_Is_It_Playing(unit)) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + + /* + ** If we find a unit who has fallen too far away from the center of + ** the pack, then we need to order that unit to catch up with the + ** first unit. + */ + if (unit->Distance(ClosestMember) > stray) { +// TCTCTC + if (!Target_Legal(unit->NavCom)) { +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, ClosestMember) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(ClosestMember); + } + lag = true; + + } else { + /* + ** We need to order all of the other units to hold their + ** position until all lagging units catch up. + */ + if (unit->Mission != MISSION_GUARD) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + } + unit = unit->Member; + } + + /* + ** Once we have handled the loop we know whether there are any lagging + ** units or not. + */ + IsLagging = lag; + return(lag); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * + * * + * This routine tells all transport vehicles to unload passengers now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Unload(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + /* + ** Only assign the mission if the unit is carrying a passenger, OR + ** if the unit is a minelayer, with mines in it, and the cell it's + ** on doesn't have a building (read: mine) in it already. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* Also, allow unload if it's a MAD Tank. */ + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MAD )) { +#else + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) ) { +#endif + if (unit->Is_Something_Attached()) { + /* + ** Passenger-carrying vehicles will always return false until + ** they've unloaded all passengers. + */ + finished = false; + } + + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (Map[unit->Center_Coord()].Cell_Building() == NULL && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + + } else { + + /* + ** A loaner transport should vacate the map when all transported objects + ** have been offloaded. + */ + if (unit->IsALoaner) { + Remove(unit); + unit->Assign_Mission(MISSION_RETREAT); + unit->Commence(); + } + } + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * + * * + * This routine tells all non-transport units in the team to climb onto the transport in the* + * team. Note the transport must be a member of this team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1996 BWG : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Load(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + FootClass * trans = 0; + + /* + ** First locate the transport in the team, if there is one. There should + ** only be one transport in the team. + */ + while(unit != NULL && trans == NULL) { + if (unit->Techno_Type_Class()->Max_Passengers() > 0) { + trans = unit; + break; + } + unit = unit->Member; + } + + /* + ** In the case of no transport available, then consider the mission complete + ** since it can never complete otherwise. + */ + if (trans == NULL) { + IsNextMission = true; + return(1); + } + + /* + ** If the transport is already in radio contact, then this means that + ** it is in the process of loading. During this time, don't bother to assign + ** the enter mission to the other team members. + */ + if (trans->In_Radio_Contact()) { + return(1); + } + + /* + ** Find a member to assign the entry logic for. + */ + bool finished = true; + unit = Member; // re-point at the first member of the team again. + while (unit != NULL && Total > 1) { + Coordinate_Conscript(unit); + + /* + ** Only assign the mission if the unit is not the transport. + */ + if (_Is_It_Playing(unit) && unit != trans) { + if (unit->Mission != MISSION_ENTER) { + unit->Assign_Mission(MISSION_ENTER); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(trans->As_Target()); + finished = false; + break; + } + finished = false; + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * * + * This routine will give the movement orders to the conscript so that it will group * + * with the other members of the team. * + * * + * INPUT: unit -- Pointer to the conscript unit. * + * * + * OUTPUT: bool; Is the unit still scurrying to reach the team's current location? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Conscript(FootClass * unit) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (_Is_It_Breathing(unit) && !unit->IsInitiated) { + if (unit->Distance(Zone) > Rule.StrayDistance) { + if (!Target_Legal(unit->NavCom)) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Target(TARGET_NONE); + unit->IsFormationMove = false; + unit->Assign_Destination(Zone); + } + return(true); + + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered initiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + } + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Is_A_Member(void const * who) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + while (unit != NULL) { + if (unit == who) { + return(true); + } + unit = unit->Member; + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * * + * INPUT: int priority - determines what is considered low priority. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/19/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Suspend_Teams(int priority, HouseClass const * house) +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + /* + ** If a team is below the "survival priority level", then it gets + ** destroyed. The team members are then free to be reassigned. + */ + if (team != NULL && team->House == house && team->Class->RecruitPriority < priority) { + FootClass * unit = team->Member; + while (team->Member) { + team->Remove(team->Member); + } + team->IsAltered = team->JustAltered = true; + team->SuspendTimer = Rule.SuspendDelay * TICKS_PER_MINUTE; + team->Suspended = true; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * + * * + * This routine is used to see if the team is leaving the map. A team that is leaving the * + * map gives implicit permission for its members to leave the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this team trying to leave the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Is_Leaving_Map(void) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (IsMoving && CurrentMission >= 0) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + if (mission->Mission == TMISSION_MOVE && !Map.In_Radar(Scen.Waypoint[mission->Data.Value])) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * + * * + * This will examine all team members and only if all of them have entered the map, will * + * it return true. This routine is used to recognize the case of a team that has been * + * generated off map and one that has already entered game play. This knowledge can lead * + * to more intelligent behavior regarding team and member disposition. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Have all members of this team entered the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Has_Entered_Map(void) const +{ + bool ok = true; + FootClass * foot = Member; + while (foot != NULL) { + if (!foot->IsLocked) { + ok = false; + break; + } + foot = (FootClass *)(ObjectClass *)(foot->Next); + } + return(ok); +} + + +/*********************************************************************************************** + * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * + * * + * This routine is used when one of the team members cannot get within range of the team's * + * target. In such a case, the team must be assigned a new target and all members of that * + * team must recognize that a restricted target scan is required. This is done by clearing * + * out the team's target so that it will be forced to search for a new one. Also, since the * + * members are flagged for short scanning, whichever team member is picked to scan for a * + * target will scan for one that is within range. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The team will reassign its target as a result of this routine. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Scan_Limit(void) +{ + Assign_Mission_Target(TARGET_NONE); + FootClass * foot = Member; + while (foot != NULL) { + foot->Assign_Target(TARGET_NONE); + foot->IsScanLimited = true; + foot = foot->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::TMission_Formation -- Process team formation change command. * + * * + * This routine will change the team's formation to that specified in the team command * + * parameter. It is presumed that the team will have further movement orders so that the * + * formation can serve some purpose. Merely changing the formation doesn't alter the * + * member's location. The team must be given a movement order before team member * + * repositioning will occur. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to delay before further team actions should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Formation(void) +{ + FootClass * member = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Formation = mission->Data.Formation; + int group = ID + 10; + int xdir = 0; + int ydir = 0; + bool evenodd = 1; + + /* + ** Assign appropriate formation offsets for each of the members + ** of this team. + */ + switch (Formation) { + case FORMATION_NONE: + while (member != NULL) { + member->Group = 0xFF; + member->XFormOffset = 0x80000000; + member->YFormOffset = 0x80000000; + member->IsFormationMove = false; + member = member->Member; + } + break; + case FORMATION_TIGHT: + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + } + break; + case FORMATION_LOOSE: + break; + case FORMATION_WEDGE_N: + ydir = -(Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir += 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_E: + xdir = (Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_S: + ydir = (Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_W: + xdir = -(Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir += 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_LINE_NS: + ydir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = ydir; + member->IsFormationMove = true; + member = member->Member; + ydir += 2; + } + break; + case FORMATION_LINE_EW: + xdir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + xdir += 2; + } + break; + } + + /* + ** Now calculate the group's movement type and speed + */ + if (Formation != FORMATION_NONE) { + TeamSpeed[group] = SPEED_WHEEL; + TeamMaxSpeed[group] = MPH_LIGHT_SPEED; + member = Member; + while (member != NULL) { + RTTIType mytype = member->What_Am_I(); + SpeedType memspeed; + MPHType memmax; + bool speedcheck = false; + + if (mytype == RTTI_INFANTRY) { + memspeed = SPEED_FOOT; + memmax = ((InfantryClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + if (mytype == RTTI_UNIT) { + memspeed = ((UnitClass *)member)->Class->Speed; + memmax = ((UnitClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (mytype == RTTI_VESSEL) { + memspeed = ((VesselClass *)member)->Class->Speed; + memmax = ((VesselClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (speedcheck) { + if (memmax < TeamMaxSpeed[group]) { + TeamMaxSpeed[group] = memmax; + TeamSpeed[group] = memspeed; + } + } + member = member->Member; + } + + /* + ** Now that it's all calculated, assign the movement type and + ** speed to every member of the team. + */ + member = Member; + while (member != NULL) { + member->FormationSpeed = TeamSpeed[group]; + member->FormationMaxSpeed = TeamMaxSpeed[group]; + if (member->What_Am_I() == RTTI_INFANTRY) { + member->FormationSpeed = SPEED_FOOT; + member->FormationMaxSpeed = MPH_SLOW_ISH; + } + member = member->Member; + } + } + + // Advance past the formation-setting command. + IsNextMission = true; + + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Attack -- Perform the team attack mission command. * + * * + * This will tell the team to attack the quarry specified in the team command. If the team * + * already has a target, this it is presumed that this target take precidence and it won't * + * be changed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Attack(void) +{ + if (!Target_Legal(MissionTarget) && Member != NULL) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + /* + ** Pick a team leader that has a weapon. Only in the case of no + ** team members having any weapons, will a member without a weapon + ** be chosen. + */ + FootClass const * candidate = Fetch_A_Leader(); + + /* + ** Have the team leader pick what the next team target will be. + */ + switch (mission->Data.Quarry) { + case QUARRY_ANYTHING: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_BUILDINGS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BUILDINGS)); + break; + + case QUARRY_HARVESTERS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_TIBERIUM)); + break; + + case QUARRY_INFANTRY: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_INFANTRY)); + break; + + case QUARRY_VEHICLES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_VEHICLES)); + break; + + case QUARRY_FACTORIES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FACTORIES)); + break; + + case QUARRY_DEFENSE: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BASE_DEFENSE)); + break; + + case QUARRY_THREAT: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_POWER: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_POWER)); + break; + + case QUARRY_FAKES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FAKES)); + break; + + default: + break; + } + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Spy -- Perform the team spy mission. * + * * + * This will give the team a spy mission to the location specified. It is presumed that * + * the location of the team mission actually resides under the building to be spied. If * + * no building exists at the location, then the spy operation is presumed to be a mere * + * move operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Spy(void) +{ + if (Is_Target_Cell(MissionTarget)) + { + CELL cell = ::As_Cell(MissionTarget); + CellClass * cellptr = &Map[cell]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + ObjectClass * bldg = cellptr->Cell_Building(); +#else + ObjectClass * bldg = cellptr->Cell_Object(); +#endif + if (bldg != NULL) + { + Assign_Mission_Target(bldg->As_Target()); + Coordinate_Attack(); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else + { + FootClass *member = Member; + if(member->What_Am_I() == RTTI_UNIT && *(UnitClass *)member == UNIT_CHRONOTANK) + { + bool finished = true; + while (member) + { + if ( !((UnitClass *)member)->MoebiusCountDown) finished = false; + member = member->Member; + } + + if (!finished) + { + Coordinate_Attack(); + } + else + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + } + } +#endif + } + else + { + if (!Target_Legal(MissionTarget)) + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + else + { + Coordinate_Attack(); + } + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * + * * + * This will cause the team members to search out and follow the nearest friendly object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Follow(void) +{ + Calc_Center(Zone, ClosestMember); + Target = Zone; + Coordinate_Move(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * + * * + * This is equivalent to a jump or goto command. It will alter the team command processing * + * such that it will continue processing at the command number specified. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Loop(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + CurrentMission = mission->Data.Value-1; + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of time * + * * + * This is a team mission that simulates the Iron Curtain device. It will make all team * + * members invlunerable for a temporary period of time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Invulnerable(void) +{ + FootClass * foot = Member; + while (foot != NULL) { + foot->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; + foot->Mark(MARK_CHANGE); + foot = foot->Member; + } + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * + * * + * This routine is used by the team to set a global variable but otherwise perform no * + * visible effect on the team. By using this routine, sophisticated trigger dependencies * + * can be implemented. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Set_Global(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Scen.Set_Global_To(mission->Data.Value, true); + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * + * * + * A patrolling team will move to the designated waypoint, but along the way it will * + * periodically scan for nearby enemies. If an enemy is found, the patrol mission turns * + * into an attack mission until the target is destroyed -- after which it resumes its * + * patrol duties. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next call to this routine is needed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Patrol(void) +{ + /* + ** Reassign the movement destination if the target has been prematurely + ** cleared (probably because the object has been destroyed). + */ + if (!Target_Legal(Target)) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + } + + /* + ** Every so often, scan for a nearby enemy. + */ + if (Frame % (Rule.PatrolTime * TICKS_PER_MINUTE) == 0) { + FootClass * leader = Fetch_A_Leader(); + if (leader != NULL) { + TARGET target = leader->Greatest_Threat(THREAT_NORMAL|THREAT_RANGE); + + if (Target_Legal(target)) { + Assign_Mission_Target(target); + } else { + Assign_Mission_Target(TARGET_NONE); + } + } + } + + /* + ** If the mission target looks like it should be attacked, then do so, otherwise + ** treat it as a movement destination. + */ + if (Is_Target_Object(Target)) { + Coordinate_Attack(); + } else { + Coordinate_Move(); + } + return(1); +} + + +int TeamClass::TMission_Deploy(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MCV) { + if (unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo != 0) { + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (!Map[unit->Center_Coord()].Cell_Building() && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * + * * + * This will scan through the team members looking for one that is suitable as a leader * + * type. A team can sometimes contain limboed or unarmed members. These members are not * + * suitable for leadership roles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suitable leader type unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1996 JLB : Created. * + *=============================================================================================*/ +FootClass * TeamClass::Fetch_A_Leader(void) const +{ + FootClass * leader = Member; + + /* + ** Scan through the team members trying to find one that is an active member and + ** is equipped with a weapon. + */ + while (leader != NULL) { + if (_Is_It_Playing(leader) && leader->Is_Weapon_Equipped()) break; + leader = leader->Member; + } + + /* + ** If no suitable leader was found, then just return with the first conveniently + ** accessable team member. This presumes that some member is better than no member + ** at all. + */ + if (leader == NULL) { + leader = Member; + } + + return(leader); +} \ No newline at end of file diff --git a/REDALERT/TEAM.H b/REDALERT/TEAM.H new file mode 100644 index 000000000..fce840235 --- /dev/null +++ b/REDALERT/TEAM.H @@ -0,0 +1,272 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : December 11, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAM_H +#define TEAM_H + + +#include "wwfile.h" +#include "teamtype.h" +#include "abstract.h" + +/* +** Units are only allowed to stray a certain distance away from their +** team. When they exceed this distance, some sort of fixup must be +** done. +*/ +#define STRAY_DISTANCE 2 + +class TeamClass : public AbstractClass +{ + public: + /* + ** This specifies the type of team this is. + */ + CCPtr Class; + + /* + ** This specifies the owner of this team. + */ + CCPtr House; + + /* + ** This flag forces the team into active state regardless of whether it + ** is understrength or not. + */ + unsigned IsForcedActive:1; + + /* + ** This flag is set to true when the team initiates into active mode. The + ** flag is never cleared. By examining this flag, it is possible to determine + ** if the team has ever launched into active mode. + */ + unsigned IsHasBeen:1; + + /* + ** If the team is full strength, then this flag is true. A full strength + ** team will not try to recruit members. + */ + unsigned IsFullStrength:1; + + /* + ** A team that is below half strength has this flag true. It means that the + ** the team should hide back at the owner's base and try to recruit + ** members. + */ + unsigned IsUnderStrength:1; + + /* + ** If a team is not understrength but is not yet full strength, then + ** the team is regrouping. If this flag is set and the team becomes + ** full strength, the all members of the team will become initiated + ** and this flag will be reset. + */ + unsigned IsReforming:1; + + /* + ** This bit should be set if a team is determined to have lagging + ** units in its formation. + */ + unsigned IsLagging:1; + + /* + ** If a team member was removed or added, then this flag will be set to true. The + ** team system uses this flag to tell whether it should recalculate the team + ** under strength or full strength flags. This process does not need to occur + ** EVERY time a unit added or deleted from a team, just every so often if the + ** team has been changed. + */ + unsigned IsAltered:1; + unsigned JustAltered:1; + + /* + ** If the team is working on it's primary mission (it is past the build up stage) + ** then this flag will be true. The transition between "moving" and "stationary" + ** stages usually requires some action on the team's part. + */ + unsigned IsMoving:1; + + /* + ** When the team determines that the next mission should be advanced to, it will + ** set this flag to true. Mission advance will either change the behavior of the + ** team or cause it to disband. + */ + unsigned IsNextMission:1; + + /* + ** If at least one member of this team successfully left the map, then this + ** flag will be set. At the time the team is terminated, if this flag is true, then + ** if there are any triggers that depend upon this team leaving, they will be + ** sprung. + */ + unsigned IsLeaveMap:1; + + /* + ** Records whether the team is suspended from production. + */ + unsigned Suspended:1; + + /* + ** A team will have a center point. This is the point used to determine if + ** any member of the team is "too far" from the team and must return. This + ** center point is usually calculated as the average position of all the + ** team members. + */ + TARGET Zone; + + /* + ** This is the target value of the team member that is closest to the + ** destination of the team. The implied location serves as the + ** regroup point for the unit as it is moving. + */ + TARGET ClosestMember; + + /* + ** This is the target of the team. Typically, it is a unit or structure, but + ** for the case of teams with a movement mission, it might represent a + ** destination cell. + */ + TARGET MissionTarget; + TARGET Target; + + /* + ** This is the total number of members in this team. + */ + int Total; + + /* + ** This is the teams combined risk value + */ + int Risk; + + /* + ** If this team is assigned a formation, then the formation type + ** will be stored here. If the formation type is FORMATION_NONE, then + ** the team is a loose grouping -- just like C&C. + */ + FormationType Formation; + + /* + ** This is the amount of time the team is suspended for. + */ + CDTimerClass SuspendTimer; + + /* + ** If there is a trigger that should be attached to every member of this team, then + ** it is pointed to by this. + */ + CCPtr Trigger; + + //------------------------------------------------------------ + TeamClass(TeamTypeClass const * team=0, HouseClass * owner=0); + TeamClass(NoInitClass const & x) : AbstractClass(x), Class(x), House(x), SuspendTimer(x), Trigger(x), TimeOut(x), Member(x) {}; + virtual ~TeamClass(void); + static void operator delete(void *ptr); + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void Init(void); + static void Suspend_Teams(int priority, HouseClass const * house); + void Debug_Dump(MonoClass * mono) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Is_Empty(void) const {return(Member == (void*)NULL);} + bool Has_Entered_Map(void) const; + void Force_Active(void) {IsForcedActive = true;IsUnderStrength=false;}; + bool Remove(FootClass *, int typeindex=-1); + void Detach(TARGET target, bool all); + void AI(void); + void Took_Damage(FootClass * obj, ResultType result, TechnoClass * source); + bool Add(FootClass *); + bool Can_Add(FootClass * obj, int & typeindex) const; + void Assign_Mission_Target(TARGET new_target); + bool Is_Leaving_Map(void) const; + void Scan_Limit(void); + + private: + + /* + ** The current mission index into the mission list is recorded here. + */ + int CurrentMission; + + /* + ** Some missions will time out. This is the timer that keeps track of the + ** time to transition between missions. + */ + CDTimerClass TimeOut; + + int TMission_Formation(void); + int TMission_Attack(void); + int TMission_Spy(void); + int TMission_Follow(void); + int TMission_Loop(void); + int TMission_Load(void); + int TMission_Unload(void); + int TMission_Invulnerable(void); + int TMission_Set_Global(void); + int TMission_Patrol(void); + int TMission_Deploy(void); + bool Coordinate_Regroup(void); + void Coordinate_Attack(void); + void Coordinate_Move(void); + bool Coordinate_Conscript(FootClass * unit); + void Coordinate_Do(void); + void Calc_Center(TARGET ¢er, TARGET &obj_center) const; + int Recruit(int typeindex); + bool Is_A_Member(void const * who) const; + bool Lagging_Units(void); + FootClass * Fetch_A_Leader(void) const; + + /* + ** Points to the first member in the list of members for this team. + */ + SmartPtr Member; + + unsigned char Quantity[TeamTypeClass::MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/TEAMTYPE.CPP b/REDALERT/TEAMTYPE.CPP new file mode 100644 index 000000000..6cfbd33bf --- /dev/null +++ b/REDALERT/TEAMTYPE.CPP @@ -0,0 +1,1879 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAMTYPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/28/96 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamMissionClass::Description -- Compose a text description of team mi * + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * TeamMission_Needs -- Determines what extra data is needed by team miss * + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * TeamTypeClass::Description -- Builds a description of the team. * + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * TeamTypeClass::Edit -- Edit the team type. * + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * TeamTypeClass::Init -- pre-scenario initialization * + * TeamTypeClass::Member_Description -- Builds a member description string * + * TeamTypeClass::Mission_From_Name -- returns mission for given name * + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * TeamTypeClass::Read_INI -- reads INI data * + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * TeamTypeClass::TeamTypeClass -- class constructor * + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * TeamTypeClass::operator delete -- 'delete' operator * + * TeamTypeClass::operator new -- 'new' operator * + * TeamTypeClass::~TeamTypeClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +TeamMissionClass TeamMissions[TMISSION_COUNT] = { + {TMISSION_ATTACK}, + {TMISSION_ATT_WAYPT}, + {TMISSION_FORMATION}, + {TMISSION_MOVE}, + {TMISSION_MOVECELL}, + {TMISSION_GUARD}, + {TMISSION_LOOP}, + {TMISSION_ATTACKTARCOM}, + {TMISSION_UNLOAD}, + {TMISSION_DEPLOY}, + {TMISSION_HOUND_DOG}, + {TMISSION_DO}, + {TMISSION_SET_GLOBAL}, + {TMISSION_LOAD}, + {TMISSION_SPY}, + {TMISSION_PATROL}, +}; + + + +int atoh(char * str); + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * * + * This is a helper routine that is used when this team type is stored into a list box * + * and must be displayed. It will display the team type as a single line of text. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/* +********************************** Globals ********************************** +*/ +char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { + "Attack...", + "Attack Waypoint...", + "Change Formation to...", + "Move to waypoint...", + "Move to Cell...", + "Guard area (1/10th min)...", + "Jump to line #...", + "Attack Tarcom", + "Unload", + "Deploy", + "Follow friendlies", + "Do this...", + "Set global...", + "Invulnerable", + "Load onto Transport", + "Spy on bldg @ waypt...", + "Patrol to waypoint..." +}; + + +/*************************************************************************** + * TeamTypeClass::TeamTypeClass -- class constructor * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 11/22/1995 JLB : Uses initializer constructor method. * + *=========================================================================*/ +TeamTypeClass::TeamTypeClass(void) : + AbstractTypeClass(RTTI_TEAMTYPE, TeamTypes.ID(this), TXT_NONE, ""), + IsRoundAbout(false), + IsSuicide(false), + IsAutocreate(false), + IsPrebuilt(true), + IsReinforcable(true), + IsTransient(false), + RecruitPriority(7), + InitNum(0), + MaxAllowed(0), + Fear(0), + House(HOUSE_NONE), + Trigger(0), + Origin(-1), + Number(0), + MissionCount(0), + ClassCount(0) +{ + for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { + Members[i].Class = NULL; + Members[i].Quantity = 0; + } +} + + +/*************************************************************************** + * TeamTypeClass::Init -- pre-scenario initialization * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Init(void) +{ + TeamTypes.Free_All(); +} + + +/*************************************************************************** + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * * + * INPUT: * + * name name of teamtype * + * * + * OUTPUT: * + * ptr to TeamType with that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass * TeamTypeClass::As_Pointer(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (!stricmp(name, TeamTypes.Ptr(index)->IniName)) { + return(TeamTypes.Ptr(index)); + } + } + } + return(NULL); +} + + +/*************************************************************************** + * TeamTypeClass::Mission_From_Name -- returns team mission for given name * + * * + * INPUT: * + * name name to compare * + * * + * OUTPUT: * + * mission for that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +TeamMissionType TeamTypeClass::Mission_From_Name(char const * name) +{ + if (name) { + for (TeamMissionType order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { + if (stricmp(TMissions[order], name) == 0) { + return(order); + } + } + } + + return(TMISSION_NONE); +} + + +/*************************************************************************** + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * * + * INPUT: * + * order mission to get name for * + * * + * OUTPUT: * + * name of mission * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) +{ + assert((unsigned)order < TMISSION_COUNT); + + return(TMissions[order]); +} + + +/*************************************************************************** + * TeamTypeClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new TeamType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void * TeamTypeClass::operator new(size_t ) +{ + void * ptr = TeamTypes.Allocate(); + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*************************************************************************** + * TeamTypeClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = false; + } + TeamTypes.Free((TeamTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * * + * Use this routine to create a team object from this team type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly created team object. If one could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * TeamTypeClass::Create_One_Of(void) const +{ + if (ScenarioInit || Number < MaxAllowed) { +// if (ScenarioInit || TeamClass::Number[ID] < MaxAllowed) { + return(new TeamClass(this, HouseClass::As_Pointer(House))); + } + return(NULL); +} + + +/*********************************************************************************************** + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * * + * This routine will destroy all teams of this type. Typical use of this is from a trigger * + * event. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Destroy_All_Of(void) const +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + if (team->Class == this) { + delete team; + index--; + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * * + * This routine will scan through the team types available and create teams of the * + * type that can best utilize the existing unit mix. * + * * + * INPUT: house -- Pointer to the house that this team is to be created for. * + * * + * atypes -- A bit mask of the aircraft types available for this house. * + * * + * utypes -- A bit mask of the unit types available for this house. * + * * + * itypes -- A bit mask of the infantry types available for this house. * + * * + * vtypes -- A bit mask of the vessel types available for this house. * + * * + * alerted -- Is this house alerted? If true, then the Autocreate teams will be * + * considered in the selection process. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1995 JLB : Created. * + * 07/21/1995 JLB : Will autocreate team even if no members in field. * + *=============================================================================================*/ +TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long , long , long , long , bool alerted) +//TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted) +{ +// TeamTypeClass const * best = NULL; +// int bestvalue = 0; + + TeamTypeClass const * choices[20]; + int choicecount = 0; + + for (int index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * ttype = TeamTypes.Ptr(index); + + assert(ttype != NULL); + + int maxnum = ttype->MaxAllowed; + if ((alerted && !ttype->IsAutocreate) || (!alerted && ttype->IsAutocreate)) { + maxnum = 0; + } + + if (choicecount >= ARRAY_SIZE(choices)) break; + + if (ttype != NULL && ttype->House == house->Class->House && ttype->Number < maxnum) { + + choices[choicecount++] = ttype; + +#ifdef OBSOLETE + /* + ** Determine what kind of units this team requires. + */ + long uneeded = 0; + long ineeded = 0; + long vneeded = 0; + long aneeded = 0; + for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { + switch (ttype->Members[ctype].Class->What_Am_I()) { + case RTTI_INFANTRYTYPE: + ineeded |= (1 << ((InfantryTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_UNITTYPE: + uneeded |= (1 << ((UnitTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_VESSELTYPE: + vneeded |= (1 << ((VesselTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_AIRCRAFTTYPE: + aneeded |= (1 << ((AircraftTypeClass *)ttype->Members[ctype].Class)->Type); + break; + } + } + + /* + ** If this team can use the types required, then consider it a possible + ** team type to create. + */ + int value = 0; + if ((aneeded & atypes) != 0 || (ineeded & itypes) != 0 || (uneeded & utypes) != 0 || (vneeded & vtypes) != 0) { + value = ttype->RecruitPriority; + } else { + value = ttype->RecruitPriority/2; + } + + if (best == NULL || bestvalue < value) { + bestvalue = value; + best = ttype; + } +#endif + } + } + + if (choicecount > 0) { + return(choices[Random_Pick(0, choicecount-1)]); + } + return(NULL); + +// return(best); +} + + +/*********************************************************************************************** + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * * + * This routine is used to convert an ASCII name of a team type into the corresponding * + * team type pointer. * + * * + * INPUT: name -- Pointer to the ASCII name of the team type. * + * * + * OUTPUT: Returns with a pointer to the team type that this ASCII name represents. If there * + * is no match, the NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * TeamTypeClass::From_Name(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (stricmp(name, TeamTypes.Ptr(index)->IniName) == 0) { + return(TeamTypes.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * TeamMission_Needs -- Determines what extra data is needed by team mission. * + * * + * This routine will return the required extra data that the specified team mission will * + * need. * + * * + * INPUT: tmtype -- The team mission type to check. * + * * + * OUTPUT: Returns with the data type needed for this team mission. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +NeedType TeamMission_Needs(TeamMissionType tmtype) +{ + switch (tmtype) { + /* + ** Requires a formation type. + */ + case TMISSION_FORMATION: + return(NEED_FORMATION); + + /* + ** Team mission requires a target quarry value. + */ + case TMISSION_ATTACK: + return(NEED_QUARRY); + + /* + ** Team mission requires a data value. + */ + case TMISSION_MOVECELL: + return(NEED_HEX_NUMBER); + + case TMISSION_SET_GLOBAL: + case TMISSION_GUARD: + case TMISSION_LOOP: + return(NEED_NUMBER); + + /* + ** Team mission requires a waypoint. + */ + case TMISSION_PATROL: + case TMISSION_MOVE: + case TMISSION_ATT_WAYPT: + case TMISSION_SPY: + return(NEED_WAYPOINT); + + /* + ** Team mission requires a general mission type. + */ + case TMISSION_DO: + return(NEED_MISSION); + + default: + break; + } + return(NEED_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * * + * This routine will display a team mission list box entry. It converts the index number * + * into the appropriate text string and then displays the text at the coordinates * + * specified. * + * * + * INPUT: index -- The index number to use when rendering the team mission. * + * * + * x,y -- The X,Y coordinate to use when drawing the team mission entry. * + * * + * width, height -- The dimensions of the area that the description can be * + * rendered into. * + * * + * selected -- Is this entry currently selected? If so, then it should be rendered * + * differently. * + * * + * flags -- Text print control flags used for when the text is printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +void TeamMissionClass::Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, + scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(index), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(index), x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Edit -- Edit the team type. * + * * + * This routine handles the editing dialog box for the team type. * + * * + * INPUT: none * + * * + * OUTPUT: Was the team type edited? A FALSE indicates that the dialog was canceled. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Edit(void) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_NAME_X=D_DIALOG_X+35, + D_NAME_Y=D_DIALOG_Y+27, + ED_WIDTH=40, + + D_CHECK_X=D_DIALOG_X+35, // Start of check box attribute list. + D_CHECK_Y=D_NAME_Y+25, + + CB_SPACING_Y=9, // Vertical spacing between check box lines. + CB_SPACING_X=8, // Horizontal spacing for check box description text. + D_SPACING_X=9, // Horizontal spacing between data entry fields. + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+35), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - (D_CANCEL_H+20), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + D_DIALOG_W - (D_OK_W+18)*2, + D_OK_Y = D_CANCEL_Y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_NAME=100, + BUTTON_RECRUIT, + BUTTON_MAXNUM, + BUTTON_INITNUM, + BUTTON_HOUSE, + BUTTON_ROUNDABOUT, + BUTTON_LEARNING, + BUTTON_SUICIDE, + BUTTON_AUTO, + BUTTON_PREBUILT, + BUTTON_REINFORCE, + BUTTON_MISSION1, + BUTTON_MISSION2, + BUTTON_ADD, + BUTTON_INSERT, + BUTTON_DELETE, + BUTTON_REPLACE, + BUTTON_ARG, + BUTTON_FORMATION, + BUTTON_MEMBERS, + BUTTON_MISSION, + BUTTON_TRIGGER, + BUTTON_ORIGIN, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_QUARRY, + }; + + /* + ** Dialog variables: + */ + ControlClass * commands = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team name edit field. + */ + char name_buf[10]; + EditClass name_edt(BUTTON_NAME, name_buf, sizeof(name_buf), + TPF_EFNT | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, ED_WIDTH, 9, EditClass::ALPHANUMERIC); + strcpy(name_buf, IniName); + commands = &name_edt; + + /* + ** House ownership of this team. + */ + char housetext[25] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X+name_edt.Width+D_SPACING_X, name_edt.Y, 55, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + housebtn.Add(*commands); + + /* + ** Recruit priority for this team. + */ + char recr_buf[4]; + EditClass recr_edt(BUTTON_RECRUIT, recr_buf, sizeof(recr_buf), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X+housebtn.Width+5+D_SPACING_X, housebtn.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(recr_buf, "%d", RecruitPriority); + recr_edt.Add(*commands); + + /* + ** Maximum allowed for this team type. + */ + char maxnum_buf[4]; + EditClass maxnum_edt(BUTTON_MAXNUM, maxnum_buf, sizeof(maxnum_buf), + TPF_EFNT | TPF_NOSHADOW, + recr_edt.X+recr_edt.Width+D_SPACING_X, recr_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(maxnum_buf, "%d", MaxAllowed); + maxnum_edt.Add(*commands); + + /* + ** Initial number for this team type. + */ + char initnum_buf[4]; + EditClass initnum_edt(BUTTON_INITNUM, initnum_buf, sizeof(initnum_buf), + TPF_EFNT | TPF_NOSHADOW, + maxnum_edt.X+maxnum_edt.Width+D_SPACING_X, maxnum_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(initnum_buf, "%d", InitNum); + initnum_edt.Add(*commands); + + /* + ** Waypoint preference to create/reinforce this team. + */ + char origin[4]; + EditClass originbtn(BUTTON_ORIGIN, origin, sizeof(origin), + TPF_EFNT | TPF_NOSHADOW, + initnum_edt.X+initnum_edt.Width+D_SPACING_X, initnum_edt.Y, 20, 9, EditClass::ALPHA); + *originbtn.Get_Text() = '\0'; + if (Origin != -1) { + if (Origin < 26) { + sprintf(originbtn.Get_Text(), "%c", Origin + 'A'); + } else { + sprintf(originbtn.Get_Text(), "%c%c", (Origin/26) + 'A'-1, Origin % 26 + 'A'); + } + } + originbtn.Add(*commands); + + /* + ** Members of this team control button. + */ + TextButtonClass membersbtn(BUTTON_MEMBERS, "Members", TPF_EBUTTON, name_edt.X, name_edt.Y+12, 50); + membersbtn.Add(*commands); + + /* + ** Trigger to assign to each member of this team (when object joins team). + */ + char trigtext[25] = ""; + DropListClass triggerbtn(BUTTON_TRIGGER, trigtext, sizeof(trigtext), + TPF_EFNT | TPF_NOSHADOW, + D_DIALOG_X+D_DIALOG_W-95, membersbtn.Y, 60, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + triggerbtn.Add_Item(""); + for (int index = 0; index < TriggerTypes.Count(); index++) { + triggerbtn.Add_Item(TriggerTypes.Ptr(index)->IniName); + } + if (Trigger.Is_Valid()) { + triggerbtn.Set_Selected_Index(Trigger->Name()); + } else { + triggerbtn.Set_Selected_Index(0); + } + triggerbtn.Add(*commands); + + /* + ** Roundabout travel logic attribute for this team. + */ + CheckBoxClass roundbtn(BUTTON_ROUNDABOUT, D_CHECK_X, D_CHECK_Y); + if (IsRoundAbout) { + roundbtn.Turn_On(); + } else { + roundbtn.Turn_Off(); + } + roundbtn.Add(*commands); + + /* + ** Suicide travel to target attribute. + */ + CheckBoxClass suicidebtn(BUTTON_SUICIDE, D_CHECK_X, roundbtn.Y+CB_SPACING_Y); + if (IsSuicide) { + suicidebtn.Turn_On(); + } else { + suicidebtn.Turn_Off(); + } + suicidebtn.Add(*commands); + + /* + ** Autocreate attribute for this team. + */ + CheckBoxClass autocreatebtn(BUTTON_AUTO, D_CHECK_X, suicidebtn.Y+CB_SPACING_Y); + if (IsAutocreate) { + autocreatebtn.Turn_On(); + } else { + autocreatebtn.Turn_Off(); + } + autocreatebtn.Add(*commands); + + /* + ** Prebuild team members attribute for this team. + */ + CheckBoxClass prebuildbtn(BUTTON_PREBUILT, D_CHECK_X, autocreatebtn.Y+CB_SPACING_Y); + if (IsPrebuilt) { + prebuildbtn.Turn_On(); + } else { + prebuildbtn.Turn_Off(); + } + prebuildbtn.Add(*commands); + + /* + ** Reinforce this team in progress attribute. + */ + CheckBoxClass reinforcebtn(BUTTON_REINFORCE, D_CHECK_X, prebuildbtn.Y+CB_SPACING_Y); + if (IsReinforcable) { + reinforcebtn.Turn_On(); + } else { + reinforcebtn.Turn_Off(); + } + reinforcebtn.Add(*commands); + + /* + ** Create drop box of possible team missions. This is used when building/editing the + ** team mission list. + */ + char droptext[45]; + DropListClass missionlist1(BUTTON_MISSION1, droptext, sizeof(droptext), + TPF_EFNT | TPF_NOSHADOW, + reinforcebtn.X, reinforcebtn.Y+15, 170, 8*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TeamMissionType tm = TMISSION_FIRST; tm < TMISSION_COUNT; tm++) { + missionlist1.Add_Item(TeamTypeClass::Name_From_Mission(tm)); + } + missionlist1.Set_Selected_Index(0); + missionlist1.Add_Tail(*commands); + + /* + ** Optional mission argument entry field. + */ + char arg_buf[6] = {0}; + EditClass arg_edt(BUTTON_ARG, arg_buf, sizeof(arg_buf), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 60, -1, EditClass::ALPHANUMERIC); +// arg_edt.Add(*commands); + + char qtext[55]; + DropListClass qlist(BUTTON_QUARRY, qtext, sizeof(qtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist.Add_Item(QuarryName[q]); + } + qlist.Set_Selected_Index(0); + qlist.Add_Tail(*commands); + + char ftext[55]; + DropListClass flist(BUTTON_FORMATION, ftext, sizeof(ftext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (FormationType f = FORMATION_FIRST; f < FORMATION_COUNT; f++) { + flist.Add_Item(FormationName[f]); + } + flist.Set_Selected_Index(0); + flist.Add_Tail(*commands); + + char mtext[55]; + DropListClass mlist(BUTTON_MISSION, mtext, sizeof(mtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (MissionType m = MISSION_FIRST; m < MISSION_COUNT; m++) { + mlist.Add_Item(MissionClass::Mission_Name(m)); + } + mlist.Set_Selected_Index(0); + mlist.Add_Tail(*commands); + + TListClass missionlist2(BUTTON_MISSION2, missionlist1.X+60, missionlist1.Y+22, 240, 8*7, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (index = 0; index < MissionCount; index++) { + missionlist2.Add_Item(new TeamMissionClass(MissionList[index])); +// missionlist2.Add_Item(&TeamMissions[MissionList[index].Mission]); + } + static int tabs[] = {13, 40}; // list box tab stops + missionlist2.Set_Tabs(tabs); + missionlist2.Add_Tail(*commands); + + /* + ** Mission editing command buttons. + */ + TextButtonClass addbtn(BUTTON_ADD, "Append", TPF_EBUTTON, D_NAME_X, missionlist1.Y+missionlist1.Height+1, 50); + addbtn.Add(*commands); + + TextButtonClass insertbtn(BUTTON_INSERT, "Insert", TPF_EBUTTON, addbtn.X, addbtn.Y+10, 50); + insertbtn.Add(*commands); + + TextButtonClass delbtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, insertbtn.X, insertbtn.Y+10, 50); + delbtn.Add(*commands); + + TextButtonClass repbtn(BUTTON_REPLACE, "Replace", TPF_EBUTTON, delbtn.X, delbtn.Y+10, 50); + repbtn.Add(*commands); + + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the list + */ + okbtn.Add(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + int lastindex = -1; + int lastcount = -1; + int lastbutton = -1; + bool cancel = false; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + /* + ** Add the optional data entry field as necessary. + */ + arg_edt.Remove(); + qlist.Remove(); + flist.Remove(); + mlist.Remove(); + switch (TeamMission_Needs(TeamMissionType(missionlist1.Current_Index()))) { + default: + break; + + case NEED_MISSION: + mlist.Add(*commands); + break; + + case NEED_FORMATION: + flist.Add(*commands); + break; + + case NEED_WAYPOINT: + case NEED_NUMBER: + case NEED_HEX_NUMBER: + arg_edt.Add(*commands); + break; + + case NEED_QUARRY: + qlist.Add(*commands); + break; + } + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Pri:", recr_edt.X, recr_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Max:", maxnum_edt.X, maxnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Num:", initnum_edt.X, initnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Loc:", originbtn.X, originbtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("# Team Mission", missionlist2.X, missionlist2.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Trigger:", triggerbtn.X-4, triggerbtn.Y+1, scheme, TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print(Member_Description(), membersbtn.X + membersbtn.Width + 3, membersbtn.Y+1, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print("Use safest, possibly longer, route to target?", roundbtn.X+CB_SPACING_X, roundbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Charge toward target ignoring distractions?", suicidebtn.X+CB_SPACING_X, suicidebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Only 'Autocreate A.I.' uses this team type?", autocreatebtn.X+CB_SPACING_X, autocreatebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Prebuild team members before team is created?", prebuildbtn.X+CB_SPACING_X, prebuildbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Automatically reinforce team whenever possible?", reinforcebtn.X+CB_SPACING_X, reinforcebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw the buttons + */ + commands->Draw_All(); + Show_Mouse(); + + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + + /* + ** Select a Mission on the right-hand mission list; update the Argument + ** field to reflect the current value. This only serves as an aide to editing + ** the team mission list. + */ + case BUTTON_MISSION2 | KN_BUTTON: + if (missionlist2.Count() && lastcount == missionlist2.Count() && lastbutton == BUTTON_MISSION2 && lastindex == missionlist2.Current_Index()) { + missionlist1.Set_Selected_Index(missionlist2.Current_Item()->Mission); + + switch (TeamMission_Needs(missionlist2.Current_Item()->Mission)) { + case NEED_MISSION: + mlist.Set_Selected_Index(missionlist2.Current_Item()->Data.Mission); + break; + + case NEED_FORMATION: + flist.Set_Selected_Index(missionlist2.Current_Item()->Data.Formation); + break; + + case NEED_NUMBER: + sprintf(arg_edt.Get_Text(), "%d", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(arg_edt.Get_Text(), "%x", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_QUARRY: + strcpy(qlist.Get_Text(), QuarryName[missionlist2.Current_Item()->Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (missionlist2.Current_Item()->Data.Value < 26) { + sprintf(arg_edt.Get_Text(), "%c", missionlist2.Current_Item()->Data.Value + 'A'); + } else { + sprintf(arg_edt.Get_Text(), "%c%c", (missionlist2.Current_Item()->Data.Value)/26 + 'A'-1, (missionlist2.Current_Item()->Data.Value % 26) + 'A'); + } + break; + } + } + lastindex = missionlist2.Current_Index(); + lastcount = missionlist2.Count(); + display = true; + break; + + /* + ** Add current mission data to current position of team mission list. Any + ** subsequent missions get moved downward. + */ + case BUTTON_INSERT | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Insert_Item(tm); + } + display = true; + break; + + /* + ** Add mission data to the end of the mission list. + */ + case BUTTON_ADD | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A';; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Add_Item(tm); + display = true; + } + break; + + /* + ** Replace the currently selected mission with the work mission data. + */ + case BUTTON_REPLACE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = missionlist2.Current_Item(); + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A'; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2[missionlist2.Current_Index()] = tm; + } + display = true; + break; + + /* + ** Delete the currently selected mission. + */ + case BUTTON_DELETE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = missionlist2.Current_Item(); + missionlist2.Remove_Index(missionlist2.Current_Index()); + delete tm; + } + display = true; + break; + + /* + ** Invoke the members dialog + */ + case BUTTON_MEMBERS | KN_BUTTON: + + /* + ** Take editor focus away + */ + membersbtn.Turn_Off(); + + /* + ** Invoke the dialog + */ + Map.Team_Members(HousesType(housebtn.Current_Index())); + + /* + ** Redraw + */ + display = true; + break; + + /* + ** When the OK button is selected, lift the values from the dialog box + ** and place them into the team type object. + */ + case BUTTON_OK | KN_BUTTON: + strtrim(name_edt.Get_Text()); + if (strlen(name_edt.Get_Text()) != 0) { + strcpy(IniName, name_edt.Get_Text()); + } else { + strcpy(IniName, "----"); + } + + IsRoundAbout = roundbtn.IsOn; + IsSuicide = suicidebtn.IsOn; + IsAutocreate = autocreatebtn.IsOn; + IsPrebuilt = prebuildbtn.IsOn; + IsReinforcable = reinforcebtn.IsOn; + + RecruitPriority = atoi(recr_edt.Get_Text()); + InitNum = atoi(initnum_edt.Get_Text()); + MaxAllowed = atoi(maxnum_edt.Get_Text()); + House = HousesType(housebtn.Current_Index()); + Trigger = NULL; + if (triggerbtn.Current_Index() > 0) { + Trigger = TriggerTypes.Ptr(triggerbtn.Current_Index()-1); + } + + MissionCount = missionlist2.Count(); + for (index = 0; index < MissionCount; index++) { + MissionList[index].Data.Value = 0; // Clears extra bits. + MissionList[index] = *missionlist2[index]; + } + +#ifdef TOFIX +// the mission class objects pointed to in the list should be deleted? +#endif + + if (strlen(originbtn.Get_Text())) { + if (strlen(originbtn.Get_Text()) == 1) { + Origin = toupper(*originbtn.Get_Text()) - 'A'; + } else { + Origin = (toupper(*originbtn.Get_Text())+1 - 'A' ) * 26; + Origin += toupper(*(originbtn.Get_Text()+1)) - 'A'; + } + } else { + Origin = -1; + } + cancel = false; + process = false; + break; + + /* + ** Cancel: return + */ + case BUTTON_CANCEL | KN_BUTTON: + cancel = true; + process = false; + break; + + /* + ** Unrecognized events are ignored. If any button related event is + ** detected, then collapse any drop down list boxes. This keeps the + ** dialog box clean. + */ + default: + if (input & KN_BUTTON) { + housebtn.Collapse(); + missionlist1.Collapse(); + triggerbtn.Collapse(); + display = true; + } + break; + } + + /* + ** Record the last dialog control touched so that a double click + ** can be detected. + */ + if (input & KN_BUTTON) { + lastbutton = (input & ~KN_BUTTON); + } + } + + return(!cancel); +} + + +int atoh(char * str) +{ + int retval = 0; + while (*str) { + retval *= 16; + if (*str >= '0' && *str <= '9') { + retval += *str-'0'; + } else { + if (*str >= 'a' && *str <= 'f') { + retval += 10 + (*str - 'a'); + } else { + if (*str >= 'A' && *str <= 'F') { + retval += 10 + (*str - 'A'); + } + } + } + str++; + } + return(retval); +} + +#endif + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Member_Description -- Builds a member description string. * + * * + * This routine will build a team member description string. The string will be composed * + * of the team member type and quantity. As many team member types will be listed that * + * can fit within a reasonable size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text string that contains a description of the team * + * type members. * + * * + * WARNINGS: The return string may be truncated if necessary to fit within reasonable size * + * limits. * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Member_Description(void) const +{ + static char buffer[128]; + + buffer[0] = '\0'; + + /* + ** Fill in class & count for all classes + */ + for (int index = 0; index < ClassCount; index++) { + char txt[10]; + + strcat(buffer, Members[index].Class->IniName); + strcat(buffer, ":"); + + sprintf(txt, "%d", Members[index].Quantity); + strcat(buffer, txt); + + if (index < ClassCount-1) { + strcat(buffer, ","); + } + } + + if (strlen(buffer) > 25) { + strcpy(&buffer[25-3], "..."); + } + + return(buffer); +} + + +/*********************************************************************************************** + * TeamTypeClass::Description -- Builds a description of the team. * + * * + * This routine will build a brief description of the team type. This description is used * + * in the team type list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the composed text string that represents the team type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Description(void) const +{ + static char _buffer[128]; + char extra = ' '; + char loc[3]; + + loc[0] = loc[1] = loc[2] = 0; + if (IsAutocreate) extra = '*'; + if (Origin > -1) { +// if (Origin != -1) { + if (Origin < 26) { + loc[0] = 'A' + Origin; + } else { + loc[0] = Origin / 26 + 'A'-1; + loc[1] = Origin % 26 + 'A'; + } + } + + sprintf(_buffer, "%s\t%s\t%c%s\t%d\t%s", IniName, HouseTypeClass::As_Reference(House).Suffix, extra, loc, MissionCount, Member_Description()); + return(_buffer); +} + + +/*********************************************************************************************** + * TeamMissionClass::Description -- Compose a text description of team mission. * + * * + * This routine will create a text representation of a team mission. This description will * + * be used in the list of team missions for display purposes. * + * * + * INPUT: index -- The index to assign to this team. The index is used since some team * + * missions refer to the another team mission by index number. * + * * + * OUTPUT: Returns with a pointer to the team mission text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamMissionClass::Description(int index) const +{ + static char buffer[64]; + + sprintf(buffer, "%d\t%s", index, TeamTypeClass::Name_From_Mission(Mission)); + + switch (TeamMission_Needs(Mission)) { + case NEED_MISSION: + strcat(buffer, MissionClass::Mission_Name(Data.Mission)); + break; + + case NEED_FORMATION: + strcat(buffer, FormationName[Data.Quarry]); + break; + + case NEED_NUMBER: + sprintf(&buffer[strlen(buffer)], "%d", Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(&buffer[strlen(buffer)], "%x", Data.Value); + break; + + case NEED_QUARRY: + strcat(buffer, QuarryName[Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (Data.Value < 26) { + sprintf(&buffer[strlen(buffer)], "%c", Data.Value + 'A'); + } else { + sprintf(&buffer[strlen(buffer)], "%c%c", (Data.Value/26) + 'A'-1, (Data.Value % 26) + 'A'); + } + break; + + } + + return(buffer); +} +#endif + + +/*********************************************************************************************** + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * * + * This routine is called when some object is about to be removed from the game system and * + * all references to it must be severed. This will check to see if the specified object * + * is a trigger that this team refers to. If so, then the reference will be cleared. * + * * + * INPUT: target -- The target object to remove references to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Detach(TARGET target, bool) +{ + if (Is_Target_TriggerType(target) && Trigger.Is_Valid() && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*************************************************************************** + * TeamTypeClass::Read_INI -- reads INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_INI(CCINIClass & ini) +{ + TeamTypeClass * team; // Working team pointer. + char buf[500]; // INI entry buffer + + int len = ini.Entry_Count(INI_Name()); + + /* + ** Loop for all team entries, create and fill in. + */ + for (int index = 0; index < len; index++) { + team = new TeamTypeClass(); + if (team != NULL) { + char const * entry = ini.Get_Entry(INI_Name(), index); + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + team->Fill_In((char *)entry, buf); + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given teamtype with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + * 11/29/1995 JLB : Revamped to use new team class. * + *=============================================================================================*/ +void TeamTypeClass::Fill_In(char * name, char * entry) +{ + assert(TeamTypes.ID(this) == ID); + + /* + ** Set its name + */ + Set_Name(name); + + House = HousesType(atoi(strtok(entry, ","))); + + int code; + switch (NewINIFormat) { + default: + code = atoi(strtok(NULL, ",")); + IsRoundAbout = ((code & 0x0001) != 0); + IsSuicide = ((code & 0x0002) != 0); + IsAutocreate = ((code & 0x0004) != 0); + IsPrebuilt = ((code & 0x0008) != 0); + IsReinforcable = ((code & 0x0010) != 0); + break; + + case 0: + case 1: + IsRoundAbout = atoi(strtok(NULL, ",")); + IsSuicide = atoi(strtok(NULL, ",")); + IsAutocreate = atoi(strtok(NULL, ",")); + IsPrebuilt = atoi(strtok(NULL, ",")); + IsReinforcable = atoi(strtok(NULL, ",")); + break; + } + + RecruitPriority = atoi(strtok(NULL, ",")); + InitNum = atoi(strtok(NULL, ",")); + MaxAllowed = atoi(strtok(NULL, ",")); + Origin = atoi(strtok(NULL, ",")); + + switch (NewINIFormat) { + default: + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + break; + + case 0: + case 1: + // Throw this token away -- it isn't used. + strtok(NULL, ","); + break; + } + + /* + ** Fetch the team member types and quantity values. + */ + ClassCount = atoi(strtok(NULL, ",")); + for (int index = 0; index < ClassCount; index++) { + char * p1 = strtok(NULL, ",:"); + char * p2 = strtok(NULL, ",:"); + TechnoTypeClass const * otype = NULL; + + /* + ** See if this is an infantry name + */ + InfantryType i_id = InfantryTypeClass::From_Name(p1); + if (i_id != INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } else { + + /* + ** See if this is a unit name + */ + UnitType u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } else { + + /* + ** See if this is an aircraft name + */ + AircraftType a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } else { + + /* + ** See if this is a vessel name. + */ + VesselType v_id = VesselTypeClass::From_Name(p1); + if (v_id != VESSEL_NONE) { + otype = &VesselTypeClass::As_Reference(v_id); + } + } + } + } + + /* + ** If the name was resolved, add this class + */ + if (otype) { + if (index < MAX_TEAM_CLASSCOUNT) { + Members[index].Class = otype; + Members[index].Quantity = atoi(p2); + } + } else { + ClassCount--; + if (index == 0) break; + index--; + } + } + ClassCount = min(MAX_TEAM_CLASSCOUNT, ClassCount); + + /* + ** Fetch the missions assigned to this team type. + */ + MissionCount = atoi(strtok(NULL, ",")); + for (int index = 0; index < MissionCount; index++) { + MissionList[index].Mission = TeamMissionType(atoi(strtok(NULL, ",:"))); + MissionList[index].Data.Value = atoi(strtok(NULL, ",:")); + } + + if (NewINIFormat < 2) { + /* + ** Fetch the trigger ID. + */ + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * * + * This routine will take all team types and write them out to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that will hold al the teams. * + * * + * OUTPUT: none * + * * + * WARNINGS: All preexisting team data in the database will be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("TeamTypes"); + ini.Clear(INI_Name()); + + /* + ** Now write all the team data out + */ + for (int index = 0; index < TeamTypes.Count(); index++) { +// for (int index = TeamTypes.Count()-1; index >= 0; index--) { + TeamTypeClass * team = TeamTypes.Ptr(index); + char buf[256]; + + buf[0] = 0; + team->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), team->IniName, buf); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * * + * This routine is used to build the text string that will go into the INI database for * + * a team of this type. This text string will be parsed back into a team object when the * + * scenario INI is read in. * + * * + * INPUT: buf -- Pointer to a buffer that will hold the team text entry. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the buffer can hold the string build. A size of 80 or so is * + * usually sufficient. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Build_INI_Entry(char * buf) +{ + int code = 0; + code |= IsRoundAbout ? 0x0001 : 0; + code |= IsSuicide ? 0x0002 : 0; + code |= IsAutocreate ? 0x0004 : 0; + code |= IsPrebuilt ? 0x0008 : 0; + code |= IsReinforcable ? 0x0010 : 0; + + /* + ** Output the general data for this team type. + */ + sprintf(buf, "%d,%d,%d,%d,%d,%d,%d", + House, + code, + RecruitPriority, + InitNum, + MaxAllowed, + Origin, + TriggerTypes.Logical_ID(Trigger) + ); + buf += strlen(buf); + + /* + ** For every class in the team, record the class's name & desired count + */ + sprintf (buf, ",%d", ClassCount); + buf += strlen(buf); + for (int i = 0; i < ClassCount; i++) { + sprintf (buf, ",%s:%d", Members[i].Class->IniName, Members[i].Quantity); + buf += strlen(buf); + } + + /* + ** Record the # of missions, and each mission name & argument value. + */ + sprintf(buf, ",%d", MissionCount); + buf += strlen(buf); + for (int i = 0; i < MissionCount; i++) { + sprintf (buf, ",%d:%d", MissionList[i].Mission, MissionList[i].Data.Value); + buf += strlen(buf); + } +} + + diff --git a/REDALERT/TEAMTYPE.H b/REDALERT/TEAMTYPE.H new file mode 100644 index 000000000..905f55765 --- /dev/null +++ b/REDALERT/TEAMTYPE.H @@ -0,0 +1,282 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAMTYPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAMTYPE_H +#define TEAMTYPE_H + +/* +** TeamMissionType: the various missions that a team can have. +*/ +typedef enum TeamMissionType : char { + TMISSION_NONE=-1, + TMISSION_ATTACK, // Attack specified quarry type. + TMISSION_ATT_WAYPT, // Attack specified waypoint + TMISSION_FORMATION, // Change formation of team. + TMISSION_MOVE, // moves to waypoint specified. + TMISSION_MOVECELL, // moves to cell # specified. + TMISSION_GUARD, // works like an infantry's guard mission + TMISSION_LOOP, // loop back to start of mission list + TMISSION_ATTACKTARCOM, // attack tarcom + TMISSION_UNLOAD, // Unload at current location. + TMISSION_DEPLOY, // Deploy mobile building type. + TMISSION_HOUND_DOG, // Follow nearest friendly unit. + TMISSION_DO, // Do guard, sticky, area guard (mission sticks on this). + TMISSION_SET_GLOBAL, // Set global variable. + TMISSION_INVULNERABLE, // Magical invulnerability. + TMISSION_LOAD, // Load onto transport member of team. + TMISSION_SPY, // Spy enter the building at specified waypoint + TMISSION_PATROL, // Move but look for enemies as well. + + TMISSION_COUNT, + TMISSION_FIRST=0 +} TeamMissionType; + + +/* +** Forward declarations. +*/ +class TechnoTypeClass; + + +/* +** This structure contains one team mission value & its argument. +*/ +class TeamMissionClass +{ + public: +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(int index) const; + operator const char * () const {return(Description(0));}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags); + + TeamMissionType Mission; // Mission type. + union { + FormationType Formation; // Formation to use. + QuarryType Quarry; // Combat quarry type. + MissionType Mission; // General mission orders. + int Value; // Usually a waypoint number. + } Data; +}; + + +/* +** This class specifies the quantity and type of members desired for the +** team. +*/ +class TeamMemberClass +{ + public: + int Quantity; // Number of objects desired for this type. + TechnoTypeClass const * Class; // The type of object desired. +}; + + +/* +** TeamTypeClass declaration +*/ +class TeamTypeClass : public AbstractTypeClass +{ + public: + enum TeamTypeClassEnums { + MAX_TEAM_CLASSCOUNT=5, + MAX_TEAM_MISSIONS=20 + }; + + /* + ** Constructor/Destructor + */ + TeamTypeClass(void); + TeamTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Trigger(x) {}; + virtual ~TeamTypeClass(void) {}; + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all team types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + void Build_INI_Entry(char * buffer); + static void Read_INI(CCINIClass & ini); + void Fill_In(char *name, char *entry); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "TeamTypes";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give its name + */ + static TeamTypeClass *As_Pointer(char const * name); + + /* + ** Processing routines + */ + TeamClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + void Detach(TARGET target, bool all=true); + + /* + ** Utility routines + */ + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + static char const * Name_From_Mission(TeamMissionType order); + static TeamMissionType Mission_From_Name(char const *name); + static TeamTypeClass const * Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted); + static TeamTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Member_Description(void) const; + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + + /* + ** If this teamtype object is active, then this flag will be true. + ** TeamType objects that are not active are either not yet created or have + ** been deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** If RoundAbout, the team avoids high-threat areas + */ + unsigned IsRoundAbout:1; + + /* + ** If Suicide, the team won't stop until it achieves its mission or it's + ** dead + */ + unsigned IsSuicide:1; + + /* + ** Is this team type allowed to be created automatically by the computer + ** when the appropriate trigger indicates? + */ + unsigned IsAutocreate:1; + + /* + ** This flag tells the computer that it should build members to fill + ** a team of this type regardless of whether there actually is a team + ** of this type active. + */ + unsigned IsPrebuilt:1; + + /* + ** If this team should allow recruitment of new members, then this flag + ** will be true. A false value results in a team that fights until it + ** is dead. This is similar to IsSuicide, but they will defend themselves. + */ + unsigned IsReinforcable:1; + + /* + ** A transient team type was created exclusively to bring on reinforcements + ** as a result of some special event. As soon as there are no teams + ** existing of this type, then this team type should be deleted. + */ + unsigned IsTransient:1; + + /* + ** Priority given the team for recruiting purposes; higher priority means + ** it can steal members from other teams (scale: 0 - 15) + */ + int RecruitPriority; + + /* + ** Initial # of this type of team + */ + unsigned char InitNum; + + /* + ** Max # of this type of team allowed at one time + */ + unsigned char MaxAllowed; + + /* + ** Fear level of this team + */ + unsigned char Fear; + + /* + ** House the team belongs to + */ + HousesType House; + + /* + ** Trigger to assign to each object as it joins this team. + */ + CCPtr Trigger; + + /* + ** This is the waypoint origin to use when creating this team or + ** when bringing the team on as a reinforcement. + */ + WAYPOINT Origin; + + /* + ** This records the number of teams of this type that are currently + ** active. + */ + int Number; + + /* + ** Number and list of missions that this team will follow. + */ + int MissionCount; + TeamMissionClass MissionList[MAX_TEAM_MISSIONS]; + + /* + ** Number and type of members desired for this team. + */ + int ClassCount; + TeamMemberClass Members[MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + static char const * TMissions[TMISSION_COUNT]; +}; + + +#endif \ No newline at end of file diff --git a/REDALERT/TECHNO.CPP b/REDALERT/TECHNO.CPP new file mode 100644 index 000000000..2a9e3c804 --- /dev/null +++ b/REDALERT/TECHNO.CPP @@ -0,0 +1,6913 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TECHNO.CPP 5 3/17/97 1:28a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 8, 1994 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TechnoClass::AI -- Handles AI processing for techno object. * + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * TechnoClass::Captured -- Handles capturing this object. * + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon.* + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate.* + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * TechnoClass::Mark -- Handles marking of techno objects. * + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * TechnoClass::Owner -- Who is the owner of this object? * + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * TechnoClass::Renovate -- Heal a building to maximum * + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * TechnoClass::Set_Mission -- Forced mission set (used by editor). * + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * TechnoClass::Value -- Fetches the target value for this object. * + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Cloaking control values. +*/ +#define MAX_UNCLOAK_STAGE 38 + +//Added for getting the input for special character keys from the client +// - 6/26/2019 JAS +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + + +/*************************************************************************** +** These are the pointers to the special shape data that the units may need. +*/ +void const * TechnoTypeClass::WakeShapes = 0; +void const * TechnoTypeClass::TurretShapes = 0; +void const * TechnoTypeClass::SamShapes = 0; +void const * TechnoTypeClass::MGunShapes = 0; + +//Xlat Tables for French and German +// For name overriding +//#define NEWNAMES 22 +#ifdef GERMAN +const char* NewName[] = { + "Scout Ant", "Kundschafter-Ameise", + "Warrior Ant", "Krieger-Ameise", + "Fire Ant", "Feuer-Ameise", + "Queen Ant", "Ameisenk”nigin", + "ATS", "Angriffs-U-Boot", + "Tesla Tank", "Telsapanzer", + "Volkov", "Modell Volkov", + "Chitzkoi", "Roboterhund", + "Stavros", "Stavros", + "F-A Longbow", "Jagdhubschrauber Longbow", + "Civilian Specialist", "Wissenschaftler", + "Alloy Facility", "Legierungswerk", + NULL, + }; + +#endif +#ifdef FRENCH +const char* NewName[] = { + "Scout Ant", "Fourmi de Reconnaissance", + "Warrior Ant", "Fourmi GuerriŠre", + "Fire Ant", "Fourmi Lance-Flammes", + "Queen Ant", "Reine des Fourmis", + "ATS", "SMTA", + "Tesla Tank", "Tank Tesla", + "Volkov", "Volkov", + "Chitzkoi", "Cyber-Chien", + "Stavros", "Stavros", + "F-A Longbow", "HAA (H‚licoptŠre d'Assaut Avanc‚)", + "Civilian Specialist", "Sp‚cialiste Civil", + "Alloy Facility", "Usine M‚tallurgique", + NULL, + }; + +#endif + + +/*************************************************************************** +** Which shape to use depending on which facing is controlled by these arrays. +*/ +int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + + +/*********************************************************************************************** + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * * + * The player's army is considered to be all those mobile units that can be selected * + * and controlled by the player (they may or may not have weapons in the traditional * + * sense). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object part of the player's selectable controllable army? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Players_Army(void) const +{ + /* + ** An object that is dead (or about to be) is not considered part of + ** the player's army. + */ + if (Strength <= 0) { + return(false); + } + + /* + ** If the object is not yet on the map or is otherwise indisposed, then + ** don't consider it. + */ + if (IsInLimbo || !IsLocked) { + return(false); + } + + /* + ** Buildings, although sometimes serving a combat purpose, are not part + ** of the player's army. + */ + if (What_Am_I() == RTTI_BUILDING) { + return(false); + } + + /* + ** If not discoverd by the player, then don't consider it part of the + ** player's army (yet). + */ + if (!Is_Discovered_By_Player()) { + return(false); + } + + /* + ** If not selectable, then not really part of the player's active army. + */ + if (!Techno_Type_Class()->IsSelectable) { + return(false); + } + + /* + ** If not under player control, then it isn't part of the player's army. + */ + if (!House->IsPlayerControl) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * * + * Use this routine to examine the cell specified and if the cell could be a valid * + * destination for a teleport, then return true. * + * * + * INPUT: cell -- The cell to examine as a possible legal teleport destination. * + * * + * OUTPUT: bool; Is the specified cell a legal teleport destination? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Teleport_Here(CELL cell) const +{ + /* + ** Infantry are never allowed to teleport. + */ + if (What_Am_I() == RTTI_INFANTRY) { + return(false); + } + + /* + ** The destination cell must be on the playfield. + */ + if (!Map.In_Radar(cell)) { + return(false); + } + + /* + ** Teleporting directly onto the flag spot is not allowed. This is due to the + ** requirement that entering that location must proceed under normal channels. + */ + if (Map[cell].Overlay == OVERLAY_FLAG_SPOT) { + return(false); + } + + /* + ** Determine if the object could enter the cell by normal means. If the + ** cell is impassable, then it can't be teleported there. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (!Map[cell].Is_Clear_To_Move(ttype->Speed, true, true, -1, ttype->Speed == SPEED_FLOAT ? MZONE_WATER : MZONE_NORMAL)) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * * + * This routine will compare the weapons this object is equipped with verses the * + * candidate target object. The best weapon to use against the target will be returned. * + * Special emphasis is given to weapons that can fire on the target without requiring * + * this object to move within range. * + * * + * INPUT: target -- The candidate target to determine which weapon is best against. * + * * + * OUTPUT: Returns with an identifier the specifies what weapon to use against the target. * + * The return value will be "0" for the primary weapon and "1" for the secondary. * + * * + * WARNINGS: This routine is called very frequently. It should be as efficient as possible. * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::What_Weapon_Should_I_Use(TARGET target) const +{ + if (!Target_Legal(target)) return(0); + + /* + ** Fetch the armor of the candidate target object. Presume that if the target + ** is not an object, then its armor is equivalent to wood. Who knows why? + */ + ArmorType armor = ARMOR_WOOD; + if (Is_Target_Object(target)) { + armor = As_Object(target)->Class_Of().Armor; + } + + TechnoTypeClass const * ttype = Techno_Type_Class(); + + /* + ** Get the value of the primary weapon verses the candidate target. Increase the + ** value of the weapon if it happens to be in range. + */ + int w1 = 0; + WeaponTypeClass const * wptr = ttype->PrimaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w1 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 0)) w1 *= 2; + FireErrorType ok = Can_Fire(target, 0); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w1 = 0; + + /* + ** Calculate a similar value for the secondary weapon. + */ + int w2 = 0; + wptr = ttype->SecondaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w2 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 1)) w2 *= 2; + ok = Can_Fire(target, 1); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w2 = 0; + + /* + ** Return with the weapon identifier that should be used to fire upon the + ** candidate target. + */ + if (w2 > w1) return(1); + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * * + * This routine will determine the number of survivors that should run from this object * + * when it is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of survivors that should run from this object * + * when the object gets destroyed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::How_Many_Survivors(void) const +{ + if (Techno_Type_Class()->IsCrew) { + return(1); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon. * + * * + * This routine will examine the specified weapon of this object and determine how much * + * damage it could inflict -- best case. * + * * + * INPUT: which -- Which weapon to consider. If -1 is specified, then the average of both * + * weapon types (if present) is returned. * + * * + * OUTPUT: Returns with the combat damage that could be inflicted by the specified weapon. * + * * + * WARNINGS: This routine could return a negative number if a medic is scanned. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Combat_Damage(int which) const +{ + TechnoTypeClass const * ttype = Techno_Type_Class(); + + int divisor = 0; + int value = 0; + + if (which == 0 || which == -1) { + if (ttype->PrimaryWeapon != NULL) { + value += ttype->PrimaryWeapon->Attack; + divisor += 1; + } + } + + if (which == 1 || which == -1) { + if (ttype->SecondaryWeapon != NULL) { + value += ttype->SecondaryWeapon->Attack; + divisor += 1; + } + } + + if (divisor > 1) { + return(value / divisor); + } + return(value); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * * + * Determine is this object can recloak now and returns that info. Usually the answer is * + * yes, but it can be overridden be derived classes. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object recloak now? (presumes it has the ability to cloak) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Recloak(void) const +{ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * * + * This routine will determine the coordinate to use when this object fires. The coordinate * + * is the location where bullets appear (or fire effects appear) when the object fires * + * its weapon. * + * * + * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * + * weapon, 1 means secondary weapon. * + * * + * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * + * should appear. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TechnoClass::Fire_Coord(int which) const +{ + assert(IsActive); + + DirType dir = Turret_Facing(); + TechnoTypeClass const * tclass = Techno_Type_Class(); + + int dist = 0; + int lateral = 0; + if (which == 0) { + dist = tclass->PrimaryOffset; + lateral = tclass->PrimaryLateral; + } else { + dist = tclass->SecondaryOffset; + lateral = tclass->SecondaryLateral; + } + + COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height); + coord = Coord_Move(coord, DIR_E, tclass->HorizontalOffset); + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); + } + coord = Coord_Move(coord, dir, dist); + + return(coord); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * * + * This routine is used to dump the status of the object class to the monochrome screen. * + * This display can be used to track down or prevent bugs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + mono->Set_Cursor(10, 7);mono->Printf("%2d", Fetch_Rate()); + mono->Set_Cursor(14, 7);mono->Printf("%2d", Fetch_Stage()); + mono->Set_Cursor(49, 1);mono->Printf("%3d", Ammo); + mono->Set_Cursor(71, 1);mono->Printf("$%4d", PurchasePrice); + mono->Set_Cursor(54, 1);mono->Printf("%3d", (long)Arm); + if (Is_Something_Attached()) { + mono->Set_Cursor(1, 5);mono->Printf("%08X", Attached_Object()); + } + if (Target_Legal(TarCom)) { + mono->Set_Cursor(29, 3);mono->Printf("%08X", TarCom); + } + if (Target_Legal(SuspendedTarCom)) { + mono->Set_Cursor(38, 3);mono->Printf("%08X", SuspendedTarCom); + } + if (Target_Legal(ArchiveTarget)) { + mono->Set_Cursor(69, 5);mono->Printf("%08X", ArchiveTarget); + } + mono->Set_Cursor(47, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); + mono->Set_Cursor(64, 1);mono->Printf("%d(%d)", Cloak, CloakingDevice); + + mono->Fill_Attrib(14, 15, 12, 1, IsUseless ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 16, 12, 1, IsTickedOff ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 17, 12, 1, IsCloakable ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 13, 12, 1, IsLeader ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 14, 12, 1, IsALoaner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 15, 12, 1, IsLocked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 16, 12, 1, IsInRecoilState ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 17, 12, 1, IsTethered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 13, 12, 1, IsOwnedByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 14, 12, 1, IsDiscoveredByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 15, 12, 1, IsDiscoveredByComputer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 16, 12, 1, IsALemon ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(47, 17);mono->Printf("%3d", (long)IronCurtainCountDown); + mono->Fill_Attrib(40, 17, 12, 1, IronCurtainCountDown > 0 ? MonoClass::INVERSE : MonoClass::NORMAL); + + RadioClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * * + * This default constructor for techno objects is used to reset all internal variables to * + * their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) : + RadioClass(rtti, id), + IsUseless(false), + IsTickedOff(false), + IsCloakable(false), + IsLeader(false), + IsALoaner(false), + IsLocked(false), + IsInRecoilState(false), + IsTethered(false), + IsOwnedByPlayer(false), + IsDiscoveredByPlayer(false), + IsDiscoveredByComputer(false), + IsALemon(false), + IsSecondShot(true), + ArmorBias(1), + FirepowerBias(1), + IdleTimer(0), + IronCurtainCountDown(0), + SpiedBy(0), + ArchiveTarget(TARGET_NONE), + House(HouseClass::As_Pointer(house)), + Cloak(UNCLOAKED), + TarCom(TARGET_NONE), + SuspendedTarCom(TARGET_NONE), + PrimaryFacing(DIR_N), + Arm(0), + Ammo(-1), + ElectricZapDelay(-1), + ElectricZapTarget(0), + ElectricZapWhich(0), + PurchasePrice(0) +{ + //IsOwnedByPlayer = (PlayerPtr == House); + // Added for multiplayer changes. ST - 4/24/2019 10:40AM + IsDiscoveredByPlayerMask = 0; + if (Session.Type == GAME_NORMAL) { + IsOwnedByPlayer = (PlayerPtr == House); + } else { + IsOwnedByPlayer = House->IsHuman; + } +} + + +/*********************************************************************************************** + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * * + * Use this routine to determine the amount of time it would take to build an object of * + * this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time it should take (unmodified by outside factors) to build * + * this object. The time is expressed in game frames. * + * * + * WARNINGS: The time will usually be modified by power status and handicap rating for the * + * owning house. The value returned is merely the raw unmodified time to build. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 09/27/1996 JLB : Takes into account the power availability. * + *=============================================================================================*/ +//#define UNIT_BUILD_BIAS fixed(5,4) +//#define UNIT_BUILD_BIAS fixed(6,4) +#define UNIT_BUILD_BIAS fixed(1,1) +//#define UNIT_BUILD_BIAS fixed(5,1) + +extern int UnitBuildPenalty; + +int TechnoClass::Time_To_Build(void) const +{ + int val = Class_Of().Time_To_Build(); + +#ifdef FIXIT_VERSION_3 + if (Session.Type == GAME_NORMAL ) { +#else + if (Session.Type == GAME_NORMAL || + PlayingAgainstVersion == VERSION_RED_ALERT_104 || + PlayingAgainstVersion == VERSION_RED_ALERT_107){ +#endif + val *= House->BuildSpeedBias; + }else{ + if (What_Am_I() == RTTI_BUILDING || What_Am_I() == RTTI_INFANTRY) { + val *= House->BuildSpeedBias; + } else { + val *= House->BuildSpeedBias * fixed (UnitBuildPenalty, 100); //UNIT_BUILD_BIAS; + } + } + + + /* + ** Adjust the time to build based on the power output of the owning house. + */ + fixed power = House->Power_Fraction(); + if (power > 1) power = 1; + if (power < 1 && power > fixed::_3_4) power = fixed::_3_4; + if (power < fixed::_1_2) power = fixed::_1_2; + power.Inverse(); + val *= power; + + int divisor = House->Factory_Count(What_Am_I()); + if (divisor != 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack: allow the multiple-factory bonus, but only up to two factories if +// this is an AM<->AM game. + if(NewUnitsEnabled) { + val /= min(divisor,2); + } else { + val /= divisor; + } +#else + val /= divisor; +#endif + } + return(val); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * * + * Use this routine to determine if this object should be visible on the player's radar * + * screen. This routine doesn't take into consideration mapped terrain or whether the * + * radar is active. It merely checks to see that if all else is functioning correctly, * + * is this unit invisible or visible on the radar map. Typically, cloaked vehicles will * + * not be visible. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object nominally visible on the player's radar screen? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Visible_On_Radar(void) const +{ + /* + ** Hack: MRJ is invisible to radar, unless it's allied with the player. + */ + if (What_Am_I() == RTTI_UNIT) { + if(*((UnitClass *)this) == UNIT_MRJ) { + if(!House->Is_Ally(PlayerPtr)) { + return(false); + } + } + } + if (!Techno_Type_Class()->IsInvisible && (Cloak != CLOAKED || House->Is_Ally(PlayerPtr))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + assert(IsActive); + + /* Replaced by client/server version from TD. ST - 8/7/2019 11:09AM + if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + if (house != PlayerPtr) { + if (IsDiscoveredByComputer) return(false); + IsDiscoveredByComputer = true; + } + */ + if (house == NULL) { + return false; + } + + if (Is_Discovered_By_Player(house)) { + return false; + } + + if (house->IsHuman == false) { + if (IsDiscoveredByComputer) { + return false; + } + IsDiscoveredByComputer = true; + } + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (Session.Type == GAME_NORMAL) { + if (house == PlayerPtr) { + IsDiscoveredByPlayer = true; + + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (!ScenarioInit && Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + /* + ** A newly revealed object will always perform a look operation. + */ + Look(); + } + } else { + IsDiscoveredByComputer = true; + } + + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } + + Look(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * * + * This routine is called for every object that returns to the darkness shroud or when * + * it gets destroyed. This also occurs when an object enters another (such as infantry * + * entering a transport helicopter). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Hidden(void) +{ + assert(IsActive); + + //if (!IsDiscoveredByPlayer) return; + if (!Is_Discovered_By_Player()) { // ST - 8/7/2019 11:17AM + return; + } + if (!House->IsHuman) { + Clear_Discovered_By_Players(); + } +} + + +/*********************************************************************************************** + * TechnoClass::Mark -- Handles marking of techno objects. * + * * + * On the Techno-level, marking handles transmission of the redraw command to any object * + * that it is 'connected' with. This only occurs during loading and unloading. * + * * + * INPUT: mark -- The marking method. This routine just passes this on to base classes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Mark(MarkType mark) +{ + assert(IsActive); + + if (RadioClass::Mark(mark)) { + + /* + ** When redrawing an object, if there is another object tethered to this one, + ** redraw it as well. + */ + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * * + * This routine is used to handle inbound radio messages. It only handles those messages * + * that deal with techno objects. Typically, these include loading and unloading. * + * * + * INPUT: from -- Pointer to the originator of the radio message. * + * * + * message -- The inbound radio message. * + * * + * param -- Reference to optional parameter that might be used to transfer * + * more information than is possible with the simple radio message * + * type. * + * * + * OUTPUT: Returns with the radio response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 06/17/1995 JLB : Handles tether contact messages. * + *=============================================================================================*/ +RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Just received instructions to attack the specified target. + */ + case RADIO_ATTACK_THIS: + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + Assign_Target(param); + Assign_Mission(MISSION_ATTACK); + return(RADIO_ROGER); + } + break; + + /* + ** Establish a tethered connection to the object in radio contact. + */ + case RADIO_TETHER: + if (!IsTethered) { + IsTethered = true; + Transmit_Message(RADIO_TETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** Break the tethered connection to the object in radio contact. + */ + case RADIO_UNTETHER: + if (IsTethered) { + IsTethered = false; + Transmit_Message(RADIO_UNTETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** A tethered unit has reached it's destination. All is + ** clear, so radio contact can be broken. + */ + case RADIO_UNLOADED: + Transmit_Message(RADIO_UNTETHER, from); + return(Transmit_Message(RADIO_OVER_OUT, from)); + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + Transmit_Message(RADIO_UNTETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Request to be informed when unloaded. This message is transmitted + ** by the transport unit to the transported unit as it is about to be + ** unloaded. It is saying, "All is clear. Drive off now." + */ + case RADIO_UNLOAD: + case RADIO_BACKUP_NOW: + case RADIO_HOLD_STILL: + Transmit_Message(RADIO_TETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Handle reloading one ammo point for this unit. + */ + case RADIO_RELOAD: + if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); +/*BG*/ Mark(MARK_CHANGE_REDRAW); + Ammo++; + return(RADIO_ROGER); + + /* + ** Handle repair of this unit. + */ + case RADIO_REPAIR: + /* + ** If it's a mine layer, re-arm him if he's empty. This always takes precedence + ** over repair, since this operation is free. + */ + if (What_Am_I() == RTTI_UNIT && *((UnitClass *)this) == UNIT_MINELAYER && ((UnitClass *)this)->Ammo < ((UnitClass *)this)->Class->MaxAmmo) { + ((UnitClass *)this)->Ammo = ((UnitClass *)this)->Class->MaxAmmo; + return(RADIO_NEGATIVE); + } + + /* + ** Determine if this unit can be repaired becaause it is under strength. If so, then + ** proceed with the repair process. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + int cost = Techno_Type_Class()->Repair_Cost(); + cost = max(cost, 1); + int step = Techno_Type_Class()->Repair_Step(); + step = max(step, 1); + + /* + ** If there is sufficient money to repair the unit one step, then do so. + ** Otherwise return with a "can't complete" radio response. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + /* + ** Return with either an all ok or mission accomplished radio message. This + ** lets the repairing object know if it should abort the repair control process + ** or continue it. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + return(RADIO_ROGER); + } else { + Strength = Techno_Type_Class()->MaxStrength; + return(RADIO_ALL_DONE); + } + } else { + return(RADIO_CANT); + } + } + return(RADIO_NEGATIVE); + + default: + break; + } + return(RadioClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * + * * + * This routine handles marking a game object as not a loaner. It is set only if the unit * + * is not player owned and is on the regular map. This is necessary so that enemy objects * + * can exist off-map but as soon as they move onto the map, are flagged so that can never * + * leave it again. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Handles scanner units. * + * 12/27/1994 JLB : Checks for an processes any trigger in cell. * + *=============================================================================================*/ +void TechnoClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Center_Coord()); + + /* + ** When enemy units enter the proper map area from off map, they are + ** flagged so that they won't travel back off the map again. + */ + if (!IsLocked && Map.In_Radar(cell)) { + IsLocked = true; + } + + /* + ** If this object somehow moves into mapped terrain, but is not yet + ** discovered, then flag it to be discovered. + */ + // Change for client/server multiplayer ST - 8/7/2019 11:19AM + //if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { + //Revealed(PlayerPtr); + if (!Is_Discovered_By_Player() && Map[cell].Is_Visible(House)) { + Revealed(House); + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * * + * This routine will draw the common elements for techno type objects. This element is * + * the health bar. The main game object has already been rendered by the time this * + * routine is called. * + * * + * INPUT: x,y -- The coordinate of the center of the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Knows about radar scanned cells. * + * 12/13/1994 JLB : Clips health bar against map edge. * + * 01/23/1995 JLB : Dynamic selected object rectangle. * + *=============================================================================================*/ +void TechnoClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Tells the door logic that it has been drawn. + */ + ((TechnoClass *)this)->Clear_Redraw_Flag(); + + /* + ** Draw electric zap + */ + if ((ElectricZapDelay >= 0) && ElectricZapTarget) { + Electric_Zap(ElectricZapTarget, ElectricZapWhich, window); + } + + int width, height; + Class_Of().Dimensions(width, height); + + const bool show_health_bar = (Strength > 0) && (Is_Selected_By_Player() || + ((Rule.HealthBarDisplayMode == RulesClass::HB_DAMAGED) && (Strength < Techno_Type_Class()->MaxStrength)) || + (Rule.HealthBarDisplayMode == RulesClass::HB_ALWAYS)); + + /* + ** Draw the selected object graphic. + */ + int lx = width / 2; + int ly = height / 2; + int dx = width / 5; + int dy = height / 5; + int fudge = show_health_bar ? 4 : 0; + if (What_Am_I() == RTTI_VESSEL) { + lx = width / 2; + } + + //if (IsSelected) { + if (Is_Selected_By_Player() || show_health_bar) { + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + + + /* + ** The infantry select box should be a bit higher than normal. + */ + if (What_Am_I() == RTTI_INFANTRY) { + y -= 6; + } + + if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { + y -= 5; + } + + /* + ** Fetch the dimensions of the object. These dimensions will be used to draw + ** the selection box and the health bar. + */ + + if (show_health_bar) { + fixed ratio = Health_Ratio(); + int pwidth; // Pixel width of bar interior. + int color; // The color to give the interior of the bargraph. + + int xx = x-width/2; + int yy = y-(height/2); + + /* + ** Draw the outline of the bargraph. + */ + draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); + draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); + + /* + ** Determine the width of the interior strength + ** graph. + */ + pwidth = (width-2) * ratio; + + pwidth = Bound(pwidth, 1, width-2); + + color = LTGREEN; + if (ratio <= Rule.ConditionYellow) { + color = YELLOW; + } + if (ratio <= Rule.ConditionRed) { + color = RED; + } + draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); + } + + /* + ** Draw the selected object graphic. + */ + if (Is_Selected_By_Player()) { + // Upper left corner. + draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx + dx, fudge + y - ly, WHITE); + draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx, fudge + y - ly + dy, WHITE); + + // Upper right corner. + draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx - dx, fudge + y - ly, WHITE); + draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx, fudge + y - ly + dy, WHITE); + + // Lower right corner. + draw_window.Draw_Line(x + lx, y + ly, x + lx - dx, y + ly, WHITE); + draw_window.Draw_Line(x + lx, y + ly, x + lx, y + ly - dy, WHITE); + + // Lower left corner. + draw_window.Draw_Line(x - lx, y + ly, x - lx + dx, y + ly, WHITE); + draw_window.Draw_Line(x - lx, y + ly, x - lx, y + ly - dy, WHITE); + } + } + + // MBL 04.21.2020 + bool selected = Is_Selected_By_Player() || Rule.ResourceBarDisplayMode == RulesClass::RB_ALWAYS; + // if ((window == WINDOW_VIRTUAL) || (Is_Selected_By_Player() && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) + if ((window == WINDOW_VIRTUAL) || (selected && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) + { + Draw_Pips((x-lx)+5, y+ly-3, window); + } +} + + +/*********************************************************************************************** + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * * + * This routine handles the common operation between techno objects when they are * + * unlimboed. This includes revealing the map. * + * * + * INPUT: coord -- The coordinate to unlimbo object at. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + if (RadioClass::Unlimbo(coord, dir)) { + PrimaryFacing = dir; + Enter_Idle_Mode(true); + Commence(); + + IsLocked = Map.In_Radar(Coord_Cell(coord)); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine is used to compare the distance to the specified target with the range * + * of the weapon. If the target is outside of weapon range, then false is returned. * + * * + * INPUT: target -- The target to check if it is within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the specified target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(TARGET target, int which, bool reciprocal_check) const +{ + assert(IsActive); + + if (IsLocked && Target_Legal(target)) { + int range = Weapon_Range(which); + BuildingClass const * building = As_Building(target); + if (building != NULL) { + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check && !building) { + ObjectClass *target_object = As_Object(target); + if (target_object) { + RTTIType my_type = What_Am_I(); + if (target_object->What_Am_I() == my_type) { + if (range == target_object->Weapon_Range(which)) { + TechnoClass *tech = As_Techno(target); + if (tech->In_Range(As_Target(), which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine will determine if the pointer to the target object passed into this * + * routine is within weapon range. * + * * + * INPUT: target -- Pointer to the target object to check if within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(ObjectClass const * target, int which, bool reciprocal_check) const +{ + assert(IsActive); + + if (IsLocked && target) { + int range = Weapon_Range(which); + if (target->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * building = (BuildingClass const *)target; + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check) { + RTTIType my_type = What_Am_I(); + if (my_type != RTTI_BUILDING) { + if (target->What_Am_I() == my_type) { + if (range == target->Weapon_Range(which)) { + if (((TechnoClass*)target)->In_Range(this, which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * * + * Use this routine to determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to examine against the object to determine range. * + * * + * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the weapon within range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/16/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(COORDINATE coord, int which) const +{ + assert(IsActive); + + return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); +} + + +/*********************************************************************************************** + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * * + * This routine scans around the cell specified and if there are any friendly buildings * + * nearby, the multiplier return value will be reduced. If there are no friendly buildings * + * nearby, then the return value will be 1. It checks to see if the primary weapon is * + * supposed to perform this scan and if so, the scan will be performed. Otherwise the * + * default value is quickly returned. * + * * + * INPUT: cell -- The cell where the potential target lies. An area around this cell will * + * be scanned for friendly buildings. * + * * + * OUTPUT: Returns with the multiplier to be multiplied by the potential target score value. * + * For less opportune targets, the multiplier fraction will be less than one. For * + * all other cases, it will return the default value of 1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Area_Modify(CELL cell) const +{ +// assert(Techno_Type_Class()->PrimaryWeapon != NULL); + if (Techno_Type_Class()->PrimaryWeapon == NULL || !Techno_Type_Class()->PrimaryWeapon->IsSupressed) return(1); + + int crange = Lepton_To_Cell(Rule.SupressRadius); + fixed odds = 1; + + for (int radius = 1; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + } + return(odds); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * * + * This routine is used to determine the score value (value as a potential target) of the * + * object specified. This routine will check the specified object for all the various * + * legality checks that threat scanning requires. This is the main workhorse routine for * + * target searching. * + * * + * INPUT: method -- The threat method requested. This is a combined bitflag value that * + * not only specifies the kind of targets to consider, but how far away * + * they are allowed to be. * + * * + * mask -- This is an RTTI mask to use for quickly eliminating object types. * + * The mask is created outside of this routine because this routine is * + * usually called from within a loop and this value is constant in that * + * context. * + * * + * range -- The range at which potential target objects are rejected. * + * 0 = must be within weapon range. * + * >0 = must be within this lepton distance. * + * <0 = range doesn't matter. * + * * + * object -- Pointer to the object itself. * + * * + * value -- Reference to the value variable that this routine will fill in. The * + * higher the value the more likely this object will be selected as best. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * + * value parameter will be filled in correctly. * + * * + * WARNINGS: This routine is time consuming. Don't call unless necessary. * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * + * 09/22/1995 JLB : Zone checking enabled. * + * 10/05/1995 JLB : Gives greater weight to designated enemy house targets. * + * 02/16/1996 JLB : Added additional threat checks. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone) const +{ + assert(IsActive); + assert(object != NULL); + + BStart(BENCH_EVAL_OBJECT); + + /* + ** An object in limbo can never be a valid target. + */ + if (object == NULL || object->IsInLimbo) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is cloaked, then it isn't a legal target. + */ + if (object->Cloak == CLOAKED) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is in a "harmless" state, then don't bother to consider it + ** a threat. + */ + if (MissionControl[object->Mission].IsNoThreat) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is not within the desired zone, then ignore it, but only if + ** zone checking is desired. + */ + COORDINATE objectcoord = object->Center_Coord(); + if (zone != -1 && Map[objectcoord].Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Friendly units are never considered a good target. Bail if this + ** object is a friend. Unless we're a medic, of course. But then, + ** only consider it a target if it's injured. + */ + if (House->Is_Ally(object)) { + if (Combat_Damage() < 0) { + if (object->Health_Ratio() == Rule.ConditionGreen) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is further away than allowed, bail. + */ + int dist = Distance(object); + if (range > 0 && dist > range) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + if (range == 0) { + int primary = What_Weapon_Should_I_Use(object->As_Target()); + if (!In_Range(object, primary)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is not visible, then bail. Human controlled units + ** are always considered to be visible. + */ + if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && Session.Type == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Quickly eliminate all unit types that are not allowed according to the mask + ** value. + */ + RTTIType otype = object->What_Am_I(); + if (!((1 << otype) & mask)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Mask failure. + } + + /* + ** Determine if the target is theoretically allowed to be a target. If + ** not, then bail. + */ + TechnoTypeClass const * tclass = object->Techno_Type_Class(); + if (!tclass->IsLegalTarget) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Legality failure. + } + + /* + ** Never consider a spy to be a valid target, unless you're a dog + */ + if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_SPY) { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsDog) { + // continue executing... + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** Special case so that SAM site doesn't fire on aircraft that are landed. + */ + if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { + if (((AircraftClass *)object)->Height == 0) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If only allowed to attack civilians, then eliminate all other types. + */ + if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the scan is limited to capturable buildings only, then bail if the examined + ** object isn't a capturable building. + */ + if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If we're a sub and the subject is a structure, bail if the structure + ** is other than a sub pen or shipyard. + */ + if (otype == RTTI_BUILDING && What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_SS) { + StructType ostruc = *(BuildingClass *)object; + if (ostruc != STRUCT_SUB_PEN && ostruc != STRUCT_SHIP_YARD) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** SPECIAL CASE: Friendly units won't automatically fire on buildings + ** if the building is not aggressive. That is, unless it is part of a team. A team + ** is allowed to pick any target it so chooses. + */ + if ((!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && + (House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && + otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#ifdef OBSOLETE + if ((!Is_Foot() || ((FootClass *)this)->Team.Is_Valid()) && House->IsHuman && otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#endif + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Player-controlled demo trucks never automatically target. + */ + if ((House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_DEMOTRUCK) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the search is restricted to Tiberium processing objects, then + ** perform the special qualification check now. + */ + if (method & THREAT_TIBERIUM) { + switch (otype) { + case RTTI_UNIT: + if (!((UnitTypeClass const *)tclass)->IsToHarvest) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + case RTTI_BUILDING: + if (!((BuildingTypeClass const *)tclass)->Capacity && Session.Type != GAME_NORMAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + default: + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If this target value is better than the previously recorded best + ** target value then record this target for possible return as the + ** best. + */ + int rawval = object->Value(); + value = rawval + object->Crew.Kills; + + /* + ** If the candidate object is owned by the designated enemy of this house, then + ** give it a higher value. This will tend to gravitate attacks toward the main + ** antagonist of this house. + */ + if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) { + value += 500; + value *= 3; + } + + /* + ** If the object is outside of the protective umbrella of the enemy base, then give it + ** a target boost value. + */ + if (object->House->Which_Zone(object) == ZONE_NONE) { + value *= 2; + } + + /* + ** If fake buildings are considered to be a greater target option, then boost + ** the fake building's value. + */ + if ((method & THREAT_FAKES) && otype == RTTI_BUILDING) { + //switch (!((BuildingTypeClass const *)tclass)->Type) { + //The '!' in front of the expression means we are switching on a bool instead of the type. Going to remove it so it compiles, but it might change behavior depending on what Watcom original made of this. + //Might be better to remove this switch altogether + //ST - 5/9/2019 + switch (((BuildingTypeClass const *)tclass)->Type) { + case STRUCT_FAKECONST: + case STRUCT_FAKEWEAP: + case STRUCT_FAKE_YARD: + case STRUCT_FAKE_PEN: + case STRUCT_FAKE_RADAR: + break; + + /* + ** Ignore all non-fake buildings. + */ + default: + value = 0; + break; + } + } + + /* + ** If power plants are to be considered a greater threat, then increase + ** their value here. Buildings that produce no power are not considered + ** a threat. + */ + if ((method & THREAT_POWER) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->Power > 0) { + value += ((BuildingTypeClass const *)tclass)->Power * 1000; + } else { + value = 0; + } + } + + /* + ** If factories are to be considered a greater threat, then don't + ** consider any non-factory building. + */ + if ((method & THREAT_FACTORIES) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->ToBuild == RTTI_NONE) { + value = 0; + } + } + + /* + ** If base defensive structures are to be considered a greater threat, then + ** don't consider an unarmed building to be a threat. + */ + if ((method & THREAT_BASE_DEFENSE) /*&& otype == RTTI_BUILDING*/) { + if (tclass->PrimaryWeapon == NULL) { + value = 0; + } + } + + /* + ** Possibly cause a reduction of the target's value if it is nearby friendly + ** structures and the primary weapon of this object is flagged for + ** friendly fire supression special check logic. + */ + fixed areamod = Area_Modify(Coord_Cell(object->Center_Coord())); + if (areamod != 1) { + value = areamod * value; + } + + /* + ** Adjust the target value upward if it is in the 'nervous zone' of the + ** owning base. This will tend to protect the base more thoroughly than + ** an unmodified scan would. + */ + if (House->Which_Zone(object) != ZONE_NONE) { + value *= Rule.NervousBias; + } + + /* + ** Lessen threat as a factor of distance. + */ + if (value) { +// if (rawval) { + + value = (value * 32000) / ((dist/ICON_LEPTON_W)+1); +// value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); + +// if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; + value = max(value, 1); + BEnd(BENCH_EVAL_OBJECT); + return(true); + } + value = 0; + BEnd(BENCH_EVAL_OBJECT); + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * * + * This routine will examine the specified cell and return with the potential target * + * object it contains and the value of it. Use this routine when searching for threats. * + * * + * INPUT: method -- The scan method to use for target searching. * + * * + * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * + * * + * range -- Scan range limit to use for elimination purposes. This ensures that * + * objects in the "corner" of a square scan get properly discarded. * + * * + * object -- Pointer to object pointer to be filled in with the object at this * + * cell as a valid target. * + * * + * value -- Reference to the value of the object in this cell. It will be set * + * according to the object's value. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Was a valid potential target found in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Zone checking enabled. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value, int zone) const +{ + assert(IsActive); + + BStart(BENCH_EVAL_CELL); + + *object = NULL; + value = 0; + + /* + ** If the cell is not on the legal map, then always ignore it. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + if (!Map.In_Radar(cell)) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + /* + ** Fetch the techno object from the cell. If there is no + ** techno object there, then bail. + */ + CellClass * cellptr = &Map[cell]; + + /* + ** Don't consider for evaluation a cell that is not within the same zone. Only + ** perform this check if zone checking is required. + */ + if (zone != -1 && cellptr->Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); + while (tentative != NULL) { + if (tentative != this) { + if (tentative->Is_Techno()) { + if (Combat_Damage() < 0) { + if (tentative->Health_Ratio() < Rule.ConditionGreen && House->Is_Ally(tentative)) break; + } else { + if (!House->Is_Ally(tentative)) break; + } + } + } + tentative = (TechnoClass const *)(ObjectClass *)tentative->Next; + } + + if (tentative == NULL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + *object = tentative; + + bool result = Evaluate_Object(method, mask, range, tentative, value); + + BEnd(BENCH_EVAL_CELL); + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * * + * This will examine the cell (as if it contained no sentient objects) and determine a * + * target value to assign to it. Typically, this is only useful for wall destroyable * + * weapons when dealing with enemy walls. * + * * + * INPUT: cell -- The cell to examine and evaluate. * + * * + * OUTPUT: Returns with the target value to assign to this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Evaluate_Just_Cell(CELL cell) const +{ + BStart(BENCH_EVAL_WALL); + + /* + ** Ships don't scan for walls. + */ + if (What_Am_I() == RTTI_VESSEL) { + return(0); + } + + /* + ** First, only computer objects are allowed to automatically scan for walls. + */ + if (House->IsHuman) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Even then, if the difficulty indicates that it shouldn't search for wall + ** targets, then don't allow it to do so. + */ + if (!Rule.Diff[House->Difficulty].IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Determine if, in fact, a wall is located at this cell location. + */ + CellClass const * cellptr = &Map[cell]; + if (cellptr->Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr->Overlay).IsWall) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** As a convenience to the target scanning logic, don't consider any wall to be + ** a target if it isn't in range of the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (!In_Range(Cell_Coord(cell), primary)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** See if the object has a weapon that can damage walls. + */ + TechnoTypeClass const * ttype = (TechnoTypeClass const *)Techno_Type_Class(); + if (ttype->PrimaryWeapon == NULL || ttype->PrimaryWeapon->WarheadPtr == NULL) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the weapon cannot deal with ground based targets, then don't consider + ** this a valid cell target. + */ + if (ttype->PrimaryWeapon->Bullet != NULL && !ttype->PrimaryWeapon->Bullet->IsAntiGround) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the primary weapon cannot destroy a wall, then don't give the cell any + ** value as a target. + */ + if (!ttype->PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If this is a friendly wall, then don't attack it. + */ + if (House->Is_Ally(cellptr->Owner)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Since a wall was found, then return a value adjusted according to the range the wall + ** is from the object. The greater the range, the lesser the value returned. + */ + BEnd(BENCH_EVAL_WALL); + return(Weapon_Range(0) - Distance(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * * + * This routine will scan game objects looking for the best target. It is used by the * + * general target searching processes. The type of target scan to perform is controlled * + * by the method control parameter. * + * * + * INPUT: method -- The method control parameter is used to control the type of target * + * scan performed. It consists of a series of bit flags (see ThreatType) * + * that are combined to form the target scan desired. * + * * + * OUTPUT: Returns the target value of a suitable target. If no target was found then the * + * value TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + * 06/20/1995 JLB : Greatly optimized scan method. * + * 09/22/1995 JLB : Takes into account the zone (if necessary). * + * 05/30/1996 JLB : Tighter elimination mask checking. * + *=============================================================================================*/ +TARGET TechnoClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + BStart(BENCH_GREATEST_THREAT); + + ObjectClass const * bestobject = NULL; + int bestval = -1; + int zone = -1; + + TargetScan++; + + /* + ** Determine the zone that the target must be in. For aircraft and gunboats, they + ** ignore zones since they either can fly over any zone or are designed to fire into + ** other zones. If scanning for targets that are within range, then zone checking need + ** not be performed -- range checking is much more thorough and effective. + */ + if (!(method & THREAT_RANGE) && + What_Am_I() != RTTI_VESSEL && + What_Am_I() != RTTI_BUILDING && + What_Am_I() != RTTI_AIRCRAFT) { + + zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + } + + /* + ** Hack for dogs, 'cause they can only consider infantrymen to be a + ** threat. Medics also. + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (((InfantryClass *)this)->Class->IsDog || Combat_Damage() < 0) { + method = THREAT_INFANTRY | (method & (THREAT_RANGE | THREAT_AREA)); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*(InfantryClass *)this == INFANTRY_MECHANIC) { + method = (THREAT_VEHICLES | THREAT_AIR) | (method & (THREAT_RANGE | THREAT_AREA)); + } +#endif + } + } + + /* + ** Build a quick elimination mask. If the RTTI of the object doesn't + ** qualify with this mask, then we KNOW that it shouldn't be considered. + */ + int mask = 0; + if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); + if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); + if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_BUILDINGS|THREAT_FACTORIES|THREAT_POWER|THREAT_FAKES|THREAT_BASE_DEFENSE|THREAT_TIBERIUM)) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_INFANTRY|THREAT_BASE_DEFENSE)) mask |= (1 << RTTI_INFANTRY); + if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); + if (method & THREAT_BASE_DEFENSE) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_BOATS) mask |= (1 << RTTI_VESSEL); + + /* + ** Limit area target scans use a method where the actual map cells are + ** examined for occupants. The occupant is then examined in turn. The + ** best target within the area is returned as a target. + */ + if (method & (THREAT_AREA|THREAT_RANGE)) { + int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); + + int crange = range / ICON_LEPTON_W; + if (range == 0) { + crange = max(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; + crange++; + } + CELL cell = Coord_Cell(Fire_Coord(0)); + + /* + ** BG: Miserable hack to get the stupid doctor to actually do area + ** guarding. + */ + if (Combat_Damage() < 0) { + /*if (method & THREAT_AREA)*/ crange++; + } + + /* + ** If aircraft are a legal target, then scan through all of them at this time. + ** Scanning by cell is not possible for aircraft since they are not recorded + ** at the cell level. + */ + if (method & THREAT_AIR) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (object->In_Which_Layer() != LAYER_GROUND && Evaluate_Object(method, mask, range, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + CELL bestcell = -1; + int bestcellvalue = 0; + TechnoClass const * object; + int value; +// int rad = 1; + + // BG: Medics need to be able to look in their own cell too. +// if (Combat_Damage() < 0 || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass*)this)->Class->IsDog)) { +// rad = 0; +// } + + for (int radius = 0; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Bail early if a target has already been found and the range is at + ** one of the breaking points (i.e., normal range or range * 2). + */ + if (bestobject != NULL) { + if (radius == crange/4) { + return(bestobject->As_Target()); + } + if (radius == crange/2) { + return(bestobject->As_Target()); + } + } + if (bestcell != -1) { + return(::As_Target(bestcell)); + } + } + + } else { + /* + ** A full map scan was requested. First scan through aircraft. The top map layer + ** is NOT scanned since that layer will probably contain more bullets and animations + ** than aircraft. + */ + if (mask & (1L << RTTI_AIRCRAFT)) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, -1, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Now scan through the entire ground layer. This is painful, but what other + ** choice is there? + */ + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; + + int value = 0; + if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value, zone)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + BEnd(BENCH_GREATEST_THREAT); + + /* + ** If a good target object was found, then return with the target value + ** of it. + */ + if (bestobject != NULL) { + return(bestobject->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Owner -- Who is the owner of this object? * + * * + * Use this routine to examine this object and return who the owner is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the house number of the owner of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +HousesType TechnoClass::Owner(void) const +{ + assert(IsActive); + + return(House->Class->House); +} + + +/*********************************************************************************************** + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * * + * Use this routine to set the flash count for the object. This flash count is the number * + * of times the object will "flash". Typically it is called as a result of the player * + * clicking on this object in order to make it the target of a move or attack. * + * * + * INPUT: count -- The number of times the object should flash. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Clicked_As_Target(HousesType house, int count) // 2019/09/20 JAS - Added record of who clicked on the object +{ + assert(IsActive); + + FlashCount = count; + + // 2019/09/20 JAS - Flashing info needs to exist per player + if (house < HOUSE_COUNT) + { + FlashCountPerPlayer[house] = count; + } + else + { + //receiving HOUSE_COUNT means do it for every player + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = count; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::AI -- Handles AI processing for techno object. * + * * + * This routine handles AI processing for techno objects. Typically, this merely dispatches * + * to the appropriate AI routines for the base classes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure that this routine is only called ONCE per game tick. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::AI(void) +{ + assert(IsActive); + + /* + ** Handle recoil recovery here. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If this building is being spied on by the player, need to redraw if selected + ** since the money amount is rendering. + */ + if (Is_Selected_By_Player()) { + if (What_Am_I() == RTTI_BUILDING) { + int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); + if (spiedby) { + if (((BuildingClass *)this)->Class->Capacity) { + Mark(MARK_CHANGE_REDRAW); + } + } + } + } + + CargoClass::AI(); + RadioClass::AI(); + + if (!IsActive || (Height > 0 && What_Am_I() != RTTI_AIRCRAFT)) return; + + DoorClass::AI(); + + /* + ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform + ** the heal logic here. + */ + if (Techno_Type_Class()->IsSelfHealing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0 && Health_Ratio() <= Rule.ConditionYellow) { + Strength++; + Mark(MARK_CHANGE); + } + + /* + ** Cloaking device processing. + */ + Cloaking_AI(); + + /* + ** If for some strange reason, the computer is firing upon itself, then + ** tell it not to. + */ + if (!House->IsHuman && As_Techno(TarCom) && As_Techno(TarCom)->House->Is_Ally(this)) { +//#ifdef FIXIT_CSII // checked - ajw 9/28/98 (commented out) +//if(What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this==INFANTRY_GENERAL && Session.Type==GAME_NORMAL && House->Class->House==HOUSE_UKRAINE) { +//} else +//#endif + Assign_Target(TARGET_NONE); + } + + /* + ** Perform a maintenance check to see that if somehow this object is trying to fire + ** upon an object it can never hit (because it can't reach it), then abort the tarcom + */ + if (What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && (!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && (!Is_Foot() || !Is_In_Same_Zone(As_Cell(TarCom)))) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + + /* + ** Update the animation timer system. If the animation stage + ** changes, then flag the object to be redrawn as well as determine + ** if the current animation process needs to change. + */ + if (What_Am_I() != RTTI_BUILDING) { + if (StageClass::About_To_Change()) { + Mark(MARK_CHANGE_REDRAW); + } + if (StageClass::Graphic_Logic() || Time_To_Redraw()) { + Mark(MARK_CHANGE_REDRAW); + } + } + + /* + ** If the object is flashing and a change of flash state has occurred, then mark the + ** object to be redrawn. + */ + if (FlasherClass::Process()) { + Mark(MARK_CHANGE); + } + + /* + ** Handle electric zap delay logic. + */ + if (ElectricZapDelay >= 0) { + Map.Flag_To_Redraw(true); + if (--ElectricZapDelay < 0) { + ElectricZapTarget = 0; + ElectricZapWhich = 0; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * * + * This routine handles the cloaking device logic for this object. It will handle the * + * transition effects as the object cloaks or decloaks. It will also try to start an * + * object to cloak if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Cloaking_AI(void) +{ + /* + ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. + */ + if (IsCloakable) { + + /* + ** If this object is uncloaked, but it can be cloaked and it thinks that it + ** is a good time do so, then begin cloaking. + */ + if (Cloak == UNCLOAKED) { +#ifdef PREDATOR + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + //if (IsOwnedByPlayer) Mark(MARK_CHANGE); + if (Is_Owned_By_Player()) Mark(MARK_CHANGE); +#endif + CloakingDevice.Graphic_Logic(); + if (Is_Ready_To_Cloak()) { + if (Health_Ratio() > Rule.ConditionRed) { + Do_Cloak(); + } else { + if (Percent_Chance(4)) { + Do_Cloak(); + } + } + } + } else { + + CloakingDevice.Graphic_Logic(); + switch (Cloak) { + + /* + ** Handle the uncloaking process. Always mark to redraw + ** the object and when cloaking is complete, stabilize into + ** the normal uncloaked state. + */ + case UNCLOAKING: + Mark(MARK_CHANGE); + if (Visual_Character(true) == VISUAL_NORMAL) { + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); // re-start the stage counter + Cloak = UNCLOAKED; + CloakDelay = Rule.CloakDelay * TICKS_PER_MINUTE; + Mark(MARK_CHANGE); + } + break; + + /* + ** Handle the cloaking process. Always mark to redraw the object + ** and when the cloaking process is complete, stabilize into the + ** normal cloaked state. + */ + case CLOAKING: + Mark(MARK_CHANGE); + if(!CloakingDevice.Fetch_Rate()) { + CloakingDevice.Set_Rate(1); + } + switch (Visual_Character(true)) { + + /* + ** If badly damaged, then it can never fully cloak. + */ + case VISUAL_DARKEN: + if (Health_Ratio() <= Rule.ConditionRed && Percent_Chance(25)) { + Cloak = UNCLOAKING; + } + break; + + case VISUAL_HIDDEN: + Cloak = CLOAKED; + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); + Mark(MARK_CHANGE); + + Map[Center_Coord()].Redraw_Objects(true); + Map.RadarClass::Flag_To_Redraw(true); + + /* + ** Special check to ensure that if the unit is carrying a captured + ** flag, it will never fully cloak. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + Do_Shimmer(); + } else { + Detach_All(false); + } + + /* + ** A computer controlled unit will try to scatter if possible so + ** that it will be much harder to locate. + */ + if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { + Scatter(0, true); + } + break; + } + break; + + /* + ** A cloaked object will always be redrawn if it is owned by the + ** player. This ensures that the shimmering effect will animate. + */ + case CLOAKED: +#ifdef PREDATOR + //if (IsOwnedByPlayer) { + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + if (Is_Owned_By_Player()) { + Mark(MARK_CHANGE); + } +#endif + break; + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * * + * This routine will examine this object and determine if it can and is ready and able * + * to begin cloaking. It will also check to make sure it appears to be a good time to cloak * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this unit ready and able to start cloaking? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Cloak(void) const +{ + /* + ** If it is already cloaked or in the process of cloaking, then it can't start cloaking. + */ + if (Cloak == CLOAKED || (Cloak == CLOAKING && CloakingDevice.Fetch_Rate())) { + return(false); + } + + /* + ** If the object cannot recloak, then it certainly is not allowed to start. + */ + if (!IsCloakable || !Is_Allowed_To_Recloak()) { + return(false); + } + + /* + ** If the object is currently rearming, then don't begin to recloak. + */ + if (Arm != 0) { + return(false); + } + + /* + ** If it seems like this object is about to fire on a target, then don't begin + ** cloaking either. + */ + if (Target_Legal(TarCom) && In_Range(TarCom)) { + return(false); + } + + /* + ** Recloaking can only begin if the cloaking device is not already operating. + */ + if (CloakingDevice.Fetch_Stage() != 0) { + return(false); + } + + /* + ** If the arbitrary cloak delay value is still counting down, then don't + ** allow recloaking just yet. + */ + if (CloakDelay != 0) { + return(false); + } + + /* + ** All tests passed, so this object is allowed to begin cloaking. + */ + return(true); +} + + + +/*********************************************************************************************** + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * * + * This function checks to see if this techno object can be selected. If it can, then it * + * is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Select(bool allow_mixed) +{ + assert(IsActive); + + //if (!IsDiscoveredByPlayer && !House->IsPlayerControl && !Debug_Unshroud) { // ST - 8/7/2019 11:24AM + if (!Is_Discovered_By_Player() && !House->IsPlayerControl && !Debug_Unshroud) { + return(false); + } + + if (RadioClass::Select(allow_mixed)) { + + /* + ** Speak a confirmation of selection. + */ + if (House->IsPlayerControl && AllowVoice) { + Response_Select(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * * + * This performs a simple check to make sure that this techno object can fire. At this * + * level, the only thing checked for is the rearming delay. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the fire legality control code. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const +{ + assert(IsActive); + + /* + ** Don't allow firing if the target is illegal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + ObjectClass * object = As_Object(target); + + /* + ** If the object is completely cloaked, then you can't fire on it. + */ + if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) { + return(FIRE_CANT); + } + + /* + ** A falling object is too busy falling to fire. + */ + if (IsFalling) { + return(FIRE_CANT); + } + + /* + ** If there is no weapon, then firing is not allowed. + */ + WeaponTypeClass const * weapon = ((which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon); + if (weapon == NULL) { + return(FIRE_CANT); + } + + /* + ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is + ** sitting on the ground. + */ + if (object != NULL && object->What_Am_I() == RTTI_AIRCRAFT && + !weapon->Bullet->IsAntiAircraft && + ((AircraftClass *)object)->Height > 0) { + + return(FIRE_CANT); + } + + /* + ** If the object is on the ground, then don't allow firing if it can't fire upon ground objects. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || (*((VesselClass*)object) != VESSEL_SS && *((VesselClass*)object) != VESSEL_MISSILESUB )) && +#else + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || *((VesselClass*)object) != VESSEL_SS) && +#endif + !weapon->Bullet->IsAntiGround) { + + return(FIRE_CANT); + } + if (Is_Target_Cell(target) && !weapon->Bullet->IsAntiGround) { + return(FIRE_CANT); + } + + /* + ** Don't allow firing if still rearming. + */ + if (Arm != 0) return(FIRE_REARM); + + /* + ** The target must be within range in order to allow firing. + */ + if (!In_Range(target, which)) { + return(FIRE_RANGE); + } + + /* + ** If there is no ammo left, then it can't fire. + */ + if (!Ammo) { + return(FIRE_AMMO); + } + + /* + ** If cloaked, then firing is disabled. + */ + if (Cloak != UNCLOAKED) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Special hack for John Archer's Hunt-The-Wumpus multiplayer mission... if +// the object firing is a cloaked civilian, don't require uncloaking before +// allowing firing. + if (What_Am_I()==RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCivilian ) { + return(FIRE_OK); + } +#endif + return(FIRE_CLOAKED); + } + + return(FIRE_OK); +} + + +/*********************************************************************************************** + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * * + * This routine handles cleaning up this techno object from the game system so that when * + * it is subsequently removed, it doesn't leave any loose ends. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Stun(void) +{ + assert(IsActive); + + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + Transmit_Message(RADIO_OVER_OUT); + Detach_All(); + //Unselect(); + //When an object is stunned it needs to be deselected from all players, not just the current PlayerPtr. + // - 8/18/2019 JAS + Unselect_All_Players(); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * * + * Use this routine to set the targeting computer for this object. It checks to make sure * + * that targeting of itself is prohibited. * + * * + * INPUT: target -- The target for this object to attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Target(TARGET target) +{ + assert(IsActive); + + if (target == TarCom) return; + + if (!Target_Legal(target)) { + target = TARGET_NONE; + } else { + + /* + ** Prevent targeting of self. + */ + if (target == As_Target()) { + target = ::As_Target(Coord_Cell(Coord)); + } else { + + /* + ** Make sure that the target is not already dead. + */ + ObjectClass * object = As_Object(target); + if (object != NULL && (object->IsActive == false || object->Strength == 0)) { + target = TARGET_NONE; + } + } + } + + /* + ** Set the unit's targeting computer. + */ + TarCom = target; +} + + +/*********************************************************************************************** + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * * + * This function calculates the delay between shots. It determines this from the standard * + * rate of fire (ROF) of the base class and modifies it according to game speed and * + * whether this is the first or second shot. All single shot attackers consider their * + * shots to be "second" since the second shot is the one handled normally. The first shot * + * usually gets assigned a much shorter delay time before the next shot can fire. * + * * + * INPUT: second -- bool; Is this the second of a two shot salvo? * + * * + * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Rearm_Delay(bool second, int which) const +{ + assert(IsActive); + + if (What_Am_I() == RTTI_BUILDING && Ammo > 1) { + return(1); + } + + WeaponTypeClass const * weapon = (which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; + if (second && weapon != NULL) { + return(weapon->ROF * House->ROFBias); + } + return(3); +} + + +/*********************************************************************************************** + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * * + * This routine is used to fire an electric zap at the target specified. * + * * + * INPUT: target -- The target to fire the zap at. * + * * + * which -- Which weapon is this zap associated with (0=primary, 1=secondary). * + * * + * window -- The clipping window to use when rendering. * + * * + * source_coord -- The coordinate that the zap is to originate from. This is an * + * override value and if not specifide, the normal fire coordinate * + * is used. * + * * + * remap -- Pointer to the zap animation remap override table. If not specified * + * then the zap remains the normal blue white color. * + * * + * OUTPUT: bool; Does this object need to redraw? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 BWG : Created. * + * 09/30/1996 JLB : Uses standard facing conversion and distance routines. * + *=============================================================================================*/ +bool TechnoClass::Electric_Zap(COORDINATE target_coord, int which, WindowNumberType window, COORDINATE source_coord, unsigned char * remap) const +{ + //int x,y,x1,y1; + //PG init variables + int x = 0; + int y = 0; + int x1 = 0; + int y1 = 0; + COORDINATE source; + + if (source_coord != 0) { + source = source_coord; + } else { + source = Fire_Coord(which); + } + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharging = false; + } + bool gonnadraw = false; + + if (SpecialDialog == SDLG_NONE) { + Map.Coord_To_Pixel(source, x, y); + Map.Coord_To_Pixel(target_coord, x1, y1); + x += Map.TacPixelX; + x1 += Map.TacPixelX; + y += Map.TacPixelY; + y1 += Map.TacPixelY; + gonnadraw = true; + } + + static int _shape[]={ 2, 3, 1, 0, 2, 3, 1, 0}; + static int _xadd[8][8]={ + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8} + }; + static int _yadd[8][8]={ + {-8,-8,-8, 0, 0, 0,-8,-8}, + {-8,-8,-8, 0, 0, 0,-8,-8}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + {-8,-8,-8, 0, 0, 0,-8,-8} + }; + + int savex = x, savey = y; + if (gonnadraw) { + for (int shots = 0; shots < 3; shots++) { + x = savex; + y = savey; + int lastfacing = 0; + while (::Distance(x, y, x1, y1) > 8) { + + /* + ** Determine true (0..7) facing from current position to + ** destination (actually the source coordinate of the zap). + */ + int facing = Dir_Facing(Desired_Facing8(x, y, x1, y1)); + + /* + ** If there's quite a bit of distance to go, + ** we may vary the desired facing to give the + ** bolt some randomness. + */ + if (::Distance(x, y, x1, y1) > 40) { + switch (Sim_Random_Pick(1, 3 + ((shots==0) ? 3 : 0))) { + case 1: + facing++; + break; + + case 2: + facing--; + break; + + default: + break; + } + facing &= 7; + } + + /* + ** Now that we have the direction of the bolt, + ** draw it and move the x & y coords in the right + ** direction for the next piece. + */ + // Electric zap coordinates are always tactical, so don't use the partial window if passed - SKY + x += _xadd[facing][lastfacing]; + y += _yadd[facing][lastfacing]; + if (remap != NULL) { + CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap); + } else { + CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL); + } + lastfacing = facing; + } + } + } + + return (gonnadraw); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * * + * This is the main projectile firing code. Buildings, units, and infantry route fire * + * requests through this function. * + * * + * INPUT: target -- The target that the projectile is to be fired at. * + * * + * which -- Which weapon to fire. * + * * + * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * + * could be created or there was some other illegality detected, the return value * + * will be NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * + * 02/22/1996 JLB : Handles camera "weapon" case. * + *=============================================================================================*/ +BulletClass * TechnoClass::Fire_At(TARGET target, int which) +{ + assert(IsActive); + + BulletClass * bullet; // Projectile. + DirType dir; // The facing to impart upon the projectile. + COORDINATE target_coord; // Coordinate of the target. + COORDINATE fire_coord; // Coordinate of firing position. + TechnoTypeClass const & tclass = *Techno_Type_Class(); + ObjectClass * object; + WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; + + /* + ** If this object doesn't have a weapon, then it is obvious that firing + ** cannot ever succeed. Return with failure flag. + */ + if (weapon == NULL) return(NULL); + + BulletTypeClass const & btype = *weapon->Bullet; + + /* + ** Perform a quick legality check to see if firing can occur. + */ + if (Debug_Map || !Target_Legal(target)) { + return(NULL); + } + + /* + ** Fetch the target coordinate for the target specified. + */ + object = As_Object(target); + if (object != NULL) { + target_coord = object->Target_Coord(); + } else { + target_coord = As_Coord(target); + } + + /* + ** Get the location where the projectile should appear. + */ + fire_coord = Fire_Coord(which); + + /* + ** If the projectile is a homing type (such as a missile), then it will + ** launch in the direction the turret is facing, NOT necessarily the same + ** direction as the target. + */ + if (btype.ROT != 0 || btype.IsDropping) { + dir = Fire_Direction(); + if (btype.IsDropping) { + fire_coord = Center_Coord(); + } + } else { + dir = ::Direction(fire_coord, target_coord); + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + int firepower = weapon->Attack; + if (firepower > 0) { + firepower = weapon->Attack * FirepowerBias * House->FirepowerBias; + } + + + /* + ** Give the bullet a boost of speed if the weapon indicates that this is required. Only + ** need to perform this check if the target is an aircraft. + */ + int firespeed = weapon->MaxSpeed; + if (weapon->IsTurboBoosted && Is_Target_Aircraft(target)) { + firespeed *= Rule.TurboBoost; + } + + bullet = new BulletClass(weapon->Bullet->Type, target, this, firepower, WarheadType(weapon->WarheadPtr->ID), firespeed); + + if (bullet != NULL) { + + /* + ** If this is firing from a moving platform, then the projectile is inaccurate. + */ + if (Is_Foot() && ((FootClass const *)this)->IsDriving) { + bullet->IsInaccurate = true; + } + + if (bullet->Unlimbo(fire_coord, dir)) { + } else { + delete bullet; + } + if (tclass.IsTurretEquipped) { + IsInRecoilState = true; + Mark(MARK_CHANGE_REDRAW); + } + + Arm = Rearm_Delay(IsSecondShot, which); + if (tclass.Is_Two_Shooter()) { + IsSecondShot = (IsSecondShot == false); + } + + /* + ** Perform any animation effect for this weapon. + */ + AnimType a = weapon->Anim; + switch (a) { + case ANIM_GUN_N: + a = AnimType(a + Dir_Facing(Fire_Direction())); + break; + + case ANIM_SAM_N: + a = AnimType(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())); + break; + } + + /* + ** Play any sound effect tied to this weapon type. + */ + Sound_Effect(weapon->Sound, Fire_Coord(which)); + + /* + ** If there is a special firing animation, then create and attach it + ** now. + */ + if (a != ANIM_NONE) { + AnimClass * anim = new AnimClass(a, Fire_Coord(which)); + if (anim != NULL) { + anim->Attach_To(this); + } + } + + /* + ** Electric zap animation. + */ + if (weapon->IsElectric) { + ElectricZapDelay = 3; + ElectricZapTarget = target_coord; + ElectricZapWhich = which; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() != RTTI_INFANTRY) { + Set_Stage(0); + Set_Rate(0); + } +#else + Set_Stage(0); + Set_Rate(0); +#endif + if (Ammo <= 1 && What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharged = false; + } + + TechnoClass * tech = As_Techno(target); + if (tech != NULL) { + tech->Clicked_As_Target(PlayerPtr->Class->House, 4); // 2019/09/20 JAS - Added record of who clicked on the object + } + } + + /* + ** Reduce ammunition for this object. + */ + if (Ammo > 0) { + Ammo--; + } + + /* + ** Firing will in all likelihood, require the unit to be redrawn. Flag it to be + ** redrawn here. + */ + Mark(MARK_CHANGE); + + /* + ** If a projectile was fired from a unit that is hidden in the darkness, + ** reveal that unit and a little area around it. + ** For multiplayer games, only reveal the unit if the target is the + ** local player. + */ +#if (0) + if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || (!Map[Center_Coord()].IsMapped && (What_Am_I()!=RTTI_AIRCRAFT || !IsOwnedByPlayer)) ) { + if (Session.Type == GAME_NORMAL) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } else { + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + HousesType tgt_owner = obj->Owner(); + + if (PlayerPtr->Class->House == tgt_owner) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } + } + } + } + +#else + + /* + ** For client/server multiplayer, we need to reveal for any human player that is the target. ST - 3/13/2019 5:43PM + */ + ObjectClass *obj = As_Object(target); + if (obj) { + HousesType tgt_owner = obj->Owner(); + + HouseClass *player = HouseClass::As_Pointer(tgt_owner); + if ((player != NULL) && player->IsHuman) { + if ((!Is_Owned_By_Player(player) && !Is_Discovered_By_Player(player)) || (!Map[Coord_Cell(Center_Coord())].Is_Mapped(House) && (What_Am_I()!=RTTI_AIRCRAFT || !Is_Owned_By_Player(player))) ) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, player, false); + } + } + } + +#endif + } + + return(bullet); +} + + +/*********************************************************************************************** + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * * + * This routine is called when the mission for an object needs to change as a result of * + * player input. The basic operation would be to queue the event and let the action * + * occur at the frame dictated by the queuing system. However, if a voice response is * + * indicated, then perform it at this time. This will give a greater illusion of * + * immediate response. * + * * + * INPUT: mission -- The mission order to assign to this object. * + * * + * target -- The target of this object. This will be used for combat and attack. * + * * + * destination -- The movement destination for this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + + if (FormMove) { + SpeedType speed = FormSpeed; + MPHType maxspeed = FormMaxSpeed; + if (Is_Foot()) { + const FootClass* foot = (const FootClass*)this; + if (foot->Group < MAX_TEAMS) { + speed = TeamSpeed[foot->Group]; + maxspeed = TeamMaxSpeed[foot->Group]; + } + } + Queue_Mission(TargetClass(this), mission, target, destination, speed, maxspeed); + } else { + + /* + ** Cooerce the movement mission into a queued movement mission if the ALT key was + ** held down. + */ + // + // MBL 04.14.2020 Original code KO, since is still active and can still hit + // + // if (mission == MISSION_MOVE && (Keyboard->Down(Options.KeyQueueMove1) || Keyboard->Down(Options.KeyQueueMove2))) { + // mission = MISSION_QMOVE; + // } + + // + // MBL 04.14.2020 - Apply the same logic as above, using what is assigned as hotkeys + // + if (PlayerPtr && House) { + if (PlayerPtr == House) { + if (mission == MISSION_MOVE && PlayerPtr->IsQueuedMovementToggle) { + mission = MISSION_QMOVE; + } + } + } + + Queue_Mission(TargetClass(this), mission, target, destination); + } +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * * + * This routine will examine the object specified and return with the action that will * + * be performed if the mouse button were clicked over the object. * + * * + * INPUT: object -- The object that the mouse button might be clicked on. * + * * + * OUTPUT: Returns with the action that will be performed if the object was clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 03/21/1995 JLB : Special target control for trees. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(ObjectClass const * object) const +{ + assert(IsActive); + + if (object != NULL) { + + /* + ** Return the ACTION_SELF flag if clicking on itself. However, if this + ** object cannot do anything special with itself, then just return with + ** the no action flag. + */ + if (object == this && CurrentObject.Count() == 1 && House->IsPlayerControl) { + return(ACTION_SELF); + } + + //bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + //bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); + //bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); + //Added for getting the input for special character keys from the client + // - 6/26/2019 JAS + bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); + bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() /*KO && Can_Player_Fire()*/) { +// if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (altdown) { + if (House->IsPlayerControl && Can_Player_Move()) { + return(ACTION_MOVE); + } + } + + /* + ** Override so that toggled select state can be performed while the key + ** is held down. + */ + bool is_a_loaner = object->Is_Techno() && ((TechnoClass*)object)->IsALoaner; + if (shiftdown) { + if (!is_a_loaner) { + return(ACTION_TOGGLE_SELECT); + } + } + + /* + ** If the weapon is blatantly disallowed from firing on the object specified, then + ** don't allow the attach check logic to proceed. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (Is_Weapon_Equipped() && + ttype->PrimaryWeapon->Bullet != NULL && + ttype->PrimaryWeapon->Bullet->IsSubSurface && + Map[object->Target_Coord()].Land_Type() != LAND_WATER) { + + // Do nothing. + + } else { + + /* + ** If firing is possible and legal, then return this action potential. + */ + if (House->IsPlayerControl && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Rule.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + + if (Is_Weapon_Equipped() || + (What_Am_I() == RTTI_INFANTRY && + (((InfantryTypeClass const *)ttype)->IsBomber || + ((InfantryTypeClass const *)ttype)->IsCapture) + )) { + // Check for anti-air capability + int primary = What_Weapon_Should_I_Use(object->As_Target()); + WeaponTypeClass const * weapon = (primary == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; + if ((object->What_Am_I() != RTTI_AIRCRAFT) || + ((weapon != NULL) && weapon->Bullet->IsAntiAircraft) || + (object->Is_Techno() && (((TechnoClass *)object)->Height == 0))) { + if (Can_Player_Move() || In_Range(object, primary)) { + if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable)) { + return(ACTION_ATTACK); + } else { + if (!Can_Player_Move()) { + return(ACTION_NONE); + } else { + return(ACTION_ATTACK); + } + } + } + } + } + } + } + + /* + ** Possibly try to select the specified object, if that is warranted. + */ + if (!Is_Weapon_Equipped() || !House->IsPlayerControl || object->Owner() == Owner()) { + if ((!is_a_loaner || !Is_Owned_By_Player()) && object->Class_Of().IsSelectable && (!object->Is_Selected_By_Player() || CurrentObject.Count())) { + return(ACTION_SELECT); + } + return(ACTION_NONE); + } + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * * + * Use this routine to determine what action will be performed if the specified cell * + * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * + * nomove is used to perform special case checking for nearby cells if in fact the mouse * + * is clicked over the cell. * + * * + * INPUT: cell -- The cell to check for being clicked over. * + * * + * OUTPUT: Returns with the action that will occur if the cell is clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 07/10/1995 JLB : Force fire for buildings is explicitly disabled. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(CELL cell) const +{ + assert(IsActive); + + CellClass const * cellptr = &Map[cell]; + OverlayTypeClass const * optr = NULL; + + //bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + //bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); + //bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); + //Added for getting the input for special character keys from the client + // - 6/26/2019 JAS + bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); + bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + /* + ** Disable recognizing the key forced fire option when dealing with buildings. + */ + if (What_Am_I() == RTTI_BUILDING) ctrldown = false; + + /* + ** Disable recognizing the key forced fire option when dealing with submarines. + */ + if(What_Am_I() == RTTI_VESSEL) { + WeaponTypeClass const * weapon = ((VesselClass *)this)->Class->PrimaryWeapon; + if (weapon && weapon->Bullet->IsSubSurface) ctrldown = false; + } + + + if (cellptr->Overlay != OVERLAY_NONE) { + optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + } + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + if (House->IsPlayerControl && Techno_Type_Class()->PrimaryWeapon != NULL && (ctrldown || (optr && optr->IsLegalTarget))) { + WarheadTypeClass const * whead = Techno_Type_Class()->PrimaryWeapon->WarheadPtr; + +// To be fixed for firing on ore by accounting for ore and ignoring the overlay in that case. + + if (optr == NULL || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (Can_Player_Move() || In_Range(::As_Target(cell), primary)) { + return(ACTION_ATTACK); + } + } + } + + if (House->IsPlayerControl && Can_Player_Move()) { + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (shiftdown) { + return(ACTION_MOVE); + } + + /* + ** If the object can enter the cell specified, then allow + ** movement to it. + */ + if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { + return(ACTION_MOVE); + } + return(ACTION_NOMOVE); + } + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * * + * Use this routine to determine whether a movement order can be given to this object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given a movement order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Move(void) const +{ + assert(IsActive); + + return(House->IsPlayerControl); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * + * * + * Call this routine to determine if this object can be given a fire order by the player. * + * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * + * cursor to appear. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given firing orders by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Fire(void) const +{ + assert(IsActive); + + if (House->IsPlayerControl && Is_Techno() && Techno_Type_Class()->PrimaryWeapon != NULL) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * * + * Use this routine to determine if this object is equipped with a combat weapon. Such * + * determination is used by the AI system to gauge the threat potential of the object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object equipped with a combat weapon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Weapon_Equipped(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->PrimaryWeapon != NULL); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * * + * Use this routine to determine if the specified object is a candidate for repair. In * + * order to qualify, the object must be allowed to be repaired (in theory) and it must * + * be below full strength. If these conditions are met, then it can be repaired. * + * * + * INPUT: none * + * * + * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * + * is not allowed to be repaired, or it might be full strength already. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Repair(void) const +{ + assert(IsActive); + + /* + ** Temporary hack to disable repair cursor over non-buildings. + */ + if (What_Am_I() != RTTI_BUILDING) { + return(false); + } + return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); +} + + +/*********************************************************************************************** + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * * + * Use this routine to determine the maximum range for the weapon indicated. * + * * + * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the range of the weapon (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Weapon_Range(int which) const +{ + assert(IsActive); + assert((unsigned)which < 2); + + WeaponTypeClass const * weapon = NULL; + TechnoTypeClass const & ttype = *Techno_Type_Class(); + + switch (which) { + case 0: + weapon = ttype.PrimaryWeapon; + break; + + case 1: + weapon = ttype.SecondaryWeapon; + break; + } + if (weapon != NULL) { + return(weapon->Range); + } + return(0); +} + + +/*************************************************************************** + * TechnoClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override* + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedTarCom = TarCom; + RadioClass::Override_Mission(mission, tarcom, navcom); + Assign_Target(tarcom); +} + + +/*************************************************************************** + * TechnoClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool TechnoClass::Restore_Mission(void) +{ + assert(IsActive); + + if (RadioClass::Restore_Mission()) { + Assign_Target(SuspendedTarCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Renovate -- Heal a building to maximum * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1996 BWG : Created. * + *=============================================================================================*/ +void TechnoClass::Renovate(void) +{ + assert(IsActive); + + Mark(MARK_CHANGE); + Strength = Techno_Type_Class()->MaxStrength; + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->Repair(0); + } +} + + +/*********************************************************************************************** + * TechnoClass::Captured -- Handles capturing this object. * + * * + * This routine is called when this object gets captured by the house specified. It handles * + * removing this object from any targeting computers and then changes the ownership of * + * the object to the new house. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the object captured? Failure would mean that it is already under control of * + * the house specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 09/29/1995 JLB : Keeps track of quantity records. * + *=============================================================================================*/ +bool TechnoClass::Captured(HouseClass * newowner) +{ + assert(IsActive); + + if (newowner != House) { + + /* + ** Capture attempt springs any "entered" trigger. The entered trigger + ** occurs first since there may be a special trigger attached to this + ** object that flags a capture as a win and a destroy as a loss. This + ** order is necessary because the object is recorded as a kill as well. + */ + if (Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + /* + ** Record this as a kill. + */ + Record_The_Kill(NULL); + + /* + ** Special kill record logic for capture process. + */ + House->Tracking_Remove(this); + newowner->Tracking_Add(this); + switch (What_Am_I()) { + case RTTI_BUILDING: + newowner->BuildingsKilled[Owner()]++; + break; + + case RTTI_AIRCRAFT: + case RTTI_INFANTRY: + case RTTI_UNIT: + case RTTI_VESSEL: + newowner->UnitsKilled[Owner()]++; + break; + + default: + break; + } + House->WhoLastHurtMe = newowner->Class->House; + + /* + ** Remove from targeting computers. + */ + Detach_All(false); + + /* + ** Change ownership now. + */ + House = newowner; + IsOwnedByPlayer = (House == PlayerPtr); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * * + * This routine is called when this object has taken damage. It handles recording whether * + * this object has been destroyed. If it has, then mark the appropriate kill records as * + * necessary. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(IsActive); + + ResultType result = RESULT_NONE; + + /* + ** If not a forced damage condition, adjust damage according to house override armor + ** value. + */ + if (!forced && damage > 0) { + damage = damage * ArmorBias * House->ArmorBias; + } + + if (IronCurtainCountDown == 0) { + result = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + } + + switch (result) { + case RESULT_DESTROYED: + Transmit_Message(RADIO_OVER_OUT); + Stun(); + + /* + ** If this object explodes with violent damage, then perform the explosion + ** now and use the warhead type and full strength as the explosion values. + */ + if (Techno_Type_Class()->IsExploding) { + + /* + ** The warhead to use is based on the weapon this object is equipped with. + */ + WarheadType wh = WARHEAD_HE; + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + wh = WarheadType(Techno_Type_Class()->PrimaryWeapon->WarheadPtr->ID); + } + + int damage = Techno_Type_Class()->MaxStrength; + new AnimClass(Combat_Anim(damage, wh, Map[Center_Coord()].Land_Type()), Center_Coord()); + int radius = damage * Rule.ExplosionSpread; +// int radius = damage/2; + Wide_Area_Damage(Center_Coord(), radius, damage, source, wh); + } + + if (this == (TechnoClass *)::As_Object(House->UnitToTeleport)) { + House->UnitToTeleport = 0; + if (!Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + if (Map.IsTargettingMode == SPC_CHRONO2) { + KeyNumType input = KN_RMOUSE; + Map.AI(input, 0, 0); + } + } + + /* + ** May trigger an achievement. ST - 11/14/2019 1:56PM + */ + if (Session.Type == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + + RTTIType what = What_Am_I(); + if (what == RTTI_AIRCRAFT || what == RTTI_INFANTRY || what == RTTI_UNIT || what == RTTI_VESSEL) { + On_Achievement_Event(source->House, "UNIT_DESTROYED", object_type->IniName); + } + } + } + break; + + /* + ** If some damage was received and this object is cloaked, shimmer + ** the cloak a bit. + */ + default: + if (source != NULL && !House->Is_Ally(source)) { + IsTickedOff = true; + } + Do_Shimmer(); + break; + + case RESULT_NONE: + break; + } + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * * + * This routine is used to record the death of this object. It will handle updating the * + * owner house with the kill record as well as springing any trigger events associated with * + * this object's death. * + * * + * INPUT: source -- Pointer to the source of this object's death (if there is a source). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 08/23/1995 JLB : Building loss is only counted if it received damage. * + *=============================================================================================*/ +void TechnoClass::Record_The_Kill(TechnoClass * source) +{ + assert(IsActive); + + int total_recorded = 0; + + int points = Techno_Type_Class()->Points; + + /* + ** Handle any trigger event associated with this object. + */ + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_ATTACKED, this); + + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_DISCOVERED, this); + + if (Trigger.Is_Valid()) Trigger->Spring(TEVENT_DESTROYED, this); + + if (source != NULL) { + Crew.Made_A_Kill(); + + House->WhoLastHurtMe = source->Owner(); + + /* + ** Add up the score for killing this unit + */ + source->House->PointTotal += points; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack check: if they were trying to teleport this unit when it died, take +// the map mode out of teleportation mode. + if(IsOwnedByPlayer && Map.IsTargettingMode == SPC_CHRONO2 && House->UnitToTeleport == As_Target()) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + switch (What_Am_I()) { + case RTTI_BUILDING: + { + StructType bldg = *(BuildingClass *)this; + if (bldg != STRUCT_BARREL && bldg != STRUCT_BARREL3 && + bldg != STRUCT_APMINE && bldg != STRUCT_AVMINE) { + if (((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { + House->BuildingsLost++; + } + + if (source != NULL) { + if (Session.Type == GAME_INTERNET) { + source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); + } + source->House->BuildingsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + } + } + break; + + case RTTI_AIRCRAFT: + if (source != NULL && Session.Type == GAME_INTERNET) { + source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_INFANTRY: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_UNIT: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_VESSEL: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((VesselClass*)this)->Class->Type ); + } + + House->UnitsLost++; + if (source != NULL) source->House->UnitsKilled[Owner()]++; + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + default: + break; + } + + /* + ** Since we lost an object, we lose the associated points as well. + */ + House->PointTotal -= points; +} + + +/*********************************************************************************************** + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * * + * This routine is used to find a nearby location from center of this object. It can lean * + * toward finding a location closest to an optional object. * + * * + * INPUT: object -- Optional object that the finding algorithm will try to find a close * + * spot to. * + * * + * OUTPUT: Returns with the cell that is closest to this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1995 JLB : Created. * + * 09/28/1995 JLB : Uses map scan function. * + *=============================================================================================*/ +CELL TechnoClass::Nearby_Location(TechnoClass const * techno) const +{ + assert(IsActive); + + SpeedType speed = Techno_Type_Class()->Speed; + if (speed == SPEED_WINGED) { + speed = SPEED_TRACK; + } + + CELL cell = 0; + if (techno != NULL) { + cell = Coord_Cell(techno->Center_Coord()); + } else { + cell = Coord_Cell(Center_Coord()); + } + + return(Map.Nearby_Location(cell, speed, Map[cell].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +} + + +/*********************************************************************************************** + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * * + * This routine will start the stealth tank to uncloak. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Uncloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { + if (Cloak == CLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + Cloak = UNCLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * * + * This routine will start the object into its cloaking state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Cloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { + Detach_All(false); + + if (Cloak == UNCLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + + Cloak = CLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * * + * This routine is called when this object should shimmer. If the object is cloaked, then * + * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * + * effect occurs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Shimmer(void) +{ + assert(IsActive); +#if(0) + if (IsCloakable && Cloak == CLOAKED) { + Cloak = CLOAKING; + CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); + CloakingDevice.Set_Rate(1); + } +#else + Do_Uncloak(); +#endif +} + + +/*********************************************************************************************** + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * * + * This routine will determine how this object should be drawn. Typically, this is the * + * unmodified visible state, but cloaked objects have a different character. * + * * + * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * + * object? If false, then an object owned by the player will never become * + * completely invisible. * + * * + * OUTPUT: Returns with the visual character to use when displaying this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1995 JLB : Created. * + * 05/27/1996 JLB : Knows about invisible objects. * + *=============================================================================================*/ +VisualType TechnoClass::Visual_Character(bool raw) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsInvisible) { + if ((Session.Type != GAME_NORMAL) || Is_Owned_By_Player()) return(VISUAL_NORMAL); + if (!Debug_Map) return(VISUAL_HIDDEN); + } + + /* + ** When uncloaked or in map editor mode, always draw the object normally. + */ + if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); + + /* + ** A cloaked unit will not be visible at all unless it is owned + ** by the player. + */ + if (Cloak == CLOAKED) { + // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + return(VISUAL_HIDDEN); + } + + int stage = CloakingDevice.Fetch_Stage(); + if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; + if (stage <= 0) { + return(VISUAL_NORMAL); + } + + stage = fixed(stage, MAX_UNCLOAK_STAGE) * 256; + + if (stage < 0x0040) return(VISUAL_INDISTINCT); + if (stage < 0x0080) return(VISUAL_DARKEN); + if (stage < 0x00C0) return(VISUAL_SHADOWY); + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + if (stage < 0x00FF) return(VISUAL_RIPPLE); + return(VISUAL_HIDDEN); +} + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * * + * This routine is used to draw the object. It will handle any remapping or cloaking * + * effects required. This logic is isolated here since all techno object share the same * + * render logic when it comes to remapping and cloaking. * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * rotation -- The rotation of the object. * + * * + * scale -- The scaling factor to use (24.8 fixed point). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 01/11/1996 JLB : Added rotation and scaling. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const +{ + assert(IsActive); + + if (rotation != DIR_N || scale != 0x0100) { + Disable_Uncompressed_Shapes(); + } + + if (shapefile != NULL) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + void const * shadow = Map.UnitShadow; + +#ifdef PARTIAL + /* + ** Create a minimum shape rectangle if one hasn't already been + ** calculated and the shape file matches the one that the + ** class thinks it should be using. This check is necessary because + ** the dimension rectangle pointer is referenced from the type class + ** object on the presumption that the shapefile pointer passed to this + ** routine matches. If it doesn't match, then the wrong rectangle information + ** will be stored into the type class object. + */ + TechnoTypeClass * ttype = Techno_Type_Class(); + if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { + if (ttype->DimensionData == NULL) { + ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; + } + if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { + ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); + } + } +#endif + if (Height > 0) { + shadow = Map.UnitShadowAir; + } + + y -= Lepton_To_Pixel(Height); + + /* + ** If they're viewing a spy, and the spy belongs to some other house, + ** make it look like an infantryman from our house + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (!IsOwnedByPlayer) { + if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); + } + if (((InfantryClass *)this)->Class->IsRemapOverride) { + remap = ((InfantryClass *)this)->Class->OverrideRemap; + } + } + + /* + ** Check for the special visual effect for the iron curtain + */ + if (IronCurtainCountDown > 0) { +// remap = RemapEmber; + remap = DisplayClass::FadingRed; + } + +#ifdef PREDATOR + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); + } +#else + switch (visual) { + case VISUAL_NORMAL: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + break; + + case VISUAL_INDISTINCT: + case VISUAL_DARKEN: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); + break; + + case VISUAL_SHADOWY: + case VISUAL_RIPPLE: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + break; + + case VISUAL_HIDDEN: + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if (window == WINDOW_VIRTUAL) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + } + break; + } +#endif + } + + Enable_Uncompressed_Shapes(); +} + + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object_Virtual -- Draw object with virtual window support * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * rotation -- The rotation of the object. * + * * + * scale -- The scaling factor to use (24.8 fixed point). * + * * + * shape_name -- The name of the shapefile * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 8/1/2019 5:32PM - ST : Created. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale, const char *shape_name) const +{ + assert(IsActive); + + if (shape_name == NULL || *shape_name == 0) { + /* + ** If there's no override shape name, then call the regular draw + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); + return; + } + + if (shapefile != NULL) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + void const * shadow = Map.UnitShadow; + +#ifdef PARTIAL + /* + ** Create a minimum shape rectangle if one hasn't already been + ** calculated and the shape file matches the one that the + ** class thinks it should be using. This check is necessary because + ** the dimension rectangle pointer is referenced from the type class + ** object on the presumption that the shapefile pointer passed to this + ** routine matches. If it doesn't match, then the wrong rectangle information + ** will be stored into the type class object. + */ + TechnoTypeClass * ttype = Techno_Type_Class(); + if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { + if (ttype->DimensionData == NULL) { + ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; + } + if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { + ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); + } + } +#endif + + if (Height > 0) { + shadow = Map.UnitShadowAir; + } + + y -= Lepton_To_Pixel(Height); + + /* + ** If they're viewing a spy, and the spy belongs to some other house, + ** make it look like an infantryman from our house + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (!IsOwnedByPlayer) { + if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); + } + if (((InfantryClass *)this)->Class->IsRemapOverride) { + remap = ((InfantryClass *)this)->Class->OverrideRemap; + } + } + + /* + ** Check for the special visual effect for the iron curtain + */ + if (IronCurtainCountDown > 0) { +// remap = RemapEmber; + remap = DisplayClass::FadingRed; + } + +#ifdef PREDATOR + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); + } +#else + switch (visual) { + case VISUAL_NORMAL: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + break; + + case VISUAL_INDISTINCT: + case VISUAL_DARKEN: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); + break; + + case VISUAL_SHADOWY: + case VISUAL_RIPPLE: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + break; + + case VISUAL_HIDDEN: + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if (window == WINDOW_VIRTUAL) { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + } + break; + } +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * * + * This routine is used to fetch the appropriate remap table to use for this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoClass::Remap_Table(void) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsRemappable) { + return(House->Remap_Table(IsBlushing, Techno_Type_Class()->Remap)); + } + return(ColorRemaps[PCOLOR_GOLD].RemapTable); +} + + +/*********************************************************************************************** + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * * + * This routine is called when the specified object is about to be removed from the game * + * system. The target object is removed from any tracking computers that this object may * + * have. * + * * + * INPUT: target -- The target object (as a target value) that is being removed from the * + * game. * + * * + * all -- Is the target about to die? A false value might indicate that the * + * object is merely cloaking. In such a case, radio contact will not * + * be affected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + RadioClass::Detach(target, all); + + if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { + SuspendedMission = MISSION_NONE; + SuspendedTarCom = TARGET_NONE; + } + + /* + ** If the targeting computer is assigned to the target, then the targeting + ** computer must be cleared. + */ + if (TarCom == target) { + Assign_Target(TARGET_NONE); + Restore_Mission(); + } + + /* + ** If it is in radio contact with another object, then that radio contact + ** must be broken. + */ + if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { + Transmit_Message(RADIO_OVER_OUT); + } +} + + +/*********************************************************************************************** + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * * + * This routine handles the destruction of any cargo this object may contain. Typical of * + * this would be when a transport helicopter gets destroyed. * + * * + * INPUT: source -- The source of the destruction of the cargo. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Kill_Cargo(TechnoClass * source) +{ + assert(IsActive); + + while (Is_Something_Attached()) { + FootClass * foot = Detach_Object(); + if (foot != NULL) { + foot->Record_The_Kill(source); + delete foot; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * * + * This routine is called when generating survivors to this object. This routine returns * + * the type of survivor to generate. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type of a survivor. * + * * + * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * + * generate. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType TechnoClass::Crew_Type(void) const +{ + assert(IsActive); + + /* + ** If this object contains no crew, then there can be no + ** crew inside, duh... return this news. + */ + if (!Techno_Type_Class()->IsCrew) { + return(INFANTRY_NONE); + } + + /* + ** The normal infantry survivor is the standard issue + ** minigunner. Certain buildings, especially neutral ones, tend to have + ** civilians exit them instead. + */ + InfantryType infantry = INFANTRY_E1; + if (House->ActLike == HOUSE_NEUTRAL) { + infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); + } else { + if (Techno_Type_Class()->PrimaryWeapon == NULL && Percent_Chance(15)) { + if (Percent_Chance(50)) { + infantry = INFANTRY_C1; + } else { + infantry = INFANTRY_C7; + } + } + } + return(infantry); +} + + +/*********************************************************************************************** + * TechnoClass::Value -- Fetches the target value for this object. * + * * + * This routine is used to fetch the target value for this object. The greater the value * + * returned, the better this object is as a target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + * 08/16/1995 JLB : Adjusted for early mission lame-out. * + *=============================================================================================*/ +int TechnoClass::Value(void) const +{ + assert(IsActive); + + int value = 0; + + /* + ** In early missions, contents of transports are not figured + ** into the total value. + */ + if (Rule.Diff[House->Difficulty].IsContentScan || House->IQ >= Rule.IQContentScan) { + if (Is_Something_Attached()) { + FootClass * object = Attached_Object(); + + while (object != NULL) { + value += object->Value(); + object = (FootClass *)(ObjectClass *)object->Next; + } + } + } + +#ifdef TOFIX + /* + ** Increase the value of power producing object when there is power critical + ** defensive structures. + */ + if (What_Am_I() == RTTI_BUILDING && ((BuildingClass *)this)->Class->Power) { + if (House->BScan & (STRUCTF_ATOWER|STRUCTF_OBELISK)) { + value += Techno_Type_Class()->Reward; + } + } +#endif + + return Risk() + Techno_Type_Class()->Reward + value; +} + + +/*********************************************************************************************** + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * * + * This routine will return the range to scan based on the control value specified. The * + * value returned by this routine is typically used when scanning for enemies. * + * * + * INPUT: control -- The range control parameter. * + * 0 = Use weapon range (zero is returned in this special case). * + * -1 = Scan without range restrictions (-1 is returned in this case). * + * 1 = Scan up to twice weapon range. * + * * + * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * + * process. If zero is returned, then always check threat against In_Range(). If * + * -1 is returned, then no range limitation restriction exists. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Threat_Range(int control) const +{ + assert(IsActive); + + /* + ** Threat range means nothing if scanning the whole map. In such a case, just + ** return with the same control flag specified. + */ + if (control == -1) return(-1); + + /* + ** If simple guard range is requested, then return "0" since + ** this is a special control value that is calculated as the object's + ** weapon range. + */ + if (control == 0) { + /* + ** For normal guard mode or for area guard mode, use the override + ** threat range value as specified by the object's type class. + */ + if (Techno_Type_Class()->ThreatRange != 0) { + return(Techno_Type_Class()->ThreatRange); + } + return(0); + } + + /* + ** Area guard range is specified, so figure twice the weapon range of the + ** longest range weapon this object is equipped with. + */ + int range = Techno_Type_Class()->ThreatRange; + if (range == 0) { + range = max(Weapon_Range(0), Weapon_Range(1)); + } + + range *= 2; + range = Bound(range, 0x0000, 0x0A00); + + return(range); +} + + +/*********************************************************************************************** + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * * + * This will examine the specified cell to determine if it is in the same zone as this * + * object's location. * + * * + * INPUT: cell -- The cell that is to be checked against this object's current location. * + * * + * OUTPUT: bool; Is the specified cell in the same zone as this object is? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/06/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_In_Same_Zone(CELL cell) const +{ + MZoneType zone = Techno_Type_Class()->MZone; + return(Map[cell].Zones[zone] == Map[Center_Coord()].Zones[zone]); +} + + +/*********************************************************************************************** + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * * + * This routine is called when the base is being attacked. It will pull units off of the * + * field and send them back to defend the base. This routine will make taking an enemy * + * base much more difficult. * + * * + * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can drastically affect the game play. The computer will probably * + * call off its attacks as a result. * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + * 10/15/1996 JLB : Alternates between guard area and attack. * + * 11/01/1996 JLB : Allow recruit of guard area units in multiplay. * + *=============================================================================================*/ +void TechnoClass::Base_Is_Attacked(TechnoClass const * enemy) +{ + assert(IsActive); + + FootClass * defender[6]; + memset(defender, '\0', sizeof(defender)); + + int value[ARRAY_SIZE(defender)]; + memset(value, '\0', sizeof(value)); + + int count = 0; + int weakest = 0; + int desired = enemy->Risk() * House->Control.TechLevel; + int risktotal = 0; + int zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + + /* + ** Humans have to deal with their own base is attacked problems. + */ + if (enemy == NULL || House->Is_Ally(enemy) || House->IsHuman) { + return; + } + + /* + ** Don't overreact if this building can defend itself. + */ + if (Session.Type == GAME_NORMAL && Techno_Type_Class()->PrimaryWeapon != NULL) return; + + /* + ** If the enemy is not an infantry or a unit there is not much we can + ** do about it. + */ + if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT) { + return; + } + + /* + ** If we are a certain type of building, such as a barrel or land mine, + ** ignore the attack. + */ + if (Techno_Type_Class()->IsInsignificant) { + return; + } + + /* + ** If the threat has already been dealt with then we don't need to do + ** any work. Check for that here. + */ + if (enemy->Is_Foot() && ((FootClass *)enemy)->BaseAttackTimer != 0) { + return; + } + + /* + ** We will need units to defend our base. We need to suspend teams until + ** the situation has been dealt with. + */ + TeamClass::Suspend_Teams(Rule.SuspendPriority, House); + + /* + ** Loop through the infantry looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Infantry.Count() && desired > 0; index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (infantry != NULL && infantry->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!infantry->Is_Weapon_Equipped() || + (!MissionControl[infantry->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; +// (Mission != MISSION_GUARD_AREA || Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (infantry->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[infantry->Center_Coord()].Zones[infantry->Class->MZone] != Map[Center_Coord()].Zones[infantry->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = infantry->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (ArchiveTarget == As_Target()) { + threat *= 100; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = infantry; + value[count] = threat; + count++; + continue; + } + + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) infantry; + continue; + } + if (value[lp] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + /* + ** Loop through the units looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Units.Count() && desired > 0; index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && unit->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!unit->Is_Weapon_Equipped() || + (!MissionControl[unit->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (unit->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[unit->Center_Coord()].Zones[unit->Class->MZone] != Map[Center_Coord()].Zones[unit->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = unit->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (threat > 0 && ArchiveTarget == As_Target()) { + threat *= 10; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = unit; + value[count] = threat; + count++; + continue; + } + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) unit; + continue; + } + if (value[lp] < newweakest) { +// if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + if (desired > 0) { + + /* + ** Sort the defenders by value, this doesn't take very long and will + ** help the closest defenders to respond. + */ + for (int lp = 0; lp < count - 1; lp ++) { + for (int lp2 = lp + 1; lp2 < count; lp2++) { + if (value[lp] < value[lp2]) { + + value[lp] ^= value[lp2]; + value[lp2] ^= value[lp]; + value[lp] ^= value[lp2]; + + FootClass *temp; + temp = defender[lp]; + defender[lp] = defender[lp2]; + defender[lp2] = temp; + } + } + } + + for (int lp = 0; lp < count; lp ++) { + if (Percent_Chance(50)) { + defender[lp]->Assign_Mission(MISSION_RESCUE); + } else { + defender[lp]->Assign_Mission(MISSION_GUARD_AREA); + defender[lp]->ArchiveTarget = As_Target(); + } + defender[lp]->Assign_Target(enemy->As_Target()); + risktotal += defender[lp]->Risk(); + if (risktotal > desired) { + break; + } + } + } + + if (risktotal > desired && enemy->Is_Foot()) { + ((FootClass *)enemy)->BaseAttackTimer = TICKS_PER_MINUTE * Rule.BaseDefenseDelay; + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * * + * This routine is called when this object has suffered some damage and it needs to know * + * if it should fight back. The object that caused the damage is specifed as a parameter. * + * * + * INPUT: source -- The points to the object that was the source of the damage applied * + * to this object. * + * * + * OUTPUT: bool; Should retaliation occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Retaliate(TechnoClass const * source) const +{ + /* + ** If there is no source of the damage, then retaliation cannot occur. + */ + if (source == NULL) return(false); + + /* + ** If the mission precludes retaliation, then don't retaliate. + */ + if (!MissionControl[Mission].IsRetaliate) return(false); + + /* + ** Fixed wing aircraft are not responsive enough to retaliate to damage recieved. + */ + if (What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)this)->Class->IsFixedWing) { + return(false); + } + + /* + ** If the source of the damage is an ally, then retaliation shouldn't + ** occur either. + */ + if (House->Is_Ally(source)) return(false); + + /* + ** Only objects that have a damaging weapon are allowed to retaliate. + */ + if (Combat_Damage() <= 0 || !Is_Weapon_Equipped()) return(false); + + /* + ** If this is not equipped with a weapon that can attack the molester, then + ** don't allow retaliation. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (ttype->PrimaryWeapon->WarheadPtr != NULL && + ttype->PrimaryWeapon->WarheadPtr->Modifier[source->Techno_Type_Class()->Armor] == 0) { + return(false); + } + + /* + ** One can never retaliate against a dog because of their peculiar nature of attacking. + ** Dogs must be attacked using normal target processing. + */ + if (source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) return(false); + + /* + ** Don't allow retaliation if it isn't equipped with a weapon that can deal with the threat. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT && !ttype->PrimaryWeapon->Bullet->IsAntiAircraft) return(false); + + /* + ** Tanya is not allowed to retaliate against buildings in the normal sense while in guard mode. That + ** is, unless it is owned by the computer. Normally, Tanya can't do anything substantial to a building + ** except to blow it up. + */ + if ((House->IsHuman || (Session.Type == GAME_NORMAL && House->IsPlayerControl)) + && source->What_Am_I() == RTTI_BUILDING && What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const *)ttype)->IsBomber) { + return(false); + } + + /* + ** If a human house is not allowed to retaliate automatically, then don't + */ + if (House->IsHuman && !Rule.IsSmartDefense && (What_Am_I() != RTTI_INFANTRY || *((InfantryClass*)this) != INFANTRY_TANYA || source->What_Am_I() != RTTI_INFANTRY)) return(false); + + /* + ** If this object is part of a team that prevents retaliation then don't allow retaliation. + */ + if (Is_Foot() && ((FootClass *)this)->Team.Is_Valid() && ((FootClass *)this)->Team->Class->IsSuicide) { + return(false); + } + + /* + ** Compare potential threat of the current target and the potential new target. Don't retaliate + ** if it is currently attacking the greater threat. + */ + if (!House->IsHuman && Percent_Chance(50)) { + fixed source_val = 0; + int primary = What_Weapon_Should_I_Use(source->As_Target()); + WeaponTypeClass const * weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(source, primary)) { + source_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + + fixed current_val = 0; + TechnoClass const * tech = As_Techno(TarCom); + if (tech != NULL) { + primary = What_Weapon_Should_I_Use(tech->As_Target()); + weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(tech, primary)) { + current_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + } + if (source_val <= current_val) return(false); + } + + /* + ** If it is already busy attacking another target, then don't retaliate. + */ +// if (In_Range(TarCom)) return(false); + + /* + ** All checks passed, so return that retaliation is allowed. + */ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * * + * This routine will return the ownable bits for this object. The ownable bits represent * + * the houses that are allowed to own this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the ownable bits for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Get_Ownable(void) const +{ + assert(IsActive); + + return ((TechnoTypeClass const &)Class_Of()).Get_Ownable(); +// return ((TechnoTypeClass const &)Class_Of()).Ownable; +} + + +/*********************************************************************************************** + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * * + * This routine is called when the risk value for this object needs to be determined. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the risk value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Risk(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->Risk); +} + + +/*********************************************************************************************** + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * * + * This routine will return the current Tiberium load (expressed as a fixed point fraction) * + * that this object currently contains. Typical implementor of this function would be * + * the harvester. Any object that can return a non-zero value should derive from this * + * function in order to return the appropriate value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * + * 0x0000 = empty * + * 0x0080 = half full * + * 0x0100 = full * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Tiberium_Load(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * * + * This routine is called when an object desires to load up on this object. The object * + * desiring to load is specified. The cell that the loading object should move to is * + * determined. The direction that this object should face is also calculated. This routine * + * will be overridden by those objects that can actually load up passengers. * + * * + * INPUT: object -- The object that is desiring to load up. * + * * + * moveto -- Reference to the cell that the loading object should move to before * + * the final load process occurs (this value will be filled in). * + * * + * OUTPUT: Returns with the direction that the transport object should face. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const +{ + assert(IsActive); + + moveto = 0; + return(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * * + * This routine will return the number of pips to display on this object when the object * + * is selected. The default condition is to return no pips at all. This routine is * + * derived for those objects that can have pips. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this object when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Pip_Count(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * * + * This routine will fetch the direction that a fired projectile will take. This is * + * usually the facing of the object's weapon. This routine will be derived for the objects * + * that have their weapon barrel facing a different direction than the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction a fired projectile will take. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Fire_Direction(void) const +{ + assert(IsActive); + + return(Turret_Facing()); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * * + * This routine is called when a voice response to a select action is desired. This routine * + * should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Select(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * * + * This routine is called when a voice response to a movement order is desired. This * + * routine should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Move(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * * + * This routine is called when a voice response to an attack order is desired. This routine * + * should be overridden for any object that actually have a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Attack(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * * + * This routine will search for a nearby target and assign it to this object's TarCom. * + * The method to use when scanning for a target is controlled by the parameter passed. * + * * + * INPUT: threat -- The threat control parameter used to control the range searched. The * + * only values recognized are THREAT_RANGE and THREAT_AREA. * + * * + * OUTPUT: Was a suitable target acquired and assigned to the TarCom? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Target_Something_Nearby(ThreatType threat) +{ + assert(IsActive); + threat = threat & (THREAT_RANGE|THREAT_AREA); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + if ((threat & THREAT_RANGE)) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + } + + /* + ** If there is no target, then try to find one and assign it as + ** the target for this unit. + */ + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** Return with answer to question: Does this unit now have a target? + */ + return(Target_Legal(TarCom)); +} + + +/*********************************************************************************************** + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * * + * This routine is called when there is an attached object that should detach and leave * + * this object. Typical of this would be the refinery and APC. * + * * + * INPUT: object -- The object that is trying to leave this object. * + * * + * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * + * there is insufficient room to detach the specified object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Exit_Object(TechnoClass *) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate. * + * * + * This will examine this object to determine if it is time and ready to perform some * + * kind of random animation. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is it time to perform an random animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Random_Animate(void) const +{ + assert(IsActive); + return(IdleTimer == 0); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. This routine must be overridden since at this level, movement is not allowed. * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Destination(TARGET ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * * + * This routine is called when the object should intelligently revert to an idle state. * + * Typically this routine is called after some mission has completed. This routine must * + * be overridden by the various object types. It is located at this level merely to provide * + * a virtual function entry point. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Enter_Idle_Mode(bool ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * * + * This routine is used to render the small transportation pip (occupant feedback graphic) * + * used for transporter object. It will also display if the techno object is "primary" * + * if necessary. * + * * + * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsequent pips * + * are drawn rightward. * + * * + * window-- The window that pip clipping is relative to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1995 JLB : Created. * + * 10/06/1995 JLB : Displays the team group number. * + * 09/10/1996 JLB : Medic hack for red pip. * + *=============================================================================================*/ +void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Transporter type objects have a different graphic representation for the pips. The + ** pip color represents the type of occupant. + */ + bool carrying_passengers = (Techno_Type_Class()->Max_Passengers() > 0) && + ((What_Am_I() != RTTI_AIRCRAFT) || (*(AircraftClass*)this != AIRCRAFT_BADGER) || (Mission != MISSION_HUNT)); + if (carrying_passengers) { + ObjectClass const * object = Attached_Object(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + PipEnum pip = PIP_EMPTY; + + if (object != NULL) { + pip = PIP_FULL; + if (object->What_Am_I() == RTTI_INFANTRY) { + pip = ((InfantryClass *)object)->Class->Pip; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_CARRIER) { + if (object->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass *heli = (AircraftClass *)object; + if (heli->Ammo != heli->Techno_Type_Class()->MaxAmmo) { + pip = PIP_ENGINEER; + if (!heli->Ammo) { + pip = PIP_COMMANDO; + } + } + } + } +#endif + object = object->Next; + } + CC_Draw_Pip(this, Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + } else { + + /* + ** Display number of how many attached objects there are. This is also used + ** to display the fullness rating for a harvester. + */ + int pips = Pip_Count(); + + /* + ** Check if it's a harvester, to show the right type of pips for the + ** various minerals it could have harvested. + */ + if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_HARVESTER) { + UnitClass * harv = (UnitClass *)this; + + int iron = harv->Gems; + int nickel = harv->Gold; + int graypips = pips * fixed(iron, Rule.BailCount); + int greenpips = pips * fixed(nickel, Rule.BailCount); + + while (greenpips + graypips < pips) { + int ironnickelmax = max(iron, nickel); + if (iron > nickel) { + graypips++; + } else { + greenpips++; + } + } + + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + int shape = PIP_EMPTY; + if (index < pips) { + if (greenpips) { + shape = PIP_FULL; + greenpips--; + } else { + shape = PIP_COMMANDO; + graypips--; + } + } + CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** Check if it's a Chrono tank, to show the recharge gauge. + */ + else if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_CHRONOTANK) { + for (int index = 0; index < 5; index++) { + int shape = PIP_EMPTY; + if (index < pips) { + switch(index) { + case 0: + case 1: + shape = PIP_COMMANDO; + break; + + case 2: + case 3: + shape = PIP_ENGINEER; + break; + + case 4: + shape = PIP_FULL; + default: + break; + } + } + CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } +#endif + } else { + bool building = false; + int pip = PIP_FULL; // green + if(!IsOwnedByPlayer && What_Am_I() == RTTI_BUILDING) { + if(*(BuildingClass *)this==STRUCT_POWER || *(BuildingClass *)this==STRUCT_ADVANCED_POWER) { + building = true; + if (House->Power_Fraction() < 1) { + pip = PIP_ENGINEER; // gold + if (House->Drain > (House->Power * 2) ) { + pip = PIP_COMMANDO; + } + } + } + } + + // Ally/spied power display is handled separately in the virtual window + if (!building || (window != WINDOW_VIRTUAL)) { + for (int index = 0; index < (building ? 5 : Class_Of().Max_Pips()); index++) { + if (building) { + CC_Draw_Pip(this, Class_Of().PipShapes, pip, x, y-index*3, window, SHAPE_CENTER|SHAPE_WIN_REL); + } else { + CC_Draw_Pip(this, Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + +//BG for (int index = 0; index < Class_Of().Max_Pips(); index++) { +//BG CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +//BG } + } + } + } + + /* + ** Special hack to display a red pip on the medic. + */ + if (What_Am_I() == RTTI_INFANTRY && Combat_Damage() < 0) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_MEDIC, x+8, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + /* + ** Display whether this unit is a leader unit or not. + */ + if (IsLeader && (window != WINDOW_VIRTUAL)) { + PipEnum prishape = PIP_PRIMARY; + + if(What_Am_I() == RTTI_BUILDING) { + if(*((BuildingClass *)this) == STRUCT_KENNEL) { + prishape = PIP_PRI; + } + } + CC_Draw_Shape(Class_Of().PipShapes, prishape, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); + } + + /* + ** Display what group this unit belongs to. This corresponds to the team + ** number assigned with the key. + */ + if (Is_Foot() && ((FootClass *)this)->Group != 0xFF && ((FootClass *)this)->Group < 10) { + int yval = -1; + int group = ((FootClass *)this)->Group; + + if (Class_Of().Max_Pips()) yval -= 4; + if (group == 10) group = 0; + + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS+group, x+2, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + + /* + ** If this unit is part of a formation, draw an 'F' after the group + ** number. + */ + if ( ((FootClass *)this)->XFormOffset != 0x80000000UL) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_LETTERF, x+8, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + /* + ** If this building is being spied on by the player, draw the money or + ** factory-producing item or whatever. + */ + if (What_Am_I() == RTTI_BUILDING) { + int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); + + /* + ** Print word "Decoy" above buildings that are spied upon or fake + */ + if (((BuildingClass *)this)->Class->IsFake) { + if (spiedby || IsOwnedByPlayer) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_DECOY, x, y-16, window, SHAPE_WIN_REL); + } + } + /* + ** See if we should print the credits for a spied refinery + */ + if (spiedby) { + // If it's a refinery/silo, print the enemy's money + if (((BuildingClass *)this)->Class->Capacity) { + long money = House->Available_Money(); + + /* + ** Determine how many digits will be printed. + */ + int digits; + int factor = 10; + for (digits = 1; digits < 9; digits++) { + if (money < factor) break; + factor *= 10; + } + + int startx = x + 6 * digits - 3;// + 6 * 8; + while (money) { + int xdigit = money % 10; + money /= 10; + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS + xdigit, startx, y-6, window, SHAPE_CENTER|SHAPE_WIN_REL); + startx -= 6; + } + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * + *=============================================================================================*/ +BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + assert(IsActive); + + BuildingClass * best = 0; + + /* + ** First check to see if there are ANY buildings of the specified + ** type in this house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->Get_Quantity(b) != 0) { + int bestval = -1; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (building != NULL && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + !building->IsInLimbo && + *building == b && + (What_Am_I() == RTTI_AIRCRAFT || Map[building->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) && + ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { + best = building; + bestval = Distance(building); + } + } + } + } + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * * + * This routine is called when an object would like to exit from this (presumed) transport. * + * A suitable cell should be returned by this routine. The specified object will probably * + * be unloaded at that cell. * + * * + * INPUT: techno -- Pointer to the object that would like to unload. This is used to * + * determine suitability for placement. * + * * + * OUTPUT: Returns with the cell that is recommended for object exit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const +{ + assert(IsActive); + + return(Coord_Cell(Docking_Coord())); +} + + +/*********************************************************************************************** + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * * + * This routine is used by the selling back mechanism in order to credit the owning house * + * with some refund credits. The value returned is the credits to refund to the owner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credits to refund to the owner. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Refund_Amount(void) const +{ + assert(IsActive); + + int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias; + +#ifdef TOFIX + /* + ** If the object is carrying Tiberium directly (i.e., the harvester), then + ** account for the credits of the load. + */ +// cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; +#endif + + if (House->IsHuman) { + cost = cost * Rule.RefundPercent; +// cost /= 2; + } + return(cost); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * * + * This routine will calculate and return the anti-aircraft strength of this object. * + * Typical users of this strength value is the base defense expert system AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned * + * is an abstract number to be used for relative comparisons only. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Air(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + + if (bullet->IsAntiAircraft) { + int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF; + + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + return(value/50); + } + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * * + * This routine is used to examine and calculate the anti-armor strength of this object. * + * Typical user user of this would be the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the relative anti-armor combat value for this object. The value * + * is abstract and is only to be used in relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Armor(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * * + * This routine is used to determine the anti-infantry strength of this object. The * + * typical user of this routine is the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is * + * abstract and should only be used for relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Infantry(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * * + * This routine will reveal the map around this object. * + * * + * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * + * If it is absolutely known that the object has only moved one * + * cell from its previous location that it performed a Look() at, * + * then set this parameter to TRUE. It will only perform the look * + * check on the perimeter cells. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is slow, try to call it only when necessary. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + + if (sight_range) { + + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + + +#if (0) // Leaving this here for posterity, in case we need it for revealing allies. ST - 10/17/2019 10:51AM + /* + ** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 8/7/2019 11:34AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass *house = HouseClass::As_Pointer(house_type); + + if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) { + Map.Sight_From(Coord_Cell(Center_Coord()), sight_range, house, incremental); + } + } + } +#endif + } +} + + +//********************************************************************************************** +// MODULE SEPARATION -- TechnoTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * * + * This is the normal constructor for techno type objects. It is called in the process of * + * constructing all the object type (constant) data for the various techno type objects. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 05/11/1996 JLB : Moderated risk calc so range doesn't dominate. * + *=============================================================================================*/ +TechnoTypeClass::TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed, + int horizontaloffset) : + ObjectTypeClass( rtti, + id, + true, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_footprint, + name, + ininame), + Remap(remap), + IsDoubleOwned(false), + IsInvisible(false), + IsLeader(false), + IsScanner(false), + IsNominal(is_nominal), + IsTheater(is_theater), + IsTurretEquipped(is_turret_equipped), + IsCrew(false), + IsRepairable(true), + IsRemappable(is_remappable), + IsCloakable(false), + IsSelfHealing(false), + IsExploding(false), + MZone(MZONE_NORMAL), + ThreatRange(0), + MaxPassengers(0), + SightRange(0), + Cost(0), + Level(-1), + Prerequisite(STRUCTF_NONE), + Risk(0),Reward(0), + MaxSpeed(MPH_IMMOBILE), + Speed(speed), + MaxAmmo(-1), + Ownable(0), + CameoData(NULL), + Rotation(rotation), + ROT(0), + PrimaryWeapon(NULL), + SecondaryWeapon(NULL), + HorizontalOffset(horizontaloffset), + VerticalOffset(verticaloffset), + PrimaryOffset(primaryoffset), + PrimaryLateral(primarylateral), + SecondaryOffset(secondaryoffset), + SecondaryLateral(secondarylateral), + Points(0) +{ +} + + +/*********************************************************************************************** + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * * + * This routine is used to find the underlying cost for this object. The underlying cost * + * does not include any free items that normally come with the object when purchased * + * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * + * harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost of the base object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Raw_Cost(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * * + * This routine will return the ownable bits for this object type. The ownable bits are * + * a bitflag composite of the houses that can own (build) this object type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable bits for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Get_Ownable(void) const +{ + if (IsDoubleOwned && Session.Type != GAME_NORMAL) { + return(Ownable | HOUSEF_SOVIET | HOUSEF_ALLIES); + } + return(Ownable); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * * + * This routine will return the time it takes to construct this object. Usually the time * + * to produce is directly related to cost. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * + * form of game ticks. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Time_To_Build(void) const +{ + return(Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000)); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * * + * This routine will return the cost to produce an object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce one object of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Cost_Of(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * * + * This routine will fetch the cameo (sidebar small image) shape of this object type. * + * If there is no cameo data available (typical for non-produceable units), then NULL will * + * be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoTypeClass::Get_Cameo_Data(void) const +{ + return(CameoData); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * * + * This routine will return the cost to repair one step. At the TechnoTypeClass level, * + * this merely serves as a placeholder function. The derived classes will provide a * + * functional version of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to repair one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Cost(void) const +{ + // MBL 04.28.2020 "Fix" for RA Barracks structures coming back with a repair cost of 0, and repairing for free + // + // if (Is_Foot()) { + // return((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); + // } + // return((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); + // + int repair_cost = 0; + if (Is_Foot()) { + repair_cost = ((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); + } + else + { + repair_cost = ((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); + } + repair_cost = MAX(repair_cost, 1); + return repair_cost; +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * * + * This routine merely serves as placeholder virtual function. The various type classes * + * will override this routine to return the number of health points to repair in one * + * "step". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to repair in one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Step(void) const +{ + if (Is_Foot()) { + return(Rule.URepairStep); + } + return(Rule.RepairStep); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * * + * Some objects fire two shots in quick succession. If this is true for this object, then * + * a 'true' value will be returned from this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object a two shooter? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Is_Two_Shooter(void) const +{ + if (PrimaryWeapon != NULL && (PrimaryWeapon == SecondaryWeapon || PrimaryWeapon->Burst > 1)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * * + * Use this routine to fill in the data for this techno type class object from the * + * database specified. Typical use of this is for the rules parsing. * + * * + * INPUT: ini -- Reference to the INI database that the information will be lifted from. * + * * + * OUTPUT: bool; Was the database used to extract information? A failure (false) response * + * would mean that the database didn't contain a section that applies to this * + * techno class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { +#ifdef FIXIT_NAME_OVERRIDE + char buffer[256]; + int id = ((RTTI+1) * 100) + ID; + + ini.Get_String(Name(), "Name", "", buffer, sizeof(buffer)); + if (strlen(buffer) > 0) { + +#if defined (GERMAN)||(FRENCH) + + for(int xx=0; NewName[xx] != NULL; xx++){ + if(!strcmp(NewName[xx], buffer)){ + memcpy(buffer, "", sizeof(buffer)); + memcpy(buffer, NewName[xx+1], sizeof(buffer)); + break; + } + } +#endif + + /* + ** Insert the new name text into the buffer list. + */ + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == 0) { + NameOverride[index] = strdup(buffer); + NameIDOverride[index] = id; +// FullName = -(index+1); + break; + } + } + } +#endif + + IsDoubleOwned = ini.Get_Bool(Name(), "DoubleOwned", IsDoubleOwned); + ThreatRange = ini.Get_Lepton(Name(), "GuardRange", ThreatRange); + IsExploding = ini.Get_Bool(Name(), "Explodes", IsExploding); + PrimaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Primary", PrimaryWeapon != NULL ? (WeaponType)(PrimaryWeapon->ID) : WEAPON_NONE)); + SecondaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Secondary", SecondaryWeapon != NULL ? (WeaponType)(SecondaryWeapon->ID) : WEAPON_NONE)); + IsCloakable = ini.Get_Bool(Name(), "Cloakable", IsCloakable); + IsCrushable = ini.Get_Bool(Name(), "Crushable", IsCrushable); + IsScanner = ini.Get_Bool(Name(), "Sensors", IsScanner); + Armor = ini.Get_ArmorType(Name(), "Armor", Armor); + Prerequisite = ini.Get_Buildings(Name(), "Prerequisite", Prerequisite); + MaxStrength = ini.Get_Int(Name(), "Strength", MaxStrength); + SightRange = ini.Get_Int(Name(), "Sight", SightRange); + Level = ini.Get_Int(Name(), "TechLevel", Level); + MaxSpeed = MPHType(_Scale_To_256(ini.Get_Int(Name(), "Speed", fixed(MaxSpeed, 256) * 100))); + Cost = ini.Get_Int(Name(), "Cost", Cost); + MaxAmmo = ini.Get_Int(Name(), "Ammo", MaxAmmo); + Risk = Reward = Points = ini.Get_Int(Name(), "Points", Points); + Ownable = ini.Get_Owners(Name(), "Owner", Ownable); + IsCrew = ini.Get_Bool(Name(), "Crewed", IsCrew); + IsRepairable = ini.Get_Bool(Name(), "Repairable", IsRepairable); + IsInvisible = ini.Get_Bool(Name(), "Invisible", IsInvisible); + IsSelfHealing = ini.Get_Bool(Name(), "SelfHealing", IsSelfHealing); + ROT = ini.Get_Int(Name(), "ROT", ROT); + MaxPassengers = ini.Get_Int(Name(), "Passengers", MaxPassengers); +//Mono_Printf("before image=: %s\n",GraphicName); + ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); +//Mono_Printf("after image=: %s\n",GraphicName);if(Random_Pick(0,4)) Keyboard->Get(); + + IsLeader = false; + if (PrimaryWeapon != NULL && PrimaryWeapon->Attack > 0) { + IsLeader = true; + } + + /* + ** Check to see what zone this object should recognize. + */ + if (PrimaryWeapon != NULL && PrimaryWeapon->WarheadPtr != NULL && PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + MZone = MZONE_DESTROYER; + } + if (Speed == SPEED_FLOAT) { + MZone = MZONE_WATER; + } + + return(true); + } + return(false); +} + + +int TechnoTypeClass::Legal_Placement(CELL pos) const +{ + if (pos == -1) return(0); + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + short const * offset = Occupy_List(true); + bool build = (What_Am_I() == RTTI_BUILDINGTYPE); + + while (offset != NULL && *offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + if (build) { + if (!Map[cell].Is_Clear_To_Build(Speed)) { + return(0); + } + } else { + if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) { + return(0); + } + } + } + return(1); +} + + + + + + + +/* +** Additions to TechnoClass to track discovery per-player. ST - 3/6/2019 11:18AM +** +** +** +** +** +*/ + + +/*********************************************************************************************** + * TechnoClass::Is_Discovered_By_Player -- Has this object been disovered by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if discovered by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:20AM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Discovered_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int) player->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } else { + int shift = (int) PlayerPtr->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } + return false; +} + + +/*********************************************************************************************** + * TechnoClass::Set_Discovered_By_Player -- Mark that this object been disovered by the player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:23AM - ST * + *=============================================================================================*/ +void TechnoClass::Set_Discovered_By_Player(HouseClass *player) +{ + int shift = 0; + if (player && player->Class) { + shift = (int) player->Class->House; + } else { + shift = (int)PlayerPtr->Class->House; + } + IsDiscoveredByPlayerMask |= (1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsDiscoveredByPlayer = true; + } +} + + + +/*********************************************************************************************** + * TechnoClass::Clear_Discovered_By_Players -- Clear player discovery flags * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/27/2020 - SKY * + *=============================================================================================*/ +void TechnoClass::Clear_Discovered_By_Players() +{ + IsDiscoveredByPlayerMask = 0; + IsDiscoveredByPlayer = false; +} + + + +/*********************************************************************************************** + * TechnoClass::Is_Owned_By_Player -- Is this object owned by the active human player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/13/2019 5:00PM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Owned_By_Player(HouseClass *player) const +{ + if (player == NULL) { + return (PlayerPtr == House) ? true : false; + } + return (player == House) ? true : false; +} + + + +/*********************************************************************************************** + * TechnoClass::Spied_By -- Spied by flags for this object * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/2019 - SKY * + *=============================================================================================*/ +unsigned TechnoClass::Spied_By() const +{ + return SpiedBy; +} \ No newline at end of file diff --git a/REDALERT/TECHNO.H b/REDALERT/TECHNO.H new file mode 100644 index 000000000..aa9e84c4a --- /dev/null +++ b/REDALERT/TECHNO.H @@ -0,0 +1,418 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TECHNO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TECHNO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TECHNO_H +#define TECHNO_H + +#include "radio.h" +#include "stage.h" +#include "cargo.h" +#include "flasher.h" +#include "house.h" +#include "target.h" +#include "bullet.h" +#include "door.h" +#include "crew.h" + +/**************************************************************************** +** This is the common data between building and units. +*/ +class TechnoClass : public RadioClass, + public FlasherClass, + public StageClass, + public CargoClass, + public DoorClass +{ + public: + CrewClass Crew; + + /* + ** If this techno object has detected that it has outlived its + ** purpose, then this flag will be true. Such object will either + ** be sold or sacrificed at the first opportunity. + */ + unsigned IsUseless:1; + + /* + ** This flag will be true if the object has been damaged with malice. + ** Damage received due to friendly fire or wear and tear does not count. + ** The computer is not allowed to sell a building unless it has been + ** damaged with malice. + */ + unsigned IsTickedOff:1; + + /* + ** If this object has inherited the ability to cloak, then this bit will + ** be set to true. + */ + unsigned IsCloakable:1; + + /* + ** If this object is designated as special then this flag will be true. For + ** buildings, this means that it is the primary factory. For units, it means + ** that the unit is the team leader. + */ + unsigned IsLeader:1; + + /* + ** Certain units are flagged as "loaners". These units are typically transports that + ** are created solely for the purpose of delivering reinforcements. Such "loaner" + ** units are not owned by the player and thus cannot be directly controlled. These + ** units will leave the game as soon as they have fulfilled their purpose. + */ + unsigned IsALoaner:1; + + /* + ** Once a unit enters the map, then this flag is set. This flag is used to make + ** sure that a unit doesn't leave the map once it enters the map. + */ + unsigned IsLocked:1; + + /* + ** Buildings and units with turrets usually have a recoil animation when they + ** fire. If this flag is true, then the next rendering of the object will be + ** in the "recoil state". The flag will then be cleared pending the next + ** firing event. + */ + unsigned IsInRecoilState:1; + + /* + ** If this unit is "loosely attached" to another unit it is given special + ** processing. A unit is in such a condition when it is in the process of + ** unloading from a transport type object. During the unloading process + ** the transport object must stay still until the unit is free and clear. + ** At that time it radios the transport object and the "tether" is broken - + ** freeing both the unit and the transport object. + */ + unsigned IsTethered:1; + + /* + ** Is this object owned by the player? If not, then it is owned by the computer + ** or remote opponent. This flag facilitates the many logic differences when dealing + ** with player's or computer's units or buildings. + */ + unsigned IsOwnedByPlayer:1; + + /* + ** The more sophisticated game objects must keep track of whether they are discovered + ** or not. This is because the state of discovery can often control how the object + ** behaves. In addition, this fact is used in radar and user I/O processing. + */ + unsigned IsDiscoveredByPlayer:1; + + /* + ** This is used to control the computer recognizing this object. + */ + unsigned IsDiscoveredByComputer:1; + + /* + ** Some game objects can be of the "lemon" variety. This means that they take damage + ** even when everything is ok. This adds a little variety to the game. + */ + unsigned IsALemon:1; + + /* + ** This flag is used to control second shot processing for those units or buildings + ** that fire two shots in quick succession. When this flag is true, it indicates that + ** the second shot is ready to fire. After this shot is fired, regular rearm timing + ** is used rather than the short rearm time. + */ + unsigned IsSecondShot:1; + + /* + ** This is the firepower and armor modifiers for this techno object. Normally, + ** these values are fixed at 0x0100, but they can be modified by certain + ** crate powerups. + */ + fixed ArmorBias; + fixed FirepowerBias; + + /* + ** Idle animations (if any are supported by the object type) are regulated by + ** this timer. When the timer expires an idle animation occurs. Then the + ** timer is reinitialized to some random (bounded) setting. + */ + CDTimerClass IdleTimer; + + /* + ** This timer keeps track of how long the unit is under the influence + ** of the iron curtain. + */ + CDTimerClass IronCurtainCountDown; + + + /* + ** This is a list of bits of which houses are spying on this building, + ** if in fact this is a building. + */ + unsigned SpiedBy; + + /* + ** For units in area guard mode, this is the recorded home position. The guarding + ** unit will try to stay near this location in the course of it's maneuvers. This is + ** also used to record a pending transport for those passengers that are waiting for + ** the transport to become available. It is also used by harvesters so that they know + ** where to head back to after unloading. + */ + TARGET ArchiveTarget; + + /* + ** This is the house that the unit belongs to. + */ + CCPtr House; + + /* + ** This records the current cloak state for this vehicle. + */ + CloakType Cloak; + StageClass CloakingDevice; + CDTimerClass CloakDelay; + + /* (Targeting Computer) + ** This is the target value for the item that this vehicle should ATTACK. If this + ** is a vehicle with a turret, then it may differ from its movement destination. + */ + TARGET TarCom; + TARGET SuspendedTarCom; + + /* + ** This is the visible facing for the unit or building. + */ + FacingClass PrimaryFacing; + + /* + ** This is the arming countdown. It represents the time necessary + ** to reload the weapon. + */ + CDTimerClass Arm; + + /* + ** The number of shot this object can fire before running out of ammo. If this + ** value is zero, then firing is not allowed. If -1, then there is no ammunition + ** limit. + */ + int Ammo; + + /* + ** Used by the tesla to handle electric zap + */ + int ElectricZapDelay; + COORDINATE ElectricZapTarget; + int ElectricZapWhich; + + /* + ** This is the amount of money spent to produce this object. This value really + ** only comes into play for the case of buildings that have special "free" + ** objects available when purchased at the more expensive rate. + */ + int PurchasePrice; + + /* + ** Per-player view of whether a techno object is discovered. One bit for each house type. ST - 3/6/2019 11:15AM + */ + unsigned int IsDiscoveredByPlayerMask; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TechnoClass(RTTIType rtti, int id, HousesType house=HOUSE_NONE); +#ifdef FIXIT_MULTI_SAVE + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IdleTimer(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#else + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#endif + virtual ~TechnoClass(void) {House=0;}; + + /* + ** Query functions. + */ + bool Is_Allowed_To_Retaliate(TechnoClass const * source) const; + bool Can_Teleport_Here(CELL cell) const; + bool Is_In_Same_Zone(CELL cell) const; + virtual bool Is_Players_Army(void) const; + int Combat_Damage(int which=-1) const; + bool Is_Ready_To_Cloak(void) const; + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const {return(PrimaryFacing.Current());} + CELL Nearby_Location(TechnoClass const * from=NULL) const; + TechnoTypeClass * Techno_Type_Class(void) const {return((TechnoTypeClass *)&Class_Of());}; + bool Is_Visible_On_Radar(void) const; + int Anti_Air(void) const; + int Anti_Armor(void) const; + int Anti_Infantry(void) const; + int Time_To_Build(void) const; + int What_Weapon_Should_I_Use(TARGET target) const; + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual BuildingClass * Find_Docking_Bay(StructType b, bool friendly) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual DirType Desired_Load_Dir(ObjectClass * , CELL & moveto) const; + virtual DirType Fire_Direction(void) const; + virtual HousesType Owner(void) const; + virtual InfantryType Crew_Type(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + virtual bool Is_Allowed_To_Recloak(void) const; + virtual bool Can_Repair(void) const; + virtual bool Is_Weapon_Equipped(void) const; + virtual fixed Tiberium_Load(void) const; + virtual int Pip_Count(void) const; + virtual int Rearm_Delay(bool second=true, int which=0) const; + virtual int Refund_Amount(void) const; + virtual int Risk(void) const; + virtual int Threat_Range(int control) const; + virtual int Value(void) const; + virtual int Get_Ownable(void) const; + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType house, int count = 7); // 2019/09/20 JAS - Added record of who clicked on the object + virtual bool Select(bool allow_mixed = false); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Player_Assign_Mission(MissionType order, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + + /* + ** Combat related. + */ + fixed Area_Modify(CELL cell) const; + virtual int Made_A_Kill(void) {return(Crew.Made_A_Kill());} + void Base_Is_Attacked(TechnoClass const *enemy); + void Kill_Cargo(TechnoClass * source); + virtual void Record_The_Kill(TechnoClass * source); + virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual void Stun(void); + virtual bool In_Range(COORDINATE coord, int which=0) const; + virtual bool In_Range(TARGET target, int which=0, bool reciprocal_check = true) const; + virtual bool In_Range(ObjectClass const * target, int which=0, bool reciprocal_check = true) const; + virtual void Death_Announcement(TechnoClass const * source=0) const = 0; + virtual FireErrorType Can_Fire(TARGET target, int which=0) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual void Assign_Target(TARGET target); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + virtual BulletClass * Fire_At(TARGET target, int which=0); + virtual int Weapon_Range(int which) const; + virtual bool Captured(HouseClass * newowner); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value, int zone=0) const; + bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone=-1) const; + int Evaluate_Just_Cell(CELL cell) const; + virtual bool Electric_Zap (COORDINATE target_coord, int which, WindowNumberType window, COORDINATE source_coord=0L, unsigned char * remap=NULL) const; + + /* + ** AI. + */ + virtual void Renovate(void); + virtual void AI(void); + virtual bool Revealed(HouseClass * house); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + void Cloaking_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void) const; + VisualType Visual_Character(bool raw = false) const; + void Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation=DIR_N, int scale=0x0100) const; + + // Added. ST - 8/1/2019 5:37PM + void Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation=DIR_N, int scale=0x0100, const char *shape_name = NULL) const; + + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Draw_Pips(int x, int y, WindowNumberType window) const; + virtual void Hidden(void); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual int Exit_Object(TechnoClass *); + virtual void Do_Uncloak(void); + virtual void Do_Cloak(void); + virtual void Do_Shimmer(void); + + /* + ** Movement and animation. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + virtual bool Random_Animate(void) {return(false);} + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Look(bool incremental=false); + + /* + ** Map entry and exit logic. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual void Detach(TARGET target, bool all); + + /* + ** New functions for per-player discovery for multiplayer. ST - 3/6/2019 11:17AM + */ + bool Is_Discovered_By_Player(HouseClass *player = NULL) const; + void Set_Discovered_By_Player(HouseClass *player = NULL); + void Clear_Discovered_By_Players(); + bool Is_Owned_By_Player(HouseClass *player = NULL) const; + + virtual unsigned Spied_By() const; + + /* + ** Facing translation tables that fix the flaw with 3D studio when + ** it renders 45 degree angles. + */ + static int const BodyShape[32]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/TEMPLATE.CPP b/REDALERT/TEMPLATE.CPP new file mode 100644 index 000000000..83777ae3f --- /dev/null +++ b/REDALERT/TEMPLATE.CPP @@ -0,0 +1,238 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEMPLATE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateClass::Init -- Resets the template object system. * + * TemplateClass::Mark -- Lifts or drops a template object. * + * TemplateClass::Select -- Select the template object. * + * TemplateClass::TemplateClass -- Template object constructor. * + * TemplateClass::Unlimbo -- Places a template object into the game/map system. * + * TemplateClass::delete -- Returns a template object to the pool. * + * TemplateClass::new -- Allocates a template object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "template.h" + + +/*********************************************************************************************** + * TemplateClass::Init -- Resets the template object system. * + * * + * This routine resets the template object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Init(void) +{ + Templates.Free_All(); +} + + +/*********************************************************************************************** + * TemplateClass::Mark -- Lifts or drops a template object. * + * * + * This routine handles placing or removing a template object. This * + * entails marking the map as appropriate and redisplaying affected * + * cells. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the template successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + * 12/23/1994 JLB : Examines low level legality before processing. * + *=============================================================================================*/ +bool TemplateClass::Mark(MarkType mark) +{ + assert(Templates.ID(this) == ID); + assert(IsActive); + + static bool noup = false; + void const * iset = Get_Image_Data(); + if (iset && ObjectClass::Mark(mark)) { + + void * map = Get_Icon_Set_Map(iset); + + for (int y = 0; y < Class->Height; y++) { + for (int x = 0; x < Class->Width; x++) { + CELL cell = Coord_Cell(Coord) + y*MAP_CELL_W + x; + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + int number = y*Class->Width + x; + + /* + ** Determine if this logical icon actually maps to a real icon. If no real + ** icon is associated with this logical position, then don't do any action + ** since none is required. + */ + char * mapptr = (char*)map; + bool real = (mapptr[number] != -1); + + if (real) { + /* + ** Lift the terrain object from the map. + */ + if (mark == MARK_UP && !noup) { + if (cellptr->TType == Class->Type && cellptr->TIcon == number) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } + } + + /* + ** Place the terrain object down. + */ + if (mark == MARK_DOWN) { + if (*this == TEMPLATE_CLEAR1) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } else { + cellptr->TType = Class->Type; + cellptr->TIcon = number; + } + + /* + ** Make sure that no overlays or smudges exist after + ** placing the template down. + */ + cellptr->Smudge = SMUDGE_NONE; + cellptr->SmudgeData = 0; + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + } + + cellptr->Redraw_Objects(); + cellptr->Recalc_Attributes(); + } + } + } + } + + /* + ** When marking this template down onto the map, the map template numbers are update + ** but the template is removed from existence. Make sure that the deletion of the + ** template object doesn't also lift the template numbers up from the map. + */ + if (mark == MARK_DOWN) { + noup = true; + delete this; + noup = false; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateClass::new -- Allocates a template object from pool * + * * + * This routine is used to allocate a template object from the * + * template object pool. * + * * + * INPUT: size -- The size of a template object (not used). * + * * + * OUTPUT: Returns with a pointer to an available template object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * TemplateClass::operator new(size_t ) +{ + void * ptr = Templates.Allocate(); + if (ptr) { + ((TemplateClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TemplateClass::delete -- Returns a template object to the pool. * + * * + * This routine will return a template object to the template object * + * pool. A template so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::operator delete(void * ptr) +{ + if (ptr) { + ((TemplateClass *)ptr)->IsActive = false; + } + Templates.Free((TemplateClass *)ptr); +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Template object constructor. * + * * + * This is the constructor for a template object. * + * * + * INPUT: type -- The template object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(TemplateType type, CELL pos) : + ObjectClass(RTTI_TEMPLATE, Templates.ID(this)), + Class(TemplateTypes.Ptr((int)type)) +{ + if (pos != -1) { + Unlimbo(Cell_Coord(pos)); + } +} diff --git a/REDALERT/TEMPLATE.H b/REDALERT/TEMPLATE.H new file mode 100644 index 000000000..710e6a561 --- /dev/null +++ b/REDALERT/TEMPLATE.H @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEMPLATE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This class controls the template object. Template objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class TemplateClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TemplateClass(TemplateType type, CELL pos=-1); + TemplateClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~TemplateClass(void) {if (GameActive) TemplateClass::Limbo();Class=0;}; + operator TemplateType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + int Icon_Number(CELL cell); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int , int , WindowNumberType ) const {}; + virtual bool Mark(MarkType mark); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/TENMGR.CPP b/REDALERT/TENMGR.CPP new file mode 100644 index 000000000..b30787ece --- /dev/null +++ b/REDALERT/TENMGR.CPP @@ -0,0 +1,1118 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : July 22, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TenConnManClass::TenConnManClass -- Class constructor * + * TenConnManClass::~TenConnManClass -- Class destructor * + * TenConnManClass::Init -- Inits TEN * + * TenConnManClass::Service -- Service routine * + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * TenConnManClass::Get_Global_Message -- Gets next global message * + * TenConnManClass::Num_Connections -- Reports # connections * + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * TenConnManClass::Connection_Index -- Gets a connection's index * + * TenConnManClass::Create_Connection -- Creates a new connection * + * TenConnManClass::Delete_Connection -- Deletes a connection * + * TenConnManClass::Connection_Name -- Reports a connection's name * + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Flush_All -- Flushes all packets * + * TenConnManClass::Reset_Response_Time -- Does nothing * + * TenConnManClass::Response_Time -- Reports response time * + * TenConnManClass::Set_Timing -- Does nothing * + * TenConnManClass::Configure_Debug -- Does nothing * + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * terminateApp -- Callback: app terminates on error * + * debugMessage -- outputs debug message * + * doAlert -- Outputs debug message * + * doPregameHook -- Callback: game is starting * + * doIncomingPacket -- Callback: packet has arrived * + * doPlayerJoins -- Callback: player joins * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WIN32 +#define WINDOWS +#endif + +#if(TEN) +#include "ten.h" + +//************************************************************************** +// Constants +// +const unsigned char kGlobalChannelFlag = 0x80; +const int kMaxPlayers = 8; + +//************************************************************************** +// Functions +// +static void terminateApp(void); +static void debugMessage(int msgLevel, char *msg); +static void doAlert(int, int, char *); +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *); +void doIncomingPacket(int addr, void *buf, size_t size); +void doPlayerEntered(int pid, int isYou, char *, char *, char *, long , char *); + + +//************************************************************************** +// Globals +// +static int IgnoreIncoming = 0; + +/*************************************************************************** + * TenConnManClass::TenConnManClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::TenConnManClass(void) +{ + int i; + + IsHost = 0; + + GlobalQueue = new CommBufferClass(1, 50, sizeof(GlobalPacketType), 4); + PrivateQueue = new CommBufferClass(1, 50, Session.TenSize, 4); + + NumConnections = 0; + for (i = 0; i < MAX_PLAYERS; i++) { + Connections[i] = 0; + ID[i] = 0; + Names[i][0] = 0; + } + +} // end of TenConnManClass + + +/*************************************************************************** + * TenConnManClass::~TenConnManClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::~TenConnManClass() +{ + tenArExitArena(); + + delete GlobalQueue; + delete PrivateQueue; + +} // end of ~TenConnManClass + + +/*************************************************************************** + * TenConnManClass::Init -- Inits TEN * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Init(void) +{ + // + // set the debugging functions + // + setExitRoutine(terminateApp); + setDebugMsgRoutine(debugMessage); + + // + // callback function addresses + // + tenArSetAlertMessageRoutine(doAlert); + + tenArSetPregameHookRoutine(doPregameHook); + + tenArSetIncomingPacketRoutine(doIncomingPacket); + + tenArSetPlayerEnteredRoutine(doPlayerEntered); + + verifyNoErr(tenArInitArena("redalt")); + + return (1); + +} // end of Init + + +/*************************************************************************** + * TenConnManClass::Service -- Service routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Service(void) +{ + tenArIdleArena(); + + return (1); + +} // Service + + +/*************************************************************************** + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * * + * If Connection ID is -1, the packet is multicast; otherwise, it's sent * + * to only the specified connection. * + * * + * Private & Global messages are sent via the same mechanism. The only * + * way to tell the difference between them is that the Global Channel * + * packets have the 'kGlobalChannelFlag' bit set in the 1st byte (the * + * "Type" field for EventClass's, and the NetCommand field for Global * + * packets). * + * * + * INPUT: * + * buf packet to send * + * buflen size of packet * + * reliable 1 = must be delivered reliably * + * conn_id connection ID to send to, -1 = all * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Private_Message(void *buf, int buflen, + int reliable, int conn_id) +{ + int doBroadcast = conn_id == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = Connection_Address(conn_id); + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + return (1); + +} // end of Send_Private_Message + + +/*************************************************************************** + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * * + * Retrieves the next-available "private" message, if there is one. * + * * + * INPUT: * + * buf packet retrieved * + * buflen length of packet * + * conn_id ptr to store sender's connection ID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + int addr; + int addrlen; + int i; + + if (PrivateQueue->Num_Receive() > 0) { + PrivateQueue->UnQueue_Receive(buf, buflen, 0, &addr, &addrlen); + (void)verify(addrlen == 4); + (*conn_id) = CONNECTION_NONE; + for (i = 0; i < NumConnections; i++) { + if (addr == Connections[i]) { + (*conn_id) = ID[i]; + return (1); + } + } + } + + return (0); + +} // end of Get_Private_Message + + +/*************************************************************************** + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * * + * INPUT: * + * buf packet to send * + * buflen length of packet * + * reliable 1 = send reliably * + * address address to send to; -1 = broadcast it (for TEN, this * + * means send to all connected players). * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Global_Message(void *buf, int buflen, + int reliable, int address) +{ + int doBroadcast = address == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + // + // Set the global channel flag for this packet + // + ucbuf[0] |= kGlobalChannelFlag; + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = address; + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + + // + // The caller may re-use this buffer, so clear the global channel flag. + // + ucbuf[0] &= ~kGlobalChannelFlag; + + return (1); + +} // end of Send_Global_Message + + +/*************************************************************************** + * TenConnManClass::Get_Global_Message -- Gets next global message * + * * + * INPUT: * + * buf buffer to store packet in * + * buflen ptr filled in with packet length * + * address ptr filled in with address (Player ID) of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Global_Message(void *buf, int *buflen, + int *address) +{ + int addrlen; + + if (GlobalQueue->Num_Receive() > 0) { + GlobalQueue->UnQueue_Receive(buf, buflen, 0, address, &addrlen); + (void)verify(addrlen == 4); + return (1); + } + + return (0); + +} // end of Get_Global_Message + + +/*************************************************************************** + * TenConnManClass::Num_Connections -- Reports # connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # connections * + * * + * WARNINGS: * + * * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Num_Connections(void) +{ + return (NumConnections); + +} // end of Num_Connections + + +/*************************************************************************** + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * * + * INPUT: * + * index index of connection to report * + * * + * OUTPUT: * + * connection ID for this connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(ID[index]); + } else { + return(CONNECTION_NONE); + } + +} // end of Connection_ID + + +/*************************************************************************** + * TenConnManClass::Connection_Index -- Gets a connection's index * + * * + * INPUT: * + * id Connection ID to find index for * + * * + * OUTPUT: * + * index for that connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i] == id) { + return (i); + } + } + + return(CONNECTION_NONE); + +} // end of Connection_Index + + +/*************************************************************************** + * TenConnManClass::Create_Connection -- Creates a new connection * + * * + * INPUT: * + * id ID of connection * + * name name of connection * + * address TEN address (player ID) to give this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Create_Connection(int id, char *name, int address) +{ + Connections[NumConnections] = address; + ID[NumConnections] = id; + strcpy(Names[NumConnections], name); + NumConnections++; + + return (1); + +} // end of Create_Connection + + +/*************************************************************************** + * TenConnManClass::Delete_Connection -- Deletes a connection * + * * + * INPUT: * + * id ID for connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == CONNECTION_NONE) { + return 0; + } + + for (i = idx; i < NumConnections - 1; i++) { + Connections[i] = Connections[i+1]; + ID[i] = ID[i + 1]; + strcpy (Names[i], Names[i + 1]); + } + + NumConnections--; + + return (1); + +} // end of Delete_Connection + + +/*************************************************************************** + * TenConnManClass::Connection_Name -- Reports a connection's name * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +char * TenConnManClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Names[i]); + } + } + + return(NULL); + +} // end of Connection_Name + + +/*************************************************************************** + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection "address" (TEN player ID) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Connections[i]); + } + } + + return(NULL); + +} // end of Connection_Address + + +/*************************************************************************** + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Send(void) +{ + return(0); + +} // end of Global_Num_Send + +/*************************************************************************** + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Receive(void) +{ + return (GlobalQueue->Num_Receive()); + +} // end of Global_Num_Receive + +/*************************************************************************** + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Send(int /*id*/) +{ + return(0); + +} // end of Private_Num_Send + +/*************************************************************************** + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Receive(int /*id*/) +{ + return (PrivateQueue->Num_Receive()); + +} // end of Private_Num_Receive + + +/*************************************************************************** + * TenConnManClass::Flush_All -- Flushes all packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Flush_All(void) +{ + int i; + int maxqueuesize; + int rc; + + // + // Set the max # of packets that Ten will send me during any given + // call to tenArIdleArena() to slightly smaller than my max queue + // size. + // + maxqueuesize = 45; + rc = tenArSetOption(kTenArOptReadQueueSize, &maxqueuesize, + sizeof(maxqueuesize)); + + verifyNoErr(rc); + + + // + // Set the flag to tell the doIncomingPacket routine to ignore packets. + // (doIncomingPacket() is called by tenArIdleArena().) + // + IgnoreIncoming = 1; + + while (i++ < 1000) { + tenArIdleArena(); + if (GlobalQueue->Num_Receive() == 0 && + PrivateQueue->Num_Receive() == 0) { + break; + } + GlobalQueue->Init(); + PrivateQueue->Init(); + } + + IgnoreIncoming = 0; + +} // end of Flush_All + + +/*************************************************************************** + * TenConnManClass::Reset_Response_Time -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Reset_Response_Time(void) +{ + // + // (This function intentionally left blank.) + // + +} // end of Reset_Response_Time + + +/*************************************************************************** + * TenConnManClass::Response_Time -- Reports response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * worst-case connection response time (round-trip) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +unsigned long TenConnManClass::Response_Time(void) +{ + return((Session.NetResponseTime * 60) / 1000); // 300 milliseconds one way + +} // end of Response_Time + + +/*************************************************************************** + * TenConnManClass::Set_Timing -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, unsigned long /*timeout*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Set_Timing + + +/*************************************************************************** + * TenConnManClass::Configure_Debug -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char **/*names*/, int /*namestart*/, int /*namecount*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Configure_Debug + +/*************************************************************************** + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Mono_Debug_Print + +/*************************************************************************** + * terminateApp -- Callback: app terminates on error * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void terminateApp(void) +{ + Prog_End(); + dprintf("Exiting due to a fatal error.\n"); + exit(0); + +} // end of terminateApp + + +/*************************************************************************** + * debugMessage -- outputs debug message * + * * + * INPUT: * + * msgLevel not used * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void debugMessage(int /*msgLevel*/, char *msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tendebug.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of debugMessage + + +/*************************************************************************** + * doAlert -- Outputs debug message * + * * + * INPUT: * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doAlert(int, int, char * msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tenalert.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of doAlert + + +/*************************************************************************** + * doPregameHook -- Callback: game is starting * + * * + * INPUT: * + * joinType if "create", we're the game host * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *) +{ + char typeToken[16]; + + sscanf(joinType, "%s", typeToken); + if (!strcmp(typeToken, "create")) { + Ten->IsHost = 1; + } + + if (Ten->IsHost) { + verifyNoErr(tenArReturnGameOptions("")); + } + + verifyNoErr(tenArReturnPlayerOptions("")); + +} // end of doPregameHook + + +/*************************************************************************** + * doIncomingPacket -- Callback: packet has arrived * + * * + * INPUT: * + * addr player ID of sender * + * buf buffer containing incoming packet * + * size size of incoming packet * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doIncomingPacket(int addr, void *buf, size_t size) +{ + int rc; + unsigned char *byte; + + // + // Check to see if this packet belongs to the Global Channel or not + // + byte = (unsigned char *)buf; + + // + // If the global channel flag is set in this packet, queue it onto the + // Global Channel queue. Ignore any errors if "IgnoreIncoming" is set, + // which means we're in the process of flushing the queues. + // + if (byte[0] & kGlobalChannelFlag) { + byte[0] &= (~kGlobalChannelFlag); + rc = Ten->GlobalQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } else { + rc = Ten->PrivateQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } + +} // end of doIncomingPacket + + +/*************************************************************************** + * doPlayerJoins -- Callback: player joins * + * * + * INPUT: * + * pid player ID of the player joining * + * isYou true = this is you * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doPlayerEntered(int pid, int isYou, char * /*options*/, + char */*termOptions*/, char */*address*/, long /*uniqueId*/, + char */*joinType*/) +{ + if (isYou) { + Session.TenPlayerID = pid; + } + +} // end of doPlayerJoins + + +#endif //TEN +/************************** end of tenmgr.cpp ******************************/ diff --git a/REDALERT/TENMGR.H b/REDALERT/TENMGR.H new file mode 100644 index 000000000..f5a9c84ae --- /dev/null +++ b/REDALERT/TENMGR.H @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : June 26, 1996 [BRR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class TenConnManClass : public ConnManClass +{ + public: + // + // Constructor / Destructor + // + TenConnManClass(void); + virtual ~TenConnManClass(); + + // + // Initialization + // + int Init(void); + + // + // Service routine + // + virtual int Service(void); + + // + // Sending & receiving data + // + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id); + + int Send_Global_Message(void *buf, int buflen, + int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, + int *address = 0); + + // + // Connection management + // + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + virtual int Connection_Index(int id); + + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // + // Queue utility routines + // + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + void Flush_All(void); + + // + // Timing management + // + virtual void Reset_Response_Time(void); + virtual unsigned long Response_Time(void); + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + // + // Debugging + // + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh); + + CommBufferClass *GlobalQueue; + CommBufferClass *PrivateQueue; + + // + // This flag will be set if I'm the game host. + // + int IsHost; + + private: + int Connections[MAX_PLAYERS]; + int ID[MAX_PLAYERS]; + char Names[MAX_PLAYERS][128]; + int NumConnections; + +}; + +/***************************** end of tenmgr.h *****************************/ diff --git a/REDALERT/TERRAIN.CPP b/REDALERT/TERRAIN.CPP new file mode 100644 index 000000000..f166f9642 --- /dev/null +++ b/REDALERT/TERRAIN.CPP @@ -0,0 +1,789 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TERRAIN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainClass::AI -- Process the terrain object AI. * + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * TerrainClass::Heath_Ratio -- Determines the health ratio for the terrain object. * + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * TerrainClass::Mark -- Marks the terrain object on the map. * + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * TerrainClass::TerrainClass -- This is the constructor for a terrain object. * + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * TerrainClass::delete -- Deletes a terrain object. * + * TerrainClass::new -- Creates a new terrain object. * + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" + + +/*********************************************************************************************** + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * * + * This is the default destructor for terrain objects. It will remove the object from the * + * map and tracking systems, but only if the game is running. Otherwise, it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::~TerrainClass(void) +{ + if (GameActive && Class) { + TerrainClass::Limbo(); + } +} + + +/*********************************************************************************************** + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * * + * This routine is called when damage is to be inflicted upon the terrain object. It is * + * through this routine that terrain objects are attacked and thereby destroyed. Not all * + * terrain objects can be damaged by this routine however. * + * * + * INPUT: damage -- The damage points to inflict (raw). * + * * + * warhead -- The warhead type the indicates the kind of damage. This is used to * + * determine if the terrain object is damaged and if so, by how much. * + * * + * OUTPUT: bool; Was the terrain object destroyed by this damage? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 12/11/1994 JLB : Shortens attached burning animations. * + *=============================================================================================*/ +ResultType TerrainClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Small arms can never destroy a terrain element. + */ + if ((!IsOnFire || warhead == WARHEAD_FIRE) && warhead != WARHEAD_SA && !Class->IsImmune) { + + res = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + + if (damage && warhead == WARHEAD_FIRE) { + Catch_Fire(); + } + + /* + ** If the terrain object is destroyed by this damage, then only remove it if it + ** currently isn't on fire and isn't in the process of crumbling. + */ + if (res == RESULT_DESTROYED) { + + /* + ** Remove this terrain object from the targeting computers of all other + ** game objects. No use beating a dead horse. + */ + Detach_All(); + + if (IsOnFire) { + + /* + ** Attached flame animation should be shortened as much as possible so that + ** crumbling can begin soon. + */ + Shorten_Attached_Anims(this); + } else { + Start_To_Crumble(); + } + } + } + return(res); +} + + +/*********************************************************************************************** + * TerrainClass::new -- Creates a new terrain object. * + * * + * This routine creates a new terrain object by grabbing a free slot * + * out of the terrain object pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void * TerrainClass::operator new(size_t) +{ + void * ptr = Terrains.Allocate(); + if (ptr) { + ((TerrainClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TerrainClass::delete -- Deletes a terrain object. * + * * + * This routine deletes a terrain object by returning it to the * + * terrain object pool. * + * * + * INPUT: ptr -- Pointer to the terrain object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::operator delete(void * ptr) +{ + if (ptr) { + ((TerrainClass *)ptr)->IsActive = false; + } + Terrains.Free((TerrainClass *)ptr); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- This is the constructor for a terrain object * + * * + * This constructor for a terrain object will initialize the terrain * + * object with it's proper type and insert it into the access * + * tracking system. * + * * + * INPUT: type -- The terrain object type. * + * * + * cell -- The location of the terrain object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(TerrainType type, CELL cell) : + ObjectClass(RTTI_TERRAIN, Terrains.ID(this)), + Class(TerrainTypes.Ptr((int)type)), + IsOnFire(false), + IsCrumbling(false) +{ + Strength = Class->MaxStrength; + if (cell != -1) { + if (!Unlimbo(Cell_Coord(cell))) { + delete this; + } + } + Set_Rate(0); // turn off animation +} + + +/*********************************************************************************************** + * TerrainClass::Mark -- Marks the terrain object on the map. * + * * + * This routine will mark or remove the terrain object from the map * + * tracking system. This is typically called when the terrain object * + * is first created, when it is destroyed, and whenever it needs to be * + * redrawn. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the terrain object successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 12/23/1994 JLB : Performs low level legality check before proceeding. * + *=============================================================================================*/ +bool TerrainClass::Mark(MarkType mark) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + CELL cell = Coord_Cell(Coord); + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * * + * This routine is used to render the terrain object at the location specified and * + * clipped to the window specified. This is the gruntwork drawing routine for the * + * terrain objects as they are displayed on the map. * + * * + * INPUT: x,y -- The coordinate to draw the terrain object at (centered). * + * * + * window -- The clipping window to draw to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + * 11/09/1994 JLB : Changed selected terrain highlight method. * + *=============================================================================================*/ +void TerrainClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + void const * shapedata; + + shapedata = Get_Image_Data(); + if (shapedata) { + int shapenum = 0; + + /* + ** Determine the animation stage to render the terrain object. If it is crumbling, then + ** it will display the crumbling animation. + */ + if (IsCrumbling) { + shapenum = Fetch_Stage()+IsCrumbling; + } else { + if (Strength < 2) { + shapenum++; + } + } + + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Is_Selected_By_Player() && Debug_Map) flags = flags | SHAPE_FADING; + + /* + **Terrain is always theater specific so flag it as such for Build_Frame + */ + IsTheaterShape = true; + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapedata, shapenum, x, y, window, flags|SHAPE_WIN_REL|SHAPE_GHOST, Map.FadingLight, Map.UnitShadow); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * * + * This routine will clear out the terrain object system so that no terrain objects will * + * exists. It is called prior to loading or starting a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Init(void) +{ + Terrains.Free_All(); +} + + +/*********************************************************************************************** + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * * + * This routine will examine the cell specified and determine if the the terrain object * + * can legally exist there. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: If the terrain object can be placed in the cell specified, then a value less than * + * 256 will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 01/01/1995 JLB : Actually works now. * + *=============================================================================================*/ +MoveType TerrainClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + short const * offset; // Pointer to cell offset list. + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (Class->IsWaterBased) { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build(SPEED_FLOAT)) { + return(MOVE_NO); + } + } else { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build()) { + return(MOVE_NO); + } + } + } + return(MOVE_OK); +} + + +/*********************************************************************************************** + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * * + * This routine is called if the terrain object is supposed to catch on fire. The routine * + * performs checking to make sure that only flammable terrain objects that aren't already * + * on fire get caught on fire. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object caught on fire by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 12/11/1994 JLB : Don't catch fire if already on fire or crumbling. * + *=============================================================================================*/ +bool TerrainClass::Catch_Fire(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling && !IsOnFire && Class->Armor == ARMOR_WOOD) { + AnimClass * anim = new AnimClass(ANIM_BURN_BIG, Coord_Add(Sort_Y(), 0xFFB00000L)); + if (anim) { + anim->Attach_To(this); + } + anim = new AnimClass(ANIM_BURN_MED, Coord_Add(Sort_Y(), 0xFF200000L), 15); + if (anim) { + anim->Attach_To(this); + } + IsOnFire = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * * + * When the fire has gone out on a burning terrain object, this routine is called. The * + * animation has already been terminated prior to calling this routine. All this routine * + * needs to perform is any necessary local flag updating. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Fire_Out(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (IsOnFire) { + IsOnFire = false; + if (!IsCrumbling && !Strength) { + Detach_All(); + Mark(); + Start_To_Crumble(); + new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, Class->CenterBase)); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::AI -- Process the terrain object AI. * + * * + * This is used to handle any AI processing necessary for terrain objects. This might * + * include animation effects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 09/28/1994 JLB : Crumbling animation. * + * 08/12/1996 JLB : Reset map zone when terrain object destroyed. * + * 10/04/1996 JLB : Growth speed regulated by rules. * + *=============================================================================================*/ +void TerrainClass::AI(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::AI(); + + if ((*this == TERRAIN_MINE) && (Frame % (Rule.GrowthRate * TICKS_PER_MINUTE)) == 0) { + Map[::As_Cell(As_Target())].Spread_Tiberium(true); + } + if (StageClass::Graphic_Logic()) { + Mark(); + + /* + ** If the terrain object is in the process of crumbling, then when at the + ** last stage of the crumbling animation, delete the terrain object. + */ + if (IsCrumbling && Fetch_Stage() == Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + delete this; + + Map.Zone_Reset(MZONEF_NORMAL|MZONEF_CRUSHER|MZONEF_DESTROYER); + } + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * * + * This debugging support routine is used to display the status of the terrain object to * + * the debug screen. * + * * + * INPUT: mono -- The mono screen to display the status to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Debug_Dump(MonoClass * mono) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * * + * This routine is used to unlimbo the terrain object onto a location on the map. Normal * + * unlimbo procedures are sufficient except that the coordinate location of a terrain * + * object is based on the upper left corner of a cell rather than the center. Mask the * + * coordinate value so that it snaps to the upper left corner and then proceed with a * + * normal unlimbo process. * + * * + * INPUT: coord -- The coordinate to mark as the terrain's location. * + * * + * dir -- unused * + * * + * OUTPUT: bool; Was the terrain object successful in the unlimbo process? Failure could be * + * the result of illegal positioning. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + * 11/16/1994 JLB : Checks for theater legality. * + *=============================================================================================*/ +bool TerrainClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (Class->Theater & (1 << Scen.Theater)) { + return(ObjectClass::Unlimbo(coord, dir)); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * * + * This routine is used to start the crumbling process for terrain object. This only * + * applies to trees. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Start_To_Crumble(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling) { + IsCrumbling = true; + Set_Rate(2); + Set_Stage(0); + } +} + + +/*********************************************************************************************** + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * * + * This routine (called as a part of the limbo process) will remove the terrain occupation * + * flag in the cell it occupies. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Limbo(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + CELL cell = Coord_Cell(Coord); + Map[cell].Flag.Occupy.Monolith = false; + } + return(ObjectClass::Limbo()); +} + + +/*********************************************************************************************** + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * * + * Use this routine to fetch the center point terrain * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Center_Coord(void) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, Class->CenterBase)); +} + + +/*********************************************************************************************** + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * * + * This routine will return with a pointer to the radar icon to use for the cell number * + * specified. * + * * + * INPUT: cell -- The cell number to use when determine what icon pointer to return. * + * * + * OUTPUT: Returns with a pointer to the 9 pixel values that make up the icon of this * + * terrain object located at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char * TerrainClass::Radar_Icon(CELL cell) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + unsigned char * icon = (unsigned char *)Class->Get_Radar_Data(); // get a pointer to radar icons + int width = *icon++; // extract the width from data + int height = *icon++; // extract the width from data + + /* + ** Icon number that we need can be found by converting the cell and base + ** cell to and x and y offset from the upper left of the cell, and then + ** multiplying it by the width of the terrain in icons, which we + ** conveniently stored out as the first byte of every icon we made. + */ + int basecell = Coord_Cell(Coord); // find the base cell of terrain + int ydiff = Cell_Y(cell) - Cell_Y(basecell); + int xdiff = Cell_X(cell) - Cell_X(basecell); + if (xdiff < width && ydiff < height) { + int iconnum = (ydiff * width) + xdiff; + return(icon + (iconnum * 9)); + } + return(NULL); +} + + +/*********************************************************************************************** + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * * + * This routine will return with the coordinate to use if this terrain object were to be * + * fired upon and the coordinate where the bullets should hit is needed. Usually, this * + * location is the base of the object (e.g., the trunk of a tree). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing at this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Target_Coord(void) const +{ + return(Coord_Add(XY_Coord(0, -Height), Sort_Y())); +} + + +/*********************************************************************************************** + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * * + * This routine reads a scenario control INI file and creates all * + * terrain objects specified therein. Objects so created are placed * + * upon the map. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Read_INI(CCINIClass & ini) +{ + TerrainClass * tptr; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + TerrainType terrain = ini.Get_TerrainType(INI_Name(), entry, TERRAIN_NONE); + CELL cell = atoi(entry); + + if (terrain != TERRAIN_NONE) { + tptr = new TerrainClass(terrain, cell); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * * + * This routine will clear out any old terrain data from the INI database and then * + * fill it in with all the data from the terrain objects that currently exists. * + * * + * INPUT: ini -- Reference to the INI database to store the terrain objects in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing terrain data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the terrain data out. + */ + for (int index = 0; index < Terrains.Count(); index++) { + TerrainClass * terrain; + + terrain = Terrains.Ptr(index); + if (terrain != NULL && !terrain->IsInLimbo && terrain->IsActive) { + char uname[10]; + sprintf(uname, "%d", Coord_Cell(terrain->Coord)); + ini.Put_TerrainType(INI_Name(), uname, *terrain); + } + } +} \ No newline at end of file diff --git a/REDALERT/TERRAIN.H b/REDALERT/TERRAIN.H new file mode 100644 index 000000000..7744b1897 --- /dev/null +++ b/REDALERT/TERRAIN.H @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TERRAIN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "object.h" +#include "type.h" + + +/**************************************************************************** +** Each type of terrain has certain pieces of static information associated +** with it. This class elaborates this data. +*/ +class TerrainClass : public ObjectClass, public StageClass +{ + public: + /* + ** This points to the constant terrain data (for this type) that gives this + ** terrain object its character. + */ + CCPtr Class; + + /* + ** Constructor for terrain object class. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TerrainClass(TerrainType id, CELL cell); + TerrainClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~TerrainClass(void); + operator TerrainType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Terrain specific support functions. + */ + void Start_To_Crumble(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const {return Coord;}; + virtual COORDINATE Sort_Y(void) const {return Coord_Add(Coord, Class->CenterBase);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType dir=DIR_N); + virtual bool Limbo(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing = FACING_NONE) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + unsigned char *Radar_Icon(CELL cell); + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType house, int) {}; // 2019/09/20 JAS - Added record of who clicked on the object + + /* + ** Combat related. + */ + virtual void Fire_Out(void); + virtual bool Catch_Fire(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + + /* + ** AI. + */ + virtual void AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "TERRAIN";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + private: + + /* + ** If this terrain object is on fire, then this flag will be true. + */ + unsigned IsOnFire:1; + + /* + ** Is this a terrain object that undergoes crumbling animation and it is + ** in fact crumbling at this time? + */ + unsigned IsCrumbling:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/REDALERT/TEVENT.CPP b/REDALERT/TEVENT.CPP new file mode 100644 index 000000000..449afbcd9 --- /dev/null +++ b/REDALERT/TEVENT.CPP @@ -0,0 +1,766 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEVENT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Attaches_To -- Determines what event can be attached to. * + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * Event_From_Name -- retrieves EventType for given name * + * Event_Needs -- Returns with what this event type needs for data. * + * Name_From_Event -- retrieves name for EventType * + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +/* +** This is the text name for all of the trigger events. These are used by the scenario editor +*/ +const char * EventText[TEVENT_COUNT] = { + "-No Event-", + "Entered by...", + "Spied by...", + "Thieved by...", + "Discovered by player", + "House Discovered...", + "Attacked by anybody", + "Destroyed by anybody", + "Any Event", + "Destroyed, Units, All...", + "Destroyed, Buildings, All...", + "Destroyed, All...", + "Credits exceed (x100)...", + "Elapsed Time (1/10th min)...", + "Mission Timer Expired", + "Destroyed, Buildings, #...", + "Destroyed, Units, #...", + "No Factories left", + "Civilians Evacuated", + "Build Building Type...", + "Build Unit Type...", + "Build Infantry Type...", + "Build Aircraft Type...", + "Leaves map (team)...", + "Zone Entry by...", + "Crosses Horizontal Line...", + "Crosses Vertical Line...", + "Global is set...", + "Global is clear...", + "Destroyed, Fakes, All...", + "Low Power...", + "All bridges destroyed", + "Building exists..." +}; + + +/* +** This is an ordinal list of trigger events. This list +** is used when generating the trigger dialog box. +*/ +EventChoiceClass EventChoices[TEVENT_COUNT] = { + {TEVENT_NONE}, + {TEVENT_PLAYER_ENTERED}, + {TEVENT_SPIED}, + {TEVENT_THIEVED}, + {TEVENT_DISCOVERED}, + {TEVENT_HOUSE_DISCOVERED}, + {TEVENT_ATTACKED}, + {TEVENT_DESTROYED}, + {TEVENT_ANY}, + {TEVENT_UNITS_DESTROYED}, + {TEVENT_BUILDINGS_DESTROYED}, + {TEVENT_ALL_DESTROYED}, + {TEVENT_CREDITS}, + {TEVENT_TIME}, + {TEVENT_MISSION_TIMER_EXPIRED}, + {TEVENT_NBUILDINGS_DESTROYED}, + {TEVENT_NUNITS_DESTROYED}, + {TEVENT_NOFACTORIES}, + {TEVENT_EVAC_CIVILIAN}, + {TEVENT_BUILD}, + {TEVENT_BUILD_UNIT}, + {TEVENT_BUILD_INFANTRY}, + {TEVENT_BUILD_AIRCRAFT}, + {TEVENT_LEAVES_MAP}, + {TEVENT_ENTERS_ZONE}, + {TEVENT_CROSS_HORIZONTAL}, + {TEVENT_CROSS_VERTICAL}, + {TEVENT_GLOBAL_SET}, + {TEVENT_GLOBAL_CLEAR}, + {TEVENT_FAKES_DESTROYED}, + {TEVENT_LOW_POWER}, + {TEVENT_ALL_BRIDGES_DESTROYED}, + {TEVENT_BUILDING_EXISTS}, +}; + + +/*********************************************************************************************** + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * * + * This utility routine is used by the list box display code to print the text line that * + * describes the event choice action. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void EventChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * * + * This will reset the trigger event so that it may be "sprung" again. Typically, this * + * occurs for persistent triggers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Reset(TDEventClass & td) const +{ + td.IsTripped = false; + if (Event == TEVENT_TIME) { + td.Timer = Data.Value * (TICKS_PER_MINUTE/10); + } +} + + +/*********************************************************************************************** + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * * + * This routine is called to see if the event has been satisfied. Typically, when the * + * necessary trigger events have been satisfied, then the trigger is sprung. For some * + * events, the act of calling this routine is tacit proof enough that the event has * + * occurred. For most other events, the success condition must be explicitly checked. * + * * + * INPUT: event -- The event that has occurred according to the context from which this * + * routine was called. In the case of no specific event having occurred, * + * then TEVENT_ANY will be passed in. * + * * + * house -- The house that this event is tied to. * + * * + * object-- The object that this event might apply to. For object triggering * + * events, this will point to the perpetrator object. * + * * + * forced-- If this event is forced by some outside action, this flag will be true. * + * Forcing only occurs as an explicit action from another trigger. * + * * + * OUTPUT: Was this event satisfied? A satisfied event will probably spring the trigger * + * it is attached to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +bool TEventClass::operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced) +{ + /* + ** If this trigger event has been forced, then no further checks are required. + ** Force the trigger to be tripped. + */ + if (forced) { + td.IsTripped = true; + } + + if (td.IsTripped) return(true); + + /* + ** Triggers based on the game's global environment such as time or + ** global flags are triggered only when the appropriate condition + ** is true. + */ + switch (Event) { + case TEVENT_GLOBAL_SET: + if (!Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_GLOBAL_CLEAR: + if (Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_MISSION_TIMER_EXPIRED: +// if (MissionSuspend == -1 || MissionTimer != 0) return(false); + if (!Scen.MissionTimer.Is_Active() || Scen.MissionTimer != 0) return(false); + return(true); + + case TEVENT_TIME: + if (td.Timer != 0) return(false); + return(true); + } + + /* + ** Don't trigger this event if the parameters mean nothing. Typical of + ** this would be for events related to time or other outside influences. + */ + if (Event == TEVENT_NONE) { + return(false); + } + + /* + ** If this is not the event for this trigger, just return. This is only + ** necessary to check for those trigger events that are presumed to be + ** true just by the fact that this routine is called with the appropriate + ** event identifier. + */ + if (Event == TEVENT_ATTACKED || + Event == TEVENT_DESTROYED || + Event == TEVENT_DISCOVERED || + Event == TEVENT_SPIED || + Event == TEVENT_NONE || + Event == TEVENT_CROSS_HORIZONTAL || + Event == TEVENT_CROSS_VERTICAL || + Event == TEVENT_ENTERS_ZONE || + Event == TEVENT_PLAYER_ENTERED) { + + if (event != Event && event != TEVENT_ANY) { + return(false); + } + } + + /* + ** The cell entry trigger event is only tripped when an object of the + ** matching ownership has entered the cell in question. All other + ** conditions will not trigger the event. + */ + if (Event == TEVENT_PLAYER_ENTERED || Event == TEVENT_CROSS_HORIZONTAL || Event == TEVENT_CROSS_VERTICAL || Event == TEVENT_ENTERS_ZONE) { + if (!object || object->Owner() != Data.House) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** Check for all bridges destroyed condition. + */ + if (Event == TEVENT_ALL_BRIDGES_DESTROYED) { + if (Scen.BridgeCount) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** The following trigger events are not considered to have sprung + ** merely by fact that this routine has been called. These trigger + ** events must be verified manually by examining the house that + ** they are assigned to. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + int index; + if (hptr != NULL) { + switch (Event) { + /* + ** Check to see if a team of the appropriate type has left the map. + */ + case TEVENT_LEAVES_MAP: + for (index = 0; index < Teams.Count(); index++) { + TeamClass * ptr = Teams.Ptr(index); + if (ptr->Class == Team && ptr->Is_Empty() && ptr->IsLeaveMap) { +// if (ptr->Class == Team && ptr->House == hptr && ptr->Is_Empty() && ptr->IsLeaveMap) { + td.IsTripped = true; + break; + } + } + if (index == Teams.Count()) return(false); + break; + + /* + ** Credits must be equal or greater to the value specified. + */ + case TEVENT_CREDITS: + if (hptr->Available_Money() < Data.Value) return(false); + break; + + /* + ** Ensure that there are no more factories left. + */ + case TEVENT_NOFACTORIES: + if (hptr->BScan & (STRUCTF_AIRSTRIP|STRUCTF_TENT|STRUCTF_WEAP|STRUCTF_BARRACKS|STRUCTF_CONST)) return(false); + break; + + /* + ** Ensure that there are no fake structures left. + */ + case TEVENT_FAKES_DESTROYED: + //PG_TO_FIX + //warning C4293: '<<': shift count negative or too big, undefined behavior + //Indeed, STRUCTF_FAKEWEAP and STRUCTF_FAKECONST don't fit in the BScan bit range. ST - 5/9/2019 + //if (hptr->BScan & (STRUCTF_FAKECONST|STRUCTF_FAKEWEAP)) return(false); + break; + + /* + ** A civilian must have been evacuated. + */ + case TEVENT_EVAC_CIVILIAN: + if (!hptr->IsCivEvacuated) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILDING_EXISTS: + if ((hptr->ActiveBScan & (1 << Data.Structure)) == 0) return(false); +// if (hptr->Get_Quantity(Data.Structure) == 0) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILD: + if (hptr->JustBuiltStructure != Data.Structure) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the unit has been built. + */ + case TEVENT_BUILD_UNIT: + if (hptr->JustBuiltUnit != Data.Unit) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the infantry has been built. + */ + case TEVENT_BUILD_INFANTRY: + if (hptr->JustBuiltInfantry != Data.Infantry) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the aircraft has been built. + */ + case TEVENT_BUILD_AIRCRAFT: + if (hptr->JustBuiltAircraft != Data.Aircraft) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the specified number of buildings have been destroyed. + */ + case TEVENT_NBUILDINGS_DESTROYED: + if ((int)hptr->BuildingsLost < Data.Value) return(false); + break; + + /* + ** Verify that the specified number of units have been destroyed. + */ + case TEVENT_NUNITS_DESTROYED: + if ((int)hptr->UnitsLost < Data.Value) return(false); + break; + + default: + break; + } + } + + hptr = HouseClass::As_Pointer(Data.House); + if (hptr) { + switch (Event) { + case TEVENT_LOW_POWER: + if (hptr->Power_Fraction() >= 1) return(false); + break; + +// case TEVENT_SPIED: +// if (!hptr->IsSpied) return(false); +// break; + + case TEVENT_THIEVED: + if (!hptr->IsThieved) return(false); + break; + + /* + ** Verify that the house has been discovered. + */ + case TEVENT_HOUSE_DISCOVERED: + if (!hptr->IsDiscovered) return(false); + break; + + /* + ** Verify that all buildings have been destroyed. + */ + case TEVENT_BUILDINGS_DESTROYED: + if (hptr->ActiveBScan) return(false); + break; + + /* + ** Verify that all units have been destroyed -- with some + ** exceptions. + */ + case TEVENT_UNITS_DESTROYED: + if (hptr->ActiveUScan | hptr->ActiveIScan) return(false); + break; + + /* + ** Verify that all buildings and units have been destroyed. + */ + case TEVENT_ALL_DESTROYED: + if (hptr->ActiveBScan | hptr->ActiveUScan | hptr->ActiveIScan | hptr->ActiveVScan) return(false); + break; + + default: + break; + } + } + + return(true); +} + + +/*********************************************************************************************** + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * * + * This routine will build the ini text for this trigger event. The ini text is appended * + * to the string buffer specified. This routine is used to build the complete trigger * + * ini text for writing out to the INI scenario file. * + * * + * INPUT: ptr -- Pointer to the string buffer that will hold the event INI data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d", Event, TeamTypes.Logical_ID(Team), Data.Value); +} + + +/*********************************************************************************************** + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * * + * This routine is used to parse the INI data line to fetch the event's data from it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Read_INI(void) +{ + char const * token; + switch (NewINIFormat) { + default: + Event = TEventType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + token = strtok(NULL, ","); + Event = TEVENT_NONE; + if (token) Event = TEventType(atoi(token)); + + token = strtok(NULL, ","); + Team = NULL; + Data.Value = -1; + if (token) { + if (Event_Needs(Event) == NEED_TEAM) { + Team = TeamTypes.Raw_Ptr(atoi(token)); + } else { + Data.Value = atoi(token); + } + } + break; + + case 0: + Event = TEventType(atoi(strtok(NULL, ","))); + + strtok(NULL, ","); + strtok(NULL, ","); + + Team = TeamTypeClass::From_Name(strtok(NULL, ",")); + Data.Value = atoi(strtok(NULL, ",")); + strtok(NULL, ","); + break; + } +} + + +/*********************************************************************************************** + * Event_Needs -- Returns with what this event type needs for data. * + * * + * This routine will examine the specified event type and return a code that indicates * + * the type of data that must be supplied to this event. Some events require no data and * + * these will return NEED_NONE. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the type of additional data that is needed by this event. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +NeedType Event_Needs(TEventType event) +{ + switch (event) { + case TEVENT_THIEVED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_LOW_POWER: + return(NEED_HOUSE); + + case TEVENT_NUNITS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_CREDITS: + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + return(NEED_NUMBER); + + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + return(NEED_STRUCTURE); + + case TEVENT_BUILD_UNIT: + return(NEED_UNIT); + + case TEVENT_BUILD_INFANTRY: + return(NEED_INFANTRY); + + case TEVENT_BUILD_AIRCRAFT: + return(NEED_AIRCRAFT); + + case TEVENT_LEAVES_MAP: + return(NEED_TEAM); + + default: + break; + } + return(NEED_NONE); +} + + +/*********************************************************************************************** + * Event_From_Name -- retrieves EventType for given name * + * * + * INPUT: * + * name name to get event for * + * * + * OUTPUT: * + * EventType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TEventType Event_From_Name (char const * name) +{ + if (name) { + for (TEventType i = TEVENT_NONE; i < TEVENT_COUNT; i++) { + if (!stricmp(name, EventText[i])) { + return(i); + } + } + } + + return(TEVENT_NONE); +} + + +/*********************************************************************************************** + * Name_From_Event -- retrieves name for EventType * + * * + * INPUT: * + * event EventType to get name for * + * * + * OUTPUT: * + * name for EventType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Event(TEventType event) +{ + return(EventText[event]); +} + + +/*********************************************************************************************** + * Attaches_To -- Determines what event can be attached to. * + * * + * This routine is used to determine what this event type can be attached to in the game. * + * Some events are specifically tied to cells or game object. This routine will indicate * + * this requirement. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the attachable characteristics for this event type. These * + * characteristics are represented by a composite bit field. Some events can be * + * attached to multiple objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +AttachType Attaches_To(TEventType event) +{ + AttachType attach = ATTACH_NONE; + + switch (event) { + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_PLAYER_ENTERED: + case TEVENT_ANY: + case TEVENT_DISCOVERED: + case TEVENT_NONE: + attach = attach | ATTACH_CELL; + break; + + default: + break; + } + + switch (event) { + case TEVENT_SPIED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_DISCOVERED: + case TEVENT_DESTROYED: + case TEVENT_ATTACKED: + case TEVENT_ANY: + case TEVENT_NONE: + attach = attach | ATTACH_OBJECT; + break; + + default: + break; + } + + switch (event) { +// case TEVENT_CROSS_HORIZONTAL: +// case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_ANY: + attach = attach | ATTACH_MAP; + break; + + default: + break; + } + + switch (event) { + case TEVENT_LOW_POWER: + case TEVENT_EVAC_CIVILIAN: + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + case TEVENT_BUILD_UNIT: + case TEVENT_BUILD_INFANTRY: + case TEVENT_BUILD_AIRCRAFT: + case TEVENT_NOFACTORIES: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_NUNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_CREDITS: +// case TEVENT_ATTACKED: + case TEVENT_THIEVED: + case TEVENT_ANY: + case TEVENT_FAKES_DESTROYED: + attach = attach | ATTACH_HOUSE; + break; + + default: + break; + } + + switch (event) { + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + case TEVENT_MISSION_TIMER_EXPIRED: + case TEVENT_ANY: + case TEVENT_ALL_BRIDGES_DESTROYED: + case TEVENT_LEAVES_MAP: + attach = attach | ATTACH_GENERAL; + break; + + default: + break; + } + + return(attach); +} diff --git a/REDALERT/TEVENT.H b/REDALERT/TEVENT.H new file mode 100644 index 000000000..cd0fe7d35 --- /dev/null +++ b/REDALERT/TEVENT.H @@ -0,0 +1,185 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEVENT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef TEVENT_H +#define TEVENT_H + +/* +** These are the trigger events that are checked for and if qualified, they will signal +** a successful trigger event. This might result in the trigger action being performed. +*/ +typedef enum TEventType : unsigned char { + TEVENT_NONE, + TEVENT_PLAYER_ENTERED, // player enters this square + TEVENT_SPIED, // Spied by. + TEVENT_THIEVED, // Thieved by (raided or stolen vehicle). + TEVENT_DISCOVERED, // player discovers this object + TEVENT_HOUSE_DISCOVERED, // House has been discovered. + TEVENT_ATTACKED, // player attacks this object + TEVENT_DESTROYED, // player destroys this object + TEVENT_ANY, // Any object event will cause the trigger. + TEVENT_UNITS_DESTROYED, // all house's units destroyed + TEVENT_BUILDINGS_DESTROYED, // all house's buildings destroyed + TEVENT_ALL_DESTROYED, // all house's units & buildings destroyed + TEVENT_CREDITS, // house reaches this many credits + TEVENT_TIME, // Scenario elapsed time from start. + TEVENT_MISSION_TIMER_EXPIRED, // Pre expired mission timer. + TEVENT_NBUILDINGS_DESTROYED, // Number of buildings destroyed. + TEVENT_NUNITS_DESTROYED, // Number of units destroyed. + TEVENT_NOFACTORIES, // No factories left. + TEVENT_EVAC_CIVILIAN, // Civilian has been evacuated. + TEVENT_BUILD, // Specified building has been built. + TEVENT_BUILD_UNIT, // Specified unit has been built. + TEVENT_BUILD_INFANTRY, // Specified infantry has been built. + TEVENT_BUILD_AIRCRAFT, // Specified aircraft has been built. + TEVENT_LEAVES_MAP, // Specified team member leaves map. + TEVENT_ENTERS_ZONE, // Enters same zone as waypoint 'x'. + TEVENT_CROSS_HORIZONTAL, // Crosses horizontal trigger line. + TEVENT_CROSS_VERTICAL, // Crosses vertical trigger line. + TEVENT_GLOBAL_SET, // If specified global has been set. + TEVENT_GLOBAL_CLEAR, // If specified global has been cleared. + TEVENT_FAKES_DESTROYED, // If all fake structures are gone. + TEVENT_LOW_POWER, // When power drops below 100%. + TEVENT_ALL_BRIDGES_DESTROYED, // All bridges destroyed. + TEVENT_BUILDING_EXISTS, // Check for building existing. + + TEVENT_COUNT, + TEVENT_FIRST=0 +} TEventType; + +TEventType Event_From_Name(char const * name); +NeedType Event_Needs(TEventType event); +char const * Name_From_Event(TEventType event); + +/* +** This holds the changable data that is associated with an event as +** it relates to a trigger. +*/ +struct TDEventClass { + /* + ** If this event has been triggered by something that is temporal, then + ** this flag will be set to true so that subsequent trigger examination + ** will return a successful event trigger flag. Typical use of this is + ** for when objects of a specific type are built. + */ + unsigned IsTripped:1; + + /* + ** Timer based events require a special timer control handler. + */ + CDTimerClass Timer; + + TDEventClass(void) : IsTripped(false), Timer(0) {}; + TDEventClass(NoInitClass const & x) : Timer(x) {}; +}; + + +/* +** This elaborates the information necessary to trigger +** an event. +*/ +class TeamTypeClass; +struct TEventClass { + + /* + ** This is the event that will controls how this event gets triggered. + */ + TEventType Event; + + /* + ** If this event needs to reference a team type, then this is the pointer + ** to the team type object. This must be separated from the following + ** union because Watcom compiler won't allow a class that has a + ** constructor to be declared in a union. + */ + CCPtr Team; + + union { + StructType Structure; // Used for structure type checking. + UnitType Unit; // Used for unit type checking. + InfantryType Infantry; // Used for infantry type checking. + AircraftType Aircraft; // Used for aircraft type checking. + HousesType House; // Used for house specific events. + long Value; // Used for other events that need data. + } Data; + + TEventClass(void) : Event(TEVENT_NONE) {Data.Value = 0;}; + TEventClass(TEventType event) : Event(event) {Data.Value = 0;}; + TEventClass(NoInitClass const & x) : Team(x) {}; + + void Code_Pointers(void); + void Decode_Pointers(void); + void Reset(TDEventClass & td) const; + bool operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; +}; + + +typedef enum AttachType : unsigned char { + ATTACH_NONE=0x00, // Trigger doesn't attach to anything (orphan trigger types). + ATTACH_CELL=0x01, // Trigger can only attach to a cell. + ATTACH_OBJECT=0x02, // Trigger can attach only to object (usually building or vehicle). + ATTACH_MAP=0x04, // Trigger applies to the general map (usually zone or parallel triggers). + ATTACH_HOUSE=0x08, // Trigger applies only to a house. + ATTACH_GENERAL=0x10, // General purpose trigger attached to game state. + ATTACH_TEAM=0x20 // Trigger applies to team object. +} AttachType; + +AttachType Attaches_To(TEventType event); + + + +class EventChoiceClass { + public: + EventChoiceClass(TEventType event=TEVENT_NONE) : Event(event) {} + + operator TEventType (void) const {return(Event);} + bool operator == (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event);} + bool operator != (EventChoiceClass const & rvalue) const {return(Event != rvalue.Event);} + bool operator > (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Event(Event));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TEventType Event; +}; + +extern EventChoiceClass EventChoices[TEVENT_COUNT]; + + +#endif diff --git a/REDALERT/TEXTBTN.CPP b/REDALERT/TEXTBTN.CPP new file mode 100644 index 000000000..ad9d9b764 --- /dev/null +++ b/REDALERT/TEXTBTN.CPP @@ -0,0 +1,348 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEXTBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textbtn.h" + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass(id, x, y, w, h), + String(text) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + + if (w == -1 || h == -1) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + IsBlackBorder = false; + String = NULL; + PrintFlags = TPF_8POINT; +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- The text number to use for displaying on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass (unsigned id, int text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass (id, x, y, w, h), + String(0) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + Set_Text(text); + + if (w == -1 || h == -1) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int TextButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(char const * text, bool resize) +{ + String = text; + Flag_To_Redraw(); + if (resize && String) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String)+8; + Height = FontHeight + FontYSpacing + 2; + } +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * * + * This will set the text for this button. The text is provided as a text number. This * + * number is automatically converted to the appropriate text string before being stored * + * in the button's structure. * + * * + * INPUT: text -- The text number to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text number information is lost when it is assigned to the button. Once * + * the assignment takes place, the text number is NOT remembered by the button. * + * Only the associated text string is. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(int text, bool resize) +{ + if (text != TXT_NONE) { + Set_Text(Text_String(text), resize); + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Background(void) +{ + /* + ** Draw a border if selected style. + */ + if (IsBlackBorder) { + LogicPage->Draw_Rect (X-1, Y-1, X+Width+2, Y+Height+2, BLACK); + } + + /* + ** Draw the body & set text color. + */ + BoxStyleEnum style; + + if (IsDisabled) { +#ifdef WOLAPI_INTEGRATION + style = BOXSTYLE_BOX; +#else + style = BOXSTYLE_DIS_RAISED; +#endif + } else { + if (IsPressed) { + style = BOXSTYLE_DOWN; + } else { + style = BOXSTYLE_RAISED; + } + } + + Draw_Box(X, Y, Width, Height, style, true); +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Text(char const * text) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Display the text. + */ + if (String) { + TextPrintType flags; + + if (IsDisabled) { + flags = (TextPrintType)0; + } else { + if (IsPressed || IsOn) { + flags = TPF_USE_GRAD_PAL|TPF_BRIGHT_COLOR; + } else { + flags = TPF_USE_GRAD_PAL|TPF_MEDIUM_COLOR; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, scheme, TBLACK, PrintFlags|flags|TPF_CENTER); + } +} diff --git a/REDALERT/TEXTBTN.H b/REDALERT/TEXTBTN.H new file mode 100644 index 000000000..5157b1e1c --- /dev/null +++ b/REDALERT/TEXTBTN.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEXTBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEXTBTN_H +#define TEXTBTN_H + +#include "toggle.h" + + +class TextButtonClass : public ToggleClass +{ + public: + TextButtonClass(void); + TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + TextButtonClass(unsigned id, int text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const *text, bool resize = false); + virtual void Set_Text(int text, bool resize = false); + virtual void Set_Style (TextPrintType style) {PrintFlags = style;} + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + unsigned IsBlackBorder:1; + + /* + ** This points to a constant string that is used for the button's text. + */ + char const * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/REDALERT/THEME.CPP b/REDALERT/THEME.CPP new file mode 100644 index 000000000..5bc3a4af7 --- /dev/null +++ b/REDALERT/THEME.CPP @@ -0,0 +1,614 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/THEME.CPP 3 3/11/97 4:03p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ThemeClass::AI -- Process the theme engine and restart songs. * + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * ThemeClass::From_Name -- Determines theme number from specified name. * + * ThemeClass::Full_Name -- Retrieves the full score name. * + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * ThemeClass::Scan -- Scans all scores for availability. * + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * ThemeClass::Stop -- Stops the current theme from playing. * + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "theme.h" + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/* +** These are the actual filename list for the theme sample files. +*/ +ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { + {"BIGF226M", TXT_THEME_BIGF, 0, 307, true, false, true, HOUSEF_ALLIES}, + {"CRUS226M", TXT_THEME_CRUS, 0, 222, true, false, true, HOUSEF_SOVIET}, + {"FAC1226M", TXT_THEME_FAC1, 0, 271, true, false, true, HOUSEF_ALLIES}, + {"FAC2226M", TXT_THEME_FAC2, 0, 328, true, false, true, HOUSEF_SOVIET}, + {"HELL226M", TXT_THEME_HELL, 0, 375, true, false, true, HOUSEF_ALLIES}, + {"RUN1226M", TXT_THEME_RUN1, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"SMSH226M", TXT_THEME_SMSH, 0, 272, true, false, true, HOUSEF_ALLIES}, + {"TREN226M", TXT_THEME_TREN, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"WORK226M", TXT_THEME_WORK, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"AWAIT", TXT_THEME_AWAIT, 0, 259, true, false, true, HOUSEF_ALLIES}, + {"DENSE_R", TXT_THEME_DENSE_R, 0, 294, true, false, true, HOUSEF_ALLIES}, + {"FOGGER1A", TXT_THEME_FOGGER1A, 0, 297, true, false, true, HOUSEF_ALLIES}, + {"MUD1A", TXT_THEME_MUD1A, 0, 280, true, false, true, HOUSEF_ALLIES}, + {"RADIO2", TXT_THEME_RADIO2, 0, 237, true, false, true, HOUSEF_ALLIES}, + {"ROLLOUT", TXT_THEME_ROLLOUT, 0, 227, true, false, true, HOUSEF_ALLIES}, + {"SNAKE", TXT_THEME_SNAKE, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"TERMINAT", TXT_THEME_TERMINAT, 0, 310, true, false, true, HOUSEF_ALLIES}, + {"TWIN", TXT_THEME_TWIN, 0, 229, true, false, true, HOUSEF_ALLIES}, + {"VECTOR1A", TXT_THEME_VECTOR1A, 0, 252, true, false, true, HOUSEF_ALLIES}, + {"MAP", TXT_THEME_MAP, 0, 63, false, true, true, HOUSEF_NONE}, + {"SCORE", TXT_THEME_SCORE, 0, 106, false, true, true, HOUSEF_NONE}, + {"INTRO", TXT_THEME_INTRO, 0, 205, false, true, true, HOUSEF_NONE}, + {"CREDITS", TXT_THEME_CREDITS, 0, 163, false, true, true, HOUSEF_NONE}, + + {"2ND_HAND", TXT_THEME_2ND_HAND, 0, 268, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"ARAZOID", TXT_THEME_ARAZOID, 0, 257, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"BACKSTAB", TXT_THEME_BACKSTAB, 0, 278, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"CHAOS2", TXT_THEME_CHAOS2, 0, 250, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"SHUT_IT", TXT_THEME_SHUT_IT, 0, 261, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"TWINMIX1", TXT_THEME_TWINMIX1, 0, 222, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"UNDER3", TXT_THEME_UNDER3, 0, 246, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"VR2", TXT_THEME_VR2, 0, 255, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"BOG", TXT_THEME_BOG, 0, 212, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"FLOAT_V2", TXT_THEME_FLOAT_V2, 0, 274, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"GLOOM", TXT_THEME_GLOOM, 0, 236, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"GRNDWIRE", TXT_THEME_GRNDWIRE, 0, 228, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"RPT", TXT_THEME_RPT, 0, 275, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"SEARCH", TXT_THEME_SEARCH, 0, 276, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"TRACTION", TXT_THEME_TRACTION, 0, 237, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"WASTELND", TXT_THEME_WASTELND, 0, 242, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#endif +}; + + +/*********************************************************************************************** + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * * + * This routine is used to retrieve a pointer to the base filename for the theme * + * specified. * + * * + * INPUT: theme -- The theme number to convert into a base filename. * + * * + * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * + * theme number is invalid, then a pointer to "No Theme" is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Base_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(_themes[theme].Name); + } + return("No theme"); +} + + +/*********************************************************************************************** + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * * + * This is the default constructor for the theme class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ThemeClass::ThemeClass(void) : + Current(-1), + Score(THEME_NONE), + Pending(THEME_NONE) +{ +} + + +/*********************************************************************************************** + * ThemeClass::Full_Name -- Retrieves the full score name. * + * * + * This routine will fetch and return with a pointer to the full name of the theme * + * specified. * + * * + * INPUT: theme -- The theme to fetch the full name for. * + * * + * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * + * EMS memory. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Full_Name(ThemeType theme) const +{ + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + return(Text_String(_themes[theme].Fullname)); + } + return(NULL); +} + + +/*********************************************************************************************** + * ThemeClass::AI -- Process the theme engine and restart songs. * + * * + * This is a maintenance function that will restart an appropriate theme if the current one * + * has finished. This routine should be called frequently. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 01/23/1995 JLB : Picks new song just as it is about to play it. * + *=============================================================================================*/ +void ThemeClass::AI(void) +{ + if (SampleType && !Debug_Quiet) { + if (ScoresPresent && Options.ScoreVolume != 0 && !Still_Playing() && Pending != THEME_NONE) { + + /* + ** If the pending song needs to be picked, then pick it now. + */ + if (Pending == THEME_PICK_ANOTHER) { + Pending = Next_Song(Score); + } + + /* + ** Start the song playing and then flag it so that a new song will + ** be picked when this one ends. + */ + Play_Song(Pending); + Pending = THEME_PICK_ANOTHER; + } + Sound_Callback(); + } +} + + +/*********************************************************************************************** + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * * + * use this routine to figure out what song number to play. It examines the option settings * + * for repeat and shuffle so that it can return the correct value. * + * * + * INPUT: theme -- The origin (last) index. The new value is related to this for all but * + * the shuffling method of play. * + * * + * OUTPUT: Returns with the song number for the next song to play. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * + *=============================================================================================*/ +ThemeType ThemeClass::Next_Song(ThemeType theme) const +{ + if (theme == THEME_NONE || theme == THEME_PICK_ANOTHER || (theme != THEME_QUIET && !_themes[theme].Repeat && !Options.IsScoreRepeat)) { + if (Options.IsScoreShuffle) { + + /* + ** Shuffle the theme, but never pick the same theme that was just + ** playing. + */ + ThemeType newtheme; + do { + newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); + } while (newtheme == theme || !Is_Allowed(newtheme)); + theme = newtheme; + + } else { + + /* + ** Sequential score playing. + */ + do { + theme++; + if (theme > THEME_LAST) { + theme = THEME_FIRST; + } + } while (!Is_Allowed(theme)); + } + } + return(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * * + * This routine will cause the current song to fade and the specified song to start. This * + * is the normal and friendly method of changing the current song. * + * * + * INPUT: theme -- The song to start playing. If -1 is passed in, then just the current song.* + * is faded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Queue_Song(ThemeType theme) +{ + /* + ** If there is no score file present, then abort. + */ + if (!ScoresPresent) return; + + /* + ** If there is no sound driver or sounds have been specifically + ** turned off, then abort. + */ + if (SampleType == 0 || Debug_Quiet) return; + + /* + ** If the current score volumne is set to silent, then there is no need to play the + ** specified theme. + */ + if (Options.ScoreVolume == 0) return; + + /* + ** If the pending theme is available to be set and the specified theme is valid, then + ** set the queued theme accordingly. + */ + if (Pending == THEME_NONE || Pending == THEME_PICK_ANOTHER || theme == THEME_NONE || theme == THEME_QUIET) { + Pending = theme; + if (Still_Playing()) { + Fade_Sample(Current, THEME_DELAY); + } + } +} + + +/*********************************************************************************************** + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * * + * This routine is used to start the specified theme playing right now. If there is already * + * a theme playing, it is cut short so that this one may start. * + * * + * INPUT: theme -- The theme number to start playing. * + * * + * OUTPUT: Returns with the sample play handle. * + * * + * WARNINGS: This cuts off any current song in a abrupt manner. Only use this routine when * + * necessary. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Play_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume != 0) { + Stop(); + Score = theme; + if (theme != THEME_NONE && theme != THEME_QUIET) { + //PG StreamLowImpact = true; + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); + //PG StreamLowImpact = false; + } + } + return(Current); +} + + +/*********************************************************************************************** + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * * + * This routine will construct (into a static buffer) a filename that matches the theme * + * number specified. This constructed filename is returned as a pointer. The filename will * + * remain valid until the next call to this routine. * + * * + * INPUT: theme -- The theme number to convert to a filename. * + * * + * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 05/09/1995 JLB : Theme variation support. * + *=============================================================================================*/ +char const * ThemeClass::Theme_File_Name(ThemeType theme) +{ + static char name[_MAX_FNAME+_MAX_EXT]; + + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); + return((char const *)(&name[0])); + } + + return(""); +} + + +/*********************************************************************************************** + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * * + * Use this routine to calculate the length of the song. The length is determined by * + * reading the header of the song and dividing the sample rate into the sample length. * + * * + * INPUT: theme -- The song number to examine to find its length. * + * * + * OUTPUT: Returns with the length of the specified theme. This length is in the form of * + * seconds. * + * * + * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Track_Length(ThemeType theme) const +{ + if ((unsigned)theme < THEME_COUNT) { + return(_themes[theme].Duration); + } + return(0); +} + + +/*********************************************************************************************** + * ThemeClass::Stop -- Stops the current theme from playing. * + * * + * Use this routine to stop the current theme. After this routine is called, no more music * + * will play until the Start() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Stop(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; + } +} + + +void ThemeClass::Suspend(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Pending = Score; + Score = THEME_NONE; + } +} + + +/*********************************************************************************************** + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * * + * Use this routine to determine if music is still playing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the music still audible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Still_Playing(void) const +{ + if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { + return(Sample_Status(Current)); + } + return(false); +} + + +/*********************************************************************************************** + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * * + * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * + * if the scenario is too early for that score, or the score only is allowed in special * + * cases. * + * * + * INPUT: index -- The score the check to see if it is allowed to play. * + * * + * OUTPUT: Is the specified score allowed to play in the normal score playlist? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 07/04/1996 JLB : Handles alternate playlist checking. * + *=============================================================================================*/ +bool ThemeClass::Is_Allowed(ThemeType index) const +{ + if ((unsigned)index >= THEME_COUNT) return(true); + + /* + ** If the theme is not present, then it certainly isn't allowed. + */ + if (!_themes[index].Available) return(false); + + /* + ** Only normal themes (playable during battle) are considered allowed. + */ + if (!_themes[index].Normal) return(false); + + /* + ** If the theme is not allowed to be played by the player's house, then don't allow + ** it. If the player's house hasn't yet been determined, then presume this test + ** passes. + */ + if (PlayerPtr != NULL && ((1 << PlayerPtr->ActLike) & _themes[index].Owner) == 0) return(false); + + /* + ** If the scenario doesn't allow this theme yet, then return the failure flag. The + ** scenario check only makes sense for solo play. + */ + if (Session.Type == GAME_NORMAL && Scen.Scenario < _themes[index].Scenario) return(false); + + /* + ** Since all tests passed, return with the "is allowed" flag. + */ + return(true); +} + + +/*********************************************************************************************** + * ThemeClass::From_Name -- Determines theme number from specified name. * + * * + * Use this routine to convert a name (either the base filename of the theme, or a partial * + * substring of the full name) into the matching ThemeType value. Typical use of this is * + * when parsing the INI file for theme control values. * + * * + * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * + * theme name. * + * * + * OUTPUT: Returns with the matching theme number. If no match could be found, then * + * THEME_NONE is returned. * + * * + * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * + * the full theme name, the comparison is case sensitive. * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +ThemeType ThemeClass::From_Name(char const * name) const +{ + if (name && strlen(name) > 0) { + /* + ** First search for an exact name match with the filename + ** of the theme. This is guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (stricmp(_themes[theme].Name, name) == 0) { + return(theme); + } + } + + /* + ** If the filename scan failed to find a match, then scan for + ** a substring within the full name of the score. This might + ** yield a match, but is not guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { + return(theme); + } + } + } + + return(THEME_NONE); +} + + +/*********************************************************************************************** + * ThemeClass::Scan -- Scans all scores for availability. * + * * + * This routine should be called whenever a score mixfile is registered. It will scan * + * to see if any score is unavailable. If this is the case, then the score will be so * + * flagged in order not to appear on the play list. This condition is likely to occur * + * when expansion mission disks contain a different score mix than the release version. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Scan(void) +{ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); + } +} + + + +/*********************************************************************************************** + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * * + * This is an override function used to set a particular theme's initial scenario and * + * owner values. Typically, the rules control file will be the source of calling this * + * routine. * + * * + * INPUT: theme -- The theme to set these override values for. * + * * + * scenario -- The first scenario when this theme becomes available on the play list. * + * * + * owners -- A bitfield representing the owners allowed to play this song. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Set_Theme_Data(ThemeType theme, int scenario, int owners) +{ + if (theme != THEME_NONE) { + _themes[theme].Normal = true; + _themes[theme].Scenario = scenario; + _themes[theme].Owner = owners; + } +} + diff --git a/REDALERT/THEME.H b/REDALERT/THEME.H new file mode 100644 index 000000000..b46c34ed5 --- /dev/null +++ b/REDALERT/THEME.H @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/THEME.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef THEME_H +#define THEME_H + +class ThemeClass +{ + private: + static char const * Theme_File_Name(ThemeType theme); + + int Current; // Handle to current score. + ThemeType Score; // Score number currently being played. + ThemeType Pending; // Score to play next. + + typedef struct { + char const * Name; // Filename of score. + int Fullname; // Text number for full score name. + int Scenario; // Scenario when it first becomes available. + int Duration; // Duration of theme in seconds. + bool Normal; // Allowed in normal game play? + bool Repeat; // Always repeat this score? + bool Available; // Is the score available? + int Owner; // What houses are allowed to play this theme (bit field)? + } ThemeControl; + + static ThemeControl _themes[THEME_COUNT]; + + enum { + THEME_DELAY=TIMER_SECOND + }; + + public: + ThemeClass(void); + + ThemeType From_Name(char const * name) const; + ThemeType Next_Song(ThemeType index) const; + ThemeType What_Is_Playing(void) const {return Score;} + bool Is_Allowed(ThemeType index) const; + bool Is_Regular(ThemeType theme) const {return(theme != THEME_NONE && _themes[theme].Normal);} + char const * Base_Name(ThemeType index) const; + char const * Full_Name(ThemeType index) const; + int Max_Themes(void) const {return THEME_COUNT;} + int Play_Song(ThemeType index); + int Still_Playing(void) const; + int Track_Length(ThemeType index) const; + static void Scan(void); + void AI(void); + void Fade_Out(void) {Queue_Song(THEME_QUIET);} + void Queue_Song(ThemeType index); + void Set_Theme_Data(ThemeType theme, int scenario, int owners); + void Stop(void); + void Suspend(void); +}; + +#endif diff --git a/REDALERT/TOGGLE.CPP b/REDALERT/TOGGLE.CPP new file mode 100644 index 000000000..7e1f5372a --- /dev/null +++ b/REDALERT/TOGGLE.CPP @@ -0,0 +1,198 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TOGGLE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : February 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "toggle.h" + + +/*********************************************************************************************** + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * * + * This is the normal constructor for toggle buttons. A toggle button is one that most * + * closely resembles the Windows style. It has an up and down state as well as an on * + * and off state. * + * * + * INPUT: id -- ID number for this button. * + * * + * x,y -- Pixel coordinate of upper left corner of this button. * + * * + * w,h -- Width and height of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ToggleClass::ToggleClass(unsigned id, int x, int y, int w, int h) : + ControlClass(id, x, y, w, h, LEFTPRESS|LEFTRELEASE, true), + IsPressed(false), + IsOn(false), + IsToggleType(false) +{ +} + + +/*********************************************************************************************** + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * * + * This routine will turn the button on. The button will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_On(void) +{ + IsOn = true; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * * + * This routine will turn the toggle button "off". It will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_Off(void) +{ + IsOn = false; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Action -- Handles mouse clicks on a text button. * + * * + * This routine will process any mouse or keyboard event that is associated with this * + * button object. It detects and flags the text button so that it will properly be drawn * + * in a pressed or raised state. It also handles any toggle state for the button. * + * * + * INPUT: flags -- The event flags that triggered this button. * + * * + * key -- The keyboard code associated with this event. Usually this is KN_LMOUSE * + * or similar, but it could be a regular key if this text button is given * + * a hotkey. * + * * + * OUTPUT: Returns whatever the lower level processing for buttons decides. This is usually * + * true. * + * * + * WARNINGS: The button is flagged to be redrawn by this routine. * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + * 02/02/1995 JLB : Left press doesn't get passed to other buttons now * + *=============================================================================================*/ +int ToggleClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there are no action flag bits set, then this must be a forced call. A forced call + ** must never actually function like a real call, but rather only performs any necessary + ** graphic updating. + */ + bool overbutton = ((Get_Mouse_X() - X) < Width && (Get_Mouse_Y() - Y) < Height ); + if (!flags) { + if (overbutton) { + if (!IsPressed) { + IsPressed = true; + Flag_To_Redraw(); + } + } else { + if (IsPressed) { + IsPressed = false; + Flag_To_Redraw(); + } + } + } + + /* + ** Handle the sticky state for this gadget. It must be processed here + ** because the event flags might be cleared before the action function + ** is called. + */ + Sticky_Process(flags); + + /* + ** Flag the button to show the pressed down imagery if this mouse button + ** was pressed over this gadget. + */ + if (flags & LEFTPRESS) { + IsPressed = true; + Flag_To_Redraw(); + flags &= ~LEFTPRESS; + ControlClass::Action(flags, key); + key = KN_NONE; // erase the event + return(true); // stop processing other buttons now + } + + if (flags & LEFTRELEASE) { + if (IsPressed) { + if (IsToggleType && overbutton) { + IsOn = (IsOn == false); + } + IsPressed = false; + Flag_To_Redraw(); + } else { + flags &= ~LEFTRELEASE; + } + } + + /* + ** Do normal button processing. This ends up causing the button's ID number to + ** be returned from the controlling Input() function. + */ + return(ControlClass::Action(flags, key)); +} + + diff --git a/REDALERT/TOGGLE.H b/REDALERT/TOGGLE.H new file mode 100644 index 000000000..4c4a5bbd5 --- /dev/null +++ b/REDALERT/TOGGLE.H @@ -0,0 +1,79 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TOGGLE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TOGGLE_H +#define TOGGLE_H + +#include "control.h" + +/* +** This class handles gadgets that behave like the Windows buttons. That is, once the mouse +** button is clicked over them, they capture the focus until the mouse button is released. +** They have a different imagery for the pressed and released states. They only recognize +** a valid selection when the mouse button is release while over the button. +*/ +class ToggleClass : public ControlClass +{ + public: + ToggleClass(unsigned id, int x, int y, int w, int h); + void Turn_On(void); + void Turn_Off(void); + + /* + ** Is this button in a pressed down state? This occurs when the mouse is clicked on the + ** button and the mouse is still being held down. + */ + unsigned IsPressed:1; + + /* + ** This is the button on/off state. Sometimes a button that is "on" has a different + ** imagery than one that is "off". If the on/off state is not necessary, then just + ** ignore this flag. + */ + unsigned IsOn:1; + + /* + ** If this button can be turned "on" or "off", then this flag should be set to + ** true. Sometimes a button needs to display its on/off state. In the render routine, + ** examine the IsOn flag and display accordingly. If this flag is false, then the + ** IsOn flag will not be changed, regardless of button clicking. + */ + unsigned IsToggleType:1; + + protected: + + virtual int Action(unsigned flags, KeyNumType &key); +}; + +#endif diff --git a/REDALERT/TOOLTIP.CPP b/REDALERT/TOOLTIP.CPP new file mode 100644 index 000000000..9d786fce8 --- /dev/null +++ b/REDALERT/TOOLTIP.CPP @@ -0,0 +1,294 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// ToolTip.cpp +#if (0)//PG +#include "function.h" +#include "ToolTip.h" +#include "IconList.h" + +//#include "WolDebug.h" + +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ); +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ); + +//*********************************************************************************************** +ToolTipClass::ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, + bool bRightAlign /* = false */, bool bIconList /*= false */ ) + : pGadget( pGadget ), xShow( xShow ), yShow( yShow ), next( NULL ), bShowing( false ), bIconList( bIconList ), + bRightAlign( bRightAlign ) + +{ + if( szText ) + { + if( strlen( szText ) > TOOLTIPTEXT_MAX_LEN ) + strcpy( szTip, "Tooltip too long!" ); + else + strcpy( szTip, szText ); + } + else + *szTip = 0; + + Set_Font( TypeFontPtr ); + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShow = String_Pixel_Width( szTip ) + 2; + hShow = 11; + + if( !bIconList ) + { + pSaveRect = new char[ wShow * hShow ]; // Else it is reallocated on every draw. + if( bRightAlign ) + this->xShow -= wShow; + } + else + pSaveRect = NULL; + + // bIconList is true if tooltips appear for individual line items in an iconlist. + // szText in this case is ignored. + // yShow is the y position of the top row's tooltip - other rows will be offset from here. +} + +//*********************************************************************************************** +ToolTipClass* ToolTipClass::GetToolTipHit() +{ + // Returns 'this' if the mouse is over gadget bound to tooltip. + // Otherwise calls the same function in the next tooltip in the list of which *this is a part. + if( bGadgetHit() ) + return this; + else if( next ) + return next->GetToolTipHit(); + else + return NULL; +} + +//*********************************************************************************************** +bool ToolTipClass::bGadgetHit() +{ + // Returns true if the mouse is currently over the gadget to which *this is bound. + int x = Get_Mouse_X(); + int y = Get_Mouse_Y(); + return ( x > pGadget->X && x < pGadget->X + pGadget->Width && y > pGadget->Y && y < pGadget->Y + pGadget->Height ); +} + +//*********************************************************************************************** +void ToolTipClass::Move( int xShow, int yShow ) +{ + bool bRestoreShow = false; + if( bShowing ) + { + bRestoreShow = true; + Unshow(); + } + this->xShow = xShow; + if( !bIconList ) + { + if( bRightAlign ) + this->xShow -= wShow; + } + this->yShow = yShow; + if( bRestoreShow ) + Show(); +} + +//*********************************************************************************************** +void ToolTipClass::Show() +{ + if( !bShowing ) + { + Set_Font( TypeFontPtr ); + int xShowUse = xShow, yShowUse, wShowUse; + const char* szTipUse; + if( !bIconList ) + { + yShowUse = yShow; + wShowUse = wShow; + szTipUse = szTip; + } + else + { + IconListClass* pIconList = (IconListClass*)pGadget; + iLastIconListIndex = pIconList->IndexUnderMouse(); + if( iLastIconListIndex < 0 ) + { + // Nothing to show. + bShowing = true; + return; + } + yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); + szTipUse = pIconList->Get_Item_Help( iLastIconListIndex ); + if( !szTipUse || *szTipUse == 0 ) + { + // Nothing to show. + bShowing = true; + bLastShowNoText = true; + return; + } + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShowUse = String_Pixel_Width( szTipUse ) + 2; + if( bRightAlign ) + xShowUse -= wShowUse; + delete [] pSaveRect; + pSaveRect = new char[ wShowUse * hShow ]; + bLastShowNoText = false; + xLastShow = xShowUse; + yLastShow = yShowUse; + wLastShow = wShowUse; + } + + // Save rect about to be corrupted. + Hide_Mouse(); + SaveSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + // Draw text. + //Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), ColorRemaps[ PCOLOR_BROWN ].Color, TPF_TYPE ); //TPF_DROPSHADOW ); + Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), BLACK, TPF_TYPE ); //TPF_DROPSHADOW ); + // Draw bounding rect. +// LogicPage->Draw_Rect( xShowUse, yShowUse, xShowUse + wShowUse - 1, yShowUse + hShow - 1, ColorRemaps[ PCOLOR_GOLD ].Color ); + Draw_Box( xShowUse, yShowUse, wShowUse, hShow, BOXSTYLE_BOX, false ); + Show_Mouse(); + bShowing = true; + } +} + +//*********************************************************************************************** +void ToolTipClass::Unshow() +{ + if( bShowing ) + { + int xShowUse, yShowUse, wShowUse; + if( !bIconList ) + { + xShowUse = xShow; + wShowUse = wShow; + yShowUse = yShow; + } + else + { + if( iLastIconListIndex == -1 || bLastShowNoText ) + { + // Nothing to restore. + bShowing = false; + return; + } + // (Can't rely on iconlist being the same as when Show() occurred.) +// IconListClass* pIconList = (IconListClass*)pGadget; +// yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); +// const char* szTipUsed = pIconList->Get_Item_Help( iLastIconListIndex ); +// if( !szTipUsed || *szTipUsed == 0 ) +// { +// // Nothing to restore. +// bShowing = false; +// return; +// } +// Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. +// wShowUse = String_Pixel_Width( szTipUsed ) + 2; +// if( bRightAlign ) +// xShowUse -= wShowUse; + xShowUse = xLastShow; + yShowUse = yLastShow; + wShowUse = wLastShow; + } + Hide_Mouse(); + RestoreSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + Show_Mouse(); + bShowing = false; + } +} + +//*********************************************************************************************** +bool ToolTipClass::bOverDifferentLine() const +{ + // bIconList must be true if this is being used. + // Returns true if the iconlist line that the mouse is over is different than the last time Show() was called. + return ( ((IconListClass*)pGadget)->IndexUnderMouse() != iLastIconListIndex ); +} + +//*********************************************************************************************** +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ) +{ + // Saves a rect of the LogicPage DirectDraw surface to pBits. +// if( wRect * hRect > iBufferSize ) +// { +// debugprint( "SaveSurfaceRect failed.\n" ); +// return false; +// } + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + const char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + const char* pSurf = pLineSurf; + char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSave++ = *pSurf++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "SaveSurfaceRect Could not lock surface.\n" ); + return false; + } +} + +//*********************************************************************************************** +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ) +{ + // Copies a saved rect of bits back to the LogicPage DD surface. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + const char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + char* pSurf = pLineSurf; + const char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSurf++ = *pSave++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "RestoreSurfaceRect Could not lock surface.\n" ); + return false; + } +} +#endif \ No newline at end of file diff --git a/REDALERT/TOOLTIP.H b/REDALERT/TOOLTIP.H new file mode 100644 index 000000000..718174b78 --- /dev/null +++ b/REDALERT/TOOLTIP.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// ToolTip.h + +#ifdef WOLAPI_INTEGRATION + +#ifndef TOOLTIP_H +#define TOOLTIP_H + +#include "Gadget.h" + +#define TOOLTIPTEXT_MAX_LEN 100 + +#define TOOLTIPDELAY 400 // Milliseconds + +class ToolTipClass +{ +public: + ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, bool bRightAlign = false, bool bIconList = false ); + ~ToolTipClass() + { + delete [] pSaveRect; + } + + ToolTipClass* GetToolTipHit(); + void Show(); + void Unshow(); + void Move( int xShow, int yShow ); + bool bOverDifferentLine() const; + + ToolTipClass* next; // Next tooltip in list of which *this is a part. + + GadgetClass* pGadget; // Gadget to which this tooltip is bound. + + bool bRightAlign; + + bool bShowing; + bool bIconList; // True if gadget is iconlist and line-specific tooltips are to be used. + +protected: + bool bGadgetHit() const; + + int xShow; + int yShow; + int wShow; + int hShow; + char szTip[ TOOLTIPTEXT_MAX_LEN + 1 ]; // Text to show as tip. + + char* pSaveRect; + + // Used only if bIconList. + int iLastIconListIndex; + bool bLastShowNoText; + int xLastShow; + int yLastShow; + int wLastShow; +}; + +#endif + +#endif diff --git a/REDALERT/TRACKER.CPP b/REDALERT/TRACKER.CPP new file mode 100644 index 000000000..0a4e1c33e --- /dev/null +++ b/REDALERT/TRACKER.CPP @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRACKER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRACKER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/14/96 * + * * + * Last Update : June 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Detach_This_From_All -- Detaches this object from all others. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * Detach_This_From_All -- Detaches this object from all others. * + * * + * This routine sweeps through all game objects and makes sure that it is no longer * + * referenced by them. Typically, this is called in preparation for the object's death * + * or limbo state. * + * * + * INPUT: target -- This object expressed as a target number. * + * * + * all -- Is this object really in truly being removed from the game? The * + * answer would be false if the target was actually a stealth * + * tank that is cloaking. In such a case, the object should be removed * + * from all non-friendly tracking systems, but otherwise left alone. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Detach_This_From_All(TARGET target, bool all) +{ + int index; + if (Target_Legal(target)) { + + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypes.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Units.Count(); index++) { + Units.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Vessels.Count(); index++) { + Vessels.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Aircraft.Count(); index++) { + Aircraft.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Bullets.Count(); index++) { + Bullets.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Infantry.Count(); index++) { + Infantry.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Anims.Count(); index++) { + Anims.Ptr(index)->Detach(target, all); + } + + Map.Detach(target, all); + + Logic.Detach(target, all); + + ChronalVortex.Detach(target); + + /* + ** Removing a trigger type must also remove all triggers that are dependant + ** upon that type. + */ + if (As_TriggerType(target) != NULL) { + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + + if (tp->Class->As_Target() == target) { + Detach_This_From_All(tp->As_Target()); + delete tp; + index--; + } + } + } + + for (index = 0; index < Triggers.Count(); index++) { + Triggers.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypes.Ptr(index)->Detach(target, all); + } + } +} + + + + + diff --git a/REDALERT/TRIGGER.CPP b/REDALERT/TRIGGER.CPP new file mode 100644 index 000000000..57dabfeb2 --- /dev/null +++ b/REDALERT/TRIGGER.CPP @@ -0,0 +1,494 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGGER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : August 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Or_Make -- Find or create a trigger of the type specified. * + * TriggerClass::As_Target -- Converts trigger to a target value * + * TriggerClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * TriggerClass::Detach -- Detach specified target from this trigger. * + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * TriggerClass::Init -- clears triggers for new scenario * + * TriggerClass::Spring -- Spring the trigger (possibly). * + * TriggerClass::TriggerClass -- constructor * + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * TriggerClass::operator new -- 'new' operator * + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * * + * In the rare (possibly never?) case of requiring a description for this trigger, then * + * just have the model class generate a description and return that. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with an ASCII description of this trigger. * + * * + * WARNINGS: see TriggerTypeClass::Description() * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerClass::Description(void) const +{ + return(Class->Description()); +} + + +/*********************************************************************************************** + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * * + * This routine is called when the trigger has been assigned to a list box. It will * + * display a description of the trigger. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerClass::TriggerClass -- constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::TriggerClass(TriggerTypeClass * trigtype) : + RTTI(RTTI_TRIGGER), + ID(Triggers.ID(this)), + Class(trigtype), + AttachCount(0), + Cell(0) +{ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); +} + + +/*********************************************************************************************** + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * * + * This destructor will update the house blockage value if necessary. No other action need * + * be performed on trigger destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass::~TriggerClass(void) +{ + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_GENERAL) != 0) { + if (LogicTriggerID >= LogicTriggers.ID(this)) { + LogicTriggerID--; + if (LogicTriggerID < 0 && LogicTriggers.Count() == 0) { + LogicTriggerID = 0; + } + } + } + + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_MAP) != 0) { + if (MapTriggerID >= MapTriggers.ID(this)) { + MapTriggerID--; + if (MapTriggerID < 0 && MapTriggers.Count() == 0) { + MapTriggerID = 0; + } + } + } + + if (GameActive && Class->House != HOUSE_NONE && Class->Action1.Action == TACTION_ALLOWWIN) { + if (Houses.Ptr(Class->House)->Blockage) Houses.Ptr(Class->House)->Blockage--; + Houses.Ptr(Class->House)->BorrowedTime = TICKS_PER_SECOND*4; + } + ID = -1; +} + + +/*********************************************************************************************** + * TriggerClass::Init -- clears triggers for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Init(void) +{ + Triggers.Free_All(); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Spring the trigger (possibly). * + * * + * This routine is called when a potential trigger even has occurred. The event is matched * + * with the trigger event needed by this trigger. If the condition warrants, the trigger * + * action is performed. * + * * + * INPUT: event -- The event that is occurring. * + * * + * obj -- If the trigger is attached to an object, this points to the object. * + * * + * cell -- If the trigger is attached to a cell, this is the cell number. * + * * + * forced -- Should the trigger be forced to execute regardless of the event? * + * * + * OUTPUT: bool; Was the trigger sprung? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 JLB : Created. * + * 08/13/1996 JLB : Linked triggers supported. * + *=============================================================================================*/ +bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool forced) +{ + assert(Triggers.ID(this) == ID); + + bool e1 = Class->Event1(Event1, event, Class->House, obj, forced); + bool e2 = false; + bool execute = false; + + /* + ** Forced triggers must presume that the cell parameter is invalid. It then + ** uses the embedded cell value in the trigger as the official location where + ** the trigger will detonate at. + */ + if (forced) { + cell = Cell; + } else { + + /* + ** Determine if the trigger event is considered to have been sprung according to the + ** event control value. This might require that both events be triggered, one event + ** triggered, or either event triggered to activate the trigger action. + */ + switch (Class->EventControl) { + case MULTI_ONLY: + execute = e1; + break; + + case MULTI_AND: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 && e2); + break; + + case MULTI_LINKED: + case MULTI_OR: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 || e2); + break; + } + } + + /* + ** See if the trigger is sprung with a qualifying event. + */ + if (execute || forced) { + + /* + ** Verify that the trigger event should really be sprung. Exceptions + ** would include semi-persistent triggers that don't actually + ** spring until all triggers have sprung. + */ + if (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT) { + + /* + ** Detach ourselves from the object and record that there + ** is one less attachment to keep track of. + */ + if (obj) { + obj->Trigger = NULL; + } + if (cell) { + Map[cell].Trigger = NULL; + } + + /* + ** If we're attached to more objects, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + AttachCount--; + if (AttachCount > 0) { + return(false); + } + } + + /* + ** For linked trigger events, perform the action associated with the matching + ** trigger event. Otherwise perform the action for both events. + */ + bool ok = false; + HousesType hh = Class->House; + if (Class->EventControl == MULTI_LINKED) { + if (e1 || forced) ok |= Class->Action1(hh, obj, ID, cell); + if (e2 && !forced) ok |= Class->Action2(hh, obj, ID, cell); + } else { + + switch (Class->ActionControl) { + case MULTI_ONLY: + ok |= Class->Action1(hh, obj, ID, cell); + break; + + default: + case MULTI_AND: + ok |= Class->Action1(hh, obj, ID, cell); + ok |= Class->Action2(hh, obj, ID, cell); + break; + } + } + if (!IsActive) return(true); + + /* + ** If at least one action was performed, then consider this + ** trigger to have completed and thus will be deleted if + ** necessary. + */ + if (ok) { + #ifdef CHEAT_KEYS + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 17, 11); + MonoArray[DMONO_STRESS].Scroll(); + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 18, 11); + MonoArray[DMONO_STRESS].Set_Cursor(0, 10); + MonoArray[DMONO_STRESS].Printf("%02d:%02d:%02d-%s", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND, Class->IniName); + MonoArray[DMONO_STRESS].Sub_Window(); + #endif + + if (Class->IsPersistant == TriggerTypeClass::VOLATILE || (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT && AttachCount <= 1)) { + Detach_This_From_All(As_Target(), true); + delete this; + return(true); + } else { + + /* + ** Reset event data so that the event will + ** repeat as necessary. + */ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new trigger * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void * TriggerClass::operator new(size_t ) +{ + void * ptr = Triggers.Allocate(); + if (ptr) { + ((TriggerClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * * + * This routine will return a previously allocated trigger object back to the memory * + * pool from which it came. * + * * + * INPUT: pointer -- Pointer to the trigger to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::operator delete(void * pointer) +{ + if (pointer) { + ((TriggerClass *)pointer)->IsActive = false; + } + Triggers.Free((TriggerClass *)pointer); +} + + +/*********************************************************************************************** + * TriggerClass::As_Target -- Converts trigger to a target value * + * * + * Converts the trigger object into a target identifier. * + * * + * INPUT: none * + * * + * OUTPUT: TARGET value * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerClass::As_Target(void) const +{ + assert(Triggers.ID(this) == ID); + + return(Build_Target(RTTI_TRIGGER, ID)); +} + + +/*********************************************************************************************** + * Find_Or_Make -- Find or create a trigger of the type specified. * + * * + * This routine is used when, given a trigger type, an actual trigger object is needed. * + * In this case, an existing trigger of the correct type must be located, or a trigger * + * object must be created. In either case, this routine will return a trigger object that * + * corresponds to the trigger type class specified. * + * * + * INPUT: trigtype -- Pointer to the trigger type to find (or create) a matching trigger * + * object. * + * * + * OUTPUT: Returns a pointer to a matching trigger object. If no more triggers could be * + * allocated and no matching trigger could be found, then this routine will return * + * NULL (a very rare case). * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype) +{ + if (!trigtype) return(NULL); + + for (int index = 0; index < Triggers.Count(); index++) { + if (trigtype == Triggers.Ptr(index)->Class) { + return(Triggers.Ptr(index)); + } + } + + /* + ** No trigger was found, so make one. + */ + TriggerClass * trig = new TriggerClass(trigtype); + return(trig); +} + + +/*********************************************************************************************** + * TriggerClass::Detach -- Detach specified target from this trigger. * + * * + * This routine is called when the specified trigger MUST be detached from all references * + * to it. The only reference maintained by a trigger is the reference to the trigger * + * type class it is modeled after. * + * * + * INPUT: target -- The target identifier to remove all attachments to. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must never detach the trigger type class from a trigger. Such a process * + * will leave the trigger orphan and in a 'crash the game immediately if used' * + * state. As such, this routine will throw an assertion if this is tried. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Detach(TARGET target, bool ) +{ + if (Is_Target_TriggerType(target)) { + assert((TriggerTypeClass*)Class != As_TriggerType(target)); +// if (Class == As_TriggerType(target)) { +// Class = NULL; +// } + } +} diff --git a/REDALERT/TRIGGER.H b/REDALERT/TRIGGER.H new file mode 100644 index 000000000..cb0d772df --- /dev/null +++ b/REDALERT/TRIGGER.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGGER.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : November 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TRIGGER_H +#define TRIGGER_H + +class TriggerClass { + public: + RTTIType RTTI; + int ID; + + CCPtr Class; + + /* + ** Record of the "already sprung" flag for the events. + */ + TDEventClass Event1; + TDEventClass Event2; + + /* + ** Constructor/Destructor + */ + TriggerClass(TriggerTypeClass * trigtype=NULL); + TriggerClass(NoInitClass const & x) : Class(x), Event1(x), Event2(x) {}; + ~TriggerClass(void); + + /* + ** Initialization: clears all triggers in preparation for new scenario + */ + static void Init(void); + + /* + ** Processing routines + */ + bool Spring(TEventType event=TEVENT_ANY, ObjectClass * object=0, CELL cell=0, bool forced=false); + void Detach(TARGET target, bool all=true); + + /* + ** File I/O routines + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + + /* + ** Utility routines + */ + TARGET As_Target(void) const; + char const * Description(void) const; + void Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Name(void) const {return(Class->Name());} + + /* + ** Overloaded operators + */ + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + /* + ** If this trigger object is active, then this flag will be true. Trigger + ** objects that are not active are either not yet created or have been + ** deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** This value tells how many objects or cells this trigger is attached + ** to. The Read_INI routine for all classes that point to a trigger must + ** increment this value! + */ + int AttachCount; + + /* + ** This value is used for triggers that can only exist in one cell. It is + ** needed for such triggers that the exact location of the trigger is needed + ** during processing but its location cannot be inferred from other data. + ** For all other triggers, this value is ignored. + */ + CELL Cell; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + + + + +#endif \ No newline at end of file diff --git a/REDALERT/TRIGTYPE.CPP b/REDALERT/TRIGTYPE.CPP new file mode 100644 index 000000000..9e2c1b131 --- /dev/null +++ b/REDALERT/TRIGTYPE.CPP @@ -0,0 +1,2126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGTYPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "trigtype.h" + + +/*********************************************************************************************** + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * * + * This is the normal constructor for a trigger object. The trigger starts with no team * + * members, no mission, and default values for all settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/10/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::TriggerTypeClass(void) : + AbstractTypeClass(RTTI_TRIGGERTYPE, TriggerTypes.ID(this), TXT_NONE, "x"), + IsPersistant(VOLATILE), + EventControl(MULTI_ONLY), + ActionControl(MULTI_ONLY), + House(HOUSE_SPAIN) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers. * + * * + * When a trigger type is deleted, then all triggers that refer to that type must also * + * be deleted as well. There can be no 'orphan' triggers in existence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::~TriggerTypeClass(void) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * * + * This routine will allocate a block of memory from the special trigger type object * + * pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated trigger type memory block. If there is * + * no more block available in the pool, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * TriggerTypeClass::operator new(size_t ) +{ + void * ptr = TriggerTypes.Allocate(); + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * * + * This routine will return a previously allocated trigger type object to the private * + * memory pool from which it was allocated. * + * * + * INPUT: ptr -- Pointer to the trigger type class to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = false; + } + TriggerTypes.Free((TriggerTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * * + * Use this routine to take this trigger type class object and convert it into a * + * target number. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target number that represents this trigger type class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerTypeClass::As_Target(void) const +{ + return(Build_Target(RTTI_TRIGGERTYPE, ID)); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * * + * When an object disappears from the game, it must be detached from all other objects that * + * may be referring to it. This routine will detach the specified target object from any * + * references to it in this trigger type class. * + * * + * INPUT: target -- The target object to be detached from this trigger type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Detach(TARGET target, bool) +{ + Action1.Detach(target); + Action2.Detach(target); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * * + * This is the scenario editor interface to a trigger type class object. It brings up a * + * fancy schmancy dialog to allow full edit control of the trigger type. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the "OK" button pressed? A false return value indicates that the edits * + * to this trigger type class object should be rejected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TriggerTypeClass::Edit(void) +{ + enum { + /* + ** Dialog position and dimensions. + */ + D_DIALOG_W = 320 + 100, + D_DIALOG_H = 200 + 20, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + /* + ** Event entry list box coordinates and dimensions. + */ + E1_X=D_DIALOG_X+45, + E1_Y=D_DIALOG_Y+65, + E2_X=E1_X, + E2_Y=E1_Y+22, + E_WIDTH=160, + E_HEIGHT=8*5, + + /* + ** Event optional data entry coordinates and dimensions. + */ + ED1_X=E1_X+E_WIDTH+20, + ED1_Y=E1_Y, + ED2_X=ED1_X, + ED2_Y=E2_Y, + + ED_WIDTH=95, + ED_HEIGHT=8*5, + + /* + ** Action entry list box coordinates. + */ + A1_X=E1_X, + A1_Y=D_DIALOG_Y+120, + A2_X=E1_X, + A2_Y=A1_Y+22, + + /* + ** Action optional data entry coordinates. + */ + AD1_X=A1_X+E_WIDTH+20, + AD1_Y=A1_Y, + AD2_X=AD1_X, + AD2_Y=A2_Y, + + /* + ** Misc control values. + */ + GENERAL_SIZE=10, // Text length for general data entry fields. + ENTRY_SIZE=35, // Maximum size of event or action description text. + WAYPOINT_SIZE=3, // Text length maximum for waypoint entry. + TEAM_SIZE=10, // Team name text entry field length. + DESC_SIZE=35 // Maximum length of object full name description. + }; + + /* + ** Button enumerations: + */ + enum { + EVENT_LIST=100, // Primary event list. + EVENT_LIST2, // Secondary event list. + ACTION_LIST, // Primary action list. + ACTION_LIST2, // Secondary action list. + NAME_EDIT, // Trigger name edit field. + DATA_SPEECH1, // Primary action speech. + DATA_SPEECH2, // Secondary action speech. + DATA_THEME1, // Primary action theme. + DATA_THEME2, // Secondary action theme. + DATA_MOVIE1, // Primary action movie. + DATA_MOVIE2, // Secondary action movie. + DATA_SOUND1, // Primary action sound effect. + DATA_SOUND2, // Secondary action sound effect. + DATA_SPECIAL1, // Primary action special weapon. + DATA_SPECIAL2, // Secondary action special weapon. + DATA_EDIT, // Primary event waypoint data field. + DATA_EDIT2, // Secondary event waypoint data field. + DATA_EDIT3, // Primary action waypoint data field. + DATA_EDIT4, // Secondary action waypoint data field. + DATA_HTYPE1, // Primary event house choice list. + DATA_HTYPE2, // Secondary event house choice list. + DATA_HTYPE3, // Primary action house choice list. + DATA_HTYPE4, // Secondary action house choice list. + DATA_BOOLTYPE1, // Primary action boolean data list. + DATA_BOOLTYPE2, // Secondary action boolean data list. + DATA_GENERAL1, // Primary event general data field. + DATA_GENERAL2, // Secondary event general data field. + DATA_GENERAL3, // Primary action general data field. + DATA_GENERAL4, // Secondary action general data field. + DATA_BTYPE1, // Primary event building type list. + DATA_BTYPE2, // Secondary event building type list. + DATA_ITYPE1, // Primary event infantry type list. + DATA_ITYPE2, // Secondary event infantry type list. + DATA_ATYPE1, // Primary event aircraft type list. + DATA_ATYPE2, // Secondary event aircraft type list. + DATA_UTYPE1, // Primary event unit type list. + DATA_UTYPE2, // Secondary event unit type list. + DATA_TTYPE1, // Primary event team type entry list. + DATA_TTYPE2, // Secondary event team type entry list. + DATA_TTYPE3, // Primary action team type entry list. + DATA_TTYPE4, // Secondary action team type entry list. + DATA_TRTYPE1, // Primary action trigger list. + DATA_TRTYPE2, // Secondary action trigger list. + BUTTON_HOUSE, // House ownership for this trigger. + BUTTON_PERSISTANCE, // Persistence of this trigger. + BUTTON_OK, // Ok button - save and exit. + BUTTON_CANCEL, // Cancel button - just exit. + BUTTON_ACTION, // Multiple action control button. + BUTTON_EVENT, // Multiple event control button. + }; + + /* + ** Dialog variables: + */ + bool cancel = false; // true = user cancels + int i; // loop counter + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + /* + ** List of events allowed. + */ + char eventtext[ENTRY_SIZE] = ""; + TDropListClass event1list(EVENT_LIST, eventtext, sizeof(eventtext), + TPF_EFNT | TPF_NOSHADOW, + E1_X, E1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char event2text[ENTRY_SIZE] = ""; + TDropListClass event2list(EVENT_LIST2, event2text, sizeof(event2text), + TPF_EFNT | TPF_NOSHADOW, + E2_X, E2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TEventType event = TEVENT_FIRST; event < TEVENT_COUNT; event++) { + event1list.Add_Item(&EventChoices[event]); + event2list.Add_Item(&EventChoices[event]); + } + + PBubble_Sort(&event1list[0], event1list.Count()); + PBubble_Sort(&event2list[0], event2list.Count()); + + if (Event1.Event == TEVENT_NONE) Event1.Event = TEVENT_FIRST; + event1list.Set_Selected_Index(&EventChoices[Event1.Event]); + if (Event2.Event == TEVENT_NONE) Event2.Event = TEVENT_FIRST; + event2list.Set_Selected_Index(&EventChoices[Event2.Event]); + + /* + ** List of actions allowed. + */ + char actiontext[ENTRY_SIZE] = ""; + TDropListClass action1list(ACTION_LIST, actiontext, sizeof(actiontext), + TPF_EFNT | TPF_NOSHADOW, + A1_X, A1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char action2text[ENTRY_SIZE] = ""; + TDropListClass action2list(ACTION_LIST2, action2text, sizeof(action2text), + TPF_EFNT | TPF_NOSHADOW, + A2_X, A2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TActionType action = TACTION_FIRST; action < TACTION_COUNT; action++) { + action1list.Add_Item(&ActionChoices[action]); + action2list.Add_Item(&ActionChoices[action]); + } + + PBubble_Sort(&action1list[0], action1list.Count()); + PBubble_Sort(&action2list[0], action2list.Count()); + + if (Action1.Action == ACTION_NONE) Action1.Action = TACTION_FIRST; + action1list.Set_Selected_Index(&ActionChoices[Action1.Action]); + if (Action2.Action == ACTION_NONE) Action2.Action = TACTION_FIRST; + action2list.Set_Selected_Index(&ActionChoices[Action2.Action]); + + /* + ** Optional waypoint entry field. + */ + char way1[WAYPOINT_SIZE] = "A"; + EditClass way1data(DATA_EDIT, way1, sizeof(way1), TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event1.Event) == NEED_WAYPOINT) { + if (Event1.Data.Value < 26) { + sprintf(way1data.Get_Text(), "%c", Event1.Data.Value + 'A'); + } else { + sprintf(way1data.Get_Text(), "%c%c", (Event1.Data.Value / 26) + 'A'-1, (Event1.Data.Value % 26) + 'A'); + } + } + + char way2[WAYPOINT_SIZE] = "A"; + EditClass way2data(DATA_EDIT2, way2, sizeof(way2), TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event2.Event) == NEED_WAYPOINT) { + if (Event2.Data.Value < 26) { + sprintf(way2data.Get_Text(), "%c", Event2.Data.Value + 'A'); + } else { + sprintf(way2data.Get_Text(), "%c%c", (Event2.Data.Value / 26) + 'A'-1, (Event2.Data.Value % 26) + 'A'); + } + } + + char way3[WAYPOINT_SIZE] = "A"; + EditClass way3data(DATA_EDIT3, way3, sizeof(way3), TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action1.Action) == NEED_WAYPOINT) { + if (Action1.Data.Value < 26) { + sprintf(way3data.Get_Text(), "%c", Action1.Data.Value + 'A'); + } else { + sprintf(way3data.Get_Text(), "%c%c", (Action1.Data.Value / 26) + 'A'-1, (Action1.Data.Value % 26) + 'A'); + } + } + + char way4[WAYPOINT_SIZE] = "A"; + EditClass way4data(DATA_EDIT4, way4, sizeof(way4), TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action2.Action) == NEED_WAYPOINT) { + if (Action2.Data.Value < 26) { + sprintf(way4data.Get_Text(), "%c", Action2.Data.Value + 'A'); + } else { + sprintf(way4data.Get_Text(), "%c%c", (Action2.Data.Value / 26) + 'A'-1, (Action2.Data.Value % 26) + 'A'); + } + } + + /* + ** Optional event data entry field. + */ + char databuf1[GENERAL_SIZE] = ""; + EditClass event1data(DATA_GENERAL1, databuf1, sizeof(databuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event1.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event1data.Get_Text(), "%d", Event1.Data.Value); + break; + } + + char databuf2[GENERAL_SIZE] = ""; + EditClass event2data(DATA_GENERAL2, databuf2, sizeof(databuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event2.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event2data.Get_Text(), "%d", Event2.Data.Value); + break; + } + + char actionbuf1[GENERAL_SIZE] = ""; + EditClass action1data(DATA_GENERAL3, actionbuf1, sizeof(actionbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action1.Action)) { + case NEED_NUMBER: + sprintf(action1data.Get_Text(), "%d", Action1.Data.Value); + break; + } + + char actionbuf2[GENERAL_SIZE] = ""; + EditClass action2data(DATA_GENERAL4, actionbuf2, sizeof(actionbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action2.Action)) { + case NEED_NUMBER: + sprintf(action2data.Get_Text(), "%d", Action2.Data.Value); + break; + } + + /* + ** Optional team entry list. + */ + char tbuf1[TEAM_SIZE] = ""; + DropListClass ttype1list(DATA_TTYPE1, tbuf1, sizeof(tbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf2[TEAM_SIZE] = ""; + DropListClass ttype2list(DATA_TTYPE2, tbuf2, sizeof(tbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf3[TEAM_SIZE] = ""; + DropListClass ttype3list(DATA_TTYPE3, tbuf3, sizeof(tbuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf4[TEAM_SIZE] = ""; + DropListClass ttype4list(DATA_TTYPE4, tbuf4, sizeof(tbuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int index = 0; index < TeamTypes.Count(); index++) { + ttype1list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype2list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype3list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype4list.Add_Item(TeamTypes.Ptr(index)->IniName); + } + + if (Event1.Team.Is_Valid()) { + ttype1list.Set_Selected_Index(Event1.Team->IniName); + } else { + ttype1list.Set_Selected_Index(0); + } + if (Event2.Team.Is_Valid()) { + ttype2list.Set_Selected_Index(Event2.Team->IniName); + } else { + ttype2list.Set_Selected_Index(0); + } + if (Action1.Team.Is_Valid()) { + ttype3list.Set_Selected_Index(Action1.Team->IniName); + } else { + ttype3list.Set_Selected_Index(0); + } + if (Action2.Team.Is_Valid()) { + ttype4list.Set_Selected_Index(Action2.Team->IniName); + } else { + ttype4list.Set_Selected_Index(0); + } + + /* + ** Optional trigger entry list. + */ + char trbuf1[TEAM_SIZE] = ""; + DropListClass trtype1list(DATA_TRTYPE1, trbuf1, sizeof(trbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char trbuf2[TEAM_SIZE] = ""; + DropListClass trtype2list(DATA_TRTYPE2, trbuf2, sizeof(trbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (index = 0; index < TriggerTypes.Count(); index++) { + trtype1list.Add_Item(TriggerTypes.Ptr(index)->IniName); + trtype2list.Add_Item(TriggerTypes.Ptr(index)->IniName); + } + + if (Action1.Trigger.Is_Valid()) { + trtype1list.Set_Selected_Index(Action1.Trigger->IniName); + } else { + trtype1list.Set_Selected_Index(0); + } + if (Action2.Trigger.Is_Valid()) { + trtype2list.Set_Selected_Index(Action2.Trigger->IniName); + } else { + trtype2list.Set_Selected_Index(0); + } + + /* + ** Optional boolean value list. + */ + char boolbuf1[TEAM_SIZE] = ""; + DropListClass booltype1list(DATA_BOOLTYPE1, boolbuf1, sizeof(boolbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char boolbuf2[TEAM_SIZE] = ""; + DropListClass booltype2list(DATA_BOOLTYPE2, boolbuf2, sizeof(boolbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + booltype1list.Add_Item("OFF"); + booltype1list.Add_Item("ON"); + booltype2list.Add_Item("OFF"); + booltype2list.Add_Item("ON"); + + booltype1list.Set_Selected_Index(Action1.Data.Bool); + booltype2list.Set_Selected_Index(Action2.Data.Bool); + + /* + ** Optional musical theme choice list. + */ + char themebuf1[DESC_SIZE] = ""; + DropListClass themetype1list(DATA_THEME1, themebuf1, sizeof(themebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char themebuf2[DESC_SIZE] = ""; + DropListClass themetype2list(DATA_THEME2, themebuf2, sizeof(themebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + themetype1list.Add_Item(Theme.Full_Name(theme)); + themetype2list.Add_Item(Theme.Full_Name(theme)); + } + + if (Action_Needs(Action1.Action) == NEED_THEME) { + themetype1list.Set_Selected_Index(Action1.Data.Theme); + } else { + themetype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_THEME) { + themetype2list.Set_Selected_Index(Action2.Data.Theme); + } else { + themetype2list.Set_Selected_Index(0); + } + + /* + ** Optional movie list. + */ + char moviebuf1[DESC_SIZE] = ""; + DropListClass movietype1list(DATA_MOVIE1, moviebuf1, sizeof(moviebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char moviebuf2[DESC_SIZE] = ""; + DropListClass movietype2list(DATA_MOVIE2, moviebuf2, sizeof(moviebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VQType movie = VQ_FIRST; movie < VQ_COUNT; movie++) { + movietype1list.Add_Item(VQName[movie]); + movietype2list.Add_Item(VQName[movie]); + } + + if (Action_Needs(Action1.Action) == NEED_MOVIE) { + movietype1list.Set_Selected_Index(Action1.Data.Movie); + } else { + movietype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_MOVIE) { + movietype2list.Set_Selected_Index(Action2.Data.Movie); + } else { + movietype2list.Set_Selected_Index(0); + } + + /* + ** Optional sound effect list. + */ + char soundbuf1[DESC_SIZE] = ""; + DropListClass soundtype1list(DATA_SOUND1, soundbuf1, sizeof(soundbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char soundbuf2[DESC_SIZE] = ""; + DropListClass soundtype2list(DATA_SOUND2, soundbuf2, sizeof(soundbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VocType sound = VOC_FIRST; sound < VOC_COUNT; sound++) { + soundtype1list.Add_Item(Voc_Name(sound)); + soundtype2list.Add_Item(Voc_Name(sound)); + } + + if (Action_Needs(Action1.Action) == NEED_SOUND) { + soundtype1list.Set_Selected_Index(Action1.Data.Sound); + } else { + soundtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SOUND) { + soundtype2list.Set_Selected_Index(Action2.Data.Sound); + } else { + soundtype2list.Set_Selected_Index(0); + } + + /* + ** Optional speech effect list. + */ + char speechbuf1[DESC_SIZE] = ""; + DropListClass speechtype1list(DATA_SPEECH1, speechbuf1, sizeof(speechbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char speechbuf2[DESC_SIZE] = ""; + DropListClass speechtype2list(DATA_SPEECH2, speechbuf2, sizeof(speechbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VoxType speech = VOX_FIRST; speech < VOX_COUNT; speech++) { + speechtype1list.Add_Item(Speech_Name(speech)); + speechtype2list.Add_Item(Speech_Name(speech)); + } + + if (Action_Needs(Action1.Action) == NEED_SPEECH) { + speechtype1list.Set_Selected_Index(Action1.Data.Speech); + } else { + speechtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SPEECH) { + speechtype2list.Set_Selected_Index(Action2.Data.Speech); + } else { + speechtype2list.Set_Selected_Index(0); + } + + /* + ** Optional building type entry list. + */ + char bbuf1[DESC_SIZE] = ""; + DropListClass btype1list(DATA_BTYPE1, bbuf1, sizeof(bbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char bbuf2[DESC_SIZE] = ""; + DropListClass btype2list(DATA_BTYPE2, bbuf2, sizeof(bbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (StructType ss = STRUCT_FIRST; ss < STRUCT_COUNT; ss++) { + btype1list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + btype2list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_STRUCTURE) { + btype1list.Set_Selected_Index(Event1.Data.Structure); + } else { + btype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_STRUCTURE) { + btype2list.Set_Selected_Index(Event2.Data.Structure); + } else { + btype2list.Set_Selected_Index(0); + } + + /* + ** Optional infantry type entry list. + */ + char ibuf1[DESC_SIZE] = ""; + DropListClass itype1list(DATA_ITYPE1, ibuf1, sizeof(ibuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ibuf2[DESC_SIZE] = ""; + DropListClass itype2list(DATA_ITYPE2, ibuf2, sizeof(ibuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (InfantryType ii = INFANTRY_FIRST; ii < INFANTRY_COUNT; ii++) { + itype1list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + itype2list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_INFANTRY) { + itype1list.Set_Selected_Index(Event1.Data.Infantry); + } else { + itype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_INFANTRY) { + itype2list.Set_Selected_Index(Event2.Data.Infantry); + } else { + itype2list.Set_Selected_Index(0); + } + + /* + ** Optional aircraft type entry list. + */ + char abuf1[DESC_SIZE] = ""; + DropListClass atype1list(DATA_ATYPE1, abuf1, sizeof(abuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char abuf2[DESC_SIZE] = ""; + DropListClass atype2list(DATA_ATYPE2, abuf2, sizeof(abuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (AircraftType aa = AIRCRAFT_FIRST; aa < AIRCRAFT_COUNT; aa++) { + atype1list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + atype2list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_AIRCRAFT) { + atype1list.Set_Selected_Index(Event1.Data.Aircraft); + } else { + atype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_AIRCRAFT) { + atype2list.Set_Selected_Index(Event2.Data.Aircraft); + } else { + atype2list.Set_Selected_Index(0); + } + + /* + ** Optional unit type entry list. + */ + char ubuf1[DESC_SIZE] = ""; + DropListClass utype1list(DATA_UTYPE1, ubuf1, sizeof(ubuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ubuf2[DESC_SIZE] = ""; + DropListClass utype2list(DATA_UTYPE2, ubuf2, sizeof(ubuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (UnitType uu = UNIT_FIRST; uu < UNIT_COUNT; uu++) { + utype1list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + utype2list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_UNIT) { + utype1list.Set_Selected_Index(Event1.Data.Unit); + } else { + utype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_UNIT) { + utype2list.Set_Selected_Index(Event2.Data.Unit); + } else { + utype2list.Set_Selected_Index(0); + } + + /* + ** Optional house type entry list. + */ + char housebuf1[DESC_SIZE] = ""; + DropListClass htype1list(DATA_HTYPE1, housebuf1, sizeof(housebuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf2[DESC_SIZE] = ""; + DropListClass htype2list(DATA_HTYPE2, housebuf2, sizeof(housebuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf3[DESC_SIZE] = ""; + DropListClass htype3list(DATA_HTYPE3, housebuf3, sizeof(housebuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf4[DESC_SIZE] = ""; + DropListClass htype4list(DATA_HTYPE4, housebuf4, sizeof(housebuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (HousesType hh = HOUSE_FIRST; hh < HOUSE_COUNT; hh++) { + htype1list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype2list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype3list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype4list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + } + + if (Event_Needs(Event1.Event) == NEED_HOUSE) { + htype1list.Set_Selected_Index(Event1.Data.House); + } else { + htype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_HOUSE) { + htype2list.Set_Selected_Index(Event2.Data.House); + } else { + htype2list.Set_Selected_Index(0); + } + if (Action_Needs(Action1.Action) == NEED_HOUSE) { + htype3list.Set_Selected_Index(Action1.Data.House); + } else { + htype3list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_HOUSE) { + htype4list.Set_Selected_Index(Action2.Data.House); + } else { + htype4list.Set_Selected_Index(0); + } + + /* + ** Optional special weapon list. + */ + char special1[DESC_SIZE] = ""; + DropListClass spc1(DATA_SPECIAL1, special1, sizeof(special1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char special2[DESC_SIZE] = ""; + DropListClass spc2(DATA_SPECIAL2, special2, sizeof(special2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SpecialWeaponType spec = SPC_FIRST; spec < SPC_COUNT; spec++) { + spc1.Add_Item(SpecialWeaponName[spec]); + spc2.Add_Item(SpecialWeaponName[spec]); + } + if ((unsigned)Action1.Data.Special < SPC_COUNT) { + spc1.Set_Selected_Index(Action1.Data.Special); + } else { + spc1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Special < SPC_COUNT) { + spc2.Set_Selected_Index(Action2.Data.Special); + } else { + spc2.Set_Selected_Index(0); + } + + /* + ** Optional quarry type. + */ + char quarry1[DESC_SIZE] = ""; + DropListClass qlist1(DATA_SPECIAL1, quarry1, sizeof(quarry1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char quarry2[DESC_SIZE] = ""; + DropListClass qlist2(DATA_SPECIAL2, quarry2, sizeof(quarry2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist1.Add_Item(QuarryName[q]); + qlist2.Add_Item(QuarryName[q]); + } + if ((unsigned)Action1.Data.Quarry < QUARRY_COUNT) { + qlist1.Set_Selected_Index(Action1.Data.Quarry); + } else { + qlist1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Quarry < QUARRY_COUNT) { + qlist2.Set_Selected_Index(Action2.Data.Quarry); + } else { + qlist2.Set_Selected_Index(0); + } + + /* + ** Name of this trigger text edit field. + */ + char namebuf[5] = ""; + EditClass name_edt(NAME_EDIT, namebuf, sizeof(namebuf), TPF_EFNT | TPF_NOSHADOW, D_DIALOG_X+40, D_DIALOG_Y+30, 40, 9, EditClass::ALPHANUMERIC); + strcpy(namebuf, IniName); // Name + + /* + ** Create the list of house's allowed for trigger. + */ + char housetext[DESC_SIZE] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X+name_edt.Width+20, name_edt.Y, 95, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + + /* + ** Must match order and number of PersistantType specified in + ** TriggerTypeClass definition. + */ + char perstext[DESC_SIZE] = ""; + static char * _perstext[3] = { + "Volatile", + "Semi-persistent", + "Persistent" + }; + DropListClass persbtn(BUTTON_PERSISTANCE, perstext, sizeof(perstext), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X+housebtn.Width+20, housebtn.Y, 105, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (i = 0; i < sizeof(_perstext)/sizeof(_perstext[0]); i++) { + persbtn.Add_Item(_perstext[i]); + } + persbtn.Set_Selected_Index(IsPersistant); + + /* + ** This button controls the existence and relationship of a second trigger + ** event. + */ + int eventflag = EventControl; + TextButtonClass eventbtn(BUTTON_EVENT, TXT_TRIGGER_JUST_EVENT, TPF_EBUTTON, event1list.X, event1list.Y+11, 100, 9); + + /* + ** This button controls the existence of a secondary action. + */ + bool actionflag = ActionControl; + TextButtonClass actionbtn(BUTTON_ACTION, TXT_TRIGGER_JUST_ACTION, TPF_EBUTTON, action1list.X, action1list.Y+11, 100, 9); + + /* + ** Create the ubiquitous OK and Cancel buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_DIALOG_X+35, D_DIALOG_Y+D_DIALOG_H-30, 45, 9); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_DIALOG_X+D_DIALOG_W-80, D_DIALOG_Y+D_DIALOG_H-30, 45, 9); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + event1list.Add_Tail(*commands); + action1list.Add_Tail(*commands); + eventbtn.Add_Tail(*commands); + actionbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + persbtn.Add_Tail(*commands); + housebtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Trigger Event:", event1list.X, event1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Action to Perform:", action1list.X, action1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Persistence:", persbtn.X, persbtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + if (eventflag == 3) { + LogicPage->Draw_Line(event1list.X-1, event1list.Y+3, event1list.X-2*RESFACTOR, event1list.Y+3, WHITE); + LogicPage->Draw_Line(event1list.X-2*RESFACTOR, event1list.Y+3, action1list.X-2*RESFACTOR, action1list.Y+3, WHITE); + LogicPage->Draw_Line(action1list.X-1, action1list.Y+3, action1list.X-2*RESFACTOR, action1list.Y+3, WHITE); + + LogicPage->Draw_Line(event2list.X-1, event2list.Y+3, event2list.X-5*RESFACTOR, event2list.Y+3, WHITE); + LogicPage->Draw_Line(event2list.X-5*RESFACTOR, event2list.Y+3, action2list.X-5*RESFACTOR, action2list.Y+3, WHITE); + LogicPage->Draw_Line(action2list.X-1, action2list.Y+3, action2list.X-5*RESFACTOR, action2list.Y+3, WHITE); + } + + /* + ** Adjust the button list to match current control settings. + */ + event2list.Remove(); + switch (eventflag) { + case 0: + eventbtn.Set_Text(TXT_TRIGGER_JUST_EVENT); + break; + + case 1: + eventbtn.Set_Text(TXT_TRIGGER_AND); + event2list.Add(*commands); + break; + + case 2: + eventbtn.Set_Text(TXT_TRIGGER_OR); + event2list.Add(*commands); + break; + + case 3: + eventbtn.Set_Text(TXT_TRIGGER_LINKED); + event2list.Add(*commands); + break; + } + + /* + ** Prepare the primary event data field. + */ + htype1list.Remove(); + way1data.Remove(); + event1data.Remove(); + btype1list.Remove(); + itype1list.Remove(); + atype1list.Remove(); + utype1list.Remove(); + ttype1list.Remove(); + switch (Event_Needs(*event1list.Current_Item())) { + case NEED_HOUSE: + htype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype1list.Add(*commands); + break; + + case NEED_WAYPOINT: + way1data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event1data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype1list.Add(*commands); + break; + + case NEED_INFANTRY: + itype1list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype1list.Add(*commands); + break; + + case NEED_UNIT: + utype1list.Add(*commands); + break; + + default: + break; + } + + /* + ** Prepare the secondary event data field. + */ + htype2list.Remove(); + way2data.Remove(); + event2data.Remove(); + btype2list.Remove(); + itype2list.Remove(); + atype2list.Remove(); + utype2list.Remove(); + ttype2list.Remove(); + if (commands->Extract_Gadget(EVENT_LIST2)) { + switch (Event_Needs(*event2list.Current_Item())) { + case NEED_HOUSE: + htype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype2list.Add(*commands); + break; + + case NEED_WAYPOINT: + way2data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event2data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype2list.Add(*commands); + break; + + case NEED_INFANTRY: + itype2list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype2list.Add(*commands); + break; + + case NEED_UNIT: + utype2list.Add(*commands); + break; + + default: + break; + } + } + + /* + ** Setup the action buttons and associated data entry fields. + */ + actionbtn.Remove(); + action2list.Remove(); + if (eventflag == 3) { + action2list.Add(*commands); + } else { + actionbtn.Add(*commands); + if (actionflag) { + actionbtn.Set_Text(TXT_TRIGGER_AND); + action2list.Add(*commands); + } else { + actionbtn.Set_Text(TXT_TRIGGER_JUST_ACTION); + } + } + + qlist1.Remove(); + spc1.Remove(); + htype3list.Remove(); + booltype1list.Remove(); + trtype1list.Remove(); + way3data.Remove(); + action1data.Remove(); + ttype3list.Remove(); + themetype1list.Remove(); + soundtype1list.Remove(); + movietype1list.Remove(); + speechtype1list.Remove(); + switch (Action_Needs(*action1list.Current_Item())) { + case NEED_MOVIE: + movietype1list.Add(*commands); + break; + + case NEED_SPECIAL: + spc1.Add(*commands); + break; + + case NEED_HOUSE: + htype3list.Add(*commands); + break; + + case NEED_BOOL: + booltype1list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype3list.Add(*commands); + break; + + case NEED_NUMBER: + action1data.Add(*commands); + break; + + case NEED_WAYPOINT: + way3data.Add(*commands); + break; + + case NEED_THEME: + themetype1list.Add(*commands); + break; + + case NEED_SOUND: + soundtype1list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype1list.Add(*commands); + break; + + case NEED_QUARRY: + qlist1.Add(*commands); + break; + } + + qlist2.Remove(); + spc2.Remove(); + htype4list.Remove(); + booltype2list.Remove(); + trtype2list.Remove(); + way4data.Remove(); + action2data.Remove(); + ttype4list.Remove(); + themetype2list.Remove(); + soundtype2list.Remove(); + movietype2list.Remove(); + speechtype2list.Remove(); + if (commands->Extract_Gadget(ACTION_LIST2)) { + switch (Action_Needs(*action2list.Current_Item())) { + case NEED_MOVIE: + movietype2list.Add(*commands); + break; + + case NEED_SPECIAL: + spc2.Add(*commands); + break; + + case NEED_HOUSE: + htype4list.Add(*commands); + break; + + case NEED_BOOL: + booltype2list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype4list.Add(*commands); + break; + + case NEED_NUMBER: + action2data.Add(*commands); + break; + + case NEED_WAYPOINT: + way4data.Add(*commands); + break; + + case NEED_THEME: + themetype2list.Add(*commands); + break; + + case NEED_SOUND: + soundtype2list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype2list.Add(*commands); + break; + + case NEED_QUARRY: + qlist2.Add(*commands); + break; + } + } + + /* + ** Collapse any dropped down list boxes. + */ + spc1.Collapse(); + spc2.Collapse(); + qlist1.Collapse(); + qlist2.Collapse(); + htype1list.Collapse(); + htype2list.Collapse(); + htype3list.Collapse(); + htype4list.Collapse(); + ttype1list.Collapse(); + ttype2list.Collapse(); + ttype3list.Collapse(); + ttype4list.Collapse(); + btype1list.Collapse(); + btype2list.Collapse(); + utype1list.Collapse(); + utype2list.Collapse(); + itype1list.Collapse(); + itype2list.Collapse(); + atype1list.Collapse(); + atype2list.Collapse(); + trtype1list.Collapse(); + trtype2list.Collapse(); + action1list.Collapse(); + action2list.Collapse(); + event1list.Collapse(); + event2list.Collapse(); + housebtn.Collapse(); + persbtn.Collapse(); + booltype1list.Collapse(); + booltype2list.Collapse(); + themetype1list.Collapse(); + themetype2list.Collapse(); + soundtype1list.Collapse(); + soundtype2list.Collapse(); + movietype1list.Collapse(); + movietype2list.Collapse(); + speechtype1list.Collapse(); + speechtype2list.Collapse(); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case BUTTON_EVENT | KN_BUTTON: + eventflag = (eventflag+1) % 4; + display = true; + break; + + case BUTTON_ACTION | KN_BUTTON: + actionflag = (actionflag == false); + display = true; + break; + + case DATA_SPEECH1 | KN_BUTTON: + Speak(VoxType(speechtype1list.Current_Index())); + display = true; + break; + + case DATA_SPEECH2 | KN_BUTTON: + Speak(VoxType(speechtype2list.Current_Index())); + display = true; + break; + + case DATA_SOUND1 | KN_BUTTON: + Sound_Effect(VocType(soundtype1list.Current_Index())); + display = true; + break; + + case DATA_SOUND2 | KN_BUTTON: + Sound_Effect(VocType(soundtype2list.Current_Index())); + display = true; + break; + + /* + ** Transfer all the necessary values from the edit fields into their + ** respective positions within the trigger object. + */ + case KN_RETURN: + case BUTTON_OK | KN_BUTTON: + House = HousesType(housebtn.Current_Index()); + IsPersistant = PersistantType(persbtn.Current_Index()); + if (strlen(namebuf)==0) { + Set_Name("____"); + } else { + Set_Name(namebuf); + } + + /* + ** Primary event specific data retrieval. + */ + EventControl = MultiStyleType(eventflag); + Event1.Event = *event1list.Current_Item(); + switch (Event_Needs(Event1.Event)) { + case NEED_HOUSE: + Event1.Data.House = HousesType(htype1list.Current_Index()); + break; + + case NEED_TIME: + Event1.Data.Value = atoi(event1data.Get_Text()); + break; + + case NEED_NUMBER: + Event1.Data.Value = atoi(event1data.Get_Text()); + break; + + case NEED_STRUCTURE: + Event1.Data.Structure = StructType(btype1list.Current_Index()); + break; + + case NEED_UNIT: + Event1.Data.Unit = UnitType(utype1list.Current_Index()); + break; + + case NEED_INFANTRY: + Event1.Data.Infantry = InfantryType(itype1list.Current_Index()); + break; + + case NEED_AIRCRAFT: + Event1.Data.Aircraft = AircraftType(atype1list.Current_Index()); + break; + + case NEED_WAYPOINT: + Event1.Data.Value = toupper(way1[0]) - 'A'; + if (way1[1] != '\0') { + Event1.Data.Value = (Event1.Data.Value+1)*26; + Event1.Data.Value += toupper(way1[1]) - 'A'; + } + break; + + case NEED_TEAM: + Event1.Team = TeamTypeClass::From_Name(ttype1list.Current_Item()); + break; + } + + /* + ** Secondary event specific data retrieval. + */ + Event2.Event = *event2list.Current_Item(); + switch (Event_Needs(Event2.Event)) { + case NEED_HOUSE: + Event2.Data.House = HousesType(htype2list.Current_Index()); + break; + + case NEED_TIME: + Event2.Data.Value = atoi(event2data.Get_Text()); + break; + + case NEED_NUMBER: + Event2.Data.Value = atoi(event2data.Get_Text()); + break; + + case NEED_STRUCTURE: + Event2.Data.Structure = StructType(btype2list.Current_Index()); + break; + + case NEED_UNIT: + Event2.Data.Unit = UnitType(utype2list.Current_Index()); + break; + + case NEED_INFANTRY: + Event2.Data.Infantry = InfantryType(itype2list.Current_Index()); + break; + + case NEED_AIRCRAFT: + Event2.Data.Aircraft = AircraftType(atype2list.Current_Index()); + break; + + case NEED_WAYPOINT: + Event2.Data.Value = toupper(way2[0]) - 'A'; + if (way2[1] != '\0') { + Event2.Data.Value = (Event2.Data.Value+1)*26; + Event2.Data.Value += toupper(way2[1]) - 'A'; + } + break; + + case NEED_TEAM: + Event2.Team = TeamTypeClass::As_Pointer(ttype2list.Current_Item()); + break; + } + + /* + ** Primary action data retrieval. + */ + ActionControl = MultiStyleType(actionflag); + Action1.Action = *action1list.Current_Item(); + switch (Action_Needs(Action1.Action)) { + case NEED_SPECIAL: + Action1.Data.Special = SpecialWeaponType(spc1.Current_Index()); + break; + + case NEED_HOUSE: + Action1.Data.House = HousesType(htype3list.Current_Index()); + break; + + case NEED_TRIGGER: + Action1.Trigger = TriggerTypeClass::From_Name(trtype1list.Current_Item()); + break; + + case NEED_TEAM: + Action1.Team = TeamTypeClass::From_Name(ttype3list.Current_Item()); + break; + + case NEED_NUMBER: + Action1.Data.Value = atoi(action1data.Get_Text()); + break; + + case NEED_WAYPOINT: + Action1.Data.Value = toupper(way3[0]) - 'A'; + if (way3[1] != '\0') { + Action1.Data.Value = (Action1.Data.Value+1)*26; + Action1.Data.Value += toupper(way3[1]) - 'A'; + } + break; + + case NEED_BOOL: + Action1.Data.Bool = booltype1list.Current_Index(); + break; + + case NEED_THEME: + Action1.Data.Theme = ThemeType(themetype1list.Current_Index()); + break; + + case NEED_SOUND: + Action1.Data.Sound = VocType(soundtype1list.Current_Index()); + break; + + case NEED_MOVIE: + Action1.Data.Movie = VQType(movietype1list.Current_Index()); + break; + + case NEED_SPEECH: + Action1.Data.Speech = VoxType(speechtype1list.Current_Index()); + break; + + case NEED_QUARRY: + Action1.Data.Quarry = QuarryType(qlist1.Current_Index()); + break; + } + + /* + ** Secondary action data retrieval. + */ + Action2.Action = *action2list.Current_Item(); + switch (Action_Needs(Action2.Action)) { + case NEED_SPECIAL: + Action2.Data.Special = SpecialWeaponType(spc2.Current_Index()); + break; + + case NEED_HOUSE: + Action2.Data.House = HousesType(htype4list.Current_Index()); + break; + + case NEED_TRIGGER: + Action2.Trigger = TriggerTypeClass::From_Name(trtype2list.Current_Item()); + break; + + case NEED_TEAM: + Action2.Team = TeamTypeClass::From_Name(ttype4list.Current_Item()); + break; + + case NEED_NUMBER: + Action2.Data.Value = atoi(action2data.Get_Text()); + break; + + case NEED_WAYPOINT: + Action2.Data.Value = toupper(way4[0]) - 'A'; + if (way4[1] != '\0') { + Action2.Data.Value = (Action2.Data.Value+1)*26; + Action2.Data.Value += toupper(way4[1]) - 'A'; + } + break; + + case NEED_BOOL: + Action2.Data.Bool = booltype2list.Current_Index(); + break; + + case NEED_THEME: + Action2.Data.Theme = ThemeType(themetype2list.Current_Index()); + break; + + case NEED_MOVIE: + Action2.Data.Movie = VQType(movietype2list.Current_Index()); + break; + + case NEED_SOUND: + Action2.Data.Sound = VocType(soundtype2list.Current_Index()); + break; + + case NEED_SPEECH: + Action2.Data.Speech = VoxType(speechtype2list.Current_Index()); + break; + + case NEED_QUARRY: + Action2.Data.Quarry = QuarryType(qlist1.Current_Index()); + break; + } + return(true); + + case KN_ESC: + case BUTTON_CANCEL | KN_BUTTON: + process = false; + + /* + ** Always signal a redraw if any of the buttons were touched. This + ** can be determined by examining the button bit flag in the input + ** return value. + */ + default: + if (input & KN_BUTTON) { + display = true; + } + break; + } + } + return(false); +} +#endif + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * * + * This will build a (static) text description of the trigger type. Use this description * + * when displaying this trigger in a list box. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a one line text description of this trigger. * + * * + * WARNINGS: The pointer returned actually points to a static buffer. The pointer is only * + * valid until this routine is called again. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerTypeClass::Description(void) const +{ + static char _buffer[128]; + + char special; + switch (EventControl) { + case MULTI_AND: + special = '&'; + break; + + case MULTI_OR: + special = '|'; + break; + + case MULTI_LINKED: + special = '='; + break; + + default: + special = '.'; + break; + } + + char special2 = '.'; + if (ActionControl == MULTI_AND) { + special2 = '&'; + } + + char tbuf[32]; + char const * added = ""; + switch (Event_Needs(Event1.Event)) { + case NEED_NUMBER: + sprintf(tbuf, "%d", Event1.Data.Value); + added = tbuf; + break; + + case NEED_UNIT: + added = Text_String(UnitTypeClass::As_Reference(Event1.Data.Unit).Full_Name());; + break; + + case NEED_AIRCRAFT: + added = Text_String(AircraftTypeClass::As_Reference(Event1.Data.Aircraft).Full_Name());; + break; + + case NEED_STRUCTURE: + added = Text_String(BuildingTypeClass::As_Reference(Event1.Data.Structure).Full_Name()); + break; + + case NEED_INFANTRY: + added = Text_String(InfantryTypeClass::As_Reference(Event1.Data.Infantry).Full_Name()); + break; + + case NEED_WAYPOINT: + if (Event1.Data.Value < 26) { + sprintf(tbuf, "'%c'", Event1.Data.Value + 'A'); + } else { + sprintf(tbuf, "'%c%c'", (Event1.Data.Value / 26) + 'A'-1, (Event1.Data.Value % 26) + 'A'); + } + added = tbuf; + break; + + default: + break; + } + + /* + ** Persistence indicator value. + */ + char pers = 'V'; + if (IsPersistant == SEMIPERSISTANT) pers = 'S'; + if (IsPersistant == PERSISTANT) pers = 'P'; + + sprintf(_buffer, "%4.4s\t %s %c%c%c %s%s", + IniName, + HouseTypeClass::As_Reference(House).Suffix, + pers, + special, + special2, + Name_From_Event(Event1.Event), + added); + return(_buffer); +} + +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * * + * This routine will examine the trigger events and return with a composit bitfield that * + * indicates what this trigger can be attached to. This is used for trigger placement * + * and logic processing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with AttachType bitfield representing what this trigger can be attached * + * to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +AttachType TriggerTypeClass::Attaches_To(void) const +{ + AttachType attach = ::Attaches_To(Event1.Event); + + if (EventControl != MULTI_ONLY) { + attach = attach | ::Attaches_To(Event2.Event); + } + return(attach); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * This routine reads in the triggers & creates them. Then, other classes can * + * get pointers to the triggers they're linked to. * + * * + * The routine relies on the TeamTypeClasses already being loaded so it can resolve * + * references to teams in this function. * + * * + * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * + * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * + * * + * Object's pointers are set in: * + * InfantryClass::Read_INI() * + * BuildingClass::Read_INI() * + * UnitClass::Read_INI() * + * TerrainClass::Read_INI() * + * The object trigger pointers are cleared in the ObjectClass constructor. * + * * + * The House's EMSListOf triggers is set in this routine, and cleared in the * + * HouseClass::Init() routine. * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This function must be called before any other class's Read_INI. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Read_INI(CCINIClass & ini) +{ + TriggerTypeClass *trigger; // Working trigger pointer. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Create a new trigger. + */ + trigger = new TriggerTypeClass(); + + /* + ** Get the trigger entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** Fill in the trigger. + */ + trigger->Fill_In((char *)entry, buf); + } + + if (NewINIFormat < 2) { + /* + ** Fix up the self-referential trigger pointers. + */ + for (int trig_index = 0; trig_index < TriggerTypes.Count(); trig_index++) { + TriggerTypeClass * trigger = TriggerTypes.Ptr(trig_index); + + char * ptr = (char *)trigger->Action1.Trigger.Raw(); + if (ptr /*&& trigger->Action1.Trigger.Raw() != -1*/) { + trigger->Action1.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + + ptr = (char *)trigger->Action2.Trigger.Raw(); + if (ptr /*&& trigger->Action2.Trigger.Raw() != -1*/) { + trigger->Action2.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + } + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given trigger with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Fill_In(char * name, char * entry) +{ + assert(TriggerTypes.ID(this) == ID); + + /* + ** Set its name. + */ + Set_Name(name); + + IsPersistant = PersistantType(atoi(strtok(entry, ","))); + House = HousesType(atoi(strtok(NULL, ","))); + EventControl = MultiStyleType(atoi(strtok(NULL, ","))); + ActionControl = MultiStyleType(atoi(strtok(NULL, ","))); + + Event1.Read_INI(); + Event2.Read_INI(); + Action1.Read_INI(); + Action2.Read_INI(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * * + * This routine will write out all trigger type objects to the INI database. Any existing * + * trigger types in the database will be cleared out. * + * * + * INPUT: ini -- Reference to the INI database to have the trigger types added. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("Triggers"); + ini.Clear(INI_Name()); + + /* + ** Now write all the trigger data out + */ + for (int index = 0; index < TriggerTypes.Count(); index++) { +// for (int index = TriggerTypes.Count()-1; index >= 0; index--) { + char buf[256]; + TriggerTypeClass * trigger = TriggerTypes.Ptr(index); + + trigger->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), trigger->IniName, buf); + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * * + * This low level routine will take the information in this trigger type and store it * + * into a buffer such that the resultant string can be stored into an INI database for * + * later retrieval. * + * * + * INPUT: buffer -- Pointer to the buffer to store the INI entry string. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the buffer is big enough. Usually 128 bytes is more than sufficient. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Build_INI_Entry(char * buffer) const +{ + /* + ** Build the root portion of the trigger event. + */ + sprintf(buffer, "%d,%d,%d,%d,", IsPersistant, House, EventControl, ActionControl); + + /* + ** Append the event and action values. + */ + buffer += strlen(buffer); + Event1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Event2.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action2.Build_INI_Entry(buffer); +} + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * * + * This routine is called when triggers are assigned to a list box and then must be drawn. * + * It will display an identifying text string with as much information as is useful. * + * * + * INPUT: index -- The index number of this line in the list box. * + * * + * x,y -- The pixel coordinate of the upper left corner of the text box. * + * * + * width,height -- The dimensions of the text box to display the description in. * + * * + * selected -- Is this a selected line? If so, then it should be highlighted. * + * * + * flags -- The text print flags to use to display this text string. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * * + * This routine should be called to initialize the trigger type object system. It should * + * be called when clearing out a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All trigger types will be destroyed by this routine. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Init(void) +{ + TriggerTypes.Free_All(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * * + * Given just an ASCII representation of the trigger type, this routine will return with * + * a pointer to the trigger type it refers to. Typical use of this is when parsing * + * scenario INI files. * + * * + * INPUT: name -- Pointer to the name to use to identify the trigger type class object to * + * be looked up. * + * * + * OUTPUT: Returns with a pointer to the trigger type class object that matches the name * + * specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * TriggerTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int index = 0; index < TriggerTypes.Count(); index++) { + if (stricmp(TriggerTypes.Ptr(index)->Name(), name) == 0) { + return(TriggerTypes.Ptr(index)); + } + } + } + return(NULL); +} diff --git a/REDALERT/TRIGTYPE.H b/REDALERT/TRIGTYPE.H new file mode 100644 index 000000000..cb538492b --- /dev/null +++ b/REDALERT/TRIGTYPE.H @@ -0,0 +1,149 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGTYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef TRIGTYPE_H +#define TRIGTYPE_H + +#include "tevent.h" +#include "taction.h" + +class TriggerClass; + +/* +** There can be multiple trigger events and trigger actions. This enumeration is used to +** indicate if there are multiple events/actions and what their relationship is. +*/ +typedef enum MultiStyleType : unsigned char { + MULTI_ONLY, // "Only" main trigger action/event? + MULTI_AND, // "And" secondary trigger action/event? + MULTI_OR, // "Or" secondary event? + MULTI_LINKED // Cause and effect pairs are linked? +} MultiStyleType; + + +class TriggerTypeClass : public AbstractTypeClass +{ + public: + unsigned IsActive:1; + + typedef enum PersistantType : unsigned char { + VOLATILE = 0, + SEMIPERSISTANT = 1, + PERSISTANT = 2 + } PersistantType; + + /* + ** This flag controls whether the trigger destroys itself after it goes + ** off. + ** 0 = trigger destroys itself immediately after going off, and removes + ** itself from all objects it's attached to + ** 1 = trigger is "Semi-Persistent"; it maintains a count of all objects + ** it's attached to, and only actually "springs" after its been + ** triggered from all the objects; then, it removes itself. + ** 2 = trigger is Fully Persistent; it just won't go away. + */ + PersistantType IsPersistant; + + /* + ** For house-specific events, this is the house for that event. + */ + HousesType House; + + /* + ** Each trigger must have an event which activates it. This is the event that is + ** used to activate this trigger. + */ + TEventClass Event1; + TEventClass Event2; + MultiStyleType EventControl; + + /* + ** This is the action to perform when the trigger event occurs. + */ + TActionClass Action1; + TActionClass Action2; + MultiStyleType ActionControl; + + + TriggerTypeClass(void); + TriggerTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Event1(x), Event2(x), Action1(x), Action2(x) {}; + virtual ~TriggerTypeClass(void); + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all trigger types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + void Fill_In(char * name, char * entry); + void Build_INI_Entry(char * buf) const; + + static char * INI_Name(void) {return "Trigs";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Processing routines + */ + TriggerClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + + /* + ** Utility routines + */ + void Detach(TARGET target, bool all=true); + AttachType Attaches_To(void) const; + TARGET As_Target(void) const; + static TriggerTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; +}; + + +#endif diff --git a/REDALERT/TURRET.CPP b/REDALERT/TURRET.CPP new file mode 100644 index 000000000..7e2f03955 --- /dev/null +++ b/REDALERT/TURRET.CPP @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\turret.cpv 3.1 13 Mar 1996 09:49:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * TurretClass::Unlimbo -- Unlimboes turret object. * + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "turret.h" + + +/*********************************************************************************************** + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * * + * This is the default destructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::~TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * * + * This is the default constructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * * + * This is the normal constructor for the turret class. It merely sets the turret up to * + * face north. * + * * + * INPUT: classid -- The type id for this particular unit. * + * * + * house -- The house that this unit will belong to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(UnitType classid, HousesType house) : + DriveClass(classid, house) +{ +} + + + diff --git a/REDALERT/TURRET.H b/REDALERT/TURRET.H new file mode 100644 index 000000000..e8b3de4da --- /dev/null +++ b/REDALERT/TURRET.H @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\turret.h_v 3.1 13 Mar 1996 09:43:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TURRET_H +#define TURRET_H + +#include "drive.h" + +class TurretClass : public DriveClass +{ + public: + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + TurretClass(UnitType classid, HousesType house); + TurretClass(void); + virtual ~TurretClass(void); +}; + + +#endif diff --git a/REDALERT/TXTLABEL.CPP b/REDALERT/TXTLABEL.CPP new file mode 100644 index 000000000..98c0bbd28 --- /dev/null +++ b/REDALERT/TXTLABEL.CPP @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TXTLABEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextLableClass::Draw_Me -- Graphical update routine * + * TextLableClass::TextLabelClass -- Constructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TextLableClass::TextLabelClass -- Constructor * + * * + * INPUT: * + * txt pointer to text buffer to print from * + * x x-coord for text printing * + * y y-coord for text printing * + * color color to print in * + * style style to print (determines the meaning of x & y) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +TextLabelClass::TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style) : GadgetClass(x, y, 1, 1, 0, 0) +{ + Text = txt; + Color = color; + Style = style; + UserData1 = 0; + UserData2 = 0; + PixWidth = -1; +} + + +/*********************************************************************************************** + * TextLableClass::Draw_Me -- Graphical update routine * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state * + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int TextLabelClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + if (PixWidth == -1) { + Simple_Text_Print(Text, X, Y, Color, TBLACK, Style); +// Fancy_Text_Print(Text, X, Y, Color, TBLACK, Style); + } else { + Conquer_Clip_Text_Print(Text, X, Y, Color, TBLACK, Style, PixWidth); + } + return(true); + } + return(false); +} diff --git a/REDALERT/TXTLABEL.H b/REDALERT/TXTLABEL.H new file mode 100644 index 000000000..66146a00d --- /dev/null +++ b/REDALERT/TXTLABEL.H @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TXTLABEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TXTLABEL_H +#define TXTLABEL_H + +class TextLabelClass : public GadgetClass +{ + public: + /* + ** Constructor/Destructor + */ + TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + /* + ** Sets the displayed text of the label + */ + virtual void Set_Text(char *txt) {Text = txt;}; + + /* + ** General-purpose data fields + */ + unsigned long UserData1; + unsigned long UserData2; + TextPrintType Style; + char *Text; + RemapControlType * Color; + int PixWidth; +}; + +#endif + diff --git a/REDALERT/TXTPRNT.ASM b/REDALERT/TXTPRNT.ASM new file mode 100644 index 000000000..ae4fffcd1 --- /dev/null +++ b/REDALERT/TXTPRNT.ASM @@ -0,0 +1,520 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Print -- Assembly text print to a buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT +.MODEL FLAT + + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +externdef C Buffer_Print : NEAR + +GraphicViewPort STRUCT +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch DD ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ENDS + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +extern C FontPtr:DWORD +extern C FontXSpacing:DWORD +extern C FontYSpacing:DWORD +externdef C ColorXlat:byte + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +;LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + ;DATASEG + .data + +ColorXlat DB 00H,01H,02H,03H,04H,05H,06H,07H + DB 08H,09H,0AH,0BH,0CH,0DH,0EH,0FH + + DB 01H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 02H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 03H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 04H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 05H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 06H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 07H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 08H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 09H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0AH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0BH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0CH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0DH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0EH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0FH + + ;CODESEG + .code + + + +;*************************************************************************** +;* Buffer_Print -- Assembly text print to graphic buffer routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* +Buffer_Print proc C public USES ebx ecx edx esi edi this_object:DWORD, string:DWORD, x_pixel:DWORD, y_pixel:DWORD, fcolor:DWORD, bcolor:DWORD + + ;PROC Buffer_Print C near + ;USES ebx,ecx,edx,esi,edi + + ;ARG this_object:DWORD + ;ARG string:DWORD + ;ARG x_pixel:DWORD + ;ARG y_pixel:DWORD + ;ARG fcolor:DWORD + ;ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this_object] ; get a pointer to dest + mov eax,[ebx].GraphicViewPort.GVPHeight ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[ebx].GraphicViewPort.GVPWidth ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[ebx].GraphicViewPort.GVPXAdd ; add in xadd for bytes_per_line + add eax,[ebx].GraphicViewPort.GVPPitch ; add in pitch for direct daraw + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[ebx].GraphicViewPort.GVPOffset ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,WORD PTR [esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,WORD PTR [esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,WORD PTR [esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,WORD PTR [esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,BYTE PTR [ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov byte ptr [ColorXlat+1],al + mov byte ptr [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov byte ptr [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,BYTE PTR [eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,WORD PTR [esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat ;[ebx] ; get translated color into al + test al,al ; is it transparent black + jnz loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz draw_char ; we are done here, go draw the character. + jmp short loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz next_char ; if no data, go on to next character. + +while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat ;[ebx] ; get new color + + test al,al ; is it a transparent? + jz short skiplo ; skip over write + mov [edi],al ; write it out +skiplo: + inc edi + dec dh ; decrement our width. + jz short nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat ;[ebx] ; get new color + + test al,al ; is it a transparent? + jz short skiphi ; skip over write + mov [edi],al ; write it out +skiphi: + + inc edi + dec dh ; decrement our width. + jnz short while_data ; check if done with width of char + +nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat ;[ebx] ; get translated color into al + test al,al ; is it transparent black + jz next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz next_char ; we are done here, go to the next character. + jmp short loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je lfeed + mov eax,[original_x] +lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp next_char + +overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + +Buffer_Print endp + + + +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + +Get_Font_Palette_Ptr proc C public + + ;GLOBAL C Get_Font_Palette_Ptr:NEAR + + ;PROC Get_Font_Palette_Ptr C near + + mov eax, OFFSET ColorXlat + ret + +Get_Font_Palette_Ptr endp + + +END \ No newline at end of file diff --git a/REDALERT/TYPE.H b/REDALERT/TYPE.H new file mode 100644 index 000000000..fb56bb990 --- /dev/null +++ b/REDALERT/TYPE.H @@ -0,0 +1,2057 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TYPE_H +#define TYPE_H + +#include "mission.h" +#include "target.h" + +class MapEditClass; +class HouseClass; +class WeaponTypeClass; + + +/*************************************************************************** +** This is the abstract type class. It holds information common to all +** objects that might exist. This contains the name of the object type. +*/ +class AbstractTypeClass +{ + public: + + /* + ** This serves to identify the object class. The ID corresponds to the + ** variation number (e.g., UNIT_TANK1, UNIT_TANK2, etc.). + */ + RTTIType RTTI; + int ID; + + /* + ** This is the internal control name of the object. This name does + ** not change regardless of language specified. This is the name + ** used in scenario control files and for other text based unique + ** identification purposes. + */ + char IniName[24]; + + /* + ** The translated (language specific) text name number of this object. + ** This number is used to fetch the object's name from the language + ** text file. Whenever the name of the object needs to be displayed, + ** this is used to determine the text string. + */ + int FullName; + + AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini); + AbstractTypeClass(NoInitClass const & ) {}; + ~AbstractTypeClass(void) {}; + + RTTIType What_Am_I(void) const {return(RTTI);}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Full_Name(void) const; + char const * Name(void) const {return(IniName);} + void Set_Name(char const * buf) const { + strncpy((char *)IniName, buf, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; + }; + virtual int Get_Ownable(void) const; + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} +}; + + +/********************************************************************** +** Each house has certain unalienable characteristics. This structure +** elaborates these. +*/ +class HouseTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the house number (enum). This is a unique identification + ** number for the house. + */ + HousesType House; + + /* + ** This is the filename suffix to use when creating a house specific + ** file name. It is three characters long. + */ + char Suffix[_MAX_EXT]; + + /* + ** This is the "lemon percentage" to use when determining if a particular + ** object owned by this house is to be flagged as a "lemon". Objects so + ** flagged have a greater break-down chance. The percentage is expressed + ** as a fixed point number with 0x000 meaning 0% and 0x100 meaning 100%. + */ + unsigned Lemon; + + /* + ** This points to the default remap table for this house. + */ + PlayerColorType RemapColor; + + /* + ** This is a unique ASCII character used when constructing filenames. It + ** serves a similar purpose as the "Suffix" element, but is only one + ** character long. + */ + char Prefix; + + /* + ** This controls the various general adjustments to the house owned + ** unit and building ratings. The default value for these ratings is + ** a fixed point number of 1.0. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + //------------------------------------------------------------------------ + HouseTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + HouseTypeClass(HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix); + + unsigned char const * Remap_Table(void) const; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static HousesType From_Name(char const * name); + static HouseTypeClass & As_Reference(HousesType house); + static void One_Time(void); + static void Init_Heap(void); + + virtual bool Read_INI(CCINIClass & ini); +}; + + +/*************************************************************************** +** This the the common base class of game objects. Since these values +** represent the unchanging object TYPES, this data is initialized at game +** start and not changed during play. It is "const" data. +*/ +class ObjectTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the base name of the graphic data associated with this object + ** type. If the graphic name is a null string, then there is no graphic + ** associated with this object type. + */ + char GraphicName[_MAX_FNAME]; + + /* + ** Is this object squashable by heavy vehicles? If it is, then the vehicle + ** can travel over this object and destroy it in the process. + */ + unsigned IsCrushable:1; + + /* + ** Does this object type NOT show up on radar scans? If true, then in any + ** radar display, only the underlying ground will be show, not this object. + ** Most terrain falls into this category, but only a few special real units/buildings + ** do. + */ + unsigned IsStealthy:1; + + /* + ** It is legal to "select" some objects in the game. If it is legal to select this + ** object type then this flag will be true. Selected game objects typically display + ** a floating health bar and allows special user I/O control. + */ + unsigned IsSelectable:1; + + /* + ** Can this object be the target of an attack or move command? Typically, only objects + ** that take damage or can be destroyed are allowed to be a target. + */ + unsigned IsLegalTarget:1; + + /* + ** "Insignificant" objects will not be announced when they are destroyed or when they + ** appear. Terrain elements and some lesser vehicles have this characteristic. + */ + unsigned IsInsignificant:1; + + /* + ** Is this object immune to normal combat damage? Rocks and other inert type terrain + ** object are typically of this type. + */ + unsigned IsImmune:1; + + /* + ** "Sentient" objects are ones that have logic AI processing performed on them. All + ** vehicles, buildings, infantry, and aircraft are so flagged. Terrain elements also + ** fall under this category, but only because certain animation effects require this. + */ + unsigned IsSentient:1; + + /* + ** If this object type affects the occupation and collision logic associated with + ** cells, then this flag will be true. Typically, this characteristic is limited + ** to buildings, units, terrain objects, and landed aircraft. + */ + unsigned IsFootprint:1; + + /* + ** The defense of this object is greatly affected by the type of armor + ** it possesses. This value specifies the type of armor. + */ + ArmorType Armor; + + /* + ** This is the maximum strength of this object type. + */ + unsigned short MaxStrength; + + /* + ** These point to the shape imagery for this object type. Since the shape imagery + ** exists in a separate file, the data is filled in after this object is constructed. + ** The "mutable" keyword allows easy modification to this otherwise const object. + */ + void const * ImageData; + + /* + ** Points to the dimension data for each shape in the image list. By using this + ** data, the minimum number of cells will be redrawn when the object changes shape. + */ + Rect * DimensionData; + + /* + ** This points to the radar imagery for this object. + */ + void const * RadarIcon; + + //-------------------------------------------------------------------- + ObjectTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + ObjectTypeClass( RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int fullname, + char const * name + ); + + static void One_Time(void); + + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRYTYPE || RTTI == RTTI_UNITTYPE || RTTI == RTTI_VESSELTYPE || RTTI == RTTI_AIRCRAFTTYPE);}; + char const * Graphic_Name(void) const {if (GraphicName[0] != '\0') return(GraphicName); return(Name());} + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const = 0; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual void const * Get_Cameo_Data(void) const; + void const * Get_Image_Data(void) const {return ImageData;}; + void const * Get_Radar_Data(void) const {return RadarIcon;}; + + #ifdef SCENARIO_EDITOR + virtual void Display(int, int, WindowNumberType, HousesType) const {}; + #endif + + static void const * SelectShapes; + static void const * PipShapes; +}; + + +/*************************************************************************** +** This class is the common data for all objects that can be owned, produced, +** or delivered as reinforcements. These are objects that typically contain +** crews and weapons -- the fighting objects of the game. +*/ +class TechnoTypeClass : public ObjectTypeClass +{ + public: + + /* + ** This controls how this object type is remapped when it is displayed + ** in the sidebar. + */ + RemapType Remap; + + /* + ** Is this object ownable by all sides in a multiplayer game? There are some + ** special case objects that need this override ability. + */ + unsigned IsDoubleOwned:1; + + /* + ** If this object should be completely and always invisible to the enemy, then + ** this flag will be true. + */ + unsigned IsInvisible:1; + + /* + ** If this object can serve as a good leader for a group selected + ** series of objects, then this flag will be true. Unarmed or + ** ability challenged units do not make good leaders. This flag is + ** also used to indicate the primary factory when dealing with + ** buildings. + */ + unsigned IsLeader:1; + + /* + ** Does this object have the ability to detect the presence of a nearby + ** cloaked object? + */ + unsigned IsScanner:1; + + /* + ** If this object is always given its proper name rather than a generic + ** name, then this flag will be true. Typically, civilians and Dr. Moebius + ** fall under this category. + */ + unsigned IsNominal:1; + + /* + ** If the artwork for this object (only for generics) is theater specific, then + ** this flag will be true. Civilian buildings are a good example of this. + */ + unsigned IsTheater:1; + + /* + ** Does this object type contain a rotating turret? Gun emplacements, SAM launchers, + ** and many vehicles contain a turret. If a turret is present, special rendering and + ** combat logic must be performed. + */ + unsigned IsTurretEquipped:1; + + /* + ** Certain objects can be repaired. For buildings, they repair "in place". For units, + ** they must travel to a repair center to be repaired. If this flag is true, then + ** allow the player or computer AI to repair the object. + */ + unsigned IsRepairable:1; + + /* + ** Does this object contain a crew? If it does, then when the object is destroyed, there + ** is a distinct possibility that infantry will "pop out". Only units with crews can + ** become "heros". + */ + unsigned IsCrew:1; + + /* + ** This tells whether this unit should EVER be remapped when it is displayed + ** on the tactical map. Normally, the unit is remapped, but for certain civilian + ** object, remapping is not to be performed, regardless of owner. + */ + unsigned IsRemappable:1; + + /* + ** Is the unit capable of cloaking? Only Stealth Tank can do so now. + */ + unsigned IsCloakable:1; + + /* + ** Can this object self heal up to half strength? Mammoth tanks from C&C had this + ** feature. + */ + unsigned IsSelfHealing:1; + + /* + ** If this object explodes violently when destroyed, then this flag will be true. + ** The type of explosion is based on the warhead type and the damage generated + ** corresponds to the full strength of the object. + */ + unsigned IsExploding:1; + + /* + ** This specifies the zone that an object of this type should recognize. Zones + ** of this type or lower will be considered "possible to travel to". + */ + MZoneType MZone; + + /* + ** When determining threat, the range can be overridden to be the value + ** specified here. Otherwise, the range for enemy scan is equal to the + ** longest weapon range the object has. If the value is zero, then the + ** weapon range is used. + */ + LEPTON ThreatRange; + + /* + ** If this is a transporter object (e.g., hovercraft, chinook, APC), then this + ** value specifies the maximum number of passengers it may carry. + */ + int MaxPassengers; + + /* + ** Most objects have the ability to reveal the terrain around themselves. + ** This sight range (expressed in cell distance) is specified here. If + ** this value is 0, then this unit never reveals terrain. Bullets are + ** typically of this nature. + */ + int SightRange; + + /* + ** This is the credit cost to produce this object (presuming production is + ** allowed). + */ + int Cost; + + /* + ** The tech level that this object can be produced at. + */ + unsigned Level; + + /* + ** This specifies the building prerequisites required before an object + ** of this type can be produced. + */ + long Prerequisite; + + /* + ** The risk and reward values are used to determine targets and paths + ** toward targets. When heading toward a target, a path of least + ** risk will be followed. When picking a target, the object of + ** greatest reward will be selected. The values assigned are + ** arbitrary. + */ + int Risk,Reward; + + /* + ** This value indicates the maximum speed that this object can achieve. + */ + MPHType MaxSpeed; + + /* + ** This indicates the speed (locomotion) type for this unit. Through this + ** value the movement capabilities are deduced. + */ + SpeedType Speed; + + /* + ** This is the maximum number of ammo shots this object can hold. If + ** this number is -1, then this indicates unlimited ammo. + */ + int MaxAmmo; + + /* + ** This is a bit field representing the houses that are allowed to + ** own (by normal means) this particular object type. This value is + ** typically used in production contexts. It is possible for a side + ** to take possession of an object type otherwise not normally allowed. + ** This event usually occurs as a result of capture. + */ + long Ownable; + + /* + ** This is the small icon image that is used to display the object in + ** the sidebar for construction selection purposes. + */ + void const * CameoData; + + /* + ** The number of animation frames allotted to rotation is specified here. + ** For an object that has no rotation, this value will be 1. For normal + ** vehicles this value will be 32. There are some special case units that + ** have intermediate rotation frames. + */ + int Rotation; + + /* + ** This is the rotational speed of the object. This value represents the + ** turret or body rotation speed expresses as 360/256ths rotation steps per + ** game tick. + */ + int ROT; + + /* + ** These are the weapons that this techno object is armed with. + */ + WeaponTypeClass const * PrimaryWeapon; + WeaponTypeClass const * SecondaryWeapon; + + /* + ** These specify the lepton offsets to locate the exact coordinate of the + ** 'tip of the barrel' for the weapon. This is used for generating the bullet + ** at the proper location. + */ + int HorizontalOffset; // Distance to move east (compensates for offsets in animation from center coordinate). + int VerticalOffset; // Distance to move north (compensates for perspective). + int PrimaryOffset; // Offset along turret centerline and facing. + int PrimaryLateral; // Sideways offset from turret centerline and facing. + int SecondaryOffset; + int SecondaryLateral; + + /* + ** Points you're awarded for destroying an object of this type, and + ** points you lose if you lose an object of this type. + */ + int Points; + + //-------------------------------------------------------------------- + TechnoTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed, + int horizontaloffset=0 + ); + + bool Is_Two_Shooter(void) const; + int Legal_Placement(CELL pos) const; + virtual int Raw_Cost(void) const; + virtual int Max_Passengers(void) const {return(MaxPassengers);} + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + virtual void const * Get_Cameo_Data(void) const; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual int Get_Ownable(void) const; + virtual bool Read_INI(CCINIClass & ini); + + /* + ** This is a pointer to the wake shape (as needed by the gunboat). + */ + static void const * WakeShapes; + static void const * TurretShapes; + static void const * SamShapes; + static void const * MGunShapes; +}; + + +/*************************************************************************** +** Building types need some special information custom to buildings. This +** is a derived class that elaborates these additional data elements. +*/ +class BuildingTypeClass : public TechnoTypeClass { + public: + /* + ** Is this building allowed to be considered for building adjacency + ** checking? If false, then building off of (or adjacent to) this building + ** is not considered. + */ + unsigned IsBase:1; + + /* + ** If this building is a fake, this flag will be set. + */ + unsigned IsFake:1; + + /* + ** This flag controls whether the building is equiped with a dirt + ** bib or not. A building with a bib has a dirt patch automatically + ** attached to the structure when it is placed. + */ + unsigned IsBibbed:1; + + /* + ** If this building is a special wall type, such that it exists as a building + ** for purposes of construction but transforms into an overlay wall object when + ** it is placed on the map, then this flag will be true. + */ + unsigned IsWall:1; + + /* + ** Buildings can have either simple or complex damage stages. If simple, + ** then the second to the last frame is the half damage stage, and the last + ** frame is the complete damage stage. For non-simple damage, buildings + ** have a complete animation set for damaged as well as undamaged condition. + ** Turrets, oil pumps, and repair facilities are a few examples. + */ + unsigned IsSimpleDamage:1; + + /* + ** Certain building types can be captures by enemy infantry. For those + ** building types, this flag will be true. Typically, military or hardened + ** structures such as turrets cannot be captured. + */ + unsigned IsCaptureable:1; + + /* + ** If this building really only has cosmetic idle animation, then this flag will be + ** true if this animation should run at a relatively constant rate regardless of game + ** speed setting. + */ + unsigned IsRegulated:1; + + /* + ** Does this building require power to function? Usually, this isn't the case. The building + ** normally either has no effect by power level or is gradually reduced in effectiveness. This + ** flag is for those buildings that completely cease to function when the power drops below + ** full. + */ + unsigned IsPowered:1; + + /* + ** If this flag is true, then the building cannot be sold even if it could have been built. This + ** is especially useful for mines which can be built but cannot be sold. + */ + unsigned IsUnsellable:1; + + /* + ** This is the direction (from the center cell) of the building in order to find a + ** legitimate foundation square. This location will be used for targeting and capture + ** move destination purposes. + */ + FacingType FoundationFace; + + /* + ** Adjacent distance for building next to. + */ + int Adjacent; + + /* + ** This flag specifies the type of object this factory building can "produce". For non + ** factory buildings, this value will be RTTI_NONE. + */ + RTTIType ToBuild; + + /* + ** For building that produce ground units (infantry and vehicles), there is a default + ** exit point defined. This point is where the object is first placed on the map. + ** Typically, this is located next to a door. The unit will then travel on to a clear + ** terrain area and enter normal game processing. + */ + COORDINATE ExitCoordinate; + + /* + ** When determine which cell to head toward when exiting a building, use the + ** list elaborated by this variable. There are directions of exit that are + ** more suitable than others. This list is here to inform the system which + ** directions those are. + */ + short const * ExitList; + + /* + ** This is the structure type identifier. It can serve as a unique + ** identification number for building types. + */ + StructType Type; + + /* + ** This is the starting facing to give this building when it first + ** gets constructed. The facing matches the final stage of the + ** construction animation. + */ + DirType StartFace; + + /* + ** This is the Tiberium storage capacity of the building. The sum of all + ** building's storage capacity is used to determine how much Tiberium can + ** be accumulated. + */ + int Capacity; + + /* + ** Each building type produces and consumes power. These values tell how + ** much. + */ + int Power; + int Drain; + + /* + ** This is the size of the building. This size value is a rough indication + ** of the building's "footprint". + */ + BSizeType Size; + + /********************************************************************** + ** For each stage that a building may be in, its animation is controlled + ** by this structure. It dictates the starting and length of the animation + ** frames needed for the specified state. In addition it specifies how long + ** to delay between changes in animation. With this data it is possible to + ** control the appearance of all normal buildings. Turrets and SAM sites are + ** an exception since their animation is not merely cosmetic. + */ + typedef struct { + int Start; // Starting frame of animation. + int Count; // Number of frames in this animation. + int Rate; // Number of ticks to delay between each frame. + } AnimControlType; + AnimControlType Anims[BSTATE_COUNT]; + + /*--------------------------------------------------------------------------- + ** This is the building type explicit constructor. + */ + BuildingTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + BuildingTypeClass ( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap + ); + operator StructType(void) const {return(Type);}; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BuildingTypeClass & As_Reference(StructType type); + static StructType From_Name(char const * name); + static void Init(TheaterType theater); + static void One_Time(void); + static void Prep_For_Add(void); + + int Width(void) const; + int Height(bool bib=false) const; + + virtual int Full_Name(void) const; + virtual bool Read_INI(CCINIClass & ini); + bool Flush_For_Placement(CELL cell, HouseClass * house) const; + virtual int Cost_Of(void) const; + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual void const * Get_Buildup_Data(void) const {return(BuildupData);}; + + bool Is_Factory(void) const {return(ToBuild != RTTI_NONE);} + virtual int Raw_Cost(void) const; + bool Bib_And_Offset(SmudgeType & bib, CELL & cell) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** Special overlay for the weapons factory. + */ + static void const * WarFactoryOverlay; + + private: + + /* + ** This is a pointer to a list of offsets (from the upper left corner) that + ** are used to indicate the building's "footprint". This footprint is used + ** to determine building placement legality and terrain passibility. + */ + short const * OccupyList; + + /* + ** Buildings can often times overlap a cell but not actually "occupy" it for + ** purposes of movement. This points to a list of offsets that indicate which + ** cells the building has visual overlap but does not occupy. + */ + short const * OverlapList; + + /* + ** The construction animation graphic data pointer is + ** pointed to by this element. + */ + void const * BuildupData; + + void Init_Anim(BStateType state, int start, int count, int rate) const; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class UnitTypeClass : public TechnoTypeClass +{ + public: + /* + ** If this unit can appear out of a crate, then this flag will be true. + */ + unsigned IsCrateGoodie:1; + + /* + ** Can this unit squash infantry? If it can then if the player selects + ** an (enemy) infantry unit as the movement target, it will ride over and + ** squish the infantry unit. + */ + unsigned IsCrusher:1; + + /* + ** Does this unit go into harvesting mode when it stops on a tiberium + ** field? Typically, only one unit does this and that is the harvester. + */ + unsigned IsToHarvest:1; + + /* + ** Some units are equipped with a rotating radar dish. These units have special + ** animation processing. The rotating radar dish is similar to a turret, but + ** always rotates and does not affect combat. + */ + unsigned IsRadarEquipped:1; + + /* + ** If this unit has a firing animation, this flag is true. Infantry and some special + ** vehicles are the ones with firing animations. + */ + unsigned IsFireAnim:1; + + /* + ** Many vehicles have a turret with restricted motion. These vehicles must move the + ** turret into a locked down position while travelling. Rocket launchers and artillery + ** are good examples of this kind of unit. + */ + unsigned IsLockTurret:1; + + /* + ** Is this unit of the humongous size? Harvesters and mobile construction vehicles are + ** of this size. If the vehicle is greater than 24 x 24 but less than 48 x 48, it is + ** considered "Gigundo". + */ + unsigned IsGigundo:1; + + /* + ** Does this unit have a constant animation (like Visceroid?) + */ + unsigned IsAnimating:1; + + /* + ** Does this unit have the ability to jam radar facilities? + */ + unsigned IsJammer:1; + + /* + ** Is this unit a mobile gap generator? + */ + unsigned IsGapper:1; + + /* + ** If this unit cannot fire while moving, then this flag will be + ** true. Such a unit must stop and stabilize for a bit before it + ** can fire. + */ + unsigned IsNoFireWhileMoving:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + UnitType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + UnitTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + UnitTypeClass ( + UnitType type, + int name, + char const * ininame, + AnimType exp, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_goodie, + bool is_nominal, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_insignificant, + bool is_turret_equipped, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_gigundo, + bool is_animating, + bool is_jammer, + bool is_gapper, + int rotation, + int toffset, + MissionType order + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static UnitType From_Name(char const * name); + static UnitTypeClass & As_Reference(UnitType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** The animation stage list for harvester dumping into the refinery. + */ + static const int Harvester_Dump_List[22]; + + /* + ** The animatino stage list for harvester loading up on ore. + */ + static const int Harvester_Load_List[9]; + + /* + ** The number of animation stages when the harvester is loading + ** up on ore in the field. + */ + static const int Harvester_Load_Count; +}; + + +/*************************************************************************** +** This specifies the constant attribute data associated with naval +** vessels. +*/ +class VesselTypeClass : public TechnoTypeClass +{ + public: + /* + ** Does this unit have only 8 facings? Special test units have limited + ** facings. + */ + unsigned IsPieceOfEight:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + VesselType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + VesselTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + VesselTypeClass ( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static VesselType From_Name(char const * name); + static VesselTypeClass & As_Reference(VesselType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + virtual short const * Overlap_List(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class InfantryTypeClass : public TechnoTypeClass +{ + public: + + /* + ** If this civilian infantry type is female, then this flag + ** will be true. This information is used to get the correct + ** voice response. + */ + unsigned IsFemale:1; + + /* + ** Does this infantry unit have crawling animation? If not, then this + ** means that the "crawling" frames are actually running animation frames. + */ + unsigned IsCrawling:1; + + /* + ** For those infantry types that can capture buildings, this flag + ** will be set to true. Typically, this is the engineer. + */ + unsigned IsCapture:1; + + /* + ** For infantry types that will run away from any damage causing + ** events, this flag will be true. Typically, this is so for all + ** civilians as well as the flame thrower guys. + */ + unsigned IsFraidyCat:1; + + /* + ** This flags whether this infantry is actually a civilian. A + ** civilian uses different voice responses, has less ammunition, + ** and runs from danger more often. + */ + unsigned IsCivilian:1; + + /* + ** If the infantry unit is equipped with C4 explosives, then this + ** flag will be true. Such infantry can enter and destroy enemy + ** buildings. + */ + unsigned IsBomber:1; + + /* + ** This flags whether this infantry is actually a dog. A dog + ** uses different voice responses, has no ammo, and runs instead + ** of walks to attack. + */ + unsigned IsDog:1; + + /* + ** This flag specifies whether this infantry type should use the + ** override remap table, instead of the house remap table. This is + ** used to turn the two civilian animations into a veritable smorgasbord + ** of civilian types, for example. + */ + unsigned IsRemapOverride:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + InfantryType Type; + + /* + ** When this infantry unit is loaded onto a transport, then this + ** is the pip shape to use. Primarily, this is a color control. + */ + PipEnum Pip; + + /* + ** This is an array of the various animation frame data for the actions that + ** the infantry may perform. + */ + DoInfoStruct const * DoControls; + + /* + ** Alternate animation info for the 'virtual' window which gets rendered on the GlyphX client. + ** The infantry frames here map to the original TD infantry frames, so a different set is needed depending on whether + ** we are rendering in legacy mode or on the GlyphX client. ST - 9/5/2019 12:17PM + */ + DoInfoStruct const * DoControlsVirtual; + + /* + ** There are certain units with special animation sequences built into the + ** shape file. These values tell how many frames are used for the firing animation. + */ + char FireLaunch; + char ProneLaunch; + + /* + ** This is a pointer to the special override remap table, which is + ** used only in conjunction with the IsRemapOverride flag, and is + ** primarily used for the civilians. + */ + unsigned char const * OverrideRemap; + + /* + ** This is the explicit unit class constructor. + */ + InfantryTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * controls, + DoInfoStruct const * virtual_controls, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap, + int horizontaloffset=0); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static InfantryType From_Name(char const * name); + static InfantryTypeClass & As_Reference(InfantryType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int & width, int & height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual int Full_Name(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/**************************************************************************** +** The various aircraft types are controlled by object types of +** this class. +*/ +class AircraftTypeClass : public TechnoTypeClass +{ + public: + + /* + ** Fixed wing aircraft (ones that cannot hover) have this flag set to true. + ** Such aircraft will not vary speed while it is flying. + */ + unsigned IsFixedWing:1; + + /* + ** Can this aircraft land? If it can land it is presumed to be controllable by the player. + */ + unsigned IsLandable:1; + + /* + ** Does this aircraft have a rotor blade (helicopter) type propulsion? + */ + unsigned IsRotorEquipped:1; // Is a rotor attached? + + /* + ** Is there a custom rotor animation stage set for each facing of the aircraft? + */ + unsigned IsRotorCustom:1; // Custom rotor sets for each facing? + + /* + ** This is the kind of aircraft identifier number. + */ + AircraftType Type; + + /* + ** This specifies the default mission order for this aircraft. Some aircraft default + ** to guard mode (e.g., helicopters) while some default to attack mode (e.g., bombers). + */ + MissionType Mission; + + /* + ** This is the preferred landing building. The aircraft will try to land at the + ** building of this type. + */ + StructType Building; + + /* + ** This is the final approach speed of this aircraft type for landing + ** at an airfield. Most aircraft hit it at full speed, but the MIG is + ** an example of a plane that needs a slower approach speed to hit the + ** airfield. + */ + int LandingSpeed; + + AircraftTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static AircraftType From_Name(char const * name); + static AircraftTypeClass & As_Reference(AircraftType a); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL, HousesType) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual int Max_Pips(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + static void const * LRotorData; + static void const * RRotorData; +}; + + +/*************************************************************************** +** Bullets and other projectiles need some specific information according +** to their type. +*/ +class BulletTypeClass : public ObjectTypeClass +{ + public: + + /* + ** Does this bullet type fly over walls? + */ + unsigned IsHigh:1; + + /* + ** Does this bullet need a shadow drawn under it? Shadowed bullets + ** use the Height value to offset their Y position. + */ + unsigned IsShadow:1; + + /* + ** If this projectile is one that ballistically arcs from ground level, up into the air and + ** then back to the ground, where it explodes. Typical uses of this are for grenades and + ** artillery shells. + */ + unsigned IsArcing:1; + + /* + ** Certain projectiles do not travel horizontally, but rather, vertically -- they drop + ** from a height. Bombs fall into this category and will have this value set to + ** true. Dropping projectiles do not calculate collision with terrain (such as walls). + */ + unsigned IsDropping:1; + + /* + ** Is this projectile invisible? Some bullets and weapon effects are not directly + ** rendered. Small caliber bullets and flame thrower flames are treated like + ** normal projectiles for damage purposes, but are displayed using custom + ** rules. + */ + unsigned IsInvisible:1; + + /* + ** Does this bullet explode when near the target? Some bullets only explode if + ** it actually hits the target. Some explode even if nearby. + */ + unsigned IsProximityArmed:1; + + /* + ** Does this projectile spew puffs of smoke out its tail while it + ** travels? Missiles are prime examples of this projectile type. + */ + unsigned IsFlameEquipped:1; + + /* + ** Should fuel consumption be tracked for this projectile? Rockets are the primary + ** projectile with this characteristic, but even for bullets it should be checked so that + ** bullets don't travel too far. + */ + unsigned IsFueled:1; + + /* + ** Is this projectile without different facing visuals? Most plain bullets do not change + ** visual imagery if their facing changes. Rockets, on the other hand, are equipped with + ** the full 32 facing imagery. + */ + unsigned IsFaceless:1; + + /* + ** If this is a typically inaccurate projectile, then this flag will be true. Artillery + ** is a prime example of this type. + */ + unsigned IsInaccurate:1; + + /* + ** If the bullet contains translucent pixels, then this flag will be true. These + ** translucent pixels really are "shadow" pixels in the same style as the shadow + ** cast by regular ground units. + */ + unsigned IsTranslucent:1; + + /* + ** If this bullet can be fired on aircraft, then this flag will be true. + */ + unsigned IsAntiAircraft:1; + + /* + ** If this bullet can fire upon ground targets, then this flag will be true. + */ + unsigned IsAntiGround:1; + + /* + ** If this bullet can be fired upon submarines (that are submerged), then + ** this flag will be true. + */ + unsigned IsAntiSub:1; + + /* + ** If this bullet should lose strength as it travels toward the target, then + ** this flag will be true. + */ + unsigned IsDegenerate:1; + + /* + ** Does this projectile travel under the water? If so, then its imagery will be modified + ** to look like it is doing so. + */ + unsigned IsSubSurface:1; + + /* + ** If this projectile is equipped with a parachute, then this flag will be set. Parachute + ** bombs are usually the only one with this flag set. + */ + unsigned IsParachuted:1; + + /* + ** Is this unit of the humongous size? Certain very large projectiles have + ** this flag set. Typically, they require a special offset list so that the cells + ** they overlap will be properly redrawn. + */ + unsigned IsGigundo:1; + + /* + ** This element is a unique identification number for the bullet + ** type. + */ + BulletType Type; + + /* + ** This is the rotation speed of the bullet. It only has practical value + ** for those projectiles that performing homing action during flight -- such + ** as with rockets. If the ROT is zero, then no homing is performed. Otherwise + ** the projectile is considered to be a homing type. + */ + unsigned char ROT; + + /* + ** Some projectiles have a built in arming distance that must elapse before the + ** projectile may explode. If this value is non-zero, then this override is + ** applied. + */ + int Arming; + + /* + ** If this bullet is of the tumbling type, then this is the modulo to factor + ** into the game frame when determining what shape number to use for the + ** imagery. + */ + int Tumble; + + //--------------------------------------------------------------------- + BulletTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + BulletTypeClass(char const * name); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BulletTypeClass & As_Reference(BulletType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const {return false;}; + virtual ObjectClass * Create_One_Of(HouseClass *) const {return 0;}; +}; + + +/**************************************************************************** +** These are the different TYPES of terrain objects. Every terrain object must +** be one of these types. +*/ +class TerrainTypeClass : public ObjectTypeClass +{ + public: + /* + ** Which terrain object does this class type represent. + */ + TerrainType Type; + + /* + ** This is the coordinate offset (from upper left) of where the center base + ** position of the terrain object lies. For trees, this would be the base of + ** the trunk. This is used for sorting purposes. + */ + COORDINATE CenterBase; + + /* + ** This is the bitfield control that tells which theater this terrain object is + ** valid for. If the bit (1 << TheaterType) is true, then this terrain object + ** is allowed. + */ + int Theater; + + /* + ** Does this terrain object get placed on the water instead of the ground? + */ + unsigned IsWaterBased:1; + + //---------------------------------------------------------------- + TerrainTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_immune, + bool is_water, + char const * ininame, + int fullname, + short const * occupy, + short const * overlap); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static TerrainType From_Name(char const * name); + static TerrainTypeClass & As_Reference(TerrainType type); + static void Init(TheaterType theater = THEATER_TEMPERATE); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house=HOUSE_NONE) const; + #endif + + private: + short const * Occupy; + short const * Overlap; +}; + + +/**************************************************************************** +** The tile type objects are controlled by this class. It specifies the form +** of the tile set for the specified object as well as other related datum. +** It is derived from the ObjectTypeClass solely for the purpose of scenario +** editing and creation. +*/ +class TemplateTypeClass: public ObjectTypeClass +{ + public: + /* + ** What template is this. + */ + TemplateType Type; + + /* + ** A bitfield container that indicates which theaters this template is allowed + ** in. A bit set in the (1<Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + return(false); +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * * + * This function creates a unit of this type and keeps it in limbo. A pointer to the * + * created unit object is returned. It is presumed that this object will later be * + * unlimboed at the correct time and place. * + * * + * INPUT: house -- Pointer to the house that is to own the unit. * + * * + * OUTPUT: Returns with a pointer to the created unit object. If the unit object * + * could not be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * UnitTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new UnitClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * * + * Use this routine to return a reference to the UnitTypeClass object as indicated by * + * the unit type number specified. * + * * + * INPUT: type -- The unit type number to convert into a UnitTypeClass object reference. * + * * + * OUTPUT: Returns with a reference to the unit type class object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass & UnitTypeClass::As_Reference(UnitType type) +{ + return(*UnitTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * * + * This routine will fill in the width and height for this unit type. This width and * + * height are used to render the selection rectangle and the positioning of the health * + * bargraph. * + * * + * INPUT: width -- Reference to the width of the unit (to be filled in). * + * * + * height -- Reference to the height of the unit (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Dimensions(int &width, int &height) const +{ + width = MaxSize-(MaxSize/4); + width = min(width, 48); + height = MaxSize-(MaxSize/4); + height = min(height, 48); +} + + +/*********************************************************************************************** + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed, but for harvesters, it is the * + * number of credits it holds divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this unit type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int UnitTypeClass::Max_Pips(void) const +{ + if (Type == UNIT_HARVESTER) { + return(7); + } + + if (Type == UNIT_MINELAYER) { + return(MaxAmmo); + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * UnitTypeClass::Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * * + * This routine adjusts the pixel coordinates specified to account for the displacement of * + * the turret on the MLRS and MSAM vehicles. * + * * + * INPUT: dir -- The direction of the body of the vehicle. * + * * + * x,y -- References to the turret center pixel position. These will be modified as * + * necessary. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Turret_Adjust(DirType dir, int &x, int &y) const +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {1,2}, // N + {-1,1}, + {-2,0}, + {-3,0}, + {-3,1}, // NW + {-4,-1}, + {-4,-1}, + {-5,-2}, + {-5,-3}, // W + {-5,-3}, + {-3,-3}, + {-3,-4}, + {-3,-4}, // SW + {-3,-5}, + {-2,-5}, + {-1,-5}, + {0,-5}, // S + {1,-6}, + {2,-5}, + {3,-5}, + {4,-5}, // SE + {6,-4}, + {6,-3}, + {6,-3}, + {6,-3}, // E + {5,-1}, + {5,-1}, + {4,0}, + {3,0}, // NE + {2,0}, + {2,1}, + {1,2} + }; + + int index = 0; + switch (Type) { + case UNIT_JEEP: + y -= 4; + break; + + case UNIT_MGG: + index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Read_INI -- Fetch the unit type data from the INI database. * + * * + * This routine will find the section in the INI database for this unit type object and * + * then fill in the override values specified. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the section for this unit found in the database and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsNoFireWhileMoving = ini.Get_Bool(IniName, "NoMovingFire", IsNoFireWhileMoving); + Speed = ini.Get_Bool(IniName, "Tracked", (Speed == SPEED_TRACK)) ? SPEED_TRACK : SPEED_WHEEL; + + /* + ** If this unit can drive over walls, then mark it as recognizing the crusher zone. + */ + if (MZone < MZONE_CRUSHER && IsCrusher) { + MZone = MZONE_CRUSHER; + } + return(true); + } + return(false); +} diff --git a/REDALERT/UDPADDR.CPP b/REDALERT/UDPADDR.CPP new file mode 100644 index 000000000..61a80e85e --- /dev/null +++ b/REDALERT/UDPADDR.CPP @@ -0,0 +1,234 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + + +#include "function.h" +//#include "_WSProto.h" +#include "WSPUDP.h" + + +bool Get_Broadcast_Addresses (void) +{ + return false; +#if (0)//PG + + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 160 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_ip_address_list_w = 300 *RESFACTOR; + int d_ip_address_list_h = ((20 * 6) + 3) *RESFACTOR; // 6 rows high + int d_ip_address_list_x = d_dialog_cx - d_ip_address_list_w / 2; + int d_ip_address_list_y = d_margin2 + d_dialog_y; + + + int d_ok_w = 40*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_cx + d_dialog_w / 4; + int d_ok_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_dialog_w / 4; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_IPLIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int width; + int height; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String("IP Addresses", SeenBuff.Get_Height(), width, height); + + + GadgetClass *commands; // button list + ColorListClass ip_address_list(BUTTON_IPLIST, d_ip_address_list_x, d_ip_address_list_y, d_ip_address_list_w, d_ip_address_list_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ip_address_list.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + + + /* + ** Add all the ip addresses from the ini file to the list box. + */ + CCINIClass ip_ini; + int res=0; + + if (ip_ini.Load(CCFileClass("IP.INI"), false)) { + int entry=0; + char entry_name[16]; + do { + entry++; + char *temp = new char [128]; + sprintf (entry_name, "%d", entry); + res = ip_ini.Get_String("IP_ADDRESSES", entry_name, "", temp, 128); + if ( res ) { + ip_address_list.Add_Item (temp); +char debug[128]; +sprintf (debug, "RA95 - Adding address %s\n", temp); +WWDebugString (debug); + } + }while (res); + } + ip_address_list.Flag_To_Redraw(); + + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print("IP Addresses", d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + okbtn.Zap(); + cancelbtn.Zap(); + ip_address_list.Zap(); + + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + ip_address_list.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return (false); + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Show_Mouse(); + + for ( int i=0 ; iSet_Broadcast_Address ((char*)temp); + } + + return (true); +#endif +} \ No newline at end of file diff --git a/REDALERT/UNIT.CPP b/REDALERT/UNIT.CPP new file mode 100644 index 000000000..bdcdb6597 --- /dev/null +++ b/REDALERT/UNIT.CPP @@ -0,0 +1,5111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/UNIT.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * UnitClass::AI -- AI processing for the unit. * + * UnitClass::APC_Close_Door -- Closes an APC door. * + * UnitClass::APC_Open_Door -- Opens an APC door. * + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * UnitClass::Click_With -- Handles player map clicking while this unit is selected. * + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * UnitClass::Draw_It -- Draws a unit object. * + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * UnitClass::Init -- Clears all units for scenario preparation. * + * UnitClass::Limbo -- Limbo this unit. * + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * UnitClass::Overlap_List -- Determines overlap list for units. * + * UnitClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * UnitClass::Random_Animate -- Handles random idle animation for the unit. * + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * UnitClass::Set_Speed -- Initiate unit movement physics. * + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * UnitClass::UnitClass -- Constructor for units. * + * UnitClass::Unlimbo -- Removes unit from stasis. * + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * UnitClass::Write_INI -- Store the units to the INI database. * + * UnitClass::delete -- Deletion operator for units. * + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * UnitClass::~UnitClass -- Destructor for unit objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "COORDA.h" + +/*********************************************************************************************** + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * * + * This is a helper routine that modifies the pixel coordinates provided according to the * + * direction specified. The effect is the simulate recoil effects by moving an object 'back'* + * one pixel. Since the pixels moved depend on facing, this routine handles the pixel * + * adjustment quickly. * + * * + * INPUT: dir -- The direction to base the recoil on. * + * * + * x,y -- References to the pixel coordinates that will be adjusted. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Recoil_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {0, 1}, // N + {0, 1}, + {0, 1}, + {-1, 1}, + {-1, 1}, // NE + {-1, 1}, + {-1, 0}, + {-1, 0}, + {-1, 0}, // E + {-1, 0}, + {-1, -1}, + {-1, -1}, + {-1, -1}, // SE + {-1, -1}, + {-1, -1}, + {0, -1}, + {0, -1}, // S + {0, -1}, + {0, -1}, + {1, -1}, + {1, -1}, // SW + {1, -1}, + {1, 0}, + {1, 0}, + {1, 0}, // W + {1, 0}, + {1, 1}, + {1, 1}, + {1, 1}, // NW + {1, 1}, + {0, 1}, + {0, 1} + }; + + int index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * * + * This routine will allocate a unit from the available unit pool and * + * fixup all the access lists to match. It will allocate a unit slot * + * from within the range allowed for the specified unit type. If no * + * slot was found, then it will fail. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + *=============================================================================================*/ +void * UnitClass::operator new(size_t) +{ + void * ptr = Units.Alloc(); + if (ptr != NULL) { + ((UnitClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * UnitClass::delete -- Deletion operator for units. * + * * + * This removes the unit from the local allocation system. Since this * + * is a fixed block of memory, not much has to be done to delete the * + * unit. Merely marking it as inactive is enough. * + * * + * INPUT: ptr -- Pointer to the unit to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((UnitClass *)ptr)->IsActive = false; + } + Units.Free((UnitClass *)ptr); +} + + +/*********************************************************************************************** + * UnitClass::~UnitClass -- Destructor for unit objects. * + * * + * This destructor will lower the unit count for the owning house as well as inform any * + * other units in communication, that this unit is about to leave reality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::~UnitClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * UnitClass::UnitClass -- Constructor for units. * + * * + * This constructor for units will initialize the unit into the game * + * system. It will be placed in all necessary tracking lists. The initial condition will * + * be in a state of limbo. * + * * + * INPUT: classid -- The type of unit to create. * + * * + * house -- The house owner of this unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::UnitClass(UnitType classid, HousesType house) : + DriveClass(RTTI_UNIT, Units.ID(this), house), + Class(UnitTypes.Ptr((int)classid)), + Flagged(HOUSE_NONE), + IsDumping(false), + Gems(0), + Gold(0), + Tiberium(0), + IsToScatter(false), + ShroudBits(0xFFFFFFFFUL), + ShroudCenter(0), + Reload(0), + SecondaryFacing(PrimaryFacing) +{ + Reload = 0; + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + IsCloakable = Class->IsCloakable; + if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3)); + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * * + * This displays the current status of the unit class to the mono monitor. By this display * + * bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Debug_Dump(MonoClass * mono) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_VEHICLE)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Set_Cursor(1, 11);mono->Printf("%03", Gems); + mono->Set_Cursor(7, 11);mono->Printf("%03", Gold); + + mono->Fill_Attrib(66, 13, 12, 1, IsDumping ? MonoClass::INVERSE : MonoClass::NORMAL); + + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * * + * This routine is used by the rendering system in order to sort the * + * game objects in a back to front order. This is now the correct * + * overlap effect is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value that can be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Sort_Y(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * UnitClass::AI -- AI processing for the unit. * + * * + * This routine will perform the AI processing necessary for the unit. These are non- * + * graphic related operations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::AI(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (Height == 0 && !IsDumping && !IsDriving && Is_Door_Closed() /*&& Mission != MISSION_UNLOAD*/) { +// if (MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + DriveClass::AI(); + if (!IsActive || Height > 0) { + return; + } + + /* + ** Hack check to ensure that a harvester won't harvest if it is not harvesting. + */ + if (Mission != MISSION_HARVEST) { + IsHarvesting = false; + } + + /* + ** Handle combat logic for this unit. It will determine if it has a target and + ** if so, if conditions are favorable for firing. When conditions permit, the + ** unit will fire upon its target. + */ + Firing_AI(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!IsActive) { + return; + } +#endif + /* + ** Turret rotation processing. Handles rotating radar dish + ** as well as conventional turrets if present. If no turret present, but + ** it decides that the body should face its target, then body rotation + ** would occur by this process as well. + */ + Rotation_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Units will reload every so often if they are under the burden of + ** being required to reload between shots. + */ + Reload_AI(); + + /* + ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS + ** mission that they can follow. Passenger loading is merely a part of their normal operation. + */ + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) { + APC_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDumping && !IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** A cloaked object that is carrying the flag will always shimmer. + */ + if (Cloak == CLOAKED && Flagged != HOUSE_NONE) { + Do_Shimmer(); + } + + /* + ** Mobile gap generators regenerate their gap every so often (just in case). + */ + if (Class->IsGapper && !IsDriving && (Frame % TICKS_PER_SECOND) == 0) { + Shroud_Regen(); + } +} + + +/*********************************************************************************************** + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * * + * This routine will handle the rotation logic for the unit's turret (if it has one) as * + * well as its normal body shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { + PrimaryFacing.Set_Desired(dir); + } + } + } + + if (Class->IsRadarEquipped) { + Mark(MARK_CHANGE_REDRAW); + SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8)); + Mark(MARK_CHANGE_REDRAW); + } else { + + IsRotating = false; + if (Class->IsTurretEquipped) { + if (IsTurretLockedDown) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + if (!Class->IsRadarEquipped) { + IsRotating = SecondaryFacing.Is_Rotating(); + } + } else { + if (!IsTurretLockedDown && !Target_Legal(TarCom)) { + if (!Target_Legal(NavCom)) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * * + * When a unit leaves the map it will be eliminated. This routine checks for this case * + * and eliminates the unit accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the unit eliminated by this routine? * + * * + * WARNINGS: Be sure to check for the return value and if 'true' abort any further processing* + * of the unit since it is dead. Only call this routine once per unit per * + * game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Edge_Of_World_AI(void) +{ + if (Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * * + * Some units require special reload logic. The V2 rocket launcher in particular. Perform * + * this reload logic with this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Reload_AI(void) +{ + if (*this == UNIT_V2_LAUNCHER && Ammo < Class->MaxAmmo) { + if (IsDriving) { + Reload = Reload + 1; + } else { + if (Reload == 0) { + Ammo++; + if (Ammo < Class->MaxAmmo) { + Reload = TICKS_PER_SECOND*30; + } + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * * + * This routine wil check for and perform any firing logic required of this unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This should be called only once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Firing_AI(void) +{ + if (Target_Legal(TarCom) && Class->PrimaryWeapon != NULL) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + switch (ok) { + case FIRE_OK: + if (!((UnitClass *)this)->Class->IsFireAnim) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Class->IsLockTurret || Class->Type == UNIT_DEMOTRUCK) { +#else + if (Class->IsLockTurret) { +#endif + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a unit receives a radio * + * message. Typical use of this is when a unit unloads from a hover * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + switch (message) { + /* + ** Checks to see if this object is in need of service depot processing. + */ + case RADIO_NEED_REPAIR: + if (!IsDriving && !Target_Legal(NavCom) && (Health_Ratio() >= 1 && (*this != UNIT_MINELAYER || Ammo >= Class->MaxAmmo))) return(RADIO_NEGATIVE); + break; +// return(RADIO_ROGER); + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** The refinery has told this harvester that it should begin the backup procedure + ** so that proper unloading may take place. + */ + case RADIO_BACKUP_NOW: + DriveClass::Receive_Message(from, message, param); + if (!IsRotating && PrimaryFacing != DIR_W) { + Do_Turn(DIR_W); + } else { + if (!IsDriving) { + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (Transmit_Message(RADIO_IM_IN, whom) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOADED, whom); + } + } + } + } + } + return(RADIO_ROGER); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + APC_Close_Door(); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + Do_Turn(dir); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is or needs + ** to rotate. + */ +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_APC || *this == UNIT_PHASE) { +#else + if (*this == UNIT_APC) { +#endif + if (IsRotating) { + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + } else { + if (!Is_Door_Open()) { + APC_Open_Door(); + } + } + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if ( (*this != UNIT_APC && *this != UNIT_PHASE) || Is_Door_Open()) { +#else + if (*this != UNIT_APC || Is_Door_Open()) { +#endif + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * UnitClass::Unlimbo -- Removes unit from stasis. * + * * + * This routine will place a unit into the game and out of its limbo * + * state. This occurs whenever a unit is unloaded from a transport. * + * * + * INPUT: coord -- The coordinate to make the unit appear. * + * * + * dir -- The initial facing to impart upon the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? If the desired * + * coordinate is illegal, then this might very well return * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** All units must start out facing one of the 8 major directions. + */ + dir = Facing_Dir(Dir_Facing(dir)); + if (DriveClass::Unlimbo(coord, dir)) { + + SecondaryFacing = dir; + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->UScan |= (1L << Class->Type); + House->ActiveUScan |= (1L << Class->Type); + + /* + ** If it starts off the edge of the map, then it already starts cloaked. + */ + if (IsCloakable && !IsLocked) Cloak = CLOAKED; + + /* + ** Units default to no special animation. + */ + Set_Rate(0); + Set_Stage(0); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * * + * This routine will inflict the specified number of damage points on * + * the given unit. If the unit is destroyed, then this routine will * + * remove the unit cleanly from the game. The return value indicates * + * whether the unit was destroyed. This will allow appropriate death * + * animation or whatever. * + * * + * INPUT: damage-- The number of damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead--The type of damage to inflict. * + * * + * source -- Who is responsible for this damage? * + * * + * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to * + * RESULT_DESTROYED. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1991 JLB : Created. * + * 07/12/1991 JLB : Script initiated by unit destruction. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Warhead modifier. * + * 06/03/1994 JLB : Added the source of the damage target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 06/30/1995 JLB : Lasers do maximum damage against gunboat. * + * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. * + *=============================================================================================*/ +ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Remember if this object was selected. If it was and it gets destroyed and it has + ** passengers that pop out, then the passengers will inherit the select state. + */ + //bool select = (IsSelected && House->IsPlayerControl); + bool select = (Is_Selected_By_Player() );//&& House->IsPlayerControl); + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = DriveClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + Shroud_Regen(); // remove the shroud if it's a gap generator + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + /* + ** SSM launchers will really explode big if they are carrying + ** missiles at the time of the explosion. + */ + if (*this == UNIT_V2_LAUNCHER && Ammo) { + anim = ANIM_NAPALM3; + } + + new AnimClass(anim, Coord); + + /* + ** Harvesters explode with a force equal to the amount of + ** Tiberium they are carrying. + */ + if (Tiberium > 0 && Rule.IsExplosiveHarvester) { + Wide_Area_Damage(Coord, CELL_LEPTON_W + CELL_LEPTON_W/2, Credit_Load()+Class->MaxStrength, this, WARHEAD_HE); + } + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_The_Screen(3, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(3, source->Owner()); + } + } + } + + /* + ** Possibly have the crew member run away. + */ + CELL cell = Coord_Cell(Center_Coord()); + Mark(MARK_UP); + + if (Class->IsCrew && Class->Max_Passengers() == 0) { + if (Percent_Chance(50)) { + InfantryClass * i = 0; + + if (Class->PrimaryWeapon == NULL) { + i = new InfantryClass(INFANTRY_C1, House->Class->House); + if (i != NULL) i->IsTechnician = true; + } else { + i = new InfantryClass(INFANTRY_E1, House->Class->House); + } + if (i != NULL) { + if (i->Unlimbo(Coord, DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2); + i->Scatter(0, true); + if (!House->IsHuman) { + i->Assign_Mission(MISSION_HUNT); + } else { + i->Assign_Mission(MISSION_GUARD); + } + if (select) i->Select(); + } else { + delete i; + } + } + } + } else { + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + if (object == NULL) break; // How can this happen? + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) { + object->Look(false); + object->Scatter(0, true); + if (select) object->Select(); + } else { + object->Record_The_Kill(source); + delete object; + } + } + } + + /* + ** If this is a truck, there is a possibility that a crate will drop out + ** if the scenario so indicates and there is room. + */ + if (Scen.IsTruckCrate && *this == UNIT_TRUCK) { + cell = Nearby_Location(); + if (cell != 0) { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + } + + if (*this == UNIT_MCV) { + if (House) { + House->Check_Pertinent_Structures(); + } + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking. + */ + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached) { +#ifdef FIXIT_ANTS + if (*this != UNIT_ANT1 && *this != UNIT_ANT2 && *this != UNIT_ANT3) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim) anim->Attach_To(this); +#ifdef FIXIT_ANTS + } +#endif + } + + /* + ** Try to crush anyone that fires on this unit if possible. The harvester + ** typically is the only one that will qualify here. + */ + if (!Team.Is_Valid() && source != NULL && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Rule.IsAutoCrush)) { + + /* + ** Try to crush the attacker if it can be crushed by this unit and this unit is + ** not equipped with a flame type weapon. If this unit has a weapon and the target + ** is not very close, then fire on it instead. In easy mode, they never run over the + ** player. In hard mode, they always do. In normal mode, they only overrun past + ** mission #8. + */ + if (Should_Crush_It(source)) { + Assign_Destination(source->As_Target()); + Assign_Mission(MISSION_MOVE); + } else { + + /* + ** Try to return to base if possible. + */ + if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() <= Rule.ConditionYellow) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + } + } + } + } + + /* + ** Computer controlled harvester will radio for help if they are attacked. + */ + if (*this == UNIT_HARVESTER && !House->IsHuman && source) { + Base_Is_Attacked(source); + } + } + return(res); +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + break; + } + } + + /* + ** Short circuit out if trying to tell a unit to "nomove" to itself. This bypass of the + ** normal active click with logic prevents any disturbance to the vehicle's state. Without + ** this bypass, a unit on a repair bay would stop repairing because it would break radio + ** contact. + */ + if (object == this && action == ACTION_NOMOVE) { + return; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, object); + } +#else + DriveClass::Active_Click_With(action, object); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, cell); + } +#else + DriveClass::Active_Click_With(action, cell); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * * + * This routine is called when the unit completes one mission but does not have a clear * + * follow up mission to perform. In such a case, the unit should enter a default idle * + * state. This idle state varies depending on what the current internal computer * + * settings of the unit is as well as what kind of unit it is. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/03/1994 JLB : Fixed to handle non-combat vehicles. * + * 06/18/1995 JLB : Allows a harvester to stop harvesting. * + *=============================================================================================*/ +void UnitClass::Enter_Idle_Mode(bool initial) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (IsToScatter) { + IsToScatter = false; + Scatter(0, true); + } + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (!Is_Weapon_Equipped()) { + if (Class->IsToHarvest) { + if (!In_Radio_Contact() && Mission != MISSION_HARVEST && MissionQueue != MISSION_HARVEST) { + if (initial || !House->IsHuman || Map[Coord].Land_Type() == LAND_TIBERIUM) { + order = MISSION_HARVEST; + } else { + order = MISSION_GUARD; + } + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } else { + return; + } + } else { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team.Is_Valid()) { + order = MISSION_UNLOAD; + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*this == UNIT_MAD && Mission == MISSION_UNLOAD) { + order = MISSION_UNLOAD; + } else { +#endif + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IQ < Rule.IQGuardArea || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * * + * This routine is used by the MCV to find a clear spot to deploy. If a clear spot * + * is found, then the MCV will assign that location to its navigation computer. This only * + * occurs if the MCV isn't already heading toward a spot. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the located at a spot where it can deploy? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Clear_Spot(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Mark(MARK_UP); + if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Adjacent_Cell(Coord_Cell(Center_Coord()), FACING_NW))) { + Mark(MARK_DOWN); + return(true); + } + + if (!Target_Legal(NavCom)) { + /* + ** This scan table is skewed to north scanning only. This should + ** probably be converted to a more flexible method. + */ + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, +//BG: Added south scanning + MAP_CELL_W*1, + MAP_CELL_W*2, + (MAP_CELL_W*2)+1, + (MAP_CELL_W*2)-1, + MAP_CELL_W*3, + (MAP_CELL_W*3)+1, + (MAP_CELL_W*3)-1, + (MAP_CELL_W*3)+2, + (MAP_CELL_W*3)-2, + MAP_CELL_W*4, + (MAP_CELL_W*4)+1, + (MAP_CELL_W*4)-1, + (MAP_CELL_W*4)+2, + (MAP_CELL_W*4)-2, + +//BG: Added some token east/west scanning + -1,-2,-3,-4, + + 1, 2, 3, 4, + 0 + }; + int * ptr; + + ptr = &_offsets[0]; + while (*ptr) { + CELL cell = Coord_Cell(Coord)+*ptr++; + CELL check_cell = Adjacent_Cell(cell, FACING_NW); + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(check_cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + } + Mark(MARK_DOWN); + + /* + ** If we couldn't find a destination to go to, let's try random movement + ** to see if that brings us to a better spot. + */ + if(!Target_Legal(NavCom) && !House->IsHuman) { + Scatter(0); + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * * + * Certain units have the ability to deploy into a building. When this routine is called * + * for one of those units, it will attempt to deploy at its current location. If the unit * + * is in motion to a destination or it isn't one of the special units that can deploy or * + * it isn't allowed to deploy at this location for some reason it won't deploy. In all * + * other cases, it will begin to deploy and once it begins only a player abort action will * + * stop it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was deployment begun? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Try_To_Deploy(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsRotating) { + if (*this == UNIT_MCV) { + + /* + ** Determine if it is legal to deploy at this location. If not, tell the + ** player. + */ + Mark(MARK_UP); + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + if (PlayerPtr == House) { + Speak(VOX_DEPLOY); + } + if (!House->IsHuman) { + BuildingTypeClass::As_Reference(STRUCT_CONST).Flush_For_Placement(cell, House); + } + Mark(MARK_DOWN); + IsDeploying = false; + return(false); + } + Mark(MARK_DOWN); + + /* + ** If the unit is not facing the correct direction, then start it rotating + ** toward the right facing, but still flag it as if it had deployed. This is + ** because it will deploy as soon as it reaches the correct facing. + */ + if (PrimaryFacing.Current() != DIR_SW) { + Do_Turn(DIR_SW); +// PrimaryFacing.Set_Desired(DIR_SW); + IsDeploying = true; + return(true); + } + + /* + ** Since the unit is already facing the correct direction, actually do the + ** deploy logic. If for some reason this cannot occur, then don't delete the + ** unit, just mark it as not deploying. + */ + Mark(MARK_UP); + BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House); + if (building != NULL) { + if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) { + + /* + ** Play the buildup sound for the player if this is the players + ** MCV. + */ + if (building->House == PlayerPtr) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN, Center_Coord()); + } else { + building->IsToRebuild = true; + building->IsToRepair = true; + } + + /* + ** Always reveal the construction yard to the player that owned the + ** mobile construction vehicle. + */ + building->Revealed(House); + + /* + ** When the MCV deploys, always consider production to have started + ** for the owning house. This ensures that in multiplay, computer + ** opponents will begin construction as soon as they start their + ** base. + */ + House->IsStarted = true; + + /* + ** Force the newly placed construction yard to be in the same strength + ** ratio as the MCV that deployed into it. + */ + building->Strength = Health_Ratio() * (int)building->Class->MaxStrength; + + /* + ** Force the MCV to drop any flag it was carrying. This will also set + ** the owner house's flag home cell (since the house's FlagHome is + ** presumably 0 at this point). + */ + Stun(); + + /* + ** If this MCV was teleported here, clear the gray flag so + ** the screen will go back to color. + */ + if (IsMoebius && !Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + delete this; + return(true); + } else { + + /* + ** Could not deploy the construction yard at this location! Just revert + ** back to normal "just sitting there" mode and await further instructions. + */ + delete building; + } + } + Mark(MARK_DOWN); + IsDeploying = false; + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * * + * This routine will perform the operations necessary that occur when a unit is at the * + * center of a cell. These operations could entail deploying into a construction yard, * + * radioing a transport unit, and looking around for the enemy. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + * 06/17/1995 JLB : Handles case when building says "NO!" * + * 06/30/1995 JLB : Gunboats head back and forth now. * + *=============================================================================================*/ +void UnitClass::Per_Cell_Process(PCPType why) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + HousesType house; + + if (why == PCP_END || why == PCP_ROTATION) { + /* + ** Check to see if this is merely the end of a rotation for the MCV as it is + ** preparing to deploy. In this case, it should begin its deploy process. + */ + if (IsDeploying) { + Try_To_Deploy(); + if (!IsActive) return; // Unit no longer exists -- bail. + } + } + + BStart(BENCH_PCP); + if (why == PCP_END) { + /* + ** If this is a unit that is driving onto a building then the unit must enter + ** the building as the final step. + */ + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (whom == Map[CELL(cell-MAP_CELL_W)].Cell_Building()) { + switch (Transmit_Message(RADIO_IM_IN, whom)) { + case RADIO_ROGER: + break; + + case RADIO_ATTACH: + break; + + default: + Scatter(0, true); + break; + } + } + } + } + + /* + ** Unit entering a transport vehicle will break radio contact + ** and attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** When breaking away from a transport object or building, possibly + ** scatter or otherwise begin normal unit operations. + */ + if (IsTethered && (Mission != MISSION_ENTER || + (As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom)) + ) && + Mission != MISSION_UNLOAD) { + + /* + ** Special hack check to make sure that even though it has moved one + ** cell, if it is still on the building (e.g., service depot), have + ** it scatter again. + */ + if (Map[Coord].Cell_Building() != NULL && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } else { + TechnoClass * contact = Contact_With_Whom(); + if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) { + if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING) { + Assign_Mission(MISSION_HARVEST); + } else if (!Target_Legal(NavCom)) { + Scatter(0, true); + } else { + + /* + ** Special case hack to allow automatic transition to loading + ** onto a transport (or other situation) if the destination + ** so indicates. + */ + TechnoClass * techno = As_Techno(NavCom); + if (techno != NULL) { + Transmit_Message(RADIO_DOCKING, techno); + } + } + } else { + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } + } + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that + ** it is probably carrying an incoming reinforcement and it should not be eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + if (IsPlanningToLook) { + IsPlanningToLook = false; + Look(false); + } else { + Look(true); + } + + /* + ** If this is a mobile gap generator, restore the shroud where appropriate + ** and re-shroud around us. + */ + if (Class->IsGapper && !House->IsPlayerControl) { + Shroud_Regen(); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDumping) { + Commence(); + } + + /* + ** Certain units require some setup time after they come to a halt. + */ + if (!Target_Legal(NavCom) && Path[0] == FACING_NONE) { + if (Class->IsNoFireWhileMoving) { + Arm = Rearm_Delay(true)/4; + } + } + + /* + ** If there is a house flag here, then this unit just might pick it up. + */ + if (Flagged == HOUSE_NONE) { + + if (Map[cell].IsFlagged && !House->Is_Ally(Map[cell].Owner)) { + HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this); + } + } + + /* + ** If this is the unit's own flag-home-cell and the unit is carrying + ** a flag, destroy the house of the flag the unit is carrying. + */ + if (Flagged != HOUSE_NONE) { + + /* + ** If this vehicle is carrying your flag, then it will reveal the + ** map for you as well as itself. This gives you and opportunity to + ** attack the unit. + */ + if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) { + Map.Sight_From(Coord_Cell(Coord), Class->SightRange, House, true); + } + + /* + ** If the flag reaches the home cell for the player, then the flag's + ** owner will be destroyed. + */ + if (cell == HouseClass::As_Pointer(Owner())->FlagHome) { + house = Flagged; // Flag_Remove will clear 'Flagged', so save it + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(), true); + HouseClass::As_Pointer(house)->Flag_To_Die(); + } + } + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = Map[cell].Cell_Building(); + if (bldng != NULL && (*bldng == STRUCT_AVMINE || *bldng == STRUCT_APMINE) && !bldng->House->Is_Ally(this)) { + + /* + ** Special case: if it's a land mine deployer, and it ran over the + ** type of mine it deploys (only possible if it just dropped it + ** down) then ignore the mine. + */ + if (*this != UNIT_MINELAYER || bldng->House != House) { + + COORDINATE blcoord = bldng->Center_Coord(); + + new AnimClass(ANIM_MINE_EXP1, blcoord); +// new AnimClass(Combat_Anim(Rule.AVMineDamage, WARHEAD_HE, Map[cell].Land_Type()), blcoord); + + /* + ** Vehicles blow up both mines, but they only take significant damage from AV mines. + */ + if (*bldng == STRUCT_AVMINE) { + int damage = Rule.AVMineDamage; + Take_Damage(damage, 0, WARHEAD_HE); + } else { + int damage = 10; + Take_Damage(damage, 0, WARHEAD_HE); + } + delete bldng; + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + } + + /* + ** If after all is said and done, the unit finishes its move on an impassable cell, then + ** it must presume that it is in the case of a unit driving onto a bridge that blows up + ** before the unit completes it's move. In such a case the unit should have been destroyed + ** anyway, so blow it up now. + */ + LandType land = Map[Coord].Land_Type(); + if (!IsDriving && IsMovingOntoBridge && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + new AnimClass(Combat_Anim(Strength, WARHEAD_AP, land), Coord); + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; + } + } + + /* + ** Destroy any crushable wall that is driven over by a tracked vehicle. + */ + CellClass * cellptr = &Map[cell]; + if (Class->IsCrusher && cellptr->Overlay != OVERLAY_NONE) { +// if (Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrushable) { + if (optr->Type == OVERLAY_SANDBAG_WALL) { + Sound_Effect(VOC_SANDBAG, Center_Coord()); + } else { + Sound_Effect(VOC_WALLKILL2, Center_Coord()); + } + cellptr->Reduce_Wall(-1); + } + } + + /* + ** Check to see if crushing of any unfortunate infantry is warranted. + */ + Overrun_Square(Coord_Cell(Coord), false); + + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + DriveClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * * + * This routine will calculate the shape number for this unit. The shape number is used * + * for the body of the unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to be used for this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Shape_Number(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + +#ifdef FIXIT_ANTS + /* + ** This handles the ant case. + */ + if (Class->Rotation == 8) { + + /* + ** The starting frame is based on the facing of the unit. + */ + shapenum = ((UnitClass::BodyShape[facing]+2)/4) & 0x07; + + /* + ** If the unit is driving, then it has an animation adjustment to the frame number. + */ + if (IsDriving) { + shapenum = 8 + (shapenum * 8) + ((::Frame+ID)/2) % 8; + } else { + + /* + ** If in combat, then do combat anims. + */ + if (Arm > 0) { + shapenum = 8 + 64 + (shapenum * 4) + ((::Frame+ID)/2) % 4; + } + } + } else { +#endif + + /* + ** Fetch the harvesting animation stage as appropriate. + */ + if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) { +// static char _hstage[] = {0, 1, 2, 3, 4, 5, 6, 7, 0}; + unsigned stage = Fetch_Stage(); + if (stage >= ARRAY_SIZE(Class->Harvester_Load_List)) stage = ARRAY_SIZE(Class->Harvester_Load_List)-1; + shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*Class->Harvester_Load_Count)+Class->Harvester_Load_List[stage]; + } else { + /* + ** If the harvester's dumping a load of ore, show that animation + */ + if (IsDumping) { + unsigned stage = Fetch_Stage(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD) { + if (stage >= 8) { + stage = 7; + } + shapenum = 32 + stage + (UnitClass::BodyShape[facing]/4)*8; + } else { + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; + } +#else + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; +#endif + } else { + shapenum = UnitClass::BodyShape[facing]; + + if (Class->IsAnimating) { + shapenum = Fetch_Stage(); + } + + /* + ** Door opening and closing animation must be handled carefully. There are only + ** certain directions where this door animation will work. + */ + if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) { + if (PrimaryFacing == DIR_NE) { + shapenum = 32; + } else { + if (PrimaryFacing == DIR_NW) { + shapenum = 35; + } + } + shapenum += Door_Stage(); + } + } + } + +#ifdef FIXIT_ANTS + } +#endif + + /* + ** The body of the V2 launcher indicates whether it is loaded with a missile + ** or not. + */ + if (*this == UNIT_V2_LAUNCHER) { + if (Ammo == 0) shapenum += 32; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * UnitClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Removed infantry support. * + * 01/07/1995 JLB : Harvester animation support. * + * 07/08/1995 JLB : Uses general purpose draw routine. * + *=============================================================================================*/ +void UnitClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + void const * shapefile; // Working shape file pointer. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + + /* + ** Verify the legality of the unit class. + */ + shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); + if (!is_hidden) { + shapenum = Shape_Number(); + + /* + ** The artillery unit should have its entire body recoil when it fires. + */ + if (*this == UNIT_ARTY && IsInRecoilState) { + Recoil_Adjust(PrimaryFacing.Current(), x, y); + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); + + /* + ** If there is a rotating radar dish, draw it now. + */ + if (Class->IsRadarEquipped) { + if (*this == UNIT_MGG) { + int x2 = x, y2 = y; + shapenum = 32 + (Frame & 7); + Class->Turret_Adjust(PrimaryFacing, x2, y2); + Techno_Draw_Object(shapefile, shapenum, x2, y2, window); + } else { +//#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 +// if (*this == UNIT_PHASE) { +// shapenum = 38 + (Frame & 7); +// } else { +// shapenum = 32 + (Frame % 32); +// } +//#else + shapenum = 32 + (Frame % 32); +//#endif +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_TESLATANK) { + Techno_Draw_Object(shapefile, shapenum, x, y, window); + } else { + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); + } +#else + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); +#endif + } + } + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (/*!Class->IsChunkyShape &&*/ Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + shapenum = TechnoClass::BodyShape[tfacing]+32; +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_PHASE) { + shapenum += 6; + } +#endif + /* + ** A recoiling turret moves "backward" one pixel. + */ + if (IsInRecoilState) { + Recoil_Adjust(SecondaryFacing, xx, yy); + } + + Class->Turret_Adjust(PrimaryFacing, xx, yy); + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + + /* + ** If this unit is carrying the flag, then draw that on top of everything else. + */ + if (Flagged != HOUSE_NONE) { + CC_Draw_Shape(this, "FLAGFLY", MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged); + } + + DriveClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * * + * This routine is used to move a harvester to a place where it can load up with * + * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets * + * the navigation computer toward the nearest Tiberium and lets the unit head there * + * automatically. * + * * + * INPUT: center -- Reference to the center of the radius scan. * + * * + * x,y -- Relative offset from the center cell to perform the check upon. * + * * + * OUTPUT: bool; Is it located directly over a Tiberium patch? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Tiberium_Check(CELL & center, int x, int y) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** If the specified offset from the origin will cause it + ** to spill past the map edge, then abort this cell check. + */ + if (Cell_X(center)+x < Map.MapCellX) return(false); + if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false); + if (Cell_Y(center)+y < Map.MapCellY) return(false); + if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false); + + center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y); + + if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsMapped))) { + if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(false); + if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Tiberium -- Searches for and heads toward tiberium. * + * * + * This routine will cause the unit to search for and head toward nearby Tiberium. When * + * the Tiberium is reached, then this routine should not be called again until such time * + * as additional harvesting is required. When this routine returns false, then it should * + * be called again until such time as it returns true. * + * * + * INPUT: rad = size of ring to search * + * * + * OUTPUT: Has the unit reached Tiberium and harvesting should begin? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Tiberium(int rad) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom)) { + CELL center = Coord_Cell(Center_Coord()); + if (Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } else { + + /* + ** Perform a ring search outward from the center. + */ + for (int radius = 1; radius < rad; radius++) { + for (int x = -radius; x <= radius; x++) { + CELL cell = center; + if (Tiberium_Check(cell, x, -radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, x, +radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, -radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, +radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * * + * This routine is used to by the harvester to harvest Tiberium at the current location. * + * When harvesting is complete, this routine will return true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is harvesting complete? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Harvesting(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + CellClass * ptr = &Map[cell]; + + /* + ** Keep waiting if still heading toward a spot to harvest. + */ + if (Target_Legal(NavCom)) return(true); + + if (Tiberium_Load() < 1 && ptr->Land_Type() == LAND_TIBERIUM) { + + /* + ** Lift some Tiberium from the ground. Try to lift a complete + ** "level" of Tiberium. A level happens to be 6 steps. If there + ** is a partial level, then lift that instead. Never lift more + ** than the harvester can carry. + */ +// int reducer = (ptr->OverlayData % 6) + 1; + int reducer = 1; + OverlayType overlay = ptr->Overlay; + reducer = ptr->Reduce_Tiberium(min(reducer, Rule.BailCount-Tiberium)); + Tiberium += reducer; + switch (overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + Gold += reducer; + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + Gems += reducer; + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + break; + + default: + break; + } + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + + } else { + + /* + ** If the harvester is stopped on a non Tiberium field and the harvester + ** isn't loaded with Tiberium, then no further action can be performed + ** by this logic routine. Bail with a failure and thus cause a branch to + ** a better suited logic processor. + */ + Set_Stage(0); + Set_Rate(0); + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo and * + * then exit the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +extern void Logic_Switch_Player_Context(ObjectClass *object); +extern void Logic_Switch_Player_Context(HouseClass *object); +extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + +int UnitClass::Mission_Unload(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case UNIT_HARVESTER: + if (PrimaryFacing != DIR_W) { + if (!IsRotating) { + Do_Turn(DIR_W); + } + return(5); + } + + if (!IsDumping) { + IsDumping = true; + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + break; + } + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Dump_List)-1) break; + + IsDumping = false; + if (Tiberium) { + Tiberium = 0; + int credits = Credit_Load(); + House->Harvested(credits); + Tiberium = Gold = Gems = 0; + } + Transmit_Message(RADIO_OVER_OUT); + + Assign_Mission(MISSION_HARVEST); + break; + + case UNIT_TRUCK: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + Status = UNLOADING; + return(1); + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + Assign_Mission(MISSION_GUARD); + break; + } + break; + + case UNIT_APC: +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + case UNIT_PHASE: +#endif + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + + case UNIT_MCV: + switch (Status) { + case 0: + Path[0] = FACING_NONE; + Status = 1; + break; + + case 1: + if (!IsDriving) { + Try_To_Deploy(); + if (IsActive) { + if (IsDeploying) { + Status = 2; + } else { + if (!House->IsHuman && Session.Type != GAME_NORMAL) { + Assign_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + } + } + break; + + case 2: + if (!IsDeploying) { + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); + + case UNIT_MINELAYER: + switch (Status) { + case INITIAL_CHECK: + dir = DIR_NE; + if (Ammo > 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (Ammo > 0) { + if (!Map[Center_Coord()].Cell_Building()) { + Mark(MARK_UP); + BuildingClass * building = new BuildingClass((House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE || House->ActLike == HOUSE_BAD) ? STRUCT_APMINE : STRUCT_AVMINE, House->Class->House); + if (building != NULL) { + ScenarioInit = true; + if (building->Unlimbo(Coord)) { + Sound_Effect(VOC_MINELAY1, Coord); + ScenarioInit = false; + building->Revealed(House); + Ammo--; + } + ScenarioInit = false; + } + Status = CLOSING_DOOR; + Mark(MARK_DOWN); + } else { + Status = CLOSING_DOOR; + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case UNIT_MAD: + if (!Gems && !IsDumping) { + Gems = 1; + Gold = 0; + Arm = QuakeDelay * House->ROFBias; +#ifdef ENGLISH + Speak(VOX_MADTANK_DEPLOYED); // this voice only exists in English +#else + Sound_Effect(VOC_BUZZY1,Center_Coord()); +#endif + Set_Stage(0); + Set_Rate(Rule.OreDumpRate*2); + IsDumping = true; + +#if 1 + InfantryClass *crew = new InfantryClass(INFANTRY_C1, House->Class->House); + if (crew != NULL) crew->IsTechnician = true; + + if (crew != NULL) { + DirType toface = DIR_S + PrimaryFacing; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + if (crew->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + crew->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + crew->Assign_Mission(MISSION_MOVE); + crew->Assign_Destination(::As_Target(newcell)); + break; + } + } + } +#endif + } + + if ( (Arm && !Gold) || IronCurtainCountDown) { + Set_Stage(Fetch_Stage() & 1); + return(1); + } + + if (!Gold) { + Sound_Effect(VOC_MAD_CHARGE, Center_Coord()); + Set_Stage(0); + Gold = 1; + return(1); + } + + if (Fetch_Stage() < 7) { + return(1); + } + + IsDumping = false; + + Sound_Effect(VOC_MAD_EXPLODE, Center_Coord()); + + Strength = 1; // assure destruction + PendingTimeQuake = true; // trigger a time quake + TimeQuakeCenter = ::As_Target(Center_Coord()); + break; + + case UNIT_CHRONOTANK: + if (IsOwnedByPlayer) { + Map.IsTargettingMode = SPC_CHRONO2; + HouseClass* old_player_ptr = PlayerPtr; + Logic_Switch_Player_Context(this); + Unselect_All(); + On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode); + Logic_Switch_Player_Context(old_player_ptr); + } + House->UnitToTeleport = As_Target(); + + Assign_Mission(MISSION_GUARD); + break; +#endif + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * * + * This is the AI process used by harvesters when they are doing their harvesting action. * + * This entails searching for nearby Tiberium, heading there, harvesting, and then * + * returning to a refinery for unloading. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 06/21/1995 JLB : Force guard mode if no Tiberium found. * + * 09/28/1995 JLB : Aborts harvesting if there are no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Harvest(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + LOOKING, + HARVESTING, + FINDHOME, + HEADINGHOME, + GOINGTOIDLE, + }; + + /* + ** A non-harvesting type unit will just sit still if it is given the harvest mission. This + ** allows combat units to act "brain dead". + */ + if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30); + + /* + ** If there are no more refineries, then drop into guard mode. + */ + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + switch (Status) { + + /* + ** Go and find a Tiberium field to harvest. + */ + case LOOKING: + /* + ** When full of tiberium, just skip to finding a free refinery + ** to unload at. + */ + if (Tiberium_Load() == 1) { + Status = FINDHOME; + return(1); + } + + /* + ** Look for ore where we last found some - mine the same patch + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + ArchiveTarget = 0; + } + IsHarvesting = false; + if (Goto_Tiberium(Rule.TiberiumLongScan / CELL_LEPTON_W)) { + IsHarvesting = true; + Set_Rate(2); + Set_Stage(0); + Status = HARVESTING; + return(1); + } else { + + /* + ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then + ** force it to go into guard mode. This will prevent the harvester from repeatedly + ** searching for Tiberium. + */ + if (!Target_Legal(NavCom)) { + + /* + ** If the archive target is legal, then head there since it is presumed + ** that the archive target points to the last place it harvested at. This might + ** solve the case where the harvester gets stuck and can't find Tiberium just because + ** it is greater than 32 squares away. + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + } else { + Status = GOINGTOIDLE; + IsUseless = true; + House->IsTiberiumShort = true; + return(TICKS_PER_SECOND*7); + } + } else { + IsUseless = false; + } + } + break; + + /* + ** Harvest at current location until full or Tiberium exhausted. + */ + case HARVESTING: +// if (Fetch_Stage() > ARRAY_SIZE(Class->Harvester_Load_List)) { +// Set_Stage(0); +// } + if (Fetch_Rate() == 0) { + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + } + + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Load_List)) return(1); + if (!Harvesting()) { + IsHarvesting = false; + if (Tiberium_Load() == 1) { + Status = FINDHOME; + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } else { + if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom)) { + ArchiveTarget = TARGET_NONE; + Status = FINDHOME; + } else { + Status = HARVESTING; + IsHarvesting = true; + } + } + return(1); + } + return(1); +// return(TICKS_PER_SECOND*Rule.OreDumpRate); + + /* + ** Find and head to refinery. + */ + case FINDHOME: + if (!Target_Legal(NavCom)) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + } + } else { + ScenarioInit++; + nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + ScenarioInit--; + if (nearest != NULL) { + Assign_Destination(::As_Target(Nearby_Location(nearest))); + } + } + } + break; + + /* + ** In communication with refinery so that it will successfully dock and + ** unload. If, for some reason, radio contact was lost, then hunt for + ** another refinery to unload at. + */ + case HEADINGHOME: + Assign_Mission(MISSION_ENTER); + return(1); + + /* + ** The harvester has nothing to do. There is no Tiberium nearby and + ** no where to go. + */ + case GOINGTOIDLE: + if (IsUseless) { + if (House->ActiveBScan & STRUCTF_REPAIR) { + Assign_Mission(MISSION_REPAIR); + } else { + Assign_Mission(MISSION_HUNT); + } + } + Assign_Mission(MISSION_GUARD); + break; + + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * * + * Computer controlled units must be intelligent enough to find enemies as well as to * + * attack them. This AI process will handle both the simple attack process as well as the * + * scanning for enemy units to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Hunt(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == UNIT_MCV) { + enum { + FIND_SPOT, + WAITING + }; + + switch (Status) { + + /* + ** This stage handles locating a convenient spot, rotating to face the correct + ** direction and then commencing the deployment operation. + */ + case FIND_SPOT: + if (Goto_Clear_Spot()) { + if (Try_To_Deploy()) { + Status = WAITING; + } + } + break; + + /* + ** This stage watchdogs the deployment operation and if for some reason, the deployment + ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for + ** a convenient spot to deploy. + */ + case WAITING: + if (!IsDeploying) { + Status = FIND_SPOT; + } + break; + } + } else { + + return(DriveClass::Mission_Hunt()); + } + return(MissionControl[Mission].Normal_Delay()+Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Overlap_List -- Determines overlap list for units. * + * * + * The unit overlap list is used to keep track of which cells are to * + * be marked for redraw. This is critical in order to keep the units * + * displayed correctly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the overlap list pointer for the unit at its * + * present position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1994 JLB : Created. * + * 06/19/1994 JLB : Uses Coord_Spillable_List function. * + *=============================================================================================*/ +short const * UnitClass::Overlap_List(bool redraw) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (Is_Selected_By_Player()) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + if (Class->IsTurretEquipped || Class->IsRadarEquipped) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + int size = ICON_PIXEL_W; + + if (redraw && (Is_Selected_By_Player() || IsFiring)) { + size += 24; + } + if (Class->IsGigundo || IsAnimAttached) { + size = ICON_PIXEL_W*2; + } + + return(Coord_Spillage_List(Coord, size)+1); +} + + +/*********************************************************************************************** + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * * + * Use this routine to determine if the unit can enter the cell * + * specified and given the direction of entry specified. Typically, * + * this is used when determining unit travel path. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The facing that the unit would enter the specified * + * cell. If this value is -1, then don't consider * + * facing when performing the check. * + * * + * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is * + * allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/16/1994 JLB : Converted to member function. * + * 07/04/1995 JLB : Allowed to drive on building trying to enter it. * + *=============================================================================================*/ +MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + bool cancrush = false; + + CellClass const * cellptr = &Map[cell]; + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map() && IsLocked) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** Certain vehicles can drive over walls. Check for this case and + ** and return the appropriate flag. Other units treat walls as impassable. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (optr->IsWall) { + + /* + ** If the blocking wall is crushable (and not owned by this player or one of this players + ** allies, then record that it is crushable and let the normal logic take over. The end + ** result should cause this unit to consider the cell passable. + */ + if (optr->IsCrushable && Class->IsCrusher) { + cancrush = !House->Is_Ally(cellptr->Owner); + } + + if (!cancrush && Is_Weapon_Equipped()) { + WarheadTypeClass const * whead = Class->PrimaryWeapon->WarheadPtr; + + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { +// if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; +// } else { +// return(MOVE_NO); +// } + } else { + return(MOVE_NO); + } + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + bool crushable = false; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** If object is a land mine, allow movement if possible. + */ + if (obj->What_Am_I() == RTTI_BUILDING && (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House))) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) return(MOVE_OK); + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) return(MOVE_OK); + } + + /* + ** Always allow entry if trying to move on a cell with + ** authorization from the occupier. + */ + if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) { + return(MOVE_OK); + } + + /* + ** Special check to allow entry into the sea transport this vehicle + ** is trying to enter. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target()) { + return(MOVE_NO); + } + + bool is_moving = obj->Is_Foot() && + (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->IsRotating || ((FootClass *)obj)->IsDriving); +// (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->IsDriving); + + if (House->Is_Ally(obj)) { + if (is_moving) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4; + if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) { + return(MOVE_NO); + } + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO); + + /* + ** If the blocking object is not in the same zone, then it certainly + ** isn't a temporary block, it is a permanent one. + */ + if (Map[Coord].Zones[Class->MZone] != cellptr->Zones[Class->MZone]) return(MOVE_NO); + + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** If this unit can crush infantry, and there is an enemy infantry in the + ** cell, don't consider the cell impassible. This is true even if the unit + ** doesn't contain a legitimate weapon. + */ + bool crusher = Class->IsCrusher; + if (!crusher || !obj->Class_Of().IsCrushable) { + + /* + ** Any non-allied blockage is considered impassable if the unit + ** is not equipped with a weapon. + */ + if (Class->PrimaryWeapon == NULL) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the unit is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + +#ifdef TOFIX + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + crushable = true; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (!cancrush && retval != MOVE_DESTROYABLE && Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (retval == MOVE_OK && !crushable && (cellptr->Flag.Composite & 0x3F) != 0) { + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } else { + if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) { + retval = MOVE_MOVING_BLOCK; + } else { + + /* + ** Enemy infantry have reserved the cell. If this unit can crush + ** infantry, consider the cell passable. If not, then consider the + ** cell destroyable if it has a weapon. If neither case applies, then + ** this vehicle should avoid the cell altogether. + */ + if (!Class->IsCrusher) { + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsAntiGround) { + retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + } + } + + /* + ** If its ok to move into the cell because we can crush whats in the cell, then + ** make sure no one else is already moving into the cell to crush something. + */ + if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) { + + /* + ** However, if the cell is occupied by a crushable vehicle, then we can + ** never be sure if some other friendly vehicle is also trying to crush + ** the cell at the same time. In the case of a crushable vehicle in the + ** cell, then allow entry. + */ + if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) { + return(MOVE_MOVING_BLOCK); + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * UnitClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the unit list and unit objects. This routine is typically * + * used in preparation for a new scenario load. All units are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Init(void) +{ + Units.Free_All(); +} + + +/*********************************************************************************************** + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * * + * This routine will start the vehicle moving by marking the destination cell as * + * reserved. Cells must be reserved in this fashion or else they might become occupied as * + * the vehicle is proceeding toward it. * + * * + * INPUT: headto -- The location where the vehicle will be heading. * + * * + * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell * + * being occupied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Start_Driver(COORDINATE & headto) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) {//BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/11/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(ObjectClass const * object) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + /* + ** Allow units to move onto land mines. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** If the unit doesn't have a weapon, but can crush the object, then consider + ** the object as a movable location. + */ + if (action == ACTION_ATTACK && !Can_Player_Fire()) { + if (Class->IsCrusher && object->Class_Of().IsCrushable) { + action = ACTION_MOVE; + } else { + action = ACTION_SELECT; + } + } + + /* + ** Don't allow special deploy action unless there is something to deploy. + */ + if (action == ACTION_SELF) { + if (*this == UNIT_MCV) { + + /* + ** The MCV will get the no-deploy cursor if it couldn't + ** deploy at its current location. + */ + ((ObjectClass &)(*this)).Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + action = ACTION_NO_DEPLOY; + } + ((ObjectClass &)(*this)).Mark(MARK_DOWN); + + } else { + + /* + ** The mine layer can "deploy" its mines if it currently isn't + ** sitting on top of a mine and it still has mines available. + */ + if (*this == UNIT_MINELAYER) { + if (!Ammo || Map[Center_Coord()].Cell_Building() || (Map[Center_Coord()].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Map[Center_Coord()].Smudge).IsBib)) { + action = ACTION_NO_DEPLOY; + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK || *this == UNIT_MAD) { + if (*this == UNIT_CHRONOTANK) { +// If the chrono tank's counter is still charging up, don't allow deploy. Or, +// if it's a player-controlled chrono tank, and the player's currently trying +// to teleport a different unit, don't allow teleporting this unit. + if(MoebiusCountDown || (IsOwnedByPlayer && House->UnitToTeleport && Map.IsTargettingMode == SPC_CHRONO2)) { + action = ACTION_NO_DEPLOY; + } + } + } else { +#endif + /* + ** All other units can "deploy" their passengers if they in-fact have + ** passengers and are a transport vehicle. Otherwise, they cannot + ** perform any self action. + */ + if (Class->Max_Passengers() > 0) { + if (How_Many() == 0) { + action = ACTION_NO_DEPLOY; + } + } else { + action = ACTION_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + + /* + ** Special return to friendly refinery action. + */ + if (House->IsPlayerControl && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) { + if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { + action = ACTION_ENTER; + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_MOVE; + } + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno() && object->What_Am_I() == RTTI_VESSEL) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if( *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + action = ACTION_NONE; + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NONE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. At the unit level, only the harvester is checked for. The lower * + * classes determine the regular action response. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(CELL cell) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) { + return(ACTION_HARVEST); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NOMOVE; + } +#endif + return(action); +} + + +/*********************************************************************************************** + * UnitClass::Exit_Repair -- Drive the unit off the repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +#define XYCELL(x, y) (y*MAP_CELL_W+x) +void UnitClass::Exit_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int i; + CELL cell; + bool found = false; + static short const ExitRepair[] = { + XYCELL(0, -2), + XYCELL(1, -1), + XYCELL(2, 0), + XYCELL(1, 1), + XYCELL(0, 2), + XYCELL(-1, 1), + XYCELL(-2, 0), + XYCELL(-1, -1) + }; + + cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())]; + if (Can_Enter_Cell(cell) == MOVE_OK) found = true; + + if (!found) for (i=0; i<8; i++) { + cell = Coord_Cell(Coord) + ExitRepair[i]; + if (Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } + if (found) { +// DirType dir = Direction(cell); + + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * * + * Handles the guard mission for the unit. If the IQ is high enough and the unit is * + * a harvester, it will begin to harvest automatically. An MCV might autodeploy. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the time delay before this command is executed again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/08/1995 JLB : Fixes gunboat problems. * + *=============================================================================================*/ +int UnitClass::Mission_Guard(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + if (/*House->IsBaseBuilding &&*/ !House->IsHuman && Class->IsToHarvest && House->Get_Quantity(STRUCT_REFINERY) > 0 && !House->IsTiberiumShort) { + Assign_Mission(MISSION_HARVEST); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + if (*this == UNIT_MCV && House->IsBaseBuilding) { + Assign_Mission(MISSION_UNLOAD); + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + return(DriveClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * * + * This routine intercepts the normal move mission and if a gunboat is being processed, * + * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission * + * regardless of what the player did. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 09/28/1995 JLB : Harvester stick in guard mode if no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Move(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + IsHarvesting = false; + + /* + ** Always make sure that that transport door is closed if the vehicle is moving. + */ + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + + return(DriveClass::Mission_Move()); +} + + +/*********************************************************************************************** + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; + + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_S); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * * + * This routine will attach a house flag to this unit. * + * * + * INPUT: house -- The house that is having its flag attached to it. * + * * + * OUTPUT: Was the house flag successfully attached to this unit? * + * * + * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure * + * of this routine. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Attach(HousesType house) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (house != HOUSE_NONE && Flagged == HOUSE_NONE) { + Flagged = house; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * * + * This routine will remove the house flag that is attached to this unit. * + * * + * INPUT: none * + * * + * OUTPUT: Was the flag successfully removed? * + * * + * WARNINGS: This routine doesn't put the flag into a new location. That operation must * + * be performed or else the house flag will cease to exist. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Remove(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Flagged != HOUSE_NONE) { + Flagged = HOUSE_NONE; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the unit. * + * This will either be the number of passengers or the percentage full (in 1/5ths) of * + * a harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Pip_Count(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->Max_Passengers() > 0) { + return(How_Many()); + } + + if (*this == UNIT_MINELAYER) { + int retval = 0; + if (Ammo > 0) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + return(retval); + } + + if (*this == UNIT_HARVESTER) { + return((Gold + Gems) / 4); + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK) { + int fulldur = ChronoTankDuration * TICKS_PER_MINUTE; + return( (fulldur - MoebiusCountDown) / (fulldur / 5)); + } +#endif + return(0); +} + + +/*********************************************************************************************** + * UnitClass::APC_Close_Door -- Closes an APC door. * + * * + * This routine will initiate closing of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Close_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Close_Door(10, 2); +} + + +/*********************************************************************************************** + * UnitClass::APC_Open_Door -- Opens an APC door. * + * * + * This routine will initiate opening of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Open_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) { + Open_Door(10, 2); + } else { + Open_Door(1, 2); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * * + * When a unit is destroyed, a crew member might be generated. This routine will return * + * with the infantry type to produce for this unit. This routine will be called for every * + * survivor that is generated. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType UnitClass::Crew_Type(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon == NULL) { + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + } + return(DriveClass::Crew_Type()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * * + * This mission handler will look for a repair facility. If one is found then contact * + * is established and then the normal Mission_Enter logic is performed. The repair facility * + * will take over the actual repair coordination process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, true); + + IsHarvesting = false; + + /* + ** If there is no available repair facility, then check to see if there + ** are any repair facilities at all. If not, then enter this unit + ** into idle state. + */ + if (nearest == NULL) { + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Enter_Idle_Mode(); + } + } else { + + /* + ** Try to establish radio contact with the repair facility. If contact + ** was established, then proceed with normal enter mission, which handles + ** the repair process. + */ + if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + return(1); + } + } + + /* + ** If no action could be performed at this time, then wait + ** around for a bit before trying again. + */ + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * * + * This routine will return with the facing that a projectile will travel if it was * + * fired at this instant. The facing should match the turret facing for those units * + * equipped with a turret. If the unit doesn't have a turret, then it will be the facing * + * of the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for a projectile. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Fire_Direction(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + if (*this == UNIT_V2_LAUNCHER) { + int diff1 = SecondaryFacing.Difference(DIR_E); + int diff2 = SecondaryFacing.Difference(DIR_W); + diff1 = ABS(diff1); + diff2 = ABS(diff2); + int diff = min(diff1, diff2); + int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff); + if (SecondaryFacing.Difference(DIR_N) < 0) { + return(DirType)(SecondaryFacing - (DirType)adj); + } else { + return(DirType)(SecondaryFacing + (DirType)adj); + } + } + return(SecondaryFacing.Current()); + } + + return(DriveClass::Fire_Direction()); +} + + +/*********************************************************************************************** + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * * + * This virtual routine is used to determine if the vehicle is allowed * + * to start moving. It is typically called when the vehicle desires * + * to move but needs confirmation from the turret logic before * + * proceeding. This happens when dealing with a vehicle that must have * + * its turret face the same direction as the body before the vehicle * + * may begin movement. * + * * + * INPUT: dir -- The facing the unit wants to travel in. * + * * + * OUTPUT: bool; Can the unit begin forward movement now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Ok_To_Move(DirType dir) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsLockTurret) { + if (IsRotating) { + return(false); + } else { + if (SecondaryFacing.Difference(dir)) { + ((UnitClass *)this)->SecondaryFacing.Set_Desired(dir); + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * * + * This routine determines if the turret can fire upon the target * + * specified. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use to determine legality to fire. 0=primary, * + * 1=secondary. * + * * + * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + * 06/01/1994 JLB : Returns reason why it can't fire. * + *=============================================================================================*/ +FireErrorType UnitClass::Can_Fire(TARGET target, int which) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + FireErrorType fire = DriveClass::Can_Fire(target, which); + + if (fire == FIRE_OK) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((Class->IsNoFireWhileMoving /*!Class->IsTurretEquipped || Class->IsLockTurret*/) && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + dir = Direction(target); + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff < 8) { + return(DriveClass::Can_Fire(target, which)); + } + return(FIRE_FACING); + } + return(fire); +} + + +/*********************************************************************************************** + * UnitClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the turret. It will check * + * to see if firing is technically legal given the specified target. * + * If it is legal to fire, it does so. It is safe to call this routine * + * every game tick. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * UnitClass::Fire_At(TARGET target, int which) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BulletClass * bullet = NULL; + WeaponTypeClass const * weap = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + if (weap == NULL) return(NULL); + + if (Can_Fire(target, which) == FIRE_OK) { + bullet = DriveClass::Fire_At(target, which); + + if (bullet != NULL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +if(Class->Type == UNIT_DEMOTRUCK && IsActive) delete this; +#endif + /* + ** Possible reload timer set. + */ + if ((*this == UNIT_V2_LAUNCHER) && Reload == 0) { + Reload = TICKS_PER_SECOND * 30; + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * UnitClass::Class_Of -- Fetches a reference to the class type for this object. * + * * + * This routine will fetch a reference to the TypeClass of this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with reference to the type class of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & UnitClass::Class_Of(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * * + * Use this routine to determine what the Tiberium load is (as a fixed point percentage). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current "fullness" rating for the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +fixed UnitClass::Tiberium_Load(void) const +{ + assert(IsActive); + + if (*this == UNIT_HARVESTER) { + return(fixed(Tiberium, Rule.BailCount)); + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * * + * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple * + * calls to this routine are needed in order to fully offload all Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,* + * then this indicates that all Tiberium has been offloaded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + if (Tiberium) { + Tiberium--; + +// MBL 05.15.2020: Note, if the below code is ever reeanbled for some ready, make sure to see fix in +// Tiberian Dawn's DriveClass::Offload_Tiberium_Bail() for AI players + +#ifdef TOFIX + if (House->IsHuman) { + return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); + } + return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); +#endif + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * * + * This routine will check to see if the target is infantry and it can be overrun. It will * + * try to overrun the infantry rather than attack it. This only applies to computer * + * controlled vehicles. If it isn't the infantry overrun case, then it falls into the * + * base class for normal (complex) approach algorithm. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. * + *=============================================================================================*/ +void UnitClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Only if there is a legal target should the approach check occur. + */ + if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) { + + /* + ** Special case: + ** If this is for a unit that can crush infantry, and the target is + ** infantry, AND the infantry is pretty darn close, then just try + ** to drive over the infantry instead of firing on it. + */ + TechnoClass * target = As_Techno(TarCom); + if (Class->IsCrusher && Distance(TarCom) < Rule.CrushDistance && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) { + Assign_Destination(TarCom); + return; + } + } + + /* + ** In the other cases, uses the more complex "get to just within weapon range" + ** algorithm. + */ + DriveClass::Approach_Target(); +} + + +/*********************************************************************************************** + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * * + * This routine is called when a vehicle enters a square or when it is about to enter a * + * square (controlled by parameter). When a vehicle that can crush infantry enters a * + * cell that contains infantry, then the infantry will be destroyed (regardless of * + * affiliation). When a vehicle threatens to overrun a square, all occupying infantry * + * will attempt to get out of the way. * + * * + * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. * + * * + * threaten -- Don't kill, but just threaten to enter the cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Overrun_Square(CELL cell, bool threaten) +{ + assert(IsActive); + + CellClass * cellptr = &Map[cell]; + + if (Class->IsCrusher) { + if (threaten) { + + /* + ** If the cell contains infantry, then they will panic when a vehicle tries + ** drive over them. Have the infantry run away instead. + */ + if (cellptr->Flag.Composite & 0x1F) { + + /* + ** Scattering is controlled by the game difficulty level. + */ + cellptr->Incoming(0, true); + } + } else { + ObjectClass * object = cellptr->Cell_Occupier(); + int crushed = false; + while (object != NULL) { + if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < CELL_LEPTON_W/2) { + +#ifdef OBSOLETE + /* + ** If we're running over infantry, let's see if the infantry we're + ** squashing is a thief trying to capture us. If so, let him succeed. + */ + if (object->What_Am_I() == RTTI_INFANTRY && *((InfantryClass *)object) == INFANTRY_THIEF && ((InfantryClass *)object)->NavCom == As_Target()) { + ObjectClass * next = object->Next; + IsOwnedByPlayer = ((InfantryClass *)object)->IsOwnedByPlayer; + House = ((InfantryClass *)object)->House; + delete object; + object = next; + } else { +#endif + ObjectClass * next = object->Next; + crushed = true; + + /* + ** Record credit for the kill(s) + */ + Sound_Effect(VOC_SQUISH, Coord); + if (object->Height == 0) { + AnimClass* anim = new AnimClass(ANIM_CORPSE1, object->Center_Coord()); + if (anim != NULL) { + anim->OwnerHouse = object->Owner(); + } + } + object->Record_The_Kill(this); + object->Mark(MARK_UP); + object->Limbo(); + delete object; + //new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord)); + + object = next; +#ifdef OBSOLETE + } +#endif + } else { + object = object->Next; + } + } + if (crushed) Do_Uncloak(); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * * + * This will assign the specified destination to the unit. It is presumed that doing is * + * is all that is needed in order to cause the unit to move to the specified destination. * + * * + * INPUT: target -- The target (location) to move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && Contact_With_Whom()->Is_Infantry()) { + Transmit_Message(RADIO_OVER_OUT); + } + + BuildingClass * b = As_Building(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + if (b != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (b->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + ArchiveTarget = target; + +/* +** HACK ALERT: The repair bay is counting on the assignment of the NavCom by this routine. +** The refinery must NOT have the navcom assigned by this routine. +*/ +if (*b != STRUCT_REPAIR) { + target = TARGET_NONE; +} + } else { + if (Transmit_Message(RADIO_DOCKING, b) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + if (*b == STRUCT_REPAIR) { + ArchiveTarget = target; + } + } +if (*b != STRUCT_REPAIR) { + ArchiveTarget = target; + target = TARGET_NONE; +} + } + } else { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { + // TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + + /* + ** If the player clicked on a friendly repair facility and the repair + ** facility is currently not involved with some other unit (radio or unloading). + */ + if (b != NULL && *b == STRUCT_REPAIR) { + if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this) ) { +// if (target != NULL) { + ArchiveTarget = target; +// } +// target = TARGET_NONE; + } else { + + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + + /* + ** Last check to make sure that the loading square is free from permanent + ** occupation (such as a building). + */ + CELL cell = (CELL)(Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1)); + if (Ground[Map[cell].Land_Type()].Cost[Techno_Type_Class()->Speed] > 0) { + if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) { + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; + return; + } + + /* + ** Failure to establish a docking relationship with the refinery. + ** Bail & await further instructions. + */ + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * * + * This routine will search the map looking for a good target to attack. It takes into * + * consideration the type of weapon it is equipped with. * + * * + * INPUT: threat -- The threat type to search for. * + * * + * OUTPUT: Returns with a target value of the target that this unit should pursue. If there * + * is no suitable target, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET UnitClass::Greatest_Threat(ThreatType threat) const +{ + assert(IsActive); + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +#ifdef OBSOLETE + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } +#endif + + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Read_INI(CCINIClass & ini) +{ + UnitClass * unit; // Working unit pointer. + HousesType inhouse; // Unit house. + UnitType classid; // Unit class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = UnitTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != UNIT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + unit = new UnitClass(classid, inhouse); + if (unit != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + unit->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL,",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + unit->Trigger = tt; + } + } + + if (unit->Unlimbo(coord, dir)) { + unit->Strength = (int)unit->Class->MaxStrength * fixed(strength, 256); + if (unit->Strength > unit->Class->MaxStrength-3) unit->Strength = unit->Class->MaxStrength; + if (Session.Type == GAME_NORMAL || unit->House->IsHuman) { + unit->Assign_Mission(mission); + unit->Commence(); + } else { + unit->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the unit could not be unlimboed, then this is a catastrophic error + ** condition. Delete the unit. + */ + delete unit; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Write_INI -- Store the units to the INI database. * + * * + * This routine will store all the unit data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object to store to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the unit data out. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && !unit->IsInLimbo && unit->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio()*256, + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission), + unit->Trigger.Is_Valid() ? unit->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * * + * This will determine the value of the cargo carried (limited to considering only gold * + * and gems) and return that value. Use this to determine how 'valuable' a harvester is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit value of the cargo load of this unit (harvester). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Credit_Load(void) const +{ + return((Gold * Rule.GoldValue) + (Gems * Rule.GemValue)); +} + + +/*********************************************************************************************** + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * * + * Call this routine to determine if this unit should crush the object specified. The * + * test for crushable action depends on proximity and ability of the unit. If a unit * + * should crush the object, then it should be given a movement order to enter the cell * + * where the object is located. * + * * + * INPUT: it -- The object to see if it should be crushed. * + * * + * OUTPUT: bool; Should "it" be crushed by this unit? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Should_Crush_It(TechnoClass const * it) const +{ + assert(IsActive); + + /* + ** If this unit cannot crush anything or the candidate object cannot be crushed, + ** then it obviously should not try to crush it -- return negative answer. + */ + if (!Class->IsCrusher || it == NULL || !it->Techno_Type_Class()->IsCrushable) return(false); + + /* + ** Objects that are far away should really be fired upon rather than crushed. + */ + if (Distance(it) > Rule.CrushDistance) return(false); + + /* + ** Human controlled units don't automatically crush. Neither do computer controlled ones + ** if they are at difficult setting. + */ + if (House->IsHuman || House->Difficulty == DIFF_HARD) return(false); + + /* + ** If the weapon this unit is equipped with is very good against crushable objects then + ** fire the weapon instead. It is presumed that a wood destroying weapon is good against + ** most crushable object types (infantry). + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) return(false); + + /* + ** If the house IQ indicates that crushing should not be allowed, then don't + ** suggest that crushing be done. + */ + if (House->IQ < Rule.IQCrush) return(false); + + /* + ** Don't allow crushing of spies by computer-controlled vehicles. + */ + if (it->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)it == INFANTRY_SPY) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * * + * This scatter logic will actually look for a nearby location rather than an adjacent * + * free location. This is necessary because sometimes a unit is required to scatter more * + * than one cell. A vehicle on a service depot is a prime example. * + * * + * INPUT: threat -- The coordinate that a potential threat resides. If this is a non * + * threat related scatter, then this parameter will be zero. * + * * + * forced -- Should the scatter be performed even if it would be otherwise * + * inconvenient? * + * * + * nokidding-- Should the scatter be performed even if it would otherwise be * + * illegal? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + if (Mission == MISSION_SLEEP || Mission == MISSION_STICKY || Mission == MISSION_UNLOAD) return; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + if (PrimaryFacing.Is_Rotating()) return; +// if (IsRotating) return; + + if (Target_Legal(NavCom) && !nokidding) return; + + if (threat == 0) { + Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord), Class->Speed))); + } else { + DriveClass::Scatter(threat, forced, nokidding); + } +} + + +/*********************************************************************************************** + * UnitClass::Limbo -- Limbo this unit. * + * * + * This will cause the unit to go into a limbo state. If it was carrying a flag, then * + * the flag will be dropped where the unit is at. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was this unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Limbo(void) +{ + if (DriveClass::Limbo()) { + if (Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord)); + Flagged = HOUSE_NONE; + } + return(true); + } + return(false); +} + +/* +** Updated for client/server multiplayer - ST 8/12/2019 11:46AM +*/ +void UnitClass::Shroud_Regen(void) +{ + if (Class->IsGapper/*KO && !House->IsPlayerControl*/) { + static int _xtab[]={ + -1, 0, 1, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -1, 0, 1 + }; + static int _ytab[]={ + -3,-3,-3, + -2,-2,-2,-2,-2, + -1,-1,-1,-1,-1, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, + 3, 3, 3 + }; + int index; + int centerx, centery; + CELL trycell; + + // Only restore under the shroud if it's a valid field. + if (ShroudBits != (unsigned)-1L) { + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 30; index >= 0 && ShroudBits; index--) { + if (ShroudBits & 1) { + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); +#if(0) + Map.Map_Cell(trycell, PlayerPtr); +#else + Map.UnJam_Cell(trycell, House); + Map.Map_Cell(trycell, House); +#endif + } + ShroudBits >>= 1; + } + } + + if(IsActive && Strength) { + // Now shroud around the new center + ShroudBits = 0L; + ShroudCenter = Coord_Cell(Center_Coord()); + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 0; index < 31; index++) { + ShroudBits <<= 1; + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); + if (Map[trycell].Is_Mapped(House)) { + Map.Jam_Cell(trycell, House); + ShroudBits |= 1; + } + } + } + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (House->IsPlayerControl) { + Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != House) { + Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house); + } + } + } + + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * * + * This logic is similar to normal guard area except that APCs owned by the computer will * + * try to load up with nearby infantry. This will give the computer some fake intelligence * + * when playing in skirmish mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay to use before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + /* + ** Check to see if this is an APC that is largely empty and not otherwise doing anything. + ** Such an APC should load up with infantry. + */ + if (Session.Type != GAME_NORMAL && +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + (*this == UNIT_APC || *this == UNIT_PHASE ) && +#else + *this == UNIT_APC && +#endif + !Target_Legal(TarCom) && + !In_Radio_Contact() && + House->Which_Zone(this) != ZONE_NONE && + !House->IsHuman) { + + + int needed = Class->Max_Passengers() - How_Many(); + for (int index = 0; index < Infantry.Count(); index++) { + if (needed == 0) break; + + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry != NULL && + infantry->IsActive && + !infantry->IsInLimbo && + infantry->Strength > 0 && + infantry->House == House && + !Target_Legal(infantry->TarCom) && + !Target_Legal(infantry->NavCom) && + Distance(infantry) < 7 * CELL_LEPTON_W && + (infantry->Mission == MISSION_GUARD || infantry->Mission == MISSION_GUARD_AREA)) { + + infantry->Assign_Mission(MISSION_ENTER); + infantry->ArchiveTarget = As_Target(); + needed--; + } + } + } + return(DriveClass::Mission_Guard_Area()); +} \ No newline at end of file diff --git a/REDALERT/UNIT.H b/REDALERT/UNIT.H new file mode 100644 index 000000000..778d6a88b --- /dev/null +++ b/REDALERT/UNIT.H @@ -0,0 +1,250 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/UNIT.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef UNIT_H +#define UNIT_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + + +/**************************************************************************** +** For each instance of a unit (vehicle) in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular unit. +*/ +class UnitClass : public DriveClass +{ + public: + + /* + ** This points to the static control data that gives 'this' unit its characteristics. + */ + CCPtr Class; + + /* + ** This records the house flag that this object is currently carrying. + */ + HousesType Flagged; + + /* + ** This flag is used for when the harvester dumps ore, to track its + ** special animation. + */ + unsigned IsDumping:1; + + /* + ** This is a count of the # of loads of the various minerals that the + ** unit has harvested. + */ + unsigned Gold:5; + unsigned Gems:5; + + /* + ** This flag tells a unit that, if after reaching its destination, it + ** should scatter away. It's meant to help a LST unload its units by + ** having its previous passengers get out of the way. + */ + unsigned IsToScatter:1; + + /* + ** This records the number of "loads" of Tiberium the unit is carrying. Only + ** harvesters use this field. + */ + int Tiberium; + + /* + ** This is the area where a mobile gap generator stores the previously-held + ** shroud values for the cells surrounding itself. + */ + unsigned long ShroudBits; + + /* + ** This is the center coordinate for the mobile gap generator, as to + ** what cells should be revealed (according to ShroudBits) + */ + CELL ShroudCenter; + + /* + ** This is the timer that controls the reload rate. The MSAM rocket + ** launcher is the primary user of this. + */ + CDTimerClass Reload; + + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + UnitClass(UnitType classid, HousesType house); + UnitClass(NoInitClass const & x) : DriveClass(x), Class(x), Reload(x), SecondaryFacing(x) {}; + operator UnitType(void) const {return Class->Type;}; + virtual ~UnitClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual ObjectTypeClass const & Class_Of(void) const; + static void Init(void); + + bool Goto_Clear_Spot(void); + bool Try_To_Deploy(void); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + bool Tiberium_Check(CELL ¢er, int x, int y); + bool Flag_Attach(HousesType house); + bool Flag_Remove(void); + bool Goto_Tiberium(int radius); + bool Harvesting(void); + void APC_Close_Door(void); + void APC_Open_Door(void); + + /* + ** Query functions. + */ + bool Should_Crush_It(TechnoClass const * it) const; + int Credit_Load(void) const; + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + int Shape_Number(void) const; + virtual int Pip_Count(void) const; + virtual InfantryType Crew_Type(void) const; + virtual DirType Fire_Direction(void) const; + virtual bool Ok_To_Move(DirType facing) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual fixed Tiberium_Load(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * object) const; + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which=0); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Unload(void); + virtual int Mission_Guard(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int Mission_Repair(void); + virtual int Mission_Move(void); + void Rotation_AI(void); + void Firing_AI(void); + void Reload_AI(void); + bool Edge_Of_World_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Assign_Destination(TARGET target); + virtual void Overrun_Square(CELL cell, bool threaten=true); + virtual void Approach_Target(void); + virtual int Offload_Tiberium_Bail(void); + virtual void Enter_Idle_Mode(bool initial=false); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + void Exit_Repair(void); + void Shroud_Regen(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "UNITS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; +}; + +#endif diff --git a/REDALERT/UTRACKER.CPP b/REDALERT/UTRACKER.CPP new file mode 100644 index 000000000..f2feb58e1 --- /dev/null +++ b/REDALERT/UTRACKER.CPP @@ -0,0 +1,247 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + * * + * Functions: * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" + +/* +** Define host to network to host functions for DOS +*/ +#ifndef WIN32 + +#define htonl(val) 0 +#define ntohl(val) 0 + +#endif //WIN32 + + +/*********************************************************************************************** + * UTC::UnitTrackerClass -- Class constructor * + * * + * * + * * + * INPUT: Number of unit types to reserve space for * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::UnitTrackerClass (int unit_count) +{ + UnitTotals = new long [unit_count]; // Allocate memory for the unit totals + UnitCount = unit_count; // Keep a record of how many unit entries there are + InNetworkFormat = 0; // The unit entries are in host format + Clear_Unit_Total(); // Clear each entry +} + + +/*********************************************************************************************** + * UTC::~UnitTrackerClass -- Class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::~UnitTrackerClass (void) +{ + delete UnitTotals; +} + + + +/*********************************************************************************************** + * UTC::Increment_Unit_Total -- Increment the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:12AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Increment_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]++; +} + + +/*********************************************************************************************** + * UTC::Decrement_Unit_Total -- Decrement the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Decrement_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]--; +} + + +/*********************************************************************************************** + * UTC::Get_All_Totals -- Returns a pointer to the start of the unit totals list * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Ptr to unit totals list * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +long *UnitTrackerClass::Get_All_Totals (void) +{ + return (UnitTotals); +} + + +/*********************************************************************************************** + * UTC::Clear_Unit_Total -- Clear out all the unit totals * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:14AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Clear_Unit_Total (void) +{ + memset (UnitTotals, 0, UnitCount * sizeof(long) ); +} + + + +/*********************************************************************************************** + * UTC::To_Network_Format -- Changes all unit totals to network format for the internet * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:15AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::To_Network_Format (void) +{ + if (!InNetworkFormat){ + for (int i=0 ; i= 0 && unit_type < UnitCount) + { + return UnitTotals[unit_type]; + } + return 0; +} + diff --git a/REDALERT/UTRACKER.H b/REDALERT/UTRACKER.H new file mode 100644 index 000000000..35c216b63 --- /dev/null +++ b/REDALERT/UTRACKER.H @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** UnitTracker Class +*/ + +class UnitTrackerClass { + + public: + + UnitTrackerClass(int unit_count); + ~UnitTrackerClass(void); + + void Increment_Unit_Total (int unit_type); + void Decrement_Unit_Total (int unit_type); + void Clear_Unit_Total(void); + + int Get_Unit_Total (int unit_type); + long *Get_All_Totals (void); + int Get_Unit_Count (void){return (UnitCount);}; + + void To_Network_Format(void); + void To_PC_Format(void); + + private: + + long *UnitTotals; + int UnitCount; + int InNetworkFormat; + +}; + + + + + + + + + + + + + + + + diff --git a/REDALERT/VDATA.CPP b/REDALERT/VDATA.CPP new file mode 100644 index 000000000..bb4a00aa7 --- /dev/null +++ b/REDALERT/VDATA.CPP @@ -0,0 +1,709 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VDATA.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * VesselTypeClass::VesselTypeClass -- Constructor for naval vessel types. * + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel.* + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory poo* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +// Submarine +static VesselTypeClass const VesselSubmarine( + VESSEL_SS, + TXT_SS, // NAME: Text name of this unit type. + "SS", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Destroyer +static VesselTypeClass const VesselDestroyer( + VESSEL_DD, + TXT_DD, // NAME: Text name of this unit type. + "DD", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Cruiser +static VesselTypeClass const VesselCruiser( + VESSEL_CA, + TXT_CA, // NAME: Text name of this unit type. + "CA", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Transport +static VesselTypeClass const VesselTransport( + VESSEL_TRANSPORT, + TXT_TRANSPORT, // NAME: Text name of this unit type. + "LST", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); + +// Gun Boat +static VesselTypeClass const VesselPTBoat( + VESSEL_PT, + TXT_PT, // NAME: Text name of this unit type. + "PT", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Missile Submarine +static VesselTypeClass const VesselMissileSubmarine( + VESSEL_MISSILESUB, + TXT_MISSILESUB, // NAME: Text name of this unit type. + "MSUB", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Transport +static VesselTypeClass const VesselCarrier( + VESSEL_CARRIER, + TXT_CARRIER, // NAME: Text name of this unit type. + "CARR", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); +#endif + +/*********************************************************************************************** + * VesselTypeClass::VesselTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the vessel static data. Each vessels is assign a specific * + * variation. This class elaborates what the variation actually is. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created * + *=============================================================================================*/ +VesselTypeClass::VesselTypeClass( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ) : + TechnoTypeClass( + RTTI_VESSELTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + secondaryoffset, + secondarylateral, + is_nominal, + false, + true, + true, + false, + false, + false, + is_turret_equipped, + true, + true, + rotation, + SPEED_FLOAT + ), + IsPieceOfEight(is_eight), + Type(type), + TurretOffset(toffset), + Mission(MISSION_GUARD), + Explosion(exp), + MaxSize(0) +{ + /* + ** Forced vessel overrides from the default. + */ + IsCrew = false; + Speed = SPEED_FLOAT; + IsScanner = true; +} + + +/*********************************************************************************************** + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory pool * + * * + * This will allocate a vessel type class object from the memory pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated vessel type class object. If memory in the * + * special heap has been exhaused, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselTypeClass::operator new(size_t) +{ + return(VesselTypes.Alloc()); +} + + +/*********************************************************************************************** + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * * + * This will return a previously allocated vessel object back to the special pool from * + * whence it was originally allocated. * + * * + * INPUT: pointer -- Pointer to the vessel type object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::operator delete(void * pointer) +{ + VesselTypes.Free((VesselTypeClass *)pointer); +} + + +/*********************************************************************************************** + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * * + * This will pre-allocate all the vessel types required. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once and do so before processing the rules.ini file. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Init_Heap(void) +{ + /* + ** These vessel type class objects must be allocated in the exact order that they + ** are specified in the VesselType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new VesselTypeClass(VesselSubmarine); // VESSEL_SS + new VesselTypeClass(VesselDestroyer); // VESSEL_DD + new VesselTypeClass(VesselCruiser); // VESSEL_CA + new VesselTypeClass(VesselTransport); // VESSEL_TRANSPORT + new VesselTypeClass(VesselPTBoat); // VESSEL_PT +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new VesselTypeClass(VesselMissileSubmarine); // VESSEL_MISSILESUB +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new VesselTypeClass(VesselCarrier); // VESSEL_CARRIER +#endif +} + + +/*********************************************************************************************** + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * * + * This routine will fetch a reference to the vessel type that corresponds to the vessel * + * type specified. * + * * + * INPUT: type -- The vessel type number to convert. * + * * + * OUTPUT: Returns with a reference to the vessel type class that corresponds to the vessel * + * type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselTypeClass & VesselTypeClass::As_Reference(VesselType type) +{ + return(*VesselTypes.Ptr(type)); +} + + +#ifdef NEVER +/*********************************************************************************************** + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel. * + * * + * Use this routine to fetch a pointer to the vessel factory that can build this vessel * + * type. * + * * + * INPUT: intheory -- If true, then this indicates that if the factory is currently * + * busy doing other things, this won't make in ineligible for searching. * + * Typical use of this is by the sidebar logic which needs only to know * + * if theoretical production is allowed. * + * * + * legal -- If true, then the buildings are checked for specific legality when * + * being scanned. For building placement, this is usually false, for * + * sidebar button adding, this is usually true. * + * * + * house -- The owner of the unit to be produced. This has an effect of legality. * + * * + * OUTPUT: Returns with a pointer to the factory (building) that can produce this vessel type.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * VesselTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_VESSELTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} +#endif + + +/*********************************************************************************************** + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * * + * This routine is used by the scenario editor to display a representation of this * + * vessel type in the object placement dialog. * + * * + * INPUT: x,y -- Pixel coordinate to render the center of this vessel type to. * + * * + * window -- The window to clip the shape to. The pixel coordinates are relative * + * to this window. * + * * + * house -- The owner of the vessel. This is used to give the vessel its color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef SCENARIO_EDITOR +void VesselTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = Rotation/6; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * * + * This routine is called when the scenario editor needs to obtain a list of the * + * vessel object that can be placed down. It will submit all the vessel types that can * + * be placed down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Prep_For_Add(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif //SCENARIO_EDITOR + + +/*********************************************************************************************** + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * * + * This routine is called when the type of vessel is known (by way of a VesselTypeClass) * + * and a corresponding vessel object needs to be created. * + * * + * INPUT: house -- Pointer to the owner that this vessel will be assigned to. * + * * + * OUTPUT: Returns with a pointer to the vessel object created. If no vessel could be * + * created, then NULL is returned. * + * * + * WARNINGS: The vessel is created in a limbo state. It must first be placed down upon * + * the map before it starts to function. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +ObjectClass * VesselTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new VesselClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * * + * This routine is used to create a vessel and then place it down upon the * + * map. * + * * + * INPUT: cell -- The location to place this vessel down upon. * + * * + * house -- The house to assign this vessel's ownership to. * + * * + * OUTPUT: bool; Was the vessel successfully created and placed down upon the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + VesselClass * unit = new VesselClass(Type, house); + if (unit != NULL) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + delete unit; + return(false); +} + + +/*********************************************************************************************** + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * * + * This routine is used to fetch the width and height of this vessel type. These dimensions * + * are not specific to any particular facing. Rather, they are only for the generic vessel * + * size. * + * * + * INPUT: width, height -- Reference to the integers that are to be initialized with the * + * pixel width and height of this vessel type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Dimensions(int & width, int & height) const +{ + width = 48; + height = 48; +} + + +/*********************************************************************************************** + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * * + * This routine will load in the vessel shape data. It should be called only once at the * + * beginning of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::One_Time(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + VesselTypeClass const & uclass = As_Reference(index); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (uclass.Level != -1 || index==VESSEL_CARRIER) { +#else + if (uclass.Level != -1) { +#endif +// if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch a pointer to the unit's shape data. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + + ((int &)uclass.MaxSize) = 26; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * * + * This routine will determine the pixel adjustment necessary for a turret. The direction * + * specified is what the vessel body is facing. * + * * + * INPUT: dir -- The presumed direction of the body facing for the vessel. * + * * + * x,y -- The center pixel position for the vessel. These values should be * + * adjusted (they are references) to match the adjusted offset for the * + * turret. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Turret_Adjust(DirType dir, int & x, int & y) const +{ + short xx = x; + short yy = y; + + switch (Type) { + case VESSEL_CA: + Normal_Move_Point(xx, yy, dir, 22); + x = xx; + y = yy-4; + break; + + case VESSEL_PT: + Normal_Move_Point(xx, yy, dir, 14); + x = xx; + y = yy+1; + break; + + case VESSEL_DD: + Normal_Move_Point(xx, yy, dir+DIR_S, 8); + x = xx; + y = yy-4; + break; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * * + * This routine will return the overlap list for a vessel that is sitting still in the * + * center of a cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list that this vessel would use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselTypeClass::Overlap_List(void) const +{ + static short const _ship[] = {-3, -2, -1, 1, 2, 3, + -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), -(MAP_CELL_W+2), -(MAP_CELL_W-2), + +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +(MAP_CELL_W+2), +(MAP_CELL_W-2), + REFRESH_EOL}; +// static short const _ship[] = {-1, 1, +// -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), +// +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +// REFRESH_EOL}; + + return(&_ship[0]); +} + + +/*********************************************************************************************** + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * * + * Use this routine to convert an ASCII version of a vessel type into the corresponding * + * VesselType id value. Typical use of this would be to parse the INI file. * + * * + * INPUT: name -- Pointer to the ASCII name to be converted into a vessel type. * + * * + * OUTPUT: Returns with the vessel type number that matches the string specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselType VesselTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (VesselType classid = VESSEL_FIRST; classid < VESSEL_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(VESSEL_NONE); +} + + +/*********************************************************************************************** + * VesselTypeClass::Max_Pips -- Fetches the maximum pips allowed for this vessel. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this vessel type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselTypeClass::Max_Pips(void) const +{ + return(Max_Passengers()); +} diff --git a/REDALERT/VECTOR.CPP b/REDALERT/VECTOR.CPP new file mode 100644 index 000000000..87774343d --- /dev/null +++ b/REDALERT/VECTOR.CPP @@ -0,0 +1,672 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VECTOR.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BooleanVectorClass::BooleanVectorClass -- Copy constructor for boolean array. * + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * BooleanVectorClass::operator = -- Assignment operator. * + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::ID -- Finds object ID based on value. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::Resize -- Changes the size of the vector. * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +#include "vector.h" +//#include +#include + +/* +** The following template function can be located here ONLY if all the instantiations are +** declared in a header file this module includes. By placing the template functions here, +** it speeds up compiler operation and reduces object module size. +*/ +#if (0) +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) : + Vector(0), + VectorMax(size), + IsAllocated(false) +{ + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) : + Vector(0), + VectorMax(0), + IsAllocated(false) +{ + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + if (this != &vector) { + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasible) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assignment operator will + ** only work for the simplest of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} +#endif +//---------------------------------------------------------------------------------------------- + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * * + * This is the constructor for a boolean array. This constructor takes the memory pointer * + * provided as assigns that as the array data pointer. * + * * + * INPUT: size -- The size of the array (in bits). * + * * + * array -- Pointer to the memory that the array is to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must make sure that the memory specified is large enough to contain the * + * bits specified. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(unsigned size, unsigned char * array) +{ + BitArray.Resize(((size + (8-1)) / 8), array); + LastIndex = -1; + BitCount = size; +} + + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Copy constructor of boolean array. * + * * + * This is the copy constructor for a boolean array. It is used to make a duplicate of the * + * boolean array. * + * * + * INPUT: vector -- Reference to the vector to be duplicated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(BooleanVectorClass const & vector) +{ + LastIndex = -1; + *this = vector; +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator = -- Assignment operator. * + * * + * This routine will make a copy of the specified boolean vector array. The vector is * + * copied into an already constructed existing vector. The values from the existing vector * + * are destroyed by this copy. * + * * + * INPUT: vector -- Reference to the vector to make a copy of. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass & BooleanVectorClass::operator =(BooleanVectorClass const & vector) +{ + Fixup(); + Copy = vector.Copy; + LastIndex = vector.LastIndex; + BitArray = vector.BitArray; + BitCount = vector.BitCount; + return(*this); +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * * + * This is the comparison operator for a boolean vector class. Boolean vectors are equal * + * if the bit count and bit values are identical. * + * * + * INPUT: vector -- Reference to the vector to compare to. * + * * + * OUTPUT: Are the boolean vectors identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::operator == (const BooleanVectorClass & vector) +{ + Fixup(LastIndex); + return(BitCount == vector.BitCount && BitArray == vector.BitArray); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * * + * This routine will resize the boolean vector object. An index value used with a boolean * + * vector must be less than the value specified in as the new size. * + * * + * INPUT: size -- The new maximum size of this boolean vector. * + * * + * OUTPUT: Was the boolean vector sized successfully? * + * * + * WARNINGS: The boolean array might be reallocated or even deleted by this routine. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::Resize(unsigned size) +{ + Fixup(); + + if (size) { + + /* + ** Record the previous bit count of the boolean vector. This is used + ** to determine if the array has grown in size and thus clearing is + ** necessary. + */ + int oldsize = BitCount; + + /* + ** Actually resize the bit array. Since this is a bit packed array, + ** there are 8 elements per byte (rounded up). + */ + int success = BitArray.Resize(((size + (8-1)) / 8)); + + /* + ** Since there is no default constructor for bit packed integers, a manual + ** clearing of the bits is required. + */ + BitCount = size; + if (success && oldsize < (int)size) { + for (int index = oldsize; index < (int)size; index++) { + (*this)[index] = 0; + } + } + + return(success); + } + + /* + ** Resizing to zero is the same as clearing and deallocating the array. + ** This is always successful. + */ + Clear(); + return(true); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * * + * This routine will clear out the boolean array. This will free any allocated memory and * + * result in the boolean vector being unusable until the Resize function is subsequently * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The boolean vector cannot be used until it is resized to a non null condition. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Clear(void) +{ + Fixup(); + BitCount = 0; + BitArray.Clear(); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * * + * This is the preferred (and quick) method to clear the boolean array to a false condition.* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Reset(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\0', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * * + * This is the preferred (and fast) way to set all boolean elements to true. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Set(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\xFF', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * * + * Use this routine to set the boolean value copy to match the appropriate bit in the * + * boolean array. The boolean array will be updated with any changes from the last time * + * a value was fetched from the boolean vector. By using this update method, the boolean * + * array can be treated as a normal array even though the elements are composed of * + * otherwise inaccessible bits. * + * * + * INPUT: index -- The index to set the new copy value to. If the index is -1, then the * + * previous value will be updated into the vector array, but no new value * + * will be fetched from it. * + * * + * OUTPUT: none * + * * + * WARNINGS: Always call this routine with "-1" if any direct manipulation of the bit * + * array is to occur. This ensures that the bit array is accurate. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Fixup(int index) const +{ + /* + ** If the requested index value is illegal, then force the index + ** to be -1. This is the default non-index value. + */ + if (index >= BitCount) { + index = -1; + } + + /* + ** If the new index is different than the previous index, there might + ** be some fixing up required. + */ + if (index != LastIndex) { + + /* + ** If the previously fetched boolean value was changed, then update + ** the boolean array accordingly. + */ + if (LastIndex != -1) { + Set_Bit((void*)&BitArray[0], LastIndex, Copy); + } + + /* + ** If this new current index is valid, then fill in the reference boolean + ** value with the appropriate data from the bit array. + */ + if (index != -1) { + ((unsigned char&)Copy) = Get_Bit(&BitArray[0], index); + } + + ((int &)LastIndex) = index; + } +} + + diff --git a/REDALERT/VECTOR.H b/REDALERT/VECTOR.H new file mode 100644 index 000000000..2500f86bb --- /dev/null +++ b/REDALERT/VECTOR.H @@ -0,0 +1,1012 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VECTOR.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::Resize -- Changes the size of the vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::ID -- Finds object ID based on value. * + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VECTOR_H +#define VECTOR_H + +#ifdef NEVER +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif +#endif + +#include +#include + +// ST - 5/8/1029 +//inline void * operator new(size_t , void * pointer) {return(pointer);} +//inline void * operator new[](size_t , void * pointer) {return(pointer);} + + +/************************************************************************** +** This is a general purpose vector class. A vector is defined by this +** class, as an array of arbitrary objects where the array can be dynamically +** sized. Because is deals with arbitrary object types, it can handle everything. +** As a result of this, it is not terribly efficient for integral objects (such +** as char or int). It will function correctly, but the copy constructor and +** equality operator could be highly optimized if the integral type were known. +** This efficiency can be implemented by deriving an integral vector template +** from this one in order to supply more efficient routines. +*/ +template +class VectorClass +{ + public: + VectorClass(NoInitClass const & ) {}; + VectorClass(unsigned size=0, T const * array=0); + VectorClass(VectorClass const &); // Copy constructor. + virtual ~VectorClass(void); + + T & operator[](unsigned index) {return(Vector[index]);}; + T const & operator[](unsigned index) const {return(Vector[index]);}; + virtual VectorClass & operator =(VectorClass const &); // Assignment operator. + virtual int operator == (VectorClass const &) const; // Equality operator. + virtual int Resize(unsigned newsize, T const * array=0); + virtual void Clear(void); + unsigned Length(void) const {return VectorMax;}; + virtual int ID(T const * ptr); // Pointer based identification. + virtual int ID(T const & ptr); // Value based identification. + + protected: + + /* + ** This is a pointer to the allocated vector array of elements. + */ + T * Vector; + + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; + + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated:1; +}; + + +/************************************************************************** +** This derivative vector class adds the concept of adding and deleting +** objects. The objects are packed to the beginning of the vector array. +** If this is instantiated for a class object, then the assignment operator +** and the equality operator must be supported. If the vector allocates its +** own memory, then the vector can grow if it runs out of room adding items. +** The growth rate is controlled by setting the growth step rate. A growth +** step rate of zero disallows growing. +*/ +template +class DynamicVectorClass : public VectorClass +{ + public: + DynamicVectorClass(unsigned size=0, T const * array=0); + + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const * array=0); + + // Resets and frees the vector array. + virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + + // Fetch number of "allocated" vector objects. + int Count(void) const {return(ActiveCount);}; + + // Add object to vector (growing as necessary). + int Add(T const & object); + int Add_Head(T const & object); + + // Delete object just like this from vector. + int Delete(T const & object); + + // Delete object at this vector index. + int Delete(int index); + + // Deletes all objects in the vector. + void Delete_All(void) {ActiveCount = 0;}; + + // Set amount that vector grows by. + int Set_Growth_Step(int step) {return(GrowthStep = step);}; + + // Fetch current growth step rate. + int Growth_Step(void) {return GrowthStep;}; + + virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; + virtual int ID(T const & ptr); + + protected: + + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; + + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; +}; + + +/************************************************************************** +** A fixed-size array of dynamic vectors. +*/ +template +class DynamicVectorArrayClass +{ +public: + static const int COUNT = COUNT; + + DynamicVectorArrayClass() : Active(DEFAULT) {} + + void Set_Active_Context(int active) + { + Active = active; + } + + void Clear_All() + { + for (int i = FIRST; i < COUNT; ++i) + { + Clear(i); + } + } + + void Clear() + { + Clear(Active); + } + + int Count() const + { + return Count(Active); + } + + int Add(T const & object) + { + return Add(Active, object); + } + + int Add_Head(T const & object) + { + return Add_Head(Active, object); + } + + int Delete(T const & object) + { + return Delete(Active, object); + } + + int Delete_All(T const & object) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + count += Delete(i, object); + } + return count; + } + + int Delete_All_Except(T const & object, int except) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + if (except != i) + { + count += Delete(i, object); + } + } + return count; + } + + int Delete(int index) + { + return Delete(Active, index); + } + + T & operator[](unsigned index) + { + return Collection[Active][index]; + } + + T const & operator[](unsigned index) const + { + return Collection[Active][index]; + } + + void Clear(int context) + { + Collection[context].Clear(); + } + + int Count(int context) const + { + return Collection[context].Count(); + } + + int Add(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Add_Head(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Delete(int context, T const & object) + { + return Collection[context].Delete(object); + } + + int Delete(int context, int index) + { + return Collection[context].Delete(index); + } + + DynamicVectorClass & Raw() + { + return Collection[Active]; + } + + DynamicVectorClass & Raw(int context) + { + return Collection[context]; + } + +private: + DynamicVectorClass Collection[COUNT]; + int Active; +}; + + +/************************************************************************** +** This is a derivative of a vector class that supports boolean flags. Since +** a boolean flag can be represented by a single bit, this class packs the +** array of boolean flags into an array of bytes containing 8 boolean values +** each. For large boolean arrays, this results in an 87.5% savings. Although +** the indexing "[]" operator is supported, DO NOT pass pointers to sub elements +** of this bit vector class. A pointer derived from the indexing operator is +** only valid until the next call. Because of this, only simple +** direct use of the "[]" operator is allowed. +*/ +class BooleanVectorClass +{ + public: + BooleanVectorClass(unsigned size=0, unsigned char * array=0); + BooleanVectorClass(BooleanVectorClass const & vector); + + // Assignment operator. + BooleanVectorClass & operator =(BooleanVectorClass const & vector); + + // Equivalency operator. + int operator == (BooleanVectorClass const & vector); + + // Fetch number of boolean objects in vector. + int Length(void) {return BitCount;}; + + // Set all boolean values to false; + void Reset(void); + + // Set all boolean values to true. + void Set(void); + + // Resets vector to zero length (frees memory). + void Clear(void); + + // Change size of this boolean vector. + int Resize(unsigned size); + + // Fetch reference to specified index. + bool const & operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool & operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index)); + }; + + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_False_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + private: + void Fixup(int index=-1) const; + + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; +}; + + + + + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previously allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previously and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= (int)Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } + else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * * + * This routine will add the specified element to the head of the vector. If necessary, * + * the vector will be expanded accordingly. * + * * + * INPUT: object -- Reference to the object to add to the head of this vector. * + * * + * OUTPUT: bool; Was the object added without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= (int)Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } + else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; + // (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + return(Delete(ID(object))); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if (index >= 0 && index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i + 1]; + } + return(true); + } + return(false); +} + + + + + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) : + Vector(0), + VectorMax(size), + IsAllocated(false) +{ + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } + else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) : + Vector(0), + VectorMax(0), + IsAllocated(false) +{ + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + if (this != &vector) { + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } + else { + Vector = 0; + IsAllocated = false; + } + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } + else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasible) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assignment operator will + ** only work for the simplest of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } + else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} + +#endif diff --git a/REDALERT/VERSION.CPP b/REDALERT/VERSION.CPP new file mode 100644 index 000000000..53b245ef9 --- /dev/null +++ b/REDALERT/VERSION.CPP @@ -0,0 +1,850 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VERSION.CPP 14 3/16/97 10:16p Joe_b $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : VERSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : September 17, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VersionClass::VersionClass -- Class constructor * + * VersionClass::Version_Number -- Returns program version number * + * VersionClass::Major_Version -- returns major version # * + * VersionClass::Minor_Version -- returns minor version (revision) number* + * VersionClass::Version_Name -- returns version # as char string * + * VersionClass::Read_Text_String -- reads version text string from disk * + * VersionClass::Version_Protocol -- returns default protocol for version* + * VersionClass::Init_Clipping -- Initializes version clipping * + * VersionClass::Clip_Version -- "clips" the given version range * + * VersionClass::Min_Version -- returns lowest version # to connect to * + * VersionClass::Max_Version -- returns highest version # to connect to * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "rawolapi.h" // For version number. +#endif + +/****************************** Globals ************************************/ +//--------------------------------------------------------------------------- +// This is a table of version numbers # the communications protocol used for +// that version number. It's used by the game owner to determine the +// protocol to be used for a given session. +// +// This table needs to be updated every time a new communications protocol +// is implemented, not every time a new version is created. +// +// A given protocol is used from its corresponding version #, up to (but not +// including) the next version number in the table. The last protocol in +// the table is the default protocol for this version. +//--------------------------------------------------------------------------- +static VersionProtocolType VersionProtocol[] = { + {0x00001000,COMM_PROTOCOL_SINGLE_NO_COMP}, // (obsolete) + {0x00002000,COMM_PROTOCOL_SINGLE_E_COMP}, // (obsolete) + {0x00010000,COMM_PROTOCOL_MULTI_E_COMP}, +}; + + +/*************************************************************************** + * VersionClass::VersionClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + * 09/17/1996 JLB : Converted to used initializer list. * + *=========================================================================*/ +VersionClass::VersionClass(void) : + Version(0), + MajorVer(0), + MinorVer(0), + MinClipVer(0), + MaxClipVer(0), + VersionInit(false), + MajorInit(false), + MinorInit(false), + TextInit(false) +{ + VersionText[0] = '\0'; + VersionName[0] = '\0'; +} + + +/*************************************************************************** + * VersionClass::Version_Number -- Returns program version number * + * * + * Version Number Format: * + * Non-CHEAT format: * + * Byte 3,2: major version (printed to the left of a decimal) * + * Byte 1,0: minor version (printed to the right of a decimal) * + * Thus, version 1.07 would appear as 0x0001 0700 * + * * + * This format guarantees that a greater-than or less-than comparison * + * will work on version numbers. * + * * + * CHEAT format: * + * Byte 3: Month # * + * Byte 2: Day # * + * Byte 1: Hour # * + * Byte 0: Minute # * + * * + * This format guarantees a unique version number for each compile (as * + * long as they're a minute or more apart), with increasing version #'s * + * for later times. * + * * + * Either format should be printed in hex. * + * * + * This routine also fills in a text string (retrieved with Version_Text), * + * which may contain a custom string (such as "Beta"); this string is * + * read from the file VERSION.TXT. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ + +//ajw Note: This function is no longer called. MIN_VERSION is now incorrect, but I don't have time +// for a full rebuild (3 hrs!), and as MIN_VERSION is no longer referred to, I'm going to leave it. +// Really, it should be deleted or commented out. +// Version number used is now GAME_VERSION. +// Note also that VERSION_RA_300 is wrong, but not used. + +unsigned long VersionClass::Version_Number(void) +{ + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the version has already been set, just return it. + //------------------------------------------------------------------------ + if (VersionInit) { + return (Version); + } + + //------------------------------------------------------------------------ + // Generate the version # + //------------------------------------------------------------------------ + Version = ((Major_Version() << 16) | Minor_Version()); + VersionInit = 1; + + return (Version); + +} /* end of Version_Number */ + + +/*************************************************************************** + * VersionClass::Major_Version -- returns major version # * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Major Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Major_Version(void) +{ +#ifdef DEV_VERSION + static char * date = __DATE__; // format: Mmm dd yyyy + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char buf[10]; + char * ptr; + char * tok; + int monthnum; + int daynum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the major version # is already set, just return it. + //------------------------------------------------------------------------ + if (MajorInit) { + return (MajorVer); + } + + //------------------------------------------------------------------------ + // For a development version, use the date (month & day) as the major + // version number. + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the month and place in the high byte. + //........................................................................ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + monthnum = (((ptr - months) / 3) + 1); + } else { + monthnum = 0; + } + + //........................................................................ + // Convert the month number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",monthnum); + sscanf(buf,"%x",&monthnum); + + //........................................................................ + // Fetch the date and place that in the low byte. + //........................................................................ + tok = strtok(NULL, " "); + if (tok) { + daynum = atoi(tok); + } else { + daynum = 0; + } + + //........................................................................ + // Convert the day number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",daynum); + sscanf(buf,"%x",&daynum); + + MajorVer = ((monthnum << 8) | daynum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor version number. + //------------------------------------------------------------------------ +#else + MajorVer = MAJOR_VERSION; +#endif + + MajorInit = 1; + + return (MajorVer); + +} /* end of Major_Version */ + + +/*************************************************************************** + * VersionClass::Minor_Version -- returns minor version (revision) number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Minor Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Minor_Version(void) +{ +#ifdef DEV_VERSION + static char * time = __TIME__; // format: hh:mm:ss + char * tok; + char buf[10]; + int hournum; + int minnum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the minor version # is already set, just return it. + //------------------------------------------------------------------------ + if (MinorInit) { + return (MinorVer); + } + + //------------------------------------------------------------------------ + // For in-development versions, use the time (hour & min) as the minor + // version + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the hour and place that in the last two digit positions. + //........................................................................ + tok = strtok(time, ": "); + if (tok) { + hournum = atoi(tok); + } else { + hournum = 0; + } + + //........................................................................ + // Convert the hour number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",hournum); + sscanf(buf,"%x",&hournum); + + //........................................................................ + // Fetch the minute and place that in the last two digit positions. + //........................................................................ + tok = strtok(NULL, ": "); + if (tok) { + minnum = atoi(tok); + } else { + minnum = 0; + } + + //........................................................................ + // Convert the minute number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",minnum); + sscanf(buf,"%x",&minnum); + + MinorVer = ((hournum << 8) | minnum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor revision number. + //------------------------------------------------------------------------ +#else + +#ifdef FIXIT_VERSION_3 // Insanity. CS installation should not have affected version number. ajw + + MinorVer = MINOR_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_CSII + MinorVer = MINOR_VERSION; + if (Is_Counterstrike_Installed()) { + MinorVer = MINOR_VERSION - 1; + } +#else + #ifdef FIXIT_VERSION + /* If counterstrike is not installed then we report version 1.06 + * otherwise we report ourselves as 1.08 + */ + if (Is_Counterstrike_Installed() == false) { + MinorVer = (MINOR_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + MinorVer = MINOR_VERSION; + } + #else + MinorVer = MINOR_VERSION; + #endif +#endif + +#endif // FIXIT_VERSION_3 + +#endif + + MinorInit = 1; + + return (MinorVer); + +} /* end of Minor_Version */ + + +/*************************************************************************** + * VersionClass::Version_Name -- returns version # as char string * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +char * VersionClass::Version_Name(void) +{ + //------------------------------------------------------------------------ + // For developmental versions, just use the major & minor version #'s + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), VerNum.Minor_Version()); + + //------------------------------------------------------------------------ + // For final versions, trim 0's off the minor version + //------------------------------------------------------------------------ +#else + unsigned short adjusted_minor; + int i; + + adjusted_minor = Minor_Version(); + for (i = 0; i < 4; i++) { + if ( (adjusted_minor & 0x000f) != 0) { + break; + } + adjusted_minor >>= 4; + } + + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), adjusted_minor); +#endif + + return (VersionName); + +} /* end of Version_Name */ + + +/*************************************************************************** + * VersionClass::Read_Text_String -- reads version # text string from disk * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Read_Text_String(void) +{ + RawFileClass file("VERSION.TXT"); + + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[strlen(VersionText)-1] == '\r') { + VersionText[strlen(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + +} /* end of Read_Text_String */ + + +/*************************************************************************** + * VersionClass::Version_Protocol -- returns default protocol for version * + * * + * INPUT: * + * version version # to look up * + * * + * OUTPUT: * + * protocol value to use for that version # * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +CommProtocolType VersionClass::Version_Protocol(unsigned long version) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # entries in the VersionProtocol table + //------------------------------------------------------------------------ + j = sizeof (VersionProtocol) / sizeof(VersionProtocolType); + + //------------------------------------------------------------------------ + // Search backwards through the table, finding the first entry for which + // the given version # is >= the table's; this is the range containing + // the given version number. + //------------------------------------------------------------------------ + for (i = j - 1; i >= 0; i--) { + if (version >= VersionProtocol[i].Version) { + return (VersionProtocol[i].Protocol); + } + } + + //------------------------------------------------------------------------ + // If no range was found for the given version, return the highest + // possible protocol. (If version clipping is being done properly, this + // case should never happen, but never say never.) + //------------------------------------------------------------------------ + return (VersionProtocol[j-1].Protocol); + +} /* end of Version_Protocol */ + + +/*************************************************************************** + * VersionClass::Init_Clipping -- Initializes version clipping * + * * + * Initializes the Min & Max clip version #'s to the min & max values * + * defined for this program. This sets the initial range for use by * + * the Clip_Version routine. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Init_Clipping(void) +{ + MinClipVer = Min_Version(); + MaxClipVer = Max_Version(); + +} /* end of Init_Clipping */ + + +/*************************************************************************** + * VersionClass::Clip_Version -- "clips" the given version range * + * * + * This routine compares another program's supported min/max version * + * range with the range currently defined by 'MinClipVer' and 'MaxClipVer'.* + * If there is overlap in the two ranges, Min & MaxClipVer are adjusted * + * to the bounds of the overlap. The routine returns the largest version * + * number shared by the ranges (MaxClipVer). * + * * + * Thus, by calling Init_Clipping(), then a series of Clip_Version() calls,* + * a mutually-acceptable range of version #'s may be negotiated between * + * different versions of this program. The max shared version may then * + * be used to decide upon a communications protocol that all programs * + * support. * + * * + * INPUT: * + * minver min version to clip to * + * maxver max version to clip to * + * * + * OUTPUT: * + * highest clipped version # * + * 0 = given range is below our current range * + * 0xFFFFFFFF = given range is above our current range * + * * + * WARNINGS: * + * Be sure Init_Clipping() was called before performing a clipping * + * session. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Clip_Version(unsigned long minver, + unsigned long maxver) +{ + //------------------------------------------------------------------------ + // If the given range is outside & above our own, return an error. + //------------------------------------------------------------------------ + if (minver > MaxClipVer) + return (0xffffffff); + + //------------------------------------------------------------------------ + // If the given range is outside & below our own, return an error. + //------------------------------------------------------------------------ + if (maxver < MinClipVer) + return (0); + + //------------------------------------------------------------------------ + // Clip the lower range value + //------------------------------------------------------------------------ + if (minver > MinClipVer) + MinClipVer = minver; + + //------------------------------------------------------------------------ + // Clip the upper range value + //------------------------------------------------------------------------ + if (maxver < MaxClipVer) + MaxClipVer = maxver; + + //------------------------------------------------------------------------ + // Return the highest version supported by the newly-adjusted range. + //------------------------------------------------------------------------ + return (MaxClipVer); + +} /* end of Clip_Version */ + + +/*************************************************************************** + * VersionClass::Min_Version -- returns lowest version # to connect to * + * * + * Returns the minimum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * min version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Min_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_VERSION + if ( Is_Counterstrike_Installed() ) { + return (MIN_VERSION - 1); + } + return (MIN_VERSION); +#else + if ( Is_Counterstrike_Installed() ){ + return (MIN_VERSION - CS_MINOR_VERSION_MODIFIER); + }else{ + return (MIN_VERSION); + } +#endif + +#endif // FIXIT_VERSION_3 + +#endif + +} /* end of Min_Version */ + + +/*************************************************************************** + * VersionClass::Max_Version -- returns highest version # to connect to * + * * + * Returns the maximum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * max version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Max_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else + +#ifdef FIXIT_CSII // checked - ajw + return (MAX_VERSION); +#else + #ifdef FIXIT_VERSION + if (Is_Counterstrike_Installed() == false) { + return (MAX_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + return (MAX_VERSION); + } + #else + if ( Is_Counterstrike_Installed() ){ + return (MAX_VERSION + CS_MINOR_VERSION_MODIFIER); + }else{ + return (MAX_VERSION); + } + #endif +#endif +#endif + +#endif // FIXIT_VERSION_3 + +} /* end of Max_Version */ + + +char const * Version_Name(void) +{ +#ifdef NEVER + static char buffer[32]; + + /* + ** Fetch the day and month components from the current + ** build date. + */ + static char * date = __DATE__; // format: Mmm dd yyyy + strupr(date); + char const * tok = strtok(date, " "); + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char const * ptr = strstr(months, tok); + int monthnum = 0; + if (ptr != NULL) { + monthnum = (((ptr - months) / 3) + 1); + } + + tok = strtok(NULL, " "); + int daynum = 0; + if (tok != NULL) { + daynum = atoi(tok); + } + + /* + ** Fetch the time components from the current build time. + */ + static char * time = __TIME__; // format: hh:mm:ss + tok = strtok(time, ": "); + int hournum = 0; + if (tok != NULL) { + hournum = atoi(tok); + } + + tok = strtok(NULL, ": "); + int minnum = 0; + if (tok != NULL) { + minnum = atoi(tok); + } + + sprintf(buffer, "%02d%02d%02d", monthnum, daynum, (hournum*4) + (minnum / 15)); + return(buffer); +#else + + static char buffer[128]; + + memset(buffer, '\0', sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + strcpy( buffer, "3.03" ); + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_PATCH_108 + //strcpy(buffer, "1.08PE"); + strcpy(buffer, "1.08P"); + +#ifdef FIXIT_CSII + strcpy(buffer,"2.00"); +#ifdef DEV_VERSION + strcpy(buffer,VerNum.Version_Name()); +#endif +#ifdef DEV_VER_NAME + strcpy(buffer,__DATE__); // format: Mmm dd yyyy +#endif +#endif + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else + strcpy(buffer, "1.07E"); +#endif + +#endif // FIXIT_VERSION_3 + + if (Is_Counterstrike_Installed ()){ + strcat (buffer, "CS"); + } + if (Is_Aftermath_Installed()) { + strcat (buffer, "AM"); + } + +#if(TEN) + strcat(buffer, "Ten"); // Ten version +#endif + +#if(MPATH) + strcat(buffer, "MPath"); // MPath version +#endif + + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + strcat(buffer, "\r"); + file.Read(&buffer[strlen(buffer)], 25); + } + return(buffer); +#endif +} +#endif \ No newline at end of file diff --git a/REDALERT/VERSION.H b/REDALERT/VERSION.H new file mode 100644 index 000000000..eab28b11f --- /dev/null +++ b/REDALERT/VERSION.H @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/VERSION.H 2 3/10/97 6:22p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : VERSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : October 26, 1995 [BRR] * + * * + * This class maintains version information, and communications protocol * + * information. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_RED_ALERT_104 0x00010000 //Shipped US version number +#define VERSION_RED_ALERT_107 0x00011000 //Shipped Counterstrike number +#define VERSION_RED_ALERT_106 0x00010500 //Patch without CS installed +#define VERSION_RED_ALERT_108 0x00011500 //Patch with CS installed +#define VERSION_AFTERMATH_CS 0x00011FFF //Aftermath with CS installed +#define VERSION_AFTERMATH 0x00012000 //Aftermath + +// Aftermath has, in a sense, used version 2.00. (Because of the text on title screen.) Call ourselves version 3. +#define VERSION_RA_300 0x00030000 // RA, CS, AM executables unified into one. All are now the same version. -ajw +// It seems that extra information, that didn't belong there, was being stuffed into version number. Namely, whether or not +// Counterstrike is installed. I'm going to change things back to the way they should be, as I see it. Version will describe +// the version of the executable only. When it comes to communicating whether or not a player has expansions present, separate +// data will be transmitted. + +// However, having said that, a caveat. I'm going to have to use the same communication method that was used previously, because +// I need to have prior versions of the game recognize that they can't play against this version. What I'll do is encode +// "does player have aftermath" (which is actually the only fact that matters, in multiplayer) in the communicated version +// number, as a high bit set/unset. This version of the game will receive this communicated value and pull out the Aftermath +// bit. Older version will reject us as a possible opponent, because, whether or not AM is installed, our version number will +// be too high for them. + +// These horrible things are no longer used. ajw +#define CS_MAJOR_VERSION_MODIFIER 0x0000 +#define CS_MINOR_VERSION_MODIFIER 0x1000 + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +typedef enum CommProtocolEnum { + COMM_PROTOCOL_SINGLE_NO_COMP = 0, // single frame with no compression + COMM_PROTOCOL_SINGLE_E_COMP, // single frame with event compression + COMM_PROTOCOL_MULTI_E_COMP, // multiple frame with event compression + COMM_PROTOCOL_COUNT, + DEFAULT_COMM_PROTOCOL = COMM_PROTOCOL_MULTI_E_COMP +} CommProtocolType; + +typedef struct { + unsigned long Version; + CommProtocolType Protocol; +} VersionProtocolType; + +class VersionClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + VersionClass(void); + virtual ~VersionClass() {}; + + //..................................................................... + // These routines return the current version number. The long version + // number contains the major version in the high word, and the minor + // version in the low word. They should be interpreted in hex. + //..................................................................... + unsigned long Version_Number(void); + unsigned short Major_Version(void); + unsigned short Minor_Version(void); + + //..................................................................... + // Retrieves a pointer to the version # as a text string (#.#), with + // the trailing 0's trimmed off. + //..................................................................... + char *Version_Name(void); + + //..................................................................... + // Retrieves a pointer to the current version text. + //..................................................................... + char *Version_Text() {return (VersionText);} + + //..................................................................... + // Returns the default comm protocol for a given version number. + //..................................................................... + CommProtocolType Version_Protocol(unsigned long version); + + //..................................................................... + // These routines support "version clipping". + //..................................................................... + void Init_Clipping(void); + unsigned long Clip_Version(unsigned long minver, unsigned long maxver); + unsigned long Get_Clipped_Version(void) {return (MaxClipVer);} + + //..................................................................... + // These routines return the theoretical lowest & highest version #'s + // that this program will connect to; this does not take any previous + // version clipping into account. + //..................................................................... + unsigned long Min_Version(void); + unsigned long Max_Version(void); + + private: + //..................................................................... + // Fills in a 'VersionText' with a descriptive version name. + //..................................................................... + void Read_Text_String(void); + + //..................................................................... + // These values define the major & minor version #'s for the current + // version. Change these values to change the game's version #! + //..................................................................... + enum VersionEnum { +#ifdef FIXIT_VERSION_3 + MAJOR_VERSION = 0x0003, + MINOR_VERSION = 0x0000 +#else + MAJOR_VERSION = 0x0001, + MINOR_VERSION = 0x2000 +#endif + }; + + //..................................................................... + // These values control which other versions this program will connect + // to. Keep them current! + // If CHEAT is defined, the program will only connect to itself; these + // values aren't used. + //..................................................................... + enum VersionRangeEnum { +#ifdef FIXIT_VERSION_3 + // ajw - We can only play against same version. + MIN_VERSION = VERSION_RA_300, + MAX_VERSION = VERSION_RA_300 +#else + MIN_VERSION = VERSION_RED_ALERT_104, //0x00010000, // Version: 1.0 + MAX_VERSION = VERSION_AFTERMATH //0x00012000 // Version: 1.2 +#endif + }; + + //..................................................................... + // This is the program's version number, stored internally. + //..................................................................... + unsigned long Version; + unsigned short MajorVer; + unsigned short MinorVer; + + //..................................................................... + // This array is used for formatting the version # as a string + //..................................................................... + char VersionName[30]; + + //..................................................................... + // This array contains special version labels (such as "Beta"), stored + // in the file VERSION.TXT. If the file isn't present, no label is + // shown. + //..................................................................... + char VersionText[16]; + + //..................................................................... + // Values used for "Version Clipping" + //..................................................................... + unsigned long MinClipVer; + unsigned long MaxClipVer; + + //..................................................................... + // Bitfield Flags + // IsInitialized: is set if the VERSION.TXT file has been read + //..................................................................... + unsigned VersionInit : 1; + unsigned MajorInit : 1; + unsigned MinorInit : 1; + unsigned TextInit : 1; +}; +#endif +/************************** end of version.h *******************************/ + diff --git a/REDALERT/VESSEL.CPP b/REDALERT/VESSEL.CPP new file mode 100644 index 000000000..08163f26d --- /dev/null +++ b/REDALERT/VESSEL.CPP @@ -0,0 +1,2425 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VESSEL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * VesselClass::Draw_It -- Draws the vessel. * + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse* + * VesselClass::Init -- Initialize the vessel heap system. * + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * operator delete -- Deletes a vessel's memory block. * + * operator new -- Allocates a vessel object memory block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * * + * This is the normal constructor for vessel class objects. It will set up a vessel that * + * is valid excepting that it won't be placed on the map. * + * * + * INPUT: classid -- The type of vessel this will be. * + * * + * house -- The owner of this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::VesselClass(VesselType classid, HousesType house) : + DriveClass(RTTI_VESSEL, Vessels.ID(this), house), + Class(VesselTypes.Ptr((int)classid)), + IsToSelfRepair(false), + IsSelfRepairing(false), + DoorShutCountDown(0), + PulseCountDown(0), + SecondaryFacing(PrimaryFacing) +{ + House->Tracking_Add(this); + + /* + ** The ammo member is actually part of the techno class, but must be initialized + ** manually here because this is where we first have access to the class pointer. + */ + Ammo = Class->MaxAmmo; + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** The techno class cloakabilty flag is set according to the type + ** class cloakability flag. + */ + IsCloakable = Class->IsCloakable; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +/*********************************************************************************************** + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * * + * The destructor will destroy the vessel and ensure that it is properly removed from the * + * game engine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::~VesselClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * operator new -- Allocates a vessel object memory block. * + * * + * This routine is used to allocate a block of memory from the vessel heap. If there is * + * no more space in the heap, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the pointer to the allocated block of memory. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselClass::operator new(size_t) +{ + void * ptr = Vessels.Alloc(); + if (ptr != NULL) { + ((VesselClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * operator delete -- Deletes a vessel's memory block. * + * * + * This overloaded delete operator will return the vessel's memory back to the pool of * + * memory used for vessel allocation. * + * * + * INPUT: ptr -- Pointer to the vessel's memory block. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + assert(((VesselClass *)ptr)->IsActive); + ((VesselClass *)ptr)->IsActive = false; + } + Vessels.Free((VesselClass *)ptr); +} + + +/*********************************************************************************************** + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * * + * This routine will return with a reference to the static class data for this vessel. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the class data structure associated with this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & VesselClass::Class_Of(void) const +{ + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * * + * This routine is used by find path and other movement logic to determine if this * + * vessel can enter the cell specified. * + * * + * INPUT: cell -- The cell to check this vessel against. * + * * + * OUTPUT: Returns with the movement restriction associated with movement into this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + CellClass const * cellptr = &Map[cell]; + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** If there is blocking terrain (such as ice), then the vessel + ** can't move there. + */ + if (cellptr->Cell_Terrain() != NULL) { + return(MOVE_NO); + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (cellptr->Flag.Composite) { + + if (cellptr->Flag.Occupy.Building) { + return(MOVE_NO); + } + + TechnoClass * techno = cellptr->Cell_Techno(); + if (techno != NULL && techno->Cloak == CLOAKED && !House->Is_Ally(techno)) { + return(MOVE_CLOAK); + } + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * * + * This routine will return with the shape number to use for the ship's body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the ship's body when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Shape_Number(void) const +{ + /* + ** For eight facing units, adjust the facing number accordingly. + */ + FacingType facing = Dir_Facing(PrimaryFacing.Current()); + + int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1; + + /* + ** Special case code for transport. The north/south facing is in frame + ** 0. The east/west facing is in frame 3. + */ + if (*this == VESSEL_TRANSPORT) { + shapenum = 0; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + shapenum = 0; + } +#endif + /* + ** Door opening and closing animation stage check. + */ + if (!Is_Door_Closed()) { + shapenum = Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * VesselClass::Draw_It -- Draws the vessel. * + * * + * Draws the vessel on the tactical display. This routine is called by the map rendering * + * process to display the vessel. * + * * + * INPUT: x,y -- The pixel coordinate to draw this vessel at. * + * * + * window-- The window to base clipping and coordinates upon when drawing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** Need to know the shape name for the overlay now. ST - 8/19/2019 1:37PM + */ + const char *turret_shape_name = NULL; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); + if (!is_hidden) { + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale); + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + int shapenum = TechnoClass::BodyShape[tfacing]+32; + DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16); + + switch (Class->Type) { + case VESSEL_CA: + turret_shape_name = "TURR"; + shapefile = Class->TurretShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM + //Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); + xx = x; + yy = y; + turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16); + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_DD: + turret_shape_name = "SSAM"; + shapefile = Class->SamShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_PT: + turret_shape_name = "MGUN"; + shapefile = Class->MGunShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + default: + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM + if (turret_shape_name) { + Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); + } else { + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + } + + DriveClass::Draw_It(x, y, window); + + /* + ** Patch so the transport will draw its passengers on top of itself. + */ + if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { + TechnoClass * contact = Contact_With_Whom(); + assert(contact->IsActive); + + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * * + * This routine will display the vessel's status information. The information is dumped to * + * the monochrome monitor. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed * + * to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Debug_Dump(MonoClass * mono) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_SHIP)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL); + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * * + * This routine will fetch the overlap list for this vessel type. It takes into * + * consideration any movement the vessel may be doing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list for this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselClass::Overlap_List(bool redraw) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (IsSelected) { + rect = Union(rect, Rect(-32, 32, 64, 64)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + return(Coord_Spillage_List(Coord, 56)+1); +} + + +/*********************************************************************************************** + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * * + * This routine is called once for each vessel object during each main game loop. All * + * normal AI processing is handled here. This includes dispatching and maintaining any * + * processing that is specific to vessel objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. * + *=============================================================================================*/ +void VesselClass::AI(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + + /* + ** HACK ALERT: + ** If the ship finds itself in a hunt order but it has no weapons, then tell it + ** to sail off the map instead. + */ + if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) { + Assign_Mission(MISSION_RETREAT); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { + Commence(); + } + +#ifndef CLIPDRAW + if (Map.In_View(Coord_Cell(Center_Coord()))) { + Mark(MARK_CHANGE); + } +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Re-stock the ammo of any on-board helicopters on an aircraft carrier. + if (*this == VESSEL_CARRIER && How_Many()) { + if (!MoebiusCountDown) { + MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE; + ObjectClass *obj = Attached_Object(); + while (obj) { + long bogus; + ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus); + obj = (obj->Next); + } + } + } +#endif + /* + ** Process base class AI routine. If as a result of this, the vessel gets + ** destroyed, then detect this fact and bail early. + */ + DriveClass::AI(); + if (!IsActive) { + return; + } + + /* + ** Handle body and turret rotation. + */ + Rotation_AI(); + + /* + ** Handle any combat processing required. + */ + Combat_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) { + LST_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** Do a step of repair here, if appropriate. + */ + Repair_AI(); +} + + +/*********************************************************************************************** + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * * + * This routine is called when the vessel travels one cell. It handles any processes that * + * must occur on a per-cell basis. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Per_Cell_Process(PCPType why) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + Look(!IsPlanningToLook); + IsPlanningToLook = false; + + if (IsToSelfRepair) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face)); + SmartPtr whom; + whom = Map[cell].Cell_Building(); + if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) { + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House); + + IsSelfRepairing = true; + IsToSelfRepair = false; + break; + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + } + + if (IsActive) { + DriveClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(ObjectClass const * object) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + if (action == ACTION_SELF) { + if (Class->Max_Passengers() == 0 || !How_Many() ) { + action = ACTION_NONE; + } else { +// check to see if the transporter can unload. + bool found = 0; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this != VESSEL_CARRIER) +#endif + for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) { + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + CellClass * cell = &Map[cellnum]; + if (Map.In_Radar(cellnum)) { + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + continue; + } else { + found = true; + } + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + + if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) { + action = ACTION_ENTER; + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL && + (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) { + action = ACTION_NOMOVE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +// if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_ENTER: + action = ACTION_MOVE; + // BRR 10/18/96 IsToSelfRepair = true; + break; + + default: +// action = ACTION_NONE; + break; + } +// } +// if (action == ACTION_ENTER) { + // BRR 10/18/96 IsToSelfRepair = true; +// action = ACTION_MOVE; +// } else { +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false; +// } +// } + + DriveClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + // BRR 10/18/96 IsToSelfRepair = false; +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = false; +// } + DriveClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * * + * This routine is called to apply damage to this vessel. The amount and type of damage * + * to apply is passed as parameters. This routine could end up destroying the vessel. * + * * + * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage * + * value will be adjusted so that the actual damage applied will be * + * stored into this variable for possible subsequent examination. * + * * + * distance -- The distance from the center of the damage to the vessel itself. * + * * + * warhead -- The warhead type of damage to apply. * + * * + * source -- The perpetrator of this damage. Knowing who was responsible allows * + * retaliation logic. * + * * + * forced -- Is this damage forced upon the vessel by some supernatural means? * + * * + * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the * + * general effect of the damage. Examine this return value to see if the vessel * + * has been destroyed. * + * * + * WARNINGS: The vessel could be destroyed by the call to this routine! * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + new AnimClass(anim, Coord); + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + int shakes = Class->MaxStrength / 150; + Shake_The_Screen(shakes, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(shakes, source->Owner()); + } + } + } + + /* + ** Possibly have the crew member run away. + */ + Mark(MARK_UP); + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + object->Record_The_Kill(source); + delete object; + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking (and it's not a submarine). + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) { +#else + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim != NULL) anim->Attach_To(this); + } + } + return(res); +} + + +/*********************************************************************************************** + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * * + * This routine is used to determine if this vessel can fire its weapon at the target * + * specified. * + * * + * INPUT: target -- The target candidate to determine if firing upon is valid. * + * * + * which -- Which weapon to use when considering the candidate as a potential * + * target. * + * * + * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If * + * it can't fire, then the enum indicates why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +FireErrorType VesselClass::Can_Fire(TARGET target, int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + if(!How_Many() || Arm) { + return(FIRE_REARM); + } else { + return(FIRE_OK); + } + } +#endif + FireErrorType fire = DriveClass::Can_Fire(target, which); +if(*this==VESSEL_DD) { +Mono_Set_Cursor(0,0); +} + if (fire == FIRE_OK || fire == FIRE_CLOAKED) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** Ensure that a torpedo will never be fired upon a non naval target. + ** Unless that non-naval target is a naval building (sub pen/ship yard) + */ + bool isseatarget = Is_Target_Vessel(target); + bool isbridgetarget = false; + if (weapon->Bullet->IsSubSurface) { + isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges + isseatarget |= isbridgetarget; + } + BuildingClass * bldg = ::As_Building(target); + if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) { + isseatarget = true; + } + + dir = Direction(target); + + if (weapon->Bullet->IsSubSurface) { + if (!isseatarget && Is_Target_Object(target)) { + return(FIRE_CANT); + } + + /* + ** If it's a torpedo, let's check line-of-sight to make sure that + ** there's only water squares between us and the target. + */ + ObjectClass * obj = As_Object(target); + COORDINATE coord = Center_Coord(); + if (obj != NULL) { + int totaldist = ::Distance(coord, obj->Center_Coord()); + while (totaldist > CELL_LEPTON_W) { + coord = Coord_Move(coord, dir, CELL_LEPTON_W); + if (Map[coord].Land_Type() != LAND_WATER) { + if (!isbridgetarget) { + return(FIRE_RANGE); + } + } + + /* + ** Check for friendly boats in the way. + */ + TechnoClass * tech = Map[coord].Cell_Techno(); + if (tech != NULL && tech != this && House->Is_Ally(tech)) { + return(FIRE_RANGE); + } + totaldist -= CELL_LEPTON_W; + } + } + } + + /* + ** Depth charges are only good against submarines. + */ + if (weapon->Bullet->IsAntiSub) { + if (!isseatarget) { + return(FIRE_CANT); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) { +#endif + if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) { + return(FIRE_CANT); + } + } + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) { +#endif + return(FIRE_CANT); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (!Class->IsTurretEquipped && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff > 8) { + return(FIRE_FACING); + } + } + return(fire); +} + + +/*********************************************************************************************** + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * * + * This routine is called to determine the coordinate that a fired projectile will * + * originate from. * + * * + * INPUT: which -- Which weapon is this query directed at? * + * * + * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE VesselClass::Fire_Coord(int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + COORDINATE coord = Center_Coord(); + + if (*this == VESSEL_CA) { + if (IsSecondShot) { + coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100); + } else { + coord = Coord_Move(coord, PrimaryFacing, 0x0100); + } + coord = Coord_Move(coord, DIR_N, 0x0030); + coord = Coord_Move(coord, Turret_Facing(), 0x0040); + return(coord); + } + + if (*this == VESSEL_PT) { + coord = Coord_Move(coord, PrimaryFacing, 0x0080); + coord = Coord_Move(coord, DIR_N, 0x0020); + coord = Coord_Move(coord, Turret_Facing(), 0x0010); + return(coord); + } + + return(DriveClass::Fire_Coord(which)); +} + + +/*********************************************************************************************** + * VesselClass::Init -- Initialize the vessel heap system. * + * * + * This routine is used to clear out the vessel heap. It is called whenever a scenario is * + * being initialized prior to scenario or saved game loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All vessel objects are invalid after this routine is called. * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Init(void) +{ + Vessels.Free_All(); +} + + +/*********************************************************************************************** + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel * + * * + * This routine is used by ships to determine what target they should go after. * + * * + * INPUT: threat -- The threat type that this ship should go after (as determined by the * + * team mission or general self defense principles). * + * * + * OUTPUT: Returns with the target that this ship should attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +TARGET VesselClass::Greatest_Threat(ThreatType threat) const +{ + if (*this == VESSEL_SS) { + threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA); + threat = threat | THREAT_BOATS; + + //BG: get subs to attack buildings also. + threat = threat | THREAT_BUILDINGS; + threat = threat | THREAT_FACTORIES; + } else { + if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) { + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +// threat = threat | THREAT_GROUND | THREAT_BOATS; + } + + // Cruisers can never hit infantry anyway, so take 'em out of the list + // of possible targets. + if (*this == VESSEL_CA) { + threat = (ThreatType) (threat & (~THREAT_INFANTRY)); + } + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + return(TARGET_NONE); + } +#endif + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * * + * This routine is called when the vessel is finished with what it is doing, but the next * + * action is not known. This routine will determine what is the appropriate course of * + * action for this vessel and then start it doing that. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Enter_Idle_Mode(bool ) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (Class->PrimaryWeapon == NULL) { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) { + order = MISSION_UNLOAD; + } else { + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * VesselClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a vessel receives a radio * + * message. Typical use of this is when a unit unloads from a lst * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 BWG : Created. * + *=============================================================================================*/ +RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) { + return(RADIO_ROGER); + } +#endif + /* + ** Before saying "Sure, come on board", make sure we're adjacent to + ** the shore. + */ + CELL cell; + Desired_Load_Dir(from, cell); + if(cell) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this != VESSEL_CARRIER) { +#endif + if (How_Many() == Class->Max_Passengers()) { + LST_Close_Door(); + } else { + DoorShutCountDown = TICKS_PER_SECOND; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + param = TARGET_NONE; + Transmit_Message(RADIO_HELLO, from); + Transmit_Message(RADIO_MOVE_HERE, param); + return(RADIO_ROGER); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + /* + ** + */ + if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating) { +// if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is full or needs + ** to rotate. + */ + if (!Is_Door_Open()) { + LST_Open_Door(); + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + if (Is_Door_Open()) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) { + TechnoClass::Receive_Message(from, message, param); + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + param = As_Target(); + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + Transmit_Message(RADIO_TETHER); + } + } + return(RADIO_ROGER); + } +#endif + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + +#if(0) + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; +#endif + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; +// } else { +// ObjectClass * obj = Map[cellnum].Cell_Occupier(); +// if (obj) obj->Scatter(Coord, true); + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * VesselClass::LST_Open_Door -- Opens a LST door. * + * * + * This routine will initiate opening of the doors on the LST. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Open_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + Open_Door(5, 6); + } +} + + +/*********************************************************************************************** + * VesselClass::LST_Close_Door -- Closes a LST door. * + * * + * This routine will initiate closing of the LST door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Close_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + Close_Door(5, 6); +} + + +/*********************************************************************************************** + * VesselClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Unload(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case VESSEL_TRANSPORT: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() > 0 && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + if (!How_Many()) { // don't break out if still carrying passengers + Assign_Mission(MISSION_GUARD); + } + } + break; + + case MANEUVERING: + if (!IsRotating) { + LST_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + + /* + ** Don't do anything if still in radio contact. + */ + if (In_Radio_Contact()) return(TICKS_PER_SECOND); + + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + passenger->Commence(); + Transmit_Message(RADIO_HELLO, passenger); + Transmit_Message(RADIO_TETHER, passenger); + if (passenger->What_Am_I() == RTTI_UNIT) { + ((UnitClass *)passenger)->IsToScatter = true; + } + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + + /* + ** Tell everyone around the transport to scatter. + */ + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CellClass * cellptr = &Map[Coord].Adjacent_Cell(face); + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + cellptr->Incoming(0, true); + } + } + +// Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close LST door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + LST_Close_Door(); + } + if (Is_Door_Closed()) { + if (IsALoaner) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + break; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * * + * This routine is called when a destination is to be assigned to this vessel. * + * * + * INPUT: target -- The destination to assign to this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) { + long param = TARGET_NONE; + Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport. + Transmit_Message(RADIO_OVER_OUT); + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + } + + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * VesselClass::Pip_Count -- Fetches the number of pips to display on vessel. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the vessel. * + * This will be the number of passengers on a transport. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Pip_Count(void) const +{ + if (Techno_Type_Class()->Max_Passengers() > 0) { + int passengers = 0; + ObjectClass const * object = Attached_Object(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + if (object != NULL) { + passengers++; + object = object->Next; + } + } + return passengers; + } + + return 0; +} + + +/*********************************************************************************************** + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * * + * This will cause the vessel to run away from the battlefield. It searches for an escape * + * map edge according to the reinforcement edge specified in the house. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine is called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Retreat(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + PICK_RETREAT_POINT, + TRAVEL + }; + switch (Status) { + case PICK_RETREAT_POINT: + IsALoaner = true; + if (!Target_Legal(NavCom)) { +// CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed); + CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed); + if (Team.Is_Valid()) { + Team->Remove(this); + } + Assign_Destination(::As_Target(cell)); + } + Status = TRAVEL; + return(1); + + case TRAVEL: + if (!Target_Legal(NavCom)) { + Status = PICK_RETREAT_POINT; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * * + * Asking this question is part of the recloak process. If the answer is no, then * + * recloaking is postponed. This facilitates keeping submarines visible for longer than * + * they otherwise would be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this vessel recloak now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 BWG : Created. * + *=============================================================================================*/ +bool VesselClass::Is_Allowed_To_Recloak(void) const +{ + return(PulseCountDown == 0); +} + + +/*********************************************************************************************** + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * * + * This will read and create all vessels specified in the INI database. This routine is * + * called when the scenario starts. * + * * + * INPUT: ini -- Reference to the INI database to read the vessel data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: Vessels will be created and placed on the map by this function. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Read_INI(CCINIClass & ini) +{ + VesselClass * vessel; // Working vessel pointer. + HousesType inhouse; // Vessel house. + VesselType classid; // Vessel class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = VesselTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != VESSEL_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + vessel = new VesselClass(classid, inhouse); + if (vessel != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + vessel->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + vessel->Trigger = tt; + } + } + + if (vessel->Unlimbo(coord, dir)) { + vessel->Strength = (int)vessel->Class->MaxStrength * fixed(strength, 256); + if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength; + // vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength); + if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) { + vessel->Assign_Mission(mission); + vessel->Commence(); + } else { + vessel->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the vessel could not be unlimboed, then this is a catastrophic error + ** condition. Delete the vessel. + */ + delete vessel; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * * + * This routine is used to add the vessel data (needed for scenario start) to the INI * + * database specified. If there was any preexisting vessel data in the database, it will * + * be cleared * + * * + * INPUT: ini -- Reference to the ini database to store the vessel data into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing vessel data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the vessel data out. + */ + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + vessel->House->Class->IniName, + vessel->Class->IniName, + vessel->Health_Ratio()*256, + Coord_Cell(vessel->Coord), + vessel->PrimaryFacing.Current(), + MissionClass::Mission_Name(vessel->Mission), + vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * * + * This routine is called when the vessel starts moving. It will reserve the destination * + * cell so that it won't be occupied by another vessel as this one is travelling. * + * * + * INPUT: headto -- The coordinate that will be headed to. * + * * + * OUTPUT: bool; Was the destination location successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Start_Driver(COORDINATE & headto) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(CELL cell) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) { + return(ACTION_MOVE); + } + + if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) { + return(ACTION_ATTACK); + } + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * * + * Any turret or body rotation for this vessel will be handled by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } + } + + IsRotating = false; + if (Class->IsTurretEquipped) { + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + IsRotating = SecondaryFacing.Is_Rotating(); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * * + * This routine will process firing logic for the vessel. It includes searching for targets * + * and performing any adjustments necessary to bring the target to bear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Combat_AI(void) +{ + if (Target_Legal(TarCom) && Is_Weapon_Equipped()) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + + switch (ok) { + case FIRE_OK: + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * * + * In addition to detecting the edge of world case, this routine will delete the vessel * + * if it occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vessel deleted by this routine? * + * * + * WARNINGS: Be sure to examine the return value and if true, abort any further processing * + * for this vessel since it has been deleted. This routine should be called once * + * per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Edge_Of_World_AI(void) +{ + if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * * + * When a vessel repairs, it does so 'by itself' and not under direct control of another * + * object. This self repair logic is processed here. Upon repair completion of money * + * exhuastion, the repair process will terminate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Repair_AI(void) +{ + if (IsSelfRepairing) { + if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) { + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsSelfRepairing = IsToSelfRepair = false; + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House); + } + } + } + } +} + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +/*********************************************************************************************** + * VesselClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the ship. It will check * + * to see if we're an aircraft carrier, and if so, launch one of our * + * aircraft. If we're not an aircraft carrier, it lets the higher-level * + * Fire_At logic take over. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * VesselClass::Fire_At(TARGET target, int which) +{ + //PG assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == VESSEL_CARRIER) { + Arm = CarrierLaunchDelay; + FootClass * passenger = Detach_Object(); + if (passenger != NULL) { + ScenarioInit++; + passenger->Unlimbo(Center_Coord()); + ScenarioInit--; + passenger->Assign_Mission(MISSION_ATTACK); + passenger->Assign_Target(TarCom); + passenger->Commence(); +// If we've launched our last aircraft, discontinue attacking. + if (!How_Many()) Assign_Target(TARGET_NONE); + } + } else { + return DriveClass::Fire_At(target, which); + } + return (NULL); +} + +#endif \ No newline at end of file diff --git a/REDALERT/VESSEL.H b/REDALERT/VESSEL.H new file mode 100644 index 000000000..dc383d222 --- /dev/null +++ b/REDALERT/VESSEL.H @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VESSEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VESSEL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : March 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VESSEL_H +#define VESSEL_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + +class VesselClass : public DriveClass +{ + public: + /* + ** This points to the controling static characteristic data associated with + ** this vessel. + */ + CCPtr Class; + + /* + ** Has this sea vessel been told to move to a shipyard? If so, then + ** when we get there, start the repair process. + */ + unsigned IsToSelfRepair:1; + + /* + ** Is this sea vessel parked next to a shipyard/subpen, and therefore + ** in the special self-repair mode? + */ + unsigned IsSelfRepairing:1; + + /* + ** If this is an LST, is it time to shut the door? + */ + CDTimerClass DoorShutCountDown; + + /* + ** If this is a sub, has the sonar pulse worn off, such that we can + ** re-submerge? + */ + CDTimerClass PulseCountDown; + + VesselClass(VesselType classid, HousesType house); + VesselClass(NoInitClass const & x) : DriveClass(x), Class(x), SecondaryFacing(x) {}; + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + operator VesselType(void) const {return Class->Type;}; + + static void Init(void); + + virtual ~VesselClass(void); + virtual ObjectTypeClass const & Class_Of(void) const; + + virtual MZoneType Zone_Check_Type(void) const {return(MZONE_WATER);} + int Shape_Number(void) const; + void Rotation_AI(void); + void Combat_AI(void); + bool Edge_Of_World_AI(void); + void Repair_AI(void); + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + virtual bool Start_Driver(COORDINATE & headto); + virtual int Mission_Retreat(void); + virtual int Mission_Unload(void); + void LST_Open_Door(void); + void LST_Close_Door(void); + virtual COORDINATE Fire_Coord(int which) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Assign_Destination(TARGET target); + virtual int Pip_Count(void) const; + + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + virtual void Enter_Idle_Mode(bool initial=false); + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual bool Is_Allowed_To_Recloak(void) const; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + virtual BulletClass * Fire_At(TARGET target, int which=0); +#endif + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "SHIPS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + protected: + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + +}; + +#endif \ No newline at end of file diff --git a/REDALERT/VISUDLG.CPP b/REDALERT/VISUDLG.CPP new file mode 100644 index 000000000..43cb954b6 --- /dev/null +++ b/REDALERT/VISUDLG.CPP @@ -0,0 +1,413 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VISUDLG.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : June 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VisualControlsClass::Process -- Process the visual control dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "visudlg.h" + + +/*********************************************************************************************** + * VisualControlsClass::Process -- Process the visual control dialog box. * + * * + * This routine displays and processes the visual controls dialog box. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +void VisualControlsClass::Process(void) +{ + static int _titles[4] = { + TXT_BRIGHTNESS, + TXT_COLOR, + TXT_CONTRAST, + TXT_TINT + }; + + enum { + NUM_OF_BUTTONS = 6, + }; + + /* + ** Make em resolution independent + */ + int option_width = OPTION_WIDTH * RESFACTOR; // Width of dialog box. + int option_height = OPTION_HEIGHT * RESFACTOR; // Height of dialog box. + int option_x = OPTION_X * RESFACTOR; + int option_y = OPTION_Y * RESFACTOR; + int text_x = TEXT_X * RESFACTOR; + int text_y = TEXT_Y * RESFACTOR; + int slider_x = SLIDER_X * RESFACTOR; + int slider_y = SLIDER_Y * RESFACTOR; + int slider_width = SLIDER_WIDTH * RESFACTOR; // Width of each control slider. + int slider_height = SLIDER_HEIGHT * RESFACTOR; // Height of each control slider. + int slider_y_spacing = SLIDER_Y_SPACING * RESFACTOR; // Vertical spacing between sliders. + int button_x = BUTTON_X * RESFACTOR; // Options button x pos + int button_y = BUTTON_Y * RESFACTOR; // Options button y pos + + + /* + ** Variables. + */ + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + SliderClass * buttonsliders[NUM_OF_BUTTONS]; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, button_y, 60*RESFACTOR); +// TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, 0, button_y); + TextButtonClass resetbtn(BUTTON_RESET, TXT_RESET_MENU, TPF_BUTTON, 0, button_y, 80*RESFACTOR); + + /* + ** Centers options button. + */ + optionsbtn.X = option_x + (option_width - optionsbtn.Width - (17 * RESFACTOR)); + resetbtn.X = option_x + (17 * RESFACTOR); + + resetbtn.Add_Tail(optionsbtn); + + /* + ** Brightness (value) control. + */ + SliderClass brightness(BUTTON_BRIGHTNESS, slider_x, slider_y + (slider_y_spacing*0), slider_width, slider_height, true); + brightness.Set_Thumb_Size(20 * RESFACTOR); + brightness.Set_Value(Options.Get_Brightness() * 256); + brightness.Add_Tail(optionsbtn); + + /* + ** Color (saturation) control. + */ + SliderClass color(BUTTON_COLOR, slider_x, slider_y + (slider_y_spacing*1), slider_width, slider_height, true); + color.Set_Thumb_Size(20 * RESFACTOR); + color.Set_Value(Options.Get_Saturation() * 256); + color.Add_Tail(optionsbtn); + + /* + ** Contrast control. + */ + SliderClass contrast(BUTTON_CONTRAST, slider_x, slider_y + (slider_y_spacing*2), slider_width, slider_height, true); + contrast.Set_Thumb_Size(20 * RESFACTOR); + contrast.Set_Value(Options.Get_Contrast() * 256); + contrast.Add_Tail(optionsbtn); + + /* + ** Tint (hue) control. + */ + SliderClass tint(BUTTON_TINT, slider_x, slider_y + (slider_y_spacing*3), slider_width, slider_height, true); + tint.Set_Thumb_Size(20 * RESFACTOR); + tint.Set_Value(Options.Get_Tint() * 256); + tint.Add_Tail(optionsbtn); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(optionsbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), + GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(optionsbtn); + + curbutton = 0; + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = NULL; + buttons[3] = NULL; + buttons[4] = &resetbtn; + buttons[5] = &optionsbtn; + + buttonsliders[0] = &brightness; + buttonsliders[1] = &color; + buttonsliders[2] = &contrast; + buttonsliders[3] = ∭ + buttonsliders[4] = NULL; + buttonsliders[5] = NULL; + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + bool partial = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_VISUAL_CONTROLS, option_x, option_y, option_width); + Show_Mouse(); + display = false; + partial = true; + } + + /* + ** If just the buttons and captions need to be redrawn, then do so now. + */ + if (partial) { + Hide_Mouse(); + + /* + ** Draw the titles. + */ + for (int i = 0; i < (sizeof(_titles)/sizeof(_titles[0])); i++) { + Fancy_Text_Print(_titles[i], slider_x - (8 * RESFACTOR), text_y + (i*slider_y_spacing), + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_RIGHT| ((curbutton == i) ? TPF_BRIGHT_COLOR : TPF_TEXT)); + } + optionsbtn.Draw_All(); + Show_Mouse(); + partial = false; + } + + /* + ** Get and process player input. + */ + KeyNumType input = optionsbtn.Input(); + switch (input) { + case (BUTTON_BRIGHTNESS | KN_BUTTON): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR | KN_BUTTON): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST | KN_BUTTON): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT | KN_BUTTON): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + + case (BUTTON_RESET | KN_BUTTON): + selection = BUTTON_RESET; + pressed = true; + break; + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + selection = BUTTON_OPTIONS; + pressed = true; + break; + + case (KN_LEFT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(1); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_OPTIONS - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(0); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (BUTTON_OPTIONS - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_UP): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton == (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton--; + } + + if (curbutton < 0) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_DOWN): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = 0; + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_BRIGHTNESS; + pressed = true; + break; + + default: + break; + } + + + if (pressed) { + switch (selection) { + case (BUTTON_RESET): + brightness.Set_Value(128); + contrast.Set_Value(128); + color.Set_Value(128); + tint.Set_Value(128); + + Options.Set_Brightness(fixed::_1_2); + Options.Set_Contrast(fixed::_1_2); + Options.Set_Saturation(fixed::_1_2); + Options.Set_Tint(fixed::_1_2); + break; + + case (BUTTON_OPTIONS): + process = false; + break; + } + + pressed = false; + } + } +} + diff --git a/REDALERT/VISUDLG.H b/REDALERT/VISUDLG.H new file mode 100644 index 000000000..328bc7de7 --- /dev/null +++ b/REDALERT/VISUDLG.H @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VISUDLG.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + *---------------------------------------------------------------------------------------------*/ + +#ifndef VISUDLG_H +#define VISUDLG_H + +#include "gadget.h" + +class VisualControlsClass +{ + private: + + enum VisualControlEnums { + BUTTON_BRIGHTNESS=1, + BUTTON_COLOR, + BUTTON_CONTRAST, + BUTTON_TINT, + BUTTON_RESET, + BUTTON_OPTIONS, // Button number for "Options menu" + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2)), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+28, // Title's x pos + TEXT_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_X=OPTION_X+105, // Slider's x pos + SLIDER_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_WIDTH=70, // Width of each control slider. + SLIDER_HEIGHT=5, // Height of each control slider. + SLIDER_Y_SPACING=11, // Vertical spacing between sliders. + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + }; + + public: + + VisualControlsClass(void) {}; + void Process(void); +}; + +#endif diff --git a/REDALERT/VORTEX.CPP b/REDALERT/VORTEX.CPP new file mode 100644 index 000000000..bdf455a59 --- /dev/null +++ b/REDALERT/VORTEX.CPP @@ -0,0 +1,1257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : VORTEX.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 12th, 1996 * + * * + * Last Update : September 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Circley vortexy swirly type thing. (Really just a pixel & color remap). * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * CVC::ChronalVortexClass -- vortex class constructor * + * CVC::~ChronalVortexClass -- vortex class destructor * + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * CVC::Disappear -- Makes the chronal vortex go away. * + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * CVC::Show -- Makes a hidden vortex visible again. * + * CVC::Stop -- Stops the vortex without going through the hide animation * + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * CVC::Save -- Saves the vortex class data to a savegame file * + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * CVC::Movement -- Movement AI for the vortex. * + * CVC::Set_Target -- Make the vortex zap a particular object. * + * CVC::Attack -- look for objects to attack * + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * CVC::Coordinate_Remap -- Draws the vortex * + * CVC::Render -- Renders the vortex at its current position. * + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * CVC::Build_Fading_Table -- Builds a fading color lookup table. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + + +/* +** Instance of chronal vortex class. This must be the only instance. +*/ +ChronalVortexClass ChronalVortex; + + + +/*********************************************************************************************** + * CVC::ChronalVortexClass -- vortex class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::ChronalVortexClass (void) +{ + Active = 0; + Theater = THEATER_NONE; + Speed = 10; + Range = 10; + Damage = 200; + RenderBuffer = NULL; //We havn't allocated it yet. It will be allocated as needed. +} + + + +/*********************************************************************************************** + * CVC::~ChronalVortexClass -- vortex class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::~ChronalVortexClass (void) +{ + if (RenderBuffer) delete RenderBuffer; + Active = 0; +} + + + +/*********************************************************************************************** + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * * + * * + * * + * INPUT: Coordinate that vortex should appear at. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This member does nothing if the vortex is already active * + * * + * HISTORY: * + * 8/29/96 4:27PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Appear (COORDINATE coordinate) +{ + if (Active) return; + + /* + ** Adjust the given coordinate so the vortex appears in a central position + */ + int x = Lepton_To_Pixel(Coord_X(coordinate)); + int y = Lepton_To_Pixel(Coord_Y(coordinate)); + + x -= 32; + y -= 32; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + Position = XY_Coord (lx, ly); + + /* + ** Initialise the vortex variables. + */ + AnimateDir = 1; + AnimateFrame = 0; + State = STATE_GROW; + Active = true; + Animate = 0; + StartShutdown = false; + LastAttackFrame= Frame; + TargetObject = TARGET_NONE; + ZapFrame = 0; + Hidden = false; + StartHiding = false; + XDir = 0; + YDir = 0; + + /* + ** Vortex starts off in a random direction. + */ + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + +} + + +/*********************************************************************************************** + * CVC::Disappear -- Makes the chronal vortex go away. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Disappear (void) +{ + if (Hidden) { + Active = false; + } else { + StartShutdown = true; + } +} + + + +/*********************************************************************************************** + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This doesnt deactivate the vortex. Use Disappear to get rid of it permanently. * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Hide (void) +{ + if (!StartShutdown) { + StartHiding = true; + } +} + + +/*********************************************************************************************** + * CVC::Show -- Makes a hidden vortex visible again. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:31PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Show (void) +{ + /* + ** Dont do anything if vortx is dying. + */ + if (!StartShutdown) { + + /* + ** If the vortex is hidden then show it again. + */ + if (Hidden) { + Hidden = false; + StartHiding = false; + AnimateFrame = 0; + State = STATE_GROW; + XDir = 0; + YDir = 0; + } else { + /* + ** If the vortex is in the process of hiding then reverse it. + */ + StartHiding = false; + if (State == STATE_SHRINK) { + State = STATE_GROW; + AnimateFrame = VORTEX_FRAMES - AnimateFrame; + } + } + } +} + + + +/*********************************************************************************************** + * CVC::Stop -- Stops the vortex without going through the hide animation * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Stop(void) +{ + if (Active) Active = false; +} + + + + +/*********************************************************************************************** + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * * + * * + * * + * INPUT: ptr to file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Load(Straw &file) +{ + /* + ** Delete the render buffer as we are going to lose the pointer anyway. + ** It will be re-allocated when needed. + */ + if (RenderBuffer) delete RenderBuffer; + + file.Get (this, sizeof (ChronalVortexClass)); +} + + + +/*********************************************************************************************** + * CVC::Save -- Saves the vortex class data to a savegame file * + * * + * * + * * + * INPUT: file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:33PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Save(Pipe &file) +{ + GraphicBufferClass *save_ptr = NULL; + + if (RenderBuffer){ + /* + ** Save the ptr to the render buffer so we can null it for the save + */ + save_ptr = RenderBuffer; + RenderBuffer = NULL; + } + + file.Put (this, sizeof (ChronalVortexClass)); + + /* + ** Restore the render buffer ptr + */ + if (save_ptr){ + RenderBuffer = save_ptr; + } +} + + + +/*********************************************************************************************** + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:34PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::AI(void) +{ + + int chance; + + /* + ** No AI if vortex isnt active + */ + if (Active) { + + /* + ** Do the movement AI + */ + Movement(); + + /* + ** Do the attack AI + */ + Zap_Target(); + + + if (Hidden && (Frame - HiddenFrame > 50) ) { + /* + ** Vortex is hidden. Chance of it showing itself increases the longer its stays hidden. + */ + chance = Random_Pick(0,2000); + if (chance <= Frame - HiddenFrame) { + Show(); + } + } else { + + if (Animate == 0) { + + /* + ** Its time to animate the vortex. + */ + AnimateFrame += AnimateDir; + + if (AnimateFrame > VORTEX_FRAMES) { + /* + ** State changes can only occur on final animation frames. + */ + AnimateFrame = 1; + + if (StartShutdown) { + + /* + ** Vortex is in the process of dying. + */ + if (State == STATE_SHRINK) { + Set_Redraw(); + Active = false; + AnimateFrame = 0; + } else { + Attack(); + State = STATE_SHRINK; + } + } else { + + if (StartHiding) { + /* + ** Vortex wants to hide. + */ + if (State == STATE_SHRINK) { + /* + ** Hide the vortex now. + */ + Set_Redraw(); + StartHiding = false; + Hidden = true; + HiddenFrame = Frame; + if (Random_Pick(0,4) == 4) { + Disappear(); + } + } else { + /* + ** Start hiding the vortex. + */ + Attack(); + State = STATE_SHRINK; + } + } else { + + Attack(); + if (State == STATE_GROW) { + State = STATE_ROTATE; + } else { + //Attack(); + } + } + } + } else { + if (AnimateFrame == VORTEX_FRAMES / 2) Attack(); + } + } + Animate++; + Animate &= 1; + } + } +} + + + + + +/*********************************************************************************************** + * CVC::Movement -- Movement AI for the vortex. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:39PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Movement (void) +{ + bool newpick = true; + + /* + ** Update the vortex position by applying the x and y direction variables + */ + LEPTON x = Coord_X(Position); + LEPTON y = Coord_Y(Position); + + x += XDir; + y += YDir; + + Position = XY_Coord (x,y); + + /* + ** Reverse the direction of the vortex if its drifting off the map. + */ + if (x > CELL_LEPTON_W *(Map.MapCellX + Map.MapCellWidth -4)) { + newpick = false; + if (DesiredXDir >0 ) DesiredXDir = -DesiredXDir; + } + + if (y > CELL_LEPTON_H *(Map.MapCellY + Map.MapCellHeight -4)) { + newpick = false; + if (DesiredYDir >0 ) DesiredYDir = -DesiredYDir; + } + + if (x < CELL_LEPTON_W *Map.MapCellX + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredXDir <0 ) DesiredXDir = -DesiredXDir; + } + + if (y < CELL_LEPTON_H *Map.MapCellY + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredYDir <0 ) DesiredYDir = -DesiredYDir; + } + + /* + ** Vortex direction tends towards the desired direction unless the vortex is shutting down or + ** appearing in which case the direction tends towards 0. + */ + if (State == STATE_ROTATE || Hidden) { + if (XDir < DesiredXDir) XDir ++; + if (XDir > DesiredXDir) XDir --; + if (YDir < DesiredYDir) YDir ++; + if (YDir > DesiredYDir) YDir --; + } else { + if (XDir > 0) XDir -= Speed/8; + if (XDir < 0) XDir += Speed/8; + if (YDir > 0) YDir -= Speed/8; + if (YDir < 0) YDir += Speed/8; + } + + /* + ** Occasionally change the direction of the vortex. + */ + if (newpick && Random_Pick (0, 100) == 100) { + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + } +} + + + +/*********************************************************************************************** + * CVC::Set_Target -- Make the vortex zap a particular object. * + * * + * * + * * + * INPUT: ptr to object to zap * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Target (ObjectClass *target) +{ + if (Active){ + ZapFrame = 0; + TargetObject = TARGET_NONE; + if (target != NULL) TargetObject = target->As_Target(); + LastAttackFrame = Frame; + TargetDistance = (target != NULL) ? Distance (target->Center_Coord(), Position) : 0; + } +} + + +/*********************************************************************************************** + * CVC::Attack -- look for objects to attack * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Attack(void) +{ + int distance; +// if(TargetObject) return; +// if(!TargetObject) return; + /* + ** Calculate the position of the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Scan through the ground layer objects and see who we should attack + */ + + /* + ** First scan - find any object directly above the vortex. + */ + for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj->Is_Techno() && obj->Strength > 0 ) { + + distance = Distance (obj->Center_Coord(), here); + + if (distance <= CELL_LEPTON_W*2) { + Set_Target (obj); + break; + } + } + } + + /* + ** If we found something to attack then just return + */ + if (!Target_Legal(TargetObject)) return; + + + /* + ** Scan through all ground level objects. + ** + ** Objects within range have a chance of being selected based on their distance from the vortex. + */ + + int chance = Random_Pick (0, 1000); + if (chance > Frame - LastAttackFrame) return; + + for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj && obj->Is_Techno() ) { + + distance = Distance (obj->Center_Coord(), Position); + + if (distance < CELL_LEPTON_W * Range) { + chance = Random_Pick (0, distance); + if (chance < CELL_LEPTON_W) { + Set_Target (obj); + break; + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:45PM ST : Created * + *=============================================================================================*/ +#define ZAP_COUNT 1 +void ChronalVortexClass::Zap_Target (void) +{ + if (!Hidden && Target_Legal(TargetObject) && ZapFrame < ZAP_COUNT) { + + /* + ** Get the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Create a temporary techno object se we can access the lightning ability of the tesla. + */ + TechnoClass *temptech = new BuildingClass (STRUCT_TESLA, HOUSE_GOOD); + if (temptech != NULL) { + temptech->Coord = here; + ObjectClass * obj = As_Object(TargetObject); + TARGET target = As_Target (obj->Center_Coord()); + Sound_Effect(VOC_TESLA_ZAP, obj->Center_Coord()); + temptech->Electric_Zap (target, 0, WINDOW_TACTICAL, here, LightningRemap); + delete temptech; + + /* + ** Flag the whole map to redraw to cover the lightning. + */ + Map.Flag_To_Redraw(true); + + /* + ** Zap the target 3 times but only do damage on the last frame. + */ + ZapFrame++; + + if (ZapFrame == ZAP_COUNT) { + ZapFrame = 0; + int damage = Damage; + obj->Take_Damage(damage, TargetDistance, WARHEAD_TESLA, NULL, 1); + TargetObject = TARGET_NONE; + } + } + + /* + ** Vortex might pretend to go away after zapping the target. + */ + if (Random_Pick (0,2) == 2) Hide(); + } +} + + + + + +/*********************************************************************************************** + * CVC::Coordinate_Remap -- Draws the vortex * + * * + * * + * * + * INPUT: ptr to view port to draw the vortex into * + * x offset * + * y offset * + * width of vortex * + * height of vortex * + * ptr to shading remap tables * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:48PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table) +{ + unsigned char getx,gety, remap_color, pixel_color; + + + BufferClass destbuf (width * height); + + unsigned char *destptr = (unsigned char*) destbuf.Get_Buffer(); + + int destx = x; + int desty = y; + + int dest_width = width; + int dest_height = height; + + if (inbuffer->Lock()) { + + /* + ** Get a pointer to the section of buffer we are going to work on. + */ + unsigned char *bufptr = (unsigned char *) inbuffer->Get_Offset() + + destx +#ifdef WIN32 + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd() + inbuffer->Get_Pitch()); +#else + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd()); +#endif + + +#ifdef WIN32 + int modulo = inbuffer->Get_Pitch() + inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#else + int modulo = inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#endif + + + for (int yy = desty ; yy < desty+dest_height ; yy++) { + for (int xx = destx ; xx < destx+dest_width ; xx++) { + /* + ** Get the coordinates of the pixel to draw + */ + getx = *(remap_table++); + gety = *(remap_table++); + remap_color = *(remap_table++); + + pixel_color = * (bufptr + getx + (gety * modulo) ); + + *(destptr++) = VortexRemapTables [remap_color] [pixel_color]; + } + + remap_table += 3*(width - dest_width); + destptr += width - dest_width; + + } + + destbuf.To_Page(destx, desty, dest_width, dest_height, *inbuffer); + + + inbuffer->Unlock(); + } +} + + + +/*********************************************************************************************** + * CVC::Render -- Renders the vortex at its current position. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:49PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Render (void) +{ + if (Active && !Hidden) { + char fname [80]; + + int frame; + + /* + ** Calculate which coordinate lookup table we should be using for this frame. + */ + switch (State) { + case STATE_GROW: + frame = 0; + break; + + case STATE_ROTATE: + frame = VORTEX_FRAMES; + break; + + case STATE_SHRINK: + frame = VORTEX_FRAMES*2; + break; + } + + frame += AnimateFrame; + + sprintf (fname, "HOLE%04d.lut", frame); + + void const *lut_ptr = MFCD::Retrieve(fname); + if (lut_ptr) { + + /* + ** Build a representation of the area of the screen where the vortex will be + ** in an off-screen buffer. + ** This is necessary for clipping as we cant remap pixels from off screen if we build + ** the image from the hidpage. + */ + if (!RenderBuffer) { + RenderBuffer = new GraphicBufferClass(CELL_PIXEL_W * 4, CELL_PIXEL_H * 4, (void*)NULL); + } + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + CellClass *cellptr; + CELL cell; + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + + +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(RenderBuffer); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(RenderBuffer); +#endif + + /* + ** Temporarily modify the tactical window so it works with our offscreen buffer + */ + int wx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int wy = WindowList[WINDOW_TACTICAL][WINDOWY]; + int ww = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int wh = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + WindowList[WINDOW_TACTICAL][WINDOWX] = 0; + WindowList[WINDOW_TACTICAL][WINDOWY] = 0; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = RenderBuffer->Get_Width(); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = RenderBuffer->Get_Height(); + + + /* + ** Loop through all the cells that the vortex overlaps and render the template, smudge + ** and overlay for each cell. + */ + for (int y = 0 ; y<4 ; y++) { + for (int x = 0 ; x<4 ; x++) { + + cell = XY_Cell (xc+x,yc+y); + if (cell != -1) { + + //cellptr = &Map[ Coord_Whole (Cell_Coord(cell)) ]; + cellptr = &Map [cell]; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != TEMPLATE_CLEAR1 && cellptr->TType != 255) { + ttype = &TemplateTypeClass::As_Reference(cellptr->TType); + icon = cellptr->TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = cellptr->Clear_Icon(); + } + + /* + ** Draw the template + */ + if (ttype->Get_Image_Data()) { + RenderBuffer->Draw_Stamp(ttype->Get_Image_Data(), icon, x*CELL_PIXEL_W, y*CELL_PIXEL_H, NULL, WINDOW_MAIN); + } + + /* + ** Draw any smudge. + */ + if (cellptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(cellptr->Smudge).Draw_It(x*CELL_PIXEL_W, y*CELL_PIXEL_H, cellptr->SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + CC_Draw_Shape(otype.Get_Image_Data(), + cellptr->OverlayData, + x*CELL_PIXEL_W + (CELL_PIXEL_W >> 1), + y*CELL_PIXEL_H + (CELL_PIXEL_H >> 1), + WINDOW_TACTICAL, + SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, + DisplayClass::UnitShadow); + + IsTheaterShape = false; + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (cellptr->IsFlagged) { + void const * flag_remap = HouseClass::As_Pointer(cellptr->Owner)->Remap_Table(false, REMAP_NORMAL); + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); + } + } + } + } + + + Set_Logic_Page(oldpage); + + /* + ** Restore the tactical window to its correct value + */ + WindowList[WINDOW_TACTICAL][WINDOWX] = wx; + WindowList[WINDOW_TACTICAL][WINDOWY] = wy; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = ww; + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = wh; + + /* + ** Render the vortex over the cells we just rendered to our buffer + */ + Coordinate_Remap (RenderBuffer, Lepton_To_Pixel(Coord_X(Coord_Fraction(Position))), + Lepton_To_Pixel(Coord_Y(Coord_Fraction(Position))), + 64, + 64, + (unsigned char*) lut_ptr); + + + /* + ** Calculate the pixel position of our fresh block of cells on the tactical map so + ** we can blit it to the hid page. + */ + COORDINATE render_pos = XY_Coord(xc * CELL_LEPTON_W, yc * CELL_LEPTON_H); //Coord_Whole(Position); + + int x, y; + Map.Coord_To_Pixel(render_pos, x, y); + + /* + ** Create a view port to blit to + */ + GraphicViewPortClass target (LogicPage->Get_Graphic_Buffer(), + 0, + LogicPage->Get_YPos(), + Lepton_To_Pixel (Map.TacLeptonWidth), + Lepton_To_Pixel (Map.TacLeptonHeight)); + + + /* + ** Do some clipping since the library clipping gets it wrong. + */ + int diff; + + int source_x = 0; + int source_y = 0; + int source_width = CELL_PIXEL_W*4; + int source_height = CELL_PIXEL_H*4; + + int dest_x = x; + int dest_y = y; + int dest_width = source_width; + int dest_height = source_height; + + if (dest_x < 0) { + source_width += dest_x; + dest_width += dest_x; + source_x -= dest_x; + dest_x = 0; + } + + if (dest_y <0) { + source_height += dest_y; + dest_height += dest_y; + source_y -= dest_y; + dest_y = 0; + } + + if (dest_x + dest_width > target.Get_Width() ) { + diff = dest_x + dest_width - target.Get_Width(); + dest_width -= diff; + source_width -= diff; + } + + if (dest_y + dest_height > target.Get_Height() ) { + diff = dest_y + dest_height - target.Get_Height(); + dest_height -= diff; + source_height -= diff; + } + + + /* + ** Blit our freshly draw cells and vortex into their correct position on the hidpage + */ + if (dest_width > 0 && dest_height > 0) { + RenderBuffer->Blit (target, source_x, source_y, dest_x, dest_y, dest_width, dest_height, false); + } + + } + } +} + + + +/*********************************************************************************************** + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:50PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Redraw(void) +{ + + if (Active) { + + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + + CELL cell; + + for (int y = MAX(0,yc - 1) ; y< yc+4 ; y++) { + for (int x = MAX(0, xc-1) ; x< xc + 4 ; x++) { + cell = XY_Cell (x,y); + if (cell != -1) { + Map[cell].Redraw_Objects(); + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * * + * * + * * + * INPUT: Theater * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:51PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Setup_Remap_Tables (TheaterType theater) +{ + /* + ** The names of the remap files for each theater + */ + static char _remaps[3][13] ={ + "TEMP_VTX.PAL", + "SNOW_VTX.PAL", + "INTR_VTX.PAL" + }; + + int i; + + /* + ** If the theater has changed then load the remap tables from disk if they exist or create + ** them if they dont. + */ + if (theater != Theater) { + + Theater = theater; + + CCFileClass file (_remaps[(int)Theater]); + + if (file.Is_Available()) { + file.Read (VortexRemapTables, MAX_REMAP_SHADES*256); + } else { + + for (i=0 ; i= CYCLE_COLOR_START && index < (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) || + index == CC_PULSE_COLOR || + index == CC_EMBER_COLOR) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. + */ + int best = -1; + int bvalue = 0; + for (int id = 0; id < PaletteClass::COLOR_COUNT; id++) { + + if (id != 0 && + (id < CYCLE_COLOR_START || id >= (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) && + id != CC_PULSE_COLOR && + id != CC_EMBER_COLOR) { + + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + } + *ptr++ = best; + } + } + } +} + + +void ChronalVortexClass::Detach(TARGET target) +{ + if (Target_Legal(target) && target == TargetObject) { + Set_Target(NULL); + } +} diff --git a/REDALERT/VORTEX.H b/REDALERT/VORTEX.H new file mode 100644 index 000000000..173f3c6ce --- /dev/null +++ b/REDALERT/VORTEX.H @@ -0,0 +1,270 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : VORTEX.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 8/12/96 * + * * + * Last Update : August 29th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Definition of ChronalVortexClass. The Chronal vortex sometimes appears when the * + * chronosphere is used. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VORTEX_H +#define VORTEX_H + + +#define MAX_REMAP_SHADES 16 //Number of lookup tables required for vortex shading. +#define VORTEX_FRAMES 16 //Number of frames in one complete rotation of the vortex. + + + +class ChronalVortexClass { + + public: + + /* + ** Constructor and destructor. + */ + ChronalVortexClass(void); + ~ChronalVortexClass(void); + + void Detach(TARGET target); + + /* + ** Makes the vortex appear at the specified coordinate. + */ + void Appear (COORDINATE coordinate); + + /* + ** Makes the vortex go away. + */ + void Disappear (void); + + /* + ** Call this every frame. + */ + void AI (void); + + /* + ** Render the vortex + */ + void Render (void); + + /* + ** Flags cells under the vortex to be redrawn + */ + void Set_Redraw (void); + + /* + ** Call whenever the theater changes to recalculate the shading lookup tables + */ + void Setup_Remap_Tables (TheaterType theater); + + /* + ** Functions to load and save the vortex. + */ + void Load(Straw &file); + void Save(Pipe &file); + + /* + ** Returns true of vortex is currently active. + */ + bool Is_Active(void) {return (Active);}; + + /* + ** Makes the vortex attack the specified target. Target must be in range of the vortex. + */ + void Set_Target (ObjectClass *target); + + /* + ** Disables the vortex. + */ + void Stop(void); + + /* + ** Members to allow read access to private data + */ + COORDINATE Get_Position (void) {return (Position);}; + int Get_Range (void) {return (Range);}; + int Get_Speed (void) {return (Speed);}; + int Get_Damage (void) {return (Damage);}; + + /* + ** Members to allow write access to private data. + */ + void Set_Range (int range) {Range = range;}; + void Set_Speed (int speed) {Speed = speed;}; + void Set_Damage (int damage) {Damage = damage;}; + + /* + ** Possible states the vortex can be in. + */ + typedef enum AnimStateType : unsigned char { + STATE_GROW, //Vortex has just appeared and is growing larger + STATE_ROTATE, //Vortex is rotating + STATE_SHRINK //Vortex is shrinking and about to disappear + }AnimStateType; + + private: + + /* + ** Members for setting up the lookup tables. + */ + void Build_Fading_Table (PaletteClass const & palette, void * dest, int color, int frac); + void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); + + /* + ** Misc internal functions + */ + void Attack(void); + void Zap_Target(void); + void Movement(void); + void Hide(void); + void Show(void); + + /* + ** Position of the top left of the vortex + */ + COORDINATE Position; + + /* + ** Direction of rotation + */ + int AnimateDir; + + /* + ** Current frame of animation + */ + int AnimateFrame; + + /* + ** Animation flag. When 0 vortex will animate 1 frame. + */ + int Animate; + + /* + ** State of vortex. See ENUM for info. + */ + AnimStateType State; + + /* + ** Color lookup tables for shading on vortex. + */ + unsigned char VortexRemapTables [MAX_REMAP_SHADES][256]; + + /* + ** Color lookup table to make the blue lightning orange. + */ + unsigned char LightningRemap[256]; + + /* + ** Is vortex currently active? + */ + int Active : 1; + + /* + ** Is the vortex winding down? + */ + int StartShutdown : 1; + + /* + ** Is the vortex about to hide from view? + */ + int StartHiding : 1; + + /* + ** Is the vortex active but hidden? + */ + int Hidden : 1; + + /* + ** Theater that lookup table is good for. + */ + TheaterType Theater; + + /* + ** Last frame that vortex attacked on + */ + int LastAttackFrame; + + /* + ** How many times lightning has zapped on this attack + */ + int ZapFrame; + + /* + ** Ptr to object that the vortex is zapping + */ + TARGET TargetObject; +// ObjectClass *TargetObject; + + /* + ** Distance to the target object + */ + int TargetDistance; + + /* + ** Game frame that vortex hid on. + */ + int HiddenFrame; + + /* + ** Direction vortex is going in. + */ + int XDir; + int YDir; + + /* + ** Direction vortex should be going in + */ + int DesiredXDir; + int DesiredYDir; + + /* + ** Range in cells of the vortex lightning + */ + int Range; + + /* + ** Max speed in leptons per frame of the vortex. + */ + int Speed; + + /* + ** Damge of vortex lightning zap. + */ + int Damage; + + /* + ** Offscreen buffer to render vortex into. This is needed so we can handle clipping. + */ + GraphicBufferClass *RenderBuffer; +}; + + +#endif diff --git a/REDALERT/W95TRACE.CPP b/REDALERT/W95TRACE.CPP new file mode 100644 index 000000000..453cfc0ff --- /dev/null +++ b/REDALERT/W95TRACE.CPP @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* + Implementation of Win95 tracing facility to mimic that of NT +*/ + +#include +#include +#include +#include +#include "w95trace.h" + + +void OutputDebugStringW95( LPCTSTR /*lpOutputString*/, ...) +{ +#if 0 + + HANDLE heventDBWIN; /* DBWIN32 synchronization object */ + HANDLE heventData; /* data passing synch object */ + HANDLE hSharedFile; /* memory mapped file shared data */ + LPSTR lpszSharedMem; + char achBuffer[500]; + + /* create the output buffer */ + va_list args; + va_start(args, lpOutputString); + vsprintf(achBuffer, lpOutputString, args); + va_end(args); + + achBuffer[499] = 0; // Null-terminate here, just in case vsprintf didn't do it because lpOutputString was too long. + + /* + Do a regular OutputDebugString so that the output is + still seen in the debugger window if it exists. + + This ifdef is necessary to avoid infinite recursion + from the inclusion of W95TRACE.H + */ +#ifdef _UNICODE + ::OutputDebugStringW(achBuffer); +#else + ::OutputDebugStringA(achBuffer); +#endif + + // added by ajw + //FILE* pFile = fopen( "debugout.wri", "a" ); + FILE* pFile = fopen( "wolapi.out", "a" ); + fprintf( pFile, achBuffer ); + fclose( pFile ); + +// /* bail if it's not Win95 */ +// { +// OSVERSIONINFO VerInfo; +// VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); +// GetVersionEx(&VerInfo); +// if ( VerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS ) +// return; +// } + + /* make sure DBWIN is open and waiting */ + heventDBWIN = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_BUFFER_READY"); + if ( !heventDBWIN ) + { + //MessageBox(NULL, "DBWIN_BUFFER_READY nonexistent", NULL, MB_OK); + return; + } + + /* get a handle to the data synch object */ + heventData = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_DATA_READY"); + if ( !heventData ) + { + // MessageBox(NULL, "DBWIN_DATA_READY nonexistent", NULL, MB_OK); + CloseHandle(heventDBWIN); + return; + } + + hSharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, "DBWIN_BUFFER"); + if (!hSharedFile) + { + //MessageBox(NULL, "DebugTrace: Unable to create file mapping object DBWIN_BUFFER", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + lpszSharedMem = (LPSTR)MapViewOfFile(hSharedFile, FILE_MAP_WRITE, 0, 0, 512); + if (!lpszSharedMem) + { + //MessageBox(NULL, "DebugTrace: Unable to map shared memory", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + /* wait for buffer event */ + WaitForSingleObject(heventDBWIN, INFINITE); + + /* write it to the shared memory */ + *((LPDWORD)lpszSharedMem) = getpid(); + wsprintf(lpszSharedMem + sizeof(DWORD), "%s", achBuffer); + + /* signal data ready event */ + SetEvent(heventData); + + /* clean up handles */ + CloseHandle(hSharedFile); + CloseHandle(heventData); + CloseHandle(heventDBWIN); + + return; +#endif +} diff --git a/REDALERT/W95TRACE.H b/REDALERT/W95TRACE.H new file mode 100644 index 000000000..e581cdde5 --- /dev/null +++ b/REDALERT/W95TRACE.H @@ -0,0 +1,55 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + declarations for Win95 tracing facility +*/ + +#ifdef _DEBUG + +#ifndef __TRACEW95__ +#define __TRACEW95__ + +#include + +// redefine all the MFC macros to point to us + +#undef TRACE +#define TRACE OutputDebugStringW95 + +#undef TRACE0 +#define TRACE0 OutputDebugStringW95 + +#undef TRACE1 +#define TRACE1 OutputDebugStringW95 + +#undef TRACE2 +#define TRACE2 OutputDebugStringW95 + +#undef TRACE3 +#define TRACE3 OutputDebugStringW95 + +// redefine OutputDebugString so that it works with +// API calls +#undef OutputDebugString +#define OutputDebugString OutputDebugStringW95 + + +// function declarations +void OutputDebugStringW95( LPCTSTR lpOutputString, ... ); + +#endif //__TRACEW95__ + +#endif // _DEBUG diff --git a/REDALERT/WARHEAD.CPP b/REDALERT/WARHEAD.CPP new file mode 100644 index 000000000..c0844578b --- /dev/null +++ b/REDALERT/WARHEAD.CPP @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WARHEAD.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WARHEAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*************************************************************************** +** This is the warhead data object array. +*/ +TFixedIHeapClass Warheads; + + +/*********************************************************************************************** + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * * + * This default constructor for a warhead object will fill in all the default values * + * for a warhead. It is presumed that these values will be normal unless specifically * + * overridden by the INI database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass::WarheadTypeClass(char const * name) : + ID(Warheads.ID(this)), + IniName(name), + SpreadFactor(1), + IsWallDestroyer(false), + IsWoodDestroyer(false), + IsTiberiumDestroyer(false), + IsOrganic(false), + ExplosionSet(0), + InfantryDeath(0) +{ + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = 1; + } +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * * + * This will allocate a warhead object from the special heap that is maintained for * + * this purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated warhead type object. If there is * + * insufficient memory for the allocation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void * WarheadTypeClass::operator new(size_t) +{ + return(Warheads.Alloc()); +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * * + * This routine will return the warhead object to the memory pool from whence it came. * + * * + * INPUT: pointer -- Pointer to the warhead object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void WarheadTypeClass::operator delete(void * pointer) +{ + Warheads.Free((WarheadTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * * + * This will locate the warhead type specified and return a pointer to it. * + * * + * INPUT: warhead -- The warhead to convert into a pointer. * + * * + * OUTPUT: Returns with a pointer to the warhead type object that is represented by the * + * warhead type number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass * WarheadTypeClass::As_Pointer(WarheadType warhead) +{ + if (warhead != WARHEAD_NONE) { + return(Warheads.Ptr(warhead)); + } + return(NULL); +} + + +/*********************************************************************************************** + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * * + * Use this routine to retrieve the data specific to this warhead type class object from * + * the INI database specified. Typical use of this is when processing the rules.ini * + * file. * + * * + * INPUT: ini -- Reference to the INI database to fetch the values from. * + * * + * OUTPUT: bool; Was the warhead entry found and the data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WarheadTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + SpreadFactor = ini.Get_Int(Name(), "Spread", SpreadFactor); + IsWallDestroyer = ini.Get_Bool(Name(), "Wall", IsWallDestroyer); + IsWoodDestroyer = ini.Get_Bool(Name(), "Wood", IsWoodDestroyer); + IsTiberiumDestroyer = ini.Get_Bool(Name(), "Ore", IsTiberiumDestroyer); + ExplosionSet = ini.Get_Int(Name(), "Explosion", ExplosionSet); + InfantryDeath = ini.Get_Int(Name(), "InfDeath", InfantryDeath); + + char buffer[128]; + if (ini.Get_String(Name(), "Verses", "100%%,100%%,100%%,100%%,100%%", buffer, sizeof(buffer))) { + char * aval = strtok(buffer, ","); + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = fixed(aval); + aval = strtok(NULL, ","); + } + } + + IsOrganic = (Modifier[ARMOR_STEEL] == 0); + return(true); + } + return(false); +} + diff --git a/REDALERT/WARHEAD.H b/REDALERT/WARHEAD.H new file mode 100644 index 000000000..c400af9e7 --- /dev/null +++ b/REDALERT/WARHEAD.H @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WARHEAD.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WARHEAD.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WARHEAD_H +#define WARHEAD_H + + +/********************************************************************** +** Each of the warhead types has specific characteristics. This structure +** holds these characteristics. +*/ +class WarheadTypeClass +{ + public: + WarheadTypeClass(char const * name); + WarheadTypeClass(NoInitClass const &) {} + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WarheadTypeClass * As_Pointer(WarheadType weapon); + + /* + ** This is the ID number of the weapon type. It is the weapon + ** type ID enum as well as the index number into the weapon + ** heap. + */ + int ID; + + /* + ** This is the identifying name of this warhead type. + */ + char const * IniName; + + /* + ** This value control how damage from this warhead type will reduce + ** over distance. The larger the number, the less the damage is reduced + ** the further the distance from the source of the damage. + */ + int SpreadFactor; + + /* + ** If this warhead type can destroy walls, then this flag will be true. + */ + bool IsWallDestroyer:1; + + /* + ** If this warhead can destroy wooden walls, then this flag will be true. + */ + bool IsWoodDestroyer:1; + + /* + ** Does this warhead damage tiberium? + */ + bool IsTiberiumDestroyer:1; + + /* + ** Only effective against infantry? + */ + bool IsOrganic:1; + + /* + ** The warhead damage is reduced depending on the the type of armor the + ** defender has. This table is what gives weapons their "character". + */ + fixed Modifier[ARMOR_COUNT]; + + /* + ** Which explosion set to use for warhead impact. + */ + int ExplosionSet; + + /* + ** This specifies the infantry death animation to use if the infantry dies as + ** a result of a warhead of this type. + */ + int InfantryDeath; +}; + + +#endif diff --git a/REDALERT/WATCOM.H b/REDALERT/WATCOM.H new file mode 100644 index 000000000..c8ded8df3 --- /dev/null +++ b/REDALERT/WATCOM.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WATCOM.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WATCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/12/95 * + * * + * Last Update : March 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WATCOM_H +#define WATCOM_H +#if (0) + +// Turn all warnings into errors. +#pragma warning * 0 + +// Disables warning when "sizeof" is used on an object with virtual functions. +#pragma warning 549 9 + +// Disable the "Integral value may be truncated during assignment or initialization". +#pragma warning 389 9 + +// Allow constructing a temporary to be used as a parameter. +#pragma warning 604 9 + +// Disable the construct resolved as an expression warning. +#pragma warning 595 9 + +// Disable the strange "construct resolved as a declaration/type" warning. +#pragma warning 594 9 + +// Disable the "pre-compiled header file cannot be used" warning. +#pragma warning 698 9 + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + +// Disable the "pointer or reference truncated by cast. Cast is supposed to REMOVE warnings, not create them. +#pragma warning 579 9 + +// Disable the warning that suggests a null destructor be placed in class definition. +#pragma warning 656 9 + +// Disable the warning about moving empty constructors/destructors to the class declaration. +#pragma warning 657 9 + +// No virtual destructor is not an error in C&C. +#pragma warning 004 9 + +// Turns off unreferenced function parameter warning. +//#pragma off(unreferenced) + + +// Fix deficiency in Watcom so that true/false will be defined. +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#endif + +#endif diff --git a/REDALERT/WEAPON.CPP b/REDALERT/WEAPON.CPP new file mode 100644 index 000000000..6eb90dbf8 --- /dev/null +++ b/REDALERT/WEAPON.CPP @@ -0,0 +1,333 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WEAPON.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WEAPON.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : September 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Armor_From_Name -- Convert ASCII name into armor type number. * + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object.* + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the various weapons and their characteristics. +*/ +TFixedIHeapClass Weapons; + + +/*********************************************************************************************** + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * * + * This default constructor will initialize all the values of the weapon to the default * + * state. Thus, if any of these settings are not specifically overridden by the rules.ini * + * file, they will remain this value in the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::WeaponTypeClass(char const * name) : + ID(Weapons.ID(this)), + IniName(name), + IsSupressed(false), + IsCamera(false), + IsElectric(false), + Burst(1), + Bullet(NULL), + Attack(0), + MaxSpeed(MPH_IMMOBILE), + WarheadPtr(NULL), + ROF(0), + Range(0), + Sound(VOC_NONE), + Anim(ANIM_NONE) +{ +} + + +/*********************************************************************************************** + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * * + * This destructor really doesn't do anything but set the pointers to NULL. This is a * + * general purposes safety tactic but is otherwise useless. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::~WeaponTypeClass(void) +{ + IniName = NULL; + Bullet = NULL; + WarheadPtr = NULL; +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * * + * This will allocate a weapon type object from a special heap that has been set up for * + * that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the weapon type object allocated. If there was * + * insufficient memory available, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void * WeaponTypeClass::operator new(size_t) +{ + return(Weapons.Alloc()); +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * * + * This routine will return the weapon type object back to the heap so that the memory * + * can be reused for subsiquent allocations of weapon type objects. * + * * + * INPUT: pointer -- Pointer to the weapon type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void WeaponTypeClass::operator delete(void * pointer) +{ + Weapons.Free((WeaponTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object. * + * * + * This routine will conver the weapon type ID specified into a pointer to the weapon type * + * object it represents. * + * * + * INPUT: weapon -- The weapon type object ID to convert to a pointer. * + * * + * OUTPUT: Returns with a pointer to the weapon type class object that is represented by the * + * weapon ID number specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass * WeaponTypeClass::As_Pointer(WeaponType weapon) +{ + if (weapon != WEAPON_NONE) { + return(Weapons.Ptr(weapon)); +// for (int index = 0; index < Weapons.Count(); index++) { +// WeaponTypeClass * ptr = Weapons.Ptr(index); +// if (ptr->ID == weapon) { +// return(ptr); +// } +// } + } + return(NULL); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * * + * This routine will fetch the weapon data for this weapon type object from the INI * + * database specified. * + * * + * INPUT: ini -- Reference to the INI database that the weapon data will be fetched * + * from. * + * * + * OUTPUT: bool; Was this weapon type described in the database and the values retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WeaponTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsSupressed = ini.Get_Bool(Name(), "Supress", IsSupressed); + Burst = ini.Get_Int(Name(), "Burst", Burst); + Attack = ini.Get_Int(Name(), "Damage", Attack); + MaxSpeed = ini.Get_MPHType(Name(), "Speed", MaxSpeed); + ROF = ini.Get_Int(Name(), "ROF", ROF); + Range = ini.Get_Lepton(Name(), "Range", Range); + Sound = ini.Get_VocType(Name(), "Report", Sound); + Anim = ini.Get_AnimType(Name(), "Anim", Anim); + IsCamera = ini.Get_Bool(Name(), "Camera", IsCamera); + IsElectric = ini.Get_Bool(Name(), "Charges", IsElectric); + IsTurboBoosted = ini.Get_Bool(Name(), "TurboBoost", IsTurboBoosted); + + WarheadType wtype = (WarheadPtr != NULL) ? WarheadType(WarheadPtr->ID) : WARHEAD_NONE; + wtype = ini.Get_WarheadType(Name(), "Warhead", wtype); + if (wtype != WARHEAD_NONE) { + WarheadPtr = WarheadTypeClass::As_Pointer(wtype); +// WarheadPtr = &Warheads[wtype]; + } else { + WarheadPtr = NULL; + } + + BulletType btype = (Bullet != NULL) ? BulletType(Bullet->ID) : BULLET_NONE; + btype = ini.Get_BulletType(Name(), "Projectile", btype); + if (btype != BULLET_NONE) { + Bullet = &BulletTypeClass::As_Reference(btype); + } else { + Bullet = NULL; + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * * + * This will find the weapon whos name matches that specified and then it will return the * + * weapon ID number associated with it. * + * * + * INPUT: name -- Pointer to the ASCII name of the weapon type. * + * * + * OUTPUT: Returns with the weapon type ID number that matches the name specified. If no * + * match could be found, then WEAPON_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType Weapon_From_Name(char const * name) +{ + if (!name) return(WEAPON_NONE); + + for (int index = 0; index < Weapons.Count(); index++) { + if (stricmp(Weapons.Ptr(index)->Name(), name) == 0) { + return(WeaponType(Weapons.Ptr(index)->ID)); + } + } + + return(WEAPON_NONE); +} + + +/*********************************************************************************************** + * Armor_From_Name -- Convert ASCII name into armor type number. * + * * + * This will find the armor type that matches the ASCII name specified and then will return * + * the armor ID number of it. * + * * + * INPUT: name -- Pointer to the ASCII name of the armor to find. * + * * + * OUTPUT: Returns with the armor ID number of the armor that matches the name specified. If * + * no match could be found, then ARMOR_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType Armor_From_Name(char const * name) +{ + if (!name) return(ARMOR_NONE); + + for (ArmorType index = ARMOR_FIRST; index < ARMOR_COUNT; index++) { + if (stricmp(ArmorName[index], name) == 0) { + return(index); + } + } + + return(ARMOR_NONE); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * * + * This routine will examine the capabilities of this weapon and return with the threat * + * types that it can address. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the threat types that this weapon can address. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +ThreatType WeaponTypeClass::Allowed_Threats(void) const +{ + ThreatType threat = THREAT_NORMAL; + if (Bullet->IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + if (Bullet->IsAntiGround) { + threat = threat | THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BOATS|THREAT_BUILDINGS; + } + return(threat); +} + + +bool WeaponTypeClass::Is_Wall_Destroyer(void) const +{ + if (WarheadPtr != NULL && WarheadPtr->IsWallDestroyer) { + return(true); + } + return(false); +} diff --git a/REDALERT/WEAPON.H b/REDALERT/WEAPON.H new file mode 100644 index 000000000..966d5c956 --- /dev/null +++ b/REDALERT/WEAPON.H @@ -0,0 +1,162 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WEAPON.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WEAPON.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WEAPON_H +#define WEAPON_H + + +/********************************************************************** +** This is the constant data associated with a weapon. Some objects +** can have multiple weapons and this class is used to isolate and +** specify this data in a convenient and selfcontained way. +*/ +class WeaponTypeClass +{ + public: + WeaponTypeClass(char const * name); + WeaponTypeClass(NoInitClass const &) {} + ~WeaponTypeClass(void); + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WeaponTypeClass * As_Pointer(WeaponType weapon); + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + ThreatType Allowed_Threats(void) const; + bool Is_Wall_Destroyer(void) const; + + /* + ** This is both the weapon type number and the index number into + ** the weapon array. + */ + int ID; + + /* + ** This is the identifying name of this weapon. + */ + char const * IniName; + + /* + ** Increase the weapon speed if the target is flying. + */ + unsigned IsTurboBoosted:1; + + /* + ** If potential targets of this weapon should be scanned for + ** nearby friendly structures and if found, firing upon the target + ** would be discouraged, then this flag will be true. + */ + unsigned IsSupressed:1; + + /* + ** If this weapon is equipped with a camera that reveals the + ** area around the firer, then this flag will be true. + */ + unsigned IsCamera:1; + + /* + ** If this weapon requires charging before it can fire, then this + ** flag is true. In actuality, this only applies to the Tesla coil + ** which has specific charging animation. The normal rate of fire + ** value suffices for all other cases. + */ + unsigned IsElectric:1; + + /* + ** This is the number of shots this weapon first (in rapid succession). + ** The normal value is 1, but for the case of two shooter weapons such as + ** the double barreled gun turrets of the Mammoth tank, this value will be + ** set to 2. + */ + int Burst; + + /* + ** This is the unit class of the projectile fired. A subset of the unit types + ** represent projectiles. It is one of these classes that is specified here. + ** If this object does not fire anything, then this value will be BULLET_NONE. + */ + BulletTypeClass const * Bullet; + + /* + ** This is the damage (explosive load) to be assigned to the projectile that + ** this object fires. For the rare healing weapon, this value is negative. + */ + int Attack; + + /* + ** Speed of the projectile launched. + */ + MPHType MaxSpeed; + + /* + ** Warhead to attach to the projectile. + */ + WarheadTypeClass const * WarheadPtr; + + /* + ** Objects that fire (which can be buildings as well) will fire at a + ** frequency controlled by this value. This value serves as a count + ** down timer between shots. The smaller the value, the faster the + ** rate of fire. + */ + int ROF; + + /* + ** When this object fires, the range at which it's projectiles travel is + ** controlled by this value. The value represents the number of cells the + ** projectile will travel. Objects outside of this range will not be fired + ** upon (in normal circumstances). + */ + LEPTON Range; + + /* + ** This is the typical sound generated when firing. + */ + VocType Sound; + + /* + ** This is the animation to display at the firing coordinate. + */ + AnimType Anim; +}; + + +#endif + + diff --git a/REDALERT/WIN32LIB/ALLOC.CPP b/REDALERT/WIN32LIB/ALLOC.CPP new file mode 100644 index 000000000..cd3f15fed --- /dev/null +++ b/REDALERT/WIN32LIB/ALLOC.CPP @@ -0,0 +1,509 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +//#include +//#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; +extern void (*Memory_Error_Exit)(char *string)=NULL; + + +//#define MEM_CHECK + +#ifdef MEM_CHECK +extern "C"{ + extern void __cdecl Int3(void); +} +#endif //MEM_CHECK + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *, long const ) +{ +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *, long const ) +{ +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + * 09/28/1995 ST : Simplified for win95 * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + +#ifdef WIN32 + + void *mem_ptr; + +#ifdef MEM_CHECK + bytes_to_alloc += 32; +#endif //MEM_CHECK + + mem_ptr = malloc ( bytes_to_alloc ); + + if ( !mem_ptr && Memory_Error ){ + Memory_Error(); + } + + if ( mem_ptr && ( flags & MEM_CLEAR ) ){ + memset ( mem_ptr , 0 , bytes_to_alloc ); + } + +#ifdef MEM_CHECK + mem_ptr = (void*)((char*)mem_ptr + 16); + unsigned long *magic_ptr =(unsigned long*) ( ((char *)mem_ptr) - 16 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = bytes_to_alloc - 32; + magic_ptr = (unsigned long*) ( ((char*)mem_ptr) + bytes_to_alloc - 32 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = (unsigned long)mem_ptr; +#endif //MEM_CHECK + + Memory_Calls++; + return ( mem_ptr ); + +#else + + + + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); + +#endif +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +#ifdef WIN32 + +void Free(void const *pointer) +{ + + if ( pointer ){ + +#ifdef MEM_CHECK + + unsigned long *magic_ptr = (unsigned long*) ( ((char*)pointer) - 16 ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + magic_ptr = (unsigned long*) ( ((char*)pointer) + *magic_ptr ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + pointer = (void*) (((char*)pointer)-16); +#endif //MEM_CHECK + + free ( (void*)pointer ); + Memory_Calls--; + } + +#else + +void Free(void const *pointer) +{ + + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } + +#endif +} + + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + return ( 64*1024*1024 ); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + + return ( 64*1024*1024 ); +} + diff --git a/REDALERT/WIN32LIB/AUDIO.H b/REDALERT/WIN32LIB/AUDIO.H new file mode 100644 index 000000000..1e5d9adc3 --- /dev/null +++ b/REDALERT/WIN32LIB/AUDIO.H @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +//#pragma pack(1); +#pragma pack(push,1) +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; +#pragma pack(pop) + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; + +extern int StreamLowImpact; \ No newline at end of file diff --git a/REDALERT/WIN32LIB/BUFFER.CPP b/REDALERT/WIN32LIB/BUFFER.CPP new file mode 100644 index 000000000..7affb0886 --- /dev/null +++ b/REDALERT/WIN32LIB/BUFFER.CPP @@ -0,0 +1,128 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/REDALERT/WIN32LIB/BUFFER.H b/REDALERT/WIN32LIB/BUFFER.H new file mode 100644 index 000000000..39ce89481 --- /dev/null +++ b/REDALERT/WIN32LIB/BUFFER.H @@ -0,0 +1,121 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif diff --git a/REDALERT/WIN32LIB/BUFFGLBL.CPP b/REDALERT/WIN32LIB/BUFFGLBL.CPP new file mode 100644 index 000000000..6c16b9d1a --- /dev/null +++ b/REDALERT/WIN32LIB/BUFFGLBL.CPP @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "gbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +#ifdef not_any_more_it_doesnt +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +#endif //not_any_more_it_doesnt + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicViewPortClass *LogicPage; + +BOOL IconCacheAllowed = TRUE; + +/* +** Pointer to a function we will call if we detect loss of focus +*/ +void (*Gbuffer_Focus_Loss_Function)(void) = NULL; diff --git a/REDALERT/WIN32LIB/DDRAW.CPP b/REDALERT/WIN32LIB/DDRAW.CPP new file mode 100644 index 000000000..fb84677a2 --- /dev/null +++ b/REDALERT/WIN32LIB/DDRAW.CPP @@ -0,0 +1,1002 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Win32 Library * + * * + * File Name : DDRAW.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : October 10, 1995 * + * * + * Last Update : October 10, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +#include "misc.h" +#include +#include +#include "stdio.h" + +LPDIRECTDRAW DirectDrawObject=NULL; // Pointer to the direct draw object +LPDIRECTDRAW2 DirectDraw2Interface = NULL; // Pointer to direct draw 2 interface + +HWND MainWindow; // Handle to programs main window + // this is passed to SetCooperativeLevel + // so DirectDraw knows which window is ours + + +PALETTEENTRY PaletteEntries[256]; // 256 windows palette entries +LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object +BOOL FirstPaletteSet=FALSE; // Is this the first time 'Set_Palette' has been called? +LPDIRECTDRAWSURFACE PaletteSurface=NULL; +SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces +BOOL CanVblankSync = TRUE; + +BOOL SystemToVideoBlits =FALSE; // Does hardware support system mem to video mem blits? +BOOL VideoToSystemBlits =FALSE; // Does hardware support video mem to system mem blits? +BOOL SystemToSystemBlits = FALSE; // Does hardware support system mem to system mem blits? +BOOL OverlappedVideoBlits = TRUE; // Can video driver blit overlapped regions? + +/* +** Function to call if we detect focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void) = NULL; +extern void (*Misc_Focus_Restore_Function)(void) = NULL; + + +/*********************************************************************************************** + * Process_DD_Result -- Does a message box based on the result of a DD command * + * * + * INPUT: HRESULT result - the result returned from the direct draw command * + * int display_ok_msg - should a message be displayed if command ok * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/27/1995 PWG : Created. * + *=============================================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg) +{ + switch (result) { + case DD_OK: + if (display_ok_msg) { + MessageBox(MainWindow, "Direct Draw request went ok.", "Note", MB_ICONEXCLAMATION|MB_OK); + } + break; + case DDERR_ALREADYINITIALIZED: + MessageBox(MainWindow, "This object is already initialized ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_BLTFASTCANTCLIP: + MessageBox(MainWindow, "Return if a clipper object is attached to the source surface passed into a BltFast call.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTATTACHSURFACE: + MessageBox(MainWindow, "This surface can not be attached to the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTDETACHSURFACE: + MessageBox(MainWindow, "This surface can not be detached from the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTCREATEDC: + MessageBox(MainWindow, "Windows can not create any more DCs","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTDUPLICATE: + MessageBox(MainWindow, "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTLOCKSURFACE: + MessageBox(MainWindow, "Unable to lock surface because no driver exists which can supply a pointer to the surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CLIPPERISUSINGHWND: + MessageBox(MainWindow, "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_COLORKEYNOTSET: + MessageBox(MainWindow, "No src color key specified for this operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CURRENTLYNOTAVAIL: + MessageBox(MainWindow, "Support is currently not available. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_DIRECTDRAWALREADYCREATED: + MessageBox(MainWindow, "A DirectDraw object representing this driver has already been created for this process. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCEPTION: + MessageBox(MainWindow, "An exception was encountered while performing the requested operation. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCLUSIVEMODEALREADYSET: + MessageBox(MainWindow, "An attempt was made to set the cooperative level when it was already set to exclusive.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_GENERIC: + MessageBox(MainWindow, "Generic failure.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HEIGHTALIGN: + MessageBox(MainWindow, "Height of rectangle provided is not a multiple of reqd alignment.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDALREADYSET: + MessageBox(MainWindow, "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDSUBCLASSED: + MessageBox(MainWindow, "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_IMPLICITLYCREATED: + MessageBox(MainWindow, "This surface can not be restored because it is an implicitly created surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INCOMPATIBLEPRIMARY: + MessageBox(MainWindow, "Unable to match primary surface creation request with existing primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCAPS: + MessageBox(MainWindow, "One or more of the caps bits passed to the callback are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCLIPLIST: + MessageBox(MainWindow, "DirectDraw does not support the provided cliplist.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDDIRECTDRAWGUID: + MessageBox(MainWindow, "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDMODE: + MessageBox(MainWindow, "DirectDraw does not support the requested mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDOBJECT: + MessageBox(MainWindow, "DirectDraw received a pointer that was an invalid DIRECTDRAW object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPARAMS: + MessageBox(MainWindow, "One or more of the parameters passed to the function are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPIXELFORMAT: + MessageBox(MainWindow, "The pixel format was invalid as specified.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPOSITION: + MessageBox(MainWindow, "Returned when the position of the overlay on the destination is no longer legal for that destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDRECT: + MessageBox(MainWindow, "Rectangle provided was invalid.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + + case DDERR_INVALIDSURFACETYPE: + MessageBox(MainWindow, "The requested action could not be performed because the surface was of the wrong type.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_LOCKEDSURFACES: + MessageBox(MainWindow, "Operation could not be carried out because one or more surfaces are locked.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NO3D: + MessageBox(MainWindow, "There is no 3D present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOALPHAHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no alpha accleration hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOANTITEARHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for synchronizing blts to avoid tearing. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOBLTHW: + MessageBox(MainWindow, "No blter hardware present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOBLTQUEUEHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for asynchronous blting. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOCLIPLIST: + MessageBox(MainWindow, "No cliplist available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCLIPPERATTACHED: + MessageBox(MainWindow, "No clipper object attached to surface object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORCONVHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no color conversion hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEY: + MessageBox(MainWindow, "Surface doesn't currently have a color key","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support of the destination color key.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOOPERATIVELEVELSET: + MessageBox(MainWindow, "Create function called without DirectDraw object method SetCooperativeLevel being called.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODC: + MessageBox(MainWindow, "No DC was ever created for this surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODDROPSHW: + MessageBox(MainWindow, "No DirectDraw ROP hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWHW: + MessageBox(MainWindow, "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWSUPPORT: + MessageBox(MainWindow, "No DirectDraw support possible with current display driver.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEMULATION: + MessageBox(MainWindow, "Software emulation not available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEXCLUSIVEMODE: + MessageBox(MainWindow, "Operation requires the application to have exclusive mode but the application does not have exclusive mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOFLIPHW: + MessageBox(MainWindow, "Flipping visible surfaces is not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOGDI: + MessageBox(MainWindow, "There is no GDI present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOHWND: + MessageBox(MainWindow, "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOMIRRORHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYDEST: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no overlay hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEATTACHED: + MessageBox(MainWindow, "No palette object attached to this surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEHW: + MessageBox(MainWindow, "No hardware support for 16 or 256 color palettes.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NORASTEROPHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no appropriate raster op hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOROTATIONHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no rotation hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOSTRETCHHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for stretching.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLORINDEX: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT8BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTAOVERLAYSURFACE: + MessageBox(MainWindow, "Returned when an overlay member is called for a non-overlay surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTEXTUREHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no texture mapping hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFLIPPABLE: + MessageBox(MainWindow, "An attempt has been made to flip a surface that is not flippable.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFOUND: + MessageBox(MainWindow, "Requested item was not found.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTLOCKED: + MessageBox(MainWindow, "Surface was not locked. An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTPALETTIZED: + MessageBox(MainWindow, "The surface being used is not a palette-based surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOVSYNCHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZBUFFERHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for zbuffer blting.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZOVERLAYHW: + MessageBox(MainWindow, "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFCAPS: + MessageBox(MainWindow, "The hardware needed for the requested operation has already been allocated.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFVIDEOMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCANTCLIP: + MessageBox(MainWindow, "The hardware does not support clipped overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + MessageBox(MainWindow, "Can only have ony color key active at one time for overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYNOTVISIBLE: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on a hidden overlay.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PALETTEBUSY: + MessageBox(MainWindow, "Access to this palette is being refused because the palette is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PRIMARYSURFACEALREADYEXISTS: + MessageBox(MainWindow, "This process already has created a primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_REGIONTOOSMALL: + MessageBox(MainWindow, "Region passed to Clipper::GetClipList is too small.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYATTACHED: + MessageBox(MainWindow, "This surface is already attached to the surface it is being attached to.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYDEPENDENT: + MessageBox(MainWindow, "This surface is already a dependency of the surface it is being made a dependency of.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEBUSY: + MessageBox(MainWindow, "Access to this surface is being refused because the surface is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEISOBSCURED: + MessageBox(MainWindow, "Access to surface refused because the surface is obscured.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACELOST: + MessageBox(MainWindow, "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.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACENOTATTACHED: + MessageBox(MainWindow, "The requested surface is not attached.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGHEIGHT: + MessageBox(MainWindow, "Height requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGSIZE: + MessageBox(MainWindow, "Size requested by DirectDraw is too large -- the individual height and width are OK.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGWIDTH: + MessageBox(MainWindow, "Width requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTED: + MessageBox(MainWindow, "Action not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDFORMAT: + MessageBox(MainWindow, "FOURCC format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDMASK: + MessageBox(MainWindow, "Bitmask in the pixel format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_VERTICALBLANKINPROGRESS: + MessageBox(MainWindow, "Vertical blank is in progress.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WASSTILLDRAWING: + MessageBox(MainWindow, "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WRONGMODE: + MessageBox(MainWindow, "This surface can not be restored because it was created in a different mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_XALIGN: + MessageBox(MainWindow, "Rectangle provided was not horizontally aligned on required boundary.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + default: + char string[256]; + sprintf (string, "Unrecognised Direct Draw result code: %d", result & 0xffff); + MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + break; + } +} + + + +/*********************************************************************************************** + * Check_Overlapped_Blit_Capability -- See if video driver supports blitting overlapped regions* + * * + * We will check for this by drawing something to a video page and blitting it over itself. * + * If we end up with the top line repeating then overlapped region blits dont work. * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 5:06PM ST : Created * + *=============================================================================================*/ +void Check_Overlapped_Blit_Capability(void) +{ + + /* + ** Assume we can until we find out otherwise + */ + OverlappedVideoBlits = TRUE; + + GraphicBufferClass test_buffer; + + test_buffer.Init (64, 64, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + test_buffer.Clear(); + + /* + ** Plot a pixel in the top left corner of the buffer. + */ + test_buffer.Put_Pixel(0, 0, 255); + + /* + ** Blit the buffer down by one line. If we end up with a vertical strip of pixel 255's then + ** overlapped blits dont work + */ + + test_buffer.Blit(test_buffer, 0, 0, 0, 1, test_buffer.Get_Width(), test_buffer.Get_Height()-1); + + if (test_buffer.Get_Pixel (0 ,5) == 255) OverlappedVideoBlits = FALSE; +} + + + +/*********************************************************************************************** + * Set_Video_Mode -- Initializes Direct Draw and sets the required Video Mode * + * * + * INPUT: int width - the width of the video mode in pixels * + * int height - the height of the video mode in pixels * + * int bits_per_pixel - the number of bits per pixel the video mode supports * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel) +{ +// ST - 1/3/2019 10:41AM + return TRUE; +#if (0) + HRESULT result; + // + // If there is not currently a direct draw object then we need to define one. + // + if ( DirectDrawObject == NULL ){ + //MessageBox(MainWindow, "In Set_Video_Mode. About to call DirectDrawCreate.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawCreate(NULL, &DirectDrawObject, NULL); + Process_DD_Result(result, FALSE); + if (result == DD_OK){ + if (w==320){ + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + } else { + //MessageBox(MainWindow, "In Set_Video_Mode. About to call SetCooperativeLevel.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + Process_DD_Result(result, FALSE); + }else{ + return (FALSE); + } + } + + // + // Set the required display mode with 8 bits per pixel + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call call SetDisplayMode.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetDisplayMode ( w , h , bits_per_pixel ); + if (result != DD_OK){ +// Process_DD_Result(result, FALSE); + DirectDrawObject->Release(); + DirectDrawObject = NULL; + return(FALSE); + } + + // + // Create a direct draw palette object + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call CreatePalette.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &PaletteEntries[0] , &PalettePtr ,NULL); + Process_DD_Result(result, FALSE); + if (result != DD_OK){ + return (FALSE); + } + + Check_Overlapped_Blit_Capability(); + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); +#if (0) + /* + ** Find out if DirectX 2 extensions are available + */ + result = DirectDrawObject->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DirectDraw2Interface); + SystemToVideoBlits = FALSE; + VideoToSystemBlits = FALSE; + SystemToSystemBlits= FALSE; + if (result != DD_OK){ + DirectDraw2Interface = NULL; + }else{ + DDCAPS capabilities; + DDCAPS emulated_capabilities; + + memset ((char*)&capabilities, 0, sizeof(capabilities)); + memset ((char*)&emulated_capabilities, 0, sizeof(emulated_capabilities)); + capabilities.dwSize = sizeof (capabilities); + emulated_capabilities.dwSize = sizeof (emulated_capabilities); + + DirectDrawObject->GetCaps (&capabilities, &emulated_capabilities); + + if (capabilities.dwCaps & DDCAPS_CANBLTSYSMEM){ + SystemToVideoBlits = (capabilities.dwSVBCaps & DDCAPS_BLT) ? TRUE : FALSE; + VideoToSystemBlits = (capabilities.dwVSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + SystemToSystemBlits = (capabilities.dwSSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + } + } +#endif //(0) + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + + return (TRUE); +#endif +} + +/*********************************************************************************************** + * Reset_Video_Mode -- Resets video mode and deletes Direct Draw Object * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +void Reset_Video_Mode(void) +{ + HRESULT result; + + // + // If a direct draw object has been declared and a video mode has been set + // then reset the video mode and release the direct draw object. + // + if ( DirectDrawObject ) { + result = DirectDrawObject->RestoreDisplayMode(); + Process_DD_Result(result, FALSE); + result = DirectDrawObject->Release(); + Process_DD_Result(result, FALSE); + + DirectDrawObject = NULL; + } +} + + + + +/*********************************************************************************************** + * Get_Free_Video_Memory -- returns amount of free video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: bytes of available video RAM * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:52PM ST : Created * + *=============================================================================================*/ +unsigned int Get_Free_Video_Memory(void) +{ + + DDCAPS video_capabilities; + + if (DirectDrawObject){ + + video_capabilities.dwSize = sizeof (video_capabilities); + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + char string [256]; + sprintf (string, "In Get_Free_Video_Memory. About to return %d bytes",video_capabilities.dwVidMemFree); + //MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + return (video_capabilities.dwVidMemFree); + } + } + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to return failure","Note", MB_ICONEXCLAMATION|MB_OK); + return (0); +} + + + + +/*********************************************************************************************** + * Get_Video_Hardware_Caps -- returns bitmask of direct draw video hardware support * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: hardware flags * + * * + * WARNINGS: Must call Set_Video_Mode 1st to create the direct draw object * + * * + * HISTORY: * + * 1/12/96 9:14AM ST : Created * + *=============================================================================================*/ +unsigned Get_Video_Hardware_Capabilities(void) +{ + DDCAPS video_capabilities; + unsigned video; + + /* + ** Fail if the direct draw object has not been initialised + */ + if (!DirectDrawObject) return (0); + + /* + ** Get the capabilities of the direct draw object + */ + video_capabilities.dwSize = sizeof(video_capabilities); + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + HRESULT result = DirectDrawObject->GetCaps (&video_capabilities, NULL); + if (result != DD_OK){ + Process_DD_Result(result, FALSE); + return (0); + } + + /* + ** Set flags to indicate the presence of the features we are interested in + */ + video = 0; + + /* Hardware blits supported? */ + if (video_capabilities.dwCaps & DDCAPS_BLT) video |= VIDEO_BLITTER; + + /* Hardware blits asyncronous? */ + if (video_capabilities.dwCaps & DDCAPS_BLTQUEUE) video |= VIDEO_BLITTER_ASYNC; + + /* Can palette changes be synced to vertical refresh? */ + if (video_capabilities.dwCaps & DDCAPS_PALETTEVSYNC) video |= VIDEO_SYNC_PALETTE; + + /* Is the video cards memory bank switched? */ + if (video_capabilities.dwCaps & DDCAPS_BANKSWITCHED) video |= VIDEO_BANK_SWITCHED; + + /* Can the blitter do filled rectangles? */ + if (video_capabilities.dwCaps & DDCAPS_BLTCOLORFILL) video |= VIDEO_COLOR_FILL; + + /* Is there no hardware assistance avaailable at all? */ + if (video_capabilities.dwCaps & DDCAPS_NOHARDWARE) video |= VIDEO_NO_HARDWARE_ASSIST; + + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + return (video); +} + + + + +/*********************************************************************************************** + * Wait_Vert_Blank -- Waits for the start (leading edge) of a vertical blank * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=============================================================================================*/ +extern int ScreenWidth; +void Wait_Vert_Blank(void) +{ + if (DirectDrawObject == NULL) { + return; + } + if( ScreenWidth!=320 && CanVblankSync){ + HRESULT result = DirectDrawObject->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); + if (result == E_NOTIMPL){ + CanVblankSync = FALSE; + return; + } + Process_DD_Result(result, FALSE); + } +} + + + + + +/*********************************************************************************************** + * Set_Palette -- set a direct draw palette * + * * + * * + * * + * INPUT: ptr to 768 rgb palette bytes * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/11/95 3:33PM ST : Created * + *=============================================================================================*/ +void Set_DD_Palette ( void *palette ) +{ + + /* + ** Trap null ptr + */ + if (!palette) return; + + int j; + int k; + char *palette_get; + + if ( DirectDrawObject && PaletteSurface ){ + + k=0; + + palette_get = (char*)palette; + + for( j=0 ; j<768 ; j+=3 ) + { + PaletteEntries[k].peRed = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peGreen = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peBlue = (unsigned char)((*palette_get++)<<2); + k++; + } + + if ( !FirstPaletteSet ){ + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetPalette","Note", MB_ICONEXCLAMATION|MB_OK); + PaletteSurface->SetPalette( PalettePtr ); + FirstPaletteSet=TRUE; + } + + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetEntries","Note", MB_ICONEXCLAMATION|MB_OK); + PalettePtr->SetEntries( 0 , 0 , 256 , &PaletteEntries[0] ); + } + //MessageBox(MainWindow, "Leaving Set_DD_Palette","Note", MB_ICONEXCLAMATION|MB_OK); + +} + + + + + +/*********************************************************************************************** + * Wait_Blit -- waits for the DirectDraw blitter to become idle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 07-25-95 03:53pm ST : Created * + *=============================================================================================*/ + +void Wait_Blit (void) +{ + HRESULT return_code; + + do { + return_code=PaletteSurface->GetBltStatus (DDGBS_ISBLTDONE); + } while (return_code != DD_OK && return_code != DDERR_SURFACELOST); + +} + + + +/*********************************************************************************************** + * SMC::SurfaceMonitorClass -- constructor for surface monitor class * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:23PM ST : Created * + *=============================================================================================*/ + +SurfaceMonitorClass::SurfaceMonitorClass(void) +{ + for (int i=0 ; iRestore() != DD_OK){ + if (Misc_Focus_Loss_Function){ + Misc_Focus_Loss_Function(); + } + return; + } + } + } + + /* + ** PWG/ST: Now that we know all the surfaces are restored call + ** the function pointer to notify the program that it has + ** happened. This function pointer is used to clear the pages, + ** etc. + */ + if (Misc_Focus_Restore_Function){ + Misc_Focus_Restore_Function(); + } + + SurfacesRestored = TRUE; + + /* + ** Restore the palette + */ + Set_DD_Palette (CurrentPalette); + } +} + + +/*********************************************************************************************** + * SMC::Set_Surface_Focus -- set the InFocus flag to the given state * + * * + * The InFocus flag is used to keep track of whether our application is currently in focus. * + * We dont want to be restoring video surfaces when we are supposed to be running in the * + * background. * + * * + * INPUT: bool in focus * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/6/95 12:21PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Set_Surface_Focus ( BOOL in_focus ) +{ + InFocus=in_focus; +} + + + + +/*********************************************************************************************** + * SMC::Release -- releases all direct draw surfaces * + * * + * Call this at the end of the game before called RestoreDisplayMode * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:23PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Release(void) +{ + /* + ** Call release for each Direct Draw surface + */ + for (int i=0 ; iRelease(); + Surface[i] = 0; + } + } + +} diff --git a/REDALERT/WIN32LIB/DDRAW.H b/REDALERT/WIN32LIB/DDRAW.H new file mode 100644 index 000000000..d1af82f96 --- /dev/null +++ b/REDALERT/WIN32LIB/DDRAW.H @@ -0,0 +1,3117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1994-1996 Microsoft Corporation. All Rights Reserved. + * + * File: ddraw.h + * Content: DirectDraw include file + * + ***************************************************************************/ + +#ifndef __DDRAW_INCLUDED__ +#define __DDRAW_INCLUDED__ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#define CO_E_NOTINITIALIZED 0x800401F0L +#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_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 ); + +#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 IDirectDrawPalette FAR *LPDIRECTDRAWPALETTE; +typedef struct IDirectDrawClipper FAR *LPDIRECTDRAWCLIPPER; + +typedef struct _DDFXROP FAR *LPDDFXROP; +typedef struct _DDSURFACEDESC FAR *LPDDSURFACEDESC; + +/* + * 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 ); + #ifdef WINNT + //This is the user-mode entry stub to the kernel mode procedure. + extern HRESULT NtDirectDrawCreate( GUID FAR *lpGUID, HANDLE *lplpDD, IUnknown FAR *pUnkOuter ); + #endif +#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 + +#ifdef WINNT +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 + 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 + +typedef struct _DDCAPS +{ + 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; + +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 (BD_4,8,16,24,32) + DWORD dwYUVBitCount; // how many bits per pixel (BD_4,8,16,24,32) + DWORD dwZBufferBitDepth; // how many bits for z buffers (BD_8,16,24,32) + DWORD dwAlphaBitDepth; // how many bits for alpha channels (BD_1,2,4,8) + }; + 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 + }; +} 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) +#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) (p)->lpVtbl->SetDisplayMode(p, a, b, c, d) +#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) +#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) +#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) +#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) +#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) +#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 + LONG lPitch; // distance to start of next line (return value only) + 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 + + + +/* + * 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 + + +/* + * All input fields are valid. + */ +#define DDSD_ALL 0x0007f9eel + + +/*============================================================================ + * + * 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 currently has no meaning. + */ +#define DDSCAPS_3D 0x00000001l + +/* + * Indicates that this surface contains alpha information. The pixel + * format must be interrogated to determine whether this surface + * contains only alpha information or alpha information interlaced + * with pixel color data (e.g. RGBA or YUVA). + */ +#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 320x200 or 320x240 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 + + + +/* + * Indicates that memory for the surface is not allocated until the surface + * is loaded (via the Direct3D texture Load() function). + */ +#define DDSCAPS_ALLOCONLOAD 0x04000000l + + + + /**************************************************************************** + * + * 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 + +/**************************************************************************** + * + * 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 + +/*=========================================================================== + * + * + * 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 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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. + */ +#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 ) + + + +/* + * A DC has already been returned for this surface. Only one DC can be + * retrieved per surface. + */ +#define DDERR_DCALREADYCREATED MAKE_DDHRESULT( 620 ) + +/* + * 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 ) + +/* + * 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/REDALERT/WIN32LIB/DEFINES.H b/REDALERT/WIN32LIB/DEFINES.H new file mode 100644 index 000000000..954e91d23 --- /dev/null +++ b/REDALERT/WIN32LIB/DEFINES.H @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/REDALERT/WIN32LIB/DELAY.CPP b/REDALERT/WIN32LIB/DELAY.CPP new file mode 100644 index 000000000..51cf57927 --- /dev/null +++ b/REDALERT/WIN32LIB/DELAY.CPP @@ -0,0 +1,59 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : DELAY.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : 27 March, 1991 [CY] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include + +void Delay(int duration) +{ + unsigned long count; + TimerClass timer(BT_SYSTEM,TRUE); + + while (duration--) { + count = timer.Time() + 1L; + while (count >= (unsigned)timer.Time()) { + ; + } + } + +#if(FALSE) + while (duration--) + Wait_Vert_Blank(VertBlank); +#endif +} + +#if(FALSE) +void Vsync() +{ + Wait_Vert_Blank(VertBlank); +} +#endif + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/DESCMGMT.H b/REDALERT/WIN32LIB/DESCMGMT.H new file mode 100644 index 000000000..f8102558d --- /dev/null +++ b/REDALERT/WIN32LIB/DESCMGMT.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/REDALERT/WIN32LIB/DIFFTB.INC b/REDALERT/WIN32LIB/DIFFTB.INC new file mode 100644 index 000000000..0c345aa55 --- /dev/null +++ b/REDALERT/WIN32LIB/DIFFTB.INC @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/REDALERT/WIN32LIB/DIPTHONG.CPP b/REDALERT/WIN32LIB/DIPTHONG.CPP new file mode 100644 index 000000000..7202f9b21 --- /dev/null +++ b/REDALERT/WIN32LIB/DIPTHONG.CPP @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + char temp; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + temp = *src++; + temp += 127; + *dest++ = temp; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int common, // Common character index. + dipthong; // Dipthong character index. + + unsigned long length=0; // Length of output string + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = (char)KA_EXTEND; + first -= 127; + length++; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char) (common << 3); + first |= (unsigned char)dipthong; + first |= (unsigned char)0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + length++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(length); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ + +#define TXT_GUEST 4567+3 +#define TXT_LOGIN 4567+4 +#define TXT_LOGIN_TO_INTERNET 4567+5 +#define TXT_YOUR_HANDLE 4567+6 +#define TXT_YOUR_PASSWORD 4567+7 +#define TXT_INTERNET_HOST 4567+8 +#define TXT_INTERNET_JOIN 4567+9 +#define TXT_INTERNET_GAME_TYPE 4567+10 +#define TXT_JOIN_INTERNET_GAME 4567+11 +#define TXT_ENTER_IP_ADDRESS 4567+12 +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + +static char InternetTxt[22][40]={ + "Internet H2H", + "Host Internet Game", + "Join Internet Game", + "Guest", + "Login", + "Login to Planet Westwood", + "Planet Westwood Handle", + "Planet Westwood Password", + "Host Game", + "Join Game", + "Choose Type of Internet Game", + "Join Internet Game", + "Address of Host", + "Connecting...", + "Connection Error!", + "Unable to connect to host!", + "Connecting to host...", + "Unable to resolve host address!", + "Unable to accept client connection", + "Unable to connect!", + "Connection lost!", + "Resolving address of host..." +}; + +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + + if (!data || string < 0) return(NULL); + + if (string >= 4567) return (InternetTxt[string-4567]); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/REDALERT/WIN32LIB/DIPTHONG.H b/REDALERT/WIN32LIB/DIPTHONG.H new file mode 100644 index 000000000..c8ab804cc --- /dev/null +++ b/REDALERT/WIN32LIB/DIPTHONG.H @@ -0,0 +1,21 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/REDALERT/WIN32LIB/DPLAY.H b/REDALERT/WIN32LIB/DPLAY.H new file mode 100644 index 000000000..c8e703dfe --- /dev/null +++ b/REDALERT/WIN32LIB/DPLAY.H @@ -0,0 +1,323 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1994-1995 Microsoft Corporation. All Rights Reserved. + * + * File: dplay.h + * Content: DirectPlay include file + * + ***************************************************************************/ + +#ifndef __DPLAY_INCLUDED__ +#define __DPLAY_INCLUDED__ +#ifdef _WIN32 +/* for DECLARE_INTERFACE and HRESULT. */ +#include +#endif + +#define _FACDP 0x877 +#define MAKE_DPHRESULT( code ) MAKE_HRESULT( 1, _FACDP, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(push, 1) + + +/*============================================================================ + * + * DirectPlay Structures + * + * Various structures used to invoke DirectPlay. + * + *==========================================================================*/ + +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectPlay; +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#else +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#endif + +typedef DWORD DPID, FAR *LPDPID; + +typedef struct _DPCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMaxBufferSize; + DWORD dwMaxQueueSize; // Function of DPlay, not SP. + DWORD dwMaxPlayers; + DWORD dwHundredBaud; // 24 is 2400, 96 is 9600, etc. + DWORD dwLatency; +} DPCAPS; + +typedef DPCAPS FAR *LPDPCAPS; + +#define DPLONGNAMELEN 52 +#define DPSHORTNAMELEN 20 +#define DPSESSIONNAMELEN 32 +#define DPPASSWORDLEN 16 +#define DPUSERRESERVED 16 + +typedef struct +{ + DWORD dwSize; + GUID guidSession; // Id for Game. Null is all games. + DWORD dwSession; // session identifier + DWORD dwMaxPlayers; // Maximum players allowed in game. + DWORD dwCurrentPlayers; // Current players in Game. + DWORD dwFlags; // DPOPEN_* flags + char szSessionName[DPSESSIONNAMELEN];// Human readable name for Game + char szUserField[DPUSERRESERVED]; + DWORD dwReserved1; // Reserved for future MS use. + char szPassword[DPPASSWORDLEN]; // Password to be allowed into game. + DWORD dwReserved2; // Reserved for future MS use. + DWORD dwUser1; + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC; +typedef DPSESSIONDESC FAR *LPDPSESSIONDESC; + + +/* + * Create API + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACK)( + LPGUID lpSPGuid, + LPSTR lpFriendlyName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +typedef BOOL (FAR PASCAL * LPDPENUMSESSIONSCALLBACK)( + LPDPSESSIONDESC lpDPSGameDesc, + LPVOID lpContext, + LPDWORD lpdwTimeOut, + DWORD dwFlags); + + + +extern HRESULT WINAPI DirectPlayCreate( LPGUID lpGUID, LPDIRECTPLAY FAR *lplpDP, IUnknown FAR *pUnk); +extern HRESULT WINAPI DirectPlayEnumerate( LPDPENUMDPCALLBACK, LPVOID ); + + +/* Player enumeration callback prototype */ +typedef BOOL (FAR PASCAL *LPDPENUMPLAYERSCALLBACK)( + DPID dpId, + LPSTR lpFriendlyName, + LPSTR lpFormalName, + DWORD dwFlags, + LPVOID lpContext ); + +/* + * IDirectPlay + */ +#undef INTERFACE +#define INTERFACE IDirectPlay +#ifdef _WIN32 +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; +}; +#endif + + + + +/**************************************************************************** + * + * DIRECTPLAY ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ +#define DP_OK 0 +#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_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_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 DPOPEN_OPENSESSION 0x00000001 +#define DPOPEN_CREATESESSION 0x00000002 + +#define DPSEND_GUARANTEE 0x00000001 +#define DPSEND_HIGHPRIORITY 0x00000002 +#define DPSEND_TRYONCE 0x00000004 + +#define DPRECEIVE_ALL 0x00000001 +#define DPRECEIVE_TOPLAYER 0x00000002 +#define DPRECEIVE_FROMPLAYER 0x00000004 +#define DPRECEIVE_PEEK 0x00000008 + +#define DPCAPS_NAMESERVICE 0x00000001 // A name server is supported. +#define DPCAPS_NAMESERVER 0x00000002 // You are the name server. +#define DPCAPS_GUARANTEED 0x00000004 // SP's don't have to implement guarantees. + +#define DPENUMSESSIONS_AVAILABLE 0x00000001 // All games that match password (if given) + // and have openings. +#define DPENUMSESSIONS_ALL 0x00000002 +#define DPENUMSESSIONS_PREVIOUS 0x00000004 + +#define DPENUMPLAYERS_ALL 0x00000000 +#define DPENUMPLAYERS_PREVIOUS 0x00000004 +#define DPENUMPLAYERS_LOCAL 0x00000008 +#define DPENUMPLAYERS_REMOTE 0x00000010 +#define DPENUMPLAYERS_GROUP 0x00000020 +#define DPENUMPLAYERS_SESSION 0x00000080 + +// +// This flag is set on the enumsessions callback when the time out has occured. +// This means that there is no session data for this callback. +// If lpdwTimeOut is set to a non-zero value and the EnumSessionsCallback returns +// TRUE then EnumSessions will continue until the next timeout occurs. +// Timeouts are in milliseconds. + +#define DPESC_TIMEDOUT 0x00000001 + + +// +// System message structures and types. +// +// System messages have a leading 4 byte type code to identify the message. +// an app knows it is a system message because it is addressed 'To' player 0. +// + + +#define DPSYS_ADDPLAYER 0x0003 // DPMSG_ADDPLAYER +#define DPSYS_DELETEPLAYER 0x0005 // DPMSG_DELETEPLAYER + +#define DPSYS_ADDPLAYERTOGROUP 0x0007 // DPMSG_GROUPADD + +#define DPSYS_INVITE 0x000e // DPMSG_INVITE, Net only. + +#define DPSYS_DELETEGROUP 0x0020 // DPMSG_DELETEPLAYER +#define DPSYS_DELETEPLAYERFROMGRP 0x0021 // DPMSG_GROUPDELETE +#define DPSYS_SESSIONLOST 0x0031 + +#define DPSYS_CONNECT 0x484b // DPMSG_GENERIC + + + +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 struct +{ + DWORD dwType; + DPSESSIONDESC dpsDesc; +} DPMSG_INVITE; + + + +typedef struct +{ + DWORD dwType; +} DPMSG_GENERIC; + +#pragma pack(pop) + + +DEFINE_GUID( IID_IDirectPlay, 0x5454e9a0, 0xdb65, 0x11ce, 0x92, 0x1c, 0x00, 0xaa, 0x00, 0x6c, 0x49, 0x72); + + + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/REDALERT/WIN32LIB/DRAWBUFF.H b/REDALERT/WIN32LIB/DRAWBUFF.H new file mode 100644 index 000000000..1b08216ae --- /dev/null +++ b/REDALERT/WIN32LIB/DRAWBUFF.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef DRAWBUFF_H +#define DRAWBUFF_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +extern "C" { + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + long __cdecl Buffer_Size_Of_Region(void *thisptr, int w, int h); + + void __cdecl Buffer_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + int __cdecl Buffer_Get_Pixel(void * thisptr, int x, int y); + void __cdecl Buffer_Clear(void *thisptr, unsigned char color); + long __cdecl Buffer_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + BOOL __cdecl Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + BOOL __cdecl Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + LONG __cdecl Buffer_Print(void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); +} + +extern GraphicViewPortClass *LogicPage; +extern BOOL AllowHardwareBlitFills; +#endif diff --git a/REDALERT/WIN32LIB/DRAWBUFF.INC b/REDALERT/WIN32LIB/DRAWBUFF.INC new file mode 100644 index 000000000..fcc061c09 --- /dev/null +++ b/REDALERT/WIN32LIB/DRAWBUFF.INC @@ -0,0 +1,90 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWBUFF.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Clear :NEAR + +; Externs from BITBLIT.ASM module of the DRAWBUFF library +GLOBAL C Linear_Blit_To_Linear :NEAR + +; Externs from TOBUFF.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the DRAWBUFF library +GLOBAL C Linear_Scale_To_Linear :NEAR + +; Externs from TXTPRNT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Print :NEAR + + +;*-------------------------------------------------------------------------* +;* Define Buffer only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Line:NEAR + +; Externs from FILLQUAD.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Remap :NEAR + +; Externs from STAMP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Stamp :NEAR + +GLOBAL C get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + diff --git a/REDALERT/WIN32LIB/DRAWRECT.CPP b/REDALERT/WIN32LIB/DRAWRECT.CPP new file mode 100644 index 000000000..beaaacd07 --- /dev/null +++ b/REDALERT/WIN32LIB/DRAWRECT.CPP @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Lock(); + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); + Unlock(); +} + diff --git a/REDALERT/WIN32LIB/DSETUP.H b/REDALERT/WIN32LIB/DSETUP.H new file mode 100644 index 000000000..54ea585ea --- /dev/null +++ b/REDALERT/WIN32LIB/DSETUP.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsetup.h + * Content: DirectXSetup, error codes and flags + ***************************************************************************/ + +#ifndef __DSETUP_H__ +#define __DSETUP_H__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#define GUID void +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#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 MAX_INFLINE (16*1024) +#define MAX_DESCRIPTION 256 + +#define DSETUP_DDRAW 0x00000001 /* install DirectDraw */ +#define DSETUP_DSOUND 0x00000002 /* install DirectSound */ +#define DSETUP_DPLAY 0x00000004 /* install DirectPlay */ +#define DSETUP_DDRAWDRV 0x00000008 /* install DirectDraw Drivers */ +#define DSETUP_DSOUNDDRV 0x00000010 /* install DirectSound Drivers */ +#define DSETUP_DPLAYSP 0x00000020 /* install DirectPlay Providers */ +#define DSETUP_DIRECTX DSETUP_DDRAW | DSETUP_DSOUND | DSETUP_DPLAY | DSETUP_DDRAWDRV | DSETUP_DSOUNDDRV | DSETUP_DPLAYSP +#define DSETUP_REINSTALL 0x00000080 /* install DirectX even if existing components have the same version */ + +int WINAPI DirectXSetup( HWND hwnd, LPSTR root_path, DWORD flags ); +int WINAPI DirectXDeviceDriverSetup( HWND hwnd, LPSTR driver_class, LPSTR inf_path, LPSTR driver_path, DWORD flags ); + +typedef int (WINAPI * LPDIRECTXSETUP)( HWND, LPSTR, DWORD ); +typedef int (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)( HWND, LPSTR, LPSTR, LPSTR, DWORD ); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/REDALERT/WIN32LIB/DSOUND.H b/REDALERT/WIN32LIB/DSOUND.H new file mode 100644 index 000000000..5c31e9bec --- /dev/null +++ b/REDALERT/WIN32LIB/DSOUND.H @@ -0,0 +1,380 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + ***************************************************************************/ + +#ifndef __DSOUND_INCLUDED__ +#define __DSOUND_INCLUDED__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#endif + +#define _FACDS 0x878 +#define MAKE_DSHRESULT( code ) MAKE_HRESULT( 1, _FACDS, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +// 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 279afa83-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSound,0x279AFA83,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); +// DirectSoundBuffer 279afa85-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSoundBuffer,0x279AFA85,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); + + + +//==========================================================================; +// +// Structures... +// +//==========================================================================; +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectSound; +struct IDirectSoundBuffer; +#endif + +typedef struct IDirectSound *LPDIRECTSOUND; +typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; +typedef struct IDirectSoundBuffer **LPLPDIRECTSOUNDBUFFER; + + +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 struct _DSBCAPS +{ + + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} DSBCAPS, *LPDSBCAPS; + +typedef struct _DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSBUFFERDESC, *LPDSBUFFERDESC; + + + +typedef LPVOID* LPLPVOID; + + +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID); +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKA)(GUID FAR *, LPSTR, LPSTR, LPVOID); + +extern HRESULT WINAPI DirectSoundCreate(GUID FAR * lpGUID, LPDIRECTSOUND * ppDS, IUnknown FAR *pUnkOuter ); +extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW lpCallback, LPVOID lpContext ); +extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext ); + +#ifdef UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKW +#define DirectSoundEnumerate DirectSoundEnumerateW +#else +#define LPDSENUMCALLBACK LPDSENUMCALLBACKA +#define DirectSoundEnumerate DirectSoundEnumerateA +#endif + +// +// IDirectSound +// +#undef INTERFACE +#define INTERFACE IDirectSound +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSound, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectSound methods ***/ + + STDMETHOD( CreateSoundBuffer)(THIS_ LPDSBUFFERDESC, LPLPDIRECTSOUNDBUFFER, IUnknown FAR *) PURE; + STDMETHOD( GetCaps)(THIS_ LPDSCAPS ) PURE; + STDMETHOD( DuplicateSoundBuffer)(THIS_ LPDIRECTSOUNDBUFFER, LPLPDIRECTSOUNDBUFFER ) PURE; + STDMETHOD( SetCooperativeLevel)(THIS_ HWND, DWORD ) PURE; + STDMETHOD( Compact)(THIS ) PURE; + STDMETHOD( GetSpeakerConfig)(THIS_ LPDWORD ) PURE; + STDMETHOD( SetSpeakerConfig)(THIS_ DWORD ) PURE; + STDMETHOD( Initialize)(THIS_ GUID FAR * ) 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) +#endif + +#endif + +// +// IDirectSoundBuffer +// +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSoundBuffer, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) 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, LPDSBUFFERDESC ) 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_ LPWAVEFORMATEX ) 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) +#endif + +#endif + + + +/* + * 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 DSound 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 ) +/* + * The DSound subsystem couldn't allocate sufficient memory to complete the + * caller's request. + */ +#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 ) +/* + * The Initialize() member on the Direct Sound Object has not been + * called or called successfully before calls to other members. + */ +#define DSERR_UNINITIALIZED MAKE_DSHRESULT( 170 ) + + + + +//==========================================================================; +// +// 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 DSSCL_NORMAL 1 +#define DSSCL_PRIORITY 2 +#define DSSCL_EXCLUSIVE 3 +#define DSSCL_WRITEPRIMARY 4 + + + +#define DSBCAPS_PRIMARYBUFFER 0x00000001 +#define DSBCAPS_STATIC 0x00000002 +#define DSBCAPS_LOCHARDWARE 0x00000004 +#define DSBCAPS_LOCSOFTWARE 0x00000008 +#define DSBCAPS_CTRLFREQUENCY 0x00000020 +#define DSBCAPS_CTRLPAN 0x00000040 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLDEFAULT 0x000000E0 // Pan + volume + frequency. +#define DSBCAPS_CTRLALL 0x000000E0 // All control capabilities +#define DSBCAPS_STICKYFOCUS 0x00004000 + + + + +#define DSSPEAKER_HEADPHONE 1 +#define DSSPEAKER_MONO 2 +#define DSSPEAKER_QUAD 3 +#define DSSPEAKER_STEREO 4 +#define DSSPEAKER_SURROUND 5 + + + + + + +#ifdef __cplusplus +}; +#endif + +#endif /* __DSOUND_INCLUDED__ */ diff --git a/REDALERT/WIN32LIB/DrawMisc.cpp b/REDALERT/WIN32LIB/DrawMisc.cpp new file mode 100644 index 000000000..f26bad2be --- /dev/null +++ b/REDALERT/WIN32LIB/DrawMisc.cpp @@ -0,0 +1,5509 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +** +** +** Misc asm functions from ww lib +** ST - 12/19/2018 1:20PM +** +** +** +** +** +** +** +** +** +** +** +*/ + +#include "gbuffer.h" +#include "MISC.H" +#include "WSA.H" + +IconCacheClass::IconCacheClass (void) +{ + IsCached =FALSE; + SurfaceLost =FALSE; + DrawFrequency =0; + CacheSurface =NULL; + IconSource =NULL; +} + +IconCacheClass::~IconCacheClass (void) +{ +} + +IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern "C"{ +IconSetType IconSetList[MAX_ICON_SETS]; +short IconCacheLookup[MAX_LOOKUP_ENTRIES]; +} + +int CachedIconsDrawn=0; //Counter of number of cache hits +int UnCachedIconsDrawn=0; //Counter of number of cache misses +BOOL CacheMemoryExhausted; //Flag set if we have run out of video RAM + + +void Invalidate_Cached_Icons (void) {} +void Restore_Cached_Icons (void) {} +void Register_Icon_Set (void *icon_data , BOOL pre_cache) {}; + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void __cdecl Clear_Icon_Pointers (void) {}; +extern "C" void __cdecl Cache_Copy_Icon (void const *icon_ptr ,void * , int) {}; +extern "C" int __cdecl Is_Icon_Cached (void const *icon_data , int icon) {return -1;}; +extern "C" int __cdecl Get_Icon_Index (void *icon_ptr) {return 0;}; +extern "C" int __cdecl Get_Free_Index (void) {return 0;}; +extern "C" BOOL __cdecl Cache_New_Icon (int icon_index, void *icon_ptr) {return -1;}; +extern "C" int __cdecl Get_Free_Cache_Slot(void) {return -1;} + +void IconCacheClass::Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height) {} + + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +extern "C" void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx) +{ +} + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +void __cdecl Buffer_Draw_Line(void *this_object, int sx, int sy, int dx, int dy, unsigned char color) +{ + unsigned int clip_min_x; + unsigned int clip_max_x; + unsigned int clip_min_y; + unsigned int clip_max_y; + unsigned int clip_var; + unsigned int accum; + unsigned int bpr; + + static int _one_time_init = 0; + + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + static void *_clip_table [4*4] = {0}; + + unsigned int int_color = color; + unsigned int x1_pixel = (unsigned int) sx; + unsigned int y1_pixel = (unsigned int) sy; + unsigned int x2_pixel = (unsigned int) dx; + unsigned int y2_pixel = (unsigned int) dy; + + __asm { + mov eax,_one_time_init + and eax,eax + jnz init_done + + call do_init + +init_done: + + //;*================================================================== + //;* Take care of find the clip minimum and maximums + //;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[ebx]GraphicViewPortClass.Width + mov [clip_max_x],eax + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + mov [bpr],eax + mov eax,[ebx]GraphicViewPortClass.Height + mov [clip_max_y],eax + + //;*================================================================== + //;* Adjust max pixels as they are tested inclusively. + //;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + //;*================================================================== + //;* Set the registers with the data for drawing the line + //;*================================================================== + mov eax,[x1_pixel] //; eax = start x pixel position + mov ebx,[y1_pixel] //; ebx = start y pixel position + mov ecx,[x2_pixel] //; ecx = dest x pixel position + mov edx,[y2_pixel] //; edx = dest y pixel position + + //;*================================================================== + //;* This is the section that "pushes" the line into bounds. + //;* I have marked the section with PORTABLE start and end to signify + //;* how much of this routine is 100% portable between graphics modes. + //;* It was just as easy to have variables as it would be for constants + //;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + //;* to clip the line (default is the screen) + //;* PORTABLE start + //;*================================================================== + + cmp eax,[clip_min_x] + jl short clip_it + cmp eax,[clip_max_x] + jg short clip_it + cmp ebx,[clip_min_y] + jl short clip_it + cmp ebx,[clip_max_y] + jg short clip_it + cmp ecx,[clip_min_x] + jl short clip_it + cmp ecx,[clip_max_x] + jg short clip_it + cmp edx,[clip_min_y] + jl short clip_it + cmp edx,[clip_max_y] + jle short on_screen + + //;*================================================================== + //;* Takes care off clipping the line. + //;*================================================================== + clip_it: + call set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call set_bits + mov [clip_var],edi + or [clip_var],esi + jz short on_screen + test edi,esi + jne short off_screen + shl esi,2 + //call [clip_tbl+esi] + call [_clip_table+esi] + jc clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + //call [clip_tbl+edi] + call [_clip_table+edi] + jmp clip_it + + on_screen: + jmp draw_it + + off_screen: + jmp and_out + + //;*================================================================== + //;* Jump table for clipping conditions + //;*================================================================== + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + nada: + clc + ret + + a_up: + mov esi,[clip_min_y] + call clip_vert + stc + ret + + a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call clip_vert + neg ebx + neg edx + stc + ret + + //;*================================================================== + //;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + //;*================================================================== + clip_vert: + push edx + push eax + mov [clip_var],edx //; clip_var = yb + sub [clip_var],ebx //; clip_var = (yb-ya) + neg eax //; eax=-xa + add eax,ecx //; (ebx-xa) + mov edx,esi //; edx=miny + sub edx,ebx //; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + ret + + a_lft: + mov esi,[clip_min_x] + call clip_horiz + stc + ret + + a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call clip_horiz + neg eax + neg ecx + stc + ret + + //;*================================================================== + //;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + //;*================================================================== + clip_horiz: + push edx + mov [clip_var],ecx //; clip_var = xb + sub [clip_var],eax //; clip_var = (xb-xa) + sub edx,ebx //; edx = (yb-ya) + neg eax //; eax = -xa + add eax,esi //; eax = (minx-xa) + imul edx //; eax = (minx-xa)(yb-ya) + idiv [clip_var] //; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax //; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + ret + + //;*================================================================== + //;* Sets the condition bits + //;*================================================================== + set_bits: + xor esi,esi + cmp ebx,[clip_min_y] //; if y >= top its not up + jge short a_not_up + or esi,1 + + a_not_up: + cmp ebx,[clip_max_y] //; if y <= bottom its not down + jle short a_not_down + or esi,2 + + a_not_down: + cmp eax,[clip_min_x] //; if x >= left its not left + jge short a_not_left + or esi,4 + + a_not_left: + cmp eax,[clip_max_x] //; if x <= right its not right + jle short a_not_right + or esi,8 + + a_not_right: + ret + + //;*================================================================== + //;* Draw the line to the screen. + //;* PORTABLE end + //;*================================================================== + draw_it: + sub edx,ebx //; see if line is being draw down + jnz short not_hline //; if not then its not a hline + jmp short hline //; do special case h line + + not_hline: + jg short down //; if so there is no need to rev it + neg edx //; negate for actual pixel length + xchg eax,ecx //; swap x's to rev line draw + sub ebx,edx //; get old edx + + down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + + mov esi,1 //; assume a right mover + sub ecx,eax //; see if line is right + jnz short not_vline //; see if its a vertical line + jmp vline + + not_vline: + jg short right //; if so, the difference = length + + //left: + neg ecx //; else negate for actual pixel length + neg esi //; negate counter to move left + + right: + cmp ecx,edx //; is it a horiz or vert line + jge short horiz //; if ecx > edx then |x|>|y| or horiz + + //vert: + xchg ecx,edx //; make ecx greater and edx lesser + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //vert_loop: + add ebx,eax + mov eax,[int_color] + + v_midloop: + mov [ebx],al + dec ecx + jl and_out + add ebx,[bpr] + sub [accum],edx //; sub the lesser + jge v_midloop //; any line could be new + add [accum],edi //; add greater for new accum + add ebx,esi //; next pixel over + jmp v_midloop + + horiz: + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //horiz_loop: + add ebx,eax + mov eax,[int_color] + + h_midloop: + mov [ebx],al + dec ecx //; dec counter + jl and_out //; end of line + add ebx,esi + sub [accum],edx //; sub the lesser + jge h_midloop + add [accum],edi //; add greater for new accum + add ebx,[bpr] //; goto next line + jmp h_midloop + + //;*================================================================== + //;* Special case routine for horizontal line draws + //;*================================================================== + hline: + cmp eax,ecx //; make eax < ecx + jl short hl_ac + xchg eax,ecx + + hl_ac: + sub ecx,eax //; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg big_line + mov al,[color] + rep stosb //; write as many words as possible + jmp short and_out //; get outt + + + big_line: + mov al,[color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + + aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp and_out + + + //;*================================================================== + //;* a special case routine for vertical line draws + //;*================================================================== + vline: + mov ecx,edx //; get length of line to draw + inc ecx + add ebx,eax + mov eax,[int_color] + + vl_loop: + mov [ebx],al //; store bit + add ebx,[bpr] + dec ecx + jnz vl_loop + jmp and_out + + +do_init: + mov edi, offset _clip_table + + lea esi, nada + mov [edi], esi + mov [edi+12], esi + lea esi, a_up + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + + add edi, 16 + + lea esi, a_lft + mov [edi], esi + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, a_rgt + mov [edi], esi + mov [edi+8], esi + lea esi, a_up + mov [edi+4], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, nada + mov [edi], esi + mov [edi+4], esi + mov [edi+8], esi + mov [edi+12], esi + + mov [_one_time_init], 1 + ret + + and_out: + } +} + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg ??big_line + mov al,[byte color] + rep stosb ; write as many words as possible + jmp short ??out ; get outt + + +??big_line: + mov al,[byte color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + +??aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp ??out + + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP Buffer_Draw_Line + + +*/ + + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* +*/ + +/* +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +*/ +#define OPTIMAL_BYTE_COPY 14 + + +void __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color) +{ +/* + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a member function + ARG x1_pixel:WORD + ARG y1_pixel:WORD + ARG x2_pixel:WORD + ARG y2_pixel:WORD + ARG color:BYTE ; what color should we clear to +*/ + + void *this_object = thisptr; + int x1_pixel = sx; + int y1_pixel = sy; + int x2_pixel = dx; + int y2_pixel = dy; + +/* + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL VPwidth:DWORD ; the width of the viewport + LOCAL VPheight:DWORD ; the height of the viewport + LOCAL VPxadd:DWORD ; the additional x offset of viewport + LOCAL VPbpr:DWORD ; the number of bytes per row of viewport +*/ + + int VPwidth; + int VPheight; + int VPxadd; + int VPbpr; + + int local_ebp; // Can't use ebp + + __asm { + + ;*=================================================================== + ;* save off the viewport characteristics on the stack + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + mov eax,[ebx]GraphicViewPortClass.Width ; get width from viewport + mov ecx,[ebx]GraphicViewPortClass.Height ; get height from viewport + mov edx,[ebx]GraphicViewPortClass.XAdd ; get xadd from viewport + add edx,[ebx]GraphicViewPortClass.Pitch ; extra pitch of direct draw surface + mov [VPwidth],eax ; store the width of locally + mov [VPheight],ecx + mov [VPxadd],edx + add eax,edx + mov [VPbpr],eax + + ;*=================================================================== + ;* move the important parameters into local registers + ;*=================================================================== + mov eax,[x1_pixel] + mov ebx,[y1_pixel] + mov ecx,[x2_pixel] + mov edx,[y2_pixel] + + ;*=================================================================== + ;* Convert the x2 and y2 pixel to a width and height + ;*=================================================================== + cmp eax,ecx + jl no_swap_x + xchg eax,ecx + + no_swap_x: + sub ecx,eax + cmp ebx,edx + jl no_swap_y + xchg ebx,edx + no_swap_y: + sub edx,ebx + inc ecx + inc edx + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + cmp eax, [VPwidth] ; compare with the max + jge done ; starts off screen, then later + jb short sx_done ; if it's not negative, it's ok + + ;------ Clip source X to left edge of screen. + add ecx, eax ; Reduce width (add in negative src X). + xor eax, eax ; Clip to left of screen. + sx_done: + + ;*=================================================================== + ;* Bounds check source Y. + ;*=================================================================== + cmp ebx, [VPheight] ; compare with the max + jge done ; starts off screen, then later + jb short sy_done ; if it's not negative, it's ok + + ;------ Clip source Y to top edge of screen. + add edx, ebx ; Reduce height (add in negative src Y). + xor ebx, ebx ; Clip to top of screen. + + sy_done: + ;*=================================================================== + ;* Bounds check width versus width of source and dest view ports + ;*=================================================================== + push ebx ; save off ebx for later use + mov ebx,[VPwidth] ; get the source width + sub ebx, eax ; Maximum allowed pixel width (given coordinates). + sub ebx, ecx ; Pixel width undershoot. + jns short width_ok ; if not signed no adjustment necessary + add ecx, ebx ; Reduce width to screen limits. + + width_ok: + pop ebx ; restore ebx to old value + + ;*=================================================================== + ;* Bounds check height versus height of source view port + ;*=================================================================== + push eax ; save of eax for later use + mov eax, [VPheight] ; get the source height + sub eax, ebx ; Maximum allowed pixel height (given coordinates). + sub eax, edx ; Pixel height undershoot. + jns short height_ok ; if not signed no adjustment necessary + add edx, eax ; Reduce height to screen limits. + height_ok: + pop eax ; restore eax to old value + + ;*=================================================================== + ;* Perform the last minute checks on the width and height + ;*=================================================================== + or ecx,ecx + jz done + + or edx,edx + jz done + + cmp ecx,[VPwidth] + ja done + cmp edx,[VPheight] + ja done + + ;*=================================================================== + ;* Get the offset into the virtual viewport. + ;*=================================================================== + xchg edi,eax ; save off the contents of eax + xchg esi,edx ; and edx for size test + mov eax,ebx ; move the y pixel into eax + mul [VPbpr] ; multiply by bytes per row + add edi,eax ; add the result into the x position + mov ebx,[this_object] + add edi,[ebx]GraphicViewPortClass.Offset + + mov edx,esi ; restore edx back to real value + mov eax,ecx ; store total width in ecx + sub eax,[VPwidth] ; modify xadd value to include clipped + sub [VPxadd],eax ; width bytes (subtract a negative number) + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ebx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,bx + + ;*=================================================================== + ; If there is no row offset then adjust the width to be the size of + ; the entire viewport and adjust the height to be 1 + ;*=================================================================== + mov esi,[VPxadd] + or esi,esi ; set the flags for esi + jnz row_by_row_aligned ; and act on them + + xchg eax,ecx ; switch bit pattern and width + mul edx ; multiply by edx to get size + xchg eax,ecx ; switch size and bit pattern + mov edx,1 ; only 1 line off view port size to do + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + row_by_row_aligned: + mov [local_ebp],ecx ; width saved in ebp + cmp ecx,OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl row_by_row ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + mov ebx,edi ; get output position + and ebx,3 ; is there a remainder? + jz aligned_loop ; if not we are aligned + xor ebx,3 ; find number of align bytes + inc ebx ; this number is off by one + sub [local_ebp],ebx ; subtract from width + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== + aligned_loop: + mov ecx,ebx ; get number of bytes to align + rep stosb ; and move them over + mov ecx,[local_ebp] ; get number of aligned bytes + shr ecx,2 ; convert to DWORDS + rep stosd ; and move them over + mov ecx,[local_ebp] ; get number of aligned bytes + and ecx,3 ; find the remainder + rep stosb ; and move it over + add edi,esi ; fix the line offset + dec edx ; decrement the height + jnz aligned_loop ; if more to do than do it + jmp done ; we are all done + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== + row_by_row: + mov ecx,[local_ebp] ; get total width in bytes + rep stosb ; store the width + add edi,esi ; handle the xadd + dec edx ; decrement the height + jnz row_by_row ; if any left then next line + done: + } +} + + + + +/* +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* +*/ +void __cdecl Buffer_Clear(void *this_object, unsigned char color) +{ + unsigned int local_color = color; + + __asm { + + cld ; always go forward + + mov ebx,[this_object] ; get a pointer to viewport + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov edx,[ebx]GraphicViewPortClass.Height ; get height from viewport + mov esi,[ebx]GraphicViewPortClass.Width ; get width from viewport + //push [dword (GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + push [ebx]GraphicViewPortClass.Pitch + + mov ebx,[ebx]GraphicViewPortClass.XAdd ; esi = add for each line + add ebx,[esp] ; Yes, I know its nasty but + add esp,4 ; it works! + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl byte_by_byte ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + push ebx + + dword_aligned_loop: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz dword_aligned_loop ; if more to do than do it + pop eax + jmp done + //ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== + byte_by_byte: + mov ecx,esi ; get total width in bytes + rep stosb ; store the width + add edi,ebx ; handle the xadd + dec edx ; decrement the height + jnz byte_by_byte ; if any left then next line + done: + } +} + + + + + + + + + + + + + + +BOOL __cdecl Linear_Blit_To_Linear( void *this_object, void * dest, int x_pixel, int y_pixel, int dest_x0, int dest_y0, int pixel_width, int pixel_height, BOOL trans) +{ +/* + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword +*/ + + int x1_pixel; + int y1_pixel; + int dest_x1; + int dest_y1; + int scr_adjust_width; + int dest_adjust_width; + int source_area; + int dest_area; + + __asm { + + ;This Clipping algorithm is a derivation of the very well known + ;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency + ;it is probably the most commontly implemented algorithm both in software + ;and hardware for clipping lines, rectangles, and convex polygons against + ;a rectagular clipping window. For reference see + ;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes + ; pages 113 to 177". + ; Briefly consist in computing the Sutherland code for both end point of + ; the rectangle to find out if the rectangle is: + ; - trivially accepted (no further clipping test, display rectangle) + ; - trivially rejected (return with no action) + ; - retangle must be iteratively clipped again edges of the clipping window + ; and the remaining retangle is display. + + ; Clip Source Rectangle against source Window boundaries. + mov esi,[this_object] ; get ptr to src + xor ecx,ecx ; Set sutherland code to zero + xor edx,edx ; Set sutherland code to zero + + ; compute the difference in the X axis and get the bit signs into ecx , edx + mov edi,[esi]GraphicViewPortClass.Width ; get width into register + mov ebx,[x_pixel] ; Get first end point x_pixel into register + mov eax,[x_pixel] ; Get second end point x_pixel into register + add ebx,[pixel_width] ; second point x1_pixel = x + width + shld ecx, eax,1 ; the sign bit of x_pixel is sutherland code0 bit4 + mov [x1_pixel],ebx ; save second for future use + inc edi ; move the right edge by one unit + shld edx,ebx,1 ; the sign bit of x1_pixel is sutherland code0 bit4 + sub eax,edi ; compute the difference x0_pixel - width + sub ebx,edi ; compute the difference x1_pixel - width + shld ecx,eax,1 ; the sign bit of the difference is sutherland code0 bit3 + shld edx,ebx,1 ; the sign bit of the difference is sutherland code0 bit3 + + ; the following code is just a repeticion of the above code + ; in the Y axis. + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov ebx,[y_pixel] + mov eax,[y_pixel] + add ebx,[pixel_height] + shld ecx,eax,1 + mov [y1_pixel ],ebx + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + ; Here we have the to Sutherland code into cl and dl + xor cl,5 ; bit 2 and 0 are complented, reverse then + xor dl,5 ; bit 2 and 0 are complented, reverse then + mov al,cl ; save code1 in case we have to clip iteratively + test dl,cl ; if any bit in code0 and its counter bit + jnz real_out ; in code1 is set then the rectangle in outside + or al,dl ; if all bit of code0 the counter bit in + jz clip_against_dest ; in code1 is set to zero, then all + ; end points of the rectangle are + ; inside the clipping window + + ; if we are here the polygon have to be clip iteratively + test cl,1000b ; if bit 4 in code0 is set then + jz scr_left_ok ; x_pixel is smaller than zero + mov [x_pixel],0 ; set x_pixel to cero. + + scr_left_ok: + test cl,0010b ; if bit 2 in code0 is set then + jz scr_bottom_ok ; y_pixel is smaller than zero + mov [ y_pixel ],0 ; set y_pixel to cero. + + scr_bottom_ok: + test dl,0100b ; if bit 3 in code1 is set then + jz scr_right_ok ; x1_pixel is greater than the width + mov eax,[esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ],eax ; set x1_pixel to width. + scr_right_ok: + test dl,0001b ; if bit 0 in code1 is set then + jz clip_against_dest ; y1_pixel is greater than the width + mov eax,[esi]GraphicViewPortClass.Height ; get height into register + mov [ y1_pixel ],eax ; set y1_pixel to height. + + ; Clip Source Rectangle against destination Window boundaries. + clip_against_dest: + + ; build the destination rectangle before clipping + ; dest_x1 = dest_x0 + ( x1_pixel - x_pixel ) + ; dest_y1 = dest_y0 + ( y1_pixel - y_pixel ) + mov eax,[dest_x0] ; get dest_x0 into eax + mov ebx,[dest_y0] ; get dest_y0 into ebx + sub eax,[x_pixel] ; subtract x_pixel from eax + sub ebx,[y_pixel] ; subtract y_pixel from ebx + add eax,[x1_pixel] ; add x1_pixel to eax + add ebx,[y1_pixel] ; add y1_pixel to ebx + mov [dest_x1],eax ; save eax into dest_x1 + mov [dest_y1],ebx ; save eax into dest_y1 + + + ; The followin code is a repeticion of the Sutherland clipping + ; descrived above. + mov esi,[dest] ; get ptr to src + xor ecx,ecx + xor edx,edx + mov edi,[esi]GraphicViewPortClass.Width ; get width into register + mov eax,[dest_x0] + mov ebx,[dest_x1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax,[dest_y0] + mov ebx,[dest_y1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + xor cl,5 + xor dl,5 + mov al,cl + test dl,cl + jnz real_out + or al,dl + jz do_blit + + test cl,1000b + jz dest_left_ok + mov eax,[ dest_x0 ] + mov [ dest_x0 ],0 + sub [ x_pixel ],eax + + dest_left_ok: + test cl,0010b + jz dest_bottom_ok + mov eax,[ dest_y0 ] + mov [ dest_y0 ],0 + sub [ y_pixel ],eax + + + dest_bottom_ok: + test dl,0100b + jz dest_right_ok + mov ebx,[esi]GraphicViewPortClass.Width ; get width into register + mov eax,[ dest_x1 ] + mov [ dest_x1 ],ebx + sub eax,ebx + sub [ x1_pixel ],eax + + dest_right_ok: + test dl,0001b + jz do_blit + mov ebx,[esi]GraphicViewPortClass.Height ; get width into register + mov eax,[ dest_y1 ] + mov [ dest_y1 ],ebx + sub eax,ebx + sub [ y1_pixel ],eax + + + ; Here is where we do the actual blit + do_blit: + cld + mov ebx,[this_object] + mov esi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.Pitch + mov ecx,eax + mul [y_pixel] + add esi,[x_pixel] + mov [source_area],ecx + add esi,eax + + add ecx,[x_pixel ] + sub ecx,[x1_pixel ] + mov [scr_adjust_width ],ecx + + mov ebx,[dest] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.Pitch + mov ecx,eax + mul [ dest_y0 ] + add edi,[ dest_x0 ] + mov [ dest_area ],ecx + add edi,eax + + mov eax,[ dest_x1 ] + sub eax,[ dest_x0 ] + jle real_out + sub ecx,eax + mov [ dest_adjust_width ],ecx + + mov edx,[ dest_y1 ] + sub edx,[ dest_y0 ] + jle real_out + + cmp esi,edi + jz real_out + jl backupward_blit + + ; ******************************************************************** + ; Forward bitblit + + test [ trans ],1 + jnz forward_Blit_trans + + + ; the inner loop is so efficient that + ; the optimal consept no longer apply because + ; the optimal byte have to by a number greather than 9 bytes + cmp eax,10 + jl forward_loop_bytes + + forward_loop_dword: + mov ecx,edi + mov ebx,eax + neg ecx + and ecx,3 + sub ebx,ecx + rep movsb + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,3 + rep movsb + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_dword + jmp real_out //ret + + forward_loop_bytes: + mov ecx,eax + rep movsb + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_bytes + jmp real_out + + forward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + + forward_loop_trans: + mov ecx,eax + jmp [ y1_pixel ] + forward_trans_line: + //REPT 32 + //local transp_pixel + //No REPT in msvc inline assembly. + // Save ECX and use as counter instead. ST - 12/19/2018 5:41PM + push ecx + mov ecx, 32 + + rept_loop: + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + inc esi + inc edi + + dec ecx //ST - 12/19/2018 5:44PM + jnz rept_loop //ST - 12/19/2018 5:44PM + + pop ecx //ST - 12/19/2018 5:44PM + + //ENDM + transp_reference: + dec ecx + jge forward_trans_line + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_trans + jmp real_out //ret + + + ; ************************************************************************ + ; backward bitblit + + backupward_blit: + + mov ebx,[ source_area ] + dec edx + add esi,eax + imul ebx,edx + std + lea esi,[ esi + ebx - 1 ] + + mov ebx,[ dest_area ] + add edi,eax + imul ebx,edx + lea edi,[ edi + ebx - 1] + + test [ trans ],1 + jnz backward_Blit_trans + + cmp eax,15 + jl backward_loop_bytes + + backward_loop_dword: + push edi + push esi + lea ecx,[edi+1] + mov ebx,eax + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_dword + cld + jmp real_out //ret + + backward_loop_bytes: + push edi + mov ecx,eax ; remove that from the total size to be copied later. + push esi + rep movsb ; do the copy. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_bytes + cld + jmp real_out //ret + + backward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ back_transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + + backward_loop_trans: + mov ecx,eax + push edi + push esi + jmp [ y1_pixel ] + backward_trans_line: + //REPT 32 + //local transp_pixel2 + //No REPT in msvc inline assembly. + // Save ECX and use as counter instead. ST - 12/19/2018 5:41PM + push ecx + mov ecx, 32 + rept_loop2: + mov bl,[ esi ] + test bl,bl + jz transp_pixel2 + mov [ edi ],bl + transp_pixel2: + dec esi + dec edi + + dec ecx //ST - 12/19/2018 5:44PM + jnz rept_loop2 //ST - 12/19/2018 5:44PM + + pop ecx //ST - 12/19/2018 5:44PM + + //ENDM + + back_transp_reference: + dec ecx + jge backward_trans_line + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_trans + cld + //ret + + real_out: + } +} + + + + + + + + + + + + +/* +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +// Ran out of registers so had to use ebp. ST - 12/19/2018 6:22PM +#pragma warning (push) +#pragma warning (disable : 4731) + +BOOL __cdecl Linear_Scale_To_Linear(void *this_object, void *dest, int src_x, int src_y, int dst_x, int dst_y, int src_width, int src_height, int dst_width, int dst_height, BOOL trans, char *remap) +{ +/* + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword +*/ + + int src_x0; + int src_y0; + int src_x1; + int src_y1; + + int dst_x0; + int dst_y0; + int dst_x1; + int dst_y1; + + int src_win_width; + int dst_win_width; + int dy_intr; + int dy_frac; + int dy_acc; + int dx_frac; + + int counter_x; + int counter_y; + int remap_counter; + int entry; + + + __asm { + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je all_done + cmp [dst_height],0 + je all_done + cmp [src_width],0 + je all_done + cmp [src_height],0 + je all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + + ; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz all_done + or al , dl + jz clip_against_dest + mov bl , dl + test cl , 1000b + jz src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + + src_left_ok: + test cl , 0010b + jz src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + + src_bottom_ok: + test bl , 0100b + jz src_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + + src_right_ok: + test bl , 0001b + jz clip_against_dest + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + + ; Clip destination Rectangle against source Window boundaries. + clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz all_done + or al , dl + jz do_scaling + mov bl , dl + test cl , 1000b + jz dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + + dst_left_ok: + test cl , 0010b + jz dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + + dst_bottom_ok: + test bl , 0100b + jz dst_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + + dst_right_ok: + test bl , 0001b + jz do_scaling + + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + + do_scaling: + + cld + mov ebx , [ this_object ] + mov esi , [ebx]GraphicViewPortClass. Offset + mov eax , [ebx]GraphicViewPortClass. XAdd + add eax , [ebx]GraphicViewPortClass. Width + add eax , [ebx]GraphicViewPortClass. Pitch + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ebx]GraphicViewPortClass. Offset + mov eax , [ebx]GraphicViewPortClass. XAdd + add eax , [ebx]GraphicViewPortClass. Width + add eax , [ebx]GraphicViewPortClass. Pitch + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jle all_done + sub ebx , [ dst_x0 ] + jle all_done + + mov [ counter_y ] , ecx + + cmp [ trans ] , 0 + jnz transparency + + cmp [ remap ] , 0 + jnz normal_remap + + ; ************************************************************************* + ; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + outter_loop: + push esi + push edi + xor ecx , ecx + mov ebx , [ counter_x ] + jmp [ entry ] + inner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + push ebx //ST - 12/19/2018 6:11PM + mov ebx,32 //ST - 12/19/2018 6:11PM +rept_loop: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + + dec ebx //ST - 12/19/2018 6:11PM + jnz rept_loop //ST - 12/19/2018 6:11PM + pop ebx //ST - 12/19/2018 6:11PM + //ENDM + ref_point: + dec ebx + jge inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz outter_loop + jmp all_done //ret + + + ; ************************************************************************* + ; normal scale with remap + + normal_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ remapref_point + ecx ] + mov [ entry ] , ecx + + remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + remapinner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + + //ENDM + remapref_point: + dec [ remap_counter ] + jge remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz remapoutter_loop + jmp all_done //ret + + + ;**************************************************************************** + ; scale with trnsparency + + transparency: + cmp [ remap ] , 0 + jnz trans_remap + + ; ************************************************************************* + ; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ trans_ref_point + ecx ] + mov [ entry ] , ecx + + trans_outter_loop: + xor ecx , ecx + push esi + push edi + mov ebx , [ counter_x ] + jmp [ entry ] + trans_inner_loop: + + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + push ebx //ST - 12/19/2018 6:11PM + mov ebx,32 //ST - 12/19/2018 6:11PM +rept_loop2: + + mov cl , [ esi ] + test cl , cl + jz trans_pixel + mov [ edi ] , cl + trans_pixel: + add ecx , eax + adc esi , edx + inc edi + + dec ebx //ST - 12/19/2018 6:11PM + jnz rept_loop2 //ST - 12/19/2018 6:11PM + pop ebx //ST - 12/19/2018 6:11PM + + //ENDM + trans_ref_point: + dec ebx + jge trans_inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle trans_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + trans_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz trans_outter_loop + jmp all_done //ret + + + ; ************************************************************************* + ; normal scale with remap + + trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ trans_remapref_point + ecx ] + mov [ entry ] , ecx + + trans_remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + + + trans_remapinner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + // Run out of registers so use ebp + push ebp //ST - 12/19/2018 6:11PM + mov ebp,32 //ST - 12/19/2018 6:11PM +rept_loop3: + mov bl , [ esi ] + test bl , bl + jz trans_pixel2 + mov cl , [ eax + ebx ] + mov [ edi ] , cl + trans_pixel2: + add ecx , [ dx_frac ] + adc esi , edx + inc edi + + dec ebp //ST - 12/19/2018 6:11PM + jnz rept_loop3 //ST - 12/19/2018 6:11PM + pop ebp //ST - 12/19/2018 6:11PM + + //ENDM + + trans_remapref_point: + dec [ remap_counter ] + jge trans_remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz trans_remapoutter_loop + //ret + + + all_done: + } +} + + +#pragma warning (pop) + + + + + + + + + + + + + + + + + + + + +unsigned int LastIconset = 0; +unsigned int StampPtr = 0; // DD 0 ; Pointer to icon data. + +unsigned int IsTrans = 0; // DD 0 ; Pointer to transparent icon flag table. + +unsigned int MapPtr = 0; // DD 0 ; Pointer to icon map. +unsigned int IconWidth = 0; // DD 0 ; Width of icon in pixels. +unsigned int IconHeight = 0; // DD 0 ; Height of icon in pixels. +unsigned int IconSize = 0; // DD 0 ; Number of bytes for each icon data. +unsigned int IconCount = 0; // DD 0 ; Number of icons in the set. + + + +#if (0) +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + +GLOBAL C Buffer_Draw_Stamp:near +GLOBAL C Buffer_Draw_Stamp_Clip:near + +; 256 color icon system. +#endif + + +/* +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* +*/ +extern "C" void __cdecl Init_Stamps(unsigned int icondata) +{ + + __asm { + pushad // ST - 12/20/2018 10:30AM + + ; Verify legality of parameter. + cmp [icondata],0 + je short fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[edi]IControl_Type.Count + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[edi]IControl_Type.Width + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[edi]IControl_Type.Height + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[edi]IControl_Type.Map + add eax,edi + mov [MapPtr],eax + +//nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[edi]IControl_Type.Icons + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[edi]IControl_Type.TransFlag + mov [IsTrans],eax + +fini: + popad // ST - 12/20/2018 10:30AM + + } +} + + +/* +;*********************************************************** + +;*********************************************************** +; DRAW_STAMP +; +; VOID cdecl Buffer_Draw_Stamp(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* +*/ + +void __cdecl Buffer_Draw_Stamp(void const *this_object, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + unsigned int modulo = 0; + unsigned int iwidth = 0; + unsigned char doremap = 0; + + +/* + PROC Buffer_Draw_Stamp C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL doremap:BYTE ; Should remapping occur? +*/ + + __asm { + + pushad + cmp [icondata],0 + je proc_out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short noreset + push eax + call Init_Stamps + pop eax // Clean up stack. ST - 12/20/2018 10:42AM +noreset: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short notmap + mov edi,[MapPtr] + mov bl,[edi+ebx] +notmap: + cmp ebx,[IconCount] + jae proc_out + mov [icon],ebx ; Updated icon number. + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[IconWidth] + mov [modulo],eax + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short istranscheck + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + ;;; mov edx,[remap] + mov ebx,[remap] + xor eax,eax +xrowloop: + push ecx + mov ecx,[iwidth] + +xcolumnloop: + lodsb + ;;; mov ebx,edx + ;;; add ebx,eax + ;;; mov al,[ebx] ; New real color to draw. + xlatb + or al,al + jz short xskip1 ; Transparency skip check. + mov [edi],al +xskip1: + inc edi + loop xcolumnloop + + pop ecx + add edi,[modulo] + loop xrowloop + jmp short proc_out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +istranscheck: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short rowloop + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + shr ebx,2 + mov edx,[modulo] + mov eax,[iwidth] + shr eax,2 +loop1: + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + dec ebx + jnz loop1 + jmp short proc_out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +rowloop: + push ecx + mov ecx,[iwidth] + +columnloop: + lodsb + or al,al + jz short skip1 ; Transparency check. + mov [edi],al +skip1: + inc edi + loop columnloop + + pop ecx + add edi,[modulo] + loop rowloop + + ; Cleanup and exit icon drawing routine. +proc_out: + popad + //ret + } +} + + + + +/* +;*********************************************************** +; DRAW_STAMP_CLIP +; +; VOID cdecl MCGA_Draw_Stamp_Clip(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap, LONG min_x, LONG min_y, LONG max_x, LONG max_y); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* +*/ +void __cdecl Buffer_Draw_Stamp_Clip(void const *this_object, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int min_x, int min_y, int max_x, int max_y) +{ + + + unsigned int modulo = 0; + unsigned int iwidth = 0; + unsigned int skip = 0; + unsigned char doremap = 0; + + +/* + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + ARG min_x:DWORD ; Clipping rectangle boundary + ARG min_y:DWORD ; Clipping rectangle boundary + ARG max_x:DWORD ; Clipping rectangle boundary + ARG max_y:DWORD ; Clipping rectangle boundary + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL skip:DWORD ; amount to skip per row of icon data + LOCAL doremap:BYTE ; Should remapping occur? +*/ + __asm { + pushad + cmp [icondata],0 + je proc_out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short noreset2 + push eax + call Init_Stamps + pop eax // Clean up stack. ST - 12/20/2018 10:42AM +noreset2: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short notmap2 + mov edi,[MapPtr] + mov bl,[edi+ebx] +notmap2: + cmp ebx,[IconCount] + jae proc_out + mov [icon],ebx ; Updated icon number. + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Update the clipping window coordinates to be valid maxes instead of width & height + ; , and change the coordinates to be window-relative + mov ebx,[min_x] + add [max_x],ebx + add [x_pixel],ebx ; make it window-relative + mov ebx,[min_y] + add [max_y],ebx + add [y_pixel],ebx ; make it window-relative + + ; See if the icon is within the clipping window + ; First, verify that the icon position is less than the maximums + mov ebx,[x_pixel] + cmp ebx,[max_x] + jge proc_out + mov ebx,[y_pixel] + cmp ebx,[max_y] + jge proc_out + ; Now verify that the icon position is >= the minimums + add ebx,[IconHeight] + cmp ebx,[min_y] + jle proc_out + mov ebx,[x_pixel] + add ebx,[IconWidth] + cmp ebx,[min_x] + jle proc_out + + ; Now, clip the x, y, width, and height variables to be within the + ; clipping rectangle + mov ebx,[x_pixel] + cmp ebx,[min_x] + jge nominxclip + ; x < minx, so must clip + mov ebx,[min_x] + sub ebx,[x_pixel] + add esi,ebx ; source ptr += (minx - x) + sub [iwidth],ebx ; icon width -= (minx - x) + mov ebx,[min_x] + mov [x_pixel],ebx + +nominxclip: + mov eax,[IconWidth] + sub eax,[iwidth] + mov [skip],eax + + ; Check for x+width > max_x + mov eax,[x_pixel] + add eax,[iwidth] + cmp eax,[max_x] + jle nomaxxclip + ; x+width is greater than max_x, so must clip width down + mov eax,[iwidth] ; eax = old width + mov ebx,[max_x] + sub ebx,[x_pixel] + mov [iwidth],ebx ; iwidth = max_x - xpixel + sub eax,ebx + add [skip],eax ; skip += (old width - iwidth) +nomaxxclip: + ; check if y < miny + mov eax,[min_y] + cmp eax,[y_pixel] ; if(miny <= y_pixel), no clip needed + jle nominyclip + sub eax,[y_pixel] + sub ecx,eax ; height -= (miny - y) + mul [IconWidth] + add esi,eax ; icon source ptr += (width * (miny - y)) + mov eax,[min_y] + mov [y_pixel],eax ; y = miny +nominyclip: + ; check if (y+height) > max y + mov eax,[y_pixel] + add eax,ecx + cmp eax,[max_y] ; if (y + height <= max_y), no clip needed + jle nomaxyclip + mov ecx,[max_y] ; height = max_y - y_pixel + sub ecx,[y_pixel] +nomaxyclip: + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[iwidth] + mov [modulo],eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short istranscheck2 + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + mov ebx,[remap] + xor eax,eax +xrowloopc: + push ecx + mov ecx,[iwidth] + +xcolumnloopc: + lodsb + xlatb + or al,al + jz short xskip1c ; Transparency skip check. + mov [edi],al +xskip1c: + inc edi + loop xcolumnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop xrowloopc + jmp short proc_out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +istranscheck2: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short rowloopc + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + mov edx,[modulo] + mov eax,[iwidth] + + ; + ; Optimise copy by dword aligning the destination + ; +loop1c: + push eax + //rept 3 // No rept in inline asm. ST - 12/20/2018 10:43AM + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + //endm +aligned: + mov ecx,eax + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,3 + rep movsb + +finishedit: + add edi,edx + add esi,[skip] + pop eax + + dec ebx + jnz loop1c + jmp short proc_out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +rowloopc: + push ecx + mov ecx,[iwidth] + +columnloopc: + lodsb + or al,al + jz short skip1c ; Transparency check. + mov [edi],al +skip1c: + inc edi + loop columnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop rowloopc + + ; Cleanup and exit icon drawing routine. +proc_out: + popad + //ret + } +} + + + + + + + + + + + + + + + + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + + +VOID __cdecl Buffer_Remap(void * this_object, int sx, int sy, int width, int height, void *remap) +{ +/* + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword +*/ + + unsigned int x0_pixel = (unsigned int) sx; + unsigned int y0_pixel = (unsigned int) sy; + unsigned int region_width = (unsigned int) width; + unsigned int region_height = (unsigned int) height; + + unsigned int x1_pixel = 0; + unsigned int y1_pixel = 0; + unsigned int win_width = 0; + unsigned int counter_x = 0; + + __asm { + + cmp [ remap ] , 0 + jz real_out + + ; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_remap + + test cl , 1000b + jz scr_left_ok + mov [ x0_pixel ] , 0 + +scr_left_ok: + test cl , 0010b + jz scr_bottom_ok + mov [ y0_pixel ] , 0 + +scr_bottom_ok: + test dl , 0100b + jz scr_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ] , eax +scr_right_ok: + test dl , 0001b + jz do_remap + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ y1_pixel ] , eax + + +do_remap: + cld + mov edi , [esi]GraphicViewPortClass.Offset + mov eax , [esi]GraphicViewPortClass.XAdd + mov ebx , [ x1_pixel ] + add eax , [esi]GraphicViewPortClass.Width + add eax , [esi]GraphicViewPortClass.Pitch + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +outer_loop: + mov ebx , [ counter_x ] +inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz inner_loop + add edi , esi + dec ecx + jnz outer_loop + + + + +real_out: +// ret + } +} + + + + + + + + + + + + + + + +/* +; ************************************************************************** +; ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S * +; ************************************************************************** +; * * +; * Project Name : WSA Support routines * +; * * +; * File Name : XORDELTA.ASM * +; * * +; * Programmer : Scott K. Bowen * +; * * +; * Last Update :May 23, 1994 [SKB] * +; * * +; *------------------------------------------------------------------------* +; * Functions: * +;* Apply_XOR_Delta -- Apply XOR delta data to a buffer. * +;* Apply_XOR_Delta_To_Page_Or_Viewport -- Calls the copy or the XOR funti* +;* Copy_Delta_buffer -- Copies XOR Delta Data to a section of a page. * +;* XOR_Delta_Buffer -- Xor's the data in a XOR Delta format to a page. * +; * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* + +IDEAL +P386 +MODEL USE32 FLAT +*/ + + +/* +LOCALS ?? + +; These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +; These change, make sure and change their values in wsa.cpp. +DO_XOR equ 0 +DO_COPY equ 1 +TO_VIEWPORT equ 0 +TO_PAGE equ 2 + +; +; Routines defined in this module +; +; +; UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr); +; PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD copy) +; +; PROC C XOR_Delta_Buffer +; PROC C Copy_Delta_Buffer +; + +GLOBAL C Apply_XOR_Delta:NEAR +GLOBAL C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR +*/ + +#define DO_XOR 0 +#define DO_COPY 1 +#define TO_VIEWPORT 0 +#define TO_PAGE 2 + +void __cdecl XOR_Delta_Buffer(int nextrow); +void __cdecl Copy_Delta_Buffer(int nextrow); + + +/* +;*************************************************************************** +;* APPLY_XOR_DELTA -- Apply XOR delta data to a linear buffer. * +;* AN example of this in C is at the botton of the file commented out. * +;* * +;* INPUT: BYTE *target - destination buffer. * +;* BYTE *delta - xor data to be delta uncompress. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 05/23/1994 SKB : Created. * +;*=========================================================================* +*/ +unsigned int __cdecl Apply_XOR_Delta(char *target, char *delta) +{ +/* +PROC Apply_XOR_Delta C near + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointers. + ARG delta:DWORD ; pointers. +*/ + + __asm { + + ; Optimized for 486/pentium by rearanging instructions. + mov edi,[target] ; get our pointers into offset registers. + mov esi,[delta] + + cld ; make sure we go forward + xor ecx,ecx ; use cx for loop + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ;get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edi + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edi ; go to next dest pixel + dec ecx ; one less to go. + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] + lea esi,[esi+2] ; get word code in ax + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + add edi,eax ; do the skip. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + } +} + + +/* +;---------------------------------------------------------------------------- + +;*************************************************************************** +;* APPLY_XOR_DELTA_To_Page_Or_Viewport -- Calls the copy or the XOR funtion. * +;* * +;* * +;* This funtion is call to either xor or copy XOR_Delta data onto a * +;* page instead of a buffer. The routine will set up the registers * +;* need for the actual routines that will perform the copy or xor. * +;* * +;* The registers are setup as follows : * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ + +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy) +{ + /* + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointer to the destination buffer. + ARG delta:DWORD ; pointer to the delta buffer. + ARG width:DWORD ; width of animation. + ARG nextrow:DWORD ; Page/Buffer width - anim width. + ARG copy:DWORD ; should it be copied or xor'd? + */ + + __asm { + + mov edi,[target] ; Get the target pointer. + mov esi,[delta] ; Get the destination pointer. + + xor eax,eax ; clear eax, later put them into ecx and edx. + + cld ; make sure we go forward + + mov ebx,[nextrow] ; get the amount to add to get to next row from end. push it later... + + mov ecx,eax ; use cx for loop + mov edx,eax ; use dx to count the relative column. + + push ebx ; push nextrow onto the stack for Copy/XOR_Delta_Buffer. + mov ebx,[width] ; bx will hold the max column for speed compares + + ; At this point, all the registers have been set up. Now call the correct function + ; to either copy or xor the data. + + cmp [copy],DO_XOR ; Do we want to copy or XOR + je xorfunct ; Jump to XOR if not copy + call Copy_Delta_Buffer ; Call the function to copy the delta buffer. + jmp didcopy ; jump past XOR + xorfunct: + call XOR_Delta_Buffer ; Call funtion to XOR the deltat buffer. + didcopy: + pop ebx ; remove the push done to pass a value. + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* XOR_DELTA_BUFFER -- Xor's the data in a XOR Delta format to a page. * +;* This will only work right if the page has the previous data on it. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* edx,ecx,eax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl XOR_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edx ; increment our count on current column + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* COPY_DELTA_BUFFER -- Copies XOR Delta Data to a section of a page. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl Copy_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + + mov [edi],al ; store that byte on the dest + + inc edx ; increment our count on current column + inc esi + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + mov [edi],al ; store the byte (instead of XOR against current color) + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} +/* +;---------------------------------------------------------------------------- +*/ + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* +*/ +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac) +{ + /* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + */ + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini + cmp [dest],0 + je fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 + innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne mainloop + + fini: + mov eax,[dest] +// ret + + + } +} + + + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;include "keyboard.inc" +FALSE = 0 +TRUE = 1 + +;****************************** Declarations ******************************** +GLOBAL C Set_Palette_Range:NEAR +GLOBAL C Bump_Color:NEAR +GLOBAL C CurrentPalette:BYTE:768 +GLOBAL C PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG +*/ + +extern "C" unsigned char CurrentPalette[768] = {255}; // DB 768 DUP(255) ; copy of current values of DAC regs +extern "C" unsigned char PaletteTable[1024] = {0}; // DB 1024 DUP(0) + + +/* +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* +*/ +void __cdecl Set_Palette_Range(void *palette) +{ + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); + + /* + PROC Set_Palette_Range C NEAR + ARG palette:DWORD + + GLOBAL Set_DD_Palette_:near + GLOBAL Wait_Vert_Blank_:near + + pushad + mov esi,[palette] + mov ecx,768/4 + mov edi,offset CurrentPalette + cld + rep movsd + ;call Wait_Vert_Blank_ + mov eax,[palette] + push eax + call Set_DD_Palette_ + pop eax + popad + ret + */ +} + + +/* +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +*/ +BOOL __cdecl Bump_Color(void *pal, int color, int desired) +{ + /* +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + */ + + short short_color = (short) color; + short short_desired = (short) desired; + bool changed = false; + + __asm { + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[short_color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[short_desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. + colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] + gotit: + inc edi + inc esi + loop colorloop + + movzx eax,[changed] + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : GraphicViewPortClass * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + +/* +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Put_Pixel C near + USES eax,ebx,ecx,edx,edi +*/ + +void __cdecl Buffer_Put_Pixel(void * this_object, int x_pixel, int y_pixel, unsigned char color) +{ + + /* + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + */ + + __asm { + + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short done ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae done ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; add in direct draw pitch + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen + done: + } +} + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Clip_Rect :NEAR +GLOBAL C Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * +*/ + +extern "C" int __cdecl Clip_Rect ( int * x , int * y , int * w , int * h , int width , int height ) +{ + +/* + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword +*/ + + __asm { + + ;This Clipping algorithm is a derivation of the very well known + ;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency + ;it is probably the most commontly implemented algorithm both in software + ;and hardware for clipping lines, rectangles, and convex polygons against + ;a rectagular clipping window. For reference see + ;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes + ; pages 113 to 177". + ; Briefly consist in computing the Sutherland code for both end point of + ; the rectangle to find out if the rectangle is: + ; - trivially accepted (no further clipping test, return the oroginal data) + ; - trivially rejected (return with no action, return error code) + ; - retangle must be iteratively clipped again edges of the clipping window + ; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + + left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + + bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify + clip_out: + //ret + } + + //ENDP Clip_Rect +} + +/* +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * +*/ + +extern "C" int __cdecl Confine_Rect ( int * x , int * y , int w , int h , int width , int height ) +{ + +/* + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword +*/ + __asm { + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl x_axix_ok + mov eax,1 + + test esi,esi + jl shift_right + mov [dword ptr ebx],0 + jmp x_axix_ok + shift_right: + inc edi + sub [ebx],edi + x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl confi_out + mov eax,1 + + test esi,esi + jl shift_top + mov [dword ptr ebx],0 + + //ret + jmp confi_out + shift_top: + inc edi + sub [ebx],edi + confi_out: + //ret + + //ENDP Confine_Rect + } +} + + + + + + + + + +/* +; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- +*/ +#if (0)//ST 5/10/2019 +extern "C" unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length_) +{ +//PROC LCW_Uncompress C near +// +// USES ebx,ecx,edx,edi,esi +// +// ARG source:DWORD +// ARG dest:DWORD +// ARG length:DWORD +//;LOCALS +// LOCAL a1stdest:DWORD +// LOCAL maxlen:DWORD +// LOCAL lastbyte:DWORD +// LOCAL lastcom:DWORD +// LOCAL lastcom1:DWORD + + unsigned long a1stdest; + unsigned long maxlen; + unsigned long lastbyte; + //unsigned long lastcom; + //unsigned long lastcom1; + + __asm { + + + mov edi,[dest] + mov esi,[source] + mov edx,[length_] + + ; + ; + ; uncompress data to the following codes in the format b = byte, w = word + ; n = byte code pulled from compressed data + ; Bit field of n command description + ; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 + ; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes + ; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 + ; n=11111111,w1,w2 long copy copy w1 bytes from offset w2 + ; n=11111110,w1,b1 long run run byte b1 for w1 bytes + ; n=10000000 end end of data reached + ; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + + loop_label: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short out_label ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp loop_label + + notshort: + test al,40h ; is it a length? + jne short notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short out_label ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp loop_label + + out_label: + mov eax,edi + sub eax,[a1stdest] + jmp label_exit + + notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runlenok: + test ecx,0ffe0h + jnz dont_use_stosb + rep stosb + jmp loop_label + + + dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + + aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz loop_label + mov ecx,edx + rep stosb + jmp loop_label + + + + + + + notrunlength: + cmp al,0FFh ; is it a long run? + jne short notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + + notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runok: + test ecx,0ffe0h + jnz dont_use_movsb + rep movsb + jmp loop_label + + + + + dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja use_movsb + + test edi,3 + jz aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + + aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx + use_movsb: + rep movsb + jmp loop_label + + + + + label_exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + //ret + + } +} +#endif + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + */ + +extern "C" long __cdecl Buffer_To_Page(int x_pixel, int y_pixel, int pixel_width, int pixel_height, void *src, void *dest) +{ + +/* + PROC Buffer_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + local scr_x : dword + local scr_y : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword +*/ + + unsigned long x1_pixel; + unsigned long y1_pixel; + unsigned long scr_x; + unsigned long scr_y; + unsigned long dest_ajust_width; + unsigned long scr_ajust_width; + //unsigned long dest_area; + + __asm { + + cmp [ src ] , 0 + jz real_out + + + ; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi, [esi]GraphicViewPortClass.Height ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_blit + + test cl , 1000b + jz dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + + dest_left_ok: + test cl , 0010b + jz dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + + dest_bottom_ok: + test dl , 0100b + jz dest_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ] , eax + dest_right_ok: + test dl , 0001b + jz do_blit + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ y1_pixel ] , eax + + do_blit: + + cld + + mov eax , [esi]GraphicViewPortClass.XAdd + add eax , [esi]GraphicViewPortClass.Width + add eax , [esi]GraphicViewPortClass.Pitch + mov edi , [esi]GraphicViewPortClass.Offset + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle real_out + sub eax , [ x_pixel ] + jle real_out + + + ; ******************************************************************** + ; Forward bitblit only + + //IF TRANSP + // test [ trans ] , 1 + // jnz forward_Blit_trans + //ENDIF + + + ; the inner loop is so efficient that + ; the optimal consept no longer apply because + ; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl forward_loop_bytes + + forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_dword + jmp real_out //ret + + forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + // ret + + //IF TRANSP + // + // + //forward_Blit_trans: + // + // + // mov ecx , eax + // and ecx , 01fh + // lea ecx , [ ecx + ecx * 4 ] + // neg ecx + // shr eax , 5 + // lea ecx , [ transp_reference + ecx * 2 ] + // mov [ y1_pixel ] , ecx + // + // + //forward_loop_trans: + // mov ecx , eax + // jmp [ y1_pixel ] + //forward_trans_line: + // REPT 32 + // local transp_pixel + // mov bl , [ esi ] + // inc esi + // test bl , bl + // jz transp_pixel + // mov [ edi ] , bl + // transp_pixel: + // inc edi + // ENDM + // transp_reference: + // dec ecx + // jge forward_trans_line + // add esi , [ scr_ajust_width ] + // add edi , [ dest_ajust_width ] + // dec edx + // jnz forward_loop_trans + // ret + //ENDIF + + real_out: + //ret + } +} + + //ENDP Buffer_To_Page + //END + + + + + + + + + +/* + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set +*/ + +extern "C" int __cdecl Buffer_Get_Pixel(void * this_object, int x_pixel, int y_pixel) +{ + __asm { + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short exit_label ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae exit_label ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel + exit_label: + //ret + //ENDP Buffer_Get_Pixel + + } +} + + diff --git a/REDALERT/WIN32LIB/EXTERNS.H b/REDALERT/WIN32LIB/EXTERNS.H new file mode 100644 index 000000000..441409100 --- /dev/null +++ b/REDALERT/WIN32LIB/EXTERNS.H @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/REDALERT/WIN32LIB/FASTFILE.H b/REDALERT/WIN32LIB/FASTFILE.H new file mode 100644 index 000000000..2ba372b34 --- /dev/null +++ b/REDALERT/WIN32LIB/FASTFILE.H @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: fastfile.h + * Content: Definitions for fastfile access. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + * + ***************************************************************************/ + +typedef LPVOID HFASTFILE; + +extern BOOL FastFileInit( LPSTR fname, int max_handles ); +extern void FastFileFini( void ); +extern HFASTFILE FastFileOpen( LPSTR name ); +extern BOOL FastFileClose( HFASTFILE pfe ); +extern BOOL FastFileRead( HFASTFILE pfh, LPVOID ptr, int size ); +extern BOOL FastFileSeek( HFASTFILE pfe, int off, int how ); +extern long FastFileTell( HFASTFILE pfe ); +extern LPVOID FastFileLock( HFASTFILE pfe, int off, int len ); +extern BOOL FastFileUnlock( HFASTFILE pfe, int off, int len ); diff --git a/REDALERT/WIN32LIB/FILE.H b/REDALERT/WIN32LIB/FILE.H new file mode 100644 index 000000000..e9009fd6f --- /dev/null +++ b/REDALERT/WIN32LIB/FILE.H @@ -0,0 +1,257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#ifndef READ +#define READ 1 // Read access. +#endif +#ifndef WRITE +#define WRITE 2 // Write access. +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Find_First(unsigned char *fname, unsigned int mode, struct find_t *ffblk); +extern int __cdecl Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/REDALERT/WIN32LIB/FILEPCX.H b/REDALERT/WIN32LIB/FILEPCX.H new file mode 100644 index 000000000..0fa46958f --- /dev/null +++ b/REDALERT/WIN32LIB/FILEPCX.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/REDALERT/WIN32LIB/FILETEMP.H b/REDALERT/WIN32LIB/FILETEMP.H new file mode 100644 index 000000000..9a36b1b22 --- /dev/null +++ b/REDALERT/WIN32LIB/FILETEMP.H @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD __cdecl ( __cdecl IO_Error)(FileErrorType error, BYTE const *filename); +void __cdecl Prog_End(const char *why, bool fatal); // Added why and fatal. ST - 8/7/2019 10:54AM +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H diff --git a/REDALERT/WIN32LIB/FONT.CPP b/REDALERT/WIN32LIB/FONT.CPP new file mode 100644 index 000000000..8681a0ee0 --- /dev/null +++ b/REDALERT/WIN32LIB/FONT.CPP @@ -0,0 +1,138 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + /*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : FONT.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : July 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Char_Pixel_Width -- Return pixel width of a character. * + * String_Pixel_Width -- Return pixel width of a string of characters. * + * Get_Next_Text_Print_XY -- Calculates X and Y given ret value from Text_P* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "font.h" +#include +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + * CHAR_PIXEL_WIDTH -- Return pixel width of a character. * + * * + * Retreives the pixel width of a character from the font width block. * + * * + * INPUT: Character. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/31/1992 DRD : Created. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +int __cdecl Char_Pixel_Width(char chr) +{ + int width; + + width = (unsigned char)*(FontWidthBlockPtr + (unsigned char)chr) + FontXSpacing; + + return(width); +} + + +/*************************************************************************** + * STRING_PIXEL_WIDTH -- Return pixel width of a string of characters. * + * * + * Calculates the pixel width of a string of characters. This uses * + * the font width block for the widths. * + * * + * INPUT: Pointer to string of characters. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/30/1992 DRD : Created. * + * 01/31/1992 DRD : Use Char_Pixel_Width. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +unsigned int __cdecl String_Pixel_Width(char const *string) +{ + WORD width; // Working accumulator of string width. + WORD largest = 0; // Largest recorded width of the string. + + if (!string) return(0); + + width = 0; + while (*string) { + if (*string == '\r') { + string++; + largest = MAX(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); // add each char's width + } + } + largest = MAX(largest, width); + return(largest); +} + + + +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * unsigned long offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& gp, unsigned long offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = gp.Get_Width() + gp.Get_XAdd(); + offset -= gp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} diff --git a/REDALERT/WIN32LIB/FONT.H b/REDALERT/WIN32LIB/FONT.H new file mode 100644 index 000000000..1160f5ff9 --- /dev/null +++ b/REDALERT/WIN32LIB/FONT.H @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Set_Font(void const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +int __cdecl Char_Pixel_Width(char chr); +unsigned int __cdecl String_Pixel_Width(char const *string); +void __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, unsigned long offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Load_Font(char const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern char FontWidth ; +extern char FontHeight; +extern char *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/REDALERT/WIN32LIB/FUNCTION.H b/REDALERT/WIN32LIB/FUNCTION.H new file mode 100644 index 000000000..0f0eea9e1 --- /dev/null +++ b/REDALERT/WIN32LIB/FUNCTION.H @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/REDALERT/WIN32LIB/GBUFFER.CPP b/REDALERT/WIN32LIB/GBUFFER.CPP new file mode 100644 index 000000000..3f1365a1b --- /dev/null +++ b/REDALERT/WIN32LIB/GBUFFER.CPP @@ -0,0 +1,707 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * GBC::DD_Init -- Inits a direct draw surface for a GBC * + * GBC::Init -- Core function responsible for initing a GBC * + * GBC::Lock -- Locks a Direct Draw Surface * + * GBC::Unlock -- Unlocks a direct draw surface * + * GBC::GraphicBufferClass -- Default constructor (requires explicit init)* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif +//#pragma inline + +int TotalLocks; +BOOL AllowHardwareBlitFills = TRUE; + + +//int CacheAllowed; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) : + LockCount(0), + GraphicBuff(NULL) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ + Offset = 0; + Width = 0; // Record width of Buffer + Height = 0; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + Pitch = 0; // Record width of Buffer + IsDirectDraw = FALSE; + LockCount = 0; + GraphicBuff = NULL; +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not attach a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return; + } + + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + ((gbuffer->Get_Width()+gbuffer->Get_Pitch()) * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + Pitch = gbuffer->Get_Pitch(); + GraphicBuff = gbuffer; + IsDirectDraw= gbuffer->IsDirectDraw; +} + + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + + +/*************************************************************************** + * GBC::DD_INIT -- Inits a direct draw surface for a GBC * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::DD_Init(GBC_Enum flags) +{ + // + // Create the direct draw surface description + // + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.dwFlags = DDSD_CAPS; + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + + if (!(flags & GBC_VISIBLE)) { + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags |= DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = Height; + VideoSurfaceDescription.dwWidth = Width; + } + + // + // Need to set the DDSCAPS_MODEX flag if we want a 320 wide mode + // + if ( Width == 320 ) { + VideoSurfaceDescription.ddsCaps.dwCaps |= DDSCAPS_MODEX; + } + + // + // Call CreateSurface + // + DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &VideoSurfacePtr , NULL); + AllSurfaces.Add_DD_Surface (VideoSurfacePtr); + + if ( GBC_VISIBLE & flags ){ + PaletteSurface=VideoSurfacePtr; + } + + Allocated = FALSE; // even if system alloced, dont flag it cuz + // we dont want it freed. + IsDirectDraw = TRUE; // flag it as a video surface + Offset = NOT_LOCKED; // flag it as unavailable for reading or writing + LockCount = 0; // surface is not locked +} + + +void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer) +{ + VideoSurfacePtr->AddAttachedSurface (attach_buffer->Get_DD_Surface()); +} + + +/*************************************************************************** + * GBC::INIT -- Core function responsible for initing a GBC * + * * + * INPUT: int - the width in pixels of the GraphicBufferClass * + * int - the heigh in pixels of the GraphicBufferClass * + * void * - pointer to user supplied buffer (system will * + * allocate space if buffer is NULL) * + * long - size of the user provided buffer * + * GBC_Enum - flags if this is defined as a direct draw * + * surface * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::Init(int w, int h, void *buffer, long size, GBC_Enum flags) +{ + Size = size; // find size of physical buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + + // + // If the surface we are creating is a direct draw object then + // we need to do a direct draw init. Otherwise we will do + // a normal alloc. + // + if (flags & (GBC_VIDEOMEM | GBC_VISIBLE)) { + DD_Init(flags); + } else { + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + if (!Size) Size = w*h; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + Offset = (long)Buffer; // Get offset to the buffer + IsDirectDraw = FALSE; + } + + Pitch = 0; // Record width of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} + + +/*********************************************************************************************** + * GBC::Un_Init -- releases the video surface belonging to this gbuffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:44PM ST : Created * + *=============================================================================================*/ + +void GraphicBufferClass::Un_Init (void) +{ + if ( IsDirectDraw ){ + + if ( VideoSurfacePtr ){ + + while ( LockCount ){ + + if (VideoSurfacePtr->Unlock ( NULL ) == DDERR_SURFACELOST){ + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + } + } + + AllSurfaces.Remove_DD_Surface (VideoSurfacePtr); + VideoSurfacePtr->Release(); + VideoSurfacePtr = NULL; + } + } +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Default constructor (requires explicit init) * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(void) +{ + GraphicBuff = this; // Get a pointer to our self + VideoSurfacePtr = NULL; + memset(&VideoSurfaceDescription, 0, sizeof(DDSURFACEDESC)); +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer, long size) +{ + Init(w, h, buffer, size, GBC_NONE); +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Init(w, h, buffer, w * h, GBC_NONE); +} + +/*====================================================================================* + * GBC::GRAPHICBUFFERCLASS -- contructor for GraphicsBufferClass with special flags * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - unused * + * unsigned flags - flags for creation of special buffer types * + * GBC_VISIBLE - buffer is a visible screen surface * + * GBC_VIDEOMEM - buffer resides in video memory * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09-21-95 04:19pm ST : Created * + *====================================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, GBC_Enum flags) +{ + Init(w, h, NULL, w * h, flags); +} + +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ + +// +// Release the direct draw surface if it exists +// + Un_Init(); +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} + + +/*************************************************************************** + * GBC::LOCK -- Locks a Direct Draw Surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ +extern void Colour_Debug (int call_number); +extern bool GameInFocus; + +extern void Block_Mouse(GraphicBufferClass *buffer); +extern void Unblock_Mouse(GraphicBufferClass *buffer); + +BOOL GraphicBufferClass::Lock(void) +{ + HRESULT result; + int restore_attempts=0; + + // + // If its not a direct draw surface then the lock is always sucessful. + // + if (!IsDirectDraw) return(TRUE); + + /* + ** If the video surface pointer is null then return + */ + if (!VideoSurfacePtr) return (FALSE); + + /* + ** If we dont have focus then return failure + */ + if (!GameInFocus) return (FALSE); + + + Block_Mouse(this); + + + // + // If surface is already locked then inc the lock count and return true + // + if (LockCount){ + LockCount++; + Unblock_Mouse(this); + return(TRUE); + } + + // + // If it isn't locked at all then we will have to request that Direct + // Draw actually lock the surface. + // + + if (VideoSurfacePtr){ + while (!LockCount && restore_attempts<2) { + result = VideoSurfacePtr->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL); + + switch (result){ + case DD_OK : + Offset = (unsigned long)VideoSurfaceDescription.lpSurface; + Pitch = VideoSurfaceDescription.lPitch; + Pitch -= Width; + LockCount++; // increment count so we can track if + TotalLocks++; // Total number of times we have locked (for debugging) + //Colour_Debug (1); + Unblock_Mouse(this); + return (TRUE); // we locked it multiple times. + + case DDERR_SURFACELOST : + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + restore_attempts++; + break; + + default : + Unblock_Mouse(this); + return (FALSE); + } + } + } + //Colour_Debug(1); + Unblock_Mouse(this); + return (FALSE); //Return false because we couldnt lock or restore the surface +} + +/*************************************************************************** + * GBC::UNLOCK -- Unlocks a direct draw surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ + + +BOOL GraphicBufferClass::Unlock(void) +{ + // + // If there is no lock count or this is not a direct draw surface + // then just return true as there is no harm done. + // + if (!(LockCount && IsDirectDraw)) { + return(TRUE); + } + + // + // If lock count is directly equal to one then we actually need to + // unlock so just give it a shot. + // + if (LockCount == 1 && VideoSurfacePtr) { + Block_Mouse(this); + if ( VideoSurfacePtr->Unlock ( NULL ) != DD_OK ){ + Unblock_Mouse(this); + return(FALSE); + } else { + Offset=NOT_LOCKED; + LockCount--; + Unblock_Mouse(this); + return(TRUE); + } + } + //Colour_Debug (0); + LockCount--; + return(TRUE); +} + + +/*********************************************************************************************** + * GVPC::DD_Linear_Blit_To_Linear -- blit using the hardware blitter * + * * + * * + * * + * INPUT: destination vvpc * + * x coord to blit from * + * y coord to blit from * + * x coord to blit to * + * y coord to blit to * + * width to blit * + * height to blit * + * * + * OUTPUT: DD_OK if successful * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-22-95 11:05am ST : Created * + *=============================================================================================*/ + +HRESULT GraphicViewPortClass::DD_Linear_Blit_To_Linear ( + GraphicViewPortClass &dest + , int source_x + , int source_y + , int dest_x + , int dest_y + , int width + , int height + , BOOL mask ) + +{ + RECT source_rectangle; + RECT dest_rectangle; + int key_source=0; + + if ( mask ){ + key_source=DDBLT_KEYSRC; + } + + + source_rectangle.left = source_x; + source_rectangle.top = source_y; + source_rectangle.right = source_x+width; + source_rectangle.bottom = source_y+height; + + dest_rectangle.left = dest_x; + dest_rectangle.top = dest_y; + dest_rectangle.right = dest_x+width; + dest_rectangle.bottom = dest_y+height; + + return ( dest.GraphicBuff->Get_DD_Surface()->Blt ( &dest_rectangle, + GraphicBuff->Get_DD_Surface(), + &source_rectangle, + key_source | DDBLT_WAIT | DDBLT_ASYNC, + NULL ) ); +} + + + diff --git a/REDALERT/WIN32LIB/GBUFFER.H b/REDALERT/WIN32LIB/GBUFFER.H new file mode 100644 index 000000000..438154f17 --- /dev/null +++ b/REDALERT/WIN32LIB/GBUFFER.H @@ -0,0 +1,1363 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef DRAWBUFF_H +#include "drawbuff.h" +#endif + +//#ifndef BUFFER_H +#include "buffer.h" +//#endif + +#ifndef WINDOWS_H +#include "ww_win.h" +#endif +#include + +#include "iconcach.h" + + + +#ifndef FUNCTION_H + +//#pragma off (unreferenced) + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +//#pragma on (unreferenced) +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// Defines for direct draw +// +// +extern LPDIRECTDRAW DirectDrawObject; //pointer to direct draw object +extern HWND MainWindow; //handle to programs main window + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Gbuffer_Focus_Loss_Function)(void); + +enum GBC_Enum { + GBC_NONE = 0, + GBC_VIDEOMEM = 1, + GBC_VISIBLE = 2, +}; + +#define NOT_LOCKED NULL + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 356 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr); +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + int Get_Pitch(void); + inline BOOL Get_IsDirectDraw(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + HRESULT Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + +// This doesnt seem to exist anywhere?? - Steve T 9/26/95 6:05PM +// VOID Grey_Out_Region(int x, int y, int width, int height, int color); + + // + // New members to lock and unlock the direct draw video memory + // + inline BOOL Lock (); + inline BOOL Unlock(); + inline int Get_LockCount(); + + // Member to blit using direct draw access to hardware blitter + HRESULT DD_Linear_Blit_To_Linear ( GraphicViewPortClass &dest, int source_x, int source_y, int dest_x, int dest_y, int width , int height, BOOL mask ); + + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + protected: + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + long Pitch; //Distance from one line to the next + GraphicBufferClass *GraphicBuff; // related graphic buff + BOOL IsDirectDraw; //Flag to let us know if it is a direct draw surface + int LockCount; // Count for stacking locks if non-zero the buffer +}; // is a locked DD surface + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/* long Pitch - modulo of buffer for reading and writing */ +/* BOOL IsDirectDraw - flag if its a direct draw surface */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + + public: + GraphicBufferClass(int w, int h, GBC_Enum flags); + GraphicBufferClass(int w, int h, void *buffer, long size); + GraphicBufferClass(int w, int h, void *buffer = 0); + GraphicBufferClass(void); + ~GraphicBufferClass(); + + void DD_Init(GBC_Enum flags); + void Init(int w, int h, void *buffer, long size, GBC_Enum flags); + void Un_Init(void); + void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer); + BOOL Lock(void); + BOOL Unlock(void); + + void Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle); + + // Member to get a pointer to a direct draw surface + LPDIRECTDRAWSURFACE Get_DD_Surface ( void ); + + protected: + LPDIRECTDRAWSURFACE VideoSurfacePtr; //Pointer to the related direct draw surface + DDSURFACEDESC VideoSurfaceDescription;//Description of the said surface + +}; + + + +inline int GraphicViewPortClass::Get_LockCount(void) +{ + return (LockCount); +} + + + +/*********************************************************************************************** + * GVPC::Get_IsDirectDraw -- provide read access to the IsDirectDraw flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsDirectDraw * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 1:02PM ST : Created * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Get_IsDirectDraw(void) +{ + return (IsDirectDraw); +} + + + +/*********************************************************************************************** + * GBC::Get_DD_Surface -- returns a pointer to the buffer direct draw surface * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to direct draw surface * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/29/95 9:43AM ST : Created * + *=============================================================================================*/ +inline LPDIRECTDRAWSURFACE GraphicBufferClass::Get_DD_Surface ( void ) +{ + return ( VideoSurfacePtr ); + +} + + + +/*********************************************************************************************** + * GVPC::Lock -- lock the graphics buffer for reading or writing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully locked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 12:33pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Lock(void) +{ + BOOL lock = GraphicBuff->Lock(); + if ( !lock ) return(FALSE); + + if (this != GraphicBuff) { + Attach(GraphicBuff, XPos, YPos, Width, Height); + } + return(TRUE); +} + +/*********************************************************************************************** + * GVPC::Unlock -- unlock the video buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully unlocked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 02:20pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Unlock(void) +{ + BOOL unlock = GraphicBuff->Unlock(); + if (!unlock) return(FALSE); + if (this != GraphicBuff && IsDirectDraw && !GraphicBuff->LockCount) { + Offset = 0; + } + return(TRUE); +} + + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return Buffer_Size_Of_Region(this, w, h); +} + + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + + if (Lock()){ + Buffer_Put_Pixel(this, x, y, color); + } + Unlock(); + + +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + int return_code=0; + + if (Lock()){ + return_code=(Buffer_Get_Pixel(this, x, y)); + } + Unlock(); + return(return_code); + +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + if (Lock()){ + Buffer_Clear(this, color); + } + Unlock(); + +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff, size)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + HRESULT return_code=0; + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos+x_pixel, YPos+y_pixel + , dest.Get_XPos()+dx_pixel, dest.Get_YPos()+dy_pixel + , pixel_width, pixel_height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel + , dx_pixel, dy_pixel + , pixel_width, pixel_height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos()+dx, dest.Get_YPos()+dy + , Width, Height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , dx, dy + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos(), dest.Get_YPos() + , MAX( Width, dest.Get_Width()) + , MAX( Height, dest.Get_Height()) + , trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, str, x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + if (Lock()){ + Buffer_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); + } + Unlock(); +} + + + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +extern BOOL IconCacheAllowed; +inline void GraphicViewPortClass::Draw_Stamp(void const * icondata, int icon, int x_pixel, int y_pixel, void const * remap, int clip_window) +{ + int cache_index=-1; + + int drewit = 0; + if (IconCacheAllowed){ + if (IsDirectDraw){ + if (!remap){ + cache_index = Is_Icon_Cached(icondata,icon); + } + + if (cache_index != -1){ + if (CachedIcons[cache_index].Get_Is_Cached() ){ + CachedIcons[cache_index].Draw_It (GraphicBuff->Get_DD_Surface() , x_pixel, y_pixel, + WindowList[clip_window][WINDOWX] + XPos, + WindowList[clip_window][WINDOWY] +YPos, + WindowList[clip_window][WINDOWWIDTH], + WindowList[clip_window][WINDOWHEIGHT]); + CachedIconsDrawn++; + drewit = 1; + } + } + } + } + + + if (drewit == 0) { + if (Lock()){ + UnCachedIconsDrawn++; + Buffer_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); + } + } + Unlock(); +} + + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + if (Lock()){ + Buffer_Draw_Line(this, sx, sy, dx, dy, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + if ( AllowHardwareBlitFills + && IsDirectDraw + && ( (dx-sx) * (dy-sy) >= (32*32) ) + && GraphicBuff->Get_DD_Surface()->GetBltStatus(DDGBS_CANBLT) == DD_OK){ + DDBLTFX blit_effects; + RECT dest_rectangle; + + dest_rectangle.left =sx+XPos; + dest_rectangle.top =sy+YPos; + dest_rectangle.right =dx+XPos; + dest_rectangle.bottom=dy+YPos; + + if (dest_rectangle.left= Width + XPos){ + dest_rectangle.right = Width +XPos -1; + } + + if (dest_rectangle.top= Height + YPos){ + dest_rectangle.bottom = Height + YPos -1; + } + + if (dest_rectangle.left >= dest_rectangle.right) return; + if (dest_rectangle.top >= dest_rectangle.bottom) return; + + dest_rectangle.right++; + dest_rectangle.bottom++; + + blit_effects.dwSize=sizeof(blit_effects); + blit_effects.dwFillColor = color; + GraphicBuff->Get_DD_Surface()->Blt(&dest_rectangle, + NULL, + NULL, + DDBLT_WAIT | DDBLT_ASYNC | DDBLT_COLORFILL, + &blit_effects); + } else { + if (Lock()){ + Buffer_Fill_Rect(this, sx, sy, dx, dy, color); + Unlock(); + } + } +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, sx, sy, width, height, remap); + } + Unlock(); +} + + +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + if (Lock()){ + Buffer_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, 0, 0, Width, Height, remap); + } + Unlock(); +} + +inline int GraphicViewPortClass::Get_Pitch(void) +{ + return(Pitch); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + + +#endif diff --git a/REDALERT/WIN32LIB/GBUFFER.INC b/REDALERT/WIN32LIB/GBUFFER.INC new file mode 100644 index 000000000..09f2e45b2 --- /dev/null +++ b/REDALERT/WIN32LIB/GBUFFER.INC @@ -0,0 +1,48 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +GraphicViewPort struct +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ends \ No newline at end of file diff --git a/REDALERT/WIN32LIB/GETSHAPE.CPP b/REDALERT/WIN32LIB/GETSHAPE.CPP new file mode 100644 index 000000000..30299dcdf --- /dev/null +++ b/REDALERT/WIN32LIB/GETSHAPE.CPP @@ -0,0 +1,359 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; +//PG int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ + diff --git a/REDALERT/WIN32LIB/ICONCACH.H b/REDALERT/WIN32LIB/ICONCACH.H new file mode 100644 index 000000000..f2e320584 --- /dev/null +++ b/REDALERT/WIN32LIB/ICONCACH.H @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 16th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains definition of the IconCacheClass and associated non member * + * function prototypes. * + * * + * Functions: * + * IconCacheClass::Get_Is_Cached -- member to allow access to private IsCached flag * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#ifndef ICONCACH_H +#define ICONCACH_H + +#include + +#define ICON_WIDTH 24 // Icons must be this width to be cached +#define ICON_HEIGHT 24 // Icons must be this height to be cached +#define MAX_CACHED_ICONS 500 // Maximum number of icons that can be cached +#define MAX_ICON_SETS 100 // Maximum number of icon sets that can be registered +#define MAX_LOOKUP_ENTRIES 3000 // Size of icon index table + + +/* +** IconCacheClass for tracking individual icons cached into video memory +** +** Use Register_Icon_Set to identify a set of icons as cachable. Once registered, the icons +** will be cached automatically when drawn. +** Use Invalidate_Cached_Icons at the end of icon drawing to release the video memory used by the +** caching system. +** Restore_Cached_Icons may be used to reload the icons into video memory after a focus loss. +** +*/ + +class IconCacheClass { + + public: + + IconCacheClass (void); // class constructor + ~IconCacheClass (void); // class destructor + + void Restore(void); // restore the surface + BOOL Cache_It (void * icon_ptr); // Cache the icon to video memory + void Uncache_It (void); // Restore the video memory and flag the icon as uncached + void Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height); + inline BOOL Get_Is_Cached(void); // Return the IsCached member + + int TimesDrawn; // counter of times cached icon has been drawn + int TimesFailed; // counter of times cached icon has failed to draw + + + private: + + LPDIRECTDRAWSURFACE CacheSurface; // Ptr to direct draw surface where icon resides + BOOL IsCached; // Flag to say whether an icon is cached + BOOL SurfaceLost; // Flag to indicate that our icons surface has been lost + int DrawFrequency; // Number of times icon has been drawn + void *IconSource; // Ptr to original icon data in system memory + +}; + + + +/* +** Structure to keep track of registered icon sets +** +*/ + +typedef struct tIconSetType{ + IControl_Type *IconSetPtr; // Ptr to icon set data + int IconListOffset; // Offset into icon index table for this icon set +}IconSetType; + + +extern IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern void Invalidate_Cached_Icons (void); +extern void Restore_Cached_Icons (void); +extern void Register_Icon_Set (void *icon_data , BOOL pre_cache); + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void Clear_Icon_Pointers (void); +extern "C" void Cache_Copy_Icon (void const *icon_ptr ,void * , int); +extern "C" int Is_Icon_Cached (void const *icon_data , int icon); +extern "C" int Get_Icon_Index (void *icon_ptr); +extern "C" int Get_Free_Index (void); +extern "C" BOOL Cache_New_Icon (int icon_index, void *icon_ptr); +extern "C" int Get_Free_Cache_Slot(void); + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/*********************************************************************************************** + * ICC::Get_Is_Cached -- member to allow access to the private IsCached flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsCached * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:42AM ST : Created * + *=============================================================================================*/ +inline BOOL IconCacheClass::Get_Is_Cached (void) +{ + return (IsCached); +} + + + + + +#endif //ICONCACH_H + diff --git a/REDALERT/WIN32LIB/ICONSET.CPP b/REDALERT/WIN32LIB/ICONSET.CPP new file mode 100644 index 000000000..5b3301c8c --- /dev/null +++ b/REDALERT/WIN32LIB/ICONSET.CPP @@ -0,0 +1,341 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" + +//#define _WIN32 +//#define WIN32_LEAN_AND_MEAN + +//#include +#include +#include +//#include +#include +#include +#include "tile.h" +#include + + +// Misc? ST - 1/3/2019 10:40AM +//extern int Misc; +int Misc; + + +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != WW_ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= (unsigned long)buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = (short)(((short)sinf.Width)<<3); + idata->Height = (short)(((short)sinf.Height)<<3); + idata->Allocated = (short)allocated; + idata->Icons = (long)iconsetptr + sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = sizeof(IControl_Type) + size + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if ((unsigned long)buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = (void*)idata->Map; + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Icons)); + return(NULL); +} + +void * Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Map)); + return(NULL); +} diff --git a/REDALERT/WIN32LIB/IFF.CPP b/REDALERT/WIN32LIB/IFF.CPP new file mode 100644 index 000000000..b0a47a162 --- /dev/null +++ b/REDALERT/WIN32LIB/IFF.CPP @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +#ifdef MIN +#undef MIN +#endif + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +int __cdecl Open_Iff_File(char const *filename) +{ + int fh; // File handle. + long type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = WW_ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +void __cdecl Close_Iff_File(int fh) +{ + if (fh != WW_ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: int file handle to open IFF file, long id to get size of * + * * + * OUTPUT: long size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id) +{ + long form; // Chunk iff form name. + long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize) +{ + long form; // Chunk iff form name. + unsigned long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length) +{ + long pos; // Current position in the IFF file. + long oldpos; // Record of start of chunk offset. + long endpos; // end of file offset before we write our data + long value; + BOOL odd; // Is length odd? + char pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (short)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size long. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/REDALERT/WIN32LIB/IFF.H b/REDALERT/WIN32LIB/IFF.H new file mode 100644 index 000000000..51f8c5768 --- /dev/null +++ b/REDALERT/WIN32LIB/IFF.H @@ -0,0 +1,164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((long) ((long) d << 24) | ((long) c << 16) | ((long) b << 8) | (long)(a)) +#define IFFize_WORD(a) Reverse_Word(a) +#define IFFize_LONG(a) Reverse_Long(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + char Method; // Compression method (CompressionType). + char pad; // Reserved pad byte (always 0). + long Size; // Size of the uncompressed data. + short Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +int __cdecl Open_Iff_File(char const *filename); +void __cdecl Close_Iff_File(int fh); +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id); +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize); +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size); +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size); +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags); +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data=NULL); +unsigned long __cdecl Uncompress_Data(void const *src, void *dst); +void __cdecl Set_Uncomp_Buffer(int buffer_segment, int size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern void __cdecl Pack_2_Plane(void *buffer, void * pageptr, int planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Compress(void *source, void *dest, unsigned long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H diff --git a/REDALERT/WIN32LIB/INDEXTB.INC b/REDALERT/WIN32LIB/INDEXTB.INC new file mode 100644 index 000000000..36d8e03d6 --- /dev/null +++ b/REDALERT/WIN32LIB/INDEXTB.INC @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/REDALERT/WIN32LIB/IRANDOM.CPP b/REDALERT/WIN32LIB/IRANDOM.CPP new file mode 100644 index 000000000..03b0bd0d5 --- /dev/null +++ b/REDALERT/WIN32LIB/IRANDOM.CPP @@ -0,0 +1,183 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "misc.h" + + + +extern "C" unsigned long RandNumb = 0x12349876; + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + + + +unsigned char Random(void) +{ + __asm { + lea esi, [RandNumb] //; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 //; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 //; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 //; rcl byte 2 of RandNumb + cmc //; complement carry + sbb al,[esi] //; sbb byte 1 of RandNumb + shr al,1 //; sets carry + rcr [BYTE PTR esi],1 //; rcr byte 1 of RandNumb + mov al,[esi] //; reload byte 1 of RandNumb + xor al,[esi+1] //; xor with byte 2 of RandNumb + } +} + + + +int Get_Random_Mask(int maxval) +{ + __asm { + + bsr ecx,[maxval] // ; put bit position of highest bit in ecx + mov eax, 1 // ; set one bit on in eax + jz invalid // ; if BSR shows maxval==0, return eax=1 + inc ecx // ; get one bit higher than count showed + shl eax,cl // ; move our EAX bit into position + dec eax // ; dec it to create the mask. +invalid: + } +} + + + + + + + + + + + +#if (0) + +RandNumb DD 12349876H + +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END +#endif \ No newline at end of file diff --git a/REDALERT/WIN32LIB/KEYBOARD.H b/REDALERT/WIN32LIB/KEYBOARD.H new file mode 100644 index 000000000..8233bc49b --- /dev/null +++ b/REDALERT/WIN32LIB/KEYBOARD.H @@ -0,0 +1,675 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + unsigned char VKRemap[256]; // gives vk for any ascii char + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT | WWKEY_VK_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT | WWKEY_VK_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); +int KN_To_VK(int key); + +#endif diff --git a/REDALERT/WIN32LIB/KEYBOARD.INC b/REDALERT/WIN32LIB/KEYBOARD.INC new file mode 100644 index 000000000..58e521f11 --- /dev/null +++ b/REDALERT/WIN32LIB/KEYBOARD.INC @@ -0,0 +1,126 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/REDALERT/WIN32LIB/KEYSTRUC.INC b/REDALERT/WIN32LIB/KEYSTRUC.INC new file mode 100644 index 000000000..f230404fa --- /dev/null +++ b/REDALERT/WIN32LIB/KEYSTRUC.INC @@ -0,0 +1,159 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/REDALERT/WIN32LIB/LCWCOMP.ASM b/REDALERT/WIN32LIB/LCWCOMP.ASM new file mode 100644 index 000000000..1c3e6395a --- /dev/null +++ b/REDALERT/WIN32LIB/LCWCOMP.ASM @@ -0,0 +1,288 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: g:/library/wwlib32/misc/rcs/lcwcomp.asm 1.1 1994/04/11 15:31:10 jeff_wilson Exp $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : COMPRESS.ASM * +;* * +;* Programmer : Louis Castle * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT + +.MODEL FLAT + +;GLOBAL LCW_Compress :NEAR +externdef C LCW_Compress:near + +;CODESEG +.code + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +;*********************************************************** +; +; ULONG LCW_Compress(BYTE *source, BYTE *dest, ULONG length) +; +; returns the size of the compressed data in bytes +; +;* +LCW_Compress proc C source:DWORD, dest:DWORD, datasize:DWORD + + ;USES ebx,ecx,edx,edi,esi + + ;ARG source:DWORD + ;ARG dest:DWORD + ;ARG datasize:DWORD + + LOCAL inlen:DWORD + LOCAL a1stdest:DWORD + LOCAL a1stsrc:DWORD + LOCAL lenoff:DWORD + LOCAL ndest:DWORD + LOCAL count:DWORD + LOCAL matchoff:DWORD + LOCAL end_of_data:DWORD + + pushad + + cld + mov edi,[dest] + mov esi,[source] + mov edx,[datasize] ; get length of data to compress + +; mov ax,ds +; mov es,ax + +; +; compress data to the following codes in the format b = byte, w = word +; n = byte code pulled from compressed data +; Bit field of n command description +; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 +; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes +; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 +; n=11111111,w1,w2 long run run w1 bytes from offset w2 +; n=10000000 end end of data reached +; + cld ; make sure all string commands are forward + mov ebx,esi + add ebx,edx + mov [end_of_data],ebx + mov [inlen],1 ; set the in-length flag + mov [a1stdest],edi ; save original dest offset for size calc + mov [a1stsrc],esi ; save offset of first byte of data + mov [lenoff],edi ; save the offset of the legth of this len + sub eax,eax + mov al,081h ; the first byte is always a len + stosb ; write out a len of 1 + lodsb ; get the byte + stosb ; save it +_loop: + mov [ndest],edi ; save offset of compressed data + mov edi,[a1stsrc] ; get the offset to the first byte of data + mov [count],1 ; set the count of run to 0 +searchloop: + sub eax,eax + mov al,[esi] ; get the current byte of data + cmp al,[esi+64] + jne short notrunlength + + mov ebx,edi + + mov edi,esi + mov ecx,[end_of_data] + sub ecx,edi + repe scasb + dec edi + mov ecx,edi + sub ecx,esi + cmp ecx,65 + jb short notlongenough + + mov [DWORD PTR inlen],0 ; clear the in-length flag + mov esi,edi + mov edi,[ndest] ; get the offset of our compressed data + + mov ah,al + mov al,0FEh + stosb + xchg ecx,eax + stosw + mov al,ch + stosb + + mov [ndest],edi ; save offset of compressed data + mov edi,ebx + jmp searchloop +notlongenough: + mov edi,ebx +notrunlength: + +oploop: + mov ecx,esi ; get the address of the last byte +1 + sub ecx,edi ; get the total number of bytes left to comp + jz short searchdone + + repne scasb ; look for a match + jne short searchdone ; if we don't find one we're done + + mov ebx,[count] + mov ah,[esi+ebx-1] + cmp ah,[edi+ebx-2] + + jne oploop + + mov edx,esi ; save this spot for the next search + mov ebx,edi ; save this spot for the length calc + dec edi ; back up one for compare + mov ecx,[end_of_data] ; get the end of data + sub ecx,esi ; sub current source for max len + + repe cmpsb ; see how many bytes match + +; start of change MH 9-24-91 + jne short notend ; if found mismatch then di - bx = match count + + inc edi ; else cx = 0 and di + 1 - bx = match count + +notend: +; end of change MH 9-24-91 + + mov esi,edx ; restore si + mov eax,edi ; get the dest + sub eax,ebx ; sub the start for total bytes that match + mov edi,ebx ; restore dest + cmp eax,[count] ; see if its better than before + jb searchloop ; if not keep looking + + mov [count],eax ; if so keep the count + dec ebx ; back it up for the actual match offset + mov [matchoff],ebx ; save the offset for later + jmp searchloop ; loop until we searched it all + +searchdone: + + mov ecx,[count] ; get the count of the longest run + mov edi,[ndest] ; get the offset of our compressed data + cmp ecx,2 ; see if its not enough run to matter + jbe short lenin ; if its 0,1, or 2 its too small + + cmp ecx,10 ; if not, see if it would fit in a short + ja short medrun ; if not, see if its a medium run + + mov eax,esi ; if its short get the current address + sub eax,[matchoff] ; sub the offset of the match + cmp eax,0FFFh ; if its less than 12 bits its a short + ja short medrun ; if its not, its a medium + +shortrun: + sub ebx,ebx + mov bl,cl ; get the length (3-10) + sub bl,3 ; sub 3 for a 3 bit number 0-7 + shl bl,4 ; shift it left 4 + add ah,bl ; add in the length for the high nibble + xchg ah,al ; reverse the bytes for a word store + jmp short srunnxt ; do the run fixup code + +medrun: + cmp ecx,64 ; see if its a short run + ja short longrun ; if not, oh well at least its long + + sub cl,3 ; back down 3 to keep it in 6 bits + or cl,0C0h ; the highest bits are always on + mov al,cl ; put it in al for the stosb + stosb ; store it + jmp short medrunnxt ; do the run fixup code + +lenin: + cmp [DWORD PTR inlen],0 ; is it doing a length? + jnz short len ; if so, skip code + +lenin1: + mov [lenoff],edi ; save the length code offset + mov al,80h ; set the length to 0 + stosb ; save it + +len: + mov ebx,[lenoff] ; get the offset of the length code + cmp BYTE PTR [ebx],0BFh ; see if its maxed out + je lenin1 ; if so put out a new len code + +stolen: + inc BYTE PTR [ebx] ; inc the count code + lodsb ; get the byte + stosb ; store it + mov DWORD PTR [inlen],1 ; we are now in a length so save it + jmp short nxt ; do the next code + +longrun: + mov al,0ffh ; its a long so set a code of FF + stosb ; store it + + mov eax,[count] ; send out the count + stosw ; store it +medrunnxt: + mov eax,[matchoff] ; get the offset + sub eax,[a1stsrc] ; make it relative tot he start of data +srunnxt: + stosw ; store it +; this code common to all runs + add esi,[count] ; add in the length of the run to the source + mov DWORD PTR [inlen],0 ; set the in leght flag to false + +;======================================================================= + +nxt: + cmp esi,[end_of_data] ; see if we did the whole pic + jae short _out ; if so, cool! were done + + jmp _loop + +_out: + mov ax,080h ; remember to send an end of data code + stosb ; store it + mov eax,edi ; get the last compressed address + sub eax,[a1stdest] ; sub the first for the compressed size + + popad + ret + +LCW_Compress endp + + +END diff --git a/REDALERT/WIN32LIB/LCWUNCMP.ASM b/REDALERT/WIN32LIB/LCWUNCMP.ASM new file mode 100644 index 000000000..d7a7298bb --- /dev/null +++ b/REDALERT/WIN32LIB/LCWUNCMP.ASM @@ -0,0 +1,294 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: g:/library/wwlib32/misc/rcs/lcwuncmp.asm 1.1 1994/04/11 15:31:21 jeff_wilson Exp $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT +.MODEL FLAT + +;GLOBAL C LCW_Uncompress :NEAR +externdef C LCW_Uncompress:NEAR + +;CODESEG +.code + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +LCW_Uncompress proc C source:DWORD, dest:DWORD, _length:DWORD + + ;USES ebx,ecx,edx,edi,esi + + ;ARG source:DWORD + ;ARG dest:DWORD + ;ARG length:DWORD +;LOCALS + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + ;LOCAL lastcom:DWORD + ;LOCAL lastcom1:DWORD + + + pushad + + mov edi,[dest] + mov esi,[source] + mov edx,[_length] + +; +; +; uncompress data to the following codes in the format b = byte, w = word +; n = byte code pulled from compressed data +; Bit field of n command description +; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 +; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes +; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 +; n=11111111,w1,w2 long copy copy w1 bytes from offset w2 +; n=11111110,w1,b1 long run run byte b1 for w1 bytes +; n=10000000 end end of data reached +; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + +??loop: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short ??out ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short ??notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp ??loop + +??notshort: + test al,40h ; is it a length? + jne short ??notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short ??out ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp ??exit + +??notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short ??notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runlenok: + test ecx,0ffe0h + jnz ??dont_use_stosb + rep stosb + jmp ??loop + + +??dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz ??aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + +??aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz ??loop + mov ecx,edx + rep stosb + jmp ??loop + + + + + + +??notrunlength: + cmp al,0FFh ; is it a long run? + jne short ??notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + +??notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short ??runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runok: + test ecx,0ffe0h + jnz ??dont_use_movsb + rep movsb + jmp ??loop + + + + +??dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja ??use_movsb + + test edi,3 + jz ??aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + +??aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx +??use_movsb: + rep movsb + jmp ??loop + + + + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + popad + ret + +LCW_Uncompress endp + +;*********************************************************** + + + END diff --git a/REDALERT/WIN32LIB/LOAD.CPP b/REDALERT/WIN32LIB/LOAD.CPP new file mode 100644 index 000000000..1a1055876 --- /dev/null +++ b/REDALERT/WIN32LIB/LOAD.CPP @@ -0,0 +1,375 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include +#include +#include +#include + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + void *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * void * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize=0; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((short *)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * void *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_Word(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((short*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_Word(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Uncompress_Data(void const *src, void *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_Long(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_Word(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((void *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((void *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((void *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((void *) src, (void *) dst, (unsigned long) uncomp_size); + break; + + } + + return(uncomp_size); +} + + diff --git a/REDALERT/WIN32LIB/LOADFONT.CPP b/REDALERT/WIN32LIB/LOADFONT.CPP new file mode 100644 index 000000000..87cd7f676 --- /dev/null +++ b/REDALERT/WIN32LIB/LOADFONT.CPP @@ -0,0 +1,137 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include +#include +#include + +#if(IBM) +#include +#include + +#include + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +char FontWidth = 8; +char FontHeight = 8; + +// only font.c and set_font.c use the following +char *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Load_Font(char const *name) +{ + char valid; + int fh; // DOS file handle for font file. + unsigned short size; // Size of the data in the file (-2); + char *ptr = NULL; // Pointer to newly loaded font. + + + + fh=Open_File(name,READ); + if ( fh>=0 ){ + if ( Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size , MEM_NORMAL ); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return ((void*)errno); + } + + + +#ifdef cuts + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size, MEM_NORMAL); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } +#endif + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} + +#endif diff --git a/REDALERT/WIN32LIB/MCGAPRIM.INC b/REDALERT/WIN32LIB/MCGAPRIM.INC new file mode 100644 index 000000000..a730e411d --- /dev/null +++ b/REDALERT/WIN32LIB/MCGAPRIM.INC @@ -0,0 +1,119 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/MEMFLAG.H b/REDALERT/WIN32LIB/MEMFLAG.H new file mode 100644 index 000000000..dbd93ab90 --- /dev/null +++ b/REDALERT/WIN32LIB/MEMFLAG.H @@ -0,0 +1,106 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/* +** Prototypes for VMPAGEIN.ASM +*/ +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + +//#pragma option -Jgd + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + +//#pragma option -Jgd + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +extern "C" { + void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); +} + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif diff --git a/REDALERT/WIN32LIB/MISC.H b/REDALERT/WIN32LIB/MISC.H new file mode 100644 index 000000000..39567a172 --- /dev/null +++ b/REDALERT/WIN32LIB/MISC.H @@ -0,0 +1,268 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +#define WIN32_LEAN_AND_MEAN // eliminates unecessary definitions in windows.h +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include + +extern LPDIRECTDRAWSURFACE PaletteSurface; + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: DDRAW.CPP */ +/*=========================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg); +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +unsigned Get_Free_Video_Memory(void); +void Wait_Blit(void); +unsigned Get_Video_Hardware_Capabilities(void); + +extern "C" void Wait_Vert_Blank(void); +extern "C" void Set_DD_Palette (void *palette); + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void); +/* +** Pointer to function to call if we detect a surface restore +*/ +extern void (*Misc_Focus_Restore_Function)(void); + + +/* + * Flags returned by Get_Video_Hardware_Capabilities + */ +/* Hardware blits supported? */ +#define VIDEO_BLITTER 1 + +/* Hardware blits asyncronous? */ +#define VIDEO_BLITTER_ASYNC 2 + +/* Can palette changes be synced to vertical refresh? */ +#define VIDEO_SYNC_PALETTE 4 + +/* Is the video cards memory bank switched? */ +#define VIDEO_BANK_SWITCHED 8 + +/* Can the blitter do filled rectangles? */ +#define VIDEO_COLOR_FILL 16 + +/* Is there no hardware assistance avaailable at all? */ +#define VIDEO_NO_HARDWARE_ASSIST 32 + + + +/* + * Definition of surface monitor class + * + * This class keeps track of all the graphic buffers we generate in video memory so they + * can be restored after a focus switch. +*/ + +#define MAX_SURFACES 20 + +class SurfaceMonitorClass { + + public: + + SurfaceMonitorClass(); + + void Add_DD_Surface (LPDIRECTDRAWSURFACE); + void Remove_DD_Surface (LPDIRECTDRAWSURFACE); + BOOL Got_Surface_Already (LPDIRECTDRAWSURFACE); + void Restore_Surfaces (void); + void Set_Surface_Focus ( BOOL in_focus ); + void Release(void); + + BOOL SurfacesRestored; + + private: + + LPDIRECTDRAWSURFACE Surface[MAX_SURFACES]; + BOOL InFocus; + +}; + +extern SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces + + +/*=========================================================================*/ +/* The following variables are declared in: DDRAW.CPP */ +/*=========================================================================*/ +extern LPDIRECTDRAW DirectDrawObject; +extern LPDIRECTDRAW2 DirectDraw2Interface; +extern HWND MainWindow; +extern BOOL SystemToVideoBlits; +extern BOOL VideoToSystemBlits; +extern BOOL SystemToSystemBlits; +extern BOOL OverlappedVideoBlits; // Can video driver blit overlapped regions? + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +void __cdecl Prog_End(const char *why, bool fatal); // Added why and fatal. ST - 8/7/2019 10:54AM +VOID __cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +unsigned char __cdecl Random(void); +int __cdecl Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void __cdecl Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +long __cdecl Reverse_Long(long number); +short __cdecl Reverse_Short(short number); +long __cdecl Swap_Long(long number); +#if (0) +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ +#endif + +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long __cdecl Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD __cdecl Processor(void); +extern WORD __cdecl Operating_System(void); +extern unsigned long random ( unsigned long mod ) ; +//extern void randomize ( void ) ; + +extern int __cdecl Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int __cdecl Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H diff --git a/REDALERT/WIN32LIB/MODEMREG.H b/REDALERT/WIN32LIB/MODEMREG.H new file mode 100644 index 000000000..4e3b81a61 --- /dev/null +++ b/REDALERT/WIN32LIB/MODEMREG.H @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef WIN32 +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif //WIN32 +#include + + + +class ModemRegistryEntryClass { + + public: + + ModemRegistryEntryClass (int modem_number); + ~ModemRegistryEntryClass (void); + + + char *Get_Modem_Name (void) { return (ModemName); } + + char *Get_Modem_Device_Name (void) { return (ModemDeviceName); } + + char *Get_Modem_Error_Correction_Enable (void) { return (ErrorCorrectionEnable); } + + char *Get_Modem_Error_Correction_Disable (void) { return (ErrorCorrectionDisable); } + + char *Get_Modem_Compression_Enable (void) { return (CompressionEnable); } + + char *Get_Modem_Compression_Disable (void) { return (CompressionDisable); } + + char *Get_Modem_Hardware_Flow_Control (void) { return (HardwareFlowControl); } + + char *Get_Modem_No_Flow_Control (void) { return (HardwareFlowControl); } + + private: + + char *ModemName; + char *ModemDeviceName; + char *ErrorCorrectionEnable; + char *ErrorCorrectionDisable; + char *CompressionEnable; + char *CompressionDisable; + char *HardwareFlowControl; + char *NoFlowControl; + +}; + + + + + + + diff --git a/REDALERT/WIN32LIB/MONO.H b/REDALERT/WIN32LIB/MONO.H new file mode 100644 index 000000000..3a5baa1b8 --- /dev/null +++ b/REDALERT/WIN32LIB/MONO.H @@ -0,0 +1,182 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.16 06 Sep 1995 16:29:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif + diff --git a/REDALERT/WIN32LIB/MOUSE.H b/REDALERT/WIN32LIB/MOUSE.H new file mode 100644 index 000000000..b40d07e72 --- /dev/null +++ b/REDALERT/WIN32LIB/MOUSE.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +class WWMouseClass { + public: + WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, void *cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicViewPortClass *scr); + void Erase_Mouse(GraphicViewPortClass *scr, int forced = FALSE); + + void Block_Mouse(GraphicBufferClass *buffer); + void Unblock_Mouse(GraphicBufferClass *buffer); + void Set_Cursor_Clip(void); + void Clear_Cursor_Clip(void); + + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicViewPortClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + CRITICAL_SECTION MouseCriticalSection; // Control for mouse re-enterancy + unsigned TimerHandle; + +}; + +extern "C" { + void __cdecl Mouse_Shadow_Buffer(void *thisptr, GraphicViewPortClass *srcdst, void *buffer, int x, int y, int hotx, int hoty, int store); + void __cdecl Draw_Mouse(void *thisptr, GraphicViewPortClass *srcdst, int x, int y); + void * __cdecl ASM_Set_Mouse_Cursor(void * thisptr, int hotspotx, int hotspoty, VOID *cursor); +}; + +void Hide_Mouse(void); +void Show_Mouse(void); +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/REDALERT/WIN32LIB/MOUSE.INC b/REDALERT/WIN32LIB/MOUSE.INC new file mode 100644 index 000000000..c8031cbc6 --- /dev/null +++ b/REDALERT/WIN32LIB/MOUSE.INC @@ -0,0 +1,64 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/REDALERT/WIN32LIB/MOUSEWW.CPP b/REDALERT/WIN32LIB/MOUSEWW.CPP new file mode 100644 index 000000000..bdb952b10 --- /dev/null +++ b/REDALERT/WIN32LIB/MOUSEWW.CPP @@ -0,0 +1,720 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); +extern bool GameInFocus; + + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + InitializeCriticalSection (&MouseCriticalSection); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); // Removed. ST - 2/13/2019 5:12PM + + /* + ** Force the windows mouse pointer to stay withing the graphic view port region + */ + Set_Cursor_Clip(); +} + +WWMouseClass::~WWMouseClass() +{ + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + timeEndPeriod (1000/60); + DeleteCriticalSection(&MouseCriticalSection); + + /* + ** Free up the windows mouse pointer movement + */ + Clear_Cursor_Clip(); +} + + +void Block_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Block_Mouse(buffer); + } +} + + +void Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Unblock_Mouse(buffer); + } +} + + + +void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + EnterCriticalSection(&MouseCriticalSection); + } +} + + +void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + LeaveCriticalSection(&MouseCriticalSection); + } +} + + + + + +void WWMouseClass::Set_Cursor_Clip(void) +{ +#if (0) // Not needed. ST - 1/3/2019 2:18PM + if (Screen){ + RECT region; + + region.left = 0; + region.top = 0; + region.right = Screen->Get_Width(); + region.bottom = Screen->Get_Height(); + + ClipCursor(®ion); + } +#endif +} + + + +void WWMouseClass::Clear_Cursor_Clip(void) +{ +#if (0) + ClipCursor(NULL); +#endif +} + + + +void WWMouseClass::Process_Mouse(void) +{ +//ST - 1/3/2019 10:50AM + return; +#if (0) + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!Screen || !_Mouse || State > 0 || MouseUpdate || EraseFlags || !GameInFocus) + return; + + // + // Make sure there are no conflicts with other + // threads that may try and lock the screen buffer + // + //Block_Mouse(Screen->Get_Graphic_Buffer()); + + // + // If the screen is already locked by another thread then just exit + // + if (Screen->Get_LockCount()!=0){ + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); + return; + } + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } + + // + // Allow other threads to lock the screen again + // + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); +#endif +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ +//ST - 1/3/2019 10:50AM + xhotspot; + yhotspot; + cursor; + return cursor; + +#if (0) + + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +#endif +} + +void WWMouseClass::Low_Hide_Mouse() +{ +//ST - 1/3/2019 10:50AM +#if (0) + if (!State) { + if (MouseBuffX != -1 || MouseBuffY != -1) { + if (Screen->Lock()){ + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + Screen->Unlock(); + } + } + MouseBuffX = -1; + MouseBuffY = -1; + } +#endif + State++; +} +void WWMouseClass::Hide_Mouse() +{ + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + +//ST - 1/3/2019 10:50AM +#if (0) + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + if (Screen->Lock()){ + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } + } +#endif +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) { + Low_Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; +} + + +void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr) +{ + scr; + return; +//ST - 1/3/2019 10:50AM +#if (0) + + POINT pt; + + if (State != 0) return; + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen - dont do video stuff if we cant. + // + if (scr->Lock()){ + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + } + + MouseUpdate--; +#endif +} + +void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced) +{ +//ST - 1/3/2019 10:50AM + scr; + forced; + return; +#if (0) + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + if (scr->Lock()){ + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + scr->Unlock(); + } + EraseBuffX = -1; + EraseBuffY = -1; + } + } + MouseUpdate--; + EraseFlags = FALSE; +#endif +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +//#pragma off(unreferenced) + +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + static BOOL in_mouse_callback = false; + + if (_Mouse && !in_mouse_callback) { + in_mouse_callback = TRUE; + _Mouse->Process_Mouse(); + in_mouse_callback = FALSE; + } +} +//#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +extern int DLLForceMouseX; +extern int DLLForceMouseY; + + +int Get_Mouse_X(void) +{ + if (DLLForceMouseX >= 0) { + return DLLForceMouseX; + } + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (DLLForceMouseY >= 0) { + return DLLForceMouseY; + } + + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} \ No newline at end of file diff --git a/REDALERT/WIN32LIB/NYBBTB.INC b/REDALERT/WIN32LIB/NYBBTB.INC new file mode 100644 index 000000000..48444c49d --- /dev/null +++ b/REDALERT/WIN32LIB/NYBBTB.INC @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/REDALERT/WIN32LIB/PALETTE.CPP b/REDALERT/WIN32LIB/PALETTE.CPP new file mode 100644 index 000000000..3c8b92442 --- /dev/null +++ b/REDALERT/WIN32LIB/PALETTE.CPP @@ -0,0 +1,381 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +//#include + +#include "palette.h" +#include "timer.h" +#include "wwstd.h" + + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate); +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * void *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette(void *palette) +{ + + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * void *palette - palette to set color in * + * int color - which color index to set * + * void *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette_Color(void *palette, int color, void *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(unsigned short*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * char *palette1 - this is the palette to fade to. * + * unsigned int delay - fade with this timer count down * + * void *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + short jump; // Gun values to jump per palette set. + unsigned long timer; // Tick count timer used for timing. + short ticksper; // The ticks (fixed point) per bit jump. + int tickaccum; + + + extern void (*cb_ptr)(void); // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < (int)timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * unsigned char *palette - palette to fade to * + * int delay - desired time delay in 60ths of a second * + * short *ticks - output: loop ticks per color jump * + * short *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, + short *rate) +{ + int gun1; // Palette 1 gun value. + int gun2; // Palette 2 gun value. + int diff; // Maximum color gun difference. + int tp; // Temporary tick accumulator. + int index; // Color gun working index. + long t; // Working tick intermediate value. + int adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((long)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (short)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + int index; // Index to DAC register gun. + int gun1,gun2; // Palette 1 gun value. + unsigned char palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = (unsigned char)gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +void (*cb_ptr)(void); // callback function pointer + +/**************************** End of palette.cpp ***************************/ + + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/PALETTE.H b/REDALERT/WIN32LIB/PALETTE.H new file mode 100644 index 000000000..6087d0d64 --- /dev/null +++ b/REDALERT/WIN32LIB/PALETTE.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +#include +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +void __cdecl Set_Palette(void *palette); +void __cdecl Set_Palette_Color(void *palette, int color, void *data); +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +void __cdecl Morph_Palette (void *src_palette, void *dst_palette, unsigned int delay, + void *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern void __cdecl Set_Palette_Range(void *palette); +extern BOOL __cdecl Bump_Color(void *palette, int changable, int target); + +#ifdef __cplusplus +} +#endif +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ diff --git a/REDALERT/WIN32LIB/PLAYCD.H b/REDALERT/WIN32LIB/PLAYCD.H new file mode 100644 index 000000000..0c5a68d85 --- /dev/null +++ b/REDALERT/WIN32LIB/PLAYCD.H @@ -0,0 +1,306 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + + +#ifdef NOT_FOR_WIN95 +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +}; + + + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + + + + + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#ifdef NOT_FOR_WIN95 +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + +#endif //NOT_FOR_WIN95 +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/REDALERT/WIN32LIB/PROFILE.H b/REDALERT/WIN32LIB/PROFILE.H new file mode 100644 index 000000000..bc20add58 --- /dev/null +++ b/REDALERT/WIN32LIB/PROFILE.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Library profiler * + * * + * File Name : PROFILE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define MAX_PROFILE_TIME 60*1 //1 minute(s) @ 14.4 Mb per hour +#define PROFILE_RATE 1000 //samples per sec (max 1000) + + +/* + * Defines for choosing between the old and new profiler system + * +*/ + +#define OLD_PROFILE_SYSTEM 1 +#define NEW_PROFILE_SYSTEM 2 + +//#define PROFILE_SYSTEM OLD_PROFILE_SYSTEM +#define PROFILE_SYSTEM NEW_PROFILE_SYSTEM + + + +extern "C"{ + void __cdecl Profile_Init(void); + void __cdecl Profile_End(void); + void __cdecl Start_Profiler(void); + void __cdecl Stop_Profiler(void); +} + diff --git a/REDALERT/WIN32LIB/PROFILE.INC b/REDALERT/WIN32LIB/PROFILE.INC new file mode 100644 index 000000000..ec99294c2 --- /dev/null +++ b/REDALERT/WIN32LIB/PROFILE.INC @@ -0,0 +1,41 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;USE_PROFILER =1 + + +macro prologue +Ifdef USE_PROFILER + global __PRO:near + + call __PRO +endif ;USE_PROFILER +endm + + + +macro epilogue +ifdef USE_PROFILER + global __EPI:near + + push ebp + call __EPI + pop ebp +endif ;USE_PROFILER +endm + + + diff --git a/REDALERT/WIN32LIB/RAWFILE.H b/REDALERT/WIN32LIB/RAWFILE.H new file mode 100644 index 000000000..a6583902e --- /dev/null +++ b/REDALERT/WIN32LIB/RAWFILE.H @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.15 06 Sep 1995 16:29:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include + +//#include +#include +#include +#include +//#include +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void) {if (Allocated && Filename) free((char *)Filename);}; + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + virtual void Set_Buffer_Size(int size); + + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return Filename; +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Filename(0) +{ + Handle = -1; + Allocated = false; +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle != -1); +} + +#endif diff --git a/REDALERT/WIN32LIB/SET_FONT.CPP b/REDALERT/WIN32LIB/SET_FONT.CPP new file mode 100644 index 000000000..ed1134209 --- /dev/null +++ b/REDALERT/WIN32LIB/SET_FONT.CPP @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : SET_FONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Font -- Changes the default text printing font. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include + + + +/*************************************************************************** + * SET_FONT -- Changes the default text printing font. * + * * + * This routine will change the default text printing font for all * + * text output. It handles updating the system where necessary. * + * * + * INPUT: fontptr -- Pointer to the font to change to. * + * * + * OUTPUT: Returns with a pointer to the previous font. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/06/1991 JLB : Created. * + * 09/17/1991 JLB : Fixed return value bug. * + * 01/31/1992 DRD : Modified to use new font format. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Set_Font(void const *fontptr) +{ + void *oldfont; + char const *blockptr; + + oldfont = (void *) FontPtr; + + if (fontptr) { + FontPtr = (void *) fontptr; + + /* + ** Inform the system about the new font. + */ + + FontWidthBlockPtr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTWIDTHBLOCK); + blockptr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTINFOBLOCK); + FontHeight = *(blockptr + FONTINFOMAXHEIGHT); + FontWidth = *(blockptr + FONTINFOMAXWIDTH); + //Draw_Char_Setup(); + +#if FALSE + WindowLines = WinH / FontHeight; + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / FontWidth; +#endif + } + + return(oldfont); +} diff --git a/REDALERT/WIN32LIB/SHAPE.H b/REDALERT/WIN32LIB/SHAPE.H new file mode 100644 index 000000000..44d8050a5 --- /dev/null +++ b/REDALERT/WIN32LIB/SHAPE.H @@ -0,0 +1,184 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum : unsigned short { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +inline MakeShapeFlags_Type operator|(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline MakeShapeFlags_Type operator&(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline MakeShapeFlags_Type operator~(MakeShapeFlags_Type a) +{return static_cast(~static_cast(a));} + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum : unsigned short { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_BOTTOM = 0x0040, // Y coord is based on shape's bottom pt + SHAPE_FADING = 0x0100, // Fading effect (void * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (void * color_table) +} ShapeFlags_Type; + +inline ShapeFlags_Type operator|(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline ShapeFlags_Type operator&(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ShapeFlags_Type operator~(ShapeFlags_Type a) +{return static_cast(~static_cast(a));} + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + unsigned short ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + unsigned char Height; // Height of the shape in scan lines + unsigned short Width; // Width of the shape in bytes + unsigned char OriginalHeight; // Original height of shape in scan lines + unsigned short ShapeSize; // Size of the shape, including header + unsigned short DataLength; // Size of the uncompressed shape (just data) + unsigned char Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +#pragma warning (disable : 4200) +typedef struct { + unsigned short NumShapes; // number of shapes in the block + long Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern void *MaskPage; +extern void *BackGroundPage; +extern long _ShapeBufferSize; +extern char *_ShapeBuffer; +} + + +void __cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +int Draw_Shape(GraphicViewPortClass *gvp, void const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short __cdecl Get_Shape_Data(void const *shape, int data); +int __cdecl Extract_Shape_Count(void const *buffer); +void * __cdecl Extract_Shape(void const *buffer, int shape); +int __cdecl Restore_Shape_Height(void *shape); +int __cdecl Set_Shape_Height(void const *shape, int newheight); + +extern "C" { +int __cdecl Get_Shape_Width(void const *shape); +int __cdecl Get_Shape_Height(void const *shape); +int __cdecl Get_Shape_Original_Height(void const *shape); +int __cdecl Get_Shape_Uncomp_Size(void const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +void __cdecl Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +int __cdecl Get_Shape_Flags(void const *shape); +int __cdecl Get_Shape_Size(void const *shape); +int __cdecl Get_Shape_Scaled_Width(void const *shape, int scale); +int __cdecl Get_Shape_Scaled_Height(void const *shape, int scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/SHAPE.INC b/REDALERT/WIN32LIB/SHAPE.INC new file mode 100644 index 000000000..f1cb250e9 --- /dev/null +++ b/REDALERT/WIN32LIB/SHAPE.INC @@ -0,0 +1,212 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_BOTTOM EQU 0040h ; Y coord is based on shape bottom pt +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL C ShapeBuffer:DWORD +GLOBAL C ShapeBufferSize:DWORD +GLOBAL C _MaskPage:DWORD +GLOBAL C _BackGroundPage:DWORD +GLOBAL C PredCount:DWORD +GLOBAL C PredTable:BYTE +GLOBAL C PredValue:DWORD +GLOBAL C PartialPred:DWORD +GLOBAL C PartialCount:DWORD +GLOBAL C Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/REDALERT/WIN32LIB/SOS.H b/REDALERT/WIN32LIB/SOS.H new file mode 100644 index 000000000..414d0dee9 --- /dev/null +++ b/REDALERT/WIN32LIB/SOS.H @@ -0,0 +1,562 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far __cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far __cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif \ No newline at end of file diff --git a/REDALERT/WIN32LIB/SOSCOMP.H b/REDALERT/WIN32LIB/SOSCOMP.H new file mode 100644 index 000000000..73ac03a5b --- /dev/null +++ b/REDALERT/WIN32LIB/SOSCOMP.H @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void __cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long __cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl General_sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif diff --git a/REDALERT/WIN32LIB/SOSDATA.H b/REDALERT/WIN32LIB/SOSDATA.H new file mode 100644 index 000000000..5f8f9eefe --- /dev/null +++ b/REDALERT/WIN32LIB/SOSDATA.H @@ -0,0 +1,125 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/REDALERT/WIN32LIB/SOSDEFS.H b/REDALERT/WIN32LIB/SOSDEFS.H new file mode 100644 index 000000000..a3b592c9a --- /dev/null +++ b/REDALERT/WIN32LIB/SOSDEFS.H @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/REDALERT/WIN32LIB/SOSFNCT.H b/REDALERT/WIN32LIB/SOSFNCT.H new file mode 100644 index 000000000..fbfad6fc1 --- /dev/null +++ b/REDALERT/WIN32LIB/SOSFNCT.H @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int __cdecl sosRealFree ( int ); +extern BOOL __cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int __cdecl sosRealAlloc( int, int *, int * ); +extern void __cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int __cdecl sosGetCS( VOID ); +extern int __cdecl sosGetES( VOID ); +#else +extern int __cdecl sosRealAlloc ( int, int *, int * ); +extern int __cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD __cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD __cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void __cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void __cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void __cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void __cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void __cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void __cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void __cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void __cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD __cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD __cdecl sosDIGIInitForWindows( WORD ); +extern WORD __cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR __cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR __cdecl sosCreateAliasCS ( LPSTR ); +extern VOID __cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR __cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID __cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR __cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID __cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID __cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD __cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD __cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD __cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD __cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD __cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD __cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD __cdecl getDS( VOID ); +extern WORD __cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD __cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void __cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void __cdecl sosTIMERDRVUnInit( void ); +extern void __cdecl sosTIMERDRVHandler( void ); +extern void __cdecl sosTIMERDRVFHandler( void ); +extern void __cdecl sosTIMERDRVEnable( void ); +extern void __cdecl sosTIMERDRVDisable( void ); +extern void __cdecl sosTIMERDRVCallOld( void ); +extern void __cdecl sosTIMERDRVSetRate( WORD ); +extern void __cdecl sosDIGITimer_Start( void ); +extern void __cdecl sosDIGITimer_End( void ); +extern void __cdecl sosDIGIDrv_Start( void ); +extern void __cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif + diff --git a/REDALERT/WIN32LIB/SOSRES.H b/REDALERT/WIN32LIB/SOSRES.H new file mode 100644 index 000000000..0584fc90b --- /dev/null +++ b/REDALERT/WIN32LIB/SOSRES.H @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/SOUND.H b/REDALERT/WIN32LIB/SOUND.H new file mode 100644 index 000000000..ff7258209 --- /dev/null +++ b/REDALERT/WIN32LIB/SOUND.H @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" +#include "dsound.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/REDALERT/WIN32LIB/SOUNDINT.H b/REDALERT/WIN32LIB/SOUNDINT.H new file mode 100644 index 000000000..76171dfd1 --- /dev/null +++ b/REDALERT/WIN32LIB/SOUNDINT.H @@ -0,0 +1,289 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/REDALERT/WIN32LIB/STAMP.INC b/REDALERT/WIN32LIB/STAMP.INC new file mode 100644 index 000000000..c629e2350 --- /dev/null +++ b/REDALERT/WIN32LIB/STAMP.INC @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + +ifdef NO_WAY_THIS_WILL_BE_DEFINED_HAHAHAHA + + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +else + + + STRUC IControl_Type + +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? +MapHeight DW ? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? +Map DD ? ; Icon map offset. + ENDS + +endif + +ICON256 EQU 1 diff --git a/REDALERT/WIN32LIB/STRUCTS.H b/REDALERT/WIN32LIB/STRUCTS.H new file mode 100644 index 000000000..995ebb542 --- /dev/null +++ b/REDALERT/WIN32LIB/STRUCTS.H @@ -0,0 +1,34 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/REDALERT/WIN32LIB/SVGAPRIM.INC b/REDALERT/WIN32LIB/SVGAPRIM.INC new file mode 100644 index 000000000..33813ca90 --- /dev/null +++ b/REDALERT/WIN32LIB/SVGAPRIM.INC @@ -0,0 +1,74 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/REDALERT/WIN32LIB/TILE.H b/REDALERT/WIN32LIB/TILE.H new file mode 100644 index 000000000..30b7f9a89 --- /dev/null +++ b/REDALERT/WIN32LIB/TILE.H @@ -0,0 +1,91 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); +#if (0) +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char *Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char *Map; // Icon map offset (if present). +} IControl_Type; +#endif //(0) + +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; + + +#endif //TILE_H + diff --git a/REDALERT/WIN32LIB/TIMER.CPP b/REDALERT/WIN32LIB/TIMER.CPP new file mode 100644 index 000000000..fb1cc38fe --- /dev/null +++ b/REDALERT/WIN32LIB/TIMER.CPP @@ -0,0 +1,200 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + TickType=timer; + + if (on && TimerSystemOn) Start(); +} + + + + +/*********************************************************************************************** + * TC:Get_Ticks -- return the number of ticks on the system or user timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:17PM ST : Created * + *=============================================================================================*/ + +long TimerClass::Get_Ticks ( void ) + +{ + if ( WindowsTimer ){ + switch ( TickType ){ + + case BT_SYSTEM : + return ( WindowsTimer->Get_System_Tick_Count() ); + + case BT_USER : + return ( WindowsTimer->Get_User_Tick_Count() ); + + } + } + return 0; +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} \ No newline at end of file diff --git a/REDALERT/WIN32LIB/TIMER.H b/REDALERT/WIN32LIB/TIMER.H new file mode 100644 index 000000000..fb5d98625 --- /dev/null +++ b/REDALERT/WIN32LIB/TIMER.H @@ -0,0 +1,198 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include +#include + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; +extern HANDLE TimerThreadHandle; //Handle of timer thread +extern int InTimerCallback; //true if we are currently in a callback + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: +// long (*Get_Ticks)(void); // System timer fetch. + BaseTimerEnum TickType; + long Get_Ticks (void); +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + + +class WinTimerClass { + + public: + WinTimerClass ( UINT freq=60 , BOOL partial=0 ); + ~WinTimerClass(); + + void Update_Tick_Count ( void ); + unsigned Get_System_Tick_Count ( void ); + unsigned Get_User_Tick_Count ( void ); + + private: + + unsigned TimerHandle; //Handle for windows timer event + unsigned Frequency; //Frequency of our windows timer in ticks per second + + unsigned TrueRate; //True rate of clock. (only use word) + unsigned SysTicks; //Tick count of timer. + unsigned UserTicks; //Tick count of timer. + unsigned UserRate; //Desired rate of timer. + + +}; + + +extern WinTimerClass *WindowsTimer; + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// +#ifndef FUNCTION_H +extern TimerClass TickCount; +#endif +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long __cdecl Get_System_Tick_Count(void); + long __cdecl Get_User_Tick_Count(void); + void far __cdecl Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void __cdecl Disable_Timer_Interrupt(void); + void __cdecl Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL __cdecl Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL __cdecl Remove_Timer_System(VOID); + + +#endif // TIMER_H + diff --git a/REDALERT/WIN32LIB/TIMERDWN.CPP b/REDALERT/WIN32LIB/TIMERDWN.CPP new file mode 100644 index 000000000..90fea6e97 --- /dev/null +++ b/REDALERT/WIN32LIB/TIMERDWN.CPP @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/REDALERT/WIN32LIB/TIMERINI.CPP b/REDALERT/WIN32LIB/TIMERINI.CPP new file mode 100644 index 000000000..f453fc84f --- /dev/null +++ b/REDALERT/WIN32LIB/TIMERINI.CPP @@ -0,0 +1,314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" +#include +#include + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + + +// Prototype for timer callback +void CALLBACK Timer_Callback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +HANDLE TimerThreadHandle = 0; //Handle of timer thread +int InTimerCallback = 0; //Flag to say if we are in a timer callback + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + + +#pragma warning (disable : 4996) + + +/*************************************************************************** + * WinTimerClass::WinTimerClass -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::WinTimerClass (UINT freq, BOOL partial) +{ + BOOL success; + + // + // Inform windows that we want a higher than normal + // timer resolution + // +#ifdef __SW_EP + timeBeginPeriod(1000/PROFILE_RATE); + Frequency = PROFILE_RATE; +#else + timeBeginPeriod ( 1000/freq ); + Frequency = freq; +#endif + + SysTicks = 0; + UserTicks = 0; + + // + // Install the timer callback event handler + // + //TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC); + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + TimerSystemOn = success = ( TimerHandle !=0 ); + + if (success) { + if (!partial) { + WindowsTimer=this; + TickCount.Start(); + } + }else{ + char error_str [128]; + sprintf (error_str, "Error - timer system failed to start. Error code %d\n", GetLastError()); + OutputDebugString(error_str); + } +} + + + +/*************************************************************************** + * WinTimerClass::~WinTimerClass -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::~WinTimerClass( void ) +{ + + if ( TimerHandle ){ + timeKillEvent ( TimerHandle ); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + + TimerSystemOn = FALSE; + timeEndPeriod ( 1000/Frequency ); +} + + + + + +/*********************************************************************************************** + * Timer_Callback -- Main timer callback. Equivalent to a timer interrupt handler * + * * + * * + * * + * INPUT: uint timer ID * + * uint reserved * + * long 0 (application defined) * + * long reserved * + * long reserved * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:19PM ST : Created * + *=============================================================================================*/ + + +void CALLBACK Timer_Callback (UINT , UINT , DWORD , DWORD , DWORD) +{ + //CONTEXT context; + + InTimerCallback++; + + // Removed. ST - 2/13/2019 5:11PM + //if (!TimerThreadHandle){ + // DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&TimerThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS); + //} + + + if (WindowsTimer) { + WindowsTimer->Update_Tick_Count(); + } + InTimerCallback--; +} + + + + + + +/*********************************************************************************************** + * WinTimerClass::Update_Tick_Count -- update westwood timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:58PM ST : Created * + *=============================================================================================*/ + +void WinTimerClass::Update_Tick_Count ( void ) +{ +/* + * + * Increment westwood timers + * + */ + SysTicks++; + UserTicks++; + +} + + + + + + + + +/* +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + */ + + + + +/*********************************************************************************************** + * WinTimerClass::Get_System_Tick_Count -- returns the system tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_System_Tick_Count ( void ) +{ + return ( SysTicks ); +} + + + +/*********************************************************************************************** + * WinTimerClass::Get_User_Tick_Count -- returns the user tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_User_Tick_Count ( void ) +{ + return ( UserTicks ); +} \ No newline at end of file diff --git a/REDALERT/WIN32LIB/TOBUFF.ASM b/REDALERT/WIN32LIB/TOBUFF.ASM new file mode 100644 index 000000000..b210cf3bc --- /dev/null +++ b/REDALERT/WIN32LIB/TOBUFF.ASM @@ -0,0 +1,294 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* Last Update : Feb 10, 1995 [jrj] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT +;LOCALS ?? +.model flat + + +TRANSP equ 0 + + +;INCLUDE "drawbuff.inc" +INCLUDE gbuffer.inc + + +;CODESEG +.code + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* +Buffer_To_Buffer proc C public USES ebx ecx edx esi edi this_object:DWORD, x_pixel:DWORD, y_pixel:DWORD, pixel_width:DWORD, pixel_height:DWORD, dest:DWORD, buffer_size:DWORD + + ;PROC Buffer_To_Buffer C near + ;USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ;ARG this_object:DWORD ; this is a class member function + ;ARG x_pixel:DWORD ; Page X pixel coordinate. + ;ARG y_pixel:DWORD ; Page Y pixel coordinate. + ;ARG pixel_width:DWORD ; Width of region in pixels. + ;ARG pixel_height:DWORD ; Height of region in pixels. + ;ARG dest:DWORD ; the buffer to copy to + ;ARG buffer_size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + ;LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this_object ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi].GraphicViewPort.GVPWidth ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi].GraphicViewPort.GVPHeight ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_blit + + test cl , 1000b + jz scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +scr_left_ok: + test cl , 0010b + jz scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +scr_bottom_ok: + test dl , 0100b + jz scr_right_ok + mov eax , [esi].GraphicViewPort.GVPWidth ; get width into register + mov [ x1_pixel ] , eax +scr_right_ok: + test dl , 0001b + jz do_blit + mov eax , [esi].GraphicViewPort.GVPHeight ; get width into register + mov [ y1_pixel ] , eax + +do_blit: + + cld + + mov eax , [esi].GraphicViewPort.GVPXAdd + add eax , [esi].GraphicViewPort.GVPWidth + add eax , [esi].GraphicViewPort.GVPPitch + mov esi , [esi].GraphicViewPort.GVPOffset + + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + sub edx , [ y_pixel ] + jle real_out + sub eax , [ x_pixel ] + jle real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ buffer_size ] + jg real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + cmp [ transp ] , 0 + jnz forward_Blit_trans +ENDIF + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl forward_loop_bytes + +forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_dword + ret + +forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + ret + + +IF TRANSP + +forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + transp_reference: + dec ecx + jge forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_trans + ret +ENDIF + +real_out: + ret + +;ENDP Buffer_To_Buffer +Buffer_To_Buffer endp + +END \ No newline at end of file diff --git a/REDALERT/WIN32LIB/VIDEO.H b/REDALERT/WIN32LIB/VIDEO.H new file mode 100644 index 000000000..223d4e6a8 --- /dev/null +++ b/REDALERT/WIN32LIB/VIDEO.H @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + +#endif //NOT_FOR_WIN95 + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + +extern REALPTR VesaFunc; + +#endif //NOT_FOR_WIN95 + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H \ No newline at end of file diff --git a/REDALERT/WIN32LIB/WINCOMM.H b/REDALERT/WIN32LIB/WINCOMM.H new file mode 100644 index 000000000..5d26049b8 --- /dev/null +++ b/REDALERT/WIN32LIB/WINCOMM.H @@ -0,0 +1,454 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WIN32 +#define WIN32 +#define _WIN32 +#endif //WIN32 +#include + +typedef enum WinCommDialMethodType { + WC_TOUCH_TONE = 0, + WC_PULSE +} WinCommDialMethodType; + + + +#define COMMSUCCESS 0 +#define ASTIMEOUT -10 +#define COMMUSERABORT -16 + + +/* +** The size of our serial buffer within the class. +** +** !!!!!! THIS MUST BE A POWER OF 2 !!!!!! +** +*/ +#define SIZE_OF_WINDOWS_SERIAL_BUFFER 2048 + + + +/* +** WinModemClass. +** +** This class provides access to modems under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +*/ + +class WinModemClass +{ + + public: + + WinModemClass (void); //WinModemClass Contructor + virtual ~WinModemClass (void); //WinModemClass Destructor + + + /* + ** Serial port open should be called to get a handle to the COM port + ** This needs to be called first as other class members rely on the handle + ** + ** Replacement for Greenleaf function: PortOpenGreenleafFast + */ + //virtual HANDLE Serial_Port_Open (int port, int baud, int parity, int wordlen, int stopbits); + virtual HANDLE Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol); + + /* + ** This function releases the COM port handle and should be called after + ** communications have finished + ** + ** Replacement for Greenleaf function: PortClose + */ + void Serial_Port_Close (void); + + /* + ** This member copies any bytes from the internal class serial buffer + ** into your user buffer. + ** + ** Replacement for Greenleaf function: ReadBuffer + */ + int Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len); + + /* + ** Write chars to the serial port + ** + ** Replacement for Greenleaf function: WriteBuffer + */ + void Write_To_Serial_Port (unsigned char *buffer, int length); + + /* + ** Wait for the outgoing buffer to empty + */ + void Wait_For_Serial_Write (void); + + /* + ** Set the dial type to DIAL_TOUCH_TONE or DIAL_PULSE + ** + ** Replacement for Greenleaf function: HMSetDiallingMethod + */ + virtual void Set_Modem_Dial_Type (WinCommDialMethodType method); + + /* + ** Get the status of the modem control lines + ** Possible flags are: CTS_SET DSR_SET RI_SET & CD_SET + ** + ** Replacement for Greenleaf function: GetModemStatus + */ + virtual unsigned Get_Modem_Status (void); + + /* + ** Set the DTR line to the given state + ** + ** Replacement for Greenleaf function: SetDtr + */ + virtual void Set_Serial_DTR (BOOL state); + + /* + ** Get the result code from the modem after issuing an 'AT' command + ** + ** Replacement for Greenleaf function: HMInputLine + */ + virtual int Get_Modem_Result (int delay, char *buffer, int buffer_len); + + /* + ** Issue a dial command to the modem. + ** Use Set_Modem_Dial_Type to select pulse or tone dial + ** + ** Replacement for Greenleaf function: HMDial + */ + virtual void Dial_Modem (char *dial_number); + + /* + ** Send a command to the modem. This is usually an 'AT' command. + ** Function will optionally retry until 'OK' is received. + */ + virtual int Send_Command_To_Modem (char *command, char terminator, char *buffer, int buflen, int delay, int retries); + + /* + ** Sets a pointer to a function that will be called for each incoming serial char + ** + ** Replacement for Greenleaf function: HMSetUpEchoRoutine + */ + virtual void Set_Echo_Function (void(*func)(char c)); + + /* + ** Sets a pointer to a function that will be called if ESC is pressed during a dial + ** + ** Replacement for Greenleaf function: HMSetUpAbortKey + */ + virtual void Set_Abort_Function (int (*func)(void)); + + /* + ** Member to allow access to the serial port handle + */ + HANDLE Get_Port_Handle(void); + + /* + ** Status vars for debugging purposes + */ + int FramingErrors; + int IOErrors; + int BufferOverruns; + int InBufferOverflows; + int ParityErrors; + int OutBufferOverflows; + int InQueue; + int OutQueue; + + /* + ** Modem send result codes + */ + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + + /* + ** Enums for modem status flags + */ + enum { + CTS_SET = 0x10, + DSR_SET = 0x20, + RI_SET = 0x40, + CD_SET = 0x80 + }; + + + protected: + + + /* + ** Copy incoming data from the windows file buffer into the internal class buffer + */ + BOOL Read_Serial_Chars(void); + + /* + ** Pointer to the internal class circular buffer for incoming data + */ + unsigned char *SerialBuffer; + + /* + ** Overlap object for asyncronous reads from the serial port + */ + OVERLAPPED ReadOverlap; + + /* + ** Overlap object for asyncronous writes to the serial port + */ + OVERLAPPED WriteOverlap; + + /* + ** Flag that there is no outstanding incoming data in the windows buffer + */ + BOOL WaitingForSerialCharRead; + + /* + ** Flag that we are waiting for the last write to port operation to complete + */ + BOOL WaitingForSerialCharWrite; + + /* + ** Head and Tail pointers for our internal serial buffer + */ + int SerialBufferReadPtr; + int SerialBufferWritePtr; + + /* + ** Windows handle to the COM port device + */ + HANDLE PortHandle; + + /* + ** Dialing method - DIAL_TOUCH_TONE or DIAL_PULSE + */ + WinCommDialMethodType DialingMethod; + + /* + ** Pointer to function for echoing incoming data - can be NULL + */ + void (*EchoFunction)(char c); + + /* + ** Pointer to function for aborting when ESC pressed - can be NULL + */ + int (*AbortFunction)(void); + + /* + ** Serial buffer for asyncronous reads + */ + char TempSerialBuffer[SIZE_OF_WINDOWS_SERIAL_BUFFER]; +}; + + + + + + + + + + +/* +** WinNullModemClass. +** +** This class provides access to serial ports under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +** +** This class just overloads the WinModemClass members that arent required for direct serial communications +** via a 'null modem' cable. +*/ +class WinNullModemClass : public WinModemClass +{ + + public: + + virtual inline void Set_Modem_Dial_Type (int){}; + virtual inline unsigned Get_Modem_Status (void){return (0);}; + virtual inline void Set_Serial_DTR (BOOL){}; + virtual inline int Get_Modem_Result (int, char*, int){return(0);}; + virtual inline void Dial_Modem (char*){}; + virtual inline int Send_Command_To_Modem (char*, char, char*, int, int, int){return (0);}; + virtual inline void Set_Echo_Function (void(*)(char)){}; + virtual inline void Set_Abort_Function (int(*)(void)){}; + +}; + + +extern WinModemClass *SerialPort; + + + + + + + + + +// +// +// This bit swiped from the SDK because its not in the Watcom headers yet +// +// + +/************************************************************************ +* * +* mcx.h -- This module defines the 32-Bit Windows MCX APIs * +* * +* Copyright (c) 1990-1995, Microsoft Corp. All rights reserved. * +* * +************************************************************************/ + +#ifndef _MCX_H_ +#define _MCX_H_ + +typedef struct _MODEMDEVCAPS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // product and version identification + DWORD dwModemProviderVersion; + DWORD dwModemManufacturerOffset; + DWORD dwModemManufacturerSize; + DWORD dwModemModelOffset; + DWORD dwModemModelSize; + DWORD dwModemVersionOffset; + DWORD dwModemVersionSize; + + // local option capabilities + DWORD dwDialOptions; // bitmap of supported values + DWORD dwCallSetupFailTimer; // maximum in seconds + DWORD dwInactivityTimeout; // maximum in seconds + DWORD dwSpeakerVolume; // bitmap of supported values + DWORD dwSpeakerMode; // bitmap of supported values + DWORD dwModemOptions; // bitmap of supported values + DWORD dwMaxDTERate; // maximum value in bit/s + DWORD dwMaxDCERate; // maximum value in bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS; + +typedef struct _MODEMSETTINGS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // static local options (read/write) + DWORD dwCallSetupFailTimer; // seconds + DWORD dwInactivityTimeout; // seconds + DWORD dwSpeakerVolume; // level + DWORD dwSpeakerMode; // mode + DWORD dwPreferredModemOptions; // bitmap + + // negotiated options (read only) for current or last call + DWORD dwNegotiatedModemOptions; // bitmap + DWORD dwNegotiatedDCERate; // bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMSETTINGS, *PMODEMSETTINGS, *LPMODEMSETTINGS; + +// Dial Options +#define DIALOPTION_BILLING 0x00000040 // Supports wait for bong "$" +#define DIALOPTION_QUIET 0x00000080 // Supports wait for quiet "@" +#define DIALOPTION_DIALTONE 0x00000100 // Supports wait for dial tone "W" + +// SpeakerVolume for MODEMDEVCAPS +#define MDMVOLFLAG_LOW 0x00000001 +#define MDMVOLFLAG_MEDIUM 0x00000002 +#define MDMVOLFLAG_HIGH 0x00000004 + +// SpeakerVolume for MODEMSETTINGS +#define MDMVOL_LOW 0x00000000 +#define MDMVOL_MEDIUM 0x00000001 +#define MDMVOL_HIGH 0x00000002 + +// SpeakerMode for MODEMDEVCAPS +#define MDMSPKRFLAG_OFF 0x00000001 +#define MDMSPKRFLAG_DIAL 0x00000002 +#define MDMSPKRFLAG_ON 0x00000004 +#define MDMSPKRFLAG_CALLSETUP 0x00000008 + +// SpeakerMode for MODEMSETTINGS +#define MDMSPKR_OFF 0x00000000 +#define MDMSPKR_DIAL 0x00000001 +#define MDMSPKR_ON 0x00000002 +#define MDMSPKR_CALLSETUP 0x00000003 + +// Modem Options +#define MDM_COMPRESSION 0x00000001 +#define MDM_ERROR_CONTROL 0x00000002 +#define MDM_FORCED_EC 0x00000004 +#define MDM_CELLULAR 0x00000008 +#define MDM_FLOWCONTROL_HARD 0x00000010 +#define MDM_FLOWCONTROL_SOFT 0x00000020 +#define MDM_CCITT_OVERRIDE 0x00000040 +#define MDM_SPEED_ADJUST 0x00000080 +#define MDM_TONE_DIAL 0x00000100 +#define MDM_BLIND_DIAL 0x00000200 +#define MDM_V23_OVERRIDE 0x00000400 + +#endif /* _MCX_H_ */ + + + + + + + + + + + + + + + + diff --git a/REDALERT/WIN32LIB/WINDOWS.CPP b/REDALERT/WIN32LIB/WINDOWS.CPP new file mode 100644 index 000000000..b31d4345e --- /dev/null +++ b/REDALERT/WIN32LIB/WINDOWS.CPP @@ -0,0 +1,973 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "ww_win.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; + +extern GraphicViewPortClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + // PWG - have to figure out how to do this in windows library + +// Clear_KeyBuffer(); +// Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ +// case KA_LITERAL: +// if (c != (char) 127) { // check if fell thru from extend case +// c = 0; // set to zero for literal case +// } +// c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > (unsigned)WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + (unsigned char)WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= (unsigned)WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= (unsigned)WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > (unsigned)breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= (unsigned)WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & (char)0x07; + c = (char)((c & (char)0x78) >> 3); + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/WINHIDE.CPP b/REDALERT/WIN32LIB/WINHIDE.CPP new file mode 100644 index 000000000..93680deaf --- /dev/null +++ b/REDALERT/WIN32LIB/WINHIDE.CPP @@ -0,0 +1,94 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "ww_win.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; +// Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ +// Conditional_Show_Mouse(); +} +#endif + + + diff --git a/REDALERT/WIN32LIB/WRITEPCX.CPP b/REDALERT/WIN32LIB/WRITEPCX.CPP new file mode 100644 index 000000000..d3681b4a2 --- /dev/null +++ b/REDALERT/WIN32LIB/WRITEPCX.CPP @@ -0,0 +1,177 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +#include +static void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + *=========================================================================*/ +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ) +{ + unsigned char palcopy[256 * 3]; + unsigned i ; + //unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == WW_ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + + for ( i = 0 ; i < (unsigned)header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, (unsigned char*)ptr + i * VP_Scan_Line ) ; + + Mem_Copy(palette, palcopy, 256 * 3); + pal = ( RGB * ) palcopy ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palcopy , 256 * sizeof (RGB) ) ; + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + + +void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + unsigned char * file_ptr ; + unsigned char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + + for ( i = 1 ; i < (unsigned)scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else { + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last ) ; + } + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} diff --git a/REDALERT/WIN32LIB/WSA.H b/REDALERT/WIN32LIB/WSA.H new file mode 100644 index 000000000..8cb0fd6dc --- /dev/null +++ b/REDALERT/WIN32LIB/WSA.H @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WSA 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette=NULL); +void __cdecl Close_Animation( void *handle ); +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +BOOL __cdecl Animate_Frame(void *handle, VideoViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +int __cdecl Get_Animation_X(void const *handle); +int __cdecl Get_Animation_Y(void const *handle); +int __cdecl Get_Animation_Width(void const *handle); +int __cdecl Get_Animation_Height(void const *handle); +int __cdecl Get_Animation_Palette(void const *handle); +unsigned long __cdecl Get_Animation_Size(void const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, BufferClass& buffer, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, (char *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +unsigned int __cdecl Apply_XOR_Delta(char *source_ptr, char *delta_ptr); +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy); +} + + + +#endif // WSA_H + diff --git a/REDALERT/WIN32LIB/WWFILE.H b/REDALERT/WIN32LIB/WWFILE.H new file mode 100644 index 000000000..d7dd4069d --- /dev/null +++ b/REDALERT/WIN32LIB/WWFILE.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.14 06 Sep 1995 16:30:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/REDALERT/WIN32LIB/WWLIB32.H b/REDALERT/WIN32LIB/WWLIB32.H new file mode 100644 index 000000000..88fe24e93 --- /dev/null +++ b/REDALERT/WIN32LIB/WWLIB32.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H + + diff --git a/REDALERT/WIN32LIB/WWMEM.H b/REDALERT/WIN32LIB/WWMEM.H new file mode 100644 index 000000000..20bb06ac8 --- /dev/null +++ b/REDALERT/WIN32LIB/WWMEM.H @@ -0,0 +1,67 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/REDALERT/WIN32LIB/WWMEM.INC b/REDALERT/WIN32LIB/WWMEM.INC new file mode 100644 index 000000000..954b433ad --- /dev/null +++ b/REDALERT/WIN32LIB/WWMEM.INC @@ -0,0 +1,40 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/REDALERT/WIN32LIB/WW_WIN.H b/REDALERT/WIN32LIB/WW_WIN.H new file mode 100644 index 000000000..cdebfbb48 --- /dev/null +++ b/REDALERT/WIN32LIB/WW_WIN.H @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][9]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/_DIPTABL.CPP b/REDALERT/WIN32LIB/_DIPTABL.CPP new file mode 100644 index 000000000..d3cf78465 --- /dev/null +++ b/REDALERT/WIN32LIB/_DIPTABL.CPP @@ -0,0 +1,55 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/REDALERT/WIN32LIB/wwstd.h b/REDALERT/WIN32LIB/wwstd.h new file mode 100644 index 000000000..00b76741c --- /dev/null +++ b/REDALERT/WIN32LIB/wwstd.h @@ -0,0 +1,342 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// +// Win 95 includes +// + +#ifndef WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 1 +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#endif + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +//#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define WW_ERROR -1 + +#if (0) //PG +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +//#pragma option -Jg +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +//int ABS(int); +//long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +//PG +//short MIN(short, short); +//int MIN(int, int); +//long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +//short MAX(short, short); +//int MAX(int, int); +//long MAX(long, long); +//#pragma option -Jgd + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +#if (0)//PG +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +//#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +//#define true 1 +//#define false 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. +#if(0) +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif +#endif +#endif //PG + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum : unsigned short { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif diff --git a/REDALERT/WINSTUB.CPP b/REDALERT/WINSTUB.CPP new file mode 100644 index 000000000..6c8ad7f24 --- /dev/null +++ b/REDALERT/WINSTUB.CPP @@ -0,0 +1,968 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WINSTUB.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : October 4th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This file contains stubs for undefined externals when linked under Watcom for Win 95 * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Assert_Failure -- display the line and source file where a failed assert occurred * + * Check_For_Focus_Loss -- check for the end of the focus loss * + * Create_Main_Window -- opens the MainWindow for C&C * + * Focus_Loss -- this function is called when a library function detects focus loss * + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#include "ipx95.h" +#endif //WINSOCK_IPX + +void output(short,short) +{} + + +unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus +extern void VQA_PauseAudio(void); +extern void VQA_ResumeAudio(void); + +//#include "WolDebug.h" + + +/*********************************************************************************************** + * Focus_Loss -- this function is called when a library function detects focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 2:10PM ST : Created * + *=============================================================================================*/ + +void Focus_Loss(void) +{ + Theme.Suspend(); + Stop_Primary_Sound_Buffer(); + if (WWMouse) WWMouse->Clear_Cursor_Clip(); +} + +void Focus_Restore(void) +{ + Restore_Cached_Icons(); + Map.Flag_To_Redraw(true); + Start_Primary_Sound_Buffer(TRUE); + if (WWMouse) WWMouse->Set_Cursor_Clip(); + VisiblePage.Clear(); + HiddenPage.Clear(); +} + + + +/*********************************************************************************************** + * Check_For_Focus_Loss -- check for the end of the focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/2/96 10:49AM ST : Created * + *=============================================================================================*/ + +void Check_For_Focus_Loss(void) +{ +#if (0)//PG + static BOOL focus_last_time = 1; + MSG msg; + + + if ( !GameInFocus ) { + Focus_Loss(); + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (!focus_last_time && GameInFocus) { + + VQA_PauseAudio(); + CountDownTimerClass cd; + cd.Set(60*1); + + do { + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } while (cd.Time()); + VQA_ResumeAudio(); + PostMessage (MainWindow, CCFocusMessage, 0, 0); +// AllSurfaces.Restore_Surfaces(); +// VisiblePage.Clear(); +// HiddenPage.Clear(); +// Map.Flag_To_Redraw(true); + } + + focus_last_time = GameInFocus; +#endif +} + + + +extern bool InMovie; +#if (0)//PG +long FAR PASCAL _export Windows_Procedure(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + + int low_param = LOWORD(wParam); + + if (message == CCFocusMessage) { + Start_Primary_Sound_Buffer(TRUE); + if (!InMovie) { + Theme.Stop(); + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + return(0); + } + +#ifdef WINSOCK_IPX + /* + ** Pass on any messages intended for the winsock message handler. + */ + if ( PacketTransport ) { + if ( message == (UINT) PacketTransport->Protocol_Event_Message() ) { + if ( PacketTransport->Message_Handler (hwnd, message, wParam, lParam) ){ + return ( DefWindowProc (hwnd, message, wParam, lParam) ); + }else{ + return (0); + } + } + } +#endif //WINSOCK_IPX + + + /* + ** Pass this message through to the keyboard handler. If the message + ** was processed and requires no further action, then return with + ** this information. + */ + if (Keyboard->Message_Handler(hwnd, message, wParam, lParam)) { + return(1); + } + + switch ( message ) { +// case WM_SYSKEYDOWN: +// Mono_Printf("wparam=%08X lparam=%08X\n", (long)wParam, (long)lParam); + // fall through + +// case WM_MOUSEMOVE: +// case WM_KEYDOWN: +// case WM_SYSKEYUP: +// case WM_KEYUP: +// case WM_LBUTTONDOWN: +// case WM_LBUTTONUP: +// case WM_LBUTTONDBLCLK: +// case WM_MBUTTONDOWN: +// case WM_MBUTTONUP: +// case WM_MBUTTONDBLCLK: +// case WM_RBUTTONDOWN: +// case WM_RBUTTONUP: +// case WM_RBUTTONDBLCLK: +// Keyboard->Message_Handler(hwnd, message, wParam, lParam); +// return(0); + + /* + ** Windoze message says we have to shut down. Try and do it cleanly. + */ + case WM_DESTROY: + Prog_End("WM_DESTROY", false); + Invalidate_Cached_Icons(); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger) Reset_Video_Mode(); + Stop_Profiler(); + PostQuitMessage( 0 ); + + /* + ** If we are shutting down gracefully than flag that the message loop has finished. + ** If this is a forced shutdown (ReadyToQuit == 0) then try and close down everything + ** before we exit. + */ + switch (ReadyToQuit) { + case 1: + ReadyToQuit = 2; + break; + + case 0: + Shutdown_Network(); +#ifndef WINSOCK_IPX + if (Winsock.Get_Connected()) Winsock.Close(); + /* + ** Free the THIPX32 dll + */ + Unload_IPX_Dll(); +#endif //WINSOCK_IPX + ExitProcess(0); + break; + case 3: + Shutdown_Network(); +#ifndef WINSOCK_IPX + /* + ** Call the function to disable the IPX callback as horrible things can + ** happen if we get a callback after the process has exited! + */ + if (Session.Type == GAME_IPX){ + IPX_Shut_Down95(); + } + /* + ** Free the THIPX32 dll + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#else + Unload_IPX_Dll(); +#endif + + if (Winsock.Get_Connected()) Winsock.Close(); +#endif //WINSOCK_IPX + ReadyToQuit = 2; + break; + + } + return(0); + + case WM_ACTIVATEAPP: + GameInFocus=(BOOL)wParam; + if (!GameInFocus) Focus_Loss(); + AllSurfaces.Set_Surface_Focus (GameInFocus); + AllSurfaces.Restore_Surfaces(); +// if (GameInFocus) { +// Restore_Cached_Icons(); +// Map.Flag_To_Redraw(true); +// Start_Primary_Sound_Buffer(TRUE); +// if (WWMouse) WWMouse->Set_Cursor_Clip(); +// } + return(0); +#ifdef NEVER + case WM_ACTIVATE: + if (low_param == WA_INACTIVE) { + GameInFocus = FALSE; + Focus_Loss(); + } + return(0); +#endif //NEVER + + + case WM_SYSCOMMAND: + switch ( wParam ) { + + case SC_CLOSE: + /* + ** Windows sent us a close message. Probably in response to Alt-F4. Ignore it by + ** pretending to handle the message and returning true; + */ + return (0); + + case SC_SCREENSAVE: + /* + ** Windoze is about to start the screen saver. If we just return without passing + ** this message to DefWindowProc then the screen saver will not be allowed to start. + */ + return (0); + } + break; + + +#ifndef WINSOCK_IPX + case WM_ACCEPT: + case WM_HOSTBYADDRESS: + case WM_HOSTBYNAME: + case WM_ASYNCEVENT: + case WM_UDPASYNCEVENT: + Winsock.Message_Handler(hwnd, message, wParam, lParam); + return (0); +#endif //WINSOCK_IPX + } + + + + return DefWindowProc (hwnd, message, wParam, lParam); +} +#endif + + + + + +HANDLE DebugFile = INVALID_HANDLE_VALUE; + +/*********************************************************************************************** + * WWDebugString -- sends a string to the debugger and echos it to disk * + * * + * * + * * + * INPUT: string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/28/96 12:48PM ST : Created * + *=============================================================================================*/ +void WWDebugString (char *string) +{ +#if (0) + char outstr[256]; + + sprintf (outstr, "%s", string); + + DWORD actual; + if (DebugFile == INVALID_HANDLE_VALUE){ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + }else{ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (DebugFile != INVALID_HANDLE_VALUE){ + SetFilePointer (DebugFile, 0, NULL, FILE_END); + WriteFile(DebugFile, outstr, strlen(outstr)+1, &actual, NULL); + CloseHandle (DebugFile); + } + + OutputDebugString (string); +#else //(0) + + string = string; +// debugprint( string ); + +#endif //(0) + +} + + + + + + + + + +/*********************************************************************************************** + * Create_Main_Window -- opens the MainWindow for C&C * + * * + * * + * * + * INPUT: instance -- handle to program instance * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/10/95 4:08PM ST : Created * + *=============================================================================================*/ + +#define CC_ICON 1 + +#if (ENGLISH) +#define WINDOW_NAME "Red Alert" +#endif + +#if (FRENCH) +#define WINDOW_NAME "Alerte Rouge" +#endif + +#if (GERMAN) +#define WINDOW_NAME "Alarmstufe Rot" +#endif + + +void Create_Main_Window ( HANDLE instance , int command_show , int width , int height ) + +{ + MainWindow = NULL; + return; +#if (0)//PG + HWND hwnd ; + WNDCLASS wndclass ; + // + // Register the window class + // + + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = Windows_Procedure ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = instance ; + wndclass.hIcon = LoadIcon (instance, MAKEINTRESOURCE(CC_ICON)) ; + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = WINDOW_NAME; //NULL + wndclass.lpszClassName = WINDOW_NAME; + + RegisterClass (&wndclass) ; + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + WINDOW_NAME, + WINDOW_NAME, + WS_POPUP, // Denzil | WS_MAXIMIZE, + 0, + 0, + // Denzil 5/18/98 - Making window fullscreen prevents other apps + // from getting WM_PAINT messages + GetSystemMetrics(SM_CXSCREEN), //width, + GetSystemMetrics(SM_CYSCREEN), //height, + // End Denzil + NULL, + NULL, + instance, + NULL ); +// Denzil +width = width; height = height; +// End + + ShowWindow (hwnd, command_show ); + ShowCommand = command_show; + UpdateWindow (hwnd); + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + hInstance = instance; + + CCFocusMessage = RegisterWindowMessage ("CC_GOT_FOCUS"); + + Audio_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Restore_Function = &Focus_Restore; + Gbuffer_Focus_Loss_Function = &Focus_Loss; +#endif +} + + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc) +{ +#if (0)//PG + MSG msg; + /* + ** Get rid of the Westwood mouse cursor because we are showing a + ** dialog box and we want to have the right windows cursor showing + ** for it. + */ + Hide_Mouse(); + ShowCursor(TRUE); + + /* + ** Pop up the dialog box and then run a standard message handler + ** until the dialog box is closed. + */ + + DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc); + while (GetMessage(&msg, NULL, 0, 0) && !AllDone) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* + ** Restore the westwood mouse cursor and get rid of the windows one + ** because it is now time to restore back to the westwood way of + ** doing things. + */ + ShowCursor(FALSE); + Show_Mouse(); +#endif +} + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + +int DebugColour=1; + + +extern "C" void Set_Palette_Register(int number, int red, int green, int blue); +//#pragma off (unreferenced) +void Colour_Debug (int call_number) +{ +#if (0)//PG + //#if 0 + //if (DebugColour==call_number || !call_number) { + + //if (call_number) { + // Wait_Vert_Blank(); + //} + + Set_Palette_Register (0,ColourLookup[call_number].Red , + ColourLookup[call_number].Green, + ColourLookup[call_number].Blue); + //} + //#endif +#endif +} + +//#pragma on (unreferenced) + + + + + +BOOL Any_Locked (void) +{ + if (SeenBuff.Get_LockCount() || + HidPage.Get_LockCount()) { + return (TRUE); + } else { + return(FALSE); + } +} + + + + + + + + +// +// Miscellaneous stubs. Mainly for multi player stuff +// +// +// + +//IPXAddressClass::IPXAddressClass(void) { +// int i; +// i++; +//} +//int IPXManagerClass::Num_Connections(void) { return (0); } +//int IPXManagerClass::Connection_ID( int ) { return (0); } +//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); } +//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); } +//int IPXAddressClass::Is_Broadcast() { return (0); } +//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); } +//int IPXManagerClass::Service() { return (0); } +//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); } +//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); } +//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {} +//IPXManagerClass::~IPXManagerClass() { +// int i; +// i++; +// } +//int IPXManagerClass::Delete_Connection( int ) { return (0); } +//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Socket( short unsigned ) {} +//int IPXManagerClass::Is_IPX() { return (0); } +//int IPXManagerClass::Init() { return (0); } +//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Bridge( char unsigned * ) {} +//int IPXManagerClass::Global_Num_Send() { return (0); } +//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ) {} +//unsigned long IPXManagerClass::Global_Response_Time() { return (0); } +//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); } +//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); } +//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); } +//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); } +//int IPXManagerClass::Connection_Index( int ) { return (0); } +//void IPXManagerClass::Reset_Response_Time() {} +//long unsigned IPXManagerClass::Response_Time() { return (0); } +//int IPXManagerClass::Private_Num_Send( int ) { return (0); } + +//_VQAHandle * VQA_Alloc(void) { return ((_VQAHandle *)0); } +//void VQA_Init( _VQAHandle *, long ( *)()) {} +//long VQA_Open( _VQAHandle *, char const *, _VQAConfig * ) { return (0); } +//void VQA_Free( _VQAHandle * ) {} +//void VQA_Close( _VQAHandle * ) {} +//long VQA_Play( _VQAHandle *, long ) { return (0); } + +//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)) {} + +//long VQA_Open(VQAHandle *, char const *, VQAConfig *) +//{ +// return (0); +//} + +//void VQA_Close(VQAHandle *) {} + +//long VQA_Play(VQAHandle *, long) +//{ +// return (0); +//} + + +unsigned char *VQPalette; +long VQNumBytes; +unsigned long VQSlowpal; +bool VQPaletteChange = false; + + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette, long numbytes, unsigned long slowpal); +} + + + +void Flag_To_Set_Palette(unsigned char *palette, long numbytes, unsigned long slowpal) +{ + VQPalette = palette; + VQNumBytes = numbytes; + VQSlowpal = slowpal; + VQPaletteChange = true; +} + + + +void Check_VQ_Palette_Set(void) +{ + if (VQPaletteChange) { + SetPalette(VQPalette, VQNumBytes, VQSlowpal); + VQPaletteChange = false; + } +} + + + + + +void __cdecl SetPalette(unsigned char *palette, long, unsigned long) +{ + for (int i=0 ; i<256*3 ; i++) { + *(palette+i)&=63; + } + Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63); + + if (PalettesRead) { + memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536); + } + Set_Palette(palette); +} + + +#ifndef NDEBUG +/*********************************************************************************************** + * Assert_Failure -- display the line and source file where a failed assert occurred * + * * + * * + * INPUT: line number in source file * + * name of source file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/17/96 9:58AM ST : Created * + *=============================================================================================*/ + +void Assert_Failure (char *expression, int line, char *file) +{ + char assertbuf[256]; + char timebuff[512]; + SYSTEMTIME time; + + sprintf (assertbuf, "assert '%s' failed at line %d in module %s.\n", expression, line, file); + + if (!MonoClass::Is_Enabled()) MonoClass::Enable(); + + Mono_Clear_Screen(); + Mono_Printf("%s", assertbuf); + + WWDebugString(assertbuf); + + GetLocalTime(&time); + + sprintf (timebuff, "%02d/%02d/%04d %02d:%02d:%02d - %s", time.wMonth, time.wDay, time.wYear, + time.wHour, time.wMinute, time.wSecond, + assertbuf); + + + HMMIO handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + if (!handle) { + handle = mmioOpen("ASSERT.TXT", NULL, MMIO_CREATE | MMIO_WRITE); + //mmioClose(handle, 0); + //handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + } + + if (handle) { + + mmioWrite(handle, timebuff, strlen(timebuff)); + mmioClose(handle, 0); + } + + WWMessageBox().Process(assertbuf); +// WWMessageBox().Process("Red Alert demo timed out - Aborting"); + //Get_Key(); + + Prog_End(assertbuf, false); + Invalidate_Cached_Icons(); + //PostQuitMessage( 0 ); + //ExitProcess(0); +} +#endif + + + + + + + +/*********************************************************************************************** + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 3:57PM ST : Created * + *=============================================================================================*/ +void Memory_Error_Handler(void) +{ + VisiblePage.Clear(); + CCPalette.Set(); + while (Get_Mouse_State()) {Show_Mouse();}; + WWMessageBox().Process(TEXT_MEMORY_ERROR, TEXT_ABORT, false); + + ReadyToQuit = 1; + + PostMessage(MainWindow, WM_DESTROY, 0, 0); + do + { + Keyboard->Check(); + }while (ReadyToQuit == 1); + + ExitProcess(0); +} + + + + + + + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size); +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette) +{ + + GraphicBufferClass *load_buffer; + + + load_buffer = Read_PCX_File (name, (char*)palette, NULL, 0); + + if (load_buffer) { + load_buffer->Blit(*video_page); + delete load_buffer; + } +} + + + +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the format [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optional, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointed by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; * * + * OUTPUT: on success a pointer to a GraphicBufferClass containing the * + * pcx file, NULL otherwise. * + * * + * WARNINGS: * + * Appears to be a comment-free zone * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + * 04/30/1996 ST : Tidied up and modified to use CCFileClass * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + file_handle.Read (pool , POOL_SIZE ); \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size) +{ + unsigned i, j; + unsigned rle; + unsigned color; + unsigned scan_pos; + char *file_ptr; + int width; + int height; + char *buffer; + PCX_HEADER header; + RGB *pal; + char pool [POOL_SIZE]; + GraphicBufferClass *pic; + + CCFileClass file_handle(name); + + if (!file_handle.Is_Available()) return (NULL); + + file_handle.Open(READ); + + file_handle.Read (&header, sizeof (PCX_HEADER)); + + if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1; + height = header.height - header.y + 1; + + if (Buff) { + buffer = (char *)Buff; + i = Size / width; + height = MIN ((int)i - 1, height); + pic = new GraphicBufferClass(width, height, buffer, Size); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } else { + pic = new GraphicBufferClass(width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } + + buffer = (char *) pic->Get_Buffer(); + file_ptr = pool ; + file_handle.Read (pool , POOL_SIZE); + + if ( header.byte_per_line != width ) { + + for ( scan_pos = j = 0 ; j < (unsigned) height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < (unsigned)width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ); + i += rle; + } else { + *(buffer+scan_pos + i++ ) = (char)rle; + } + } + } + + if ( i == width ) rle = READ_CHAR (); + if ( rle > 192 ) rle = READ_CHAR (); + + } else { + + for ( i = 0 ; i < (unsigned)width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ); + i += rle ; + } else { + *(buffer + i++) = (char)rle; + } + } + } + + if ( palette ) { + file_handle.Seek (- (256 * (int)sizeof ( RGB )) , SEEK_END ); + file_handle.Read (palette , 256L * sizeof ( RGB )); + + pal = ( RGB * ) palette; + for (i = 0 ; i < 256 ; i ++) { + pal ->red >>= 2; + pal ->green >>= 2; + pal ->blue >>= 2; + pal ++ ; + } + } + + file_handle.Close(); + return pic; +} \ No newline at end of file diff --git a/REDALERT/WOLAPIOB.CPP b/REDALERT/WOLAPIOB.CPP new file mode 100644 index 000000000..a1d72a704 --- /dev/null +++ b/REDALERT/WOLAPIOB.CPP @@ -0,0 +1,3432 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.cpp - Implementation of class WolapiObject. +// ajw 07/10/98 + +#include "WolapiOb.h" +#include "RAWolapi.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "ToolTip.h" +#include "Wol_GSup.h" + +#include "WolDebug.h" + +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ); + +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); + +extern bool cancel_current_msgbox; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +WolapiObject::WolapiObject() : pChat( NULL ), pDownload( NULL ), pChatSink( NULL ), pDownloadSink( NULL ), + dwChatAdvise( 0 ), dwDownloadAdvise( 0 ), pILChat( NULL ), pILUsers( NULL ), + CurrentLevel( WOL_LEVEL_TOP ), bChannelOwner( false ), GameTypeInfos( NULL ), + nGameTypeInfos( 0 ), bChatShownBefore( false ), + pILPlayers( NULL ), pChatSaveList( NULL ), iLobbyReturnAfterGame( -1 ), iLobbyLast( -1 ), + bFindEnabled( true ), + bPageEnabled( true ), + bLangFilter( true ), + bAllGamesShown( true ), + pGSupDlg( NULL ), + pShpDiscon( NULL ), + pShpLeave( NULL ), + pShpRefresh( NULL ), + pShpSquelch( NULL ), + pShpBan( NULL ), + pShpKick( NULL ), + pShpFindpage( NULL ), + pShpOptions( NULL ), + pShpLadder( NULL ), + pShpHelp( NULL ), + pShpBtnDiscon( NULL ), + pShpBtnLeave( NULL ), + pShpBtnRefresh( NULL ), + pShpBtnSquelch( NULL ), + pShpBtnBan( NULL ), + pShpBtnKick( NULL ), + pShpBtnFindpage( NULL ), + pShpBtnOptions( NULL ), + pShpBtnLadder( NULL ), + pShpBtnHelp( NULL ), + bReturningAfterGame( false ), + pTTipDiscon( NULL ), + pTTipLeave( NULL ), + pTTipRefresh( NULL ), + pTTipSquelch( NULL ), + pTTipBan( NULL ), + pTTipKick( NULL ), + pTTipFindpage( NULL ), + pTTipOptions( NULL ), + pTTipLadder( NULL ), + pTTipHelp( NULL ), + bMyRecordUpdated( false ), + bChannelListTitleUpdated( false ), + bInGame( false ), + pStaticUsers( NULL ), + bPump_In_Call_Back( false ), + bFreezeExternalPager( false ), + bDoingDisconnectPinging( false ), + bSelfDestruct( false ), + bEggSounds( true ), + bEgg8Player( false ), + bShowRankRA( true ), + bShowRankUpdated( false ) +{ + *szMyName = 0; + *szMyRecord = 0; + *szMyRecordAM = 0; + *szChannelListTitle = 0; + *szChannelNameCurrent = 0; + *szChannelReturnOnGameEnterFail = 0; + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + dwTimeNextChannelUpdate = 0; + *szLadderServerHost = 0; + *szGameResServerHost1 = 0; + *szGameResServerHost2 = 0; + + strcpy( DibIconInfos[ DIBICON_OWNER ].szFile, "dib_own.bmp" ); + strcpy( DibIconInfos[ DIBICON_SQUELCH ].szFile, "dib_sqel.bmp" ); + strcpy( DibIconInfos[ DIBICON_LATENCY ].szFile, "latency.bmp" ); + strcpy( DibIconInfos[ DIBICON_ACCEPT ].szFile, "dib_acpt.bmp" ); + strcpy( DibIconInfos[ DIBICON_NOTACCEPT ].szFile, "dib_acp2.bmp" ); + strcpy( DibIconInfos[ DIBICON_USER ].szFile, "dib_user.bmp" ); + strcpy( DibIconInfos[ DIBICON_PRIVATE ].szFile, "privgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_TOURNAMENT ].szFile, "tourgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_VOICE ].szFile, "voice.bmp" ); + for( int i = 0; i != NUMDIBICONS; i++ ) + { + DibIconInfos[ i ].hDIB = 0; + DibIconInfos[ i ].pDIB = NULL; + } + + // Determine name of executable of user's web browser. + // This seems to be the "correct" way to do this, but it's bloody stupid. + *szWebBrowser = 0; + char szFile[] = "\\name_unlikely_to_conflict_77.html"; //"\\it is really dumb for me to have to create this file.html"; + HANDLE hFile = ::CreateFile( szFile, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile != INVALID_HANDLE_VALUE ) + { + ::CloseHandle( hFile ); + HINSTANCE hExecutable = ::FindExecutable( szFile, "", szWebBrowser ); +// if( (int)hExecutable <= 32 ) +// { +// debugprint( "error %i getting browser\n", hExecutable ); +// } +// else +// debugprint( "szWebBrowser is %s\n", szWebBrowser ); + ::DeleteFile( szFile ); + } +} + +//*********************************************************************************************** +WolapiObject::~WolapiObject() +{ + DeleteSavedChat(); + + if( nGameTypeInfos ) + { + // Delete DIBs that were created, and the wol_gametypeinfos themselves + for( unsigned int n = 0; n != nGameTypeInfos; n++ ) + { + if( GameTypeInfos[ n ].hDIB ) + { + GlobalUnlock( GameTypeInfos[ n ].hDIB ); // Release pDIB. + DestroyDIB( GameTypeInfos[ n ].hDIB ); // Destroy mem alloc'ed for dib bits data. + } + } + delete [] GameTypeInfos; + GameTypeInfos = NULL; + } + + for( int i = 0; i != NUMDIBICONS; i++ ) + { + if( DibIconInfos[ i ].pDIB ) + { + GlobalUnlock( DibIconInfos[ i ].hDIB ); + DestroyDIB( DibIconInfos[ i ].hDIB ); + } + } + + if( pChatSink ) + UnsetupCOMStuff(); + + // Delete buttons, etc., shared by dialogs. + delete pShpBtnDiscon; + delete pShpBtnLeave; + delete pShpBtnRefresh; + delete pShpBtnSquelch; + delete pShpBtnBan; + delete pShpBtnKick; + delete pShpBtnFindpage; + delete pShpBtnOptions; + delete pShpBtnLadder; + delete pShpBtnHelp; + // Delete shared tooltips. + delete pTTipDiscon; + delete pTTipLeave; + delete pTTipRefresh; + delete pTTipSquelch; + delete pTTipBan; + delete pTTipKick; + delete pTTipFindpage; + delete pTTipOptions; + delete pTTipLadder; + delete pTTipHelp; +} + +//*********************************************************************************************** +bool WolapiObject::bSetupCOMStuff() +{ +// debugprint( "++++Begin WolapiObject::bSetupCOMStuff\n" ); + + HRESULT hRes; + + // Grab IChat, INetUtil, set up "sinks". +// debugprint( "CoCreateInstance\n" ); + CoCreateInstance( CLSID_Chat, NULL, CLSCTX_INPROC_SERVER, IID_IChat, (void**)&pChat ); + if( !pChat ) + return false; // Severe, essentially fatal. + CoCreateInstance( CLSID_NetUtil, NULL, CLSCTX_INPROC_SERVER, IID_INetUtil, (void**)&pNetUtil ); + if( !pNetUtil ) + return false; // Severe, essentially fatal. + + // Set up RAChatEventSink. + pChatSink = new RAChatEventSink( this ); + pChatSink->AddRef(); + // Set up RANetUtilEventSink. + pNetUtilSink = new RANetUtilEventSink( this ); + pNetUtilSink->AddRef(); + + // If we could use ATL stuff, this would be different. (We'd use AtlAdvise.) + + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + + // Get a connection point from the chat class for the chatsink. +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect chat to chatsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (IChatEvent*)pChatSink, &dwChatAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; + // Get a connection point from the netutil class for the netutilsink. +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect netutil to netutilsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (INetUtilEvent*)pNetUtilSink, &dwNetUtilAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "++++End WolapiObject::bSetupCOMStuff\n" ); + return true; +} + +//*********************************************************************************************** +void WolapiObject::UnsetupCOMStuff() +{ +// debugprint( "----Begin WolapiObject::UnsetupCOMStuff\n" ); + + HRESULT hRes; + + // If we could use ATL stuff, this would be different. (We'd use AtlUnadvise.) + + // Unsetup RAChatEventSink and RANetUtilEventSink, release IChat. + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwChatAdvise ); + pConnectionPoint->Unadvise( dwChatAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwNetUtilAdvise ); + pConnectionPoint->Unadvise( dwNetUtilAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "pChat->Release\n" ); + pChat->Release(); + +// debugprint( "pChatSink->Release\n" ); + pChatSink->Release(); // This results in pChatSink deleting itself for us. + pChatSink = NULL; + +// debugprint( "pNetUtil->Release\n" ); + pNetUtil->Release(); + +// debugprint( "pNetUtilSink->Release\n" ); + pNetUtilSink->Release(); // This results in pChatSink deleting itself for us. + pNetUtilSink = NULL; + +// debugprint( "----End WolapiObject::UnsetupCOMStuff\n" ); +} + +//*********************************************************************************************** +void WolapiObject::LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ) +{ + // Called to initialize this before the chat dialog is shown. + + // Set pointers to lists in dialog. + this->pILChat = pILChat; + this->pILChannels = pILChannels; + this->pILUsers = pILUsers; + + this->pStaticUsers = pStaticUsers; +} + +//*********************************************************************************************** +void WolapiObject::ClearListPtrs() +{ + // Called to clear list pointers when chat or gamesetup dialog goes away, for safety. + pILChat = NULL; + pILChannels = NULL; + pILUsers = NULL; + + pILPlayers = NULL; + + pStaticUsers = NULL; +} + +//*********************************************************************************************** +void WolapiObject::LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ) +{ + // Called to initialize this before the gamesetup dialog is shown. + + // Set pointers to lists in dialog. + pILChat = pILDisc; + this->pILPlayers = pILPlayers; +} + +//*********************************************************************************************** +void WolapiObject::PrepareButtonsAndIcons() +{ + // Load shapes for buttons. Store images in this order: up, down, disabled. + //pShpDiscon = LoadShpFile( "discon.shp" ); etc + pShpDiscon = (char*)MFCD::Retrieve( "discon.shp" ); + pShpLeave = (char*)MFCD::Retrieve( "leave.shp" ); + pShpRefresh = (char*)MFCD::Retrieve( "refresh.shp" ); + pShpSquelch = (char*)MFCD::Retrieve( "squelch.shp" ); + pShpBan = (char*)MFCD::Retrieve( "ban.shp" ); + pShpKick = (char*)MFCD::Retrieve( "kick.shp" ); + pShpFindpage = (char*)MFCD::Retrieve( "findpage.shp" ); + pShpOptions = (char*)MFCD::Retrieve( "ops.shp" ); + pShpLadder = (char*)MFCD::Retrieve( "ladder.shp" ); + pShpHelp = (char*)MFCD::Retrieve( "help.shp" ); + + // Set up standard wol buttons, used by both main dialogs. Note hardcoded ID values: must match values in dialog. + int iWolButtons_x = 34; + int iWolButtons_y = 20; + int iWolButtons_dx = 53; + int xWolButton = iWolButtons_x; + int xTTip = 10; // Offset for tooltip. + int yTTip = - 5; // Offset for tooltip. + pShpBtnDiscon = new ShapeButtonClass( 100, pShpDiscon, xWolButton, iWolButtons_y ); + pTTipDiscon = new ToolTipClass( pShpBtnDiscon, TXT_WOL_TTIP_DISCON, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnLeave = new ShapeButtonClass( 101, pShpLeave, xWolButton, iWolButtons_y ); + pTTipLeave = new ToolTipClass( pShpBtnLeave, TXT_WOL_TTIP_LEAVE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnRefresh = new ShapeButtonClass( 102, pShpRefresh, xWolButton, iWolButtons_y ); + pTTipRefresh = new ToolTipClass( pShpBtnRefresh, TXT_WOL_TTIP_REFRESH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnSquelch = new ShapeButtonClass( 103, pShpSquelch, xWolButton, iWolButtons_y ); + pTTipSquelch = new ToolTipClass( pShpBtnSquelch, TXT_WOL_TTIP_SQUELCH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnBan = new ShapeButtonClass( 104, pShpBan, xWolButton, iWolButtons_y ); + pTTipBan = new ToolTipClass( pShpBtnBan, TXT_WOL_TTIP_BAN, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnKick = new ShapeButtonClass( 105, pShpKick, xWolButton, iWolButtons_y ); + pTTipKick = new ToolTipClass( pShpBtnKick, TXT_WOL_TTIP_KICK, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnFindpage = new ShapeButtonClass( 106, pShpFindpage, xWolButton, iWolButtons_y ); + pTTipFindpage = new ToolTipClass( pShpBtnFindpage, TXT_WOL_TTIP_FINDPAGE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton = 452; + pShpBtnOptions = new ShapeButtonClass( 107, pShpOptions, xWolButton, iWolButtons_y ); + pTTipOptions = new ToolTipClass( pShpBtnOptions, TXT_WOL_TTIP_OPTIONS, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnLadder = new ShapeButtonClass( 108, pShpLadder, xWolButton, iWolButtons_y ); + pTTipLadder = new ToolTipClass( pShpBtnLadder, TXT_WOL_TTIP_LADDER, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnHelp = new ShapeButtonClass( 109, pShpHelp, xWolButton, iWolButtons_y ); + pTTipHelp = new ToolTipClass( pShpBtnHelp, TXT_WOL_TTIP_HELP, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + + // Load standard hard-coded icons. + HPALETTE hPal = GetCurrentScreenPalette(); + + int iFileLength; + const char* pFileData; + for( int iDibIcon = 0; iDibIcon != NUMDIBICONS; iDibIcon++ ) + { + //pFileData = LoadFileIntoMemory( DibIconInfos[ iDibIcon ].szFile, iFileLength ); + pFileData = (char*)MFCD::Retrieve( DibIconInfos[ iDibIcon ].szFile ); + if( pFileData ) + { + CCFileClass ccfileDib( DibIconInfos[ iDibIcon ].szFile ); + iFileLength = ccfileDib.Size(); + //debugprint( "Loaded %s, size is %i.\n", DibIconInfos[ iDibIcon ].szFile, iFileLength ); + DibIconInfos[ iDibIcon ].hDIB = LoadDIB_FromMemory( (unsigned char*)pFileData, iFileLength ); + if( DibIconInfos[ iDibIcon ].hDIB ) + { + DibIconInfos[ iDibIcon ].pDIB = (char*)GlobalLock( DibIconInfos[ iDibIcon ].hDIB ); + RemapDIBToPalette( hPal, DibIconInfos[ iDibIcon ].pDIB ); + } +// else +// debugprint( "LoadDIB_FromMemory failed!\n" ); + } +// else +// debugprint( "Couldn't find %s in mix.\n", DibIconInfos[ iDibIcon ].szFile ); + } + + if( DibIconInfos[ DIBICON_LATENCY ].pDIB ) + fLatencyToIconWidth = (float)DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) / 1000; + else + fLatencyToIconWidth = 0; + + // All of the following is for the list of game icons... + + // Load game icons from the wol api. + LPCSTR szSkus; + if( pChat->GetGametypeList( &szSkus ) == S_OK ) + { + // Make two copies of szSkus because strtok insists on messing with them. + char* szSkus1 = new char[ strlen( szSkus ) + 1 ]; + char* szSkus2 = new char[ strlen( szSkus ) + 1 ]; + strcpy( szSkus1, szSkus ); + strcpy( szSkus2, szSkus ); + // Count commas. + char seps[] = ","; + char* token; + nGameTypeInfos = 0; + token = strtok( szSkus1, seps ); + while( token != NULL ) + { + nGameTypeInfos++; + token = strtok( NULL, seps ); + } + // There are actually 2 additional game types available in wolapi - 0 (ws icon) and -1 (wwonline icon). + nGameTypeInfos += 2; + // Create structs to hold infos. +// debugprint( "Creating %i gametypeinfos\n", nGameTypeInfos ); + GameTypeInfos = new WOL_GAMETYPEINFO[ nGameTypeInfos ]; + int iMyIndex = 0; + token = strtok( szSkus2, seps ); + while( token != NULL ) + { + GetGameTypeInfo( atoi( token ), GameTypeInfos[ iMyIndex ], hPal ); + token = strtok( NULL, seps ); + iMyIndex++; + } + // Get the two extra game type infos... + GetGameTypeInfo( -1, GameTypeInfos[ iMyIndex++ ], hPal ); + GetGameTypeInfo( 0, GameTypeInfos[ iMyIndex++ ], hPal ); + } +// else +// debugprint( "GetGametypeList() failed.\n" ); + + // Load icons that we'll need to represent Red Alert GameKinds. + // These are available in wolapi at their old sku number locations. + GetGameTypeInfo( 2, OldRAGameTypeInfos[ 0 ], hPal ); // RA + GetGameTypeInfo( 3, OldRAGameTypeInfos[ 1 ], hPal ); // CS + GetGameTypeInfo( 4, OldRAGameTypeInfos[ 2 ], hPal ); // AM + + if( hPal ) + DeleteObject( hPal ); +} + +//*********************************************************************************************** +void WolapiObject::GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ) +{ + unsigned char* pVirtualFile; + int iFileLength; +// debugprint( "GetGametypeInfo, type %i\n", iGameType ); + LPCSTR szName; + LPCSTR szURL; + pChat->GetGametypeInfo( iGameType, 12, &pVirtualFile, &iFileLength, &szName, &szURL ); + GameTypeInfo.iGameType = iGameType; + if( szName ) + strcpy( GameTypeInfo.szName, szName ); + else + *GameTypeInfo.szName = 0; + if( szURL ) + strcpy( GameTypeInfo.szURL, szURL ); + else + *GameTypeInfo.szURL = 0; + +// debugprint( "LoadDIB_FromMemory( %i, %i )\n", pVirtualFile, iFileLength ); + // Create a DIB by "loading" (as if it was a file) the bitmap data. + GameTypeInfo.hDIB = LoadDIB_FromMemory( pVirtualFile, iFileLength ); +// debugprint( "hDIB is %i\n", GameTypeInfo.hDIB ); + if( !GameTypeInfo.hDIB ) + { + GameTypeInfo.pDIB = NULL; + return; // Load failed. Should not ever happen. + } + GameTypeInfo.pDIB = (const char*)GlobalLock( GameTypeInfo.hDIB ); + +// debugprint( "@@@@@ Created gametypeinfo #%i: name %s, pDIB = %i\n", iIndex, GameTypeInfo.szName, GameTypeInfo.pDIB ); + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GameTypeInfo.pDIB; + if( lpbi->biBitCount != 8 ) + { + // Do not use this loaded bmp, as it's not 256 color. + GlobalUnlock( GameTypeInfo.hDIB ); // Release pDIB. + GameTypeInfo.pDIB = NULL; + DestroyDIB( GameTypeInfo.hDIB ); // Destroy mem alloc'ed for dib bits data. + GameTypeInfo.hDIB = 0; + return; + } + + // Remap colors... + RemapDIBToPalette( hPal, GameTypeInfo.pDIB ); + +} + +//*********************************************************************************************** +void* WolapiObject::IconForGameType( int iGameType ) +{ + // Returns a GameTypeInfos entry by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return (void*)GameTypeInfos[i].pDIB; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::NameOfGameType( int iGameType ) const +{ + // Returns the name of a sku by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szName; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::URLForGameType( int iGameType ) const +{ + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szURL; + } + return NULL; +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, iColorRemap ); +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, RemapControlType* pColorRemap ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, pColorRemap ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::GetChatServer() +{ + // Calls RequestServerList() to get a chat server ready for us to login to. + // Returns S_OK and sets pChatSink->pServer if successful. + WWMessageBox().Process( TXT_WOL_CONNECTING, TXT_NONE ); + +//if( !( ::GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) // ajw - allow use of test servers +//{ + + // Request chat server address from server server. + pChatSink->bRequestServerListWait = true; +// debugprint( "Calling RequestServerList...\n" ); + if( !SUCCEEDED( pChat->RequestServerList( GAME_SKU, GAME_VERSION, "unused", "unused", 5 ) ) ) + { +// debugprint( "RequestServerList call failed\n" ); + return E_FAIL; + } + DWORD dwTimeLimit = timeGetTime(); // ajw My own extra timeout at one minute, in case wolapi chokes. +// debugprint( "Called RequestServerList...\n" ); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + hresPatchResults = 0; + while( pChatSink->bRequestServerListWait && timeGetTime() - dwTimeLimit < 60000 ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } +// debugprint( "PumpMessages after RequestServerList...\n" ); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + // If an "update list" of patches has been received, instead of a server list, this flag will have been set + // for us describing the results. We'll either cancel log in or trigger game exit. + if( hresPatchResults ) + { + pChatSink->bRequestServerListWait = false; + return hresPatchResults; + } + } +// debugprint( "RequestServerList wait finished\n" ); + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->pServer ) + return S_OK; + else + return E_FAIL; + +/* +} +else +{ + // Test using local server on LAN. + + // Bypass RequestServerList, as it is unnecessary and may not be possible if serverserver can't be reached. + // Set SKU manually because normally RequestServerList does this for you. + pChat->SetProductSKU( GAME_SKU ); + if( pChatSink->pServer ) + delete pChatSink->pServer; + pChatSink->pServer = new Server; + if( !( ::GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) + strcpy( (char*)pChatSink->pServer->conndata, "TCP;irc.westwood.com;9000" ); + else + // Control key down as well. + strcpy( (char*)pChatSink->pServer->conndata, "TCP;10.2.20.28;4000" ); + strcpy( (char*)pChatSink->pServer->connlabel, "IRC" ); + strcpy( (char*)pChatSink->pServer->name, "Chat"); + return S_OK; +} +*/ + +} + +//*********************************************************************************************** +HRESULT WolapiObject::AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ) +{ + // If RequestConnection() succeeds, sets pChatSink->bConnected true and returns S_OK. + // Else returns RequestConnection() error result. + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGIN, TXT_NONE ); + +// debugprint( "~1\n" ); + strcpy( (char*)pChatSink->pServer->login, szName ); + strcpy( (char*)pChatSink->pServer->password, szPass ); + +/* +// debugprint( "RequestConnection with:\n%s,%s,%s,%s,%s - %s\n", + pChatSink->pServer->name, + pChatSink->pServer->connlabel, + pChatSink->pServer->conndata, + pChatSink->pServer->login, + pChatSink->pServer->password, + bPassIsMangled ? "(mangled)" : "(unmangled)" ); +*/ + pChatSink->bRequestConnectionWait = true; + pChatSink->hresRequestConnectionError = 0; + + if( !SUCCEEDED( pChat->RequestConnection( pChatSink->pServer, 15, !bPassIsMangled ) ) ) + { +// debugprint( "RequestConnection call failed\n" ); + return CHAT_E_CON_ERROR; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + while( pChatSink->bRequestConnectionWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } + if( bCancel ) + break; + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + } + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->bRequestConnectionWait ) + return CHAT_E_CON_ERROR; + + if( pChatSink->bConnected ) + { + strcpy( szMyName, szName ); + strcpy( szMyRecord, szName ); + strcpy( szMyRecordAM, szName ); + return S_OK; + } + else + return pChatSink->hresRequestConnectionError; +} + +//*********************************************************************************************** +bool WolapiObject::bLoggedIn() +{ + return pChatSink->bConnected; +} + +//*********************************************************************************************** +void WolapiObject::Logout() +{ + // Requests logout from wolapi. Doesn't return any error values, as what we would do if it + // failed - force the user to stay connected? + + if( bSelfDestruct ) + WWMessageBox().Process( TXT_WOL_ERRORLOGOUT, TXT_NONE ); + else + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGOUT, TXT_NONE ); + +// debugprint( "RequestLogout()\n" ); + + pChatSink->bRequestLogoutWait = true; + + if( !SUCCEEDED( pChat->RequestLogout() ) ) + { +// debugprint( "RequestLogout() call failed\n" ); + } + + DWORD dwTimePatience = timeGetTime(); // After 5 seconds we run out of patience and bail. + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestLogoutWait && timeGetTime() - dwTimePatience < 5000 ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + pChatSink->bConnected = false; + *szMyName = 0; + + Sound_Effect( WOLSOUND_LOGOUT ); +} + +//*********************************************************************************************** +bool WolapiObject::UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ) +{ + // This is now non-modal. + // Sends off a request for a new channels list. + +// // Returns false upon total failure. +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + +// pChatSink->bRequestChannelListWait = true; + pChatSink->ChannelFilter = ChannelFilter; + +// debugprint( "RequestChannelList(), iChannelType = %i, filter = %i\n", iChannelType, ChannelFilter ); + if( !SUCCEEDED( pChat->RequestChannelList( iChannelType, bAutoping ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } +/* + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListWait ) + return false; +*/ + LastUpdateChannelCallLevel = CurrentLevel; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnChannelList() +{ + // The chatsink calls this when its OnChannelList() is called, and it has remade its internal channel list. + // The question here is: should we display the values now in the chatsink? + // As UpdateChannels() is now non-modal, there is the danger that we have moved away from the place in + // the channel heirarchy where we originally called RequestChannelList(). + // To help ensure we're getting this where we expect to get it, we check the value of CurrentLevel against + // what it was when we called UpdateChannels(). + if( CurrentLevel == LastUpdateChannelCallLevel ) + ListChannels(); +} + +//*********************************************************************************************** +void WolapiObject::ListChannels() +{ + // Show pChatSink's pChannelList in pILChannels. + // The extra data ptr hidden in each list item will hold a void pointer to the channel described. +// debugprint( "ListChannels(), pChannelList = %i\n", pChatSink->pChannelList ); + + static WOL_LEVEL LevelLastListed = WOL_LEVEL_INVALID; + + int iListViewIndex = 0; + + // If redrawing the same list as before, preserve the view position. + if( LevelLastListed == CurrentLevel ) + iListViewIndex = pILChannels->Get_View_Index(); + else + LevelLastListed = CurrentLevel; + + pILChannels->Clear(); + switch( CurrentLevel ) + { + case WOL_LEVEL_GAMESOFTYPE: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_LOBBIES: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_INLOBBY: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + break; + default: + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + break; + } + + Channel* pChannel = pChatSink->pChannelList; + while( pChannel ) + { + if( pChannel->type == 0 ) + { + // Show chat channel. + char* pShow; + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby == - 1 ) + { + // Regular chat channel. + pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + sprintf( pShow, "%s\t%-3u", (char*)pChannel->name, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_CHAT, (char*)pChannel->name, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, (void*)DibIconInfos[ DIBICON_USER ].pDIB, ICON_DIB, CHANNELTYPE_CHATCHANNEL, (void*)pChannel ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + pShow = new char[ REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( pShow, "%s\t%-3u", szLobbyName, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_LOBBY, szLobbyName, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, IconForGameType( -1 ), ICON_DIB, CHANNELTYPE_LOBBYCHANNEL, (void*)pChannel ); +// debugprint( ":::::added pChannel %i, name %s, as %s\n", pChannel, pChannel->name, pShow ); + } + delete [] pShow; + } + else + { + // Show game channel. + char* pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + char szHelp[ 200 ]; + void* pGameKindIcon; + if( pChannel->type == GAME_TYPE ) + { + // Get RedAlert GameKind. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 0 ].pDIB; // Red Alert icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_REDALERT, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::CSGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 1 ].pDIB; // CS icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_COUNTERSTRIKE, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::AMGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 2 ].pDIB; // AM icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_AFTERMATH, + pChannel->currentUsers, pChannel->maxUsers ); + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + pGameKindIcon = NULL; + break; + } + sprintf( pShow, "%s\t%u/%u", (char*)pChannel->name, pChannel->currentUsers, pChannel->maxUsers ); + } + else + { + pGameKindIcon = IconForGameType( pChannel->type ); + sprintf( pShow, "%s\t%-2u", (char*)pChannel->name, pChannel->currentUsers ); + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_GAME, NameOfGameType( pChannel->type ), + pChannel->currentUsers ); + } + void* pPrivateIcon = NULL; + if( pChannel->flags & CHAN_MODE_KEY ) + { + // Game is private. + pPrivateIcon = (void*)DibIconInfos[ DIBICON_PRIVATE ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_PRIVATEGAME ); + } + + void* pTournamentIcon = NULL; + if( pChannel->tournament ) + { + // Game is tournament. + pTournamentIcon = (void*)DibIconInfos[ DIBICON_TOURNAMENT ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_TOURNAMENTGAME ); + } + + int iLatencyUse = pChannel->latency; + if( iLatencyUse == -1 ) + iLatencyUse = 0; + + static int iLatencyBarX = 227 - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 19; + + pILChannels->Add_Item( pShow, szHelp, pGameKindIcon, ICON_DIB, + CHANNELTYPE_GAMECHANNEL, (void*)pChannel, NULL, + pPrivateIcon, ICON_DIB, pTournamentIcon, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, + iLatencyBarX, 0, iLatencyUse * fLatencyToIconWidth ); + delete [] pShow; + } + pChannel = pChannel->next; + } + if( iListViewIndex ) + pILChannels->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( const char* szChannelName, const char* szKey ) +{ + // Used for CHAT channels (or lobbies) only. Channel type is set to 0. + Channel ChannelTemp; + memset( &ChannelTemp, 0, sizeof( ChannelTemp ) ); + strcpy( (char*)ChannelTemp.name, szChannelName ); + strcpy( (char*)ChannelTemp.key, szKey ); + return ChannelJoin( &ChannelTemp ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( Channel* pChannelToJoin ) +{ + // Returns an HRESULT, the meaning of which is totally customized for my own uses. + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelJoinWait = true; + pChatSink->hresRequestJoinResult = 0; + +// debugprint( "RequestChannelJoin(), %s\n", pChannelToJoin->name ); + HRESULT hRes = pChat->RequestChannelJoin( pChannelToJoin ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelJoin() call failed, result %i ", hRes ); + DebugChatDef( hRes ); + return E_FAIL; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelJoinWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; // Turn on response to channel lists. + + if( pChatSink->bRequestChannelJoinWait ) + return CHAT_E_TIMEOUT; + + switch( pChatSink->hresRequestJoinResult ) + { + case CHAT_E_CHANNELDOESNOTEXIST: + case CHAT_E_BADCHANNELPASSWORD: + case CHAT_E_BANNED: + case CHAT_E_CHANNELFULL: + return pChatSink->hresRequestJoinResult; + } + + if( !pChatSink->bJoined ) + return E_FAIL; + + return S_OK; +} + +//*********************************************************************************************** +bool WolapiObject::ChannelLeave() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelLeaveWait = true; + pChatSink->DeleteUserList(); + +// debugprint( "RequestChannelLeave()\n" ); + if( !SUCCEEDED( pChat->RequestChannelLeave() ) ) + { +// debugprint( "RequestChannelLeave() call failed\n" ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelLeaveWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelLeaveWait || pChatSink->bJoined ) + return false; + + return true; +} + +/* +//*********************************************************************************************** +bool WolapiObject::UserList() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestUserListWait = true; + + // I no longer request a user list, as this was being done only when entering a channel, and it turns out wolapi gives + // us a user list automatically when joining. This function is used as a blocker that waits until the user list has + // definitely arrived. +// debugprint( "RequestUserList()\n" ); +// if( !SUCCEEDED( pChat->RequestUserList() ) ) +// { +// debugprint( "RequestUserList() call failed\n" ); +// return false; +// } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestUserListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestUserListWait ) + { + pChatSink->bRequestUserListWait = false; + return false; + } + + return true; +} +*/ + +//*********************************************************************************************** +//typedef char CHANNELUSERNAME[ WOL_NAME_LEN_MAX ]; +struct CHANNELUSERINFO +{ + char szName[ WOL_NAME_LEN_MAX ]; + bool bFlagged; + RemapControlType* pColorRemap; + HousesType House; // Only used if game channel. + bool bAccept; // Only used if game channel. + char szExtra[ 50 ]; // Only used if game channel. +}; + +//*********************************************************************************************** +bool WolapiObject::ListChannelUsers() +{ + // Show pChatSink's pUserList in pILUsers or pILPlayers (depending on CurrentLevel), after clearing it. + // The extra data ptr hidden in each list item will hold a void pointer to the user described. + // For simplicity, I destroy the old list and write a new one, even though possibly only one item + // may have changed. + // In order for the multiselect flags in the list to persist, I record the names that are flagged + // before clearing the list, then reset them (if found) in the new list. + // This is inefficient, but should be fine in this non-time-critical situation. + // (I also save item color here, and save all items. Now it's really inefficient.) + // (Oh, boy. Now I've added the persistence of house info when this is a game channel...) + // The idea was to avoid duplication of player data, and not have any dependence on the integrity of the chatsink's + // players list. Now it is a case of it working "well enough" to not require time to elegantize it. + + // Extra bonus, if useful: returns true if an operator (channel owner) is found in the channel, false otherwise. + + bool bChannelOwnerFound = false; + + bool bInLobby; + IconListClass* pListToUse; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + bInLobby = false; + pListToUse = pILPlayers; + } + else + { + bInLobby = ( iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ) != -1 ); + pListToUse = pILUsers; + } + + if( pListToUse && // Fails in rare cases when list draw is triggered before it is fully set up. + *szChannelNameCurrent ) // No users to list if not in a channel. + { + // If redrawing the same list as before, preserve the view position. + static char szChannelLastListed[ WOL_CHANNAME_LEN_MAX ] = { 0 }; +//debugprint( "szChannelLastListed '%s', szChannelNameCurrent '%s'\n", szChannelLastListed, szChannelNameCurrent ); + int iListViewIndex = 0; + if( strcmp( szChannelLastListed, szChannelNameCurrent ) == 0 ) + iListViewIndex = pListToUse->Get_View_Index(); + else + strcpy( szChannelLastListed, szChannelNameCurrent ); + +//debugprint( "ListChannelUsers(), pUserList = %i\n", pChatSink->pUserList ); + // Save users in current list. + int iCount = pListToUse->Count(); + CHANNELUSERINFO* pUsersSaved = NULL; + int iUsersSaved = 0; + if( iCount ) + { + pUsersSaved = new CHANNELUSERINFO[ iCount ]; + for( int i = 0; i != iCount; i++ ) + { + PullPlayerName_Into_From( pUsersSaved[ iUsersSaved ].szName, pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bFlagged = pListToUse->bItemIsMultiSelected( i ); + pUsersSaved[ iUsersSaved ].pColorRemap = pListToUse->Get_Item_Color( i ); +// debugprint( " Saving color of %s as %i.\n", pUsersSaved[ iUsersSaved ].szName, pUsersSaved[ iUsersSaved ].pColorRemap ); + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + pUsersSaved[ iUsersSaved ].House = PullPlayerHouse_From( pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bAccept = bItemMarkedAccepted( i ); + const char* szExtra = pListToUse->Get_Item_ExtraDataString( i ); + if( szExtra ) + strcpy( pUsersSaved[ iUsersSaved ].szExtra, szExtra ); + else + *pUsersSaved[ iUsersSaved ].szExtra = 0; + } + iUsersSaved++; + } + } + // Clear list and recreate with new users list. + pListToUse->Clear(); + User* pUser = pChatSink->pUserList; + int iUserCount = 0; + while( pUser ) + { + ++iUserCount; + void* pIcon1 = NULL; + void* pIcon2 = NULL; + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + pIcon1 = (void*)DibIconInfos[ DIBICON_OWNER ].pDIB; + bChannelOwnerFound = true; + } + else + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + pIcon1 = (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB; + else + { + if( pUser->flags & CHAT_USER_VOICE ) + pIcon1 = (void*)DibIconInfos[ DIBICON_VOICE ].pDIB; + else + pIcon1 = (void*)DibIconInfos[ DIBICON_USER ].pDIB; + } + } + if( pUser->flags & CHAT_USER_SQUELCHED ) + pIcon2 = (void*)DibIconInfos[ DIBICON_SQUELCH ].pDIB; + + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL || bInLobby ) + { + int iRank = pNetUtilSink->GetUserRank( (char*)pUser->name, bShowRankRA ); + char szNameToShow[ WOL_NAME_LEN_MAX + 40 ]; + if( iRank ) + { +// debugprint(" Found %s has rank %u\n", (char*)pUser->name, iRank ); + sprintf( szNameToShow, TXT_WOL_USERRANK, (char*)pUser->name, iRank ); + } + else + strcpy( szNameToShow, (char*)pUser->name ); + + static int iLatencyBarX = 124*RESFACTOR - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 5 - 16; + + // If we have had a chance to request pings to the player, there'll be some avg. results waiting for us. + int iLatencyBarWidth = 0; + int iLatency; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); +// debugprint( "player %s ip address %i\n", szNameToShow, UserIP ); + if( UserIP && pNetUtil->GetAvgPing( UserIP, &iLatency ) == S_OK ) + { +// debugprint( "player %s latency %i\n", szNameToShow, iLatency ); + if( iLatency == -1 ) + iLatency = 0; + iLatencyBarWidth = iLatency * fLatencyToIconWidth; + } + } + pListToUse->Add_Item( szNameToShow, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB, NULL, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, iLatencyBarX, 2, iLatencyBarWidth ); + } + else + pListToUse->Add_Item( (char*)pUser->name, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB ); + + pUser = pUser->next; + } + if( pStaticUsers ) + { + // Display number of users in channel. + char szCount[100]; + sprintf( szCount, TXT_WOL_USERLIST, iUserCount ); + pStaticUsers->Set_Text( szCount ); + } + // Reset multiselectedness, color, and item text for a user. Slow. + // (What a bloody, bloody hack.) + for( int iUser = 0; iUser != iUsersSaved; iUser++ ) + { + int iFind = pListToUse->Find( pUsersSaved[ iUser ].szName ); // Finds any item beginning with szName... + if( iFind != -1 ) + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pUsersSaved[ iUser ].House != HOUSE_NONE ) + { + // Append house text to item string, as we found a valid house name after the name, above. + char szItem[ 120 ]; + WritePlayerListItem( szItem, pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].House ); + pListToUse->Set_Item( iFind, szItem ); + } + if( pUsersSaved[ iUser ].bAccept ) + { + // Player was marked "accepted" before. If he has one now, it's because he is the host. + // Else it was an accepted icon before, so put one in again now. (a-hacking-we-will-go) + if( !bItemMarkedAccepted( iFind ) ) + MarkItemAccepted( iFind, true ); + } + if( *pUsersSaved[ iUser ].szExtra ) + pListToUse->Set_Item_ExtraDataString( iFind, pUsersSaved[ iUser ].szExtra ); + } + if( pUsersSaved[ iUser ].bFlagged ) + pListToUse->MultiSelect( iFind, true ); +// debugprint( " Restoring color of %s as %i.\n", pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].pColorRemap ); + pListToUse->Set_Item_Color( iFind, pUsersSaved[ iUser ].pColorRemap ); + } +// else +// debugprint( "ListChannelUsers() - Couldn't find %s!\n", pUsersSaved[ iUser ].szName ); + } + delete [] pUsersSaved; + if( iListViewIndex ) + pListToUse->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. + } + return bChannelOwnerFound; +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedAccepted( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers has an icon pointer in position 0 that + // is either the host icon or the accepted icon. + const IconList_ItemExtras* pItemExtras = pILPlayers->Get_ItemExtras( iIndex ); + return ( pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_OWNER ].pDIB || + pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB ); +} + +//*********************************************************************************************** +bool WolapiObject::MarkItemAccepted( int iIndex, bool bAccept ) +{ + pILPlayers->Flag_To_Redraw(); + if( bAccept ) + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB, ICON_DIB ); + else + //return pILPlayers->Set_Icon( iIndex, 0, NULL, ICON_DIB ); + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB, ICON_DIB ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedReadyToGo( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as "ready to go". + // This is true if the player is marked as "ready" or "need scenario". + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; +// debugprint( "szItem is %s\n", szItem ); + return ( strcmp( szItem, "ready" ) == 0 || strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::MarkItemReadyToGo( int iIndex, const char* szReadyState ) +{ + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + pILPlayers->Flag_To_Redraw(); + pILPlayers->Set_Item_ExtraDataString( iIndex, szReadyState ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedNeedScenario( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as ready to go, but needing scenario download. + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; + return ( strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::PullPlayerName_Into_From( char* szDest, const char* szSource ) +{ + // Sets szDest to the "player name" found in szSource. + // Called "player" name because this is mainly designed for use in game channels. + // Player name appears first in item, separated by a space from anything later. + + char* pSpace = strstr( szSource, " " ); + if( !pSpace ) + { + // No space character. Use entire item. + strcpy( szDest, szSource ); + } + else + { + int iSpacePosition = pSpace - szSource; + strncpy( szDest, szSource, iSpacePosition ); + szDest[ iSpacePosition ] = 0; // terminate + } +// debugprint( "PullPlayerName_Into_From: '%s' from '%s', ok?\n", szDest, szSource ); +} + +//*********************************************************************************************** +HousesType WolapiObject::PullPlayerHouse_From( const char* szSource ) +{ + // Pulls the house value out of a player list item in a game channel. + // House appears as the last word, and it's in <>. +// char* pChar = strrchr( szSource, ' ' ); // Last space character. was failing on roy. uni cause of space +// if( !pChar ) +// return HOUSE_NONE; +// ++pChar; +// if( *pChar++ != '<' ) // We know house has to be last, so if not the case, no house in item. +// return HOUSE_NONE; + char* pChar = strrchr( szSource, '<' ); // Last < character. + if( !pChar ) + return HOUSE_NONE; + ++pChar; + int iLen = strlen( pChar ); // Remaining: "housename>" + // Copy remaining string. + char szHouse[ 30 ]; + strcpy( szHouse, pChar ); + *( szHouse + iLen - 1 ) = 0; // Terminate to remove ">" +// debugprint( "PullPlayerHouse_From: '%s' from '%s', ok?\n", szHouse, szSource ); + // pChar is now a valid house name. + +// return HouseTypeClass::From_Name( szHouse ); +#ifdef ENGLISH + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( szHouse, "Russia" ) == 0 ) + return HOUSE_USSR; + else + return HouseTypeClass::From_Name( szHouse ); // Fails on "Russia". (Thinks "USSR".) +#else + for( HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++ ) + if( strcmp( Text_String(HouseTypeClass::As_Reference(house).Full_Name()), szHouse ) == 0 ) + return house; +// debugprint( "dohfus" ); // should never happen +// Fatal( "" ); + return HOUSE_USSR; +#endif +} + +//*********************************************************************************************** +void WolapiObject::WritePlayerListItem( char* szDest, const char* szName, HousesType House ) +{ + // Sets szDest to the way a player list item appears in a game channel. + char szHouse[ 50 ]; + strcpy( szHouse, Text_String( HouseTypeClass::As_Reference( House ).Full_Name() ) ); + + int iRank = pNetUtilSink->GetUserRank( szName, bShowRankRA ); // Horrendous inefficiency here, when called for relisting players... + if( iRank ) + sprintf( szDest, TXT_WOL_USERRANKHOUSE, szName, iRank, szHouse ); + else + sprintf( szDest, TXT_WOL_USERHOUSE, szName, szHouse ); +// debugprint( "WritePlayerListItem: '%s', ok?\n", szDest ); +} + +//*********************************************************************************************** +void WolapiObject::RequestPlayerPings() +{ + // Does a RequestPing for every other player listed in pILPlayers. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_MYSELF ) ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); + if( UserIP ) + { + int iUnused; + in_addr inaddrUser; + inaddrUser.s_addr = UserIP; + char* szIP = inet_ntoa( inaddrUser ); +// debugprint( "RequestPing of %s, ipaddr of %i, aka %s\n", (char*)pUser->name, UserIP, szIP ); + pNetUtil->RequestPing( szIP, 1000, &iUnused ); + } + } + } +} + +//*********************************************************************************************** +void WolapiObject::SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ) +{ + // Send regular chat message. + + if( *szMessage == 0 ) + return; + + if( strlen( szMessage ) > 4 && szMessage[0] == 63 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + return; + } + if( strlen( szMessage ) > 4 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + } + + // Iterate through ILUsers looking for selected entries. Build up a users list of selected + // items. If the list turns out to be blank, send message publicly. + User* pUserListSend = NULL; + User* pUserNew; + User* pUserTail = NULL; + int iCount = ILUsers.Count(); + int iPrivatePrintLen = 1; + for( int i = 0; i != iCount; i++ ) + { + if( ILUsers.bItemIsMultiSelected( i ) ) + { + pUserNew = new User; + *pUserNew = *( (User*)ILUsers.Get_Item_ExtraDataPtr( i ) ); +// debugprint( "Copied %s for sendmessage.\n", pUserNew->name ); + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserListSend = pUserNew; + } + else + { + pUserTail->next = pUserNew; + } + pUserTail = pUserNew; + iPrivatePrintLen += ( strlen( (char*)pUserNew->name ) + 2 ); // Extra space and comma. + } + } + if( pUserListSend ) + { + // Send private message. + if( !bAction ) + pChat->RequestPrivateMessage( pUserListSend, szMessage ); + else + pChat->RequestPrivateAction( pUserListSend, szMessage ); + char* szPrint = 0; + if( iPrivatePrintLen > 50 ) + { + // Too many users specified to print out. Just say "multiple users". + if( !bAction ) + { + szPrint = new char[ strlen( szMessage ) + 135 ]; + sprintf( szPrint, "%s %s", TXT_WOL_PRIVATETOMULTIPLE, szMessage ); + } + else + { + szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 138 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATETOMULTIPLE, szMyName, szMessage ); + } + } + else + { + if( !bAction ) + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 120 ]; + else + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 125 + strlen( szMyName ) ]; + //strcpy( szPrint, "name ); + if( pUserPrint->next ) + strcat( szPrint, ", " ); + else + strcat( szPrint, ">: " ); + pUserPrint = pUserPrint->next; + } + if( bAction ) + { + strcat( szPrint, szMyName ); + strcat( szPrint, " " ); + } + strcat( szPrint, szMessage ); + } + if( !bAction ) + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + else + { + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } + else + { + // Send public message. + if( !bAction ) + { + // Easter egg related. + if( _stricmp( szMessage, "/nousersounds" ) == 0 ) + { + bEggSounds = false; + return; + } + else if( _stricmp( szMessage, "/usersounds" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEggSounds = true; + return; + } + else if( _stricmp( szMessage, "/8playergames" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEgg8Player = true; + return; + } + HRESULT hRes = pChat->RequestPublicMessage( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicMessage() failed with: " ); +// DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pChat->RequestPublicAction( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicAction() failed with: " ); +// DebugChatDef( hRes ); + } + } + char* szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 10 ]; + if( !bAction ) + { + sprintf( szPrint, "%s: %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + } + else + { + sprintf( szPrint, "%s %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } +} + +//*********************************************************************************************** +bool WolapiObject::ChannelCreate( const char* szChannelName, const char* szKey, bool bGame /* = false */, + int iMaxPlayers /* = 0 */, bool bTournament /* = false */, int iLobby /* = 0 */, + CREATEGAMEINFO::GAMEKIND GameKind /* = red alert */ ) +{ + // Create a channel. + // szKey is NULL if a public channel is to be created, else channel password. + + // Returns true if everything goes okay. + + if( pChatSink->bJoined ) + { + // This never happens. Here just in case. +// debugprint( "WolapiObject::ChannelCreate called when bJoined is true!\n" ); + return false; +// Fatal( "WolapiObject::ChannelCreate called when bJoined is true!" ); + } + + Channel ChannelNew; + + // Prepare the struct. + memset( &ChannelNew, 0, sizeof( ChannelNew ) ); + + if( !bGame ) + { + // ChannelNew.type = 0; 0 for chat channel. + strcpy( (char*)ChannelNew.name, szChannelName ); + } + else + { + ChannelNew.type = GAME_TYPE; + ChannelNew.maxUsers = iMaxPlayers; + ChannelNew.tournament = bTournament; + // Channel 'reserved' stores GameKind in the highest byte, and + // lobby number to return to in the lower three bytes. + // Note: If lobby number is -1 (no lobby to return to), it's encoded as 0x00FFFFFF + ChannelNew.reserved = ( iLobby & 0x00FFFFFF ) | GameKind; + strcpy( (char*)ChannelNew.name, szChannelName ); + } + +// debugprint( "RequestChannelCreate(), channel name: '%s'\n", szChannelName ); + + if( szKey ) + strcpy( (char*)ChannelNew.key, szKey ); + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelCreateWait = true; + + HRESULT hRes = pChat->RequestChannelCreate( &ChannelNew ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelCreate() call failed:" ); + DebugChatDef( hRes ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelCreateWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelCreateWait || !pChatSink->bJoined ) + return false; // Timed out or callback got fail value. + + if( bGame ) + iLobbyReturnAfterGame = iLobby; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoFindPage() +{ + // User presses find/page button. + SimpleEditDlgClass* pFindPageDlg; + + // Ask user for user desired. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pFindPageDlg = new SimpleEditDlgClass( 400, TXT_WOL_PAGELOCATE, TXT_WOL_USERNAMEPROMPT, WOL_NAME_LEN_MAX ); + pFindPageDlg->SetButtons( TXT_WOL_LOCATE, Text_String( TXT_CANCEL ), TXT_WOL_PAGE ); + bPump_In_Call_Back = true; + const char* szNameDlgResult = pFindPageDlg->Show(); + bPump_In_Call_Back = false; + + if( strcmp( szNameDlgResult, Text_String( TXT_CANCEL ) ) == 0 || !*pFindPageDlg->szEdit ) + { + delete pFindPageDlg; + return; + } + + if( strcmp( szNameDlgResult, TXT_WOL_LOCATE ) == 0 ) + { + // Locate user. + HRESULT hRes = Locate( pFindPageDlg->szEdit ); + switch( hRes ) + { + case CHAT_S_FIND_NOTHERE: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOTHERE ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_NOCHAN: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOCHAN ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_OFF: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_OFF ); + bPump_In_Call_Back = false; + break; + case CHAT_E_TIMEOUT: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + bPump_In_Call_Back = false; + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + { + char* szChannel = (char*)pChatSink->OnFindChannel.name; + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannel ); + char* szFound; + if( iLobby != -1 ) + { + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szLobbyName ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szLobbyName ); + } + else + { + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szChannel ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szChannel ); + } + bPump_In_Call_Back = true; + WWMessageBox().Process( szFound ); + bPump_In_Call_Back = false; + delete [] szFound; + break; + } + } + } + else + { + // Page user. + // Ask user for text to send. + SimpleEditDlgClass* pMessDlg = new SimpleEditDlgClass( 600, TXT_WOL_PAGEMESSAGETITLE, + TXT_WOL_PAGEMESSAGEPROMPT, MAXCHATSENDLENGTH ); + bPump_In_Call_Back = true; + if( strcmp( pMessDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pMessDlg->szEdit ) + { + switch( Page( pFindPageDlg->szEdit, pMessDlg->szEdit, true ) ) + { + case CHAT_S_PAGE_NOTHERE: + WWMessageBox().Process( TXT_WOL_PAGE_NOTHERE ); + break; + case CHAT_S_PAGE_OFF: + WWMessageBox().Process( TXT_WOL_PAGE_OFF ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + char szMessage[ WOL_NAME_LEN_MAX + 30 ]; + sprintf( szMessage, TXT_WOL_WASPAGED, pFindPageDlg->szEdit ); + PrintMessage( szMessage, WOLCOLORREMAP_LOCALMACHINEMESS ); + break; + } + } + bPump_In_Call_Back = false; + } + + delete pFindPageDlg; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Locate( const char* szUser ) +{ + // Returns HRESULT with possibly customized meanings. + + char* szMessage = new char[ strlen( TXT_WOL_LOCATING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_LOCATING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + + pChatSink->bRequestFindWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestFind()\n" ); + if( !SUCCEEDED( pChat->RequestFind( &userFind ) ) ) + { +// debugprint( "RequestFind() call failed\n" ); + return 0; + } + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestFindWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Find pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestFindWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestFindResult; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Page( const char* szUser, const char* szSend, bool bWaitForResult ) +{ + // Returns HRESULT with possibly customized meanings. + + if( bWaitForResult ) + { + char* szMessage = new char[ strlen( TXT_WOL_PAGING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_PAGING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + } + + pChatSink->bRequestPageWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestPage()\n" ); + if( !SUCCEEDED( pChat->RequestPage( &userFind, szSend ) ) ) + { +// debugprint( "RequestPage() call failed\n" ); + return 0; + } + if( !bWaitForResult ) + return 0; + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestPageWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Page pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestPageWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestPageResult; +} + +//*********************************************************************************************** +void WolapiObject::DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ) +{ + // Kick selected users. + + if( CurrentLevel != WOL_LEVEL_INCHATCHANNEL && CurrentLevel != WOL_LEVEL_INLOBBY && CurrentLevel != WOL_LEVEL_INGAMECHANNEL ) + { + PrintMessage( TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else if( !bChannelOwner ) + { + PrintMessage( TXT_WOL_ONLYOWNERCANKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else + { + int iFound = 0; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't kick yourself. + { + Kick( pUser ); + if( bAndBan ) + Ban( pUser ); + iFound++; + if( iFound < 5 ) + Sound_Effect( (VocType)( VOC_SCREAM1 + ( rand() % 9 ) ) ); + } + } + } + if( !iFound ) + { + PrintMessage( TXT_WOL_NOONETOKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + } +} + +//*********************************************************************************************** +bool WolapiObject::Kick( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestUserKick()\n" ); + if( !SUCCEEDED( pChat->RequestUserKick( pUserToKick ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::Ban( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestChannelBan()\n" ); + if( !SUCCEEDED( pChat->RequestChannelBan( (char*)pUserToKick->name, true ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoSquelch( IconListClass* pILUsersOrPlayers ) +{ + // Squelch/unsquelch selected users. + bool bFound = false; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser ) + { + if( strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't squelch yourself. + { + Squelch( pUser ); +// char szMess[ 150 ]; +// if( Squelch( pUser ) ) +// sprintf( szMess, TXT_WOL_USERISSQUELCHED, (char*)pUser->name ); +// else +// sprintf( szMess, TXT_WOL_USERISNOTSQUELCHED, (char*)pUser->name ); +// WOL_PrintMessage( chatlist, szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + + bFound = true; + pILUsersOrPlayers->Flag_To_Redraw(); + } + else + PrintMessage( TXT_WOL_CANTSQUELCHSELF, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + } + if( bFound ) + { + Sound_Effect( VOC_SQUISH ); + ListChannelUsers(); // Refresh displayed user list. + } +} + +//*********************************************************************************************** +bool WolapiObject::Squelch( User* pUserToSquelch ) +{ + // Returns true if user is now squelched, false if not squelched. + // Sets User pointer flags value. +// debugprint( "Squelch:: pUser is %i, flags is %i\n", pUserToSquelch, pUserToSquelch->flags ); + + if( pUserToSquelch->flags & CHAT_USER_SQUELCHED ) + { + pChat->SetSquelch( pUserToSquelch, false ); + pUserToSquelch->flags &= ~CHAT_USER_SQUELCHED; + return false; + } + pChat->SetSquelch( pUserToSquelch, true ); + pUserToSquelch->flags |= CHAT_USER_SQUELCHED; + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoOptions() +{ + // Show options dialog. + bPump_In_Call_Back = true; + WOL_Options_Dialog( this, false ); + bPump_In_Call_Back = false; + // Set trigger for an immediate channel list update, in case local lobby games filter was changed. + dwTimeNextChannelUpdate = ::timeGetTime(); +} + +//*********************************************************************************************** +bool WolapiObject::DoLadder() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_LADDERSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; +#ifdef ENGLISH + //return SpawnBrowser( "http://www.westwood.com/ra_ladders.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +#ifdef GERMAN +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_german.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_french.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#endif +#endif + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoHelp() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_HELPSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + const char* szURL; + if( pChat->GetHelpURL( &szURL ) == S_OK ) + return SpawnBrowser( szURL ); + GenericErrorMessage(); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoWebRegistration() +{ + // Get the executable name from the registry. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Westwood\\Register", 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + char szPath[ _MAX_PATH + 1 ]; + DWORD dwBufSize = _MAX_PATH; + if( RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szPath, &dwBufSize ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + RegCloseKey( hKey ); +// debugprint( "Registration app is '%s'\n", szPath ); + + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_WEBREGISTRATIONSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + ::ShellExecute( NULL, "open", szPath, NULL, ".", SW_SHOW ); + return true; + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoGameAdvertising( const Channel* pChannel ) +{ + const char* szURL = URLForGameType( pChannel->type ); + if( !szURL ) + { + GenericErrorMessage(); + return false; + } + + char szQuestion[512]; + sprintf( szQuestion, TXT_WOL_GAMEADVERTSHELL, NameOfGameType( pChannel->type ) ); + bPump_In_Call_Back = true; + if( WWMessageBox().Process( szQuestion, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + return SpawnBrowser( szURL ); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::SpawnBrowser( const char* szURL ) +{ + // Attempts to launch user's web browser, and monitors it, waiting for user to close it, at which + // point we bring focus back to the game. + + // Loosely based on Dune2000 example. + + bool bSuccess = false; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + + if( *szWebBrowser ) + { + char szCommandLine[ _MAX_PATH + 300 ]; + sprintf( szCommandLine, "\"%s\" %s", szWebBrowser, szURL ); +// debugprint( "About to CreateProcess: '%s'\n", szCommandLine ); + Hide_Mouse(); + BlackPalette.Set( FADE_PALETTE_FAST, Call_Back ); +// ::ShowWindow( MainWindow, SW_SHOWMINIMIZED ); + SeenPage.Clear(); + if( ::CreateProcess( NULL, + szCommandLine, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent’s environment block. + NULL, // Use parent’s starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi ) ) // Pointer to PROCESS_INFORMATION structure. + { + if( pi.hProcess ) + { +// debugprint( "CreateProcess: '%s'\n", szCommandLine ); + bSuccess = true; + ::WaitForInputIdle( pi.hProcess, 5000 ); + bPump_In_Call_Back = true; + for( ; ; ) + { + DWORD dwActive; + Call_Back(); + Sleep( 200 ); + ::GetExitCodeProcess( pi.hProcess, &dwActive ); + if( dwActive != STILL_ACTIVE || cancel_current_msgbox ) + { + // Either user closed the browser app, or game is starting and we should return focus to game. + cancel_current_msgbox = false; + ::SetForegroundWindow( MainWindow ); + ::ShowWindow( MainWindow, SW_RESTORE ); + break; + } + if( ::GetTopWindow( NULL ) == MainWindow ) + { + ::ShowWindow( MainWindow, SW_RESTORE ); // In case it was topmost but minimized. + break; + } + } + bPump_In_Call_Back = false; + GamePalette.Set( FADE_PALETTE_FAST, Call_Back ); + Show_Mouse(); + } + } + } + + if( !bSuccess ) + { + // This was the old way - does not pop you back into game when finished... + if( (int)::ShellExecute( NULL, NULL, szURL, NULL, ".", SW_SHOW ) <= 32 ) + { +// debugprint( "ShellExecute\n" ); + // ShellExecute failed as well. Just print a message instead. + GamePalette.Set(); + ::ShowWindow( MainWindow, SW_RESTORE ); + char szError[ 300 ]; + sprintf( szError, TXT_WOL_CANTLAUNCHBROWSER, szURL ); + Show_Mouse(); + WWMessageBox().Process( szError ); + return false; + } + // (We return immediately after launching in this case.) + GamePalette.Set(); + Show_Mouse(); + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::ChannelListTitle( const char* szTitle ) +{ + strcpy( szChannelListTitle, szTitle ); + bChannelListTitleUpdated = true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Top() +{ + // + +// debugprint( "*** EnterLevel_Top\n" ); + // (Might as well hardcode the channels tree.) + + ChannelListTitle( TXT_WOL_TOPLEVELTITLE ); + pILChannels->Clear(); + //void* pTopIcon = (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB; + void* pTopIcon = IconForGameType( 0 ); + pILChannels->Add_Item( TXT_WOL_OFFICIALCHAT, CHANNELTYPE_OFFICIALCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_OFFICIALCHAT ); + pILChannels->Add_Item( TXT_WOL_USERCHAT, CHANNELTYPE_USERCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_USERCHAT ); + pILChannels->Add_Item( TXT_WOL_GAMECHANNELS, CHANNELTYPE_GAMES, pTopIcon, ICON_DIB, CHANNELTYPE_GAMES ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + CurrentLevel = WOL_LEVEL_TOP; + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_OfficialChat() +{ + // + +// debugprint( "*** EnterLevel_OfficialChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_OFFICIALCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_OFFICIALCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_UserChat() +{ + // + +// debugprint( "*** EnterLevel_UserChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_USERCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_USERCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Games() +{ + // + +// debugprint( "*** EnterLevel_Games\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_GAMES; + + ChannelListTitle( TXT_WOL_GAMECHANNELS ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + + // Create entry for our lobbies at the top. + bool bFound = false; + // (There are actually 2 additional game types at the end of GameTypeInfos - for ws icon and wwonline icon.) + for( int i = 0; i < nGameTypeInfos - 2; i++ ) + { + if( GameTypeInfos[ i ].iGameType == GAME_TYPE ) + { + //pILChannels->Add_Item( GameTypeInfos[ i ].szName, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + bFound = true; + break; + } + } + if( !bFound ) + { + // In the production version, this should never happen, as there should always be a gametypeinfo created that matches + // our game type. It depends on the recentness of the WOR file accompanying the wolapi.dll. + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)OldRAGameTypeInfos[ 0 ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + } + + // A pointer to the GameTypeInfos entry is stored in the item for convenience later. + for( i = 0; i < nGameTypeInfos - 2; i++ ) + { + int iType = GameTypeInfos[ i ].iGameType; + if( iType != GAME_TYPE ) // Else it is our game - skip it here since we put it at the top. + { + if( iType != 2 && iType != 3 && iType != 4 ) // Hack needed for the time being, to prevent the old ra games from being seen. + { + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE, GameTypeInfos[ i ].szName ); + pILChannels->Add_Item( GameTypeInfos[ i ].szName, szHelp, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_GAMESOFTYPE, (void*)&GameTypeInfos[ i ] ); + } + } + } + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ) +{ + // + +// debugprint( "*** EnterLevel_GamesOfType: pGameTypeInfo->szName %s, iGameType %i, URL %s\n", pGameTypeInfo->szName, pGameTypeInfo->iGameType, pGameTypeInfo->szURL ); + + CurrentLevel = WOL_LEVEL_GAMESOFTYPE; + if( !UpdateChannels( pGameTypeInfo->iGameType, CHANNELFILTER_NO, true ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( pGameTypeInfo->szName ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Lobbies() +{ + // + +// debugprint( "*** EnterLevel_Lobbies\n" ); + + CurrentLevel = WOL_LEVEL_LOBBIES; + if( !UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_REDALERTLOBBIES ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ) +{ + // Called when a chat channel (or lobby) has been successfully joined. +// debugprint( "*** OnEnteringChatChannel '%s'\n", szChannelName ); + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders if this is a lobby. + if( iLobby != -1 ) + RequestLadders( NULL ); + + // Set channels list. + pILChannels->Clear(); + // Add a "return" choice at the top of the channel list, based on where we want to go 'back' to... + if( iLobby == -1 ) + { + switch( CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_OFFICIALCHAT, NULL, ICON_SHAPE, CHANNELTYPE_OFFICIALCHAT ); + break; + case WOL_LEVEL_USERCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); + break; + default: + // If entering a channel from anywhere else, user must have created the channel. + // Make "back" take them to user channels list. +// if( bICreatedChannel ) // ajw just verifying + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); +/* else + { +// debugprint( "Case that should not occur in OnEnteringChatChannel. CurrentLevel %i\n", CurrentLevel ); + pILChannels->Add_Item( "ERROR in OnEnteringChatChannel", NULL, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + } +*/ + break; + } + } + else + { + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + } + + char* szMess; + if( iLobby == -1 ) + { + CurrentLevel = WOL_LEVEL_INCHATCHANNEL; + szMess = new char[ strlen( TXT_WOL_YOUJOINED ) + strlen( szChannelName ) + 5 ]; + sprintf( szMess, TXT_WOL_YOUJOINED, szChannelName ); + ChannelListTitle( szChannelName ); + } + else + { + CurrentLevel = WOL_LEVEL_INLOBBY; + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOUJOINEDLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOUJOINEDLOBBY, szLobbyName ); + ChannelListTitle( szLobbyName ); + iLobbyLast = iLobby; + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + } + + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; + + // Set users list. + ListChannelUsers(); + + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + Sound_Effect( WOLSOUND_ENTERCHAN ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + if( CurrentLevel == WOL_LEVEL_INLOBBY ) + pShpBtnRefresh->Enable(); + else + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnExitingChatChannel() +{ + // Called when we successfully ExitChannel, or we get kicked out. (Lobbies included.) + + // Clear users list. + pILUsers->Clear(); + if( pStaticUsers ) + pStaticUsers->Set_Text( TXT_WOL_NOUSERLIST ); + +// debugprint( "*** OnExitingChatChannel() - szChannelNameCurrent '%s', CurrentLevel %i\n", szChannelNameCurrent, CurrentLevel ); + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ); + char* szMess; + if( iLobby == -1 ) + { + szMess = new char[ strlen( TXT_WOL_YOULEFT ) + strlen( szChannelNameCurrent ) + 5 ]; + sprintf( szMess, TXT_WOL_YOULEFT, szChannelNameCurrent ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOULEFTLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOULEFTLOBBY, szLobbyName ); + } + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + *szChannelNameCurrent = 0; + CurrentLevel = WOL_LEVEL_INVALID; + + Sound_Effect( WOLSOUND_EXITCHAN ); +} + +//*********************************************************************************************** +bool WolapiObject::ExitChatChannelForGameChannel() +{ + // We are about to try and join/create a game channel, and are currently in a chat channel. + + // Save this channel name, so we can come back to it if game channel join/create fails. + strcpy( szChannelReturnOnGameEnterFail, szChannelNameCurrent ); + + if( !ChannelLeave() ) + { + GenericErrorMessage(); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, + const CREATEGAMEINFO& CreateGameInfo ) +{ + // Called when a game channel has been successfully joined, while still in chat dialog, + // before game dialog has been created. + // CreateGameInfo is copied to GameInfoCurrent, so that we know what kind of a game we're in during setup. + +// debugprint( "*** OnEnteringGameChannel() - %s\n", szChannelName ); + + CurrentLevel = WOL_LEVEL_INGAMECHANNEL; + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; +// GameKindCurrent = GameKind; + GameInfoCurrent = CreateGameInfo; + strcpy( GameInfoCurrent.szPassword, CreateGameInfo.szPassword ); + + // Remove shared buttons from wolchat's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + if( CreateGameInfo.GameKind == CREATEGAMEINFO::AMGAME ) + { + if( bShowRankRA ) + { + // Switch to "show AM rankings" mode. + bShowRankRA = false; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + else + { + if( !bShowRankRA ) + { + // Switch to "show RA rankings" mode. + bShowRankRA = true; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameSetup() +{ + // Called when entering the game setup screen. Controls are initialized. OnEnteringGameChannel + // has just been called earlier. + + // Returns false only if we find there is not host - he must have simultaneously left. + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders. + RequestLadders( NULL ); + + // Request IP addresses. + RequestIPs( NULL ); + + // Set users list. + if( !ListChannelUsers() ) + { + // No host was found currently in channel! + return false; + } + + if( !pGSupDlg->bHost ) + { + char* szMess = new char[ strlen( TXT_WOL_YOUJOINEDGAME ) + WOL_NAME_LEN_MAX + 5 ]; + char szHostName[ WOL_NAME_LEN_MAX ]; + HostNameFromGameChannelName( szHostName, szChannelNameCurrent ); + sprintf( szMess, TXT_WOL_YOUJOINEDGAME, szHostName ); + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + } + else + PrintMessage( TXT_WOL_YOUCREATEDGAME, WOLCOLORREMAP_LOCALMACHINEMESS ); + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnFailedToEnterGameChannel() +{ + if( *szChannelReturnOnGameEnterFail == 0 ) + return; + + // This is called when we fail to join/create a game channel. + *szChannelNameCurrent = 0; + + // Because we don't save the channel key as well, assume the usual lobby password. If we fail, we'll return to top level. + HRESULT hRes = ChannelJoin( szChannelReturnOnGameEnterFail, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + OnEnteringChatChannel( szChannelReturnOnGameEnterFail, false, iChannelLobbyNumber( (unsigned char*)szChannelReturnOnGameEnterFail ) ); + break; + default: + // ChannelJoin returned fail value. + // (Now only applies if you could ever enter a game channel from a non-lobby.) + // There is the possibility that the channel we were in disappeared in the instant between leaving it and + // failing to join the game channel. Or, the channel has a password, that we didn't record. In either + // case, go back to the top level. + GenericErrorMessage(); + EnterLevel_Top(); + } +} + +//*********************************************************************************************** +void WolapiObject::OnExitingGameChannel() +{ + // This is called after we leave a game channel, while still in the game setup dialog. + + // Remove shared buttons from wolgsup's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + CurrentLevel = WOL_LEVEL_INVALID; + *szChannelNameCurrent = 0; +} + +//*********************************************************************************************** +void WolapiObject::RejoinLobbyAfterGame() +{ + // Called to rejoin lobby after EITHER a game, or the game setup dialog. +//debugprint( "RejoinLobbyAfterGame, iLobbyReturnAfterGame is %i\n", iLobbyReturnAfterGame ); + + if( iLobbyReturnAfterGame == -1 ) + { + // Will never happen presumably, if games are always entered via a lobby chat channel. + // We will naturally reenter the top level. + } + else + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, iLobbyReturnAfterGame ); +//debugprint( "RejoinLobbyAfterGame, channel is %s\n", szChannelToJoin ); + + HRESULT hRes = ChannelJoin( szChannelToJoin, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + //OnEnteringChatChannel( szChannelToJoin, false ); Done automatically now in wol_chat. + break; + default: + // Something went wrong when trying to rejoin the lobby we were in. + // We'll go back to the top level instead, which happens automatically if we do this... + iLobbyReturnAfterGame = -1; + break; + } + } +} + +//*********************************************************************************************** +bool WolapiObject::RequestLadders( const char* szName ) +{ + // If szName is NULL, calls RequestLadderList() until all ladder structs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current ladder list. + // This function does not block. + + if( szName && *szName ) + { +// debugprint( "RequestLadderList( %s )\n", szName ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + return true; + } + + char szNames[ ( WOL_NAME_LEN_MAX + 1 ) * 30 ]; // Neal says max is actually 25 names requested at once. Do 24... + + pNetUtilSink->DeleteLadderList(); + + // Do not request more than this number of times, to prevent overloads to ladder server. + // If we have that many people in the channel, forget about doing ladders for all of them. + // Probably this will never come into play (except while testing), because lobbies will be limited in # of users. + int iCallLimit = 4; + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + // Reset names string. + *szNames = 0; + // Get 24 users from list and add names to string. + for( int i = 0; i != 24; ++i ) + { + strcat( szNames, (char*)pUser->name ); + strcat( szNames, ":" ); + pUser = pUser->next; + if( !pUser ) + break; + } + // Remove last colon. + szNames[ strlen( szNames ) - 1 ] = 0; + +// debugprint( "RequestLadderList( %s )\n", szNames ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( --iCallLimit == 0 ) + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestIPs( const char* szName ) +{ + // If szName is NULL, calls RequestUserIP() until IPs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current IP list. + // This function does not block. + + if( szName && *szName ) + { + User user; + strcpy( (char*)user.name, szName ); +// debugprint( "RequestUserIP( %s )\n", szName ); + if( !SUCCEEDED( pChat->RequestUserIP( &user ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + return true; + } + + // Do all users in current chatsink list. + + pChatSink->DeleteUserIPList(); // Clear old user IPs. (To keep searches fast, if we go in and out of game channels a lot.) + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + if( !( pUser->flags & CHAT_USER_MYSELF ) ) + { + if( !SUCCEEDED( pChat->RequestUserIP( pUser ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::SaveChat() +{ + // Basically, a big hack to avoiding restructuring things so that the dialogs are persistent + // objects. Save the contents of the chat list in the chat dialog so that we can refresh it + // after returning from the game setup dialog (if necessary). + // This turns out to be the easiest and most straightforward way to implement this. + pChatSaveLast = pChatSaveList = NULL; + CHATSAVE* pChatSaveNew; + + for( int i = 0; i != pILChat->Count(); i++ ) + { + pChatSaveNew = new CHATSAVE; + const char* szItem = pILChat->Get_Item( i ); + if( strlen( szItem ) < SAVECHATWIDTH ) + strcpy( pChatSaveNew->szText, szItem ); + const IconList_ItemExtras* pItemExtras = pILChat->Get_ItemExtras( i ); + pChatSaveNew->ItemExtras.pColorRemap = pItemExtras->pColorRemap; + pChatSaveNew->next = NULL; + + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; + } +} + +//*********************************************************************************************** +void WolapiObject::RestoreChat() +{ + // See SaveChat()... + CHATSAVE* pChatSave = pChatSaveList; + while( pChatSave ) + { + PrintMessage( pChatSave->szText, pChatSave->ItemExtras.pColorRemap ); + pChatSave = pChatSave->next; + } +} + +//*********************************************************************************************** +void WolapiObject::AddHostLeftMessageToSavedChat( const char* szName ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + sprintf( pChatSaveNew->szText, TXT_WOL_HOSTLEFTGAME, szName ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::AddMessageToSavedChat( const char* szMessage ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + strcpy( pChatSaveNew->szText, szMessage ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::DeleteSavedChat() +{ + // See SaveChat()... + CHATSAVE* pChatSaveNext; + while( pChatSaveList ) + { + pChatSaveNext = pChatSaveList->next; + delete pChatSaveList; + pChatSaveList = pChatSaveNext; + } +} + +//*********************************************************************************************** +void WolapiObject::GenericErrorMessage() +{ + // Displays generic "something bad happened" error message. + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_ERRORMESSAGE ); + bPump_In_Call_Back = false; +} + +//*********************************************************************************************** +bool WolapiObject::GetNameOfBeginningLobby( char* szNameToSet ) +{ + // Checks for game lobbies, sets szNameToSet to the channel name that the new user should enter and returns true if succeeds. + if( !GetLobbyChannels() ) + return false; + + // Chatsink should now have a list of lobbies. + int iCount = 0; + Channel* pChannel = pChatSink->pChannelList; + if( !pChannel ) + // List is empty. + return false; + + // Return the name of the first lobby with less than 50 users. + while( pChannel ) + { + if( pChannel->currentUsers < 50 ) + { + strcpy( szNameToSet, (char*)pChannel->name ); + return true; + } + ++iCount; + pChannel = pChannel->next; + } + + // All lobbies have 50 or more users. So just choose a random one. + int iChoice = ( rand() % iCount ); + pChannel = pChatSink->pChannelList; + for( int i = 0; i != iChoice; i++ ) + pChannel = pChannel->next; + + strcpy( szNameToSet, (char*)pChannel->name ); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::GetLobbyChannels() +{ + // Modal version of UpdateChannels, for fetching lobby names. + +// // Returns false upon total failure. ajxxx do same for other calls +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelListForLobbiesWait = true; + pChatSink->ChannelFilter = CHANNELFILTER_LOBBIES; + +// debugprint( "RequestChannelList() for lobbies\n" ); + if( !SUCCEEDED( pChat->RequestChannelList( 0, false ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListForLobbiesWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListForLobbiesWait ) + return false; + + return true; +} + +//*********************************************************************************************** +const char* WolapiObject::pGameHostName() +{ + // Returns a POINTER (careful - temporary!) to the name of the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return (char*)pUser->name; + } + } + return NULL; +} + +//*********************************************************************************************** +User* WolapiObject::pGameHost() +{ + // Returns a POINTER (careful - temporary!) to the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return pUser; + } + } + return NULL; +} + +//*********************************************************************************************** +bool WolapiObject::SendGameOpt( const char* szSend, User* pUserPriv ) +{ + // Used during game setup to send public or private game options string. + // If pUserPriv is NULL, message is public, else private to pUserPriv. + if( !pUserPriv ) + { +// debugprint( "Send public game opt: '%s'\n", szSend ); + if( !SUCCEEDED( pChat->RequestPublicGameOptions( szSend ) ) ) + { +// debugprint( "RequestPublicGameOptions() call failed\n" ); + return false; + } + } + else + { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUserPriv->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUserPriv, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestGameStart() +{ + // Host is starting a game. + +/* + // Block any users that join the channel in the next microsecond from becoming involved, and + // block any users that leave from being recognized as having left. + //what if someone leaves? + // This is done to preserve the integrity of the ChatSink's user list + pWO->pChatSink->bIgnoreJoin = true; +*/ + pChatSink->bRequestGameStartWait = true; + +// debugprint( "RequestGameStart()\n" ); + if( !SUCCEEDED( pChat->RequestGameStart( pChatSink->pUserList ) ) ) + { +// debugprint( "RequestGameStart() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestGameStartWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestGameStartWait ) + { +// debugprint( "WolapiObject::RequestGameStart returning false\n" ); + pChatSink->bRequestGameStartWait = false; + return false; + } + +// debugprint( "WolapiObject::RequestGameStart returning true\n" ); + return true; +} + +//*********************************************************************************************** +bool WolapiObject::SendGo( const char* szSend ) +{ + // Send a "GO" message to all players included in the list that came back from OnGameStart. + // (Don't just broadcast it. We don't want to include any users that may have joined the channel + // in the last microsecond.) +// debugprint( "SendGo()\n" ); + + User* pUser = pChatSink->pGameUserList; + + while( pUser ) + { +// if( !( pUser->flags & CHAT_USER_MYSELF ) ) Method changed. I now wait for go message to bounce back to me. +// { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUser->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUser, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } +// } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::Init_DisconnectPinging() +{ + // Sets us up to begin "disconnect pinging" - the pinging that occurs when connection is broken + // during a tournament game. The idea is to try and figure out who is responsible for the connection + // going down. We do this by repeatedly pinging the opponent and the game results server. The number + // of successful pings is sent in the game results package. + iDisconnectPingCurrent = 0; + for( int i = 0; i != DISCONNECT_PING_COUNT; ++i ) + { + DisconnectPingResult_Server[ i ] = PING_UNSTARTED; + DisconnectPingResult_Opponent[ i ] = PING_UNSTARTED; + } + bDisconnectPingingCompleted = false; + bDoingDisconnectPinging = true; +} + +//*********************************************************************************************** +bool WolapiObject::Pump_DisconnectPinging() +{ + // Called repeatedly and continuously when it seems a tournament game connection with the opponent + // has been broken. Does PumpMessages() and requests new pings when previous results have been received. + // Returns true when the required number of pings have been completed. + + if( ::timeGetTime() > dwTimeNextWolapiPump ) + { + pChat->PumpMessages(); + pNetUtil->PumpMessages(); + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + switch( DisconnectPingResult_Server[ iDisconnectPingCurrent ] ) + { + case PING_UNSTARTED: + // Pings have yet to be requested. + // Ping game results server. + int iUnused; + if( *szGameResServerHost1 ) + { +// debugprint( "RequestPing ( gameres server )\n" ); + if( pNetUtil->RequestPing( szGameResServerHost1, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( gameres server ) failed\n" ); + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + } + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_WAITING; + } + else + // We never got an address for the gameresults server. Fake fail result. + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + + // Ping opponent. + in_addr inaddr; + char* szIP; + inaddr.s_addr = TournamentOpponentIP; + szIP = inet_ntoa( inaddr ); +// debugprint( "RequestPing ( opponent )\n" ); + if( pNetUtil->RequestPing( szIP, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( opponent ) failed\n" ); + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_BAD; + } + else + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_WAITING; + break; + case PING_WAITING: + // Ping results still pending. (Callback will set vars when results arrive.) + break; + default: + // Ping result for server is in. + if( DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] == PING_WAITING ) + break; + // Both results are in. Begin new ping, or end disconnect pinging. + iDisconnectPingCurrent++; + if( iDisconnectPingCurrent == DISCONNECT_PING_COUNT ) + { + bDisconnectPingingCompleted = true; + bDoingDisconnectPinging = false; + return true; + } + break; + } + return false; +} + +//*********************************************************************************************** +void WolapiObject::DisconnectPingResultsString( char* szStringToSet ) +{ + int iGoodServerPings = 0; + int iGoodPlayerPings = 0; + for( int i = 0; i < DISCONNECT_PING_COUNT; ++i ) + { + if( DisconnectPingResult_Server[ i ] == PING_GOOD ) ++iGoodServerPings; + if( DisconnectPingResult_Opponent[ i ] == PING_GOOD ) ++iGoodPlayerPings; + } + + sprintf( szStringToSet, "%1u/%1u %1u/%1u", iGoodServerPings, DISCONNECT_PING_COUNT, iGoodPlayerPings, DISCONNECT_PING_COUNT ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptionDefaults() +{ + // Get stored defaults for options. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WOLAPI Find Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bFindEnabled = true; + else + bFindEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Page Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bPageEnabled = true; + else + bPageEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Lang Filter", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bLangFilter = true; + else + bLangFilter = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Show All Games", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bAllGamesShown = true; + else + bAllGamesShown = (bool)dwValue; + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ) +{ + // Set options and remember them in registry. + + bFindEnabled = bEnableFind; + bPageEnabled = bEnablePage; + bLangFilter = bLangFilterOn; + bAllGamesShown = bShowAllGames; + + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_WRITE, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue = bFindEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Find Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bPageEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Page Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bLangFilter ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Lang Filter", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bAllGamesShown ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Show All Games", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette() +{ + // Get the palette of the current screen. + // Returns 0 if can't get palette. + // Remember to DeleteObject the HPALETTE if non-zero. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[WINDOW_MAIN][WINDOWX] + LogicPage->Get_XPos(), + WindowList[WINDOW_MAIN][WINDOWY] + LogicPage->Get_YPos(), + WindowList[WINDOW_MAIN][WINDOWWIDTH], + WindowList[WINDOW_MAIN][WINDOWHEIGHT] ); + LPDIRECTDRAWSURFACE lpDDS = draw_window.Get_Graphic_Buffer()->Get_DD_Surface(); + LPDIRECTDRAWPALETTE lpDDP; + HPALETTE hPal = 0; + if( lpDDS->GetPalette( &lpDDP ) == DD_OK ) + { + PALETTEENTRY pe[256]; + if( lpDDP->GetEntries( 0, 0, 256, pe ) == DD_OK ) + { + LOGPALETTE* pLogPal = (LOGPALETTE*)new char[ sizeof( LOGPALETTE ) + sizeof( PALETTEENTRY ) * 255 ]; + pLogPal->palVersion = 0x300; + pLogPal->palNumEntries = 256; + for( int i = 0; i != 256; i++ ) + { + pLogPal->palPalEntry[i].peRed = pe[i].peRed; + pLogPal->palPalEntry[i].peGreen = pe[i].peGreen; + pLogPal->palPalEntry[i].peBlue = pe[i].peBlue; + pLogPal->palPalEntry[i].peFlags = 0; +// debugprint( "DD Palette %03u: %03u, %03u, %03u\n", i, pe[i].peRed, pe[i].peGreen, pe[i].peBlue ); + } + hPal = CreatePalette( pLogPal ); + delete [] pLogPal; + } +// else +// debugprint( "DD GetEntries failed.\n" ); + } +// else +// debugprint( "DD GetPalette failed.\n" ); + + return hPal; +} + +//*********************************************************************************************** +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ) // Note: pDIB is treated as non-const. +{ + // Converts pDIB's actual pixel data to proper values for a different palette (hPal). + // Obeys convention that index 0 in the DIB's palette should map to transparent. For our purposes, make it black. + + // Set the values of the qNewPalette array to hold the new destination palette index we want + // a bmp palette entry to map to. + unsigned char qNewPalette[ 256 ]; + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pDIB; + + RGBQUAD* pRGBEntry = (RGBQUAD*)( (char*)lpbi + lpbi->biSize ); // This now points to the first entry in the bmp's palette table. + +// debugprint( "Starting rgbquads at %i\n", pRGBEntry ); + + // Index zero is supposed to be transparent. In our case, that means make it black. + qNewPalette[0] = GetNearestPaletteIndex( hPal, RGB( 0, 0, 0 ) ); + pRGBEntry++; + + for( int i = 1; i != 256; i++ ) + { + qNewPalette[i] = GetNearestPaletteIndex( hPal, RGB( pRGBEntry->rgbRed, pRGBEntry->rgbGreen, pRGBEntry->rgbBlue ) ); +// if( iIndex == 1 ) +// debugprint( "Remapping bmp %03u to new %03u\n", i, qNewPalette[i] ); + pRGBEntry++; + } + + // Convert the data to values that match game palette. + int iWidth = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + int iSrcPitch = ( iWidth + 3 ) & ~3; + int iLength = iSrcPitch * iHeight; + unsigned char* pBits = (unsigned char*)FindDIBBits( pDIB ); +// debugprint( "First Byte value %03u will become %03u\n", *pBits, qNewPalette[ *pBits ] ); + for( i = 0; i != iLength; i++ ) + { + *pBits++ = qNewPalette[ *pBits ]; + } +} + +/* +//*********************************************************************************************** +char* LoadFileIntoMemory( const char* szFileName, int& iLength ) +{ + // Loads a file into a buffer. + // Delete[] the pointer when you're done with the buffer. + HANDLE hFile; + hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + iLength = GetFileSize( hFile, NULL ); + char* pData = new char[ iLength ]; + DWORD dwBytesRead; + ReadFile( hFile, pData, iLength, &dwBytesRead, NULL ); + if( dwBytesRead != iLength ) +// debugprint( "######LoadFileIntoMemory expected %i bytes and got %i\n", iLength, dwBytesRead ); + CloseHandle( hFile ); + return pData; +} +*/ + +//*********************************************************************************************** +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ) +{ + int iApostrophe = strcspn( szChannelName, "'" ); + memcpy( szNameToSet, szChannelName, iApostrophe ); + szNameToSet[ iApostrophe ] = 0; +} + +#endif diff --git a/REDALERT/WOLAPIOB.H b/REDALERT/WOLAPIOB.H new file mode 100644 index 000000000..ba72c13c9 --- /dev/null +++ b/REDALERT/WOLAPIOB.H @@ -0,0 +1,417 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.h +// ajw 07/10/98 + +// Class WolapiObject is mainly a container so I can avoid globals and keep things clean. +// All WOLAPI interfacing will be done through this object. It's lifetime will begin when +// API functions are first needed and end when we are finished with the API - this will +// presumably parallel the duration of the user's connection to WOL. + +#ifndef WOLAPI_H +#define WOLAPI_H + +#include "RAWolapi.h" +#include "dibapi.h" +#include "IconList.h" + +//*********************************************************************************************** +class IconListClass; +class WOL_GameSetupDialog; +class ToolTipClass; + +#define PUMPSLEEPDURATION 300 // Milliseconds between PumpMessages() calls. +#define EMERGENCY_TIMEOUT 40000 // Longest we wait for a wolapi response before terminating everything. + +// Milliseconds between automatic behaviors. +#define WOLAPIPUMPWAIT 300 +#define CHANNELUPDATEWAIT 45000 + +#define WOL_NAME_LEN_MAX 10 // Includes null-terminator. +#define WOL_PASSWORD_LEN 9 // Includes null-terminator. +#define WOL_CHANNAME_LEN_MAX 17 // Includes null-terminator. +#define WOL_CHANKEY_LEN_MAX 9 // Includes null-terminator. + +#define CHAT_USER_SQUELCHED 0x0004 // Will theoretically be added to the api and implemented. + + +#define LOBBYPASSWORD "not_a_valid_password" // password removed per Security requirements - 8/27/2018 + +#define USERCANCELLED 1 +#define PATCHDOWNLOADED 2 +#define PATCHAVOIDED 3 + +// Special hidden descriptors added to channel list items. +// These serve double-duty as tooltip help text. +#define CHANNELTYPE_TOP TXT_WOL_CHANNELTYPE_TOP +#define CHANNELTYPE_OFFICIALCHAT TXT_WOL_CHANNELTYPE_OFFICIALCHAT +#define CHANNELTYPE_USERCHAT TXT_WOL_CHANNELTYPE_USERCHAT +#define CHANNELTYPE_GAMES TXT_WOL_CHANNELTYPE_GAMES +#define CHANNELTYPE_GAMESOFTYPE "GamesOfType" // Not seen. +#define CHANNELTYPE_CHATCHANNEL "ChatChannel" // Not seen. +#define CHANNELTYPE_GAMECHANNEL "GameChannel" // Not seen. +#define CHANNELTYPE_LOADING TXT_WOL_CHANNELTYPE_LOADING +#define CHANNELTYPE_LOBBIES TXT_WOL_CHANNELTYPE_LOBBIES +#define CHANNELTYPE_LOBBYCHANNEL "LobbyChannel" // Not seen. + +enum WOL_LEVEL +{ + WOL_LEVEL_TOP, // Viewing top level menu choices. + WOL_LEVEL_OFFICIALCHAT, // Viewing official chat channels. + WOL_LEVEL_USERCHAT, // Viewing user chat channels. + WOL_LEVEL_INCHATCHANNEL, // In a chat channel. + WOL_LEVEL_GAMES, // Viewing types (skus) of games. + WOL_LEVEL_GAMESOFTYPE, // Viewing game channels of a type. + WOL_LEVEL_INGAMECHANNEL, // In a game channel. + WOL_LEVEL_LOBBIES, // Viewing the game lobbies. + WOL_LEVEL_INLOBBY, // In a "lobby" chat channel. + WOL_LEVEL_INVALID +}; + +struct WOL_GAMETYPEINFO +{ + int iGameType; + char szName[128]; + char szURL[256]; + HDIB hDIB; // DIB handle. + const char* pDIB; // What you get when you GlobalLock hDIB. +}; + +// Header values for game options messages. Note that 0 is not used! +enum WOL_GAMEOPT +{ + WOL_GAMEOPT_REQCOLOR = 1, // REQuest = guest asks game host for a color + WOL_GAMEOPT_INFCOLOR, // INForm = game host tells guests color of a single player (not "accept-canceling") + WOL_GAMEOPT_INFPARAMS, // host tells guests all common game params + WOL_GAMEOPT_REQHOUSE, // guest tells host he's changed house (REQ because it's guest->host) + WOL_GAMEOPT_INFHOUSE, // host tells guests about new house of a single player + WOL_GAMEOPT_REQACCEPT, // guest tells host he accepts current params + WOL_GAMEOPT_INFACCEPT, // host tells guests that a player accepted + WOL_GAMEOPT_INFSTART, // host tell guests to go into wait for start mode + WOL_GAMEOPT_REQSTART, // guest acknowledges WOL_GAMEOPT_INFSTART + WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, // guests acks WOL_GAMEOPT_INFSTART and asks for scenario download + WOL_GAMEOPT_INFCANCELSTART, // host tells guests to cancel game start, as a change arrived or player joined/left + WOL_GAMEOPT_INFGO, // host tells everyone to start + WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, // host tells new guest a lot of stuff about everyone that's in the game +}; +enum DIBICON +{ + DIBICON_OWNER, + DIBICON_SQUELCH, + DIBICON_LATENCY, + DIBICON_ACCEPT, + DIBICON_NOTACCEPT, + DIBICON_USER, + DIBICON_PRIVATE, + DIBICON_TOURNAMENT, + DIBICON_VOICE, +}; +#define NUMDIBICONS 9 + +struct DIBICONINFO +{ + char szFile[50]; + HDIB hDIB; + const char* pDIB; +}; + +// See SaveChat()... +#define SAVECHATWIDTH 150 // Wider than text that will fit in the chat list window. +struct CHATSAVE // What we save about each individual list item. +{ + char szText[ SAVECHATWIDTH + 1 ]; + IconList_ItemExtras ItemExtras; // Only color is used. + CHATSAVE* next; +}; + +struct CREATEGAMEINFO +{ + enum GAMEKIND // Gets or'ed with lobby number in channel 'reserved' field. + { + RAGAME = 0x01000000, + CSGAME = 0x02000000, + AMGAME = 0x04000000, + }; + + bool bCreateGame; // True if user confirms game creation. + int iPlayerMax; // NOT number of players, but maximum number allowed into game channel. + int iPlayerCount; // Number of initial human players in game. Set at game launch, used for stats. + bool bTournament; + bool bPrivate; + GAMEKIND GameKind; + char szPassword[ WOL_CHANKEY_LEN_MAX ]; // If not blank, key for private game. +}; + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette(); +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ); // Note: pDIB is treated as non-const. +//char* LoadFileIntoMemory( const char* szFileName, int& iLength ); + +//*********************************************************************************************** +class WolapiObject +{ +public: + WolapiObject(); + virtual ~WolapiObject(); + + IChat* pChat; + IDownload* pDownload; + INetUtil* pNetUtil; + DWORD dwChatAdvise; // Value that identifies the "connection" from chat to chatsink. + DWORD dwDownloadAdvise; + DWORD dwNetUtilAdvise; + + RAChatEventSink* pChatSink; + RADownloadEventSink* pDownloadSink; + RANetUtilEventSink* pNetUtilSink; + + bool bChatShownBefore; + + char szLadderServerHost[ 150 ]; + int iLadderServerPort; + char szGameResServerHost1[ 150 ]; + int iGameResServerPort1; + char szGameResServerHost2[ 150 ]; + int iGameResServerPort2; + + bool bFindEnabled; // I have to maintain these, though wolapi should do it for me... + bool bPageEnabled; // Note they are initialized true, as is currently the case in wol. + bool bLangFilter; // + bool bAllGamesShown; + + bool bEggSounds; // Easter egg related. True = user actions trigger sounds. + bool bEgg8Player; // True = 8 player games can be created. This is hidden so that we don't really have to support the feature... + + WOL_LEVEL CurrentLevel; + WOL_LEVEL LastUpdateChannelCallLevel; + char szMyName[WOL_NAME_LEN_MAX]; // Local user's name, valid while connected. + char szMyRecord[ WOL_NAME_LEN_MAX + 80 ]; + char szMyRecordAM[ WOL_NAME_LEN_MAX + 80 ]; + bool bMyRecordUpdated; // True when szMyRecord has changed and not yet recognized by chat dialog. + char szChannelListTitle[ 100 ]; + bool bChannelListTitleUpdated; + char szChannelNameCurrent[WOL_CHANNAME_LEN_MAX]; + bool bChannelOwner; + char szChannelReturnOnGameEnterFail[WOL_CHANNAME_LEN_MAX]; + + int iLobbyReturnAfterGame; // When in game channel, part of the value of the channel's 'reserved' field. + + bool bReturningAfterGame; + + int iLobbyLast; // Number of last lobby we personally were in. + +// CREATEGAMEINFO::GAMEKIND GameKindCurrent; // Kind of game (Red Alert, CS, AM) we are in game setup for. + CREATEGAMEINFO GameInfoCurrent; // Kind of game (Red Alert, CS, AM, tournament, private) we are in game setup for. + bool bEnableNewAftermathUnits; // Used to pass game parameter back to init only. + + DWORD dwTimeNextWolapiPump; + DWORD dwTimeNextChannelUpdate; + + DIBICONINFO DibIconInfos[ NUMDIBICONS ]; + + HRESULT hresPatchResults; // Used when a patch has been downloaded or cancelled. + + WOL_GameSetupDialog* pGSupDlg; // When in a game channel, setting up a game; ptr to the dialog. + + bool bInGame; // True while playing a game. + bool bConnectionDown; // Flag used while in a game, set to true if connection goes down. + bool bGameServer; // Flag used while in a game, true if game server (host). + + unsigned long TournamentOpponentIP; // Valid while playing a tournament game. IP address of opponent. + + bool bPump_In_Call_Back; // Used to enable PumpMessages during Call_Back(), for when we're in a modal dialog. + + bool bSelfDestruct; // If set true, causes logout and deletion of wolapi object. + + char szWebBrowser[ _MAX_PATH + 1 ]; + + // For "disconnect pinging". + bool bDoingDisconnectPinging; + bool bDisconnectPingingCompleted; + int iDisconnectPingCurrent; + DISCONNECT_PING_STATUS DisconnectPingResult_Server[ DISCONNECT_PING_COUNT ]; + DISCONNECT_PING_STATUS DisconnectPingResult_Opponent[ DISCONNECT_PING_COUNT ]; + + // Used for in-game paging and responding. + char szExternalPager[ WOL_NAME_LEN_MAX ]; // Last person to page me from outside the game, or blank for none. + bool bFreezeExternalPager; + + bool bShowRankRA; // true = view RA rankings, false = view AM rankings + bool bShowRankUpdated; // set true when bShowRankRA value changes + + // Standard wol buttons. + char* pShpDiscon; + char* pShpLeave; + char* pShpRefresh; + char* pShpSquelch; + char* pShpBan; + char* pShpKick; + char* pShpFindpage; + char* pShpOptions; + char* pShpLadder; + char* pShpHelp; + ShapeButtonClass* pShpBtnDiscon; + ShapeButtonClass* pShpBtnLeave; + ShapeButtonClass* pShpBtnRefresh; + ShapeButtonClass* pShpBtnSquelch; + ShapeButtonClass* pShpBtnBan; + ShapeButtonClass* pShpBtnKick; + ShapeButtonClass* pShpBtnFindpage; + ShapeButtonClass* pShpBtnOptions; + ShapeButtonClass* pShpBtnLadder; + ShapeButtonClass* pShpBtnHelp; + ToolTipClass* pTTipDiscon; + ToolTipClass* pTTipLeave; + ToolTipClass* pTTipRefresh; + ToolTipClass* pTTipSquelch; + ToolTipClass* pTTipBan; + ToolTipClass* pTTipKick; + ToolTipClass* pTTipFindpage; + ToolTipClass* pTTipOptions; + ToolTipClass* pTTipLadder; + ToolTipClass* pTTipHelp; + + WOL_GAMETYPEINFO OldRAGameTypeInfos[ 3 ]; // Used for storing old red alert icons only. + +public: + bool bLoggedIn(); + + void LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ); + void ClearListPtrs(); + void LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ); + + void PrepareButtonsAndIcons(); + + bool bSetupCOMStuff(); + void UnsetupCOMStuff(); + + void PrintMessage( const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); + void PrintMessage( const char* szText, RemapControlType* pColorRemap ); + + HRESULT GetChatServer(); + HRESULT AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ); + void Logout(); + bool UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ); + void OnChannelList(); + void ListChannels(); + HRESULT ChannelJoin( const char* szChannelName, const char* szKey ); + HRESULT ChannelJoin( Channel* pChannelToJoin ); + bool ChannelLeave(); +// bool UserList(); + bool ListChannelUsers(); + + bool bItemMarkedAccepted( int iIndex ); + bool MarkItemAccepted( int iIndex, bool bAccept ); + bool bItemMarkedReadyToGo( int iIndex ); + void MarkItemReadyToGo( int iIndex, const char* szReadyState ); + bool bItemMarkedNeedScenario( int iIndex ); + void PullPlayerName_Into_From( char* szDest, const char* szSource ); + HousesType PullPlayerHouse_From( const char* szSource ); + void WritePlayerListItem( char* szDest, const char* szName, HousesType House ); + + void RequestPlayerPings(); + + void SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ); + bool ChannelCreate( const char* szChannelName, const char* szKey, bool bGame = false, int iMaxPlayers = 0, + bool bTournament = false, int iLobby = 0, CREATEGAMEINFO::GAMEKIND GameKind = CREATEGAMEINFO::RAGAME ); + void DoFindPage(); + HRESULT Locate( const char* szUser ); + HRESULT Page( const char* szUser, const char* szSend, bool bWaitForResult ); + void DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ); + bool Kick( User* pUserToKick ); + bool Ban( User* pUserToKick ); + void DoSquelch( IconListClass* pILUsersOrPlayers ); + bool Squelch( User* pUserToSquelch ); + void DoOptions(); + bool DoLadder(); + bool DoHelp(); + bool DoWebRegistration(); + bool DoGameAdvertising( const Channel* pChannel ); + bool SpawnBrowser( const char* szURL ); + + void ChannelListTitle( const char* szTitle ); + bool EnterLevel_Top(); + bool EnterLevel_OfficialChat(); + bool EnterLevel_UserChat(); + bool EnterLevel_Games(); + bool EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ); + bool EnterLevel_Lobbies(); + bool OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ); + void OnExitingChatChannel(); + bool ExitChatChannelForGameChannel(); + bool OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, const CREATEGAMEINFO& CreateGameInfo ); + bool OnEnteringGameSetup(); + void OnFailedToEnterGameChannel(); + void OnExitingGameChannel(); + void RejoinLobbyAfterGame(); + + bool RequestLadders( const char* szName ); + bool RequestIPs( const char* szName ); + + void SaveChat(); + void RestoreChat(); + void AddHostLeftMessageToSavedChat( const char* szName ); + void AddMessageToSavedChat( const char* szMessage ); + void DeleteSavedChat(); + void GenericErrorMessage(); + + bool GetNameOfBeginningLobby( char* szNameToSet ); + bool GetLobbyChannels(); + + const char* pGameHostName(); + User* pGameHost(); + bool SendGameOpt( const char* szSend, User* pUserPriv ); + bool RequestGameStart(); + bool SendGo( const char* szSend ); + + void Init_DisconnectPinging(); + bool Pump_DisconnectPinging(); + void DisconnectPingResultsString( char* szStringToSet ); + + void SetOptionDefaults(); + void SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ); + +protected: + void GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ); + void* IconForGameType( int iGameType ); + const char* NameOfGameType( int iGameType ) const; + const char* URLForGameType( int iGameType ) const; + +protected: + // Used by the general chat dialog. + IconListClass* pILChat; // Main messages list. + IconListClass* pILChannels; // Channels list. + IconListClass* pILUsers; // Users list. + +// IconListClass* pILDisc; // Main messages list. (pILChat is used.) + IconListClass* pILPlayers; // Players list. + + StaticButtonClass* pStaticUsers; // Title for a users list. Used by main chat dialog only, not by game setup. + + WOL_GAMETYPEINFO* GameTypeInfos; + unsigned int nGameTypeInfos; + + float fLatencyToIconWidth; + + CHATSAVE* pChatSaveList; + CHATSAVE* pChatSaveLast; +}; + +#endif + +#endif diff --git a/REDALERT/WOLDEBUG.H b/REDALERT/WOLDEBUG.H new file mode 100644 index 000000000..d3aa16448 --- /dev/null +++ b/REDALERT/WOLDEBUG.H @@ -0,0 +1,32 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#if 0 + +#define debugprogress +#define _ASSERTE( x ) + +#else + +#define debugprint OutputDebugString + +#define _DEBUG +#include "w95trace.h" + +#define debugprogress debugprint( "...%s: %i\n", __FILE__, __LINE__ ) +#define _ASSERTE( x ) if( !(x) ) debugprint( "ASSERT FALSE!\n" ); + +#endif diff --git a/REDALERT/WOLEDIT.CPP b/REDALERT/WOLEDIT.CPP new file mode 100644 index 000000000..05b63752d --- /dev/null +++ b/REDALERT/WOLEDIT.CPP @@ -0,0 +1,164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * Note: An editbox of this class cannot be made read-only. See comment below. + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "WOLEdit.h" + +//#include "WolDebug.h" + +bool bTabKeyPressedHack = false; + +//*********************************************************************************************** +void WOLEditClass::Draw_Text( char const * text ) +{ + // Only difference between this and EditClass: cursor shows up when + // string is at MaxLength. + + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && // strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + +//*********************************************************************************************** +// Override of EditClass::Action, because the base class does not behave correctly in certain circumstances. +// (Escape key is being processed as enter key.) +// Again, I'm not about to change the base class directly, as I'm trying to have as minimal an affect as possible on +// the current game code. -ajw +int WOLEditClass::Action(unsigned flags, KeyNumType & key) +{ + // (Mostly duplicated from base class ::Action) +/* For some painful reason, IsReadOnly is private in the base class, so I can't do the following. + For this reason, don't make a WOLEditClass edit box read-only. + + // + // If this is a read-only edit box, it's a display-only device + // + if (IsReadOnly) { + return(false); + } +*/ + + //debugprint( "WOLEditClass::Action this=%i, flags=0x%x, key=0x%x\n", this, flags, key ); + // + // If the left mouse button is pressed over this gadget, then set the focus to + // this gadget. The event flag is cleared so that no button ID number is returned. + // + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + // + // Handle keyboard events here. Normally, the key is added to the string, but if the + // RETURN key is pressed, then the button ID number is returned from the Input() + // function. + // + if ((flags & KEYBOARD) && Has_Focus()) { + + // + // Process the keyboard character. If indicated, consume this keyboard event + // so that the edit gadget ID number is not returned. + // + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + + // + // Allow numeric keypad presses to map to ascii numbers + // + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + key = (KeyNumType)(key & ~WWKEY_VK_BIT); + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + // + // Filter out all special keys except return and backspace + // + if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) + || key == KN_RETURN || key == KN_BACKSPACE) { + + + + if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + if( key == KN_TAB ) + { + bTabKeyPressedHack = true; + } + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + else + { + // ajw added +// if( key == ( KN_ESC | WWKEY_RLS_BIT ) && ( key & WWKEY_ALT_BIT ) ) +// { + //Clear_Focus(); + flags = 0; + key = KN_NONE; +// } + } + + return(ControlClass::Action(flags, key)); +} + +#endif diff --git a/REDALERT/WOLEDIT.H b/REDALERT/WOLEDIT.H new file mode 100644 index 000000000..5f46dea3c --- /dev/null +++ b/REDALERT/WOLEDIT.H @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "Function.h" + +class WOLEditClass : public EditClass +{ +public: + WOLEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + EditClass( id, text, max_len, flags, x, y, w, h, style ) {} + + virtual int Action (unsigned flags, KeyNumType &key); // Override of base + +protected: + virtual void Draw_Text( char const * text ); // Override of base + +}; + +#endif diff --git a/REDALERT/WOLSTRNG.CPP b/REDALERT/WOLSTRNG.CPP new file mode 100644 index 000000000..9ab304e48 --- /dev/null +++ b/REDALERT/WOLSTRNG.CPP @@ -0,0 +1,804 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// New character strings for wolapi integration. + +//#ifdef WOLAPI_INTEGRATION +#include "function.h" +#include "WolStrng.h" + +//#undef ENGLISH +//#define GERMAN + +#ifdef ENGLISH +#pragma message( "...Building English version..." ) + +// Menu choice for Internet game. +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +// Generic error message, though implies that blame lies with Westwood Online. +const char TXT_WOL_ERRORMESSAGE[] = "Unexpected error occurred communicating with Westwood Online."; +// Connect button on login dialog. +const char TXT_WOL_CONNECT[] = "Connect"; +// Title for login dialog. +const char TXT_WOL_LOGINDIALOG[] = "Westwood Online Login"; +// Appears on login dialog - user login name field. +const char TXT_WOL_NAME[] = "Nickname"; +// Appears on login dialog - user password field. +const char TXT_WOL_PASSWORD[] = "Password"; +// Appears on login dialog - checkbox specifying whether nickname/password should be saved to disk. +const char TXT_WOL_SAVELOGIN[] = "Save"; +// User hit the Escape button to cancel the logging in process. +const char TXT_WOL_LOGINCANCEL[] = "Login cancelled."; + +const char TXT_WOL_MISSINGNAME[] = "Please enter your login nickname."; + +const char TXT_WOL_MISSINGPASSWORD[] = "Please enter your login password."; + +const char TXT_WOL_CANTSAVENICK[] = "Error saving nickname/password."; + +const char TXT_WOL_NICKINUSE[] = "That nickname is in use. Please select another."; + +const char TXT_WOL_BADPASS[] = "Invalid password for this nickname."; + +const char TXT_WOL_TIMEOUT[] = "Connection to Westwood Online timed out."; + +const char TXT_WOL_CONNECTING[] = "Connecting to Westwood Online..."; + +const char TXT_WOL_CANTCONNECT[] = "Could not establish connection to Westwood Online."; +// Appears while connecting and logging in to Westwood Online. +const char TXT_WOL_ATTEMPTLOGIN[] = "Logging in..."; +// Appears while logging out and disconnecting from Westwood Online. +const char TXT_WOL_ATTEMPTLOGOUT[] = "Logging out..."; +// Appears while logging out and disconnecting from Westwood Online after an error has occurred. +const char TXT_WOL_ERRORLOGOUT[] = "Terminating connection with Westwood Online..."; +// Common "please wait" message. +const char TXT_WOL_WAIT[] = "Please wait... communicating with Westwood Online..."; +// Title for the top WW Online level. +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +// Title for the WW Online level where "official" chat channels are listed. +const char TXT_WOL_OFFICIALCHAT[] = "Official Chat"; +// Title for the WW Online level where "user" (in other words, unofficial) chat channels are listed. +const char TXT_WOL_USERCHAT[] = "User Chat"; +// Title for the WW Online level where game channels are listed. +const char TXT_WOL_GAMECHANNELS[] = "Game Channels"; +// Title for the WW Online level where Red Alert game lobbies are listed. +const char TXT_WOL_REDALERTLOBBIES[] = "Red Alert Lobbies"; +// Appears briefly while a list of channels is being downloaded. +const char TXT_WOL_CHANNELLISTLOADING[] = "...downloading..."; + +const char TXT_WOL_YOURENOTINCHANNEL[] = "You are not currently in a chat channel."; +// "Action" button. Causes text entered by user to show up as if they were performing an action, as opposed to speaking. +const char TXT_WOL_ACTION[] = "Action"; +// "Join" button. Allows user to join a channel, game, or WW Online level. +const char TXT_WOL_JOIN[] = "Join"; + +const char TXT_WOL_CANTCREATEINCHANNEL[] = "You can't create a new channel until you exit this channel."; +// "New" button. Allows user to create a new chat channel or game. +const char TXT_WOL_NEWSOMETHING[] = "New"; +// Title for chat channel creation dialog. +const char TXT_WOL_CREATECHANNELTITLE[] = "Create Channel"; + +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel Name: "; +// Prompt for fields where the user must enter a password. +const char TXT_WOL_PASSPROMPT[] = "Password: "; +// Prompt for fields where the user may enter a password, but it is not required. +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Password (optional): "; +// Appears in channel list, as top choice, which the user can use to go back to the top WW Online level. +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +// Appears in channel list, as top choice, which the user can use to go back up one WW Online level. +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOUJOINED[] = "You have joined the %s channel."; +// %s is replaced by the name of a user. +const char TXT_WOL_YOUJOINEDGAME[] = "You have joined %s's game."; +// Message confirming that user created a new game. +const char TXT_WOL_YOUCREATEDGAME[] = "New game created."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOUJOINEDLOBBY[] = "You have entered the %s lobby."; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOULEFT[] = "You have left the %s channel."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOULEFTLOBBY[] = "You have left the %s lobby."; +// Title for dialog that prompts user for the password needed to enter a private channel. +const char TXT_WOL_JOINPRIVATETITLE[] = "Join Private Channel"; + +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Enter Channel Password: "; + +const char TXT_WOL_BADCHANKEY[] = "Incorrect channel password."; +// Title for the Page/Locate dialog. Page = send a user a message. Locate = find out where a user is. +const char TXT_WOL_PAGELOCATE[] = "Page/Locate"; +// Appears on Page/Locate dialog. +const char TXT_WOL_USERNAMEPROMPT[] = "User Name: "; +// Text for Page button on dialog. +const char TXT_WOL_PAGE[] = "Page"; +// Text for Locate button on dialog. +const char TXT_WOL_LOCATE[] = "Locate"; +// %s is replaced with name of user being located. +const char TXT_WOL_LOCATING[] = "Locating %s..."; + +const char TXT_WOL_FIND_NOTHERE[] = "The specified user name does not exist."; + +const char TXT_WOL_FIND_NOCHAN[] = "The specified user is currently not in a channel."; + +const char TXT_WOL_FIND_OFF[] = "The specified user has disabled find capability."; +// %s is replaced with name of user being located. +const char TXT_WOL_FOUNDIN[] = "User found in the %s channel."; +// Title for Page dialog. +const char TXT_WOL_PAGEMESSAGETITLE[] = "Page User"; +// Prompt for field in which user enters the message that is to be sent to user. +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message to Send: "; +// %s is replaced with name of user being paged. +const char TXT_WOL_PAGING[] = "Paging %s..."; + +const char TXT_WOL_PAGE_NOTHERE[] = "The specified user is not logged in."; + +const char TXT_WOL_PAGE_OFF[] = "The specified user has disabled page capability."; +// First %s is replaced with user name, second %s with a text message. +const char TXT_WOL_ONPAGE[] = "Page from %s: %s"; +// %s is replaced with name of user being paged. +const char TXT_WOL_WASPAGED[] = "%s was successfully paged."; +// %s is replaced with the name of a user that has just been squelched. (Currently unused.) +//const char TXT_WOL_USERISSQUELCHED[] = "%s has been squelched."; +// %s is replaced with the name of a user that has had squelch removed. (Currently unused.) +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s is no longer squelched."; + +const char TXT_WOL_ONLYOWNERCANKICK[] = "Only the channel owner can kick users out."; +// Both %s replaced with user names. +const char TXT_WOL_USERKICKEDUSER[] = "%s kicked %s out of the channel."; +// %s replaced with user name. +const char TXT_WOL_USERKICKEDYOU[] = "You were kicked out of the channel by %s."; + +const char TXT_WOL_NOONETOKICK[] = "Select the user(s) you wish to kick out."; +// %s replaced with user name. +const char TXT_WOL_USERWASBANNED[] = "%s has been banned from the channel."; +// Title for dialog in which user enters password for new game they are creating. +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Create Private Game"; + +const char TXT_WOL_YOUREBANNED[] = "You've been banned from entering this channel."; +// %s replaced with user name. +const char TXT_WOL_PLAYERLEFTGAME[] = "%s has left the game."; +// %s replaced with user name. +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s has joined the game."; + +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "You've been kicked out of the game."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Red Alert: Ranked %u. Won %u. Lost %u. Points %u."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. Appended Aftermath ranking. +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Aftermath: Ranked %u. Won %u. Lost %u. Points %u."; +// Used to show brief user ladder ranking in user lists. Example: FredX (Rank 134) +const char TXT_WOL_USERRANK[] = "%s (Rank %u)"; +// No need to translate. +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +// "Rank" translates the same here as above. +const char TXT_WOL_USERRANKHOUSE[] = "%s (Rank %u) <%s>"; +// Button host user presses to start a game they have created. +const char TXT_WOL_STARTBUTTON[] = "Start"; +// Button that guests joining a game press to indicate that they agree to the game rules set up by the host. +const char TXT_WOL_ACCEPTBUTTON[] = "Accept"; +// %s replaced with user name. +const char TXT_WOL_HOSTLEFTGAME[] = "%s has cancelled the game."; +// Appears when game is actually being started. +const char TXT_WOL_WAITINGTOSTART[] = "Launching game..."; +// Tooltip help for WW Online button: disconnect. +const char TXT_WOL_TTIP_DISCON[] = " Leave Westwood Online "; +// Tooltip help for WW Online button: leave current channel. +const char TXT_WOL_TTIP_LEAVE[] = " Leave the channel you are in "; +// Tooltip help for WW Online button: refresh current list. +const char TXT_WOL_TTIP_REFRESH[] = " Refresh current channel list "; +// Tooltip help for WW Online button: squelch user(s). +const char TXT_WOL_TTIP_SQUELCH[] = " Enable/disable incoming message from user(s) "; +// Tooltip help for WW Online button: ban (and kick) user(s). +const char TXT_WOL_TTIP_BAN[] = " Ban user(s) from channel "; +// Tooltip help for WW Online button: kick user(s). +const char TXT_WOL_TTIP_KICK[] = " Kick user(s) out of channel "; +// Tooltip help for WW Online button: find/page. +const char TXT_WOL_TTIP_FINDPAGE[] = " Find or page a user "; +// Tooltip help for WW Online button: show options dialog. +const char TXT_WOL_TTIP_OPTIONS[] = " Set Westwood Online options "; +// Tooltip help for WW Online button: browse game ladder. +const char TXT_WOL_TTIP_LADDER[] = " Browse Red Alert ladders "; +// Tooltip help for WW Online button: show help. +const char TXT_WOL_TTIP_HELP[] = " Show Westwood Online help "; +// Tooltip help. Appears for button host presses to start a game. +const char TXT_WOL_TTIP_START[] = " Start the game "; +// Tooltip help. Appears for button guests press in order to agree to (accept) game rules set up by the host. +const char TXT_WOL_TTIP_ACCEPT[] = " Accept the current game settings "; +// Tooltip help. Appears for the small buttons that allow users to enlarge or diminish the size of channel/user lists. +const char TXT_WOL_TTIP_EXPANDLIST[] = " Expand/contract list "; +// Tooltip for Cancel button during game setup. +const char TXT_WOL_TTIP_CANCELGAME[] = " Go back a level "; +// Tooltip for Join button during chat. +const char TXT_WOL_TTIP_JOIN[] = " Join a chat or game channel "; +// Tooltip for Back button during chat. +const char TXT_WOL_TTIP_BACK[] = " Go back a level "; +// Tooltip for New button during chat. +const char TXT_WOL_TTIP_CREATE[] = " Create a new chat/game channel "; +// Tooltip for Action button. +const char TXT_WOL_TTIP_ACTION[] = " Action message "; + +const char TXT_WOL_OPTFIND[] = "Let others FIND you."; + +const char TXT_WOL_OPTPAGE[] = "Let others PAGE you."; + +const char TXT_WOL_OPTLANGUAGE[] = "Filter out bad language."; +// "Display just the games that were created by someone in the lobby you are currently in." +const char TXT_WOL_OPTGAMESCOPE[] = "Show local lobby games only."; + +const char TXT_WOL_CHANNELGONE[] = "Channel no longer exists."; +// Title for create new game dialog. +const char TXT_WOL_CG_TITLE[] = "Create Game"; +// %i replaced by number of players allowed into game channel. +const char TXT_WOL_CG_PLAYERS[] = "Players: %i"; +// Marks field indicating whether or not this is a tournament game. +const char TXT_WOL_CG_TOURNAMENT[] = "Tournament"; +// Marks field indicating whether or not this is a private game. +const char TXT_WOL_CG_PRIVACY[] = "Private"; + +const char TXT_WOL_CG_RAGAME[] = "Red Alert game"; + +const char TXT_WOL_CG_CSGAME[] = "Counterstrike game"; + +const char TXT_WOL_CG_AMGAME[] = "Aftermath game"; + +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sorry, you must have Counterstrike installed to play this game."; + +const char TXT_WOL_NEEDAFTERMATH[] = "Sorry, you must have Aftermath installed to play this game."; +// %s = name of channel, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doubleclick to join the '%s' channel (%i current users). "; +// %s = name of lobby, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doubleclick to join the '%s' lobby (%i current users). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_REDALERT[] = "Red Alert"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Counterstrike"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_AFTERMATH[] = "Aftermath"; +// %s = name of user, first %i = number of players in channel, second %i = maximum number of players allowed. +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s game (%i players of a maximum %i). "; +// %s = name of user, %i = number of players in channel. +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s game (%i players). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Private) "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournament) "; +// %s is a kind of game, for example, "Dune 2000". +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doubleclick to view %s games. "; + +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Tournament games must be two player games."; +// Shows on game setup screen for private games. %s = password for game. +const char TXT_WOL_PRIVATEPASSWORD[] = "Password: %s"; +// User cannot join game because either he or the game host has hacked the game. +const char TXT_WOL_RULESMISMATCH[] = "Your game is incompatible with the host's!"; +// Message appears when game host presses start button but slow responses cause an automatic cancellation of game start. +const char TXT_WOL_STARTTIMEOUT[] = "Timed out waiting for guest responses! Game start cancelled."; +// Message appears for guests when automatic cancellation occurs. +const char TXT_WOL_STARTCANCELLED[] = "Game start cancelled."; +// Text of button on game setup screen that takes user out of the game channel. +const char TXT_WOL_CANCELGAME[] = "Back"; + +const char TXT_WOL_PATCHQUESTION[] = "An update patch is required for Internet play. Do you want to download it now?"; +// Title of patch download dialog. First %i = current file being downloaded, second %i = total # of files to download. +const char TXT_WOL_DOWNLOADING[] = "Download file %i of %i"; + +const char TXT_WOL_DOWNLOADERROR[] = "An error occurred during file download."; +// Appears on patch download dialog. First %i = current # of bytes downloaded, second %i = total # of bytes to download. +const char TXT_WOL_DOWNLOADBYTES[] = "Received %i bytes out of %i. (%i%%%%)"; +// Appears on patch download dialog. First %i = number of minutes left, second %i = number of seconds left. +const char TXT_WOL_DOWNLOADTIME[] = "Time Remaining: %i min. %i secs."; +// Appended to title of patch download dialog when resuming an interrupted download. %s is the regular title, as above. +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Resumed after interruption.)"; + +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Connecting..."; + +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Locating file..."; + +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Downloading..."; + +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Download complete! Red Alert will now restart in order to apply the update patch."; + +const char TXT_WOL_HELPSHELL[] = "Are you sure you want to launch the Internet browser for Westwood Online help?"; + +const char TXT_WOL_LADDERSHELL[] = "Are you sure you want to launch the Internet browser for the Red Alert ladders?"; + +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "No saved usernames found. Would you like to register a new username for Westwood Online?"; + +const char TXT_WOL_GAMEADVERTSHELL[] = "Are you sure you want to launch the Internet browser for information about %s?"; +// Appears above user list. %i = number of users in the current channel. +const char TXT_WOL_USERLIST[] = "Users %i"; +// Appears above user list to explain why no users are being listed: because the user is not currently in a chat channel. +const char TXT_WOL_NOUSERLIST[] = "(not in a channel)"; + +const char TXT_WOL_CANTCREATEHERE[] = "To start a game, you have to be in a Red Alert lobby."; +// Appears inside game, when connection to WW Online is lost. +const char TXT_WOL_WOLAPIGONE[] = "Connection to Westwood Online has been lost!"; +// Appears after game, when attempting to get back into WW Online. +const char TXT_WOL_WOLAPIREINIT[] = "Connection to Westwood Online was lost. Reinitializing..."; + +const char TXT_WOL_NOTPAGED[] = "Can't respond to page; no one has paged you."; +// Appears briefly in the space for scenario name, in game setup dialog. +const char TXT_WOL_SCENARIONAMEWAIT[] = "waiting for scenario..."; +// Text of button on chat screen that takes user out of a chat channel, or up one WW Online level. +const char TXT_WOL_BACK[] = "Back"; + +const char TXT_WOL_AMDISCNEEDED[] = "The Aftermath disk will be required for this game; please insert it now."; + +const char TXT_WOL_CONFIRMLOGOUT[] = "Are you sure you want to leave Westwood Online?"; +// "Propose a stalemate" button. +const char TXT_WOL_PROPOSE_DRAW[] = "Propose a Draw"; +// Withdraw proposed stalemate button. +const char TXT_WOL_RETRACT_DRAW[] = "Retract Draw Proposal"; +// Accept offered stalemate button. +const char TXT_WOL_ACCEPT_DRAW[] = "Accept Proposed Draw"; +// User proposes that the game be declared a stalemate. +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Are you sure you want to propose a draw?"; +// User accepts the other's offer that the game be a tie. +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Are you sure you want to accept a draw?"; + +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "You have proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s has proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "You have retracted your offer of a draw."; + +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s has retracted the offer of a draw."; +// Message that appears in place of "Mission Accomplished" or "Mission Failed", when game is a draw. +const char TXT_WOL_DRAW[] = "The Game is a Draw"; +// Error message that appears when user's web browser can't be automatically launched. %s is a web site URL. +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Can't launch web browser to open %s!"; + +const char TXT_WOL_CHANNELFULL[] = "That chat/game channel is full."; + +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doubleclick to go to the top level. "; + +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doubleclick to go to the official chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doubleclick to go to the user chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doubleclick to go to the game channels level. "; + +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Loading list from Westwood Online, please wait... "; + +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doubleclick to go to the lobbies level. "; + +const char TXT_WOL_FINDINGLOBBY[] = "Connected - finding available lobby to enter..."; + +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; + +const char TXT_WOL_PRIVATETO[] = "Private to"; + +const char TXT_WOL_CS_MISSIONS[] = "Counterstrike Missions"; + +const char TXT_WOL_AM_MISSIONS[] = "Aftermath Missions"; + +const char TXT_WOL_CANTSQUELCHSELF[] = "You cannot disable viewing of your own messages!"; +// Title of the WW Online options dialog. +const char TXT_WOL_OPTTITLE[] = "Westwood Online Options"; + +const char TXT_WOL_SLOWUNITBUILD[] = "Slow Unit Build"; + +const char TXT_WOL_THEGAMEHOST[] = "The game host"; + +const char TXT_WOL_TTIP_RANKRA[] = " Show Red Alert ladder rankings "; + +const char TXT_WOL_TTIP_RANKAM[] = " Show Aftermath ladder rankings "; + +const char TXT_WOL_OPTRANKAM[] = "Show Aftermath rankings (instead of Red Alert)"; + +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (AND FORFEIT THE GAME)"; + +const char TXT_WOL_DLLERROR_GETIE3[] = "Your version of Windows is out of date. Please upgrade to Windows SP1, or install Internet Explorer 3.0 or higher."; + +const char TXT_WOL_DLLERROR_CALLUS[] = "An unexpected error has occurred. Please contact Westwood technical support."; + +const char TXT_WOL_PRIVATE[] = ""; + +#else + +#ifdef GERMAN +#pragma message( "...Building German version..." ) + +// Replaced "á" with ascii 169 (in octal, 251) in cases where 8 point font is used (see 8point.lbm for why). +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Unerwarteter Fehler trat bei der Kommunikation mit Westwood Online auf."; +const char TXT_WOL_CONNECT[] = "Verbinden"; +const char TXT_WOL_LOGINDIALOG[] = "Westwood-Online-Login"; +const char TXT_WOL_NAME[] = "Spitzname"; +const char TXT_WOL_PASSWORD[] = "Pa\251wort"; +const char TXT_WOL_SAVELOGIN[] = "Speichern"; +const char TXT_WOL_LOGINCANCEL[] = "Login abgebrochen."; +const char TXT_WOL_MISSINGNAME[] = "Bitte geben Sie Ihren Login-Spitznamen ein."; +const char TXT_WOL_MISSINGPASSWORD[] = "Bitte geben Sie Ihr Login-Pa\251wort ein."; +const char TXT_WOL_CANTSAVENICK[] = "Fehler beim Speichern des Spitznamens/Pa\251worts"; +const char TXT_WOL_NICKINUSE[] = "Dieser Spitzname wird bereits verwendet. Bitte w„hlen Sie einen anderen."; +const char TXT_WOL_BADPASS[] = "Ungltiges Pa\251wort fr diesen Spitznamen"; +const char TXT_WOL_TIMEOUT[] = "Verbindung zu Westwood Online unterbrochen"; +const char TXT_WOL_CONNECTING[] = "Verbinde zu Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Verbindung zu Westwood Online konnte nicht hergestellt werden."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Einloggen ... "; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Ausloggen ..."; +const char TXT_WOL_ERRORLOGOUT[] = "Verbindung zu Westwood Online beenden ..."; +const char TXT_WOL_WAIT[] = "Bitte warten ... Verbindung zu Westwood Online wird hergestellt ..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Offizieller Chat"; +const char TXT_WOL_USERCHAT[] = "User-Chat"; +const char TXT_WOL_GAMECHANNELS[] = "Game-Channels"; +const char TXT_WOL_REDALERTLOBBIES[] = "Alarmstufe-Rot-Lobbies"; +const char TXT_WOL_CHANNELLISTLOADING[] = "... Daten werden heruntergeladen ..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Sie befinden sich zur Zeit nicht in einem Chat-Channel."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Teilnehmen"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Sie k”nnen keinen neuen Channel erstellen, bevor Sie diesen Channel verlassen."; +const char TXT_WOL_NEWSOMETHING[] = "Neu"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Channel erstellen"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel-Name: "; +const char TXT_WOL_PASSPROMPT[] = "Pa\251wort: "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Pa\251wort (optional): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Sie nehmen am %s-Channel teil."; +const char TXT_WOL_YOUJOINEDGAME[] = "Sie nehmen an %ss Spiel teil."; +const char TXT_WOL_YOUCREATEDGAME[] = "Neues Spiel erstellt."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Sie haben die %s-Lobby betreten."; +const char TXT_WOL_YOULEFT[] = "Sie haben den %s-Channel verlassen."; +const char TXT_WOL_YOULEFTLOBBY[] = "Sie haben die %s-Lobby verlassen."; +const char TXT_WOL_JOINPRIVATETITLE[] = "An privatem Channel teilnehmen"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Channel-Pa\251wort eingeben: "; +const char TXT_WOL_BADCHANKEY[] = "Falsches Channel-Pa\251wort."; +const char TXT_WOL_PAGELOCATE[] = "Senden/Suchen"; +const char TXT_WOL_USERNAMEPROMPT[] = "User-Name: "; +const char TXT_WOL_PAGE[] = "Senden"; +const char TXT_WOL_LOCATE[] = "Suchen"; +const char TXT_WOL_LOCATING[] = "Suche %s..."; +const char TXT_WOL_FIND_NOTHERE[] = "Der gesuchte User-Name existiert nicht."; +const char TXT_WOL_FIND_NOCHAN[] = "Der genannte User befindet sich zur Zeit nicht in einem Channel."; +const char TXT_WOL_FIND_OFF[] = "Der genannte User hat die Suchfunktion ausgeschaltet."; +const char TXT_WOL_FOUNDIN[] = "User wurde im %s-Channel gefunden."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Sender"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Zu sendende Nachricht: "; +const char TXT_WOL_PAGING[] = "Sende an %s ..."; +const char TXT_WOL_PAGE_NOTHERE[] = "Der genannte User ist nicht eingeloggt."; +const char TXT_WOL_PAGE_OFF[] = "Der genannte User hat die Empfangsfunktion ausgeschaltet."; +const char TXT_WOL_ONPAGE[] = "Sende von %s: %s"; +const char TXT_WOL_WASPAGED[] = "Die Nachricht wurde %s erfolgreich zugestellt."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s ist zur Zeit nicht erreichbar."; +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s ist jetzt wieder erreichbar."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Nur der Channel-Besitzer kann andere User hinauswerfen."; +const char TXT_WOL_USERKICKEDUSER[] = "%s hat %s aus dem Channel geworfen."; +const char TXT_WOL_USERKICKEDYOU[] = "Sie wurden von %s aus dem Channel geworfen."; +const char TXT_WOL_NOONETOKICK[] = "W„hlen Sie den/die User, die Sie hinauswerfen m”chten."; +const char TXT_WOL_USERWASBANNED[] = "%s hat keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Privates Spiel erstellen"; +const char TXT_WOL_YOUREBANNED[] = "Sie haben keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s hat das Spiel verlassen."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s hat an dem Spiel teilgenommen."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Sie wurden aus dem Spiel geworfen."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alarmstufe Rot: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Vergeltungsschlag: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_USERRANK[] = "%s (Platz %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Platz %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "Start"; +const char TXT_WOL_ACCEPTBUTTON[] = "Best„tigen"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s hat das Spiel abgebrochen."; +const char TXT_WOL_WAITINGTOSTART[] = "Spiel wird gestartet ..."; +const char TXT_WOL_TTIP_DISCON[] = " Westwood Online verlassen"; +const char TXT_WOL_TTIP_LEAVE[] = " Derzeitigen Channel verlassen "; +const char TXT_WOL_TTIP_REFRESH[] = " Channel-Liste aktualisieren "; +const char TXT_WOL_TTIP_SQUELCH[] = " Nachrichteneingang von User(n) ein/ausschalten"; +const char TXT_WOL_TTIP_BAN[] = " User(n) Zutritt zum Channel verwehren "; +const char TXT_WOL_TTIP_KICK[] = " User aus dem Channel werfen "; +const char TXT_WOL_TTIP_FINDPAGE[] = " User suchen oder an User senden "; +const char TXT_WOL_TTIP_OPTIONS[] = " Westwood-Online-Optionen einstellen "; +const char TXT_WOL_TTIP_LADDER[] = " Alarmstufe-Rot-Tabelle durchsuchen "; +const char TXT_WOL_TTIP_HELP[] = " Westwood-Online-Hilfe anzeigen "; +const char TXT_WOL_TTIP_START[] = " Spiel starten "; +const char TXT_WOL_TTIP_ACCEPT[] = " Aktuelle Spieleinstellungen best„tigen "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Listen erweitern/verkleinern "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_JOIN[] = " An Chat- oder Game-Channel teilnehmen "; +const char TXT_WOL_TTIP_BACK[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_CREATE[] = " Neuen Chat- oder Game-Level erstellen "; +const char TXT_WOL_TTIP_ACTION[] = " Action-Nachricht "; +const char TXT_WOL_OPTFIND[] = "Lassen Sie zu, da\251 andere Sie FINDEN."; +const char TXT_WOL_OPTPAGE[] = "Lassen Sie zu, da\251 andere Ihnen Nachrichten SENDEN."; +const char TXT_WOL_OPTLANGUAGE[] = "Unangemessene Sprache herausfiltern."; +const char TXT_WOL_OPTGAMESCOPE[] = "Nur lokale Spiel-Lobby anzeigen."; +//const char TXT_WOL_OPTTITLE[] = "Optionen"; +const char TXT_WOL_CHANNELGONE[] = "Channel existiert nicht mehr."; +const char TXT_WOL_CG_TITLE[] = "Spiel erstellen"; +const char TXT_WOL_CG_PLAYERS[] = "Spieler: %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Turnier"; +const char TXT_WOL_CG_PRIVACY[] = "Privat"; +const char TXT_WOL_CG_RAGAME[] = "Alarmstufe-Rot-Spiel"; +const char TXT_WOL_CG_CSGAME[] = "Gegenangriff-Spiel"; +const char TXT_WOL_CG_AMGAME[] = "Vergeltungsschlag-Spiel"; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sie mssen 'Gegenangriff' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_NEEDAFTERMATH[] = "Sie mssen 'Vergeltungsschlag' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doppelklick zur Teilnahme am '%s'-Channel (z.Z. %i User). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doppelklick zur Teilnahme an der '%s'-Lobby (z.Z. %i User). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alarmstufe Rot"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Gegenangriff"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Vergeltungsschlag"; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s-Spiel (%i Spieler von maximal %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s-Spiel (%i Spieler). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Privat) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Turnier) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doppelklicken Sie, um %s-Spiele anzusehen. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Turnierspiele mssen von zwei Spieler gespielt werden."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Pa\251wort: %s"; +const char TXT_WOL_RULESMISMATCH[] = "Ihr Spiel ist mit dem des Host nicht kompatibel!"; +const char TXT_WOL_STARTTIMEOUT[] = "Keine Antworten von G„sten! Spielstart abgebrochen."; +const char TXT_WOL_STARTCANCELLED[] = "Spielstart abgebrochen."; +const char TXT_WOL_CANCELGAME[] = "Zurck"; +const char TXT_WOL_PATCHQUESTION[] = "Ein Update-Patch wird fr Internet-Spiele ben”tigt. M”chten Sie es jetzt herunterladen?"; +const char TXT_WOL_DOWNLOADING[] = "Datei %i von %i herunterladen"; +const char TXT_WOL_DOWNLOADERROR[] = "Ein Fehler trat beim Herunterladen der Dateien auf."; +const char TXT_WOL_DOWNLOADBYTES[] = "%i Bytes von %i erhalten. (%i%%%%)"; +const char TXT_WOL_DOWNLOADTIME[] = "Verbleibende Zeit: %i Min. %i Sek."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Nach Unterbrechung wiederaufgenommen.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Verbinde ..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Suche Datei ..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Lade herunter ..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Herunterladen abgeschlossen! Alarmstufe Rot wird jetzt neugestartet, damit das Update-Patch angewendet werden kann."; +const char TXT_WOL_HELPSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Westwood-Online-Hilfe starten m”chten?"; +const char TXT_WOL_LADDERSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Alarmstufe-Rot-Tabellen starten m”chten?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Keine gespeicherten User-Namen gefunden. M”chten Sie einen neuen User-Namen fr Westwood Online eintragen?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr Informationen ber %s starten m”chten?"; +const char TXT_WOL_USERLIST[] = "User: %i"; +const char TXT_WOL_NOUSERLIST[] = "(nicht in einem Channel)"; +const char TXT_WOL_CANTCREATEHERE[] = "Um ein Spiel zu starten, mssen Sie in der Lobby Alarmstufe Rot sein."; +const char TXT_WOL_WOLAPIGONE[] = "Verbindung zu Westwood Online verloren!"; +const char TXT_WOL_WOLAPIREINIT[] = "Verbindung zu Westwood Online verloren. Verbinde erneut ..."; +const char TXT_WOL_NOTPAGED[] = "Kann keine Antwort senden, niemand hat Ihnen geschrieben."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "Warte auf Szenario ..."; +const char TXT_WOL_BACK[] = "Zurck"; +const char TXT_WOL_AMDISCNEEDED[] = "Die CD 'Vergeltungsschlag' wird fr dieses Spiel ben”tigt, bitte legen Sie sie jetzt ein."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Sind Sie sicher, da\251 Sie Westwood Online verlassen m”chten?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Unentschieden vorschlagen"; +const char TXT_WOL_RETRACT_DRAW[] = "Unentschieden-Vorschlag zurckziehen"; +const char TXT_WOL_ACCEPT_DRAW[] = "Unentschieden-Vorschlag akzeptieren"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden vorschlagen m”chten?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden akzeptieren m”chten?"; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Sie haben vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s hat vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Sie haben Ihr Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s hat das Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW[] = "Das Spiel ist unentschieden"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Web-Browser kann %s nicht ”ffnen!"; +const char TXT_WOL_CHANNELFULL[] = "Dieser Chat-/Game-Channel ist voll."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doppelklicken Sie, um zum obersten Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doppelklicken Sie, um zum offiziellen Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doppelklicken Sie, um zum User-Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doppelklicken Sie, um zum Game-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Liste von Westwood Online wird geladen, bitte warten... "; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doppelklicken Sie, um zum Lobby-Level zu gelangen. "; +const char TXT_WOL_FINDINGLOBBY[] = "Verbunden - suche verfgbare Lobby..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; +const char TXT_WOL_PRIVATETO[] = "Privat an"; +const char TXT_WOL_CS_MISSIONS[] = "Gegenangriff-Missionen"; +const char TXT_WOL_AM_MISSIONS[] = "Vergeltungsschlag-Missionen"; +const char TXT_WOL_CANTSQUELCHSELF[] = "Sie k”nnen die Option zum Lesen Ihrer eigenen Nachrichten nicht ausschalten!"; +const char TXT_WOL_OPTTITLE[] = "Westwood Online-Optionen"; +const char TXT_WOL_SLOWUNITBUILD[] = "Einheitenbau verlangsamen"; +const char TXT_WOL_THEGAMEHOST[] = "Der Spiel-Host"; +const char TXT_WOL_TTIP_RANKRA[] = " Alarmstufe-Rot-Platz anzeigen "; +const char TXT_WOL_TTIP_RANKAM[] = " Vergeltungsschlag-Platz anzeigen "; +const char TXT_WOL_OPTRANKAM[] = "Vergeltungsschlag-Platz anzeigen."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (UND BSSEN SIE DAS SPIEL EIN)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Ihre Version der Windows ist veraltet. Bauen Sie bitte zu den Windows SP1, aus oder installieren Sie Internet Explorer 3,0 oder h”heres."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Ein unerwarteter Fehler ist aufgetreten. Bitte wenden Sie sich an den Technischen Kundendienst."; +const char TXT_WOL_PRIVATE[] = ""; + +#else // FRENCH + +#pragma message( "...Building French version..." ) + +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Erreur inattendue lors de la connexion … Westwood Online."; +const char TXT_WOL_CONNECT[] = "Se connecter"; +const char TXT_WOL_LOGINDIALOG[] = "Identifiant … Westwood Online"; +const char TXT_WOL_NAME[] = "Pseudo"; +const char TXT_WOL_PASSWORD[] = "Mot de passe"; +const char TXT_WOL_SAVELOGIN[] = "Sauvegarder"; +const char TXT_WOL_LOGINCANCEL[] = "Ouverture de session annul‚e."; +const char TXT_WOL_MISSINGNAME[] = "Veuillez entrer l'identifiant pour votre pseudo."; +const char TXT_WOL_MISSINGPASSWORD[] = "Veuillez entrer l'identifiant pour votre mot de passe."; +const char TXT_WOL_CANTSAVENICK[] = "Erreur lors de la sauvegarde du pseudo/mot de passe."; +const char TXT_WOL_NICKINUSE[] = "Ce pseudo est d‚j… utilis‚. S‚lectionnez-en un autre."; +const char TXT_WOL_BADPASS[] = "Mot de passe invalide pour ce pseudo."; +const char TXT_WOL_TIMEOUT[] = "Expiration du temps de connexion … Westwood Online."; +const char TXT_WOL_CONNECTING[] = "Connexion … Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Impossible d'‚tablir la connexion … Westwood Online."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Ouverture de la session en cours..."; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Fermeture de la session en cours..."; +const char TXT_WOL_ERRORLOGOUT[] = "Fin de connexion avec Westwood Online..."; +const char TXT_WOL_WAIT[] = "Attendez svp, en communication … Westwood Online..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Conversation officielle"; +const char TXT_WOL_USERCHAT[] = "Conversation utilisateur"; +const char TXT_WOL_GAMECHANNELS[] = "Canaux de jeu"; +const char TXT_WOL_REDALERTLOBBIES[] = "Salons d'Alerte Rouge"; +const char TXT_WOL_CHANNELLISTLOADING[] = "...En cours de t‚l‚chargement..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Vous n'ˆtes pas dans un canal de conversation."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Rejoindre"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Cr‚ation d'un nouveau canal impossible tant que vous ne quittez pas ce canal."; +const char TXT_WOL_NEWSOMETHING[] = "Nouveau"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Cr‚er un canal"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Nom du canal : "; +const char TXT_WOL_PASSPROMPT[] = "Mot de passe : "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Mot de passe (en option): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Vous avez rejoint le canal %s."; +const char TXT_WOL_YOUJOINEDGAME[] = "Vous rejoignez la partie de %s."; +const char TXT_WOL_YOUCREATEDGAME[] = "Cr‚ation d'une nouvelle partie."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Vous ˆtes entr‚ dans le salon %s."; +const char TXT_WOL_YOULEFT[] = "Vous avez quitt‚ le canal %s."; +const char TXT_WOL_YOULEFTLOBBY[] = "Vous avez quitt‚ le salon %s."; +const char TXT_WOL_JOINPRIVATETITLE[] = "Rejoindre un canal priv‚"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Entrer le mot de passe du canal : "; +const char TXT_WOL_BADCHANKEY[] = "Mot de passe du canal incorrect."; +const char TXT_WOL_PAGELOCATE[] = "Envoyer/Rechercher"; +const char TXT_WOL_USERNAMEPROMPT[] = "Nom de l'utilisateur : "; +const char TXT_WOL_PAGE[] = "Envoyer"; +const char TXT_WOL_LOCATE[] = "Rechercher"; +const char TXT_WOL_LOCATING[] = "Recherche de %s en cours ..."; +const char TXT_WOL_FIND_NOTHERE[] = "Le nom de l'utilisateur sp‚cifi‚ n'existe pas."; +const char TXT_WOL_FIND_NOCHAN[] = "L'utilisateur sp‚cifi‚ n'est pas sur le canal pour le moment."; +const char TXT_WOL_FIND_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction de recherche."; +const char TXT_WOL_FOUNDIN[] = "Utilisateur trouv‚ dans le canal %s."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Envoyer … l'utilisateur"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message … envoyer : "; +const char TXT_WOL_PAGING[] = "Envoi … %s en cours..."; +const char TXT_WOL_PAGE_NOTHERE[] = "L'utilisateur sp‚cifi‚ n'a pas ouvert la session."; +const char TXT_WOL_PAGE_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction d'envoi de messages."; +const char TXT_WOL_ONPAGE[] = "Envoi de %s : %s"; +const char TXT_WOL_WASPAGED[] = "Envoi … %s r‚ussi."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s a ‚t‚ rejet‚."; // ajw rejete really means squelched? +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s n'est plus rejet‚."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Seul le responsable du canal peut expulser des utilisateurs."; +const char TXT_WOL_USERKICKEDUSER[] = "%s expulse %s du canal."; +const char TXT_WOL_USERKICKEDYOU[] = "Vous ˆtes expuls‚ du canal par %s."; +const char TXT_WOL_NOONETOKICK[] = "S‚lectionnez l'(les) utilisateur(s) que vous voulez expulser."; +const char TXT_WOL_USERWASBANNED[] = "%s est exclu du canal."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Cr‚er une partie priv‚e"; +const char TXT_WOL_YOUREBANNED[] = "Vous n'ˆtes pas autoris‚ … entrer dans ce canal."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s a quitt‚ la partie."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s a rejoint la partie."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Vous avez ‚t‚ expuls‚ de la partie."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alerte Rouge: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Missions M.A.D.: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_USERRANK[] = "%s (Position %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Position %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "D‚marrer"; +const char TXT_WOL_ACCEPTBUTTON[] = "Accepter"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s a annul‚ la partie."; +const char TXT_WOL_WAITINGTOSTART[] = "Lancement de la partie..."; +const char TXT_WOL_TTIP_DISCON[] = " Quitter Westwood Online "; +const char TXT_WOL_TTIP_LEAVE[] = " Quitter le canal o— vous vous trouvez "; +const char TXT_WOL_TTIP_REFRESH[] = " RafraŒchir la liste du canal "; +const char TXT_WOL_TTIP_SQUELCH[] = " Activer/d‚sactiver les messages en provenance de(s) l'utilisateur(s) "; +const char TXT_WOL_TTIP_BAN[] = " Exclure l'/les utilisateur(s)du canal "; +const char TXT_WOL_TTIP_KICK[] = " Expulser l'/les utilisateurs du canal "; +const char TXT_WOL_TTIP_FINDPAGE[] = " Rechercher ou envoyer un message … un utilisateur "; +const char TXT_WOL_TTIP_OPTIONS[] = " R‚gler les options de Westwood Online "; +const char TXT_WOL_TTIP_LADDER[] = " Parcourir les hi‚rarchies d'Alerte Rouge "; +const char TXT_WOL_TTIP_HELP[] = " Afficher l'aide de Westwood Online "; +const char TXT_WOL_TTIP_START[] = " D‚marrer le jeu "; +const char TXT_WOL_TTIP_ACCEPT[] = " Valider les paramŠtres actuels du jeu "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Compl‚ter/r‚duire la liste "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_JOIN[] = " Rejoindre un canal de conversation/jeu "; +const char TXT_WOL_TTIP_BACK[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_CREATE[] = " Cr‚er un nouveau canal de conversation/jeu "; +const char TXT_WOL_TTIP_ACTION[] = " Message d'action "; +const char TXT_WOL_OPTFIND[] = "Laisser les autres vous RECHERCHER."; +const char TXT_WOL_OPTPAGE[] = "Laisser les autres vous ENVOYER des messages."; +const char TXT_WOL_OPTLANGUAGE[] = "Filtrer les vulgarit‚s."; +const char TXT_WOL_OPTGAMESCOPE[] = "Afficher seulement les parties en salons locaux."; +//const char TXT_WOL_OPTTITLE[] = "Options"; +const char TXT_WOL_CHANNELGONE[] = "Ce canal n'existe plus."; +const char TXT_WOL_CG_TITLE[] = "Cr‚er une partie"; +const char TXT_WOL_CG_PLAYERS[] = "Joueurs : %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Tournoi"; +const char TXT_WOL_CG_PRIVACY[] = "Priv‚e"; +const char TXT_WOL_CG_RAGAME[] = "Partie Alerte Rouge"; +const char TXT_WOL_CG_CSGAME[] = "Partie Missions Ta‹ga"; +const char TXT_WOL_CG_AMGAME[] = "Partie Missions M.A.D."; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "D‚sol‚, vous devez installer Missions Ta‹ga pour jouer cette partie."; +const char TXT_WOL_NEEDAFTERMATH[] = "D‚sol‚, vous devez installer Missions M.A.D. pour jouer cette partie."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Double-clic pour rejoindre canal %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Double-clic pour rejoindre salon %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alerte Rouge"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Missions Ta‹ga"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Missions M.A.D."; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " Partie de %s (%i joueurs pour un max. de %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " Partie de %s (%i joueurs). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Priv‚e) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournoi) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Double-clic pour afficher les parties %s. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Les parties en tournoi doivent rassembler deux joueurs."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Mot de passe : %s"; +const char TXT_WOL_RULESMISMATCH[] = "Votre partie n'est pas compatible avec celle du serveur !"; +const char TXT_WOL_STARTTIMEOUT[] = "Expiration du temps de r‚ponse des clients ! D‚marrage du jeu annul‚."; +const char TXT_WOL_STARTCANCELLED[] = "D‚marrage du jeu annul‚."; +const char TXT_WOL_CANCELGAME[] = "Retour"; +const char TXT_WOL_PATCHQUESTION[] = "Un patch mis … jour est n‚cessaire pour le jeu sur Internet. Voulez-vous le t‚l‚charger maintenant ?"; +const char TXT_WOL_DOWNLOADING[] = "T‚l‚charger %i fichier(s) sur %i."; +const char TXT_WOL_DOWNLOADERROR[] = "Erreur lors du t‚l‚chargement du fichier."; +const char TXT_WOL_DOWNLOADBYTES[] = "R‚ception de %i octets sur %i. (%i%%%%)."; +const char TXT_WOL_DOWNLOADTIME[] = "Temps restant : %i min. %i secs."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (reprise aprŠs interruption.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Etat : en cours de connexion..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Etat : recherche du fichier..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Etat : en cours de t‚l‚chargement..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "T‚l‚chargement termin‚ ! Alerte Rouge est relanc‚ pour que le nouveau patch soit pris en compte."; +const char TXT_WOL_HELPSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir l'aide Westwood Online ?"; +const char TXT_WOL_LADDERSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour les hi‚rarchies d'Alerte Rouge ?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Aucun nom d'utilisateur sauvegard‚. Voulez-vous enregistrer un nouveau nom d'utilisateur pour Westwood Online ?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir des informations sur %s ?"; +const char TXT_WOL_USERLIST[] = "Utilisateurs %i"; +const char TXT_WOL_NOUSERLIST[] = "(absent du canal)"; +const char TXT_WOL_CANTCREATEHERE[] = "Pour commencer une partie, vous devez ˆtre dans un salon d'Alerte Rouge."; +const char TXT_WOL_WOLAPIGONE[] = "Perte de connexion avec Westwood Online !"; +const char TXT_WOL_WOLAPIREINIT[] = "Perte de connexion avec Westwood Online. R‚initialisation en cours..."; +const char TXT_WOL_NOTPAGED[] = "Impossible de r‚pondre au message ; personne ne vous en a envoy‚."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "En attente du sc‚nario..."; +const char TXT_WOL_BACK[] = "Retour"; +const char TXT_WOL_AMDISCNEEDED[] = "Le CD de Missions M.A.D. est n‚cessaire pour cette partie ; ins‚rez-le maintenant."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Voulez-vous vraiment quitter Westwood Online ?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Proposer une fin avec ‚galit‚"; +const char TXT_WOL_RETRACT_DRAW[] = "Annuler la proposition de fin avec ‚galit‚"; +const char TXT_WOL_ACCEPT_DRAW[] = "Accepter la proposition de fin avec ‚galit‚"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Voulez-vous vraiment proposer une fin avec ‚galit‚ ?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Voulez-vous vraiment accepter une fin avec ‚galit‚ ?" ; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Vous proposez de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s a propos‚ de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Vous avez annul‚ votre proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s a annul‚ sa proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW[] = "Match nul"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Impossible de lancer le navigateur web pour ouvrir %s !"; +const char TXT_WOL_CHANNELFULL[] = "Ce canal de jeu/conversation est satur‚."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Double-clic pour retourner au premier niveau. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Double-clic pour les canaux de conversation officiels. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Double-clic pour les canaux d' utilisateur. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Double-clic pour acc‚der au niveau des canaux de jeu. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Chargement de la liste depuis Westwood Online, veuillez patienter..."; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Double-clic pour acc‚der au niveau des salons. "; +const char TXT_WOL_FINDINGLOBBY[] = "Connection : recherche de salons disponibles..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = " :"; +const char TXT_WOL_PRIVATETO[] = "Message personnel …"; +const char TXT_WOL_CS_MISSIONS[] = "Missions extraites de Missions Ta‹ga"; +const char TXT_WOL_AM_MISSIONS[] = "Missions extraites de Missions M.A.D."; +const char TXT_WOL_CANTSQUELCHSELF[] = "Vous ne pouvez pas d‚sactiver vos propres messages!"; +const char TXT_WOL_OPTTITLE[] = "Options de Westwood Online"; +const char TXT_WOL_SLOWUNITBUILD[] = "Ralentir la Construction"; +const char TXT_WOL_THEGAMEHOST[] = "Le serveur"; +const char TXT_WOL_TTIP_RANKRA[] = " Afficher les positions d'Alerte Rouge "; +const char TXT_WOL_TTIP_RANKAM[] = " Afficher les positions de Missions M.A.D. "; +const char TXT_WOL_OPTRANKAM[] = "Afficher les positions de Missions M.A.D."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (ET RENONCER AU JEU)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Votre version des Windows est d‚mod‚e. Am‚liorez s'il vous plait aux Windows SP1, ou installez l'Internet Explorer 3,0 ou plus haut."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Une erreur inattendue s'est produite. Veuillez contacter l'assistance technique de Electronic Arts."; +const char TXT_WOL_PRIVATE[] = ""; + +#endif + +#endif + + +//#endif diff --git a/REDALERT/WOLSTRNG.H b/REDALERT/WOLSTRNG.H new file mode 100644 index 000000000..286899a1d --- /dev/null +++ b/REDALERT/WOLSTRNG.H @@ -0,0 +1,209 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// Wolstrng.h - this will presumably go away eventually. + +// New character strings. + +extern const char TXT_WOL_INTERNETBUTTON[]; +extern const char TXT_WOL_ERRORMESSAGE[]; +extern const char TXT_WOL_CONNECT[]; +extern const char TXT_WOL_LOGINDIALOG[]; +extern const char TXT_WOL_NAME[]; +extern const char TXT_WOL_PASSWORD[]; +extern const char TXT_WOL_SAVELOGIN[]; +extern const char TXT_WOL_LOGINCANCEL[]; +extern const char TXT_WOL_MISSINGNAME[]; +extern const char TXT_WOL_MISSINGPASSWORD[]; +extern const char TXT_WOL_CANTSAVENICK[]; +extern const char TXT_WOL_NICKINUSE[]; +extern const char TXT_WOL_BADPASS[]; +extern const char TXT_WOL_TIMEOUT[]; +extern const char TXT_WOL_CONNECTING[]; +extern const char TXT_WOL_CANTCONNECT[]; +extern const char TXT_WOL_ATTEMPTLOGIN[]; +extern const char TXT_WOL_ATTEMPTLOGOUT[]; +extern const char TXT_WOL_ERRORLOGOUT[]; +extern const char TXT_WOL_WAIT[]; +extern const char TXT_WOL_OFFICIALCHAT[]; +extern const char TXT_WOL_USERCHAT[]; +extern const char TXT_WOL_YOURENOTINCHANNEL[]; +extern const char TXT_WOL_ACTION[]; +extern const char TXT_WOL_JOIN[]; +extern const char TXT_WOL_CANTCREATEINCHANNEL[]; +extern const char TXT_WOL_NEWSOMETHING[]; +extern const char TXT_WOL_CREATECHANNELTITLE[]; +extern const char TXT_WOL_CREATECHANNELPROMPT[]; +extern const char TXT_WOL_PASSPROMPT[]; +extern const char TXT_WOL_OPTIONALPASSPROMPT[]; +extern const char TXT_WOL_CHANNEL_TOP[]; +extern const char TXT_WOL_CHANNEL_BACK[]; +extern const char TXT_WOL_YOUJOINED[]; +extern const char TXT_WOL_YOUJOINEDGAME[]; +extern const char TXT_WOL_YOUCREATEDGAME[]; +extern const char TXT_WOL_YOUJOINEDLOBBY[]; +extern const char TXT_WOL_YOULEFT[]; +extern const char TXT_WOL_YOULEFTLOBBY[]; +extern const char TXT_WOL_JOINPRIVATETITLE[]; +extern const char TXT_WOL_JOINPRIVATEPROMPT[]; +extern const char TXT_WOL_BADCHANKEY[]; +extern const char TXT_WOL_PAGELOCATE[]; +extern const char TXT_WOL_USERNAMEPROMPT[]; +extern const char TXT_WOL_PAGE[]; +extern const char TXT_WOL_LOCATE[]; +extern const char TXT_WOL_LOCATING[]; +extern const char TXT_WOL_FIND_NOTHERE[]; +extern const char TXT_WOL_FIND_NOCHAN[]; +extern const char TXT_WOL_FIND_OFF[]; +extern const char TXT_WOL_FOUNDIN[]; +extern const char TXT_WOL_PAGEMESSAGETITLE[]; +extern const char TXT_WOL_PAGEMESSAGEPROMPT[]; +extern const char TXT_WOL_PAGING[]; +extern const char TXT_WOL_PAGE_NOTHERE[]; +extern const char TXT_WOL_PAGE_OFF[]; +extern const char TXT_WOL_ONPAGE[]; +extern const char TXT_WOL_WASPAGED[]; +//extern const char TXT_WOL_USERISSQUELCHED[]; +//extern const char TXT_WOL_USERISNOTSQUELCHED[]; +extern const char TXT_WOL_ONLYOWNERCANKICK[]; +extern const char TXT_WOL_USERKICKEDUSER[]; +extern const char TXT_WOL_USERKICKEDYOU[]; +extern const char TXT_WOL_NOONETOKICK[]; +extern const char TXT_WOL_USERWASBANNED[]; +extern const char TXT_WOL_GAMECHANNELS[]; +extern const char TXT_WOL_CHANNELLISTLOADING[]; +extern const char TXT_WOL_CREATEPRIVGAMETITLE[]; +extern const char TXT_WOL_YOUREBANNED[]; +extern const char TXT_WOL_PLAYERLEFTGAME[]; +extern const char TXT_WOL_PLAYERJOINEDGAME[]; +extern const char TXT_WOL_YOUWEREKICKEDFROMGAME[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORD[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORDAM[]; +extern const char TXT_WOL_USERRANK[]; +extern const char TXT_WOL_USERHOUSE[]; +extern const char TXT_WOL_USERRANKHOUSE[]; +extern const char TXT_WOL_STARTBUTTON[]; +extern const char TXT_WOL_ACCEPTBUTTON[]; +extern const char TXT_WOL_HOSTLEFTGAME[]; +extern const char TXT_WOL_WAITINGTOSTART[]; +extern const char TXT_WOL_TTIP_DISCON[]; +extern const char TXT_WOL_TTIP_LEAVE[]; +extern const char TXT_WOL_TTIP_REFRESH[]; +extern const char TXT_WOL_TTIP_SQUELCH[]; +extern const char TXT_WOL_TTIP_BAN[]; +extern const char TXT_WOL_TTIP_KICK[]; +extern const char TXT_WOL_TTIP_FINDPAGE[]; +extern const char TXT_WOL_TTIP_OPTIONS[]; +extern const char TXT_WOL_TTIP_LADDER[]; +extern const char TXT_WOL_TTIP_HELP[]; +extern const char TXT_WOL_TTIP_START[]; +extern const char TXT_WOL_TTIP_ACCEPT[]; +extern const char TXT_WOL_TTIP_EXPANDLIST[]; +extern const char TXT_WOL_TTIP_CANCELGAME[]; +extern const char TXT_WOL_TTIP_JOIN[]; +extern const char TXT_WOL_TTIP_BACK[]; +extern const char TXT_WOL_TTIP_CREATE[]; +extern const char TXT_WOL_TTIP_ACTION[]; +extern const char TXT_WOL_OPTFIND[]; +extern const char TXT_WOL_OPTPAGE[]; +extern const char TXT_WOL_OPTLANGUAGE[]; +extern const char TXT_WOL_OPTGAMESCOPE[]; +extern const char TXT_WOL_OPTTITLE[]; +extern const char TXT_WOL_REDALERTLOBBIES[]; +extern const char TXT_WOL_TOPLEVELTITLE[]; +extern const char TXT_WOL_CHANNELGONE[]; +extern const char TXT_WOL_CG_TITLE[]; +extern const char TXT_WOL_CG_PLAYERS[]; +extern const char TXT_WOL_CG_TOURNAMENT[]; +extern const char TXT_WOL_CG_PRIVACY[]; +extern const char TXT_WOL_CG_RAGAME[]; +extern const char TXT_WOL_CG_CSGAME[]; +extern const char TXT_WOL_CG_AMGAME[]; +extern const char TXT_WOL_NEEDCOUNTERSTRIKE[]; +extern const char TXT_WOL_NEEDAFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_CHAT[]; +extern const char TXT_WOL_TTIP_CHANLIST_LOBBY[]; +extern const char TXT_WOL_TTIP_REDALERT[]; +extern const char TXT_WOL_TTIP_COUNTERSTRIKE[]; +extern const char TXT_WOL_TTIP_AFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_RAGAME[]; +extern const char TXT_WOL_TTIP_CHANLIST_GAME[]; +extern const char TXT_WOL_TTIP_PRIVATEGAME[]; +extern const char TXT_WOL_TTIP_TOURNAMENTGAME[]; +extern const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[]; +extern const char TXT_WOL_TOURNAMENTPLAYERLIMIT[]; +extern const char TXT_WOL_PRIVATEPASSWORD[]; +extern const char TXT_WOL_RULESMISMATCH[]; +extern const char TXT_WOL_STARTTIMEOUT[]; +extern const char TXT_WOL_CANCELGAME[]; +extern const char TXT_WOL_PATCHQUESTION[]; +extern const char TXT_WOL_DOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADERROR[]; +extern const char TXT_WOL_DOWNLOADBYTES[]; +extern const char TXT_WOL_DOWNLOADTIME[]; +extern const char TXT_WOL_DOWNLOADRESUMED[]; +extern const char TXT_WOL_DOWNLOADCONNECTING[]; +extern const char TXT_WOL_DOWNLOADLOCATING[]; +extern const char TXT_WOL_DOWNLOADDOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADEXITWARNING[]; +extern const char TXT_WOL_HELPSHELL[]; +extern const char TXT_WOL_LADDERSHELL[]; +extern const char TXT_WOL_WEBREGISTRATIONSHELL[]; +extern const char TXT_WOL_GAMEADVERTSHELL[]; +extern const char TXT_WOL_USERLIST[]; +extern const char TXT_WOL_NOUSERLIST[]; +extern const char TXT_WOL_STARTCANCELLED[]; +extern const char TXT_WOL_CANTCREATEHERE[]; +extern const char TXT_WOL_WOLAPIGONE[]; +extern const char TXT_WOL_WOLAPIREINIT[]; +extern const char TXT_WOL_NOTPAGED[]; +extern const char TXT_WOL_SCENARIONAMEWAIT[]; +extern const char TXT_WOL_BACK[]; +extern const char TXT_WOL_AMDISCNEEDED[]; +extern const char TXT_WOL_CONFIRMLOGOUT[]; +extern const char TXT_WOL_PROPOSE_DRAW[]; +extern const char TXT_WOL_RETRACT_DRAW[]; +extern const char TXT_WOL_ACCEPT_DRAW[]; +extern const char TXT_WOL_PROPOSE_DRAW_CONFIRM[]; +extern const char TXT_WOL_ACCEPT_DRAW_CONFIRM[]; +extern const char TXT_WOL_DRAW_PROPOSED_LOCAL[]; +extern const char TXT_WOL_DRAW_PROPOSED_OTHER[]; +extern const char TXT_WOL_DRAW_RETRACTED_LOCAL[]; +extern const char TXT_WOL_DRAW_RETRACTED_OTHER[]; +extern const char TXT_WOL_DRAW[]; +extern const char TXT_WOL_CANTLAUNCHBROWSER[]; +extern const char TXT_WOL_CHANNELFULL[]; +extern const char TXT_WOL_CHANNELTYPE_TOP[]; +extern const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_USERCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_GAMES[]; +extern const char TXT_WOL_CHANNELTYPE_LOADING[]; +extern const char TXT_WOL_CHANNELTYPE_LOBBIES[]; +extern const char TXT_WOL_FINDINGLOBBY[]; +extern const char TXT_WOL_PRIVATETOMULTIPLE[]; +extern const char TXT_WOL_PRIVATETO[]; +extern const char TXT_WOL_CS_MISSIONS[]; +extern const char TXT_WOL_AM_MISSIONS[]; +extern const char TXT_WOL_CANTSQUELCHSELF[]; +extern const char TXT_WOL_SLOWUNITBUILD[]; +extern const char TXT_WOL_THEGAMEHOST[]; +extern const char TXT_WOL_TTIP_RANKRA[]; +extern const char TXT_WOL_TTIP_RANKAM[]; +extern const char TXT_WOL_OPTRANKAM[]; +extern const char TXT_WOL_CANCELMEANSFORFEIT[]; +extern const char TXT_WOL_DLLERROR_GETIE3[]; +extern const char TXT_WOL_DLLERROR_CALLUS[]; +extern const char TXT_WOL_PRIVATE[]; + diff --git a/REDALERT/WOL_CGAM.CPP b/REDALERT/WOL_CGAM.CPP new file mode 100644 index 000000000..72bc6e6f1 --- /dev/null +++ b/REDALERT/WOL_CGAM.CPP @@ -0,0 +1,386 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION // Implies FIXIT_CSII. + +// Wol_CGam.cpp - Create game dialog. +// ajw 09/9/98 + +#include "function.h" + +#ifndef FIXIT_CSII +#error FIXIT_CSII must be defined. +#endif + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "BigCheck.h" + +//extern char* LoadShpFile( const char* szShpFile ); +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ); + +//*********************************************************************************************** +CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ) +{ + CREATEGAMEINFO cgiReturn; + cgiReturn.bCreateGame = false; + cgiReturn.iPlayerMax = 2; + cgiReturn.bTournament = false; + cgiReturn.bPrivate = false; + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + *cgiReturn.szPassword = 0; + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 150 * RESFACTOR; // dialog width + int d_dialog_h = 135 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_gaugeplayers_w = 70 * RESFACTOR; + int d_gaugeplayers_h = 9 * RESFACTOR; + int d_gaugeplayers_x = d_dialog_cx - d_gaugeplayers_w / 2; + int d_gaugeplayers_y = d_dialog_y + d_margin + 42; + + int d_checktourn_w = 75 * RESFACTOR; + int d_checktourn_h = 9 * RESFACTOR; + int d_checktourn_x = d_dialog_cx - d_checktourn_w / 2; + int d_checktourn_y = d_gaugeplayers_y + d_gaugeplayers_h + 10; + + int d_checkpriv_w = d_checktourn_w; + int d_checkpriv_h = 9 * RESFACTOR; + int d_checkpriv_x = d_checktourn_x; + int d_checkpriv_y = d_checktourn_y + d_checktourn_h + 10; + + int d_checkra_w = d_checktourn_w; + int d_checkra_h = 9 * RESFACTOR; + int d_checkra_x = d_checktourn_x; + int d_checkra_y = d_checkpriv_y + d_checkpriv_h + 20; + + int d_checkcs_w = d_checktourn_w; + int d_checkcs_h = 9 * RESFACTOR; + int d_checkcs_x = d_checktourn_x; + int d_checkcs_y = d_checkra_y + d_checkra_h + 5; + + int d_checkam_w = d_checktourn_w; + int d_checkam_h = 9 * RESFACTOR; + int d_checkam_x = d_checktourn_x; + int d_checkam_y = d_checkcs_y + d_checkcs_h + 5; + +#if (GERMAN | FRENCH) + int d_ok_w = 30 * RESFACTOR; +#else + int d_ok_w = 30 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_x + ( d_dialog_w / 3 ) - ( d_ok_w / 2 ); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + int d_cancel_w = 40 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ( ( d_dialog_w * 2 ) / 3 ) - ( d_cancel_w / 2 ); + int d_cancel_y = d_ok_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + GAUGE_PLAYERCOUNT, + CHECK_TOURNAMENT, + CHECK_PRIVACY, + CHECK_RA, + CHECK_CS, + CHECK_AM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + StaticButtonClass PlayerCountStatic( 0, " ", TPF_TEXT, d_gaugeplayers_x, d_gaugeplayers_y - 16 ); + GaugeClass PlayerCountGauge( GAUGE_PLAYERCOUNT, d_gaugeplayers_x, d_gaugeplayers_y, d_gaugeplayers_w, d_gaugeplayers_h ); + + if( pWO->bEgg8Player ) + PlayerCountGauge.Set_Maximum( 6 ); + else + PlayerCountGauge.Set_Maximum( 2 ); + PlayerCountGauge.Set_Value( cgiReturn.iPlayerMax - 2 ); + + BigCheckBoxClass TournamentCheck( CHECK_TOURNAMENT, d_checktourn_x, d_checktourn_y, d_checktourn_w, d_checktourn_h, + TXT_WOL_CG_TOURNAMENT, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bTournament ); + + BigCheckBoxClass PrivacyCheck( CHECK_PRIVACY, d_checkpriv_x, d_checkpriv_y, d_checkpriv_w, d_checkpriv_h, + TXT_WOL_CG_PRIVACY, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bPrivate ); + + BigCheckBoxClass RA_Check( CHECK_RA, d_checkra_x, d_checkra_y, d_checkra_w, d_checkra_h, + TXT_WOL_CG_RAGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::RAGAME ); + BigCheckBoxClass CS_Check( CHECK_CS, d_checkcs_x, d_checkcs_y, d_checkcs_w, d_checkcs_h, + TXT_WOL_CG_CSGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::CSGAME ); + BigCheckBoxClass AM_Check( CHECK_AM, d_checkam_x, d_checkam_y, d_checkam_w, d_checkam_h, + TXT_WOL_CG_AMGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::AMGAME ); + + if( !Is_Counterstrike_Installed() ) + CS_Check.Disable(); + + if( !Is_Aftermath_Installed() ) + AM_Check.Disable(); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + PlayerCountStatic.Add_Tail(*commands); + PlayerCountGauge.Add_Tail(*commands); + TournamentCheck.Add_Tail(*commands); + PrivacyCheck.Add_Tail(*commands); + RA_Check.Add_Tail(*commands); + CS_Check.Add_Tail(*commands); + AM_Check.Add_Tail(*commands); + + char szPlayerCount[ 100 ]; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_CG_TITLE, d_dialog_x, d_dialog_y, d_dialog_w ); +// Fancy_Text_Print( TXT_WOL_CG_PLAYERS, d_gaugeplayers_x - 2*RESFACTOR, d_gaugeplayers_y, +// GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_RIGHT ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + /* + ** Process input. + */ + + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + cgiReturn.bCreateGame = true; + process = false; + break; + + case ( BUTTON_CANCEL | KN_BUTTON ): + process = false; + break; + + case ( GAUGE_PLAYERCOUNT | KN_BUTTON ): + if( PlayerCountGauge.Get_Value() != 0 && cgiReturn.bTournament ) + { + WWMessageBox().Process( TXT_WOL_TOURNAMENTPLAYERLIMIT ); + PlayerCountGauge.Set_Value( 0 ); + display = true; + } + cgiReturn.iPlayerMax = PlayerCountGauge.Get_Value() + 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + break; + + case ( CHECK_TOURNAMENT | KN_BUTTON ): + cgiReturn.bTournament = TournamentCheck.IsOn; + if( cgiReturn.bTournament ) + { + PlayerCountGauge.Set_Value( 0 ); +// PlayerCountGauge.Disable(); + cgiReturn.iPlayerMax = 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + } +// else +// PlayerCountGauge.Enable(); + break; + + case ( CHECK_PRIVACY | KN_BUTTON ): + cgiReturn.bPrivate = PrivacyCheck.IsOn; + break; + + case ( CHECK_RA | KN_BUTTON ): + if( RA_Check.IsOn ) + { + // Box was checked. + CS_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + } + else + // Box was unchecked. Has no effect. + RA_Check.Turn_On(); + break; + case ( CHECK_CS | KN_BUTTON ): + if( CS_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::CSGAME; + } + else + // Box was unchecked. Has no effect. + CS_Check.Turn_On(); + break; + case ( CHECK_AM | KN_BUTTON ): + if( AM_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + CS_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::AMGAME; + } + else + // Box was unchecked. Has no effect. + AM_Check.Turn_On(); + break; + + default: + break; + } + } + + if( cgiReturn.bCreateGame && cgiReturn.bPrivate ) + { + // Get a password for the channel. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATEPRIVGAMETITLE, + TXT_WOL_PASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + strcpy( cgiReturn.szPassword, pEditDlg->szEdit ); + else + cgiReturn.bCreateGame = false; // Cancel creation. + pWO->bPump_In_Call_Back = false; + } + + return cgiReturn; +} + +//*********************************************************************************************** +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ) +{ + // Checks appropriate list item based on iPlayerMax. + switch( iPlayerMax ) + { + case 2: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 3: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 4: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + break; + } +} + +#endif diff --git a/REDALERT/WOL_CHAT.CPP b/REDALERT/WOL_CHAT.CPP new file mode 100644 index 000000000..be36a01e4 --- /dev/null +++ b/REDALERT/WOL_CHAT.CPP @@ -0,0 +1,1578 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// wol_chat.cpp +// ajw 7/8/98 + +#include "function.h" +#include "iconlist.h" +#include "WolapiOb.h" +#include "SEditDlg.h" +#include "WolStrng.h" +#include "ToolTip.h" + +//#include "WolDebug.h" + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ); +bool ExitChatChannel( WolapiObject* pWO ); +void CreateChatChannel( WolapiObject* pWO ); +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ); +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ); + +enum LIST_EXPAND_STATE +{ + LES_NORMAL, + LES_CHANNELS_EXPANDED, + LES_USERS_EXPANDED, +}; +static LIST_EXPAND_STATE lesCurrent = LES_NORMAL; + +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ); +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ); +void ResizeChannelList( IconListClass& chanlist, bool bExpand ); +void ResizeUserList( IconListClass& userlist, bool bExpand ); + +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ); + +extern CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ); + +static int d_chanlist_w; +static int d_chanlist_h; +static int d_chanlist_x; +static int d_chanlist_y; + +static int d_userlist_w; +static int d_userlist_h; +static int d_userlist_x; +static int d_userlist_y; + +#define DRAWTOGDOWN Turn_Off() +#define DRAWTOGUP Turn_On() + +//*********************************************************************************************** +int WOL_Chat_Dialog( WolapiObject* pWO ) +{ + int rc; + bool bFirsttime = true; + bool bHackFocus = true; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_text_h = 12; + int d_margin1 = 34; // large margin + int d_margin2 = 14; // small margin + + int d_chatlist_w = 340; + int d_chatlist_x = d_dialog_x + d_margin1; + int d_chatlist_y = d_dialog_y + d_margin2 + d_margin1 + 27; + int d_chatlist_h = 337 - d_chatlist_y; + + d_chanlist_w = 227; + d_chanlist_h = 50 * RESFACTOR; + d_chanlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_chanlist_w); + d_chanlist_y = d_chatlist_y; + + d_userlist_w = d_chanlist_w; +// int d_userlist_h = ((10 * 6) + 3) *RESFACTOR; + d_userlist_x = d_chanlist_x; + d_userlist_y = d_chanlist_y + d_chanlist_h + 14 + 5; + + d_userlist_h = d_chatlist_y + d_chatlist_h - d_userlist_y; + + int d_action_w = 100; + int d_action_h = 9 *RESFACTOR; + int d_action_x = d_dialog_x + 500; + int d_action_y = 365; + +// int d_chanpriv_w = 60; +// int d_chanpriv_h = 9 *RESFACTOR; +// int d_chanpriv_x = d_dialog_x + 150; +// int d_chanpriv_y = d_action_y; + +// int d_cgame_w = 60; +// int d_cgame_h = 9 *RESFACTOR; +// int d_cgame_x = d_dialog_x + 390; //d_dialog_cx - d_cgame_w / 2; +// int d_cgame_y = d_action_y; + + int d_back_w = 100; + int d_back_h = 9 *RESFACTOR; + int d_back_x = d_dialog_x + 100; + int d_back_y = d_action_y; + + int d_join_w = 100; + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + 210; + int d_join_y = d_action_y; + + int d_create_w = 100; + int d_create_h = 9 *RESFACTOR; + int d_create_x = d_dialog_x + 320; //((d_dialog_w * 5) / 6) - (d_create_w / 2); + int d_create_y = d_action_y; + + int d_send_w = d_chanlist_x + d_chanlist_w - d_chatlist_x; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_chatlist_x; + int d_send_y = d_chatlist_y + d_chatlist_h + 5; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_CHATLIST, + BUTTON_CHANLIST, + BUTTON_USERLIST, + BUTTON_SENDEDIT, + BUTTON_ACTION, +// BUTTON_CGAME, + BUTTON_CREATE, + BUTTON_JOIN, + BUTTON_BACK, + BUTTON_EXPANDCHANNELS, + BUTTON_EXPANDUSERS, + BUTTON_RANKRA, + BUTTON_RANKAM + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + TTimerClass lastclick_timer; + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + char* pShpExpand = (char*)MFCD::Retrieve( "exp.shp" ); + char* pShpUnexpand = (char*)MFCD::Retrieve( "unexp.shp" ); + + IconListClass chatlist( BUTTON_CHATLIST, d_chatlist_x, d_chatlist_y, d_chatlist_w, d_chatlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 500 ); + ShapeButtonClass ExpandChanBtn( BUTTON_EXPANDCHANNELS, pShpExpand, d_chanlist_x + d_chanlist_w - 17, d_chanlist_y - 14 ); + IconListClass chanlist( BUTTON_CHANLIST, d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + ShapeButtonClass ExpandUserBtn( BUTTON_EXPANDUSERS, pShpExpand, d_userlist_x + d_userlist_w - 17, d_userlist_y - 14 ); + IconListClass userlist( BUTTON_USERLIST, d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); + TextButtonClass ActionBtn( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + TextButtonClass CreateBtn( BUTTON_CREATE, TXT_WOL_NEWSOMETHING, TPF_BUTTON, d_create_x, d_create_y, d_create_w ); + TextButtonClass JoinBtn( BUTTON_JOIN, TXT_WOL_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w ); + TextButtonClass BackBtn( BUTTON_BACK, TXT_WOL_BACK, TPF_BUTTON, d_back_x, d_back_y, d_back_w ); + char* szRecordToStartWith; + if( pWO->bShowRankRA ) + szRecordToStartWith = pWO->szMyRecord; + else + szRecordToStartWith = pWO->szMyRecordAM; + StaticButtonClass chatlistTitle( 0, szRecordToStartWith, TPF_TYPE, d_chatlist_x + 2, d_chatlist_y - 13, d_chatlist_w - 4, 12 ); + StaticButtonClass chanlistTitle( 0, "", TPF_TYPE, d_chanlist_x + 2, d_chanlist_y - 16 + 4, d_chanlist_w - 4 - 16, 12 ); + StaticButtonClass userlistTitle( 0, TXT_WOL_NOUSERLIST, TPF_TYPE, d_userlist_x + 2, d_userlist_y - 16 + 4, d_userlist_w - 4 - 16 * 4, 12 ); + + char szSendBuffer[MAXCHATSENDLENGTH] = ""; + EditClass sendedit( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + + char* pShpRankRA = (char*)MFCD::Retrieve( "rank_ra.shp" ); + char* pShpRankAM = (char*)MFCD::Retrieve( "rank_am.shp" ); + ShapeButtonClass RankRABtn( BUTTON_RANKRA, pShpRankRA, d_userlist_x + d_userlist_w - ( 16 * 4 + 1 ), d_userlist_y - 14 ); + ShapeButtonClass RankAMBtn( BUTTON_RANKAM, pShpRankAM, d_userlist_x + d_userlist_w - ( 16 * 3 + 1 ), d_userlist_y - 14 ); + // Change draw behavior of toggle buttons. + RankRABtn.ReflectButtonState = true; + RankAMBtn.ReflectButtonState = true; + + // Build the button list. + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + chatlist.Add_Tail(*commands); + ExpandChanBtn.Add_Tail(*commands); + chanlist.Add_Tail(*commands); + ExpandUserBtn.Add_Tail(*commands); + userlist.Add_Tail(*commands); + ActionBtn.Add_Tail(*commands); + CreateBtn.Add_Tail(*commands); +// CreatePrivBtn.Add_Tail(*commands); + JoinBtn.Add_Tail(*commands); + BackBtn.Add_Tail(*commands); +// CGameBtn.Add_Tail(*commands); + chatlistTitle.Add_Tail(*commands); + chanlistTitle.Add_Tail(*commands); + userlistTitle.Add_Tail(*commands); + sendedit.Add_Tail(*commands); + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead = NULL; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast = NULL; // ToolTip the mouse was last over, or null. + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + pToolTip = pToolTip->next; + ToolTipClass TTipChanExpand( &ExpandChanBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandChanBtn.X + 8, ExpandChanBtn.Y - 9, true ); + pToolTip->next = &TTipChanExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipUserExpand( &ExpandUserBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandUserBtn.X + 8, ExpandUserBtn.Y - 9, true ); + pToolTip->next = &TTipUserExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipChanList( &chanlist, 0, chanlist.X + 1, chanlist.Y + 1, true, true ); + pToolTip->next = &TTipChanList; + pToolTip = pToolTip->next; + ToolTipClass TTipJoin( &JoinBtn, TXT_WOL_TTIP_JOIN, d_join_x + d_join_w/2, d_join_y - 6 ); + pToolTip->next = &TTipJoin; + pToolTip = pToolTip->next; + ToolTipClass TTipBack( &BackBtn, TXT_WOL_TTIP_BACK, d_back_x + d_back_w/2, d_back_y - 6 ); + pToolTip->next = &TTipBack; + pToolTip = pToolTip->next; + ToolTipClass TTipCreate( &CreateBtn, TXT_WOL_TTIP_CREATE, d_create_x + d_create_w/2, d_create_y - 6 ); + pToolTip->next = &TTipCreate; + pToolTip = pToolTip->next; + ToolTipClass TTipAction( &ActionBtn, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + pToolTip->next = &TTipAction; + pToolTip = pToolTip->next; + ToolTipClass TTipRankRA( &RankRABtn, TXT_WOL_TTIP_RANKRA, RankRABtn.X + 8, RankRABtn.Y - 9, true ); + pToolTip->next = &TTipRankRA; + pToolTip = pToolTip->next; + ToolTipClass TTipRankAM( &RankAMBtn, TXT_WOL_TTIP_RANKAM, RankAMBtn.X + 8, RankAMBtn.Y - 9, true ); + pToolTip->next = &TTipRankAM; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + //........................................................................ + // List boxes + //........................................................................ + int tabs[] = { 150 }; // tabs for channel list + chanlist.Set_Tabs( tabs ); + +// Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + lastclick_timer = 0; + + // Tell WolapiObject about lists to use for output. + // (Sure wish I'd gone against the grain and made this dialog a class...) + pWO->LinkToChatDlg( &chatlist, &chanlist, &userlist, &userlistTitle ); + + if( !pWO->bChatShownBefore ) + { + // Print message of the day. + chatlist.Add_Item( pWO->pChatSink->szMotd, NULL, NULL, ICON_SHAPE ); + } + else + { + // We have returned to the chat dialog after being in either game setup or an actual game. + pWO->RestoreChat(); + pWO->DeleteSavedChat(); + + if( pWO->bReturningAfterGame ) + pWO->RejoinLobbyAfterGame(); + else + { + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of a game setup. + WOL_PrintMessage( chatlist, TXT_WOL_YOUWEREKICKEDFROMGAME, WOLCOLORREMAP_KICKORBAN ); + pWO->pChatSink->bGotKickedTrigger = false; + } + } + + if( pWO->iLobbyReturnAfterGame != -1 ) + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, pWO->iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, pWO->iLobbyReturnAfterGame ); + pWO->OnEnteringChatChannel( szChannelToJoin, false, iChannelLobbyNumber( (unsigned char*)szChannelToJoin ) ); + } + else + // Will never happen presumably, if games are always entered via a lobby chat channel. + pWO->EnterLevel_Top(); + + pWO->iLobbyReturnAfterGame = -1; + + if( pWO->bReturningAfterGame ) + { + Sound_Effect( WOLSOUND_LOGIN ); + pWO->bReturningAfterGame = false; + } + else + Sound_Effect( WOLSOUND_EXITGAME ); + } + + // Cause a refresh of szMyRecord, the string showing my win/loss record. + pWO->RequestLadders( pWO->szMyName ); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) + { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { +/* + if( pToolTipHitLast && pToolTipHitLast->bShowing ) // Lame hack. Problem is draws that occur in callbacks. + { + pToolTipHitLast->Unshow(); + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + pToolTipHitLast->Show(); + } + else + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + } +*/ + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + + // Special post-callback processing... + if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + rc = -1; // As if the user logged himself out. + process = false; + break; + } + if( pWO->pChatSink->bGotKickedTrigger ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_UserChat(); + } + else if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_Lobbies(); + } + else // Must be WOL_LEVEL_INOFFICIALCHATCHANNEL. + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_OfficialChat(); + } + pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + if( pWO->bMyRecordUpdated ) + { + if( pWO->bShowRankRA ) + chatlistTitle.Set_Text( pWO->szMyRecord ); + else + chatlistTitle.Set_Text( pWO->szMyRecordAM ); + pWO->bMyRecordUpdated = false; + } + if( pWO->bChannelListTitleUpdated ) + { + chanlistTitle.Set_Text( pWO->szChannelListTitle ); + pWO->bChannelListTitleUpdated = false; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Synch rank toggle buttons state. + if( BackBtn.Get_Prev() == &RankAMBtn ) + { + if( pWO->CurrentLevel != WOL_LEVEL_INLOBBY ) + { + // Rank buttons are there and shouldn't be. + RankRABtn.Remove(); + RankAMBtn.Remove(); + display = REDRAW_ALL; + } + else + { + if( pWO->bShowRankUpdated ) + { + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + // Cause my own record to get refreshed. + pWO->bMyRecordUpdated = true; + // Refresh list to show different rankings type. + pWO->ListChannelUsers(); + } + } + } + else + { + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Rank buttons aren't there and should be. + RankRABtn.Add( JoinBtn ); + RankAMBtn.Add( RankRABtn ); + RankRABtn.Flag_To_Redraw(); + RankAMBtn.Flag_To_Redraw(); + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + } + } + + // Regularly update the channels list in certain cases. + if( ( pWO->CurrentLevel == WOL_LEVEL_OFFICIALCHAT || pWO->CurrentLevel == WOL_LEVEL_USERCHAT || + pWO->CurrentLevel == WOL_LEVEL_LOBBIES || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) && + ::timeGetTime() > pWO->dwTimeNextChannelUpdate ) + { + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ); + break; + case WOL_LEVEL_USERCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ); + break; + case WOL_LEVEL_LOBBIES: // Overkill in this case to update so often... + pWO->UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ); + break; + case WOL_LEVEL_INLOBBY: + pWO->UpdateChannels( GAME_TYPE, CHANNELFILTER_LOCALLOBBYGAMES, true ); + break; + } + pWO->dwTimeNextChannelUpdate = ::timeGetTime() + CHANNELUPDATEWAIT; + } + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if( bFirsttime && !pWO->bChatShownBefore ) + { + WWMessageBox().Process( TXT_WOL_FINDINGLOBBY, TXT_NONE ); + char szLobbyName[ WOL_CHANNAME_LEN_MAX ]; + if( pWO->GetNameOfBeginningLobby( szLobbyName ) ) + { +// debugprint( "Found lobby to go into: '%s'\n", szLobbyName ); + if( !EnterChannel( pWO, chatlist, NULL, szLobbyName, false ) ) + { + // Could not enter channel for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + } + else + { + // Could not find name of a lobby for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + pWO->bChatShownBefore = true; + display = REDRAW_ALL; + // Play login sound. + Sound_Effect( WOLSOUND_LOGIN ); + } + bFirsttime = false; + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) + { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + + commands->Draw_All(); + + // Draw title bar above channel list. + Draw_Box( d_chanlist_x, d_chanlist_y - 15, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + switch( lesCurrent ) + { + case LES_CHANNELS_EXPANDED: + // Draw users title bar at bottom. + Draw_Box( d_userlist_x, d_userlist_y + d_userlist_h - 16, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + case LES_USERS_EXPANDED: + // Draw users title bar at top. + Draw_Box( d_chanlist_x, d_chanlist_y, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + break; + default: + // Draw users title bar in middle. + Draw_Box( d_userlist_x, d_userlist_y - 15, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + // Mouse button is down. + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + } + + // If anything currently on the controls list is set to redraw, hide tooltip. + if( pToolTipHitLast && pToolTipHitLast->bShowing && commands->Is_List_To_Redraw() ) + { + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + // This hack, used elsewhere in this form, appears to be the standard dodge around GadgetClass::Input's + // tendency to remove any focus the first time it runs for a 'commands' list. + + // ajw - Perhaps I could try doing this every cycle regardless - would avoid stupid non-focused editbox key reactions bug. + if( bHackFocus ) + { + sendedit.Set_Focus(); + sendedit.Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && bLinkInList( commands, pToolTipHit->pGadget ) ) // (Gadget must be in controls list.) + { + if( !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && + !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + else if( pToolTipHit->bIconList && pToolTipHit->bOverDifferentLine() ) + { + pToolTipHit->Unshow(); + pToolTipHit->Show(); + } + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) + { + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by sendedit control. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, false ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case KN_LMOUSE: + break; + + case ( BUTTON_EXPANDCHANNELS | KN_BUTTON ): + if( OnExpandChannelList( chanlist, userlist ) ) + { + // Hide userlist. + if( ExpandUserBtn.Get_Next() == &userlist ) + userlist.Remove(); + // Ensure chanlist is visible. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, chanlist.Y + chanlist.Height ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, chanlist.Y + chanlist.Height + 2 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpUnexpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + else + { + // Show userlist. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_EXPANDUSERS | KN_BUTTON ): + if( OnExpandUserList( chanlist, userlist ) ) + { + // Hide chanlist controls. + if( ExpandChanBtn.Get_Next() == &chanlist ) + chanlist.Remove(); + // Ensure userlist is visible. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpUnexpand ); + } + else + { + // Show chanlist. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_CHANLIST | KN_BUTTON ): + // User clicks on the game list. + //............................................................... + // Handle a double-click + //............................................................... + if( lastclick_timer < 30 && chanlist.Current_Index() == lastclick_idx ) + { + // Doubleclick on channel list. + if( ProcessChannelListSelection( pWO, chatlist, chanlist, lastclick_idx ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + else + { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = chanlist.Current_Index(); + + } + break; + + case ( BUTTON_JOIN | KN_BUTTON ): + // Pressing the join button is exactly like doubleclicking on the selected index in chanlist, except: + // if the first item is selected, ignore, unless we are at the top level + if( pWO->CurrentLevel == WOL_LEVEL_TOP || chanlist.Current_Index() != 0 ) + { + if( ProcessChannelListSelection( pWO, chatlist, chanlist, chanlist.Current_Index() ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + break; + + case ( BUTTON_USERLIST | KN_BUTTON ): + // User clicks on user list. + break; + + case ( BUTTON_CREATE | KN_BUTTON ): + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_INCHATCHANNEL: +// debugprint( "%s\n", TXT_WOL_CANTCREATEINCHANNEL ); + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + case WOL_LEVEL_INLOBBY: + { + pWO->bPump_In_Call_Back = true; + CREATEGAMEINFO CreateGameInfo = WOL_CreateGame_Dialog( pWO ); + pWO->bPump_In_Call_Back = false; + if( CreateGameInfo.bCreateGame ) + { + if( CreateGameChannel( pWO, CreateGameInfo ) ) + { + rc = 1; + process = false; + } + } + break; + } + case WOL_LEVEL_GAMES: + case WOL_LEVEL_GAMESOFTYPE: + case WOL_LEVEL_LOBBIES: + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEHERE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + default: + CreateChatChannel( pWO ); + } + + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + // Because of the way things are set up, this is exactly like selecting the first item in chanlist. + // (Button is disabled when this is not appropriate.) + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): + pWO->dwTimeNextChannelUpdate = ::timeGetTime(); + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( &userlist ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( &userlist, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( &userlist, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( KN_ESC ): +// break; ajw Put back in? + + case ( BUTTON_BACK | KN_BUTTON ): + // Pressing the back button is exactly like doubleclicking on the top item in chanlist, except + // when we're at the top level. + if( pWO->CurrentLevel != WOL_LEVEL_TOP ) + { + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + } + // Note no break; here. Fall through if at top level. + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + ExitChatChannel( pWO ); + pWO->Logout(); + rc = -1; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, true ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + //chatlist.Flag_To_Redraw(); + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_RANKRA | KN_BUTTON ): + pWO->bShowRankRA = true; + pWO->bShowRankUpdated = true; + break; + + case ( BUTTON_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = false; + pWO->bShowRankUpdated = true; + break; + + default: + + break; + } + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + +/* + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); +*/ + + pWO->SaveChat(); + + pWO->ClearListPtrs(); + + return rc; +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + RemapControlType* pColorRemap = ( iColorRemap == PCOLOR_NONE ? NULL : &ColorRemaps[ iColorRemap ] ); + WOL_PrintMessage( ILTarget, szText, pColorRemap ); +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ) +{ + ILTarget.Add_Item( szText, NULL, NULL, ICON_SHAPE, NULL, NULL, pColorRemap ); + if( !ILTarget.bScrollBeingDragged() ) + ILTarget.Show_Last_Item(); +} + +//*********************************************************************************************** +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand channel list button was pressed. + // Returns true if userlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand user list button was pressed. + // Returns true if chanlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +void ResizeChannelList( IconListClass& chanlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_userlist_y + d_userlist_h - 15 - d_chanlist_y ); + else + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h ); +} + +//*********************************************************************************************** +void ResizeUserList( IconListClass& userlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + userlist.Resize( d_userlist_x, d_chanlist_y + 15, d_userlist_w, d_userlist_y + d_userlist_h - ( d_chanlist_y + 15 ) ); + else + userlist.Resize( d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ) +{ + // Enter the channel specified in chanlist at iIndex. + // Called to enter chat channels, "lobbies", and game channels. + + // We've stored the channel pointer in the hidden extra data field. + // ( Be careful about calling RAChatEventSink::DeleteChannelList()! ) + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + return EnterChannel( pWO, chatlist, pChannel, NULL, bGame ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ) +{ + // Called to cause the user to enter a channel (chat, lobby, or game). + // If pChannel is NULL, szChannelName will be used. + + Channel ChannelWhenNameOnly; + if( !pChannel ) + { + if( !szChannelName ) + { +// debugprint( "pChannel and szChannelName null in EnterChannel" ); + pWO->bSelfDestruct = true; + return false; + } + pChannel = &ChannelWhenNameOnly; + strcpy( (char*)pChannel->name, szChannelName ); + } + + if( bGame && pChannel->currentUsers >= pChannel->maxUsers ) // Pre-emptive fullness check. + { + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + + if( bGame ) + { + // It is possible to enter a game channel while currently in a chat channel. (A lobby, presumably.) + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel on join failed" ); + pWO->bSelfDestruct = true; + return false; + } + } + +/* The following doesn't work because the needpw field is not currently being set in wolapi for chat channels. + Instead, we wait for a fail on join, then present this dialog and try again. + if( pChannel->needpw ) + { + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); + if( !pEditDlg->Show() || !*pEditDlg->szEdit ) + return false; + + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + } +*/ + bool bKeepTrying = true; + + // Set password automatically for our lobbies, if trying to join one. + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby != -1 ) + strcpy( (char*)pChannel->key, LOBBYPASSWORD ); + + char szSuccessfulPassword[ WOL_PASSWORD_LEN + 5 ]; + *szSuccessfulPassword = 0; + + HRESULT hRes; + while( bKeepTrying ) + { + hRes = pWO->ChannelJoin( pChannel ); + switch( hRes ) + { + case CHAT_E_BADCHANNELPASSWORD: + { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. +#ifdef ENGLISH + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else +#ifdef GERMAN + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 400, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 500, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#endif +#endif + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) != 0 || !*pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + delete pEditDlg; + bKeepTrying = false; + break; + } + pWO->bPump_In_Call_Back = false; + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + strcpy( szSuccessfulPassword, pEditDlg->szEdit ); + delete pEditDlg; + break; + } + case CHAT_E_TIMEOUT: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_CHANNELDOESNOTEXIST: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_CHANNELGONE ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_BANNED: + WOL_PrintMessage( chatlist, TXT_WOL_YOUREBANNED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case CHAT_E_CHANNELFULL: + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case E_FAIL: + pWO->GenericErrorMessage(); + bKeepTrying = false; + break; + case S_OK: + bKeepTrying = false; + break; + } + } + + if( !bGame ) + { + if( hRes == S_OK ) + return pWO->OnEnteringChatChannel( (char*)pChannel->name, false, iLobby ); + else + return false; + } + else + { + if( hRes == S_OK ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; + // Return later to the lobby of the channel creator - which was saved in the channel itself. + pWO->iLobbyReturnAfterGame = pChannel->reserved & 0x00FFFFFF; + if( pWO->iLobbyReturnAfterGame == 0x00FFFFFF ) + pWO->iLobbyReturnAfterGame = -1; + CREATEGAMEINFO CreateGameInfo; + // Not all of these values are currently used during setup. + CreateGameInfo.bCreateGame = false; + CreateGameInfo.iPlayerMax = pChannel->maxUsers; + CreateGameInfo.bTournament = pChannel->tournament; + if( *szSuccessfulPassword ) + { + CreateGameInfo.bPrivate = true; + strcpy( CreateGameInfo.szPassword, szSuccessfulPassword ); + } + else + { + CreateGameInfo.bPrivate = false; + *CreateGameInfo.szPassword = 0; + } + CreateGameInfo.GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + return pWO->OnEnteringGameChannel( (char*)pChannel->name, false, CreateGameInfo ); + } + else + { + pWO->OnFailedToEnterGameChannel(); + *pWO->szChannelReturnOnGameEnterFail = 0; + return false; + } + } +} + +//*********************************************************************************************** +bool ExitChatChannel( WolapiObject* pWO ) +{ + // Called to cause user to leave current chat or lobby channel. + +// debugprint( "ExitChatChannel\n" ); + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingChatChannel(); + + return true; +} + +//*********************************************************************************************** +//void CreateChatChannel( WolapiObject* pWO, bool bPrivate ) +void CreateChatChannel( WolapiObject* pWO ) +{ + SimpleEditDlgClass* pEditDlg; +/* if( !bPrivate ) + { + pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX ); + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + else +*/ { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pEditDlg = new SimpleEditDlgClass( 350, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX, TXT_WOL_OPTIONALPASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + if( *pEditDlg->szEdit2 ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, pEditDlg->szEdit2 ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + else + { + // Create public channel. + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + pWO->bPump_In_Call_Back = false; + } + + delete pEditDlg; +} + +//*********************************************************************************************** +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ) +{ + char szNewChannelName[ WOL_CHANNAME_LEN_MAX ]; + sprintf( szNewChannelName, "%s's_game", pWO->szMyName ); + + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel in CreateGameChannel() error" ); + pWO->bSelfDestruct = true; + return false; + } + + const char* szKey; + if( *cgi.szPassword ) + szKey = cgi.szPassword; + else + szKey = NULL; + + if( pWO->ChannelCreate( szNewChannelName, szKey, true, cgi.iPlayerMax, cgi.bTournament, pWO->iLobbyLast, cgi.GameKind ) ) + pWO->OnEnteringGameChannel( szNewChannelName, true, cgi ); + else + { + pWO->OnFailedToEnterGameChannel(); +// debugprint( "CreateGameChannel fail" ); + return false; + } + *pWO->szChannelReturnOnGameEnterFail = 0; + + return true; +} + +//*********************************************************************************************** +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ) +{ + // Takes whatever action necessary due to user selecting iIndex from chanlist. + // Returns true if user selected to enter a game channel, else false. + if( iIndex < 0 ) + return false; + +// debugprint( "iIndex %i\n", iIndex ); + + const char* szChannelType = chanlist.Get_Item_ExtraDataString( iIndex ); + if( szChannelType ) + { +//debugprint( "szChannelType %s\n", szChannelType ); + if( strcmp( szChannelType, CHANNELTYPE_OFFICIALCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_OfficialChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_USERCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_UserChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_TOP ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Top(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Games(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMESOFTYPE ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + void* pExtraData = chanlist.Get_Item_ExtraDataPtr( iIndex ); + pWO->EnterLevel_GamesOfType( (WOL_GAMETYPEINFO*)pExtraData ); + } + else if( strcmp( szChannelType, CHANNELTYPE_CHATCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMECHANNEL ) == 0 ) + { + // User attempting to join game channel. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Check if local user is allowed to join GameKind. + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + if( pChannel->type == GAME_TYPE ) + { + // It is a game of our type, at least. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + break; + case CREATEGAMEINFO::CSGAME: + if( !Is_Counterstrike_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDCOUNTERSTRIKE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + case CREATEGAMEINFO::AMGAME: + if( !Is_Aftermath_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDAFTERMATH, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + // Join the game channel. + if( EnterChannel( pWO, chatlist, chanlist, iIndex, true ) ) + { + // Exit the chat dialog, go to game dialog. + return true; + } + } + else + { + // User doubleclicked on a game that is of a different type. + // Offer to take them to a web page regarding the game type. + pWO->DoGameAdvertising( pChannel ); + } + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBIES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Chat channel to lobbies level?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Lobbies(); + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBYCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || + pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Not currently possible. +// debugprint( "Chat or lobby channel to lobby channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the lobby chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_LOADING ) == 0 ) + { + // User clicked on the channel list loading notification - do nothing. + } + else + { +// debugprint( "Item selected in channel list unidentifiable from extradata field\n" ); + pWO->bSelfDestruct = true; + return false; + } + + } + return false; +} + +//*********************************************************************************************** +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ) +{ + const LinkClass* pLink = pListHead; + while( pLink ) + { + if( pLink == pLinkToFind ) + return true; + pLink = pLink->Get_Next(); + } + return false; +} + +#endif diff --git a/REDALERT/WOL_DNLD.CPP b/REDALERT/WOL_DNLD.CPP new file mode 100644 index 000000000..8f53f8f3a --- /dev/null +++ b/REDALERT/WOL_DNLD.CPP @@ -0,0 +1,260 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Dnld.cpp - WW online patch download dialog. +// ajw 10/12/98 + +#include "function.h" +#include "WolapiOb.h" +#include "WolStrng.h" + +//*********************************************************************************************** +bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ) +{ + // This dialog is presented for each file that is to be downloaded during a WOLAPI patch. + + bool bReturn = true; + DWORD dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200*RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin = 34; + int d_txt6_h = 15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + +// int width; +// int height; +// char* info_string = (char*)szTitle; + + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW ); + +// Format_Window_String( info_string, SeenBuff.Get_Height(), width, height ); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + TextButtonClass cancelbtn( BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y ); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h ); +#endif + + GaugeClass progress_meter( BUTTON_PROGRESS, d_progress_x, d_progress_y, d_progress_w, d_progress_h ); + progress_meter.Use_Thumb( 0 ); + + StaticButtonClass StatTitle( 0, szTitle, TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 28, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatStatus( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 49, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatBytes( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 71, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatTime( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 117, d_dialog_w - 2 * d_margin, d_txt6_h ); + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass* commands; // button list + + commands = &cancelbtn; + progress_meter.Add_Tail(*commands); + StatTitle.Add_Tail(*commands); + StatBytes.Add_Tail(*commands); + StatTime.Add_Tail(*commands); + StatStatus.Add_Tail(*commands); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + +// Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, +// GadgetClass::Get_Color_Scheme(), TBLACK, +// TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + if (process){ + input = cancelbtn.Input(); + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + pDownload->Abort(); + process = false; + bReturn = false; + break; + } + } + + if( ::timeGetTime() > dwTimeNextPump ) + { + pDownload->PumpMessages(); + if( pDownloadSink->bFlagEnd ) + { + pDownloadSink->bFlagEnd = false; + process = false; + break; + } + if( pDownloadSink->bFlagError ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADERROR ); + pDownloadSink->bFlagError = false; + process = false; + bReturn = false; + break; + } + if( pDownloadSink->bFlagProgressUpdate ) + { + pDownloadSink->bFlagProgressUpdate = false; + progress_meter.Set_Value( ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + char szText[200]; + sprintf( szText, TXT_WOL_DOWNLOADBYTES, pDownloadSink->iBytesRead, pDownloadSink->iTotalSize, + ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + StatBytes.Set_Text( szText ); + sprintf( szText, TXT_WOL_DOWNLOADTIME, pDownloadSink->iTimeLeft / 60, pDownloadSink->iTimeLeft % 60 ); + StatTime.Set_Text( szText ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagStatusUpdate ) + { + pDownloadSink->bFlagStatusUpdate = false; + switch( pDownloadSink->iStatus ) + { + case DOWNLOADSTATUS_CONNECTING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADCONNECTING ); + break; + + case DOWNLOADSTATUS_FINDINGFILE: + StatStatus.Set_Text( TXT_WOL_DOWNLOADLOCATING ); + break; + + case DOWNLOADSTATUS_DOWNLOADING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADDOWNLOADING ); + break; + + default: +// debugprint( "Unknown status update!\n" ); + break; + } + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagQueryResume ) + { + if( pDownloadSink->bResumed ) + { + char szTitleNew[200]; + sprintf( szTitleNew, TXT_WOL_DOWNLOADRESUMED, szTitle ); + StatTitle.Set_Text( szTitleNew ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + } + + dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Invoke game callback + Call_Back(); + + } while ( process ); + + return bReturn; +} + +#endif diff --git a/REDALERT/WOL_GSUP.CPP b/REDALERT/WOL_GSUP.CPP new file mode 100644 index 000000000..8b655c4ba --- /dev/null +++ b/REDALERT/WOL_GSUP.CPP @@ -0,0 +1,3500 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifdef WOLAPI_INTEGRATION // Now implies also WINSOCK_IPX, WIN32, and FIXIT_CSII must be true + +#include "wol_gsup.h" +#include "function.h" +#include "IconList.h" +#include +#include "WolStrng.h" +#include "wsproto.h" +#include "BigCheck.h" +#include "ToolTip.h" + +extern char const* EngMisStr[]; + +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); + +bool Force_Scenario_Available( const char* szName ); + +int ScenarioIndex_From_Filename( const char* szScenarioFilename ); + +bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +#include "WolDebug.h" + +#define PARAMREFRESHWAIT 2000 + +#define PING_AND_DISPLAY_WAIT 5000 + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ); +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ); +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ); + +extern unsigned long PlanetWestwoodStartTime; //Time that game was started + +extern bool cancel_current_msgbox; +extern bool disable_current_msgbox; + +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ); + +//*********************************************************************************************** +/* Game startup logic: +When a guest joins the channel, the host sends all current setup info. +When a guest changes house, he tells the host, who informs everyone. A house change can't be denied. +When a guest changes color, he asks the host for the new color, and assumes the request will go through. If it doesn't, the +host sends the guest a messages setting him back to the old color, otherwise, everyone gets the new color for the guest. +When the host changes a game param, the change will be noticed within PARAMREFRESHWAIT milliseconds and then transmitted to +all guests. +All player specific information is stored within the players list (IconListClass), and nowhere else. Though this storage +method got a bit inelegant, it gets the job done. +All "informs" (messages sent from host to guest) except color changes (WOL_GAMEOPT_INFCOLOR) and player "accept" status +changes (WOL_GAMEOPT_INFACCEPT) are assigned a ID, which +increments on each send. Color informs are: a) not a condition for removing a guest's "accept" status, and b) sent out to +individual guests in certain cases, and would thus complicate the ID tracking scheme. +When a guest "accepts" the game setup (with a WOL_GAMEOPT_REQACCEPT), the latest received ID is included in the message. +If the host receives a WOL_GAMEOPT_REQACCEPT with an out-of-date ID, the accept is ignored. Though the guest sets himself to +"accepted" when he sends the WOL_GAMEOPT_REQACCEPT, if it gets ignored by the host it is because a new inform is already on its +way, which will reset the guest's own status to "not accepted" when it arrives. +Host clears all accepteds when params are changed, player joins or leaves, or house change message arrives. Though a +WOL_GAMEOPT_INFACCEPT is sent out when a guest accepts, there is no corresponding "unaccept all" message sent out at this point, +as all guests will naturally clear their recorded accept status for everyone when the change arrives there. +Guest clears own accepted when an inform from the server other than color change arrives, player joins or leaves, or own +house is directly changed. +When all players are "accepted", and the host says "start", a WOL_GAMEOPT_INFSTART is sent to all guests and the host goes into +"waiting to start" mode. If a house change or other arrives in this phase, host clears waiting mode and (naturally) marks guests +unaccepted and sends out the house change to the guests. (The same applies to player joins/leaves.) +While in this mode, the host user is locked out of making any changes to game params. +If a change does come in during this phase, a WOL_GAMEOPT_INFCANCELSTART is sent to the guests, and the waiting mode cancelled. +When a guest receives WOL_GAMEOPT_INFSTART: If he does not see himself as accepted, it is because a change has just been sent +to the host, or some other event occurred that the host will learn about, so the guest can simply ignore the WOL_GAMEOPT_INFSTART. +If the guest does see himself as accepted when WOL_GAMEOPT_INFSTART arrives, he responds with WOL_GAMEOPT_REQSTART and goes into +"waiting to start" mode. When in this mode, the user is locked out of making any changes. +Note that the host is not really asking for any sort of "confirmation that it's ok" to start from the guest here. (Hence no +"cancel" response is possible.) It's really just a way of telling the guests not to make any more changes to the setup; it's +like we're making sure we've "flushed the queue" of game changes. +If a guest receives a WOL_GAMEOPT_INFCANCELSTART, he cancels out of "waiting to start" mode. Note that while in the waiting +mode, normal processing of messages is allowed to take place - the user is simply blocked from doing anything. +Presumably (hopefully) this phase will last less than a second. +When the host receives a WOL_GAMEOPT_REQSTART from everyone, he tells everyone to start with a WOL_GAMEOPT_INFGO. +Because there is a chance of color changes being out of sync, all player colors are sent to all guests at this point. +There is no chance of other data being out of sync. +*/ + +//-------------------------------------------------------------------------- +// Pieced together from Net_New_Dialog() and Net_Join_Dialog(). +//-------------------------------------------------------------------------- +//*********************************************************************************************** +WOL_GameSetupDialog::WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ) : pWO( pWO ), bHost( bHost ), + HousePrevious( HOUSE_NONE ), + pILPlayers( NULL ), + pILScens( NULL ), + pILDisc( NULL ), + pEditSend( NULL ), + pGaugeCount( NULL ), + pGaugeLevel( NULL ), + pGaugeCredits( NULL ), + pGaugeAIPlayers( NULL ), + pCheckListOptions( NULL ), +// pTextBtnOk( NULL ), + pTextBtnCancel( NULL ), + pTextBtnAcceptStart( NULL ), + pTextBtnAction( NULL ), + pStaticDescrip( NULL ), + pStaticUnit( NULL ), + pStaticLevel( NULL ), + pStaticCredits( NULL ), + pStaticAIPlayers( NULL ), + pDropListHouse( NULL ), + pCheckAftermathUnits( NULL ), + nHostLastParamID( 0 ), + nGuestLastParamID( 0 ), + bWaitingToStart( false ), + bParamsReceived( false ), + bHostSayGo( false ), + bExitForGameTrigger( false ), + pToolTipHead( NULL ), + pToolTipHitLast( NULL ), + pTTipAcceptStart( NULL ), + pTTipCancel( NULL ), + pTTipAction( NULL ), + ScenKindCurrent( SCENARIO_UNINITIALIZED ), + pShpBtnScenarioRA( NULL ), + pShpBtnScenarioCS( NULL ), + pShpBtnScenarioAM( NULL ), + pShpBtnScenarioUser( NULL ), + bLeaveDueToRulesMismatchTrigger( false ), + bHostWaitingForGoTrigger( false ) +{ + *szSendBuffer = 0; + *szHouseBuffer = 0; + memset( &GParamsLastSent, 0, sizeof( GAMEPARAMS ) ); + *szNameOfHostWhoJustBailedOnUs = 0; + *szTriggerGameStartInfo = 0; +} + +//*********************************************************************************************** +WOL_GameSetupDialog::~WOL_GameSetupDialog() +{ + delete pILPlayers; + delete pILScens; + delete pILDisc; + delete pEditSend; + delete pGaugeCount; + delete pGaugeLevel; + delete pGaugeCredits; + delete pGaugeAIPlayers; + delete pCheckListOptions; +// delete pTextBtnOk; + delete pTextBtnCancel; + delete pTextBtnAcceptStart; + delete pTextBtnAction; +// delete pStaticDescrip; + delete pStaticUnit; + delete pStaticLevel; + delete pStaticCredits; + delete pStaticAIPlayers; + delete pDropListHouse; + delete pCheckAftermathUnits; + delete pTTipAcceptStart; + delete pTTipCancel; + delete pTTipAction; + delete pShpBtnScenarioRA; + delete pShpBtnScenarioCS; + delete pShpBtnScenarioAM; + delete pShpBtnScenarioUser; +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Run() +{ + Initialize(); + return Show(); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::Initialize() +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + d_dialog_w = 640; // dialog width + d_dialog_h = 400; // dialog height + d_dialog_x = 0; + d_dialog_y = 0; + d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_text_h = 12; + d_margin1 = 34; // large margin + + d_color_w = 10 *RESFACTOR; + d_color_h = 9 *RESFACTOR; + d_color_x = 294; //54; //d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + d_color_y = 89; //142; //d_house_y; + + d_house_w = 60 *RESFACTOR; + d_house_h = (8 * 5 *RESFACTOR); + d_house_x = 466; //65; //d_color_x; //d_dialog_cx - (d_house_w / 2); + d_house_y = d_color_y; // + 36; //d_dialog_y + ( 7 * RESFACTOR ) + d_txt6_h + (2*RESFACTOR); + + if( bHost ) + d_disc_w = 365; //d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + else + d_disc_w = d_dialog_w - ( d_margin1 * 2 ); + d_disc_x = d_dialog_x + d_margin1; + d_disc_y = 205; //d_dialog_y + d_dialog_h - ( 65 + d_disc_h ); + d_disc_h = 337 - d_disc_y; + + d_playerlist_w = 124*RESFACTOR; + d_playerlist_h = d_text_h * 4 + 4; + d_playerlist_x = d_dialog_x + d_margin1; + d_playerlist_y = 75; //d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR + 18 * RESFACTOR; + + int d_tab_h = 19; + + d_scenariolist_w = 200; +// d_scenariolist_h = (4 * d_txt6_h) + 3*RESFACTOR; // 4 rows high + d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + d_scenariolist_y = d_disc_y + d_tab_h; + d_scenariolist_h = d_disc_y + d_disc_h - d_scenariolist_y; + + d_gamekind_w = 182; + d_gamekind_h = 30; + d_gamekind_x = 52; // 370; + d_gamekind_y = 153; // d_playerlist_y + d_text_h + 3; // + d_playerlist_h - d_text_h; + + d_count_w = 25*RESFACTOR; + d_count_h = d_txt6_h; + d_count_x = 310; + d_count_y = 138; //d_playerlist_y + d_playerlist_h + d_margin2 + 9*RESFACTOR; + + d_level_w = 25*RESFACTOR; + d_level_h = d_txt6_h; + d_level_x = d_count_x; + d_level_y = d_count_y + d_count_h; + + d_credits_w = 25*RESFACTOR; + d_credits_h = d_txt6_h; + d_credits_x = d_count_x; + d_credits_y = d_level_y + d_level_h; + + d_aiplayers_w = 25*RESFACTOR; + d_aiplayers_h = d_txt6_h; + d_aiplayers_x = d_count_x; + d_aiplayers_y = d_credits_y + d_credits_h; + +#ifdef GERMAN + d_options_w = 186; +#else + d_options_w = 180; +#endif + d_options_h = ((6 * 6) + 4)*RESFACTOR + 1; + d_options_x = d_dialog_x + d_dialog_w - d_options_w - d_margin1; + d_options_y = 127 - d_txt6_h + 2; + + d_send_w = d_dialog_w - ( d_margin1 * 2 ); + d_send_h = 9*RESFACTOR; + d_send_x = d_dialog_x + d_margin1; + d_send_y = d_disc_y + d_disc_h + 5; + +// d_ok_w = 50*RESFACTOR; +// d_ok_h = 9*RESFACTOR; +// d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); +// d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + d_cancel_w = 100; + d_cancel_h = 9*RESFACTOR; + d_cancel_x = d_dialog_x + 100; //d_dialog_cx - (d_cancel_w / 2); + d_cancel_y = 365; //d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + d_accept_w = 100; + d_accept_h = 9 *RESFACTOR; + d_accept_x = d_dialog_x + 210; + d_accept_y = d_cancel_y; //d_dialog_y + d_dialog_h - 17*RESFACTOR; + + d_action_w = 100; + d_action_h = 9 *RESFACTOR; + d_action_x = d_dialog_x + 500; + d_action_y = d_cancel_y; + +/* int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; +*/ + d_amunits_w = 160; + d_amunits_h = 18; + d_amunits_x = d_dialog_x + (d_dialog_w / 6) - (d_amunits_w / 2); + d_amunits_y = 365; + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + + pILPlayers = new IconListClass( BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); +// ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + pILScens = new IconListClass( BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + pILDisc = new IconListClass( BUTTON_DISCLIST, d_disc_x, d_disc_y, d_disc_w, d_disc_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 300 ); + + pEditSend = new EditClass( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + +// TextButtonClass rejectbtn( BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y ); + pGaugeCount = new GaugeClass( BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h ); + pGaugeLevel = new GaugeClass( BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h ); + pGaugeCredits = new GaugeClass( BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h ); + pGaugeAIPlayers = new GaugeClass( BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h ); + pCheckListOptions = new CheckListClass( BUTTON_PARAMS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP") ); +// pTextBtnOk = new TextButtonClass( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR ); +// TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + pTextBtnCancel = new TextButtonClass( BUTTON_CANCEL, TXT_WOL_CANCELGAME, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + pTTipCancel = new ToolTipClass( pTextBtnCancel, TXT_WOL_TTIP_CANCELGAME, d_cancel_x + d_cancel_w/2, d_cancel_y - 6 ); + + if( bHost ) + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_STARTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_START, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + else + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_ACCEPTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_ACCEPT, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + + pTextBtnAction = new TextButtonClass( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + pTTipAction = new ToolTipClass( pTextBtnAction, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + + pToolTip = pToolTip->next; + pToolTip->next = pTTipCancel; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAcceptStart; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAction; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + if( bHost ) + pTextBtnAcceptStart->Disable(); + + // pStaticDescrip is no longer used - can't get the bloody thing to clip text. You'd think a StaticButton control would. +// pStaticDescrip = new StaticButtonClass( 0, "", TPF_TYPE, d_gamekind_x, d_gamekind_y, d_gamekind_w, d_gamekind_h ); + pStaticUnit = new StaticButtonClass( 0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y ); + pStaticLevel = new StaticButtonClass( 0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y ); + pStaticCredits = new StaticButtonClass( 0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y ); + pStaticAIPlayers = new StaticButtonClass( 0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y ); + + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + pDropListHouse = new DropListClass( BUTTON_HOUSE, szHouseBuffer, sizeof( szHouseBuffer ), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP") ); + + // ajw - This checkbox is not used. Could be turned on, though. + pCheckAftermathUnits = new BigCheckBoxClass( BUTTON_AFTERMATHUNITS, d_amunits_x, d_amunits_y, d_amunits_w, d_amunits_h, + "Aftermath units enabled", TPF_TEXT, false ); + + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + bAftermathUnits = true; + int current_drive = CCFileClass::Get_CD_Drive(); + int cd_index = Get_CD_Index(current_drive, 1*60); + if( cd_index != 3 && cd_index != 5 ) + { + WOL_PrintMessage( *pILDisc, TXT_WOL_AMDISCNEEDED, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + else + { + bAftermathUnits = false; + pCheckAftermathUnits->Disable(); + } + + bSlowUnitBuildRate = true; + +#define TABSPACING 38 + pShpBtnScenarioRA = new ShapeButtonClass( BUTTON_SCENARIO_RA, MFCD::Retrieve( "tabra.shp" ), d_scenariolist_x, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioCS = new ShapeButtonClass( BUTTON_SCENARIO_CS, MFCD::Retrieve( "tabcs.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioAM = new ShapeButtonClass( BUTTON_SCENARIO_AM, MFCD::Retrieve( "tabam.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + + int iScenarioUserTabPos; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME || pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + // Place user tab in the third tab position. (It may still not be present.) + iScenarioUserTabPos = d_scenariolist_x + TABSPACING * 2; + else + iScenarioUserTabPos = d_scenariolist_x + TABSPACING; + + pShpBtnScenarioUser = new ShapeButtonClass( BUTTON_SCENARIO_USER, MFCD::Retrieve( "tabus.shp" ), iScenarioUserTabPos, d_scenariolist_y - d_tab_h ); + + // Change draw behavior of tab buttons. + pShpBtnScenarioRA->ReflectButtonState = true; + pShpBtnScenarioCS->ReflectButtonState = true; + pShpBtnScenarioAM->ReflectButtonState = true; + pShpBtnScenarioUser->ReflectButtonState = true; + + if( !bHost ) + { + pILScens->Disable(); + pGaugeCount->Disable(); + pGaugeLevel->Disable(); + pGaugeCredits->Disable(); + pGaugeAIPlayers->Disable(); + pCheckListOptions->Disable(); + pCheckAftermathUnits->Disable(); + } + + pWO->LinkToGameDlg( pILDisc, pILPlayers ); + pWO->pGSupDlg = this; + + dwTimeNextParamRefresh = ::timeGetTime(); +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Show() +{ + // Returns: -1 == return to chat dialog. + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + bool bHackFocus = true; + + display = REDRAW_ALL; // redraw level + bProcess = true; // process while true + KeyNumType input; + + DWORD timeWaitingToStartTimeout; + + char szScenarioNameDisplay[ 300 ]; + + bool bInformParamChange; + + long ok_timer = 0; // for timing OK button + int i; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + + bool bRetractHouseDropDown = false; + + if( !pWO->OnEnteringGameSetup() ) // Gets a userlist setup, among other things. + strcpy( szNameOfHostWhoJustBailedOnUs, TXT_WOL_THEGAMEHOST ); // Will cause immediate exit. + + // If I'm not already listed with a color, give myself a color. + // (I may have already received my assigned color from the game host.) + int iItem = pILPlayers->Find( pWO->szMyName ); // (I must be in the list I just got.) + _ASSERTE( iItem != -1 ); + RemapControlType* pColorRemap = pILPlayers->Get_Item_Color( iItem ); + PlayerColorType Color = PlayerColorTypeOf( pColorRemap ); + +// debugprint( "Starting up, I see myself as color %i\n", Color ); + if( Color == PCOLOR_NONE ) + SetPlayerColor( pWO->szMyName, ColorNextAvailable() ); // Unless I'm host, this will be changed quite immediately, + // but nice to have it be a valid value until then. + + DWORD dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + DWORD dwTimeNextPingDisplay = dwTimeNextPlayerPing + 1500; // Stagger ping and ping display periods. + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + BindControls( true ); + + pILPlayers->Set_Tabs(tabs); + + // If we have not already received a param update from a host, set default param values. + if( !bParamsReceived ) + { + if( !bHost ) + // Accept button disabled until first params arrive. + pTextBtnAcceptStart->Disable(); + + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; // Ugh. Use of "Special" global. + if( bHost ) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + //first_time = 0; + } + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + if( bHost ) + { + //------------------------------------------------------------------------ + // Set up array of lists of available scenarios. + //------------------------------------------------------------------------ + for( i = 0; i < Session.Scenarios.Count(); i++ ) + { + // Reworking of the loop previously used for language translation. (What a hack I have inherited...) + MultiMission* pMMission = Session.Scenarios[ i ]; + const char* szScenarioNameShow = pMMission->Description(); + #if defined( GERMAN ) || defined( FRENCH ) + for( int j = 0; EngMisStr[j] != NULL; j++ ) + { + if( !strcmp( szScenarioNameShow, EngMisStr[j] ) ) + { + // Found description in translation array that matches mission. + szScenarioNameShow = EngMisStr[ j + 1 ]; + break; + } + } + // (If no match found, defaults to English description.) + #endif + // Place scenario name in a specific scenario list. + if( pMMission->Get_Official() ) + { + if( Is_Mission_Counterstrike( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as CS\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_CS ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_CS ].Add( (void*)i ); + } + else if( Is_Mission_Aftermath( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as AM\n", szScenarioNameShow ); + // If this is not an Aftermath game channel, we must filter out any AM maps that have + // special AM units on them. + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + !bSpecialAftermathScenario( pMMission->Description() ) ) + { + ar_szScenarios[ SCENARIO_AM ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_AM ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as RA\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_RA ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_RA ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as User\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_USER ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_USER ].Add( (void*)i ); + } + } + + // Set default scenario list viewing mode and prepare tab buttons. + /* switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + ScenarioDisplayMode( SCENARIO_RA ); + break; + case CREATEGAMEINFO::CSGAME: + ScenarioDisplayMode( SCENARIO_CS ); + break; + case CREATEGAMEINFO::AMGAME: + ScenarioDisplayMode( SCENARIO_AM ); + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + Fatal( "Illegal GameInfoCurrent value." ); + } + */ + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + ScenarioDisplayMode( SCENARIO_RA ); // Always start on RedAlert tab. Next line depends on selected item in + // list matching selected scenario. +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + } + + Seed = rand(); + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + pCheckListOptions->Set_Tabs(optiontabs); + pCheckListOptions->Set_Read_Only(0); + + pCheckListOptions->Add_Item(Text_String(TXT_BASES)); + pCheckListOptions->Add_Item(Text_String(TXT_ORE_SPREADS)); + pCheckListOptions->Add_Item(Text_String(TXT_CRATES)); + pCheckListOptions->Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + pCheckListOptions->Add_Item(Text_String(TXT_SHADOW_REGROWS)); + pCheckListOptions->Add_Item(TXT_WOL_SLOWUNITBUILD); + + SetSpecialControlStates(); + + //........................................................................ + // House buttons + //........................................................................ + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + pDropListHouse->Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + pDropListHouse->Set_Selected_Index(Session.House - HOUSE_USSR); + pDropListHouse->Set_Read_Only (true); + + + bInformParamChange = false; + +// PlayingAgainstVersion = VerNum.Version_Number(); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + +// Load_Title_Page(true); +// CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // +// pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); +// pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + Sound_Effect( WOLSOUND_ENTERGAME ); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (bProcess) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Check for change of house. Occurs on first loop and when user changes house. + if( HousePrevious != Session.House ) + { + if( bHost ) + { + // Host changed house. + // Do processing as if we'd received a message from a guest. + + // Set house in our own list. + SetPlayerHouse( pWO->szMyName, Session.House ); + // Tell guests. + nHostLastParamID++; + InformAboutPlayerHouse( pWO->szMyName, Session.House, NULL ); + HousePrevious = Session.House; + ClearAllAccepts(); + } + else + { + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else we have not received the user list yet and don't know who the host is. + // We'll keep trying this until we get a host - HousePrevious keeps us triggering until then. + { +// debugprint( "Session.House changed.\n" ); + // Tell host we changed our house. + char szSend[ 20 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQHOUSE, Session.House ); + pWO->SendGameOpt( szSend, pUserHost ); + // Set house in our own list. This is fine because we know that the change must be affirmed by the host. + SetPlayerHouse( pWO->szMyName, Session.House ); + HousePrevious = Session.House; + } + } + } + + // Regularly ping other players to assess latencies. + // Display of ping results is on a parallel timer, slightly behind the ping so that we'll be likely to have + // a fresh average to show. Not too big a deal if OnPings haven't arrived by then (though they should have). + if( ::timeGetTime() > dwTimeNextPlayerPing ) + { + pWO->RequestPlayerPings(); + dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + if( ::timeGetTime() > dwTimeNextPingDisplay ) + { + pWO->ListChannelUsers(); + dwTimeNextPingDisplay = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + + if( pWO->bShowRankUpdated ) + { + pWO->bShowRankUpdated = false; + pWO->ListChannelUsers(); + } + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + // Special post-callback processing... + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of the channel. + pWO->OnExitingGameChannel(); + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + // Leave the bGotKickedTrigger flag so we can react to it upon reentering the chat dialog. + //pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + if( bHostSayGo ) + { +// debugprint( "bHostSayGo trigger\n" ); + // We are the host, now ready to tell everyone to GO and start our game. + HostSaysGo(); +/* Part of old method of game start. + bProcess = false; + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; +// debugprint( "Host about to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; +*/ + } + else if( *szNameOfHostWhoJustBailedOnUs ) // Host left channel - cancel setup. + { +// debugprint( "Guest about to exit game setup dialog because host bailed on us.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_HOSTLEFT; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddHostLeftMessageToSavedChat( szNameOfHostWhoJustBailedOnUs ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( bLeaveDueToRulesMismatchTrigger ) + { +// debugprint( "Guest about to exit game setup dialog because of rules.ini mismatch.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_RULESMISMATCH; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddMessageToSavedChat( TXT_WOL_RULESMISMATCH ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( *szTriggerGameStartInfo ) + { +// debugprint( "About to trigger game start.\n" ); + TriggerGameStart( szTriggerGameStartInfo ); + } + else if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; // As if the user logged himself out. + } + + if( bExitForGameTrigger ) + { + // We are now exiting to go into a game. + bProcess = false; + if( bHost ) + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; + else + ResultReturn = RESULT_WOLGSUP_STARTGAME; +// debugprint( "About to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; + } + + if( bHost && bWaitingToStart && !bExitForGameTrigger && timeGetTime() > timeWaitingToStartTimeout ) + { + ClearAllAccepts(); // Results in all required steps to cancel game start. + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTTIMEOUT, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + + // Regularly send game param changes if I'm the host. + if( bHost && !bHostSayGo && !bExitForGameTrigger && ::timeGetTime() > dwTimeNextParamRefresh ) + { + if( bParamsUnfresh() ) + { + nHostLastParamID++; + SendParams(); + ClearAllAccepts(); + } + dwTimeNextParamRefresh = ::timeGetTime() + PARAMREFRESHWAIT; + } + + if( !bProcess ) // Avoid redrawing if we're about to bail. + break; + + // + // If we have just received input focus again after running in the background then + // we need to redraw. + // + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + + if( bRetractHouseDropDown ) //|| pCheckListOptions->Is_To_Redraw() ) + { + bRetractHouseDropDown = false; + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + } +// if( pILDisc->Is_To_Redraw() ) +// { +// pDropListHouse->Flag_To_Redraw(); +// } + + if( display ) + { + Hide_Mouse(); + + /* + ** Collapse the country list if we are going to redraw the game list + */ +// if (pILScens->Is_To_Redraw() && pDropListHouse->IsDropped) { +// pDropListHouse->Collapse(); +// if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; +// } + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( pDropListHouse->IsDropped ) + pDropListHouse->Collapse(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + if( bHost ) + Fancy_Text_Print( TXT_SCENARIOS, d_scenariolist_x + d_scenariolist_w, d_scenariolist_y - 12, scheme, TBLACK, TPF_TYPE | TPF_RIGHT ); +// else +// Fancy_Text_Print( TXT_SCENARIO_COLON, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_SIDE_COLON, +// d_house_x + (d_house_w / 2), + d_house_x + ( ( d_house_w + 16 ) / 2 ), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COLOR_COLON, + d_color_x + d_color_w * 4, + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + const char* szGameKind; + const char* pDIB; + switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + szGameKind = TXT_WOL_CG_RAGAME; + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + break; + case CREATEGAMEINFO::CSGAME: + szGameKind = TXT_WOL_CG_CSGAME; + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + break; + case CREATEGAMEINFO::AMGAME: + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + //Fatal( "Illegal GameInfoCurrent value." ); + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = NULL; + pWO->bSelfDestruct = true; + break; + } + int iGameInfoSpacingY = 14; + int iGameInfoSecondColumnX = 0; //170; + // Game kind. + Fancy_Text_Print( szGameKind, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + // Game kind icon. + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + // "Tournament." + if( pWO->GameInfoCurrent.bTournament ) + { + Fancy_Text_Print( TXT_WOL_CG_TOURNAMENT, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_TOURNAMENT ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + } + // "Password: ..." + if( pWO->GameInfoCurrent.bPrivate ) + { + char szPrivatePassword[ 100 ]; + sprintf( szPrivatePassword, TXT_WOL_PRIVATEPASSWORD, pWO->GameInfoCurrent.szPassword ); + Fancy_Text_Print( szPrivatePassword, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 2, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_PRIVATE ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 2 - 2, 100, WINDOW_MAIN ); + } + // "Scenario:" - scenario name is drawn separately. + //Fancy_Text_Print( TXT_SCENARIO_COLON, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY, scheme, TBLACK, TPF_TYPE ); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS ) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { +// Draw_Box(d_disc_x, d_disc_y, d_disc_w, d_disc_h, BOXSTYLE_BOX, true); +// Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); +// Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) + { + char txt[80]; + + char* szScenarioDesc; + bool bOfficial; + char* szScenarioFileName; + if( !bHost ) + { + szScenarioDesc = Session.Options.ScenarioDescription; + bOfficial = Session.ScenarioIsOfficial; + szScenarioFileName = Session.ScenarioFileName; + } + else + { + szScenarioDesc = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description(); + bOfficial = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + szScenarioFileName = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename(); + } + + if( *szScenarioDesc ) + { + // Language translation. + for (int ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp( szScenarioDesc, EngMisStr[ii]) ) { + #if defined(GERMAN) || defined(FRENCH) + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), EngMisStr[ii+1]); + sprintf(txt, "%s", EngMisStr[ii+1]); + #else + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Session.Options.ScenarioDescription); + sprintf(txt, "%s", szScenarioDesc); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s", szScenarioDesc); + } +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + + // Show icon for gamekind of scenario. + const char* pDIB; + + if( bOfficial ) + { + if( Is_Mission_Counterstrike( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + else if( Is_Mission_Aftermath( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + else + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + } + else + pDIB = pWO->DibIconInfos[ DIBICON_USER ].pDIB; + + DrawScenarioDescripIcon( pDIB ); + } + else + { + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Text_String(TXT_NOT_FOUND)); + sprintf(txt, "%s", TXT_WOL_SCENARIONAMEWAIT ); +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + } + + // Print scenario name. + Conquer_Clip_Text_Print( szScenarioNameDisplay, d_gamekind_x, d_gamekind_y, scheme, TBLACK, TPF_TYPE, d_gamekind_w ); +// pStaticDescrip->Draw_Me(); + + sprintf(txt,"%d",Session.Options.UnitCount); + pStaticUnit->Set_Text(txt); + pStaticUnit->Draw_Me(); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + pStaticLevel->Set_Text(txt); + pStaticLevel->Draw_Me(); + + sprintf(txt,"%d",Session.Options.Credits); + pStaticCredits->Set_Text(txt); + pStaticCredits->Draw_Me(); + + sprintf(txt,"%d",Session.Options.AIPlayers); + pStaticAIPlayers->Set_Text(txt); + pStaticAIPlayers->Draw_Me(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + if( bHackFocus ) + { + pEditSend->Set_Focus(); + pEditSend->Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + // Yummy special hack. Luv' that UI system. + if( input == 2049 ) // Left mouse released, not on control that captured it. + { + // Redraw the tabs in case they were what were pressed down on. + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); + } + + //..................................................................... + // Process input + //..................................................................... + switch( input ) + { + case KN_LMOUSE: + if( !bWaitingToStart ) + { + // Check for mouse down on a control when player is not host. + if( !bHost ) + { + if ( ( + Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h + ) || ( + Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h + ) || ( + Get_Mouse_X() >= d_scenariolist_x && + Get_Mouse_X() <= d_scenariolist_x + d_scenariolist_w && + Get_Mouse_Y() >= d_scenariolist_y && + Get_Mouse_Y() <= d_scenariolist_y + d_scenariolist_h + ) ) + { + //Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + WOL_PrintMessage( *pILDisc, Text_String( TXT_ONLY_HOST_CAN_MODIFY ), WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) + { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + + // Ensure that no one is using this color (to our knowledge). + if( pILPlayers->FindColor( &ColorRemaps[ Session.PrefColor == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Session.PrefColor ] ) == -1 ) + { + // Show me as the new color. +// debugprint( "Color box pressed - " ); + SetPlayerColor( pWO->szMyName, Session.PrefColor ); + if( bHost ) + { + // Tell all guests about the color change. + InformAboutPlayerColor( pWO->szMyName, Session.PrefColor, NULL ); + } + else + { + RequestPlayerColor( Session.PrefColor ); + } + } + + } + } + break; + + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { +// debugprint( "Logging out from gsup.\n" ); + ExitGameChannel(); + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): // Always disabled. + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( pILPlayers ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( pILPlayers, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( pILPlayers, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HOUSE | KN_BUTTON ): + Session.House = (HousesType)( pDropListHouse->Current_Index() + HOUSE_USSR ); +/* + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( pDropListHouse->Current_Item(), "Russia" ) == 0 ) + Session.House = HOUSE_USSR; + else + { + Session.House = HouseTypeClass::From_Name( pDropListHouse->Current_Item() ); // Fails on "Russia". (Thinks "USSR".) + if( Session.House == HOUSE_NONE ) + { +// debugprint( "Couldn't find house from selected '%s'.\n", pDropListHouse->Current_Item() ); + } + } +*/ + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + else + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; // Droplist already got contracted. + + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_AFTERMATHUNITS | KN_BUTTON ): + bAftermathUnits = pCheckAftermathUnits->IsOn; + break; + + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, false ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, true ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + //.................................................................. + // New Scenario selected. + //.................................................................. + case (BUTTON_SCENARIOLIST | KN_BUTTON): + { + if( pILScens->Count() ) + { + int iSelectedScenIndex = (int)pILScens->Get_Item_ExtraDataPtr( pILScens->Current_Index() ); + if( iSelectedScenIndex != Session.Options.ScenarioIndex ) + { + Session.Options.ScenarioIndex = iSelectedScenIndex; + bInformParamChange = true; + if( !pILScens->SetSelectType( 1 ) ) // Hack to deal with ListClass "highlighting nothing" problem. + // SelectType was 0 before call. + pILScens->Flag_To_Redraw(); +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + //if (display < REDRAW_PARMS) display = REDRAW_PARMS; + display = REDRAW_ALL; + Sound_Effect(VOC_OPTIONS_CHANGED); + } + } + break; + } + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = pGaugeCount->Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = pGaugeLevel->Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = pGaugeCredits->Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = pGaugeAIPlayers->Get_Value(); +// if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + if (Session.Options.AIPlayers + pWO->GameInfoCurrent.iPlayerMax > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax; + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + } + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case ( BUTTON_PARAMS | KN_BUTTON ): + bRetractHouseDropDown = true; + if (Special.IsCaptureTheFlag != pCheckListOptions->Is_Checked(3) && !Special.IsCaptureTheFlag) { + pCheckListOptions->Check_Item(0, true); + } + if (Session.Options.Bases != pCheckListOptions->Is_Checked(0)) { + Session.Options.Bases = pCheckListOptions->Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + pCheckListOptions->Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + pGaugeCount->Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = pCheckListOptions->Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = pCheckListOptions->Is_Checked(2); + Special.IsCaptureTheFlag = pCheckListOptions->Is_Checked(3); // Ugh. Use of "Special" global. + Special.IsShadowGrow = pCheckListOptions->Is_Checked(4); // Ugh. Use of "Special" global. + + bSlowUnitBuildRate = pCheckListOptions->Is_Checked(5); + + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_ACCEPTSTART | KN_BUTTON ): // 'Accept' or 'Start Game' button. + if( !bHost ) + { + // Guest wishes to accept game params. + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else it's too early to even be thinking about accepting, anyway. + { + // Set ourself as accepted. We want to do this immediately so the user has feedback for pressing button. + // Besides, the host is guaranteed to allow this and set us to "accepted", unless it is simultaneously + // sending us a new update we haven't received yet. If this is the case, we will get that update in a + // second and will set ourselves to unaccepted, which will match the unaccepted state on the server. + // Upshot - We can ignore messages from the server telling us that we, ourself, accepted. + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Disable(); + if( SetPlayerAccepted( pWO->szMyName, true ) ) // Else we didn't find ourself in list! + { +// debugprint( "Sending accept.\n" ); + // Tell host we accept. + char szSend[ 20 ]; + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQACCEPT, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + } + } + } + else + { + // Host says start the game. + // If we have changes just made and not yet sent, don't say start, because we're about to send changes + // that will unaccept everyone. + if( !bParamsUnfresh() ) + { + // Force user to put the correct disk in before proceeding. (Not crucial, but can lead to ugly + // timeouts if the scenario has to be downloaded before game start.) + if( !Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() || + Force_Scenario_Available( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ) ) + { + // Go into "waiting to start" mode, tell guests to, and wait for responses. + bWaitingToStart = true; + timeWaitingToStartTimeout = ::timeGetTime() + 30000; + nHostLastParamID++; + InformAboutStart(); + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + SetPlayerReadyToGo( pWO->szMyName, "ready" ); + Sound_Effect( VOC_GAME_CLOSED ); + } + } + } + break; + + case ( BUTTON_SCENARIO_RA | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_RA ); + break; + case ( BUTTON_SCENARIO_CS | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_CS ); + break; + case ( BUTTON_SCENARIO_AM | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_AM ); + break; + case ( BUTTON_SCENARIO_USER | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_USER ); + break; + +// case (BUTTON_LOAD | KN_BUTTON): +// case (BUTTON_OK | KN_BUTTON): +// break; + + case (KN_ESC): + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + case ( BUTTON_CANCEL | KN_BUTTON ): + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + + Call_Back(); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + pWO->ClearListPtrs(); + pWO->pGSupDlg = NULL; + + return ResultReturn; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetSpecialControlStates() +{ + // Set gauges and checklist. + + pCheckListOptions->Check_Item(0, Session.Options.Bases); + pCheckListOptions->Check_Item(1, Session.Options.Tiberium); + pCheckListOptions->Check_Item(2, Session.Options.Goodies); + pCheckListOptions->Check_Item(3, Special.IsCaptureTheFlag); // Ugh. Use of "Special" global. + pCheckListOptions->Check_Item(4, Special.IsShadowGrow); // Ugh. Use of "Special" global. + + pCheckListOptions->Check_Item(5, bSlowUnitBuildRate); // Ugh. Use of "Special" global. + + pGaugeCount->Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + pGaugeLevel->Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + pGaugeLevel->Set_Value(BuildLevel - 1); + + pGaugeCredits->Set_Maximum(Rule.MPMaxMoney); + pGaugeCredits->Set_Value(Session.Options.Credits); + + if( pWO->GameInfoCurrent.bTournament ) + { + pGaugeAIPlayers->Set_Maximum( 0 ); + } + else + { + // Note dependency of AIPlayers on number of human players. + // pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + pGaugeAIPlayers->Set_Maximum( Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax ); + } + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + if( bAftermathUnits ) + pCheckAftermathUnits->Turn_On(); + else + pCheckAftermathUnits->Turn_Off(); +} + +#define DRAWTABDOWN Turn_On() +#define DRAWTABUP Turn_Off() + +//*********************************************************************************************** +void WOL_GameSetupDialog::BindControls( bool bBind ) +{ + if( bBind ) + { + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + pILPlayers->Add_Tail(*commands); + if( bHost ) + { + // Draw order of tabs depends on which one is selected, as we want them to overlap appropriately. + // Also, the selected tab must appear over the scenario list, while the unselected tabs are below it. + // This assures that that bottom of the tab overlaps the scenario list border, making it look connected. + + // ajw I could make all maps always available now that they're downloadable. Playing CS map with AM rules + // would mean switching CDs, though. + // Would mean no difference between RA and CS games. + + switch( ScenKindCurrent ) + { + case SCENARIO_RA: + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABUP; + break; + case SCENARIO_CS: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::CSGAME. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABUP; + break; + case SCENARIO_AM: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::AMGAME, or RAGAME with AM installed. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABUP; + break; + case SCENARIO_USER: // pWO->GameInfoCurrent.bTournament must be false. + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABUP; + break; + } + } +// pStaticDescrip->Add_Tail(*commands); + pEditSend->Add_Tail(*commands); + pStaticUnit->Add_Tail(*commands); + pStaticLevel->Add_Tail(*commands); + pStaticCredits->Add_Tail(*commands); + pStaticAIPlayers->Add_Tail(*commands); + pGaugeCount->Add_Tail(*commands); + pGaugeCredits->Add_Tail(*commands); + pGaugeAIPlayers->Add_Tail(*commands); + pGaugeLevel->Add_Tail(*commands); + pCheckListOptions->Add_Tail(*commands); + pTextBtnCancel->Add_Tail(*commands); + pTextBtnAcceptStart->Add_Tail(*commands); + pTextBtnAction->Add_Tail(*commands); + pILDisc->Add_Tail(*commands); + pDropListHouse->Add_Tail(*commands); +// pCheckAftermathUnits->Add_Tail(*commands); + } + else + { + pWO->pShpBtnDiscon->Zap(); + pWO->pShpBtnLeave->Zap(); + pWO->pShpBtnRefresh->Zap(); + pWO->pShpBtnSquelch->Zap(); + pWO->pShpBtnBan->Zap(); + pWO->pShpBtnKick->Zap(); + pWO->pShpBtnFindpage->Zap(); + pWO->pShpBtnOptions->Zap(); + pWO->pShpBtnLadder->Zap(); + pWO->pShpBtnHelp->Zap(); + pILPlayers->Zap(); + if( bHost ) + { + pShpBtnScenarioRA->Zap(); + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Zap(); + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + pShpBtnScenarioCS->Zap(); + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + pShpBtnScenarioAM->Zap(); + pILScens->Zap(); + } +// pStaticDescrip->Zap(); + pEditSend->Zap(); + pStaticUnit->Zap(); + pStaticLevel->Zap(); + pStaticCredits->Zap(); + pStaticAIPlayers->Zap(); + pGaugeCount->Zap(); + pGaugeCredits->Zap(); + pGaugeAIPlayers->Zap(); + pGaugeLevel->Zap(); + pCheckListOptions->Zap(); + pTextBtnCancel->Zap(); + pTextBtnAcceptStart->Zap(); + pTextBtnAction->Zap(); + pILDisc->Zap(); + pDropListHouse->Zap(); +// pCheckAftermathUnits->Zap(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ) +{ +// debugprint( "ScenarioDisplayMode, from %i going into %i mode.\n", ScenKindCurrent, ScenKind ); + + // Puts us into mode where we are viewing a particular gamekind of scenario list. + if( ScenKindCurrent == ScenKind ) + return; + + // Reorder tab buttons. + BindControls( false ); + ScenKindCurrent = ScenKind; + BindControls( true ); + + // Reset list items. + pILScens->Clear(); +// debugprint( " * * * * * * ScenKind %i has %i items.\n", ScenKind, ar_szScenarios[ ScenKind ].Count() ); + // Check for the currently selected scenario. + bool bFoundCurrentSelection = false; + int iSelect; + for( int i = 0; i != ar_szScenarios[ ScenKind ].Count(); i++ ) + { + // Put ScenarioIndex in as extradata to list item. + int iScenIndex = (int)ar_szScenIndexes[ ScenKind ][ i ]; + int iIndexNew = pILScens->Add_Item( ar_szScenarios[ ScenKind ][ i ], NULL, NULL, ICON_DIB, NULL, (void*)iScenIndex ); + if( iScenIndex == Session.Options.ScenarioIndex && !bFoundCurrentSelection ) + { + // (Choose first line of what can be multiline description of currently selected scenario.) + bFoundCurrentSelection = true; + iSelect = i; + } + } + // If the current scenario selection is in this list, enable the list to show the selection, otherwise, + // make the listclass selection invisible, because it doesn't indicate the real scenario selection. + // Basically, problem is that I can't have no selection in ListClass, and I don't want to risk changing + // it as I'll affect the rest of the code. So I do this horrible hack. + if( bFoundCurrentSelection ) + { + pILScens->SetSelectType( 1 ); // Regular selection. + pILScens->Set_Selected_Index( iSelect ); + } + else + pILScens->SetSelectType( 0 ); // Invisible selection. + +// display = REDRAW_ALL; + pILScens->Flag_To_Redraw(); + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::ExitGameChannel() +{ +// debugprint( "ExitGameChannel \n" ); + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingGameChannel(); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::DrawScenarioDescripIcon( const char* pDIB ) const +{ + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - 2, 100, WINDOW_MAIN ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerColor( const char* szName, PlayerColorType Color ) +{ + // Sets player color - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerColor %s to %i\n", szName, Color ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player color Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - color will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerColor could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + if( strcmp( pWO->szMyName, szName ) == 0 ) + { + // I am the player involved. + Session.ColorIdx = Color; + if( display < REDRAW_COLORS ) display = REDRAW_COLORS; + } + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ Color == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Color ] ); + pILPlayers->Flag_To_Redraw(); + +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::GetPlayerColor( const char* szName ) +{ + // Returns player color, if player is found in list, else PCOLOR_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return PCOLOR_NONE; + } + RemapControlType* pRemap = pILPlayers->Get_Item_Color( iItem ); + return PlayerColorTypeOf( pRemap ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerHouse( const char* szName, HousesType House ) +{ + // Sets player house - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerHouse %s to %i\n", szName, House ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player house Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - house will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerHouse could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + // Reset item text. + char szItem[ 100 ]; + pWO->WritePlayerListItem( szItem, szName, House ); +// debugprint ( "%i, %s\n", iItem, szItem ); + pILPlayers->Set_Item( iItem, szItem ); + pILPlayers->Flag_To_Redraw(); +} + +//*********************************************************************************************** +HousesType WOL_GameSetupDialog::GetPlayerHouse( const char* szName ) +{ + // Returns player house for user if found in list, else HOUSE_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return HOUSE_NONE; + } + + return pWO->PullPlayerHouse_From( pILPlayers->Get_Item( iItem ) ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerAccepted( const char* szName, bool bAccepted ) +{ + // Sets player's 'accepted' state to true or false. + // Value is stored in the player list: if there is an accepted icon, player has accepted. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerAccepted() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerAccepted() - set '%s' to %s.\n", szName, bAccepted ? "accepted" : "NOT accepted" ); + return pWO->MarkItemAccepted( iItem, bAccepted ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::IveAccepted() +{ + // Returns true if I am marked as "accepted". + int iItem = pILPlayers->Find( pWO->szMyName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedAccepted( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerReadyToGo( const char* szName, const char* szReadyState ) +{ + // Sets player's 'ready to go' state. + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + // Value is stored in the player list in the hidden string field. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerReadyToGo() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerReadyToGo() - set '%s' to %s.\n", szName, szReadyState ); + pWO->MarkItemReadyToGo( iItem, szReadyState ); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ResetReadyToGo() +{ + // Resets all users to "not ready to go". + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + pWO->MarkItemReadyToGo( i, NULL ); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bPlayerReadyToGo( const char* szName ) +{ + // Returns true if player is marked as "ready to go". + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedReadyToGo( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllPlayersReadyToGo() +{ + // Returns true if all players are marked as "ready to go". +// debugprint( "Checking for all players ready - there are %i\n", pILPlayers->Count() ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedReadyToGo( i ) ) + { +// debugprint( "Item %i NOT ready\n", i ); + return false; + } +// debugprint( "Item %i is ready\n", i ); + } +// debugprint( "Return true\n" ); + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessGuestRequest( User* pUser, const char* szRequest ) +{ + // Game host processes a request that arrived as a privategameopt from one of the guests. + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 null-terminator +// debugprint( "ProcessGuestRequest. szRequest is '%s', len %i.\n", szRequest, strlen( szRequest ) ); + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szRequest ); + szRequest += 3; + + switch( opt ) + { + case WOL_GAMEOPT_REQCOLOR: + { + PlayerColorType ColorDesired = (PlayerColorType)( atoi( szRequest ) ); + if( pILPlayers->FindColor( &ColorRemaps[ ColorDesired == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : ColorDesired ] ) == -1 ) + { + // Color is available. + SetPlayerColor( (char*)pUser->name, ColorDesired ); + // Tell all guests about the color change. + InformAboutPlayerColor( (char*)pUser->name, ColorDesired, NULL ); + } + else + { + // Color is not available. +// debugprint( "Color %i denied to %s\n", ColorDesired, (char*)pUser->name ); + // Tell requestor that his color is still the same. + RemapControlType* pColorRemapCurrent = pILPlayers->Get_Item_Color( pILPlayers->Find( (char*)pUser->name ) ); + InformAboutPlayerColor( (char*)pUser->name, PlayerColorTypeOf( pColorRemapCurrent ), pUser ); + } + break; + } + case WOL_GAMEOPT_REQHOUSE: + { + HousesType HouseChoice = (HousesType)( atoi( szRequest ) ); +// debugprint( "Host received: '%s' changed house to %u.\n", (char*)pUser->name, HouseChoice ); + SetPlayerHouse( (char*)pUser->name, HouseChoice ); + nHostLastParamID++; + InformAboutPlayerHouse( (char*)pUser->name, HouseChoice, NULL ); + ClearAllAccepts(); + break; + } + case WOL_GAMEOPT_REQACCEPT: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) + { +// debugprint( "Host received valid accept from '%s'.\n", (char*)pUser->name ); + SetPlayerAccepted( (char*)pUser->name, true ); + InformAboutPlayerAccept( (char*)pUser->name, NULL ); + // We may be ready to start a game now. + if( bAllGuestsAccept() ) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Enable(); + } + } + else + { +// debugprint( "______________Host received invalid accept from '%s'. ID = %i when it should be %i.\n", (char*)pUser->name, +// atoi( szRequest ), nHostLastParamID ); + } + break; + case WOL_GAMEOPT_REQSTART: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART from '%s'.\n", (char*)pUser->name ); +// WOL_PrintMessage( *pILDisc, "WOL_GAMEOPT_REQSTART response", WOLCOLORREMAP_LOCALMACHINEMESS ); +// WOL_PrintMessage( *pILDisc, (char*)pUser->name, WOLCOLORREMAP_LOCALMACHINEMESS ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "ready" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO from '%s'.\n", (char*)pUser->name ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "need scenario" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_INFGO: + // I have told myself to start game right now. + // This is the new method. Avoids apparent "private messages/gameopts are getting delayed" problem in chatserver... + // (I don't want to end up leaving the channel before guests get my go message.) + strcpy( szTriggerGameStartInfo, szRequest ); + break; + default: +// debugprint( "Unhandled value of %i in ProcessGuestRequest!!!\n", opt ); + break; + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessInform( char* szInform ) +{ + // Process inform message arriving from game host. +// debugprint( "ProcessInform: '%s'\n", szInform ); + if( !bHost ) + { + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szInform ); + szInform += 3; + switch( opt ) + { + case WOL_GAMEOPT_INFCOLOR: + { + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + PlayerColorType Color = (PlayerColorType)( atoi( szInform ) ); + szInform += 3; + SetPlayerColor( szInform, Color ); // (szInform is now sitting at the start of the name string.) + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFHOUSE: // Note: In theory, I could ignore this if it refers to me. I've already set my own house. + { + nGuestLastParamID = atoi( szInform ); + szInform += 7; + HousesType House = (HousesType)( atoi( szInform ) ); + szInform += 3; + SetPlayerHouse( szInform, House ); // (szInform is now sitting at the start of the name string.) + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFACCEPT: // Note: In theory, I could ignore this if it refers to me. + // A guest has accepted. + SetPlayerAccepted( szInform, true ); // (szInform is now sitting at the start of the name string.) + break; + case WOL_GAMEOPT_INFPARAMS: + // Game params have changed. + bParamsReceived = true; + if( !AcceptParams( szInform ) ) + bLeaveDueToRulesMismatchTrigger = true; + SetSpecialControlStates(); + //pILScens->Set_Selected_Index( Session.Options.ScenarioIndex ); + display = REDRAW_ALL; + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFNEWGUESTPLAYERINFO: + // I have just joined and have received a message with info on all players in game. + AcceptNewGuestPlayerInfo( szInform ); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFSTART: + { + // Host tells us to wait for start of game. +// debugprint( "Guest received WOL_GAMEOPT_INFSTART.\n" ); + nGuestLastParamID = atoi( szInform ); +// The following check is not necessary. Rules.ini, if manually replaced by a cheater, is not reloaded. +// So prior checks (that occur on game params receives) are sufficient. +// szInform += 7; +// // Check rules.ini compatibility. +// int iRulesID = atoi( szInform ); +// if( RuleINI.Get_Unique_ID() != iRulesID ) +// { +// // Rules.ini incompatible. Don't respond to call for start. +// bLeaveDueToRulesMismatchTrigger = true; +// break; +// } + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // This better'd be true. + { + // Send ack back to host. + char szSend[ 20 ]; + // Make sure we have the scenario. + if( !bNeedScenarioDownload() ) + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART, nGuestLastParamID ); + else + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + // Enter waiting mode. + bWaitingToStart = true; + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. + // Set global that will force edit dialogs to stop accepting characters. + // This is to fix a minor glitch: guests can keep typing into a "page" dialog editbox after the + // "Launching game..." message has appeared on top of it. + if( pWO->bPump_In_Call_Back ) + disable_current_msgbox = true; + } + else + { +// debugprint( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + Fatal( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + } + Sound_Effect( VOC_GAME_CLOSED ); + break; + } + case WOL_GAMEOPT_INFCANCELSTART: + // Host tells us to cancel waiting for start of game. + bWaitingToStart = false; + ClearAllAccepts(); + BindControls( true ); + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTCANCELLED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( VOC_SYS_ERROR ); + display = REDRAW_ALL; + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + case WOL_GAMEOPT_INFGO: + // Host says start game right now. + strcpy( szTriggerGameStartInfo, szInform ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + default: +// debugprint( "Unhandled value of %i in ProcessInform!!!\n", opt ); + //WOL_PrintMessage( *pILDisc, "Error - Unhandled value in ProcessInform!!!", WOLCOLORREMAP_LOCALMACHINEMESS ); + Fatal( "Error - Unhandled value in ProcessInform!" ); + break; + } + } +// debugprint( "* END of ProcessInform: '%s'\n", szInform ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bParamsUnfresh() +{ + // Returns true if game setup parameters do not match what they were last time they were sent. + GAMEPARAMS GParamsNow; + SetGParamsToCurrent( GParamsNow ); + +// if( !( GParamsNow == GParamsLastSent ) ) +// { +// debugprint( "---- Params Unfresh: OLD...\n" ); +// Debug_GlobalPacketType( GParamsLastSent.GPacket ); +// debugprint( "------------------- NEW...\n" ); +// Debug_GlobalPacketType( GParamsNow.GPacket ); +// debugprint( "old bAftermathUnits = %i\n", GParamsLastSent.bAftermathUnits ); +// debugprint( "new bAftermathUnits = %i\n", GParamsNow.bAftermathUnits ); +// } + + return !( GParamsNow == GParamsLastSent ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SendParams() +{ + // Host only. + + SetGParamsToCurrent( GParamsLastSent ); + + char szSend[ 130 + DESCRIP_MAX + 12 + 32 + 100 ]; + sprintf( szSend, + "%01u " + "%06u " + "%03u " + "%s " + "%u " + "%s " + "%01u " + "%.32s " // No null-terminator on digest. There may be nothing at all inserted here. + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + , + WOL_GAMEOPT_INFPARAMS, + nHostLastParamID, + strlen( GParamsLastSent.GPacket.ScenarioInfo.Scenario ), + GParamsLastSent.GPacket.ScenarioInfo.Scenario, + GParamsLastSent.GPacket.ScenarioInfo.FileLength, + GParamsLastSent.GPacket.ScenarioInfo.ShortFileName, +// strlen( (char*)GParamsLastSent.GPacket.ScenarioInfo.FileDigest ), not null-terminated! + GParamsLastSent.GPacket.ScenarioInfo.FileDigest[0] ? 1 : 0, + GParamsLastSent.GPacket.ScenarioInfo.FileDigest, + GParamsLastSent.GPacket.ScenarioInfo.OfficialScenario, + GParamsLastSent.GPacket.ScenarioInfo.Credits, + GParamsLastSent.GPacket.ScenarioInfo.IsBases, + GParamsLastSent.GPacket.ScenarioInfo.IsTiberium, + GParamsLastSent.GPacket.ScenarioInfo.IsGoodies, + GParamsLastSent.GPacket.ScenarioInfo.BuildLevel, + GParamsLastSent.GPacket.ScenarioInfo.UnitCount, + GParamsLastSent.GPacket.ScenarioInfo.AIPlayers, + GParamsLastSent.GPacket.ScenarioInfo.Seed, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsShadowGrow, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsSpeedBuild, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsFromInstall, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsCaptureTheFlag, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsInert, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsThreePoint, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTGrowth, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTSpread, + GParamsLastSent.GPacket.ScenarioInfo.GameSpeed, + GParamsLastSent.GPacket.ScenarioInfo.Version, + GParamsLastSent.bAftermathUnits, // Not currently used. + GParamsLastSent.bSlowUnitBuildRate, + RuleINI.Get_Unique_ID() // Used to verify rules.ini files match. + ); + + pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::AcceptParams( char* szParams ) +{ + // Reverse of SendParams() process. szParams has already been stripped of 2 bytes header. Guest only. + + // Returns false if rules.ini doesn't match that of the host. + // (Or if an error occurs due to the packet being incorrect - which happened once in test...) + + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szParams, szDelimiter ); + nGuestLastParamID = atoi( szToken ); + + // Read in length of following string. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iLen = atoi( szToken ); + // Set string pointer to start of string (length is 3 digits). + szRemaining = szToken + 4; + // Read in string. + memcpy( Session.Options.ScenarioDescription, szRemaining, iLen ); + // Null-terminate. + Session.Options.ScenarioDescription[ iLen ] = 0; + // Advance string pointer to next param. + szRemaining += iLen + 1; + +//debugprint( "scenario description is '%s'\n", Session.Options.ScenarioDescription ); +//debugprint( "remaining: '%s'\n", szRemaining ); + + szToken = strtok( szRemaining, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioFileLength = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + strcpy( Session.ScenarioFileName, szToken ); + +// // Read in length of following string. +// szToken = strtok( NULL, szDelimiter ); +// iLen = atoi( szToken ); +// // Set string pointer to start of string (length is 3 digits). +// szRemaining = szToken + 4; + // Method changed. + // Check if there is a digest. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + iLen = atoi( szToken ); // 1 or 0, indicating if there is a digest following. + if( iLen ) + { +// // Set string pointer to start of string (previous field is 1 digit). +// szRemaining = szToken + 2; +// iLen = sizeof( Session.ScenarioDigest ); +// // Read in string. +// memcpy( Session.ScenarioDigest, szRemaining, iLen ); +// // // Null-terminate. +// // Session.ScenarioDigest[ iLen ] = 0; Digest has no null-terminator! +// // Advance string pointer to next param. +// szRemaining += iLen + 1; + // There is a digest. + szToken = strtok( NULL, szDelimiter ); // (Digests can't have spaces in the them.) +//debugprint( "digest: '%s'\n", szToken ); + if( !szToken ) return false; + strncpy( Session.ScenarioDigest, szToken, sizeof( Session.ScenarioDigest ) ); + } + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioIsOfficial = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Credits = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Bases = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Tiberium = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Goodies = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + BuildLevel = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.UnitCount = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.AIPlayers = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Seed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsShadowGrow = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsSpeedBuild = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsFromInstall = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsCaptureTheFlag = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsInert = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsThreePoint = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTGrowth = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTSpread = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Options.GameSpeed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; +// "Version" = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bAftermathUnits = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bSlowUnitBuildRate = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iRulesID = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + return ( RuleINI.Get_Unique_ID() == iRulesID ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetGParamsToCurrent( GAMEPARAMS& GParams ) +{ + // Sets values in a GAMEPARAMS to the current game settings. + + strcpy( GParams.GPacket.ScenarioInfo.Scenario, Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + CCFileClass file( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + GParams.GPacket.ScenarioInfo.FileLength = file.Size(); + strcpy( GParams.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + // Digest is not null-terminated. + strncpy( (char*)GParams.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Digest(), sizeof( GParams.GPacket.ScenarioInfo.FileDigest ) ); + GParams.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + GParams.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + GParams.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + GParams.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + GParams.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + GParams.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GParams.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + GParams.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + GParams.GPacket.ScenarioInfo.Seed = Seed; + GParams.GPacket.ScenarioInfo.Special = Special; + GParams.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + GParams.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + GParams.bAftermathUnits = bAftermathUnits; + GParams.bSlowUnitBuildRate = bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ) +{ + return gp1.GPacket == gp2.GPacket && + gp1.bAftermathUnits == gp2.bAftermathUnits && gp1.bSlowUnitBuildRate == gp2.bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ) +{ + if( strcmp( gp1.ScenarioInfo.Scenario, gp2.ScenarioInfo.Scenario ) != 0 ) return false; + if( strcmp( gp1.ScenarioInfo.ShortFileName, gp2.ScenarioInfo.ShortFileName ) != 0 ) return false; + // Digest is not null-terminated... + if( strncmp( (const char*)gp1.ScenarioInfo.FileDigest, (const char*)gp2.ScenarioInfo.FileDigest, sizeof( gp1.ScenarioInfo.FileDigest ) ) != 0 ) return false; + + if( gp1.ScenarioInfo.FileLength == gp2.ScenarioInfo.FileLength && + gp1.ScenarioInfo.OfficialScenario == gp2.ScenarioInfo.OfficialScenario && + gp1.ScenarioInfo.Credits == gp2.ScenarioInfo.Credits && + gp1.ScenarioInfo.IsBases == gp2.ScenarioInfo.IsBases && + gp1.ScenarioInfo.IsTiberium == gp2.ScenarioInfo.IsTiberium && + gp1.ScenarioInfo.IsGoodies == gp2.ScenarioInfo.IsGoodies && + gp1.ScenarioInfo.BuildLevel == gp2.ScenarioInfo.BuildLevel && + gp1.ScenarioInfo.UnitCount == gp2.ScenarioInfo.UnitCount && + gp1.ScenarioInfo.AIPlayers == gp2.ScenarioInfo.AIPlayers && + gp1.ScenarioInfo.Seed == gp2.ScenarioInfo.Seed && + gp1.ScenarioInfo.Special.IsShadowGrow == gp2.ScenarioInfo.Special.IsShadowGrow && + gp1.ScenarioInfo.Special.IsSpeedBuild == gp2.ScenarioInfo.Special.IsSpeedBuild && + gp1.ScenarioInfo.Special.IsFromInstall == gp2.ScenarioInfo.Special.IsFromInstall && + gp1.ScenarioInfo.Special.IsCaptureTheFlag == gp2.ScenarioInfo.Special.IsCaptureTheFlag && + gp1.ScenarioInfo.Special.IsInert == gp2.ScenarioInfo.Special.IsInert && + gp1.ScenarioInfo.Special.IsThreePoint == gp2.ScenarioInfo.Special.IsThreePoint && + gp1.ScenarioInfo.Special.IsTGrowth == gp2.ScenarioInfo.Special.IsTGrowth && + gp1.ScenarioInfo.Special.IsTSpread == gp2.ScenarioInfo.Special.IsTSpread && + gp1.ScenarioInfo.GameSpeed == gp2.ScenarioInfo.GameSpeed && + gp1.ScenarioInfo.Version == gp2.ScenarioInfo.Version ) + return true; + + return false; +} + +//*********************************************************************************************** +/* +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ) // ajw debugging +{ + if( *gp1.ScenarioInfo.Scenario ) +// debugprint( "Scenario = %s\n", (char*)gp1.ScenarioInfo.Scenario ); + else +// debugprint( "!Scenario string is null\n" ); + if( *gp1.ScenarioInfo.ShortFileName ) +// debugprint( "ShortFileName = %s\n", (char*)gp1.ScenarioInfo.ShortFileName ); + else +// debugprint( "!ShortFileName string is null\n" ); + // Remember ShortFileName is not null-terminated... + if( *gp1.ScenarioInfo.FileDigest ) +// debugprint( "FileDigest = %.32s\n", (char*)gp1.ScenarioInfo.FileDigest ); + else +// debugprint( "!FileDigest string is null\n" ); + +// debugprint( "FileLength = %i\n" + "OfficialScenario = %i\n" + "Credits = %i\n" + "IsBases = %i\n" + "IsTiberium = %i\n" + "IsGoodies = %i\n" + "BuildLevel = %i\n" + "UnitCount = %i\n" + "AIPlayers = %i\n" + "Seed = %i\n" + "Special.IsShadowGrow = %i\n" + "Special.IsSpeedBuild = %i\n" + "Special.IsFromInstall = %i\n" + "Special.IsCaptureTheFlag = %i\n" + "Special.IsInert = %i\n" + "Special.IsThreePoint = %i\n" + "Special.IsTGrowth = %i\n" + "Special.IsTSpread = %i\n" + "GameSpeed = %i\n" + "Version = %i\n", + gp1.ScenarioInfo.FileLength, + gp1.ScenarioInfo.OfficialScenario, + gp1.ScenarioInfo.Credits, + gp1.ScenarioInfo.IsBases, + gp1.ScenarioInfo.IsTiberium, + gp1.ScenarioInfo.IsGoodies, + gp1.ScenarioInfo.BuildLevel, + gp1.ScenarioInfo.UnitCount, + gp1.ScenarioInfo.AIPlayers, + gp1.ScenarioInfo.Seed, + gp1.ScenarioInfo.Special.IsShadowGrow, + gp1.ScenarioInfo.Special.IsSpeedBuild, + gp1.ScenarioInfo.Special.IsFromInstall, + gp1.ScenarioInfo.Special.IsCaptureTheFlag, + gp1.ScenarioInfo.Special.IsInert, + gp1.ScenarioInfo.Special.IsThreePoint, + gp1.ScenarioInfo.Special.IsTGrowth, + gp1.ScenarioInfo.Special.IsTSpread, + gp1.ScenarioInfo.GameSpeed, + gp1.ScenarioInfo.Version ); +} +*/ + +//*********************************************************************************************** +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ) +{ + for( PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++ ) + { + if( &ColorRemaps[ pcolor ] == pColorRemap ) + return pcolor; + } + return PCOLOR_NONE; +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::RequestPlayerColor( PlayerColorType Color ) +{ + // Local player sends a request to the game host asking for a particular color. + char szSend[ 20 ]; + + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQCOLOR, Color ); + + _ASSERTE( pWO->pGameHost() ); + + return pWO->SendGameOpt( szSend, pWO->pGameHost() ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ) +{ + // Game host tells guests about a player color assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 20 + WOL_NAME_LEN_MAX ]; + + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + + if( Color == PCOLOR_NONE ) + { +// debugprint( "Bad Color for %s in InformAboutPlayerColor.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %02u %s", WOL_GAMEOPT_INFCOLOR, Color, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ) +{ + // Game host tells guests about a player house assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 28 + WOL_NAME_LEN_MAX ]; + // WOL_GAMEOPT_INFHOUSE format: + // 2 WOL_GAMEOPT + // 1 space + // 6 param ID + // 1 space + // 2 house + // 1 space + // string name of player + + if( House == HOUSE_NONE ) + { +// debugprint( "Bad House for %s in InformAboutPlayerHouse.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %06u %02u %s", WOL_GAMEOPT_INFHOUSE, nHostLastParamID, (short)House, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerAccept( const char* szName, User* pUserPriv ) +{ + // Game host tells guests about player accepting game params. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 6 + WOL_NAME_LEN_MAX ]; + sprintf( szSend, "%02u %s", WOL_GAMEOPT_INFACCEPT, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. + char szSend[ 10 ]; + + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_INFSTART, nHostLastParamID ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutCancelStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. +// debugprint( "InformAboutCancelStart!\n" ); + + char szSend[ 10 ]; + + sprintf( szSend, "%02u", WOL_GAMEOPT_INFCANCELSTART ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestJoin( User* pUser ) +{ + // A guest (not myself) has entered the game channel. +// debugprint( "OnGuestJoin()\n" ); + char* szPrint = new char[ strlen( TXT_WOL_PLAYERJOINEDGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERJOINEDGAME, (char*)pUser->name ); + WOL_PrintMessage( *pILDisc, szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + + ClearAllAccepts(); + + if( bHost ) + { + // Send the new guest the current setup. Note that nHostLastParamID doesn't change here. + + // Assign color to new guest. + PlayerColorType Color = ColorNextAvailable(); + SetPlayerColor( (char*)pUser->name, Color ); + + // Previously, I was sending an individual color, house, and acceptedstate message for each other guest. + // Though convenient code-wise, this causes the initial info to arrive at the new guest in a very slow + // manner. This is because of the wonderful "anti-flood" feature of the chat server, which prevents a + // series of messages from a client from being passed on faster than a certain rate. + // For this reason, a new message that contains all of the info about all of the other guests has been + // created (WOL_GAMEOPT_INFNEWGUESTPLAYERINFO). + + // WOL_GAMEOPT_INFNEWGUESTPLAYERINFO format (items separated by spaces): + // WOL_GAMEOPT + // number of players + // for each player { + // length of player name string + // player name + // color + // bool - is there a house field following? + // (house) + // // (removed) acceptedness - true for set player accepted, false for do nothing + // } + + // Build up a big WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + char szSend[ 500 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, pILPlayers->Count() ); + // Send color and house of all players (including himself) to the new guest. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + char szSendPiece[ 100 ]; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( i ) ); +// InformAboutPlayerColor( szPlayerName, PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ), pUser ); + sprintf( szSendPiece, " %02u %s %02u", strlen( szPlayerName ), szPlayerName, + PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ) ); + + if( strcmp( szPlayerName, (char*)pUser->name ) != 0 ) + { + HousesType House = pWO->PullPlayerHouse_From( pILPlayers->Get_Item( i ) ); + if( House != HOUSE_NONE ) + { + // InformAboutPlayerHouse( szPlayerName, House, pUser ); + char szSendHouse[ 50 ]; + sprintf( szSendHouse, " 1 %02u", (short)House ); + strcat( szSendPiece, szSendHouse ); + } + else + { + // Player must not have told me what house he is yet. Don't send house value. + strcat( szSendPiece, " 0" ); + } + } + else + { + // Player is the new guest himself. Don't send house value. + strcat( szSendPiece, " 0" ); + } + +// Acceptedness must be false! No need to send. +// // Send "accepted" status of player. Ignore myself, as I'm the host. +// if( strcmp( szPlayerName, pWO->szMyName ) != 0 && pWO->bItemMarkedAccepted( i ) ) +// strcat( szSendPiece, " 1" ); +// else +// strcat( szSendPiece, " 0" ); + + strcat( szSend, szSendPiece ); + } + pWO->SendGameOpt( szSend, pUser ); + + // Send everyone the color of the new guest. + InformAboutPlayerColor( (char*)pUser->name, Color, NULL ); + + // Send game params. + // This is done last because it contains a param ID value, and we need to ensure that the new guest has + // received everything we are sending him here before he tries to send me an accept. + // If game params were sent first, he could theoretically receive them, then send an accept, even though he + // has not received the WOL_GAMEOPT_INFNEWGUESTPLAYERINFO. + // By doing this I avoid having to have a param ID in WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, which would be hard, + // since it is a private message. + // For simplicity, send public. + SendParams(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::AcceptNewGuestPlayerInfo( char* szMsg ) +{ + // Process a received WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + // szMsg has already been stripped of 2 bytes header. + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szMsg, szDelimiter ); + unsigned int nPlayers = atoi( szToken ); + + // We have to assist strtok a bit because of calls below that may also call strtok()... + szRemaining = szMsg + 3; + + for( unsigned int nPlayer = 0; nPlayer != nPlayers; ++nPlayer ) + { + // Read in length of following string. + szToken = strtok( szRemaining, szDelimiter ); + int iLen = atoi( szToken ); + + // Set string pointer to start of string (length is 2 digits). + szRemaining = szToken + 3; + // Read in string. + char szPlayerName[ 50 ]; + memcpy( szPlayerName, szRemaining, iLen ); + // Null-terminate. + szPlayerName[ iLen ] = 0; + + // Advance string pointer to next param. + szRemaining += iLen + 1; + + // Read color. + szToken = strtok( szRemaining, szDelimiter ); + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); + + // SetPlayerColor may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 3; + + // Read whether there is a house field. + szToken = strtok( szRemaining, szDelimiter ); + bool bHouseField = (bool)atoi( szToken ); + + if( bHouseField ) + { + // Read house. + szToken = strtok( NULL, szDelimiter ); + HousesType House = (HousesType)atoi( szToken ); + SetPlayerHouse( szPlayerName, House ); + // SetPlayerHouse may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 5; + } + else + szRemaining += 2; // Advance past "0 ". + +// Acceptedness must be false. No need for it in message. +// // Read acceptedness. +// szToken = strtok( NULL, szDelimiter ); +// bool bAccepted = (bool)atoi( szToken ); +// if( bAccepted ) +// SetPlayerAccepted( szPlayerName, true ); + } + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + ClearAllAccepts(); // Most likely a pointless precaution. +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestLeave( User* pUser ) +{ + // pUser is about to leave but is still in our player list. + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + // Host is leaving the channel. We must be a guest, and so must leave also. This will trigger exit. + strcpy( szNameOfHostWhoJustBailedOnUs, (char*)pUser->name ); + } + else + { + ClearAllAccepts(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ClearAllAccepts() +{ + // Clears all "player has accepted" marks. +//debugprint( "ClearAllAccepts()\n" ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_CHANNELOWNER ) ) // pUser null if this is an "early insertion" entry on startup + pWO->MarkItemAccepted( i, false ); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( bHost ) + { + pTextBtnAcceptStart->Disable(); + if( bWaitingToStart ) + { + // Something has happened that makes starting a game not possible now. + // Cancel out of waiting mode and tell guests to do the same. + bWaitingToStart = false; + InformAboutCancelStart(); + BindControls( true ); + ResetReadyToGo(); + display = REDRAW_ALL; + } + else + pTextBtnAcceptStart->Flag_To_Redraw(); + } + else + { + pTextBtnAcceptStart->Enable(); + pTextBtnAcceptStart->Flag_To_Redraw(); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllGuestsAccept() +{ + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedAccepted( i ) ) + return false; + } + return true; +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::ColorNextAvailable() +{ + // Returns the first free player color available. + + // (Totally unoptimized, but hardly ever called.) + + for( int i = 0; i < MAX_MPLAYER_COLORS; i++ ) + { + if( pILPlayers->FindColor( &ColorRemaps[ i ] ) == -1 ) + { + return (PlayerColorType)i; + } + } +// debugprint( "ColorNextAvailable is NONE!\n" ); + return PCOLOR_NONE; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::GuestIsReadyToPlay( const char* szName, const char* szReadyState ) +{ + // A guest has responded to a game start request. + SetPlayerReadyToGo( szName, szReadyState ); + + if( bAllPlayersReadyToGo() ) + { +// debugprint( "All players ready to go.\n" ); + // We can start the game. + bHostSayGo = true; // Set trigger to fire function after we're out of callback. + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bNeedScenarioDownload() +{ + // Returns true if we don't have the scenario and it is allowable as a download. + if( !bHost ) + { + if( Find_Local_Scenario( Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial ) ) + { +// debugprint( "bNeedScenarioDownload() returning false.\n" ); + bRequestedScenarioDownload = false; + return false; + } + else + { +/* if( !Session.ScenarioIsOfficial ) + { + bRequestedScenarioDownload = true; + return true; + } + else + { +// debugprint( "bNeedScenarioDownload fatal\n" ); + Fatal( "" ); + } +*/ +// debugprint( "Requesting download\n" ); // ajw Shouldn't be happening with am maps when i have am...? + bRequestedScenarioDownload = true; // All maps are downloadable. + return true; + } + } + + bRequestedScenarioDownload = false; + return false; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::HostSaysGo() +{ + bHostSayGo = false; + bHostWaitingForGoTrigger = true; + +// debugprint( "HostSaysGo()\n" ); + + if( !pWO->RequestGameStart() ) + { +// debugprint( "Call to RequestGameStart() failed.\n" ); + //Fatal( "Call to RequestGameStart() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + + // Tell guests to start game. +// debugprint( "Telling guests to start game.\n" ); + + // Create WOL_GAMEOPT_INFGO message. + // This contains the color for each player, which can change about haphazardly at the end of setup, + // without causing "unacceptedness". This means that the colors everyone thinks everyone else is might not + // be sync'ed. Host sets everyone straight here. + char szSend[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 50 ] = ""; + sprintf( szSend, "%02u", WOL_GAMEOPT_INFGO ); + + User* pUser = pWO->pChatSink->pGameUserList; + while( pUser ) + { + char szUser[ WOL_NAME_LEN_MAX + 10 ]; + PlayerColorType Color = GetPlayerColor( (char*)pUser->name ); + sprintf( szUser, " %s %02u", (char*)pUser->name, Color ); // What if player left just now, and got removed from list. Ok to continue and fail on game start? + strcat( szSend, szUser ); + pUser = pUser->next; + } + if( !pWO->SendGo( szSend ) ) + { +// debugprint( "Call to SendGo() failed.\n" ); + //Fatal( "Call to SendGo() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + +/* ...Method changed. It appears that my channelleave may appear to guests before the "go" privategameopt. + For this reason, I'll wait until I receive a copy of the "go" message sent to myself, before proceeding. + + pWO->pChat->PumpMessages(); // Flush the send out. + // (ajw - Note: An apparent bug in wolapi means that this pump does not necessarily flush the go gameopts. + // This is ok in practice, because the host hits pumps later that will flush them.) + + // Pretend we just received szSend, and processed it like a guest would. + char* szFakeGoMessage = szSend + 3; + + TriggerGameStart( szFakeGoMessage ); +*/ +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::TriggerGameStart( char* szGoMessage ) +{ + // Last function before dialog is exited for game start. (Which must occur now.) + // Host or guest is about to start a game using final data in szGoMessage. + +// debugprint( "TriggerGameStart( %s )\n", szGoMessage ); + + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + + bHostWaitingForGoTrigger = false; + bExitForGameTrigger = true; + + // The following is based on Read_Game_Options()... + +// WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, sizeof(Session.Handle), buffer); + + strcpy( Session.Handle, pWO->szMyName ); + + // GameName will be the host's name... + strcpy( Session.GameName, pWO->pGameHostName() ); +// debugprint( "Session.GameName is %s\n", Session.GameName ); + +// gotit Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); +// gotit Session.PrefColor = Session.ColorIdx; +// gotit int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); +// gotit Session.House = (HousesType) ((int)HOUSE_USSR + temp); + +// gotit Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); +// gotit Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); +// gotit Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); +// gotit Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); +// gotit Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); +// gotit BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); +// gotit Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); +// gotit Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); +// gotit Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + +// UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + if( bSlowUnitBuildRate ) + UnitBuildPenalty = 250; + else + UnitBuildPenalty = 100; + + //PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodGameID = pWO->pChatSink->iGameID; + + // Reset ChatSink's iGameID. + pWO->pChatSink->iGameID = 0; + + //PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + PlanetWestwoodStartTime = time( NULL ); + //WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + +// gotit Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + // The preceding was based on Read_Game_Options()... + + + // Now do whatever we've left out that the horrific Net_Fake_New_Dialog() and Net_Fake_Join_Dialog() used to do for us... + + // Set up the Session.Players list. + // I think there is dependence on the local player being first, so put him there. + // Else put them in order listed in the szGoMessage. + // I will set "ID" based on a player's color, though it seems unclear if this is even used in the game, or what it should be. + + Clear_Vector( &Session.Players ); + + // Make the pILPlayers a valid list of players in the game. + // Players might (incredibly rarely) have joined in the last split-second, and we only want the players listed in + // the szGoMessage. To test for whether they're in this list, first wipe the colors from all list items. + // Then we fill them in from info in szGoMessage. + // We can ignore any list items then that have no color assigned. + // Also, we'll know that the colors assigned to valid players indeed match up with what every other client has. + // Remember, all other data should already be sync'ed because it has been implemented in such a way that changes would + // cause "unacceptedness" of guests to occur. + + // Clear colors in list. + for( int iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ PCOLOR_NONE ] ); + + // Parse szGoMessage to iterate through players. + char szDelimiter[] = " "; + char* szToken; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + + szToken = strtok( szGoMessage, szDelimiter ); + + while( szToken ) + { + strcpy( szPlayerName, szToken ); + szToken = strtok( NULL, szDelimiter ); + + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); // ajw note: inserts if not found. + szToken = strtok( NULL, szDelimiter ); + } + + // Add myself to Session.Players list. + _ASSERTE( pILPlayers->Find( pWO->szMyName ) != -1 ); + + NodeNameType* pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, pWO->szMyName ); // "Name" is 12 chars max. + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( pWO->szMyName ); +//debugprint( "ME: pPlayerNew->Player.House = %i\n", pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( pWO->szMyName ); +// This gets done later. +// pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + char szHostName[ WOL_NAME_LEN_MAX ] = "Game host"; + + // Add all other players to Session.Players list (if they have a valid color - see just above). + // Also in this step - build the scenario download requests array (used by hosts only). + memset( Session.ScenarioRequests, 0, sizeof( Session.ScenarioRequests ) ); + Session.RequestCount = 0; + for( iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + { + // The following is not very efficient, but doesn't have to be. Better in this case to keep it clear and simple. + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( iItem ) ); + if( strcmp( szPlayerName, pWO->szMyName ) != 0 && GetPlayerColor( szPlayerName ) != PCOLOR_NONE ) + { +// debugprint( "Creating player node '%s'\n", szPlayerName ); + pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, szPlayerName ); + // Get player's IP address from pChatSink... + unsigned long lAddress = ( pWO->pChatSink->GetPlayerGameIP( szPlayerName ) ); //ntohl( +// debugprint( "IP address is %i, or 0x%x\n", lAddress, lAddress ); + if( pWO->GameInfoCurrent.bTournament ) + { + // This is a tournament game, and I therefore have only one opponent: this one. + // for convenience, save a copy of his IP address in case I need it later for disconnect pinging. + pWO->TournamentOpponentIP = lAddress; + pWO->bDisconnectPingingCompleted = false; + } + NetNumType net; + NetNodeType node; + memset( net, 0, 4 ); + memset( node, 0, 6 ); + memcpy( node, &lAddress, 4 ); + //memcpy( node + 2, &lAddress, 4 ); + pPlayerNew->Address.Set_Address( net, node ); + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( szPlayerName ); +//debugprint( "Player %i: pPlayerNew->Player.House = %i\n", iItem, pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( szPlayerName ); + // This gets done later. + //pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + // If player is the game host, set HostAddress. This global is used when downloading scenarios; who knows where else. + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( iItem ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + { + Session.HostAddress = pPlayerNew->Address; + strcpy( szHostName, (char*)pUser->name ); +/* + // debugging + NetNumType netxxx; + NetNodeType nodexxx; + Session.HostAddress.Get_Address( netxxx, nodexxx ); +// debugprint( "Host, ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); +*/ + } +/* + else + { + NetNumType netxxx; + NetNodeType nodexxx; + pPlayerNew->Address.Get_Address( netxxx, nodexxx ); +// debugprint( "Player ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); + } +*/ + + if( bHost && pWO->bItemMarkedNeedScenario( iItem ) ) + { +// debugprint( "%s has requested scenario download.\n", szPlayerName ); + Session.ScenarioRequests[ Session.RequestCount++ ] = Session.Players.Count() - 1; + } + } +// else +// debugprint( "%s excluded from Session.Players\n", szPlayerName ); + } + + + // From Init... +// debugprint( "About to call Open_Socket().\n"); + PacketTransport->Open_Socket( 0 ); + +// debugprint( "RA95 - About to call Start_Listening.\n" ); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + +// debugprint( "Session.ScenarioFileName is %s.\n", Session.ScenarioFileName ); + + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ +// gotit WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", +// Session.Options.ScenarioDescription, +// sizeof (Session.Options.ScenarioDescription), +// buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + if( !bHost ) // Else ScenarioIndex is already set. + { + if( bRequestedScenarioDownload ) + { + Session.Options.ScenarioIndex = 1; + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + { + // Shouldn't ever happen. We should never have the opportunity to ask for one of these maps to be downloaded. + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + // Wait for download from game host. +//debugprint( "Wait for download from game host.\n" ); + if( !Get_Scenario_File_From_Host( Session.ScenarioFileName, 1 ) ) + { +// debugprint( "Get_Scenario_File_From_Host failed!\n" ); + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.ScenarioFileName ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + else + { + // Match ScenarioDescription to a ScenarioIndex. +/* This is how the same code existed previously. Insufficient because Description may match on many scenarios. + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } +*/ + // (We have already done the lookup, in Find_Local_Scenario, above.) + Session.Options.ScenarioIndex = ScenarioIndex_From_Filename( Session.ScenarioFileName ); + _ASSERTE( Session.Options.ScenarioIndex != -1 ); + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + } + else // bHost + { + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + strcpy( Session.Options.ScenarioDescription, (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + } + + Options.GameSpeed = 0; + + //Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + //Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + Session.MaxAhead = 15; //9; + //Session.FrameSendRate = 5; //3; + Session.FrameSendRate = 3; //3; + + // This is from NETDLG processing... + Session.NumPlayers = Session.Players.Count(); + + pWO->GameInfoCurrent.iPlayerCount = Session.Players.Count(); + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + + if( bHost ) + { + if( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + { + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + pWO->bSelfDestruct = true; + return; + } + } + if( Session.RequestCount ) + { + // Send the scenario to any guests that requested a download. +//debugprint( "Send the scenario to any guests that requested a download.\n" ); + Send_Remote_File( Scen.ScenarioName, 1 ); + } + } + + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + Ipx.Set_Timing (30, (unsigned long) -1, 600); + + pWO->bEnableNewAftermathUnits = bAftermathUnits; + bAftermathMultiplayer = bAftermathUnits; + + *pWO->szExternalPager = 0; + + pWO->bDoingDisconnectPinging = false; // Pointlessly making sure. + + *szTriggerGameStartInfo = 0; // This was set in order to trigger my coming here. +} + +#endif + +#include +//*********************************************************************************************** +bool bSpecialAftermathScenario( const char* szScenarioDescription ) +{ + // Returns true if szScenarioDescription matches one of the descriptions for Aftermath multiplayer + // scenarios that have special Aftermath-only units *embedded* within them. + if( strcmp( szScenarioDescription, "Booby Traps (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Central Conflict Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Circles of Death (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Holy Grounds (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Island Wars Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "King of the Hills Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "The Hills Have Eyes (Mega 8 players)" ) == 0 ) + return true; + return false; +} + +//*********************************************************************************************** +int ScenarioIndex_From_Filename( const char* szScenarioFilename ) +{ +#if (0)//PG + // Returns the scenario index that matches the scenario filename, or -1 if no match found. + for( int index = 0; index < Session.Scenarios.Count(); index++ ) + { + if( _stricmp( szScenarioFilename, Session.Scenarios[index]->Get_Filename() ) == 0 ) + return index; + } +#endif + return -1; +} diff --git a/REDALERT/WOL_GSUP.H b/REDALERT/WOL_GSUP.H new file mode 100644 index 000000000..633c804dd --- /dev/null +++ b/REDALERT/WOL_GSUP.H @@ -0,0 +1,361 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// wol_gsup.h "WOL Game Setup Dialog" +// ajw 08/06/98 + +// Class WOL_GameSetupDialog is a move away from doing dialogs purely in C, a format that I've maintained in the login and +// chat dialogs, mimicking how dialogs are done elsewhere in the code, but became frustrated with. +// Though I'll follow the same pattern as before, things will be centralized a little cleaner through the use of this object. +// Why a standard dialog class that would handle common input behavior and so forth (one that this class could derive from) +// wasn't written 5 years ago, I don't know... + +#include "WolapiOb.h" +//class WolapiObject; +class IconListClass; +class EditClass; +class GaugeClass; +class CheckListClass; +class TextButtonClass; +class StaticButtonClass; +class DropListClass; +class ShapeButtonClass; +class BigCheckBoxClass; +class ToolTipClass; + +//*********************************************************************************************** +enum RESULT_WOLGSUP +{ + RESULT_WOLGSUP_BACKTOCHAT, + RESULT_WOLGSUP_FATALERROR, + RESULT_WOLGSUP_HOSTLEFT, + RESULT_WOLGSUP_STARTGAMEHOST, + RESULT_WOLGSUP_STARTGAME, + RESULT_WOLGSUP_RULESMISMATCH, + RESULT_WOLGSUP_LOGOUT, +}; + +struct GAMEPARAMS +{ + GlobalPacketType GPacket; + // My additions to game params. - ajw + bool bAftermathUnits; + bool bSlowUnitBuildRate; +}; + +//*********************************************************************************************** +class WOL_GameSetupDialog +{ +public: + WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ); + ~WOL_GameSetupDialog(); + + RESULT_WOLGSUP Run(); + +public: + bool bHost; // True when I created the game channel and am the host. + + bool bHostSayGo; // Trigger host instructing all to start game immediately. + bool bHostWaitingForGoTrigger; // True while host is waiting for go message to bounce back to him and trigger start. + bool bExitForGameTrigger; // Trigger exiting dialog for game. + + + void ProcessGuestRequest( User* pUser, const char* szRequest ); + void ProcessInform( char* szRequest ); + void OnGuestJoin( User* pUser ); + void OnGuestLeave( User* pUser ); + +protected: + void Initialize(); + RESULT_WOLGSUP Show(); + void SetSpecialControlStates(); + void BindControls( bool bBind ); + bool ExitGameChannel(); + void DrawScenarioDescripIcon( const char* pDIB ) const; + void SetPlayerColor( const char* szName, PlayerColorType Color ); + PlayerColorType GetPlayerColor( const char* szName ); + void SetPlayerHouse( const char* szName, HousesType House ); + HousesType GetPlayerHouse( const char* szName ); + bool SetPlayerAccepted( const char* szName, bool bAccepted ); + bool IveAccepted(); + bool SetPlayerReadyToGo( const char* szName, const char* szReadyState ); + void ResetReadyToGo(); + bool bPlayerReadyToGo( const char* szName ); + bool bAllPlayersReadyToGo(); + + bool bParamsUnfresh(); + void SendParams(); + bool AcceptParams( char* szParams ); + void SetGParamsToCurrent( GAMEPARAMS& GParams ); + + void AcceptNewGuestPlayerInfo( char* szMsg ); + + bool RequestPlayerColor( PlayerColorType Color ); + bool InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ); + bool InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ); + bool InformAboutPlayerAccept( const char* szName, User* pUserPriv ); + bool InformAboutStart(); + bool InformAboutCancelStart(); + + void ClearAllAccepts(); + bool bAllGuestsAccept(); + + PlayerColorType ColorNextAvailable(); + + void GuestIsReadyToPlay( const char* szName, const char* szReadyState ); + bool bNeedScenarioDownload(); + void HostSaysGo(); + void TriggerGameStart( char* szGoMessage ); + + enum SCENARIO_GAMEKIND + { + SCENARIO_RA = 0, + SCENARIO_CS, + SCENARIO_AM, + SCENARIO_USER, + SCENARIO_UNINITIALIZED, + }; + void ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ); + +// bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +public: + int d_dialog_w; + int d_dialog_h; + int d_dialog_x; + int d_dialog_y; + int d_dialog_cx; + + int d_txt6_h; + int d_margin1; + + int d_house_w; + int d_house_h; + int d_house_x; + int d_house_y; + + int d_color_w; + int d_color_h; + int d_color_x; + int d_color_y; + + int d_playerlist_w; + int d_playerlist_h; + int d_playerlist_x; + int d_playerlist_y; + + int d_scenariolist_w; + int d_scenariolist_h; + int d_scenariolist_x; + int d_scenariolist_y; + + int d_gamekind_w; + int d_gamekind_h; + int d_gamekind_x; + int d_gamekind_y; + + int d_count_w; + int d_count_h; + int d_count_x; + int d_count_y; + + int d_level_w; + int d_level_h; + int d_level_x; + int d_level_y; + + int d_credits_w; + int d_credits_h; + int d_credits_x; + int d_credits_y; + + int d_aiplayers_w; + int d_aiplayers_h; + int d_aiplayers_x; + int d_aiplayers_y; + + int d_options_w; + int d_options_h; + int d_options_x; + int d_options_y; + + int d_disc_w; + int d_disc_h; + int d_disc_x; + int d_disc_y; + + int d_send_w; + int d_send_h; + int d_send_x; + int d_send_y; + + int d_ok_w; + int d_ok_h; + int d_ok_x; + int d_ok_y; + + int d_cancel_w; + int d_cancel_h; + int d_cancel_x; + int d_cancel_y; + + int d_accept_w; + int d_accept_h; + int d_accept_x; + int d_accept_y; + + int d_amunits_w; + int d_amunits_h; + int d_amunits_x; + int d_amunits_y; + + int d_action_w; + int d_action_h; + int d_action_x; + int d_action_y; + +protected: + GadgetClass* commands; // The controls list. + + IconListClass* pILPlayers; + IconListClass* pILScens; + IconListClass* pILDisc; + char szSendBuffer[ MAXCHATSENDLENGTH ]; + EditClass* pEditSend; + GaugeClass* pGaugeCount; + GaugeClass* pGaugeLevel; + GaugeClass* pGaugeCredits; + GaugeClass* pGaugeAIPlayers; + CheckListClass* pCheckListOptions; + TextButtonClass* pTextBtnOk; + TextButtonClass* pTextBtnCancel; + TextButtonClass* pTextBtnAcceptStart; + TextButtonClass* pTextBtnAction; + StaticButtonClass* pStaticDescrip; + StaticButtonClass* pStaticUnit; + StaticButtonClass* pStaticLevel; + StaticButtonClass* pStaticCredits; + StaticButtonClass* pStaticAIPlayers; + char szHouseBuffer[25]; // buffer for house droplist + DropListClass* pDropListHouse; + BigCheckBoxClass* pCheckAftermathUnits; + ShapeButtonClass* pShpBtnScenarioRA; + ShapeButtonClass* pShpBtnScenarioCS; + ShapeButtonClass* pShpBtnScenarioAM; + ShapeButtonClass* pShpBtnScenarioUser; + + ToolTipClass* pTTipAcceptStart; + ToolTipClass* pTTipCancel; + ToolTipClass* pTTipAction; + + WolapiObject* pWO; + + + GAMEPARAMS GParamsLastSent; // Used merely as a handy container for the vars I need to set. + + DWORD dwTimeNextParamRefresh; // Param changes are sent by host at certain interval. + + HousesType HousePrevious; + + unsigned int nHostLastParamID; // Host's send update tracking packet ID. + unsigned int nGuestLastParamID; // Guest's record of last ID received from host. + + bool bWaitingToStart; + + bool bProcess; // True means continue doing input loop. + RESULT_WOLGSUP ResultReturn; // Value that will be returned from Show(). + char szNameOfHostWhoJustBailedOnUs[ WOL_NAME_LEN_MAX ]; // If set, triggers setup cancellation. + + bool bParamsReceived; // True after any WOL_GAMEOPT_INFPARAMS messages have been received from a host. + + bool bLeaveDueToRulesMismatchTrigger; + + bool bRequestedScenarioDownload; + + char szTriggerGameStartInfo[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 60 ]; + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast; // ToolTip the mouse was last over, or null. + + // Extra game params... + bool bAftermathUnits; // True if aftermath units are to be used in the game. + bool bSlowUnitBuildRate; + + SCENARIO_GAMEKIND ScenKindCurrent; // Describes what gamekind of scenarios we are viewing, if host. + DynamicVectorClass< const char* > ar_szScenarios[ 4 ]; // Lists of scenarios, by SCENARIO_GAMEKIND. + // ar_szScenIndexes parallels ar_szScenarios, holds ScenarioIndex. It's actually an int, but I'm using void* to avoid + // template instantiation problems and the need to change defines.h. + DynamicVectorClass< void* > ar_szScenIndexes[ 4 ]; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_PLAYERLIST, + BUTTON_HOUSE, + BUTTON_SCENARIOLIST, + BUTTON_DISCLIST, + BUTTON_SENDEDIT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_PARAMS, +// BUTTON_OK, + BUTTON_CANCEL, + BUTTON_ACCEPTSTART, + BUTTON_ACTION, + BUTTON_AFTERMATHUNITS, + BUTTON_SCENARIO_RA, + BUTTON_SCENARIO_CS, + BUTTON_SCENARIO_AM, + BUTTON_SCENARIO_USER, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + RedrawType display; + +}; + +#endif + diff --git a/REDALERT/WOL_LOGN.CPP b/REDALERT/WOL_LOGN.CPP new file mode 100644 index 000000000..f53f04913 --- /dev/null +++ b/REDALERT/WOL_LOGN.CPP @@ -0,0 +1,662 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Logn.cpp - WW online name/password dialog. +// ajw 07/16/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "PassEdit.h" +#include "WolStrng.h" +#include "BigCheck.h" + +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ); +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ); +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ); +//char* LoadShpFile( const char* szShpFile ); + +void DebugChatDef( HRESULT hRes ); + +extern bool bTabKeyPressedHack; + +//#include "WolDebug.h" + +//*********************************************************************************************** +int WOL_Login_Dialog( WolapiObject* pWO ) +{ + // Return values: 0 = user cancels, 1 = success, -1 = force game exit + + if( pWO->bLoggedIn() ) + { + pWO->bReturningAfterGame = true; // Set trigger for chat dialog. + return 1; // We are already logged in, and have just come back from a game. + } + + /* + ** Dialog & button dimensions + */ +#ifdef FRENCH + int d_dialog_w = 160 * RESFACTOR; // dialog width +#else + int d_dialog_w = 150 * RESFACTOR; // dialog width +#endif + int d_dialog_h = 85 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((255 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_name_w = 66 * RESFACTOR; + int d_name_h = 10 * RESFACTOR; +#ifdef FRENCH + int d_name_x = d_dialog_x + 25 * RESFACTOR; +#else + int d_name_x = d_dialog_x + 20 * RESFACTOR; +#endif + int d_name_y = d_dialog_y + top_margin + 25 * RESFACTOR; + + int d_pass_w = 36 * RESFACTOR; + int d_pass_h = d_name_h; + int d_pass_x = d_name_x + d_name_w + 6 * RESFACTOR; + int d_pass_y = d_name_y; + + int d_list_w = d_name_w; + int d_list_h = 20 * RESFACTOR; + int d_list_x = d_name_x; + int d_list_y = d_dialog_y + top_margin + 40 * RESFACTOR; + +// int d_save_w = d_pass_w; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_pass_x + ( d_pass_w / 2 ) - ( d_pass_w / 2 ); + int d_save_y = d_list_y; // + ( d_list_h / 2 ) - ( d_save_h / 2 ); + + int d_delete_w = d_pass_w; + int d_delete_h = 10 * RESFACTOR; + int d_delete_x = d_save_x; + int d_delete_y = d_list_y + d_list_h - d_delete_h; + +#ifdef FRENCH + int d_connect_w = 45 * RESFACTOR; +#else + int d_connect_w = 40 * RESFACTOR; +#endif + int d_connect_h = 13 * RESFACTOR; + int d_connect_x = d_name_x + d_name_w/2 - d_connect_w/2; + int d_connect_y = d_dialog_y + top_margin + 65 * RESFACTOR; //d_dialog_y + d_dialog_h - d_connect_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 40 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_pass_x + d_pass_w/2 - d_cancel_w/2; //d_dialog_cx + d_margin; + int d_cancel_y = d_connect_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_CONNECT = 100, + BUTTON_CANCEL, + LISTBOX_NICKS, + EDITBOX_NAME, + EDITBOX_PASS, + BUTTON_SAVECHECK, + BUTTON_DELETE, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + int iReturn = 1; // 0 = user cancels, 1 = success, -1 = force game exit + + /* + ** Other Variables + */ + char szNameBuffer[ WOL_NAME_LEN_MAX ] = {0}; // User name. + char szPassBuffer[ WOL_PASSWORD_LEN ] = {0}; // User password. + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass ConnectBtn( BUTTON_CONNECT, TXT_WOL_CONNECT, TPF_BUTTON, d_connect_x, d_connect_y, d_connect_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + IconListClass NickList( LISTBOX_NICKS, d_list_x, d_list_y, d_list_w, d_list_h, TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1, 0 ); + + WOLEditClass NameEdit( EDITBOX_NAME, szNameBuffer, sizeof(szNameBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, -1, EditClass::ALPHANUMERIC ); + + PassEditClass PassEdit( EDITBOX_PASS, szPassBuffer, sizeof(szPassBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_pass_x, d_pass_y, d_pass_w, -1, EditClass::ALPHANUMERIC ); + + // Just making sure globals are set right before String_Pixel_Width() call... sigh + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW ); + int iSaveTextWidth = String_Pixel_Width( TXT_WOL_SAVELOGIN ) + BIGCHECK_OFFSETX; + BigCheckBoxClass SaveCheckBox( BUTTON_SAVECHECK, d_save_x, d_save_y, iSaveTextWidth, d_save_h, + TXT_WOL_SAVELOGIN, TPF_6PT_GRAD | TPF_NOSHADOW, true ); + + TextButtonClass DeleteBtn( BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + // Get saved nickname/passwords from the registry. + if( ReadSavedNicks( pWO, NickList, szNameBuffer, szPassBuffer ) ) + { + PassEdit.bClearOnNextSetFocus = true; + } + else + { + // Offer user the chance to go to web site now to get a nick. + if( pWO->DoWebRegistration() ) + { + // User chose to go to web page. Leave function so that we'll re-read nicks when they return. + return 0; + } + } + + /* + ** Create the button list. + */ + commands = &ConnectBtn; + CancelBtn.Add_Tail(*commands); + NickList.Add_Tail(*commands); + NameEdit.Add_Tail(*commands); + PassEdit.Add_Tail(*commands); + SaveCheckBox.Add_Tail(*commands); + DeleteBtn.Add_Tail(*commands); + NameEdit.Set_Focus(); + + if( NickList.Count() == 0 ) + DeleteBtn.Disable(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + //------------------------------------------------------------------------ + // Clear screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); +// Show_Mouse(); + + /* + ** Display the dialog box. + */ +// Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_WOL_LOGINDIALOG, d_dialog_x, d_dialog_y, d_dialog_w); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( TXT_WOL_NAME, d_name_x + ( d_name_w / 2 ), d_name_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + Fancy_Text_Print( TXT_WOL_PASSWORD, d_pass_x + ( d_pass_w / 2 ), d_pass_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + bTabKeyPressedHack = false; + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + } + +// /* +// ** If the key was pressed, then default to the appropriate +// ** action button according to the style of this dialog box. +// */ +/* if (input == KN_RETURN || input == (BUTTON_CONNECT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + input = (KeyNumType)(BUTTON_CONNECT|KN_BUTTON); + CancelBtn.Turn_Off(); + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_CONNECT); + if (toggle != NULL) { + toggle->Turn_On(); + toggle->IsPressed = true; + } + + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } +*/ + /* + ** Process input. + */ +// if( input ) +// debugprint( "input: %i\n", input ); + + if( bTabKeyPressedHack ) + { + if( NameEdit.Has_Focus() ) + PassEdit.Set_Focus(); + else + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + } + + switch( input ) + { + /* + ** ESC/Cancel: break + */ + case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + iReturn = 0; + process = false; + break; + + case KN_RETURN: + case ( EDITBOX_NAME | KN_BUTTON ): + case ( EDITBOX_PASS | KN_BUTTON ): + case ( BUTTON_CONNECT | KN_BUTTON ): + { + if( !strlen( szNameBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGNAME ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + if( !strlen( szPassBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGPASSWORD ); + firsttime = true; // Bloody hack. + PassEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + + // If we have not done RequestServerList() yet, do it now. + if( !pWO->pChatSink->pServer ) + { + bool bBreak = false; + HRESULT hRes = pWO->GetChatServer(); + switch( hRes ) + { + case E_FAIL: + bBreak = true; + WWMessageBox().Process( TXT_WOL_CANTCONNECT ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case USERCANCELLED: + bBreak = true; + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHAVOIDED: + bBreak = true; + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHDOWNLOADED: + bBreak = true; + process = false; + iReturn = -1; + break; + } + if( bBreak ) + break; + } + + // RequestConnection()... + HRESULT hRes = pWO->AttemptLogin( szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ); + if( hRes == S_OK ) + { + if( SaveCheckBox.IsOn && !bSaveNick( pWO, szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ) ) + { + // Nick/pass save failed. + WWMessageBox().Process( TXT_WOL_CANTSAVENICK ); + } + process = false; + } + else + { + switch( hRes ) + { + case USERCANCELLED: + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case CHAT_E_BADPASS: + WWMessageBox().Process( TXT_WOL_BADPASS ); + break; + case CHAT_E_NICKINUSE: + WWMessageBox().Process( TXT_WOL_NICKINUSE ); + break; + case CHAT_E_CON_ERROR: + // This error value I pass back myself, when the emergency timeout is hit. + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + } + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + } + break; + } + +/* + case( EDITBOX_PASS | KN_BUTTON ): + { + // Message with delay so that user has time to read it... + CDTimerClass timer; + timer = TICKS_PER_SECOND*4; + WWMessageBox().Process(TXT_WOL_DEBUG2, TXT_NONE); + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + display = true; + break; + } +*/ + case ( LISTBOX_NICKS | KN_BUTTON ): + strcpy( szNameBuffer, NickList.Get_Item( NickList.Current_Index() ) ); + strcpy( szPassBuffer, NickList.Get_Item_ExtraDataString( NickList.Current_Index() ) ); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + // Because the password is mangled, if the user begins to edit it now, we clear it. + // Otherwise we could get a half-mangled, half-unmangled password field. + // PassEdit.bClearOnNextSetFocus also acts as a flag telling us whether or not the + // password field is mangled or not. + PassEdit.bClearOnNextSetFocus = true; +// display = true; + break; + + case ( BUTTON_SAVECHECK | KN_BUTTON ): + break; + + case ( BUTTON_DELETE | KN_BUTTON ): + if( NickList.Count() > 0 ) + { + DeleteNick( pWO, NickList.Current_Index() + 1 ); + NickList.Remove_Item( NickList.Current_Index() ); + NickList.Flag_To_Redraw(); + if( NickList.Count() == 0 ) + { + DeleteBtn.Disable(); + DeleteBtn.Flag_To_Redraw(); + } + } + break; + + default: + break; + } + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ) +{ + // Read saved nickname/passwords from the registry. + // Set up the list of nick/passwords. + // Copy the first nick into the nick/password edits. + + // Returns true if edits are set with a default nick/pass because a nick was found. + + LPCSTR szNick; + LPCSTR szPass; + bool bReturn = false; + + for( int i = 1; i != 3; i++ ) + { + if( pWO->pChat->GetNick( i, &szNick, &szPass ) == S_OK ) + { + if( *szNick ) + { + NickList.Add_Item( szNick, NULL, NULL, ICON_SHAPE, szPass ); + if( i == 1 ) + { + strcpy( szNameBuffer, szNick ); + strcpy( szPassBuffer, szPass ); + bReturn = true; + } + } + } + } + return bReturn; +} + +//*********************************************************************************************** +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ) +{ + // Saves specified nick and password in the registry, using SetNick. + // Returns false if nick can't be saved. + + // If slot 1 empty, use slot 1. + // Else push nick 1 down to second slot and save new nick in slot 1, unless + // nick 1 name matches new entry. + + LPCSTR szNick; + LPCSTR szPass; + bool bPushSlot1 = true; + + switch( pWO->pChat->GetNick( 1, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bPushSlot1 = false; + break; + case S_OK: + if( *szNick == 0 ) + bPushSlot1 = false; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bPushSlot1 = false; // We can use this slot as the name is the same. + break; + } + + if( bPushSlot1 ) + { + // Move nick in slot 1 to slot 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + } + + // Save new nick in slot 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + +/* + int iSlot; + bool bStop = false; + for( iSlot = 1; iSlot != 3; iSlot++ ) + { + switch( pWO->pChat->GetNick( iSlot, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bStop = true; + break; + case S_OK: + if( *szNick == 0 ) + bStop = true; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bStop = true; // We can use this slot as the name is the same. + break; + } + if( bStop ) + break; + } + if( iSlot == 3 ) + { + // No open slots were found. + // Get nick 1. + pWO->pChat->GetNick( 1, &szNick, &szPass ); + // Save as nick 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + // Save new nick 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } + else + { + // iSlot points to an open slot. + return ( pWO->pChat->SetNick( iSlot, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } +*/ +} + +//*********************************************************************************************** +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ) +{ + // Delete a nick from the registry via wolapi SetNick. + // If nick to delete is in position one, and there is a second nick, move the second nick into position one. + if( iOneBasedEntryToDelete == 1 ) + { + // Check for nick 2. + LPCSTR szNick; + LPCSTR szPass; + if( pWO->pChat->GetNick( 2, &szNick, &szPass ) == S_OK && *szNick != 0 ) + { + // Copy nick in slot 2 to slot 1. + pWO->pChat->SetNick( 1, szNick, szPass, false ); // (Already mangled.) + // Delete slot 2. + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } + else + { + // No second nick. + HRESULT hRes = pWO->pChat->SetNick( 1, "", "", false ); + DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } +} + +/* +//*********************************************************************************************** +char* LoadShpFile( const char* szShpFile ) +{ + // Returns pointer to shp data that has been new'ed (and must be delete[]d), or NULL if failure. + // ajw: No longer needed - I used this before putting new resources into a mix file. + HANDLE hFile; + hFile = CreateFile( szShpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + DWORD dwFileSize = GetFileSize( hFile, NULL ); + char* pShp = new char[ dwFileSize ]; + DWORD dwBytesRead; + ReadFile( hFile, pShp, dwFileSize, &dwBytesRead, NULL ); +// debugprint( "~~ LoadShpFile() - Read %i bytes out of %i from shp file.\n", dwBytesRead, dwFileSize ); + CloseHandle( hFile ); + return pShp; +} +*/ + +#endif diff --git a/REDALERT/WOL_MAIN.CPP b/REDALERT/WOL_MAIN.CPP new file mode 100644 index 000000000..9557a7767 --- /dev/null +++ b/REDALERT/WOL_MAIN.CPP @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Main.cpp - Bottom level wolapi-stuff function. +// ajw 07/16/98 + +#include "function.h" +#include "WolapiOb.h" +#include "wol_gsup.h" +#include "WolStrng.h" + +int WOL_Login_Dialog( WolapiObject* pWolapi ); +int WOL_Chat_Dialog( WolapiObject* pWolapi ); +const char* Game_Registry_Key(); + +bool ReregisterWolapiDLL(); +void HandleDLLFail(); + +WolapiObject* pWolapi = NULL; + +#include "WolDebug.h" + +//*********************************************************************************************** +// The first time through, pWolapi is NULL thus wolapi gets set up. WOL_Login_Dialog presents the user +// with the login dialog and attempts to log us on to the server. If the user continues on all the +// way to a game start, we will drop out of here with pWolapi still pointing to a valid WolapiObject, +// and with pWolapi's iLobbyReturnAfterGame set to the number of the lobby to return to automatically +// after the game ends. +// Init() automatically brings us here if pWolapi is non-null. +//*********************************************************************************************** +int WOL_Main() +{ + // Return values: + // 0 = cancel + // 1 = start game + // -1 = patch downloaded, shut down app + int iReturn = 0; + + if( pWolapi ) + { + // We have returned from a game started through ww online. + + // Start theme up again. + Theme.Play_Song( THEME_INTRO ); + + // Verify that we are still connected. If we aren't, kill WolapiObject and start over. + // (This will likely occur during the game, if connection is lost. Ensure that it is done here.) + pWolapi->pChat->PumpMessages(); // Causes OnNetStatus() call if no longer connected. + if( pWolapi->bConnectionDown ) + { +//debugprint( "Re-entering WOL_Main(), pWolapi->bConnectionDown is true. Deleting old WolapiObject...\n" ); + WWMessageBox().Process( TXT_WOL_WOLAPIREINIT ); + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + } + + if( !pWolapi ) + { + // Start up wolapi. + pWolapi = new WolapiObject; + if( !pWolapi->bSetupCOMStuff() ) + { + // Things are really bad if this happens. A COM call failed. + + // We first assume that their wolapi.dll failed to register during wolsetup.exe, part of the patch process. + // This happens if they have an outdated oleaut32.dll, such as the one that comes with original + // version of Windows 95. + +// debugprint( "bSetupCOMStuff failed. Attemping to reregister wolapi.dll...\n" ); + // Attempt to re-register wolapi.dll... + if( ReregisterWolapiDLL() ) + { + if( !pWolapi->bSetupCOMStuff() ) + { + // Still failed after reregistering seemed to work. + HandleDLLFail(); + return 0; + } + } + else + { + HandleDLLFail(); + return 0; + } + } + pWolapi->PrepareButtonsAndIcons(); + // Undocumented hack needed for patch downloading, per Neal. + pWolapi->pChat->SetAttributeValue( "RegPath", Game_Registry_Key() ); + // (Not that anything's really "documented".) + } + + pWolapi->bInGame = false; + + int iLoginResult = WOL_Login_Dialog( pWolapi ); + if( iLoginResult == 1 ) + { + pWolapi->SetOptionDefaults(); + bool bKeepGoing = true; + while( bKeepGoing ) + { + bool bCreator; + switch( WOL_Chat_Dialog( pWolapi ) ) + { + case -1: + bKeepGoing = false; + break; + case 1: + // User created game channel. + bCreator = true; + break; + case 2: + // User joined game channel. + bCreator = false; + break; + } + if( bKeepGoing ) + { + WOL_GameSetupDialog GSupDlg( pWolapi, bCreator ); + switch( GSupDlg.Run() ) + { + case RESULT_WOLGSUP_LOGOUT: + // User logged out. + bKeepGoing = false; + break; + case RESULT_WOLGSUP_BACKTOCHAT: + case RESULT_WOLGSUP_HOSTLEFT: + case RESULT_WOLGSUP_RULESMISMATCH: + // Return to chat. + break; + case RESULT_WOLGSUP_STARTGAMEHOST: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = true; + break; + case RESULT_WOLGSUP_STARTGAME: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = false; + break; + case RESULT_WOLGSUP_FATALERROR: +// debugprint( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); +// Fatal( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); + if( pWolapi->pChatSink->bConnected ) + pWolapi->Logout(); + bKeepGoing = false; + break; + } + } + } + } + + if( iReturn != 1 ) + { + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + else + { + pWolapi->bInGame = true; + pWolapi->bConnectionDown = false; + } + + if( iLoginResult == -1 ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADEXITWARNING ); + iReturn = -1; + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReregisterWolapiDLL() +{ + // Attempt to reregister wolapi.dll. + // Returns true if we think we succeeded. + HKEY hKey; + char szInstallPath[ _MAX_PATH ]; + if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Westwood\\WOLAPI", 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwBufSize = _MAX_PATH; + if( ::RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szInstallPath, &dwBufSize ) == ERROR_SUCCESS ) + { + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szInstallPath, &wfd ); + if( handle == INVALID_HANDLE_VALUE ) + { + // File is not there. + FindClose( handle ); + ::RegCloseKey( hKey ); + return false; + } +// debugprint( "Found dll -> %s\n", szInstallPath ); + // Get the DLL to register itself. + HINSTANCE hLib = LoadLibrary( szInstallPath ); + if( !hLib ) + { +// debugprint( "LoadLibrary failed, GetLastError is %i\n", GetLastError() ); + ::RegCloseKey( hKey ); + return false; + } + FARPROC lpDllRegisterFunction = GetProcAddress( hLib, "DllRegisterServer" ); + if( !lpDllRegisterFunction ) + { + ::RegCloseKey( hKey ); + return false; + } + if( lpDllRegisterFunction() != S_OK ) + { + ::RegCloseKey( hKey ); + return false; + } + // There is a bug in wolapi.dll that makes the following delay necessary. + // Something about Neal's extra threads only getting half-way set up before they get deleted. + // (The extra threads shouldn't really be created in this case, anyway...) + ::Sleep( 1000 ); + FreeLibrary( hLib ); + FindClose( handle ); + } + else + { + ::RegCloseKey( hKey ); + return false; + } + ::RegCloseKey( hKey ); + } + else + { + return false; + } + return true; +} + +//*********************************************************************************************** +void HandleDLLFail() +{ + // The DLL failed to load. Either we failed to reregister it, or we think we succeeded at this but it + // still is not working. Show an error message and delete pWolapi. + // We show either "call tech support" or "download IE3", depending on whether oleaut32.dll looks out of date. + + char szPath[ _MAX_PATH + 1 ]; + ::GetSystemDirectory( szPath, _MAX_PATH ); + if( *szPath && szPath[ strlen( szPath ) - 1 ] != '\\' ) + strcat( szPath, "\\" ); + + strcat( szPath, "oleaut32.dll" ); + + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szPath, &wfd ); + +// debugprint( "HandleDLLFail(): filesize of oleaut32 is %i\n", wfd.nFileSizeLow ); + if( handle != INVALID_HANDLE_VALUE && wfd.nFileSizeLow <= 232720 ) + WWMessageBox().Process( TXT_WOL_DLLERROR_GETIE3 ); + else + WWMessageBox().Process( TXT_WOL_DLLERROR_CALLUS ); + + FindClose( handle ); + + delete pWolapi; + pWolapi = NULL; +} + +#endif diff --git a/REDALERT/WOL_OPT.CPP b/REDALERT/WOL_OPT.CPP new file mode 100644 index 000000000..04bf76cdb --- /dev/null +++ b/REDALERT/WOL_OPT.CPP @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Opt.cpp - WW online options dialog. +// ajw 09/1/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "BigCheck.h" +//#include "WolDebug.h" + +extern bool cancel_current_msgbox; + +//*********************************************************************************************** +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ) +{ + // Returns true only if called from inside game, and the game ended on us unexpectedly. + bool bReturn = false; + + bool bEscapeDown = false; + bool bReturnDown = false; + + bool bIgnoreReturnDown = false; + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + // The return key is already down, as we enter the dialog. + // Until it comes up again, ignore this fact, so that we don't act on a return press that's not valid. + bIgnoreReturnDown = true; + } + + /* + ** Dialog & button dimensions + */ +#ifdef GERMAN + int d_list_w = 180 * RESFACTOR; +#else +#ifdef FRENCH + int d_list_w = 165 * RESFACTOR; +#else + int d_list_w = 165 * RESFACTOR; +#endif +#endif + + int d_dialog_w = d_list_w + 40 * RESFACTOR; // dialog width + int d_dialog_h = 90 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + +// int d_list_w = 100 * RESFACTOR; + int d_list_h = 7 * RESFACTOR; + int d_list_x = d_dialog_cx - d_list_w / 2; + int d_list_y = d_dialog_y + d_margin + 24; + +#if (GERMAN | FRENCH) + int d_ok_w = 40 * RESFACTOR; +#else + int d_ok_w = 40 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_cx - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + CHECK_FIND, + CHECK_PAGE, + CHECK_LANGUAGE, + CHECK_ALLGAMES, + CHECK_RANKAM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + + BigCheckBoxClass FindCheck( CHECK_FIND, d_list_x, d_list_y, d_list_w, d_list_h, + TXT_WOL_OPTFIND, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bFindEnabled ); + BigCheckBoxClass PageCheck( CHECK_PAGE, d_list_x, d_list_y + d_list_h + 2, d_list_w, d_list_h, + TXT_WOL_OPTPAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bPageEnabled ); + BigCheckBoxClass LanguageCheck( CHECK_LANGUAGE, d_list_x, d_list_y + 2 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTLANGUAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bLangFilter ); + BigCheckBoxClass GamescopeCheck( CHECK_ALLGAMES, d_list_x, d_list_y + 3 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTGAMESCOPE, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bAllGamesShown ); + BigCheckBoxClass RankAMCheck( CHECK_RANKAM, d_list_x, d_list_y + 4 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTRANKAM, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bShowRankRA ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + FindCheck.Add_Tail(*commands); + PageCheck.Add_Tail(*commands); + LanguageCheck.Add_Tail(*commands); + GamescopeCheck.Add_Tail(*commands); + RankAMCheck.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if( !bCalledFromGame ) + Call_Back(); + else + { + if( Main_Loop() ) // Game ended on us in the background. + { + process = false; + bReturn = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_OPTTITLE, d_dialog_x, d_dialog_y, d_dialog_w ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + if( !bIgnoreReturnDown ) + bReturnDown = true; + } + else + { + bIgnoreReturnDown = false; + if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + } + + /* + ** Process input. + */ + + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + } + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + process = false; + break; + + case ( CHECK_FIND | KN_BUTTON ): + case ( CHECK_PAGE | KN_BUTTON ): + case ( CHECK_LANGUAGE | KN_BUTTON ): + case ( CHECK_ALLGAMES | KN_BUTTON ): + pWO->SetOptions( FindCheck.IsOn, PageCheck.IsOn, LanguageCheck.IsOn, !GamescopeCheck.IsOn ); + break; + + case ( CHECK_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = !RankAMCheck.IsOn; + pWO->bMyRecordUpdated = true; + pWO->bShowRankUpdated = true; + break; + + default: + break; + } + } + return bReturn; +} + +#endif diff --git a/REDALERT/WSNWLINK.H b/REDALERT/WSNWLINK.H new file mode 100644 index 000000000..86d80c169 --- /dev/null +++ b/REDALERT/WSNWLINK.H @@ -0,0 +1,294 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + * wsnwlink.h + * + * + * Microsoft Windows + * Copyright (C) Microsoft Corporation, 1992-1996. + * Microsoft-specific extensions to the Windows NT IPX/SPX Windows + * Sockets interface. These extensions are provided for use as + * necessary for compatibility with existing applications. They are + * otherwise not recommended for use, as they are only guaranteed to + * work * over the Microsoft IPX/SPX stack. An application which + * uses these * extensions may not work over other IPX/SPX + * implementations. Include this header file after winsock.h and + * wsipx.h. + * + * To open an IPX socket where a particular packet type is sent in + * the IPX header, specify NSPROTO_IPX + n as the protocol parameter + * of the socket() API. For example, to open an IPX socket that + * sets the packet type to 34, use the following socket() call: + * + * s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX + 34); + * + * Below are socket option that may be set or retrieved by specifying + * the appropriate manifest in the "optname" parameter of getsockopt() + * or setsockopt(). Use NSPROTO_IPX as the "level" argument for the + * call. + * + */ + +#ifndef _WSNWLINK_ +#define _WSNWLINK_ + + +/* + * Set/get the IPX packet type. The value specified in the + * optval argument will be set as the packet type on every IPX + * packet sent from this socket. The optval parameter of + * getsockopt()/setsockopt() points to an int. + * + */ + +#define IPX_PTYPE 0x4000 + + +/* + * Set/get the receive filter packet type. Only IPX packets with + * a packet type equal to the value specified in the optval + * argument will be returned; packets with a packet type that + * does not match are discarded. optval points to an int. + * + */ + +#define IPX_FILTERPTYPE 0x4001 + + +/* + * Stop filtering on packet type set with IPX_FILTERPTYPE. + * + */ + +#define IPX_STOPFILTERPTYPE 0x4003 + + +/* + * Set/get the value of the datastream field in the SPX header on + * every packet sent. optval points to an int. + * + */ + +#define IPX_DSTYPE 0x4002 + + +/* + * Enable extended addressing. On sends, adds the element + * "unsigned char sa_ptype" to the SOCKADDR_IPX structure, + * making the total length 15 bytes. On receives, add both + * the sa_ptype and "unsigned char sa_flags" to the SOCKADDR_IPX + * structure, making the total length 16 bytes. The current + * bits defined in sa_flags are: + * + * 0x01 - the received frame was sent as a broadcast + * 0x02 - the received frame was sent from this machine + * + * optval points to a BOOL. + * + */ + +#define IPX_EXTENDED_ADDRESS 0x4004 + + +/* + * Send protocol header up on all receive packets. optval points + * to a BOOL. + * + */ + +#define IPX_RECVHDR 0x4005 + + +/* + * Get the maximum data size that can be sent. Not valid with + * setsockopt(). optval points to an int where the value is + * returned. + * + */ + +#define IPX_MAXSIZE 0x4006 + + +/* + * Query information about a specific adapter that IPX is bound + * to. In a system with n adapters they are numbered 0 through n-1. + * Callers can issue the IPX_MAX_ADAPTER_NUM getsockopt() to find + * out the number of adapters present, or call IPX_ADDRESS with + * increasing values of adapternum until it fails. Not valid + * with setsockopt(). optval points to an instance of the + * IPX_ADDRESS_DATA structure with the adapternum filled in. + * + */ + +#define IPX_ADDRESS 0x4007 + +typedef struct _IPX_ADDRESS_DATA { + INT adapternum; /* input: 0-based adapter number */ + UCHAR netnum[4]; /* output: IPX network number */ + UCHAR nodenum[6]; /* output: IPX node address */ + BOOLEAN wan; /* output: TRUE = adapter is on a wan link */ + BOOLEAN status; /* output: TRUE = wan link is up (or adapter is not wan) */ + INT maxpkt; /* output: max packet size, not including IPX header */ + ULONG linkspeed; /* output: link speed in 100 bytes/sec (i.e. 96 == 9600 bps) */ +} IPX_ADDRESS_DATA, *PIPX_ADDRESS_DATA; + + +/* + * Query information about a specific IPX network number. If the + * network is in IPX's cache it will return the information directly, + * otherwise it will issue RIP requests to find it. Not valid with + * setsockopt(). optval points to an instance of the IPX_NETNUM_DATA + * structure with the netnum filled in. + * + */ + +#define IPX_GETNETINFO 0x4008 + +typedef struct _IPX_NETNUM_DATA { + UCHAR netnum[4]; /* input: IPX network number */ + USHORT hopcount; /* output: hop count to this network, in machine order */ + USHORT netdelay; /* output: tick count to this network, in machine order */ + INT cardnum; /* output: 0-based adapter number used to route to this net */ + /* can be used as adapternum input to IPX_ADDRESS */ + UCHAR router[6]; /* output: MAC address of the next hop router, zeroed if */ + /* the network is directly attached */ +} IPX_NETNUM_DATA, *PIPX_NETNUM_DATA; + + +/* + * Like IPX_GETNETINFO except it *does not* issue RIP requests. If the + * network is in IPX's cache it will return the information, otherwise + * it will fail (see also IPX_RERIPNETNUMBER which *always* forces a + * re-RIP). Not valid with setsockopt(). optval points to an instance of + * the IPX_NETNUM_DATA structure with the netnum filled in. + * + */ + +#define IPX_GETNETINFO_NORIP 0x4009 + + +/* + * Get information on a connected SPX socket. optval points + * to an instance of the IPX_SPXCONNSTATUS_DATA structure. + * + * All numbers are in Novell (high-low) order. + * + */ + +#define IPX_SPXGETCONNECTIONSTATUS 0x400B + +typedef struct _IPX_SPXCONNSTATUS_DATA { + UCHAR ConnectionState; + UCHAR WatchDogActive; + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + USHORT LocalSequenceNumber; + USHORT LocalAckNumber; + USHORT LocalAllocNumber; + USHORT RemoteAckNumber; + USHORT RemoteAllocNumber; + USHORT LocalSocket; + UCHAR ImmediateAddress[6]; + UCHAR RemoteNetwork[4]; + UCHAR RemoteNode[6]; + USHORT RemoteSocket; + USHORT RetransmissionCount; + USHORT EstimatedRoundTripDelay; /* In milliseconds */ + USHORT RetransmittedPackets; + USHORT SuppressedPacket; +} IPX_SPXCONNSTATUS_DATA, *PIPX_SPXCONNSTATUS_DATA; + + +/* + * Get notification when the status of an adapter that IPX is + * bound to changes. Typically this will happen when a wan line + * goes up or down. Not valid with setsockopt(). optval points + * to a buffer which contains an IPX_ADDRESS_DATA structure + * followed immediately by a HANDLE to an unsignaled event. + * + * When the getsockopt() query is submitted, it will complete + * successfully. However, the IPX_ADDRESS_DATA pointed to by + * optval will not be updated at that point. Instead the + * request is queued internally inside the transport. + * + * When the status of an adapter changes, IPX will locate a + * queued getsockopt() query and fill in all the fields in the + * IPX_ADDRESS_DATA structure. It will then signal the event + * pointed to by the HANDLE in the optval buffer. This handle + * should be obtained before calling getsockopt() by calling + * CreateEvent(). If multiple getsockopts() are submitted at + * once, different events must be used. + * + * The event is used because the call needs to be asynchronous + * but currently getsockopt() does not support this. + * + * WARNING: In the current implementation, the transport will + * only signal one queued query for each status change. Therefore + * only one service which uses this query should be running at + * once. + * + */ + +#define IPX_ADDRESS_NOTIFY 0x400C + + +/* + * Get the maximum number of adapters present. If this call returns + * n then the adapters are numbered 0 through n-1. Not valid + * with setsockopt(). optval points to an int where the value + * is returned. + * + */ + +#define IPX_MAX_ADAPTER_NUM 0x400D + + +/* + * Like IPX_GETNETINFO except it forces IPX to re-RIP even if the + * network is in its cache (but not if it is directly attached to). + * Not valid with setsockopt(). optval points to an instance of + * the IPX_NETNUM_DATA structure with the netnum filled in. + * + */ + +#define IPX_RERIPNETNUMBER 0x400E + + +/* + * A hint that broadcast packets may be received. The default is + * TRUE. Applications that do not need to receive broadcast packets + * should set this sockopt to FALSE which may cause better system + * performance (note that it does not necessarily cause broadcasts + * to be filtered for the application). Not valid with getsockopt(). + * optval points to a BOOL. + * + */ + +#define IPX_RECEIVE_BROADCAST 0x400F + + +/* + * On SPX connections, don't delay before sending ack. Applications + * that do not tend to have back-and-forth traffic over SPX should + * set this; it will increase the number of acks sent but will remove + * delays in sending acks. optval points to a BOOL. + * + */ + +#define IPX_IMMEDIATESPXACK 0x4010 + +#endif + diff --git a/REDALERT/WSPIPX.CPP b/REDALERT/WSPIPX.CPP new file mode 100644 index 000000000..2d5107d5f --- /dev/null +++ b/REDALERT/WSPIPX.CPP @@ -0,0 +1,446 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPIPX.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 6 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * * + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wspipx.h" +#include "ipxaddr.h" + +#include +#include + + +/*********************************************************************************************** + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 11:41AM ST : Created * + *=============================================================================================*/ +IPXInterfaceClass::IPXInterfaceClass (void) : WinsockInterfaceClass() +{ + /* + ** Set the net and node addressed to their default values. + */ + memset ( BroadcastNet, 0xff, sizeof (BroadcastNet) ); + memset ( BroadcastNode, 0xff, sizeof (BroadcastNode) ); + memset ( MyNode, 0xff, sizeof (MyNode) ); +} + + +/*********************************************************************************************** + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * * + * * + * * + * INPUT: card number to retrieve ID for * + * ptr to addr to return ID in * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/1/97 3:04PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr) +{ + int cbOpt; + int cbAddr = sizeof( SOCKADDR_IPX ); + SOCKET s; + SOCKADDR_IPX Addr; + IPX_ADDRESS_DATA IpxData; + + /* + ** Create a temporary IPX socket. + */ + s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ); + if ( s == SOCKET_ERROR ) { + assert ( s != SOCKET_ERROR ); + return (false); + } + + /* + ** Socket must be bound prior to calling IPX_MAX_ADAPTER_NUM + */ + memset( &Addr, 0, sizeof( Addr )); + Addr.sa_family = AF_IPX; + int err = bind( s, (SOCKADDR*) &Addr, cbAddr); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + memset( &IpxData, 0, sizeof(IpxData)); + + /* + ** Specify which adapter to check. + */ + IpxData.adapternum = card_number; + cbOpt = sizeof( IpxData ); + + /* + ** Get information for the current adapter. + */ + err = getsockopt( s, NSPROTO_IPX, IPX_ADDRESS, (char*) &IpxData, &cbOpt ); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + /* + ** IpxData contains the address for the current adapter. + ** The network number will be needed later for broadcasts as the net number ff,ff,ff,ff + ** doesn't work under NT. + ** + ** Note: Due to a bug in Win95s implementation of Winsock, only the netnum & nodenum + ** values are correctly returned. NT returns all expected values. ST - 7/31/97 0:57AM + */ + memcpy (addr->sa_netnum, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (BroadcastNet, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (addr->sa_nodenum, IpxData.nodenum, sizeof (addr->sa_nodenum)); + + closesocket (s); + return (true); +} + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * * + * * + * * + * INPUT: SOCKET number to open. This is usually VIRGIN_SOCKET * + * * + * OUTPUT: true if socket was opened without error * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:54PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Open_Socket( SOCKET socketnum ) +{ + SOCKADDR_IPX addr; + bool delay = true; + int err; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + IPXSocketNumber = socketnum; + + /* + ** Set up the addr structure for the IPX socket + */ + addr.sa_family = AF_IPX; + memset (addr.sa_netnum, 0, sizeof (addr.sa_netnum)); + memset (addr.sa_nodenum, -1, sizeof (addr.sa_nodenum)); + addr.sa_socket = htons ( socketnum ); + + /* + ** Create the socket. + */ + Socket = socket (AF_NS, SOCK_DGRAM, NSPROTO_IPX); + if (Socket == INVALID_SOCKET) { + char out[128]; + sprintf (out, "TS: Failed to create IPX socket - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( Socket != INVALID_SOCKET ); + closesocket(Socket); + return ( false ); + } + + /* + ** Get the network card address. This is needed so we can bind the socket to the net card. + */ + if ( !Get_Network_Card_Address (0, &addr)){ + closesocket ( Socket ); + return ( false ); + } + + /* + ** Bind the IPX socket to the network card. + */ + if (bind ( Socket, (const struct sockaddr *) &addr, 16) == SOCKET_ERROR ){ + char out[128]; + sprintf (out, "TS: IPX socket bind failed with error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + closesocket(Socket); + return ( false );; + } + + + /* + ** Set the various options for this IPX socket + */ + unsigned long optval = true; + int packet_type = 4; + + /* + ** The SO_BROADCAST option allows broadcasting on this socket. This shouldn't be needed + ** except for the bug in the Win95 implementation of Winsock which causes broadcasts to + ** fail if it isn't set. + */ + if ( setsockopt ( Socket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval) ) == SOCKET_ERROR ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_BROADCAST - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + } + + /* + ** Set the value in the packet type field for outgoing packets. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_PTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_PTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Ignore all incoming packets not of this type. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_FILTERPTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_FILTERTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Set the the base class socket options for buffer sizes. + */ + WinsockInterfaceClass::Set_Socket_Options(); + + /* + ** Woohoo! + */ + return ( true ); +} + + + + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * * + * * + * * + * INPUT: Usual windoze message handler stuff * + * * + * OUTPUT: 0 if message was handled * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:55PM ST : Created * + *=============================================================================================*/ +long IPXInterfaceClass::Message_Handler(HWND , UINT message, UINT , LONG lParam) +{ + + int addr_len; // Length of address structure + int rc; // Result code + SOCKADDR_IPX addr; // Winsock IPX addressing structure + WinsockBufferType *packet; // Ptr to packet + NetNumType netnum; + NetNodeType nodenum; + + + /* + ** We only handle IPX events. + */ + if ( message != WM_IPXASYNCEVENT ) return ( 1 ); + + + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return(0); + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*) ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len ); + if (rc == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + } + return(0); + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make a copy of the address that this packet came from. + */ + memcpy ( netnum, addr.sa_netnum, sizeof (netnum) ); + memcpy ( nodenum, addr.sa_nodenum, sizeof (nodenum) ); + + /* + ** If this packet was from me then ignore it. + */ + if ( !memcmp (netnum, BroadcastNet, sizeof (BroadcastNet)) && !memcmp(nodenum, MyNode, sizeof (MyNode)) ) { + return (0); + } + + /* + ** Create a new buffer and store this packet in it. + */ + packet = new WinsockBufferType; + packet->BufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Set_Address ( netnum, nodenum ); + InBuffers.Add ( packet ); + } + return(0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error ( Socket ); + return(0); + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + while ( OutBuffers.Count() != 0 ) { + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sa_family = AF_IPX; + addr.sa_socket = htons ( IPXSocketNumber ); + + /* + ** Set up the address as either a broadcast address or the given address + */ + if ( packet->IsBroadcast ) { + memcpy ( addr.sa_netnum, BroadcastNet, sizeof (BroadcastNet) ); + memcpy ( addr.sa_nodenum, BroadcastNode, sizeof (BroadcastNode) ); + }else{ + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Get_Address ( netnum, nodenum ); + memcpy ( addr.sa_netnum, netnum, sizeof (netnum) ); + memcpy ( addr.sa_nodenum, nodenum, sizeof (nodenum) ); + } + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + break; + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + } + + return(0); + } + + return (0); +} + + diff --git a/REDALERT/WSPIPX.H b/REDALERT/WSPIPX.H new file mode 100644 index 000000000..2ee300e75 --- /dev/null +++ b/REDALERT/WSPIPX.H @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPIPX.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPIPX_H +#define WSPIPX_H + +#include "WSProto.h" + +/* +** Include Windows specific extensions for Winsock that allow IPX over winsock 1.1 +*/ +#include + +/* +** This file normally resides with the SDK. However, since it needs fixing up before watcom will +** compile it, it has been incorporated into the project. +*/ +#include "wsnwlink.h" + +/* +** IPX interface class. This handles access to the IPX specific portions of the +** Winsock interface. +** +*/ +class IPXInterfaceClass : public WinsockInterfaceClass { + + public: + + IPXInterfaceClass (void); + //virtual ~IPXInterfaceClass(void){Close();}; + bool Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr); + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_IPX); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_IPXASYNCEVENT); + }; + + + private: + /* + ** The address of the network we will send broadcasts to. Normally you would expect + ** this to be ff,ff,ff,ff but this fails under NT 4.0. Instead, we can use the network + ** number of the net that this PC is attached to. This limits broadcasts to the current + ** network. + */ + unsigned char BroadcastNet[4]; + + /* + ** The node to use as a broadcast address. Normally ff,ff,ff,ff,ff,ff. + */ + unsigned char BroadcastNode[6]; + + /* + ** The id of the network cars in this machine. + */ + unsigned char MyNode[6]; + + /* + ** The socket number to connect with. Normally this will be virgins reserved socket + ** number - VIRGIN_SOCKET (0x8813). + */ + SOCKET IPXSocketNumber; + +}; + + + +#endif \ No newline at end of file diff --git a/REDALERT/WSPROTO.CPP b/REDALERT/WSPROTO.CPP new file mode 100644 index 000000000..e84f411d7 --- /dev/null +++ b/REDALERT/WSPROTO.CPP @@ -0,0 +1,591 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 5 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * WIC::Close -- Releases any currently in use Winsock resources. * + * WIC::Close_Socket -- Close the communication socket if its open * + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * WIC::Stop_Listening -- Disable the winsock event callback * + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * WIC::Init -- Initialised Winsock and this class for use. * + * WIC::Read -- read any pending input from the communications socket * + * WIC::WriteTo -- Send data via the Winsock socket * + * WIC::Broadcast -- Send data via the Winsock socket * + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "WSProto.h" + +#include + + +/*********************************************************************************************** + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::WinsockInterfaceClass(void) +{ + WinsockInitialised = false; + ASync = INVALID_HANDLE_VALUE; + Socket = INVALID_SOCKET; +} + + +/*********************************************************************************************** + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::~WinsockInterfaceClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * WIC::Close -- Releases any currently in use Winsock resources. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + Stop_Listening(); + + /* + ** Close any open sockets + */ + Close_Socket(); + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = false; +} + + + +/*********************************************************************************************** + * WIC::Close_Socket -- Close the communication socket if its open * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:53AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close_Socket (void) +{ + if ( Socket != INVALID_SOCKET ) { + closesocket (Socket); + Socket = INVALID_SOCKET; + } +} + + + +/*********************************************************************************************** + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:54AM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Start_Listening (void) +{ + /* + ** Enable asynchronous events on the socket + */ + if ( WSAAsyncSelect ( Socket, MainWindow, Protocol_Event_Message(), FD_READ | FD_WRITE) == SOCKET_ERROR ){ + WWDebugString ( "TS: Async select failed.\n" ); + assert (false); + WSACancelAsyncRequest(ASync); + ASync = INVALID_HANDLE_VALUE; + return (false); + } + return (true); +} + + +/*********************************************************************************************** + * WIC::Stop_Listening -- Disable the winsock event callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:06PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Stop_Listening (void) +{ + if ( ASync != INVALID_HANDLE_VALUE ) { + WSACancelAsyncRequest ( ASync ); + ASync = INVALID_HANDLE_VALUE; + } +} + + + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_In_Buffers (void) +{ + WinsockBufferType *packet; + + while ( InBuffers.Count() ) { + packet = InBuffers [ 0 ]; + delete packet; + InBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_Out_Buffers (void) +{ + WinsockBufferType *packet; + + while ( OutBuffers.Count() ) { + packet = OutBuffers [ 0 ]; + delete packet; + OutBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Init -- Initialised Winsock and this class for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (true); + + /* + ** Create a buffer much larger than the sizeof (WSADATA) would indicate since Bounds Checker + ** says that a buffer of that size gets overrun. + */ + char *buffer = new char [sizeof (WSADATA) + 1024]; + WSADATA *winsock_info = (WSADATA*) (&buffer[0]); + + /* + ** Initialise socket and event handle to null + */ + Socket =INVALID_SOCKET; + ASync = INVALID_HANDLE_VALUE; + Discard_In_Buffers(); + Discard_Out_Buffers(); + + /* + ** Start WinSock, and fill in our Winsock info structure + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, winsock_info); + if (rc != 0) { + char out[128]; + sprintf (out, "TS: Winsock failed to initialise - error code %d.\n", GetLastError() ); + OutputDebugString (out); + delete [] buffer; + return (false); + } + + /* + ** Check the Winsock version number + */ + if ((winsock_info->wVersion & 0x00ff) != (version & 0x00ff) || + (winsock_info->wVersion >> 8) != (version >> 8)) { + OutputDebugString ("TS: Winsock version is less than 1.1\n" ); + delete [] buffer; + return (false); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = true; + + delete [] buffer; + return (true); + +} + + + + +/*********************************************************************************************** + * WIC::Read -- read any pending input from the communications socket * + * * + * * + * * + * INPUT: ptr to buffer to receive input * + * length of buffer * + * ptr to address to fill with address that packet was sent from * + * length of address buffer * + * * + * OUTPUT: number of bytes transfered to buffer * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * * + * HISTORY: * + * 3/20/96 2:58PM ST : Created * + *=============================================================================================*/ +int WinsockInterfaceClass::Read(void *buffer, int &buffer_len, void *address, int &address_len) +{ + address_len = address_len; + /* + ** Call the message loop in case there are any outstanding winsock READ messages. + */ + Keyboard->Check(); + + /* + ** If there are no available packets then return 0 + */ + if ( InBuffers.Count() == 0 ) return (0); + + /* + ** Get the oldest packet for reading + */ + int packetnum = 0; + WinsockBufferType *packet = InBuffers [packetnum]; + + assert ( buffer_len >= packet->BufferLen ); + assert ( address_len >= sizeof (packet->Address) ); + + /* + ** Copy the data and the address it came from into the supplied buffers. + */ + memcpy ( buffer, packet->Buffer, packet->BufferLen ); + memcpy ( address, packet->Address, sizeof (packet->Address) ); + + /* + ** Return the length of the packet in buffer_len. + */ + buffer_len = packet->BufferLen; + + /* + ** Delete the temporary storage for the packet now that it is being passed to the game. + */ + InBuffers.Delete ( packetnum ); + delete packet; + + return ( buffer_len ); +} + + + + +/*********************************************************************************************** + * WIC::WriteTo -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * address to send data to. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::WriteTo(void *buffer, int buffer_len, void *address) +{ + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + packet->IsBroadcast = false; +// memcpy ( packet->Address, address, sizeof (packet->Address) ); + memcpy ( packet->Address, address, sizeof( IPXAddressClass ) ); // Steve Tall has revised WriteTo due to this bug. + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * * + * * + * * + * INPUT: Socket * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:05PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Clear_Socket_Error(SOCKET socket) +{ + unsigned long error_code; + int length = 4; + + getsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length); + error_code = 0; + setsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, length); +} + + + + + + + +/*********************************************************************************************** + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:07PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Set_Socket_Options ( void ) +{ + static int socket_transmit_buffer_size = SOCKET_BUFFER_SIZE; + static int socket_receive_buffer_size = SOCKET_BUFFER_SIZE; + + /* + ** Specify the size of the receive buffer. + */ + int err = setsockopt ( Socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_receive_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_RCVBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Specify the size of the send buffer. + */ + err = setsockopt ( Socket, SOL_SOCKET, SO_SNDBUF, (char*)&socket_transmit_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_SNDBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + return ( true ); +} + + diff --git a/REDALERT/WSPROTO.H b/REDALERT/WSPROTO.H new file mode 100644 index 000000000..146e05caa --- /dev/null +++ b/REDALERT/WSPROTO.H @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 4 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPROTO_H +#define WSPROTO_H + +#include "_WSProto.h" + +/* +** Include standard Winsock 1.0 header file. +*/ +#include + +/* +** Misc defines +*/ +#define WINSOCK_MINOR_VER 1 // Version of Winsock +#define WINSOCK_MAJOR_VER 1 // that we require + +//#define WS_RECEIVE_BUFFER_LEN 32768 // Length of our temporary receive buffer. Needs to be more that the max packet size which is about 550 bytes. +//#define SOCKET_BUFFER_SIZE 32768 // Length of winsocks internal buffer. +#define WS_RECEIVE_BUFFER_LEN 1024 // Length of our temporary receive buffer. +#define SOCKET_BUFFER_SIZE 1024*128 // Length of winsocks internal buffer. + +#define PLANET_WESTWOOD_HANDLE_MAX 20 // Max length of a WChat handle + +/* +** Define events for Winsock callbacks +*/ +#define WM_IPXASYNCEVENT (WM_USER + 115) // IPX socket Async event +#define WM_UDPASYNCEVENT (WM_USER + 116) // UDP socket Async event + + +/* +** Enum to identify the protocols supported by the Winsock interface. +*/ +typedef enum tProtocolEnum { + PROTOCOL_NONE, + PROTOCOL_IPX, + PROTOCOL_UDP +} ProtocolEnum; + + + +/* +** +** Class to interface with Winsock. This interface only supports connectionless packet protocols +** like UDP & IPX. Connection orientated or streaming protocols like TCP are not supported by this +** class. +** +*/ +class WinsockInterfaceClass { + + public: + + WinsockInterfaceClass(void); + virtual ~WinsockInterfaceClass(void); + + bool Init(void); + void Close(void); + + + virtual void Close_Socket(void); + virtual int Read(void *buffer, int &buffer_len, void *address, int &address_len); + virtual void WriteTo (void *buffer, int buffer_len, void *address); + virtual void Broadcast (void *buffer, int buffer_len); + virtual void Discard_In_Buffers (void); + virtual void Discard_Out_Buffers (void); + virtual bool Start_Listening (void); + virtual void Stop_Listening (void); + virtual void Clear_Socket_Error(SOCKET socket); + virtual bool Set_Socket_Options ( void ); + virtual void Set_Broadcast_Address ( void * ) {}; + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_NONE); + }; + + virtual int Protocol_Event_Message (void) { + return (0); + }; + + virtual bool Open_Socket ( SOCKET ) { + return (false); + }; + + virtual long Message_Handler(HWND, UINT, UINT, LONG) { + return (1); + } + + + typedef enum ConnectStatusEnum { + CONNECTED_OK = 0, + NOT_CONNECTING, + CONNECTING, + UNABLE_TO_CONNECT_TO_SERVER, + CONTACTING_SERVER, + SERVER_ADDRESS_LOOKUP_FAILED, + RESOLVING_HOST_ADDRESS, + UNABLE_TO_ACCEPT_CLIENT, + UNABLE_TO_CONNECT, + CONNECTION_LOST + } ConnectStatusEnum; + + inline ConnectStatusEnum Get_Connection_Status(void) {return (ConnectStatus);} + + protected: + + /* + ** This struct contains the information needed for each incoming and outgoing packet. + ** It acts as a temporary control for these packets. + */ + typedef struct tWinsockBufferType { + unsigned char Address [64]; // Address. IN_ADDR, IPXAddressClass etc. + int BufferLen; // Length of data in buffer + bool IsBroadcast; // Flag to broadcast this packet + unsigned char Buffer[1024]; // Buffer to store packet in. + } WinsockBufferType; + + /* + ** Array of buffers to temporarily store incoming and outgoing packets. + */ + DynamicVectorClass InBuffers; + DynamicVectorClass OutBuffers; + + + /* + ** Is Winsock present and initialised? + */ + bool WinsockInitialised; + + /* + ** Socket that communications will take place over. + */ + SOCKET Socket; + + /* + ** Async object required for callbacks to our message handler. + */ + HANDLE ASync; + + /* + ** Temporary receive buffer to use when querying Winsock for incoming packets. + */ + unsigned char ReceiveBuffer[WS_RECEIVE_BUFFER_LEN]; + + /* + ** Current connection status. + */ + ConnectStatusEnum ConnectStatus; +}; + + + + + + + +#endif //WSPROTO_H diff --git a/REDALERT/WSPUDP.CPP b/REDALERT/WSPUDP.CPP new file mode 100644 index 000000000..85151b9d5 --- /dev/null +++ b/REDALERT/WSPUDP.CPP @@ -0,0 +1,428 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPUDP.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "internet.h" +#include "WSPUDP.h" + +#include +#include +#include + + +/*********************************************************************************************** + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:11PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::UDPInterfaceClass (void) : WinsockInterfaceClass() +{} + + + +/*********************************************************************************************** + * UDPIC::~UDPInterfaceClass -- UDPInterface class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/9/97 12:17PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::~UDPInterfaceClass (void) +{ + while ( BroadcastAddresses.Count() ) { + delete BroadcastAddresses[0]; + BroadcastAddresses.Delete(0); + } + + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + Close(); +} + + +/*********************************************************************************************** + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * * + * * + * * + * INPUT: ptr to address in decimal dot format. i.e. xxx.xxx.xxx.xxx * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:12PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Set_Broadcast_Address (void *address) +{ + char* ip_addr = (char*) address; + assert ( strlen (ip_addr) <= strlen ( "xxx.xxx.xxx.xxx" ) ); + + unsigned char *baddr = new unsigned char[4]; + + sscanf ( ip_addr, "%hhu.%hhu.%hhu.%hhu", &baddr[0], &baddr[1], &baddr[2], &baddr[3] ); + BroadcastAddresses.Add (baddr); +} + + + +/*********************************************************************************************** + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * * + * * + * * + * INPUT: Socket number to use. Not required for this protocol. * + * * + * OUTPUT: True if socket was opened OK * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:13PM ST : Created * + *=============================================================================================*/ +bool UDPInterfaceClass::Open_Socket ( SOCKET ) +{ + LINGER ling; + struct sockaddr_in addr; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + /* + ** Create our UDP socket + */ + Socket = socket(AF_INET, SOCK_DGRAM, 0); + if (Socket == INVALID_SOCKET) { + return (false); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ( (unsigned short) PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl (INADDR_ANY); + + if ( bind (Socket, (LPSOCKADDR)&addr, sizeof(addr) ) == SOCKET_ERROR) { + Close_Socket (); + return (false); + } + + /* + ** Use gethostbyname to find the name of the local host. We will need this to look up + ** the local ip address. + */ + char hostname[128]; + gethostname(hostname, 128); + WWDebugString (hostname); + struct hostent *host_info = gethostbyname ( hostname ); + + /* + ** Clear out any old local addresses from the local address list. + */ + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + + /* + ** Add all local IP addresses to the list. This list will be used to discard any packets that + ** we send to ourselves. + */ + unsigned long **addresses = (unsigned long**) (host_info->h_addr_list); + + for ( ;; ) { + if ( !*addresses ) break; + + unsigned long address = **addresses++; + //address = ntohl (address); + + char temp[128]; + sprintf (temp, "RA95: Found local address: %d.%d.%d.%d\n", address & 0xff, (address & 0xff00) >> 8, (address & 0xff0000) >> 16, (address & 0xff000000) >> 24); + OutputDebugString (temp); + + unsigned char *a = new unsigned char [4]; + * ((unsigned long*) a) = address; + LocalAddresses.Add (a); + } + + /* + ** Set options for the UDP socket + */ + ling.l_onoff = 0; // linger off + ling.l_linger = 0; // timeout in seconds (ie close now) + setsockopt (Socket, SOL_SOCKET, SO_LINGER, (LPSTR)&ling, sizeof(ling)); + + WinsockInterfaceClass::Set_Socket_Options(); + + return (true); + + +} + + + + +/*********************************************************************************************** + * UDPIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + for ( int i=0 ; iBuffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Set up the send address for this packet. + */ + memset (packet->Address, 0, sizeof (packet->Address)); + memcpy (packet->Address+4, BroadcastAddresses[i], 4); + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); + } +} + + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ +long UDPInterfaceClass::Message_Handler(HWND, UINT message, UINT, LONG lParam) +{ + struct sockaddr_in addr; + int rc; + int addr_len; + WinsockBufferType *packet; + + /* + ** We only handle UDP events. + */ + if ( message != WM_UDPASYNCEVENT ) return (1); + + /* + ** Handle UDP packet events + */ + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*)ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make sure this packet didn't come from us. If it did then throw it away. + */ + for ( int i=0 ; iBufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + memset ( packet->Address, 0, sizeof (packet->Address) ); + memcpy ( packet->Address+4, &addr.sin_addr.s_addr, 4 ); + InBuffers.Add (packet); + } + return (0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + if ( OutBuffers.Count() == 0 ) return (0); + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ((unsigned short)PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, packet->Address+4, 4); + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + return (0); + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + return(0); + } + + return (0); +} diff --git a/REDALERT/WSPUDP.H b/REDALERT/WSPUDP.H new file mode 100644 index 000000000..cc7bb9137 --- /dev/null +++ b/REDALERT/WSPUDP.H @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPUDP.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPUDP_H +#define WSPUDP_H + +#include "WSProto.h" +#include + + + + +/* +** Class to allow access to UDP specific portions of the Winsock interface. +** +*/ +class UDPInterfaceClass : public WinsockInterfaceClass { + + public: + + UDPInterfaceClass (void); + virtual ~UDPInterfaceClass(void); + + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + virtual void Set_Broadcast_Address ( void *address ); + virtual void Broadcast (void *buffer, int buffer_len); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_UDP); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_UDPASYNCEVENT); + }; + + + private: + + /* + ** Address to use when broadcasting a packet. + */ + DynamicVectorClass BroadcastAddresses; + + /* + ** List of local addresses. + */ + DynamicVectorClass LocalAddresses; +}; + + + +#endif \ No newline at end of file diff --git a/REDALERT/WWALLOC.H b/REDALERT/WWALLOC.H new file mode 100644 index 000000000..c96a18576 --- /dev/null +++ b/REDALERT/WWALLOC.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\wwalloc.h_v 4.9 07 May 1996 17:14:00 JOE_BOSTIC $ */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** This should be located in the wwlib32.h file, but is located here for +** test purposes. +*/ +#ifdef __FLAT__ +#define PRIVATE static +#endif + +typedef enum MemoryFlagType { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_PUBLIC = 0x0000, // Default memory (normal). + MEM_CHIP = 0x0000, // Graphic & sound buffer memory (Amiga). + MEM_UNUSED = 0x0001, // + MEM_SYSTEM = 0x0002, // Allocate out of system heap (XMS or EMS only). + MEM_RELAXED= 0x0004, // Don't worry about page conservation in EMS. + MEM_TEMP = 0x0008, // Temporary allocation (used by library only). + MEM_CLEAR = 0x0010, // Fill memory with '\0' characters. + MEM_PARA = 0x0020, // Paragraph aligned (IBM only). + MEM_XMS = 0x0040, // XMS memory. + MEM_EMS = 0x0080, // EMS memory (not implemented). + MEM_X = 0x8000 // Here to force this enum to be unsigned sized. +} MemoryFlagType; +MemoryFlagType operator |(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator &(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator ~(MemoryFlagType); + + +/* Prototypes for functions defined in this file */ +void *cdecl Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void cdecl Free(void const *pointer); +void *cdecl Resize_Alloc(void const *original_ptr, unsigned long new_size_in_bytes); +long cdecl Ram_Free(MemoryFlagType flag); +long cdecl Total_Ram_Free(MemoryFlagType flag); +long cdecl Heap_Size(MemoryFlagType flag); + +extern unsigned long cdecl MinRam; // Record of least memory at worst case. +extern unsigned long cdecl MaxRam; // Record of total allocated at worst case. + +#ifdef __cplusplus +} +#endif + diff --git a/REDALERT/WWFILE.H b/REDALERT/WWFILE.H new file mode 100644 index 000000000..33feded04 --- /dev/null +++ b/REDALERT/WWFILE.H @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WWFILE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_Hx +#define WWFILE_Hx + +#define YEAR(dt) (((dt & 0xFE000000) >> (9 + 16)) + 1980) +#define MONTH(dt) ((dt & 0x01E00000) >> (5 + 16)) +#define DAY(dt) ((dt & 0x001F0000) >> (0 + 16)) +#define HOUR(dt) ((dt & 0x0000F800) >> 11) +#define MINUTE(dt) ((dt & 0x000007E0) >> 5) +#define SECOND(dt) ((dt & 0x0000001F) << 1) + +#include +#include + +#ifndef READ +#define READ 1 +#endif +#ifndef WRITE +#define WRITE 2 +#endif + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(false);} + virtual void Error(int error, int canretry = false, char const * filename=NULL) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/REDALERT/XPIPE.CPP b/REDALERT/XPIPE.CPP new file mode 100644 index 000000000..7781d5a40 --- /dev/null +++ b/REDALERT/XPIPE.CPP @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XPIPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * FilePipe::Put -- Submit a block of data to the pipe. * + * FilePipe::End -- End the file pipe handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "FUNCTION.H" +#include "xpipe.h" +#include +#include + + +//--------------------------------------------------------------------------------------------------------- +// BufferPipe +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * * + * The buffered pipe is a pipe terminator. That is, the data never flows onto subsequent * + * pipe chains. The data is stored into the buffer previously submitted to the pipe. * + * If the buffer is full, no more data is output to the buffer. * + * * + * INPUT: source -- Pointer to the data to submit. * + * * + * length -- The number of bytes to be submitted. * + * * + * OUTPUT: Returns with the number of bytes output to the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferPipe::Put(void const * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(((char *)BufferPtr.Get_Buffer()) + Index, source, len); + } + + Index += len; +// Length -= len; +// Buffer = ((char *)Buffer) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FilePipe +//--------------------------------------------------------------------------------------------------------- + +FilePipe::~FilePipe(void) +{ + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + File = NULL; + } +} + + +/*********************************************************************************************** + * FilePipe::End -- End the file pipe handler. * + * * + * This routine is called when there will be no more data sent through the pipe. It is * + * responsible for cleaning up anything it needs to. This is not handled by the * + * destructor, although it serves a similar purpose, because pipe are linked together and * + * the destructor order is not easily controlled. If the destructors for a pipe chain were * + * called out of order, the result might be less than pleasant. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes flushed out the final end of the pipe as a * + * consequence of this routine. * + * * + * WARNINGS: Don't send any more data through the pipe after this routine is called. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::End(void) +{ + int total = Pipe::End(); + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + } + return(total); +} + + +/*********************************************************************************************** + * FilePipe::Put -- Submit a block of data to the pipe. * + * * + * Takes the data block submitted and writes it to the file. If the file was not already * + * open, this routine will open it for write. * + * * + * INPUT: source -- Pointer to the data to submit to the file. * + * * + * length -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::Put(void const * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + File->Open(WRITE); + } + + return(File->Write(source, slen)); + } + return(0); +} diff --git a/REDALERT/XPIPE.H b/REDALERT/XPIPE.H new file mode 100644 index 000000000..ff85dcc5d --- /dev/null +++ b/REDALERT/XPIPE.H @@ -0,0 +1,92 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XPIPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef XPIPE_H +#define XPIPE_H + +#include "pipe.h" +#include "wwfile.h" +#include "buff.h" + +/* +** This is a simple store-into-buffer pipe terminator. Use it as the final link in a pipe process +** that needs to store the data into a memory buffer. This can only serve as the final +** link in the chain of pipe segments. +*/ +class BufferPipe : public Pipe +{ + public: + BufferPipe(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferPipe(void * buffer, int length) : BufferPtr(buffer, length), Index(0) {} + virtual int Put(void const * source, int slen); + + private: + Buffer BufferPtr; + int Index; + +// void * Buffer; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferPipe(BufferPipe & rvalue); + BufferPipe & operator = (BufferPipe const & pipe); +}; + + +/* +** This is a store-to-file pipe terminator. Use it as the final link in a pipe process that +** needs to store the data to a file. This can only serve as the last link in the chain +** of pipe segments. +*/ +class FilePipe : public Pipe +{ + public: + FilePipe(FileClass * file) : File(file), HasOpened(false) {} + FilePipe(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FilePipe(void); + + virtual int Put(void const * source, int slen); + virtual int End(void); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FilePipe(FilePipe & rvalue); + FilePipe & operator = (FilePipe const & pipe); + +}; + +#endif diff --git a/REDALERT/XSTRAW.CPP b/REDALERT/XSTRAW.CPP new file mode 100644 index 000000000..14e375285 --- /dev/null +++ b/REDALERT/XSTRAW.CPP @@ -0,0 +1,148 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XSTRAW.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * FileStraw::Get -- Fetch data from the file. * + * FileStraw::~FileStraw -- The destructor for the file straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "FUNCTION.H" +#include "xstraw.h" +#include +#include + +//--------------------------------------------------------------------------------------------------------- +// BufferStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * * + * This routine will copy the requested number of bytes from the buffer holding tank (as * + * set up by the straw's constructor) to the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to be filled with data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes copied to the buffer. If this is less than * + * requested, then it indicates that the data holding tank buffer is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(source, ((char*)BufferPtr.Get_Buffer()) + Index, len); + } + + Index += len; +// Length -= len; +// BufferPtr = ((char *)BufferPtr) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FileStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * FileStraw::Get -- Fetch data from the file. * + * * + * This routine will read data from the file (as specified in the straw's constructor) into * + * the buffer indicated. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If this number is less * + * than the number requested, then this indicates that the file is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FileStraw::Get(void * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + if (!File->Is_Available()) return(0); + if (!File->Open(READ)) return(0); + } + + return(File->Read(source, slen)); + } + return(0); +} + + +/*********************************************************************************************** + * FileStraw::~FileStraw -- The destructor for the file straw. * + * * + * This destructor only needs to close the file if it was the one to open it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +FileStraw::~FileStraw(void) +{ + if (Valid_File() && HasOpened) { + File->Close(); + HasOpened = false; + File = NULL; + } +} diff --git a/REDALERT/XSTRAW.H b/REDALERT/XSTRAW.H new file mode 100644 index 000000000..7a3361a46 --- /dev/null +++ b/REDALERT/XSTRAW.H @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XSTRAW.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef XSTRAW_H +#define XSTRAW_H + +#include "straw.h" +#include "buff.h" +#include "wwfile.h" +#include + +/* +** This class is used to manage a buffer as a data source. Data requests will draw from the +** buffer supplied until the buffer is exhausted. +*/ +class BufferStraw : public Straw +{ + public: + BufferStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferStraw(void const * buffer, int length) : BufferPtr((void*)buffer, length), Index(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; +// void const * BufferPtr; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferStraw(BufferStraw & rvalue); + BufferStraw & operator = (BufferStraw const & pipe); +}; + +/* +** This class is used to manage a file as a data source. Data requests will draw from the +** file until the file has been completely read. +*/ +class FileStraw : public Straw +{ + public: + FileStraw(FileClass * file) : File(file), HasOpened(false) {} + FileStraw(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FileStraw(void); + virtual int Get(void * source, int slen); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FileStraw(FileStraw & rvalue); + FileStraw & operator = (FileStraw const & pipe); +}; + + +#endif diff --git a/REDALERT/_WSPROTO.CPP b/REDALERT/_WSPROTO.CPP new file mode 100644 index 000000000..a31e4652d --- /dev/null +++ b/REDALERT/_WSPROTO.CPP @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/_WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:47p $* + * * + * $Revision:: 2 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "_WSProto.h" + + +WinsockInterfaceClass *PacketTransport = 0; //The object for interfacing with Winsock diff --git a/REDALERT/_WSPROTO.H b/REDALERT/_WSPROTO.H new file mode 100644 index 000000000..ee240f0c2 --- /dev/null +++ b/REDALERT/_WSPROTO.H @@ -0,0 +1,42 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/_WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:39p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef _WSPROTO_H +#define _WSPROTO_H + + +class WinsockInterfaceClass; +extern WinsockInterfaceClass *PacketTransport; //The object for interfacing with Winsock + +#endif //_WSPROTO_H diff --git a/TIBERIANDAWN/AADATA.CPP b/TIBERIANDAWN/AADATA.CPP new file mode 100644 index 000000000..f2881a265 --- /dev/null +++ b/TIBERIANDAWN/AADATA.CPP @@ -0,0 +1,795 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\aadata.cpv 2.18 16 Oct 1995 16:49:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * name in * + * File Name : AADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : August 7, 1995 [JLB] * + * Determines * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste* + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * AircraftTypeClass::From_Name -- Converts an ASCIIto an aircraft type number. * + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class.* + * AircraftTypeClass::Overlap_List -- the overlap list for a landed aircraft. * + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * AircraftTypeClass::Repair_Cost -- Fetchs the cost per repair step. * + * AircraftTypeClass::Repair_Step -- Fetches the number of health points per repair. * + * AircraftTypeClass::Who_Can_Build_Me -- Determines which object can build the aircraft obje* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * AircraftTypeClass::LRotorData = NULL; +void const * AircraftTypeClass::RRotorData = NULL; + +// A-10 attack plane +static AircraftTypeClass const AttackPlane( + AIRCRAFT_A10, // What kind of aircraft is this. + TXT_A10, // Translated text number for aircraft. + "A10", // INI name of aircraft. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + 3, // Number of shots it has (default). + 60, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 800, // Credit cost to construct. + 0, // The scenario this becomes available. + 10,1, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_NAPALM,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + +// Transport helicopter. +static AircraftTypeClass const TransportHeli( + AIRCRAFT_TRANSPORT, // What kind of aircraft is this. + TXT_TRANS, // Translated text number for aircraft. + "TRAN", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + true, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + true, // Custom rotor sets for each facing? + true, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Theater specific graphic image? + false, // Is it equipped with a combat turret? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 0, // Number of shots it has (default). + 90, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1500, // Credit cost to construct. + 98, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // Who can own this aircraft type. + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_MEDIUM_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + +// Apache attach helicopter. +static AircraftTypeClass const AttackHeli( + AIRCRAFT_HELICOPTER, // What kind of aircraft is this. + TXT_HELI, // Translated text number for aircraft. + "HELI", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + true, // Is a leader type? + true, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 15, // Number of shots it has (default). + 125, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1200, // Credit cost to construct. + 10, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_CHAIN_GUN,WEAPON_NONE, + ARMOR_STEEL, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 4, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +// Orca attack helicopter. +static AircraftTypeClass const OrcaHeli( + AIRCRAFT_ORCA, // What kind of aircraft is this. + TXT_ORCA, // Translated text number for aircraft. + "ORCA", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + true, // Is a leader type? + true, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 6, // Number of shots it has (default). + 125, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1200, // Credit cost to construct. + 10, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // Who can own this aircraft type. + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_STEEL, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 4, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +// C-17 transport plane. +static AircraftTypeClass const CargoPlane( + AIRCRAFT_CARGO, // What kind of aircraft is this. + TXT_C17, // Translated text number for aircraft. + "C17", // INI name of aircraft. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + true, // Is this a typical transport vehicle? + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + false, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + 0, // Number of shots it has (default). + 25, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 800, // Credit cost to construct. + 0, // The scenario this becomes available. + 10,1, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +AircraftTypeClass const * const AircraftTypeClass::Pointers[AIRCRAFT_COUNT] = { + &TransportHeli, + &AttackPlane, + &AttackHeli, + &CargoPlane, + &OrcaHeli, +}; + + +/*********************************************************************************************** + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * * + * This is the constructor for the aircraft object. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass::AircraftTypeClass( + AircraftType airtype, + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_twoshooter, + bool is_transporter, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + MPHType maxspeed, + int rot, + MissionType deforder) : + TechnoTypeClass(name, + ininame, + level, + pre, + is_leader, + false, + false, + is_transporter, + false, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_theater, + is_twoshooter, + false, + is_repairable, + is_buildable, + is_crew, + ammo, + strength, + maxspeed, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary, + secondary, + armor) +{ + IsRotorEquipped = is_rotorequipped; + IsRotorCustom = is_rotorcustom; + IsLandable = is_landable; + IsFixedWing = is_fixedwing; + Type = airtype; + ROT = rot; + Mission = deforder; +} + + +/*********************************************************************************************** + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * * + * This routine is used to convert an ASCII representation of an aircraft into the * + * matching aircraft type number. This is used by the scenario INI reader code. * + * * + * INPUT: name -- Pointer to ASCII name to translate. * + * * + * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no * + * match could be found, then AIRCRAFT_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftType AircraftTypeClass::From_Name(char const *name) +{ + if (name) { + for (AircraftType classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(AIRCRAFT_NONE); +} + + +/*********************************************************************************************** + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. * + * * + * This routine is used to perform the onetime initialization of the aircraft type. This * + * includes primarily the shape and other graphic data loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This goes to disk and also must only be called ONCE. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::One_Time(void) +{ + AircraftType index; + + for (index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + AircraftTypeClass const & uclass = As_Reference(index); + + /* + ** Fetch the supporting data files for the unit. + */ + char buffer[_MAX_FNAME]; + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", uclass.IniName); + } else { + sprintf(buffer, "%sICON", uclass.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MixFileClass::Retrieve(fullname); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass.IniName, ".SHP"); + ((void const *&)uclass.ImageData) = MixFileClass::Retrieve(fullname); + } + + LRotorData = MixFileClass::Retrieve("LROTOR.SHP"); + RRotorData = MixFileClass::Retrieve("RROTOR.SHP"); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * * + * This routine is used to create an aircraft object that matches the aircraft type. It * + * serves as a shortcut to creating an object using the "new" operator and "if" checks. * + * * + * INPUT: house -- The house owner of the aircraft that is to be created. * + * * + * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be * + * created, then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new AircraftClass(Type, house->Class->House)); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * * + * This routine is used by the scenario editor to prepare for the adding operation. It * + * builds a list of pointers to object types that can be added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Prep_For_Add(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * * + * This routine is used by the scenario editor to display a generic version of the object * + * type. This is displayed in the object selection dialog box. * + * * + * INPUT: x,y -- The coordinates to draw the aircraft at (centered). * + * * + * window -- The window to base the coordinates upon. * + * * + * house -- The owner of this generic aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, HouseClass::As_Pointer(house)->Remap_Table(false, true)); +} +#endif + + +/*********************************************************************************************** + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * * + * This determines the occupation list for the aircraft (if it was landed). * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. * + * * + * WARNINGS: This occupation list is only valid if the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * * + * This routine figures out the overlap list for the aircraft as if it were landed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell offset overlap list for the aircraft. * + * * + * WARNINGS: This overlap list is only valid when the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Overlap_List(void) const +{ + static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Who_Can_Build_Me -- Determines which object can build the aircraft objec * + * * + * Use this routine to determine which object (factory) can build the aircraft. It * + * determines this by scanning through the available factories, looking for one that is * + * of the proper ownership and is available. * + * * + * INPUT: intheory -- When true, it doesn't consider if the factory is currently busy. It * + * only considers that it is the right type. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The house of the desired aircraft to be built. * + * * + * OUTPUT: Returns with a pointer to the object that can build the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * AircraftTypeClass::Who_Can_Build_Me(bool , bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + building->Class->ToBuild == RTTI_AIRCRAFTTYPE) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Repair_Cost -- Fetchs the cost per repair step. * + * * + * This routine will return the cost for every repair step. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit expense for every repair step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Repair_Cost(void) const +{ + return(Fixed_To_Cardinal(Cost/(MaxStrength/REPAIR_STEP), REPAIR_PERCENT)); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Repair_Step -- Fetches the number of health points per repair. * + * * + * For every repair event, the returned number of health points is acquired. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to recover each repair step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * * + * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is * + * the maximum number of passengers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips for this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Max_Pips(void) const +{ + if (IsTransporter) { + return(Max_Passengers()); + } else { + if (Primary != WEAPON_NONE) { + return(5); + } + } + return(0); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system * + * * + * This routine is used to create and place an aircraft through the normal game system. * + * Since creation of aircraft in this fashion is prohibited, this routine does nothing. * + * * + * INPUT: na * + * * + * OUTPUT: Always returns a failure code (false). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const +{ + return(false); +} + + + + + +/*********************************************************************************************** + * ATC::Init -- load up terrain set dependant sidebar icons * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/25/96 0:33AM ST : Created * + *=============================================================================================*/ + +void AircraftTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + if ( Get_Resolution_Factor() ) { + + AircraftType index; + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + for (index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + AircraftTypeClass const & uclass = As_Reference(index); + + ((void const *&)uclass.CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", uclass.IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass.CameoData) = cameo_ptr; + } + } + } + } +} + + + + + +/*********************************************************************************************** + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * * + * This routine will fetch the pixel dimensions of this aircraft type. These dimensions * + * are used to control map refresh and select box rendering. * + * * + * INPUT: width -- Reference to variable that will be filled in with aircraft width. * + * * + * height -- Reference to variable that will be filled in with aircraft height. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Dimensions(int &width, int &height) const +{ + width = 21; + height = 20; +} + + +RTTIType AircraftTypeClass::What_Am_I(void) const {return RTTI_AIRCRAFTTYPE;}; diff --git a/TIBERIANDAWN/ABSTRACT.CPP b/TIBERIANDAWN/ABSTRACT.CPP new file mode 100644 index 000000000..49b7d08b5 --- /dev/null +++ b/TIBERIANDAWN/ABSTRACT.CPP @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\abstract.cpv 2.20 16 Oct 1995 16:49:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AbstractClass::Distance -- Determines distance to target. * + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * AbstractClass::Distance -- Determines distance to target. * + * * + * This will determine the distance (direct line) to the target. The distance is in * + * 'leptons'. This routine is typically used for weapon range checks. * + * * + * INPUT: target -- The target to determine range to. * + * * + * OUTPUT: Returns with the range to the specified target (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1994 JLB : Created. * + *=============================================================================================*/ +int AbstractClass::Distance(TARGET target) const +{ + /* + ** Should subtract a fudge-factor distance for building targets. + */ + BuildingClass *obj = As_Building(target); + int dist = Distance(As_Coord(target)); + + /* + ** If the object is a building the adjust it by the average radius + ** of the object. + */ + if (obj && obj->IsActive) { + dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + if (dist < 0) dist = 0; + } + + /* + ** Return the distance to the target + */ + return(dist); +} + + +/*********************************************************************************************** + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * * + * This is the constructor for AbstractTypeClass objects. It initializes the INI name and * + * the text name for this object type. * + * * + * INPUT: name -- Text number for the full name of the object. * + * * + * ini -- The ini name for this object type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +AbstractTypeClass::AbstractTypeClass(int name, char const * ini) +{ + Name = name; + strncpy((char *)IniName, ini, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; +} + +RTTIType AbstractTypeClass::What_Am_I(void) const {return RTTI_ABSTRACTTYPE;}; +COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const {return coord;} +int AbstractTypeClass::Full_Name(void) const {return Name;}; +unsigned short AbstractTypeClass::Get_Ownable(void) const {return 0xffff;}; + + + + +void AbstractClass::Delete_This(void) +{ + /* + ** Apparently Watcom preserved the vtable through the virtual destructor chain, otherwise C&C would crash all the time. + ** + ** MSVC doesn't. It overwrites the vtable pointer with the vtable of the class currently being destructed - presumably to + ** prevent virtual functions of the classes already destructed from being called. + ** + ** So this is a hacky workaround to restore the original vtable pointer after the destructor chain has finished, for when the + ** C&C code wants to call a virtual function on an object it just 'deleted'. + ** + ** ST - 1/9/2019 6:02PM + */ + unsigned long *this_ptr = (unsigned long*) this; + unsigned long vtable_ptr = *this_ptr; + + /* + ** delete this calls the operator delete, it doesn't actually deallocate the memory. + */ + delete this; + + *this_ptr = vtable_ptr; +} \ No newline at end of file diff --git a/TIBERIANDAWN/ABSTRACT.H b/TIBERIANDAWN/ABSTRACT.H new file mode 100644 index 000000000..1a3720c5a --- /dev/null +++ b/TIBERIANDAWN/ABSTRACT.H @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\abstract.h_v 2.20 16 Oct 1995 16:46:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : January 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +COORDINATE As_Coord(TARGET target); + +class AbstractTypeClass; + +class AbstractClass +{ + public: + + /* + ** The coordinate location of the unit. For vehicles, this is the center + ** point. For buildings, it is the upper left corner. + */ + COORDINATE Coord; + + /* + ** The actual object ram-space is located in arrays in the data segment. This flag + ** is used to indicate which objects are free to be reused and which are currently + ** in use by the game. + */ + unsigned IsActive:1; + + /* + ** A flag to indicate that this object was recently created. Since an object's allocation is just a matter of whether + ** the IsActive flag is set, during a logic frame an object with a given ID could be 'deleted' then reallocated + ** as a different type of object in a different location. This flag lets us know that this happened. ST - 8/19/2019 5:33PM + */ + unsigned IsRecentlyCreated:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + AbstractClass(void) {Coord = 0L;}; + virtual ~AbstractClass(void) {}; + + /* + ** Query functions. + */ + virtual HousesType Owner(void) const {return HOUSE_NONE;}; + + /* + ** Coordinate query support functions. + */ + virtual COORDINATE Center_Coord(void) const {return Coord;}; + virtual COORDINATE Target_Coord(void) const {return Coord;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());}; + DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);}; + DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));}; + DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);}; + int Distance(TARGET target) const; + int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);}; + int Distance(CELL cell) const {return ::Distance(Coord_Cell(Center_Coord()), cell);}; + int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());}; + + /* + ** Object entry and exit from the game system. + */ + virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;}; + + /* + ** AI. + */ + virtual void AI(void) {}; + + /* + ** Workaround for difference between watcom and VC destructor behavior + */ + void Delete_This(void); + + /* + ** Set the new recently created flag every time the active flag is set. ST - 8/19/2019 5:41PM + */ + void Set_Active(void) {IsActive = true; IsRecentlyCreated = true;} +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ADATA.CPP b/TIBERIANDAWN/ADATA.CPP new file mode 100644 index 000000000..201bdf8e5 --- /dev/null +++ b/TIBERIANDAWN/ADATA.CPP @@ -0,0 +1,2550 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\adata.cpv 2.18 16 Oct 1995 16:49:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : August 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +// Dinosaur death animations +static AnimTypeClass const TricDie( + ANIM_TRIC_DIE, // Animation number. + "TRIC", // Data name of animation. + 32, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 176, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 20, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const TRexDie( + ANIM_TREX_DIE, // Animation number. + "TREX", // Data name of animation. + 48, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 144, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 40, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const StegDie( + ANIM_STEG_DIE, // Animation number. + "STEG", // Data name of animation. + 33, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 176, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 22, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const RaptDie( + ANIM_RAPT_DIE, // Animation number. + "RAPT", // Data name of animation. + 24, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 144, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 40, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SAMN( + ANIM_SAM_N, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNW( + ANIM_SAM_NW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 22, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*1, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMW( + ANIM_SAM_W, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 40, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*2, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSW( + ANIM_SAM_SW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*3, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMS( + ANIM_SAM_S, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 76, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*4, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSE( + ANIM_SAM_SE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 94, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*5, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAME( + ANIM_SAM_E, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 112, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*6, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNE( + ANIM_SAM_NE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 130, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*7, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const LZSmoke( + ANIM_LZ_SMOKE, // Animation number. + "SMOKLAND", // Data name of animation. + 32, // Maximum dimension of animation. + 72, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 72, // Loop start frame number. + 91, // Ending frame of loop back. + -1, // Number of animation stages. + 255, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations. Primarily used on trees and buildings. +*/ +static AnimTypeClass const BurnSmall( + ANIM_BURN_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnMed( + ANIM_BURN_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnBig( + ANIM_BURN_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0018, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations that trail into smoke. Used for +** buildings and the gunboat. +*/ +static AnimTypeClass const OnFireSmall( + ANIM_ON_FIRE_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_SMOKE_M +); +static AnimTypeClass const OnFireMed( + ANIM_ON_FIRE_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_SMALL +); +static AnimTypeClass const OnFireBig( + ANIM_ON_FIRE_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0018, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_MED +); + +/* +** Flame thrower animations. These are direction specific. +*/ +static AnimTypeClass const FlameN( + ANIM_FLAME_N, // Animation number. + "FLAME-N", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameNW( + ANIM_FLAME_NW, // Animation number. + "FLAME-NW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameW( + ANIM_FLAME_W, // Animation number. + "FLAME-W", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameSW( + ANIM_FLAME_SW, // Animation number. + "FLAME-SW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameS( + ANIM_FLAME_S, // Animation number. + "FLAME-S", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameSE( + ANIM_FLAME_SE, // Animation number. + "FLAME-SE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameE( + ANIM_FLAME_E, // Animation number. + "FLAME-E", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameNE( + ANIM_FLAME_NE, // Animation number. + "FLAME-NE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Chem sprayer animations. These are direction specific. +*/ +static AnimTypeClass const ChemN( + ANIM_CHEM_N, // Animation number. + "CHEM-N", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemNW( + ANIM_CHEM_NW, // Animation number. + "CHEM-NW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemW( + ANIM_CHEM_W, // Animation number. + "CHEM-W", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemSW( + ANIM_CHEM_SW, // Animation number. + "CHEM-SW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemS( + ANIM_CHEM_S, // Animation number. + "CHEM-S", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemSE( + ANIM_CHEM_SE, // Animation number. + "CHEM-SE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemE( + ANIM_CHEM_E, // Animation number. + "CHEM-E", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemNE( + ANIM_CHEM_NE, // Animation number. + "CHEM-NE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Grenade( + ANIM_GRENADE, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_GUN20, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FBall1( + ANIM_FBALL1, // Animation number. + "FBALL1", // Data name of animation. + 67, // Maximum dimension of animation. + 6, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag1( + ANIM_FRAG1, // Animation number. + "FRAG1", // Data name of animation. + 45, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOBIG4, // Sound effect to play. + ANIM_NONE, + 29 // Virtual stages +); + +static AnimTypeClass const Frag3( + ANIM_FRAG2, // Animation number. + "FRAG3", // Data name of animation. + 41, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOBIG6, // Sound effect to play. + ANIM_NONE, + 29 // Virtual stages +); + +static AnimTypeClass const VehHit1( + ANIM_VEH_HIT1, // Animation number. + "VEH-HIT1", // Data name of animation. + 30, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit2( + ANIM_VEH_HIT2, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit3( + ANIM_VEH_HIT3, // Animation number. + "VEH-HIT3", // Data name of animation. + 19, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const ArtExp1( + ANIM_ART_EXP1, // Animation number. + "ART-EXP1", // Data name of animation. + 41, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOSML2, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm1( + ANIM_NAPALM1, // Animation number. + "NAPALM1", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm2( + ANIM_NAPALM2, // Animation number. + "NAPALM2", // Data name of animation. + 41, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm3( + ANIM_NAPALM3, // Animation number. + "NAPALM3", // Data name of animation. + 78, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokePuff( + ANIM_SMOKE_PUFF, // Animation number. + "SMOKEY", // Data name of animation. + 24, // Maximum dimension of animation. + 2, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Piff( + ANIM_PIFF, // Animation number. + "PIFF", // Data name of animation. + 13, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const PiffPiff( + ANIM_PIFFPIFF, // Animation number. + "PIFFPIFF", // Data name of animation. + 20, // Maximum dimension of animation. + 2, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire3( + ANIM_FIRE_SMALL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + ANIM_FIRE_SMALL_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire3Virtual( + ANIM_FIRE_SMALL_VIRTUAL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire1( + ANIM_FIRE_MED2, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + ANIM_FIRE_MED2_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire1Virtual( + ANIM_FIRE_MED2_VIRTUAL, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire4( + ANIM_FIRE_TINY, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + ANIM_FIRE_TINY_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire4Virtual( + ANIM_FIRE_TINY_VIRTUAL, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 30 // Virtual stages +); + +static AnimTypeClass const Fire2( + ANIM_FIRE_MED, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + ANIM_FIRE_MED_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire2Virtual( + ANIM_FIRE_MED_VIRTUAL, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const OilFieldBurn( + ANIM_OILFIELD_BURN, // Animation number. + "FLMSPT", // Data name of animation. + 42, // Maximum dimension of animation. + 58, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 33, // Loop start frame number. + 99, // Ending frame of loop back. + 66, // Number of animation stages. + 65535, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Gunfire( + ANIM_MUZZLE_FLASH, // Animation number. + "GUNFIRE", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. +// 2, // Number of times the animation loops. + 1, // Number of animation stages. +// 2, // Number of animation stages. + 1, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 10 // Virtual stages +); + +#ifdef NEVER +static AnimTypeClass const E1RotFire( + ANIM_E1_ROT_FIRE, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotGrenade( + ANIM_E1_ROT_GRENADE, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotGun( + ANIM_E1_ROT_GUN, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotExp( + ANIM_E1_ROT_EXP, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E2RotFire( + ANIM_E2_ROT_FIRE, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotGrenade( + ANIM_E2_ROT_GRENADE, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotGun( + ANIM_E2_ROT_GUN, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotExp( + ANIM_E2_ROT_EXP, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E3RotFire( + ANIM_E3_ROT_FIRE, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotGrenade( + ANIM_E3_ROT_GRENADE, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotGun( + ANIM_E3_ROT_GUN, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotExp( + ANIM_E3_ROT_EXP, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E4RotFire( + ANIM_E4_ROT_FIRE, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotGrenade( + ANIM_E4_ROT_GRENADE, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotGun( + ANIM_E4_ROT_GUN, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotExp( + ANIM_E4_ROT_EXP, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +#endif + +static AnimTypeClass const SmokeM( + ANIM_SMOKE_M, // Animation number. + "SMOKE_M", // Data name of animation. + 28, // Maximum dimension of animation. + 30, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 67, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 6, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 105 // Virtual stages +); + +/* +** Mini-gun fire effect -- used by guard towers. +*/ +static AnimTypeClass const GUNN( + ANIM_GUN_N, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNW( + ANIM_GUN_NW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 6, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNW( + ANIM_GUN_W, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 12, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSW( + ANIM_GUN_SW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNS( + ANIM_GUN_S, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSE( + ANIM_GUN_SE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 30, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNE( + ANIM_GUN_E, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 36, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNE( + ANIM_GUN_NE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 42, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const IonCannon( + ANIM_ION_CANNON, // Animation number. + "IONSFX", // Data name of animation. + 48, // Maximum dimension of animation. + 11, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 15, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_ION_CANNON, // Sound effect to play. + ANIM_ART_EXP1, + 32, // Virtual stages + 0x200 // Virtual scale +); + +static AnimTypeClass const AtomBomb( + ANIM_ATOM_BLAST, // Animation number. + "ATOMSFX", // Data name of animation. + 72, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NUKE_EXPLODE, // Sound effect to play. + ANIM_NONE, + 75, // Virtual stages + 0x300 // Virtual scale +); +static AnimTypeClass const AtomDoor( + ANIM_ATOM_DOOR, // Animation number. + "ATOMDOOR", // Data name of animation. + 48, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const CDeviator( + ANIM_CRATE_DEVIATOR, // Animation number. + "DEVIATOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CDollar( + ANIM_CRATE_DOLLAR, // Animation number. + "DOLLAR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEarth( + ANIM_CRATE_EARTH, // Animation number. + "EARTH", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEmpulse( + ANIM_CRATE_EMPULSE, // Animation number. + "EMPULSE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CInvun( + ANIM_CRATE_INVUN, // Animation number. + "INVUN", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMine( + ANIM_CRATE_MINE, // Animation number. + "MINE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CRapid( + ANIM_CRATE_RAPID, // Animation number. + "RAPID", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CStealth( + ANIM_CRATE_STEALTH, // Animation number. + "STEALTH2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMissile( + ANIM_CRATE_MISSILE, // Animation number. + "MISSILE2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const MoveFlash( + ANIM_MOVE_FLASH, // Animation number. + "MOVEFLSH", // Data name of animation. + 24, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + true, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const ChemBall( + ANIM_CHEM_BALL, // Animation number. + "CHEMBALL", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + + + +AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = { + &FBall1, + &Grenade, + &Frag1, + &Frag3, + &VehHit1, + &VehHit2, + &VehHit3, + &ArtExp1, + &Napalm1, + &Napalm2, + &Napalm3, + &SmokePuff, + &Piff, + &PiffPiff, + &FlameN, + &FlameNE, + &FlameE, + &FlameSE, + &FlameS, + &FlameSW, + &FlameW, + &FlameNW, + &ChemN, + &ChemNE, + &ChemE, + &ChemSE, + &ChemS, + &ChemSW, + &ChemW, + &ChemNW, + &Fire3, + &Fire2, + &Fire1, + &Fire4, + &Gunfire, +#ifdef NEVER + &E1RotFire, + &E1RotGrenade, + &E1RotGun, + &E1RotExp, + &E2RotFire, + &E2RotGrenade, + &E2RotGun, + &E2RotExp, + &E3RotFire, + &E3RotGrenade, + &E3RotGun, + &E3RotExp, + &E4RotFire, + &E4RotGrenade, + &E4RotGun, + &E4RotExp, +#endif + &SmokeM, + &BurnSmall, + &BurnMed, + &BurnBig, + &OnFireSmall, + &OnFireMed, + &OnFireBig, + &SAMN, + &SAMNE, + &SAME, + &SAMSE, + &SAMS, + &SAMSW, + &SAMW, + &SAMNW, + &GUNN, + &GUNNE, + &GUNE, + &GUNSE, + &GUNS, + &GUNSW, + &GUNW, + &GUNNW, + &LZSmoke, + &IonCannon, + &AtomBomb, + &CDeviator, + &CDollar, + &CEarth, + &CEmpulse, + &CInvun, + &CMine, + &CRapid, + &CStealth, + &CMissile, + &AtomDoor, + &MoveFlash, + &OilFieldBurn, + &TricDie, + &TRexDie, + &StegDie, + &RaptDie, + &ChemBall, + &Fire3Virtual, + &Fire2Virtual, + &Fire1Virtual, + &Fire4Virtual +}; + + +/*********************************************************************************************** + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * * + * This is the constructor for static objects that elaborate the various animation types * + * allowed in the game. Each animation in the game is of one of these types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass::AnimTypeClass(AnimType anim, char const *name, int size, int biggest, + bool isnormal, bool iswhitetrans, bool isscorcher, bool iscrater, bool issticky, bool ground, + bool istrans, bool isflame, unsigned int damage, + int delaytime, int start, int loopstart, int loopend, int stages, int loops, + VocType sound, AnimType chainto, int virtualstages, int virtualscale, AnimType virtualanim) : + ObjectTypeClass(true, false, false, true, false, false, true, true, TXT_NONE, name, ARMOR_NONE, 0) +{ + Biggest = biggest; + ChainTo = chainto; + Damage = damage; + Delay = (unsigned char)delaytime; + IsCraterForming = iscrater; + IsFlameThrower = isflame; + IsGroundLayer = ground; + IsNormalized = isnormal; + IsScorcher = isscorcher; + IsSticky = issticky; + IsTranslucent = istrans; + IsWhiteTrans = iswhitetrans; + LoopEnd = loopend; + LoopStart = loopstart; + Loops = (unsigned char)loops; + Size = size; + Sound = sound; + Stages = stages; + Start = start; + Type = anim; + VirtualStages = virtualstages; + VirtualScale = virtualscale; + VirtualAnim = virtualanim; +} + + +/*********************************************************************************************** + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * * + * This will load the animation shape data. It is called by the game initialization * + * process. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called ONLY once. * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::One_Time(void) +{ + AnimType index; + + for (index = ANIM_FIRST; index < ANIM_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + _makepath(fullname, NULL, NULL, As_Reference(index).IniName, ".SHP"); + + RawFileClass file(fullname); + if (file.Is_Available()) { + ((void const *&)As_Reference(index).ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)As_Reference(index).ImageData) = MixFileClass::Retrieve(fullname); + } + } +} + + diff --git a/TIBERIANDAWN/AIRCRAFT.CPP b/TIBERIANDAWN/AIRCRAFT.CPP new file mode 100644 index 000000000..c8ee3f3cf --- /dev/null +++ b/TIBERIANDAWN/AIRCRAFT.CPP @@ -0,0 +1,3539 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\aircraft.cpv 2.12 19 Jun 1995 09:27:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * AircraftClass::As_Target -- Returns aircraft as a target number. * + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * AircraftClass::Fire_Coord -- Calculates the point of origin for a bullet. * + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * AircraftClass::In_Which_Layer -- Determine which render layer the aircraft lies. * + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * AircraftClass::Mark -- Flags cells under the aircraft so that they will be redrawn. * + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * AircraftClass::Mission_Move -- Handles movement mission. * + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * AircraftClass::New_LZ -- Find a good landing zone. * + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * AircraftClass::Rearm_Delay -- Returns the delay between shots for this aircraft. * + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * AircraftClass::Response_Move -- Gives audio response to move request. * + * AircraftClass::Response_Select -- Gives audio response when selected. * + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * AircraftClass::Threat_Range -- Returns with a range to scan for targets. * + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::Write_INI -- Writes the current aircraft objects to an INI file. * + * AircraftClass::operator delete -- Deletes the aircraft object. * + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * AircraftClass::Validate -- validates aircraft pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * AircraftClass::VTable; + + +/*********************************************************************************************** + * AircraftClass::Validate -- validates aircraft pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int AircraftClass::Validate(void) const +{ + int num; + + num = Aircraft.ID(this); + if (num < 0 || num >= AIRCRAFT_MAX) { + Validate_Error("AIRCRAFT"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * AircraftClass::As_Target -- Returns aircraft as a target number. * + * * + * This routine will convert the aircraft into a target number. This target number can * + * then be assigned to a targeting or navigation computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the aircraft as a target number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_AIRCRAFT, Aircraft.ID(this))); +} + + +/*********************************************************************************************** + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * * + * This routine will allocate an aircraft object from the free aircraft object pool. If * + * there are no free object available, then this routine will fail (return NULL). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were * + * available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void * AircraftClass::operator new(size_t) +{ + void * ptr = Aircraft.Allocate(); + if (ptr) { + ((AircraftClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * AircraftClass::operator delete -- Deletes the aircraft object. * + * * + * This routine will return the aircraft object back to the free aircraft object pool. * + * * + * INPUT: ptr -- Pointer to the aircraft object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::operator delete(void *ptr) +{ + if (ptr) { + ((AircraftClass *)ptr)->IsActive = false; + } + Aircraft.Free((AircraftClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * * + * This routine is the constructor for aircraft objects. An aircraft object can be * + * created and possibly placed into the game system by this routine. * + * * + * INPUT: classid -- The type of aircraft to create. * + * * + * house -- The owner of this aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftClass::AircraftClass(AircraftType classid, HousesType house) : + Class(&AircraftTypeClass::As_Reference(classid)), + FootClass(house) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Ammo = Class->MaxAmmo; + AttacksRemaining = 3; + Altitude = FLIGHT_LEVEL; + IsLanding = false; + IsTakingOff = false; + IsHovering = false; + IsHoming = false; + Strength = Class->MaxStrength; + NavCom = TARGET_NONE; + SecondaryFacing = PrimaryFacing; + Jitter = 0; + + /* + ** Keep count of the number of units created. Dont track cargo planes as they are created + ** automatically, not bought. + */ + if (classid != AIRCRAFT_CARGO && GameToPlay == GAME_INTERNET){ + House->AircraftTotals->Increment_Unit_Total((int)classid); + } + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Add(this); +#endif +} + + +/*********************************************************************************************** + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * * + * This routine is used to transition the aircraft from the limbo to the non limbo state. * + * It occurs when the aircraft is placed on the map for whatever reason. When it is * + * unlimboed, only then will normal game processing recognize it. * + * * + * INPUT: coord -- The coordinate that the aircraft should appear at. * + * * + * dir -- The direction it should start facing. * + * * + * strength (optional) -- sets initial strength * + * * + * mission (optional) -- sets initial mission * + * * + * OUTPUT: bool; Was the aircraft unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + if (FootClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->AScan |= (1L << Class->Type); + House->ActiveAScan |= (1L << Class->Type); + + /* + ** Forces the body of the helicopter to face the correct direction. + */ + SecondaryFacing = dir; + + /* + ** Start rotor animation. + */ + Set_Rate(1); + Set_Stage(0); + + /* + ** Presume it starts in flight? + */ + if (Altitude == FLIGHT_LEVEL) { + Set_Speed(0xFF); + } else { + Set_Speed(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * * + * This routine is used to display the aircraft object at the coordinates specified. * + * The tactical map display uses this routine for all aircraft rendering. * + * * + * INPUT: x,y -- The coordinates to render the aircraft at. * + * * + * window -- The window that the coordinates are based upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Working shape file pointer. + int shapenum = 0; + int facing = Facing_To_32(SecondaryFacing); + + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + shapenum = UnitClass::BodyShape[facing]; + + /* + ** The orca attack helicopter uses a special shape set when it is travelling + ** forward above a certain speed. + */ + if (*this == AIRCRAFT_ORCA && Get_Speed() >= MPH_MEDIUM_FAST) { + shapenum += 32; + } + + /* + ** If there is a door on this aircraft (Chinook), then adjust the + ** shape number to match the door open state. + */ + if (!Is_Door_Closed()) { + shapenum = 32 + Door_Stage(); + } + + /* + ** Helicopters that are flying have a "bobbing" effect. + */ + int jitter = 0; + if (Altitude == FLIGHT_LEVEL && !Class->IsFixedWing) { + Jitter++; + + static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0}; + jitter = _jitter[Jitter % 16]; + } + + // Virtual window needs to draw the body first so it's considered the primary object and the shadow is a sub-object + if (window == WINDOW_VIRTUAL) { + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, (y - Altitude) + jitter, window); + + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x + 1, y + 2, window, SHAPE_PREDATOR | SHAPE_CENTER | SHAPE_WIN_REL | SHAPE_FADING, Map.FadingShade, NULL); + } + } else { + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, Map.FadingShade, NULL); + } + + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, (y-Altitude)+jitter, window); + } + + /* + ** Draw rotor effects. The rotor art can be either generic or custom. Custom rotor + ** art has a different rotor set for each facing. Rotor shapes occur after the first + ** 32 shapes of the helicopter body. + */ + if (Class->IsRotorEquipped) { + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + + /* + ** The rotor shape number depends on whether the helicopter is idling + ** or not. A landed helicopter uses slow moving "idling" blades. + */ + if (Altitude == 0) { + shapenum = (Fetch_Stage()%8)+4; + flags = flags | SHAPE_GHOST; + } else { + shapenum = Fetch_Stage()%4; + flags = flags | SHAPE_FADING|SHAPE_PREDATOR; + } + + if (*this == AIRCRAFT_TRANSPORT) { + int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9}; + + /* + ** Dual rotors offset along flight axis. + */ + short xx = x; + short yy = y-Altitude; + FacingType face = Dir_Facing(SecondaryFacing); + Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]); + //CC_Draw_Shape(Class->RRotorData, shapenum, xx, yy-2, window, flags, NULL, Map.UnitShadow); // 6/18/2019 - ST + CC_Draw_Shape(this, "RROTOR", Class->RRotorData, shapenum, xx, yy - 2, window, flags, NULL, Map.UnitShadow); + + Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2); + //CC_Draw_Shape(this, Class->LRotorData, shapenum, xx, yy-2, window, flags, NULL, Map.UnitShadow); // 6/18/2019 - ST + CC_Draw_Shape(this, "LROTOR", Class->LRotorData, shapenum, xx, yy - 2, window, flags, NULL, Map.UnitShadow); + + } else { + + /* + ** Single rotor centered about shape. + */ + //CC_Draw_Shape(this, Class->RRotorData, shapenum, x, (y-Altitude)-2, window, flags, NULL, Map.UnitShadow); // 6/18/2019 - ST + CC_Draw_Shape(this, "RROTOR", Class->RRotorData, shapenum, x, (y - Altitude) - 2, window, flags, NULL, Map.UnitShadow); + } + } + + FootClass::Draw_It(x, y-Altitude, window); +} + + +/*********************************************************************************************** + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * * + * This routine is used to read the aircraft object data from the INI file buffer * + * specified. This is used by the scenario loader code to interpret the INI file and * + * create the specified objects therein. * + * * + * INPUT: buffer -- Pointer to the INI buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Read_INI(char *buffer) +{ + AircraftClass *air; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType inhouse; // Unit house. + AircraftType classid; // Unit class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = AircraftTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != AIRCRAFT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + air = new AircraftClass(classid, inhouse); + if (air) { + COORDINATE coord; + int strength; + DirType dir; + + /* + ** Read the raw data. + */ + strength = atoi(strtok(NULL, ",")); + coord = Cell_Coord((CELL)atoi(strtok(NULL, ","))); + dir = (DirType)atoi(strtok(NULL, ",")); + + if (!Map.In_Radar(Coord_Cell(coord))) { + delete air; + } else { + + air->Strength = Fixed_To_Cardinal(air->Class->MaxStrength, strength); + if (air->Unlimbo(coord, dir)) { + air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r"))); + } else { + delete air; + } + } + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * AircraftClass::Write_INI -- Writes the current aircraft objects to an INI file. * + * * + * This routine is used to output the current list of aircraft objects to the INI file * + * buffer specified. It is typically used by the scenario editor. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * unit; + + unit = Aircraft.Ptr(index); + if (!unit->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio(), + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission) + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * * + * Hunt AI consists of finding a target and attacking it. If there is no target assigned * + * and this unit doesn't automatically hunt for more targets, then it will change * + * mission to a more passive (land and await further orders) type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of ticks before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Hunt(void) +{ + Validate(); + if (Class->IsFixedWing) { + enum { + LOOK_FOR_TARGET, + FLY_TO_TARGET, + DROP_BOMBS + }; + switch (Status) { + + /* + ** Acquiring target stage. + */ + case LOOK_FOR_TARGET: + if (Target_Legal(TarCom)) { + Status = FLY_TO_TARGET; + return(1); + } else { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + + /* + ** If there is no target, then this aircraft should just do its normal thing. + */ + if (!Target_Legal(TarCom)) { + Enter_Idle_Mode(); + } + } + break; + + /* + ** Homing in on target stage. + */ + case FLY_TO_TARGET: + if (Target_Legal(TarCom)) { + IsHoming = true; + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + if (Distance(TarCom) < 0x0380) { + IsHoming = false; + Status = DROP_BOMBS; + return(1); + } + } else { + Status = LOOK_FOR_TARGET; + } + break; + + /* + ** Dropping a stream of bombs phase. + */ + case DROP_BOMBS: + if (!Ammo) { + AttacksRemaining--; + if (!AttacksRemaining) { + Assign_Mission(MISSION_RETREAT); + Commence(); + } else { + Ammo = Class->MaxAmmo; + Status = LOOK_FOR_TARGET; + } + } + if (!Target_Legal(TarCom)) { + Status = LOOK_FOR_TARGET; + } else { + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + return(5); + } + break; + } + } else { + if (!Ammo) { + Enter_Idle_Mode(); + } else { + Assign_Mission(MISSION_ATTACK); + return(1); +// return(FootClass::Mission_Hunt()); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * * + * This handles the non-graphic AI processing for the aircraft. This usually entails * + * maintenance and other AI functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::AI(void) +{ + Validate(); + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + FootClass::AI(); + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Handle any body rotation at this time. Body rotation can occur even if the + ** flying object is not actually moving. + */ + if (PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(); + } + } + if (Class->IsFixedWing) { + SecondaryFacing = PrimaryFacing; + } + if (SecondaryFacing.Is_Rotating()) { + if (SecondaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(); + } + } + if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { + Mark(); + } + + /* + ** Perform sighting every so often as controlled by the sight timer. + */ + //if (IsOwnedByPlayer && Class->SightRange && SightTimer.Expired()) { // Changed for multiple player mapping + if (House->IsHuman && Class->SightRange && SightTimer.Expired()) { + Map.Sight_From(House, Coord_Cell(Coord), Class->SightRange, false); + SightTimer = TICKS_PER_SECOND; + } + + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Is_Door_Closed() && (IsLanding || IsTakingOff)) { + Mark(); + LayerType layer = In_Which_Layer(); + + if (IsLanding) { + if (Altitude) Altitude--; + if (!Altitude) { + IsLanding = false; + Set_Speed(0); + if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) { + if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) { + Scatter(0, true); + } + } + } + } + if (IsTakingOff) { + Altitude++; + if (Altitude >= FLIGHT_LEVEL) { + Altitude = FLIGHT_LEVEL; + IsTakingOff = false; + } + } + + /* + ** Make adjustments for altitude by moving from one layer to another as + ** necessary. + */ + if (layer != In_Which_Layer()) { + + /* + ** When the aircraft is about to enter the ground layer, perform on last + ** check to see if it is legal to enter that location. If not, then + ** start the take off process. Let the normal logic handle this + ** change of plans. + */ + bool ok = true; + if (In_Which_Layer() == LAYER_GROUND) { + if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) { + IsTakingOff = true; + Altitude++; + ok = false; + } + } + + if (ok) { + /* + ** If landing in a cell that already contains an object, then + ** the landing attempt must be aborted. + */ + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + /* + ** When the aircraft is close to the ground, it should exist as a ground object. + ** This aspect is controlled by the Place_Down and Pick_Up functions. + */ + if (In_Which_Layer() == LAYER_GROUND) { + Assign_Destination(TARGET_NONE); // Clear the navcom. + Transmit_Message(RADIO_TETHER); + Map.Place_Down(Coord_Cell(Coord), this); + //if (IsOwnedByPlayer) { // Changed for multiple player mapping. ST - 3/6/2019 1:31PM + if (House->IsHuman) { + Map.Sight_From(House, Coord_Cell(Coord), 1, false); + } + } else { + Transmit_Message(RADIO_UNTETHER); + Map.Pick_Up(Coord_Cell(Coord), this); + + /* + ** If the navigation computer is not attached to the object this + ** aircraft is in radio contact with, then assume that radio + ** contact is now superfluous. Break radio contact. + */ + if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) { + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + } + + /* + ** Always flag the map draw process to occur if there is an aircraft in the view. + ** This ensures that it will be rendered even if there is nothing else that flagged + ** the map to be redrawn. + */ + if (Map.In_View(Coord_Cell(Coord))) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the + ** aircraft is a non-player produced unit and it has completed its mission. A transport + ** helicopter that has already delivered reinforcements is a good example of this. + */ + if (!Map.In_Radar(Coord_Cell(Coord))) { + if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) { + + /* + ** Check to see if there are any civilians aboard. If so, then flag the house + ** that the civilian evacuation trigger event has been fulfilled. + */ + while (Is_Something_Attached()) { + FootClass * obj = Detach_Object(); + if (obj->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)obj)->Class->IsCivilian && !((InfantryClass *)obj)->IsTechnician) { + House->IsCivEvacuated = true; + } + + /* + ** Transport planes that leave can only be because they carry purchased + ** equipment and must be have their cost refunded. + */ + if (*this == AIRCRAFT_CARGO) { + House->Refund_Money(obj->Class_Of().Cost_Of()); + } + delete obj; + } + Stun(); + Delete_This(); + return; + } + } else { + IsLocked = true; +// House->NewAScan |= (1L << Class->Type); + +#ifdef NEVER + /* + ** Transport helicopters must ensure that their passengers are properly + ** considered "alive" by setting the appropriate scan bits. + */ + FootClass const * foot = Attached_Object(); + while (foot) { + switch (foot->What_Am_I()) { + case RTTI_UNIT: + House->NewUScan |= (1L << ((UnitTypeClass const &)Class_Of()).Type); + break; + + case RTTI_INFANTRY: + House->NewIScan |= (1L << ((InfantryTypeClass const &)Class_Of()).Type); + break; + } + + foot = (FootClass const *)foot->Next; + } +#endif + } +} + + +/*********************************************************************************************** + * AircraftClass::Mark -- Flags cells under the aircraft so that they will be redrawn. * + * * + * This routine is used to flag the cells under the aircraft so that those cells will * + * be redrawn during the next map drawing process. This is a necessary step whenever the * + * aircraft moves or changes shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Mark(MarkType mark) +{ + Validate(); + if (FootClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + Map.Refresh_Cells(Coord_Cell(Coord), Overlap_List()); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * * + * When aircraft are flying, they can overlap quite a number of cells. These cells can * + * be determined from the coordinate where the aircraft is centered and the size of the * + * aircraft's shape. Landed aircraft are a special case and are usually much smaller * + * than when flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that * + * the aircraft overlaps given the aircraft's current state. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const *AircraftClass::Overlap_List(void) const +{ + Validate(); + static short const _list[] = { + -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), + -1, 0, 1, + (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), + -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), + -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), + REFRESH_EOL + }; + + if (Altitude) { + return(_list); + //return Coord_Spillage_List(Coord, 25); + } + return(Class->Overlap_List()); +} + + +/*********************************************************************************************** + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * * + * This routine is used to clear out the aircraft allocation system. It is called in * + * preparation for a scenario load or save game load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Init(void) +{ + AircraftClass *ptr; + + Aircraft.Free_All(); + + ptr = new AircraftClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * * + * This function is used to handle finding, heading toward, landing, and unloading the * + * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows * + * a different mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks to delay before calling this function again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Unload(void) +{ + Validate(); + if (Class->IsFixedWing) { + enum { + PICK_AIRSTRIP, + FLY_TO_AIRSTRIP, + BUG_OUT + }; + + switch (Status) { + + /* + ** Find a suitable airfield to land at. + */ + case PICK_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + + BuildingClass * target_building = As_Building(NavCom); + BuildingClass * building = (target_building != NULL && *target_building == STRUCT_AIRSTRIP) ? target_building : Find_Docking_Bay(STRUCT_AIRSTRIP, false); + if (building) { + if (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Set_Speed(0xFF); + Assign_Destination(building->As_Target()); + if (Team) { + Team->Target = NavCom; + } + Status = FLY_TO_AIRSTRIP; + } + } + + /* + ** If a suitable airfield could not be found, then just randomly change + ** direction and then try again later. + */ + if (Status == PICK_AIRSTRIP) { + + /* + ** If there are no more airstrips, regardless of busy state, then + ** abort this transport plane completely. + */ + if (!(House->ActiveBScan & STRUCTF_AIRSTRIP)) { + Assign_Mission(MISSION_RETREAT); + } + + /* + ** Pick a new direction and fly off. + */ + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(TICKS_PER_SECOND*3); + } + } else { + Status = FLY_TO_AIRSTRIP; + } + break; + + /* + ** Home in on target. When close enough, drop the cargo. + */ + case FLY_TO_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + Status = PICK_AIRSTRIP; + } else { + + /* + ** If, for some reason, there is no cargo, then don't stick around. + */ + if (!Is_Something_Attached()) { + Status = BUG_OUT; + return(1); + } + + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(As_Movement_Coord(NavCom))); + } + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + + int navdist = Distance(As_Movement_Coord(NavCom)); + Altitude = FLIGHT_LEVEL; + if (navdist < 0x0600) { + Altitude = Fixed_To_Cardinal(FLIGHT_LEVEL, Cardinal_To_Fixed(0x0600, navdist)); + } + + if (navdist < 0x0080) { + FootClass * unit = (FootClass *)Detach_Object(); + + if (unit) { + CELL cell = Contact_With_Whom()->Find_Exit_Cell(unit); + if (cell) { + ScenarioInit++; + if (!unit->Unlimbo(Cell_Coord(cell))) { + Attach(unit); + } else { + + /* + ** Cargo planes announce reinforcements when they unload + ** their cargo. + */ + if (*this == AIRCRAFT_CARGO && House == PlayerPtr) { + Speak(VOX_REINFORCEMENTS, NULL, Cell_Coord(cell)); + } + unit->IsALoaner = false; + unit->IsLocked = true; + unit->Scatter(0, true); + } + ScenarioInit--; + + Transmit_Message(RADIO_OVER_OUT); + Assign_Target(TARGET_NONE); + } else { + Attach(unit); + } + +// if (Is_Something_Attached()) { +// Status = PICK_AIRSTRIP; +// } else { + Status = BUG_OUT; +// } + } else { + Status = BUG_OUT; + } + } + return(1); + } + break; + + /* + ** All cargo unloaded, head off the map. + */ + case BUG_OUT: + Assign_Mission(MISSION_RETREAT); + return(1); + } + + } else { + enum { + SEARCH_FOR_LZ, + FLY_TO_LZ, + LAND_ON_LZ, + UNLOAD_PASSENGERS, + TAKE_OFF, + }; + + switch (Status) { + + /* + ** Search for an appropriate destination spot if one isn't already assigned. + */ + case SEARCH_FOR_LZ: + if (Altitude == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) { + Status = UNLOAD_PASSENGERS; + } else { + if (!Is_LZ_Clear(NavCom)) { + Assign_Destination(New_LZ(::As_Target(Waypoint[WAYPT_REINF]))); + } else { + if (Altitude == FLIGHT_LEVEL) { + Status = FLY_TO_LZ; + } else { + Status = TAKE_OFF; + } + } + } + break; + + /* + ** Fly to destination. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true); + + if (distance < 0x0100) { + SecondaryFacing.Set_Desired(Pose_Dir()); + + if (distance < 0x0010) { + Status = LAND_ON_LZ; + } + return(1); + } else { + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(5); + } + } else { + Status = SEARCH_FOR_LZ; + } + break; + + /* + ** Landing phase. Just delay until landing is complete. At that time, + ** transition to the unloading phase. + */ + case LAND_ON_LZ: + if (IsTakingOff) { + Status = TAKE_OFF; + } else { + if (Process_Landing()) { + Status = UNLOAD_PASSENGERS; + } + } + return(1); + + /* + ** Hold while unloading passengers. When passengers are unloaded the order for this + ** transport gets changed to MISSION_RETREAT. + */ + case UNLOAD_PASSENGERS: + if (!IsTethered) { + if (Is_Something_Attached()) { + FootClass * unit = (FootClass *)Detach_Object(); + + /* + ** First thing is to lift the transport off of the map so that the unlimbo + ** process for the passengers is more likely to succeed. + */ + Map.Pick_Up(Coord_Cell(Coord), this); + + if (!Exit_Object(unit)) { + delete unit; + } + + /* + ** Restore the transport back down on the map. + */ + Map.Place_Down(Coord_Cell(Coord), this); + + if (!Is_Something_Attached()) { + Enter_Idle_Mode(); + } + + } else { + + Enter_Idle_Mode(); + } + } + break; + + /* + ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it + ** will either take off or look for another landing spot to try again. + */ + case TAKE_OFF: { + if (Process_Take_Off()) { + if (Is_Something_Attached()) { + Status = SEARCH_FOR_LZ; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + } else { + Enter_Idle_Mode(); + } + } + return(1); + } + } + } + return(10); +} + + +/*********************************************************************************************** + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * * + * This routine examines the landing zone (as specified by the target parameter) in order * + * to determine if it is free to be landed upon. Call this routine when it is necessary * + * to double check this. Typically this occurs right before a helicopter lands and also * + * when determining the landing zone in the first place. * + * * + * INPUT: target -- The target that is the "landing zone". * + * * + * OUTPUT: bool; Is the landing zone clear for landing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Is_LZ_Clear(TARGET target) const +{ + Validate(); + if (!Target_Legal(target)) return(false); + CELL cell = ::As_Cell(target); + if (!Map.In_Radar(cell)) return(false); + + ObjectClass * object = Map[cell].Cell_Object(); + if (object) { + if (object == this) return(true); + + if (In_Radio_Contact() && Contact_With_Whom() == object) { + return(true); + } + return(false); + } + + if (!Map[cell].Is_Generally_Clear()) return(false); + + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::In_Which_Layer -- Determine which render layer the aircraft lies. * + * * + * This routine is used to figure out which rendering layer the aircraft is located in. * + * It can be determined from the aircraft's height. The layer value is used to handle the * + * display sequence. Objects in lower layers appear beneath objects in higher layers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the layer that the aircraft is currently located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AircraftClass::In_Which_Layer(void) const +{ + Validate(); + if (Class->IsFixedWing) return(LAYER_TOP); + + if (Altitude < FLIGHT_LEVEL - (FLIGHT_LEVEL/3)) { + return(LAYER_GROUND); + } + return(LAYER_TOP); +} + + +/*********************************************************************************************** + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * * + * This routine is used to determine the coordinate to use for sorting the aircraft. This * + * sorting value is used when the aircraft is on the ground. At that time the aircraft * + * must be rendered in proper relationship to the other ground objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground * + * objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Sort_Y(void) const +{ + Validate(); + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * * + * This mission will be followed when the aircraft decides that it is time to leave the * + * battle. Typically, this occurs when a loaner transport has dropped off its load or when * + * an attack air vehicle has expended its ordinance. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. * + *=============================================================================================*/ +int AircraftClass::Mission_Retreat(void) +{ + Validate(); + if (Class->IsFixedWing) { + if (Class->IsFixedWing && Altitude < FLIGHT_LEVEL) { + Altitude++; + return(3); + } + return(TICKS_PER_SECOND*10); + } + + enum { + TAKE_OFF, + FACE_MAP_EDGE, + KEEP_FLYING + }; + switch (Status) { + + /* + ** Take off if landed. + */ + case TAKE_OFF: + if (Process_Take_Off()) { + Status = FACE_MAP_EDGE; + } + return(1); + + /* + ** Set facing and speed toward the friendly map edge. + */ + case FACE_MAP_EDGE: + Set_Speed(0xFF); + + /* + ** Take advantage of the fact that the source map edge enumerations happen to + ** occur in a clockwise order and are the first four enumerations of the map + ** edge default for the house. If this value is masked and then shifted, a + ** normalized direction value results. Use this value to head the aircraft + ** toward the "friendly" map edge. + */ + PrimaryFacing.Set_Desired((DirType)((House->Edge & 0x03) << 6)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Status = KEEP_FLYING; + break; + + /* + ** Just do nothing since we are headed toward the map edge. When the edge is + ** reached, the aircraft should be automatically eliminated. + */ + case KEEP_FLYING: + break; + + default: + break; + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * * + * This routine is called when the aircraft is to unload a passenger. The passenger must * + * be able to move under its own power. Typical situation is when a transport helicopter * + * is to unload an infantry unit. * + * * + * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. * + * * + * OUTPUT: bool; Was the unit unloaded successfully? * + * * + * WARNINGS: The unload process is merely started by this routine. Radio contact is * + * established with the unloading unit and when the unit is clear of the aircraft * + * the radio contact will be broken and then the aircraft is free to pursue * + * other. * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Exit_Object(TechnoClass * unit) +{ + Validate(); + static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E}; + CELL cell; + + /* + ** Find a free cell to drop the unit off at. + */ + FacingType face; + for (face = FACING_N; face < FACING_COUNT; face++) { + cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]); + if (unit->Can_Enter_Cell(cell) == MOVE_OK) break; + } + + // Should perform a check here to see if no cell could be found. + + /* + ** If the passenger can be placed on the map, then start it moving toward the + ** destination cell and establish radio contact with the transport. This is used + ** to make sure that the transport waits until the passenger is clear before + ** unloading the next passenger or taking off. + */ + if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(cell)); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + unit->Look(false); + return(1); + } + return(0); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * * + * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the * + * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the * + * default speed defined in the BulletTypeClass structure. * + * * + * INPUT: target -- The target that the projectile is heading for. * + * * + * which -- Which weapon to use in the attack. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * AircraftClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet = FootClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Aircraft reveal when firing + */ + HouseClass *player = HouseClass::As_Pointer(Owner()); + if (player != nullptr && player->IsHuman) { + Map.Sight_From(player, Coord_Cell(Center_Coord()), 1, false); + } + + /* + ** Play the sound effect associated with this weapon. + */ + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + Sound_Effect(weapon->Sound, Coord); + + + /* + ** Falling bullets move at a speed proportionate to the delivery craft. + */ + if (bullet->Class->IsDropping) { + bullet->Fly_Speed(40, bullet->Class->MaxSpeed); + } + } + return(bullet); +} + + +/*********************************************************************************************** + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * * + * This routine is used to apply damage to the specified aircraft. This is where any * + * special crash animation will be initiated. * + * * + * INPUT: damage -- Reference to the damage that will be applied to the aircraft. * + * This value will be filled in with the actual damage that was * + * applied. * + * * + * distance -- Distance from the source of the explosion to this aircraft. * + * * + * warhead -- The warhead type that the damage occurs from. * + * * + * source -- Pointer to the originator of the damage. This can be used so that * + * proper "thank you" can be delivered. * + * * + * OUTPUT: Returns with the result of the damage as it affects this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1995 JLB : Created. * + *=============================================================================================*/ +ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Flying aircraft take half damage. + */ + if (Altitude) { + damage /= 2; + } + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source); + + switch (res) { + case RESULT_DESTROYED: + Kill_Cargo(source); + Death_Announcement(); + new AnimClass(ANIM_FBALL1, Target_Coord()); + Delete_This(); + break; + + default: + case RESULT_HALF: + break; + } + + return(res); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Move -- Handles movement mission. * + * * + * This state machine routine is used when an aircraft (usually helicopter) is to move * + * from one location to another. It will handle any necessary take off and landing this * + * may require. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames that should elapse before this routine * + * is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Move(void) +{ + Validate(); + if (Class->IsFixedWing) { + + /* + ** Force aircraft in movement mission into a retreat + ** mission so that it leaves the map. + */ + if (*this == AIRCRAFT_A10) { + Assign_Mission(MISSION_RETREAT); + Commence(); + return(1); + } + + enum { + FLY_TO_AIRSTRIP, + BUG_OUT + }; + switch (Status) { + /* + ** Home in on target. When close enough, drop the cargo. + */ + case FLY_TO_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + return(TICKS_PER_SECOND); + } else { + + /* + ** If, for some reason, there is no cargo, then don't stick around. + */ + if (!Is_Something_Attached()) { + Status = BUG_OUT; + return(1); + } + + + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(NavCom)); + } + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + if (Distance(NavCom) < 0x0080) { + FootClass * unit = (FootClass *)Detach_Object(); + + if (unit) { + ScenarioInit++; + if (!unit->Unlimbo(Coord_Snap(Contact_With_Whom()->Coord))) { + Attach(unit); + } + ScenarioInit--; + + Transmit_Message(RADIO_OVER_OUT); + Assign_Target(TARGET_NONE); + } + Status = BUG_OUT; + } + } + return(1); + + case BUG_OUT: + return(TICKS_PER_SECOND); + } + return(5); + } + + enum { + VALIDATE_LZ, + TAKE_OFF, + FLY_TO_LZ, + LAND + }; + switch (Status) { + + /* + ** Double check and change LZ if necessary. + */ + case VALIDATE_LZ: + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } else { + if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) { + Assign_Destination(New_LZ(NavCom)); + } else { + Status = TAKE_OFF; + } + } + break; + + /* + ** Take off if necessary. + */ + case TAKE_OFF: + if (!Target_Legal(NavCom)) { + Status = VALIDATE_LZ; + } else { + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_LZ; + } + return(1); + } + break; + + /* + ** Fly toward target. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LAND; + } + return(1); + } + + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } else { + Assign_Destination(New_LZ(NavCom)); + if (!Target_Legal(NavCom)) { + Status = LAND; + } + } + return(1); + + /* + ** Land on target. + */ + case LAND: + if (IsTakingOff) { + Assign_Destination(New_LZ(NavCom)); + Status = TAKE_OFF; + } + if (Process_Landing()) { + if (MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + } + return(1); + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * * + * Use this routine when the mission for the aircraft is in doubt. This routine will find * + * an appropriate mission for the aircraft and dispatch it. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Enter_Idle_Mode(bool ) +{ + Validate(); + MissionType mission = MISSION_GUARD; + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + if (Is_Something_Attached()) { + mission = MISSION_UNLOAD; + } else { + if (Team) { + Team->Remove(this); + } + mission = MISSION_RETREAT; + } + } else { +#ifdef NEVER + if (In_Radio_Contact() && Contact_With_Whom() == Map[Coord_Cell(Coord)].Cell_Techno()) { + Transmit_Message(RADIO_IM_IN); + } +#endif + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + if (Is_Something_Attached()) { + if (IsALoaner) { + if (Team) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + Assign_Destination(Good_LZ()); + } + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if (IsALoaner) { + if (Team) { + Team->Remove(this); + } + } + + if (Class->Primary != WEAPON_NONE) { + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (Ammo == 0 && !House->IsHuman && IsALoaner) { + mission = MISSION_RETREAT; + } else { + + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); + Assign_Destination(TARGET_NONE); + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + mission = MISSION_ENTER; + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } else { + if (Team) return; + + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } + Assign_Mission(mission); + Commence(); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * * + * This support routine is used when the helicopter is to fly to the destination. It can * + * optionally slow the helicopter down as it approaches the destination. * + * * + * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? * + * * + * OUTPUT: Returns with the distance remaining between the aircraft and the destination. * + * * + * WARNINGS: Because the aircraft can be move at a fast speed, the distance to target value * + * will probably never be zero. The likely case will be that the aircraft * + * overshoots the target. * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Process_Fly_To(bool slowdown) +{ + Validate(); + COORDINATE coord; + if (Is_Target_Building(NavCom)) { + coord = As_Building(NavCom)->Docking_Coord(); + } else { + coord = As_Coord(NavCom); + } + int distance = Distance(coord); + + PrimaryFacing.Set_Desired(Direction(coord)); + + if (slowdown) { + int speed = MIN(distance, 0x0300); + speed = Bound(speed/3, 0x0020, 0x00FF); + if (Speed != speed) { + Set_Speed(speed); + } + } + + if (distance < 0x0010) { + if (slowdown) { + Set_Speed(0); + } + distance = 0; + } + return(distance); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. * + * * + * This displays the current status of the aircraft class to the mono monitor. By this * + * display bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂAltitudeÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂFdir:ÂÄBdir:ÄÂSpeed:ÂÄÄÄÄÄÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³Is Landing....³ ³ ³ \n" + "³Is Taking Off.³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Set_Cursor(36, 3);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + mono->Set_Cursor(42, 1);mono->Printf("%04X", NavCom); + mono->Set_Cursor(66, 1);mono->Printf("%d", Altitude); + mono->Set_Cursor(44, 3);mono->Printf("%d", Get_Speed()); + mono->Text_Print("X", 16 + (IsLanding?2:0), 12); + mono->Text_Print("X", 16 + (IsTakingOff?2:0), 13); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * * + * This routine is used when the player clicks over the speicifed object. It will assign * + * the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action that was nominally determined by the What_Action function. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will alter the game sequence and causes an event packet to be * + * propogated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + switch (action) { + case ACTION_ENTER: + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE); + break; + + default: + break; + } + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * * + * This routine is used when the player clicks the mouse of the specified cell. It will * + * assign the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action nominally determined by What_Action(). * + * * + * cell -- The cell over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will affect the game sequence and causes an event object to be * + * propogated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, CELL cell) +{ + Validate(); +#ifdef NEVER + switch (action) { + case ACTION_MOVE: + if (Map[cell].IsVisible) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_NOMOVE: + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + } +#endif + FootClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * * + * This routine is called as a result of player input with the intent to change the * + * mission of the aircraft. * + * * + * INPUT: mission -- The mission requested of the aircraft. * + * * + * target -- The value to assign to the aircraft's targeting computer. * + * * + * dest. -- The value to assign to the aircraft's navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: The mission specified will be executed at an indeterminate future game frame. * + * This is controlled by net/modem propogation delay. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + Validate(); + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(As_Target(), mission, target, destination); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine is used to determine what action will likely be performed if the mouse * + * were clicked over the object specified. The display system calls this routine to * + * control the mouse shape. * + * * + * INPUT: target -- Pointer to the object that the mouse is currently over. * + * * + * OUTPUT: Returns with the action that will occur if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(ObjectClass * target) const +{ + Validate(); + ActionType action = FootClass::What_Action(target); + + if (action == ACTION_SELF && (!How_Many() || (Altitude > 0) || IsTethered)) { + action = ACTION_NONE; + } + + if (action == ACTION_ATTACK && Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + + //Changed for multiplayer ST - 3/13/2019 5:31PM + //if (IsOwnedByPlayer && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + if (Is_Owned_By_Player() && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine will determine what action would occur if the mouse were clicked over the * + * cell specified. The display system calls this routine to determine what mouse shape * + * to use. * + * * + * INPUT: cell -- The cell over which the mouse is currently positioned. * + * * + * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the * + * specified cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = FootClass::What_Action(cell); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (action == ACTION_MOVE && !Map[cell].Is_Visible(PlayerPtr)) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_ATTACK && Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * * + * Use this routine to get the desired facing the aircraft should assume when landing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the normal default facing the aircraft should have when landed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Pose_Dir(void) const +{ + Validate(); + if (*this == AIRCRAFT_TRANSPORT) { + return(DIR_N); + } + return(DIR_NE); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * * + * This routine is the state machine that handles the attack mission for aircraft. It will * + * handling homing in on and firing on the target in the aircraft's targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to pass before this routine must be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Attack(void) +{ + Validate(); + if (Class->IsFixedWing) { + Assign_Mission(MISSION_HUNT); + return(1); + } + + enum { + VALIDATE_AZ, + PICK_ATTACK_LOCATION, + TAKE_OFF, + FLY_TO_POSITION, + FIRE_AT_TARGET, + FIRE_AT_TARGET2, + RETURN_TO_BASE + }; + switch (Status) { + + /* + ** Double check target and validate the attack zone. + */ + case VALIDATE_AZ: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + + /* + ** Pick a good location to attack from. + */ + case PICK_ATTACK_LOCATION: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Assign_Destination(Good_Fire_Location(TarCom)); + if (Target_Legal(NavCom)) { + Status = TAKE_OFF; + } else { + Status = RETURN_TO_BASE; + } + } + break; + + /* + ** Take off (if necessary). + */ + case TAKE_OFF: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + if (Process_Take_Off()) { + Status = FLY_TO_POSITION; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Start flying toward the destination by skewing at first. + ** As the flight progresses, the body will rotate to face + ** the direction of travel. + */ + int diff = SecondaryFacing.Difference(Direction(NavCom)); + diff = Bound(diff, -128, 128); + PrimaryFacing = SecondaryFacing.Current()+diff; + } + return(1); + } + break; + + /* + ** Fly to attack location. + */ + case FLY_TO_POSITION: + if (Target_Legal(TarCom)) { + + /* + ** If the navcom was cleared mysteriously, then try to pick + ** a new attack location. This is a likely event if the player + ** clicks on a new target while in flight to an existing target. + */ + if (!Target_Legal(NavCom)) { + Status = PICK_ATTACK_LOCATION; + return(1); + } + + int distance = Process_Fly_To(true); + + if (distance < 0x0200) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + + if (distance < 0x0010) { + Status = FIRE_AT_TARGET; + Assign_Destination(TARGET_NONE); + } + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + return(1); + } + } else { + Status = RETURN_TO_BASE; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + Status = FIRE_AT_TARGET2; + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = FIRE_AT_TARGET2; + } + break; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET2: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_REARM: + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + + if (Ammo) { + Status = PICK_ATTACK_LOCATION; + } else { + Status = RETURN_TO_BASE; + } + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + } + break; + + /* + ** Fly back to landing spot. + */ + case RETURN_TO_BASE: + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + break; + } + + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** + * AircraftClass::New_LZ -- Find a good landing zone. * + * * + * Use this routine to locate a good landing zone that is nearby the location specified. * + * By using this routine it is possible to assign the same landing zone to several * + * aircraft and they will land nearby without conflict. * + * * + * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). * + * * + * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. * + * * + * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is * + * extensive blocking terrain in the vicinity. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::New_LZ(TARGET oldlz) const +{ + Validate(); + if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) { + COORDINATE coord = As_Coord(oldlz); + + /* + ** Scan outward in a series of concentric rings up to certain distance + ** in cells. + */ + for (int radius = 0; radius < 16; radius++) { + FacingType modifier = Random_Pick(FACING_N, FACING_NW); + CELL lastcell = -1; + + /* + ** Perform a radius scan out from the original center location. Try to + ** find a cell that is allowed to be a legal LZ. + */ + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W)); + if (Map.In_Radar(newcell)) { + TARGET newtarget = ::As_Target(newcell); + + if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) { + return(newtarget); + } + lastcell = newcell; + } + } + } + } + return(oldlz); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Coord -- Calculates the point of origin for a bullet. * + * * + * This routine is used to find the exact coordinate where the bullet should appear if * + * fired from this object. * + * * + * INPUT: which -- Which weapon to consider. * + * * + * OUTPUT: Returns with the coordinate of where the projectile will appear if fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/15/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Fire_Coord(int ) const +{ + Validate(); + return(Coord_Move(Coord_Add(XYP_Coord(0, -Altitude), Coord), SecondaryFacing, 0x040)); +} + + +COORDINATE AircraftClass::Target_Coord(void) const +{ + Validate(); + return(Coord_Add(XYP_Coord(0, -Altitude), Coord)); +} + + +/*********************************************************************************************** + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * * + * This routine receives all radio messages directed at this aircraft. It is used to handle * + * all inter-object coordination. Typically, this would be for transport helicopters and * + * other complex landing operations required of helicopters. * + * * + * INPUT: from -- The source of this radio message. * + * * + * message -- The message itself. * + * * + * param -- An optional parameter that may be used to transfer additional * + * data. * + * * + * OUTPUT: Returns with the radio response from the aircraft. * + * * + * WARNINGS: Some radio messages are handled by the base classes. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + case RADIO_PREPARED: + if (Target_Legal(TarCom)) return(RADIO_NEGATIVE); + if ((Altitude == 0 && Ammo == Class->MaxAmmo) || (Altitude > 0 && Ammo > 0)) return(RADIO_ROGER); + return(RADIO_NEGATIVE); + + /* + ** Something disasterous has happened to the object in contact with. Fall back + ** and regroup. This means that any landing process is immediately aborted. + */ + case RADIO_RUN_AWAY: + if (IsLanding) { + IsLanding = false; + IsTakingOff = true; + } + Scatter(0, true); + break; + + /* + ** The ground control requests that this specified landing spot be used. + */ + case RADIO_MOVE_HERE: + FootClass::Receive_Message(from, message, param); + if (Is_Target_Building(param)) { + if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) { + return(RADIO_NEGATIVE); + } + Assign_Mission(MISSION_ENTER); + Assign_Destination((TARGET)param); + } else { + Assign_Mission(MISSION_MOVE); + Assign_Destination((TARGET)param); + } + Commence(); + return(RADIO_ROGER); + + /* + ** Ground control is requesting if the aircraft requires navigation direction. + */ + case RADIO_NEED_TO_MOVE: + FootClass::Receive_Message(from, message, param); + if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + Close_Door(5, 4); + } + + /* + ** If a civilian has entered the transport, then the transport will immediately + ** fly off the map. + */ + if (from->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)from)->Class->IsCivilian && !((InfantryClass *)from)->IsTechnician) { + Assign_Mission(MISSION_RETREAT); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->IsTransporter && How_Many() < Class->Max_Passengers()) { + FootClass::Receive_Message(from, message, param); + + if (!IsTethered && !IsLanding && !IsTakingOff && Altitude == 0) { + + Open_Door(5, 4); + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (!In_Radio_Contact() && Class->IsTransporter && How_Many() < Class->Max_Passengers() && from && House->Class->House == from->Owner() && Altitude == 0) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + /* + ** Let the base class take over processing this message. + */ + return(FootClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * * + * This routine is used by the transport helicopter to determine the location where the * + * infantry passengers should line up before loading. * + * * + * INPUT: object -- The object that is trying to load up on this transport. * + * * + * -- Reference to the cell that the passengers should move to before the * + * actual load process may begin. * + * * + * OUTPUT: Returns with the direction that the helicopter should face for the load operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/30/1995 JLB : Revamped to scan all adjacent cells. * + *=============================================================================================*/ +DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const +{ + Validate(); + CELL center = Coord_Cell(Center_Coord()); + for (int sweep = FACING_N; sweep < FACING_S; sweep++) { + moveto = Adjacent_Cell(center, FACING_S+sweep); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Generally_Clear())) return(DIR_N); + + moveto = Adjacent_Cell(center, FACING_S-sweep); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Generally_Clear())) return(DIR_N); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * * + * This routine is used by the main game state machine processor. This utility routine * + * handles a helicopter as it transitions from landed to flying state. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter reached flight level now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Take_Off(void) +{ + Validate(); + IsLanding = false; + IsTakingOff = true; + switch (Altitude) { + case 0: + Close_Door(5, 4); + PrimaryFacing = SecondaryFacing; + break; + + case FLIGHT_LEVEL/2: + PrimaryFacing.Set_Desired(Direction(NavCom)); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/3): + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Set_Speed(0x20); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/5): + Set_Speed(0x40); + break; + + case FLIGHT_LEVEL: + Set_Speed(0xFF); + IsTakingOff = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * * + * This is a support routine that is called by the main state machine routines. This * + * routine is responsible for handling the helicopter as it transitions from flight to * + * landing. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter completely landed now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Landing(void) +{ + Validate(); + IsTakingOff = false; + IsLanding = true; + switch (Altitude) { + case 0: + IsLanding = false; + return(true); + + case FLIGHT_LEVEL/2: + Set_Speed(0); + break; + + case FLIGHT_LEVEL: + break; + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * * + * This routine is used when the passability of a cell needs to be determined. This is * + * necessary when scanning for a location that the aircraft can land. * + * * + * INPUT: cell -- The cell location to check for landing. * + * * + * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + if (!Map.In_Radar(cell)) return(MOVE_NO); + + CellClass * cellptr = &Map[cell]; + + if (!cellptr->Is_Generally_Clear(true)) return(MOVE_NO); + + if (GameToPlay == GAME_NORMAL && IsOwnedByPlayer && !cellptr->Is_Visible(PlayerPtr)) return(MOVE_NO); + + return(MOVE_OK); +} + + +/*********************************************************************************************** + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * * + * Given the specified target, this routine will locate a good spot for the aircraft to * + * fire at the target. * + * * + * INPUT: target -- The target that is desired to be attacked. * + * * + * OUTPUT: Returns with the target location of the place that firing should be made from. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 06/14/1995 JLB : Finer resolution on ring scan. * + *=============================================================================================*/ +TARGET AircraftClass::Good_Fire_Location(TARGET target) const +{ + Validate(); + if (Target_Legal(target)) { + int range = Weapon_Range(0); + COORDINATE tcoord = As_Coord(target); + CELL bestcell = 0; + CELL best2cell = 0; + int bestval = -1; + int best2val = -1; + + for (int r = range-0x0180; r > 0x0180; r -= 0x0100) { + for (int face = 0; face < 255; face += 16) { + COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r); + CELL newcell = Coord_Cell(newcoord); + + if (Map.In_Radar(newcell) && (GameToPlay != GAME_NORMAL || Map[newcell].Is_Visible(PlayerPtr)) && Cell_Seems_Ok(newcell, true)) { + int dist = Distance(newcoord); + if (bestval == -1 || dist < bestval) { + best2val = bestval; + best2cell = bestcell; + bestval = dist; + bestcell = newcell; + } + } + } + if (bestval != -1) break; + } + + if (best2val == -1) { + best2cell = bestcell; + } + + /* + ** If it found a good firing location, then return this location as + ** a target value. + */ + if (bestval != -1) { + if (Random_Pick(0, 1) == 0) { + return(::As_Target(bestcell)); + } else { + return(::As_Target(best2cell)); + } + } + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * * + * This routine examines the navigation computers of other aircraft in order to see if the * + * specified cell is safe to fly to. The intent of this routine is to avoid unneccessary * + * mid-air collisions. * + * * + * INPUT: cell -- The cell to examine for clear airspace. * + * * + * strict -- Should the scan consider the aircraft, that is making this check, a * + * blocking aircraft. Typically, the aircraft itself is not considered * + * a blockage -- an aircraft can always exist where it is currently * + * located. A strict check is useful for helicopters that need to move * + * around at the slightest provocation. * + * * + * OUTPUT: Is the specified cell free from airspace conflicts? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const +{ + Validate(); + /* + ** Make sure that no other aircraft are heading to the selected location. If they + ** are, then don't consider the location as valid. + */ + TARGET astarget = ::As_Target(cell); + bool ok = true; + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * air = Aircraft.Ptr(index); + if (air && (strict || air != this) && !air->IsInLimbo) { + if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) { + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * * + * This routine is used by the render logic to draw the little container "pips". This * + * corresponds to the number of passengers for a transport helicopter or the number of * + * shots remaining for an attack helicopter. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of "pips" to render on the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Pip_Count(void) const +{ + Validate(); + int retval = 0; + + if (Class->IsTransporter) { + retval = How_Many(); + } else { + if (Ammo) { + retval = Cardinal_To_Fixed(Class->MaxAmmo, Ammo); + retval = Fixed_To_Cardinal(Class->Max_Pips(), retval); + if (!retval) retval = 1; + } + } + return(retval); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * * + * This routine is used when the aircraft needs to fly for either rearming or repairing. * + * It tries to establish contact with the support building. Once contact is established * + * the ground controller takes care of commanding the aircraft. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/04/1995 JLB : Ground controller gives orders. * + *=============================================================================================*/ +int AircraftClass::Mission_Enter(void) +{ + Validate(); + enum { + INITIAL, + TAKEOFF, + ALTITUDE, + TRAVEL, + LANDING + }; + switch (Status) { + case INITIAL: + if (Altitude < FLIGHT_LEVEL || IsLanding) { + Status = TAKEOFF; + } else { + Status = ALTITUDE; + } + break; + + case TAKEOFF: + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + Status = ALTITUDE; + } + break; + + case ALTITUDE: + /* + ** Establish radio contact with the building this helicopter is trying + ** to land at. + */ + if (In_Radio_Contact()) { + Status = TRAVEL; + } else { + TechnoClass * tech = As_Techno(NavCom); + if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) { + Transmit_Message(RADIO_HELLO, tech); + Transmit_Message(RADIO_DOCKING); + Status = TRAVEL; + } else { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } + } + break; + + case TRAVEL: + Transmit_Message(RADIO_DOCKING); + if (!In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } else { + int distance = Process_Fly_To(true); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LANDING; + } + break; + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + return(3); + } + break; + + case LANDING: + if (IsTakingOff) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } + if (Process_Landing()) { + switch (Transmit_Message(RADIO_IM_IN)) { + case RADIO_ROGER: + Assign_Mission(MISSION_GUARD); + break; + + case RADIO_ATTACH: + Limbo(); + Contact_With_Whom()->Attach(this); + break; + + default: + Enter_Idle_Mode(); + } + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * * + * This routine is used when helicopters need a place to land, but there are no obvious * + * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly * + * building if there are no helipads anywhere. In the event that there are no friendly * + * buildings anywhere on the map, then just land right where it is flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target location where this aircraft should land. This value may * + * not be a clear cell, but the normal landing logic will resolve that problem. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::Good_LZ(void) const +{ + Validate(); + /* + ** Scan through all of the buildings and try to land near + ** the helipad (if there is one) or the nearest friendly building. + */ + CELL bestcell; + int bestdist = -1; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == House) { + int dist = Distance(building); + if (*building == STRUCT_HELIPAD) { + dist /= 4; + } + if (bestdist == -1 || dist < bestdist) { + bestdist = dist; + bestcell = Coord_Cell(building->Center_Coord()); + } + } + } + + /* + ** Return with the suitable location if one was found. + */ + if (bestdist != -1) { + return(::As_Target(bestcell)); + } + + /* + ** No good location was found. Just try to land here. + */ + return(::As_Target(Coord_Cell(Coord))); +} + + +/*********************************************************************************************** + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * * + * This routine will set the speed for the aircraft. The speed is specified as a fraction * + * of full speed. * + * * + * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full * + * speed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Set_Speed(int speed) +{ + Validate(); + MPHType sp = MPHType(min((unsigned)(Class->MaxSpeed * House->AirspeedBias), MPH_LIGHT_SPEED)); + Fly_Speed(speed, sp); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * * + * This routine will determine what direction a projectile would take if it were fired * + * from the aircraft. This is the direction that the aircraft's body is facing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction of projectile fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Fire_Direction(void) const +{ + Validate(); + return(SecondaryFacing.Current()); +} + + +/*********************************************************************************************** + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * * + * This is the destructor for aircraft. It will limbo the aircraft if it isn't already * + * and also removes the aircraft from any team it may be attached to. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass::~AircraftClass(void) +{ + if (GameActive && Class) { + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Remove(this); +#endif + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + + if (GameActive && Class && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * * + * This routine will cause the aircraft to move away from its current location and then * + * enter some idle mode. Typically this is called when the aircraft is attacked while on * + * the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Scatter(COORDINATE , bool , bool ) +{ + Validate(); + if (IsLanding || Altitude == 0) { + IsLanding = false; + IsTakingOff = true; + } + Enter_Idle_Mode(); +} + + +/*********************************************************************************************** + * AircraftClass::Rearm_Delay -- Returns the delay between shots for this aircraft. * + * * + * Aircraft have a faster rearm delay than their weapon would otherwise indicate. This is * + * necessary to give helicopters a combat edge while still allowing them to share the * + * weapon types used by ground units. * + * * + * INPUT: second -- Is this for the second shot? The second shot uses the full rearm * + * delay. The first shot, if part of a two shot weapon, is given an * + * abbreviated rearm time. * + * * + * OUTPUT: Returns with the game frames to delay before the next shot can fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Rearm_Delay(bool second) const +{ + Validate(); + return(FootClass::Rearm_Delay(second)/2); +} + + +/*********************************************************************************************** + * AircraftClass::Threat_Range -- Returns with a range to scan for targets. * + * * + * This routine returns with the distance to scan for targets according to the type of * + * search requested. The search type is typically the weapon (or short) range and vicinity * + * distances. This is used by Guard and Guard Area missions. Aircraft never consider their * + * weapon range in this determination since they can fly so fast, weapon range is * + * meaningless. * + * * + * INPUT: control -- The range control parameter; * + * -1 = range doesn't matter -- return -1 for compatability reasons. * + * 0 = short range scan * + * 1 = long range scan * + * * + * OUTPUT: Returns with the lepton distance to use for the scan operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Threat_Range(int control) const +{ + Validate(); + if (control == -1) return(-1); + + int range = 20 * ICON_LEPTON_W; + if (control == 1) { + range *= 2; + } + return(range); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * * + * Aircraft don't like to be in guard mode if in flight. If this situation is detected, * + * then figure out what the aircraft should be doing and go do it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This routine typically calls the normal guard logic for ground units. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard(void) +{ + Validate(); + if (Altitude == FLIGHT_LEVEL) { + + /* + ** If part of a team, then do nothing, since the team + ** handler will take care of giving this aircraft a + ** mission. + */ + if (Team) { + if (Target_Legal(NavCom)) { + Assign_Mission(MISSION_MOVE); + } + return(TICKS_PER_SECOND); + } + + if (Class->Primary == WEAPON_NONE) { + Assign_Destination(::As_Target(Coord_Cell(Coord))); + Assign_Mission(MISSION_MOVE); + } else { + Enter_Idle_Mode(); + } + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + /* + ** Special case to force the GDI helicopter to be brain dead in the Nod + ** mission where it is supposed to be captured. + */ + if (GameToPlay == GAME_NORMAL && Scenario == 7 && House->Class->House == HOUSE_GOOD) { + return(TICKS_PER_SECOND*20); + } + + /* + ** If the aircraft is very badly damaged, then it will search for a + ** repair bay first. + */ + if (House->Available_Money() >= 100 && Health_Ratio() <= 0x0080) { + if (!In_Radio_Contact() || + (Altitude == 0 && + (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) { + + + BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true); + if (building) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft cannot attack anything because of lack of ammo, + ** abort any normal guard logic in order to look for a helipad + ** to rearm. + */ + if (Ammo == 0 && Class->Primary != WEAPON_NONE) { + if (!In_Radio_Contact()) { + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); + if (building) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } +// return(TICKS_PER_SECOND*3); + } + + /* + ** If the aircraft already has a target, then attack it if possible. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + + /* + ** Transport helicopters don't really do anything but just sit there. + */ + if (Class->Primary == WEAPON_NONE) { + return(TICKS_PER_SECOND*3); + } + + /* + ** Computer controlled helicopters will defend themselves by bouncing around + ** and looking for a free helipad. + */ + if (Altitude == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + return(FootClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * * + * This routine handles area guard logic for aircraft. Aircraft require special handling * + * for this mode since they are to guard area only if they are in a position to do so. * + * Otherwise they just defend themselves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard_Area(void) +{ + Validate(); + if (Altitude == FLIGHT_LEVEL) { + Enter_Idle_Mode(); + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + if (Altitude == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + return(FootClass::Mission_Guard_Area()); +} + + +/*********************************************************************************************** + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * * + * This routine is used to give an audio response to an attack order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Attack(void) +{ + Validate(); + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Move -- Gives audio response to move request. * + * * + * This routine is used to give an audio response to movement orders. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Move(void) +{ + Validate(); + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ACKNOWL, + VOC_AFFIRM, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Select -- Gives audio response when selected. * + * * + * This routine is called when an audio response for selection is desired. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Select(void) +{ + Validate(); + static VocType _response[] = { + VOC_VEHIC, + VOC_UNIT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/AIRCRAFT.H b/TIBERIANDAWN/AIRCRAFT.H new file mode 100644 index 000000000..2f2124dd1 --- /dev/null +++ b/TIBERIANDAWN/AIRCRAFT.H @@ -0,0 +1,256 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\aircraft.h_v 2.17 16 Oct 1995 16:47:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AIRCRAFT_H +#define AIRCRAFT_H + +#include "radio.h" +#include "fly.h" +#include "target.h" + + +class AircraftClass : public FootClass, public FlyClass +{ + public: + /* + ** This is a pointer to the class control structure for the aircraft. + */ + AircraftTypeClass const * const Class; + + //----------------------------------------------------------------------------- + void * operator new(size_t); + void operator delete(void *); + operator AircraftType(void) const {return Class->Type;}; + AircraftClass(void) : Class(0) {}; + AircraftClass(AircraftType classid, HousesType house); + virtual ~AircraftClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_AIRCRAFT;}; + + static void Init(void); + enum {FLIGHT_LEVEL=24}; + + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Hunt(void); + virtual int Mission_Retreat(void); + virtual int Mission_Move(void); + virtual int Mission_Enter(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + + /* + ** State machine support routines. + */ + bool Process_Take_Off(void); + bool Process_Landing(void); + int Process_Fly_To(bool slowdown); + + /* + ** Query functions. + */ + virtual int Threat_Range(int control) const; + virtual int Rearm_Delay(bool second) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual int Pip_Count(void) const; + TARGET Good_Fire_Location(TARGET target) const; + bool Cell_Seems_Ok(CELL cell, bool landing=false) const; + DirType Pose_Dir(void) const; + TARGET Good_LZ(void) const; + virtual DirType Fire_Direction(void) const; + + /* + ** Landing zone support functionality. + */ + bool Is_LZ_Clear(TARGET target) const; + TARGET New_LZ(TARGET oldlz) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual int Exit_Object(TechnoClass *); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Set_Speed(int speed); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ +// virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + virtual BulletClass * Fire_At(TARGET target, int which); + virtual TARGET As_Target(void) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual void Enter_Idle_Mode(bool initial = false); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char * INI_Name(void) {return "AIRCRAFT";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + public: + + /* + ** This is the facing used for the body of the aircraft. Typically, this is the same + ** as the PrimaryFacing, but in the case of helicopters, it can be different. + */ + FacingClass SecondaryFacing; + + private: + + /* + ** Aircraft can be in either state of landing, taking off, or in steady altitude. + ** These flags are used to control transition between flying and landing. It is + ** necessary to handle the transition in this manner so that it occurs smoothly + ** during the graphic processing section. + */ + unsigned IsLanding:1; + unsigned IsTakingOff:1; + + /* + ** It is very common for aircraft to be homing in on a target. When this flag is + ** true, the aircraft will constantly adjust its facing toward the TarCom. When the + ** target is very close (one cell away or less), then this flag is automatically cleared. + ** This is because the homing algorithm is designed to get the aircraft to the destination + ** but no more. Checking when this flag is cleared is a way of flagging transition into + ** a new mode. Example: Transport helicopters go into a hovering into correct position + ** mode when the target is reached. + */ + unsigned IsHoming:1; + + /* + ** Helicopters that are about to land must hover into a position exactly above the landing + ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over + ** the TarCom. The facing of the aircraft is not altered by this movement. The affect + ** like the helicopter is hovering and shifting sideways to position over the landing + ** zone. When the position is over the landing zone, then this flag is set to false. + */ + unsigned IsHovering:1; + + /* + ** This is the jitter tracker to be used when the aircraft is a helicopter and + ** is flying. It is most noticable when the helicopter is hovering. + */ + unsigned char Jitter; + + public: + /* + ** This is the altitude of the aircraft. It is expressed in pixels that + ** the shadow is offset to the south. If the altitude reaches zero, then + ** the aircraft has landed. The altitude for normal aircraft is at + ** Flight_Level(). + */ + int Altitude; + + private: + + /* + ** This timer controls when the aircraft will reveal the terrain around itself. + ** When this timer expires and this aircraft has a sight range, then the + ** look around process will occur. + */ + TCountDownTimerClass SightTimer; + + /* + ** Most attack aircraft can make several attack runs. This value contains the + ** number of attack runs the aircraft has left. When this value reaches + ** zero then the aircraft is technically out of ammo. + */ + char AttacksRemaining; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ALLOC.CPP b/TIBERIANDAWN/ALLOC.CPP new file mode 100644 index 000000000..b1a8c45ac --- /dev/null +++ b/TIBERIANDAWN/ALLOC.CPP @@ -0,0 +1,590 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0) + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 8*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ANIM.CPP b/TIBERIANDAWN/ANIM.CPP new file mode 100644 index 000000000..d65d448f1 --- /dev/null +++ b/TIBERIANDAWN/ANIM.CPP @@ -0,0 +1,1263 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\anim.cpv 2.18 16 Oct 1995 16:48:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::Adjust_Coord -- Adjusts anim coordinates * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::As_Target -- Converts the animation into a target value. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Sort_Above -- Sorts the animation above the target specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * AnimClass::Validate -- validates anim pointer * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * AnimClass::VTable; + + +/*********************************************************************************************** + * AnimClass::Validate -- validates anim pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int AnimClass::Validate(void) const +{ + int num; + + num = Anims.ID(this); + if (num < 0 || num >= ANIM_MAX) { + Validate_Error("ANIM"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = *Anims.Ptr(index); + + if (anim.Object == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ + Validate(); + if (Object) { + return(Coord_Add(Object->Sort_Y(), 0x00010000L)); + } + if (Target_Legal(SortTarget)) { + ObjectClass * obj = As_Object(SortTarget); + if (obj && obj->IsActive) { + return(Coord_Add(obj->Sort_Y(), 0x00010000L)); + } + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (*this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ + Validate(); + if (Object) { + return(Coord_Add(Coord, Object->Center_Coord())); + } + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) +{ + Validate(); + if (Delay) return(false); + IsToDisplay = true; + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +//#pragma off (unreferenced) +void AnimClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + + bool render_legacy = !IsInvisible && (Class->VirtualAnim == ANIM_NONE || window != WINDOW_VIRTUAL); + bool render_virtual = VirtualAnim != NULL && window == WINDOW_VIRTUAL; + if (render_legacy) { + void const * shapefile = Class->Get_Image_Data(); + if (shapefile) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + + /* + ** Some animations require special fixups. + */ + switch (Class->Type) { + case ANIM_ION_CANNON: + if (window != WINDOW_VIRTUAL) { + y -= Get_Build_Frame_Height(shapefile) >> 1; + } else { + flags = flags | SHAPE_BOTTOM; + } + y += 12; + break; + + case ANIM_RAPT_DIE: + case ANIM_STEG_DIE: + case ANIM_TREX_DIE: + case ANIM_TRIC_DIE: + case ANIM_ATOM_BLAST: + transtable = Map.UnitShadow; + break; + } + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (!transtable && Class->IsWhiteTrans) transtable = Map.WhiteTranslucentTable; + if (!transtable && Class->IsTranslucent) transtable = Map.TranslucentTable; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + if (IsAlternate) { + flags = flags | SHAPE_FADING; + remap = Map.RemapTables[HOUSE_GOOD][0]; + } + if (transtable) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape, but ignore legacy if beyond normal stage count. + */ + if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, Class->VirtualScale); + } + } + } + if (render_virtual) { + VirtualAnim->Make_Visible(); + VirtualAnim->Draw_It(x, y, window); + VirtualAnim->Make_Invisible(); + } +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +// ObjectClass::Mark(mark); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ + Validate(); + static short const OverlapN[] = {0, -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), -(2*MAP_CELL_W), -(2*MAP_CELL_W-1), -(2*MAP_CELL_W+1), REFRESH_EOL}; + static short const OverlapNW[] = {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2), -(MAP_CELL_W*2+2), -(MAP_CELL_W*2+1), REFRESH_EOL}; + static short const OverlapW[] = {0, -1, -2, -(MAP_CELL_W+1), -(MAP_CELL_W+2), REFRESH_EOL}; + static short const OverlapSW[] = {0, -1, MAP_CELL_W, (MAP_CELL_W-1), (MAP_CELL_W-2), (MAP_CELL_W*2-2), (MAP_CELL_W*2-1), REFRESH_EOL}; + static short const OverlapS[] = {0, MAP_CELL_W-1, MAP_CELL_W, MAP_CELL_W+1, 2*MAP_CELL_W+1, 2*MAP_CELL_W, 2*MAP_CELL_W-1, REFRESH_EOL}; + static short const OverlapSE[] = {0, 1, MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2), (MAP_CELL_W*2+2), (MAP_CELL_W*2+1), REFRESH_EOL}; + static short const OverlapE[] = {0, 1, 2, -(MAP_CELL_W-1), -(MAP_CELL_W-2), REFRESH_EOL}; + static short const OverlapNE[] = {0, 1, -MAP_CELL_W, -(MAP_CELL_W-1), -(MAP_CELL_W-2), -(MAP_CELL_W*2-2), -(MAP_CELL_W*2-1), REFRESH_EOL}; + static short const OverlapIon[] = { + (-MAP_CELL_W * 7) - 1, (-MAP_CELL_W * 7), (-MAP_CELL_W * 7) + 1, + (-MAP_CELL_W * 6) - 1, (-MAP_CELL_W * 6), (-MAP_CELL_W * 6) + 1, + (-MAP_CELL_W * 5) - 1, (-MAP_CELL_W * 5), (-MAP_CELL_W * 5) + 1, + (-MAP_CELL_W * 4) - 1, (-MAP_CELL_W * 4), (-MAP_CELL_W * 4) + 1, + (-MAP_CELL_W * 3) - 1, (-MAP_CELL_W * 3), (-MAP_CELL_W * 3) + 1, + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + REFRESH_EOL + }; + + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + switch (Class->Type) { + case ANIM_CHEM_N: + case ANIM_FLAME_N: + return(OverlapN); + + case ANIM_CHEM_NW: + case ANIM_FLAME_NW: + return(OverlapNW); + + case ANIM_CHEM_W: + case ANIM_FLAME_W: + return(OverlapW); + + case ANIM_CHEM_SW: + case ANIM_FLAME_SW: + return(OverlapSW); + + case ANIM_CHEM_S: + case ANIM_FLAME_S: + return(OverlapS); + + case ANIM_CHEM_SE: + case ANIM_FLAME_SE: + return(OverlapSE); + + case ANIM_CHEM_E: + case ANIM_FLAME_E: + return(OverlapE); + + case ANIM_CHEM_NE: + case ANIM_FLAME_NE: + return(OverlapNE); + + case ANIM_ION_CANNON: + return(OverlapIon); + + case ANIM_ATOM_BLAST: + return(OverlapAtom); + + default: + break; + } + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(void) const +{ + Validate(); + static short _simple[] = {REFRESH_EOL}; + + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + AnimClass *ptr; + + Anims.Free_All(); + + ptr = new AnimClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void *AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr) { + ((AnimClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void *ptr) +{ + if (ptr) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop, bool alt) : + Class(&AnimTypeClass::As_Reference(animnum)) +{ + Object = 0; + SortTarget = TARGET_NONE; + Owner = HOUSE_NONE; + + if (Class->Stages == -1) { + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + Accum = 0; + coord = Adjust_Coord(coord); + Unlimbo(coord); + + VisibleFlags = 0xffff; + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + // Added PlayerPtr here as Sight_From now needs to know who to perform the action for. This should be OK as it's not used in MP. ST - 3/28/2019 2:43PM + Map.Sight_From(PlayerPtr, Coord_Cell(coord), 4, false); + } + + /* + ** Determine the time before the first animation process. For time delayed + ** animations, this is the value passed as a parameter. + */ + Delay = timedelay; + + Loops = (unsigned char)(MAX(loop, (unsigned char)1) * Class->Loops); + Loops = (unsigned char)MAX(Loops, (unsigned char)1); + + IsToDelete = false; + IsBrandNew = true; + IsAlternate = alt; + IsInvisible = false; + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } + + /* + ** Check for a virtual animation + */ + if (Class->VirtualAnim != ANIM_NONE) { + VirtualAnim = new AnimClass(Class->VirtualAnim, 0, timedelay, loop, alt); + if (VirtualAnim != NULL) { + VirtualAnim->Make_Invisible(); + } + } else { + VirtualAnim = NULL; + } +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ + Validate(); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Object) { + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + int index; + for (index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index) != this && Anims.Ptr(index)->Object == Object) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index == Anims.Count()) { + Object->Fire_Out(); + if (Object->In_Which_Layer() == LAYER_GROUND) Object->Mark(MARK_OVERLAP_UP); + Object->IsAnimAttached = false; + if (Object->In_Which_Layer() == LAYER_GROUND) Object->Mark(MARK_OVERLAP_DOWN); + } + Coord = Coord_Add(Object->Center_Coord(), Coord); + Object = NULL; + } + + Limbo(); + } +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ + Validate(); + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Coord_Cell(Center_Coord())].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + Delete_This(); + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (Object && Class->Damage && stage < Class->Stages) { + unsigned int accum = Accum; + + accum += Class->Damage; + + if (accum > 255) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = accum >> 8; + if (Object->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + //Object = 0; + Delete_This(); + if (VirtualAnim != NULL) { + VirtualAnim->Delete_This(); + } + return; + } + } + Accum = (unsigned char)(accum & 0x00FF); + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existance while in plain sight. + */ + if (Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + if (Class->VirtualAnim != ANIM_NONE) { + Make_Invisible(); + if (VirtualAnim == NULL) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + Delete_This(); + } + } + } + else { + if ((Class->VirtualStages < 0) || (stage >= Class->VirtualStages)) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + Delete_This(); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ + Validate(); + if (!obj) return; + + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_DOWN); + Map.Remove(this, In_Which_Layer()); + Object = obj; + Map.Submit(this, In_Which_Layer()); + Coord = Coord_Sub(Coord, obj->Center_Coord()); +} + + +/*********************************************************************************************** + * AnimClass::Sort_Above -- Sorts the animation right above the specified target. * + * * + * Allows an animation to always be sorted above a particular target. Typically used * + * for explosions and other effects that look weird beneath those objects. * + * * + * INPUT: target -- Target to sort above. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/2019 SKY : Created. * + *=============================================================================================*/ +void AnimClass::Sort_Above(TARGET target) +{ + SortTarget = target; +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ + Validate(); + if (Object || Class->IsGroundLayer) { + return(LAYER_GROUND); + } + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsiquent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } + + /* + ** If this is the kind of animation that sticks to whatever object is in the same + ** location, then attach the animation to the object. If the animation is already + ** attached, then do nothing. + */ + if (!Object && Class->IsSticky && Map.In_Radar(cell)) { + UnitClass * unit = Map[cell].Cell_Unit(); + + if (unit && *unit == UNIT_GUNBOAT) { + Attach_To(unit); + } + } +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ + Validate(); + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (Owner != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && obj->Is_Techno() && obj->Owner() == Owner) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_TEMPLE) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (!building) building = (BuildingClass *)backup; + } + + int radius = 3; + int rawdamage = 200; + if (GameToPlay == GAME_NORMAL) { + radius = 4; + rawdamage = 1000; + Fade_Palette_To(WhitePalette, 30, NULL); + } + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + int xpos = Cell_X(cell) + x; + int ypos = Cell_Y(cell) + y; + + /* + ** If the potential damage cell is outside of the map bounds, + ** then don't process it. This unusual check method ensures that + ** damage won't wrap from one side of the map to the other. + */ + if ((unsigned)xpos > MAP_CELL_W) { + continue; + } + if ((unsigned)ypos > MAP_CELL_H) { + continue; + } + CELL tcell = XY_Cell(xpos, ypos); + if (!Map.In_Radar(tcell)) continue; + + int damage = rawdamage / ((ABS(radius)/2)+1); + Explosion_Damage(Cell_Coord(tcell), damage, building, WARHEAD_FIRE); + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell)); + } + } + Shake_The_Screen(3); + if (GameToPlay == GAME_NORMAL) { + Fade_Palette_To(GamePalette, 15, NULL); + } + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + /* + ** Flame throwers leave scorch marks in unusual positions. In addition, they leave fire + ** shards in unusual positions as well. + */ + if (Class->IsFlameThrower) { + COORDINATE c2 = Coord_Move(Center_Coord(), (DirType)((Class->Type - ANIM_FLAME_N)<<5), 0x00E0); + CELL cell = Coord_Cell(c2); + COORDINATE c3 = Map.Closest_Free_Spot(Coord_Move(Center_Coord(), (DirType)((Class->Type - ANIM_FLAME_N)<<5), 0x0140), true); + + c2 = Map.Closest_Free_Spot(c2, true); + if (c3 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c3)].Cell_Terrain()) { + new AnimClass(ANIM_FIRE_SMALL, c3, 0, 2); + } + } + if (c2 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c2)].Cell_Terrain()) { + new AnimClass(ANIM_FIRE_SMALL, c2, 0, 2); + } + } + new SmudgeClass(SMUDGE_SCORCH1, c2); + if (c3 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c3)].Cell_Terrain()) { + new AnimClass(ANIM_SMOKE_M, c3); + } + } + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. + */ + switch (Class->Type) { + case ANIM_ION_CANNON: { + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (Owner != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && obj->Is_Techno() && obj->Owner() == Owner && !obj->IsInLimbo) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_EYE) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (!building) building = (BuildingClass *)backup; + } + Explosion_Damage(Center_Coord(), 600, building, WARHEAD_PB); + } + break; + + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + if (Random_Pick(0, 1) == 1) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + } + if (Random_Pick(0, 1) == 1) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + if (newanim && Object) { + newanim->Attach_To(Object); + } + break; + + default: + break; + } +} + + +void AnimClass::Chain(void) +{ + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { + ((AnimTypeClass const *&)Class) = &AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + } +} + + +/*********************************************************************************************** + * AnimClass::As_Target -- Converts the animation into a target value. * + * * + * This support routine is used to convert the animation (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the animation as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET AnimClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_ANIMATION, Anims.ID(this))); +} + + +/*************************************************************************** + * AnimClass::Adjust_Coord -- Adjusts anim coordinates * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/17/1995 PWG : Created. * + *=========================================================================*/ +COORDINATE AnimClass::Adjust_Coord(COORDINATE coord) +{ + Validate(); + int x,y; + + switch (Class->Type) { + case ANIM_ATOM_DOOR: + x = -1; + y = -36; + break; + + default: + return(coord); + } + COORDINATE addval = XYPixel_Coord(x, y); + coord = Coord_Add(coord, addval); + return(coord); +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ + Validate(); + if (all) { + if (VirtualAnim && VirtualAnim->As_Target() == target) { + VirtualAnim = NULL; + } + if (Object && Object->As_Target() == target) { + Map.Remove(this, In_Which_Layer()); + Object = NULL; + IsToDelete = true; + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/ANIM.H b/TIBERIANDAWN/ANIM.H new file mode 100644 index 000000000..084c34ea7 --- /dev/null +++ b/TIBERIANDAWN/ANIM.H @@ -0,0 +1,202 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\anim.h_v 2.20 16 Oct 1995 16:45:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ANIM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : May 30, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ANIM_H +#define ANIM_H + +#include "type.h" + +/********************************************************************************************** +** This is the class that controls the shape animation objects. Shape animation objects are +** displayed over the top of the game map. Typically, they are used for explosion and fire +** effects. +*/ +class AnimClass : public ObjectClass, private StageClass { + public: + + static void * AnimClass::operator new(size_t size); + static void AnimClass::operator delete(void *ptr); + AnimClass(void) : Class(0) {Owner=HOUSE_NONE;Object=0;}; // Default constructor does nothing. + AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1, bool alt=false); + virtual ~AnimClass(void); + operator AnimType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_ANIM;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + void Attach_To(ObjectClass *obj); + void Sort_Above(TARGET target); + void Make_Invisible(void) {IsInvisible = true;}; + void Make_Visible(void) {IsInvisible = false;}; + + /* + ** 2019/09/19 JAS + ** Added functions for accessing which players can see this anim + */ + void Set_Visible_Flags(unsigned flags) { VisibleFlags = flags; } + unsigned Get_Visible_Flags() const { return (Delay == 0) ? VisibleFlags : 0; } + + virtual bool Can_Place_Here(COORDINATE ) const {return true;} + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual bool Render(bool forced); + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual short const * Occupy_List(void) const; + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void AI(void); + virtual TARGET As_Target(void) const; + virtual void Detach(TARGET target, bool all); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this animation is attached to an object, then this points to that object. An + ** animation that is attached will follow that object as it moves. This is important + ** for animations such as flames and smoke. + */ + ObjectClass * Object; + + /* + ** If specified, this animation uses the sort target for Y sorting + */ + TARGET SortTarget; + + /* + ** If this animation has an owner, then it will be recorded here. An owner + ** is used when damage is caused by this animation during the middle of its + ** animation. + */ + HousesType Owner; + + /* + ** This counter tells how many more times the animation should loop before it + ** terminates. + */ + unsigned char Loops; + + protected: + void Middle(void); + void Start(void); + void Chain(void); + + private: + /* + ** Define a function to make adjustments for where special animations + ** are going to render. + */ + COORDINATE Adjust_Coord(COORDINATE coord); + + /* + ** Delete this animation at the next opportunity. This is flagged when the + ** animation is to be prematurely ended as a result of some outside event. + */ + unsigned IsToDelete:1; + + /* + ** If the animation has just been created, then don't do any animation + ** processing until it has been through the render loop at least once. + */ + unsigned IsBrandNew:1; + + // Use alternate color when drawing? + unsigned IsAlternate:1; + + /* + ** If this animation is invisible, then this flag will be true. An invisible + ** animation is one that is created for the sole purpose of keeping all + ** machines syncronised. It will not be displayed. + */ + unsigned IsInvisible:1; + + /* + ** 2019/09/19 JAS + ** Flags storing which players can see this anim. + */ + unsigned VisibleFlags; + + /* + ** This points to the type of animation object this is. + */ + AnimTypeClass const * const Class; + + /* + ** Is this animation in a temporary suspended state? If so, then it won't + ** be rendered until this flag is false. The flag will be set to false + ** after the first countdown timer reaches 0. + */ + unsigned char Delay; + + /* + ** If this is an animation that damages whatever it is attached to, then this + ** value holds the accumulation of fractional damage points. When the accumulated + ** fractions reach 256, then one damage point is applied to the attached object. + */ + unsigned char Accum; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; + + /* + ** This points to the virtual animation. + */ + AnimClass * VirtualAnim; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/AUDIO.CPP b/TIBERIANDAWN/AUDIO.CPP new file mode 100644 index 000000000..674679ef3 --- /dev/null +++ b/TIBERIANDAWN/AUDIO.CPP @@ -0,0 +1,636 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\audio.cpv 2.17 16 Oct 1995 16:50:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 4, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + + + +/* +** +** +** Win32lib stubs +** +** +** +*/ + + +SFX_Type SoundType; +Sample_Type SampleType; + +int File_Stream_Sample(char const *filename, BOOL real_time_start) {return 1;}; +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) {return 1;}; +void __cdecl Sound_Callback(void) {}; +void __cdecl far maintenance_callback(void) {}; +void *Load_Sample(char const *filename) {return NULL;}; +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) {return 0;} +long Sample_Read(int fh, void *buffer, long size) {return 0;}; +void Free_Sample(void const *sample) {}; +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels) {return 0;}; +void Sound_End(void) {}; +void Stop_Sample(int handle) {}; +BOOL Sample_Status(int handle) {return 0;}; +BOOL Is_Sample_Playing(void const * sample) {return 0;}; +void Stop_Sample_Playing(void const * sample) {}; +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) {return 1;}; +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) {return 1;}; +int Set_Sound_Vol(int volume) {return 0;}; +int Set_Score_Vol(int volume) {return 0;}; +void Fade_Sample(int handle, int ticks) {}; +int Get_Free_Sample_Handle(int priority) {return 1;}; +int Get_Digi_Handle(void) {return 1;} +long Sample_Length(void const *sample) {return 0;}; +void Restore_Sound_Buffers (void) {}; +BOOL Set_Primary_Buffer_Format(void) {return 0;}; +BOOL Start_Primary_Sound_Buffer (BOOL forced) {return 0;}; +void Stop_Primary_Sound_Buffer (void) {}; + + + + + + + + + + + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_JUV, // Juvenile sound effect alternate option. + IN_VAR, // Infantry variance response modification. +} ContextType; + +// static struct { // MBL 02.21.2019 +// Had to name the struct for VS 2017 distributed build. ST - 4/10/2019 3:59PM +struct SoundEffectNameStruct { + char const *Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Special voices (typically associated with the commando). + */ + {"BOMBIT1", 20, IN_NOVAR}, // VOC_RAMBO_PRESENT "I've got a present for ya" + {"CMON1", 20, IN_NOVAR}, // VOC_RAMBO_CMON "c'mon" + {"GOTIT1", 20, IN_NOVAR}, // VOC_RAMBO_UGOTIT "you got it" * + {"KEEPEM1", 20, IN_NOVAR}, // VOC_RAMBO_COMIN "keep 'em commin'" + {"LAUGH1", 20, IN_NOVAR}, // VOC_RAMBO_LAUGH "hahaha" + {"LEFTY1", 20, IN_NOVAR}, // VOC_RAMBO_LEFTY "that was left handed" * + {"NOPRBLM1", 20, IN_NOVAR}, // VOC_RAMBO_NOPROB "no problem" +// {"OHSH1", 20, IN_NOVAR}, // VOC_RAMBO_OHSH "oh shiiiiii...." + {"ONIT1", 20, IN_NOVAR}, // VOC_RAMBO_ONIT "I'm on it" + {"RAMYELL1", 20, IN_NOVAR}, // VOC_RAMBO_YELL "ahhhhhhh" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_RAMBO_ROCK "time to rock and roll" + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_RAMBO_TUFF "real tuff guy" * + {"YEAH1", 20, IN_NOVAR}, // VOC_RAMBO_YEA "yea" * + {"YES1", 20, IN_NOVAR}, // VOC_RAMBO_YES "yes" * + {"YO1", 20, IN_NOVAR}, // VOC_RAMBO_YO "yo" + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + /* + ** Infantry and vehicle responses. + */ + {"2DANGR1", 10, IN_VAR}, // VOC_2DANGER "negative, too dangerous" + {"ACKNO", 10, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 10, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 10, IN_VAR}, // VOC_AWAIT1 "awaiting orders" +// {"BACKUP", 10, IN_VAR}, // VOC_BACKUP "send backup" +// {"HELP", 10, IN_VAR}, // VOC_HELP "send help" + {"MOVOUT1", 10, IN_VAR}, // VOC_MOVEOUT "movin' out" + {"NEGATV1", 10, IN_VAR}, // VOC_NEGATIVE "negative" + {"NOPROB", 10, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 10, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 10, IN_VAR}, // VOC_REPORT "reporting" + {"RITAWAY", 10, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 10, IN_VAR}, // VOC_ROGER "roger" +// {"SIR1", 10, IN_VAR}, // VOC_SIR1 "sir?" +// {"SQUAD1", 10, IN_VAR}, // VOC_SQUAD1 "squad reporting" +// {"TARGET1", 10, IN_VAR}, // VOC_PRACTICE "target practice" + {"UGOTIT", 10, IN_VAR}, // VOC_UGOTIT "you got it" + {"UNIT1", 10, IN_VAR}, // VOC_UNIT1 "unit reporting" + {"VEHIC1", 10, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 10, IN_VAR}, // VOC_YESSIR "yes sir" + + /* + ** Sound effects that have a juvenile counterpart. + */ + {"BAZOOK1", 1, IN_JUV}, // VOC_BAZOOKA Gunfire + {"BLEEP2", 1, IN_JUV}, // VOC_BLEEP Clean metal bing + {"BOMB1", 1, IN_JUV}, // VOC_BOMB1 Crunchy parachute bomb type explosion + {"BUTTON", 1, IN_JUV}, // VOC_BUTTON Dungeon Master button click + {"COMCNTR1", 10, IN_JUV}, // VOC_RADAR_ON Elecronic static with beeps + {"CONSTRU2", 10, IN_JUV}, // VOC_CONSTRUCTION construction sounds + {"CRUMBLE", 1, IN_JUV}, // VOC_CRUMBLE muffled crumble sound + {"FLAMER2", 4, IN_JUV}, // VOC_FLAMER1 flame thrower + {"GUN18", 4, IN_JUV}, // VOC_RIFLE rifle shot + {"GUN19", 4, IN_JUV}, // VOC_M60 machine gun burst -- 6 rounds + {"GUN20", 4, IN_JUV}, // VOC_GUN20 bat hitting heavy metal door + {"GUN5", 4, IN_JUV}, // VOC_M60A medium machine gun burst + {"GUN8", 4, IN_JUV}, // VOC_MINI mini gun burst + {"GUNCLIP1", 1, IN_JUV}, // VOC_RELOAD gun clip reload + {"HVYDOOR1", 5, IN_JUV}, // VOC_SLAM metal plates slamming together + {"HVYGUN10", 1, IN_JUV}, // VOC_HVYGUN10 loud sharp cannon + {"ION1", 1, IN_JUV}, // VOC_ION_CANNON partical beam + {"MGUN11", 1, IN_JUV}, // VOC_MGUN11 alternate tripple burst + {"MGUN2", 1, IN_JUV}, // VOC_MGUN2 M-16 tripple burst + {"NUKEMISL", 1, IN_JUV}, // VOC_NUKE_FIRE long missile sound + {"NUKEXPLO", 1, IN_JUV}, // VOC_NUKE_EXPLODE long but not loud explosion + {"OBELRAY1", 1, IN_JUV}, // VOC_LASER humming laser beam + {"OBELPOWR", 1, IN_JUV}, // VOC_LASER_POWER warming-up sound of laser beam + {"POWRDN1", 1, IN_JUV}, // VOC_RADAR_OFF doom door slide + {"RAMGUN2", 1, IN_JUV}, // VOC_SNIPER silenced rifle fire + {"ROCKET1", 1, IN_JUV}, // VOC_ROCKET1 rocket launch variation #1 + {"ROCKET2", 1, IN_JUV}, // VOC_ROCKET2 rocket launch variation #2 + {"SAMMOTR2", 1, IN_JUV}, // VOC_MOTOR dentists drill + {"SCOLD2", 1, IN_JUV}, // VOC_SCOLD cannot perform action feedback tone + {"SIDBAR1C", 1, IN_JUV}, // VOC_SIDEBAR_OPEN xylophone clink + {"SIDBAR2C", 1, IN_JUV}, // VOC_SIDEBAR_CLOSE xylophone clink + {"SQUISH2", 1, IN_JUV}, // VOC_SQUISH2 crushing infantry + {"TNKFIRE2", 1, IN_JUV}, // VOC_TANK1 sharp tank fire with recoil + {"TNKFIRE3", 1, IN_JUV}, // VOC_TANK2 sharp tank fire + {"TNKFIRE4", 1, IN_JUV}, // VOC_TANK3 sharp tank fire + {"TNKFIRE6", 1, IN_JUV}, // VOC_TANK4 big gun tank fire + {"TONE15", 0, IN_JUV}, // VOC_UP credits counting up + {"TONE16", 0, IN_JUV}, // VOC_DOWN credits counting down + {"TONE2", 1, IN_JUV}, // VOC_TARGET target sound + {"TONE5", 10, IN_JUV}, // VOC_SONAR sonar echo + {"TOSS", 1, IN_JUV}, // VOC_TOSS air swish + {"TRANS1", 1, IN_JUV}, // VOC_CLOAK stealth tank + {"TREEBRN1", 1, IN_JUV}, // VOC_BURN burning crackle + {"TURRFIR5", 1, IN_JUV}, // VOC_TURRET muffled gunfire + {"XPLOBIG4", 5, IN_JUV}, // VOC_XPLOBIG4 very long muffled explosion + {"XPLOBIG6", 5, IN_JUV}, // VOC_XPLOBIG6 very long muffled explosion + {"XPLOBIG7", 5, IN_JUV}, // VOC_XPLOBIG7 very long muffled explosion + {"XPLODE", 1, IN_JUV}, // VOC_XPLODE long soft muffled explosion + {"XPLOS", 4, IN_JUV}, // VOC_XPLOS short crunchy explosion + {"XPLOSML2", 5, IN_JUV}, // VOC_XPLOSML2 muffled mechanical explosion + + /* + ** Generic sound effects (no variations). + */ + {"NUYELL1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"NUYELL3", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"NUYELL4", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"NUYELL5", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"NUYELL6", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"NUYELL7", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"NUYELL10", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"NUYELL11", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"NUYELL12", 10, IN_NOVAR}, // VOC_SCREAM12 short infantry scream + {"YELL1", 1, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"MYES1", 10, IN_NOVAR}, // VOC_YES "Yes?" + {"MCOMND1", 10, IN_NOVAR}, // VOC_COMMANDER "Commander?" + {"MHELLO1", 10, IN_NOVAR}, // VOC_HELLO "Hello?" + {"MHMMM1", 10, IN_NOVAR}, // VOC_HMMM "Hmmm?" +// {"MHASTE1", 10, IN_NOVAR}, // VOC_PROCEED1 "I will proceed, post haste." +// {"MONCE1", 10, IN_NOVAR}, // VOC_PROCEED2 "I will proceed, at once." +// {"MIMMD1", 10, IN_NOVAR}, // VOC_PROCEED3 "I will proceed, immediately." +// {"MPLAN1", 10, IN_NOVAR}, // VOC_EXCELLENT1 "That is an excellent plan." +// {"MPLAN2", 10, IN_NOVAR}, // VOC_EXCELLENT2 "Yes, that is an excellent plan." + {"MPLAN3", 10, IN_NOVAR}, // VOC_EXCELLENT3 "A wonderful plan." +// {"MACTION1", 10, IN_NOVAR}, // VOC_EXCELLENT4 "Astounding plan of action commander." +// {"MREMARK1", 10, IN_NOVAR}, // VOC_EXCELLENT5 "Remarkable contrivance." + {"MCOURSE1", 10, IN_NOVAR}, // VOC_OF_COURSE "Of course." + {"MYESYES1", 10, IN_NOVAR}, // VOC_YESYES "Yes yes yes." + {"MTIBER1", 10, IN_NOVAR}, // VOC_QUIP1 "Mind the Tiberium." +// {"MMG1", 10, IN_NOVAR}, // VOC_QUIP2 "A most remarkable Metasequoia Glyptostroboides." + {"MTHANKS1", 10, IN_NOVAR}, // VOC_THANKS "Thank you." + + {"CASHTURN", 1, IN_NOVAR}, // VOC_CASHTURN Sound of money being piled up. + {"BLEEP2", 10, IN_NOVAR}, // VOC_BLEEPY3 Clean computer bleep sound. + {"DINOMOUT", 10, IN_NOVAR}, // VOC_DINOMOUT Movin' out in dino-speak. + {"DINOYES", 10, IN_NOVAR}, // VOC_DINOYES Yes Sir in dino-speak. + {"DINOATK1", 10, IN_NOVAR}, // VOC_DINOATK1 Dino attack sound. + {"DINODIE1", 10, IN_NOVAR}, // VOC_DINODIE1 Dino die sound. + +#ifdef PETROGLYPH_EXAMPLE_MOD + {"NUKE_LOB", 10, IN_NOVAR} // VOC_NUKE_LOB Mod expansion unit firing sound +#endif //PETROGLYPH_EXAMPLE_MOD + +}; + + +// +// External handlers. ST - 2/20/2019 3:41PM +// +extern void On_Sound_Effect(int sound_index, int variation, COORDINATE coord); +// extern void On_Speech(int speech_index); // MBL 02.06.2020 +extern void On_Speech(int speech_index, HouseClass *house); +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation) +{ + // + // Intercept sound effect calls. ST - 2/20/2019 3:37PM + // + On_Sound_Effect((int)voc, variation, coord); + +#if (0) + unsigned distance; + CELL cell_pos; + int pan_value; + + if (!Options.Volume || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + distance = 0xFF; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + distance = Map.Cell_Distance(cell_pos, Coord_Cell(Map.TacticalCoord)); + distance = (unsigned int)MIN((int)distance, (int)MAP_CELL_W); + distance = Cardinal_To_Fixed(MAP_CELL_W, distance); + distance = MIN(distance, 0xFFu); + distance ^= 0xFF; + + distance /= 2; + distance = MAX(distance, 25U); + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) >> 1); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth >> 1)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); +// pan_value = MAX((int)pan_value, (int)-0x7FFF); +// pan_value = MIN((int)pan_value, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, (VolType)Fixed_To_Cardinal(distance, Options.Volume), variation, pan_value); +#endif +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, VolType volume, int variation, signed short pan_value) +{ + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (!Options.Volume || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (Special.IsJuvenile && SoundEffectName[voc].Where == IN_JUV) { + ext = ".JUV"; + } else { + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MixFileClass::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr) { + return(Play_Sample(ptr, Fixed_To_Cardinal(SoundEffectName[voc].Priority, (int)volume), (int)volume, pan_value)); + } + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +char const * Speech[VOX_COUNT] = { + "ACCOM1", // mission accomplished + "FAIL1", // your mission has failed + "BLDG1", // unable to comply, building in progress + "CONSTRU1", // construction complete + "UNITREDY", // unit ready + "NEWOPT1", // new construction options + "DEPLOY1", // cannot deploy here + "GDIDEAD1", // GDI unit destroyed + "NODDEAD1", // Nod unit destroyed + "CIVDEAD1", // civilian killed +// "EVAYES1", // affirmative +// "EVANO1", // negative +// "UPUNIT1", // upgrade complete, new unit available +// "UPSTRUC1", // upgrade complete, new structure available + "NOCASH1", // insufficient funds + "BATLCON1", // battle control terminated + "REINFOR1", // reinforcements have arrived + "CANCEL1", // canceled + "BLDGING1", // building + "LOPOWER1", // low power + "NOPOWER1", // insufficient power + "MOCASH1", // need more funds + "BASEATK1", // our base is under attack + "INCOME1", // incoming missile + "ENEMYA", // enemy planes approaching + "NUKE1", // nuclear warhead approaching - VOX_INCOMING_NUKE +// "RADOK1", // radiation levels are acceptable +// "RADFATL1", // radiation levels are fatal + "NOBUILD1", // unable to build more + "PRIBLDG1", // primary building selected +// "REPDONE1", // repairs completed + "NODCAPT1", // Nod building captured + "GDICAPT1", // GDI building captured +// "SOLD1", // structure sold + "IONCHRG1", // ion cannon charging + "IONREDY1", // ion cannon ready + "NUKAVAIL", // nuclear weapon available + "NUKLNCH1", // nuclear weapon launched - VOX_NUKE_LAUNCHED + "UNITLOST", // unit lost + "STRCLOST", // structure lost + "NEEDHARV", // need harvester + "SELECT1", // select target + "AIRREDY1", // airstrike ready + "NOREDY1", // not ready + "TRANSSEE", // Nod transport sighted + "TRANLOAD", // Nod transport loaded + "ENMYAPP1", // enemy approaching + "SILOS1", // silos needed + "ONHOLD1", // on hold + "REPAIR1", // repairing + "ESTRUCX", // enemy structure destroyed + "GSTRUC1", // GDI structure destroyed + "NSTRUC1", // NOD structure destroyed + "ENMYUNIT", // Enemy unit destroyed +// "GUKILL1", // gold unit destroyed +// "GSTRUD1", // gold structure destroyed +// "GONLINE1", // gold player online +// "GLEFT1", // gold player has departed +// "GOLDKILT", // gold player destroyed +// "GOLDWIN", // gold player is victorious +// "RUKILL1", // red unit destroyed +// "RSTRUD1", // red structure destroyed +// "RONLINE1", // red player online +// "RLEFT1", // red player has departed +// "REDKILT", // red player destroyed +// "REDWIN", // red player is victorious +// "GYUKILL1", // grey unit destroyed +// "GYSTRUD1", // grey structure destroyed +// "GYONLINE", // grey player online +// "GYLEFT1", // grey player has departed +// "GREYKILT", // grey player destroyed +// "GREYWIN", // grey player is victorious +// "OUKILL1", // orange unit destroyed +// "OSTRUD1", // orange structure destroyed +// "OONLINE1", // orange player online +// "OLEFT1", // orange player has departed +// "ORANKILT", // orange player destroyed +// "ORANWIN", // orange player is victorious +// "GNUKILL1", // green unit destroyed +// "GNSTRUD1", // green structure destroyed +// "GNONLINE", // green player online +// "GNLEFT1", // green player has departed +// "GRENKILT", // green player destroyed +// "GRENWIN", // green player is victorious +// "BUKILL1", // blue unit destroyed +// "BSTRUD1", // blue structure destroyed +// "BONLINE1", // blue player online +// "BLEFT1", // blue player has departed +// "BLUEKILT", // blue player destroyed +// "BLUEWIN" // blue player is victorious +}; +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +void Speak(VoxType voice, HouseClass *house, COORDINATE coord) +{ + // MBL 02.22.2019 + if (voice == VOX_NONE) + { + return; + } + + // + // Intercept speech calls. ST - 2/20/2019 3:37PM + // + // On_Speech((int)voice); // MBL 02.06.2020 + On_Speech((int)voice, house); + if (coord) { + On_Ping(house, coord); + } + +#if (0) + if (Options.Volume && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + } +#endif +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Speak_AI(void) +{ +// MBL 06.17.2019 KO +#if 0 + static VoxType _last = VOX_NONE; + if (SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer)) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + if (SpeakQueue != _last) { + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + if (CCFileClass(name).Read(SpeechBuffer, SPEECH_BUFFER_SIZE)) { + Play_Sample(SpeechBuffer, 254, Options.Volume); + } + _last = SpeakQueue; + } else { + Play_Sample(SpeechBuffer, 254, Options.Volume); + } + SpeakQueue = VOX_NONE; + } + } +#endif +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + if (SampleType != 0) { + Stop_Sample_Playing(SpeechBuffer); + } +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + if (SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) { + return(true); + } + return(false); +} \ No newline at end of file diff --git a/TIBERIANDAWN/AUDIO.H b/TIBERIANDAWN/AUDIO.H new file mode 100644 index 000000000..6c2380a52 --- /dev/null +++ b/TIBERIANDAWN/AUDIO.H @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\audio.h_v 2.18 16 Oct 1995 16:45:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : June 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "memory.h" + +class AudioClass { + char const * Name; // Name of audio asset. + void const * Data; // Loaded audio data. + int Handle; // Handle of asset (as it is playing). + MemoryClass *Mem; // Pointer to memory handler class. + unsigned IsMIDI:1; // Is this a midi file? + + public: + AudioClass(void); + AudioClass(char const *name, MemoryClass &mem); + virtual ~AudioClass(void); + + bool Load(char const *name = 0); + bool Free(void); + bool Play(int volume = 0xFF); + bool Stop(void); + bool Pause(void); + bool Resume(void); + bool Set_Name(char const *name); + bool Is_Playing(void) const; + bool Is_Loaded(void) const; + bool Is_MIDI(void) const; +}; + +inline AudioClass::AudioClass(void) +{ + Name = 0; + Data = 0; + Mem = 0; + Handle = -1; +}; + +inline AudioClass::AudioClass(char const *name, MemoryClass &mem) +{ + if (mem) { + Mem = &mem; + } else { + Mem = &::Mem; // Uses global default memory handler. + } + Name = strdup(name); + Data = 0; + Handle = -1; +}; + +inline AudioClass::~AudioClass(void) +{ + if (GameActive) { + if (Name) free(Name); + if (Data) Mem->Free(Data); + Name = 0; + Data = 0; + Handle = -1; + } +}; + + +#endif diff --git a/TIBERIANDAWN/BASE.CPP b/TIBERIANDAWN/BASE.CPP new file mode 100644 index 000000000..40186ae3e --- /dev/null +++ b/TIBERIANDAWN/BASE.CPP @@ -0,0 +1,521 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\base.cpv 1.9 16 Oct 1995 16:48:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * BaseClass::Load -- loads from a saved game file * + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * BaseClass::Read_INI -- INI reading routine * + * BaseClass::Save -- saves to a saved game file * + * BaseClass::Write_INI -- INI writing routine * + * BaseNodeClass::operator != -- inequality operator * + * BaseNodeClass::operator == -- equality operator * + * BaseNodeClass::operator > -- greater-than operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BaseNodeClass::operator == -- equality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * true = equal, false = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator == (BaseNodeClass const & node) +{ + return(Type == node.Type && Coord == node.Coord); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator != -- inequality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator !=(BaseNodeClass const & node) +{ + return(Type != node.Type || Coord != node.Coord); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator > -- greater-than operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator > (BaseNodeClass const & ) +{ + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Read_INI -- INI reading routine * + * * + * INI entry format: * + * BLDG=COORD * + * BLDG=COORD * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +void BaseClass::Read_INI(char *buffer) +{ + char buf[128]; + char uname[10]; + BaseNodeClass node; // node to add to list + + /* + ** First, determine the house of the human player, and set the Base's house + ** accordingly. + */ + WWGetPrivateProfileString("BASIC", "Player", "GoodGuy", buf, 20, buffer); + if (HouseTypeClass::From_Name(buf) == HOUSE_GOOD) { + House = HOUSE_BAD; + } else { + House = HOUSE_GOOD; + } + + /* + ** Read the number of buildings that will go into the base node list + */ + int count = WWGetPrivateProfileInt (INI_Name(),"Count",0,buffer); + + /* + ** Read each entry in turn, in the same order they were written out. + */ + for (int i = 0; i < count; i++) { + + /* + ** Get an INI entry + */ + sprintf(uname,"%03d",i); + WWGetPrivateProfileString(INI_Name(), uname, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** Set the node's building type + */ + node.Type = BuildingTypeClass::From_Name(strtok(buf,",")); + + /* + ** Read & set the node's coordinate + */ + node.Coord = atol(strtok(NULL,",")); + + /* + ** Add this node to the Base's list + */ + Nodes.Add(node); + } +} + + +/*********************************************************************************************** + * BaseClass::Write_INI -- INI writing routine * + * * + * INI entry format: * + * BLDG=COORD * + * BLDG=COORD * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +void BaseClass::Write_INI(char *buffer) +{ + char buf[128]; + char uname[10]; + + /* + ** Clear out all existing teamtype data from the INI file. + */ + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + + /* + ** Save the # of buildings in the Nodes list. This is essential because + ** they must be read in the same order they were created, so "000" must be + ** read first, etc. + */ + WWWritePrivateProfileInt (INI_Name(),"Count",Nodes.Count(),buffer); + + /* + ** Write each entry into the INI + */ + for (int i = 0; i < Nodes.Count(); i++) { + sprintf(uname,"%03d",i); + sprintf(buf,"%s,%d", + BuildingTypeClass::As_Reference(Nodes[i].Type).IniName, + Nodes[i].Coord); + + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } +} + + +/*********************************************************************************************** + * BaseClass::Load -- loads from a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Load(FileClass &file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Read in & check the size of this class + */ + if (file.Read(&i, sizeof(i)) != sizeof(i)) { + return(false); + } + + if (i != sizeof(*this)) { + return(false); + } + + /* + ** Read in the House & the number of structures in the base + */ + if (file.Read(&House,sizeof(House)) != sizeof(House)) { + return(false); + } + + if (file.Read(&num_struct,sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Read each node entry & add it to the list + */ + for (i = 0; i < num_struct; i++) { + if (file.Read(&node,sizeof(node)) != sizeof(node)) { + return(false); + } + Nodes.Add(node); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Save -- saves to a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Save(FileClass &file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Write the size of this class + */ + i = sizeof(*this); + if (file.Write(&i,sizeof(i)) != sizeof(i)) { + return(false); + } + + /* + ** Write the House & the number of structures in the base + */ + if (file.Write(&House,sizeof(House)) != sizeof(House)) { + return(false); + } + + num_struct = Nodes.Count(); + if (file.Write(&num_struct,sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Write each node entry + */ + for (i = 0; i < num_struct; i++) { + node = Nodes[i]; + if (file.Write(&node,sizeof(node)) != sizeof(node)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * * + * INPUT: * + * index index into base list * + * * + * OUTPUT: * + * true = yes, false = no * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Built(int index) +{ + if (Get_Building(index) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to already-built building, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BuildingClass * BaseClass::Get_Building(int index) +{ + BuildingClass *bldg; + ObjectClass *obj[4]; + + /* + ** Check the location on the map where this building should be; if it's + ** there, return a pointer to it. + */ + CELL cell = Coord_Cell(Nodes[index].Coord); + + obj[0] = Map[cell].Cell_Building(); + obj[1] = Map[cell].Overlapper[0]; + obj[2] = Map[cell].Overlapper[1]; + obj[3] = Map[cell].Overlapper[2]; + + bldg = NULL; + for (int i = 0; i < 4; i++) { + if (obj[i] && + obj[i]->Coord == Nodes[index].Coord && + obj[i]->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) { + + bldg = (BuildingClass *)obj[i]; + break; + } + } + + return(bldg); +} + + +/*********************************************************************************************** + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * true = building is a node in the list, false = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Node(BuildingClass *obj) +{ + if (Get_Node(obj) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to node * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(BuildingClass *obj) +{ + for (int i = 0; i < Nodes.Count(); i++) { + if (obj->Class->Type == Nodes[i].Type && obj->Coord == Nodes[i].Coord) { + return(&Nodes[i]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * * + * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. * + * Otherwise, returns ptr to the next hole in the list of any type. * + * * + * INPUT: * + * type type of building to check for * + * * + * OUTPUT: * + * ptr to a BaseNodeClass, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Next_Buildable(StructType type) +{ + /* + ** Loop through all node entries, returning a pointer to the first + ** un-built one that matches the requested type. + */ + for (int i = 0; i < Nodes.Count(); i++) { + + /* + ** For STRUCT_NONE, return the first hole found + */ + if (type == STRUCT_NONE) { + if (!Is_Built(i)) { + return(&Nodes[i]); + } + + } else { + + /* + ** For a "real" building type, return the first hold for that type + */ + if (Nodes[i].Type==type && !Is_Built(i)) { + return(&Nodes[i]); + } + } + } + + +// If no entry could be found, then create a fake one that will allow +// placement of the building. Make it static and reuse the next time this +// routine is called. + + return(NULL); +} diff --git a/TIBERIANDAWN/BASE.H b/TIBERIANDAWN/BASE.H new file mode 100644 index 000000000..e6c6c2a4a --- /dev/null +++ b/TIBERIANDAWN/BASE.H @@ -0,0 +1,124 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\base.h_v 1.12 16 Oct 1995 16:46:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BASE_H +#define BASE_H + + +/**************************************************************************** +** This class defines one "node" in the pre-built base list. Each node +** contains a type of building to build, and the COORD to build it at. +*/ +class BaseNodeClass +{ + public: + BaseNodeClass(void) {}; + int operator == (BaseNodeClass const & node); + int operator != (BaseNodeClass const & node); + int operator > (BaseNodeClass const & node); + + StructType Type; + COORDINATE Coord; +}; + + +/**************************************************************************** +** This is the class that defines a pre-built base for the computer AI. +** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!) +*/ +class BaseClass +{ + public: + + /********************************************************************** + ** Constructor/Destructor + */ + BaseClass(void) {}; + virtual ~BaseClass() {Nodes.Clear();} + + /********************************************************************** + ** Initialization + */ + void Init(void) {Nodes.Clear();} + + /********************************************************************** + ** The standard suite of load/save support routines + */ + void Read_INI(char *buffer); + void Write_INI(char *buffer); + static char *INI_Name(void) {return "Base";} + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void) {}; + virtual void Decode_Pointers(void) {}; + + /********************************************************************** + ** Tells if the given node has been built or not + */ + bool Is_Built(int index); + + /********************************************************************** + ** Returns a pointer to the object for the given node + */ + BuildingClass * Get_Building(int index); + + /********************************************************************** + ** Tells if the given building ptr is a node in this base's list. + */ + bool Is_Node (BuildingClass *obj); + + /********************************************************************** + ** Returns a pointer to the requested node. + */ + BaseNodeClass * Get_Node(BuildingClass *obj); + BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); } + + /********************************************************************** + ** Returns a pointer to the next "hole" in the Nodes list. + */ + BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE); + + /********************************************************************** + ** This is the list of "nodes" that define the base. Portions of this + ** list can be pre-built by simply saving those buildings in the INI + ** along with non-base buildings, so Is_Built will return true for them. + */ + DynamicVectorClass Nodes; + + /********************************************************************** + ** This is the house this base belongs to. + */ + HousesType House; +}; + + +#endif diff --git a/TIBERIANDAWN/BBDATA.CPP b/TIBERIANDAWN/BBDATA.CPP new file mode 100644 index 000000000..3f72a4790 --- /dev/null +++ b/TIBERIANDAWN/BBDATA.CPP @@ -0,0 +1,636 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\bbdata.cpv 2.17 16 Oct 1995 16:49:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BBDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. * + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/*************************************************************************** +** Detailed information about each class of bullet (projectile) in the game. +*/ +static BulletTypeClass const ClassSniper( + BULLET_BULLET, + "50cal", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HOLLOW_POINT, // WARHEAD: If fires weapon, warhead type + ANIM_PIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassBullet( + BULLET_BULLET, + "50cal", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_SA, // WARHEAD: If fires weapon, warhead type + ANIM_PIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassSpreadfire( + BULLET_SPREADFIRE, + "50cal", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_PIFFPIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassAPDS( + BULLET_APDS, + "120mm", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT3 // Explosion to use upon impact. +); + +static BulletTypeClass const Class120mm( + BULLET_HE, + "120mm", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + true, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_ART_EXP1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassMissile( + BULLET_SSM, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 7, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassMissile2( + BULLET_SSM2, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 9, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 7, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassPatriot( + BULLET_SAM, + "MISSILE", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + true, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassDragon( + BULLET_TOW, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 3, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassFlame( + BULLET_FLAME, + "FLAME", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 12, // ARMING: Time to arm projectile after launch. + 12, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassChem( + BULLET_CHEMSPRAY, + "FLAME", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 12, // ARMING: Time to arm projectile after launch. + 12, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNapalm( + BULLET_NAPALM, + "BOMBLET", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + true, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + false, // Good against aircraft? + 24, // ARMING: Time to arm projectile after launch. + 24, // RANGE: Inherent override range factor. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NAPALM2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassGrenade( + BULLET_GRENADE, + "BOMB", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + true, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassLaser( + BULLET_LASER, + "Laser", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_LASER, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNukeUp( + BULLET_NUKE_UP, + "ATOMICUP", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNukeDown( + BULLET_NUKE_DOWN, + "ATOMICDN", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_ATOM_BLAST // Explosion to use upon impact. +); + +static BulletTypeClass const ClassHonestJohn( + BULLET_HONEST_JOHN, + "MISSILE", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 10, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NAPALM3 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassHeadButt( + BULLET_HEADBUTT, + "GORE", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HEADBUTT, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassTRexBite( + BULLET_TREXBITE, + "CHEW", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FEEDME, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + + +#ifdef PETROGLYPH_EXAMPLE_MOD + +static BulletTypeClass const NukeLob( + BULLET_NUKE_LOB, + "BOMB", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + true, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_ATOM_BLAST // Explosion to use upon impact. +); + +#endif //PETROGLYPH_EXAMPLE_MOD + + +/* +** This is the array of pointers to the static data associated with +** each bullet (projectile) type. +*/ +BulletTypeClass const * const BulletTypeClass::Pointers[BULLET_COUNT] = { + &ClassSniper, // BULLET_SNIPER + &ClassBullet, // BULLET_BULLET + &ClassAPDS, // BULLET_APDS + &Class120mm, // BULLET_HE + &ClassMissile, // BULLET_SSM + &ClassMissile2, // BULLET_SSM2 + &ClassPatriot, // BULLET_SAM + &ClassDragon, // BULLET_TOW + &ClassFlame, // BULLET_FLAME + &ClassChem, // BULLET_CHEMSPRAY + &ClassNapalm, // BULLET_NAPALM + &ClassGrenade, // BULLET_GRENADE + &ClassLaser, // BULLET_LASER + &ClassNukeUp, // BULLET_NUKE_UP + &ClassNukeDown, // BULLET_NUKE_DOWN + &ClassHonestJohn, // BULLET_HONEST_JOHN + &ClassSpreadfire, // BULLET_SPREADFIRE + &ClassHeadButt, // BULLET_HEADBUTT + &ClassTRexBite, // BULLET_TREXBITE +#ifdef PETROGLYPH_EXAMPLE_MOD + &NukeLob, // BULLET_NUKE_LOB +#endif //PETROGLYPH_EXAMPLE_MOD + +}; + + +/*********************************************************************************************** + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * * + * This is basically a constructor for static type objects used by bullets. All bullets * + * are of a type constructed by this routine at game initialization time. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +BulletTypeClass::BulletTypeClass( + BulletType type, + char const *ininame, + bool is_high, + bool is_homing, + bool is_arcing, + bool is_dropping, + bool is_invisible, + bool is_proximity_armed, + bool is_flame_equipped, + bool is_fueled, + bool is_faceless, + bool is_inaccurate, + bool is_translucent, + bool is_antiaircraft, + int arming, int range, MPHType maxspeed, unsigned rot, + WarheadType warhead, AnimType explosion) : + ObjectTypeClass(true, false, false, true, false, false, true, true, TXT_NONE, ininame, ARMOR_NONE, 0) +{ + Explosion = explosion; + IsHigh = is_high; + IsAntiAircraft = is_antiaircraft; + IsTranslucent = is_translucent; + IsArcing = is_arcing; + IsHoming = is_homing; + IsDropping = is_dropping; + IsInvisible = is_invisible; + IsProximityArmed = is_proximity_armed; + IsFlameEquipped = is_flame_equipped; + IsFueled = is_fueled; + IsFaceless = is_faceless; + IsInaccurate = is_inaccurate; + Type = type; + Warhead = warhead; + MaxSpeed = maxspeed; + ROT = rot; + Arming = arming; + Range = range; +} + + +/*********************************************************************************************** + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * * + * This routine is used to perform any one time processing for the bullet type class. It * + * handles loading of the shape files. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called before any rendering of bullets occurs and should * + * only be called once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::One_Time(void) +{ + BulletType index; + + /* + ** Load the bullet shapes. + */ + for (index = BULLET_FIRST; index < BULLET_COUNT; index++) { + BulletTypeClass const & bullet = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + if (!bullet.IsInvisible) { + _makepath(fullname, NULL, NULL, bullet.IniName, ".SHP"); + + RawFileClass file(fullname); + + if (file.Is_Available()) { + ((void const *&)bullet.ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)bullet.ImageData) = MixFileClass::Retrieve(fullname); + } + } + } +} + diff --git a/TIBERIANDAWN/BDATA.CPP b/TIBERIANDAWN/BDATA.CPP new file mode 100644 index 000000000..635e50663 --- /dev/null +++ b/TIBERIANDAWN/BDATA.CPP @@ -0,0 +1,4938 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\bdata.cpv 2.17 16 Oct 1995 16:50:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 17, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * BuildingTypeClass::Display -- Renders a generic view of building. * + * BuildingTypeClass::Full_Name -- Fetches the full name text number. * + * BuildingTypeClass::Height -- Determins the height of the building in icons. * + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * BuildingTypeClass::Legal_Placement -- Determines if building can be legally placed at pos.* + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. * + * BuildingTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * BuildingTypeClass::Repair_Step -- Determines the repair step rate. * + * BuildingTypeClass::Who_Can_Build_Me -- Determines which factory can create the building. * + * BuildingTypeClass::Width -- Determines width of bulding in icons. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W + +#define XYCELL(x,y) (y*MAP_CELL_W+x) +static short const ExitPyle[] = { + XYCELL(1,2), + XYCELL(2,2), + XYCELL(0,2), + XYCELL(-1,2), + XYCELL(-1,-1), + XYCELL(0,-1), + XYCELL(1,-1), + XYCELL(2,-1), + XYCELL(2,-1), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; +static short const ExitHand[] = { + XYCELL(2,3), + XYCELL(1,3), + XYCELL(0,3), + XYCELL(2,2), + XYCELL(-1,3), + XYCELL(-1,2), + XYCELL(0,0), + XYCELL(1,0), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; +static short const ExitWeap[] = { + XYCELL(-1,3), + XYCELL(0,3), + XYCELL(-1,2), + XYCELL(1,3), +// XYCELL(0,0), +// XYCELL(1,0), +// XYCELL(2,0), +// XYCELL(-1,0), +// XYCELL(3,0), + XYCELL(-1,1), + XYCELL(3,1), + XYCELL(3,2), + XYCELL(3,3), + XYCELL(2,3), + REFRESH_EOL +}; +static short const ExitAirstrip[] = { + XYCELL(-1,-1), + XYCELL(-1,0), + XYCELL(-1,1), + XYCELL(-1,2), + XYCELL(0,-1), + XYCELL(0,2), + XYCELL(1,-1), + XYCELL(1,2), + XYCELL(2,-1), + XYCELL(2,2), + XYCELL(3,-1), + XYCELL(3,2), + XYCELL(4,-1), + XYCELL(4,0), + XYCELL(4,1), + XYCELL(4,2), + REFRESH_EOL +}; + +static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL}; +static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL}; +static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL}; +static short const List22_1100[] = {0, 1, REFRESH_EOL}; +static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const StoreList[] = {0, 1, REFRESH_EOL}; +static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL}; +static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const OListWestwood[] = {0, MCW, REFRESH_EOL}; +static short const ComList[] = {0, MCW, MCW+1, REFRESH_EOL}; +static short const List21[] = {0, 1, REFRESH_EOL}; +static short const ListWeap[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List12[] = {MCW, REFRESH_EOL}; +static short const ListHand[] = {MCW, MCW+1, MCW*2+1, REFRESH_EOL}; +static short const ListTmpl[] = {MCW, MCW+1, MCW+2, MCW*2, MCW*2+1, MCW*2+2, REFRESH_EOL}; +static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL}; +static short const List11[] = {0, 1, REFRESH_EOL}; +static short const List1[] = {0, REFRESH_EOL}; +static short const List1100[] = {0, 1, REFRESH_EOL}; +static short const List0010[] = {MCW, REFRESH_EOL}; +static short const List1000[] = {0, REFRESH_EOL}; +static short const List0100[] = {1, REFRESH_EOL}; +static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +//static short const List1111[] = {0, 1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1011[] = {0, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List010111000[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL}; +static short const List101000111[] = {0, 2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; + +static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +static short const OListWeap[] = {0, 1, 2, REFRESH_EOL}; +static short const OComList[] = {1, REFRESH_EOL}; +static short const OList12[] = {0, REFRESH_EOL}; +static short const OListHand[] = {0, 1, MCW*2, MCW*1, REFRESH_EOL}; +static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL}; + + +/*************************************************************************** +*/ +static BuildingTypeClass const ClassTemple( + STRUCT_TEMPLE, + TXT_TEMPLE, // NAME: Short name of the structure. + "TMPL", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_RADAR, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1000, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 3000, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points generated. + 150, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListTmpl, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassEye( + STRUCT_EYE, + TXT_EYE, // NAME: Short name of the structure. + "EYE", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_RADAR, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)160, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 10, // SIGHTRANGE: Range of sighting. + 2800, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,100, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 200, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassWeapon( + STRUCT_WEAP, + TXT_WEAPON_FACTORY, // NAME: Short name of the structure. + "WEAP", // NAME: Short name of the structure. + XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. +#ifdef ADVANCED + 500, // STRNTH: Full strength of building. +#else + 200, // STRNTH: Full strength of building. +#endif + 3, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 0,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassGTower( + STRUCT_GTOWER, + TXT_GUARD_TOWER, // NAME: Short name of the structure. + "GTWR", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 7, // SCENARIO: Starting availability scenario. + 100,25, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_CHAIN_GUN,WEAPON_NONE, +// WEAPON_M60MG,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 00, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassATower( + STRUCT_ATOWER, + TXT_AGUARD_TOWER, // NAME: Short name of the structure. + "ATWR", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 100,30, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_TOW_TWO,WEAPON_NONE, +// WEAPON_TOMAHAWK,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassObelisk( + STRUCT_OBELISK, + TXT_OBELISK, // NAME: Short name of the structure. + "OBLI", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to purchase. + 11, // SCENARIO: Starting availability scenario. + 100,35, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_OBELISK_LASER,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 150, // DRAIN: Power points required. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTurret( + STRUCT_TURRET, + TXT_TURRET, // NAME: Short name of the structure. + "GUN", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)208, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. +#ifdef ADVANCED + 600, // COST: Cost to purchase. +#else +#ifdef PATCH + 600, +#else + 250, // COST: Cost to purchase. +#endif +#endif + 8, // SCENARIO: Starting availability scenario. + 300,26, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_TURRET_GUN,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassConst( + STRUCT_CONST, + TXT_CONST_YARD, // NAME: Short name of the structure. + "FACT", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_BUILDINGTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 5000, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,70, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 30, // POWER: Power points required. + 15, // DRAIN: Power points required. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRefinery( + STRUCT_REFINERY, + TXT_REFINERY, // NAME: Short name of the structure. + "PROC", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 1, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 450, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,55, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 1000, // CAPACITY: Spice storage capacity. + 10, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List010111000, // OCCUPYLIST: List of active foundation squares. + (short const *)List101000111 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassStorage( + STRUCT_STORAGE, + TXT_STORAGE, // NAME: Short name of the structure. + "SILO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 1, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 150, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 150, // COST: Cost to purchase. +// 300, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,16, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 1500, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)StoreList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHelipad( + STRUCT_HELIPAD, + TXT_HELIPAD, // NAME: Short name of the structure. + "HPAD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 6, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to purchase. + 10, // SCENARIO: Starting availability scenario. + 0,65, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCommand( + STRUCT_RADAR, + TXT_COMMAND, // NAME: Short name of the structure. + "HQ", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)160, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 10, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 3, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSAM( + STRUCT_SAM, + TXT_SAM, // NAME: Short name of the structure. + "SAM", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 6, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 750, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 300,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NIKE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAirStrip( + STRUCT_AIRSTRIP, + TXT_AIRSTRIP, // NAME: Short name of the structure. + "AFLD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 300,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_42, // SIZE: Building size. + ExitAirstrip, // Preferred exit cell list. + (short const *)List42, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPower( + STRUCT_POWER, + TXT_POWER, // NAME: Short name of the structure. + "NUKE", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 0, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 100, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1011, // OCCUPYLIST: List of active foundation squares. + (short const *)List0100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedPower( + STRUCT_ADVANCED_POWER, + TXT_ADVANCED_POWER, // NAME: Short name of the structure. + "NUK2", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 700, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,75, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 200, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1011, // OCCUPYLIST: List of active foundation squares. + (short const *)List0100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHospital( + STRUCT_HOSPITAL, + TXT_HOSPITAL, // NAME: Short name of the structure. + "HOSP", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_BARRACKS, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 100, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL, // OVERLAPLIST:List of overlap cell offset. + true // Is this building un-sellable? +); + +static BuildingTypeClass const ClassBioLab( + STRUCT_BIO_LAB, + TXT_BIO_LAB, // NAME: Short name of the structure. + "BIO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_HOSPITAL, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,1, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 100, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarracks( + STRUCT_BARRACKS, + TXT_BARRACKS, // NAME: Short name of the structure. + "PYLE", // NAME: Short name of the structure. + XYP_COORD(30,33), // Exit point for produced units. + 0, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,60, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22_1100, // OCCUPYLIST: List of active foundation squares. + (short const *)List22_0011 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHand( + STRUCT_HAND, + TXT_HAND, // NAME: Short name of the structure. + "HAND", // NAME: Short name of the structure. + XYP_COORD(36,63), // Exit point for produced units. + 0, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,61, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_23, // SIZE: Building size. + (short const *)ExitHand, // Preferred exit cell list. + (short const *)ListHand, // OCCUPYLIST: List of active foundation squares. + (short const *)OListHand // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTanker( + STRUCT_TANKER, + TXT_TANKER, // NAME: Short name of the structure. + "ARCO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 100, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,1, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRepair( + STRUCT_REPAIR, + TXT_FIX_IT, // NAME: Short name of the structure. + "FIX", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 1200, // COST: Cost to purchase. + 8, // SCENARIO: Starting availability scenario. + 0,46, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFix, // OCCUPYLIST: List of active foundation squares. + (short const *)OListFix // OVERLAPLIST:List of overlap cell offset. +); + +#ifdef OBSOLETE +static BuildingTypeClass const ClassRoad( + STRUCT_ROAD, + TXT_ROAD, // NAME: Short name of the structure. + "ROAD", // NAME: Short name of the structure. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + 0, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 50, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_NONE, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +#endif + +static BuildingTypeClass const ClassV01( + STRUCT_V01, + TXT_CIV1, // NAME: Short name of the structure. + "V01", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV02( + STRUCT_V02, + TXT_CIV2, // NAME: Short name of the structure. + "V02", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV03( + STRUCT_V03, + TXT_CIV3, // NAME: Short name of the structure. + "V03", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV04( + STRUCT_V04, + TXT_CIV4, // NAME: Short name of the structure. + "V04", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV05( + STRUCT_V05, + TXT_CIV5, // NAME: Short name of the structure. + "V05", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV06( + STRUCT_V06, + TXT_CIV6, // NAME: Short name of the structure. + "V06", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV07( + STRUCT_V07, + TXT_CIV7, // NAME: Short name of the structure. + "V07", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV08( + STRUCT_V08, + TXT_CIV8, // NAME: Short name of the structure. + "V08", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV09( + STRUCT_V09, + TXT_CIV9, // NAME: Short name of the structure. + "V09", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV10( + STRUCT_V10, + TXT_CIV10, // NAME: Short name of the structure. + "V10", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV11( + STRUCT_V11, + TXT_CIV11, // NAME: Short name of the structure. + "V11", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV12( + STRUCT_V12, + TXT_CIV12, // NAME: Short name of the structure. + "V12", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV13( + STRUCT_V13, + TXT_CIV13, // NAME: Short name of the structure. + "V13", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV14( + STRUCT_V14, + TXT_CIV14, // NAME: Short name of the structure. + "V14", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV15( + STRUCT_V15, + TXT_CIV15, // NAME: Short name of the structure. + "V15", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV16( + STRUCT_V16, + TXT_CIV16, // NAME: Short name of the structure. + "V16", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV17( + STRUCT_V17, + TXT_CIV17, // NAME: Short name of the structure. + "V17", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV18( + STRUCT_V18, + TXT_CIV18, // NAME: Short name of the structure. + "V18", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV19( + STRUCT_PUMP, + TXT_PUMP, // NAME: Short name of the structure. + "V19", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV20( + STRUCT_V20, + TXT_CIV20, // NAME: Short name of the structure. + "V20", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV21( + STRUCT_V21, + TXT_CIV21, // NAME: Short name of the structure. + "V21", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1101, // OCCUPYLIST: List of active foundation squares. + (short const *)List0010 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV22( + STRUCT_V22, + TXT_CIV22, // NAME: Short name of the structure. + "V22", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV23( + STRUCT_V23, + TXT_CIV23, // NAME: Short name of the structure. + "V23", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV24( + STRUCT_V24, + TXT_CIV24, // NAME: Short name of the structure. + "V24", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV25( + STRUCT_V25, + TXT_CIV25, // NAME: Short name of the structure. + "V25", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV26( + STRUCT_V26, + TXT_CIV26, // NAME: Short name of the structure. + "V26", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV27( + STRUCT_V27, + TXT_CIV27, // NAME: Short name of the structure. + "V27", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV28( + STRUCT_V28, + TXT_CIV28, // NAME: Short name of the structure. + "V28", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV29( + STRUCT_V29, + TXT_CIV29, // NAME: Short name of the structure. + "V29", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV30( + STRUCT_V30, + TXT_CIV30, // NAME: Short name of the structure. + "V30", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV31( + STRUCT_V31, + TXT_CIV31, // NAME: Short name of the structure. + "V31", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV32( + STRUCT_V32, + TXT_CIV32, // NAME: Short name of the structure. + "V32", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV33( + STRUCT_V33, + TXT_CIV33, // NAME: Short name of the structure. + "V33", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV34( + STRUCT_V34, + TXT_CIV34, // NAME: Short name of the structure. + "V34", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV35( + STRUCT_V35, + TXT_CIV35, // NAME: Short name of the structure. + "V35", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV36( + STRUCT_V36, + TXT_CIV36, // NAME: Short name of the structure. + "V36", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassV37( + STRUCT_V37, + TXT_CIV37, // NAME: Short name of the structure. + "V37", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_42, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassMission( + STRUCT_MISSION, + TXT_CIVMISS, // NAME: Short name of the structure. + "MISS", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + true, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +// Sandbag wall +static BuildingTypeClass const Sandbag( + STRUCT_SANDBAG_WALL, + TXT_SANDBAG_WALL, // NAME: Short name of the structure. + "SBAG", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 50, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Cyclone fence +static BuildingTypeClass const Cyclone( + STRUCT_CYCLONE_WALL, + TXT_CYCLONE_WALL, // NAME: Short name of the structure. + "CYCL", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 75, // COST: Cost to purchase. + 9, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Brick wall +static BuildingTypeClass const Brick( + STRUCT_BRICK_WALL, + TXT_BRICK_WALL, // NAME: Short name of the structure. + "BRIK", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 100, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Barbwire wall +static BuildingTypeClass const Barbwire( + STRUCT_BARBWIRE_WALL, + TXT_BARBWIRE_WALL, // NAME: Short name of the structure. + "BARB", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 98, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 25, // COST: Cost to purchase. + 98, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_BAD| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Wood wall +static BuildingTypeClass const Wood( + STRUCT_WOOD_WALL, + TXT_WOOD_WALL, // NAME: Short name of the structure. + "WOOD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 25, // COST: Cost to purchase. + 98, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + + +BuildingTypeClass const * const BuildingTypeClass::Pointers[STRUCT_COUNT] = { + &ClassWeapon, // STRUCT_WEAP + &ClassGTower, // STRUCT_GTOWER + &ClassATower, // STRUCT_ATOWER + &ClassObelisk, // STRUCT_OBLISK + &ClassCommand, // STRUCT_RADAR + &ClassTurret, // STRUCT_TURRET + &ClassConst, // STRUCT_CONST + &ClassRefinery, // STRUCT_REFINERY + &ClassStorage, // STRUCT_STORAGE + &ClassHelipad, // STRUCT_HELIPAD + &ClassSAM, // STRUCT_SAM + &ClassAirStrip, // STRUCT_AIRSTRIP + &ClassPower, // STRUCT_POWER + &ClassAdvancedPower, // STRUCT_POWER + &ClassHospital, // STRUCT_HOSPITAL + &ClassBarracks, // STRUCT_BARRACKS + &ClassTanker, // STRUCT_TANKER + &ClassRepair, // STRUCT_REPAIR + &ClassBioLab, // STRUCT_BIO_LAB + &ClassHand, // STRUCT_HAND + &ClassTemple, // STRUCT_TEMPLE + &ClassEye, // STRUCT_EYE + &ClassMission, // STRUCT_MISSION + + &ClassV01, // STRUCT_V1 + &ClassV02, // STRUCT_V2 + &ClassV03, // STRUCT_V3 + &ClassV04, // STRUCT_V4 + &ClassV05, // STRUCT_V5 + &ClassV06, // STRUCT_V6 + &ClassV07, // STRUCT_V7 + &ClassV08, // STRUCT_V8 + &ClassV09, // STRUCT_V9 + &ClassV10, // STRUCT_V10 + &ClassV11, // STRUCT_V11 + &ClassV12, // STRUCT_V12 + &ClassV13, // STRUCT_V13 + &ClassV14, // STRUCT_V14 + &ClassV15, // STRUCT_V15 + &ClassV16, // STRUCT_V16 + &ClassV17, // STRUCT_V17 + &ClassV18, // STRUCT_V18 + &ClassV19, // STRUCT_PUMP + &ClassV20, // STRUCT_V20 + &ClassV21, // STRUCT_V21 + &ClassV22, // STRUCT_V22 + &ClassV23, // STRUCT_V23 + &ClassV24, // STRUCT_V24 + &ClassV25, // STRUCT_V25 + &ClassV26, // STRUCT_V26 + &ClassV27, // STRUCT_V27 + &ClassV28, // STRUCT_V28 + &ClassV29, // STRUCT_V29 + &ClassV30, // STRUCT_V30 + &ClassV31, // STRUCT_V31 + &ClassV32, // STRUCT_V32 + &ClassV33, // STRUCT_V33 + &ClassV34, // STRUCT_V34 + &ClassV35, // STRUCT_V35 + &ClassV36, // STRUCT_V36 + &ClassV37, // STRUCT_V37 +#ifdef OBSOLETE + &ClassRoad, // STRUCT_ROAD +#endif + &Sandbag, // STRUCT_SANDBAG_WALL + &Cyclone, // STRUCT_CYCLONE_WALL + &Brick, // STRUCT_BRICK_WALL + &Barbwire, // STRUCT_BARBWIRE_WALL + &Wood, // STRUCT_WOOD_WALL +}; + +void const *WarFactoryOverlay; + + +/*********************************************************************************************** + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * * + * This is the constructor used to create the building types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass::BuildingTypeClass( + StructType type, + int name, + char const *ininame, + COORDINATE exitpoint, + unsigned char level, + long pre, + bool is_scanner, + bool is_regulated, + bool is_bibbed, + bool is_nominal, + bool is_wall, + bool is_factory, + bool is_captureable, + bool is_flammable, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_sturdy, + RTTIType tobuild, + DirType sframe, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, WeaponType secondary, + ArmorType armor, + unsigned long canenter, + unsigned capacity, + int power, + int drain, + BSizeType size, + short const *exitlist, + short const *sizelist, + short const *overlap, + bool is_unsellable) : + TechnoTypeClass(name, + ininame, + level, + pre, + false, + is_scanner, + is_nominal, + false, + is_flammable, + false, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_theater, + is_twoshooter, + is_turret_equipped, + is_repairable, + is_buildable, + is_crew, + -1, + strength*2, + MPH_IMMOBILE, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary, + secondary, + armor) +{ + CanEnter = canenter; + Capacity = capacity; + Drain = drain; + ExitList = exitlist; + ExitPoint = exitpoint; + IsBibbed = is_bibbed; + IsCaptureable = is_captureable; + IsFactory = is_factory; + IsRegulated = is_regulated; + IsUnsellable = is_unsellable; + IsSimpleDamage = is_simpledamage; + IsSturdy = is_sturdy; + IsWall = is_wall; + OccupyList = sizelist; + OverlapList = overlap; + Power = power; + Size = size; + StartFace = sframe; + ToBuild = tobuild; + Type = type; + + Anims[BSTATE_CONSTRUCTION].Start = 0; + Anims[BSTATE_CONSTRUCTION].Count = 1; + Anims[BSTATE_CONSTRUCTION].Rate = 0; + + Anims[BSTATE_IDLE].Start = 0; + Anims[BSTATE_IDLE].Count = 1; + Anims[BSTATE_IDLE].Rate = 0; + + Anims[BSTATE_ACTIVE].Start = 0; + Anims[BSTATE_ACTIVE].Count = 1; + Anims[BSTATE_ACTIVE].Rate = 0; + + Anims[BSTATE_AUX1].Start = 0; + Anims[BSTATE_AUX1].Count = 1; + Anims[BSTATE_AUX1].Rate = 0; + + Anims[BSTATE_AUX2].Start = 0; + Anims[BSTATE_AUX2].Count = 1; + Anims[BSTATE_AUX2].Rate = 0; +} + + +/*********************************************************************************************** + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * * + * This routine is used to do the one time action necessary to handle building type class * + * objects. This entails loading of the building shapes and the brain file used by * + * buildings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should only be called ONCE. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/11/1994 JLB : Updated construction time and frame count logic. * + *=============================================================================================*/ +void BuildingTypeClass::One_Time(void) +{ + static const struct { + StructType Class; // Building class number. + BStateType Stage; // Animation sequence to assign animation range to. + int Start; // Starting frame number. + int Length; // Number of frames (-1 means use all frames). + int Rate; // Rate of animation. + } _anims[] = { + {STRUCT_OBELISK, BSTATE_ACTIVE, 0, 4, OBELISK_ANIMATION_RATE}, + {STRUCT_ADVANCED_POWER, BSTATE_IDLE, 0, 4, 15}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 16,3}, + {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3}, + {STRUCT_CONST, BSTATE_ACTIVE, 4, 20,3}, + {STRUCT_CONST, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_EYE, BSTATE_IDLE, 0, 16,4}, + {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4}, + {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_POWER, BSTATE_IDLE, 0, 4, 15}, + {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4}, + {STRUCT_RADAR, BSTATE_IDLE, 0, 16,4}, + {STRUCT_REFINERY, BSTATE_ACTIVE, 12,7, 4}, // Docking phase. + {STRUCT_REFINERY, BSTATE_AUX1, 19,5, 4}, // Siphoning phase. + {STRUCT_REFINERY, BSTATE_AUX2, 24,6, 4}, // Undocking phase. + {STRUCT_REFINERY, BSTATE_IDLE, 0, 6, 4}, // Idle phase. + {STRUCT_REFINERY, BSTATE_FULL, 6, 6, 4}, // Flashing lights + {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2}, + {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_TEMPLE, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_V20, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V21, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V22, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V23, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_TEMPLE, BSTATE_ACTIVE, 0, 5, 1}, + }; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + BuildingTypeClass const & building = As_Reference(sindex); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (building.IsBuildable) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", building.IniName); + } else { + sprintf(buffer, "%sICON", building.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)building.CameoData) = MixFileClass::Retrieve(fullname); + } + + /* + ** Fetch the construction animation for this building. + */ + sprintf(buffer, "%sMAKE", building.IniName); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + void const * dataptr = MixFileClass::Retrieve(fullname); + ((void const *&)building.BuildupData) = dataptr; + if (dataptr) { + int timedelay = 1; + int count = Get_Build_Frame_Count(dataptr); + if (count) { + timedelay = (5 * TICKS_PER_SECOND) / count; + } + building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + + /* + ** Fetch the normal game shape for this building. + */ + _makepath(fullname, NULL, NULL, building.IniName, ".SHP"); + ((void const *&)building.ImageData) = MixFileClass::Retrieve(fullname); + } + + // Try to load weap2.shp + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP"); + WarFactoryOverlay = MixFileClass::Retrieve(fullname); + + /* + ** Install all the special animation sequences for the different building types. + */ + for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) { + BuildingTypeClass const *b = &As_Reference(_anims[index].Class); + if (b) { + b->Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate); + } + } +} + + + + +/*********************************************************************************************** + * Struct_From_Name -- Find BData structure from its name. * + * * + * This routine will convert an ASCII name for a building class into * + * the actual building class it represents. * + * * + * INPUT: name -- ASCII representation of a building class. * + * * + * OUTPUT: Returns with the actual building class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +StructType BuildingTypeClass::From_Name(char const *name) +{ + if (name) { + for (StructType classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(STRUCT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * BuildingTypeClass::Display -- Renders a generic view of building. * + * * + * This routine is used to display a generic representation of the * + * building. Typical use of this occurs with the scenario editor. * + * * + * INPUT: x,y -- Coordinate to display the building (centered). * + * * + * window -- The window the building should be rendered * + * relative to. * + * * + * house -- The house color to use for the building. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + } + CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, HouseTypeClass::As_Reference(house).RemapTable); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a * + * * + * This routine is used to prepare the scenario editor for the addition * + * of a building object to the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface routines. * + *=============================================================================================*/ +void BuildingTypeClass::Prep_For_Add(void) +{ + for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * * + * This routine is used by the scenario editor to create and place buildings on the map. * + * * + * INPUT: cell -- The cell that the building is to be placed upon. * + * * + * house -- The owner of the building. * + * * + * OUTPUT: bool; Was the building successfully created and placed on the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + BuildingClass * ptr; + + ptr = new BuildingClass(Type, house); + if (ptr) { + return(ptr->Unlimbo(Cell_Coord(cell), DIR_N)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * * + * This routine will create a building object of this type. The building object is in a * + * limbo state. It is presumed that the building object will be unlimboed at the correct * + * place and time. Typical use is when the building is created in a factory situation * + * and will be placed on the map when construction completes. * + * * + * INPUT: house -- Pointer to the house that is to be the owner of the building. * + * * + * OUTPUT: Returns with a pointer to the building. If the building could not be created * + * then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const +{ + HousesType htype = HOUSE_NEUTRAL; + if (house) { + htype = house->Class->House; + } + return(new BuildingClass(Type, htype)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * * + * This routine will initialize one animation control element for a * + * specified building. This modifies a "const" class and thus must * + * perform some strategic casting to get away with this. * + * * + * INPUT: state -- The animation state to apply these data values to. * + * * + * start -- Starting frame for the building's animation. * + * * + * count -- The number of frames in this animation. * + * * + * rate -- The countdown timer between animation frames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/18/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const +{ + ((int &)Anims[state].Start) = start; + ((int &)Anims[state].Count) = count; + ((int &)Anims[state].Rate) = rate; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Legal_Placement -- Determines if building can be legally placed at pos. * + * * + * This routine is used to determine if a building can be legally * + * placed at the specified position. Buildings can only be placed on * + * unoccupied rock terrain. * + * * + * INPUT: pos -- Position that the building would be placed (up-left) * + * * + * OUTPUT: 0=illegal, 1=on concrete, -1..-8=part on concrete. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/15/1991 JLB : Created. * + * 04/26/1992 JLB : Concrete and scenario init adjustment. * + * 05/06/1992 JLB : Good, Bad, and Adjacent checking added. * + * 08/09/1992 JLB : Determines full or partial concrete foundation. * + * 06/07/1994 JLB : Handles concrete special check. * + * 06/21/1994 JLB : Converted to building type class member function. * + *=============================================================================================*/ +int BuildingTypeClass::Legal_Placement(CELL pos) const +{ + short const *offset; // Pointer to cell offset list. + + if (pos == -1) return(false); + +#ifdef NEVER + /* + ** Concrete has special checking performed to determine legal placement. Concrete + ** can legally be placed if there is any cell that would be affected by the concrete + ** placement. Unlike other buildings, only one cell needs to be effective in order + ** to flag legal placement for the entire "structure". + */ + if (Type == STRUCT_CONCRETE_NOD || Type == STRUCT_CONCRETE_GDI) { + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (!Map.Cell_Template(pos + (CELL)*offset++)) { + return(true); + } + } + + /* + ** No squares would be affected by concrete placement so consider legal + ** placement query to be false. + */ + return(false); + } +#endif + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + offset = Occupy_List(true); + while (*offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + if (!Map[cell].Is_Generally_Clear()) { + return(false); + } + } + return(true); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Who_Can_Build_Me -- Determines which factory can create the building. * + * * + * Use this routine to determine which building is available to build the building. If * + * there are no suitable buildings, then NULL is returned. Typical use of this function is * + * to maintain the construction list sidebar. * + * * + * INPUT: intheory -- If true, then it merely checks for a building of the proper ownership * + * when determining if construction is allowed. It doesn't consider the * + * possibility that the construction building is currently busy or not. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The owner of the building to be built. Only construction buildings of * + * the same ownership are allowed to build. * + * * + * OUTPUT: Returns with a pointer to the construction object (building) that can build * + * the building type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 01/02/1995 JLB : Scans in reverse order so that later buildings are biased. * + *=============================================================================================*/ +BuildingClass * BuildingTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + for (int index = Buildings.Count()-1; index >= 0; index--) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_BUILDINGTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + return(building); + } + } + return(0); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * * + * This routine is used to perform any initialization that is custom per theater. * + * Typically, this is fetching the building shape data for those building types that have * + * theater specific art. * + * * + * INPUT: theater -- The theater to base this initialization on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + char fullname[_MAX_FNAME+_MAX_EXT]; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const *classptr = &As_Reference(sindex); + + if (classptr->IsTheater) { + _makepath(fullname, NULL, NULL, classptr->IniName, Theaters[theater].Suffix); + ((void const *&)classptr->ImageData) = MixFileClass::Retrieve(fullname); + } + + if ( Get_Resolution_Factor() ) { + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + ((void const *&)classptr->CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", classptr->IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)classptr->CameoData) = cameo_ptr; + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * * + * This routine will fetch the dimensions of the building (in pixels). These dimensions are * + * used to render the selection rectangle and the health bar. * + * * + * INPUT: width -- Reference to the pixel width (to be filled in). * + * * + * height -- Reference to the pixel height (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Dimensions(int &width, int &height) const +{ + static struct { + int Width; + int Height; + } _dimensions[BSIZE_COUNT] = { + {1,1}, + {2,1}, + {1,2}, + {2,2}, + {2,3}, + {3,2}, + {3,3}, + {4,2}, + {5,5} + }; + + width = _dimensions[Size].Width * ICON_PIXEL_W; + width -= (width/5); + height = _dimensions[Size].Height * ICON_PIXEL_H; + height -= (height/5); +} + + +/*********************************************************************************************** + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * * + * This routine will fetch a reference to the BuildingTypeClass as indicated by the * + * building type number specified. * + * * + * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. * + * * + * OUTPUT: Returns with a reference to the building type class as indicated by the * + * parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const & BuildingTypeClass::As_Reference(StructType type) +{ + return(* Pointers[type]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * * + * Use this routine to fetch the occupy list pointer for the building. The occupy list is * + * used to determine what cells the building occupies and thus precludes other buildings * + * or objects from using. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells * + * this building occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Occupy_List(bool placement) const +{ + SmudgeType bib = SMUDGE_NONE; + CELL cell=0; + + if ((placement && Bib_And_Offset(bib, cell)) || (Special.IsRoad && (*this == STRUCT_BARRACKS || (placement && *this == STRUCT_REFINERY)))) { + + /* + ** The barracks is always considered to have a bib under it for placement reasons even + ** if the bib logic is turned off. + */ + if (Special.IsRoad && *this == STRUCT_BARRACKS) { + bib = SMUDGE_BIB3; + cell = 0; + } + + /* + ** If bibs are disabled, then always ensure that the refinery bib is marked + ** as occupied. + */ + if (Special.IsRoad && *this == STRUCT_REFINERY) { + bib = SMUDGE_BIB2; + cell = MAP_CELL_W; + } + + SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib); + static short _list[25]; + short * dest = &_list[0]; + + /* + ** Copy the bib overlap list into the working buffer. + */ + short const * src = smudge.Occupy_List(); + while (*src != REFRESH_EOL) { + *dest++ = (*src++) + cell; + } + + /* + ** Append the building occupy list to this working buffer. + */ + src = OccupyList; + while (src && *src != REFRESH_EOL) { + *dest++ = *src++; + } + *dest = REFRESH_EOL; + + return(&_list[0]); + } + + if (OccupyList) { + return(OccupyList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * * + * This routine will fetch the overlap list for the building. The overlap list is used * + * to determine what cells the building's graphics cover, but is not considered to occupy * + * for movement purposes. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the * + * cells that this building overlaps. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Overlap_List(void) const +{ + if (OverlapList) { + return(OverlapList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Width -- Determines width of bulding in icons. * + * * + * Use this routine to determine the width of the building type in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building width in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Width(void) const +{ + static int width[BSIZE_COUNT] = { + 1, + 2, + 1, + 2, + 2, + 3, + 3, + 4, + 5 + }; + return(width[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Height -- Determins the height of the building in icons. * + * * + * Use this routine to find the height of the building in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building height in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Height(void) const +{ + static int height[BSIZE_COUNT] = { + 1, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 5 + }; + return(height[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * * + * Use this routine to determine how much it will cost to repair the building one * + * step. A step is defined as the number of hit points returned from the Repair_Step() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost to repair this building one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Repair_Cost(void) const +{ + int cost = (Raw_Cost()*REPAIR_STEP) / MaxStrength; + cost /= 2; + cost = MAX(cost, 1); + cost = Fixed_To_Cardinal(cost, REPAIR_PERCENT); + return(MAX(cost, 1)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Repair_Step -- Determines the repair step rate. * + * * + * This routine will determine how many strength points get healed for each "step". The * + * cost to repair one step is determine from the Repair_Cost() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points repaired for each "step". * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * * + * This routine is used to determine what (if any) bib should be used for this building * + * and also the cell offset for the upper left corner of the bib smudge type. * + * * + * INPUT: bib -- Reference to the bib that should be used for this building. * + * * + * cell -- The cell offset for the upper left corner of the bib. This offset is * + * relative to the upper left corner of the building. * + * * + * OUTPUT: Is a bib required for this building? If the result is true, then the correct * + * bib and cell offset will be filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const +{ + bib = SMUDGE_NONE; + + if (IsBibbed && !Special.IsRoad) { + switch (Width()) { + case 2: + bib = SMUDGE_BIB3; + break; + + case 3: + bib = SMUDGE_BIB2; + break; + + case 4: + bib = SMUDGE_BIB1; + break; + } + + /* + ** Adjust the bib position for special buildings that have the bib as part + ** of the building art itself. + */ + if (bib != SMUDGE_NONE) { + cell += ((Height()-1)*MAP_CELL_W); + } + } + return(bib != SMUDGE_NONE); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * * + * Use this routine to determine the maximum number of pips to display on this building * + * when it is rendered. Typically, this is the tiberium capacity divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this building when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Max_Pips(void) const +{ + return(Bound(Capacity/100, 0, 10)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name of this building (expressed as a text number). * + * If special civilian names are enabled, then the civilian buildings will show their true * + * name rather than "civilian building". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name of this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + * 07/17/1995 JLB : Village wells will always have their name displayed. * + *=============================================================================================*/ +int BuildingTypeClass::Full_Name(void) const +{ + if (::Scenario == 3 && Type == STRUCT_MISSION) { + return(TXT_PRISON); + } + if (!IsNominal || Special.IsNamed || IsWall || Debug_Map || Type == STRUCT_V23 || Type == STRUCT_V30 || Type == STRUCT_MISSION || Type == STRUCT_BIO_LAB) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN_BUILDING); +} + + +int BuildingTypeClass::Raw_Cost(void) const +{ +#ifdef PATCH + /* + ** Forces the turret cost down to original 250 for old + ** version games. + */ + if (IsV107 && Type == STRUCT_TURRET && GameToPlay != GAME_NORMAL) { + return(250); + } +#endif + + int cost = TechnoTypeClass::Raw_Cost(); + + if (Type == STRUCT_HELIPAD) { + cost -= AircraftTypeClass::As_Reference(AIRCRAFT_ORCA).Cost; + } + if (Type == STRUCT_REFINERY) { + cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost; + } + return(cost); +} + + +int BuildingTypeClass::Cost_Of(void) const +{ + if (Special.IsSeparate && Type == STRUCT_HELIPAD) { + return(Raw_Cost()); + } + +#ifdef PATCH + /* + ** Forces the turret cost down to original 250 for old + ** version games. + */ + if (IsV107 && Type == STRUCT_TURRET && GameToPlay != GAME_NORMAL) { + return(250); + } +#endif + + return(TechnoTypeClass::Cost_Of()); +} diff --git a/TIBERIANDAWN/BUILDING.CPP b/TIBERIANDAWN/BUILDING.CPP new file mode 100644 index 000000000..60bf1aaad --- /dev/null +++ b/TIBERIANDAWN/BUILDING.CPP @@ -0,0 +1,5429 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\building.cpv 2.13 02 Aug 1995 17:00:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * BuildingClass::As_Target -- Convert the building into a target value. * + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * BuildingClass::BuildingClass -- Constructor for buildings. * + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * BuildingClass::Captured -- Captures the building. * + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * BuildingClass::Detach -- Handles target removal from the game system. * + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * BuildingClass::Fire_At -- Fires weapon at specified target. * + * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * BuildingClass::Look -- Reveal map around building. * + * BuildingClass::Mark -- Building interface to map rendering system. * + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * BuildingClass::Mission_Construction -- Handles mission construction. * + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * BuildingClass::Update_Specials -- removes computer specials for lost bld * + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * BuildingClass::What_Action -- Determines what action will occur. * + * BuildingClass::Write_INI -- Writes all building data to an INI file. * + * BuildingClass::delete -- Deallocates building object. * + * BuildingClass::new -- Allocates a building object from building pool. * + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * BuildingClass::Validate -- validates building pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 3/26/2019 12:24PM +*/ +#include "SidebarGlyphx.h" + + +enum SAMState { + SAM_NONE=-1, // Used for non SAM site buildings. + SAM_UNDERGROUND, // Launcher is underground and awaiting orders. + SAM_RISING, // Doors open and launcher rises to normal locked down position. + SAM_READY, // Launcher can be facing any direction tracking targets. + SAM_FIRING, // Stationary while missile is being fired. + SAM_READY2, // Launcher can be facing any direction tracking targets. + SAM_FIRING2, // Stationary while missile is being fired. + SAM_LOCKING, // Rotating to locked position in preparation for lowering. + SAM_LOWERING, // Launcher is lowering into the ground. +}; + + +/*************************************************************************** +** Center of building offset table. +*/ +COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { + 0x00800080L, + 0x008000FFL, + 0x00FF0080L, + 0x00FF00FFL, + 0x018000FFL, + 0x00FF0180L, + 0x01800180L, + + 0x00FF0200L, + + 0x02800280L, +}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * BuildingClass::VTable; + + +/*********************************************************************************************** + * BuildingClass::Validate -- validates building pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int BuildingClass::Validate(void) const +{ + int num; + + num = Buildings.ID(this); + if (num < 0 || num >= BUILDING_MAX) { + Validate_Error("BUILDING"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * * + * This routine handles an incoming message to the building. Messages regulate the * + * various cooperative ventures between buildings and units. This might include such * + * actions as coordinating the construction yard animation with the actual building's * + * construction animation. * + * * + * INPUT: from -- The originator of the message received. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter that might be used to return * + * extra information to the message originator. * + * * + * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/09/1994 JLB : Created. * + * 06/26/1995 JLB : Forces refinery load anim to start immediately. * + * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * + *=============================================================================================*/ +RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + /* + ** This message is received as a request to attach/load/dock with this building. + ** Verify that this is allowed and return the appropriate response. + */ + case RADIO_HELLO: + //Refineries can't be interupted while they're processing a harvester - LLL April 22, 2020 + if (Mission == MISSION_HARVEST) { + return(RADIO_NEGATIVE); + } + break; + + case RADIO_CAN_LOAD: + TechnoClass::Receive_Message(from, message, param); + if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact())) return(RADIO_NEGATIVE); + switch (Class->Type) { + case STRUCT_AIRSTRIP: + if (from->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass const *)from) == AIRCRAFT_CARGO) { + return(RADIO_ROGER); + } + break; + + case STRUCT_HELIPAD: + if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_REPAIR: + if (/*from->Health_Ratio() < 0x0100 &&*/ from->What_Am_I() == RTTI_UNIT || from->What_Am_I() == RTTI_AIRCRAFT) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + case STRUCT_REFINERY: + if (from->What_Am_I() == RTTI_UNIT && + *((UnitClass *)from) == UNIT_HARVESTER && + (ScenarioInit || !Is_Something_Attached())) { + + return(RADIO_ROGER); + } + break; + + default: + break; + } + return(RADIO_NEGATIVE); + + /* + ** This message is received when the object has attached itself to this + ** building. + */ + case RADIO_IM_IN: + if (Mission == MISSION_DECONSTRUCTION) { + return(RADIO_NEGATIVE); + } + switch (Class->Type) { + case STRUCT_REPAIR: + IsReadyToCommence = true; + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_HELIPAD: + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_REFINERY: + ScenarioInit++; + Begin_Mode(BSTATE_ACTIVE); + ScenarioInit--; + Mark(MARK_CHANGE); + Assign_Mission(MISSION_HARVEST); + return(RADIO_ATTACH); + } + break; + + /* + ** Docking maneuver maintenance message. See if new order should be given to the + ** unit trying to dock. + */ + case RADIO_DOCKING: + TechnoClass::Receive_Message(from, message, param); + + /* + ** When in radio contact for loading, the refinery starts + ** flashing the lights. + */ + //Fix for refinery animation bug when mission is harvest - LLL April 22, 2020 + if (*this == STRUCT_REFINERY && BState != BSTATE_FULL && Mission != MISSION_HARVEST) { + Begin_Mode(BSTATE_FULL); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + if (*this == STRUCT_HELIPAD) { + param = As_Target(); + } else { + if (*this == STRUCT_REPAIR) { + Transmit_Message(RADIO_TETHER); + param = ::As_Target(Coord_Cell(Center_Coord())); + } else { + param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW))); + } + } + + /* + ** Tell the harvester to move to the docking pad of the refinery. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + + /* + ** Since the harvester is already there, tell it to begin the backup + ** procedure now. If it can't, then tell it to get outta here. + */ + Transmit_Message(RADIO_TETHER); + if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { + from->Scatter(NULL, true); + } + } + } + return(RADIO_ROGER); + + /* + ** If a transport or harvester is requesting permission to head toward, dock + ** and load/unload, check to make sure that this is allowed given the current + ** state of the building. + */ + case RADIO_ARE_REFINERY: + if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { + return(RADIO_NEGATIVE); + } + return(RADIO_ROGER); + + /* + ** Someone is telling us that it is starting construction. This should only + ** occur if this is a construction yard and a building was just placed on + ** the map. + */ + case RADIO_BUILDING: + Assign_Mission(MISSION_REPAIR); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Someone is telling us that they have finished construction. This should + ** only occur if this is a construction yard and the building that was being + ** constructed has finished. In this case, stop the construction yard + ** animation. + */ + case RADIO_COMPLETE: + if (Mission != MISSION_DECONSTRUCTION) { + Assign_Mission(MISSION_GUARD); + } + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message may occur unexpectedly if the unit in contact with this + ** building is suddenly destroyed. Handle any cleanup necessary. For example, + ** a construction yard should stop its construction animation in this case. + */ + case RADIO_OVER_OUT: + Begin_Mode(BSTATE_IDLE); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message is received when an object has completely left + ** building. Sometimes special cleanup action is required when + ** this event occurs. + */ + case RADIO_UNLOADED: + if (*this == STRUCT_REPAIR) { + if (Distance(from) < 0x0180) { + return(RADIO_ROGER); + } + } + + //Turn off the refinery lights - LLL April 22, 2020 + if (*this == STRUCT_REFINERY) { + Begin_Mode(BSTATE_IDLE); + } + + TechnoClass::Receive_Message(from, message, param); + if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + return(RADIO_ROGER); + } + + /* + ** Pass along the message to the default message handler in the radio itself. + */ + return(TechnoClass::Receive_Message(from, message, param)); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * * + * This utility function will output the current status of the building class to the * + * monochrome screen. It is through this data that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂÄÄÄÄÄÄÄÂRadio:ÂCoord:ÄÄÂÄÄÄÄÄÄÄÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂÄÄÄÁÄÂTurret:ÂÄÄÄÄÄÁÂÄBuilding:ÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂÄÄÄÄÄÄÂTiberium:ÂFlash:ÂStage:ÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ \n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ \n" + "³Locked on Map.³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Repairing.....³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Set_Cursor(35, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); + mono->Set_Cursor(50, 3); + if (Factory) { + mono->Printf(Factory->Get_Object()->Class_Of().IniName); + mono->Printf(" "); + mono->Printf("%d%%", Factory->Completion()); + } else { + mono->Printf("(empty)"); + } + + mono->Text_Print("X", 16 + (IsRepairing?2:0), 14); +// mono->Set_Cursor(44, 3);mono->Printf("%d", SAM); + mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * * + * This is the low level graphic routine that displays the building at the location * + * specified. * + * * + * INPUT: x,y -- The coordinate to draw the building at. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a clipping window parameter. * + * 07/06/1995 JLB : Handles damaged silos correctly. * + *=============================================================================================*/ +void BuildingClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Pointer to loaded shape file. + int shapenum; + + shapenum = Fetch_Stage(); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + if (BState == BSTATE_CONSTRUCTION) { + shapefile = Class->Get_Buildup_Data(); + + /* + ** If the building is deconstructing, then the display frame progresses + ** from the end to the beginning. Reverse the shape number accordingly. + */ + if (Mission == MISSION_DECONSTRUCTION) { + shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; + } + + } else { + + shapefile = Class->Get_Image_Data(); + + /* + ** The obelisk has a stage value than can be overridden by + ** its current state. + */ + if (*this == STRUCT_OBELISK) { + if (IsCharged) { + shapenum = 3; + } else { + if (IsCharging) { + shapenum = Fetch_Stage(); + } else { + shapenum = 0; + } + } + } + + /* + ** Buildings that contain a turret handle their shape determination + ** differently than normal buildings. They need to take into consideration + ** the direction the turret is facing. + */ + if (Class->IsTurretEquipped) { + shapenum = UnitClass::BodyShape[Facing_To_32(PrimaryFacing.Current())]; + + if (*this == STRUCT_SAM) { + + /* + ** SAM sites that are free to rotate fetch their animation frame + ** from the building's turret facing. All other animation stages + ** fetch their frame from the embedded animation sequencer. + */ + if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_READY2 || Status == SAM_FIRING2 || Status == SAM_LOCKING) { + shapenum += 16; + } else { + shapenum = Fetch_Stage(); + } + } else { + if (IsInRecoilState) { + shapenum += 32; + } + } + if (Health_Ratio() < 0x0080) { + shapenum += 64; + } + } else { + + /* + ** If it has only one point of strength left, it is shown in the + ** worst state possible. + */ + if (Strength <= 1) { + shapenum = Get_Build_Frame_Count(shapefile)-1; + } else { + + if (*this == STRUCT_WEAP) { + shapenum = 0; + if (Health_Ratio() < 0x0080) { + shapenum = 1; + } + + } else { + + /* + ** Special render stage for silos. The stage is dependant on the current + ** Tiberium collected as it relates to Tiberium capacity. + */ + if (*this == STRUCT_STORAGE) { + int level = 0; + if (House->Capacity) { + level = (House->Tiberium * 5) / House->Capacity; + } +// int level = Fixed_To_Cardinal(4, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); + + shapenum += Bound(level, 0, 4); + if (Health_Ratio() < 0x0080) { + shapenum += 5; + } + + } else { + + if (Health_Ratio() < 0x0080) { + + /* + ** Special damage stage for pump. + */ + if (!Class->IsSimpleDamage) { + int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; + int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; + int largest = MAX(last1, last2); + last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; + largest = MAX(largest, last2); + last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; + largest = MAX(largest, last2); + + shapenum += largest; + } else { + + /* + ** Presume that the damage stage is the end frame. + */ + shapenum = Get_Build_Frame_Count(shapefile) - 2; + } + } + } + } + } + } + } + + /* + ** Actually draw the building shape. + */ + IsTheaterShape = Class->IsTheater; + Techno_Draw_Object(shapefile, shapenum, x, y, window); + IsTheaterShape = false; + + /* + ** Patch for adding overlay onto weapon factory. Only add the overlay if + ** the building has more than 1 hp. Also, if the building's in radio + ** contact, he must be unloading a constructed vehicle, so draw that + ** vehicle before drawing the overlay. + */ + if (BState != BSTATE_CONSTRUCTION) { + + /* + ** A Tethered object is always rendered AFTER the building. + */ + if (*this == STRUCT_WEAP && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { + TechnoClass * contact = Contact_With_Whom(); + + assert(contact->IsActive); + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } + + /* + ** Draw the weapon factory custom overlay graphic. + */ + if (*this == STRUCT_WEAP && Strength > 1) { + shapenum = Door_Stage(); + if (Health_Ratio() < 0x0080) shapenum += 10; + // Added override shape file name. ST - 6/20/2019 1:35PM + //Techno_Draw_Object(WarFactoryOverlay, shapenum, x, y, window); + Techno_Draw_Object_Virtual(WarFactoryOverlay, shapenum, x, y, window, "WEAP2"); + } + + /* + ** Draw any repair feedback graphic required. + */ + if (IsRepairing && IsWrenchVisible) { + CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + TechnoClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * BuildingClass::Mark -- Building interface to map rendering system. * + * * + * This routine is used to mark the map cells so that when it renders * + * the underlying icons will also be updated as necessary. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Building is removed. * + * MARK_CHANGE -- Building changes shape. * + * MARK_DOWN -- Building is added. * + * * + * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * + * when the building is already marked down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1994 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added health bar tracking. * + * 12/23/1994 JLB : Calls low level check before proceeding. * + * 01/27/1995 JLB : Special road spacer template added. * + *=============================================================================================*/ +bool BuildingClass::Mark(MarkType mark) +{ + Validate(); + if (TechnoClass::Mark(mark)) { + short const *offset = Overlap_List(); + short const *occupy = Occupy_List(); + CELL cell = Coord_Cell(Coord); + SmudgeType bib; + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } + } + break; + + case MARK_DOWN: + + /* + ** Special wall logic is handled here. A building that is really a wall + ** gets converted into an overlay wall type when it is placed down. The + ** actual building object itself is destroyed. + */ + if (Class->IsWall) { + switch (Class->Type) { + case STRUCT_BRICK_WALL: + new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); + break; + + case STRUCT_BARBWIRE_WALL: + new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); + break; + + case STRUCT_SANDBAG_WALL: + new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); + break; + + case STRUCT_WOOD_WALL: + new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); + break; + + case STRUCT_CYCLONE_WALL: + new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); + break; + } + Transmit_Message(RADIO_OVER_OUT); + Delete_This(); + + } else { + + if (Can_Enter_Cell(cell) == MOVE_OK) { + + /* + ** Determine if a bib is required for this building. If one is, then + ** create and place it. + */ + CELL newcell = cell; + if (Class->Bib_And_Offset(bib, newcell)) { + new SmudgeClass(bib, Cell_Coord(newcell), House->Class->House); + } + + Map.Place_Down(cell, this); + } else { + return(false); + } + } + break; + + default: + Map.Refresh_Cells(cell, offset); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Fire_At -- Fires weapon at specified target. * + * * + * This routine does the actual firing of a projectile from the * + * building toward the specified target. Prior to calling this * + * routine, the building must have rotated into position and acquired * + * a suitable target. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the projectile just launched. This * + * may come in handy if additional adjustments to the projectile * + * are required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * BuildingClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet; // Projectile. + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + bullet = TechnoClass::Fire_At(target, which); + if (bullet) { + if (*this == STRUCT_SAM) { + AnimClass *anim = new AnimClass((AnimType)(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())), Center_Coord()); + if (anim) { + anim->Attach_To(this); + } + + // MBL 04.17.2020 + Sound_Effect(weapon->Sound, Coord); + + } else { + + /* + ** Flash the muzzle, play sound, and perform any firing animation. + */ + Sound_Effect(weapon->Sound, Coord); + + AnimClass* anim = NULL; + if (weapon->Fires == BULLET_BULLET) { + anim = new AnimClass((AnimType)(ANIM_GUN_N + Dir_Facing(PrimaryFacing.Current())), Fire_Coord(which)); + } else { + switch (weapon->Fires) { + case BULLET_SPREADFIRE: + break; + case BULLET_LASER: + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + break; + default: + anim = new AnimClass(ANIM_MUZZLE_FLASH, Fire_Coord(which)); + break; + } + } + if (anim != NULL) { + anim->Attach_To(this); + } + Mark(MARK_CHANGE); + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * * + * This function is to handle the AI logic for the building. The graphic logic (facing, * + * firing, and animation) is handled elsewhere. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/26/1994 JLB : Handles production. * + * 06/11/1995 JLB : Revamped. * + *=============================================================================================*/ +void BuildingClass::AI(void) +{ + Validate(); + + /* + ** Process building animation state changes. Transition to a following state + ** if there is one specified and the current animation sequence has expired. + ** This process must occur before mission AI since the mission AI relies on + ** the bstate change to occur immediately before the MissionClass::AI. + */ + bool stagechange = Graphic_Logic(); + bool toloop = false; + + /* + ** Always refresh the SAM site if it has an animation change. + */ + if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); + + if ((!Class->IsTurretEquipped && *this != STRUCT_OBELISK) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { + if (stagechange) { + + /* + ** Check for animation end or if special case of MCV deconstructing when it is allowed + ** to convert back into an MCV. + */ + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + /* + ** When the last frame of the current animation sequence is reached, flag that + ** a new mission may be started. This must occur before the animation actually + ** loops so that if a mission change does occur, it will have a chance to change + ** the building graphic before the last frame is replaced by the first frame of + ** the loop. + */ + if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (Special.IsMCVDeploy && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { + IsReadyToCommence = true; + } + + /* + ** If the animation advances beyond the last frame, then start the animation + ** sequence over from the beginning. + */ + if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { + toloop = true; + } + Mark(MARK_CHANGE); + } else { + if (BState == BSTATE_NONE || Fetch_Rate() == 0) { + IsReadyToCommence = true; + } + } + } + + /* + ** If there is a door that is animating, then it might cause this building + ** to be redrawn. Check for and flag to redraw as necessary. + */ + if (Time_To_Redraw()) { + Clear_Redraw_Flag(); + Mark(MARK_CHANGE); + } + + /* + ** The animation sequence has looped. Restart it and flag this loop condition. + ** This is used to tell the mission system that the animation has completed. It + ** also signals that now is a good time to act on any pending mission. + */ + if (toloop) { + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + Mark(MARK_CHANGE); + } + + /* + ** If now is a good time to act on a new mission, then do so. This process occurs + ** here because some outside event may have requested a mission change for the building. + ** Such outside requests (player input) must be initiated BEFORE the normal AI process. + */ + if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { + + /* + ** Clear the commencement flag ONLY if something actually occured. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** Proceed with normal logic processing. This is where the mission processing + ** occurs. This call must be located after the animation sequence makes the + ** transition to the next frame (see above) in order for the mission logic to + ** act at the exact moment of graphic transition BEFORE it has a chance to + ** be displayed. + */ + TechnoClass::AI(); + + /* + ** If now is a good time to act on a new mission, then do so. This occurs here because + ** some AI event may have requested a mission change (usually from another mission + ** state machine). This must occur here before it has a chance to render. + */ + if (IsReadyToCommence) { + + /* + ** Clear the commencement flag ONLY if something actually occured. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** If a change of animation was requested, then make the change + ** now. The building animation system acts independantly but subordinate + ** to the mission state machine system. By performing the animation change-up + ** here, the mission AI system is ensured of immediate visual affect when it + ** decides to change the animation state of the building. + */ + if (QueueBState != BSTATE_NONE) { + if (BState != QueueBState) { + BState = QueueBState; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + } + QueueBState = BSTATE_NONE; + } + + /* + ** If the building's strength has changed, then update the power + ** accordingly. + */ + if (Strength != LastStrength) { + int oldpower = Power_Output(); + LastStrength = Strength; + int newpower = Power_Output(); + House->Adjust_Power(newpower - oldpower); + } + + /* + ** Check to see if the destruction countdown timer is active. If so, then decrement it. + ** When this timer reaches zero, the building is removed from the map. All the explosions + ** are presumed to be in progress at this time. + */ + if (!Strength) { + if (CountDown.Expired()) { + Limbo(); + Drop_Debris(WhomToRepay); + Delete_This(); + } + return; + } + + /* + ** Obelisk charging logic. + */ + if (*this == STRUCT_OBELISK && BState != BSTATE_CONSTRUCTION) { + if (Target_Legal(TarCom) && House->Power_Fraction() >= 0x0100) { + if (!IsCharged) { + if (IsCharging) { + if (stagechange) { + Mark(MARK_CHANGE); + if (Fetch_Stage() >= 4) { + IsCharged = true; + IsCharging = false; + Set_Rate(0); + } + } + } else { + IsCharged = false; + IsCharging = true; + Set_Stage(0); + Set_Rate(OBELISK_ANIMATION_RATE); + Sound_Effect(VOC_LASER_POWER, Coord); + } + } + } else { + if (IsCharging || IsCharged) { + Mark(MARK_CHANGE); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + } + } + } + + /* + ** Handle any repair process that may be going on. + */ + if (IsRepairing) { + if ((Frame % 15) == 0) { + IsWrenchVisible = (IsWrenchVisible == false); + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + /* + ** Check for and expend any necessary monies to continue the repair. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsRepairing = false; + } + } else { + IsRepairing = false; + } + } + } + + /* + ** Handle any production tied to this building. Only computer controlled buildings have + ** production attached to the building itself. The player uses the sidebar interface for + ** all production control. + */ + if (Factory && Factory->Has_Completed() && PlacementDelay.Expired()) { + + switch (Exit_Object(Factory->Get_Object())) { + + /* + ** If the object could not leave the factory, then either request + ** a transport, place the (what must be a) building using another method, or + ** abort the production and refund money. + */ + case 0: + Factory->Abandon(); + delete Factory; + Factory = 0; + break; + + case 1: + PlacementDelay = TICKS_PER_SECOND*3; + break; + + case 2: + Factory->Completed(); + delete Factory; + Factory = 0; + break; + + } + } + + /* + ** For computer controlled buildings, determine what should be produced and start + ** production accordingly. + */ + if (!House->IsHuman && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Possibly start repair process if the building is below half strength. + */ + int ratio = 0x0040; + if (Scenario > 6) ratio = 0x0080; + if (Scenario > 10) ratio = 0x00C0; + if (Class->IsRepairable && Health_Ratio() <= (unsigned) ratio) { + if (House->Available_Money() >= REPAIR_THRESHHOLD) { + Repair(1); + } else { + if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger) { + Sell_Back(1); + } + } + } + + /* + ** Buildings that produce other objects have special factory logic handled here. + */ + if (Class->ToBuild != RTTI_NONE) { + + if (Factory) { + + /* + ** If production has halted, then just abort production and make the + ** funds available for something else. + */ + if (PlacementDelay.Expired() && !Factory->Is_Building()) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + } else { + + /* + ** Only look to start production if there is at least a small amount of + ** money available. In cases where there is no practical money left, then + ** production can never complete -- don't bother starting it. + */ + if (House->IsStarted && House->Available_Money() > 10) { + TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild); + + /* + ** If a suitable object type was selected for production, then start + ** producing it now. + */ + if (techno) { + Factory = new FactoryClass; + if (Factory) { + if (!Factory->Set(*techno, *House)) { + delete Factory; + Factory = 0; + } else { +#ifdef USE_RA_AI + House->Production_Begun(Factory->Get_Object()); // Added for RA AI in TD. ST - 7/26/2019 9:46AM +#endif + Factory->Start(); + } + } + } + } + } + } + } + + /* + ** Check for demolition timeout. When timeout has expired, the building explodes. + */ + if (IsGoingToBlow && CountDown.Expired()) { + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + TechnoClass *saboteur = As_Techno(WhomToRepay); + if (saboteur && saboteur->IsActive && saboteur->House && saboteur->House->IsHuman) { + On_Achievement_Event(saboteur->House, "BUILDING_DESTROYED_C4", object_type->IniName); + } + } + + SabotagedType = Class->Type; + int damage = 5000; + Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay)); + Mark(MARK_CHANGE); + } + + /* + ** If the building was in a recoil state (as it would be just as it fires), then + ** restore the building. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE); + } + + /* + ** Turret equiped buildings must handle turret rotation logic here. This entails + ** rotating the turret to the desired facing as well as figuring out what that + ** desired facing should be. + */ + if (Class->IsTurretEquipped && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Rotate turret to match desired facing. + */ + if (PrimaryFacing.Is_Rotating()) { + if (*this == STRUCT_SAM) { + if (PrimaryFacing.Rotation_Adjust(15)) { + Mark(MARK_CHANGE); + } + } else { + if (PrimaryFacing.Rotation_Adjust(12)) { + Mark(MARK_CHANGE); + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * * + * Use this routine to transform a building that has been held in limbo * + * state, into one that really exists on the map. Once a building as * + * been unlimboed, then it becomes a normal object in the game world. * + * * + * INPUT: pos -- The position to place the building on the map. * + * * + * dir (optional) -- not used for this class * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: The unlimbo operation might not be successful if the * + * building could not be placed at the location specified. * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 06/07/1994 JLB : Matches virtual function format for base class. * + * 05/09/1995 JLB : Handles wall placement. * + * 06/18/1995 JLB : Checks for wall legality before placing down. * + *=============================================================================================*/ +bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); +#ifdef OBSOLETE + if (*this == STRUCT_ROAD) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + ObjectClass * o = OverlayTypeClass::As_Reference(OVERLAY_ROAD).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Transmit_Message(RADIO_OVER_OUT); + Delete_This(); + return(true); + } + } + return(false); + } +#endif + + /* + ** If this is a wall type building, then it never gets unlimboed. Instead, it gets + ** converted to an overlay type. + */ + if (Class->IsWall) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + OverlayType otype = OVERLAY_NONE; + switch (Class->Type) { + case STRUCT_SANDBAG_WALL: + otype = OVERLAY_SANDBAG_WALL; + break; + + case STRUCT_CYCLONE_WALL: + otype = OVERLAY_CYCLONE_WALL; + break; + + case STRUCT_BRICK_WALL: + otype = OVERLAY_BRICK_WALL; + break; + + case STRUCT_BARBWIRE_WALL: + otype = OVERLAY_BARBWIRE_WALL; + break; + + case STRUCT_WOOD_WALL: + otype = OVERLAY_WOOD_WALL; + break; + } + if (otype != OVERLAY_NONE) { + ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Map[Coord_Cell(coord)].Owner = House->Class->House; + Transmit_Message(RADIO_OVER_OUT); + Delete_This(); + return(true); + } + } + } + return(false); + } + + /* + ** Normal building unlimbo process. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->BScan |= (1L << Class->Type); + House->ActiveBScan |= (1L << Class->Type); + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:25AM + // + House->Recalc_Center(); +#endif + + /* + ** Update the total factory type, assuming this building has a factory. + */ + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories++; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories++; + break; + + case RTTI_UNITTYPE: + House->UnitFactories++; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories++; + break; + + default: + break; + } + + /* + ** Possibly the sidebar will be affected by this addition. + */ + House->IsRecalcNeeded = true; + LastStrength = 0; + + // Changed for new multiplayer. ST - 4/3/2019 11:20AM + //if ((!IsDiscoveredByPlayer && Map[Coord_Cell(coord)].IsVisible) || GameToPlay != GAME_NORMAL) { + // Revealed(PlayerPtr); + //} + if (!Is_Discovered_By_Player(House) && Map[Coord_Cell(coord)].Is_Visible(House) || GameToPlay != GAME_NORMAL) { + if (House->IsHuman) { + Revealed(House); + } + } + + if (!House->IsHuman) { + Revealed(House); + } + + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * * + * This routine will inflict damage points upon the specified building. * + * It will handle the damage animation and building destruction. Use * + * this routine whenever a building is attacked. * + * * + * INPUT: damage -- Amount of damage to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The kind of damage to inflict. * + * * + * source -- The source of the damage. This is used to change targeting. * + * * + * OUTPUT: true/false; Was the building destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1991 : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added warhead modifier to damage. * + * 06/03/1994 JLB : Added source of damage as target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 07/15/1995 JLB : Power ratio gets adjusted. * + *=============================================================================================*/ +ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + int shakes; + + if (this != source) { + if (source) Base_Is_Attacked(source); + + short const *offset = Occupy_List(); + + /* + ** SPECIAL CASE: + ** SAM sites that are closed will take half damage, but never less than one point. + */ + if (*this == STRUCT_SAM && Status == SAM_UNDERGROUND) { + damage /= 2; + damage++; + } + + /* + ** Damage from an ion cannon against the Temple of Nod does more damage than + ** usual. + */ + if (GameToPlay == GAME_NORMAL && *this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { + damage += damage/2; + } + + /* + ** Perform the low level damage assessment. + */ + res = TechnoClass::Take_Damage(damage, distance, warhead, source); + + switch (res) { + case RESULT_DESTROYED: + + /* + ** Destroy all attached objects. + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + Sound_Effect(VOC_XPLOBIG4, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + + /* + ** If the building is destroyed, then lots of + ** explosions occur. + */ + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); + if (Random_Pick(0, 1) == 0) { + new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); + if (Random_Pick(0, 1) == 0) { + new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + //Start_Profiler(); + new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0,3)); + } + + shakes = Class->Cost_Of() / 400; + if (shakes) { + Shake_The_Screen(shakes, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(shakes, source->Owner()); + } + } + Sound_Effect(VOC_CRUMBLE, Coord); + if (Mission == MISSION_DECONSTRUCTION) { + CountDown = 0; + Set_Rate(0); + } else { + CountDown = 8; + } + + /* + ** A destuction of the Temple by an ion cannon requires a global + ** remembering of this fact. The finale uses this information to + ** play the correct movie. + */ + if (*this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { + TempleIoned = true; + + /* + ** Maybe trigger an achivement if the structure is owned by an AI house in campaign mode. ST - 11/14/2019 1:53PM + */ + if (GameToPlay == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + On_Achievement_Event(source->House, "ION_DESTROYS_TEMPLE", object_type->IniName); + } + } + + } else { + TempleIoned = false; + } + + if (House) { + House->Check_Pertinent_Structures(); + } + break; + + case RESULT_HALF: + if (*this == STRUCT_PUMP) { + AnimClass *anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); + if (anim) { + anim->Attach_To(this); + } + } + // Fall into next case. + + case RESULT_MAJOR: + Sound_Effect(VOC_XPLOBIG4, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + AnimClass * anim = NULL; + + /* + ** Show pieces of fire to indicate that a significant change in + ** damage level has occurred. + */ + if (warhead == WARHEAD_FIRE) { + switch (Random_Pick(0, 13)) { + case 0: + case 1: + case 2: + case 3: + case 4: + anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 5: + case 6: + case 7: + anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 8: + anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); + break; + + case 9: + case 10: + case 11: + case 12: + case 13: + break; + } + } else { + if (Random_Pick(0, 1) == 0) { + anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + + /* + ** If the animation was created, then attach it to the building. + */ + if (anim) { + anim->Attach_To(this); + } + } + break; + + case RESULT_NONE: + break; + } + + if (source && res != RESULT_NONE) { + + /* + ** If any damage occurred, then inform the house of this fact. If it is the player's + ** house, it might announce this fact. + */ + House->Attacked(this); + + /* + ** Save the type of the house that's doing the damage, so if the building burns + ** to death credit can still be given for the kill + */ + WhoLastHurtMe = source->Owner(); + + /* + ** When certain buildings are hit, they "snap out of it" and + ** return fire if they are able and allowed. + */ + if (*this != STRUCT_SAM && + !House->Is_Ally(source) && + Class->Primary != WEAPON_NONE && + (!Target_Legal(TarCom) || !In_Range(TarCom))) { + + if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Special.IsSmartDefense)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Generate a random rotation effect since there is nothing else that this + ** building can do. + */ + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + } + } + } + } + } + + return(res); +} + + +/*********************************************************************************************** + * BuildingClass::Look -- Reveal map around building. * + * * + * Given a building, reveal the cells around the building in accordance * + * with the building's sighting range. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a very slow routine. Call only when necessary. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void BuildingClass::Look(bool) +{ + /* + ** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 4/17/2019 11:50AM + */ + Validate(); + + if (Class->SightRange) { + Map.Sight_From(House, Coord_Cell(Center_Coord()), Class->SightRange, false); + } + +#if (0) + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { + + if (Is_Owned_By_Player(PlayerPtr) || Is_Discovered_By_Player(PlayerPtr)) { + Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false); + } + } else { + + for (int i = 0; i < MPlayerCount; i++) { + HousesType house_type = MPlayerHouses[i]; + HouseClass *house = HouseClass::As_Pointer(house_type); + + if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) { + Map.Sight_From(house, Coord_Cell(Center_Coord()), Class->SightRange, false); + } + } + } +#endif +} + + +#if (0) // For reference. ST - 4/17/2019 11:38AM +void BuildingClass::Look(bool) +{ + Validate(); + if (IsOwnedByPlayer || IsDiscoveredByPlayer) { + Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false); + } +} +#endif + + +/*********************************************************************************************** + * BuildingClass::new -- Allocates a building object from building pool. * + * * + * This routine will allocate a building slot from the building alloc * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated building. If NULL is * + * returned, then this indicates a failure to allocate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + * 05/17/1994 JLB : Revamped allocation scheme * + * 07/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void * BuildingClass::operator new(size_t ) +{ + void * ptr = Buildings.Allocate(); + if (ptr) { + ((BuildingClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BuildingClass::delete -- Deallocates building object. * + * * + * This is the memory deallocation operation for a building object. * + * Since buildings are allocated out of a fixed memory block, all that * + * is needed is to flag the unit as inactive. * + * * + * INPUT: ptr -- Pointer to building to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::operator delete(void *ptr) +{ + if (ptr) { + ((BuildingClass *)ptr)->IsActive = false; + } + Buildings.Free((BuildingClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * BuildingClass::BuildingClass -- Constructor for buildings. * + * * + * This routine inserts a building into the object tracking system. * + * It is placed into a limbo state unless a location is provided for * + * it to unlimbo at. * + * * + * INPUT: type -- The structure type to make this object. * + * * + * house -- The owner of this building. * + * * + * pos -- The position to unlimbo the building. If -1 is * + * specified, then the building remains in a limbo * + * state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + * 08/07/1995 JLB : Fixed act like value to match expected value. * + *=============================================================================================*/ +BuildingClass::BuildingClass(StructType type, HousesType house) : + Class(&BuildingTypeClass::As_Reference(type)), + TechnoClass(house) +{ + PlacementDelay = 0; + LastStrength = 0; + ActLike = House->ActLike; + BState = BSTATE_NONE; + CountDown.Set(0); + Factory = 0; +#ifndef USE_RA_AI + House->CurBuildings++; // Removed for RA AI in TD +#endif + WhomToRepay = TARGET_NONE; + IsCaptured = false; + IsCharged = false; + IsCharging = false; + IsSurvivorless = false; + IsGoingToBlow = false; + IsReadyToCommence = false; + IsRepairing = false; + IsSecondShot = !Class->IsTwoShooter; + IsWrenchVisible = false; + QueueBState = BSTATE_NONE; + Strength = Class->MaxStrength; + WhoLastHurtMe = house; + Ammo = Class->MaxAmmo; + + /* + ** Make sure that newly built house specific building types will act like + ** the house they are supposed to act like, regardless of who the current + ** owner may happen to be. + */ + if ((type == STRUCT_AIRSTRIP || type == STRUCT_HAND) && house != HOUSE_BAD) { + ActLike = HOUSE_BAD; + IsCaptured = true; + } + if ((type == STRUCT_WEAP || type == STRUCT_BARRACKS) && house != HOUSE_GOOD) { + ActLike = HOUSE_GOOD; + IsCaptured = true; + } + + if (GameToPlay == GAME_INTERNET){ + House->BuildingTotals->Increment_Unit_Total( (int) type); + } + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Add(this); +#endif // USE_RA_AI +} + + +/*********************************************************************************************** + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * * + * This destructor for building objects will put the building in limbo if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass::~BuildingClass(void) +{ + if (GameActive && Class) { + if (House) { +#ifndef USE_RA_AI + House->CurBuildings--; +#else + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Remove(this); +#endif + } + Limbo(); + } +} + + +/*********************************************************************************************** + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * * + * This routine is called when a building is destroyed. It handles * + * placing the rubble down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * + * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * + *=============================================================================================*/ +void BuildingClass::Drop_Debris(TARGET source) +{ + Validate(); + CELL const *offset; + CELL cell; + + /* + ** Special case for Moebius to run from destroyed technology + ** building. + */ + if (GameToPlay == GAME_NORMAL && *this == STRUCT_MISSION && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10) { + InfantryClass * i = new InfantryClass(INFANTRY_CHAN, House->Class->House); + + ScenarioInit++; + if (i->Unlimbo(Center_Coord(), DIR_N)) { + i->Trigger = TriggerClass::As_Pointer("CHAN"); + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + ScenarioInit--; + i->Scatter(0, true); + ScenarioInit++; + i->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete i; + } + ScenarioInit--; + } + + + /* + ** Generate random survivors from the destroyed building. + */ + cell = Coord_Cell(Coord); + offset = Occupy_List(); + int odds = 2; + if (Target_Legal(WhomToRepay)) odds -= 1; + if (IsCaptured) odds += 6; + while (*offset != REFRESH_EOL) { + CELL newcell; + + newcell = cell + *offset++; + + /* + ** Infantry could run out of a destroyed building. + */ + if (!House->IsToDie && !IsSurvivorless) { + InfantryClass * i = NULL; + + if (Random_Pick(0, odds) == 1) { + i = new InfantryClass(Crew_Type(), House->Class->House); + if (i) { + if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; + ScenarioInit++; + if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + i->Scatter(0, true); + if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { + i->Assign_Mission(MISSION_ATTACK); + i->Assign_Target(source); + } else { + if (House->IsHuman) { + i->Assign_Mission(MISSION_GUARD); + } else { + i->Assign_Mission(MISSION_HUNT); + } + } + } else { + delete i; + } + ScenarioInit--; + } + } + } + + /* + ** Possibly add some smoke rising from the ashes of the building. + */ + switch (Random_Pick(0, 5)) { + case 0: + case 1: + case 2: + new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); + break; + + default: + break; + } + + /* + ** The building always scars the ground in some fashion. + */ + if (Random_Pick(0, 3) == 0) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); + } else { + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* + * * + * This interface routine handles when the player clicks on the map while this building * + * is currently selected. This is used to assign an override target to a turret or * + * guard tower. * + * * + * INPUT: target -- The target that was clicked upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + + if (action == ACTION_TOGGLE_PRIMARY && Class->IsFactory) { + OutList.Add(EventClass(EventClass::PRIMARY, As_Target())); + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * * + * This routine really only serves one purpose -- to allow targeting of the ground for * + * buildings that are euipped with weapons. * + * * + * INPUT: action -- The requested action to perform. * + * * + * cell -- The cell location to perform the action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, CELL cell) +{ + Validate(); + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + } + + if (action == ACTION_MOVE && *this == STRUCT_CONST) { + OutList.Add(EventClass(EventClass::ARCHIVE, As_Target(), ::As_Target(cell))); + OutList.Add(EventClass(EventClass::SELL, As_Target())); + + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } +} + + +/*********************************************************************************************** + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * * + * Assigning of a target to a building makes sense if the building is one that can attack. * + * This routine would be used to assign the attack target to a turret or guard tower. * + * * + * INPUT: target -- The target that was clicked on while this building was selected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 11/02/1994 JLB : Checks for range before assigning target. * + *=============================================================================================*/ +void BuildingClass::Assign_Target(TARGET target) +{ + Validate(); + if (*this != STRUCT_SAM && !In_Range(target, 0)) { + target = TARGET_NONE; + } + + TechnoClass::Assign_Target(target); +} + + +/*********************************************************************************************** + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * * + * This routine initializes the building system in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Init(void) +{ + BuildingClass *ptr; + + Buildings.Free_All(); + + ptr = new BuildingClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * * + * This function is used to cause an object to exit the building. It is called when a * + * factory produces a vehicle or other mobile object and that object needs to exit the * + * building to join the ranks of a regular unit. Typically, the object is placed down on * + * the map such that it overlaps the building and then it is given a movement order so that * + * it will move to an adjacent free cell. * + * * + * INPUT: base -- Pointer to the object that is to exit the building. * + * * + * OUTPUT: Returns the success rating for the exit attempt; * + * 0 = complete failure (refund money please) * + * 1 = temporarily prevented (try again later please) * + * 2 = successful * + * * + * WARNINGS: The building is placed in radio contact with the object. The object is in a * + * teathered condition. This condition will be automatically broken when the * + * object reaches the adjacent square. * + * * + * HISTORY: * + * 11/28/1994 JLB : Created. * + * 04/10/1995 JLB : Handles building production by computer. * + * 06/17/1995 JLB : Handles refinery exit. * + *=============================================================================================*/ +int BuildingClass::Exit_Object(TechnoClass * base) +{ + Validate(); + if (!base) return(0); + + TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); + + /* + ** A unit exiting a building is always considered to be "locked". That means, it + ** will be considered as to have legally entered the visible map domain. + */ + base->IsLocked = true; + + /* + ** Find a good cell to unload the object to. The object, probably a vehicle + ** will drive/walk to the adjacent free cell. + */ + switch (base->What_Am_I()) { + CELL cell; + + case RTTI_AIRCRAFT: + if (!In_Radio_Contact()) { + AircraftClass *air = (AircraftClass *)base; + + air->Altitude = 0; + ScenarioInit++; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + Transmit_Message(RADIO_HELLO, air); + Transmit_Message(RADIO_TETHER); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } else { + AircraftClass *air = (AircraftClass *)base; + + if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { + cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } else { + cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } + ScenarioInit++; + if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { + air->Assign_Destination(::As_Target(Nearby_Location(air))); + air->Assign_Mission(MISSION_MOVE); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + case RTTI_INFANTRY: + case RTTI_UNIT: + switch (Class->Type) { + case STRUCT_REFINERY: + if (base->What_Am_I() == RTTI_UNIT) { + CELL cell = Coord_Cell(Center_Coord()); + UnitClass * unit = (UnitClass *)base; + + cell = Adjacent_Cell(cell, FACING_SW); + ScenarioInit++; + if (unit->Unlimbo(Coord_Add(unit->Coord, 0x00550060L), DIR_SW_X2)) { + unit->PrimaryFacing = DIR_SW_X2; + Transmit_Message(RADIO_HELLO, unit); + Transmit_Message(RADIO_TETHER); + unit->Assign_Mission(MISSION_HARVEST); + unit->Force_Track(DriveClass::OUT_OF_REFINERY, Cell_Coord(cell)); + unit->Set_Speed(128); + } + ScenarioInit--; + } else { + base->Scatter(true); + } + break; + + case STRUCT_AIRSTRIP: + if (Create_Special_Reinforcement(House, &AircraftTypeClass::As_Reference(AIRCRAFT_CARGO), ttype, TMISSION_UNLOAD, As_Target())) { + delete base; + return(2); + } + return(0); + + case STRUCT_WEAP: + ScenarioInit++; + if (base->Unlimbo(Coord_Add(Coord, Class->ExitPoint), DIR_SW)) { +// base->Assign_Mission(MISSION_MOVE); +// base->Assign_Destination(::As_Target(As_Cell(Coord)+MAP_CELL_W*2)); + base->Mark(MARK_UP); + base->Coord = Coord_Add(Coord, Class->ExitPoint); + base->Mark(MARK_DOWN); + Transmit_Message(RADIO_HELLO, base); + Transmit_Message(RADIO_TETHER); + Assign_Mission(MISSION_UNLOAD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + case STRUCT_BARRACKS: + case STRUCT_HAND: + CELL cell; + bool found = false; + + cell = Find_Exit_Cell(base); + if (cell) found = true; + +#ifdef OBSOLETE + CELL const *ptr; + bool found = false; + + ptr = Class->ExitList; + while (*ptr != REFRESH_EOL) { + cell = Coord_Cell(Coord) + *ptr++; + if (base->Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } +#endif + + if (found) { + DirType dir = Direction(cell); + COORDINATE start = Coord_Add(Coord, Class->ExitPoint); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + base->Assign_Destination(::As_Target(cell)); + + /* + ** Establish radio contact so unload coordination can occur. This + ** radio contact should always succeed. + */ + if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + } + break; + + case RTTI_BUILDING: + + if (!House->IsHuman) { + /* + ** Find the next available spot to place this newly created building. If the + ** building could be placed at the desired location, fine. If not, then this + ** routine will return failure. The calling routine will probably abandon this + ** building in preference to building another. + */ + + BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); + // Replaced with RA AI functioality. ST - 7/25/2019 4:14PM +#ifndef USE_RA_AI + if (node) { + if (Flush_For_Placement(base, Coord_Cell(node->Coord))) { + return(1); + } + if (base->Unlimbo(node->Coord)) { + return(2); + } + } +#else + if (GameToPlay == GAME_NORMAL) { + if (node) { + if (Flush_For_Placement(base, Coord_Cell(node->Coord))) { + return(1); + } + if (base->Unlimbo(node->Coord)) { + return(2); + } + } + } else { + + COORDINATE coord = 0; + if (node) { + coord = node->Coord; + } else { + + /* + ** Find a suitable new spot to place. + */ + coord = House->Find_Build_Location((BuildingClass *)base); + } + + if (coord) { + if (Flush_For_Placement(base, Coord_Cell(coord))) { + return(1); + } + if (base->Unlimbo(coord)) { + if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) { + House->BuildStructure = STRUCT_NONE; + } + return(2); + } + } + } +#endif + } + break; + } + + /* + ** Failure to exit the object results in a false return value. + */ + return(0); +} + + + + + +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + Validate(); + + /* + ** Only do this for real human players. ST - 3/22/2019 1:38PM + */ + if (House != PlayerPtr) { + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER || House->IsHuman == false) { + return; + } + } + + bool buildable_via_capture = (IsCaptured && ActLike != House->ActLike) ? true : false; + + if (!IsInLimbo && Is_Discovered_By_Player()) { + switch (Class->ToBuild) { + StructType i; + UnitType u; + InfantryType f; + AircraftType a; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(i, ActLike)) { +// if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) { + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_BUILDINGTYPE, i, House, buildable_via_capture); + } else { + Map.Add(RTTI_BUILDINGTYPE, i, buildable_via_capture); + } +// } + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(u, ActLike)) { +// if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) { + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_UNITTYPE, u, House, buildable_via_capture); + } else { + Map.Add(RTTI_UNITTYPE, u, buildable_via_capture); + } +// } + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(f, ActLike)) { +// if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) { + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); + } else { + Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); + } +// } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(a, ActLike)) { +// if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) { + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_AIRCRAFTTYPE, a, House, buildable_via_capture); + } else { + Map.Add(RTTI_AIRCRAFTTYPE, a, buildable_via_capture); + } +// } + } + } + break; + } + } +} + +#if (0) //ST - 3/22/2019 1:33PM +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + Validate(); + + if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { + switch (Class->ToBuild) { + StructType i; + UnitType u; + InfantryType f; + AircraftType a; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(i, ActLike)) { +// if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_BUILDINGTYPE, i); +// } + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(u, ActLike)) { +// if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_UNITTYPE, u); +// } + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(f, ActLike)) { +// if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_INFANTRYTYPE, f); +// } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(a, ActLike)) { +// if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_AIRCRAFTTYPE, a); +// } + } + } + break; + } + } +} +#endif + +/*********************************************************************************************** + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * * + * This routine is used to perform any fixups necessary when the attached animation has * + * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * + * At that point, normal reload procedures can commence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Fire_Out(void) +{ + Validate(); +// SAM = SAM_READY; +// IsFiring = false; +} + + +/*********************************************************************************************** + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * * + * This routine will handle the power adjustments for the associated house when the * + * building goes into limbo. This means that its power drain or production is subtracted * + * from the house accumulated totals. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the building limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Limbo(void) +{ + Validate(); +// HouseClass *housep; +// RTTIType bld_type; + + if (!IsInLimbo) { + + /* + ** Update the total factory type, assuming this building has a factory. + */ + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories--; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories--; + break; + + case RTTI_UNITTYPE: + House->UnitFactories--; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories--; + break; + + default: + break; + } + + House->IsRecalcNeeded = true; + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:25AM + // + House->Recalc_Center(); +#endif + /* + ** Update the power status of the owner's house. + */ + House->Adjust_Power(-Power_Output()); + House->Adjust_Drain(-Class->Drain); + House->Adjust_Capacity(-(int)Class->Capacity, true); + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + +#ifdef OBSOLETE + /* + ** If this building is building something, the FactoryClass doing the + ** building must be deleted, as well as the object being built. + ** - If this building is controlled by Computer AI, this building's Factory + ** pointer will point to the factory doing the building. + ** - Otherwise, the owner House's Factory indices will indicate what's + ** being built. (The player's sidebar also contains indices to indicate + ** what's being built, but those just echo the house's indices.) + */ + if (Factory) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + housep = HouseClass::As_Pointer(Owner()); + if (housep) { + FactoryClass * factory = 0; + bld_type = RTTI_NONE; + if (housep->AircraftFactory != -1 && Class->ToBuild == RTTI_AIRCRAFTTYPE) { + bld_type = RTTI_AIRCRAFTTYPE; + factory = Factories.Raw_Ptr(housep->AircraftFactory); + } + if (housep->InfantryFactory != -1 && Class->ToBuild==RTTI_INFANTRYTYPE) { + bld_type = RTTI_INFANTRYTYPE; + factory = Factories.Raw_Ptr(housep->InfantryFactory); + } + if (housep->UnitFactory != -1 && Class->ToBuild==RTTI_UNITTYPE) { + bld_type = RTTI_UNITTYPE; + factory = Factories.Raw_Ptr(housep->UnitFactory); + } + if (housep->BuildingFactory != -1 && Class->ToBuild==RTTI_BUILDINGTYPE) { + bld_type = RTTI_BUILDINGTYPE; + factory = Factories.Raw_Ptr(housep->BuildingFactory); + } + if (housep->SpecialFactory != -1 && Class->ToBuild==RTTI_SPECIAL) { + bld_type = RTTI_SPECIAL; + } + + if (bld_type != RTTI_NONE) { + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, housep->Class->House)) { + housep->Abandon_Production(bld_type); + } + IsInLimbo = false; + } + } + } +#endif + + /* + ** This could be a building that builds. If so, then the sidebar may need adjustment. + ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building + ** isn't available. Set it back to false so the rest of the Limbo code works. + ** Otherwise, the sidebar won't properly remove non-available buildables. + */ + if (IsOwnedByPlayer && !ScenarioInit) { + IsInLimbo = true; + Map.Recalc(); + IsInLimbo = false; + } +#ifdef NEVER + if (!House->IsHuman) { + Update_Specials(); + } +#endif + + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * + * * + * This routine is used to determine where a projectile would appear if this building * + * were to fire. The location usually depends on the current rotation setting of the * + * turret or else on the direction of the target that will be fired upon. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with coordinate that the projectile should appear at if the building * + * were to fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Fire_Coord(int ) const +{ + Validate(); + COORDINATE coord = Center_Coord(); // Center of firing building. + + /* + ** Make adjustments to the firing coordinate to account for turret + ** position. This depends on building type and turret facing. + */ + switch (Class->Type) { + default: + case STRUCT_GTOWER: + case STRUCT_ATOWER: + coord = Coord_Move(coord, DIR_N, 0x0030); + if (Target_Legal(TarCom)) { + coord = Coord_Move(coord, ::Direction(coord, As_Coord(TarCom)), 0x0040); + } + break; + case STRUCT_OBELISK: + coord = Coord_Move(coord, DIR_N, 0x00A8); +// coord = Coord_Move(coord, DIR_N, 0x0058); + coord = Coord_Move(coord, DIR_W, 0x0018); + break; + + case STRUCT_SAM: + case STRUCT_TURRET: + coord = Coord_Move(coord, DIR_N, 0x0030); + coord = Coord_Move(coord, PrimaryFacing.Current(), 0x0080); + break; + } + + return(coord); +} + + +/*********************************************************************************************** + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * * + * This routine intercepts the Greatest_Threat function so that it can add the ability * + * to search for ground targets, if this isn't a SAM site. * + * * + * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * + * or THREAT_NORMAL. * + * * + * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * + * returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::Greatest_Threat(ThreatType threat) const +{ + Validate(); + if (*this != STRUCT_SAM) { + threat = threat | (THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES|THREAT_RANGE); + + if (!House->IsHuman) { + threat = threat | THREAT_BUILDINGS; + } +// threat = threat & ~THREAT_AIR; + } else { + threat = threat | THREAT_AREA; + } + + if (Class->Primary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + return(TechnoClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * * + * This routine is called when construction has finished. Typically, this enables * + * new production options for factories. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + * 06/13/1995 JLB : Added helipad. * + *=============================================================================================*/ +void BuildingClass::Grand_Opening(bool captured) +{ + Validate(); + + /* + ** Adjust the owning house according to the power, drain, and Tiberium capacity that + ** this building has. + */ + House->Adjust_Drain(Class->Drain); + House->Adjust_Capacity(Class->Capacity); + House->IsRecalcNeeded = true; + + /* SPECIAL CASE: + ** Refineries get a free harvester. Add a harvester to the reinforcement list + ** at this time. + */ + if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW)); +// if (!Map[cell].Cell_Unit()) { + UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); + if (unit) { + + /* + ** Try to place down the harvesters. If it could not be placed, then try + ** to place it in a nearby location. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + cell = Nearby_Location(unit); + + /* + ** If the harvester could still not be placed, then refund the money + ** to the owner and then bail. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + House->Refund_Money(unit->Class->Cost_Of()); + delete unit; + } + } + } else { + + /* + ** If the harvester could not be created in the first place, then give + ** the full refund price to the owning player. + */ + House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); + } + } +// } + + /* + ** Helicopter pads get a free attack helicopter. + */ + if (*this == STRUCT_HELIPAD && !captured && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + ScenarioInit++; + AircraftClass * air = 0; + if (House->ActLike == HOUSE_GOOD) { + air = new AircraftClass(AIRCRAFT_ORCA, House->Class->House); + } else { + air = new AircraftClass(AIRCRAFT_HELICOPTER, House->Class->House); + } + if (air) { + air->Altitude = 0; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + air->Assign_Mission(MISSION_GUARD); + air->Transmit_Message(RADIO_HELLO, this); + Transmit_Message(RADIO_TETHER); + } + } + ScenarioInit--; + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * * + * This routine will start, stop, or toggle the repair process. When a building repairs, it * + * occurs incrementally over time. * + * * + * INPUT: control -- Determines how to control the repair process. * + * 0: Turns repair process off (if it was on). * + * 1: Turns repair process on (if it was off). * + * -1:Toggles repair process to other state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair(int control) +{ + Validate(); + switch (control) { + case -1: + IsRepairing = (IsRepairing == false); + break; + + case 1: + if (IsRepairing) return; + IsRepairing = true; + break; + + case 0: + if (!IsRepairing) return; + IsRepairing = false; + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + VocType sound = VOC_NONE; + if (IsRepairing) { + if (Strength == Class->MaxStrength) { + sound = VOC_SCOLD; + } else { + sound = VOC_BUTTON; + Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object + IsWrenchVisible = true; + } + } else { + sound = VOC_BUTTON; + } + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Sound_Effect(sound, Coord); + } +} + + +/*********************************************************************************************** + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * * + * This routine will initiate or stop the sell back process for a building. It is called * + * when the player clicks on a building when the sell mode is active. * + * * + * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * + * -1 = toggle deconstruction state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Sell_Back(int control) +{ + Validate(); + if (Class->Get_Buildup_Data()) { + bool decon = false; + switch (control) { + case -1: + decon = (Mission != MISSION_DECONSTRUCTION); + break; + + case 1: + if (Mission == MISSION_DECONSTRUCTION) return; + decon = true; + break; + + case 0: + if (Mission != MISSION_DECONSTRUCTION) return; + decon = false; + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + if (decon) { +// Transmit_Message(RADIO_RUN_AWAY); +// Transmit_Message(RADIO_OVER_OUT); + Assign_Mission(MISSION_DECONSTRUCTION); + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object + } + } + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Sound_Effect(VOC_BUTTON); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * * + * This routine will determine what action to perform if the mouse was clicked on the * + * object specified. This determination is used to control the mouse imagery and the * + * function process when the mouse button is pressed. * + * * + * INPUT: object -- Pointer to the object that, if clicked on, will control what action * + * is to be performed. * + * * + * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * + * object specified while the building is currently selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = TechnoClass::What_Action(object); + + if (action == ACTION_SELF) { + if (Class->IsFactory && PlayerPtr == House) { + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + if (House->AircraftFactories < 2) { + action = ACTION_NONE; + } + else { + action = ACTION_TOGGLE_PRIMARY; + } + break; + + case RTTI_INFANTRYTYPE: + if (House->InfantryFactories < 2) { + action = ACTION_NONE; + } + else { + action = ACTION_TOGGLE_PRIMARY; + } + break; + + case RTTI_UNITTYPE: + if (House->UnitFactories < 2) { + action = ACTION_NONE; + } + else { + action = ACTION_TOGGLE_PRIMARY; + } + break; + + default: + action = ACTION_NONE; + break; + } + + } else { + action = ACTION_NONE; + } + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && *this == STRUCT_SAM) { + action = ACTION_NONE; + } + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines what action will occur. * + * * + * This routine examines the cell specified and returns with the action that will be * + * performed if that cell were clicked upon while the building is selected. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * + * on this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = TechnoClass::What_Action(cell); + + if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Special.IsMCVDeploy)) { + action = ACTION_NONE; + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && *this == STRUCT_SAM) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * * + * This routine will start the building animating. This animation will loop indefinately * + * until explicitly stopped. * + * * + * INPUT: bstate -- The animation state to initiate. * + * * + * OUTPUT: none * + * * + * WARNINGS: The buliding graphic state will reflect the first stage of this animation the * + * very next time it is rendered. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/02/1995 JLB : Uses normalize animation rate where applicable. * + *=============================================================================================*/ +void BuildingClass::Begin_Mode(BStateType bstate) +{ + Validate(); + QueueBState = bstate; + if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { + BState = bstate; + QueueBState = BSTATE_NONE; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + int rate = ctrl->Rate; + if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { + rate = Options.Normalize_Delay(rate); + } + Set_Rate(rate); + Set_Stage(ctrl->Start); + } +} + + +/*********************************************************************************************** + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * * + * This is the basic scenario initialization of building function. It * + * is called when reading the scenario startup INI file and it handles * + * creation of all specified buildings. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Read_INI(char *buffer) +{ + BuildingClass *b; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType bhouse; // Building house. + StructType classid; // Building type. + int len; // Size of data in buffer. + CELL cell; // Cell of building. + char buf[128]; + char *trigname; // building's trigger's name + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /* + ** Read the entire building INI section into HIDBUF + */ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + + /* + ** Get a building entry. + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** 1st token: house name. + */ + bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + + /* + ** 2nd token: building name. + */ + classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); + + if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { + int strength; + DirType facing; + + /* + ** 3rd token: strength. + */ + strength = atoi(strtok(NULL, ",")); + + /* + ** 4th token: cell #. + */ + cell = atoi(strtok(NULL, ",")); + + /* + ** 5th token: facing. + */ + facing = (DirType)atoi(strtok(NULL, ",")); + + /* + ** 6th token: triggername (can be NULL). + */ + trigname = strtok(NULL,","); + + if (HouseClass::As_Pointer(bhouse) != NULL) { + b = new BuildingClass(classid, bhouse); + if (b) { + if (b->Unlimbo(Cell_Coord(cell), facing)) { + strength = MIN(strength, 0x100); + strength = Fixed_To_Cardinal(b->Class->MaxStrength, strength); + b->Strength = strength; + b->IsALemon = false; + b->Trigger = TriggerClass::As_Pointer(trigname); + if (b->Trigger) { + b->Trigger->AttachCount++; + } + } else { + + /* + ** If the building could not be unlimboed on the map, then this indicates + ** a serious error. Delete the building. + */ + delete b; + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * BuildingClass::Write_INI -- Writes all building data to an INI file. * + * * + * This routine is used to write the buildings into an INI file. It is necessary for the * + * scenario editor save game option. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- The buffer that holds the INI data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing building data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the data out. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building; + + building = Buildings.Ptr(index); + if (!building->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s", + building->House->Class->IniName, + building->Class->IniName, + building->Health_Ratio(), + Coord_Cell(building->Coord), + building->PrimaryFacing.Current(), + building->Trigger ? building->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::As_Target -- Convert the building into a target value. * + * * + * Use this routine to take the building and convert it into a target number. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the target number for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_BUILDING, Buildings.ID(this))); +} + + +/*********************************************************************************************** + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * * + * This routine is used to set the center coordinate for this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate for the center location for the building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Center_Coord(void) const +{ + Validate(); + return(Coord_Add(Coord, CenterOffset[Class->Size])); +} + +static bool Occupy_List_Contains(const short * list, short cell) +{ + while (*list != REFRESH_EOL) { + if (*list == cell) { + return true; + } + list++; + } + return false; +} + +COORDINATE BuildingClass::Target_Coord(void) const +{ + static constexpr int _num_facings = 3; + static const FacingType _facings[_num_facings] = { FACING_S, FACING_E, FACING_SE }; + static const COORDINATE _offsets[_num_facings] = { 0x00800000, 0x00000080, 0x00800080 }; + + Validate(); + COORDINATE offset = CenterOffset[Class->Size]; + + const short * list = Occupy_List(); + CELL cell = Coord_Cell(offset); + if (!Occupy_List_Contains(list, cell)) { + for (int i = 0; i < _num_facings; ++i) { + CELL adjcell = Adjacent_Cell(cell, _facings[i]); + if (Occupy_List_Contains(list, adjcell)) { + offset = Coord_Add(offset, _offsets[i]) & 0xFF80FF80; + break; + } + } + } + return(Coord_Add(Coord, offset)); +} + + +COORDINATE BuildingClass::Docking_Coord(void) const +{ + Validate(); + if (*this == STRUCT_HELIPAD) { + return(Coord_Add(Coord, XYP_COORD(24, 18))); + } + if (*this == STRUCT_AIRSTRIP) { + return(Coord_Add(Coord, XYP_COORD(18, 30))); + } + return(TechnoClass::Docking_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * * + * Use this routine to see if the building can fire its weapon. * + * * + * * + * INPUT: target -- The target that firing upon is desired. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * + * returned. Other cases will result in appropriate fire code value that indicates * + * why firing is not allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + FireErrorType canfire = TechnoClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsTurretEquipped) { + /* + ** If the turret is rotating then firing must be delayed. + */ + if (PrimaryFacing.Is_Rotating()) { + return(FIRE_ROTATING); + } + + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) > 8) { + return(FIRE_FACING); + } + } + + /* + ** Advanced guard towers need power to fire. + */ + if (*this == STRUCT_ATOWER && House->Power_Fraction() < 0x0100) { + return(FIRE_BUSY); + } + + /* + ** If an obelisk can fire, check the state of charge. If it isn't charging + ** up, start it charging up and return FIRE_BUSY. If it is charging but + ** isn't done yet, return FIRE_BUSY. If it's done charging, stop the + ** charging process, clear the stage timer, and return FIRE_OK. + */ + if (Class->Primary == WEAPON_OBELISK_LASER && !IsCharged) { + return(FIRE_BUSY); + } + } + return(canfire); +} + + +/*********************************************************************************************** + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * * + * This routine will change the primary factory state of this building. The primary * + * factory is the one that units will be produced from (by default). * + * * + * INPUT: none * + * * + * OUTPUT: Is this building NOW the primary factory? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Toggle_Primary(void) +{ + Validate(); + if (IsLeader) { + IsLeader = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { + building->IsLeader = false; + } + } + IsLeader = true; + if (House == PlayerPtr) { + Speak(VOX_PRIMARY_SELECTED); + } + } + Mark(MARK_CHANGE); + return(IsLeader); +} + + +/*********************************************************************************************** + * BuildingClass::Captured -- Captures the building. * + * * + * This routine will change the owner of the building. It handles updating any related * + * game systems as a result. Factories are the most prone to have great game related * + * consequences when captured. This could also affect the sidebar and building ownership. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the capture attempt successful? * + * * + * WARNINGS: Capturing could fail if the house is already owned by the one specified or * + * the building isn't allowed to be captured. * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * + *=============================================================================================*/ +bool BuildingClass::Captured(HouseClass * newowner) +{ + Validate(); + if (Class->IsCaptureable && newowner != House) { + switch (Owner()) { + case HOUSE_GOOD: + Speak(VOX_GDI_CAPTURED); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_CAPTURED); + break; + } + + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + if (newowner->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + if (newowner->ActLike != House->ActLike) { + On_Achievement_Event(newowner, "OPPOSING_BUILDING_CAPTURED", object_type->IniName); + } else { + On_Achievement_Event(newowner, "BUILDING_CAPTURED", object_type->IniName); + } + } + } + + /* + ** Add this building to the list of buildings captured this game. For internet stats purposes + */ + if (GameToPlay == GAME_INTERNET){ + newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); + } + + House->Adjust_Power(-Power_Output()); + LastStrength = 0; + House->Adjust_Drain(-Class->Drain); + int booty = House->Adjust_Capacity(-(int)Class->Capacity, true); + + /* + ** If there is something loaded, then it gets captured as well. + */ + TechnoClass * tech = Attached_Object(); + if (tech) tech->Captured(newowner); + + /* + ** If something isn't technically attached, but is sitting on this + ** building for another reason (e.g., helicopter on helipad), then it + ** gets captured as well. + */ + tech = Contact_With_Whom(); + if (tech) { + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && ::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040) { + tech->Captured(newowner); + } else { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } + } + + /* + ** Decrement the factory counter for the original owner. + */ + switch (Class->ToBuild) { + case RTTI_UNITTYPE: + House->UnitFactories--; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories--; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories--; + break; + + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories--; + break; + + default: + break; + } + +#ifdef NEVER + if (IsOwnedByPlayer && !ScenarioInit) { + Map.Recalc(); + } + if (!House->IsHuman) { + Update_Specials(); + } +#endif + + /* + ** Flag that both owners now need to update their buildable lists. + */ + House->IsRecalcNeeded = true; + newowner->IsRecalcNeeded = true; + + HouseClass * oldowner = House; // Added for RA AI in TD. ST - 7/26/2019 9:25AM + + IsCaptured = true; + TechnoClass::Captured(newowner); + + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:25AM + // + oldowner->Recalc_Center(); + House->Recalc_Center(); +#endif + + SmudgeType bib; + CELL cell = Coord_Cell(Coord); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); + } + + /* + ** Increment the factory count for the new owner. + */ + switch (Class->ToBuild) { + case RTTI_UNITTYPE: + House->UnitFactories++; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories++; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories++; + break; + + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories++; + break; + + default: + break; + } + + IsRepairing = false; + Grand_Opening(true); + House->Harvested(booty); + + Mark(MARK_CHANGE); + + /* + ** Perform a look operation when catpured if it was the player + ** that performed the capture. + */ + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) { + Look(false); + } else { + if (House == PlayerPtr) { + Look(false); + } + } + + if (oldowner) { + oldowner->Check_Pertinent_Structures(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * * + * The coordinate value returned from this function should be used for sorting purposes. * + * It has special offset adjustment applied so that vehicles don't overlap (as much). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * + *=============================================================================================*/ +COORDINATE BuildingClass::Sort_Y(void) const +{ + Validate(); + if (*this == STRUCT_REPAIR) { + return(Coord); + } + if (*this == STRUCT_HELIPAD) { + return(Center_Coord()); + } + if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { + return(Center_Coord()); + } + if (*this == STRUCT_REFINERY) { + return(Center_Coord()); + } + return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * * + * This routine will determine if the building can be placed down at the location * + * specified. * + * * + * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * + * of the building if it were to be placed down. * + * * + * OUTPUT: Returns with the move legality value for placement at the location specified. This * + * will either be MOVE_OK or MOVE_NO. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + Validate(); + + if (*this == STRUCT_CONST && IsDown) { + return(Map[cell].Is_Generally_Clear() ? MOVE_OK : MOVE_NO); + } + + return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * * + * Determines if the player can sell this building. Selling is possible if the building * + * is not currently in construction or deconstruction animation. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building be demolished at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * + * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * + *=============================================================================================*/ +bool BuildingClass::Can_Demolish(void) const +{ + Validate(); + // !Mission != sounds a bit fishy. ST - 2018 + //if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && !Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (Class->IsUnsellable) return(false); + if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); + return(true); + } + return(false); +} + + +bool BuildingClass::Can_Demolish_Unit(void) const +{ + return(*this == STRUCT_REPAIR && In_Radio_Contact() && Distance(Contact_With_Whom()) < 0x0080); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * * + * Buildings that can attack are given this mission. They will wait until a suitable target * + * comes within range and then launch into the attack mission. Buildings that have no * + * weaponry will just sit in this routine forever. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine will be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Guard(void) +{ + Validate(); + /* + ** If this building has a weapon, then search for a target to attack. When + ** a target is found, switch into attack mode to deal with the threat. + */ + if (Class->Primary != WEAPON_NONE) { + + /* + ** Weapon equipped buildings are ALWAYS ready to launch into another mission if + ** they are sitting around in guard mode. + */ + IsReadyToCommence = true; + + /* + ** If there is no target available, then search for one. + */ + if (!Target_Legal(TarCom)) { + ThreatType threat = THREAT_NORMAL; + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** There is a valid target. Switch into attack mode right away. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + } else { + + /* + ** This is the very simple state machine that basically does + ** nothing. This is the mode that non weapon equipped buildings + ** are normally in. + */ + enum { + INITIAL_ENTRY, + IDLE + }; + switch (Status) { + case INITIAL_ENTRY: + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + case IDLE: + /* + ** Special case to break out of guard mode if this is a repair + ** facility and there is a customer waiting at the grease pit. + */ + if (*this == STRUCT_REPAIR && + In_Radio_Contact() && + Contact_With_Whom()->Is_Techno() && + ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && + Distance(Contact_With_Whom()) < 0x0040 && + Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + + Assign_Mission(MISSION_REPAIR); + return(1); + } + break; + } + return(TICKS_PER_SECOND*5); + } + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Construction -- Handles mission construction. * + * * + * This routine will handle mission construction. When this mission is complete, the * + * building will begin normal operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Construction(void) +{ + Validate(); + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_CONSTRUCTION); + Transmit_Message(RADIO_BUILDING); + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Sound_Effect(VOC_CONSTRUCTION, Coord); + } + Status = DURING; + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** When construction is complete, then transmit this + ** to the construction yard so that it can stop its + ** construction animation. + */ + Transmit_Message(RADIO_COMPLETE); // "I'm finished." + Transmit_Message(RADIO_OVER_OUT); // "You're free." + Begin_Mode(BSTATE_IDLE); + // Construction yard already called this on reveal in normal game mode, so don't do twice + if (*this != STRUCT_CONST || GameToPlay != GAME_NORMAL) { + Grand_Opening(); + } + Assign_Mission(MISSION_GUARD); + PrimaryFacing = Class->StartFace; + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * * + * This state machine is only used when the building is deconstructing as a result of * + * selling. When this mission is finished, the building will no longer exist. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 08/13/1995 JLB : Enable selling of units on a repair bay. * + * 08/20/1995 JLB : Scatters infantry from scattered starting points. * + *=============================================================================================*/ +int BuildingClass::Mission_Deconstruction(void) +{ + Validate(); + /* + ** Always force repair off. + */ + Repair(0); + + enum { + INITIAL, + HOLDING, + DURING + }; + switch (Status) { + case INITIAL: + + /* + ** Special check for the repair bay which has the ability to sell + ** whatever is on it. If there is something on the repair bay, then + ** it will be sold. If there is nothing on the repair bay, then + ** the repair bay itself will be sold. + */ + if (Can_Demolish_Unit() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * tech = Contact_With_Whom(); + Transmit_Message(RADIO_OVER_OUT); + tech->Sell_Back(1); +// House->Refund_Money(tech->Refund_Amount()); +// tech->Limbo(); + IsReadyToCommence = true; + Assign_Mission(MISSION_GUARD); + return(1); + } + + IsReadyToCommence = false; + Transmit_Message(RADIO_RUN_AWAY); + Status = HOLDING; + break; + + case HOLDING: + if (!IsTethered) { + + /* + ** The crew will evacuate from the building. The number of crew + ** members leaving is equal to the unrecovered cost of the building + ** divided by 100 (the typical cost of a minigunner infantryman). + */ + if (!Target_Legal(ArchiveTarget) || !Special.IsMCVDeploy || *this != STRUCT_CONST) { + int divisor = 200; + if (IsCaptured) divisor *= 2; + int count = (Class->Raw_Cost()+(divisor-1)) / divisor; + bool engine = false; + count = Bound(count, 1, 5); + + while (count) { + + /* + ** Ensure that the player only gets ONE engineer and not from a captured + ** construction yard. + */ + InfantryType typ = Crew_Type(); + while (typ == INFANTRY_E7 && engine) { + typ = Crew_Type(); + } + if (typ == INFANTRY_E7) engine = true; + + InfantryClass * infantry = new InfantryClass(typ, House->Class->House); + if (infantry) { + ScenarioInit++; + COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); + coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, false); + + if (infantry->Unlimbo(coord, DIR_N)) { + if (infantry->Class->IsNominal) infantry->IsTechnician = true; + ScenarioInit--; + infantry->Scatter(0, true); + ScenarioInit++; + infantry->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete infantry; + } + ScenarioInit--; + } + count--; + } + } + + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Sound_Effect(VOC_CASHTURN, Coord); + } + + /* + ** Destroy all attached objects. ST - 4/24/2020 9:38PM + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + Transmit_Message(RADIO_OVER_OUT); + Status = DURING; + Begin_Mode(BSTATE_CONSTRUCTION); + IsReadyToCommence = false; + break; + } + Transmit_Message(RADIO_RUN_AWAY); + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** Construction yards that deconstruct, really just revert back + ** to an MCV. + */ + if (Target_Legal(ArchiveTarget) && Special.IsMCVDeploy && *this == STRUCT_CONST && House->IsHuman) { + ScenarioInit++; + UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); + ScenarioInit--; + if (unit) { + + /* + ** Unlimbo the MCV onto the map. The MCV should start in the same + ** health condition that the construction yard was in. + */ + int ratio = Health_Ratio(); + int money = Refund_Amount(); + TARGET arch = ArchiveTarget; + COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE)); + + Delete_This(); + + if (unit->Unlimbo(place, DIR_SW)) { + unit->Strength = Fixed_To_Cardinal(unit->Class_Of().MaxStrength, ratio); + + /* + ** Lift the move destination from the building and assign + ** it to the unit. + */ + if (Target_Legal(arch)) { + unit->Assign_Destination(arch); + unit->Assign_Mission(MISSION_MOVE); + } + } else { + + /* + ** If, for some strange reason, the MCV could not be placed on the + ** map, then give the player some money to compensate. + */ + House->Refund_Money(money); + } + } else { + House->Refund_Money(Refund_Amount()); + Delete_This(); + } + + } else { + + /* + ** A sold building still counts as a kill, but it just isn't directly + ** attributed to the enemy. + */ + WhoLastHurtMe = HOUSE_NONE; + Record_The_Kill(NULL); + + /* + ** The player gets part of the money back for the sell. + */ + House->Refund_Money(Refund_Amount()); + Limbo(); + + if (House) { + House->Check_Pertinent_Structures(); + } + + /* + ** Finally, delete the building from the game. + */ + Delete_This(); + } + House->IsRecalcNeeded = true; + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * * + * Buildings that can attack are processed by this attack mission state machine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Attack(void) +{ + Validate(); + if (*this == STRUCT_SAM) { + switch (Status) { + + /* + ** The launcher is underground and awaiting the acquisition of + ** a target. + */ + case SAM_UNDERGROUND: + IsReadyToCommence = true; + if (Target_Legal(TarCom)) { + Set_Rate(2); + Set_Stage(0); + Status = SAM_RISING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + /* + ** The launcher is rising into the ready position so that it + ** may rotate to face the target. + */ + case SAM_RISING: + if (Fetch_Stage() == 15) { + Set_Rate(0); + PrimaryFacing = DIR_N; + if (!Target_Legal(TarCom)) { + Status = SAM_LOWERING; + } else { + Status = SAM_READY; + } + } + return(1); + + /* + ** This is the target tracking state of the launcher. It will rotate + ** to face the current TarCom of the launcher. + */ + case SAM_READY: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING; + } + } + } + return(1); + + /* + ** The launcher is in the process of firing. + */ + case SAM_FIRING: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Status = SAM_READY2; + return(1); + } + } + } + } + return(1); + + case SAM_READY2: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING2; + } + } + } + return(1); + + case SAM_FIRING2: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY2; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND*3); + } + } + } + } + return(1); + + /* + ** Rotating to face north in preparation for lowering to reload. + */ + case SAM_LOCKING: + if (!PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing == DIR_N) { + Set_Rate(2); + Set_Stage(48); + Status = SAM_LOWERING; + } else { + PrimaryFacing.Set_Desired(DIR_N); + } + } + return(1); + + /* + ** Lowering into the ground in order to reload. + */ + case SAM_LOWERING: + if (Fetch_Stage() >= 63) { + Set_Rate(0); + Set_Stage(0); + Status = SAM_UNDERGROUND; + return(TICKS_PER_SECOND); + } else { + if (Fetch_Rate() == 0) { + Set_Rate(2); + } + } + return(1); + + default: + break; + } + + } else { + IsReadyToCommence = true; + switch (Can_Fire(TarCom, 0)) { + case FIRE_ILLEGAL: + case FIRE_CANT: + case FIRE_RANGE: + case FIRE_AMMO: + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + break; + + case FIRE_FACING: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(2); + + case FIRE_REARM: + case FIRE_BUSY: + return(1); + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + return(1); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * * + * This state machine handles the refinery when it unloads the harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Harvest(void) +{ + Validate(); + enum { + INITIAL, // Dock the Tiberium canister. + WAIT_FOR_DOCK, // Waiting for docking to complete. + MIDDLE, // Offload "bails" of tiberium. + WAIT_FOR_UNDOCK, // Waiting for undocking to complete. + EXITING // Cause the harvester to drive away. + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = WAIT_FOR_DOCK; + break; + + case WAIT_FOR_DOCK: + if (IsReadyToCommence) { + IsReadyToCommence = false; + Status = MIDDLE; + Begin_Mode(BSTATE_AUX1); + } + break; + + case MIDDLE: + if (IsReadyToCommence) { + IsReadyToCommence = false; + + /* + ** Force any bib squaters to scatter. + */ + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_SW)].Incoming(0, true); + Special.IsScatter = old; + + FootClass * techno = Attached_Object(); + if (techno) { + int bail = techno->Offload_Tiberium_Bail(); + + if (bail) { + House->Harvested(bail); + if (techno->Tiberium_Load()) { + return(1); + } + } + } + Begin_Mode(BSTATE_AUX2); + Status = WAIT_FOR_UNDOCK; + } + break; + + case WAIT_FOR_UNDOCK: + if (IsReadyToCommence) { + + /* + ** Detach harvester and go back into idle state. + */ + Exit_Object(Detach_Object()); + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * * + * This state machine is used when the building is active in some sort of repair or * + * construction mode. The construction yard will animate. The repair facility will repair * + * anything that it docked on it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 06/25/1995 JLB : Handles repair facility * + * 07/29/1995 JLB : Repair rate is controlled by power rating. * + *=============================================================================================*/ +int BuildingClass::Mission_Repair(void) +{ + Validate(); + if (*this == STRUCT_CONST) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = DURING; + break; + + case DURING: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); + } + + if (*this == STRUCT_REPAIR) { + enum { + INITIAL, + IDLE, + DURING + }; + switch (Status) { + case INITIAL: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Assign_Mission(MISSION_GUARD); + return(1); + } + IsReadyToCommence = false; + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0010) { + Status = IDLE; + return(TICKS_PER_SECOND/4); + } + break; + + case IDLE: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + if (Contact_With_Whom()->Health_Ratio() < 0x0100 && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { + Status = DURING; + Begin_Mode(BSTATE_ACTIVE); + IsReadyToCommence = false; + } else { + if (!House->IsHuman) { + Transmit_Message(RADIO_RUN_AWAY); + } + } +// } else { +// Assign_Mission(MISSION_GUARD); +// return(1); + } + break; + + case DURING: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + return(1); + } + if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + IsReadyToCommence = false; + long param = Health_Ratio(); + if (Transmit_Message(RADIO_REPAIR, param) != RADIO_ROGER) { +#ifdef OBSOLETE + if (House->Available_Money() < 10) { + Transmit_Message(RADIO_RUN_AWAY); + } +#endif + Begin_Mode(BSTATE_IDLE); + Status = IDLE; +#ifdef OBSOLETE + } else { + int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); + time = (TICKS_PER_SECOND + (TICKS_PER_SECOND/2)) - time; + return(time); +#endif + } + } + break; + } + return(TICKS_PER_SECOND/2); + } + + if (*this == STRUCT_HELIPAD) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { + Begin_Mode(BSTATE_ACTIVE); + Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); + Status = DURING; + return(1); + } + Assign_Mission(MISSION_GUARD); + break; + + case DURING: + if (IsReadyToCommence) { + if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { + Assign_Mission(MISSION_GUARD); + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + return(1); + } else { + int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); + time = (TICKS_PER_SECOND*3) - time; + IsReadyToCommence = false; + return(time); + } + } + break; + } + return(3); + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * * + * This handles the Temple of Nod launching its nuclear missile. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int BuildingClass::Mission_Missile(void) +{ + Validate(); + enum { + INITIAL, + DOOR_OPENING, + LAUNCH_UP, + LAUNCH_DOWN, + DONE_LAUNCH + }; + + if (*this == STRUCT_TEMPLE) { + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building. + */ + case INITIAL: + IsReadyToCommence = false; + Begin_Mode(BSTATE_ACTIVE); + Status = DOOR_OPENING; + return(1); + + /* + ** This polls for the case when the door is actually open and + ** then kicks off the missile smoke. + */ + case DOOR_OPENING: + if (IsReadyToCommence) { + Begin_Mode(BSTATE_IDLE); + new AnimClass(ANIM_ATOM_DOOR, Center_Coord()); + Status = LAUNCH_UP; + return(14); + } + return(1); + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + BulletClass *bullet = new BulletClass(BULLET_NUKE_UP); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)1, 0x1A0); + bullet->Assign_Target(TARGET_NONE); + bullet->Payback = NULL; + bullet->Strength = 1; + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } else { + bullet->PrimaryFacing.Set_Current(DIR_N); + Sound_Effect(VOC_NUKE_FIRE, launch); + + // MBL 03.27.2020 This is never getting triggered for any player in multiplayer, so removing the check (https://jaas.ea.com/browse/TDRA-5458) + // if (House == PlayerPtr) + { + Speak(VOX_NUKE_LAUNCHED); // "NUKLNCH1" - "Nuclear Weapon Launched" + } + } + } + + if (bullet) { + Status = LAUNCH_DOWN; + return(8 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is in the air, this handles waiting for + ** the missile to be off the screen and then launching one down + ** over the target. + */ + case LAUNCH_DOWN: + { + BulletClass *bullet = new BulletClass(BULLET_NUKE_DOWN); + if (bullet) { +// Theme.Queue_Song(THEME_NONE); + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), 1)); + bullet->Assign_Target(::As_Target(House->NukeDest)); + + // MBL 05.20.2020 + // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked + // Per https://jaas.ea.com/browse/TDRA-6610 + // + // bullet->Payback = NULL; + bullet->Payback = this; + + bullet->Strength = 1; + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } else { + bullet->PrimaryFacing.Set_Current(DIR_S); + } + Speak(VOX_INCOMING_NUKE); // "Nuclear Warhead Approaching" - "NUKE1" + Sound_Effect(VOC_NUKE_FIRE, start); + } + if (bullet) { + Status = DONE_LAUNCH; + return(7 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is done launching this handles allowing + ** the building to sit there with its door open. + */ + case DONE_LAUNCH: + Assign_Mission(MISSION_GUARD); + return(60); + } + } + return(60); +} + + +/*********************************************************************************************** + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * * + * This routine will reveal the building to the specified house. It will handle updating * + * the sidebar for player owned buildings. A player owned building that hasn't been * + * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * + * abilities even though it exists on the map for all other purposes. * + * * + * INPUT: house -- The house that this building is being revealed to. * + * * + * OUTPUT: Was this building revealed by this procedure? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Revealed(HouseClass * house) +{ + Validate(); + if (TechnoClass::Revealed(house)) { + + if (!ScenarioInit) { + House->JustBuilt = Class->Type; + } + House->IsRecalcNeeded = true; + + /* + ** Perform any grand opening here so that in the scenarios where a player + ** owned house is not yet revealed, it won't be reflected in the sidebar + ** selection icons. + */ + /* + ** Making a change here to avoid Grand_Opening happening multiple times in MP/skirmish. ST - 7/26/2019 11:26AM + */ + //if (!In_Radio_Contact() && (house == House || GameToPlay != GAME_NORMAL) && Mission != MISSION_CONSTRUCTION) { + if (!In_Radio_Contact() && (house == House && GameToPlay == GAME_NORMAL) && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * * + * This routine is called when the exact mode of the building isn't known. By examining * + * the building's condition, this routine will assign an appropriate mission. * + * * + * INPUT: initial -- This this being called during scenario init? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Enter_Idle_Mode(bool initial) +{ + Validate(); + /* + ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then + ** this must be an initial building. Start such buildings in idle state. For other buildings + ** it indicates that it is being placed during game play and thus it must start in + ** the "construction" mission. + */ + MissionType mission = MISSION_GUARD; + if (!initial || ScenarioInit || Debug_Map) { + Begin_Mode(BSTATE_IDLE); + mission = MISSION_GUARD; + } else { + Begin_Mode(BSTATE_CONSTRUCTION); + mission = MISSION_CONSTRUCTION; + } + Assign_Mission(mission); +} + + +/*************************************************************************** + * BuildingClass::Update_Specials -- removes computer specials * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1995 PWG : Created. * + *=========================================================================*/ +void BuildingClass::Update_Specials(void) +{ + Validate(); +} + + +/*********************************************************************************************** + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * * + * This routine will determine the number of pips that should be filled in when rendering * + * the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of pips to display as filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Pip_Count(void) const +{ + Validate(); + return(Fixed_To_Cardinal(Class->Max_Pips(), House->Tiberium_Fraction())); +} + + +/*********************************************************************************************** + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * * + * This routine is called when the building is destroyed by "unnatural" means. Typically * + * as a result of combat. If the building is known to the player, then it should be * + * announced. * + * * + * INPUT: source -- The object most directly responsible for the building's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Death_Announcement(TechnoClass const * ) const +{ + Validate(); + //Changed for multiplayer ST - 3/13/2019 5:31PM + if (Is_Discovered_By_Player() || Is_Owned_By_Player()) { + //if (IsDiscoveredByPlayer || IsOwnedByPlayer) { + if (House != PlayerPtr && GameToPlay != GAME_NORMAL) { + if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_STRUCTURE); + } else { + if (House == PlayerPtr || Options.IsDeathAnnounce) { + if (!Options.IsDeathAnnounce) { + Speak(VOX_STRUCTURE_LOST); + } else { + switch (House->ActLike) { + case HOUSE_GOOD: + Speak(VOX_GDI_STRUCTURE); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_STRUCTURE); + break; + + default: + break; + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * * + * This routine will return with the default direction to use when firing from this * + * building. This is the facing of the turret except for the case of non-turret equipped * + * buildings that have a weapon (e.g., guard tower). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Fire_Direction(void) const +{ + Validate(); + if (Class->IsTurretEquipped) { + return(PrimaryFacing.Current()); + } + return(Direction(TarCom)); +} + + +/*********************************************************************************************** + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * * + * Use this routine to fetch the remap table to use. This override function is needed * + * because the default remap table for techno objects presumes the object is a unit. * + * Buildings aren't units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the proper remap table to use for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Remap_Table(void) +{ + Validate(); + return(House->Remap_Table(IsBlushing, false)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * * + * This is the unload mission for a building. This really only applies to the weapon's * + * factory, since it needs the sophistication of an unload mission due to the door * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Unload(void) +{ + Validate(); + if (*this == STRUCT_WEAP) { + COORDINATE coord = Adjacent_Cell(Center_Coord(), FACING_SW); + CELL cell = Coord_Cell(coord); + CellClass * cellptr = &Map[cell]; + enum { + INITIAL, + CLEAR_BIB, + OPEN, + LEAVE, + CLOSE + }; + UnitClass * unit; + switch (Status) { + case INITIAL: + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_GUARD); + unit->Commence(); + } + Open_Door(2, 11); + Status = CLEAR_BIB; + break; + + /* + ** Now that the occupants can peek out the door, they will tell + ** everyone that could be blocking the way, that they should + ** scatter away. + */ + case CLEAR_BIB: + unit = (UnitClass *)Contact_With_Whom(); + if (cellptr->Cell_Unit() || cellptr->Cell_Infantry()) { + cellptr->Incoming(0, true, true); + + /* + ** Scatter everything around the weapon's factory door. + */ + for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) { + CellClass * cptr = &cellptr->Adjacent_Cell(f); + UnitClass * cellunit = cptr->Cell_Unit(); + if ((cellunit && cellunit != unit) || cptr->Cell_Infantry()) { + cptr->Incoming(coord, true, true); + } + } + } else { + Status = OPEN; + } + break; + + case OPEN: + if (Is_Door_Open()) { + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_MOVE); + unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Center_Coord(), FACING_SW)); + unit->Set_Speed(128); + Status = LEAVE; + } else { + Close_Door(2, 11); + Status = CLOSE; + } + } + break; + + case LEAVE: + if (!IsTethered) { + Close_Door(2, 11); + Status = CLOSE; + } + break; + + case CLOSE: + if (Is_Door_Closed()) { + Enter_Idle_Mode(); + } + break; + } + return(TICKS_PER_SECOND/2); + } + Assign_Mission(MISSION_GUARD); + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * * + * This routine will return the current power output for this building. The power output * + * is adjusted according to the damage level of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current power output for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Power_Output(void) const +{ + Validate(); + if (Class->Power) { + return(Fixed_To_Cardinal(Class->Power, Cardinal_To_Fixed(Class->MaxStrength, LastStrength))); + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Detach -- Handles target removal from the game system. * + * * + * This routine is called when the specified target is about to be removed from the game * + * system. * + * * + * INPUT: target -- The target to be removed from this building's targeting computer. * + * * + * all -- Is the target about to be completely eliminated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach(TARGET target, bool all) +{ + Validate(); + TechnoClass::Detach(target, all); + if (target == WhomToRepay) { + WhomToRepay = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * + * * + * This routine will return the amount of money to be refunded to the building's owner * + * if the building is sold. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the refund amount available for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Refund_Amount(void) const +{ + Validate(); + int cost = TechnoClass::Refund_Amount(); + + /* + ** Add in any Tiberium that was stored within the building. + */ + if (IsV107 && Class->Capacity > 0) { + cost += Fixed_To_Cardinal(Class->Capacity, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); + } + return(cost); +} + + +/*********************************************************************************************** + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * * + * When selling very cheap buildings (such as the silo), a technician will pop out since * + * generating minigunners would be overkill -- the player could use this loophole to * + * gain an advantage. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type that this building will generate as a survivor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType BuildingClass::Crew_Type(void) const +{ + Validate(); + switch (Class->Type) { + case STRUCT_STORAGE: + if (Random_Pick(0, 1) == 0) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + + case STRUCT_CONST: + if (!IsCaptured && House->IsHuman && Random_Pick(0, 3) == 0) { + return(INFANTRY_E7); + } + break; + + default: + break; + } + return(TechnoClass::Crew_Type()); +} + + +/*********************************************************************************************** + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * * + * When this routine is called, it indicates that the building is about to be destroyed * + * or captured. In such a case any production it may be doing, must be abandoned. * + * * + * INPUT: all -- Is the object about the be completely destroyed? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach_All(bool all) +{ + Validate(); + /* + ** If it is producing something, then it must be abandoned. + */ + if (Factory) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + if (House) { + int fnum = -1; + + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + fnum = House->AircraftFactory; + break; + + case RTTI_INFANTRYTYPE: + fnum = House->InfantryFactory; + break; + + case RTTI_UNITTYPE: + fnum = House->UnitFactory; + break; + + case RTTI_BUILDINGTYPE: + fnum = House->BuildingFactory; + break; + + case RTTI_SPECIAL: + fnum = House->SpecialFactory; + break; + + } + + /* + ** Convert the factory number into a real factory pointer. + */ + FactoryClass * factory = 0; + if (fnum != -1) { + factory = Factories.Raw_Ptr(fnum); + } + + /* + ** If a factory was found, then temporarily disable this building and then + ** detmermine if any object that is being produced can still be produced. If + ** not, then the object being produced must be abandoned. + */ + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { + House->Abandon_Production(Class->ToBuild); + } + IsInLimbo = false; + } + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * * + * This routine is used to clear the way for placement of the specified object (usually * + * a building). If there are friendly units blocking the placement area, they are told * + * to scatter. Enemy blocking units are attacked. * + * * + * INPUT: techno -- Pointer to the object that is desired to be placed. * + * * + * cell -- The cell that placement wants to occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) +{ + Validate(); + bool again = false; + if (techno && cell > 0) { + short const * list = techno->Class_Of().Occupy_List(true); + + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + + if (Map.In_Radar(newcell)) { + TechnoClass * occupier = Map[newcell].Cell_Techno(); + if (occupier) { + again = true; + if (occupier->House->Is_Ally(this)) { + Map[newcell].Incoming(0, true); + } else { + Base_Is_Attacked(occupier); + } + } + } + } + } + return(again); +} + + +void BuildingClass::Hidden(void) +{ +// if (IsDiscoveredByPlayer && House->IsHuman) { +// House->Adjust_Drain(-Class->Drain); +// } + TechnoClass::Hidden(); +} + + +CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const +{ + CELL const *ptr; + CELL origin = Coord_Cell(Coord); + bool found = false; + + ptr = Class->ExitList; + if (ptr) { + while (*ptr != REFRESH_EOL) { + CELL cell = origin + *ptr++; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } + return(0); +} + +/*********************************************************************************************** + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * * + * This routine answers the question 'can this building be moved?' Typically, only the * + * construction yard can be moved and it does this by undeploying back into a MCV. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building move to a new location under player control? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/04/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Can_Player_Move(void) const +{ + Validate(); + return(*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); +} + +/*********************************************************************************************** + * BuildingClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: homecell -- The cell that the building would like to be placed down at. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + *=============================================================================================*/ +bool BuildingClass::Passes_Proximity_Check(CELL homecell) +{ + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map || !House->IsHuman) { + return(true); + } + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + short const * ptr = Occupy_List(true); + while (*ptr != REFRESH_EOL) { + CELL cell = homecell + *ptr++; + + if (!Map.In_Radar(cell)) return(false); + + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + BuildingClass * base = Map[newcell].Cell_Building(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + if (Map[newcell].Owner == House->Class->House) { + return(true); + } + + if (base && base->House->Class->House == House->Class->House) { + return(true); + } + } + } + return(false); +} \ No newline at end of file diff --git a/TIBERIANDAWN/BUILDING.H b/TIBERIANDAWN/BUILDING.H new file mode 100644 index 000000000..61b7d460d --- /dev/null +++ b/TIBERIANDAWN/BUILDING.H @@ -0,0 +1,307 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\building.h_v 2.20 16 Oct 1995 16:47:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUILDING_H +#define BUILDING_H + +#include "tarcom.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "bullet.h" +#include "target.h" +#include "factory.h" + +#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory +#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open +#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling + +/**************************************************************************** +** For each instance of a building in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular building. +*/ +class BuildingClass : public TechnoClass +{ + public: + BuildingTypeClass const * const Class; + operator StructType(void) const {return Class->Type;}; + + /* + ** If this building is in the process of producing something, then this + ** will point to the factory manager. + */ + FactoryClass * Factory; + + /* + ** This is the house that originally owned this factory. Objects buildable + ** by this house type will be produced from this factory regardless of who + ** the current owner is. + */ + HousesType ActLike; + + /* + ** If the building is at a good point to change orders, then this + ** flag will be set to true. + */ + unsigned IsReadyToCommence:1; + + /* + ** If this building is currently spending money to repair itself, then + ** this flag is true. It will automatically be set to false when the building + ** has reached full strength, when money is exhausted, or if the player + ** specifically stops the repair process. + */ + unsigned IsRepairing:1; + + /* + ** If repair is currently in progress and this flag is true, then a wrench graphic + ** will be overlaid on the building to give visual feedback for the repair process. + */ + unsigned IsWrenchVisible:1; + + /* + ** This flag is set when a commando has raided the building and planted + ** plastic explosives. When the CommandoCountDown timer expires, the + ** building takes massive damage. + */ + unsigned IsGoingToBlow:1; + + /* + ** If this building was destroyed by some method that would prevent + ** survivors, then this flag will be true. + */ + unsigned IsSurvivorless:1; + + /* + ** These state control variables are used by the oblisk for the charging + ** animation. + */ + unsigned IsCharging:1; + unsigned IsCharged:1; + + /* + ** A building that has been captured will not contain the full compliment + ** of crew. This is true even if it subsiquently gets captured back. + */ + unsigned IsCaptured:1; + + /* + ** Special countdown to destruction value. If the building is destroyed, + ** it won't actually be removed from the map until this value reaches + ** zero. This delay is for cosmetic reasons. + */ + TCountDownTimerClass CountDown; + + /* + ** This is the current animation processing state that the building is + ** in. + */ + BStateType BState; + BStateType QueueBState; + + /* + ** For multiplayer games, this keeps track of the last house to damage + ** this building, so if it burns to death or otherwise gradually dies, + ** proper credit can be given for the kill. + */ + HousesType WhoLastHurtMe; + + /* + ** This is the saboteur responsible for this building's destruction. + */ + TARGET WhomToRepay; + + /* + ** This is a record of the last strength of the building. Every so often, + ** it will compare this strength to the current strength. If there is a + ** discrepency, then the owner power is adjusted accordingly. + */ + int LastStrength; + + /* + ** This is the countdown timer that regulates placement retry logic + ** for factory type buildings. + */ + TCountDownTimerClass PlacementDelay; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * BuildingClass::operator new(size_t size); + static void BuildingClass::operator delete(void *ptr); + BuildingClass(void) : Class(0) {}; + BuildingClass(StructType type, HousesType house); + virtual ~BuildingClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_BUILDING;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + TARGET Target_Scan(void); + BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);}; + + /* + ** Query functions. + */ + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual InfantryType Crew_Type(void) const; + virtual int Pip_Count(void) const; + virtual bool Can_Player_Move(void) const; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual int Refund_Amount(void) const; + virtual DirType Fire_Direction(void) const; + int Power_Output(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Grand_Opening(bool captured = false); + virtual void Update_Buildables(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const; + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + bool Passes_Proximity_Check(CELL homecell); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual int Exit_Object(TechnoClass * base); + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark); + virtual void Look(bool incremental=false); + virtual void Fire_Out(void); + void Begin_Mode(BStateType bstate); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Death_Announcement(TechnoClass const * source=0) const; + virtual FireErrorType Can_Fire(TARGET, int which) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual bool Captured(HouseClass * newowner); + + /* + ** AI. + */ + virtual void Hidden(void); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int control); + virtual void Sell_Back(int control); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Assign_Target(TARGET target); + virtual bool Toggle_Primary(void); + bool Flush_For_Placement(TechnoClass * techno, CELL cell); + + virtual int Mission_Unload(void); + virtual int Mission_Repair(void); + virtual int Mission_Attack(void); + virtual int Mission_Harvest(void); + virtual int Mission_Guard(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Missile(void); + virtual void Enter_Idle_Mode(bool initial=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "STRUCTURES";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + void Update_Specials(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + void Drop_Debris(TARGET source = TARGET_NONE); + virtual BulletClass * Fire_At(TARGET target, int which); + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + static COORDINATE const CenterOffset[BSIZE_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/BULLET.CPP b/TIBERIANDAWN/BULLET.CPP new file mode 100644 index 000000000..ef187eb1a --- /dev/null +++ b/TIBERIANDAWN/BULLET.CPP @@ -0,0 +1,795 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\bullet.cpv 2.18 16 Oct 1995 16:50:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : March 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::AI -- Logic processing for bullet. * + * BulletClass::As_Target -- Converts the bullet into a target value. * + * BulletClass::BulletClass -- Bullet constructor. * + * BulletClass::BulletClass -- Default constructor for bullet objects. * + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * BulletClass::delete -- Bullet memory delete. * + * BulletClass::new -- Allocates memory for bullet object. * + * BulletClass::Validate -- validates bullet pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define GRAVITY 3 + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * BulletClass::VTable; + + +/*********************************************************************************************** + * BulletClass::Validate -- validates bullet pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int BulletClass::Validate(void) const +{ + int num; + + num = Bullets.ID(this); + if (num < 0 || num >= BULLET_MAX) { + Validate_Error("BULLET"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Default constructor for bullet objects. * + * * + * This is the default constructor for bullet objects. A bullet constructed by this routine * + * is not in a usable state for game purposes. It must be constructed by the full * + * (parameterized) constructor -- usually called as part of the overloaded "new" operator. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Do not use bullets that were constructed solely by this routine. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass::BulletClass(void) : + Class(0) +{ + Payback = NULL; + IsToAnimate = false; + Altitude = 0; + Riser = 0; + TarCom = TARGET_NONE; + Strength = 0; + IsLocked = true; + IsInaccurate = false; +} + + +/*********************************************************************************************** + * BulletClass::new -- Allocates memory for bullet object. * + * * + * This function will "allocate" a block of memory for a bullet object. * + * This memory block is merely lifted from a fixed pool of blocks. * + * * + * INPUT: size -- The size of the memory block needed. * + * * + * OUTPUT: Returns with a pointer to an available bullet object block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void * BulletClass::operator new(size_t ) +{ + void * ptr = Bullets.Allocate(); + if (ptr) { + ((BulletClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BulletClass::delete -- Bullet memory delete. * + * * + * Since bullets memory is merely "allocated" out of a pool, it never * + * actually gets deleted. * + * * + * INPUT: ptr -- Generic pointer to bullet object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::operator delete(void *ptr) +{ + if (ptr) { + ((BulletClass *)ptr)->IsActive = false; + } + Bullets.Free((BulletClass *)ptr); +} + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Bullet constructor. * + * * + * This is the constructor for the bullet class. It handles all * + * initialization of the bullet and starting it in motion toward its * + * target. * + * * + * INPUT: id -- The type of bullet this is (could be missile). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 06/20/1994 JLB : Firer is a base class pointer. * + * 12/10/1994 JLB : Auto calculate range optional. * + * 12/12/1994 JLB : Handles small arms as an instantaneous effect. * + * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. * + * 12/31/1994 JLB : Removed range parameter (not needed). * + *=============================================================================================*/ +BulletClass::BulletClass(BulletType id) : + Class(&BulletTypeClass::As_Reference(id)) +{ + Altitude = 0; + IsInaccurate = false; + IsLocked = true; +// IsLocked = false; + IsToAnimate = false; + Payback = 0; + Riser = 0; + Strength = Class->MaxStrength; + TarCom = TARGET_NONE; +} + + +/*********************************************************************************************** + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * * + * This function will determine the cell occupation list and return a pointer to it. Most * + * bullets are small and the list is usually short, but on occasion, it can be a list that * + * rivals the size of regular vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet * + * is over. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 01/05/1995 JLB : Handles projectiles with altitude. * + *=============================================================================================*/ +short const *BulletClass::Occupy_List(void) const +{ + Validate(); + switch (*this) { + case BULLET_FLAME: + return(Coord_Spillage_List(Coord, 25)); + + case BULLET_NUKE_UP: + case BULLET_NUKE_DOWN: + return(Coord_Spillage_List(Coord, 48)); + + default: + if (Altitude) { + static CELL _list[10]; + const short * ptr = Coord_Spillage_List(Coord, 5); + int index = 0; + CELL cell1 = Coord_Cell(Coord); + + while (ptr[index] != REFRESH_EOL) { + _list[index] = ptr[index]; + index++; + } + + COORDINATE coord = XY_Coord(0, Altitude); + coord = Coord_Sub(Coord, coord); + CELL cell2 = Coord_Cell(coord); + ptr = Coord_Spillage_List(coord, 5); + while (*ptr != REFRESH_EOL) { + _list[index++] = (*ptr++) + (cell2 - cell1); + } + _list[index] = REFRESH_EOL; + return(_list); + } + } + return(Coord_Spillage_List(Coord, 10)); +} + + +/*********************************************************************************************** + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * * + * This routine marks the objects under the bullet so that they will * + * be redrawn. This is necessary as the bullet moves -- objects under * + * its path need to be restored. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (!Class->IsInvisible) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::AI -- Logic processing for bullet. * + * * + * This routine will perform all logic (flight) logic on the bullet. * + * Primarily this is motion, fuse tracking, and detonation logic. Call * + * this routine no more than once per bullet per game tick. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::AI(void) +{ + Validate(); + COORDINATE coord; + + ObjectClass::AI(); + + /* + ** Balistic objects are handled here. + */ + bool forced = false; // Forced explosion. + if (Class->IsArcing) { + Altitude += Riser; + + if (Altitude <= 0) { + forced = true; + } + if (Riser > -100) { + Riser -= GRAVITY; + } + } + if (Class->IsDropping) { + Altitude += Riser; + + if (Altitude <= 0) { + forced = true; + } + if (Riser > -100) { + Riser -= 1; + } + } + + /* + ** Homing projectiles constantly change facing to face toward the target but + ** they only do so every other game frame (improves game speed and makes + ** missiles not so deadly). + */ + if ((Frame & 0x01) && Class->IsHoming && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom))); + } + + /* + ** Move the projectile forward according to its speed + ** and direction. + */ + coord = Coord; + if (Class->IsFlameEquipped) { + if (IsToAnimate) { + new AnimClass(ANIM_SMOKE_PUFF, coord, 1); + } + IsToAnimate = !IsToAnimate; + } + + /* + ** Handle any body rotation at this time. This process must + ** occur every game fame in order to achieve smooth rotation. + */ + if (PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Rotation_Adjust(Class->ROT); + } + + switch (Physics(coord, PrimaryFacing)) { + + /* + ** When a projectile reaches the edge of the world, it + ** vanishes from existence -- presumed to explode off + ** map. + */ + case IMPACT_EDGE: +// if (IsLocked) { + Mark(); + Delete_This(); +// } + break; + + default: + case IMPACT_NONE: + + /* + ** The projectile has moved. Check its fuse. If detonation + ** is signaled, then do so. Otherwise, just move. + */ + case IMPACT_NORMAL: + Mark(); +// IsLocked = true; + if (!Class->IsHigh) { + CellClass * cellptr = &Map[Coord_Cell(coord)]; + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) { + forced = true; + Coord = coord = Cell_Coord(Coord_Cell(coord)); + } + } + + /* + ** Bullets are generaly more effective when they are fired at aircraft. + */ + if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) { + forced = true; + + if (*this == BULLET_TOW) { + Strength += Strength/3; + } else { + Strength += Strength/2; + } + } + + if (!forced && (Class->IsDropping || !Fuse_Checkup(coord))) { + Coord = coord; + Mark(); + + /* + ** Certain projectiles loose strength when they travel. + */ + if (*this == BULLET_BULLET) { + if (Strength > 5) Strength--; + } + + } else { + + /* + ** When the target is reached, explode and do the damage + ** required of it. For homing objects, don't force the explosion to + ** match the target position. Non-homing projectiles adjust position so + ** that they hit the target. This compensates for the error in line of + ** flight logic. + */ + Mark(); + if (!forced && !Class->IsArcing && !Class->IsHoming && Fuse_Target()) { + Coord = Fuse_Target(); + } + + /* + ** Non-aircraft targets apply damage to the ground. + */ + if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) { + Explosion_Damage(Coord, Strength, Payback, Class->Warhead); + } else { + + /* + ** Special damage apply for SAM missiles. This is the only way that missile + ** damage affects the aircraft target. + */ + if (Distance(TarCom) < 0x0080) { + AircraftClass * object = As_Aircraft(TarCom); + + int str = Strength; + if (object) object->Take_Damage(str, 0, Class->Warhead, Payback); + } + } + + /* + ** For projectiles that are invisible while travelling toward the target, + ** allow scatter effect for the impact animation. + */ + if (Class->IsInvisible) { + Coord = Coord_Scatter(Coord, 0x0020); + } + if (Class->Explosion != ANIM_NONE) { + AnimClass * newanim = new AnimClass(Class->Explosion, Coord); + if (newanim) { + newanim->Sort_Above(TarCom); + } + + // MBL 05.20.2020 + // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked + // Per https://jaas.ea.com/browse/TDRA-6610 + // + if (newanim && Class->Explosion == ANIM_ATOM_BLAST && newanim->Owner == HOUSE_NONE) { + if (Payback && Payback->House && Payback->House->Class) { + newanim->Owner = Payback->House->Class->House; + } + } + } + Delete_This(); + return; + } + break; + } +} + + +/*********************************************************************************************** + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * * + * This routine displays the bullet visual at the location specified. * + * * + * INPUT: x,y -- The center coordinate to render the bullet at. * + * * + * window -- The window to clip to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window clipping parameter. * + * 01/08/1995 JLB : Handles translucent colors if necessary. * + *=============================================================================================*/ +void BulletClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + int facing = Facing_To_32(PrimaryFacing); + + /* + ** Certain projectiles aren't visible. This includes small bullets (which are actually + ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile). + */ + if (Class->IsInvisible) return; + + /* + ** If there is no shape loaded for this object, then + ** it obviously can't be rendered -- just bail. + */ + void const * shapeptr = Class->Get_Image_Data(); + if (!shapeptr) return; + + /* + ** Get the basic shape number for this projectile. + */ + int shapenum = 0; + if (!Class->IsFaceless) { + shapenum = UnitClass::BodyShape[facing]; + } + + /* + ** For tumbling projectiles, fetch offset stage. + */ + if (*this == BULLET_NAPALM) { + shapenum += Frame % 6; + } + + if (*this == BULLET_GRENADE) { + shapenum += Frame % 8; +// Timer++; + } + + /* + ** For flying projectiles, draw the shadow and adjust the actual projectile body + ** render position. + */ + if (Altitude) { + CC_Draw_Shape(shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, Map.FadingShade); + y -= Lepton_To_Pixel(Altitude); + } + + /* + ** Draw the main body of the projectile. + */ + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Class->IsTranslucent) { + flags = SHAPE_GHOST; + } + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.UnitShadow); +} + + +/*********************************************************************************************** + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * * + * This routine will zero out the bullet tracking list and object array in preparation for * + * the start of a new scenario. All bullets cease to exists after this function is * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Init(void) +{ + BulletClass *ptr; + + Bullets.Free_All(); + + ptr = new BulletClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * * + * When an object is removed from the game system, it must be removed all targeting and * + * tracking systems as well. This routine is used to remove the specified object from the * + * bullet. If the object isn't part of this bullet's tracking system, then no action is * + * performed. * + * * + * INPUT: target -- The target to remove from this tracking system. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Detach(TARGET target, bool all) +{ + Validate(); + ObjectClass * obj = As_Object(target); + + if (obj == Payback) { + Payback = 0; + } + + if (all && target == TarCom) { + TarCom = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * * + * This routine is used to take a bullet object that is in limbo and transition it to the * + * game system. A bullet object so transitioned, will be drawn and logic processing * + * performed. In effect, it comes into existance. * + * * + * INPUT: coord -- The location where the bullet object is to appear. * + * * + * dir -- The initial facing for the bullet object. * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + /* + ** Try to unlimbo the bullet as far as the base class is concerned. Use the already + ** set direction and strength if the "punt" values were passed in. This allows a bullet + ** to be setup prior to being launched. + */ + if (ObjectClass::Unlimbo(coord)) { + COORDINATE tcoord = As_Coord(TarCom); + + /* + ** Homing projectiles (missiles) do NOT override facing. They just fire in the + ** direction specified and let the chips fall where they may. + */ + if (!Class->IsHoming && !Class->IsDropping) { + dir = Direction(tcoord); + } + + /* + ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever + ** certain weapons are trained upon targets they were never designed to attack. Example: when + ** turrets or anti-tank missiles are fired at infantry. Indirect + ** fire is inherently inaccurate. + */ + if (IsInaccurate || Class->IsInaccurate || + ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Class->Warhead == WARHEAD_AP || Class->IsFueled))) { + + /* + ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard + ** Cicular Error of Probability (CEP) algorithm. High speed projectiles usually + ** just overshoot the target by extending the straight line flight. + */ + if (Class->IsHoming || Class->IsArcing) { + int scatterdist = ::Distance(coord, tcoord)/3; + scatterdist = MIN(scatterdist, 0x0200); + + if (*this == BULLET_GRENADE) { + scatterdist = ::Distance(coord, tcoord)/4; + scatterdist = MIN(scatterdist, 0x0080); + } + + dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF); + tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist)); + } else { + tcoord = Coord_Move(tcoord, dir, Random_Pick(0, 0x0100)); + } + + /* + ** Limit scatter to the weapon range of the firer. + */ + if (Payback) { + if (!Payback->In_Range(tcoord, 0) && !Payback->In_Range(tcoord, 1)) { + tcoord = Coord_Move(tcoord, ::Direction(tcoord, Coord), Distance(tcoord) - MAX(Payback->Weapon_Range(0), Payback->Weapon_Range(1))); + } + } + } + + /* + ** For very fast and invisible projectiles, just make the projectile exist at the target + ** location and dispense with the actual flight. + */ + if (Class->MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) { + Coord = tcoord; + } + + /* + ** Set the range equal to either the class defined range or the calculated + ** number of game frames it would take for the projectile to reach the target. + */ + int range = 0xFF; + if (!Class->Range) { + if (!Class->IsDropping) { + range = (::Distance(tcoord, Coord) / Class->MaxSpeed) + 4; + } + } else { + range = Class->Range; + } + + /* + ** Projectile speed is usually the default value for that projectile, but + ** certian projectiles alter speed according to the distance to the + ** target. + */ + int speed = Class->MaxSpeed; + if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE; + if (Class->IsArcing) { + speed = Class->MaxSpeed + (Distance(tcoord)>>5); + + /* + ** Set minimum speed (i.e., distance) for arcing projectiles. + */ + speed = MAX(speed, 25); + } + if (!Class->IsDropping) { + Fly_Speed(255, (MPHType)speed); + } + + /* + ** Arm the fuse. + */ + Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming)); + + /* + ** Projectiles that make a ballistic flight to impact point must determine a + ** vertical component for the projectile launch. This is crudely simulated + ** by biasing ground speed according to target distance and then giving + ** enough vertical velocity to keep the projectile airborne for the + ** desired amount of time. The mathematically correct solution would be to + ** calculate launch angle (given fixed projectile velocity) and then derive + ** the vertical and horizontal components. This solution would require a + ** of square root and an arcsine lookup table. OUCH! + */ + Altitude = 0; + Riser = 0; + if (Class->IsArcing) { + Altitude = 1; + Riser = ((Distance(tcoord)/2) / (speed+1))*GRAVITY; + Riser = MAX(Riser, (signed char)10); + } + if (Class->IsDropping) { + Altitude = Pixel_To_Lepton(24); + Riser = 0; + } + + PrimaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::As_Target -- Converts the bullet into a target value. * + * * + * This support routine is used to convert the bullet (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the bullet as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET BulletClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_BULLET, Bullets.ID(this))); +} \ No newline at end of file diff --git a/TIBERIANDAWN/BULLET.H b/TIBERIANDAWN/BULLET.H new file mode 100644 index 000000000..3273b1daf --- /dev/null +++ b/TIBERIANDAWN/BULLET.H @@ -0,0 +1,155 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\bullet.h_v 2.18 16 Oct 1995 16:47:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BULLET_H +#define BULLET_H + +#include "object.h" +#include "fly.h" +#include "fuse.h" + + +class BulletClass : public ObjectClass, + public FlyClass, + public FuseClass +{ + public: + + /* + ** This specifies exactly what kind of bullet this is. All of the static attributes + ** for this bullet is located in the BulletTypeClass pointed to by this variable. + */ + BulletTypeClass const * const Class; + operator BulletType(void) const {return Class->Type;}; + + /* + ** Records who sent this "present" so that an appropriate "thank you" can + ** be returned. + */ + TechnoClass * Payback; + + /* + ** This is the facing that the projectile is travelling. + */ + FacingClass PrimaryFacing; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * BulletClass::operator new(size_t size); + static void BulletClass::operator delete(void *ptr); + BulletClass(void); + BulletClass(BulletType id); + virtual ~BulletClass(void) {if (GameActive) BulletClass::Limbo();}; + virtual RTTIType What_Am_I(void) const {return RTTI_BULLET;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Target(TARGET target) {TarCom = target;}; + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual LayerType In_Which_Layer(void) const {return LAYER_TOP;}; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Detach(TARGET target, bool all); + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void AI(void); + virtual short const * Occupy_List(void) const; + virtual short const * Overlap_List(void) const {return Occupy_List();}; + virtual TARGET As_Target(void) const; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this bullet is forced to be inaccurate because of some outside means. A tank + ** firing while moving is a good example. + */ + unsigned IsInaccurate:1; + + private: + + // Crude animation flag. + unsigned IsToAnimate:1; + + /* + ** This is the height of the projectile. It starts at a low height, rises to an + ** apogee and then drops to explode upon impact. The height is used to render + ** the bullet's vertical offset. + */ + int Altitude; + + /* + ** This is a modifier for the altitude that rises and falls in order to simulate + ** the arc of the projectile. This value is added to the height every game tick + ** while simultaneously being reduced itself. The net effect, is a rising + ** projectile that slows and then eventually drops. + */ + signed char Riser; + + /* + ** This is the target of the projectile. It is especially significant for those projectiles + ** that home in on a target. + */ + TARGET TarCom; + + /* + ** Is this missle allowed to come in from out of bounds? + */ + unsigned IsLocked:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/CARGO.CPP b/TIBERIANDAWN/CARGO.CPP new file mode 100644 index 000000000..e6620fb6c --- /dev/null +++ b/TIBERIANDAWN/CARGO.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cargo.cpv 2.18 16 Oct 1995 16:49:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : 10/31/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CargoClass::Attach -- Add unit to cargo hold. * + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * * + * This routine is used to dump the current cargo value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void CargoClass::Debug_Dump(MonoClass * mono) const +{ + if (How_Many()) { + mono->Set_Cursor(63, 3); + mono->Printf("(%d)%04X", How_Many(), Attached_Object()); + } +} +#endif + + +/*********************************************************************************************** + * CargoClass::Attach -- Add unit to cargo hold. * + * * + * This routine will add the specified unit to the cargo hold. The * + * unit will chain to any existing units in the hold. The chaining is * + * in a LIFO order. * + * * + * INPUT: object-- Pointer to the object to attach to the cargo hold. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 10/31/94 JLB : Handles chained objects. * + *=============================================================================================*/ +void CargoClass::Attach(FootClass * object) +{ + /* + ** If there is no object, then no action is necessary. + */ + if (!object) return; + + object->Limbo(); + + /* + ** Attach any existing cargo hold object to the end of the list as indicated by the + ** object pointer passed into this routine. This is necessary because several objects may + ** be attached at one time or several objects may be attached as a result of several calls + ** to this routine. Either case must be handled properly. + */ + ObjectClass * o = object->Next; + while (o) { + if (!o->Next) break; + o = o->Next; + } + if (o) { + o->Next = CargoHold; + } else { + object->Next = CargoHold; + } + + /* + ** Finally, assign the object pointer as the first object attached to this cargo hold. + */ + CargoHold = object; + Quantity = 0; + object = CargoHold; + while (object) { + Quantity++; + object = (FootClass *)object->Next; + } +} + + +/*********************************************************************************************** + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * * + * This routine will take a unit from the cargo hold and extract it. * + * The unit extracted is the last unit added to the hold. If there * + * is no unit in the hold or the occupant is not a unit, then NULL is * + * returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the unit that has been extracted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Detach_Object(void) +{ + FootClass * unit = Attached_Object(); + + if (unit) { + CargoHold = (FootClass *)unit->Next; + unit->Next = 0; + Quantity--; + } + return(unit); +} + + +/*********************************************************************************************** + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * * + * This routine will return with a pointer to the attached unit if one * + * is present. One would need to know this if this is a transport * + * unit and it needs to unload. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the attached unit. If there is no * + * attached unit, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Attached_Object(void) const +{ + if (Is_Something_Attached()) { + return(CargoHold); + } + return(NULL); +} + + diff --git a/TIBERIANDAWN/CARGO.H b/TIBERIANDAWN/CARGO.H new file mode 100644 index 000000000..dd4f96853 --- /dev/null +++ b/TIBERIANDAWN/CARGO.H @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cargo.h_v 2.20 16 Oct 1995 16:45:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CARGO_H +#define CARGO_H + +class FootClass; + +/**************************************************************************** +** This class handles the basic cargo logic. +*/ +class CargoClass { + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CargoClass(void) {Quantity = 0;CargoHold = 0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void) {}; + + int How_Many(void) const {return Quantity;}; + bool Is_Something_Attached(void) const {return (CargoHold != 0);}; + FootClass * Attached_Object(void) const; + FootClass * Detach_Object(void); + void Attach(FootClass * object); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + + /* + ** This is the number of objects attached to this cargo hold. For transporter + ** objects, they might contain more than one object. + */ + unsigned char Quantity; + + /* + ** This is the target value of any attached object. A value of zero indicates + ** that no object is attached. + */ + FootClass * CargoHold; +}; + +#endif + diff --git a/TIBERIANDAWN/CCDDE.CPP b/TIBERIANDAWN/CCDDE.CPP new file mode 100644 index 000000000..3202c7f34 --- /dev/null +++ b/TIBERIANDAWN/CCDDE.CPP @@ -0,0 +1,419 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DDE_Callback -- DDE server callback function * + * DDEServerClass::DDEServerClass -- class constructor * + * DDEServerClass::Enable -- Enables the DDE callback * + * DDEServerClass::Disable -- Disables the DDE callback * + * DDEServerClass::~DDEServerClass -- class destructor * + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "ccdde.h" +#include +#include + +DDEServerClass DDEServer; //Instance of the DDE Server class + +Instance_Class *DDE_Class = NULL; // pointer for client callback + // this *must* be called DDE_Class + +BOOL CC95AlreadyRunning = FALSE; //Was there an instance of C&C 95 already running when we started? + +extern HWND MainWindow; +extern TimerClass GameTimer; +extern bool GameTimerInUse; +extern void CCDebugString (char *string); + + +/*********************************************************************************************** + * DDE_Callback -- DDE server callback function * + * * + * Just acts as a wrapper for the DDEServerClass callback function * + * * + * INPUT: ptr to data from client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:19PM ST : Created * + *=============================================================================================*/ +BOOL CALLBACK DDE_Callback (unsigned char *data, long length) +{ + return (DDEServer.Callback(data, length)); +} + + + + +/*********************************************************************************************** + * DDEServerClass::DDEServerClass -- class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::DDEServerClass(void) +{ + MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet + + DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); + + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + + if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ + CC95AlreadyRunning = TRUE; + }else{ + + //ST - 12/20/2018 2:27PM + //DDE_Class->Register_Server( DDE_Callback ); + } +} + + + +void DDEServerClass::Enable(void) +{ + if (!IsEnabled){ + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Disable -- Disables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Disable(void) +{ + if (IsEnabled){ + DDE_Class->Enable_Callback( FALSE ); + IsEnabled = FALSE; + } +} + + + + + + +/*********************************************************************************************** + * DDEServerClass::~DDEServerClass -- class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::~DDEServerClass(void) +{ + Delete_MPlayer_Game_Info(); + delete( DDE_Class ); +} + + + +/*********************************************************************************************** + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * * + * * + * * + * INPUT: data from DDE client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Data has length and type as first 2 ints * + * * + * HISTORY: * + * 6/8/96 3:21PM ST : Created * + *=============================================================================================*/ +BOOL DDEServerClass::Callback(unsigned char *data, long length) +{ + + /* + ** If the packet length < 0 then this is a special advisory packet + */ + if ( length<0 ) { + + switch( length ) { + + case DDE_ADVISE_CONNECT: + CCDebugString("C&C95 - DDE advisory: client connect detected."); + return TRUE; + + case DDE_ADVISE_DISCONNECT: + CCDebugString("C&C95 - DDE advisory: client disconnect detected."); + return TRUE; + + default: + CCDebugString("C&C95 - DDE advisory: Unknown DDE advise type."); + return FALSE; + } + + }else{ + + /* + ** Packet must be at least the length of the packet type & size fields to be valid + */ + if (length < 2*sizeof(int)) { + CCDebugString ("C&C95 - Received invalid packet."); + return (FALSE); + } + + /* + ** Find out what kind of packet this is and its length. + */ + int *packet_pointer = (int *)data; + int actual_length = ntohl(*packet_pointer++); + int packet_type = ntohl(*packet_pointer++); + + /* + ** Strip the ID int from the start of the packet + */ + data += 2*sizeof (int); + length -= 2*sizeof (int); + actual_length -= 2*sizeof (int); + + /* + ** Take the appropriate action for the packet type + */ + switch ( packet_type ){ + + /* + ** This is a packet with the info required for starting a new internet game. This is really + * just C&CSPAWN.INI sent from WChat instead of read from disk. + */ + case DDE_PACKET_START_MPLAYER_GAME: + CCDebugString("C&C95 - Received start game packet."); + Delete_MPlayer_Game_Info(); + MPlayerGameInfo = new char [actual_length + 1]; + memcpy (MPlayerGameInfo, data, actual_length); + *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string + MPlayerGameInfoLength = actual_length; + LastHeartbeat = 0; + break; + + case DDE_TICKLE: + CCDebugString("C&C95 - Received 'tickle' packet."); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + break; + + case DDE_PACKET_HEART_BEAT: + CCDebugString("C&C95 - Received heart beat packet."); + if (GameTimerInUse){ + LastHeartbeat = GameTimer.Time(); + }else{ + LastHeartbeat = 0; + } + break; + + default: + CCDebugString("C&C95 - Received unrecognised packet."); + break; + + } + } + + return (TRUE); + +} + + + +/*********************************************************************************************** + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to data in .INI file format * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:23PM ST : Created * + *=============================================================================================*/ +char *DDEServerClass::Get_MPlayer_Game_Info (void) +{ + return (MPlayerGameInfo); +} + + + +/*********************************************************************************************** + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:24PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Delete_MPlayer_Game_Info(void) +{ + if (MPlayerGameInfo){ + delete [] MPlayerGameInfo; + MPlayerGameInfo = NULL; + } +} + + + +/*********************************************************************************************** + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: time since heartbeat * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:05PM ST : Created * + *=============================================================================================*/ +int DDEServerClass::Time_Since_Heartbeat(void) +{ + return (GameTimer.Time() - LastHeartbeat); +} + + + + +/*********************************************************************************************** + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * * + * * + * INPUT: ptr to the data to send * + * length of data * + * packet type identifier * + * * + * OUTPUT: true if packet successfully sent * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:07PM ST : Created * + *=============================================================================================*/ +BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) +{ +#if (0) + BOOL app_exists; + + app_exists = DDE_Class->Test_Server_Running(DDE_Class->remote_name); + + if (app_exists != TRUE) { + CCDebugString("Connection to server failed!"); + return(FALSE); + } +#endif //(0) + + if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { + CCDebugString("C&C95 - Failed to connect for POKE!"); + return (FALSE); + } + + char *poke_data = new char [length + 2*sizeof(int)]; + + int *poke_data_int = (int*)poke_data; + + *poke_data_int = htonl (length + 2*sizeof(int)); + *(poke_data_int+1)= htonl (packet_type); + + memcpy (poke_data + 8, data, length); + + + if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { + CCDebugString("C&C95 - POKE failed!\n"); + DDE_Class->Close_Poke_Connection(); // close down the link + delete poke_data; + return (FALSE); + } + + DDE_Class->Close_Poke_Connection(); // close down the link + + delete poke_data; + + return (TRUE); +} diff --git a/TIBERIANDAWN/CCDDE.H b/TIBERIANDAWN/CCDDE.H new file mode 100644 index 000000000..fc8dd5f93 --- /dev/null +++ b/TIBERIANDAWN/CCDDE.H @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "dde.h" + +class DDEServerClass { + + public: + + DDEServerClass (void); + ~DDEServerClass (void); + + + char *Get_MPlayer_Game_Info (void); //Returns pointer to game info + int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info + BOOL Callback(unsigned char *data, long length); //DDE callback function + void Delete_MPlayer_Game_Info(void); //release the game info memory + void Enable(void); //Enable the DDE callback + void Disable(void); //Disable the DDE callback + int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat + + /* + ** Enumeration for DDE packet types from WChat + */ + enum { + DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options + DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics. + DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there. + DDE_TICKLE, //Message to prompt other app to take focus. + DDE_CONNECTION_FAILED + }; + + + private: + + char *MPlayerGameInfo; //Pointer to game start packet + int MPlayerGameInfoLength; //Length of game start packet. + BOOL IsEnabled; //Flag for DDE callback enable + int LastHeartbeat; // Time since last heartbeat packet was received from WChat + +}; + + +extern DDEServerClass DDEServer; +extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type); + + +#endif //WIN32 diff --git a/TIBERIANDAWN/CCFILE.CPP b/TIBERIANDAWN/CCFILE.CPP new file mode 100644 index 000000000..d4b86ebef --- /dev/null +++ b/TIBERIANDAWN/CCFILE.CPP @@ -0,0 +1,626 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : March 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * CCFileClass::Error -- Handles displaying a file error message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +//#include +//#include +//#include +//#include +//#include +//#include +//#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ +#ifdef DEMO + if (strstr(File_Name(), "\\")) { + if (!Force_CD_Available(-1)) { + Prog_End(); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + } + } + +#else + + if (!Force_CD_Available(RequiredCD)) { + Prog_End("CCFileClass::Error CD not found", true); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + } + +#endif +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const *filename) : + CDFileClass(), + FromDisk(false), + Pointer(0), + Position(0), + Length(0), + Start(0) +{ + Set_Name(filename); +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const *buffer, long size) +{ + + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Pointer || FromDisk) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void *buffer, long size) +{ + int opened = false; + + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Pointer) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size) { + Mem_Copy(Add_Long_To_Pointer(Pointer, Position), buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + /* + ** If the file is part of a mixfile, but the mixfile is located + ** on disk, then a special read operation is necessary. + */ + if (FromDisk) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size > 0) { + CDFileClass::Seek(Start + Position, SEEK_SET); + size = CDFileClass::Read(buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + if (opened) Close(); + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + if (Pointer || FromDisk) { + switch (dir) { + case SEEK_END: + Position = Length; + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + if (Position < 0) Position = 0; + if (Position > Length) Position = Length; + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + if (Pointer || FromDisk) return(Length); + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + if (MixFileClass::Offset(File_Name())) { + return(true); + } + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Pointer) return(true); + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; // Starts at beginning offset. + Start = 0; + Length = 0; + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MixFileClass *mixfile = 0; + if (MixFileClass::Offset(File_Name(), &Pointer, &mixfile, &Start, &Length)) { + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (!Pointer) { + long start = Start; + long length = Length; + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char const * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + Searching(true); + if (dupfile) free((void *)dupfile); + Start = start; + Length = length; + FromDisk = true; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + +static CCFileClass Handles[10]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(char const *) +{ + CCFileClass::Set_Search_Path(path); + return(true); +} +#endif + +int __cdecl Open_File(char const *file_name, int mode) +{ + for (int index = 0; index < sizeof(Handles)/sizeof(Handles[0]); index++) { + if (!Handles[index].Is_Open()) { + Handles[index].Set_Name(file_name); + if (Handles[index].Open(mode)) { +// if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(WW_ERROR); +} + +VOID __cdecl Close_File(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(int handle, VOID *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(int handle, VOID const *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const *file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(char const *file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +int __cdecl Create_File(char const *file_name) +{ + return(CCFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(char const *name, VOID *ptr, ULONG size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +VOID * __cdecl Load_Alloc_Data(char const *name, int ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +ULONG __cdecl File_Size(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(char const *name, VOID const *ptr, ULONG size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(int handle, LONG offset, int starting) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +void WWDOS_Shutdown(void) +{ + for (int index = 0; index < 10; index++) { + Handles[index].Set_Name(NULL); + } +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +int __cdecl Find_Disk_Number(char const *) +{ + return(0); +} +#endif + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} + +//extern "C" { +//int MaxDevice; +//int DefaultDrive; +//char CallingDOSInt; + +//} + + +void Unfragment_File_Cache(void) +{ +} \ No newline at end of file diff --git a/TIBERIANDAWN/CCFILE.H b/TIBERIANDAWN/CCFILE.H new file mode 100644 index 000000000..070dd32e1 --- /dev/null +++ b/TIBERIANDAWN/CCFILE.H @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ccfile.h_v 2.18 16 Oct 1995 16:45:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 17, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCFILE_H +#define CCFILE_H + +#include +#include +#include "mixfile.h" +#include "cdfile.h" + + +/* +** This derived class for file access knows about mixfiles (packed files). It can handle opening +** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on +** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive. +*/ +class CCFileClass : public CDFileClass +{ + public: + CCFileClass(char const *filename); + CCFileClass(void); + virtual ~CCFileClass(void) {}; + + // Delete should be overloaded here as well. Don't allow deletes of mixfiles. + + virtual int Open(char const *filename, int rights=READ) {Set_Name(filename);return Open(rights);}; + virtual int Open(int rights=READ); + virtual int Is_Open(void) const; + virtual int Is_Available(int forced=false); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + private: + + /* + ** This flag indicates that the file is part of a mixfile and the mixfile resides on + ** disk. The file handle for this file is a legitimate DOS handle, although special + ** handling is necessary that takes into account the embedded nature of the file. + */ + bool FromDisk; + + /* + ** This indicates the file is actually part of a resident image of the mixfile + ** itself. In this case, the embedded file handle is invalid. All file access actually + ** gets routed through the cached version of the file. This is a pointer to the start + ** of the RAM image of the file. + */ + void * Pointer; + + /* + ** This is the starting offset of the beginning of the file. This value is only valid + ** if the file is part of a mixfile that resides on disk. It serves as the counterpart + ** to the "Pointer" variable. + */ + long Start; + + /* + ** This is the current seek position of the file. It is duplicated here if the file is + ** part of a mixfile since the DOS seek position is not accurate. This value will + ** range from zero to the size of the file in bytes. + */ + long Position; + + /* + ** This is the size of the file if it was embedded in a mixfile. The size must be manually + ** kept track of because the DOS file size is invalid. + */ + long Length; + + // Force these to never be invoked. + CCFileClass const operator = (CCFileClass const & c); + CCFileClass (CCFileClass const & ) {}; +}; + +#endif diff --git a/TIBERIANDAWN/CC_ICON.RC b/TIBERIANDAWN/CC_ICON.RC new file mode 100644 index 000000000..a2324fa5f --- /dev/null +++ b/TIBERIANDAWN/CC_ICON.RC @@ -0,0 +1,16 @@ +/**************************************************************************** + + +CC_ICON.RC + +produced by Borland Resource Workshop + + +*****************************************************************************/ + +#include "cc_icon.rh" + + + +ICON_1 ICON "conquer.ico" + diff --git a/TIBERIANDAWN/CDATA.CPP b/TIBERIANDAWN/CDATA.CPP new file mode 100644 index 000000000..26175edc4 --- /dev/null +++ b/TIBERIANDAWN/CDATA.CPP @@ -0,0 +1,2770 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cdata.cpv 2.18 16 Oct 1995 16:50:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : July 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static char const _slope00000001[] = {7,-1}; +static char const _slope000000101[] = {6,8,-1}; +static char const _slope00000011[] = {6,7,-1}; +static char const _slope0000001[] = {6,-1}; +static char const _slope000001001[] = {5,8,-1}; +static char const _slope000001[] = {5,-1}; +static char const _slope000101[] = {3,5,-1}; +static char const _slope00011010000100000001000011[] = {3,4,6,11,19,25,25,-1}; +static char const _slope00011010010100100001000011[] = {3,4,6,9,11,14,19,24,25,-1}; +static char const _slope0001[] = {3,-1}; +static char const _slope001001001[] = {2,5,8,-1}; +static char const _slope00110000000011[] = {2,3,12,13,-1}; +static char const _slope00110010010011[] = {2,3,6,9,12,13,-1}; +static char const _slope001111001[] = {2,3,4,5,8,-1}; +static char const _slope0011[] = {2,3,-1}; +static char const _slope001[] = {2,-1}; +static char const _slope01000000000000000000001[] = {1,22,-1}; +static char const _slope01000000100000010000001[] = {1,8,15,22,-1}; +static char const _slope0111[] = {1,2,3,-1}; +static char const _slope01[] = {1,-1}; +static char const _slope1001001[] = {0,3,6,-1}; +static char const _slope1001[] = {0,3,-1}; +static char const _slope1100000000000000001100011[] = {0,1,18,19,23,24,-1}; +static char const _slope1100001000001000001100011[] = {0,1,6,12,18,19,23,24,-1}; +static char const _slope1101101[] = {0,1,3,4,6,-1}; +static char const _slope1101[] = {0,1,3,-1}; +static char const _slope111[] = {0,1,2,-1}; +static char const _slope111010011[] = {0,1,2,4,7,8,-1}; +static char const _slope11101[] = {0,1,2,4,-1}; +static char const _slope111111011[] = {0,1,2,3,4,5,7,8,-1}; +static char const _slope11111111[] = {0,1,2,3,4,5,6,7,-1}; +static char const _slope111111[] = {0,1,2,3,4,5,-1}; +static char const _slope1[] = {0,-1}; + +static TemplateTypeClass const Empty( + TEMPLATE_CLEAR1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE|THEATERF_JUNGLE, + "CLEAR1", + TXT_CLEAR, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Clear( + TEMPLATE_CLEAR1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE|THEATERF_JUNGLE, + "CLEAR1", + TXT_CLEAR, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road1( + TEMPLATE_ROAD1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D01", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road2( + TEMPLATE_ROAD2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D02", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road3( + TEMPLATE_ROAD3, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D03", + TXT_ROAD, + LAND_CLEAR, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road4( + TEMPLATE_ROAD4, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D04", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road5( + TEMPLATE_ROAD5, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D05", + TXT_ROAD, + LAND_CLEAR, + 3,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road6( + TEMPLATE_ROAD6, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D06", + TXT_ROAD, + LAND_CLEAR, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road7( + TEMPLATE_ROAD7, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D07", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road8( + TEMPLATE_ROAD8, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D08", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road9( + TEMPLATE_ROAD9, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D09", + TXT_ROAD, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road10( + TEMPLATE_ROAD10, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D10", + TXT_ROAD, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road11( + TEMPLATE_ROAD11, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D11", + TXT_ROAD, + LAND_CLEAR, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road12( + TEMPLATE_ROAD12, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D12", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road13( + TEMPLATE_ROAD13, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D13", + TXT_ROAD, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road14( + TEMPLATE_ROAD14, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D14", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road15( + TEMPLATE_ROAD15, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D15", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road16( + TEMPLATE_ROAD16, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D16", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road17( + TEMPLATE_ROAD17, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D17", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road18( + TEMPLATE_ROAD18, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D18", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road19( + TEMPLATE_ROAD19, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D19", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road20( + TEMPLATE_ROAD20, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D20", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road21( + TEMPLATE_ROAD21, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D21", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road22( + TEMPLATE_ROAD22, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D22", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road23( + TEMPLATE_ROAD23, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D23", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road24( + TEMPLATE_ROAD24, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D24", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road25( + TEMPLATE_ROAD25, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D25", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road26( + TEMPLATE_ROAD26, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D26", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road27( + TEMPLATE_ROAD27, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D27", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road28( + TEMPLATE_ROAD28, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D28", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road29( + TEMPLATE_ROAD29, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D29", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road30( + TEMPLATE_ROAD30, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D30", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road31( + TEMPLATE_ROAD31, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D31", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road32( + TEMPLATE_ROAD32, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D32", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road33( + TEMPLATE_ROAD33, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D33", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road34( + TEMPLATE_ROAD34, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D34", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road35( + TEMPLATE_ROAD35, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D35", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road36( + TEMPLATE_ROAD36, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D36", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road37( + TEMPLATE_ROAD37, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D37", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road38( + TEMPLATE_ROAD38, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D38", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road39( + TEMPLATE_ROAD39, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D39", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road40( + TEMPLATE_ROAD40, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D40", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road41( + TEMPLATE_ROAD41, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D41", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road42( + TEMPLATE_ROAD42, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D42", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road43( + TEMPLATE_ROAD43, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D43", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Water( + TEMPLATE_WATER, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "W1", + TXT_WATER, + LAND_WATER, + 1,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Water2( + TEMPLATE_WATER2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "W2", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore1( + TEMPLATE_SHORE1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH1", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore2( + TEMPLATE_SHORE2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH2", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope111 +); +static TemplateTypeClass const Shore3( + TEMPLATE_SHORE3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH3", + TXT_WATER, + LAND_ROCK, + 1,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore4( + TEMPLATE_SHORE4, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH4", + TXT_WATER, + LAND_ROCK, + 2,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore5( + TEMPLATE_SHORE5, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH5", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore6( + TEMPLATE_SHORE6, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH6", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore7( + TEMPLATE_SHORE7, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH7", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_BEACH, + (char const *)_slope1 +); +static TemplateTypeClass const Shore8( + TEMPLATE_SHORE8, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH8", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope11111111 +); +static TemplateTypeClass const Shore9( + TEMPLATE_SHORE9, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH9", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111011 +); +static TemplateTypeClass const Shore10( + TEMPLATE_SHORE10, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH10", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_BEACH, + (char const *)_slope01 +); +static TemplateTypeClass const Shore11( + TEMPLATE_SHORE11, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH11", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope1001 +); +static TemplateTypeClass const Shore12( + TEMPLATE_SHORE12, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH12", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope000001001 +); +static TemplateTypeClass const Shore13( + TEMPLATE_SHORE13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH13", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope0000001 +); +static TemplateTypeClass const Shore14( + TEMPLATE_SHORE14, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH14", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope00000011 +); +static TemplateTypeClass const Shore15( + TEMPLATE_SHORE15, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH15", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope000000101 +); +static TemplateTypeClass const Shore16( + TEMPLATE_SHORE16, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH16", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore17( + TEMPLATE_SHORE17, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "SH17", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore18( + TEMPLATE_SHORE18, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "SH18", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore19( + TEMPLATE_SHORE19, + THEATERF_DESERT, + "SH19", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore20( + TEMPLATE_SHORE20, + THEATERF_DESERT, + "SH20", + TXT_WATER, + LAND_ROCK, + 4,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore21( + TEMPLATE_SHORE21, + THEATERF_DESERT, + "SH21", + TXT_WATER, + LAND_ROCK, + 3,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore22( + TEMPLATE_SHORE22, + THEATERF_DESERT, + "SH22", + TXT_WATER, + LAND_ROCK, + 6,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore23( + TEMPLATE_SHORE23, + THEATERF_DESERT, + "SH23", + TXT_WATER, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Shore24( + TEMPLATE_SHORE24, + THEATERF_DESERT, + "SH24", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Shore25( + TEMPLATE_SHORE25, + THEATERF_DESERT, + "SH25", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0001 +); +static TemplateTypeClass const Shore26( + TEMPLATE_SHORE26, + THEATERF_DESERT, + "SH26", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore27( + TEMPLATE_SHORE27, + THEATERF_DESERT, + "SH27", + TXT_WATER, + LAND_ROCK, + 4,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore28( + TEMPLATE_SHORE28, + THEATERF_DESERT, + "SH28", + TXT_WATER, + LAND_ROCK, + 3,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore29( + TEMPLATE_SHORE29, + THEATERF_DESERT, + "SH29", + TXT_WATER, + LAND_ROCK, + 6,2, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const Shore30( + TEMPLATE_SHORE30, + THEATERF_DESERT, + "SH30", + TXT_WATER, + LAND_ROCK, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore31( + TEMPLATE_SHORE31, + THEATERF_DESERT, + "SH31", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore32( + TEMPLATE_SHORE32, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH32", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1 +); +static TemplateTypeClass const Shore33( + TEMPLATE_SHORE33, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH33", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope001 +); +static TemplateTypeClass const Shore34( + TEMPLATE_SHORE34, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH34", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope001001001 +); +static TemplateTypeClass const Shore35( + TEMPLATE_SHORE35, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH35", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1001001 +); +static TemplateTypeClass const Shore36( + TEMPLATE_SHORE36, + THEATERF_DESERT, + "SH36", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore37( + TEMPLATE_SHORE37, + THEATERF_DESERT, + "SH37", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore38( + TEMPLATE_SHORE38, + THEATERF_DESERT, + "SH38", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore39( + TEMPLATE_SHORE39, + THEATERF_DESERT, + "SH39", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore40( + TEMPLATE_SHORE40, + THEATERF_DESERT, + "SH40", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore41( + TEMPLATE_SHORE41, + THEATERF_DESERT, + "SH41", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1101101 +); +static TemplateTypeClass const Shore42( + TEMPLATE_SHORE42, + THEATERF_DESERT, + "SH42", + TXT_WATER, + LAND_WATER, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore43( + TEMPLATE_SHORE43, + THEATERF_DESERT, + "SH43", + TXT_WATER, + LAND_WATER, + 1,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore44( + TEMPLATE_SHORE44, + THEATERF_DESERT, + "SH44", + TXT_WATER, + LAND_WATER, + 1,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore45( + TEMPLATE_SHORE45, + THEATERF_DESERT, + "SH45", + TXT_WATER, + LAND_WATER, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore46( + TEMPLATE_SHORE46, + THEATERF_DESERT, + "SH46", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1101 +); +static TemplateTypeClass const Shore47( + TEMPLATE_SHORE47, + THEATERF_DESERT, + "SH47", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore48( + TEMPLATE_SHORE48, + THEATERF_DESERT, + "SH48", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore49( + TEMPLATE_SHORE49, + THEATERF_DESERT, + "SH49", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore50( + TEMPLATE_SHORE50, + THEATERF_DESERT, + "SH50", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const Shore51( + TEMPLATE_SHORE51, + THEATERF_DESERT, + "SH51", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore52( + TEMPLATE_SHORE52, + THEATERF_DESERT, + "SH52", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore53( + TEMPLATE_SHORE53, + THEATERF_DESERT, + "SH53", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + (char const *)_slope11101 +); +static TemplateTypeClass const Shore54( + TEMPLATE_SHORE54, + THEATERF_DESERT, + "SH54", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore55( + TEMPLATE_SHORE55, + THEATERF_DESERT, + "SH55", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + (char const *)_slope001 +); +static TemplateTypeClass const Shore56( + TEMPLATE_SHORE56, + THEATERF_DESERT, + "SH56", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore57( + TEMPLATE_SHORE57, + THEATERF_DESERT, + "SH57", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore58( + TEMPLATE_SHORE58, + THEATERF_DESERT, + "SH58", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore59( + TEMPLATE_SHORE59, + THEATERF_DESERT, + "SH59", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore60( + TEMPLATE_SHORE60, + THEATERF_DESERT, + "SH60", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope000101 +); +static TemplateTypeClass const Shore61( + TEMPLATE_SHORE61, + THEATERF_DESERT, + "SH61", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Shore62( + TEMPLATE_SHORE62, + THEATERF_DESERT, + "SH62", + TXT_WATER, + LAND_WATER, + 6,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore63( + TEMPLATE_SHORE63, + THEATERF_DESERT, + "SH63", + TXT_WATER, + LAND_WATER, + 4,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Boulder1( + TEMPLATE_BOULDER1, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "B1", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder2( + TEMPLATE_BOULDER2, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "B2", + TXT_SLOPE, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder3( + TEMPLATE_BOULDER3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "B3", + TXT_SLOPE, + LAND_ROCK, + 3,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder4( + TEMPLATE_BOULDER4, + THEATERF_TEMPERATE, + "B4", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder5( + TEMPLATE_BOULDER5, + THEATERF_TEMPERATE, + "B5", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder6( + TEMPLATE_BOULDER6, + THEATERF_TEMPERATE, + "B6", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope1( + TEMPLATE_SLOPE1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S01", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope001 +); +static TemplateTypeClass const Slope2( + TEMPLATE_SLOPE2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S02", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope3( + TEMPLATE_SLOPE3, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S03", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope4( + TEMPLATE_SLOPE4, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S04", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope5( + TEMPLATE_SLOPE5, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S05", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope6( + TEMPLATE_SLOPE6, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S06", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Slope7( + TEMPLATE_SLOPE7, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S07", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope8( + TEMPLATE_SLOPE8, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S08", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope9( + TEMPLATE_SLOPE9, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S09", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0001 +); +static TemplateTypeClass const Slope10( + TEMPLATE_SLOPE10, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S10", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope11( + TEMPLATE_SLOPE11, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S11", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope12( + TEMPLATE_SLOPE12, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S12", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope13( + TEMPLATE_SLOPE13, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S13", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope14( + TEMPLATE_SLOPE14, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S14", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope0111 +); +static TemplateTypeClass const Slope15( + TEMPLATE_SLOPE15, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S15", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope16( + TEMPLATE_SLOPE16, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S16", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope17( + TEMPLATE_SLOPE17, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S17", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope18( + TEMPLATE_SLOPE18, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S18", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope19( + TEMPLATE_SLOPE19, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S19", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope20( + TEMPLATE_SLOPE20, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S20", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope21( + TEMPLATE_SLOPE21, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S21", + TXT_SLOPE, + LAND_ROCK, + 1,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope22( + TEMPLATE_SLOPE22, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S22", + TXT_SLOPE, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope23( + TEMPLATE_SLOPE23, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S23", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope24( + TEMPLATE_SLOPE24, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S24", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope25( + TEMPLATE_SLOPE25, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S25", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope26( + TEMPLATE_SLOPE26, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S26", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope27( + TEMPLATE_SLOPE27, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S27", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0011 +); +static TemplateTypeClass const Slope28( + TEMPLATE_SLOPE28, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S28", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope29( + TEMPLATE_SLOPE29, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S29", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope30( + TEMPLATE_SLOPE30, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S30", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope31( + TEMPLATE_SLOPE31, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S31", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope32( + TEMPLATE_SLOPE32, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S32", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope33( + TEMPLATE_SLOPE33, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S33", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope34( + TEMPLATE_SLOPE34, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S34", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope35( + TEMPLATE_SLOPE35, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S35", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope36( + TEMPLATE_SLOPE36, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S36", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope37( + TEMPLATE_SLOPE37, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S37", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope38( + TEMPLATE_SLOPE38, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S38", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush1( + TEMPLATE_BRUSH1, + THEATERF_DESERT, + "BR1", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush2( + TEMPLATE_BRUSH2, + THEATERF_DESERT, + "BR2", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush3( + TEMPLATE_BRUSH3, + THEATERF_DESERT, + "BR3", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush4( + TEMPLATE_BRUSH4, + THEATERF_DESERT, + "BR4", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush5( + TEMPLATE_BRUSH5, + THEATERF_DESERT, + "BR5", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush6( + TEMPLATE_BRUSH6, + THEATERF_DESERT, + "BR6", + TXT_BRUSH, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush7( + TEMPLATE_BRUSH7, + THEATERF_DESERT, + "BR7", + TXT_BRUSH, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush8( + TEMPLATE_BRUSH8, + THEATERF_DESERT, + "BR8", + TXT_BRUSH, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush9( + TEMPLATE_BRUSH9, + THEATERF_DESERT, + "BR9", + TXT_BRUSH, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush10( + TEMPLATE_BRUSH10, + THEATERF_DESERT, + "BR10", + TXT_BRUSH, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Patch1( + TEMPLATE_PATCH1, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P01", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch2( + TEMPLATE_PATCH2, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P02", + TXT_PATCH, + LAND_ROCK, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch3( + TEMPLATE_PATCH3, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P03", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch4( + TEMPLATE_PATCH4, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P04", + TXT_PATCH, + LAND_ROCK, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch5( + TEMPLATE_PATCH5, + THEATERF_DESERT, + "P05", + TXT_PATCH, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch6( + TEMPLATE_PATCH6, + THEATERF_DESERT, + "P06", + TXT_PATCH, + LAND_CLEAR, + 6,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch7( + TEMPLATE_PATCH7, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "P07", + TXT_PATCH, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch8( + TEMPLATE_PATCH8, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "P08", + TXT_PATCH, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch13( + TEMPLATE_PATCH13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P13", + TXT_PATCH, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch14( + TEMPLATE_PATCH14, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P14", + TXT_PATCH, + LAND_CLEAR, + 2,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch15( + TEMPLATE_PATCH15, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P15", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch16( + TEMPLATE_PATCH16, + THEATERF_WINTER, + "P16", + TXT_PATCH, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch17( + TEMPLATE_PATCH17, + THEATERF_WINTER, + "P17", + TXT_PATCH, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch18( + TEMPLATE_PATCH18, + THEATERF_WINTER, + "P18", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch19( + TEMPLATE_PATCH19, + THEATERF_WINTER, + "P19", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch20( + TEMPLATE_PATCH20, + THEATERF_WINTER, + "P20", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const River1( + TEMPLATE_RIVER1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV01", + TXT_RIVER, + LAND_WATER, + 5,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const River2( + TEMPLATE_RIVER2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV02", + TXT_RIVER, + LAND_WATER, + 5,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River3( + TEMPLATE_RIVER3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV03", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const River4( + TEMPLATE_RIVER4, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV04", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River5( + TEMPLATE_RIVER5, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV05", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River6( + TEMPLATE_RIVER6, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV06", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River7( + TEMPLATE_RIVER7, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV07", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River8( + TEMPLATE_RIVER8, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV08", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River9( + TEMPLATE_RIVER9, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV09", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River10( + TEMPLATE_RIVER10, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV10", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River11( + TEMPLATE_RIVER11, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV11", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River12( + TEMPLATE_RIVER12, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV12", + TXT_RIVER, + LAND_WATER, + 3,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River13( + TEMPLATE_RIVER13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV13", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River14( + TEMPLATE_RIVER14, + THEATERF_DESERT, + "RV14", + TXT_RIVER, + LAND_WATER, + 4,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River15( + TEMPLATE_RIVER15, + THEATERF_DESERT, + "RV15", + TXT_RIVER, + LAND_WATER, + 4,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River16( + TEMPLATE_RIVER16, + THEATERF_DESERT, + "RV16", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River17( + TEMPLATE_RIVER17, + THEATERF_DESERT, + "RV17", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River18( + TEMPLATE_RIVER18, + THEATERF_DESERT, + "RV18", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River19( + TEMPLATE_RIVER19, + THEATERF_DESERT, + "RV19", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River20( + TEMPLATE_RIVER20, + THEATERF_DESERT, + "RV20", + TXT_RIVER, + LAND_WATER, + 6,8, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River21( + TEMPLATE_RIVER21, + THEATERF_DESERT, + "RV21", + TXT_RIVER, + LAND_WATER, + 5,8, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River22( + TEMPLATE_RIVER22, + THEATERF_DESERT, + "RV22", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River23( + TEMPLATE_RIVER23, + THEATERF_DESERT, + "RV23", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River24( + TEMPLATE_RIVER24, + THEATERF_DESERT, + "RV24", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River25( + TEMPLATE_RIVER25, + THEATERF_DESERT, + "RV25", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Ford1( + TEMPLATE_FORD1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FORD1", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope001111001 +); +static TemplateTypeClass const Ford2( + TEMPLATE_FORD2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FORD2", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope111010011 +); +static TemplateTypeClass const Falls1( + TEMPLATE_FALLS1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FALLS1", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Falls2( + TEMPLATE_FALLS2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FALLS2", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Bridge1( + TEMPLATE_BRIDGE1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE1", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00110010010011 +); +static TemplateTypeClass const Bridge1d( + TEMPLATE_BRIDGE1D, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE1D", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00110000000011 +); +static TemplateTypeClass const Bridge2( + TEMPLATE_BRIDGE2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE2", + TXT_RIVER, + LAND_WATER, + 5,5, + LAND_CLEAR, + (char const *)_slope1100001000001000001100011 +); +static TemplateTypeClass const Bridge2d( + TEMPLATE_BRIDGE2D, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE2D", + TXT_RIVER, + LAND_WATER, + 5,5, + LAND_CLEAR, + (char const *)_slope1100000000000000001100011 +); +static TemplateTypeClass const Bridge3( + TEMPLATE_BRIDGE3, + THEATERF_DESERT, + "BRIDGE3", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_CLEAR, + (char const *)_slope00011010010100100001000011 +); +static TemplateTypeClass const Bridge3d( + TEMPLATE_BRIDGE3D, + THEATERF_DESERT, + "BRIDGE3D", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_CLEAR, + (char const *)_slope00011010000100000001000011 +); +static TemplateTypeClass const Bridge4( + TEMPLATE_BRIDGE4, + THEATERF_DESERT, + "BRIDGE4", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_CLEAR, + (char const *)_slope01000000100000010000001 +); +static TemplateTypeClass const Bridge4d( + TEMPLATE_BRIDGE4D, + THEATERF_DESERT, + "BRIDGE4D", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_CLEAR, + (char const *)_slope01000000000000000000001 +); + +TemplateTypeClass const * const TemplateTypeClass::Pointers[TEMPLATE_COUNT] = { + &Clear, // TEMPLATE_CLEAR1 + &Water, // TEMPLATE_WATER + &Water2, // TEMPLATE_WATER2 + &Shore1, // TEMPLATE_SHORE1 + &Shore2, // TEMPLATE_SHORE2 + &Shore3, // TEMPLATE_SHORE3 + &Shore4, // TEMPLATE_SHORE4 + &Shore5, // TEMPLATE_SHORE5 + &Shore11, // TEMPLATE_SHORE11 + &Shore12, // TEMPLATE_SHORE12 + &Shore13, // TEMPLATE_SHORE13 + &Shore14, // TEMPLATE_SHORE14 + &Shore15, // TEMPLATE_SHORE15 + &Slope1, // TEMPLATE_SLOPE1 + &Slope2, // TEMPLATE_SLOPE2 + &Slope3, // TEMPLATE_SLOPE3 + &Slope4, // TEMPLATE_SLOPE4 + &Slope5, // TEMPLATE_SLOPE5 + &Slope6, // TEMPLATE_SLOPE6 + &Slope7, // TEMPLATE_SLOPE7 + &Slope8, // TEMPLATE_SLOPE8 + &Slope9, // TEMPLATE_SLOPE9 + &Slope10, // TEMPLATE_SLOPE10 + &Slope11, // TEMPLATE_SLOPE11 + &Slope12, // TEMPLATE_SLOPE12 + &Slope13, // TEMPLATE_SLOPE13 + &Slope14, // TEMPLATE_SLOPE14 + &Slope15, // TEMPLATE_SLOPE15 + &Slope16, // TEMPLATE_SLOPE16 + &Slope17, // TEMPLATE_SLOPE17 + &Slope18, // TEMPLATE_SLOPE18 + &Slope19, // TEMPLATE_SLOPE19 + &Slope20, // TEMPLATE_SLOPE20 + &Slope21, // TEMPLATE_SLOPE21 + &Slope22, // TEMPLATE_SLOPE22 + &Slope23, // TEMPLATE_SLOPE23 + &Slope24, // TEMPLATE_SLOPE24 + &Slope25, // TEMPLATE_SLOPE25 + &Slope26, // TEMPLATE_SLOPE26 + &Slope27, // TEMPLATE_SLOPE27 + &Slope28, // TEMPLATE_SLOPE28 + &Slope29, // TEMPLATE_SLOPE29 + &Slope30, // TEMPLATE_SLOPE30 + &Slope31, // TEMPLATE_SLOPE31 + &Slope32, // TEMPLATE_SLOPE32 + &Slope33, // TEMPLATE_SLOPE33 + &Slope34, // TEMPLATE_SLOPE34 + &Slope35, // TEMPLATE_SLOPE35 + &Slope36, // TEMPLATE_SLOPE36 + &Slope37, // TEMPLATE_SLOPE37 + &Slope38, // TEMPLATE_SLOPE38 + &Shore32, // TEMPLATE_SHORE32 + &Shore33, // TEMPLATE_SHORE33 + &Shore20, // TEMPLATE_SHORE20 + &Shore21, // TEMPLATE_SHORE21 + &Shore22, // TEMPLATE_SHORE22 + &Shore23, // TEMPLATE_SHORE23 + &Brush1, // TEMPLATE_BRUSH1 + &Brush2, // TEMPLATE_BRUSH2 + &Brush3, // TEMPLATE_BRUSH3 + &Brush4, // TEMPLATE_BRUSH4 + &Brush5, // TEMPLATE_BRUSH5 + &Brush6, // TEMPLATE_BRUSH6 + &Brush7, // TEMPLATE_BRUSH7 + &Brush8, // TEMPLATE_BRUSH8 + &Brush9, // TEMPLATE_BRUSH9 + &Brush10, // TEMPLATE_BRUSH10 + &Patch1, // TEMPLATE_PATCH1 + &Patch2, // TEMPLATE_PATCH2 + &Patch3, // TEMPLATE_PATCH3 + &Patch4, // TEMPLATE_PATCH4 + &Patch5, // TEMPLATE_PATCH5 + &Patch6, // TEMPLATE_PATCH6 + &Patch7, // TEMPLATE_PATCH7 + &Patch8, // TEMPLATE_PATCH8 + &Shore16, // TEMPLATE_SHORE16 + &Shore17, // TEMPLATE_SHORE17 + &Shore18, // TEMPLATE_SHORE18 + &Shore19, // TEMPLATE_SHORE19 + &Patch13, // TEMPLATE_PATCH13 + &Patch14, // TEMPLATE_PATCH14 + &Patch15, // TEMPLATE_PATCH15 + &Boulder1, // TEMPLATE_BOULDER1 + &Boulder2, // TEMPLATE_BOULDER2 + &Boulder3, // TEMPLATE_BOULDER3 + &Boulder4, // TEMPLATE_BOULDER4 + &Boulder5, // TEMPLATE_BOULDER5 + &Boulder6, // TEMPLATE_BOULDER6 + &Shore6, // TEMPLATE_SHORE6 + &Shore7, // TEMPLATE_SHORE7 + &Shore8, // TEMPLATE_SHORE8 + &Shore9, // TEMPLATE_SHORE9 + &Shore10, // TEMPLATE_SHORE10 + + &Road1, // TEMPLATE_ROAD1 + &Road2, // TEMPLATE_ROAD2 + &Road3, // TEMPLATE_ROAD3 + &Road4, // TEMPLATE_ROAD4 + &Road5, // TEMPLATE_ROAD5 + &Road6, // TEMPLATE_ROAD6 + &Road7, // TEMPLATE_ROAD7 + &Road8, // TEMPLATE_ROAD8 + &Road9, // TEMPLATE_ROAD9 + &Road10, // TEMPLATE_ROAD10 + &Road11, // TEMPLATE_ROAD11 + &Road12, // TEMPLATE_ROAD12 + &Road13, // TEMPLATE_ROAD13 + &Road14, // TEMPLATE_ROAD14 + &Road15, // TEMPLATE_ROAD15 + &Road16, // TEMPLATE_ROAD16 + &Road17, // TEMPLATE_ROAD17 + &Road18, // TEMPLATE_ROAD18 + &Road19, // TEMPLATE_ROAD19 + &Road20, // TEMPLATE_ROAD20 + &Road21, // TEMPLATE_ROAD21 + &Road22, // TEMPLATE_ROAD22 + &Road23, // TEMPLATE_ROAD23 + &Road24, // TEMPLATE_ROAD24 + &Road25, // TEMPLATE_ROAD25 + &Road26, // TEMPLATE_ROAD26 + &Road27, // TEMPLATE_ROAD27 + &Road28, // TEMPLATE_ROAD28 + &Road29, // TEMPLATE_ROAD29 + &Road30, // TEMPLATE_ROAD30 + &Road31, // TEMPLATE_ROAD31 + &Road32, // TEMPLATE_ROAD32 + &Road33, // TEMPLATE_ROAD33 + &Road34, // TEMPLATE_ROAD34 + &Road35, // TEMPLATE_ROAD35 + &Road36, // TEMPLATE_ROAD36 + &Road37, // TEMPLATE_ROAD37 + &Road38, // TEMPLATE_ROAD38 + &Road39, // TEMPLATE_ROAD39 + &Road40, // TEMPLATE_ROAD40 + &Road41, // TEMPLATE_ROAD41 + &Road42, // TEMPLATE_ROAD42 + &Road43, // TEMPLATE_ROAD43 + + &River1, // TEMPLATE_RIVER1 + &River2, // TEMPLATE_RIVER2 + &River3, // TEMPLATE_RIVER3 + &River4, // TEMPLATE_RIVER4 + &River5, // TEMPLATE_RIVER5 + &River6, // TEMPLATE_RIVER6 + &River7, // TEMPLATE_RIVER7 + &River8, // TEMPLATE_RIVER8 + &River9, // TEMPLATE_RIVER9 + &River10, // TEMPLATE_RIVER10 + &River11, // TEMPLATE_RIVER11 + &River12, // TEMPLATE_RIVER12 + &River13, // TEMPLATE_RIVER13 + &River14, // TEMPLATE_RIVER14 + &River15, // TEMPLATE_RIVER15 + &River16, // TEMPLATE_RIVER16 + &River17, // TEMPLATE_RIVER17 + &River18, // TEMPLATE_RIVER18 + &River19, // TEMPLATE_RIVER19 + &River20, // TEMPLATE_RIVER20 + &River21, // TEMPLATE_RIVER21 + &River22, // TEMPLATE_RIVER22 + &River23, // TEMPLATE_RIVER23 + &River24, // TEMPLATE_RIVER24 + &River25, // TEMPLATE_RIVER25 + &Ford1, // TEMPLATE_FORD1 + &Ford2, // TEMPLATE_FORD2 + &Falls1, // TEMPLATE_FALLS1 + &Falls2, // TEMPLATE_FALLS2 + &Bridge1, // TEMPLATE_BRIDGE1 + &Bridge1d, // TEMPLATE_BRIDGE1D + &Bridge2, // TEMPLATE_BRIDGE2 + &Bridge2d, // TEMPLATE_BRIDGE2D + &Bridge3, // TEMPLATE_BRIDGE3 + &Bridge3d, // TEMPLATE_BRIDGE3D + &Bridge4, // TEMPLATE_BRIDGE4 + &Bridge4d, // TEMPLATE_BRIDGE4D + + &Shore24, // TEMPLATE_SHORE24 + &Shore25, // TEMPLATE_SHORE25 + &Shore26, // TEMPLATE_SHORE26 + &Shore27, // TEMPLATE_SHORE27 + &Shore28, // TEMPLATE_SHORE28 + &Shore29, // TEMPLATE_SHORE29 + &Shore30, // TEMPLATE_SHORE30 + &Shore31, // TEMPLATE_SHORE31 + + &Patch16, // TEMPLATE_PATCH16 + &Patch17, // TEMPLATE_PATCH17 + &Patch18, // TEMPLATE_PATCH18 + &Patch19, // TEMPLATE_PATCH19 + &Patch20, // TEMPLATE_PATCH20 + + &Shore34, // TEMPLATE_SHORE34 + &Shore35, // TEMPLATE_SHORE35 + &Shore36, // TEMPLATE_SHORE36 + &Shore37, // TEMPLATE_SHORE37 + &Shore38, // TEMPLATE_SHORE38 + &Shore39, // TEMPLATE_SHORE39 + &Shore40, // TEMPLATE_SHORE40 + &Shore41, // TEMPLATE_SHORE41 + &Shore42, // TEMPLATE_SHORE42 + &Shore43, // TEMPLATE_SHORE43 + &Shore44, // TEMPLATE_SHORE44 + &Shore45, // TEMPLATE_SHORE45 + + &Shore46, // TEMPLATE_SHORE46 + &Shore47, // TEMPLATE_SHORE47 + &Shore48, // TEMPLATE_SHORE48 + &Shore49, // TEMPLATE_SHORE49 + &Shore50, // TEMPLATE_SHORE50 + &Shore51, // TEMPLATE_SHORE51 + &Shore52, // TEMPLATE_SHORE52 + &Shore53, // TEMPLATE_SHORE53 + &Shore54, // TEMPLATE_SHORE54 + &Shore55, // TEMPLATE_SHORE55 + &Shore56, // TEMPLATE_SHORE56 + &Shore57, // TEMPLATE_SHORE57 + &Shore58, // TEMPLATE_SHORE58 + &Shore59, // TEMPLATE_SHORE59 + &Shore60, // TEMPLATE_SHORE60 + &Shore61, // TEMPLATE_SHORE61 + + &Shore62, // TEMPLATE_SHORE62 + &Shore63, // TEMPLATE_SHORE63 +}; + + +/*********************************************************************************************** + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * * + * This is the constructor for the template types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass::TemplateTypeClass(TemplateType iconset, int theater, + char const *ininame, int fullname, LandType land, + int width, int height, LandType altland, char const *alticons ) : + ObjectTypeClass(false, false, false, true, false, false, true, true, fullname, ininame, ARMOR_NONE, 0) +{ + Theater = theater; + AltIcons = alticons; + AltLand = altland; + Type = iconset; + Land = land; + Width = width; + Height = height; +} + + +/*********************************************************************************************** + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * * + * This routine is used to determine the template number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the template. * + * * + * OUTPUT: Returns with the template number. If the name had no match, * + * then returns with TEMPLATE_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +TemplateType TemplateTypeClass::From_Name(char const *name) +{ + if (name) { + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(TEMPLATE_NONE); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the template map and build an * + * occupation list. This list is used to render a template cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the template occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * TemplateTypeClass::Occupy_List(bool) const +{ + static short _occupy[13*8+5]; + unsigned char map[13*8]; + short *ptr; + int index; + + Mem_Copy(Get_Icon_Set_Map(Get_Image_Data()), map, Width*Height); + + ptr = &_occupy[0]; + for (index = 0; index < Width*Height; index++) { + if (map[index] != 0xFF) { + *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + + return((short const *)&_occupy[0]); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * * + * This routine loads the template graphic data for all the template * + * type supported for the specified theater. This routine is called * + * whenever the theater for the scenario is first determined. * + * * + * INPUT: theater -- The theater that the template data is to be * + * loaded for. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk! * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/02/1994 JLB : Only handles iconset loading now (as it should). * + *=============================================================================================*/ +void TemplateTypeClass::Init(TheaterType theater) +{ + //if (theater != LastTheater){ + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + void const * ptr; // Working loaded iconset pointer. + + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + TemplateTypeClass const & tplate = As_Reference(index); + + ((void const *&)tplate.ImageData) = NULL; + if (tplate.Theater & (1< 3 || h > 3); + if (scale) { + x -= (w/2) * (ICON_PIXEL_W/2); + y -= (h/2) * (ICON_PIXEL_H/2); + } else { + x -= (w/2) * ICON_PIXEL_W; + y -= (h/2) * ICON_PIXEL_H; + } + x += WindowList[window][WINDOWX]<<3; + y += WindowList[window][WINDOWY]; + + Mem_Copy(Get_Icon_Set_Map(Get_Image_Data()), map, Width*Height); + + for (index = 0; index < w*h; index++) { + if (map[index] != 0xFF) { + HidPage.Draw_Stamp(Get_Image_Data(), index, 0, 0, NULL, WINDOW_MAIN); + if (scale) { + + HidPage.Scale((*LogicPage), 0, 0, + x + ((index % w)*(ICON_PIXEL_W/2)), + y + ((index / w)*(ICON_PIXEL_H/2)), + ICON_PIXEL_W, ICON_PIXEL_H, + ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL); + + } else { + HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)), + y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H); + } + } + } +} + + +/*********************************************************************************************** + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * * + * This routine prepares a list of template objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a template object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 05/28/1994 JLB : Only handles real templates now. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void TemplateTypeClass::Prep_For_Add(void) +{ + for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * * + * This support routine is used by the scenario editor to add a template object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the template object. * + * * + * OUTPUT: bool; Was the template object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TemplateClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * * + * This routine will create an object of this type. For certain template objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a template at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this template type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TemplateClass(Type, -1)); +} + + +/*********************************************************************************************** + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::One_Time(void) +{ +} + + + diff --git a/TIBERIANDAWN/CDFILE.CPP b/TIBERIANDAWN/CDFILE.CPP new file mode 100644 index 000000000..1468c799f --- /dev/null +++ b/TIBERIANDAWN/CDFILE.CPP @@ -0,0 +1,534 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cdfile.cpv 2.18 16 Oct 1995 16:48:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : CDFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * CDFileClass::Open -- Opens the file object -- with path search. * + * CDFileClass::Open -- Opens the file wherever it can be found. * + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include "cdfile.h" + +/* +** Pointer to the first search path record. +*/ +CDFileClass::SearchDriveType * CDFileClass::First = 0; + +int CDFileClass::CurrentCDDrive = 0; +int CDFileClass::LastCDDrive = 0; +char CDFileClass::RawPath[512]; + + + +int __cdecl Is_Disk_Inserted(int disk) +{ + return true; + +#if (0) // ST - 12/17/2018 5:31PM + struct find_t fb; + char scan[] = "?:\\*.*"; + + scan[0] = 'A' + disk; + return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0); +#endif +} + + +CDFileClass::CDFileClass(char const *filename) : + IsDisabled(false) +{ + Set_Name(filename); + memset (RawPath, 0, sizeof(RawPath)); +} + + +CDFileClass::CDFileClass(void) : + IsDisabled(false) +{ +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file object -- with path search. * + * * + * This will open the file object, but since the file object could have been constructed * + * with a pathname, this routine will try to find the file first. For files opened for * + * writing, then use the existing filename without performing a path search. * + * * + * INPUT: rights -- The access rights to use when opening the file * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(int rights) +{ + return(RawFileClass::Open(rights)); +} + + +/*********************************************************************************************** + * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:01AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Refresh_Search_Drives (void) +{ + Clear_Search_Drives(); + Set_Search_Drives(RawPath); +} + + + + + +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + * 05/21/1996 ST : Modified to recognise multiple CD drives * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = FALSE; + int empty = FALSE; + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + /* + ** Save the path as it was passed in so we can parse it again later. + ** Check for the case where RawPath was passed in. + */ + if (pathlist != RawPath){ + strcat (RawPath, ";"); + strcat (RawPath, pathlist); + } + + char const * ptr = strtok(pathlist, ";"); + while (ptr) { + if (strlen(ptr)){ + + char path[MAX_PATH]; // Working path buffer. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + */ + if (strncmp(path, "?:", 2) == 0) { + if (CurrentCDDrive){ + found = TRUE; + /* + ** If the drive has a C&C CD in it then add it to the path + */ + if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0){ + path[0] = CurrentCDDrive + 'A'; + Add_Search_Drive(path); + } + } + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + continue; + } + + found = TRUE; + Add_Search_Drive(path); + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} + + + +/*********************************************************************************************** + * CDFC::Set_CD_Drive -- sets the current CD drive letter * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:39AM ST : Created * + *=============================================================================================*/ + +void CDFileClass::Set_CD_Drive (int drive) +{ + LastCDDrive = CurrentCDDrive; + CurrentCDDrive = drive; +} + + + +/*********************************************************************************************** + * CDFC::Add_Search_Drive -- Add a new path to the search path list * + * * + * * + * * + * INPUT: path * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 10:12AM ST : Created * + *=============================================================================================*/ + +void CDFileClass::Add_Search_Drive(char *path) +{ + SearchDriveType *srch; // Working pointer to path object. + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } +} + + + + +/*********************************************************************************************** + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * * + * Use this routine to clear out any previous path(s) set with Set_Search_Drives() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void CDFileClass::Clear_Search_Drives(void) +{ + SearchDriveType * chain; // Working pointer to path chain. + + chain = First; + while (chain) { + SearchDriveType *next; + + next = (SearchDriveType *)chain->Next; + if (chain->Path) { + free((char *)chain->Path); + } + delete chain; + + chain = next; + } + First = 0; +} + + +/*********************************************************************************************** + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * * + * This routine will scan all the directories specified in the path list and if the file * + * was found in one of the directories, it will set the filename to a composite of the * + * correct directory and the filename. It is used to allow path searching when searching * + * for files. Typical use is to support CD-ROM drives. This routine examines the current * + * directory first before scanning through the path list. If after scanning the entire * + * path list, the file still could not be found, then the file object's name is set with * + * just the raw filename as passed to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +char const * CDFileClass::Set_Name(char const *filename) +{ + /* + ** Try to find the file in the current directory first. If it can be found, then + ** just return with the normal file name setting process. Do the same if there is + ** no multi-drive search path. + */ + RawFileClass::Set_Name(filename); + if (IsDisabled || !First || RawFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** Attempt to find the file first. Check the current directory. If not found there, then + ** search all the path specifications available. If it still can't be found, then just + ** fall into the normal raw file filename setting system. + */ + SearchDriveType * srch = First; + + while (srch) { + char path[_MAX_PATH]; + + /* + ** Build a pathname to search for. + */ + strcpy(path, srch->Path); + strcat(path, filename); + + /* + ** Check to see if the file could be found. The low level Is_Available logic will + ** prompt if necessary when the CD-ROM drive has been removed. In all other cases, + ** it will return false and the search process will continue. + */ + RawFileClass::Set_Name(path); + if (RawFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** It wasn't found, so try the next path entry. + */ + srch = (SearchDriveType *)srch->Next; + } + + /* + ** At this point, all path searching has failed. Just set the file name to the + ** plain text passed to this routine and be done with it. + */ + RawFileClass::Set_Name(filename); + return(File_Name()); +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file wherever it can be found. * + * * + * This routine is similar to the RawFileClass open except that if the file is being * + * opened only for READ access, it will search all specified directories looking for the * + * file. If after a complete search the file still couldn't be found, then it is opened * + * using the normal RawFileClass system -- resulting in normal error procedures. * + * * + * INPUT: filename -- Pointer to the override filename to supply for this file object. It * + * would be the base filename (sans any directory specification). * + * * + * rights -- The access rights to use when opening the file. * + * * + * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different * + * than requested. The location of the file can be determined by examining the * + * filename of this file object. The filename will contain the complete * + * pathname used to open the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(char const *filename, int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!filename) { + Error(ENOENT, false); + } + + /* + ** If writing is requested, then multiple drive searching is not performed. + */ + if (IsDisabled || rights == WRITE) { + return(RawFileClass::Open(filename, rights)); + } + + /* + ** Perform normal multiple drive searching for the filename and open + ** using the normal procedure. + */ + Set_Name(filename); + return(RawFileClass::Open(rights)); +} + + +#ifdef NEVER +/* Get the drive letters if the CD's online */ +WORD __cdecl GetCDDrive(VOID) +{ + _ES = FP_SEG(&cdDrive[0]); + _BX = FP_OFF(&cdDrive[0]); + _AX = 0x150d; + geninterrupt(0x2F); + return((WORD)(*cdDrive)); +} +#endif + +int Get_CD_Drive(void) +{ +#ifdef NEVER + for (int index = 0; index < 26; index++) { + union REGS regs; + + regs.w.ax = 0x150B; + regs.w.bx = 0; + regs.w.cx = index; + int386(0x2F, ®s, ®s); + if (regs.w.bx == 0xADAD) { + return(index); + } + } + return(0); +#else + +// GetCDClass temp; +// return(temp.GetCDDrive()); + return (0); +#endif + +} + + + +const char *CDFileClass::Get_Search_Path(int index) +{ + if (First == NULL) { + return NULL; + } + + SearchDriveType *sd = First; + + for (int i=0 ; i <= index ; i++) { // We want to loop once, even if index==0 + + if (i == index) { + return sd->Path; + } + + sd = (SearchDriveType *) sd->Next; + if (sd == NULL) { + return NULL; + } + } + + return NULL; +} \ No newline at end of file diff --git a/TIBERIANDAWN/CDFILE.H b/TIBERIANDAWN/CDFILE.H new file mode 100644 index 000000000..c8b0f13ae --- /dev/null +++ b/TIBERIANDAWN/CDFILE.H @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cdfile.h_v 2.20 16 Oct 1995 16:45:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood LIbrary * + * * + * File Name : CDFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CDFILE_H +#define CDFILE_H + +#include +#include "rawfile.h" + +/* +** This class is derived from the raw file class. This class adds the functionality of searching +** across multiple directories or drives. It is designed for the typical case of a CD-ROM game +** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM. +** Searching for the file occurs by first examining the current directory. If the file does not +** exist there, then all the paths available are examined in turn until the file can be found. +** For opening files to write, only the current directory is examined. The directory search order +** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path +** string is the same as the DOS path string. +*/ +class CDFileClass : public RawFileClass +{ + public: + CDFileClass(char const *filename); + CDFileClass(void); + virtual ~CDFileClass(void) {}; + + virtual char const * Set_Name(char const *filename); + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + + void Searching(int on) {IsDisabled = !on;}; + + static bool Is_There_Search_Drives(void) {return(First != NULL);}; + static int Set_Search_Drives(char * pathlist); + static void Add_Search_Drive(char *path); + static void Clear_Search_Drives(void); + static void Refresh_Search_Drives(void); + static void Set_CD_Drive(int drive); + static int Get_CD_Drive(void) {return(CurrentCDDrive);}; + static int Get_Last_CD_Drive(void) {return(LastCDDrive);}; + + // RawPath will overflow if we keep setting the path. ST - 1/23/2019 11:12AM + static void Reset_Raw_Path(void) {RawPath[0] = 0;} + + // Need to access the paths. ST - 3/15/2019 2:14PM + static const char *Get_Search_Path(int index); + + private: + + /* + ** Is multi-drive searching disabled for this file object? + */ + unsigned IsDisabled:1; + + /* + ** This is the control record for each of the drives specified in the search + ** path. There can be many such search paths available. + */ + typedef struct { + void * Next; // Pointer to next search record. + char const * Path; // Pointer to path string. + } SearchDriveType; + + /* + ** This points to the first path record. + */ + static SearchDriveType * First; + + /* + ** This is a copy of the unparsed search path list + */ + static char RawPath[512]; + + /* + ** The drive letter of the current cd drive + */ + static int CurrentCDDrive; + + /* + ** The drive letter of the last used CD drive + */ + static int LastCDDrive; +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/CELL.CPP b/TIBERIANDAWN/CELL.CPP new file mode 100644 index 000000000..d09a532c6 --- /dev/null +++ b/TIBERIANDAWN/CELL.CPP @@ -0,0 +1,2735 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cell.cpv 2.18 16 Oct 1995 16:49:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : August 17, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * CellClass::CellClass -- Constructor for cell objects. * + * CellClass::Cell_Building -- Return with building at specified cell. * + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * CellClass::Cell_Coord -- Returns the coordinate of this cell. * + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * CellClass::Cell_Occupier -- Fetches the occupier for the cell. * + * CellClass::Get_Trigger -- retrieves reference to the cell's trigger * + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * CellClass::Is_Generally_Clear -- Determines if cell can be built upon. * + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * CellClass::Occupy_Unit -- Marks cell as unit occupied. * + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * CellClass::Read -- Reads a particular cell value from a save game file. * + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * CellClass::Spot_Index -- returns cell sub-coord index for given COORD * + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * + * CellClass::Validate -- validates cell's number * + * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 3/26/2019 12:24PM +*/ +#include "SidebarGlyphx.h" + +#define FIXUP 0 + + +/*********************************************************************************************** + * CellClass::Validate -- validates cell's number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int CellClass::Validate(void) const +{ + int num; + + num = Cell_Number(); + if (num < 0 || num > 4095) { + Validate_Error("CELL"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * CellClass::CellClass -- Constructor for cell objects. * + * * + * A cell object is constructed into an empty state. It contains no specific objects, * + * templates, or overlays. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/09/1994 JLB : Created. * + *=============================================================================================*/ +CellClass::CellClass(void) +{ + memset(this, 0, sizeof(CellClass)); + Overlay = OVERLAY_NONE; + Smudge = SMUDGE_NONE; + TType = TEMPLATE_NONE; + Owner = HOUSE_NONE; + InfType = HOUSE_NONE; + OverrideLand = LAND_COUNT; +} + + +/*********************************************************************************************** + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * * + * Use this routine to determine what radar color to render a radar * + * pixel with. This routine is called many many times to render the * + * radar map, so it must be fast. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the color to display the radar pixel with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * + *=============================================================================================*/ +int CellClass::Cell_Color(bool override) const +{ + Validate(); + BuildingClass * object = Cell_Building(); + if (object) { + return(object->House->Class->Color); + } + + if (override) { + return(TBLACK); + } + return(::Ground[Land_Type()].Color); +} + + +/*********************************************************************************************** + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * * + * Returns an object located in the cell. If there is a * + * building present, it returns a pointer to that, otherwise it returns * + * a pointer to one of the units there. If nothing is present in the * + * specified cell, then it returns NULL. * + * * + * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * + * the desired object within the cell. * + * * + * OUTPUT: Returns a pointer to a building or unit located in cell. If * + * nothing present, just returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +TechnoClass * CellClass::Cell_Techno(int x, int y) const +{ + Validate(); + ObjectClass * object; + COORDINATE click; // Coordinate of click relative to cell corner. + TechnoClass * close = NULL; + long distance = 0; // Recorded closest distance. + + /* + ** Create a coordinate value that represent the pixel location within the cell. This is + ** actually the lower significant bits (leptons) of a regular coordinate value. + */ + click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); + + if (Cell_Occupier()) { + object = Cell_Occupier(); + while (object) { + if (object->Is_Techno()) { + COORDINATE coord; // Coordinate relative to cell corner. + long dist; + + coord = object->Center_Coord() & 0x00FF00FFL; + dist = Distance(coord, click); + if (!close || dist < distance) { + close = (TechnoClass *)object; + distance = dist; + } + } + object = object->Next; + } + } + return(close); +} + + +/*************************************************************************** + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * * + * INPUT: RTTIType the RTTI type we are searching for * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 PWG : Created. * + * 06/12/1995 JLB : Returns object class pointer. * + *=========================================================================*/ +ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const +{ + Validate(); + ObjectClass * object = Cell_Occupier(); + + while (object) { + if (object->What_Am_I() == rtti) { + return(object); + } + object = object->Next; + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Cell_Building -- Return with building at specified cell. * + * * + * Given a cell, determine if there is a building associated * + * and return with a pointer to this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building associated with the * + * cell. If there is no building associated, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +BuildingClass * CellClass::Cell_Building(void) const +{ + Validate(); + return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * * + * This routine is used to determine the terrain object (if any) that * + * overlaps this cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object that overlaps * + * this cell. If there is no terrain object present, then NULL * + * is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass * CellClass::Cell_Terrain(void) const +{ + Validate(); + return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * * + * This routine is used to determine which object is to be selected * + * by a player click upon the cell. Not all objects that overlap the * + * cell are selectable by the player. This routine sorts out which * + * is which and returns with the appropriate object pointer. * + * * + * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * + * selecting the object within the cell. This plays a role in those cases * + * where several objects (such as infantry) exist within the same cell. * + * * + * OUTPUT: Returns with pointer to the object clickable within the * + * cell. NULL is returned if there is no clickable object * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Object(int x, int y) const +{ + Validate(); + ObjectClass * ptr; + + /* + ** Hack so that aircraft landed on helipads can still be selected if directly + ** clicked on. + */ + ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); + if (ptr) { + return(ptr); + } + + ptr = Cell_Techno(x, y); + if (ptr) { + return(ptr); + } + ptr = Cell_Terrain(); + if (ptr) return(ptr); + return(ptr); +} + + +/*********************************************************************************************** + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * * + * This is a low level routine that marks all objects that overlap this * + * cell to be redrawn. It is necessary to call this routine whenever * + * the underlying icon has to be redrawn. * + * * + * INPUT: forced -- Should this redraw be forced even if flags * + * indicate that it would be redundant? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/20/1994 JLB : Simplified to use object pointers. * + * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * + *=============================================================================================*/ +void CellClass::Redraw_Objects(bool forced) +{ + Validate(); + CELL cell = Cell_Number(); + + if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { + + /* + ** Flag the icon to be redrawn. + */ + Map.Flag_Cell(cell); + + /* + ** Flag the main object in the cell to be redrawn. + */ + if (Cell_Occupier()) { + ObjectClass * optr = Cell_Occupier(); + while (optr) { + optr->Mark(MARK_CHANGE); + optr = optr->Next; + } + } + + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index]) { + if (!Overlapper[index]->IsActive) { + Overlapper[index] = 0; + } else { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } + } +} + + +/*********************************************************************************************** + * CellClass::Is_Generally_Clear -- Determines if cell can be built upon. * + * * + * This determines if the cell can become a proper foundation for * + * building placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the cell be built upon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Is_Generally_Clear(bool ignore_cloaked) const +{ + Validate(); + if (ScenarioInit) return(true); + if (Flag.Composite || IsFlagged || Cell_Occupier()) { + if (!ignore_cloaked || (Cell_Occupier() && Cell_Occupier()->Is_Techno() && ((TechnoClass *)Cell_Occupier())->Cloak != CLOAKED)) { + return(false); + } + } + if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib && Owner != HOUSE_NONE) { + return(false); + } + if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || OverlayTypeClass::As_Reference(Overlay).IsWall)) { + return(false); + } + +#ifdef ADVANCED + /* + ** Building certain kinds of terrain is prohibited -- bridges in particular. + */ + switch (TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1D: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2D: + case TEMPLATE_BRIDGE3: + case TEMPLATE_BRIDGE3D: + case TEMPLATE_BRIDGE4: + case TEMPLATE_BRIDGE4D: + case TEMPLATE_FORD1: + case TEMPLATE_FORD2: + return(false); + + default: + break; + } +#endif + + return(::Ground[Land_Type()].Build); +} + + +/*********************************************************************************************** + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * * + * This routine recalculates the ground type in the cell. The speeds the find path * + * algorithm and other determinations of the cell type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 06/20/1994 JLB : Knows about template pointer in cell object. * + *=============================================================================================*/ +void CellClass::Recalc_Attributes(void) +{ + Validate(); + /* + ** Check for wall effects. + */ + if (Overlay != OVERLAY_NONE) { + Land = OverlayTypeClass::As_Reference(Overlay).Land; + if (Land != LAND_CLEAR) return; + } + + /* + ** If there is a template associated with this cell, then scan + ** through the template exception list checking to see if this cell + ** is one of the exception types. If it is, then return that ground type, + ** otherwise return the template's default type. + */ + if (TType != TEMPLATE_NONE) { + TemplateTypeClass const *ttype = &TemplateTypeClass::As_Reference(TType); + + /* + ** If there is an exception type list for the icons of this template, then + ** find out if the current icon is one of them. If so, apply the exception + ** ground type to the cell. + */ + char const *ptr = ttype->AltIcons; + if (ptr) { + int icon = TIcon; + + while (*ptr != -1) { + if (icon == *ptr++) { + Land = ttype->AltLand; + return; + } + } + } + + /* + ** No exception found, so just return the default ground type for this template. + */ + Land = ttype->Land; + return; + } + + /* + ** No template is the same as clear terrain. + */ + Land = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Land; +} + + +/*********************************************************************************************** + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * * + * This routine is used to mark the cell as being occupied by the specified object. * + * * + * INPUT: object -- The object that is to occupy the cell * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Occupy_Down(ObjectClass * object) +{ + Validate(); + ObjectClass * optr; + + /* + ** If the specified object is already part of the occupation list, then don't add + ** it again -- bail instead. + */ + if (Cell_Occupier()) { + optr = Cell_Occupier(); + while (optr) { + if (optr == object) { + return; + } + if (!optr->Next) break; + optr = optr->Next; + } + } + optr = Cell_Occupier(); + object->Next = optr; + + OccupierPtr = object; + Map.Radar_Pixel(Cell_Number()); + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + // Changes for GlyphX multiplayer. ST - 4/17/2019 3:02PM + //if (IsVisible || GameToPlay != GAME_NORMAL) { + // object->Revealed(PlayerPtr); + //} + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { + if (IsVisible || GameToPlay != GAME_NORMAL) { + object->Revealed(PlayerPtr); + } + } else { + + for (int i = 0; i < MPlayerCount; i++) { + HousesType house_type = MPlayerHouses[i]; + if (Is_Visible(house_type)) { + HouseClass *house = HouseClass::As_Pointer(house_type); + object->Revealed(house); + } + } + } + + /* + ** Special occupy bit set. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = true; + break; + + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = true; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = true; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * * + * This routine will lift the object from the cell and free the cell to be occupied by * + * another object. Only if the cell was previously marked with the object specified, will * + * the object be lifted off. This routine is the counterpart to Occupy_Down(). * + * * + * INPUT: object -- The object that is being lifted off. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * + *=============================================================================================*/ +void CellClass::Occupy_Up(ObjectClass * object) +{ + Validate(); + ObjectClass * optr = NULL; // Working pointer to the objects in the chain. + + if (Cell_Occupier()) { + optr = Cell_Occupier(); + } + if (optr == object) { + OccupierPtr = object->Next; + object->Next = 0; + } else { + while (optr) { + if (optr->Next == object) { + optr->Next = object->Next; + object->Next = 0; + break; + } + if (!optr->Next) break; + optr = optr->Next; + } + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** Special occupy bit clear. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = false; + break; + + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = false; +#ifdef NEVER + int x,y; + if (Map.Coord_To_Pixel(Cell_Coord(), x, y)) { + SeenBuff.Put_Pixel(x, y, BLUE); + } +#endif + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = false; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * * + * Most game objects can often have their graphic imagery spill into more than one cell * + * even though they are considered to "occupy" only one cell. All cells overlapped are * + * flagged by this routine. Using this information it is possible to keep the tactical map * + * display correct. * + * * + * INPUT: object -- The object to mark as overlapping this cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 07/04/1995 JLB : Ensures that buildings are always marked down. * + *=============================================================================================*/ +void CellClass::Overlap_Down(ObjectClass * object) +{ + Validate(); + ObjectClass **ptr = 0; + + if (!object) return; + int index; + for (index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index] == object) return; + if (!Overlapper[index]) ptr = &Overlapper[index]; + } + + /* + ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody + ** else out in this case. + */ + if (!ptr && object->What_Am_I() == RTTI_BUILDING) { + for (index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + switch (Overlapper[index]->What_Am_I()) { + case RTTI_BUILDING: + case RTTI_TERRAIN: + break; + + default: + Overlapper[index] = object; + index = sizeof(Overlapper)/sizeof(Overlapper[0]); + break; + } + } + } + if (ptr) *ptr = object; + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + // Changes for GlyphX multiplayer. ST - 4/18/2019 9:50AM + //if (IsVisible) { + // object->Revealed(PlayerPtr); + //} + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { + if (IsVisible) { + object->Revealed(PlayerPtr); + } + } else { + + if (object->Is_Techno()) { + TechnoClass *tech = static_cast(object); + object->Revealed(tech->House); + } else { + + for (int i = 0; i < MPlayerCount; i++) { + HousesType house_type = MPlayerHouses[i]; + HouseClass *house = HouseClass::As_Pointer(house_type); + object->Revealed(house); + } + } + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * * + * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * + * specified unit on the cell. * + * * + * INPUT: object -- The object to remove the overlap flag for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Overlap_Up(ObjectClass *object) +{ + Validate(); + for (int index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index] == object) { + Overlapper[index] = 0; + break; + } + } + + //Map.Validate(); +} + + +/*********************************************************************************************** + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * * + * This routine will determine if a unit is occupying the cell and if so, return a pointer * + * to it. If there is no unit occupying the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * CellClass::Cell_Unit(void) const +{ + Validate(); + return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * + * * + * This routine examines the cell and returns a pointer to the first infantry unit * + * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * CellClass::Cell_Infantry(void) const +{ + Validate(); + return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); +} + + + +/*********************************************************************************************** + * CellClass::Get_Template_Info -- Get some info about a template for external use * + * * + * * + * * + * * + * INPUT: Ref to info required * + * * + * OUTPUT: True if image info available * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/10/2019 5:57PM ST : Created. * + *=============================================================================================*/ +bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data) +{ + TemplateTypeClass const *ttype = NULL; + + if (TType != TEMPLATE_NONE) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + if (ttype) { + + strcpy(template_name, ttype->IniName); + image_data = (void*) ttype->ImageData; + + return true; + } + + return false; +} + + + + +/*********************************************************************************************** + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * * + * This is the gruntwork cell rendering code. It draws the cell at the screen location * + * specified. This routine doesn't draw any overlapping or occupying units. It only * + * deals with the ground (cell) layer -- icon level. * + * * + * INPUT: x,y -- The screen coordinates to render the cell imagery at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 08/21/1994 JLB : Revised for simple template objects. * + * 11/01/1994 BRR : Updated placement cursor; draws actual object * + * 11/14/1994 BRR : Added remapping code to show passable areas * + * 12/02/1994 BRR : Added trigger display * + * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * + * 04/25/1995 JLB : Smudges drawn BELOW overlays. * + *=============================================================================================*/ +void CellClass::Draw_It(int x, int y, int draw_type) const +{ + Validate(); + TemplateTypeClass const *ttype = 0; + int icon; // The icon number to use from the template set. + CELL cell = Cell_Number(); + void * remap = NULL; +#ifdef SCENARIO_EDITOR + TemplateTypeClass * tptr; + TriggerClass * trig; + int i; + char waypt[2]; +#endif + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (TType != TEMPLATE_NONE) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + + /* + ** Draw the stamp of the template. + */ + if (Debug_Icon) { + LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); + FontXSpacing -= 2; + Fancy_Text_Print("%d\r%2X%c\r%02X.%02X", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, WHITE, TBLACK, TPF_6POINT|TPF_NOSHADOW|TPF_CENTER, cell, Flag.Composite, (Cell_Occupier() ? '*' : ' '), Overlay, OverlayData); + FontXSpacing += 2; + } else { + + + if (!draw_type || draw_type == CELL_BLIT_ONLY){ + + +#ifdef SCENARIO_EDITOR + /* + ** Set up the remap table for this icon. + */ + if (Debug_Map && Debug_Passable) { + if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && + Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable + remap = Map.FadingRed; + } else { + if (::Ground[Land].Cost[0] > 0x70) { // pretty passable + remap = Map.FadingGreen; + } else { + remap = Map.FadingYellow; // moderately passable + } + } + } +#endif + + // ****** maybe this icon shouldn't be drawn if it is known that the cell will be covered + // with shadow. + /* + ** This is the underlying terrain icon. + */ + if (ttype->Get_Image_Data()) { + LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + if (remap) { + LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); + } + } + + +#ifdef SCENARIO_EDITOR + /* + ** Draw the map editor's "current" cell. This is the cell that can be + ** assigned attributes such as tag labels. + ** This must be draw before the placement cursor, but after drawing the + ** objects in the cell. + */ + if (Debug_Map && CurrentCell == Cell_Number()) { + LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); + } +#endif + +#ifdef NEVER + /* + ** Special concrete render. It always renders over the underlying + ** terrain unless this concrete piece will cover the entire terrain + ** piece. + */ + if (Concrete) { + LogicPage->Draw_Stamp(TemplateTypeClass::As_Pointer(TEMPLATE_CONCRETE_GDI)->Get_Image_Data(), Concrete-1, x, y, NULL, WINDOW_TACTICAL); + } +#endif + } + + /* + ** Redraw any smudge. + */ + if (Smudge != SMUDGE_NONE) { +#ifdef NEVER + switch (Smudge) { + case SMUDGE_BIB1: + CC_Draw_Shape(Bib1, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + + case SMUDGE_BIB2: + CC_Draw_Shape(Bib2, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + + case SMUDGE_BIB3: + CC_Draw_Shape(Bib3, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + } +#endif + SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + } + + + if (!draw_type || draw_type == CELL_DRAW_ONLY){ + + /* + ** Draw the overlay object. + */ + if (Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + IsTheaterShape = (bool)otype.IsTheater; + CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + IsTheaterShape = false; + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map) { + /* + ** Draw the cell's Trigger mnemonic, if it has a trigger + */ + if (IsTrigger) { + trig = Get_Trigger(); + Fancy_Text_Print(trig->Get_Name(), x+Map.TacPixelX, y+Map.TacPixelY, PINK, TBLACK, TPF_NOSHADOW|TPF_6POINT); + } + + /* + ** Draw the cell's Waypoint designation if there is one. + */ + if (IsWaypoint) { + for (i = 0; i < 26; i++) { + if (Waypoint[i]==Cell_Number()) { + waypt[0] = 'A' + i; + waypt[1] = 0; + Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, + Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, YELLOW, TBLACK, + TPF_NOSHADOW | TPF_6POINT | TPF_CENTER); + break; + } + } + if (Waypoint[WAYPT_HOME] == Cell_Number()) { + Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + WHITE, TBLACK, TPF_NOSHADOW | TPF_6POINT); + } + if (Waypoint[WAYPT_REINF] == Cell_Number()) { + Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + WHITE, TBLACK, TPF_NOSHADOW | TPF_6POINT); + } + } + } + #endif + + /* + ** Draw the placement cursor: + ** - First, draw the hash-mark cursor, so it will appear underneath + ** any cursor being drawn + ** - If the PendingObject is a template, overlay, or smudge, draw it + ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it + */ + if (IsCursorHere) { + + /* + ** Draw the hash-mark cursor: + */ + if (Map.ProximityCheck && Is_Generally_Clear()) { + LogicPage->Draw_Stamp(Map.TransIconset, 0, x, y, NULL, WINDOW_TACTICAL); + } else { + LogicPage->Draw_Stamp(Map.TransIconset, 2, x, y, NULL, WINDOW_TACTICAL); + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map && Map.PendingObject) { + + switch (Map.PendingObject->What_Am_I()) { + + /* + ** Draw a template: + ** - Compute the icon offset of this cell for this template, using + ** ZoneCell+ZoneOffset to get the upper-left corner of the placement + ** cursor + ** - Draw the icon + */ + case RTTI_TEMPLATETYPE: + tptr = (TemplateTypeClass *)Map.PendingObject; + if (tptr->Get_Image_Data()) { + icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + + (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * + tptr->Width; + LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + } + break; + + /* + ** Draw an overlay; just use the existing 'OverlayData' even though + ** it means nothing. + */ + case RTTI_OVERLAYTYPE: + OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); + break; + + /* + ** Draw a smudge + */ + case RTTI_SMUDGETYPE: + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + break; + } + } + #endif + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (IsFlagged) { + void const * remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, false); + CC_Draw_Shape(MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, remap, Map.UnitShadow); + } + } + } +} + + +/*********************************************************************************************** + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * * + * This routine examines the cells around the current one and from this, determines what * + * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * + * for redraw if the icon changed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Concrete_Calc(void) +{ + Validate(); + static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; + static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; + FacingType *ptr; // Working pointer into adjacent cell list. + int index; // Constructed bit index. + int icon; // Icon number. + bool isodd; // Is this for the odd column? + +#define OF_N 0x01 +#define OF_NE 0x02 +#define OF_E 0x04 +#define OF_SE 0x08 +#define OF_S 0x10 + +#define EF_N 0x01 +#define EF_NW 0x10 +#define EF_W 0x08 +#define EF_SW 0x04 +#define EF_S 0x02 + + /* + ** Determine if the even or odd row logic is necessary. + */ + isodd = ((Cell_Number() & 0x01) != 0); + + /* + ** Fetch correct pointer depending on whether this is for an + ** odd or even row. + */ + ptr = (isodd) ? _odd : _even; + + /* + ** Build an index according to the presence of concrete in the special + ** adjacent cells. This is a short list of adjacent cell flags since + ** only 5 adjacent cells need to be examined. The choice of which 5 + ** depends on whether this is for an even or odd column. + */ + index = 0; + for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { + CellClass & cellptr = Adjacent_Cell(*ptr++); + +// if ((cellptr->IsConcrete) || +// cellptr->Concrete == C_UPDOWN_RIGHT || +// cellptr->Concrete == C_UPDOWN_LEFT) { + + if (cellptr.Overlay == OVERLAY_CONCRETE) { + index |= (1< levels) { + OverlayData -= levels; + reducer = levels; + } else { + Overlay = OVERLAY_NONE; + reducer = OverlayData; + OverlayData = 0; + Recalc_Attributes(); + } + } + return(reducer); +} + + +/*********************************************************************************************** + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * * + * This routine will change the wall shape used for a wall if it's damaged. * + * * + * * + * INPUT: damage -- The number of damage points the wall was hit with. * + * * + * OUTPUT: bool; Was the wall destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BWG : Created. * + * 03/19/1995 JLB : Updates cell information if wall was destroyed. * + *=============================================================================================*/ +int CellClass::Reduce_Wall(int damage) +{ + Validate(); + if (Overlay != OVERLAY_NONE) { + bool destroyed = false; + OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); + + if (wall.IsWall) { + + /* + ** If the damage was great enough to ensure wall destruction, reduce the wall by one + ** level (no more). Otherwise determine wall reduction based on a percentage chance + ** proportional to the damage received and the wall's strength. + */ + if (damage >= wall.DamagePoints) { + destroyed = true; + } else { + destroyed = Random_Pick(0, wall.DamagePoints) < damage; + } + + if (destroyed) { + OverlayData+=16; + if ((OverlayData>>4) >= wall.DamageLevels) { + ObjectClass::Detach_This_From_All(As_Target()); + Owner = HOUSE_NONE; + Overlay = OVERLAY_NONE; + OverlayData = 0; + Recalc_Attributes(); + Redraw_Objects(); + Adjacent_Cell(FACING_N).Wall_Update(); + Adjacent_Cell(FACING_W).Wall_Update(); + Adjacent_Cell(FACING_S).Wall_Update(); + Adjacent_Cell(FACING_E).Wall_Update(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Get_Trigger -- retrieves reference to the cell's trigger * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TriggerClass reference * + * * + * WARNINGS: * + * Never call this function unless the IsTrigger flag is set for the cell! * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass * CellClass::Get_Trigger(void) const +{ + Validate(); + if (IsTrigger) { + return(CellTriggers[Cell_Number()]); + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Spot_Index -- returns cell sub-coord index for given COORD * + * * + * INPUT: * + * coord COORD to compute index for * + * * + * OUTPUT: * + * index into StoppingCoord that's closest to this coord * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1994 BR : Created. * + * 12/10/1994 JLB : Uses alternate sub-position algorithm. * + *=============================================================================================*/ +int CellClass::Spot_Index(COORDINATE coord) +{ + COORDINATE rel = coord & 0x00FF00FFL; // Sub coordinate value within cell. + + /* + ** If the coordinate is close enough to the center of the cell, then return + ** the center position index. + */ + if (Distance(rel, 0x00800080L) < 60) { + return(0); + } + + /* + ** Since the center cell position has been eliminated, a simple comparison + ** as related to the center of the cell can be used to determine the sub + ** position. Take advantage of the fact that the sub positions are organized + ** from left to right, top to bottom. + */ + int index = 0; + if (Coord_X(rel) > 0x80) index |= 0x01; + if (Coord_Y(rel) > 0x80) index |= 0x02; + return(index+1); +} + + +/*********************************************************************************************** + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * * + * Similar to the CellClass::Free_Spot; this routine finds the spot in * + * the cell closest to the given coordinate, and returns the COORD of * + * that spot if it's available, NULL if it's not. * + * * + * INPUT: * + * coord coordinate to check (only sub cell position examined) * + * * + * any -- If only the closest spot is desired regardless of whether it is free or * + * not, then this parameter will be true. * + * * + * OUTPUT: * + * COORD of free spot, NULL if none. The coordinate return value does not alter the cell * + * coordinate data portions of the coordinate passed in. Only the lower sub-cell * + * data is altered. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 12/10/1994 JLB : Picks best of closest stopping positions. * + * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * + *=============================================================================================*/ +COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + Validate(); + int spot_index = Spot_Index(coord); + + /* + ** This precalculated sequence table records the closest spots to any given spot. Sequential + ** examination of these spots for availability ensures that the closes available one is + ** discovered first. + */ + static unsigned char _sequence[5][4] = { + {1,2,3,4}, + {0,2,3,4}, + {0,1,4,3}, + {0,1,4,2}, + {0,2,3,1} + }; + + /* + ** In the case of the center coordinate being requested, but is occupied, then all other + ** sublocations are equidistant. Instead of picking a static sequence of examination, the + ** order is mixed up by way of this table. + */ + static unsigned char _alternate[4][4] = { + {1,2,3,4}, + {2,3,4,1}, + {3,4,1,2}, + {4,1,2,3}, + }; + coord &= 0xFF00FF00L; + + /* + ** Cells occupied by buildings or vehicles don't have any free spots. + */ + if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { + return(NULL); + } + + /* + ** If just the nearest position is desired regardless of whether occupied or not, + ** then just return with the stopping coordinate value. + */ + if (any || Is_Spot_Free(spot_index)) { + return(Coord_Add(coord, StoppingCoordAbs[spot_index])); + } + + /* + ** Scan through all available sub-locations in the cell in order to determine + ** the closest one to the coordinate requested. Use precalculated table so that + ** when the first free position is found, bail. + */ + unsigned char *sequence; + if (spot_index == 0) { + sequence = &_alternate[Random_Pick(0,3)][0]; + } else { + sequence = &_sequence[spot_index][0]; + } + for (int index = 0; index < 4; index++) { + int pos = *sequence++; + + if (Is_Spot_Free(pos)) { + return(Coord_Add(coord, StoppingCoordAbs[pos])); + } + } + + /* + ** No free spot could be found so return a NULL coordinate. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * * + * This support routine will determine what the clear icon number would be for the cell. * + * The icon number is determined by converting the cell number into an index into a * + * lookup table. This yields what appears to be a randomized map without the necessity of * + * generating and recording randomized map numbers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * + *=============================================================================================*/ +int CellClass::Clear_Icon(void) const +{ + Validate(); + CELL cell = Cell_Number(); + return((cell & 0x03) | ((cell>>4) & 0x0C)); +} + + +/*********************************************************************************************** + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * * + * This routine is called whenever a great, but slow moving, threat is presented to the * + * occupants of a cell. The occupants will, in most cases, stop what they are doing and * + * try to get out of the way. * + * * + * INPUT: threat -- The coordinate source of the threat. * + * * + * forced -- If this threat is so major that the occupants should stop what * + * they are doing, then this parameter should be set to true. * + * * + * nokidding -- Override the scatter to also affect human controlled objects. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding) +{ + Validate(); + ObjectClass * object = NULL; + + object = Cell_Occupier(); + while (object) { + + /* + ** Special check to make sure that friendly units never scatter. + */ + if (nokidding || Special.IsScatter || (object->Is_Techno() && !((TechnoClass *)object)->House->IsHuman)) { + if (object->What_Am_I() == RTTI_INFANTRY) { + object->Scatter(threat, forced, nokidding); + } else { + object->Scatter(threat, forced, nokidding); +// object->Scatter(threat, false); + } + } + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * * + * Use this routine to return a reference to the adjacent cell in the direction specified. * + * * + * INPUT: face -- The direction to use when determining the adjacent cell. * + * * + * OUTPUT: Returns with a reference to the adjacent cell. * + * * + * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +CellClass const & CellClass::Adjacent_Cell(FacingType face) const +{ + Validate(); + if ((unsigned)face >= FACING_COUNT) { + return(*this); + } + + CELL id = Cell_Number(); + //The top row doesn't have any adjacent cells to the north. - LLL + if (id < MAP_CELL_W && (face == FACING_N || face == FACING_NE || face == FACING_NW)) { + return (*this); + } + + //The bottom row doesn't have any adjacent cells to the south. - LLL + if ((id > MAP_CELL_TOTAL - MAP_CELL_W) && (face == FACING_S || face == FACING_SE || face == FACING_SW)) { + return (*this); + } + + CellClass const * ptr = this + AdjacentCell[face]; + if (ptr->Cell_Number() & 0xF000) return(*this); + return(*ptr); +// return(*(this + AdjacentCell[face])); +} + + +/*************************************************************************** + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/24/1995 PWG : Created. * + *=========================================================================*/ +void CellClass::Adjust_Threat(HousesType house, int threat_value) +{ + Validate(); + int region = Map.Cell_Region(Cell_Number()); + + for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { + if (lp == house) continue; + + HouseClass *house_ptr = HouseClass::As_Pointer(lp); + if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { + house_ptr->Adjust_Threat(region, threat_value); + } + } + if (Debug_Threat) { + Map.Flag_To_Redraw(true); + } +} + + +/*********************************************************************************************** + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * + * * + * This routine will adjust the level of the Tiberium in the cell so that it will * + * smoothly blend with the adjacent Tiberium. This routine should only be called for * + * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * + * pattern. * + * * + * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * + * used. * + * * + * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * + * * + * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * + * increase the net worth of the existing Tiberium. * + * * + * HISTORY: * + * 05/16/1995 JLB : Created. * + *=============================================================================================*/ +long CellClass::Tiberium_Adjust(bool pregame) +{ + Validate(); + if (Overlay != OVERLAY_NONE) { + if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { + static int _adj[9] = {0,1,3,4,6,7,8,10,11}; + int count = 0; + + /* + ** Mixup the Tiberium overlays so that they don't look the same. + */ + if (pregame) { + Overlay = Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12); + } + + /* + ** Add up all adjacent cells that contain tiberium. + ** (Skip those cells which aren't on the map) + */ + for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { + CELL cell = Cell_Number() + AdjacentCell[face]; + if ((unsigned)cell >= MAP_CELL_TOTAL) continue; + + CellClass & adj = Adjacent_Cell(face); + + if (adj.Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) { + count++; + } + } + + OverlayData = _adj[count]; + return((OverlayData+1) * UnitTypeClass::TIBERIUM_STEP); + } + } + return(0); +} + + +/*********************************************************************************************** + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * * + * Call this routine whenever an object enters a cell. It will check for the existance * + * of a crate and generate any "goodie" it might contain. * + * * + * INPUT: object -- Pointer to the object that is triggering this crate. * + * * + * OUTPUT: Can the object continue to enter this cell? A false return value means that the * + * cell is now occupied and must not be entered. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + * 07/08/1995 JLB : Added a bunch of goodies to the crates. * + *=============================================================================================*/ +bool CellClass::Goodie_Check(FootClass * object) +{ + Validate(); + enum { + MONEY, // Cash award. + UNIT, // A free unit. + NUKE, // A nuclear device that explodes. + ION, // Calls forth an ion blast on discoverer. + NUKE_MISSILE, // Gets a one time nuclear missile options. + ION_BLAST, // Gets a one time ion blast option. + AIR_STRIKE, // Gets a one time air strike option. + HEAL_BASE, // Heals the player's entire base. + CLOAK, // Units in region gain cloak ability. + EXPLOSION, // Conventional explosion. + NAPALM, // A napalm explosion. + SQUAD, // A mixed squad of friendly infantry appear. + VISCEROID, // A visceroid appears! + DARKNESS, // Shroud the entire map. + REVEAL, // Reveal the entire map. + TOTAL_CRATES, + }; + static int _what[] = { + DARKNESS,DARKNESS, + REVEAL,REVEAL, + NUKE, +// ION,ION, + NUKE_MISSILE, + ION_BLAST,ION_BLAST, + AIR_STRIKE,AIR_STRIKE,AIR_STRIKE,AIR_STRIKE, + HEAL_BASE,HEAL_BASE, + CLOAK,CLOAK, + EXPLOSION,EXPLOSION,EXPLOSION,EXPLOSION, + NAPALM,NAPALM,NAPALM, + SQUAD,SQUAD,SQUAD,SQUAD,SQUAD, + UNIT,UNIT,UNIT,UNIT,UNIT, + VISCEROID + }; + + + /* + ** Crate types are only defined here so it needs to match my new global crate total ST - 6/4/96 2:16PM + */ +#if (TOTAL_CRATES) == (TOTAL_CRATE_TYPES) + +#else + //Huge_Errrrror..... Oh NO! + +#endif + + + if (object && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { + bool steel = (Overlay == OVERLAY_STEEL_CRATE); + COORDINATE coord; // Temporary working coordinate value. + + /* + ** A triggered crate is automatically destroyed regardless of who or how + ** it was triggered. + */ + Redraw_Objects(); + Overlay = OVERLAY_NONE; + OverlayData = 0; + + if (steel) { + if (object->Owner() == HOUSE_BAD) { + object->House->Add_Nuke_Piece(); + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + } + + } else { + + int index; + UnitClass * unit = 0; + unsigned damage = 0; + int what = MONEY; + + if (GameToPlay != GAME_NORMAL && (Random_Pick(1, 2) == 1 || !object->House->BScan)) { + what = -1; + + /* + ** If the player has a construction yeard and practically no money and no legitmate means + ** to make any more money, then give money to build a refinery. + */ + if ((object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && + object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost) { + + what = MONEY; + } + + /* + ** If the player should get an MCV replacement, then give it now (probably). + */ + if (Random_Pick(0, 1) == 0 && + MPlayerBases && + !(object->House->UScan & UNITF_MCV) && + object->House->BScan == 0 && + object->House->Available_Money() > ((BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias)) { + + what = UNIT; + } + + bool allow_super = true; + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER && !Rule.AllowSuperWeapons) { + allow_super = false; + } + + while (what == -1) { + what = _what[Random_Pick((unsigned)0, sizeof(_what)/sizeof(_what[0])-1)]; + + if (what == REVEAL && object->House->IsVisionary) what = -1; + if (what == AIR_STRIKE && (!allow_super || object->House->AirStrike.Is_Present())) what = -1; + if (what == NUKE_MISSILE && (!allow_super || object->House->NukeStrike.Is_Present())) what = -1; + if (what == ION_BLAST && (!allow_super || object->House->IonCannon.Is_Present())) what = -1; + if (what == CLOAK && object->IsCloakable) what = -1; + } + } + + /* + ** Keep track of the number of each type of crate found + */ + if (GameToPlay == GAME_INTERNET){ + object->House->TotalCrates->Increment_Unit_Total(what); + } + + /* + ** Update the crate count and when all the crates have been discovered, flag + ** to generate a new one. + */ + CrateCount--; + if (!CrateMaker && CrateCount <= 0 && GameToPlay != GAME_NORMAL) { + CrateMaker = true; + CrateTimer = 1; + } + + /* + ** Create the effect requested. + */ + switch (what) { + default: + + /* + ** Give the player money. + */ + case MONEY: + new AnimClass(ANIM_CRATE_DOLLAR, Cell_Coord()); + if (GameToPlay == GAME_NORMAL) { + HouseClass::As_Pointer(object->Owner())->Refund_Money(2000); + } else { + HouseClass::As_Pointer(object->Owner())->Refund_Money(100 + (Random_Pick(0,19)*100)); + } + break; + + /* + ** Shroud the world in blackness. + */ + case DARKNESS: + { + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + bool shroud = false; + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { + if (object->House == PlayerPtr) { + shroud = true; + } + } else { + if (object->House->IsHuman) { + shroud = true; + } + } + if (shroud) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->Is_Mapped(object->House) || cellptr->Is_Visible(object->House)) { + cellptr->Redraw_Objects(); + cellptr->Set_Mapped(object->House, false); + cellptr->Set_Visible(object->House, false); + } + } + HouseClass *find_house = object->House; + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Map.Layer[LAYER_GROUND][index]; + if (object && object->Is_Techno() && ((TechnoClass *)object)->House == find_house) { + object->Look(); + } + } + Map.Flag_To_Redraw(true); + } + break; + } + /* + ** Reveal the entire map. + */ + case REVEAL: + { + bool reveal = false; + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { + if (object->House == PlayerPtr) { + reveal = true; + } + } else { + if (object->House->IsHuman) { + reveal = true; + } + } + + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + object->House->IsVisionary = true; + if (reveal) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, object->House, true); + } + } + break; + } + /* + ** Try to create a unit where the crate was. + */ + case UNIT: { + UnitTypeClass const * utp = NULL; + + /* + ** Give the player an MCV if he has no base left but does have more than enough + ** money to rebuild a new base. Of course, if he already has an MCV, then don't + ** give him another one. + */ + if (MPlayerBases && + !(object->House->UScan & UNITF_MCV) && + object->House->BScan == 0 && + object->House->Available_Money() > (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost)) { + + utp = &UnitTypeClass::As_Reference(UNIT_MCV); + } + + /* + ** If the player has a base and a refinery, but no harvester, then give him + ** a free one. + */ + if (!utp && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { + utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + } + + while (!utp) { + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); + if (utype != UNIT_MCV || MPlayerBases) { + utp = &UnitTypeClass::As_Reference(utype); + if (utp->IsCrateGoodie && (utp->Ownable & (1 << object->Owner())) && utp->Level <= BuildLevel+2) break; + utp = NULL; + } + } + + UnitClass * unit = (UnitClass *)utp->Create_One_Of(object->House); + if (unit) { + if (unit->Unlimbo(Cell_Coord())) { + return(false); + } + delete unit; + } + } + break; + + /* + ** Create a squad of miscellanous composition. + */ + case SQUAD: + for (index = 0; index < 5; index++) { + static InfantryType _inf[] = { + INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, + INFANTRY_E2, + INFANTRY_E3, + INFANTRY_E4, + INFANTRY_E5, + INFANTRY_E7, + INFANTRY_RAMBO + }; + InfantryTypeClass::As_Reference(_inf[Random_Pick((unsigned)0, sizeof(_inf)/sizeof(_inf[0])-1)]).Create_And_Place(Cell_Number(), object->Owner()); + } + return(false); + + /* + ** Sometimes an explosion of great magnitude occurs. + */ + case NUKE: + new AnimClass(ANIM_ATOM_BLAST, Cell_Coord()); + break; + + /* + ** Sometimes an explosion of great magnitude occurs. + */ + case ION: + new AnimClass(ANIM_ION_CANNON, Cell_Coord()); + break; + + /* + ** A nuclear missile was discovered. Add it to the sidebar. + */ + case NUKE_MISSILE: + new AnimClass(ANIM_CRATE_MISSILE, Cell_Coord()); + if (object->House->NukeStrike.Enable(true)) { + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A one time ion blast was discovered. Add it to the sidebar. + */ + case ION_BLAST: + new AnimClass(ANIM_CRATE_EARTH, Cell_Coord()); + if (object->House->IonCannon.Enable(true)) { + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_ION_CANNON, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A one time air strike can be called int. Add it to the sidebar. + */ + case AIR_STRIKE: + new AnimClass(ANIM_CRATE_DEVIATOR, Cell_Coord()); + if (object->House->AirStrike.Enable(true)) { + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_AIR_STRIKE, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A group of explosions are triggered around the crate. + */ + case EXPLOSION: + damage = 400; + object->Take_Damage((int&)damage, 0, WARHEAD_HE); + for (index = 0; index < 5; index++) { + COORDINATE coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); + new AnimClass(ANIM_FBALL1, coord); + damage = 400; + Explosion_Damage(coord, damage, NULL, WARHEAD_HE); + } + break; + + /* + ** A napalm blast is triggered. + */ + case NAPALM: + coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); + new AnimClass(ANIM_NAPALM3, coord); + damage = 600; + Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); + break; + + /* + ** A visceroid appears and, boy, he's angry! + */ + case VISCEROID: + unit = new UnitClass(UNIT_VICE, HOUSE_JP); + if (unit) { + if (unit->Unlimbo(Cell_Coord())) { + return(false); + } + delete unit; + } + break; + + /* + ** All objects within a certain range will gain the ability to cloak. + */ + case CLOAK: + new AnimClass(ANIM_CRATE_STEALTH, Cell_Coord()); + for (index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < 0x0300) { + ((TechnoClass *)obj)->IsCloakable = true; + } + } + break; + + /* + ** All of the player's objects heal up. + */ + case HEAL_BASE: + new AnimClass(ANIM_CRATE_INVUN, Cell_Coord()); + for (index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { + obj->Strength = obj->Class_Of().MaxStrength; + } + } + break; + + } + } + } + return(true); +} + + +/*********************************************************************************************** + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * * + * This routine will place the house flag at this cell location. * + * * + * INPUT: house -- The house that is having its flag placed here. * + * * + * OUTPUT: Was the flag successfully placed here? * + * * + * WARNINGS: Failure to place means that the cell is impassable for some reason. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Place(HousesType house) +{ + Validate(); + if (!IsFlagged && Is_Generally_Clear()) { + IsFlagged = true; + Owner = house; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * * + * This routine will free the cell of any house flag that may be located there. * + * * + * INPUT: none * + * * + * OUTPUT: Was there a flag here that was removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Remove(void) +{ + Validate(); + if (IsFlagged) { + IsFlagged = false; + Owner = HOUSE_NONE; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * * + * This routine is called when some event would cause a momentary disruption in the * + * cloaking device. All objects that are cloaked in the cell will have their cloaking * + * device shimmer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Shimmer(void) +{ + Validate(); + ObjectClass * object = Cell_Occupier(); + + while (object) { + object->Do_Shimmer(); + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Occupier -- Fetches the occupier for the cell. * + * * + * This routine returns with the first recorded occupier of this cell. A special validity * + * check is performed to ensure that there are no dead objects still marked on the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the first occupier object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Occupier(void) const +{ + ObjectClass * ptr = OccupierPtr; + + while (ptr && !ptr->IsActive) { + ptr = ptr->Next; + ((ObjectClass *&)OccupierPtr) = 0; + } + + return(ptr); +} + + + + + + + + + +/* +** Additions to CellClass to track visibility per-player. ST - 3/5/2019 3:08PM +** +** +** +** +** +*/ + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsMappedByPlayerMask |= (1 << shift); + } else { + IsMappedByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Mapped(player->Class->House, set); + if (GameToPlay == GAME_NORMAL && player->IsHuman) { + IsMapped = set; // Also set the regular flag in single player + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HousesType house) const +{ + int shift = (int) house; + return (IsMappedByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Mapped(player->Class->House); + } + return false; +} + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsVisibleByPlayerMask |= (1 << shift); + } else { + IsVisibleByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Visible(player->Class->House, set); + if (GameToPlay == GAME_NORMAL && player->IsHuman) { + IsVisible = set; // Also set the regular flag in single player. This is needed for rendering + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HousesType house) const +{ + int shift = (int) house; + return (IsVisibleByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Visible(player->Class->House); + } + return false; +} + +void CellClass::Override_Land_Type(LandType type) +{ + OverrideLand = type; +} + +#ifdef USE_RA_AI + +/* +** Addition from Red Alert for AI. ST - 7/24/2019 5:33PM +*/ +/*********************************************************************************************** + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * * + * This routine is called when determining general passability for purposes of zone * + * calculation. Only blockages that cannot be circumvented are considered to make a cell * + * impassable. All other obstructions can either be destroyed or are temporary. * + * * + * INPUT: loco -- The locomotion type to use when determining passablility. * + * * + * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? * + * * + * ignorevehicles -- If vehicles should be ignored, then this flag will be true. * + * * + * zone -- If specified, the zone must match this value or else movement is * + * presumed disallowed. * + * * + * check -- This specifies the zone type that this check applies to. * + * * + * OUTPUT: Is the cell generally passable to ground targeting? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. * + * 10/05/1996 JLB : Allows checking for crushable blockages. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Move(bool ignoreinfantry, bool ignorevehicles) const +{ + //assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Flying objects always consider every cell passable since they can fly over everything. + */ + //if (loco == SPEED_WINGED) { + // return(true); + //} + + /* + ** If a zone was specified, then see if the cell is in a legal + ** zone to allow movement. + */ + //if (zone != -1) { + // if (zone != Zones[check]) { + // return(false); + // } + //} + + /* + ** Check the occupy bits for passable legality. If ignore infantry is true, then + ** don't consider infnatry. + */ + int composite = Flag.Composite; + if (ignoreinfantry) { + composite &= 0xE0; // Drop the infantry occupation bits. + } + if (ignorevehicles) { + composite &= 0x5F; // Drop the vehicle/building bit. + } + if (composite != 0) { + return(false); + } + + /* + ** Fetch the land type of the cell -- to be modified and used later. + */ + //LandType land = Land_Type(); + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes unless this is a wall crushing check or if the checking object + ** can destroy walls. + */ + OverlayTypeClass const * overlay = NULL; + if (Overlay != OVERLAY_NONE) { + overlay = &OverlayTypeClass::As_Reference(Overlay); + } + if (overlay != NULL && overlay->IsWall) { + //if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) { + return(false); + //} + + /* + ** Crushing objects consider crushable walls as clear rather than the + ** typical LAND_WALL setting. + */ + //land = LAND_CLEAR; + } + + /* + ** See if the ground type is impassable to this locomotion type and if + ** so, return the error condition. + */ + //if (::Ground[land].Cost[loco] == 0) { + // return(false); + //} + + /* + ** All checks passed, so this cell must be passable. + */ + return(true); +} + +#endif //USE_RA_AI \ No newline at end of file diff --git a/TIBERIANDAWN/CELL.H b/TIBERIANDAWN/CELL.H new file mode 100644 index 000000000..091d2948b --- /dev/null +++ b/TIBERIANDAWN/CELL.H @@ -0,0 +1,295 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cell.h_v 2.20 16 Oct 1995 16:45:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CELL_H +#define CELL_H + +#include "building.h" +#include "unit.h" +#include "template.h" + + +/**************************************************************************** +** Each cell on the map is controlled by the following structure. +*/ +class CellClass +{ + public: + /* + ** Does this cell need to be updated on the radar map? If something changes in the cell + ** that might change the radar map imagery, then this flag will be set. It gets cleared + ** when the cell graphic is updated to the radar map. + */ + unsigned IsPlot:1; + + /* + ** Does this cell contain the special placement cursor graphic? This graphic is + ** present when selecting a site for building placement. + */ + unsigned IsCursorHere:1; + + /* + ** Is this cell mapped by the player? A mapped cell is visible. An unmapped cell + ** is covered in a dark shroud. In addition to visibility, mapped cells are the only + ** legal place for transports to land. + */ + unsigned IsMapped:1; + + /* + ** If any part of this cell is visible (even just peeking out from under the shadow), + ** this this flag will be true. Mapped cells always have this flag set, but unmapped + ** cells might not -- it depends on where the shadow edge is located. + */ + unsigned IsVisible:1; + + /* + ** Every cell can be assigned a trigger. The same trigger can be assigned to + ** multiple cells. This bitflag indicates whether this cell has a trigger. + ** The trigger pointers for all cells must be stored elsewhere. + */ + unsigned IsTrigger:1; + + /* + ** Every cell can be assigned a waypoint. A waypoint can only be assigned + ** to one cell, and vice-versa. This bit simply indicates whether this + ** cell is assigned a waypoint or not. + */ + unsigned IsWaypoint:1; + + /* + ** Is this cell currently under the radar map cursor? If so then it + ** needs to be updated whenever the map is updated. + */ + unsigned IsRadarCursor:1; + + /* + ** If this cell contains a house flag, then this will be true. The actual house + ** flag it contains is specified by the Owner field. + */ + unsigned IsFlagged:1; + + /* + ** This contains the icon number and set to use for the base + ** of the terrain. All rendering on an icon occurs AFTER the icon + ** specified by this element is rendered. It is the lowest of the low. + */ + TemplateType TType; + unsigned char TIcon; + + /* + ** The second layer of 'terrain' icons is represented by a simple + ** type number and a value byte. This is sufficient for handling + ** concrete and walls. + */ + OverlayType Overlay; + unsigned char OverlayData; + + /* + ** This is used to specify any special 'stain' overlay icon. This + ** typically includes infantry bodies or other temporary marks. + */ + SmudgeType Smudge; + unsigned char SmudgeData; + + /* + ** Smudges and walls need to record ownership values. For walls, this + ** allows adjacent building placement logic to work. For smudges, it + ** allows building over smudges that are no longer attached to buildings + ** in addition to fixing the adjacent placement logic. + */ + HousesType Owner; + + /* + ** This flag tells you what type of infantry currently occupy the + ** cell or are moving into it. + */ + HousesType InfType; + + /* + ** These point to the object(s) that are located in this cell or overlap + ** this cell. + */ + ObjectClass *OccupierPtr; + ObjectClass *Overlapper[3]; + + + /* + ** Per-player view of whether a cell is mapped. One bit for each house type. ST - 3/5/2019 3:00PM + */ + unsigned int IsMappedByPlayerMask; + + /* + ** Per-player view of whether a cell is visible. One bit for each house type. ST - 3/5/2019 3:00PM + */ + unsigned int IsVisibleByPlayerMask; + + + /* + ** This array of bit flags is used to indicate which sub positions + ** within the cell are either occupied or are soon going to be + ** occupied. For vehicles, the cells that the vehicle is passing over + ** will be flagged with the vehicle bit. For infantry, the the sub + ** position the infantry is stopped at or headed toward will be marked. + ** The sub positions it passes over will NOT be marked. + */ + union { + struct { + unsigned Center:1; + unsigned NW:1; + unsigned NE:1; + unsigned SW:1; + unsigned SE:1; + unsigned Vehicle:1; // Reserved for vehicle occupation. + unsigned Monolith:1; // Some immovable blockage is in cell. + unsigned Building:1; // A building of some time (usually blocks movement). + } Occupy; + unsigned char Composite; + } Flag; + + //---------------------------------------------------------------- + CellClass(void); + ~CellClass(void) {}; + + int operator == (CellClass const & cell) const {return &cell == this;}; + + /* + ** Query functions. + */ + ObjectClass * Cell_Occupier(void) const; + static int Spot_Index(COORDINATE coord); + bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); } + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());}; + bool Is_Generally_Clear(bool ignore_cloaked = false) const; + TARGET As_Target(void) const {return ::As_Target(Cell_Number());}; + BuildingClass * Cell_Building(void) const; + CellClass const & Adjacent_Cell(FacingType face) const; + CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));}; + COORDINATE Cell_Coord(void) const; + int Cell_Color(bool override=false) const; + CELL Cell_Number(void) const; + LandType Land_Type(void) const {return((OverrideLand != LAND_COUNT) ? OverrideLand : Land);}; + ObjectClass * Cell_Find_Object(RTTIType rtti) const; + ObjectClass * Cell_Object(int x=0, int y=0) const; + TechnoClass * Cell_Techno(int x=0, int y=0) const; + TerrainClass * Cell_Terrain(void) const; + UnitClass * Cell_Unit(void) const; + InfantryClass * Cell_Infantry(void) const; + TriggerClass * Get_Trigger(void) const; + int Clear_Icon(void) const; + bool Goodie_Check(FootClass * object); + ObjectClass * Fetch_Occupier(void) const; + bool Get_Template_Info(char *template_name, int &icon, void *&image_data); + + /* + ** Object placement and removal flag operations. + */ + void Occupy_Down(ObjectClass * object); + void Occupy_Up(ObjectClass * object); + void Overlap_Down(ObjectClass * object); + void Overlap_Up(ObjectClass * object); + bool Flag_Place(HousesType house); + bool Flag_Remove(void); + + /* + ** File I/O. + */ + bool Should_Save(void) const; + bool Save(FileClass & file); + bool Load(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Display and rendering controls. + */ + void Draw_It(int x, int y, int draw_flags = 0) const; + void Redraw_Objects(bool forced=false); + void Shimmer(void); + + /* + ** Maintenance calculation support. + */ + long Tiberium_Adjust(bool pregame=false); + void Wall_Update(void); + void Concrete_Calc(void); + void Recalc_Attributes(void); + int Reduce_Tiberium(int levels); + int Reduce_Wall(int damage); + void Incoming(COORDINATE threat=0, bool forced = false, bool nokidding = false); + void Adjust_Threat(HousesType house, int threat_value); + + int operator != (CellClass const &) const {return 0;}; + + int Validate(void) const; + + + /* + ** Additional per-player functionality is needed for multiplayer. ST - 3/5/2019 3:03PM + */ + void Set_Mapped(HousesType house, bool set = true); + void Set_Mapped(HouseClass *player, bool set = true); + bool Is_Mapped(HousesType house) const; + bool Is_Mapped(HouseClass *player) const; + void Set_Visible(HousesType house, bool set = true); + void Set_Visible(HouseClass *player, bool set = true); + bool Is_Visible(HousesType house) const; + bool Is_Visible(HouseClass *player) const; + + /* + ** Override land type to fix passability issues on some maps + */ + void Override_Land_Type(LandType type); + +#ifdef USE_RA_AI + /* + ** Imported from RA for AI. ST - 7/24/2019 5:36PM + */ + bool Is_Clear_To_Move(bool ignoreinfantry, bool ignorevehicles) const; +#endif + + private: + CellClass (CellClass const &) {}; + + LandType Land; // The land type of this cell. + + LandType OverrideLand; // The overriden land type of this cell. + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/CHECKBOX.CPP b/TIBERIANDAWN/CHECKBOX.CPP new file mode 100644 index 000000000..923edea7e --- /dev/null +++ b/TIBERIANDAWN/CHECKBOX.CPP @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\checkbox.cpv 1.6 16 Oct 1995 16:49:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : July 1, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "checkbox.h" + + +/*********************************************************************************************** + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * * + * This routine will draw the checkbox either filled or empty as necessary. * + * * + * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? * + * * + * OUTPUT: Was the check box rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Draw_Me(int forced) +{ + if (ToggleClass::Draw_Me(forced)) { + + Hide_Mouse(); + Draw_Box(X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, false); + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY); + if (IsOn) { + LogicPage->Draw_Line(X+1, Y+1, X+Width-2, Y+Height-2, BLACK); + LogicPage->Draw_Line(X+Width-2, Y+1, X+1, Y+Height-2, BLACK); + } + Show_Mouse(); + return(true); + } + return(false); +} diff --git a/TIBERIANDAWN/CHECKBOX.H b/TIBERIANDAWN/CHECKBOX.H new file mode 100644 index 000000000..9c5fa3fb8 --- /dev/null +++ b/TIBERIANDAWN/CHECKBOX.H @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\checkbox.h_v 1.6 16 Oct 1995 16:46:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : May 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "toggle.h" + +class CheckBoxClass : public ToggleClass +{ + public: + CheckBoxClass(unsigned id, int x, int y) : + ToggleClass(id, x, y, 7, 7) + {}; + + virtual int Draw_Me(int forced=false); + + protected: +}; + +#endif diff --git a/TIBERIANDAWN/CHEKLIST.CPP b/TIBERIANDAWN/CHEKLIST.CPP new file mode 100644 index 000000000..956b93aa2 --- /dev/null +++ b/TIBERIANDAWN/CHEKLIST.CPP @@ -0,0 +1,165 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cheklist.cpv 2.18 16 Oct 1995 16:48:36 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CheckListClass::Action -- action function for this class * + * CheckListClass::CheckListClass -- constructor * + * CheckListClass::Check_Item -- [un]checks an items * + * CheckListClass::~CheckListClass -- destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CheckListClass::CheckListClass -- constructor * + * * + * INPUT: * + * id control ID for this list box * + * x x-coord * + * y y-coord * + * w width * + * h height * + * flags mouse event flags * + * up ptr to Up-arrow shape * + * down ptr to Down-arrow shape * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down) +{ + IsReadOnly = false; +} + + +/*************************************************************************** + * CheckListClass::Check_Item -- [un]checks an items * + * * + * INPUT: * + * index index of item to check or uncheck * + * checked 0 = uncheck, non-zero = check * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +void CheckListClass::Check_Item(int index, int checked) +{ + if (List[index]) { + ((char &)List[index][0]) = checked ? CHECK_CHAR : UNCHECK_CHAR; + } +} + + +/*************************************************************************** + * CheckListClass::Is_Checked -- returns checked state of an item * + * * + * INPUT: * + * index index of item to query * + * * + * OUTPUT: * + * 0 = item is unchecked, 1 = item is checked * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Is_Checked(int index) const +{ + if (List[index]) { + return(List[index][0] == CHECK_CHAR); + } + return(false); +} + + +/*************************************************************************** + * CheckListClass::Action -- action function for this class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Action(unsigned flags, KeyNumType &key) +{ + int rc; + + /* + ** If this is a read-only list, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** Invoke parents Action first, so it can set the SelectedIndex if needed. + */ + rc = ListClass::Action(flags, key); + + /* + ** Now, if this event was a left-press, toggle the checked state of the + ** current item. + */ + if (flags & LEFTPRESS) { + if (Is_Checked(SelectedIndex)) { + Check_Item(SelectedIndex,0); + } else { + Check_Item(SelectedIndex,1); + } + } + + return(rc); +} diff --git a/TIBERIANDAWN/CHEKLIST.H b/TIBERIANDAWN/CHEKLIST.H new file mode 100644 index 000000000..603ffcdcb --- /dev/null +++ b/TIBERIANDAWN/CHEKLIST.H @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\cheklist.h_v 2.19 16 Oct 1995 16:46:20 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * This class behaves just like the standard list box, except that if the * + * first character of a list entry is a space, clicking on it toggles the * + * space with a check-mark ('\3'). This makes each entry in the list box * + * "toggle-able". * + *-------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHEKLIST_H +#define CHEKLIST_H + +#include "list.h" + + +class CheckListClass : public ListClass +{ + public: + /*--------------------------------------------------------------------- + Constructor/Destructor + ---------------------------------------------------------------------*/ + CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + ~CheckListClass(void) {}; + + /*--------------------------------------------------------------------- + Checkmark utility functions + ---------------------------------------------------------------------*/ + void Check_Item(int index, int checked); // sets checked state of item + int Is_Checked(int index) const; // gets checked state of item + + /*--------------------------------------------------------------------- + This defines the ASCII value of the checkmark character & non-checkmark + character. + ---------------------------------------------------------------------*/ + enum CheckListClassEnum { + CHECK_CHAR = '\3', + UNCHECK_CHAR = ' ', + }; + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly ? true : false;} + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + + private: + bool IsReadOnly; + +}; + + + +#endif +/************************** end of cheklist.h ******************************/ \ No newline at end of file diff --git a/TIBERIANDAWN/COLRLIST.CPP b/TIBERIANDAWN/COLRLIST.CPP new file mode 100644 index 000000000..eb19e8f61 --- /dev/null +++ b/TIBERIANDAWN/COLRLIST.CPP @@ -0,0 +1,280 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\colrlist.cpv 1.9 16 Oct 1995 16:50:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : April 19, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ColorListClass::Add_Item -- Adds an item to the list * + * ColorListClass::ColorListClass -- Class constructor * + * ColorListClass::Draw_Entry -- Draws one text line * + * ColorListClass::Remove_Item -- Removes an item from the list * + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * ColorListClass::~ColorListClass -- Class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ColorListClass::ColorListClass -- class constructor * + * * + * INPUT: * + * id button ID * + * x,y upper-left corner, in pixels * + * w,h width, height, in pixels * + * list ptr to array of char strings to list * + * flags flags for mouse, style of listbox * + * up,down pointers to shapes for up/down buttons * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ColorListClass::ColorListClass (int id, int x, int y, int w, int h, + TextPrintType flags, void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down) +{ + Style = SELECT_HIGHLIGHT; + SelectColor = -1; +} + + +/*************************************************************************** + * ColorListClass::~ColorListClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +ColorListClass::~ColorListClass(void) +{ + Colors.Clear(); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(char const * text, char color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(int text, char color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Remove_Item -- Removes an item from the list * + * * + * INPUT: * + * text ptr to item to remove * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Remove_Item(char const * text) +{ + int index = List.ID(text); + if (index != -1) { + Colors.Delete(index); + ListClass::Remove_Item(text); + } +} + + +/*************************************************************************** + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * * + * INPUT: * + * style style to draw * + * color color to draw the special style in; -1 = use item's color* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Set_Selected_Style(SelectStyleType style, int color) +{ + Style = style; + SelectColor = color; +} + + +/*************************************************************************** + * ColorListClass::Draw_Entry -- Draws one text line * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + int color; + + /* + ** Draw a non-selected item in its color + */ + if (!selected) { + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + return; + } + + /* + ** For selected items, choose the right color & style: + */ + if (SelectColor==-1) { + color = Colors[index]; + } else { + color = SelectColor; + } + + switch (Style) { + /* + ** NONE: Just print the string in its native color + */ + case SELECT_NONE: + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must + ** be set) + */ + case SELECT_HIGHLIGHT: + if (TextFlags & TPF_6PT_GRAD) { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** BOX: Draw a box around the item in the current select color + */ + case SELECT_BOX: + LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** BAR: draw a color bar under the text + */ + case SELECT_BAR: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, SelectColor); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + LogicPage->Fill_Rect (x, y, x + width - 2, y + LineHeight - 2, SelectColor); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** INVERT: Draw text as the background color on foreground color + */ + case SELECT_INVERT: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]); + Conquer_Clip_Text_Print(List[index], x, y, BLACK, TBLACK, TextFlags, width, Tabs); + } else { + LogicPage->Fill_Rect (x, y, x + width - 2, y + LineHeight - 2, Colors[index]); + Conquer_Clip_Text_Print(List[index], x, y, LTGREY, TBLACK, TextFlags, width, Tabs); + } + break; + + } +} diff --git a/TIBERIANDAWN/COLRLIST.H b/TIBERIANDAWN/COLRLIST.H new file mode 100644 index 000000000..710346fee --- /dev/null +++ b/TIBERIANDAWN/COLRLIST.H @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\colrlist.h_v 1.10 16 Oct 1995 16:47:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COLORLIST_H +#define COLORLIST_H + +#include "list.h" + + +/*************************************************************************** +** This class adds the ability for every list item to have a different color. +*/ +class ColorListClass : public ListClass +{ + public: + /********************************************************************* + ** These enums are the ways a selected item can be drawn + */ + typedef enum SelectEnum { + SELECT_NONE, // selected items aren't drawn differently + SELECT_HIGHLIGHT, // item is highlighted + SELECT_BOX, // draw a box around the item + SELECT_BAR, // draw a bar behind the item + SELECT_INVERT, // draw the string inverted + } SelectStyleType; + + ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + virtual ~ColorListClass(void); + + virtual int Add_Item(char const * text, char color = WHITE); + virtual int Add_Item(int text, char color = WHITE); + virtual void Remove_Item(char const * text); + + virtual void Set_Selected_Style(SelectStyleType style, int color = -1); + + /* + ** This is the list of colors for each item. + */ + DynamicVectorClass Colors; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This tells how to draw the selected item. + */ + SelectStyleType Style; + int SelectColor; + +}; + +#endif diff --git a/TIBERIANDAWN/COMBAT.CPP b/TIBERIANDAWN/COMBAT.CPP new file mode 100644 index 000000000..c2898bf74 --- /dev/null +++ b/TIBERIANDAWN/COMBAT.CPP @@ -0,0 +1,232 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\combat.cpv 2.17 16 Oct 1995 16:48:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBAT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 19, 1994 * + * * + * Last Update : January 1, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Explosion_Damage -- Inflict an explosion damage affect. * + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass *source, WarheadType warhead); + + +/*********************************************************************************************** + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * * + * This routine is the core of combat tactics. It implements the * + * affect various armor types have against various weapon types. By * + * careful exploitation of this table, tactical advantage can be * + * obtained. * + * * + * INPUT: damage -- The damage points to process. * + * * + * warhead -- The source of the damage points. * + * * + * armor -- The type of armor defending against the damage. * + * * + * distance -- The distance (in leptons) from the source of the damage. * + * * + * OUTPUT: Returns with the adjusted damage points to inflict upon the * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 04/17/1994 JLB : Always does a minimum of damage. * + * 01/01/1995 JLB : Takes into account distance from damage source. * + *=============================================================================================*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance) +{ + /* + ** If there is no raw damage value to start with, then + ** there can be no modified damage either. + */ + if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0); + + WarheadTypeClass const * whead = &Warheads[warhead]; + + damage = Fixed_To_Cardinal(damage, whead->Modifier[armor]); + + /* + ** Reduce damage according to the distance from the impact point. + */ + if (damage) { +// if (distance < 0x0010) damage *= 2; // Double damage for direct hits. + distance >>= whead->SpreadFactor; + distance = Bound(distance, 0, 16); + damage >>= distance; + } + + /* + ** If damage was indicated, then it should never drop below one damage point regardless + ** of modifiers. This allows a very weak attacker to eventually destroy anything it + ** fires upon, given enough time. + */ + return(damage); +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + *=============================================================================================*/ +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead) +{ + CELL cell; // Cell number under explosion. + ObjectClass * object; // Working object pointer. + ObjectClass * objects[32]; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int index; + int count; // Number of vehicle IDs in list. + + if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return; + + WarheadTypeClass const * whead = &Warheads[warhead]; + range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1); + cell = Coord_Cell(coord); + if ((unsigned)cell >= MAP_CELL_TOTAL) return; +// if (!Map.In_Radar(cell)) return; + + CellClass * cellptr = &Map[cell]; + ObjectClass * impacto = cellptr->Cell_Occupier(); + + /* + ** Fill the list of unit IDs that will have damage + ** assessed upon them. The units can be lifted from + ** the cell data directly. + */ + count = 0; + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + /* + ** Fetch a pointer to the cell to examine. This is either + ** an adjacent cell or the center cell. Damage never spills + ** further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /* + ** Add all objects in this cell to the list of objects to possibly apply + ** damage to. The list stops building when the object pointer list becomes + ** full. Do not include overlapping objects; selection state can affect + ** the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(); + while (object) { + if (!object->IsToDamage && object != source) { + object->IsToDamage = true; + objects[count++] = object; + if (count >= (sizeof(objects)/sizeof(objects[0]))) break; + } + object = object->Next; + } + if (count >= (sizeof(objects)/sizeof(objects[0]))) break; + } + + /* + ** Sweep through the units to be damaged and damage them. When damaging + ** buildings, consider a hit on any cell the building occupies as if it + ** were a direct hit on the building's center. + */ + for (index = 0; index < count; index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->What_Am_I() == RTTI_BUILDING && impacto == object) { + distance = 0; + } else { + distance = Distance(coord, object->Center_Coord()); + } + if (object->IsDown && !object->IsInLimbo && distance < range) { + int damage = strength; + + /* + ** High explosive does double damage against aircraft. + */ + if (warhead == WARHEAD_HE && object->What_Am_I() == RTTI_AIRCRAFT) { + damage *= 2; + } + + /* + ** Apply the damage to the object. + */ + if (damage) { + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + /* + ** If there is a wall present at this location, it may be destroyed. Check to + ** make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + cellptr->Reduce_Tiberium(strength / 10); + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsWall) { + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + Map[cell].Reduce_Wall(strength); + } + } + } +} diff --git a/TIBERIANDAWN/COMBUF.CPP b/TIBERIANDAWN/COMBUF.CPP new file mode 100644 index 000000000..04dedba42 --- /dev/null +++ b/TIBERIANDAWN/COMBUF.CPP @@ -0,0 +1,1036 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\combuf.cpv 1.4 16 Oct 1995 16:50:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 2, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommBufferClass::CommBufferClass -- class constructor * + * CommBufferClass::~CommBufferClass -- class destructor * + * CommBufferClass::Init -- initializes this queue * + * CommBufferClass::Queue_Send -- queues a message for sending * + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * CommBufferClass::Queue_Receive -- queues a received message * + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue* + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * CommBufferClass::Add_Delay -- adds a new delay value for response time* + * CommBufferClass::Avg_Response_Time -- returns average response time * + * CommBufferClass::Max_Response_Time -- returns max response time * + * CommBufferClass::Reset_Response_Time -- resets computations * + * Mono_Debug_Print -- Debug output routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CommBufferClass::CommBufferClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ----------------------------- Init variables ----------------------------- + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ----------------------- Allocate the queue entries ----------------------- + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + SendIndex = new int[numsend]; + ReceiveIndex = new int[numreceive]; + + /* + ---------------------- Allocate queue entry buffers ---------------------- + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); + +} /* end of CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::~CommBufferClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::~CommBufferClass() +{ + int i; + + /* + ------------------------ Free queue entry buffers ------------------------ + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; + + delete [] SendIndex; + delete [] ReceiveIndex; + +} /* end of ~CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Init(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + + ReceiveCount = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + + SendIndex[i] = 0; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + + ReceiveIndex[i] = 0; + } + + /*------------------------------------------------------------------------ + Init debug values + ------------------------------------------------------------------------*/ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; + +} /* end of Init */ + + +void CommBufferClass::Init_Send_Queue(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendCount = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + + SendIndex[i] = 0; + } + +} /* end of Init_Send_Queue */ + + +/*************************************************************************** + * CommBufferClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Send(void *buf, int buflen) +{ + int i; + int index; + + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (SendCount==MaxSend) + return(0); + + /* + -------------------------- Find an empty slot ---------------------------- + */ + index = -1; + for (i = 0; i < MaxSend; i++) { + if (SendQueue[i].IsActive==0) { + index = i; + break; + } + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[index].IsActive = 1; // entry is now active + SendQueue[index].IsACK = 0; // entry hasn't been ACK'd + SendQueue[index].FirstTime = 0L; // filled in by Manager when sent + SendQueue[index].LastTime = 0L; // filled in by Manager when sent + SendQueue[index].SendCount = 0L; // filled in by Manager when sent + SendQueue[index].BufLen = buflen; // save buffer size + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(SendQueue[index].Buffer,buf,buflen); + + /* + ----------------------- Save this entry's index -------------------------- + */ + SendIndex[SendCount] = index; + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + SendCount++; + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index "index" of entry to un-queue * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index) +{ + int i; + + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) + return(0); + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendIndex[index]].Buffer, + SendQueue[SendIndex[index]].BufLen); + (*buflen) = SendQueue[SendIndex[index]].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendIndex[index]].IsActive = 0; + SendQueue[SendIndex[index]].IsACK = 0; + SendQueue[SendIndex[index]].FirstTime = 0L; + SendQueue[SendIndex[index]].LastTime = 0L; + SendQueue[SendIndex[index]].SendCount = 0L; + SendQueue[SendIndex[index]].BufLen = 0; + + /* + ------------------------- Move Indices back one -------------------------- + */ + for (i = index; i < SendCount - 1; i++) { + SendIndex[i] = SendIndex[i + 1]; + } + SendIndex[SendCount - 1] = 0; + SendCount--; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommBufferClass::Get_Send(int index) +{ + if (SendQueue[SendIndex[index]].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[SendIndex[index]]); + } + +} /* end of Get_Send */ + + +/*************************************************************************** + * CommBufferClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Receive(void *buf, int buflen) +{ + int i; + int index; + +//CCDebugString ("C&C95 - Queueing a receive packet\n"); + + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (ReceiveCount==MaxReceive) { +CCDebugString ("C&C95 - Error - Receive queue full!\n"); + return(0); + } + + /* + -------------------------- Find an empty slot ---------------------------- + */ + index = -1; + for (i = 0; i < MaxReceive; i++) { + if (ReceiveQueue[i].IsActive==0) { + index = i; + break; + } + } + +if (index == -1){ +CCDebugString ("C&C95 - Error - Receive queue full too!\n"); +} + + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[index].IsActive = 1; + ReceiveQueue[index].IsRead = 0; + ReceiveQueue[index].IsACK = 0; + ReceiveQueue[index].BufLen = buflen; + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(ReceiveQueue[index].Buffer,buf,buflen); + + /* + ----------------------- Save this entry's index -------------------------- + */ + ReceiveIndex[ReceiveCount] = index; + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + ReceiveCount++; + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index index of entry to un-queue * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index) +{ + int i; + + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(0); + } + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer, + ReceiveQueue[ReceiveIndex[index]].BufLen); + (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveIndex[index]].IsActive = 0; + ReceiveQueue[ReceiveIndex[index]].IsRead = 0; + ReceiveQueue[ReceiveIndex[index]].IsACK = 0; + ReceiveQueue[ReceiveIndex[index]].BufLen = 0; + + /* + ------------------------- Move Indices back one -------------------------- + */ + for (i = index; i < ReceiveCount - 1; i++) { + ReceiveIndex[i] = ReceiveIndex[i + 1]; + } + ReceiveIndex[ReceiveCount - 1] = 0; + ReceiveCount--; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommBufferClass::Get_Receive(int index) +{ + if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveIndex[index]]); + } + +} /* end of Get_Receive */ + + +/*************************************************************************** + * CommBufferClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) + MaxDelay = delay; + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommBufferClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + If few enough entries, call the verbose debug version + ------------------------------------------------------------------------*/ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (SendQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames > 0 && val >= 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (ReceiveQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames > 0 && val >= 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + diff --git a/TIBERIANDAWN/COMBUF.H b/TIBERIANDAWN/COMBUF.H new file mode 100644 index 000000000..8c3a14780 --- /dev/null +++ b/TIBERIANDAWN/COMBUF.H @@ -0,0 +1,178 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\combuf.h_v 1.6 16 Oct 1995 16:46:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to store outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * * + * This class stores buffers in a non-sequenced order; it allows freeing * + * any entry, so the buffers can be kept clear, even if packets come in * + * out of order. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMBUF_H +#define COMBUF_H + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommBufferClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommBufferClass(int numsend, int numrecieve, int maxlen); + virtual ~CommBufferClass(); + void Init(void); + void Init_Send_Queue(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen, int index); // remove from Send queue + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen, int index); // remove from Receive queue + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + unsigned long SendTotal; // total # added to send queue + int *SendIndex; // array of Send entry indices + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + unsigned long ReceiveTotal; // total # added to receive queue + int *ReceiveIndex; // array of Receive entry indices + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/**************************** end of combuf.h ******************************/ + diff --git a/TIBERIANDAWN/COMPAT.H b/TIBERIANDAWN/COMPAT.H new file mode 100644 index 000000000..69d128e4a --- /dev/null +++ b/TIBERIANDAWN/COMPAT.H @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\compat.h_v 2.19 16 Oct 1995 16:46:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMPAT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/95 * + * * + * Last Update : March 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMPAT_H +#define COMPAT_H + + +//#include "i86.h" + +#define KeyNumType int +#define KeyASCIIType int + + + +#define BuffType BufferClass +#define movmem(a,b,c) memmove(b,a,c) +#define ShapeBufferSize _ShapeBufferSize +extern "C" { + extern long ShapeBufferSize; + extern char *ShapeBuffer; +} + +/*=========================================================================*/ +/* Define some equates for the different graphic routines we will install */ +/* later. */ +/*=========================================================================*/ +#define HIDBUFF ((void *)(0xA0000)) +//#define Size_Of_Region(a, b) a*b + +/*=========================================================================*/ +/* Define some Graphic Routines which will only be fixed by these defines */ +/*=========================================================================*/ +#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15) + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. + +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + +#define ERROR_WINDOW 1 +#define ErrorWindow 1 + + +extern unsigned char *Palette; +extern unsigned char MDisabled; // Is mouse disabled? +extern WORD Hard_Error_Occured; + +/* +** This is the menu control structures. +*/ +typedef enum MenuIndexType { + MENUX, + MENUY, + ITEMWIDTH, + ITEMSHIGH, + MSELECTED, + NORMCOL, + HILITE, + MENUPADDING=0x1000 +} MenuIndexType; + + +#define BITSPERBYTE 8 +#define MAXSHORT 0x7fff +#define HIBITS 0x8000 +//#define MAXLONG 0x7fffffffL +#define HIBITL 0x80000000 + +//PG_TO_FIX +#ifndef MAXINT +#define MAXINT MAXLONG +#endif +#define HIBITI HIBITL + +#define DMAXEXP 308 +#define FMAXEXP 38 +#define DMINEXP -307 +#define FMINEXP -37 + +#define MAXDOUBLE 1.797693E+308 +#define MAXFLOAT 3.37E+38F +#define MINDOUBLE 2.225074E-308 +#define MINFLOAT 8.43E-37F + +#define DSIGNIF 53 +#define FSIGNIF 24 + +#define DMAXPOWTWO 0x3FF +#define FMAXPOWTWO 0x7F +#define DEXPLEN 11 +#define FEXPLEN 8 +#define EXPBASE 2 +#define IEEE 1 +#define LENBASE 1 +#define HIDDENBIT 1 +#define LN_MAXDOUBLE 7.0978E+2 +#define LN_MINDOUBLE -7.0840E+2 + +/* These defines handle the various names given to the same color. */ +#define DKGREEN GREEN +#define DKBLUE BLUE +#define GRAY GREY +#define DKGREY GREY +#define DKGRAY GREY +#define LTGRAY LTGREY + +#if 0 +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char * Map; // Icon map offset (if present). +} IControl_Type; +#endif + + + +void Stuff_Key_Num ( int ); + + +extern "C"{ +extern int MouseQX; +extern int MouseQY; +} + +#endif diff --git a/TIBERIANDAWN/COMQUEUE.CPP b/TIBERIANDAWN/COMQUEUE.CPP new file mode 100644 index 000000000..c5df96eea --- /dev/null +++ b/TIBERIANDAWN/COMQUEUE.CPP @@ -0,0 +1,1009 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\comqueue.cpv 1.10 16 Oct 1995 16:49:14 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommQueueClass::CommQueueClass -- class constructor * + * CommQueueClass::~CommQueueClass -- class destructor * + * CommQueueClass::Init -- initializes this queue * + * CommQueueClass::Queue_Send -- queues a message for sending * + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * CommQueueClass::Queue_Receive -- queues a received message * + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * CommQueueClass::Avg_Response_Time -- returns average response time * + * CommQueueClass::Max_Response_Time -- returns max response time * + * CommQueueClass::Reset_Response_Time -- resets computations * + * CommQueueClass::Configure_Debug -- sets up special debug values * + * CommQueueClass::Mono_Debug_Print -- Debug output routine * + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) + + +#include "function.h" + +#ifndef DEMO + +/*************************************************************************** + * CommQueueClass::CommQueueClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ----------------------------- Init variables ----------------------------- + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ----------------------- Allocate the queue entries ----------------------- + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + /* + ---------------------- Allocate queue entry buffers ---------------------- + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); + +} /* end of CommQueueClass */ + + +/*************************************************************************** + * CommQueueClass::~CommQueueClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::~CommQueueClass() +{ + int i; + + /* + ------------------------ Free queue entry buffers ------------------------ + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; + +} /* end of ~CommQueueClass */ + + +/*************************************************************************** + * CommQueueClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Init(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + SendNext = 0; + SendEmpty = 0; + + ReceiveCount = 0; + ReceiveNext = 0; + ReceiveEmpty = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + ReceiveQueue[i].BufLen = 0; + } + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + } + + /*------------------------------------------------------------------------ + Init debug values + ------------------------------------------------------------------------*/ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; + +} /* end of Init */ + + +/*************************************************************************** + * CommQueueClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Send(void *buf, int buflen) +{ + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) + return(0); + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendEmpty].IsActive = 1; // entry is now active + SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd + SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].BufLen = buflen; // save buffer size + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(SendQueue[SendEmpty].Buffer,buf,buflen); + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + SendCount++; + SendEmpty++; + if (SendEmpty==MaxSend) + SendEmpty = 0; + + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Send(void *buf, int *buflen) +{ + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (SendCount==0 || SendQueue[SendNext].IsActive==0) + return(0); + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen); + (*buflen) = SendQueue[SendNext].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendNext].IsActive = 0; + SendQueue[SendNext].IsACK = 0; + SendQueue[SendNext].FirstTime = 0L; + SendQueue[SendNext].LastTime = 0L; + SendQueue[SendNext].SendCount = 0L; + SendQueue[SendNext].BufLen = 0; + SendCount--; + SendNext++; + if (SendNext==MaxSend) + SendNext = 0; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Next_Send(void) +{ + if (SendCount==0) { + return(NULL); + } else { + return(&SendQueue[SendNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Get_Send(int index) +{ + int i; + + i = SendNext + index; + if (i >= MaxSend) + i -= MaxSend; + + if (SendQueue[i].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Receive(void *buf, int buflen) +{ + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) { + return(0); + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveEmpty].IsActive = 1; + ReceiveQueue[ReceiveEmpty].IsRead = 0; + ReceiveQueue[ReceiveEmpty].IsACK = 0; + ReceiveQueue[ReceiveEmpty].BufLen = buflen; + + /* + ------------------------- Copy the packet data --------------------------- + */ + if (buflen > MaxPacketLen){ + CCDebugString ("C&C95 - Error. Incoming packet too large"); + } + memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen); + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + ReceiveCount++; + ReceiveEmpty++; + if (ReceiveEmpty==MaxReceive) + ReceiveEmpty = 0; + + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Receive(void *buf, int *buflen) +{ + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) { + return(0); + } + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveNext].Buffer, + ReceiveQueue[ReceiveNext].BufLen); + (*buflen) = ReceiveQueue[ReceiveNext].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveNext].IsActive = 0; + ReceiveQueue[ReceiveNext].IsRead = 0; + ReceiveQueue[ReceiveNext].IsACK = 0; + ReceiveQueue[ReceiveNext].BufLen = 0; + ReceiveCount--; + ReceiveNext++; + if (ReceiveNext==MaxReceive) + ReceiveNext = 0; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Next_Receive(void) +{ + if (ReceiveCount==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Get_Receive(int index) +{ + int i; + + i = ReceiveNext + index; + if (i >= MaxReceive) + i -= MaxReceive; + + if (ReceiveQueue[i].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommQueueClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + If few enough entries, call the verbose debug version + ------------------------------------------------------------------------*/ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (SendQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (ReceiveQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + +#endif + + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/COMQUEUE.H b/TIBERIANDAWN/COMQUEUE.H new file mode 100644 index 000000000..3210ee502 --- /dev/null +++ b/TIBERIANDAWN/COMQUEUE.H @@ -0,0 +1,190 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\comqueue.h_v 1.12 16 Oct 1995 16:45:08 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to queue up outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * It allows the application to keep track of how many messages have * + * passed through this queue, in & out, so packets can use this as a * + * unique ID. (If packets have a unique ID, the application can use this * + * to detect re-sends.) * + * * + * The queues act as FIFO buffers (First-In, First-Out). The first entry * + * placed in a queue is the first one read from it, and so on. The * + * controlling application must ensure it places the entries on the queue * + * in the order it wants to access them. * + * * + * The queue is implemented as an array of Queue Entries. Index 0 is the * + * first element placed on the queue, and is the first retrieved. Index * + * 1 is the next, and so on. When Index 0 is retrieved, the next-available* + * entry becomes Index 1. The array is circular; when the end is reached, * + * the indices wrap around to the beginning. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMQUEUE_H +#define COMQUEUE_H + + + +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommQueueClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommQueueClass(int numsend, int numrecieve, int maxlen); + virtual ~CommQueueClass(); + void Init(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen); // remove from Send queue + SendQueueType * Next_Send(void); // ptr to next avail entry + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue + ReceiveQueueType * Next_Receive(void); // ptr to next avail entry + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + int SendNext; // next entry read from queue + int SendEmpty; // next empty spot in queue + unsigned long SendTotal; // total # added to send queue + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + int ReceiveNext; // next entry read from queue + int ReceiveEmpty; // next empty spot in queue + unsigned long ReceiveTotal; // total # added to receive queue + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/*************************** end of comqueue.h *****************************/ + diff --git a/TIBERIANDAWN/CONFDLG.CPP b/TIBERIANDAWN/CONFDLG.CPP new file mode 100644 index 000000000..ce6ebd307 --- /dev/null +++ b/TIBERIANDAWN/CONFDLG.CPP @@ -0,0 +1,247 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\confdlg.cpv 2.17 16 Oct 1995 16:49:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "confdlg.h" + + +bool ConfirmationClass::Process(int text) +{ + return(Process(Text_String(text))); +} + + +/*********************************************************************************************** + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to confirm a deletion. * + * * + * INPUT: char *string - display in edit box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +bool ConfirmationClass::Process(char const * string) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + enum { + NUM_OF_BUTTONS = 2 + }; + + char buffer[80*3]; + int result = true; + int width; + int bwidth, bheight; // button width and height + int height; + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + strcpy(buffer, string); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200*factor, width, height); + width += 60*factor; + height += 60*factor; + int x = (320*factor - width) / 2; + int y = (200*factor - height) / 2; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + + bheight = FontHeight + FontYSpacing + 2; + bwidth = MAX( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30U); + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + 10*factor, y + height - (bheight + 5*factor), bwidth ); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + width - (bwidth + 10*factor), + y + height - (bheight + 5*factor), bwidth ); + + nobtn.Add_Tail(yesbtn); + + curbutton = 1; + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(yesbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_NO, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(yesbtn); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + result = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_CONFIRMATION, x, y, width); + Fancy_Text_Print(buffer, x+20*factor, y+30*factor, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Draw the titles. + */ + yesbtn.Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = yesbtn.Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_YES | KN_BUTTON): + selection = BUTTON_YES; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_NO | KN_BUTTON): + selection = BUTTON_NO; + pressed = true; + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = NUM_OF_BUTTONS - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_YES; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_YES): + result = true; + process = false; + break; + + case (BUTTON_NO): + result = false; + process = false; + break; + } + + pressed = false; + } + } + return(result); +} diff --git a/TIBERIANDAWN/CONFDLG.H b/TIBERIANDAWN/CONFDLG.H new file mode 100644 index 000000000..2726766a4 --- /dev/null +++ b/TIBERIANDAWN/CONFDLG.H @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\confdlg.h_v 2.18 16 Oct 1995 16:46:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef CONFDLG_H +#define CONFDLG_H + +#include "gadget.h" + +class ConfirmationClass +{ + private: + enum ConfirmationClassEnum { + BUTTON_YES=1, // Button number for "Options menu" + BUTTON_NO, // Button number for "Options menu" + }; + + public: + ConfirmationClass(void) { }; + bool Process(char const * string); + bool Process(int text); +}; + +#endif diff --git a/TIBERIANDAWN/CONNECT.CPP b/TIBERIANDAWN/CONNECT.CPP new file mode 100644 index 000000000..fe5289d51 --- /dev/null +++ b/TIBERIANDAWN/CONNECT.CPP @@ -0,0 +1,248 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\connect.cpv 1.9 16 Oct 1995 16:48:56 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WWLIB32_H +#include "TIMER.H" +#else +#include +#endif + +/* +********************************* Globals *********************************** +*/ +char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * If either max_retries or timeout is -1, that parameter is ignored in * + * timeout computations. If both are -1, the connection will just keep * + * retrying forever. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int maxlen, unsigned short magicnum, + unsigned long retry_delta, unsigned long max_retries, unsigned long timeout) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue. This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + ------------------------------------------------------------------------*/ +// if (!Service_Send_Queue()) +// return(0); + + /*------------------------------------------------------------------------ + Service the Receive Queue. This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ +// if (!Service_Receive_Queue()) +// return(0); + +// return(1); + + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } else { + return(0); + } + +} /* end of Service */ + +// ST = 12/17/2018 5:44PM +#ifndef TickCount +extern TimerClass TickCount; +#endif + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ +#ifdef WWLIB32_H + return(TickCount.Time()); // Westwood Library time +#else + static struct timeb mytime; // DOS time + unsigned long msec; + + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } else { + return(NULL); + } +} + diff --git a/TIBERIANDAWN/CONNECT.H b/TIBERIANDAWN/CONNECT.H new file mode 100644 index 000000000..eee968aaf --- /dev/null +++ b/TIBERIANDAWN/CONNECT.H @@ -0,0 +1,340 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\connect.h_v 1.12 16 Oct 1995 16:46:04 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * DESCRIPTION: * + * This class represents a single "connection" with another system. It's * + * a pure virtual base class that acts as a framework for other classes. * + * * + * This class contains a CommQueueClass member, which stores received * + * & transmitted packets. The ConnectionClass has virtual functions to * + * handle adding packets to the queue, reading them from the queue, * + * a Send routine for actually sending data, and a Receive_Packet function * + * which is used to tell the connection that a new packet has come in. * + * * + * The virtual Service routine will handle all ACK & Retry logic for * + * communicating between this system & another. Thus, any class derived * + * from this class must provide the basic ACK/Retry logic. * + * * + * THE HEADER: * + * The Connection Classes prefix every packet sent with a header that's * + * local to this class. The header contains a "Magic Number" which should * + * be unique for each product, and Packet "Code", which will tell the * + * receiving end if this is DATA, or an ACK packet, and a packet ID, which * + * is a unique numerical ID for this packet (useful for detecting resends).* + * The header is stored with each packet in the send & receive Queues; * + * it's removed before it's passed back to the application, via * + * Get_Packet() * + * * + * THE CONNECTION MANAGER: * + * It is assumed that there will be a "Connection Manager" class which * + * will handle parsing incoming packets; it will then tell the connection * + * that new packets have come in, and the connection will process them in * + * whatever way it needs to for its protocol (check for resends, handle * + * ACK packets, etc). The job of the connection manager is to parse * + * incoming packets & distribute them to the connections that need to * + * store them (for multi-connection protocols). * + * * + * NOTES ON ACK/RETRY: * + * The packet's ID is used to check for re-sends. The ID is set to the * + * Queue's total Send Count; if the receiving system checks this value, * + * and it's less than that system's Receive Count, this is a resend. * + * (ie packet 0 will be the 1st packet sent, since the Send Queue's count * + * is 0 when it's sent; as soon as it's received, the Receive Count goes * + * up to 1, and an ID of 0 then means a resend.) This scheme keeps the * + * application from seeing the same packet twice. All the Connection * + * Manager has to do is mark the resent packet as non-ACK'd. Of course, * + * the Manager doesn't have to use this value at all. * + * * + * Both DATA_ACK packets and DATA_NOACK packets must go through the Send * + * Queue when "sent", so that the SendTotal value for this system * + * will still match the ReceiveTotal value for the other system; this is * + * why a NOACK packet can't just be sent immediately; it must go through * + * the queue. * + * * + * If the protocol being used already guarantees delivery of packets, * + * no ACK is required for the packets. In this case, the connection * + * class for this protocol can overload the Service routine to avoid * + * sending ACK packets, or the Connection Manager can just mark the * + * packet as ACK'd when it adds it to the Receive Queue for the connection.* + * * + * Derived classes must provide: * + * - Init a version of Init that gives the connection * + * access to any hardware-specific values it needs * + * Must chain to the parent's Init routine. * + * - Send_Packet adds the CommHeaderType header, adds the packet * + * to the out-going queue * + * - Receive_Packet processes incoming ACK packets, detects resends,* + * adds new packets to the in-coming queue * + * - Get_Packet reads the next-available packet from the * + * receive queue * + * - Send the hardware-dependent data-sending routine * + * - Service_Send_Queue services the send queue; handles re-sends, * + * detects when outgoing packets have been ACK'd; * + * cleans out the queue of old packets * + * - Service_Receive_Queue services the receive queue; handles sending * + * ACK's for new or re-sent packets; cleans out * + * the queue of old packets * + * * + * Any other routines can be overloaded as the derived class needs. * + * * + * CLASS HIERARCHY: * + * ConnectionClass * + * | * + * | * + * -------------------------------------- * + * | | * + * | | * + * SequencedConnClass NonSequencedConnClass * + * | | * + * | | * + * IPXConnClass ------------------------ * + * | | | * + * | | | * + * IPXGlobalConnClass NullModemConnClass ModemConnClass * + * * + * * + * ConnectionClass: * + * Abstract base class. * + * Provides: Queue for sent/recv'd packets * + * PacketBuf for preparing packets * + * Timeout variables * + * Service() routine * + * * + * SequencedConnClass: * + * Abstract base class * + * Provides: * "Sequenced" ACK/Retry logic, in Service_Send_Queue() & * + * Service_Receive_Queue() routines * + * * Send_Packet(): adds header to packet, adds it to Queue * + * * Receive_Packet(): adds incoming packet to receive Queue, * + * handles incoming ACK's & resends * + * * Get_Packet(): gets packet from the receive queue * + * * + * NonSequencedConnClass: * + * Abstract base class * + * Provides: * "Non-Sequenced" ACK/Retry logic, in Service_Send_Queue() * + * & Service_Receive_Queue() routines * + * * Send_Packet(): adds header to packet, adds it to Queue * + * * Receive_Packet(): adds incoming packet to receive Queue, * + * handles incoming ACK's & resends * + * * Get_Packet(): gets packet from the receive queue * + * * + * IPXConnClass: * + * Provides: * Hardware-dependent IPX interface routines, which allow * + * Service_Send_Queue() & Service_Receive_Queue() to do * + * their job * + * * Ability to associate an IPX Address, a numerical ID, and * + * a character-string Name with a connection * + * Inherits: * Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * IPXGlobalConnClass: * + * Special type of IPX Connection; supports receiving packets from * + * multiple systems at once, and sending packets via Broadcast or * + * to a specific address. * + * Provides: * Specialized Receive_Packet() routine, which handles * + * receiving packets from multiple systems * + * * Specialized Send_Packet() & Get_Packet() routines, * + * which pass IPX address of destination through to * + * the application, giving the application control over * + * whether the packet will be Broadcast or sent to a * + * specific destination (embeds destination address within * + * the packet itself) * + * * Specialized Send routine, which extracts the destination * + * address from the packet * + * Inherits: * Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables, IPX-specific routines * + * * + * NullModemConnClass: * + * Provides: * Hardware-dependent Serial-communication routines, which * + * allow Service_Send_Queue() & Service_Receive_Queue() to * + * do their job * + * Inherits: * Non-Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * ModemConnClass: * + * Provides: * Hardware-dependent Modem-communication routines, which * + * allow Service_Send_Queue() & Service_Receive_Queue() to * + * do their job * + * Inherits: * Non-Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * So, do ya think this header is long enough, or what? * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#define CONN_DEBUG 0 + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This structure is the header prefixed to any packet sent by the application. +MagicNumber: This is a number unique to the application; it's up to the + Receive_Packet routine to check this value, to be sure we're + not getting data from some other product. This value should + be unique for each application. +Code: This will be one of the below-defined codes. +PacketID: This is a unique numerical ID for this packet. The Connection + sets this ID on all packets sent out. +---------------------------------------------------------------------------*/ +typedef struct { + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; +} CommHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + These are the possible values for the Code field of the CommHeaderType: + .....................................................................*/ + enum ConnectionEnum { + PACKET_DATA_ACK, // this is a data packet requiring an ACK + PACKET_DATA_NOACK, // this is a data packet not requiring an ACK + PACKET_ACK, // this is an ACK for a packet + PACKET_COUNT, // for computational purposes + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + ConnectionClass (int maxlen, unsigned short magicnum, + unsigned long retry_delta, unsigned long max_retries, + unsigned long timeout); + virtual ~ConnectionClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void) {}; + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req) = 0; + virtual int Receive_Packet (void * buf, int buflen) = 0; + virtual int Get_Packet (void * buf, int * buflen) = 0; + + /*..................................................................... + The main polling routine for the connection. Should be called as often + as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine is used by the retry logic; returns the current time in + 60ths of a second. + .....................................................................*/ + static unsigned long Time (void); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned short Magic_Num (void) { return (MagicNum); } + unsigned long Retry_Delta (void) { return (RetryDelta); } + void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;} + unsigned long Max_Retries (void) { return (MaxRetries); } + void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;} + unsigned long Time_Out (void) { return (Timeout); } + void Set_TimeOut (unsigned long t) { Timeout = t;} + unsigned long Max_Packet_Len (void) { return (MaxPacketLen); } + static char * Command_Name(int command); + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue(void) = 0; + virtual int Service_Receive_Queue(void) = 0; + + /*..................................................................... + This routine actually performs a hardware-dependent data send. It's + pure virtual, so it >must< be defined by a derived class. + .....................................................................*/ + virtual int Send(char *buf, int buflen) = 0; + + /*..................................................................... + This is the maximum packet length, including our own internal header. + .....................................................................*/ + int MaxPacketLen; + + /*..................................................................... + Packet staging area; this is where the CommHeaderType gets tacked onto + the application's packet before it's sent. + .....................................................................*/ + char *PacketBuf; + + /*..................................................................... + This is the magic number assigned to this connection. It is the first + few bytes of any transmission. + .....................................................................*/ + unsigned short MagicNum; + + /*..................................................................... + This value determines the time delay before a packet is re-sent. + .....................................................................*/ + unsigned long RetryDelta; + + /*..................................................................... + This is the maximum number of retries allowed for a packet; if this + value is exceeded, the connection is probably broken. + .....................................................................*/ + unsigned long MaxRetries; + + /*..................................................................... + This is the total timeout for this connection; if this time is exceeded + on a packet, the connection is probably broken. + .....................................................................*/ + unsigned long Timeout; + + /*..................................................................... + Names of all packet commands + .....................................................................*/ + static char *ConnectionClass::Commands[PACKET_COUNT]; +}; + +#endif + diff --git a/TIBERIANDAWN/CONNMGR.H b/TIBERIANDAWN/CONNMGR.H new file mode 100644 index 000000000..e375af495 --- /dev/null +++ b/TIBERIANDAWN/CONNMGR.H @@ -0,0 +1,148 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\connmgr.h_v 1.25 02 Jan 1996 11:14:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager base class. This is an abstract base * + * class that's just a shell for more functional derived classes. * + * The main job of the Connection Manager classes is to parse a "pool" of * + * incoming packets, which may be from different computers, and distribute * + * those packets to Connection Classes via their Receive_Packet function. * + * * + * This class should be the only access to the network/modem for the * + * application, so if the app needs any functions to access the * + * connections or the queue's, the derived versions of this class should * + * provide them. * + * * + * It's up to the derived class to define: * + * - Service: polling routine; should Service each connection * + * - Init: initialization; should perform hardware-dependent * + * initialization, then Init each connection; this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Send_Message:sends a packet across the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Get_Message: gets a message from the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * * + * If the derived class supports multiple connections, it should provide * + * functions for creating the connections, associating them with a name * + * or ID or both, destroying them, and sending data through all or any * + * connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNMGR_H +#define CONNMGR_H + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONNECTION_NONE = -1, // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/Destructor. These currently do nothing. + .....................................................................*/ + ConnManClass (void) {}; + virtual ~ConnManClass () {}; + + /*..................................................................... + The Service routine: + - Parses incoming packets, and adds them to the Receive Queue for the + Connection Class(s) for this protocol + - Invokes each connection's Service routine; returns an error if the + connection's Service routine indicates an error. + .....................................................................*/ + virtual int Service (void) = 0; + + /*..................................................................... + Sending & receiving data + .....................................................................*/ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE) = 0; + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id) = 0; + + /*..................................................................... + Connection management + .....................................................................*/ + virtual int Num_Connections(void) = 0; + virtual int Connection_ID(int index) = 0; + virtual int Connection_Index(int id) = 0; + + /*..................................................................... + Queue utility routines + .....................................................................*/ + virtual int Global_Num_Send(void) = 0; + virtual int Global_Num_Receive(void) = 0; + virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0; + virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0; + + /*..................................................................... + Timing management + .....................................................................*/ + virtual void Reset_Response_Time(void) = 0; + virtual unsigned long Response_Time(void) = 0; + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) = 0; + + /*..................................................................... + Debugging + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int maxnames) = 0; + virtual void Mono_Debug_Print(int index, int refresh) = 0; + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This abstract class contains no data members; but a derived class + will contain: + - An instance of one or more derived Connection Classes + - A buffer to store incoming packets + .....................................................................*/ +}; + +#endif diff --git a/TIBERIANDAWN/CONQUER.CPP b/TIBERIANDAWN/CONQUER.CPP new file mode 100644 index 000000000..cf88fff9d --- /dev/null +++ b/TIBERIANDAWN/CONQUER.CPP @@ -0,0 +1,4259 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\conquer.cpv 2.18 16 Oct 1995 16:50:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Play_Movie -- Plays a VQ movie. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Trap_Object -- gets a ptr to object of given type & coord * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * Validate_Error -- prints an error message when an object fails validation * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" + +#define SHAPE_TRANS 0x40 + +void *Get_Shape_Header_Data(void *ptr); + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(); +void Keyboard_Process(KeyNumType & input); +#ifndef DEMO +static void Message_Input(KeyNumType &input); +#endif +bool Color_Cycle(void); +bool Map_Edit_Loop(void); +void Trap_Object(void); + +#ifdef CHEAT_KEYS +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char *string ); +#endif +static void Do_Record_Playback(void); +extern void Register_Game_Start_Time(void); +extern void Register_Game_End_Time(void); +extern void Send_Statistics_Packet(void); +extern "C" { + extern char *__nheapbeg; +} +bool InMainLoop = false; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +extern int TotalLocks; +extern bool Spawn_WChat(bool can_launch); +extern bool SpawnedFromWChat; +void Main_Game(int argc, char *argv[]) +{ + bool fade = false; // don't fade title screen the first time through + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + CCDebugString ("C&C95 - Game initialisation complete.\n"); + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + + if (RunningAsDLL) { + return; + } + ScenarioInit = 0; // Kludge. +// Theme.Queue_Song(THEME_PICK_ANOTHER); + + fade = true; + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + Fade_Palette_To(GamePalette, FADE_PALETTE_MEDIUM, NULL); + Keyboard::Clear(); + + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (PlaybackGame) { + Hide_Mouse(); + } else { + Show_Mouse(); + } + + SpecialDialog = SDLG_NONE; + //Start_Profiler(); + if (GameToPlay == GAME_INTERNET){ + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + }else{ + DDEServer.Disable(); + } + + InMainLoop = true; + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { + TotalLocks=0; + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + //Stop_Profiler(); + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { + + /* + ** Call the game's main loop + */ + TotalLocks=0; + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + //Stop_Profiler(); + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } +#endif + //Stop_Profiler(); + InMainLoop = false; + + if (!GameStatisticsPacketSent && PacketLater){ + Send_Statistics_Packet(); + } + + /* + ** Scenario is done; fade palette to black + */ + Fade_Palette_To(BlackPalette, FADE_PALETTE_SLOW, NULL); + VisiblePage.Clear(); + +#ifndef DEMO + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if ( (RecordGame && !SuperRecord) || PlaybackGame) { + RecordFile.Close(); + } + + if (!PlaybackGame){ + + switch (GameToPlay){ + case GAME_NULL_MODEM: + case GAME_MODEM: + //ST - 1/2/2019 4:04PM + //Modem_Signoff(); + break; + + case GAME_IPX: + Shutdown_Network(); + break; + + case GAME_INTERNET: + //Winsock.Close(); + break; + } + } + + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (PlaybackGame) { + Show_Mouse(); + GameToPlay = GAME_NORMAL; + PlaybackGame = 0; + } + + + /* + ** If we were spawned from WChat then dont go back to the main menu - just quit + ** + ** New: If spawned from WChat then maximise WChat and go back to the main menu after all + */ +#ifdef FORCE_WINSOCK + if (Special.IsFromWChat){ + Shutdown_Network(); // Clear up the pseudo IPX stuff + Winsock.Close(); + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + GameToPlay = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us + //break; + } +#endif //FORCE_WINSOCK + +#endif //DEMO + + } + +#ifdef DEMO + Hide_Mouse(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, NULL); + Clear_KeyBuffer(); + Get_Key(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); +// Show_Mouse(); +#else + + /* + ** Free the scenario description buffers + */ + Free_Scenario_Descriptions(); +#endif + +#ifndef NOMEMCHECK + Uninit_Game(); +#endif + +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +extern int DebugColour; +void Keyboard_Process(KeyNumType &input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + +#ifndef DEMO + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); +#endif + /* + ** Use WWKEY values because KN values have WWKEY_VK_BIT or'd in with them + ** and we need WWKEY_VK_BIT to still be set if it is. + */ + KeyNumType plain = input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT); + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + switch (input) { + case (int)KN_M|(int)KN_SHIFT_BIT: + case (int)KN_M|(int)KN_ALT_BIT: + case (int)KN_M|(int)KN_CTRL_BIT: + PlayerPtr->Credits += 10000; + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +//#ifdef CHEAT_KEYS + if (/*Debug_Playtest && */input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if (Debug_Flag && input == KN_SLASH) { + if (GameToPlay != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +//#endif + + /* + ** If the options key(s) were pressed, then bring up the options screen. + */ + if (input == KN_SPACE || input == KN_ESC) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + //DebugColour++; + //DebugColour &=7; + } + + /* + ** Process prerecorded team selection. This will be an addative select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; + + switch (KN_To_VK(plain)) { + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + case VK_HOME: + if (CurrentObject.Count()) { + Map.Center_Map(); + Map.Flag_To_Redraw(true); + break; + } + // Fall into next case. + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + case VK_H: + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + break; + } + } + Map.Center_Map(); + Map.Flag_To_Redraw(true); + break; + +#ifdef CHEAT_KEYS + /* + ** Toggle free scrolling mode. + */ + case VK_F: + Options.IsFreeScroll = (Options.IsFreeScroll == false); + break; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + case VK_N: + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + case VK_R: + if (/*GameToPlay != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + break; + + /* + ** Handle making and breaking alliances. + */ + case VK_A: + if (GameToPlay != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, (int)CurrentObject[0]->Owner())); + } + } + } + break; + + /* + ** Control the remembered tactical location. + */ + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + if (!Debug_Map) { + Handle_View(KN_To_VK(plain) - VK_F7, action); + } + break; +#if (0) + case VK_F11: + Winsock.Set_Protocol_UDP(FALSE); + break; + + case VK_F12: + Winsock.Set_Protocol_UDP(TRUE); + break; +#endif //(0) + + + /* + ** Control the custom team select state. + */ + case VK_1: + case VK_2: + case VK_3: + case VK_4: + case VK_5: + case VK_6: + case VK_7: + case VK_8: + case VK_9: + case VK_0: + Handle_Team(KN_To_VK(plain) - VK_1, action); + break; + + /* + ** All selected units will go into idle mode. + */ + case VK_S: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && + tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, tech->As_Target())); + } + } + } + break; + + /* + ** All selected units will attempt to scatter. + */ + case VK_X: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, tech->As_Target())); + } + } + } + break; + + /* + ** All selected units will attempt to go into guard area mode. + */ + case VK_G: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA)); + } + } + } + break; + + default: + break; + } + +#ifdef NEVER + FacingType facing = KN_To_Facing(input); + + /* + ** Scroll the map according to the cursor key pressed. + */ + if (facing != FACING_NONE) { + Map.Scroll_Map(facing); + input = 0; + facing = FACING_NONE; + } +#endif + +#ifdef NEVER + /* + ** If the key is pressed, then select the next object. + */ + if (input == KN_TAB) { + ObjectClass * obj = Map.Next_Object(CurrentObject); + if (obj) { + if (CurrentObject) { + CurrentObject->Unselect(); + } + obj->Select(); + } + } +#endif + +#ifdef CHEAT_KEYS + if (Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +#ifndef DEMO +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * MAX_MESSAGE_LENGTH has increased over the DOS version. COMPAT_MESSAGE_LENGTH reflects * * + * the length of the DOS message and also the length of the message in the packet header. * + * To allow transmission of longer messages I split the message into COMPAT_MESSAGE_LENGTH-4 * + * sized chunks and use the extra space after the zero terminator to specify which segment * + * of the whole message this is and also to supply a crc for the string. * + * This allows message segments to arrive out of order and still be displayed correctly. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + * 03/26/1995 ST : Modified to break up longer messages into multiple packets * + *=============================================================================================*/ +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+12]; + int id; +//PG_TO_FIX +#if (0) + SerialPacketType *serial_packet; + int i; + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; +#endif + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ + if (input >= KN_F1 && input < (KN_F1 + MPlayerMax) && + Messages.Get_Edit_Buf()==NULL) { + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (GameToPlay==GAME_NULL_MODEM + || GameToPlay==GAME_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + if (input==KN_F1 || input==(KN_F1 + MPlayerMax - 1)) { + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } + } else { + + /* + ** For a network game: + ** F1-F3 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F4 = "To All:" + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + if (input==(KN_F1 + MPlayerMax - 1) && Messages.Get_Edit_Buf()==NULL) { + + MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } else { + if (Messages.Get_Edit_Buf()==NULL) { + if ((input - KN_F1) < Ipx.Num_Connections() && !MPlayerObiWan) { + + id = Ipx.Connection_ID(input - KN_F1); + MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt,Text_String(TXT_TO),Ipx.Connection_Name(id)); + + Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } + } + } + } + } + } + + /* + ** Function key input is meaningless beyond this point + */ + if (input >= KN_F1 && input <= KN_F10) return; + if (input >= KN_F11 && input <= KN_F12) return; + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + rc = Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. This assumes the map is going to + ** completely refresh all cells covered by the messages. Set DisplayClass's + ** IsToRedraw to true to tell it to re-compute the cells that it needs to + ** redraw. + */ + if (rc==2) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + + /* + ** Send a message + */ + if (rc==3) { +// +// PG_TO_FIX +#if (0) + /*..................................................................... + Store this message in our LastMessage buffer; the computer may send + us a version of it later. + .....................................................................*/ + if (strlen(Messages.Get_Edit_Buf())) { + strcpy(LastMessage,Messages.Get_Edit_Buf()); + } + + message_length = strlen(Messages.Get_Edit_Buf()); + + long actual_message_size; + char *the_string; + + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (GameToPlay==GAME_NULL_MODEM + || GameToPlay==GAME_MODEM){ + //|| GameToPlay==GAME_INTERNET) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); + + while (sent_so_far < message_length){ + + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, MPlayerName); + memcpy (serial_packet->Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = serial_packet->Message; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + + *(serial_packet->Message + COMPAT_MESSAGE_LENGTH-5) = 0; + /* + ** Flag this message segment as either a message head or a message tail. + */ + *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + serial_packet->ID = MPlayerLocalID; + + NullModem.Send_Message(NullModem.BuildBuf, sizeof(SerialPacketType), 1); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + + } else { + + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); + + while (sent_so_far < message_length){ + + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, MPlayerName); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + /* + ** Flag this message segment as either a message head or a message tail. + */ + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + + GPacket.Message.ID = MPlayerLocalID; + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (MessageAddress.Is_Broadcast()) { + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, + &MessageAddress); + Ipx.Service(); + } + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + } + + } + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); +#endif + } +} +#endif + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: true if palette changed * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + *=============================================================================================*/ +bool Color_Cycle(void) +{ + static CountDownTimerClass _timer(BT_SYSTEM,0L); + static CountDownTimerClass _ftimer(BT_SYSTEM,0L); + static bool _up = false; + bool changed = false; + + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer.Time()) { + _ftimer.Set(TIMER_SECOND/8); + + /* + ** Pulse the pulsing text color. + */ + #define STEP_RATE 5 + if (_up) { + GamePalette[767] += STEP_RATE; + GamePalette[766] += STEP_RATE; + GamePalette[765] += STEP_RATE; + if (GamePalette[767] > MAX_CYCLE_COLOR) { + GamePalette[767] = MAX_CYCLE_COLOR; + GamePalette[766] = MAX_CYCLE_COLOR; + GamePalette[765] = MAX_CYCLE_COLOR; + _up = false; + } + } else { + GamePalette[767] -= STEP_RATE; + GamePalette[766] -= STEP_RATE; + GamePalette[765] -= STEP_RATE; + if ((unsigned)GamePalette[767] < MIN_CYCLE_COLOR) { + GamePalette[767] = MIN_CYCLE_COLOR; + GamePalette[766] = MIN_CYCLE_COLOR; + GamePalette[765] = MIN_CYCLE_COLOR; + _up = true; + } + } + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer.Time()) { + unsigned char colors[3]; + + _timer.Set(TIMER_SECOND/4); + + memmove(colors, &GamePalette[(CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1)*3], sizeof(colors)); + memmove(&GamePalette[(CYCLE_COLOR_START+1)*3], &GamePalette[CYCLE_COLOR_START*3], (CYCLE_COLOR_COUNT-1)*3); + memmove(&GamePalette[CYCLE_COLOR_START*3], colors, sizeof(colors)); + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + Wait_Vert_Blank(); + Set_Palette(GamePalette); + return (true); + } + return (false); +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ +#ifndef DEMO + int i; + int id; + int color; + unsigned short magic_number; + unsigned short crc; +#endif + + /* + ** Score maintenance + */ + if (SampleType) { + Theme.AI(); + Speak_AI(); + } + +#ifndef DEMO + /* + ** Network maintenance + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!NetOpen) { + if (Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID)) { + if (GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (GPacket.Command == NET_SIGN_OFF) { + for (i = 0; i < Ipx.Num_Connections(); i++) { + + id = Ipx.Connection_ID(i); + + if (!strcmp (GPacket.Name, Ipx.Connection_Name(id) ) && + GAddress == (*Ipx.Connection_Address(id))) { + + CCDebugString ("C&C95 = Destroying connection due to sign off\n"); + Destroy_Connection (id,0); + } + } + } else { + + /* + ** Process a message from another user. + */ + if (GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + char txt[80]; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!NetProtect) { + msg_ok = true; + } else { + if (GPacket.Message.NameCRC == Compute_Name_CRC(MPlayerGameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message(txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(false); + + /* + ** Save this message in our last-message buffer + */ + if (strlen(GPacket.Message.Buf)) { + strcpy(LastMessage, GPacket.Message.Buf); + } + } + } else { + Process_Global_Packet(&GPacket, &GAddress); + } + } + } + } + } + } + + /* + ** Modem and Null Modem maintenance + */ + if (GameToPlay == GAME_NULL_MODEM + || ((GameToPlay == GAME_MODEM) && ModemService)){ + //|| GameToPlay == GAME_INTERNET) { + + // PG_TO_FIX + //NullModem.Service(); + } +#endif +} + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const *Language_Name(char const *basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const *name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const *Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const *name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Will delay for up to 1/15 of a second. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Delay one tick and keep a record that one tick was "wasted" here. + ** This accumulates into a running histogram of performance. + */ + SpareTicks += FrameTimer.Time(); + while (FrameTimer.Time()) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + KeyNumType input = KN_NONE; + int x, y; + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + Color_Cycle(); + Call_Back(); +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + +// InMainLoop = true; + + /* + ** I think I'm gonna cry if this makes it work + */ + if (Get_Mouse_State())Show_Mouse(); + + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); + + /* + ** Sync-bug trapping code + */ + if (Frame >= TrapFrame) { + Trap_Object(); + } + + // + // Initialize our AI processing timer + // + ProcessTimer.Set(0, true); + + +#if 1 + if (TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + Heap_Dump_Check( "After Trap" ); + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (GameToPlay != GAME_NORMAL && CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + framedelay = 60 / DesiredFrameRate; + FrameTimer.Set(framedelay); + } else { + FrameTimer.Set(Options.GameSpeed); + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!PlaybackGame) { + if (SpecialDialog == SDLG_NONE && GameInFocus) { + + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } +// HidPage.Lock(); + Map.Render(); +// HidPage.Unlock(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (RecordGame || PlaybackGame) { + Do_Record_Playback(); + } + + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will sync on different machines. + */ + Map.Layer[LAYER_GROUND].Sort(); + +// Heap_Dump_Check( "Before Logic.AI" ); + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + +// Heap_Dump_Check( "After Logic.AI" ); + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Messages.Manage()) { + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + ProcessTicks += ProcessTimer.Time(); + ProcessFrames++; + +// Heap_Dump_Check( "Before Queue_AI" ); + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + +// Heap_Dump_Check( "After Queue_AI" ); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + Call_Back(); + +// Heap_Dump_Check( "After Call_Back" ); + + /* + ** Perform any win/lose code as indicated by the global control flags. + */ + if (EndCountDown) EndCountDown--; + + /* + ** Check for player wins or loses according to global event flag. + */ + + + + if (PlayerWins) { + + if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + } + if (PlayerLoses) { + + if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + } + if (PlayerRestarts) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Very rarely, the human players will get a message from the computer. + */ + if (GameToPlay != GAME_NORMAL && MPlayerGhosts && IRandom(0,10000) == 1) { + Computer_Message(); + } + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { +#ifdef GERMAN + if (CCMessageBox().Process ("Kartenfehler!","Halt","Weiter")==0) +#else +#ifdef FRENCH + if (CCMessageBox().Process ("Erreur de carte!","Stop","Continuer")==0) +#else + if (CCMessageBox().Process ("Map Error!","Stop","Continue")==0) +#endif +#endif + GameActive = false; + Map.Validate(); // give debugger a chance to catch it + } + } + + Sync_Delay(); +// InMainLoop = false; + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + +#if (0) +#define VQ_THREAD_BUFFER_SIZE 1024*1024 +#define VQ_THREAD_BUFFER_CHUNK VQ_THREAD_BUFFER_SIZE/4 +unsigned char *VQThreadBuffer = NULL; +volatile bool ThreadReading = false; +unsigned long VQThreadBlockHead; +unsigned long VQThreadBlockTail; +unsigned long VQBytesLeft; +unsigned long VQBytesRead; + + +void Init_VQ_Threading(CCFileClass *file) +{ + if (!VQThreadBuffer){ + VQThreadBuffer = new unsigned char [VQ_THREAD_BUFFER_SIZE]; + } + Force_VM_Page_In(VQThreadBuffer, VQ_THREAD_BUFFER_SIZE); + VQThreadBlockHead = 0; + VQThreadBlockTail = 0; + VQBytesRead = 0; + VQBytesLeft = file->Size(); +} + + +void Cleanup_VQ_Threading(void) +{ + while (ThreadReading){} + if (VQThreadBuffer){ + delete VQThreadBuffer; + VQThreadBuffer = NULL; + } +} + +unsigned long __stdcall Thread_Read(void *file) +{ + int bytes_to_read; + int left_to_read; + int read_this_time; + unsigned long head; + int sleep_time; + + CCFileClass *ccfile = (CCFileClass*)file; + + bytes_to_read = MIN (VQBytesLeft, VQ_THREAD_BUFFER_CHUNK); + + if (!bytes_to_read){ + ThreadReading = false; + return(0); + } + + left_to_read = bytes_to_read; + + while (left_to_read){ + read_this_time = MIN(8*1024, left_to_read); + //if (read_this_time & 3){ + ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time); + //}else{ + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+read_this_time/4, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*2, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*3, read_this_time/4); + //} + VQThreadBlockHead += read_this_time; + left_to_read -= read_this_time; + + head = VQThreadBlockHead; + if (head VQThreadBlockTail){ + bytes_to_read = MIN(bytes, VQThreadBlockHead-VQThreadBlockTail); + + }else{ + bytes_to_read = MIN(bytes, VQThreadBlockHead+VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail); + } + + }while(ThreadReading && bytes_to_read VQ_THREAD_BUFFER_SIZE){ + + int first_chunk = VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail; + int second_chunk = bytes_to_read - first_chunk; + + memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, first_chunk); + memcpy ((unsigned char*)buffer + first_chunk, VQThreadBuffer, second_chunk); + }else{ + memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, bytes_to_read); + } + + VQThreadBlockTail += bytes_to_read; + VQThreadBlockTail &= VQ_THREAD_BUFFER_SIZE - 1; + + unsigned long head = VQThreadBlockHead; + if (headVQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = VQ_Thread_Read (file, buffer, nbytes); + //error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + if (error == nbytes) error = 0; + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + //error = (file->Seek(nbytes, SEEK_CUR) == -1); + VQ_Thread_Seek(nbytes); + error = 0; + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + //file->Set_Buffer_Size(8*1024); + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + + if (error != -1){ + Init_VQ_Threading(file); + Read_VQ_Thread_Block(file); + CountDownTimerClass timer; + timer.Set(60); + while (ThreadReading || timer.Time()){} + } + break; + + case VQACMD_CLOSE: + Cleanup_VQ_Threading(); + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return(error); +} +#endif //(0) +//#if (0) + + +// PG_TO_FIX +typedef void* VQAHandle; + +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes) +{ + return 0;; +#if (0) + CCFileClass *file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + file->Set_Buffer_Size(8*1024); + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return(error); +#endif +} + +//#endif //(0) + + +void Rebuild_Interpolated_Palette(unsigned char *interpal) +{ + for (int y=0 ; y<255 ; y++){ + for (int x=y+1 ; x<256 ; x++){ + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char *InterpolatedPalettes[100]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +int Load_Interpolated_Palettes(char const *filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + +// RawFileClass *palette_file; + + if (!add){ + for (i=0 ; iUnselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + while (CurrentObject.Count()) { + + int count_before = CurrentObject.Count(); + CurrentObject[0]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[0]); + } + } + //End of change - JAS 6/28/2019 +} + + +void Unselect_All_Except(ObjectClass* object) +{ + int index = 0; + while (index < CurrentObject.Count()) { + + if (CurrentObject[index] == object) { + index++; + continue; + } + + int count_before = CurrentObject.Count(); + CurrentObject[index]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[index]); + } + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char *retval = NULL; + char *buffer = NULL; + void *ptr; + + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we dont add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *((char *)(Add_Long_To_Pointer(ptr, ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]))); + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + + + +void CC_Texture_Fill (void const *shapefile, int shapenum, int xpos, int ypos, int width, int height) +{ + unsigned char *shape_pointer; + //unsigned char *shape_save; + unsigned long shape_size; + //int x,y; + + if (shapefile && shapenum != -1) { + + /* + ** Build frame returns a pointer now instead of the shapes length + */ + shape_size=Build_Frame(shapefile , shapenum , _ShapeBuffer); + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); + Get_Key(); + } + + if (shape_size) { + shape_pointer = (unsigned char *)Get_Shape_Header_Data ((void*)shape_size); + int source_width = Get_Build_Frame_Width (shapefile); + int source_height = Get_Build_Frame_Height (shapefile); + + + LogicPage->Texture_Fill_Rect (xpos, ypos, width, height, shape_pointer, source_width, source_height); +#if (0) + if (LogicPage->Lock()){ + + for (y = ypos ; y < ypos + MIN(source_height, height) ; y++ ){ + + shape_save = shape_pointer; + + for (x = xpos ; x < xpos + MIN(source_width, width) ; x++ ){ + LogicPage->Put_Pixel (x, y, *shape_pointer++); + } + + shape_pointer = shape_save + source_width; + } + + LogicPage->Unlock(); + } +#endif + } + } +} + + + + +extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name, char override_owner, int scale); +extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); +extern void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame); + +void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, int scale) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, NULL, -1, scale); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); +} + + +void CC_Draw_Shape(ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, char override_owner) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, shape_file_name, override_owner, 0x100); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); +} + + +void CC_Draw_Pip(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata) +{ + if (window == WINDOW_VIRTUAL) { + DLL_Draw_Pip_Intercept(object, shapenum); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); +} + + +void CC_Draw_Line(int x, int y, int x1, int y1, unsigned char color, int frame, WindowNumberType window) +{ + if (window == WINDOW_VIRTUAL) { + DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); + return; + } + + LogicPage->Draw_Line(x, y, x1, y1, color); +} + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +//#pragma off(unreferenced) +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata) +{ +#if(TRUE) + int predoffset; + char *shape_pointer; + unsigned long shape_size; + + if (shapefile && shapenum != -1) { + + /* + ** Build frame returns a pointer now instead of the shapes length + */ + shape_size=Build_Frame(shapefile , shapenum , _ShapeBuffer); + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); + Get_Key(); + } + + if (shape_size) { + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + (WindowList[window][WINDOWX] << 3) + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH] << 3, + WindowList[window][WINDOWHEIGHT]); + + shape_pointer = (char *)shape_size; + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = Map.SpecialGhost; + } + + predoffset = Frame; + + if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + predoffset = -predoffset; + } + + if (draw_window.Lock()){ + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + } + draw_window.Unlock(); +// } else { +// Mono_Printf( "Overrun ShapeBuffer!!!!!!!!!\n" ); + } + } +#endif +} + + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference((UnitType)id)); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference((StructType)id)); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference((InfantryType)id)); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference((AircraftType)id)); + } + return(NULL); +} + + +/*************************************************************************** + * Trap_Object -- gets a ptr to object of given type & coord * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1995 BRR : Created. * + *=========================================================================*/ +void Trap_Object(void) +{ + int i; + + TrapObject.Ptr.All = NULL; + + switch (TrapObjType) { + case RTTI_AIRCRAFT: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Ptr(i)->Coord == TrapCoord || Aircraft.Ptr(i)==TrapThis) { + TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); + break; + } + } + break; + + case RTTI_ANIM: + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Ptr(i)->Coord == TrapCoord || Anims.Ptr(i)==TrapThis) { + TrapObject.Ptr.Anim = Anims.Ptr(i); + break; + } + } + break; + + case RTTI_BUILDING: + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->Coord == TrapCoord || Buildings.Ptr(i)==TrapThis) { + TrapObject.Ptr.Building = Buildings.Ptr(i); + break; + } + } + break; + + case RTTI_BULLET: + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Ptr(i)->Coord == TrapCoord || Bullets.Ptr(i)==TrapThis) { + TrapObject.Ptr.Bullet = Bullets.Ptr(i); + break; + } + } + break; + + case RTTI_INFANTRY: + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->Coord == TrapCoord || Infantry.Ptr(i)==TrapThis) { + TrapObject.Ptr.Infantry = Infantry.Ptr(i); + break; + } + } + break; + + case RTTI_UNIT: + for (i = 0; i < Units.Count(); i++) { + if (Units.Ptr(i)->Coord == TrapCoord || Units.Ptr(i)==TrapThis) { + TrapObject.Ptr.Unit = Units.Ptr(i); + break; + } + } + break; + + /* + ** Last-ditch find-the-object-right-now-darnit loop + */ + case RTTI_NONE: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || Aircraft.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); + TrapObjType = RTTI_AIRCRAFT; + return; + } + } + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Raw_Ptr(i)->Coord == TrapCoord || Anims.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); + TrapObjType = RTTI_ANIM; + return; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || Buildings.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); + TrapObjType = RTTI_BUILDING; + return; + } + } + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || Bullets.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); + TrapObjType = RTTI_BULLET; + return; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || Infantry.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); + TrapObjType = RTTI_INFANTRY; + return; + } + } + for (i = 0; i < Units.Count(); i++) { + if (Units.Raw_Ptr(i)->Coord == TrapCoord || Units.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Unit = Units.Raw_Ptr(i); + TrapObjType = RTTI_UNIT; + return; + } + } + + default: + break; + } +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +void VQA_PauseAudio(void) {}; +void Check_VQ_Palette_Set(void); + +long VQ_Call_Back(unsigned char *, long ) +{ +// PG_TO_FIX - 1/2/2019 3:59PM +#if (0) + int key = 0; + if (Keyboard::Check()){ + key = Keyboard::Get(); + Keyboard::Clear(); + } + + Check_VQ_Palette_Set(); + + Interpolate_2X_Scale(&SysMemPage,&SeenBuff,NULL); + //Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard::Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus){ + VQA_PauseAudio(); + while (!GameInFocus){ + Keyboard::Check(); + Check_For_Focus_Loss(); + } + } +#endif + return(false); +} + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + switch (CurrentObject[0]->What_Am_I()) { + case RTTI_UNIT: + case RTTI_INFANTRY: + case RTTI_AIRCRAFT: + if (((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + break; + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->Is_Selected_By_Player()) obj->Group = team; + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->Is_Selected_By_Player()) obj->Group = team; + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->Is_Selected_By_Player()) obj->Group = team; + } + } + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < sizeof(Views)/sizeof(Views[0])) { + if (action == 0) { + Map.Set_Tactical_Position(Cell_Coord(Views[view])&0xFF00FF00L); + Map.Flag_To_Redraw(true); + } else { + Views[view] = Coord_Cell(Map.TacticalCoord); + } + } +} + +#ifdef CHEAT_KEYS +void Heap_Dump_Check( char *string ) +{ +#if 0 + struct _heapinfo h_info; + int heap_status; +#endif + + + if ( !Debug_Trap_Check_Heap ) { // check the heap? + return; + } + +// Debug_Heap_Dump = true; + + Smart_Printf( "%s\n", string ); + + Dump_Heap_Pointers(); + +#if 0 + heap_status = _heapset( 0xee ); + +#if (1) + if ( heap_status == _HEAPOK || + heap_status == _HEAPEMPTY ) { + Debug_Heap_Dump = false; + return; + } + + h_info._pentry = NULL; + + for(;;) { + heap_status = _heapwalk( &h_info ); + + if ( heap_status != _HEAPOK ) + break; + } + + if (heap_status != _HEAPEND && + heap_status != _HEAPEMPTY) { +#endif + h_info._pentry = NULL; + + for(;;) { + heap_status = _heapwalk( &h_info ); + + if ( heap_status != _HEAPOK ) + break; + + Smart_Printf( " %s block at %Fp of size %4.4X\n", + (h_info._useflag == _USEDENTRY ? "USED" : "FREE"), + h_info._pentry, h_info._size ); + } + + Smart_Printf( " %d block at %Fp of size %4.4X\n", + h_info._useflag, h_info._pentry, h_info._size ); + + switch ( heap_status ) { +// case _HEAPEND: +// Smart_Printf( "OK - end of heap\n" ); +// break; + +// case _HEAPEMPTY: +// Smart_Printf( "OK - heap is empty\n" ); +// break; + + case _HEAPBADBEGIN: + Smart_Printf( "ERROR - heap is damaged\n" ); + break; + + case _HEAPBADPTR: + Smart_Printf( "ERROR - bad pointer to heap\n" ); + break; + + case _HEAPBADNODE: + Smart_Printf( "ERROR - bad node in heap\n" ); + break; + } +#if (1) + } +#endif +#endif + +// Debug_Heap_Dump = false; +} + + +void Dump_Heap_Pointers( void ) +{ +// ST - 1/2/2019 4:04PM +#if (0) + char *ptr, *lptr, *nptr, *cptr, *dptr, *wlptr, *nlptr, *aptr, *clptr; + int numallocs, numfrees, sizefree; + static char _freeorused[2][5] = { "FREE", "USED" }; + + + ptr = (char *)__nheapbeg; + + while ( ptr ) { + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p pre header\n", (ptr - 8) ); + Hex_Dump_Data( (ptr - 8), 0x08); + + Smart_Printf( "%p header\n", ptr ); + Hex_Dump_Data( ptr, 0x30); + } + + dptr = (char *)*(int *)(ptr + 0x0c); + + sizefree = *(int *)(ptr + 0x14); + numallocs = *(int *)(ptr + 0x18); + numfrees = *(int *)(ptr + 0x1c); + + cptr = (char *)*(int *)(ptr + 0x24); + lptr = (char *)*(int *)(ptr + 0x28); + + if ( ((int)cptr & 0xff000000) || + ((int)dptr & 0xff000000) || + ((int)lptr & 0xff000000) ) { + Error_In_Heap_Pointers( "local free heap ptrs too large" ); + } + + if ( Debug_Heap_Dump ) { + if ( lptr != dptr || + lptr != cptr || + cptr != dptr ) { + Smart_Printf( "The pointers are different!!\n" ); + } + } + + nptr = (char *)*(int *)(ptr + 8); // next block + + if ( ((int)nptr & 0xFF000000) ) { + Error_In_Heap_Pointers( "next block ptr too large" ); + } + + if ( lptr != (ptr + 0x20) ) { + + if ( !(*((int *)(ptr + 0x20))) ) { // allocated + aptr = (ptr + 0x2c); + + while ( aptr < lptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > lptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); + } + numfrees--; + } + + wlptr = lptr; + + while ( wlptr != (ptr + 0x20) ) { + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p link %s, size %X.\n", wlptr, + _freeorused[ ((*wlptr) & 1) ], + ((*(int *)(wlptr)) & 0xfffffffe) ); + Hex_Dump_Data( wlptr, 0x10); + } + + nlptr = (char *)*(int *)(wlptr + 8); + + if ( !(*((int *)(wlptr))) ) { // allocated + aptr = (wlptr + 0x0c); + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(wlptr)) & 0xfffffffe); + } + numfrees--; + + aptr = (wlptr + ((*(int *)(wlptr)) & 0xfffffffe)); + } + + if (nlptr == (ptr + 0x20) ) { + clptr = nptr; + } else { + clptr = nlptr; + } + + while ( aptr < clptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > clptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + + wlptr = nlptr; + } + } else { +// Smart_Printf( "only link %s, size %X.\n", +// _freeorused[ ((*lptr) & 1) ], +// ((*(int *)(lptr)) & 0xfffffffe) ); + + if ( !(*((int *)(lptr))) ) { // allocated + aptr = (ptr + 0x2c); + + while ( aptr < nptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > nptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); + } + numfrees--; + } + } + + if ( sizefree != 0 && sizefree != -1 ) { + Smart_Printf( "sizefree left over %X.\n", sizefree ); + } + + if ( numallocs != 0 ) { + Smart_Printf( "numallocs unaccounted for %d.\n", numallocs ); + } + + if ( numfrees != 0 ) { + Smart_Printf( "numfrees unaccounted for %d.\n", numfrees ); + } + + ptr = nptr; + } +#endif +} + + +void Error_In_Heap_Pointers( char *string ) +{ + Smart_Printf( "Error in Heap for %s\n", string ); +} +#endif + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert * + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) +{ + char volume_name[128]; + unsigned filename_length; + unsigned misc_dword; + //unsigned error_code; + int count = 0; + + CountDownTimerClass timer; + + static char * _volid[] = { + "GDI95", + "NOD95", + "COVERT" + }; + static int num_volumes = 3; + + char buffer[128]; + + timer.Set(timeout); + + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + do{ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + if (GetVolumeInformation ((char const *)buffer, + &volume_name[0] , + (unsigned long)128 , + (unsigned long *)NULL , + (unsigned long *)&filename_length, + (unsigned long *)&misc_dword , + (char *)NULL , + (unsigned long)0)) { + + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\movies.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i=0 ; i. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + *=============================================================================================*/ +#pragma warning (disable : 4101) +bool Force_CD_Available(int cd) +{ + +#if (1) //ST - 1/2/2019 5:44PM + + static int _last = -1; + + if (_last != cd) { + + _last = cd; + + Theme.Stop(); + + if (MoviesMix) { + delete MoviesMix; + MoviesMix = 0; + } + if (GeneralMix) { + delete GeneralMix; + GeneralMix = 0; + } + if (ScoreMix) { + delete ScoreMix; + ScoreMix = 0; + } + + MoviesMix = new MixFileClass("MOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("SCORES.MIX"); +#if (0) + switch ( cd ) { + case -1: + default: + MoviesMix = new MixFileClass("MOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("SCORES.MIX"); + break; + + case 0: + MoviesMix = new MixFileClass("GDIMOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("GDISCORES.MIX"); + break; + + case 1: + MoviesMix = new MixFileClass("NODMOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("NODSCORES.MIX"); + break; + + case 2: + MoviesMix = new MixFileClass("COVERTMOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("COVERTSCORES.MIX"); + break; + } +#endif + ThemeClass::Scan(); + + } + + return true; + + + +#else + +#ifndef DEMO + static int _last = -1; + int open_failed; + int file; +#endif + static char _palette[768]; + static char _hold[256]; + static void *font; + static char * _volid[] = { + "GDI", + "NOD", + "COVERT" + }; + + int drive; + + char volume_name[100]; + unsigned filename_length; + unsigned misc_dword; + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + bool old_in_main_loop; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == -2) return(true); + + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + if (cd_index >= 0){ + if (cd == cd_index || cd == -1){ + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + + if (!new_cd_drive){ + /* + ** Check the last CD drive we used if its different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + /* + ** Make sure the last drive is valid and it isnt the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()){ + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + if (cd_index >= 0){ + if (cd == cd_index || cd == -1){ + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still cant find it prompt the user to insert it. + */ + if (!new_cd_drive){ + + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) { + /* + ** Search all present CD drives for the required disc. + */ + for (int i=0 ; i=0){ + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + if (cd == cd_index || cd == -1){ + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + if (cd == -1) { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _volid[cd]); + } else { + if (cd == 2) { + sprintf(buffer, Text_String(TXT_CD_DIALOG_3)); + } else { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _volid[cd]); + } + } + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + Mem_Copy(CurrentPalette, _palette, 768); + Mem_Copy(Get_Font_Palette_Ptr(), _hold, 256); + + + /* + ** Only set the palette if necessary. + */ +// if (CurrentPalette[3] == 0) { + Set_Palette(GamePalette); +// } + + /* + ** Pretend we are in the game, even if we arent + */ + old_in_main_loop = InMainLoop; + InMainLoop = true; + + Keyboard::Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (CCMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) { + Set_Logic_Page(oldpage); + Hide_Mouse(); + InMainLoop = old_in_main_loop; + return(false); + } + while (hidden--) Hide_Mouse(); + Set_Palette(_palette); + Set_Font(font); + Mem_Copy(_hold, Get_Font_Palette_Ptr(), 256); + Set_Logic_Page(oldpage); + InMainLoop = old_in_main_loop; + } + } + + +#ifndef DEMO + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + if (cd > -1 && _last != cd) { + _last = cd; + + Theme.Stop(); + + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + + MoviesMix = new MixFileClass("MOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("SCORES.MIX"); + ThemeClass::Scan(); + } +#endif + + if (theme_playing != THEME_NONE) { + Theme.Queue_Song(theme_playing); + } + + return(true); +#endif +} + + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + + return 0x7fffffff; + +#if (0) + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +#endif +} + + +/*********************************************************************************************** + * Validate_Error -- prints an error message when an object fails validation * + * * + * INPUT: * + * name name of object type that failed * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +void Validate_Error(char *name) +{ + GlyphX_Debug_Print("Validate_Error"); + GlyphX_Debug_Print(name); +#ifdef CHEAT_KEYS + Prog_End(NULL, true); + printf("%s object error!\n",name); + if (!RunningAsDLL) { + exit(0); + } +#else + name = name; +#endif +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass *obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /*------------------------------------------------------------------------ + Record a game + ------------------------------------------------------------------------*/ + if (RecordGame) { + + /*..................................................................... + For 'SuperRecord', we'll open & close the file with every entry. + .....................................................................*/ + if (SuperRecord) { + RecordFile.Open(READ|WRITE); + RecordFile.Seek(0,SEEK_END); + } + + /*..................................................................... + Save the map's location + .....................................................................*/ + RecordFile.Write(&Map.DesiredTacticalCoord, sizeof (Map.DesiredTacticalCoord)); + + /*..................................................................... + Save the current object list count + .....................................................................*/ + count = CurrentObject.Count(); + RecordFile.Write(&count, sizeof(count)); + + /*..................................................................... + Save a CRC of the selected-object list. + .....................................................................*/ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + RecordFile.Write (&sum, sizeof(sum)); + + /*..................................................................... + Save all selected objects. + .....................................................................*/ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + RecordFile.Write (&tgt, sizeof(tgt)); + } + + /*..................................................................... + If 'SuperRecord', close the file now. + .....................................................................*/ + if (SuperRecord) { + RecordFile.Close(); + } + } + + /*------------------------------------------------------------------------ + Play back a game ("attract" mode) + ------------------------------------------------------------------------*/ + if (PlaybackGame) { + + /*..................................................................... + Read & set the map's location. + .....................................................................*/ + if (RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /*.................................................................. + Compute a CRC of the current object-selection list. + ..................................................................*/ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /*.................................................................. + Load the CRC of the objects on disk; if it doesn't match, select + all objects as they're loaded. + ..................................................................*/ + RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) + Unselect_All(); + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + /*..................................................................... + The map isn't drawn in playback mode, so draw it here. + .....................................................................*/ + Map.Render(); + } +} +/*************************************************************************** + * HIRES_RETRIEVE -- retrieves a resolution dependant file * + * * + * INPUT: char * file name of the file to retrieve * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 01/25/1996 : Created. * + *=========================================================================*/ +void const * Hires_Retrieve(char *name) +{ + char filename[30]; + + if (SeenBuff.Get_Width() != 320) { + sprintf(filename,"H%s", name); + } else { + strcpy(filename, name); + } + return(MixFileClass::Retrieve(filename)); +} +int Get_Resolution_Factor(void) +{ + return ((SeenBuff.Get_Width() == 320) ? 0 : 1); +} + + + + + +/************************************************************************************************** +* Blit_Hid_Page_To_Seen_Buff -- Intercept for when the game refreshes the visible page +* +* In: Command line +* +* Out: +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +void Blit_Hid_Page_To_Seen_Buff(void) +{ + HidPage.Blit(SeenBuff); +} + + +/*********************************************************************************************** + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * * + * This routine will shake the game screen the number of shakes requested. * + * * + * INPUT: shakes -- The number of shakes to shake the screen. * + * house -- House to perform the shake for (or HOUSE_NONE if all players). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 BWG : Created. * + *=============================================================================================*/ +void Shake_The_Screen(int shakes, HousesType house) +{ + for (char h = HOUSE_FIRST; h < HOUSE_COUNT; ++h) { + if ((house != HOUSE_NONE) && (h != house)) { + continue; + } + HouseClass* hptr = HouseClass::As_Pointer((HousesType)h); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + hptr->ScreenShakeTime = hptr->ScreenShakeTime + shakes + shakes; + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/CONQUER.H b/TIBERIANDAWN/CONQUER.H new file mode 100644 index 000000000..df85e2bb3 --- /dev/null +++ b/TIBERIANDAWN/CONQUER.H @@ -0,0 +1,772 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define TXT_NONE 0 // +#define TXT_CREDIT_FORMAT 1 // %3d.%02d +#define TXT_BUTTON_UPGRADE 2 // Upgrade +#define TXT_UPGRADE 3 // Upgrade Structure +#define TXT_UPGRADE_BUTTON 4 // Upgrade +#define TXT_BUTTON_SELL 5 // Sell +#define TXT_SELL 6 // Sell Structure +#define TXT_DEMOLISH 7 // Demolish Structure +#define TXT_BUTTON_REPAIR 8 // Repair +#define TXT_REPAIR 9 // Repair Structure +#define TXT_REPAIR_BUTTON 10 // Repair +#define TXT_YOU 11 // You: +#define TXT_ENEMY 12 // Enemy: +#define TXT_BUILD_DEST 13 // Buildings Destroyed By +#define TXT_UNIT_DEST 14 // Units Destroyed By +#define TXT_TIB_HARV 15 // Tiberium Harvested By +#define TXT_SCORE_1 16 // Score: %d +#define TXT_RANK_OF 17 // You have attained the rank +#define TXT_YES 18 // Yes +#define TXT_NO 19 // No +#define TXT_READY 20 // Ready +#define TXT_HOLDING 21 // Holding +#define TXT_SCENARIO_WON 22 // Accomplished +#define TXT_SCENARIO_LOST 23 // Failed +#define TXT_CHOOSE_SIDE 24 // Choose Your Side +#define TXT_START_NEW_GAME 25 // Start New Game +#define TXT_INTRO 26 // Intro & Sneak Peek +#define TXT_CANCEL 27 // Cancel +#define TXT_ROCK 28 // Rock +#define TXT_CHOAM_RESUME 29 // Resume Game +#define TXT_CHOAM_BUILD_THIS 30 // Build This +#define TXT_THANK_YOU 31 // Thank you for playing +#define TXT_FAME 32 // Hall of Fame +#define TXT_GDI 33 // Global Defense Initiative +#define TXT_NOD 34 // Brotherhood of Nod +#define TXT_CIVILIAN 35 // Civilian +#define TXT_JP 36 // Containment Team +#define TXT_OK 37 // OK +#define TXT_TREE 38 // Tree +#define TXT_LEFT 39 //  +#define TXT_RIGHT 40 //  +#define TXT_UP 41 //  +#define TXT_DOWN 42 //  +#define TXT_CLEAR_MAP 43 // Clear the map +#define TXT_INHERIT_MAP 44 // Inherit previous map +#define TXT_CLEAR 45 // Clear +#define TXT_WATER 46 // Water +#define TXT_ROAD 47 // Road +#define TXT_TILE 48 // Tile Object +#define TXT_SLOPE 49 // Slope +#define TXT_BRUSH 50 // Brush +#define TXT_PATCH 51 // Patch +#define TXT_RIVER 52 // River +#define TXT_LOAD_MISSION 53 // Load Mission +#define TXT_SAVE_MISSION 54 // Save Mission +#define TXT_DELETE_MISSION 55 // Delete Mission +#define TXT_LOAD_BUTTON 56 // Load +#define TXT_SAVE_BUTTON 57 // Save +#define TXT_DELETE_BUTTON 58 // Delete +#define TXT_GAME_CONTROLS 59 // Game Controls +#define TXT_SOUND_CONTROLS 60 // Sound Controls +#define TXT_RESUME_MISSION 61 // Resume Mission +#define TXT_VISUAL_CONTROLS 62 // Visual Controls +#define TXT_QUIT_MISSION 63 // Abort Mission +#define TXT_EXIT_GAME 64 // Exit Game +#define TXT_OPTIONS 65 // Options +#define TXT_TIBERIUM 66 // Tiberium +#define TXT_TIBERIUM_ON 67 // Tiberium On +#define TXT_TIBERIUM_OFF 68 // Tiberium Off +#define TXT_SQUISH 69 // Squish mark +#define TXT_CRATER 70 // Crater +#define TXT_SCORCH 71 // Scorch Mark +#define TXT_BRIGHTNESS 72 // BRIGHTNESS: +#define TXT_MUSIC 73 // MUSIC VOLUME +#define TXT_VOLUME 74 // SOUND VOLUME +#define TXT_TINT 75 // TINT: +#define TXT_CONTRAST 76 // CONTRAST: +#define TXT_SPEED 77 // GAME SPEED: +#define TXT_SCROLLRATE 78 // SCROLL RATE: +#define TXT_COLOR 79 // COLOR: +#define TXT_RETURN_TO_GAME 80 // Return to game +#define TXT_ENEMY_SOLDIER 81 // Enemy Soldier +#define TXT_ENEMY_VEHICLE 82 // Enemy Vehicle +#define TXT_ENEMY_STRUCTURE 83 // Enemy Structure +#define TXT_FTANK 84 // Flame Tank +#define TXT_STANK 85 // Stealth Tank +#define TXT_LTANK 86 // Light Tank +#define TXT_MTANK 87 // Med. Tank +#define TXT_HTANK 88 // Mammoth Tank +#define TXT_DUNE_BUGGY 89 // Nod Buggy +#define TXT_SAM 90 // SAM Site +#define TXT_EYE 91 // Advanced Com. Center +#define TXT_MLRS 92 // Rocket Launcher +#define TXT_MHQ 93 // Mobile HQ +#define TXT_JEEP 94 // Hum-vee +#define TXT_TRANS 95 // Transport Helicopter +#define TXT_A10 96 // A10 +#define TXT_C17 97 // C17 +#define TXT_HARVESTER 98 // Harvester +#define TXT_ARTY 99 // Artillery +#define TXT_MSAM 100 // S.S.M. Launcher +#define TXT_E1 101 // Minigunner +#define TXT_E2 102 // Grenadier +#define TXT_E3 103 // Bazooka +#define TXT_E4 104 // Flamethrower +#define TXT_E5 105 // Chem-warrior +#define TXT_RAMBO 106 // Commando +#define TXT_HOVER 107 // Hovercraft +#define TXT_HELI 108 // Attack Helicopter +#define TXT_ORCA 109 // Orca +#define TXT_APC 110 // APC +#define TXT_GUARD_TOWER 111 // Guard Tower +#define TXT_COMMAND 112 // Communications Center +#define TXT_HELIPAD 113 // Helicopter Pad +#define TXT_AIRSTRIP 114 // Airstrip +#define TXT_STORAGE 115 // Tiberium Silo +#define TXT_CONST_YARD 116 // Construction Yard +#define TXT_REFINERY 117 // Tiberium Refinery +#define TXT_CIV1 118 // Church +#define TXT_CIV2 119 // Han's and Gretel's +#define TXT_CIV3 120 // Hewitt's Manor +#define TXT_CIV4 121 // Ricktor's House +#define TXT_CIV5 122 // Gretchin's House +#define TXT_CIV6 123 // The Barn +#define TXT_CIV7 124 // Damon's pub +#define TXT_CIV8 125 // Fran's House +#define TXT_CIV9 126 // Music Factory +#define TXT_CIV10 127 // Toymaker's +#define TXT_CIV11 128 // Ludwig's House +#define TXT_CIV12 129 // Haystacks +#define TXT_CIV13 130 // Haystack +#define TXT_CIV14 131 // Wheat Field +#define TXT_CIV15 132 // Fallow Field +#define TXT_CIV16 133 // Corn Field +#define TXT_CIV17 134 // Celery Field +#define TXT_CIV18 135 // Potato Field +#define TXT_CIV20 136 // Sala's House +#define TXT_CIV21 137 // Abdul's House +#define TXT_CIV22 138 // Pablo's Wicked Pub +#define TXT_CIV23 139 // Village Well +#define TXT_CIV24 140 // Camel Trader +#define TXT_CIV25 141 // Church +#define TXT_CIV26 142 // Ali's House +#define TXT_CIV27 143 // Trader Ted's +#define TXT_CIV28 144 // Menelik's House +#define TXT_CIV29 145 // Prestor John's House +#define TXT_CIV30 146 // Village Well +#define TXT_CIV31 147 // Witch Doctor's Hut +#define TXT_CIV32 148 // Rikitikitembo's Hut +#define TXT_CIV33 149 // Roarke's Hut +#define TXT_CIV34 150 // Mubasa's Hut +#define TXT_CIV35 151 // Aksum's Hut +#define TXT_CIV36 152 // Mambo's Hut +#define TXT_CIV37 153 // The Studio +#define TXT_CIVMISS 154 // Technology Center +#define TXT_TURRET 155 // Gun Turret +#define TXT_GUNBOAT 156 // Gun Boat +#define TXT_MCV 157 // Mobile Construction Yard +#define TXT_BIKE 158 // Recon Bike +#define TXT_POWER 159 // Power Plant +#define TXT_ADVANCED_POWER 160 // Advanced Power Plant +#define TXT_HOSPITAL 161 // Hospital +#define TXT_BARRACKS 162 // Barracks +#define TXT_CONCRETE 163 // Concrete +#define TXT_PUMP 164 // Oil Pump +#define TXT_TANKER 165 // Oil Tanker +#define TXT_SANDBAG_WALL 166 // Sandbag Wall +#define TXT_CYCLONE_WALL 167 // Chain Link Fence +#define TXT_BRICK_WALL 168 // Concrete Wall +#define TXT_BARBWIRE_WALL 169 // Barbwire Fence +#define TXT_WOOD_WALL 170 // Wood Fence +#define TXT_WEAPON_FACTORY 171 // Weapons Factory +#define TXT_AGUARD_TOWER 172 // Advanced Guard Tower +#define TXT_OBELISK 173 // Obelisk Guard Tower +#define TXT_BIO_LAB 174 // Bio-Research Laboratory +#define TXT_HAND 175 // Hand of Nod +#define TXT_TEMPLE 176 // Temple of Nod +#define TXT_FIX_IT 177 // Repair Bay +#define TXT_TAB_SIDEBAR 178 // Sidebar +#define TXT_TAB_BUTTON_CONTROLS 179 // Options +#define TXT_TAB_BUTTON_DATABASE 180 // Database +#define TXT_SHADOW 181 // Unrevealed Terrain +#define TXT_OPTIONS_MENU 182 // Options Menu +#define TXT_STOP 183 // STOP +#define TXT_PLAY 184 // PLAY +#define TXT_SHUFFLE 185 // SHUFFLE +#define TXT_REPEAT 186 // REPEAT +#define TXT_MUSIC_VOLUME 187 // Music volume: +#define TXT_SOUND_VOLUME 188 // Sound volume: +#define TXT_ON 189 // On +#define TXT_OFF 190 // Off +#define TXT_THEME_AOI 191 // Act On Instinct +#define TXT_THEME_TROUBLE 192 // Looks Like Trouble +#define TXT_THEME_IND 193 // Industrial +#define TXT_THEME_ROUT 194 // Reaching Out +#define TXT_THEME_OTP 195 // On The Prowl +#define TXT_THEME_PRP 196 // Prepare For Battle +#define TXT_THEME_JUSTDOIT 197 // Just Do It! +#define TXT_THEME_LINEFIRE 198 // In The Line Of Fire +#define TXT_THEME_MARCH 199 // March To Doom +#define TXT_THEME_STOPTHEM 200 // Deception +#define TXT_THEME_CCTHANG 201 // C&C Thang +#define TXT_THEME_BEFEARED 202 // Enemies To Be Feared +#define TXT_THEME_WARFARE 203 // Warfare +#define TXT_THEME_FWP 204 // Fight, Win, Prevail +#define TXT_THEME_DIE 205 // Die!! +#define TXT_THEME_NOMERCY 206 // No Mercy +#define TXT_THEME_TARGET 207 // Mechanical Man +#define TXT_THEME_IAM 208 // I Am +#define TXT_THEME_WIN1 209 // Great Shot! +#define TXT_MULTIPLAYER_GAME 210 // Multiplayer Game +#define TXT_NO_FILES 211 // No files available +#define TXT_DELETE_SINGLE_FILE 212 // Do you want to delete this +#define TXT_DELETE_MULTIPLE_FILES 213 // Do you want to delete %d +#define TXT_RESET_MENU 214 // Reset Values +#define TXT_CONFIRMATION 215 // Confirmation +#define TXT_CONFIRM_EXIT 216 // Do you want to abort the +#define TXT_MISSION_DESCRIPTION 217 // Mission Description +#define TXT_C1 218 // Joe +#define TXT_C2 219 // Bill +#define TXT_C3 220 // Shelly +#define TXT_C4 221 // Maria +#define TXT_C5 222 // Eydie +#define TXT_C6 223 // Dave +#define TXT_C7 224 // Phil +#define TXT_C8 225 // Dwight +#define TXT_C9 226 // Erik +#define TXT_MOEBIUS 227 // Dr. Moebius +#define TXT_BIB 228 // Road Bib +#define TXT_FASTER 229 // Faster +#define TXT_SLOWER 230 // Slower +#define TXT_ION_CANNON 231 // Ion Cannon +#define TXT_NUKE_STRIKE 232 // Nuclear Strike +#define TXT_AIR_STRIKE 233 // Air Strike +#define TXT_TREX 234 // Tyrannosaurus Rex +#define TXT_TRIC 235 // Triceratops +#define TXT_RAPT 236 // Velociraptor +#define TXT_STEG 237 // Stegasaurus +#define TXT_STEEL_CRATE 238 // Steel Crate +#define TXT_WOOD_CRATE 239 // Wood Crate +#define TXT_FLAG_SPOT 240 // Flag Location +#define TXT_G_D_I 241 // GDI +#define TXT_N_O_D 242 // NOD +#define TXT_UNABLE_READ_SCENARIO 243 // Unable to read scenario! +#define TXT_ERROR_LOADING_GAME 244 // Error loading game! +#define TXT_OBSOLETE_SAVEGAME 245 // Obsolete saved game. +#define TXT_MUSTENTER_DESCRIPTION 246 // You must enter a +#define TXT_ERROR_SAVING_GAME 247 // Error saving game! +#define TXT_DELETE_FILE_QUERY 248 // Delete this file? +#define TXT_EMPTY_SLOT 249 // [EMPTY SLOT] +#define TXT_SELECT_MPLAYER_GAME 250 // Select Multiplayer Game +#define TXT_MODEM_SERIAL 251 // Modem/Serial +#define TXT_NETWORK 252 // Network +#define TXT_INIT_NET_ERROR 253 // Unable to initialize +#define TXT_JOIN_NETWORK_GAME 254 // Join Network Game +#define TXT_NEW 255 // New +#define TXT_JOIN 256 // Join +#define TXT_SEND_MESSAGE 257 // Send Message +#define TXT_YOUR_NAME 258 // Your Name: +#define TXT_SIDE_COLON 259 // Side: +#define TXT_COLOR_COLON 260 // Color: +#define TXT_GAMES 261 // Games +#define TXT_PLAYERS 262 // Players +#define TXT_SCENARIO_COLON 263 // Scenario: +#define TXT_NOT_FOUND 264 // >> NOT FOUND << +#define TXT_START_CREDITS_COLON 265 // Starting Credits: +#define TXT_BASES_COLON 266 // Bases: +#define TXT_TIBERIUM_COLON 267 // Tiberium: +#define TXT_CRATES_COLON 268 // Crates: +#define TXT_AI_PLAYERS_COLON 269 // AI Players: +#define TXT_REQUEST_DENIED 270 // Request denied. +#define TXT_UNABLE_PLAY_WAAUGH 271 // Unable to play; scenario +#define TXT_NOTHING_TO_JOIN 272 // Nothing to join! +#define TXT_NAME_ERROR 273 // You must enter a name! +#define TXT_DUPENAMES_NOTALLOWED 274 // Duplicate names are not +#define TXT_YOURGAME_OUTDATED 275 // Your game version is +#define TXT_DESTGAME_OUTDATED 276 // Destination game version is +#define TXT_THATGUYS_GAME 277 // %s's Game +#define TXT_THATGUYS_GAME_BRACKET 278 // [%s's Game] +#define TXT_NETGAME_SETUP 279 // Network Game Setup +#define TXT_REJECT 280 // Reject +#define TXT_CANT_REJECT_SELF 281 // You can't reject yourself! +#define TXT_SELECT_PLAYER_REJECT 282 // You must select a player to +#define TXT_BASES_ON 283 // Bases On +#define TXT_BASES_OFF 284 // Bases Off +#define TXT_CRATES_ON 285 // Crates On +#define TXT_CRATES_OFF 286 // Crates Off +#define TXT_AI_PLAYERS_ON 287 // AI Players On +#define TXT_AI_PLAYERS_OFF 288 // AI Players Off +#define TXT_SCENARIOS 289 // Scenarios +#define TXT_START_CREDITS 290 // Starting Credits +#define TXT_ONLY_ONE 291 // Only one player? +#define TXT_OOPS 292 // Oops! +#define TXT_TO 293 // To %s: +#define TXT_TO_ALL 294 // To All: +#define TXT_MESSAGE 295 // Message: +#define TXT_CONNECTION_LOST 296 // Connection to %s lost! +#define TXT_LEFT_GAME 297 // %s has left the game. +#define TXT_PLAYER_DEFEATED 298 // %s has been defeated! +#define TXT_WAITING_CONNECT 299 // Waiting to Connect... +#define TXT_NULL_CONNERR_CHECK_CABLES 300 // Connection error! Check +#define TXT_MODEM_CONNERR_REDIALING 301 // Connection +#define TXT_MODEM_CONNERR_WAITING 302 // Connection error! Waiting +#define TXT_SELECT_SERIAL_GAME 303 // Select Serial Game +#define TXT_DIAL_MODEM 304 // Dial Modem +#define TXT_ANSWER_MODEM 305 // Answer Modem +#define TXT_NULL_MODEM 306 // Null Modem +#define TXT_SETTINGS 307 // Settings +#define TXT_PORT_COLON 308 // Port: +#define TXT_IRQ_COLON 309 // IRQ: +#define TXT_BAUD_COLON 310 // Baud: +#define TXT_INIT_STRING 311 // Init String: +#define TXT_CWAIT_STRING 312 // Call Waiting String: +#define TXT_TONE_BUTTON 313 // Tone Dialing +#define TXT_PULSE_BUTTON 314 // Pulse Dialing +#define TXT_HOST_SERIAL_GAME 315 // Host Serial Game +#define TXT_OPPONENT_COLON 316 // Opponent: +#define TXT_USER_SIGNED_OFF 317 // User signed off! +#define TXT_JOIN_SERIAL_GAME 318 // Join Serial Game +#define TXT_PHONE_LIST 319 // Phone List +#define TXT_ADD 320 // Add +#define TXT_EDIT 321 // Edit +#define TXT_DIAL 322 // Dial +#define TXT_DEFAULT 323 // Default +#define TXT_DEFAULT_SETTINGS 324 // Default Settings +#define TXT_CUSTOM_SETTINGS 325 // Custom Settings +#define TXT_PHONE_LISTING 326 // Phone Listing +#define TXT_NAME_COLON 327 // Name: +#define TXT_NUMBER_COLON 328 // Number: +#define TXT_UNABLE_FIND_MODEM 329 // Unable to find modem. Check +#define TXT_NO_CARRIER 330 // No carrier. +#define TXT_LINE_BUSY 331 // Line busy. +#define TXT_NUMBER_INVALID 332 // Number invalid. +#define TXT_SYSTEM_NOT_RESPONDING 333 // Other system not +#define TXT_OUT_OF_SYNC 334 // Games are out of sync! +#define TXT_PACKET_TOO_LATE 335 // Packet received too late! +#define TXT_PLAYER_LEFT_GAME 336 // Other player has left the +#define TXT_FROM 337 // From %s:%s +#define TXT_MAP_P01 338 // 2,728,000 +#define TXT_MAP_P02 339 // 38,385,000 +#define TXT_MAP_P03 340 // 10,373,000 +#define TXT_MAP_P04 341 // 51,994,000 +#define TXT_MAP_P05 342 // 80,387,000 +#define TXT_MAP_P06 343 // 10,400,000 +#define TXT_MAP_P07 344 // 5,300,000 +#define TXT_MAP_P08 345 // 7,867,000 +#define TXT_MAP_P09 346 // 10,333,000 +#define TXT_MAP_P10 347 // 1,974,000 +#define TXT_MAP_P11 348 // 23,169,000 +#define TXT_MAP_P12 349 // 10,064,000 +#define TXT_MAP_P13 350 // 3,285,000 +#define TXT_MAP_P14 351 // 8,868,000 +#define TXT_MAP_P15 352 // 10,337,000 +#define TXT_MAP_P16 353 // 4,365,000 +#define TXT_MAP_P17 354 // 1,607,000 +#define TXT_MAP_P18 355 // 4,485,000 +#define TXT_MAP_P19 356 // 56,386,000 +#define TXT_MAP_P20 357 // 28,305,000 +#define TXT_MAP_P21 358 // 5,238,000 +#define TXT_MAP_P22 359 // 2,059,000 +#define TXT_MAP_P23 360 // 13,497,000 +#define TXT_MAP_P24 361 // 4,997,000 +#define TXT_MAP_P25 362 // 88,500,000 +#define TXT_MAP_P26 363 // 1,106,000 +#define TXT_MAP_P27 364 // 12,658,000 +#define TXT_MAP_P28 365 // 3,029,000 +#define TXT_MAP_P29 366 // 39,084,000 +#define TXT_MAP_P30 367 // 23,154,000 +#define TXT_MAP_P31 368 // 8,902,000 +#define TXT_MAP_P32 369 // 27,791,000 +#define TXT_MAP_P33 370 // 1,574,000 +#define TXT_MAP_P34 371 // 15,469,000 +#define TXT_MAP_P35 372 // 1,300,000 +#define TXT_MAP_P36 373 // 41,688,000 +#define TXT_MAP_A00 374 // 24,900 SQ. MI. +#define TXT_MAP_A01 375 // 120,727 SQ. MI. +#define TXT_MAP_A02 376 // 80,134 SQ. MI. +#define TXT_MAP_A03 377 // 233,100 SQ. MI. +#define TXT_MAP_A04 378 // 137,838 SQ. MI. +#define TXT_MAP_A05 379 // 30,449 SQ. MI. +#define TXT_MAP_A06 380 // 18,932 SQ. MI. +#define TXT_MAP_A07 381 // 32,377 SQ. MI. +#define TXT_MAP_A08 382 // 35,919 SQ. MI. +#define TXT_MAP_A09 383 // 7,819 SQ. MI. +#define TXT_MAP_A10 384 // 91,699 SQ. MI. +#define TXT_MAP_A11 385 // 51,146 SQ. MI. +#define TXT_MAP_A12 386 // 11,100 SQ. MI. +#define TXT_MAP_A13 387 // 44,365 SQ. MI. +#define TXT_MAP_A14 388 // 39,449 SQ. MI. +#define TXT_MAP_A15 389 // 19,741 SQ. MI. +#define TXT_MAP_A16 390 // 17,413 SQ. MI. +#define TXT_MAP_C00 391 // RIGA +#define TXT_MAP_C01 392 // WARSAW +#define TXT_MAP_C02 393 // MINSK +#define TXT_MAP_C03 394 // KIEV +#define TXT_MAP_C04 395 // BERLIN +#define TXT_MAP_C05 396 // PRAGUE +#define TXT_MAP_C06 397 // BRATISLAVA +#define TXT_MAP_C07 398 // VIENNA +#define TXT_MAP_C08 399 // BUDAPEST +#define TXT_MAP_C09 400 // LJUBLJANA +#define TXT_MAP_C10 401 // BUCHAREST +#define TXT_MAP_C11 402 // ATHENS +#define TXT_MAP_C12 403 // TIRANA +#define TXT_MAP_C13 404 // SOFIA +#define TXT_MAP_C14 405 // BELGRADE +#define TXT_MAP_C15 406 // SARAJEVO +#define TXT_MAP_C16 407 // TALLINN +#define TXT_MAP_C17 408 // TRIPOLI +#define TXT_MAP_C18 409 // CAIRO +#define TXT_MAP_C19 410 // KHARTOUM +#define TXT_MAP_C20 411 // N'DJAMENA +#define TXT_MAP_C21 412 // NOUAKCHOTT +#define TXT_MAP_C22 413 // YAMOUSSOUKRO +#define TXT_MAP_C23 414 // PORTO-NOVO +#define TXT_MAP_C24 415 // ABUJA +#define TXT_MAP_C25 416 // LIBREVILLE +#define TXT_MAP_C26 417 // YAOUNDE +#define TXT_MAP_C27 418 // BANGUI +#define TXT_MAP_C28 419 // KINSHASA +#define TXT_MAP_C29 420 // CAIRO +#define TXT_MAP_C30 421 // LUANDA +#define TXT_MAP_C31 422 // DAR-ES-SALAAM +#define TXT_MAP_C32 423 // WINDHOEK +#define TXT_MAP_C33 424 // MAPUTO +#define TXT_MAP_C34 425 // GABARONE +#define TXT_MAP_C35 426 // CAPE TOWN +#define TXT_MAP_GDP00 427 // NEGLIGIBLE +#define TXT_MAP_GDP01 428 // $162.7 BLN +#define TXT_MAP_GDP02 429 // $47.6 BLN +#define TXT_MAP_GDP03 430 // $1,131 BLN +#define TXT_MAP_GDP04 431 // $120 BLN +#define TXT_MAP_GDP05 432 // $164 BLN +#define TXT_MAP_GDP06 433 // $60.1 BLN +#define TXT_MAP_GDP07 434 // $21 BLN +#define TXT_MAP_GDP08 435 // $71.9 BLN +#define TXT_MAP_GDP09 436 // $77 BLN +#define TXT_MAP_GDP10 437 // $4.0 BLN +#define TXT_MAP_GDP11 438 // $47.3 BLN +#define TXT_MAP_GDP12 439 // $120.1 BLN +#define TXT_MAP_GDP13 440 // $14.0 BLN +#define TXT_MAP_GDP14 441 // $28.9 BLN +#define TXT_MAP_GDP15 442 // $39.2 BLN +#define TXT_MAP_GDP16 443 // $12.1 BLN +#define TXT_MAP_GDP17 444 // $1.0 BLN +#define TXT_MAP_GDP18 445 // $10.0 BLN +#define TXT_MAP_GDP19 446 // $1.7 BLN +#define TXT_MAP_GDP20 447 // $28.0 BLN +#define TXT_MAP_GDP21 448 // $5.3 BLN +#define TXT_MAP_GDP22 449 // $11.6 BLN +#define TXT_MAP_GDP23 450 // $1.3 BLN +#define TXT_MAP_GDP24 451 // $6.6 BLN +#define TXT_MAP_GDP25 452 // $8.3 BLN +#define TXT_MAP_GDP26 453 // $6.9 BLN +#define TXT_MAP_GDP27 454 // $2.0 BLN +#define TXT_MAP_GDP28 455 // $3.1 BLN +#define TXT_MAP_GDP29 456 // $104.0 BLN +#define TXT_MAP_PC00 457 // JELGAVA +#define TXT_MAP_PC01 458 // GDANSK +#define TXT_MAP_PC02 459 // BYELISTOK +#define TXT_MAP_PC03 460 // BOBYRUSK +#define TXT_MAP_PC04 461 // IVANO-FRANKOVSK +#define TXT_MAP_PC05 462 // HANOVER +#define TXT_MAP_PC06 463 // DRESDEN +#define TXT_MAP_PC07 464 // OSTRAVA +#define TXT_MAP_PC08 465 // BRATISLAVA +#define TXT_MAP_PC09 466 // SALZBURG +#define TXT_MAP_PC10 467 // BUDAPEST +#define TXT_MAP_PC11 468 // TRIESTE +#define TXT_MAP_PC12 469 // ARAD +#define TXT_MAP_PC13 470 // CORINTH +#define TXT_MAP_PC14 471 // SHKODER +#define TXT_MAP_PC15 472 // SOFIA +#define TXT_MAP_PC16 473 // NIS +#define TXT_MAP_PC17 474 // BELGRADE +#define TXT_MAP_PC18 475 // ? +#define TXT_MAP_PC19 476 // PARNU +#define TXT_MAP_PC20 477 // TMASSAH +#define TXT_MAP_PC21 478 // AL-ALAMYN +#define TXT_MAP_PC22 479 // AL-KHARIJAH +#define TXT_MAP_PC23 480 // AL-UBAYYID +#define TXT_MAP_PC24 481 // KAFIA-KINGI +#define TXT_MAP_PC25 482 // OUM HADJER +#define TXT_MAP_PC26 483 // MAO +#define TXT_MAP_PC27 484 // TIDJIKDJA +#define TXT_MAP_PC28 485 // ABIDJAN +#define TXT_MAP_PC29 486 // PORTO-NOVO +#define TXT_MAP_PC30 487 // ABUJA +#define TXT_MAP_PC31 488 // KOULA-MOUTOU +#define TXT_MAP_PC32 489 // BERTOUA +#define TXT_MAP_PC33 490 // BANGASSOU +#define TXT_MAP_PC34 491 // LODJA +#define TXT_MAP_PC35 492 // KINSHASA +#define TXT_MAP_PC36 493 // LUXOR +#define TXT_MAP_PC37 494 // CAIUNDO +#define TXT_MAP_PC38 495 // MZUZU +#define TXT_MAP_PC39 496 // KEETMANSHOOP +#define TXT_MAP_PC40 497 // XAI-XAI +#define TXT_MAP_PC41 498 // GHANZI +#define TXT_MAP_PC42 499 // CAPE TOWN +#define TXT_MAP_GDI 500 // GDI PROGRESSION +#define TXT_MAP_NOD 501 // NOD PROGRESSION +#define TXT_MAP_LOCATE 502 // LOCATING COORDINATES +#define TXT_MAP_NEXT_MISSION 503 // OF NEXT MISSION +#define TXT_MAP_SELECT 504 // SELECT TERRITORY +#define TXT_MAP_TO_ATTACK 505 // TO ATTACK +#define TXT_MAP_GDISTAT0 506 // POPULATION: +#define TXT_MAP_GDISTAT1 507 // GEOGRAPHIC AREA: +#define TXT_MAP_GDISTAT2 508 // CAPITAL: +#define TXT_MAP_GDISTAT3 509 // GOVERNMENT: +#define TXT_MAP_GDISTAT4 510 // GROSS DOMESTIC PRODUCT: +#define TXT_MAP_GDISTAT5 511 // POINT OF CONFLICT: +#define TXT_MAP_GDISTAT6 512 // MILITARY POWER: +#define TXT_MAP_NODSTAT0 513 // EXPENDABILITY: +#define TXT_MAP_NODSTAT1 514 // GOVT CORRUPTABILITY: +#define TXT_MAP_NODSTAT2 515 // NET WORTH: +#define TXT_MAP_NODSTAT3 516 // MILITARY STRENGTH: +#define TXT_MAP_NODSTAT4 517 // MILITARY RESISTANCE: +#define TXT_MAP_COUNTRYNAME0 518 // LATVIA +#define TXT_MAP_COUNTRYNAME1 519 // POLAND +#define TXT_MAP_COUNTRYNAME2 520 // BELARUS +#define TXT_MAP_COUNTRYNAME3 521 // UKRAINE +#define TXT_MAP_COUNTRYNAME4 522 // GERMANY +#define TXT_MAP_COUNTRYNAME5 523 // CZECH REPUBLIC +#define TXT_MAP_COUNTRYNAME6 524 // SLOVAKIA +#define TXT_MAP_COUNTRYNAME7 525 // AUSTRIA +#define TXT_MAP_COUNTRYNAME8 526 // HUNGARY +#define TXT_MAP_COUNTRYNAME9 527 // SLOVENIA +#define TXT_MAP_COUNTRYNAME10 528 // ROMANIA +#define TXT_MAP_COUNTRYNAME11 529 // GREECE +#define TXT_MAP_COUNTRYNAME12 530 // ALBANIA +#define TXT_MAP_COUNTRYNAME13 531 // BULGARIA +#define TXT_MAP_COUNTRYNAME14 532 // YUGOSLAVIA +#define TXT_MAP_COUNTRYNAME15 533 // BOSNIA/HERZOGOVINA +#define TXT_MAP_COUNTRYNAME16 534 // LIBYA +#define TXT_MAP_COUNTRYNAME17 535 // EGYPT +#define TXT_MAP_COUNTRYNAME18 536 // SUDAN +#define TXT_MAP_COUNTRYNAME19 537 // CHAD +#define TXT_MAP_COUNTRYNAME20 538 // MAURITANIA +#define TXT_MAP_COUNTRYNAME21 539 // IVORY COAST +#define TXT_MAP_COUNTRYNAME22 540 // BENIN +#define TXT_MAP_COUNTRYNAME23 541 // NIGERIA +#define TXT_MAP_COUNTRYNAME24 542 // GABON +#define TXT_MAP_COUNTRYNAME25 543 // CAMEROON +#define TXT_MAP_COUNTRYNAME26 544 // CENTRAL AFRICAN REPUBLIC +#define TXT_MAP_COUNTRYNAME27 545 // ZAIRE +#define TXT_MAP_COUNTRYNAME28 546 // ANGOLA +#define TXT_MAP_COUNTRYNAME29 547 // TANZANIA +#define TXT_MAP_COUNTRYNAME30 548 // NAMIBIA +#define TXT_MAP_COUNTRYNAME31 549 // MOZAMBIQUE +#define TXT_MAP_COUNTRYNAME32 550 // BOTSWANA +#define TXT_MAP_COUNTRYNAME33 551 // SOUTH AFRICA +#define TXT_MAP_COUNTRYNAME34 552 // ESTONIA +#define TXT_MAP_GOVT0 553 // REPUBLIC +#define TXT_MAP_GOVT1 554 // DEMOCRATIC STATE +#define TXT_MAP_GOVT2 555 // FEDERAL REPUBLIC +#define TXT_MAP_GOVT3 556 // CONST. REPUBLIC +#define TXT_MAP_GOVT4 557 // PARL. DEMOCRACY +#define TXT_MAP_GOVT5 558 // PRES. PARL. REPUBLIC +#define TXT_MAP_GOVT6 559 // DEMOCRACY +#define TXT_MAP_GOVT7 560 // IN TRANSITION +#define TXT_MAP_GOVT8 561 // ISLAMIC SOCIALIST +#define TXT_MAP_GOVT9 562 // MILITARY +#define TXT_MAP_GOVT10 563 // ISLAMIC REPUBLIC +#define TXT_MAP_GOVT11 564 // PARL. REPUBLIC +#define TXT_MAP_ARMY0 565 // LOCAL MILITIA +#define TXT_MAP_ARMY1 566 // STATE MILITIA +#define TXT_MAP_ARMY2 567 // NATIONAL GUARD +#define TXT_MAP_ARMY3 568 // FREE STANDING ARMY +#define TXT_MAP_ARMY4 569 // ? +#define TXT_MAP_ARMY5 570 // NATIONAL POWER +#define TXT_MAP_MILITARY0 571 // RESPECTABLE +#define TXT_MAP_MILITARY1 572 // FORMIDABLE +#define TXT_MAP_MILITARY2 573 // LAUGHABLE +#define TXT_MAP_MILITARY3 574 // REASONABLE +#define TXT_MAP_MILITARY4 575 // INSIGNIFICANT +#define TXT_MAP_CLICK2 576 // CLICK TO CONTINUE +#define TXT_MAP_LMH0 577 // LOW +#define TXT_MAP_LMH1 578 // MEDIUM +#define TXT_MAP_LMH2 579 // HIGH +#define TXT_SCORE_TIME 580 // TIME: +#define TXT_SCORE_LEAD 581 // LEADERSHIP: +#define TXT_SCORE_EFFI 582 // EFFICIENCY: +#define TXT_SCORE_TOTA 583 // TOTAL SCORE: +#define TXT_SCORE_CASU 584 // CASUALTIES: +#define TXT_SCORE_NEUT 585 // NEUTRAL: +#define TXT_SCORE_GDI 586 // GDI: +#define TXT_SCORE_BUIL 587 // BUILDINGS LOST +#define TXT_SCORE_BUIL1 588 // BUILDINGS +#define TXT_SCORE_BUIL2 589 // LOST: +#define TXT_SCORE_TOP 590 // TOP SCORES +#define TXT_SCORE_ENDCRED 591 // ENDING CREDITS: +#define TXT_SCORE_TIMEFORMAT1 592 // %dh %dm +#define TXT_SCORE_TIMEFORMAT2 593 // %dm +#define TXT_SCORE_NOD 594 // NOD: +#define TXT_DIALING 595 // Dialing... +#define TXT_DIALING_CANCELED 596 // Dialing Canceled +#define TXT_WAITING_FOR_CALL 597 // Waiting for Call... +#define TXT_ANSWERING_CANCELED 598 // Answering Canceled +#define TXT_E7 599 // Engineer +#define TXT_SPECIAL_OPTIONS 600 // Special Options +#define TXT_VISIBLE_TARGET 601 // Targeting flash visible to +#define TXT_TREE_TARGET 602 // Allow targeting of trees. +#define TXT_MCV_DEPLOY 603 // Allow undeploy of +#define TXT_SMART_DEFENCE 604 // Employ smarter self defense +#define TXT_SLOW_BUILD 605 // Moderate production speed. +#define TXT_THREE_POINT 606 // Use three point turn logic. +#define TXT_TIBERIUM_GROWTH 607 // Tiberium will grow. +#define TXT_TIBERIUM_SPREAD 608 // Tiberium will spread. +#define TXT_ROAD_PIECES 609 // Disable building "bib" +#define TXT_SCATTER 610 // Allow running from +#define TXT_MODEM_OR_LOOPBACK 611 // Not a Null Modem Cable +#define TXT_MAP 612 // Map +#define TXT_FROM_COMPUTER 613 // From Computer: +#define TXT_COMP_MSG1 614 // Prepare to die! +#define TXT_COMP_MSG2 615 // How about a bullet +#define TXT_COMP_MSG3 616 // Incoming! +#define TXT_COMP_MSG4 617 // I see you! +#define TXT_COMP_MSG5 618 // Hey, I'm over here! +#define TXT_COMP_MSG6 619 // Come get some! +#define TXT_COMP_MSG7 620 // I got you! +#define TXT_COMP_MSG8 621 // You humans are never a +#define TXT_COMP_MSG9 622 // Abort, Retry, Ignore? (Ha +#define TXT_COMP_MSG10 623 // Format another? (Just +#define TXT_COMP_MSG11 624 // Beat me and I'll reboot! +#define TXT_COMP_MSG12 625 // You're artificial +#define TXT_COMP_MSG13 626 // My AI is better than your +#define TXT_THEME_AIRSTRIKE 627 // Air Strike +#define TXT_THEME_HEAVYG 628 // Demolition +#define TXT_THEME_J1 629 // Untamed Land +#define TXT_THEME_JDI_V2 630 // Take 'em Out +#define TXT_THEME_RADIO 631 // Radio +#define TXT_THEME_RAIN 632 // Rain In The Night +#define TXT_THEME_IND2 633 // Canyon Chase +#define TXT_THEME_HEART 634 // Heartbreak +#define TXT_BLOSSOM_TREE 635 // Blossom Tree +#define TXT_RESTATE_MISSION 636 // Restate +#define TXT_COMPUTER 637 // Computer +#define TXT_COUNT 638 // Unit Count: +#define TXT_LEVEL 639 // Tech Level: +#define TXT_OPPONENT 640 // Opponent +#define TXT_KILLS_COLON 641 // Kills: +#define TXT_VIDEO 642 // Video +#define TXT_C10 643 // Nikoomba +#define TXT_CAPTURE_THE_FLAG 644 // Capture The Flag +#define TXT_THEME_VALK 645 // Ride of the Valkyries +#define TXT_OBJECTIVE 646 // Mission Objective +#define TXT_MISSION 647 // Mission +#define TXT_NO_SAVES 648 // No saved games available. +#define TXT_CIVILIAN_BUILDING 649 // Civilian Building +#define TXT_TECHNICIAN 650 // Technician +#define TXT_VISCEROID 651 // Visceroid +#define TXT_NO_SAVELOAD 652 // Save game options are not +#define TXT_DEFENDER_ADVANTAGE 653 // Defender has the advantage. +#define TXT_SHOW_NAMES 654 // Show true object names. +#define TXT_DELPHI 655 // Agent Delphi +#define TXT_TO_REPLAY 656 // Would you like to replay +#define TXT_RECONN_TO 657 // Reconnecting to %s. +#define TXT_PLEASE_WAIT 658 // Please wait %02d seconds. +#define TXT_SURRENDER 659 // Do you wish to surrender? +#define TXT_GDI_NAME 660 // GLOBAL DEFENSE INITIATIVE +#define TXT_NOD_NAME 661 // BROTHERHOOD OF NOD +#define TXT_SEL_TRANS 662 // SELECT TRANSMISSION +#define TXT_GAMENAME_MUSTBE_UNIQUE 663 // Your game name must be +#define TXT_GAME_IS_CLOSED 664 // Game is closed. +#define TXT_NAME_MUSTBE_UNIQUE 665 // Your name must be unique. +#define TXT_RECONNECTING_TO 666 // Reconnecting to %s +#define TXT_WAITING_FOR_CONNECTIONS 667 // Waiting for connections... +#define TXT_TIME_ALLOWED 668 // Time allowed: %02d seconds +#define TXT_PRESS_ESC 669 // Press ESC to cancel. +#define TXT_JUST_YOU_AND_ME 670 // From Computer: It's just +#define TXT_CAPTURE_THE_FLAG_COLON 671 // Capture the Flag: +#define TXT_CHAN 672 // Dr. Chan +#define TXT_HAS_ALLIED 673 // %s has allied with %s +#define TXT_AT_WAR 674 // %s declares war on %s +#define TXT_SEL_TARGET 675 // Select a target +#define TXT_SEPARATE_HELIPAD 676 // Allow separate helipad +#define TXT_RESIGN 677 // Resign Game +#define TXT_TIBERIUM_FAST 678 // Tiberium grows quickly. +#define TXT_ANSWERING 679 // Answering... +#define TXT_INITIALIZING_MODEM 680 // Initializing Modem... +#define TXT_SCENARIOS_DO_NOT_MATCH 681 // Scenarios don't match. +#define TXT_POWER_OUTPUT 682 // Power Output +#define TXT_POWER_OUTPUT_LOW 683 // Power Output (low) +#define TXT_CONTINUE 684 // Continue +#define TXT_QUEUE_FULL 685 // Data Queue Overflow +#define TXT_SPECIAL_WARNING 686 // %s changed game options! +#define TXT_CD_DIALOG_1 687 // Please insert a Command & +#define TXT_CD_DIALOG_2 688 // Please insert CD %d (%s) +#define TXT_CD_ERROR1 689 // Command & Conquer is unable +#define TXT_NO_SOUND_CARD 690 // No Sound Card Detected +#define TXT_UNKNOWN 691 // UNKNOWN +#define TXT_OLD_GAME 692 // (old) +#define TXT_NO_SPACE 693 // Insufficient Disk Space to +#define TXT_MUST_HAVE_SPACE 694 // You must have %d megabytes +#define TXT_RUN_SETUP 695 // Run SETUP program first. +#define TXT_WAITING_FOR_OPPONENT 696 // Waiting for Opponent +#define TXT_SELECT_SETTINGS 697 // Please select 'Settings' to +#define TXT_PRISON 698 // Prison +#define TXT_GAME_WAS_SAVED 699 // Game Saved +#define TXT_SPACE_CANT_SAVE 700 // Insufficient disk space to +#define TXT_INVALID_PORT_ADDRESS 701 // Invalid Port/Address. COM +#define TXT_INVALID_SETTINGS 702 // Invalid Port and/or IRQ +#define TXT_IRQ_ALREADY_IN_USE 703 // IRQ already in use +#define TXT_ABORT 704 // Abort +#define TXT_RESTART 705 // Restart +#define TXT_RESTARTING 706 // Mission is +#define TXT_LOADING 707 // Mission is loading. Please +#define TXT_ERROR_IN_INITSTRING 708 // Error in the InitString +#define TXT_ORDER_INFO 709 // Order Info +#define TXT_SCENES 710 // Scenes +#define TXT_NEW_MISSIONS 711 // New Missions +#define TXT_THEME_CHRG 712 // Depth Charge +#define TXT_THEME_DRON 713 // Drone +#define TXT_THEME_FIST 714 // Iron Fist +#define TXT_THEME_CREP 715 // Creeping Upon +#define TXT_THEME_80MX 716 // C&C 80's Mix +#define TXT_THEME_DRIL 717 // Drill +#define TXT_CD_DIALOG_3 718 // Please insert the Covert +#define TXT_THEME_RECON 719 // Recon +#define TXT_THEME_VOICE 720 // Voice Rhythm +#define TXT_ERROR_NO_INIT 721 // Error - modem did not +#define TXT_NO_FLOW_CONTROL_RESPONSE 722 // Error - Modem failed to +#define TXT_NO_COMPRESSION_RESPONSE 723 // Error - Modem failed to +#define TXT_NO_ERROR_CORRECTION_RESPONSE 724 // Error - Modem failed to +#define TXT_ERROR_NO_DISABLE 725 // Error - unable to disable +#define TXT_ERROR_TOO_MANY 726 // Error - Too many errors +#define TXT_IGNORE 727 // Ignore +#define TXT_CONNECTING 728 // Connecting... Please Wait. +#define TXT_EXPLAIN_REGISTRATION 729 // To play Command & Conquer +#define TXT_REGISTER 730 // Register +#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 731 // Wchat not installed. Please +#define TXT_INTERNET 732 // Internet Game +#define TXT_UNABLE_TO_SET_VIDEO_MODE 733 // Error - Unable to set the +#define TXT_UNABLE_TO_ALLOCATE_PRIMARY_VIDEO_BUFFER 734 // Error - Unable to allocate +#define TXT_NO_DIAL_TONE 735 // No dial tone. Ensure your +#define TXT_MODEM_INITIALISATION 736 // Modem Initialization +#define TXT_DATA_COMPRESSION 737 // Data Compression +#define TXT_ERROR_CORRECTION 738 // Error Correction +#define TXT_HARDWARE_FLOW_CONTROL 739 // Hardware Flow Control +#define TXT_ADVANCED 740 // Advanced +#define TXT_JUST_INTRO 741 // Intro +#define TXT_READING_IMAGE_DATA 742 // READING IMAGE DATA +#define TXT_ANALYZING 743 // ANALYZING +#define TXT_ENHANCING_IMAGE_DATA 744 // ENHANCING IMAGE DATA +#define TXT_ISOLATING_OPERATIONAL_THEATER 745 // ISOLATING OPERATIONAL +#define TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES 746 // ESTABLISHING TRADITIONAL +#define TXT_FOR_VISUAL_REFERENCE 747 // FOR VISUAL REFERENCE +#define TXT_ENHANCING_IMAGE 748 // ENHANCING IMAGE +#define TXT_BONUS_MISSIONS 749 // Bonus Missions +#define TXT_BONUS_MISSION_1 750 // Bonus Mission 1 +#define TXT_BONUS_MISSION_2 751 // Bonus Mission 2 +#define TXT_BONUS_MISSION_3 752 // Bonus Mission 3 +#define TXT_BONUS_MISSION_4 753 // Bonus Mission 4 +#define TXT_BONUS_MISSION_5 754 // Bonus Mission 5 +#define TXT_LOW_POWER 755 +#define TXT_INSUFFICIENT_FUNDS 756 \ No newline at end of file diff --git a/TIBERIANDAWN/CONST.CPP b/TIBERIANDAWN/CONST.CPP new file mode 100644 index 000000000..f9d481e17 --- /dev/null +++ b/TIBERIANDAWN/CONST.CPP @@ -0,0 +1,425 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\const.cpv 2.17 16 Oct 1995 16:52:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 20, 1993 * + * * + * Last Update : September 20, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +char const * SourceName[SOURCE_COUNT] = +{ + "North", + "East", + "South", + "West", + "Shipping", + "Beach", + "Air", + "Visible", + "EnemyBase", + "HomeBase", + "Ocean", +}; + + +/*************************************************************************** +** Relative coordinate offsets from the center of a cell for each +** of the legal positions that an object in a cell may stop at. Only infantry +** are allowed to stop at other than the center of the cell. +*/ +COORDINATE const StoppingCoordAbs[5] = { + 0x00800080L, // center + 0x00400040L, // upper left + 0x004000C0L, // upper right + 0x00C00040L, // lower left + 0x00C000C0L // lower right +}; + + +/*************************************************************************** +** These are the various weapons and their characteristics. +** +** bullet type dmg, rof, range, sound +*/ +WeaponTypeClass const Weapons[WEAPON_COUNT] = { + {BULLET_SNIPER, 125, 40, 0x0580, VOC_SNIPER, ANIM_NONE}, // WEAPON_RIFLE + {BULLET_SPREADFIRE, 25, 50, 0x0400, VOC_MINI, ANIM_GUN_N}, // WEAPON_CHAIN_GUN + {BULLET_BULLET, 1, 7, 0x01C0, VOC_RIFLE, ANIM_NONE}, // WEAPON_PISTOL + {BULLET_BULLET, 15, 20, 0x0200, VOC_MGUN2, ANIM_NONE}, // WEAPON_M16 + {BULLET_TOW, 30, 60, 0x0400, VOC_BAZOOKA,ANIM_NONE}, // WEAPON_DRAGON + {BULLET_FLAME, 35, 50, 0x0200, VOC_FLAMER1,ANIM_FLAME_N}, // WEAPON_FLAMETHROWER + {BULLET_FLAME, 50, 50, 0x0200, VOC_FLAMER1,ANIM_FLAME_N}, // WEAPON_FLAME_TONGUE + {BULLET_CHEMSPRAY, 80, 70, 0x0200, VOC_FLAMER1,ANIM_CHEM_N}, // WEAPON_CHEMSPRAY + {BULLET_GRENADE, 50, 60, 0x0340, VOC_TOSS, ANIM_NONE}, // WEAPON_GRENADE + {BULLET_APDS, 25, 60, 0x0400, VOC_TANK2, ANIM_MUZZLE_FLASH}, // WEAPON_75MM + {BULLET_APDS, 30, 50, 0x04C0, VOC_TANK3, ANIM_MUZZLE_FLASH}, // WEAPON_105MM + {BULLET_APDS, 40, 80, 0x04C0, VOC_TANK4, ANIM_MUZZLE_FLASH}, // WEAPON_120MM + {BULLET_APDS, 40, 60, 0x0600, VOC_TANK4, ANIM_MUZZLE_FLASH}, // WEAPON_TURRET_GUN + {BULLET_SSM, 75, 80, 0x0500, VOC_ROCKET1,ANIM_NONE}, // WEAPON_MAMMOTH_TUSK + {BULLET_SSM2, 75, 80, 0x0600, VOC_ROCKET1,ANIM_NONE}, // WEAPON_MLRS + {BULLET_HE, 150, 65, 0x0600, VOC_TANK1, ANIM_MUZZLE_FLASH}, // WEAPON_155MM + {BULLET_BULLET, 15, 30, 0x0400, VOC_MGUN11, ANIM_GUN_N}, // WEAPON_M60MG + {BULLET_SSM, 60, 35, 0x0780, VOC_ROCKET2,ANIM_NONE}, // WEAPON_TOMAHAWK + {BULLET_SSM, 60, 40, 0x0680, VOC_ROCKET2,ANIM_NONE}, // WEAPON_TOW_TWO + {BULLET_NAPALM, 100, 20, 0x0480, VOC_NONE, ANIM_NONE}, // WEAPON_NAPALM + {BULLET_LASER, 200, 90, 0x0780, VOC_LASER, ANIM_NONE}, // WEAPON_OBELISK_LASER + {BULLET_SAM, 50, 50, 0x0780, VOC_ROCKET2,ANIM_NONE}, // WEAPON_NIKE + {BULLET_HONEST_JOHN, 100, 200, 0x0A00, VOC_ROCKET1,ANIM_NONE}, // WEAPON_HONEST_JOHN + {BULLET_HEADBUTT, 100, 30, 0x0180, VOC_DINOATK1,ANIM_NONE}, // WEAPON_STEG + {BULLET_TREXBITE, 155, 30, 0x0180, VOC_DINOATK1,ANIM_NONE}, // WEAPON_TREX +#ifdef PETROGLYPH_EXAMPLE_MOD + {BULLET_NUKE_LOB, 150, 130, 0x0B00, VOC_NUKE_LOB, ANIM_MUZZLE_FLASH}, // WEAPON_NUKE_LOB +#endif //PETROGLYPH_EXAMPLE_MOD +}; + + +/*************************************************************************** +** These are the various warheads. +** +** spread factor, destroys walls, destroys wood, destroys Tiberium, {armor defense table} +** -vs- {none, wood, aluminum, steel, concrete} +*/ +WarheadTypeClass const Warheads[WARHEAD_COUNT] = { + { 2,false,false,false,{0x100, 0x80, 0x90, 0x40, 0x40}}, // WARHEAD_SA Small arms -- good against infantry. + { 6,true,true,true,{0xE0, 0xC0, 0x90, 0x40, 0x100}}, // WARHEAD_HE High explosive -- good against buildings & infantry. + { 6,true,true,false,{0x40, 0xC0, 0xC0, 0x100, 0x80}}, // WARHEAD_AP Armor piercing -- good against armor. + { 8,false,true,true,{0xE0, 0x100, 0xB0, 0x40, 0x80}}, // WARHEAD_FIRE Incendiary -- Good against flammables. + { 4,false,false,false,{0x100, 0x100, 0x100, 0x100, 0x100}},// WARHEAD_LASER Light Amplification of Stimulated Emission by Radiation. + { 7,true,true,true,{0x100, 0x100, 0xC0, 0xC0, 0xC0}}, // WARHEAD_PB Particle beam (neutron beam). + { 4,false,false,false,{0x100, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FIST Punching in hand-to-hand combat. + { 4,false,false,false,{0x100, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FOOT Kicking in hand-to-hand combat. + { 4,false,false,false,{0x100, 0x08, 0x08, 0x08, 0x08}}, // WARHEAD_HOLLOW_POINT Sniper bullet type. + {255,false,false,false,{0x100, 0x01, 0x01, 0x01, 0x01}}, // WARHEAD_SPORE + { 1, true,true,false,{0x100, 0xC0, 0x80, 0x20, 0x08}}, // WARHEAD_HEADBUTT + { 1, true,true,false,{0x100, 0xC0, 0x80, 0x20, 0x08}}, // WARHEAD_FEEDME +}; + + +/*************************************************************************** +** Converts pixel values (cell relative) into the appropriate lepton (sub cell) +** value. This is used to convert pixel (screen) coordinates into the underlying +** coordinate system. +*/ +unsigned char const Pixel2Lepton[24] = { + 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B, + 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0, + 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5 +}; + + +/*************************************************************************** +** This array is used to index a facing in order to retrieve a cell +** offset that, when added to another cell, will achieve the adjacent cell +** in the indexed direction. +*/ +CELL const AdjacentCell[FACING_COUNT] = { + -(MAP_CELL_W), // North + -(MAP_CELL_W-1), // North East + 1, // East + MAP_CELL_W+1, // South East + MAP_CELL_W, // South + MAP_CELL_W-1, // South West + -1, // West + -(MAP_CELL_W+1) // North West +}; + +COORDINATE const AdjacentCoord[FACING_COUNT] = { + 0xFF000000L, + 0xFF000100L, + 0x00000100L, + 0x01000100L, + 0x01000000L, + 0x0100FF00L, + 0x0000FF00L, + 0xFF00FF00L +}; + + +/*************************************************************************** +** This converts 0..255 facing values into either 8, 16, or 32 facing values. +** Note: a simple shift won't suffice because 0..255 facing values should +** be converted to the CLOSEST appropriate facing, NOT rounded down to the +** nearest facing. +*/ +unsigned char const Facing8[256] = { + 0,0,0,0,0,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,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +#ifdef NEVER +unsigned char const Facing16[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0 +}; +#endif + +/* +** This table incorporates a compensating factor for the distortion caused +** by 3D-Studio when it tries to render 45% angles. +*/ +unsigned char const Facing32[256] = { + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8, + 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19, + 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24, + 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0 +}; + +#ifdef OBSOLETE +unsigned char const Facing32[256] = { + 0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8, + 9,9,9,9,9,9,9,9, + 10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11, + 12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13, + 14,14,14,14,14,14,14,14, + 15,15,15,15,15,15,15,15, + 16,16,16,16,16,16,16,16, + 17,17,17,17,17,17,17,17, + 18,18,18,18,18,18,18,18, + 19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20, + 21,21,21,21,21,21,21,21, + 22,22,22,22,22,22,22,22, + 23,23,23,23,23,23,23,23, + 24,24,24,24,24,24,24,24, + 25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26, + 27,27,27,27,27,27,27,27, + 28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29, + 30,30,30,30,30,30,30,30, + 31,31,31,31,31,31,31,31, + 0,0,0,0 +}; +#endif + + +/*************************************************************************** +** These are the movement costs (in ticks at fastest speed) to enter each +** of the given terrain cells. +*/ +#define S1 0x00 +#define S2 0x40 +#define S3 0x70 +#define S4 0xA0 +#define S5 0xC0 +#define S6 0xFF +GroundType const Ground[LAND_COUNT] = { +// Foot +// | Tracked +// | | Harvester +// | | | Wheeled +// | | | | Winged +// | | | | | Hover +// | | | | | | float build + {66, {S3, S3, S3, S4, S6, S5, S1 }, true}, // LAND_CLEAR + {68, {S5, S4, S4, S4, S6, S5, S1 }, true}, // LAND_ROAD + {BLUE, {S1, S1, S1, S1, S6, S5, S6 }, false}, // LAND_WATER + {DKGREY, {S1, S1, S1, S1, S6, S1, S1 }, false}, // LAND_ROCK + {DKGREY, {S1, S1, S1, S1, S6, S1, S1 }, false}, // LAND_WALL + {143, {S3, S3, S3, S4, S6, S5, S1 }, false}, // LAND_TIBERIUM + {66, {S3, S3, S3, S4, S6, S5, S1 }, false}, // LAND_BEACH +}; + + +/*************************************************************************** +** These are the names of the theaters. +*/ +TheaterDataType const Theaters[THEATER_COUNT] = { + {"DESERT","DESERT","DES"}, + {"JUNGLE","JUNGLE","JUN"}, + {"TEMPERATE","TEMPERAT","TEM"}, + {"WINTER","WINTER","WIN"} +}; + + +/*************************************************************************** +** These are the remap tables that are used to convert the units/buildings +** into the other color schemes. +*/ +unsigned char const RemapGold[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapRed[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 127,126,125,124,122,46,120,47,125,124,123,122,42,121,120,120, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapBlue[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 2,119,118,135,136,138,112,12,118,135,136,137,138,139,114,112, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapOrange[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 24,25,26,27,29,31,46,47,26,27,28,29,30,31,43,47, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapGreen[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 5,165,166,167,159,142,140,199,166,167,157,3,159,143,142,141, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapLtBlue[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 161,200,201,202,204,205,206,12,201,202,203,204,205,115,198,114, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapNone[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; \ No newline at end of file diff --git a/TIBERIANDAWN/CONTROL.CPP b/TIBERIANDAWN/CONTROL.CPP new file mode 100644 index 000000000..28683058e --- /dev/null +++ b/TIBERIANDAWN/CONTROL.CPP @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\control.cpv 2.18 16 Oct 1995 16:51:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ControlClass::Action -- Normal action for control gaget objects. * + * ControlClass::ControlClass -- Constructor for control class objects. * + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Constructor for control class objects. * + * * + * This is the normal constructor for control class objects. At this level, it only needs * + * to record the ID number assigned to this button. * + * * + * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then * + * this tells the system that no special ID code should be returned. * + * * + * x,y -- Pixel coordinate of upper left corner of gadget's region. * + * * + * w,h -- Pixel dimensions of the gadget's region. * + * * + * flags -- The input event flags that this gadget recognizes. * + * * + * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the * + * gadget list while the mouse button is held down, if the mouse button was * + * initially clicked over its region. This is the behavior of "normal" * + * buttons in Windows. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) + : GadgetClass(x, y, w, h, flags, sticky) +{ + ID = id; + Peer = 0; +} + + +/*********************************************************************************************** + * ControlClass::Action -- Normal action for control gaget objects. * + * * + * This function gets called when the input event that this control gadget is looking for * + * occurs. In such a case, the return key code value is changed to the gaget's ID number * + * with the special button bit flag attached. * + * * + * INPUT: flags -- The event that triggered this function call. If this value is NULL, then * + * this is a forced (probably due to the sticky flag) call and the key code * + * is not altered. * + * * + * key -- Reference to the key code that will be returned by the controlling * + * Input() function. * + * * + * OUTPUT: bool; Should futher list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Action(unsigned flags, KeyNumType & key) +{ + /* + ** If there is a peer link established, inform that gadget of this + ** action call. + */ + if (Peer) { + Peer->Peer_To_Peer(flags, key, *this); + } + + /* + ** Only if the flags indicate that a recognized action has occured, do the + ** normal processing of this gadget and set return value to the gadget ID. + */ + if (flags) { + if (ID) { + key = (KeyNumType)(ID | KN_BUTTON); + } else { + key = KN_NONE; + } + } + + return(GadgetClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * * + * This function will assign another gadget to this one. That other gadget will receive * + * notification of any Action() call to this gadget. Presumably, this is how one gadget * + * can automatically adapt to changes in another. Say for example, a slider bar can affect * + * the list box it is attached to. * + * * + * INPUT: gadget -- The gadget to inform when any Action() function is called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ControlClass::Make_Peer(GadgetClass & gadget) +{ + Peer = &gadget; +} + + +/*********************************************************************************************** + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * * + * This function will query and return with the ID number for this gadget. It is primarily * + * used by the Extract_Gadget() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that * + * no ID was assigned to this gadget. This is a special case since a zero value will * + * never be returned as a pseudo-key as is done with non-zero values. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +unsigned ControlClass::Get_ID(void) const +{ + return(ID); +} + + +/*********************************************************************************************** + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * * + * This is called when the control object might need to be redrawn or when redrawing is * + * necessary. Since at this level of the class heirarchy, no actual drawing occurs, this * + * routine doesn't perform any rendering. It does, however, inform any peer attached * + * object that a Draw_Me function has been called. Presumably, the attached peer gadget * + * might very well need to be redrawn as a result of some action by this gadget. Since this * + * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me * + * for the other gadget will not occur. It must rely on the call by this routine in order * + * to update correctly. A typical example of this would be a slider that is attached to * + * a list box. As the slider is being drug around, the attached list box must be redrawn. * + * * + * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the gadget redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Draw_Me(int forced) +{ + if (Peer) { + Peer->Draw_Me(); + } + return(GadgetClass::Draw_Me(forced)); +} diff --git a/TIBERIANDAWN/CONTROL.H b/TIBERIANDAWN/CONTROL.H new file mode 100644 index 000000000..d20262b61 --- /dev/null +++ b/TIBERIANDAWN/CONTROL.H @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\control.h_v 2.18 16 Oct 1995 16:46:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "gadget.h" + +/*************************************************************************** + * ControlClass -- Region tracking class * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: 0 = new scenario created, -1 = not * + * WARNINGS: This class is Abstract (cannot make an instance of it) * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=========================================================================*/ +class ControlClass : public GadgetClass +{ + public: + ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); +// static ControlClass * Create_One_Of(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); + + virtual void Make_Peer(GadgetClass & gadget); + + /* + ** Render support function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the ID number for this control gadget. This number is used to generate + ** a special pseudo-key when the gadget detects valid input. + */ + unsigned ID; + + protected: + virtual unsigned Get_ID(void) const; + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This points to the peer button to inform when something happens to this + ** gadget. + */ + GadgetClass * Peer; +}; + +#endif + diff --git a/TIBERIANDAWN/COORD.CPP b/TIBERIANDAWN/COORD.CPP new file mode 100644 index 000000000..f8e6d7604 --- /dev/null +++ b/TIBERIANDAWN/COORD.CPP @@ -0,0 +1,599 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\coord.cpv 2.18 16 Oct 1995 16:51:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COORD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : January 7, 1995 [JLB] * + * * + * Support code to handle the coordinate system is located in this module. * + * Routines here will be called QUITE frequently during play and must be * + * as efficient as possible. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * * + * This routine will take an arbitrary position and object size and return with a list of * + * cell offsets from the current cell for all cells that are overlapped by the object. The * + * first cell offset is always zero, so to just get the adjacent spill cell list, add one * + * to the return pointer. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * maxsize -- The maximum width/height of the object (pixels). * + * * + * OUTPUT: Returns with a pointer to a spillage list. * + * * + * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values * + * will generate an incomplete overlap list. * + * * + * HISTORY: * + * 11/06/1993 JLB : Created. * + * 03/25/1994 JLB : Added width optimization. * + * 04/29/1994 JLB : Converted to C. * + * 06/03/1994 JLB : Converted to general purpose spillage functionality. * + * 01/07/1995 JLB : Manually calculates spillage list for large objects. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, int maxsize) +{ + static short const _MoveSpillage[(int)FACING_COUNT+1][5] = { + {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N + {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE + {0, 1, REFRESH_EOL, 0, 0}, // E + {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE + {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S + {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW + {0, -1, REFRESH_EOL, 0, 0}, // W + {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW + {0, REFRESH_EOL, 0, 0, 0} // non-moving. +// {0, -MAP_CELL_W, -(MAP_CELL_W-1), 1, MAP_CELL_W+1, MAP_CELL_W, MAP_CELL_W-1, -1, -(MAP_CELL_W+1), REFRESH_EOL} + }; + static short _manual[10]; +//; 00 = on axis +//; 01 = below axis +//; 10 = above axis +//; 11 = undefined + static char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1}; + int index=0; + int x,y; + + /* + ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table + ** that covers a 5x5 square region. + */ + if (maxsize > ICON_PIXEL_W * 2) { + static short const _gigundo[] = { + -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2), + -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2), + -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2), + ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2), + +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2), + REFRESH_EOL + }; + return(&_gigundo[0]); + } + + /* + ** For very large objects, build the overlap list by hand. This is time consuming, but + ** not nearly as time consuming as drawing even a single cell unnecessarily. + */ + if (maxsize > ICON_PIXEL_W) { + maxsize = MIN(maxsize, (ICON_PIXEL_W*2))/2; + + x = Fixed_To_Cardinal(ICON_PIXEL_W, Coord_XLepton(coord)); + y = Fixed_To_Cardinal(ICON_PIXEL_H, Coord_YLepton(coord)); + int left = x-maxsize; + int right = x+maxsize; + int top = y-maxsize; + int bottom = y+maxsize; + + _manual[index++] = 0; + if (left < 0) _manual[index++] = -1; + if (right >= ICON_PIXEL_W) _manual[index++] = 1; + if (top < 0) _manual[index++] = -MAP_CELL_W; + if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W; + if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1); + if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1; + if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1; + if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1); + _manual[index] = REFRESH_EOL; + return(&_manual[0]); + } + + /* + ** Determine the number of leptons "leeway" allowed this unit. + */ + int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2]; + + x = Coord_XLepton(coord) - 0x0080; + y = Coord_YLepton(coord) - 0x0080; + if (y > posval) index |= 0x08; // Spilling South. + if (y < -posval) index |= 0x04; // Spilling North. + if (x > posval) index |= 0x02; // Spilling East. + if (x < -posval) index |= 0x01; // Spilling West. + + return(&_MoveSpillage[_SpillTable[index]][0]); +} + + +/*********************************************************************************************** + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * * + * This function will move a coordinate in a using SIN and COS arithmetic. * + * * + * INPUT: start -- The starting coordinate. * + * * + * dir -- The direction to move the coordinate. * + * * + * distance -- The distance to move the coordinate position (in leptons). * + * * + * OUTPUT: Returns the new coordinate position. * + * * + * WARNINGS: This routine uses multiplies -- use with caution. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance) +{ + short x = Coord_X(start); + short y = Coord_Y(start); + + Move_Point(x, y, dir, distance); + return(XY_Coord(x,y)); +#ifdef NEVER + static char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + *((int*)&start) += _DX; +// asm add [word ptr start],ax + + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + *(((int*)&start)+1) += _DX; +// asm add [word ptr start+2],ax + + return(start); +#endif +} + +#ifdef OBSOLETE +//BG: Note, this routine is replaced by assembly in COORDA.ASM +/*********************************************************************************************** + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * * + * This utility function will convert cardinal numbers into a fixed point fraction. The * + * use of fixed point numbers occurs throughout the product -- since it is a convenient * + * tool. The fixed point number is based on the formula: * + * * + * result = cardinal / base * + * * + * The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * + * 256 as the largest. * + * * + * INPUT: base -- The key number to base the fraction about. * + * * + * cardinal -- The other number (hey -- what do you call it?) * + * * + * OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * + * to the "base" parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal) +{ + long temp = 0; + + if (base) { + *(unsigned*)(((char*)&temp)+1) = cardinal; // Shift up by 8 bits. + _DX = FP_SEG(temp); + _AX = FP_OFF(temp); + asm div word ptr base + return(_AX); + } + return(0xFFFF); +} +#endif + +#ifdef OBSOLETE +//BG: Note, this routine is replaced by assembly in COORDA.ASM +/*********************************************************************************************** + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * * + * Use this routine to convert a fixed point number into a cardinal number. * + * * + * INPUT: base -- The base number that the original fixed point number was created from. * + * * + * fixed -- The fixed point number to convert. * + * * + * OUTPUT: Returns with the reconverted number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed) +{ + unsigned long temp; + + _AX = base; + asm mul word ptr fixed + _CX = _AX; + temp = ((unsigned long)MK_FP(_DX, _CX)) + 0x80; + if ( *((char*)&temp+3) ) { + return(0xFFFF); + } + return(*(unsigned*)(((char*)&temp)+1)); +} +#endif + + +/*********************************************************************************************** + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * * + * This routine will perform a scatter algorithm on the specified * + * anchor point in order to return with another coordinate that is * + * randomly nearby the original. Typical use of this would be for * + * missile targeting. * + * * + * INPUT: coord -- This is the anchor coordinate. * + * * + * distance -- This is the distance in pixels that the scatter * + * should fall within. * + * * + * lock -- bool; Convert the new coordinate into a center * + * cell based coordinate? * + * * + * OUTPUT: Returns with a new coordinate that is nearby the original. * + * * + * WARNINGS: Maximum pixel scatter distance is 255. * + * * + * HISTORY: * + * 02/01/1992 JLB : Created. * + * 05/13/1992 JLB : Only uses Random(). * + *=============================================================================================*/ +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock) +{ + COORDINATE newcoord; + + newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance); + + if (newcoord & 0xC000C000L) newcoord = coord; + + if (lock) { + newcoord = Coord_Snap(newcoord); + } + + return(newcoord); +} + + + +int __cdecl calcx(signed short param1, short distance) +{ + __asm { + +//#pragma aux calcx parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax,1 + rcl dx,1 + mov al,ah + mov ah,dl + cwd + } +} + + +int __cdecl calcy(signed short param1, short distance) +{ + __asm { + +//#pragma aux calcy parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax,1 + rcl dx,1 + mov al,ah + mov ah,dl + cwd + neg eax + } +} + + + + +#if (0) +extern int calcx(signed short, short distance); +#pragma aux calcx parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ +// "and eax,0FFFFh"; + +extern int calcy(signed short, short distance); +#pragma aux calcy parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ + "neg eax"; +// "and eax,0FFFFh" +#endif + + + +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ +// static char const CosTable[256] = { + static unsigned char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + +// static char const SinTable[256] = { + static unsigned char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + +#ifdef OBSOLETE + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + x += _DX; +#else + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + // ST - 1/10/2019 11:20AM + // + char *cos_table = (char*)&CosTable[0]; + x += calcx(cos_table[dir], distance); +#endif +// asm add [word ptr start],ax + +#ifdef OBSOLETE + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + y += _DX; +#else + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + // ST - 1/10/2019 11:20AM + // + char *sin_table = (char*)&SinTable[0]; + y += calcy(sin_table[dir], distance); +#endif +// asm add [word ptr start+2],ax + +} \ No newline at end of file diff --git a/TIBERIANDAWN/COORDA.ASM b/TIBERIANDAWN/COORDA.ASM new file mode 100644 index 000000000..398ab7c02 --- /dev/null +++ b/TIBERIANDAWN/COORDA.ASM @@ -0,0 +1,131 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C Cardinal_To_Fixed :NEAR +global C Fixed_To_Cardinal :NEAR + + CODESEG + +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Cardinal_To_Fixed(unsigned base, unsigned cardinal); + + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed + + +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Fixed_To_Cardinal(unsigned base, unsigned fixed); + PROC Fixed_To_Cardinal C near + USES edx + + ARG base:DWORD + ARG fixed:DWORD + + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END diff --git a/TIBERIANDAWN/COORDA.h b/TIBERIANDAWN/COORDA.h new file mode 100644 index 000000000..912c662ed --- /dev/null +++ b/TIBERIANDAWN/COORDA.h @@ -0,0 +1,141 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + +#ifndef COORD_A_H + +//IDEAL +//P386 +//MODEL USE32 FLAT + +//global C Cardinal_To_Fixed :NEAR +//global C Fixed_To_Cardinal :NEAR + +// CODESEG +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal); + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed); + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + + + + +#endif COORD_A_H \ No newline at end of file diff --git a/TIBERIANDAWN/CREDITS.CPP b/TIBERIANDAWN/CREDITS.CPP new file mode 100644 index 000000000..098f8de95 --- /dev/null +++ b/TIBERIANDAWN/CREDITS.CPP @@ -0,0 +1,183 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\credits.cpv 2.17 16 Oct 1995 16:51:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDITS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 17, 1994 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CreditClass::AI -- Handles updating the credit display. * + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * * + * This is the constructor for the credit class object. It merely sets the credit display * + * state to null. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +CreditClass::CreditClass(void) +{ + IsToRedraw = false; + IsUp = false; + IsAudible = false; + Credits = 0; + Current = 0; + Countdown = 0; +} + + +/*********************************************************************************************** + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * * + * This routine should be called whenever the main game screen is to be updated. It will * + * check to see if the credit display should be redrawn. If so, it will redraw it. * + * * + * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw * + * flag is set? This is typically the case when the screen needs to be * + * redrawn from scratch. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +#define XX (320-120) +#define WW 50 +void CreditClass::Graphic_Logic(bool forced) +{ + int factor = Get_Resolution_Factor(); + int xx = SeenBuff.Get_Width() - (120 << factor); + if (forced || IsToRedraw) { + + /* + ** Play a sound effect when the money display changes, but only if a sound + ** effect was requested. + */ + if (IsAudible) { + if (IsUp) { + Sound_Effect(VOC_UP, VOL_1); + } else { + Sound_Effect(VOC_DOWN, VOL_1); + } + } + + /* + ** Display the new current value. + */ + //LogicPage->Fill_Rect(xx-(20 << factor), 1 << factor, xx+(20 << factor), 6 << factor, LTGREY); + TabClass::Draw_Credits_Tab(); + Fancy_Text_Print("%ld", xx, 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL, Current); + + IsToRedraw = false; + IsAudible = false; + } +} + + +/*********************************************************************************************** + * CreditClass::AI -- Handles updating the credit display. * + * * + * This routine handles the logic that controls the rate of credit change in the credit * + * display. It doesn't actually redraw the credit display, but will flag it to be redrawn * + * if it detects that a change is to occur. * + * * + * INPUT: forced -- Should the credit display immediately reflect the current credit * + * total for the player? This is usually desired when initially loading * + * a scenario or saved game. * + * player_ptr -- Player to calculate visible credits for * + * logic_only -- If true, don't flag map for redraw * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + * 10/16/2019 ST : Added house and logic parameters so we can call this from HouseClass::AI * + *=============================================================================================*/ +void CreditClass::AI(bool forced, HouseClass *player_ptr, bool logic_only) +{ + if (player_ptr == NULL) { + return; + } + + Credits = player_ptr->Available_Money(); + + /* + ** Make sure that the credit counter doesn't drop below zero. + */ + Credits = MAX(Credits, 0L); + + if (Current == Credits) return; + + if (forced) { + IsAudible = false; + Current = Credits; + } else { + + if (Countdown) Countdown--; + if (Countdown) return; + + /* + ** Determine the amount to change the display toward the + ** desired value. + */ + long adder = Credits - Current; + adder = ABS(adder); + adder >>= 5; + adder = Bound(adder, 1L, 71+72); + if (Current > Credits) adder = -adder; + Current += adder; + Countdown = 1; + + if (Current-adder != Current) { + IsAudible = true; + IsUp = (adder > 0); + } + } + IsToRedraw = true; + + if (!logic_only) { + Map.Flag_To_Redraw(false); + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/CREDITS.H b/TIBERIANDAWN/CREDITS.H new file mode 100644 index 000000000..52cb0dd71 --- /dev/null +++ b/TIBERIANDAWN/CREDITS.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\credits.h_v 2.18 16 Oct 1995 16:47:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREDITS_H +#define CREDITS_H + +class HouseClass; +extern HouseClass *PlayerPtr; + +/**************************************************************************** +** The animating credit counter display is controlled by this class. +*/ +class CreditClass { + public: + long Credits; // Value of credits trying to update display to. + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CreditClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Update(bool forced=false, bool redraw=false); + + void Graphic_Logic(bool forced=false); + void AI(bool forced=false, HouseClass *player_ptr = PlayerPtr, bool logic_only = false); // Added house and logic_only parameters. ST - 10/16/2019 2:26PM + + long Current; // Credit value currently displayed. + + unsigned IsToRedraw:1; + unsigned IsUp:1; + unsigned IsAudible:1; + + private: + int Countdown; // Delay between ticks. +}; + +#endif diff --git a/TIBERIANDAWN/CREW.CPP b/TIBERIANDAWN/CREW.CPP new file mode 100644 index 000000000..c38fa7ac8 --- /dev/null +++ b/TIBERIANDAWN/CREW.CPP @@ -0,0 +1,35 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\crew.cpv 2.18 16 Oct 1995 16:50:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" diff --git a/TIBERIANDAWN/CREW.H b/TIBERIANDAWN/CREW.H new file mode 100644 index 000000000..fb02b4b24 --- /dev/null +++ b/TIBERIANDAWN/CREW.H @@ -0,0 +1,62 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\crew.h_v 2.18 16 Oct 1995 16:47:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREW_H +#define CREW_H + + +/**************************************************************************** +** This class handles the basic crew logic. This includes hero tracking, +** crew bail-out, and attached object logic. +*/ +class CrewClass +{ + public: + /* + ** This keeps track of the number of "kills" the unit as accumulated. + ** When it reaches a certain point, the unit improves. + */ + unsigned short Kills; + + /* + ** Constructors, Destructors, and overloaded operators. + */ + CrewClass(void) {Kills = 0;}; + + int Made_A_Kill(void) {Kills++;return(Kills);}; + + private: +}; + +#endif diff --git a/TIBERIANDAWN/DDE.CPP b/TIBERIANDAWN/DDE.CPP new file mode 100644 index 000000000..7be374992 --- /dev/null +++ b/TIBERIANDAWN/DDE.CPP @@ -0,0 +1,465 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.CPP * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Instance_Class::InstanceClass -- class constructor * + * Instance_Class::InstanceClass -- class destructor * + * Instance_Class::Enable_Callback -- enables local processing of pokes * + * Instance_Class::Register_Servers -- registers a local DDE DNS service * + * Instance_Class::Cleanup_App -- currently does nothing * + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * Instance_Class::Open_Poke_Connection -- pokes some data to server * + * Instance_Class::Close_Poke_Connectionp -- closes connection to remote * + * Instance_Class::Poke_Server -- sends a chunk of data to remote * + * Instance_Class::dde_callback -- processes DDE transactions * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "dde.h" + +/*************************************************************************** + * These are static members of Instance_Class + *=========================================================================*/ + +DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize +BOOL Instance_Class::process_pokes; // controls response to pokes +char Instance_Class::ascii_name[32]; // name of server + +//PG_TO_FIX ST - 12/20/2018 2:17PM +#if (0) +static BOOL CALLBACK (*Instance_Class::callback) ( + LPBYTE pointer, // pointer to received data + long length // length of received data or advisory flag + ) = NULL; +#endif + +/*************************************************************************** + * Instance_Class::InstanceClass -- class constructor * + * * + * INPUT: * + * name1 null terminated ASCII client name * + * name1 null terminated ASCII server name * + * * + * OUTPUT: * + * dde_error = TRUE if error occurs when initializing DDE * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 ) +{ + name1; + name2; + return; + +#if (0) //ST - 1/2/2019 5:35PM + dde_error = FALSE; // no errors + process_pokes = FALSE; // disable pokes in callback + + id_inst = 0; // set to 0 for first time through + conv_handle = 0; // conversation handle reset + + lstrcpy( ascii_name, name1 ); // keep a record of ASCII name + + if ( DdeInitialize( + (LPDWORD) &id_inst, // instance identifier + dde_callback, + APPCLASS_STANDARD | // filter server messages + CBF_FAIL_SELFCONNECTIONS, // prevent from connecting with self + 0) != DMLERR_NO_ERROR) { // reserved + dde_error = TRUE; // flag an error + } + + local_name = DdeCreateStringHandle( + id_inst, // instance identifier + name1, // string to register + CP_WINANSI); // Windows ANSI code page + + remote_name = DdeCreateStringHandle( + id_inst, // instance identifier + name2, // string to register + CP_WINANSI); // Windows ANSI code page + + poke_topic = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE TOPIC", // System topic + CP_WINANSI); // Windows ANSI code page + + poke_item = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE ITEM", // System topic + CP_WINANSI); // Windows ANSI code page + + system_topic = DdeCreateStringHandle( + id_inst, // instance identifier + SZDDESYS_TOPIC, // System topic + CP_WINANSI); // Windows ANSI code page +#endif +} + +/*************************************************************************** + * Instance_Class::~Instance_Class -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::~Instance_Class() +{ + DdeUninitialize( id_inst ); +} + +/*************************************************************************** + * Instance_Class::Enable_Callback -- enables user callback * + * * + * INPUT: * + * TRUE = enable poke processing * + * FALSE = disable poke processing * + * * + * OUTPUT: * + * echos the input * + * * + * WARNINGS: * + * user callback must be explicitly enabled. Disbabled by default. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Enable_Callback( BOOL flag ) // enable or disable callback +{ + return (process_pokes = flag); + +} + +/*************************************************************************** + * Instance_Class::Register_Server -- registers a local DDE DNS service * + * * + * INPUT: * + * BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl * + * * + * OUTPUT: * + * TRUE == success * + * FALSE == failed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ +#if (0) +BOOL Instance_Class::Register_Server( BOOL (CALLBACK *)(LPBYTE, long)); + +//BOOL Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) ) +{ + + if (DdeNameService( id_inst, local_name, 0L, DNS_REGISTER ) != 0L) { + callback = callback_fnc; + return ( TRUE ); + } else { + return ( FALSE ); + } +} +#endif +/*************************************************************************** + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * * + * INPUT: * + * name = HSZ string handle of server name. * + * * + * OUTPUT: * + * TRUE == successfully connected to remote * + * FALSE == failed to connect * + * * + * WARNINGS: * + * - Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * - Disconects before exiting. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Test_Server_Running( HSZ name ) +{ + + if( Open_Poke_Connection( name ) == TRUE) { + Close_Poke_Connection(); + return( TRUE ); + } else { + return( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Open_Poke_Connection -- open a connection to server * + * * + * INPUT: * + * name = HSZ server name. * + * * + * OUTPUT: * + * TRUE == successfully opened connection * + * FALSE == failed to connect * + * * + * WARNINGS: * + * Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Open_Poke_Connection( HSZ name ) +{ + conv_handle = DdeConnect( + id_inst, // instance identifier + name, // service name string handle + poke_topic, // topic string handle + (PCONVCONTEXT) NULL);// use default context + + if (conv_handle == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +/*************************************************************************** + * Instance_Class::Close_Poke_Connection -- closes poke connection * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TRUE == successfully closed connection * + * FALSE == failed to close connection for some reason * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Close_Poke_Connection( void ) +{ + if( conv_handle ) { + HCONV temp_handle = conv_handle; + conv_handle = NULL; + return( DdeDisconnect( temp_handle )); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::Poke_Server -- pokes some data to server * + * * + * INPUT: * + * poke_data points to data to send to remote * + * poke_length length of buffer to send * + * * + * OUTPUT: * + * TRUE == successfully poked the data * + * FALSE == failed to connect * + * * + * WARNINGS: * + * has a 3 second timeout (change POKE_TIMEOUT, in milliseconds) * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +#define POKE_TIMEOUT 60*1000 // 60 sec timeout + +BOOL Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length ) +{ + + if( DdeClientTransaction( + + poke_data, // address of data to pass to server + poke_length, // length of data + conv_handle, // handle of conversation + poke_topic, // handle of item name string + CF_TEXT, // no special clipboard data format + XTYP_POKE, // transaction type + POKE_TIMEOUT, // time-out duration (millisecs) + (LPDWORD) NULL // address of transaction result (don't check) + ) == 0) { + + return( FALSE); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::dde_callback -- callback dde event handler * + * * + * INPUT: * + * dde_event transaction type * + * uFmt clipboard data format * + * hconv handle of the conversation * + * hsz1 handle of a string * + * hsz2 handle of a string * + * hdata handle of a global memory object * + * dwData1 transaction-specific data * + * dwData2 transaction-specific data * + * * + * OUTPUT: * + * context specific HDDEDATA object * + * * + * WARNINGS: * + * NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +HDDEDATA CALLBACK Instance_Class::dde_callback( + + UINT dde_event, // transaction type + UINT uFmt, // clipboard data format + HCONV , // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD , // transaction-specific data + DWORD // transaction-specific data + ) +{ + + dde_event; + uFmt; + hsz1; + hsz2; + hdata; + return (HDDEDATA) NULL; + + +#if (0) //ST - 1/2/2019 5:38PM + if (!Instance_Class::callback){ + return (HDDEDATA) NULL; + } + + switch ( dde_event ) { + + case XTYP_REGISTER: + case XTYP_UNREGISTER: + + return (HDDEDATA) NULL; + + case XTYP_ADVDATA: + return (HDDEDATA) DDE_FACK; + + case XTYP_XACT_COMPLETE: + + return (HDDEDATA) NULL; + + case XTYP_DISCONNECT: + + Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT); + return (HDDEDATA) NULL; + + case XTYP_CONNECT: { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, Instance_Class::ascii_name)) { + return (HDDEDATA) NULL; + } + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) NULL; + } + + Instance_Class::callback( NULL, DDE_ADVISE_CONNECT); + return (HDDEDATA) TRUE; + } + + case XTYP_POKE: + + if (Instance_Class::process_pokes == FALSE ) { + return (HDDEDATA) DDE_FNOTPROCESSED; // processing disabled + } else { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } else if (uFmt == CF_TEXT) { // make sure it's CF_TEXT + + BOOL processed; + BYTE FAR *pdata; + DWORD dw_length; + + if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } + + processed = Instance_Class::callback((LPBYTE) pdata, dw_length); + + DdeUnaccessData( hdata ); + + if (processed == TRUE) { + return (HDDEDATA) DDE_FACK; + } else { + return (HDDEDATA) NULL; + } + + } + } + + default: + return (HDDEDATA) NULL; + } +#endif +} \ No newline at end of file diff --git a/TIBERIANDAWN/DDE.H b/TIBERIANDAWN/DDE.H new file mode 100644 index 000000000..258b9738e --- /dev/null +++ b/TIBERIANDAWN/DDE.H @@ -0,0 +1,176 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.H * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * * + * This is the DDE (Instance_Class) which provides a simple CLIENT/SERVER * + * DDE model for data transactions between Windows applications. * + * This is a fairly naieve implementation allowing only one client/server * + * per Instance_Class object. * + * * + * Typical uses for this class are: * + * * + * i. Robust verification of whether an application is running * + * ii. Data transfer between applications * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* +***************************** Class defines ***************************** +*/ +#ifndef __DDE_H +#define __DDE_H + +//#if (0) + +#define DDE_ADVISE_CONNECT -1 // advisory "client has connected" +#define DDE_ADVISE_DISCONNECT -2 // advisory "client has disconnected" + +/* +***************************** Class Declaration ***************************** +*/ + +class Instance_Class { + + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + + /*..................................................................... + Constructor: + - takes null terminated ASCII strings names for client and server + .....................................................................*/ + + Instance_Class( // constructor + LPSTR, // null terminated local sever name string + LPSTR // null terminated remote server name string + ); + + /*..................................................................... + Destructor: + .....................................................................*/ + ~Instance_Class(void); // the destructor + + /*..................................................................... + Send data routine: + - sends an unsolicited packet of data to the remote server + .....................................................................*/ + BOOL Poke_Server( LPBYTE, DWORD); + + /*..................................................................... + Send data routine: + - sets up DNS for the server and registers a user callback to handle + incoming data + .....................................................................*/ + BOOL Register_Server( BOOL (CALLBACK *)(LPBYTE, long)); + +//typedef void (CALLBACK *LPDXUTCALLBACKFRAMEMOVE)( double fTime, float fElapsedTime, void* pUserContext ); + + + /*..................................................................... + Does a trial connect to the remote server. + - used to determine whether server is alive or not (and thus running) + .....................................................................*/ + BOOL Test_Server_Running( HSZ ); + + /*..................................................................... + Enables user callback (disabled by default) + .....................................................................*/ + BOOL Enable_Callback( BOOL ); // enable or disable callback + + /*..................................................................... + Open a connection for sending data to remote server + .....................................................................*/ + BOOL Open_Poke_Connection( HSZ ); + + /*..................................................................... + Close connection with remote server + .....................................................................*/ + BOOL Close_Poke_Connection( void ); + + // + // static members + // + + /*..................................................................... + User callback - called upon receipt of incoming data (static member!) + .....................................................................*/ + static BOOL (CALLBACK *callback) ( + + LPBYTE pointer, // pointer to received data + long length // if >0 length of received data + // if <0 + // -1 == client connect detected + // -2 == client disconnect detected + ); + + /*..................................................................... + DDE callback, called when DDEML has an event for us + .....................................................................*/ + static HDDEDATA CALLBACK dde_callback( + + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); + HANDLE instance; // this application's instance + HWND hwnd; // valid window handle + + /*..................................................................... + member variables + .....................................................................*/ + + static DWORD id_inst; // instance identifier set by DdeInitialize + static BOOL process_pokes; // controls response to pokes + static char ascii_name[32]; // name of server + + // + // non-static member variables + // + + HSZ remote_name; // string handle for remote server name + HSZ local_name; // string handle for local server name + HSZ system_topic; // string handle for the "system" topic + HSZ poke_topic; // string handle for poking data to server topic + HSZ poke_item; // string handle for poking data to server item + + HCONV conv_handle; // conversation handle + BOOL dde_error; // error flag + +}; +#endif + +//#endif + diff --git a/TIBERIANDAWN/DEBUG.CPP b/TIBERIANDAWN/DEBUG.CPP new file mode 100644 index 000000000..0e3816889 --- /dev/null +++ b/TIBERIANDAWN/DEBUG.CPP @@ -0,0 +1,690 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\debug.cpv 2.17 16 Oct 1995 16:49:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 5, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Self_Regulate -- Regulates the logic timer to result in smooth animation. * + * Debug_Key -- Debug mode keyboard processing. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include +#ifdef CHEAT_KEYS + +extern bool ScreenRecording; + +/*********************************************************************************************** + * Debug_Key -- Debug mode keyboard processing. * + * * + * If debugging is enabled, then this routine will be called for every keystroke that the * + * game doesn't recognize. These extra keys usually perform some debugging function. * + * * + * INPUT: input -- The key code that was pressed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Debug_Key(unsigned input) +{ + static int map_x = -1; + static int map_y = -1; + static int map_width = -1; + static int map_height = -1; + + if (!input || input & KN_BUTTON) return; + + /* + ** Processing of normal keystrokes. + */ + if (Debug_Flag) { + + switch (input) { + + case KN_L: + extern int NetMonoMode,NewMonoMode; + if (NetMonoMode) + NetMonoMode = 0; + else + NetMonoMode = 1; + NewMonoMode = 1; + break; + + /* + ** Start saving off screens + */ + case (int)KN_K|(int)KN_CTRL_BIT: + ScreenRecording = true; + break; + + case KN_K: +//PG_TO_FIX +#if (0) + /* + ** time to create a screen shot using the PCX code (if it works) + */ + { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + if (lp < 10) { + sprintf(filename, "scrsht0%d.pcx", lp); + } else { + sprintf(filename, "scrsht%d.pcx", lp); + } + if (access(filename, F_OK) == -1) + break; + } + + Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette); + //Map.Place_Random_Crate(); + } +#endif + break; + + case KN_P: + Keyboard::Clear(); + while (!Keyboard::Check()) { + Self_Regulate(); + Sound_Callback(); + } + Keyboard::Clear(); + break; + + case KN_O: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_ORCA, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case (int)KN_B|(int)KN_ALT_BIT: + { + Debug_Instant_Build ^= 1; + } + break; + case KN_B: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_HELICOPTER, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_T: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_TRANSPORT, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_GRAVE: + new AnimClass(ANIM_ART_EXP1, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + Explosion_Damage(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), 250, NULL, WARHEAD_HE); + break; + + case KN_Z: +// new AnimClass(ANIM_LZ_SMOKE, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + GDI_Ending(); + break; + + case KN_C: + Debug_Cheat = (Debug_Cheat == false); + PlayerPtr->IsRecalcNeeded = true; + PlayerPtr->Add_Nuke_Piece(); + PlayerPtr->Add_Nuke_Piece(); + PlayerPtr->Add_Nuke_Piece(); + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + if (!ScenarioInit) { + Map.Recalc(); + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + } + break; + + case (int)KN_Z|(int)KN_ALT_BIT: + if (map_x == -1) { + map_x = Map.MapCellX; + map_y = Map.MapCellY; + map_width = Map.MapCellWidth; + map_height = Map.MapCellHeight; + Map.MapCellX = 1; + Map.MapCellY = 1; + Map.MapCellWidth = 62; + Map.MapCellHeight = 62; + } else { + Map.MapCellX = map_x; + Map.MapCellY = map_y; + Map.MapCellWidth = map_width; + Map.MapCellHeight = map_height; + map_x = -1; + map_y = -1; + map_width = -1; + map_height = -1; + } + break; + +#ifdef NEVER + case KN_G: + HouseClass::As_Pointer(HOUSE_GOOD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); + break; + + case KN_N: + HouseClass::As_Pointer(HOUSE_BAD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); + break; +#endif + + case KN_R: + if (CurrentObject.Count()) { + ((TechnoClass *)CurrentObject[0])->IsCloakable = true; + } + break; + + case KN_M: + if (Debug_Flag) { + if (MonoClass::Is_Enabled()) { + MonoClass::Disable(); + } else { + MonoClass::Enable(); + } + } + break; + + case (int)KN_W|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Win(); + break; + + case (int)KN_L|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Lose(); + break; + + case KN_F: + Debug_Find_Path ^= 1; + break; + + case KN_DELETE: + if (CurrentObject.Count()) { + Map.Recalc(); + //CurrentObject[0]->Detach_All(); + delete CurrentObject[0]; + } + break; + + case KN_D: + if (Teams.Ptr(0)) { + delete Teams.Ptr(0); + } + break; + + case (int)KN_DELETE|(int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + Map.Recalc(); + int damage = 50; + CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); + } + break; + + case KN_INSERT: + if (CurrentObject.Count()) { + Map.PendingObject = &CurrentObject[0]->Class_Of(); + if (Map.PendingObject) { + Map.PendingHouse = CurrentObject[0]->Owner(); + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + } + } + } + break; + +#ifdef NEVER + case KN_1: + case KN_2: + case KN_3: + case KN_4: + case KN_5: + case KN_6: + case KN_7: + case KN_8: + case KN_9: + case KN_0: + MonoPage = (input & 0xFF) - KN_1; + MonoPage %= sizeof(MonoArray)/sizeof(MonoArray[0]); + MonoArray[MonoPage].View(); + input = 0; + break; +#endif + +#ifdef NEVER + case ((int)KN_F1 | (int)KN_SHIFT_BIT): + Special.IsBarOn = (Special.IsBarOn == false); + Map.Flag_To_Redraw(true); + break; + + case ((int)KN_F1 | (int)KN_SHIFT_BIT): // quick load/save for debugging + if (!Save_Game(0,"Command & Conquer Save Game File")) { + CCMessageBox().Process("Error saving game!"); + Prog_End(); + exit(EXIT_SUCCESS); + } + break; + + case ((int)KN_F2 | (int)KN_SHIFT_BIT): // quick load/save for debugging + if (!Load_Game(0)) { + CCMessageBox().Process("Error loading game!"); + Prog_End(); + exit(EXIT_SUCCESS); + } + break; + +//#ifdef SCENARIO_EDITOR + case KN_F2: // enable/disable the map editor + Go_Editor(!Debug_Map); + break; +//#endif +#endif + +#ifdef NEVER + case KN_F2: { + Debug_Map++; + Scenario_Editor(); + Debug_Map--; +#ifdef NEVER + COORDINATE coord; + int index; + static COORDINATE _coords[] = { + 0x00010001L, + 0x00800080L, + 0x00810081L, + 0x00010081L, + 0x00810001L, + 0x00800081L, + 0x00800001L, + 0x00010080L, + 0x00810080L, + 0L + }; + index = 0; + while (_coords[index]) { + coord = _coords[index++]; + Mono_Printf("Spillage for %08lX = %d.\r", coord, Coord_Spillage_Number(coord)); + } + Keyboard::Clear(); + Keyboard::Get(); + +#endif + +#ifdef NEVER +#define MAX_RADIUS 10 + COORDINATE coord; + int x,y; + COORDINATE const *ptr; + int input; + int f1,f2; + TurnTrackType const *track; + + #define XCENTER 160 + #define YCENTER 100 + for (;;) { + VisiblePage.Clear(); + + // Draw grid. + { + static int _gridx[] = {0,64,128,192,0,64,128,192,0,64,128,192}; + static int _gridy[] = {0,0,0,0,64,64,64,64,128,128,128,128}; + int index; + + for (index = 0; index < 12; index++) { + LogicPage->Put_Pixel((_gridx[index]+XCENTER)-(32+64),(_gridy[index]+YCENTER)-(32+64), DKGRAY); + } + } + + // Get facing #1. + LogicPage->Print("Facing #1 (0-7)?", 0, 0, WHITE, BLACK); + input = Keyboard::Get(); + if (input == KA_ESC) break; + input -= KA_0; + input = Bound(input, 0, 7); +// input = MAX(input, 0); +// input = MIN(input, 7); + f1 = input; + Int_Print(f1, 100, 0, WHITE, BLACK); + + // Get facing #2. + LogicPage->Print("Facing #2 (0-7)?", 0, 10, WHITE, BLACK); + input = Keyboard::Get(); + if (input == KA_ESC) break; + input -= KA_0; + input = Bound(input, 0, 7); +// input = MAX(input, 0); +// input = MIN(input, 7); + f2 = input; + Int_Print(f2, 100, 10, WHITE, BLACK); + + track = &TrackControl[f1][f2]; + if (track->Track == 0) { + LogicPage->Print("Undefined track.", 0, 30, WHITE, BLACK); + } else { + int index; // Track index counter. + + ptr = TrackPointers[track->Track-1]; + index = 0; + while (ptr[index]) { + coord = Smooth_Turn(NULL, ptr[index], track->Flag); + + x = (int)(coord & 0xFFFF); + y = (int)((coord >> 16) & 0xFFFF); + LogicPage->Put_Pixel(XCENTER + (x>>2), YCENTER + (y>>2), WHITE); + Delay(1); + index++; + } + + } + input = Keyboard::Get(); + if (input == KA_ESC) break; + } + + Map.Flag_To_Redraw(true); +#endif +#ifdef NEVER + FILE *fh; + int index; + COORDINATE coord; + + fh = fopen("diagonal.txt", "wt"); + if (fh) { + + fprintf(fh, "track 2\n"); + coord = 0x0100FF00L; + for (index = 0; index <= 48; index++) { + fprintf(fh, "0x%08lXL\n", coord); + coord = Coord_Move(coord, 32, 11); + } + fprintf(fh, "\n\n"); + + fprintf(fh, "track 1\n"); + coord = 0x01000000L; + for (index = 0; index <= 40; index++) { + fprintf(fh, "0x%08lXL\n", coord); + coord = Coord_Move(coord, 0, 11); + } + fprintf(fh, "\n\n"); + + fclose(fh); + } +#endif +#ifdef NEVER + FILE *fh; + int x,y,radius; + int radsize[MAX_RADIUS+2]; + int count; + + memset(radsize, 0, sizeof(radsize)); + fh = fopen("Range.txt", "wt"); + if (fh) { + fprintf(fh, "int const RadiusOffset[] = {\n"); + + for (radius = 0; radius <= MAX_RADIUS; radius++) { + + fprintf(fh, "\t/* %-2d */\t", radius); + for (y = -MAX_RADIUS; y <= MAX_RADIUS; y++) { + for (x = -MAX_RADIUS; x <= MAX_RADIUS; x++) { + int xd,yd,dist; + + xd = ABS(x); + yd = ABS(y); + if (xd > yd) { + dist = yd/2 + xd; + } else { + dist = xd/2 + yd; + } + if (dist == radius) { + dist = y*MAP_CELL_W + x; + + if (y) { + if (y < 0) { + fprintf(fh, "(-MCW*%d)", ABS(y)); + } else { + fprintf(fh, "(MCW*%d)", ABS(y)); + } + fprintf(fh, "%c%d,", (x<0) ? '-' : '+', ABS(x)); + } else { + fprintf(fh, "%d,", x); + } + radsize[radius]++; + } + } + } + fprintf(fh, "\n"); + } + fprintf(fh, "};\n\n"); + + count = 0; + fprintf(fh, "int const RadiusCount[%d] = {", MAX_RADIUS+1); + for (radius = 0; radius <= MAX_RADIUS; radius++) { + count += radsize[radius]; + fprintf(fh, "%d", count); + if (radius != MAX_RADIUS) { + fprintf(fh, ","); + } + } + fprintf(fh, "};\n"); + fclose(fh); + } +#endif + } + break; +#endif + +#ifdef NEVER + case ((int)KN_F3 | (int)KN_ALT_BIT): // quick load/save for debugging + Debug_Threat = (Debug_Threat == false); + Map.Flag_To_Redraw(true); + break; + +#endif + + case KN_F3: + Debug_Icon = (Debug_Icon == false); + Map.Flag_To_Redraw(true); + break; + + + /* + ** Reveal entire map to player. + */ + case KN_F4: + if (GameToPlay == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Shows sight and fire range in the form of circles emanating from the currently + ** selected unit. The white circle is for sight range, the red circle is for + ** fire range. + */ + case KN_F7: + if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { + TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); + int sight = ((int)ttype.SightRange)<<8; + int weapon = 0; + if (ttype.Primary != WEAPON_NONE) weapon = Weapons[ttype.Primary].Range; + Set_Logic_Page(SeenBuff); + COORDINATE center = CurrentObject[0]->Center_Coord(); + COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); + + for (int r = 0; r < 255; r += 10) { + int x,y,x1,y1; + DirType r1 = (DirType)r; + DirType r2 = (DirType)((r+10) & 0xFF); + + if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); + } + if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); + } + } + } + break; + + case ((int)KN_F4 | (int)KN_CTRL_BIT): + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + break; + +#ifdef NEVER + case KN_F5: + Special.IsShowPath = (Special.IsShowPath == false); + //PlayerPtr->Credits += 1000; + break; + + case KN_F6: + if (Map.In_Radar(XY_Cell(Map.MapCellX+5, Map.MapCellY - 1))) { + Mono_Printf("Arrrggggghhhhh!"); + } else { + Mono_Printf("No Arrrggggghhhhh!"); + } + break; + + case ((int)KN_F9 | (int)KN_CTRL_BIT): + if (HouseClass::As_Pointer(HOUSE_GOOD)) + (HouseClass::As_Pointer(HOUSE_GOOD))->Blowup_All(); + break; + + case ((int)KN_F10 | (int)KN_CTRL_BIT): + if (HouseClass::As_Pointer(HOUSE_BAD)) + (HouseClass::As_Pointer(HOUSE_BAD))->Blowup_All(); + break; +#endif + } + + } +} + + +/*********************************************************************************************** + * Self_Regulate -- Regulates the logic timer to result in smooth animation * + * * + * The self regulation process checks the number of frames displayed * + * per second and from this determines the amount of time to devote * + * to internal logic processing. By adjusting the time allotted to * + * internal processing, smooth animation can be maintained. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: In order for this routine to work properly it MUST be * + * called every display loop. * + * * + * HISTORY: * + * 07/31/1991 JLB : Created. * + * 07/05/1994 JLB : Handles new monochrome system. * + *=============================================================================================*/ +#define UPDATE_INTERVAL TIMER_SECOND +void Self_Regulate(void) +{ + static CountDownTimerClass DebugTimer(BT_SYSTEM); + static ObjectClass * _lastobject = 0; + + if (!DebugTimer.Time()) { + DebugTimer.Set(UPDATE_INTERVAL); + + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + mono->Set_Default_Attribute(2); + + switch (MonoPage) { + case 0: + mono = &MonoArray[0]; + mono->Clear(); + + /* + ** Display the status of the currently selected object. + */ + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject) { + _lastobject->Debug_Dump(mono); + } + Logic.Debug_Dump(mono); + mono->Set_Cursor(0, 20); + mono->Printf( + "Heap size:%10ld \r" + "Largest: %10ld \r" + "Ttl Free: %10ld \r" + "Frag: %10ld \r", + Heap_Size(MEM_NORMAL), + Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) + ); + *MonoClass::Get_Current() = *mono; + break; + } + + MonoArray[MonoPage] = *mono; + } + } +} +#endif diff --git a/TIBERIANDAWN/DEBUG.H b/TIBERIANDAWN/DEBUG.H new file mode 100644 index 000000000..57ad063bb --- /dev/null +++ b/TIBERIANDAWN/DEBUG.H @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define TXT_NONE_DEBUG 0x3e8 // +#define TXTED_BLANK 0x3e9 // ____ +#define TXTED_UNABLETOREAD 0x3ea // Unable to read scenario! +#define TXTED_FILEEXISTS 0x3eb // File exists. Replace? +#define TXTED_LOWMEM 0x3ec // Insufficient memory! +#define TXTED_EXIT 0x3ed // Exit Scenario Editor? +#define TXT_GENERIC_EXCEPTION 0x3ee // ERROR: Exception was +#define TXT_RADIO_1 0x3ef // hisssss +#define TXT_RADIO_2 0x3f0 // Roger. +#define TXT_RADIO_3 0x3f1 // Come in. +#define TXT_RADIO_4 0x3f2 // Over and out. +#define TXT_RADIO_5 0x3f3 // Requesting transport. +#define TXT_RADIO_6 0x3f4 // I've got a delivery for +#define TXT_RADIO_7 0x3f5 // I'm performing load/unload +#define TXT_RADIO_8 0x3f6 // I'm clear. +#define TXT_RADIO_9 0x3f7 // You are clear to unload. +#define TXT_RADIO_10 0x3f8 // Am unable to comply. +#define TXT_RADIO_11 0x3f9 // I'm starting construction +#define TXT_RADIO_12 0x3fa // I've finished construction. +#define TXT_RADIO_13 0x3fb // Oops, sorry. I might have +#define TXT_RADIO_14 0x3fc // I'm full. May I unload at +#define TXT_RADIO_15 0x3fd // Are you a refinery and are +#define TXT_RADIO_16 0x3fe // Take this kick! You... +#define TXT_RADIO_17 0x3ff // Take this punch! You... diff --git a/TIBERIANDAWN/DEFINES.H b/TIBERIANDAWN/DEFINES.H new file mode 100644 index 000000000..db2d5b309 --- /dev/null +++ b/TIBERIANDAWN/DEFINES.H @@ -0,0 +1,2961 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\defines.h_v 2.19 16 Oct 1995 16:44:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEFINES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DEFINES_H +#define DEFINES_H + + +//#define PETROGLYPH_EXAMPLE_MOD + +/********************************************************************** +** If defined, then the advanced balancing features will be enabled +** for this version. +*/ +//#define ADVANCED +#define PATCH // Super patch (1.17?) + + +/********************************************************************** +** The demo version of C&C will be built if the following define +** is active. +*/ +//#define DEMO + + +/* +** Use AI pulled in from Red Alert. ST - 7/26/2019 11:42AM +*/ +#define USE_RA_AI + + +/********************************************************************** +** Define this to allow play of the bonus missions for the Gateway +** bundle deal. +*/ +#define BONUS_MISSIONS + + +/********************************************************************** +** Handle expansion scnearios as a set of single missions with all +** necessary information self contained within the mission file. +*/ +#ifndef DEMO +#define NEWMENU +#endif + + +/********************************************************************** +** If the scenario editor to to be active in this build then uncomment +** the following #define line. +*/ +//#define SCENARIO_EDITOR + + +/********************************************************************** +** This define enables the full set of cheat keys and special +** command line options. +*/ +#define CHEAT_KEYS + + +/********************************************************************** +** If this is defined, the special Virgin limited cheat keys +** are enabled. This allows the "cheat" parameter and then only +** allows the ALT-W to win the mission. +*/ +//#define VIRGIN_CHEAT_KEYS + + +/********************************************************************** +** Optional parameter control for special options. +*/ +//#define PARM_6PLAYER 0x5D9F6F24 // "6" +#define PARM_6PLAYER 0x9CAFC93B // Alternate 6 player keyphrase. + +/* +** Enable the set of limited cheat key options. +*/ +#ifdef VIRGIN_CHEAT_KEYS +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif + +/* +** Enable the full set of cheat key options. +*/ +#ifdef CHEAT_KEYS +#ifndef PARM_PLAYTEST +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif +#define PARM_CHEATDAVID 0xBE79088C // Cheat keys for David Dettmer +#define PARM_CHEATERIK 0x9F38A19D // Cheat keys for Erik Yeo +#define PARM_EDITORERIK 0xC2AA509B // Map editor for Erik Yeo +#define PARM_CHEATPHIL 0x39D01821 // Cheat keys for Phil Gorrow +#define PARM_CHEATJOE 0xABDD0362 // Cheat keys for Joe Bostic +#define PARM_CHEATBILL 0xB5B63531 // Cheat keys for Bill Randolph +#define PARM_CHEAT_STEVET 0x2E7FE493 // Cheat keys for Steve Tall +#define PARM_EDITORBILL 0x7E7C4CCA // "-EDITOR" +#define PARM_CHEATMIKE 0x00532693 // Mike Lightner +#define PARM_CHEATADAM 0xDFABC23A // Adam Isgreen +#endif + +//#define PARM_CHEAT 0x6F4BE7CA // "CHEAT" +//#define PARM_EDITOR 0x7E7C4CCA // "-EDITOR" + +#define PARM_EASY 0x59E975CE // "EASY" Enables easy mode. +#define PARM_HARD 0xACFE9D13 // "HARD" Enables hard mode. + +#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL" +#define PARM_TRUENAME 0xB1A34435 // Enables true object names. +#define PARM_3POINT 0x03552894 // Enable three point turns. +#define PARM_SCORE 0x7FDE2C33 // Enables alternate themes. +#define PARM_COMBAT 0xDC57C4B2 // Gives combat advantage to attacker. +#define PARM_TREETARGET 0x00AB6BEF // Allows targeting of trees without key. +#define PARM_BIB 0xF7867BF0 // Disables building bibs. +#define PARM_MCV 0x104DF10F // MCV undeploys rather than sells. +#define PARM_HELIPAD 0x53EBECBC // Helipad can be purchased separately from helicopter. +#define PARM_IQ 0x9E3881B8 // Smart self defense logic enable. +#define PARM_SQUISH 0x4EA2FBDF // Squish images for infantry bodies. +#define PARM_HUMAN 0xACB58F61 // Human generated sound effects. +#define PARM_SCROLLING 0xC084AE82 // Restricts scrolling over the tabs. +//#define PARM_SPECIAL 0xD18129F6 // Enables special mode. +//#define PARM_SPECIAL 0x2E84E394 // #1 +//#define PARM_SPECIAL 0x63CE7584 // #2 +//#define PARM_SPECIAL 0x85F110A5 // #3 +///#define PARM_SPECIAL 0x7F65F13C // #4 +//#define PARM_SPECIAL 0x431F5F61 // #5 +#define PARM_SPECIAL 0x11CA05BB // #6 funpark +//#define PARM_SPECIAL 0xE0F651B9 // #7 +//#define PARM_SPECIAL 0x10B9683D // #8 +//#define PARM_SPECIAL 0xEE1CD37D // #9 + + +/********************************************************************** +** Defines for verifying free disk space +*/ +#define INIT_FREE_DISK_SPACE 1024*4096 //8388608 +#define SAVE_GAME_DISK_SPACE INIT_FREE_DISK_SPACE // (INIT_FREE_DISK_SPACE - (1024*4096)) +//#define SAVE_GAME_DISK_SPACE 100000 + + +/********************************************************************** +** This is the credit threshold that the computer's money must exceed +** in order for structure repair to commence. +*/ +#define REPAIR_THRESHHOLD 1000 + + +//#define GERMAN 1 +//#define FRENCH 1 +//#define JAPANESE 1 + +#define FOREIGN_VERSION_NUMBER 6 + +// +// typedef enums with -1 will show this warning, even when the type of the enum is signed. It's a compiler bug, apparently +// ST - 1/8/2019 9:23AM +// +#pragma warning (push) +#pragma warning (disable:4341) + +/********************************************************************** +** These enumerations are used to implement RTTI. +*/ +typedef enum RTTIType : unsigned char { + RTTI_NONE=0, + RTTI_INFANTRY, + RTTI_INFANTRYTYPE, + RTTI_UNIT, + RTTI_UNITTYPE, + RTTI_AIRCRAFT, + RTTI_AIRCRAFTTYPE, + RTTI_BUILDING, + RTTI_BUILDINGTYPE, + + RTTI_TERRAIN, + RTTI_ABSTRACTTYPE, + RTTI_ANIM, + RTTI_ANIMTYPE, + RTTI_BULLET, + RTTI_BULLETTYPE, + RTTI_OVERLAY, + RTTI_OVERLAYTYPE, + RTTI_SMUDGE, + RTTI_SMUDGETYPE, + RTTI_TEAM, + RTTI_TEMPLATE, + RTTI_TEMPLATETYPE, + RTTI_TERRAINTYPE, + RTTI_OBJECT, + RTTI_SPECIAL +} RTTIType; + + +/********************************************************************** +** These are the difficulty settings of the game. +*/ +typedef enum DiffType : unsigned char { + DIFF_EASY, + DIFF_NORMAL, + DIFF_HARD, + + DIFF_COUNT, + DIFF_FIRST = 0 +} DiffType; + + +/********************************************************************** +** This is the size of the speech buffer. This value should be as large +** as the largest speech sample, plus a few bytes for overhead +** (16 bytes is sufficient). +*/ +#define SPEECH_BUFFER_SIZE 50000L + + +/********************************************************************** +** This is the size of the shape buffer. This buffer is used as a staging +** buffer for the shape drawing technology. It MUST be as big as the +** largest shape (uncompressed) that will be drawn. If this value is +** changed, be sure to update the makefile and rebuild all of the shape +** data files. +*/ +#define SHAPE_BUFFER_SIZE 131072L + +// Use this to allow keep track of versions as they affect saved games. +#define VERSION_NUMBER 1 +#define RELEASE_NUMBER 01 + +#define FAME_FILE_NAME "HALLFAME.DAT" + + +/********************************************************************** +** Map controls. The map is composed of square elements called 'cells'. +** All larger elements are build upon these. +*/ + +// Size of the map in cells. The width of the map must be a power +// of two. This is accomplished by setting the width by the number of +// bits it occupies. The number of meta-cells will be a subset of the +// cell width. +#define MAP_CELL_MAX_X_BITS 6 +#define MAP_CELL_MAX_Y_BITS 6 +#define MAP_CELL_X_MASK (~(~0 << MAP_CELL_MAX_X_BITS)) +//#define MAP_CELL_Y_MASK ((~(~0 << MAP_CELL_MAX_Y_BITS)) << MAP_CELL_MAX_Y_BITS) + +// Size of the map in cells. +#define MAP_CELL_W (1<(static_cast(a) | static_cast(b));} + +inline ThreatType operator&(ThreatType a, ThreatType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ThreatType operator~(ThreatType a) +{return static_cast(~static_cast(a));} + + +#define THREAT_GROUND (THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_INFANTRY) + + +/********************************************************************** +** These return values are used when determine if firing is legal. +** By examining this value it can be determined what should be done +** to fix the reason why firing wasn't allowed. +*/ +typedef enum FireErrorType : unsigned char { + FIRE_OK, // Weapon is allowed to fire. + FIRE_AMMO, // No ammo available to fire? + FIRE_FACING, // Not correctly facing target? + FIRE_REARM, // It is busy rearming? + FIRE_ROTATING, // Is it in process of rotating? + FIRE_ILLEGAL, // Is it targeting something illegal? + FIRE_CANT, // Is this unit one that cannot fire anything? + FIRE_MOVING, // Is it moving and not allowed to fire while moving? + FIRE_RANGE, // Is the target out of range? + FIRE_CLOAKED, // Is the shooter currently cloaked? + FIRE_BUSY // Is shooter currently doing something else? +} FireErrorType; + + +/********************************************************************** +** If an object can cloak, then it will be in one of these states. +** For objects that cannot cloak, they will always be in the +** UNCLOAKED state. This state controls how the obect transitions between +** cloaked and uncloaked conditions. +*/ +typedef enum CloakType : unsigned char { + UNCLOAKED, // Completely visible (normal state). + CLOAKING, // In process of claoking. + CLOAKED, // Completely cloaked (invisible). + UNCLOAKING // In process of uncloaking. +} CloakType; + + +/********************************************************************** +** For units that are cloaking, these value specify the visual character +** of the object. +*/ +typedef enum VisualType : unsigned char { + VISUAL_NORMAL, // Completely visible -- normal. + VISUAL_INDISTINCT, // The edges shimmer and become indistinct. + VISUAL_DARKEN, // Color and texture is muted along with shimmering. + VISUAL_SHADOWY, // Body is translucent in addition to shimmering. + VISUAL_RIPPLE, // Just a ripple (true predator effect). + VISUAL_HIDDEN, // Nothing at all is visible. +} VisualType; + + +/********************************************************************** +** These missions enumerate the various state machines that can apply to +** a game object. Only one of these state machines is active at any one +** time. +*/ +typedef enum MissionType : char { + MISSION_NONE=-1, + + MISSION_SLEEP, // Do nothing whatsoever. + MISSION_ATTACK, // Attack nearest enemy. + MISSION_MOVE, // Guard location or unit. + MISSION_RETREAT, // Return home for R & R. + MISSION_GUARD, // Stay still. + MISSION_STICKY, // Stay still -- never recruit. + MISSION_ENTER, // Move into object cooperatively. + MISSION_CAPTURE, // Move into in order to capture. + MISSION_HARVEST, // Hunt for and collect nearby Tiberium. + MISSION_GUARD_AREA, // Active guard of area. + MISSION_RETURN, // Head back to refinery. + MISSION_STOP, // Sit still. + MISSION_AMBUSH, // Wait until discovered. + MISSION_HUNT, // Active search and destroy. + MISSION_TIMED_HUNT, // Wait a while, then go into HUNT (multiplayer AI) + MISSION_UNLOAD, // Search for and deliver cargo. + MISSION_SABOTAGE, // Move into in order to destroy. + MISSION_CONSTRUCTION, // Building buildup operation. + MISSION_DECONSTRUCTION, // Building builddown operation. + MISSION_REPAIR, // Repair process mission. + MISSION_RESCUE, + MISSION_MISSILE, + + MISSION_COUNT, + MISSION_FIRST=0 +} MissionType; + + +/********************************************************************** +** These are the enumerated animation sequences that a building may +** be processing. These serve to control the way that a building +** appears. +*/ +typedef enum BStateType : char { + BSTATE_NONE=-1, + BSTATE_CONSTRUCTION, // Construction animation. + BSTATE_IDLE, // Idle animation. + BSTATE_ACTIVE, // Animation when building is "doing its thing". + BSTATE_FULL, // Special alternate active state. + BSTATE_AUX1, // Auxiliary animation. + BSTATE_AUX2, // Auxiliary animation. + + BSTATE_COUNT +} BStateType; + + +/********************************************************************** +** Whenever a unit is selected and a click occurs over another object +** or terrain element, there is some action to initiate. This specifies +** the different types of actions possible. This also controls how the +** mouse cursor looks when "hovering" over the spot that clicking would +** occur at. +*/ +typedef enum ActionType : unsigned char { + ACTION_NONE, // Either undefined action or "do nothing". + ACTION_MOVE, // Can move there or at least try to. + ACTION_NOMOVE, // Special case for movable object, but illegal mouse position. + ACTION_ENTER, // Special case for infantry->APC or vehicle->Repair facility. + ACTION_SELF, // Self select special case. + ACTION_ATTACK, // Can attack or fire upon it in some fashion. + ACTION_HARVEST, // Special harvest mode. + ACTION_SELECT, // Would change selection to specified object. + ACTION_TOGGLE_SELECT,// Toggles select state of the object. + ACTION_CAPTURE, // The unit will try to capture the object. + ACTION_REPAIR, // The target object should be repaired. + ACTION_SELL, // The target building should be sold back. + ACTION_SELL_UNIT, // The target unit should be sold back. + ACTION_NO_SELL, // No sell or no repair. + ACTION_NO_REPAIR, // No sell or no repair. + ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object. + ACTION_ION, // That target object should be blasted. + ACTION_NUKE_BOMB, // That target object should be blasted. + ACTION_AIR_STRIKE, // That target object should be blasted. + ACTION_GUARD_AREA, // Guard the area/object clicked on. + ACTION_TOGGLE_PRIMARY, // Toggle the primary status of the factory. + ACTION_NO_DEPLOY, + + ACTION_COUNT +} ActionType; + + +/********************************************************************** +** When a unit gets damaged, the result of the damage is returned as +** this type. It can range from no damage taken to complete destruction. +*/ +typedef enum ResultType : unsigned char { + RESULT_NONE, // No damage was taken by the target. + RESULT_LIGHT, // Some damage was taken, but no state change occurred. + RESULT_HALF, // Damaged to below half strength (only returned on transition). + RESULT_MAJOR, // Damaged down to 1 hit point. + RESULT_DESTROYED, // Damaged to complete destruction. +} ResultType; + + +/********************************************************************** +** These are the special concrete control defines. They enumerate the +** sequence order of the concrete icons in the concrete art file. +*/ +// DEBUG === convert this to be zero based so that a nulled cell is the +// default cell. +enum ConcreteEnum : char { + C_NONE=-1, + C_LEFT=0, + C_RIGHT=1, + C_RIGHT_UPDOWN=2, + C_LEFT_UPDOWN=3, + C_UP_RIGHT=4, + C_UP_LEFT=5, + C_DOWN_RIGHT=6, + C_DOWN_LEFT=7, + C_RIGHT_DOWN=8, + C_LEFT_DOWN=9, + C_RIGHT_UP=10, + C_LEFT_UP=11, + C_UPDOWN_RIGHT=12, + C_UPDOWN_LEFT=13 +}; + + +/********************************************************************** +** Units that move can move at different speeds. These enumerate the +** different speeds that a unit can move. +*/ +typedef enum MPHType: unsigned char { + MPH_IMMOBILE=0, + MPH_VERY_SLOW=5, + MPH_KINDA_SLOW=6, + MPH_SLOW=8, + MPH_SLOW_ISH=10, + MPH_MEDIUM_SLOW=12, + MPH_MEDIUM=18, + MPH_MEDIUM_FAST=30, + MPH_MEDIUM_FASTER=35, + MPH_FAST=40, + MPH_ROCKET=60, + MPH_VERY_FAST=100, + MPH_LIGHT_SPEED=255 +} MPHType; + + +/********************************************************************** +** General audio volume is enumerated by these identifiers. Since small +** volume variations are usually unnoticable when specifying the volume +** to play a sample, this enumeration list creates more readable code. +*/ +typedef enum VolType : unsigned char +{ + VOL_OFF=0, + VOL_0=VOL_OFF, + VOL_1=0x19, + VOL_2=0x32, + VOL_3=0x4C, + VOL_4=0x66, + VOL_5=0x80, + VOL_6=0x9A, + VOL_7=0xB4, + VOL_8=0xCC, + VOL_9=0xE6, + VOL_10=0xFF, + VOL_FULL=VOL_10 +} VolType; + + +/********************************************************************** +** The houses that can be played are listed here. Each has their own +** personality and strengths. +*/ +typedef enum HousesType : char { + HOUSE_NONE=-1, + HOUSE_GOOD, // Global Defense Initiative + HOUSE_BAD, // Brotherhood of Nod + HOUSE_NEUTRAL, // Civilians + HOUSE_JP, // Disaster Containment Team + HOUSE_MULTI1, // Multi-Player house #1 + HOUSE_MULTI2, // Multi-Player house #2 + HOUSE_MULTI3, // Multi-Player house #3 + HOUSE_MULTI4, // Multi-Player house #4 + HOUSE_MULTI5, // Multi-Player house #5 + HOUSE_MULTI6, // Multi-Player house #6 + + HOUSE_COUNT, + HOUSE_FIRST=HOUSE_GOOD +} HousesType; + +//inline HousesType operator++(HousesType &, int); + +#define HOUSEF_GOOD (1< 2 players. +*/ +typedef enum ScenarioPlayerEnum : char +{ + SCEN_PLAYER_NONE = -1, + SCEN_PLAYER_GDI, + SCEN_PLAYER_NOD, + SCEN_PLAYER_JP, + SCEN_PLAYER_2PLAYER, + SCEN_PLAYER_MPLAYER, + SCEN_PLAYER_COUNT, + SCEN_PLAYER_FIRST = 0, +} ScenarioPlayerType; + +//inline ScenarioPlayerType operator++(ScenarioPlayerType &, int); + + +/********************************************************************** +** These are the directional parameters for a scenario. +*/ +typedef enum ScenarioDirEnum : char +{ + SCEN_DIR_NONE = -1, + SCEN_DIR_EAST, + SCEN_DIR_WEST, + SCEN_DIR_COUNT, + SCEN_DIR_FIRST = 0, +} ScenarioDirType; + +//inline ScenarioDirType operator++(ScenarioDirType &, int); + + +/********************************************************************** +** These are the random variations of a scenario. +*/ +typedef enum ScenarioVarEnum : char +{ + SCEN_VAR_NONE = -1, + SCEN_VAR_A, + SCEN_VAR_B, + SCEN_VAR_C, + SCEN_VAR_D, + SCEN_VAR_COUNT, // comes before the Lose value! + SCEN_VAR_LOSE, + SCEN_VAR_FIRST = 0, +} ScenarioVarType; + +//inline ScenarioVarType operator++(ScenarioVarType &, int); + + +/********************************************************************** +** The objects to be drawn on the map are grouped into layers. These +** enumerated values specify those layers. The ground layer is sorted +** from back to front. +*/ +typedef enum LayerType : char { + LAYER_NONE=-1, + LAYER_GROUND, // Touching the ground type object (units & buildings). + LAYER_AIR, // Flying above the ground (explosions & flames). + LAYER_TOP, // Topmost layer (aircraft & bullets). + + LAYER_COUNT, + LAYER_FIRST=0 +} LayerType; + +//inline LayerType operator++(LayerType &, int); + + +/********************************************************************** +** This enumerates the various bullet types. These types specify bullet's +** visual and explosive characteristics. +*/ +typedef enum BulletType : char { + BULLET_NONE=-1, + BULLET_SNIPER, // Sniper bullet. + BULLET_BULLET, // Small arms + BULLET_APDS, // Armor piercing projectile. + BULLET_HE, // High explosive shell. + BULLET_SSM, // Surface to surface small missile type. + BULLET_SSM2, // MLRS missile. + BULLET_SAM, // Fast homing anti-aircraft missile. + BULLET_TOW, // TOW anti-vehicle short range missile. + BULLET_FLAME, // Flame thrower flame. + BULLET_CHEMSPRAY, // Chemical weapon spray. + BULLET_NAPALM, // Napalm bomblet. + BULLET_GRENADE, // Hand tossed grenade. + BULLET_LASER, // Laser beam from obelisk + BULLET_NUKE_UP, // Nuclear Missile on its way down + BULLET_NUKE_DOWN, // Nuclear Missile on its way up + BULLET_HONEST_JOHN, // SSM with napalm warhead. + BULLET_SPREADFIRE, // Chain gun bullets. + BULLET_HEADBUTT, // Stegosaurus, Triceratops head butt + BULLET_TREXBITE, // Tyrannosaurus Rex's bite - especially bad for infantry + +#ifdef PETROGLYPH_EXAMPLE_MOD + BULLET_NUKE_LOB, // Nuke projectile +#endif //PETROGLYPH_EXAMPLE_MOD + + BULLET_COUNT, + BULLET_FIRST=0 +} BulletType; + +//inline BulletType operator++(BulletType &, int); + + +/********************************************************************** +** All game buildings (structures) are enumerated here. This includes +** civilian structures as well. +*/ +typedef enum StructType : char { + STRUCT_NONE=-1, + STRUCT_WEAP, + STRUCT_GTOWER, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_RADAR, + STRUCT_TURRET, + STRUCT_CONST, + STRUCT_REFINERY, + STRUCT_STORAGE, + STRUCT_HELIPAD, + STRUCT_SAM, + STRUCT_AIRSTRIP, + STRUCT_POWER, + STRUCT_ADVANCED_POWER, + STRUCT_HOSPITAL, + STRUCT_BARRACKS, + STRUCT_TANKER, + STRUCT_REPAIR, + STRUCT_BIO_LAB, + STRUCT_HAND, + STRUCT_TEMPLE, + STRUCT_EYE, + STRUCT_MISSION, + + /* + ** All buildings that are never used as a prerequisite + ** for construction, follow this point. Typically, this is + ** limited to civilian structures. + */ + STRUCT_V01, + STRUCT_V02, + STRUCT_V03, + STRUCT_V04, + STRUCT_V05, + STRUCT_V06, + STRUCT_V07, + STRUCT_V08, + STRUCT_V09, + STRUCT_V10, + STRUCT_V11, + STRUCT_V12, + STRUCT_V13, + STRUCT_V14, + STRUCT_V15, + STRUCT_V16, + STRUCT_V17, + STRUCT_V18, + STRUCT_PUMP, + STRUCT_V20, + STRUCT_V21, + STRUCT_V22, + STRUCT_V23, + STRUCT_V24, + STRUCT_V25, + STRUCT_V26, + STRUCT_V27, + STRUCT_V28, + STRUCT_V29, + STRUCT_V30, + STRUCT_V31, + STRUCT_V32, + STRUCT_V33, + STRUCT_V34, + STRUCT_V35, + STRUCT_V36, + STRUCT_V37, +#ifdef OBSOLETE + STRUCT_ROAD, +#endif + STRUCT_SANDBAG_WALL, + STRUCT_CYCLONE_WALL, + STRUCT_BRICK_WALL, + STRUCT_BARBWIRE_WALL, + STRUCT_WOOD_WALL, + + STRUCT_COUNT, + STRUCT_FIRST=0 +} StructType; + +//inline StructType operator++(StructType &, int); + +#define STRUCTF_NONE 0L +#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER) +#define STRUCTF_REPAIR (1L << STRUCT_REPAIR) +#define STRUCTF_EYE (1L << STRUCT_EYE) +#define STRUCTF_TEMPLE (1L << STRUCT_TEMPLE) +#define STRUCTF_HAND (1L << STRUCT_HAND) +#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB) +#define STRUCTF_OBELISK (1L << STRUCT_OBELISK) +#define STRUCTF_ATOWER (1L << STRUCT_ATOWER) +#define STRUCTF_WEAP (1L << STRUCT_WEAP) +#define STRUCTF_GTOWER (1L << STRUCT_GTOWER) +#define STRUCTF_RADAR (1L << STRUCT_RADAR) +#define STRUCTF_TURRET (1L << STRUCT_TURRET) +#define STRUCTF_CIV1 (1L << STRUCT_CIV1) +#define STRUCTF_CIV2 (1L << STRUCT_CIV2) +#define STRUCTF_CIV3 (1L << STRUCT_CIV3) +#define STRUCTF_CONST (1L << STRUCT_CONST) +#define STRUCTF_REFINERY (1L << STRUCT_REFINERY) +#define STRUCTF_STORAGE (1L << STRUCT_STORAGE) +#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD) +#define STRUCTF_SAM (1L << STRUCT_SAM) +#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP) +#define STRUCTF_POWER (1L << STRUCT_POWER) +#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL) +#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS) +#define STRUCTF_TANKER (1L << STRUCT_TANKER) +#define STRUCTF_MISSION (1L << STRUCT_MISSION) + + +/********************************************************************** +** The overlays are enumerated here. An overlay functions similarly to +** a transparent icon. It is placed over the terrain but usually falls +** "under" buildings, trees, and units. +*/ +typedef enum OverlayType : char { + OVERLAY_NONE=-1, + OVERLAY_CONCRETE, // Concrete. + OVERLAY_SANDBAG_WALL, // Piled sandbags. + OVERLAY_CYCLONE_WALL, // Chain-link fence. + OVERLAY_BRICK_WALL, // Solid concrete wall. + OVERLAY_BARBWIRE_WALL, // Barbed-wire wall. + OVERLAY_WOOD_WALL, // Wooden fence. + OVERLAY_TIBERIUM1, // Tiberium patch. + OVERLAY_TIBERIUM2, // Tiberium patch. + OVERLAY_TIBERIUM3, // Tiberium patch. + OVERLAY_TIBERIUM4, // Tiberium patch. + OVERLAY_TIBERIUM5, // Tiberium patch. + OVERLAY_TIBERIUM6, // Tiberium patch. + OVERLAY_TIBERIUM7, // Tiberium patch. + OVERLAY_TIBERIUM8, // Tiberium patch. + OVERLAY_TIBERIUM9, // Tiberium patch. + OVERLAY_TIBERIUM10, // Tiberium patch. + OVERLAY_TIBERIUM11, // Tiberium patch. + OVERLAY_TIBERIUM12, // Tiberium patch. + OVERLAY_ROAD, // Road/concrete piece. + OVERLAY_SQUISH, // Squish mark for overran infantry. + OVERLAY_V12, // Haystacks + OVERLAY_V13, // Haystack + OVERLAY_V14, // Wheat field + OVERLAY_V15, // Fallow field + OVERLAY_V16, // Corn field + OVERLAY_V17, // Celery field + OVERLAY_V18, // Potato field + OVERLAY_FLAG_SPOT, // Flag start location. + OVERLAY_WOOD_CRATE, // Wooden goodie crate. + OVERLAY_STEEL_CRATE, // Steel goodie crate. + + OVERLAY_COUNT, + OVERLAY_FIRST=0 +} OverlayType; + +//inline OverlayType operator++(OverlayType &, int); + + +/********************************************************************** +** This specifies the infantry in the game. The "E" designation is +** similar to the army classification of enlisted soldiers. +*/ +typedef enum InfantryType : char { + INFANTRY_NONE=-1, + INFANTRY_E1, // Mini-gun armed. + INFANTRY_E2, // Grenade thrower. + INFANTRY_E3, // Rocket launcher. + INFANTRY_E4, // Flame thrower equipped. + INFANTRY_E5, // Chemical thrower equipped. + INFANTRY_E7, // Engineer. + INFANTRY_RAMBO, // Commando. + + INFANTRY_C1, // Civilian + INFANTRY_C2, // Civilian + INFANTRY_C3, // Civilian + INFANTRY_C4, // Civilian + INFANTRY_C5, // Civilian + INFANTRY_C6, // Civilian + INFANTRY_C7, // Civilian + INFANTRY_C8, // Civilian + INFANTRY_C9, // Civilian + INFANTRY_C10, // Nikumba + INFANTRY_MOEBIUS, // Dr. Moebius + INFANTRY_DELPHI, // Agent "Delphi" + INFANTRY_CHAN, // Dr. Chan + + INFANTRY_COUNT, + INFANTRY_FIRST=0 +} InfantryType; + +//inline InfantryType operator++(InfantryType &, int); + + +/********************************************************************** +** The game units are enumerated here. These include not only traditional +** vehicles, but also hovercraft and gunboats. +*/ +typedef enum UnitType : char{ + UNIT_NONE=-1, + UNIT_HTANK, // Heavy tank (Mammoth). + UNIT_MTANK, // Medium tank (M1). + UNIT_LTANK, // Light tank ('Bradly'). + UNIT_STANK, // Stealth tank (Romulan). + UNIT_FTANK, // Flame thrower tank. + UNIT_VICE, // Visceroid + UNIT_APC, // APC. + UNIT_MLRS, // MLRS rocket launcher. + UNIT_JEEP, // 4x4 jeep replacement. + UNIT_BUGGY, // Rat patrol dune buggy type. + UNIT_HARVESTER, // Resource gathering vehicle. + UNIT_ARTY, // Artillery unit. + UNIT_MSAM, // Anti-Aircraft vehicle. + UNIT_HOVER, // Hovercraft. + UNIT_MHQ, // Mobile Head Quarters. + UNIT_GUNBOAT, // Gunboat + UNIT_MCV, // Mobile construction vehicle. + UNIT_BIKE, // Nod recon motor-bike. + UNIT_TRIC, // Triceratops + UNIT_TREX, // Tyranosaurus Rex + UNIT_RAPT, // Velociraptor + UNIT_STEG, // Stegasaurus + +#ifdef PETROGLYPH_EXAMPLE_MOD + UNIT_NUKE_TANK, // Mammoth with a nuke +#endif + + UNIT_COUNT, + UNIT_FIRST=0 +} UnitType; + +//inline UnitType operator++(UnitType &, int); + +#define UNITF_HTANK (1L<(static_cast(a) | static_cast(b));} + +inline TextPrintType operator&(TextPrintType a, TextPrintType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline TextPrintType operator~(TextPrintType a) +{return static_cast(~static_cast(a));} + + +/********************************************************************** +** These control the maximum number of objects in the game. Make sure that these +** maximums never exceed the maximum value for the "ID" element in the +** object class. +*/ +#define AIRCRAFT_MAX 100 // Lasts for minutes. +#define ANIM_MAX 200 // Lasts only a few seconds. +#define BUILDING_MAX 500 // Lasts for hours. +#define BULLET_MAX 50 // Lasts several seconds. +#define FACTORY_MAX 32 // Lasts a few minutes. +#define HOUSE_MAX 12 // Lasts entire scenario. +#define INFANTRY_MAX 500 // Lasts for minutes. +#define OVERLAY_MAX 1 // Very transitory. +#define REINFORCEMENT_MAX 50 // Maximum number of reinforcements. +#define SMUDGE_MAX 1 // Very transitory. +#define TEAM_MAX 60 // Lasts forever. +#define TEMPLATE_MAX 1 // Very transitory. +#define TERRAIN_MAX 500 // Lasts for hours or eternity. +#define TRIGGER_MAX 80 // Lasts forever. +#define UNIT_MAX 500 // Lasts for minutes. +#define TEAMTYPE_MAX 60 // Lasts forever. + +// Save filename description. +#define DESCRIP_MAX 44 // 40 chars + CR + LF + CTRL-Z + NULL + +#define MAX_ENTRY_SIZE 15 + +#define OBUTTON_HEIGHT 9 // Is defined in mapedit.h, need for buttons + +#define CONQUER_PATH_MAX 9 // Number of cells to look ahead for movement. + +#define EACH_UNIT_MAX (UNIT_MAX/4) // Default maximum any one player can have. +#define EACH_BUILDING_MAX (BUILDING_MAX/4) // Default maximum any one player can build. + + +/********************************************************************** +** Terrain can be of these different classes. At any point in the game +** a particular piece of ground must fall under one of these classifications. +** This is true, even if it is undergoing a temporary transition. +*/ +typedef enum LandType : unsigned char { + LAND_CLEAR, // "Clear" terrain. + LAND_ROAD, // Road terrain. + LAND_WATER, // Water. + LAND_ROCK, // Impassable rock. + LAND_WALL, // Wall (blocks movement). + LAND_TIBERIUM, // Tiberium field. + LAND_BEACH, // Beach terrain. + + LAND_COUNT +} LandType; + + +/********************************************************************** +** The theaters of operation are as follows. +*/ +typedef enum TheaterType : char { + THEATER_NONE=-1, + THEATER_DESERT, + THEATER_JUNGLE, + THEATER_TEMPERATE, + THEATER_WINTER, + + THEATER_COUNT, + THEATER_FIRST=0 +} TheaterType; + +//inline TheaterType operator++(TheaterType &, int); + +#define THEATERF_DESERT (1<id) + + + + +/* +** +** Imports from Red Alert for AI. ST - 7/16/2019 11:44AM +** +** +** +*/ +#ifdef USE_RA_AI + + +/********************************************************************** +** A base is broken up into several zones. This type enumerates the +** various zones. +*/ +typedef enum ZoneType : char { + ZONE_CORE, // Center of base. + ZONE_NORTH, // North section. + ZONE_EAST, // East section. + ZONE_SOUTH, // South section. + ZONE_WEST, // West section. + + ZONE_COUNT, + ZONE_FIRST=0, + ZONE_NONE=-1 +} ZoneType; + + +/********************************************************************** +** The map is prescanned to mark of movement zones according to certain +** movement characteristics. This enum specifies those characteristics +** and movement zones kept track of. +*/ +typedef enum MZoneType : unsigned char { + MZONE_NORMAL, // Normal terrestrial objects (can't crush walls). + MZONE_CRUSHER, // Can crush crushable wall types. + MZONE_DESTROYER, // Can destroy walls. + MZONE_WATER, // Water based objects. + + MZONE_COUNT, + MZONE_FIRST=0 +} MZoneType; + +#define MZONEF_NORMAL (1<Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1,CC_GREEN_SHADOW); + LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1],CC_LIGHT_GREEN); + + /* + ** Restore the mouse. + */ + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * DirType dial is pointing to * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +DirType Dial8Class::Get_Direction(void) const +{ + return(Direction); +} + + +/*************************************************************************** + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * * + * INPUT: * + * DirType to set dial to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void Dial8Class::Set_Direction(DirType dir) +{ + Direction = dir; + Facing = Dir_Facing(Direction); + OldFacing = Facing; + Flag_To_Redraw(); +} diff --git a/TIBERIANDAWN/DIAL8.H b/TIBERIANDAWN/DIAL8.H new file mode 100644 index 000000000..676b154fa --- /dev/null +++ b/TIBERIANDAWN/DIAL8.H @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\dial8.h_v 2.18 16 Oct 1995 16:47:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DIAL8_H +#define DIAL8_H + +class Dial8Class : public ControlClass +{ + public: + /* + ** Constructor/Destructor + */ + Dial8Class(int id, int x, int y, int w, int h, DirType dir); + + /* + ** Get/Set the direction the dial is currently pointing + */ + DirType Get_Direction(void) const; + void Set_Direction(DirType dir); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + protected: + /* + ** Overloaded event processing routine + */ + virtual int Action(unsigned flags, KeyNumType &key); + + private: + int FaceX; // x-coord of center of face + int FaceY; // y-coord of center of face + int FacePoint[8][2]; // coords of the little dial decorations + int FaceLine[8][2]; // coords for drawing the dial hand + DirType Direction; // 0-255 numerical direction of dial + FacingType Facing; // numerical facing direction of dial (0 - 7) + FacingType OldFacing; // previous Facing value + +}; + +#endif + diff --git a/TIBERIANDAWN/DIALOG.CPP b/TIBERIANDAWN/DIALOG.CPP new file mode 100644 index 000000000..14ef93721 --- /dev/null +++ b/TIBERIANDAWN/DIALOG.CPP @@ -0,0 +1,787 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\dialog.cpv 2.17 16 Oct 1995 16:51:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIALOG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clip_Text_Print -- Prints text with clipping and support. * + * Dialog_Box -- draws a dialog background box * + * Display_Place_Building -- Displays the "place building" dialog box. * + * Display_Select_Target -- Displays the "choose target" prompt. * + * Display_Status -- Display the player scenario status box. * + * Draw_Box -- Displays a highlighted box. * + * Fancy_Text_Print -- Prints text with a drop shadow. * + * Redraw_Needed -- Determine if sidebar needs to be redrawn. * + * Render_Bar_Graph -- Renders a specified bargraph. * + * Simple_Text_Print -- Prints text with a drop shadow. * + * Window_Box -- Draws a fancy box over the specified window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Dialog_Box -- draws a dialog background box * + * * + * INPUT: * + * x,y,w,h the usual * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/26/1995 BR : Created. * + *=============================================================================================*/ +void Dialog_Box(int x, int y, int w, int h) +{ + Draw_Box( x, y, w, h, BOXSTYLE_GREEN_BORDER, true); +} + + +/*********************************************************************************************** + * Draw_Box -- Displays a highlighted box. * + * * + * This will draw a highlighted box to the logicpage. It can * + * optionally fill the box with a color as well. This is a low level * + * function and thus, it doesn't do any graphic mode color adjustments. * + * * + * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). * + * * + * w,h -- Width and height of box (in pixels). * + * * + * up -- Is the box rendered in the "up" stated? * + * * + * filled-- Is the box to be filled. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 JLB : Created. * + * 05/30/1992 JLB : Embedded color codes. * + * 07/31/1992 JLB : Depressed option added. * + *=============================================================================================*/ +extern void CC_Texture_Fill (void const *shapefile, int shapenum, int xpos, int ypos, int width, int height); + +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled) +{ + static BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = { + + //Filler, Shadow, Hilite, Corner colors + + { LTGREY, WHITE, DKGREY, LTGREY}, // 0 Button is down. + { LTGREY, DKGREY, WHITE, LTGREY}, // 1 Button is up w/border. + { LTBLUE, BLUE, LTCYAN, LTBLUE}, // 2 Raised blue. + { DKGREY, WHITE, BLACK, DKGREY}, // 3 Button is disabled down. + { DKGREY, BLACK, WHITE, LTGREY}, // 4 Button is disabled up. + { LTGREY, DKGREY, WHITE, LTGREY}, // 5 Button is up w/arrows. + //{ CC_GREEN_BKGD, CC_LIGHT_GREEN, CC_GREEN_SHADOW, CC_GREEN_CORNERS }, // 6 Button is down. + //{ CC_GREEN_BKGD, CC_GREEN_SHADOW, CC_LIGHT_GREEN, CC_GREEN_CORNERS }, // 7 Button is up w/border. + { CC_GREEN_BKGD, 14, 12, 13 }, // 6 Button is down. + { CC_GREEN_BKGD, 12, 14, 13 }, // 7 Button is up w/border. + { DKGREY, WHITE, BLACK, DKGREY}, // 8 Button is disabled down. + { DKGREY, BLACK, LTGREY, DKGREY}, // 9 Button is disabled up. + //{ BLACK, CC_GREEN_BOX, CC_GREEN_BOX, BLACK}, // 10 List box. + //{ BLACK, CC_GREEN_BOX, CC_GREEN_BOX, BLACK}, // 11 Menu box. + { BLACK, 14, 14, BLACK}, // 10 List box. + { BLACK, 14, 14, BLACK}, // 11 Menu box. + }; + + w--; + h--; + BoxStyleType const &style = ButtonColors[up]; + + if (filled) { + if (style.Filler == CC_GREEN_BKGD){ + CC_Texture_Fill (MixFileClass::Retrieve("BTEXTURE.SHP"), InMainLoop, x, y, w, h); + }else{ + LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler); + } + } + + switch ( up ) { + case ( BOXSTYLE_GREEN_BOX ): + LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight); + break; + + case ( BOXSTYLE_GREEN_BORDER ): + LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight); + break; + + default: + LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow); + LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow); + + LogicPage->Draw_Line(x, y, x+w, y, style.Highlight); + LogicPage->Draw_Line(x, y, x, y+h, style.Highlight); + + LogicPage->Put_Pixel(x, y+h, style.Corner); + LogicPage->Put_Pixel(x+w, y, style.Corner); + break; + } +} + + +/*********************************************************************************************** + * Format_Window_String -- Separates a String into Lines. * + * This function will take a long string and break it up into lines * + * which are not longer then the window width. Any character < ' ' is * + * considered a new line marker and will be replaced by a NULL. * + * * + * INPUT: char *String - string to be formated. * + * int maxlinelen - Max length of any line in pixels. * + * * + * OUTPUT: int - number of lines string is. * + * * + * WARNINGS: The string passed in will be modified - NULLs will be put * + * into each position that will be a new line. * + * * + * HISTORY: * + * 03/27/1992 SB : Created. * + * 05/18/1995 JLB : Greatly revised for new font system. * + *=============================================================================================*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + // While the current line is less then the max length... + while (linelen < maxlinelen && *string != '\r' && *string != '\0') { + linelen += Char_Pixel_Width(*string++); + } + + // if the line is to long... + if (linelen >= maxlinelen) { + + /* + ** Back up to an appropriate location to break. + */ + while (*string != ' ' && *string != '\r' && *string != '\0') { + linelen -= Char_Pixel_Width(*string--); + } + + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *string++ = '\r'; + } + } + return(lines); +} + + +/*********************************************************************************************** + * Window_Box -- Draws a fancy box over the specified window. * + * * + * This routine will draw a fancy (shaded) box over the specified * + * window. This is the effect used to give the polished look to * + * screen rectangles without having to use art. * + * * + * INPUT: window -- Specified window to fill and border. * + * * + * style -- The style to render the window. * + * * + * OUTPUT: none * + * * + * WARNINGS: The rendering is done to the LogicPage. * + * * + * HISTORY: * + * 03/03/1992 JLB : Created. * + * 07/31/1992 JLB : Cool raised border effect. * + * 06/08/1994 JLB : Takes appropriate enumeration parameters. * + *=============================================================================================*/ +void Window_Box(WindowNumberType window, BoxStyleEnum style) +{ + int x,y,w,h; // Window dimensions. + int border; // Width of border. + + static int _border[BOXSTYLE_COUNT][2] = { + {0,0}, // 0 Simple beveled edge. + {2,4}, // 1 Wide raised border. + {1,1}, // 2 Thick beveled edge. + {2,1}, // 3 Thin raised border. + {0,0}, // 4 Simple beveled edge. + {20,0}, // 5 Simple beveled edge. + {0,0}, // 6 Simple beveled edge. + {2,4}, // 7 Wide raised border. + {0,0}, // 8 Simple beveled edge. + {20,0}, // 9 Simple beveled edge. + {0,1} // 10 Simple 1 pixel box. + }; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; + + /* + ** If it is to be rendered to the seenpage, then + ** hide the mouse. + */ + if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x,y,x+w,y+h); + + Draw_Box(x, y, w, h, style, true); + border = _border[style][1]; + + /* + ** Draw the second border if requested. + */ + if (border) { + Draw_Box(x+border, y+border, w-(border<<1), h-(border<<1), style, false); + } + + /* + ** Restore the mouse if it has been hidden and return. + */ + if (LogicPage == &SeenBuff) Conditional_Show_Mouse(); +} + + +/*********************************************************************************************** + * Simple_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + *=============================================================================================*/ +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag) +{ + static int yspace=0; // Y spacing adjustment for font. + static int xspace=0; // Spacing adjustment for font. + void const * font=0; // Font to use. + +////////////////#if (0) + static unsigned char _textfontpal[16][16] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 27, 26, 25, 24 }, + { 0,135, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 136,135,119, 2 }, + { 0,159, 0, 0, 0, 0, 0, 0, 0, 0, 0,142, 143,159,41 ,167 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,157, 0, 0, 0, 0, 0, 0, 0, 0, 0,180, 180,157,158, 5 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,179, 0, 0, 0, 0, 0, 0, 0, 0, 0,180, 180,179,178,176 }, + + { 0,123, 0, 0, 0, 0, 0, 0, 0, 0, 0,122, 122,123,125,127 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + { 0, 1, 4,166, 41, 3, 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,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + { 0,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + }; + static unsigned char _textpalmedium[16] = { + 0, 25, 119,41, 0, 158,0, 178, 125,0, 202,0, 0, 0, 0, 0 + }; + + static unsigned char _textpalbright[16] = { + 0, 24, 2, 4, 0, 5, 0, 176, 127,0, 201,0, 0, 0, 0, 0 + }; +///////////////////////#endif //(0) + + int point; // Requested font size. + int shadow; // Requested shadow value. + unsigned char fontpalette[16]; // Working font palette array. + memset(&fontpalette[0], back, 16); + + if ((flag & 0xf) == TPF_VCR) { + fontpalette[3] = 12; + fontpalette[9] = 15; + fontpalette[10] = 200; + fontpalette[11] = 201; + fontpalette[12] = 202; + fontpalette[13] = 203; + fontpalette[14] = 204; + fontpalette[15] = 205; + } + + char *tempstr = NULL; + + if (text){ + /* + ** remove any 0xff characters from the string + */ + tempstr = new char [strlen (text)+1]; + char *tempptr = tempstr; + + for ( int i=0 ; i<(int)strlen(text)+1 ; i++ ) { + if (text[i] != -1){ + *tempptr = text[i]; + tempptr++; + } + } + } + + /* + ** A gradient font always requires special fixups for the palette. + */ + if ((flag & 0xf) == TPF_6PT_GRAD || + (flag & 0xf) == TPF_GREEN12_GRAD || + (flag & 0xf) == TPF_GREEN12) { + /* + ** If a gradient palette was requested, then fill in the font palette array + ** according to the color index specified. + */ + if (flag & TPF_USE_GRAD_PAL) { + memcpy(&fontpalette[0], _textfontpal[ (fore & 0x0f) ], 16); + } else { + + /* + ** Special adjustment for fonts that have gradient artwork. When there is + ** no special gradient effect desired, then set the font color based on the + ** forground color specified. + */ + memset(&fontpalette[4], fore, 12); + } + + if (flag & TPF_MEDIUM_COLOR) { + fore = _textpalmedium[fore & 0x0F]; + memset(&fontpalette[4], fore, 12); + }else{ + if (flag & TPF_BRIGHT_COLOR) { + fore = _textpalbright[fore & 0x0F]; + memset(&fontpalette[4], fore, 12); + }else{ + fore = fontpalette[1]; + } + } + } + + + /* + ** Change the current font if it differs from the font desired. + */ + point = (flag & (TextPrintType)0x000F); + xspace = 1; + yspace = 0; + + switch (point) { + case TPF_GREEN12: + font = Green12FontPtr; + break; + + case TPF_GREEN12_GRAD: + font = Green12GradFontPtr; + break; + + case TPF_MAP: + font = MapFontPtr; + xspace -= 1; + break; + + case TPF_VCR: + font = VCRFontPtr; + break; + + case TPF_6PT_GRAD: + font = GradFont6Ptr; + xspace -= 1; + //yspace -= 1; + break; + + case TPF_3POINT: + xspace += 1; + font = Font3Ptr; + flag = flag & ~(TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_NOSHADOW); + break; + + case TPF_6POINT: + font = Font6Ptr; + xspace -= 1; + //yspace -= 1; + break; + + case TPF_8POINT: + font = Font8Ptr; + xspace -= 2; + yspace -= 4; + break; + + case TPF_LED: + xspace -= 4; + font = FontLEDPtr; + break; + + default: + font = FontPtr; + break; + } + + /* + ** Change the current font palette according to the dropshadow flags. + */ + shadow = (flag & (TPF_NOSHADOW|TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_LIGHTSHADOW)); + switch (shadow) { + + /* + ** The text is rendered plain. + */ + case TPF_NOSHADOW: + fontpalette[2] = back; + fontpalette[3] = back; + xspace -= 1; + yspace -= 2; + break; + + /* + ** The text is rendered with a simple + ** drop shadow. + */ + case TPF_DROPSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Special engraved text look for the options + ** dialog system. + */ + case TPF_LIGHTSHADOW: + fontpalette[2] = ((14 * 16) + 7)+1; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Each letter is surrounded by black. This is used + ** when the text will be over a non-plain background. + */ + case TPF_FULLSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = BLACK; + xspace -= 1; + break; + + default: + break; + } + fontpalette[0] = back; + fontpalette[1] = fore; + + /* + ** Set the font and spacing according to the values they should be. + */ + FontXSpacing = xspace; + FontYSpacing = yspace; + Set_Font(font); + Set_Font_Palette(fontpalette); + + /* + ** Display the (centered) message if there is one. + */ + if (text && *text) { + switch (flag & (TPF_CENTER|TPF_RIGHT)) { + case TPF_CENTER: + x -= String_Pixel_Width(tempstr)>>1; + break; + + case TPF_RIGHT: + x -= String_Pixel_Width(tempstr); + break; + + default: + break; + } + + if (x < (unsigned)SeenBuff.Get_Width() && y < (unsigned)SeenBuff.Get_Height()) { + LogicPage->Print(tempstr, x, y, fore, back); + } + } + if (tempstr){ + delete [] tempstr; + } + +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Text number to print. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 11/29/1994 JLB : Created * + *=============================================================================================*/ +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If the text number is valid, then process it. + */ + if (text != TXT_NONE) { + va_start(arg, flag); + + /* + ** The text string must be locked since the vsprintf function doesn't know + ** how to handle EMS pointers. + */ + char const * tptr = Text_String(text); + vsprintf(buffer, tptr, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are to be changed, since the text number is TXT_NONE. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + * 11/29/1994 JLB : Separated actual draw action. * + *=============================================================================================*/ +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If there is a valid text string pointer then build the final string into the + ** working buffer before sending it to the simple string printing routine. + */ + if (text) { + + /* + ** Since vsprintf doesn't know about EMS pointers, be sure to surround this + ** call with locking code. + */ + va_start(arg, flag); + vsprintf(buffer, text, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are desired to be changed, so call the simple print routine with + ** a NULL text pointer. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Clip_Text_Print -- Prints text with clipping and support. * + * * + * Use this routine to print text that that should be clipped at an arbitrary right margin * + * as well as possibly recognizing characters. Typical users of this routine would * + * be list boxes. * + * * + * INPUT: text -- Reference to the text to print. * + * * + * x,y -- Pixel coordinate of the upper left corner of the text position. * + * * + * fore -- The foreground color to use. * + * * + * back -- The background color to use. * + * * + * flag -- The text print flags to use. * + * * + * width -- The maximum pixel width to draw the text. Extra characters beyond this * + * point will not be printed. * + * * + * tabs -- Optional pointer to a series of pixel tabstop positions. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void Conquer_Clip_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, unsigned width, int const * tabs) +{ + char buffer[512]; + + if (text) { + strcpy(buffer, text); + + /* + ** Set the font and spacing characteristics according to the flag + ** value passed in. + */ + Simple_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, flag); + + char * source = &buffer[0]; + unsigned offset = 0; + int processing = true; + while (processing && offset < width) { + char * ptr = strchr(source, '\t'); + + /* + ** Zap the tab character. It will be processed later. + */ + if (ptr) { + *ptr = '\0'; + } + + if (*source) { + + /* + ** Scan forward until the end of the string is reached or the + ** maximum width, whichever comes first. + */ + int w = 0; + char * bptr = source; + do { + w += Char_Pixel_Width(*bptr++); + } while(*bptr && offset+w < width); + + /* + ** If the maximum width has been exceeded, then remove the last + ** character and signal that further processing is not necessary. + */ + if (offset+w >= width) { + bptr--; + w -= Char_Pixel_Width(*bptr); + *bptr = '\0'; + processing = 0; + } + + /* + ** Print this text block and advance the offset accordingly. + */ + Simple_Text_Print(source, x+offset, y, fore, back, flag); + offset += w; + } + + /* + ** If a was the terminator for this text block, then advance + ** to the next tabstop. + */ + if (ptr) { + if (tabs) { + while (offset > (unsigned) *tabs) { + tabs++; + } + offset = (unsigned) *tabs; + } else { + offset = ((offset+1 / 50) + 1) * 50; + } + source = ptr+1; + } else { + break; + } + } + } +} diff --git a/TIBERIANDAWN/DISPLAY.CPP b/TIBERIANDAWN/DISPLAY.CPP new file mode 100644 index 000000000..188c55aeb --- /dev/null +++ b/TIBERIANDAWN/DISPLAY.CPP @@ -0,0 +1,4407 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\display.cpv 2.16 16 Oct 1995 16:48:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * DisplayClass::DisplayClass -- Default constructor for display class. * + * DisplayClass::Draw_It -- Draws the tactical map. * + * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * DisplayClass::Init_Clear -- Clears the display to a known state. * + * DisplayClass::Init_IO -- Creates the map's button list * + * DisplayClass::Init_Theater -- Theater-specific initialization * + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * DisplayClass::Next_Object -- Searches for next object on display. * + * DisplayClass::One_Time -- Performs any special one time initializations. * + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * DisplayClass::Write_INI -- Writes map data into INI file. * + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +/* +** These layer control elements are used to group the displayable objects +** so that proper overlap can be obtained. +*/ +LayerClass DisplayClass::Layer[LAYER_COUNT]; + +/* +** Fading tables +*/ +unsigned char DisplayClass::FadingBrighten[256]; +unsigned char DisplayClass::FadingShade[256]; +unsigned char DisplayClass::FadingLight[256]; +unsigned char DisplayClass::RemapTables[HOUSE_COUNT][3][256]; +unsigned char DisplayClass::FadingGreen[256]; +unsigned char DisplayClass::FadingYellow[256]; +unsigned char DisplayClass::FadingRed[256]; +unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; +unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; +unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; +void const * DisplayClass::TransIconset; +unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::SpecialGhost[2*256]; + +void const * DisplayClass::ShadowShapes; +unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + +/* +** Bit array of cell redraw flags +*/ +BooleanVectorClass DisplayClass::CellRedraw; + +/* +** The main button that intercepts user input to the map +*/ +DisplayClass::TacticalClass DisplayClass::TacButton; + +// +// We need a way to bypass visible view checks when we are running in the context of GlyphX without using the +// internal C&C renderer. We shouldn't know or care what the user is actually looking at +// ST - 4/17/2019 9:01AM +// +bool DisplayClass::IgnoreViewConstraints = false; + +/* +** Define "_RETRIEVE" if the palette morphing tables are part of the loaded data. If this +** is undefined, then the files will be created. +*/ +#define _RETRIEVE + + +static int const TEX_X = 0; +static int const TEX_Y = 6; +static int const TEX_W = 14; + +extern MixFileClass *TheaterIcons; + + +//Added for getting the input for special character keys from the client +// - 6/26/2019 JAS +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + + +/*********************************************************************************************** + * DisplayClass::DisplayClass -- Default constructor for display class. * + * * + * This constructor for the display class just initializes some of the display settings. * + * Most settings are initialized with the correct values at the time that the Init function * + * is called. There are some cases where default values are wise and this routine fills * + * those particular ones in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + *=============================================================================================*/ +DisplayClass::DisplayClass(void) +{ + TacticalCoord = 0; + ShadowShapes = 0; + TransIconset = 0; + ZoneCell = 0; + ZoneOffset = 0; + CursorSize = 0; + ProximityCheck = false; + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + IsRepairMode = false; + IsTargettingMode = false; + IsToRedraw = true; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; +} + + +/*********************************************************************************************** + * DisplayClass::One_Time -- Performs any special one time initializations. * + * * + * This routine is called from the game initialization process. It is to perform any one * + * time initializations necessary for the map display system. It allocates the staging * + * buffer needed for the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called ONCE and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Handles layer system now. * + * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * + *=============================================================================================*/ +void DisplayClass::One_Time(void) +{ + Set_View_Dimensions(0, Map.Get_Tab_Height()); + + MapClass::One_Time(); + + /* + ** Init the CellRedraw bit array. Do not do this in the constructor, since the + ** BooleanVector may not have been constructed yet. + */ + CellRedraw.Resize(MAP_CELL_TOTAL); + + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].One_Time(); + } + + /* + ** Load the generic transparent icon set. + */ + TransIconset = MixFileClass::Retrieve("TRANS.ICN"); + + ShadowShapes = MixFileClass::Retrieve("SHADOW.SHP"); + + Set_View_Dimensions(0, Map.Get_Tab_Height()); + + /* + ** Allocate and initialize the remap tables needed for each "house". + */ + HousesType hindex; + int fade; + + for (fade = 0; fade < 3; fade++) { + for (hindex = HOUSE_FIRST; hindex < HOUSE_COUNT; hindex++) { + int color; + + switch (fade) { + case 0: + for (color = 0; color < 256; color++) { + RemapTables[hindex][fade][color] = color; + } + break; + + case 1: + Mem_Copy(FadingLight, RemapTables[hindex][fade], 256); + break; + + case 2: + Mem_Copy(FadingShade, RemapTables[hindex][fade], 256); + break; + } + Mem_Copy(&RemapTables[hindex][fade][((int)hindex+11)*16], &RemapTables[hindex][fade][(0+11)*16], 16); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Clear -- clears the display to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Clear(void) +{ + MapClass::Init_Clear(); + + /* + ** Clear any object being placed + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + CursorSize = 0; + IsTargettingMode = false; + IsRepairMode = false; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; + + /* + ** Empty all the display's layers + */ + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].Init(); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_IO -- clears & re-builds the map's button list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_IO(void) +{ + MapClass::Init_IO(); + + /* + ** Re-attach our buttons to the main map button list, only in non-edit mode. + */ + if (!Debug_Map) { + TacButton.Zap(); + Add_A_Button(TacButton); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * + * * + * INPUT: * + * theater new theater * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Theater(TheaterType theater) +{ + char fullname[16]; + char iconname[16]; +#ifndef _RETRIEVE + static TLucentType const MouseCols[4] = { + {BLACK, BLACK, 110, 0}, + {WHITE, WHITE, 110, 0}, + {LTGREY, LTGREY, 110, 0}, + {DKGREY, DKGREY, 110, 0} + }; + static TLucentType const MagicCols[MAGIC_COL_COUNT] = { + {32,32,110,0}, + {33,33,110,0}, + {34,34,110,0}, + {35,35,110,0}, + {36,36,110,0}, + {37,37,110,0}, + {38,38,110,0}, + {39,39,110,0}, + {BLACK, BLACK, 200, 0}, + {WHITE, BLACK, 40, 0}, + {LTGREY, BLACK, 80, 0}, + {DKGREY, BLACK, 140, 0} + }; + static TLucentType const WhiteCols[1] = { + {1, WHITE, 80, 0} + }; + static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { + {WHITE+1, BLACK,130,0}, + {WHITE, BLACK,170,0}, + {LTGRAY, BLACK,250,0}, + {DKGRAY, BLACK,250,0} + }; + static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,130,0} + }; +#endif + + /* + ---------------------- Invoke parent's init routine ---------------------- + */ + MapClass::Init_Theater(theater); + + /* + ** Save the new theater value + */ + Theater = theater; + +#ifndef DEMO + /* + ** Unload old mixfiles, and cache the new ones + */ + sprintf(fullname, "%s.MIX", Theaters[Theater].Root); + if (Theater != LastTheater){ + if (TheaterData) { + delete TheaterData; + } + TheaterData = new MixFileClass(fullname); + TheaterData->Cache(); + } + +#endif + /* + ** Register the hi-res icons mix file now since it is theater specific + */ + sprintf(fullname, "%s.MIX", Theaters[Theater].Root); + strcpy (iconname, fullname); + strcpy (&iconname[4], "ICNH.MIX"); + if (Theater != LastTheater){ + if (TheaterIcons) { + delete TheaterIcons; + } + TheaterIcons = new MixFileClass(iconname); + TheaterIcons->Cache(); + } + + + + /* + ** Load the custom palette associated with this theater. + ** The fading palettes will have to be generated as well. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + void const * ptr = MixFileClass::Retrieve(fullname); + Mem_Copy((void *)ptr, GamePalette, 768); + + + Mem_Copy(GamePalette, OriginalPalette, 768); + +#ifndef _RETRIEVE + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + memset(&GamePalette[CYCLE_COLOR_START*3], 0x3F, CYCLE_COLOR_COUNT*3); +#endif + + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("GREEN", theater)).Read(FadingGreen, sizeof(FadingGreen)); +#else + Build_Fading_Table(GamePalette, FadingGreen, GREEN, 110); + CCFileClass(Fading_Table_Name("GREEN", theater)).Write(FadingGreen, sizeof(FadingGreen)); +#endif + if (theater == THEATER_DESERT) { + FadingGreen[196] = 160; + } + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("YELLOW", theater)).Read(FadingYellow, sizeof(FadingYellow)); +#else + Build_Fading_Table(GamePalette, FadingYellow, YELLOW, 140); + CCFileClass(Fading_Table_Name("YELLOW", theater)).Write(FadingYellow, sizeof(FadingYellow)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("RED", theater)).Read(FadingRed, sizeof(FadingRed)); +#else + Build_Fading_Table(GamePalette, FadingRed, RED, 140); + CCFileClass(Fading_Table_Name("RED", theater)).Write(FadingRed, sizeof(FadingRed)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("MOUSE", theater)).Read(MouseTranslucentTable, sizeof(MouseTranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); + CCFileClass(Fading_Table_Name("MOUSE", theater)).Write(MouseTranslucentTable, sizeof(MouseTranslucentTable)); +#endif + +// MouseDrawPtr = MouseTranslucentTable; +// MouseDrawPtr2 = Add_Long_To_Pointer(MouseTranslucentTable, 256L); +// MouseDrawVal = 1; +// MouseDrawFlags = (int)SHAPE_GHOST; + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("TRANS", theater)).Read(TranslucentTable, sizeof(TranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); + CCFileClass(Fading_Table_Name("TRANS", theater)).Write(TranslucentTable, sizeof(TranslucentTable)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("WHITE", theater)).Read(WhiteTranslucentTable, sizeof(WhiteTranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); + CCFileClass(Fading_Table_Name("WHITE", theater)).Write(WhiteTranslucentTable, sizeof(WhiteTranslucentTable)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("SHADOW", theater)).Read(ShadowTrans, sizeof(ShadowTrans)); +#else + Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); + CCFileClass(Fading_Table_Name("SHADOW", theater)).Write(ShadowTrans, sizeof(ShadowTrans)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("UNITS", theater)).Read(UnitShadow, sizeof(UnitShadow)); +#else + Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); + CCFileClass(Fading_Table_Name("UNITS", theater)).Write(UnitShadow, sizeof(UnitShadow)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("SHADE", theater)).Read(FadingShade, sizeof(FadingShade)); +#else + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 150); + CCFileClass(Fading_Table_Name("SHADE", theater)).Write(FadingShade, sizeof(FadingShade)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("LIGHT", theater)).Read(FadingLight, sizeof(FadingLight)); +#else + Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); + CCFileClass(Fading_Table_Name("LIGHT", theater)).Write(FadingLight, sizeof(FadingLight)); +#endif + + /* + ** Create the shadow color used by aircraft. + */ + Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); + for (int index = 0; index < 256; index++) { + SpecialGhost[index] = 0; + } + + Build_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); + + +#ifndef _RETRIEVE + /* + ** Restore the palette since it was mangled while building the fading tables. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + ptr = MixFileClass::Retrieve(fullname); + Mem_Copy((void *)ptr, GamePalette, 768); + Mem_Copy(GamePalette, OriginalPalette, 768); +#endif + + /* + ** Adjust the palette according to the visual control option settings. + */ + Options.Fixup_Palette(); +} + + +/*********************************************************************************************** + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * * + * This routine is used to create an overlap list that specifies all the cells that are * + * covered by the specified text string. This overlap list is used to handle map refresh * + * logic. * + * * + * INPUT: text -- Pointer to the text that would appear on the map and must have an * + * overlap list generated. * + * * + * x,y -- The coordinates that the text would appear (upper left corner). * + * * + * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * + * if were displayed at the coordinates specified. The list is actually a series of * + * offsets from the display's upper left corner cell number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 12/07/1994 JLB : Sidebar fixup. * + * 08/13/1995 JLB : Optimized for variable sized help text. * + *=============================================================================================*/ +short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y, int lines) +{ + static short _list[30]; + + if (text) { + short * ptr = &_list[0]; + int len = String_Pixel_Width(text)+CELL_PIXEL_W; + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); + + /* + ** If the help text would spill into the sidebar, then flag this fact, but + ** shorten the apparent length so that the icon list calculation will + ** function correctly. + */ + if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { + len = right-x; + *ptr++ = REFRESH_SIDEBAR; + } + + /* + ** Build the list of overlap cell offset values according to the text + ** coordinate and the length. + */ + int height = (((FontHeight * lines) + 23) / 24) * 24; + + if (x <= right) { + CELL ul = Click_Cell_Calc(x, y-1); + CELL lr = Click_Cell_Calc(x+len-1, Bound(y+height, TacPixelY, SeenBuff.Get_Height() - 1)); + + if (ul == -1) ul = Click_Cell_Calc(x, y); +// if (lr == -1) lr = Click_Cell_Calc(x+len, y); + + if (ul != -1 && lr != -1) { + for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { + for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { + *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); + } + } + } + } + + *ptr = REFRESH_EOL; + } + return(_list); +} + + +/*********************************************************************************************** + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * * + * Use this routine to set the tactical map screen coordinates and dimensions. This routine * + * is typically used when the screen size or position changes as a result of the sidebar * + * changing position or appearance. * + * * + * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * + * corner. * + * * + * width -- The width of the tactical display (in pixels). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * height-- The height of the tactial display (in pixels). If this parameter is * + * omitted, then the width wil be as wide as the screen will allow. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 06/27/1995 JLB : Adjusts tactical map position if necessary. * + *=============================================================================================*/ +void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) +{ +#if (1) // This code pulled in from RA1. ST - 1/9/2019 10:53AM + if (width == -1) { + TacLeptonWidth = Pixel_To_Lepton(SeenBuff.Get_Width()-x); + } else { + TacLeptonWidth = width * CELL_LEPTON_W; + } + + // ST - 3/1/2019 12:05PM + // Made the below code more consistent with the width calculation. This is needed if we aren't going to draw the tabs at the top of the screen + // + if (height == -1) { + TacLeptonHeight = Pixel_To_Lepton(SeenBuff.Get_Height()-y); + //height = (SeenBuff.Get_Height()-y) / CELL_PIXEL_H; + } else { + TacLeptonHeight = height * CELL_LEPTON_H; + } + //TacLeptonHeight = height * CELL_LEPTON_H; + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = 0;// Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = 0;// Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight); + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); + +#else //CNC code + if (width == -1) { + width = SeenBuff.Get_Width() - x; + } + TacLeptonWidth = Pixel_To_Lepton(width); + + if (height == -1) { + height = SeenBuff.Get_Height() - y; + } + TacLeptonHeight = Pixel_To_Lepton(height); + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x >> 3; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = width >> 3; + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = height; + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); +#endif +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * * + * This routine is used to set up the terrain cursor according to the size of the object * + * that is to be placed down. The terrain cursor looks like an arbitrary collection of * + * hatched square overlays. Typical use is when placing buildings. * + * * + * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * + * be marked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1994 JLB : Created. * + * 06/26/1995 JLB : Puts placement cursor into static buffer. * + *=============================================================================================*/ +void DisplayClass::Set_Cursor_Shape(short const * list) +{ + if (CursorSize) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + ZoneOffset = 0; + + if (list) { + int w,h; + static short _list[50]; + + memcpy(_list, list, sizeof(_list)); + CursorSize = _list; + Get_Occupy_Dimensions (w, h, CursorSize); + ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); + Cursor_Mark(ZoneCell+ZoneOffset, true); + } else { + CursorSize = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const *object) +{ +#ifdef USE_RA_AI + + return Passes_Proximity_Check(object, PendingHouse, CursorSize, ZoneCell + ZoneOffset); + +#else //USE_RA_AI // Replaced by more generic function from RA, for RA AI. ST - 7/24/2019 5:46PM + + short const *ptr; + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (!object || !CursorSize || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = ZoneCell + ZoneOffset + *ptr++; + + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(cell)) return(false); + + TechnoClass * base = (*this)[newcell].Cell_Techno(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + if ((*this)[newcell].Owner == PendingHouse) { + return(true); + } + + if (base && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == PendingHouse) { + return(true); + } + } + } + return(false); +#endif +} + + + + +#ifdef USE_RA_AI +/* +** Additional version of Passes_Proximity_Check, inspired by RA implementation. Needed for RA AI. ST - 7/24/2019 5:42PM +*/ + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * house -- The house to base the proximity check upon. Typically this is the * + * player's house, but in multiplay, the computer needs to check for * + * proximity as well. * + * * + * list -- Pointer to the building's offset list. * + * * + * trycell -- The cell to base the offset list on. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + * 10/11/1994 BWG : Added IsProximate check for ore refineries * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const +{ + short const *ptr; + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (!object || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = list; + while (*ptr != REFRESH_EOL) { + CELL cell = trycell + *ptr++; + + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(cell)) return(false); + + TechnoClass * base = (*this)[newcell].Cell_Techno(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + if ((*this)[newcell].Owner == house) { + return(true); + } + + if (base && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == house) { + return(true); + } + } + } + return(false); +} + +#endif //USE_RA_AI + + + + + + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * * + * This routine controls the location, display, and animation of the * + * tactical map cursor. * + * * + * INPUT: pos -- Position to move the cursor do. If -1 is passed then * + * the cursor will just be hidden. If the position * + * passed is the same as the last position passed in, * + * then animation could occur (based on timers). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + * 06/08/1994 JLB : If position is -1, then follow mouse. * + * 02/28/1995 JLB : Forces placement cursor to fit on map. * + *=============================================================================================*/ +CELL DisplayClass::Set_Cursor_Pos(CELL pos) +{ + CELL prevpos; // Last position of cursor (for jump-back reasons). + + /* + ** Follow the mouse position if no cell number is provided. + */ + if (pos == -1) { + pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + } + + if (!CursorSize) { + prevpos = ZoneCell; + ZoneCell = pos; + return(prevpos); + } + + /* + ** Adjusts the position so that the placement cursor is never partway off the + ** tactical map. + */ + int w,h; + Get_Occupy_Dimensions (w, h, CursorSize); + + int x = Cell_X(pos + ZoneOffset); + int y = Cell_Y(pos + ZoneOffset); + + if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); +// if (x < TacMapX) x = TacMapX; + if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); +// if (y < TacMapY) y = TacMapY; + if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; +// if (x+w >= TacMapX+TacWidth) x = TacMapX+TacWidth-w; + if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) x = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; +// if (y+h >= TacMapY+TacHeight) y = TacMapY+TacHeight-h; + pos = XY_Cell(x, y) - ZoneOffset; + + /* + ** This checks to see if NO animation or drawing is to occur and, if so, + ** exits. + */ + if (pos == ZoneCell) return(pos); + + prevpos = ZoneCell; + + /* + ** If the cursor is visible, then handle the graphic update. + ** Otherwise, just update the global position of the cursor. + */ + if (CursorSize) { + + /* + ** Erase the old cursor (if it exists) AND the cursor is moving. + */ + if (pos != ZoneCell && ZoneCell != -1) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + /* + ** Render the cursor (could just be animation). + */ + if (pos != -1) { + Cursor_Mark(pos+ZoneOffset, true); + } + } + ZoneCell = pos; + ProximityCheck = Passes_Proximity_Check(PendingObject); + + return(prevpos); +} + + +/*********************************************************************************************** + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * * + * INPUT: * + * w ptr to fill in with height * + * h ptr to fill in with width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/31/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const *list) +{ + int min_x = MAP_CELL_W; + int max_x = -MAP_CELL_W; + int min_y = MAP_CELL_H; + int max_y = -MAP_CELL_H; + int x,y; + + w = 0; + h = 0; + + if (!list) { + /* + ** Loop through all cell offsets, accumulating max & min x- & y-coords + */ + while (*list != REFRESH_EOL) { + /* + ** Compute x & y coords of the current cell offset. We can't use Cell_X() + ** & Cell_Y(), because they use shifts to compute the values, and if the + ** offset is negative we'll get a bogus coordinate! + */ + x = (*list) % MAP_CELL_W; + y = (*list) / MAP_CELL_H; + + max_x = MAX(max_x, x); + min_x = MIN(min_x, x); + max_y = MAX(max_y, y); + min_y = MIN(min_y, y); + + list++; + } + + w = MAX(1, max_x - min_x + 1); + h = MAX(1, max_y - min_y + 1); + } +} + + +/*********************************************************************************************** + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * * + * This routine will clear or set the cursor display bits on the map. * + * If the bit is set, then the cursor will be rendered on that map * + * icon. * + * * + * INPUT: pos -- Position of the upper left corner of the cursor. * + * * + * on -- Should the bit be turned on? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that every call to set the bits is matched by a * + * corresponding call to clear the bits. * + * * + * HISTORY: * + * 09/04/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DisplayClass::Cursor_Mark(CELL pos, bool on) +{ + CELL const *ptr; + CellClass *cellptr; + + if (pos == -1) return; + + /* + ** For every cell in the CursorSize list, invoke its Redraw_Objects and + ** toggle its IsCursorHere flag + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + if (on) { + cellptr->IsCursorHere = true; + } else { + cellptr->IsCursorHere = false; + } + } + } + + /* + ** For every cell in the PendingObjectPtr's Overlap_List, invoke its + ** Redraw_Objects routine. + */ + if (PendingObjectPtr) { + ptr = PendingObjectPtr->Overlap_List(); + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * * + * This routine is called once per game display frame (15 times per second). It handles * + * the mouse shape tracking and map scrolling as necessary. * + * * + * INPUT: input -- The next key just fetched from the input queue. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * + * set to 0. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1994 JLB : Created. * + * 06/02/1994 JLB : Filters mouse click input. * + * 06/07/1994 JLB : Fixed so template click will behave right. * + * 10/14/1994 JLB : Changing cursor shape over target. * + * 12/31/1994 JLB : Takes mouse coordinates as parameters. * + * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * + *=============================================================================================*/ +void DisplayClass::AI(KeyNumType & input, int x, int y) +{ + if ( + IsRubberBand && + (Get_Mouse_X() < TacPixelX || + Get_Mouse_Y() < TacPixelY || + Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || + Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { + Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); + } + + MapClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * * + * This routine is used to add an arbitrary (but tangible) game object to the map. It will * + * be rendered (made visible) once it is submitted to this function. This function builds * + * the list of game objects that get rendered each frame as necessary. It is possible to * + * submit the game object to different rendering layers. All objects in a layer get drawn * + * at the same time. Using this layer method it becomes possible to have objects "below" * + * other objects. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * layer -- The layer to add the object to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * + *=============================================================================================*/ +void DisplayClass::Submit(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Submit(object, (layer == LAYER_GROUND)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * * + * Every object that is to disappear from the map must be removed from the rendering * + * system. * + * * + * INPUT: object -- The object to remove. * + * * + * layer -- The layer to remove it from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + *=============================================================================================*/ +void DisplayClass::Remove(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Delete((ObjectClass *)object); + } +} + + +/*********************************************************************************************** + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * * + * This routine is used to determine the cell that is located at the * + * screen pixel coordinates given. Typical use is when the player * + * clicks with the mouse on the tactical map. * + * * + * INPUT: x,y -- Screen pixel coordinates. * + * * + * OUTPUT: Returns with cell that is under the coordinates specified. * + * If the coordinate specified is outside of the tactical * + * map, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL DisplayClass::Click_Cell_Calc(int x, int y) +{ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 4/17/2019 9:06AM + //if (x < TacLeptonWidth && y < TacLeptonHeight) { + if (IgnoreViewConstraints || (x < TacLeptonWidth && y < TacLeptonHeight)) { + + COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); + + return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); + } + return(-1); +} + + +/*********************************************************************************************** + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * * + * This routine is used to read the map control data from the INI * + * file. * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The TriggerClass INI data must have been read before calling this function. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Read_INI(char *buffer) +{ + char name[16]; + int len; // Length of data in buffer. + char *tbuffer; // Accumulation buffer of Trigger names. + char *trigsection = "CellTriggers"; + char buf[20]; // trigger name for a cell + int cell; + int i; + + /* + ** Read the map dimensions. + */ + Set_Map_Dimensions(WWGetPrivateProfileInt("MAP", "X", 1, buffer), + WWGetPrivateProfileInt("MAP", "Y", 1, buffer), + WWGetPrivateProfileInt("MAP", "Width", MAP_CELL_W-2, buffer), + WWGetPrivateProfileInt("MAP", "Height", MAP_CELL_H-2, buffer)); + + /* + ** The theater is determined at this point. There is specific data that + ** is custom to this data. Load the custom data (as it related to terrain) + ** at this point. + */ + WWGetPrivateProfileString("MAP", "Theater", Theaters[THEATER_DESERT].Name, name, 13, buffer); + Theater = Theater_From_Name(name); + if (Theater == THEATER_NONE) { + Theater = THEATER_DESERT; + } + + /* + ** Remove any old theater specific uncompressed shapes + */ + if (Theater != LastTheater){ + Reset_Theater_Shapes(); + } + + /* + ** Now that the theater is known, init the entire map hierarchy + */ + Init(Theater); + + /* + ** Special initializations occur when the theater is known. + */ + TerrainTypeClass::Init(Theater); + TemplateTypeClass::Init(Theater); + OverlayTypeClass::Init(Theater); + UnitTypeClass::Init(Theater); + InfantryTypeClass::Init(Theater); + BuildingTypeClass::Init(Theater); + BulletTypeClass::Init(Theater); + AnimTypeClass::Init(Theater); + AircraftTypeClass::Init(Theater); + SmudgeTypeClass::Init(Theater); + + LastTheater = Theater; + + /* + ** Read the Waypoint entries. + */ + for (i = 0; i < WAYPT_COUNT; i++) { + sprintf(buf,"%d",i); + Waypoint[i] = WWGetPrivateProfileInt ("Waypoints",buf,-1,buffer); + if (Waypoint[i] != -1) { + (*this)[Waypoint[i]].IsWaypoint = 1; + } + } + + /* + ** Set the starting position (do this after Init(), which clears the cells' + ** IsWaypoint flags). + */ + if (Waypoint[WAYPT_HOME] == -1) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, MapCellY); + } + Set_Tactical_Position(Cell_Coord(Waypoint[WAYPT_HOME])&0xFF00FF00L); + Views[0] = Views[1] = Views[2] = Views[3] = Waypoint[WAYPT_HOME]; + + /* + ** Read the cell trigger names, and assign TriggerClass pointers + */ + len = strlen(buffer) + 2; // len is the length of the INI data + tbuffer = buffer + len; // tbuffer is after the INI data + + /* + ** Read all entry names into 'tbuffer'. + */ + WWGetPrivateProfileString(trigsection, NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ** Loop through all CellTrigger entries. + */ + while (*tbuffer != '\0') { + + /* + ** Get a cell trigger assignment. + */ + WWGetPrivateProfileString(trigsection, tbuffer, NULL, buf, sizeof(buf) - 1, buffer); + + /* + ** Get cell # from entry name. + */ + cell = atoi(tbuffer); + if (cell > 0 && cell < MAP_CELL_TOTAL && !(*this)[cell].IsTrigger) { + + /* + ** Assign trigger pointer using trigger name. + */ + CellTriggers[cell] = TriggerClass::As_Pointer(buf); + if (CellTriggers[cell]) { + (*this)[cell].IsTrigger = 1; + if (CellTriggers[cell]) { + CellTriggers[cell]->AttachCount++; + } + } + } + + /* + ** Step to next entry name. + */ + tbuffer += strlen(tbuffer) + 1; + } +} + + +/*********************************************************************************************** + * DisplayClass::Write_INI -- Writes map data into INI file. * + * * + * This routine is used to write the map control data into the INI * + * file. The scenario editor uses this when creating the scenario * + * startup file. * + * * + * INPUT: buffer -- Pointer to INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Write_INI(char *buffer) +{ + char entry[20]; + + /* + ** Save the map parameters. + */ + WWWritePrivateProfileString("MAP", "Theater", Theaters[Theater].Name, buffer); + WWWritePrivateProfileInt("MAP", "X", MapCellX, buffer); + WWWritePrivateProfileInt("MAP", "Y", MapCellY, buffer); + WWWritePrivateProfileInt("MAP", "Width", MapCellWidth, buffer); + WWWritePrivateProfileInt("MAP", "Height", MapCellHeight, buffer); + + /* + ** Save the Waypoint entries. + */ + for (int i = 0; i < WAYPT_COUNT; i++) { + sprintf(entry,"%d",i); + WWWritePrivateProfileInt ("Waypoints",entry,Waypoint[i],buffer); + } + + /* + ** Erase the CellTriggers section. + */ + WWWritePrivateProfileString("CellTriggers",NULL,NULL,buffer); + + /* + ** Save the cell's triggers. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].IsTrigger) { + + /* + ** Get cell trigger pointer. + */ + TriggerClass const * trig = CellTriggers[cell]; + + /* + ** Generate entry name. + */ + sprintf(entry,"%d",cell); + + /* + ** Save entry. + */ + WWWritePrivateProfileString("CellTriggers", entry, trig->Get_Name(), buffer); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * * + * This routine is used to scroll the tactical map view in the desired * + * direction. It can also be used to determine if scrolling would be * + * legal without actually performing any scrolling action. * + * * + * INPUT: facing -- The direction to scroll the tactical map. * + * * + * distance -- The distance in leptons to scroll the map. * + * * + * really -- Should the map actually be scrolled? If false, * + * then only the legality of a scroll is checked. * + * * + * OUTPUT: bool; Would scrolling in the desired direction be possible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/20/1994 JLB : Converted to member function. * + * 08/09/1995 JLB : Added distance parameter. * + * 08/10/1995 JLB : Any direction scrolling. * + *=============================================================================================*/ +bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + /* + ** If the distance is invalid then no further checking is required. Bail + ** with a no-can-do flag. + */ + if (distance == 0) return(false); + FacingType crude = Dir_Facing(facing); + + if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { + if (crude == FACING_SW) facing = DIR_S; + if (crude == FACING_NW) facing = DIR_N; + } + if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { + if (crude == FACING_NW) facing = DIR_W; + if (crude == FACING_NE) facing = DIR_E; + } + if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { + if (crude == FACING_NE) facing = DIR_N; + if (crude == FACING_SE) facing = DIR_S; + } + if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { + if (crude == FACING_SE) facing = DIR_E; + if (crude == FACING_SW) facing = DIR_W; + } + + /* + ** Determine the coordinate that it wants to scroll to. + */ + COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); + + /* + ** Clip the new coordinate to the edges of the game world. + */ + int xx = Coord_X(coord) - Cell_To_Lepton(MapCellX); + int yy = Coord_Y(coord) - Cell_To_Lepton(MapCellY); + bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + if (xx < 0) { + xx = 0; + shifted = true; + } + if (yy < 0) { + yy = 0; + shifted = true; + } + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + /* + ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately + ** reflect the actual distance moved. + */ + if (shifted) { + distance = Distance(TacticalCoord, coord); + } + + /* + ** If the new coordinate is the same as the old, then no scrolling would occur. + */ + if (!distance || coord == TacticalCoord) return(false); + + /* + ** Since the new coordinate is different than the old one, possibly adjust the real + ** tactical map accordingly. + */ + if (really) { + Set_Tactical_Position(coord); + IsToRedraw = true; + Flag_To_Redraw(false); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * * + * This routine is used to flag all cells in the specified list for * + * redrawing. * + * * + * INPUT: cell -- The origin cell that the list is offset from. * + * * + * list -- Pointer to a list of offsets from the origin cell. * + * Each cell so specified is flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is rather slow (by definition). * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 08/01/1994 JLB : Simplified. * + *=============================================================================================*/ +void DisplayClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + list++; + } + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if (In_Radar(newcell)) { + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + * 03/06/2019 ST : Added house parameter so we can do this per player * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell, HouseClass *house) +{ + if (house == NULL) { + return -1; + } + int index; + int value = -1; + CellClass *cellptr; + static char const CardShadow[16] = {-2,0,1,2,3,-1,4,-1,5,6,-1,-1,7,-1,-1,-1}; + static char const DiagShadow[16] = {-2,8,9,-1,10,-1,-1,-1,11,-1,-1,-1,-1,-1,-1,-1}; + + /* + ** Don't map cells that are at the edges. This solves + ** problem of accessing cells off the bounds of map and into + ** who-knows-what memory. + */ + if ((unsigned)(Cell_X(cell)-1) >= MAP_CELL_W-2) return(-2); + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-2); // Changed > to >= per Red Alert to fix out of bounds crash. ST - 3/25/2020 11:02PM + + cellptr = &(*this)[cell]; + if (!cellptr->Is_Mapped(house)) { + + /* + ** Check the cardinal directions first. This will either result + ** in a solution or the flag to check the diagonals. + */ + index = 0; + cellptr--; + if (cellptr->Is_Mapped(house)) index |= 0x08; + cellptr += MAP_CELL_W+1; + if (cellptr->Is_Mapped(house)) index |= 0x04; + cellptr -= MAP_CELL_W-1; + if (cellptr->Is_Mapped(house)) index |= 0x02; + cellptr -= MAP_CELL_W+1; + if (cellptr->Is_Mapped(house)) index |= 0x01; + value = CardShadow[index]; + + /* + ** The diagonals must be checked, since the cardinal directions + ** did not yield a valid result. + */ + if (value == -2) { + index = 0; + cellptr--; + if (cellptr->Is_Mapped(house)) index |= 0x08; + cellptr += MAP_CELL_W*2; + if (cellptr->Is_Mapped(house)) index |= 0x04; + cellptr += 2; + if (cellptr->Is_Mapped(house)) index |= 0x02; + cellptr -= MAP_CELL_W*2; + if (cellptr->Is_Mapped(house)) index |= 0x01; + value = DiagShadow[index]; + } + + /* + ** Randomizer should go here. Add sets in multiples of 12. + */ + + } + return(value); +} + +#if (0) +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell) +{ + int index; + int value = -1; + CellClass *cellptr; + static char const CardShadow[16] = {-2,0,1,2,3,-1,4,-1,5,6,-1,-1,7,-1,-1,-1}; + static char const DiagShadow[16] = {-2,8,9,-1,10,-1,-1,-1,11,-1,-1,-1,-1,-1,-1,-1}; + + /* + ** Don't map cells that are at the top or bottom edge. This solves + ** problem of accessing cells off the top or bottom of the map and into + ** who-knows-what memory. + */ + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + cellptr = &(*this)[cell]; + if (!cellptr->IsMapped) { + + /* + ** Check the cardinal directions first. This will either result + ** in a solution or the flag to check the diagonals. + */ + index = 0; + cellptr--; + if (cellptr->IsMapped) index |= 0x08; + cellptr += MAP_CELL_W+1; + if (cellptr->IsMapped) index |= 0x04; + cellptr -= MAP_CELL_W-1; + if (cellptr->IsMapped) index |= 0x02; + cellptr -= MAP_CELL_W+1; + if (cellptr->IsMapped) index |= 0x01; + value = CardShadow[index]; + + /* + ** The diagonals must be checked, since the cardinal directions + ** did not yield a valid result. + */ + if (value == -2) { + index = 0; + cellptr--; + if (cellptr->IsMapped) index |= 0x08; + cellptr += MAP_CELL_W*2; + if (cellptr->IsMapped) index |= 0x04; + cellptr += 2; + if (cellptr->IsMapped) index |= 0x02; + cellptr -= MAP_CELL_W*2; + if (cellptr->IsMapped) index |= 0x01; + value = DiagShadow[index]; + } + + /* + ** Randomizer should go here. Add sets in multiples of 12. + */ + + } + return(value); +} +#endif + + + +/*********************************************************************************************** + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * * + * This routine maps the specified cell. The cell must not already * + * have been mapped and the mapping player must be the human. * + * This routine will update any adjacent cell map icon as appropriate. * + * * + * INPUT: cell -- The cell to be mapped. * + * * + * house -- The player that is doing the mapping. * + * * + * OUTPUT: bool; Was action taken to map this cell? * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/24/1994 JLB : Takes pointer to HouseClass. * + * 03/06/2019 ST : Use per-player mapping so we can track the shroud for all players * + *=============================================================================================*/ +bool DisplayClass::Map_Cell(CELL cell, HouseClass * house, bool and_for_allies) +{ + // It's OK to do this if the house isn't the local player. ST - 3/6/2019 11:06AM + //if (house != PlayerPtr || cell >= (CELL)Size) return(false); + if (house == NULL || cell >= (CELL)Size) return(false); + + if (!house->IsHuman) { + if (!ShareAllyVisibility || !and_for_allies) { + return false; + } + } + + /* + ** Maybe also recurse to map for allies + */ + if (ShareAllyVisibility && and_for_allies && GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + for (HousesType house_type = HOUSE_MULTI1; house_type < HOUSE_COUNT; house_type++) { + HouseClass *hptr = HouseClass::As_Pointer(house_type); + if (hptr && hptr->IsActive) { + if (hptr != house && house->Is_Ally(hptr)) { + Map_Cell(cell, hptr, false); + } + } + } + } + + /* + ** Don't bother remapping this cell if it is already mapped. + */ + //if ((*this)[cell].IsMapped) { + if ((*this)[cell].Is_Mapped(house)) { // Check by player. ST - 3/6/2019 10:28AM + return(false); + } + + /* + ** Mark the cell as being mapped. + */ + //(*this)[cell].IsMapped = true; + //(*this)[cell].IsVisible = true; + // Set by player. ST - 3/6/2019 10:29AM + (*this)[cell].Set_Mapped(house); + (*this)[cell].Set_Visible(house); + + (*this)[cell].Redraw_Objects(); + + /* + ** Check out all adjacent cells to see if they need + ** to be mapped as well. This is necessary because of the + ** "unique" method of showing shadowed cells. Many combinations + ** are not allowed, and to fix this, just map the cells until + ** all is ok. + */ + int xx = Cell_X(cell); + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + int shadow; + CELL c; + int xdiff; + + c = Adjacent_Cell(cell, dir); + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)c >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(c) - xx; + xdiff = ABS(xdiff); + if (xdiff > 1) continue; + + if (c != cell && !(*this)[c].Is_Mapped(house)) { // Check by player. ST - 3/6/2019 10:28AM + shadow = Cell_Shadow(c, house); + + /* + ** Either map the cell or mark it to be refreshed. It + ** will probably change form if it isn't actually mapped. + */ + if (shadow == -1) { + Map_Cell(c, house, false); + } else { + if (shadow != -2) { + //(*this)[c].IsVisible = true; + (*this)[c].Set_Visible(house); // Set by player. ST - 3/6/2019 11:07AM + (*this)[c].Redraw_Objects(); + } + } + } + } + + TechnoClass * tech = (*this)[cell].Cell_Techno(); + if (tech) { + tech->Revealed(house); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * * + * This is the routine that figures out the location on the screen for * + * a specified coordinate. It is one of the fundamental routines * + * necessary for rendering the game objects. It performs some quick * + * tests to see if the coordinate is in a visible region and returns * + * this check as a boolean value. * + * * + * INPUT: coord -- The coordinate to check. * + * * + * x,y -- Reference to the pixel coordinates that this * + * coordinate would be when rendered. * + * * + * OUTPUT: bool; Is this coordinate in a visible portion of the map? * + * * + * WARNINGS: If the coordinate is not in a visible portion of the * + * map, then this X and Y parameters are not set. * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 12/15/1994 JLB : Converted to member function. * + * 01/07/1995 JLB : Uses inline functions to extract coord components. * + * 08/09/1995 JLB : Uses new coordinate system. * + *=============================================================================================*/ +#define EDGE_ZONE (CELL_LEPTON_W*2) +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) +{ + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + xoff = (xoff + EDGE_ZONE) - xtac; + + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + yoff = (yoff + EDGE_ZONE) - ytac; + + x = Lepton_To_Pixel(xoff) - CELL_PIXEL_W * 2; + y = Lepton_To_Pixel(yoff) - CELL_PIXEL_H * 2; + + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 4/17/2019 9:06AM + return(coord && (IgnoreViewConstraints || ((xoff <= TacLeptonWidth + EDGE_ZONE * 2) && (yoff <= TacLeptonHeight + EDGE_ZONE * 2)))); +} + + +#if (0) // reference. ST - 4/17/2019 9:07AM +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) +{ + if (coord) { + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + + xoff = (xoff+EDGE_ZONE) - xtac; + if (xoff <= TacLeptonWidth + EDGE_ZONE*2) { + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + + yoff = (yoff+EDGE_ZONE) - ytac; + if (yoff <= TacLeptonHeight + EDGE_ZONE*2) { + x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; + y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; + return(true); + } + } + } + return(false); +} +#endif + + + +/*********************************************************************************************** + * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * + * * + * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * + * be within the region bounded by TacMapX,Y - + TacMapW,H. * + * * + * INPUT: source, dest -- References to the coordinates to check. * + * * + * * + * OUTPUT: bool; Are these coordinates in a visible portion of the map? * + * Returns true if the pushed source & dest are visible, but if neither are * + * within the map, then it returns false. * + * * + * * + * HISTORY: * + * 03/27/1995 BWG : Created. * + *=============================================================================================*/ +bool DisplayClass::Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest) +{ + if (!source || !dest) return(false); + + int x1 = Coord_X(source); + int y1 = Coord_Y(source); + int x2 = Coord_X(dest); + int y2 = Coord_Y(dest); + int left = Coord_X(TacticalCoord); + int right = Coord_X(TacticalCoord) + TacLeptonWidth; + int top = Coord_Y(TacticalCoord); + int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; + + if (x1 < left && x2 < left) return(false); + if (x1 > right && x2 > right) return(false); + if (y1 < top && y2 < top) return(false); + if (y1 > bottom && y2 > bottom) return(false); + + x1 = Bound(x1, left, right); + x2 = Bound(x2, left, right); + y1 = Bound(y1, top, bottom); + y2 = Bound(y2, top, bottom); + + source = XY_Coord(x1, y1); + dest = XY_Coord(x2, y2); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * * + * This routine is used to determine what the player has clicked on. * + * It is passed the cell that the click was on and it then examines * + * the cell and returns with a pointer to the object that is there. * + * * + * INPUT: cell -- The cell that has been clicked upon. * + * * + * x,y -- Optional offsets from the upper left corner of the cell to be used in * + * determining exactly which object in the cell is desired. * + * * + * OUTPUT: Returns with a pointer to the object that is "clickable" in * + * the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) +{ + return(*this)[cell].Cell_Object(x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Draw_It -- Draws the tactical map. * + * * + * This will draw the tactical map at the recorded position. This * + * routine is used whenever the tactical map moves or needs to be * + * completely redrawn. It will handle making the necessary adjustments * + * to accomodate a moving cursor. * + * * + * INPUT: forced -- bool; force redraw of the entire display? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1991 JLB : Created. (benchmark = 292) * + * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * + * 04/15/1991 JLB : Added actual map reference for terrain (207) * + * 04/16/1991 JLB : _cell2meta converted to int (194) * + * 04/16/1991 JLB : References actual CellIcon[] array (204) * + * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * + * 04/17/1991 JLB : Cell based tactical map rendering (165) * + * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * + * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * + * 04/23/1991 JLB : Map active location cursor (334) * + * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * + * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * + * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * + * 05/12/1992 JLB : Destination page support. * + * 02/14/1994 JLB : Revamped. * + * 05/01/1994 JLB : Converted to member function. * + * 12/15/1994 JLB : Updated to work with display heirarchy. * + * 12/24/1994 JLB : Examines redraw bit intelligently. * + * 12/24/1994 JLB : Combined with old Refresh_Map() function. * + * 01/10/1995 JLB : Rubber band drawing. * + *=============================================================================================*/ + void DisplayClass::Draw_It(bool forced) +{ + int x,y; // Working cell index values. + + MapClass::Draw_It(forced); + + if (IsToRedraw || forced) { + IsToRedraw = false; + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + Refresh_Band(); + + /* + ** If the multiplayer message system is displaying one or more messages, + ** flag all cells covered by the messages to redraw. This will prevent + ** messages from smearing the map if it scrolls. + */ + int num = Messages.Num_Messages(); + if (num) { + CELL cell; + for (cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + if (num > 1) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 3) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 4) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + } + + /* + ** Check for a movement of the tactical map. If there has been some + ** movement, then part (or all) of the icons must be redrawn. + */ + if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || + Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { + + int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); + int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); + + int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. + int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; + + int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. + int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. + + if (oldw < 1) forced = true; + if (oldh < 1) forced = true; + + /* + ** Work out which map edges need to be redrawn + */ + BOOL redraw_right = (oldx < 0) ? TRUE : FALSE; //Right hand edge + BOOL redraw_left = (oldx > 0) ? TRUE : FALSE; //Left hand edge + BOOL redraw_bottom= (oldy < 0) ? TRUE : FALSE; //Bottom edge + BOOL redraw_top = (oldy > 0) ? TRUE : FALSE; //Top edge + + +//Colour_Debug(2); + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + CachedIconsDrawn=0; + UnCachedIconsDrawn=0; + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + /* + ** If hid page is in video memory then we may nned to blit from the seen page to + ** avoid blitting an overlapped region. + */ + if (HidPage.Get_IsDirectDraw() && !OverlappedVideoBlits){ + Hide_Mouse(); + SeenBuff.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + Show_Mouse(); + }else{ + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** + ** Set the 'redraw stamp' bit for any cells that could not be copied. + ** + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + + if (abs(oldx) < 0x25 && abs(oldy) < 0x25){ + + /* + ** The width of the area we redraw depends on the scroll speed + */ + int extra_x = (abs(oldx)>=16) ? 2 : 1; + int extra_y = (abs(oldy)>=16) ? 2 : 1; + + /* + ** Flag the cells across the top of the visible area if required + */ + if (redraw_top){ + for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells across the bottom of the visible area if required + */ + if (redraw_bottom){ + for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the left of the visible area if required + */ + if (redraw_left){ + for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the right of the visible area if required + */ + if (redraw_right){ + for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + }else{ + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } + + /* + ** If the entire tactical map is forced to be redrawn, then set all the redraw flags + ** and let the normal processing take care of the rest. + */ + if (forced) { + CellRedraw.Set(); + } + +//Colour_Debug(3); + /* + ** The first order of business is to redraw all the underlying icons that are + ** flagged to be redrawn. + */ + //Redraw_Icons(CELL_BLIT_ONLY); + Redraw_Icons(0); + + /* + ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom + ** area one line below the screen. This causes the predator effect to work on any + ** shape drawn at the bottom of the screen. + */ +//Colour_Debug(4); +#ifdef FIX_ME_LATER +// HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); +#endif //FIX_ME_LATER + if (HidPage.Lock()){ + + //Redraw_Icons(CELL_DRAW_ONLY); + + /* + ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer + ** first and then followed by all the layers in increasing altituded. + */ + for (LayerType layer = LAYER_GROUND; layer < LAYER_COUNT; layer++) { + for (int index = 0; index < Layer[layer].Count(); index++) { + Layer[layer][index]->Render(forced); + } + } + + /* + ** Finally, redraw the shadow overlay as necessary. + */ +//Colour_Debug(5); + Redraw_Shadow(); + } + + Redraw_Shadow_Rects(); + + HidPage.Unlock(); + +//Colour_Debug(8); + /* + ** Draw the rubber band over the top of it all. + */ + if (IsRubberBand) { + LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); + } + /* + ** Clear the redraw flags so that normal redraw flag setting can resume. + */ + CellRedraw.Reset(); +//Colour_Debug(0); + +#ifdef SCENARIO_EDITOR + /* + ** If we're placing an object (PendingObject is non-NULL), and that object + ** is NOT an icon, smudge, or overlay, draw it here. + ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; + ** they're drawn at the upper left coord, so I have to AND the coord value + ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the + ** cell coordinates. + */ + if (Debug_Map && PendingObjectPtr) { + PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); + PendingObjectPtr->Render(true); + } +#endif + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * * + * This routine will redraw all of the terrain icons that are flagged * + * to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + * 06/20/1994 JLB : Uses cell drawing support function. * + * 12/06/1994 JLB : Scans tactical view in separate row/colum loops * + * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * + *=============================================================================================*/ +void DisplayClass::Redraw_Icons(int draw_flags) +{ + IsShadowPresent = false; + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00L; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (cellptr->Is_Visible(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + cellptr->Draw_It(xpixel, ypixel, draw_flags); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + if (!cellptr->Is_Mapped(PlayerPtr) && !Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + IsShadowPresent = true; + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + if (!cellptr->Is_Mapped(PlayerPtr)) { // Pass player pointer since we will only be rendering in single player mode. ST - 3/6/2019 1:36PM + if (cellptr->Is_Visible(PlayerPtr)) { // Pass player pointer since we will only be rendering in single player mode. ST - 3/6/2019 1:36PM + int shadow = Cell_Shadow(cell, PlayerPtr); // Pass player pointer since we will only be rendering in single player mode. ST - 3/6/2019 1:36PM + if (shadow >= 0) { + CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); + } + } + } + } + } + } + } + } +} + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow_Rects(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + if (!cellptr->Is_Mapped(PlayerPtr)) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + if (!cellptr->Is_Visible(PlayerPtr)) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + int ww = CELL_PIXEL_W; + int hh = CELL_PIXEL_H; + + if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { + LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); + } + } + } + } + } + } + } + } +} + +/*********************************************************************************************** + * DisplayClass::Next_Object -- Searches for next object on display. * + * * + * This utility routine is used to find the "next" object from the object specified. This * + * is typically used when is pressed and the current object shifts. * + * * + * INPUT: object -- The current object to base the "next" calculation off of. * + * * + * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * + * then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Next_Object(ObjectClass * object) +{ + ObjectClass * firstobj = 0; + bool foundmatch = false; + + if (!object) { + foundmatch = true; + } + for (unsigned uindex = 0; uindex < (unsigned)Layer[LAYER_GROUND].Count(); uindex++) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj && obj->Is_Techno() && ((TechnoClass *)obj)->Is_Discovered_By_Player() && obj->Class_Of().IsSelectable && obj->Owner() == PlayerPtr->Class->House) { + if (!firstobj) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * * + * This routine will search for the previous object. Previous is defined as the one listed * + * before the specified object in the ground layer. If there is no specified object, then * + * the last object in the ground layer is returned. * + * * + * INPUT: object -- Pointer to the object that "previous" is to be defined from. * + * * + * OUTPUT: Returns with a pointer to the object previous to the specified one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) +{ + ObjectClass * firstobj = 0; + bool foundmatch = false; + + if (!object) { + foundmatch = true; + } + for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj && obj->Is_Techno() && ((TechnoClass *)obj)->Is_Discovered_By_Player() && obj->Class_Of().IsSelectable && obj->Owner() == PlayerPtr->Class->House) { + if (!firstobj) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * * + * INPUT: * + * x,y pixel coordinates to convert * + * * + * OUTPUT: * + * COORDINATE of pixel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/09/1994 BR : Created. * + * 12/06/1994 JLB : Uses map dimension variables in display class. * + * 12/10/1994 JLB : Uses union to speed building coordinate value. * + *=============================================================================================*/ +COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) +{ + /* + ** Normalize the pixel coorindates to be relative to the upper left corner + ** of the tactical map. The coordinates are expressed in leptons. + */ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + /* + ** If pixel coordinate is over the tactical map, then translate it into a coordinate + ** value. If not, then just return with NULL. + */ + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 4/17/2019 9:06AM + //if (x < TacLeptonWidth && y < TacLeptonHeight) { + if (IgnoreViewConstraints || (x < TacLeptonWidth && y < TacLeptonHeight)) { + return(Coord_Add(TacticalCoord, XY_Coord(x, y))); + } + return(0); +} + + +/*********************************************************************************************** + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * * + * Find a cell meeting the specified requirements. This function is * + * used for scenario reinforcements. * + * * + * INPUT: dir -- Method of picking a map cell. * + * * + * house -- The house to base calculation on. * + * * + * OUTPUT: Returns with the calculated cell. If 0, then this indicates * + * that no legal cell was found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/11/1994 JLB : Revamped. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +CELL DisplayClass::Calculated_Cell(SourceType dir, HousesType house) +{ + CELL cell = 0; // Working cell number. + + while (cell == 0) { + int x,y; + int index; + + /* + ** Select a candidate cell based on the desired method. + */ + switch (dir) { + + /* + ** Looks for the northern most straight path shipping lane and returns + ** the cell of one of the ends. + */ + case SOURCE_SHIPPING: + for (y = 0; y < MapCellHeight; y++) { + for (x = 0; x < MapCellWidth; x++) { + if ((*this)[XY_Cell(MapCellX+x, MapCellY+y)].Land_Type() != LAND_WATER) break; + } + if (x == MapCellWidth) { + return(XY_Cell(MapCellX+MapCellWidth, MapCellY+y)); + } + } + return(0); + + /* + ** Select a map edge. + */ + case SOURCE_NORTH: + index = Random_Pick(1, MapCellWidth); + for (x = 0; x < MapCellWidth; x++) { + cell = XY_Cell(MapCellX+((x+index)%MapCellWidth), MapCellY-1); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell+MAP_CELL_W].Is_Generally_Clear()) break; + } + if (x == MapCellWidth) return(0); + break; + + case SOURCE_EAST: + index = Random_Pick(1, MapCellHeight); + for (y = 0; y < MapCellHeight; y++) { + cell = XY_Cell(MapCellX+MapCellWidth, MapCellY+((y+index)%MapCellHeight)); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell-1].Is_Generally_Clear()) break; + } + if (y == MapCellHeight) return(0); + break; + + case SOURCE_SOUTH: + index = Random_Pick(1, MapCellWidth); + for (x = 0; x < MapCellWidth; x++) { + cell = XY_Cell(MapCellX+((x+index)%MapCellWidth), MapCellY+MapCellHeight); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell-MAP_CELL_W].Is_Generally_Clear()) break; + } + if (x == MapCellWidth) return(0); + break; + + case SOURCE_WEST: + index = Random_Pick(1, MapCellHeight); + for (y = 0; y < MapCellHeight; y++) { + cell = XY_Cell(MapCellX-1, MapCellY+((y+index)%MapCellHeight)); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell+1].Is_Generally_Clear()) break; + } + if (y == MapCellHeight) return(0); + break; + + /* + ** Drop in at a random location. + */ + case SOURCE_AIR: + cell = Waypoint[WAYPT_REINF]; + if (cell < 1) { + cell = Coord_Cell(TacticalCoord); + return(cell); + } else { + if ((*this)[cell].Cell_Techno()) { + for (int radius = 1; radius < 7; radius++) { + CELL newcell = Coord_Cell(Coord_Scatter(Cell_Coord(cell), radius << 8, true)); + if (In_Radar(newcell) && !(*this)[newcell].Cell_Techno()) { + cell = newcell; + break; + } + } + } + } + break; + + /* + ** Dramatic entry point is somewhere on the visible screen as defined + ** by the current tactical map position. + */ + case SOURCE_VISIBLE: + cell = XY_Cell(Coord_XCell(TacticalCoord)+Random_Pick(0, Lepton_To_Cell(TacLeptonWidth)-1), Coord_YCell(TacticalCoord)+Random_Pick(0, Lepton_To_Cell(TacLeptonHeight)-1)); + if (house == PlayerPtr->Class->House && !In_Radar(cell)) { + cell = 0; + } + break; + + /* + ** Drop off near friendly base or near a friendly unit or + ** just randomly if all else fails. + */ + case SOURCE_ENEMYBASE: + case SOURCE_HOMEBASE: + return(0); + + /* + ** Find an unoccupied beach cell. + */ + case SOURCE_BEACH: { + CELL cells[MAP_CELL_W]; + CELL alternate[MAP_CELL_W]; + unsigned counter=0; + + for (x = 0; x < MapCellWidth; x++) { + CELL newcell = 0; + + if ((*this)[XY_Cell(x + MapCellX, MapCellHeight + MapCellY)].Land_Type() != LAND_WATER) continue; + if ((*this)[XY_Cell(x + MapCellX, MapCellHeight + MapCellY-1)].Land_Type() != LAND_WATER) continue; + for (y = MapCellHeight; y >= 0; y--) { + newcell = XY_Cell(x + MapCellX, y + MapCellY); + if ((*this)[newcell].Cell_Techno()) { + break; + } + if ((*this)[newcell].Land_Type() != LAND_WATER) { + break; + } + } + LandType land = (*this)[newcell].Land_Type(); + if (( land == LAND_BEACH || land == LAND_CLEAR || land == LAND_ROAD) && + !(*this)[newcell].Cell_Techno() && + !(*this)[newcell].Cell_Terrain() && + !(*this)[newcell-MAP_CELL_W].Cell_Techno() && + !(*this)[newcell-MAP_CELL_W].Cell_Terrain() && + !(*this)[newcell-(MAP_CELL_W*2)].Cell_Terrain() && + !(*this)[newcell-(MAP_CELL_W*2)].Cell_Techno()) { + + cells[counter++] = newcell; + if (counter >= (sizeof(cells) / sizeof(cells[0]))) { + break; + } + } + } + + /* + ** Fixup entry list so that it doesn't come close to blocking terrain or other + ** units. + */ + int counter2 = 0; + for (int index = 1; index < (int)counter-1; index++) { + if (Cell_X(cells[index-1])+1 == Cell_X(cells[index]) && Cell_X(cells[index+1])-1 == Cell_X(cells[index])) { + alternate[counter2++] = cells[index]; + } + } + + CELL cell = 0; + if (counter2) { + if (counter2 < 4) { + cell = alternate[counter2-1]; + } else { + cell = alternate[counter2 - (counter2 / 4)]; + } + } else { + if (counter) { + if (counter < 4) { + cell = cells[counter-1]; + } else { + cell = cells[counter - (counter / 4)]; + } + } + } + if (cell) { + if (Map.Theater == THEATER_DESERT) { + cell += MAP_CELL_W; + } + } + return(cell); + } + + case SOURCE_OCEAN: + cell = XY_Cell(Random_Pick(0, MapCellWidth-2)+MapCellX, MapCellHeight+MapCellY); + break; + + default: + return(0); + } + + /* + ** The selected edge cell must be unoccupied and if this is for + ** the player, then it must be on an accessible map cell. + */ + cell &= 0x0FFF; + if (cell && (*this)[cell].Cell_Techno()) { + cell = 0; + } + } + return(cell); +} + + +/*********************************************************************************************** + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * * + * Use this routine to simultaneously select all objects within the coordinate region * + * specified. This routine is used by the multi-select rubber band handler. * + * * + * INPUT: coord1 -- Coordinate of one corner of the selection region. * + * * + * coord2 -- The opposite corner of the selection region. * + * * + * additive -- Does this add to the existing selection or replace it. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 04/25/1995 JLB : Limited to non-building type. * + *=============================================================================================*/ +static bool should_exclude_from_selection(ObjectClass* obj) +{ + return (obj->What_Am_I() == RTTI_UNIT) && + (((UnitClass *)obj)->Class->IsToHarvest || + *((UnitClass *)obj) == UNIT_MCV); +} + +void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool additive) +{ + COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; + + coord1 = Coord_Add(tcoord, coord1); + coord2 = Coord_Add(tcoord, coord2); + int x1 = Coord_X(coord1); + int x2 = Coord_X(coord2); + int y1 = Coord_Y(coord1); + int y2 = Coord_Y(coord2); + + /* + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. + */ + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + /* + ** Sweep through all ground layer objects and select the ones within the + ** bounding box. + */ + if (!additive) { + Unselect_All(); + } + AllowVoice = true; + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Layer[LAYER_GROUND][index]; + COORDINATE ocoord = obj->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are owned by the player, are allowed to be + ** selected, and are within the bouding box. + */ + if ( obj->Class_Of().IsSelectable && + obj->What_Am_I() != RTTI_BUILDING && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = obj->Owner() == PlayerPtr->Class->House; + AllowVoice &= is_player_controlled; + if (obj->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** Select any aircraft with the bounding box. + */ + for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { + AircraftClass * aircraft = Aircraft.Ptr(air_index); + COORDINATE ocoord = Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -Pixel_To_Lepton(aircraft->Altitude))); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are owned by the player, are allowed to be + ** selected, and are within the bounding box. + */ + if ( aircraft->Class_Of().IsSelectable && + !aircraft->Is_Selected_By_Player() && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = aircraft->Owner() == PlayerPtr->Class->House; + AllowVoice &= is_player_controlled; + if (aircraft->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** If a mix of player and non-player controlled units were selected, make sure non-player controlled units are de-selected + */ + bool player_controlled_units = false, non_player_controlled_units = false; + for (int i = 0; (i < CurrentObject.Count()) && (!player_controlled_units || !non_player_controlled_units); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (CurrentObject[i]->Owner() == PlayerPtr->Class->House) { + player_controlled_units = true; + } + else { + non_player_controlled_units = true; + } + } + if (player_controlled_units && non_player_controlled_units) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (CurrentObject[i]->Owner() != PlayerPtr->Class->House) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + + /* + ** If player-controlled units are non-additively selected, remove harvesters and MCVs if they aren't the only types of units selected + */ + if (!additive && player_controlled_units) { + bool any_to_exclude = false, all_to_exclude = true; + for (int i = 0; i < CurrentObject.Count(); ++i) { + bool exclude = should_exclude_from_selection(CurrentObject[i]); + any_to_exclude |= exclude; + all_to_exclude &= exclude; + } + if (any_to_exclude && !all_to_exclude) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (should_exclude_from_selection(CurrentObject[i])) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + } + + AllowVoice = true; +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * * + * Use this routine to flag all cells that are covered in some fashion by the multi-unit * + * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * + * size or is being removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Refresh_Band(void) +{ + if (IsRubberBand) { + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + int x1 = BandX+TacPixelX; + int y1 = BandY+TacPixelY; + int x2 = NewX+TacPixelX; + int y2 = NewY+TacPixelY; + + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + CELL cell; + for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { + cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * * + * This routine handles the input directed at the tactical map. Since input, in this * + * regard, includes even the presence of the mouse over the tactical map, this routine * + * is called nearly every game frame. It handles adjusting the mouse shape as well as * + * giving orders to units. * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/17/1995 JLB : Created. * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + int x,y; // Sub cell pixel coordinates. + bool shadow; + ObjectClass *object = 0; + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + bool edge = false; + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); +// CELL cell = Map.Click_Cell_Calc(x, y); + if (coord) { + shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { +// int xxx = x + Lepton_To_Pixel(Coord_XLepton(Map.TacticalCoord)); +// int yyy = y + Lepton_To_Pixel(Coord_YLepton(Map.TacticalCoord)); +// object = Map.Cell_Object(cell, xxx % CELL_PIXEL_W, yyy % CELL_PIXEL_H); + object = Map.Close_Object(coord); + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ +// if (object && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && ((TechnoClass *)object)->Cloak == CLOAKED) { +// object = NULL; +// } + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = Best_Object_Action(object); + } else { + action = Best_Object_Action(cell); + } + } else { + if (object && object->Class_Of().IsSelectable) { + action = ACTION_SELECT; + } + + if (Map.IsRepairMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { + action = ACTION_REPAIR; + } else { + action = ACTION_NO_REPAIR; + } + } + + if (Map.IsSellMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { + if (object->What_Am_I() == RTTI_BUILDING) { + action = ACTION_SELL; + } else { + action = ACTION_SELL_UNIT; + } + } else { + + /* + ** Check to see if the cursor is over an owned wall. + */ + if (Map[cell].Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && + Map[cell].Owner == PlayerPtr->Class->House) { + action = ACTION_SELL; + } else { + action = ACTION_NO_SELL; + } + } + } + + if (Map.IsTargettingMode == SPC_ION_CANNON) { + action = ACTION_ION; + } + + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + action = ACTION_NUKE_BOMB; + } + + if (Map.IsTargettingMode == SPC_AIR_STRIKE) { + action = ACTION_AIR_STRIKE; + } + + if (Map.PendingObject) { + action = ACTION_NONE; + } + } + + /* + ** Move any cursor displayed. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** A right mouse button press cancels the current action or selection. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** Make sure that if the mouse button has been released and the map doesn't know about it, + ** then it must be informed. Do this by faking a mouse release event. + */ + if ((flags & LEFTUP) && Map.IsRubberBand) { + flags |= LEFTRELEASE; + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (!edge) { + if (flags & LEFTUP) { + Map.Mouse_Left_Up(shadow, object, action); + } + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTRELEASE) { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + + /* + ** When the mouse is first pressed on the map, then record the mouse + ** position so that a proper check before going into rubber band + ** mode can be made. Rubber band mode starts when the mouse is + ** held down and moved a certain minimum distance. + */ + if (!edge && (flags & LEFTPRESS)) { + Map.Mouse_Left_Press(x, y); + } + + /* + ** While the mouse is being held down, determine if rubber band mode should + ** start. If rubber band mode is already active, then update the size + ** and flag the map to redraw it. + */ + if (flags & LEFTHELD) { + Map.Mouse_Left_Held(x, y); + } + } + + return(GadgetClass::Action(0, key)); +} + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Selection_At_Mouse -- Object selection * + * * + * Selects any objects at the current mouse position. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Selection_At_Mouse(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + if (coord) { + bool shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (object != nullptr) + { + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + if (shiftdown) + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_TOGGLE_SELECT); + } + else + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_SELECT); + } + + } + else + { + Unselect_All(); + } + } + + return 0; +} + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Command_Object -- Commanding Units * + * * + * Issues a command to the currently selected unit. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Command_Object(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + ActionType action = ACTION_NONE; + + if (coord) { + bool shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (CurrentObject.Count()) { + if (object) { + action = Best_Object_Action(object); + } + else { + action = Best_Object_Action(cell); + } + } + + if (action != ACTION_SELECT) + { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + } + return 0; +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * * + * This routine is called when the right mouse button is pressed. This action is supposed * + * to cancel whatever mode or process is active. If there is nothing to cancel, then it * + * will default to unselecting any units that might be currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Right_Press(void) +{ + if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { + //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + } else { + if (IsRepairMode) { + IsRepairMode = false; + } else { + if (IsSellMode) { + IsSellMode = false; + } else { + if (IsTargettingMode) { + IsTargettingMode = false; + } else { + Unselect_All(); + } + } + } + } + Set_Default_Mouse(MOUSE_NORMAL, false); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * * + * This routine is called continuously while the mouse is over the tactical map but there * + * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * + * help text. * + * * + * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * + * * + * object -- Pointer to the object that the mouse is currently over (may be NULL). * + * * + * action -- This is the action that the currently selected object (if any) will * + * perform if the left mouse button were clicked at this location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Up(bool shadow, ObjectClass * object, ActionType action, bool wwsmall) +{ + IsTentative = false; + + /* + ** Don't allow selection of an object that is located in shadowed terrain. + ** In fact, just show the normal move cursor in order to keep the shadowed + ** terrain a mystery. + */ + if (shadow) { + switch (action) { + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wwsmall); + break; + + case ACTION_NONE: + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + break; + + case ACTION_NO_SELL: + case ACTION_SELL: + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wwsmall); + break; + + case ACTION_NO_REPAIR: + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wwsmall); + break; + + case ACTION_ION: + Set_Default_Mouse(MOUSE_ION_CANNON, wwsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wwsmall); + break; + + case ACTION_AIR_STRIKE: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wwsmall); + break; + + case ACTION_NOMOVE: + if (CurrentObject.Count()) { + MouseType mouse_type = MOUSE_NO_MOVE; + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (CurrentObject[i]->What_Am_I() != RTTI_AIRCRAFT) { + mouse_type = MOUSE_CAN_MOVE; + break; + } + } + Set_Default_Mouse(mouse_type, wwsmall); + break; + } + // Fall into next case for non aircraft object types. + + default: + Set_Default_Mouse(MOUSE_CAN_MOVE, wwsmall); + break; + + } + } else { + + /* + ** Change the mouse shape according to the default action that will occur + ** if the mouse button were clicked at this location. + */ + switch (action) { + case ACTION_TOGGLE_SELECT: + case ACTION_SELECT: + Set_Default_Mouse(MOUSE_CAN_SELECT, wwsmall); + break; + + case ACTION_MOVE: + Set_Default_Mouse(MOUSE_CAN_MOVE, wwsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wwsmall); + break; + + case ACTION_HARVEST: + case ACTION_ATTACK: + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + break; + + case ACTION_SABOTAGE: + Set_Default_Mouse(MOUSE_DEMOLITIONS, wwsmall); + break; + + case ACTION_ENTER: + case ACTION_CAPTURE: + Set_Default_Mouse(MOUSE_ENTER, wwsmall); + break; + + case ACTION_NOMOVE: + Set_Default_Mouse(MOUSE_NO_MOVE, wwsmall); + break; + + case ACTION_NO_SELL: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wwsmall); + break; + + case ACTION_NO_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wwsmall); + break; + + case ACTION_SELF: + Set_Default_Mouse(MOUSE_DEPLOY, wwsmall); + break; + + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_REPAIR, wwsmall); + break; + + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_SELL_UNIT, wwsmall); + break; + + case ACTION_SELL: + Set_Default_Mouse(MOUSE_SELL_BACK, wwsmall); + break; + + case ACTION_ION: + Set_Default_Mouse(MOUSE_ION_CANNON, wwsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wwsmall); + break; + + case ACTION_AIR_STRIKE: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wwsmall); + break; + + case ACTION_TOGGLE_PRIMARY: + Set_Default_Mouse(MOUSE_DEPLOY, wwsmall); + break; + + default: + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + break; + } + } +#if 0 + /* + ** Give a generic help message when over shadow terrain. + */ + if (shadow) { + if (Scenario < 4) { + Help_Text(TXT_SHADOW); + } else { + Help_Text(TXT_NONE); + } + } else { + + /* + ** If the mouse is held over objects on the map, then help text may + ** pop up that tells what the object is. This call informs the help + ** system of the text name for the object under the mouse. + */ + if (object) { + int text; + int color = LTGREY; + + /* + ** Fetch the appropriate background color for help text. + */ + if (PlayerPtr->Is_Ally(object)) { + color = CC_GREEN; + } else { + if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { + color = LTGREY; + } else { + color = PINK; + } + } + + /* + ** Fetch the name of the object. If it is an enemy object, then + ** the exact identity is glossed over with a generic text. + */ + text = object->Full_Name(); + if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) { + + if (!PlayerPtr->Is_Ally(object)) { + switch (object->What_Am_I()) { + case RTTI_INFANTRY: + text = TXT_ENEMY_SOLDIER; + break; + + case RTTI_UNIT: + text = TXT_ENEMY_VEHICLE; + break; + + case RTTI_BUILDING: + if ( *((BuildingClass*)object) != STRUCT_MISSION) { + text = TXT_ENEMY_STRUCTURE; + } + break; + } + } + } + + if (Scenario > 3 || object->What_Am_I() != RTTI_TERRAIN) { + Help_Text(text, -1, -1, color); + } else { + Help_Text(TXT_NONE); + } + } else { + Help_Text(TXT_NONE); + } + } +#endif +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * * + * This routine is called when the left mouse button is released over the tactical map. * + * The release event is the workhorse of the game. Most actions occur at the moment of * + * mouse release. * + * * + * INPUT: cell -- The cell that the mouse is over. * + * * + * x,y -- The mouse pixel coordinate. * + * * + * object -- Pointer to the object that the mouse is over. * + * * + * action -- The action that the currently selected object (if any) will * + * perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 03/27/1995 JLB : Handles sell and repair actions. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wwsmall) +{ + if (PendingObjectPtr) { + + /* + ** Try to place the pending object onto the map. + */ + if (ProximityCheck) { + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); + } else { + Speak(VOX_DEPLOY); + } + + } else { + + if (IsRubberBand) { + Refresh_Band(); + Select_These(XYPixel_Coord(BandX, BandY), XYPixel_Coord(x, y)); + + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); +#ifdef NEVER + if (CurrentObject.Count()) { + if (CurrentObject[0]->Can_Player_Fire()) { + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } +#endif + + IsRubberBand = false; + IsTentative = false; + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + + } else { + + /* + ** Toggle the select state of the object. + */ + if (action == ACTION_TOGGLE_SELECT) { + if (!object || !CurrentObject.Count()) { + action = ACTION_SELECT; + } else { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } else { + object->Select(); + } + } + } + + /* + ** Selection of other object action. + */ + if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->Is_Selected_By_Player())) { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } + if (object->Select()) { + Unselect_All_Except(object); + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } +#ifdef NEVER + if (object->Can_Player_Fire()) { + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } +#endif + } + + /* + ** If an action was detected as possible, then pass this action event + ** to all selected objects. + */ + if (action != ACTION_NONE && action != ACTION_SELECT && action != ACTION_TOGGLE_SELECT) { + + /* + ** Pass the action to all the selected objects. But first, redetermine + ** what action that object should perform. This, seemingly redundant + ** process, is necessary since multiple objects could be selected and each + ** might perform a different action when the click occurs. + */ + bool doflash = true; + AllowVoice = true; + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass * tobject = CurrentObject[index]; + if (object) { + tobject->Active_Click_With(tobject->What_Action(object), object); + } else { + tobject->Active_Click_With(tobject->What_Action(cell), cell); + } + AllowVoice = false; + } + AllowVoice = true; + + if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { + OutList.Add(EventClass(EventClass::REPAIR, object->As_Target())); + } + if (action == ACTION_SELL_UNIT && object) { + switch (object->What_Am_I()) { + case RTTI_AIRCRAFT: + case RTTI_UNIT: + OutList.Add(EventClass(EventClass::SELL, object->As_Target())); + break; + + default: + break; + } + + } + if (action == ACTION_SELL) { + if (object) { + OutList.Add(EventClass(EventClass::SELL, object->As_Target())); + } else { + OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); + } + } + if (action == ACTION_ION) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_ION_CANNON, cell)); + } + if (action == ACTION_NUKE_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); + } + if (action == ACTION_AIR_STRIKE) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_AIR_STRIKE, cell)); + } + } + + IsTentative = false; + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * * + * Handle the left mouse button press while over the tactical map. If it isn't is * + * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * + * mouse moves a sufficient distance from this recorded position, then rubber band mode * + * is officially started. * + * * + * INPUT: x,y -- The mouse coordinates at the time of the press. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Press(int x, int y) +{ + if (!IsRepairMode && !IsSellMode && !IsTargettingMode && !PendingObject) { + IsTentative = true; + BandX = x; + BandY = y; + NewX = x; + NewY = y; + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * * + * This routine is called continuously while the left mouse button is held down over * + * the tactical map. This handles the rubber band mode detection and dragging. * + * * + * INPUT: x,y -- The mouse coordinate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Held(int x, int y) +{ + if (IsRubberBand) { + if (x != NewX || y != NewY) { + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + Refresh_Band(); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } else { + + /* + ** If the mouse is still held down while a tentative extended select is possible, then + ** check to see if the mouse has moved a sufficient distance in order to activate + ** extended select mode. + */ + if (IsTentative) { + + /* + ** The mouse must have moved a minimum distance before rubber band mode can be + ** initiated. + */ + if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { + IsRubberBand = true; + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + } +} + + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +extern int GlyphXClientSidebarWidthInLeptons; + +/*********************************************************************************************** + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * * + * This routine is used to set the tactical view position. The requested position is * + * clipped to the map dimensions as necessary. * + * * + * INPUT: coord -- The coordinate desired for the upper left corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Set_Tactical_Position(COORDINATE coord) +{ + /* + ** Bound the desired location to fit the legal map edges. + */ + int xx = 0;// Coord_X(coord) - Cell_To_Lepton(MapCellX); + int yy = 0;// Coord_Y(coord) - Cell_To_Lepton(MapCellY); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth) + GlyphXClientSidebarWidthInLeptons, Cell_To_Lepton(MapCellHeight)); // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + if (ScenarioInit) { + TacticalCoord = coord; + } + DesiredTacticalCoord = coord; + IsToRedraw = true; + Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * * + * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * + * * + * INPUT: none * + * * + * OUTPUT: x, y -- Player starting location * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/28/1995 JLB : Commented. * + * 06/26/1995 JLB : Fixed building loop. * + *=============================================================================================*/ +void DisplayClass::Compute_Start_Pos(long& x, long& y) +{ + /* + ** Find the summation cell-x & cell-y for all the player's units, infantry, + ** and buildings. Buildings are weighted so that they count 16 times more + ** than units or infantry. + */ + x = 0; + y = 0; + long num = 0; + int i; + for (i = 0; i < Infantry.Count(); i++) { + InfantryClass * infp = Infantry.Ptr(i); + if (!infp->IsInLimbo && infp->House == PlayerPtr) { + x += (long)Coord_XCell (infp->Coord); + y += (long)Coord_YCell (infp->Coord); + num++; + } + } + + for (i = 0; i < Units.Count(); i++) { + UnitClass * unitp = Units.Ptr(i); + if (!unitp->IsInLimbo && unitp->House == PlayerPtr) { + x += (long)Coord_XCell (unitp->Coord); + y += (long)Coord_YCell (unitp->Coord); + num++; + } + } + + for (i = 0; i < Buildings.Count(); i++) { + BuildingClass * bldgp = Buildings.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->House == PlayerPtr) { + x += (((long)Coord_XCell (bldgp->Coord)) << 4); + y += (((long)Coord_YCell (bldgp->Coord)) << 4); + num += 16; + } + } + + /* + ** Divide each coord by 'num' to compute the average value + */ + if (num > 0) { + x /= num; + } else { + x = 0; + } + + if (num > 0) { + y /= num; + } else { + y = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * * + * This routine will control the sell mode for the player. * + * * + * INPUT: control -- The mode to set the sell state to. * + * 0 = Turn sell mode off. * + * 1 = Turn sell mode on. * + * -1 = Toggle sell mode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Sell_Mode_Control(int control) +{ + bool mode = IsSellMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsSellMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsSellMode && !PendingObject) { + IsRepairMode = false; + if (mode && PlayerPtr->BScan) { + IsSellMode = true; + Unselect_All(); + } else { + IsSellMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * * + * This routine is used to control the repair mode for the player. * + * * + * INPUT: control -- The mode to set the repair to. * + * 0 = Turn repair off. * + * 1 = Turn repair on. * + * -1= Toggle repair state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Repair_Mode_Control(int control) +{ + bool mode = IsRepairMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsRepairMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsRepairMode && !PendingObject) { + IsSellMode = false; + if (mode && PlayerPtr->BScan) { + IsRepairMode = true; + Unselect_All(); + } else { + IsRepairMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * * + * Use this routine to determine if the specified cell is visible on * + * the display. This is a useful fact, since many display operations * + * can be skipped if the cell is not visible. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: bool; Is this cell visible on the display? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DisplayClass::In_View(register CELL cell) +{ + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + COORDINATE tcoord = TacticalCoord & 0xFF00FF00L; + + if ((Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+255) return(false); + if ((Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+255) return(false); + return(true); + +#ifdef OBSOLETE + int fudgex = Coord_XLepton(TacticalCoord) ? -1 : 0; + int fudgey = Coord_YLepton(TacticalCoord) ? -1 : 0; + if ((unsigned)(Cell_X(cell)-Coord_XCell(TacticalCoord)) > Lepton_To_Cell(TacLeptonWidth)+fudgex) return(false); + if ((unsigned)(Cell_Y(cell)-Coord_YCell(TacticalCoord)) > Lepton_To_Cell(TacLeptonHeight)+fudgey) return(false); + return(true); +#endif + +#ifdef OBSOLETE + cell -= TacticalCell; + + if (Cell_X(cell) >= TacWidth + (TacPartialX ? 1 : 0)) return(false); + if (Cell_Y(cell) >= TacHeight + (TacPartialY ? 1 : 0)) return(false); + return(true); +#endif +} + + +COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + if (coord & 0xC000C000) { + return(0x00800080); + } + return (*this)[Coord_Cell(coord)].Closest_Free_Spot(coord, any); +} + + +bool DisplayClass::Is_Spot_Free(COORDINATE coord) const +{ + // This doesn't seem right... ST - 12/18/2018 10:09AM + //if (coord & 0xC000C000) { + // return(0x00800080); + //} + return (*this)[Coord_Cell(coord)].Is_Spot_Free(CellClass::Spot_Index(coord)); +} + + +/*********************************************************************************************** + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * * + * This routine will average the position of all the selected objects and then center * + * the map about those objects. * + * * + * INPUT: none * + * * + * OUTPUT: The center coordinate. * + * * + * WARNINGS: The map position changes by this routine. * + * * + * HISTORY: * + * 08/22/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE DisplayClass::Center_Map(void) +{ + if (CurrentObject.Count()) { + unsigned x = 0; + unsigned y = 0; + + for (int index = 0; index < CurrentObject.Count(); index++) { + COORDINATE coord = CurrentObject[index]->Center_Coord(); + + x += Coord_X(coord); + y += Coord_Y(coord); + } + + x /= CurrentObject.Count(); + y /= CurrentObject.Count(); + Set_Tactical_Position(XY_Coord(x - (TacLeptonWidth/2), y - (TacLeptonHeight/2))); + + return XY_Coord(x, y); + } + + return 0; +} + +static ActionType _priority_actions[] = { + ACTION_ATTACK, + ACTION_ENTER, + ACTION_REPAIR, + ACTION_SABOTAGE, + ACTION_CAPTURE, + ACTION_MOVE +}; + +static int get_action_priority(ActionType action) +{ + for (int i = 0; i < sizeof(_priority_actions) / sizeof(_priority_actions[0]); ++i) { + if (_priority_actions[i] == action) { + return i; + } + } + return INT_MAX; +} + +template +static int index_of(const DynamicVectorClass& list, T* object) +{ + for (int i = 0; i < list.Count(); i++) { + if (list[i] == object) { + return i; + } + } + return -1; +} + +template +static ObjectClass* Best_Object_With_ActionT(DynamicVectorClass& objects, T subject) +{ + DynamicVectorClass checked_types; + + if (objects.Count()) { + int best_priority = INT_MAX; + ObjectClass* best_object = objects[0]; + for (int i = 0; i < objects.Count(); ++i) { + ObjectClass* object = objects[i]; + const ObjectTypeClass* type = &object->Class_Of(); + if (index_of(checked_types, type) != -1) { + continue; + } + checked_types.Add(type); + ActionType action = object->What_Action(subject); + int priority = get_action_priority(action); + if (priority < best_priority) { + best_priority = priority; + best_object = object; + if (best_priority == 0) { + break; + } + } + } + return best_object; + } + return NULL; +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, ObjectClass* object) +{ + return Best_Object_With_ActionT(objects, object); +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell) +{ + return Best_Object_With_ActionT(objects, cell); +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, ObjectClass* object) +{ + ObjectClass* obj = Best_Object_With_Action(objects, object); + return (obj != NULL) ? obj->What_Action(object) : ACTION_NONE; +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell) +{ + ObjectClass* obj = Best_Object_With_Action(objects, cell); + return (obj != NULL) ? obj->What_Action(cell) : ACTION_NONE; +} + +ObjectClass* Best_Object_With_Action(ObjectClass* object) +{ + return Best_Object_With_Action(CurrentObject.Raw(), object); +} + +ObjectClass* Best_Object_With_Action(CELL cell) +{ + return Best_Object_With_Action(CurrentObject.Raw(), cell); +} + +ActionType Best_Object_Action(ObjectClass* object) +{ + return Best_Object_Action(CurrentObject.Raw(), object); +} + +ActionType Best_Object_Action(CELL cell) +{ + return Best_Object_Action(CurrentObject.Raw(), cell); +} \ No newline at end of file diff --git a/TIBERIANDAWN/DISPLAY.H b/TIBERIANDAWN/DISPLAY.H new file mode 100644 index 000000000..9f8580672 --- /dev/null +++ b/TIBERIANDAWN/DISPLAY.H @@ -0,0 +1,341 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\display.h_v 2.15 16 Oct 1995 16:47:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 1, 1994 * + * * + * Last Update : May 1, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "map.h" +#include "layer.h" + + +#define ICON_PIXEL_W 24 +#define ICON_PIXEL_H 24 +#define ICON_LEPTON_W 256 +#define ICON_LEPTON_H 256 +#define CELL_PIXEL_W ICON_PIXEL_W +#define CELL_PIXEL_H ICON_PIXEL_H +#define CELL_LEPTON_W ICON_LEPTON_W +#define CELL_LEPTON_H ICON_LEPTON_H + +// ----------------------------------------------------------- +#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W) +#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H) + + +extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2); + +class DisplayClass: public MapClass +{ + // Need access to shadow shapes + friend class DLLExportClass; + + public: + + /* + ** This indicates the theater that the display is to represent. + */ + TheaterType Theater; + + /* + ** The tactical map display position is indicated by the cell of the + ** upper left hand corner. These should not be altered directly. Use + ** the Set_Tactical_Position function instead. + */ + COORDINATE TacticalCoord; + + /* + ** The dimensions (in cells) of the visible window onto the game map. This tactical + ** map is how the player interacts and views the game world. + */ + int TacLeptonWidth; + int TacLeptonHeight; + + /* + ** These layer control elements are used to group the displayable objects + ** so that proper overlap can be obtained. + */ + static LayerClass Layer[LAYER_COUNT]; + + /* + ** This records the position and shape of a placement cursor to display + ** over the map. This cursor is used when placing buildings and also used + ** extensively by the scenario editor. + */ + CELL ZoneCell; + short ZoneOffset; + short const *CursorSize; + short CursorShapeSave[256]; // For save/load + bool ProximityCheck; // Is proximity check ok? + + /* + ** This holds the building type that is about to be placed upon the map. + ** It is only valid during the building placement state. The PendingLegal + ** flag is updated as the cursor moves and it reflects the legality of + ** placing the building at the desired location. + */ + ObjectClass * PendingObjectPtr; + ObjectTypeClass const * PendingObject; + HousesType PendingHouse; + + static unsigned char FadingBrighten[256]; + static unsigned char FadingShade[256]; + static unsigned char FadingLight[256]; + static unsigned char RemapTables[HOUSE_COUNT][3][256]; + static unsigned char FadingGreen[256]; + static unsigned char FadingYellow[256]; + static unsigned char FadingRed[256]; + static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256]; + static unsigned char WhiteTranslucentTable[(1+1)*256]; + static unsigned char MouseTranslucentTable[(4+1)*256]; + static void const *TransIconset; + static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256]; + static unsigned char SpecialGhost[2*256]; + + //------------------------------------------------------------------------- + DisplayClass(void); + + virtual void Read_INI(char *buffer); + void Write_INI(char *buffer); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** General display/map/interface support functionality. + */ + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + /* + ** Added functionality. + */ + COORDINATE Center_Map(void); + virtual bool Map_Cell(CELL cell, HouseClass *house, bool and_for_allies); + + virtual CELL Click_Cell_Calc(int x, int y); + virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false, int =0) {}; + virtual MouseType Get_Mouse_Shape(void) const = 0; + virtual bool Scroll_Map(DirType facing, int & distance, bool really); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1); + + /* + ** Pending object placement control. + */ + virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system. + void Cursor_Mark(CELL pos, bool on); + void Set_Cursor_Shape(short const * list); + CELL Set_Cursor_Pos(CELL pos = -1); + void Get_Occupy_Dimensions(int & w, int & h, short const *list); + + /* + ** Tactical map only functionality. + */ + virtual void Set_Tactical_Position(COORDINATE coord); + void Refresh_Band(void); + void Select_These(COORDINATE coord1, COORDINATE coord2, bool additive = false); + COORDINATE Pixel_To_Coord(int x, int y); + bool Coord_To_Pixel(COORDINATE coord, int &x, int &y); + bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest); + void Remove(ObjectClass const *object, LayerType layer); + void Submit(ObjectClass const *object, LayerType layer); + CELL Calculated_Cell(SourceType dir, HousesType house); + bool In_View(register CELL cell); + bool Passes_Proximity_Check(ObjectTypeClass const *object); +#ifdef USE_RA_AI + bool Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const; +#endif + ObjectClass * Cell_Object(CELL cell, int x=0, int y=0); + ObjectClass * Next_Object(ObjectClass * object); + ObjectClass * Prev_Object(ObjectClass * object); + int Cell_Shadow(CELL cell, HouseClass *house); + short const * Text_Overlap_List(char const * text, int x, int y, int lines = 1); + bool Is_Spot_Free(COORDINATE coord) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + void Sell_Mode_Control(int control); + void Repair_Mode_Control(int control); + + void Flag_Cell(CELL cell) { + Flag_To_Redraw(false); + IsToRedraw = true; + CellRedraw[cell] = true; + }; + bool Is_Cell_Flagged(CELL cell) const {return CellRedraw.Is_True(cell);}; + + /* + ** Computes starting position based on player's units' Coords. + */ + void Compute_Start_Pos(long& x, long& y); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + virtual void Mouse_Right_Press(void); + virtual void Mouse_Left_Press(int x, int y); + virtual void Mouse_Left_Up(bool shadow, ObjectClass * object, ActionType action, bool wwsmall = false); + virtual void Mouse_Left_Held(int x, int y); + virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wwsmall = false); + + public: + /* + ** This is the pixel offset for the upper left corner of the tactical map. + */ + int TacPixelX; + int TacPixelY; + + /* + ** This is the coordinate that the tactical map should be in at next available opportunity. + */ + COORDINATE DesiredTacticalCoord; + + /* + ** If something in the tactical map is to be redrawn, this flag is set to true. + */ + unsigned IsToRedraw:1; + + /* + ** If the player is currently wielding a wrench (to select buildings for repair), + ** then this flag is true. In such a state, normal movement and combat orders + ** are preempted. + */ + unsigned IsRepairMode:1; + + /* + ** If the player is currently in "sell back" mode, then this flag will be + ** true. While in this mode, anything clicked on will be sold back to the + ** "factory". + */ + unsigned IsSellMode:1; + + /* + ** If the player is currently in ion cannon targetting mode, then this + ** flag will be true. While in this mode, anything clicked on will be + ** be destroyed by the ION cannon. + */ + unsigned IsTargettingMode:2; + + protected: + + /* + ** If it is currently in rubber band mode (multi unit selection), then this + ** flag will be true. While in such a mode, normal input is prempted while + ** the extended selection is in progress. + */ + unsigned IsRubberBand:1; + + /* + ** The moment the mouse is held down, this flag gets set. If the mouse is dragged + ** a sufficient distance while held down, then true rubber band mode selection + ** can begin. Using a minimum distance prevents accidental rubber band selection + ** mode from being initiated. + */ + unsigned IsTentative:1; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + int Selection_At_Mouse(unsigned flags, KeyNumType & key); + int Command_Object(unsigned flags, KeyNumType & key); + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ +public: //ST - 1/21/2019 11:59AM + static TacticalClass TacButton; + + private: + + /* + ** This is a utility flag that is set during the icon draw process only if there + ** was at least one shadow icon detected that should be redrawn. When the shadow + ** drawing logic is to take place, but this flag is false, then the shadow drawing + ** will be skipped since it would perform no function. + */ + unsigned IsShadowPresent:1; + + /* + ** Rubber band mode consists of stretching a box from the anchor point (specified + ** here) to the current cursor position. + */ + int BandX,BandY; + int NewX,NewY; + + static void const *ShadowShapes; + static unsigned char ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + + void Redraw_Icons(int draw_flags=0); + void Redraw_Shadow(void); + void Redraw_Shadow_Rects(void); + + /* + ** This bit array is used to flag cells to be redrawn. If the icon needs to + ** be redrawn for a cell, then the corresponding flag will be true. + */ + static BooleanVectorClass CellRedraw; + + // + // We need a way to bypass visible view checks when we are running in the context of GlyphX without using the + // internal C&C renderer. We shouldn't know or care what the user is actually looking at + // ST - 4/17/2019 9:01AM + // + static bool IgnoreViewConstraints; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/DLLInterface.cpp b/TIBERIANDAWN/DLLInterface.cpp new file mode 100644 index 000000000..b2962b9b4 --- /dev/null +++ b/TIBERIANDAWN/DLLInterface.cpp @@ -0,0 +1,7667 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** DLLInterfac.cpp +** +** This is where we implement the API expected by the Instance Server. +** +** The Instance Server will pass in requests for loading and starting maps, control input from players, +** and requests for game simulation and rendering states. +** +** +*/ + + + +#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" + + + + +/* +** Externs +*/ +extern int DLL_Startup(const char * command_line); +extern void Reallocate_Big_Shape_Buffer(void); +extern bool ProgEndCalled; +extern int Write_PCX_File(char* name, GraphicViewPortClass& pic, unsigned char* palette ); +extern bool Color_Cycle(void); + + + + +/* +** Prototypes and constants +*/ +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum); +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags = 0, void const * ghostdata = NULL); + +typedef void (__cdecl* CNC_Event_Callback_Type)(const EventCallbackStruct &event); +typedef unsigned __int64 uint64; +typedef __int64 int64; + + + + +/* +** Audio defines +** +** +** +** +** +*/ +// For compatibility with Watcom in audio enums +#pragma warning (disable : 4091) + +// From TiberianDawn\Audio.cpp +enum ContextType; +extern struct SoundEffectNameStruct { + char const *Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT]; + +// From TiberianDawn\Audio.cpp +extern char const* Speech[VOX_COUNT]; + +// From TiberianDawn\Audio.cpp +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_JUV, // Juvenile sound effect alternate option. + IN_VAR, // Infantry variance response modification. +}; + + + + +/* +** Misc defines +** +** +** +** +** +*/ +#define RANDOM_START_POSITION 0x7f + + + + + +/* +** DLL Interface +** +** +** +** +** +*/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in); +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback); +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules); +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, const char* scenario_name, int build_level, bool multiplayer); +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); +extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum mouse_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy); +extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players); +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time); + + + +/* +** Class to implement the interface, and contain additional game state required by the conversion from peer/peer to client/server +** +** +** +** +** +*/ +class DLLExportClass { + public: + + static void Init(void); + static void Shutdown(void); + static void Config(const CNCRulesDataStruct& rules); + static void Add_Mod_Path(const char *mod_path); + static void Set_Home_Cell(int x, int y, uint64 player_id); + static void Set_Content_Directory(const char *dir); + + static bool Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Start_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Hold_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Start_Placement(uint64 player_id, int buildable_type, int buildable_id); + static BuildingClass *Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id); + static bool Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static void Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out); + static void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE, int scale = 0x100); + static void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); + static void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame); + static bool Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); + static bool Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id); + static bool Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y); + static bool Create_Control_Group(unsigned char control_group_index); + static bool Add_To_Control_Group(unsigned char control_group_index); + static bool Toggle_Control_Group_Selection(unsigned char control_group_index); + static bool Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance); + static void Calculate_Start_Positions(void); + static void Computer_Message(bool last_player_taunt); + + static void Repair_Mode(uint64 player_id); + static void Repair(uint64 player_id, int object_id); + static void Sell_Mode(uint64 player_id); + static void Sell(uint64 player_id, int object_id); + static void Repair_Sell_Cancel(uint64 player_id); + + static void Scatter_Selected(uint64 player_id); + static void Select_Next_Unit(uint64 player_id); + static void Select_Previous_Unit(uint64 player_id); + static void Selected_Guard_Mode(uint64 player_id); + static void Selected_Stop(uint64 player_id); + static void Team_Units_Formation_Toggle_On(uint64 player_id); + static void Units_Queued_Movement_Toggle(uint64 player_id, bool toggle); + + static void Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output); + static bool Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + + + static void Set_Event_Callback(CNC_Event_Callback_Type event_callback) {EventCallback = event_callback;} + static void Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy = false); + static void Debug_Spawn_All(int x, int y); + static bool Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y); + static void Debug_Kill_Unit(int x, int y); + static void Debug_Heal_Unit(int x, int y); + + static void On_Play_Movie(const char * movie_name, ThemeType theme, bool immediate); + static void On_Display_Briefing_Text(); + + static void On_Sound_Effect(const HouseClass* player_ptr, int sound_effect_index, const char* extension, int variation, COORDINATE coord); + static void On_Speech(const HouseClass* player_ptr, int speech_index); + + static void On_Game_Over(uint64 glyphx_player_id, bool player_wins); + static void On_Multiplayer_Game_Over(void); + + static void On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id); + + static void On_Debug_Output(const char *debug_text); + + static void On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); + + static void On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y); + + static void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + + static void Glyphx_Queue_AI(); + + static void Force_Human_Team_Wins(uint64 quitting_player_id); + + /* + ** Player context switching for input/output + */ + static bool Set_Player_Context(uint64 glyphx_player, bool force = false); + static void Reset_Player_Context(void); + static void Adjust_Internal_View(bool force_ignore_view_constraints = false); + static void Logic_Switch_Player_Context(ObjectClass *object); + static void Logic_Switch_Player_Context(HouseClass *house); + static __int64 Get_GlyphX_Player_ID(const HouseClass *house); + + static void Recalculate_Placement_Distances(); + + static void Reset_Sidebars(void); + + static SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr = NULL); + + static uint64 GlyphxPlayerIDs[MAX_PLAYERS]; + + + + static const void *Get_Shadow_Shapes(void) {return Map.ShadowShapes;} + static const unsigned char *Get_Shadow_Trans(void) {return &Map.ShadowTrans[0];} + + static bool Legacy_Render_Enabled(void); + + static bool Get_Input_Key_State(KeyNumType key); + + static void Set_Special_Key_Flags(unsigned char special_key_flags); + static void Clear_Special_Key_Flags(); + + static bool Load(FileClass & file); + static bool Save(FileClass & file); + static void Code_Pointers(void); + static void Decode_Pointers(void); + + static bool Get_Game_Over() { return GameOver; } + + + private: + static void Calculate_Single_Player_Score(EventCallbackStruct&); + + static unsigned int TD_Calculate_Efficiency( unsigned int harvested_credits, unsigned int initial_credits, unsigned int available_credits ); + static unsigned int TD_Calculate_Leadership( int house, unsigned int units_lost, unsigned int buildings_lost ); + static unsigned int TD_Calculate_Score( unsigned int leadership, unsigned int efficiency, unsigned int build_level ); + + static void Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type); + + static void Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance); + + static int CurrentDrawCount; + static int TotalObjectCount; + static int SortOrder; + static CNCObjectListStruct *ObjectList; + + static CNC_Event_Callback_Type EventCallback; + + + static int CurrentLocalPlayerIndex; + + static bool GameOver; + + /* + ** Pseudo sidebars for players in multiplayer + */ + static SidebarGlyphxClass MultiplayerSidebars[MAX_PLAYERS]; + + static CELL MultiplayerStartPositions[MAX_PLAYERS]; + + static BuildingTypeClass *PlacementType[MAX_PLAYERS]; + + static unsigned char PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; + + static unsigned char SpecialKeyFlags[MAX_PLAYERS]; + + /* + ** Mod directories + */ + static DynamicVectorClass ModSearchPaths; + +}; + + +/* +** DLLExportClass static data +** +** +** +** +** +*/ +int DLLExportClass::CurrentDrawCount = 0; +int DLLExportClass::TotalObjectCount = 0; +int DLLExportClass::SortOrder = 0; +CNCObjectListStruct *DLLExportClass::ObjectList = NULL; +SidebarGlyphxClass DLLExportClass::MultiplayerSidebars [MAX_PLAYERS]; +uint64 DLLExportClass::GlyphxPlayerIDs[MAX_PLAYERS] = {0xffffffffl}; +int DLLExportClass::CurrentLocalPlayerIndex = -1; +CELL DLLExportClass::MultiplayerStartPositions[MAX_PLAYERS]; +BuildingTypeClass *DLLExportClass::PlacementType[MAX_PLAYERS]; +unsigned char DLLExportClass::PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; +unsigned char DLLExportClass::SpecialKeyFlags[MAX_PLAYERS] = { 0U }; +DynamicVectorClass DLLExportClass::ModSearchPaths; +bool DLLExportClass::GameOver = false; + + +/* +** Global variables +** +** +** +** +** +*/ +int DLLForceMouseX = 0; +int DLLForceMouseY = 0; + +CNC_Event_Callback_Type DLLExportClass::EventCallback = NULL; + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +int GlyphXClientSidebarWidthInLeptons = 0; + +bool MPlayerIsHuman[MAX_PLAYERS]; +int MPlayerTeamIDs[MAX_PLAYERS]; +int MPlayerStartLocations[MAX_PLAYERS]; + +bool ShareAllyVisibility = true; + + + + + + +void Play_Movie_GlyphX(const char * movie_name, ThemeType theme) +{ + if ((movie_name[0] == 'x' || movie_name[0] == 'X') && movie_name[1] == 0) { + return; + } + + DLLExportClass::On_Play_Movie(movie_name, theme, false); +} + + +void On_Sound_Effect(int sound_index, int variation, COORDINATE coord) +{ + // MBL 02.26.2019 + int voc = sound_index; + if (voc == VOC_NONE) + { + return; + } + + // MBL 02.26.2019 - Borrowed from AUDIO.CPP Sound_Effect() + // + #if 1 + char const * ext = ""; // ".AUD"; + if (Special.IsJuvenile && SoundEffectName[voc].Where == IN_JUV) { + ext = ".JUV"; + } else { + if (SoundEffectName[voc].Where == IN_VAR) { + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } + } + #endif + // END MBL + + DLLExportClass::On_Sound_Effect(PlayerPtr, sound_index, ext, variation, coord); +} + +// MBL 02.06.2020 +// void On_Speech(int speech_index) +void On_Speech(int speech_index, HouseClass *house) +{ + // DLLExportClass::On_Speech(PlayerPtr, speech_index); // MBL 02.06.2020 + if (house == NULL) { + DLLExportClass::On_Speech(PlayerPtr, speech_index); + } + else + { + DLLExportClass::On_Speech(house, speech_index); + } +} + + +void On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + DLLExportClass::On_Ping(player_ptr, coord); +} + + +void GlyphX_Debug_Print(const char *debug_text) +{ + DLLExportClass::On_Debug_Output(debug_text); +} + + +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + DLLExportClass::On_Achievement(player_ptr, achievement_type, achievement_reason); +} + + + + +/************************************************************************************************** +* CNC_Version -- Check DLL/Server version +* +* In: Version expected +* +* Out: Actual version +* +* +* +* History: 4/9/2020 2:12PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in) +{ + // Unreferenced, but potentially useful to know which version the server is expecting + version_in; + + return CNC_DLL_API_VERSION; +} + + + + +/************************************************************************************************** +* CNC_Init -- Initialize the .DLL +* +* In: Command line +* +* Out: +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback) +{ + DLLExportClass::Set_Content_Directory(NULL); + + DLL_Startup(command_line); + + // MBL + DLLExportClass::Set_Event_Callback( event_callback ); + + DLLExportClass::Init(); +} + + + +/************************************************************************************************** +* DLL_Shutdown -- Shutdown the .DLL +* +* In: +* +* Out: +* +* +* +* History: 2/20/2020 1:58PM - ST +**************************************************************************************************/ +void DLL_Shutdown(void) +{ + DLLExportClass::Shutdown(); +} + + + + + +/************************************************************************************************** +* CNC_Config -- Configure the plugin +* +* In: Configuration data +* +* Out: +* +* +* +* History: 10/03/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules) +{ + DLLExportClass::Config(rules); +} + + + + +/************************************************************************************************** +* CNC_Add_Mod_Path -- Add a path to load mod files from +* +* In: Path to load mods from +* +* Out: +* +* +* +* History: 2/20/2020 2:04PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path) +{ + DLLExportClass::Add_Mod_Path(mod_path); +} + + + + + +/************************************************************************************************** +* CNC_Get_Visible_Page -- Get the screen buffer 'SeenBuff' from the game +* +* In: If buffer_in is null, just return info about page +* +* Out: false if not changed since last call +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height) +{ + if (!DLLExportClass::Legacy_Render_Enabled() || (buffer_in == NULL)) { + return false; + } + + /* + ** Assume the seen page viewport is the same size as the page + */ + + GraphicBufferClass *gbuffer = HidPage.Get_Graphic_Buffer(); + if (gbuffer == NULL) { + return false; + } + + int view_port_width = Map.MapCellWidth * CELL_PIXEL_W; + int view_port_height = Map.MapCellHeight * CELL_PIXEL_H; + + if (view_port_width == 0 || view_port_height == 0) { + return false; + } + + unsigned char *raw_buffer = (unsigned char*) gbuffer->Get_Buffer(); + long raw_size = gbuffer->Get_Size(); + if (raw_buffer == NULL || gbuffer->Get_Width() < view_port_width || gbuffer->Get_Height() < view_port_height) { + return false; + } + + width = view_port_width; + height = view_port_height; + + int pitch = gbuffer->Get_Width(); + for (int i = 0; i < view_port_height; ++i, buffer_in += view_port_width, raw_buffer += pitch) { + memcpy(buffer_in, raw_buffer, view_port_width); + } + + return true; +} + + + + +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]) +{ + memcpy(palette_in, CurrentPalette, sizeof(palette_in)); + return true; +} + + + + +/************************************************************************************************** +* CNC_Set_Multiplayer_Data -- Set up for a multiplayer match +* +* In: Multiplayer data +* +* Out: false if data is bad +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players) +{ + + if (num_players <= 0) { + return false; + } + + if (num_players > min(MAX_PLAYERS, max_players)) { + return false; + } + + DLLExportClass::Init(); + + //MPlayerPrefColor; // preferred color index for this player + //MPlayerColorIdx; // actual color index of this player + //MPlayerHouse; // House of this player (GDI/NOD) + //MPlayerLocalID; // ID of this player + MPlayerCount = num_players; // # of human players in this game + MPlayerBases = game_options.MPlayerBases; // 1 = bases are on for this scenario + MPlayerCredits = game_options.MPlayerCredits; // # credits everyone gets + MPlayerTiberium = game_options.MPlayerTiberium; // 1 = tiberium enabled for this scenario + MPlayerGoodies = game_options.MPlayerGoodies; // 1 = goodies enabled for this scenario + MPlayerGhosts = game_options.MPlayerGhosts; // 1 = houses with no players will still play + MPlayerSolo = game_options.MPlayerSolo; // 1 = allows a single-player net game + MPlayerUnitCount = game_options.MPlayerUnitCount; // # units for non-base multiplayer scenarios + + Special.IsMCVDeploy = game_options.IsMCVDeploy; + Special.IsVisceroids = game_options.SpawnVisceroids; + Special.IsCaptureTheFlag = game_options.CaptureTheFlag; + Special.IsEarlyWin = game_options.DestroyStructures; + + Rule.AllowSuperWeapons = game_options.EnableSuperweapons; // Are superweapons available + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + Scenario = scenario_index; + MPlayerCount = 0; + + for (int i=0 ; i 0 && MPlayerStartLocations[i] == 0 && MPlayerStartLocations[0] == 0) { + MPlayerStartLocations[i] = i; + } + + MPlayerCount++; + } + + /* + ** We need some default for the local ID in order to have a valid PlayerPtr during scenario load. ST - 4/24/2019 10:33AM + */ + MPlayerLocalID = MPlayerID[0]; + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + Unselect_All(); + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + switch (object_type_id) + { + case INFANTRY: + { + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House == PlayerPtr + && Infantry.ID((InfantryClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case UNIT: + { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House == PlayerPtr + && Units.ID((UnitClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case AIRCRAFT: + { + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House == PlayerPtr + && Aircraft.ID((AircraftClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case BUILDING: + { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj + && !obj->IsInLimbo + && obj->House == PlayerPtr + && Buildings.ID((BuildingClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + } + + return false; +} + + +/************************************************************************************************** +* GlyphX_Assign_Houses -- Replacement for Assign_Houses in INI.CPP +* +* In: +* +* Out: +* +* +* +* History: 6/25/2019 11:09AM - ST +**************************************************************************************************/ +void GlyphX_Assign_Houses(void) +{ + HousesType house; + HousesType pref_house; + HouseClass *housep; + bool house_used[MAX_PLAYERS]; // true = this house is in use + bool color_used[16]; // true = this color is in use. We have more than 6 color options now, so bumped this to 16. ST - 6/19/2019 5:18PM + bool preassigned; + int i,j,random_start_location; + PlayerColorType color; + HousesType house2; + HouseClass *housep2; + + srand(timeGetTime()); + + /* + ** Init the 'used' flag for all houses & colors to 0 + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house_used[i] = false; + } + for (i = 0; i < 16; i++) { + color_used[i] = false; + } + + /* + ** Assign random start positions if needed. + */ + int random_start_locations[26]; + int num_start_locations = 0; + int num_random_start_locations = 0; + for (i = 0; i < 26; i++) { + if (Waypoint[i] != -1) { + preassigned = false; + for (j = 0; !preassigned && (j < MPlayerCount); j++) { + if (MPlayerStartLocations[j] == num_start_locations) { + preassigned = true; + } + } + if (!preassigned) { + random_start_locations[num_random_start_locations] = num_start_locations; + num_random_start_locations++; + } + num_start_locations++; + } + } + + if (num_random_start_locations > 1) { + for (i = 0; i < num_random_start_locations - 1; i++) { + j = i + rand() / (RAND_MAX / (num_random_start_locations - i) + 1); + int t = random_start_locations[j]; + random_start_locations[j] = random_start_locations[i]; + random_start_locations[i] = t; + } + } + + /* + ** For each player, randomly pick a house + */ + random_start_location = 0; + for (i = 0; i < MPlayerCount; i++) { + j = Random_Pick(0, MPlayerMax-1); + + /* + ** If this house was already selected, decrement 'i' & keep looping. + */ + if (house_used[j]) { + i--; + continue; + } + + /* + ** Set the house, preferred house (GDI/NOD), color, and actual house; + ** get a pointer to the house instance + */ + house = (HousesType)(j + (int)HOUSE_MULTI1); + pref_house = MPlayerID_To_HousesType(MPlayerID[i]); + color = MPlayerID_To_ColorIndex(MPlayerID[i]); + housep = HouseClass::As_Pointer(house); + MPlayerHouses[i] = house; + + /* + ** Mark this house & color as used + */ + house_used[j] = true; + color_used[color] = true; + + /* + ** Set the house's IsHuman, Credits, ActLike, & RemapTable + */ + memset((char *)housep->Name, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->Name, MPlayerNames[i], MPLAYER_NAME_MAX-1); + housep->IsHuman = MPlayerIsHuman[i]; + housep->Init_Data(color, pref_house, MPlayerCredits); + + /* + ** Set the start location override + */ + if (MPlayerStartLocations[i] != RANDOM_START_POSITION) { + housep->StartLocationOverride = MPlayerStartLocations[i]; + } else { + if (random_start_location < num_random_start_locations) { + housep->StartLocationOverride = random_start_locations[random_start_location++]; + } else { + housep->StartLocationOverride = -1; + } + } + + /* + ** If this ID is for myself, set up PlayerPtr + */ + if (MPlayerID[i] == MPlayerLocalID) { + PlayerPtr = housep; + } + } + + /* + ** From INI.CPP. Remove unused AI players. + */ + for (int i=0 ; iIsHuman == false) { + housep->Clobber_All(); + } + } + + for (i = 0; i < MPlayerCount; i++) { + + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer(house); + + if (housep) { + + int team = MPlayerTeamIDs[i]; + + for (int j=0 ; jMake_Ally(house2); + } + } + } + } + } + } +} + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map to use WITHOUT a sceanrio variation (SCEN_VAR) or scenarion direction (SCEN_DIR) +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* +* History: 7/10/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) +{ + return CNC_Start_Instance_Variation(scenario_index, (int)SCEN_VAR_NONE, (int)SCEN_DIR_EAST, build_level, faction, game_type, content_directory, sabotaged_structure, override_map_name); +} + + +/************************************************************************************************** +* HandleSabotagedStructure +* +* A port of the code from the original game code which is suppose to remove the main previously sabatoged building. +* From what I can tell since it only stores the type it might remove a different building of the same type. +* Watching the GDI longplay on YouTube the player destroys the refinery and yet it exists in the next level. Perhaps there are 2 refineries. +* +* History: 7/10/2019 - LLL +**************************************************************************************************/ +void HandleSabotagedStructure(int structure_type) +{ + SabotagedType = (StructType) structure_type; + + int index; + if (SabotagedType != STRUCT_NONE && Scenario == 7 && PlayerPtr->Class->House == HOUSE_GOOD) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House != PlayerPtr && building->Class->Type == SabotagedType) { + building->Limbo(); + delete building; + break; + } + } + + /* + ** Remove the building from the prebuild list. + */ + for (index = 0; index < Base.Nodes.Count(); index++) { + BaseNodeClass * node = Base.Get_Node(index); + + if (node && node->Type == SabotagedType) { + Base.Nodes.Delete(index); + break; + } + } + } + SabotagedType = STRUCT_NONE; +} + + + +/************************************************************************************************** +* CNC_Read_INI -- Load an ini file into the supplied buffer +* +* In: Map initialization parameters +* +* Out: false if ini load failed +* +* +* History: 12/16/2019 11:44AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size) +{ + if (content_directory == NULL) { + return false; + } + + DLLExportClass::Set_Content_Directory(content_directory); + + // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 + Scenario = (scenario_index == 81) ? 21 : scenario_index; + + ScenVar = (ScenarioVarType)scenario_variation; + ScenDir = (ScenarioDirType)scenario_direction; + + GameToPlay = GAME_GLYPHX_MULTIPLAYER; + ScenPlayer = SCEN_PLAYER_MPLAYER; + + if (override_map_name && strlen(override_map_name)) { + strcpy(ScenarioName, override_map_name); + } else { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, (ScenarioDirType)scenario_direction, (ScenarioVarType)scenario_variation); + } + + + if (_ini_buffer_size < _ShapeBufferSize) { + GlyphX_Debug_Print("INI file buffer may be too small"); + return false; + } + + if (!ini_buffer) { + GlyphX_Debug_Print("No INI file buffer"); + return false; + } + + memset(ini_buffer, _ini_buffer_size, 0); + + char fname[_MAX_PATH]; + + sprintf(fname,"%s.INI", ScenarioName); + CCFileClass file(fname); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(fname); + return(false); + + } else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(fname); + + int bytes_read = file.Read(ini_buffer, _ini_buffer_size-1); + if (bytes_read == _ini_buffer_size - 1) { + GlyphX_Debug_Print("INI file buffer is too small"); + return false; + } + } + + /* + ** Ini buffer should be zero terminated + */ + if ((int) strlen(ini_buffer) >= _ini_buffer_size) { + GlyphX_Debug_Print("INI file buffer overrun"); + return false; + } + + return true; +} + + +/************************************************************************************************** +* CNC_Set_Home_Cell -- Allows overriding the start position for the camera +* +* +* History: 2/14/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id) +{ + DLLExportClass::Set_Home_Cell(x, y, player_id); +} + + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* Renamed and modified to accept a scenario variation 7/10/2019 - LLL +* Modified to accept a scenario direction 7/12/2019 - LLL +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) +{ + if (game_type == NULL) { + return false; + } + + if (faction == NULL) { + return false; + } + + if (content_directory == NULL) { + return false; + } + + if (stricmp(faction, "GDI") == 0) { + ScenPlayer = SCEN_PLAYER_GDI; + Whom = HOUSE_GOOD; + } + + if (stricmp(faction, "NOD") == 0) { + ScenPlayer = SCEN_PLAYER_NOD; + Whom = HOUSE_BAD; + } + + if (stricmp(faction, "Jurassic") == 0) { + ScenPlayer = SCEN_PLAYER_JP; + Whom = HOUSE_JP; + Special.IsJurassic = true; + AreThingiesEnabled = true; + } + + DLLExportClass::Set_Content_Directory(content_directory); + + // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 + Scenario = (scenario_index == 81) ? 21 : scenario_index; + BuildLevel = build_level; + + SabotagedType = (StructType)sabotaged_structure; + + ScenVar = (ScenarioVarType)scenario_variation; + ScenDir = (ScenarioDirType)scenario_direction; + + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GameToPlay = GAME_NORMAL; + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GameToPlay = GAME_GLYPHX_MULTIPLAYER; + ScenPlayer = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + if (override_map_name && strlen(override_map_name)) { + strcpy(ScenarioName, override_map_name); + } else { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, (ScenarioDirType)scenario_direction, (ScenarioVarType)scenario_variation); + } + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + _Kbd->MouseQX = 100; + _Kbd->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + Seed = timeGetTime(); + + if (!Start_Scenario(ScenarioName)) { + return(false); + } + + HandleSabotagedStructure(sabotaged_structure); + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw(true); + Map.Render(); + + Set_Palette(GamePalette); + + return true; +} + + + + + +/************************************************************************************************** +* CNC_Start_Custom_Instance -- +* +* +* +* +* History: 2019/10/17 - JAS +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, + const char* scenario_name, int build_level, bool multiplayer) +{ + if (content_directory == NULL) { + return false; + } + + DLLExportClass::Set_Content_Directory(content_directory); + + if (multiplayer) { + GameToPlay = GAME_GLYPHX_MULTIPLAYER; + ScenPlayer = SCEN_PLAYER_MPLAYER; + } else { + GameToPlay = GAME_NORMAL; + ScenPlayer = SCEN_PLAYER_GDI; // Don't think it matters since we are specifying the exact file to load + } + + BuildLevel = build_level; + + const int MAX_FILE_PATH = 1024; + char scenario_file_name[MAX_FILE_PATH]; + char bin_file_name[MAX_FILE_PATH]; + snprintf(scenario_file_name, MAX_FILE_PATH, "%s%s.INI", directory_path, scenario_name); + snprintf(bin_file_name, MAX_FILE_PATH, "%s%s.BIN", directory_path, scenario_name); + + Seed = timeGetTime(); + + Clear_Scenario(); + + Read_Scenario_Ini_File(scenario_file_name, bin_file_name, scenario_name, true); + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + _Kbd->MouseQX = 100; + _Kbd->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + /* + if (!Start_Scenario(ScenarioName)) { + return(false); + } + */ + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw(true); + Map.Render(); + + Set_Palette(GamePalette); + + return true; +} + + +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + CCFileClass file; + + if (type->ImageData != NULL) { + + sprintf(buffer, "%s_%d", type->IniName, shapenum); + _makepath(fullname, NULL, NULL, buffer, ".PCX"); + + return Debug_Write_Shape(fullname, type->ImageData, shapenum); + } + + return false; +} + + +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags, void const * ghostdata) +{ + /* + ** Build frame returns a pointer now instead of the shapes length + */ + char *shape_pointer = (char*) Build_Frame(shapefile , shapenum , _ShapeBuffer); + if (shape_pointer == NULL) { + return false;; + } + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + return false;; + } + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + + GraphicBufferClass temp_gbuffer(width, height); + GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, width, height); + + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = width; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = height; + + static const char _shape_trans = 0x40; + + if (flags == 0) { + Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, SHAPE_NORMAL|SHAPE_WIN_REL|_shape_trans); //, ghostdata, predoffset); + } else { + Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, flags, ghostdata); + } + Write_PCX_File((char*)file_name, temp_viewport, GamePalette); + + return true; +} + + + +/************************************************************************************************** +* CNC_Advance_Instance -- Process one logic frame +* +* In: +* +* Out: Is game still playing? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id) +{ + //DLLExportClass::Set_Event_Callback(event_callback); + + InMainLoop = true; + + if (Frame <= 10) { // Don't spam forever, but useful to know that we actually started advancing + GlyphX_Debug_Print("CNC_Advance_Instance - TD"); + } + + /* + ** Shouldn't really need to do this, but I like the idea of always running the main loop in the context of the same player. + ** Might make tbe bugs more repeatable and consistent. ST - 3/15/2019 11:58AM + */ + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + } else { + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0]); + } + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + //if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + // Theme.Queue_Song(THEME_PICK_ANOTHER); + //} + + /* + ** Update the display, unless we're inside a dialog. + */ + //if (SpecialDialog == SDLG_NONE && GameInFocus) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + //Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + /* + ** The main loop passes these in uninitialized. ST - 2/7/2019 4:36PM + */ + KeyNumType input = KN_NONE; // Player input. + int x = 0; + int y = 0; + Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + /* + ** Process the sidebar. ST - 4/18/2019 11:59AM + */ + HouseClass *old_player_ptr = PlayerPtr; + for (int i=0 ; iErase_Mouse(&HidPage, TRUE); + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + + InMainLoop = false; + + GlyphX_Debug_Print("PlayerWins = true"); + + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, true); + } + + return false; + } + if (PlayerLoses) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + + //Do_Lose(); //Old C&C code + GlyphX_Debug_Print("PlayerLoses = true"); + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, false); + } + + InMainLoop = false; + + return false; + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Very rarely, the human players will get a message from the computer. + */ + if (GameToPlay != GAME_NORMAL && MPlayerGhosts && IRandom(0,10000) == 1) { + DLLExportClass::Computer_Message(false); + } + + /* + ** The code is often leaving dangling pointers in overlappers. We can afford the CPU time to just clean them up. I suspect + ** the underlying cause was probably fixed in RA. + ** ST - 4/14/2020 11:45AM + */ + Map.Clean(); + +#ifndef NDEBUG + /* + ** Is there a memory trasher altering the map?? + */ + if (!Map.Validate()) { + GlyphX_Debug_Print("Map.Validate() failed"); + + //if (CCMessageBox().Process ("Map Error!","Stop","Continue")==0) { + // GameActive = false; + //} + Map.Validate(); // give debugger a chance to catch it + } +#endif NDEBUG + + InMainLoop = false; + + if (ProgEndCalled) { + GlyphX_Debug_Print("ProgEndCalled - GameActive = false"); + GameActive = false; + } + + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + + //Sync_Delay(); + Color_Cycle(); + //DLLExportClass::Set_Event_Callback(NULL); + return(GameActive); +} + + +/************************************************************************************************** +* CNC_Save_Load -- Process a save or load game action +* +* In: +* +* Out: Success? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type) +{ + bool result = false; + + if (save) { + result = Save_Game(file_path_and_name, "internal"); + } else { + + if (game_type == NULL) { + return false; + } + + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GameToPlay = GAME_NORMAL; + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GameToPlay = GAME_GLYPHX_MULTIPLAYER; + ScenPlayer = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + result = Load_Game(file_path_and_name); + + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true); + Set_Logic_Page(SeenBuff); + VisiblePage.Clear(); + Map.Flag_To_Redraw(true); + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + Set_Palette(GamePalette); + } + + return result; +} + + + +/************************************************************************************************** +* CNC_Set_Difficulty -- Set game difficulty +* +* In: +* +* Out: +* +* +* +* History: 10/02/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty) +{ + if (GameToPlay == GAME_NORMAL) { + Set_Scenario_Difficulty(difficulty); + } +} + + +/************************************************************************************************** +* CNC_Handle_Player_Switch_To_AI -- Renamed 3/9/20202 - LLL +* previously named: CNC_Handle_Player_Disconnect -- Handle player disconnected during multiplayuer game +* +* In: +* +* Out: +* +* +* +* History: 12/3/2019 1:46PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id) +{ + if (PlayerWins || PlayerLoses || DLLExportClass::Get_Game_Over()) { + return; + } + + HousesType house; + HouseClass *ptr; + + GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI"); + + if (GameToPlay == GAME_NORMAL) { + return; + } + + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + + if (PlayerPtr) { + PlayerPtr->WasHuman = true; + PlayerPtr->IsHuman = false; + PlayerPtr->IsStarted = true; + PlayerPtr->IQ = Rule.MaxIQ; + PlayerPtr->IsBaseBuilding = true; + + /* + ** Start the unload mission for MCVs + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (*obj == UNIT_MCV) { + obj->Assign_Mission(MISSION_GUARD); + obj->Assign_Target(TARGET_NONE); + obj->Assign_Destination(TARGET_NONE); + obj->Assign_Mission(MISSION_UNLOAD); + obj->Commence(); + } + } + } + + DLLExportClass::On_Message(PlayerPtr, "", 60.0f, MESSAGE_TYPE_PLAYER_DISCONNECTED, -1); + + /* + ** Send the disconnect taunt message + */ + int human_count = 0; + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (ptr && ptr->IsHuman && !ptr->IsDefeated) { + human_count++; + } + } + + if (human_count == 1) { + DLLExportClass::Computer_Message(true); + } + } + } +} + + +/************************************************************************************************** +* CNC_Handle_Human_Team_Wins +* +* History: 3/10/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 quitting_player_id) +{ + GlyphX_Debug_Print("CNC_Handle_Human_Team_Wins"); + DLLExportClass::Force_Human_Team_Wins(quitting_player_id); +} + + +/************************************************************************************************** +* CNC_Start_Mission_Timer +* +* History: 11/25/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time) +{ + //Only implemented in Red Alert. +} + + + +/************************************************************************************************** +* DLLExportClass::Init -- Init the class +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Init(void) +{ + for (int i=0 ; i= VOC_FIRST && sound_effect_index < VOC_COUNT ) + { + strncpy( new_event.SoundEffect.SoundEffectName, SoundEffectName[ sound_effect_index ].Name, CNC_OBJECT_ASSET_NAME_LENGTH); + new_event.SoundEffect.SoundEffectName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncpy can leave strings unterminated + if ( extension != NULL ) + { + strncat( new_event.SoundEffect.SoundEffectName, extension, CNC_OBJECT_ASSET_NAME_LENGTH); + new_event.SoundEffect.SoundEffectName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncat can leave strings unterminated + } + new_event.SoundEffect.SoundEffectPriority = SoundEffectName[ sound_effect_index ].Priority; + new_event.SoundEffect.SoundEffectContext = SoundEffectName[ sound_effect_index ].Where; + } + else + { + strncpy( new_event.SoundEffect.SoundEffectName, "BADINDEX", CNC_OBJECT_ASSET_NAME_LENGTH); + new_event.SoundEffect.SoundEffectPriority = -1; + new_event.SoundEffect.SoundEffectContext = -1; + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Speech -- Called when C&C wants to play a speech line +* +* In: +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Speech(const HouseClass* player_ptr, int speech_index) +{ + // player_ptr could be NULL + + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_SPEECH; + new_event.Speech.SpeechIndex = speech_index; + + new_event.GlyphXPlayerID = 0; + if ( player_ptr != NULL ) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + if ( speech_index >= VOX_FIRST && speech_index < VOX_COUNT ) + { + strncpy( new_event.Speech.SpeechName, Speech[ speech_index ], CNC_OBJECT_ASSET_NAME_LENGTH); + new_event.Speech.SpeechName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncpy can leave strings unterminated + } + else + { + strncpy( new_event.Speech.SpeechName, "BAD_SPEECH_INDEX", CNC_OBJECT_ASSET_NAME_LENGTH); + } + + EventCallback(new_event); +} + +/************************************************************************************************** +* DLLExportClass::TD_Calculate_Efficiency -- +* +* History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +unsigned int DLLExportClass::TD_Calculate_Efficiency( unsigned int harvested_credits, unsigned int initial_credits, unsigned int available_credits ) +{ + unsigned efficiency = Cardinal_To_Fixed( harvested_credits + (initial_credits + 1), (available_credits + 1) ); + if ( efficiency == 0 ) { + efficiency++; + } + + efficiency = Fixed_To_Cardinal(100, efficiency); + if (efficiency > 100) { + efficiency = 100; + } + + return efficiency; +} + +/************************************************************************************************** +* DLLExportClass::TD_Calculate_Leadership -- +* +* History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +unsigned int DLLExportClass::TD_Calculate_Leadership( int house, unsigned int units_lost, unsigned int buildings_lost ) +{ + unsigned int leadership = 0; + + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + if (object->Owner() == house) { + leadership++; + } + } + + if (leadership == 0) { + leadership = 1; + } + + leadership = Cardinal_To_Fixed(units_lost + buildings_lost + leadership, leadership); + + leadership = Fixed_To_Cardinal(100, leadership); + if (leadership > 100) { + leadership = 100; + } + + return leadership; +} + +/************************************************************************************************** +* DLLExportClass::TD_Calculate_Score -- +* +* History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +unsigned int DLLExportClass::TD_Calculate_Score( unsigned int leadership, unsigned int efficiency, unsigned int build_level ) +{ + long total = ((leadership * 40) + (4600) + (efficiency * 14)) / 100; + if (!total) total++; + total *= (build_level + 1); + + return total; +} + +void DLLExportClass::Calculate_Single_Player_Score(EventCallbackStruct& event) +{ + //Adapted from Tiberian Dawn SCORE.CPP Presentation() - LLL + int house = PlayerPtr->Class->House; // 0 or 1 + + HouseClass *houses[3]; + for (int index = 0; index < 3; index++) { + houses[index] = (HouseClass::As_Pointer((HousesType)(HOUSE_GOOD + index))); + } + + int gdi_units_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; + int nod_units_lost = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; + int civilians_killed = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; + int gdi_buildings_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; + int nod_buildings_lost = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; + int civilian_buildings_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; + int gdi_harvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; + int nod_harvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; + + // unsigned leadership = 0; + // int index; + // for (index = 0; index < Logic.Count(); index++) { + // ObjectClass * object = Logic[index]; + // if (object->Owner() == house) { + // leadership++; + // } + // } + // + // if (leadership == 0) { + // leadership = 1; + // } + // + // leadership = Cardinal_To_Fixed(gdi_units_lost + gdi_buildings_lost + leadership, leadership); + // leadership = Fixed_To_Cardinal(100, leadership); + // if (leadership > 100) { + // leadership = 100; + // } + // + unsigned leadership = TD_Calculate_Leadership( house, (house == HOUSE_GOOD ? gdi_units_lost : nod_units_lost), (house == HOUSE_GOOD ? gdi_buildings_lost : nod_buildings_lost) ); + + /* + ** Determine efficiency rating. + */ + // int gharv = gdi_harvested; + // int init = PlayerPtr->InitialCredits; + // int cred = PlayerPtr->Available_Money(); + // + // unsigned efficiency = Cardinal_To_Fixed((house == HOUSE_GOOD ? gdi_harvested : nod_harvested) + (unsigned)PlayerPtr->InitialCredits + 1, (unsigned)PlayerPtr->Available_Money() + 1); + // if (!efficiency) efficiency++; + // efficiency = Fixed_To_Cardinal(100, efficiency); + // if (efficiency > 100) { + // efficiency = 100; + // } + // + unsigned efficiency = TD_Calculate_Efficiency((house == HOUSE_GOOD ? gdi_harvested : nod_harvested), PlayerPtr->InitialCredits, PlayerPtr->Available_Money()); + + /* + ** Calculate total score + */ + // long total_score = ((leadership * 40) + (4600) + (efficiency * 14)) / 100; + // if (!total_score) total_score++; + // total_score *= (BuildLevel + 1); + // + unsigned total_score = TD_Calculate_Score( leadership, efficiency, BuildLevel ); + + //Score Stats + event.GameOver.Leadership = leadership; + event.GameOver.Efficiency = efficiency; + event.GameOver.Score = total_score; + event.GameOver.CategoryTotal = 0; //Only used in Red Alert + event.GameOver.NODKilled = nod_units_lost; + event.GameOver.GDIKilled = gdi_units_lost; + event.GameOver.CiviliansKilled = civilians_killed; + event.GameOver.NODBuildingsDestroyed = nod_buildings_lost; + event.GameOver.GDIBuildingsDestroyed = gdi_buildings_lost; + event.GameOver.CiviliansBuildingsDestroyed = civilian_buildings_lost; + event.GameOver.RemainingCredits = PlayerPtr->Available_Money(); +} + +void DLLExportClass::Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type) +{ + switch (type) + { + case ACTION_NONE: + default: + dll_type = DAT_NONE; + break; + case ACTION_MOVE: + dll_type = DAT_MOVE; + break; + case ACTION_NOMOVE: + dll_type = DAT_NOMOVE; + break; + case ACTION_ENTER: + dll_type = DAT_ENTER; + break; + case ACTION_SELF: + dll_type = DAT_SELF; + break; + case ACTION_ATTACK: + if (Target_Legal(target) && (object != NULL) && object->Is_Techno() && ((TechnoClass*)object)->In_Range(target, 0)) { + dll_type = DAT_ATTACK; + } + else { + dll_type = DAT_ATTACK_OUT_OF_RANGE; + } + break; + case ACTION_GUARD_AREA: + dll_type = DAT_GUARD; + break; + case ACTION_HARVEST: + dll_type = DAT_ATTACK; + break; + case ACTION_SELECT: + case ACTION_TOGGLE_SELECT: + dll_type = DAT_SELECT; + break; + case ACTION_CAPTURE: + dll_type = DAT_CAPTURE; + break; + case ACTION_SABOTAGE: + dll_type = DAT_SABOTAGE; + break; + case ACTION_TOGGLE_PRIMARY: + dll_type = DAT_TOGGLE_PRIMARY; + break; + case ACTION_NO_DEPLOY: + dll_type = DAT_CANT_DEPLOY; + break; + } +} + + + + +/************************************************************************************************** +* DLLExportClass::On_Game_Over -- Called when the C&C campaign game finishes +* +* +* History: 6/19/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::On_Game_Over(uint64 glyphx_Player_id, bool player_wins) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_GAME_OVER; + new_event.GlyphXPlayerID = glyphx_Player_id; + new_event.GameOver.PlayerWins = player_wins; + new_event.GameOver.MovieName = player_wins ? WinMovie : LoseMovie; + new_event.GameOver.AfterScoreMovieName = ""; + new_event.GameOver.Multiplayer = false; + new_event.GameOver.MultiPlayerTotalPlayers = 0; + + Calculate_Single_Player_Score(new_event); + + if (player_wins) + { + if (PlayerPtr->Class->House == HOUSE_BAD && Scenario == 13) { + //TODO: Nod_Ending() Also looks like it plays some audio that might need to be integrated. + new_event.GameOver.MovieName = ""; + new_event.GameOver.AfterScoreMovieName = "NODFINAL"; + } + else if (PlayerPtr->Class->House == HOUSE_GOOD && Scenario == 15) { + if (TempleIoned) { + new_event.GameOver.MovieName = "GDIFINB"; + new_event.GameOver.AfterScoreMovieName = "GDIEND2"; + } + else { + new_event.GameOver.MovieName = "GDIFINA"; + new_event.GameOver.AfterScoreMovieName = "GDIEND1"; + } + } + } + + if (strcmp(new_event.GameOver.MovieName, "x") == 0 || strcmp(new_event.GameOver.MovieName, "X") == 0) { + new_event.GameOver.MovieName = ""; + } + + new_event.GameOver.MovieName2 = WinMovie2; + if (strcmp(new_event.GameOver.MovieName2, "x") == 0 || strcmp(new_event.GameOver.MovieName2, "X") == 0) { + new_event.GameOver.MovieName2 = ""; + } + + new_event.GameOver.MovieName3 = WinMovie3; + if (strcmp(new_event.GameOver.MovieName3, "x") == 0 || strcmp(new_event.GameOver.MovieName3, "X") == 0) { + new_event.GameOver.MovieName3 = ""; + } + + new_event.GameOver.MovieName4 = WinMovie4; + if (strcmp(new_event.GameOver.MovieName4, "x") == 0 || strcmp(new_event.GameOver.MovieName4, "X") == 0) { + new_event.GameOver.MovieName4 = ""; + } + + new_event.GameOver.SabotagedStructureType = SabotagedType; + new_event.GameOver.TimerRemaining = -1; //Used in RA + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Multiplayer_Game_Over -- Called when the C&C multiplayer game finishes +* +* +* History: 6/19/2019 - LLL +* History: 10/25/2019 - MBL - Adding the multi-player score stats support for debrief +**************************************************************************************************/ +void DLLExportClass::On_Multiplayer_Game_Over(void) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + EventCallbackStruct event; + + event.EventType = CALLBACK_EVENT_GAME_OVER; + + // Multiplayer players data for debrief stats + + event.GameOver.Multiplayer = true; + event.GameOver.MultiPlayerTotalPlayers = MPlayerCount; + + for ( int player_index = 0; player_index < MPlayerCount; player_index ++ ) + { + HouseClass* player_ptr = HouseClass::As_Pointer( MPlayerHouses[player_index] ); //HouseClass::As_Pointer(HOUSE_MULTI2); + if ( player_ptr != NULL ) + { + int house = player_ptr->Class->House; + unsigned int leadership = TD_Calculate_Leadership( house, player_ptr->UnitsLost, player_ptr->BuildingsLost ); + + unsigned int efficiency = TD_Calculate_Efficiency( player_ptr->HarvestedCredits, player_ptr->InitialCredits, player_ptr->Available_Money() ); + + unsigned int total_score = TD_Calculate_Score( leadership, efficiency, BuildLevel ); + + int units_killed = 0; + int structures_killed = 0; + for ( unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++ ) + { + units_killed += player_ptr->UnitsKilled[ house_index ]; + structures_killed += player_ptr->BuildingsKilled[ house_index ]; + } + + // Populate and copy the multiplayer player data structure + + GameOverMultiPlayerStatsStruct multi_player_data; + + multi_player_data.GlyphXPlayerID = Get_GlyphX_Player_ID( player_ptr ); + multi_player_data.IsHuman = (player_ptr->IsHuman || player_ptr->WasHuman); + multi_player_data.WasHuman = player_ptr->WasHuman; + multi_player_data.IsWinner = !player_ptr->IsDefeated; + multi_player_data.Efficiency = efficiency; + multi_player_data.Score = total_score; + multi_player_data.ResourcesGathered = player_ptr->HarvestedCredits; + multi_player_data.TotalUnitsKilled = units_killed; + multi_player_data.TotalStructuresKilled = structures_killed; + + if ( player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ) + { + event.GameOver.MultiPlayerPlayersData[ player_index ] = multi_player_data; + } + } + } + for ( int player_index = MPlayerCount; player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED; player_index ++ ) + { + memset( &event.GameOver.MultiPlayerPlayersData[ player_index ], 0, sizeof( GameOverMultiPlayerStatsStruct ) ); + } + + // Single-player N/A stuff + + event.GameOver.MovieName = ""; + event.GameOver.MovieName2 = ""; + event.GameOver.MovieName3 = ""; + event.GameOver.MovieName4 = ""; + event.GameOver.AfterScoreMovieName = ""; + event.GameOver.Leadership = 0; + event.GameOver.Efficiency = 0; + event.GameOver.Score = 0; + event.GameOver.NODKilled = 0; + event.GameOver.GDIKilled = 0; + event.GameOver.CiviliansKilled = 0; + event.GameOver.NODBuildingsDestroyed = 0; + event.GameOver.GDIBuildingsDestroyed = 0; + event.GameOver.CiviliansBuildingsDestroyed = 0; + event.GameOver.RemainingCredits = 0; + event.GameOver.SabotagedStructureType = 0; + event.GameOver.TimerRemaining = -1; + + // Trigger an event for each human player + for (int i=0 ; iIsHuman == true ) + { + event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + event.GameOver.PlayerWins = !player_ptr->IsDefeated; + event.GameOver.RemainingCredits = player_ptr->Available_Money(); + EventCallback(event); + } + } + } +} + + +/************************************************************************************************** +* DLLExportClass::On_Message -- Called when the game wants to display a message (ex. tutorial text) +* +* In: +* +* Out: +* +* +* +* History: 10/16/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) +{ + if (EventCallback == NULL) + { + return; + } + + const char* p_msg = message; + if (message_id != -1) { + if (message_id == TXT_LOW_POWER) { + p_msg = "TEXT_LOW_POWER_MESSAGE_001"; + } + else if (message_id == TXT_INSUFFICIENT_FUNDS) { + p_msg = "TEXT_INSUFFICIENT_FUNDS_MESSAGE"; + } + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_MESSAGE; + new_event.Message.Message = p_msg; + new_event.Message.TimeoutSeconds = timeout_seconds; + new_event.Message.MessageType = message_type; + new_event.Message.MessageParam1 = message_id; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + +void On_Message(const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, message_type, message_id); +} + +void On_Message(const char* message, float timeout_seconds, int64 message_id) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_DIRECT, message_id); +} + +void On_Defeated_Message(const char* message, float timeout_seconds) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_PLAYER_DEFEATED, -1); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Achievement -- Called when something achievement-related happens +* +* In: Type of achievement, reason this happened +* +* Out: +* +* +* +* History: 11/11/2019 11:37AM - ST +**************************************************************************************************/ +void DLLExportClass::On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_ACHIEVEMENT; + new_event.Achievement.AchievementType = achievement_type; + new_event.Achievement.AchievementReason = achievement_reason; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +void DLLExportClass::On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_CENTER_CAMERA; + new_event.CenterCamera.CoordX = coord_x; + new_event.CenterCamera.CoordY = coord_y; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +void DLLExportClass::On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_PING; + new_event.Ping.CoordX = Coord_X(coord); + new_event.Ping.CoordY = Coord_Y(coord); + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Debug_Output -- Called when C&C wants to print debug output +* +* In: String to print to GlyphX log system +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Debug_Output(const char *debug_text) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_DEBUG_PRINT; + new_event.DebugPrint.PrintString = debug_text; + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::Force_Human_Team_Wins +* +* History: 3/10/2020 - LL +**************************************************************************************************/ +void DLLExportClass::Force_Human_Team_Wins(uint64 quitting_player_id) +{ + if (PlayerWins || PlayerLoses || GameOver) { + return; + } + + int winning_team = -1; + + //Find the first human's multiplayer team. + for (int i = 0; i < MPlayerCount; i++) + { + if (GlyphxPlayerIDs[i] != quitting_player_id) { + HousesType house_type = MPlayerHouses[i]; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class && house_class->IsHuman && !house_class->IsDefeated) { + winning_team = MPlayerTeamIDs[i]; + break; + } + } + } + + //Mark all players not on that team as defeated. + for (int i = 0; i < MPlayerCount; i++) + { + HousesType house_type = MPlayerHouses[i]; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class) { + house_class->IsDefeated = MPlayerTeamIDs[i] != winning_team; + } + } + + PlayerWins = true; +} + + +/************************************************************************************************** +* CNC_Get_Game_State -- Get game state +* +* In: Type of state requested +* Player perspective +* Buffer to contain game state +* Size of buffer +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + bool got_state = false; + + switch (state_type) { + + case GAME_STATE_LAYERS: + { + got_state = DLLExportClass::Get_Layer_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_SIDEBAR: + { + got_state = DLLExportClass::Get_Sidebar_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_PLACEMENT: + { + got_state = DLLExportClass::Get_Placement_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_DYNAMIC_MAP: + got_state = DLLExportClass::Get_Dynamic_Map_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_SHROUD: + got_state = DLLExportClass::Get_Shroud_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_OCCUPIER: + got_state = DLLExportClass::Get_Occupier_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_PLAYER_INFO: + got_state = DLLExportClass::Get_Player_Info_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_STATIC_MAP: + { + if (buffer_size < sizeof(CNCMapDataStruct)) { + got_state = false; + break; + } + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + CNCMapDataStruct *map_data = (CNCMapDataStruct *)buffer_in; + + map_data->OriginalMapCellX = map_cell_x; + map_data->OriginalMapCellY = map_cell_y; + map_data->OriginalMapCellWidth = map_cell_width; + map_data->OriginalMapCellHeight = map_cell_height; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + map_data->MapCellX = map_cell_x; + map_data->MapCellY = map_cell_y; + map_data->MapCellWidth = map_cell_width; + map_data->MapCellHeight = map_cell_height; + + map_data->Theater = (CnCTheaterType) Map.Theater; + + // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 + memset(map_data->ScenarioName, 0, sizeof(map_data->ScenarioName)); + if ((Map.Theater == CNC_THEATER_DESERT) && (Scenario == 21)) { + strncpy(map_data->ScenarioName, "SCB81EA", sizeof(map_data->ScenarioName) - 1); + } else { + strncpy(map_data->ScenarioName, ScenarioName, sizeof(map_data->ScenarioName) - 1); + } + + int cell_index = 0; + char cell_name[_MAX_PATH]; + char icon_number[32]; + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + CellClass * cellptr = &Map[cell]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) { + itoa(icon, icon_number, 10); + strncat(cell_name, "_i", 32); + strncat(cell_name, icon_number, 32); + strncat(cell_name, ".tga", 32); + cell_name[31] = 0; + + CNCStaticCellStruct &cell_info = map_data->StaticCells[cell_index++]; + strncpy(cell_info.TemplateTypeName, cell_name, 32); + cell_info.TemplateTypeName[31] = 0; + cell_info.IconNumber = icon; + } + } + } + + got_state = true; + break; + } + + default: + { + got_state = false; + break; + } + } + + return got_state; +} + +/************************************************************************************************** +* CNC_Handle_Game_Request +* +* Callback for when the requested movie is done playing. +* +* 7/23/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type) +{ + switch (request_type) + { + case INPUT_GAME_MOVIE_DONE: + + InMainLoop = true; + break; + } +} + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode) +{ + if (!DLLExportClass::Legacy_Render_Enabled()) { + return; + } + + SpecialClass::eHealthBarDisplayMode new_hb_mode = (SpecialClass::eHealthBarDisplayMode)health_bar_display_mode; + if (new_hb_mode != Special.HealthBarDisplayMode) { + Special.HealthBarDisplayMode = new_hb_mode; + Map.Flag_To_Redraw(true); + } + + SpecialClass::eResourceBarDisplayMode new_rb_mode = (SpecialClass::eResourceBarDisplayMode)resource_bar_display_mode; + if (new_rb_mode != Special.ResourceBarDisplayMode) { + Special.ResourceBarDisplayMode = new_rb_mode; + Map.Flag_To_Redraw(true); + } +} + + + + + +void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE, int scale = 0x100) +{ + DLLExportClass::DLL_Draw_Intercept(shape_number, x, y, width, height, flags, object, shape_file_name, override_owner, scale); +} + + +void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + DLLExportClass::DLL_Draw_Pip_Intercept(object, pip); +} + + +void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) +{ + DLLExportClass::DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); +} + + +void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name, char override_owner, int scale) +{ + CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount]; + Convert_Type(object, new_object); + if (new_object.Type == UNKNOWN) { + return; + } + + CNCObjectStruct* base_object = NULL; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + base_object = &draw_object; + break; + } + } + + new_object.CNCInternalObjectPointer = (void*)object; + new_object.OccupyListLength = 0; + new_object.SortOrder = SortOrder++; + + strncpy(new_object.TypeName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + + if (shape_file_name != NULL) { + strncpy(new_object.AssetName, shape_file_name, CNC_OBJECT_ASSET_NAME_LENGTH); + } + else { + strncpy(new_object.AssetName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + } + + new_object.TypeName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + new_object.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + new_object.Owner = ((base_object != NULL) && (override_owner != HOUSE_NONE)) ? override_owner : (char)object->Owner(); + + HouseClass* owner_house = nullptr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && (hptr->Class->House == new_object.Owner)) { + owner_house = hptr; + break; + } + } + + new_object.RemapColor = (owner_house != nullptr) ? owner_house->RemapColor : -1; + + if (base_object == NULL) { + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + + if (new_object.Type == BUILDING) { + BuildingClass *building = (BuildingClass*)object; + if (building->BState == BSTATE_CONSTRUCTION) { + strncat(new_object.AssetName, "MAKE", CNC_OBJECT_ASSET_NAME_LENGTH); + new_object.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + } + const BuildingTypeClass *building_type = building->Class; + short const *occupy_list = building_type->Occupy_List(); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && new_object.OccupyListLength < MAX_OCCUPY_CELLS) { + new_object.OccupyList[new_object.OccupyListLength] = *occupy_list; + new_object.OccupyListLength++; + occupy_list++; + } + } + } + + COORDINATE coord = object->Render_Coord(); + CELL cell = Coord_Cell(coord); + int dimx, dimy; + object->Class_Of().Dimensions(dimx, dimy); + + short sim_lepton_x = 0; + short sim_lepton_y = 0; + + if (new_object.Type == UNIT) { + sim_lepton_x = ((DriveClass*)object)->SimLeptonX; + sim_lepton_y = ((DriveClass*)object)->SimLeptonY; + } + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = 0; + new_object.DrawFlags = flags; + new_object.SubObject = 0; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Scale = scale; + new_object.Rotation = 0; + new_object.FlashingFlags = 0; + new_object.Cloak = (CurrentDrawCount > 0) ? root_object.Cloak : UNCLOAKED; + new_object.VisibleFlags = CNCObjectStruct::VISIBLE_FLAGS_ALL; + new_object.SpiedByFlags = 0U; + + new_object.SortOrder = SortOrder++; + new_object.IsSelectable = object->Class_Of().IsSelectable; + new_object.IsSelectedMask = object->IsSelectedMask; + new_object.MaxStrength = object->Class_Of().MaxStrength; + new_object.Strength = object->Strength; + new_object.CellX = (CurrentDrawCount > 0) ? root_object.CellX : Cell_X(cell); + new_object.CellY = (CurrentDrawCount > 0) ? root_object.CellY : Cell_Y(cell); + new_object.CenterCoordX = Coord_X(object->Center_Coord()); + new_object.CenterCoordY = Coord_Y(object->Center_Coord()); + new_object.DimensionX = dimx; + new_object.DimensionY = dimy; + new_object.SimLeptonX = (CurrentDrawCount > 0) ? root_object.SimLeptonX : sim_lepton_x; + new_object.SimLeptonY = (CurrentDrawCount > 0) ? root_object.SimLeptonY : sim_lepton_y; + new_object.BaseObjectID = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING)) ? root_object.ID : 0; + new_object.BaseObjectType = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING)) ? root_object.Type : UNKNOWN; + new_object.NumLines = 0; + new_object.RecentlyCreated = object->IsRecentlyCreated; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.CanDemolish = object->Can_Demolish(); + new_object.CanDemolishUnit = object->Can_Demolish_Unit(); + new_object.CanRepair = object->Can_Repair(); + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + HousesType house = hptr->Class->House; + DynamicVectorClass& selected_objects = CurrentObject.Raw(house); + if (selected_objects.Count() > 0) { + Logic_Switch_Player_Context(hptr); + Convert_Action_Type(Best_Object_Action(selected_objects, object), (selected_objects.Count() == 1) ? selected_objects[0] : NULL, object->As_Target(), new_object.ActionWithSelected[house]); + } + } + } + Logic_Switch_Player_Context(old_player_ptr); + + RTTIType what_is_object = object->What_Am_I(); + + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = false; + new_object.IsFactory = false; + new_object.IsPrimaryFactory = false; + new_object.IsAntiGround = false; + new_object.IsAntiAircraft = false; + new_object.IsSubSurface = false; + new_object.IsNominal = false; + new_object.IsDog = false; + new_object.IsIronCurtain = false; + new_object.IsInFormation = false; + new_object.IsFake = false; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + + bool is_building = what_is_object == RTTI_BUILDING; + if (is_building) { + BuildingClass* building = static_cast(object); + new_object.IsRepairing = building->IsRepairing; + new_object.IsFactory = building->Class->IsFactory; + new_object.IsPrimaryFactory = building->IsLeader; + } + + if (object->Is_Techno()) { + TechnoClass* techno_object = static_cast(object); + const TechnoTypeClass *ttype = techno_object->Techno_Type_Class(); + + new_object.MaxSpeed = (unsigned char)ttype->MaxSpeed; + new_object.IsALoaner = techno_object->IsALoaner; + new_object.IsNominal = ttype->IsNominal; + new_object.MaxPips = ttype->Max_Pips(); + new_object.IsAntiGround = ttype->Primary != WEAPON_NONE; + new_object.IsAntiAircraft = (ttype->Primary != WEAPON_NONE) && (Weapons[ttype->Primary].Fires != BULLET_NONE) && BulletTypeClass::As_Reference(Weapons[ttype->Primary].Fires).IsAntiAircraft; + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + Logic_Switch_Player_Context(hptr); + HousesType house = hptr->Class->House; + new_object.CanMove[house] = techno_object->Can_Player_Move(); + new_object.CanFire[house] = techno_object->Can_Player_Fire(); + } + } + Logic_Switch_Player_Context(old_player_ptr); + } + + new_object.ControlGroup = (unsigned char)(-1); + new_object.CanPlaceBombs = false; + bool is_infantry = what_is_object == RTTI_INFANTRY; + if (is_infantry) { + InfantryClass* infantry = static_cast(object); + new_object.ControlGroup = infantry->Group; + new_object.CanPlaceBombs = infantry->Class->Type == INFANTRY_RAMBO; + } + + new_object.CanHarvest = false; + bool is_unit = what_is_object == RTTI_UNIT; + if (is_unit) { + UnitClass* unit = static_cast(object); + if (unit->Class->Type == UNIT_HARVESTER) { + new_object.CanHarvest = true; + } + + new_object.ControlGroup = unit->Group; + } + + new_object.IsFixedWingedAircraft = false; + bool is_aircraft = what_is_object == RTTI_AIRCRAFT; + if (is_aircraft) { + AircraftClass* aircraft = static_cast(object); + new_object.Altitude = Pixel_To_Lepton(aircraft->Altitude); + new_object.IsFixedWingedAircraft = aircraft->Class->IsFixedWing; + new_object.ControlGroup = aircraft->Group; + } + + switch (what_is_object) + { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + case RTTI_UNIT: + case RTTI_UNITTYPE: + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + { + TechnoClass* techno_object = static_cast(object); + new_object.FlashingFlags = techno_object->Get_Flashing_Flags(); + new_object.Cloak = techno_object->Cloak; + } + break; + + case RTTI_ANIM: + { + AnimClass* anim_object = static_cast(object); + new_object.VisibleFlags = anim_object->Get_Visible_Flags(); + } + break; + } + } + else { + new_object.MaxStrength = 0; + new_object.MaxSpeed = 0; + new_object.Strength = 0; + new_object.CellX = base_object->CellX; + new_object.CellY = base_object->CellY; + new_object.CenterCoordX = base_object->CenterCoordX; + new_object.CenterCoordY = base_object->CenterCoordY; + new_object.DimensionX = base_object->DimensionX; + new_object.DimensionY = base_object->DimensionY; + new_object.IsSelectable = false; + new_object.IsSelectedMask = 0U; + new_object.SimLeptonX = base_object->SimLeptonX; + new_object.SimLeptonY = base_object->SimLeptonY; + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = base_object->Altitude; + new_object.DrawFlags = flags; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Scale = scale; + new_object.Rotation = 0; + new_object.SubObject = CurrentDrawCount; + new_object.BaseObjectID = base_object->ID; + new_object.BaseObjectType = base_object->Type; + new_object.FlashingFlags = base_object->FlashingFlags; + new_object.Cloak = base_object->Cloak; + new_object.OccupyListLength = 0; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = base_object->IsALoaner; + new_object.NumLines = 0; + new_object.CanDemolish = base_object->CanDemolish; + new_object.CanDemolishUnit = base_object->CanDemolishUnit; + new_object.CanRepair = base_object->CanRepair; + new_object.RecentlyCreated = base_object->RecentlyCreated; + new_object.IsFactory = base_object->IsFactory; + new_object.IsPrimaryFactory = base_object->IsPrimaryFactory; + new_object.IsAntiGround = base_object->IsAntiGround; + new_object.IsAntiAircraft = base_object->IsAntiAircraft; + new_object.IsSubSurface = base_object->IsSubSurface; + new_object.IsNominal = base_object->IsNominal; + new_object.IsDog = base_object->IsDog; + new_object.IsIronCurtain = base_object->IsIronCurtain; + new_object.IsInFormation = false; + new_object.CanHarvest = base_object->CanHarvest; + new_object.CanPlaceBombs = base_object->CanPlaceBombs; + new_object.ControlGroup = base_object->ControlGroup; + new_object.VisibleFlags = base_object->VisibleFlags; + new_object.SpiedByFlags = base_object->SpiedByFlags; + new_object.IsFixedWingedAircraft = base_object->IsFixedWingedAircraft; + new_object.IsFake = base_object->IsFake; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + } + + CurrentDrawCount++; +} + + + +void DLLExportClass::DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + CNCObjectStruct* base_object = NULL; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + base_object = &draw_object; + break; + } + } + + if ((base_object != NULL) && (base_object->NumPips < MAX_OBJECT_PIPS)) { + base_object->Pips[base_object->NumPips] = pip; + base_object->NumPips++; + base_object->MaxPips = max(base_object->MaxPips, base_object->NumPips); + } +} + + + +void DLLExportClass::DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) +{ + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + if (root_object.NumLines < MAX_OBJECT_LINES) { + root_object.Lines[root_object.NumLines].X = x; + root_object.Lines[root_object.NumLines].Y = y; + root_object.Lines[root_object.NumLines].X1 = x1; + root_object.Lines[root_object.NumLines].Y1 = y1; + root_object.Lines[root_object.NumLines].Frame = frame; + root_object.Lines[root_object.NumLines].Color = color; + + SortOrder++; + root_object.NumLines++; + } +} + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get game objects from the layers +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + player_id; + + static int _export_count = 0; + + bool got_state = false; + + ObjectList = (CNCObjectListStruct*) buffer_in; + + TotalObjectCount = 0; + + /* + ** Get a reference draw coordinate for cells + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + if (map_cell_x > 0) { + map_cell_x--; + } + if (map_cell_y > 0) { + map_cell_y--; + } + + SortOrder = 0; + + /* + ** Get the ground layer first and then followed by all the layers in increasing altitude. + */ + for (int layer = 0; layer < DLL_LAYER_COUNT; layer++) { + + for (int index = 0; index < Map.Layer[layer].Count(); index++) { + + ObjectClass *object = Map.Layer[layer][index]; + if (object->IsActive) { + + unsigned int memory_needed = sizeof(CNCObjectListStruct); + memory_needed += (TotalObjectCount + 10) * sizeof(CNCObjectStruct); + if (memory_needed >= buffer_size) { + return false; + } + + if (object->Is_Techno()) { + /* + ** Skip units tethered to buildings, since the building will draw them itself + */ + TechnoClass* techno_object = static_cast(object); + TechnoClass* contact_object = techno_object->In_Radio_Contact() ? techno_object->Contact_With_Whom() : nullptr; + if ((contact_object != nullptr) && (contact_object->What_Am_I() == RTTI_BUILDING) && contact_object->IsTethered && *((BuildingClass*)contact_object) == STRUCT_WEAP) { + continue; + } + } + + if (Debug_Map || Debug_Unshroud || (object->IsDown && !object->IsInLimbo)) { + int x, y; + Map.Coord_To_Pixel(object->Render_Coord(), x, y); + + /* + ** Call to Draw_It can result in multiple callbacks to the draw intercept + */ + CurrentDrawCount = 0; + object->Draw_It(x, y, WINDOW_VIRTUAL); + + /* + ** Shadows need to be rendered before the base object so they appear underneath, + ** even though they get drawn as sub-objects (after the base object) + */ + for (int i = 1; i < CurrentDrawCount; ++i) { + CNCObjectStruct& sub_object = ObjectList->Objects[TotalObjectCount + i]; + if (!sub_object.SubObject) { + continue; + } + static const int shadow_flags = SHAPE_PREDATOR | SHAPE_FADING; + if (((sub_object.DrawFlags & shadow_flags) == shadow_flags) || (strncmp(sub_object.AssetName, "WAKE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0)) { + if ((strncmp(sub_object.AssetName, "RROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0) && + (strncmp(sub_object.AssetName, "LROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0)) { + for (int j = i - 1; j >= 0; --j) { + CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + j]; + if (!base_object.SubObject && (base_object.CNCInternalObjectPointer == sub_object.CNCInternalObjectPointer)) { + int sort_order = base_object.SortOrder; + base_object.SortOrder = sub_object.SortOrder; + sub_object.SortOrder = sort_order; + break; + } + } + } + } + } + + TotalObjectCount += CurrentDrawCount; + } + } + } + } + + ObjectList->Count = TotalObjectCount; + + if (ObjectList->Count) { + _export_count++; + return true; + } + + return false; +} + + + + +void DLLExportClass::Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out) +{ + object_out.Type = UNKNOWN; + object_out.ID = -1; + + if (object == NULL) { + return; + } + + RTTIType type = object->What_Am_I(); + + switch (type) { + default: + break; + + case RTTI_INFANTRY: + object_out.Type = INFANTRY; + object_out.ID = Infantry.ID((InfantryClass*)object); + break; + + case RTTI_UNIT: + object_out.Type = UNIT; + object_out.ID = Units.ID((UnitClass*)object); + break; + + case RTTI_AIRCRAFT: + object_out.Type = AIRCRAFT; + object_out.ID = Aircraft.ID((AircraftClass*)object); + break; + + case RTTI_BUILDING: + object_out.Type = BUILDING; + object_out.ID = Buildings.ID((BuildingClass*)object); + break; + + case RTTI_BULLET: + object_out.Type = BULLET; + object_out.ID = Bullets.ID((BulletClass*)object); + break; + + case RTTI_ANIM: + object_out.Type = ANIM; + object_out.ID = Anims.ID((AnimClass*)object); + break; + + case RTTI_SMUDGE: + object_out.Type = SMUDGE; + object_out.ID = Smudges.ID((SmudgeClass*)object); + break; + + case RTTI_TERRAIN: + object_out.Type = TERRAIN; + object_out.ID = Terrains.ID((TerrainClass*)object); + break; + } +} + + + + + + +/************************************************************************************************** +* CNC_Handle_Input -- Process input to the game +* +* In: +* +* +* +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum input_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2) +{ + + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (input_event) { + + /* + ** Special keys have changed + */ + case INPUT_REQUEST_SPECIAL_KEYS: + { + DLLExportClass::Set_Special_Key_Flags(special_key_flags); + break; + } + + /* + ** The mouse is moving + */ + case INPUT_REQUEST_MOUSE_MOVE: + { + if (!DLLExportClass::Legacy_Render_Enabled()) { + break; + } + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + if (coord) { + //x -= Map.TacPixelX; + //y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + } + break; + } + + /* + ** Player left-clicked + */ + case INPUT_REQUEST_MOUSE_LEFT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right-clicked (on up) + */ + case INPUT_REQUEST_MOUSE_RIGHT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right button down + */ + case INPUT_REQUEST_MOUSE_RIGHT_DOWN: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, 100, 100); + } + break; + } + + + /* + ** Player drag selected + */ + case INPUT_REQUEST_MOUSE_AREA: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYPixel_Coord(x1, y1), XYPixel_Coord(x2, y2), false); + break; + } + + case INPUT_REQUEST_MOUSE_AREA_ADDITIVE: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYPixel_Coord(x1, y1), XYPixel_Coord(x2, y2), true); + break; + } + + case INPUT_REQUEST_SELL_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + PlayerPtr->Sell_Wall(cell); + } + + break; + } + + case INPUT_REQUEST_SELECT_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + DisplayClass::TacButton.Selection_At_Mouse(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + case INPUT_REQUEST_COMMAND_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + _Kbd->MouseQX = x1; + _Kbd->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + DisplayClass::TacButton.Command_Object(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + default: + break; + } +} + + + + +/************************************************************************************************** +* CNC_Handle_Structure_Request -- Process requests to repair and sell structures. +* +* In: +* +* +* Out: +* +* +* +* History: 4/29/2019 - LLL +**************************************************************************************************/ + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_STRUCTURE_REPAIR_START: + DLLExportClass::Repair_Mode(player_id); + break; + case INPUT_STRUCTURE_REPAIR: + DLLExportClass::Repair(player_id, object_id); + break; + case INPUT_STRUCTURE_SELL_START: + DLLExportClass::Sell_Mode(player_id); + break; + case INPUT_STRUCTURE_SELL: + DLLExportClass::Sell(player_id, object_id); + break; + case INPUT_STRUCTURE_CANCEL: + DLLExportClass::Repair_Sell_Cancel(player_id); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Unit_Request -- Process requests on selected units. +* +* In: +* +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_UNIT_SCATTER: + DLLExportClass::Scatter_Selected(player_id); + break; + case INPUT_UNIT_SELECT_NEXT: + DLLExportClass::Select_Next_Unit(player_id); + break; + case INPUT_UNIT_SELECT_PREVIOUS: + DLLExportClass::Select_Previous_Unit(player_id); + break; + case INPUT_UNIT_GUARD_MODE: + DLLExportClass::Selected_Guard_Mode(player_id); + break; + case INPUT_UNIT_STOP: + DLLExportClass::Selected_Stop(player_id); + break; + case INPUT_UNIT_FORMATION_TOGGLE: + DLLExportClass::Team_Units_Formation_Toggle_On(player_id); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_ON: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, true); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_OFF: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, false); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Sidebar_Request -- Process an input request to the sidebar +* +* In: +* +* +* Out: +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) { + + case SIDEBAR_REQUEST_START_CONSTRUCTION: + DLLExportClass::Start_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + DLLExportClass::Hold_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + DLLExportClass::Cancel_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_START_PLACEMENT: + DLLExportClass::Start_Placement(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_PLACE: + DLLExportClass::Place(player_id, buildable_type, buildable_id, cell_x, cell_y); + break; + + case SIDEBAR_CANCEL_PLACE: + DLLExportClass::Cancel_Placement(player_id, buildable_type, buildable_id); + break; + + default: + break; + } +} + +/************************************************************************************************** +* CNC_Handle_SuperWeapon_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON: + DLLExportClass::Place_Super_Weapon(player_id, buildable_type, buildable_id, x1, y1); + break; + } +} + +/************************************************************************************************** +* CNC_Handle_ControlGroup_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case CONTROL_GROUP_REQUEST_CREATE: + DLLExportClass::Create_Control_Group(control_group_index); + break; + case CONTROL_GROUP_REQUEST_TOGGLE: + DLLExportClass::Toggle_Control_Group_Selection(control_group_index); + break; + case CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION: + DLLExportClass::Add_To_Control_Group(control_group_index); + break; + + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get a snapshot of the sidebar state +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCSidebarStruct *sidebar = (CNCSidebarStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*sidebar); // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + sidebar->Credits = 0; + sidebar->CreditsCounter = 0; + sidebar->Tiberium = 0; + sidebar->MaxTiberium = 0; + sidebar->PowerProduced = 0; + sidebar->PowerDrained = 0; + sidebar->RepairBtnEnabled = false; + sidebar->SellBtnEnabled = false; + sidebar->RadarMapActive = false; + sidebar->MissionTimer = -1; + + sidebar->UnitsKilled = 0; + sidebar->BuildingsKilled = 0; + sidebar->UnitsLost = 0; + sidebar->BuildingsLost = 0; + sidebar->TotalHarvestedCredits = 0; + + if (PlayerPtr) { + sidebar->Credits = PlayerPtr->Credits; + sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Current; // Timed display + // sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Credits; // Actual + sidebar->Tiberium = PlayerPtr->Tiberium; + sidebar->MaxTiberium = PlayerPtr->Capacity; + sidebar->PowerProduced = PlayerPtr->Power; + sidebar->PowerDrained = PlayerPtr->Drain; + + sidebar->RepairBtnEnabled = PlayerPtr->BScan > 0; + sidebar->SellBtnEnabled = PlayerPtr->BScan > 0; + sidebar->RadarMapActive = PlayerPtr->Radar == RADAR_ON; + + + // A. Get the DestroyedBuildings and DestroyedInfantry stats if they are available at this point + if (PlayerPtr->DestroyedBuildings) { + for ( int index = 0; index < PlayerPtr->DestroyedBuildings->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedBuildings->Get_Unit_Total( index ); + sidebar->BuildingsKilled += count; + } + } + if (PlayerPtr->DestroyedInfantry) { + for ( int index = 0; index < PlayerPtr->DestroyedInfantry->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedInfantry->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedUnits) { + for ( int index = 0; index < PlayerPtr->DestroyedUnits->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedUnits->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedAircraft) { + for ( int index = 0; index < PlayerPtr->DestroyedAircraft->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedAircraft->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + + // B. If the DestroyedBuildings and DestroyedInfantry stats seemed to be unvailable, this is another way to do it + // Note that we need to do both of these depending on which type of match we are running, as well as for Replays/Observer and live stats reporting + // We can't just do it this way for everything, as it does not work for all cases + if (sidebar->BuildingsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->BuildingsKilled += PlayerPtr->BuildingsKilled[ house_index ]; + } + } + if (sidebar->UnitsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->UnitsKilled += PlayerPtr->UnitsKilled[ house_index ]; // Includes Infantry, Vehicles, Aircraft + } + } + + + sidebar->UnitsLost = PlayerPtr->UnitsLost; + sidebar->BuildingsLost = PlayerPtr->BuildingsLost; + sidebar->TotalHarvestedCredits = PlayerPtr->HarvestedCredits; + } + + if (GameToPlay == GAME_NORMAL) { + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = Map.Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = Map.Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = Map.Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = Map.Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(Map.Column[c].Buildables[b].BuildableType, Map.Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + + bool isbusy = false; + + switch (Map.Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + sidebar_entry.Type = UNIT_TYPE; + isbusy |= Units.Avail() <= 0; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + sidebar_entry.Type = AIRCRAFT_TYPE; + isbusy |= Aircraft.Avail() <= 0; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + } + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + switch (Map.Column[c].Buildables[b].BuildableID) + { + case SPC_ION_CANNON: + sidebar_entry.SuperWeaponType = SW_ION_CANNON; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_Ion", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->IonCannon; + break; + + case SPC_NUCLEAR_BOMB: + sidebar_entry.SuperWeaponType = SW_NUKE; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_Nuke", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->NukeStrike; + break; + + case SPC_AIR_STRIKE: + sidebar_entry.SuperWeaponType = SW_AIR_STRIKE; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_AirStrike", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->AirStrike; + break; + + default: + sidebar_entry.SuperWeaponType = SW_UNKNOWN; + sidebar_entry.Type = SPECIAL; + break; + } + break; + } + + int fnumber = Map.Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + + } else { + + + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = context_sidebar->Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = context_sidebar->Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = context_sidebar->Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = context_sidebar->Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(context_sidebar->Column[c].Buildables[b].BuildableType, context_sidebar->Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + + bool isbusy = false; + + switch (context_sidebar->Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + isbusy |= Units.Avail() <= 0; + sidebar_entry.Type = UNIT_TYPE; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + isbusy |= Aircraft.Avail() <= 0; + sidebar_entry.Type = AIRCRAFT_TYPE; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + } + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + switch (context_sidebar->Column[c].Buildables[b].BuildableID) { + case SPC_ION_CANNON: + sidebar_entry.SuperWeaponType = SW_ION_CANNON; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_Ion", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->IonCannon; + break; + + case SPC_NUCLEAR_BOMB: + sidebar_entry.SuperWeaponType = SW_NUKE; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_Nuke", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->NukeStrike; + break; + + case SPC_AIR_STRIKE: + sidebar_entry.SuperWeaponType = SW_AIR_STRIKE; + sidebar_entry.Type = SPECIAL; + strncpy(sidebar_entry.AssetName, "SW_AirStrike", CNC_OBJECT_ASSET_NAME_LENGTH); + sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + super_weapon = &PlayerPtr->AirStrike; + break; + + default: + sidebar_entry.SuperWeaponType = SW_UNKNOWN; + sidebar_entry.Type = SPECIAL; + break; + } + break; + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + } + } + + + + return true; +} + + +void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + memset(placement_distance, 255U, MAP_CELL_TOTAL); + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++) { + CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << 6); + BuildingClass* base = (BuildingClass*)Map[cell].Cell_Find_Object(RTTI_BUILDING); + if ((base && base->House->Class->House == PlayerPtr->Class->House) || (Map[cell].Owner == PlayerPtr->Class->House)) { + placement_distance[cell] = 0U; + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL adjcell = Adjacent_Cell(cell, facing); + if (Map.In_Radar(adjcell)) { + placement_distance[adjcell] = min(placement_distance[adjcell], 1U); + } + } + } + } + } +} + +void Recalculate_Placement_Distances() +{ + DLLExportClass::Recalculate_Placement_Distances(); +} + +void DLLExportClass::Recalculate_Placement_Distances() +{ + if (PlacementType[CurrentLocalPlayerIndex] != NULL) { + Calculate_Placement_Distances(PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + } +} + + +/************************************************************************************************** +* DLLExportClass::Get_Placement_State -- Get a snapshot of legal validity of placing a structure on all map cells +* +* In: +* +* Out: +* +* +* +* History: 2/4/2019 3:11PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (PlacementType[CurrentLocalPlayerIndex] == NULL) { + return false; + } + + CNCPlacementInfoStruct *placement_info = (CNCPlacementInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*placement_info); // Base amount needed. Will need more depending on how many entries there are + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + memory_needed += map_cell_width * map_cell_height * sizeof(CNCPlacementCellInfoStruct); + + if (memory_needed + 128 >= buffer_size) { + return false; + } + + placement_info->Count = map_cell_width * map_cell_height; + + int index = 0; + for (int y=0 ; y < map_cell_height ; y++) { + for (int x=0 ; x < map_cell_width ; x++) { + + CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << 6); + + bool pass = Passes_Proximity_Check(cell, PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + + CellClass * cellptr = &Map[cell]; + bool clear = cellptr->Is_Generally_Clear(); + + CNCPlacementCellInfoStruct &placement_cell_info = placement_info->CellInfo[index++]; + placement_cell_info.PassesProximityCheck = pass; + placement_cell_info.GenerallyClear = clear; + } + } + + Map.ZoneOffset = 0; + + return true; +} + + +bool DLLExportClass::Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance) +{ + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + short const *occupy_list = placement_type->Occupy_List(true); + + while (*occupy_list != REFRESH_EOL) { + + CELL center_cell = cell_in + *occupy_list++; + + if (!Map.In_Radar(center_cell)) { + return false; + } + + if (placement_distance[center_cell] <= 1U) { + return true; + } + } + + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Start_Construction -- Start sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (GameToPlay == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Hold_Construction -- Pause sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Hold_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + if (GameToPlay == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Cancel_Construction -- Stop sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + return Cancel_Placement(player_id, buildable_type, buildable_id) && + ((GameToPlay == GAME_NORMAL) ? + Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id) : + MP_Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id)); +} + +/************************************************************************************************** +* DLLExportClass::Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(false); + } + + if (factory) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + break; + } + + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + Queue_AI(); + return true; + } + } + } + } + } + } + } + return false; +} + + + + +/************************************************************************************************** +* DLLExportClass::MP_Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 3/26/2019 1:02PM - ST +**************************************************************************************************/ +bool DLLExportClass::MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && genfactory != -1) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return(false); + } + + if (factory) { + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + if (DLLExportClass::Legacy_Render_Enabled()) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + Unselect_All(); + } + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + break; + } + + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** + */ + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + DLLExportClass::Glyphx_Queue_AI(); + return true; + } + } + } + } + } + } + } + return false; +} + + + + + +/************************************************************************************************** +* DLLExportClass::Start_Placement -- Start placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + Recalculate_Placement_Distances(); + + if (GameToPlay == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + } + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Cancel_Placement -- Cancel placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 2/7/2019 10:52AM - ST +**************************************************************************************************/ +bool DLLExportClass::Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + PlacementType[CurrentLocalPlayerIndex] = NULL; + + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + + return true; +} + + + +/************************************************************************************************** +* DLLExportClass::Place -- Place a completed structure down +* +* In: +* +* Out: +* +* +* +* History: 2/6/2019 11:51AM - ST +**************************************************************************************************/ +bool DLLExportClass::Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + /* + ** Need to check for proximity again here? + */ +#if (0) +Map.Passes_Proximity_Check + + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + + + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); +#endif + + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + + /* + ** The cell coordinates passed in will be relative to the playable area that the client knows about + */ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << 6 ); + + /* + ** Call the place directly instead of queueing it, so we can evaluate the return code. + */ + if (PlayerPtr->Place_Object(building->What_Am_I(), cell + Map.ZoneOffset)) { + PlacementType[CurrentLocalPlayerIndex] = NULL; + } + } + } + return true; + +} + + + + +BuildingClass *DLLExportClass::Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** + */ + if (GameToPlay == GAME_NORMAL) { + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + + } else { + + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + } + } + return NULL; +} + +/************************************************************************************************** +* DLLExportClass::Place_Super_Weapon +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y) +{ + if (buildable_type != RTTI_SPECIAL) + { + return false; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + SpecialWeaponType weapon_type = (SpecialWeaponType)buildable_id; + + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, weapon_type, cell)); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Create_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Create_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 2); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Add_To_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Add_To_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 1); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Toggle_Control_Group_Selection +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Toggle_Control_Group_Selection(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 0); + + return true; +} + + + +/************************************************************************************************** +* DLLExportClass::Get_Shroud_State -- Get a snapshot of the shroud for the given player +* +* In: +* +* Out: +* +* +* +* History: 4/12/2019 3:44PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCShroudStruct *shroud = (CNCShroudStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*shroud) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCShroudEntryStruct); + if (memory_needed >= buffer_size) { + return false; + } + + int xpixel; + int ypixel; + + Map.Coord_To_Pixel(coord, xpixel, ypixel); + + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + CNCShroudEntryStruct &shroud_entry = shroud->Entries[entry_index]; + + shroud_entry.IsVisible = cellptr->Is_Visible(PlayerPtr); + shroud_entry.IsMapped = cellptr->Is_Mapped(PlayerPtr); + shroud_entry.IsJamming = false; + shroud_entry.ShadowIndex = -1; + + if (!shroud_entry.IsMapped) { + if (shroud_entry.IsVisible) { + shroud_entry.ShadowIndex = (char) Map.Cell_Shadow(cell, PlayerPtr); + } + } + + entry_index++; + } + } + + shroud->Count = entry_index; + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Occupier_State -- Get the occupier state for this player +* +* In: +* +* Out: +* +* +* +* History: 10/25/2019 - SKY +**************************************************************************************************/ +bool DLLExportClass::Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + UNREFERENCED_PARAMETER(player_id); + + CNCOccupierHeaderStruct* occupiers = (CNCOccupierHeaderStruct*)buffer_in; + CNCOccupierEntryHeaderStruct* entry = reinterpret_cast(occupiers + 1U); + occupiers->Count = 0; + + unsigned int memory_needed = sizeof(CNCOccupierHeaderStruct); + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++, occupiers->Count++) { + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + int occupier_count = 0; + ObjectClass* const cell_occupier = cellptr->Cell_Occupier(); + for (ObjectClass* optr = cell_occupier; optr != NULL; optr = optr->Next) { + if (optr->IsActive) { + occupier_count++; + } + } + + memory_needed += sizeof(CNCOccupierEntryHeaderStruct) + (sizeof(CNCOccupierObjectStruct) * occupier_count); + if (memory_needed >= buffer_size) { + return false; + } + + CNCOccupierObjectStruct* occupier = reinterpret_cast(entry + 1U); + entry->Count = occupier_count; + + for (ObjectClass* optr = cell_occupier; optr != NULL; optr = optr->Next) { + if (optr->IsActive) { + CNCObjectStruct object; + Convert_Type(optr, object); + occupier->Type = object.Type; + occupier->ID = object.ID; + occupier++; + } + } + + entry = reinterpret_cast(occupier + 1U); + } + } + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Player_Info_State -- Get the multiplayer info for this player +* +* In: +* +* Out: +* +* +* +* History: 4/22/2019 10:33AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCPlayerInfoStruct *player_info = (CNCPlayerInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*player_info) + 32; // A little extra for no reason + + if (memory_needed >= buffer_size) { + return false; + } + + player_info->GlyphxPlayerID = 0; + + if (PlayerPtr == NULL) { + return false;; + } + + strncpy(&player_info->Name[0], MPlayerNames[CurrentLocalPlayerIndex], MPLAYER_NAME_MAX); + player_info->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated + player_info->House = PlayerPtr->Class->House; + player_info->AllyFlags = PlayerPtr->Get_Ally_Flags(); + player_info->ColorIndex = MPlayerID_To_ColorIndex(MPlayerID[CurrentLocalPlayerIndex]); + player_info->GlyphxPlayerID = player_id; + player_info->HomeCellX = Cell_X(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->HomeCellY = Cell_Y(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->IsDefeated = PlayerPtr->IsDefeated; + player_info->SpiedPowerFlags = 0U; + player_info->SpiedMoneyFlags = 0U; + player_info->IsRadarJammed = false; + + // Populate selection info + if (CurrentObject.Count() > 0) { + CNCObjectStruct object; + Convert_Type(CurrentObject[0], object); + player_info->SelectedID = object.ID; + player_info->SelectedType = object.Type; + + const int left = Map.MapCellX; + const int right = Map.MapCellX + Map.MapCellWidth - 1; + const int top = Map.MapCellY; + const int bottom = Map.MapCellY + Map.MapCellHeight - 1; + + // Use first object with a weapon, or first object if none + ObjectClass* action_object = nullptr; + for (int i = 0; i < CurrentObject.Count(); ++i) { + ObjectClass* object = CurrentObject[i]; + if (object->Is_Techno()) { + TechnoClass* techno = (TechnoClass*)object; + if (techno->Techno_Type_Class()->Primary != WEAPON_NONE || techno->Techno_Type_Class()->Secondary != WEAPON_NONE) { + action_object = object; + break; + } + } + } + if (action_object == nullptr) { + action_object = CurrentObject[0]; + } + + int index = 0; + for (int y = top; y <= bottom; ++y) { + for (int x = left; x <= right; ++x, ++index) { + Convert_Action_Type(action_object->What_Action(XY_Cell(x, y)), (CurrentObject.Count() == 1) ? action_object : NULL, As_Target(XY_Cell(x, y)), player_info->ActionWithSelected[index]); + } + } + + player_info->ActionWithSelectedCount = Map.MapCellWidth * Map.MapCellHeight; + } + else { + player_info->SelectedID = -1; + player_info->SelectedType = UNKNOWN; + player_info->ActionWithSelectedCount = 0U; + } + + // Screen shake + player_info->ScreenShake = PlayerPtr->ScreenShakeTime; + + return true; +}; + + + + +/************************************************************************************************** +* DLLExportClass::Get_Dynamic_Map_State -- Get a snapshot of the smudges and overlays on the terrain +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 10:45AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + player_id; + + static int _call_count = 0; + + CNCDynamicMapStruct *dynamic_map = (CNCDynamicMapStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*dynamic_map) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + int cell_index = 0; + + bool debug_output = false; + //if (_call_count == 20) { + //debug_output = true; + //} + + // Need to ignore view constraints for dynamic map updates, so the radar map + // has the latest tiberium state for cells outside the tactical view + DLLExportClass::Adjust_Internal_View(true); + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCDynamicMapEntryStruct) * 2; + if (memory_needed >= buffer_size) { + return false; + } + + /* + ** Only cells flagged to be redraw are examined. + */ + //if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Map.Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER || cellptr->IsVisible || Debug_Unshroud) { + Cell_Class_Draw_It(dynamic_map, entry_index, cellptr, xpixel, ypixel, debug_output); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + //if (!cellptr->IsMapped && !Debug_Unshroud) { + // IsShadowPresent = true; + //} + } + //} + } + } + + if (entry_index) { + _call_count++; + } + + dynamic_map->Count = entry_index; + dynamic_map->VortexActive = false; + + return true; +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Cell_Class_Draw_It -- Go through the motions of drawing a cell to get the smudge and overlay info +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 11:09AM - ST +**************************************************************************************************/ +void DLLExportClass::Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output) +{ + /* + ** + ** Based on CellClass::Draw_It and SmudgeTypeClass::Draw_It + ** + ** + */ + + CELL cell = cell_ptr->Cell_Number(); + + /* + ** Redraw any smudge. + */ + if (cell_ptr->Smudge != SMUDGE_NONE) { + //SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + + const SmudgeTypeClass &smudge_type = SmudgeTypeClass::As_Reference(cell_ptr->Smudge); + + if (smudge_type.Get_Image_Data() != NULL) { + + if (debug_output) { + IsTheaterShape = true; + Debug_Write_Shape_Type(&smudge_type, 0); + IsTheaterShape = false; + } + + CNCDynamicMapEntryStruct &smudge_entry = dynamic_map->Entries[entry_index++]; + + strncpy(smudge_entry.AssetName, smudge_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + smudge_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + smudge_entry.Type = (short) cell_ptr->Smudge; + smudge_entry.Owner = (char)cell_ptr->Owner; + smudge_entry.DrawFlags = SHAPE_WIN_REL; // Looks like smudges are drawn top left + smudge_entry.PositionX = xpixel; + smudge_entry.PositionY = ypixel; + smudge_entry.Width = Get_Build_Frame_Width(smudge_type.Get_Image_Data()); + smudge_entry.Height = Get_Build_Frame_Height(smudge_type.Get_Image_Data()); + smudge_entry.CellX = Cell_X(cell); + smudge_entry.CellY = Cell_Y(cell); + smudge_entry.ShapeIndex = cell_ptr->SmudgeData; + smudge_entry.IsSmudge = true; + smudge_entry.IsOverlay = false; + smudge_entry.IsResource = false; + smudge_entry.IsSellable = false; + smudge_entry.IsTheaterShape = true; // Smudges are always theater-specific + smudge_entry.IsFlag = false; + } + } + + /* + ** Draw the overlay object. + */ + if (cell_ptr->Overlay != OVERLAY_NONE) { + //OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + //IsTheaterShape = (bool)otype.IsTheater; + //CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + //IsTheaterShape = false; + + const OverlayTypeClass &overlay_type = OverlayTypeClass::As_Reference(cell_ptr->Overlay); + + if (overlay_type.Get_Image_Data() != NULL) { + + CNCDynamicMapEntryStruct &overlay_entry = dynamic_map->Entries[entry_index++]; + + + if (debug_output) { + IsTheaterShape = (bool)overlay_type.IsTheater; + Debug_Write_Shape_Type(&overlay_type, 0); + IsTheaterShape = false; + } + + strncpy(overlay_entry.AssetName, overlay_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + overlay_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + overlay_entry.Type = (short)cell_ptr->Overlay; + overlay_entry.Owner = (char) cell_ptr->Owner; + overlay_entry.DrawFlags = SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST; // Looks like overlays are drawn centered and translucent + overlay_entry.PositionX = xpixel + (CELL_PIXEL_W>>1); + overlay_entry.PositionY = ypixel + (CELL_PIXEL_H>>1); + overlay_entry.Width = Get_Build_Frame_Width(overlay_type.Get_Image_Data()); + overlay_entry.Height = Get_Build_Frame_Height(overlay_type.Get_Image_Data()); + overlay_entry.CellX = Cell_X(cell); + overlay_entry.CellY = Cell_Y(cell); + overlay_entry.ShapeIndex = cell_ptr->OverlayData; + overlay_entry.IsSmudge = false; + overlay_entry.IsOverlay = true; + overlay_entry.IsResource = overlay_entry.Type >= OVERLAY_TIBERIUM1 && overlay_entry.Type <= OVERLAY_TIBERIUM12; + overlay_entry.IsSellable = overlay_entry.Type >= OVERLAY_SANDBAG_WALL && overlay_entry.Type <= OVERLAY_WOOD_WALL; + overlay_entry.IsTheaterShape = (bool)overlay_type.IsTheater; + overlay_entry.IsFlag = false; + } + } + + + if (cell_ptr->IsFlagged) { + + const void* image_data = MixFileClass::Retrieve("FLAGFLY.SHP"); + if (image_data != NULL) { + + CNCDynamicMapEntryStruct &flag_entry = dynamic_map->Entries[entry_index++]; + + strncpy(flag_entry.AssetName, "FLAGFLY", CNC_OBJECT_ASSET_NAME_LENGTH); + flag_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + flag_entry.Type = -1; + flag_entry.Owner = cell_ptr->Owner; + flag_entry.DrawFlags = SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING; + flag_entry.PositionX = xpixel + (ICON_PIXEL_W / 2); + flag_entry.PositionY = ypixel + (ICON_PIXEL_H / 2); + flag_entry.Width = Get_Build_Frame_Width(image_data); + flag_entry.Height = Get_Build_Frame_Height(image_data); + flag_entry.CellX = Cell_X(cell); + flag_entry.CellY = Cell_Y(cell); + flag_entry.ShapeIndex = Frame % 14; + flag_entry.IsSmudge = false; + flag_entry.IsOverlay = false; + flag_entry.IsResource = false; + flag_entry.IsSellable = false; + flag_entry.IsTheaterShape = false; + flag_entry.IsFlag = true; + } + + } + +} + + + + +/************************************************************************************************** +* DLLExportClass::Glyphx_Queue_AI -- Special queue processing for Glyphx multiplayer mode +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Glyphx_Queue_AI(void) +{ + + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + OutList.Next(); + } + + /* + ** Based on Execute_DoList in queue.cpp + ** + ** The events have the ID of the player encoded in them, so no special per-player processing should be needed. + ** When the event is created, the 'local player' is assumed to be the originator of the event, so PlayerPtr will need + ** to be swapped out to represent the real originating player prior to any events being created as a result of GlyphX input + ** + ** ST - 3/12/2019 10:51AM + */ + + for (int i = 0; i < MPlayerCount; i++) { + + HousesType house; + HouseClass *housep; + + house = MPlayerHouses [i]; + housep= HouseClass::As_Pointer (house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!housep){ + continue; + } + + if (!housep->IsHuman){ + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (int j = 0; j < DoList.Count; j++) { + + if (!DoList[j].IsExecuted && (unsigned)Frame >= DoList[j].Frame) { + DoList[j].Execute(); + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + } + } + } + + + + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + while (DoList.Count) { + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + } + else { + break; + } + } + +} + + + + + +/************************************************************************************************** +* DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:10PM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Sidebars(void) +{ + for (int i=0 ; iClass->House); + } + return true; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + for (int i=0 ; iClass->House); + CurrentLocalPlayerIndex = i; + + return true; + } + } + + return false; +} + + + +/************************************************************************************************** +* DLLExportClass::Reset_Player_Context -- Clear out old player context data +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 10:36AM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Player_Context(void) +{ + for (int i=0 ; iIs_Techno()) { + return; + } + + TechnoClass *tech = static_cast(object); + + //HousesType house = tech->House->Class->House; + DLLExportClass::Logic_Switch_Player_Context(tech->House); +} + + + +/************************************************************************************************** +* Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void Logic_Switch_Player_Context(HouseClass *object) +{ + DLLExportClass::Logic_Switch_Player_Context(object); +} + + +/************************************************************************************************** +* DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void DLLExportClass::Logic_Switch_Player_Context(HouseClass *house) +{ + if (GameToPlay == GAME_NORMAL) { + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + return; + } + + if (house == NULL) { + return; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + HousesType house_type = house->Class->House; + + for (int i=0 ; iClass->House); + CurrentLocalPlayerIndex = i; + + return; + } + } +} + + +/************************************************************************************************** +* DLLExportClass::Calculate_Start_Positions -- Calculate the initial view positions for the players +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 6/12/2019 3:00PM - ST +**************************************************************************************************/ +void DLLExportClass::Calculate_Start_Positions(void) +{ + if (GameToPlay == GAME_NORMAL) { + MultiplayerStartPositions[0] = Views[0]; + return; + } + + HouseClass *player_ptr = PlayerPtr; + + ScenarioInit++; + COORDINATE old_tac = Map.TacticalCoord; + for (int i=0 ; iClass->House; + + for (int i=0 ; iIsActive) { + GlyphX_Debug_Print("DLLExportClass::Repair -- trying to repair a non-active building"); + } else { + + if (building && building->Can_Repair() && building->House && building->House->Class->House == PlayerPtr->Class->House) + { + building->Repair(-1); + } + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Sell_Mode -- Starts the player's sell mode. All it does here is unselect all units. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + Unselect_All(); +} + +/************************************************************************************************** +* DLLExportClass::Sell -- Sell's a player's speceific building. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell(uint64 player_id, int object_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + TARGET target = Build_Target(KIND_BUILDING, object_id); + if (target != TARGET_NONE) + { + BuildingClass* building = As_Building(target); + if (building) { + if (!building->IsActive) { + GlyphX_Debug_Print("DLLExportClass::Sell -- trying to sell a non-active building"); + } else { + if (building->House && building->House->Class->House == PlayerPtr->Class->House) + { + building->Sell_Back(1); + } + } + } + } +} + + + +/************************************************************************************************** +* DLLExportClass::Repair_Sell_Cancel -- Ends the player's repair or sell mode. Doesn't do anything right now. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair_Sell_Cancel(uint64 player_id) +{ + //OutputDebugString("Repair_Sell_Cancel\n"); +} + +/************************************************************************************************** +* DLLExportClass::Scatter_Selected -- Scatter the selected units +* +* In: +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::Scatter_Selected(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, tech->As_Target())); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Next_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Next_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Previous_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Previous_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Guard_Mode +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Guard_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA)); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Stop +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Stop(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // Copied from TiberianDawn/Conquer.cpp - Keyboard_Process() with VK_S + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && + tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, tech->As_Target())); + } + } + } +} + + +/************************************************************************************************** +* DLLExportClass::Units_Queued_Movement_Toggle +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Units_Queued_Movement_Toggle(uint64 /*player_id*/, bool /*toggle*/) +{ + // Currently Red Alert only but stubbed in support in case we add to Tiberian Dawn later +} + +/************************************************************************************************** +* DLLExportClass::Team_Units_Formation_Toggle_On +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // Red Alert only at this time, unless we do some updates to support in Tiberian Dawn + #if 0 + Toggle_Formation(); // Conquer.cpp + #endif +} + + +/************************************************************************************************** +* CNC_Handle_Debug_Request -- Process a debug input request +* +* In: +* +* +* Out: +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (debug_request_type) { + + case DEBUG_REQUEST_SPAWN_OBJECT: + { + DLLExportClass::Debug_Spawn_Unit(object_name, x, y, enemy); + } + break; + + case DEBUG_REQUEST_FORCE_CRASH: + Debug_Force_Crash = true; + break; + + case DEBUG_REQUEST_KILL_OBJECT: + if (strcmp(object_name, "HEAL") == 0) { + DLLExportClass::Debug_Heal_Unit(x, y); + } + else { + DLLExportClass::Debug_Kill_Unit(x, y); + } + break; + + case DEBUG_REQUEST_END_GAME: + { + bool win = true; + + const char lose[] = "LOSE"; + if (strcmp(lose, object_name) == 0) { + win = false; + } + + PlayerWins = win; + PlayerLoses = !win; + } + break; + + case DEBUG_REQUEST_UNSHROUD: + Debug_Unshroud = unshroud; + Map.Flag_To_Redraw(true); + break; + + case DEBUG_REQUEST_SUPERWEAPON_RECHARGE: + PlayerPtr->IonCannon.Forced_Charge(true); + PlayerPtr->NukeStrike.Forced_Charge(true); + PlayerPtr->AirStrike.Forced_Charge(true); + break; + + case DEBUG_REQUEST_END_PRODUCTION: + { + for (int index = 0; index < Factories.Count(); index++) { + FactoryClass* factory = Factories.Ptr(index); + if (factory->Get_House()->IsHuman) { + Factories.Ptr(index)->Force_Complete(); + } + } + } + break; + + case DEBUG_REQUEST_ADD_RESOURCES: + { + if (object_name) { + int amount = atoi(object_name); + PlayerPtr->Credits += amount; + if (PlayerPtr->Credits < 0) { + PlayerPtr->Credits = 0; + } + } + } + break; + + case DEBUG_REQUEST_UNLOCK_BUILDABLES: + PlayerPtr->DebugUnlockBuildables = !PlayerPtr->DebugUnlockBuildables; + PlayerPtr->IsRecalcNeeded = true; + break; + + default: + break; + } + + +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: Object to unlimbo , x & y cell positions +* +* +* Out: True if unlimbo succeeded +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +bool DLLExportClass::Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y) +{ + if (techno) { + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_right = map_cell_x + Map.MapCellWidth; + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + map_cell_right = min(map_cell_right, cell_x + 26); // Generally try to prevent the objects from spawing off the right of the screen + + int try_x = cell_x; + int try_y = cell_y; + + while (try_y < map_cell_bottom) { + + CELL cell = XY_Cell(try_x, try_y); + + if (techno->Unlimbo(Cell_Coord(cell))) { + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + + cell_x = try_x; + cell_y = try_y; + return true; + } + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + } + + cell_x = try_x; + cell_y = try_y; + } + + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: +* +* Out: +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_All(int x, int y) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + int origin_x = map_cell_x + 2; + int origin_y = map_cell_y + 2; + + if (x != 0 || y != 0) { + CELL screen_cell = Coord_Cell(Map.Pixel_To_Coord(x, y)); + origin_x = Cell_X(screen_cell); + origin_y = Cell_Y(screen_cell); + } + + int try_x = origin_x; + int try_y = origin_y; + + HousesType house = PlayerPtr->Class->House; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const & building_type = BuildingTypeClass::As_Reference(sindex); + + if (building_type.IsBuildable) { + + BuildingClass * building = new BuildingClass(building_type, house); + if (building) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(building, try_x, try_y)) { + break; + } + } + } + } + } + + + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const & unit_type = UnitTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (unit_type.IsBuildable) { + + UnitClass * unit = (UnitClass*) unit_type.Create_One_Of(PlayerPtr); + if (unit) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(unit, try_x, try_y)) { + break; + } + } + } + } + } + + + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + InfantryTypeClass const &infantry_type = InfantryTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (infantry_type.IsBuildable) { + + InfantryClass * inf = (InfantryClass*) infantry_type.Create_One_Of(PlayerPtr); + if (inf) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(inf, try_x, try_y)) { + break; + } + } + } + } + } + + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + AircraftTypeClass const &aircraft_type = AircraftTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (aircraft_type.IsBuildable) { + + AircraftClass * air = (AircraftClass*) aircraft_type.Create_One_Of(PlayerPtr); + if (air) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(air, try_x, try_y)) { + break; + } + } + } + } + } + +} + + + + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_Unit -- Debug spawn a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy) +{ + if (object_name == NULL) { + return; + } + + if (strlen(object_name) == 0) { + return; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + HousesType house = PlayerPtr->Class->House; + + /* + ** Place all? + */ + if (stricmp(object_name, "ALLOBJECTS") == 0) { + Debug_Spawn_All(x, y); + return; + } + + /* + ** If this is for the enemy, find the enemy with the most stuff + */ + + if (enemy) { + unsigned max_count = 0; + for (int i = 0; i < Houses.Count(); ++i) { + const HouseClass* player = Houses.Ptr(i); + const unsigned count = player->CurUnits + player->CurBuildings + player->CurInfantry + player->CurAircraft; + if (!PlayerPtr->Is_Ally(player) && (count >= max_count)) { + house = player->Class->House; + max_count = count; + } + } + } + + /* + ** What is this thing? + */ + + StructType structure_type = BuildingTypeClass::From_Name(object_name); + if (structure_type != STRUCT_NONE) { + + BuildingClass * building = new BuildingClass(structure_type, house); + if (building) { + if (!building->Unlimbo(Cell_Coord(cell))) { + delete building; + } + } + +#if (0) + Map.PendingObject = &BuildingTypeClass::As_Reference(structure_type); + Map.PendingHouse = PlayerPtr->ActLike; + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(PlayerPtr); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + + //OutList.Add(EventClass(EventClass::PLACE, RTTI_BUILDING, (CELL)(cell + Map.ZoneOffset))); + } +#endif + return; + } + + + UnitType unit_type = UnitTypeClass::From_Name(object_name); + if (unit_type != UNIT_NONE) { + + UnitClass * unit = new UnitClass(unit_type, house); + if (unit) { + unit->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + + return; + } + + + InfantryType infantry_type = InfantryTypeClass::From_Name(object_name); + if (infantry_type != INFANTRY_NONE) { + + InfantryClass * inf = new InfantryClass(infantry_type, house); + if (inf) { + inf->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + AircraftType aircraft_type = AircraftTypeClass::From_Name(object_name); + if (aircraft_type != AIRCRAFT_NONE) { + + AircraftClass * air = new AircraftClass(aircraft_type, house); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + OverlayType overlay_type = OverlayTypeClass::From_Name(object_name); + if (overlay_type != OVERLAY_NONE) + { + new OverlayClass(overlay_type, cell); + return; + } +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Kill_Unit -- Kill a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 8/19/2019 3:09PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Kill_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + static const int debug_damage = 1000; + if (obj) { + int damage = debug_damage; + obj->Take_Damage(damage, 0, WARHEAD_HE, 0); + } else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + cellptr->Reduce_Tiberium(1); + } + if (optr->IsWall) { + Map[cell].Reduce_Wall(debug_damage); + } + } + } + } +} + +void DLLExportClass::Debug_Heal_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + if (obj) { + obj->Strength = obj->Class_Of().MaxStrength; + } + else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + const int cellcount = (int)FACING_COUNT + 1; + CellClass* cells[cellcount]; + cells[0] = cellptr; + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + cells[(int)index + 1] = &cellptr->Adjacent_Cell(index); + } + + for (int index = 0; index < cellcount; index++) { + CellClass *newcell = cells[index]; + + if (newcell && newcell->Cell_Object() == NULL) { + if (newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) { + switch (newcell->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE3: + case TEMPLATE_BRIDGE4: + break; + + default: + new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number()); + newcell->OverlayData = 1; + break; + + } + } + else if (newcell->Land_Type() == LAND_TIBERIUM) { + newcell->OverlayData = MIN(newcell->OverlayData + 1, 11); + newcell->Recalc_Attributes(); + newcell->Redraw_Objects(); + } + } + } + } + } + } + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Legacy_Render_Enabled -- Is the legacy rendering enabled? +* +* In: +* +* Out: +* +* +* +* History: 4/15/2019 5:46PM - ST +**************************************************************************************************/ +bool DLLExportClass::Legacy_Render_Enabled(void) +{ + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + unsigned int num_humans = 0U; + for (int i = 0; i < MPlayerCount; ++i) { + HouseClass *player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); + if (player_ptr && player_ptr->IsHuman) { + if (++num_humans > 1) break; + } + } + return num_humans < 2; + } + + //return false; + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Computer_Message -- Replacement for original Computer_Message function +* +* In: +* +* Out: +* +* +* +* History: 1/27/2020 1:42PM - ST +**************************************************************************************************/ +void DLLExportClass::Computer_Message(bool last_player_taunt) +{ + HousesType house; + HouseClass *ptr; + + HouseClass *ai_players[MAX_PLAYERS]; + int ai_player_count = 0; + + /*------------------------------------------------------------------------ + Find the computer house that the message will be from + ------------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + ai_players[ai_player_count++] = ptr; + } + + if (ai_player_count) { + int ai_player_index = 0; + if (ai_player_count > 1) { + ai_player_index = IRandom(0, ai_player_count - 1); + } + + int taunt_index; + if (last_player_taunt) { + taunt_index = 13; + } else { + taunt_index = IRandom(0,12); + } + + On_Message(ai_players[ai_player_index], "", 15.0f, MESSAGE_TYPE_COMPUTER_TAUNT, taunt_index); + } +} + + +/************************************************************************************************** +* DLLExportClass::Set_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Set_Special_Key_Flags(unsigned char special_key_flags) +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = special_key_flags; +} + +/************************************************************************************************** +* DLLExportClass::Clear_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Clear_Special_Key_Flags() +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = 0; +} + +/************************************************************************************************** +* DLLExportClass::Get_Input_Key_State -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLLExportClass::Get_Input_Key_State(KeyNumType key) +{ + switch (key) + { + case KN_LCTRL: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_CTRL) != 0; + break; + case KN_LSHIFT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_SHIFT) != 0; + break; + case KN_LALT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_ALT) != 0; + break; + default: + break; + }; + + return false; + +} + + +/************************************************************************************************** +* Get_Input_Key_State +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLL_Export_Get_Input_Key_State(KeyNumType key) +{ + return DLLExportClass::Get_Input_Key_State(key); +} + + + +bool DLLSave(FileClass &file) +{ + return DLLExportClass::Save(file); +} + +bool DLLLoad(FileClass &file) +{ + return DLLExportClass::Load(file); +} + + +/************************************************************************************************** +* DLLExportClass::Save -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +bool DLLExportClass::Save(FileClass & file) +{ + /* + ** Version first + */ + unsigned int version = CNC_DLL_API_VERSION; + if (file.Write(&version, sizeof(version)) != sizeof(version)) { + return false; + } + + if (file.Write(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { + return false; + } + + if (file.Write(GlyphxPlayerIDs, sizeof(GlyphxPlayerIDs)) != sizeof(GlyphxPlayerIDs)) { + return false; + } + + if (file.Write(&GlyphXClientSidebarWidthInLeptons, sizeof(GlyphXClientSidebarWidthInLeptons)) != sizeof(GlyphXClientSidebarWidthInLeptons)) { + return false; + } + + if (file.Write(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { + return false; + } + + if (file.Write(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { + return false; + } + + if (file.Write(PlacementType, sizeof(PlacementType)) != sizeof(PlacementType)) { + return false; + } + + if (file.Write(&MPlayerCount, sizeof(MPlayerCount)) != sizeof(MPlayerCount)) { + return false; + } + + if (file.Write(&MPlayerBases, sizeof(MPlayerBases)) != sizeof(MPlayerBases)) { + return false; + } + + if (file.Write(&MPlayerCredits, sizeof(MPlayerCredits)) != sizeof(MPlayerCredits)) { + return false; + } + + if (file.Write(&MPlayerTiberium, sizeof(MPlayerTiberium)) != sizeof(MPlayerTiberium)) { + return false; + } + + if (file.Write(&MPlayerGoodies, sizeof(MPlayerGoodies)) != sizeof(MPlayerGoodies)) { + return false; + } + + if (file.Write(&MPlayerGhosts, sizeof(MPlayerGhosts)) != sizeof(MPlayerGhosts)) { + return false; + } + + if (file.Write(&MPlayerSolo, sizeof(MPlayerSolo)) != sizeof(MPlayerSolo)) { + return false; + } + + if (file.Write(&MPlayerUnitCount, sizeof(MPlayerUnitCount)) != sizeof(MPlayerUnitCount)) { + return false; + } + + if (file.Write(&MPlayerLocalID, sizeof(MPlayerLocalID)) != sizeof(MPlayerLocalID)) { + return false; + } + + if (file.Write(MPlayerHouses, sizeof(MPlayerHouses)) != sizeof(MPlayerHouses)) { + return false; + } + + if (file.Write(MPlayerNames, sizeof(MPlayerNames)) != sizeof(MPlayerNames)) { + return false; + } + + if (file.Write(MPlayerID, sizeof(MPlayerID)) != sizeof(MPlayerID)) { + return false; + } + + if (file.Write(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { + return false; + } + + for (int i=0 ; iType; + } + } +} + + + +/************************************************************************************************** +* DLLExportClass::Decode_Pointers -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +void DLLExportClass::Decode_Pointers(void) +{ + for (int i=0 ; i(PlacementType[i]); + PlacementType[i] = NULL; + if (type >= STRUCT_FIRST && type < STRUCT_COUNT) { + + TechnoTypeClass const * tech = Fetch_Techno_Type(RTTI_BUILDINGTYPE, type); + if (tech) { + BuildingTypeClass* build_type = (BuildingTypeClass*)(tech); + if (build_type) { + PlacementType[i] = build_type; + } + } + } + } + } +} + + +void DLL_Code_Pointers(void) +{ + DLLExportClass::Code_Pointers(); +} + +void DLL_Decode_Pointers(void) +{ + DLLExportClass::Decode_Pointers(); +} \ No newline at end of file diff --git a/TIBERIANDAWN/DLLInterface.h b/TIBERIANDAWN/DLLInterface.h new file mode 100644 index 000000000..d2f2a2d59 --- /dev/null +++ b/TIBERIANDAWN/DLLInterface.h @@ -0,0 +1,927 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#pragma once + +#ifndef DLL_INTERFACE_H +#define DLL_INTERFACE_H + +struct CarryoverObjectStruct; + + +/* +** DLL Interface version +** +** +** +*/ +#define CNC_DLL_API_VERSION 0x100 + + + +#define MAX_EXPORT_CELLS (128 * 128) + +#ifdef TIBERIAN_DAWN +#define MAP_MAX_CELL_WIDTH 64 +#define MAP_MAX_CELL_HEIGHT 64 +#else +#define MAP_MAX_CELL_WIDTH 128 +#define MAP_MAX_CELL_HEIGHT 128 +#endif + + +/* +** Interface structs require stricter packing +** +** +*/ +#pragma pack(push) +#pragma pack(1) + + + + +/************************************************************************************** +** +** Game state request types +** +** +*/ +enum GameStateRequestEnum { + GAME_STATE_NONE, + GAME_STATE_STATIC_MAP, + GAME_STATE_DYNAMIC_MAP, + GAME_STATE_LAYERS, + GAME_STATE_SIDEBAR, + GAME_STATE_PLACEMENT, + GAME_STATE_SHROUD, + GAME_STATE_OCCUPIER, + GAME_STATE_PLAYER_INFO +}; + + + + +/************************************************************************************** +** +** Static map data (tiles) +** +** +*/ +struct CNCStaticCellStruct { + char TemplateTypeName[32]; + int IconNumber; +}; + + + +enum CnCTheaterType { + CNC_THEATER_NONE=-1, + CNC_THEATER_DESERT, + CNC_THEATER_JUNGLE, + CNC_THEATER_TEMPERATE, + CNC_THEATER_WINTER, + + CNC_THEATER_COUNT, + CNC_THEATER_FIRST=0 +}; + + +struct CNCMapDataStruct { + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + int OriginalMapCellX; + int OriginalMapCellY; + int OriginalMapCellWidth; + int OriginalMapCellHeight; + + CnCTheaterType Theater; + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + CNCStaticCellStruct StaticCells[MAX_EXPORT_CELLS]; +}; + + + + +/************************************************************************************** +** +** Object type enum +** +** +*/ +#define DLL_LAYER_COUNT 3 + +enum DllObjectTypeEnum { + UNKNOWN, + INFANTRY, + UNIT, + AIRCRAFT, + BUILDING, + TERRAIN, + ANIM, + BULLET, + OVERLAY, + SMUDGE, + OBJECT, + SPECIAL, + INFANTRY_TYPE, + UNIT_TYPE, + AIRCRAFT_TYPE, + BUILDING_TYPE, + VESSEL, + VESSEL_TYPE +}; + + + +/************************************************************************************** +** +** Object action types +** +** +*/ +enum DllActionTypeEnum : unsigned char { + DAT_NONE, + DAT_MOVE, + DAT_NOMOVE, + DAT_ENTER, + DAT_SELF, + DAT_ATTACK, + DAT_ATTACK_OUT_OF_RANGE, + DAT_GUARD, + DAT_SELECT, + DAT_CAPTURE, + DAT_SABOTAGE, + DAT_HEAL, + DAT_DAMAGE, + DAT_TOGGLE_PRIMARY, + DAT_CANT_DEPLOY, + DAT_REPAIR, + DAT_CANT_REPAIR +}; + + + + + + +/************************************************************************************** +** +** Object state data +** +** +*/ +#define MAX_OCCUPY_CELLS 36 +#define MAX_OBJECT_PIPS 18 +#define MAX_OBJECT_LINES 3 +#define MAX_HOUSES 32 + +struct CNCObjectLineStruct { + int X; + int Y; + int X1; + int Y1; + int Frame; + unsigned char Color; +}; + +#define CNC_OBJECT_ASSET_NAME_LENGTH 16 +struct CNCObjectStruct { + void *CNCInternalObjectPointer; + char TypeName[CNC_OBJECT_ASSET_NAME_LENGTH]; + char AssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + DllObjectTypeEnum Type; + int ID; + int BaseObjectID; + DllObjectTypeEnum BaseObjectType; + int PositionX; + int PositionY; + int Width; + int Height; + int Altitude; + int SortOrder; + int Scale; + int DrawFlags; + short MaxStrength; + short Strength; + unsigned short ShapeIndex; + unsigned short CellX; + unsigned short CellY; + unsigned short CenterCoordX; + unsigned short CenterCoordY; + short SimLeptonX; + short SimLeptonY; + unsigned char DimensionX; + unsigned char DimensionY; + unsigned char Rotation; + unsigned char MaxSpeed; + char Owner; + char RemapColor; + char SubObject; + bool IsSelectable; + unsigned int IsSelectedMask; + bool IsRepairing; + bool IsDumping; + bool IsTheaterSpecific; + unsigned int FlashingFlags; + unsigned char Cloak; + bool CanRepair; + bool CanDemolish; + bool CanDemolishUnit; + short OccupyList[MAX_OCCUPY_CELLS]; + int OccupyListLength; + int Pips[MAX_OBJECT_PIPS]; + int NumPips; + int MaxPips; + CNCObjectLineStruct Lines[MAX_OBJECT_LINES]; + int NumLines; + bool RecentlyCreated; + bool IsALoaner; + bool IsFactory; + bool IsPrimaryFactory; + bool IsDeployable; + bool IsAntiGround; + bool IsAntiAircraft; + bool IsSubSurface; + bool IsNominal; + bool IsDog; + bool IsIronCurtain; + bool IsInFormation; + bool CanMove[MAX_HOUSES]; + bool CanFire[MAX_HOUSES]; + bool CanDeploy; + bool CanHarvest; + bool CanPlaceBombs; + bool IsFixedWingedAircraft; + bool IsFake; + unsigned char ControlGroup; + unsigned int VisibleFlags; + unsigned int SpiedByFlags; + char ProductionAssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; + const char* OverrideDisplayName; + DllActionTypeEnum ActionWithSelected[MAX_HOUSES]; + + static const unsigned int VISIBLE_FLAGS_ALL = 0xffffffff; +}; + +struct CNCObjectListStruct { + int Count; + CNCObjectStruct Objects[1]; // Variable length +}; + + + + +/************************************************************************************** +** +** Placement validity data +** +** Used to pass back info about tructure placement validity +*/ +struct CNCPlacementCellInfoStruct { + bool PassesProximityCheck; // If the structure was placed in this cell, does that satisfy the proximity check for the whole structure? + bool GenerallyClear; // Is this cell generally clear of obstructions that would prevent placement? +}; + +struct CNCPlacementInfoStruct { + int Count; + CNCPlacementCellInfoStruct CellInfo[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Sidebar/construction state data +** +** +*/ +enum DllSuperweaponTypeEnum { + SW_NONE, + SW_UNKNOWN, + + //TD values + SW_NUKE, + SW_AIR_STRIKE, + SW_ION_CANNON, + + //RA values + SW_SONAR_PULSE, + SW_CHRONOSPHERE, + SW_PARA_BOMB, + SW_PARA_INFANTRY, + SW_SPY_MISSION, + SW_IRON_CURTAIN, + SW_GPS, + SW_CHRONOSPHERE_DESTINATION +}; + +struct CNCSidebarEntryStruct { + char AssetName[16]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + int BuildableType; // This is the original buildable type that should be passed back if we want to start/cancel construction + int BuildableID; // This is the original buildable id that should be passed back if we want to start/cancel construction + DllObjectTypeEnum Type; // Type converted to shared enum + DllSuperweaponTypeEnum SuperWeaponType; + int Cost; // Cost to construct + int PowerProvided; // Power cost to construct + int BuildTime; // Cost to construct + float Progress; // Construction progress (0.0 - 1.0) + short PlacementList[MAX_OCCUPY_CELLS]; // Which cells this structure occupies for placement (if structure) + int PlacementListLength; // How many cells + bool Completed; // Construction has completed + bool Constructing; // Is it currently constructing + bool ConstructionOnHold; // Is the current construction on hold + bool Busy; // Is the associated factory busy + bool BuildableViaCapture; // Is this buildable due to the capture of a structure of a different faction. This will be false for captured structures of the same faction (ActLike) + bool Fake; // Is this a fake structure? +}; + + +struct CNCSidebarStruct { + int EntryCount[2]; // Counts for the left and right columns + int Credits; // Amount of currency available (excluding Tiberium) + int CreditsCounter; // Visible credits to display in the sidebar (includes count up/down logic) + int Tiberium; // Amount of Tiberium in reserve + int MaxTiberium; // Maximum amount of Tiberium storage available + int PowerProduced; + int PowerDrained; + int MissionTimer; + unsigned int UnitsKilled; // Total count of enemy units killed by this player; Includes Infantry, Vehicles, Aircraft + unsigned int BuildingsKilled; // Total count of enemy structures killed by this player + unsigned int UnitsLost; // Total count player-owned units killed/lost + unsigned int BuildingsLost; // Total count player-owned structures killed/lost + unsigned int TotalHarvestedCredits; // Complete total of gained credits over the match (does not include starting credits) + bool RepairBtnEnabled; + bool SellBtnEnabled; + bool RadarMapActive; + + CNCSidebarEntryStruct Entries[1]; // Variable length column entries +}; + + + +enum SidebarRequestEnum { + SIDEBAR_REQUEST_START_CONSTRUCTION, + SIDEBAR_REQUEST_HOLD_CONSTRUCTION, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, + SIDEBAR_REQUEST_START_PLACEMENT, + SIDEBAR_REQUEST_PLACE, + SIDEBAR_CANCEL_PLACE, + SIDEBAR_CLICK_REPAIR, + SIDEBAR_REQUEST_ENABLE_QUEUE, + SIDEBAR_REQUEST_DISABLE_QUEUE, + SIDEBAR_REQUEST_START_CONSTRUCTION_MULTI, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION_MULTI +}; + +enum SuperWeaponRequestEnum { + SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON +}; + +enum ControlGroupRequestEnum { + CONTROL_GROUP_REQUEST_CREATE, + CONTROL_GROUP_REQUEST_TOGGLE, + CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION, +}; + + + +/************************************************************************************** +** +** Input events sent into the DLL +** +** +*/ +enum InputRequestEnum { + INPUT_REQUEST_NONE, + INPUT_REQUEST_MOUSE_MOVE, + INPUT_REQUEST_MOUSE_LEFT_CLICK, + INPUT_REQUEST_MOUSE_RIGHT_DOWN, + INPUT_REQUEST_MOUSE_RIGHT_CLICK, + INPUT_REQUEST_MOUSE_AREA, + INPUT_REQUEST_MOUSE_AREA_ADDITIVE, + INPUT_REQUEST_SELL_AT_POSITION, + INPUT_REQUEST_SELECT_AT_POSITION, + INPUT_REQUEST_COMMAND_AT_POSITION, + INPUT_REQUEST_SPECIAL_KEYS +}; + + +/************************************************************************************** +** +** Structure Requests Repair, Sell +** +** +*/ +enum StructureRequestEnum { + INPUT_STRUCTURE_NONE, + INPUT_STRUCTURE_REPAIR_START, + INPUT_STRUCTURE_REPAIR, + INPUT_STRUCTURE_SELL_START, + INPUT_STRUCTURE_SELL, + INPUT_STRUCTURE_CANCEL, +}; + + +/************************************************************************************** +** +** Unit Requests Scatter, Select Next, Select Previous, Guard Mode, Stop +** +** +*/ +enum UnitRequestEnum { + INPUT_UNIT_NONE, + INPUT_UNIT_SCATTER, + INPUT_UNIT_SELECT_NEXT, + INPUT_UNIT_SELECT_PREVIOUS, + INPUT_UNIT_GUARD_MODE, + INPUT_UNIT_STOP, + INPUT_UNIT_FORMATION_TOGGLE, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_ON, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_OFF, // RA Only +}; + + +/************************************************************************************** +** +** Game Action Requests +** +** +*/ +enum GameRequestEnum { + INPUT_GAME_MOVIE_DONE, + INPUT_GAME_LOADING_DONE, +}; + + + +/************************************************************************************** +** +** Special Keys +** +** +*/ +enum SpecialKeyRequestEnum { + INPUT_SPECIAL_KEY_CTRL = 0b00000001, + INPUT_SPECIAL_KEY_ALT = 0b00000010, + INPUT_SPECIAL_KEY_SHIFT = 0b00000100, +}; + + + +/************************************************************************************** +** +** Non-static map data. +** +** Per-cell smudges and overlays. Smudges are used for things like craters and structure bibs that draw under units. +** Overlays are things like walls and tiberium that can't move from the cell but aren't flat like smudges. +** +** +*/ +struct CNCDynamicMapEntryStruct { + char AssetName[16]; + int PositionX; + int PositionY; + int Width; + int Height; + short Type; + char Owner; + int DrawFlags; + unsigned char CellX; + unsigned char CellY; + unsigned char ShapeIndex; + bool IsSmudge; + bool IsOverlay; + bool IsResource; + bool IsSellable; + bool IsTheaterShape; + bool IsFlag; +}; + +struct CNCDynamicMapStruct { + bool VortexActive; + int VortexX; + int VortexY; + int VortexWidth; + int VortexHeight; + int Count; + CNCDynamicMapEntryStruct Entries[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Event data +** +** Used to call back into the GlyphX engine for one-time events like sound effect triggers +** +** +*/ +enum EventCallbackType { + CALLBACK_EVENT_INVALID = -1, + CALLBACK_EVENT_SOUND_EFFECT = 0, + CALLBACK_EVENT_SPEECH, + CALLBACK_EVENT_GAME_OVER, + CALLBACK_EVENT_DEBUG_PRINT, + CALLBACK_EVENT_MOVIE, + CALLBACK_EVENT_MESSAGE, + CALLBACK_EVENT_UPDATE_MAP_CELL, + CALLBACK_EVENT_ACHIEVEMENT, + CALLBACK_EVENT_STORE_CARRYOVER_OBJECTS, + CALLBACK_EVENT_SPECIAL_WEAPON_TARGETTING, + CALLBACK_EVENT_BRIEFING_SCREEN, + CALLBACK_EVENT_CENTER_CAMERA, + CALLBACK_EVENT_PING +}; + + +struct GameOverMultiPlayerStatsStruct +{ + GameOverMultiPlayerStatsStruct() + : + GlyphXPlayerID( 0 ), + IsHuman( false ), + WasHuman( false ), + IsWinner( false ), + ResourcesGathered( 0 ), + TotalUnitsKilled( 0 ), + TotalStructuresKilled( 0 ), + Efficiency( 0 ), + Score( 0 ) + { + } + __int64 GlyphXPlayerID; + bool IsHuman; + bool WasHuman; + bool IsWinner; + int ResourcesGathered; + int TotalUnitsKilled; + int TotalStructuresKilled; + int Efficiency; // AKA Economy + int Score; +}; + +#define GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED 8 + +enum EventCallbackMessageEnum { + MESSAGE_TYPE_DIRECT = 0, + MESSAGE_TYPE_PLAYER_DEFEATED, + MESSAGE_TYPE_COMPUTER_TAUNT, + MESSAGE_TYPE_PLAYER_DISCONNECTED +}; + +struct EventCallbackStruct { + + EventCallbackStruct::EventCallbackStruct(void) : EventType(CALLBACK_EVENT_INVALID), GlyphXPlayerID(0) { } + + EventCallbackType EventType; + + __int64 GlyphXPlayerID; + + union { + + struct SoundEffectEvent { + int SFXIndex; + int Variation; + int PixelX; + int PixelY; + int PlayerID; //TO_FIX + char SoundEffectName[ 16 ]; + int SoundEffectPriority; + int SoundEffectContext; + } SoundEffect; + + struct SpeechEvent { + int SpeechIndex; + int PlayerID; //TO_FIX + char SpeechName[ 16 ]; + } Speech; + + struct GameOverEvent { + bool Multiplayer; + // + // Single-player data + // + bool PlayerWins; + const char* MovieName; + const char* MovieName2; + const char* MovieName3; + const char* MovieName4; + const char* AfterScoreMovieName; + int Score; + int Leadership; + int Efficiency; + int CategoryTotal; + int NODKilled; + int GDIKilled; + int CiviliansKilled; + int NODBuildingsDestroyed; + int GDIBuildingsDestroyed; + int CiviliansBuildingsDestroyed; + int RemainingCredits; + int SabotagedStructureType; + int TimerRemaining; + // + // Multi-player data + // + int MultiPlayerTotalPlayers; + GameOverMultiPlayerStatsStruct MultiPlayerPlayersData[ GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ]; + } GameOver; + + struct DebugPrintEvent { + const char *PrintString; + } DebugPrint; + + struct MovieEvent { + const char* MovieName; + int Theme; + bool Immediate; + } Movie; + + struct MessageEvent { + const char* Message; + float TimeoutSeconds; + EventCallbackMessageEnum MessageType; + __int64 MessageParam1; + } Message; + + struct UpdateMapCellEvent { + int CellX; + int CellY; + char TemplateTypeName[32]; + } UpdateMapCell; + + struct AchievementEvent { + const char* AchievementType; + const char* AchievementReason; + } Achievement; + + struct StoreCarryoverObjectsEvent { + CarryoverObjectStruct* CarryoverList; + } StoreCarryoverObjects; + + struct SpecialWeaponTargettingEvent { + int Type; + int ID; + char Name[16]; + DllSuperweaponTypeEnum WeaponType; + } SpecialWeaponTargetting; + + struct CenterCameraEvent { + int CoordX; + int CoordY; + } CenterCamera; + + struct PingEvent { + int CoordX; + int CoordY; + } Ping; + }; + +}; + + + + + + + + +/************************************************************************************** +** +** Multiplayer setup data +** +** Used to pass multiplayer setup info into the C&C code from the GlyphX engine +** +** +*/ + + + +struct CNCMultiplayerOptionsStruct { + //int MPlayerPrefColor; // preferred color index for this player + //int MPlayerColorIdx; // actual color index of this player + //CnCHousesType MPlayerHouse; // House of this player (GDI/NOD) + //unsigned char MPlayerLocalID; // ID of this player + int MPlayerCount; // # of human players in this game + int MPlayerBases; // 1 = bases are on for this scenario + int MPlayerCredits; // # credits everyone gets + int MPlayerTiberium; // 1 = tiberium enabled for this scenario + int MPlayerGoodies; // 1 = goodies enabled for this scenario + int MPlayerGhosts; // 1 = houses with no players will still play + int MPlayerSolo; // 1 = allows a single-player net game + int MPlayerUnitCount; // # units for non-base multiplayer scenarios + bool IsMCVDeploy; // MCV undeploys instead of selling + bool SpawnVisceroids; // Do visceroids spawn + bool EnableSuperweapons; // Are superweapons available + bool MPlayerShadowRegrow; + bool MPlayerAftermathUnits; + bool CaptureTheFlag; + bool DestroyStructures; // New early win condition via destroying all a player's structures +}; + + + +struct CNCSpiedInfoStruct { + int Power; + int Drain; + int Money; +}; + + +struct CNCPlayerInfoStruct { + char Name[64]; + unsigned char House; + int ColorIndex; + unsigned __int64 GlyphxPlayerID; + int Team; + int StartLocationIndex; + unsigned char HomeCellX; + unsigned char HomeCellY; + bool IsAI; + unsigned int AllyFlags; + bool IsDefeated; + unsigned int SpiedPowerFlags; + unsigned int SpiedMoneyFlags; + CNCSpiedInfoStruct SpiedInfo[MAX_HOUSES]; + int SelectedID; + DllObjectTypeEnum SelectedType; + DllActionTypeEnum ActionWithSelected[MAX_EXPORT_CELLS]; + unsigned int ActionWithSelectedCount; + unsigned int ScreenShake; + bool IsRadarJammed; +}; + + + + +// +enum GameRequestType { + GAME_REQUEST_MOVIE_DONE, +}; + + + + +/************************************************************************************** +** +** Rules configuration data +** +** +*/ +struct CNCDifficultyDataStruct +{ + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + + float RepairDelay; + float BuildDelay; + + bool IsBuildSlowdown; + bool IsWallDestroyer; + bool IsContentScan; +}; + +struct CNCRulesDataStruct +{ + CNCDifficultyDataStruct Difficulties[3]; +}; + + + + +/************************************************************************************** +** +** Debug input interface +** +** +*/ + +enum DebugRequestEnum { + DEBUG_REQUEST_SPAWN_OBJECT, + DEBUG_REQUEST_END_GAME, + DEBUG_REQUEST_UNSHROUD, + DEBUG_REQUEST_SUPERWEAPON_RECHARGE, + DEBUG_REQUEST_KILL_OBJECT, + DEBUG_REQUEST_END_PRODUCTION, + DEBUG_REQUEST_ADD_RESOURCES, + DEBUG_REQUEST_UNLOCK_BUILDABLES, + DEBUG_REQUEST_FORCE_CRASH, + DEBUG_REQUEST_SET_GLOBAL_FLAG, +}; + + + + + + +/************************************************************************************** +** +** Shroud data. +** +** Per-cell shroud info +** +** +*/ +struct CNCShroudEntryStruct { + char ShadowIndex; + bool IsVisible; + bool IsMapped; + bool IsJamming; +}; + +struct CNCShroudStruct { + int Count; + CNCShroudEntryStruct Entries[1]; // Variable length +}; + + + + + + +/************************************************************************************** +** +** Occupier data. +** +** Per-cell occupier info +** +** +*/ +struct CNCOccupierObjectStruct { + DllObjectTypeEnum Type; + int ID; +}; + +struct CNCOccupierEntryHeaderStruct { + int Count; +}; + +struct CNCOccupierHeaderStruct { + int Count; +}; + + + + +/************************************************************************************** +** +** Carryover object. +** +** Used to store object data that persists between missions +** +** +*/ +struct CarryoverObjectStruct +{ + CarryoverObjectStruct() : Next(0) {} + + CarryoverObjectStruct* Next; + + int RTTI; + int Type; + int Cell; + int Strength; + int House; +}; + + + + + +/* +** End of strict structure packing +** +** +*/ +#pragma pack(pop) + + + +#endif //DLL_INTERFACE_H \ No newline at end of file diff --git a/TIBERIANDAWN/DLLInterfaceEditor.cpp b/TIBERIANDAWN/DLLInterfaceEditor.cpp new file mode 100644 index 000000000..cb205a5a9 --- /dev/null +++ b/TIBERIANDAWN/DLLInterfaceEditor.cpp @@ -0,0 +1,741 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include +//#include +//#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" +#include "TEMPLATE.H" + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map(char* cncdata_directory, char* house_name, int scenario_index, char* east_west, char* variant); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name(char* cncdata_directory, char* scenario_name); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory, int CD); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points); + +char EditorMapINIBuffer[SHAPE_BUFFER_SIZE]; +bool EditorMapInitialized = false; + +const static int EDITOR_COMMMAND_SUCCESS = 0; +const static int EDITOR_COMMMAND_FAILURE = 1; + +const static char* MixFileNames[] = +{ + "GENERAL.MIX", + "SC-000.MIX", + "SC-001.MIX", + "DESERT.MIX", + "TEMPERAT.MIX", + "WINTER.MIX" +}; + +extern MixFileClass *TheaterIcons; +extern bool Read_Movies_From_Scenario_Ini(char *root, bool fresh); + +/************************************************************************************************** +* CNC_Editor_Startup +* Initializes the system to allow map loading for the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup() +{ + BlackPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + GamePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + OriginalPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + WhitePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + memset(WhitePalette, 63, 768); + + + Set_Palette(GamePalette); + + TheaterData = 0; + TheaterIcons = 0; + LowTheaterData = 0; + + return EDITOR_COMMMAND_SUCCESS; +} + +/************************************************************************************************** +* CNC_Editor_Cleanup +* Cleans up systems initialized by the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup() +{ + if (BlackPalette) + { + delete[] BlackPalette; + } + + if (GamePalette) + { + delete[] GamePalette; + } + + if (OriginalPalette) + { + delete[] OriginalPalette; + } + if (WhitePalette) + { + delete[] WhitePalette; + } + + return EDITOR_COMMMAND_SUCCESS; +} +/************************************************************************************************** +* CNC_Editor_Load_Mix_Files +* Loads all the Mix files for Tiberian Dawn +**************************************************************************************************/ +void CNC_Editor_Load_Mix_Files() +{ + int count = sizeof(MixFileNames) / sizeof(MixFileNames[0]); + + for (int i = count - 1; i >= 0; --i) + { + MixFileClass::Free(MixFileNames[i]); + } + + for (int i = 0; i < count; ++i) + { + new MixFileClass(MixFileNames[i]); + MixFileClass::Cache(MixFileNames[i]); + } +} + +/************************************************************************************************** +* CNC_Editor_Setup_Content_Directory +* Sets up where the system should load map data from. +**************************************************************************************************/ +void CNC_Editor_Setup_Content_Directory(char* cncdata_directory, char* cd_directory) +{ + char content_directory[_MAX_PATH]; + + sprintf(content_directory, "%s\\TIBERIAN_DAWN\\%s\\", cncdata_directory, cd_directory); + + if (strlen(content_directory) != 0) { + CCFileClass::Clear_Search_Drives(); + CCFileClass::Reset_Raw_Path(); + char *dll_dir = strdup(content_directory); + CCFileClass::Set_Search_Drives(dll_dir); + free(dll_dir); + } +} + +void CNC_Editor_Setup_Content_Directory(char* cncdata_directory, int CD_index) +{ + char cd_name[_MAX_PATH]; + sprintf(cd_name, "CD%i", CD_index); + CNC_Editor_Setup_Content_Directory(cncdata_directory, cd_name); +} + + + + +/************************************************************************************************** +* CNC_Editor_Load_Map +* Loads the map with the given parameters. +* +* cncdata_directory: path of the base CNC data directory +* faction: the name of the faction we are loading the map for +* scenario_index: int scenario index +* east_west: +* variant: +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map( + char* cncdata_directory, + char* house_name, + int scenario_index, + char* east_west, + char* variant) +{ + CNC_Editor_Clear_Map(); + + int CD = 1; + if (stricmp(house_name, "GDI") == 0) { + ScenPlayer = SCEN_PLAYER_GDI; + GameToPlay = GAME_NORMAL; + + if (scenario_index == 30 || scenario_index == 90) { + CD = 5; + } + else if (scenario_index >= 60 && scenario_index <= 72) { + CD = 4; + } + else if (scenario_index >= 20 && scenario_index < 60) { + CD = 3; + } + } + + if (stricmp(house_name, "NOD") == 0) { + ScenPlayer = SCEN_PLAYER_NOD; + GameToPlay = GAME_NORMAL; + CD = 2; + + // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 + if (scenario_index == 81 || scenario_index == 22) { + CD = 5; + if (scenario_index == 81) { + scenario_index = 21; + } + } + else if (scenario_index >= 60 && scenario_index <= 61) { + CD = 4; + } + else if (scenario_index >= 20 && scenario_index < 60) { + CD = 3; + } + } + + if (stricmp(house_name, "MULTI") == 0) + { + ScenPlayer = SCEN_PLAYER_MPLAYER; + GameToPlay = GAME_GLYPHX_MULTIPLAYER; + } + + if (stricmp(house_name, "JUR") == 0) + { + ScenPlayer = SCEN_PLAYER_JP; + GameToPlay = GAME_NORMAL; + } + + switch (CD) + { + case 4: + CNC_Editor_Setup_Content_Directory(cncdata_directory, "CONSOLE_1"); + break; + case 5: + CNC_Editor_Setup_Content_Directory(cncdata_directory, "CONSOLE_2"); + break; + default: + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD); + break; + } + + CNC_Editor_Load_Mix_Files(); + + Scenario = scenario_index; + BuildLevel = 7; + + if (stricmp(east_west, "w") == 0) + { + ScenDir = SCEN_DIR_WEST; + } + else + { + ScenDir = SCEN_DIR_EAST; + } + + ScenarioVarType variant_enum; + + if (stricmp(variant, "b") == 0) + { + variant_enum = SCEN_VAR_B; + } + else if (stricmp(variant, "c") == 0) + { + variant_enum = SCEN_VAR_C; + } + else if (stricmp(variant, "d") == 0) + { + variant_enum = SCEN_VAR_D; + } + else + { + variant_enum = SCEN_VAR_A; + } + + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, variant_enum); + + char fname[_MAX_FNAME + _MAX_EXT]; + sprintf(fname, "%s.INI", ScenarioName); + CCFileClass file(fname); + if (!file.Is_Available()) + { + return(EDITOR_COMMMAND_FAILURE); + } + else + { + memset(EditorMapINIBuffer, 0, SHAPE_BUFFER_SIZE); + file.Read(EditorMapINIBuffer, SHAPE_BUFFER_SIZE - 1); + } + + Map.One_Time_Editor(); + Map.Read_INI(EditorMapINIBuffer); + if (Map.Read_Binary(ScenarioName, &ScenarioCRC)) + { + EditorMapInitialized = true; + return EDITOR_COMMMAND_SUCCESS; + } + else + { + return EDITOR_COMMMAND_FAILURE; + } +} + + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name(char* cncdata_directory, char* scenario_name) +{ + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Clear_Map +* Deletes the data for the currently loaded map. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map() +{ + if (EditorMapInitialized) + { + Map.Init_Clear(); + + int count = sizeof(MixFileNames) / sizeof(MixFileNames[0]); + for (int i = count - 1; i >= 0; --i) + { + MixFileClass::Free(MixFileNames[i]); + } + + EditorMapInitialized = false; + TheaterData = nullptr; + TheaterIcons = nullptr; + + return EDITOR_COMMMAND_SUCCESS; + } + else + { + return EDITOR_COMMMAND_FAILURE; + } + +} + +/************************************************************************************************** +* CNC_Editor_Get_Map_Stats +* Gets the stats for the currently loaded map +* +* map_width: out parameter storing the width of the map +* map_height: out parameter storing the height of the map +* theater: out paramter storing the theater of the map +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater) +{ + if (EditorMapInitialized) + { + map_width = Map.MapCellWidth + 1; + map_height = Map.MapCellHeight + 1; + theater = Map.Theater; + return EDITOR_COMMMAND_SUCCESS; + } + + map_width = -1; + map_height = -1; + theater = -1; + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data_By_Index +* Get the data from the given cell. +* +* cell_index: The index of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + CellClass * cellptr = &Map[cell_index]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + + char template_name[10]; + if (cellptr->Get_Template_Info(template_name, icon, image_data)) + { + snprintf(cell_name, cell_name_size, "%s-%04d", template_name, icon); + + template_type = cellptr->TType; + template_icon_index = icon; + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data +* Get the data from the given cell. +* +* x,y: The corrdinates of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + if (!EditorMapInitialized) + { + return EDITOR_COMMMAND_FAILURE; + } + + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + + return CNC_Editor_Get_Cell_Data_By_Index((int)cell, cell_name, cell_name_size, template_type, template_icon_index); +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Texture_Buffer +* +* x,y: +* out_width, out_height: dimensions of the outputed texture array +* out_texture_array: output array of unsigned chars storing the color data for the requested object, +* every 3 chars is a set of RGB values +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array) +{ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + char cell_name[_MAX_PATH]; + + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) + { + GraphicBufferClass temp_gbuffer(24, 24); + GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, 24, 24); + + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = 24; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = 24; + + temp_viewport.Draw_Stamp(image_data, icon, 0, 0, NULL, WINDOW_CUSTOM); + + out_width = temp_viewport.Get_Width(); + out_height = temp_viewport.Get_Height(); + + const int COLOR_SIZE = 3; + + SAFEARRAYBOUND Bound; + Bound.lLbound = 0; + Bound.cElements = out_width * out_height * COLOR_SIZE; + + out_texture_array = SafeArrayCreate(VT_UI1, 1, &Bound); + + unsigned char* out_buffer; + + HRESULT hr = SafeArrayAccessData(out_texture_array, (void **)&out_buffer); + if (SUCCEEDED(hr)) + { + GraphicBufferClass* Graphic_Buffer = temp_viewport.Get_Graphic_Buffer(); + + int VP_Scan_Line = temp_viewport.Get_Width() + temp_viewport.Get_XAdd(); + + char * start_ptr; + start_ptr = (char *)Graphic_Buffer->Get_Buffer(); + start_ptr += ((temp_viewport.Get_YPos() * VP_Scan_Line) + temp_viewport.Get_XPos()); + + for (int y = 0; y < out_height; ++y) + { + unsigned char* scanline_ptr = (unsigned char*)start_ptr + y * VP_Scan_Line; + unsigned char* out_buffer_y_ptr = out_buffer + (y * out_width * COLOR_SIZE); + for (int x = 0; x < out_width; ++x) + { + unsigned char* pallete_index_ptr = scanline_ptr + x; + unsigned char* out_buffer_ptr = out_buffer_y_ptr + (x * COLOR_SIZE); + + int red_palette_index = (*pallete_index_ptr) * COLOR_SIZE; + out_buffer_ptr[0] = GamePalette[red_palette_index] << 2; + out_buffer_ptr[1] = GamePalette[red_palette_index + 1] << 2; + out_buffer_ptr[2] = GamePalette[red_palette_index + 2] << 2; + } + } + + SafeArrayUnaccessData(out_texture_array); + + return EDITOR_COMMMAND_SUCCESS; + } + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Template_Data +* Get the data from the given tile template type. +* +* template_type_index: The index of the template type to use. should come from the Get_Cell_Data function. +* template_points: Out buffer to be filled with the list of positions of the tiles as offsets from the origin of the template. +* This data is store is an X, Y, X, Y, X, Y format. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points) +{ + if (template_type_index >= TEMPLATE_COUNT || template_type_index == TEMPLATE_NONE) + { + return EDITOR_COMMMAND_FAILURE; + } + + const TemplateTypeClass& template_type = TemplateTypeClass::As_Reference((TemplateType)template_type_index); + if (template_type.Get_Image_Data() == nullptr) + { + return EDITOR_COMMMAND_FAILURE; + } + + short const * occupy_list = template_type.Occupy_List(); + + short const * counter = occupy_list; + while (counter && *counter != REFRESH_EOL) + { + counter++; + } + + int occupy_list_size = counter - occupy_list; + + + SAFEARRAYBOUND bounds; + bounds.lLbound = 0; + bounds.cElements = occupy_list_size * 2; + template_points = SafeArrayCreate(VT_I4, 1, &bounds); + + int *pData; + HRESULT hr = SafeArrayAccessData(template_points, (void **)&pData); + if (SUCCEEDED(hr)) + { + for (int i = 0; i < occupy_list_size; i++) + { + CELL cell = occupy_list[i]; + + int x = Cell_X(cell); + int y = Cell_Y(cell); + + pData[i * 2] = x; + pData[i * 2 + 1] = y; + } + + SafeArrayUnaccessData(template_points); + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + + +/************************************************************************************************** +* CNC_Editor_Get_Scenario_Names +* +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory, int CD) +{ + unsigned char read_buffer[SHAPE_BUFFER_SIZE]; + Set_Shape_Buffer(read_buffer, SHAPE_BUFFER_SIZE); + + char team_ids[] = + { + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + }; + /* + { + 'g', + 'b', + 'm', + 'j' + }; + */ + const int team_count = sizeof(team_ids) / sizeof(char); + + char direction_ids[] = + { + 'e', + 'w' + }; + const int direction_count = sizeof(direction_ids) / sizeof(char); + + char variant_ids[] = + { + 'a', + 'b', + 'c', + 'd' + }; + + const int variant_count = sizeof(variant_ids) / sizeof(char); + + const int min_scenario_index = 1; + const int max_scenario_index = 99; + + char scenario_name[_MAX_FNAME + _MAX_EXT]; + char file_name[_MAX_FNAME + _MAX_EXT]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD); + CNC_Editor_Load_Mix_Files(); + + char output_file_name[256]; + snprintf(output_file_name, 256, "d:\\TD_Disk%d_Names.txt", CD); + + FILE * names_file = fopen(output_file_name, "w+"); + + for (int team_index = 0; team_index < team_count; team_index++) + { + for (int scenario_index = min_scenario_index; scenario_index <= max_scenario_index; ++scenario_index) + { + for (int direction_index = 0; direction_index < direction_count; direction_index++) + { + for (int variant_index = 0; variant_index < variant_count; variant_index++) + { + sprintf(scenario_name, "sc%c%.2d%c%c", + team_ids[team_index], + scenario_index, + direction_ids[direction_index], + variant_ids[variant_index]); + + sprintf(file_name, "%s.INI", scenario_name); + CCFileClass file(file_name); + if (file.Is_Available()) + { + if (Read_Movies_From_Scenario_Ini(scenario_name, false)) + { + //fprintf(names_file, "%s - \t$Intro:%s \t\t$Brief:%s \t\t$Win:%s \t\t$Lose:%s \t\t$Action:%s \t\t$Theme:%s", scenario_name, IntroMovie, BriefMovie, WinMovie, LoseMovie, ActionMovie, MovieThemeName); + fprintf(names_file, "------------------%s-------------- %s \n", scenario_name, MovieThemeName); + + if (stricmp("x", IntroMovie) != 0) + { + fprintf(names_file, "%s\n", IntroMovie); + } + if (stricmp("x", BriefMovie) != 0) + { + fprintf(names_file, "%s\n", BriefMovie); + } + if (stricmp("x", ActionMovie) != 0) + { + fprintf(names_file, "%s\n", ActionMovie); + } + if (stricmp("x", WinMovie) != 0) + { + fprintf(names_file, "%s\n", WinMovie); + } + } + + fprintf(names_file, "\n"); + } + + + + } + } + } + } + + fclose(names_file); + + return EDITOR_COMMMAND_SUCCESS; +} diff --git a/TIBERIANDAWN/DOOR.CPP b/TIBERIANDAWN/DOOR.CPP new file mode 100644 index 000000000..2c795fb5c --- /dev/null +++ b/TIBERIANDAWN/DOOR.CPP @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\door.cpv 1.4 16 Oct 1995 16:49:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 14, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DoorClass::AI -- Handles the door processing logic. * + * DoorClass::Close_Door -- Try to close the unit's door. * + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * DoorClass::Open_Door -- Opens the door for this unit. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * * + * This constructor sets the door to an initial closed state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +DoorClass::DoorClass(void) +{ + State = IS_CLOSED; + IsToRedraw = false; + Stages = 0; +} + + +/*********************************************************************************************** + * DoorClass::AI -- Handles the door processing logic. * + * * + * This routine should be called every game frame. It handles the door closing and opening * + * logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/13/1995 JLB : Created. * + *=============================================================================================*/ +void DoorClass::AI(void) +{ + if (Control.Graphic_Logic()) { + if (Control.Fetch_Stage() >= Stages) { + Control.Set_Rate(0); + switch (State) { + case IS_OPENING: + State = IS_OPEN; + break; + + case IS_CLOSING: + State = IS_CLOSED; + break; + } + } + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * DoorClass::Open_Door -- Opens the door for this unit. * + * * + * This routine will perform the door open operation for this unit. It will control vehicle * + * rotation if necessary. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Was action initiated to open the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Open_Door(int rate, int stages) +{ + switch (State) { + case IS_CLOSED: + case IS_CLOSING: + State = IS_OPENING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Close_Door -- Try to close the unit's door. * + * * + * This routine will attempt to close the unit's door. If the door is already closed or * + * in the process of closing, then no action is performed. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Action was initiated to close the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Close_Door(int rate, int stages) +{ + switch (State) { + case IS_OPEN: + case IS_OPENING: + State = IS_CLOSING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * * + * Use this routine to fetch the current door animation frame number. Frame zero is the * + * closed frame and frame 'N' is the open frame. If the door is in the process of opening * + * or closing, the appropriate frame number is used. 'N' is defined as the number of * + * stages in the animation minus 1 (e.g., a four frame animation will return a door stage * + * number between 0 and 3, inclusive). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the door animation frame number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int DoorClass::Door_Stage(void) const +{ + switch (State) { + case IS_CLOSING: + return((Stages-1) - Control.Fetch_Stage()); + + case IS_CLOSED: + return(0); + + case IS_OPENING: + return(Control.Fetch_Stage()); + + case IS_OPEN: + return(Stages-1); + } + return(0); +} diff --git a/TIBERIANDAWN/DOOR.H b/TIBERIANDAWN/DOOR.H new file mode 100644 index 000000000..c743e0df9 --- /dev/null +++ b/TIBERIANDAWN/DOOR.H @@ -0,0 +1,91 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\door.h_v 1.8 16 Oct 1995 16:47:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DOOR_H +#define DOOR_H + +class DoorClass +{ + private: + + /* + ** This is the animation control handler. + */ + StageClass Control; + + /* + ** This is the recorded number of stages of the current + ** door animation process. + */ + unsigned char Stages; + + /* + ** This is the door state. + */ + enum { + IS_CLOSED, // Door is closed. + IS_OPENING, // Door is in the process of opening. + IS_OPEN, // Door is fully open. + IS_CLOSING // Door is in the process of closing. + } State; + + /* + ** If the animation for this door indicates that the object it is + ** attached to should be redrawn, then this flag will be true. + */ + unsigned IsToRedraw:1; + + public: + DoorClass(void); + + bool Time_To_Redraw(void) {return(IsToRedraw);}; + void Clear_Redraw_Flag(void) {IsToRedraw = false;}; + void AI(void); + int Door_Stage(void) const; + bool Is_Door_Opening(void) {return(State == IS_OPENING);}; + bool Is_Door_Closing(void) {return(State == IS_CLOSING);}; + bool Open_Door(int rate, int stages); + bool Close_Door(int rate, int stages); + bool Is_Door_Open(void) {return(State == IS_OPEN);}; + bool Is_Door_Closed(void) {return(State == IS_CLOSED);}; + bool Is_Ready_To_Open(void); + + /* + ** File I/O. + */ + void Code_Pointers(void) { return; } + void Decode_Pointers(void) { return; } +}; + +#endif diff --git a/TIBERIANDAWN/DPMI.CPP b/TIBERIANDAWN/DPMI.CPP new file mode 100644 index 000000000..f24b1524c --- /dev/null +++ b/TIBERIANDAWN/DPMI.CPP @@ -0,0 +1,172 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\dpmi.cpv 2.17 16 Oct 1995 16:49:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//PG_TO_FIX +#if (0) + +#ifdef __FLAT__ +#pragma inline +#endif + +#include "function.h" +#include "dpmi.h" + +#ifndef __FLAT__ + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + if (!size) return; + + unsigned short ssel = src.Selector; + unsigned short dsel = dest.Selector; + + asm { + push es + push ds + + mov si,soffset + mov di,doffset + mov cx,size + mov ax,ssel + mov dx,dsel + mov ds,ax + mov es,dx + } +again: + asm { + mov al,ds:[si] + mov ah,es:[di] + mov ds:[si],ah + mov es:[di],al + inc di + inc si + dec cx + jnz again + + pop ds + pop es + } +} +#endif + + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_swap(char *src, char *dest, int size); + + #pragma aux dss_swap = \ + "again: mov al,[esi]" \ + "mov ah,[edi]" \ + "mov [esi],ah" \ + "stosb" \ + "inc esi" \ + "loop again" \ + parm [esi] [edi] [ecx] \ + modify [ax]; + + if (!size) return; + dss_swap((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} + +#ifdef OBSOLETE +void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_copy(char *src, char *dest, int size); + #pragma aux dss_copy = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copskip1" \ + "rep movsd" \ +"copskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copskip2" \ + "rep movsb" \ +"copskip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + extern void dss_copy_to(void *src, (void *)dest, int size); + + #pragma aux dss_copy_to = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz cop2skip1" \ + "rep movsd" \ +"cop2skip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz cop2skip2" \ + "rep movsb" \ +"cop2skip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_to(src, (void *)(Selector + dest), size); + +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + extern void dss_copy_from(void *dest, (void *)source, int size); + + #pragma aux dss_copy_from = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copfskip1" \ + "rep movsd" \ +"copfskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copfskip2" \ + "rep movsb" \ +"copfskip2:" \ + parm [edi esi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_from(dest, (void *)(Selector + source), size); +} +#endif + + +#endif //PG_TO_FIX \ No newline at end of file diff --git a/TIBERIANDAWN/DPMI.H b/TIBERIANDAWN/DPMI.H new file mode 100644 index 000000000..bcf710b5c --- /dev/null +++ b/TIBERIANDAWN/DPMI.H @@ -0,0 +1,172 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\dpmi.h_v 2.17 16 Oct 1995 16:44:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DPMI_H +#define DPMI_H +#include +#include +#include +#include + + +extern void output(short port, short data); + + +class DOSSegmentClass { + /* + ** This is the selector/segment value. In real mode it is the segment, in protected + ** mode it is the selector (also 16 bits). This value is moved into DS or ES when + ** accessing memory. + ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h) + */ + unsigned int Selector; + + /* + ** These are C equivalents for pushing and popping the DS segment register. By using + ** these, it is possible to create very small code that uses a segment and + ** offset without damaging the DS register. These are especially useful in protected + ** mode, but they are legal in real mode as well. + */ + void Push_DS(void) {/*__emit__(0x1E);*/}; + void Pop_DS(void) {/*__emit__(0x1F);*/}; + + public: + DOSSegmentClass(void); + ~DOSSegmentClass(void); + DOSSegmentClass(unsigned short segment, long size=(1024L*64L)); + + unsigned int Get_Selector(void); + + /* + ** This routine is used to assign where the descriptor actually points to in + ** low DOS memory. In real mode, this is a simple segment assignment and the size + ** is always 64K regardless of what is specified. In protected mode, the segment + ** is used to update the selector and the size can be any length. + ** In Watcom flat mode, it sets Selector == segment<<4 + */ + void Assign(unsigned short segment, long size=(1024L*64L)); + + /* + ** These routines will move the data to/from regular memory and the segment/descriptor + ** memory. + */ + void Copy_To(void *source, int dest, int size); + void Copy_From(void *dest, int source, int size); + void Copy_Word_To(short data, int dest); + void Copy_Byte_To(char data, int dest); + void Copy_DWord_To(long data, int dest); + short Copy_Word_From(int source); + char Copy_Byte_From(int source); + long Copy_DWord_From(int source); + + /* + ** These routines move data around between sections of segmented (descriptor) memory. + ** Typically, this is used when accessing DOS memory in protected mode or when dealing + ** with hard memory areas such as the screen. + */ + static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); + static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); +}; + + +inline DOSSegmentClass::DOSSegmentClass(void) +{ + Selector = 0xB0000; +} + +inline DOSSegmentClass::~DOSSegmentClass(void) +{ +} + +inline void DOSSegmentClass::Copy_Word_To(short data, int dest) +{ + *(short *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_Byte_To(char data, int dest) +{ + *(char *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_DWord_To(long data, int dest) +{ + *(long *)(Selector+dest) = data; +} + +inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long) +{ + Assign(segment); +} + +inline void DOSSegmentClass::Assign(unsigned short segment, long) +{ + Selector = (long)(segment)<<4L; +} + +inline void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + memmove((void*)(Selector+dest), source, size); +} + +inline void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + memmove(dest, (void*)(Selector+source), size); +} + +inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) { + memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), size); +} + +inline short DOSSegmentClass::Copy_Word_From(int source) +{ + return *(short*)(Selector+source); +} + +inline char DOSSegmentClass::Copy_Byte_From(int source) +{ + return *(char*)(Selector+source); +} + +inline long DOSSegmentClass::Copy_DWord_From(int source) +{ + return *(long*)(Selector+source); +} + +inline unsigned int DOSSegmentClass::Get_Selector(void) +{ + return Selector; +} +#endif + + diff --git a/TIBERIANDAWN/DRIVE.CPP b/TIBERIANDAWN/DRIVE.CPP new file mode 100644 index 000000000..f2ad696fb --- /dev/null +++ b/TIBERIANDAWN/DRIVE.CPP @@ -0,0 +1,2284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\drive.cpv 2.17 16 Oct 1995 16:51:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : July 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DriveClass::AI -- Processes unit movement and rotation. * + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * DriveClass::DriveClass -- Constructor for drive class object. * + * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. * + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * DriveClass::While_Moving -- Processes unit movement. * + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +DriveClass::DriveClass(void) : Class(0), SimLeptonX(0), SimLeptonY(0) {}; // Added SimLeptonX and Y. ST - 4/30/2019 8:06AM + + +/*********************************************************************************************** + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * * + * This routine will set the vehicle to rotate to the direction specified. For tracked * + * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series * + * of short drives (three point turn) to face the desired direction. * + * * + * INPUT: dir -- The direction that this vehicle should face. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Do_Turn(DirType dir) +{ + if (dir != PrimaryFacing) { + + /* + ** Special rotation track is needed for units that + ** cannot rotate in place. + */ + if (Special.IsThreePoint && TrackNumber == -1 && Class->Speed == SPEED_WHEEL) { + int facediff; // Signed difference between current and desired facing. + FacingType face; // Current facing (ordinal value). + + facediff = PrimaryFacing.Difference(dir) >> 5; + facediff = Bound(facediff, -2, 2); + if (facediff) { + face = Dir_Facing(PrimaryFacing); + + IsOnShortTrack = true; + Force_Track(face*FACING_COUNT + (face + facediff), Coord); + + Path[0] = FACING_NONE; + Set_Speed(0xFF); // Full speed. + } + } else { + PrimaryFacing.Set_Desired(dir); + //if (Special.IsJurassic && AreThingiesEnabled && What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir); + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir); + } + } +} + + +/*********************************************************************************************** + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * * + * This override (nuclear bomb) style routine is to be used when a unit needs to start * + * on a movement track but is outside the normal movement system. This occurs when a * + * harvester starts driving off of a refinery. * + * * + * INPUT: track -- The track number to start on. * + * * + * coord -- The coordinate that the unit will end up at when the movement track * + * is completed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Force_Track(int track, COORDINATE coord) +{ + TrackNumber = track; + TrackIndex = 0; + Start_Driver(coord); +} + + +/*********************************************************************************************** + * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * * + * Use this routine to determine what the Tiberium load is (as a fixed point percentage). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current "fullness" rating for the object. This will be 0x0000 for * + * empty and 0x0100 for full. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +int DriveClass::Tiberium_Load(void) const +{ + if (*this == UNIT_HARVESTER) { + return(Cardinal_To_Fixed(UnitTypeClass::STEP_COUNT, Tiberium)); + } + return(0x0000); +} + + +/*********************************************************************************************** + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * * + * This routine will check to see if the target is infantry and it can be overrun. It will * + * try to overrun the infantry rather than attack it. This only applies to computer * + * controlled vehicles. If it isn't the infantry overrun case, then it falls into the * + * base class for normal (complex) approach algorithm. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. * + *=============================================================================================*/ +void DriveClass::Approach_Target(void) +{ + /* + ** Only if there is a legal target should the approach check occur. + */ + if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) { + + /* + ** Special case: + ** If this is for a unit that can crush infantry, and the target is + ** infantry, AND the infantry is pretty darn close, then just try + ** to drive over the infantry instead of firing on it. + */ + TechnoClass * target = As_Techno(TarCom); + if (Class->Primary != WEAPON_FLAME_TONGUE && Class->IsCrusher && Distance(TarCom) < 0x0180 && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) { + Assign_Destination(TarCom); + return; + } + } + + /* + ** In the other cases, uses the more complex "get to just within weapon range" + ** algorithm. + */ + FootClass::Approach_Target(); +} + + +/*********************************************************************************************** + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * * + * This routine is called when a vehicle enters a square or when it is about to enter a * + * square (controlled by parameter). When a vehicle that can crush infantry enters a * + * cell that contains infantry, then the infantry will be destroyed (regardless of * + * affiliation). When a vehicle threatens to overrun a square, all occupying infantry * + * will attempt to get out of the way. * + * * + * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. * + * * + * threaten -- Don't kill, but just threaten to enter the cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Overrun_Square(CELL cell, bool threaten) +{ + CellClass * cellptr = &Map[cell]; + + if (Class->IsCrusher) { + if (threaten) { + + /* + ** If the cell contains infantry, then they will panic when a vehicle tries + ** drive over them. Have the infantry run away instead. + */ + if (cellptr->Flag.Composite & 0x1F) { + + /* + ** Scattering is controlled by the game difficulty level. + */ + if (((GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_HARD) || Special.IsScatter || Scenario > 8) && + !(GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY)) { + cellptr->Incoming(0, true); + } + } + } else { + ObjectClass * object = cellptr->Cell_Occupier(); + int crushed = false; + while (object) { + if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < 0x80) { + ObjectClass * next = object->Next; + crushed = true; + + /* + ** Record credit for the kill(s) + */ + Sound_Effect(VOC_SQUISH2, Coord); + object->Record_The_Kill(this); + object->Mark(MARK_UP); + object->Limbo(); + delete object; + new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord)); + + object = next; + } else { + object = object->Next; + } + } + if (crushed) Do_Uncloak(); + } + } +} + + +/*********************************************************************************************** + * DriveClass::DriveClass -- Constructor for drive class object. * + * * + * This will initialize the drive class to its default state. It is called as a result * + * of creating a unit. * + * * + * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1994 JLB : Created. * + *=============================================================================================*/ +DriveClass::DriveClass(UnitType classid, HousesType house) : + Class(&UnitTypeClass::As_Reference(classid)), + FootClass(house) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + IsHarvesting = false; + IsTurretLockedDown = false; + IsOnShortTrack = false; + IsReturning = false; + TrackNumber = -1; + TrackIndex = 0; + SpeedAccum = 0; + Tiberium = 0; + Strength = Class->MaxStrength; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * * + * This debug utility function will display the status of the drive class to the mono * + * screen. It is through this information that bugs can be tracked down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(33, 7); + mono->Printf("%2d:%2d", TrackNumber, TrackIndex); + mono->Text_Print("X", 16 + (IsTurretLockedDown?2:0), 10); +// mono->Text_Print("X", 16 + (IsOnShortTrack?2:0), 11); + mono->Set_Cursor(41, 7);mono->Printf("%d", Fixed_To_Cardinal(100, Tiberium_Load())); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. * + * * + * This routine is used to assign an appropriate movement destination for the unit so that * + * it will leave the map. The scripts are usually the one to call this routine when it * + * is determined that the unit has fulfilled its mission and must "depart". * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Exit_Map(void) +{ + CELL cell; // Map exit cell number. + + if (*this == UNIT_HOVER && !Target_Legal(NavCom)) { + + /* + ** Scan a swath of cells from current position to the edge of the map and if + ** there is any blocking object, just wait so to try again later. + */ + Mark(MARK_UP); + for (int x = Cell_X(Coord_Cell(Center_Coord()))-1; x <= Cell_X(Coord_Cell(Center_Coord()))+1; x++) { + for (int y = Cell_Y(Coord_Cell(Center_Coord()))+1; y < Map.MapCellY+Map.MapCellHeight; y++) { + cell = XY_Cell(x, y); + if (Map[cell].Cell_Techno()) { + Mark(MARK_DOWN); + return; + } + } + } + Mark(MARK_DOWN); + + /* + ** A clear path to the map edge exists. Assign it as the navigation computer + ** destination and let the transport move. + */ + cell = XY_Cell(Cell_X(Coord_Cell(Coord)), Map.MapCellY+Map.MapCellHeight); + IsReturning = true; + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * * + * This routine calculates the new coordinate value needed for the * + * smooth turn logic. The adjustment and flag values must be * + * determined prior to entering this routine. * + * * + * INPUT: adj -- The adjustment coordinate as lifted from the * + * correct smooth turn table. * + * * + * dir -- Pointer to dir for possible modification * + * according to the flag bits. * + * * + * OUTPUT: Returns with the coordinate the unit should positioned to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1994 JLB : Created. * + * 07/13/1994 JLB : Converted to member function. * + *=============================================================================================*/ +COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType *dir) +{ + DirType workdir = *dir; + int x,y; + int temp; + TrackControlType flags = TrackControl[TrackNumber].Flag; + + x = Coord_X(adj); + y = Coord_Y(adj); + + if (flags & F_T) { + temp = x; + x = y; + y = temp; + workdir = (DirType)(DIR_W - workdir); + } + + if (flags & F_X) { + x = -x; + workdir = (DirType)-workdir; + } + + if (flags & F_Y) { + y = -y; + workdir = (DirType)(DIR_S - workdir); + } + + *dir = workdir; + + return(XY_Coord( Coord_X(Head_To_Coord()) + x, Coord_Y(Head_To_Coord()) + y)); +} + + +/*********************************************************************************************** + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * * + * This routine is used to set the unit's navigation computer to the * + * specified target. Once the navigation computer is set, the unit * + * will start planning and moving toward the destination. * + * * + * INPUT: target -- The destination target for the unit to head to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Assign_Destination(TARGET target) +{ + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + +#ifdef NEVER + UnitClass *tunit; // Destination unit pointer. + + /* + ** When in move mode, a map position may really indicate + ** a unit to guard. + */ + if (Is_Target_Cell(target)) { + cell = As_Cell(target); + + tunit = Map[cell].Cell_Unit(); + if (tunit) { + + /* + ** Prevent targeting of itself. + */ + if (tunit != this) { + target = tunit->As_Target(); + } + } else { + + tbuilding = Map[cell].Cell_Building(); + if (tbuilding) { + target = tbuilding->As_Target(); + } + } + } +#endif + + /* + ** For harvesting type vehicles, it might go into a dock and unload procedure + ** when the harvester is full and an empty refinery is selected as a target. + */ + BuildingClass * b = As_Building(target); + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + //if (tunit && In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) { + if (In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** If the player clicked on a friendly repair facility and the repair + ** facility is currently not involved with some other unit (radio or unloading). + */ + if (b && *b == STRUCT_REPAIR) { + if (b->In_Radio_Contact()) { + target = 0; + } else { + + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + + /* + ** Last check to make sure that the loading square is free from permanent + ** occupation (such as a building). + */ + CELL cell = Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1); + if (Ground[Map[cell].Land_Type()].Cost[Class->Speed] ) { + if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) { + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; + return; + } + + /* + ** Failure to establish a docking relationship with the refinery. + ** Bail & await further instructions. + */ + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + + /* + ** Set the unit's navigation computer. + */ + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; // Force recalculation of path. + if (!IsDriving) { + Start_Of_Move(); + } +} + + +/*********************************************************************************************** + * DriveClass::While_Moving -- Processes unit movement. * + * * + * This routine is used to process movement for the units as they move. * + * It is called many times for each cell's worth of movement. This * + * routine only applies after the next cell HeadTo has been determined. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DriveClass::While_Moving(void) +{ + int actual; // Working movement addition value. + + /* + ** Perform quick legality checks. + */ + if (!IsDriving || TrackNumber == -1 || (IsRotating && !Class->IsTurretEquipped)) { + SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold. + return(false); + } + + /* + ** If enough movement has accumulated so that the unit can + ** visibly move on the map, then process accordingly. + ** Slow the unit down if he's carrying a flag. + */ + MPHType maxspeed = MPHType(min((int)(Class->MaxSpeed * House->GroundspeedBias), (int)MPH_LIGHT_SPEED)); + if (((UnitClass *)this)->Flagged != HOUSE_NONE) { + actual = SpeedAccum + Fixed_To_Cardinal(maxspeed /2, Speed); + } else { + actual = SpeedAccum + Fixed_To_Cardinal(maxspeed, Speed); + } + + if (actual > PIXEL_LEPTON_W) { + TurnTrackType const *track; // Track control pointer. + TrackType const *ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + /* + ** Skip ahead the number of track steps required (limited only + ** by track length). Set the unit to the new position and + ** flag the unit accordingly. + */ + Mark(MARK_UP); + while (actual > PIXEL_LEPTON_W) { + COORDINATE offset; + DirType dir; + + actual -= PIXEL_LEPTON_W; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + Coord = Smooth_Turn(offset, &dir); + + PrimaryFacing.Set(dir); + + /* + ** See if "per cell" processing is necessary. + */ + if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) { + Per_Cell_Process(false); + if (!IsActive) { + return(false); + } + } + + /* + ** The unit could "jump tracks". Check to see if the unit should + ** do so. + */ + if (*this != UNIT_GUNBOAT && nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) { + TurnTrackType const *newtrack; // Proposed jump-to track. + int tnum; + + tnum = Dir_Facing(track->Facing)*FACING_COUNT + nextface; + newtrack = &TrackControl[tnum]; + if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) { + COORDINATE c = Head_To_Coord(); + int oldspeed = Speed; + + c = Adjacent_Cell(c, nextface); + + switch(Can_Enter_Cell(Coord_Cell(c), nextface)) { + case MOVE_OK: + IsOnShortTrack = false; // Shouldn't be necessary, but... + TrackNumber = tnum; + track = newtrack; + + // Mono_Printf("**Jumping from track %d to track %d. **\n", tracknum, track->Track);Keyboard::Get(); + + tracknum = track->Track; + TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment. + ptr = RawTracks[tracknum-1].Track; + adj = false; + + Stop_Driver(); + Per_Cell_Process(true); + if (Start_Driver(c)) { + Set_Speed(oldspeed); + memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1); + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } else { + Path[0] = FACING_NONE; + TrackNumber = -1; + actual = 0; + } + break; + + case MOVE_CLOAK: + Map[Coord_Cell(c)].Shimmer(); + break; + + case MOVE_TEMP: + if (*this == UNIT_HARVESTER || !House->IsHuman) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[Coord_Cell(c)].Incoming(0, true); + Special.IsScatter = old; + } + break; + } + } + } + TrackIndex++; + + } else { + actual = 0; + Coord = Head_To_Coord(); + Stop_Driver(); + TrackNumber = -1; + TrackIndex = NULL; + + /* + ** Perform "per cell" activities. + */ + Per_Cell_Process(true); + + break; + } + } + if (IsActive) { + Mark(MARK_DOWN); + } + } + + + + /* + ** NEW 4/30/2019 7:59AM + ** + ** When we don't have enough speed accumulated to move another pixel, it would be good to know at a sub-pixel (lepton) level + ** how far we would move if we could. It didn't matter in the original when it was 320x200 pixels, but on a 3840x2160 + ** screen, what was half a pixel could now be several pixels. + ** + ** ST + ** + */ + if (actual && actual <= PIXEL_LEPTON_W) { + TurnTrackType const *track; // Track control pointer. + TrackType const *ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + COORDINATE simulated_pos = Coord; + + COORDINATE offset; + DirType dir; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + simulated_pos = Smooth_Turn(offset, &dir); + } + + int x_diff = Coord_X(simulated_pos) - Coord_X(Coord); + int y_diff = Coord_Y(simulated_pos) - Coord_Y(Coord); + + SimLeptonX = (x_diff * actual) / PIXEL_LEPTON_W; + SimLeptonY = (y_diff * actual) / PIXEL_LEPTON_W; + } else { + SimLeptonX = 0; + SimLeptonY = 0; + } + + + + /* + ** Replace any remainder back into the unit's movement + ** accumulator to be processed next pass. + */ + SpeedAccum = actual; + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * * + * This routine is called when a unit has mostly or completely * + * entered a cell. The unit might be in the middle of a movement track * + * when this routine is called. It's primary purpose is to perform * + * sighting and other "per cell" activities. * + * * + * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" * + * to the center, then this parameter will be false. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1993 JLB : Created. * + * 03/30/1994 JLB : Revamped for track system. * + * 04/15/1994 JLB : Converted to member function. * + * 06/18/1994 JLB : Converted to virtual function. * + * 06/18/1994 JLB : Distinguishes between center and near-center conditions. * + *=============================================================================================*/ +void DriveClass::Per_Cell_Process(bool center) +{ + CELL cell = Coord_Cell(Coord); + + /* + ** Check to see if it has reached its destination. If so, then clear the NavCom + ** regardless of the remaining path list. + */ + if (center && As_Cell(NavCom) == cell) { + IsTurretLockedDown = false; + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + +#ifdef NEVER + /* + ** A "lemon" vehicle will have a tendency to break down as + ** it moves about the terrain. + */ + if (Is_A_Lemon) { + if (Random_Pick(1, 4) == 1) { + Take_Damage(1); + } + } +#endif + + Lay_Track(); + + FootClass::Per_Cell_Process(center); +} + + +/*********************************************************************************************** + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * * + * This will try to start a unit advancing toward the cell it is * + * facing. It will check for and handle legality and reserving of the * + * necessary cell. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again because * + * initial start operation is temporarily delayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. * + * 03/16/1994 JLB : Revamped for track logic. * + * 04/15/1994 JLB : Converted to member function. * + * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. * + * 07/13/1995 JLB : Handles bumping into cloaked objects. * + *=============================================================================================*/ +bool DriveClass::Start_Of_Move(void) +{ + FacingType facing; // Direction movement will commence. + DirType dir; // Desired actual facing toward destination. + int facediff; // Difference between current and desired facing. + int speed; // Speed of unit. + CELL destcell; // Cell of destination. + LandType ground; // Ground unit is entering. + COORDINATE dest; // Destination coordinate. + + facing = Path[0]; + + if (!Target_Legal(NavCom) && facing == FACING_NONE) { + IsTurretLockedDown = false; + Stop_Driver(); + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + return(false); // Why is it calling this routine!?! + } + +#ifdef NEVER + /* + ** Movement start logic can't begin until a unit that requires + ** a locked down turret gets to a locked down state (i.e., the + ** turret rotation stops. + */ + if (ClassF & CLASSF_LOCKTURRET) { + Set_Secondary_Facing(facing<<5); + if (Is_Rotating) { + return(true); + } + } +#endif + + /* + ** Reduce the path length if the target is a unit and the + ** range to the unit is less than the precalculated path steps. + */ + if (facing != FACING_NONE) { + int dist; + + if (Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) { + dist = Lepton_To_Cell(Distance(NavCom)); + +// if (dist > CELL_LEPTON_W || +// !As_Techno(NavCom)->Techno_Type_Class()->IsCrushable || +// !Class->IsCrusher) { + + if (dist < CONQUER_PATH_MAX) { + Path[dist] = FACING_NONE; + facing = Path[0]; // Maybe needed. + } +// } + } + } + + /* + ** If the path is invalid at this point, then generate one. If + ** generating a new path fails, then abort NavCom. + */ + if (facing == FACING_NONE) { + + /* + ** If after a path search, there is still no valid path, then set the + ** NavCom to null and let the script take care of assigning a new + ** navigation target. + */ + if (!PathDelay.Expired()) { + return(false); + } + if (!Basic_Path()) { + if (Distance(NavCom) < 0x0280 && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current()); + if (Map.In_Radar(cell)) { + if (Can_Enter_Cell(cell) == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + bool old = Special.IsScatter; + Special.IsScatter = true; + cellptr->Incoming(0, true); + Special.IsScatter = old; + } + } + } + + if (TryTryAgain) { + TryTryAgain--; + } else { + Assign_Destination(TARGET_NONE); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + Stop_Driver(); + TrackNumber = -1; + IsTurretLockedDown = false; + return(false); + } + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]); + if (Map.In_Radar(cell)) { + if (Can_Enter_Cell(cell) == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + bool old = Special.IsScatter; + Special.IsScatter = true; + cellptr->Incoming(0, true); + Special.IsScatter = old; + } + } + } + + TryTryAgain = PATH_RETRY; + facing = Path[0]; + } + + if (Class->IsLockTurret || !Class->IsTurretEquipped) { + IsTurretLockedDown = true; + } + +#ifdef NEVER + /* + ** If the turret needs to match the body's facing before + ** movement can occur, then start it's rotation and + ** don't start a movement track until it is aligned. + */ + if (!Ok_To_Move(BodyFacing)) { + return(true); + } +#endif + + /* + ** Determine the coordinate of the next cell to move into. + */ + dest = Adjacent_Cell(Coord, facing); + dir = Facing_Dir(facing); + + /* + ** Set the facing correctly if it isn't already correct. This + ** means starting a rotation track if necessary. + */ + facediff = PrimaryFacing.Difference(dir); + if (facediff) { + + /* + ** Request a change of facing. + */ + Do_Turn(dir); + return(true); + + } else { + + /* NOTE: Beyond this point, actual track assignment can begin. + ** + ** If the cell to move into is impassable (probably for some unexpected + ** reason), then abort the path list and set the speed to zero. The + ** next time this routine is called, a new path will be generated. + */ + destcell = Coord_Cell(dest); + Mark(MARK_UP); + MoveType cando = Can_Enter_Cell(destcell, facing); + Mark(MARK_DOWN); + + if (cando != MOVE_OK) { + + if (Mission == MISSION_MOVE && House->IsHuman && Distance(NavCom) < 0x0200) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[destcell].Incoming(0, true); + Special.IsScatter = old; + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Stop_Driver(); + if (cando != MOVE_MOVING_BLOCK) { + Path[0] = FACING_NONE; // Path is blocked! + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (cando == MOVE_DESTROYABLE) { + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + } + IsNewNavCom = false; + TrackNumber = -1; + return(true); + } + + /* + ** Determine the speed that the unit can travel to the desired square. + */ + ground = Map[destcell].Land_Type(); + speed = Ground[ground].Cost[Class->Speed]; + if (!speed) speed = 128; + +#ifdef NEVER + /* + ** Set the jiggle flag if the terrain would cause the unit + ** to jiggle when travelled over. + */ + BaseF &= ~BASEF_JIGGLE; + if (Ground[ground].Jiggle) { + BaseF |= BASEF_JIGGLE; + } +#endif + + /* + ** A damaged unit has a reduced speed. + */ + if ((Class->MaxStrength>>1) > Strength) { + speed -= (speed>>2); // Three quarters speed. + } + if ((speed != Speed)/* || !SpeedAdd*/) { + Set_Speed(speed); // Full speed. + } + + /* + ** Adjust speed depending on distance to ultimate movement target. The + ** further away the target is, the faster the vehicle will travel. + */ + int dist = Distance(NavCom); + if (dist < 0x0200) { + speed = Fixed_To_Cardinal(speed, 0x00A0); + } else { + if (dist < 0x0700) { + speed = Fixed_To_Cardinal(speed, 0x00D0); + } + } + + /* + ** Reserve the destination cell so that it won't become + ** occupied AS this unit is moving into it. + */ + if (cando != MOVE_OK) { + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + } else { + + Overrun_Square(Coord_Cell(dest), true); + + /* + ** Determine which track to use (based on recorded path). + */ + FacingType nextface = Path[1]; + if (nextface == FACING_NONE) nextface = facing; + + IsOnShortTrack = false; + TrackNumber = facing * FACING_COUNT + nextface; + if (TrackControl[TrackNumber].Track == 0) { + Path[0] = FACING_NONE; + TrackNumber = -1; + return(true); + } else { + if (TrackControl[TrackNumber].Flag & F_D) { + /* + ** If the middle cell of a two cell track contains a crate, + ** the check for goodies before movement starts. + */ + if (!Map[destcell].Goodie_Check(this)) { + cando = MOVE_NO; + } else { + + dest = Adjacent_Cell(dest, nextface); + destcell = Coord_Cell(dest); + cando = Can_Enter_Cell(destcell); + } + + if (cando != MOVE_OK) { + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[destcell].Incoming(0, true); + Special.IsScatter = old; + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + if (cando == MOVE_DESTROYABLE) { + + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + IsNewNavCom = false; + TrackIndex = 0; + return(true); + } + } else { + memcpy(&Path[0], &Path[2], CONQUER_PATH_MAX-2); + Path[CONQUER_PATH_MAX-2] = FACING_NONE; + IsPlanningToLook = true; + } + } else { + memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1); + } + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } + } + + IsNewNavCom = false; + TrackIndex = 0; + if (!Start_Driver(dest)) { + TrackNumber = -1; + Path[0] = FACING_NONE; + Set_Speed(0); + } + } + return(false); +} + + +/*********************************************************************************************** + * DriveClass::AI -- Processes unit movement and rotation. * + * * + * This routine is used to process unit movement and rotation. It * + * functions autonomously from the script system. Thus, once a unit * + * is give rotation command or movement path, it will follow this * + * until specifically instructed to stop. The advantage of this * + * method is that it allows smooth movement of units, faster game * + * execution, and reduced script complexity (since actual movement * + * dynamics need not be controlled directly by the scripts). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine relies on the process control bits for the * + * specified unit (for speed reasons). Thus, only setting * + * movement, rotation, or path list will the unit perform * + * any physics. * + * * + * HISTORY: * + * 09/26/1993 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::AI(void) +{ + FootClass::AI(); + + /* + ** If the unit is following a track, then continue + ** to do so -- mindlessly. + */ + if (TrackNumber != -1) { + + /* + ** Perform the movement accumulation. + */ + While_Moving(); + if (!IsActive) return; + if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE)) { + Start_Of_Move(); + While_Moving(); + if (!IsActive) return; + } + + } else { + + /* + ** For tracked units that are rotating in place, perform the rotation now. + */ + if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust((int)(Class->ROT * House->GroundspeedBias))) { + Mark(MARK_CHANGE); + } + if (!IsRotating) { + Per_Cell_Process(true); + if (!IsActive) return; + } + + } else { + + /* + ** The unit has no track to follow, but if there + ** is a navigation target or a remaining path, + ** then start on a new track. + */ + if (Mission != MISSION_GUARD || NavCom != TARGET_NONE) { + if (Target_Legal(NavCom) || Path[0] != FACING_NONE) { + Start_Of_Move(); + While_Moving(); + if (!IsActive) return; + } else { + Stop_Driver(); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * * + * This routine modifies the path of the specified unit so that it * + * will not start out with a rotation. This is necessary for those * + * vehicles that have difficulty with rotating in place. Typically, * + * this includes wheeled vehicles. * + * * + * INPUT: unit -- Pointer to the unit to adjust. * + * * + * path -- Pointer to path structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only units that require a fixup get modified. The * + * modification only occurs, if there is a legal path to * + * do so. * + * * + * HISTORY: * + * 04/03/1994 JLB : Created. * + * 04/06/1994 JLB : Uses path structure. * + * 04/10/1994 JLB : Diagonal smooth turn added. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Fixup_Path(PathType *path) +{ + FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements. + int facediff; // The facing difference value (0..4 | 0..-4). + static FacingType _path[4][6] = { + {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + static FacingType _dpath[4][6] = { + {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}, + {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + + int index; + int counter; // Path addition + FacingType *ptr; // Path list pointer. + FacingType *ptr2; // Copy of new path list pointer. + FacingType nextpath; // Next path value. + CELL cell; // Working cell value. + bool ok; + + /* + ** Verify that the unit is valid and there is a path problem to resolve. + */ + if (!path || path->Command[0] == FACING_NONE) { + return; + } + + /* + ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns. + */ + if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) { + return; + } + + /* + ** If the original path starts in the same direction as the unit, then + ** there is no problem to resolve -- abort. + */ + facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5; + + if (!facediff) return; + + if (Dir_Facing(PrimaryFacing) & FACING_NE) { + ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } else { + ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } + ptr2 = ptr; + + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Adjacent_Cell(cell, nextpath); + //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + + /* + ** If veering to the left was not successful, then try veering + ** to the right. This only makes sense if the vehicle is trying + ** to turn 180 degrees. + */ + if (!ok && ABS(facediff) == 4) { + ptr = ptr2; // Pointer to path adjust list. + facediff = -facediff; + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + } + + /* + ** If a legal path addition was created, then install it in place + ** of the first path value. The initial path entry is to be replaced + ** with a sequence of path entries that create smooth turning. + */ + if (ok) { + if (path->Length <= 1) { + movmem(&stage[0], path->Command, MAX(counter, 1)); + path->Length = counter; + } else { + + /* + ** Optimize the transition path step from the smooth turn + ** first part as it joins with the rest of the normal + ** path. The normal prefix path steps are NOT to be optimized. + */ + if (counter) { + counter--; + path->Command[0] = stage[counter]; + Optimize_Moves(path, MOVE_OK); + } + + /* + ** If there is more than one prefix path element, then + ** insert the rest now. + */ + if (counter) { + movmem(&path->Command[0], &path->Command[counter], 40-counter); + movmem(&stage[0], &path->Command[0], counter); + path->Length += counter; + } + } + path->Command[path->Length] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * * + * This routine handles the track laying for the unit. This entails examining the unit's * + * current location as well as the direction and whether this unit is allowed to lay * + * tracks in the first place. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Lay_Track(void) +{ +#ifdef NEVER + static IconCommandType *_trackdirs[8] = { + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE, + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE + }; + + if (!(ClassF & CLASSF_TRACKS)) return; + + Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]); +#endif +} + + +/*********************************************************************************************** + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * * + * This routine will ensure that the midpoint (if any) of the track that the unit is * + * following, will be marked according to the mark type specified. * + * * + * INPUT: headto -- The head to coordinate. * + * * + * type -- The type of marking to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Mark_Track(COORDINATE headto, MarkType type) +{ + int value; + + if (type == MARK_UP) { + value = false; + } else { + value = true; + } + + if (headto) { + if (!IsOnShortTrack && TrackNumber != -1) { + + /* + ** If we have not passed the per cell process point we need + ** to deal with it. + */ + int tracknum = TrackControl[TrackNumber].Track; + if (tracknum) { + TrackType const * ptr = RawTracks[tracknum - 1].Track; + int cellidx = RawTracks[tracknum - 1].Cell; + if (cellidx > -1) { + DirType dir = ptr[cellidx].Facing; + + if (TrackIndex < cellidx && cellidx != -1) { + COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, &dir); + Map[Coord_Cell(offset)].Flag.Occupy.Vehicle = value; + } + } + } + } + Map[Coord_Cell(headto)].Flag.Occupy.Vehicle = value; + } +} + + +/*********************************************************************************************** + * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * * + * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple * + * calls to this routine are needed in order to fully offload all Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,* + * then this indicates that all Tiberium has been offloaded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int DriveClass::Offload_Tiberium_Bail(void) +{ + if (Tiberium) { + Tiberium--; + if (House->IsHuman) { + return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); // 25 in debugger + } + + // MBL 05.14.2020: AI harvested credits fix for multiplayer, since they are miscalculated, and it's noticed + // + // return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); 708 in debugger + // + if (GameToPlay == GAME_NORMAL) // Non-multiplayer game, keep the original calculation; 708 in debugger + { + return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); // Original (708), wrong calcualation but preserving to not break missions + } + else // Multiplayer game, apply the 1/3 bonus credits correction, so not be as extreme; 33 in debugger + { + return((UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3))/UnitTypeClass::STEP_COUNT); // Corrected calculation + } + } + return(0); +} + + +/*********************************************************************************************** + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * * + * This routine is used to verify that this object is allowed to move. Some objects can * + * be temporarily occupied and thus cannot move until the situation permits. * + * * + * INPUT: direction -- The direction that movement would be desired. * + * * + * OUTPUT: Can the unit move in the direction specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Ok_To_Move(DirType ) const +{ + return true; +} + + +/*********************************************************************************************** + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * * + * This routine will fetch a reference to the TypeClass of this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with reference to the type class of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & DriveClass::Class_Of(void) const +{ + return *Class; +} + + +/*************************************************************************** +** Smooth turn track tables. These are coordinate offsets from the center +** of the destination cell. These are the raw tracks that are modified +** by negating the X and Y portions as necessary. Also for reverse travelling +** direction, the track list can be processed backward. +** +** Track 1 = N +** Track 2 = NE +** Track 3 = N->NE 45 deg (double path consumption) +** Track 4 = N->E 90 deg (double path consumption) +** Track 5 = NE->SE 90 deg (double path consumption) +** Track 6 = NE->N 45 deg (double path consumption) +** Track 7 = N->NE (facing change only) +** Track 8 = NE->E (facing change only) +** Track 9 = N->E (facing change only) +** Track 10= NE->SE (facing change only) +** Track 11= back up into refinery +** Track 12= drive out of refinery +*/ +//#pragma warn -ias +DriveClass::TrackType const DriveClass::Track1[24] = { + {0x00F50000L,(DirType)0}, + {0x00EA0000L,(DirType)0}, + {0x00DF0000L,(DirType)0}, + {0x00D40000L,(DirType)0}, + {0x00C90000L,(DirType)0}, + {0x00BE0000L,(DirType)0}, + {0x00B30000L,(DirType)0}, + {0x00A80000L,(DirType)0}, + {0x009D0000L,(DirType)0}, + {0x00920000L,(DirType)0}, + {0x00870000L,(DirType)0}, + {0x007C0000L,(DirType)0}, // Track jump check here. + {0x00710000L,(DirType)0}, + {0x00660000L,(DirType)0}, + {0x005B0000L,(DirType)0}, + {0x00500000L,(DirType)0}, + {0x00450000L,(DirType)0}, + {0x003A0000L,(DirType)0}, + {0x002F0000L,(DirType)0}, + {0x00240000L,(DirType)0}, + {0x00190000L,(DirType)0}, + {0x000E0000L,(DirType)0}, + {0x00030000L,(DirType)0}, + {0x00000000L,(DirType)0} +}; + +DriveClass::TrackType const DriveClass::Track2[] = { + {0x00F8FF08L,(DirType)32}, + {0x00F0FF10L,(DirType)32}, + {0x00E8FF18L,(DirType)32}, + {0x00E0FF20L,(DirType)32}, + {0x00D8FF28L,(DirType)32}, + {0x00D0FF30L,(DirType)32}, + {0x00C8FF38L,(DirType)32}, + {0x00C0FF40L,(DirType)32}, + {0x00B8FF48L,(DirType)32}, + {0x00B0FF50L,(DirType)32}, + {0x00A8FF58L,(DirType)32}, + {0x00A0FF60L,(DirType)32}, + {0x0098FF68L,(DirType)32}, + {0x0090FF70L,(DirType)32}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track3[] = { + {0x01F5FF00L,(DirType)0}, + {0x01EAFF00L,(DirType)0}, + {0x01DFFF00L,(DirType)0}, + {0x01D4FF00L,(DirType)0}, + {0x01C9FF00L,(DirType)0}, + {0x01BEFF00L,(DirType)0}, + {0x01B3FF00L,(DirType)0}, + {0x01A8FF00L,(DirType)0}, + {0x019DFF00L,(DirType)0}, + {0x0192FF00L,(DirType)0}, + {0x0187FF00L,(DirType)0}, + {0x0180FF00L,(DirType)0}, + {0x0175FF00L,(DirType)0}, // Jump entry point here. + {0x016BFF00L,(DirType)0}, + {0x0160FF02L,(DirType)1}, + {0x0155FF04L,(DirType)3}, + {0x014CFF06L,(DirType)4}, + {0x0141FF08L,(DirType)5}, + {0x0137FF0BL,(DirType)7}, + {0x012EFF0FL,(DirType)8}, + {0x0124FF13L,(DirType)9}, + {0x011AFF17L,(DirType)11}, + {0x0110FF1BL,(DirType)12}, + {0x0107FF1FL,(DirType)13}, // Center cell processing here. + {0x00FCFF24L,(DirType)15}, + {0x00F3FF28L,(DirType)16}, + {0x00ECFF2CL,(DirType)17}, + {0x00E0FF32L,(DirType)19}, + {0x00D7FF36L,(DirType)20}, + {0x00CFFF3DL,(DirType)21}, + {0x00C6FF42L,(DirType)23}, + {0x00BAFF49L,(DirType)24}, + {0x00B0FF4DL,(DirType)25}, + {0x00A8FF58L,(DirType)27}, + {0x00A0FF60L,(DirType)28}, + {0x0098FF68L,(DirType)29}, + {0x0090FF70L,(DirType)31}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track4[] = { + {0x00F5FF00L,(DirType)0}, + {0x00EBFF00L,(DirType)0}, + {0x00E0FF00L,(DirType)0}, + {0x00D5FF00L,(DirType)0}, + {0x00CBFF01L,(DirType)0}, + {0x00C0FF03L,(DirType)0}, + {0x00B5FF05L,(DirType)1}, + {0x00ABFF07L,(DirType)1}, + {0x00A0FF0AL,(DirType)2}, + {0x0095FF0DL,(DirType)3}, + {0x008BFF10L,(DirType)4}, + {0x0080FF14L,(DirType)5}, // Track entry here. + {0x0075FF18L,(DirType)8}, + {0x006DFF1CL,(DirType)12}, + {0x0063FF22L,(DirType)16}, + {0x005AFF25L,(DirType)20}, + {0x0052FF2BL,(DirType)23}, + {0x0048FF32L,(DirType)27}, + {0x0040FF37L,(DirType)32}, + {0x0038FF3DL,(DirType)36}, + {0x0030FF46L,(DirType)39}, + {0x002BFF4FL,(DirType)43}, + {0x0024FF58L,(DirType)47}, + {0x0020FF60L,(DirType)51}, + {0x001BFF6DL,(DirType)54}, + {0x0017FF79L,(DirType)57}, + {0x0014FF82L,(DirType)60}, // Track jump here. + {0x0011FF8FL,(DirType)62}, + {0x000DFF98L,(DirType)63}, + {0x0009FFA2L,(DirType)64}, + {0x0006FFACL,(DirType)64}, + {0x0004FFB5L,(DirType)66}, + {0x0003FFC0L,(DirType)64}, + {0x0002FFCBL,(DirType)64}, + {0x0001FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track5[] = { + {0xFFF8FE08L,(DirType)32}, + {0xFFF0FE10L,(DirType)32}, + {0xFFE8FE18L,(DirType)32}, + {0xFFE0FE20L,(DirType)32}, + {0xFFD8FE28L,(DirType)32}, + {0xFFD0FE30L,(DirType)32}, + {0xFFC8FE38L,(DirType)32}, + {0xFFC0FE40L,(DirType)32}, + {0xFFB8FE48L,(DirType)32}, + {0xFFB0FE50L,(DirType)32}, + {0xFFA8FE58L,(DirType)32}, + {0xFFA0FE60L,(DirType)32}, + {0xFF98FE68L,(DirType)32}, + {0xFF90FE70L,(DirType)32}, + {0xFF88FE78L,(DirType)32}, + {0xFF80FE80L,(DirType)32}, // Track entry here. + {0xFF78FE88L,(DirType)32}, + {0xFF71FE90L,(DirType)32}, + {0xFF6AFE97L,(DirType)32}, + {0xFF62FE9FL,(DirType)32}, + {0xFF5AFEA8L,(DirType)32}, + {0xFF53FEB0L,(DirType)35}, + {0xFF4BFEB7L,(DirType)38}, + {0xFF44FEBEL,(DirType)41}, + {0xFF3EFEC4L,(DirType)44}, + {0xFF39FECEL,(DirType)47}, + {0xFF34FED8L,(DirType)50}, + {0xFF30FEE0L,(DirType)53}, + {0xFF2DFEEBL,(DirType)56}, + {0xFF2CFEF5L,(DirType)59}, + {0xFF2BFF00L,(DirType)62}, + {0xFF2CFF0BL,(DirType)66}, + {0xFF2DFF15L,(DirType)69}, + {0xFF30FF1FL,(DirType)72}, + {0xFF34FF28L,(DirType)75}, + {0xFF39FF30L,(DirType)78}, + {0xFF3EFF3AL,(DirType)81}, + {0xFF44FF44L,(DirType)84}, + {0xFF4BFF4BL,(DirType)87}, + {0xFF53FF50L,(DirType)90}, + {0xFF5AFF58L,(DirType)93}, + {0xFF62FF60L,(DirType)96}, + {0xFF6AFF68L,(DirType)96}, + {0xFF71FF70L,(DirType)96}, + {0xFF78FF78L,(DirType)96}, + {0xFF80FF80L,(DirType)96}, // Track jump check here. + {0xFF88FF88L,(DirType)96}, + {0xFF90FF90L,(DirType)96}, + {0xFF98FF98L,(DirType)96}, + {0xFFA0FFA0L,(DirType)96}, + {0xFFA8FFA8L,(DirType)96}, + {0xFFB0FFB0L,(DirType)96}, + {0xFFB8FFB8L,(DirType)96}, + {0xFFC0FFC0L,(DirType)96}, + {0xFFC8FFC8L,(DirType)96}, + {0xFFD0FFD0L,(DirType)96}, + {0xFFD8FFD8L,(DirType)96}, + {0xFFE0FFE0L,(DirType)96}, + {0xFFE8FFE8L,(DirType)96}, + {0xFFF0FFF0L,(DirType)96}, + {0xFFF8FFF8L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track6[] = { + {0x0100FE00L,(DirType)32}, + {0x00F8FE08L,(DirType)32}, + {0x00F0FE10L,(DirType)32}, + {0x00E8FE18L,(DirType)32}, + {0x00E0FE20L,(DirType)32}, + {0x00D8FE28L,(DirType)32}, + {0x00D0FE30L,(DirType)32}, + {0x00C8FE38L,(DirType)32}, + {0x00C0FE40L,(DirType)32}, + {0x00B8FE48L,(DirType)32}, + {0x00B0FE50L,(DirType)32}, + {0x00A8FE58L,(DirType)32}, + {0x00A0FE60L,(DirType)32}, + {0x0098FE68L,(DirType)32}, + {0x0090FE70L,(DirType)32}, + {0x0088FE78L,(DirType)32}, + {0x0080FE80L,(DirType)32}, // Jump entry point here. + {0x0078FE88L,(DirType)32}, + {0x0070FE90L,(DirType)32}, + {0x0068FE98L,(DirType)32}, + {0x0060FEA0L,(DirType)32}, + {0x0058FEA8L,(DirType)32}, + {0x0055FEAEL,(DirType)32}, + {0x004EFEB8L,(DirType)35}, + {0x0048FEC0L,(DirType)37}, + {0x0042FEC9L,(DirType)40}, + {0x003BFED2L,(DirType)43}, + {0x0037FEDAL,(DirType)45}, + {0x0032FEE3L,(DirType)48}, + {0x002BFEEBL,(DirType)51}, + {0x0026FEF5L,(DirType)53}, + {0x0022FEFEL,(DirType)56}, + {0x001CFF08L,(DirType)59}, + {0x0019FF12L,(DirType)61}, + {0x0015FF1BL,(DirType)64}, + {0x0011FF26L,(DirType)64}, + {0x000EFF30L,(DirType)64}, + {0x000BFF39L,(DirType)64}, + {0x0009FF43L,(DirType)64}, + {0x0007FF4EL,(DirType)64}, + {0x0005FF57L,(DirType)64}, + {0x0003FF62L,(DirType)64}, + {0x0001FF6DL,(DirType)64}, + {0x0000FF77L,(DirType)64}, + {0x0000FF80L,(DirType)64}, // Track jump check here. + {0x0000FF8BL,(DirType)64}, + {0x0000FF95L,(DirType)64}, + {0x0000FFA0L,(DirType)64}, + {0x0000FFABL,(DirType)64}, + {0x0000FFB5L,(DirType)64}, + {0x0000FFC0L,(DirType)64}, + {0x0000FFCBL,(DirType)64}, + {0x0000FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track7[] = { + {0x0006FFFFL,(DirType)0}, + {0x000CFFFEL,(DirType)4}, + {0x0011FFFCL,(DirType)8}, + {0x0018FFFAL,(DirType)12}, + {0x001FFFF6L,(DirType)16}, + {0x0024FFF3L,(DirType)19}, + {0x002BFFF0L,(DirType)22}, + {0x0030FFFDL,(DirType)23}, + {0x0035FFEBL,(DirType)24}, + {0x0038FFE8L,(DirType)25}, + {0x003CFFE6L,(DirType)26}, + {0x0040FFE3L,(DirType)27}, + {0x0043FFE0L,(DirType)28}, + {0x0046FFDDL,(DirType)29}, + {0x0043FFDFL,(DirType)30}, + {0x0040FFE1L,(DirType)30}, + {0x003CFFE3L,(DirType)30}, + {0x0038FFE5L,(DirType)30}, + {0x0035FFE7L,(DirType)31}, + {0x0030FFE9L,(DirType)31}, + {0x002BFFEBL,(DirType)31}, + {0x0024FFEDL,(DirType)31}, + {0x001FFFF1L,(DirType)31}, + {0x0018FFF4L,(DirType)32}, + {0x0011FFF7L,(DirType)32}, + {0x000CFFFAL,(DirType)32}, + {0x0006FFFDL,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track8[] = { + {0x0003FFFCL,(DirType)32}, + {0x0006FFF7L,(DirType)36}, + {0x000AFFF1L,(DirType)40}, + {0x000CFFEBL,(DirType)44}, + {0x000DFFE4L,(DirType)46}, + {0x000EFFDCL,(DirType)48}, + {0x000FFFD5L,(DirType)50}, + {0x0010FFD0L,(DirType)52}, + {0x0011FFC9L,(DirType)54}, + {0x0012FFC2L,(DirType)56}, + {0x0011FFC0L,(DirType)58}, + {0x0010FFC2L,(DirType)60}, + {0x000EFFC9L,(DirType)62}, + {0x000CFFCFL,(DirType)64}, + {0x000AFFD5L,(DirType)64}, + {0x0008FFDAL,(DirType)64}, + {0x0006FFE2L,(DirType)64}, + {0x0004FFE9L,(DirType)64}, + {0x0002FFEFL,(DirType)64}, + {0x0001FFF5L,(DirType)64}, + {0x0000FFF9L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track9[] = { + {0xFFF50002L,(DirType)0}, + {0xFFEB0004L,(DirType)2}, + {0xFFE00006L,(DirType)4}, + {0xFFD50009L,(DirType)6}, + {0xFFCE000CL,(DirType)9}, + {0xFFC8000FL,(DirType)11}, + {0xFFC00012L,(DirType)13}, + {0xFFB80015L,(DirType)16}, + {0xFFC00012L,(DirType)18}, + {0xFFC8000EL,(DirType)20}, + {0xFFCE000AL,(DirType)22}, + {0xFFD50004L,(DirType)24}, + {0xFFDE0000L,(DirType)26}, + {0xFFE9FFF8L,(DirType)28}, + {0xFFEEFFF2L,(DirType)30}, + {0xFFF5FFEBL,(DirType)32}, + {0xFFFDFFE1L,(DirType)34}, + {0x0002FFD8L,(DirType)36}, + {0x0007FFD2L,(DirType)39}, + {0x000BFFCBL,(DirType)41}, + {0x0010FFC5L,(DirType)43}, + {0x0013FFBEL,(DirType)45}, + {0x0015FFB7L,(DirType)48}, + {0x0013FFBEL,(DirType)50}, + {0x0011FFC5L,(DirType)52}, + {0x000BFFCCL,(DirType)54}, + {0x0008FFD4L,(DirType)56}, + {0x0005FFDFL,(DirType)58}, + {0x0003FFEBL,(DirType)62}, + {0x0001FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track10[] = { + {0xFFF6000BL,(DirType)32}, + {0xFFF00015L,(DirType)37}, + {0xFFEB0020L,(DirType)42}, + {0xFFE9002BL,(DirType)47}, + {0xFFE50032L,(DirType)52}, + {0xFFE30038L,(DirType)57}, + {0xFFE00040L,(DirType)60}, + {0xFFE20038L,(DirType)62}, + {0xFFE40032L,(DirType)64}, + {0xFFE5002AL,(DirType)68}, + {0xFFE6001EL,(DirType)70}, + {0xFFE70015L,(DirType)72}, + {0xFFE8000BL,(DirType)74}, + {0xFFE90000L,(DirType)76}, + {0xFFE8FFF5L,(DirType)78}, + {0xFFE7FFEBL,(DirType)80}, + {0xFFE6FFE0L,(DirType)82}, + {0xFFE5FFD5L,(DirType)84}, + {0xFFE4FFCEL,(DirType)86}, + {0xFFE2FFC5L,(DirType)88}, + {0xFFE0FFC0L,(DirType)90}, + {0xFFE3FFC5L,(DirType)92}, + {0xFFE5FFCEL,(DirType)94}, + {0xFFE9FFD5L,(DirType)95}, + {0xFFEBFFE0L,(DirType)96}, + {0xFFF0FFEBL,(DirType)96}, + {0xFFF6FFF5L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track11[] = { + {0x01000000L,DIR_SW}, + {0x00F30008L,DIR_SW}, + {0x00E50010L,DIR_SW_X1}, + {0x00D60018L,DIR_SW_X1}, + {0x00C80020L,DIR_SW_X1}, + {0x00B90028L,DIR_SW_X1}, + {0x00AB0030L,DIR_SW_X2}, + {0x009C0038L,DIR_SW_X2}, + {0x008D0040L,DIR_SW_X2}, + {0x007F0048L,DIR_SW_X2}, + {0x00710050L,DIR_SW_X2}, + {0x00640058L,DIR_SW_X2}, + {0x00550060L,DIR_SW_X2}, + + {0x00000000L,DIR_SW_X2} +}; + +DriveClass::TrackType const DriveClass::Track12[] = { + {0xFF550060L,DIR_SW_X2}, + {0xFF640058L,DIR_SW_X2}, + {0xFF710050L,DIR_SW_X2}, + {0xFF7F0048L,DIR_SW_X2}, + {0xFF8D0040L,DIR_SW_X2}, + {0xFF9C0038L,DIR_SW_X2}, + {0xFFAB0030L,DIR_SW_X2}, + {0xFFB90028L,DIR_SW_X1}, + {0xFFC80020L,DIR_SW_X1}, + {0xFFD60018L,DIR_SW_X1}, + {0xFFE50010L,DIR_SW_X1}, + {0xFFF30008L,DIR_SW}, + + {0x00000000L,DIR_SW} +}; + +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-17),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-16),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-15),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-14),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-13),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-12),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-11),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-10),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-9),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-8),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-7),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-6),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-5),(DirType)(DIR_SW-9)}, + {XYP_COORD(2,-4),(DirType)(DIR_SW-7)}, + {XYP_COORD(2,-3),(DirType)(DIR_SW-5)}, + {XYP_COORD(1,-2),(DirType)(DIR_SW-3)}, + {XYP_COORD(1,-1),(DirType)(DIR_SW-1)}, + + {0x00000000L,DIR_SW} +}; + + +/* +** There are a limited basic number of tracks that a vehicle can follow. These +** are they. Each track can be interpreted differently but this is controlled +** by the TrackControl structure elaborated elsewhere. +*/ +DriveClass::RawTrackType const DriveClass::RawTracks[13] = { + {Track1, -1, 0, -1}, + {Track2, -1, 0, -1}, + {Track3, 37, 12, 22}, + {Track4, 26, 11, 19}, + {Track5, 45, 15, 31}, + {Track6, 44, 16, 27}, + {Track7, -1, 0, -1}, + {Track8, -1, 0, -1}, + {Track9, -1, 0, -1}, + {Track10, -1, 0, -1}, + {Track11, -1, 0, -1}, + {Track12, -1, 0, -1}, + {Track13, -1, 0, -1} +}; + + +/*************************************************************************** +** Smooth turning control table. Given two directions in a path list, this +** table determines which track to use and what modifying operations need +** be performed on the track data. +*/ +DriveClass::TurnTrackType const DriveClass::TrackControl[67] = { + {1, 0, DIR_N, F_}, // 0-0 + {3, 7, DIR_NE, F_D}, // 0-1 (raw chart) + {4, 9, DIR_E, F_D}, // 0-2 (raw chart) + {0, 0, DIR_SE, F_}, // 0-3 ! + {0, 0, DIR_S, F_}, // 0-4 ! + {0, 0, DIR_SW, F_}, // 0-5 ! + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0 + {2, 0, DIR_NE, F_}, // 1-1 (raw chart) + {6, 8, DIR_E, F_D}, // 1-2 (raw chart) + {5, 10, DIR_SE, F_D}, // 1-3 (raw chart) + {0, 0, DIR_S, F_}, // 1-4 ! + {0, 0, DIR_SW, F_}, // 1-5 ! + {0, 0, DIR_W, F_}, // 1-6 ! + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0 + {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1 + {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3 + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4 + {0, 0, DIR_SW, F_}, // 2-5 ! + {0, 0, DIR_W, F_}, // 2-6 ! + {0, 0, DIR_NW, F_}, // 2-7 ! + {0, 0, DIR_N, F_}, // 3-0 ! + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1 + {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2 + {2, 0, DIR_SE, F_Y}, // 3-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4 + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5 + {0, 0, DIR_W, F_}, // 3-6 ! + {0, 0, DIR_NW, F_}, // 3-7 ! + {0, 0, DIR_N, F_}, // 4-0 ! + {0, 0, DIR_NE, F_}, // 4-1 ! + {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3 + {1, 0, DIR_S, F_Y}, // 4-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5 + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6 + {0, 0, DIR_NW, F_}, // 4-7 ! + {0, 0, DIR_N, F_}, // 5-0 ! + {0, 0, DIR_NE, F_}, // 5-1 ! + {0, 0, DIR_E, F_}, // 5-2 ! + {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4 + {2, 0, DIR_SW, F_T}, // 5-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6 + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0 + {0, 0, DIR_NE, F_}, // 6-1 ! + {0, 0, DIR_E, F_}, // 6-2 ! + {0, 0, DIR_SE, F_}, // 6-3 ! + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5 + {1, 0, DIR_W, F_T}, // 6-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0 + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1 + {0, 0, DIR_E, F_}, // 7-2 ! + {0, 0, DIR_SE, F_}, // 7-3 ! + {0, 0, DIR_S, F_}, // 7-4 ! + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6 + {2, 0, DIR_NW, F_X}, // 7-7 + + {11, 11, DIR_SW, F_}, // Backup harvester into refinery. + {12, 12, DIR_SW_X2, F_}, // Drive back into refinery. + {13, 13, DIR_SW, F_} // Drive out of weapons factory. +}; \ No newline at end of file diff --git a/TIBERIANDAWN/DRIVE.H b/TIBERIANDAWN/DRIVE.H new file mode 100644 index 000000000..dc66aa0b3 --- /dev/null +++ b/TIBERIANDAWN/DRIVE.H @@ -0,0 +1,223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\drive.h_v 2.19 16 Oct 1995 16:47:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DRIVE_H +#define DRIVE_H + +#include "foot.h" + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class DriveClass : public FootClass +{ + public: + /* + ** This points to the static control data that gives 'this' unit its characteristics. + */ + UnitTypeClass const * const Class; + + + /* + ** Simulated sub-pixel offset to find out where the object would be if it could make a sub-pixel move. Only used by + ** GlyphX client for more accurate positioning. + ** + ** ST - 4/30/2019 8:05AM + */ + short SimLeptonX; + short SimLeptonY; + + /* + ** This records the number of "loads" of Tiberium the unit is carrying. Only + ** harvesters use this field. + */ + unsigned char Tiberium; + + /* + ** If this unit performing harvesting action, then this flag is true. The flag + ** is located here because the other bit flags here give it a free place to + ** reside. + */ + unsigned IsHarvesting:1; + + /* + ** This flags when a transport vehicle could not unload at its designated location + ** and is heading off the map to try again later. When this flag is true, the + ** transport unit is allowed to disappear when it reaches the edge of the map. + */ + unsigned IsReturning:1; + + /* + ** Some units must have their turret locked down to face their body direction. + ** When this flag is set, this condition is in effect. This flag is a more + ** accurate check than examining the TrackNumber since the turret may be + ** rotating into position so that a pending track may start. During this process + ** the track number does not indicate anything. + */ + unsigned IsTurretLockedDown:1; + + /* + ** This vehicle could be processing a "short track". A short track is one that + ** doesn't actually go anywhere. Kind of like turning in place. + */ + unsigned IsOnShortTrack:1; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + DriveClass(void); + DriveClass(UnitType classid, HousesType house); + virtual ~DriveClass(void) {}; + operator UnitType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual int Offload_Tiberium_Bail(void); + void Do_Turn(DirType dir); + virtual void Approach_Target(void); + virtual ObjectTypeClass const & Class_Of(void) const; + virtual void Overrun_Square(CELL cell, bool threaten=true); + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(bool center); + virtual bool Ok_To_Move(DirType ) const; + virtual void AI(void); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + void Force_Track(int track, COORDINATE coord); + virtual int Tiberium_Load(void) const; + + void Exit_Map(void); + void Mark_Track(COORDINATE headto, MarkType type); + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /********************************************************************** + ** These enumerations are used as working constants that exist only + ** in the DriveClass namespace. + */ + enum DriveClassEnum { + BACKUP_INTO_REFINERY=64, // Track to backup into refinery. + OUT_OF_REFINERY, // Track to leave refinery. + OUT_OF_WEAPON_FACTORY // Track to leave weapons factory. + }; + + private: + + /**************************************************************************** + ** Smooth turning tracks are controlled by this structure and these + ** processing bits. + */ + typedef enum TrackControlType : unsigned char { + F_=0x00, // No translation necessary? + F_T=0x01, // Transpose X and Y components? + F_X=0x02, // Reverse X component sign? + F_Y=0x04, // Reverse Y component sign? + F_D=0x08 // Two cell consumption? + } TrackControlType; + //#define F_S 0x10 // Is this a 90 degree turn? + + typedef struct { + char Track; // Which track to use. + char StartTrack; // Track when starting from stand-still. + DirType Facing; // Facing when track has been completed. + DriveClass::TrackControlType Flag; // List processing flag bits. + } TurnTrackType; + + typedef struct { + COORDINATE Offset; // Offset to origin coordinate. + DirType Facing; // Facing (primary track). + } TrackType; + + typedef struct { + DriveClass::TrackType const * Track; // Pointer to track list. + int Jump; // Index where track jumping is allowed. + int Entry; // Entry point if jumping to this track. + int Cell; // Per cell process should occur at this index. + } RawTrackType; + + /* + ** These speed values are used to accumulate movement and then + ** convert them into pixel "steps" that are then translated through + ** the currently running track so that the unit will move. + */ + unsigned char SpeedAccum; + + /* + ** This the track control logic (used for ground vehicles only). The 'Track' + ** variable holds the track being followed (0 == not following track). The + ** 'TrackIndex' variable holds the current index into the specified track + ** (starts at 0). + */ + char TrackNumber; + char TrackIndex; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual void Fixup_Path(PathType *path); + bool While_Moving(void); + bool Start_Of_Move(void); + void Lay_Track(void); + COORDINATE Smooth_Turn(COORDINATE adj, DirType *dir); + + static TurnTrackType const TrackControl[67]; + static RawTrackType const RawTracks[13]; + static TrackType const Track13[]; + static TrackType const Track12[]; + static TrackType const Track11[]; + static TrackType const Track10[]; + static TrackType const Track9[]; + static TrackType const Track8[]; + static TrackType const Track7[]; + static TrackType const Track6[]; + static TrackType const Track5[]; + static TrackType const Track4[]; + static TrackType const Track3[]; + static TrackType const Track2[]; + static TrackType const Track1[24]; +}; + +//PG_TO_FIX +//inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType); +//inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType); +//inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType); + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/EDIT.CPP b/TIBERIANDAWN/EDIT.CPP new file mode 100644 index 000000000..6771013a4 --- /dev/null +++ b/TIBERIANDAWN/EDIT.CPP @@ -0,0 +1,468 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\edit.cpv 2.18 16 Oct 1995 16:48:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EditClass::Action -- Handles input events. * + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * EditClass::Draw_Text -- Draws the edit gadget text. * + * EditClass::EditClass -- Normal constructor for edit class object. * + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * EditClass::EditClass -- Normal constructor for edit class object. * + * * + * This is the normal constructor used to create an edit object. * + * * + * INPUT: id -- The ID number for this edit object. This is the ID number that will be * + * returned by the Input() function when the key is pressed if this * + * gadget has the keyboard input focus. * + * * + * text -- Referenct to the text buffer that the edit gadget will modify as keyboard * + * input is processed. The value that this buffer contains is the default * + * text displayed. * + * * + * maxlen-- The maximum size of the text buffer specified. This length INCLUDES the * + * trailing null character so a simple sizeof() function call can be used. * + * * + * flags -- These are the text print control flags. It is used to control how the * + * text looks in the edit box. Use the normal TPF_??? flags. * + * * + * x,y -- The pixel coordinates of the upper left corner of the edit gadget area. * + * * + * w,h -- The pixel dimensions of the edit box. If either of these are no provided, * + * or set to -1, then the dimension is determined from the string itself. * + * * + * sytle -- This style flag parameter control what kind of characters are allowed in * + * the edit box. The initial string in the text buffer may contain illegal * + * characters, but they are NOT removed regardless of this parameter. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/05/1995 MML : Created. * + * 01/21/1995 JLB : Modified. * + *=============================================================================================*/ +EditClass::EditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + ControlClass (id, x, y, w, h, LEFTPRESS), String(text) +{ + TextFlags = flags; + EditFlags = style; + Color = CC_GREEN; + Set_Text(text, max_len); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + + if (h == -1) { + Height = FontHeight+2; + } + if (w == -1) { + if (strlen(String) > 0) { + Width = String_Pixel_Width(String) + 6; + } else { + Width = ((Char_Pixel_Width('X')+FontXSpacing) * (MaxLength+1)) + 2; + } + } + } + IsReadOnly = 0; +} + + +/*********************************************************************************************** + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * * + * This default destructor removes the focus setting if it currently has it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/24/1995 JLB : Created. * + *=============================================================================================*/ +EditClass::~EditClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*********************************************************************************************** + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * * + * Use this routine to change the text that this edit gadget refers to. * + * * + * INPUT: text -- Reference to the character array that this edit gadget will be * + * modifying. * + * max_len -- The maximum size of the buffer that will be modified. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Set_Text(char * text, int max_len) +{ + String = text; + MaxLength = max_len-1; + Length = strlen(String); + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * * + * This routine will render the edit box. This will show the box outline as well as any * + * text it may contain. * + * * + * INPUT: forced -- Should the edit box be drawn even if it thinks it doesn't have to? * + * * + * OUTPUT: Was the edit box drawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Background(); + + /* + ** Display the text. + */ + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * EditClass::Action -- Handles input events. * + * * + * This routine will handle all mouse and keyboard events directed at this edit box * + * gadget. For keyboard events, this will insert the characters into the edit box. * + * * + * INPUT: flags -- The event flag that triggered this function call. * + * * + * key -- Reference to the keyboard/mouse event that triggered this function call. * + * * + * OUTPUT: Should the list be processed further? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Action(unsigned flags, KeyNumType & key) +{ + + /* + ** If this is a read-only edit box, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** If the left mouse button is pressed over this gadget, then set the focus to + ** this gadget. The event flag is cleared so that no button ID number is returned. + */ + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + /* + ** Handle keyboard events here. Normally, the key is added to the string, but if the + ** RETURN key is pressed, then the button ID number is returned from the Input() + ** function. + */ + if ((flags & KEYBOARD) && Has_Focus()) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard::To_ASCII(key) & 0x00ff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9'){ + key &= ~WWKEY_VK_BIT; + + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + + }else{ + + /* + ** Filter out all special keys except return and backspace + */ + if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 127) + || key == KN_RETURN || key == KN_BACKSPACE){ + + + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + if (Handle_Key(Keyboard::To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + }else{ + //if (key & WWKEY_RLS_BIT){ + // if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + flags &= ~KEYBOARD; + key = KN_NONE; + // } + //} + } + } + } + } + + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * * + * This routine will redraw the edit gadget background. The overlaying text is handled by * + * a different routine. The mouse is guaranteed to be hidden when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Background(void) +{ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_BOX, true); +} + + +/*********************************************************************************************** + * EditClass::Draw_Text -- Draws the edit gadget text. * + * * + * This routine is called when the edit gadget text needs to be drawn. The background has * + * already been drawn by the time this function is called. The mouse is guaranteed to be * + * hidden as well. * + * * + * INPUT: text -- The text to draw in the edit gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Text(char const * text) +{ + if (FontPtr == GradFont6Ptr) { + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && (int)strlen(text) < MaxLength && + ((int)String_Pixel_Width(text) + (int)String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } + } else { + Conquer_Clip_Text_Print(text, X+1, Y+1, Has_Focus() ? BLUE : WHITE, TBLACK, TextFlags, Width-2); + + if (Has_Focus() && (int)strlen(text) < MaxLength && + ((int)String_Pixel_Width(text) + (int)String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print("_",X+1+String_Pixel_Width(text),Y+1,BLUE,TBLACK, TextFlags); + } + } + +} + + +/*********************************************************************************************** + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * * + * This is the gruntwork routine that processes keyboard input to the edit gadget. This * + * routine will be called when keyboard input has been detected and this gadget has the * + * current focus. * + * * + * INPUT: ascii -- The ASCII key code that was fetched from the keyboard buffer. * + * * + * OUTPUT: bool; Should this keyboard input NOT cause the gadget ID number to be returned * + * from the controlling Input() routine? Typically, the return value would be * + * true unless the focus is lost due to the key being pressed. * + * * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool EditClass::Handle_Key(KeyASCIIType ascii) +{ + switch (ascii) { + /* + ** Handle the special case of a non-keyboard event. It is possible that this + ** key code might be passed to this routine if this routine has been overridden + ** and the key event was consumed. + */ + case 0: + break; + + /* + ** If the return key is pressed, then remove the focus from this edit + ** gadget but otherwise let the normal gadget processing proceed. This + ** causes the gadget ID number to be returned from the Input() function + ** so that the controlling program will know that the text can be + ** processed. + */ + case KA_RETURN: + Clear_Focus(); + return(false); + + /* + ** When the BACKSPACE key is pressed, remove the last character in the edit string. + */ + case KA_BACKSPACE: + if (Length) { + Length--; + String[Length] = '\0'; + Flag_To_Redraw(); + } + break; + + /* + ** If the keyboard event was not a recognized special key, then examine to see + ** if it can legally be added to the edit string and do so if possible. + */ + default: + + /* + ** Don't add a character if the length is greater than edit width. + */ + if (((int)String_Pixel_Width(String) + (int)Char_Pixel_Width(ascii) ) >= (Width-2)) { + break; + } + + /* + ** Don't add a character if the length is already at maximum. + */ + if (Length >= MaxLength) break; + + /* + ** Invisible characters are never added to the string. This is + ** especially true for spaces at the beginning of the string. + */ + if (!isgraph(ascii) && ascii != ' ') break; + if (ascii == ' ' && Length == 0) break; + + /* + ** If this is an upper case only edit gadget, then force the alphabetic + ** character to upper case. + */ + if ((EditFlags & UPPERCASE) && isalpha(ascii)) { + ascii = (KeyASCIIType)toupper(ascii); + } + + if ((!(EditFlags & NUMERIC) || !isdigit(ascii)) && + (!(EditFlags & ALPHA) || !isalpha(ascii)) && + (!(EditFlags & MISC) || isalnum(ascii)) && + ascii != ' ') { + break; + } + + /* + ** The character passed all legality checks, so add it to the edit string + ** and flag this gadget to be redrawn. The manual flag to redraw is needed + ** because the event flag has been cleared. This prevents the gadget's ID + ** number from being returned just because the gadget has been edited. + */ + String[Length++] = ascii; + String[Length] = '\0'; + Flag_To_Redraw(); + break; + } + return(true); +} diff --git a/TIBERIANDAWN/EDIT.H b/TIBERIANDAWN/EDIT.H new file mode 100644 index 000000000..ff0191122 --- /dev/null +++ b/TIBERIANDAWN/EDIT.H @@ -0,0 +1,113 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\edit.h_v 2.17 16 Oct 1995 16:46:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EDIT_H +#define EDIT_H + +class EditClass : public ControlClass +{ + public: + typedef enum EditStyle { + ALPHA =0x0001, // Edit accepts alphabetic characters. + NUMERIC =0x0002, // Edit accepts numbers. + MISC =0x0004, // Edit accepts misc graphic characters. + UPPERCASE =0x0008, // Force to upper case. + ALPHANUMERIC=(int)ALPHA|(int)NUMERIC|(int)MISC, + } EditStyle; + + EditClass (int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1, EditStyle style=ALPHANUMERIC); + virtual ~EditClass(void); + + virtual int Draw_Me(int forced); + virtual void Set_Text(char * text, int max_len); + void Set_Color (int color) { Color = color; } + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + + /* + ** These are the text size and style flags to be used when displaying the text + ** of the edit gadget. + */ + TextPrintType TextFlags; + + /* + ** Input flags that control what characters are allowed in the string. + */ + EditStyle EditFlags; + + /* + ** Pointer to text staging buffer and the maximum length of the string it + ** can contain. + */ + char *String; + int MaxLength; + + /* + ** This is the current length of the string. This length will never exceed the + ** MaxLength allowed. + */ + int Length; + + /* + ** This is the desired color of the edit control. + */ + int Color; + + virtual int Action (unsigned flags, KeyNumType &key); + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + virtual bool Handle_Key(KeyASCIIType ascii); + + private: + int IsReadOnly; + +}; + +//inline EditClass::EditStyle operator |(EditClass::EditStyle, EditClass::EditStyle); +//inline EditClass::EditStyle operator &(EditClass::EditStyle, EditClass::EditStyle); +//inline EditClass::EditStyle operator ~(EditClass::EditStyle); + +inline EditClass::EditStyle operator|(EditClass::EditStyle a, EditClass::EditStyle b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline EditClass::EditStyle operator&(EditClass::EditStyle a, EditClass::EditStyle b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline EditClass::EditStyle operator~(EditClass::EditStyle a) +{return static_cast(~static_cast(a));} + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ENDING.CPP b/TIBERIANDAWN/ENDING.CPP new file mode 100644 index 000000000..68265aeb1 --- /dev/null +++ b/TIBERIANDAWN/ENDING.CPP @@ -0,0 +1,258 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ending.cpv 1.5 16 Oct 1995 16:50:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +void GDI_Ending(void) +{ +#ifdef DEMO + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + Get_Key_Num(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + +#else + if (TempleIoned) { + Play_Movie("GDIFINB"); + } else { + Play_Movie("GDIFINA"); + } + + Score.Presentation(); + + if (TempleIoned) { + Play_Movie("GDIEND2"); + } else { + Play_Movie("GDIEND1"); + } + + CountDownTimerClass count; + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); +// CountDownTimerClass count; + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); +#endif +} + + +#ifndef DEMO +/*********************************************************************************************** + * Nod_Ending -- play ending movies for Nod players * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 7/10/1995 BWG : Created. * + *=============================================================================================*/ +void Nod_Ending(void) +{ + static unsigned char const _tanpal[]={0x0,0xED,0xED,0x2C,0x2C,0xFB,0xFB,0xFD,0xFD,0x0,0x0,0x0,0x0,0x0,0x52,0x0}; + + char fname[12]; +#ifdef NOT_FOR_WIN95 + char *satpic = new char[64000]; +#endif //NOT_FOR_WIN95 + int oldfontxspacing = FontXSpacing; + void const *oldfont; + + Score.Presentation(); + + oldfont = Set_Font(ScoreFontPtr); + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + SeenBuff.Clear(); + HidPage.Clear(); + PseudoSeenBuff->Clear(); + + void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL")); + Load_Uncompress(CCFileClass("SATSEL.CPS"), SysMemPage, SysMemPage); +#ifdef NOT_FOR_WIN95 + memcpy(satpic, HidPage.Get_Buffer(), 64000); +#else + SysMemPage.Blit(*PseudoSeenBuff); +#endif //NOT_FOR_WIN95 + void *kanefinl = Load_Sample("KANEFINL.AUD"); + void *loopie6m = Load_Sample("LOOPIE6M.AUD"); + + Play_Movie("NODFINAL", THEME_NONE, false); + + Hide_Mouse(); + Wait_Vert_Blank(); + Set_Palette(localpal); +#ifdef NOT_FOR_WIN95 + memcpy(SeenBuff.Get_Buffer(), satpic, 64000); +#endif //NOT_FOR_WIN95 + Show_Mouse(); + + InterpolationPaletteChanged = TRUE; + InterpolationPalette = (unsigned char*)localpal; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("SATSELIN.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,"SATSELIN.PAL"); + + Keyboard::Clear(); + Play_Sample(kanefinl,255,128); + Play_Sample(loopie6m,255,128); + + bool mouseshown = false; + bool done = false; + int selection = 1; + bool printedtext = false; + while (!done) { + if (!printedtext && !Is_Sample_Playing(kanefinl)) { + printedtext++; + Alloc_Object(new ScorePrintClass(Text_String(TXT_SEL_TARGET), 0, 180,_tanpal)); + mouseshown = true; + Show_Mouse(); + } + Call_Back_Delay(1); + if (!Keyboard::Check()) { + if (!Is_Sample_Playing(loopie6m)) Play_Sample(loopie6m,255,128); + } else { + if (Is_Sample_Playing(kanefinl)) { + Clear_KeyBuffer(); + } else { + int key = Keyboard::Get(); + if ((key & 0x10FF) == KN_LMOUSE && !(key & KN_RLSE_BIT)) { + int mousex = _Kbd->MouseQX; + int mousey = _Kbd->MouseQY; + if (mousey >= 22*2 && mousey <= 177*2) { + done++; + if (mousex < 160*2 && mousey < 100*2) selection = 2; + if (mousex < 160*2 && mousey >= 100*2) selection = 3; + if (mousex >= 160*2 && mousey >= 100*2) selection = 4; + } + } + } + } + } + if (mouseshown) Hide_Mouse(); +#ifdef NOT_FOR_WIN95 + delete satpic; +#else + delete PseudoSeenBuff; +#endif //NOT_FOR_WIN95 + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + // erase the "choose a target" text + SeenBuff.Fill_Rect(0,180*2,319*2,199*2,0); + TextPrintBuffer->Fill_Rect(0,180*2,319*2,199*2,0); + + Hide_Mouse(); + Keyboard::Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + Free_Sample(kanefinl); + Free_Sample(loopie6m); + + sprintf(fname,"NODEND%d",selection); + PreserveVQAScreen = 1; + Play_Movie(fname); + + CountDownTimerClass count; + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); +// CountDownTimerClass count; + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); + + delete [] localpal; + delete TextPrintBuffer; + BlitList.Clear(); +} +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ENDING.H b/TIBERIANDAWN/ENDING.H new file mode 100644 index 000000000..0d67f44b8 --- /dev/null +++ b/TIBERIANDAWN/ENDING.H @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ENDING_H +#define ENDING_H + +void Nod_Ending(void); + +#endif diff --git a/TIBERIANDAWN/EVENT.CPP b/TIBERIANDAWN/EVENT.CPP new file mode 100644 index 000000000..fe3dd46f2 --- /dev/null +++ b/TIBERIANDAWN/EVENT.CPP @@ -0,0 +1,779 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\event.cpv 2.17 16 Oct 1995 16:50:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EventClass::EventClass -- Construct an id and cell based event. * + * EventClass::EventClass -- Construct simple target type event. * + * EventClass::EventClass -- Constructor for mission change events. * + * EventClass::EventClass -- Constructor for navigation computer events. * + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * EventClass::EventClass -- Constructor for sidebar build events. * + * EventClass::EventClass -- Constructs event to transfer special flags. * + * EventClass::EventClass -- Default constructor for event objects. * + * EventClass::EventClass -- Event for sequencing animations. * + * EventClass::EventClass -- Megamission assigned to unit. * + * EventClass::Execute -- Execute a queued command. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ccdde.h" + +/*************************************************************************** +** Table of what data is really used in the EventClass struct for different +** events. This table must be kept current with the EventType enum. +*/ +unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { + 0, // EMPTY + size_of(EventClass, Data.General ), // ALLY + size_of(EventClass, Data.MegaMission ), // MEGAMISSION + size_of(EventClass, Data.Target ), // IDLE + size_of(EventClass, Data.Target ), // SCATTER + 0, // DESTRUCT + 0, // DEPLOY + size_of(EventClass, Data.Place ), // PLACE + 0, // OPTIONS + size_of(EventClass, Data.General ), // GAMESPEED + size_of(EventClass, Data.Specific ), // PRODUCE + size_of(EventClass, Data.Specific.Type ), // SUSPEND + size_of(EventClass, Data.Specific.Type ), // ABANDON + size_of(EventClass, Data.Target ), // PRIMARY + size_of(EventClass, Data.Special ), // SPECIAL_PLACE + 0, // EXIT + size_of(EventClass, Data.Anim ), // ANIMATION + size_of(EventClass, Data.Target ), // REPAIR + size_of(EventClass, Data.Target ), // SELL + size_of(EventClass, Data.Options ), // SPECIAL + 0, // FRAMESYNC + 0, // MESSAGE + size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME + size_of(EventClass, Data.FrameInfo ), // FRAMEINFO + size_of(EventClass, Data.NavCom ), // ARCHIVE + size_of(EventClass, Data.Timing ), // TIMING + size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME +}; + +char * EventClass::EventNames[EventClass::LAST_EVENT] = { + "EMPTY", + "ALLY", + "MEGAMISSION", + "IDLE", + "SCATTER", + "DESTRUCT", + "DEPLOY", + "PLACE", + "OPTIONS", + "GAMESPEED", + "PRODUCE", + "SUSPEND", + "ABANDON", + "PRIMARY", + "SPECIAL_PLACE", + "EXIT", + "ANIMATION", + "REPAIR", + "SELL", + "SPECIAL", + "FRAMESYNC", + "MESSAGE", + "RESPONSE_TIME", + "FRAMEINFO", + "ARCHIVE", + "TIMING", + "PROCESS_TIME", +}; + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructs event to transfer special flags. * + * * + * This constructs an event that will transfer the special flags. * + * * + * INPUT: data -- The special flags to be transported to all linked computers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(SpecialClass data) +{ + ID = Houses.ID(PlayerPtr); + Type = SPECIAL; + Frame = ::Frame; + Data.Options.Data = data; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct simple target type event. * + * * + * This will construct a generic event that needs only a target parameter. The actual * + * event and target values are specified as parameters. * + * * + * INPUT: type -- The event type to construct. * + * * + * target-- The target value that this event is to apply to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TARGET target) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Target.Whom = target; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Default constructor for event objects. * + * * + * This constructs a simple event object that requires no parameters other than the * + * type of event it is. * + * * + * INPUT: type -- The type of event to construct. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for general-purpose-data events. * + * * + * INPUT: type -- The type of event to construct. * + * val -- data value * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int val) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Data.General.Value = val; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for navigation computer events. * + * * + * Constructor for events that are used to assign the navigation computer. * + * * + * INPUT: type -- The type of event (this constructor can be used by other navigation * + * type events). * + * * + * src -- The object that the event should apply to. * + * * + * dest -- The destination (or target) that the event needs to complete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TARGET src, TARGET dest) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.NavCom.Whom = src; + Data.NavCom.Where = dest; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Event for sequencing animations. * + * * + * This constructor is used for animations that must be created through the event system. * + * * + * INPUT: anim -- The animation that will be created. * + * * + * coord -- The location where the animation is to be created. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord) +{ + ID = Houses.ID(PlayerPtr); + Type = ANIMATION; + Frame = ::Frame; + Data.Anim.What = anim; + Data.Anim.Owner = owner; + Data.Anim.Where = coord; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TARGET src, MissionType mission, TARGET target, TARGET destination) +{ + ID = Houses.ID(PlayerPtr); + Type = MEGAMISSION; + Frame = ::Frame; + Data.MegaMission.Whom = src; + Data.MegaMission.Mission = mission; + Data.MegaMission.Target = target; + Data.MegaMission.Destination = destination; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for sidebar build events. * + * * + * This constructor is used for events that deal with an object type and an object ID. * + * Typically, this is used exclusively by the sidebar. * + * * + * INPUT: type -- The event type of this object. * + * * + * object -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, int id) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Specific.Type = object; + Data.Specific.ID = id; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * * + * This constructor is used for those events that have an object type and associated cell. * + * Typically, this is for building placement after construction has completed. * + * * + * INPUT: type -- The event type for this object. * + * * + * object -- The object type number (actual object is probably inferred from the * + * sidebar data). * + * * + * cell -- The cell location where this event is to occur. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, CELL cell) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Place.Type = object; + Data.Place.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct an id and cell based event. * + * * + * This constructor is used for those events that require an ID number and a cell location. * + * * + * INPUT: type -- The event type this will be. * + * * + * id -- The arbitrary id number to assign. * + * * + * cell -- The location for this event. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int id, CELL cell) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Special.ID = id; + Data.Special.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::Execute -- Execute a queued command. * + * * + * This routine executes an event. The even must already have been confirmed by any * + * remote machine before calling this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void EventClass::Execute(void) +{ + TechnoClass * techno; + AnimClass * anim = 0; + HouseClass * house = 0; + char txt[80]; + int i; +//#if (0) +if (Type < 0 || Type > PROCESS_TIME){ +char tempbuf[128]; +sprintf (tempbuf, "Packet type %d received\n", Type); +CCDebugString (tempbuf); + +sprintf (tempbuf, " ID = %d\n", ID); +CCDebugString (tempbuf); + +sprintf (tempbuf, " Frame = %d\n", Frame); +CCDebugString (tempbuf); + +sprintf (tempbuf, " MPlayer ID = %d\n", MPlayerID); +CCDebugString (tempbuf); + +} +//#endif //(0) + + + switch (Type) { + /* + ** Update the archive target for this building. + */ + case ARCHIVE: + techno = As_Techno(Data.NavCom.Whom); + if (techno && techno->IsActive) { + techno->ArchiveTarget = Data.NavCom.Where; + } + break; + + /* + ** Make or break alliance. + */ + case ALLY: + house = Houses.Raw_Ptr(Data.General.Value); + if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { + Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); + } else { + Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); + } + break; + + /* + ** Special self destruct action requested. This is active in the multiplayer mode. + */ + case DESTRUCT: +CCDebugString ("C&C95 - Resignation packet received\n"); + Houses.Raw_Ptr(ID)->Flag_To_Die(); + Houses.Raw_Ptr(ID)->Resigned = true; + break; + + /* + ** Update the special control flags. This is necessary so that in a multiplay + ** game, all machines will agree on the rules. If these options change during + ** game play, then all players are informed that options have changed. + */ + case SPECIAL: + { + Special = Data.Options.Data; + HouseClass * house = Houses.Raw_Ptr(ID); + + sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); + Messages.Add_Message(txt, MPlayerTColors[house->RemapColor], + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200, 0, 0); + Map.Flag_To_Redraw(false); + } + break; + + /* + ** Starts or stops repair on the specified object. This event is triggered by the + ** player clicking the repair wrench on a building. + */ + case REPAIR: +CCDebugString ("C&C95 - Repair packet received\n"); + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive) { + techno->Repair(-1); + } + break; + + /* + ** Tells a building/unit to sell. This event is triggered by the player clicking the + ** sell animating cursor over the building or unit. + */ + case SELL: +CCDebugString ("C&C95 - Sell packet received\n"); + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { + techno->Sell_Back(-1); + } else { + if (Is_Target_Cell(Data.Target.Whom)) { + Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); + } + } + break; + + /* + ** This even is used to trigger an animation that is generated as a direct + ** result of player intervention. + */ + case ANIMATION: + anim = new AnimClass(Data.Anim.What, Data.Anim.Where); + if (anim) { + //2019/09/19 JAS - Visibility needs to be determined per player + if (Data.Anim.What != ANIM_MOVE_FLASH || Data.Anim.Owner == HOUSE_NONE || Special.IsVisibleTarget) + { + anim->Set_Visible_Flags(0xffff); + } + else + { + anim->Set_Visible_Flags(1 << Data.Anim.Owner); + } + } + break; + + /* + ** This event will place the specified object at the specified location. + ** The event is used to place newly constructed buildings down on the map. The + ** object type is specified. From this object type, the house can determine the + ** exact factory and real object pointer to use. + */ + case PLACE: +CCDebugString ("C&C95 - Place packet received\n"); + Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); + break; + + /* + ** This event starts production of the speicified object type. The house can + ** determine from the type and ID value, what object to begin production on and + ** what factory to use. + */ + case PRODUCE: +CCDebugString ("C&C95 - Produce packet received\n"); + Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); + break; + + /* + ** This event is generated when the player puts production on hold. From the + ** object type, the factory can be inferred. + */ + case SUSPEND: +CCDebugString ("C&C95 - Suspend packet received\n"); + Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); + break; + + /* + ** This event is generated when the player cancels production of the specified + ** object type. From the object type, the exact factory can be inferred. + */ + case ABANDON: +CCDebugString ("C&C95 - Abandon packet received\n"); + Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); + break; + + /* + ** Toggles the primary factory state of the specified building. + */ + case PRIMARY:{ +CCDebugString ("C&C95 - Primary building packet received\n"); + BuildingClass * building = As_Building(Data.Target.Whom); + if (building && building->IsActive) { + building->Toggle_Primary(); + } + } + break; + + /* + ** This is the general purpose mission control event. Most player + ** action routes through this event. It sets a unit's mission, TarCom, + ** and NavCom to the values specified. + */ + case MEGAMISSION: + techno = As_Techno(Data.MegaMission.Whom); + if (techno && techno->IsActive) { + + /* + ** Fetch a pointer to the object of the mission. + */ + ObjectClass * object; + if (Target_Legal(Data.MegaMission.Target)) { + object = As_Object(Data.MegaMission.Target); + } else { + object = As_Object(Data.MegaMission.Destination); + } + + /* + ** Break any existing team contact, since it is now invalid. + */ + if (!techno->IsTethered) { + techno->Transmit_Message(RADIO_OVER_OUT); + } + switch (techno->What_Am_I()) { + case RTTI_INFANTRY: + case RTTI_UNIT: + if (((FootClass *)techno)->Team) { + ((FootClass *)techno)->Team->Remove((FootClass *)techno); + } + break; + } + + if (object) { + + // 2019/09/20 JAS - Added record of who clicked on the object + HouseClass* house = Houses.Raw_Ptr(ID); + bool is_allied = house != nullptr && house->Is_Ally(techno); + if (is_allied || Special.IsVisibleTarget) { + object->Clicked_As_Target((HousesType)ID); + } + } + techno->Assign_Mission(Data.MegaMission.Mission); + + /* + ** Guard area mode is handled with care. The specified target is actually + ** assigned as the location that should be guarded. In addition, the + ** movement destination is immediately set to this new location. + */ + if (Data.MegaMission.Mission == MISSION_GUARD_AREA && +// Target_Legal(Data.MegaMission.Target) && + (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_AIRCRAFT)) { + + techno->ArchiveTarget = Data.MegaMission.Target; + techno->Assign_Target(TARGET_NONE); + techno->Assign_Destination(Data.MegaMission.Target); + } else { + techno->Assign_Target(Data.MegaMission.Target); + techno->Assign_Destination(Data.MegaMission.Destination); + } + +#ifdef NEVER + if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && + Data.MegaMission.Mission == MISSION_GUARD_AREA) { + + techno->ArchiveTarget = Data.MegaMission.Destination; + } +#endif + } + break; + + /* + ** Request that the unit/infantry/aircraft go into idle mode. + */ + case IDLE: + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + techno->Assign_Destination(TARGET_NONE); + techno->Assign_Target(TARGET_NONE); + techno->Enter_Idle_Mode(); + } + break; + + /* + ** Request that the unit/infantry/aircraft scatter from its current location. + */ + case SCATTER: + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + techno->Scatter(0, true); + } + break; + + /* + ** If we are placing down the ion cannon blast then lets take + ** care of it. + */ + case SPECIAL_PLACE: +CCDebugString ("C&C95 - Special blast packet received\n"); + Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); + break; + + /* + ** Exit the game. + ** Give parting message while palette is fading to black. + */ + case EXIT: +CCDebugString ("C&C95 - Exit game packet received\n"); + Theme.Queue_Song(THEME_NONE); + Stop_Speaking(); + Speak(VOX_CONTROL_EXIT); + while (Is_Speaking()) { + Call_Back(); + } + GameActive = false; + break; + + /* + ** Process the options menu. + */ + case OPTIONS: + SpecialDialog = SDLG_OPTIONS; + break; + + /* + ** Process the options Game Speed + */ + case GAMESPEED: +CCDebugString ("C&C95 - Game speed packet received\n"); + Options.GameSpeed = Data.General.Value; + break; + + /* + ** Adjust connection timing for multiplayer games + */ + case RESPONSE_TIME: +char flip[128]; +sprintf (flip, "C&C95 - Changing MaxAhead to %d frames\n", Data.FrameInfo.Delay); +CCDebugString (flip); + MPlayerMaxAhead = Data.FrameInfo.Delay; + break; + + // + // This event tells all systems to use new timing values. It's like + // RESPONSE_TIME, only it works. It's only used with the + // COMM_MULTI_E_COMP protocol. + // + case TIMING: +CCDebugString ("C&C95 - Timing packet received\n"); +//#if(TIMING_FIX) + // + // If MaxAhead is about to increase, we're vulnerable to a Packet- + // Received-Too-Late error, if any system generates an event after + // this TIMING event, but before it executes. So, record the + // period of vulnerability's frame start & end values, so we + // can reschedule these events to execute after it's over. + // + if (Data.Timing.MaxAhead > MPlayerMaxAhead) { + NewMaxAheadFrame1 = Frame; + NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; + } +//#endif + + DesiredFrameRate = Data.Timing.DesiredFrameRate; + MPlayerMaxAhead = Data.Timing.MaxAhead; + +sprintf (flip, "C&C95 - Timing packet: DesiredFrameRate = %d\n", Data.Timing.DesiredFrameRate); +CCDebugString (flip); +sprintf (flip, "C&C95 - Timing packet: MaxAhead = %d\n", Data.Timing.MaxAhead); +CCDebugString (flip); + + /* + ** If spawned from WChat then we should be getting poked every minute. If not then + ** deliberately break the max ahead value + */ + if (Special.IsFromWChat){ + MPlayerMaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); +//if (DDEServer.Time_Since_Heartbeat() >= 70*60) CCDebugString ("C&C95 - Missed a heartbeat\n"); + } + break; + + // + // This event tells all systems what the other systems' process + // timing requirements are; it's used to compute a desired frame rate + // for the game. + // + case PROCESS_TIME: + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID == ::MPlayerID[i]) { + TheirProcessTime[i] = Data.ProcessTime.AverageTicks; + +char flip[128]; +sprintf (flip, "C&C95 - Received PROCESS_TIME packet of %04x ticks\n", Data.ProcessTime.AverageTicks); +CCDebugString (flip); + + break; + } + } + break; + + /* + ** Default: do nothing. + */ + default: + break; + } +} diff --git a/TIBERIANDAWN/EVENT.H b/TIBERIANDAWN/EVENT.H new file mode 100644 index 000000000..b880d877a --- /dev/null +++ b/TIBERIANDAWN/EVENT.H @@ -0,0 +1,224 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\event.h_v 2.19 16 Oct 1995 16:46:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EVENT_H +#define EVENT_H + +/* +** This event class is used to contain all external game events (things that the player can +** do at any time) so that these events can be transported between linked computers. This +** encapsulation is required in order to ensure that each event affects all computers at the +** same time (same game frame). +*/ +class EventClass +{ + public: + + /* + ** All external events are identified by these labels. + */ + typedef enum EventType : unsigned char { + EMPTY, + + ALLY, // Make allie of specified house. + MEGAMISSION, // Full change of mission with target and destination. + IDLE, // Request to enter idle mode. + SCATTER, // Request to scatter from current location. + DESTRUCT, // Self destruct request (surrender action). + DEPLOY, // MCV is to deploy at current location. + PLACE, // Place building at location specified. + OPTIONS, // Bring up options screen. + GAMESPEED, // Set game speed + PRODUCE, // Start or Resume production. + SUSPEND, // Suspend production. + ABANDON, // Abandon production. + PRIMARY, // Primary factory selected. + SPECIAL_PLACE, // Special target location selected + EXIT, // Exit game. + ANIMATION, // Flash ground as movement feedback. + REPAIR, // Repair specified object. + SELL, // Sell specified object. + SPECIAL, // Special options control. + + // Private events. + FRAMESYNC, // Game-connection packet; includes Scenario CRC & sender's frame # + // Used to initiate game connection phase & to reconnect; + // When one of these is received, the receiver knows there are + // no associated commands in this packet. + MESSAGE, // Message to another player (The message is the 40 bytes + // after the event class). + RESPONSE_TIME, // use a new propogation delay value + FRAMEINFO, // Game-heartbeat packet; includes Game CRC & command count + // All packets sent for a frame are prefixed with one of these + ARCHIVE, // Updates archive target on specified object. + TIMING, // new timing values for all systems to use + PROCESS_TIME, // a system's average processing time, in ticks per frame + LAST_EVENT, // one past the last event + } EventType; + + EventType Type; // Type of queue command object. + + /* + ** 'Frame' is the frame that the command should execute on. + ** 27 bits gives over 25 days of playing time without wrapping, + ** at 30 frames per second, so it should be plenty! + */ + unsigned Frame : 27; + + /* + ** House index of the player originating this event + */ + unsigned ID : 4; + + /* + ** This bit tells us if we've already executed this event. + */ + unsigned IsExecuted: 1; + + /* + ** Multiplayer ID of the player originating this event. + ** High nybble: the color index of this player. + ** Low nybble: the HousesType this player is "acting like" (GDI/NOD) + */ + unsigned char MPlayerID; + + /* + ** This union contains the specific data that the event requires. + */ + union { + struct { + SpecialClass Data; // The special option flags. + } Options; + struct { + TARGET Whom; // The object to apply the event to. + } Target; + struct { + AnimType What; // The animation to create. + HousesType Owner; // The owner of the animation (when it matters). + COORDINATE Where; // The location to place the animation. + } Anim; + struct { + int Value; // general-purpose data + } General; + struct { + TARGET Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + TARGET Target; // Target to assign. + TARGET Destination;// Destination to assign. + } MegaMission; + struct { + TARGET Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + } Mission; + struct { + TARGET Whom; // Whom to apply movement change to. + TARGET Where; // Where to set NavCom to. + } NavCom; + struct { + TARGET Whom; // Whom to apply attack change to. + TARGET Target; // What to set TarCom to. + } TarCom; + struct { + RTTIType Type; + int ID; + } Specific; + struct { + RTTIType Type; + CELL Cell; + } Place; + struct { + int ID; + CELL Cell; + } Special; + /* + ** This structure is used for FRAMEINFO, FRAMESYNC, and RESPONSE_TIME + ** events; exactly one of these will be sent each frame, whether there's + ** data that frame or not. + ** CRC: the game CRC when this packet was generated; used to detect sync errors + ** CommandCount: # of commands the sender has sent; used to detect missed packets + ** Delay: sender's propogation delay value for this frame + */ + struct { + unsigned long CRC; + unsigned short CommandCount; // # commands sent so far + unsigned char Delay; // propogation delay used this frame + // (Frame - Delay = sender's current frame #) + } FrameInfo; + + // + // This structure sets new timing values for all systems in a multiplayer + // game. This structure replaces the RESPONSE_TIME event for + // the COMM_MULTI_E_COMP protocol. + // + struct { + unsigned short DesiredFrameRate; + unsigned short MaxAhead; + } Timing; + + // + // This structure is transmitted by all systems, and is used to compute + // the "desired" frame rate for the game. + // + struct { + unsigned short AverageTicks; + } ProcessTime; + + } Data; + + //-------------- Functions --------------------- + EventClass(void) {Type = EMPTY;}; + EventClass(SpecialClass data); + EventClass(EventType type, TARGET target); + EventClass(EventType type); + EventClass(EventType type, int val); + EventClass(EventType type, TARGET src, TARGET dest); +// EventClass(TARGET src, MissionType mission); + EventClass(TARGET src, MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + EventClass(EventType type, RTTIType object, int id); + EventClass(EventType type, RTTIType object, CELL cell); + EventClass(EventType type, int id, CELL cell); + EventClass(AnimType anim, HousesType owner, COORDINATE coord); + + // Process the event. + void Execute(void); + + int operator == (EventClass & q) { + return memcmp(this, &q, sizeof(q)) == 0; + }; + + static unsigned char EventLength[LAST_EVENT]; + static char * EventNames[LAST_EVENT]; +}; + +#endif diff --git a/TIBERIANDAWN/EXPAND.CPP b/TIBERIANDAWN/EXPAND.CPP new file mode 100644 index 000000000..ec92ab090 --- /dev/null +++ b/TIBERIANDAWN/EXPAND.CPP @@ -0,0 +1,420 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header$ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXPAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/03/95 * + * * + * Last Update : November 3, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef NEWMENU + +bool Expansion_Present(void) +{ + CCFileClass file("EXPAND.DAT"); + + return(file.Is_Available()); +} + + + + +class EListClass : public ListClass +{ + public: + EListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ListClass(id, x, y, w, h, flags, up, down) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; + + +void EListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index]+sizeof(int), x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index]+sizeof(int), x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} + +bool Expansion_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int option_width = 236 * factor; + int option_height = 162 * factor; + int option_x = (320*factor - option_width) /2; + int option_y = (200*factor - option_height) /2; + + GadgetClass * buttons = NULL; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+25*factor, option_y+option_height-15*factor); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+option_width-50*factor, option_y+option_height-15*factor); + EListClass list(202, option_x+10*factor, option_y+20*factor, option_width-20*factor, option_height-40*factor, TPF_6PT_GRAD|TPF_NOSHADOW, up_button, down_button); + + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + + /* + ** Add in all the expansion scenarios. + */ + char * sbuffer = (char*)_ShapeBuffer; + int index; + for (index = 20; index < 60; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_GDI, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + file.Read(sbuffer, 1000); + sbuffer[1000] = '\r'; + sbuffer[1000+1] = '\n'; + sbuffer[1000+2] = '\0'; + + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "GDI: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + for (index = 20; index < 60; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_NOD, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + file.Read(sbuffer, 1000); + sbuffer[1000] = '\r'; + sbuffer[1000+1] = '\n'; + sbuffer[1000+2] = '\0'; + + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "NOD: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + while (process) { + + Call_Back(); + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_MISSION_DESCRIPTION, option_x, option_y, option_width); + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_RETURN: + case 200|KN_BUTTON: + if (list.Current_Item()[sizeof(int)] == 'G') { + ScenPlayer = SCEN_PLAYER_GDI; + } else { + ScenPlayer = SCEN_PLAYER_NOD; + } + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = false; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (index = 0; index < list.Count(); index++) { + delete [] (char *)list.Get_Item(index); + } + + return(okval); +} + + + + + + + + + + + + +/*********************************************************************************************** + * Bonus_Dialog -- Asks the user which bonus mission he wants to play * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/26/97 11:07AM ST : Created * + *=============================================================================================*/ +bool Bonus_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int option_width = 236 * factor; + int option_height = 162 * factor; + int option_x = (320*factor - option_width) /2; + int option_y = (200*factor - option_height) /2; + + GadgetClass * buttons = NULL; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+25*factor, option_y+option_height-15*factor); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+option_width-50*factor, option_y+option_height-15*factor); + EListClass list(202, option_x+10*factor, option_y+20*factor, option_width-20*factor, option_height-40*factor, TPF_6PT_GRAD|TPF_NOSHADOW, up_button, down_button); + + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + + /* + ** Add in all the expansion scenarios. + */ + char * sbuffer = (char*)_ShapeBuffer; + int gdi_scen_names[3]={ + TXT_BONUS_MISSION_1, + TXT_BONUS_MISSION_2, + TXT_BONUS_MISSION_3 + }; + + int nod_scen_names[2]={ + TXT_BONUS_MISSION_4, + TXT_BONUS_MISSION_5 + }; + + int index; + for (index = 60; index < 63; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_GDI, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + memcpy (buffer, Text_String (gdi_scen_names[index-60]), 1+strlen(Text_String (gdi_scen_names[index-60]))); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "GDI: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + for (index = 60; index < 62; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_NOD, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + memcpy (buffer, Text_String (nod_scen_names[index-60]), 1+strlen(Text_String (nod_scen_names[index-60]))); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "NOD: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + while (process) { + + Call_Back(); + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_BONUS_MISSIONS, option_x, option_y, option_width); + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_RETURN: + case 200|KN_BUTTON: + if (list.Current_Item()[sizeof(int)] == 'G') { + ScenPlayer = SCEN_PLAYER_GDI; + } else { + ScenPlayer = SCEN_PLAYER_NOD; + } + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = false; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (index = 0; index < list.Count(); index++) { + delete [] (char *)list.Get_Item(index); + } + + return(okval); +} + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/EXTERNS.H b/TIBERIANDAWN/EXTERNS.H new file mode 100644 index 000000000..0e84ea60e --- /dev/null +++ b/TIBERIANDAWN/EXTERNS.H @@ -0,0 +1,407 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\externs.h_v 2.15 16 Oct 1995 16:45:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EXTERNS_H +#define EXTERNS_H + +#include "cell.h" + +#ifdef SCENARIO_EDITOR +#include "mapedit.h" +#endif +#include "techno.h" +#include "type.h" +#include "building.h" +#include "unit.h" +#include "credits.h" +#include "goptions.h" +#include "options.h" +#include "infantry.H" + +#ifdef JAPANESE +extern bool ForceEnglish; +#endif + +extern bool Debug_Quiet; +extern bool Debug_Cheat; +extern bool Debug_Remap; +extern bool Debug_Flag; +extern bool Debug_Lose; +extern bool Debug_Map; +extern bool Debug_Win; +extern bool Debug_Icon; +extern bool Debug_Passable; +extern bool Debug_Unshroud; +extern bool Debug_Threat; +extern bool Debug_Find_Path; +extern bool Debug_Check_Map; +extern bool Debug_Playtest; + +extern bool Debug_Heap_Dump; +extern bool Debug_Smart_Print; +extern bool Debug_Trap_Check_Heap; +extern bool Debug_Instant_Build; +extern bool Debug_Force_Crash; + +extern void const *WarFactoryOverlay; + + +/* +** Dynamic global variables (these change or are initialized at run time). +*/ +#ifdef PATCH +extern bool IsV107; +extern char OverridePath[128]; +#endif +extern bool SlowPalette; +extern char VersionText[16]; +extern bool ScoresPresent; +extern int CrateCount; +extern TCountDownTimerClass CrateTimer; +extern bool CrateMaker; +extern ThemeType TransitTheme; +extern bool AllowVoice; +extern NewConfigType NewConfig; +extern char BriefingText[512]; +extern char IntroMovie[_MAX_FNAME+_MAX_EXT]; +extern char ActionMovie[_MAX_FNAME+_MAX_EXT]; +extern char BriefMovie[_MAX_FNAME+_MAX_EXT]; +extern char WinMovie[_MAX_FNAME+_MAX_EXT]; +extern char WinMovie2[_MAX_FNAME + _MAX_EXT]; +extern char WinMovie3[_MAX_FNAME + _MAX_EXT]; +extern char WinMovie4[_MAX_FNAME + _MAX_EXT]; +extern char LoseMovie[_MAX_FNAME+_MAX_EXT]; +extern char MovieThemeName[_MAX_FNAME + _MAX_EXT]; +extern VoxType SpeakQueue; +extern bool PlayerWins; +extern bool PlayerLoses; +extern bool PlayerRestarts; +extern StructType SabotagedType; +extern bool TempleIoned; +extern long Frame; +extern void * SpeechBuffer; +extern int PreserveVQAScreen; +extern bool BreakoutAllowed; +extern bool Brokeout; +extern CELL Views[4]; + +extern GameOptionsClass Options; + +extern LogicClass Logic; +#ifdef SCENARIO_EDITOR +extern MapEditClass Map; +#else +extern MouseClass Map; +#endif +extern ScoreClass Score; +extern MonoClass MonoArray[MonoClass::MAX_MONO_PAGES]; +extern MixFileClass * ScoreMix; +extern MixFileClass * TheaterData; +extern MixFileClass * LowTheaterData; +extern MixFileClass * MoviesMix; +extern MixFileClass * GeneralMix; +extern ThemeClass Theme; +extern SpecialClass Special; +extern RulesClass Rule; + +/* +** Game object allocation and tracking classes. +*/ +extern TFixedIHeapClass Units; +extern TFixedIHeapClass Factories; +extern TFixedIHeapClass Terrains; +extern TFixedIHeapClass Templates; +extern TFixedIHeapClass Smudges; +extern TFixedIHeapClass Overlays; +extern TFixedIHeapClass Infantry; +extern TFixedIHeapClass Bullets; +extern TFixedIHeapClass Buildings; +extern TFixedIHeapClass Anims; +extern TFixedIHeapClass Aircraft; +extern TFixedIHeapClass Triggers; +extern TFixedIHeapClass TeamTypes; +extern TFixedIHeapClass Teams; +extern TFixedIHeapClass Houses; + +extern QueueClass OutList; +extern QueueClass DoList; + +typedef DynamicVectorArrayClass SelectedObjectsType; +extern SelectedObjectsType CurrentObject; +extern DynamicVectorClass CellTriggers; +extern DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + +extern CELL Waypoint[WAYPT_COUNT]; + +extern BaseClass Base; + +/* +** Loaded data file pointers. +*/ +extern void const * Green12FontPtr; +extern void const * Green12GradFontPtr; +extern void const * MapFontPtr; +extern void const * VCRFontPtr; +extern void const * Font3Ptr; +extern void const * Font6Ptr; +extern void const * Font8Ptr; +extern void const * FontLEDPtr; +extern void const * ScoreFontPtr; +extern void const * GradFont6Ptr; +extern char const * SystemStrings; + +/* +** Miscellaneous globals. +*/ +extern HousesType Whom; +//extern _VQAConfig AnimControl; +extern long SpareTicks; +extern int MonoPage; +extern unsigned char * OriginalPalette; +extern int EndCountDown; +extern bool GameActive; +extern bool SpecialFlag; +extern int ScenarioInit; +extern long TutorFlags[2]; +extern HouseClass * PlayerPtr; +extern unsigned char * BlackPalette; +extern unsigned char * WhitePalette; +extern unsigned char * GamePalette; +extern unsigned Scenario; +extern ScenarioPlayerType ScenPlayer; +extern ScenarioDirType ScenDir; +extern ScenarioVarType ScenVar; +extern int CarryOverMoney; +extern int CarryOverCap; +extern int CarryOverPercent; +extern char ScenarioName[_MAX_FNAME+_MAX_EXT]; +extern unsigned BuildLevel; +extern unsigned long ScenarioCRC; + +#ifdef SCENARIO_EDITOR +extern CELL CurrentCell; +#endif + +extern GameType GameToPlay; + +extern CommProtocolType CommProtocol; + +extern CCFileClass RecordFile; +extern int RecordGame; +extern int SuperRecord; +extern int PlaybackGame; +extern int AllowAttract; + +extern GetCDClass CDList; + +/* +** Modem globals +*/ +extern bool ModemService; +//extern NullModemClass NullModem; +//extern DynamicVectorClass PhoneBook; +extern int CurPhoneIdx; +extern DynamicVectorClass InitStrings; +extern SerialSettingsType SerialDefaults; +extern ModemGameType ModemGameToPlay; +extern char * DialMethodCheck[ DIAL_METHODS ]; +extern char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + +/* +** Network/Modem globals +*/ +extern int ScenarioIdx; +extern int ColorUsed[]; +extern char MPlayerName[MPLAYER_NAME_MAX]; +extern int MPlayerGColors[]; +extern int MPlayerTColors[]; +extern char MPlayerDescriptions[100][40]; +extern DynamicVectorClass MPlayerScenarios; +extern DynamicVectorClass MPlayerFilenum; +extern int MPlayerMax; +extern int MPlayerPrefColor; +extern int MPlayerColorIdx; +extern HousesType MPlayerHouse; +extern unsigned char MPlayerLocalID; +extern int MPlayerCount; +extern int MPlayerBases; +extern int MPlayerCredits; +extern int MPlayerTiberium; +extern int MPlayerGoodies; +extern int MPlayerGhosts; +extern int MPlayerSolo; +extern int MPlayerUnitCount; +extern int MPlayerCountMin[2]; +extern int MPlayerCountMax[2]; +extern unsigned long MPlayerMaxAhead; +extern unsigned long FrameSendRate; +extern unsigned char MPlayerID[MAX_PLAYERS]; +extern HousesType MPlayerHouses[MAX_PLAYERS]; +extern char MPlayerNames [MAX_PLAYERS][MPLAYER_NAME_MAX]; +extern MessageListClass Messages; +extern IPXAddressClass MessageAddress; +extern char LastMessage[MAX_MESSAGE_LENGTH]; +extern int MPlayerBlitz; +extern int MPlayerObiWan; +extern MPlayerScoreType MPlayerScore[MAX_MULTI_NAMES]; +extern int MPlayerGamesPlayed; +extern int MPlayerNumScores; +extern int MPlayerWinner; +extern int MPlayerCurGame; + +extern int TheirProcessTime[MAX_PLAYERS - 1]; +extern int DesiredFrameRate; + +extern char * GlobalPacketNames[]; +extern char * SerialPacketNames[]; + +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +extern long TrapFrame; +extern RTTIType TrapObjType; +extern TrapObjectType TrapObject; +extern COORDINATE TrapCoord; +extern void * TrapThis; +extern CellClass * TrapCell; +extern int TrapCheckHeap; + +/* +** Network (IPX) globals +*/ +extern IPXManagerClass Ipx; +extern int IsBridge; +extern IPXAddressClass BridgeNet; +extern bool NetMaster; +extern bool NetStealth; +extern bool NetProtect; +extern bool NetOpen; +extern char MPlayerGameName[MPLAYER_NAME_MAX]; +extern GlobalPacketType GPacket; +extern int GPacketlen; +extern IPXAddressClass GAddress; +extern unsigned short GProductID; +extern char * MetaPacket; +extern int MetaSize; +extern DynamicVectorClass Games; +extern DynamicVectorClass Players; + +extern int Seed; +extern long * RandSeedPtr; +extern int CustomSeed; +extern int NewMaxAheadFrame1; +extern int NewMaxAheadFrame2; + +/* +** Constant externs (data is not modified during game play). +*/ +extern unsigned char const RemapGreen[256]; +extern unsigned char const RemapBlue[256]; +extern unsigned char const RemapOrange[256]; +extern unsigned char const RemapNone[256]; +extern unsigned char const RemapGold[256]; +extern unsigned char const RemapRed[256]; +extern unsigned char const RemapLtBlue[256]; +extern WeaponTypeClass const Weapons[WEAPON_COUNT]; +extern WarheadTypeClass const Warheads[WARHEAD_COUNT]; +extern char const * SourceName[SOURCE_COUNT]; +extern GroundType const Ground[LAND_COUNT]; +extern TheaterDataType const Theaters[THEATER_COUNT]; +extern unsigned char const Facing32[256]; +extern unsigned char const Facing8[256]; +extern unsigned char const Pixel2Lepton[24]; +extern COORDINATE const StoppingCoordAbs[5]; +extern CELL const AdjacentCell[FACING_COUNT]; +extern COORDINATE const AdjacentCoord[FACING_COUNT]; + +extern int SoundOn; +//extern GraphicBufferClass SeenPage; +extern GraphicBufferClass VisiblePage; +extern GraphicBufferClass HiddenPage; +extern GraphicViewPortClass SeenBuff; +extern GraphicBufferClass ModeXBuff; +extern GraphicViewPortClass HidPage; +extern GraphicBufferClass LoResHidPage; +extern GraphicBufferClass SysMemPage; +extern int MenuList[][8]; +extern CountDownTimerClass FrameTimer; +extern CountDownTimerClass CountDownTimer; + +extern TimerClass ProcessTimer; +extern int ProcessTicks; +extern int ProcessFrames; + +extern SpecialDialogType SpecialDialog; +//extern bool IsFindPath; + +extern char *DebugFname; // for stoopid debugging purposes +extern int DebugLine; // for stoopid debugging purposes +extern int RequiredCD; +extern int MouseInstalled; +extern int AreThingiesEnabled; + +extern WWKeyboardClass Kbd; +extern int In_Debugger; +extern WWMouseClass *WWMouse; +extern HANDLE hInstance; +extern int AllDone; +extern "C" bool MMXAvailable; +extern int Get_CD_Index (int cd_drive, int timeout); +void Memory_Error_Handler(void); +extern bool GameStatisticsPacketSent; +extern bool ConnectionLost; +extern bool InMainLoop; // True if in game state rather than menu state +void CCDebugString (char *string); +extern void *PacketLater; +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette); + +extern "C"{ + extern bool IsTheaterShape; +} + +extern void Reset_Theater_Shapes(void); +extern TheaterType LastTheater; + +extern bool ShareAllyVisibility; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/FACING.CPP b/TIBERIANDAWN/FACING.CPP new file mode 100644 index 000000000..eef3edcbb --- /dev/null +++ b/TIBERIANDAWN/FACING.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\facing.cpv 1.9 16 Oct 1995 16:49:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * FacingClass::Set_Current -- Sets the current rotation value. * + * FacingClass::Set_Desired -- Sets the desired facing value. * + * FacingClass::FacingClass -- Default constructor for the facing class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "facing.h" + + +/*********************************************************************************************** + * FacingClass::FacingClass -- Default constructor for the facing class. * + * * + * This default constructor merely sets the desired and current facing values to be the * + * same (North). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +FacingClass::FacingClass(void) +{ + CurrentFacing = DIR_N; + DesiredFacing = DIR_N; +} + + +/*********************************************************************************************** + * FacingClass::Set_Desired -- Sets the desired facing value. * + * * + * This routine is used to set the desired facing value without altering the current * + * facing setting. Typicall use of this routine is when a vehicle needs to face a * + * direction, but currently isn't facing the correct direction. After this routine is * + * called, it is presumed that subsequent calls to Rotation_Adjust() will result in the * + * eventual alignment of the current facing. * + * * + * INPUT: facing -- The new facing to assign to the desired value. * + * * + * OUTPUT: bool; Did the desired facing value actually change by this routine call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Desired(DirType facing) +{ + if (DesiredFacing != facing) { + DesiredFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Set_Current -- Sets the current rotation value. * + * * + * This routine will set the current rotation value. It is used to override the facing * + * value without adjusting the desired setting. * + * * + * INPUT: facing -- The new facing to assign to the current facing value. * + * * + * OUTPUT: bool; Was the current setting changed by this routine. Failure means that the * + * current setting was already at the value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Current(DirType facing) +{ + if (CurrentFacing != facing) { + CurrentFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * * + * This routine is used when the current and desired facings differ but the current * + * facing should be adjusted toward the desired facing. The amount of rotation to adjust * + * is provided as a rotation rate parameter. Typical use of this routine is for turrets * + * and other vehicle related rotating. * + * * + * INPUT: rate -- The rotation rate to use when adjusting the current facing toward the * + * desired facing. A value of 127 means instantaneous rotation. * + * * + * OUTPUT: bool; Did the rotation result in the current facing transitioning from one * + * 1/32 zone to another? If true, then the owning object most likely will * + * need to be redrawn to reflect the change. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Rotation_Adjust(int rate) +{ + /* + ** Only perform the rotation adjustment if the desired facing is not the + ** same as the current facing. + */ + if (Is_Rotating()) { + rate = MIN(rate, 127); + + DirType oldfacing = CurrentFacing; + int diff = Difference(); + + /* + ** If the allowed facing change is greater than the difference between + ** the current facing and the desired facing, then just snap the + ** facing to the new value. + */ + if (ABS(diff) < rate) { + CurrentFacing = DesiredFacing; + } else { + + /* + ** Adjust the current facing clockwise or counterclockwise depending + ** on the shortest distance to the desired facing from the current + ** facing. + */ + if (diff < 0) { + CurrentFacing = (DirType)(CurrentFacing - (DirType)rate); + } else { + CurrentFacing = (DirType)(CurrentFacing + (DirType)rate); + } + } + + /* + ** If this facing adjustment caused the current facing to rotate into a + ** new 1/32 rotation zone (likely to cause a redraw), then return + ** this fact with a true value. + */ + return(Facing_To_32(CurrentFacing) != Facing_To_32(oldfacing)); + } + return(false); +} diff --git a/TIBERIANDAWN/FACING.H b/TIBERIANDAWN/FACING.H new file mode 100644 index 000000000..afbbacfcd --- /dev/null +++ b/TIBERIANDAWN/FACING.H @@ -0,0 +1,76 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\facing.h_v 1.14 16 Oct 1995 16:46:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACING_H +#define FACING_H + +/* +** This is a general facing handler class. It is used in those cases where facing needs to be +** kept track of, but there could also be an associated desired facing. The current facing +** is supposed to transition to the desired state over time. Using this class facilitates this +** processing as well as isolating the rest of the code from the internals. +*/ +class FacingClass +{ + public: + FacingClass(void); + FacingClass(DirType dir) {CurrentFacing = DesiredFacing = dir;}; + operator DirType(void) const {return CurrentFacing;}; + + DirType Current(void) const {return CurrentFacing;}; + DirType Desired(void) const {return DesiredFacing;}; + + int Set_Desired(DirType facing); + int Set_Current(DirType facing); + + void Set(DirType facing) { + Set_Current(facing); + Set_Desired(facing); + }; + + DirType Get(void) const { return CurrentFacing; } + + int Is_Rotating(void) const {return (DesiredFacing != CurrentFacing);}; + + int Difference(void) const {return (signed char)(*((unsigned char*)&DesiredFacing) - *((unsigned char*)&CurrentFacing));}; + int Difference(DirType facing) const {return (signed char)(*((signed char*)&facing) - *((signed char*)&CurrentFacing));}; + int Rotation_Adjust(int rate); + + private: + DirType CurrentFacing; + DirType DesiredFacing; +}; + + +#endif diff --git a/TIBERIANDAWN/FACTORY.CPP b/TIBERIANDAWN/FACTORY.CPP new file mode 100644 index 000000000..b9fbe93b5 --- /dev/null +++ b/TIBERIANDAWN/FACTORY.CPP @@ -0,0 +1,785 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\factory.cpv 2.18 16 Oct 1995 16:51:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FactoryClass::AI -- Process factory production logic. * + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * FactoryClass::Completion -- Fetchs the completion step for this factory. * + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * FactoryClass::Get_Special_Item -- gets factorys spc prod item * + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * FactoryClass::Set -- Assigns a factory to produce an object. * + * FactoryClass::Set -- Fills a factory with an already completed object. * + * FactoryClass::Set -- Force factory to "produce" special object. * + * FactoryClass::Start -- Resumes production after suspension or creation. * + * FactoryClass::Suspend -- Temporarily stop production. * + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * FactoryClass::Validate -- validates factory pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FactoryClass::Validate -- validates factory pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int FactoryClass::Validate(void) const +{ + int num; + + num = Factories.ID(this); + if (num < 0 || num >= FACTORY_MAX) { + Validate_Error("FACTORY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * * + * This brings the factory into a null state. It is called when a factory object is * + * created. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::FactoryClass(void) +{ + IsSuspended = false; + IsDifferent = false; + IsBlocked = false; + Balance = 0; + SpecialItem = SPC_NONE; + Object = NULL; + House = NULL; + Set_Rate(0); + Set_Stage(0); +} + + +/*********************************************************************************************** + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * * + * This cleans up a factory object in preparation for deletion. If there is currently * + * an object in production, it is abandoned and money is refunded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::~FactoryClass(void) +{ + if (GameActive) { + Abandon(); + } +} + + +/*********************************************************************************************** + * FactoryClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the factory list and objects. This routine is typically * + * used in preparation for a new scenario load. All factorys are guaranteed to be eliminated* + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Init(void) +{ + Factories.Free_All(); +} + + +/*********************************************************************************************** + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * * + * This routine allocates a factory from the free factory pool. If there is no more room * + * to allocate a factory, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to the newly allocated factory object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void * FactoryClass::operator new(size_t) +{ + void * ptr = Factories.Allocate(); + if (ptr) { + ((FactoryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * * + * This returns the factory object back to the factory allocation pool. The factory is then * + * available to be allocated. * + * * + * INPUT: ptr -- Pointer to the factory object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::operator delete(void *ptr) +{ + if (ptr) { + ((FactoryClass *)ptr)->IsActive = false; + } + Factories.Free((FactoryClass *)ptr); +} + + +/*********************************************************************************************** + * FactoryClass::AI -- Process factory production logic. * + * * + * This routine should be called once per game tick. It handles the production process. * + * As production proceeds, money is deducted from the owner object's house. When production * + * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * + * required after that point. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 01/04/1995 JLB : Uses exact installment payment method. * + *=============================================================================================*/ +void FactoryClass::AI(void) +{ + Validate(); + if (!IsSuspended && (Object != NULL || SpecialItem)) { + int stages = 1; + + /* + ** Determine the acceleration factor for factory production. + ** This applies only to human players. The computer builds + ** units on a building by building basis -- quantity of building + ** factory types doesn't affect individual factories. + */ + if (Object && House->IsHuman) { + switch (Object->What_Am_I()) { + case RTTI_AIRCRAFT: + stages = House->AircraftFactories; + break; + + case RTTI_INFANTRY: + stages = House->InfantryFactories; + break; + + case RTTI_UNIT: + stages = House->UnitFactories; + break; + + case RTTI_BUILDING: + stages = House->BuildingFactories; + break; + } + stages = MAX(stages, 1); + } + + + for (int index = 0; index < stages; index++) { + if (!Has_Completed() && Graphic_Logic()) { + IsDifferent = true; + + int cost = Cost_Per_Tick(); + + cost = MIN(cost, Balance); + + /* + ** Enough time has expired so that another production step can occur. + ** If there is insufficient funds, then go back one production step and + ** continue the countdown. The idea being that by the time the next + ** production step occurs, there may be sufficient funds available. + */ + if (cost > House->Available_Money()) { + Set_Stage(Fetch_Stage()-1); + } else { + House->Spend_Money(cost); + Balance -= cost; + } + if ( Debug_Instant_Build ) { + Set_Stage(STEP_COUNT); + } + /* + ** If the production has completed, then suspend further production. + */ + if (Fetch_Stage() == STEP_COUNT) { + IsSuspended = true; + Set_Rate(0); + House->Spend_Money(Balance); + Balance = 0; + } + } + } + } +} + + + +/*********************************************************************************************** + * FactoryClass::Force_Complete -- Force the factory to finish what it's building * + * * + * For debugging/testing support * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 8/23/2019 3:54PM ST : Created. * + *=============================================================================================*/ +void FactoryClass::Force_Complete(void) +{ + Validate(); + if (!IsSuspended && (Object != NULL || SpecialItem)) { + Set_Stage(STEP_COUNT); + IsSuspended = true; + Set_Rate(0); + Balance = 0; + IsDifferent = true; + } +} + + +/*********************************************************************************************** + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * * + * Use this routine to determine if production has advanced at least one step. By using * + * this function, intelligent rendering may be performed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the production process advanced one step since the last time this * + * function was called? * + * * + * WARNINGS: This function clears the changed status flag as a side effect. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Changed(void) +{ + Validate(); + bool changed = IsDifferent; + IsDifferent = false; + return(changed); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Assigns a factory to produce an object. * + * * + * This routine initializes a factory to produce the object specified. The desired object * + * type is created and placed in suspended animation (limbo) until such time as production * + * completes. Production is not actually started by this routine. An explicit call to * + * Start() is required to begin production. * + * * + * INPUT: object -- Reference to the object type class that is to be produced. * + * * + * house -- Reference to the owner of the object to be produced. * + * * + * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * + * that the object could not be created. This is catastrophic and in such * + * cases, the factory object should be deleted. * + * * + * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * + * the factory means that the factory is useless and should be deleted. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) +{ + Validate(); + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + Object = (TechnoClass *)object.Create_One_Of(&house); + + if (Object) { + House = Object->House; + Balance = object.Cost_Of() * house.CostBias; + Object->PurchasePrice = Balance; + } + + /* + ** If all was set up successfully, then return true. + */ + return(Object != NULL); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Force factory to "produce" special object. * + * * + * Use this routine to force the factory into special production mode. Such production is * + * used for the ion cannon and other timed special weapon events. * + * * + * INPUT: type -- The special weapon type to begin "production" of. * + * * + * house -- The owner of this production object. * + * * + * OUTPUT: Was the assignment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(int const & type, HouseClass & house) +{ + Validate(); + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + SpecialItem = type; + House = &house; + Balance = 0; + + /* + ** If all was set up successfully, then return true. + */ + return(SpecialItem != SPC_NONE); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Fills a factory with an already completed object. * + * * + * This routine is called when a produced object is in placement mode but then placement * + * is suspended. The object must then return to the factory as if it were newly completed * + * and awaiting removal. * + * * + * INPUT: object -- The object to return to the factory. * + * * + * OUTPUT: none * + * * + * WARNINGS: This will abandon any current object being produced at the factory in order * + * to set the new object into it. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Set(TechnoClass & object) +{ + Validate(); + Abandon(); + Object = &object; + House = Object->House; + Balance = 0; + Set_Rate(0); + Set_Stage(STEP_COUNT); + IsDifferent = true; + IsSuspended = true; +} + + +/*********************************************************************************************** + * FactoryClass::Suspend -- Temporarily stop production. * + * * + * This routine will suspend production until a subsiquent call to Start() or Abandon(). * + * Typical use of this function is when the player puts production on hold or when there * + * is insufficient funds. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * + * factory was empty or production was already stopped (or never started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Suspend(void) +{ + Validate(); + if (!IsSuspended) { + IsSuspended = true; + Set_Rate(0); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Start -- Resumes production after suspension or creation. * + * * + * This function will start the production process. It works for newly created factory * + * objects, as well as if production had been suspended previously. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production started? A false return value means that the factory is * + * empty or there is unsufficient credits to begin production. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Start(void) +{ + Validate(); + if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { + if (House->IsHuman || House->Available_Money() >= Cost_Per_Tick()) { + int time; + + if (Object) { + time = Object->Class_Of().Time_To_Build(House->Class->House); + } else { + time = TICKS_PER_MINUTE * 5; + } + + /* + ** Adjust time according to IQ setting of computer controlled house. The + ** build time will range from double normal time at the slowest to + ** just normal time at the fastest. + */ + if (!House->IsHuman && Rule.Diff[House->Difficulty].IsBuildSlowdown) { + time = (int)((time*Rule.MaxIQ*2.0f) / (House->IQ+Rule.MaxIQ)); + } + + int frac = House->Power_Fraction(); + frac = Bound(frac, 0x0010, 0x0100); + int rate = (time*256) / frac; + + rate /= STEP_COUNT; + rate = Bound(rate, 1, 255); + + Set_Rate(rate); + IsSuspended = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * * + * This routine is used when construction is to be abandoned and current money spend is * + * to be refunded. This function effectively clears out this factory of all record of the * + * producing object so that it may either be deleted or started anew with the Set() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * + * factory was not producing any object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Abandon(void) +{ + Validate(); + if (Object) { + + if (Object) { + /* + ** Refund all money expended so far, back to the owner of the object under construction. + */ + int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; + House->Refund_Money(money - Balance); + Balance = 0; + + /* + ** Delete the object under construction. + */ + ScenarioInit++; + delete Object; + Object = NULL; + ScenarioInit--; + } + if (SpecialItem) { + SpecialItem = SPC_NONE; + } + + /* + ** Set the factory back to the idle and empty state. + */ + Set_Rate(0); + Set_Stage(0); + IsSuspended = true; + IsDifferent = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Completion -- Fetchs the completion step for this factory. * + * * + * Use this routine to determine what animation (or completion step) the factory is * + * currently on. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a completion step number beteen 0 (uncompleted), to STEP_COUNT (completed) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Completion(void) +{ + Validate(); + return(Fetch_Stage()); +} + + +/*********************************************************************************************** + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * * + * Use this routine to examine the factory object in order to determine if the associated * + * object has completed production and is awaiting placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Completed(void) +{ + Validate(); + if (Object && Fetch_Stage() == STEP_COUNT) { + return(true); + } + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * * + * This routine gets the pointer to the currently constructing object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object undergoing construction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * FactoryClass::Get_Object(void) const +{ + Validate(); + return(Object); +} + + +/*************************************************************************** + * FactoryClass::Get_Special_Item -- gets factorys spc prod item * + * * + * INPUT: none * + * * + * OUTPUT: int the item the factory is currently working on * + * * + * HISTORY: * + * 05/05/1995 PWG : Created. * + *=========================================================================*/ +int FactoryClass::Get_Special_Item(void) const +{ + Validate(); + return(SpecialItem); +} + + +/*********************************************************************************************** + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * + * * + * Use this routine to determine the cost per game "tick" to produce the object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of credits necessary to advance production one game tick. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Cost_Per_Tick(void) +{ + Validate(); + if (Object) { + int steps = STEP_COUNT - Fetch_Stage(); + if (steps) { + return(Balance / steps); + } + return(Balance); + } + return(0); +} + + +/*********************************************************************************************** + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * * + * This routine is called after production completes, and the object produced has been * + * placed into the game. It resets the factory for deletion or starting of new production. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * + * any completed object. An immediate second call to this routine will also * + * yield false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Completed(void) +{ + Validate(); + if (Object && Fetch_Stage() == STEP_COUNT) { + Object = NULL; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + SpecialItem = SPC_NONE; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + return(false); +} diff --git a/TIBERIANDAWN/FACTORY.H b/TIBERIANDAWN/FACTORY.H new file mode 100644 index 000000000..f351a82cb --- /dev/null +++ b/TIBERIANDAWN/FACTORY.H @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\factory.h_v 2.17 16 Oct 1995 16:45:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : December 26, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "stage.h" + +class FactoryClass : private StageClass +{ + friend class DLLExportClass; // ST - 1/29/2019 11:04AM + + public: + FactoryClass(void); + ~FactoryClass(void); + static void * operator new(size_t size); + static void operator delete(void *ptr); + + static void Init(void); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Abandon(void); + bool Completed(void); + bool Has_Changed(void); + bool Has_Completed(void); + bool Is_Building(void) const {return(Fetch_Rate() != 0);}; + bool Set(TechnoTypeClass const & object, HouseClass & house); + bool Set(int const & type, HouseClass & house); + bool Start(void); + bool Suspend(void); + int Completion(void); + TechnoClass * Get_Object(void) const; + int Get_Special_Item(void) const; + void AI(void); + void Set(TechnoClass & object); + HouseClass * Get_House(void) {return(House);}; + bool Is_Blocked(void) {return IsBlocked;} + void Set_Is_Blocked(bool set) {IsBlocked = set;} + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** Added for debugging / testing. ST - 8/23/2019 3:52PM + */ + void Force_Complete(void); + + /* + ** This flag is used to maintain the pool of factory class objects. If the object has + ** been allocated, then this flag is true. Otherwise, the object is free to be + ** allocated. + */ + unsigned IsActive:1; + + protected: + enum StepCountEnum { + STEP_COUNT=108 // Number of steps to break production down into. + }; + + int Cost_Per_Tick(void); + + private: + + /* + ** If production is temporarily suspended, then this flag will be true. A factory + ** is suspended when it is first created, when production has completed, and when + ** explicitly instructed to Suspend() production. Suspended production is not + ** abandoned. It may be resumed with a call to Start(). + */ + unsigned IsSuspended:1; + + /* + ** If the AI process detected that the production process has advanced far enough + ** that a change in the building animation would occur, this flag will be true. + ** Examination of this flag (through the Has_Chaged function) allows intelligent + ** updating of any production graphic. + */ + unsigned IsDifferent:1; + + /* + ** The exit from the factory is blocked by something, which means a unit is prevented from exiting after construction + ** has completed. ST - 2/25/2020 11:29AM + */ + unsigned IsBlocked:1; + + /* + ** This records the balance due on the current production item. This value will + ** be reduced as production proceeds. It will reach zero the moment production has + ** finished. Using this method ensures that the total production cost will be EXACT + ** regardless of the number of installment payments that are made. + */ + int Balance; + int OriginalBalance; + + /* + ** This is the object that is being produced. It is held in a state of limbo while + ** undergoing production. Since the object is created at the time production is + ** started, it is always available when production completes. + */ + TechnoClass * Object; + + /* + ** If the factory is not producing an object and is instead producing + ** a special item, then special item will be set. + */ + int SpecialItem; + + /* + ** The factory has to be doing production for one house or another. + ** The house pointer will point to whichever house it is being done + ** for. + */ + HouseClass * House; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/FIELD.CPP b/TIBERIANDAWN/FIELD.CPP new file mode 100644 index 000000000..c9cf696b6 --- /dev/null +++ b/TIBERIANDAWN/FIELD.CPP @@ -0,0 +1,213 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include "field.h" + +// ST - 12/18/2018 10:14AM +#pragma warning(disable:4996) + +FieldClass::FieldClass(char *id, char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, char *data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Convert the variables to host order. This needs to be converted so + // the switch statement does compares on the data that follows. + // + Size = ntohs(Size); + + DataType = ntohs(DataType); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} diff --git a/TIBERIANDAWN/FIELD.H b/TIBERIANDAWN/FIELD.H new file mode 100644 index 000000000..d35b0c534 --- /dev/null +++ b/TIBERIANDAWN/FIELD.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __FIELD_H +#define __FIELD_H + +#include +#include + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass { + + public: + friend class PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + void *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + +#endif diff --git a/TIBERIANDAWN/FINDPATH.CPP b/TIBERIANDAWN/FINDPATH.CPP new file mode 100644 index 000000000..028bf2dc8 --- /dev/null +++ b/TIBERIANDAWN/FINDPATH.CPP @@ -0,0 +1,1543 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\findpath.cpv 2.17 16 Oct 1995 16:51:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FINDPATH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 25, 1995 [PWG] * + * * + * The path algorithm works by following a LOS path to the target. If it * + * collides with an impassable spot, it uses an Edge following routine to * + * get around it. The edge follower moves along the edge in a clockwise or * + * counter clockwise fashion until finding the destination spot. The * + * destination is determined by Find_Path. It is the first passable that * + * can be reached (so it will handle the doughnut case, where there is * + * a passable in the center of an unreachable area). * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Path_Overlap -- clears the path overlap list * + * Find_Path -- Find a path from point a to point b. * + * Find_Path_Cell -- Finds a given cell on a specified path * + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * Get_New_XY -- Get the new x,y based on current position and direction. * + * Optimize_Moves -- Optimize the move list. * + * Set_Path_Overlap -- Sets the overlap bit for given cell * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include + +/* +** When an edge search is started, it can be performed CLOCKwise or +** COUNTERCLOCKwise direction. +*/ +#define CLOCK (FacingType)1 // Clockwise. +#define COUNTERCLOCK (FacingType)-1 // Counterclockwise. + +/* +** If defined, diagonal moves are allowed, else no diagonals. +*/ +#define DIAGONAL + +/* +** This is the marker to signify the end of the path list. +*/ +#define END FACING_NONE + +/* +** If memory is more important than speed, set this define to +** true. It will then perform intermediate optimizations to get the most +** milage out of a limited movement list staging area. If this value +** is true then it figures paths a bit more intelligently. +*/ +#define SAVEMEM true + +/* +** Modify this macro so that given two cell values, it will return +** a value between 0 and 7, with 0 being North and moving +** clockwise (just like map degrees). +*/ +#define CELL_FACING(a,b) Dir_Facing(::Direction((a),(b))) + + +/*-------------------------------------------------------------------------*/ +/* +** Cells values are really indexes into the 'map'. The following value is +** the X width of the map. +*/ +#define MODULO MAP_CELL_W + +/* +** Maximum lookahead cells. Twice this value in bytes will be +** reserved on the stack. The smaller this number, the faster the processing. +*/ +#define MAX_MLIST_SIZE 300 +#define THREAT_THRESHOLD 5 + +#ifdef NEVER +typedef enum { + FACING_N, // North + FACING_NE, // North-East + FACING_E, // East + FACING_SE, // South-East + FACING_S, // South + FACING_SW, // South-West + FACING_W, // West + FACING_NW, // North-West + + FACING_COUNT // Total of 8 directions (0..7). +} FacingType; +#endif + + +/*-------------------------------------------------------------------------*/ +static bool DrawPath; + +inline FacingType Opposite(FacingType face) +{ + return( (FacingType) (face ^ 4)); +} + +static inline void Draw_Cell_Point(CELL cell, bool passable, int threat_stage, int overide = 0) +{ + if (DrawPath) { + if (!Debug_Find_Path) { + int x, y; + + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + if (threat_stage>2) { + SeenBuff.Put_Pixel(x, y, (passable) ? LTGREEN : RED); + } else { + SeenBuff.Put_Pixel(x, y, (passable) ? 9+threat_stage : RED); + } + } + } else { + int x = cell & 63; + int y = cell / 64; + if (!overide) { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, (passable) ? WHITE : BLACK); + } else { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, overide); + } + } + } +} + +inline static FacingType Next_Direction(FacingType facing, FacingType dir) +{ + facing = facing + dir; + #ifndef DIAGONAL + facing = (FacingType)(facing & 0x06); + #endif + return(facing); +} + +/*=========================================================================*/ +/* Define a couple of variables which are private to the module they are */ +/* declared in. */ +/*=========================================================================*/ +static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path +static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path +static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path + + +//static CELL MoveMask = 0; +static CELL DestLocation; +static CELL StartLocation; + +/*************************************************************************** + * Point_Relative_To_Line -- Relation between a point and a line * + * * + * If a point is on a line then the following function holds true: * + * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * + * line (x1,z1),(x2,z2). * + * If the right side is > then the left side then the point is on one * + * side of the line and if the right side is < the the left side, then* + * the point is on the other side of the line. By subtracting one side* + * from the other we can determine on what side (if any) the point is on* + * by testing the side of the resulting subtraction. * + * * + * INPUT: * + * int x - x pos of point. * + * int z - z pos of point. * + * int x1 - x pos of first end of line segment. * + * int z1 - z pos of first end of line segment. * + * int x1 - x pos of second end of line segment. * + * int z1 - z pos of second end of line segment. * + * * + * OUTPUT: * + * Assuming (x1,z1) is north, (x2,z2) is south: * + * 0 : point is on line. * + * > 0 : point is east of line. * + * < 0 : point is west of line. * + * * + * WARNINGS: * + * Remember that int means that is assumes 16 bits of persision. * + * * + * HISTORY: * + * 10/28/1994 SKB : Created. * + *=========================================================================*/ +int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) +{ + return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); +} + + +/*************************************************************************** + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * * + * While in the midst of the Follow Edge logic, it is possible (due to the * + * fact that we support diagonal movement) to begin looping around a * + * column of some type. The Unravel loop function will scan backward * + * through the list and fixup the path to try to prevent the loop. * + * * + * INPUT: path - pointer to the generated path so we can pull the * + * commands out of it. * + * cell - the cell we tried to enter that generated the * + * double overlap condition. * + * dir - the direction we tried to enter from when we * + * generated the double overlap condition * + * startx - the start x position of this path segment * + * starty - the start y position of this path segment * + * destx - the dest x position for this path segment * + * desty - the dest y position for this path segment * + * * + * OUTPUT: TRUE - loop has been sucessfully unravelled * + * FALSE - loop can not be unravelled so abort follow edge * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/25/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) +{ + /* + ** Walk back to the actual cell before we advanced our position + */ + FacingType curr_dir = dir; + CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); + int idx = path->Length; // start at the last position + FacingType *list = &path->Command[idx-1]; // point to the last command + int checkx; + int checky; + int last_was_line = false; + + /* + ** loop backward through the list searching for a point that is + ** on the line. If the point was a diagonal move then adjust + ** it. + */ + while (idx) { + checkx = Cell_X(curr_pos); + checky = Cell_Y(curr_pos); + + if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { + + /* + ** We have now found a point on the line. Now we must check to see + ** if we left the line on a diagonal. If we did then we need to fix + ** it up. + */ + if (curr_dir & 1 && curr_pos != path->LastFixup) { + cell = curr_pos; + dir = *(list-1); + path->Length = idx; + path->LastFixup = curr_pos; + Draw_Cell_Point(curr_pos, true, -1, CYAN); + return(true); + } + + last_was_line = !last_was_line; + } + + /* + ** Since this cell will not be in the list, then pull out its cost + */ + path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); + + /* + ** Remove this cells flag from the overlap list for the path + */ + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); + + /* + ** Mark cell on the map + */ + Draw_Cell_Point(curr_pos, true, -1, LTCYAN); + + /* + ** Adjust to the next list position and direction. + */ + curr_dir = *list--; + curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); + idx--; + } + + /* + ** If we can't modify the list to eliminate the problem, then we have + ** a larger problem in that we have deleted all of the cells in the + ** list. + */ + return(false); +} + + +/*************************************************************************** + * Register_Cell -- registers a cell on our path and check for backtrack * + * * + * This function adds a new cell to our path. If the cell has already * + * been recorded as part of our path, then this function moves back down * + * the list truncating it at the point we registered that cell. This * + * function will elliminate all backtracking from the list. * + * * + * INPUT: long * list - the list to set the overlap bit for * + * CELL cell - the cell to mark on the overlap list * + * * + * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * + * * + * HISTORY: * + * 05/23/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType *list; + int pos = cell >> 5; + int bit = (cell & 31) - 1; + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + path->Length--; + Draw_Cell_Point(pos, true, -1, BLUE); + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + int idx = 0; + list = path->Command; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words). + ** + ** PWG 8/16/95 - However there is no sense searching the list if + ** the cell we have overlapped on is the cell we + ** started in. + */ + + if (pos != cell) { + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + idx++; + list++; + } + newlen = idx; + } + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + Draw_Cell_Point(pos, true, -1, LTBLUE); + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} +#ifdef OBSOLETE +bool FootClass::Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType *list; + int pos = cell >> 5; + int bit = (cell & 31) - 1; + int idx; + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + path->Length--; + Draw_Cell_Point(pos, true, -1, BLUE); + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words) + */ + for (idx = 0, list = path->Command; idx < path->Length; idx++, list++) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + } + newlen = idx; + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + Draw_Cell_Point(pos, true, -1, LTBLUE); + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} +#endif + + +/*********************************************************************************************** + * Find_Path -- Find a path from point a to point b. * + * * + * INPUT: int source x,y, int destination x,y, char *final moves * + * array to store moves, int maximum moves we may attempt * + * * + * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * + * not reach the destination * + * * + * WARNINGS: This algorithm assumes that the target is NOT situated * + * inside an impassable. If this case may arise, the do-while * + * statement inside the inner while (true) must be changed * + * to include a check to se if the next_x,y is equal to the * + * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + *=============================================================================================*/ +PathType * FootClass::Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold) +{ + CELL source = Coord_Cell(Coord); // Source expressed as cell + static PathType path; // Main path control. + CELL next; // Next cell to enter + CELL startcell; // Cell we started in + FacingType direction; // Working direction of look ahead. + FacingType newdir; // Tentative facing value. + + bool left=false, // Was leftward path legal? + right=false; // Was rightward path legal? + + int len; // Length of detour command list. + int unit_threat; // Calculated unit threat rating + int cost; // Cost to enter the square + FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. + moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. + PathType pleft,pright; // Path control structures. + PathType *which; // Which path to actually use. + int threat = 0; // + int threat_stage = 0; //These weren't initialized. ST - 1/8/2019 12:03PM + + /* + ** If we have been provided an illegal place to store our final moves + ** then forget it. + */ + if (!final_moves) return(NULL); +// IsFindPath = true; + + /* + ** Set the draw path variable to draw the path of the selected unit + ** if necessary. + */ + if (!Debug_Find_Path) { + DrawPath = Is_Selected_By_Player() && Special.IsShowPath; + } else { + DrawPath = Is_Selected_By_Player(); + } + Debug_Draw_Map("Initial Draw", source, dest, false); + +// MoveMask = flags; + if (Team && Team->Class->IsRoundAbout) { + unit_threat = (Team) ? Team->Risk : Risk(); + threat_stage = 0; + threat = 0; + } else { + unit_threat = threat = -1; + } + + StartLocation = source; + DestLocation = dest; + + /* + ** Initialize the path structure so that we can keep track of the + ** path. + */ + path.Start = source; + path.Cost = 0; + path.Length = 0; + path.Command = final_moves; + path.Command[0] = END; + path.Overlap = MainOverlap; + path.LastOverlap = -1; + path.LastFixup = -1; + + memset(path.Overlap, 0, sizeof(MainOverlap)); + + /* + ** Clear the over lap list and then make sure that our starting position is marked + ** on the overlap list. (Otherwise the harvesters will drive in circles... ) + */ +// memset(path.Overlap, 0, 512); + path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); + + startcell = source; + + /* + ** Account for trailing end of list command, so reduce the maximum + ** allowed legal commands to reflect this. + */ + maxlen--; + + /* + ** As long as there is room to put commands in the movement command list, + ** then put commands in it. We build the path using the following + ** methodology. + ** + ** 1. Scan through the desired strait line path until we eiter hit an + ** impassable or have created a valid path. + ** + ** 2. If we have hit an impassable, walk through the impassable to make + ** sure that there is a passable on the other side. If there is not + ** and we can not change the impassable, then this list is dead. + ** + ** 3. Walk around the impassable on both the left and right edges and + ** take the shorter of the two paths. + ** + ** 4. Taking the new location as our start location start again with + ** step #1. + */ + while (path.Length < maxlen) { + +top_of_list: + /* + ** Have we reached the destination already? If so abort any further + ** command building. + */ + if (startcell == dest) { + break; + } + + /* + ** Find the absolute correct direction to reach the next straight + ** line cell and what cell it is. + */ + direction = CELL_FACING(startcell, dest); + next = Adjacent_Cell(startcell, direction); + + /* + ** If we can move here, then make this our next move. + */ + cost = Passable_Cell(next, direction, threat, threshhold); + if (cost) { + Draw_Cell_Point(next, true, threat_stage); + Register_Cell(&path, next, direction, cost, threshhold); + } else { + if (Debug_Find_Path && DrawPath) { + Debug_Draw_Map("Walk Through Obstacle", startcell, dest, true); + } + Draw_Cell_Point(next, false, threat_stage); + + /* + ** If the impassable location is actually the destination, + ** then stop here and consider this "good enough". + */ + if (next == dest) break; + + /* + ** We could not move to the next cell, so follow through the + ** impassable until we find a passable spot that can be reached. + ** Once we find a passable, figure out the shortest path to it. + ** Since we have variable passable conditions this is not as + ** simple as it used to be. The limiter loop below allows us to + ** step through ten donuts before we give up. + */ + for (int limiter = 0; limiter < 5; limiter++) { + + /* + ** Get the next passable position by zipping through the + ** impassable positions until a passable position is found + ** or the destination is reached. + */ + for (;;) { + + /* + ** Move one step closer toward destination. + */ + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + + /* + ** If the cell is passable then we have been completely + ** sucessful. If the cell is not passable then continue. + */ + if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { + Draw_Cell_Point(next, true, threat_stage); + break; + } else { + Draw_Cell_Point(next, false, threat_stage); + } + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + } + + /* + ** Try to find a path to the passable position by following + ** the edge of the blocking object in both CLOCKwise and + ** COUNTERCLOCKwise fashions. + */ + int follow_len = maxlen + (maxlen >> 1); + + Debug_Draw_Map("Follow left edge", startcell,next,true); + Mem_Copy(&path, &pleft, sizeof(PathType)); + pleft.Command = &moves_left[0]; + pleft.Overlap = LeftOverlap; + Mem_Copy(path.Command, pleft.Command, path.Length); + Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left), threshhold); +// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); + + if (left) { + follow_len = MIN(maxlen, pleft.Length + (pleft.Length >> 1)); + } + + /* + ** If we are in debug mode then let us know how well our left path + ** did. + */ + if (Debug_Find_Path && DrawPath) { + Fancy_Text_Print(" Left", 0, 92, WHITE, BLACK, TPF_6POINT); + Fancy_Text_Print("Total Steps", 0, 100, WHITE, BLACK, TPF_6POINT); + if (left) { + Fancy_Text_Print(" %d", 0, 108, WHITE, BLACK, TPF_6POINT, pleft.Length); + } else { + Fancy_Text_Print(" FAIL", 0, 108, WHITE, BLACK, TPF_6POINT); + } + } + + Debug_Draw_Map("Follow right edge", startcell, next, true); + Mem_Copy(&path, &pright, sizeof(PathType)); + pright.Command = &moves_right[0]; + pright.Overlap = RightOverlap; + Mem_Copy(path.Command, pright.Command, path.Length); + Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right), threshhold); +// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); + + /* + ** If we are in debug mode then let us know how well our right path + ** did. + */ + if (Debug_Find_Path && DrawPath) { + Fancy_Text_Print(" Right", 0, 92, WHITE, BLACK, TPF_6POINT); + Fancy_Text_Print("Total Steps", 0, 100, WHITE, BLACK, TPF_6POINT); + if (right) { + Fancy_Text_Print(" %d", 0, 108, WHITE, BLACK, TPF_6POINT, pright.Length); + } else { + Fancy_Text_Print(" FAIL", 0, 108, WHITE, BLACK, TPF_6POINT); + } + } + + /* + ** If we could find a path, break from this loop. Otherwise this + ** means that we have found a "hole" of passable terrain that + ** cannot be reached by normal means. Scan forward looking for + ** the other side of the "doughnut". + */ + if (left || right) break; + + /* + ** If no path can be found to the intermediate cell, then + ** presume we have found a doughnut of some sort. Scan + ** forward until the next impassable is found and then + ** process this loop again. + */ + do { + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + } while (Passable_Cell(next, newdir, threat, threshhold)); + } + + if (!left && !right) break; + + /* + ** We found a path around the impassable locations, so figure out + ** which one was the smallest and copy those moves into the + ** path.Command array. + */ + which = &pleft; + if (right) { + which = &pright; + if (left) { + if (pleft.Length < pright.Length) { + which = &pleft; + } else { + which = &pright; + } + } + } + + /* + ** Record as much as possible of the shorter of the two + ** paths. The trailing EOL command is not copied because + ** this may not be the end of the find path logic. + */ + len = which->Length; + len = MIN(len, maxlen); + if (len > 0) { + memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); + memcpy(&path.Command[0], &which->Command[0], len); + path.Length = len; + path.Cost = which->Cost; + path.LastOverlap = -1; + path.LastFixup = -1; + } else { + break; + } + Debug_Draw_Map("Walking to next obstacle", next, dest, true); + } + startcell = next; + } + +end_of_list: + /* + ** Poke in the stop command. + */ + if (path.Length < maxlen) { + path.Command[path.Length++] = END; + } + if (Debug_Find_Path && DrawPath) { + Map.Flag_To_Redraw(true); + } + /* + ** Optimize the move list but only necessary if + ** diagonal moves are allowed. + */ + #ifdef DIAGONAL + Optimize_Moves(&path, threshhold); + #endif + if (Debug_Find_Path && DrawPath) { + Debug_Draw_Map("Final Generated Path", startcell,dest,false); + Debug_Draw_Path(&path); + Get_Key_Num(); + } +// IsFindPath = false; + return(&path); +} + + +/*********************************************************************************************** + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * * + * INPUT: start -- cell to head from * + * * + * target -- Target cell to head to. * + * * + * path -- Pointer to path list structure. * + * * + * search -- Direction of search (1=clock, -1=counterclock). * + * * + * olddir -- Facing impassible direction from start. * + * * + * callback -- Function pointer for determining if a cell is * + * passable or not. * + * * + * OUTPUT: bool: Could a path be found to the desired cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized & commented. * + *=============================================================================================*/ +bool FootClass::Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold) +{ + FacingType newdir; // Direction of facing before surrounding cell check. + CELL oldcell, // Current cell. + newcell; // Tentative new cell. + int cost; // Working cost value. + int startx; + int starty; + int online=true; + int targetx; + int targety; + int oldval = 0; + int cellcount=0; + int forceout = false; + FacingType firstdir = (FacingType)-1; + CELL firstcell = -1; + bool stepped_off_line = false; + startx = Cell_X(start); + starty = Cell_Y(start); + targetx = Cell_X(target); + targety = Cell_Y(target); + + if (!path) return(false); + path->LastOverlap = -1; + path->LastFixup = -1; + + #ifndef DIAGONAL + /* + ** The edge following algorithm doesn't "do" diagonals. Force initial facing + ** to be an even 90 degree value. Adjust it in the direction it should be + ** rotating. + */ + if (olddir & 0x01) { + olddir = Next_Direction(olddir, search); + } + #endif + + newdir = Next_Direction(olddir, search); + oldcell = start; + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** Continue until we find our target, find our original starting spot, + ** or run out of moves. + */ + while (path->Length < max_cells) { + + /* + ** Look in all the adjacent cells to determine a passable one that + ** most closely matches the desired direction (working in the specified + ** direction). + */ + newdir = olddir; + for (;;) { + bool forcefail; // Is failure forced? + + forcefail = false; + + #ifdef DIAGONAL + /* + ** Rotate 45/90 degrees in desired direction. + */ + newdir = Next_Direction(newdir, search); + + /* + ** If facing a diagonal we must check the next 90 degree location + ** to make sure that we don't walk right by the destination. This + ** will happen if the destination it is at the corner edge of an + ** impassable that we are moving around. + */ + if (newdir & FACING_NE) { + CELL checkcell; // Non-diagonal check cell. + //int x,y; + + checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); + + if (checkcell == target) { + + /* + ** This only works if in fact, it is possible to move to the + ** cell from the current location. + */ + cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); + if (cost) { + Draw_Cell_Point(checkcell, true, threat_stage); + + /* + ** YES! The destination is at the corner of an impassable, so + ** set the direction to point directly at it and then the + ** scanning will terminate later. + */ + newdir = Next_Direction(newdir, search); + newcell = Adjacent_Cell(oldcell, newdir); + break; + } else { + Draw_Cell_Point(checkcell, false, threat_stage); + } + } + + /* + ** Perform special diagonal check. If the edge follower would cross the + ** diagonal or fall on the diagonal line from the source, then consider + ** that cell impassible. Otherwise, the find path algorithm will fail + ** when there are two impassible locations located on a diagonal + ** that is lined up between the source and destination location. + ** + ** P.S. It might help if you check the right cell rather than using + ** the value that just happened to be in checkcell. + */ + + checkcell = Adjacent_Cell(oldcell, newdir); + + int checkx = Cell_X(checkcell); + int checky = Cell_Y(checkcell); + int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); + if (checkval && !online) { + forcefail = ((checkval ^ oldval) < 0); + } else { + forcefail = false; + } + /* + ** The only exception to the above is when we are directly backtracking + ** because we could be trying to escape from a culdesack! + */ + if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { +// ST - 12/18/96 5:15PM if (forcefail && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { + forcefail = false; + } + } + + #else + newdir = Next_Direction(newdir, search*2); + #endif + + /* + ** If we have just checked the same heading we started with, + ** we are surrounded by impassable characters and we exit. + */ + if (newdir == olddir) { + return(false); + } + + /* + ** Get the new cell. + */ + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** If we found a passable position, this is where we should move. + */ + if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { + Draw_Cell_Point(newcell, true, threat_stage); + break; + } else { + Draw_Cell_Point(newcell, false, threat_stage, (forcefail) ? BROWN : 0); + if (newcell == target) { + forceout = true; + break; + } + } + + } + + /* + ** Record the direction. + */ + if (!forceout) { + /* + ** Mark the cell because this is where we need to be. If register + ** cell fails then the list has been shortened and we need to adjust + ** the new direction. + */ + if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { + /* + ** The only reason we could not register a cell is that we are in + ** a looping situation. So we need to try and unravel the loop if + ** we can. + */ + if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { + return(false); + } + /* + ** Since we need to eliminate a diagonal we must pretend the upon + ** attaining this square, we were moving turned farther in the + ** search direction then we really were. + */ + newdir = Next_Direction(newdir, (FacingType)(search*2)); + } + /* + ** Find out which side of the line this cell is on. If it is on + ** a side, then store off that side. + */ + int newx = Cell_X(newcell); + int newy = Cell_Y(newcell); + int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); + if (val) { + oldval = val; + online = false; + } else { + online = true; + } + cellcount++; + if (cellcount==100) { +// DrawPath = true; +// Debug_Find_Path = true; +// Debug_Draw_Map("Loop failure", start, target, false); +// Debug_Draw_Path(path); + return(false); + } + } + + /* + ** If we have found the target spot, we are done. + */ + if (newcell == target) { + path->Command[path->Length] = END; + return(true); + } + + /* + ** If we make a full circle back to our original spot, get out. + */ + if (newcell == firstcell && newdir == firstdir) { + return(false); + } + + if (firstcell == -1) { + firstcell = newcell; + firstdir = newdir; + } + + /* + ** Because we moved, our facing is now incorrect. We want to face toward + ** the impassable edge we are following (well, not actually toward, but + ** a little past so that we can turn corners). We have to turn 45/90 degrees + ** more than expected in anticipation of the pending 45/90 degree turn at + ** the start of this loop. + */ + #ifdef DIAGONAL + olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); + #else + olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); + #endif + oldcell = newcell; + } + + /* + ** The maximum search path is exhausted... abort with a failure. + */ + return(false); +} + + +/*********************************************************************************************** + * Optimize_Moves -- Optimize the move list. * + * * + * INPUT: char *moves to optimize * + * * + * OUTPUT: none (list is optimized) * + * * + * WARNINGS: EMPTY moves are used to hold the place of eliminated * + * commands. Also, NEVER call this routine with a list that * + * contains illegal commands. The list MUST be terminated * + * with a EOL command * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized and commented. * + *=============================================================================================*/ +#define EMPTY (FacingType)-2 +int FootClass::Optimize_Moves(PathType *path, MoveType threshhold) +//int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) +{ + /* + ** Facing command pair adjustment table. Compare the facing difference between + ** the two commands. 0 means no optimization is possible. 3 means backtracking + ** so eliminate both commands. Any other value adjusts the first command facing. + */ +#ifdef DIAGONAL + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. +#else + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; +#endif + FacingType *cmd1, // Floating first command pointer. + *cmd2, // Floating second command pointer. + newcmd; // Calculated new optimized command. + FacingType newdir; // Tentative new direction for smoothing. + CELL cell; // Working cell (as it moves along path). + + /* + ** Abort if there is any illegal parameter. + */ + if (!path || !path->Command) return(0); + + /* + ** Optimization loop -- start scanning with the + ** first pair of commands (if there are at least two + ** in the command list). + */ + path->Command[path->Length] = END; // Force end of list. + cell = path->Start; + if (path->Length > 1) { + cmd2 = path->Command + 1; + while (*cmd2 != END) { + + /* + ** Set the cmd1 pointer to point to the valid command closest, but + ** previous to cmd2. Be sure not to go previous to the head of the + ** command list. + */ + cmd1 = cmd2-1; + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + + /* + ** If there isn't any valid previous command, then bump the + ** cmd pointers to the next command pair and continue... + */ + if (*cmd1 == EMPTY) { + cmd2++; + continue; + } + + /* + ** Fetch precalculated command change value. 0 means leave + ** command set alone, 3 means backtrack and eliminate two + ** commands. Any other value is new direction and eliminate + ** one command. + */ + newcmd = (FacingType)(*cmd2 - *cmd1); + if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); + newcmd = _trans[newcmd]; + + /* + ** Check for backtracking. If this occurs, then eliminate the + ** two commands. This is the easiest optimization. + */ + if (newcmd == FACING_SE) { + *cmd1 = EMPTY; + *cmd2++ = EMPTY; + continue; + } + + /* + ** If an optimization code was found the process it. The command is a facing + ** offset to more directly travel toward the immediate destination cell. + */ + if (newcmd) { + + /* + ** Optimizations differ when dealing with diagonals. Especially when dealing + ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can + ** only be optimized if the intervening cell is passable. The distance travelled + ** is the same, but the path is less circuitous. + */ + if (*cmd1 & FACING_NE) { + + /* + ** Diagonal optimizations are always only 45 + ** degree adjustments. + */ + newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); + + /* + ** Diagonal 90 degree changes can be smoothed, although + ** the path isn't any shorter. + */ + if (ABS((int)newcmd) == 1) { + if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { + *cmd2 = newdir; + *cmd1 = newdir; + } + // BOB 16.12.92 + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + continue; + } + } else { + newdir = Next_Direction(*cmd1, newcmd); + } + + /* + ** Allow shortening turn only on right angle moves that are based on + ** 90 degrees. Always allow 135 degree optimizations. + */ + *cmd2 = newdir; + *cmd1 = EMPTY; + + /* + ** Backup what it thinks is the current cell. + */ + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + if (*cmd1 != EMPTY) { + cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); + } else { + cell = path->Start; + } + continue; + } + + /* + ** Since we could not make an optimization, we move our + ** head pointer forward. + */ + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + } + } + + /* + ** Pack the command list to remove any EMPTY command entries. + */ + cmd1 = path->Command; + cmd2 = path->Command; + cell = path->Start; + path->Cost = 0; + path->Length = 0; + while (*cmd2 != END) { + if (*cmd2 != EMPTY) { + +#ifdef NEVER + if (Debug_ShowPath) { + int x,y,x1,y1; + + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + Map.Coord_To_Pixel(Cell_Coord(Adjacent_Cell(cell, *cmd2)), x1, y1); + Set_Logic_Page(SeenBuff); + LogicPage->Draw_Line(x, y+8, x1, y1+8, DKGREY); + } + } +#endif + + cell = Adjacent_Cell(cell, *cmd2); + path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); + path->Length++; + *cmd1++ = *cmd2; + } + cmd2++; + } + path->Length++; + *cmd1 = END; + return(path->Length); +} + + +CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) +{ + FacingType dir; + CELL next; + int lp; + + dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; + + /* + ** Loop through the different acceptable distances. + */ + for (int dist = start; dist < max; dist ++) { + + /* + ** Move to the starting location. + */ + next = dst; + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir); + } + + if (dir & 1) { + /* + ** If our direction is diagonal than we need to check + ** only one side which is as long as both of the old sides + ** together. + */ + for (lp = 0; lp < dist << 1; lp ++) { + next = Adjacent_Cell(next, dir + 3); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } else { + /* + ** If our direction is not diagonal than we need to check two + ** sides so that we are checking a corner like location. + */ + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 2); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 4); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } + } + return(-1); +} + + + + +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +{ + MoveType move = Can_Enter_Cell(cell, face); + + if (move < MOVE_MOVING_BLOCK && Distance(cell) > 1) threshhold = MOVE_MOVING_BLOCK; + + if (move > threshhold) return(0); + + if (GameToPlay == GAME_NORMAL) { + if (threat != -1) { + if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + } + + static int _value[MOVE_COUNT] = { + 1, // MOVE_OK + 1, // MOVE_CLOAK + 3, // MOVE_MOVING_BLOCK + 8, // MOVE_DESTROYABLE + 10, // MOVE_TEMP + 0 // MOVE_NO + }; + return(_value[move]); + +#ifdef NEVER + int can; + int retval; + + int temp_move_mask = MoveMask; + + if (!House->IsHuman) { + temp_move_mask &= ~MOVEF_TEMP; + } + +#ifdef NEVER + if ((!(MoveMask & MOVEF_MOVING_BLOCK)) && Map.Cell_Distance(StartLocation, cell) > 2) { + temp_move_mask |= MOVEF_MOVING_BLOCK; + } +#endif + + can = (temp_move_mask & Can_Enter_Cell(cell, face)); + if (can & MOVEF_NO) return(0); + + retval = 1; + if (can & MOVEF_MOVING_BLOCK) retval += 3; + if (can & MOVEF_DESTROYABLE) retval += 10; + if (can & MOVEF_TEMP) retval += 10; + + if (threat != -1) { + if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + + return(retval); +#endif +} + + +void FootClass::Debug_Draw_Map(char *txt, CELL start, CELL dest, bool pause) +{ + if ((!Debug_Find_Path) || (!DrawPath)) return; + + if (pause) Get_Key_Num(); + GraphicViewPortClass * page = Set_Logic_Page(SeenBuff); + + VisiblePage.Clear(); + Fancy_Text_Print(txt, 160, 0, WHITE, BLACK, TPF_8POINT|TPF_CENTER); + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + int color = 0; + + switch (Can_Enter_Cell( (CELL)((y << 6) + x))) { + case MOVE_OK: + color = GREEN; + break; + case MOVE_MOVING_BLOCK: + color = LTGREEN; + break; + + case MOVE_DESTROYABLE: + color = YELLOW; + break; + case MOVE_TEMP: + color = BROWN; + break; + default: + color = RED; + break; + } + if ((CELL)((y << 6) + x) == start) + color = LTBLUE; + if ((CELL)((y << 6) + x) == dest) + color = BLUE; + Fat_Put_Pixel(64 + (x*3), 8 + (y*3), color, 3, SeenBuff); + } + } + Set_Logic_Page(page); +} + +void FootClass::Debug_Draw_Path(PathType *path) +{ + if (!path) return; + + FacingType *list = path->Command; + CELL pos = path->Start; + + for (int idx = 0; idx < path->Length; idx++) { + pos = Adjacent_Cell(pos, *list++); + Draw_Cell_Point(pos, true, -1, 0); + } +} diff --git a/TIBERIANDAWN/FLASHER.CPP b/TIBERIANDAWN/FLASHER.CPP new file mode 100644 index 000000000..b81aa8087 --- /dev/null +++ b/TIBERIANDAWN/FLASHER.CPP @@ -0,0 +1,129 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\flasher.cpv 2.18 16 Oct 1995 16:49:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * * + * This utility function will output the current status of the FlasherClass to the mono * + * screen. It is through this display that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void FlasherClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(50, 7); + mono->Printf("%2d", FlashCount); +} +#endif + + +/*********************************************************************************************** + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * * + * The ability for an object to flash is controlled by this logic processing routine. It * + * should be called once per game tick per unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the associated object be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/20/1994 JLB : Is now independent of object it represents. * + *=============================================================================================*/ +bool FlasherClass::Process(void) +{ + // 2019/09/20 JAS - Flashing info needs to exist per player + for (int i = 0; i < HOUSE_COUNT; i++) + { + if (FlashCountPerPlayer[i]) + { + FlashCountPerPlayer[i]--; + } + } + + if (FlashCount) { + FlashCount--; + IsBlushing = false; + + if (FlashCount & 0x01) { + IsBlushing = true; + } + return(true); + } + return(false); +} + +/*********************************************************************************************** + * FlasherClass::Get_Flashing_Flags -- * + * * + * Gets the flags tell which players this object should flash for. * + * * + * INPUT: none * + * * + * OUTPUT: unsigned int; Flag representing the players to flash for * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/20 JAS : Created. * + *=============================================================================================*/ +unsigned int FlasherClass::Get_Flashing_Flags() const +{ + unsigned flags = 0; + for (int i = 0; i < HOUSE_COUNT; ++i) + { + if (FlashCountPerPlayer[i] > 0) + { + flags |= (1 << i); + } + } + + return flags; +} diff --git a/TIBERIANDAWN/FLASHER.H b/TIBERIANDAWN/FLASHER.H new file mode 100644 index 000000000..c150ce805 --- /dev/null +++ b/TIBERIANDAWN/FLASHER.H @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\flasher.h_v 2.17 16 Oct 1995 16:45:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : May 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLASHER_H +#define FLASHER_H + +class FlasherClass { + public: + /* + ** When this object is targeted, it will flash a number of times. This is the + ** flash control number. It counts down to zero and then stops. Odd values + ** cause the object to be rendered in a lighter color. + */ + unsigned FlashCount:7; + + /* + ** When an object is targeted, it flashes several times to give visual feedback + ** to the player. Every other game "frame", this flag is true until the flashing + ** is determined to be completed. + */ + unsigned IsBlushing:1; + + // 2019/09/20 JAS - Flashing info needs to exist per player + FlasherClass(void) { + FlashCount = 0; + IsBlushing = false; + + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = 0; + } + } + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + bool Process(void); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + + // 2019/09/20 JAS - Flashing info needs to exist per player + unsigned int Get_Flashing_Flags() const; + unsigned int FlashCountPerPlayer[HOUSE_COUNT]; +}; + +#endif diff --git a/TIBERIANDAWN/FLY.CPP b/TIBERIANDAWN/FLY.CPP new file mode 100644 index 000000000..d68ab5d2d --- /dev/null +++ b/TIBERIANDAWN/FLY.CPP @@ -0,0 +1,130 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\fly.cpv 2.18 16 Oct 1995 16:50:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : June 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlyClass::Physics -- Performs vector physics (movement). * + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FlyClass::Physics -- Performs vector physics (movement). * + * * + * This routine performs movement (vector) physics. It takes the * + * specified location and moves it according to the facing and speed * + * of the vector. It returns the status of the move. * + * * + * INPUT: coord -- Reference to the coordinate that the vector will * + * be applied to. * + * * + * OUTPUT: Returns with the status of the vector physics. This could * + * range from no effect, to exiting the edge of the world. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + * 06/05/1995 JLB : Simplified to just do movement. * + *=============================================================================================*/ +ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing) +{ + if (SpeedAdd != MPH_IMMOBILE) { + int actual = (int)SpeedAdd + SpeedAccum; + div_t result = div(actual, PIXEL_LEPTON_W); + SpeedAccum = result.rem; + actual -= result.rem; + COORDINATE old = coord; + + /* + ** If movement occurred that is at least one + ** pixel, then check update the coordinate and + ** check for edge of world collision. + */ + if (result.quot) { + COORDINATE newcoord; // New working coordinate. + + newcoord = Coord_Move(coord, facing, actual); + + /* + ** If no movement occurred, then presume it hasn't moved at all + ** and return immediately with this indication. + */ + if (newcoord == coord) { + return(IMPACT_NONE); + } + + /* + ** Remember the new position. + */ + coord = newcoord; + + /* + ** If the new coordinate is off the edge of the world, then report + ** this. + */ + if (newcoord & 0xC000C000L /*|| !Map.In_Radar(Coord_Cell(newcoord))*/) { + coord = old; + return(IMPACT_EDGE); + } + + return(IMPACT_NORMAL); + } + } + return(IMPACT_NONE); +} + + +/*********************************************************************************************** + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * * + * This sets the speed of the projectile. It basically functions like a throttle value * + * where 0 equals stop and 255 equals maximum speed (whatever that is for the particular * + * object). * + * * + * INPUT: speed -- Speed setting from 0 to 255. * + * * + * maximum -- The maximum speed of the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + * 07/26/1994 JLB : Added maximum speed as guiding value. * + *=============================================================================================*/ +void FlyClass::Fly_Speed(int speed, MPHType maximum) +{ + SpeedAdd = (MPHType)Fixed_To_Cardinal((int)maximum, speed); +} + diff --git a/TIBERIANDAWN/FLY.H b/TIBERIANDAWN/FLY.H new file mode 100644 index 000000000..e0818ed88 --- /dev/null +++ b/TIBERIANDAWN/FLY.H @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\fly.h_v 2.19 16 Oct 1995 16:45:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLY_H +#define FLY_H + +typedef enum ImpactType : unsigned char { + IMPACT_NONE, // No movement (of significance) occurred. + IMPACT_NORMAL, // Some (non eventful) movement occurred. + IMPACT_EDGE // The edge of the world was reached. +} ImpactType; + + +/**************************************************************************** +** Flying objects are handled by this class definition. +*/ +class FlyClass { + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FlyClass(void) { + SpeedAdd = MPH_IMMOBILE; + SpeedAccum = 0; + }; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Fly_Speed(int speed, MPHType maximum); + ImpactType Physics(COORDINATE &coord, DirType facing); + MPHType Get_Speed(void) const {return(SpeedAdd);}; + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + /* + ** Object movement consists of incrementing the accumulator until enough "distance" + ** has accumulated so that moving the object becomes reasonable. + */ + unsigned SpeedAccum; // Lepton accumulator. + MPHType SpeedAdd; // Lepton add (per frame). +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/FOOT.CPP b/TIBERIANDAWN/FOOT.CPP new file mode 100644 index 000000000..9d878c31c --- /dev/null +++ b/TIBERIANDAWN/FOOT.CPP @@ -0,0 +1,2045 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\foot.cpv 2.17 16 Oct 1995 16:51:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FootClass::Active_Click_With -- Intiates attack or move according to target clicked on. * + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * FootClass::Assign_Mission -- Assign mission to foot class object. * + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * FootClass::Body_Facing -- Set the body rotation/facing. * + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * FootClass::Death_Announcement -- Announces the death of a unit. * + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * FootClass::Detach -- Detaches a target from tracking systems. * + * FootClass::Detach_All -- Removes this object from the game system. * + * FootClass::Enters_Building -- When unit enters a building for some reason. * + * FootClass::FootClass -- Default constructor for foot class objects. * + * FootClass::FootClass -- Normal constructor for the foot class object. * + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * FootClass::Limbo -- Intercepts limbo event and handles FootClass processing. * + * FootClass::Mark -- Unit interface to map rendering system. * + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * FootClass::Mission_Capture -- Handles the capture mission. * + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * FootClass::Override_Mission -- temporarily overides a units mission * + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * FootClass::Restore_Mission -- Restores an overidden mission * + * FootClass::Sell_Back -- Causes this object to be sold back. * + * FootClass::Set_Speed -- Initiate unit movement physics. * + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * FootClass::Take_Damage -- Handles taking damage to this object. * + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * FootClass::~FootClass -- Default destructor for foot class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FootClass::FootClass -- Default constructor for foot class objects. * + * * + * This is the default constructor for FootClass objects. It sets the foot class values to * + * their default starting settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/23/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(void) : Speed(0) +{ + IsDriving = false; + IsInitiated = false; + IsPlanningToLook = false; + IsDeploying = false; + IsNewNavCom = false; + IsFiring = false; + IsRotating = false; + IsUnloading = false; + NavCom = TARGET_NONE; + SuspendedNavCom = TARGET_NONE; + Path[0] = FACING_NONE; + HeadToCoord = NULL; + Member = 0; + Team = 0; + PathDelay = 0; + TryTryAgain = PATH_RETRY; + if (House) { + House->CurUnits++; + } + Group = -1; +} + + +/*********************************************************************************************** + * FootClass::~FootClass -- Default destructor for foot class objects. * + * * + * At this level of the destruction process, the house record of the number of units * + * currently in inventory is decremented to reflect this units destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +FootClass::~FootClass(void) +{ + if (GameActive && House) { + House->CurUnits--; + } +} + + +/*********************************************************************************************** + * FootClass::FootClass -- Normal constructor for the foot class object. * + * * + * This is the normal constructor used when creating a foot class object. * + * * + * INPUT: house -- The house that owns this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(HousesType house) : + TechnoClass(house), + Speed(0) +{ + Member = 0; + Team = 0; + Path[0] = FACING_NONE; + NavCom = TARGET_NONE; + SuspendedNavCom = TARGET_NONE; + IsUnloading = false; + IsDriving = false; + IsInitiated = false; + IsRotating = false; + IsFiring = false; + IsDeploying = false; + IsNewNavCom = false; + IsPlanningToLook = false; + HeadToCoord = 0L; + PathDelay = 0; + Group = -1; + TryTryAgain = PATH_RETRY; + House->CurUnits++; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * * + * This routine is used to output the current status of the foot class to the mono * + * monitor. Through this display bugs may be tracked down or eliminated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 07/04/1995 JLB : Handles aircraft special case. * + *=============================================================================================*/ +void FootClass::Debug_Dump(MonoClass *mono) const +{ + static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; +#define Path_To_String(a) _p2c[((ABS((int)a+1))%9)] + + /* + ** Display the common data for all objects that inherity from FootClass. + */ + mono->Set_Cursor(63, 7); + if (Team) { + mono->Printf("%s(%d)", Team->Class->IniName, Teams.ID(Team)); + } else { + mono->Printf("(none)"); + } + mono->Set_Cursor(73, 7);mono->Printf("%04X", ArchiveTarget); + mono->Set_Cursor(42, 1);mono->Printf("%04X", NavCom); + mono->Set_Cursor(44, 3);mono->Printf("%d", Speed); + + /* + ** Although aircraft inherit from FootClass, some of the variables are not + ** used and thus should not be displayed. + */ + if (What_Am_I() != RTTI_AIRCRAFT) { + mono->Set_Cursor(50, 3); + mono->Printf("%s%s%s%s%s%s%s%s%s%s%s%s", + Path_To_String(Path[0]), + Path_To_String(Path[1]), + Path_To_String(Path[2]), + Path_To_String(Path[3]), + Path_To_String(Path[4]), + Path_To_String(Path[5]), + Path_To_String(Path[6]), + Path_To_String(Path[7]), + Path_To_String(Path[8]), + Path_To_String(Path[9]), + Path_To_String(Path[10]), + Path_To_String(Path[11]), + Path_To_String(Path[12])); + + mono->Set_Cursor(65, 1);mono->Printf("%08lX", Head_To_Coord()); + mono->Text_Print("X", 16 + (IsDeploying?2:0), 12); + mono->Text_Print("X", 16 + (IsRotating?2:0), 13); + mono->Text_Print("X", 16 + (IsDriving?2:0), 15); + mono->Text_Print("X", 16 + (IsFiring?2:0), 14); + mono->Text_Print("X", 16 + (IsPlanningToLook?2:0), 16); + } + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * FootClass::Set_Speed -- Initiate unit movement physics. * + * * + * This routine is used to set a unit's velocity control structure. * + * The game will then process the unit's movement during the momentum * + * physics calculation. * + * * + * INPUT: unit -- Pointer to the unit to alter. * + * * + * speed -- Throttle setting (0=stop, 255=full throttle). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 09/24/1993 JLB : Revised for faster speed. * + * 04/02/1994 JLB : Revised for new system. * + * 04/15/1994 JLB : Converted to member function. * + * 07/21/1994 JLB : Simplified. * + *=============================================================================================*/ +void FootClass::Set_Speed(int speed) +{ + speed &= 0xFF; + ((unsigned char &)Speed) = speed; +} + + +/*********************************************************************************************** + * FootClass::Mark -- Unit interface to map rendering system. * + * * + * This routine is the interface function for units as they relate to * + * the map rendering system. Whenever a unit's imagery changes, this * + * function is called. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Unit is removed. * + * MARK_CHANGE -- Unit alters image but doesn't move. * + * MARK_DOWN -- Unit is overlaid onto existing icons. * + * * + * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * + * down when it is already down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 12/23/1994 JLB : Performs low level check before processing. * + *=============================================================================================*/ +bool FootClass::Mark(MarkType mark) +{ + if (TechnoClass::Mark(mark)) { + CELL cell = Coord_Cell(Coord); + + /* + ** Inform the map of the refresh, occupation, and overlap + ** request. + */ + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List()); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * * + * This is a common routine used by both infantry and other ground travelling units. It * + * will fill in the unit's basic path to the NavCom destination. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * + * be found or the terrain prohibits the unit's movement. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Basic_Path(void) +{ + PathType *path; // Pointer to path control structure. + CELL cell; + int skip_path = false; + + Path[0] = FACING_NONE; + + if (Target_Legal(NavCom)) { + cell = As_Cell(NavCom); + + /* + ** When the navigation computer is set to a location that is impassible, then + ** find a nearby cell that can be entered and try to head toward that instead. + ** EXCEPT when that cell is very close -- then just bail. + */ +// IsFindPath = true; + if (Can_Enter_Cell(cell) == MOVE_NO && Distance(NavCom) > 0x0300) { + static int _faceadjust[8] = {0, 1, -1, 2, -2, 3, -3, 4}; + FacingType f2 = (FacingType)(((unsigned)::Direction(cell, Coord_Cell(Coord)))>>5); + + for (unsigned index = 0; index < (sizeof(_faceadjust) / sizeof(_faceadjust[0])); index++) { + CELL cell2; + + cell2 = Adjacent_Cell(cell, (FacingType)((f2+_faceadjust[index])&0x7)); + if (Can_Enter_Cell(cell2, FACING_NONE) <= MOVE_CLOAK) { + cell = cell2; + break; + } + } + } +// IsFindPath = false; + +#ifdef SPECIAL + if (What_Am_I() == RTTI_INFANTRY) { + CELL mycell = Coord_Cell(Center_Coord()); +// Mark(MARK_UP); + ObjectClass *obj = Map[mycell].Cell_Occupier(); + while (obj) { + if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { + InfantryClass *inf = (InfantryClass *)obj; + if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { + if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { + Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); + } else { + Mem_Copy(inf->Path, Path, sizeof(Path)); + } + if (Path[0] != FACING_NONE) { + skip_path = true; + } + break; + } + } + obj = obj->Next; + } +// Mark(MARK_DOWN); + } +#endif + + if (!skip_path) { + Mark(MARK_UP); + Path[0] = FACING_NONE; // Probably not necessary, but... + + /* + ** Try to find a path to the destination. If a failure occurs, then keep trying + ** with greater determination until either a complete failure occurs, or a decent + ** path was found. + */ + bool found1=false; // Found a best path yet? + PathType path1; + FacingType workpath1[200]; // Staging area for path list. + FacingType workpath2[200]; // Staging area for path list. + MoveType maxtype = MOVE_TEMP; + if (!House->IsHuman) { + maxtype = MOVE_DESTROYABLE; + } else { + + /* + ** For simple movement missions by the human player, then don't + ** consider friendly units as passable if close to the destination. + ** This will prevent a human controlled unit from just sitting next + ** to a destination just because there is another friendly unit + ** occupying the destination location. + */ + if (Mission == MISSION_MOVE && Distance(NavCom) < 0x0280) { + maxtype = MOVE_DESTROYABLE; + } + } + + /* + ** Determine if ANY path could be calculated by first examining the most + ** aggressive case. If this fails, then no path will succeed. Further + ** scanning is unnecessary. + */ + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + + /* + ** Scan for the best path possible. If this succeeds, then do a simple + ** comparison with the most agressive path. If they are very close, then + ** go with the best (easiest) path method. + */ + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); + if (path && path->Cost && path->Cost < MAX((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } else { + + /* + ** The easiest path method didn't result in a satisfactory path. Scan through + ** the rest of the path options, looking for the best one. + */ + for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype; move++) { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + if (path && path->Cost && path->Cost < MAX((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } + +#ifdef OBSOLETE + for (MoveType move = MOVE_CLOAK; move <= maxtype; move++) { + if (!found1) { + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), move); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + if (path1.Cost < 5) break; + } + } else { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + + if (path) { + if (path->Cost && path->Cost <= path1.Cost/2) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } +#endif + + /* + ** If a good path was found, then record it in the object's path + ** list. + */ + if (found1) { + Fixup_Path(&path1); + memcpy(&Path[0], &workpath1[0], MIN(path->Length, (int)sizeof(Path))); + } + + Mark(MARK_DOWN); + } + + +#ifdef NEVER + /* + ** Patch at this point to see if we are moving directly into a + ** MOVE_TEMP. This allows units to bunch up at a bridge even if there + ** is an enormously long way around. This also allows units to give + ** up trying to move into the MOVE_TEMP using the normal movement + ** retry logic. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Coord), Path[0]); + if (Can_Enter_Cell(cell, FACING_NONE) == MOVEF_TEMP) { + Path[0] = FACING_NONE; + } +#endif + + PathDelay = PATH_DELAY; + if (Path[0] != FACING_NONE) return(true); + + /* + ** If a basic path couldn't be determined, then abort the navigation process. + */ +// NavCom = TARGET_NONE; + Stop_Driver(); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * * + * This simple AI script handles moving the vehicle to its desired destination. Since * + * simple movement is handled directly by the engine, this routine merely waits until * + * the unit has reached its destination, and then causes the unit to enter idle mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Move(void) +{ + if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + if (!Target_Legal(TarCom) && !House->IsHuman) { + Target_Something_Nearby(THREAT_RANGE); + } + return(TICKS_PER_SECOND+3); +} + + +/*********************************************************************************************** + * FootClass::Mission_Capture -- Handles the capture mission. * + * * + * Capture missions are nearly the same as normal movement missions. The only difference * + * is that the final destination is handled in a special way so that it is not marked as * + * impassable. This allows the object (usually infantry) the ability to walk onto the * + * object and thus capture it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Capture(void) +{ + if (!Target_Legal(NavCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + if (Map[Coord_Cell(Center_Coord())].Cell_Building()) { + Scatter(0, true); + } + } + return(TICKS_PER_SECOND-2); +} + + +/*********************************************************************************************** + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * * + * This AI routine handles heading to within range of the target and then firing upon * + * it until it is destroyed. If the target is destroyed, then the unit will change * + * missions to match its "idle mode" of operation (usually guarding). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Attack(void) +{ + if (Target_Legal(TarCom)) { + Approach_Target(); + } else { + Enter_Idle_Mode(); + } + return(TICKS_PER_SECOND+2); +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard -- Handles the AI for guarding in place. * + * * + * Units that are performing stationary guard duty use this AI process. They will sit * + * still and target any enemies that get within range. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Guard(void) +{ + if (!Target_Something_Nearby(THREAT_RANGE)) { + Random_Animate(); + } + return(TICKS_PER_SECOND+Random_Picky((int)0, (int)4, (char*)NULL, (int)0)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * * + * This routine is the default hunt order for game objects. It handles searching for a * + * nearby object and heading toward it. The act of targetting will cause it to attack * + * the target it selects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the game tick delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Hunt(void) +{ + if (!Target_Something_Nearby(THREAT_NORMAL)) { + Random_Animate(); + } else { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_E7) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + } else { + Approach_Target(); + } + } + return(TICKS_PER_SECOND+5); +} + + +/*********************************************************************************************** + * FootClass::Mission_Timed_Hunt -- This is the AI process for multiplayer computer units. * + * * + * For multiplayer games, the computer AI can't just blitz the human players; the humans * + * need a little time to set up their base, or whatever. This state just waits for * + * a certain period of time, then goes into hunt mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Timed_Hunt(void) +{ + int rndmax; + int changed = 0; // has the unit changed into Hunt mode? + + if (!House->IsHuman) { + + /* + ** Jump into HUNT mode if we're supposed to Blitz, and the EndCountDown + ** has expired, or if our owning house has lost more than 1/4 of its units + ** (it gets mad at you) + */ + if ( (MPlayerBlitz && House->BlitzTime==0) || + House->CurUnits < ((House->MaxUnit * 4) / 5)) { + Assign_Mission(MISSION_HUNT); + changed = 1; + } + + /* + ** Jump into HUNT mode on a random die roll; the computer units will periodically + ** "snap out" of their daze, and begin hunting. Try to time it so that all + ** units will be hunting within 10 minutes (600 calls to this routine). + */ + if (MPlayerBases) { + rndmax = 5000; + } else { + rndmax = 1000; + } + + if (IRandom(0,rndmax) == 1) { + Assign_Mission(MISSION_HUNT); + changed = 1; + } + + /* + ** If this unit is still just sitting in Timed Hunt mode, call Guard Area + ** so it doesn't just sit there stupidly. + */ + if (!changed) { + Mission_Guard_Area(); + } + } + + return(TICKS_PER_SECOND+Random_Pick(0, 4)); // call me back in 1 second. + +} + + +/*********************************************************************************************** + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * * + * This is the counterpart routine to the Start_Driver function. It clears the driving * + * status flags and destination coordinate record. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Greatly simplified. * + *=============================================================================================*/ +bool FootClass::Stop_Driver(void) +{ + if (HeadToCoord) { + HeadToCoord = NULL; + Set_Speed(0); + IsDriving = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * * + * Before a unit can move it must be started by this routine. This routine handles * + * reserving the cell and setting the driving flag. * + * * + * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * + * away from the unit's current location. * + * * + * OUTPUT: bool; Was driving initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Uses simple spot index finder. * + *=============================================================================================*/ +bool FootClass::Start_Driver(COORDINATE &headto) +{ + Stop_Driver(); + if (headto) { + HeadToCoord = headto; + IsDriving = true; + + /* + ** Check for crate goodie finder here. + */ + if (Map[Coord_Cell(headto)].Goodie_Check(this)) { + return(true); + } + + HeadToCoord = NULL; + IsDriving = false; + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * * + * This routine will determine the sort coordinate for foot class object. This coordinate * + * is usually the coordinate of the object. The exception is if the object is teathered. * + * In this case (presumes offloading to the north), the sorting coordinate is adjusted * + * so that the object will be drawn on top of the transport unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * + *=============================================================================================*/ +COORDINATE FootClass::Sort_Y(void) const +{ + if (IsUnloading) { + return(Coord_Add(Coord, 0x01000000L)); + } + if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { + return(Coord_Add(Coord, 0x01000000L)); + } + return(Coord_Add(Coord, 0x00300000L)); +} + + +/*********************************************************************************************** + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * * + * This routine clears the units' navigation computer in preparation for removal from the * + * game. This is probably called as a result of unit destruction in combat. Clearing the * + * navigation computer ensures that the normal AI process won't start it moving again while * + * the object is undergoing any death animations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Stun(void) +{ + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + Stop_Driver(); + TechnoClass::Stun(); +} + + +/*********************************************************************************************** + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * * + * This routine will set the navigation computer to approach the target indicated by the * + * targeting computer. It is through this function that the unit nears the target so * + * that weapon firing may occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/13/1994 JLB : Made part of TechnoClass. * + * 12/22/1994 JLB : Enhanced search algorithm. * + * 05/20/1995 JLB : Always approaches if the object is off the map. * + *=============================================================================================*/ +void FootClass::Approach_Target(void) +{ + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + + /* + ** If the target is too far away then head toward it. + */ + int maxrange = MAX(Weapon_Range(0), Weapon_Range(1)); + + if (!Target_Legal(NavCom) && (!In_Range(TarCom) || !IsLocked)) { +// if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { + + /* + ** If the object that we are attacking is a building adjust the units + ** max range so that people can stand far away from the buildings and + ** hit them. + */ + BuildingClass * obj = As_Building(TarCom); + if (obj) { + maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + } + + /* + ** Adjust the max range of an infantry unit for where he is standing + ** in the room. + */ + maxrange -= 0x00B7; +#ifdef OBSOLETE + if (What_Am_I() == RTTI_INFANTRY) { + maxrange -= 0x0111; + } else { + maxrange -= 0x00B7; + } +#endif + maxrange = MAX(maxrange, 0); + + COORDINATE tcoord = ::As_Coord(TarCom); + COORDINATE trycoord = 0; + CELL tcell = Coord_Cell(tcoord); + CELL trycell = tcell; + DirType dir = Direction256(tcoord, Center_Coord()); + bool found = false; + + /* + ** Sweep through the cells between the target and the unit, looking for + ** a cell that the unit can enter but which is also within weapon range + ** of the target. If after a reasonable search, no appropriate cell could + ** be found, then the target will be assigned as the movement destination + ** and let "the chips fall where they may." + */ + for (int range = maxrange; range > 0x0080; range -= 0x0100) { + static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; + + for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { + trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); + + if (::Distance(trycoord, tcoord) < range) { + trycell = Coord_Cell(trycoord); + if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { + found = true; + break; + } + } + } + if (found) break; + } + + /* + ** If a suitable intermediate location was found, then head toward it. + ** Otherwise, head toward the enemy unit directly. + */ + if (found) { + Assign_Destination(::As_Target(trycell)); + } else { + Assign_Destination(TarCom); + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * * + * This mission routine causes the unit to scan for targets out to twice its weapon range * + * from the home point. If a target was found, then it will be attacked. The unit will * + * chase the target until it gets up to to its weapon range from the home position. * + * In that case, it will return to home position and start scanning for another target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with time delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 07/27/1995 JLB : Greatly simplified. * + *=============================================================================================*/ +int FootClass::Mission_Guard_Area(void) +{ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { + Assign_Mission(MISSION_HARVEST); + return(1+Random_Pick(1, 10)); + } + + /* + ** Ensure that the archive target is valid. + */ + if (!Target_Legal(ArchiveTarget)) { + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } + + /* + ** Make sure that the unit has not strayed too far from the home position. + ** If it has, then race back to it. + */ + int maxrange = MAX(Weapon_Range(0), Weapon_Range(1))+0x0100; + if (!Target_Legal(NavCom) && (Distance(ArchiveTarget) > maxrange || (!Target_Legal(TarCom) && Distance(ArchiveTarget) > 0x0200))) { + Assign_Target(TARGET_NONE); + Assign_Destination(ArchiveTarget); + } + + if (!Target_Legal(TarCom)) { + COORDINATE old = Coord; + Coord = As_Coord(ArchiveTarget); + Target_Something_Nearby(THREAT_AREA); + Coord = old; + if (Target_Legal(TarCom)) return(1); + } else { + Approach_Target(); + } + return(TICKS_PER_SECOND+Random_Picky((int)0, (int)4, (char*)NULL, (int)0)); +} + + +/*********************************************************************************************** + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * * + * This routine will make sure that the home position for the foot class object gets * + * reset. This is necessary since the home position may change depending on the unit's * + * transition between limbo and non-limbo condition. * + * * + * INPUT: coord -- The coordinate to unlimbo the unit at. * + * * + * dir -- The initial direction to give the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Unlimbo(COORDINATE coord, DirType dir) +{ + /* + ** Try to unlimbo the unit. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Mobile units are always revealed to the house that owns them. + */ + Revealed(House); + + /* + ** Start in a still (non-moving) state. + */ + Path[0] = FACING_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Assign_Mission -- Assign mission to foot class object. * + * * + * When a new mission is assigned, any precalculated path should be truncated. This is * + * in anticipation that the new mission will result in a change of path. * + * * + * INPUT: order -- The new mission to assign to the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Mission(MissionType order) +{ + if (What_Am_I() != RTTI_UNIT || *(UnitClass*)this != UNIT_GUNBOAT) { + Path[0] = FACING_NONE; + } + TechnoClass::Assign_Mission(order); +} + + +/*********************************************************************************************** + * FootClass::Limbo -- Intercepts limbo event and handles FootClass processing. * + * * + * When an object of FootClass type is limboed, then it must be removed from any team * + * it may be a member of. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Limbo(void) +{ + if (!IsInLimbo) { + if (Team) { + Team->Remove(this); + } + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * FootClass::Take_Damage -- Handles taking damage to this object. * + * * + * This routine intercepts the damage assigned to this object and if this object is * + * a member of a team, it informs the team that the damage has occurred. The team may * + * change it's priority or action based on this event. * + * * + * INPUT: damage -- The damage points inflicted on the unit. * + * * + * distance -- The distance from the point of damage to the unit itself. * + * * + * warhead -- The type of damage that is inflicted. * + * * + * source -- The purpitrator of the damage. By knowing who caused the damage, * + * the team know's who to "get even with". * + * * + * OUTPUT: Returns with the result type of the damage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source); + + if (result != RESULT_NONE && Team) { + + if (GameToPlay != GAME_NORMAL || (source && !House->Is_Ally(source))) { + Team->Took_Damage(this, result, source); + } + + } else { + + if (result != RESULT_DESTROYED) { + /* + ** Determine if the target that is current being attacked has a weapon that can + ** do harm to a ground based unit. This information is needed so that an appropriate + ** response will occur when damage is taken. + */ + WeaponType weap = WEAPON_NONE; + if (As_Techno(TarCom)) { + weap = As_Techno(TarCom)->Techno_Type_Class()->Primary; + } + bool tweap = (weap != WEAPON_NONE && weap != WEAPON_NIKE); + + /* + ** This ensures that if a unit is in sticky mode, then it will snap out of + ** it when it takes damage. + */ + if (source && Mission == MISSION_STICKY) { + Enter_Idle_Mode(); + } + + /* + ** If this object is not part of a team and it can retaliate for the damage, then have + ** it try to do so. This prevents it from just sitting there and taking damage. + */ + if ( + source && + !House->Is_Ally(source) && + Techno_Type_Class()->Primary != WEAPON_NONE && + (source->What_Am_I() != RTTI_AIRCRAFT || BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) && + (!Target_Legal(TarCom) || ((!House->IsHuman || Special.IsSmartDefense) && (!tweap || !In_Range(TarCom)))) && +// !Target_Legal(NavCom) && + (Mission == MISSION_AMBUSH || + Mission == MISSION_GUARD || + Mission == MISSION_RESCUE || + Mission == MISSION_GUARD_AREA || + Mission == MISSION_ATTACK || + Mission == MISSION_TIMED_HUNT)) { + + /* + ** Assign the source of the damage as the new target. This occurs for the computer + ** controled units. For the player, this only occurs if the source of the damage + ** is within range. + */ + if (!House->IsHuman) { + + /* + ** If this unit is in TIMED_HUNT (multiplayer computer-controlled) + ** mode, "snap out of it" into HUNT mode; otherwise, assign + ** HUNT as the next mission through the normal mission queue. + */ + if (Mission == MISSION_TIMED_HUNT) { + Set_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_HUNT); + } + Assign_Target(source->As_Target()); + } else { + if (In_Range(source)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Simple retaliation cannot occur because the source of the damage + ** is too far away. If scatter logic is enabled, then scatter now. + */ + if (Special.IsScatter) { + Scatter(0, true); + } + } + } + } else { + + /* + ** If this object isn't doing anything important, then scatter. + */ + if (!IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && Special.IsScatter && What_Am_I() != RTTI_AIRCRAFT) { + Scatter(0, true); + } + } + } + } + return(result); +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Intiates attack or move according to target clicked on. * + * * + * At this level, the object is known to have the ability to attack or move to the * + * target specified (in theory). Perform the attack or move as indicated. * + * * + * INPUT: target -- The target clicked upon that will precipitate action. * + * * + * OUTPUT: Returns with the type of action performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + switch (action) { + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); + } + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD); + break; + + case ACTION_ATTACK: + if (Can_Player_Fire()) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + break; + + case ACTION_ENTER: + if (Can_Player_Move() && object && object->Is_Techno() && !((RadioClass *)object)->In_Radio_Contact()) { + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_SABOTAGE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NOMOVE: + case ACTION_MOVE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NO_DEPLOY: + Speak(VOX_DEPLOY); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * * + * This routine performs the action requested when the left mouse button was clicked over * + * a cell. Typically, this is just a move command. * + * * + * INPUT: action -- The predetermined action that should occur. * + * * + * cell -- The cell number that the action should occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, CELL cell) +{ + switch (action) { + case ACTION_HARVEST: + Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); + break; + + case ACTION_MOVE: + if (AllowVoice) { + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + // Fall into next case. + + case ACTION_NOMOVE: + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].Is_Visible(PlayerPtr)) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + + // MBL 05.15.2020 - Adding support for CTRL+ALT clicking the ground to have units move to an area and guard it + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + Player_Assign_Mission(MISSION_GUARD_AREA, ::As_Target(cell)); + } + break; + // END MBL 05.15.2020 + } +} + + +/*********************************************************************************************** + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * * + * This routine is called as this object moves from cell to cell. When the center of the * + * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * + * the path to the distance to the target. This forces path recalculation in an effort to * + * avoid units passing each other. * + * * + * INPUT: center -- Is this the center of the cell? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/08/1995 JLB : Handles generic enter trigger event. * + * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * + *=============================================================================================*/ +void FootClass::Per_Cell_Process(bool center) +{ +// if (center) { + + /* + ** Clear any unloading flag if necessary. + */ + IsUnloading = false; + + /* + ** If adjacent to an enemy building that has the ability to reveal a stealth tank, + ** then shimmer the cloaked object. + */ + if (Cloak == CLOAKED) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); + + if (Map.In_Radar(cell)) { + TechnoClass const * techno = Map[cell].Cell_Techno(); + + if (techno && !House->Is_Ally(techno) && techno->Techno_Type_Class()->IsScanner) { + Do_Shimmer(); + break; + } + } + } + } + + /* + ** Shorten the path if the target is now within weapon range of this + ** unit and this unit is on an attack type mission. But only if the target + ** is slow enough for leading to make sense. + */ + if (What_Am_I() != RTTI_UNIT || *((UnitClass *)this) != UNIT_GUNBOAT) { + bool inrange = In_Range(TarCom); + TechnoClass const * techno = As_Techno(TarCom); + if (techno && techno->What_Am_I() != RTTI_BUILDING) { + FootClass const * foot = (FootClass const *)techno; + MPHType speed = ((TechnoTypeClass const &)techno->Class_Of()).MaxSpeed; + COORDINATE rangecoord = (speed > MPH_SLOW) ? foot->Likely_Coord() : foot->Target_Coord(); + inrange = In_Range(rangecoord); + } + + if (Target_Legal(TarCom) && (Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + } + } + + /* + ** Trigger event associated with the player entering the cell. + */ + TriggerClass * trigger = Map[Coord_Cell(Coord)].Get_Trigger(); + if (Cloak != CLOAKED && trigger && trigger->House == Owner()) { + trigger->Spring(EVENT_PLAYER_ENTERED, Coord_Cell(Coord)); + } +// } + + TechnoClass::Per_Cell_Process(center); +} + + +/*************************************************************************** + * FootClass::Override_Mission -- temporarily overides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + SuspendedNavCom = NavCom; + TechnoClass::Override_Mission(mission, tarcom, navcom); + + Assign_Destination(navcom); +} + + +/*************************************************************************** + * FootClass::Restore_Mission -- Restores an overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Restore_Mission(void) +{ + if (TechnoClass::Restore_Mission()) { + Assign_Destination(SuspendedNavCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * * + * This routine handles radio message that are related to movement. These are used for * + * complex coordinated maneuvers. * + * * + * INPUT: from -- Pointer to the originator of this radio message. * + * * + * message -- The radio message that is being received. * + * * + * param -- The optional parameter (could be a movement destination). * + * * + * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * + * response is RADIO_ROGER. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + switch (message) { + + /* + ** Intercept the repair request and if this object is moving, then no repair + ** is possible. + */ + case RADIO_REPAIR: + if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); + break; + + /* + ** Something bad has happened to the object in contact with. Abort any coordinated + ** activity with this object. Basically, ... run away! Run away! + */ + case RADIO_RUN_AWAY: + if (In_Radio_Contact()) { + if (NavCom == Contact_With_Whom()->As_Target()) { + Assign_Destination(TARGET_NONE); + } + } + if (Mission == MISSION_SLEEP) { + Assign_Mission(MISSION_GUARD); + Commence(); + } + if (Mission == MISSION_ENTER) { + Assign_Mission(MISSION_GUARD); + } + if (!IsRotating && !Target_Legal(NavCom)) { + Scatter(0, true); + } + break; + + /* + ** Checks to see if this unit needs to move somewhere. If it is already in motion, + ** then it doesn't need furthur movement instructions. + */ + case RADIO_NEED_TO_MOVE: + param = (long)NavCom; + if (!Target_Legal(NavCom)) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Radio request to move to location specified. Typically this is used + ** for complex loading and unloading missions. + */ + case RADIO_MOVE_HERE: + if (NavCom != (TARGET)param) { + if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { + return(RADIO_YEA_NOW_WHAT); + } else { + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + Assign_Destination((TARGET)param); + } + } + return(RADIO_ROGER); + + /* + ** Requests if this unit is trying to cooperatively load up. Typically, this occurs + ** for passengers and when vehicles need to be repaired. + */ + case RADIO_TRYING_TO_LOAD: + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + } + break; + } + return(TechnoClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * * + * This mission handler will cooperatively coordinate the object to maneuver into the * + * object it is in radio contact with. This is used by infantry when they wish to load * + * into an APC as well as by vehicles when they wish to enter a repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Enter(void) +{ + /* + ** If radio contact has not yet been established with the transport, try to + ** establish contact now. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(ArchiveTarget); + if (!techno) techno = As_Techno(NavCom); + if (techno) { + + /* + ** If the transport is already in radio contact, do nothing. Try to + ** establish radio contact later. + */ + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + Assign_Destination(TARGET_NONE); + } + } else { + ArchiveTarget = TARGET_NONE; + Enter_Idle_Mode(); + } + + } else { + + /* + ** Since radio contact exists with the transport, maintain a dialogue so that + ** the transport can give proper instructions to the passenger. + */ + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + Enter_Idle_Mode(); + } + } + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** +z * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * * + * This routine will assign the specified target to the navigation computer. No legality * + * checks are performed. * + * * + * INPUT: target -- The target value to assign to the navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Destination(TARGET target) +{ + NavCom = target; +} + + +/*********************************************************************************************** + * FootClass::Detach_All -- Removes this object from the game system. * + * * + * This routine will remove this object from the game system. This routine is called when * + * this object is about to be deleted. All other objects should no longer reference this * + * object in that case. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach_All(bool all) +{ + if (Team) Team->Remove(this); + Team = NULL; + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * * + * This routine is called when the house determines that it should attack the specified * + * target. This routine will determine if it can attack the target specified and if so, * + * the amount of power it can throw at it. This returned power value is used to allow * + * intelligent distribution of retaliation. * + * * + * INPUT: target -- The target that this object just might be assigned to attack and thus * + * how much power it can bring to bear should be returned. * + * * + * OUTPUT: Returns with the amount of power that this object can bring to bear against the * + * potential target specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Rescue_Mission(TARGET tarcom) +{ + /* + ** If the target specified is not legal, then it cannot be attacked. Always return + ** zero in this case. + */ + if (!Target_Legal(tarcom)) return(0); + + /* + ** If the unit is already assigned to destroy the tarcom then we need + ** to return a negative value which tells the computer to lower the + ** desired threat rating. + */ + if (TarCom == tarcom) { + return(-Risk()); + } + + /* + ** If the unit is currently attacking a target that has a weapon then we + ** cannot abandon it as it will destroy us if we return to base. + */ + if (Target_Legal(TarCom)) { + TechnoClass * techno = As_Techno(TarCom); + if (techno && techno->Techno_Type_Class()->Primary != WEAPON_NONE) { + return(0); + } + } + + /* + ** If the unit is in a harvest mission or is currently attacking + ** something, or is not very effective, then it will be of no help + ** at all. + */ + if (Team || Mission == MISSION_HARVEST || !Risk()) { + return(0); + } + + /* + ** Find the distance to the target modified by the range. If the + ** the distance is 0, then things are ok. + */ + int dist = Distance(tarcom) - Weapon_Range(0); + int threat = Risk() * 256; + int speed = -1; + if (dist > 0) { + + /* + ** Next we need to figure out how fast the unit moves because this + ** decreases the distance penalty. + */ + speed = Max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); + + int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; + + /* + ** Finally modify the threat by the distance the unit is away. + */ + threat = Max(threat/ratio, 1); + } + return(threat); +} + + +/*********************************************************************************************** + * FootClass::Death_Announcement -- Announces the death of a unit. * + * * + * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * + * * + * INPUT: source -- The purpetrator of this death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Death_Announcement(TechnoClass const * source) const +{ + // Changed for multiplayer. ST - 3/13/2019 5:36PM + if ((GameToPlay == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) || (GameToPlay != GAME_GLYPHX_MULTIPLAYER && Is_Discovered_By_Player() || Is_Owned_By_Player())) { // ST + //if (Is_Discovered_By_Player() || Is_Owned_By_Player()) { + //if (IsDiscoveredByPlayer || IsOwnedByPlayer) { + if (!source || source->What_Am_I() != RTTI_INFANTRY || *((InfantryClass const *)source) != INFANTRY_RAMBO) { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const &)Class_Of()).IsCivilian && !((InfantryClass *)this)->IsTechnician) { + // if (Options.IsDeathAnnounce) Speak(VOX_DEAD_CIV); // MBL 02.06.2020 + if (Options.IsDeathAnnounce) Speak(VOX_DEAD_CIV, House, Center_Coord()); + } + else { + if (House != PlayerPtr && GameToPlay != GAME_NORMAL) { + // if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_UNIT); // MBL 02.06.2020 + if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_UNIT, House); + } + else { + if (((GameToPlay == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) || House == PlayerPtr) || Options.IsDeathAnnounce) { // ST + if (!Options.IsDeathAnnounce) { // MBL 02.06.2020 + // Speak(VOX_UNIT_LOST); + Speak(VOX_UNIT_LOST, House, Center_Coord()); + } + else { + switch (House->ActLike) { + case HOUSE_GOOD: + // Speak(VOX_DEAD_GDI); // MBL 02.06.2020 + Speak(VOX_DEAD_GDI, House, Center_Coord()); + break; + + case HOUSE_BAD: + // Speak(VOX_DEAD_NOD); // MBL 02.06.2020 + Speak(VOX_DEAD_NOD, House, Center_Coord()); + break; + + default: + break; + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * * + * This routine will return with the greatest threat (best target) for this object. For * + * movable ground object, they won't automatically return ANY target if this object is * + * cloaked. Otherwise, cloaking is relatively useless. * + * * + * INPUT: method -- The request method (bit flags) to use when scanning for a target. * + * * + * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * + * TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TARGET FootClass::Greatest_Threat(ThreatType method) const +{ + /* + ** If this object can cloak, then it won't select a target automatically. + */ + if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { + return(TARGET_NONE); + } + + if (Techno_Type_Class()->Primary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) { + method = method | THREAT_AIR; + } + if (Techno_Type_Class()->Secondary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Secondary].Fires).IsAntiAircraft) { + method = method | THREAT_AIR; + } + + return(TechnoClass::Greatest_Threat(method|THREAT_GROUND)); +} + + +/*********************************************************************************************** + * FootClass::Detach -- Detaches a target from tracking systems. * + * * + * This routine will detach the specified target from the tracking systems of this object. * + * It will be removed from the navigation computer and any queued mission record. * + * * + * INPUT: target -- The target to be removed from this object. * + * * + * all -- Is the unit really about to be eliminated? If this is true then even * + * friendly contact (i.e., radio) must be eliminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach(TARGET target, bool all) +{ + TechnoClass::Detach(target, all); + + if (!SpecialFlag) { + if (ArchiveTarget == target) { + ArchiveTarget = TARGET_NONE; + } + } + + if (SuspendedNavCom == target) { + SuspendedNavCom = TARGET_NONE; + SuspendedMission = MISSION_NONE; + } + + /* + ** If the navigation computer is assigned to the target, then the navigation + ** computer must be cleared. + */ + if (NavCom == target) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + Restore_Mission(); + } + + /* + ** If targeting the specified object and this unit is obviously heading + ** toward the target to get within range, then abort the path. + */ + if (TarCom == target && House->IsHuman) { + Path[0] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * * + * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * + * from the object. This function is overridden for those objects that can contain * + * Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded from the object. * + * * + * WARNINGS: This routine must be called multiple times in order to completely offload the * + * Tiberium. When this routine return 0, all Tiberium has been offloaded. * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Offload_Tiberium_Bail(void) +{ + return(0); +} + + +/*********************************************************************************************** + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * * + * This routine examines the specified cell to see if the object can enter it. This * + * function is to be overridden for objects that could have the possibility of not being * + * allowed to enter the cell. Typical objects at the FootClass level always return * + * MOVE_OK. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The direction that this cell might be entered from. * + * * + * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * + * blockage. There are various other values that represent other blockage types. * + * The value returned will indicatd the most severe reason why entry into the cell * + * is blocked. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const +{ + return MOVE_OK; +} + + +/*********************************************************************************************** + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * * + * This routine determines if it is legal to sell the object back. A foot class object can * + * only be sold back if it is sitting on a repair bay. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully sold back? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Can_Demolish(void) const +{ + switch (What_Am_I()) { + case RTTI_UNIT: + case RTTI_AIRCRAFT: + if (In_Radio_Contact() && + Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && + *((BuildingClass *)Contact_With_Whom()) == STRUCT_REPAIR && + Distance(Contact_With_Whom()) < 0x0080) { + + return(true); + } + break; + + default: + break; + } + return(TechnoClass::Can_Demolish()); +} + + +/*********************************************************************************************** + * FootClass::Sell_Back -- Causes this object to be sold back. * + * * + * When an object is sold back, a certain amount of money is refunded to the owner and then * + * the object is removed from the game system. * + * * + * INPUT: control -- The action to perform. The only supported action is "1", which means * + * to sell back. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Sell_Back(int control) +{ + if (control != 0) { + if (House == PlayerPtr) { + Sound_Effect(VOC_CASHTURN); + } + House->Refund_Money(Refund_Amount()); + Stun(); + Limbo(); + Delete_This(); + } +} + + +/*********************************************************************************************** + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * * + * This routine comes in handy when determining where a travelling object will be at * + * when considering the amount of time it would take for a normal unit to travel one cell. * + * Using this information, an intelligent "approach target" logic can be employed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate the object is at or soon will be. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE FootClass::Likely_Coord(void) const +{ + if (Head_To_Coord()) { + return(Head_To_Coord()); + } + return(Target_Coord()); +} \ No newline at end of file diff --git a/TIBERIANDAWN/FOOT.H b/TIBERIANDAWN/FOOT.H new file mode 100644 index 000000000..3a76c2e28 --- /dev/null +++ b/TIBERIANDAWN/FOOT.H @@ -0,0 +1,304 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\foot.h_v 2.20 16 Oct 1995 16:47:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FOOT_H +#define FOOT_H + +#include "target.h" +#include "type.h" +#include "techno.h" +#include "ftimer.h" + +class UnitClass; +class BuildingClass; + + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class FootClass : public TechnoClass +{ + public: + /* + ** If this unit has officially joined the team's group, then this flag is + ** true. A newly assigned unit to a team is not considered part of the + ** team until it actually reaches the location where the team is. By + ** using this flag, it allows a team to continue to intelligently attack + ** a target without falling back to regroup the moment a distant member + ** joins. + */ + unsigned IsInitiated:1; + + /* + ** When the player gives this object a navigation target AND that target + ** does not result in any movement of the unit, then a beep should be + ** sounded. This typically occurs when selecting an invalid location for + ** movement. This flag is cleared if any movement was able to be performed. + ** It never gets set for computer controlled units. + */ + unsigned IsNewNavCom:1; + + /* + ** There are certain cases where a unit should perform a full scan rather than + ** the more efficient "ring scan". This situation occurs when a unit first + ** appears on the map or when it finishes a multiple cell movement track. + */ + unsigned IsPlanningToLook:1; + + /* + ** Certain units have the ability to metamorphize into a building. When this + ** operation begins, certain processes must occur. During these operations, this + ** flag will be true. This ensures that any necessary special case code gets + ** properly executed for this unit. + */ + unsigned IsDeploying:1; + + /* + ** This flag tells the system that the unit is doing a firing animation. This is + ** critical to the firing logic. + */ + unsigned IsFiring:1; + + /* + ** This unit could be either rotating its body or rotating its turret. During the + ** process of rotation, this flag is set. By examining this flag, unnecessary logic + ** can be avoided. + */ + unsigned IsRotating:1; + + /* + ** If this object is current driving to a short range destination, this flag is + ** true. A short range destination is either the next cell or the end of the + ** current "curvy" track. An object that is driving is not allowed to do anything + ** else until it reaches its destination. The exception is when infantry wish to + ** head to a different destination, they are allowed to start immediately. + */ + unsigned IsDriving:1; + + /* + ** If this object is unloading from a hover transport, then this flag will be + ** set to true. This handles the unusual case of an object disembarking from the + ** hover lander yet not necessarily tethered but still located in an overlapping + ** position. This flag will be cleared automatically when the object moves to the + ** center of a cell. + */ + unsigned IsUnloading:1; + + /* + ** This is the "throttle setting" of the unit. It is a fractional value with 0 = stop + ** and 255 = full speed. + */ + unsigned char const Speed; + + /* + ** + ** This is the desired destination of the unit. The unit will attempt to head + ** toward this target (avoiding intervening obstacles). + */ + TARGET NavCom; + TARGET SuspendedNavCom; + + /* + ** This points to the team that "owns" this object. This pointer is used to + ** quickly process the team when this object is the source of the change. An + ** example would be if this object were to be destroyed, it would inform the + ** team of this fact by using this pointer. + */ + TeamClass * Team; + + /* + ** If this object is part of a pseudo-team that the player is managing, then + ** this will be set to the team number (0 - 9). If it is not part of any + ** pseudo-team, then the number will be -1. + */ + unsigned char Group; + + /* + ** This points to the next member in the team that this object is part of. This + ** is used to quickly process each team member when the team class is the source + ** of the change. An example would be if the team decided that everyone is going + ** to move to a new location, it would inform each of the objects by chaining + ** through this pointer. + */ + FootClass * Member; + + /* + ** Since all objects derived from this class move according to a path list. + ** This is the path list. It specifies, as a simple list of facings, the + ** path that the object should follow in order to reach its destination. + ** This path list is limited in size, so it might require several generations + ** of path lists before the ultimate destination is reached. The game logic + ** handles regenerating the path list as necessary. + */ + FacingType Path[CONQUER_PATH_MAX]; + + /* + ** When there is a complete findpath failure, this timer is initialized so + ** that a findpath won't be calculated until this timer expires. + */ + TCountDownTimerClass PathDelay; + enum {PATH_DELAY=15,PATH_RETRY=10}; + int TryTryAgain; // Number of retry attempts remaining. + + /* + ** If the object has recently attacked a base, then this timer will not + ** have expired yet. It is used so a building does not keep calling + ** for help from the same attacker. + */ + TCountDownTimerClass BaseAttackTimer; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FootClass(void); + virtual ~FootClass(void); + FootClass(HousesType house); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Basic_Path(void); + + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Can_Demolish(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Likely_Coord(void) const; + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + COORDINATE Head_To_Coord(void) const {return (HeadToCoord);}; + virtual bool Start_Driver(COORDINATE &headto); + virtual bool Stop_Driver(void); + virtual void Assign_Destination(TARGET target); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + virtual bool Mark(MarkType mark); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Stun(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual void Death_Announcement(TechnoClass const * source=0) const; + + /* + ** AI. + */ + virtual void Sell_Back(int control); + virtual int Offload_Tiberium_Bail(void); + virtual TARGET Greatest_Threat(ThreatType method) const; + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Assign_Mission(MissionType order); + virtual int Mission_Enter(void); + virtual int Mission_Move(void); + virtual int Mission_Capture(void); + virtual int Mission_Attack(void); + virtual int Mission_Guard(void); + virtual int Mission_Hunt(void); + virtual int Mission_Timed_Hunt(void); + virtual int Mission_Guard_Area(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Per_Cell_Process(bool center); + virtual void Approach_Target(void); + virtual void Fixup_Path(PathType *) {}; + virtual void Set_Speed(int speed); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + int Optimize_Moves(PathType *path, MoveType threshhold); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + CELL Safety_Point(CELL src, CELL dst, int start, int max); + int Rescue_Mission(TARGET tarcom); + + private: + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + void Debug_Draw_Map(char *txt, CELL start, CELL dest, bool pause); + void Debug_Draw_Path(PathType *path); + bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); + bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); + bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); + + /* + ** This is the coordinate that the unit is heading to + ** as an immediate destination. This coordinate is never further + ** than once cell (or track) from the unit's location. When this coordinate + ** is reached, then the next location in the path list becomes the + ** next HeadTo coordinate. + */ + COORDINATE HeadToCoord; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/FTIMER.H b/TIBERIANDAWN/FTIMER.H new file mode 100644 index 000000000..d3a3bc7fb --- /dev/null +++ b/TIBERIANDAWN/FTIMER.H @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ftimer.h_v 2.14 16 Oct 1995 16:47:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FTIMER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/16/95 * + * * + * Last Update : March 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FTIMER_H +#define FTIMER_H + +/* +** This timer class is based around an external tick system. As such, it is inherently +** in sync with any connected system (through network or modem) that also keeps the external +** tick system in sync. The game frame number is a good sync value. +*/ +class TCountDownTimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TCountDownTimerClass(long set=0) { + Set(set); + }; + + // No destructor. + ~TCountDownTimerClass(void) {} + + operator long(void) const {return Time();}; + + // Public functions + void Set(long set) { + Started = Frame; + DelayTime = set; + }; // Set count down value. + + void Clear(void) { + Started = -1; + DelayTime = 0; + }; + long Get_Start(void) const { + return(Started); + }; + long Get_Delay(void) const { + return(DelayTime); + }; + bool Active(void) const { + return(Started != -1); + }; + int Expired(void) const {return (Time() == 0);}; + long Time(void) const { + long remain = DelayTime - (Frame-Started); + if (remain < 0) remain = 0; + return(remain); + }; // Fetch current count down value. + + protected: + long Started; // Initial frame time start. + long DelayTime; // Ticks remaining before countdown timer expires. +}; + + +#endif diff --git a/TIBERIANDAWN/FUNCTION.H b/TIBERIANDAWN/FUNCTION.H new file mode 100644 index 000000000..94ce1fec3 --- /dev/null +++ b/TIBERIANDAWN/FUNCTION.H @@ -0,0 +1,982 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\function.h_v 2.21 16 Oct 1995 16:46:44 JOE_BOSTIC $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + TurretClass + ³ + TarComClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ + ³ InfantryTypeClass + AircraftTypeClass +#endif + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#if (0) +#ifndef TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#define TRUE_FALSE_DEFINED +#endif //TRUE_FALSE_DEFINED +#endif + +//#define _WIN32 +//#define WIN32 =1 //_LEAN_AND_MEAN +#include + + +/********************************************************************** +** If the following define is enabled, then the memory checking code +** will be disabled. +*/ +#define NOMEMCHECK + +#include "watcom.h" +#define FILE_H +#define WWMEM_H +#include "compat.h" +#include "rawfile.h" +#include "wwlib32.h" +#include "jshell.h" + + + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +// Don't complain if these headers aren't referenced. +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +//#include + +/* +** VQ player specific includes. +*/ +//#include +//#include + +extern bool GameActive; +extern long LParam; + +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" + + +/* +** Greenleaf specific includes. +*/ +//#include +//#include + + +extern long Frame; +inline CELL Coord_XCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+1));} +inline CELL Coord_YCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+3));} +//inline CELL Coord_Cell(COORD coord){return (CELL)(((*(((unsigned short *)&coord)+1) & 0xFF00) >> 2) | *(((unsigned char *)&coord)+1));} +#if (0) +__cdecl CELL Coord_Cell(COORDINATE coord); +#pragma aux Coord_Cell parm [eax] \ + modify [ebx] \ + value [ax] = \ + "mov ebx,eax" \ + "shr eax,010h" \ + "xor al,al" \ + "shr eax,2" \ + "or al,bh" +#endif +CELL __cdecl Coord_Cell(COORDINATE coord); + +#include "rules.h" +#include "utracker.h" +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "slider.h" +#include "list.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +//#include "mapedit.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "teamtype.h" // Team type objects. +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // ??? +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "ipxmgr.h" +#include "combuf.h" +#include "connect.h" +#include "connmgr.h" +#include "noseqcon.h" +#include "msglist.h" +//#include "nullconn.h" +//#include "nullmgr.h" +//#include "phone.h" +#include "loaddlg.h" +#include "ipxaddr.h" +#include "FACINGFF.h" +/**************************************************************************** +** This is a "node", used for the lists of available games & players. The +** 'Game' structure is used for games; the 'Player' structure for players. +*/ +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; + IPXAddressClass Address; + union { + struct { + int Version; + unsigned char IsOpen; + unsigned long LastTime; + } Game; + struct { + HousesType House; + unsigned char Color; + } Player; + }; +} NodeNameType; + + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); + +/* +** AUDIO.CPP +*/ +int Sound_Effect(VocType voc, VolType volume, int variation=1, signed short panvalue=0); +// void Speak(VoxType voice); // MBL 02.06.2020 +void Speak(VoxType voice, HouseClass *house=NULL, COORDINATE coord=0); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord=NULL, int variation=1); +bool Is_Speaking(void); + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Unselect_All_Except(ObjectClass* object); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true); +bool Main_Loop(); +TheaterType Theater_From_Name(char const *name); +//DirType Rotation_Calc(DirType current, DirType desired, int rate); +void Main_Game(int argc, char *argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const *basename); +SourceType Source_From_Name(char const *name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const *shapefile, int shapenum, int frames, int zoomfactor); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0); + +// Added for draw intercept. ST - 1/17/2019 12:31PM +void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, int scale=0x100); +void CC_Draw_Shape(ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, char override_owner = HOUSE_NONE); + +// Added for pip draw intercept - SKY +void CC_Draw_Pip(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0); + +// Added for line draw intercept (ex. Obelisk laser) - SKY +void CC_Draw_Line(int x, int y, int x1, int y1, unsigned char color, int frame, WindowNumberType window); + +void Go_Editor(bool flag); +//long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes); + +char *CC_Get_Shape_Filename(void const *shapeptr ); +void CC_Add_Shape_To_Global(void const *shapeptr, char *filename, char code ); + +void Bubba_Print(char *format,...); + +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +unsigned long Disk_Space_Available(void); + +void Validate_Error(char *name); +void const * Hires_Retrieve(char *name); +int Get_Resolution_Factor(void); + +void Shake_The_Screen(int shakes, HousesType house = HOUSE_NONE); + + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + + +/* +** COORD.CPP +*/ +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); +//void Move_Point(unsigned short &x, unsigned short &y, DirType dir, unsigned short distance); + +/* +** COORDA.CPP +*/ +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +//} + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, unsigned fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, unsigned width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int __cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag); + +/* +** DISPLAY.CPP +*/ +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, ObjectClass* object); +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell); + +ActionType Best_Object_Action(DynamicVectorClass& objects, ObjectClass* object); +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell); + +ObjectClass* Best_Object_With_Action(ObjectClass* object); +ObjectClass* Best_Object_With_Action(CELL cell); + +ActionType Best_Object_Action(ObjectClass* object); +ActionType Best_Object_Action(CELL cell); + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); +bool Bonus_Dialog(void); + +/* +** FINDPATH.CPP +*/ +//PathType * Find_Path(CELL source, CELL dest, FacingType *final_moves, int maxlen, int (*callback)(CELL, FacingType), int threshhold); +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); + +/* +** INI.CPP +*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); +void Write_Scenario_Ini(char *root); +bool Read_Scenario_Ini(char *root, bool fresh=true); +bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const char* root, bool fresh); +int Scan_Place_Object(ObjectClass *obj, CELL cell); + +/* +** INIT.CPP +*/ +void Uninit_Game(void); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); +int Version_Number(void); +void Save_Recording_Values(void); +void Load_Recording_Values(void); + +/* +** JSHELL.CPP +*/ +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); + +/* +** KEYFBUFF.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +#ifdef __cplusplus +} +#endif + +/* +** KEYFRAME.CPP +*/ +int Get_Last_Frame_Length(void); +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Read_MultiPlayer_Settings (void); +void Write_MultiPlayer_Settings (void); +void Read_Scenario_Descriptions (void); +void Free_Scenario_Descriptions(void); +void Computer_Message(void); +int Surrender_Dialog(void); + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(void); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TARGET whom, TARGET target); +bool Queue_Destination(TARGET whom, TARGET target); +bool Queue_Mission(TARGET whom, MissionType mission); +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RAND.CPP +*/ +int Sim_IRandom(int minval, int maxval); +int Sim_Random(void); + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass *team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(FileClass &file); +bool Save_Misc_Values(FileClass &file); +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep); +bool Load_Game(int id); +bool Load_Game(const char *file_name); +//bool Read_Object (void *ptr, int base_size, int class_size, FileClass & file, void * vtable); // Original Read_Object prototype. ST - 9/17/2019 12:50PM +bool Read_Object(void *ptr, int class_size, FileClass & file, bool has_vtable); +bool Save_Game(int id,char *descr); +bool Save_Game(const char *file_name, const char *descr); +bool Write_Object (void *ptr, int class_size, FileClass & file); +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr); +TechnoTypeClass const * Target_To_TechnoType(TARGET target); +void * Get_VTable(void *ptr, int base_size); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); +void Set_VTable(void *ptr, int base_size, void *vtable); + +/* +** SCENARIO.CPP +*/ +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +void Set_Scenario_Difficulty(int difficulty); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); + +/* +** SCORE.CPP +*/ +void Map_Selection(void); +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicViewPortClass *seen , int delay=0, int dagger=0); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); +extern GraphicBufferClass *PseudoSeenBuff; + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(void); + +/* +** SUPPORT.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +void __cdecl Remove_From_List(void **list, int *index, void * ptr); +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac); +void __cdecl Fat_Put_Pixel(int x, int y, int color, int size, GraphicViewPortClass &); +void __cdecl strtrim(char *buffer); +long __cdecl Get_EAX( void ); +#ifdef __cplusplus +} +#endif + +/* +** TARCOM.CPP +*/ + +/* +** TARGET.CPP +*/ +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target, bool check_active = true); +AnimClass * As_Animation(TARGET target, bool check_active = true); +BuildingClass * As_Building(TARGET target, bool check_active = true); +BulletClass * As_Bullet(TARGET target, bool check_active = true); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target, bool check_active = true); +TeamClass * As_Team(TARGET target, bool check_active = true); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target, bool check_active = true); +//TerrainClass * As_Terrain(TARGET target); +TriggerClass * As_Trigger(TARGET target, bool check_active = true); +UnitClass * As_Unit(TARGET target, bool check_active = true); +bool Target_Legal(TARGET target); +ObjectClass * As_Object(TARGET target, bool check_active = true); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** Inline miscellaneous functions. +*/ +#define XYP_COORD(x,y) (unsigned long)(((x)*ICON_LEPTON_W)/CELL_PIXEL_W + ((((y)*ICON_LEPTON_H)/CELL_PIXEL_H)<<16)) +inline FacingType Dir_Facing(DirType facing) {return (FacingType)(((unsigned char)(facing+0x10)&0xFF)>>5);} +inline DirType Facing_Dir(FacingType facing) {return (DirType)((int)facing << 5);} +inline int Cell_To_Lepton(int cell) {return cell<<8;} +inline int Lepton_To_Cell(int lepton) {return ((unsigned)(lepton + 0x0080))>>8;} +inline CELL XY_Cell(int x, int y) {return ((CELL)(((y)<<6)|(x)));} +inline COORDINATE XY_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG(y, x));} +inline int Coord_X(COORDINATE coord) {return (short)(LOW_WORD(coord));} +inline int Coord_Y(COORDINATE coord) {return (short)(HIGH_WORD(coord));} +inline int Cell_X(CELL cell) {return (int)(((unsigned)cell) & 0x3F);} +inline int Cell_Y(CELL cell) {return (int)(((unsigned)cell) >> 6);} +inline int Dir_Diff(DirType dir1, DirType dir2) {return (int)(*((signed char*)&dir2) - *((signed char*)&dir1));} +inline CELL Coord_XLepton(COORDINATE coord) {return (CELL)(*((unsigned char*)&coord));} +inline CELL Coord_YLepton(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+2));} +//inline COORD CellXY_Coord(unsigned x, unsigned y) {return (COORD)(MAKE_LONG(y<<8, x<<8));} +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) + *((short*)(&coord2)+1)), (*((short*)(&coord1)) + *((short*)(&coord2))));} +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) - *((short*)(&coord2)+1)), (*((short*)(&coord1)) - *((short*)(&coord2))));} +inline COORDINATE Coord_Snap(COORDINATE coord) {return (COORDINATE)MAKE_LONG((((*(((unsigned short *)&coord)+1))&0xFF00)|0x80), (((*((unsigned short *)&coord))&0xFF00)|0x80));} +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((unsigned short *)(&coord1)+1) + *((unsigned short *)(&coord2)+1))>>1, (*((unsigned short *)(&coord1)) + *((unsigned short *)(&coord2)))>>1);} +inline COORDINATE Cell_Coord(CELL cell) {return (COORDINATE) MAKE_LONG( (((cell & 0x0FC0)<<2)|0x80), ((((cell & 0x003F)<<1)+1)<<7) );} +inline COORDINATE XYPixel_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG((int)(((long)y*(long)ICON_LEPTON_H)/(long)ICON_PIXEL_H)/*+LEPTON_OFFSET_Y*/, (int)(((long)x*(long)ICON_LEPTON_W)/(long)ICON_PIXEL_W)/*+LEPTON_OFFSET_X*/));} +//inline int Facing_To_16(int facing) {return Facing16[facing];} +inline int Facing_To_32(DirType facing) {return Facing32[facing];} +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +//inline int Direction16(COORDINATE coord1, COORD coord2) {return (Desired_Facing16(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(CELL cell1, CELL cell2) {return (DirType)(Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) {return (Coord_Snap(Coord_Add(AdjacentCoord[dir & 0x07], coord)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) {return Adjacent_Cell(coord, Dir_Facing(dir));} +inline CELL Adjacent_Cell(CELL cell, FacingType dir) {return (CELL)(cell + AdjacentCell[dir]);} +inline CELL Adjacent_Cell(CELL cell, DirType dir) {return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]);} +inline int Lepton_To_Pixel(int lepton) {return ((lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2) - ((lepton < 0) ? (ICON_LEPTON_W - 1) : 0)) / ICON_LEPTON_W;} +inline int Pixel_To_Lepton(int pixel) {return ((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2) - ((pixel < 0) ? (ICON_PIXEL_W - 1) : 0)) / ICON_PIXEL_W;} +//inline FacingType Facing_To_8(DirType facing) {return (FacingType)(((unsigned char)(facing|0x10))>>5);} +inline COORDINATE XYP_Coord(int x,int y) {return XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));}; +inline char const * Text_String(int string) {return(Extract_String(SystemStrings, string));}; + +// +// We need to know when the visible page changes +// ST - 1/4/2019 10:31AM +// +void Blit_Hid_Page_To_Seen_Buff(void); +extern bool RunningAsDLL; + + +template inline T Random_Picky(T a, T b, char *sfile, int line) +{ + sfile = sfile; + line = line; + return (T)IRandom((int)a, (int)b); //, sfile, line); +}; + +#define Random_Pick(low, high) Random_Picky ( (low), (high), __FILE__, __LINE__) + + +template inline T Sim_Random_Pick(T a, T b) +{ + return (T)Sim_IRandom((int)a, (int)b); +}; + + +#ifdef CHEAT_KEYS +#define Check_Ptr(ptr,file,line) \ +{ \ + if (!ptr) { \ + Mono_Clear_Screen(); \ + Mono_Printf("NULL Pointer, Module:%s, line:%d!\n",file,line); \ + Prog_End(); \ + exit(EXIT_SUCCESS); \ + } \ +} +#else +#define Check_Ptr(ptr,file,line) +#endif + +/* +** These routines are for coding & decoding multiplayer ID's +*/ +inline PlayerColorType MPlayerID_To_ColorIndex(unsigned short id) {return (PlayerColorType)(id >> 4);} +inline HousesType MPlayerID_To_HousesType(unsigned short id) {return ((HousesType)(id & 0x000f)); } +inline unsigned short Build_MPlayerID(int c_idx, HousesType htype) { return ((c_idx << 4) | htype); } + + + + +// +// True if we are the currently in focus windows app +// +extern bool GameInFocus; + +extern int ScreenWidth; +extern int ScreenHeight; +extern "C" void ModeX_Blit (GraphicBufferClass *source); +extern void Colour_Debug (int call_number); + + +extern unsigned char *InterpolatedPalettes[100]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; + +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; +} + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); + + +#define CELL_BLIT_ONLY 1 +#define CELL_DRAW_ONLY 2 + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2); + +#if (0) +#pragma aux Distance_Coord parm [eax] [ebx] \ + modify [edx ebx] \ + value [eax] = \ + "mov dx,ax" \ + "sub dx,bx" \ + "jg okx" \ + "neg dx" \ + "okx:" \ + "shr eax,16" \ + "shr ebx,16" \ + "sub ax,bx" \ + "jg oky" \ + "neg ax" \ + "oky:" \ + "cmp ax,dx" \ + "jg ok" \ + "xchg ax,dx" \ + "ok:" \ + "shr dx,1" \ + "add ax,dx" +#endif + +inline int Distance(COORDINATE coord1, COORDINATE coord2) +{ +#ifdef NEVER + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +#else + return(Distance_Coord(coord1, coord2)); +#endif +} + + + +/*********************************************************************************************** + * Distance -- Determines the cell distance between two cells. * + * * + * Use this routine to determine the distance between the two cells specified. The distance * + * is returned in cells. * + * * + * INPUT: cell1, cell2 -- The two cells to determine the distance between. * + * * + * OUTPUT: Returns with the distance between the two cells in units of cell size. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +inline int Distance(CELL coord1, CELL coord2) +{ + int diff1, diff2; + + diff1 = Cell_Y(coord1) - Cell_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Cell_X(coord1) - Cell_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Number -- Returns the cell ID number for this cell object. * + * * + * Call this routine if you wish to determine what the cell number ID is for the currrent * + * cell object. This ID number is the index number into the cell array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number for this cell object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +inline CELL CellClass::Cell_Number(void) const +{ + return(Map.ID(this)); +} +#if(0) +#ifndef NOMEMCHECK +#define NO_INTERCEPT +#include "memcheck.h" +#endif +#endif + +void WWDOS_Shutdown(void); + +#endif + + + +/* +** Debug output. ST - 6/27/2019 10:00PM +*/ +void GlyphX_Debug_Print(const char *debug_text); + +/* +** Achievement event. ST - 11/11/2019 11:39AM +*/ +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); \ No newline at end of file diff --git a/TIBERIANDAWN/FUSE.CPP b/TIBERIANDAWN/FUSE.CPP new file mode 100644 index 000000000..5390aa8b3 --- /dev/null +++ b/TIBERIANDAWN/FUSE.CPP @@ -0,0 +1,193 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\fuse.cpv 2.18 16 Oct 1995 16:50:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FuseClass::FuseClass -- Constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 BRR : Created. Gosh, what a lotta work. * + *=============================================================================================*/ +FuseClass::FuseClass(void) +{ + Timer = 0; + Arming = 0; + HeadTo = 0; + Proximity = 0; +} + + +/*********************************************************************************************** + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * * + * This starts a fuse. Fuses are proximity detonation variety but * + * can be modified to have a minimum time to elapse before detonation * + * and a maximum time to exist before detonation. Typically, the * + * timing values are used for missiles that have a minimum arming * + * distance and a limited amount of fuel. * + * * + * INPUT: location -- The coordinate where the projectile start. This * + * is needed for proper proximity tracking. * + * * + * target -- The actual impact point. Fuses are based on real * + * word coordinates. * + * * + * time -- The maximum time that the fuse may work before * + * explosion is forced. * + * * + * arming -- The minimum time that must elapse before the * + * fuse may explode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Arm_Fuse(COORDINATE location, COORDINATE target, int timeto, int arming) +{ + timeto = MAX(timeto, arming); + Timer = MIN(timeto, 0xFF); + Arming = MIN(arming, 0xFF); + HeadTo = target; + Proximity = Distance(location, target); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * * + * This will process the fuse and update the internal clocks as well * + * as check to see if the fuse should trigger (explode) or not. * + * * + * INPUT: newlocation -- The new location of the fuse. This is needed * + * to determine proximity explosions. * + * * + * OUTPUT: bool; Was the fuse triggered to explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +bool FuseClass::Fuse_Checkup(COORDINATE newlocation) +{ + int proximity; + + /* + ** Always decrement the fuse timer. + */ + if (Timer) Timer--; + + /* + ** If the arming countdown has not expired, then do nothing. + */ + if (Arming) { + Arming--; + } else { + + /* + ** If the timer has run out, then the warhead explodes. + */ + if (!Timer) return(true); + + proximity = Distance(newlocation, HeadTo); + if (proximity < 0x0010) return(true); + if (proximity < ICON_LEPTON_W && proximity > Proximity) { + return(true); + } + Proximity = proximity; + } + return(false); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * * + * Use this routine to output the fuse class data to the save game file specified. * + * * + * INPUT: file -- The file to output the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Write(FileClass & file) +{ + file.Write(&Timer, sizeof(Timer)); + file.Write(&Arming, sizeof(Arming)); + file.Write(&HeadTo, sizeof(HeadTo)); + file.Write(&Proximity, sizeof(Proximity)); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * * + * Use this routine to input the fuse class data from the save game file specified. * + * * + * INPUT: file -- The file to input the data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Read(FileClass & file) +{ + file.Read(&Timer, sizeof(Timer)); + file.Read(&Arming, sizeof(Arming)); + file.Read(&HeadTo, sizeof(HeadTo)); + file.Read(&Proximity, sizeof(Proximity)); +} + diff --git a/TIBERIANDAWN/FUSE.H b/TIBERIANDAWN/FUSE.H new file mode 100644 index 000000000..9714aba64 --- /dev/null +++ b/TIBERIANDAWN/FUSE.H @@ -0,0 +1,92 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\fuse.h_v 2.17 16 Oct 1995 16:46:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUSE_H +#define FUSE_H + +/**************************************************************************** +** The fuse is used by projectiles to determine whether detonation should +** occur. This is usually determined by tracking the distance to the +** designated target reaches zero or when the timer expires. +*/ +class FuseClass { + public: + FuseClass(void); + void Arm_Fuse(COORDINATE location, COORDINATE target, int time=0xFF, int arming=0); + bool Fuse_Checkup(COORDINATE newlocation); + void Fuse_Write(FileClass & file); + void Fuse_Read(FileClass & file); + COORDINATE Fuse_Target(void); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Fuses can detonate if enough time has elapsed. This value counts + ** down. When it reaches zero, detonation occurs. + */ + unsigned char Timer; + + private: + + /* + ** Some fuses need a certain amount of time before detonation can + ** occur. This counts down and when it reaches zero, normal fuse + ** detonation checking can occur. + */ + unsigned char Arming; + + /* + ** This is the designated impact point of the projectile. The fuse + ** will trip when the closest point to this location has been reached. + */ + COORDINATE HeadTo; + + /* + ** This is the running proximity value to the impact point. This value + ** will progressively get smaller. Detonation occurs when it reaches + ** zero or when it starts to grow larger. + */ + short Proximity; +}; + +inline COORDINATE FuseClass::Fuse_Target(void) +{ + return(HeadTo); +} + +#endif diff --git a/TIBERIANDAWN/GADGET.CPP b/TIBERIANDAWN/GADGET.CPP new file mode 100644 index 000000000..bfdbaf5e9 --- /dev/null +++ b/TIBERIANDAWN/GADGET.CPP @@ -0,0 +1,801 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gadget.cpv 2.18 16 Oct 1995 16:49:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : 01/03/95 * + * * + * Last Update : July 8, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GadgetClass::Action -- Base action for gadget. * + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * GadgetClass::Disable -- Disables the gaget from input processing. * + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * GadgetClass::Enable -- Enables the gadget. * + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * GadgetClass::Remove -- Removes the specified gagdet from the list. * + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +/* +** This records the current gadget the the gadget system is "stuck on". Such a +** gadget will be processed to the exclusion of all others until the mouse button +** is no longer pressed. +*/ +GadgetClass * GadgetClass::StuckOn = 0; + +/* +** This is a copy of a pointer to the last list used by the gadget input system. +** If a change of list is detected, then all gadgets are forced to be redrawn. +*/ +GadgetClass * GadgetClass::LastList = 0; + + +/* +** This points to the gadget that is intercepting all keyboard events. +*/ +GadgetClass * GadgetClass::Focused = 0; + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * * + * This is the normal constructor for gadget objects. A gadget object is only concerned * + * with the region on the screen to considered "its own" as well as the flags that tell * + * what mouse action should be recognized when the mouse is over this screen area. * + * * + * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * + * will be "owned" by this gadget. * + * * + * w,h -- Width and height (in pixels) of this gadget's region. * + * * + * flags -- The flags (mouse conditions) that will cause this gadget's action * + * function to be called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) +{ + X = x; + Y = y; + Width = w; + Height = h; + Flags = flags; + IsToRepaint = false; + IsSticky = sticky; + IsDisabled = false; + + if (IsSticky) { + Flags |= LEFTPRESS|LEFTRELEASE; + } +} + + +/*********************************************************************************************** + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * * + * This is the destructor for the gadget object. It will clear the focus from this gadget * + * if this gadget currently has the focus. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass::~GadgetClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*************************************************************************** + * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * + * area and the appropriate flag is set, then call Action(). * + * * + * INPUT: int key, int mousex, int mousey * + * * + * OUTPUT: true or false * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +{ + /* + ** Set flags to match only those events that occur AND are being looked for. If + ** the result is NULL, then we know that this button should be ignored. + */ + flags &= Flags; + + /* + ** If keyboard input should be processed by this "gadget" and keyboard input is + ** detected, then always call the action function. It is up to the action function + ** in this case to either ignore the keyboard input or not. + ** + ** For mouse actions, check to see if the mouse is in the region of the button + ** before calling the associated action function. This is the typical action for + ** buttons. + */ + if (this == StuckOn || + (flags & KEYBOARD) || + (flags && (mousex - X) < Width && (mousey - Y) < Height)) { + + return(Action(flags, key)); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Enable -- Enables the gadget. * + * * + * This function enables the gadget. An enabled gadget will be processed for input * + * purposes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Enable(void) +{ + IsDisabled = false; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Disable -- Disables the gaget from input processing. * + * * + * This routine will disable the gadget. A disabled gadget might be rendered, but is * + * ignored for input processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Disable(void) +{ + IsDisabled = true; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Remove -- Removes the specified gagdet from the list. * + * * + * Use this routine if an individual gadget needs to be removed from the list of gadgets. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * + * gadget wasn't in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Remove(void) +{ + Clear_Focus(); + return(GadgetClass *)LinkClass::Remove(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * * + * This returns with the next gadget's pointer. It is identical to the base Get_Next() * + * function, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the next gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Next(void) const +{ + return(GadgetClass*)LinkClass::Get_Next(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * * + * This routine will return the previous gadget in the list. It is identical to the base * + * function Get_Prev, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Prev(void) const +{ + return(GadgetClass*)LinkClass::Get_Prev(); +} + + +/*********************************************************************************************** + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * * + * This function will delete all gadgets in the list. It is the counterpart to the * + * Create_One_Of functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Any references to these gadget become invalidated by this routine. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Delete_List(void) +{ + GadgetClass * g = this; + + /* + ** Move to head of the list. + */ + while (g->Get_Prev()) { + g = g->Get_Prev(); + } + + /* + ** First delete all the gadgets following the first one. The reason the first one + ** is kept around is that sometimes deleting one gadget will result in related gadgets + ** in the same list also being deleted. The first gadget will always contain the + ** correct gadget pointer. + */ + while (g) { + g->Clear_Focus(); + + GadgetClass * temp = g; + g = g->Get_Next(); + delete temp; + } +} + + +/*********************************************************************************************** + * GadgetClass::Action -- Base action for gadget. * + * * + * This handles the base level action that a gadget performs when a qualifying input event * + * is detected. This sets the redraw flag and returns true (to stop further processing). * + * If no qualifying input event was detected, but this routine was called anyway, then * + * don't perform any action. The call to this routine, in that case, must have been forced * + * for some other reason. * + * * + * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * + * no qualifying event occured. * + * * + * OUTPUT: bool; Should further gadget list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Action(unsigned flags, KeyNumType &) +{ + /* + ** If any of the event flags are active, then this indicates that something probably + ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that + ** any sticky flags are cleared up. + */ + if (flags) { + IsToRepaint = true; + Sticky_Process(flags); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * * + * At this level, there is no actual rendering taking place with the call to Draw_Me, but * + * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * + * returns true, they will perform their custom rendering. * + * * + * INPUT: forced -- Is this redraw forced by outside circumstances? * + * * + * OUTPUT: bool; Should the gadget imagery be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Draw_Me(int forced) +{ + if (forced || IsToRepaint) { + IsToRepaint = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * * + * Use this function to cause all gadget in the list to be redrawn regardless of the state * + * of the IsToRepaint flag. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +void GadgetClass::Draw_All(bool forced) +{ + GadgetClass *gadget = this; + + while (gadget != NULL) { + gadget->Draw_Me(forced); + gadget = gadget->Get_Next(); + } +} + + +/*************************************************************************** + * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* + * * + * INPUT: none. * + * * + * OUTPUT: key pressed. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +KeyNumType GadgetClass::Input(void) +{ + int mousex, mousey; + KeyNumType key; + unsigned flags; + int forced = false; + + /* + ** Record this list so that a forced redraw only occurs the FIRST time the + ** gadget list is passed to this routine. + */ + if (LastList != this) { + LastList = this; + forced = true; + StuckOn = NULL; + Focused = NULL; + } + + /* + ** Fetch any pending keyboard input. + */ + key = Keyboard::Check(); + if (key) { + key = Keyboard::Get(); + } + +#ifdef SCENARIO_EDITOR + + if ( key == KN_K ){ + /* + ** time to create a screen shot using the PCX code (if it works) + */ + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + if (lp < 10) { + sprintf(filename, "scrsht0%d.pcx", lp); + } else { + sprintf(filename, "scrsht%d.pcx", lp); + } + if (access(filename, F_OK) == -1) + break; + } + + Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette); + //Map.Place_Random_Crate(); + } + +#endif //SCENARIO_EDITOR + + /* + ** For mouse button clicks, the mouse position is actually held in the MouseQ... + ** globals rather than their normal Mouse... globals. This is because we need to + ** know the position of the mouse at the exact instant when the click occured + ** rather the the mouse position at the time we get around to this function. + */ + if (((key&0x10FF) == KN_LMOUSE) || ((key&0x10FF) == KN_RMOUSE)) { + mousex = _Kbd->MouseQX; + mousey = _Kbd->MouseQY; + } else { + mousex = Get_Mouse_X(); + mousey = Get_Mouse_Y(); + } + + /* + ** Set the mouse button state flags. These will be passed to the individual + ** buttons so that they can determine what action to perform (if any). + */ + flags = 0; + if (key) { + if (key == KN_LMOUSE) { + flags |= LEFTPRESS; + } + if (key == KN_RMOUSE) { + flags |= RIGHTPRESS; + } + if (key == (KN_LMOUSE | KN_RLSE_BIT)) { + flags |= LEFTRELEASE; + } + if (key == (KN_RMOUSE | KN_RLSE_BIT)) { + flags |= RIGHTRELEASE; + } + } + + /* + ** If the mouse wasn't responsible for this key code, then it must be from + ** the keyboard. Flag this fact. + */ + if (key && !flags) { + flags |= KEYBOARD; + } + + /* + ** Mouse button up or down action is ignored if there is a keyboard event. This + ** allows keyboard events to fall through normally even if the mouse is over a + ** gadget that is flagged for LEFTUP or RIGHTUP. + */ + if (!key) { + + /* + ** Check for the mouse being held down. We can't use the normal input system + ** for this, so we must examine the actual current state of the mouse + ** buttons. As a side note, if we determine that the mouse button isn't being + ** held down, then we automatically know that it must be up -- set the flag + ** accordingly. + */ + if (Keyboard::Down(KN_LMOUSE)) { + flags |= LEFTHELD; + } else { + flags |= LEFTUP; + } + if (Keyboard::Down(KN_RMOUSE)) { + flags |= RIGHTHELD; + } else { + flags |= RIGHTUP; + } + } + + /* + ** If "sticky" processing is active, then only process the stuck gadget. + */ + if (StuckOn) { + StuckOn->Draw_Me(false); + StuckOn->Clicked_On(key, flags, mousex, mousey); + if (StuckOn) { + StuckOn->Draw_Me(false); + } + } else { + + /* + ** If there is a gadget that has the keyboard focus, then route all keyboard + ** events to it. + */ + if (Focused && (flags & KEYBOARD)) { + Focused->Draw_Me(false); + Focused->Clicked_On(key, flags, mousex, mousey); + if (Focused) { + Focused->Draw_Me(false); + } + } else { + + /* + ** Sweep through all the buttons in the chain and pass the current button state + ** and keyboard input data to them. These routines will detect whether they should + ** perform some action and return a flag to this effect. They also have the option + ** of changing the key value so that an appropriate return value is use for this + ** processing routine. + */ + GadgetClass *next_button = this; + while (next_button != NULL) { + + /* + ** Maybe redraw the button if it needs to or is being forced to redraw. + */ + next_button->Draw_Me(forced); + + if (!next_button->IsDisabled) { + + /* + ** Process this button. If the button was recognized and action was + ** performed, then bail from further processing (speed reasons?). + */ + if (next_button->Clicked_On(key, flags, mousex, mousey)) { + + /* + ** Some buttons will require repainting when they perform some action. + ** Do so at this time. + */ + next_button->Draw_Me(false); + break; + } + } + + next_button = next_button->Get_Next(); + } + } + } + return(key); +} + + +/*********************************************************************************************** + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * * + * This examines the gadget list looking for on that has the same ID as specified. If that * + * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * + * or ones derived from it can have an ID value, we know that the returned pointer is at * + * least of the ControlClass type. * + * * + * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * + * a NULL will always be returned. * + * * + * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * + * If no matching gadget was found, then NULL is returned. * + * * + * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * + * will return a pointer to the first one only. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass * GadgetClass::Extract_Gadget(unsigned id) +{ + GadgetClass * g = this; + + if (id) { + while (g) { + if (g->Get_ID() == id) { + return((ControlClass *)g); + } + g = g->Get_Next(); + } + } + return(0); +} + + +/*********************************************************************************************** + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * * + * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * + * Draw_Me function called at the next available opportunity. Usually, this is the next * + * time the Input() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Flag_To_Redraw(void) +{ + IsToRepaint = true; +} + + +/*********************************************************************************************** + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * * + * This function examines the event flags and handles any "sticky" processing required. * + * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * + * be processed to the exclusion of all other gadgets while the mouse button is held * + * down. * + * * + * INPUT: flags -- The event flags that triggered the call to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Sticky_Process(unsigned flags) +{ + if (IsSticky && (flags & LEFTPRESS)) { + StuckOn = this; + } + if (StuckOn == this && (flags & LEFTRELEASE)) { + StuckOn = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * * + * This will set the focus to this gadget regardless of any current focus setting. If there * + * is another gadget that has focus, it will have its focus cleared before this gadget will * + * get the focus. A focused gadget is one that has all keyboard input routed to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Focus(void) +{ + if (Focused) { + Focused->Flag_To_Redraw(); + Focused->Clear_Focus(); + } + Flags |= KEYBOARD; + Focused = this; +} + + +/*********************************************************************************************** + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * * + * Use this function to clear the focus for the gadget. If the gadget doesn't currently * + * have focus, then this routine will do nothing. For added functionality, overload this * + * virtual function so that gadget specific actions may be take when focus is lost. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Clear_Focus(void) +{ + if (Focused == this) { + Flags &= ~KEYBOARD; + Focused = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * * + * If this object has the keyboard focus, then this routine will return true. When the * + * gadget has keyboard focus, all keyboard events get routed to the gadget. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this gadget have the keyboard focus? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool GadgetClass::Has_Focus(void) +{ + return(this == Focused); +} + +/*********************************************************************************************** + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * * + * This function is mostly for supporting HidPage drawing. If it returns true, it means * + * the application needs to re-blit the HidPage forward, after calling the list's Input(). * + * * + * INPUT: none * + * * + * OUTPUT: true = an item needs redrawing, false = no items need redrawing * + * * + * WARNINGS: It is assumed 'this' is the head of the list. * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +int GadgetClass::Is_List_To_Redraw(void) +{ + GadgetClass *gadget = this; + + while (gadget != NULL) { + if (gadget->IsToRepaint) + return (true); + gadget = gadget->Get_Next(); + } + return (false); +} + + diff --git a/TIBERIANDAWN/GADGET.H b/TIBERIANDAWN/GADGET.H new file mode 100644 index 000000000..3fe8fad5d --- /dev/null +++ b/TIBERIANDAWN/GADGET.H @@ -0,0 +1,245 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gadget.h_v 2.17 16 Oct 1995 16:46:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.H * + * * + * Programmer : Maria del Mar McCready Legg * + * * + * Start Date : January 3, 1995 * + * * + * Last Update : January 3, 1995 [MML] * + * * + * * + * LinkClass [This is the linked list manager class. It keeps a record * + * ³ of the next and previous gadget in the list. It is possible * + * ³ delete a gadget out of the middle of the list with this * + * ³ class.] * + * ³ * + * GadgetClass [The is the basic gadget class. It handles processing of * + * ³ input events and dispatching the appropriate functions. * + * ³ All gadgets must be derived from this class.] * + * ÃÄÄÄÄ¿ * + * ³ ³ * + * ³ ListClass [Ths list class functions like a list box does in Windows. It * + * ³ keeps track of a list of text strings. This list can be * + * ³ scrolled and an item selected. If the list becomes larger than * + * ³ can be completely displayed, it will automatically create a * + * ³ slider (at the right edge) to manage the scrolling.] * + * ³ * + * ControlClass [This class adds the concept of giving an ID number to the * + * ³ gadget. This ID can then be returned from the Input() * + * ³ function as if it were a pseudo-keystroke. Additionally, * + * ³ the ability to inform another button that this button has * + * ³ been actioned is allowed. This ability allows one button * + * ³ to watch what happens to another button. Example: a list * + * ³ box gadget can tell when an attached slider has been * + * ³ touched.] * + * ÚÄÄÄÄÄÄÄÅÄÄÄÄ¿ * + * ³ ³ ³ * + * ³ ³ GaugeClass [This class looks similar to Windows slider, but has * + * ³ ³ ³ a different controlling logic. There is no thumb and * + * ³ ³ ³ it serves as a simple variable control setting. This * + * ³ ³ ³ is analagous to a volume slider.] * + * ³ ³ ³ * + * ³ ³ SliderClass [The slider class is similar to the typical Windows slider. It * + * ³ ³ has a current setting, a thumb, and a controlable scale. This * + * ³ ³ is the object created to handle a scrolling list box.] * + * ³ ³ * + * ³ EditClass * + * ³ * + * ³ * + * ToggleClass [The toggle class is used for buttons that have an image and behave just * + * ³ like the buttons in Windows do. That is, they have a separate visual for * + * ³ when they are pressed and raised. They are officially triggered (return * + * ³ their ID number) when the mouse button is released while over the button. * + * ³ This class doesn't perform any rendering itself. It merely provides the * + * ³ logic so that the derived classes will function correctly.] * + * ÚÄÁÄÄÄÄ¿ * + * ³ ³ * + * ³ TextButtonClass [The text button functions like a normal Windows style button, but * + * ³ the imagery is based on text that is displayed on the button. A * + * ³ typical example would be the "OK" or "Cancel" buttons.] * + * ³ * + * ShapeButtonClass [The shape buttons is similar to the TextButton but instead of text * + * being used to give the button its imagery, an actual shape is used * + * instead. This allows graphic buttons. These are similar to the up/down * + * arrows seen in a Windows slider.] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GADGET_H +#define GADGET_H + +#include "link.h" + +class ControlClass; + +class GadgetClass : public LinkClass +{ + public: + typedef enum FlagEnum { + LEFTPRESS = 0x0001, // Left mouse button press. + LEFTHELD = 0x0002, // Left mouse button is being held down. + LEFTRELEASE = 0x0004, // Left mouse button released. + LEFTUP = 0x0008, // Left mouse button is being held up. + RIGHTPRESS = 0x0010, // Right mouse button press. + RIGHTHELD = 0x0020, // Right mouse button is being held down. + RIGHTRELEASE = 0x0040, // Right mouse button released. + RIGHTUP = 0x0080, // Right mouse button is being held up. + KEYBOARD = 0x0100, // Keyboard input processing (maybe). + } FlagEnum; + + GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky=false); + GadgetClass(void) {}; + virtual ~GadgetClass(void); +// static GadgetClass * Create_One_Of(int x, int y, int w, int h, unsigned flags, int sticky=false); + + /* + ** Gadget list management functions. + */ + virtual KeyNumType Input(void); + virtual void Draw_All(bool forced=true); + virtual void Delete_List(void); + virtual ControlClass * Extract_Gadget(unsigned id); + virtual void Flag_List_To_Redraw(void) {LastList = 0;}; + virtual GadgetClass * Remove(void); + virtual GadgetClass * Get_Next(void) const; + virtual GadgetClass * Get_Prev(void) const; + + /* + ** Manages individual gadget states and actions. + */ + virtual void Disable(void); + virtual void Enable(void); + virtual unsigned Get_ID(void) const {return 0;}; + virtual void Flag_To_Redraw(void); + virtual void Peer_To_Peer(unsigned , KeyNumType & , ControlClass & ) {}; + virtual void Set_Focus(void); + virtual void Clear_Focus(void); + virtual bool Has_Focus(void); + virtual int Is_List_To_Redraw(void); + + /* + ** General render function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the coordinates and dimensions of the gadget region. These are in + ** absolute screen pixel coordinates. + */ + int X; + int Y; + int Width; + int Height; + + protected: + + /* + ** Processes the event flags so that if this gadget needs to "stick" or + ** "unstick", it will be properly flagged. Call this function if you are + ** going to clear the button press flags before calling the base class + ** Action() function. Otherwise, calling this function manually, is + ** unnecessary since the base class Action() function already does so. + */ + virtual void Sticky_Process(unsigned flags); + + /* + ** This is the action functio that will be called whenever the flags and mouse + ** input indicates. This is the main method by which this button performs a useful + ** function. + */ + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** If there is a sticky button being processed, then this will point to it. A sticky + ** button is one that will ONLY be processed while the mouse button is being + ** held down. + */ + static GadgetClass * StuckOn; + + /* + ** This is a record of the last list passed to the Input() function. If a list + ** different than the last recorded one is detected, then the draw function is + ** called for every gadget in the list. This causes all buttons to be redrawn the + ** fire time Input() is called without forced a manual call to Draw_All(). + */ + static GadgetClass * LastList; + + /* + ** This points to the gadget that has the keyboard focus. All keyboard only + ** events are fed to this gadget to the exclusion of all others. + */ + static GadgetClass * Focused; + + /* + ** This button should call the Draw_Me function because some graphic element needs + ** to be redrawn. This flag is set by default if the Action function is called. + */ + unsigned IsToRepaint:1; + + public: // HACK HACK HACK.. this is here becuase the sidebar buttons are static. + /* + ** A sticky button is one that is processed to the exclusion of all other buttons + ** IF the mouse was pressed down while over this button and the mouse continues + ** to remain pressed. This is the standard behavior for all normal Windows style + ** buttons. + */ + unsigned IsSticky:1; + + protected: + + /* + ** If the button is disabled, then it won't be processed by the input function. It will + ** have its Draw_Me function called as necessary. In order to not display the button + ** at all, the appropriate draw function should perform no action -- just return. Or, + ** just remove the button from the list. + */ + unsigned IsDisabled:1; + + /* + ** These are the action flags that are used to determine when the action function + ** should be called. Example: If this gadget only wants the action button called when + ** the left mouse button is pressed over the its region, then the flag will be set + ** to LEFTPRESS. + */ + unsigned Flags; + + private: +public: //ST - 1/21/2019 12:06PM + virtual int Clicked_On(KeyNumType & key, unsigned flags, int x, int y); +}; + +//inline GadgetClass::FlagEnum operator |(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator &(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator ~(GadgetClass::FlagEnum); + +inline GadgetClass::FlagEnum operator|(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline GadgetClass::FlagEnum operator&(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline GadgetClass::FlagEnum operator~(GadgetClass::FlagEnum a) +{return static_cast(~static_cast(a));} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/GAMEDLG.CPP b/TIBERIANDAWN/GAMEDLG.CPP new file mode 100644 index 000000000..5fc581dcf --- /dev/null +++ b/TIBERIANDAWN/GAMEDLG.CPP @@ -0,0 +1,419 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gamedlg.cpv 2.17 16 Oct 1995 16:52:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "gamedlg.h" +#include "sounddlg.h" +#include "visudlg.h" + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 12/31/1994 MML : Created. * + *=============================================================================================*/ +void GameControlsClass::Process(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 232 * factor; // dialog width + int d_dialog_h = 141 * factor; // dialog height + int d_dialog_x = ((SeenBuff.Get_Width()- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + int d_top_margin= 30 * factor; + + int d_txt6_h = 7 * factor; // ht of 6-pt text + int d_margin1 = 5 * factor; // large margin + int d_margin2 = 2 * factor; // small margin + + int d_speed_w = d_dialog_w - (20 * factor); + int d_speed_h = 6 * factor; + int d_speed_x = d_dialog_x + (10 * factor); + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h; + + int d_scroll_w = d_dialog_w - (20 * factor); + int d_scroll_h = 6 * factor; + int d_scroll_x = d_dialog_x + (10 * factor); + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h; + + int d_visual_w = d_dialog_w - (40 * factor); + int d_visual_h = 9 * factor; + int d_visual_x = d_dialog_x + (20 * factor); + int d_visual_y = d_scroll_y + d_scroll_h + d_txt6_h + (d_margin1 * 2); + + int d_sound_w = d_dialog_w - (40 * factor); + int d_sound_h = 9 * factor; + int d_sound_x = d_dialog_x + (20 * factor); + int d_sound_y = d_visual_y + d_visual_h + d_margin1; + + int d_ok_w = 20 * factor; + int d_ok_h = 9 * factor; + int d_ok_x = d_dialog_cx - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1; + + /* + ** Button Enumerations + */ + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; + + /* + ** Dialog variables + */ + KeyNumType input; + + int gamespeed = Options.GameSpeed; + int scrollrate = Options.ScrollRate; + int selection; + bool pressed = false; + int curbutton = 0; + TextButtonClass *buttons[BUTTON_COUNT - BUTTON_FIRST]; + TextPrintType style; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + SliderClass gspeed_btn(BUTTON_SPEED, d_speed_x, d_speed_y, d_speed_w, d_speed_h); + + SliderClass scrate_btn(BUTTON_SCROLLRATE, d_scroll_x, d_scroll_y, d_scroll_w, d_scroll_h); + + TextButtonClass visual_btn(BUTTON_VISUAL, TXT_VISUAL_CONTROLS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_visual_x, d_visual_y, d_visual_w, d_visual_h); + + TextButtonClass sound_btn(BUTTON_SOUND, TXT_SOUND_CONTROLS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_sound_x, d_sound_y, d_sound_w, d_sound_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OPTIONS_MENU, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y); + okbtn.X = (SeenBuff.Get_Width()-okbtn.Width)/2; + + /* + ** Various Inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build button list + */ + commands = &okbtn; + gspeed_btn.Add_Tail(*commands); + scrate_btn.Add_Tail(*commands); + visual_btn.Add_Tail(*commands); + sound_btn.Add_Tail(*commands); + + /* + ** Init button states + ** For sliders, the thumb ranges from 0 - (maxval-1), so to convert the + ** thumb value to a real-world value: + ** val = (MAX - slider.Get_Value()) - 1; + ** and, + ** slider.Set_Value(-(val + 1 - MAX)); + */ + gspeed_btn.Set_Maximum(OptionsClass::MAX_SPEED_SETTING); // varies from 0 - 7 + gspeed_btn.Set_Thumb_Size(1); + gspeed_btn.Set_Value((OptionsClass::MAX_SPEED_SETTING-1) - gamespeed); + + scrate_btn.Set_Maximum(OptionsClass::MAX_SCROLL_SETTING); // varies from 0 - 7 + scrate_btn.Set_Thumb_Size(1); + scrate_btn.Set_Value((OptionsClass::MAX_SCROLL_SETTING-1) - scrollrate); + + /* + ** Fill array of button ptrs. + */ + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = &visual_btn; + buttons[3] = &sound_btn; + buttons[4] = &okbtn; + + /* + ** Processing loop. + */ + bool process = true; + bool display = true; + bool refresh = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_GAME_CONTROLS, d_dialog_x, d_dialog_y, d_dialog_w); + Show_Mouse(); + display = false; + refresh = true; + } + + if (refresh) { + Hide_Mouse(); + + /* + ** Label the game speed slider + */ + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SPEED, d_speed_x, d_speed_y - d_txt6_h, CC_GREEN, TBLACK, style); + + Fancy_Text_Print(TXT_SLOWER, d_speed_x, d_speed_y + d_speed_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print(TXT_FASTER, d_speed_x + d_speed_w, d_speed_y + d_speed_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + /* + ** Label the scroll rate slider + */ + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SCROLLRATE, d_scroll_x, d_scroll_y - d_txt6_h, CC_GREEN, TBLACK, style); + + Fancy_Text_Print (TXT_SLOWER, d_scroll_x, d_scroll_y + d_scroll_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print (TXT_FASTER, d_scroll_x + d_scroll_w, d_scroll_y + d_scroll_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + commands->Draw_All(); + + Show_Mouse(); + refresh = false; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + case (BUTTON_SPEED | KN_BUTTON): + curbutton = (BUTTON_SPEED - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_SCROLLRATE | KN_BUTTON): + curbutton = (BUTTON_SCROLLRATE - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_VISUAL | KN_BUTTON): + selection = BUTTON_VISUAL; + pressed = true; + break; + + case (BUTTON_SOUND | KN_BUTTON): + selection = BUTTON_SOUND; + pressed = true; + break; + + case (BUTTON_OK | KN_BUTTON): + selection = BUTTON_OK; + pressed = true; + break; + + case (KN_ESC): + process = false; + break; + + case (KN_LEFT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(1); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(1); + } + break; + + case (KN_RIGHT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(0); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(0); + } + break; + + case (KN_UP): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton < 0) { + curbutton = (BUTTON_COUNT - BUTTON_FIRST - 1); + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_DOWN): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_COUNT - BUTTON_FIRST - 1) ) { + curbutton = 0; + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_FIRST; + pressed = true; + break; + + default: + break; + } + + /* + ** Perform some action. Either to exit the dialog or bring up another. + */ + if (pressed) { + + /* + ** Record the new options slider settings. + ** The GameSpeed data member MUST NOT BE SET HERE!!! It will cause multiplayer + ** games to go out of sync. It's set by virtue of the event being executed. + */ + if (gamespeed != ((OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value()) ) { + gamespeed = (OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value(); + OutList.Add(EventClass(EventClass::GAMESPEED, gamespeed)); + } + + if (scrollrate != ((OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value()) ) { + scrollrate = (OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value(); + Options.ScrollRate = scrollrate; + } + process = false; + + /* + ** Save the settings in such a way that the GameSpeed is only set during + ** the save process; restore it when we're done, so multiplayer games don't + ** go out of sync. + */ + int old = Options.GameSpeed; // save orig value + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + Options.GameSpeed = old; // restore old value + + /* + ** Possibly launch into another dialog if so directed. + */ + switch (selection) { + case (BUTTON_VISUAL): + VisualControlsClass().Process(); + process = true; + display = true; + refresh = true; + break; + + case (BUTTON_SOUND): + if (!SoundType) { + CCMessageBox().Process(Text_String(TXT_NO_SOUND_CARD)); + process = true; + display = true; + refresh = true; + } else { + SoundControlsClass().Process(); + } + break; + + case (BUTTON_OK): + break; + } + + pressed = false; + } + } +} + diff --git a/TIBERIANDAWN/GAMEDLG.H b/TIBERIANDAWN/GAMEDLG.H new file mode 100644 index 000000000..50f7e58af --- /dev/null +++ b/TIBERIANDAWN/GAMEDLG.H @@ -0,0 +1,49 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gamedlg.h_v 2.17 16 Oct 1995 16:45:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAMEDLG_H +#define GAMEDLG_H + +#include "gadget.h" + +class GameControlsClass +{ + public: + GameControlsClass(void) {}; + void Process(void); +}; + + +#endif + diff --git a/TIBERIANDAWN/GAUGE.CPP b/TIBERIANDAWN/GAUGE.CPP new file mode 100644 index 000000000..c6f4c52ab --- /dev/null +++ b/TIBERIANDAWN/GAUGE.CPP @@ -0,0 +1,536 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gauge.cpv 2.19 16 Oct 1995 16:50:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GaugeClass::Action -- Handles input events for the gauge. * + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * GaugeClass::Set_Value -- Set the value of the gauge. * + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * GAUGECLASS::GAUGECLASS -- class constructor * + * * + * INPUT: id -- button ID * + * * + * x,y -- upper-left corner, in pixels * + * * + * w,h -- width, height, in pixels * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +GaugeClass::GaugeClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTHELD|LEFTPRESS|LEFTRELEASE, true) +{ + Set_Maximum(255); + Set_Value(0); + + HasThumb = true; + IsHorizontal = (w > h); + IsColorized = true; + + ClickDiff = 0; +} + + +/*********************************************************************************************** + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * * + * This routine will set the maximum value for the gauge. This is the largest value that * + * the current setting may reach. The ability to change this allows the guage to use and * + * return values that are convenient for the programmer's current needs. * + * * + * INPUT: value -- The value to use as the gauge maximum. * + * * + * OUTPUT: bool; Was the gauge maximum changed? A false indicates that the specified value * + * already matches the current maximum. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Maximum(int value) +{ + if (value != MaxValue) { + MaxValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Set_Value -- Set the value of the gauge. * + * * + * This routine will set the current value for the gauge. This value is clipped to the * + * limits of the gauge maximum. * + * * + * INPUT: value -- The value to set at the new current value. * + * * + * OUTPUT: bool; Was the current setting changed? A false indicates that the setting * + * specified is the same as what was already there. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Value(int value) +{ + value = Bound(value, 0, MaxValue); +// value = MIN(value, MaxValue); +// value = MAX(value, 0); + if (value != CurValue) { + CurValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * * + * Use this routine to conver the specified pixel offset into a gauge value. This is used * + * in translating mouse clicks into a cooresponding setting for the guage. * + * * + * INPUT: pixel -- The pixel offset form the start of the gauge. * + * * + * OUTPUT: Returns with the setting value in guage coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Pixel_To_Value(int pixel) +{ + int maximum; + + if (IsHorizontal) { + pixel -= X+1; + maximum = Width; + } else { + pixel -= Y+1; + maximum = Height; + } + maximum -= 2; + pixel = Bound(pixel, 0, maximum); +// pixel = MIN(pixel, maximum); +// pixel = MAX(pixel, 0); + return(Fixed_To_Cardinal(MaxValue, Cardinal_To_Fixed(maximum, pixel))); +} + + +/*********************************************************************************************** + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * * + * Use this routine to convert the specified gauge value into a pixel offset from the * + * star of the gauge. This is used for thumb positioning. * + * * + * INPUT: value -- The value to convert to a pixel offset. * + * * + * OUTPUT: Returns with the pixel offset of the specified value from the start of the * + * guage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Value_To_Pixel(int value) +{ + int maximum; + int start; + if (IsHorizontal) { + maximum = Width; + start = X; + } else { + maximum = Height; + start = Y; + } + maximum -= 2; + return(start + Fixed_To_Cardinal(maximum, Cardinal_To_Fixed(MaxValue, value))); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * * + * OUTPUT: bool; Was the gauge redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, true); + + /* + ** Colorize the inside of the gauge if indicated. + */ + if (IsColorized) { + int middle = Value_To_Pixel(CurValue); + int color = CC_BRIGHT_GREEN; + if (IsHorizontal) { + if (middle >= (X + 1)) + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, color); + } else { + if (middle >= (Y + 1)) + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, color); + } + } + + if (HasThumb) + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Action -- Handles input events for the gauge. * + * * + * This routine will handle input event processing for the gauge. It will adjust the * + * current setting of the gauge according to the mouse position. * + * * + * INPUT: flags -- The input event that is the reason for this function call. * + * key -- The key code that caused the event. * + * * + * OUTPUT: bool; Was the even recognized, processed, and no further gadget scanning is * + * desird (for this pass). * + * * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there's no thumb on this gauge, it's a display-only device; ignore + ** any input. + */ + if (!HasThumb) { + key = KN_NONE; + return(true); + } + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + /* + ** If the thumb is currently being "dragged around", then update the slider + ** position according to the mouse position. In all other cases, ignore the + ** button being held down. + */ + if ((flags & LEFTPRESS) || ((flags & LEFTHELD) && StuckOn == this)) { + + /* + ** Compute the difference between where we clicked, and the edge of + ** the thumb (only if we clicked on the thumb.) + */ + if (flags & LEFTPRESS) { + int curpix = Value_To_Pixel(CurValue); + int clickpix = (IsHorizontal ? Get_Mouse_X() : Get_Mouse_Y()); + + if ( (clickpix > curpix) && (clickpix - curpix) < Thumb_Pixels()) { + ClickDiff = (clickpix - curpix); + } else { + ClickDiff = 0; + } + + int testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + + /* + ** Correct for round-down errors in Pixel_To_Value() and + ** Value_To_Pixe(); make ClickDiff exactly right so that + ** at this point, Get_Mouse_n() - ClickDiff converts to + ** CurValue. + */ + while (testval < CurValue && ClickDiff > 0) { + ClickDiff--; + testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + } + } + + /* + ** If no change occurred in the gauge, just call Control's Action routine, + ** but turn off the flags so it won't fill in 'key' with the button ID. + ** Thus, no button ID will be returned by Input. + */ + if (!Set_Value(Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff))) { + + flags &= ~(LEFTHELD|LEFTRELEASE|LEFTPRESS); + ControlClass::Action(0,key); + key = KN_NONE; + return(true); + } + + } else { + + /* + ** Ingore the left mouse button being held down if this gauge is not + ** currently in "sticky" mode. This allows processing of the LEFTPRESS + ** by any derived classes such that this guage can be more closely + ** controlled. + */ + flags &= ~LEFTHELD; + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Thumb -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: none. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +void GaugeClass::Draw_Thumb(void) +{ + int x = Value_To_Pixel(CurValue); + +// if ((x + 8) > Value_To_Pixel(MaxValue)) { + if ((x + 4) > Value_To_Pixel(MaxValue)) { + x = Value_To_Pixel(MaxValue) - 2; + } + + if (IsHorizontal) { + Draw_Box(x, Y, 4, Height, BOXSTYLE_GREEN_RAISED, true); + //Draw_Box(x, Y, 8, Height, BOXSTYLE_GREEN_RAISED, true); + } else { + Draw_Box(X, x, Width, 4, BOXSTYLE_GREEN_RAISED, true); + //Draw_Box(X, x, Width, 8, BOXSTYLE_GREEN_RAISED, true); + } +} + + +/*********************************************************************************************** + * TriColorGaugeClass::TriColorGaugeClass -- Constructor for 3 color (red\yellow\green) gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: See below. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +TriColorGaugeClass::TriColorGaugeClass(unsigned id, int x, int y, int w, int h) + : GaugeClass(id, x, y, w, h) +{ + RedLimit = 0; // maximum value for red + YellowLimit = 0; // maximum value for yellow +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Red_Limit -- Set the value for the red area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Red_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value > YellowLimit) { +// RedLimit = YellowLimit; +// YellowLimit = value; +// } else { + RedLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Yellow_Limit -- Set the value for the yellow area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Yellow_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value < RedLimit) { +// YellowLimit = RedLimit; +// RedLimit = value; +// } else { + YellowLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Draw_Me -- Draw the tri color gauge. * + * * + * INPUT: int forced -- draw or not? * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, (IsDisabled ? BOXSTYLE_GREEN_RAISED : BOXSTYLE_GREEN_DOWN), true); + + /* + ** Colorize the inside of the gauge if indicated. + */ + int red = Value_To_Pixel(RedLimit); + int yellow = Value_To_Pixel(YellowLimit); + int middle = Value_To_Pixel(CurValue); + + if (CurValue <= RedLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, PINK); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, PINK); + } + } else if (CurValue > RedLimit && CurValue <= YellowLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, middle, Y+Height-2, YELLOW); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, middle, YELLOW); + } + } else if (CurValue > YellowLimit && CurValue <= MaxValue) { + + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, yellow, Y+Height-2, YELLOW); + LogicPage->Fill_Rect(yellow, Y+1, middle, Y+Height-2, GREEN); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, yellow, YELLOW); + LogicPage->Fill_Rect(X+1, yellow, X+Width-2, middle, GREEN); + } + } + + if (HasThumb) + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + diff --git a/TIBERIANDAWN/GAUGE.H b/TIBERIANDAWN/GAUGE.H new file mode 100644 index 000000000..cc63ccc0e --- /dev/null +++ b/TIBERIANDAWN/GAUGE.H @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gauge.h_v 2.17 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.H * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAUGE_H +#define GAUGE_H + +class GaugeClass : public ControlClass +{ + public: + + GaugeClass(unsigned id, int x, int y, int w, int h); +// static GaugeClass * Create_One_Of(unsigned id, int x, int y, int w, int h); + + virtual int Draw_Me(int forced=false); + virtual int Set_Maximum(int value); + virtual int Set_Value(int value); + virtual int Get_Value(void) const {return (CurValue);}; + virtual void Use_Thumb(int value) { HasThumb = value ? true : false; }; + + virtual int Thumb_Pixels(void) { return (8);} + + /* + ** If this gauge has a color to the left of the current setting, then this + ** flag will be true. + */ + unsigned IsColorized:1; + + protected: + + /* + ** If a thumb is desired, set to true. + */ + unsigned HasThumb:1; + + /* + ** Is this a horizontal slider? + */ + unsigned IsHorizontal:1; + + int MaxValue; // maximum value (in application units) + int CurValue; // index of 1st displayed string in box + // (in application units) + + /* + ** This value records the difference between where the user clicked + ** and the edge of the thumb, so that the thumb follows the mouse + ** with the proper offset. + */ + int ClickDiff; + + protected: + virtual void Draw_Thumb(void); + virtual int Action(unsigned flags, KeyNumType &key); + virtual int Pixel_To_Value(int pixel); + virtual int Value_To_Pixel(int value); +}; + + + +class TriColorGaugeClass : public GaugeClass +{ + public: + TriColorGaugeClass(unsigned id, int x, int y, int w, int h); +// static TriColorGaugeClass * Create_One_Of(unsigned id, int x, int y, int w, int h); + virtual int Draw_Me(int forced); + virtual int Set_Red_Limit(int value); + virtual int Set_Yellow_Limit(int value); + + protected: + int RedLimit; // maximum value for red + int YellowLimit; // maximum value for yellow +}; + + + + +#endif diff --git a/TIBERIANDAWN/GLOBALS.CPP b/TIBERIANDAWN/GLOBALS.CPP new file mode 100644 index 000000000..84486dc8a --- /dev/null +++ b/TIBERIANDAWN/GLOBALS.CPP @@ -0,0 +1,1052 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\globals.cpv 2.17 16 Oct 1995 16:52:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef JAPANESE +bool ForceEnglish = false; +#endif + +bool Debug_Quiet = false; +bool Debug_Cheat = false; +bool Debug_Remap = false; +bool Debug_Icon = false; +bool Debug_Flag = false; +bool Debug_Lose = false; +bool Debug_Win = false; +bool Debug_Map = false; // true = map editor mode +bool Debug_Passable = false; // true = show passable/impassable terrain +bool Debug_Unshroud = false; // true = hide the shroud +bool Debug_Threat = false; +bool Debug_Find_Path = false; +bool Debug_Check_Map = false; // true = validate the map each frame +bool Debug_Playtest = false; +int In_Debugger = 0; +bool Debug_Heap_Dump = false; // true = print the Heap Dump +bool Debug_Smart_Print = false; // true = print everything that calls Smart_Printf +bool Debug_Trap_Check_Heap = false; // true = check the Heap +bool Debug_Instant_Build = false; +bool Debug_Force_Crash = false; + + +TFixedIHeapClass Units; +TFixedIHeapClass Factories; +TFixedIHeapClass Terrains; +TFixedIHeapClass Templates; +TFixedIHeapClass Smudges; +TFixedIHeapClass Overlays; +TFixedIHeapClass Infantry; +TFixedIHeapClass Bullets; +TFixedIHeapClass Buildings; +TFixedIHeapClass Anims; +TFixedIHeapClass Aircraft; +TFixedIHeapClass Triggers; +TFixedIHeapClass TeamTypes; +TFixedIHeapClass Teams; +TFixedIHeapClass Houses; + + +#ifdef PATCH +/*************************************************************************** +** Compatibility with version 1.07 flag. +*/ +bool IsV107 = false; +char OverridePath[128]="."; +#endif + + +/*************************************************************************** +** This tracks all selected objects per house (for this map). +*/ +SelectedObjectsType CurrentObject; + + +/*************************************************************************** +** This holds the custom version text that is fetched from the version +** text file. This version is displayed on the options dialog. +*/ +char VersionText[16]; + + +/*************************************************************************** +** This is the VQ animation controller structure. It is filled in by reading +** the PLAYER.INI and overridden through program control. +*/ +//PG_TO_FIX +//VQAConfig AnimControl; + +int PreserveVQAScreen; // Used for screen mode transition control. +bool BreakoutAllowed = true; // "true" if aborting of movies is allowed. +bool Brokeout; // Was the movie broken out of? +bool SlowPalette = true; // Slow palette flag set? + + +/*************************************************************************** +** These are the movie names to use for mission briefing, winning, and losing +** sequences. They are read from the INI file. +*/ +char IntroMovie[_MAX_FNAME+_MAX_EXT]; +char BriefMovie[_MAX_FNAME+_MAX_EXT]; +char WinMovie[_MAX_FNAME+_MAX_EXT]; +char WinMovie2[_MAX_FNAME + _MAX_EXT]; +char WinMovie3[_MAX_FNAME + _MAX_EXT]; +char WinMovie4[_MAX_FNAME + _MAX_EXT]; +char LoseMovie[_MAX_FNAME+_MAX_EXT]; +char ActionMovie[_MAX_FNAME+_MAX_EXT]; +char MovieThemeName[_MAX_FNAME + _MAX_EXT]; +char BriefingText[512]; +ThemeType TransitTheme = THEME_NONE; + + +/*************************************************************************** +** This records the view hotspots for the player. These are the cell numbers +** of the upper left corner for the view position. +*/ +CELL Views[4]; + + +/*************************************************************************** +** This is the pending speech sample to play. This sample will be played +** at the first opportunity. +*/ +VoxType SpeakQueue = VOX_NONE; + + +/*************************************************************************** +** This records if the score (music) file is present. If not, then much of +** the streaming score system can be disabled. +*/ +bool ScoresPresent; + + +/*************************************************************************** +** This flag will control whether there is a response from game units. +** By carefully controlling this global, multiple responses are supressed +** when a large group of infantry is given the movement order. +*/ +bool AllowVoice = true; + + +/*************************************************************************** +** This counts the number of crates on the map. When this value reaches zero, +** then a timer is started that will control crate creation. +*/ +int CrateCount; +TCountDownTimerClass CrateTimer; +bool CrateMaker = false; + + +/*************************************************************************** +** This is the current frame number. This number is guaranteed to count +** upward at the rate of one per game logic process. The target rate is 15 +** per second. This value is saved and restored with the saved game. +*/ +long Frame = 0; + + +/*************************************************************************** +** These globals are constantly monitored to determine if the player +** has won or lost. They get set according to the trigger events associated +** with the scenario. +*/ +bool PlayerWins; +bool PlayerLoses; +bool PlayerRestarts; + +/* +** This flag is set if the player neither wins nor loses; it's mostly for +** multiplayer mode. +*/ +bool PlayerAborts; + + +/*************************************************************************** +** This is the pointer for the speech staging buffer. This buffer is used +** to hold the currently speaking voice data. Since only one speech sample +** is played at a time, this buffer is only as big as the largest speech +** sample that can be played. +*/ +void * SpeechBuffer; + + +/*************************************************************************** +** This is a running accumulation of the number of ticks that were unused. +** This accumulates into a useful value that contributes to a +** histogram of game performance. +*/ +long SpareTicks; + + +/*************************************************************************** +** This is a special scenario count down value. End of game condition will +** not be checked until this value reaches zero. +*/ +int EndCountDown; + + +/*************************************************************************** +** When the player sabotages a building (scenario #6 GDI only) then when +** the next scenario starts, that building will already be destroyed. +*/ +StructType SabotagedType; + + +/*************************************************************************** +** If the Nod temple was destroyed by the ion cannon, then this flag will +** be set to true. +*/ +bool TempleIoned = false; + + +/*************************************************************************** +** This is the monochrome debug page array. The various monochrome data +** screens are located here. +*/ +MonoClass MonoArray[MonoClass::MAX_MONO_PAGES]; +int MonoPage; // The current page. + + +/*************************************************************************** +** This is true if the game is the currently in focus windows app +** +*/ +bool GameInFocus = true; + +/*************************************************************************** +** This holds the theater specific mixfiles. +*/ +MixFileClass *TheaterData = NULL; +MixFileClass *TheaterIcons = NULL; +MixFileClass *LowTheaterData; +MixFileClass *MoviesMix = 0; +MixFileClass *GeneralMix = 0; +MixFileClass *ScoreMix = 0; + + +/*************************************************************************** +** This is the options control class. The options control such things as +** game speed, visual controls, and other user settings. +*/ +GameOptionsClass Options; + + +/*************************************************************************** +** Logic processing is controlled by this element. It handles both graphic +** and AI logic. +*/ +LogicClass Logic; + + +/*************************************************************************** +** This handles the background music. +*/ +ThemeClass Theme; + + +/*************************************************************************** +** This is the main control class for the map. +*/ +#ifdef SCENARIO_EDITOR +MapEditClass Map; +#else +MouseClass Map; +#endif + +/************************************************************************** +** The running game score is handled by this class (and member functions). +*/ +ScoreClass Score; + + +/*************************************************************************** +** The running credit display is controlled by this class (and member +** functions. +*/ +CreditClass CreditDisplay; + + +/*************************************************************************** +** These are the bits that are set when the appropriate tutor message +** has been displayed. Once the message has been displayed, it will not be +** displayed again. +*/ +long TutorFlags[2]; + + +/************************************************************************** +** This class records the special command override options that C&C +** supports. +*/ +SpecialClass Special; + + +/*************************************************************************** +** General rules that control the game. +*/ +RulesClass Rule; + + +/*************************************************************************** +** This is the scenario data for the currently loaded scenario. +** These variables should all be set together. +*/ +HousesType Whom; // Initial command line house choice. +unsigned Scenario; // Scenario # +ScenarioPlayerType ScenPlayer; // GDI, NOD, 2-Player, Multi-Player +ScenarioDirType ScenDir; // East/West +ScenarioVarType ScenVar; // variation A/B/C +char ScenarioName[_MAX_FNAME+_MAX_EXT]; // name of scenario +int CarryOverMoney; // Carry over money from last scenario. +int CarryOverPercent; // Carry over money percentage control. +int CarryOverCap; // Maxmimum carry over money allowed. +int ScenarioInit; +bool SpecialFlag = false; + + +/*************************************************************************** +** This value tells the sidebar what items it's allowed to add. The +** lower the value, the simpler the sidebar will be. +*/ +unsigned BuildLevel = 3; // Buildable level (1 = simplest) + + +/*************************************************************************** +** This value is computed every time a new scenario is loaded; it's a +** CRC of the INI and binary map files. +*/ +unsigned long ScenarioCRC; + + +/*************************************************************************** +** The various tutor and dialog messages are located in the data block +** referenced by this pointer. +*/ +char const * SystemStrings; + + +/*************************************************************************** +** The game plays as long as this var is true. +*/ +bool GameActive; + + +/*************************************************************************** +** This is a scratch variable that is used to when a reference is needed to +** a long, but the value wasn't supplied to a function. This is used +** specifically for the default reference value. As such, it is not stable. +*/ +long LParam; + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** +** The currently-selected cell for the Scenario Editor +*/ +CELL CurrentCell = 0; +#endif + + +/*************************************************************************** +** Most of the text in the game will use the six point font. These are the +** pointers to the fonts. If it is NULL, then the font hasn't been loaded +** yet. +*/ +void const *Green12FontPtr; // Green font for pressed in tabs +void const *Green12GradFontPtr; // Graduated green font for tabs +void const *MapFontPtr; // Standard very small font. +void const *Font3Ptr; // Standard very small font. +void const *Font6Ptr; // Standard small font. +void const *Font8Ptr; // 8 point proportional. +void const *FontLEDPtr; // LED fixed point font. +void const *VCRFontPtr; // VCR font pointer. +void const *ScoreFontPtr; // font for score & map selection screens +void const *GradFont6Ptr; // gradient 6 point font pointer. + + +/*************************************************************************** +** This is the house that the human player is currently playing. +*/ +HouseClass * PlayerPtr; + + +/*************************************************************************** +** Special palettes for MCGA mode goes here. These palette buffers are used +** for pictures that do not use the game palette or are used for fading to +** black. +*/ +unsigned char *GamePalette; +unsigned char *BlackPalette; +unsigned char *WhitePalette; +unsigned char *OriginalPalette; +unsigned char *Palette; + + +/*************************************************************************** +** These are the event queues. One is for holding events until they are ready to be +** sent to the remote computer for processing. The other list is for incoming events +** that need to be executed when the correct frame has been reached. +*/ +QueueClass OutList; +QueueClass DoList; + + +/*************************************************************************** +** These are arrays/lists of trigger pointers for each cell & the houses. +*/ +DynamicVectorClass CellTriggers; +DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + + +/*************************************************************************** +** This is an array of waypoints; each waypoint corresponds to a letter of +** the alphabet, and points to a cell number. -1 means unassigned. +** The CellClass has a bit that tells if that cell has a waypoint attached to +** it; the only way to find which waypoint it is, is to scan this array. This +** shouldn't be needed often; usually, you know the waypoint & you want the CELL. +*/ +CELL Waypoint[WAYPT_COUNT]; + + +/*************************************************************************** +** This is the list of BuildingTypes that define the AI's base. +*/ +BaseClass Base; + + +/*************************************************************************** +** This value tells what type of multiplayer game we're playing. +*/ +GameType GameToPlay = GAME_NORMAL; + + +/*************************************************************************** +** This is the current communications protocol +*/ +CommProtocolType CommProtocol; + + +/*************************************************************************** +** These values are used for recording & playing back a game. +*/ +CCFileClass RecordFile ("RECORD.BIN"); +int RecordGame = 0; // 1 = record a game +int SuperRecord = 0; // 1 = reopen record file with every write +int PlaybackGame= 0; // 1 = play back a game +int AllowAttract = 0; // 1 = allow attract mode + + +/*************************************************************************** +** This is the null modem manager class. Declaring this class doesn't +** perform any allocations; +** the class itself is ?? bytes. +*/ +#if (0) //PG_TO_FIX +bool ModemService = true; // When false disable servicing modem. +NullModemClass NullModem ( + 16, // number of send entries + 64, // number of receive entries +// sizeof (EventClass) * MAX_EVENTS, // maxlen of entry buffer + (200 / sizeof(EventClass) ) * sizeof(EventClass) + sizeof( CommHeaderType ), + 0x1234); // Magic number must have each digit unique + // and different from the queue magic number + + +DynamicVectorClass PhoneBook; +int CurPhoneIdx; // current phonebook index, for dialing + +DynamicVectorClass InitStrings; + +SerialSettingsType SerialDefaults; // serial port default settings + +//ModemGameType ModemGameToPlay; // type of modem play Dialer, answerer, null + +char *DialMethodCheck[ DIAL_METHODS ] = { + "T", + "P" +}; + +char *CallWaitStrings[ CALL_WAIT_STRINGS_NUM ] = { + "*70,", + "70#,", + "1170,", + "CUSTOM - " +}; +#endif + +ModemGameType ModemGameToPlay; // type of modem play Dialer, answerer, null + +/*************************************************************************** +** Index into scenario description list box +*/ +int ScenarioIdx; + + +/*************************************************************************** +** This array of flags tells if the given colors have been used, or are +*/ +int ColorUsed[MAX_MPLAYER_COLORS]; + + +/*************************************************************************** +** This string stores the player's name. +*/ +char MPlayerName[MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** This is the array of remap colors. Each player in a network game is +** assigned one of these colors. The 'G' is for graphics drawing; the 'T' +** is for text printing (indicates a remap table for the font to use). +*/ +int MPlayerGColors[MAX_MPLAYER_COLORS] = { + 5, // Yellow + 127, // Red + 135, // BlueGreen + 26, // Orange + 4, // Green + 202 // Blue-Grey +}; + +int MPlayerTColors[MAX_MPLAYER_COLORS] = { + CC_GDI_COLOR, // Yellow + CC_NOD_COLOR, // Red + CC_BLUE_GREEN, // BlueGreen + CC_ORANGE, // Orange //26 + CC_GREEN, // Green + CC_BLUE_GREY, // Blue +}; + + +/*************************************************************************** +** This is a list of all the names of the multiplayer scenarios that use +** bases (production), and those that don't. There is a list for +** descriptions, and another for actual filenames. +*/ +char MPlayerDescriptions[100][40]; +DynamicVectorClass MPlayerScenarios; +DynamicVectorClass MPlayerFilenum; + + +/*************************************************************************** +** This value determines the max allowable # of players. +*/ +int MPlayerMax = 4; + + +/*************************************************************************** +** Multiplayer game options +*/ +int MPlayerPrefColor; // preferred color index for this player +int MPlayerColorIdx; // actual color index of this player +HousesType MPlayerHouse; // House of this player (GDI/NOD) +unsigned char MPlayerLocalID; // ID of this player +int MPlayerCount; // # of human players in this game +int MPlayerBases; // 1 = bases are on for this scenario +int MPlayerCredits; // # credits everyone gets +int MPlayerTiberium; // 1 = tiberium enabled for this scenario +int MPlayerGoodies; // 1 = goodies enabled for this scenario +int MPlayerGhosts; // 1 = houses with no players will still play +int MPlayerSolo = 0; // 1 = allows a single-player net game +int MPlayerUnitCount = 10; // # units for non-base multiplayer scenarios + + +/*--------------------------------------------------------------------------- +Min & Max unit count values; index0 = bases OFF, index1 = bases ON +---------------------------------------------------------------------------*/ +int MPlayerCountMin[2] = {1,0}; +int MPlayerCountMax[2] = {50,12}; + + +/*--------------------------------------------------------------------------- +MPlayerMaxAhead is the number of frames ahead of this one to execute a given +packet. It's set by the RESPONSE_TIME event. +---------------------------------------------------------------------------*/ +unsigned long MPlayerMaxAhead = 3; + + +/*--------------------------------------------------------------------------- +'FrameSendRate' is the # frames between data packets +'FrameRateDelay' is the time ticks to wait between frames, for smoothing. +---------------------------------------------------------------------------*/ +unsigned long FrameSendRate; + + +/*************************************************************************** +** Multiplayer ID's, stored in order of event execution. +** Format: +** bits 0-3: the "preferred" house of the player (GDI/NOD) +** bits 4-7: the player's Color Index +** These values are used as the IPX connection ID's. +*/ +unsigned char MPlayerID [MAX_PLAYERS]; + + +/*************************************************************************** +** This array stores the actual HousesType for all players (MULT1, etc). +*/ +HousesType MPlayerHouses [MAX_PLAYERS]; + + +/*************************************************************************** +** This array stores the names of all players in a multiplayer game. +*/ +char MPlayerNames [MAX_PLAYERS][MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** This is a list of the messages received from / sent to other players, +** the address to send to (IPX only), and the last message received or +** sent (for the computer's messages). +*/ +MessageListClass Messages; +IPXAddressClass MessageAddress; +char LastMessage[MAX_MESSAGE_LENGTH]; + + +/*************************************************************************** +** If this flag is set, computer AI will blitz the humans all at once; +** otherwise, the computer units trickle gradually out. +*/ +int MPlayerBlitz = 0; + + +/*************************************************************************** +** If this flag is set, we can move around the map, but we can't do anything. +** It means we've been defeated, but we're still allowed to watch the action. +*/ +int MPlayerObiWan = 0; + + +/*************************************************************************** +** These variables keep track of the multiplayer game scores. +*/ +MPlayerScoreType MPlayerScore[MAX_MULTI_NAMES]; +int MPlayerGamesPlayed; // # games played this run +int MPlayerNumScores; // # active entries in MPlayerScore +int MPlayerWinner; // index of winner of last game +int MPlayerCurGame; // index of current game being played + + +// +// This array stores the processing time required by all multiplayer systems. +// The values are stored in the same order as the 'MPlayerID' array. +// +int TheirProcessTime[MAX_PLAYERS - 1]; +int DesiredFrameRate; + + +/*************************************************************************** +** These values are used purely for the Mono debug display. They show the +** names of the Global Channel packet types, and the event types. +*/ +char *GlobalPacketNames[] = { + "Game?", + "Game!", + "Player?", + "Player!", + "Join?", + "Join!", + "Reject", + "GameOptions", + "Sign Off", + "GO!", + "Message", + "Ping" +}; + + +// yeah, there's 100 empty names here, because the SerialCommandType starts at 100. +char *SerialPacketNames[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "CONNECT", + "GAME_OPTIONS", + "SIGN_OFF", + "GO", + "MESSAGE", + "TIMING", + "SCORE_SCREEN", + "LAST_COMMAND", +}; + + +/*************************************************************************** +** These variables are just to help find sync bugs. +*/ +long TrapFrame = 0x7fffffff; // frame to start trapping object values at +RTTIType TrapObjType = RTTI_NONE; // type of object to trap +TrapObjectType TrapObject = {NULL}; // ptr to object being trapped +COORDINATE TrapCoord = 0; // COORD of object to trap +void *TrapThis = NULL; // 'this' ptr of object to trap +CellClass *TrapCell = NULL; // for trapping a cell +int TrapCheckHeap = 0; // start checking the Heap + + +/*************************************************************************** +** This is the network IPX manager class. It handles multiple remote +** connections. Declaring this class doesn't perform any allocations; +** the class itself is 140 bytes. +*/ +IPXManagerClass Ipx ( + sizeof (GlobalPacketType), // size of Global Channel packets + ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), + 10, // # entries in Global Queue + 8, // # entries in Private Queues + VIRGIN_SOCKET, // Socket ID # + IPXGlobalConnClass::COMMAND_AND_CONQUER); // Product ID # + + +//#if(TIMING_FIX) +// +// These values store the min & max frame #'s for when MaxAhead >>increases<<. +// If MaxAhead increases, and the other systems free-run to the new MaxAhead +// value, they may miss an event generated after the MaxAhead event was sent, +// but before it executed, since it will have been scheduled with the older, +// shorter MaxAhead value. This will cause a Packet_Received_Too_Late error. +// The frames from the point where the new MaxAhead takes effect, up to that +// frame Plus the new MaxAhead, represent a "period of vulnerability"; any +// events received that are scheduled to execute during this period should +// be re-scheduled for after that period. +// +int NewMaxAheadFrame1; +int NewMaxAheadFrame2; +//#endif + +/*************************************************************************** +** This is the user-specified IPX address of a desired game owner machine. +** Use this to cross a bridge. Only the 1st 4 numbers in the address are +** used; the rest are set to ff's, for broadcasting. 'IsBridge' is set +** if this address should be used. +*/ +int IsBridge = 0; +IPXAddressClass BridgeNet; + + +/*************************************************************************** +** This flag is true if the user has requested that this game be "secret" +** (The game will not appear to other systems just starting up.) +*/ +bool NetStealth = false; + + +/*************************************************************************** +** If this flag is true, the user won't receive messages from any player +** other than those in his own game. It defaults to protected mode. +*/ +bool NetProtect = true; + + +/*************************************************************************** +** This flag indicates whether the game is "open" or not to other network players. +*/ +bool NetOpen = false; + + +/*************************************************************************** +** This string stores the game's network name. +** GameName does not include the "'s Game"; comparing GameName to +** PlayerName can determine if this player is the originator of the game. +*/ +char MPlayerGameName[MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** These variables are for servicing the Global Channel. +*/ +GlobalPacketType GPacket; // Global Channel packet +int GPacketlen; // length of incoming packet +IPXAddressClass GAddress; // address of sender +unsigned short GProductID; // sender's Product ID + + +/*************************************************************************** +** This is the "meta-packet"; it's a bunch of events lumped together. +** The packet's size is IPX's max size (546), rounded down to accommodate +** the max number of events possible. +*/ +char *MetaPacket = 0; +int MetaSize = ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass); + + +/*************************************************************************** +** This is the random-number seed; it's synchronized between systems for +** multiplayer games. +*/ +int Seed = 0; +long *RandSeedPtr; + + +/*************************************************************************** +** If this value is non-zero, use it as the random # seed instead; this should +** help reproduce some bugs. +*/ +int CustomSeed = 0; + +int WindowList[][8] = { +/* xbyte, ypixel, bytewid, pixelht, cursor color, bkgd color, cursor x, cursor y */ + + /* do not change the first 2 entries!! they are necc. to the system */ + {0,0,40,200,WHITE,BLACK,0,0}, /* screen window */ + {1,75,38,100,WHITE,BLACK,0,0}, /* DOS Error window */ + + // Tactical map. + {0, 0, 40, 200, WHITE,LTGREY,0,0}, + + // Initial menu window. + {12, 199-42, 16, 42, LTGREY, DKGREY, 0, 0}, + + // Sidebar clipping window. + {0,0,0,0,0,0,0,0}, + + // Scenario editor window. + {5, 30, 30, 140, 0, 0, 0, 0}, + + // Custom window. + {0, 0, 0, 0, 0, 0, 0, 0}, + + // Virtual window for external rendering. ST - 1/15/2019 3:02PM + {0, 0, 0, 0, 0, 0, 0, 0}, +}; + + +/* X,Y,Item Width,Items High,Selected,Norm Color,Sel Color,zero */ +int MenuList[][8]={ + {1, 3, 12, 3, 0, WHITE, PINK, 0}, +}; + +GraphicBufferClass VisiblePage; +GraphicBufferClass HiddenPage; + +GraphicViewPortClass SeenBuff(&VisiblePage, 0,0,1536,1536); +GraphicBufferClass ModeXBuff; +GraphicViewPortClass HidPage(&HiddenPage, 0,0, 1536,1536); +GraphicBufferClass SysMemPage(DEFAULT_SCREEN_WIDTH, 200, (void*)NULL); +int SoundOn; +CountDownTimerClass FrameTimer(BT_SYSTEM, 0L); +CountDownTimerClass DebugTimer(BT_SYSTEM, 0L); +CountDownTimerClass CountDownTimer(BT_SYSTEM, 0L); + +NewConfigType NewConfig; + +/*************************************************************************** +** This timer measures how long (in ticks) it takes to process the game's +** logic, with no packet processing or artificial delays. +*/ +TimerClass ProcessTimer; +int ProcessTicks; // accumulated ticks +int ProcessFrames; // # frames used to measure 'ProcessTicks' + + +/*************************************************************************** +** This flag is for popping up dialogs that call the main loop. +*/ +SpecialDialogType SpecialDialog = SDLG_NONE; + + +/* +** This flags if used to tell can enter cell that we are in a find path +** check and thus should not uncloak units via Can_Enter_Cell. +*/ +//bool IsFindPath = false; + + +/*************************************************************************** +** Globals for the network Dialogs. +*/ + +/* +** List of all games out there, & the address of the game's owner +*/ +DynamicVectorClass Games; + +/* +** List of names & addresses of all the players in the game I'm joining. +** This is the really critical list, since it's used to form connections with +** all other players in my game. It's updated when I get a response to my +** outgoing query, or when I get a query from another system in my game asking +** who I am. This double-insurance means that if any system knows about me, +** I know about them too. The only catch is that if the game is started very, +** very soon after a player joins, not everyone may know about him; to prevent +** this, a timer restriction is put on the New Game dialog's GO button. +*/ +DynamicVectorClass Players; + +char *DebugFname; // for stoopid debugging purposes +int DebugLine; // for stoopid debugging purposes +#ifdef DEMO +int RequiredCD = -2; +#else +int RequiredCD = -1; +#endif +int MouseInstalled; + +/* +** Certain options must be enabled by both a command-line option, and an +** an entry in an INI file. If this flag is 'true', those options have been +** enabled by the INI file. +*/ +int AreThingiesEnabled = false; + + +/* +** Pointer to windows timer object +** +** +*/ + +WinTimerClass *WindowsTimer=NULL; + + +/* +** Command line arguments +** +** +*/ +char * Argv[20]; //Pointers to command line arguments +int Argc; //Command line argument count + + +WWKeyboardClass Kbd; +int ScreenWidth=1536; +int ScreenHeight=1536; +WWMouseClass *WWMouse = NULL; +HANDLE hInstance; +int AllDone; +BOOL InMovie = FALSE; //Are we currently playing a VQ movie? +bool MMXAvailable = false; //Does this CPU support MMX extensions? +//GetCDClass CDList; +bool GameStatisticsPacketSent; +bool ConnectionLost; + +TheaterType LastTheater = THEATER_NONE; + + +bool RunningAsDLL = false; \ No newline at end of file diff --git a/TIBERIANDAWN/GOPTIONS.CPP b/TIBERIANDAWN/GOPTIONS.CPP new file mode 100644 index 000000000..9d7f4bba4 --- /dev/null +++ b/TIBERIANDAWN/GOPTIONS.CPP @@ -0,0 +1,584 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\goptions.cpv 2.17 16 Oct 1995 16:50:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * Draw_Caption -- Draws a caption on a dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "goptions.h" +#include "loaddlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#include "gamedlg.h" +#include "textbtn.h" +#include "confdlg.h" +#include "descdlg.h" + +void GameOptionsClass::Adjust_Variables_For_Resolution(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + OptionWidth = (216+8) * factor; + OptionHeight = 100 * factor; + OptionX = ((SeenBuff.Get_Width() - OptionWidth) / 2); + OptionY = ((SeenBuff.Get_Height() - OptionHeight) / 2); + ButtonWidth = 130 * factor; + OButtonHeight = 9 * factor; + CaptionYPos = 5 * factor; + ButtonY = 21 * factor; + Border1Len = 72 * factor; + Border2Len = 16 * factor; + ButtonResumeY = (OptionHeight - (15 * factor)); +} +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + * 06/23/1995 JLB : Handles restating the mission objective. * + * 07/27/1995 JLB : Adjusts menu for multiplay mode. * + *=============================================================================================*/ +void GameOptionsClass::Process(void) +{ + static struct { + int ID; // Button ID to use. + int Text; // Text number to use for this button. + bool Multiplay; // Allowed in multiplayer version? + } _constants[] = { + {BUTTON_LOAD, TXT_LOAD_MISSION, false}, + {BUTTON_SAVE, TXT_SAVE_MISSION, false}, + {BUTTON_DELETE, TXT_DELETE_MISSION, true}, + {BUTTON_GAME, TXT_GAME_CONTROLS, true}, + {BUTTON_QUIT, TXT_QUIT_MISSION, true}, + {BUTTON_RESUME, TXT_RESUME_MISSION, true}, + {BUTTON_RESTATE, TXT_RESTATE_MISSION, false}, + }; + + /* + ** Variables. + */ + TextButtonClass * buttons = 0; + int selection; + bool pressed; + int curbutton = 6; + int y; + TextButtonClass *buttonsel[sizeof(_constants)/sizeof(_constants[0])]; + + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list for all of the buttons for this dialog. + */ + int maxwidth = 0; + int resfactor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + for (int index = 0; index < sizeof(_constants)/sizeof(_constants[0]); index++ ) { + int text = _constants[index].Text; + buttonsel[index] = NULL; + + if (GameToPlay != GAME_NORMAL && !_constants[index].Multiplay) { + buttonsel[index] = 0; + continue; + } + + if (GameToPlay != GAME_NORMAL && text == TXT_DELETE_MISSION) { + text = TXT_RESIGN; + } + + if (index < 5) { + y = (SeenBuff.Get_Height() - OptionHeight)/2 + ButtonY + ((OButtonHeight+2) * index); + } else { + y = OptionY + ButtonResumeY; + } + + TextButtonClass * g = new TextButtonClass(_constants[index].ID, + text, TPF_6PT_GRAD|TPF_NOSHADOW, 0, y); + + if (g->Width > maxwidth) { + maxwidth = g->Width; + } + if (!buttons) { + buttons = g; + } else { + g->Add_Tail(*buttons); + } + + buttonsel[index] = g; + } + + buttonsel[curbutton-1]->Turn_On(); + + /* + ** Force all button lengths to match the maximum length of the widest button. + */ + GadgetClass * g = buttons; + while (g) { + g->Width = MAX(maxwidth, 90 * resfactor); + g->X = OptionX+(OptionWidth-g->Width)/2; + g = g->Get_Next(); + } +#ifdef FRENCH + buttonsel[BUTTON_RESUME-1]->Width = 104 *resfactor; +#else + buttonsel[BUTTON_RESUME-1]->Width = 90 *resfactor; +#endif + buttonsel[BUTTON_RESUME-1]->X = OptionX+(5 * resfactor); + + if (GameToPlay == GAME_NORMAL) { + buttonsel[BUTTON_RESTATE-1]->Width = 90 * resfactor; + buttonsel[BUTTON_RESTATE-1]->X = OptionX+OptionWidth-(buttonsel[BUTTON_RESTATE-1]->Width+(5 * resfactor)); + } + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + (new GadgetClass(OptionX, OptionY, OptionWidth, OptionHeight, GadgetClass::LEFTPRESS))->Add_Tail(*buttons); + + /* + ** This cause a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to game button. + */ + (new ControlClass(BUTTON_RESUME, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS))->Add_Tail(*buttons); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Redraw the map. + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + /* + ** Reset up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OptionX, OptionY, OptionWidth, OptionHeight); + Hide_Mouse(); + + /* + ** Draw the background. + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_GREEN_BORDER); // has border, raised up + + /* + ** Draw the arrows border if requested. + */ + Draw_Caption(TXT_OPTIONS, OptionX, OptionY, OptionWidth); + + /* + ** Display the version number at the bottom of the dialog box. + */ +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("DEMO%s", + ((WindowList[WINDOW_EDITOR][WINDOWX]+WindowList[WINDOW_EDITOR][WINDOWWIDTH])<<3)-3*resfactor, + WindowList[WINDOW_EDITOR][WINDOWY]+WindowList[WINDOW_EDITOR][WINDOWHEIGHT]-((GameToPlay == GAME_NORMAL) ? (32*resfactor) : (24*resfactor)), + DKGREY, TBLACK, + TPF_6POINT|TPF_NOSHADOW|TPF_RIGHT, + ScenarioName, + VersionText); +#else + Fancy_Text_Print("%s\rV.%d%s", + ((WindowList[WINDOW_EDITOR][WINDOWX]+WindowList[WINDOW_EDITOR][WINDOWWIDTH])<<3)-3*resfactor, + WindowList[WINDOW_EDITOR][WINDOWY]+WindowList[WINDOW_EDITOR][WINDOWHEIGHT]-((GameToPlay == GAME_NORMAL) ? (32*resfactor) : (24*resfactor)), + DKGREY, TBLACK, + TPF_6POINT|TPF_NOSHADOW|TPF_RIGHT, + ScenarioName, + Version_Number(), + VersionText); +#endif + + buttons->Draw_All(); + TabClass::Hilite_Tab(0); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = buttons->Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_RESTATE | KN_BUTTON): + selection = BUTTON_RESTATE; + pressed = true; + break; + + case (BUTTON_LOAD | KN_BUTTON): + selection = BUTTON_LOAD; + pressed = true; + break; + + case (BUTTON_SAVE | KN_BUTTON): + selection = BUTTON_SAVE; + pressed = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + selection = BUTTON_DELETE; + pressed = true; + break; + + case (BUTTON_QUIT | KN_BUTTON): + selection = BUTTON_QUIT; + pressed = true; + break; + + case (BUTTON_GAME | KN_BUTTON): + selection = BUTTON_GAME; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_RESUME | KN_BUTTON): + selection = BUTTON_RESUME; + pressed = true; + break; + + case (KN_UP): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton--; + if (GameToPlay == GAME_NORMAL) { + if (curbutton < BUTTON_LOAD) { + curbutton = (BUTTON_COUNT - 1); + } + } else { + if (curbutton < BUTTON_DELETE) { + curbutton = BUTTON_RESUME; +// curbutton = (BUTTON_COUNT-1); + } + } + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton++; + if (GameToPlay == GAME_NORMAL) { + if (curbutton >= BUTTON_COUNT) { + curbutton = BUTTON_LOAD; + } + } else { + if (curbutton > BUTTON_RESUME) { + curbutton = BUTTON_DELETE; + } + } + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + buttonsel[curbutton-1]->IsPressed = true; + buttonsel[curbutton-1]->Draw_Me(true); + selection = curbutton; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton = selection; + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + + switch (selection) { + case BUTTON_RESTATE: + display = true; +#ifdef JAPANESE + if (!Restate_Mission(ScenarioName, TXT_VIDEO, TXT_TAB_BUTTON_CONTROLS)) { +#else + if (!Restate_Mission(ScenarioName, TXT_VIDEO, TXT_OPTIONS)) { +#endif + BreakoutAllowed = true; + char buffer[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + if (CCFileClass(buffer).Is_Available()) { + Play_Movie(BriefMovie); + } else { + Play_Movie(ActionMovie); + } + //BreakoutAllowed = false; + memset(BlackPalette, 0x01, 768); + Set_Palette(BlackPalette); + memset(BlackPalette, 0x00, 768); + Set_Palette(BlackPalette); + Map.Flag_To_Redraw(true); + Theme.Queue_Song(THEME_PICK_ANOTHER); + process = false; + } + break; + + case (BUTTON_LOAD): + display = true; + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + process = false; + } + break; + + case (BUTTON_SAVE): + display = true; + LoadOptionsClass(LoadOptionsClass::SAVE).Process(); + break; + + case (BUTTON_DELETE): + display = true; + if (GameToPlay != GAME_NORMAL) { + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + process = false; + } else { + LoadOptionsClass(LoadOptionsClass::WWDELETE).Process(); + } + break; + + case (BUTTON_QUIT): + if (GameToPlay == GAME_NORMAL) { +#ifdef JAPANESE + switch (CCMessageBox().Process(TXT_CONFIRM_EXIT, TXT_YES, TXT_NO, TXT_RESTART)) { +#else + switch (CCMessageBox().Process(TXT_CONFIRM_EXIT, TXT_ABORT, TXT_CANCEL, TXT_RESTART)) { +#endif + case 2: + display = true; + break; + + case 0: + process = false; + Queue_Exit(); + break; + + case 1: + PlayerRestarts = true; + process = false; + break; + } + } else { + if (ConfirmationClass().Process(TXT_CONFIRM_EXIT)) { + process = false; + Queue_Exit(); + } else { + display = true; + } + } + break; + + case (BUTTON_GAME): + display = true; + GameControlsClass().Process(); + break; + + case (BUTTON_RESUME): + //Save_Settings(); + process = false; + display = true; + break; + } + + pressed = false; + buttonsel[curbutton-1]->IsPressed = false; + //buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + } + } + + /* + ** Clean up and re-enter the game. + */ + buttons->Delete_List(); + + /* + ** Redraw the map. + */ + Keyboard::Clear(); + Call_Back(); + HiddenPage.Clear(); + Call_Back(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + +/*********************************************************************************************** + * Draw_Caption -- Draws a caption on a dialog box. * + * * + * This routine draws the caption text and any fancy filigree that the dialog may require. * + * * + * INPUT: text -- The text of the caption. This is the text number. * + * * + * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. * + * * + * w -- The width of the dialog box (in pixels). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + *=============================================================================================*/ +void Draw_Caption(int text, int x, int y, int w) +{ + OptionControlType option = OPTION_NONE; + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + + /* + ** Determine the filigree to use depending on the text of the caption. + */ + switch (text) { + case TXT_GAME_CONTROLS: + case TXT_OPTIONS: + option = OPTION_CONTROLS; + break; + + case TXT_LOAD_MISSION: + case TXT_SAVE_MISSION: + case TXT_DELETE_MISSION: + option = OPTION_DELETE; + break; + + case TXT_NONE: + case TXT_MODEM_SERIAL: + case TXT_SELECT_MPLAYER_GAME: + case TXT_SELECT_SERIAL_GAME: + option = OPTION_DIALOG; + break; + + case TXT_HOST_SERIAL_GAME: + case TXT_JOIN_SERIAL_GAME: + option = OPTION_SERIAL; + break; + + case TXT_SETTINGS: + case TXT_PHONE_LIST: + case TXT_PHONE_LISTING: + option = OPTION_PHONE; + break; + + case TXT_JOIN_NETWORK_GAME: + option = OPTION_JOIN_NETWORK; + break; + + case TXT_NETGAME_SETUP: + option = OPTION_NETWORK; + break; + + case TXT_VISUAL_CONTROLS: + option = OPTION_VISUAL; + break; + + case TXT_SOUND_CONTROLS: + option = OPTION_SOUND; + break; + + default: + option = OPTION_DIALOG; + break; + } + + /* + ** Draw the filigree at the corners of the dialog. + */ + if (option != OPTION_NONE) { + CC_Draw_Shape(MixFileClass::Retrieve("OPTIONS.SHP"), (int)option, x+12, y+11, WINDOW_MAIN, SHAPE_CENTER); + CC_Draw_Shape(MixFileClass::Retrieve("OPTIONS.SHP"), (int)option+1, x+w-14, y+11, WINDOW_MAIN, SHAPE_CENTER); + } + + /* + ** Draw the caption. + */ + if (text != TXT_NONE) { + Fancy_Text_Print(text, w/2 + x, 5*factor + y, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + int length = String_Pixel_Width(Text_String(text)); + LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + 5*factor, (x+(w/2))+(length/2), y+FontHeight+FontYSpacing + 5*factor, CC_GREEN); + } +} diff --git a/TIBERIANDAWN/GOPTIONS.H b/TIBERIANDAWN/GOPTIONS.H new file mode 100644 index 000000000..03b52a292 --- /dev/null +++ b/TIBERIANDAWN/GOPTIONS.H @@ -0,0 +1,97 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\goptions.h_v 2.19 16 Oct 1995 16:46:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GOPTIONS_H +#define GOPTIONS_H + +#include "options.h" +#include "gadget.h" + + +class GameOptionsClass : public OptionsClass { + enum GameOptionsButtonEnum { + BUTTON_LOAD=1, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_GAME, + BUTTON_QUIT, + BUTTON_RESUME, + BUTTON_RESTATE, + + BUTTON_COUNT, + }; + + enum GameOptionsEnum { + #if(0) + + OPTION_WIDTH=(216+8), + OPTION_HEIGHT=100, + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), +#ifdef FRENCH + BUTTON_WIDTH=142, +#else + BUTTON_WIDTH=130, +#endif +// OBUTTON_HEIGHT=13, + NUMBER_OF_BUTTONS=6, + CAPTION_Y_POS=5, + BUTTON_Y=21, + BORDER1_LEN=72, + BORDER2_LEN=16, + BUTTON_RESUME_Y=(OPTION_HEIGHT-15) + + #endif + }; + + public: + GameOptionsClass(void): OptionsClass () { }; + void Adjust_Variables_For_Resolution(void); + void Process(void); + + private: + int OptionWidth; + int OptionHeight; + int OptionX; + int OptionY; + int ButtonWidth; + int OButtonHeight; + int CaptionYPos; + int ButtonY; + int Border1Len; + int Border2Len; + int ButtonResumeY; +}; + +#endif diff --git a/TIBERIANDAWN/GSCREEN.CPP b/TIBERIANDAWN/GSCREEN.CPP new file mode 100644 index 000000000..95c47ebe6 --- /dev/null +++ b/TIBERIANDAWN/GSCREEN.CPP @@ -0,0 +1,528 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gscreen.cpv 2.17 16 Oct 1995 16:51:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * GScreenClass::One_Time -- Handles one time class setups. * + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * GScreenClass::Input -- Fetches input and processes gadgets. * + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include + +GadgetClass * GScreenClass::Buttons = 0; + +GraphicBufferClass * GScreenClass::ShadowPage = 0; + + +/*********************************************************************************************** + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * * + * This constructor merely sets the display system, so that it will redraw the first time * + * the render function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +GScreenClass::GScreenClass(void) +{ + IsToUpdate = true; + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::One_Time -- Handles one time class setups. * + * * + * This routine (and all those that overload it) must perform truly one-time initialization. * + * Such init's would normally be done in the constructor, but other aspects of the game may * + * not have been initialized at the time the constructors are called (such as the file system, * + * the display, or other WWLIB subsystems), so many initializations should be deferred to the * + * One_Time init's. * + * * + * Any variables set in this routine should be declared as static, so they won't be modified * + * by the load/save process. Non-static variables will be over-written by a loaded game. * + * * + * This function allocates the shadow buffer that is used for quick screen updates. If * + * there were any data files to load, they would be loaded at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::One_Time(void) +{ + /* + ** Allocate the screen shadow page. This page is used to reduce access to the + ** actual screen memory. It contains a duplicate of what the SEENPAGE is. + */ + Buttons = 0; + ShadowPage = new GraphicBufferClass(320,200); + if (ShadowPage) { + ShadowPage->Clear(); + HiddenPage.Clear(); + } +} + + +/*********************************************************************************************** + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * * + * This routine shouldn't be overloaded. It's the main map initialization routine, and will * + * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * + * routine results in calling every initialization routine in the entire map hierarchy. * + * * + * INPUT: * + * theater theater to initialize to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init(TheaterType theater) +{ + Init_Clear(); + Init_IO(); + Init_Theater(theater); +} + + +/*********************************************************************************************** + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * * + * This routine (and those that overload it) clears any buffers and variables to a known * + * state. It assumes that all buffers are allocated & valid. The map should be displayable * + * after calling this function, and should draw basically an empty display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Clear(void) +{ + /* + ** Clear the ShadowPage & HidPage to force a complete shadow blit. + */ + if (ShadowPage) { + ShadowPage->Clear(); + HiddenPage.Clear(); + } + + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * * + * This routine (and those that overload it) performs any theater-specific initializations * + * needed. This will include setting the palette, setting up remap tables, etc. This routine * + * only needs to be called when the theater has changed. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Theater(TheaterType ) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_IO(void) +{ + /* + ** Reset the button list. This means that any other elements of the map that need + ** buttons must attach them after this routine is called! + */ + Buttons = 0; + +} + + +/*********************************************************************************************** + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * * + * This function is used to flag the display system whether any rendering is needed. The * + * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * + * has changed and the individual Draw_It functions must be called. When a sub system * + * determines that it needs to render something local to itself, it would call this routine * + * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * + * this routine will be called with a true parameter. * + * * + * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * + * * + * OUTPUT: none * + * * + * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * + * Render() function is called, the appropriate drawing steps will be performed. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Flag_To_Redraw(bool complete) +{ + IsToUpdate = true; + if (complete) { + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * GScreenClass::Input -- Fetches input and processes gadgets. * + * * + * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * + * system. * + * * + * INPUT: key -- Reference to the key code (for future examination). * + * * + * x,y -- Reference to mouse coordinates (for future examination). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Input(KeyNumType & key, int & x, int & y) +{ + key = Keyboard::Check(); + + x = Keyboard::Mouse_X(); + y = Keyboard::Mouse_Y(); + + if (Buttons) { + + /* + ** If any buttons need redrawing, they will do so in the Input routine, and + ** they should draw themselves to the HidPage. So, flag ourselves for a Blit + ** to show the newly drawn buttons. + */ + if (Buttons->Is_List_To_Redraw()) { + Flag_To_Redraw(false); + } + + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + + key = Buttons->Input(); + + Set_Logic_Page(oldpage); + + } else { + if (key) { + key = Keyboard::Get(); + } + } + AI(key, x, y); + +} + + +/*********************************************************************************************** + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * * + * This will add a gadget to the game input system. The gadget will be processed in * + * subsiquent calls to the GScreenClass::Input() function. * + * * + * INPUT: gadget -- Reference to the gadget that will be added to the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Add_A_Button(GadgetClass & gadget) +{ + /*------------------------------------------------------------------------ + If this gadget is already in the list, remove it before adding it in: + - If 1st gadget in list, use Remove_A_Button to remove it, to reset the + value of 'Buttons' appropriately + - Otherwise, just call the Remove function for that gadget to remove it + from any list it may be in + ------------------------------------------------------------------------*/ + if (Buttons == &gadget) { + Remove_A_Button(gadget); + } else { + gadget.Remove(); + } + + /*------------------------------------------------------------------------ + Now add the gadget to our list: + - If there are not buttons, start the list with this one + - Otherwise, add it to the tail of the existing list + ------------------------------------------------------------------------*/ + if (Buttons) { + gadget.Add_Tail(*Buttons); + } else { + Buttons = &gadget; + } +} + + +/*********************************************************************************************** + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * * + * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * + * will be invalid! * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Remove_A_Button(GadgetClass & gadget) +{ + Buttons = gadget.Remove(); +} + + +/*********************************************************************************************** + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * * + * This routine should be called in the main game loop (once every game frame). It will * + * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * + * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * + * copied to the visible page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This actually updates the graphic display. As a result it can take quite a * + * while to perform. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Render(void) +{ + //if (Buttons && Buttons->Is_List_To_Redraw()) { + // IsToRedraw = true; + //} + + + if (IsToUpdate || IsToRedraw) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + + //if (IsToRedraw) { + // Hide_Mouse(); + // SeenBuff.To_Buffer(0, 0, 320, 200, ShadowPage); + // Show_Mouse(); + //} + Draw_It(IsToRedraw); + + if (Buttons) Buttons->Draw_All(false); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the Editor's buttons + */ + if (Debug_Map) { + if (Buttons) { + Buttons->Draw_All(); + } + } +#endif + /* + ** Draw the multiplayer message system to the Hidpage at this point. + ** This way, they'll Blit along with the rest of the map. + */ + if (Messages.Num_Messages() > 0) { + Messages.Set_Width(Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); + } + Messages.Draw(); + + //Blit_Display(); // 5/19/20 SKY - Skip copying to scene page, we can get the data directly from hidden page + IsToUpdate = false; + IsToRedraw = false; + + Set_Logic_Page(oldpage); + } +} + + + +#ifdef CHEAT_KEYS + +#define MAX_SCREENS_SAVED 30*15 // Enough for 30 seconds @ 15 fps + +GraphicBufferClass *ScreenList[MAX_SCREENS_SAVED]; +int CurrentScreen = 0; +bool ScreenRecording = false; + +void Add_Current_Screen(void) +{ +#if (0) // ST - 1/2/2019 5:51PM + if (ScreenRecording){ + ScreenList[CurrentScreen] = new GraphicBufferClass; + ScreenList[CurrentScreen]->Init ( SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL, 0, (GBC_Enum) 0); + SeenBuff.Blit (*ScreenList[CurrentScreen]); + + CurrentScreen++; + + if (CurrentScreen == MAX_SCREENS_SAVED){ + + char filename[20]; + for (int i = 0 ; i < MAX_SCREENS_SAVED ; i++){ + sprintf (filename, "SCRN%04d.PCX", i); + Write_PCX_File (filename,*ScreenList[i], (unsigned char *)CurrentPalette); + delete ScreenList[i]; + } + + CurrentScreen = 0; + ScreenRecording = 0; + + } + } +#endif +} + +#endif //CHEAT_KEYS + + +extern bool CanVblankSync; + +/*********************************************************************************************** + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * * + * This routine is used to copy the correct display from the HIDPAGE * + * to the SEENPAGE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void GScreenClass::Blit_Display(void) +{ + if (SeenBuff.Get_Width()!=320){ +#if (0) + if (HidPage.Get_IsDirectDraw() && (Options.GameSpeed >1 || Options.ScrollRate==6 && CanVblankSync) ){ + WWMouse->Draw_Mouse(&HidPage); + SeenBuff.Get_Graphic_Buffer()->Get_DD_Surface()->Flip(NULL , DDFLIP_WAIT); + SeenBuff.Blit (HidPage , 0 , 0 , 0 , 0 , SeenBuff.Get_Width() , SeenBuff.Get_Height() , (BOOL) FALSE ); +#ifdef CHEAT_KEYS + Add_Current_Screen(); +#endif + //HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); + WWMouse->Erase_Mouse(&HidPage, FALSE); + }else{ +#else //(0) + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); +#ifdef CHEAT_KEYS + Add_Current_Screen(); +#endif + WWMouse->Erase_Mouse(&HidPage, FALSE); +#endif //(0) +#if (0) + } +#endif //(0) + + } else { + // ST - 1/2/2019 5:26PM + //ModeX_Blit (&HiddenPage); + } + +} + + + + diff --git a/TIBERIANDAWN/GSCREEN.H b/TIBERIANDAWN/GSCREEN.H new file mode 100644 index 000000000..571c69c95 --- /dev/null +++ b/TIBERIANDAWN/GSCREEN.H @@ -0,0 +1,144 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\gscreen.h_v 2.17 16 Oct 1995 16:45:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GSCREEN_H +#define GSCREEN_H + +#include "function.h" +#include "cell.h" + +class GScreenClass : public VectorClass +{ + public: + + GScreenClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time initializations + virtual void Init(TheaterType = THEATER_NONE); // Inits everything + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** Player I/O is routed through here. It is called every game tick. + */ + virtual void Input(KeyNumType & key, int & x, int & y); + virtual void AI(KeyNumType &, int, int) {}; + virtual void Add_A_Button(GadgetClass & gadget); + virtual void Remove_A_Button(GadgetClass & gadget); + + /* + ** Called when map needs complete updating. + */ + virtual void Flag_To_Redraw(bool complete=false); + + /* + ** Render maintenance routine (call every game tick). Probably no need + ** to override this in derived classes. + */ + virtual void Render(void); + + /* + ** Is called when actual drawing is required. This is the function to + ** override in derived classes. + */ + virtual void Draw_It(bool =false) {}; + + /* + ** This moves the hidpage up to the seenpage. + */ + static void Blit_Display(void); + + /* + ** Changes the mouse shape as indicated. + */ + virtual void Set_Default_Mouse(MouseType mouse, bool wwsmall) = 0; + virtual bool Override_Mouse_Shape(MouseType mouse, bool wwsmall) = 0; + virtual void Revert_Mouse_Shape(void) = 0; + virtual void Mouse_Small(bool wwsmall) = 0; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Misc routines. + */ + virtual void * Shadow_Address(void) {return(ShadowPage);}; + + /* + ** This points to the buttons that are used for input. All of the derived classes will + ** attached their specific buttons to this list. + */ + static GadgetClass * Buttons; + + private: + + /* + ** If the entire map is required to redraw, then this flag is true. This flag + ** is set by the Flag_To_Redraw function. Typically, this occurs when the screen + ** has been trashed or is first created. + */ + unsigned IsToRedraw:1; + + /* + ** If only a sub-system of the map must be redrawn, then this flag will be set. + ** An example of something that would set this flag would be an animating icon + ** in the sidebar. In such a case, complete redrawing of the entire display is not + ** necessary, but the Draw_It function should still be called so that the appropriate + ** class can perform it's rendering. + */ + unsigned IsToUpdate:1; + + /* + ** Pointer to an exact copy of the visible graphic page. This copy is used to speed + ** display rendering by using an only-update-changed-pixels algorithm. + */ + public: + static GraphicBufferClass * ShadowPage; + private: + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; + +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/HDATA.CPP b/TIBERIANDAWN/HDATA.CPP new file mode 100644 index 000000000..d670d4226 --- /dev/null +++ b/TIBERIANDAWN/HDATA.CPP @@ -0,0 +1,322 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\hdata.cpv 2.17 16 Oct 1995 16:48:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 22, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** These are the colors used to identify the various owners. +*/ +const int COLOR_GOOD = 180; // GOLD +const int COLOR_BRIGHT_GOOD = 176; // GOLD +const int COLOR_BAD = 123; //RED; +const int COLOR_BRIGHT_BAD = 127; //RED; +const int COLOR_NEUTRAL = 205; //WHITE; +const int COLOR_BRIGHT_NEUTRAL = 202; //WHITE; + + +static HouseTypeClass const HouseGood( + HOUSE_GOOD, + "GoodGuy", // NAME: House name. + TXT_GDI, // FULLNAME: Translated house name. + "GDI", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_GOOD, // COLOR: Dark Radar map color. + COLOR_BRIGHT_GOOD, // COLOR: Bright Radar map color. + REMAP_GOLD, // Remap color ID number. + RemapGold, // Default remap table. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseBad( + HOUSE_BAD, + "BadGuy", // NAME: House name. + TXT_NOD, // FULLNAME: Translated house name. + "NOD", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_BAD, // COLOR: Dark Radar map color. + COLOR_BRIGHT_BAD, // COLOR: Bright Radar map color. + REMAP_LTBLUE, // Remap color ID number. + RemapLtBlue, // Default remap table. + 'B' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseCivilian( + HOUSE_NEUTRAL, + "Neutral", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "CIV", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Dark Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_GOLD, // Remap color ID number. + RemapNone, // Default remap table. + 'C' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseJP( + HOUSE_JP, + "Special", // NAME: House name. + TXT_JP, // FULLNAME: Translated house name. + "JP", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Dark Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_GOLD, // Remap color ID number. + RemapNone, // Default remap table. + 'J' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti1( + HOUSE_MULTI1, + "Multi1", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP1", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_LTBLUE, // Remap color ID number. + RemapLtBlue, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti2( + HOUSE_MULTI2, + "Multi2", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP2", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_ORANGE, // Remap color ID number. + RemapOrange, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti3( + HOUSE_MULTI3, + "Multi3", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP3", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_GREEN, // Remap color ID number. + RemapGreen, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti4( + HOUSE_MULTI4, + "Multi4", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP4", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_BLUE, // Remap color ID number. + RemapBlue, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti5( + HOUSE_MULTI5, + "Multi5", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP5", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_GOLD, // Remap color ID number. + RemapGold, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti6( + HOUSE_MULTI6, + "Multi6", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP6", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_RED, // Remap color ID number. + RemapRed, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +HouseTypeClass const * const HouseTypeClass::Pointers[HOUSE_COUNT] = { + &HouseGood, + &HouseBad, + &HouseCivilian, + &HouseJP, + &HouseMulti1, + &HouseMulti2, + &HouseMulti3, + &HouseMulti4, + &HouseMulti5, + &HouseMulti6, +}; + + +/*********************************************************************************************** + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * * + * This is the constructor for house type objects. This object holds the constant data * + * for the house type. * + * * + * INPUT: house -- The ID number for this house type. * + * ini -- The INI name of this house. * + * fullname -- The text number representing the complete name of the house. * + * ext -- The filename extension used when loading data files. * + * lemon -- The percentage for objects of this ownership to be lemon. * + * remapc -- The remap color number to use. * + * color -- The radar color to use for this "house". * + * prefix -- A unique prefix letter used when building custom filenames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass::HouseTypeClass(HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + int color, + int bright_color, + PlayerColorType remapcolor, + unsigned char const * remap, + char prefix) +{ + RemapTable = remap; + RemapColor = remapcolor; + House = house; + IniName = ini; + FullName = fullname; + strncpy(Suffix, ext, 3); + Suffix[3] = '\0'; + Lemon = lemon; + Color = color; + BrightColor = bright_color; + Prefix = prefix; + FirepowerBias = 1; + GroundspeedBias = 1; + AirspeedBias = 1; + ArmorBias = 1; + ROFBias = 1; + CostBias = 1; + BuildSpeedBias = 1; +} + + +/*********************************************************************************************** + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * * + * This routine will convert the ASCII house name specified into a * + * real house number. Typically, this is used when processing a * + * scenario INI file. * + * * + * INPUT: name -- ASCII name of house to process. * + * * + * OUTPUT: Returns with actual house number represented by the ASCII * + * name specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +HousesType HouseTypeClass::From_Name(char const *name) +{ + if (name) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (stricmp(Pointers[house]->IniName, name) == 0) { + return(house); + } + } + } + return(HOUSE_NONE); +} + + +/*********************************************************************************************** + * HouseTypeClass::One_Time -- One-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void HouseTypeClass::One_Time(void) +{ + /* + ** Change the radar color for special units; otherwise, they'll be the same + ** color as the player! + */ + //if (Special.IsJurassic && AreThingiesEnabled) { // Assume funpark mode might be required. ST - 10/14/2019 11:53AM + ((unsigned char &)HouseJP.Color) = (unsigned char)COLOR_BAD; + ((unsigned char &)HouseJP.BrightColor) = (unsigned char)COLOR_BRIGHT_BAD; + //} +} + + +/*********************************************************************************************** + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * * + * Use this routine to fetch a reference to the house number specified. * + * * + * INPUT: house -- The house number (HousesType) to look up. * + * * + * OUTPUT: Returns with a reference to the HouseTypeClass object that matches the house * + * number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass const & HouseTypeClass::As_Reference(HousesType house) +{ + return(*Pointers[house]); +} \ No newline at end of file diff --git a/TIBERIANDAWN/HEAP.CPP b/TIBERIANDAWN/HEAP.CPP new file mode 100644 index 000000000..6817e7a50 --- /dev/null +++ b/TIBERIANDAWN/HEAP.CPP @@ -0,0 +1,556 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\heap.cpv 2.18 16 Oct 1995 16:49:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FixedIHeapClass::Free -- Frees an object in the heap. * + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * TFixedIHeapClass::Save -- Saves all active objects * + * TFixedIHeapClass::Load -- Loads all active objects * + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "heap.h" +//#include +#include +#include +#include +#include + + +/*********************************************************************************************** + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * * + * This is the normal constructor used for the heap manager class. This initializes * + * the class but doesn't yet assign actual heap memory to this manager. That is handled * + * by the Set_Heap() function. * + * * + * INPUT: size -- The size of the individual sub-blocks in this heap. This value is * + * typically the size of some class or structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: The heap must first be assigned a block of memory to manage before it can * + * be used. * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::FixedHeapClass(int size) +{ + Size = size; + Buffer = 0; + IsAllocated = false; + TotalCount = 0; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * * + * This is the default constructor for the heap manager class. It handles freeing the * + * memory assigned to this heap. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::~FixedHeapClass(void) +{ + FixedHeapClass::Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * * + * This routine is used to assign a memory heap to this object. A memory heap so assigned * + * will start with all sub-blocks unallocated. After this routine is called, normal * + * allocation and freeing may occur. This routine will allocate necessary memory if the * + * buffer parameter is NULL. * + * * + * INPUT: count -- The number of objects that this heap should manage. * + * * + * buffer -- Pointer to pre-allocated buffer that this manager will use. If this * + * parameter is NULL, then memory will be automatically allocated. * + * * + * OUTPUT: bool; Was the heap successfully initialized? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Set_Heap(int count, void * buffer) +{ + /* + ** Clear out the old heap data. + */ + Clear(); + + /* + ** If there is no size to the objects in the heap, then this block memory + ** handler can NEVER function. Return with a failure condition. + */ + if (!Size) return(false); + + /* + ** If there is no count specified, then this indicates that the heap should + ** be disabled. + */ + if (!count) return(true); + + /* + ** Initialize the free boolean vector and the buffer for the actual + ** allocation objects. + */ + if (FreeFlag.Resize(count)) { + if (!buffer) { + buffer = new char[count * Size]; + if (!buffer) { + FreeFlag.Clear(); + return(false); + } + IsAllocated = true; + } + Buffer = buffer; + TotalCount = count; + + memset((unsigned char *)Buffer, 0x00, count * Size); // Added. Needed to identify bad states in saved games. ST - 10/3/2019 11:49AM + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * * + * Finds the first available sub-block in the heap and returns a pointer to it. The sub- * + * block is marked as allocated by this routine. If there are no more sub-blocks * + * available, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated sub-block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedHeapClass::Allocate(void) +{ + if (ActiveCount < TotalCount) { + int index = FreeFlag.First_False(); + + if (index != -1) { + ActiveCount++; + FreeFlag[index] = true; + return((*this)[index]); + } + } + return(0); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * * + * Use this routine to free a previously allocated sub-block in the heap. * + * * + * INPUT: pointer -- A pointer to the sub-block to free. This is the same pointer that * + * was returned from the Allocate() function. * + * * + * OUTPUT: bool; Was the deallocation successful? Failure could indicate a pointer that * + * doesn't refer to this heap or a null pointer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free(void * pointer) +{ + if (pointer && ActiveCount) { + int index = ID(pointer); + + if (index < TotalCount) { + if (FreeFlag[index]) { + ActiveCount--; + FreeFlag[index] = false; + + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * * + * Use this routine to convert a pointer (returned by Allocate) into the sub-block * + * index number. This index number can be used as a form of identifier for the block. * + * * + * INPUT: pointer -- A pointer to the sub-block to conver into an ID number. * + * * + * OUTPUT: Returns with the index (ID) number for the sub-block specified. This number will * + * range between 0 and the sub-block max -1. If -1 is returned, then the pointer * + * was invalid. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::ID(void const * pointer) +{ + if (pointer && Size) { + return((int)(((char *)pointer - (char *)Buffer) / Size)); + } + return(-1); +} + + +/*********************************************************************************************** + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * * + * This routine is used to bring the heap manager back into a non-functioning state. All * + * memory allocated by this manager is freeed. Any previous pointers to allocated blocks * + * from this heap are now invalid. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedHeapClass::Clear(void) +{ + /* + ** Free the old buffer (if present). + */ + if (Buffer && IsAllocated) { + delete[] Buffer; + } + Buffer = 0; + IsAllocated = false; + ActiveCount = 0; + TotalCount = 0; + FreeFlag.Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * * + * This routine will free all previously allocated objects out of the heap. Use this * + * routine to ensure that the heap is empty. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of all objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free_All(void) +{ + ActiveCount = 0; + FreeFlag.Reset(); + return(true); +} + + +///////////////////////////////////////////////////////////////////// + + +/*********************************************************************************************** + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * * + * Use this routine to free all previously allocated objects in the heap. This routine will * + * also clear out the allocated object vector as well. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free_All(void) +{ + ActivePointers.Delete_All(); + return(FixedHeapClass::Free_All()); +} + + +void FixedIHeapClass::Clear(void) +{ + FixedHeapClass::Clear(); + ActivePointers.Clear(); +} + + +int FixedIHeapClass::Set_Heap(int count, void * buffer) +{ + Clear(); + if (FixedHeapClass::Set_Heap(count, buffer)) { + ActivePointers.Resize(count); + return(true); + } + return(false); +} + + +void * FixedIHeapClass::Allocate(void) +{ + void * ptr = FixedHeapClass::Allocate(); + if (ptr) { + ActivePointers.Add(ptr); + memset (ptr, 0, Size); + } + return(ptr); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Free -- Frees an object in the heap. * + * * + * This routine is used to free an object in the heap. Freeing is accomplished by marking * + * the object's memory as free to be reallocated. The object is also removed from the * + * allocated object pointer vector. * + * * + * INPUT: pointer -- Pointer to the object that is to be removed from the heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free(void * pointer) +{ + if (FixedHeapClass::Free(pointer)) { + ActivePointers.Delete(pointer); + } + return(false); +} + +#if (0) +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(FileClass &file) +{ + int i; // loop counter + int idx; // object index + + /* + ** Save the number of instances of this class + */ + if (file.Write(&ActiveCount, sizeof(ActiveCount)) != sizeof(ActiveCount)) { + return(false); + } + + /* + ** Save each instance of this class + */ + for (i = 0; i < ActiveCount; i++) { + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + idx = ID(Ptr(i)); + if (file.Write(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Save the object itself + */ + if (!Ptr(i)->Save(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(FileClass &file) +{ + int i; // loop counter + int idx; // object index + T *ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Read(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Read (&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + if (!ptr->Load(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/HEAP.H b/TIBERIANDAWN/HEAP.H new file mode 100644 index 000000000..690dd70c2 --- /dev/null +++ b/TIBERIANDAWN/HEAP.H @@ -0,0 +1,349 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\heap.h_v 2.15 16 Oct 1995 16:47:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : February 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HEAP_H +#define HEAP_H + +#include "vector.h" + +/************************************************************************** +** This is a block memory managment handler. It is used when memory is to +** be treated as a series of blocks of fixed size. This is similar to an +** array of integral types, but unlike such an array, the memory blocks +** are annonymous. This facilitates the use of this class when overloading +** the new and delete operators for a normal class object. +*/ +class FixedHeapClass +{ + public: + FixedHeapClass(int size); + virtual ~FixedHeapClass(void); + + int ID(void const * pointer); + int Count(void) {return ActiveCount;}; + int Length(void) {return TotalCount;}; + int Avail(void) {return TotalCount-ActiveCount;}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + protected: + void * operator[](int index) {return ((char *)Buffer) + (index * Size);}; + + /* + ** If the memory block buffer was allocated by this class, then this flag + ** will be true. The block must be deallocated by this class if true. + */ + unsigned IsAllocated:1; + + /* + ** This is the size of each sub-block within the buffer. + */ + int Size; + + /* + ** This records the absolute number of sub-blocks in the buffer. + */ + int TotalCount; + + /* + ** This is the total blocks allocated out of the heap. This number + ** will never exceed Count. + */ + int ActiveCount; + + /* + ** Pointer to the heap's memory buffer. + */ + void * Buffer; + + /* + ** This is a boolean vector array of allocation flag bits. + */ + BooleanVectorClass FreeFlag; + + private: + // The assignment operator is not supported. + FixedHeapClass & operator = (FixedHeapClass const &); + + // The copy constructor is not supported. + FixedHeapClass(FixedHeapClass const &); +}; + + +/************************************************************************** +** This template serves only as an interface to the heap manager class. By +** using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedHeapClass : public FixedHeapClass +{ + public: + TFixedHeapClass(void) : FixedHeapClass(sizeof(T)) {}; + virtual ~TFixedHeapClass(void) {}; + + int ID(T const * pointer) {return FixedHeapClass::ID(pointer);}; + + virtual T * Alloc(void) {return (T*)FixedHeapClass::Allocate();}; + virtual int Free(T * pointer) {FixedHeapClass::Free(pointer);}; + + protected: + T & operator[](int index) {return *(((char *)Buffer) + (index * Size));}; +}; + + +/************************************************************************** +** This is a derivative of the fixed heap class. This class adds the +** ability to quickly iterate through the active (allocated) objects. Since the +** active array is a sequence of pointers, the overhead of this class +** is 4 bytes per potential allocated object (be warned). +*/ +class FixedIHeapClass : public FixedHeapClass +{ + public: + FixedIHeapClass(int size) : FixedHeapClass(size) {}; + virtual ~FixedIHeapClass(void) {}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + virtual void * Active_Ptr(int index) {return ActivePointers[index];}; + + /* + ** This is an array of pointers to allocated objects. Using this array + ** to control iteration through the objects ensures a minimum of processing. + ** It also allows access to this array so that custom sorting can be + ** performed. + */ + DynamicVectorClass ActivePointers; +}; + + +/************************************************************************** +** This template serves only as an interface to the iteratable heap manager +** class. By using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedIHeapClass : public FixedIHeapClass +{ + public: + TFixedIHeapClass(void) : FixedIHeapClass(sizeof(T)) {}; + virtual ~TFixedIHeapClass(void) {}; + + int ID(T const * pointer) {return FixedIHeapClass::ID(pointer);}; + virtual T * Alloc(void) {return (T*)FixedIHeapClass::Allocate();}; + virtual int Free(T * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Free(void * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Save(FileClass & ); + virtual int Load(FileClass & ); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual T * Ptr(int index) {return (T*)FixedIHeapClass::ActivePointers[index];}; + virtual T * Raw_Ptr(int index) {return (index >= 0 && index < Length()) ? (T*)((*this)[index]) : NULL;}; +}; + + +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(FileClass &file) +{ + int i; // loop counter + int idx; // object index + + /* + ** Save the number of instances of this class + */ + if (file.Write(&ActiveCount, sizeof(ActiveCount)) != sizeof(ActiveCount)) { + return(false); + } + + /* + ** Save each instance of this class + */ + for (i = 0; i < ActiveCount; i++) { + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + idx = ID(Ptr(i)); + if (file.Write(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Save the object itself + */ + if (!Ptr(i)->Save(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(FileClass &file) +{ + int i; // loop counter + int idx; // object index + T *ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Read(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Read (&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + if (!ptr->Load(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} + + +#endif + + \ No newline at end of file diff --git a/TIBERIANDAWN/HELP.CPP b/TIBERIANDAWN/HELP.CPP new file mode 100644 index 000000000..137539bf8 --- /dev/null +++ b/TIBERIANDAWN/HELP.CPP @@ -0,0 +1,403 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\help.cpv 2.18 16 Oct 1995 16:51:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : July 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * HelpClass::HelpClass -- Default constructor for the help processor. * + * HelpClass::Help_AI -- Handles the help text logic. * + * HelpClass::Help_Text -- Assigns text as the current help text. * + * HelpClass::Init_Clear -- Sets help system to a known state. * + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * HelpClass::Set_Tactical_Position -- Sets the tactial map position. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the holding buffer for the text overlap list. This buffer must be in the near +** data segment. It will be filled in by the Set_Text() function. +*/ +//short const HelpClass::OverlapList[30] = { // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM +short HelpClass::OverlapList[30] = { + REFRESH_EOL +}; + +char const * HelpClass::HelpText; + + +CountDownTimerClass HelpClass::CountDownTimer; + + +/*********************************************************************************************** + * HelpClass::HelpClass -- Default constructor for the help processor. * + * * + * The help processor is initialized by this routine. It merely sets up the help engine * + * to the default state. The default state will not display any help text. Call the * + * Help_Text() function to enable help processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +HelpClass::HelpClass(void) +{ + X = 0; + Y = 0; + Width = 0; + Text = TXT_NONE; + Color = LTGREY; + CountDownTimer.Set(0); + IsRight = false; + Cost = 0; +} + + +/*********************************************************************************************** + * HelpClass::Init_Clear -- Sets help system to a known state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Init_Clear(void) +{ + TabClass::Init_Clear(); + + Set_Text(TXT_NONE); +} + + +/*********************************************************************************************** + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * * + * Use this routine to fetch an offset list for the cells under the text displayed. If * + * there is no text displayed, then the list will consist of just the terminator code. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the offset list for the help text overlap. The offset * + * list is based on the tactical map upper left corner cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +short const * HelpClass::Overlap_List(void) const +{ + if (Text == TXT_NONE || CountDownTimer.Time()) { + ((short &)(OverlapList[0])) = REFRESH_EOL; + } + return(OverlapList); +} + + +/*********************************************************************************************** + * HelpClass::Help_AI -- Handles the help text logic. * + * * + * This routine handles tracking the mouse position to see if the mouse remains stationary * + * for the required amount of time. If the time requirement has been met, then it flags * + * the help system to display the help text the next time the Draw_Help() function is * + * called. * + * * + * INPUT: key -- Keyboard input code. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called once and only once per game frame (15 times per * + * second). * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinates as passed in. * + *=============================================================================================*/ +void HelpClass::AI(KeyNumType &key, int x, int y) +{ + /* + ** If there is any keyboard input, then the help text goes away. + */ +// if (key) { +// Help_Text(TXT_NONE); +// } + + if (!CountDownTimer.Time() && !IsRight && (x != X || y != Y)) { + Help_Text(TXT_NONE); + } + + /* + ** Process the countdown timer only if it hasn't already expired and there is + ** a real help text message to display. + */ + if (CountDownTimer.Time() && !HelpText && Text != TXT_NONE) { + + /* + ** If the mouse has moved, then reset the timer since a moving mouse is not + ** supposed to bring up the help text. + */ + if (!IsRight && (X != x || Y != y)) { + X = x; + Y = y; + CountDownTimer.Start(); + CountDownTimer.Set(HELP_DELAY); + Set_Text(TXT_NONE); + } else { + + /* + ** If the delay has expired, then the text must be drawn. Build the help text + ** overlay list at this time. Better to do it now, when we KNOW it is needed, then + ** to do it earlier when it might not be needed. + */ + Set_Text(Text); + } + } + + TabClass::AI(key, x, y); +} + + +/*********************************************************************************************** + * HelpClass::Help_Text -- Assigns text as the current help text. * + * * + * Use this routine to change the help text that will pop up if the cursor isn't moved * + * for the help delay duration. Call this routine as often as desired. * + * * + * INPUT: text -- The text number for the help text to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Help_Text(int text, int x, int y, int color, bool quick, int cost) +{ + if (text != Text) { + + /* + ** If there is an existing text message, then flag the map to redraw the underlying + ** icons so that the text message is erased. + */ + if (Text != TXT_NONE) { + Refresh_Cells(Coord_Cell(TacticalCoord), &OverlapList[0]); + } + + /* + ** Record the position of the mouse. This recorded position will be used to determine + ** if the mouse has moved. A moving mouse prevents the help text from popping up. + */ + X = x; + if (x == -1) X = Get_Mouse_X(); + Y = y; + if (y == -1) Y = Get_Mouse_Y(); + IsRight = (y != -1) || (x != -1); + + if (quick) { + CountDownTimer.Set(1); + } else { + CountDownTimer.Set(HELP_DELAY); + } + + Color = color; + Text = text; + Cost = cost; + } +} + + +/*********************************************************************************************** + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * * + * This function will print the help text if it thinks it should. The timer and text * + * message can control whether this occurs. If there is no help text or the countdown timer * + * has not expired, then no text will be printed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Draw_It(bool forced) +{ + TabClass::Draw_It(forced); + + if (Text != TXT_NONE && (forced || !CountDownTimer.Time())) { + + if (LogicPage->Lock()){ + + // Fancy_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY-1, DrawX+Width+1, DrawY+FontHeight, Color); + + if (Cost) { + char buffer[15]; + sprintf(buffer, "$%d", Cost); + int width = String_Pixel_Width(buffer); + + // Fancy_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY+FontHeight, DrawX+width+1, DrawY+FontHeight+FontHeight-1, Color); + LogicPage->Draw_Line(DrawX, DrawY+FontHeight, DrawX+MIN(width+1, Width) - 1, DrawY+FontHeight, BLACK); + } + + LogicPage->Unlock(); + } + } + // if (!In_Debugger) HidPage.Unlock(); +} + + +/*********************************************************************************************** + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * * + * This routine is used to build the overlap list -- used for icon refreshing. It also * + * determines if the text can fit on the screen and makes adjustments so that it will. * + * * + * INPUT: text -- The text number to set the help system to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/11/1994 JLB : Won't draw past tactical map edges. * + *=============================================================================================*/ +void HelpClass::Set_Text(int text) +{ + if (text != TXT_NONE) { + Text = text; +// Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_MAP|TPF_NOSHADOW); + Width = String_Pixel_Width(Text_String(Text)); + if (IsRight) { + DrawX = X - Width; + DrawY = Y; + } else { + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth) - 3; + int bottom = TacPixelY + Lepton_To_Pixel(TacLeptonHeight) - 1; + + DrawX = X+X_OFFSET; + DrawY = Y+Y_OFFSET; + if (DrawX + Width > right) { + DrawX -= (DrawX+Width) - right; + } + if (DrawY + FontHeight > bottom) { + DrawY -= (DrawY+FontHeight) - bottom; + } + if (DrawX < TacPixelX+1) DrawX = TacPixelX+1; + if (DrawY < TacPixelY+1) DrawY = TacPixelY+1; + } + int lines = (Cost) ? 2 : 1; + memcpy((void*)OverlapList, Text_Overlap_List(Text_String(Text), DrawX-1, DrawY, lines), sizeof(OverlapList)); + } +} + + +/*********************************************************************************************** + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * * + * This routine intercepts the map scrolling request and then makes sure that if, in fact, * + * the map is going to scroll, then reset and erase the help text so that it doesn't * + * mess up the display. * + * * + * INPUT: facing -- The direction to scroll (unused by this routine). * + * * + * really -- If the scroll is actually going to occur, rather than just be examined * + * for legality, then this parameter will be true. If this parameter is * + * true, then the help text is reset. * + * * + * OUTPUT: Returns if it can, or did, scroll in the requested direction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +bool HelpClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (really) { + Help_Text(TXT_NONE); + } + return(TabClass::Scroll_Map(facing, distance, really)); +} + + +/*********************************************************************************************** + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * * + * Use this routine after the Help_Text() function to activate the second line. The second * + * line displays a cost. Typically, this is used by the sidebar to display the cost of the * + * specified item. * + * * + * INPUT: cost -- The cost to associate with this help text. If this value is zero, then * + * no second line is displayed, so don't pass in zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/09/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Cost(int cost) +{ + Cost = cost; +} + + +void HelpClass::Set_Tactical_Position(COORDINATE coord) +{ + if (TacticalCoord != coord) { + Help_Text(TXT_NONE); + } + TabClass::Set_Tactical_Position(coord); +} diff --git a/TIBERIANDAWN/HELP.H b/TIBERIANDAWN/HELP.H new file mode 100644 index 000000000..8ada3a24b --- /dev/null +++ b/TIBERIANDAWN/HELP.H @@ -0,0 +1,142 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\help.h_v 2.17 16 Oct 1995 16:46:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HELP_H +#define HELP_H + +#include "tab.h" + +class HelpClass: public TabClass +{ + public: + HelpClass(void); + + /* + ** Initialization + */ + virtual void Init_Clear(void); // Clears all to known state + + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Scroll_Map(DirType facing, int &distance, bool really); + virtual void Set_Tactical_Position(COORDINATE coord); + + void Help_Text(int text, int x=-1, int y=-1, int color=LTGREY, bool quick=false, int cost = 0); + void Set_Cost(int cost); + short const * Overlap_List(void) const; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + private: + + static char const *HelpText; + int HelpX; + int HelpY; + int HelpWidth; + + + void Set_Text(int text); + + /* + ** If the help text is right justified (as with the help text that pops up over the + ** sidebar icons), then this flag is set to true. + */ + unsigned IsRight:1; + + /* + ** If the optional second line of text that displays cost is desired, then this + ** value will be non-zero. Typically, this is true when the help text is associated + ** with one of the sidebar construction icons. + */ + int Cost; + + /* + ** This is the recorded position of the cursor at the time the help text + ** pops up. The help text is rendered as an offset from this pixel position. + */ + int X; + int Y; + + /* + ** This is the draw X and Y coordinate. This position is relative to the X and + ** Y coordinates but adjusted for screen edges as necessary. + */ + int DrawX; + int DrawY; + + /* + ** The width of the help text (in pixels) is stored here. This is a convenience + ** since calculating the width takes a bit of time. + */ + int Width; + + /* + ** The text number of the help text to display is held here. If no text is to be + ** displayed, then this value will be TXT_NONE. + */ + int Text; + + /* + ** This is the background color to use for the help text. It can change according + ** to the message displayed. + */ + int Color; + + /* + ** This countdown timer controls when the help text will pop up. If the mouse + ** remains stationary while this countdown timer expires, then the help text + ** will pop up. + */ + static CountDownTimerClass CountDownTimer; + + /* + ** This is a calculated cell offset list (from the Map.TacticalCell) that indicates + ** which cells are under the help text and thus which cells need to be redrawn if + ** the help text is to be erased. + */ + //static short const OverlapList[30]; + static short OverlapList[30]; // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM + + enum HelpClassEnum { + HELP_DELAY=TIMER_SECOND*1, // The countdown timer delay before help text pops up. + Y_OFFSET=0, // The Y pixel offset from cursor for help text print. + X_OFFSET=10, // The X pixel offset from cursor for help text print. + }; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/HOUSE.CPP b/TIBERIANDAWN/HOUSE.CPP new file mode 100644 index 000000000..b0a85059d --- /dev/null +++ b/TIBERIANDAWN/HOUSE.CPP @@ -0,0 +1,8637 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\house.cpv 2.13 02 Aug 1995 17:03:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : August 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseClass::AI -- Process house logic. * + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * HouseClass::Add_Nuke_Piece -- Add a nuclear piece to the collection. * + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * HouseClass::Attacked -- Lets player know if base is under attack. * + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * HouseClass::Blowup_All -- blows up everything * + * HouseClass::Can_Build -- Determines if the aircraft type can be built. * + * HouseClass::Can_Build -- Determines if the building type can be built. * + * HouseClass::Can_Build -- Determines if the infantry unit can be built by this house. * + * HouseClass::Can_Build -- Determines if the unit can be built by this house. * + * HouseClass::Can_Build -- General purpose build legality checker. * + * HouseClass::Clobber_All -- removes house & all its objects * + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * HouseClass::Has_Nuke_Device -- Deteremines if the house has a nuclear device. * + * HouseClass::HouseClass -- Constructor for a house object. * + * HouseClass::Init -- init's in preparation for new scenario * + * HouseClass::Init_Air_Strike -- Add (or reset) the air strike sidebar button. * + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * HouseClass::Init_Ion_Cannon -- Initialize the ion cannon countdown. * + * HouseClass::Init_Nuke_Bomb -- Adds (if necessary) the atom bomb to the sidebar. * + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * HouseClass::Make_Air_Strike_Available -- Make the airstrike available. * + * HouseClass::Make_Ally -- Make the specified house an ally. * + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * HouseClass::Read_INI -- Reads house specific data from INI. * + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * HouseClass::Remove_Air_Strike -- Removes the air strike button from the sidebar. * + * HouseClass::Remove_Ion_Cannon -- Disables the ion cannon. * + * HouseClass::Remove_Nuke_Bomb -- Removes the nuclear bomb from the sidebar. * + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * HouseClass::Spend_Money -- Removes money from the house. * + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * HouseClass::Validate -- validates house pointer * + * HouseClass::Write_INI -- Writes house specific data into INI file. * + * HouseClass::delete -- Deallocator function for a house object. * + * HouseClass::new -- Allocator for a house class. * + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * HouseClass::~HouseClass -- Default destructor for a house object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 3/26/2019 12:24PM +*/ +#include "SidebarGlyphx.h" +#include "defines.h" + + +/*********************************************************************************************** + * HouseClass::Validate -- validates house pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int HouseClass::Validate(void) const +{ + int num; + + num = Houses.ID(this); + if (num < 0 || num >= HOUSE_MAX) { + Validate_Error("HOUSE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * * + * This operator will automatically convert from a houses class object into the HousesType * + * enumerated value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the object's HousesType value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass::operator HousesType(void) const +{ + Validate(); + return(Class->House); +} + + +/*********************************************************************************************** + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * * + * Use this routine to convert a house number into the house pointer that it represents. * + * A simple index into the Houses template array is not sufficient, since the array order * + * is arbitrary. An actual scan through the house object is required in order to find the * + * house object desired. * + * * + * INPUT: house -- The house type number to look up. * + * * + * OUTPUT: Returns with a pointer to the house object that the house number represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass * HouseClass::As_Pointer(HousesType house) +{ + for (int index = 0; index < Houses.Count(); index++) { + if (Houses.Ptr(index)->Class->House == house) { + return(Houses.Ptr(index)); + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * * + * This basically calls the constructor for each of the houses in the game. All other * + * data specific to the house is initialized when the scenario is loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::One_Time(void) +{ +// for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { +// new(index) HouseClass; +// } + +#ifdef USE_RA_AI + /* + ** Required for Red Alert AI. ST - 7/23/2019 3:21PM + */ + BuildChoice.Set_Heap(STRUCT_COUNT); +#endif +} + + +/*********************************************************************************************** + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * * + * The handicap rating will affect combat, movement, and production for the house. It can * + * either make it more or less difficult for the house (controlled by the handicap value). * + * * + * INPUT: handicap -- The handicap value to assign to this house. The default value for * + * a house is DIFF_NORMAL. * + * * + * OUTPUT: Returns with the old handicap value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + * 10/22/1996 JLB : Uses act like value for multiplay only. * + *=============================================================================================*/ +DiffType HouseClass::Assign_Handicap(DiffType handicap) +{ + DiffType old = Difficulty; + Difficulty = handicap; + + if (GameToPlay != GAME_NORMAL) { + HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike); + FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias; + AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias; + ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias; + ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias; + CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias; + } else { + FirepowerBias = Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = Rule.Diff[handicap].GroundspeedBias; + AirspeedBias = Rule.Diff[handicap].AirspeedBias; + ArmorBias = Rule.Diff[handicap].ArmorBias; + ROFBias = Rule.Diff[handicap].ROFBias; + CostBias = Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias; + } + + return(old); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * * + * This utility function will output the current status of the house class to the mono * + * screen. Through this information bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Debug_Dump(MonoClass *) const +{ + Validate(); +} +#endif + + +/*********************************************************************************************** + * HouseClass::new -- Allocator for a house class. * + * * + * This is the allocator for a house class. Since there can be only * + * one of each type of house, this is allocator has restricted * + * functionality. Any attempt to allocate a house structure for a * + * house that already exists, just returns a pointer to the previously * + * allocated house. * + * * + * INPUT: house -- The house to allocate a class object for. * + * * + * OUTPUT: Returns with a pointer to the allocated class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void * HouseClass::operator new(size_t) +{ + void * ptr = Houses.Allocate(); + if (ptr) { + ((HouseClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * HouseClass::delete -- Deallocator function for a house object. * + * * + * This function marks the house object as "deallocated". Such a * + * house object is available for reallocation later. * + * * + * INPUT: ptr -- Pointer to the house object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::operator delete(void *ptr) +{ + if (ptr) { + ((HouseClass *)ptr)->IsActive = false; + } + Houses.Free((HouseClass *)ptr); +} + + +/*********************************************************************************************** + * HouseClass::HouseClass -- Constructor for a house object. * + * * + * This function is the constructor and it marks the house object * + * as being allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +HouseClass::HouseClass(HousesType house) : + Class(&HouseTypeClass::As_Reference(house)), + IonCannon(ION_CANNON_GONE_TIME, VOX_ION_READY, VOX_ION_CHARGING, VOX_ION_CHARGING, VOX_NO_POWER), + AirStrike(AIR_CANNON_GONE_TIME, VOX_AIRSTRIKE_READY, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY), + NukeStrike(NUKE_GONE_TIME, VOX_NUKE_AVAILABLE, VOX_NONE, VOX_NOT_READY, VOX_NO_POWER) +{ + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + UnitsKilled[i] = 0; + BuildingsKilled[i] = 0; + } + WhoLastHurtMe = house; // init this to myself + + IsVisionary = false; + IsFreeHarvester = false; + Blockage = 0; + UnitsLost = 0; + BuildingsLost = 0; + + NewActiveBScan = 0; + ActiveBScan = 0; + NewActiveUScan = 0; + ActiveUScan = 0; + NewActiveIScan = 0; + ActiveIScan = 0; + NewActiveAScan = 0; + ActiveAScan = 0; + + strcpy((char *)Name, "Computer"); // Default computer name. + JustBuilt = STRUCT_NONE; + AlertTime = 0; + IsAlerted = false; + IsAirstrikePending = false; + AircraftFactory = -1; + AircraftFactories = 0; + ActLike = Class->House; + Allies = 0; + AScan = 0; + NukeDest = 0; + BlitzTime.Clear(); + ScreenShakeTime.Clear(); + BScan = 0; + BuildingFactories = 0; + BuildingFactory = -1; + Capacity = 0; + Credits = 0; + CreditsSpent = 0; + CurBuildings = 0; + CurUnits = 0; + DamageTime = DAMAGE_DELAY; + Drain = 0; + Edge = SOURCE_NORTH; + FlagHome = 0; + FlagLocation = TARGET_NONE; + HarvestedCredits = 0; + HouseTriggers[house].Clear(); + IGaveUp = false; + InfantryFactories = 0; + InfantryFactory = -1; + InitialCredits = 0; + InitialCredits = 0; + IScan = 0; + IsRecalcNeeded = true; + IsCivEvacuated = false; + IsDefeated = false; + IsDiscovered = false; + IsHuman = false; + WasHuman = false; + IsMaxedOut = false; + IsStarted = false; + IsToDie = false; + IsToLose = false; + IsToWin = false; + Make_Ally(house); + MaxBuilding = 0; + MaxUnit = 0; + NewAScan = 0; + NewBScan = 0; + NewIScan = 0; + NewUScan = 0; + NukePieces = 0x07; + Power = 0; + Radar = RADAR_NONE; + RemapTable = Class->RemapTable; + RemapColor = Class->RemapColor; + Resigned = false; + SpeakAttackDelay = 1; + SpeakMaxedDelay = 1; + + SpeakMoneyDelay = 1; + SpeakMoneyDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); // 2 minutes + + SpeakPowerDelay = 1; + SpecialFactories = 0; + SpecialFactory = -1; + TeamTime = TEAM_DELAY; + Tiberium = 0; + TriggerTime = 0; + UnitFactories = 0; + UnitFactory = -1; + UScan = 0; + memset((void *)&Regions[0], 0x00, sizeof(Regions)); + + Init_Unit_Trackers(); + + DebugUnlockBuildables = false; + + StartLocationOverride = -1; + + /* + ** New AI variables from RA. Need to add to save/load? + */ +#ifdef USE_RA_AI + + IsBaseBuilding = true; + Center = 0; + Radius = 0; + for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { + ZoneInfo[zone].AirDefense = 0; + ZoneInfo[zone].ArmorDefense = 0; + ZoneInfo[zone].InfantryDefense = 0; + } + + + LATime = 0; + LAType = RTTI_NONE; + LAZone = ZONE_NONE; + LAEnemy = HOUSE_NONE; + + ToCapture = TARGET_NONE; + + RadarSpied = 0; + PointTotal = 0; + + memset(BQuantity, '\0', sizeof(BQuantity)); + memset(UQuantity, '\0', sizeof(UQuantity)); + memset(IQuantity, '\0', sizeof(IQuantity)); + memset(AQuantity, '\0', sizeof(AQuantity)); + + Attack = 0; + + Enemy = HOUSE_NONE; + + AITimer = 0; + + BuildStructure = STRUCT_NONE; + BuildUnit = UNIT_NONE; + BuildInfantry = INFANTRY_NONE; + BuildAircraft = AIRCRAFT_NONE; + + State = STATE_BUILDUP; + + IsTiberiumShort = false; + + IQ = Rule.MaxIQ; + + IsParanoid = false; + + OldBScan = 0; + + Assign_Handicap(DIFF_NORMAL); + +#endif + +} + + +HouseClass::~HouseClass (void) +{ + Free_Unit_Trackers(); +} + +/*********************************************************************************************** + * HouseClass::Can_Build -- General purpose build legality checker. * + * * + * This routine is called when it needs to be determined if the specified object type can * + * be built by this house. Production and sidebar maintenance use this routine heavily. * + * * + * INPUT: type -- Pointer to the type of object that legality is to be checked for. * + * * + * house -- This is the house to check for legality against. Note that this might * + * not be 'this' house since the check could be from a captured factory. * + * Captured factories build what the original owner of them could build. * + * * + * OUTPUT: Can the specified object be built? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * + *=============================================================================================*/ +bool HouseClass::Can_Build(TechnoTypeClass const * type, HousesType house) const +{ + Validate(); + if (!type || !type->IsBuildable || !((1L << house) & type->Ownable)) return(false); + + /* + ** The computer can always build everthing. + */ +#ifdef USE_RA_AI + if (!IsHuman && GameToPlay == GAME_NORMAL) return(true); // Added game type check for AI from RA. ST - 7/25/2019 3:25PM +#else + if (!IsHuman) return(true); +#endif + + /* + ** Perform some equivalency fixups for the building existance flags. + */ + long flags = ActiveBScan; + +#ifdef USE_RA_AI + // OldBScan Copied from RA for AI. ST - 7/25/2019 3:27PM + /* + ** The computer records prerequisite buildings because it can't relay on the + ** sidebar to keep track of this information. + */ + if (!IsHuman) { + flags = OldBScan; + } +#endif + + int pre = type->Pre; + if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; + if (flags & STRUCTF_HAND) flags |= STRUCTF_BARRACKS; + if (flags & STRUCTF_OBELISK) flags |= STRUCTF_ATOWER; + if (flags & STRUCTF_TEMPLE) flags |= STRUCTF_EYE; + if (flags & STRUCTF_AIRSTRIP) flags |= STRUCTF_WEAP; + if (flags & STRUCTF_SAM) flags |= STRUCTF_HELIPAD; + + /* + ** Multiplayer game uses a different legality check for building. + */ + if (GameToPlay != GAME_NORMAL || (Special.IsJurassic && AreThingiesEnabled)) { + if (DebugUnlockBuildables) { + return true; + } + return((pre & flags) == pre && type->Level <= BuildLevel); + } + +#ifdef NEWMENU + int level = BuildLevel; +#else + int level = Scenario; +#endif + + /* + ** Special check to make the mission objective buildings the prerequisite + ** for the stealth tank in mission #11 only. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_STANK && + level == 11) { + + pre = STRUCTF_MISSION; + level = type->Scenario; + } + + /* + ** Special case check to ensure that GDI doesn't get the bazooka guy + ** until mission #8. + */ + if (house == HOUSE_GOOD && + type->What_Am_I() == RTTI_INFANTRYTYPE && + ((InfantryTypeClass const *)type)->Type == INFANTRY_E3 && + level < 7) { + + return(false); + } + + /* + ** Special check to allow GDI to build the MSAM by mission #9 + ** and no sooner. + */ + if (house == HOUSE_GOOD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_MLRS && + level < 9) { + + return(false); + } + + /* + ** Special case to disable the APC from the Nod player. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_APC) { + + return(false); + } + + /* + ** Ensure that the Temple of Nod cannot be built by GDI even + ** if GDI has captured the Nod construction yard. + */ + if (type->What_Am_I() == RTTI_BUILDINGTYPE && + (((BuildingTypeClass const *)type)->Type == STRUCT_TEMPLE || ((BuildingTypeClass const *)type)->Type == STRUCT_OBELISK) && + Class->House == HOUSE_GOOD) { + + return(false); + } + + /* + ** Ensure that the rocket launcher tank cannot be built by Nod. + */ + if (type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_MLRS && + Class->House == HOUSE_BAD) { + + return(false); + } + + /* + ** Ensure that the ion cannon cannot be built if + ** Nod has captured the GDI construction yard. + */ + if (type->What_Am_I() == RTTI_BUILDINGTYPE && + (((BuildingTypeClass const *)type)->Type == STRUCT_EYE) && + Class->House == HOUSE_BAD) { + + return(false); + } + + /* + ** Nod can build the advanced power plant at scenario #12. + */ + if (house == HOUSE_BAD && + level >= 12 && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_ADVANCED_POWER) { + + level = type->Scenario; + } + + /* + ** Nod cannot build a helipad in the normal game. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_HELIPAD) { + + return(false); + } + + /* + ** GDI can build the sandbag wall only from scenario #9 onwards. + */ + if (house == HOUSE_GOOD && + level < 8 && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_SANDBAG_WALL) { + + return(false); + } + + /* + ** GDI has a special second training mission. Adjust the scenario level so that + ** scenario two will still feel like scenario #1. + */ + if (house == HOUSE_GOOD && level == 2) { + level = 1; + } + + // ST - 8/23/2019 4:53PM + if (DebugUnlockBuildables) { + level = 98; + pre = 0; + } + + if (Debug_Cheat) level = 98; + return((pre & flags) == pre && type->Scenario <= level); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the building type can be built. * + * * + * This routine is used by the construction preparation code to building a list of building * + * types that can be built. It determines if a building can be built by checking if the * + * prerequisite buildings have been built (and still exist) as well as checking to see if * + * the house can build the specified structure. * + * * + * INPUT: s -- The structure type number that is being checked. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can this structure type be built at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/08/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(StructType s, HousesType house) const +{ + Validate(); + return(Can_Build(&BuildingTypeClass::As_Reference(s), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the infantry unit can be built by this house. * + * * + * Use this routine to determine if the infantry type specified can be built by this * + * house. It determines this by checking the ownership allowed bits in the infantry * + * type class. * + * * + * INPUT: infantry -- The infantry type to check against this house. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can the infantry be produced by this house? * + * * + * WARNINGS: It does not check to see if there is a functional barracks available, but * + * merely checks to see if it is legal for this house to build that infantry * + * type. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(InfantryType infantry, HousesType house) const +{ + Validate(); + return(Can_Build(&InfantryTypeClass::As_Reference(infantry), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the unit can be built by this house. * + * * + * This routine is used to determine if the unit type specified can in fact be built by * + * this house. It checks the ownable bits in the unit's type to determine this. * + * * + * INPUT: unit -- The unit type to check against this house. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can the unit be built by this house? * + * * + * WARNINGS: This doesn't check to see if there is a functional factory that can build * + * this unit, but merely if the unit can be built according to ownership rules. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(UnitType unit, HousesType house) const +{ + Validate(); + return(Can_Build(&UnitTypeClass::As_Reference(unit), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the aircraft type can be built. * + * * + * Use this routine to determine if the specified aircraft type can be built. This routine * + * is used by the sidebar and factory to determine what can be built. * + * * + * INPUT: aircraft -- The aircraft type to check for build legality. * + * * + * house -- The house that is performing the check. This is typically the house * + * of the original building of the factory rather than the current * + * owner. * + * * + * OUTPUT: Can this aircraft type be built by the house specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Can_Build(AircraftType aircraft, HousesType house) const +{ + Validate(); + return(Can_Build(&AircraftTypeClass::As_Reference(aircraft), house)); +} + + +/*************************************************************************** + * HouseClass::Init -- init's in preparation for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 12/17/1994 JLB : Resets tracker bits. * + *=========================================================================*/ +void HouseClass::Init(void) +{ + Houses.Free_All(); + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseTriggers[index].Clear(); + } +} + + +// Object selection list is switched with player context for GlyphX. ST - 4/17/2019 9:42AM +extern void Logic_Switch_Player_Context(HouseClass *house); + +/*********************************************************************************************** + * HouseClass::AI -- Process house logic. * + * * + * This handles the AI for the house object. It should be called once per house per game * + * tick. It processes all house global tasks such as low power damage accumulation and * + * house specific trigger events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * + *=============================================================================================*/ +extern void Recalculate_Placement_Distances(); +extern void On_Message(const char* message, float timeout_seconds, long long message_id); + +void HouseClass::AI(void) +{ + Validate(); + + // Set PlayerPtr to be this house. Not really keen on this solution but it means I have to make fewer code changes to the original code. ST - 4/17/2019 4:32PM + Logic_Switch_Player_Context(this); + + /* + ** Reset the scan accumulation bits for the next logic pass. + */ + IScan = NewIScan; + BScan = NewBScan; + UScan = NewUScan; + AScan = NewAScan; + ActiveIScan = NewActiveIScan; + ActiveBScan = NewActiveBScan; + ActiveUScan = NewActiveUScan; + ActiveAScan = NewActiveAScan; + NewIScan = 0; + NewBScan = 0; + NewUScan = 0; + NewAScan = 0; + NewActiveIScan = 0; + NewActiveBScan = 0; + NewActiveUScan = 0; + NewActiveAScan = 0; + +#ifdef USE_RA_AI + // + // Copied from RA for AI. ST - 7/25/2019 3:58PM + // + /* + ** If base building has been turned on by a trigger, then force the house to begin + ** production and team creation as well. This is also true if the IQ is high enough to + ** being base building. + */ + if (!IsHuman && GameToPlay != GAME_NORMAL && (IsBaseBuilding || IQ >= Rule.IQProduction)) { + IsBaseBuilding = true; + IsStarted = true; + IsAlerted = true; + } +#endif + + /* + ** Check to see if the house wins. + */ + if (GameToPlay == GAME_NORMAL && IsToWin && BorrowedTime.Expired() && Blockage <= 0) { + IsToWin = false; + if (this == PlayerPtr) { + PlayerWins = true; + } else { + PlayerLoses = true; + } + } + + /* + ** Check to see if the house loses. + */ + if (GameToPlay == GAME_NORMAL && IsToLose && BorrowedTime.Expired()) { + IsToLose = false; + if (this == PlayerPtr) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + } + + /* + ** Check to see if all objects of this house should be blown up. + */ + if (IsToDie && BorrowedTime.Expired()) { + IsToDie = false; + Blowup_All(); + } + + /* + ** Double check power values to correct illegal conditions. It is possible to + ** get a power output of negative (one usually) as a result of damage sustained + ** and the fixed point fractional math involved with power adjustements. If the + ** power rating drops below zero, then make it zero. + */ + if (GameToPlay == GAME_NORMAL) { + Power = MAX(Power, 0); + Drain = MAX(Drain, 0); + } + + /* + ** If the base has been alerted to the enemy and should be attacking, then + ** see if the attack timer has expired. If it has, then create the attack + ** teams. + */ + if (IsAlerted && AlertTime.Expired()) { + + /* + ** Adjusted to reduce maximum number of teams created. + */ + int maxteams = Random_Pick(2, (int)(((BuildLevel-1)/3)+1)); + for (int index = 0; index < maxteams; index++) { + TeamTypeClass const * ttype = Suggested_New_Team(true); + if (ttype) { + ScenarioInit++; + ttype->Create_One_Of(); + ScenarioInit--; + } + } + if (GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_HARD) { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(4, 10)); + } else { + if (GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY) { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(16, 40)); + } else { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(5, 20)); + } + } + } + + /* + ** Create teams for this house if necessary. + ** (Use the same timer for some extra capture-the-flag logic.) + */ + if (TeamTime.Expired()) { + TeamTypeClass const * ttype = Suggested_New_Team(false); + if (ttype) { + ttype->Create_One_Of(); + } + + /* + ** Also use this timer to detect if someone is sitting on my flag cell. + */ + if (Special.IsCaptureTheFlag && GameToPlay != GAME_NORMAL) { + TechnoClass *techno; + int moving; + + /* + ** If this house's flag waypoint is a valid cell, see if there's + ** someone sitting on it. If so, make the scatter. + */ + if (FlagHome) { + techno = Map[FlagHome].Cell_Techno(); + if (techno) { + moving = false; + if (techno->What_Am_I() == RTTI_INFANTRY || + techno->What_Am_I() == RTTI_UNIT) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + if (!moving) { + techno->Scatter(0,true,true); + } + } + } + } + + /* + ** Randomly create a Visceroid or other disastrous multiplayer object. + ** Create the object, and use Scan_Place_Object to place the object near + ** the center of the map. + */ + if (GameToPlay != GAME_NORMAL && Class->House==HOUSE_JP) { + int rlimit; + + if (Special.IsJurassic && AreThingiesEnabled) { + rlimit = 450; + } else { + rlimit = 1000; + } + + //if (IRandom(0, rlimit) == 0) { + if (IRandom(0, rlimit) <= 5) { // More visceroids! ST - 3/3/2020 4:34PM + UnitClass *obj = NULL; + CELL cell; + + if (Special.IsJurassic && AreThingiesEnabled) { + obj = new UnitClass(Random_Pick(UNIT_TRIC, UNIT_STEG), HOUSE_JP); + } else if (Special.IsVisceroids) { + if (BuildLevel >= 7) { + if (!(UScan & UNITF_VICE)) { + obj = new UnitClass(UNIT_VICE, HOUSE_JP); + } + } + } + + if (obj) { + cell = XY_Cell (Map.MapCellX + Random_Pick(0, Map.MapCellWidth - 1), + Map.MapCellY + Random_Pick(0, Map.MapCellHeight - 1)); + if (!Scan_Place_Object(obj, cell)) { + delete obj; + } + } + } + } + + TeamTime.Set(TEAM_DELAY); + } + + /* + ** If there is insufficient power, then all buildings that are above + ** half strength take a little bit of damage. + */ + if (DamageTime.Expired()) { + +/* +** No free harvesters for computer or player. - 8/16/95 +*/ +#ifdef OBSOLETE + /* + ** Replace the last harvester if there is a refinery present. + */ + if (GameToPlay == GAME_NORMAL && + Frame > 5 && + (!IsHuman && BuildLevel <= 6) && + (ActiveBScan & STRUCTF_REFINERY) != 0 && + (UScan & UNITF_HARVESTER) == 0 && + !IsFreeHarvester) { + + IsFreeHarvester = true; + FreeHarvester = TICKS_PER_MINUTE * 2; + } +#endif + + /* + ** If a free harvester is to be created and the time is right, then create + ** the harvester and clear the free harvester pending flag. + */ + if (IsFreeHarvester && FreeHarvester.Expired()) { + IsFreeHarvester = false; + Create_Special_Reinforcement(this, (TechnoTypeClass *)&UnitTypeClass::As_Reference(UNIT_HARVESTER), NULL); + } + + /* + ** When the power is below required, then the buildings will take damage over + ** time. + */ + if (Power_Fraction() < 0x100) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass & b = *Buildings.Ptr(index); + + if (b.House == this && b.Health_Ratio() > 0x080) { + if (b.Class->Drain) { + int damage = 1; + b.Take_Damage(damage, 0, WARHEAD_AP, 0); + } + } + } + } + DamageTime.Set(DAMAGE_DELAY); + } + + /* + ** If there are no more buildings to sell, then automatically cancel the + ** sell mode. + */ + if (PlayerPtr == this && !BScan && Map.IsSellMode) { + Map.Sell_Mode_Control(0); + } + + /* + ** Various base conditions may be announced to the player. + */ + if (PlayerPtr == this) { + + if (SpeakMoneyDelay.Expired() && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) { + + // MBL 03.23.2020 - Change "Need more funds" to "Insufficient funds" per https://jaas.ea.com/browse/TDRA-5370 + // Speak(VOX_NEED_MO_MONEY); + Speak(VOX_NO_CASH); + + // + // !!! MBL 03.18.2020: + // !!! Changing "Insufficient Funds" speak delay for this case to 1 minute instead of 2. + // !!! Note that all other speak delays are 2 minutes in TD (SPEAK_DELAY) and RA (Rule.SpeakDelay) + // !!! This is per https://jaas.ea.com/browse/TDRA-4659 (Ted and Jim) + // !!! I Checked with Joe mostly okay with this change, but want to note that we are changing original behavior + // !!! All other speak delays in TD and RA (max capacity and low power) remain at 2 minutes. + // !!! Also, in Red Alert, this is still 2 minutes from Rules.ini (SpeakDelay variable) + // + // SpeakMoneyDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); // 2 minutes + // + // MBL 05.18.2020: Setting back to 2 minutes as requested per https://jaas.ea.com/browse/TDRA-5834 + // + // SpeakMoneyDelay.Set(Options.Normalize_Delay(SPEAK_DELAY / 2)); // 1 minute (new) + SpeakMoneyDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); // 2 minutes (original) + + int text_id = TXT_INSUFFICIENT_FUNDS; + char const * text = Text_String(TXT_INSUFFICIENT_FUNDS); + if (text != NULL) { + On_Message(text, 35.0f, text_id); + } + } + if (SpeakMaxedDelay.Expired() && IsMaxedOut) { + IsMaxedOut = false; + if ((Capacity - Tiberium) < 300 && Capacity > 500 && (BScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + SpeakMaxedDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + } + } + if (SpeakPowerDelay.Expired() && Power_Fraction() < 0x0100) { + if (BScan & STRUCTF_CONST) { + Speak(VOX_LOW_POWER); + SpeakPowerDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + + int text_id = TXT_LOW_POWER; + char const * text = Text_String(TXT_LOW_POWER); + if (text != NULL) { + On_Message(text, 35.0f, text_id); + } + } + } + } + + /* + ** If there is a flag associated with this house, then mark it to be + ** redrawn. + */ + if (Target_Legal(FlagLocation)) { + UnitClass * unit = As_Unit(FlagLocation); + if (unit) { + unit->Mark(MARK_CHANGE); + } else { + CELL cell = As_Cell(FlagLocation); + Map[cell].Redraw_Objects(); + } + } + + bool is_time = false; + + /* + ** Triggers are only checked every so often. If the trigger timer has expired, + ** then set the trigger processing flag. + */ + if (TriggerTime.Expired()) { + is_time = true; + TriggerTime = TICKS_PER_MINUTE/10; + } + + /* + ** Check to see if the ion cannon should be removed from the sidebar + ** because of outside circumstances. The advanced communications facility + ** being destroyed is a good example of this. + */ + if (IonCannon.Is_Present()) { + if (!(ActiveBScan & STRUCTF_EYE) && !IonCannon.Is_One_Time()) { + + /* + ** Remove the ion cannon when there is no advanced communication facility. + ** Note that this will not remove the one time created ion cannon. + */ + if (IonCannon.Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + + /* + ** Turn the ion cannon suspension on or off depending on the available + ** power. Note that one time ion cannon will not be affected by this. + */ + IonCannon.Suspend(Power_Fraction() < 0x0100); + + /* + ** Process the ion cannon AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (IonCannon.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } + + /* + ** The computer may decide to fire the ion cannon if it is ready. + */ + if (IonCannon.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_ION_CANNON); + } + + } else { + + /* + ** If there is no ion cannon present, but there is an advanced communcation + ** center available, then make the ion cannon available as well. + */ + if ((GameToPlay == GAME_NORMAL || Rule.AllowSuperWeapons) && + (ActiveBScan & STRUCTF_EYE) && + (ActLike == HOUSE_GOOD || GameToPlay != GAME_NORMAL) && + (IsHuman || GameToPlay != GAME_NORMAL)) { + + IonCannon.Enable(false, this == PlayerPtr, Power_Fraction() < 0x0100); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_ION_CANNON, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the nuke strike should be removed from the sidebar + ** because of outside circumstances. The Temple of Nod + ** being destroyed is a good example of this. + */ + if (NukeStrike.Is_Present()) { + if (!(ActiveBScan & STRUCTF_TEMPLE) && (!NukeStrike.Is_One_Time() || GameToPlay == GAME_NORMAL)) { + + /* + ** Remove the nuke strike when there is no Temple of Nod. + ** Note that this will not remove the one time created nuke strike. + */ + if (NukeStrike.Remove(true)) { + IsRecalcNeeded = true; + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } else { + + /* + ** Turn the nuke strike suspension on or off depending on the available + ** power. Note that one time nuke strike will not be affected by this. + */ + NukeStrike.Suspend(Power_Fraction() < 0x0100); + + /* + ** Process the nuke strike AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (NukeStrike.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } + + /* + ** The computer may decide to fire the nuclear missile if it is ready. + */ + if (NukeStrike.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + } + + } else { + + /* + ** If there is no nuke strike present, but there is a Temple of Nod + ** available, then make the nuke strike strike available. + */ + if ((GameToPlay == GAME_NORMAL || Rule.AllowSuperWeapons) && (ActiveBScan & STRUCTF_TEMPLE) && Has_Nuke_Device() && IsHuman) { + NukeStrike.Enable((GameToPlay == GAME_NORMAL), this == PlayerPtr); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Process the airstrike AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (AirStrike.Is_Present()) { + if (AirStrike.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + + /* + ** The computer may decide to call in the airstrike if it is ready. + */ + if (AirStrike.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_AIR_STRIKE); + } + } + + /* + ** Add the airstrike option if it is pending. + */ + if (IsAirstrikePending) { + IsAirstrikePending = false; + if (AirStrike.Enable(false, this == PlayerPtr)) { + AirStrike.Forced_Charge(this == PlayerPtr); + // Add to Glyphx multiplayer sidebar. ST - 3/22/2019 1:50PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_AIR_STRIKE, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + +#ifdef NEVER + /* + ** The following logic deals with the nuclear warhead state machine. It + ** handles all the different stages of the temple firing and the rocket + ** travelling up and down. The rocket explosion is handled by the anim + ** which is attached to the bullet. + */ + if (!IsHuman && NukePresent) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + + } +#endif + + if (GameToPlay != GAME_NORMAL) { + Check_Pertinent_Structures(); + } + + /* + ** Special win/lose check for multiplayer games; by-passes the + ** trigger system. We must wait for non-zero frame, because init + ** may not properly set IScan etc for each house; you have to go + ** through each object's AI before it will be properly set. + */ + if (GameToPlay != GAME_NORMAL && !IsDefeated && + !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && Frame > 0) { + MPlayer_Defeated(); + } + + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + TriggerClass * t = HouseTriggers[Class->House][index]; + + /* + ** Check for just built the building trigger event. + */ + if (JustBuilt != STRUCT_NONE) { + if (t->Spring(EVENT_BUILD, Class->House, JustBuilt)) { + JustBuilt = STRUCT_NONE; + continue; + } + } + + /* + ** Check for civilian evacuation trigger event. + */ + if (IsCivEvacuated && t->Spring(EVENT_EVAC_CIVILIAN, Class->House)) { + continue; + } + + /* + ** Number of buildings destroyed checker. + */ + if (t->Spring(EVENT_NBUILDINGS_DESTROYED, Class->House, BuildingsLost)) { + continue; + } + + /* + ** Number of units destroyed checker. + */ + if (t->Spring(EVENT_NUNITS_DESTROYED, Class->House, UnitsLost)) { + continue; + } + + /* + ** House has been revealed trigger event. + */ + if (IsDiscovered && t->Spring(EVENT_HOUSE_DISCOVERED, Class->House)) { + IsDiscovered = false; + continue; + } + + /* + ** The "all destroyed" triggers are only processed after a certain + ** amount of safety time has expired. + */ + if (!EndCountDown) { + + /* + ** All buildings destroyed checker. + */ + if (!ActiveBScan) { + if (t->Spring(EVENT_BUILDINGS_DESTROYED, Class->House)) { + continue; + } + } + + /* + ** All units destroyed checker. + */ + if (!((ActiveUScan & ~(UNITF_GUNBOAT)) | IScan | (ActiveAScan & ~(AIRCRAFTF_TRANSPORT|AIRCRAFTF_CARGO|AIRCRAFTF_A10)))) { + if (t->Spring(EVENT_UNITS_DESTROYED, Class->House)) { + continue; + } + } + + /* + ** All buildings AND units destroyed checker. + */ + if (!(ActiveBScan | (ActiveUScan & ~(UNITF_GUNBOAT)) | IScan | (ActiveAScan & ~(AIRCRAFTF_TRANSPORT|AIRCRAFTF_CARGO|AIRCRAFTF_A10)))) { + if (t->Spring(EVENT_ALL_DESTROYED, Class->House)) { + continue; + } + } + } + + /* + ** Credit check. + */ + if (t->Spring(EVENT_CREDITS, Class->House, Credits)) { + continue; + } + + /* + ** Timeout check. + */ + if (is_time && t->Spring(EVENT_TIME, Class->House)) { + continue; + } + + /* + ** All factories destroyed check. + */ + if (!(BScan & (STRUCTF_AIRSTRIP|STRUCTF_HAND|STRUCTF_WEAP|STRUCTF_BARRACKS)) && t->Spring(EVENT_NOFACTORIES, Class->House)) { + continue; + } + } + + /* + ** If a radar facility is not present, but the radar is active, then turn the radar off. + ** The radar also is turned off when the power gets below 100% capacity. + */ + if (PlayerPtr == this) { + if (Map.Is_Radar_Active()) { + if (BScan & (STRUCTF_RADAR|STRUCTF_EYE)) { + if (Power_Fraction() < 0x0100) { + Map.Radar_Activate(0); + } + } else { + Map.Radar_Activate(0); + } + } else { + if (BScan & (STRUCTF_RADAR|STRUCTF_EYE)) { + if (Power_Fraction() >= 0x0100) { + Map.Radar_Activate(1); + } + } else { + if (Map.Is_Radar_Existing()) { + Map.Radar_Activate(4); + } + } + } + if (!(BScan & (STRUCTF_RADAR|STRUCTF_EYE))) { + Radar = RADAR_NONE; + } else { + Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF; + } + } + + + VisibleCredits.AI(false, this, true); + + + /* + ** Copied from Red Alert for multiplayer AI. ST - 7/23/2019 3:02PM + ** + ** + ** + */ +#ifdef USE_RA_AI + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER && IsHuman == false) { + + /* + ** Perform any expert system AI processing. + */ + if (IsBaseBuilding && AITimer == 0) { + AITimer = Expert_AI(); + } + + if (!IsBaseBuilding && State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } + + AI_Building(); + AI_Unit(); + AI_Vessel(); + AI_Infantry(); + AI_Aircraft(); + } +#endif + + /* + ** If the production possibilities need to be recalculated, then do so now. This must + ** occur after the scan bits have been properly updated. + */ + if (PlayerPtr == this && IsRecalcNeeded) { + IsRecalcNeeded = false; + Map.Recalc(); + +#ifdef NEVER + /* + ** Remove the ion cannon if necessary. + */ + if (IonCannon.Is_Present() && !(BScan & STRUCTF_EYE)) { + IonCannon.Remove(); + } + + /* + ** Remove the nuclear bomb if necessary. + */ + if (NukeStrike.Is_Present() && !(BScan & STRUCTF_TEMPLE)) { + NukeStrike.Remove(); + } +#endif + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building && building->Owner() == Class->House) { + + building->Update_Specials(); + if (PlayerPtr == building->House) { + building->Update_Buildables(); + } + } + } + + Recalculate_Placement_Distances(); + } +} + + +/*********************************************************************************************** + * HouseClass::Attacked -- Lets player know if base is under attack. * + * * + * Call this function whenever a building is attacked (with malice). This function will * + * then announce to the player that his base is under attack. It checks to make sure that * + * this is referring to the player's house rather than the enemy's. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Attacked(BuildingClass* source) +{ + Validate(); + if (SpeakAttackDelay.Expired() && PlayerPtr->Class->House == Class->House) { + Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0); + SpeakAttackDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + + /* + ** If there is a trigger event associated with being attacked, process it + ** now. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + HouseTriggers[Class->House][index]->Spring(EVENT_ATTACKED, Class->House); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * * + * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * + * all storage capable buildings for the house. Harvested Tiberium adds to the credit * + * value of the house, but only up to the maximum storage capacity that the house can * + * currently maintain. * + * * + * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Harvested(unsigned tiberium) +{ + Validate(); + long oldtib = Tiberium; + + Tiberium += tiberium; + if (Tiberium > Capacity) { + Tiberium = Capacity; + IsMaxedOut = true; + } + HarvestedCredits += tiberium; + Silo_Redraw_Check(oldtib, Capacity); +} + + +/*********************************************************************************************** + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * * + * Use this routine to determine the total credit value of the house. This is the sum of * + * the harvested Tiberium in storage and the initial unspent cash reserves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total credit value of the house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +long HouseClass::Available_Money(void) const +{ + Validate(); + return(Tiberium + Credits); +} + + +/*********************************************************************************************** + * HouseClass::Spend_Money -- Removes money from the house. * + * * + * Use this routine to extract money from the house. Typically, this is a result of * + * production spending. The money is extracted from available cash reserves first. When * + * cash reserves are exhausted, then Tiberium is consumed. * + * * + * INPUT: money -- The amount of money to spend. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/20/1995 JLB : Spends Tiberium before spending cash. * + *=============================================================================================*/ +void HouseClass::Spend_Money(unsigned money) +{ + Validate(); + long oldtib = Tiberium; + if ((int)money > Tiberium) { + money -= (unsigned)Tiberium; + Tiberium = 0; + Credits -= money; + } else { + Tiberium -= money; + } + Silo_Redraw_Check(oldtib, Capacity); + CreditsSpent += money; +} + + +/*********************************************************************************************** + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * * + * Use this routine when money needs to be refunded back to the house. This can occur when * + * construction is aborted. At this point, the exact breakdown of Tiberium or initial * + * credits used for the orignal purchase is lost. Presume as much of the money is in the * + * form of Tiberium as storage capacity will allow. * + * * + * INPUT: money -- The number of credits to refund back to the house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/01/1995 JLB : Refunded money is never lost * + *=============================================================================================*/ +void HouseClass::Refund_Money(unsigned money) +{ + Validate(); + Credits += money; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * * + * Use this routine to adjust the maximum storage capacity for the house. This storage * + * capacity will limit the number of Tiberium credits that can be stored at any one time. * + * * + * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * + * * + * inanger -- Is this a forced adjustment to capacity due to some hostile event? * + * * + * OUTPUT: Returns with the number of Tiberium credits lost. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Adjust_Capacity(int adjust, bool inanger) +{ + Validate(); + long oldcap = Capacity; + int retval = 0; + + Capacity += adjust; + Capacity = MAX(Capacity, 0L); + if (Tiberium > Capacity) { + retval = Tiberium - Capacity; + Tiberium = Capacity; + if (!inanger) { + Refund_Money(retval); + retval = 0; + } else { + IsMaxedOut = true; + } + } + Silo_Redraw_Check(Tiberium, oldcap); + return(retval); +} + + +/*********************************************************************************************** + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * * + * Call this routine when either the capacity or tiberium levels change for a house. This * + * routine will determine if the aggregate tiberium storage level will result in the * + * silos changing their imagery. If this is detected, then all the silos for this house * + * are flagged to be redrawn. * + * * + * INPUT: oldtib -- Pre-change tiberium level. * + * * + * oldcap -- Pre-change tiberium storage capacity. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) +{ + Validate(); + int oldratio = 0; + if (oldcap) oldratio = (oldtib * 5) / oldcap; + int newratio = 0; + if (Capacity) newratio = (Tiberium * 5) / Capacity; + + if (oldratio != newratio) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { + b->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +void HouseClass::Read_INI(char *buffer) +{ + HouseClass *p; // Pointer to current player data. + char const *hname; // Pointer to house name. + char buf[128]; + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + int maxunit = WWGetPrivateProfileInt(hname, "MaxUnit", EACH_UNIT_MAX, buffer); + + maxunit = MAX(maxunit, 150); + + int maxbuilding = WWGetPrivateProfileInt(hname, "MaxBuilding", EACH_BUILDING_MAX, buffer); + + maxbuilding = MAX(maxbuilding, 150); + + int credits = WWGetPrivateProfileInt(hname, "Credits", 0, buffer); + + p = new HouseClass(index); + + p->MaxBuilding = maxbuilding; + p->MaxUnit = maxunit; + p->Credits = (long)credits * 100; + p->InitialCredits = p->Credits; + WWGetPrivateProfileString(hname, "Edge", "", buf, sizeof(buf)-1, buffer); + p->Edge = Source_From_Name(buf); + if (p->Edge == SOURCE_NONE) { + p->Edge = SOURCE_NORTH; + } + + if (GameToPlay == GAME_NORMAL) { + WWGetPrivateProfileString(hname, "Allies", "", buf, sizeof(buf)-1, buffer); + if (strlen(buf)) { + char * tok = strtok(buf, ", \t"); + while (tok) { + HousesType h = HouseTypeClass::From_Name(tok); + p->Make_Ally(h); + tok = strtok(NULL, ", \t"); + } + + } else { + + /* + ** Special case for when no allies are specified in the INI file. + ** The GDI side defaults to considering the neutral side to be + ** friendly. + */ + if (p->Class->House == HOUSE_GOOD) { + p->Make_Ally(HOUSE_NEUTRAL); + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes house specific data into INI file. * + * * + * Use this routine to write the house specific data (for all houses) into the INI file. * + * It is used by the scenario editor when saving the scenario. * + * * + * INPUT: buffer -- INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Write_INI(char *buffer) +{ + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p) { + WWWritePrivateProfileInt(p->Class->IniName, "Credits", (int)(p->Credits / 100), buffer); + WWWritePrivateProfileString(p->Class->IniName, "Edge", Name_From_Source(p->Edge), buffer); + WWWritePrivateProfileInt(p->Class->IniName, "MaxUnit", p->MaxUnit, buffer); + WWWritePrivateProfileInt(p->Class->IniName, "MaxBuilding", p->MaxBuilding, buffer); + + bool first = true; + char sbuffer[100] = ""; + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (p->Is_Ally(house)) { + if (!first) strcat(sbuffer, ","); + strcat(sbuffer, As_Pointer(house)->Class->IniName); + first = false; + } + } + WWWritePrivateProfileString(p->Class->IniName, "Allies", sbuffer, buffer); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * * + * This routine will determine if the house number specified is a ally to this house. * + * * + * INPUT: house -- The house number to check to see if it is an ally. * + * * + * OUTPUT: Is the house an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(HousesType house) const +{ + Validate(); + if (house != HOUSE_NONE) { + return(((1<Class->House)); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * * + * This routine will examine the specified object and return whether it is an ally or not. * + * * + * INPUT: object -- The object to examine to see if it is an ally. * + * * + * OUTPUT: Is the specified object an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(ObjectClass const * object) const +{ + Validate(); + if (object) { + return(Is_Ally(object->Owner())); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Make_Ally -- Make the specified house an ally. * + * * + * This routine will make the specified house an ally to this house. An allied house will * + * not be considered a threat or potential target. * + * * + * INPUT: house -- The house to make an ally of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 08/08/1995 JLB : Breaks off combat when ally commences. * + *=============================================================================================*/ +void HouseClass::Make_Ally(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && !Is_Ally(house)) { + + /* + ** If in normal game play but the house is defeated, then don't allow the ally + ** key to work. + */ + if (!ScenarioInit && (IsDefeated || house == HOUSE_JP)) return; + + Allies |= (1 << house); + +#ifdef CHEAT_KEYS + if (Debug_Flag) { + HouseClass * enemy = HouseClass::As_Pointer(house); + if (enemy && !enemy->Is_Ally(this)) { + enemy->Make_Ally(Class->House); + } + } +#endif + + if ((Debug_Flag || GameToPlay != GAME_NORMAL) && !ScenarioInit) { + char buffer[80]; + + /* + ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure + ** that fighting will most likely stop when the cease fire begins. + */ + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + + if (object && !object->IsInLimbo && object->Owner() == Class->House) { + TARGET target = ((TechnoClass *)object)->TarCom; + if (Target_Legal(target) && As_Techno(target)) { + if (Is_Ally(As_Techno(target))) { + ((TechnoClass *)object)->TarCom = TARGET_NONE; + } + } + } + } + + sprintf(buffer, Text_String(TXT_HAS_ALLIED), Name, HouseClass::As_Pointer(house)->Name); + Messages.Add_Message(buffer, MPlayerTColors[RemapColor], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200, 0, 0); + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * * + * This routine will flag the house specified so that it will be an enemy to this house. * + * Enemy houses are legal targets for attack. * + * * + * INPUT: house -- The house to make an enemy of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/27/1995 JLB : Making war is a bilateral aaction. * + *=============================================================================================*/ +void HouseClass::Make_Enemy(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && Is_Ally(house)) { + HouseClass * enemy = HouseClass::As_Pointer(house); + Allies &= ~(1 << house); + if (enemy && enemy->Is_Ally(this)) { + enemy->Allies &= ~(1 << Class->House); + } + + if ((Debug_Flag || GameToPlay != GAME_NORMAL) && !ScenarioInit) { + char buffer[80]; + + sprintf(buffer, Text_String(TXT_AT_WAR), Name, enemy->Name); + Messages.Add_Message(buffer, MPlayerTColors[RemapColor], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * * + * This routine will return with the remap table to use when displaying an object owned * + * by this house. If the object is blushing (flashing), then the lightening remap table is * + * always used. The "unit" parameter allows proper remap selection for those houses that * + * have a different remap table for buildings or units. * + * * + * INPUT: blushing -- Is the object blushing (flashing)? * + * * + * unit -- Is the object a vehicle or infantry? * + * * + * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char const * HouseClass::Remap_Table(bool blushing, bool unit) const +{ + Validate(); + if (blushing) return(&Map.FadingLight[0]); + + /* + ** For normal game play, return the TypeClass's remap table for this + ** house type + */ + if (GameToPlay == GAME_NORMAL) { + /* + ** Special case exception for Nod and single player only. Remap + ** buildings to red as opposed to the default color of bluegrey. + */ + if (!unit && Class->House == HOUSE_BAD) { + return(RemapRed); + } + + /* + ** Special case Jurassic missions to use the bluegrey remapping + */ + if (Special.IsJurassic && Class->House == HOUSE_MULTI4) { + return(RemapLtBlue); + } + + return(Class->RemapTable); + } else { + + /* + ** For multiplayer games, return the remap table for this exact house instance. + */ + return(RemapTable); + } +} + + +/*********************************************************************************************** + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * * + * This routine examines the house condition and returns with the team that it thinks * + * should be created. The units that are not currently a member of a team are examined * + * to determine the team needed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) +{ + Validate(); + return(TeamTypeClass::Suggested_New_Team(this, UScan, IScan, IsAlerted && alertcheck)); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * * + * This routine is called when the threat rating for a region needs to change. The region * + * and threat adjustment are provided. * + * * + * INPUT: region -- The region that adjustment is to occur on. * + * * + * threat -- The threat adjustment to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Threat(int region, int threat) +{ + Validate(); + static int _val[] = { + -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, + -1, 0, 1, + MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 + }; + static int _thr[] = { + 2, 1, 2, + 1, 0, 1, + 2, 1, 2 + }; + int neg; + int *val = &_val[0]; + int *thr = &_thr[0]; + + if (threat < 0) { + threat = -threat; + neg = true; + } else { + neg = false; + } + + for (int lp = 0; lp < 9; lp ++) { + Regions[region + *val].Adjust_Threat(threat >> *thr, neg); + val++; + thr++; + } +} + + +/*********************************************************************************************** + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * * + * This routine is called from the event system. It will start production for the object * + * type specified. This will be reflected in the sidebar as well as the house factory * + * tracking variables. * + * * + * INPUT: type -- The type of object to begin production on. * + * * + * id -- The subtype of object. * + * * + * OUTPUT: Returns with the reason why, or why not, production was started. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Begin_Production(RTTIType type, int id) +{ + Validate(); + int * factory = 0; + int result = true; + bool initial_start = false; + FactoryClass * fptr; + TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code, unless we are restarting production. + */ + if (*factory != -1) { + fptr = Factories.Raw_Ptr(*factory); + if (fptr->Is_Building()) + return(PROD_CANT); + } else { + fptr = new FactoryClass(); + if (!fptr) return(PROD_CANT); + *factory = Factories.ID(fptr); + result = (tech) ? fptr->Set(*tech, *this) : fptr->Set(id, *this); + initial_start = true; + + /* + ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts. + ** ST - 3/17/2020 2:03PM + */ + if (!result) { + delete fptr; + fptr = NULL; + *factory = -1; + } + } + + if (result) { + fptr->Start(); + + /* + ** Link this factory to the sidebar so that proper graphic feedback + ** can take place. + */ + // Handle Glyphx multiplayer sidebar. ST - 3/26/2019 1:27PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Factory_Link(*factory, type, id, this); + } + } else { + if (PlayerPtr == this) { + Map.Factory_Link(*factory, type, id); + } + } + + return(PROD_OK); + } + return(PROD_CANT); +} + + +/*********************************************************************************************** + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * * + * This routine is called from the event system whenever the production of the specified * + * type needs to be suspended. The suspended production will be reflected in the sidebar * + * as well as in the house control structure. * + * * + * INPUT: type -- The type of object that production is being suspended for. * + * * + * OUTPUT: Returns why, or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Suspend_Production(RTTIType type) +{ + Validate(); + int * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code. + */ + if (*factory == -1) return(PROD_CANT); + + /* + ** Create the factory pointer object. + ** If the factory could not be created, then report this error condition. + */ + FactoryClass * fptr = Factories.Raw_Ptr(*factory); + if (!fptr) return(PROD_CANT); + + /* + ** Actually suspend the production. + */ + fptr->Suspend(); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.SidebarClass::IsToRedraw = true; + Map.SidebarClass::Column[0].IsToRedraw = true; + Map.SidebarClass::Column[1].IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * * + * This routine is called from the event system whenever production must be abandoned for * + * the type specified. This will remove the factory and pending object from the sidebar as * + * well as from the house factory record. * + * * + * INPUT: type -- The object type that production is being suspended for. * + * * + * OUTPUT: Returns the reason why or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Abandon_Production(RTTIType type) +{ + Validate(); + int * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If there is no factory to abandon, then return with a failure code. + */ + if (*factory == -1) return(PROD_CANT); + + /* + ** Fetch the factory pointer object. + */ + FactoryClass * fptr = Factories.Raw_Ptr(*factory); + if (!fptr) return(PROD_CANT); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + // Handle Glyphx multiplayer sidebar. ST - 3/22/2019 2:01PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Abandon_Production(type, *factory, this); + // Need to clear pending object here? + } + } else { + + if (PlayerPtr == this) { + Map.Abandon_Production(type, *factory); + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + } + + /* + ** Abandon production of the object. + */ + fptr->Abandon(); + delete fptr; + *factory = -1; + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * * + * This routine will pick a good target to fire the special weapon specified. * + * * + * INPUT: id -- The special weapon id to fire. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 PWG : Created. * + *=============================================================================================*/ +void HouseClass::Special_Weapon_AI(SpecialWeaponType id) +{ + Validate(); + /* + ** Loop through all of the building objects on the map + ** and see which ones are available. + */ + BuildingClass * bestptr = NULL; + int best = -1; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + /* + ** If the building is valid, not in limbo, not in the process of + ** being destroyed and not our ally, then we can consider it. + */ + if (b && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { + if (b->Value() > best || best == -1) { + best = b->Value(); + bestptr = b; + } + } + } + + if (bestptr) { + CELL cell = Coord_Cell(bestptr->Center_Coord()); + Place_Special_Blast(id, cell); + } +} + + +/*********************************************************************************************** + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * * + * This routine will create a blast effect at the cell specified. This is the result of * + * the special weapons. * + * * + * INPUT: id -- The special weapon id number. * + * * + * cell -- The location where the special weapon attack is to occur. * + * * + * OUTPUT: Was the special weapon successfully fired at the location specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented. * + * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * + * 07/28/1995 JLB : Revamped to use super weapon class controller. * + *=============================================================================================*/ +bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) +{ + Validate(); + BuildingClass * launchsite = 0; + AnimClass * anim = 0; + + // Added. ST - 12/2/2019 11:26AM + bool fired = false; + const char *what = NULL; + + int index; + switch (id) { + + case SPC_ION_CANNON: + if (IonCannon.Is_Ready()) { + anim = new AnimClass(ANIM_ION_CANNON, Cell_Coord(cell)); + if (anim) anim->Owner = Class->House; + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + IonCannon.Discharged(PlayerPtr == this); + IsRecalcNeeded = true; + fired = true; + what = "ION"; + } + break; + + case SPC_NUCLEAR_BOMB: + if (NukeStrike.Is_Ready()) { + + +#ifdef NEVER + /* + ** Scatter the nuclear bomb impact point into an adjacent cell. + */ + for (;;) { + CELL newcell = Adjacent_Cell(cell, Random_Pick(FACING_N, FACING_COUNT)); + if (Map.In_Radar(newcell)) { + cell = newcell; + break; + } + } +#endif + + /* + ** Search for a suitable launch site for this missile. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_TEMPLE) { + launchsite = b; + break; + } + } + + /* + ** If a launch site was found, then proceed with the normal launch + ** sequence. + */ + if (launchsite) { + launchsite->Assign_Mission(MISSION_MISSILE); + launchsite->Commence(); + NukeDest = cell; + NukePieces = 0; + + } else { + + /* + ** Only in the multiplayer version can the nuclear bomb be + ** sent from some off screen source. + */ + if (GameToPlay == GAME_NORMAL) return(false); + + /* + ** Since no launch site was found, just bring the missile in + ** directly from the North map edge. + */ + BulletClass *bullet = new BulletClass(BULLET_NUKE_DOWN); + if (bullet) { + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), 0)); + bullet->Assign_Target(::As_Target(cell)); + bullet->Payback = NULL; + bullet->Strength = 1; + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } else { + bullet->PrimaryFacing.Set_Current(DIR_S); + } + Speak(VOX_INCOMING_NUKE); // "Nuclear Warhead Approaching" - "NUKE1" + Sound_Effect(VOC_NUKE_FIRE, start); + } + } + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + NukeStrike.Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "NUKE"; + } + break; + + case SPC_AIR_STRIKE: + if (AirStrike.Is_Ready()) { + int strike = 1; + if (GameToPlay == GAME_NORMAL) { + strike = Bound(BuildLevel/3, 1, 3); + } else { + strike = Bound(MPlayerUnitCount/5, 1, 3); + } + Create_Air_Reinforcement(this, AIRCRAFT_A10, strike, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + AirStrike.Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "AIR"; + } + break; + } + + /* + ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM + */ + if (IsHuman && fired && what) { + On_Achievement_Event(this, "SUPERWEAPON_FIRED", what); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * * + * This routine is called when a building has been produced and now must be placed on * + * the map. When the player clicks on the map, this routine is ultimately called when the * + * event passes through the event queue system. * + * * + * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * + * * + * * + * cell -- The location to place the object on the map. * + * * + * OUTPUT: Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + +bool HouseClass::Place_Object(RTTIType type, CELL cell) +{ + Validate(); + TechnoClass * tech = 0; + FactoryClass * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (AircraftFactory != -1) { + factory = Factories.Raw_Ptr(AircraftFactory); + } + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (InfantryFactory != -1) { + factory = Factories.Raw_Ptr(InfantryFactory); + } + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (UnitFactory != -1) { + factory = Factories.Raw_Ptr(UnitFactory); + } + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildingFactory != -1) { + factory = Factories.Raw_Ptr(BuildingFactory); + } + break; + } + + /* + ** Only if there is a factory active for this type, can it be "placed". + ** In the case of a missing factory, then this request is completely bogus -- + ** ignore it. This might occur if, between two events to exit the same + ** object, the mouse was clicked on the sidebar to start building again. + ** The second placement event should NOT try to place the object that is + ** just starting construction. + */ + if (factory && factory->Has_Completed()) { + tech = factory->Get_Object(); + + if (cell == -1) { + TechnoClass * pending = factory->Get_Object(); + if (pending) { + TechnoClass * builder = pending->Who_Can_Build_Me(false, false); + TechnoTypeClass const *object_type = pending->Techno_Type_Class(); + + if (builder && builder->Exit_Object(pending)) { + + /* + ** Since the object has left the factory under its own power, delete + ** the production manager tied to this slot in the sidebar. Its job + ** has been completed. + */ + factory->Set_Is_Blocked(false); + factory->Completed(); + Abandon_Production(type); + + /* + ** Could be tied to an achievement. ST - 11/11/2019 11:56AM + */ + if (IsHuman) { + if (object_type) { + On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName); + } + if (pending->IsActive) { + On_Ping(this, pending->Center_Coord()); + } + } + + } else { + + /* + ** The object could not leave under it's own power. Just wait + ** until the player tries to place the object again. + */ + + /* + ** Flag that it's blocked so we can re-try the exit later. + ** This would have been a bad idea under the old peer-peer code since it would have pumped events into + ** the queue too often. ST - 2/25/2020 11:56AM + */ + factory->Set_Is_Blocked(true); + return(false); + } + } + + } else { + + if (tech) { + TechnoClass * builder = tech->Who_Can_Build_Me(false, false); + if (builder) { + + /* + ** Ensures that the proximity check is performed even when the building is + ** placed by way of a remote event. + */ + if (tech->What_Am_I() != RTTI_BUILDING || ((BuildingClass *)tech)->Passes_Proximity_Check(cell)) { + builder->Transmit_Message(RADIO_HELLO, tech); + if (tech->Unlimbo(Cell_Coord(cell))) { + factory->Completed(); + Abandon_Production(type); + + if (PlayerPtr == this) { + Sound_Effect(VOC_SLAM); + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + } + return(true); + } else { + if (this == PlayerPtr) { + Speak(VOX_DEPLOY); + } + } + builder->Transmit_Message(RADIO_OVER_OUT); + } + } + return(false); + + } else { + + // Play a bad sound here? + return(false); + } + } + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * * + * This routine will inform the display system that building placement mode has begun. * + * The cursor will be created that matches the layout of the building shape. * + * * + * INPUT: builder -- The factory that is building this object. * + * * + * object -- The building that is going to be placed down on the map. * + * * + * OUTPUT: Was the building placement mode successfully initiated? * + * * + * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * + * is affected. * + * * + * HISTORY: * + * 05/04/1995 JLB : Created. * + * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * + *=============================================================================================*/ +bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) +{ + Validate(); + if (this == PlayerPtr && !Map.PendingObject && builder && object) { + + /* + ** Ensures that object selection doesn't remain when + ** building placement takes place. + */ + Unselect_All(); + + Map.Repair_Mode_Control(0); + Map.Sell_Mode_Control(0); + + Map.PendingObject = object->Class; + Map.PendingObjectPtr = object; + Map.PendingHouse = Class->House; + + Map.Set_Cursor_Shape(object->Occupy_List(true)); + Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); + builder->Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * HouseClass::Init_Ion_Cannon -- Initialize the ion cannon countdown. * + * * + * This routine will initiate the ion cannon charging countdown. It will add the ion * + * cannon to the sidebar if it isn't there and it is specified to be added. * + * * + * INPUT: first_time -- Set to true if the ion cannon must be added to the sidebar. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Ion_Cannon(SpecialControlType control) +{ + Validate(); + switch (control) { + case CONTROL_RESET: + if (IonCannonPresent) { + IonOldStage = -1; + IonControl.Set(ION_CANNON_GONE_TIME); + if (PlayerPtr == this) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + if (!ScenarioInit) { + Speak(VOX_ION_CHARGING); + } + } + } + break; + + /* + ** Adds the special no-prerequisite ion cannon option. + */ + case CONTROL_ONE_TIME: + if (!IonCannonPresent) { + Init_Ion_Cannon(CONTROL_ADD); + IonOneTimeFlag = true; + } + break; + + /* + ** Adds the normal legitimate ion cannon option. If there was + ** already a one-time ion cannon available, the charging state + ** is NOT interrupted. + */ + case CONTROL_ADD: + IonOneTimeFlag = false; + if (!IconCannonPresent) { + IonCannonPresent = true; + IonReady = false; + Init_Ion_Cannon(CONTROL_RESET); + } + break; + + case CONTROL_REMOVE: + break; + } + + + if (!(first_time && IonCannonPresent)) { + + if (IonCannonPresent && IonOneTimeFlag) { + IonOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + } + + if (!ScenarioInit) { + if (this == PlayerPtr) { + Speak(VOX_ION_CHARGING); + } + } + + IonControl.Set(ION_CANNON_GONE_TIME); + IonCannonPresent = true; + IonReady = false; + IonOldStage = -1; + IonOneTimeFlag = one_time_effect; + } else { + if (first_time && IonCannonPresent && !one_time_effect && IonOneTimeFlag) { + IonOneTimeFlag = false; + } + } +} +#ifdef NEVER +void HouseClass::Init_Ion_Cannon(bool first_time, bool one_time_effect) +{ + Validate(); + if (!(first_time && IonCannonPresent)) { + + if (IonCannonPresent && IonOneTimeFlag) { + IonOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + } + + if (!ScenarioInit) { + if (this == PlayerPtr) { + Speak(VOX_ION_CHARGING); + } + } + + IonControl.Set(ION_CANNON_GONE_TIME); + IonCannonPresent = true; + IonReady = false; + IonOldStage = -1; + IonOneTimeFlag = one_time_effect; + } else { + if (first_time && IonCannonPresent && !one_time_effect && IonOneTimeFlag) { + IonOneTimeFlag = false; + } + } +} +#endif + + +/*********************************************************************************************** + * HouseClass::Remove_Ion_Cannon -- Disables the ion cannon. * + * * + * This routine will disable the ion cannon. It is called when the ion cannon cannot * + * establish a command link to the ground (usually when there is insufficient power). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Remove_Ion_Cannon(void) +{ + Validate(); + if (IonCannonPresent) { + IonCannonPresent = false; + IonOneTimeFlag = false; + IonReady = false; + IonControl.Clear(); + IonOldStage = -1; + } +} +#endif + + +/*************************************************************************** + * HouseClass::Clobber_All -- removes house & all its objects * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Clobber_All(void) +{ + Validate(); + int i; + + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this) { + delete ::Aircraft.Ptr(i); + i--; + } + } + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this) { + delete ::Units.Ptr(i); + i--; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this) { + delete Infantry.Ptr(i); + i--; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this) { + delete Buildings.Ptr(i); + i--; + } + } + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } + + delete this; +} + + +#ifdef NEVER +/*********************************************************************************************** + * HouseClass::Init_Nuke_Bomb -- Adds (if necessary) the atom bomb to the sidebar. * + * * + * Use this routine whenever a piece of atom bomb has been discovered (also at scenario * + * start). It will add the nuclear bomb button to the sidebar if necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Nuke_Bomb(bool first_time, bool one_time_effect) +{ + Validate(); + if (!first_time || !NukePresent) { + + if (NukePresent && NukeOneTimeFlag) { + NukeOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + } + + NukeControl.Set(NUKE_GONE_TIME); + NukePresent = true; + NukeReady = false; + NukeOldStage = -1; + NukeOneTimeFlag = one_time_effect; + + } else { + if (!one_time_effect && NukeOneTimeFlag) { + NukeOneTimeFlag = false; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remove_Nuke_Bomb -- Removes the nuclear bomb from the sidebar. * + * * + * This routine will remove the nuclear bomb from the sidebar. It should be called when * + * the nuclear strike has been launched. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + * 07/25/1995 JLB : Handles recharge reset logic. * + *=============================================================================================*/ +void HouseClass::Remove_Nuke_Bomb(void) +{ + Validate(); + if (NukePresent && !NukeOneTimeFlag) { + NukePresent = false; + NukeControl.Clear(); + NukeOldStage = -1; + NukeReady = false; + } +} + + +/*********************************************************************************************** + * HouseClass::Init_Air_Strike -- Add (or reset) the air strike sidebar button. * + * * + * This routine will activate (add if so indicated) the air strike button to the sidebar. * + * Call this routine when events indicate that a special air strike is available. * + * * + * INPUT: first_time -- If the air strike button is to be added, then this will be true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Air_Strike(bool first_time, bool one_time_effect) +{ + Validate(); + if (!(first_time && AirPresent)) { + + if (AirPresent && AirOneTimeFlag) { + AirOneTimeFlag = false; + AirPresent = false; + Map.Recalc(); + return; + } + + if (first_time) { + if (PlayerPtr == this) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + } + AirControl.Set(0); + } else { + AirControl.Set(AIR_CANNON_GONE_TIME); + } + + AirReady = first_time; + AirPresent = true; + AirOldStage = -1; + AirOneTimeFlag = one_time_effect; + + if (AirReady && !IsHuman) { + Special_Weapon_AI(SPC_AIR_STRIKE); + } + } else { + if (first_time && AirPresent && !one_time_effect && AirOneTimeFlag) { + AirOneTimeFlag = false; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remove_Air_Strike -- Removes the air strike button from the sidebar. * + * * + * This routine will remove the air strike button from the sidebar. Call this routine when * + * the air strike has been launched. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Remove_Air_Strike(void) +{ + Validate(); + AirPresent = false; + AirReady = false; + AirControl.Clear(); + AirOldStage = -1; +} + + +/*********************************************************************************************** + * HouseClass::Make_Air_Strike_Available -- Make the airstrike available. * + * * + * This routine will make the airstrike available. Typically, this results in a new icon * + * added to the sidebar. * + * * + * INPUT: present -- The the airstrike being added? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Make_Air_Strike_Available(bool present, bool one_time_effect) +{ + Validate(); + Init_Air_Strike(true, one_time_effect); + AirPresent = present; +} +#endif + + +/*********************************************************************************************** + * HouseClass::Add_Nuke_Piece -- Add a nuclear piece to the collection. * + * * + * This routine will a the specified nuclear piece to the house collection of parts. When * + * all the pieces have been added, a nuclear strike ability is made available. * + * * + * INPUT: piece -- The nuclear piece to add. If equal to "-1", then the next possible piece * + * is added. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Add_Nuke_Piece(int piece) +{ + Validate(); + if (piece == -1) { + piece = 1; + if (!(NukePieces & 0x01)) { + piece = 1; + } + if (!(NukePieces & 0x02)) { + piece = 2; + } + if (!(NukePieces & 0x04)) { + piece = 3; + } + } + NukePieces |= 1 << (piece - 1); +// Init_Nuke_Bomb(false); +} + + +/*********************************************************************************************** + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * * + * This routine is called when an object is to be removed from the game system. If the * + * specified object is part of the house tracking system, then it will be removed. * + * * + * INPUT: target -- The target value of the object that is to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Detach(TARGET, bool ) +{ + Validate(); +// if (LaunchSite == target) { +// LaunchSite = TARGET_NONE; +// } +} + + +/*********************************************************************************************** + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * * + * This routine will examine the enemy houses and if there is a building owned by one * + * of those house, true will be returned. * + * * + * INPUT: btype -- The building type to check for. * + * * + * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const +{ + Validate(); + int bflag = 1L << btype; + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseClass * house = HouseClass::As_Pointer(index); + + if (house && !Is_Ally(house) && (house->BScan & bflag) != 0) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * * + * This routine will examine the house status and return with a techno type pointer to * + * the object type that it thinks should be created. The type is restricted to match the * + * type specified. Typical use of this routine is by computer controlled factories. * + * * + * INPUT: objecttype -- The type of object to restrict the scan for. * + * * + * OUTPUT: Returns with a pointer to a techno type for the object type that should be * + * created. If no object should be created, then NULL is returned. * + * * + * WARNINGS: This is a time consuming routine. Only call when necessary. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype) const +{ + Validate(); + TechnoTypeClass const * techno = NULL; + +#ifdef USE_RA_AI + // + // Copied from RA for AI. ST - 7/25/2019 3:58PM + // + if (!IsHuman && GameToPlay != GAME_NORMAL) { + switch (objecttype) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (BuildAircraft != AIRCRAFT_NONE) { + return(&AircraftTypeClass::As_Reference(BuildAircraft)); + } + return(NULL); + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (BuildUnit != UNIT_NONE) { + return(&UnitTypeClass::As_Reference(BuildUnit)); + } + return(NULL); + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (BuildInfantry != INFANTRY_NONE) { + return(&InfantryTypeClass::As_Reference(BuildInfantry)); + } + return(NULL); + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); + } + + return NULL; + } + +#endif //USE_RA_AI + + + + switch (objecttype) { + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (CurUnits < MaxUnit) { + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. Never replace harvesters if the game + ** is in easy mode. + */ + if (!(GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY) && !IsHuman && (ActiveBScan & STRUCTF_REFINERY) && !(UScan & UNITF_HARVESTER)) { + techno = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + if (techno->Scenario <= BuildLevel) break; + techno = 0; + } + + int counter[UNIT_COUNT]; + if (GameToPlay == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (Can_Build(index, Class->House) && UnitTypeClass::As_Reference(index).Level <= BuildLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if ((/*team->IsReinforcable || */!tptr->IsFullStrength) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)(team->Class[subindex]))->Type] = 1; +// counter[((UnitTypeClass const *)(team->Class[subindex]))->Type] += team->DesiredNum[subindex]*2; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)(team->Class[subindex]))->Type; + counter[subtype] = MAX(counter[subtype], (int)team->DesiredNum[subindex]); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + if (unit && !unit->Team && unit->House == this && (unit->Mission != MISSION_GUARD_AREA && unit->Mission != MISSION_HUNT && unit->Mission != MISSION_STICKY && unit->Mission != MISSION_SLEEP)) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(utype, Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + techno = &UnitTypeClass::As_Reference(bestlist[Random_Pick(0, bestcount-1)]); + } + } + break; + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (CurUnits < MaxUnit) { + int counter[INFANTRY_COUNT]; + if (GameToPlay == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + if (Can_Build(index, Class->House) && InfantryTypeClass::As_Reference(index).Level <= BuildLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if ((team->IsReinforcable || !tptr->IsFullStrength) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + counter[((InfantryTypeClass const *)(team->Class[subindex]))->Type] += team->DesiredNum[subindex]+1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Class[subindex]))->Type; +// counter[subtype] = 1; + counter[subtype] = MAX(counter[subtype], (int)team->DesiredNum[subindex]); + counter[subtype] = MIN(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + if (infantry && !infantry->Team && infantry->House == this && (infantry->Mission != MISSION_GUARD_AREA && infantry->Mission != MISSION_HUNT && infantry->Mission != MISSION_STICKY && infantry->Mission != MISSION_SLEEP)) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(utype, Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + techno = &InfantryTypeClass::As_Reference(bestlist[Random_Pick(0, bestcount-1)]); + } + } + break; + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (CurBuildings < MaxBuilding) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + techno = &BuildingTypeClass::As_Reference(node->Type); + } + } + break; + } + return(techno); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * * + * This routine will remove the flag attached to the specified target object or cell. * + * Call this routine before placing the object down. This is called inherently by the * + * the Flag_Attach() functions. * + * * + * INPUT: target -- The target that the flag was attached to but will be removed from. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully removed from the specified target? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Remove(TARGET target, bool set_home) +{ + Validate(); + bool rc = false; + + if (Target_Legal(target)) { + + /* + ** Remove the flag from a unit + */ + UnitClass * object = As_Unit(target); + if (object) { + rc = object->Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + + } else { + + /* + ** Remove the flag from a cell + */ + CELL cell = As_Cell(target); + if (Map.In_Radar(cell)) { + rc = Map[cell].Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + } + } + + /* + ** Handle the flag home cell: + ** If 'set_home' is set, clear the home value & the cell's overlay + */ + if (set_home) { + if (FlagHome) { + Map[FlagHome].Overlay = OVERLAY_NONE; + Map.Flag_Cell(FlagHome); + FlagHome = 0; + } + } + } + return(rc); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * * + * This routine will attach the house flag to the location specified. If the location * + * cannot contain the flag, then a suitable nearby location will be selected. * + * * + * INPUT: cell -- The desired cell location to place the flag. * + * * + * set_home -- if true, resets the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully placed? * + * * + * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * + * Check the FlagLocation value to determine the final cell resting spot. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(CELL cell, bool set_home) +{ + Validate(); + bool rc; + bool clockwise; + FacingType rot; + FacingType fcounter; + + /* + ** Randomly decide if we're going to search cells clockwise or counter- + ** clockwise + */ + clockwise = IRandom(0,1); + + /* + ** Only continue if this cell is a legal placement cell. + */ + if (Map.In_Radar(cell)) { + + /* + ** If the flag already exists, then it must be removed from the object + ** it is attached to. + */ + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the cell specified. If it can't be placed, then pick + ** a nearby cell where it can be placed. + */ + CELL newcell = cell; + rc = Map[newcell].Flag_Place(Class->House); + if (!rc) { + + /* + ** Loop for increasing distance from the desired cell. + ** For each distance, randomly pick a starting direction. Between + ** this and the clockwise/counterclockwise random value, the flag + ** should appear to be placed fairly randomly. + */ + for (int dist = 1; dist < 32; dist++) { + + /* + ** Clockwise search. + */ + if (clockwise) { + rot = (FacingType)IRandom(FACING_N, FACING_NW); + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot++; + if (rot > FACING_NW) rot = FACING_N; + } + } else { + + /* + ** Counter-clockwise search + */ + rot = (FacingType)IRandom (FACING_N, FACING_NW); + for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot--; + if (rot < FACING_N) + rot = FACING_NW; + } + } + } + } + + /* + ** If we've found a spot for the flag, place the flag at the new cell. + ** if 'set_home' is set, OR this house has no current flag home cell, + ** mark that cell as this house's flag home cell. Otherwise fall back + ** on returning the flag to its home. + */ + if (rc) { + FlagLocation = As_Target(newcell); + + if (set_home || FlagHome == 0) { + Map[newcell].Overlay = OVERLAY_FLAG_SPOT; + FlagHome = newcell; + } + } + else if (FlagHome != 0) { + rc = Map[FlagHome].Flag_Place(Class->House); + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * * + * This routine will attach the house flag to the specified unit. This routine is called * + * when a unit drives over a cell containing a flag. * + * * + * INPUT: object -- Pointer to the object that the house flag is to be attached to. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag attached successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) +{ + Validate(); + if (object && !object->IsInLimbo) { + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the object. + */ + object->Flag_Attach(Class->House); + FlagLocation = object->As_Target(); + return(true); + } + return(false); +} + +extern void On_Defeated_Message(const char* message, float timeout_seconds); + +/*************************************************************************** + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/25/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::MPlayer_Defeated(void) +{ + Validate(); + char txt[80]; + int i,j,k; + unsigned char id; + HousesType house; + HouseClass *hptr; + HouseClass *hptr2; + int num_alive; + int num_humans; + int all_allies; + int max_index; + int max_count; + int count; + int score_index[MAX_PLAYERS]; // array of each multi-player's index into + // the score array + + /*------------------------------------------------------------------------ + Set the defeat flag for this house + ------------------------------------------------------------------------*/ + IsDefeated = true; + +#ifdef USE_RA_AI + /* + ** Moved in from RA for AI. ST - 7/24/2019 4:02PM + */ + /* + ** If this is a computer controlled house, then all computer controlled + ** houses become paranoid. + */ + if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) { + Computer_Paranoid(); + } +#endif // USE_RA_AI + + /*------------------------------------------------------------------------ + Remove this house's flag & flag home cell + ------------------------------------------------------------------------*/ + if (Special.IsCaptureTheFlag) { + if (FlagLocation) { + Flag_Remove(FlagLocation,true); + } else { + if (FlagHome) { + Flag_Remove(FlagHome,true); + } + } + } + + /*------------------------------------------------------------------------ + If this is me: + - Set MPlayerObiWan, so I can only send messages to all players, and + not just one (so I can't be obnoxiously omnipotent) + - Reveal the map + - Add my defeat message + ------------------------------------------------------------------------*/ + if (PlayerPtr == this) { + MPlayerObiWan = 1; + Debug_Unshroud = true; + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + + /*..................................................................... + Pop up a message showing that I was defeated + .....................................................................*/ + sprintf(txt,Text_String(TXT_PLAYER_DEFEATED), MPlayerName); + //Messages.Add_Message(txt, MPlayerTColors[MPlayerColorIdx], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + + int timeout = 600; + On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); + //Sound_Effect(VOC_INCOMING_MESSAGE); + + } else { + + /*------------------------------------------------------------------------ + If it wasn't me, find out who was defeated + ------------------------------------------------------------------------*/ + if (IsHuman) { + sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), Text_String(TXT_UNKNOWN)); + id = 0; + for (i = 0; i < MPlayerCount; i++) { + house = MPlayerHouses[i]; + if (HouseClass::As_Pointer(house) == this) { + sprintf (txt,Text_String(TXT_PLAYER_DEFEATED), MPlayerNames[i]); + id = MPlayerID[i]; + } + } + + Messages.Add_Message(txt, MPlayerTColors[MPlayerID_To_ColorIndex(id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + } + + /*------------------------------------------------------------------------ + Find out how many players are left alive. + ------------------------------------------------------------------------*/ + num_alive = 0; + num_humans = 0; + for (i = 0; i < MPlayerMax; i++) { + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (hptr && hptr->IsDefeated==0) { + if (hptr->IsHuman) + num_humans++; + num_alive++; + } + } + + /*------------------------------------------------------------------------ + If all the houses left alive are allied with each other, then in reality + there's only one player left: + ------------------------------------------------------------------------*/ + all_allies = 1; + for (i = 0; i < MPlayerMax; i++) { + /*..................................................................... + Get a pointer to this house + .....................................................................*/ + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (!hptr || hptr->IsDefeated) + continue; + + /*..................................................................... + Loop through all houses; if there's one left alive that this house + isn't allied with, then all_allies will be false + .....................................................................*/ + for (j = 0; j < MPlayerMax; j++) { + hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); + if (!hptr2) + continue; + if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { + all_allies = 0; + break; + } + } + if (!all_allies) + break; + } + /*........................................................................ + If all houses left are allies, set 'num_alive' to 1; game over. + ........................................................................*/ + if (all_allies) + num_alive = 1; + + /*------------------------------------------------------------------------ + If there's only one human player left or no humans left, the game is over: + - Determine whether this player wins or loses, based on the state of the + MPlayerObiWan flag + - Find all players' indices in the MPlayerScore array + - Tally up scores for this game + ------------------------------------------------------------------------*/ + if (num_alive == 1 || num_humans == 0) { + if (PlayerPtr->IsDefeated) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + + /*--------------------------------------------------------------------- + Find each player's score index + ---------------------------------------------------------------------*/ + for (i = 0; i < MPlayerCount; i++) { + score_index[i] = -1; + + /*.................................................................. + Search for this player's name in the MPlayerScore array + ..................................................................*/ + for (j = 0; j < MPlayerNumScores; j++) { + if (!stricmp(MPlayerNames[i],MPlayerScore[j].Name)) { + score_index[i] = j; + break; + } + } + + /*.................................................................. + If the index is still -1, the name wasn't found; add a new entry. + ..................................................................*/ + if (score_index[i] == -1) { + if (MPlayerNumScores < MAX_MULTI_NAMES) { + score_index[i] = MPlayerNumScores; + MPlayerNumScores++; + } else { + + /*............................................................... + For each player in the scores array, count the # of '-1' entries + from this game backwards; the one with the most is the one that + hasn't played the longest; replace him with this new guy. + ...............................................................*/ + max_index = 0; + max_count = 0; + for (j = 0; j < MPlayerNumScores; j++) { + count = 0; + for (k = MPlayerNumScores - 1; k >= 0; k--) { + if (MPlayerScore[j].Kills[k]==-1) { + count++; + } else { + break; + } + } + if (count > max_count) { + max_count = count; + max_index = j; + } + } + score_index[i] = max_index; + } + + /*............................................................... + Initialize this score entry + ...............................................................*/ + MPlayerScore[score_index[i]].Wins = 0; + strcpy (MPlayerScore[score_index[i]].Name,MPlayerNames[i]); + for (j = 0; j < MAX_MULTI_GAMES; j++) + MPlayerScore[score_index[i]].Kills[j] = -1; + } + + /*.................................................................. + Init this player's Kills to 0 (-1 means he didn't play this round; + 0 means he played but got no kills). + ..................................................................*/ + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] = 0; + + /*.................................................................. + Init this player's color to his last-used color index + ..................................................................*/ + MPlayerScore[score_index[i]].Color = MPlayerID_To_ColorIndex(MPlayerID[i]); + } + +#if 0 // (This is the old method of tallying scores: + /*--------------------------------------------------------------------- + Tally up the scores for this game: + - For each house: + - If this house is human & wasn't defeated, its the winner + - If this house was defeated, find out who did it & increment their + Kills value. + ---------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + hptr = HouseClass::As_Pointer(house); + if (!hptr) continue; + + if (!hptr->IsDefeated) { + + /*............................................................... + If this is the winning house, find which player it was & increment + their 'Wins' value + ...............................................................*/ + if (hptr->IsHuman) { + for (i = 0; i < MPlayerCount; i++) { + if (house == MPlayerHouses[i]) { + MPlayerScore[score_index[i]].Wins++; + MPlayerWinner = score_index[i]; + } + } + } + } else { + + /*.................................................................. + This house was defeated; find which player who defeated him & increment + his 'Kills' value for this game + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (hptr->WhoLastHurtMe == MPlayerHouses[i]) { + MPlayerScore[score_index[i]].Kills[MPlayerCurGame]++; + } + } + } + } + +#else // This is the new method: + + /*--------------------------------------------------------------------- + Tally up the scores for this game: + - For each player: + - If this player is undefeated this round, he's the winner + - Each player's Kills value is the sum of the unit's they killed + ---------------------------------------------------------------------*/ + for (i = 0; i < MPlayerCount; i++) { + hptr = HouseClass::As_Pointer(MPlayerHouses[i]); + + /*.................................................................. + If this house was undefeated, it must have been the winner. (If + no human houses are undefeated, the computer won.) + ..................................................................*/ + if (!hptr->IsDefeated) { + MPlayerScore[score_index[i]].Wins++; + MPlayerWinner = score_index[i]; + } + + /*.................................................................. + Tally up all kills for this player + ..................................................................*/ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] += + hptr->UnitsKilled[house]; + + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] += + hptr->BuildingsKilled[house]; + } + } +#endif + + /*--------------------------------------------------------------------- + Destroy all the IPX connections, since we have to go through the rest + of the Main_Loop() before we detect that the game is over, and we'll + end up waiting for frame sync packets from the other machines. + ---------------------------------------------------------------------*/ + if (GameToPlay==GAME_IPX || GameToPlay == GAME_INTERNET) { + i = 0; + while (Ipx.Num_Connections() && (i++ < 1000) ) { + id = Ipx.Connection_ID(0); + Ipx.Delete_Connection(id); + } + MPlayerCount = 0; + } + } + + /*------------------------------------------------------------------------ + Be sure our messages get displayed, even if we're about to exit. + ------------------------------------------------------------------------*/ + Map.Render(); +} + + +/*************************************************************************** + * HouseClass::Blowup_All -- blows up everything * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Blowup_All(void) +{ + Validate(); + int i; + int damage; + UnitClass *uptr; + InfantryClass *iptr; + BuildingClass *bptr; + int count; + WarheadType warhead; + + /* + ** Find everything owned by this house & blast it with a huge amount of damage + ** at zero range. Do units before infantry, so the units' drivers are killed + ** too. Using Explosion_Damage is like dropping a big bomb right on the + ** object; it will also damage anything around it. + */ + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { + uptr = ::Units.Ptr(i); + + /* + ** Some units can't be killed with one shot, so keep damaging them until + ** they're gone. The unit will destroy itself, and put an infantry in + ** its place. When the unit destroys itself, decrement 'i' since + ** its pointer will be removed from the active pointer list. + */ + count = 0; + while (::Units.Ptr(i)==uptr && uptr->Strength) { + damage = 0x7fff; + Explosion_Damage(uptr->Center_Coord(), damage, NULL, WARHEAD_HE); + count++; + if (count > 5) { + delete uptr; + break; + } + } + i--; + } + } + + /* + ** Destroy all aircraft owned by this house. + */ + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { + AircraftClass * aptr = ::Aircraft.Ptr(i); + + damage = 0x7fff; + aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL); + if (!aptr->IsActive) { + i--; + } + } + } + + /* + ** Buildings don't delete themselves when they die; they shake the screen + ** and begin a countdown, so don't decrement 'i' when it's destroyed. + */ + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { + bptr = Buildings.Ptr(i); + + count = 0; + bptr->IsSurvivorless = true; + while (Buildings.Ptr(i)==bptr && bptr->Strength) { + damage = 0x7fff; + Explosion_Damage(bptr->Center_Coord(), damage, NULL, WARHEAD_HE); + count++; + if (count > 5) { + delete bptr; + break; + } + } + } + } + + /* + ** Infantry don't delete themselves when they die; they go into a death- + ** animation sequence, so there's no need to decrement 'i' when they die. + ** Infantry should die by different types of warheads, so their death + ** anims aren't all synchronized. + */ + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { + iptr = Infantry.Ptr(i); + + count = 0; + while (Infantry.Ptr(i)==iptr && iptr->Strength) { + damage = 0x7fff; + warhead = (WarheadType)IRandom (WARHEAD_SA, WARHEAD_FIRE); + Explosion_Damage(iptr->Center_Coord(), damage, NULL, warhead); + if (iptr->IsActive) { + damage = 0x7fff; + iptr->Take_Damage(damage, 0, warhead); + } + + count++; + if (count > 5) { + delete iptr; + break; + } + } + } + } + +#ifdef NEVER + /* + ** Just delete the teams & triggers for this house. + */ + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } +#endif +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * * + * When this routine is called, the house will blow up after a period of time. Typically * + * this is called when the flag is captured or the HQ destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to blow up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Die(void) +{ + Validate(); + if (!IsToWin && !IsToDie && !IsToLose) { + IsToDie = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToDie); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * * + * When this routine is called, the house will be declared the winner after a period of * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to win? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Win(void) +{ + Validate(); + if (!IsToWin && !IsToDie && !IsToLose) { + IsToWin = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToWin); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * * + * When this routine is called, it will spell the doom of this house. In a short while * + * all of the object owned by this house will explode. Typical use of this routine is when * + * the flag has been captured or the command vehicle has been destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Has the doom been initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Lose(void) +{ + Validate(); + IsToWin = false; + if (!IsToDie && !IsToLose) { + IsToLose = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToLose); +} + + +/*********************************************************************************************** + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * * + * This routine is called when initializing the color and remap data for this house. The * + * primary user of this routine is the multiplayer version of the game. * + * * + * INPUT: color -- The color of this house. * + * * + * house -- The house that this should act like. * + * * + * credits -- The initial credits to assign to this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) +{ + Validate(); + Credits = InitialCredits = credits; + VisibleCredits.Current = Credits; + + ActLike = house; + RemapColor = color; + switch (color) { + case REMAP_GOLD: + RemapTable = RemapGold; + ((unsigned char &)Class->Color) = 157; + ((unsigned char &)Class->BrightColor) = 5; + break; + + case REMAP_RED: + RemapTable = RemapRed; + ((unsigned char &)Class->Color) = 123; + ((unsigned char &)Class->BrightColor) = 127; + break; + + case REMAP_LTBLUE: + RemapTable = RemapLtBlue; + ((unsigned char &)Class->Color) = 135; + ((unsigned char &)Class->BrightColor) = 2; + break; + + case REMAP_ORANGE: + RemapTable = RemapOrange; + ((unsigned char &)Class->Color) = 26; + ((unsigned char &)Class->BrightColor) = 24; + break; + + case REMAP_GREEN: + RemapTable = RemapGreen; + ((unsigned char &)Class->Color) = 167; + ((unsigned char &)Class->BrightColor) = 159; + break; + + case REMAP_BLUE: + RemapTable = RemapBlue; + ((unsigned char &)Class->Color) = 203; + ((unsigned char &)Class->BrightColor) = 201; + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * * + * Use this routine to fetch the current power output as a fixed point fraction. The * + * value 0x0100 is 100% power. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with power rating as a fixed pointer number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Power_Fraction(void) const +{ + Validate(); + if (Power) { + if (Drain) { + return(Cardinal_To_Fixed(Drain, Power)); + } else { + return(0x0100); + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Has_Nuke_Device -- Deteremines if the house has a nuclear device. * + * * + * This routine checks to see if the house has a nuclear device to launch. A nuclear * + * device is available when the necessary parts have been retrieved in earlier scenarios * + * or if this is the multiplayer version. * + * * + * INPUT: none * + * * + * OUTPUT: Does the house have a nuclear device? * + * * + * WARNINGS: This does not check to see if there is a suitable launch facility (i.e., the * + * Temple of Nod), only that there is a nuclear device potential. * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Has_Nuke_Device(void) +{ + Validate(); + if (GameToPlay != GAME_NORMAL || !IsHuman) return(true); + return((NukePieces & 0x07) == 0x07); +} + + +/*********************************************************************************************** + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * * + * This routine will try to sell the wall at the specified location. If there is a wall * + * present and it is owned by this house, then it can be sold. * + * * + * INPUT: cell -- The cell that wall selling is desired. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Sell_Wall(CELL cell) +{ + Validate(); + if ((unsigned)cell > 0) { + OverlayType overlay = Map[cell].Overlay; + + if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { + OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); + + if (optr.IsWall) { + BuildingTypeClass const * btype = NULL; + switch (overlay) { + case OVERLAY_SANDBAG_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL); + break; + + case OVERLAY_CYCLONE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL); + break; + + case OVERLAY_BRICK_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL); + break; + + case OVERLAY_BARBWIRE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL); + break; + + case OVERLAY_WOOD_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL); + break; + + default: + break; + } + if (btype != NULL && !btype->IsUnsellable) { + + if (PlayerPtr == this) { + Sound_Effect(VOC_CASHTURN); + } + + Refund_Money(btype->Cost_Of()/2); + Map[cell].Overlay = OVERLAY_NONE; + Map[cell].OverlayData = 0; + Map[cell].Owner = HOUSE_NONE; + Map[cell].Wall_Update(); + Map[cell].Adjacent_Cell(FACING_N).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_W).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_S).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_E).Wall_Update(); + Map[cell].Recalc_Attributes(); + Map[cell].Redraw_Objects(); + ObjectClass::Detach_This_From_All(::As_Target(cell), true); + } + } + } + } +} + + + +/*********************************************************************************************** + * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/31/2020 3:34PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Check_Pertinent_Structures(void) +{ + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + ** + ** Game is over when no pertinent structures remain + */ + + if (!Special.IsEarlyWin) { + return; + } + + if (IsToDie || IsToWin || IsToLose) { + return; + } + + bool any_good_buildings = false; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *b = Buildings.Ptr(index); + + if (b && b->IsActive && b->House == this) { + if (!b->Class->IsWall) { + if (!b->IsInLimbo && b->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) { + if (!unit->IsInLimbo && unit->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + Flag_To_Die(); + } +} + + + + +/*********************************************************************************************** + * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Init_Unit_Trackers(void) +{ + AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); + InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); + UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); + BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); + + DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); + DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); + DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); + DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + + CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + TotalCrates = new UnitTrackerClass ( TOTAL_CRATE_TYPES ); //15 crate types +} + + + +/*********************************************************************************************** + * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Free_Unit_Trackers(void) +{ + if (AircraftTotals) { + delete AircraftTotals; + AircraftTotals = NULL; + } + + if (InfantryTotals) { + delete InfantryTotals; + InfantryTotals = NULL; + } + + if (UnitTotals) { + delete UnitTotals; + UnitTotals = NULL; + } + + if (BuildingTotals) { + delete BuildingTotals; + BuildingTotals = NULL; + } + + if (DestroyedAircraft) { + delete DestroyedAircraft; + DestroyedAircraft = NULL; + } + + if (DestroyedInfantry) { + delete DestroyedInfantry; + DestroyedInfantry = NULL; + } + + if (DestroyedUnits) { + delete DestroyedUnits; + DestroyedUnits = NULL; + } + + if (DestroyedBuildings) { + delete DestroyedBuildings; + DestroyedBuildings = NULL; + } + + if (CapturedBuildings) { + delete CapturedBuildings; + CapturedBuildings = NULL; + } + + if (TotalCrates) { + delete TotalCrates; + TotalCrates = NULL; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifdef USE_RA_AI + + + +/*********************************************************************************************** + + Below AI code imported from RA + +***********************************************************************************************/ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/* +** In RA, Control is a container for other variables. In TD, they are defined in the class +*/ +#define Control (*this) + +/* +** Percent_Chance - implementation similar to Red Alert +*/ +inline bool Percent_Chance(int percent) +{ + return (Random_Pick(0, 99) < percent); +} + + +/* +** Engineer was renamed to RENOVATOR for RA +*/ +#define INFANTRY_RENOVATOR INFANTRY_E7 + + +TFixedIHeapClass HouseClass::BuildChoice; + +/* +** This is a replacement for the RA 'fixed' round up function. It takes the equivalent of a 'fixed' value, but returns just the integer part +** ST - 7/26/2019 11:13AM +*/ +unsigned short Round_Up(unsigned short val) +{ + if ((val & 0xff) == 0) { + return val; + } + val &= 0xff00; + val += 0x0100; + val >>= 8; + return val; +} + + +unsigned short fixed(int val) {return (unsigned short)val;} + + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * * + * This routine is called when a construction yard needs to know what to build next. It * + * will either examine the prebuilt base list or try to figure out what to build next * + * based on the current game situation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building type class to build. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const +{ + //assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Building -- Finds a building of specified type. * + * * + * This routine is used to find a building of the specified type. This is particularly * + * useful for when some event requires a specific building instance. The nuclear missile * + * launch is a good example. * + * * + * INPUT: type -- The building type to scan for. * + * * + * zone -- The zone that the building must be located in. If no zone specific search * + * is desired, then pass ZONE_NONE. * + * * + * OUTPUT: Returns with a pointer to the building type requested. If there is no building * + * of the type requested, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + * 10/02/1995 JLB : Allows for zone specifics. * + *=============================================================================================*/ +BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** Only scan if we KNOW there is at least one building of the type + ** requested. + */ + if (BQuantity[type] > 0) { + + /* + ** Search for a suitable launch site for this missile. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == type) { + if (zone == ZONE_NONE || Which_Zone(b) == zone) { + return(b); + } + } + } + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * * + * This routine is used to find a suitable building location for the building specified. * + * The auto base building logic uses this when building the base for the computer. * + * * + * INPUT: building -- Pointer to the building that needs to be placed down. * + * * + * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable * + * locations, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const +{ + //assert(Houses.ID(this) == ID); + + int zonerating[ZONE_COUNT]; + struct { + int AntiAir; // Average air defense for the base. + int AntiArmor; // Average armor defense for the base. + int AntiInfantry; // Average infantry defense for the base. + } zoneinfo = {0,0,0}; + int antiair = building->Anti_Air(); + int antiarmor = building->Anti_Armor(); + int antiinfantry = building->Anti_Infantry(); + bool adj = true; + + /* + ** Never place combat buildings adjacent to each other. This is partly + ** because combat buildings don't have a bib and jamming will occur as well + ** as because spacing defensive buildings out will yield a better + ** defense. + */ + if (antiair || antiarmor || antiinfantry) { + adj = false; + } + + /* + ** Determine the average zone strengths for the base. This value is + ** used to determine what zones are considered under or over strength. + */ + ZoneType z; + for (z = ZONE_NORTH; z < ZONE_COUNT; z++) { + zoneinfo.AntiAir += ZoneInfo[z].AirDefense; + zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense; + zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense; + } + zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH; + + /* + ** Give each zone a rating for value. The higher the value the more desirable + ** to place the specified building in that zone. Factor the average value of + ** zone defense such that more weight is given to zones that are very under + ** defended. + */ + memset(&zonerating[0], '\0', sizeof(zonerating)); + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + int diff; + + diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiair, diff); + } + + diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiarmor, diff); + } + + diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiinfantry, diff); + } + } + + /* + ** Now that each zone has been given a desirability rating, find the zone + ** with the greatest value and try to place the building in that zone. + */ + ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST); + int largest = 0; + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + if (zonerating[z] > largest) { + zone = z; + largest = zonerating[z]; + } + } + + CELL zcell = Find_Cell_In_Zone(building, zone); + if (zcell) { + return(Cell_Coord(zcell)); + } + + /* + ** Could not build in preferred zone, so try building in any zone. + */ + static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST}; + int start = Random_Pick(0U, ARRAY_SIZE(_zones)-1); + for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) { + ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)]; + zcell = Find_Cell_In_Zone(building, tryzone); + if (zcell) return(zcell); + } + + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * * + * This routine will average the location of the base and record the center point. The * + * recorded center point is used to determine such things as how far the base is spread * + * out and where to protect the most. This routine should be called whenever a building * + * is created or destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/28/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Center(void) +{ + //assert(Houses.ID(this) == ID); + + /* + ** First presume that there is no base. If there is a base, then these values will be + ** properly filled in below. + */ + Center = 0; + Radius = 0; + for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { + ZoneInfo[zone].AirDefense = 0; + ZoneInfo[zone].ArmorDefense = 0; + ZoneInfo[zone].InfantryDefense = 0; + } + + /* + ** Only process the center base size/position calculation if there are buildings to + ** consider. When no buildings for this house are present, then no processing need + ** occur. + */ + if (CurBuildings > 0) { + int x = 0; + int y = 0; + int count = 0; + int index; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + + /* + ** Give more "weight" to buildings that cost more. The presumption is that cheap + ** buildings don't affect the base disposition as much as the more expensive + ** buildings do. + */ + int weight = (b->Class->Cost_Of() / 1000)+1; + for (int i = 0; i < weight; i++) { + x += Coord_X(b->Center_Coord()); + y += Coord_Y(b->Center_Coord()); + count++; + } + } + } + + /* + ** This second check for quantity of buildings is necessary because the first + ** check against CurBuildings doesn't take into account if the building is in + ** limbo, but for base calculation, the limbo state disqualifies a building + ** from being processed. Thus, CurBuildings may indicate a base, but count may + ** not match. + */ + if (count > 0) { + x /= count; + y /= count; + +#ifdef NEVER + /* + ** Bias the center of the base away from the edges of the map. + */ + LEPTON left = Cell_To_Lepton(Map.MapCellX + 10); + LEPTON top = Cell_To_Lepton(Map.MapCellY + 10); + LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10); + LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10); + if (x < left) x = left; + if (x > right) x = right; + if (y < top) y = top; + if (y > bottom) y = bottom; +#endif + + Center = XY_Coord(x, y); + } + + /* + ** If there were any buildings discovered as legal to consider as part of the base, + ** then figure out the general average radius of the building disposition as it + ** relates to the center of the base. + */ + if (count > 1) { + int radius = 0; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + radius += Distance(Center, b->Center_Coord()); + } + } + Radius = max(radius / count, 2 * CELL_LEPTON_W); + + /* + ** Determine the relative strength of each base defense zone. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + ZoneType z = Which_Zone(b); + + if (z != ZONE_NONE) { + ZoneInfo[z].ArmorDefense += b->Anti_Armor(); + ZoneInfo[z].AirDefense += b->Anti_Air(); + ZoneInfo[z].InfantryDefense += b->Anti_Infantry(); + } + } + } + + } else { + Radius = 0x0200; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Expert_AI -- Handles expert AI processing. * + * * + * This routine is called when the computer should perform expert AI processing. This * + * method of AI is categorized as an "Expert System" process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This is relatively time consuming -- call periodically. * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Expert_AI(void) +{ + //assert(Houses.ID(this) == ID); + + BuildingClass * b = 0; + bool stop = false; + int time = TICKS_PER_SECOND * 10; + + /* + ** If the current enemy no longer has a base or is defeated, then don't consider + ** that house a threat anymore. Clear out the enemy record and then try + ** to find a new enemy. + */ + if (Enemy != HOUSE_NONE) { + HouseClass * h = HouseClass::As_Pointer(Enemy); + + if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) { + Enemy = HOUSE_NONE; + } + } + + /* + ** If there is no enemy assigned to this house, then assign one now. The + ** enemy that is closest is picked. However, don't pick an enemy if the + ** base has not been established yet. + */ + if (ActiveBScan && Center && Attack == 0) { + int close = 0; + HousesType enemy = HOUSE_NONE; + int maxunit = 0; + int maxinfantry = 0; + int maxvessel = 0; + int maxaircraft = 0; + int maxbuilding = 0; + int enemycount = 0; + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) { + + /* + ** Perform a special restriction check to ensure that no enemy is chosen if + ** there is even one enemy that has not established a base yet. This will + ** ensure an accurate first pick for enemy since the distance to base + ** value can be determined. + */ + if (!h->IsStarted) { + enemy = HOUSE_NONE; + break; + } + + /* + ** Keep track of the number of buildings and units owned by the + ** enemy. This is used to bring up the maximum allowed to match. + */ + maxunit += h->CurUnits; + maxbuilding += h->CurBuildings; + maxinfantry += h->CurInfantry; + //maxvessel += h->CurVessels; + maxaircraft += h->CurAircraft; + enemycount++; + + /* + ** Determine a priority value based on distance to the center of the + ** candidate base. The higher the value, the better the candidate house + ** is to becoming the preferred enemy for this house. + */ + int value = ((MAP_CELL_W*2)-Distance(Center, h->Center)); + value *= 2; + + /* + ** In addition to distance, record the number of kills directed + ** against this house. The enemy that does more damage might be + ** considered a greater threat. + */ + value += h->BuildingsKilled[Class->House]*5; + value += h->UnitsKilled[Class->House]; + + /* + ** Factor in the relative sizes of the bases. An enemy that has a + ** larger base will be considered a bigger threat. Conversely, a + ** smaller base is considered a lesser threat. + */ + value += h->CurUnits - CurUnits; + value += h->CurBuildings - CurBuildings; + value += (h->CurInfantry - CurInfantry)/4; + + /* + ** Whoever last attacked is given a little more priority as + ** a potential designated enemy. + */ + if (house == LAEnemy) { + value += 100; + } + +#ifdef OBSOLETE + /* + ** Human players are a given preference as the target. + */ + if (h->IsHuman) { + value *= 2; + } +#endif + + /* + ** Compare the calculated value for this candidate house and if it is + ** greater than the previously recorded maximum, record this house as + ** the prime candidate for enemy. + */ + if (value > close) { + enemy = house; + close = value; + } + } + } + + /* + ** Record this closest enemy base as the first enemy to attack. + */ + Enemy = enemy; + + /* + ** Up the maximum allowed units and buildings to match a rough average + ** of what the enemies are allowed. + */ + if (enemycount) { + maxunit /= enemycount; + maxbuilding /= enemycount; + maxinfantry /= enemycount; + maxvessel /= enemycount; + maxaircraft /= enemycount; + } + + if (Control.MaxBuilding < (unsigned)maxbuilding + 10) { + Control.MaxBuilding = maxbuilding + 10; + } + if (Control.MaxUnit < (unsigned)maxunit + 10) { + Control.MaxUnit = maxunit + 10; + } + if (Control.MaxInfantry < (unsigned)maxinfantry + 10) { + Control.MaxInfantry = maxinfantry + 10; + } + //if (Control.MaxVessel < (unsigned)maxvessel + 10) { + // Control.MaxVessel = maxvessel + 10; + //} + if (Control.MaxAircraft < (unsigned)maxaircraft + 10) { + Control.MaxAircraft = maxaircraft + 10; + } + } + + /* + ** House state transition check occurs here. Transitions that occur here are ones + ** that relate to general base condition rather than specific combat events. + ** Typically, this is limited to transitions between normal buildup mode and + ** broke mode. + */ + if (State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } else { + if (State == STATE_BUILDUP) { + if (Available_Money() < 25) { + State = STATE_BROKE; + } + } + if (State == STATE_BROKE) { + if (Available_Money() >= 25) { + State = STATE_BUILDUP; + } + } + if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) { + State = STATE_BUILDUP; + } + if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) { + State = STATE_ATTACKED; + } + } + + /* + ** Records the urgency of all actions possible. + */ + UrgencyType urgency[STRATEGY_COUNT]; + StrategyType strat; + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + urgency[strat] = URGENCY_NONE; + + switch (strat) { + case STRATEGY_BUILD_POWER: + urgency[strat] = Check_Build_Power(); + break; + + case STRATEGY_BUILD_DEFENSE: + urgency[strat] = Check_Build_Defense(); + break; + + case STRATEGY_BUILD_INCOME: + urgency[strat] = Check_Build_Income(); + break; + + case STRATEGY_FIRE_SALE: + urgency[strat] = Check_Fire_Sale(); + break; + + case STRATEGY_BUILD_ENGINEER: + urgency[strat] = Check_Build_Engineer(); + break; + + case STRATEGY_BUILD_OFFENSE: + urgency[strat] = Check_Build_Offense(); + break; + + case STRATEGY_RAISE_MONEY: + urgency[strat] = Check_Raise_Money(); + break; + + case STRATEGY_RAISE_POWER: + urgency[strat] = Check_Raise_Power(); + break; + + case STRATEGY_LOWER_POWER: + urgency[strat] = Check_Lower_Power(); + break; + + case STRATEGY_ATTACK: + urgency[strat] = Check_Attack(); + break; + + default: + urgency[strat] = URGENCY_NONE; + break; + } + } + + /* + ** Performs the action required for each of the strategies that share + ** the most urgent category. Stop processing if any strategy at the + ** highest urgency performed any action. This is because higher urgency + ** actions tend to greatly affect the lower urgency actions. + */ + for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) { + bool acted = false; + + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + if (urgency[strat] == u) { + switch (strat) { + case STRATEGY_BUILD_POWER: + acted |= AI_Build_Power(u); + break; + + case STRATEGY_BUILD_DEFENSE: + acted |= AI_Build_Defense(u); + break; + + case STRATEGY_BUILD_INCOME: + acted |= AI_Build_Income(u); + break; + + case STRATEGY_FIRE_SALE: + acted |= AI_Fire_Sale(u); + break; + + case STRATEGY_BUILD_ENGINEER: + acted |= AI_Build_Engineer(u); + break; + + case STRATEGY_BUILD_OFFENSE: + acted |= AI_Build_Offense(u); + break; + + case STRATEGY_RAISE_MONEY: + acted |= AI_Raise_Money(u); + break; + + case STRATEGY_RAISE_POWER: + acted |= AI_Raise_Power(u); + break; + + case STRATEGY_LOWER_POWER: + acted |= AI_Lower_Power(u); + break; + + case STRATEGY_ATTACK: + acted |= AI_Attack(u); + break; + + default: + break; + } + } + } + } + + return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2)); +} + + +UrgencyType HouseClass::Check_Build_Power(void) const +{ + //assert(Houses.ID(this) == ID); + + //fixed frac = Power_Fraction(); + int frac = Power_Fraction(); + + UrgencyType urgency = URGENCY_NONE; + + //if (frac < 1 && Can_Make_Money()) { + if (frac < 0x0100 && Can_Make_Money()) { + urgency = URGENCY_LOW; + + /* + ** Very low power condition is considered a higher priority. + */ + //if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM; + if (frac < 0x00C0) urgency = URGENCY_MEDIUM; + + /* + ** When under attack and there is a need for power in defense, + ** then consider power building a higher priority. + */ + // No chronosphere in TD. ST - 7/19/2019 4:38PM + //if (State == STATE_THREATENED || State == STATE_ATTACKED) { + // if (BScan | (STRUCTF_CHRONOSPHERE)) { + // urgency = URGENCY_HIGH; + // } + //} + + } + return(urgency); +} + + +UrgencyType HouseClass::Check_Build_Defense(void) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that base defense + ** should be given. The more vulnerable the base is, the higher + ** the urgency this routine should return. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Offense(void) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that offensive + ** weaponry should be given. Surplus money or a very strong + ** defense will cause the offensive urgency to increase. + */ + return(URGENCY_NONE); +} + +/* +** Determines what the attack state of the base is. The higher the state, +** the greater the immediate threat to base defense is. +*/ +UrgencyType HouseClass::Check_Attack(void) const +{ + //assert(Houses.ID(this) == ID); + + if (Frame > TICKS_PER_MINUTE && Attack == 0) { + if (State == STATE_ATTACKED) { + return(URGENCY_LOW); + } + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Income(void) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** This routine should determine if income processing buildings + ** should be constructed and at what urgency. The lower the money, + ** the lower the refineries, or recent harvester losses should + ** cause a greater urgency to be returned. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Fire_Sale(void) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** If there are no more factories at all, then sell everything off because the game + ** is basically over at this point. + */ + //if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { + if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Engineer(void) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** This routine should check to see what urgency that the production of + ** engineers should be. If a friendly building has been captured or the + ** enemy has weak defenses, then building an engineer would be a priority. + */ + return(URGENCY_NONE); +} + + +/* +** Checks to see if money is critically low and something must be done +** to immediately raise cash. +*/ +UrgencyType HouseClass::Check_Raise_Money(void) const +{ + //assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + if (Available_Money() < 100) { + urgency = URGENCY_LOW; + } + if (Available_Money() < 2000 && !Can_Make_Money()) { + urgency++; + } + + return(urgency); +} + +/* +** Checks to see if power is very low and if so, a greater urgency to +** build more power is returned. +*/ +UrgencyType HouseClass::Check_Lower_Power(void) const +{ + //assert(Houses.ID(this) == ID); + + if (Power > Drain+300) { + return(URGENCY_LOW); + } + return(URGENCY_NONE); +} + +/* +** This routine determines if there is a power emergency. Such an +** emergency might require selling of structures in order to free +** up power. This might occur if the base is being attacked and there +** are defenses that require power, but are just short of having +** enough. +*/ +UrgencyType HouseClass::Check_Raise_Power(void) const +{ + //assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + + if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) { +// if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) { + urgency = URGENCY_MEDIUM; + if (State == STATE_ATTACKED) { + urgency++; + } + } + return(urgency); +} + + +bool HouseClass::AI_Attack(UrgencyType ) +{ + //assert(Houses.ID(this) == ID); + + bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33)); + bool forced = (CurBuildings == 0); + int index; + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * a = Aircraft.Ptr(index); + + if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) { + if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + a->Assign_Mission(MISSION_HUNT); + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * u = Units.Ptr(index); + + if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) { + if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + u->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this unit is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) { + u->ArchiveTarget = ::As_Target(Where_To_Go(u)); + } + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * i = Infantry.Ptr(index); + + if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) { + if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) { + i->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this soldier is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) { + i->ArchiveTarget = ::As_Target(Where_To_Go(i)); + } + } + } + } + Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + return(true); +} + + +/* +** Given the specified urgency, build a power structure to meet +** this need. +*/ +bool HouseClass::AI_Build_Power(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + return(false); +} + + +/* +** Given the specified urgency, build base defensive structures +** according to need and according to existing base disposition. +*/ +bool HouseClass::AI_Build_Defense(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build offensive units according +** to need and according to the opponents base defenses. +*/ +bool HouseClass::AI_Build_Offense(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build income producing +** structures according to need. +*/ +bool HouseClass::AI_Build_Income(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + return(false); +} + + +bool HouseClass::AI_Fire_Sale(UrgencyType urgency) +{ + //assert(Houses.ID(this) == ID); + + if (CurBuildings && urgency == URGENCY_CRITICAL) { + Fire_Sale(); + Do_All_To_Hunt(); + return(true); + } + return(false); +} + +/* +** Given the specified urgency, build an engineer. +*/ +bool HouseClass::AI_Build_Engineer(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, sell of some power since +** there appears to be excess. +*/ +bool HouseClass::AI_Lower_Power(UrgencyType ) const +{ + //assert(Houses.ID(this) == ID); + + BuildingClass * b = Find_Building(STRUCT_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + + b = Find_Building(STRUCT_ADVANCED_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * * + * This routine is called when the computer needs to raise power by selling off buildings. * + * Usually this occurs because of some catastrophe that has lowered power levels to * + * the danger zone. * + * * + * INPUT: urgency -- The urgency that the power needs to be raised. This controls what * + * buildings will be sold. * + * * + * OUTPUT: bool; Was a building sold to raise power? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Power(UrgencyType urgency) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ +#if (0) + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_TESLA, URGENCY_HIGH} + }; +#endif + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_BIO_LAB, URGENCY_LOW}, + {STRUCT_EYE, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_OBELISK, URGENCY_HIGH}, + {STRUCT_TURRET, URGENCY_HIGH}, + {STRUCT_ATOWER, URGENCY_HIGH}, + {STRUCT_GTOWER, URGENCY_HIGH} + }; + + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + BuildingClass * b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * * + * This routine handles the situation where the computer desperately needs cash but cannot * + * wait for normal harvesting to raise it. Buildings must be sold. * + * * + * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, * + * the more important the buildings that can be sold become. * + * * + * OUTPUT: bool; Was a building sold to raise cash? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Money(UrgencyType urgency) const +{ + //assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ +#if (0) + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_STORAGE,URGENCY_LOW}, + {STRUCT_REPAIR,URGENCY_LOW}, + {STRUCT_TESLA,URGENCY_MEDIUM}, + {STRUCT_HELIPAD,URGENCY_MEDIUM}, + {STRUCT_POWER,URGENCY_HIGH}, + {STRUCT_AIRSTRIP,URGENCY_HIGH}, +// {STRUCT_WEAP,URGENCY_HIGH}, +// {STRUCT_BARRACKS,URGENCY_HIGH}, +// {STRUCT_TENT,URGENCY_HIGH}, + {STRUCT_CONST,URGENCY_CRITICAL} + }; +#endif + + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_BIO_LAB, URGENCY_LOW}, + {STRUCT_EYE, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_STORAGE,URGENCY_LOW}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_OBELISK, URGENCY_HIGH}, + {STRUCT_TURRET, URGENCY_HIGH}, + {STRUCT_ATOWER, URGENCY_HIGH}, + {STRUCT_GTOWER, URGENCY_HIGH}, + {STRUCT_HELIPAD,URGENCY_MEDIUM}, + {STRUCT_POWER,URGENCY_HIGH}, + {STRUCT_AIRSTRIP,URGENCY_HIGH}, + {STRUCT_CONST,URGENCY_CRITICAL} + }; + + + + BuildingClass * b = 0; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +#ifdef NEVER + +/*********************************************************************************************** + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * * + * This logic is used to maintain a base defense. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Base_Defense(void) +{ + //assert(Houses.ID(this) == ID); + + /* + ** Check to find if any zone of the base is over defended. Such zones should have + ** some of their defenses sold off to make better use of the money. + */ + + /* + ** Make sure that the core defense is only about 1/2 of the perimeter defense average. + */ + int average = 0; + for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { + average += ZoneInfo[z].AirDefense; + average += ZoneInfo[z].ArmorDefense; + average += ZoneInfo[z].InfantryDefense; + } + average /= (ZONE_COUNT-ZONE_NORTH); + + /* + ** If the core value is greater than the average, then sell off some of the + ** inner defensive structures. + */ + int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense; + if (core >= average) { + static StructType _stype[] = { + STRUCT_GTOWER, + STRUCT_TURRET, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_TESLA, + STRUCT_SAM + }; + BuildingClass * b; + + for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) { + b = Find_Building(_stype[index], ZONE_CORE); + if (b) { + b->Sell_Back(1); + break; + } + } + } + + /* + ** If the enemy doesn't have any offensive air capability, then sell off any + ** SAM sites. Only do this when money is moderately low. + */ + if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) { + + /* + ** Scan to find if ANY human opponents have aircraft or a helipad. If one + ** is found then consider that opponent to have a valid air threat potential. + ** Don't sell off SAM sites in that case. + */ + bool nothreat = true; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * house = HouseClass::As_Pointer(h); + + if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) { + if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) { + nothreat = false; + break; + } + } + } + } + + return(TICKS_PER_SECOND*5); +} +#endif + + +/*********************************************************************************************** + * HouseClass::AI_Building -- Determines what building to build. * + * * + * This routine handles the general case of determining what building to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + * 11/03/1996 JLB : Tries to match aircraft of enemy * + *=============================================================================================*/ +int HouseClass::AI_Building(void) +{ + //assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND); + +#if (0) // Not used for GAME_NORMAL in C&C. ST - 7/23/2019 3:04PM + if (Session.Type == GAME_NORMAL && Base.House == Class->House) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + BuildStructure = node->Type; + } + } +#endif + + if (IsBaseBuilding) { + /* + ** Don't suggest anything to build if the base is already big enough. + */ + unsigned int quant = 0; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass const * hptr = HouseClass::As_Pointer(h); + + if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) { + quant = hptr->CurBuildings; + } + } + quant += Rule.BaseSizeAdd; + +// TCTC -- Should multiply largest player base by some rational number. +// if (CurBuildings >= quant) return(TICKS_PER_SECOND); + + BuildChoice.Free_All(); + BuildChoiceClass * choiceptr; + StructType stype = STRUCT_NONE; + int money = Available_Money(); + //int level = Control.TechLevel; + bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0); + BuildingTypeClass const * b = NULL; + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + //level = Control.TechLevel; + + /* + ** Try to build a power plant if there is insufficient power and there is enough + ** money available. + */ + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a refinery if there isn't one already available. + */ + unsigned int current = BQuantity[STRUCT_REFINERY]; + if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY); + if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always make sure there is a barracks available, but only if there + ** will be sufficient money to train troopers. + */ + //current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT]; + current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_HAND]; + if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + //b = &BuildingTypeClass::As_Reference(STRUCT_TENT); + b = &BuildingTypeClass::As_Reference(STRUCT_HAND); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + } +#if (0) + /* + ** Try to build one dog house. + */ + current = BQuantity[STRUCT_KENNEL]; + if (current < 1 && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Try to build one gap generator. + */ + current = BQuantity[STRUCT_GAP]; + if (current < 1 && Power_Fraction() >= 1 && hasincome) { + b = &BuildingTypeClass::As_Reference(STRUCT_GAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } +#endif + /* + ** A source of combat vehicles is always needed, but only if there will + ** be sufficient money to build vehicles. + */ + current = BQuantity[STRUCT_WEAP]; + if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_WEAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always build up some base defense. + */ + //current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET]; + current = BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_OBELISK]; + if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) { + //b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET); + b = &BuildingTypeClass::As_Reference(STRUCT_OBELISK); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + //if (Percent_Chance(50)) { + //b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX); + b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } +#if (0) + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } +#endif + } + } + + /* + ** Build some air defense. + */ + //current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN]; + current = BQuantity[STRUCT_SAM]; + if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) { + + /* + ** Building air defense only makes sense if the opponent has aircraft + ** of some kind. + */ + bool airthreat = false; + int threat_quantity = 0; + if (enemy != NULL && enemy->AScan != 0) { + airthreat = true; + threat_quantity = enemy->CurAircraft; + } + if (!airthreat) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && !Is_Ally(house) && h->AScan != 0) { + airthreat = true; + break; + } + } + } + + if (airthreat) { + + if (BQuantity[STRUCT_RADAR] == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_RADAR); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type); + } + } + } + + b = &BuildingTypeClass::As_Reference(STRUCT_SAM); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } else { +#if (0) + b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } +#endif + } + } + } + + +#if (0) + /* + ** Advanced base defense would be good. + */ + current = BQuantity[STRUCT_TESLA]; + if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_TESLA); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a tech center as soon as possible. + */ + current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH]; + if (current < 1) { + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } +#endif + + /* + ** A helipad would be good. + */ + current = BQuantity[STRUCT_HELIPAD]; + if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** An airstrip would be good. + */ + current = BQuantity[STRUCT_AIRSTRIP]; + if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Pick the choice that is the most urgent. + */ + UrgencyType best = URGENCY_NONE; + int bestindex; + for (int index = 0; index < BuildChoice.Count(); index++) { + if (BuildChoice.Ptr(index)->Urgency > best) { + bestindex = index; + best = BuildChoice.Ptr(index)->Urgency; + } + } + if (best != URGENCY_NONE) { + BuildStructure = BuildChoice.Ptr(bestindex)->Structure; + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Unit -- Determines what unit to build next. * + * * + * This routine handles the general case of determining what units to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of games frames to delay before calling this routine again.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Unit(void) +{ + //assert(Houses.ID(this) == ID); + + if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND); + if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND); + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. + */ + if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) { + //if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) { + if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)BuildLevel) { + BuildUnit = UNIT_HARVESTER; + return(TICKS_PER_SECOND); + } + } + + //if (Session.Type == GAME_NORMAL) { // Why? ST - 7/24/2019 2:38PM + + int counter[UNIT_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + //if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->IsAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + //TechnoTypeClass const * memtype = team->Members[subindex].Class; + TechnoTypeClass const * memtype = team->Class[subindex]; + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)memtype)->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + //TechnoTypeClass const * memtype = team->Members[subindex].Class; + TechnoTypeClass const * memtype = team->Class[subindex]; + + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)memtype)->Type; + //counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = max(counter[subtype], 1); + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + //if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + if (unit != NULL && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those objects that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildUnit = bestlist[Random_Pick(0, bestcount-1)]; + } + //} + + if (IsBaseBuilding) { + + int counter[UNIT_COUNT]; + int total = 0; + UnitType index; + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index); + if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) { + //if (utype->PrimaryWeapon != NULL) { + if (utype->Primary != WEAPON_NONE) { + counter[index] = 20; + } else { + counter[index] = 1; + } + } else { + counter[index] = 0; + } + total += counter[index]; + } + + if (total > 0) { + int choice = Random_Pick(0, total-1); + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (choice < counter[index]) { + BuildUnit = index; + break; + } + choice -= counter[index]; + } + } + } + + return(TICKS_PER_SECOND); +} + + +int HouseClass::AI_Vessel(void) +{ +#if (0) + //assert(Houses.ID(this) == ID); + if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND); + + if (CurVessels >= Control.MaxVessel) { + return(TICKS_PER_SECOND); + } + + if (Session.Type == GAME_NORMAL) { + + int counter[VESSEL_COUNT]; + if (Session.Type == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + //if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)BuildLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + //if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->IsAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type; + //counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = max(counter[subtype], 1); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int vindex = 0; vindex < Vessels.Count(); vindex++) { + VesselClass * unit = Vessels.Ptr(vindex); + //if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + if (unit != NULL && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + VesselType bestlist[VESSEL_COUNT]; + for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildVessel = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + BuildVessel = VESSEL_NONE; + } +#endif + return(TICKS_PER_SECOND); +} + + + +/*********************************************************************************************** + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * * + * This routine handles the general case of determining what infantry unit to build * + * next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before being called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Infantry(void) +{ + //assert(Houses.ID(this) == ID); + + if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND); + if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND); + + //if (Session.Type == GAME_NORMAL) { + if (GameToPlay == GAME_NORMAL) { // Not used for skirmish? ST - 7/24/2019 2:59PM +#if (0) + TechnoTypeClass const * techno = 0; + int counter[INFANTRY_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + + //if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->IsAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + //counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0); + counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += 1 + (team->IsReinforcable ? 1 : 0); + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type; +// counter[subtype] = 1; + //counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = max(counter[subtype], 1); + counter[subtype] = min(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + //if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) { + if (infantry != NULL && counter[infantry->Class->Type] > 0) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + + if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) { + if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + int pick = Random_Pick(0, bestcount-1); + BuildInfantry = bestlist[pick]; + } +#endif + } + + if (IsBaseBuilding) { + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + /* + ** This structure is used to keep track of the list of infantry types that should be + ** built. The infantry type and the value assigned to it is recorded. + */ + struct { + InfantryType Type; // Infantry type. + int Value; // Relative value assigned. + } typetrack[INFANTRY_COUNT]; + int count = 0; + int total = 0; + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + //if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)BuildLevel) { + typetrack[count].Value = 0; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility. + int clipindex = index; + if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT; + if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#else + if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#endif + + switch (index) { + case INFANTRY_E1: + typetrack[count].Value = 3; + break; + + case INFANTRY_E2: + typetrack[count].Value = 5; + break; + + case INFANTRY_E3: + typetrack[count].Value = 2; + break; + + case INFANTRY_E4: + typetrack[count].Value = 5; + break; + + //case INFANTRY_RENOVATOR: + case INFANTRY_E7: + if (CurInfantry > 5) { + typetrack[count].Value = 1 - max(IQuantity[index], 0); + } + break; + + //case INFANTRY_TANYA: + // typetrack[count].Value = 1 - max(IQuantity[index], 0); + // break; + + default: + typetrack[count].Value = 0; + break; + } + } + + if (typetrack[count].Value > 0) { + typetrack[count].Type = index; + total += typetrack[count].Value; + count++; + } + } + } + + /* + ** If there is at least one choice, then pick it. The object picked + ** is influenced by the weight (value) assigned to it. This is accomplished + ** by picking a number between 0 and the total weight value. The appropriate + ** infantry object that matches the number picked is then selected to be built. + */ + if (count > 0) { + int pick = Random_Pick(0, total-1); + for (int index = 0; index < count; index++) { + if (pick < typetrack[index].Value) { + BuildInfantry = typetrack[index].Type; + break; + } + pick -= typetrack[index].Value; + } + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * * + * This routine is used to determine the general case of what aircraft to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frame to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Aircraft(void) +{ + //assert(Houses.ID(this) == ID); + + if (!IsHuman && IQ >= Rule.IQAircraft) { + if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND); + if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND); + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HELICOPTER), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_HELICOPTER).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_HELICOPTER] + AQuantity[AIRCRAFT_ORCA]) { + BuildAircraft = AIRCRAFT_HELICOPTER; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_ORCA), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_ORCA).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_ORCA] + AQuantity[AIRCRAFT_HELICOPTER]) { + BuildAircraft = AIRCRAFT_ORCA; + return(TICKS_PER_SECOND); + } + +#if (0) + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_LONGBOW; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_HIND; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_MIG; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) && + //AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel && + AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)BuildLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_YAK; + return(TICKS_PER_SECOND); + } +#endif + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::Production_Begun -- Records that production has begun. * + * * + * This routine is used to inform the Expert System that production of the specified object * + * has begun. This allows the AI to proceed with picking another object to begin production * + * on. * + * * + * INPUT: product -- Pointer to the object that production has just begun on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Production_Begun(TechnoClass const * product) +{ + //assert(Houses.ID(this) == ID); + + if (product != NULL) { + switch (product->What_Am_I()) { + case RTTI_UNIT: + if (*((UnitClass*)product) == BuildUnit) { + BuildUnit = UNIT_NONE; + } + break; +#if (0) + case RTTI_VESSEL: + if (*((VesselClass*)product) == BuildVessel) { + BuildVessel = VESSEL_NONE; + } + break; +#endif + case RTTI_INFANTRY: + if (*((InfantryClass*)product) == BuildInfantry) { + BuildInfantry = INFANTRY_NONE; + } + break; + + case RTTI_BUILDING: + if (*((BuildingClass*)product) == BuildStructure) { + BuildStructure = STRUCT_NONE; + } + break; + + case RTTI_AIRCRAFT: + if (*((AircraftClass*)product) == BuildAircraft) { + BuildAircraft = AIRCRAFT_NONE; + } + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * * + * This routine informs the Expert System that the specified object is no longer part of * + * this house's inventory. This occurs when the object is destroyed or captured. * + * * + * INPUT: techno -- Pointer to the object to remove from the tracking systems of this * + * house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Remove(TechnoClass const * techno) +{ + //assert(Houses.ID(this) == ID); + + int type; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings--; + BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_AIRCRAFT: + CurAircraft--; + AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_INFANTRY: + CurInfantry--; + if (!((InfantryClass *)techno)->IsTechnician) { + type = ((InfantryTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT; +#endif + IQuantity[type]--; + } + break; + + case RTTI_UNIT: + CurUnits--; + type = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT; +#endif + UQuantity[type]--; + break; + +#if (0) + case RTTI_VESSEL: + CurVessels--; + type = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT; +#endif + VQuantity[type]--; + break; +#endif + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * * + * This function is called when the specified object is now available as part of the house's* + * inventory. This occurs when the object is newly produced and also when it is captured * + * by this house. * + * * + * INPUT: techno -- Pointer to the object that is now part of the house inventory. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Add(TechnoClass const * techno) +{ + //assert(Houses.ID(this) == ID); + + StructType building; + AircraftType aircraft; + InfantryType infantry; + UnitType unit; + //VesselType vessel; + //int quant; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings++; + building = ((BuildingTypeClass const &)techno->Class_Of()).Type; + BQuantity[building]++; + BScan |= (1L << building); +#if (0) // This is a stats thing. ST - 7/24/2019 3:08PM + if (Session.Type == GAME_INTERNET) { + BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID); + } +#endif + break; + + case RTTI_AIRCRAFT: + CurAircraft++; + aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type; + AQuantity[aircraft]++; + AScan |= (1L << aircraft); +#if (0) // This is a stats thing. ST - 7/24/2019 3:08PM + if (Session.Type == GAME_INTERNET) { + AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID); + } +#endif + break; + + case RTTI_INFANTRY: + CurInfantry++; + infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type; + if (!((InfantryClass *)techno)->IsTechnician) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = infantry; + if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT; + IQuantity[quant]++; +#else + IQuantity[infantry]++; +#endif + +#if (0) // This is a stats thing. ST - 7/24/2019 3:08PM + if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) { + InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID); + } +#endif + IScan |= (1L << infantry); + } + break; + + case RTTI_UNIT: + CurUnits++; + unit = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = unit; + if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT; + UQuantity[quant]++; +#else + UQuantity[unit]++; +#endif + UScan |= (1L << unit); +#if (0) // This is a stats thing. ST - 7/24/2019 3:08PM + if (Session.Type == GAME_INTERNET) { + UnitTotals->Increment_Unit_Total(techno->Class_Of().ID); + } +#endif + break; + + +#if (0) + case RTTI_VESSEL: + CurVessels++; + vessel = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = vessel; + if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT; + VQuantity[quant]++; +#else + VQuantity[vessel]++; +#endif + VScan |= (1L << vessel); + if (Session.Type == GAME_INTERNET) { + VesselTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; +#endif + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * * + * Use this routine to fetch a pointer to the variable that holds the number of factories * + * that can produce the specified object type. This is a helper routine used when * + * examining the number of factories as well as adjusting their number. * + * * + * INPUT: rtti -- The RTTI of the object that could be produced. * + * * + * OUTPUT: Returns with the number of factories owned by this house that could produce the * + * object of the type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int * HouseClass::Factory_Counter(RTTIType rtti) +{ + switch (rtti) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitFactories); +#if (0) + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselFactories); +#endif + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftFactories); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryFactories); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingFactories); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * * + * This routine will recognize the specified object as having been removed from active * + * duty. * + * * + * INPUT: techno -- Pointer to the object to remove from active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Remove(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr - 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * * + * This routine will recognize the specified object as having entered active duty. Any * + * abilities granted to the house by that object are now available. * + * * + * INPUT: techno -- Pointer to the object that is entering active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Add(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr + 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * * + * This routine will determine what zone the specified coordinate lies in with respect to * + * this house's base. A location that is too distant from the base, even though it might * + * be a building, is not considered part of the base and returns ZONE_NONE. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * OUTPUT: Returns with the base zone that the specified coordinate lies in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(COORDINATE coord) const +{ + //assert(Houses.ID(this) == ID); + + if (coord == 0) return(ZONE_NONE); + + int distance = Distance(Center, coord); + if (distance <= Radius) return(ZONE_CORE); + if (distance > Radius*4) return(ZONE_NONE); + + DirType facing = Direction(Center, coord); + if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH); + if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST); + if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH); + return(ZONE_WEST); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * * + * Use this routine to determine what zone the specified object lies in. * + * * + * INPUT: object -- Pointer to the object that will be checked for zone occupation. * + * * + * OUTPUT: Returns with the base zone that the object lies in. For objects that are too * + * distant from the center of the base, ZONE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(ObjectClass const * object) const +{ + //assert(Houses.ID(this) == ID); + + if (!object) return(ZONE_NONE); + return(Which_Zone(object->Center_Coord())); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * * + * This routine is used to determine what base zone the specified cell is in. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far * + * away. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(CELL cell) const +{ + //assert(Houses.ID(this) == ID); + + return(Which_Zone(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * * + * This routine will go through all game objects and reset the existence bits for the * + * owning house. This method ensures that if the object exists, then the corresponding * + * existence bit is also set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Attributes(void) +{ + /* + ** Clear out all tracking values that will be filled in by this + ** routine. This allows the filling in process to not worry about + ** old existing values. + */ + int index; + for (index = 0; index < Houses.Count(); index++) { + HouseClass * house = Houses.Ptr(index); + + if (house != NULL) { + house->BScan = 0; + house->ActiveBScan = 0; + house->IScan = 0; + house->ActiveIScan = 0; + house->UScan = 0; + house->ActiveUScan = 0; + house->AScan = 0; + house->ActiveAScan = 0; +#if (0) + house->VScan = 0; + house->ActiveVScan = 0; +#endif + } + } + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + unit->House->UScan |= (1L << unit->Class->Type); + //if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + if (unit->IsLocked) { + if (!unit->IsInLimbo) { + unit->House->ActiveUScan |= (1L << unit->Class->Type); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + infantry->House->IScan |= (1L << infantry->Class->Type); + //if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + if (infantry->IsLocked) { + if (!infantry->IsInLimbo) { + infantry->House->ActiveIScan |= (1L << infantry->Class->Type); + //infantry->House->OldIScan |= (1L << infantry->Class->Type); + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + aircraft->House->AScan |= (1L << aircraft->Class->Type); + //if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + if (aircraft->IsLocked) { + if (!aircraft->IsInLimbo) { + aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type); + //aircraft->House->OldAScan |= (1L << aircraft->Class->Type); + } + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->Class->Type < 32) { + building->House->BScan |= (1L << building->Class->Type); + //if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + if (building->IsLocked) { + if (!building->IsInLimbo) { + building->House->ActiveBScan |= (1L << building->Class->Type); + building->House->OldBScan |= (1L << building->Class->Type); + } + } + } + } +#if (0) + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + vessel->House->VScan |= (1L << vessel->Class->Type); + if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) { + if (!vessel->IsInLimbo) { + vessel->House->ActiveVScan |= (1L << vessel->Class->Type); + vessel->House->OldVScan |= (1L << vessel->Class->Type); + } + } + } +#endif +} + + +/*********************************************************************************************** + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * * + * This routine is used to find the cell that is closest to the center point of the * + * zone specified. Typical use of this routine is for building and unit placement so that * + * they can "cover" the specified zone. * + * * + * INPUT: zone -- The zone that the center point is to be returned. * + * * + * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Zone_Cell(ZoneType zone) const +{ + //assert(Houses.ID(this) == ID); + + switch (zone) { + case ZONE_CORE: + return(Coord_Cell(Center)); + + case ZONE_NORTH: + return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3))); + + case ZONE_EAST: + return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3))); + + case ZONE_WEST: + return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3))); + + case ZONE_SOUTH: + return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * * + * This function is called for every new unit produced or delivered in order to determine * + * where the unit should "hang out" to await further orders. The best area for the * + * unit to loiter is returned as a cell location. * + * * + * INPUT: object -- Pointer to the object that needs to know where to go. * + * * + * OUTPUT: Returns with the cell that the unit should move to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + * 11/04/1996 JLB : Simplified to use helper functions * + *=============================================================================================*/ +CELL HouseClass::Where_To_Go(FootClass const * object) const +{ + //assert(Houses.ID(this) == ID); + //assert(object != NULL); + + ZoneType zone; // The zone that the object should go to. + if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) { + zone = ZONE_CORE; + } else { + zone = Random_Pick(ZONE_NORTH, ZONE_WEST); + } + + CELL cell = Random_Cell_In_Zone(zone); + //assert(cell != 0); + + return(Map.Nearby_Location(cell)); //, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL)); +} + + +/*********************************************************************************************** + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * * + * This routine is used to find targets out in the field and away from base defense. * + * Typical of this would be the attack helicopters and the roving attack bands of * + * hunter killers. * + * * + * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. * + * * + * OUTPUT: Returns with a suitable target to attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const +{ + //assert(Houses.ID(this) == ID); + + UnitClass * best = 0; + int value = 0; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) { + int val = Distance(coord, unit->Center_Coord()); + + if (unit->Anti_Air()) val *= 2; + + if (*unit == UNIT_HARVESTER) val /= 2; + + if (value == 0 || val < value) { + value = val; + best = unit; + } + } + } + if (best) { + return(best->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * * + * Call this routine to fetch the total quantity of aircraft of the type specified that is * + * owned by this house. * + * * + * INPUT: aircraft -- The aircraft type to check the quantity of. * + * * + * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this * + * house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(AircraftType aircraft) +{ + return(AQuantity[aircraft]); +} + + +/*********************************************************************************************** + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * * + * This is the counterpart to the Set_Factory function. It will return with a factory * + * pointer that is associated with the object type specified. * + * * + * INPUT: rtti -- The RTTI of the object type to find the factory for. * + * * + * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the * + * object type specified. * + * * + * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy * + * producing another unit of that category. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const +{ + int factory_index = -1; + + switch (rtti) { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = InfantryFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = AircraftFactory; + break; +#if (0) + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = VesselFactory; + break; +#endif + default: + factory_index = -1; + break; + } + + /* + ** Fetch the actual pointer to the factory object. If there is + ** no object factory that matches the specified rtti type, then + ** null is returned. + */ + if (factory_index != -1) { + return(Factories.Raw_Ptr(factory_index)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * * + * Call this routine when a factory has been created and it now must be passed on to the * + * house for tracking purposes. The house maintains several factory pointers and this * + * routine will ensure that the factory pointer gets stored correctly. * + * * + * INPUT: rtti -- The RTTI of the object the factory it to manufacture. * + * * + * factory -- The factory object pointer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory) +{ + int * factory_index = 0; + + //assert(rtti != RTTI_NONE); + + switch (rtti) { + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = &UnitFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = &InfantryFactory; + break; +#if (0) + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = &VesselFactory; + break; +#endif + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = &BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = &AircraftFactory; + break; + } + + //assert(factory_index != NULL); + + /* + ** Assign the factory to the appropriate slot. For the case of clearing + ** the factory out, then -1 is assigned. + */ + if (factory != NULL) { + //*factory_index = factory->ID; + *factory_index = Factories.ID(factory); + } else { + *factory_index = -1; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * * + * This routine will count the number of factories owned by this house that can build * + * objects of the specified type. * + * * + * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. * + * * + * OUTPUT: Returns with the number of factories that can build the object type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Factory_Count(RTTIType rtti) const +{ + int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti); + if (ptr != NULL) { + return(*ptr); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * * + * This will return the total number of buildings of that type owned by this house. * + * * + * INPUT: building -- The building type to check. * + * * + * OUTPUT: Returns with the number of buildings of that type owned by this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(StructType building) +{ + return(BQuantity[building]); +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +#if (0) // ST 7/17/2019 +void HouseClass::Read_INI(CCINIClass & ini) +{ + HouseClass * p; // Pointer to current player data. + char const * hname; // Pointer to house name. + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + + p = new HouseClass(index); + p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario); + p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding); + p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit); + p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry); + p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel); + if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit; + p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100; + p->Credits = p->Control.InitialCredits; + + int iq = ini.Get_Int(hname, "IQ", 0); + if (iq > Rule.MaxIQ) iq = 1; + p->IQ = p->Control.IQ = iq; + + p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH); + p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false); + + int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL)); + p->Make_Ally(index); + p->Make_Ally(HOUSE_NEUTRAL); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if ((owners & (1 << h)) != 0) { + p->Make_Ally(h); + } + } + } +} +#endif + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * * + * This routine will write out all data necessary to recreate it in anticipation of a * + * new scenario. All houses (that are active) will have their scenario type data written * + * out. * + * * + * INPUT: ini -- Reference to the INI database to write the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +#if (0) // ST 7/17/2019 +void HouseClass::Write_INI(CCINIClass & ini) +{ + /* + ** The identity house control object. Only if the house value differs from the + ** identity, will the data be written out. + */ + HouseStaticClass control; + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p != NULL) { + char const * name = p->Class->IniName; + + ini.Clear(name); + if (i >= HOUSE_MULTI1) continue; + + if (p->Control.InitialCredits != control.InitialCredits) { + ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100)); + } + + if (p->Control.Edge != control.Edge) { + ini.Put_SourceType(name, "Edge", p->Control.Edge); + } + + if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) { + ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit); + } + + if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) { + ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry); + } + + if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) { + ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding); + } + + if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) { + ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel); + } + + if (p->Control.TechLevel != control.TechLevel) { + ini.Put_Int(name, "TechLevel", p->Control.TechLevel); + } + + if (p->Control.IQ != control.IQ) { + ini.Put_Int(name, "IQ", p->Control.IQ); + } + + if (p->IsPlayerControl != false && p != PlayerPtr) { + ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl); + } + + ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL))); + } + } +} +#endif + + +#if (0) +/*********************************************************************************************** + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * * + * This routine will examine the current yak and mig situation verses airfields. If there * + * are equal aircraft to airfields, then this routine will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_No_YakMig(void) const +{ + int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG]; + + /* + ** Adjust the quantity down one if there is an aircraft in production. This will + ** allow production to resume after being held. + */ + FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT); + if (factory != NULL && factory->Get_Object() != NULL) { + AircraftClass const * air = (AircraftClass const *)factory->Get_Object(); + if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) { + quantity -= 1; + } + } + + if (quantity >= BQuantity[STRUCT_AIRSTRIP]) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * * + * This is a special hack check routine to see if the object type and id specified is * + * prevented from being produced. The Yak and the Mig are so prevented if there would be * + * insufficient airfields for them to land upon. * + * * + * INPUT: rtti -- The RTTI type of the value specified. * + * * + * value -- The type number (according to the RTTI type specified). * + * * + * OUTPUT: bool; Is production of this object prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const +{ + if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) { + return(Is_No_YakMig()); + } + return(false); +} +#endif + +/*********************************************************************************************** + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * * + * This routine will sell back all buildings owned by this house. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a fire sale performed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Fire_Sale(void) +{ + if (CurBuildings > 0) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) { + b->Sell_Back(1); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * * + * This routine will cause all combatants of this house to go into hunt mode. The effect of * + * this is to throw everything this house has to muster at the enemies of this house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 10/02/1996 JLB : Handles aircraft too. * + *=============================================================================================*/ +void HouseClass::Do_All_To_Hunt(void) const +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == this && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } + +#if (0) + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + + if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) { + if (vessel->Team) vessel->Team->Remove(vessel); + vessel->Assign_Mission(MISSION_HUNT); + } + } +#endif + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) { + if (aircraft->Team) aircraft->Team->Remove(aircraft); + aircraft->Assign_Mission(MISSION_HUNT); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * * + * Use this routine to determine if this house is legally allowed to ally with the * + * house specified. There are many reason why an alliance is not allowed. Typically, this * + * is when there would be no more opponents left to fight or if this house has been * + * defeated. * + * * + * INPUT: house -- The house that alliance with is desired. * + * * + * OUTPUT: bool; Is alliance with the house specified prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Allowed_To_Ally(HousesType house) const +{ + /* + ** Is not allowed to ally with a house that is patently invalid, such + ** as one that is illegally defined. + */ + if (house == HOUSE_NONE) { + return(false); + } + + /* + ** One cannot ally twice with the same house. + */ + if (Is_Ally(house)) { + return(false); + } + + /* + ** If the scenario is being set up, then alliances are always + ** allowed. No further checking is required. + */ + if (ScenarioInit) { + return(true); + } + + /* + ** Alliances (outside of scneario init time) are allowed only if + ** this is a multiplayer game. Otherwise, they are prohibited. + */ + //if (Session.Type == GAME_NORMAL) { + if (GameToPlay == GAME_NORMAL) { + return(false); + } + + /* + ** When the house is defeated, it can no longer make alliances. + */ + if (IsDefeated) { + return(false); + } + +#ifdef FIXIT_VERSION_3 + // Fix to prevent ally with computer. + if ( !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#else // FIXIT_VERSION_3 +#ifdef FIXIT_NO_COMP_ALLY + // Fix to prevent ally with computer. + if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#endif +#endif // FIXIT_VERSION_3 + + /* + ** Count the number of active houses in the game as well as the + ** number of existing allies with this house. + */ + int housecount = 0; + int allycount = 0; + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr = HouseClass::As_Pointer(house2); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) { + housecount++; + if (Is_Ally(hptr)) { + allycount++; + } + } + } + + /* + ** Alliance is not allowed if there wouldn't be any enemies left to + ** fight. + */ + if (housecount == allycount+1) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * * + * This routine will cause the computer players to become suspicious of the human * + * players and thus the computer players will band together in order to defeat the * + * human players. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Computer_Paranoid(void) +{ + if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019 + + /* + ** Loop through every computer controlled house and make allies with all other computer + ** controlled houses and then make enemies with all other human controlled houses. + */ + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) { + hptr->IsParanoid = true; + + /* + ** Break alliance with every human it is allied with and make friends with + ** any other computer players. + */ + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr2 = HouseClass::As_Pointer(house2); + if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) { + if (hptr2->IsHuman) { + hptr->Make_Enemy(house2); + } else { + hptr->Make_Ally(house2); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * * + * This routine will update the power output value of the house. It will cause any buildgins* + * that need to be redrawn to do so. * + * * + * INPUT: adjust -- The amount to adjust the power output value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Power(int adjust) +{ + Power += adjust; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * * + * This routine will update the drain value of the house. It will cause any buildings that * + * need to be redraw to do so. * + * * + * INPUT: adjust -- The amount to adjust the drain (positive means more drain). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Drain(int adjust) +{ + Drain += adjust; +} + + +/*********************************************************************************************** + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * * + * Use this routine to determine where the specified object should go if it were to go * + * some random (but legal) location within the zone specified. * + * * + * INPUT: techno -- The object that is desirous of going into the zone specified. * + * * + * zone -- The zone to find a location within. * + * * + * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If * + * no valid location could be found, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + * 11/04/1996 JLB : Not so strict on zone requirement. * + *=============================================================================================*/ +CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const +{ + if (techno == NULL) return(0); + + int bestval = -1; + int bestcell = 0; + TechnoTypeClass const * ttype = techno->Techno_Type_Class(); + + /* + ** Pick a random location within the zone specified. + */ + CELL trycell = Random_Cell_In_Zone(zone); + + short const * list = NULL; + if (techno->What_Am_I() == RTTI_BUILDING) { + list = techno->Occupy_List(true); + } + + /* + ** Find a legal placement position as close as possible to the picked location while still + ** remaining within the zone. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { +// if (Map.In_Radar(cell)) { + if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) { + bool ok = ttype->Legal_Placement(cell); + + /* + ** Another (adjacency) check is required for buildings. + */ + if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) { + ok = false; + } + + if (ok) { + int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell)); + if (bestval == -1 || dist < bestval) { + bestval = dist; + bestcell = cell; + } + } + } + } + + /* + ** Return the best location to move to. + */ + return(bestcell); +} + + +/*********************************************************************************************** + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * * + * This routine will pick a random cell within the zone specified. The pick will be * + * clipped to the map edge when necessary. * + * * + * INPUT: zone -- The zone to pick a cell from. * + * * + * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the * + * map, then a cell in the core zone is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/04/1996 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const +{ + COORDINATE coord = 0; + int maxdist = 0; + switch (zone) { + case ZONE_CORE: + coord = Coord_Scatter(Center, Random_Pick(0, Radius), true); + break; + + case ZONE_NORTH: + maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_EAST: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_SOUTH: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_WEST: + maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + } + + /* + ** Double check that the location is valid and if so, convert it into a cell + ** number. + */ + CELL cell; + if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) { + if (zone == ZONE_CORE) { + + /* + ** Finding a cell within the core failed, so just pick the center + ** cell. This cell is guaranteed to be valid. + */ + cell = Coord_Cell(Center); + } else { + + /* + ** If the edge fails, then try to find a cell within the core. + */ + cell = Random_Cell_In_Zone(ZONE_CORE); + } + } else { + cell = Coord_Cell(coord); + } + + /* + ** If the randomly picked location is not in the legal map area, then clip it to + ** the legal map area. + */ + if (!Map.In_Radar(cell)) { + int x = Cell_X(cell); + int y = Cell_Y(cell); + + if (x < Map.MapCellX) x = Map.MapCellX; + if (y < Map.MapCellY) y = Map.MapCellY; + if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1; + if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1; + cell = XY_Cell(x, y); + } + return(cell); +} + +/*********************************************************************************************** + * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the bit field storing which houses this house is allied with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/12/2019 JAS : Created. * + *=============================================================================================*/ +unsigned HouseClass::Get_Ally_Flags() +{ + return Allies; +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/HOUSE.H b/TIBERIANDAWN/HOUSE.H new file mode 100644 index 000000000..0d77d1cdc --- /dev/null +++ b/TIBERIANDAWN/HOUSE.H @@ -0,0 +1,899 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\house.h_v 2.21 16 Oct 1995 16:46:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : May 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HOUSE_H +#define HOUSE_H + +#include "type.h" +#include "region.h" +#include "vector.h" +#include "COORDA.h" +#include "Credits.h" + +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//} +class TriggerClass; +#ifdef USE_RA_AI +class FootClass; // ST - 7/17/2019 11:54AM +class FactoryClass; // ST - 7/17/2019 11:54AM +#endif // USE_RA_AI +/**************************************************************************** +** Player control structure. Each player (computer or human) has one of +** these structures associated. These are located in a global array. +*/ +//#define REBUILD_MAX 5 // Maximum number of structures to rebuild. +class HouseClass { + public: + /* + ** Pointer to the HouseTypeClass that this house is "owned" by. + ** All constant data for a house type is stored in that class. + */ + HouseTypeClass const * const Class; + + /* + ** Override handicap control values. + */ + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + float RepairDelay; + float BuildDelay; + + /* + ** This is the house type that this house object should act like. This + ** value controls production choices and radar cover plate imagery. + */ + HousesType ActLike; + + /* + ** Is this player active? Usually that answer is true, but for civilians, it + ** might possibly be false. + */ + unsigned IsActive:1; + + /* + ** If this house is controlled by the player, then this flag will be true. The + ** computer controls all other active houses. + */ + unsigned IsHuman:1; + unsigned WasHuman:1; + + /* + ** When the computer becomes alerted to the presence of the player's forces, it + ** begins production and attack logic. This flag is set to true if the human + ** player has been discovered by the computer. + */ + unsigned IsStarted:1; + + /* + ** When alerted, the house will create teams of the special "auto" type and + ** will generate appropriate units to fill those team types. + */ + unsigned IsAlerted:1; + + /* + ** If the house has been discovered, then this flag will be set + ** to true. However, the trigger even associated with discovery + ** will only be executed during the next house AI process. + */ + unsigned IsDiscovered:1; + + /* + ** If Tiberium storage is maxed out, then this flag will be set. At some point + ** the player is told of this fact and then this flag is cleared. This allows the + ** player to be told, but only occationally rather than continuously. + */ + unsigned IsMaxedOut:1; + + /* + ** If this house is played by a human in a multiplayer game, this flag + ** keeps track of whether this house has been defeated or not. + */ + unsigned IsDefeated:1; + + /* + ** These flags are used in conjunction with the BorrowedTime timer. When + ** that timer expires and one of these flags are set, then that event is + ** applied to the house. This allows a dramatic pause between the event + ** trigger and the result. + */ + unsigned IsToDie:1; + unsigned IsToWin:1; + unsigned IsToLose:1; + + /* + ** This flag is set when a transport carrying a civilian has been + ** successfully evacuated. It is presumed that a possible trigger + ** event will be sprung by this event. + */ + unsigned IsCivEvacuated:1; + + /* + ** If potentially something changed that might affect the sidebar list of + ** buildable objects, then this flag indicates that at the first LEGAL opportunity, + ** the sidebar will be recalculated. + */ + unsigned IsRecalcNeeded:1; + + /* + ** If the map has been completely revealed to the player, then this flag + ** will be set to true. By examining this flag, a second "reveal all map" + ** crate won't be given to the player. + */ + unsigned IsVisionary:1; + + /* + ** If a trigger has indicated that the airstrike option should appear, this flag + ** will be set to true. It is up to the normal house AI processing to actually + ** add the airstrike to the sidebar. + */ + unsigned IsAirstrikePending:1; + + /* + ** This records the existance of the three nuke weapon pieces. + */ + unsigned NukePieces:3; + + /* + ** This flag indicates that a free harvester is pending and will be + ** created when the FreeHarvester timer expires. + */ + unsigned IsFreeHarvester:1; + + TCountDownTimerClass FreeHarvester; + + /* + ** These super weapon control objects are used to control the recharge + ** and availability of these special weapons to this house. + */ + SuperClass IonCannon; + SuperClass AirStrike; + SuperClass NukeStrike; + + /* + ** This is a record of the last building that was built. For buildings that + ** were built as a part of scenario creation, it will be the last one + ** discovered. + */ + StructType JustBuilt; + + /* + ** This records the number of triggers associated with this house that are + ** blocking a win condition. A win will only occur if all the blocking + ** triggers have been deleted. + */ + int Blockage; + + /* + ** This timer controls the computer auto-attack logic. When this timer expires + ** and the house has been alerted, then it will create a set of attack + ** teams. + */ + TCountDownTimerClass AlertTime; + + /* + ** This timer is used to handle the delay between some catastrophic + ** event trigger and when it is actually carried out. + */ + TCountDownTimerClass BorrowedTime; + + /* + ** This is the last working scan bits for buildings. If a building is + ** active and owned by this house, it will have a bit set in this element + ** that corresponds to the building type number. Since this value is + ** accumulated over time, the "New" element contains the under-construction + ** version. + */ + unsigned long BScan; + unsigned long ActiveBScan; + unsigned long NewBScan; + unsigned long NewActiveBScan; +#ifdef USE_RA_AI + unsigned long OldBScan; +#endif + + /* + ** This is the last working scan bits for units. For every existing unit + ** type owned by this house, a corresponding bit is set in this element. As + ** the scan bits are being constructed, they are built into the "New" element + ** and then duplicated into the regular element at the end of every logic cycle. + */ + unsigned long UScan; + unsigned long ActiveUScan; + unsigned long NewUScan; + unsigned long NewActiveUScan; + + /* + ** Infantry type existence bits. Similar to unit and building bits. + */ + unsigned long IScan; + unsigned long ActiveIScan; + unsigned long NewIScan; + unsigned long NewActiveIScan; + + /* + ** Aircraft type existence bits. Similar to unit and building buts. + */ + unsigned long AScan; + unsigned long ActiveAScan; + unsigned long NewAScan; + unsigned long NewActiveAScan; + + /* + ** Record of gains and losses for this house during the course of the + ** scenario. + */ + unsigned CreditsSpent; + unsigned HarvestedCredits; + + /* + ** This is the running count of the number of units owned by this house. This + ** value is used to keep track of ownership limits. + */ + unsigned CurUnits; + unsigned CurBuildings; +#ifdef USE_RA_AI + unsigned CurInfantry; // Added from RA for AI. ST - 7/17/2019 12:03PM + unsigned CurAircraft; // Added from RA for AI. ST - 7/17/2019 12:03PM +#endif //USE_RA_AI + + /* + ** This is the maximum number allowed to be built by this house. The + ** value depends on the scenario being played. + */ + unsigned MaxUnit; + unsigned MaxBuilding; +#ifdef USE_RA_AI + unsigned MaxInfantry; // Added from RA for AI. ST - 7/19/2019 4:31PM + unsigned MaxAircraft; // Added from RA for AI. ST - 7/19/2019 4:31PM +#endif //USE_RA_AI + + /* + ** This is the running total of the number of credits this house has accumulated. + */ + long Tiberium; + long Credits; + long InitialCredits; + long Capacity; + + /* + ** Did this house lose via resignation? + */ + unsigned Resigned:1; + + /* + ** Did this house lose because the player quit? + */ + unsigned IGaveUp:1; + + /* + ** Stuff to keep track of the total number of units built by this house. + */ + UnitTrackerClass *AircraftTotals; + UnitTrackerClass *InfantryTotals; + UnitTrackerClass *UnitTotals; + UnitTrackerClass *BuildingTotals; + + /* + ** Total number of units destroyed by this house + */ + UnitTrackerClass *DestroyedAircraft; + UnitTrackerClass *DestroyedInfantry; + UnitTrackerClass *DestroyedUnits; + UnitTrackerClass *DestroyedBuildings; + + /* + ** Total number of enemy buildings captured by this house + */ + UnitTrackerClass *CapturedBuildings; + + /* + ** Total number of crates found by this house + */ + UnitTrackerClass *TotalCrates; + + /* + ** Records the number of infantry and vehicle factories active. This value is + ** used to regulate the speed of production. + */ + int AircraftFactories; + int InfantryFactories; + int UnitFactories; + int BuildingFactories; + int SpecialFactories; + + /* + ** This is the accumulation of the total power and drain factors. From these + ** values a ratio can be derived. This ratio is used to control the rate + ** of building decay. + */ + int Power; // Current power output. + int Drain; // Power consumption. + + /* + ** For generic (unspecified) reinforcements, they arrive by a common method. This + ** specifies which method is to be used. + */ + SourceType Edge; + + /* + ** For human controlled houses, only one type of unit can be produced + ** at any one instant. These factory objects control this production. + */ + int AircraftFactory; + int InfantryFactory; + int UnitFactory; + int BuildingFactory; + int SpecialFactory; + + /* + ** For human controlled houses, the current state of the radar map + */ + RadarEnum Radar; + + /* + ** This target value specifies where the flag is located. It might be a cell + ** or it might be an object. + */ + TARGET FlagLocation; + + /* + ** This is the flag-home-cell for this house. This is where we must bring + ** another house's flag back to, to defeat that house. + */ + CELL FlagHome; + + /* + ** For multiplayer games, each house instance has a remap table; the table + ** in the HousesTypeClass isn't used. This variable is set to the remap + ** table for the color the player wants to play. + */ + unsigned char const * RemapTable; + PlayerColorType RemapColor; + char Name[MPLAYER_NAME_MAX]; + + /* + ** For multiplayer games, each house needs to keep track of how many + ** objects of each other house they've killed. + */ + unsigned UnitsKilled[HOUSE_COUNT]; + unsigned UnitsLost; + unsigned BuildingsKilled[HOUSE_COUNT]; + unsigned BuildingsLost; + + /* + ** For multiplayer games, this keeps track of the last house to destroy + ** one of my units. + */ + HousesType WhoLastHurtMe; + + /* + ** Start location (waypoint index) passed in from GlyphX + */ + int StartLocationOverride; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + HouseClass(void) : Class(0) {}; + HouseClass(HousesType house); + ~HouseClass(void); + operator HousesType(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + ProdFailType Begin_Production(RTTIType type, int id); + ProdFailType Suspend_Production(RTTIType type); + ProdFailType Abandon_Production(RTTIType type); + bool Place_Object(RTTIType type, CELL cell); + bool Manual_Place(BuildingClass * builder, BuildingClass * object); + void Special_Weapon_AI(SpecialWeaponType id); + bool Place_Special_Blast(SpecialWeaponType id, CELL cell); + bool Flag_Attach(CELL cell, bool set_home = false); + bool Flag_Attach(UnitClass * object, bool set_home = false); + bool Flag_Remove(TARGET target, bool set_home = false); + void Init_Data(PlayerColorType color, HousesType house, int credits); + + void Sell_Wall(CELL cell); + bool Flag_To_Die(void); + bool Flag_To_Win(void); + bool Flag_To_Lose(void); + void Make_Ally(HousesType house); + void Make_Ally(ObjectClass * object) {if (object) Make_Ally(object->Owner());}; + void Make_Enemy(HousesType house); + void Make_Enemy(ObjectClass * object) {if (object) Make_Enemy(object->Owner());}; + bool Is_Ally(HousesType house) const; + bool Is_Ally(HouseClass const * house) const; + bool Is_Ally(ObjectClass const * object) const; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void); + bool Can_Build(StructType structure, HousesType house) const; + bool Can_Build(InfantryType infantry, HousesType house) const; + bool Can_Build(UnitType unit, HousesType) const; + bool Can_Build(AircraftType aircraft, HousesType house) const; + bool Can_Build(TechnoTypeClass const * type, HousesType house) const; + unsigned char const * Remap_Table(bool blushing=false, bool unit=false) const; + + TechnoTypeClass const * Suggest_New_Object(RTTIType objectype) const; + bool Does_Enemy_Building_Exist(StructType) const; + void Harvested(unsigned tiberium); + long Available_Money(void) const; + void Spend_Money(unsigned money); + void Refund_Money(unsigned money); + void Attacked(BuildingClass* source); +#ifndef USE_RA_AI + void Adjust_Power(int adjust) {Power += adjust;}; // Replaced by function from RA. ST - 7/17/2019 11:51AM + void Adjust_Drain(int adjust) {Drain += adjust;}; // Replaced by function from RA. ST - 7/17/2019 11:51AM +#endif + int Adjust_Capacity(int adjust, bool inanger=false); + int Power_Fraction(void) const; + int Tiberium_Fraction(void) {return (!Tiberium) ? 0 : Cardinal_To_Fixed(Capacity, Tiberium);}; + void Begin_Production(void) {IsStarted = true;}; + TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + void Adjust_Threat(int region, int threat); + + static void Init(void); + static void One_Time(void); + static HouseClass * As_Pointer(HousesType house); + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static void Read_Flag_INI(char *buffer); + static void Write_Flag_INI(char *buffer); + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** Special house actions. + */ +// void Init_Ion_Cannon(bool first_time, bool one_time_effect = false); +// void Init_Air_Strike(bool first_time, bool one_time_effect = false); +// void Init_Nuke_Bomb(bool first_time, bool one_time_effect = false); +// void Remove_Ion_Cannon(void); +// void Remove_Air_Strike(void); +// void Remove_Nuke_Bomb(void); + void Detach(TARGET target, bool all); + void Add_Nuke_Piece(int piece=-1); +// void Make_Air_Strike_Available(bool present, bool one_time_effect = false); + bool Has_Nuke_Device(void); + + + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + */ + void Check_Pertinent_Structures(void); + + void Init_Unit_Trackers(void); + void Free_Unit_Trackers(void); + +#ifdef USE_RA_AI + /* + ** AI Functions imported from RA + */ + //TechnoTypeClass const * Suggest_New_Object(RTTIType objectype, bool kennel=false) const; + BuildingTypeClass const * Suggest_New_Building(void) const; + void Recalc_Center(void); + //void Harvested(unsigned tiberium); + void Stole(unsigned worth); + //long Available_Money(void) const; + //void Spend_Money(unsigned money); + //void Refund_Money(unsigned money); + //void Attacked(void); + void Adjust_Power(int adjust); + void Adjust_Drain(int adjust); + //int Adjust_Capacity(int adjust, bool inanger=false); + //fixed Power_Fraction(void) const; + //fixed Tiberium_Fraction(void) const; + //void Begin_Production(void) {IsStarted = true;}; + //TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + //void Adjust_Threat(int region, int threat); + void Tracking_Remove(TechnoClass const * techno); + void Tracking_Add(TechnoClass const * techno); + void Active_Remove(TechnoClass const * techno); + void Active_Add(TechnoClass const * techno); + + UrgencyType Check_Attack(void) const; + UrgencyType Check_Build_Power(void) const; + UrgencyType Check_Build_Defense(void) const; + UrgencyType Check_Build_Offense(void) const; + UrgencyType Check_Build_Income(void) const; + UrgencyType Check_Fire_Sale(void) const; + UrgencyType Check_Build_Engineer(void) const; + UrgencyType Check_Raise_Money(void) const; + UrgencyType Check_Raise_Power(void) const; + UrgencyType Check_Lower_Power(void) const; + + bool AI_Attack(UrgencyType urgency); + bool AI_Build_Power(UrgencyType urgency) const; + bool AI_Build_Defense(UrgencyType urgency) const; + bool AI_Build_Offense(UrgencyType urgency) const; + bool AI_Build_Income(UrgencyType urgency) const; + bool AI_Fire_Sale(UrgencyType urgency); + bool AI_Build_Engineer(UrgencyType urgency) const; + bool AI_Raise_Money(UrgencyType urgency) const; + bool AI_Raise_Power(UrgencyType urgency) const; + bool AI_Lower_Power(UrgencyType urgency) const; + + bool Can_Make_Money(void) const { + return(Available_Money() > 300 || (BScan & STRUCTF_REFINERY)); + }; + + int Expert_AI(void); + COORDINATE Find_Build_Location(BuildingClass * building) const; + BuildingClass * Find_Building(StructType type, ZoneType zone=ZONE_NONE) const; + int AI_Building(void); + int AI_Unit(void); + int AI_Vessel(void); + int AI_Infantry(void); + int AI_Aircraft(void); + void Production_Begun(TechnoClass const * rtti); + int * Factory_Counter(RTTIType rtti); + int Factory_Count(RTTIType rtti) const; + CELL Where_To_Go(FootClass const * object) const; + CELL Zone_Cell(ZoneType zone) const; + ZoneType Which_Zone(COORDINATE coord) const; + ZoneType Which_Zone(ObjectClass const * object) const; + ZoneType Which_Zone(CELL cell) const; + DiffType Assign_Handicap(DiffType handicap); + TARGET Find_Juicy_Target(COORDINATE coord) const; + int Get_Quantity(AircraftType aircraft); + int Get_Quantity(StructType building); + FactoryClass * Fetch_Factory(RTTIType rtti) const; + void Set_Factory(RTTIType rtti, FactoryClass * factory); + bool Fire_Sale(void); + bool Is_Hack_Prevented(RTTIType rtti, int value) const; + bool Is_No_YakMig(void) const; + CELL Random_Cell_In_Zone(ZoneType zone) const; + static void Computer_Paranoid(void); + bool Is_Allowed_To_Ally(HousesType house) const; + void Do_All_To_Hunt(void) const; + void Super_Weapon_Handler(void); + CELL Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const; + + // Added so the ally flags could be sent to client machines - 09 / 12 / 2019 JAS + unsigned Get_Ally_Flags(); + + static void Recalc_Attributes(void); +#endif //USE_RA_AI + + + /* + ** This vector holds the recorded status of the map regions. It is through + ** this region information that team paths are calculated. + */ + RegionClass Regions[MAP_TOTAL_REGIONS]; + +#ifdef OBSOLETE + /* + ** This count down timer class handles decrements and then changes + ** the ion cannon state. If the Ion cannon was out of range it is + ** now in range. If the Ion cannon was in range it will toggle out + ** of range. + */ + TCountDownTimerClass IonControl; + int IonOldStage; + + TCountDownTimerClass AirControl; + int AirOldStage; + + TCountDownTimerClass NukeControl; + int NukeOldStage; +#endif + + + /* + ** This timer is for multiplayer mode; for a computer-controlled house, + ** it determines how long until this player "blitzes" the hapless humans. + */ + TCountDownTimerClass BlitzTime; + + /* + ** This count down timer class decrements and then changes + ** the Atomic Bomb state. + */ + CELL NukeDest; + + /* + ** Per-house credits class to track the visible credits state for each house. Redundant in the original game, but needed + ** to preserve the exact credits count behavior in the GlyphX client. ST - 10/16/2019 2:31PM + */ + CreditClass VisibleCredits; + + bool DebugUnlockBuildables; + + /* + ** This routine completely removes this house & all its objects from the game. + */ + void Clobber_All(void); + + /* + ** This routine blows up everything in this house. Fun! + */ + void Blowup_All(void); + + /* + ** This routine gets called in multiplayer games when every unit, building, + ** and infantry for a house is destroyed. + */ + void MPlayer_Defeated(void); + + /* + ** Screen shake timer. + */ + TCountDownTimerClass ScreenShakeTime; + + private: + void Silo_Redraw_Check(long oldtib, long oldcap); + + /* + ** This is a bit field record of all the other houses that are allies with + ** this house. It is presumed that any house that isn't an ally, is therefore + ** an enemy. A house is always considered allied with itself. + */ + unsigned Allies; + + /* + ** This is the standard delay time between announcements concerning the + ** state of the base or other intermittent house related events. + */ + enum SpeakDelayEnum { + SPEAK_DELAY=TICKS_PER_MINUTE*2, + TEAM_DELAY=TICKS_PER_MINUTE/10, + DAMAGE_DELAY=TICKS_PER_MINUTE + }; + + /* + ** General low-power related damaged is doled out whenever this timer + ** expires. + */ + TCountDownTimerClass DamageTime; + + /* + ** Team creation is done whenever this timer expires. + */ + TCountDownTimerClass TeamTime; + + /* + ** This controls the rate that the trigger time logic is processed. + */ + TCountDownTimerClass TriggerTime; + + /* + ** At various times, the computer may announce the player's condition. The following + ** variables are used as countdown timers so that these announcements are paced + ** far enough appart to reduce annoyance. + */ + TCountDownTimerClass SpeakAttackDelay; + TCountDownTimerClass SpeakPowerDelay; + TCountDownTimerClass SpeakMoneyDelay; + TCountDownTimerClass SpeakMaxedDelay; + + +#ifdef USE_RA_AI + + /*************************************************************************************** + ** + ** AI data imported from RA. ST - 7/17/2019 11:35AM + ** + ** + */ + + public: + /* + ** This records information about the location and size of + ** the base. + */ + COORDINATE Center; // Center of the base. + int Radius; // Average building distance from center (leptons). + struct { + int AirDefense; + int ArmorDefense; + int InfantryDefense; + } ZoneInfo[ZONE_COUNT]; + + /* + ** This records information about the last time a building of this + ** side was attacked. This information is used to determine proper + ** response. + */ + int LATime; // Time of attack. + RTTIType LAType; // Type of attacker. + ZoneType LAZone; // Last zone that was attacked. + HousesType LAEnemy; // Owner of attacker. + + /* + ** This target value is the building that must be captured as soon as possible. + ** Typically, this will be one of the buildings of this house that has been + ** captured and needs to be recaptured. + */ + TARGET ToCapture; + + /* + ** This value shows who is spying on this house's radar facilities. + ** This is used for the other side to be able to update their radar + ** map based on the cells that this house's units reveal. + */ + int RadarSpied; + + /* + ** Running score, based on units destroyed and units lost. + */ + int PointTotal; + + /* + ** This is the targeting directions for when this house gets a + ** special weapon. + */ + //QuarryType PreferredTarget; + + private: + /* + ** Tracks number of each building type owned by this house. Even if the + ** building is in construction, it will be reflected in this total. + */ + int BQuantity[STRUCT_COUNT]; + int UQuantity[UNIT_COUNT]; + int IQuantity[INFANTRY_COUNT]; + int AQuantity[AIRCRAFT_COUNT]; + + /* + ** This timer keeps track of when an all out attack should be performed. + ** When this timer expires, send most of this house's units in an + ** attack. + */ + /////CDTimerClass Attack; + TCountDownTimerClass Attack; + + public: + /* + ** This records the overriding enemy that the computer will try to + ** destroy. Typically, this is the last house to attack, but can be + ** influenced by nearness. + */ + HousesType Enemy; + + /* + ** The house expert system is regulated by this timer. Each computer controlled + ** house will process the Expert System AI at intermittent intervals. Not only will + ** this distribute the overhead more evenly, but will add variety to play. + */ + /////CDTimerClass AITimer; + TCountDownTimerClass AITimer; + + /* + ** For the moebius effect, this is a pointer to the unit that we + ** selected to teleport. Only one teleporter should be active per house. + */ + //TARGET UnitToTeleport; + + /* + ** This elaborates the suggested objects to construct. When the specified object + ** is constructed, then this corresponding value will be reset to nill state. The + ** expert system decides what should be produced, and then records the + ** recommendation in these variables. + */ + StructType BuildStructure; + UnitType BuildUnit; + InfantryType BuildInfantry; + AircraftType BuildAircraft; + //VesselType BuildVessel; + + /* + ** This records the current state of the base. This state is used to control + ** what action the base will perform and directly affects production and + ** unit disposition. The state will change according to time and combat + ** events. + */ + StateType State; + + /* + ** If automatic base building is on, then this flag will be set to true. + */ + unsigned IsBaseBuilding:1; + + /* + ** This flag is set to true when the house has determined that + ** there is insufficient Tiberium to keep the harvesters busy. + ** In such a case, the further refinery/harvester production + ** should cease. This is one of the first signs that the endgame + ** has begun. + */ + unsigned IsTiberiumShort:1; + + /* + ** If this computer controlled house has reason to be mad at humans, + ** then this flag will be true. Such a condition prevents alliances with + ** a human and encourages the computers players to ally amongst themselves. + */ + unsigned IsParanoid:1; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value is zero is presumed for human controlled houses. + */ + int IQ; + + /* + ** This is the handicap (difficulty level) assigned to this house. + */ + DiffType Difficulty; + + /* + ** This structure is used to record a build request as determined by + ** the house AI processing. Higher priority build requests take precidence. + */ + struct BuildChoiceClass { + static void * operator new(size_t, void * ptr) {return(ptr);}; + UrgencyType Urgency; // The urgency of the build request. + StructType Structure; // The type of building to produce. + + BuildChoiceClass(UrgencyType u, StructType s) : Urgency(u), Structure(s) {}; + int Save(FileClass &) const {return(true);}; + int Load(FileClass &) {return(true);}; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + + }; + + static TFixedIHeapClass BuildChoice; + +#endif // USE_RA_AI + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[256]; + +}; +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/IDATA.CPP b/TIBERIANDAWN/IDATA.CPP new file mode 100644 index 000000000..1853d23b3 --- /dev/null +++ b/TIBERIANDAWN/IDATA.CPP @@ -0,0 +1,2152 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\idata.cpv 2.12 02 Aug 1995 17:00:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : June 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * InfantryTypeClass::Get_Cameo_Data -- Fetches the small cameo shape for sidebar strip. * + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * InfantryTypeClass::Who_Can_Build_Me -- Determines what can build the infantry object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + + +/* + * There were too many parameters for the InfantryTypeClass constructor so I have + * created a table of Do Controls for each unit type and I am passing a pointer + * to the table to the constructor instead of passing each value as it was before. + * + * If this offends anyones C++ sensibilities then please feel free to implement a + * more elegant oop solution. + * + * Steve T. 10/3/95 10:13AM + * + */ + +// Minigunners + +int MiniGunnerDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 192, 1, 8, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 8, 8, // DO_FIRE_WEAPON + 128, 2, 2, // DO_LIE_DOWN + 144, 4, 4, // DO_CRAWL + 176, 2, 2, // DO_GET_UP + 192, 6, 8, // DO_FIRE_PRONE + 256, 16,0, // DO_IDLE1 + 272, 16,0, // DO_IDLE2 + 288, 13,0, // DO_ON_GUARD + 292, 10,0, // DO_FIGHT_READY + 301, 2, 0, // DO_PUNCH + 303, 6, 0, // DO_KICK + 309, 2, 0, // DO_PUNCH_HIT1 + 311, 4, 0, // DO_PUNCH_HIT2 + 315, 5, 0, // DO_PUNCH_DEATH + 319, 2, 0, // DO_KICK_HIT1 + 321, 4, 0, // DO_KICK_HIT2 + 325, 5, 0, // DO_KICK_DEATH + 330, 5, 0, // DO_READY_WEAPON + 382, 8, 0, // DO_GUN_DEATH + 398, 8, 0, // DO_EXPLOSION_DEATH + 398, 8, 0, // DO_EXPLOSION2_DEATH + 406, 12,0, // DO_GRENADE_DEATH + 418, 18,0, // DO_FIRE_DEATH + 436, 3, 3, // DO_GESTURE1 + 460, 3, 3, // DO_SALUTE1 + 484, 3, 3, // DO_GESTURE2 + 508, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + +static InfantryTypeClass const E1( + INFANTRY_E1, // Infantry type number. + TXT_E1, // Translate name number for infantry type. + "E1", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &MiniGunnerDos[0][0], // Ptr to minigunner 'DO' table above + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 1, // Sight range. + 100, // Cost of infantry (in credits). + 1, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_M16,WEAPON_NONE, + MPH_SLOW // Maximum speed of infantry. +); + + +// Grenadiers + + +int GrenadierDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 288, 1, 12, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 20,20, // DO_FIRE_WEAPON + 224, 2, 2, // DO_LIE_DOWN + 240, 4, 4, // DO_CRAWL + 272, 2, 2, // DO_GET_UP + 288, 8, 12, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + +static InfantryTypeClass const E2( + INFANTRY_E2, // Infantry type number. + TXT_E2, // Translate name number for infantry type. + "E2", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &GrenadierDos[0][0], // Ptr to grenadier DO table (above) + 14, // Frame of projectile launch. + 6, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 1, // Sight range. + 160, // Cost of infantry (in credits). + 3, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // Who can own this infantry unit. + WEAPON_GRENADE,WEAPON_NONE, + MPH_SLOW_ISH // Maximum speed of infantry. +); + + + + +// Bazooka + +int BazookaDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 192, 1,10, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 8, 8, // DO_FIRE_WEAPON + 128, 2, 2, // DO_LIE_DOWN + 144, 4, 4, // DO_CRAWL + 176, 2, 2, // DO_GET_UP + 192, 10,10, // DO_FIRE_PRONE + 272, 16,0, // DO_IDLE1 + 288, 16,0, // DO_IDLE2 + 304, 13,0, // DO_ON_GUARD + 308, 10,0, // DO_FIGHT_READY + 307, 2, 0, // DO_PUNCH + 319, 6, 0, // DO_KICK + 325, 2, 0, // DO_PUNCH_HIT1 + 327, 4, 0, // DO_PUNCH_HIT2 + 331, 4, 0, // DO_PUNCH_DEATH + 335, 2, 0, // DO_KICK_HIT1 + 337, 4, 0, // DO_KICK_HIT2 + 341, 5, 0, // DO_KICK_DEATH + 346, 5, 0, // DO_READY_WEAPON + 398, 8, 0, // DO_GUN_DEATH + 414, 8, 0, // DO_EXPLOSION_DEATH + 414, 8, 0, // DO_EXPLOSION2_DEATH + 422, 12,0, // DO_GRENADE_DEATH + 434, 18,0, // DO_FIRE_DEATH + 452, 3, 3, // DO_GESTURE1 + 476, 3, 3, // DO_SALUTE1 + 500, 3, 3, // DO_GESTURE2 + 524, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + + +static InfantryTypeClass const E3( + INFANTRY_E3, // Infantry type number. + TXT_E3, // Translate name number for infantry type. + "E3", // INI name for infantry. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &BazookaDos[0][0], // Ptr to DO table (above) + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 2, // Sight range. + 300, // Cost of infantry (in credits). + 3, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_DRAGON,WEAPON_NONE, + MPH_KINDA_SLOW // Maximum speed of infantry. +); + +// Flamethrower + +int FlamethrowerDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 256, 1,16, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 16,16, // DO_FIRE_WEAPON + 192, 2, 2, // DO_LIE_DOWN + 208, 4, 4, // DO_CRAWL + 240, 2, 2, // DO_GET_UP + 256, 16,16, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + + +static InfantryTypeClass const E4( + INFANTRY_E4, // Infantry type number. + TXT_E4, // Translate name number for infantry type. + "E4", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &FlamethrowerDos[0][0], // ptr to DO table (above) + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 70, // Strength of infantry (in damage points). + 1, // Sight range. + 200, // Cost of infantry (in credits). + 5, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_FLAMETHROWER,WEAPON_NONE, + MPH_SLOW_ISH +); + + +// Chemwarrior + +int ChemwarriorDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 256, 1,16, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 16,16, // DO_FIRE_WEAPON + 192, 2, 2, // DO_LIE_DOWN + 208, 4, 4, // DO_CRAWL + 240, 2, 2, // DO_GET_UP + 256, 16,16, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + +static InfantryTypeClass const E5( + INFANTRY_E5, // Infantry type number. + TXT_E5, // Translate name number for infantry type. + "E5", // INI name for infantry. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &ChemwarriorDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 70, // Strength of infantry (in damage points). + 1, // Sight range. + 300, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| +// HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_CHEMSPRAY,WEAPON_NONE, + MPH_SLOW +); + + +// Engineer + +int EngineerDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 82, 1, 4, // DO_PRONE + 16, 6, 6, // DO_WALK + 0, 0, 0, // DO_FIRE_WEAPON + 67, 2, 2, // DO_LIE_DOWN + 82, 4, 4, // DO_CRAWL + 114, 2, 2, // DO_GET_UP + 0, 0, 0, // DO_FIRE_PRONE + 130, 16,0, // DO_IDLE1 + 0, 0, 0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 146, 8, 0, // DO_GUN_DEATH + 154, 8, 0, // DO_EXPLOSION_DEATH + 162, 8, 0, // DO_EXPLOSION2_DEATH + 170, 12,0, // DO_GRENADE_DEATH + 182, 18,0, // DO_FIRE_DEATH + 200, 3, 3, // DO_GESTURE1 + 224, 3, 3, // DO_SALUTE1 + 200, 3, 3, // DO_GESTURE2 + 224, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + +static InfantryTypeClass const E7( + INFANTRY_E7, // Infantry type number. + TXT_E7, // Translate name number for infantry type. + "E6", // INI name for infantry. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + true, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &EngineerDos[0][0], // ptr to DO table + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 2, // Sight range. + 500, // Cost of infantry (in credits). + 2, // Scenario when they first appear. + 80,75, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW // Maximum speed of infantry. +); + +// Commandos + +int CommandoDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 160, 1, 4, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 4, 4, // DO_FIRE_WEAPON + 96, 2, 2, // DO_LIE_DOWN + 112, 4, 4, // DO_CRAWL + 144, 2, 2, // DO_GET_UP + 160, 4, 4, // DO_FIRE_PRONE + 192, 16,0, // DO_IDLE1 + 208, 16,0, // DO_IDLE2 + 224, 13,0, // DO_ON_GUARD + 228, 9, 0, // DO_FIGHT_READY + 237, 2, 0, // DO_PUNCH + 239, 6, 0, // DO_KICK + 245, 2, 0, // DO_PUNCH_HIT1 + 247, 4, 0, // DO_PUNCH_HIT2 + 251, 4, 0, // DO_PUNCH_DEATH + 255, 2, 0, // DO_KICK_HIT1 + 257, 4, 0, // DO_KICK_HIT2 + 261, 5, 0, // DO_KICK_DEATH + 266, 5, 0, // DO_READY_WEAPON + 318, 8, 0, // DO_GUN_DEATH + 334, 8, 0, // DO_EXPLOSION_DEATH + 334, 8, 0, // DO_EXPLOSION2_DEATH + 342, 12,0, // DO_GRENADE_DEATH + 354, 18,0, // DO_FIRE_DEATH + 372, 3, 3, // DO_GESTURE1 + 396, 3, 3, // DO_SALUTE1 + 420, 3, 3, // DO_GESTURE2 + 444, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A + +}; +static InfantryTypeClass const Commando( + INFANTRY_RAMBO, // Infantry type number. + TXT_RAMBO, // Translate name number for infantry type. + "RMBO", // INI name for infantry. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + true, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &CommandoDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 80, // Strength of infantry (in damage points). + 5, // Sight range. + 1000, // Cost of infantry (in credits). + 98, // Scenario when they first appear. + 80,75, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_RIFLE,WEAPON_NONE, + MPH_SLOW_ISH // Maximum speed of infantry. +); + +// Civilians + +int CivilianDos1[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C1( + INFANTRY_C1, // Infantry type number. + TXT_C1, // Translate name number for infantry type. + "C1", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &CivilianDos1[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos2[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C2( + INFANTRY_C2, // Infantry type number. + TXT_C2, // Translate name number for infantry type. + "C2", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos2[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos3[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH + +}; + +static InfantryTypeClass const C3( + INFANTRY_C3, // Infantry type number. + TXT_C3, // Translate name number for infantry type. + "C3", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos3[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos4[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH +}; + + +static InfantryTypeClass const C4( + INFANTRY_C4, // Infantry type number. + TXT_C4, // Translate name number for infantry type. + "C4", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos4[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos5[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C5( + INFANTRY_C5, // Infantry type number. + TXT_C5, // Translate name number for infantry type. + "C5", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos5[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos6[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C6( + INFANTRY_C6, // Infantry type number. + TXT_C6, // Translate name number for infantry type. + "C6", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos6[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos7[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C7( + INFANTRY_C7, // Infantry type number. + TXT_C7, // Translate name number for infantry type. + "C7", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &CivilianDos7[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + +int CivilianDos8[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C8( + INFANTRY_C8, // Infantry type number. + TXT_C8, // Translate name number for infantry type. + "C8", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos8[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos9[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C9( + INFANTRY_C9, // Infantry type number. + TXT_C9, // Translate name number for infantry type. + "C9", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos9[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int NikoombaDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH + +}; + +// Nikoomba +static InfantryTypeClass const C10( + INFANTRY_C10, // Infantry type number. + TXT_C10, // Translate name number for infantry type. + "C10", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &NikoombaDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + +int MoebiusDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 0, 0, 0, // DO_FIRE_PRONE + 104, 16,0, // DO_IDLE1 + 120, 20,0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 212, 8, 0, // DO_GUN_DEATH + 220, 8, 0, // DO_EXPLOSION_DEATH + 228, 12,0, // DO_EXPLOSION2_DEATH + 228, 12,0, // DO_GRENADE_DEATH + 240, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 0, 0, 0, // DO_PULL_GUN + 120, 31,0, // DO_PLEAD + 151, 14,0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const Moebius( + INFANTRY_MOEBIUS, // Infantry type number. + TXT_MOEBIUS, // Translate name number for infantry type. + "MOEBIUS", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &MoebiusDos[0][0], // ptr to DO table + 0, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + +int DelphiDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const Delphi( + INFANTRY_DELPHI, // Infantry type number. + TXT_DELPHI, // Translate name number for infantry type. + "DELPHI", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &DelphiDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,0, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int DrChanDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 0, 0, 0, // DO_FIRE_PRONE + 104, 16,0, // DO_IDLE1 + 120, 20,0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 212, 8, 0, // DO_GUN_DEATH + 220, 8, 0, // DO_EXPLOSION_DEATH + 228, 12,0, // DO_EXPLOSION2_DEATH + 228, 12,0, // DO_GRENADE_DEATH + 240, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 0, 0, 0, // DO_PULL_GUN + 120, 31,0, // DO_PLEAD + 151, 14,0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const DrChan( + INFANTRY_CHAN, // Infantry type number. + TXT_CHAN, // Translate name number for infantry type. + "CHAN", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &DrChanDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + + +/* +** This is the array of pointers to the static data associated with each +** infantry type. +*/ +InfantryTypeClass const * const InfantryTypeClass::Pointers[INFANTRY_COUNT] = { + &E1, + &E2, + &E3, + &E4, + &E5, +// &E6, + &E7, + &Commando, + &C1, + &C2, + &C3, + &C4, + &C5, + &C6, + &C7, + &C8, + &C9, + &C10, + &Moebius, + &Delphi, + &DrChan +}; + + +/*********************************************************************************************** + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * * + * This routine will construct the infantry type objects. It is use to create the static * + * infantry types that are used to give each of the infantry objects their characteristics. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryTypeClass::InfantryTypeClass ( + InfantryType type, int name, char const *ininame, + unsigned char level, long pre, + bool is_female, + bool is_leader, + bool is_crawling, + bool is_civilian, + bool is_nominal, + bool is_fraidycat, + bool is_capture, + bool is_theater, + int ammo, + int *do_table, + int firelaunch, int pronelaunch, + unsigned short strength, int sightrange, + int cost, int scenario, int risk, int reward, int ownable, + WeaponType primary, WeaponType secondary, + MPHType maxspeed) + : TechnoTypeClass(name, ininame, level, pre, + is_leader, true, is_nominal, false, false, true, true, true, true, false, false, + is_theater, false, false, false, true, false, + ammo, strength, maxspeed, sightrange, cost, + scenario, risk, reward, ownable, + primary, secondary, + ARMOR_NONE) +{ + IsFemale = is_female; + IsCrawling = is_crawling; + IsCapture = is_capture; + IsFraidyCat = is_fraidycat; + IsCivilian = is_civilian; + Type = type; + FireLaunch = firelaunch; + ProneLaunch = pronelaunch; + + /* + ** Set the animation sequence custom values. + */ + + for ( int i=0 ; iClass->House)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * * + * This routine is used by the scenario editor to create and place an infantry object onto * + * the map at the location specified. * + * * + * INPUT: cell -- The cell location to place the infantry object at. * + * * + * house -- The owner of the infantry object. * + * * + * OUTPUT: bool; Was the infantry object successfully created and placed at the location * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + InfantryClass * i = new InfantryClass(Type, house); + if (i) { + COORDINATE coord = Map[cell].Closest_Free_Spot(Cell_Coord(cell)); + if (coord) { + return(i->Unlimbo(coord, DIR_E)); + } else { + delete i; + } + } + return(false); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * * + * This routine will return with a cell offset occupation list for a generic infantry * + * object. This is typically just a single cell since infantry are never bigger than one * + * cell and this routine presumes the infantry is located in the center of the cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a cell offset list for the infantry object as if it were located * + * in the center of a cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + + return(&_list[0]); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * * + * This routine is used by the scenario editor to display a generic representation of the * + * infantry object for the scenario editor. It simply draws a single (nice profile) view * + * of the infantry type. * + * * + * INPUT: x,y -- The display coordinates to render the infantry object at. * + * * + * window -- The window that the display coordinates are relative to. * + * * + * house -- The house colors to use when rendering this infantry object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + if (house != HOUSE_NONE) { + + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = 2; + } + + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * * + * This routine will prepare the scenario editor so that the infantry objects appear on * + * the object list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Prep_For_Add(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + Map.Add_To_List(&As_Reference(index)); + } +} +#endif + + +/*********************************************************************************************** + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * * + * This routine is used to convert the infantry ASCII name as specified into an infantry * + * type number. This is called from the INI reader routine in the process if creating the * + * infantry objects needed for the scenario. * + * * + * INPUT: name -- The ASCII name to convert into an infantry type number. * + * * + * OUTPUT: Returns with the infantry type number that corresponds to the infantry ASCII name * + * specified. If no match could be found, then INFANTRY_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryType InfantryTypeClass::From_Name(char const *name) +{ + if (name) { + for (InfantryType classid = INFANTRY_FIRST; classid < INFANTRY_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(INFANTRY_NONE); +} + + +/*********************************************************************************************** + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * * + * This routine will perform one time processing for the infantry type system. This is * + * generally restricted to loading of the infantry shape data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::One_Time(void) +{ + InfantryType index; + + for (index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + InfantryTypeClass const *uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass->IniName, ".SHP"); + ((void const *&)uclass->ImageData) = MixFileClass::Retrieve(fullname); + + /* + ** The small build image icon sized shapes are always generic. + */ + char buffer[_MAX_FNAME]; + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%.4sICNH", uclass->IniName); + } else { + sprintf(buffer, "%.4sICON", uclass->IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass->CameoData) = MixFileClass::Retrieve(fullname); + } +} + + + + + + +/*********************************************************************************************** + * ITC::Init -- load up terrain set dependant sidebar icons * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/25/96 0:33AM ST : Created * + *=============================================================================================*/ + +void InfantryTypeClass::Init(TheaterType theater) +{ + if ( Get_Resolution_Factor() ) { + + if (theater != LastTheater){ + InfantryType index; + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + for (index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + InfantryTypeClass const *uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + ((void const *&)uclass->CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", uclass->IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass->CameoData) = cameo_ptr; + } + } + } + } +} + + + + + + + + + +/*********************************************************************************************** + * InfantryTypeClass::Who_Can_Build_Me -- Determines what can build the infantry object. * + * * + * Use this routine to determine what building can produce the infantry object. This is * + * typically used to maintain the construction list sidebar. * + * * + * INPUT: intheory -- If no regard is to be given to whether the construction building is * + * currently busy, then this flag should be true. In such a case, only * + * the existance of the building is sufficient to achieve success. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The house of the infantry to be produced. Only construction buildings * + * of the same house are considered. * + * * + * OUTPUT: Returns with a pointer to the object (building) that can produce the infantry * + * type. If there are no available buildings then the return value is NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * InfantryTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Mission != MISSION_DECONSTRUCTION && + building->Class->ToBuild == RTTI_INFANTRYTYPE && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + anybuilding = building; + if (building->IsLeader) return(building); + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name text number for this infantry type. It examines * + * the special custom name flag to determine whether the custom name or the generic name * + * is to be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with text number for the name to give this infantry type object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryTypeClass::Full_Name(void) const +{ + if (Debug_Map || !IsNominal || Special.IsNamed || Type == INFANTRY_C10 || Type == INFANTRY_DELPHI || Type == INFANTRY_MOEBIUS) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN); +} \ No newline at end of file diff --git a/TIBERIANDAWN/INFANTRY.CPP b/TIBERIANDAWN/INFANTRY.CPP new file mode 100644 index 000000000..1db1082fc --- /dev/null +++ b/TIBERIANDAWN/INFANTRY.CPP @@ -0,0 +1,3303 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\infantry.cpv 2.19 16 Oct 1995 16:50:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * InfantryClass::As_Target -- Converts the infantry unit into a target value. * + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * InfantryClass::Assign_Mission -- Make sure he's out of boxing mode first * + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * InfantryClass::Draw_It -- Draws a unit object. * + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * InfantryClass::Init -- Initialize the infantry object system. * + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * InfantryClass::Look -- The infantry performs a look operation. * + * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing * + * InfantryClass::Receive_Message -- Process radio messages * + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * InfantryClass::Validate -- validates infantry pointer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; + +int Infantry_Kick_Damage[] = {10,15}; +int Infantry_Punch_Damage[] = { 4, 7}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * InfantryClass::VTable; + + +/*************************************************************************** +** This is the array of constant data associated with infantry maneuvers. It +** specifies the frame rate as well as if the animation can be aborted. +*/ +// interruptable, mobile, randomstart, rate +DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { + {true, false, false, 0}, // DO_STAND_READY + {true, false, false, 0}, // DO_STAND_GUARD + {true, false, false, 0}, // DO_PRONE + {true, true, true, 2}, // DO_WALK + {true, false, false, 1}, // DO_FIRE_WEAPON + {false, true, false, 2}, // DO_LIE_DOWN + {true, true, true, 2}, // DO_CRAWL + {false, false, false, 3}, // DO_GET_UP + {true, false, false, 1}, // DO_FIRE_PRONE + {true, false, false, 2}, // DO_IDLE1 + {true, false, false, 2}, // DO_IDLE2 + {false, false, false, 2}, // DO_ON_GUARD + {true, false, false, 2}, // DO_FIGHT_READY + {false, false, false, 2}, // DO_PUNCH + {false, false, false, 2}, // DO_KICK + {false, false, false, 2}, // DO_PUNCH_HIT1 + {false, false, false, 2}, // DO_PUNCH_HIT2 + {false, false, false, 1}, // DO_PUNCH_DEATH + {false, false, false, 2}, // DO_KICK_HIT1 + {false, false, false, 2}, // DO_KICK_HIT2 + {false, false, false, 1}, // DO_KICK_DEATH + {false, false, false, 2}, // DO_READY_WEAPON + {false, false, false, 2}, // DO_GUN_DEATH + {false, false, false, 2}, // DO_EXPLOSION_DEATH + {false, false, false, 2}, // DO_EXPLOSION2_DEATH + {false, false, false, 2}, // DO_GRENADE_DEATH + {false, false, false, 2}, // DO_FIRE_DEATH + {false, false, false, 2}, // DO_GESTURE1 + {false, false, false, 2}, // DO_SALUTE1 + {false, false, false, 2}, // DO_GESTURE2 + {false, false, false, 2}, // DO_SALUTE2 + {true, false, false, 2}, // DO_PULL_GUN + {true, false, false, 2}, // DO_PLEAD + {true, false, false, 2}, // DO_PLEAD_DEATH +}; + + +/*********************************************************************************************** + * InfantryClass::Validate -- validates infantry pointer. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int InfantryClass::Validate(void) const +{ + int num; + + num = Infantry.ID(this); + if (num < 0 || num >= INFANTRY_MAX) { + Validate_Error("INFANTRY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * * + * This routine is used by the debug version to display pertinent information about the * + * infantry unit. * + * * + * INPUT: mono -- The monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0);mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³Is Prone......³ ³ ³ \n" + "³Is A Loner....³ ³ ³ \n" + "³Deploying.....³ ³ ³ \n" + "³Rotating......³ ³ ³ \n" + "³Firing........³ ³ ³ \n" + "³Driving.......³ ³ ³ \n" + "³To Look.......³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Text_Print("X", 16 + (IsProne?2:0), 10); + mono->Set_Cursor(33, 7);mono->Printf("%2d", Fear); + mono->Set_Cursor(41, 7);mono->Printf("%2d", Doing); + FootClass::Debug_Dump(mono); +} +#endif + +InfantryClass::InfantryClass(void) : Class(0) {}; // Default constructor does nothing. + + +/*********************************************************************************************** + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * * + * This is the constructor used when creating an infantry unit. All values are required * + * except for facing and position. If these are absent, then the infantry is created in * + * a state of limbo -- not placed upon the map. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass::InfantryClass(InfantryType classid, HousesType house) : + Class(&InfantryTypeClass::As_Reference(classid)), + FootClass(house) +{ + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Doing = DO_NOTHING; + Fear = 0; // Starts completely brave. + IsProne = false; + IsStoked = false; + IsBoxing = false; + IsTechnician = false; + Strength = Class->MaxStrength; + + /* + ** Civilians carry much less ammo than soldiers do. + */ + Ammo = Class->MaxAmmo; + + /* + ** Keep count of the number of units created. Dont track civilians. + */ + if (!Class->IsCivilian && GameToPlay == GAME_INTERNET){ + House->InfantryTotals->Increment_Unit_Total((int)classid); + } + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Add(this); +#endif // USE_RA_AI + + StopDriverFrame = -1; +} + + +/*********************************************************************************************** + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * * + * This is the default destructor for infantry type units. It will put the infantry into * + * a limbo state if it isn't already in that state and the game is still active. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +InfantryClass::~InfantryClass(void) +{ + if (GameActive && Class) { +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Remove(this); +#endif //USE_RA_AI + Limbo(); + } + if (GameActive && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * * + * This will allocate an infantry object from the infantry object free pool. If there is * + * no available slot, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * + * allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * InfantryClass::operator new(size_t) +{ + void * ptr = Infantry.Allocate(); + if (ptr) { + ((InfantryClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * * + * This routine is used return an infantry object back to the system. * + * * + * INPUT: ptr -- Pointer to the infantry object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::operator delete(void *ptr) +{ + if (ptr) { + ((InfantryClass *)ptr)->IsActive = false; + } + Infantry.Free((InfantryClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * * + * This routine applies the damage specified to the infantry object. It is possible that * + * this routine will DESTROY the infantry unit in the process. * + * * + * INPUT: damage -- The damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The warhead type that is inflicting the damage. * + * * + * source -- Who is responsible for inflicting the damage. * + * * + * OUTPUT: bool; Was the infantry unit destroyed by this damage? * + * * + * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * + * for this in the code that follows the call to Take_Damage(). * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 03/31/1995 JLB : Revenge factor. * + *=============================================================================================*/ +ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + IsFiring = false; + + /* + ** Prone infantry take only half damage, but never below one damage point. + */ + if (IsProne && damage) { + damage >>= 1; +// damage = MAX(damage, 1); + } + + +//Mono_Printf("Infantry Take_Damage(%d, %d, %d, %p)\r", damage, distance, warhead, source); +//Get_Key(); + + res = FootClass::Take_Damage(damage, distance, warhead, source); + + /* + ** Flame thrower guys take more damage because of the exposed pilot light + ** on their flame gun. + */ + if (damage && res != RESULT_DESTROYED && *this == INFANTRY_E4) { + damage = 5; + ResultType newres = FootClass::Take_Damage(damage, distance, warhead, source); + res = MAX(res, newres); + } + + if (res == RESULT_NONE) return(res); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + Stop_Driver(); + Stun(); + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + /* + ** Flame thrower infantry always go out with a bang. + */ + if (*this == INFANTRY_E4) { + new AnimClass(ANIM_NAPALM1, Coord); + Explosion_Damage(Coord, 80, 0, WARHEAD_FIRE); + } + + if (*this == INFANTRY_E2) { + new AnimClass(ANIM_ART_EXP1, Coord); + Explosion_Damage(Coord, 30, 0, WARHEAD_HE); + } + + if (*this == INFANTRY_E5) { + new AnimClass(ANIM_CHEM_BALL, Coord); + Explosion_Damage(Coord, 80, 0, WARHEAD_HE); + } + + VocType sound; + VocType altsound; + if (*this == INFANTRY_RAMBO) { +// if (Sim_Random_Pick(0, 3) != 1) { + sound = VOC_RAMBO_YELL; +// } else { +// sound = VOC_RAMBO_OHSH; +// } + altsound = sound; + } else { + sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM5); + altsound = VOC_YELL1; + } + + /* + ** The type of warhead determines the animation the infantry + ** will perform when killed. + */ + switch (warhead) { + case WARHEAD_FEEDME: + if (source) { + source->Strength += 30; + if (source->Strength > source->Class_Of().MaxStrength) { + source->Strength = source->Class_Of().MaxStrength; + } + } + // Fall thru to WARHEAD_SA: + + case WARHEAD_HEADBUTT: + case WARHEAD_SPORE: + case WARHEAD_HOLLOW_POINT: + case WARHEAD_SA: + Sound_Effect(sound, Coord); + Do_Action(DO_GUN_DEATH, true); + break; + + case WARHEAD_HE: + Sound_Effect(sound, Coord); + Do_Action(DO_EXPLOSION_DEATH, true); + break; + + case WARHEAD_AP: + Sound_Effect(sound, Coord); + Do_Action(DO_GRENADE_DEATH, true); + break; + + case WARHEAD_PB: + case WARHEAD_LASER: + case WARHEAD_FIRE: + Sound_Effect(altsound, Coord); + Do_Action(DO_FIRE_DEATH, true); + break; + + case WARHEAD_FIST: + Sound_Effect(sound, Coord); + Do_Action(DO_PUNCH_DEATH,true); + break; + + case WARHEAD_FOOT: + Sound_Effect(sound, Coord); + Do_Action(DO_KICK_DEATH,true); + break; + } + + return(res); + } + + /* + ** When infantry gets hit, it gets scared. + */ + if (res != RESULT_DESTROYED) { + COORDINATE c4 = (source) ? source->Coord : NULL; + if (source) { + Scatter(c4); + } + +#ifdef BOXING + if (IsBoxing) { + int addval = 0; + + switch (warhead) { + case WARHEAD_FIST: + if (damage == Infantry_Punch_Damage[1]) addval++; + Do_Action( (DoType) ( (int)DO_PUNCH_HIT1 + addval),true); + break; + + case WARHEAD_FOOT: + if (damage == Infantry_Kick_Damage[1]) addval++; + Do_Action( (DoType) ( (int)DO_KICK_HIT1 + addval),true); + break; + } + } else { +#endif + if (source && Fear < FEAR_SCARED) { + if (Class->IsFraidyCat) { + Fear = FEAR_PANIC; + } else { + Fear = FEAR_SCARED; + } + } else { + int morefear = FEAR_ANXIOUS; + if (Health_Ratio() > 0x0080) morefear /= 4; + Fear = MIN((int)Fear + morefear, FEAR_MAXIMUM); + } +#ifdef BOXING + } +#endif + } + return(res); +} + + +/*********************************************************************************************** + * InfantryClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Converted to infantry support. * + *=============================================================================================*/ +void InfantryClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Working shape file pointer. + int facing = Facing_To_32(PrimaryFacing.Current()); + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + + y += 4; + x -= 2; + + /* + ** Fetch the basic body shape pointer. This requires taking into account + ** the current animation stage. + */ + int shapenum; + int facenum; + + shapenum = 0; + facenum = HumanShape[facing]; + + /* + ** Fetch the shape pointer to use for the infantry. This is controlled by what + ** choreograph sequence the infantry is performing, it's facing, and whether it + ** is prone. + */ + DoType doit = Doing; + if (doit == DO_NOTHING) doit = DO_STAND_READY; + + /* + ** Hold the walk pose for a couple of frames after we come to a stop to try and avoid the problem where a moving infantry + ** goes into the stand pose for a single frame when pausing in the assigned cell destination. ST - 9/4/2019 1:39PM + */ + if (doit == DO_STAND_READY) { + if (window == WINDOW_VIRTUAL) { + if (StopDriverFrame != -1) { + if (Frame - StopDriverFrame <= 2) { + if (Path[0] != FACING_NONE) { + doit = DO_WALK; + } + } + } + } + } + + shapenum = Class->DoControls[doit].Count; + shapenum = Fetch_Stage() % MAX(shapenum, 1); + if (Class->DoControls[doit].Jump) { + shapenum += facenum * Class->DoControls[doit].Jump; + } + shapenum += Class->DoControls[doit].Frame; + +#ifdef BOXING +// BG hack to get him to face right when he's supposed to. + if (IsBoxing && BodyFacing<128) shapenum += 47; +#endif + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window); +// CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, House->Remap_Table(IsBlushing, true), Map.UnitShadow); + + FootClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * * + * This routine will handle any special operations that need to be performed once each * + * cell travelled. This includes radioing a transport that is is now clear and the * + * transport is free to leave. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 03/01/1995 JLB : Capture building options. * + * 05/31/1995 JLB : Capture is always successful now. * + *=============================================================================================*/ +void InfantryClass::Per_Cell_Process(bool center) +{ + Validate(); + CellClass *cellptr = &Map[Coord_Cell(Coord)]; + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** capture, then capture it. + */ + if (center && Mission == MISSION_CAPTURE) { + TechnoClass * tech = cellptr->Cell_Techno(); + if (tech && tech->As_Target() == NavCom) { + tech->Captured(House); + Delete_This(); + return; + } else { +//#ifdef NEVER + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + if (Map[Coord_Cell(Coord)].Cell_Building()) { + Scatter(0, true); + } + } +//#endif + } + } + + /* + ** Infantry entering a transport vehicle will break radio contact + ** at attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (center && Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + return; + } + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** sabotage, then sabotage it. + */ + if (center && Mission == MISSION_SABOTAGE) { + BuildingClass *building = cellptr->Cell_Building(); + if (building && building->As_Target() == NavCom) { + int temp = Special.IsScatter; + + building->IsGoingToBlow = true; + building->Clicked_As_Target(PlayerPtr->Class->House, 20); // 2019/09/20 JAS - Added record of who clicked on the object + building->Clicked_As_Target(building->Owner(), 20); + building->CountDown.Set(20); + building->WhomToRepay = As_Target(); + Special.IsScatter = true; + NavCom = TARGET_NONE; + Do_Uncloak(); + Arm = Rearm_Delay(true); + Scatter(building->Center_Coord(), true); // RUN AWAY! + Special.IsScatter = temp; + return; + } + } + + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (center && IsTethered) { + Transmit_Message(RADIO_UNLOADED); + if (House->Class->House == HOUSE_GOOD) { + Do_Action(DO_GESTURE1); + } else { + Do_Action(DO_GESTURE2); + } + + /* + ** Rambo types give a gung-ho comment when unloaded. + */ + if (*this == INFANTRY_RAMBO) { + Sound_Effect(VOC_RAMBO_ROCK, Coord); + } + + /* + ** If the cell is now full of infantry, tell them all to scatter + ** in order to make room for more. + */ + if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { + cellptr->Incoming(0, true); + } + } + + /* + ** When the infantry reaches the center of the cell, it may begin a new mission. + */ + if (center) { + Commence(); + } + + Look(true); + FootClass::Per_Cell_Process(center); + + /* + ** If over Tiberium, then this infantry unit will take damage. + */ + if (IsActive && !IsInLimbo && center && cellptr->Land_Type() == LAND_TIBERIUM && *this != INFANTRY_E5) { + int damage = 2; + Take_Damage(damage, 0, WARHEAD_FIRE); + } +} + + +/*********************************************************************************************** + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * * + * This is a support routine that removes the target specified from any targeting or * + * navigation computers. When a target is destroyed or removed from the game system, * + * the target must be removed from any tracking systems of the other units. This routine * + * handles removal for infantry units. * + * * + * INPUT: target -- The target to remove from the infantry unit's tracking systems. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Detach(TARGET target, bool all) +{ + Validate(); + if (TarCom == target) { + IsFiring = false; + } + FootClass::Detach(target, all); +} + + +/*********************************************************************************************** + * InfantryClass::As_Target -- Converts the infantry unit into a target value. * + * * + * This support routine is used to convert the infantry object (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry unit as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET InfantryClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_INFANTRY, Infantry.ID(this))); +} + + +/*********************************************************************************************** + * InfantryClass::Init -- Initialize the infantry object system. * + * * + * This routine will force the infantry object system into its empty initial state. It * + * is called when the scenario needs to be cleared in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Init(void) +{ + InfantryClass *ptr; + + Infantry.Free_All(); + + ptr = new InfantryClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * InfantryClass::Look -- The infantry performs a look operation. * + * * + * This routine will cause the infantry unit to "look". For player owned infantry, this * + * causes the dark shroud to be pushed back. * + * * + * INPUT: incremental -- If it is known that the infantry performed a look in the last cell * + * it was in AND it has only moved one cell, then setting this * + * parameter to true will perform a faster "incremental" look * + * operation. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a relatively slow routine. Call ONLY when necessary. * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Look(bool incremental) +{ + Validate(); + int sight; // Number of cells to sight. + + if (!IsInLimbo) { + //if (IsOwnedByPlayer) { // Changed for multiple player mapping. ST - 3/6/2019 1:27PM + if (House->IsHuman) { + sight = Class->SightRange; + + if (sight) { + Map.Sight_From(House, Coord_Cell(Coord), sight, incremental); // Passed our house into Map.Sight_From since it now needs to know who it is performing the action on behalf of. ST - 3/28/2019 2:55PM + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * * + * This routine updates the infantry's navigation computer so that the infantry will * + * travel to the destination target specified. * + * * + * INPUT: target -- The target to have the infantry unit move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Destination(TARGET target) +{ + Validate(); + /* + ** Special flag so that infantry will start heading in the right direction immediately. + */ + if (Target_Legal(target)) { + Stop_Driver(); + } + + /* + ** When telling an infantry soldier to move to a location twice, then this + ** means that movement is more important than safety. Get up and run! + */ + if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat) { + Do_Action(DO_GET_UP); + } + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(target); + + if (techno) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { + +// TCTCTC -- call for an update from the transport to get a good rondezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + Assign_Mission(MISSION_MOVE); + } + } else { + Assign_Mission(MISSION_MOVE); + } + } + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + FootClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * * + * This routine will update the infantry's targeting computer so that it will try to * + * attack the target specified. This might result in it moving to be within range and thus * + * also cause adjustment of the navigation computer. * + * * + * INPUT: target -- The target that this infantry should attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 06/30/1995 JLB : Tries to capture target if possible. * + *=============================================================================================*/ +void InfantryClass::Assign_Target(TARGET target) +{ + Validate(); + Path[0] = FACING_NONE; + FootClass::Assign_Target(target); + + /* + ** If this is an infantry that can only capture, then also assign its destination to the + ** target specified. + */ + if (!Target_Legal(NavCom) && Class->IsCapture && Class->Primary == WEAPON_NONE) { + BuildingClass const * building = As_Building(target); + if (building && building->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *building != STRUCT_EYE && Scenario < 13)) { + Assign_Destination(target); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * * + * This routine is used to handle the non-graphic AI processing the infantry requires. * + * Call this routine ONCE per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::AI(void) +{ + Validate(); + FootClass::AI(); + + if (IsUnloading) Mark(MARK_CHANGE); + + /* + ** Special hack to make sure that if this infantry is in firing animation, but the + ** stage class isn't set, then abort the firing flag. + */ + if (IsFiring && !Fetch_Rate()) { + IsFiring = false; + } + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + Delete_This(); + return; + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { + Commence(); + } + + /* + ** After a time, the infantry will gain courage. + */ + if (Fear) { + + /* + ** Nikumba is really a coward at heart. He never becomes un-afraid. + */ + if (*this != INFANTRY_C10) { + Fear--; + + /* + ** When an armed civilian becomes unafraid, he will then reload + ** another clip into his pistol. + */ + if (Fear == 0 && Ammo == 0 && Class->Primary != WEAPON_NONE) { + Ammo = Class->MaxAmmo; + } + } + + /* + ** Stand up if brave and lie down if afraid. + */ + if (IsProne) { + if (Fear < FEAR_ANXIOUS) { + Do_Action(DO_GET_UP); + } + } else { + + /* + ** Drop to the ground if anxious. Don't drop to the ground while moving + ** and the special elite flag is active. + */ + if (Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving) || !Special.IsDefenderAdvantage)) { + Do_Action(DO_LIE_DOWN); + } + } + } + + /* + ** When is darkness or in doubt, + ** run in circles, scream, and shout. + */ + if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsDriving && !Target_Legal(NavCom)) { + Scatter(true); + } + + /* + ** Special victory dance action. + */ + if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment.Expired()) { + IsStoked = false; + Do_Action((Random_Pick(0, 1) == 0) ? DO_GESTURE1 : DO_GESTURE2); + if (*this == INFANTRY_RAMBO) { + VocType _response[] = { + VOC_RAMBO_LEFTY, + VOC_RAMBO_LAUGH, + VOC_RAMBO_COMIN, + VOC_RAMBO_TUFF + }; + Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); + } + } + + /* + ** Determine if this infantry unit should fire off an + ** attack or not. + */ + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: +#ifdef BOXING + ObjectClass *object = As_Object(TarCom); + + if (object) { + /* If we're engaged in hand-to-hand combat, keep boxing */ + if (IsBoxing) { + IsFiring = true; + if (((InfantryClass *)object)->Doing == DO_FIGHT_READY) { + Do_Action((DoType) ((int)DO_PUNCH + (DoType)(Random_Pick(0, 1) == 1)),true); + } + } else { + + if (Is_Target_Infantry(TarCom) && (Distance(TarCom)<=0x80) && (Coord_Y(Coord) == Coord_Y(object->Coord))) { + + // Too close to shoot, so start hand-to-hand combat + if (Establish_Contact((TechnoClass *)object)) { + if (Transmit_Message(RADIO_PREPARE_TO_BOX) == RADIO_ROGER) { + IsBoxing = true; + Do_Action(DO_ON_GUARD,true); + } + } + } else { +#endif + + /* + ** Start firing animation. + */ + if (IsProne) { + Do_Action(DO_FIRE_PRONE); + IsFiring = true; + } else { + Do_Action(DO_FIRE_WEAPON); + IsFiring = true; + } +#ifdef BOXING + } +#endif + + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + + /* + ** If the target is in range, and the NavCom is the same, then just + ** stop and keep firing. + */ + if (TarCom == NavCom) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } +#ifdef BOXING + } + } +#endif + break; + } + + /* + ** If in the middle of firing animation, then only + ** process that. Infantry cannot fire and move simultaneously. + ** At some point in the firing animation process, a projectile + ** will be launched. When the required animation frames have + ** been completed, the firing animation stops. + */ + int firestage = Class->FireLaunch; + if (IsProne) firestage = Class->ProneLaunch; + +#ifdef BOXING + if (IsBoxing) { + firestage = 1; + if (Doing == DO_KICK) firestage = 2; + } +#endif + + if (IsFiring && Fetch_Stage() == firestage) { + Fire_At(TarCom, 0); + + if (Class->Primary == WEAPON_GRENADE) { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + } + + /* + ** Handle the completion of the animation sequence. + */ + + if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { + switch (Doing) { + default: + if (IsDriving) { + if (IsProne) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } else { + if (IsProne) { + Do_Action(DO_PRONE, true); + } else { + Do_Action(DO_STAND_READY, true); + } + } + break; + +#ifdef BOXING + case DO_FIGHT_READY: + case DO_ON_GUARD: + case DO_PUNCH: + case DO_KICK: + case DO_PUNCH_HIT1: + case DO_PUNCH_HIT2: + case DO_KICK_HIT1: + case DO_KICK_HIT2: + if (In_Radio_Contact()) { + Do_Action(DO_FIGHT_READY, true); + } else { + IsBoxing = false; + Do_Action(DO_READY_WEAPON); + } + break; +#endif + + /* + ** When death is due to hand-to-hand combat, use the gunfire death + ** decay animation since there is no custom animation available - yet. + */ + case DO_PUNCH_DEATH: + case DO_KICK_DEATH: + // Fall into next case. + + case DO_GUN_DEATH: + case DO_EXPLOSION_DEATH: + case DO_EXPLOSION2_DEATH: + case DO_GRENADE_DEATH: + case DO_FIRE_DEATH: + Delete_This(); + return; + } + } + + /* + ** Perform movement operations at this time. + */ + if (!IsFiring /*&& !IsBoxing*/) { + if (!IsDriving) { + + /* + ** When in guard mode, never allow a valid navcom. + */ + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { + Assign_Destination(TARGET_NONE); +// if (IsTethered) Scatter(0, true); + } + + /* + ** A head to coordinate is needed. If there is no path + ** available, then create one. + */ + if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { + + /* + ** Determine if the next cell in the list is available + ** to be entered. If not, then abort the path and try + ** again. + */ + if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { + Path[0] = FACING_NONE; + } + + /* + ** Check to see if the target is closer than expected. This occurs + ** when heading toward a moving object and that object is heading + ** toward the unit. Shorten the precalculated path to be no longer + ** than the distance to the target. + */ + int d = Lepton_To_Cell(Distance(NavCom)); + if (d < CONQUER_PATH_MAX) { + Path[d] = FACING_NONE; + } + + /* + ** Find a path to follow if one isn't already calculated. + */ + if (Path[0] == FACING_NONE) { + + /* + ** Calculate the path from the current location to the + ** destination indicated by the navigation computer. If there + ** was a fundamental error with finding a path, then this + ** indicates that basic path & movement logic needs to be + ** aborted. + */ + if (!PathDelay.Expired()) { + return; + } + if (!Basic_Path()) { +//Mono_Printf("Infantry Basic_Path is failing.\n");Get_Key(); + if (Distance(NavCom) < 0x0280 && !IsTethered) { + Assign_Destination(TARGET_NONE); + } else { + if (TryTryAgain) { + TryTryAgain--; + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + //If we're trying to enter a transport we need to fail so others can try to enter. - LLL 4/17/2020 + if (Mission == MISSION_ENTER) { + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + Transmit_Message(RADIO_OVER_OUT); + } + } + } + Stop_Driver(); + return; + } + TryTryAgain = PATH_RETRY; + } + + /* + ** Determine the coordinate to head to based on the infantry's + ** current location and the next location in the path. + */ + COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); + CELL acell = Coord_Cell(acoord); + + if (Can_Enter_Cell(acell) != MOVE_OK) { + + if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered && House->IsHuman && Distance(NavCom) < 0x0200) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { + if (Map[acell].Cell_Object()) { + if (!House->Is_Ally(Map[acell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); + } + } + } + + Path[0] = FACING_NONE; + Stop_Driver(); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + } else { + if (Start_Driver(acoord)) { + PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); + Set_Speed(0xFF); + if (IsProne) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } + } + } + + } else { + + /* + ** The infantry knows where it should be headed, so head there. Check + ** to see if the infantry is "close enough" to the desired location that + ** it should just consider itself to have arrived. In this case, force + ** the infantry to the destination location and mark this path step + ** as complete. + */ + Mark(MARK_UP); + if (Distance(Head_To_Coord()) < 0x0010) { + + memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); + Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; + Coord = Head_To_Coord(); + Stop_Driver(); + Per_Cell_Process(true); + + if (!IsActive || IsInLimbo) return; + + if (Coord_Cell(Coord) == As_Cell(NavCom)) { + NavCom = TARGET_NONE; + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + //Stop_Driver(); + Path[0] = FACING_NONE; + } + } else { + int movespeed = Speed; + + /* + ** When prone, the infantry moves at half speed or double + ** speed. This depends on whether the infantry actually has + ** prone animation stages. Civilians don't, and so they + ** run instead. + */ + if (IsProne) { + if (Class->IsFraidyCat && !Class->IsCrawling) { + movespeed = Speed*2; + } else { + movespeed = Speed/2; + } + } + + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + + /* + ** Advance the infantry as far as it should go. + */ + MPHType maxspeed = MPHType(min((unsigned)(Class->MaxSpeed * House->GroundspeedBias), MPH_LIGHT_SPEED)); + Coord = Coord_Move(Coord, Direction(Head_To_Coord()), Fixed_To_Cardinal(maxspeed, movespeed)); + } + Mark(MARK_DOWN); + } + IsNewNavCom = false; + } +} + + +#ifdef NEVER +/*************************************************************************** + * InfantryClass::Blocking_Object -- Determines how a object blocks an inf * + * * + * INPUT: TechnoClass * pointer to object that is blocking unit * + * CELL the cell the unit is being blocked in * + * * + * OUTPUT: MoveBitType the way that the object is blocking the unit * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +MoveBitType InfantryClass::Blocking_Object(TechnoClass const *techno, CELL cell) const +{ + Validate(); + bool inf = (techno->What_Am_I() == RTTI_INFANTRY); + bool unit = (techno->What_Am_I() == RTTI_UNIT) || inf; + + CellClass const * cellptr = &Map[cell]; + + if (House->Is_Ally(techno)) { + + /* + ** Logic to handle a trasport type object. + */ + if (NavCom == techno->As_Target() && Contact_With_Whom() == techno) { + return(MOVEF_OK); + } + + /* + ** If the object is of type infantry, then the square is blocked only + ** if the cell is completely full of infantry. + */ + if (inf && ((cellptr->Flag.Composite & 0x1f) != 0x1f)) { + return(MOVEF_OK); + } + + if (unit) { + + /* + ** If the unit in question has a destination than we should + ** be prepared to wait for the unit to get out of our way. + */ + if (((FootClass *)techno)->NavCom != TARGET_NONE) { + return(MOVEF_MOVING_BLOCK); + } + return(MOVEF_TEMP); + } + } else { + + /* + ** If its an enemy unit, things are dealt with a little differently + */ + if (unit) { + + + /* + ** If the object is cloaked, then consider it passable for findpath purposes, + ** but not so for all other cases. + */ + if (techno->Cloak == CLOAKED) { + if (IsFindPath) return(MOVEF_OK); + return(MOVEF_CLOAK); + } + + /* + ** If our vehicle is weapon equipped, then report that the cell occupier + ** needs only to be destroyed in order to make the cell passable. + */ + if (Class->Primary != WEAPON_NONE) { + return(MOVEF_DESTROYABLE); + } + } + } + return(MOVEF_NO); +} +#endif + + + +/*********************************************************************************************** + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * * + * This routine is used to examine the cell specified and determine if the infantry is * + * allowed to enter it. It is used by the path finding algorithm. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the type of blockage in the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + /* + ** If we are moving into an illegal cell, then we can't do that. + */ + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** If moving off the edge of the map, then consider that an illegal move. + */ + if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) { + return(MOVE_NO); + } + CellClass * cellptr = &Map[cell]; + + /* + ** Walls are considered impassable for infantry UNLESS the wall has a hole + ** in it. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (otype.IsCrate && !House->IsHuman) { + return(MOVE_NO); + } + + if (otype.IsWall) { + if ((cellptr->OverlayData / 16) != otype.DamageLevels) { + return(MOVE_NO); + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + MoveType retval = MOVE_OK; + ObjectClass *obj = cellptr->Cell_Occupier(); + while (obj) { + + if (obj != this) { + + /* + ** Special case check so that a landed aircraft that is in radio contact, will not block + ** a capture attempt. It is presumed that this case happens when a helicopter is landed + ** at a helipad. + */ + if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT) { + if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { + return(MOVE_OK); + } + } + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Allied objects block movement using different rules than for enemy + ** objects. + */ + if (House->Is_Ally(obj)) { + switch (obj->What_Am_I()) { + + /* + ** A unit blocks as either a moving blockage or a stationary temp blockage. + ** This depends on whether the unit is currently moving or not. + */ + case RTTI_UNIT: + if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + break; + + /* + ** Aircraft and buildings always block movement. If for some reason there is an + ** allied terrain object, that blocks movement as well. + */ + case RTTI_TERRAIN: + case RTTI_AIRCRAFT: + case RTTI_BUILDING: + return(MOVE_NO); + + default: + break; + } + + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** Any non-allied blockage is considered impassible if the infantry + ** is not equipped with a weapon. + */ + if (Class->Primary == WEAPON_NONE) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the infantry is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + if (((TerrainClass *)obj)->Class->IsFlammable && + BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If foot soldiers cannot travel on the cell -- consider it impassible. + */ + if (retval == MOVE_OK && !IsTethered && !Ground[cellptr->Land_Type()].Cost[SPEED_FOOT]) { + return(MOVE_NO); + } + + /* + ** if a unit has the cell reserved then we just can't go in there. + */ + if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { + return(MOVE_NO); + } + + /* + ** if a block of infantry has the cell reserved then there are two + ** possibilities... + */ + if (cellptr->InfType != HOUSE_NONE) { + if (House->Is_Ally(cellptr->InfType)) { + if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } + } else { + if (Class->Primary != WEAPON_NONE) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + + /* + ** If it is still ok to move the infantry, then perform the last check + ** to see if the cell is already full of infantry. + */ + if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { + return(MOVE_NO); + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * * + * This is a rendering support routine that will return a pointer to a list of cell offsets * + * that specify the cells the infantry unit is currently overlapping (graphic wise) but * + * is not considered to occupy. This list is used to update the map display. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * + * occupy. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryClass::Overlap_List(void) const +{ + Validate(); + //return(Coord_Spillage_List(Coord, 24 + ((IsSelected || Doing > DO_WALK)?12:0))); + return(Coord_Spillage_List(Coord, 24 + ((Doing > DO_WALK || Is_Selected_By_Player())?12:0))); +// return(Coord_Spillage_List(Coord, (IsSelected ? 24 : 14))+1); +} + + +/*********************************************************************************************** + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * * + * Determines if the infantry unit can fire on the target. If it can't fire, then the * + * reason why is returned. * + * * + * INPUT: target -- The target to determine if the infantry can fire upon. * + * * + * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * + * can't, why not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 06/27/1995 JLB : Flame thrower can fire while prone now. * + *=============================================================================================*/ +FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + +#ifdef BOXING + /* + ** If in hand-to-hand, and we're currently playing a got-hit animation, + ** then we can't punch back yet. + */ + if (IsBoxing) { + if ( (Doing>=DO_PUNCH_HIT1 && Doing<=DO_KICK_DEATH) || (Doing == DO_ON_GUARD) ) return FIRE_BUSY; + if (Arm) return(FIRE_BUSY); // don't let fire if still re-arming + } +#endif + + /* + ** Don't allow firing if the turret is not ready. + */ + if (IsFiring) return(FIRE_REARM); + +#ifdef OBSOLETE + if (weapon->Fires == BULLET_FLAME && IsProne) return(FIRE_ILLEGAL); +#endif + + /* + ** The target must still be legal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((IsDriving && Special.IsDefenderAdvantage) || (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { + return(FIRE_MOVING); + } + + /* + ** If we're moving, but not facing the right direction, then exit. + */ + if (!Special.IsDefenderAdvantage && IsDriving) { + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) >= 32) { + return(FIRE_MOVING); + } + } + + return(FootClass::Can_Fire(target, which)); +} + + +/*********************************************************************************************** + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * * + * Use this routine when the infantry unit as accomplished its task and needs to find * + * something to do. The default behavior is to enter some idle state such as guarding. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Enter_Idle_Mode(bool ) +{ + Validate(); + MissionType order; + + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + } else { + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + if (GameToPlay == GAME_NORMAL || House->IsHuman) { + order = MISSION_GUARD; + } else { +#ifndef USE_RA_AI + order = MISSION_HUNT; + +#else + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + // This applies only to non-human houses in a non-normal game type + // + + order = MISSION_GUARD; + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) { + return; + } + + if (!Team) { + if (House->IQ >= Rule.IQGuardArea) { + if (Is_Weapon_Equipped()) { + order = MISSION_GUARD_AREA; + } + } + } +#endif + + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * * + * This routine is the random animator initiator for infantry units. This routine should * + * be called regularly. On occasion, it will cause the infantry to go into an idle * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 12/13/1994 JLB : Does random facing change. * + * 07/02/1995 JLB : Nikoomba special effects. * + *=============================================================================================*/ +void InfantryClass::Random_Animate(void) +{ + Validate(); + if (!IsDriving && !IsProne && (Doing == DO_STAND_GUARD || Doing == DO_STAND_READY) && !IsFiring) { + + /* + ** Scared infantry will always follow the golden rule of civilians; + ** "When in darkness or in doubt, run in circles, scream, and shout!" + */ + if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { + Scatter(NULL, true); + return; + } + + /* + ** If Nikoomba is not scared, then he will be doing his thing with random animations. + */ + if (*this == INFANTRY_C10) { + switch (Random_Pick(0, 3)) { + case 0: + Do_Action(DO_IDLE2); + break; + + default: + break; + } + } + + switch (Random_Picky((int)0, (int)55, (char*)NULL, (int)0)) { + case 10: + Do_Action(DO_SALUTE1); + break; + + case 11: + Do_Action(DO_SALUTE2); + break; + + case 12: + Do_Action(DO_GESTURE1); + break; + + case 13: + Do_Action(DO_GESTURE2); + break; + + case 4: + case 3: + case 0: + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE); + break; + + case 1: + Do_Action(DO_IDLE1); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + if (Sim_Random_Pick(1,20) == 1 && !Is_Selected_By_Player() && *this == INFANTRY_MOEBIUS && IsDiscoveredByPlayer) { + static VocType _response[] = { +// VOC_EXCELLENT1, +// VOC_EXCELLENT2, + VOC_EXCELLENT3, +// VOC_EXCELLENT4, +// VOC_EXCELLENT5, + VOC_QUIP1, +// VOC_QUIP2 + }; + Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); + } + break; + + case 2: + Do_Action(DO_IDLE2); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + if (!Is_Selected_By_Player() && IsOwnedByPlayer && *this == INFANTRY_RAMBO && Sim_Random_Pick(0, 2) == 0) { + Sound_Effect(VOC_RAMBO_CMON, Coord); + } + break; + + /* + ** On occasion, civilian types will wander about. + */ + case 5: + case 6: + case 7: + case 8: + case 9: + if (!House->IsHuman && Class->IsFraidyCat) { + Scatter(NULL, true); + } + break; + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * * + * This routine is used when the infantry should scatter to a nearby cell. Scattering * + * occurs as an occasional consequence of being fired upon. It is one of the features * + * that makes infantry so "charming". * + * * + * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * + * scatter. If the threat isn't from a particular direction, then this * + * parameter will be NULL. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/12/1994 JLB : Flame thrower infantry always scatter. * + *=============================================================================================*/ +void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + Validate(); + + /* + ** A unit that is in the process of going somewhere will never scatter. + */ + if (IsDriving || Target_Legal(NavCom)) forced = false; + + /* + ** If the infantry is currently engaged in legitimate combat, then don't + ** scatter unless forced to. + */ + if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; + + /* + ** Don't scatter if performing an action that can't be interrupted. + */ + if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; + + /* + ** For human players, don't scatter the infantry, if the special + ** flag has not been enabled that allows infantry scatter. + */ + if (!Special.IsScatter && !nokidding && House->IsHuman && !forced && !Team) return; + + if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + (Random_Pick(0, 4)-2); + } else { + COORDINATE coord = Coord & 0x00FF00FFL; + + if (coord != 0x00800080L) { + toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + toface = toface + (Random_Pick(0, 4)-2); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(newcell)); + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * * + * This starts the infantry into a choreographed animation sequence. These sequences can * + * be as simple as standing up or lying down, but can also be complex, such as dying or * + * performing some idle animation. * + * * + * INPUT: todo -- The choreographed sequence to start. * + * * + * force -- Force starting this animation even if the current animation is flagged * + * as uninterruptible. This is necessary for death animations. * + * * + * OUTPUT: bool; Was the animation started? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Do_Action(DoType todo, bool force) +{ + Validate(); + if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { + Mark(MARK_CHANGE); + //Mark(MARK_OVERLAP_UP); + Doing = todo; + //Mark(MARK_OVERLAP_DOWN); + if (todo == DO_IDLE1 || todo == DO_IDLE2) { + Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); + } else { + Set_Rate(MasterDoControls[Doing].Rate); + } + Set_Stage(0); + + /* + ** Kludge to make sure that if infantry is in the dying animation, it isn't still + ** moving as well. + */ + if (!Strength) { + Stop_Driver(); + } + + /* + ** Since the animation sequence might be interrupted. Set any flags + ** necessary so that if interrupted, the affect on the infantry is + ** still accomplished. + */ + switch (todo) { + case DO_LIE_DOWN: + IsProne = true; + break; + + case DO_GET_UP: + IsProne = false; + break; + + case DO_READY_WEAPON: + IsBoxing = false; + break; + + case DO_ON_GUARD: + IsBoxing = true; + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + Path[0] = FACING_NONE; + break; + + default: + break; + } + + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * * + * This is used to stop the infantry from animating in movement. This function will stop * + * the infantry moving and revert it to either a prone or standing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Stop_Driver(void) +{ + Validate(); + if (Head_To_Coord()) { + + /* + ** Remove the "reservation" bit in the destination location. + */ + Clear_Occupy_Bit(Head_To_Coord()); + } + + /* + ** Set the occupation bit at the current location. + */ + Set_Occupy_Bit(Coord); + + if (Doing != DO_STAND_READY) { + StopDriverFrame = Frame; + } + + if (IsProne) { + Do_Action(DO_PRONE); + } else { + Do_Action(DO_STAND_READY); + } + + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * * + * Use this routine to being the infantry moving toward the destination specified. The * + * destination is first checked to see if there is a free spot available. Then the infantry * + * reserves that spot and begins movement toward it. * + * * + * INPUT: headto -- The coordinate location desired for the infantry to head to. * + * * + * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * + * the specified destination could not contain the infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + * 05/14/1995 JLB : Tries to move to closest spot possible. * + * 05/15/1995 JLB : Uses closest spot if moving onto transport. * + *=============================================================================================*/ +bool InfantryClass::Start_Driver(COORDINATE & headto) +{ + Validate(); + COORDINATE old = headto; + + /* + ** Convert the head to coordinate to a legal sub-position location. + */ + headto = Map[Coord_Cell(headto)].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); + if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { + headto = Map[Coord_Cell(old)].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); + } + + /* + ** If the infantry started moving, then fixup the occupation bits. + */ + if (headto && FootClass::Start_Driver(headto)) { + /* + ** Remove the occupation bit from the infantry's current location. + */ + Clear_Occupy_Bit(Coord); + + /* + ** Set the occupation bit for the new headto location. + */ + Set_Occupy_Bit(headto); + return(true); + } + + return(false); +} + + +#ifdef NEVER +/*********************************************************************************************** + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantly * + * * + * This routine ensures that when the infantry primary facing is changes, it is changed * + * instantly and always. There is no provision for infantry facing changing slowly over * + * time as the other vehicles usually do. * + * * + * INPUT: facing -- The desired facing for the infantry unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Primary_Facing(DirType facing, bool ) +{ + Validate(); + FootClass::Set_Primary_Facing(facing, true); +} +#endif + + +/*********************************************************************************************** + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * * + * This routine will clean up the infantry occupation bits (as necessary) as well as stop * + * the infantry movement process when it gets limboed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + Stop_Driver(); + + Clear_Occupy_Bit(Coord); + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * * + * Use this routine when the infantry unit wishes to fire a projectile. This routine * + * will launch the projectile and perform any other necessary infantry specific operations. * + * * + * INPUT: target -- The target of the attack. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * + * NULL is returned. If there is already the maximum bullet objects in play, then * + * this could happen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * InfantryClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet = NULL; + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + IsFiring = false; + +#ifdef BOXING + if (IsBoxing) { + RadioMessageType hitaction = (Doing == DO_KICK) ? RADIO_KICK : RADIO_PUNCH; + + /* + ** When fighting, verify that the target is legal to proceed. If there is some + ** error, then abort fightning mode. Otherwise, tell the target that it has + ** just been hit. + */ + if (In_Radio_Contact() && Target_Legal(target) && Transmit_Message(hitaction) == RADIO_ROGER) { + Arm = Rearm_Delay(true); + } else { + + /* + ** Fighting done for some reason, so pick up gun + */ + IsBoxing = false; + Do_Action(DO_READY_WEAPON,true); + } + } else { +#endif + + bullet = FootClass::Fire_At(target, which); + if (bullet) { + + /* + ** For fraidycat infantry that run out of ammo, always go into + ** a maximum fear state at that time. + */ + if (Class->IsFraidyCat && !Ammo) { + Fear = FEAR_MAXIMUM; + if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { + Assign_Mission(MISSION_GUARD); + } + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + Sound_Effect(weapon->Sound, Coord); + } + +#ifdef BOXING + } +#endif + return(bullet); +} + + +/*********************************************************************************************** + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * * + * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * + * ensure that the coordinate is a legal subposition. * + * * + * INPUT: coord -- The coordinate to unimbo the infantry at. * + * * + * facing -- The desired initial facing for the infantry unit. * + * * + * strength -- The desired initial strength for the infantry unit. * + * * + * mission -- The desired initial mission for the infantry unit. * + * * + * OUTPUT: bool; Was the infantry unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) +{ + Validate(); + /* + ** Make sure that the infantry start in a legal position on the map. + */ + coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, ScenarioInit); + if (coord == NULL) { + return(false); + } + + if (FootClass::Unlimbo(coord, facing)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->IScan |= (1L << Class->Type); + House->ActiveIScan |= (1L << Class->Type); + + /* + ** If there is no sight range, then this object isn't discovered by the player unless + ** it actually appears in a cell mapped by the player. + */ + if (Class->SightRange == 0) { + IsDiscoveredByPlayer = false; + } + + Set_Occupy_Bit(coord); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * * + * This routine intercepts the Greatest_Threat request and adds the appropriate target * + * types to search for. For regular infantry, this consists of all the ground types. For * + * rocket launching infantry, this also includes aircraft. * + * * + * INPUT: threat -- The basic threat control value. * + * * + * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * + * target could be found, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET InfantryClass::Greatest_Threat(ThreatType threat) const +{ + Validate(); + /* + ** Engineers consider only buildings that can be captures as being a threat. All others + ** are ignored. + */ + if (Class->IsCapture && Class->Primary == WEAPON_NONE) { + threat = threat | THREAT_CAPTURE; + } + + switch (Class->Primary) { + case WEAPON_NONE: + if (*this != INFANTRY_E7) { + return(TARGET_NONE); + } + // fall into next case. + + // + // ^ + // | Original comment above. + // + // Crash here because INFANTRY_E7 has no primary weapon and WEAPON_NONE == -1, so we will reference a -ve index into the Weapons array. + // Assume original intent was to fall out of the switch and call the base class Greatest_Threat, so adding a break here. + // ST - 4/24/2019 11:01AM + // + break; // Added. ST - 4/24/2019 11:02AM + + /* + ** Dragon missile equiped soldiers are also assumed to carry a Stinger missile. As such, + ** they will consider aircraft a legal target. + */ + default: + if (BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + break; + + /* + ** The sniper rifle equipped soldier doesn't go hunting for targets + ** unless specifically in hunt mode. + */ + case WEAPON_RIFLE: + if (House->IsHuman && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { + return(TARGET_NONE); + } + return(TechnoClass::Greatest_Threat(threat | THREAT_INFANTRY|THREAT_BUILDINGS)); + } + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * * + * This routine handles playing an audio response as a result of the player selecting the * + * infantry unit. This occurs prior to giving it an order and may not be followed by any * + * order at all. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Select(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_YEA, + VOC_RAMBO_YES, + VOC_RAMBO_YO + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (*this == INFANTRY_MOEBIUS) { + static VocType _response[] = { + VOC_YES, + VOC_COMMANDER, + VOC_HELLO, + VOC_HMMM + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsFemale) { + response = VOC_GIRL_YEAH; + } else { + response = VOC_GUY_YEAH; + } + } + } else { + static VocType _response[] = { + VOC_ACKNOWL, + VOC_REPORT, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_READY, + VOC_AWAIT + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * * + * When the infantry is given the order to move, this routine handles the audio repsonse * + * generated by the infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Move(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_UGOTIT, + VOC_RAMBO_ONIT, + VOC_RAMBO_NOPROB + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (*this == INFANTRY_MOEBIUS) { + static VocType _response[] = { + VOC_OF_COURSE, + VOC_YESYES + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + } + } else { + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ROGER, + VOC_RIGHT_AWAY, + VOC_UGOTIT, + VOC_AFFIRM, + VOC_AFFIRM + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * * + * When the player gives an infantry unit the order to attack, this routine handles * + * the audio response by that unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Attack(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_NOPROB, + VOC_RAMBO_UGOTIT, + VOC_RAMBO_NOPROB, + VOC_RAMBO_ONIT + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + } else { + static VocType _response[] = { + VOC_RIGHT_AWAY, + VOC_AFFIRM, + VOC_AFFIRM, + VOC_UGOTIT, + VOC_NO_PROB, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * + * * + * This routine will return with the point of origin for any firing projectiles. Typically, * + * this only includes the rocket launcher and the grenade thrower. The other infantry * + * have either invisible projectiles or special animations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate where the projectile will appear as it is fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE InfantryClass::Fire_Coord(int) const +{ + Validate(); + if (Class->Type == INFANTRY_E4) { + return(Coord); // special case for flame thrower guy + } else { + return(Coord_Add(Coord, XYP_COORD(0, -5))); + } +} + + +/*************************************************************************** + * InfantryClass::Receive_Message -- Process radio messages * + * * + * If the infantry's boxing, it needs to return to a normal state when * + * his opponent moves away. Otherwise fall thru to FootClass processing* + * * + * INPUT: from - Pointer to the originator of this message. * + * * + * message - the message to process * + * * + * param -- Reference to an optional parameter that can be * + * used to transfer more information than is * + * possible with the simple radio message values. * + * * + * OUTPUT: an appropriate response message * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/19/1995 BWG : Created. * + * 05/14/1995 JLB : Handles loading maneuver messages. * + *=========================================================================*/ +RadioMessageType InfantryClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + int damage; + + switch (message) { + + case RADIO_OVER_OUT: +#ifdef BOXING + if (IsBoxing) Do_Action(DO_READY_WEAPON); +#endif + break; + + /* + ** Request a fisticuff fight. If this infantry is already involved in a fight, + ** then refuse the offer. + */ + case RADIO_PREPARE_TO_BOX: +#ifdef BOXING + if (IsBoxing) break; +#endif + if (Contact_With_Whom() == from) { + Do_Action(DO_ON_GUARD, true); + Assign_Target(Contact_With_Whom()->As_Target()); + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Just received a kick! Take some damage. + */ + case RADIO_KICK: + damage = Infantry_Kick_Damage[Random_Pick(0, (int)(sizeof(Infantry_Kick_Damage) / sizeof(Infantry_Kick_Damage[0])))]; + if (Take_Damage(damage, 0, WARHEAD_FOOT, this) == RESULT_DESTROYED) return(RADIO_STATIC); + return(RADIO_ROGER); + + /* + ** Just recieved a punch! Take some damage. + */ + case RADIO_PUNCH: + damage = Infantry_Punch_Damage[Random_Pick(0, (int)(sizeof(Infantry_Punch_Damage) / sizeof(Infantry_Punch_Damage[0])))]; + if (Take_Damage(damage, 0, WARHEAD_FIST, this) == RESULT_DESTROYED) return(RADIO_STATIC); + return(RADIO_ROGER); + + } + return(FootClass::Receive_Message(from, message, param)); +} + + +/*************************************************************************** + * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing* + * * + * If the infantry's in a boxing mode, return an appropriate re-arming * + * delay. Otherwise return the default return val. * + * * + * INPUT: second -- bool; see TechnoClass... * + * * + * OUTPUT: Returns with the # of game frames to delay before shooting * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 BWG : Created. * + *=========================================================================*/ +int InfantryClass::Rearm_Delay(bool second) const +{ + Validate(); +#ifdef BOXING + if (IsBoxing) { + return(Random_Pick(5, 50)); + } +#endif + return(FootClass::Rearm_Delay(second)); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Mission -- Assign mission to infantry object. * + * * + * When a new mission is assigned, make sure he gets out of boxing mode. * + * * + * INPUT: order -- The new mission to assign to the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Mission(MissionType order) +{ + Validate(); + if (order == MISSION_SABOTAGE) { + Sound_Effect(VOC_RAMBO_PRESENT, Coord); + } + + IsBoxing = false; + FootClass::Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * * + * This routine checks to see if the infantry unit can capture the specified object rather * + * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * + * * + * INPUT: object -- The object that the mouse is currently over. * + * * + * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = FootClass::What_Action(object); + + /* + ** First see if it's a commando, and if he's attacking a building, have him return ACTION_SABOTAGE instead + */ + if (*this == INFANTRY_RAMBO && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { + return(ACTION_SABOTAGE); + } + + /* + ** There is no self-select action available for infantry types. + */ + if (action == ACTION_SELF) { + action = ACTION_NONE; + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + Is_Owned_By_Player() && // Changed for multiplayer. ST - 3/13/2019 5:37PM + //IsOwnedByPlayer && + object->Is_Techno() && + //IsOwnedByPlayer && + ((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { +// if (object->Owner() == Owner() && object->What_Am_I() == RTTI_UNIT && ((UnitClass *)object)->Class->IsTransporter && ((UnitClass *)object)->How_Many() < 5) { + action = ACTION_ENTER; + } + + if (Class->IsCapture && action == ACTION_ATTACK) { + if (object->Owner() != Owner() && + ((object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || + (object->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)object)->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *((BuildingClass *)object) != STRUCT_EYE || Scenario < 13) )) + ) { + + action = ACTION_CAPTURE; + } else { + if (Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + } + } + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Read_INI(char *buffer) +{ + InfantryClass *infantry; // Working infantry pointer. + char *tbuffer; // Accumulation buffer of infantry IDs. + HousesType inhouse; // Infantry house. + InfantryType classid; // Infantry class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read the entire INFANTRY INI section into HIDBUF + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + + /* + ** Get an infantry entry + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** 1st token: house name. + */ + inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); + if (inhouse != HOUSE_NONE) { + + /* + ** 2nd token: infantry type name. + */ + classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); + + if (classid != INFANTRY_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + infantry = new InfantryClass(classid, inhouse); + if (infantry) { + + /* + ** 3rd token: strength. + */ + int strength = atoi(strtok(NULL, ",\n\r")); + + /* + ** 4th token: cell #. + */ + COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\n\r"))); + + /* + ** 5th token: cell sub-location. + */ + coord = Coord_Add(coord & 0xFF00FF00L, StoppingCoordAbs[atoi(strtok(NULL, ","))]); + + /* + ** Fetch the mission and facing. + */ + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + DirType dir = (DirType)atoi(strtok(NULL,",\n\r")); + infantry->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\n\r")); + if (infantry->Trigger) { + infantry->Trigger->AttachCount++; + } + + if (infantry->Unlimbo(coord, dir)) { + infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); + if (GameToPlay == GAME_NORMAL || infantry->House->IsHuman) { + infantry->Assign_Mission(mission); + infantry->Commence(); + } else { + infantry->Enter_Idle_Mode(); + } + } else { + + /* + ** If the infantry could not be unlimboed, then this is a big error. + ** Delete the infantry. + */ + delete infantry; + } + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * + * * + * This routine writes all of the infantry in the game out to an INI file. This is used * + * in the scenario editor when the game needs to be saved. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- A pointer to the loaded INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of infantry IDs. + + /* + ** First, clear out all existing infantry data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the infantry data out. + */ + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry; + + infantry = Infantry.Ptr(index); + if (!infantry->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", + infantry->House->Class->IniName, + infantry->Class->IniName, + infantry->Health_Ratio(), + Coord_Cell(infantry->Coord), + CellClass::Spot_Index(infantry->Coord), + MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? + infantry->MissionQueue : infantry->Mission), + infantry->PrimaryFacing.Current(), + infantry->Trigger ? infantry->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * * + * This routine is called when the player clicks over an object while this infantry soldier * + * is selected. Capture attempts are prohibited if the infantry cannot capture. The * + * command might respond if told to sabotage something. * + * * + * INPUT: action -- The action that is nominally to be performed. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (What_Action(object) != action) { + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + action = ACTION_NONE; + break; + } + } + + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * + * * + * When the infantry soldier is responsible for a kill, this routine is called. It checks * + * to see if the soldier should make some comment or perform some action. The commando * + * infantry is most likely to respond. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of kills this infantry soldier has made. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Made_A_Kill(void) +{ + Validate(); + if (*this == INFANTRY_RAMBO || Random_Pick(0, 5) < Kills) { + IsStoked = true; + Comment = TICKS_PER_SECOND*2; + } + return(FootClass::Made_A_Kill()); +} + + +/*********************************************************************************************** + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * * + * INPUT: CELL - the cell we are setting the bit in * + * * + * int - the spot index we are setting the bit for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) +{ + Validate(); + /* + ** Set the occupy postion for the spot that we passed in + */ + Map[cell].Flag.Composite |= (1 << spot_index); + + /* + ** Record the type of infantry that now owns the cell + */ + Map[cell].InfType = Owner(); +} + + +/*************************************************************************** + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) +{ + Validate(); + /* + ** Clear the occupy bit for the infantry in that cell + */ + Map[cell].Flag.Composite &= ~(1 << spot_index); + + /* + ** If he was the last infantry recorded in the cell then + ** remove the infantry ownership flag. + */ + if (!(Map[cell].Flag.Composite & 0x1F)) { + Map[cell].InfType = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * * + * This routine will return with the full name (as a text number) for this infantry * + * unit. Typically, this is the normal name, but in cases of civilian type survivors from * + * a building explosion, it might be a technician instead. In such a case, the special * + * technician name number is returned instead. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name to use for this infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Full_Name(void) const +{ + Validate(); + if (IsTechnician) { + return(TXT_TECHNICIAN); + } + return(Class->Full_Name()); +} + + +/*********************************************************************************************** + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * * + * This routine intercepts the normal attack mission and if an engineer is detected and the * + * target is a building, then the engineer will be automatically assigned the capture * + * mission. In other cases, the normal attack logic will proceed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Mission_Attack(void) +{ + Validate(); + if (*this == INFANTRY_E7 && As_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + return(1); + } + return(FootClass::Mission_Attack()); +} + + +RTTIType InfantryClass::What_Am_I(void) const +{ + Validate(); + return(RTTI_INFANTRY); +} + +ActionType InfantryClass::What_Action(CELL cell) const +{ + Validate(); + return FootClass::What_Action(cell); +} + +ObjectTypeClass const & InfantryClass::Class_Of(void) const +{ + Validate(); + return(*Class); +} + +bool InfantryClass::Is_Infantry(void) const +{ + Validate(); + return(true); +} \ No newline at end of file diff --git a/TIBERIANDAWN/INFANTRY.H b/TIBERIANDAWN/INFANTRY.H new file mode 100644 index 000000000..225d9a752 --- /dev/null +++ b/TIBERIANDAWN/INFANTRY.H @@ -0,0 +1,251 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\infantry.h_v 2.18 16 Oct 1995 16:48:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INFANTRY_H +#define INFANTRY_H + +/********************************************************************** +** Infantry can be afraid. These defines are for the various infantry +** fear levels. When infantry be come scared enough they take cover and +** even run away in panic. +*/ +#define FEAR_ANXIOUS 10 // Something makes them scared. +#define FEAR_SCARED 100 // Scared enough to take cover. +#define FEAR_PANIC 200 // Run away! Run away! +#define FEAR_MAXIMUM 255 // Scared to death. + + +class InfantryClass : public FootClass +{ + public: + InfantryTypeClass const * const Class; + operator InfantryType(void) const {return Class->Type;}; + + /* + ** If the infantry is undergoing some choreographed animation sequence, then + ** this holds the particular sequence number. The frame of animation is kept + ** track of by the regular frame tracking system. When performing an animation + ** sequence, the infantry cannot perform anything else (even move). + */ + DoType Doing; + + /* + ** Certain infantry will either perform some comment or say something after an + ** amount of time has expired subsiquent to an significant event. This is the + ** timer the counts down. + */ + TCountDownTimerClass Comment; + + /* + ** If this civilian is actually a technician, then this flag will be true. + ** It should only be set for the civilian type infantry. Typically, the + ** technician appears after a building is destroyed. + */ + unsigned IsTechnician:1; + + /* + ** If the infantry just performed some feat, then it may respond with an action. + ** This flag will be true if an action is to be performed when the Comment timer + ** has expired. + */ + unsigned IsStoked:1; + + /* + ** This flag indicates if the infantry unit is prone. Prone infantry become that way + ** when they are fired upon. Infantry in the prone position are less vulnerable to + ** combat. + */ + unsigned IsProne:1; + + /* + ** This flag is set when the infantryman is engaged in hand-to-hand + ** combat. By setting this flag, it'll play the put-down-the-gun + ** sequence only once, and it'll know to pick up the gun when the + ** fight is over. + */ + unsigned IsBoxing:1; + + /* + ** Track when movement last stopped. + */ + long StopDriverFrame; + + /* + ** The fear rating of this infantry unit. The more afraid the infantry, the more + ** likely it is to panic and seek cover. + */ + unsigned char Fear; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + InfantryClass(void); + InfantryClass(InfantryType classid, HousesType house); + virtual ~InfantryClass(void); + virtual RTTIType What_Am_I(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Destination(TARGET); + + /* + ** Query functions. + */ + virtual bool Is_Infantry(void) const; + virtual ObjectTypeClass const & Class_Of(void) const; + virtual int Full_Name(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Fire_Coord(int which) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType facing); + virtual bool Limbo(void); + virtual void Detach(TARGET target, bool all); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Look(bool incremental=false); + + /* + ** User I/O. + */ + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + + /* + ** Combat related. + */ + virtual int Made_A_Kill(void); + virtual ActionType What_Action(ObjectClass * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual void Assign_Mission(MissionType order); + virtual BulletClass * Fire_At(TARGET target, int which); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual void Assign_Target(TARGET); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual int Rearm_Delay(bool second) const; + void Set_Occupy_Bit(COORDINATE coord) {Set_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Set_Occupy_Bit(CELL cell, int spot_index); + void Clear_Occupy_Bit(COORDINATE coord) {Clear_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Clear_Occupy_Bit(CELL cell, int spot_index); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual void AI(void); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual int Mission_Attack(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "INFANTRY";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Movement and animation. + */ + virtual bool Do_Action(DoType todo, bool force=false); + virtual void Random_Animate(void); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + virtual void Per_Cell_Process(bool center); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Scatter(COORDINATE threat, bool forced =false, bool nokidding =false); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** Translation table to convert facing into infantry shape number. This special + ** table is needed since several facing stages are reused and flipped about the Y + ** axis. + */ + static int const HumanShape[32]; + + private: + + static DoStruct const MasterDoControls[DO_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/INI.CPP b/TIBERIANDAWN/INI.CPP new file mode 100644 index 000000000..25d95dcab --- /dev/null +++ b/TIBERIANDAWN/INI.CPP @@ -0,0 +1,2326 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ini.cpv 2.18 16 Oct 1995 16:48:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 30, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Houses -- Assigns multiplayer houses to various players * + * Clear_Flag_Spots -- Clears flag overlays off the map * + * Clip_Move -- moves in given direction from given cell; clips to map * + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * Furthest_Cell -- Finds cell furthest from a group of cells * + * Place_Flags -- Places flags for multiplayer games * + * Read_Scenario_Ini -- Read specified scenario INI file. * + * Remove_AI_Players -- Removes the computer AI houses & their units * + * Scan_Place_Object -- places an object >near< the given cell * + * Set_Scenario_Name -- Creates the INI scenario name string. * + * Sort_Cells -- sorts an array of cells by distance * + * Write_Scenario_Ini -- Write the scenario INI file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/************************************* Prototypes *********************************************/ +static void Assign_Houses(void); +static void Remove_AI_Players(void); +static void Create_Units(void); +static void Sort_Cells(CELL *cells, int numcells, CELL *outcells); +static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells); +static CELL Clip_Scatter(CELL cell, int maxdist); +static CELL Clip_Move(CELL cell, FacingType facing, int dist); + + +/*********************************************************************************************** + * Set_Scenario_Name -- Creates the INI scenario name string. * + * * + * This routine is used by the scenario loading and saving code. It generates the scenario * + * INI root file name for the specified scenario parameters. * + * * + * INPUT: * + * buf buffer to store filename in; must be long enough for root.ext * + * scenario scenario number * + * player player type for this game (GDI, NOD, multi-player, ...) * + * dir directional parameter for this game (East/West) * + * var variation of this game (Lose, A/B/C/D, etc) * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * + *=============================================================================================*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) +{ + char c_player; // character representing player type + char c_dir; // character representing direction type + char c_var; // character representing variation type + ScenarioVarType i; + char fname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Set the player-type value. + */ + switch (player) { + case SCEN_PLAYER_GDI: + c_player = HouseTypeClass::As_Reference(HOUSE_GOOD).Prefix; +// c_player = 'G'; + break; + + case SCEN_PLAYER_NOD: + c_player = HouseTypeClass::As_Reference(HOUSE_BAD).Prefix; +// c_player = 'B'; + break; + + case SCEN_PLAYER_JP: + c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; +// c_player = 'J'; + break; + + /* + ** Multi player scenario. + */ + default: + c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; +// c_player = 'M'; + break; + } + + /* + ** Set the directional character value. + ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' + */ + switch (dir) { + case SCEN_DIR_EAST: + c_dir = 'E'; + break; + + case SCEN_DIR_WEST: + c_dir = 'W'; + break; + + default: + case SCEN_DIR_NONE: + c_dir = (Random_Pick(0, 1) == 0) ? 'W' : 'E'; + break; + } + + /* + ** Set the variation value. + */ + if (var == SCEN_VAR_NONE) { + + /* + ** Find which variations are available for this scenario + */ + for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { + sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); + if (!CCFileClass(fname).Is_Available()) { + break; + } + } + + if (i==SCEN_VAR_FIRST) { + c_var = 'X'; // indicates an error + } else { + c_var = 'A' + Random_Pick(0, i-1); + } + } else { + switch (var) { + case SCEN_VAR_A: + c_var = 'A'; + break; + + case SCEN_VAR_B: + c_var = 'B'; + break; + + case SCEN_VAR_C: + c_var = 'C'; + break; + + case SCEN_VAR_D: + c_var = 'D'; + break; + + default: + c_var = 'L'; + break; + + } + } + + /* + ** generate the filename + */ + sprintf(buf, "SC%c%02d%c%c", c_player, scenario, c_dir, c_var); +} + + +extern void GlyphX_Assign_Houses(void); //ST - 6/25/2019 11:08AM + + +/*********************************************************************************************** + * Read_Scenario_Ini -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool Read_Scenario_Ini(char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + char buf[128]; // Working string staging buffer. +#ifndef USE_RA_AI + int rndmax; + int rndmin; +#endif //USE_RA_AI + int len; + unsigned char val; + + ScenarioInit++; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + if (fresh) { + Clear_Scenario(); + } + + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (Scenario != 1) { + if (Scenario >=60){ + RequiredCD = -1; + }else{ + switch (ScenPlayer) { + case SCEN_PLAYER_GDI: + RequiredCD = 0; + break; + case SCEN_PLAYER_NOD: + RequiredCD = 1; + break; + default: + RequiredCD = -1; + break; + } + } + } else { + RequiredCD = -1; + } + } + } + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Read_Scenario_Ini - CD not found", true); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + } + + /* + ** Create scenario filename and read the file. + */ + + sprintf(fname,"%s.INI",root); + CCFileClass file(fname); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(fname); + return(false); + } else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(fname); + + file.Read(buffer, _ShapeBufferSize-1); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; +#ifndef DEMO + Add_CRC(&ScenarioCRC, (unsigned long)val); +#endif + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer); + WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer); + WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer); + + /* + ** For single-player scenarios, 'BuildLevel' is the scenario number. + ** This must be set before any buildings are created (if a factory is created, + ** it needs to know the BuildLevel for the sidebar.) + */ + if (GameToPlay == GAME_NORMAL) { +#ifdef NEWMENU + if (Scenario <= 15) { + BuildLevel = Scenario; + } else { + BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", Scenario, buffer); + } +#else + BuildLevel = Scenario; +#endif + } + + /* + ** Jurassic scenarios are allowed to build the full multiplayer set + ** of objects. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + BuildLevel = 98; + } + + /* + ** Fetch the transition theme for this scenario. + */ + TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + TransitTheme = Theme.From_Name(buf); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ +// if (GameToPlay == GAME_NORMAL && (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer == SCEN_PLAYER_NOD)) { + if (GameToPlay == GAME_NORMAL) { + WWGetPrivateProfileString("Basic", "Player", "GoodGuy", buf, 127, buffer); + CarryOverPercent = WWGetPrivateProfileInt("Basic", "CarryOverMoney", 100, buffer); + CarryOverPercent = Cardinal_To_Fixed(100, CarryOverPercent); + CarryOverCap = WWGetPrivateProfileInt("Basic", "CarryOverCap", -1, buffer); + + PlayerPtr = HouseClass::As_Pointer(HouseTypeClass::From_Name(buf)); + PlayerPtr->IsHuman = true; + int carryover; + if (CarryOverCap != -1) { + carryover = MIN((int)Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent), CarryOverCap); + } else { + carryover = Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->InitialCredits += carryover; + + if (Special.IsJurassic) { + PlayerPtr->ActLike = Whom; + } + + if (Special.IsEasy) { + PlayerPtr->Assign_Handicap(DIFF_EASY); + } else if (Special.IsDifficult) { + PlayerPtr->Assign_Handicap(DIFF_HARD); + } + } else { + +#ifdef OBSOLETE + if (GameToPlay==GAME_NORMAL && ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + PlayerPtr->Credits += CarryOverMoney; + PlayerPtr->InitialCredits += CarryOverMoney; + PlayerPtr->ActLike = Whom; + } else { + Assign_Houses(); + } +#endif + //Call new Assign_Houses function. ST - 6/25/2019 11:07AM + //Assign_Houses(); + GlyphX_Assign_Houses(); + } + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary(root, &ScenarioCRC)) { + TemplateClass::Read_INI(buffer); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, (sizeof(BriefingText)-strlen(BriefingText))-1, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + /* + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. + */ + if (BriefingText[0] == '\0') { + memset(_ShapeBuffer, '\0', _ShapeBufferSize); + CCFileClass("MISSION.INI").Read(_ShapeBuffer, _ShapeBufferSize); + + char * buffer = (char *)Add_Long_To_Pointer(_ShapeBuffer, strlen(_ShapeBuffer)); + char * work = &BriefingText[0]; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *work = '\0'; + WWGetPrivateProfileString(root, buff, "", work, (sizeof(BriefingText)-strlen(BriefingText))-1, _ShapeBuffer); + if (strlen(work) == 0) break; + strcat(work, " "); + work += strlen(work); + } + } + + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Special cases: + ** NOD7A cell 2795 - LAND_ROCK + ** NOD09A - delete airstrike trigger when radar destroyed + ** NOD10B cell 2015 - LAND_ROCK + */ + if (_stricmp(ScenarioName, "scb07ea") == 0) { + Map[(CELL)2795].Override_Land_Type(LAND_ROCK); + } + if (_stricmp(ScenarioName, "scb09ea") == 0) { + for (int index = 0; index < Buildings.Count(); ++index) { + BuildingClass* building = Buildings.Ptr(index); + if (building != NULL && building->Owner() == HOUSE_GOOD && *building == STRUCT_RADAR) { + building->Trigger = TriggerClass::As_Pointer("dely"); + if (building->Trigger) { + building->Trigger->AttachCount++; + } + break; + } + } + } + if (_stricmp(ScenarioName, "scb10eb") == 0) { + Map[(CELL)2015].Override_Land_Type(LAND_ROCK); + } + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - Otherwise, set MPlayerBlitz to 0 or 1, randomly + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (GameToPlay != GAME_NORMAL || ScenPlayer == SCEN_PLAYER_2PLAYER || + ScenPlayer == SCEN_PLAYER_MPLAYER) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if (!MPlayerGhosts && !Debug_Map) { + //Remove_AI_Players(); // Done elsewhere now. ST - 6/25/2019 12:33PM + } else { + + /* + ** If Ghosts are on, set up their houses for blitzing the humans + */ +#ifndef USE_RA_AI + MPlayerBlitz = IRandom (0,1); // 1 = computer will blitz + + if (MPlayerBlitz) { + if (MPlayerBases) { + rndmax = 14000; + rndmin = 10000; + } else { + rndmax = 8000; + rndmin = 4000; + } + + for (int i = 0; i < MPlayerMax; i++) { + HousesType house = (HousesType)(i + (int)HOUSE_MULTI1); + HouseClass *housep = HouseClass::As_Pointer (house); + if (housep) { //Added. ST - 6/25/2019 11:37AM + housep->BlitzTime = IRandom (rndmin,rndmax); + } + } + + } +#else // USE_RA_AI + MPlayerBlitz = 0; +#endif // USE_RA_AI + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. MPlayerUnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if MPlayerGoodies is on. + */ + if (MPlayerGoodies) { + for (int index = 0; index < MPlayerCount; index++) { + //for (int index = 0; index < 200; index++) { // Lots of crates for test + Map.Place_Random_Crate(); + } + } + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ + ScenarioInit--; + return(true); +} + + +/*********************************************************************************************** + * Read_Scenario_Ini_File -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * scenario_file_name path to the ini for the scenario * + * * + * bin_file_name path to the bin for the scenario * + * * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/2019 JAS : Created. * + *=============================================================================================*/ +bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const char* root, bool fresh) +{ + ScenarioInit++; + + char *buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + char buf[128]; + int len; + unsigned char val; + + CCFileClass file(scenario_file_name); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(scenario_file_name); + return(false); + } + else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(scenario_file_name); + + file.Read(buffer, _ShapeBufferSize - 1); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; +#ifndef DEMO + Add_CRC(&ScenarioCRC, (unsigned long)val); +#endif + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer); + WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer); + WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer); + + /* + ** For single-player scenarios, 'BuildLevel' is the scenario number. + ** This must be set before any buildings are created (if a factory is created, + ** it needs to know the BuildLevel for the sidebar.) + */ + if (GameToPlay == GAME_NORMAL) { + /* + ** In this function we are only dealing with custom maps, so set based on the BuildLevel from the map, or 98 if none. + ** ST - 4/22/2020 5:14PM + */ + BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", 98, buffer); + } + + /* + ** Jurassic scenarios are allowed to build the full multiplayer set + ** of objects. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + BuildLevel = 98; + } + + /* + ** Fetch the transition theme for this scenario. + */ + TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + TransitTheme = Theme.From_Name(buf); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + // if (GameToPlay == GAME_NORMAL && (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer == SCEN_PLAYER_NOD)) { + if (GameToPlay == GAME_NORMAL) { + WWGetPrivateProfileString("Basic", "Player", "GoodGuy", buf, 127, buffer); + CarryOverPercent = WWGetPrivateProfileInt("Basic", "CarryOverMoney", 100, buffer); + CarryOverPercent = Cardinal_To_Fixed(100, CarryOverPercent); + CarryOverCap = WWGetPrivateProfileInt("Basic", "CarryOverCap", -1, buffer); + + PlayerPtr = HouseClass::As_Pointer(HouseTypeClass::From_Name(buf)); + PlayerPtr->IsHuman = true; + int carryover; + if (CarryOverCap != -1) { + carryover = MIN((int)Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent), CarryOverCap); + } + else { + carryover = Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->InitialCredits += carryover; + + if (Special.IsJurassic) { + PlayerPtr->ActLike = Whom; + } + + if (Special.IsEasy) { + PlayerPtr->Assign_Handicap(DIFF_EASY); + } + else if (Special.IsDifficult) { + PlayerPtr->Assign_Handicap(DIFF_HARD); + } + } + else { + +#ifdef OBSOLETE + if (GameToPlay == GAME_NORMAL && ScenPlayer == SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + PlayerPtr->Credits += CarryOverMoney; + PlayerPtr->InitialCredits += CarryOverMoney; + PlayerPtr->ActLike = Whom; + } + else { + Assign_Houses(); + } +#endif + //Call new Assign_Houses function. ST - 6/25/2019 11:07AM + //Assign_Houses(); + GlyphX_Assign_Houses(); + } + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary_File(bin_file_name, &ScenarioCRC)) { + TemplateClass::Read_INI(buffer); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, (sizeof(BriefingText) - strlen(BriefingText)) - 1, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + /* + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. + */ + if (BriefingText[0] == '\0') { + memset(_ShapeBuffer, '\0', _ShapeBufferSize); + CCFileClass("MISSION.INI").Read(_ShapeBuffer, _ShapeBufferSize); + + char * buffer = (char *)Add_Long_To_Pointer(_ShapeBuffer, strlen(_ShapeBuffer)); + char * work = &BriefingText[0]; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *work = '\0'; + WWGetPrivateProfileString(root, buff, "", work, (sizeof(BriefingText) - strlen(BriefingText)) - 1, _ShapeBuffer); + if (strlen(work) == 0) break; + strcat(work, " "); + work += strlen(work); + } + } + + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - Otherwise, set MPlayerBlitz to 0 or 1, randomly + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (GameToPlay != GAME_NORMAL || ScenPlayer == SCEN_PLAYER_2PLAYER || + ScenPlayer == SCEN_PLAYER_MPLAYER) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if (!MPlayerGhosts && !Debug_Map) { + //Remove_AI_Players(); // Done elsewhere now. ST - 6/25/2019 12:33PM + } + else { + + /* + ** If Ghosts are on, set up their houses for blitzing the humans + */ +#ifndef USE_RA_AI + MPlayerBlitz = IRandom(0, 1); // 1 = computer will blitz + + if (MPlayerBlitz) { + if (MPlayerBases) { + rndmax = 14000; + rndmin = 10000; + } + else { + rndmax = 8000; + rndmin = 4000; + } + + for (int i = 0; i < MPlayerMax; i++) { + HousesType house = (HousesType)(i + (int)HOUSE_MULTI1); + HouseClass *housep = HouseClass::As_Pointer(house); + if (housep) { //Added. ST - 6/25/2019 11:37AM + housep->BlitzTime = IRandom(rndmin, rndmax); + } + } + + } +#else // USE_RA_AI + MPlayerBlitz = 0; +#endif // USE_RA_AI + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. MPlayerUnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if MPlayerGoodies is on. + */ + if (MPlayerGoodies) { + for (int index = 0; index < MPlayerCount; index++) { + //for (int index = 0; index < 200; index++) { // Lots of crates for test + Map.Place_Random_Crate(); + } + } + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ + ScenarioInit--; + return(true); +} + + + +/*********************************************************************************************** + * Read_Movies_From_Scenario_Ini -- Reads just the movie files from the scenario. * + * * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/14/2019 JAS : Created. * + *=============================================================================================*/ +bool Read_Movies_From_Scenario_Ini(char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char fname[_MAX_FNAME + _MAX_EXT]; // full INI filename +// char buf[128]; // Working string staging buffer. +#ifndef USE_RA_AI + int rndmax; + int rndmin; +#endif //USE_RA_AI + int len; + unsigned char val; + + ScenarioInit++; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + if (fresh) { + Clear_Scenario(); + } + + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + if (Scenario >= 20 && Scenario < 60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } + else { + if (Scenario != 1) { + if (Scenario >= 60) { + RequiredCD = -1; + } + else { + switch (ScenPlayer) { + case SCEN_PLAYER_GDI: + RequiredCD = 0; + break; + case SCEN_PLAYER_NOD: + RequiredCD = 1; + break; + default: + RequiredCD = -1; + break; + } + } + } + else { + RequiredCD = -1; + } + } + } + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Read_Scenario_Ini - CD not found", true); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + } + + /* + ** Create scenario filename and read the file. + */ + + sprintf(fname, "%s.INI", root); + CCFileClass file(fname); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(fname); + return(false); + } + else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(fname); + + file.Read(buffer, _ShapeBufferSize - 1); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; +#ifndef DEMO + Add_CRC(&ScenarioCRC, (unsigned long)val); +#endif + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer); + WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer); + WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer); + + /* + ** Fetch the transition theme for this scenario. + */ + TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", MovieThemeName, sizeof(MovieThemeName), buffer); + //TransitTheme = Theme.From_Name(buf); + + /* + ** Return with flag saying that the scenario file was read. + */ + ScenarioInit--; + return(true); +} + + + +/*********************************************************************************************** + * Write_Scenario_Ini -- Write the scenario INI file. * + * * + * INPUT: * + * root root filename for the scenario * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/11/1995 JLB : Updates movie data. * + *=============================================================================================*/ +void Write_Scenario_Ini(char *root) +{ +#ifndef CHEAT_KEYS + root = root; +#else + char * buffer; // Scenario.ini staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full scenario name + HousesType house; + CCFileClass file; + + /* + ** Get a working pointer to the INI staging buffer. Make sure that the buffer + ** starts cleared out of any data. + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + switch (ScenPlayer) { + case SCEN_PLAYER_GDI: + house = HOUSE_GOOD; + break; + + case SCEN_PLAYER_NOD: + house = HOUSE_BAD; + break; + + case SCEN_PLAYER_JP: + house = HOUSE_JP; + break; + + default: + house = HOUSE_MULTI1; + break; + } + + /* + ** Create scenario filename and clear the buffer to empty. + */ + sprintf(fname,"%s.INI",root); + file.Set_Name(fname); + if (file.Is_Available()) { +// file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); +// file.Close(); + } else { + sprintf(buffer, "; Scenario %d control for house %s.\r\n", Scenario, HouseTypeClass::As_Reference(house).IniName); + } + + WWWritePrivateProfileString("Basic", "Intro", IntroMovie, buffer); + WWWritePrivateProfileString("Basic", "Brief", BriefMovie, buffer); + WWWritePrivateProfileString("Basic", "Win", WinMovie, buffer); + WWWritePrivateProfileString("Basic", "Win2", WinMovie, buffer); + WWWritePrivateProfileString("Basic", "Win3", WinMovie, buffer); + WWWritePrivateProfileString("Basic", "Win4", WinMovie, buffer); + WWWritePrivateProfileString("Basic", "Lose", LoseMovie, buffer); + WWWritePrivateProfileString("Basic", "Action", ActionMovie, buffer); + WWWritePrivateProfileString("Basic", "Player", PlayerPtr->Class->IniName, buffer); + WWWritePrivateProfileString("Basic", "Theme", Theme.Base_Name(TransitTheme), buffer); + WWWritePrivateProfileInt("Basic", "BuildLevel", BuildLevel, buffer); + WWWritePrivateProfileInt("Basic", "CarryOverMoney", Fixed_To_Cardinal(100, CarryOverPercent), buffer); + WWWritePrivateProfileInt("Basic", "CarryOverCap", CarryOverCap, buffer); + + TeamTypeClass::Write_INI(buffer, true); + TriggerClass::Write_INI(buffer, true); + Map.Write_INI(buffer); + Map.Write_Binary(root); + HouseClass::Write_INI(buffer); + UnitClass::Write_INI(buffer); + InfantryClass::Write_INI(buffer); + BuildingClass::Write_INI(buffer); + TerrainClass::Write_INI(buffer); + OverlayClass::Write_INI(buffer); + SmudgeClass::Write_INI(buffer); + + Base.Write_INI(buffer); + + /* + ** Write the scenario data out to a file. + */ +// file.Open(WRITE); + file.Write(buffer, strlen(buffer)); +// file.Close(); + + /* + ** Now update the Master INI file, containing the master list of triggers & teams + */ + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("MASTER.INI"); + if (file.Is_Available()) { +// file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); +// file.Close(); + } else { + sprintf(buffer, "; Master Trigger & Team List.\r\n"); + } + + TeamTypeClass::Write_INI(buffer, false); + TriggerClass::Write_INI(buffer, false); + +// file.Open(WRITE); + file.Write(buffer,strlen(buffer)); +// file.Close(); +#endif +} + + +/*********************************************************************************************** + * Assign_Houses -- Assigns multiplayer houses to various players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + * 07/14/1995 JLB : Records name of player in house structure. * + *=============================================================================================*/ +static void Assign_Houses(void) +{ + HousesType house; + HousesType pref_house; + HouseClass *housep; + bool house_used[MAX_PLAYERS]; // true = this house is in use + bool color_used[16]; // true = this color is in use. We have more than 6 color options now, so bumped this to 16. ST - 6/19/2019 5:18PM + int i,j; + PlayerColorType color; + HousesType house2; + HouseClass *housep2; + + /* + ** Init the 'used' flag for all houses & colors to 0 + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house_used[i] = false; + } + for (i = 0; i < 16; i++) { + color_used[i] = false; + } + + /* + ** For each player, randomly pick a house + */ + for (i = 0; i < MPlayerCount; i++) { + j = Random_Pick(0, MPlayerMax-1); + + /* + ** If this house was already selected, decrement 'i' & keep looping. + */ + if (house_used[j]) { + i--; + continue; + } + + /* + ** Set the house, preferred house (GDI/NOD), color, and actual house; + ** get a pointer to the house instance + */ + house = (HousesType)(j + (int)HOUSE_MULTI1); + pref_house = MPlayerID_To_HousesType(MPlayerID[i]); + color = MPlayerID_To_ColorIndex(MPlayerID[i]); + housep = HouseClass::As_Pointer(house); + MPlayerHouses[i] = house; + + /* + ** Mark this house & color as used + */ + house_used[j] = true; + color_used[color] = true; + + /* + ** Set the house's IsHuman, Credits, ActLike, & RemapTable + */ + memset((char *)housep->Name, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->Name, MPlayerNames[i], MPLAYER_NAME_MAX-1); + housep->IsHuman = true; + housep->Init_Data(color, pref_house, MPlayerCredits); + + /* + ** If this ID is for myself, set up PlayerPtr + */ + if (MPlayerID[i] == MPlayerLocalID) { + PlayerPtr = housep; + } + } + + /* + ** For all houses not assigned to a player, set them up for computer use + */ + for (i = 0; i < MPlayerMax; i++) { + if (house_used[i] == false) { + + /* + ** Set the house, preferred house (GDI/NOD), and color; get a pointer + ** to the house instance + */ + house = (HousesType)(i + (int)HOUSE_MULTI1); + pref_house = (HousesType)(IRandom(0, 1) + (int)HOUSE_GOOD); + for (;;) { + color = Random_Pick(REMAP_FIRST, REMAP_LAST); + if (color_used[color] == false) { + break; + } + } + housep = HouseClass::As_Pointer (house); + + /* + ** Mark this house & color as used + */ + house_used[i] = true; + color_used[color] = true; + + /* + ** Set the house's IsHuman, Credits, ActLike, & RemapTable + */ + housep->IsHuman = false; + housep->Init_Data(color, pref_house, MPlayerCredits); + } + } + + /* + ** Now make all computer-owned houses allies of each other. + */ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + housep = HouseClass::As_Pointer(house); + if (housep->IsHuman) + continue; + + for (house2 = HOUSE_MULTI1; house2 < (HOUSE_MULTI1 + MPlayerMax); house2++) { + housep2 = HouseClass::As_Pointer (house2); + if (housep2->IsHuman) + continue; + housep->Make_Ally(house2); + } + } +} + + +/*********************************************************************************************** + * Remove_AI_Players -- Removes the computer AI houses & their units * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Remove_AI_Players(void) +{ + int i; + HousesType house; + HouseClass *housep; + + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + housep->Clobber_All(); + } + } +} + +#define USE_GLYPHX_START_LOCATIONS 1 + +/*********************************************************************************************** + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * * + * This routine uses data tables to determine which units to create for either * + * a GDI or NOD house, and how many of each. * + * * + * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * + * as that house's "home" cell. * + * * + * ------------------ Unit Summary: ------------------------------- * + * UNIT_MTANK Medium tank (M1). GDI 7 * + * UNIT_JEEP 4x4 jeep replacement. GDI 5 * + * UNIT_MLRS MLRS rocket launcher. GDI 99 * + * UNIT_APC APC. GDI 10 * + * UNIT_HTANK Heavy tank (Mammoth). GDI 13 * + * * + * UNIT_LTANK Light tank ('Bradly'). NOD 5 * + * UNIT_BUGGY Rat patrol dune buggy type NOD 5 * + * UNIT_ARTY Artillery unit. NOD 10 * + * UNIT_FTANK Flame thrower tank. NOD 11 * + * UNIT_STANK Stealth tank (Romulan). NOD 13 * + * UNIT_BIKE Nod recon motor-bike. NOD 99 * + * * + * ~1/3 chance of getting: {UNIT_MHQ, Mobile Head Quarters. * + * * + * ------------------ Infantry Summary: ------------------------------- * + * INFANTRY_E1, Mini-gun armed. GDI/NOD * + * INFANTRY_E2, Grenade thrower. GDI * + * INFANTRY_E3, Rocket launcher. NOD * + * INFANTRY_E6, Rocket launcher GDI * + * INFANTRY_E4, Flame thrower equipped. NOD * + * INFANTRY_RAMBO, Commando. GDI/NOD * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static int ReserveInfantryIndex = 0; +static void Reserve_Infantry() +{ + if (Infantry.Count() == Infantry.Length()) { + delete Infantry.Ptr(ReserveInfantryIndex); + ReserveInfantryIndex = (ReserveInfantryIndex + 1) % Infantry.Length(); + } +} + +static int ReserveUnitIndex = 0; +static void Reserve_Unit() +{ + if (Units.Count() == Units.Length()) { + delete Units.Ptr(ReserveUnitIndex); + ReserveUnitIndex = (ReserveUnitIndex + 1) % Units.Length(); + } +} + +static void Create_Units(void) +{ + enum { + NUM_UNIT_CATEGORIES = 8, + NUM_INFANTRY_CATEGORIES = 5, + }; + + static struct { + int MinLevel; + int GDICount; + UnitType GDIType; + int NODCount; + UnitType NODType; + } utable[] = { + {0, 1,UNIT_MTANK, 2,UNIT_LTANK}, + {2, 1,UNIT_JEEP, 1,UNIT_BUGGY}, + {3, 1,UNIT_MLRS, 1,UNIT_ARTY}, + {4, 1,UNIT_APC, 2,UNIT_BUGGY}, + {5, 1,UNIT_JEEP, 1,UNIT_BIKE}, + {5, 2,UNIT_JEEP, 1,UNIT_FTANK}, + {6, 1,UNIT_MSAM, 1,UNIT_MSAM}, + {7, 1,UNIT_HTANK, 2,UNIT_STANK}, + }; + static int num_units[NUM_UNIT_CATEGORIES]; // # of each type of unit to create + int tot_units; // total # units to create + + static struct { + int MinLevel; + int GDICount; + InfantryType GDIType; + int NODCount; + InfantryType NODType; + } itable[] = { + {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, + {1, 1,INFANTRY_E2, 1,INFANTRY_E3}, + {3, 1,INFANTRY_E3, 1,INFANTRY_E3}, + {5, 1,INFANTRY_E3, 1,INFANTRY_E4}, + {7, 1,INFANTRY_RAMBO, 1,INFANTRY_RAMBO}, + }; + static int num_infantry[NUM_INFANTRY_CATEGORIES];// # of each type of infantry to create + int tot_infantry; // total # infantry to create + + CELL waypts[26]; +// CELL sorted_waypts[26]; + int num_waypts; + + HousesType h; // house loop counter + HouseClass *hptr; // ptr to house being processed + + CELL centroid; // centroid of this house's stuff +// int try_count; // # times we've tried to select a centroid + CELL centerpt; // centroid for a category of objects, as a CELL + + int u_limit; // last allowable index of units for this BuildLevel + int i_limit; // last allowable index of infantry for this BuildLevel + TechnoClass *obj; // newly-created object + int i,j,k; // loop counters + int scaleval; // value to scale # units or infantry + + ReserveInfantryIndex = ReserveUnitIndex = 0; + + /*------------------------------------------------------------------------ + For the current BuildLevel, find the max allowable index into the tables + ------------------------------------------------------------------------*/ + for (i = 0; i < NUM_UNIT_CATEGORIES; i++) { + if (BuildLevel >= (unsigned)utable[i].MinLevel) + u_limit = i; + } + for (i = 0; i < NUM_INFANTRY_CATEGORIES; i++) { + if (BuildLevel >= (unsigned)utable[i].MinLevel) + i_limit = i; + } + + /*------------------------------------------------------------------------ + Compute how many of each buildable category to create + ------------------------------------------------------------------------*/ + /*........................................................................ + Compute allowed # units + ........................................................................*/ + tot_units = (MPlayerUnitCount * 2) / 3; +// tot_units = MAX(tot_units, 1); + + /*........................................................................ + Init # of each category to 0 + ........................................................................*/ + for (i = 0; i <= u_limit; i++) + num_units[i] = 0; + + /*........................................................................ + Increment # of each category, until we've used up all units + ........................................................................*/ + j = 0; + for (i = 0; i < tot_units; i++) { + num_units[j]++; + j++; + if (j > u_limit) + j = 0; + } + + /*........................................................................ + Compute allowed # infantry + ........................................................................*/ + tot_infantry = MPlayerUnitCount - tot_units; + + /*........................................................................ + Init # of each category to 0 + ........................................................................*/ + for (i = 0; i <= i_limit; i++) + num_infantry[i] = 0; + + /*........................................................................ + Increment # of each category, until we've used up all infantry + ........................................................................*/ + j = 0; + for (i = 0; i < tot_infantry; i++) { + num_infantry[j]++; + j++; + if (j > i_limit) + j = 0; + } + + /*------------------------------------------------------------------------ + Now sort all the Waypoints on the map by distance. + ------------------------------------------------------------------------*/ + num_waypts = 0; // counts # waypoints + + /*........................................................................ + First, copy all valid waytpoints into my 'waypts' array + ........................................................................*/ + for (i = 0; i < 26; i++) { + if (Waypoint[i] != -1) { + waypts[num_waypts] = Waypoint[i]; + num_waypts++; + } + } + + /*........................................................................ + Now sort the 'waypts' array + ........................................................................*/ +#ifndef USE_GLYPHX_START_LOCATIONS + //Sort_Cells (waypts, num_waypts, sorted_waypts); +#endif + + /*------------------------------------------------------------------------ + Loop through all houses. Computer-controlled houses, with MPlayerBases + ON, are treated as though bases are OFF (since we have no base-building + AI logic.) + ------------------------------------------------------------------------*/ + for (h = HOUSE_MULTI1; h < (HOUSE_MULTI1 + MPlayerMax); h++) { + + /*..................................................................... + Get a pointer to this house; if there is none, go to the next house + .....................................................................*/ + hptr = HouseClass::As_Pointer(h); + if (!hptr) + continue; + +#ifdef USE_GLYPHX_START_LOCATIONS + /* + ** New code that respects the start locations passed in from GlyphX. + ** + ** ST - 1/8/2020 3:39PM + */ + centroid = waypts[hptr->StartLocationOverride]; + +#else // USE_GLYPHX_START_LOCATIONS + /* + ** Original start position logic. + */ + + /*..................................................................... + Pick a random waypoint; if the chosen waypoint isn't valid, try again. + 'centroid' will be the centroid of all this house's stuff. + .....................................................................*/ + try_count = 0; + while (1) { + j = IRandom(0,MPlayerMax - 1); + if (sorted_waypts[j] != -1) { + centroid = sorted_waypts[j]; + sorted_waypts[j] = -1; + break; + } + try_count++; + + /*.................................................................. + OK, we've tried enough; just pick any old cell at random, as long + as it's mappable. + ..................................................................*/ + if (try_count > 200) { + while (1) { + centroid = IRandom(0,MAP_CELL_TOTAL - 1); + if (Map.In_Radar(centroid)) + break; + } + break; + } + } +#endif // USE_GLYPHX_START_LOCATIONS + + /*--------------------------------------------------------------------- + If Bases are ON, human & computer houses are treated differently + ---------------------------------------------------------------------*/ + if (MPlayerBases) { + /*.................................................................. + - For a human-controlled house: + - Set 'scaleval' to 1 + - Create an MCV + - Attach a flag to it for capture-the-flag mode + ..................................................................*/ + if (hptr->IsHuman) { + scaleval = 1; + +#ifndef USE_RA_AI // Moved to below. ST - 7/25/2019 11:21AM + obj = new UnitClass (UNIT_MCV, h); + if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj,true); + } + } +#endif //USE_RA_AI + } else { + + /*.................................................................. + - For computer-controlled house: + - Set 'scaleval' to 3 + - Create a Mobile HQ for capture-the-flag mode + ..................................................................*/ + // Added fix for divide by zero. ST - 6/26/2019 10:40AM + int ai_player_count = MPlayerMax - MPlayerCount; + //scaleval = 3 / (MPlayerMax - MPlayerCount); + //scaleval = max(ai_player_count, 1); + scaleval = 1; //Set to 1 since EA QA can't beat skirmish with scaleval set higher. + + //if (scaleval==0) { + // scaleval = 1; + //} + +#ifndef USE_RA_AI // Give the AI an MCV below. ST - 7/25/2019 11:22AM + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_MHQ, h); + if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } +#endif //USE_RA_AI + } + +#ifdef USE_RA_AI + /* + ** Moved HQ code down here, so the AI player gets one too. ST - 7/25/2019 11:21AM + */ + Reserve_Unit(); + obj = new UnitClass (UNIT_MCV, h); + if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj,true); + } + } +#endif //USE_RA_AI + + + } else { + + /*--------------------------------------------------------------------- + If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for + capture-the-flag mode. + ---------------------------------------------------------------------*/ + scaleval = 1; + if (Special.IsCaptureTheFlag) { + Reserve_Unit(); + obj = new UnitClass (UNIT_MHQ, h); + obj->Unlimbo(Cell_Coord(centroid),DIR_N); + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } + } + + /*--------------------------------------------------------------------- + Set the house's max # units (this is used in the Mission_Timed_Hunt()) + ---------------------------------------------------------------------*/ + hptr->MaxUnit = MPlayerUnitCount * scaleval; + + /*--------------------------------------------------------------------- + Create units for this house + ---------------------------------------------------------------------*/ + for (i = 0; i <= u_limit; i++) { + /*.................................................................. + Find the center point for this category. + ..................................................................*/ + centerpt = Clip_Scatter(centroid,4); + + /*.................................................................. + Place objects; loop through all unit in this category + ..................................................................*/ + for (j = 0; j < num_units[i] * scaleval; j++) { + /*............................................................... + Create a GDI unit + ...............................................................*/ + if (hptr->ActLike == HOUSE_GOOD) { + for (k = 0; k < utable[i].GDICount; k++) { + Reserve_Unit(); + obj = new UnitClass (utable[i].GDIType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + + /* + ** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM + */ + //if (!hptr->IsHuman) { + // obj->Set_Mission(MISSION_TIMED_HUNT); + //} + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } + } + } + } else { + + /*............................................................... + Create a NOD unit + ...............................................................*/ + for (k = 0; k < utable[i].NODCount; k++) { + Reserve_Unit(); + obj = new UnitClass (utable[i].NODType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + /* + ** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM + */ + //if (!hptr->IsHuman) { + // obj->Set_Mission(MISSION_TIMED_HUNT); + //} + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } + } + } + } + } + } + + /*--------------------------------------------------------------------- + Create infantry + ---------------------------------------------------------------------*/ + for (i = 0; i <= i_limit; i++) { + /*.................................................................. + Find the center point for this category. + ..................................................................*/ + centerpt = Clip_Scatter(centroid,4); + + /*.................................................................. + Place objects; loop through all unit in this category + ..................................................................*/ + for (j = 0; j < num_infantry[i] * scaleval; j++) { + /*............................................................... + Create GDI infantry (Note: Unlimbo calls Enter_Idle_Mode(), which + assigns the infantry to HUNT; we must use Set_Mission() to override + this state.) + ...............................................................*/ + if (hptr->ActLike == HOUSE_GOOD) { + for (k = 0; k < itable[i].GDICount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].GDIType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + /* + ** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM + */ + //if (!hptr->IsHuman) { + // obj->Set_Mission(MISSION_TIMED_HUNT); + //} + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } + } + } + } else { + + /*............................................................... + Create NOD infantry + ...............................................................*/ + for (k = 0; k < itable[i].NODCount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].NODType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + /* + ** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM + */ + //if (!hptr->IsHuman) { + // obj->Set_Mission(MISSION_TIMED_HUNT); + //} + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Scan_Place_Object -- places an object >near< the given cell * + * * + * INPUT: * + * obj ptr to object to Unlimbo * + * cell center of search area * + * * + * OUTPUT: * + * true = object was placed; false = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +int Scan_Place_Object(ObjectClass *obj, CELL cell) +{ + int dist; // for object placement + FacingType rot; // for object placement + FacingType fcounter; // for object placement + int tryval; + CELL newcell; + TechnoClass *techno; + int skipit; + + /*------------------------------------------------------------------------ + First try to unlimbo the object in the given cell. + ------------------------------------------------------------------------*/ + if (Map.In_Radar(cell)) { + techno = Map[cell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(cell),DIR_N)) { + return(true); + } + } + } + + /*------------------------------------------------------------------------ + Loop through distances from the given center cell; skip the center cell. + For each distance, try placing the object along each rotational direction; + if none are available, try each direction with a random scatter value. + If that fails, go to the next distance. + This ensures that the closest coordinates are filled first. + ------------------------------------------------------------------------*/ + for (dist = 1; dist < 32; dist++) { + /*..................................................................... + Pick a random starting direction + .....................................................................*/ + rot = (FacingType)IRandom (FACING_N, FACING_NW); + /*..................................................................... + Try all directions twice + .....................................................................*/ + for (tryval = 0 ; tryval < 2; tryval++) { + /*.................................................................. + Loop through all directions, at this distance. + ..................................................................*/ + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + + skipit = false; + + /*............................................................... + Pick a coordinate along this directional axis + ...............................................................*/ + newcell = Clip_Move(cell, rot, dist); + + /*............................................................... + If this is our second try at this distance, add a random scatter + to the desired cell, so our units aren't all aligned along spokes. + ...............................................................*/ + if (tryval > 0) + newcell = Clip_Scatter (newcell, 1); + + /*............................................................... + If, by randomly scattering, we've chosen the exact center, skip + it & try another direction. + ...............................................................*/ + if (newcell==cell) + skipit = true; + + if (!skipit) { + /*............................................................ + Only attempt to Unlimbo the object if: + - there is no techno in the cell + - the techno in the cell & the object are both infantry + ............................................................*/ + techno = Map[newcell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(newcell),DIR_N)) { + return(true); + } + } + } + + rot++; + if (rot > FACING_NW) + rot = FACING_N; + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * Sort_Cells -- sorts an array of cells by distance * + * * + * INPUT: * + * cells array to sort * + * numcells # entries in 'cells' * + * outcells array to store sorted values in * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/19/1995 BRR : Created. * + *=============================================================================================*/ +static void Sort_Cells(CELL *cells, int numcells, CELL *outcells) +{ + int i,j,k; + int num_sorted = 0; + int num_unsorted = numcells; + + /*------------------------------------------------------------------------ + Pick the first cell at random + ------------------------------------------------------------------------*/ + j = Random_Pick(0,numcells - 1); + outcells[0] = cells[j]; + num_sorted++; + + for (k = j; k < num_unsorted - 1; k++) { + cells[k] = cells[k + 1]; + } + num_unsorted--; + + /*------------------------------------------------------------------------ + After the first cell, assign the other cells based on who's furthest away + from the chosen ones. + ------------------------------------------------------------------------*/ + for (i = 0; i < numcells; i++) { + j = Furthest_Cell (outcells, num_sorted, cells, num_unsorted); + outcells[num_sorted] = cells[j]; + num_sorted++; + + for (k = j; k < num_unsorted - 1; k++) { + cells[k] = cells[k + 1]; + } + num_unsorted--; + } +} + + +/*********************************************************************************************** + * Furthest_Cell -- Finds cell furthest from a group of cells * + * * + * INPUT: * + * cells array of cells to find furthest-cell-away-from * + * numcells # entries in 'cells' * + * tcells array of cells to test; one of these will be selected as being * + * "furthest" from all the cells in 'cells' * + * numtcells # entries in 'tcells' * + * * + * OUTPUT: * + * index of 'tcell' that's furthest away from 'cells' * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/19/1995 BRR : Created. * + *=============================================================================================*/ +static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells) +{ + int i; + int j; + int mindist; // minimum distance a 'tcell' is from a 'cell' + int maxmindist; // the highest mindist value of all tcells + int maxmin_idx; // index of the tcell with largest mindist + int dist; // working distance measure + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + maxmindist = 0; + maxmin_idx = 0; + + /*------------------------------------------------------------------------ + Loop through all test cells, finding the furthest one from all entries in + the 'cells' array + ------------------------------------------------------------------------*/ + for (i = 0; i < numtcells; i++) { + + /*..................................................................... + Find the 'cell' closest to this 'tcell' + .....................................................................*/ + mindist = 0xffff; + for (j = 0; j < numcells; j++) { + dist = Distance (tcells[i],cells[j]); + if (dist <= mindist) { + mindist = dist; + } + } + + /*..................................................................... + If this tcell is further away than the others, save its distance & + index value + .....................................................................*/ + if (mindist >= maxmindist) { + maxmindist = mindist; + maxmin_idx = i; + } + } + + return (maxmin_idx); +} + + +/*********************************************************************************************** + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * * + * INPUT: * + * cell cell to scatter from * + * maxdist max distance to scatter * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Scatter(CELL cell, int maxdist) +{ + int x,y; + int xdist; + int ydist; + int xmin,xmax; + int ymin,ymax; + + /*------------------------------------------------------------------------ + Get X & Y coords of given starting cell + ------------------------------------------------------------------------*/ + x = Cell_X(cell); + y = Cell_Y(cell); + + /*------------------------------------------------------------------------ + Compute our x & y limits + ------------------------------------------------------------------------*/ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /*------------------------------------------------------------------------ + Adjust the x-coordinate + ------------------------------------------------------------------------*/ + xdist = IRandom(0,maxdist); + if (IRandom(0,1)==0) { + x += xdist; + if (x > xmax) { + x = xmax; + } + } else { + x -= xdist; + if (x < xmin) { + x = xmin; + } + } + + /*------------------------------------------------------------------------ + Adjust the y-coordinate + ------------------------------------------------------------------------*/ + ydist = IRandom(0,maxdist); + if (IRandom(0,1)==0) { + y += ydist; + if (y > ymax) { + y = ymax; + } + } else { + y -= ydist; + if (y < ymin) { + y = ymin; + } + } + + return (XY_Cell(x,y)); +} + + +/*********************************************************************************************** + * Clip_Move -- moves in given direction from given cell; clips to map * + * * + * INPUT: * + * cell cell to start from * + * facing direction to move * + * dist distance to move * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Move(CELL cell, FacingType facing, int dist) +{ + int x,y; + int xmin,xmax; + int ymin,ymax; + + /*------------------------------------------------------------------------ + Get X & Y coords of given starting cell + ------------------------------------------------------------------------*/ + x = Cell_X(cell); + y = Cell_Y(cell); + + /*------------------------------------------------------------------------ + Compute our x & y limits + ------------------------------------------------------------------------*/ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /*------------------------------------------------------------------------ + Adjust the x-coordinate + ------------------------------------------------------------------------*/ + switch (facing) { + case FACING_N: + y -= dist; + break; + + case FACING_NE: + x += dist; + y -= dist; + break; + + case FACING_E: + x += dist; + break; + + case FACING_SE: + x += dist; + y += dist; + break; + + case FACING_S: + y += dist; + break; + + case FACING_SW: + x -= dist; + y += dist; + break; + + case FACING_W: + x -= dist; + break; + + case FACING_NW: + x -= dist; + y -= dist; + break; + } + + /*------------------------------------------------------------------------ + Clip to the map + ------------------------------------------------------------------------*/ + if (x > xmax) + x = xmax; + if (x < xmin) + x = xmin; + + if (y > ymax) + y = ymax; + if (y < ymin) + y = ymin; + + return (XY_Cell(x,y)); +} \ No newline at end of file diff --git a/TIBERIANDAWN/INIT.CPP b/TIBERIANDAWN/INIT.CPP new file mode 100644 index 000000000..d92733569 --- /dev/null +++ b/TIBERIANDAWN/INIT.CPP @@ -0,0 +1,3054 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\init.cpv 2.18 16 Oct 1995 16:50:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Init_Game -- Main game initialization routine. * + * Load_Recording_Values -- Loads recording values from recording file * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * Version_Number -- Determines the version number. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#include "tcpip.h" +#include +#include +#include "ccdde.h" + +static HANDLE hCCLibrary; + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool for_real = false); + +extern "C" { +extern long RandNumb; +} + +extern int SimRandIndex; + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode +#if(0) + +long FAR PASCAL _export Start_Game_Proc(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_CREATE: + break; + + case WM_COMMAND: + EndDialog(hwnd, TRUE); + AllDone = TRUE; + break; + + case WM_DESTROY: + EndDialog(hwnd, TRUE); + break; + } + return(DefWindowProc(hwnd, message, wParam, lParam)); +} +#endif + + +extern bool Server_Remote_Connect(void); +extern bool Client_Remote_Connect(void); +extern bool SpawnedFromWChat; + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE! * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool Init_Game(int , char *[]) +{ + void const *temp_mouse_shapes; + + CCDebugString ("C&C95 - About to load reslib.dll\n"); + hCCLibrary = LoadLibrary("reslib.dll"); + + /* + ** Initialize the game object heaps. + */ + CCDebugString ("C&C95 - About to enter Units.Set_Heap\n"); + Units.Set_Heap(UNIT_MAX); + CCDebugString ("C&C95 - About to enter Factories.Set_Heap\n"); + Factories.Set_Heap(FACTORY_MAX); + CCDebugString ("C&C95 - About to enter Terrains.Set_Heap\n"); + Terrains.Set_Heap(TERRAIN_MAX); + CCDebugString ("C&C95 - About to enter Templates.Set_Heap\n"); + Templates.Set_Heap(TEMPLATE_MAX); + CCDebugString ("C&C95 - About to enter Smudges.Set_Heap\n"); + Smudges.Set_Heap(SMUDGE_MAX); + CCDebugString ("C&C95 - About to enter Overlays.Set_Heap\n"); + Overlays.Set_Heap(OVERLAY_MAX); + CCDebugString ("C&C95 - About to enter Infantry.Set_Heap\n"); + Infantry.Set_Heap(INFANTRY_MAX); + CCDebugString ("C&C95 - About to enter Bullets.Set_Heap\n"); + Bullets.Set_Heap(BULLET_MAX); + CCDebugString ("C&C95 - About to enter Buildings.Set_Heap\n"); + Buildings.Set_Heap(BUILDING_MAX); + CCDebugString ("C&C95 - About to enter Anims.Set_Heap\n"); + Anims.Set_Heap(ANIM_MAX); + CCDebugString ("C&C95 - About to enter Aircraft.Set_Heap\n"); + Aircraft.Set_Heap(AIRCRAFT_MAX); + CCDebugString ("C&C95 - About to enter Triggers.Set_Heap\n"); + Triggers.Set_Heap(TRIGGER_MAX); + CCDebugString ("C&C95 - About to enter TeamTypes.Set_Heap\n"); + TeamTypes.Set_Heap(TEAMTYPE_MAX); + CCDebugString ("C&C95 - About to enter Teams.Set_Heap\n"); + Teams.Set_Heap(TEAM_MAX); + CCDebugString ("C&C95 - About to enter Houses.Set_Heap\n"); + Houses.Set_Heap(HOUSE_MAX); + + /* + ** Initialize all the waypoints to invalid values. + */ + CCDebugString ("C&C95 - About to clear waypoints\n"); + memset(Waypoint, 0xFF, sizeof(Waypoint)); + + /* + ** Setup the keyboard processor in preparation for the game. + */ + CCDebugString ("C&C95 - About to do various keyboard inits\n"); +#ifdef FIX_ME_LATER + Keyboard_Attributes_Off(TRACKEXT | PAUSEON | BREAKON | SCROLLLOCKON | CTRLSON | CTRLCON | PASSBREAKS | FILTERONLY | TASKSWITCHABLE); +#endif //FIX_ME_LATER + Keyboard::Clear(); + Kbd.Clear(); + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + CCDebugString ("C&C95 - About to call Set_Shape_Buffer\n"); + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Bootstrap enough of the system so that the error dialog box can sucessfully + ** be displayed. + */ + CCDebugString ("C&C95 - About to register CCLOCAL.MIX\n"); +#ifdef DEMO + new MixFileClass("DEMOL.MIX"); + MixFileClass::Cache("DEMOL.MIX"); +#else + int temp = RequiredCD; + RequiredCD = -2; + new MixFileClass("CCLOCAL.MIX"); // Cached. + MixFileClass::Cache("CCLOCAL.MIX"); + CCDebugString ("C&C95 - About to register UPDATE.MIX\n"); + new MixFileClass("UPDATE.MIX"); // Cached. + new MixFileClass("UPDATA.MIX"); // Cached. + CCDebugString ("C&C95 - About to register UPDATEC.MIX\n"); + new MixFileClass("UPDATEC.MIX"); // Cached. + MixFileClass::Cache("UPDATEC.MIX"); +#ifdef JAPANESE + CCDebugString ("C&C95 - About to register LANGUAGE.MIX\n"); + new MixFileClass("LANGUAGE.MIX"); +#endif //JAPANESE + + RequiredCD = temp; + +#endif + CCDebugString ("C&C95 - About to load fonts\n"); + Green12FontPtr = Load_Alloc_Data(CCFileClass("12GREEN.FNT")); + Green12GradFontPtr = Load_Alloc_Data(CCFileClass("12GRNGRD.FNT")); + MapFontPtr = Load_Alloc_Data(CCFileClass("8FAT.FNT")); + Font8Ptr = MixFileClass::Retrieve(FONT8); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MixFileClass::Retrieve(FONT3); +// Font6Ptr = MixFileClass::Retrieve(FONT6); + Font6Ptr = Load_Alloc_Data(CCFileClass("6POINT.FNT")); + //ScoreFontPtr = MixFileClass::Retrieve("12GRNGRD.FNT"); //GRAD12FN"); //("SCOREFNT.FNT"); + ScoreFontPtr = Load_Alloc_Data(CCFileClass("12GRNGRD.FNT")); + FontLEDPtr = MixFileClass::Retrieve("LED.FNT"); + VCRFontPtr = MixFileClass::Retrieve("VCR.FNT"); +// GradFont6Ptr = MixFileClass::Retrieve("GRAD6FNT.FNT"); + GradFont6Ptr = Load_Alloc_Data(CCFileClass("GRAD6FNT.FNT")); + BlackPalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + GamePalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + OriginalPalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + WhitePalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + memset(WhitePalette, 63, 768); + + CCDebugString ("C&C95 - About to set palette\n"); + memset(BlackPalette, 0x01, 768); + if (!Special.IsFromInstall) Set_Palette(BlackPalette); + memset(BlackPalette, 0, 768); + if (!Special.IsFromInstall) { + Set_Palette(BlackPalette); + CCDebugString ("C&C95 - About to clear visible page\n"); + VisiblePage.Clear(); + } + + Set_Palette(GamePalette); + + CCDebugString ("C&C95 - About to set the mouse shape\n"); + /* + ** Since there is no mouse shape currently available we need' + ** to set one of our own. + */ + ShowCursor (FALSE); + if (MouseInstalled) { + temp_mouse_shapes = MixFileClass::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, Extract_Shape(temp_mouse_shapes,0)); + while (Get_Mouse_State() > 1) { + Show_Mouse(); + } + } + } + + CCDebugString ("C&C95 - About to enter wait for focus loop\n"); + /* + ** Process the message loop until we are in focus. + */ + do { + CCDebugString ("C&C95 - About to call Keyboard::Check\n"); + Keyboard::Check(); + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + + CCDebugString ("C&C95 - About to load the language file\n"); + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ + if (RawFileClass(Language_Name("CONQUER")).Is_Available()) { + SystemStrings = (char const *)Load_Alloc_Data(RawFileClass(Language_Name("CONQUER"))); + } else { + SystemStrings = (char const *)MixFileClass::Retrieve(Language_Name("CONQUER")); + } + + /* + ** Default palette initialization. Uses the desert palette for convenience, + ** but only the non terrain specific colors matter. + */ + //Mem_Copy((void *)MixFileClass::Retrieve("TEMPERAT.PAL"), GamePalette, 768L); + CCFileClass palfile ("TEMPERAT.PAL"); + palfile.Read (GamePalette, 768L); + + if (!MouseInstalled) { + char buffer[255]; + Set_Palette(GamePalette); +#ifdef GERMAN + sprintf(buffer, "Command & Conquer kann Ihren Maustreiber nicht finden.."); +#else +#ifdef FRENCH + sprintf(buffer, "Command & Conquer ne peut pas d‚tecter votre gestionnaire de souris."); +#else + sprintf(buffer, "Command & Conquer is unable to detect your mouse driver."); +#endif +#endif + CCMessageBox().Process(buffer, TXT_OK); + Prog_End(); + exit(1); + } + +#ifdef DEMO + /* + ** Add in any override path specified in the conquer.ini file. + */ + if (strlen(OverridePath)) { + CCFileClass::Set_Search_Drives(OverridePath); + } +#endif + +#if (0) //ST - 1/2/2019 5:49PM + CCDebugString ("C&C95 - About to search for CD drives\n"); + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + if (!CDList.Get_Number_Of_Drives()){ + Set_Palette(GamePalette); + Show_Mouse(); + CCMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End(); + exit(EXIT_FAILURE); + } + CCFileClass::Set_CD_Drive( CDList.Get_First_CD_Drive() ); + + error = CCFileClass::Set_Search_Drives("?:\\"); + switch(error) { + case 1: + Set_Palette(GamePalette); + Show_Mouse(); + CCMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End(); + exit(EXIT_FAILURE); + + case 2: + Set_Palette(GamePalette); + Show_Mouse(); + if (CCMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + +#ifdef DEMO + RequiredCD = -2; +#else + RequiredCD = -1; +#endif + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +#endif + + +#ifndef DEMO + CCDebugString ("C&C95 - About to register addon mixfiles\n"); + /* + ** Before all else, cache any additional mixfiles. + */ + + /* + ** Need to search the search paths. ST - 3/15/2019 2:18PM + */ + const char *path = ".\\"; + char search_path[_MAX_PATH]; + char scan_path[_MAX_PATH]; + + for (int p=0 ; p<100 ; p++) { + + strcpy(search_path, path); + if (search_path[strlen(search_path) - 1] != '\\') { + strcat(search_path, "\\"); + } + + strcpy(scan_path, search_path); + strcat(scan_path, "SC*.MIX"); + + WIN32_FIND_DATA find_data; + memset(&find_data, 0, sizeof(find_data)); + HANDLE file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MixFileClass(ptr); + MixFileClass::Cache(ptr); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + memset(&find_data, 0, sizeof(find_data)); + strcpy(scan_path, search_path); + strcat(scan_path, "Ss*.MIX"); + file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MixFileClass(ptr); + MixFileClass::Cache(ptr); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + path = CDFileClass::Get_Search_Path(p); + + if (path == NULL) { + break; + } + } + +#if (0) + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MixFileClass(ptr); + MixFileClass::Cache(ptr); +// free(ptr); + } while(!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MixFileClass(ptr); +// free(ptr); + } while(!_dos_findnext(&ff)); + } +#endif +#endif //DEMO + + CCDebugString ("C&C95 - About to register GENERAL.MIX\n"); + // + // This is a problem because registering the mix file can call Force_CD_Available which will try to load General.Mix + // Might as well just cut to the chase and call it directly. + // ST - 1/3/2019 5:19PM + // + //if (GeneralMix) delete GeneralMix; + //GeneralMix = new MixFileClass("GENERAL.MIX"); + Force_CD_Available(RequiredCD); + +// if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { +// do { +// new MixFileClass(ff.name); +// MixFileClass::Cache(ff.name); +// } while(!_dos_findnext(&ff)); +// } + + /* + ** Inform the file system of the various MIX files. + */ +#ifdef DEMO + new MixFileClass("DEMO.MIX"); + if (CCFileClass("DEMOM.MIX").Is_Available()) { + if (!MoviesMix) MoviesMix = new MixFileClass("DEMOM.MIX"); + ScoresPresent = true; + ThemeClass::Scan(); + } + +#else + CCDebugString ("C&C95 - About to register CONQUER.MIX\n"); + new MixFileClass("CONQUER.MIX"); // Cached. + CCDebugString ("C&C95 - About to register TRANSIT.MIX\n"); + new MixFileClass("TRANSIT.MIX"); + + CCDebugString ("C&C95 - About to register GENERAL.MIX\n"); + if (!GeneralMix) GeneralMix = new MixFileClass("GENERAL.MIX"); // Never cached. + +// if (CCFileClass("MOVIES.MIX").Is_Available()) { + CCDebugString ("C&C95 - About to register MOVIES.MIX\n"); + if (!MoviesMix) MoviesMix = new MixFileClass("MOVIES.MIX"); // Never cached. +// } + + + +#if (0) + + /* + ** Extract a movie from a mixfile. + */ + char *file_ptr = (char*)Alloc (32 * 1024 * 1024, MEM_NORMAL); + CCFileClass whatever ("PINTLE.VQA"); + + int len = whatever.Size(); + + whatever.Open(); + + DWORD actual; + HANDLE sfile = CreateFile("c:\\temp\\PINTLE.VQA", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (sfile != INVALID_HANDLE_VALUE){ + SetFilePointer (sfile, 0, NULL, FILE_END); + + do{ + whatever.Read (file_ptr, MIN (len, 1024*64)); + WriteFile(sfile, file_ptr, MIN (len, 1024*64), &actual, NULL); + len -= MIN (len, 1024*64); + }while ( len >0 ); + + CloseHandle (sfile); + } + + whatever.Close(); + + Free (file_ptr); + +#endif //(0) + + + + /* + ** Register the score mixfile. + */ + CCDebugString ("C&C95 - About to register SCORES.MIX\n"); + ScoresPresent = false; +// if (CCFileClass("SCORES.MIX").Is_Available()) { + ScoresPresent = true; + if (!ScoreMix) { + ScoreMix = new MixFileClass("SCORES.MIX"); + ThemeClass::Scan(); + } +// } +#endif + + /* + ** These are sound card specific, but the install program would have + ** copied the coorect versions to the hard drive. + */ + CCDebugString ("C&C95 - About to register SPEECH.MIX\n"); + if (CCFileClass("SPEECH.MIX").Is_Available()) { + new MixFileClass("SPEECH.MIX"); // Never cached. + } + CCDebugString ("C&C95 - About to register SOUNDS.MIX\n"); + new MixFileClass("SOUNDS.MIX"); // Cached. + + /* + ** Initialize the animation system. + */ + CCDebugString ("C&C95 - About to initialise the animation system\n"); + Anim_Init(); + + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } + + /* + ** Play the introduction movies. + */ + CCDebugString ("C&C95 - About to play the intro movie\n"); + if (!Special.IsFromInstall && !Special.IsFromWChat) Play_Intro(true); + + /* + ** Wait for a VSync; during the vertical blank, set the game palette & blit + ** the title screen. We must ensure no RGB values in the game palette match + ** those in the WWLIB's 'CurrentPalette', or the WWLIB palette-set routine + ** will skip that color; the VQ player will have changed that color (behind + ** WWLIB's back), so it will be incorrect. + */ + memset(CurrentPalette, 0x01, 768); + + if (!Special.IsFromInstall) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + } + + Hide_Mouse(); + Wait_Vert_Blank(); + if (!Special.IsFromInstall) { + Set_Palette(Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + } + Call_Back(); +// Window_Dialog_Box(hCCLibrary, "DIALOG_1", MainWindow, MakeProcInstance((FARPROC)Start_Game_Proc, hInstance)); +// if (hCCLibrary) FreeLibrary(hCCLibrary); + +#ifdef DEMO + MixFileClass::Cache("DEMO.MIX"); + MixFileClass::Cache("SOUNDS.MIX"); +#else + /* + ** Cache the main game data. This operation can take a very long time. + */ + MixFileClass::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MixFileClass::Cache("SOUNDS.MIX"); + if (Special.IsJuvenile) { + new MixFileClass("ZOUNDS.MIX"); // Cached. + MixFileClass::Cache("ZOUNDS.MIX"); + } + } + Call_Back(); +#endif + +// malloc(2); + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); + +#ifdef ONHOLD + /* + ** Check for addition options not specified on the command-line. This must + ** be done before the One_Time calls, but after the shape buffer is set up. + */ + Parse_INI_File(); +#endif + + /* + ** Perform one-time game system initializations. + */ + Call_Back(); +// malloc(3); + Map.One_Time(); +// malloc(4); + Logic.One_Time(); +// malloc(5); + Options.One_Time(); + +// malloc(6); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + SpeechBuffer = new char [SPEECH_BUFFER_SIZE]; + Call_Back(); + + /* + ** WWLIB bug: MouseState is in some undefined state; show the mouse until + ** it really shows. + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); +//#ifdef FIX_ME_LATER + while (Get_Mouse_State() > 0) Show_Mouse(); +//#endif //FIX_ME_LATER + Call_Back(); + +#ifndef DEMO + /* + ** Load multiplayer scenario descriptions + */ + Read_Scenario_Descriptions(); +#endif + + /* + ** Initialize the multiplayer score values + */ + MPlayerGamesPlayed = 0; + MPlayerNumScores = 0; + MPlayerCurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + MPlayerScore[i].Name[0] = '\0'; + MPlayerScore[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + MPlayerScore[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + memcpy (GamePalette, Palette, 768); + memcpy (OriginalPalette, Palette, 768); + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + return(true); +} + + +//#ifndef NOMEMCHECK +void Uninit_Game(void) +{ + delete Map.ShadowPage; + Map.ShadowPage = NULL; + Map.Free_Cells(); + + delete [] SpeechBuffer; + + CCFileClass::Clear_Search_Drives(); + MixFileClass::Free_All(); + + Units.Set_Heap(0); + Factories.Set_Heap(0); + Terrains.Set_Heap(0); + Templates.Set_Heap(0); + Smudges.Set_Heap(0); + Overlays.Set_Heap(0); + Infantry.Set_Heap(0); + Bullets.Set_Heap(0); + Buildings.Set_Heap(0); + Anims.Set_Heap(0); + Aircraft.Set_Heap(0); + Triggers.Set_Heap(0); + TeamTypes.Set_Heap(0); + Teams.Set_Heap(0); + Houses.Set_Heap(0); + + delete [] _ShapeBuffer; + Set_Shape_Buffer(NULL, 0); + delete [] BlackPalette; + delete [] GamePalette; + delete [] OriginalPalette; + delete [] WhitePalette; + + WWDOS_Shutdown(); + delete [] Palette; +} +//#endif + +extern bool Do_The_Internet_Menu_Thang(void); +extern int ShowCommand; + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +extern int Com_Fake_Scenario_Dialog(void); +extern int Com_Show_Fake_Scenario_Dialog(void); +extern int WChatMaxAhead; +extern int WChatSendRate; +void Check_From_WChat(char *wchat_name); + +bool Select_Game(bool fade) +{ + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode +#ifdef NEWMENU + SEL_NEW_SCENARIO, // Expansion scenario to play. +#endif + SEL_START_NEW_GAME, // start a new game +#ifdef BONUS_MISSIONS + SEL_BONUS_MISSIONS, +#endif //BONUS_MISSIONS + SEL_INTERNET, + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // replay the intro + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall of fame + SEL_NONE, // placeholder default value + }; + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + CountDownTimerClass count; + int cd_index; + + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); + + if (Special.IsFromInstall) { + /* + ** Special case for machines with 12 megs or less - just play intro, no choose side screen + */ + if (mem_info.dwTotalPhys < 12*1024*1024){ + VisiblePage.Clear(); + Play_Movie("INTRO2", THEME_NONE, false); + BreakoutAllowed = true; + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + fade = true; + VisiblePage.Clear(); + }else{ + display = false; + Show_Mouse(); + } + } + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + OutList.Init(); + Frame = 0; + PlayerWins = false; + PlayerLoses = false; + MPlayerObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + /* + ** Initialize multiplayer-protocol-specific variables: + ** If CommProtocol MULTI_E_COMP is used, you must: + ** Init FrameSendRate to a sensible value (3 is good) + ** Init MPlayerMaxAhead to an even multiple of FrameSendRate, and it must + ** be at least 2 * MPlayerMaxAhead + */ + CommProtocol = COMM_PROTOCOL_SINGLE_NO_COMP; + if (!Special.IsFromWChat){ + FrameSendRate = 3; + } + + ProcessTicks = 0; + ProcessFrames = 0; + DesiredFrameRate = 30; +//#if(TIMING_FIX) + NewMaxAheadFrame1 = 0; + NewMaxAheadFrame2 = 0; +//#endif + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + MPlayerScore[i].Kills[MPlayerCurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (GameToPlay == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + ScenarioInit++; + Theme.Queue_Song(THEME_MAP1); + ScenarioInit--; + + /* + ** If we're playing back a recording, load all pertinant values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (PlaybackGame && RecordFile.Is_Available()) { + if (RecordFile.Open(READ)) { + Load_Recording_Values(); + process = false; + Theme.Fade_Out(); + } else + PlaybackGame = false; + } + + + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat){ + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_NONE); + GameToPlay = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + memcpy (GamePalette, Palette, 768); + Blit_Hid_Page_To_Seen_Buff(); + + if (fade) { + Fade_Palette_To(Palette, FADE_PALETTE_SLOW, Call_Back); + fade = false; + } + + Set_Logic_Page(SeenBuff); +#ifdef VIRGIN_CHEAT_KEYS + Fancy_Text_Print("V.%d%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +// Fancy_Text_Print("V.%d%s%02d", 319, 190, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#else + +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("DEMO V%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText); +#endif +#endif + display = false; + Show_Mouse(); + } else { + if (RunningAsDLL) { + return true;; + } + } + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall && mem_info.dwTotalPhys >= 12*1024*1024){ + selection = SEL_START_NEW_GAME; + Theme.Queue_Song(THEME_NONE); + } + + /* + ** Handle case where we were spawned from Wchat + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_NONE); + GameToPlay = GAME_INTERNET; + }else{ + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480){ + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } + + if (selection == SEL_NONE) { +// selection = Main_Menu(0); + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + +#ifdef NEWMENU + + case SEL_INTERNET: + /* + ** Only call up the internet menu code if we dont already have connect info from WChat + */ + if (!DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Internet Menu.\n"); + if (Do_The_Internet_Menu_Thang() && DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Check_From_WChat.\n"); + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + display = false; + GameToPlay = GAME_INTERNET; + }else{ + selection = SEL_NONE; + display = true; + } + }else{ + CCDebugString ("C&C95 - About to call Check_From_WChat.\n"); + Check_From_WChat(NULL); + display = false; + GameToPlay = GAME_INTERNET; + selection = SEL_MULTIPLAYER_GAME; + } + break; + + + /* + ** Pick an expansion scenario. + */ + case SEL_NEW_SCENARIO: + CarryOverMoney = 0; + if (Expansion_Dialog()) { + Theme.Fade_Out(); +// Theme.Queue_Song(THEME_AOI); + GameToPlay = GAME_NORMAL; + process = false; + } else { + display = true; + selection = SEL_NONE; + } + break; + + +#ifdef BONUS_MISSIONS + + /* + ** User selected to play a bonus scenario. + */ + case SEL_BONUS_MISSIONS: + CarryOverMoney = 0; + + /* + ** Ensure that CD1 or CD2 is in the drive. These missions + ** are not on the covert CD. + */ + cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + /* + ** If cd_index == 2 then its a covert CD + */ + if (cd_index == 2){ + RequiredCD = 0; + if (!Force_CD_Available (RequiredCD)){ + Prog_End("Select_Game - CD not found", true); + exit(EXIT_FAILURE); + } + } + + if (Bonus_Dialog()) { + Theme.Fade_Out(); + GameToPlay = GAME_NORMAL; + process = false; + } else { + display = true; + selection = SEL_NONE; + } + break; + + +#endif //BONUS_MISSIONS + +#endif + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + CarryOverMoney = 0; + +#ifdef DEMO + Hide_Mouse(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Title_Screen("PREPICK.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + while (!Check_Key_Num()) { + Call_Back(); + } + Get_Key_Num(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Show_Mouse(); + + Scenario = 1; + BuildLevel = 1; +#else + Scenario = 1; + BuildLevel = 1; +#endif + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + +#ifndef DEMO + Theme.Fade_Out(); + Choose_Side(); +#endif + + /* + ** If user is playing special mode, do NOT change Whom; leave it set to + ** GDI or NOD. Ini.cpp will set the player's ActLike to mirror the + ** Whom value. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + ScenPlayer = SCEN_PLAYER_JP; + ScenDir = SCEN_DIR_EAST; + } + + GameToPlay = GAME_NORMAL; + process = false; + break; + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + //Theme.Fade_Out(); + Theme.Queue_Song(THEME_AOI); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'GameToPlay' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: + +#ifdef DEMO + Hide_Mouse(); + Set_Palette(BlackPalette); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + while (!Check_Key()) { + Call_Back(); + } + Get_Key(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Show_Mouse(); + display = true; + fade = true; + selection = SEL_NONE; +#else + switch (GameToPlay) { + + /* + ** If 'GameToPlay' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: +#if (0) + if ( NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((GameToPlay == GAME_NULL_MODEM && + ModemGameToPlay == MODEM_NULL_HOST) || + (GameToPlay == GAME_MODEM && + ModemGameToPlay == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + GameToPlay = Select_Serial_Dialog(); + if (GameToPlay == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + GameToPlay = Select_Serial_Dialog(); + if (GameToPlay == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } +#endif + break; + + + +#ifdef FORCE_WINSOCK + /* + ** Handle being spawned from WChat. Intermnet play based on IPX code now. + */ + case GAME_INTERNET: + CCDebugString ("C&C95 - case GAME_INTERNET:\n"); + if (Special.IsFromWChat){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + CCDebugString ("C&C95 - About to give myself focus.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + + CCDebugString ("C&C95 - About to initialise Winsock.\n"); + if (Winsock.Init()){ + CCDebugString ("C&C95 - About to read multiplayer settings.\n"); + Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + CCDebugString ("C&C95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + CCDebugString ("C&C95 - About to call Start_Server or Start_Client.\n"); + if (Server){ + ModemGameToPlay = INTERNET_HOST; + Winsock.Start_Server(); + }else{ + ModemGameToPlay = INTERNET_JOIN; + Winsock.Start_Client(); + } + + +//#if (0) + /* + ** Flush out any pending packets from a previous game. + */ + CCDebugString ("C&C95 - About to flush packet queue.\n"); + CCDebugString ("C&C95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + CCDebugString ("C&C95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + CCDebugString ("C&C95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)){ + + CCDebugString ("C&C95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()){}; + CCDebugString ("C&C95 - Ready to check for more packets.\n"); + + } + CCDebugString ("C&C95 - About to delete scrap memory.\n"); + delete temp_buffer; +//#endif //(0) + + + + }else{ + CCDebugString ("C&C95 - Winsock failed to initialise.\n"); + GameToPlay = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + CCDebugString ("C&C95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + }else{ + Read_Game_Options( "C&CSPAWN.INI" ); + } + + if (Server){ + + CCDebugString ("C&C95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()){ + CCDebugString ("C&C95 - Server_Remote_Connect returned success.\n"); + break; + }else{ + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + GameToPlay = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + //Special.IsFromWChat = false; + break; + } + }else{ + CCDebugString ("C&C95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()){ + CCDebugString ("C&C95 - Client_Remote_Connect returned success.\n"); + break; + }else{ + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + GameToPlay = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + //Special.IsFromWChat = false; + break; + } + } + + }else{ + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //FORCE_WINSOCK + + } + + + switch (GameToPlay) { + /* + ** Internet, Modem or Null-Modem + */ + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_INTERNET: + Theme.Fade_Out(); + ScenPlayer = SCEN_PLAYER_2PLAYER; + ScenDir = SCEN_DIR_EAST; + process = false; + Options.ScoreVolume = 0; + break; + + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + /* + ** Init network system & remote-connect + */ + if (Init_Network() && Remote_Connect()) { +#if (0) + char seed[80]; + sprintf (seed, "Seed: %08x\n", Seed); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); +#endif + Options.ScoreVolume = 0; + ScenPlayer = SCEN_PLAYER_MPLAYER; + ScenDir = SCEN_DIR_EAST; + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + GameToPlay = GAME_NORMAL; + display = true; + selection = SEL_NONE; + } + break; + } +#endif + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + Theme.Stop(); + Call_Back(); + + Force_CD_Available(-1); + Play_Intro(false); + Hide_Mouse(); + +// verify existance of movie file before playing this sequence. + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + if (CCFileClass("SIZZLE.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("SIZZLE"); // Red Alert teaser. + } + + if (CCFileClass("SIZZLE2.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("SIZZLE2"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); + Show_Mouse(); + + ScenarioInit++; + Theme.Play_Song (THEME_MAP1); + ScenarioInit--; + display = true; + fade = true; + selection = SEL_NONE; + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: +#ifdef JAPANESE + Hide_Mouse(); +#endif + Theme.Fade_Out(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_SLOW, NULL); +#ifdef JAPANESE + VisiblePage.Clear(); +#endif + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (AllowAttract && RecordFile.Is_Available()) { + PlaybackGame = true; + if (RecordFile.Open(READ)) { + Load_Recording_Values(); + process = false; + Theme.Fade_Out(); + } else { + PlaybackGame = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode, if JP option is on, set to load that scenario + */ + Scenario = 1; + if (Special.IsJurassic && AreThingiesEnabled) { + ScenPlayer = SCEN_PLAYER_JP; + ScenDir = SCEN_DIR_EAST; + } + } + CCDebugString ("C&C95 - About to start game initialisation.\n"); +#ifdef FORCE_WINSOCK + if (GameToPlay == GAME_INTERNET){ + CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + if (!Special.IsFromWChat){ + FrameSendRate = 5; //3; + } + } +#endif //FORCE_WINSOCK + /* + ** Don't carry stray keystrokes into game. + */ + Kbd.Clear(); + + /* + ** Get a pointer to the compiler's random number seed. + ** the Get_EAX() must follow immediately after the srand(0) in order to save + ** the address of the random seed. (Currently not used.) + */ + srand(0); + //RandSeedPtr = (long *)Get_EAX(); // ST - 1/2/2019 5:26PM + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (GameToPlay == GAME_NORMAL && !PlaybackGame) { + srand(timeGetTime()); + //randomize(); + Seed = rand(); + } + + /* + ** If user has specified a desired random number seed, use it for multiplayer games + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } + + /* + ** Save initialization values if we're recording this game. + ** This must be done after 'Seed' has been initialized. + */ + if (RecordGame) { + if (RecordFile.Open(WRITE)) { + Save_Recording_Values(); + } else { + RecordGame = false; + } + } + + /* + ** Initialize the random-number generator. + */ + //Seed = 1; + + srand(Seed); + RandNumb = Seed; + SimRandIndex = 0; +#if (0) + DWORD actual; + HANDLE sfile = CreateFile("random.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (sfile != INVALID_HANDLE_VALUE){ + SetFilePointer (sfile, 0, NULL, FILE_END); + + int minimum; + int maximum; + char whatever[80]; + for ( int i=0 ; i<1000 ; i++){ + minimum = rand(); + maximum = rand(); + + sprintf (whatever, "%04x\n", Random_Pick(minimum, maximum)); + WriteFile(sfile, whatever, strlen(whatever), &actual, NULL); + } + CloseHandle (sfile); + } +#endif + + + + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded) { + if (Debug_Map) { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, SCEN_VAR_A); + } else { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir); + } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + } + Show_Mouse(); + + Special.IsFromInstall = 0; + CCDebugString ("C&C95 - Starting scenario.\n"); + if (!Start_Scenario(ScenarioName)) { + return(false); + } + CCDebugString ("C&C95 - Scenario started OK.\n"); + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + CCDebugString ("C&C95 - Initialising message system.\n"); + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Messages.Init(Map.TacPixelX, Map.TacPixelY, 6, MAX_MESSAGE_LENGTH, 6*factor+1); + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + CCDebugString ("C&C95 - About to call Call_Back.\n"); + Call_Back(); + + /* + ** This is desperately sad isnt it? + */ + Hide_Mouse(); + Hide_Mouse(); + Hide_Mouse(); + Hide_Mouse(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw(); + Call_Back(); + Map.Render(); + //Show_Mouse(); + + /* + ** Special hack initialization of 'MPlayerMaxAhead' to accommodate the + ** compression protocol technology. + */ +#ifdef FORCE_WINSOCK + if (CommProtocol==COMM_PROTOCOL_MULTI_E_COMP && GameToPlay!=GAME_NORMAL) { + if (!Special.IsFromWChat){ + MPlayerMaxAhead = FrameSendRate * 3; //2; + }else{ + MPlayerMaxAhead = WChatMaxAhead; + FrameSendRate = WChatSendRate; + } + } +#endif //FORCE_WINSOCK + + if (Debug_Map) { + while (Get_Mouse_State() > 1) { + Show_Mouse(); + } + } + + return(true); +} + + +/*********************************************************************************************** + * Play_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * for_real if true, this function plays the "real" intro; otherwise, it plays * + * a delicious smorgasbord of visual delights, guaranteed to titillate * + * the ocular & auditory nerve pathways. * + * Well, it plays movies, anyway. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=============================================================================================*/ +static void Play_Intro(bool for_real) +{ + return; // No game intro movies. - LLL + + bool playright = !Key_Down(KN_LCTRL) || !Key_Down(KN_RCTRL); + static int _counter = -1; + static char * _names[] = { +#ifdef DEMO + "LOGO", + +#else + + "INTRO2", +//#ifdef CHEAT_KEYS + "GDIEND1", + "GDIEND2", + "GDIFINA", + "GDIFINB", + "AIRSTRK", + "AKIRA", + "BANNER", + "BCANYON", + "BKGROUND", + "BOMBAWAY", + "BOMBFLEE", + "BURDET1", + "BURDET2", + "CC2TEASE", + "CONSYARD", + "DESFLEES", + "DESKILL", + "DESOLAT", + "DESSWEEP", + "FLAG", + "FLYY", + "FORESTKL", + "GAMEOVER", + "GDI1", + "GDI10", + "GDI11", + "GDI12", + "GDI13", + "GDI14", + "GDI15", + "GDI2", + "GDI3", + "GDI3LOSE", + "GDI4A", + "GDI4B", + "GDI5", + "GDI6", + "GDI7", + "GDI8A", + "GDI8B", + "GDI9", + "GDILOSE", + "GUNBOAT", + "HELLVALY", + "INSITES", + "KANEPRE", + "LANDING", + "LOGO", + "NAPALM", + "NITEJUMP", + "NOD1", + "NOD10A", + "NOD10B", + "NOD11", + "NOD12", + "NOD13", + "NOD1PRE", + "NOD2", + "NOD3", + "NOD4A", + "NOD4B", + "NOD5", + "NOD6", + "NOD7A", + "NOD7B", + "NOD8", + "NOD9", + "NODEND1", + "NODEND2", + "NODEND3", + "NODEND4", + "NODFINAL", + "NODFLEES", + "NODLOSE", + "NODSWEEP", + "NUKE", + "OBEL", + "PARATROP", + "PINTLE", + "PLANECRA", + "PODIUM", + "REFINT", + "RETRO", + "SABOTAGE", + "SAMDIE", + "SAMSITE", + "SEIGE", + "SETHPRE", + "SPYCRASH", + "STEALTH", + "SUNDIAL", + "TANKGO", + "TANKKILL", + "TBRINFO1", + "TBRINFO2", + "TBRINFO3", + "TIBERFX", + "TRTKIL_D", + "TURTKILL", + "VISOR", +//#endif +#endif + NULL + }; + + Keyboard::Clear(); + if (for_real) { + Hide_Mouse(); + Play_Movie("LOGO", THEME_NONE, false); + Show_Mouse(); + } else { + if (!Debug_Flag) { + _counter = 0; + } else { + if (playright) _counter++; + if (_counter == -1) _counter = 0; + } + Hide_Mouse(); + Play_Movie(_names[_counter], THEME_NONE); + Show_Mouse(); + if (!_names[_counter]) { + _counter = -1; + } + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +//extern LPDIRECTSOUND SoundObject; +//extern LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +void Anim_Init(void) +{ +//PG_TO_FIX +#if (0) + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + //AnimControl.X1 =0; + //AnimControl.Y1 =0; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = 0; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + //AnimControl.VBIBit = VertBlank; + //AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } +#endif +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + //AnimControl.DigiCard = NewConfig.DigitCard; + //AnimControl.HMIBufSize = 8192; + //AnimControl.DigiHandle = Get_Digi_Handle(); + //AnimControl.Volume = 0x00FF; + //AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + + + //PG_TO_FIX +#if (0) + AnimControl.SoundObject = SoundObject; //Get_Sound_Object(); + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; //Get_Primart_Buffer(); +#endif + + //if (!Debug_Quiet && Get_Digi_Handle() != -1) { + //AnimControl.OptionFlags |= VQAOPTF_AUDIO; + //} + +#if (0) + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } +#endif +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char *argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ +#ifdef DEMO + Scenario = 3; +#else + Scenario = 1; +#endif + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; +// Debug_Play_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + char arg_string[512]; + int str_len = strlen(argv[index]); + char *src = argv[index]; + char *dest = arg_string; + for (int i=0 ; i = Suchpfad fr Daten-Dateien festlegen.\r\n" + " -DESTNET = Netzwerkkennung des Zielrechners festlegen\r\n" + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = Kennung des Netzwerk-Sockets (0 - 16383)\n" + " -STEALTH = Namen im Mehrspieler-Modus verstecken (\"Boss-Modus\")\r\n" + " -MESSAGES = Mitteilungen von auáerhalb des Spiels zulassen\r\n" + // " -ELITE = Fortgeschrittene KI und Gefechtstechniken.\r\n" + "\r\n"); + #else + #ifdef FRENCH + puts("Command & Conquer (c) 1995, Westwood Studios\r\n" + "ParamŠtres:\r\n" +// " -CD = Recherche des fichiers dans le\r\n" +// " r‚pertoire indiqu‚.\r\n" + " -DESTNET = Sp‚cifier le num‚ro de r‚seau du systŠme de destination\r\n" + " (Syntaxe: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = ID poste r‚seau (0 … 16383)\r\n" + " -STEALTH = Cacher les noms en mode multijoueurs (\"Mode Boss\")\r\n" + " -MESSAGES = Autorise les messages ext‚rieurs … ce jeu.\r\n" + "\r\n"); + #else + puts("Command & Conquer (c) 1995, 1996 Westwood Studios\r\n" + "Parameters:\r\n" + #ifdef NEVER + " CHEAT = Enable debug keys.\r\n" + " -EDITOR = Enable scenario editor.\r\n" + #endif +// " -CD = Set search path for data files.\r\n" + " -DESTNET = Specify Network Number of destination system\r\n" + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = Network Socket ID (0 - 16383)\n" + " -STEALTH = Hide multiplayer names (\"Boss mode\")\r\n" + " -MESSAGES = Allow messages from outside this game.\r\n" + " -o = Enable compatability with version 1.07.\r\n" +#ifdef JAPANESE + " -ENGLISH = Enable English keyboard compatibility.\r\n" +#endif + // " -ELITE = Advanced AI and combat characteristics.\r\n" + #ifdef NEVER + " -O[options]= Special control options;\r\n" + " 1 : Tiberium grows.\r\n" + " 2 : Tiberium grows and spreads.\r\n" + " A : Aggressive player unit defense enabled.\r\n" + " B : Bargraphs always displayed.\r\n" + " C : Capture the flag mode.\r\n" + " E : Elite defense mode disable (attacker advantage).\r\n" + " D : Deploy reversal allowed for construction yard.\r\n" + " F : Fleeing from direct immediate threats is enabled.\r\n" + " H : Hussled recharge time.\r\n" + " G : Growth for Tiberium slowed in multiplay.\r\n" + " I : Inert weapons -- no damage occurs.\r\n" + " J : 7th grade sound effects.\r\n" + " M : Monochrome debug messages.\r\n" + " N : Name the civilians and buildings.\r\n" + " P : Path algorithm displayed as it works.\r\n" + " Q : Quiet mode (no sound).\r\n" + " R : Road pieces are not added to buildings.\r\n" + " T : Three point turns for wheeled vehicles.\r\n" + " U : U can target and burn trees.\r\n" + " V : Show target selection by opponent.\r\n" + " X : Make a recording of a multiplayer game.\r\n" + " Y : Play a recording of a multiplayer game.\r\n" + " Z : Disaster containment team.\r\n" + #endif + "\r\n"); + #endif + #endif + return(false); + } + + + bool processed = true; + switch (Obfuscate(string)) { + + /* + ** Signal that easy mode is active. + */ + case PARM_EASY: + Special.IsEasy = true; + Special.IsDifficult = false; + break; + + /* + ** Signal that hard mode is active. + */ + case PARM_HARD: + Special.IsEasy = false; + Special.IsDifficult = true; + break; + +#ifdef VIRGIN_CHEAT_KEYS + case PARM_PLAYTEST: + Debug_Playtest = true; + break; +#endif + +#ifdef PARM_CHEATERIK + case PARM_CHEATERIK: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATADAM + case PARM_CHEATADAM: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATMIKE + case PARM_CHEATMIKE: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATDAVID + case PARM_CHEATDAVID: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATPHIL + case PARM_CHEATPHIL: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATBILL + case PARM_CHEATBILL: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEAT_STEVET + + case PARM_CHEAT_STEVET: + Debug_Playtest = true; + Debug_Flag = true; + break; + +#endif + +#ifdef PARM_EDITORBILL + case PARM_EDITORBILL: + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_EDITORERIK + case PARM_EDITORERIK: + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + break; +#endif + + case PARM_SPECIAL: + Special.IsJurassic = true; + AreThingiesEnabled = true; + break; + + /* + ** Special flag - is C&C being run from the install program? + */ + case PARM_INSTALL: +#ifndef DEMO + Special.IsFromInstall = true; +#endif + break; + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** Older version override. + */ + if (stricmp(string, "-O") == 0 || stricmp(string, "-0") == 0) { + IsV107 = true; + continue; + } + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } +#ifdef JAPANESE + /* + ** Enable english-compatible keyboard + */ + if (!stricmp(string,"-ENGLISH")) { + ForceEnglish = true; + continue; + } +#endif + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8,"."); + while (p) { + int x; + + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + IsBridge = 1; + memset(node, 0xff, 6); + BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + AllowAttract = true; + continue; + } + + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string,"-480")){ + ScreenHeight = 480; + continue; + } + + /* + ** Check for spawn from WChat + */ + if (strstr(string,"-WCHAT")){ + SpawnedFromWChat = true; + } + + /* + ** Allow use of MMX instructions + */ + if (strstr(string,"-MMX")){ + MMXAvailable = true; + continue; + } + + +#ifdef CHEAT_KEYS + /* + ** Allow solo net play + */ + if (stricmp(string, "-HANSOLO") == 0) { + MPlayerSolo = true; + continue; + } + + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } +#endif + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + +#ifdef FIX_ME_LATER + /* + ** look for passed-in video mode to default to + */ + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif //FIX_ME_LATER + + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef ONHOLD + /* + ** Should human generated sound effects be used? + */ + case 'J': + Special.IsJuvenile = true; + break; +#endif + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + Special.IsMonoEnabled = true; + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; +#endif + +#ifdef CHEAT_KEYS + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** Turn on super-record mode, which thrashes your disk terribly, + ** but is really really cool. Well, sometimes it is, anyway. + ** At least, it can be. Once in a while. + ** This flag tells the recording system to re-open the file for + ** each write, so the recording survives a crash. + */ + case 'S': + SuperRecord = 1; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + RecordGame = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + PlaybackGame = 1; + break; +#endif + +#ifdef ONHOLD + /* + ** Bonus scenario enable. + */ + case 'Z': + Special.IsJurassic = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + +#ifdef CHEAT_KEYS + /* + ** Target selection by human opponent (network/modem play) will + ** be visible to the player? + */ + case 'V': + Special.IsVisibleTarget = true; + break; +#endif + + default: +#ifdef GERMAN + puts("Ungltiger Parameter.\n"); +#else +#ifdef FRENCH + puts("Commande d'option invalide.\n"); +#else + puts("Invalid option switch.\n"); +#endif +#endif + return(false); + } + + } + + if (Special.IsMonoEnabled) { + MonoClass::Enable(); + } + continue; + } + } + return(true); +} + + +#ifdef ONHOLD +/*********************************************************************************************** + * Parse_INI_File -- Parses CONQUER.INI for certain options * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/18/1995 BRR : Created. * + *=============================================================================================*/ +void Parse_INI_File(void) +{ + char *buffer; // INI staging buffer pointer. + char buf[128]; + static char section[40]; + static char entry[40]; + static char name[40]; + int len; + int i; + + /* + ** These arrays store the coded version of the names Geologic, Period, & Jurassic. + ** Decode them by subtracting 83. For you curious types, the names look like: + ** š¸Â¿Âº¼¶ + ** £¸Å¼Â· + ** ÈÅ´ÆƼ¶ + ** If these INI entries aren't found, the IsJurassic flag does nothing. + */ + static char coded_section[] = {154,184,194,191,194,186,188,182,0}; + static char coded_entry[] = {163,184,197,188,194,183,0}; + static char coded_name[] = {157,200,197,180,198,198,188,182,0}; + + /*------------------------------------------------------------------------ + Fetch working pointer to the INI staging buffer. Make sure that the buffer + is cleared out before proceeding. + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*------------------------------------------------------------------------ + Decode the desired section, entry, & name + ------------------------------------------------------------------------*/ + strcpy (section,coded_section); + len = strlen(coded_section); + for (i = 0; i < len; i++) { + section[i] -= 83; + } + + strcpy (entry,coded_entry); + len = strlen(coded_entry); + for (i = 0; i < len; i++) { + entry[i] -= 83; + } + + strcpy (name,coded_name); + len = strlen(coded_name); + for (i = 0; i < len; i++) { + name[i] -= 83; + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + WWGetPrivateProfileString(section, entry, "", buf, sizeof(buf), buffer); + + if (!stricmp (buf,name)) + AreThingiesEnabled = true; + + memset (section, 0, sizeof(section)); + memset (entry, 0, sizeof(entry)); + memset (name, 0, sizeof(name)); +} +#endif + + +/*********************************************************************************************** + * Version_Number -- Determines the version number. * + * * + * This routine will determine the version number by analyzing the date and teim that the * + * program was compiled and then generating a unique version number based on it. The * + * version numbers are guaranteed to be larger for later dates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the version number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/24/1995 JLB : Created. * + *=============================================================================================*/ +int Version_Number(void) +{ +#ifdef OBSOLETE + static bool initialized = false; + static int version; + static char * date = __DATE__; + static char * time = __TIME__; + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + + if (!initialized) { + char * ptr; + char * tok; + + /* + ** Fetch the month and place in the first two digit positions. + */ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + version = (((ptr - months) / 3)+1) * 10000; + } + + /* + ** Fetch the date and place that in the next two digit positions. + */ + tok = strtok(NULL, " "); + if (tok) { + version += atoi(tok) * 100; + } + + /* + ** Fetch the time and place that in the last two digit positions. + */ + tok = strtok(time, ": "); + if (tok) { + version += atoi(tok); + } + + + /* + ** Fetch the virgin text file (if present). + */ + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[sizeof(VersionText)-1] == '\r') { + VersionText[sizeof(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + + initialized = true; + } + return(version); +#endif + +#ifdef WIN32 + +#if (FRENCH) + sprintf(VersionText, ".02"); // Win95 french version number +#endif //FRENCH + +#if (GERMAN) + sprintf(VersionText, ".01"); // Win95 german version number +#endif //GERMAN + +#if (JAPANESE) + sprintf(VersionText, ".01"); // Win95 german version number +#endif //GERMAN + +#if !(FRENCH | GERMAN | JAPANESE) + sprintf(VersionText, ".07"); // Win95 USA version number +#endif //FRENCH | GERMAN + + RawFileClass file("VERSION.TXT"); + char version [16]; + memset (version, 0, sizeof (version)); + if (file.Is_Available()){ + file.Read (version, sizeof (version)); + } + strncat (VersionText, version, sizeof (VersionText) - strlen (VersionText) - 1); + +#if (FRENCH) + return (1); // Win95 french version number +#endif //FRENCH + +#if (GERMAN) + return (1); // Win95 german version number +#endif //GERMAN + +#if (JAPANESE) + return (1); // Win95 german version number +#endif //GERMAN + +#if !(FRENCH | GERMAN | JAPANESE) + return (1); // Win95 USA version number +#endif //FRENCH | GERMAN + +#else + + +#ifdef PATCH + + #ifdef DEMO + sprintf(VersionText, " 1.0a"); // Demo version. + #else + strcpy(VersionText, ".34 "); + #endif + return(1); + +#else + + #ifdef DEMO + sprintf(VersionText, " 1.0a"); // Demo version. + #else + // sprintf(VersionText, ".%02dp", 13); // Patch version. + sprintf(VersionText, ".%02d", 14); // Master version. + #endif + return(1); +#endif + +#endif //WIN32 +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves recording values to a recording file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +void Save_Recording_Values(void) +{ + RecordFile.Write(&GameToPlay, sizeof(GameToPlay)); + RecordFile.Write(&ModemGameToPlay, sizeof(ModemGameToPlay)); + RecordFile.Write(&BuildLevel, sizeof(BuildLevel)); + RecordFile.Write(MPlayerName, sizeof(MPlayerName)); + RecordFile.Write(&MPlayerPrefColor, sizeof(MPlayerPrefColor)); + RecordFile.Write(&MPlayerColorIdx, sizeof(MPlayerColorIdx)); + RecordFile.Write(&MPlayerHouse, sizeof(MPlayerHouse)); + RecordFile.Write(&MPlayerLocalID, sizeof(MPlayerLocalID)); + RecordFile.Write(&MPlayerCount, sizeof(MPlayerCount)); + RecordFile.Write(&MPlayerBases, sizeof(MPlayerBases)); + RecordFile.Write(&MPlayerCredits, sizeof(MPlayerCredits)); + RecordFile.Write(&MPlayerTiberium, sizeof(MPlayerTiberium)); + RecordFile.Write(&MPlayerGoodies, sizeof(MPlayerGoodies)); + RecordFile.Write(&MPlayerGhosts, sizeof(MPlayerGhosts)); + RecordFile.Write(&MPlayerUnitCount, sizeof(MPlayerUnitCount)); + RecordFile.Write(MPlayerID, sizeof(MPlayerID)); + RecordFile.Write(MPlayerHouses, sizeof(MPlayerHouses)); + RecordFile.Write(&Seed, sizeof(Seed)); + RecordFile.Write(&Scenario, sizeof(Scenario)); + RecordFile.Write(&ScenPlayer, sizeof(ScenPlayer)); + RecordFile.Write(&ScenDir, sizeof(ScenDir)); + RecordFile.Write(&Whom, sizeof(Whom)); + RecordFile.Write(&Special, sizeof(SpecialClass)); + RecordFile.Write(&Options, sizeof(GameOptionsClass)); + RecordFile.Write(&FrameSendRate, sizeof(FrameSendRate)); + RecordFile.Write(&CommProtocol, sizeof(CommProtocol)); + + if (SuperRecord) { + RecordFile.Close(); + } +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads recording values from recording file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +void Load_Recording_Values(void) +{ + Read_MultiPlayer_Settings(); + + RecordFile.Read(&GameToPlay, sizeof(GameToPlay)); + RecordFile.Read(&ModemGameToPlay, sizeof(ModemGameToPlay)); + RecordFile.Read(&BuildLevel, sizeof(BuildLevel)); + RecordFile.Read(MPlayerName, sizeof(MPlayerName)); + RecordFile.Read(&MPlayerPrefColor, sizeof(MPlayerPrefColor)); + RecordFile.Read(&MPlayerColorIdx, sizeof(MPlayerColorIdx)); + RecordFile.Read(&MPlayerHouse, sizeof(MPlayerHouse)); + RecordFile.Read(&MPlayerLocalID, sizeof(MPlayerLocalID)); + RecordFile.Read(&MPlayerCount, sizeof(MPlayerCount)); + RecordFile.Read(&MPlayerBases, sizeof(MPlayerBases)); + RecordFile.Read(&MPlayerCredits, sizeof(MPlayerCredits)); + RecordFile.Read(&MPlayerTiberium, sizeof(MPlayerTiberium)); + RecordFile.Read(&MPlayerGoodies, sizeof(MPlayerGoodies)); + RecordFile.Read(&MPlayerGhosts, sizeof(MPlayerGhosts)); + RecordFile.Read(&MPlayerUnitCount, sizeof(MPlayerUnitCount)); + RecordFile.Read(MPlayerID, sizeof(MPlayerID)); + RecordFile.Read(MPlayerHouses, sizeof(MPlayerHouses)); + RecordFile.Read(&Seed, sizeof(Seed)); + RecordFile.Read(&Scenario, sizeof(Scenario)); + RecordFile.Read(&ScenPlayer, sizeof(ScenPlayer)); + RecordFile.Read(&ScenDir, sizeof(ScenDir)); + RecordFile.Read(&Whom, sizeof(Whom)); + RecordFile.Read(&Special, sizeof(SpecialClass)); + RecordFile.Read(&Options, sizeof(GameOptionsClass)); + RecordFile.Read(&FrameSendRate, sizeof(FrameSendRate)); + RecordFile.Read(&CommProtocol, sizeof(CommProtocol)); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cyrptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[1024]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + int index; + for (index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} \ No newline at end of file diff --git a/TIBERIANDAWN/INTERNET.CPP b/TIBERIANDAWN/INTERNET.CPP new file mode 100644 index 000000000..0e9ec225e --- /dev/null +++ b/TIBERIANDAWN/INTERNET.CPP @@ -0,0 +1,886 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : INTERNET.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Miscellaneous junk related to H2H internet connection. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * Check_From_WChat -- Interprets start game packet from WChat * + * Read_Game_Options -- Read the game setup options from the wchat packet * + * Is_User_WChat_Registered -- retrieve the users wchat entry from registry * + * Spawn_WChat -- spawns or switches focus to wchat * + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "tcpip.h" +#include "ccdde.h" + + + +/*************************************************************************** +** Internet specific globals +*/ +char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name +char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password +char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player +long PlanetWestwoodPortNumber = 1234; //Port number to send to +bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options +unsigned long PlanetWestwoodGameID; //Game ID +unsigned long PlanetWestwoodStartTime; //Time that game was started +HWND WChatHWND = 0; //Handle to Wchat window. +bool UseVirtualSubnetServer; +int InternetMaxPlayers; +int WChatMaxAhead; +int WChatSendRate; + + +int Read_Game_Options(void); + +extern bool SpawnedFromWChat; + + + + + + +/*********************************************************************************************** + * Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it * + * C&CSPAWN.INI is now sent to us by WCHAT via DDE * + * * + * * + * * + * INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 1:44PM ST : Created * + *=============================================================================================*/ +void Check_From_WChat(char *wchat_name) +{ +#ifndef DEMO + + char default_string[] = {"Error"}; + char key_string[256]; + char *ini_file; + RawFileClass wchat_file; + + /* + ** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from + ** the DDE server. + */ + if (wchat_name){ + ini_file = new char [8192]; + }else{ + ini_file = DDEServer.Get_MPlayer_Game_Info(); +#if (0) + /* + ** Save it to disk as well so I can see it + */ + RawFileClass anotherfile ("FROMCHAT.TXT"); + anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length()); +#endif //(0) + } + + if (wchat_name){ + wchat_file.Set_Name(wchat_name); + } + + if (!wchat_name || wchat_file.Is_Available()){ + + /* + ** Read the ini file from disk if we founf it there + */ + if (wchat_name){ + wchat_file.Read(ini_file, wchat_file.Size()); + } + + /* + ** Get the IP address + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Address", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + strcpy (PlanetWestwoodIPAddress, key_string); + + + + /* + ** Get the port number + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Port", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + PlanetWestwoodPortNumber = atol(key_string); + + + /* + ** Get host or client + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Host", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + if (strchr (key_string, '1')){ + PlanetWestwoodIsHost = true; + }else{ + PlanetWestwoodIsHost = false; + } + + UseVirtualSubnetServer = WWGetPrivateProfileInt("Internet", "UseVSS", 0, ini_file); + + Special.IsFromWChat = true; + } + + if (wchat_name) delete ini_file; + +#else //DEMO + + wchat_name = wchat_name; + +#endif //DEMO + +} + +//EventClass Wibble; + + +/*************************************************************************** + * Read_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * name of C&CSPAWN.INI file. Null if data should be got from DDE server* * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: \ * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_Game_Options(char *name) +{ + char *buffer; + + char filename[256] = {"INVALID.123"}; + + if (name){ + strcpy (filename, name); + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file (filename); + + if (name && !file.Is_Available()) { + return(0); + } else { + if (name){ + buffer = new char [8192]; // INI staging buffer pointer. + memset(buffer, '\0', 8192); + file.Read(buffer, 8192-1); + file.Close(); + }else{ + buffer = DDEServer.Get_MPlayer_Game_Info(); + } + } + + /*------------------------------------------------------------------------ + Get the player's name + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Options", "Handle", "Noname", MPlayerName, + sizeof(MPlayerName), buffer); + strcpy(MPlayerGameName, MPlayerName); + MPlayerColorIdx = WWGetPrivateProfileInt("Options", "Color", 0, buffer); + MPlayerPrefColor = MPlayerColorIdx; + MPlayerHouse = (HousesType)WWGetPrivateProfileInt("Options", "Side", + HOUSE_GOOD, buffer); + + MPlayerCredits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); + MPlayerBases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); + MPlayerTiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); + MPlayerGoodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); + MPlayerGhosts = WWGetPrivateProfileInt("Options", "AI", 0, buffer); + BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); + MPlayerUnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); + Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); + Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CaptureTheFlag", 0, buffer); + PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + + InternetMaxPlayers = WWGetPrivateProfileInt("Internet", "MaxPlayers", 2, buffer); + + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + ScenarioIdx = WWGetPrivateProfileInt("Options", "Scenario", 0, buffer); + Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx]; + + Options.GameSpeed = 0; + + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + MPlayerMaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + + if (name) delete buffer; + return (1); + +} + + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + +void Just_Path(char *path, char *destpath) +{ + char *terminator = NULL; //He'll be back. + + strcpy (destpath, path); + terminator = strrchr (destpath, '\\'); + if (terminator){ + *terminator = 0; + } +} + + + + + +/*********************************************************************************************** + * Is_User_WChat_Registered -- retrieve the users wchat entry from the registry * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if users wchat entry was found in the registry * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:13PM ST : Created * + *=============================================================================================*/ +bool Is_User_WChat_Registered(char *buffer, int buffer_len) +{ + buffer; + buffer_len; + return false; + +#if (0) //ST - 1/2/2019 5:54PM + + HKEY key; + char user_handle[256]; + DWORD user_handle_size = sizeof (user_handle); + char user_pword[256]; + DWORD user_pword_size = sizeof (user_pword); + + + /* + ** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there + */ + key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE); + + if (key){ + key = Get_Registry_Sub_Key (key, "Nick1", TRUE); + if (key){ + + if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){ + + if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){ + + /* + ** If the first char of the users name is non-numberic and there is a password + ** then return success + */ + if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){ + RegCloseKey( key ); + return (TRUE); + } + } + } + } + + RegCloseKey ( key ); + } + + + + /* + ** Check HKEY_LOCAL_MACKINE/Software + */ + user_handle_size = sizeof (user_handle); + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + memcpy (buffer, user_handle, min((unsigned)buffer_len, user_handle_size)); + + /* + ** If the first char of the users name is non-numeric then return success + */ + if (user_handle[0] < '0' || user_handle[0] > '9'){ + return (TRUE); + }else{ + return (FALSE); + } +#endif +} + + + +/*********************************************************************************************** + * Spawn_WChat -- spawns or switches focus to wchat * + * * + * * + * * + * INPUT: can launch. If set then we are allowed to launch WChat if not already running * + * * + * OUTPUT: True if wchat was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +bool Poke_WChat(void); +bool Spawn_WChat(bool can_launch) +{ + can_launch; + return false; + +#if (0) + CCDebugString ("C&C95 - In Spawn_WChat.\n"); + char packet[10] = {"Hello"}; + HWND chat_window = NULL; + + /* + ** See if WChat is already running... + */ + if (WChatHWND && IsWindow (WChatHWND) ){ + chat_window = WChatHWND; + }else{ + chat_window = FindWindow ( "OWL_Window", "Westwood Chat" ); + } + + if (chat_window){ + /* + ** WChat is already running. Minimize myself then try to give it focus. + */ + Set_Palette(BlackPalette); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard::Check(); + } + + /* + ** Send chat a tickle message so it knows to send the game stats to the server. + */ + if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) { + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + } + /* + ** Give the focus to WChat + */ + SetForegroundWindow ( chat_window ); + ShowWindow ( chat_window, SW_RESTORE ); + return(true); + } + + /* + ** Fail if we aren't allowed to launch wchat and we couldnt find its window. + */ + if (!can_launch) return (false); + + /* + ** Find where WChat was installed to + */ + + HKEY key; + char wchat_loc[256]; + DWORD wchat_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "WChat", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + char justpath [256]; + Just_Path(wchat_loc, justpath); + + /* + ** We found WChat in the registry. Minimize myself then try to spawn it. + */ + Set_Palette(BlackPalette); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard::Check(); + } + bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + + if (success){ + return (true); + }else{ + ShowWindow (MainWindow, SW_RESTORE); + while ( Keyboard::Check() ) {}; + return (false); + } +#endif + +} + + + + +/*********************************************************************************************** + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: True if app was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +bool Spawn_Registration_App(void) +{ + return false; +#if (0) //ST - 1/2/2019 5:53PM + /* + ** Find where inetreg was installed to + */ + + HKEY key; + char inetreg_loc[256]; + DWORD inetreg_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + char justpath [256]; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + Just_Path(inetreg_loc, justpath); + + BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + if (success){ + //WaitForSingleObject (process_info.hProcess, 1000*10000); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_RESTORE ); + } + return (success); +#endif + +} + + + + + +/*********************************************************************************************** + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 8:30PM ST : Created * + *=============================================================================================*/ +bool Do_The_Internet_Menu_Thang(void) +{ +#ifndef DEMO + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + char packet[10] = {"Hello"}; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String (TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //buttons = &cancelbtn; + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + + char users_name[256]; + int buffer_len = sizeof (users_name); + bool process; + bool display; + KeyNumType input; + + + if (!Special.IsFromWChat && !SpawnedFromWChat){ + /* + ** If the user is registered with Planet Westwood then spawn WChat. + */ + if (Is_User_WChat_Registered(users_name, buffer_len)){ + GameStatisticsPacketSent = false; + if (!Spawn_WChat(true)){ + Set_Logic_Page(SeenBuff); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + CCMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK); + LogicPage->Clear(); + return(false); + } + }else{ + Set_Logic_Page(SeenBuff); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + if (CCMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_REGISTER, TXT_CANCEL)){ + LogicPage->Clear(); + return(false); + }else{ + LogicPage->Clear(); + Spawn_Registration_App(); + return(false); + } + } + } + + /* + ** + ** User is registered and we spawned WChat. Wait for a game start message from WChat. + ** + */ + + process = true; + display = true; + + while (process){ + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored = FALSE; + display = true; + } + + if (display) { + + Set_Logic_Page(SeenBuff); + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + //cancelbtn.Zap(); + //buttons = &cancelbtn; + + /* + .................... Rebuild the button list .................... + */ + //buttons->Draw_All(); + cancelbtn.Draw_Me(true); + + Show_Mouse(); + display = false; + } + + + + /* + ** See if the game start packet has arrived from wchat yet. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + return(true); + } + + //input = buttons->Input(); + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + } + + } + +#endif //DEMO + + return (false); + + +} + + + + + + diff --git a/TIBERIANDAWN/INTERPAL.CPP b/TIBERIANDAWN/INTERPAL.CPP new file mode 100644 index 000000000..fdc643053 --- /dev/null +++ b/TIBERIANDAWN/INTERPAL.CPP @@ -0,0 +1,457 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTERPAL.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : December 7th 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This module contains functions to allow use of old 320x200 animations on a 640x400 screen * + * * + * Functions: * + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * Write_Interpolation_Palette -- writes an interpolation palette to disk * + * Create_Palette_Interpolation_Table -- build the palette interpolation table * + * Increase_Palette_Luminance -- increase the contrast of a palette * + * Interpolate_2X_Scale -- Stretch a 320x200 graphic buffer into 640x400 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +BOOL InterpolationPaletteChanged = FALSE; +extern "C" { +extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Double (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} + +extern "C"{ + unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + unsigned char *InterpolationPalette; +} + + + +/*********************************************************************************************** + * Read_Interpolatioin_Palette -- reads an interpolation palette table from disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Read_Interpolation_Palette (char const *palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (palette_file.Is_Available()){ + palette_file.Open(READ); + palette_file.Read(&PaletteInterpolationTable[0][0],256*256); + palette_file.Close(); + InterpolationPaletteChanged = FALSE; + } +} + + +/*********************************************************************************************** + * Write_Interpolatioin_Palette -- writes an interpolation palette table to disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Write_Interpolation_Palette (char const *palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (!palette_file.Is_Available()){ + palette_file.Open(WRITE); + palette_file.Write(&PaletteInterpolationTable[0][0],256*256); + palette_file.Close(); + } +} + + + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + +//Don't think we need this. ST - 12/20/2018 2:25PM + //Asm_Create_Palette_Interpolation_Table(); + + #if (0) + + int i; + int j; + int p; + unsigned char *first_palette_ptr; + unsigned char *second_palette_ptr; + unsigned char *match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) InterpolationPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) InterpolationPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) InterpolationPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + PaletteInterpolationTable[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + #endif + InterpolationPaletteChanged = FALSE; + return; + +} + + + + + + + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * cap value for colours * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage ,int cap) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iGet_IsDirectDraw()){ + if (!source->Lock()){ + if (dest == &SeenBuff) Show_Mouse(); + return; + } + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()){ + if (!dest->Lock()) { + if (source_locked){ + source->Unlock(); + } + if (dest == &SeenBuff) Show_Mouse(); + return; + } + dest_locked = TRUE; + } + + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + /* + ** Call the appropriate assembly language copy routine + */ +#if (1) + switch (CopyType){ + case 0: + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 1: + Asm_Interpolate_Line_Double( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 2: + Asm_Interpolate_Line_Interpolate( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + } +#endif + +#if (0) + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = PaletteInterpolationTable[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + last_dest_ptr += dest_width; + dest_ptr = last_dest_ptr; + } + } + +#endif + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + if (dest == &SeenBuff) Show_Mouse(); +#endif +} +#endif + + + diff --git a/TIBERIANDAWN/INTRO.CPP b/TIBERIANDAWN/INTRO.CPP new file mode 100644 index 000000000..c1a0af3da --- /dev/null +++ b/TIBERIANDAWN/INTRO.CPP @@ -0,0 +1,323 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\intro.cpv 1.6 16 Oct 1995 16:50:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +#ifndef DEMO + + +#if (0) //PG_TO_FIX +VQAHandle *Open_Movie(char *name); +VQAHandle *Open_Movie(char *name) +{ + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + VQAHandle * vqa = VQA_Alloc(); + if (vqa) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, name, &AnimControl) != 0) { + VQA_Free(vqa); + vqa = 0; + } + } + return(vqa); +} +#endif //(0) + +/*********************************************************************************************** + * Choose_Side -- play the introduction movies, select house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 5/08/1995 BWG : Created. * + *=============================================================================================*/ +void Choose_Side(void) +{ + //static char const _yellowpal[]={0x0,0x0,0xC9,0x0,0xBA,0x0,0x93,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0xEE,0x0}; + //static char const _redpal[] ={0x0,0x0,0xA8,0x0,0xD9,0x0,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + //static char const _graypal[] ={0x0,0x0,0x17,0x0,0x10,0x0,0x12,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + // PG_TO_FIX + // Chose GDI + Whom = HOUSE_GOOD; + ScenPlayer = SCEN_PLAYER_GDI; + return; + +#if (0) + + + static char const _yellowpal[]={0x0,0xC9,0xBA,0x93,0x61,0xEE,0xee,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}; + static char const _redpal[] ={0x0,0xa8,0xd9,0xda,0xe1,0xd4,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + static char const _graypal[] ={0x0,0x17,0x10,0x12,0x14,0x1c,0x12,0x1c,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + + void *anim; + VQAHandle *gdibrief=0, *nodbrief=0; + void const *staticaud, *oldfont; + void const *speechg, *speechn, *speech; + int statichandle, speechhandle, speechplaying = 0; + int oldfontxspacing = FontXSpacing; + int setpalette = 0; + int gdi_start_palette; + + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + int frame = 0, endframe = 255, selection = 0, lettersdone = 0; + + Hide_Mouse(); +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + + Call_Back(); + + staticaud = Load_Alloc_Data(CCFileClass("STRUGGLE.AUD")); + speechg = Load_Alloc_Data(CCFileClass("GDI_SLCT.AUD")); + speechn = Load_Alloc_Data(CCFileClass("NOD_SLCT.AUD")); + +// staticaud = MixFileClass::Retrieve("STRUGGLE.AUD"); +// speechg = MixFileClass::Retrieve("GDI_SLCT.AUD"); +// speechn = MixFileClass::Retrieve("NOD_SLCT.AUD"); + + if (Special.IsFromInstall){ + if (mem_info.dwTotalPhys >= 12*1024*1024){ + VisiblePage.Clear(); + PreserveVQAScreen = 1; + Play_Movie("INTRO2", THEME_NONE, false); + } + BreakoutAllowed = true; + } + + //anim = Open_Animation("CHOOSE.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("CHOOSE.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_DISK | WSA_OPEN_TO_PAGE),Palette); + Call_Back(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Read_Interpolation_Palette("SIDES.PAL"); + + nodbrief = Open_Movie("NOD1PRE.VQA"); + gdi_start_palette = Load_Interpolated_Palettes("NOD1PRE.VQP"); + Call_Back(); + gdibrief = Open_Movie("GDI1.VQA"); + Load_Interpolated_Palettes("GDI1.VQP" , TRUE); + + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + PseudoSeenBuff->Clear(); + SysMemPage.Clear(); + //if (!Special.IsFromInstall) { + VisiblePage.Clear(); + Set_Palette(Palette); + //} else { + //setpalette = 1; + //} + + statichandle = Play_Sample(staticaud,255,64); + CountDownTimerClass sample_timer; + sample_timer.Set(0x3f); + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME, 0, 180,_yellowpal)); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME2, 0, 187,_yellowpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_NOD_NAME, 180, 180,_redpal)); + +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,57, 190,_graypal)); +#else +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,103, 194,_graypal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,103, 190,_graypal)); +#endif +#endif + Keyboard::Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + while (endframe != frame || (speechplaying && Is_Sample_Playing(speech)) ) { + Animate_Frame(anim, SysMemPage, frame++); + if (setpalette) { + Wait_Vert_Blank(); + Set_Palette(Palette); + setpalette = 0; + } + SysMemPage.Blit(*PseudoSeenBuff,0,22, 0,22, 320,156); + + /* + ** If the sample has stopped or is about to then restart it + */ + if (!Is_Sample_Playing(staticaud) || !sample_timer.Time()) { + Stop_Sample(statichandle); + statichandle = Play_Sample(staticaud,255,64); + sample_timer.Set(0x3f); + } + Call_Back_Delay(3); // delay only if haven't clicked + + /* keep the mouse hidden until the letters are thru printing */ + if (!lettersdone) { + lettersdone = true; + for(int i=0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) lettersdone = 0; + if (lettersdone) { + Show_Mouse(); + } + } + if (frame >= Get_Animation_Frame_Count(anim)) frame = 0; + if (Keyboard::Check() && endframe == 255) { + if ((Keyboard::Get() & 0x10FF) == KN_LMOUSE) { + if ((_Kbd->MouseQY > 48*2) && (_Kbd->MouseQY < 150*2)) { + if ((_Kbd->MouseQX > 18*2) && (_Kbd->MouseQX < 148*2)) { + + // Chose GDI + Whom = HOUSE_GOOD; + ScenPlayer = SCEN_PLAYER_GDI; + endframe = 0; + speechhandle = Play_Sample(speechg); + speechplaying = true; + speech = speechg; + + } else if ((_Kbd->MouseQX > 160*2) && (_Kbd->MouseQX < 300*2)) { + // Chose Nod + selection = 1; + endframe = 14; + Whom = HOUSE_BAD; + ScenPlayer = SCEN_PLAYER_NOD; + speechhandle = Play_Sample(speechn); + speechplaying = true; + speech = speechn; + } + } + } + } + } + + Hide_Mouse(); + Close_Animation(anim); + + // erase the "choose side" text + PseudoSeenBuff->Fill_Rect(0,180,319,199,0); + SeenBuff.Fill_Rect(0,180*2, 319*2, 199*2, 0); + Interpolate_2X_Scale (PseudoSeenBuff , &SeenBuff ,"SIDES.PAL"); + Keyboard::Clear(); + SysMemPage.Clear(); + + /* + ** Skip the briefings if we're in special mode. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + nodbrief = NULL; + } + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + gdibrief = NULL; + } + } + + /* play the scenario 1 briefing movie */ + if (Whom == HOUSE_GOOD) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + if (gdibrief) { + PaletteCounter = gdi_start_palette; + VQA_Play(gdibrief, VQAMODE_RUN); + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + } else { + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + if (nodbrief) { + VQA_Play(nodbrief, VQAMODE_RUN); + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + } + + Free_Interpolated_Palettes(); + Set_Primary_Buffer_Format(); +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + if (Whom == HOUSE_GOOD) { + /* + ** Make sure the screen's fully clear after the movie plays + */ + VisiblePage.Clear(); + memset(BlackPalette, 0x01, 768); + Set_Palette(BlackPalette); + memset(BlackPalette, 0x00, 768); + } else { + PreserveVQAScreen = 1; + } + Free(staticaud); + Free(speechg); + Free(speechn); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + + delete PseudoSeenBuff; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); + +#endif +} +#endif diff --git a/TIBERIANDAWN/INTRO.H b/TIBERIANDAWN/INTRO.H new file mode 100644 index 000000000..01f5ed671 --- /dev/null +++ b/TIBERIANDAWN/INTRO.H @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTRO_H +#define INTRO_H + +void Choose_Side(void); + +#endif diff --git a/TIBERIANDAWN/IOMAP.CPP b/TIBERIANDAWN/IOMAP.CPP new file mode 100644 index 000000000..bf8f4c0a2 --- /dev/null +++ b/TIBERIANDAWN/IOMAP.CPP @@ -0,0 +1,1104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\iomap.cpv 2.18 16 Oct 1995 16:50:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOMAP.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : January 16, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * All map-related loading/saving routines should go in this module, so it can be overlayed. * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * CellClass::Load -- Reads from a save game file. * + * CellClass::Save -- Write to a save game file. * + * CellClass::Should_Save -- Should the cell be written to disk? * + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * GScreenClass::Code_Pointers -- codes class's pointers for load/save * + * GScreenClass::Decode_Pointers -- decodes pointers for load/save * + * HelpClass::Code_Pointers -- codes class's pointers for load/save * + * HelpClass::Decode_Pointers -- decodes pointers for load/save * + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Code_Pointers -- codes class's pointers for load/save * + * MouseClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Load -- Loads from a save game file. * + * MouseClass::Save -- Saves to a save game file. * + * PowerClass::Code_Pointers -- codes class's pointers for load/save * + * PowerClass::Decode_Pointers -- decodes pointers for load/save * + * RadarClass::Code_Pointers -- codes class's pointers for load/save * + * RadarClass::Decode_Pointers -- decodes pointers for load/save * + * ScrollClass::Code_Pointers -- codes class's pointers for load/save * + * ScrollClass::Decode_Pointers -- decodes pointers for load/save * + * SidebarClass::Code_Pointers -- codes class's pointers for load/save * + * SidebarClass::Decode_Pointers -- decodes pointers for load/save * + * SidebarClass::StripClass::Code_Pointers -- codes class's pointers for load/save * + * SidebarClass::StripClass::Decode_Pointers -- decodes pointers for load/save * + * TabClass::Code_Pointers -- codes class's pointers for load/save * + * TabClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#pragma warning (disable : 4302) // Truncation from pointer to TARGET + +/*********************************************************************************************** + * CellClass::Should_Save -- Should the cell be written to disk? * + * * + * This function will determine if the cell needs to be written to disk. Any cell that * + * contains special data should be written to disk. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should this cell's data be written to disk? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Should_Save(void) const +{ + return( + (Smudge != SMUDGE_NONE) || + (TType != TEMPLATE_NONE) || + (Overlay != OVERLAY_NONE) || + IsMapped || + IsVisible || + IsMappedByPlayerMask || + IsVisibleByPlayerMask || + IsTrigger || + Flag.Composite || + OccupierPtr || + Overlapper[0] || Overlapper[1] || Overlapper[2] + ); +} + + +/*********************************************************************************************** + * CellClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Load(FileClass & file) +{ + int rc; + TriggerClass * trig; + + /* + -------------------------- Load the object data -------------------------- + */ + rc = Read_Object(this, sizeof(CellClass), file, false); + + /* + ------------------------ Load the trigger pointer ------------------------ + */ + if (rc) { + if (IsTrigger) { + if (file.Read(&trig,sizeof(trig)) != sizeof(trig)) + return(false); + CellTriggers[Cell_Number()] = trig; + } + } + + return(rc); +} + + +/*********************************************************************************************** + * CellClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Save(FileClass & file) +{ + int rc; + TriggerClass * trig; + + /* + -------------------------- Save the object data -------------------------- + */ + rc = Write_Object(this, sizeof(CellClass), file); + + /* + ------------------------ Save the trigger pointer ------------------------ + */ + if (rc) { + if (IsTrigger) { + trig = CellTriggers[Cell_Number()]; + if (file.Write(&trig,sizeof(trig)) != sizeof(trig)) + return(false); + } + } + + return(rc); +} + + +/*********************************************************************************************** + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Code_Pointers(void) +{ + if (Cell_Occupier()) { + OccupierPtr = (ObjectClass *)OccupierPtr->As_Target(); + } + + if (Overlapper[0]) { + Overlapper[0] = (ObjectClass *)Overlapper[0]->As_Target(); + } + + if (Overlapper[1]) { + Overlapper[1] = (ObjectClass *)Overlapper[1]->As_Target(); + } + + if (Overlapper[2]) { + Overlapper[2] = (ObjectClass *)Overlapper[2]->As_Target(); + } + + /* + ------------------------ Convert trigger pointer ------------------------- + */ + if (IsTrigger) { + CellTriggers[Cell_Number()] = (TriggerClass *)CellTriggers[Cell_Number()]->As_Target(); + } +} + + +/*********************************************************************************************** + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Decode_Pointers(void) +{ + char bad[128]; + + if (OccupierPtr) { + OccupierPtr = As_Object((TARGET)OccupierPtr, false); + Check_Ptr((void *)OccupierPtr,__FILE__,__LINE__); + + /* + ** Check for bad occupier that was saved. ST - 10/3/2019 11:50AM + */ + ObjectClass * optr = Cell_Occupier(); + if (optr->IsActive == false) { + CellClass *cell0 = &Map[0]; + int cell_number = this - cell0; + sprintf(bad, "Found bad cell occupier in cell %d", cell_number); + GlyphX_Debug_Print(bad); + OccupierPtr = NULL; + } + } + + if (Overlapper[0]) { + Overlapper[0] = As_Object((TARGET)Overlapper[0], false); + Check_Ptr((void *)Overlapper[0],__FILE__,__LINE__); + } + + if (Overlapper[1]) { + Overlapper[1] = As_Object((TARGET)Overlapper[1], false); + Check_Ptr((void *)Overlapper[1],__FILE__,__LINE__); + } + + if (Overlapper[2]) { + Overlapper[2] = As_Object((TARGET)Overlapper[2], false); + Check_Ptr((void *)Overlapper[2],__FILE__,__LINE__); + } + + /* + ** Check for bad overlappers that were saved. ST - 10/3/2019 11:50AM + */ + for (int i=0 ; i <= 2 ; i++) { + if (Overlapper[i]) { + ObjectClass * optr = Overlapper[i]; + if (optr->IsActive == false) { + CellClass *cell0 = &Map[0]; + int cell_number = this - cell0; + sprintf(bad, "Found bad cell overlapper in slot %d of cell %d", i, cell_number); + GlyphX_Debug_Print(bad); + Overlapper[i] = NULL; + } + } + } + + + /* + ** Convert trigger pointer. + */ + if (IsTrigger) { + CellTriggers[Cell_Number()] = As_Trigger( (TARGET)CellTriggers[Cell_Number()], false ); + Check_Ptr((void *)CellTriggers[Cell_Number()],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * MouseClass::Load -- Loads from a save game file. * + * * + * Loading the map is very complicated. Here are the steps: * + * - Read the Theater for this save-game * + * - call Init_Theater to perform theater-specific inits * + * - call Free_Cells to free the cell array, because loading the map object will overwrite * + * the pointer to the cell array * + * - read the map object from disk * + * - call Alloc_Cells to re-allocate the cell array * + * - call Init_Cells to set the cells to a known state, because not every cell will be loaded * + * - read the cell objects into the cell array * + * - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be * + * called to restore the map's button list to the proper state. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool MouseClass::Load(FileClass & file) +{ + unsigned count; + CELL cell = 0; + int index; +// int rc; +// int i; +// int j; + + /*------------------------------------------------------------------------ + Load Theater: Even though this value is located in the DisplayClass, + it must be loaded first so initialization can be done before any other + map data is loaded. If initialization isn't done first, data read from + disk will be over-written when initialization occurs. This code must + go in the most-derived Map class. + ------------------------------------------------------------------------*/ + if (file.Read (&Theater,sizeof(Theater)) != sizeof(Theater)) + return(false); + + /* + ** Remove any old theater specific uncompressed shapes + */ + if (Theater != LastTheater){ + Reset_Theater_Shapes(); + } + + /* + ------------------------- Init display mixfiles -------------------------- + */ + Init_Theater(Theater); + TerrainTypeClass::Init(Theater); + TemplateTypeClass::Init(Theater); + OverlayTypeClass::Init(Theater); + UnitTypeClass::Init(Theater); + InfantryTypeClass::Init(Theater); + BuildingTypeClass::Init(Theater); + BulletTypeClass::Init(Theater); + AnimTypeClass::Init(Theater); + AircraftTypeClass::Init(Theater); + SmudgeTypeClass::Init(Theater); + + LastTheater = Theater; + + /* + ** Free the cell array, because we're about to overwrite its pointers + */ + Free_Cells(); + + /* + ** Read the entire map object in. Only read in sizeof(MouseClass), so if we're + ** in editor mode, none of the map editor object is read in. + */ + if (!Read_Object(this, sizeof(MouseClass), file, true)) { + return(false); + } + + /* + ** Reallocate the cell array + */ + Alloc_Cells(); + + /* + ** Init all cells to empty + */ + Init_Cells(); + + /* + --------------------------- Read # cells saved --------------------------- + */ + if (file.Read(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ------------------------------- Read cells ------------------------------- + */ + for (index = 0; index < (int)count; index++) { + if (file.Read(&cell, sizeof(cell)) != sizeof(cell)) + return(false); + + if (!(*this)[cell].Load(file)) + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Save -- Save to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool MouseClass::Save(FileClass & file) +{ + unsigned count; + long pos; + + /* + -------------------------- Save Theater >first< -------------------------- + */ + if (file.Write (&Theater,sizeof(Theater)) != sizeof(Theater)) + return(false); + + if (!Write_Object(this, sizeof(MouseClass), file)) + return(false); + + /* + ---------------------- Record current file position ---------------------- + */ + pos = file.Seek(0, SEEK_CUR); + + /* + ---------------------- write out placeholder bytes ----------------------- + */ + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + /* + ------------------------ Save cells that need it ------------------------- + */ + count = 0; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + + if ((*this)[cell].Should_Save()) { + if (file.Write(&cell, sizeof(cell)) != sizeof(cell)) + return(false); + + count++; + + if (!(*this)[cell].Save(file)) + return(false); + } + } + + /* + -------------------------- Save # cells written -------------------------- + */ + file.Seek(pos, SEEK_SET); + + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + file.Seek(0, SEEK_END); + + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MouseClass::Code_Pointers(void) +{ +// Control.Code_Pointers(); + + ScrollClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MouseClass::Decode_Pointers(void) +{ +// Control.Decode_Pointers(); + + ScrollClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * ScrollClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScrollClass::Code_Pointers(void) +{ + HelpClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * ScrollClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScrollClass::Decode_Pointers(void) +{ + HelpClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * HelpClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HelpClass::Code_Pointers(void) +{ + TabClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * HelpClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HelpClass::Decode_Pointers(void) +{ + TabClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TabClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TabClass::Code_Pointers(void) +{ + SidebarClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TabClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TabClass::Decode_Pointers(void) +{ + SidebarClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * PowerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void PowerClass::Code_Pointers(void) +{ + RadarClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * PowerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void PowerClass::Decode_Pointers(void) +{ + RadarClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::Code_Pointers(void) +{ + for (int i = 0; i < COLUMNS; i++) { + Column[i].Code_Pointers(); + } + + PowerClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::Decode_Pointers(void) +{ + for (int i = 0; i < COLUMNS; i++) { + Column[i].Decode_Pointers(); + } + + PowerClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * RadarClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadarClass::Code_Pointers(void) +{ + DisplayClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadarClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadarClass::Decode_Pointers(void) +{ + DisplayClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Code_Pointers(void) +{ + /* + ** Code PendingObjectPtr. + */ + if (PendingObjectPtr) { + PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target(); + } + + /* + ** Fix for saving game while in structure placement mode. ST - 4/15/2020 2:41PM + */ + memset(CursorShapeSave, 0, sizeof(CursorShapeSave)); + if (CursorSize && CursorSize != CursorShapeSave) { + + int save_buffer_element_size = sizeof(CursorShapeSave) / sizeof(CursorShapeSave[0]); + + int index = 0; + + while (index < save_buffer_element_size - 2 && CursorSize[index] != REFRESH_EOL) { + CursorShapeSave[index] = CursorSize[index]; + index++; + } + CursorShapeSave[index] = REFRESH_EOL; + } + + /* + ** Chain to parent. + */ + MapClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Decode_Pointers(void) +{ + /* + ** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd + ** have to reference PendingObjectPtr->Class_Of(), and the object that + ** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't + ** decode PendingObjectPtr, we can't set the placement cursor shape here + ** either. These have to be done as last-minute fixups. + */ + if (PendingObjectPtr) { + PendingObjectPtr = As_Object((TARGET)PendingObjectPtr, false); + Check_Ptr((void *)PendingObjectPtr,__FILE__,__LINE__); + } + + if (CursorSize) { + CursorSize = CursorShapeSave; + } + + /* + ** Chain to parent. + */ + MapClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Code_Pointers(void) +{ + CELL cell; + + /* + ------------------------- Code the cell pointers ------------------------- + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + (*this)[cell].Code_Pointers(); + } + + GScreenClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Decode_Pointers(void) +{ + CELL cell; + + /* + ------------------------ Decode the cell pointers ------------------------ + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + (*this)[cell].Decode_Pointers(); + } + + GScreenClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * GScreenClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Decode_Pointers(void) +{ +} \ No newline at end of file diff --git a/TIBERIANDAWN/IOOBJ.CPP b/TIBERIANDAWN/IOOBJ.CPP new file mode 100644 index 000000000..17ae31df0 --- /dev/null +++ b/TIBERIANDAWN/IOOBJ.CPP @@ -0,0 +1,2698 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ioobj.cpv 2.18 16 Oct 1995 16:51:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOOBJ.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : January 16, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * All object-related loading/saving routines should go in this module, so it can be overlayed.* + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamTypeClass::Load -- Reads from a save game file. * + * TeamTypeClass::Save -- Write to a save game file. * + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * TeamClass::Load -- Reads from a save game file. * + * TeamClass::Save -- Write to a save game file. * + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * TriggerClass::Load -- Reads from a save game file. * + * TriggerClass::Save -- Write to a save game file. * + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * AircraftClass::Load -- Reads from a save game file. * + * AircraftClass::Save -- Write to a save game file. * + * AircraftClass::Code_Pointers -- codes class's pointers for load/save * + * AircraftClass::Decode_Pointers -- decodes pointers for load/save * + * AnimClass::Load -- Reads from a save game file. * + * AnimClass::Save -- Write to a save game file. * + * AnimClass::Code_Pointers -- codes class's pointers for load/save * + * AnimClass::Decode_Pointers -- decodes pointers for load/save * + * BuildingClass::Load -- Reads from a save game file. * + * BuildingClass::Save -- Write to a save game file. * + * BuildingClass::Code_Pointers -- codes class's pointers for load/save * + * BuildingClass::Decode_Pointers -- decodes pointers for load/save * + * BulletClass::Load -- Reads from a save game file. * + * BulletClass::Save -- Write to a save game file. * + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * InfantryClass::Load -- Reads from a save game file. * + * InfantryClass::Save -- Write to a save game file. * + * InfantryClass::Code_Pointers -- codes class's pointers for load/save * + * InfantryClass::Decode_Pointers -- decodes pointers for load/save * + * OverlayClass::Load -- Reads from a save game file. * + * OverlayClass::Save -- Write to a save game file. * + * OverlayClass::Code_Pointers -- codes class's pointers for load/save * + * OverlayClass::Decode_Pointers -- decodes pointers for load/save * + * ReinforcementClass::Load -- Reads from a save game file. * + * ReinforcementClass::Save -- Write to a save game file. * + * ReinforcementClass::Code_Pointers -- codes class's pointers for load/save * + * ReinforcementClass::Decode_Pointers -- decodes pointers for load/save * + * SmudgeClass::Load -- Reads from a save game file. * + * SmudgeClass::Save -- Write to a save game file. * + * SmudgeClass::Code_Pointers -- codes class's pointers for load/save * + * SmudgeClass::Decode_Pointers -- decodes pointers for load/save * + * TemplateClass::Load -- Reads from a save game file. * + * TemplateClass::Save -- Write to a save game file. * + * TemplateClass::Code_Pointers -- codes class's pointers for load/save * + * TemplateClass::Decode_Pointers -- decodes pointers for load/save * + * TerrainClass::Load -- Reads from a save game file. * + * TerrainClass::Save -- Write to a save game file. * + * TerrainClass::Code_Pointers -- codes class's pointers for load/save * + * TerrainClass::Decode_Pointers -- decodes pointers for load/save * + * UnitClass::Load -- Reads from a save game file. * + * UnitClass::Save -- Write to a save game file. * + * UnitClass::Code_Pointers -- codes class's pointers for load/save * + * UnitClass::Decode_Pointers -- decodes pointers for load/save * + * FactoryClass::Load -- Reads from a save game file. * + * FactoryClass::Save -- Write to a save game file. * + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Load -- Reads from a save game file. * + * LayerClass::Save -- Write to a save game file. * + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * HouseClass::Load -- Reads from a save game file. * + * HouseClass::Save -- Write to a save game file. * + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * ScoreClass::Load -- Reads from a save game file. * + * ScoreClass::Save -- Write to a save game file. * + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * FlyClass::Code_Pointers -- codes class's pointers for load/save * + * FlyClass::Decode_Pointers -- decodes pointers for load/save * + * FuseClass::Code_Pointers -- codes class's pointers for load/save * + * FuseClass::Decode_Pointers -- decodes pointers for load/save * + * TarComClass::Code_Pointers -- codes class's pointers for load/save * + * TarComClass::Decode_Pointers -- decodes pointers for load/save * + * TurretClass::Code_Pointers -- codes class's pointers for load/save * + * TurretClass::Decode_Pointers -- decodes pointers for load/save * + * DriveClass::Code_Pointers -- codes class's pointers for load/save * + * DriveClass::Decode_Pointers -- decodes pointers for load/save * + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * FlasherClass::Code_Pointers -- codes class's pointers for load/save * + * FlasherClass::Decode_Pointers -- decodes pointers for load/save * + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * MissionClass::Code_Pointers -- codes class's pointers for load/save * + * MissionClass::Decode_Pointers -- decodes pointers for load/save * + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#pragma warning (disable : 4302) // Truncation from pointer to TARGET + + +/*********************************************************************************************** + * TeamTypeClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Load(FileClass & file) +{ + ::new (this) TeamTypeClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractTypeClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TeamTypeClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Code_Pointers(void) +{ + /* + -------------------------- Code the Class array -------------------------- + */ + for (int i = 0; i < ClassCount; i++) { + Class[i] = (TechnoTypeClass *)TechnoType_To_Target(Class[i]); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Decode_Pointers(void) +{ + /* + ------------------------- Decode the Class array ------------------------- + */ + for (int i = 0; i < ClassCount; i++) { + Class[i] = Target_To_TechnoType((TARGET)Class[i]); + Check_Ptr((void *)Class[i],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * TeamClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Load(FileClass & file) +{ + ::new (this) TeamClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TeamClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamClass::Code_Pointers(void) +{ + TeamTypeClass const * cls; + + /* + -------------------- Code Class & House for this team -------------------- + */ + cls = Class; + ((TeamTypeClass *&)Class) = (TeamTypeClass *)cls->As_Target(); + ((HouseClass *&)House) = (HouseClass *)House->Class->House; + + /* + --------------------------- Code the 'Member' ---------------------------- + */ + if (Member) { + Member = (FootClass *)Member->As_Target(); + } +} + + +/*********************************************************************************************** + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamClass::Decode_Pointers(void) +{ + /* + ------------------- Decode Class & House for this team ------------------- + */ + ((TeamTypeClass *&)Class) = As_TeamType((TARGET)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + ((HouseClass *&)House) = HouseClass::As_Pointer(*((HousesType*)&House)); + Check_Ptr((void *)House,__FILE__,__LINE__); + + /* + -------------------------- Decode the 'Member' --------------------------- + */ + if (Member) { + switch (Target_Kind((TARGET)Member)) { + case KIND_INFANTRY: + Member = As_Infantry((TARGET)Member, false); + break; + + case KIND_UNIT: + Member = As_Unit((TARGET)Member, false); + break; + + case KIND_AIRCRAFT: + Member = As_Aircraft((TARGET)Member, false); + break; + + default: + Member = 0; + break; + } + + Check_Ptr((void *)Member,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * TriggerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Load(FileClass & file) +{ + ::new (this) TriggerClass(); + int rc = Read_Object(this, sizeof(*this), file, false); + + //int rc = Read_Object(this, sizeof(*this), sizeof(*this), file, 0); + + /* + -------------------------- Add to HouseTriggers -------------------------- + */ + if (rc) { + if (House != HOUSE_NONE) { + HouseTriggers[House].Add(this); + } + } + + return(rc); +} + + +/*********************************************************************************************** + * TriggerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Code_Pointers(void) +{ + if (Team) { + Team = (TeamTypeClass *)Team->As_Target(); + } +} + + +/*********************************************************************************************** + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Decode_Pointers(void) +{ + if (Team) { + Team = As_TeamType((TARGET)Team); + Check_Ptr((void *)Team,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * AircraftClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Load(FileClass & file) +{ + ::new (this) AircraftClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * AircraftClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * AircraftClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AircraftClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((AircraftTypeClass *&)Class) = (AircraftTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); + FlyClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * AircraftClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AircraftClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((AircraftTypeClass const *&)Class) = &AircraftTypeClass::As_Reference(*((AircraftType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); + FlyClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * AnimClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Load(FileClass & file) +{ + ::new (this) AnimClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * AnimClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * AnimClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AnimClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((AnimTypeClass *&)Class) = (AnimTypeClass *)Class->Type; + + /* + ----------------------------- Code 'Object' ------------------------------ + */ + if (Object) { + Object = (ObjectClass *)Object->As_Target(); + } + + /* + ----------------------------- Code 'VirtualAnim' ------------------------- + */ + if (VirtualAnim) { + VirtualAnim = (AnimClass *)VirtualAnim->As_Target(); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * AnimClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AnimClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((AnimTypeClass const *&)Class) = &AnimTypeClass::As_Reference(*((AnimType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Decode 'Object' ----------------------------- + */ + if (Object) { + Object = As_Object((TARGET)Object, false); + Check_Ptr((void *)Object,__FILE__,__LINE__); + } + + /* + ---------------------------- Decode 'VirtualAnim' ------------------------ + */ + if (VirtualAnim) { + VirtualAnim = As_Animation((TARGET)VirtualAnim, false); + Check_Ptr((void *)VirtualAnim, __FILE__, __LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * BuildingClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Load(FileClass & file) +{ + ::new (this) BuildingClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * BuildingClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * BuildingClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BuildingClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((BuildingTypeClass const *&)Class) = (BuildingTypeClass *)Class->Type; + + /*------------------------------------------------------------------------ + Code the Factory value; there's not target conversion routine for factories, + so just use its Array ID, plus 1 so it doesn't look like a NULL value when + it's converted back + ------------------------------------------------------------------------*/ + if (Factory) { + Factory = (FactoryClass *)(Factories.ID(Factory) + 1); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BuildingClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BuildingClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((BuildingTypeClass const *&)Class) = &BuildingTypeClass::As_Reference(*((StructType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /*------------------------------------------------------------------------ + Decode the Factory value, subtracting off the '1' we added when coding it + ------------------------------------------------------------------------*/ + if (Factory) { + Factory = Factories.Raw_Ptr((int)Factory - 1); + Check_Ptr((void *)Factory,__FILE__,__LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Load(FileClass & file) +{ + ::new (this) BulletClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * BulletClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((BulletTypeClass *&)Class) = (BulletTypeClass *)Class->Type; + + /* + ----------------------------- Code 'Payback' ----------------------------- + */ + if (Payback) + Payback = (TechnoClass *)Payback->As_Target(); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + FlyClass::Code_Pointers(); + FuseClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((BulletTypeClass const *&)Class) = &BulletTypeClass::As_Reference(*((BulletType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Decode 'Payback' ---------------------------- + */ + if (Payback) { + Payback = As_Techno((TARGET)Payback, false); + Check_Ptr((void *)Payback,__FILE__,__LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + FlyClass::Decode_Pointers(); + FuseClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * InfantryClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Load(FileClass & file) +{ + ::new (this) InfantryClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * InfantryClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * InfantryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void InfantryClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((InfantryTypeClass *&)Class) = (InfantryTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * InfantryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void InfantryClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((InfantryTypeClass const *&)Class) = &InfantryTypeClass::As_Reference(*((InfantryType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * OverlayClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayClass::Load(FileClass & file) +{ + ::new (this) OverlayClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * OverlayClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * OverlayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void OverlayClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((OverlayTypeClass *&)Class) = (OverlayTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * OverlayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void OverlayClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((OverlayTypeClass const *&)Class) = &OverlayTypeClass::As_Reference(*((OverlayType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SmudgeClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeClass::Load(FileClass & file) +{ + ::new (this) SmudgeClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * SmudgeClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * SmudgeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SmudgeClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((SmudgeTypeClass const *&)Class) = (SmudgeTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * SmudgeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SmudgeClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((SmudgeTypeClass const *&)Class) = &SmudgeTypeClass::As_Reference(*((SmudgeType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TemplateClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateClass::Load(FileClass & file) +{ + ::new (this) TemplateClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TemplateClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TemplateClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TemplateClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((TemplateTypeClass *&)Class) = (TemplateTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TemplateClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TemplateClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((TemplateTypeClass const *&)Class) = &TemplateTypeClass::As_Reference(*((TemplateType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TerrainClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Load(FileClass & file) +{ + ::new (this) TerrainClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TerrainClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TerrainClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TerrainClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((TerrainTypeClass *&)Class) = (TerrainTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TerrainClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TerrainClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((TerrainTypeClass const *&)Class) = &TerrainTypeClass::As_Reference(*((TerrainType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * UnitClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Load(FileClass & file) +{ + ::new (this) UnitClass(); + return(Read_Object(this, sizeof(*this), file, true)); + + //return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * UnitClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * UnitClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void UnitClass::Code_Pointers(void) +{ + TarComClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * UnitClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void UnitClass::Decode_Pointers(void) +{ + TarComClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Load(FileClass & file) +{ + ::new (this) FactoryClass(); + return(Read_Object(this, sizeof(*this), file, false)); + + //return(Read_Object(this, sizeof(StageClass), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * FactoryClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Code_Pointers(void) +{ + if (Object) { + Object = (TechnoClass *)Object->As_Target(); + } + + ((HouseClass *&)House) = (HouseClass *)House->Class->House; + + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Decode_Pointers(void) +{ + if (Object) { + Object = As_Techno((TARGET)Object, false); + Check_Ptr((void *)Object,__FILE__,__LINE__); + } + + ((HouseClass *&)House) = HouseClass::As_Pointer(*((HousesType*)&House)); + Check_Ptr((void *)House,__FILE__,__LINE__); + + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * LayerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Load(FileClass & file) +{ + int count; + int i; + ObjectClass * ptr; + + /* + ---------------------- Read # elements in the layer ---------------------- + */ + if (file.Read(&count,sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ---------------------------- Clear the array ----------------------------- + */ + Clear(); + + /* + ----------------------- Read in all array elements ----------------------- + */ + for (i = 0; i < count; i++) { + if (file.Read(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) { + return(false); + } + Add(ptr); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Save(FileClass & file) +{ + int count; + int i; + ObjectClass * ptr; + + /* + ------------------------- Save # array elements -------------------------- + */ + count = Count(); + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + /* + --------------------------- Save all elements ---------------------------- + */ + for (i = 0; i < count; i++) { + ptr = (*this)[i]; + if (file.Write(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Code_Pointers(void) +{ + ObjectClass *obj; + + for (int i = 0; i < Count(); i++) { + obj = (*this)[i]; + (*this)[i] = (ObjectClass *)(obj->As_Target()); + } +} + + +/*********************************************************************************************** + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Decode_Pointers(void) +{ + TARGET target; + + for (int i = 0; i < Count(); i++) { + target = (TARGET)(*this)[i]; + (*this)[i] = (ObjectClass *)As_Object(target, false); + Check_Ptr((*this)[i],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * HouseClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Load(FileClass & file) +{ + ::new (this) HouseClass(); + return(Read_Object(this, sizeof(*this), file, false)); + + //return(Read_Object(this, sizeof(*this), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * HouseClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((HouseTypeClass const *&)Class) = (HouseTypeClass const *)Class->House; +} + + +/*********************************************************************************************** + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((HouseTypeClass const *&)Class) = &HouseTypeClass::As_Reference(*((HousesType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); +} + + +/*********************************************************************************************** + * ScoreClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ScoreClass::Load(FileClass & file) +{ + ::new (this) ScoreClass(); + return(Read_Object(this, sizeof(*this), file, false)); + + //return(Read_Object(this, sizeof(*this), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * ScoreClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ScoreClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlyClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlyClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlyClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlyClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FuseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FuseClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FuseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FuseClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TarComClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TarComClass::Code_Pointers(void) +{ + TurretClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TarComClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TarComClass::Decode_Pointers(void) +{ + TurretClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TurretClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TurretClass::Code_Pointers(void) +{ + DriveClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TurretClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TurretClass::Decode_Pointers(void) +{ + DriveClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * DriveClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DriveClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((UnitTypeClass *&)Class) = (UnitTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DriveClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DriveClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((UnitTypeClass const *&)Class) = &UnitTypeClass::As_Reference(*((UnitType*)&Class)); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Code_Pointers(void) +{ + if (Team) + Team = (TeamClass *)Team->As_Target(); + + if (Member) { + Member = (FootClass *)Member->As_Target(); + } + + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Decode_Pointers(void) +{ + if (Team) { + Team = As_Team((TARGET)Team, false); + Check_Ptr((void *)Team,__FILE__,__LINE__); + } + + if (Member) { + Member = (FootClass *)As_Techno((TARGET)Member, false); + Check_Ptr((void *)Member,__FILE__,__LINE__); + } + + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Radio' ------------------------------ + */ + if (Radio) { + Radio = (RadioClass *)Radio->As_Target(); + } + + MissionClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Radio' ----------------------------- + */ + if (Radio) { + Radio = As_Techno((TARGET)Radio, false); + Check_Ptr((void *)Radio,__FILE__,__LINE__); + } + + MissionClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'House' ------------------------------ + */ + ((HouseClass *&)House) = (HouseClass *)(House->Class->House); + + FlasherClass::Code_Pointers(); + StageClass::Code_Pointers(); + CargoClass::Code_Pointers(); + DoorClass::Code_Pointers(); + + RadioClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'House' ----------------------------- + */ + ((HouseClass *&)House) = HouseClass::As_Pointer(*((HousesType*)&House)); + Check_Ptr((void *)House,__FILE__,__LINE__); + + FlasherClass::Decode_Pointers(); + StageClass::Decode_Pointers(); + CargoClass::Decode_Pointers(); + DoorClass::Decode_Pointers(); + + RadioClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FlasherClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlasherClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlasherClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlasherClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Code_Pointers(void) +{ + /* + ---------------------------- Code 'CargoHold' ---------------------------- + */ + if (CargoHold) { + CargoHold = (FootClass *)CargoHold->As_Target(); + } +} + + +/*********************************************************************************************** + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Decode_Pointers(void) +{ + /* + --------------------------- Decode 'CargoHold' --------------------------- + */ + if (CargoHold) { + CargoHold = (FootClass *)As_Techno((TARGET)CargoHold, false); + Check_Ptr((void *)CargoHold,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * MissionClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MissionClass::Code_Pointers(void) +{ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MissionClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MissionClass::Decode_Pointers(void) +{ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Code_Pointers(void) +{ + if (Next) { + Next = (ObjectClass *)Next->As_Target(); + } + + if (Trigger) { + Trigger = (TriggerClass *)Trigger->As_Target(); + } +} + + +/*********************************************************************************************** + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Decode_Pointers(void) +{ + if (Next) { + Next = As_Object((TARGET)Next, false); + Check_Ptr((void *)Next,__FILE__,__LINE__); + } + + if (Trigger) { + Trigger = As_Trigger((TARGET)Trigger, false); + Check_Ptr((void *)Trigger,__FILE__,__LINE__); + } +} diff --git a/TIBERIANDAWN/IPX.CPP b/TIBERIANDAWN/IPX.CPP new file mode 100644 index 000000000..6082f3f5d --- /dev/null +++ b/TIBERIANDAWN/IPX.CPP @@ -0,0 +1,1108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipx.cpv 2.17 16 Oct 1995 16:49:34 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.CPP * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 15, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Pitfalls: * + * - Never try to use a closed socket; always check the return code from * + * IPX_Open_Socket(). * + * - Always give IPX an outstanding ECB for listening, before you send. * + * - It turns out that IPX is pretty bad about saving registers, so if * + * you have any register variables in your program, they may get * + * trashed. To circumvent this, all functions in this module save & * + * restore the registers before invoking any IPX or NETX function. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * IPX_Close_Socket -- closes an open socket * + * IPX_Get_Connection_Number -- gets local Connection Number * + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * IPX_Get_User_ID -- gets user ID from Connection Number * + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * IPX_Send_Packet -- commands IPX to send a packet * + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * IPX_Cancel_Event -- cancels an operation in progress * + * Let_IPX_Breath -- gives IPX some CPU time * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" + + +/*************************************************************************** + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1994 BR : Created. * + *=========================================================================*/ +int IPX_SPX_Installed(void) +{ + +#ifndef NOT_FOR_WIN95 + + return (IPX_Initialise()); + +#else //NOT_FOR_WIN95 + + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Init all registers to 0's + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + + /*------------------------------------------------------------------------ + Fill in registers for the DPMI call, function 0x300 + ------------------------------------------------------------------------*/ + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x002f; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + /*------------------------------------------------------------------------ + Fill in registers for the real-mode interrupt handler. + To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke + interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, + AL will be 0xff, and ES:DI will contain the IPX/SPX function address. + ------------------------------------------------------------------------*/ + rmi.eax = 0x00007a00; + + /*------------------------------------------------------------------------ + call DPMI + ------------------------------------------------------------------------*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + If IPX isn't there, return 0 + ------------------------------------------------------------------------*/ + if ( (rmi.eax & 0x00ff) != 0xff) { + return(0); + } + + /*------------------------------------------------------------------------ + Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. + If SPX is present, AL will be 0xff. + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = 0x00000010; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + SPX is installed; return '2' + ------------------------------------------------------------------------*/ + if ( (rmi.eax & 0x00ff) == 0xff) { + return(2); + } + + /*------------------------------------------------------------------------ + SPX is not installed; return '1' + ------------------------------------------------------------------------*/ + return(1); +#endif //NOT_FOR_WIN95 +} /* end of IPX_SPX_Installed */ + + +/*************************************************************************** + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * * + * INPUT: * + * socket the socket number to open * + * * + * OUTPUT: * + * 0 = OK * + * -1 = IPX not installed * + * 0xfe = socket table is full * + * 0xff = socket is already open * + * * + * WARNINGS: * + * The application must define its socket number carefully. Use * + * values from 0x4000 to 0x8000 for custom socket numbers. The app * + * must know its own socket number as well as the socket number of * + * a destination workstation. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Open_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int rc; + + /*------------------------------------------------------------------------ + Open the socket: + DX = socket number + AL = 0 for short-lived socket, 0xff for long-lived socket + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF (&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_OPEN_SOCKET; // function code + rmi.edx = socket; // desired socket # + rmi.eax = 0x00ff; // make this a long-lived socket + /*........................................................................ + call DPMI + ........................................................................*/ + int386x (DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0xff); + + return(rc); + +} /* end of IPX_Open_Socket */ + +#endif +/*************************************************************************** + * IPX_Close_Socket -- closes an open socket * + * * + * INPUT: * + * socket socket number to close * + * * + * OUTPUT: * + * 0 = ok, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Close_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Close the socket: + DX = socket number + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_CLOSE_SOCKET; + rmi.edx = socket; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(0); + +} /* end of IPX_Close_Socket */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Connection_Number -- gets local Connection Number * + * * + * This Novell call will the return the user's local "Connection Number". * + * This value will be 0 if the user isn't logged into Novell, so this * + * routine can be used to detect if other calls (such as Get_Local_Target) * + * will be OK. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Connection Number, 0 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Connection_Number(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int num; + + /*------------------------------------------------------------------------ + Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local + connection number into AL. + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000dc00; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + num = rmi.eax & 0x00ff; + + return(num); + +} /* end of IPX_Get_Connection_Number */ + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * * + * This gets the Connection Number for the given User ID. Since a user * + * may be logged in more than once, this just returns the first connection * + * found and ignores the others. * + * * + * INPUT: * + * username name of the user to get the Connection Number for * + * * + * OUTPUT: * + * first-found Connection Number for that user, 0 if user not logged in * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_1st_Connection_Num (char *username) +{ + struct request_buffer { + unsigned short len; // username length + 5 + unsigned char buffer_type; // ConnectionNum = 0x15 + unsigned short object_type; // set ot 0x0100 + unsigned char name_len; // length of username + char name [48]; // copy of username + unsigned short reserved; + }; + struct reply_buffer { + unsigned short len; + unsigned char number_connections; // will be 0 - 100 + unsigned char connection_num [100]; // array of connection numbers + unsigned short reserved[2]; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + int num_conns; // # connections returned + int conn_num; // connection number + int rc; + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(0); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = (unsigned short)(strlen(username) + 5); + reqbuf->buffer_type = 0x15; + reqbuf->object_type = 0x0100; + reqbuf->name_len = (unsigned char) strlen(username); + strcpy(reqbuf->name, username); + reqbuf->reserved = reqbuf->reserved; // prevent compiler warning + replybuf->len = 101; + replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning + replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + Stash the 1st connection number + ------------------------------------------------------------------------*/ + rc = (rmi.eax & 0x00ff); // if AL !=0, error + num_conns = replybuf->number_connections; // # times user is logged in + conn_num = (int )replybuf->connection_num[0]; // 1st connection # + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + /*------------------------------------------------------------------------ + Return error if function failed, or user not logged in + ------------------------------------------------------------------------*/ + if (rc != 0 || num_conns==0) { + return(0); + } else { + return(conn_num); + } +} +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * * + * Once you've obtained a Connection Number from IPX_Get_Connection_Number * + * or IPX_Get_1st_Connection_Num, use this function to translate it into * + * a Network Number and Node Address; then, place those numbers in the * + * IPX header for outgoing packets. * + * * + * INPUT: * + * connection_number Connection Number to translate * + * network_number ptr: will hold Network Number * + * physical_node ptr: will hold Node Address * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * If connection_number is 0 and NETX isn't running, this routine * + * will just put garbage into the network_number and physical_node. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // Internet = 0x13 + unsigned char connection_number; // Conn. Number to translate + }; + struct reply_buffer { + unsigned short len; + unsigned char network_number [4]; // filled in by IPX + unsigned char physical_node [6]; // filled in by IPX + unsigned short server_socket; // filled in by IPX, but don't use! + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Error if invalid connection is given + ------------------------------------------------------------------------*/ + if (connection_number==0) + return(-1); + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = 2; + reqbuf->buffer_type = 0x13; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = 12; + replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning + replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning + replybuf->server_socket = replybuf->server_socket; // suppress warning + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + memcpy(network_number, replybuf->network_number, 4); + memcpy(physical_node, replybuf->physical_node, 6); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_Internet_Address */ + +#endif // NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_User_ID -- gets user ID from Connection Number * + * * + * INPUT: * + * connection_number Connection Number to get User ID for * + * user_id ptr to buffer to put User ID into; * + * size must be >= 48 chars * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_User_ID(int connection_number, char *user_id) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // 0x16 = UserID buffer type + unsigned char connection_number; // Connection Number to get ID for + }; + struct reply_buffer { + unsigned short len; + unsigned char object_id[4]; + unsigned char object_type[2]; + char object_name[48]; + char login_time[7]; + unsigned short reserved; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Error if invalid connection is given + ------------------------------------------------------------------------*/ + if (connection_number==0) + return(-1); + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = 2; + reqbuf->buffer_type = 0x16; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = sizeof(struct reply_buffer) - 2; + replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings + replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings + replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings + replybuf->reserved = replybuf->reserved; // suppress warnings + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + Fill in the caller's buffer with the user name + ------------------------------------------------------------------------*/ + strncpy(user_id, replybuf->object_name, 48); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_User_ID */ +#endif //NOT_FOR_WIN95 + + +/*************************************************************************** + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are "listening" on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas to * + * store the incoming data in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of data buffer, for the packet * + * Packet[1].Length: size of the data buffer * + * * + * When the packet is received, ECBType.CompletionCode will be 0 if * + * successful. Otherwise, some error occurred. * + * * + * You should initialize the ECB to 0's before filling it in. * + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * + * * + * OUTPUT: * + * 0 = OK, IPX error otherwise * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Listen_For_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Call IPX with ES:SI=ecb_ptr + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_LISTEN_FOR_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(rmi.eax & 0x00ff); + +} /* end of IPX_Listen_For_Packet */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Send_Packet -- commands IPX to send a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are sending on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas the * + * outgoing data is stored in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of buffer containing data to send * + * Packet[1].Length: size of the data buffer * + * ImmediateAddress: must be filled in with the node address of * + * the bridge that will route the message; * + * fill this in by calling IPX_Get_Local_Target * + * * + * Also, you must fill in the IPXHeaderType with certain values: * + * PacketType: set to 4 for IPX * + * DestNetworkNumber: Network Number of the destination system * + * DestNetworkNode: Node Address of the destination system * + * DestNetworkSocket: the destination system's socket to send to; * + * this doesn't have to be the same as the * + * socket opened on the local machine. * + * * + * You should initialize the ECB & IPXHeader to 0's before filling them in.* + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB * + * * + * OUTPUT: * + * none. This function doesn't return anything; the caller must check * + * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +void IPX_Send_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Call IPX with ES:SI=ecb_ptr + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_SEND_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of IPX_Send_Packet */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * * + * Use this function to fill in the ECB's ImmediateAddress field when * + * sending a packet. The Immediate Address is the node address of the * + * bridge that must route the message. If there is no bridge, it's * + * filled in with the destination Node Address. In either case, it * + * will contain the proper value for sending. * + * * + * INPUT: * + * dest_network destination Network Number * + * dest_node destination Node Address * + * dest_socket destination Socket Number * + * bridge_address field to fill in with Immediate Address * + * * + * OUTPUT: * + * 0 = OK, error otherwise * + * * + * WARNINGS: * + * If the Connection Number is 0 (user not logged in), dest_network * + * and dest_node will be garbage, and this routine will probably crash. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + struct request_buffer { + unsigned char network_number [4]; + unsigned char physical_node [6]; + unsigned short socket; + }; + struct reply_buffer { + unsigned char local_target [6]; + }; + unsigned int rc; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + memcpy(reqbuf->network_number, dest_network, 4); + memcpy(reqbuf->physical_node, dest_node, 6); + reqbuf->socket = dest_socket; + + /*------------------------------------------------------------------------ + Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_GET_LOCAL_TARGET; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + memcpy(bridge_address, replybuf->local_target, 6); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(rc); + +} /* end of IPX_Get_Local_Target */ + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Cancel_Event -- cancels an operation in progress * + * * + * INPUT: * + * ecb_ptr pointer to ECB event to cancel * + * * + * OUTPUT: * + * ??? * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Cancel_Event(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + unsigned int rc; + + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_CANCEL_EVENT; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + + return(rc); + +} /* end of IPX_Cancel_Event */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * Let_IPX_Breath -- gives IPX some CPU time * + * * + * Use this function if you're polling the ECB's InUse flag, waiting * + * for it to go to 0: * + * * + * while ECBType.InUse * + * Let_IPX_Breath(); * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +void Let_IPX_Breath(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_RELINQUISH_CONTROL; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of Let_IPX_Breath */ +#endif //NOT_FOR_WIN95 diff --git a/TIBERIANDAWN/IPX.H b/TIBERIANDAWN/IPX.H new file mode 100644 index 000000000..e0effe3be --- /dev/null +++ b/TIBERIANDAWN/IPX.H @@ -0,0 +1,185 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipx.h_v 2.17 16 Oct 1995 16:46:26 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.H * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 14, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPX_H +#define IPX_H + +/* +******************************** Structures ********************************* +*/ +typedef unsigned char NetNumType[4]; +typedef unsigned char NetNodeType[6]; +typedef char UserID[48]; + +/*--------------------------------------------------------------------------- +This is the IPX Packet structure. It's followed by the data itself, which +can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +field; annotation of 'APP' means the application must set the field. +NOTE: All header fields are ordered high-byte,low-byte. +---------------------------------------------------------------------------*/ +typedef struct IPXHEADER { + unsigned short CheckSum; // IPX: Not used; always 0xffff + unsigned short Length; // IPX: Total size, incl header & data + unsigned char TransportControl; // IPX: # bridges message crossed + unsigned char PacketType; // APP: Set to 4 for IPX (5 for SPX) + unsigned char DestNetworkNumber[4]; // APP: destination Network Number + unsigned char DestNetworkNode[6]; // APP: destination Node Address + unsigned short DestNetworkSocket; // APP: destination Socket Number + unsigned char SourceNetworkNumber[4]; // IPX: source Network Number + unsigned char SourceNetworkNode[6]; // IPX: source Node Address + unsigned short SourceNetworkSocket; // IPX: source Socket Number +} IPXHeaderType; + +/*--------------------------------------------------------------------------- +This is the IPX Event Control Block. It serves as a communications area +between IPX and the application for a single IPX operation. You should set +up a separate ECB for each IPX operation you perform. +---------------------------------------------------------------------------*/ +typedef struct ECB { + void *Link_Address; + void (*Event_Service_Routine)(void); // APP: event handler (NULL=none) + unsigned char InUse; // IPX: 0 = event complete + unsigned char CompletionCode; // IPX: event's return code + unsigned short SocketNumber; // APP: socket to send data through + unsigned short ConnectionID; // returned by Listen (???) + unsigned short RestOfWorkspace; + unsigned char DriverWorkspace[12]; + unsigned char ImmediateAddress[6]; // returned by Get_Local_Target + unsigned short PacketCount; + struct { + void *Address; + unsigned short Length; + } Packet[2]; +} ECBType; + + +/*--------------------------------------------------------------------------- +This structure is used for calling DPMI function 0x300, Call-Real-Mode- +Interrupt. It passes register values to & from the interrupt handler. +---------------------------------------------------------------------------*/ +typedef struct { + long edi; + long esi; + long ebp; + long Reserved; + long ebx; + long edx; + long ecx; + long eax; + short Flags; + short es; + short ds; + short fs; + short gs; + short ip; + short cs; + short sp; + short ss; +} RMIType; + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +These defines are for the IPX functions. The function number is specified +by placing it in BX when IPX is called. There are two ways to invoke IPX: +use interrupt 0x7a, or use a function whose address is obtained by calling +interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +This is the preferred method, since other apps are known to use int 0x7a. +---------------------------------------------------------------------------*/ +#define IPX_OPEN_SOCKET 0x0000 +#define IPX_CLOSE_SOCKET 0x0001 +#define IPX_GET_LOCAL_TARGET 0x0002 +#define IPX_SEND_PACKET 0x0003 +#define IPX_LISTEN_FOR_PACKET 0x0004 +#define IPX_SCHEDULE_EVENT 0x0005 +#define IPX_CANCEL_EVENT 0x0006 +#define IPX_GET_INTERVAL_MARKER 0x0008 +#define IPX_GET_INTERNETWORK_ADDRESS 0x0009 +#define IPX_RELINQUISH_CONTROL 0x000A +#define IPX_DISCONNECT_FROM_TARGET 0x000B + +/*--------------------------------------------------------------------------- +These defines are for various IPX error codes: +---------------------------------------------------------------------------*/ +#define IPXERR_CONNECTION_SEVERED 0x00ec +#define IPXERR_CONNECTION_FAILED 0x00ed +#define IPXERR_NO_CONNECTION 0x00ee +#define IPXERR_CONNECTION_TABLE_FULL 0x00ef +#define IPXERR_NO_CANCEL_ECB 0x00f9 +#define IPXERR_NO_PATH 0x00fa +#define IPXERR_ECB_INACTIVE 0x00fc +#define IPXERR_INVALID_PACKET_LENGTH 0x00fd +#define IPXERR_SOCKET_TABLE_FULL 0x00fe +#define IPXERR_SOCKET_ERROR 0x00ff + +/*--------------------------------------------------------------------------- +These defines are for various interrupt vectors and DPMI functions: +---------------------------------------------------------------------------*/ +#define IPX_INT 0x007a +#define DPMI_INT 0x0031 +#define DPMI_ALLOC_DOS_MEM 0x0100 +#define DPMI_FREE_DOS_MEM 0x0101 +#define DPMI_CALL_REAL_INT 0x0300 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/* +******************************** Prototypes ********************************* +*/ +/*--------------------------------------------------------------------------- +NOTE: All routines return 0 for a success code and one of the above IPX +error codes if there's an error, EXCEPT: +- IPX_SPX_Installed (0 = not installed) +- Get_Connection_Number / Get_1st_Connection_Number (0 = no connection) +---------------------------------------------------------------------------*/ +/* +.................................. ipx.cpp .................................. +*/ +int IPX_SPX_Installed(void); +int IPX_Open_Socket(unsigned short socket); +int IPX_Close_Socket(unsigned short socket); +int IPX_Get_Connection_Number(void); +int IPX_Get_1st_Connection_Num (char *username); +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node); +int IPX_Get_User_ID(int connection_number, char *user_id); +int IPX_Listen_For_Packet(struct ECB *ecb_ptr); +void IPX_Send_Packet(struct ECB *ecb_ptr); +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address); +int IPX_Cancel_Event(struct ECB *ecb_ptr); +void Let_IPX_Breath(void); + +#endif diff --git a/TIBERIANDAWN/IPX95.CPP b/TIBERIANDAWN/IPX95.CPP new file mode 100644 index 000000000..9a9420376 --- /dev/null +++ b/TIBERIANDAWN/IPX95.CPP @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" + + +// Stub in old IPX here ST - 12/20/2018 1:53PM +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void) {return 0;} + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer) {return 0;} + extern void __stdcall IPX_Shut_Down95(void) {} + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*) {return 0;} + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int){return 0;} + extern BOOL __stdcall IPX_Start_Listening95(void){return 0;} + extern int __stdcall IPX_Open_Socket95(int socket){return 0;} + extern void __stdcall IPX_Close_Socket95(int socket){} + extern int __stdcall IPX_Get_Connection_Number95(void){return 0;} + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*){return 0;} +} + + + +int IPX_Open_Socket(unsigned short socket) +{ + return -1; + //return (IPX_Open_Socket95((int)socket)); //ST - 12/20/2018 11:34AM +} + + +int IPX_Close_Socket(unsigned short socket) +{ + //IPX_Close_Socket95((int)socket); //ST - 12/20/2018 11:34AM + return (0); +} + + +int IPX_Get_Connection_Number(void) +{ + //return (IPX_Get_Connection_Number95()); //ST - 12/20/2018 11:34AM + return -1; +} + + + +int IPX_Broadcast_Packet(unsigned char *buf, int buflen) +{ + //return(IPX_Broadcast_Packet95(buf, buflen)); //ST - 12/20/2018 11:34AM + return 0; +} + +//extern "C"{ +// extern void __cdecl Int3(void); +//} +//ST - 12/20/2018 11:34AM + +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + //Int3(); + // return (IPX_Get_Local_Target95(dest_network, dest_node, dest_socket, bridge_address)); //ST - 12/20/2018 11:34AM + return 0; +} + diff --git a/TIBERIANDAWN/IPX95.H b/TIBERIANDAWN/IPX95.H new file mode 100644 index 000000000..79a740dae --- /dev/null +++ b/TIBERIANDAWN/IPX95.H @@ -0,0 +1,54 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void); + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); + extern void __stdcall IPX_Shut_Down95(void); + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); + extern BOOL __stdcall IPX_Start_Listening95(void); + extern int __stdcall IPX_Open_Socket95(int socket); + extern void __stdcall IPX_Close_Socket95(int socket); + extern int __stdcall IPX_Get_Connection_Number95(void); + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*); +} diff --git a/TIBERIANDAWN/IPXADDR.CPP b/TIBERIANDAWN/IPXADDR.CPP new file mode 100644 index 000000000..6953eb622 --- /dev/null +++ b/TIBERIANDAWN/IPXADDR.CPP @@ -0,0 +1,491 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxaddr.cpv 2.17 16 Oct 1995 16:50:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXAddressClass::IPXAddressClass -- class constructor * + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * IPXAddressClass::Set_Address -- sets the IPX address values * + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * IPXAddressClass::operator== -- overloaded comparison operator * + * IPXAddressClass::operator!= -- overloaded comparison operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor * + * * + * This default constructor generates a broadcast address. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(void) +{ + NetworkNumber[0] = 0xff; + NetworkNumber[1] = 0xff; + NetworkNumber[2] = 0xff; + NetworkNumber[3] = 0xff; + NodeAddress[0] = 0xff; + NodeAddress[1] = 0xff; + NodeAddress[2] = 0xff; + NodeAddress[3] = 0xff; + NodeAddress[4] = 0xff; + NodeAddress[5] = 0xff; + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * * + * This form of the constructor takes an IPX header as an argument. It * + * extracts the address from the Source address fields in the header. * + * * + * INPUT: * + * header Header from which to extract the address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(IPXHeaderType *header) +{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * * + * This routine extracts the source addresses from the given IPX header. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(IPXHeaderType *header) +{ + +#ifdef VIRTUAL_SUBNET_SERVER + if (Winsock.Get_Connected()){ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 0, 6); + unsigned short target_mask = *(unsigned short*)header; + /* + ** If this is a head to head game (no VSS) -- + ** If mask is 0 then this packet was broadcast from the other player + ** Otherwise exclusive or with 3 to get other players mask + */ + if (!UseVirtualSubnetServer){ + if (target_mask == 0){ + target_mask = 1 << PlanetWestwoodIsHost; + } + target_mask ^= 3; + } + + *(unsigned short*) &NodeAddress[0] = target_mask; + + }else{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + } +#else //VIRTUAL_SUBNET_SERVER + if (header){ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + }else{ + /* + ** Address is meaningless when using winsock + */ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 1, 6); + } +#endif //VIRTUAL_SUBNET_SERVER +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(NetNumType net, NetNodeType node) +{ + memcpy(net,NetworkNumber,4); + memcpy(node,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(IPXHeaderType *header) +{ + memcpy(header->DestNetworkNumber,NetworkNumber,4); + memcpy(header->DestNetworkNode,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +bool IPXAddressClass::Is_Broadcast(void) +{ + if ( NetworkNumber[0] == 0xff && + NetworkNumber[1] == 0xff && + NetworkNumber[2] == 0xff && + NetworkNumber[3] == 0xff && + NodeAddress[0] == 0xff && + NodeAddress[1] == 0xff && + NodeAddress[2] == 0xff && + NodeAddress[3] == 0xff && + NodeAddress[4] == 0xff && + NodeAddress[5] == 0xff) { + return(true); + } else { + return(false); + } +} + + +/*************************************************************************** + * IPXAddressClass::operator== -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = not equal, 1 = equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator == (IPXAddressClass & addr) +{ + /*------------------------------------------------------------------------ + If either Network Number is all 0's (which can happen if the system is + not running NETX), compare only the Node Addresses. + ------------------------------------------------------------------------*/ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(true); + } else { + return(false); + } + + } else { + + /*------------------------------------------------------------------------ + Otherwise, compare both the Network Numbers and Node Addresses + ------------------------------------------------------------------------*/ + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(true); + } else { + return(false); + } + } +} + + +/*************************************************************************** + * IPXAddressClass::operator!= -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = equal, 1 = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator != (IPXAddressClass & addr) +{ + /*------------------------------------------------------------------------ + If either Network Number is all 0's (which can happen if the system is + not running NETX), compare only the Node Addresses. + ------------------------------------------------------------------------*/ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(false); + } else { + return(true); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, compare both the Network Numbers and Node Addresses + ------------------------------------------------------------------------*/ + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(false); + } else { + return(true); + } + } +} + + +/*************************************************************************** + * IPXAddressClass::operator > -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator > (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) > 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator < -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator < (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) < 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator >= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator >= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) >= 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator <= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator <= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) <= 0); + +} /* end of operator != */ + + diff --git a/TIBERIANDAWN/IPXADDR.H b/TIBERIANDAWN/IPXADDR.H new file mode 100644 index 000000000..92adcf660 --- /dev/null +++ b/TIBERIANDAWN/IPXADDR.H @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxaddr.h_v 2.19 16 Oct 1995 16:44:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + * This class is useful for any IPX-related code. It's just a utility * + * to help manage those annoying IPX address fields. This class lets you * + * compare addresses, copy addresses to & from the IPX header, etc. * + * * + * The class has no virtual functions, so you can treat this class just * + * like a data structure; it can be loaded & saved, and even transmitted * + * across the net. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXADDR_H +#define IPXADDR_H + +#include "ipx.h" // for NetNumType & NetNodeType + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXAddressClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructors: + .....................................................................*/ + IPXAddressClass(void); + IPXAddressClass(NetNumType net, NetNodeType node); + IPXAddressClass(IPXHeaderType *header); + + /*..................................................................... + Set the address from explicit variables, or from the SOURCE values + in an IPX packet header. + .....................................................................*/ + void Set_Address(NetNumType net, NetNodeType node); + void Set_Address(IPXHeaderType *header); + /*..................................................................... + Get the address values explicitly, or copy them into the DESTINATION + values in an IPX packet header. + .....................................................................*/ + void Get_Address (NetNumType net, NetNodeType node); + void Get_Address(IPXHeaderType *header); + + /*..................................................................... + Tells if this address is a broadcast address + .....................................................................*/ + bool Is_Broadcast(void); + + /*..................................................................... + Overloaded operators: + .....................................................................*/ + int operator == (IPXAddressClass & addr); + int operator != (IPXAddressClass & addr); + int operator > (IPXAddressClass &addr); + int operator < (IPXAddressClass &addr); + int operator >= (IPXAddressClass &addr); + int operator <= (IPXAddressClass &addr); + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /* + --------------------------- Private Interface ---------------------------- + */ + private: + NetNumType NetworkNumber; + NetNodeType NodeAddress; +}; + +#endif +/**************************** end of ipxaddr.h *****************************/ diff --git a/TIBERIANDAWN/IPXCONN.CPP b/TIBERIANDAWN/IPXCONN.CPP new file mode 100644 index 000000000..28dc6c4b7 --- /dev/null +++ b/TIBERIANDAWN/IPXCONN.CPP @@ -0,0 +1,677 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxconn.cpv 1.9 16 Oct 1995 16:50:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXConnClass::IPXConnClass -- class constructor * + * IPXConnClass::~IPXConnClass -- class destructor * + * IPXConnClass::Init -- hardware-specific initialization routine * + * IPXConnClass::Configure -- One-time initialization routine * + * IPXConnClass::Start_Listening -- commands IPX to listen * + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * IPXConnClass::Open_Socket -- opens communications socket * + * IPXConnClass::Close_Socket -- closes the socket * + * IPXConnClass::Send_To -- sends the packet to the given address * + * IPXConnClass::Broadcast -- broadcasts the given packet * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" +#include "tcpip.h" + +/* +********************************* Globals *********************************** +*/ +unsigned short IPXConnClass::Socket; +int IPXConnClass::ConnectionNum; +ECBType * IPXConnClass::ListenECB; +IPXHeaderType * IPXConnClass::ListenHeader; +char * IPXConnClass::ListenBuf; +ECBType * IPXConnClass::SendECB; +IPXHeaderType * IPXConnClass::SendHeader; +char * IPXConnClass::SendBuf; +long IPXConnClass::Handler; +int IPXConnClass::Configured = 0; +int IPXConnClass::SocketOpen = 0; +int IPXConnClass::Listening = 0; +int IPXConnClass::PacketLen; + + +/*************************************************************************** + * IPXConnClass::IPXConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * address address of destination (NULL = no address) * + * id connection's unique numerical ID * + * name connection's name * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXConnClass::IPXConnClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name) : +#ifdef SEQ_NET + SequencedConnClass (numsend, numreceive, maxlen, magicnum, +#else + NonSequencedConnClass (numsend, numreceive, maxlen, magicnum, +#endif + 2, // retry delta + -1, // max retries + 60) // timeout +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + if (address) + Address = (*address); + ID = id; + strcpy (Name, name); + + if (!Winsock.Get_Connected()){ + /*------------------------------------------------------------------------ + If our Address field is an actual address (ie NULL wasn't passed to the + constructor), pre-compute the ImmediateAddress value for the SendECB. + This allows pre-computing of the ImmediateAddress for all connections + created after Configure() is called. + ------------------------------------------------------------------------*/ + if (!Address.Is_Broadcast() && Configured==1) { + Address.Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + if (IPX_Get_Local_Target (net, node, Socket, ImmediateAddress)!=0) + memcpy(ImmediateAddress,node,6); + } else { + + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(ImmediateAddress,node,6); + } + + Immed_Set = 1; + } else { + memset (ImmediateAddress, 0, 6); + Immed_Set = 0; + } + } + +} /* end of IPXConnClass */ + + +/*************************************************************************** + * IPXConnClass::Init -- hardware-specific initialization routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Init (void) +{ + /*------------------------------------------------------------------------ + Invoke the parent's Init routine + ------------------------------------------------------------------------*/ +#ifdef SEQ_NET + SequencedConnClass::Init(); +#else + NonSequencedConnClass::Init(); +#endif +} + + +/*************************************************************************** + * IPXConnClass::Configure -- One-time initialization routine * + * * + * This routine sets up static members that are shared by all IPX * + * connections (ie those variables used by the Send/Listen/Broadcast * + * routines). * + * * + * INPUT: * + * socket socket ID for sending & receiving * + * conn_num local IPX Connection Number (0 = not logged in) * + * listen_ecb ptr to ECBType for listening * + * send_ecb ptr to ECBType for sending * + * listen_header ptr to IPXHeaderType for listening * + * send_header ptr to IPXHeaderType for sending * + * listen_buf ptr to buffer for listening * + * send_buf ptr to buffer for sending * + * handler_rm_ptr REAL-MODE pointer to event service routine * + * (high word = segment, low word = offset) * + * maxpacketlen max packet size to listen for * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - All pointers must be protected-mode pointers, but must point to * + * DOS real-mode memory (except the Handler segment/offset) * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Configure (unsigned short socket, int conn_num, + ECBType *listen_ecb, ECBType *send_ecb, IPXHeaderType *listen_header, + IPXHeaderType *send_header, char *listen_buf, char *send_buf, + long handler_rm_ptr, int maxpacketlen) +{ + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + Socket = socket; + ConnectionNum = conn_num; + ListenECB = listen_ecb; + SendECB = send_ecb; + ListenHeader = listen_header; + SendHeader = send_header; + ListenBuf = listen_buf; + SendBuf = send_buf; + Handler = handler_rm_ptr; + PacketLen = maxpacketlen; + + Configured = 1; + +} /* end of Configure */ + + +/*************************************************************************** + * IPXConnClass::Start_Listening -- commands IPX to listen * + * * + * This routine may be used to start listening in polled mode (if the * + * ECB's Event_Service_Routine is NULL), or in interrupt mode; it's * + * up to the caller to fill the ECB in. If in polled mode, Listening * + * must be restarted every time a packet comes in. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - The ListenECB must have been properly filled in by the IPX Manager.* + * - Configure must be called before calling this routine. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +bool IPXConnClass::Start_Listening(void) +{ + +#ifndef NOT_FOR_WIN95 + + if (Winsock.Get_Connected()) return (true); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + if (IPX_Start_Listening95()){ + Listening =1; + return (TRUE); + }else{ + return (FALSE); + } + +#else + + void *hdr_ptr; + unsigned long hdr_val; + void *buf_ptr; + unsigned long buf_val; + int rc; + + /*------------------------------------------------------------------------ + Don't do a thing unless we've been configured, and we're not listening. + ------------------------------------------------------------------------*/ + if (Configured==0 || Listening==1) + return(false); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(ListenECB, 0, sizeof(ECBType)); + memset(ListenHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)ListenHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + + buf_val = (unsigned long)ListenBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in the ECB + ------------------------------------------------------------------------*/ + ListenECB->SocketNumber = Socket; + ListenECB->PacketCount = 2; + ListenECB->Packet[0].Address = hdr_ptr; + ListenECB->Packet[0].Length = sizeof(IPXHeaderType); + ListenECB->Packet[1].Address = buf_ptr; + ListenECB->Packet[1].Length = (unsigned short)PacketLen; + + ((long &)ListenECB->Event_Service_Routine) = Handler; + + /*------------------------------------------------------------------------ + Command IPX to listen + ------------------------------------------------------------------------*/ + rc = IPX_Listen_For_Packet(ListenECB); + if (rc!=0) { + Close_Socket(Socket); + return(false); + } else { + Listening = 1; + return(true); + } + +#endif //NOT_FOR_WIN95 +} /* end of Start_Listening */ + + +/*************************************************************************** + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - This routine MUST NOT be called if IPX is not listening already! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +bool IPXConnClass::Stop_Listening(void) +{ + /*------------------------------------------------------------------------ + Don't do anything unless we're already Listening. + ------------------------------------------------------------------------*/ + if (Listening==0) + return(false); + +#ifndef NOT_FOR_WIN95 + + if (Winsock.Get_Connected()){ + Listening = 0; + return (true); + }else{ + IPX_Shut_Down95(); + Close_Socket(Socket); + } + +#else //NOT_FOR_WIN95 + + /*------------------------------------------------------------------------ + Shut IPX down. + ------------------------------------------------------------------------*/ + IPX_Cancel_Event(ListenECB); + Close_Socket(Socket); + +#endif //NOT_FOR_WIN95 + + Listening = 0; + + /*------------------------------------------------------------------------ + All done. + ------------------------------------------------------------------------*/ + return(true); + +} /* end of Stop_Listening */ + + +/*************************************************************************** + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send(char *buf, int buflen) +{ + /*------------------------------------------------------------------------ + Invoke our own Send_To routine, filling in our Address as the destination. + ------------------------------------------------------------------------*/ + if (Immed_Set) { + return(Send_To (buf, buflen, &Address, ImmediateAddress)); + } else { + return(Send_To (buf, buflen, &Address, NULL)); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXConnClass::Open_Socket -- opens communications socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Open_Socket(unsigned short socket) +{ + int rc; + + if (Winsock.Get_Connected()){ + SocketOpen = 1; + return (true); + } + + SocketOpen = 0; + + /*------------------------------------------------------------------------ + Try to open a listen socket. The socket may have been left open by + a previously-crashed program, so ignore the state of the SocketOpen + flag for this call; use IPX to determine if the socket was already open. + ------------------------------------------------------------------------*/ + rc = IPX_Open_Socket(socket); + if (rc) { + /* + ................. If already open, close & reopen it .................. + */ + if (rc==IPXERR_SOCKET_ERROR) { + IPX_Close_Socket(socket); + rc = IPX_Open_Socket(socket); + /* + .................. Still can't open: return error .................. + */ + if (rc) { + return(false); + } + } + } + + SocketOpen = 1; + + return(true); +} + + +/*************************************************************************** + * IPXConnClass::Close_Socket -- closes the socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Calling this routine when the sockets aren't open may crash! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Close_Socket(unsigned short socket) +{ + if (Winsock.Get_Connected()){ + SocketOpen = 0; + return; + } + /*------------------------------------------------------------------------ + Never, ever, ever, under any circumstances whatsoever, close a socket + that isn't open. You'll regret it forever (or until at least until you're + through rebooting, which, if you're on a Pentium is the same thing). + ------------------------------------------------------------------------*/ + if (SocketOpen==1) + IPX_Close_Socket(socket); + + SocketOpen = 0; + +} /* end of Close_Socket */ + + +/*************************************************************************** + * IPXConnClass::Send_To -- sends the packet to the given address * + * * + * The "ImmediateAddress" field of the SendECB must be filled in with the * + * address of a bridge, or the node address of the destination if there * + * is no bridge. The NETX call to find this address will always crash * + * if NETX isn't loaded (ConnectionNum is 0), so this case is trapped & * + * prevented. * + * Also, if the address of this IPX connection is known when the * + * constructor is called, and Configure has been called, Get_Local_Target * + * is called to precompute the ImmediateAddress; this case is detected & * + * if the value is already computed, it's just memcpy'd over. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address Address to send to * + * immed ImmediateAddress value, NULL if none * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +//#pragma off (unreferenced) +int IPXConnClass::Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed) +{ + + //void *hdr_ptr; + //void *buf_ptr; + //unsigned long hdr_val; + //unsigned long buf_val; + NetNumType net; + NetNodeType node; + int rc; + + //unsigned short target_mask; + + unsigned char send_address[6]; + + if (Winsock.Get_Connected()){ + +#ifdef VIRTUAL_SUBNET_SERVER + if (immed){ + memcpy(send_address, immed, 6); + }else{ + address->Get_Address(net,node); + memcpy (send_address, node, 6); + } + /* + ** Use first two bytes of ipx address as target mask + */ + unsigned short *maskptr = (unsigned short*)&send_address[0]; + target_mask = *maskptr; + + char *tempsend = new char [buflen + sizeof (target_mask)]; + + *(unsigned short*)tempsend = htons(target_mask); + memcpy (tempsend+2, buf, buflen); +#if (0) +char tempbuf[256]; +CommHeaderType *packet = (CommHeaderType *)(&tempsend[2]); +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Sending unicast packet type %d, ID=%d, code=%s, length=%d\n", tempsend[sizeof(CommHeaderType)+2], + packet->PacketID, + pcode[packet->Code], + buflen + sizeof (target_mask)); +CCDebugString (tempbuf); +#endif //(0) + + Winsock.Write((void*)tempsend, buflen + sizeof (target_mask)); + delete [] tempsend; +#else // VIRTUAL_SUBNET_SERVER + Winsock.Write((void*)buf, buflen); +#endif // VIRTUAL_SUBNET_SERVER + + return (true); + } + + + + if (immed) { + memcpy(send_address, immed, 6); + //memcpy(node, immed, 6); + //memset (net, 0, sizeof(net) ); + address->Get_Address(net,node); + } else { + address->Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, &send_address[0]); + if (rc!=0) { + return(false); + } + } else { + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(send_address,node,6); + } + } + + return (IPX_Send_Packet95(&send_address[0], (unsigned char*)buf, buflen, (unsigned char*)net, (unsigned char*)node)); + + +} +//#pragma on (unreferenced) + +/*************************************************************************** + * IPXConnClass::Broadcast -- broadcasts the given packet * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Broadcast(char *buf, int buflen) +{ + + if (Winsock.Get_Connected()){ +#ifdef VIRTUAL_SUBNET_SERVER + char *tempsend = new char [buflen + sizeof (unsigned short)]; + memcpy (tempsend+2, buf, buflen); + *tempsend = 0; + *(tempsend+1) = 0; +#if (0) +char tempbuf[256]; +CommHeaderType *packet = (CommHeaderType *)(&tempsend[2]); +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Sending multicast packet type %d, ID=%d, code=%s, length=%d\n", tempsend[sizeof(CommHeaderType)+2], + packet->PacketID, + pcode[packet->Code], + buflen + sizeof (unsigned short)); +CCDebugString (tempbuf); +#endif //(0) + + Winsock.Write((void*)tempsend, buflen + sizeof (unsigned short)); + delete [] tempsend; +#else // VIRTUAL_SUBNET_SERVER + Winsock.Write((void*)buf, buflen); +#endif // VIRTUAL_SUBNET_SERVER + return(true); + }else{ + return (IPX_Broadcast_Packet95((unsigned char*)buf, buflen)); + } + +} + diff --git a/TIBERIANDAWN/IPXCONN.H b/TIBERIANDAWN/IPXCONN.H new file mode 100644 index 000000000..e929aece4 --- /dev/null +++ b/TIBERIANDAWN/IPXCONN.H @@ -0,0 +1,208 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxconn.h_v 1.12 16 Oct 1995 16:45:10 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for IPX communications. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Recieve_Queue from * + * SequencedConnClass. It guarantees order of delivery of packets. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXCONN_H +#define IPXCONN_H + + +/* +********************************* Includes ********************************** +*/ +#ifdef SEQ_NET +#include "seqconn.h" +#else +#include "noseqcon.h" +#endif +#include "ipxaddr.h" + + +/* +***************************** Class Declaration ***************************** +*/ +#ifdef SEQ_NET +class IPXConnClass : public SequencedConnClass +#else +class IPXConnClass : public NonSequencedConnClass +#endif +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONN_NAME_MAX = 40, // max # chars allowed for connection name + CONNECTION_NONE = -1, // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXConnClass(int numsend, int numrecieve, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name); + virtual ~IPXConnClass () {}; + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + The Configure function is for configuring all connections at once. + It's static because it doesn't apply to any specific connection, but + all of them. + .....................................................................*/ + static void Configure(unsigned short socket, + int conn_num, ECBType *listen_ecb, ECBType *send_ecb, + IPXHeaderType *listen_header, IPXHeaderType *send_header, + char *listen_buf, char *send_buf, long handler_rm_ptr, + int maxpacketlen); + + /*..................................................................... + These routines tell IPX to start listening for packets, and to stop + listening for packets. They're static because they affect all + connections at once (there's no way to turn listening on for only one + connection; it's all or nothing). + .....................................................................*/ + static bool Start_Listening (void); + static bool Stop_Listening (void); + + /*..................................................................... + The Destination IPX Address for this connection + .....................................................................*/ + IPXAddressClass Address; + + /*..................................................................... + The "Immediate" (Bridge) address for this connection, and a flag + telling if the address has been precomputed. + .....................................................................*/ + NetNodeType ImmediateAddress; + int Immed_Set; + + /*..................................................................... + Each IPX Connection can have a Name & Unique numerical ID + .....................................................................*/ + int ID; + char Name[CONN_NAME_MAX]; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. + .....................................................................*/ + virtual int Send (char *buf, int buflen); + + /*..................................................................... + These are the routines that access IPX. Open_Socket & Close_Socket are + static because they're called by Start_Listening & Stop_Listening. + Send_To & Broadcast are static since they're direct interfaces to IPX, + and there's only one IPX instance running. + .....................................................................*/ + static int Open_Socket(unsigned short socket); + static void Close_Socket(unsigned short socket); + static int Send_To(char *buf, int buflen, IPXAddressClass *address, NetNodeType immed); + static int Broadcast(char *buf, int buflen); + + /*..................................................................... + The socket ID for this connection + .....................................................................*/ + static unsigned short Socket; + + /*..................................................................... + User's local Connection # (0 = not logged in) + .....................................................................*/ + static int ConnectionNum; + + /*..................................................................... + This is a static version of MaxPacketLen, which is the size of the + app's packets, plus our internal CommHeaderType. It's used in the + Start_Listening routine. + .....................................................................*/ + static int PacketLen; + + /*..................................................................... + Variables for Listening (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *ListenECB; + static IPXHeaderType *ListenHeader; + static char *ListenBuf; + + /*..................................................................... + Variables for Sending (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *SendECB; + static IPXHeaderType *SendHeader; + static char *SendBuf; + + /*..................................................................... + This is a REAL-MODE pointer to the event-service-routine for IPX. + If it's 0, IPX will operate in polled mode. Otherwise, the high word + must contain the segment, and the low word must contain the offset. + CS will be the high word value when the routine is called. (Requiring + the segment/offset to be computed by the caller gives the caller + control over CS.) + .....................................................................*/ + static long Handler; + + /*..................................................................... + This status flag tells us if Configure() has been called or not. + .....................................................................*/ + static int Configured; + + /*..................................................................... + This status flag tells us if the socket has been opened or not. + .....................................................................*/ + static int SocketOpen; + + /*..................................................................... + This status flag tells us if Start_Listening() has been called or not. + .....................................................................*/ + static int Listening; +}; + +#endif diff --git a/TIBERIANDAWN/IPXGCONN.CPP b/TIBERIANDAWN/IPXGCONN.CPP new file mode 100644 index 000000000..8d4ac8d9e --- /dev/null +++ b/TIBERIANDAWN/IPXGCONN.CPP @@ -0,0 +1,470 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxgconn.cpv 1.9 16 Oct 1995 16:51:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : July 6, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* + * IPXGlobalConnClass::Send -- sends a packet * + * IPXGlobalConnClass::Service_Receive_Queue -- services recieve queue * + * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* +* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * * + * This routine chains to the parent constructor, but it adjusts the size * + * of the packet by the added bytes in the GlobalHeaderType structure. * + * This forces the parent classes to allocate the proper sized PacketBuf * + * for outgoing packets, and to set MaxPacketLen to the proper value. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * product_id unique ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, int maxlen, + unsigned short product_id) : + IPXConnClass (numsend, numreceive, + maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), + GLOBAL_MAGICNUM, // magic number for this connection + NULL, // IPX Address (none) + 0, // Connection ID + "") // Connection Name +{ + ProductID = product_id; + IsBridge = 0; + +} /* end of IPXGlobalConnClass */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a GlobalHeaderType and * + * queues the resulting packet into the Send Queue. The packet's * + * MagicNumber, Code, PacketID, destination Address and ProductID are set * + * here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address address to send the packet to (NULL = Broadcast) * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req) +{ + /*------------------------------------------------------------------------ + Store the packet's Magic Number + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + If this is a ACK-required packet, sent to a specific system, mark it as + ACK-required; otherwise, mark as no-ACK-required. + ------------------------------------------------------------------------*/ + if (ack_req && address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; + } else { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; + } + + /*------------------------------------------------------------------------ + Fill in the packet ID. This will have very limited meaning; it only + allows us to determine if an ACK packet we receive later goes with this + packet; it doesn't let us detect re-sends of other systems' packets. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); + + /*------------------------------------------------------------------------ + Set the product ID for this packet. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; + + /*------------------------------------------------------------------------ + Set this packet's destination address. If no address is specified, use + a Broadcast address (which IPXAddressClass's default constructor creates). + ------------------------------------------------------------------------*/ + if (address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Address = (*address); + } else { + ((GlobalHeaderType *)PacketBuf)->Address = IPXAddressClass(); + } + + /*------------------------------------------------------------------------ + Copy the application's data + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Queue it + ------------------------------------------------------------------------*/ + return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType))); + +} /* end of Send_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes GlobalHeaderType) * + * buflen length of buffer to process * + * address the address of the sender (the IPX Manager class must * + * extract this from the IPX Header of the received packet.) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, + IPXAddressClass *address) +{ + GlobalHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + GlobalHeaderType *entry_data; // ptr to queue entry data + int i; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (GlobalHeaderType *)buf; + if (packet->Header.MagicNumber!=MagicNum) { + return(false); + } + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Header.Code) { + /*..................................................................... + DATA: Save the given address in the message buffer (so Get_Message() + can extract it later), and queue this message. + Don't bother checking for a Re-Send; since this queue is receiving data + from multiple systems, the Total_Receive() value for this queue will + have nothing to do with the packet's ID. The application must deal + with this by being able to handle multiple receipts of the same packet. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + packet->Address = (*address); + Queue->Queue_Receive (buf, buflen); + break; + + /*..................................................................... + ACK: If this ACK is for any of my packets, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ..................... Get queue entry ptr ....................... + */ + send_entry = Queue->Get_Send(i); + /* + ............. If ptr is valid, get ptr to its data .............. + */ + entry_data = (GlobalHeaderType *)(send_entry->Buffer); + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->Header.PacketID==entry_data->Header.PacketID && + entry_data->Header.Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * address filled in with sender's address * + * product_id filled in with sender's ProductID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet; + int packetlen; // size of received packet + + /* + ------------------------ Return if nothing to do ------------------------- + */ + if (Queue->Num_Receive() == 0) { + return(false); + } + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Get_Receive(0); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packet = (GlobalHeaderType *)(rec_entry->Buffer); + packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); + if (packetlen > 0) + memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); + (*buflen) = packetlen; + (*address) = packet->Address; + (*product_id) = packet->ProductID; + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * IPXGlobalConnClass::Send -- sends a packet * + * * + * This routine gets invoked by NonSequencedConn, when it's processing * + * the Send & Receive Queues. The buffer provided will already have the * + * GlobalHeaderType header embedded in it. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send(char *buf, int buflen) +{ + IPXAddressClass *addr; + int rc; + + /*------------------------------------------------------------------------ + Extract the packet's embedded IPX address + ------------------------------------------------------------------------*/ + addr = &(((GlobalHeaderType *)buf)->Address); + + /*------------------------------------------------------------------------ + If it's a broadcast address, broadcast it + ------------------------------------------------------------------------*/ + if (addr->Is_Broadcast()) { + return(Broadcast (buf, buflen)); + } else { + /*------------------------------------------------------------------------ + Otherwise, send it + ------------------------------------------------------------------------*/ + if (IsBridge && !memcmp (addr, BridgeNet, 4)) { + rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), + BridgeNode); + } else { + rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), NULL); + } + return (rc); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXGlobalConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * This routine is necessary because the Global Connection has to ACK * + * a packet differently from other types of connections; its Send routine * + * assumes that the destination address is embedded within the outgoing * + * packet, so we have to create our ACK Packet using the GlobalHeaderType, * + * not the CommHeaderType. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Service_Receive_Queue (void) +{ + GlobalHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Get_Receive(0); + if (rec_entry==NULL) + return(1); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (GlobalHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Header.Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { + ackpacket.Header.MagicNumber = MagicNum; + ackpacket.Header.Code = PACKET_ACK; + ackpacket.Header.PacketID = packet_hdr->Header.PacketID; + ackpacket.Address = packet_hdr->Address; + ackpacket.ProductID = ProductID; + + Send ((char *)&ackpacket, sizeof(GlobalHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) + Queue->UnQueue_Receive(NULL,NULL,0); + + return(1); + +} /* end of Service_Receive_Queue */ + +/*************************************************************************** + * Set_Bridge -- Sets up connection to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) +{ + if (Configured) { + memcpy (BridgeNet, bridge, 4); + memset (BridgeNode, 0xff, 6); + + if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { + IsBridge = 1; + } else { + IsBridge = 0; + } + } +} + diff --git a/TIBERIANDAWN/IPXGCONN.H b/TIBERIANDAWN/IPXGCONN.H new file mode 100644 index 000000000..527d6e6a3 --- /dev/null +++ b/TIBERIANDAWN/IPXGCONN.H @@ -0,0 +1,158 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxgconn.h_v 1.10 16 Oct 1995 16:47:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 11, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class is a special type of IPX Connection. It can talk to more * + * than one system at a time. It can Broadcast packets to all systems, * + * or send a packet to one individual system. The packets it sends to * + * individual systems can be DATA_NOACK or DATA_ACK packets, but the * + * packets broadcast have to be DATA_NOACK packets. This class is for * + * only the crudest "Who-are-you" type of network communications. Once * + * the IPX Address of another system is identified, a "real" IPX * + * Connection should be created, & further communications done through it. * + * * + * This means that the packet ID field no longer can be used to detect * + * resends, since the receive queue may recieve a lot more packets than * + * we send out. So, re-sends cannot be detected; the application must be * + * designed so that it can handle multiple copies of the same packet. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXGLOBALCONN_H +#define IPXGLOBALCONN_H + + +#include "ipxconn.h" + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is the header for Global Connection messages. It includes the usual +"standard" header that the other connections do; but it also includes an +IPX address field, so the application can get the address of the sender +of this message. This address field must be provided in by the IXP +Connection Manager class, when it calls this class's Receive_Packet function. +---------------------------------------------------------------------------*/ +typedef struct { + CommHeaderType Header; + IPXAddressClass Address; + unsigned short ProductID; +} GlobalHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXGlobalConnClass : public IPXConnClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Here are some useful enums: + .....................................................................*/ + enum GlobalConnectionEnum { + /*.................................................................. + This is the magic number for all Global Connections. Having the + same magic number across products lets us ID different products + on the net. + ..................................................................*/ + GLOBAL_MAGICNUM = 0x1234, + /*.................................................................. + These are the values used for the ProductID field in the Global + Message structure. It also should be the Magic Number used for the + private connections within that product. + This list should be continually updated & kept current. Never ever + ever use an old product ID for your product! + ..................................................................*/ + COMMAND_AND_CONQUER = 0xaa01, + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXGlobalConnClass (int numsend, int numrecieve, int maxlen, + unsigned short product_id); + virtual ~IPXGlobalConnClass () {}; + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req); + virtual int Receive_Packet (void * buf, int buflen, + IPXAddressClass *address); + virtual int Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id); + + /*..................................................................... + This is for telling the connection it can cross a bridge. + .....................................................................*/ + void Set_Bridge (NetNumType bridge); + + /*..................................................................... + The Product ID for this product. + .....................................................................*/ + unsigned short ProductID; + + /*..................................................................... + This describes the address of a bridge we have to cross. This class + supports crossing only one bridge. Storing the bridge's network number + allows us to obtain its local target address only once, then re-use it. + .....................................................................*/ + NetNumType BridgeNet; + NetNodeType BridgeNode; + int IsBridge; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. This special version sends to the address + embedded within the GlobalHeaderType. + .....................................................................*/ + virtual int Send (char *buf, int buflen); + + /*..................................................................... + This routine is overloaded from SequencedConnClass, because the + Global Connection needs to ACK its packets differently from the + other connections. + .....................................................................*/ + virtual int Service_Receive_Queue (void); +}; + +#endif diff --git a/TIBERIANDAWN/IPXMGR.CPP b/TIBERIANDAWN/IPXMGR.CPP new file mode 100644 index 000000000..f3b5f48df --- /dev/null +++ b/TIBERIANDAWN/IPXMGR.CPP @@ -0,0 +1,1969 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxmgr.cpv 1.9 16 Oct 1995 16:48:22 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 4, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXManagerClass::IPXManagerClass -- class constructor * + * IPXManagerClass::~IPXManagerClass -- class destructor * + * IPXManagerClass::Init -- initialization routine * + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * IPXManagerClass::Create_Connection -- creates a new connection * + * IPXManagerClass::Delete_Connection -- deletes a connection * + * IPXManagerClass::Num_Connections -- gets the # of connections * + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * IPXManagerClass::Connection_Name -- gets name for given connection * + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * IPXManagerClass::Connection_Index -- gets given connection's index * + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * IPXManagerClass::Get_Private_Message -- Polls Private Message queue * + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * IPXManagerClass::Global_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Global_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Private_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Private_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" +#include "tcpip.h" + +/*************************************************************************** + * IPXManagerClass::IPXManagerClass -- class constructor * + * * + * INPUT: * + * glb_maxlen Global Channel maximum packet length * + * pvt_maxlen Private Channel maximum packet length * + * socket socket ID to use * + * product_id a unique numerical ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::IPXManagerClass (int glb_maxlen, int pvt_maxlen, + int glb_num_packets, int pvt_num_packets, unsigned short socket, + unsigned short product_id) +{ + int i; + + /*------------------------------------------------------------------------ + Initialize data members + ------------------------------------------------------------------------*/ + /*........................................................................ + IPXStatus = 1 if IPX is installed, 0 if not + ........................................................................*/ + if (IPX_SPX_Installed()==0) { + IPXStatus = 0; + } else { + IPXStatus = 1; + } + + /*........................................................................ + Set listening state flag to off + ........................................................................*/ + Listening = 0; + + /*........................................................................ + No memory has been alloc'd yet + ........................................................................*/ + RealMemAllocd = 0; + + /*........................................................................ + Set max packet sizes, for allocating real-mode memory + ........................................................................*/ + Glb_MaxPacketLen = glb_maxlen; + Glb_NumPackets = glb_num_packets; + Pvt_MaxPacketLen = pvt_maxlen; + Pvt_NumPackets = pvt_num_packets; + + /*........................................................................ + Save the app's product ID + ........................................................................*/ + ProductID = product_id; + + /*........................................................................ + Save our socket ID number + ........................................................................*/ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + + /*........................................................................ + Get the user's IPX local connection number + ........................................................................*/ + if (IPXStatus) { + ConnectionNum = IPX_Get_Connection_Number(); + } else { + ConnectionNum = 0; + } + + /*........................................................................ + Init connection states + ........................................................................*/ + NumConnections = 0; + CurConnection = 0; + for (i = 0; i < CONNECT_MAX; i++) + Connection[i] = 0; + GlobalChannel = 0; + + SendOverflows = 0; + ReceiveOverflows = 0; + BadConnection = IPXConnClass::CONNECTION_NONE; + + /*........................................................................ + Init timing parameters + ........................................................................*/ + RetryDelta = 2; // 2 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 60; // report bad connection after 1 second + +} /* end of IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::~IPXManagerClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::~IPXManagerClass() +{ + int i; + + /*------------------------------------------------------------------------ + Stop all IPX events + ------------------------------------------------------------------------*/ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + /*------------------------------------------------------------------------ + Free all protected-mode memory + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + /*------------------------------------------------------------------------ + Free all real-mode memory + ------------------------------------------------------------------------*/ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + +} /* end of ~IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::Init -- initialization routine * + * * + * This routine allocates memory, + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Init() +{ + int i; + + if (!(GameToPlay == GAME_INTERNET)){ + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) { + return(false); + } + + /*------------------------------------------------------------------------ + Stop Listening + ------------------------------------------------------------------------*/ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + /*------------------------------------------------------------------------ + Free Real-mode memory + ------------------------------------------------------------------------*/ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + }else{ + /* + ** Pretend IPX is available for Internet games whether it is or not + */ + IPXStatus = 1; + } + + /*------------------------------------------------------------------------ + Free protected-mode memory + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + + if (!(GameToPlay == GAME_INTERNET)){ + /*------------------------------------------------------------------------ + Allocate real-mode memory + ------------------------------------------------------------------------*/ + if (!Alloc_RealMode_Mem()) return(false); + RealMemAllocd = 1; + } + + /*------------------------------------------------------------------------ + Allocate the Global Channel + ------------------------------------------------------------------------*/ + GlobalChannel = new IPXGlobalConnClass (Glb_NumPackets, Glb_NumPackets, + Glb_MaxPacketLen, ProductID); + if (!GlobalChannel) + return(false); + GlobalChannel->Init(); + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + + /*------------------------------------------------------------------------ + Configure the IPX Connections + ------------------------------------------------------------------------*/ + IPXConnClass::Configure(Socket, ConnectionNum, ListenECB, SendECB, + FirstHeaderBuf, SendHeader, FirstDataBuf, SendBuf, Handler, PacketLen); + + /*------------------------------------------------------------------------ + Start Listening + ------------------------------------------------------------------------*/ + if (!(GameToPlay == GAME_INTERNET)){ + if (!IPXConnClass::Start_Listening()) return(false); + } + + Listening = 1; + + return(true); +} + + +/*************************************************************************** + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = IPX is installed; 0 = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Is_IPX(void) +{ + return(IPXStatus); + +} /* end of Is_IPX */ + + +/*************************************************************************** + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters for all existing connections, and * + * all connections created from now on. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + int i; + + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + if (GlobalChannel) { + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + } + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Set_Retry_Delta (RetryDelta); + Connection[i]->Set_Max_Retries (MaxRetries); + Connection[i]->Set_TimeOut (Timeout); + } + +} /* end of Set_Timing */ + + +/*************************************************************************** + * IPXManagerClass::Create_Connection -- creates a new connection * + * * + * INPUT: * + * id application-specific numerical ID for this connection * + * node ptr to IPXNodeIDType (name & address) * + * address IPX address for this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * Never create a connection with an 'id' of -1. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +bool IPXManagerClass::Create_Connection(int id, char *name, IPXAddressClass *address) +{ +#if (0) + char temp[80]; + + NetNumType num; + NetNodeType node; + + address->Get_Address (num, node); + + sprintf (temp, "Address:%02x %02x %02x %02x %02x %02x\n", node[0], + node[1], + node[2], + node[3], + node[4], + node[5]); + CCDebugString (temp); + + sprintf (temp, "Network:%02x %02x %02x %02x\n", num[0], + num[1], + num[2], + num[3]); + CCDebugString (temp); +#endif //(0) + + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) + return(false); + + /* + ------------------------- Error if no more room -------------------------- + */ + if (NumConnections==CONNECT_MAX) + return(false); + + /* + ------------------------- Create new connection -------------------------- + */ + Connection[NumConnections] = new IPXConnClass(Pvt_NumPackets, + Pvt_NumPackets, Pvt_MaxPacketLen, ProductID, address, id, name); + if (!Connection[NumConnections]) + return(false); + + Connection[NumConnections]->Init (); + Connection[NumConnections]->Set_Retry_Delta (RetryDelta); + Connection[NumConnections]->Set_Max_Retries (MaxRetries); + Connection[NumConnections]->Set_TimeOut (Timeout); + + NumConnections++; + + return(true); + +} /* end of Create_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Delete_Connection -- deletes a connection * + * * + * INPUT: * + * id ID of connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +bool IPXManagerClass::Delete_Connection(int id) +{ + int i,j; + + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) + return(false); + + /* + ------------------- Error if no connections to delete -------------------- + */ + if (NumConnections==0) + return(false); + + /* + ---------------------- Loop through all connections ---------------------- + */ + for (i = 0; i < NumConnections; i++) { + /* + ........................ If a match, delete it ........................ + */ + if (Connection[i]->ID==id) { + delete Connection[i]; + /* + ................ Move array elements back one index ................ + */ + for (j = i; j < NumConnections - 1; j++) { + Connection[j] = Connection[j+1]; + } + /* + ......................... Adjust counters .......................... + */ + NumConnections--; + if (CurConnection >= NumConnections) + CurConnection = 0; + return(true); + } + } + + /* + ---------------------------- No match; error ----------------------------- + */ + return(false); + +} /* end of Delete_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Num_Connections -- gets the # of connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of connections * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Num_Connections(void) +{ +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** If we are connected to the VSS then dont coumt that in the number of connections. + */ + if (Connection_Index (VSS_ID) == IPXConnClass::CONNECTION_NONE){ + return(NumConnections); + }else{ + return(NumConnections -1); + } +#else // VIRTUAL_SUBNET_SERVER + + return(NumConnections); + +#endif //VIRTUAL_SUBNET_SERVER + +} /* end of Num_Connections */ + + +/*************************************************************************** + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * * + * INPUT: * + * index index of connection to retrieve * + * * + * OUTPUT: * + * ID for that connection, CONNECTION_NONE if invalid index * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(Connection[index]->ID); + } else { + return(IPXConnClass::CONNECTION_NONE); + } +} + + +/*************************************************************************** + * IPXManagerClass::Connection_Name -- retrieves name for given connection * + * * + * INPUT: * + * id ID of connection to get name of * + * * + * OUTPUT: * + * ptr to connection's name, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +char *IPXManagerClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(Connection[i]->Name); + } + } + + return(NULL); + +} /* end of Connection_Name */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * * + * INPUT: * + * id ID of connection to get address for * + * * + * OUTPUT: * + * pointer to IXPAddressClass, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +IPXAddressClass * IPXManagerClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(&Connection[i]->Address); + } + } + + return(NULL); + +} /* end of Connection_Address */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Index -- gets given connection's index * + * * + * INPUT: * + * ID to retrieve index for * + * * + * OUTPUT: * + * index for this connection, CONNECTION_NONE if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) + return(i); + } + + return(IPXConnClass::CONNECTION_NONE); + +} /* end of Connection_Index */ + + +/*************************************************************************** + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buf * + * ack_req 1 = ACK required; 0 = no ACK required * + * address address to send to; NULL = broadcast * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Global_Message(void *buf, int buflen, + int ack_req, IPXAddressClass *address) +{ + int rc; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) return(false); + + rc = GlobalChannel->Send_Packet (buf, buflen, address, ack_req); + if (!rc) + SendOverflows++; + + return(rc); +} + + +/*************************************************************************** + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * * + * INPUT: * + * buf buffer to store received packet * + * buflen length of data placed in 'buf' * + * address IPX address of sender * + * product_id product ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Global_Message(void *buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) return(false); + + return(GlobalChannel->Get_Packet (buf, buflen, address, product_id)); +} + + +/*************************************************************************** + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of 'buf' * + * conn_id connection ID to send to (CONNECTION_NONE = all) * + * ack_req 1 = ACK required; 0 = no ACK required * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Private_Message(void *buf, int buflen, int ack_req, + int conn_id) +{ + int i; // loop counter + int connect_idx; // index of channel to send to, if specified + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + + /*------------------------------------------------------------------------ + Send the message to all connections + ------------------------------------------------------------------------*/ + if (conn_id==IPXConnClass::CONNECTION_NONE) { + + /* + ** If this is an internet game and no ack is reqired then we only need to send + ** the packet to the VSS and it will forward it to all the other players. + */ + +#ifdef VIRTUAL_SUBNET_SERVER + if (ack_req || (!Winsock.Get_Connected() || !UseVirtualSubnetServer)){ +#endif //VIRTUAL_SUBNET_SERVER + /*..................................................................... + Check for room in all connections + .....................................................................*/ + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + if (Connection[i]->Queue->Num_Send()==Connection[i]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif // VIRTUAL_SUBNET_SERVER + } + + /*..................................................................... + Send packet to all connections + .....................................................................*/ + + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + Connection[i]->Send_Packet (buf, buflen, ack_req); +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif //VIRTUAL_SUBNET_SERVER + } + +#ifdef VIRTUAL_SUBNET_SERVER + }else{ + /* + ** Send the packet to the VSS with a 0 header so it gets forwarded to all players. + */ + if (Connection[ Connection_Index(VSS_ID) ]->Queue->Num_Send() == Connection[ Connection_Index(VSS_ID) ]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } + Connection[ Connection_Index(VSS_ID) ]->Send_Packet (buf, buflen, 0); + } +#endif // VIRTUAL_SUBNET_SERVER + + return(true); + + + } else { + + /*------------------------------------------------------------------------ + Send the message to the specified connection + ------------------------------------------------------------------------*/ + connect_idx = Connection_Index (conn_id); + if (connect_idx == IPXConnClass::CONNECTION_NONE) { + SendOverflows++; + return(false); + } + + /*..................................................................... + Check for room in the connection + .....................................................................*/ + if (Connection[connect_idx]->Queue->Num_Send() == + Connection[connect_idx]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } + + /*..................................................................... + Send the packet to that connection + .....................................................................*/ + Connection[connect_idx]->Send_Packet (buf, buflen, ack_req); + return(true); + } +} + + +/*************************************************************************** + * IPXManagerClass::Get_Private_Message -- Polls the Private Message queue * + * * + * INPUT: * + * buf buffer to store incoming packet * + * buflen length of data placed in 'buf' * + * conn_id filled in with connection ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Private_Message(void *buf, int *buflen, int *conn_id) +{ + int i; + int rc; + int c_id; +#ifdef VIRTUAL_SUBNET_SERVER + int vss = 0; +#endif // VIRTUAL_SUBNET_SERVER + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + +#ifdef VIRTUAL_SUBNET_SERVER + if (Winsock.Get_Connected()){ + vss = (int) UseVirtualSubnetServer; + } +#endif //VIRTUAL_SUBNET_SERVER + + /*------------------------------------------------------------------------ + Safety check: ensure CurConnection is in range. + ------------------------------------------------------------------------*/ +#ifdef VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections - vss) +#else // VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections) +#endif //VIRTUAL_SUBNET_SERVER + CurConnection = 0; + + /*------------------------------------------------------------------------ + Scan all connections for a received packet, starting with 'CurConnection' + ------------------------------------------------------------------------*/ +#ifdef VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections - vss; i++) { +#else // VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections; i++) { +#endif // VIRTUAL_SUBNET_SERVER + /*..................................................................... + Check this connection for a packet + .....................................................................*/ + rc = Connection[CurConnection]->Get_Packet (buf, buflen); + c_id = Connection[CurConnection]->ID; + + /*..................................................................... + Increment CurConnection to the next connection index + .....................................................................*/ + CurConnection++; +#ifdef VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections - vss) +#else // VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections) +#endif // VIRTUAL_SUBNET_SERVER + CurConnection = 0; + + /*..................................................................... + If we got a packet, return the connection ID + .....................................................................*/ + if (rc) { + (*conn_id) = c_id; + return(true); + } + } + + return(false); +} + + +/*************************************************************************** + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Service(void) +{ + + int rc = 1; + int i; + CommHeaderType *packet; + int packetlen; + IPXAddressClass address; + + + +#ifndef NOT_FOR_WIN95 + + unsigned char temp_receive_buffer[1024]; + int recv_length; + + if (Winsock.Get_Connected()){ + + while ((recv_length = Winsock.Read(temp_receive_buffer, 1024))!=0){ + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Get a pointer to the data header and swap the bit mask + */ + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; //NULL; + unsigned short *swapptr = (unsigned short*)CurHeaderBuf; + *swapptr = ntohs (*swapptr); + + CurDataBuf = (char*)&temp_receive_buffer[2]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length-2; +#else //VIRTUAL_SUBNET_SERVER + CurHeaderBuf = NULL; + CurDataBuf = (char*)&temp_receive_buffer[0]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length; + +#endif //VIRTUAL_SUBNET_SERVER + + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; +#if (0) +char tempbuf[256]; + +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Received packet type %d, ID=%d, code=%s, length=%d \n", CurDataBuf[sizeof(CommHeaderType)], + packet->PacketID, + pcode[packet->Code], + recv_length); +CCDebugString (tempbuf); +#endif //(0) + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->Address == address && Connection[i]->ID != VSS_ID) { +#else //VIRTUAL_SUBNET_SERVER + if (Connection[i]->Address == address) { +#endif //VIRTUAL_SUBNET_SERVER + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + }else{ + + while (IPX_Get_Outstanding_Buffer95(&temp_receive_buffer[0])){ + + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; + CurDataBuf = (char*)&temp_receive_buffer[sizeof(IPXHeaderType)]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + } + + //IPX_Start_Listening95(); + + +#else //NOT_FOR_WIN95 + + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(false); + + /*------------------------------------------------------------------------ + Loop until there are no more packets to process. + ------------------------------------------------------------------------*/ + for (;;) { + /*..................................................................... + Check the BufferFlags for the "current" buffer; if it's empty, + break; out of the loop. + .....................................................................*/ + if (BufferFlags[CurIndex]==0) + break; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + /*..................................................................... + Set the current BufferFlags to 0 (since we've cleaned out this buffer) + .....................................................................*/ + BufferFlags[CurIndex] = 0; + + /*..................................................................... + Go to the next packet buffer + .....................................................................*/ + CurIndex++; + CurHeaderBuf = (IPXHeaderType *)(((char *)CurHeaderBuf) + FullPacketLen); + CurDataBuf = ((char *)CurDataBuf) + FullPacketLen; + if (CurIndex >= NumBufs) { + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + CurIndex = 0; + } + + } + +#endif //NOT_FOR_WIN95 + + /*------------------------------------------------------------------------ + Service all connections. If a connection reports that it's gone "bad", + report an error to the caller. If it's the Global Channel, un-queue the + send entry that's holding things up. This will keep the Global Channel + from being clogged by one un-ACK'd outgoing packet. + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + if (!GlobalChannel->Service()) { + GlobalChannel->Queue->UnQueue_Send (NULL, NULL, 0); + rc = 0; + } + } + for (i = 0; i < NumConnections; i++) { + if (!Connection[i]->Service()) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + rc = 0; + BadConnection = Connection[i]->ID; +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif //VIRTUAL_SUBNET_SERVER + } + } + + if (rc) + BadConnection = IPXConnClass::CONNECTION_NONE; + + return(rc); + + +} /* end of Service */ + + +/*************************************************************************** + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ID of bad connection; CONNECTION_NONE if none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Bad_Connection(void) +{ + return(BadConnection); + +} /* end of Get_Bad_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Send Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Send(void) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(0); + + return(GlobalChannel->Queue->Num_Send()); + +} /* end of Global_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Receive(void) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(0); + + return(GlobalChannel->Queue->Num_Receive()); + +} /* end of Global_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * # entries in the Private Send Queue * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Send(int id) +{ + int i; + int maxnum; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + + /*------------------------------------------------------------------------ + If connection ID specified, return that connection's # of packets + ------------------------------------------------------------------------*/ + if (id != IPXConnClass::CONNECTION_NONE) { + i = Connection_Index(id); + if (i != IPXConnClass::CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Send()); + } else { + return(false); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, return the max # of all connections + ------------------------------------------------------------------------*/ + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() > maxnum) { + maxnum = Connection[i]->Queue->Num_Send(); + } + } + return(maxnum); + } +} + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * id ID of connection to query * + * * + * OUTPUT: * + * # entries in the Private Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Receive(int id) +{ + int i; + int maxnum; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) + return(0); + + /*------------------------------------------------------------------------ + If connection ID specified, return that connection's # of packets + ------------------------------------------------------------------------*/ + if (id != IPXConnClass::CONNECTION_NONE) { + i = Connection_Index(id); + if (i != IPXConnClass::CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Receive()); + } else { + return(0); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, return the max # of all connections + ------------------------------------------------------------------------*/ + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Receive() > maxnum) + maxnum = Connection[i]->Queue->Num_Receive(); + } + return(maxnum); + } +} + + +/*************************************************************************** + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * * + * INPUT: * + * socket new socket ID to use * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not call this function after communications have started; you * + * must call it before calling Init(). * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Socket(unsigned short socket) +{ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + +} /* end of Set_Socket */ + + +/*************************************************************************** + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * largest avg response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Response_Time(void) +{ + unsigned long resp; + unsigned long maxresp = 0; + int i; +#ifdef VIRTUAL_SUBNET_SERVER + int vss=0; + + if (Winsock.Get_Connected()){ + vss = (int) UseVirtualSubnetServer; + } + + for (i = 0; i < NumConnections - vss; i++) { +#else //VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections; i++) { +#endif // VIRTUAL_SUBNET_SERVER + resp = Connection[i]->Queue->Avg_Response_Time(); + if (resp > maxresp) + maxresp = resp; + } + + return(maxresp); +} + + +/*************************************************************************** + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * avg global channel response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Global_Response_Time(void) +{ + if (GlobalChannel) { + return (GlobalChannel->Queue->Avg_Response_Time()); + } else { + return (0); + } +} + + +/*************************************************************************** + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Reset_Response_Time(void) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Queue->Reset_Response_Time(); + } + + if (GlobalChannel) + GlobalChannel->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * buf ptr * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void * IPXManagerClass::Oldest_Send(void) +{ + int i,j; + unsigned long time; + unsigned long mintime = 0xffffffff; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < NumConnections; i++) { + + send_entry = NULL; + + for (j = 0; j < Connection[i]->Queue->Num_Send(); j++) { + send_entry = Connection[i]->Queue->Get_Send(j); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + break; + } else { + send_entry = NULL; + } + } + } + + if (send_entry!=NULL) { + + time = send_entry->FirstTime; + + if (time < mintime) { + mintime = time; + buf = send_entry->Buffer; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Bridge(NetNumType bridge) +{ + if (GlobalChannel) { + GlobalChannel->Set_Bridge(bridge); + } +} + + +/*************************************************************************** + * IPXManagerClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Configure_Debug(int index, int offset, int size, + char **names, int maxnames) +{ + if (index == -1) { + GlobalChannel->Queue->Configure_Debug (offset, size, names, maxnames); + } else { + if (Connection[index]) { + Connection[index]->Queue->Configure_Debug (offset, size, names, maxnames); + } + } +} + + +/*************************************************************************** + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * * + * INPUT: * + * index index of connection to display (-1 = Global Channel) * + * refresh 1 = complete screen refresh * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Mono_Debug_Print(int index, int refresh) +{ +#ifdef WWLIB32_H + char txt[80]; + int i; + + if (index == -1) + GlobalChannel->Queue->Mono_Debug_Print (refresh); + else if (Connection[index]) + Connection[index]->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (20,1); + Mono_Printf ("IPX Queue:"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (43,1); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,2); + Mono_Printf ("Receive Overflows:"); + + } + + Mono_Set_Cursor (32,1); + Mono_Printf ("%d",index); + + Mono_Set_Cursor (32,2); + if (index == -1) { + Mono_Printf ("%d ", GlobalChannel->Queue->Avg_Response_Time()); + } else { + Mono_Printf ("%d ", Connection[index]->Queue->Avg_Response_Time()); + } + + Mono_Set_Cursor (59,1); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", ReceiveOverflows); + + for (i = 0; i < NumBufs; i++) { + if (BufferFlags[i]) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + } + txt[i] = 0; + Mono_Set_Cursor ((80-NumBufs)/2,3); + Mono_Printf ("%s",txt); + +#else + index = index; + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Alloc_RealMode_Mem(void) +{ +#ifdef NOT_FOR_WIN95 + union REGS regs; + struct SREGS sregs; + int size; // required size of allocation + unsigned char *realmode; // start addresses of real-mode data + int realmodelen; // length of real-mode data + unsigned long func_val; + char *p; // for parsing buffer + int i; + + /*------------------------------------------------------------------------ + Compute # of buffers we need to allocate, & the max size of each one + ------------------------------------------------------------------------*/ + NumBufs = Glb_NumPackets + (Pvt_NumPackets * CONNECT_MAX); + + PacketLen = Glb_MaxPacketLen + sizeof (GlobalHeaderType); + if (Pvt_MaxPacketLen + sizeof (CommHeaderType) > PacketLen) + PacketLen = Pvt_MaxPacketLen + sizeof (CommHeaderType); + + FullPacketLen = PacketLen + sizeof(IPXHeaderType); + + /*------------------------------------------------------------------------ + Compute the size of everything we'll ever need, allocate it in one big + chunk. The memory is used as follows: + - Real-mode assembly IPX callback routine, plus its data, + (which includes the ListenECB) + - Array of IPX Packet buffers (IPXHeader plus data buffer) + - SendECB: ECB for sending + - SendHeader: IPX Header for sending + - SendBuf: Packet buffer for sending + - BufferFlags: 1 byte for each incoming packet buffer; 1=in use, 0=free + ------------------------------------------------------------------------*/ + realmode = (unsigned char *)Get_RM_IPX_Address(); + realmodelen = Get_RM_IPX_Size(); + size = realmodelen + // assembly routine & its data + (FullPacketLen * NumBufs) + // array of packet buffers + sizeof(ECBType) + // SendECB + FullPacketLen + // SendHeader & SendBuf + NumBufs; // BufferFlags + if (size > 65535) + return(false); + + /*------------------------------------------------------------------------ + Allocate DOS memory for the ECB, IPXHeader & packet buffers: + AX = 0x100 + BX = # paragraphs to allocate + - if Success, AX = real-mode segment, DX = selector + - if Failure, carry flag is set + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = ((size + 15) >> 4); // # paragraphs to allocate + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(false); + } + + /*........................................................................ + Save the values of the returned segment & selector + ........................................................................*/ + Selector = regs.w.dx; + Segment = regs.w.ax; + RealMemSize = size; + RealModeData = (RealModeDataType *)(((long)Segment) << 4); + + /*------------------------------------------------------------------------ + Lock the memory (since we're servicing interrupts with it) + AX = 0x600 + BX:CX = starting linear address of memory to lock + SI:DI = size of region to lock (in bytes) + - If Failure, carry flag is set. + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + return(false); + } + + /*------------------------------------------------------------------------ + Copy the Real-mode code into our memory buffer + ------------------------------------------------------------------------*/ + p = (char *)(((long)Segment) << 4); + memcpy (p,realmode,realmodelen); + p += realmodelen; + + /*------------------------------------------------------------------------ + Compute & save the entry point for the real-mode packet handler + ------------------------------------------------------------------------*/ + func_val = (unsigned long)RealModeData; + Handler = (((func_val & 0xffff0) << 12) | + ((func_val & 0x000f) + RealModeData->FuncOffset)); + + /*------------------------------------------------------------------------ + Fill in buffer pointers + ------------------------------------------------------------------------*/ + ListenECB = &(RealModeData->ListenECB); + + FirstHeaderBuf = (IPXHeaderType *)p; + FirstDataBuf = (((char *)FirstHeaderBuf) + sizeof(IPXHeaderType)); + CurIndex = 0; + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + p += FullPacketLen * NumBufs; + + SendECB = (ECBType *)p; + p += sizeof (ECBType); + + SendHeader = (IPXHeaderType *)p; + p += sizeof (IPXHeaderType); + + SendBuf = (char *)p; + p += PacketLen; + + BufferFlags = (char *)p; + + /*------------------------------------------------------------------------ + Fill in the real-mode routine's data (The ECB will be filled in when we + command IPX to Listen). + ------------------------------------------------------------------------*/ + RealModeData->NumBufs = (short)NumBufs; + RealModeData->BufferFlags = (char *) + ((((long)BufferFlags & 0xffff0) << 12) | + ((long)BufferFlags & 0x0000f)); + RealModeData->PacketSize = (short)FullPacketLen; + RealModeData->FirstPacketBuf = (IPXHeaderType *) + ((((long)FirstHeaderBuf & 0xffff0) << 12) | + ((long)FirstHeaderBuf & 0x0000f)); + RealModeData->CurIndex = 0; + RealModeData->CurPacketBuf = RealModeData->FirstPacketBuf; + RealModeData->Semaphore = 0; + RealModeData->ReEntrantCount = 0; + + /*------------------------------------------------------------------------ + Init state of all buffers to empty + ------------------------------------------------------------------------*/ + for (i = 0; i < NumBufs; i++) + BufferFlags[i] = 0; + + /*------------------------------------------------------------------------ + Check the start & end markers in the real-mode memory area + ------------------------------------------------------------------------*/ + if (RealModeData->Marker1 != 0x1111 || + RealModeData->Marker2 != 0x2222) { + Free_RealMode_Mem(); + return(false); + } else { + return(true); + } +#else //NOT_FOR_WIN95 + + return (TRUE); + +#endif //NOT_FOR_WIN95 +} + + +/*************************************************************************** + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Free_RealMode_Mem(void) +{ +#ifdef NOT_FOR_WIN95 + union REGS regs; + struct SREGS sregs; + int rc = 1; + + /*------------------------------------------------------------------------ + Unlock the memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + rc = 0; + } + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + + return(rc); + +#else // NOT_FOR_WIN95 + + return (1); + +#endif // NOT_FOR_WIN95 +} /* end of Free_RealMode_Mem */ + + diff --git a/TIBERIANDAWN/IPXMGR.H b/TIBERIANDAWN/IPXMGR.H new file mode 100644 index 000000000..8263f6d6f --- /dev/null +++ b/TIBERIANDAWN/IPXMGR.H @@ -0,0 +1,392 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\ipxmgr.h_v 1.10 16 Oct 1995 16:47:34 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for IPX network communications. It * + * creates, manages, & orchestrates multiple IPX connections, as well as * + * the "global" connection ("Global Channel"), which can talk to any * + * system on the net. * + * * + * Use the Global Channel to query systems for their names, ID's, & * + * IPX addresses. Then, create a Private Connection with each system * + * that joins your game, and use the Private Channel to send game packets * + * (the private channel will perform somewhat faster, & gives you better * + * control than the Global Channel; it can detect retries, and the Global * + * Channel can't). * + * * + * HOW THIS CLASS WORKS: * + * This class has to set up an IPX Event Service Routine in low (DOS) * + * memory. So, it uses DPMI to allocate & lock a chunk of DOS memory; * + * this memory is used for all incoming packet buffers, the outgoing * + * packet buffer, and the actual code for the event handler. The real- * + * mode handler code & this class share a portion of memory that's mapped * + * into a "RealModeDataType" structure. As packets come in, the handler * + * points IPX to the next available packet buffer & restarts listening; * + * it sets a flag to tell this class that a packet is present at that * + * buffer slot. This class must read all the packets & determine which * + * connection they go with (the Global Channel, or one of the Private * + * Channels). This parsing is done in the Service routine for this class. * + * * + * Constructor: Just inits some variables, checks to see if IPX is there * + * Destructor: Complete shutdown; stops IPX listening, frees all memory * + * Init: Should only be called once (but can be called more); * + * allocates all memory, creates the Global Channel * + * connection, starts IPX listening. By not placing this * + * step in the constructor, the app can control when * + * listening actually starts; also, you don't get a bunch * + * of allocations just by declaring an IPXManagerClass * + * instance. You have to call Init() for the allocations * + * to occur. * + * Connection utilities: Create & manage Private Connections. Each * + * connection has its own IPX address, numerical ID, and * + * character name (presumably the name of the other * + * player). * + * Send/Get_Global_Message: adds a packet to the Global Connection queue, * + * or reads from the queue. The caller should check the * + * ProductID value from returned packets to be sure it's * + * talking to the right product. * + * Send/Get_Private_Message: adds a packet to a Private Connection queue, * + * or reads from the queue * + * Service: Checks the Real-Mode-Memory packet array to see if any * + * new packets have come in; if they have, it parses them * + * & distributes them to the right connection queue. The * + * queue's Service routine handles ACK'ing or Resending * + * packets. * + * * + * Here's a memory map of the Real-Mode memory block. 'N' is the number * + * of packet buffers allocated in low memory: * + * * + * ---------------------------------- * + * | Shared-memory data | * + * |--------------------------------| * + * | Real-mode event handler code | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 0 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 1 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 2 | * + * |--------------------------------| * + * | . . . | * + * |--------------------------------| * + * | IPX Header & Packet Buffer N | * + * |--------------------------------| * + * | Send Event Control Block | * + * |--------------------------------| * + * | Send IPX Header | * + * |--------------------------------| * + * | Send Packet Buffer | * + * |--------------------------------| * + * | Flags Array [N] | * + * ---------------------------------- * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXMANAGER_H +#define IPXMANAGER_H + + +/* +********************************* Includes ********************************** +*/ +#include "ipxconn.h" +#include "ipxgconn.h" +#include "ipxaddr.h" +#include "connmgr.h" + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is Virgin Interactive Entertainment's registered socket ID. +---------------------------------------------------------------------------*/ +#define VIRGIN_SOCKET 0x8813 + +/*--------------------------------------------------------------------------- +This is the maximum number of IPX connections supported. Just change this +value to support more. +---------------------------------------------------------------------------*/ +#define CONNECT_MAX 6 + +/*--------------------------------------------------------------------------- +These routines report the location & length of the real-mode routine, as +it's stored in protected-mode memory. +---------------------------------------------------------------------------*/ +extern "C" { + void *Get_RM_IPX_Address(void); + long Get_RM_IPX_Size(void); +} + +/* +***************************** Class Declaration ***************************** +*/ +class IPXManagerClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXManagerClass (int glb_maxlen, int pvt_maxlen, int glb_num_packets, + int pvt_num_packets, unsigned short socket, unsigned short product_id); + virtual ~IPXManagerClass (); // stop listening + + /*..................................................................... + Initialization routines. + .....................................................................*/ + int Init (void); + int Is_IPX(void); + virtual void Set_Timing (unsigned long retrydelta, unsigned long maxretries, + unsigned long timeout); + void Set_Bridge(NetNumType bridge); + + /*..................................................................... + These routines control creation of the "Connections" (data queues) for + each remote system. + .....................................................................*/ + bool Create_Connection(int id, char *name, IPXAddressClass *address); + bool Delete_Connection(int id); + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + char *Connection_Name(int id); + IPXAddressClass * Connection_Address(int id); + virtual int Connection_Index(int id); + + /*..................................................................... + This is how the application sends & receives messages. + .....................................................................*/ + int Send_Global_Message (void *buf, int buflen, int ack_req = 0, + IPXAddressClass *address = NULL); + int Get_Global_Message (void *buf, int *buflen, IPXAddressClass *address, + unsigned short *product_id); + + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, int *conn_id); + + /*..................................................................... + The main polling routine; should be called as often as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine reports which connection has an error on it. + .....................................................................*/ + int Get_Bad_Connection(void); + + /*..................................................................... + Queue utility routines. The application can determine how many + messages are in the send/receive queues. + .....................................................................*/ + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + /*..................................................................... + This routine changes the socket ID assigned the IPX Manager when it + was constructed. Do not call this function after calling Init()! + The Socket ID should be known by both ends of the communications before + any packets are sent. + .....................................................................*/ + void Set_Socket(unsigned short socket); + + /*..................................................................... + Routines to return the largest average queue response time, and to + reset the response time for all queues. + .....................................................................*/ + virtual unsigned long Response_Time(void); + unsigned long Global_Response_Time(void); + virtual void Reset_Response_Time(void); + + /*..................................................................... + This routine returns a pointer to the oldest non-ACK'd buffer I've sent. + .....................................................................*/ + void * Oldest_Send(void); + + /*..................................................................... + Debug routines + .....................................................................*/ + virtual void Configure_Debug(int index, int offset, int size, + char **names, int maxnames); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + These routines allocate & free the DOS Real-mode memory block. + .....................................................................*/ + int Alloc_RealMode_Mem(void); + int Free_RealMode_Mem(void); + + /*..................................................................... + Misc variables + .....................................................................*/ + unsigned int IPXStatus : 1; // 0 = no IPX, 1 = IPX found + unsigned int Listening : 1; // 1 = Listening is on + unsigned int RealMemAllocd : 1; // 1 = Real-mode memory has been alloc'd + + /*..................................................................... + Packet Sizes, used for allocating real-mode memory + .....................................................................*/ + int Glb_MaxPacketLen; // Global Channel maximum packet size + int Glb_NumPackets; // # Global send/receive packets + int Pvt_MaxPacketLen; // Private Channel maximum packet size + int Pvt_NumPackets; // # Private send/receive packets + + /*..................................................................... + The ProductID is used in the Global Channel's packet header, and it's + used for the Private Channels' Magic Number. + .....................................................................*/ + unsigned short ProductID; // product ID + + /*..................................................................... + The Socket ID, and local Novell Connection Number + .....................................................................*/ + unsigned short Socket; // Our socket ID for sending/receiving + int ConnectionNum; // local connection #, 0=not logged in + + /*..................................................................... + Array of connection queues + .....................................................................*/ + IPXConnClass * Connection[CONNECT_MAX]; // array of connection object ptrs + int NumConnections; // # connection objects in use + IPXGlobalConnClass *GlobalChannel; // the Global Channel + + /*..................................................................... + Current queue for polling for received packets + .....................................................................*/ + int CurConnection; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /*--------------------------------------------------------------------- + Real-mode memory pointers and such + ---------------------------------------------------------------------*/ + /*..................................................................... + This is a structure that mirrors data in real-mode memory: + .....................................................................*/ + typedef struct { + short Marker1; // the byte ID marker + ECBType ListenECB; // the Listening ECB + short NumBufs; // # of buffers we're giving to the handler + char *BufferFlags; // array of buffer-avail flags + short PacketSize; // size of packet including IPX header + IPXHeaderType *FirstPacketBuf; // ptr to 1st packet buffer + short CurIndex; // handler's current packet index + IPXHeaderType *CurPacketBuf; // handler's current packet buf + short FuncOffset; // contains offset of code + char Semaphore; // prevents re-entrancy + short ReEntrantCount; // times we've been called re-entrantly + short StackPtr; // real-mode stack pointer + short StackSeg; // real-mode stack segment + short StackPtr_int; // internal stack pointer + short StackSeg_int; // internal stack segment + short StackCheck; // stack check value (0x1234) + short Stack[256]; // actual stack space + short StackSpace; // label for top of stack + short Marker2; // the byte ID marker + } RealModeDataType; + + /*..................................................................... + The number & size of packet buffers in low memory + .....................................................................*/ + int NumBufs; // # packet buffers allocated + int PacketLen; // size of packet without IPX header + int FullPacketLen; // size of packet including IPX header + + /*..................................................................... + Selector & Segment of the DOS allocation; + Size of the allocation; + Ptr to the real-mode assembly data area + .....................................................................*/ + unsigned short Selector; // selector of DOS allocation pointer + unsigned short Segment; // real-mode segment of DOS allocation + int RealMemSize; // size of real mode memory allocated + RealModeDataType *RealModeData; // assembly routine & its data + + /*..................................................................... + This is a real-mode pointer to the address of the real-mode assembly + entry point. + .....................................................................*/ + long Handler; + + /*..................................................................... + Event Control Block for listening; contained within the real-mode + assembly routine's data area + .....................................................................*/ + ECBType *ListenECB; // ECB for listening + + /*..................................................................... + ptr to the 1st header & data buffers in the packet buffer array + .....................................................................*/ + IPXHeaderType *FirstHeaderBuf; // array of packet headers & buffers + char *FirstDataBuf; // 1st data buffer area + + /*..................................................................... + Current packet index & ptrs for parsing packets + .....................................................................*/ + int CurIndex; // Current packet index, for reading + IPXHeaderType *CurHeaderBuf; // Current packet ptr, for reading + char *CurDataBuf; // Current actual data ptr + + /*..................................................................... + ECB, header, & buffer for sending + .....................................................................*/ + ECBType *SendECB; // ECB for sending + IPXHeaderType *SendHeader; // Header for sending + char *SendBuf; // buffer for sending + + /*..................................................................... + Flags indicating whether a buffer contains data or not (1 = full) + The IPXManager must clear this flag; the real-mode routine will set it. + .....................................................................*/ + char *BufferFlags; // array of rx-buffer-avail flags + + /*..................................................................... + Various Statistics + .....................................................................*/ + int SendOverflows; + int ReceiveOverflows; + int BadConnection; +}; + +#endif +/*************************** end of ipxmgr.h *******************************/ diff --git a/TIBERIANDAWN/IPXPROT.ASM b/TIBERIANDAWN/IPXPROT.ASM new file mode 100644 index 000000000..e5bf9d6b4 --- /dev/null +++ b/TIBERIANDAWN/IPXPROT.ASM @@ -0,0 +1,110 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;!!!!!!!!!!!!!!!!!!! lock the allocation + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +global C Get_RM_IPX_Address:NEAR +global C Get_RM_IPX_Size:NEAR + +;********************************* Data ************************************ + DATASEG + +LABEL RealBinStart BYTE +include "obj\ipxreal.ibn" +LABEL RealBinEnd BYTE + +;********************************* Data ************************************ + CODESEG + +;*************************************************************************** +;* Get_RM_IPX_Address -- Return address of real mode code for copy. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* VOID * to the address of the real mode IPX code * +;* * +;* PROTO: * +;* VOID *Get_RM_IPX_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* Get_RM_IPX_Size -- return size of real mode IPX code. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* LONG size of the real mode IPX code * +;* * +;* PROTO: * +;* LONG Get_RM_IPX_Size(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + END + +;************************** End of handler.asm ***************************** diff --git a/TIBERIANDAWN/IPXREAL.ASM b/TIBERIANDAWN/IPXREAL.ASM new file mode 100644 index 000000000..7e53501c5 --- /dev/null +++ b/TIBERIANDAWN/IPXREAL.ASM @@ -0,0 +1,316 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +MODEL LARGE +P386N +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +global C IPXHandler:FAR + + +;********************************* Code ************************************ + CODESEG + + +;--------------------------------------------------------------------------- +; The markers let the application verify that it's mapping this memory +; correctly. +;--------------------------------------------------------------------------- +Marker1 DW 1111h ; placeholder to find data start + +;--------------------------------------------------------------------------- +; This is the IPX Event Control Block: +;--------------------------------------------------------------------------- +ECB_LinkAddress DD ? +ECB_EventServiceRoutine DD ? ; Event Handler ptr +ECB_InUse DB ? ; 0 = event is complete +ECB_CompletionCode DB ? ; 0 = OK, IPX error otherwise +ECB_SocketNumber DW ? ; socket to listen/send on +ECB_ConnectionID DW ? +ECB_RestOfWorkspace DW ? +ECB_DriverWorkSpace DB 12 DUP (?) +ECB_ImmediateAddress DB 6 DUP (?) ; bridge address +ECB_PacketCount DW ? ; # data areas (2) +ECB_HeaderAddress DD ? ; ptr to IPX header buffer +ECB_HeaderLength DW ? ; length of IPX header buffer +ECB_PacketAddress DD ? ; ptr to packet buffer +ECB_PacketLength DW ? ; length of packet buffer + +;--------------------------------------------------------------------------- +; The rest of the variables are for telling IPX which buffer to store the +; next incoming packet in. They must be initialized by the application. +;--------------------------------------------------------------------------- +NumBufs DW 0 ; # buffers provided by app +BufferFlags DD 0 ; array of in-use flags (1 = in use) +PacketSize DW 0 ; total size of 1 buf (incl IPX hdr) +FirstPacketBuf DD 0 ; ptr to 1st packet buffer +CurIndex DW 0 ; current packet/flag index +CurPacketBuf DD 0 ; ptr to current packet buf +FuncOffset DW StartLabel ; offset of our routine + +;--------------------------------------------------------------------------- +; These values are for preventing re-entrancy; they're currently not used. +;--------------------------------------------------------------------------- +Semaphore DB 0 ; prevents re-entrancy +ReEntrantCount DW 0 ; times we've been called re-entrantly + +;--------------------------------------------------------------------------- +; Local stack space +;--------------------------------------------------------------------------- +StackPtr DW 0 ; saved copy of stack ptr +StackSeg DW 0 ; saved copy of stack seg +StackPtr_int DW 0 ; our internal stack ptr +StackSeg_int DW 0 ; our internal stack seg +StackCheck DW 1234h ; check for stack overflow + DW 256 DUP (0) ; stack storage space +StackSpace DW 0 ; label for our stack space + +;--------------------------------------------------------------------------- +; These bytes mark the end of the real-mode data area +;--------------------------------------------------------------------------- +Marker2 DW 2222h ; placeholder to find data end + + +;*************************************************************************** +;* IPXHandler -- IPX callback routine * +;* * +;* This routine is assembled as a stand-alone executable, then loaded * +;* into low DOS memory by a protected-mode application. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 04/07/1995 BRR : Created. * +;*=========================================================================* + label StartLabel + PROC IPXHandler C FAR USES + + ;................................................................... + ; Turn off interrupts; make sure memory copies go forward + ;................................................................... + pushf + cli + cld + + ;................................................................... + ; Set up segment registers to point DS to CS + ;................................................................... + push ds + push ax + mov ax,cs + mov ds,ax + + ;................................................................... + ; Set up our local stack; save SS & SP first. + ;................................................................... + mov [StackSeg],ss + mov [StackPtr],sp + mov [StackPtr_int], OFFSET StackSpace + mov [StackSeg_int], SEG StackSpace + lss sp, [DWORD PTR StackPtr_int] + + + ;................................................................... + ; Save all registers + ;................................................................... + pushad + push es + + ;................................................................... + ; If we've been called re-entrantly, just exit + ;................................................................... + cmp [Semaphore],0 + jz ??Start_Handler + add [ReEntrantCount],1 + jmp ??Exit_Handler + +??Start_Handler: + ;................................................................... + ; Set our semaphore + ;................................................................... + mov [Semaphore],1 + + ;------------------------------------------------------------------- + ; Set 'CurIndex' to the index of the next-available receive buffer, + ; and 'CurPacketBuf to the next-available packet buffer + ;------------------------------------------------------------------- + ;................................................................... + ; Get 'CurIndex' & increment it. Wrap to 0 if we reach 'NumBufs' + ; Since I'm treating 'CurPacketBuf' as a long integer (and not as + ; a segment:offset), the entire data area can't be larger than 64K. + ;................................................................... + mov dx,[CurIndex] ; DX = CurIndex + mov eax,[CurPacketBuf] ; EAX = current packet buffer addr + inc dx ; DX = next index + add ax,[PacketSize] ; EAX = next buffer ptr + cmp dx,[NumBufs] ; see if DX is past # buffers + jb ??Get_Flag + mov dx,0 ; wrap to 1st index + mov eax,[FirstPacketBuf] ; wrap to 1st packet buffer + +??Get_Flag: + ;................................................................... + ; Get the next buffer-in-use flag; if it's 0, load [CurIndex] with + ; the value of SI (the next index). If it's 1, skip the updating of + ; the index, flag & buffer ptr. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ;................................................................... + les di,[BufferFlags] ; ES:DI = BufferFlags address + mov bx,di ; BX = DI + new CurIndex + add bx,dx + + cmp [BYTE PTR es:bx],0 ; compare next flag to 0 (avail) + jne ??Set_ECB ; if not avail, skip setting new values + + ;................................................................... + ; The next buffer is available; so, set this buffer's In-Use flag + ; to 1, and move on to the next buffer. Do not set this buffer's + ; flag to 1 until we move on to the next buffer, to prevent the + ; application from reading the currently-in-use packet buffer. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ; ES:DI = BufferFlags address + ;................................................................... + mov bx,di ; BX = DI + old CurIndex + add bx,[CurIndex] + mov [BYTE PTR es:bx],1 ; set old BufferFlags value to in-use + + mov [CurIndex],dx ; save new index + mov [CurPacketBuf],eax ; save new packet address + + ;------------------------------------------------------------------- + ; Set up the Event Control Block to tell IPX to start listening. + ; The following entries are filled in by the app, and should be left + ; alone: + ; - EventServiceRoutine + ; - SocketNumber + ; The rest should be re-initialized. Note that EBX is now pointing + ; to an unavailable buffer if the next buffer was already in use; + ; so it must be reloaded with the correct buffer address from + ; [CurPacketBuf]. + ;------------------------------------------------------------------- +??Set_ECB: + mov [ECB_LinkAddress],0 ; default + mov [ECB_InUse],0 ; default + mov [ECB_CompletionCode],0 ; default + mov [ECB_ConnectionID],0 ; default + mov [ECB_RestOfWorkspace],0 ; default + mov [ECB_DriverWorkSpace],0 ; default + mov [ECB_ImmediateAddress],0 ; default + mov [ECB_PacketCount],2 ; use 2 data areas + mov ebx,[CurPacketBuf] ; get current buffer address + mov [ECB_HeaderAddress],ebx ; set header address + mov [ECB_HeaderLength],30 ; size of IPX header + add ebx,30 ; point to past the header + mov [ECB_PacketAddress],ebx ; set packet data address + mov ax,[PacketSize] ; get size of one buffer + sub ax,30 ; remove size of IPX header + mov [ECB_PacketLength],ax ; set size of packet data + + ;------------------------------------------------------------------- + ; Clear the IPX header for this packet + ;------------------------------------------------------------------- + les di,[ECB_HeaderAddress] ; ES:DI = IPX Listen Header + mov cx,30 ; (30 bytes = size of header) + mov al,0 + rep stosb ; clear to 0's + + ;------------------------------------------------------------------- + ; Command IPX to start listening again. + ;------------------------------------------------------------------- + mov bx,4 ; IPX code for Listen + mov ax,ds ; ES = segment of ListenECB + mov es,ax + mov ax,OFFSET ECB_LinkAddress + mov si,ax ; ES:SI = address of ECB + int 07ah ; call IPX interrupt + + ;................................................................... + ; Clear our semaphore + ;................................................................... + mov [Semaphore],0 + +??Exit_Handler: + ;................................................................... + ; Pop values from our local stack + ;................................................................... + pop es + popad + + ;................................................................... + ; Check our stack-check value; if the stack has overflowed, generate + ; a debugger break. + ;................................................................... + cmp [StackCheck],1234h + je ??Restore_Stack + int 3 + + ;................................................................... + ; Restore the stack to its previous value + ;................................................................... +??Restore_Stack: + lss sp, [DWORD PTR StackPtr] + + ;................................................................... + ; Pop the rest of the registers + ;................................................................... + pop ax + pop ds + + popf + + ret + +ENDP IPXHandler + +END + +;************************** End of handler.asm ***************************** diff --git a/TIBERIANDAWN/JSHELL.CPP b/TIBERIANDAWN/JSHELL.CPP new file mode 100644 index 000000000..56c8dbec5 --- /dev/null +++ b/TIBERIANDAWN/JSHELL.CPP @@ -0,0 +1,434 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\jshell.cpv 2.18 16 Oct 1995 16:51:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 2, 1994 * + * * + * Last Update : May 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Build_Translucent_Table -- Creates a translucent control table. * + * Translucent_Table_Size -- Determines the size of a translucent table. * + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * Fatal -- General purpose fatal error handler. * + * Set_Window -- Sets the window dimensions to that specified. * + * Small_Icon -- Create a small icon from a big one. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wwfile.h" + +#include + +/*********************************************************************************************** + * Small_Icon -- Create a small icon from a big one. * + * * + * This routine will extract the specified icon from the icon data file and convert that * + * incon into a small (3x3) representation. Typicall use of this mini-icon is for the radar * + * map. * + * * + * INPUT: iconptr -- Pointer to the icon data file. * + * * + * iconnum -- The embedded icon number to convert into a small image. * + * * + * OUTPUT: Returns with a pointer to the small icon imagery. This is exactly 9 bytes long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + *=============================================================================================*/ +void * Small_Icon(void const * iconptr, int iconnum) +{ + static unsigned char _icon[9]; + IControl_Type const * iptr = (IControl_Type const *)iconptr; + unsigned char * data; + + if (iconptr) { + iconnum = iptr->Map[iconnum]; + data = &iptr->Icons[iconnum*(24*24)]; + + for (int index = 0; index < 9; index++) { + int _offsets[9] = { + 4+4*24, + 12+4*24, + 20+4*24, + 4+12*24, + 12+12*24, + 20+12*24, + 4+20*24, + 12+20*24, + 20+20*24 + }; + _icon[index] = data[_offsets[index]]; + } + } + + return(_icon); +} + + +/*********************************************************************************************** + * Set_Window -- Sets the window dimensions to that specified. * + * * + * Use this routine to set the windows dimensions to the coordinates and dimensions * + * specified. * + * * + * INPUT: x -- Window X pixel position. * + * * + * y -- Window Y pixel position. * + * * + * w -- Window width in pixels. * + * * + * h -- Window height in pixels. * + * * + * OUTPUT: none * + * * + * WARNINGS: The X and width values are truncated to an even 8 pixel boundary. This is * + * the same as stripping off the lower 3 bits. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void Set_Window(int window, int x, int y, int w, int h) +{ + WindowList[window][WINDOWWIDTH] = w >> 3; + WindowList[window][WINDOWHEIGHT] = h; + WindowList[window][WINDOWX] = x >> 3; + WindowList[window][WINDOWY] = y; +} + + +/*********************************************************************************************** + * Fatal -- General purpose fatal error handler. * + * * + * This is a very simple general purpose fatal error handler. It goes directly to text * + * mode, prints the error, and then aborts with a failure code. * + * * + * INPUT: message -- The text message to display. * + * * + * ... -- Any optional parameters that are used in formatting the message. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine never returns. The game exits immediately. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void Fatal(char const *message, ...) +{ + va_list va; + + va_start(va, message); + Prog_End(); + vfprintf(stderr, message, va); + Mono_Printf(message); + GlyphX_Debug_Print("Fatal"); + GlyphX_Debug_Print(message); + + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } else { + *((int*)0) = 0; + } +} + + +#ifdef NEVER +void File_Fatal(char const *message) +{ + Prog_End(); + perror(message); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } +} +#endif + + + +/*********************************************************************************************** + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * * + * This is the C++ counterpart to the Load_Uncompress function. It will load the file * + * specified into the graphic buffer indicated and uncompress it. * + * * + * INPUT: file -- The file to load and uncompress. * + * * + * uncomp_buff -- The graphic buffer that initial loading will use. * + * * + * dest_buff -- The buffer that will hold the uncompressed data. * + * * + * reserved_data -- This is an optional pointer to a buffer that will hold any * + * reserved data the compressed file may contain. This is * + * typically a palette. * + * * + * OUTPUT: Returns with the size of the uncompressed data in the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data) +{ + unsigned short size; + void *sptr = uncomp_buff.Get_Buffer(); + void *dptr = dest_buff.Get_Buffer(); + int opened = false; + CompHeaderType header; + + /* + ** The file must be opened in order to be read from. If the file + ** isn't opened, then open it. Record this fact so that it can be + ** restored to its closed state at the end. + */ + if (!file.Is_Open()) { + if (!file.Open()) { + return(0); + } + opened = true; + } + + /* + ** Read in the size of the file (supposedly). + */ + file.Read(&size, sizeof(size)); + + /* + ** Read in the header block. This block contains the compression type + ** and skip data (among other things). + */ + file.Read(&header, sizeof(header)); + size -= sizeof(header); + + /* + ** If there are skip bytes then they must be processed. Either read + ** them into the buffer provided or skip past them. No check is made + ** to ensure that the reserved data buffer is big enough (watch out!). + */ + if (header.Skip) { + size -= header.Skip; + if (reserved_data) { + file.Read(reserved_data, header.Skip); + } else { + file.Seek(header.Skip, SEEK_CUR); + } + header.Skip = 0; + } + + /* + ** Determine where is the proper place to load the data. If both buffers + ** specified are identical, then the data should be loaded at the end of + ** the buffer and decompressed at the beginning. + */ + if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) { + sptr = Add_Long_To_Pointer(sptr, uncomp_buff.Get_Size()-(size+sizeof(header))); + } + + /* + ** Read in the bulk of the data. + */ + Mem_Copy(&header, sptr, sizeof(header)); + file.Read(Add_Long_To_Pointer(sptr, sizeof(header)), size); + + /* + ** Decompress the data. + */ + size = (unsigned int) Uncompress_Data(sptr, dptr); + + /* + ** Close the file if necessary. + */ + if (opened) { + file.Close(); + } + return((long)size); +} + + +/*********************************************************************************************** + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * * + * This is the C++ replacement for the Load_Alloc_Data function. It will allocate the * + * memory big enough to hold the file and then read the file into it. * + * * + * INPUT: file -- The file to read. * + * * + * mem -- The memory system to use for allocation. * + * * + * OUTPUT: Returns with a pointer to the allocated and filled memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void * Load_Alloc_Data(FileClass &file) +{ + void *ptr = 0; + long size = file.Size(); + + ptr = new char [size]; + if (ptr) { + file.Read(ptr, size); + } + return(ptr); +} + + +/*********************************************************************************************** + * Translucent_Table_Size -- Determines the size of a translucent table. * + * * + * Use this routine to determine how big the translucent table needs * + * to be given the specified number of colors. This value is typically * + * used when allocating the buffer for the translucent table. * + * * + * INPUT: count -- The number of colors that are translucent. * + * * + * OUTPUT: Returns the size of the translucent table. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +long Translucent_Table_Size(int count) +{ + return(256L + (256L * count)); +} + + +/*********************************************************************************************** + * Build_Translucent_Table -- Creates a translucent control table. * + * * + * The table created by this routine is used by Draw_Shape (GHOST) to * + * achieve a translucent affect. The original color of the shape will * + * show through. This differs from the fading effect, since that * + * affect only alters the background color toward a single destination * + * color. * + * * + * INPUT: palette -- Pointer to the control palette. * + * * + * control -- Pointer to array of structures that control how * + * the translucent table will be built. * + * * + * count -- The number of entries in the control array. * + * * + * buffer -- Pointer to buffer to place the translucent table. * + * If NULL is passed in, then the buffer will be * + * allocated. * + * * + * OUTPUT: Returns with pointer to the translucent table. * + * * + * WARNINGS: This routine is exceedingly slow. Use sparingly. * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control && palette) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)Add_Long_To_Pointer((void*)buffer, 256); + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)Add_Long_To_Pointer((void*)table, 256); + } + } + } + return(buffer); +} + + +/*********************************************************************************************** + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * * + * This routine will build a translucent (fading) table to remap colors into the shadow * + * color region of the palette. Shadow colors are not affected by this translucent table. * + * This means that a shape can be overlapped any number of times and the imagery will * + * remain deterministic (and constant). * + * * + * INPUT: palette -- Pointer to the palette to base the translucent process on. * + * * + * control -- Pointer to special control structure that specifies the * + * target color, and percentage of fade. * + * * + * count -- The number of colors to be remapped (entries in the control array). * + * * + * buffer -- Pointer to the staging buffer that will hold the translucent table * + * data. If this parameter is NULL, then an appropriate sized table * + * will be allocated. * + * * + * OUTPUT: Returns with a pointer to the translucent table data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control && palette) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)Add_Long_To_Pointer((void*)buffer, 256); + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)Add_Long_To_Pointer((void*)table, 256); + } + } + } + return(buffer); +} \ No newline at end of file diff --git a/TIBERIANDAWN/JSHELL.H b/TIBERIANDAWN/JSHELL.H new file mode 100644 index 000000000..09f01267c --- /dev/null +++ b/TIBERIANDAWN/JSHELL.H @@ -0,0 +1,248 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\jshell.h_v 2.16 16 Oct 1995 16:45:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef JSHELL_H +#define JSHELL_H + +/* +** Interface class to the keyboard. This insulates the game from library vagaries. Most +** notable being the return values are declared as "int" in the library whereas C&C +** expects it to be of KeyNumType. +*/ +class Keyboard +{ + public: + static KeyNumType Get(void) {return (KeyNumType)Get_Key_Num();}; + static KeyNumType Check(void) {return (KeyNumType)Check_Key_Num();}; + static KeyASCIIType To_ASCII(KeyNumType key) {return (KeyASCIIType)KN_To_KA(key);}; + static void Clear(void) {Clear_KeyBuffer();}; + static void Stuff(KeyNumType key) {Stuff_Key_Num(key);}; + static int Down(KeyNumType key) {return Key_Down(key);}; + static int Mouse_X(void) {return Get_Mouse_X();}; + static int Mouse_Y(void) {return Get_Mouse_Y();}; +}; + + +#ifdef NEVER +inline void * operator delete(void * data) +{ + Free(data); +} + +inline void * operator delete[] (void * data) +{ + Free(data); +} +#endif + + +/* +** These templates allow enumeration types to have simple bitwise +** arithmatic performed. The operators must be instatiated for the +** enumerated types desired. +*/ +template inline T operator ++(T & a) +{ + a = (T)((int)a + (int)1); + return(a); +} +template inline T operator ++(T & a, int) +{ + T aa = a; + a = (T)((int)a + (int)1); + return(aa); +} +template inline T operator --(T & a) +{ + a = (T)((int)a - (int)1); + return(a); +} +template inline T operator --(T & a, int) +{ + T aa = a; + a = (T)((int)a - (int)1); + return(aa); +} +template inline T operator |(T t1, T t2) +{ + return((T)((int)t1 | (int)t2)); +} +template inline T operator &(T t1, T t2) +{ + return((T)((int)t1 & (int)t2)); +} +template inline T operator ~(T t1) +{ + return((T)(~(int)t1)); +} + + +/* +** The shape flags are likely to be "or"ed together and other such bitwise +** manipulations. These instatiated operator templates allow this. +*/ +//inline ShapeFlags_Type operator |(ShapeFlags_Type, ShapeFlags_Type); +//inline ShapeFlags_Type operator &(ShapeFlags_Type, ShapeFlags_Type); +//inline ShapeFlags_Type operator ~(ShapeFlags_Type); + + + +//#pragma aux Set_Bit parm [esi] [ecx] [eax] +void __cdecl Set_Bit(void * array, int bit, int value); + + +//#pragma aux Get_Bit parm [esi] [eax] +int __cdecl Get_Bit(void const * array, int bit); + +//#pragma aux First_True_Bit parm [esi] +int __cdecl First_True_Bit(void const * array); + +//#pragma aux First_False_Bit parm [esi] +int __cdecl First_False_Bit(void const * array); + +//#pragma aux Bound parm [eax] [ebx] [ecx] +int __cdecl Bound(int original, int min, int max); + + +#if (0) +void Set_Bit(void * array, int bit, int value); +#pragma aux Set_Bit parm [esi] [ecx] [eax] \ + modify [esi ebx] = \ + "mov ebx,ecx" \ + "shr ebx,5" \ + "and ecx,01Fh" \ + "btr [esi+ebx*4],ecx" \ + "or eax,eax" \ + "jz ok" \ + "bts [esi+ebx*4],ecx" \ + "ok:" + +int Get_Bit(void const * array, int bit); +#pragma aux Get_Bit parm [esi] [eax] \ + modify [esi ebx] \ + value [eax] = \ + "mov ebx,eax" \ + "shr ebx,5" \ + "and eax,01Fh" \ + "bt [esi+ebx*4],eax" \ + "setc al" + +int First_True_Bit(void const * array); +#pragma aux First_True_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +int First_False_Bit(void const * array); +#pragma aux First_False_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "not ebx" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +extern int Bound(int original, int min, int max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jl okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "jg okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jl okmax" \ + "mov eax,ecx" \ + "okmax:" + +#ifdef NEVER +extern unsigned Bound(unsigned original, unsigned min, unsigned max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jb okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "ja okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jb okmax" \ + "mov eax,ecx" \ + "okmax:" +#endif + + +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +#pragma aux Fixed_To_Cardinal parm [eax] [edx] \ + modify [edx] \ + value [eax] = \ + "mul edx" \ + "add eax,080h" \ + "test eax,0FF000000h" \ + "jz ok" \ + "mov eax,000FFFFFFh" \ + "ok:" \ + "shr eax,8" + + +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +#pragma aux Cardinal_To_Fixed parm [ebx] [eax] \ + modify [edx] \ + value [eax] = \ + "or ebx,ebx" \ + "jz fini" \ + "shl eax,8" \ + "xor edx,edx" \ + "div ebx" \ + "fini:" + +#endif //(0) + + +#endif diff --git a/TIBERIANDAWN/KEYFBUFF.ASM b/TIBERIANDAWN/KEYFBUFF.ASM new file mode 100644 index 000000000..8a2e19ce5 --- /dev/null +++ b/TIBERIANDAWN/KEYFBUFF.ASM @@ -0,0 +1,4015 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +;IDEAL +;P386 +;MODEL USE32 FLAT +;jumps + +.MODEL FLAT +;.386 + +;******************************** Includes ********************************* +;INCLUDE "gbuffer.inc" +;include "profile.inc" + +OPTIMAL_BYTE_COPY equ 14 + +GraphicViewPort STRUCT +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ENDS + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +USE MACRO func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +prologue macro +endm + + + +epilogue macro +endm + + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +.DATA + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + + + + + + extern C BigShapeBufferStart:dword + extern C UseBigShapeBuffer:dword + extern C TheaterShapeBufferStart:dword + extern C IsTheaterShape:dword + ;extern C Single_Line_Trans_Entry:near + ;extern C Next_Line:near + extern C MMX_Done:near + extern C MMXAvailable:dword + ;extern C EndNewShapeJumpTable:byte + ;extern C NewShapeJumpTable:dword + + +;********************************************************************************** +; +; Jump tables for new line header system +; +; Each line of shape data now has a header byte which describes the data on the line. +; + +; +; Header byte control bits +; +BLIT_TRANSPARENT =1 +BLIT_GHOST =2 +BLIT_FADING =4 +BLIT_PREDATOR =8 +BLIT_SKIP =16 +BLIT_ALL =BLIT_TRANSPARENT or BLIT_GHOST or BLIT_FADING or BLIT_PREDATOR or BLIT_SKIP + + +ShapeHeaderType STRUCT + + draw_flags dd ? + shape_data dd ? + shape_buffer dd ? + +ShapeHeaderType ENDS + + + +; +; Global definitions for routines that draw a single line of a shape +; + ;extern Short_Single_Line_Copy:near + ;extern Single_Line_Trans:near + ;extern Single_Line_Ghost:near + ;extern Single_Line_Ghost_Trans:near + ;extern Single_Line_Fading:near + ;extern Single_Line_Fading_Trans:near + ;extern Single_Line_Ghost_Fading:near + ;extern Single_Line_Ghost_Fading_Trans:near + ;extern Single_Line_Predator:near + ;extern Single_Line_Predator_Trans:near + ;extern Single_Line_Predator_Ghost:near + ;extern Single_Line_Predator_Ghost_Trans:near + ;extern Single_Line_Predator_Fading:near + ;extern Single_Line_Predator_Fading_Trans:near + ;extern Single_Line_Predator_Ghost_Fading:near + ;extern Single_Line_Predator_Ghost_Fading_Trans:near + ;extern Single_Line_Skip:near + + ;extern Single_Line_Single_Fade:near + ;extern Single_Line_Single_Fade_Trans:near + +;externdef AllFlagsJumpTable:dword +; +externdef NewShapeJumpTable:dword +externdef EndNewShapeJumpTable:byte +externdef CriticalFadeRedirections:dword +;externdef BufferFrameTable:dword + +;externdef CriticalFadeRedirections:dword +;externdef CriticalFadeRedirections:dword +;externdef CriticalFadeRedirections:dword +;externdef BF_Copy:far ptr +;externdef BF_Trans:dword +;externdef BF_Ghost:dword +;externdef BF_Ghost_Trans:dword +;externdef BF_Fading:dword +;externdef BF_Fading_Trans:dword +;externdef BF_Ghost_Fading:dword +;externdef BF_Ghost_Fading_Trans:dword +;externdef BF_Predator:dword +;externdef BF_Predator_Trans:dword +;externdef BF_Predator_Ghost:dword +;externdef BF_Predator_Ghost_Trans:dword +;externdef BF_Predator_Fading:dword +;externdef BF_Predator_Fading_Trans:dword +;externdef BF_Predator_Ghost_Fading:dword +;externdef BF_Predator_Ghost_Fading_Trans:dword + +externdef C Single_Line_Trans:near +externdef C Single_Line_Trans_Entry:near +externdef C Next_Line:near + + +.CODE + + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + + + +.DATA + +;NewShapeJumpTable label near ptr dword + +; +; Jumptable for shape line drawing with no flags set +; + +NewShapeJumpTable dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy +;CriticalFadeRedirections label dword +CriticalFadeRedirections dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + +; +; Jumptable for shape line drawing routines with fading, transparent and ghost flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + +; +; Jumptable for shape line drawing with predator flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent and predator flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + + +; +; Jumptable for shape line drawing routines with all flags set +; + +AllFlagsJumpTable label dword + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +EndNewShapeJumpTable db 0 + +.CODE + + + +;--------------------------------------------------------------------------- + + + + +;********************************************************************************************* +;* Set_Shape_Header -- create the line header bytes for a shape * +;* * +;* INPUT: Shape width * +;* Shape height * +;* ptr to raw shape data * +;* ptr to shape headers * +;* shape flags * +;* ptr to translucency table * +;* IsTranslucent * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/29/95 10:09AM ST : Created. * +;*===========================================================================================* + + Setup_Shape_Header proc C pixel_width:DWORD, pixel_height:DWORD, src:DWORD, headers:DWORD, flags:DWORD, Translucent:DWORD, IsTranslucent:DWORD + + ;ARG pixel_width :DWORD ; width of rectangle to blit + ;ARG pixel_height :DWORD ; height of rectangle to blit + ;ARG src :DWORD ; this is a member function + ;ARG headers :DWORD + ;ARG flags :DWORD + ;ARG Translucent :DWORD + ;ARG IsTranslucent :DWORD + LOCAL trans_count :DWORD + + pushad + + + mov esi,[src] ;ptr to raw shape data + mov edi,[headers] ;ptr to headers we are going to set up + mov eax,[flags] + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST + mov [edi].ShapeHeaderType.draw_flags,eax ;save old flags in header + add edi,size ShapeHeaderType + mov edx,[pixel_height] ;number of shape lines to scan + +outer_loop: mov ecx,[pixel_width] ;number of pixels in shape line + xor bl,bl ;flag the we dont know anything about this line yet + mov [trans_count],0 ;we havnt scanned any transparent pixels yet + +; +; Scan one shape line to see what kind of data it contains +; +inner_loop: xor eax,eax + mov al,[esi] + inc esi + +; +; Check for transparent pixel +; + test al,al + jnz not_transp + test [flags],SHAPE_TRANS + jz not_transp + or bl,BLIT_TRANSPARENT ;flag that pixel is transparent + inc [trans_count] ;keep count of the number of transparent pixels on the line + jmp end_lp + +; +; Check for predator effect on this line +; +not_transp: test [flags],SHAPE_PREDATOR + jz not_pred + or bl,BLIT_PREDATOR + +; +; Check for ghost effects +; +not_pred: test [flags],SHAPE_GHOST + jz not_ghost + push edi + mov edi,[IsTranslucent] + cmp byte ptr [edi+eax],-1 + pop edi + jz not_ghost + or bl,BLIT_GHOST + +; +; Check if fading is required +; +not_ghost: test [flags],SHAPE_FADING + jz end_lp + or bl,BLIT_FADING + +end_lp: dec ecx + jnz inner_loop + + +; +; Interpret the info we have collected and decide which routine will be +; used to draw this line +; + xor bh,bh + + test bl,BLIT_TRANSPARENT + jz no_transparencies + or bh,BLIT_TRANSPARENT + mov ecx,[pixel_width] + cmp ecx,[trans_count] + jnz not_all_trans + +; all pixels in the line were transparent so we dont need to draw it at all + mov bh,BLIT_SKIP + jmp got_line_type + +not_all_trans: +no_transparencies: + mov al,bl + and al,BLIT_PREDATOR + or bh,al + mov al,bl + and al,BLIT_GHOST + or bh,al + mov al,bl + and al,BLIT_FADING + or bh,al + +; +; Save the line header and do the next line +; +got_line_type:mov [edi],bh + inc edi + + dec edx + jnz outer_loop + + + popad + ret + +Setup_Shape_Header endp + + + + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; +next_line macro + + add edi , [ dest_adjust_width ] ;add in dest modulo + dec edx ;line counter + jz real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp dword ptr [eax] ;do the jump + + endm + + + + + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* +Buffer_Frame_To_Page proc C public USES eax ebx ecx edx esi edi x_pixel:DWORD, y_pixel:DWORD, pixel_width:DWORD, pixel_height:DWORD, src:DWORD, dest:DWORD, flags:DWORD + + ;USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ;ARG x_pixel :DWORD ; x pixel position in source + ;ARG y_pixel :DWORD ; y pixel position in source + ;ARG pixel_width :DWORD ; width of rectangle to blit + ;ARG pixel_height:DWORD ; height of rectangle to blit + ;ARG src :DWORD ; this is a member function + ;ARG dest :DWORD ; what are we blitting to + + ;ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + LOCAL header_pointer :DWORD + LOCAL use_old_draw :DWORD + LOCAL save_ecx :DWORD + LOCAL ShapeJumpTableAddress :DWORD + LOCAL shape_buffer_start :DWORD + + prologue + cmp [ src ] , 0 + jz real_out + + ; + ; Save the line attributes pointers and + ; Modify the src pointer to point to the actual image + ; + cmp [UseBigShapeBuffer],0 + jz do_args ;just use the old shape drawing system + + mov edi,[src] + mov [header_pointer],edi + + mov eax,[BigShapeBufferStart] + cmp [edi].ShapeHeaderType.shape_buffer,0 + jz is_ordinary_shape + mov eax,[TheaterShapeBufferStart] +is_ordinary_shape: + mov [shape_buffer_start],eax + + mov edi,[edi].ShapeHeaderType.shape_data + add edi,[shape_buffer_start] + mov [src],edi + mov [use_old_draw],0 + + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +check_trans: + test [ flags ] , SHAPE_TRANS + jz check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + + +check_fading: + ;______________________________________________________________________ + ; If this is the first time through for this shape then + ; set up the shape header + ;______________________________________________________________________ + pushad + + cmp [UseBigShapeBuffer],0 + jz new_shape + + mov edi,[header_pointer] + cmp [edi].ShapeHeaderType.draw_flags,-1 + jz setup_headers + mov eax,[flags] ;Redo the shape headers if this shape was + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST ;initially set up with different flags + cmp eax,[edi].ShapeHeaderType.draw_flags + jz no_header_setup +new_shape: + mov [use_old_draw],1 + jmp no_header_setup + +setup_headers: + push [IsTranslucent] + push [Translucent] + push [flags] + push [header_pointer] + push [src] + push [pixel_height] + push [pixel_width] + call Setup_Shape_Header + add esp,7*4 + mov [ShapeJumpTableAddress], offset AllFlagsJumpTable + jmp headers_set +no_header_setup: + + xor eax,eax + test [flags],SHAPE_PREDATOR + jz not_shape_predator + or al,BLIT_PREDATOR + +not_shape_predator: + test [flags],SHAPE_FADING + jz not_shape_fading + or al,BLIT_FADING + +not_shape_fading: + + test [flags],SHAPE_TRANS + jz not_shape_transparent + or al,BLIT_TRANSPARENT + +not_shape_transparent: + + test [flags],SHAPE_GHOST + jz not_shape_ghost + or al,BLIT_GHOST + +not_shape_ghost: + + + shl eax,7 + add eax, offset NewShapeJumpTable + mov [ShapeJumpTableAddress],eax + ;call Init_New_Shape_Jump_Table_Address + + +headers_set: + popad + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +set_fading: + mov [ FadingNum ] , eax + + mov ebx,[ShapeJumpTableAddress] + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx],offset Single_Line_Single_Fade + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx+4],offset Single_Line_Single_Fade_Trans + cmp eax,1 + jz single_fade + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx],offset Single_Line_Fading + mov dword ptr [CriticalFadeRedirections-NewShapeJumpTable+ebx+4],offset Single_Line_Fading_Trans + +single_fade: + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp pred_cont + +check_range: + and eax , PRED_MASK ; keep entries within bounds + +pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [esi].GraphicViewPort.GVPWidth ; add width + add eax , [esi].GraphicViewPort.GVPXAdd ; add x add + add eax , [esi].GraphicViewPort.GVPPitch ; extra pitch of DD surface ST - 9/29/95 1:08PM + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , dword ptr [ BufferFrameTable + ebx ] ; get table value + mov dword ptr [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi].GraphicViewPort.GVPWidth ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi].GraphicViewPort.GVPHeight ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + + or al , dl + jz do_blit + + mov [use_old_draw],1 + test cl , 1000b + jz dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +dest_left_ok: + test cl , 0010b + jz dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +dest_bottom_ok: + test dl , 0100b + jz dest_right_ok + + mov eax , [esi].GraphicViewPort.GVPWidth ; get width into register + mov [ x1_pixel ] , eax + +dest_right_ok: + test dl , 0001b + jz do_blit + + mov eax , [esi].GraphicViewPort.GVPHeight ; get width into register + mov [ y1_pixel ] , eax + +do_blit: + cld + mov eax , [esi].GraphicViewPort.GVPXAdd + add eax , [esi].GraphicViewPort.GVPPitch + add eax , [esi].GraphicViewPort.GVPWidth + mov edi , [esi].GraphicViewPort.GVPOffset + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + +; +; If the shape needs to be clipped then we cant handle it with the new header systen +; so draw it with the old shape drawer +; + cmp [use_old_draw],0 + jnz use_old_stuff + + add [header_pointer],size ShapeHeaderType + mov edx,[pixel_height] + mov ecx,[pixel_width] + mov eax,[header_pointer] + mov al,[eax] + mov [save_ecx],ecx + inc [header_pointer] + and eax,BLIT_ALL + shl eax,2 + add eax,[ShapeJumpTableAddress] + jmp dword ptr [eax] + + +use_old_stuff: + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle real_out + + sub eax , [ x_pixel ] + jle real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +real_out: + + cmp [MMXAvailable],0 + jz no_mmx_cleanup + call MMX_Done + +no_mmx_cleanup: + epilogue + + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** +;extern BF_Copy:near + +BF_Copy: + prologue + + cmp eax , 10 + jl forward_loop_bytes + +forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz forward_loop_dword + + ret + +forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + + epilogue + + ret + + +;******************************************************************** +;******************************************************************** + +; segment code page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +; +; Expand the 'next_line' macro so we can jump to it +; +; +; ST - 12/20/2018 3:48PM +Next_Line:: next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Trans:: + prologue + +Single_Line_Trans_Entry:: + +slt_mask_map_lp: ; Pentium pipeline usage + ;Pipe Cycles + mov al,[esi] ;U 1 + inc esi ;Vee 1 + + test al,al ;U 1 + jz slt_skip ;Vee 1/5 + +slt_not_trans:mov [edi],al ;u 1 + + inc edi ;vee 1 + dec ecx ;u 1 + + jnz slt_mask_map_lp ;vee (maybe) 1 + +slt_end_line: epilogue + next_line + + ;align 32 + +slt_skip: inc edi + dec ecx + jz slt_skip_end_line + + mov al,[esi] + inc esi + test al,al + jz slt_skip2 + mov [edi],al + inc edi + dec ecx + jnz slt_mask_map_lp + + epilogue + next_line + + ;align 32 + +slt_skip2: inc edi + dec ecx + jz slt_end_line + +; +; If we have hit two transparent pixels in a row then we go into +; the transparent optimised bit +; +slt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz slt_not_trans;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz slt_end_line ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp slt_round_again + + + +slt_skip_end_line: + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; +; We have to align the destination for cards that dont bankswitch correctly +; when you write non-aligned data. +; + ;align 32 +Long_Single_Line_Copy: + prologue + + rept 3 + test edi,3 + jz LSLC_aligned + movsb + dec ecx + endm + +LSLC_aligned: + mov ebx,ecx + + shr ecx,2 + rep movsd + and ebx,3 + jz proc_out + movsb + dec bl + jz proc_out + movsb + dec bl + jz proc_out + movsb +proc_out: epilogue + next_line + + + +;***************************************************************************** +; Draw a single short line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Short_Single_Line_Copy: + prologue + cmp ecx,16 + jge Long_Single_Line_Copy + mov ebx,ecx + rep movsb + mov ecx,ebx + epilogue + next_line + + +;***************************************************************************** +; Skip a line of source that is all transparent +; +; 11/29/95 10:21AM - ST +; + + ;align 32 +Single_Line_Skip: + prologue + add esi,ecx + add edi,ecx + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Single_Line_Ghost: + + prologue + xor eax,eax +slg_loop: mov al,[esi] + mov ebx,[IsTranslucent] + inc esi + mov bh,[eax+ebx] + cmp bh,-1 + jz slg_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +slg_store_pixel: + mov [edi],al + + inc edi + dec ecx + jnz slg_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 +Single_Line_Ghost_Trans: + prologue + xor eax,eax +; cmp ecx,3 +; ja slgt4 + +slgt_loop: mov al,[esi] + inc esi + test al,al + jz slgt_transparent + +slgt_not_transparent: + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt_store_pixel: + mov [edi],al + inc edi + dec ecx + jnz slgt_loop + epilogue + next_line + + + ;align 32 + +slgt_transparent: + inc edi ;1 + dec ecx ;2 + jz slgt_out ;1 (not pairable) + +slgt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz slgt_not_transparent ;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz slgt_out ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp slgt_round_again + +slgt_out: epilogue + next_line + + + +; +; Optimised video memory access version +; + ;align 32 + +slgt4: push edx + mov edx,[edi] + + rept 4 + local slgt4_store1 + local slgt4_trans1 + mov al,[esi] + inc esi + test al,al + jz slgt4_trans1 + + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt4_store1 + + and ebx,0ff00h + mov al,dl + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt4_store1: mov dl,al + +slgt4_trans1: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + lea ecx,[ecx+0fffffffch] + cmp ecx,3 + ja slgt4 + test ecx,ecx + jnz slgt_loop + + epilogue + next_line + + + + + + + + + + +;***************************************************************************** +; Draw a single line with fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Fading: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +slf_loop: mov al,[esi] + inc esi + + mov ebp,[esp] + +slf_fade_loop:mov al,[ebx+eax] + dec ebp + jnz slf_fade_loop + + mov [edi],al + inc edi + + dec ecx + jnz slf_loop + add esp,4 + pop ebp + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Fading_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +slft_loop: mov al,[esi] + inc esi + test al,al + jz slft_transparent + + mov ebp,[esp] + +slft_fade_loop: + mov al,[ebx+eax] + dec ebp + jnz slft_fade_loop + + mov [edi],al +slft_transparent: + inc edi + + dec ecx + jnz slft_loop + add esp,4 + pop ebp + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Single_Fade: + prologue + xor eax,eax + mov ebx,[FadingTable] + +slsf_loop: mov al,[esi] + mov al,[ebx+eax] + mov [edi],al + inc esi + inc edi + + dec ecx + jnz slsf_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Single_Fade_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + +slsft_loop: mov al,[esi] + inc esi + test al,al + jz slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz slsft_loop + epilogue + next_line + + ;align 32 + +slsft_transparent: + inc edi + + dec ecx + jz slsft_next_line + mov al,[esi] + inc esi + test al,al + jz slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz slsft_loop + +slsft_next_line: + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with ghosting and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Ghost_Fading: + + prologue + mov [StashECX],ecx + +SLGF_loop: xor eax,eax + mov al,[esi] + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgf_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +slgf_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slgf_fade_loop + + mov [edi],al + inc esi + inc edi + + dec [StashECX] + jnz SLGF_loop + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Ghost_Fading_Trans: + prologue + mov [StashECX],ecx + xor eax,eax + +; cmp ecx,3 +; ja slgft4 + +SLGFT_loop: mov al,[esi] + inc esi + test al,al + jz slgft_trans_pixel + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slgft_fade_loop + + mov [edi],al +slgft_trans_pixel: + inc edi + + dec [StashECX] + jnz SLGFT_loop + epilogue + next_line + + + ;align 32 + +slgft4: push edx + mov edx,[edi] + + rept 4 + local slgft4_fade + local slgft4_fade_lp + local slgft4_trans + mov al,[esi] + inc esi + test al,al + jz slgft4_trans + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft4_fade + + and ebx,0ff00h + + mov al,dl + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft4_fade: mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft4_fade_lp: mov al,[eax+ebx] + dec ecx + jnz slgft4_fade_lp + + mov dl,al + +slgft4_trans: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + sub [StashECX],4 + jz slgft4_out + cmp [StashECX],3 + ja slgft4 + jmp SLGFT_loop + +slgft4_out: epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator effect +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator: + + prologue + +slp_loop: mov al,[esi] + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz slp_get_pred + + mov [BFPartialCount] , ebx + jmp slp_skip_pixel + +slp_get_pred: xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add byte ptr [BFPredOffset],2 + mov eax, dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +slp_skip_pixel: + inc esi + inc edi + + dec ecx + jnz slp_loop + + epilogue + next_line + + + + +;***************************************************************************** +; Draw a single line with transparent pixels and predator effect +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Trans: + + prologue + +slpt_loop: mov al,[esi] + inc esi + test al,al + jz slpt_skip_pixel + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz slpt_get_pred + + mov [BFPartialCount] , ebx + jmp slpt_skip_pixel + +slpt_get_pred:xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset ] , PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +slpt_skip_pixel: + inc edi + + dec ecx + jnz slpt_loop + + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost: + + prologue + +slpg_loop: mov al,[esi] + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpg_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpg_check_ghost + +slpg_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax ] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +slpg_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpg_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpg_store_pixel: + mov [edi],al + inc esi + inc edi + + dec ecx + jnz slpg_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator and ghosting +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Trans: + prologue + +slpgt_loop: mov al,[esi] + inc esi + test al,al + jz slpgt_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpgt_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgt_check_ghost + +slpgt_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax ] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +slpgt_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgt_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgt_store_pixel: + mov [edi],al +slpgt_transparent: + inc edi + + dec ecx + jnz slpgt_loop + + pop ecx + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Fading: + + prologue + mov [StashECX],ecx + +slpf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh,bh + jnz slpf_get_pred + + mov [BFPartialCount],ebx + jmp slpf_do_fading + +slpf_get_pred:xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +slpf_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slpf_fade_loop + + mov [edi],al + inc edi + + dec [StashECX] + jnz slpf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, fading and predator +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Fading_Trans: + prologue + mov [StashECX],ecx + +slpft_loop: mov al,[esi] + inc esi + test al,al + jz slpft_transparent + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz slpft_get_pred + + mov [BFPartialCount],ebx + jmp slpft_do_fading + +slpft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +slpft_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz slpft_fade_loop + + mov [edi],al +slpft_transparent: + inc edi + + dec [StashECX] + jnz slpft_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Fading: + + prologue + mov [StashECX],ecx + +slpgf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh , bh + jnz slpgf_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgf_check_ghost + +slpgf_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +slpgf_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgf_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgf_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpgf_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz slpgf_fade_loop + +slpgf_store_pixel: + mov [edi],al + inc edi + + dec [StashECX] + jnz slpgf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + ;align 32 + +Single_Line_Predator_Ghost_Fading_Trans: + + prologue + mov [StashECX],ecx + +slpgft_loop: mov al,[esi] + inc esi + test al,al + jz slpgft_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh , bh + jnz slpgft_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp slpgft_check_ghost + +slpgft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add byte ptr [BFPredOffset],2 + mov eax,dword ptr [BFPredTable+eax] + and byte ptr [BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +slpgft_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je slpgft_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +slpgft_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slpgft_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz slpgft_fade_loop + +slpgft_store_pixel: + mov [edi],al +slpgft_transparent: + inc edi + + dec [StashECX] + jnz slpgft_loop + + epilogue + next_line + + + + +; ends ;end of strict alignment segment + +; codeseg + + + +;extern BF_Trans:near + +BF_Trans: + + prologue +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +trans_reference: + dec ecx + jge trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz trans_loop + epilogue + + ret + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost:near +BF_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx, [offset ghost_reference] + sub ecx, [offset ghost_line] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +ghost_reference: + dec ecx + jge ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Trans:near +BF_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_t_reference ] + sub ecx, [ offset ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +ghost_t_reference: + dec ecx + jge ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Fading:near +BF_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset fading_reference ] + sub ecx, [ offset fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz fading_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Fading_Trans:near +BF_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset fading_t_reference ] + sub ecx, [ offset fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz fading_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Fading:near +BF_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_f_reference ] + sub ecx, [ offset ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , byte ptr [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , byte ptr [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Ghost_Fading_Trans:near +BF_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset ghost_f_t_reference ] + sub ecx, [ offset ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , byte ptr [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , byte ptr [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ghost_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator:near +BF_Predator: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_reference ] + sub ecx, [offset predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +predator_reference: + dec ecx + jge predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Trans:near +BF_Predator_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_t_reference ] + sub ecx, [ offset predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_t_reference: + dec ecx + jge predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost:near +BF_Predator_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_reference ] + sub ecx, [ offset predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +predator_g_reference: + dec ecx + jge predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Trans:near +BF_Predator_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_t_reference ] + sub ecx, [ offset predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_g_t_reference: + dec ecx + jge predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Fading:near +BF_Predator_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_f_reference ] + sub ecx, [ offset predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Fading_Trans:near +BF_Predator_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_f_t_reference ] + sub ecx, [ offset predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Fading:near +BF_Predator_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_f_reference ] + sub ecx, [ offset predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +;extern BF_Predator_Ghost_Fading_Trans:near +BF_Predator_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ offset predator_g_f_t_reference ] + sub ecx, [ offset predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add BYTE PTR [ BFPredOffset ] , 2 + movzx eax , WORD PTR [ BFPredTable + eax ] + and BYTE PTR [ BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , BYTE PTR [ edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , BYTE PTR [ ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , BYTE PTR [ ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, byte ptr [ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz predator_g_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + +Buffer_Frame_To_Page ENDP + + +end + diff --git a/TIBERIANDAWN/KEYFBUFF.INC b/TIBERIANDAWN/KEYFBUFF.INC new file mode 100644 index 000000000..c05ed9181 --- /dev/null +++ b/TIBERIANDAWN/KEYFBUFF.INC @@ -0,0 +1,39 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; +next_line macro + + add edi,[dest_adjust_width] ;add in dest modulo + dec edx ;line counter + jz real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp [dword eax] ;do the jump + + endm + diff --git a/TIBERIANDAWN/KEYFRAME.CPP b/TIBERIANDAWN/KEYFRAME.CPP new file mode 100644 index 000000000..8c81d41bb --- /dev/null +++ b/TIBERIANDAWN/KEYFRAME.CPP @@ -0,0 +1,599 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\keyframe.cpv 2.14 16 Oct 1995 16:48:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + +#define INITIAL_BIG_SHAPE_BUFFER_SIZE 12000*1024 +#define THEATER_BIG_SHAPE_BUFFER_SIZE 1000*1024 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char *BigShapeBufferStart = NULL; + char *TheaterShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = FALSE; + bool IsTheaterShape = false; +} +char *BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; +bool OriginalUseBigShapeBuffer = false; + +char *TheaterShapeBufferPtr = NULL; +int TotalTheaterShapes = 0; + + + +#define MAX_SLOTS 1500 +#define THEATER_SLOT_START 1000 + +char **KeyFrameSlots [MAX_SLOTS]; +int TotalSlotsUsed=0; +int TheaterSlotsUsed = THEATER_SLOT_START; + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char *shape_data; + int shape_buffer; //1 if shape is in theater buffer +} ShapeHeaderType; + +static int Length; + +void *Get_Shape_Header_Data(void *ptr) +{ + if (UseBigShapeBuffer){ + + ShapeHeaderType *header = (ShapeHeaderType*) ptr; + return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) ); + + }else{ + return (ptr); + } +} + +int Get_Last_Frame_Length(void) +{ + return(Length); +} + + + +void Reset_Theater_Shapes (void) +{ + /* + ** Delete any previously allocated slots + */ + for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE; + OriginalUseBigShapeBuffer = UseBigShapeBuffer; + + // UseBigShapeBuffer = false; +} + + + + +/*********************************************************************************************** + * Disable_Uncompressed_Shapes -- Temporarily turns off shape decompression * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/19/96 2:37PM ST : Created * + *=============================================================================================*/ +void Disable_Uncompressed_Shapes (void) +{ + UseBigShapeBuffer = false; +} + + + +/*********************************************************************************************** + * Enable_Uncompressed_Shapes -- Restores state of shape decompression before it was disabled * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/19/96 2:37PM ST : Created * + *=============================================================================================*/ +void Enable_Uncompressed_Shapes (void) +{ + UseBigShapeBuffer = OriginalUseBigShapeBuffer; +} + + +#define FIXIT_SCORE_CRASH + +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr) +{ +#ifdef FIXIT_SCORE_CRASH + char * ptr; + unsigned long offcurr, offdiff; +#else + char * ptr, * lockptr; + unsigned long offcurr, off16, offdiff; +#endif + unsigned long offset[SUBFRAMEOFFS]; + KeyFrameHeaderType *keyfr; + unsigned short buffsize, currframe, subframe; + unsigned long length = 0; + char frameflags; + unsigned long return_value; + char *temp_shape_ptr; + + // + // valid pointer?? + // + Length = 0; + if ( !dataptr || !buffptr ) { + return(0); + } + + // + // look at header then check that frame to build is not greater + // than total frames + // + keyfr = (KeyFrameHeaderType *) dataptr; + + if ( framenumber >= keyfr->frames ) { + return(0); + } + + + if (UseBigShapeBuffer){ + /* + ** If we havnt yet allocated memory for uncompressed shapes then do so now. + ** + */ + if (!BigShapeBufferStart){ + BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL); + BigShapeBufferPtr = BigShapeBufferStart; + /* + ** Allocate memory for theater specific uncompressed shapes + */ + TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL); + TheaterShapeBufferPtr = TheaterShapeBufferStart; + } + + + /* + ** Track memory usage in uncompressed shape buffers. + */ + static bool show_info = true; + + if ((Frame & 0xff) == 0){ + + if (show_info){ + + char debugstr [128]; + sprintf (debugstr, "C&C95 - Big shape buffer is now %d Kb.\n", BigShapeBufferLength / 1024); + CCDebugString (debugstr); + + sprintf (debugstr, "C&C95 - %d Kb Used in big shape buffer.\n", (unsigned)((unsigned)BigShapeBufferPtr - (unsigned)BigShapeBufferStart)/1024); + CCDebugString (debugstr); + + sprintf (debugstr, "C&C95 - %d Kb Used in theater shape buffer.\n", (unsigned)((unsigned)TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart)/1024); + CCDebugString (debugstr); + show_info = false; + } + + }else{ + show_info = true; + + } + + + /* + ** If we are running out of memory (<128k left) for uncompressed shapes + ** then allocate some more. + */ + if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128*1024){ + ReallocShapeBufferFlag = TRUE; + } + + /* + ** If this animation was not previously uncompressed then + ** allocate memory to keep the pointers to the uncompressed data + ** for these animation frames + */ + if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER){ + keyfr->x = UNCOMPRESS_MAGIC_NUMBER; + if (IsTheaterShape){ + keyfr->y = TheaterSlotsUsed; + TheaterSlotsUsed++; + }else{ + keyfr->y = TotalSlotsUsed; + TotalSlotsUsed++; + } + /* + ** Allocate and clear the memory for the shape info + */ + KeyFrameSlots[keyfr->y]= new char *[keyfr->frames]; + memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4); + } + + /* + ** If this frame was previously uncompressed then just return + ** a pointer to the raw data + */ + if (*(KeyFrameSlots[keyfr->y]+framenumber)){ + if (IsTheaterShape){ + return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + }else{ + return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } + } + } + + // calc buff size + buffsize = keyfr->width * keyfr->height; + + // get offset into data + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], 12L ); + frameflags = (char)(offset[0] >> 24); + + + if ( (frameflags & KF_KEYFRAME) ) { + + ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { // key delta or delta + + if ( (frameflags & KF_DELTA) ) { + currframe = (unsigned short)offset[1]; + + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + } + + // key frame + offcurr = offset[1] & 0x00FFFFFFL; + + // key delta + offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; + + ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + +#ifndef FIXIT_SCORE_CRASH + off16 = (unsigned long)lockptr & 0x00003FFFL; +#endif + length = LCW_Uncompress( ptr, buffptr, buffsize ); + + if (length > buffsize) { + return(0); + } + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + if ( (frameflags & KF_DELTA) ) { + // adjust to delta after the keydelta + + currframe++; + subframe = 2; + + while (currframe <= framenumber) { + offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + currframe++; + subframe += 2; + + if ( subframe >= (SUBFRAMEOFFS - 1) && + currframe <= framenumber ) { + Mem_Copy( Add_Long_To_Pointer( dataptr, + (((unsigned long)currframe << 3) + + sizeof(KeyFrameHeaderType)) ), + &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + subframe = 0; + } + } + } + } + + if (UseBigShapeBuffer){ + /* + ** Save the uncompressed shape data so we dont have to uncompress it + ** again next time its drawn. + ** We keep a space free before the raw shape data so we can add line + ** header info before the shape is drawn for the first time + */ + + if (IsTheaterShape){ + /* + ** Shape is a theater specific shape + */ + return_value = (unsigned long) TheaterShapeBufferPtr; + temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr){ + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart; + TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + /* + ** Align the next shape + */ + if (3 & (unsigned)TheaterShapeBufferPtr){ + TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + + }else{ + + + return_value=(unsigned long)BigShapeBufferPtr; + temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr){ + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart; + BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + // Align the next shape + if (3 & (unsigned)BigShapeBufferPtr){ + BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + } + + }else{ + return ((unsigned long)buffptr); + } +} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} \ No newline at end of file diff --git a/TIBERIANDAWN/LAYER.CPP b/TIBERIANDAWN/LAYER.CPP new file mode 100644 index 000000000..30427b6e9 --- /dev/null +++ b/TIBERIANDAWN/LAYER.CPP @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\layer.cpv 2.18 16 Oct 1995 16:50:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : March 10, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LayerClass::Submit -- Adds an object to a layer list. * + * LayerClass::Sort -- Perform an incremental sort pass on the layer's objects. * + * LayerClass::Sorted_Add -- Adds object in sorted order to layer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "layer.h" + + +/*********************************************************************************************** + * LayerClass::Submit -- Adds an object to a layer list. * + * * + * This routine is used to add an object to the layer list. If the list is full, then the * + * object is not added. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Allows sorted insert. * + * 01/02/1995 JLB : Fixed to work with EMSListOf template. * + *=============================================================================================*/ +bool LayerClass::Submit(ObjectClass const * object, bool sort) +{ + /* + ** Add the object to the layer. Either at the end (if "sort" is false) or at the + ** appropriately sorted position. + */ + if (sort) { + return(Sorted_Add(object)); + } + return(Add((ObjectClass *)object)); +} + + +/*********************************************************************************************** + * LayerClass::Sort -- Handles sorting the objects in the layer. * + * * + * This routine is used if the layer objects must be sorted and sorting is to occur now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Don't call this routine too often since it does take a bit of time to * + * execute. It is a single pass binary sort and thus isn't horribly slow, * + * but it does take some time. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 03/10/1995 JLB : Uses comparison operator. * + *=============================================================================================*/ +void LayerClass::Sort(void) +{ + for (int index = 0; index < Count()-1; index++) { + if (*(*this)[index+1] < *(*this)[index]) { + ObjectClass * temp; + + temp = (*this)[index+1]; + (*this)[index+1] = (*this)[index]; + (*this)[index] = temp; + } + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Sorted_Add -- Adds object in sorted order to vector. * + * * + * Use this routine to add an object to the vector but it will be inserted in sorted * + * order. This depends on the ">" operator being defined for the vector object. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added to the vector successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +int LayerClass::Sorted_Add(ObjectClass const * const object) +{ + if (ActiveCount >= (int)Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the right sorted position. + */ + int index; + for (index = 0; index < ActiveCount; index++) { + if ((*(*this)[index]) > (*object)) { + break; + } + } + + /* + ** Make room if the insertion spot is not at the end of the vector. + */ + for (int i = ActiveCount-1; i >= index; i--) { + (*this)[i+1] = (*this)[i]; + } + (*this)[index] = (ObjectClass *)object; + ActiveCount++; + return(true); +} + + diff --git a/TIBERIANDAWN/LAYER.H b/TIBERIANDAWN/LAYER.H new file mode 100644 index 000000000..e25aed7f3 --- /dev/null +++ b/TIBERIANDAWN/LAYER.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\layer.h_v 2.19 16 Oct 1995 16:46:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : May 31, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LAYER_H +#define LAYER_H + +#include "vector.h" + +class ObjectClass; + +class LayerClass : public DynamicVectorClass +{ + public: + + //----------------------------------------------------------------- + void Sort(void); + bool Submit(ObjectClass const * object, bool sort=false); + int Sorted_Add(ObjectClass const * const object); + + + virtual void Init(void) {Clear();}; + virtual void One_Time(void) {}; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/TIBERIANDAWN/LED.H b/TIBERIANDAWN/LED.H new file mode 100644 index 000000000..3e379c155 --- /dev/null +++ b/TIBERIANDAWN/LED.H @@ -0,0 +1,43 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\led.h_v 2.17 16 Oct 1995 16:45:50 JOE_BOSTIC $ */ + +#ifndef LED_H +#define LED_H + +class LEDClass +{ + public: + typedef enum ControlType { + LED_NOCHANGE, // Do nothing (just query). + LED_OFF, // Turn LED off. + LED_ON, // Turn LED on. + LED_TOGGLE // Toggle LED state. + } ControlType; + + protected: + static int Shift_Control(ControlType control, char bit); + + public: + static int Scroll_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x01);}; + static int Caps_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x02);}; + static int Num_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x04);}; + + private: + static void Send_To_Keyboard(unsigned char val); +}; + +#endif diff --git a/TIBERIANDAWN/LINK.CPP b/TIBERIANDAWN/LINK.CPP new file mode 100644 index 000000000..f8c24eefe --- /dev/null +++ b/TIBERIANDAWN/LINK.CPP @@ -0,0 +1,413 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\link.cpv 2.18 16 Oct 1995 16:52:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LinkClass::LinkClass -- Default constructor for linked list object. * + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * LinkClass::Zap -- Forces the link pointers to NULL. * + * LinkClass::operator= -- Assignment operator for linked list class object. * + * LinkClass::Get_Next -- Fetches the next object in list. * + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * LinkClass::Head_Of_List -- Finds the head of the list. * + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * LinkClass::Add -- This object adds itself to the given list * + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * LinkClass::Remove -- Removes the specified object from the list. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "link.h" + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Default constructor for linked list object. * + * * + * This is the default constructor for a linked list object. It merely initializes the * + * next and previous pointers to NULL. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(void) +{ + LinkClass::Zap(); +} + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * * + * This copy constructor, unlike the assigment operator, doesn't have to deal with an * + * already initialized and legal link object to the left of the "=". It merely puts the * + * destination object into the same list as the source object. * + * * + * INPUT: link -- Reference to the object on the right of the "=". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(LinkClass & link) +{ + LinkClass::Zap(); + Add(link); +} + + +/*********************************************************************************************** + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * * + * This default destructor will remove the object from any linked list it may be part of. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::~LinkClass(void) +{ + Remove(); +} + + +/*********************************************************************************************** + * LinkClass::Zap -- Forces the link pointers to NULL. * + * * + * This routine will "zap" out the link pointers. This is usually necessary when the link * + * pointers start in an undefined state, but we KNOW that they aren't pointing to anything * + * valid. In such a case it becomes necessary to zap them so that when the object is added * + * to a list, it will be added corrrectly. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void LinkClass::Zap(void) +{ + Next = 0; + Prev = 0; +} + + +/*********************************************************************************************** + * LinkClass::operator= -- Assignment operator for linked list class object. * + * * + * The assignment operator makes sure that the previous and next pointers remain valid. * + * Because this class only consists of pointers, the assignment operator doesn't actually * + * transfer any data from the source object. It merely makes the destination object part * + * of the same list as the source object. In essence, this is transferring information * + * but not the actual values. * + * * + * If the destination object is already part of another list, it is removed from that list * + * before being added to the source object's list. This ensures that either list remains * + * in a valid condition. * + * * + * INPUT: link -- The object to the right of the "=" operator. * + * * + * OUTPUT: Returns a reference to the rightmost object -- per standard assigment rules. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::operator=(LinkClass & link) +{ + Remove(); + Add(link); + return(link); +} + + +/*********************************************************************************************** + * LinkClass::Get_Next -- Fetches the next object in list. * + * * + * This routine will return with a pointer to the next object in the list. If there are * + * no more objects, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to next object in list or NULL if at end of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Next(void) const +{ + return(Next); +} + + +/*********************************************************************************************** + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * * + * Use this routine to get a pointer to the previous object in the linked list. If there * + * are no previous objects (such as at the head of the list), then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous object in the list or NULL if none. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Prev(void) const +{ + return(Prev); +} + + +/*********************************************************************************************** + * LinkClass::Head_Of_List -- Finds the head of the list. * + * * + * Use this routine to scan for and return a reference to the object at the head of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass const & LinkClass::Head_Of_List(void) const +{ + LinkClass const * link = this; + while (link->Prev) { + link = link->Prev; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * * + * Use this routine to scan for and return a reference to the object at the end of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the end of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass const & LinkClass::Tail_Of_List(void) const +{ + LinkClass const * link = this; + while (link->Next) { + link = link->Next; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Add -- This object adds itself to the given list * + * * + * Use this routine to add a link object to the list, but to be added right after the * + * given link. This allows inserting a link in the middle of the chain. A quite necessary * + * ability if the chain is order dependant (e.g., the gadget system). * + * * + * INPUT: list -- gadget object to add this one to * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Save ptr to next gadget. + */ + ptr = list.Next; + + /* + ** Link myself in after 'list'. + */ + list.Next = this; + Prev = &list; + + /* + ** Link myself to next gadget, if there is one. + */ + Next = ptr; + if (ptr) { + ptr->Prev = this; + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * * + * INPUT: list -- the list to make myself the head of * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Head(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Head_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Prev = this; + Next = ptr; + Prev = NULL; + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: the head of the list * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Tail(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Tail_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Next = this; + Prev = ptr; + Next = NULL; + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Remove(void) +{ + LinkClass * head = &Head_Of_List(); + LinkClass * tail = &Tail_Of_List(); + + if (Prev) { + Prev->Next = Next; + } + if (Next) { + Next->Prev = Prev; + } + Prev = 0; + Next = 0; + + if (head==this) { + if (tail==this) { + return(0); + } + return(&tail->Head_Of_List()); + } + return(head); +} + + diff --git a/TIBERIANDAWN/LINK.H b/TIBERIANDAWN/LINK.H new file mode 100644 index 000000000..b781eacf5 --- /dev/null +++ b/TIBERIANDAWN/LINK.H @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\link.h_v 2.17 16 Oct 1995 16:45:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LINK_H +#define LINK_H + +/* +** This implements a simple linked list. It is possible to add, remove, and traverse the +** list. Since this is a doubly linked list, it is possible to remove an entry from the +** middle of an existing list. +*/ +class LinkClass +{ + public: + LinkClass(void); + virtual ~LinkClass(void); + + virtual LinkClass * Get_Next(void) const; + virtual LinkClass * Get_Prev(void) const; + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual LinkClass const & Head_Of_List(void) const; + virtual LinkClass & Head_Of_List(void) + { + return (LinkClass &)(((LinkClass const *)this)->Head_Of_List()); + }; + virtual LinkClass const & Tail_Of_List(void) const; + virtual LinkClass & Tail_Of_List(void) + { + return (LinkClass &)(((LinkClass const *)this)->Tail_Of_List()); + }; + virtual void Zap(void); + virtual LinkClass * Remove(void); + + LinkClass & operator=(LinkClass & link); // Assignment operator. + LinkClass(LinkClass & link); // Copy constructor. + + private: + /* + ** Pointers to previous and next link objects in chain. + */ + LinkClass * Next; + LinkClass * Prev; +}; + +#endif diff --git a/TIBERIANDAWN/LIST.CPP b/TIBERIANDAWN/LIST.CPP new file mode 100644 index 000000000..bf8cc17a0 --- /dev/null +++ b/TIBERIANDAWN/LIST.CPP @@ -0,0 +1,894 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\list.cpv 2.17 16 Oct 1995 16:51:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ListClass::Add -- This object adds itself to the given list * + * ListClass::Add_Head -- This gadget makes itself the head of the given list. * + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * ListClass::Add_Item -- Adds an item to the list box. * + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * ListClass::Add_Tail -- Add myself to the end of the given list. * + * ListClass::Bump -- Bumps the list box up/down one "page". * + * ListClass::Current_Index -- Fetches the current selected index. * + * ListClass::Current_Item -- Fetches pointer to current item string. * + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * ListClass::Draw_Me -- Draws the listbox. * + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * ListClass::Remove -- Removes the specified object from the list. * + * ListClass::Remove_Item -- Remove specified text from list box. * + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * ListClass::Step -- Moves the list view one line in direction specified. * + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * ListClass::~ListClass -- Destructor for list class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ListClass::ListClass -- class constructor * + * * + * INPUT: id button ID * + * * + * x,y upper-left corner, in pixels * + * * + * w,h width, height, in pixels * + * * + * list ptr to array of char strings to list* + * * + * flags, style flags for mouse, style of listbox * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ListClass::ListClass (int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= MAX(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = MAX(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + TextFlags = flags; + IsScrollActive = false; + Tabs = 0; + SelectedIndex = 0; + CurrentTopIndex = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + + +/*********************************************************************************************** + * ListClass::~ListClass -- Destructor for list class objects. * + * * + * This is the destructor for list objects. It handles removing anything it might have * + * allocated. This is typically the scroll bar. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ListClass::~ListClass(void) +{ + Remove_Scroll_Bar(); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(char const * text) +{ + if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * * + * This will add the text as specified by the text number provided, to the list box. * + * The string is added to the end of the list. * + * * + * INPUT: text -- The text number for the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: Once a string is added to the list box in this fashion, there is no method of * + * retrieving the text number as it relates to any particular index in the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(int text) +{ + if (text != TXT_NONE) { + Add_Item(Text_String(text)); + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Remove_Item -- Remove specified text from list box. * + * * + * This routine will remove the specified text string from the list box. * + * * + * INPUT: text -- Pointer to the string to remove. * + * OUTPUT: none * + * WARNINGS: The text pointer passed into this routine MUST be the same text pointer that * + * was used to add the string to the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Remove_Item(char const * text) +{ + if (text) { + List.Delete(text); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) + SelectedIndex = 0; + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + + +/*************************************************************************** + * ListClass::Action -- If clicked on, do this! * + * * + * INPUT: int flags -- combination of mouse flags indicating * + * what action to take. * + * * + * OUTPUT: bool result. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +int ListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* -------------------------------------------------- + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = MIN(SelectedIndex, List.Count()-1); + } + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ListClass::Draw_Me -- Draws the listbox. * + * * + * This routine will render the listbox. * + * * + * INPUT: forced -- Should the listbox be redrawn even if it already thinks it doesn't * + * need to be? This is true when something outside of the gadget system * + * has trashed the screen. * + * * + * OUTPUT: Was the listbox redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Bump -- Bumps the list box up/down one "page". * + * * + * Use this routine to adjust the "page" that is being viewed in the list box. The view * + * will move up or down (as specified) one page (screen full) of text strings. * + * * + * INPUT: up -- Should the adjustment be up? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step -- Moves the list view one line in direction specified. * + * * + * This routine will move the current view "page" one line in the direction specified. * + * * + * INPUT: up -- Should the view be moved upward? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * * + * This routine will fetch an item string from the list box. The item fetched can be any * + * one of the ones in the list. * + * * + * INPUT: index -- The index to examine and return the text pointer from. * + * * + * OUTPUT: Returns with the text pointer to the string at the index position specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Get_Item(int index) const +{ + if (List.Count() == 0) return (NULL); + + index = MIN(index, List.Count()-1 ); + return(List[index]); +} + + +/*********************************************************************************************** + * ListClass::Current_Item -- Fetches pointer to current item string. * + * * + * This routine will fetch a pointer to the currently selected item's text. * + * * + * INPUT: none * + * * + * OUTPUT: Return with pointer to currently selected text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Current_Item(void) +{ + return(List[SelectedIndex]); +} + + +/*********************************************************************************************** + * ListClass::Current_Index -- Fetches the current selected index. * + * * + * This routine will fetch the index number for the currently selected line. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the index of the currently selected line. This ranges from zero to * + * the number of items in the list minus one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Current_Index(void) +{ + return(SelectedIndex); +} + + +/*********************************************************************************************** + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * * + * key -- The key value at the time of the event. * + * * + * whom -- Which gadget is being touched. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + + +/*********************************************************************************************** + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * * + * This routine is used to set the line that will be at the top of the list view. This is * + * how the view can be scrolled up and down. This does not affect the currently selected * + * item. * + * * + * INPUT: index -- The line (index) to move to the top of the list view. * + * * + * OUTPUT: bool; Was the view actually changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, List.Count() - LineCount); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * * + * This routine will add a scroll bar (with matching arrows) to the list box. They are * + * added to the right edge and cause the interior of the list box to become narrower. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar added? * + * * + * WARNINGS: The list box becomes narrower when the scroll bar is added. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * * + * Use this routine to remove any attached scroll bar to this list box. If the scroll bar * + * is not present, then no action occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * * + * This sets the tab stop list to be used for text printing. It specifies a series of * + * pixel offsets for each tab stop. The offsets are from the starting pixel position that * + * the text begins at. * + * * + * INPUT: tabs -- Pointer to a list of tab pixel offsets. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only a pointer to the tabs is recorded by the ListClass object. Make sure that * + * the list remains intact for the duration of the existence of this object. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + + +/*********************************************************************************************** + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index], x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} + + +/*********************************************************************************************** + * ListClass::Add -- Adds myself to list immediately after given object * + * * + * Adds the list box to the chain, immemdiately after the given object. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * * + * INPUT: object -- Pointer to the object to be added right after this one. * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Head -- Adds myself to head of the given list * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Tail -- Adds myself to tail of given list * + * * + * Adds the list box to the tail of the give chain. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: none * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * ListClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * ListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + + +/*********************************************************************************************** + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * * + * This routine will set the top line of the listbox to the index value specified. * + * * + * INPUT: index -- The index to set the top of the listbox to. * + * * + * OUTPUT: none * + * * + * WARNINGS: The requested index may be adjusted to fit within legal parameters. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Selected_Index(int index) +{ + if (index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * * + * This routine will scroll the top line of the listbox in the direction specified. * + * * + * INPUT: step -- The direction (and amount) to adjust the listbox. If negative value, then * + * the top line is scrolled upward. * + * * + * OUTPUT: Returns with the original top line index number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} diff --git a/TIBERIANDAWN/LIST.H b/TIBERIANDAWN/LIST.H new file mode 100644 index 000000000..cb5fbf2d3 --- /dev/null +++ b/TIBERIANDAWN/LIST.H @@ -0,0 +1,146 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\list.h_v 2.17 16 Oct 1995 16:46:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LIST_H +#define LIST_H + +#include "control.h" +#include "shapebtn.h" +#include "slider.h" + + +/*************************************************************************** + * ListClass -- Like a Windows ListBox structure * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class ListClass : public ControlClass +{ + public: + ListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual ~ListClass(void); + +// static ListClass * Create_One_Of(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual int Add_Item(char const * text); + virtual int Add_Item(int text); + virtual int Add_Scroll_Bar(void); + virtual void Bump(int up); + virtual int Count(void) {return List.Count();}; + virtual int Current_Index(void); + virtual char const * Current_Item(void); + virtual int Draw_Me(int forced); + virtual char const * Get_Item(int index) const; + virtual int Step_Selected_Index(int forward); + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(char const * text); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const *Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. The pointers + ** are stored in EMS. The text that is pointed to may also be in EMS. + */ + DynamicVectorClass List; + //EMSListOf List; + + /* + ** This is the total pixel height of a standar line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + +#endif diff --git a/TIBERIANDAWN/LOADDLG.CPP b/TIBERIANDAWN/LOADDLG.CPP new file mode 100644 index 000000000..9e2a10a47 --- /dev/null +++ b/TIBERIANDAWN/LOADDLG.CPP @@ -0,0 +1,735 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\loaddlg.cpv 2.18 16 Oct 1995 16:51:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.CPP * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * LoadOptionsClass::Compare -- for qsort * + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * LoadOptionsClass::Process -- main processing routine * + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for unlink + + +/*********************************************************************************************** + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * * + * INPUT: * + * style style for this load/save dialog (LOAD/SAVE/DELETE) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::LoadOptionsClass(LoadStyleType style) +{ + Style = style; + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::~LoadOptionsClass() +{ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Process -- main processing routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * false = User cancelled, true = operation completed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + int d_dialog_w = 250 *factor; + int d_dialog_h = 156 * factor; + int d_dialog_x = (SeenBuff.Get_Width() - d_dialog_w) >> 1; + int d_dialog_y = (SeenBuff.Get_Height() - d_dialog_h) >> 1; + int d_dialog_cx = d_dialog_x + (d_dialog_w >> 1); + int d_txt8_h = 11 * factor; + int d_margin = 7 * factor; + + int d_list_w = d_dialog_w - (d_margin * 2); + int d_list_h = 104 * factor; + int d_list_x = d_dialog_x + d_margin; + int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin; + + int d_edit_w = d_dialog_w - (d_margin * 2); + int d_edit_h = 13 * factor; + int d_edit_x = d_dialog_x + d_margin; + int d_edit_y = d_list_y + d_list_h - (30 * factor) + d_margin + d_txt8_h; + +#ifdef german + int d_button_w = 50 * factor; +#else + int d_button_w = 40 * factor; +#endif + int d_button_h = 13 * factor; + int d_button_x = d_dialog_cx - d_button_w - d_margin; + int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin; + +#ifdef german + int d_cancel_w = 50 * factor; +#else + int d_cancel_w = 40 * factor; +#endif + int d_cancel_h = 13 * factor; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; +#if(0) + enum { + D_DIALOG_W = 250, // dialog width + D_DIALOG_H = 156, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_LIST_W = D_DIALOG_W - (D_MARGIN * 2), + D_LIST_H = 104, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + + D_EDIT_W = D_DIALOG_W - (D_MARGIN * 2), + D_EDIT_H = 13, + D_EDIT_X = D_DIALOG_X + D_MARGIN, + D_EDIT_Y = D_LIST_Y + D_LIST_H - 30 + D_MARGIN + D_TXT8_H, + +#if (GERMAN | FRENCH) + D_BUTTON_W = 50, +#else + D_BUTTON_W = 40, +#endif + D_BUTTON_H = 13, + D_BUTTON_X = D_DIALOG_CX - D_BUTTON_W - D_MARGIN, + D_BUTTON_Y = D_DIALOG_Y + D_DIALOG_H - D_BUTTON_H - D_MARGIN, + +#if (GERMAN | FRENCH) + D_CANCEL_W = 50,//BG:40 +#else + D_CANCEL_W = 40, +#endif + D_CANCEL_H = 13, + D_CANCEL_X = D_DIALOG_CX + D_MARGIN, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + }; +#endif + /* + ** Button enumerations + */ + enum { + BUTTON_LOAD = 100, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_CANCEL, + BUTTON_LIST, + BUTTON_EDIT, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + int list_ht = d_list_h; // adjusted list box height + + /* + ** Other Variables + */ + int btn_txt; // text on the 'OK' button + int btn_id; // ID of 'OK' button + int caption; // dialog caption + int game_idx = 0; // index of game to save/load/etc + int game_num = 0; // file number of game to load/save/etc + char game_descr[40] = {0}; // save-game description + char fname[13]; // for generating filename to delete + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /* + ** Buttons + */ + ControlClass *commands = NULL; // the button list + + if (Style == LOAD) { + btn_txt = TXT_LOAD_BUTTON; + btn_id = BUTTON_LOAD; + caption = TXT_LOAD_MISSION; + } else { + if (Style == SAVE) { + btn_txt = TXT_SAVE_BUTTON; + btn_id = BUTTON_SAVE; + caption = TXT_SAVE_MISSION; + list_ht -= 30; + } else { + btn_txt = TXT_DELETE_BUTTON; + btn_id = BUTTON_DELETE; + caption = TXT_DELETE_MISSION; + } + } + + TextButtonClass button (btn_id, btn_txt, TPF_6PT_GRAD|TPF_CENTER|TPF_NOSHADOW, + d_button_x, d_button_y, d_button_w); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_6PT_GRAD|TPF_CENTER|TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w); + + ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht, + TPF_6PT_GRAD | TPF_NOSHADOW, + up_button, + down_button); + + EditClass editbtn (BUTTON_EDIT, game_descr, 40, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + Fill_List(&listbtn); + + /* + ** Do nothing if list is empty. + */ + if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) { + Clear_List(&listbtn); + CCMessageBox().Process(TXT_NO_SAVES); + return(false); + } + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + listbtn.Add_Tail(*commands); + if (Style == SAVE) { + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + } + + /* + ** Main Processing Loop. + */ + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + cancel = true; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + /* + ** Redraw the map. + */ + if (InMainLoop){ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + }else{ + HiddenPage.Clear(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + } + + + /* + ** Display the dialog box. + */ + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w); + + if (Style == SAVE) { + Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx, + d_edit_y - d_txt8_h, CC_GREEN, TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER | TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus if this is the save dialog. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime && Style == SAVE) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN) { + switch (Style) { + case SAVE: + input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON); + break; + + case LOAD: + input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON); + break; + + case WWDELETE: + input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON); + break; + } + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_LOAD | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (Files[game_idx]->Valid) { + CCMessageBox().Process(TXT_LOADING, TXT_NONE); + if (!Load_Game(game_num)) { + CCMessageBox().Process(TXT_ERROR_LOADING_GAME); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Set_Palette(GamePalette); + Show_Mouse(); + process = false; + } + } else { + CCMessageBox().Process(TXT_OBSOLETE_SAVEGAME); + } + break; + + /* + ** Save: Save the game & exit the dialog + */ + case (BUTTON_SAVE | KN_BUTTON): + if (!strlen(game_descr)) { + CCMessageBox().Process(TXT_MUSTENTER_DESCRIPTION); + firsttime = true; + display = true; + break; + } + game_idx = listbtn.Current_Index(); + if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) { +// CCMessageBox().Process("Insuficent disk space to save a game. Please delete a previous save to free up some disk space and try again."); + CCMessageBox().Process(TXT_SPACE_CANT_SAVE); + firsttime = true; + display = true; + break; + } + + game_num = Files[game_idx]->Num; + if (!Save_Game(game_num,game_descr)) { + CCMessageBox().Process(TXT_ERROR_SAVING_GAME); + } else { + CCMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE); + } + process = false; + break; + + /* + ** Delete: delete the file & stay in the dialog, to allow the user + ** to delete multiple files. + */ + case (BUTTON_DELETE | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (CCMessageBox().Process(TXT_DELETE_FILE_QUERY,TXT_YES,TXT_NO)==0) { + sprintf(fname,"SAVEGAME.%03d",game_num); + unlink(fname); + Clear_List(&listbtn); + Fill_List(&listbtn); + if (listbtn.Count() == 0) { + process = false; + } + } + display = true; + break; + + /* + ** If the user clicks on the list, see if the there is a new current + ** item; if so, and if we're in SAVE mode, copy the list item into + ** the save-game description field. + */ + case (BUTTON_LIST | KN_BUTTON): + if (Style != SAVE) { + break; + } + + if (listbtn.Count() && listbtn.Current_Index() != game_idx) { + game_idx = listbtn.Current_Index(); + /* + ** Copy the game's description, UNLESS it's the empty slot; if + ** it is, set the edit buffer to empty. + */ + if (game_idx != 0) { + strcpy(game_descr,listbtn.Get_Item(game_idx)); + } else { + game_descr[0] = 0; + } + editbtn.Set_Text(game_descr,40); + } + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + Clear_List(&listbtn); + + if (cancel) return(false); + + return(true); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * * + * This step is essential, because it frees all the strings allocated for list items. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void LoadOptionsClass::Clear_List(ListClass *list) +{ + /* + ** For every item in the list, free its buffer & remove it from the list. + */ + int j = list->Count(); + int i; + for (i = 0; i < j; i++) { + list->Remove_Item(list->Get_Item(0)); + } + + /* + ** Clear the array of game numbers + */ + for (i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 06/25/1995 JLB : Shows which saved games are "(old)". * + *=============================================================================================*/ +void LoadOptionsClass::Fill_List(ListClass *list) +{ +//PG_TO_FIX +#if (0) + FileEntryClass *fdata; // for adding entries to 'Files' + char descr[DESCRIP_MAX]; + unsigned scenario; // scenario # + HousesType house; // house + struct find_t ff; // for _dos_findfirst + int id; + + /* + ** Make sure the list is empty + */ + Clear_List(list); + + /* + ** Add the Empty Slot entry + */ + if (Style == SAVE) { + fdata = new FileEntryClass; + strcpy(fdata->Descr,Text_String(TXT_EMPTY_SLOT)); + fdata->DateTime = 0xffffffff; // will always be first + Files.Add(fdata); + } + + /* + ** Find all savegame files + */ + int rc = _dos_findfirst("SAVEGAME.*", _A_NORMAL, &ff); + + while (!rc) { + /* + ** Extract the game ID from the filename + */ + id = Num_From_Ext(ff.name); + + /* + ** get the game's info; if success, add it to the list + */ + bool ok = Get_Savefile_Info(id, descr, &scenario, &house); + + fdata = new FileEntryClass; + + fdata->Descr[0] = '\0'; + if (!ok) strcpy(fdata->Descr, Text_String(TXT_OLD_GAME)); + strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1); + fdata->Valid = ok; + fdata->Scenario = scenario; + fdata->House = house; + fdata->Num = id; + fdata->DateTime = (((unsigned long)ff.wr_date) << 16) | (unsigned long)ff.wr_time; + Files.Add(fdata); + + /* + ** Find the next file + */ + rc = _dos_findnext(&ff); + } + + /* + ** If saving a game, determine a unique file ID for the empty slot + */ + if (Style == SAVE) { + /* + ** Find an un-used number to associate with the Empty Slot by looking in + ** GameNum for each number from 0 to 'N', where 'N' is the # of entries + ** in the list; if any number isn't found, use that number; otherwise, + ** use 'N + 1'. + */ + for (int i = 0; i < Files.Count(); i++) { // i = the # we're searching for + id = -1; // mark as 'not found' + for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's + if (Files[j]->Num==i) { // if found, mark as found + id = j; + break; + } + } + if (id == -1) break; // if ID not found, use this one + } + + Files[0]->Num = i; // set the empty slot's ID + } + + /* + ** Now sort the list in order of Date/Time (newest first, oldest last) + */ + qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare); + + /* + ** Now add every file's name to the list box + */ + for (int i = 0; i < Files.Count(); i++) { + list->Add_Item(Files[i]->Descr); + } +#endif +} + + +/*********************************************************************************************** + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * * + * INPUT: * + * fname filename to parse * + * * + * OUTPUT: * + * File number for this name. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Num_From_Ext(char *fname) +{ + char ext[_MAX_EXT]; + + _splitpath(fname, NULL, NULL, NULL, ext); + int num = atoi(ext + 1); // skip the '.' + return(num); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Compare(const void *p1, const void *p2) +{ + class FileEntryClass *fe1,*fe2; + + fe1 = *((class FileEntryClass **)p1); + fe2 = *((class FileEntryClass **)p2); + + if (fe1->DateTime > fe2->DateTime) return(-1); + if (fe1->DateTime < fe2->DateTime) return(1); + return(0); +} \ No newline at end of file diff --git a/TIBERIANDAWN/LOADDLG.H b/TIBERIANDAWN/LOADDLG.H new file mode 100644 index 000000000..fb3b5f268 --- /dev/null +++ b/TIBERIANDAWN/LOADDLG.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\loaddlg.h_v 2.17 16 Oct 1995 16:48:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.H * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : March 19, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOADDLG_H +#define LOADDLG_H + +class FileEntryClass { + public: + char Descr[40]; // save-game description + unsigned Scenario; // scenario # + HousesType House; // house + int Num; // save file number (from the extension) + unsigned long DateTime; // date/time stamp of file + bool Valid; // Is the scenario valid? +}; + +class LoadOptionsClass +{ + public: + /* + ** This defines the style of the dialog + */ + typedef enum OperationModeEnum { + NONE = 0, + LOAD, + SAVE, + WWDELETE, + } LoadStyleType; + + LoadOptionsClass (LoadStyleType style = LoadOptionsClass::NONE); + ~LoadOptionsClass (); + int Process (void); + + + protected: + /* + ** Internal routines + */ + void Clear_List (ListClass *list); // clears the list & game # array + void Fill_List (ListClass *list); // fills the list & game # array + int Num_From_Ext (char *fname); // translates filename to file # + static int Compare(const void *p1, const void *p2); // for qsort() + + /* + ** This is the requested style of the dialog + */ + LoadStyleType Style; + + /* + ** This is an array of pointers to FileEntryClass objects. These objects + ** are allocated on the fly as files are found, and pointers to them are + ** added to the vector list. Thus, all the objects must be free'd before + ** the vector list is cleared. This list is used for sorting the files + ** by date/time. + */ + DynamicVectorClass Files; +}; + + +#endif diff --git a/TIBERIANDAWN/LOGIC.CPP b/TIBERIANDAWN/LOGIC.CPP new file mode 100644 index 000000000..f6298f630 --- /dev/null +++ b/TIBERIANDAWN/LOGIC.CPP @@ -0,0 +1,342 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\logic.cpv 2.17 16 Oct 1995 16:50:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 27, 1993 * + * * + * Last Update : December 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LogicClass::AI -- Handles AI logic processing for game objects. * + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "logic.h" + +static unsigned FramesPerSecond=0; + +#ifdef CHEAT_KEYS + +static unsigned TotalFrames; +static unsigned FPSDivider = 1; +static unsigned AverageFramesPerSecond; + +/*********************************************************************************************** + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * * + * This is a debugging support routine. It displays the current state of the logic class * + * to the monochrome monitor. It assumes that it is being called once per second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per second. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void LogicClass::Debug_Dump(MonoClass *mono) const +{ + #define RECORDCOUNT 40 + #define RECORDHEIGHT 21 + static struct { + int Graphic; + } _record[RECORDCOUNT]; + static int _framecounter = 0; + + TotalFrames+= FramesPerSecond; + AverageFramesPerSecond = TotalFrames/FPSDivider++; + + mono->Set_Cursor(21, 9); + mono->Print( + "ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r" + "³Units.....³ ³Frame Rate: Avg: Frame: ³\r" + "³Infantry..³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\r" + "³Aircraft..³ ³ ³\r" + "³Buildings.³ ³ ³\r" + "³Terrain...³ à ´\r" + "³Bullets...³ ³ ³\r" + "³Anims.....³ ³ ³\r" + "³Teams.....³ à Ĵ\r" + "³Triggers..³ ³ ³\r" + "³Factories.³ ³ ³\r" + "³ ³ à ´\r" + "³ ³ ³ ³\r" + "ÀÄÄÄÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄ´Spare CPU TimeÃÄÄÄÄÄÄÄÄÄÄÄÄÙ\r"); + + _framecounter++; + mono->Set_Cursor(70, 10);mono->Printf("%ld", Frame); + if (ScenarioInit) { + mono->Set_Cursor(21, 9);mono->Printf("%d", ScenarioInit); + } + + mono->Set_Cursor(33, 10);mono->Printf("%3d", Units.Count()); + mono->Set_Cursor(33, 11);mono->Printf("%3d", Infantry.Count()); + mono->Set_Cursor(33, 12);mono->Printf("%3d", Aircraft.Count()); + mono->Set_Cursor(33, 13);mono->Printf("%3d", Buildings.Count()); + mono->Set_Cursor(33, 14);mono->Printf("%3d", Terrains.Count()); + mono->Set_Cursor(33, 15);mono->Printf("%3d", Bullets.Count()); + mono->Set_Cursor(33, 16);mono->Printf("%3d", Anims.Count()); + mono->Set_Cursor(33, 17);mono->Printf("%3d", Teams.Count()); + mono->Set_Cursor(33, 18);mono->Printf("%3d", Triggers.Count()); + mono->Set_Cursor(33, 19);mono->Printf("%3d", Factories.Count()); + + mono->Set_Cursor(48, 10);mono->Printf("%d", FramesPerSecond); + mono->Set_Cursor(58, 10);mono->Printf("%d", AverageFramesPerSecond); + + /* + ** Advance to the next recorded performance record. If the record buffer + ** is full then throw out the oldest record. + */ + memcpy(&_record[0], &_record[1], sizeof(_record[0])*(RECORDCOUNT-1)); + + /* + ** Fill in the data for the current frame's performance record. + */ + SpareTicks = MIN((long)SpareTicks, (long)TIMER_SECOND); + _record[RECORDCOUNT-1].Graphic = Fixed_To_Cardinal(RECORDHEIGHT, Cardinal_To_Fixed(TIMER_SECOND, SpareTicks)); + + /* + ** Draw the bars across the performance record screen. + */ + for (int column = 0; column < RECORDCOUNT; column++) { + for (int row = 1; row < RECORDHEIGHT; row += 2) { + static unsigned char _barchar[4] = {' ', 220, 0, 219}; + char str[2]; + int index = 0; + + index |= (_record[column].Graphic >= row) ? 0x01 : 0x00; + index |= (_record[column].Graphic >= row+1) ? 0x02: 0x00; + + str[1] = '\0'; + str[0] = _barchar[index]; + mono->Text_Print(str, 37+column, 21-(row/2)); + } + } + + SpareTicks = 0; + FramesPerSecond = 0; +} +#endif + + +/*********************************************************************************************** + * LogicClass::AI -- Handles AI logic processing for game objects. * + * * + * This routine is used to perform the AI processing for all game objects. This includes * + * all houses, factories, objects, and teams. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * + * 12/23/1994 JLB : Esures that no object gets skipped if it was deleted. * + *=============================================================================================*/ +void LogicClass::AI(void) +{ + int index; + + FramesPerSecond++; + + /* + ** Crate regeneration is handled here. + */ + if (GameToPlay != GAME_NORMAL && CrateMaker && CrateTimer.Expired()) { + Map.Place_Random_Crate(); + CrateTimer = TICKS_PER_MINUTE * Random_Pick(7, 15); + } + + /* + ** Team AI is processed. + */ + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->AI(); + } + +// Heap_Dump_Check( "After Team AI" ); + + /* + ** AI for all sentient objects is processed. + */ + for (index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + + obj->AI(); + + /* + ** If the object was destroyed in the process of performing its AI, then + ** adjust the index so that no object gets skipped. + */ + if (obj != (*this)[index]) { +// if (!obj->IsActive) { + index--; + } + } + +// Heap_Dump_Check( "After Object AI" ); + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (unit->IsLocked && (GameToPlay != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + unit->House->NewUScan |= (1L << unit->Class->Type); + if (!unit->IsInLimbo) unit->House->NewActiveUScan |= (1L << unit->Class->Type); + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (infantry->IsLocked && (GameToPlay != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + infantry->House->NewIScan |= (1L << infantry->Class->Type); + if (!infantry->IsInLimbo) infantry->House->NewActiveIScan |= (1L << infantry->Class->Type); + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (aircraft->IsLocked && (GameToPlay != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + aircraft->House->NewAScan |= (1L << aircraft->Class->Type); + if (!aircraft->IsInLimbo) aircraft->House->NewActiveAScan |= (1L << aircraft->Class->Type); + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->IsLocked && (GameToPlay != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + building->House->NewBScan |= (1L << building->Class->Type); + if (!building->IsInLimbo) building->House->NewActiveBScan |= (1L << building->Class->Type); + } + } + +// Heap_Dump_Check( "After Object AI 2" ); + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 10:56AM + // + HouseClass::Recalc_Attributes(); +#endif // USE_RA_AI + + /* + ** Map related logic is performed. + */ + Map.Logic(); + +// Heap_Dump_Check( "After Map.Logic" ); + + /* + ** Factory processing is performed. + */ + for (index = 0; index < Factories.Count(); index++) { + Factories.Ptr(index)->AI(); + } + +// Heap_Dump_Check( "After Factory AI" ); + +#if (1) + /* + ** Changed integrated from RA to only call AI on the houses that need it. Without this change, AI houses immediately + ** become paranoid at the start of a multiplayer match + ** ST - 10/30/2019 11:15AM + */ + if (GameToPlay != GAME_NORMAL) { + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + + HouseClass* neutral_house = HouseClass::As_Pointer(HOUSE_NEUTRAL); + if (neutral_house && neutral_house->IsActive) { + neutral_house->AI(); + } + + HouseClass* jp_house = HouseClass::As_Pointer(HOUSE_JP); + if (jp_house && jp_house->IsActive) { + jp_house->AI(); + } + + } else { + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } + + +#else + /* + ** House processing is performed. + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } +#endif + +// Heap_Dump_Check( "After House AI" ); +} + + + + + + +/*********************************************************************************************** + * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were * + * recently created * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 8/19/2019 5:47PM ST : Created. * + *=============================================================================================*/ +void LogicClass::Clear_Recently_Created_Bits(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + obj->IsRecentlyCreated = false; + } +} + + \ No newline at end of file diff --git a/TIBERIANDAWN/LOGIC.H b/TIBERIANDAWN/LOGIC.H new file mode 100644 index 000000000..a60f991f6 --- /dev/null +++ b/TIBERIANDAWN/LOGIC.H @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\logic.h_v 2.17 16 Oct 1995 16:45:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 29, 1994 * + * * + * Last Update : May 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "layer.h" + +/*********************************************************************************************** +** Game logic processing is controlled by this class. The graphic and AI logic is handled +** separately so that on slower machines, the graphic display is least affected. +*/ +class LogicClass : public LayerClass +{ + public: + void AI(void); + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Added. ST - 8/19/2019 5:46PM + */ + void Clear_Recently_Created_Bits(void); +}; +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/License.txt b/TIBERIANDAWN/License.txt new file mode 100644 index 000000000..a4adf6d01 --- /dev/null +++ b/TIBERIANDAWN/License.txt @@ -0,0 +1,712 @@ +Electronic Arts Inc. released only TiberianDawn.dll and RedAlert.dll and their corresponding source code under the GPL V3 below, with additional terms at the bottom. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +ADDITIONAL TERMS per GNU GPL Section 7 +No trademark or publicity rights are granted. This license does NOT give you +any right, title or interest in "Command & Conquer" or any other Electronic Arts trademark. You may not distribute any +modification of this program using any Electronic Arts trademark or claim any affiliation or association with Electronic Arts Inc. +or its affiliates or their employees. + +Any propagation or conveyance of this program must include this copyright +notice and these terms. + +If you convey this program (or any modifications of it) and assume +contractual liability for the program to recipients of it, you agree to +indemnify Electronic Arts for any liability that those contractual +assumptions impose on Electronic Arts. + +You may not misrepresent the origins of this program; modified versions of +the program must be marked as such and not identified as the original program. + +This disclaimer supplements the one included in the General Public License. +TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS PROGRAM IS +PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY OF ANY KIND, AND +YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF SATISFACTORY QUALITY AND +PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS DISCLAIMS ANY AND ALL EXPRESS, +IMPLIED OR STATUTORY WARRANTIES, INCLUDING IMPLIED WARRANTIES OF +MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, +NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A +COURSE OF DEALING, USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT +AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL +MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE WITH THIRD PARTY SOFTWARE +OR THAT ANY ERRORS IN THE PROGRAM WILL BE CORRECTED. NO ORAL OR WRITTEN ADVICE +PROVIDED BY ELECTRONIC ARTS OR ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A +WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON +IMPLIED WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A +CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY +TO YOU. \ No newline at end of file diff --git a/TIBERIANDAWN/MAP.CPP b/TIBERIANDAWN/MAP.CPP new file mode 100644 index 000000000..1d1f8538b --- /dev/null +++ b/TIBERIANDAWN/MAP.CPP @@ -0,0 +1,1577 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\map.cpv 2.17 16 Oct 1995 16:52:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MapClass::Cell_Distance -- Determines the distance between two cells. * + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * MapClass::In_Radar -- Is specified cell in the radar map? * + * MapClass::Init -- clears all cells * + * MapClass::Logic -- Handles map related logic functions. * + * MapClass::One_Time -- Performs special one time initializations for the map. * + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * MapClass::Pick_Up -- Removes specified object from the map. * + * MapClass::Place_Down -- Places the specified object onto the map. * + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * MapClass::Read_Binary -- reads the map's binary image file * + * MapClass::Set_Map_Dimensions -- Initialize the map. * + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * MapClass::Validate -- validates every cell on the map * + * MapClass::Write_Binary -- writes the map's binary image file * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W +int const MapClass::RadiusOffset[] = { + /* 0 */ 0, + /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, + /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, + /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, + /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, + /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, + /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, + /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, + /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, + /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, + /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, + (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, +}; + +int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; + + +CellClass *BlubCell; + +/*********************************************************************************************** + * MapClass::One_Time -- Performs special one time initializations for the map. * + * * + * This routine is used by the game initialization function in order to perform any one * + * time initializations required for the map. This includes allocation of the map and * + * setting up its default dimensions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine MUST be called once and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/01/1994 BR : Added CellTriggers initialization * + *=============================================================================================*/ +void MapClass::One_Time(void) +{ + GScreenClass::One_Time(); + + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); + + /* + ** Init the CellTriggers array to the required size. + */ + CellTriggers.Resize(MAP_CELL_TOTAL); +} + +//////////////////////////////////////////////////// +// Added this function to allow the editor to setup the map without setting up the entire system. - 06/18/2019 JAS +void MapClass::One_Time_Editor(void) +{ + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); + + /* + ** Init the CellTriggers array to the required size. + */ + CellTriggers.Resize(MAP_CELL_TOTAL); +} +// End of change. - 06/15/2019 JAS +//////////////////////////////////////////////////// + +/*********************************************************************************************** + * MapClass::Init_Clear -- clears the map & buffers to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Clear(void) +{ + GScreenClass::Init_Clear(); + Init_Cells(); + TiberiumScan = 0; + IsForwardScan = true; + TiberiumGrowthCount = 0; + TiberiumSpreadCount = 0; +} + + +/*********************************************************************************************** + * MapClass::Alloc_Cells -- allocates the cell array * + * * + * This routine should be called at One_Time, and after loading the Map object from a save * + * game, but prior to loading the cell objects. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Alloc_Cells(void) +{ + /* + ** Assume that whatever the contents of the VectorClass are is garbage + ** (it may have been loaded from a save-game file), so zero it out first. + */ + Vector = 0; + VectorMax = 0; + IsAllocated = 0; + Resize(Size); +} + + +/*********************************************************************************************** + * MapClass::Free_Cells -- frees the cell array * + * * + * This routine is used by the Load_Game routine to free the map's cell array before loading * + * the map object from disk; the array is then re-allocated & cleared before the cell objects * + * are loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Free_Cells(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * + * * + * This routine is used by Init_Clear to set the cells to a known state; it's also used by * + * the Load_Game routine to init all cells before loading a set of cells from disk, so it * + * needs to be called separately from the other Init_xxx() routines. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Cells(void) +{ + TotalValue = 0; +#ifdef NEVER + Free_Cells(); + Alloc_Cells(); +#else + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + Map[index] = CellClass(); + } +#endif +} + + +/*********************************************************************************************** + * MapClass::Set_Map_Dimensions -- Set map dimensions. * + * * + * This routine is used to set the legal limits and position of the * + * map as it relates to the overall map array. Typically, this is * + * called by the scenario loading code. * + * * + * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * + * of the map. * + * * + * w,h -- The width and height of the legal map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + MapCellX = x; + MapCellY = y; + MapCellWidth = w; + MapCellHeight = h; +} + + +/*********************************************************************************************** + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * * + * This routine is used to reveal the cells around a specific location. * + * Typically, as a unit moves or is deployed, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: house -- Player to perform the visibility update for * + * * + * cell -- The coordinate that the sighting originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * incremental-- Is this an incremental sighting. In other * + * words, has this function been called before where * + * the center coordinate is no more than one cell * + * distant from the last time? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1992 JLB : Created. * + * 03/08/1994 JLB : Updated to use sight table and incremental flag. * + * 05/18/1994 JLB : Converted to member function. * + * 03/06/2019 ST : Added HouseClass pointer parameter for new multiplayer code * + *=============================================================================================*/ +void MapClass::Sight_From(HouseClass *house, CELL cell, int sightrange, bool incremental) +{ + int xx; // Center cell X coordinate (bounds checking). + int const *ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + // Added. ST - 3/6/2019 1:50PM + if ((house == NULL || house->IsHuman == false) && !ShareAllyVisibility) { + return; + } + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > 10) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + if (incremental) { + if (sightrange > 1) { + ptr += RadiusCount[sightrange-2]; + count -= RadiusCount[sightrange-2]; + } + } + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(newcell, cell) > sightrange) continue; + + /* + ** Map the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + if (!(*this)[newcell].Is_Mapped(house)) { + // Pass the house through, instead of assuming it's the local player. ST - 3/6/2019 10:26AM + //Map.Map_Cell(newcell, PlayerPtr); + Map.Map_Cell(newcell, house, true); + } + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Distance -- Determines the distance between two cells. * + * * + * This routine will return with the calculated "straight line" * + * distance between the two cells specified. It uses the dragon strike * + * method of distance calculation. * + * * + * INPUT: cell1 -- First cell. * + * * + * cell2 -- Second cell. * + * * + * OUTPUT: Returns with the "cell" distance between the two cells * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/29/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int MapClass::Cell_Distance(CELL cell1, CELL cell2) +{ + register int x,y; // Difference on X and Y axis. + + x = Cell_X(cell1) - Cell_X(cell2); + y = Cell_Y(cell1) - Cell_Y(cell2); + + if (x < 0) x = -x; + if (y < 0) y = -y; + + if (x > y) { + return(x + (y>>1)); + } + return(y + (x>>1)); +} + + +/*********************************************************************************************** + * MapClass::In_Radar -- Is specified cell in the radar map? * + * * + * This determines if the specified cell can be within the navigable * + * bounds of the map. Technically, this means, any cell that can be * + * scanned by radar. If a cell returns false from this function, then * + * the player could never move to or pass over this cell. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: bool; Is this cell possible to be displayed on radar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/01/1994 JLB : Speeded up. * + *=============================================================================================*/ +bool MapClass::In_Radar(CELL cell) const +{ + if (cell & 0xF000) return(false); + return((unsigned)(Cell_X(cell) - MapCellX) < (unsigned)MapCellWidth && (unsigned)(Cell_Y(cell) - MapCellY) < (unsigned)MapCellHeight); +} + + +/*********************************************************************************************** + * MapClass::Place_Down -- Places the specified object onto the map. * + * * + * This routine is used to place an object onto the map. It updates the "occupier" of the * + * cells that this object covers. The cells are determined from the Occupy_List function * + * provided by the object. Only one cell can have an occupier and this routine is the only * + * place that sets this condition. * + * * + * INPUT: cell -- The cell to base object occupation around. * + * * + * object -- The object to place onto the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Place_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Occupy_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Down(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Pick_Up -- Removes specified object from the map. * + * * + * The object specified is removed from the map by this routine. This will remove the * + * occupation flag for all the cells that the object covers. The cells that are covered * + * are determined from the Occupy_List function. * + * * + * INPUT: cell -- The cell that the object is centered about. * + * * + * object -- Pointer to the object that will be removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Pick_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Occupy_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Up(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * * + * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * * + * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * * + * This routine will clean up anything necessary with the presumption that the map has * + * been freshly created. Such things to clean up include various tiberium concentrations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the total credit value of the tiberium on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/13/1995 JLB : Returns total tiberium worth. * + * 02/15/1995 JLB : Optimal scan. * + *=============================================================================================*/ +long MapClass::Overpass(void) +{ + long value = 0; + + /* + ** Smooth out Tiberium. Cells that are not surrounded by other tiberium + ** will be reduced in density. + */ + for (int y = 0; y < MapCellHeight-1; y++) { + for (int x = 0; x < MapCellWidth; x++) { + value += (*this)[(MapCellY+y) * MAP_CELL_W + (MapCellX+x)].Tiberium_Adjust(true); + } + } + return(value); +} + + +/*********************************************************************************************** + * MapClass::Read_Binary -- reads the map's binary image file * + * * + * INPUT: * + * root root filename for scenario * + * crc ptr to CRC value to update * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + * 01/08/1995 JLB : Fixup any obsolete icons detected. * + *=============================================================================================*/ +#ifdef DEMO +bool MapClass::Read_Binary(char const * root, unsigned long *) +#else +bool MapClass::Read_Binary(char const * root, unsigned long *crc) +#endif +{ + CCFileClass file; + char fname[_MAX_FNAME+_MAX_EXT]; + int i; + char *map; + void *rawmap; + void const * shape; + + /* + ** Filename = INI name with BIN extension. + */ + sprintf(fname,"%s.BIN",root); + + /* + ** Create object & open file. + */ + file.Set_Name(fname); + if (!file.Is_Available()) { + return(false); + } + file.Open(READ); + + /* + ** Loop through all cells. + */ + CellClass * cellptr = &Map[0]; + for (i = 0; i < MAP_CELL_TOTAL; i++) { + struct { + TemplateType TType; // Template type. + unsigned char TIcon; // Template icon number. + } temp; + + if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break; + if (temp.TType == (TemplateType)255) { + temp.TType = TEMPLATE_NONE; + } + + /* + ** Verify that the template type actually contains the template number specified. If + ** an illegal icon was specified, then replace it with clear terrain. + */ + if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) { + TemplateTypeClass const &ttype = TemplateTypeClass::As_Reference(temp.TType); + shape = ttype.Get_Image_Data(); + if (shape) { + rawmap = Get_Icon_Set_Map(shape); + if (rawmap) { + map = (char*)rawmap; + if ((temp.TIcon >= (ttype.Width * ttype.Height)) || (map[temp.TIcon] == -1)) { + temp.TIcon = 0; + temp.TType = TEMPLATE_NONE; + } + } + } + } + + cellptr->TType = temp.TType; + cellptr->TIcon = temp.TIcon; + cellptr->Recalc_Attributes(); + +#ifndef DEMO + Add_CRC(crc, (unsigned long)cellptr->TType); + Add_CRC(crc, (unsigned long)cellptr->TIcon); +#endif + + cellptr++; + } + + /* + ** Close the file. + */ + file.Close(); + + return(i == MAP_CELL_TOTAL); +} + + +/*********************************************************************************************** + * MapClass::Read_BinaryRead_Binary_File -- reads the map's binary image file * + * * + * INPUT: * + * fname file path for scenario * + * crc ptr to CRC value to update * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/28/2019 JAS : Created. * + *=============================================================================================*/ +bool MapClass::Read_Binary_File(char const * fname, unsigned long *crc) +{ + CCFileClass file; + + int i; + char *map; + void *rawmap; + void const * shape; + + /* + ** Create object & open file. + */ + file.Set_Name(fname); + if (!file.Is_Available()) { + return(false); + } + file.Open(READ); + + /* + ** Loop through all cells. + */ + CellClass * cellptr = &Map[0]; + for (i = 0; i < MAP_CELL_TOTAL; i++) { + struct { + TemplateType TType; // Template type. + unsigned char TIcon; // Template icon number. + } temp; + + if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break; + if (temp.TType == (TemplateType)255) { + temp.TType = TEMPLATE_NONE; + } + + /* + ** Verify that the template type actually contains the template number specified. If + ** an illegal icon was specified, then replace it with clear terrain. + */ + if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) { + shape = TemplateTypeClass::As_Reference(temp.TType).Get_Image_Data(); + if (shape) { + rawmap = Get_Icon_Set_Map(shape); + if (rawmap) { + map = (char*)rawmap; + if (map[temp.TIcon] == -1) { + temp.TIcon = 0; + temp.TType = TEMPLATE_NONE; + } + } + } + } + + cellptr->TType = temp.TType; + cellptr->TIcon = temp.TIcon; + cellptr->Recalc_Attributes(); + +#ifndef DEMO + Add_CRC(crc, (unsigned long)cellptr->TType); + Add_CRC(crc, (unsigned long)cellptr->TIcon); +#endif + + cellptr++; + } + + /* + ** Close the file. + */ + file.Close(); + + return(i == MAP_CELL_TOTAL); +} + + + +/*********************************************************************************************** + * MapClass::Write_Binary -- writes the map's binary image file * + * * + * INPUT: * + * root root filename for scenario * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + *=============================================================================================*/ +bool MapClass::Write_Binary(char const * root) +{ + CCFileClass *file; + char fname[_MAX_FNAME+_MAX_EXT]; + int i; + + /* + ** Filename = INI name with BIN extension. + */ + sprintf(fname,"%s.BIN",root); + + /* + ** Create object & open file. + */ + file = new CCFileClass(fname); + file->Open(WRITE); + + /* + ** Loop through all cells. + */ + for (i = 0; i < MAP_CELL_TOTAL; i++) { + /* + ** Save TType. + */ + if (file->Write (&(Map[i].TType), sizeof(TemplateType)) != sizeof(TemplateType)) { + file->Close(); + delete file; + return(false); + } + + /* + ** Save TIcon. + */ + if (file->Write (&(Map[i].TIcon), sizeof(unsigned char)) != sizeof(unsigned char)) { + file->Close(); + delete file; + return(false); + } + } + + /* + ** Close the file. + */ + file->Close(); + delete file; + + return(true); +} + + +/*********************************************************************************************** + * MapClass::Logic -- Handles map related logic functions. * + * * + * Manages tiberium growth and spread. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + * 07/09/1995 JLB : Handles two directional scan. * + * 08/01/1995 JLB : Gives stronger weight to blossom trees. * + *=============================================================================================*/ +void MapClass::Logic(void) +{ + if (Debug_Force_Crash) { *((int *)0) = 1; } + + /* + ** Bail early if there is no allowed growth or spread of Tiberium. + */ + if (!Special.IsTGrowth && !Special.IsTSpread) return; + + /* + ** Scan another block of the map in order to accumulate the potential + ** Tiberium cells that can grow or spread. + */ + int subcount = 30; + int index; + for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { + CELL cell = index; + if (!IsForwardScan) cell = (MAP_CELL_TOTAL-1) - index; + CellClass *ptr = &(*this)[cell]; + + if (Special.IsTGrowth && ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData < 11) { + if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { + TiberiumGrowth[TiberiumGrowthCount++] = cell; + } else { + TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; + } + } + + /* + ** Heavy Tiberium growth can spread. + */ + TerrainClass * terrain = ptr->Cell_Terrain(); + if (Special.IsTSpread && + (ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData > 6) || + (terrain && terrain->Class->IsTiberiumSpawn)) { + + int tries = 1; + if (terrain) tries = 3; + for (int i = 0; i < tries; i++) { + if (TiberiumSpreadCount < sizeof(TiberiumSpread)/sizeof(TiberiumSpread[0])) { + TiberiumSpread[TiberiumSpreadCount++] = cell; + } else { + TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; + } + } + } + subcount--; + if (!subcount) break; + } + TiberiumScan = index; + + if (TiberiumScan >= MAP_CELL_TOTAL) { + int tries = 1; + if (Special.IsTFast || GameToPlay != GAME_NORMAL) tries = 2; + TiberiumScan = 0; + IsForwardScan = (IsForwardScan == false); + + /* + ** Growth logic. + */ + if (TiberiumGrowthCount) { + for (int i = 0; i < tries; i++) { + CELL cell = TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)]; + CellClass * newcell = &(*this)[cell]; + if (newcell->Land_Type() == LAND_TIBERIUM && newcell->OverlayData < 12-1) { + newcell->OverlayData++; + } + } + } + TiberiumGrowthCount = 0; + + /* + ** Spread logic. + */ + if (TiberiumSpreadCount) { + for (int i = 0; i < tries; i++) { + CELL cell = TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)]; + + /* + ** Find a pseudo-random adjacent cell that doesn't contain any tiberium. + */ + if (Map.In_Radar(cell)) { + FacingType offset = Random_Pick(FACING_N, FACING_NW); + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + CellClass *newcell = &(*this)[cell].Adjacent_Cell(index+offset); + + if (newcell && newcell->Cell_Object() == NULL && newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) { + bool found = false; + + switch (newcell->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE3: + case TEMPLATE_BRIDGE4: + break; + + default: + found = true; + new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number()); + newcell->OverlayData = 1; + break; + + } + if (found) break; + } + } + } + } + } + TiberiumSpreadCount = 0; + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * * + * Use this routine to determine what region a particular cell lies in. * + * * + * INPUT: cell -- The cell number to examine. * + * * + * OUTPUT: Returns with the region that the specified cell occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 JLB : Created. * + *=============================================================================================*/ +int MapClass::Cell_Region(CELL cell) +{ + return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); +} + + +/*************************************************************************** + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * * + * INPUT: CELL cell - the cell number to check * + * HouseType house - the house to check * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1995 PWG : Created. * + *=========================================================================*/ +int MapClass::Cell_Threat(CELL cell, HousesType house) +{ + int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (!threat && Map[cell].Is_Visible(house)) { + threat = 1; + } + return(threat); +} + + +/*********************************************************************************************** + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * * + * This routine will place a crate at a random location on the map. This routine will only * + * make a limited number of attempts to place and if unsuccessful, it will not place any. * + * * + * INPUT: none * + * * + * OUTPUT: Was a crate successfully placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Place_Random_Crate(void) +{ + int old = ScenarioInit; + ScenarioInit = 0; + for (int index = 0; index < 100; index++) { + int x = Random_Pick(0, MapCellWidth-1); + int y = Random_Pick(0, MapCellHeight-1); + CELL cell = XY_Cell(MapCellX+x, MapCellY+y); + + CellClass * ptr = &(*this)[cell]; + if (ptr->Is_Generally_Clear() && ptr->Overlay == OVERLAY_NONE) { + ptr->Overlay = OVERLAY_WOOD_CRATE; + ptr->OverlayData = 0; + ptr->Redraw_Objects(); + ScenarioInit = old; + return(true); + } + } + ScenarioInit = old; + return(false); +} + + +/*************************************************************************** + * MapClass::Validate -- validates every cell on the map * + * * + * This is a debugging routine, designed to detect memory trashers that * + * alter the map. This routine is slow, but thorough. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = map is OK, false = an error was found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +int MapClass::Validate(void) +{ + CELL cell; + TemplateType ttype; + unsigned char ticon; + //TemplateTypeClass const *tclass; + //unsigned char map[13*8]; + OverlayType overlay; + SmudgeType smudge; + ObjectClass *obj; + LandType land; + int i; + +BlubCell = &((*this)[797]); + +if (BlubCell->Overlapper[1]) { + obj = BlubCell->Overlapper[1]; + if (obj) { + if (obj->IsInLimbo) + obj = obj; + } +} + + /*------------------------------------------------------------------------ + Check every cell on the map, even those that aren't displayed, + in the hopes of detecting a memory trasher. + ------------------------------------------------------------------------*/ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + /*..................................................................... + Validate Template & Icon data + .....................................................................*/ + ttype = (*this)[cell].TType; + ticon = (*this)[cell].TIcon; + if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) + return(false); + + /*..................................................................... + To validate the icon value, we have to get a copy of the template's + "icon map"; this map will have 0xff's in spots where there is no + icon. If the icon value is out of range or points to an invalide spot, + return an error. + .....................................................................*/ +#if (0) + if (ttype != TEMPLATE_NONE) { + tclass = &TemplateTypeClass::As_Reference(ttype); + ticon = (*this)[cell].TIcon; + Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, + tclass->Width * tclass->Height); + if (ticon < 0 || + ticon >= (tclass->Width * tclass->Height) || + map[ticon]==0xff) + return (false); + } +#endif + /*..................................................................... + Validate Overlay + .....................................................................*/ + overlay = (*this)[cell].Overlay; + if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) + return(false); + + /*..................................................................... + Validate Smudge + .....................................................................*/ + smudge = (*this)[cell].Smudge; + if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) + return(false); + + /*..................................................................... + Validate LandType + .....................................................................*/ + land = (*this)[cell].Land_Type(); + if (land < LAND_CLEAR || land >= LAND_COUNT) + return(false); + + /*..................................................................... + Validate Occupier + .....................................................................*/ + obj = (*this)[cell].Cell_Occupier(); + if (obj) { + + volatile TARGET target = obj->As_Target(); // This will do some internal verification + + if (!obj->IsActive) { + return false; + } + + if (obj->IsInLimbo) { + return false; + } + + if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) { + return (false); + } + } + + /*..................................................................... + Validate Overlappers + .....................................................................*/ + for (i = 0; i < 3; i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + + volatile TARGET target = obj->As_Target(); // This will do some internal verification + + if (!obj->IsActive) { + return false; + } + + if (obj->IsInLimbo) { + return false; + } + + if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) { + return (false); + } + } + } + } + + return (true); +} + + + +/*********************************************************************************************** + * MapClass::Clean -- Clean up dangling pointers caused by bugs in the originl code. * + * * + * Ideally, we'd fix the underlying cause of the overlappers not being cleared * + * but we can afford the CPU time now * + * * + * INPUT: none * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/14/2020 11:48AM ST : Created. * + *=============================================================================================*/ +void MapClass::Clean(void) +{ + CELL cell; + ObjectClass *obj; + int i; +#ifndef NDEBUG + char debug_message[256]; +#endif + bool active_fail = false; + bool limbo_fail = false; + const char *type_text = NULL; + const char *ini_name = NULL; + AbstractClass abstract_object; + unsigned long abstract_vtable = *(unsigned long*)&abstract_object; + + /*------------------------------------------------------------------------ + Check every cell on the map, even those that aren't displayed. + ------------------------------------------------------------------------*/ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + + /*..................................................................... + Validate Occupier + .....................................................................*/ + (*this)[cell].Cell_Occupier(); + + /*..................................................................... + Validate Overlappers + .....................................................................*/ + for (i = 0; i < 3; i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + + if (!obj->IsActive) { + (*this)[cell].Overlapper[i] = NULL; + active_fail = true; + } + + if (!active_fail && obj->IsInLimbo) { + (*this)[cell].Overlapper[i] = NULL; + limbo_fail = true; + } + + if (active_fail || limbo_fail) { +#ifndef NDEBUG + /* + ** This object is likely deleted. + */ + if (abstract_vtable == *(unsigned long*)obj) { + type_text = "Abstract"; + ini_name = "UNKNOWN"; + } else { + + RTTIType type = obj->What_Am_I(); + + switch (type) { + default: + type_text = "Unknown"; + break; + + case RTTI_INFANTRY: + type_text = "Infantry"; + break; + + case RTTI_UNIT: + type_text = "Unit"; + break; + + case RTTI_AIRCRAFT: + type_text = "Aircraft"; + break; + + case RTTI_BUILDING: + type_text = "Building"; + break; + + case RTTI_BULLET: + type_text = "Bullet"; + break; + + case RTTI_ANIM: + type_text = "Anim"; + break; + + case RTTI_SMUDGE: + type_text = "Smudge"; + break; + + case RTTI_TERRAIN: + type_text = "Terrain"; + break; + } + + ini_name = obj->Class_Of().IniName; + } + + sprintf_s(debug_message, sizeof(debug_message) - 1, "Cleaned %s overlapper in cell %08X. Type=%s, IniName=%s", active_fail ? "inactive" : "limbo", cell, type_text, ini_name); + GlyphX_Debug_Print(debug_message); +#endif //NDEBUG + } + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * * + * This routine is used by the mouse input processing code to find a clickable object * + * close to coordinate specified. This is for targeting as well as selection determination. * + * * + * INPUT: coord -- The coordinate to scan for close object from. * + * * + * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * + * * + * WARNINGS: There could be a cloaked object at the location, but it won't be considered * + * if it is not owned by the player. * + * * + * HISTORY: * + * 08/20/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * MapClass::Close_Object(COORDINATE coord) const +{ + ObjectClass * object = 0; + int distance = 0; + CELL cell = Coord_Cell(coord); + + /* + ** Scan through current and adjacent cells, looking for the + ** closest object (within reason) to the specified coordinate. + */ + static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; + for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + + /* + ** Examine the cell for close object. Make sure that the cell actually is a + ** legal one. + */ + CELL newcell = cell + _offsets[index]; + if (In_Radar(newcell)) { + + /* + ** Search through all objects that occupy this cell and then + ** find the closest object. Check against any previously found object + ** to ensure that it is actually closer. + */ + ObjectClass * o = (*this)[newcell].Cell_Occupier(); + while (o) { + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + // Changed for multiplayer. ST - 3/13/2019 5:38PM + if (!o->Is_Techno() || ((TechnoClass *)o)->Is_Owned_By_Player() || ((TechnoClass *)o)->Cloak != CLOAKED) { + //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { + int d=-1; + if (o->What_Am_I() == RTTI_BUILDING) { + d = Distance(coord, Cell_Coord(newcell)); + if (d > 0x00C0) d = -1; + } else { + d = Distance(coord, o->Center_Coord()); + } + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = o; + } + } + o = o->Next; + } + } + } + + /* + ** Handle aircraft selection separately, since they aren't tracked in cells while flying + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->In_Which_Layer() != LAYER_GROUND) { + if (aircraft->Is_Owned_By_Player() || (aircraft->Cloak != CLOAKED)) { + int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -Pixel_To_Lepton(aircraft->Altitude)))); + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = aircraft; + } + } + } + } + + /* + ** Only return the object if it is within 1/4 cell distance from the specified + ** coordinate. + */ + if (object && distance > 0xC0) { + object = 0; + } + return(object); +} + + + + +#ifdef USE_RA_AI + +/* +** Nearby_Location pulled in from RA for AI. ST - 7/24/2019 5:54PM +*/ +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/*********************************************************************************************** + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * * + * This routine is used to find a location that probably will be ok to move to that is * + * located as close as possible to the specified cell. The computer uses this when it has * + * determined the ideal location for an object, but then needs to give a valid movement * + * destination to a unit. * + * * + * INPUT: cell -- The cell that scanning should radiate out from. * + * * + * zone -- The zone that must be matched to find a legal location (value of -1 means * + * any zone will do). * + * * + * * + * check -- The type of zone to check against. Only valid if a zone value is given. * + * * + * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * + * to the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Nearby_Location(CELL cell) const //, SpeedType speed, int zone, MZoneType check) const +{ + CELL topten[10]; + int count = 0; + int xx = Cell_X(cell); + int yy = Cell_Y(cell); + + /* + ** Determine the limits of the scanning in the four directions so that + ** it won't scan past the edge of the world. + */ + int left = MapCellX; + int right = MapCellX + MapCellWidth - 1; + int top = MapCellY; + int bottom = MapCellY + MapCellHeight - 1; + + /* + ** Radiate outward from the specified location, looking for the closest + ** location that is generally clear. + */ + for (int radius = 0; radius < MAP_CELL_W; radius++) { + CELL newcell; + CellClass const * cellptr; + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = xx-radius; x <= xx+radius; x++) { + if (x >= left && x <= right) { + int y = yy-radius; + if (y >= top) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + y = yy+radius; + if (y <= bottom) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count == ARRAY_SIZE(topten)) break; + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = yy-radius; y <= yy+radius; y++) { + if (y >= top && y <= bottom) { + int x = xx-radius; + if (x >= left) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + x = xx+radius; + if (x <= right) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count > 0) break; + } + + if (count > 0) { + return(topten[Frame % count]); + } + return(0); +} + +#endif // USE_RA_AI \ No newline at end of file diff --git a/TIBERIANDAWN/MAP.H b/TIBERIANDAWN/MAP.H new file mode 100644 index 000000000..6af2a7608 --- /dev/null +++ b/TIBERIANDAWN/MAP.H @@ -0,0 +1,174 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\map.h_v 2.19 16 Oct 1995 16:46:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAP_H +#define MAP_H + +#include "gscreen.h" + +#define BIGMAP 0 + + +class MapClass: public GScreenClass +{ + public: + + /* + ** Initialization + */ + virtual void One_Time(void); // Theater-specific inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Alloc_Cells(void); // Allocates buffers + virtual void Free_Cells(void); // Frees buffers + virtual void Init_Cells(void); // Frees buffers + + // Added this function to allow the editor to setup the map without setting up the entire system. - 06/18/2019 JAS + void One_Time_Editor(void); + + /*-------------------------------------------------------- + ** Main functions that deal with groupings of cells within the map or deals with the cell + ** as it relates to the map - not what the cell contains. + */ + ObjectClass * Close_Object(COORDINATE coord) const; + virtual void Detach(ObjectClass * ) {}; + int Cell_Region(CELL cell); + int Cell_Threat(CELL cell, HousesType house); + int Cell_Distance(CELL cell1, CELL cell2); + bool In_Radar(CELL cell) const; + void Sight_From(HouseClass *house, CELL cell, int sightrange, bool incremental=false); // Added house pointer parameter. ST - 3/6/2019 10:23AM + void Place_Down(CELL cell, ObjectClass * object); + void Pick_Up(CELL cell, ObjectClass * object); + void Overlap_Down(CELL cell, ObjectClass * object); + void Overlap_Up(CELL cell, ObjectClass * object); + bool Read_Binary(char const *root, unsigned long *crc); + bool Write_Binary(char const *root); + bool Place_Random_Crate(void); + + //Added for loading custom maps - 2019/10/28 JAS + bool Read_Binary_File(char const *fname, unsigned long *crc); + + long Overpass(void); + + virtual void Logic(void); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Debug routine + */ + int Validate(void); + + /* + ** Catch-all for bad overlappers + */ + void Clean(void); + +#ifdef USE_RA_AI + /* + ** Pulled in from RA for AI. ST - 7/24/2019 5:53PM + */ + CELL Nearby_Location(CELL cell) const; //, SpeedType speed, int zone, MZoneType check) const +#endif //USE_RA_AI + + /* + ** This is the dimensions and position of the sub section of the global map. + ** It is this region that appears on the radar map and constrains normal + ** movement. + */ + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + + /* + ** This is the total value of all harvestable Tiberium on the map. + */ + long TotalValue; + + protected: + + /* + ** These are the size dimensions of the underlying array of cell objects. + ** This is the dimensions of the "map" that the tactical view is + ** restricted to. + */ + int XSize; + int YSize; + int Size; + + static int const RadiusCount[11]; + static int const RadiusOffset[]; + + private: + friend class CellClass; + + /* + ** Tiberium growth potiential cells are recorded here. + */ + CELL TiberiumGrowth[50]; + int TiberiumGrowthCount; + + /* + ** List of cells that are full enough strength that they could spread + ** Tiberium to adjacent cells. + */ + CELL TiberiumSpread[50]; + int TiberiumSpreadCount; + + /* + ** This is the current cell number in the incremental map scan process. + */ + CELL TiberiumScan; + + /* + ** If the Tiberium map scan is processing forward, then this flag + ** will be true. It alternates between forward and backward scanning + ** in order to avoid the "Tiberium Creep". + */ + unsigned IsForwardScan:1; + + enum MapEnum {SCAN_AMOUNT=MAP_CELL_TOTAL}; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/MAPEDDLG.CPP b/TIBERIANDAWN/MAPEDDLG.CPP new file mode 100644 index 000000000..b57de3691 --- /dev/null +++ b/TIBERIANDAWN/MAPEDDLG.CPP @@ -0,0 +1,3847 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapeddlg.cpv 2.18 16 Oct 1995 16:49:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : December 12, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor dialogs & main menu options * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::New_Scenario -- creates a new scenario * + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * MapEditClass::Size_Map -- lets user set size & location of map * + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * MapEditClass::Handle_Triggers -- processes the trigger dialogs * + * MapEditClass::Select_Trigger -- lets user select a trigger * + * MapEditClass::Edit_Trigger -- lets user edit a [new] trigger * + * MapEditClass::Import_Triggers -- lets user import triggers * + * MapEditClass::Import_Teams -- lets user import teams * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::New_Scenario -- creates a new scenario * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Prompts user for map size * + * - Initializes the scenario by calling Clear_Scenario(), which calls * + * everybody's Init() routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = new scenario created, -1 = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::New_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + HousesType house; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("New Scenario", &scen_num, &player, &dir, &var, 1); + if (rc != 0) { + return(-1); + } + + /* + -------------------------- Blow away everything -------------------------- + */ + Clear_Scenario(); + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /* + ----------------------------- Create houses ------------------------------ + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + new HouseClass(house); + } + + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI1); + PlayerPtr->IsHuman = true; + LastHouse = HOUSE_MULTI1; + } else { + if (player == SCEN_PLAYER_GDI) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_GOOD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_BAD; + } else { + if (player == SCEN_PLAYER_NOD) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_BAD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_GOOD; + } else { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + } + LastHouse = HOUSE_GOOD; + } + + /* + -------------------------- Init the entire map --------------------------- + */ + Init_Clear(); + Fill_In_Data(); + + /* + -------------------------- Prompt for map size --------------------------- + */ + Size_Map(-1,-1,20,20); + + /* + ------ Set the Home & Reinforcement Cells to the center of the map ------- + */ + Waypoint[WAYPT_REINF] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + (*this)[Coord_Cell(TacticalCoord)].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + + ScenarioInit++; + Set_Tactical_Position(Cell_Coord(Waypoint[WAYPT_HOME])); + ScenarioInit--; + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Loads the INI file for that scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Load_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("Load Scenario", &scen_num, &player, &dir, &var, 1); + if (rc != 0) { + return(-1); + } + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /*------------------------------------------------------------------------ + Read_Scenario_Ini() must be able to set PlayerPtr to the right house: + - Reading the INI will create the house objects + - PlayerPtr must be set before any Techno objects are created + - For GDI or NOD scenarios, PlayerPtr is set by reading the INI; + but for multiplayer, it's set via the MPlayerLocalID; so, here we have + to set various multiplayer variables to fool the Assign_Houses() routine + into working properly. + ------------------------------------------------------------------------*/ + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + MPlayerLocalID = Build_MPlayerID(2,HOUSE_GOOD); + MPlayerCount = 1; + LastHouse = HOUSE_MULTI1; + } + else if (ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + else { + LastHouse = HOUSE_GOOD; + } + + /* + -------------------------- Blow away everything -------------------------- + */ + Clear_Scenario(); + + /* + ------------------------------ Read the INI ------------------------------ + */ + if (Read_Scenario_Ini(ScenarioName) == 0) { + CCMessageBox().Process("Unable to read scenario!"); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + Fill_In_Data(); + Set_Palette(GamePalette); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Saves the INI file for this scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = error/cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Save_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + FILE *fp; + char fname[13]; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("Save Scenario", &scen_num, &player, &dir, &var, 0); + if (rc != 0) { + return(-1); + } + + /* + ------------------- Warning if scenario already exists ------------------- + */ + Set_Scenario_Name(fname, scen_num, player, dir, var); + fp = fopen(fname,"rb"); + if (fp) { + fclose(fp); + rc = CCMessageBox().Process("File exists. Replace?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==1) + return(-1); + } + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /*------------------------------------------------------------------------ + Player may have changed from GDI to NOD, so change playerptr accordingly + ------------------------------------------------------------------------*/ + if (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer==SCEN_PLAYER_NOD) { + if (ScenPlayer==SCEN_PLAYER_GDI) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_GOOD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_BAD; + } else { + if (ScenPlayer == SCEN_PLAYER_NOD) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_BAD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_GOOD; + } else { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + } + LastHouse = HOUSE_GOOD; + } + + /* + ----------------------------- Write the INI ------------------------------ + */ + Write_Scenario_Ini(ScenarioName); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * * + * Prompts user for: * + * - House (GDI, NOD) * + * - Scenario # * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Caption ³ * + * ³ ³ * + * ³ Scenario ___ ³ * + * ³ Version ___ ³ * + * ³ ³ * + * ³ [East] [West] ³ * + * ³ ³ * + * ³ [ GDI ] ³ * + * ³ [ NOD ] ³ * + * ³ [Multi-Player] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * caption string to use as a title * + * scen_nump output: ptr to scenario # * + * playerp output: ptr to player type * + * dirp output: ptr to direction * + * varp output: ptr to variation * + * multi 1 = allow to change single/multiplayer; 0 = not * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Pick_Scenario(char const * caption, int *scen_nump, + ScenarioPlayerType *playerp, ScenarioDirType *dirp, ScenarioVarType *varp, + int multi) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 400, // dialog width + D_DIALOG_H = 328, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_SCEN_W = 90, // Scenario # width + D_SCEN_H = 18, // Scenario # height + D_SCEN_X = D_DIALOG_CX + 5, // Scenario # x + D_SCEN_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, // Scenario # y + + D_VARA_W = 26, // Version A width + D_VARA_H = 18, // Version A height + D_VARA_X = D_DIALOG_CX - (D_VARA_W * 5) / 2, // Version A x + D_VARA_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version A y + + D_VARB_W = 26, // Version B width + D_VARB_H = 18, // Version B height + D_VARB_X = D_VARA_X + D_VARA_W, // Version B x + D_VARB_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version B y + + D_VARC_W = 26, // Version C width + D_VARC_H = 18, // Version C height + D_VARC_X = D_VARB_X + D_VARB_W, // Version C x + D_VARC_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version C y + + D_VARD_W = 26, // Version D width + D_VARD_H = 18, // Version D height + D_VARD_X = D_VARC_X + D_VARC_W, // Version D x + D_VARD_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version D y + + D_VARLOSE_W = 26, // Version Lose width + D_VARLOSE_H = 18, // Version Lose height + D_VARLOSE_X = D_VARD_X + D_VARD_W, // Version Lose x + D_VARLOSE_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version Lose y + + D_EAST_W = 100, // EAST width + D_EAST_H = 18, // EAST height + D_EAST_X = D_DIALOG_CX - D_EAST_W - 5, // EAST x + D_EAST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_WEST_W = 100, // WEST width + D_WEST_H = 18, // WEST height + D_WEST_X = D_DIALOG_CX + 5, // WEST x + D_WEST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_GDI_W = 140, // GDI width + D_GDI_H = 18, // GDI height + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), // GDI x + D_GDI_Y = D_EAST_Y + D_EAST_H + D_MARGIN, // GDI y + + D_NOD_W = 140, // NOD width + D_NOD_H = 18, // NOD height + D_NOD_X = D_DIALOG_CX - (D_NOD_W / 2), // NOD x + D_NOD_Y = D_GDI_Y + D_GDI_H, // NOD y + + D_NEU_W = 140, // Neutral width + D_NEU_H = 18, // Neutral height + D_NEU_X = D_DIALOG_CX - (D_NOD_W / 2), // Neutral x + D_NEU_Y = D_NOD_Y + D_NOD_H, // Neutral y + + D_MPLAYER_W = 140, // Multi-Player width + D_MPLAYER_H = 18, // Multi-Player height + D_MPLAYER_X = D_DIALOG_CX - (D_MPLAYER_W / 2), // Multi-Player x + D_MPLAYER_Y = D_NEU_Y + D_NEU_H, // Multi-Player y + + D_OK_W = 90, // OK width + D_OK_H = 18, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, // OK y + + D_CANCEL_W = 90, // Cancel width + D_CANCEL_H = 18, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, // Cancel y + + }; + /*........................................................................ + Button enumerations + ........................................................................*/ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_MPLAYER, + BUTTON_EAST, + BUTTON_WEST, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SCENARIO, + BUTTON_VAR_A, + BUTTON_VAR_B, + BUTTON_VAR_C, + BUTTON_VAR_D, + BUTTON_VAR_L, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + bool cancel = false; // true = user cancels + /*........................................................................ + Other Variables + ........................................................................*/ + char scen_buf[10]={0}; // buffer for editing scenario # + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + EditClass editbtn (BUTTON_SCENARIO, + scen_buf, 5, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::NUMERIC); + + TextButtonClass varabtn (BUTTON_VAR_A, "A", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARA_X, D_VARA_Y, D_VARA_W, D_VARA_H); + + TextButtonClass varbbtn (BUTTON_VAR_B, "B", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARB_X, D_VARB_Y, D_VARB_W, D_VARB_H); + + TextButtonClass varcbtn (BUTTON_VAR_C, "C", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARC_X, D_VARC_Y, D_VARC_W, D_VARC_H); + + TextButtonClass vardbtn (BUTTON_VAR_D, "D", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARD_X, D_VARD_Y, D_VARD_W, D_VARD_H); + + TextButtonClass varlbtn (BUTTON_VAR_L, "L", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARLOSE_X, D_VARLOSE_Y, D_VARLOSE_W, D_VARLOSE_H); + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass playermbtn (BUTTON_MPLAYER, "Multi Player", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MPLAYER_X, D_MPLAYER_Y, D_MPLAYER_W, D_MPLAYER_H); + + TextButtonClass eastbtn (BUTTON_EAST, "East", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_EAST_X, D_EAST_Y, D_EAST_W, D_EAST_H); + + TextButtonClass westbtn (BUTTON_WEST, "West", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_WEST_X, D_WEST_Y, D_WEST_W, D_WEST_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + sprintf(scen_buf,"%d",(*scen_nump)); // init edit buffer + editbtn.Set_Text(scen_buf,5); + + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + switch (*varp) { + case SCEN_VAR_A: + varabtn.Turn_On(); + break; + + case SCEN_VAR_B: + varbbtn.Turn_On(); + break; + + case SCEN_VAR_C: + varcbtn.Turn_On(); + break; + + case SCEN_VAR_D: + vardbtn.Turn_On(); + break; + + case SCEN_VAR_LOSE: + varlbtn.Turn_On(); + break; + } + + /* + ......................... Create the button list ......................... + */ + commands = &editbtn; + varabtn.Add_Tail(*commands); + varbbtn.Add_Tail(*commands); + varcbtn.Add_Tail(*commands); + vardbtn.Add_Tail(*commands); + varlbtn.Add_Tail(*commands); + if (multi) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + playermbtn.Add_Tail(*commands); + } else { + if ((*playerp) == SCEN_PLAYER_MPLAYER) { + playermbtn.Add_Tail(*commands); + } else { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + } + } + eastbtn.Add_Tail(*commands); + westbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + /* + ......................... Init the button states ......................... + */ + if ((*playerp) == SCEN_PLAYER_GDI) { + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + playermbtn.Turn_Off(); + } else { + if ((*playerp) == SCEN_PLAYER_NOD) { + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + playermbtn.Turn_Off(); + } else { + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + playermbtn.Turn_On(); + } + } + + if ((*dirp)==SCEN_DIR_EAST) { + eastbtn.Turn_On(); + westbtn.Turn_Off(); + } else { + eastbtn.Turn_Off(); + westbtn.Turn_On(); + } + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(caption, D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Scenario", D_DIALOG_CX - 5, + D_SCEN_Y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_VAR_A | KN_BUTTON): + (*varp) = SCEN_VAR_A; + varabtn.Turn_On(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_B | KN_BUTTON): + (*varp) = SCEN_VAR_B; + varabtn.Turn_Off(); + varbbtn.Turn_On(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_C | KN_BUTTON): + (*varp) = SCEN_VAR_C; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_On(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_D | KN_BUTTON): + (*varp) = SCEN_VAR_D; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_On(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_L | KN_BUTTON): + (*varp) = SCEN_VAR_LOSE; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_On(); + break; + + case (BUTTON_EAST | KN_BUTTON): + (*dirp) = SCEN_DIR_EAST; + eastbtn.Turn_On(); + westbtn.Turn_Off(); + break; + + case (BUTTON_WEST | KN_BUTTON): + (*dirp) = SCEN_DIR_WEST; + eastbtn.Turn_Off(); + westbtn.Turn_On(); + break; + + case (BUTTON_GDI | KN_BUTTON): + (*playerp) = SCEN_PLAYER_GDI; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + playermbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + (*playerp) = SCEN_PLAYER_NOD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + playermbtn.Turn_Off(); + break; + + case (BUTTON_MPLAYER | KN_BUTTON): + (*playerp) = SCEN_PLAYER_MPLAYER; + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + playermbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case (BUTTON_SCENARIO | KN_BUTTON): + break; + + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) + return(-1); + + /* + ------------------------ Save selections & return ------------------------ + */ + (*scen_nump) = atoi(scen_buf); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Size_Map -- lets user set size & location of map * + * * + * Lets the user select a side of the map and expand/shrink it to the * + * desired size, or move the whole map around the available map area. * + * * + * The entire available map area is displayed, but the map is limited such * + * that there's always one blank cell around the map; this lets objects * + * properly exit the screen, since they have a blank undisplayed cell to * + * exit onto. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Clear Terrain ³ * + * ³ ³ ³ Water ³ * + * ³ ³ ³ Tiberium ³ * + * ³ ³ ³ Rock/Wall/Road ³ * + * ³ ³ (Map Area) ³ GDI Unit ³ * + * ³ ³ ³ NOD Unit ³ * + * ³ ³ ³ Neutral Unit ³ * + * ³ ³ ³ Terrain Object ³ * + * ³ ³ ³ Starting Cell ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ X Y Width Height ³ * + * ³ ## ## ## ## ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * x,y,w,h: initial size parameters (-1 = center the thing) * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Size_Map(int x, int y, int w, int h) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, // dialog width + D_DIALOG_H = 280, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_BORD_X1 = D_DIALOG_X + (D_DIALOG_W / 2 - MAP_CELL_W) / 2, + D_BORD_Y1 = D_DIALOG_Y + 10, + D_BORD_X2 = D_BORD_X1 + MAP_CELL_W + 1, + D_BORD_Y2 = D_BORD_Y1 + MAP_CELL_H + 1, + + D_OK_W = 90, // OK width + D_OK_H = 18, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, // OK y + + D_CANCEL_W = 90, // Cancel width + D_CANCEL_H = 18, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, // Cancel y + + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_OK=100, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MAP, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + bool process; // Loop while true + RedrawType display; // requested redraw level + bool cancel = false; // true = user cancels + KeyNumType input; // user input + int grabbed = 0; // 1=TLeft,2=TRight,3=BRight,4=BLeft + int map_x1; // map coords x1, pixel coords + int map_x2; // map coords x2, pixel coords + int map_y1; // map coords y1, pixel coords + int map_y2; // map coords y2, pixel coords + int delta1, delta2; // mouse-click proximity + int mx,my; // last-saved mouse coords + char txt[40]; + int txt_x,txt_y; // for displaying text + unsigned index; // for drawing map symbology + CELL cell; // for drawing map symbology + int color; // for drawing map symbology + ObjectClass * occupier; // cell's occupier + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /*........................................................................ + Set up the actual map area relative to the map's border coords + ........................................................................*/ + if (x==-1) { + map_x1 = D_BORD_X1 + (MAP_CELL_W - w) / 2 + 1; + } else { + map_x1 = D_BORD_X1 + x + 1; + } + + if (y==-1) { + map_y1 = D_BORD_Y1 + (MAP_CELL_H - h) / 2 + 1; + } else { + map_y1 = D_BORD_Y1 + y + 1; + } + + map_x2 = map_x1 + w - 1; + map_y2 = map_y1 + h - 1; + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /*------------------------------------------------------------------------ + Main processing loop + ------------------------------------------------------------------------*/ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ------------------------ Invoke game callback ------------------------- + */ + Call_Back(); + + /* + ---------------------- Refresh display if needed ---------------------- + */ + if (display) { + Hide_Mouse(); + /*------------------------------------------------------------------ + Redraw the background, map border, key, and coord labels + ------------------------------------------------------------------*/ + if (display >= REDRAW_BACKGROUND) { + /* + .......................... Background ........................... + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ..................... Draw the map border ....................... + */ + LogicPage->Lock(); + LogicPage->Draw_Rect(D_BORD_X1, D_BORD_Y1, D_BORD_X2, D_BORD_Y2, CC_GREEN_SHADOW); + for (index = D_BORD_X1; index < D_BORD_X2; + index += (320/ICON_PIXEL_W)) { + LogicPage->Put_Pixel(index, D_BORD_Y1-1, CC_GREEN_SHADOW); + LogicPage->Put_Pixel(index, D_BORD_Y2+1, CC_GREEN_SHADOW); + } + for (index = D_BORD_Y1; index < D_BORD_Y2-8; + index += (200/ICON_PIXEL_H)) { + LogicPage->Put_Pixel(D_BORD_X1-1, index, CC_GREEN_SHADOW); + LogicPage->Put_Pixel(D_BORD_X2+1, index, CC_GREEN_SHADOW); + } + + /*............................................................... + Draw the map "key" + ...............................................................*/ + txt_x = D_DIALOG_CX; + txt_y = D_DIALOG_Y + 8; + Fancy_Text_Print("Clear Terrain", txt_x, txt_y, LTGREY, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Water", txt_x, txt_y, BLUE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Tiberium", txt_x, txt_y, GREY, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Rock/Wall/Road", txt_x, txt_y, BROWN, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("GDI Unit", txt_x, txt_y, YELLOW, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Nod Unit", txt_x, txt_y, RED, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Neutral Unit", txt_x, txt_y, PURPLE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Terrain Object", txt_x, txt_y, DKGREEN, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Starting Cell", txt_x, txt_y, WHITE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + /* + .................. Draw the coordinate labels ................... + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 33; + Fancy_Text_Print("X", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Y", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Width", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Height", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + LogicPage->Unlock(); + + /* + ...................... Redraw the buttons ....................... + */ + commands->Flag_List_To_Redraw(); + + } + + /*------------------------------------------------------------------ + Redraw the map symbology & location + ------------------------------------------------------------------*/ + if (display >= REDRAW_MAP) { + + LogicPage->Lock(); + + /* + .................... Erase the map interior ..................... + */ + LogicPage->Fill_Rect(D_BORD_X1 + 1, D_BORD_Y1 + 1, D_BORD_X2 - 1, D_BORD_Y2 - 1, BLACK); + + /*............................................................... + Draw Land map symbols (use color according to Ground[] array). + ...............................................................*/ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier == NULL) { + color = Ground[(*this)[cell].Land_Type()].Color; + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + LogicPage->Unlock(); + + /* + ................. Draw the actual map location .................. + */ + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, WHITE); + switch (grabbed) { + case 1: + LogicPage->Draw_Line(map_x1, map_y1, map_x1 + 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x1, map_y1, map_x1, map_y1 + 5, BLUE); + break; + + case 2: + LogicPage->Draw_Line(map_x2, map_y1, map_x2 - 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x2, map_y1, map_x2, map_y1 + 5, BLUE); + break; + + case 3: + LogicPage->Draw_Line(map_x2, map_y2, map_x2 - 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x2, map_y2, map_x2, map_y2 - 5, BLUE); + break; + + case 4: + LogicPage->Draw_Line(map_x1, map_y2, map_x1 + 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x1, map_y2, map_x1, map_y2 - 5, BLUE); + break; + + case 5: + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, BLUE); + break; + + default: + break; + } + + /*............................................................... + Draw Unit map symbols (Use the radar map color according to + that specified in the house type class object. + DKGREEN = terrain object + ...............................................................*/ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier) { + color = DKGREEN; + if (occupier && occupier->Owner() != HOUSE_NONE) { + color = HouseClass::As_Pointer(occupier->Owner())->Class->Color; + } + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ...................... Draw Home location ....................... + */ + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(Waypoint[WAYPT_HOME]) + 1, D_BORD_Y1 + Cell_Y(Waypoint[WAYPT_HOME]) + 1, WHITE); + + /* + ..................... Erase old coordinates ..................... + */ + LogicPage->Fill_Rect( D_DIALOG_X + 7, + D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22, + D_DIALOG_X + D_DIALOG_W - 7, + D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22 + 10, BLACK); + + /* + ..................... Draw the coordinates ...................... + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22; + sprintf(txt,"%d",map_x1 - D_BORD_X1 - 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_y1 - D_BORD_Y1 - 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_x2 - map_x1 + 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_y2 - map_y1 + 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ------------------------- Process user input -------------------------- + */ + input = commands->Input(); + /*..................................................................... + Normal button processing: This is done when the mouse button is NOT + being held down ('grabbed' is 0). + .....................................................................*/ + if (grabbed == 0) { + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case KN_LMOUSE: + /* + ....................... Grab top left ........................ + */ + delta1 = abs(_Kbd->MouseQX - map_x1); + delta2 = abs(_Kbd->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 1; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ...................... Grab top right ........................ + */ + delta1 = abs(_Kbd->MouseQX - map_x2); + delta2 = abs(_Kbd->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 2; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab bottom right ...................... + */ + delta1 = abs(_Kbd->MouseQX - map_x2); + delta2 = abs(_Kbd->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 3; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab bottom left ....................... + */ + delta1 = abs(_Kbd->MouseQX - map_x1); + delta2 = abs(_Kbd->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 4; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab the whole map ..................... + */ + delta1 = abs(_Kbd->MouseQX - ((map_x1 + map_x2) / 2)); + delta2 = abs(_Kbd->MouseQY - ((map_y1 + map_y2) / 2)); + if (delta1 < (map_x2 - map_x1) / 4 && + delta2 < (map_y2 - map_y1) / 4) { + grabbed = 5; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + } + break; + + default: + break; + } + } else { + /*..................................................................... + Mouse motion processing: This is done while the left mouse button IS + being held down. + - First, check for the button release; if detected, un-grab + - Then, handle mouse motion. WWLIB doesn't pass through a KN_MOUSE_MOVE + value while the button is being held down, so this case must be + trapped as a default. + .....................................................................*/ + switch (input) { + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + grabbed = 0; + display = REDRAW_MAP; + break; + + default: + delta1 = Get_Mouse_X() - mx; + delta2 = Get_Mouse_Y() - my; + if (delta1==0 && delta2==0) { + break; + } + + /* + ....................... Move top left ........................ + */ + if (grabbed==1) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ....................... Move top right ....................... + */ + if (grabbed==2) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ..................... Move bottom right ...................... + */ + if (grabbed==3) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ...................... Move bottom left ...................... + */ + if (grabbed==4) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ....................... Move whole map ....................... + */ + if (grabbed==5) { + if (map_x1 + delta1 > D_BORD_X1 + 1 && map_x2 + delta1 < D_BORD_X2 - 1) { + map_x1 += delta1; + map_x2 += delta1; + } + + if (map_y1 + delta2 > D_BORD_Y1 + 1 && map_y2 + delta2 < D_BORD_Y2 - 1) { + map_y1 += delta2; + map_y2 += delta2; + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + break; + } + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ---------------------------- Save selections ----------------------------- + */ + MapCellX = map_x1 - D_BORD_X1 - 1; + MapCellY = map_y1 - D_BORD_Y1 - 1; + MapCellWidth = map_x2 - map_x1 + 1; + MapCellHeight = map_y2 - map_y1 + 1; + + /* + --------------------- Clip Home Cell to new map size --------------------- + */ + if (Cell_X(Waypoint[WAYPT_HOME]) < MapCellX) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, Cell_Y(Waypoint[WAYPT_HOME])); + } + + if (Cell_X(Waypoint[WAYPT_HOME]) > MapCellX + MapCellWidth - 1) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth - 1, Cell_Y(Waypoint[WAYPT_HOME])); + } + + if (Cell_Y(Waypoint[WAYPT_HOME]) < MapCellY) { + Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Waypoint[WAYPT_HOME]), MapCellY); + } + + if (Cell_Y(Waypoint[WAYPT_HOME]) > MapCellY + MapCellHeight - 1) { + Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Waypoint[WAYPT_HOME]), MapCellY + MapCellHeight - 1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * * + * Lets the user edit the Theater, starting credits for houses, and the * + * Edge for HOUSE_GOOD & HOUSE_BAD. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Theater Credits / 1000 ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ Temperate ³ GDI: _____ ³ * + * ³ ³ Desert ³ NOD: _____ ³ * + * ³ ³ Jungle ³ Neutral: _____ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ Build Level:___ ³ * + * ³ ³ * + * ³ Reinforcements ³ * + * ³ ³ * + * ³ GDI NOD ³ * + * ³ ³ * + * ³   ³ * + * ³ <- -> <- -> ³ * + * ³   ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Scenario_Dialog(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 544, + D_DIALOG_H = 320, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_THEATER_W = 200, + D_THEATER_H = 68, + D_THEATER_X = D_DIALOG_X + D_MARGIN, + D_THEATER_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_LEVEL_W = 80, + D_LEVEL_H = 18, + D_LEVEL_X = D_THEATER_X + D_THEATER_W - D_LEVEL_W, + D_LEVEL_Y = D_THEATER_Y + D_THEATER_H + D_MARGIN, + + D_GDICRED_W = 120, + D_GDICRED_H = 18, + D_GDICRED_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_GDICRED_W, + D_GDICRED_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_NODCRED_W = 120, + D_NODCRED_H = 18, + D_NODCRED_X = D_GDICRED_X, + D_NODCRED_Y = D_GDICRED_Y + D_GDICRED_H, + + D_NEUTCRED_W = 120, + D_NEUTCRED_H = 18, + D_NEUTCRED_X = D_GDICRED_X, + D_NEUTCRED_Y = D_NODCRED_Y + D_NODCRED_H, + + D_GDIN_W = 26, + D_GDIN_H = 18, + D_GDIN_X = D_DIALOG_CX - 5 - D_GDIN_W * 2, + D_GDIN_Y = D_LEVEL_Y + D_LEVEL_H + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_GDIS_W = 26, + D_GDIS_H = 18, + D_GDIS_X = D_GDIN_X, + D_GDIS_Y = D_GDIN_Y + D_GDIN_H * 2, + + D_GDIW_W = 26, + D_GDIW_H = 18, + D_GDIW_X = D_DIALOG_CX - 5 - D_GDIN_W * 3, + D_GDIW_Y = D_GDIN_Y + D_GDIN_H, + + D_GDIE_W = 26, + D_GDIE_H = 18, + D_GDIE_X = D_DIALOG_CX - 5 - D_GDIN_W, + D_GDIE_Y = D_GDIN_Y + D_GDIN_H, + + D_NODN_W = 26, + D_NODN_H = 18, + D_NODN_X = D_DIALOG_CX + 5 + D_NODN_W, + D_NODN_Y = D_LEVEL_Y + D_LEVEL_H + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_NODS_W = 26, + D_NODS_H = 18, + D_NODS_X = D_NODN_X, + D_NODS_Y = D_NODN_Y + D_NODN_H * 2, + + D_NODW_W = 26, + D_NODW_H = 18, + D_NODW_X = D_DIALOG_CX + 5, + D_NODW_Y = D_NODN_Y + D_NODN_H, + + D_NODE_W = 26, + D_NODE_H = 18, + D_NODE_X = D_DIALOG_CX + 5 + D_NODN_W * 2, + D_NODE_Y = D_NODN_Y + D_NODN_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + LIST_THEATER=100, + TEDIT_LEVEL, + TEDIT_GDICRED, + TEDIT_NODCRED, + TEDIT_NEUTCRED, + BUTTON_GDI_N, + BUTTON_GDI_E, + BUTTON_GDI_S, + BUTTON_GDI_W, + BUTTON_NOD_N, + BUTTON_NOD_E, + BUTTON_NOD_S, + BUTTON_NOD_W, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + bool cancel = false; // true = user cancels + /* + .......................... Scenario parameters ........................... + */ + TheaterType theater; // DisplayClass::Theater + TheaterType orig_theater; // original theater + long gdi_credits; // HouseClass::As_Pointer(HouseType)->Credits + long nod_credits; // HouseClass::As_Pointer(HouseType)->Credits + long neut_credits; // HouseClass::As_Pointer(HouseType)->Credits + SourceType gdi_edge; // HouseClass::As_Pointer(HouseType)->Edge + SourceType nod_edge; // HouseClass::As_Pointer(HouseType)->Edge + char level_buf[10] = {0}; + char gdicred_buf[10] = {0}; + char nodcred_buf[10] = {0}; + char neutcred_buf[10] = {0}; + /* + ....................... Theater-changing variables ....................... + */ + unsigned char theater_mask; // template/terrain mask + TerrainClass * terrain; // cell's terrain pointer + CELL i; // loop counter + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + ListClass theaterbtn (LIST_THEATER, + D_THEATER_X, D_THEATER_Y, D_THEATER_W, D_THEATER_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + EditClass leveledt (TEDIT_GDICRED, level_buf, 4, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEVEL_X, D_LEVEL_Y, D_LEVEL_W, D_LEVEL_H, EditClass::NUMERIC); + + EditClass gdicred (TEDIT_GDICRED, gdicred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDICRED_X, D_GDICRED_Y, D_GDICRED_W, D_GDICRED_H, EditClass::NUMERIC); + + EditClass nodcred (TEDIT_NODCRED, nodcred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODCRED_X, D_NODCRED_Y, D_NODCRED_W, D_NODCRED_H, EditClass::NUMERIC); + + EditClass neutcred (TEDIT_NEUTCRED, neutcred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEUTCRED_X, D_NEUTCRED_Y, D_NEUTCRED_W, D_NEUTCRED_H, EditClass::NUMERIC); + + TextButtonClass gdinbtn (BUTTON_GDI_N, TXT_UP, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIN_X, D_GDIN_Y, D_GDIN_W, D_GDIN_H); + + TextButtonClass gdiebtn (BUTTON_GDI_E, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIE_X, D_GDIE_Y, D_GDIE_W, D_GDIE_H); + + TextButtonClass gdisbtn (BUTTON_GDI_S, TXT_DOWN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIS_X, D_GDIS_Y, D_GDIS_W, D_GDIS_H); + + TextButtonClass gdiwbtn (BUTTON_GDI_W, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIW_X, D_GDIW_Y, D_GDIW_W, D_GDIW_H); + + TextButtonClass nodnbtn (BUTTON_NOD_N, TXT_UP, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODN_X, D_NODN_Y, D_NODN_W, D_NODN_H); + + TextButtonClass nodebtn (BUTTON_NOD_E, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODE_X, D_NODE_Y, D_NODE_W, D_NODE_H); + + TextButtonClass nodsbtn (BUTTON_NOD_S, TXT_DOWN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODS_X, D_NODS_Y, D_NODS_W, D_NODS_H); + + TextButtonClass nodwbtn (BUTTON_NOD_W, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODW_X, D_NODW_Y, D_NODW_W, D_NODW_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + .......................... Fill in theater items ......................... + */ + theaterbtn.Add_Item("Desert"); + theaterbtn.Add_Item("Jungle"); + theaterbtn.Add_Item("Temperate"); + theaterbtn.Add_Item("Winter"); + + /* + ............................ Init parameters ............................. + */ + orig_theater = theater = Theater; + if (ScenPlayer != SCEN_PLAYER_MPLAYER) { + gdi_credits = HouseClass::As_Pointer(HOUSE_GOOD)->Credits / 1000L; + nod_credits = HouseClass::As_Pointer(HOUSE_BAD)->Credits / 1000L; + neut_credits = HouseClass::As_Pointer(HOUSE_NEUTRAL)->Credits / 1000L; + gdi_edge = HouseClass::As_Pointer(HOUSE_GOOD)->Edge; + nod_edge = HouseClass::As_Pointer(HOUSE_BAD)->Edge; + } else { + gdi_credits = 0; + nod_credits = 0; + neut_credits = 0; + gdi_edge = SOURCE_NONE; + nod_edge = SOURCE_NONE; + } + + /* + ............................ Create the list ............................. + */ + commands = &theaterbtn; + leveledt.Add_Tail(*commands); + gdicred.Add_Tail(*commands); + nodcred.Add_Tail(*commands); + neutcred.Add_Tail(*commands); + gdinbtn.Add_Tail(*commands); + gdiebtn.Add_Tail(*commands); + gdisbtn.Add_Tail(*commands); + gdiwbtn.Add_Tail(*commands); + nodnbtn.Add_Tail(*commands); + nodebtn.Add_Tail(*commands); + nodsbtn.Add_Tail(*commands); + nodwbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ...................... Init GDI Edge button states ....................... + */ + if (gdi_edge==SOURCE_NORTH) gdinbtn.Turn_On(); + if (gdi_edge==SOURCE_EAST) gdiebtn.Turn_On(); + if (gdi_edge==SOURCE_SOUTH) gdisbtn.Turn_On(); + if (gdi_edge==SOURCE_WEST) gdiwbtn.Turn_On(); + + /* + ...................... Init NOD Edge button states ....................... + */ + if (nod_edge==SOURCE_NORTH) nodnbtn.Turn_On(); + if (nod_edge==SOURCE_EAST) nodebtn.Turn_On(); + if (nod_edge==SOURCE_SOUTH) nodsbtn.Turn_On(); + if (nod_edge==SOURCE_WEST) nodwbtn.Turn_On(); + + /* + .......................... Init credits buffers .......................... + */ + sprintf(level_buf,"%ld",BuildLevel); + leveledt.Set_Text(level_buf,4); + + sprintf(gdicred_buf,"%ld",gdi_credits); + gdicred.Set_Text(gdicred_buf,8); + + sprintf(nodcred_buf,"%ld",nod_credits); + nodcred.Set_Text(nodcred_buf,8); + + sprintf(neutcred_buf,"%ld",neut_credits); + neutcred.Set_Text(neutcred_buf,8); + + theaterbtn.Set_Selected_Index(orig_theater - THEATER_NONE - 1); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ..................... Draw the background ....................... + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the labels ......................... + */ + Fancy_Text_Print("Theater", D_THEATER_X + D_THEATER_W / 2, + D_THEATER_Y - D_TXT8_H, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Build Level", D_LEVEL_X, D_LEVEL_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Credits/1000", D_GDICRED_X + D_GDICRED_W / 2, + D_GDICRED_Y - D_TXT8_H, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("GDI", D_GDICRED_X - 5, D_GDICRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("NOD", D_NODCRED_X - 5, D_NODCRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("Neutral", D_NEUTCRED_X - 5, D_NEUTCRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("Reinforcements", D_DIALOG_CX, + D_LEVEL_Y + D_LEVEL_H + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("GDI", D_GDIN_X + D_GDIN_W / 2, + D_GDIN_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("NOD", D_NODN_X + D_NODN_W / 2, + D_NODN_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + + /*.................................................................. + Credit edit boxes: no need for any action + ..................................................................*/ + case (TEDIT_GDICRED | KN_BUTTON): + break; + + case (TEDIT_NODCRED | KN_BUTTON): + break; + + case (TEDIT_NEUTCRED | KN_BUTTON): + break; + + /*.................................................................. + GDI Edge buttons: turn this one on, others off, save the edge value + ..................................................................*/ + case (BUTTON_GDI_N | KN_BUTTON): + gdi_edge = SOURCE_NORTH; + gdinbtn.Turn_On(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_E | KN_BUTTON): + gdi_edge = SOURCE_EAST; + gdinbtn.Turn_Off(); + gdiebtn.Turn_On(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_S | KN_BUTTON): + gdi_edge = SOURCE_SOUTH; + gdinbtn.Turn_Off(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_On(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_W | KN_BUTTON): + gdi_edge = SOURCE_WEST; + gdinbtn.Turn_Off(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_On(); + break; + + /*.................................................................. + NOD Edge buttons: turn this one on, others off, save the edge value + ..................................................................*/ + case (BUTTON_NOD_N | KN_BUTTON): + nod_edge = SOURCE_NORTH; + nodnbtn.Turn_On(); + nodebtn.Turn_Off(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_E | KN_BUTTON): + nod_edge = SOURCE_EAST; + nodnbtn.Turn_Off(); + nodebtn.Turn_On(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_S | KN_BUTTON): + nod_edge = SOURCE_SOUTH; + nodnbtn.Turn_Off(); + nodebtn.Turn_Off(); + nodsbtn.Turn_On(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_W | KN_BUTTON): + nod_edge = SOURCE_WEST; + nodnbtn.Turn_Off(); + nodebtn.Turn_Off(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + /* + ----------------------------- Redraw the map ----------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ------------------------ Save selections & return ------------------------ + */ + if (ScenPlayer != SCEN_PLAYER_MPLAYER) { + /* + .............................. Credits ................................ + */ + gdi_credits = atol(gdicred_buf); + nod_credits = atol(nodcred_buf); + neut_credits = atol(neutcred_buf); + HouseClass::As_Pointer(HOUSE_GOOD)->Credits = gdi_credits * 1000L; + HouseClass::As_Pointer(HOUSE_BAD)->Credits = nod_credits * 1000L; + HouseClass::As_Pointer(HOUSE_NEUTRAL)->Credits = neut_credits * 1000L; + /* + ............................... Edges ................................. + */ + HouseClass::As_Pointer(HOUSE_GOOD)->Edge = gdi_edge; + HouseClass::As_Pointer(HOUSE_BAD)->Edge = nod_edge; + } + + /* + ........................... Sidebar build level .......................... + */ + BuildLevel = atoi(level_buf); + + /*........................................................................ + Change the theater: + - 1st set the Theater global + - scan all cells to check their TType for compatibility with the new + theater; if not compatible, set TType to TEMPLATE_NONE & TIcon to 0 + - Then, re-initialize the TypeClasses for the new Theater + ........................................................................*/ + theater = (TheaterType)(THEATER_NONE + 1 + theaterbtn.Current_Index()); + if (theater != orig_theater) { + /* + ....................... Loop through all cells ........................ + */ + for (i =0;iClass->Theater; + if ( (theater_mask & (1<Remove(); + CurTrigger = NULL; + Changed = 1; + } + } + } + + /*------------------------------------------------------------------------ + Don't allow trigger placement if the trigger is house-specific; such + triggers cannot be "placed". + ------------------------------------------------------------------------*/ + if (CurTrigger) { + if (!TriggerClass::Event_Need_Object(CurTrigger->Event)) { + CurTrigger = NULL; + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Trigger -- lets user select a trigger * + * * + * CurTrigger can be NULL when this function is called. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name Event Action House Team ³³ ³ * + * ³ ³ Name Event Action House Team ÃÄ´ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Trigger(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 640, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 612, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_EDIT_W = 90, + D_EDIT_H = 18, + D_EDIT_X = D_DIALOG_X + (D_DIALOG_W / 8) - (D_EDIT_W / 2), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_EDIT_H, + + D_NEW_W = 90, + D_NEW_H = 18, + D_NEW_X = D_DIALOG_X + (D_DIALOG_W / 8) * 3 - (D_NEW_W / 2), + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_NEW_H, + + D_DELETE_W = 90, + D_DELETE_H = 18, + D_DELETE_X = D_DIALOG_X + (D_DIALOG_W / 8) * 5 - (D_DELETE_W / 2), + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_DELETE_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 8) * 7 - (D_OK_W / 2), + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TRIGGER_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + char *trigtext[TRIGGER_MAX + 1]; // text for defined triggers + KeyNumType input; // user input + bool edit_trig = false; // true = user wants to edit + bool new_trig = false; // true = user wants to new + bool del_trig = false; // true = user wants to new + int i; // loop counter + int def_idx; // default list index + static int tabs[] = {70, 240, 390, 440}; // list box tab stops + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + ListClass triggerlist (TRIGGER_LIST, D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + + TextButtonClass newbtn (BUTTON_NEW, "New", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ......................... Fill in trigger names .......................... + */ + def_idx = 0; + for (i = 0; i < Triggers.Count(); i++) { + /*..................................................................... + Generate string for this trigger + - Name can be up to 4 characters + - Event can be up to 15 characters + - Action can be up to 15 characters + - House is 3 characters + - Team name is up to 11 characters + .....................................................................*/ + //trigtext[i] = (char *)HidPage.Get_Graphic_Buffer()->Get_Buffer() + 60 * i; + trigtext[i] = new char[255]; + sprintf(trigtext[i],"%s\t%s\t%s\t", + Triggers.Ptr(i)->Get_Name(), + TriggerClass::Name_From_Event(Triggers.Ptr(i)->Event), + TriggerClass::Name_From_Action(Triggers.Ptr(i)->Action)); + + /* + ......................... Add on the house ID ......................... + */ + if (TriggerClass::Event_Need_House(Triggers.Ptr(i)->Event)) { + if (Triggers.Ptr(i)->House != HOUSE_NONE) { + strcat(trigtext[i], HouseTypeClass::As_Reference(Triggers.Ptr(i)->House).Suffix); + } else { + strcat(trigtext[i], "!!!"); + } + } else { + strcat(trigtext[i]," "); + } + + /* + .......................... Add the team name .......................... + */ + strcat(trigtext[i],"\t"); + if (TriggerClass::Action_Need_Team(Triggers.Ptr(i)->Action)) { + if (Triggers.Ptr(i)->Team) { + strcat(trigtext[i],Triggers.Ptr(i)->Team->IniName); + } else { + strcat(trigtext[i], "!!!"); + } + } + + /* + ................. Set def_idx if this is CurTrigger ................... + */ + if (Triggers.Ptr(i) == CurTrigger) { + def_idx = i; + } + } + + /* + .......................... Fill in the list box .......................... + */ + for (i = 0; i < Triggers.Count(); i++) { + triggerlist.Add_Item(trigtext[i]); + } + triggerlist.Set_Selected_Index(def_idx); + + /* + ....................... Set CurTrigger if it isn't ....................... + */ + if (Triggers.Count()==0) { + CurTrigger = NULL; + } else { + if (!CurTrigger) { + CurTrigger = Triggers.Ptr(def_idx); + } + } + + /* + ............................ Create the list ............................. + */ + commands = &triggerlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + triggerlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Triggers", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + def_idx = triggerlist.Current_Index(); + if (def_idx < Triggers.Count()) { + CurTrigger = Triggers.Ptr(def_idx); + } + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTrigger) { // only allow if there's one selected + process = false; + edit_trig = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_trig = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_trig = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + for (i = 0; i < Triggers.Count(); i++) { + delete [] trigtext[i]; + } + + if (edit_trig) return(1); + if (new_trig) return(2); + if (del_trig) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Edit_Trigger -- lets user edit a [new] trigger * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Trigger Editor ³ * + * ³ ³ * + * ³ Events Actions ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ Name: _______ [ Volatile ] ³ * + * ³ [GDI] [ Persistent ] ³ * + * ³ Time / Credits: _______ [NOD] [SemiPersistent] ³ * + * ³ ³ * + * ³ [Team] Team_Name ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTrigger must NOT be NULL when this function is called. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Edit_Trigger(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, + D_DIALOG_H = 376, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_EVENT_W = 240, + D_EVENT_H = 88, + D_EVENT_X = D_DIALOG_X + D_MARGIN, + D_EVENT_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_ACTION_W = 240, + D_ACTION_H = 88, + D_ACTION_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_ACTION_W, + D_ACTION_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_NAME_W = 80, + D_NAME_H = 18, + D_NAME_X = D_EVENT_X + (D_EVENT_W / 2) - 10, + D_NAME_Y = D_EVENT_Y + D_EVENT_H + D_MARGIN, + + D_DATA_W = 80, + D_DATA_H = 18, + D_DATA_X = D_NAME_X, + D_DATA_Y = D_NAME_Y + D_NAME_H + D_MARGIN, + + D_TEAM_W = 80, + D_TEAM_H = 18, + D_TEAM_X = D_NAME_X - D_TEAM_W - 5, + D_TEAM_Y = D_DATA_Y + D_DATA_H + D_MARGIN, + + D_GDI_W = 90, + D_GDI_H = 18, + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), + D_GDI_Y = D_NAME_Y, + + D_NOD_W = 90, + D_NOD_H = 18, + D_NOD_X = D_GDI_X, + D_NOD_Y = D_GDI_Y + D_GDI_H, + + D_NEU_W = 90, + D_NEU_H = 18, + D_NEU_X = D_NOD_X, + D_NEU_Y = D_NOD_Y + D_NOD_H, + + D_MULTI1_W = 44, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 44, + D_MULTI2_H = 18, + D_MULTI2_X = D_GDI_X + D_MULTI1_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 44, + D_MULTI3_H = 18, + D_MULTI3_X = D_NOD_X, + D_MULTI3_Y = D_NOD_Y, + + D_MULTI4_W = 44, + D_MULTI4_H = 18, + D_MULTI4_X = D_NOD_X + D_MULTI1_W, + D_MULTI4_Y = D_NOD_Y, + + D_VOLATILE_W = 100, + D_VOLATILE_H = 18, + D_VOLATILE_X = D_ACTION_X + (D_ACTION_W / 2) - (D_VOLATILE_W / 2) + 10, + D_VOLATILE_Y = D_NAME_Y, + + D_PERSIST_W = 100, + D_PERSIST_H = 18, + D_PERSIST_X = D_ACTION_X + (D_ACTION_W / 2) - (D_PERSIST_W / 2) + 10, + D_PERSIST_Y = D_VOLATILE_Y + D_VOLATILE_H, + + D_SEMIPERSIST_W = 100, + D_SEMIPERSIST_H = 18, + D_SEMIPERSIST_X = D_ACTION_X + (D_ACTION_W / 2) - (D_SEMIPERSIST_W / 2) + 10, + D_SEMIPERSIST_Y = D_PERSIST_Y + D_PERSIST_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - 5 - D_OK_W, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_CANCEL_H, + + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + EVENT_LIST=100, + ACTION_LIST, + NAME_EDIT, + DATA_EDIT, + BUTTON_TEAM, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_MULTI5, + BUTTON_MULTI6, + BUTTON_VOLATILE, + BUTTON_PERSIST, + BUTTON_SEMIPERSIST, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; // true = user cancels + int i; // loop counter + EventType event_idx; // index for event list + TriggerClass::ActionType action_idx; // index for action list + char namebuf[5]; // name of this trigger + char databuf[10]; // for credit/time-based triggers + HousesType house; // house for this trigger + const char *eventnames[EVENT_COUNT + 1]; // names of events + const char *actionnames[TriggerClass::ACTION_COUNT + 1]; // names of actions + TriggerClass::PersistantType persistant; // trigger's persistence level + + /*........................................................................ + These flags enable various controls for each EventType. + ........................................................................*/ +// static char data_enabled[EVENT_COUNT] = {0,0,0,0,0,0,0,0,0,1,1,1,1,0,0}; +// static char house_enabled[EVENT_COUNT] = {1,0,0,0,0,1,1,1,1,1,1,1,1,1,1}; +// static char team_enabled[TriggerClass::ACTION_COUNT] = {0,0,0,1,1,0,1,0,0,0,0,0,0,0}; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + ListClass eventlist(EVENT_LIST, + D_EVENT_X, D_EVENT_Y, D_EVENT_W, D_EVENT_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + ListClass actionlist(ACTION_LIST, + D_ACTION_X, D_ACTION_Y, D_ACTION_W, D_ACTION_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + EditClass name_edt(NAME_EDIT, namebuf, 5, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, D_NAME_W, D_NAME_H, EditClass::ALPHANUMERIC); + + EditClass data_edt(DATA_EDIT, databuf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DATA_X, D_DATA_Y, D_DATA_W, D_DATA_H, EditClass::ALPHANUMERIC); + + TextButtonClass teambtn(BUTTON_TEAM, "Team", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TEAM_X, D_TEAM_Y, D_TEAM_W, D_TEAM_H); + + TextButtonClass gdibtn(BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn(BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neutralbtn(BUTTON_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + + TextButtonClass multi1btn(BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn(BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn(BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn(BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass volatilebtn(BUTTON_VOLATILE, "Volatile", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VOLATILE_X, D_VOLATILE_Y, D_VOLATILE_W, D_VOLATILE_H); + + TextButtonClass persistbtn(BUTTON_PERSIST, "Persistant", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PERSIST_X, D_PERSIST_Y, D_PERSIST_W, D_PERSIST_H); + + TextButtonClass semipersistbtn(BUTTON_SEMIPERSIST, "SemiPersistant", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SEMIPERSIST_X, D_SEMIPERSIST_Y, D_SEMIPERSIST_W, D_SEMIPERSIST_H); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ....................... Set default button states ........................ + */ + event_idx = CurTrigger->Event; // event list + if (event_idx == EVENT_NONE) event_idx = EVENT_FIRST; + + action_idx = CurTrigger->Action; // action list + if (action_idx == TriggerClass::ACTION_NONE) action_idx = TriggerClass::ACTION_FIRST; + + strcpy(namebuf,CurTrigger->Get_Name()); // Name + name_edt.Set_Text(namebuf,5); + + if (TriggerClass::Event_Need_Data(event_idx)) { + sprintf(databuf,"%ld",CurTrigger->Data); // Credits/Time + data_edt.Set_Text(databuf,8); + } + + house = CurTrigger->House; // House + + persistant = CurTrigger->IsPersistant; + + volatilebtn.Turn_Off(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_Off(); + switch (CurTrigger->IsPersistant) { + case TriggerClass::VOLATILE: + volatilebtn.Turn_On(); + break; + + case TriggerClass::SEMIPERSISTANT: + semipersistbtn.Turn_On(); + break; + + case TriggerClass::PERSISTANT: + persistbtn.Turn_On(); + break; + } + + /* + ......................... Fill in the list boxes ......................... + */ + for (i = 0; i < EVENT_COUNT; i++) { + eventnames[i] = TriggerClass::Name_From_Event( (EventType)i); + eventlist.Add_Item(eventnames[i]); + } + eventlist.Set_Selected_Index(event_idx); + + for (i = 0; i < TriggerClass::ACTION_COUNT; i++) { + actionnames[i] = TriggerClass::Name_From_Action( (TriggerClass::ActionType)i); + actionlist.Add_Item(actionnames[i]); + } + actionlist.Set_Selected_Index(action_idx); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Trigger Editor", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Events", D_EVENT_X + D_EVENT_W / 2, + D_EVENT_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Actions", D_ACTION_X + D_ACTION_W / 2, + D_ACTION_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Name", D_NAME_X - 5, D_NAME_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if ((EventType)event_idx==EVENT_CREDITS) { // use 'Data' for Credits + Fancy_Text_Print("Credits", D_DATA_X - 5, D_DATA_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } else { + if ((EventType)event_idx==EVENT_TIME) { // use 'Data' for Time + Fancy_Text_Print("1/10 Min", D_DATA_X - 5, D_DATA_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + if (TriggerClass::Action_Need_Team(action_idx)) { + if (CurTrigger->Team) { + Fancy_Text_Print(CurTrigger->Team->IniName, + D_TEAM_X + D_TEAM_W + 5, D_TEAM_Y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print( "!!!", + D_TEAM_X + D_TEAM_W + 5, D_TEAM_Y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + + /* + ..................... Rebuild the button list ...................... + */ + eventlist.Zap(); + actionlist.Zap(); + name_edt.Zap(); + data_edt.Zap(); + teambtn.Zap(); + gdibtn.Zap(); + nodbtn.Zap(); + neutralbtn.Zap(); + volatilebtn.Zap(); + persistbtn.Zap(); + semipersistbtn.Zap(); + okbtn.Zap(); + cancelbtn.Zap(); + + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + eventlist.Add_Tail(*commands); + actionlist.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + volatilebtn.Add_Tail(*commands); + persistbtn.Add_Tail(*commands); + semipersistbtn.Add_Tail(*commands); + if (TriggerClass::Event_Need_Data(event_idx)) { + data_edt.Add_Tail(*commands); + sprintf(databuf,"%ld",CurTrigger->Data); + data_edt.Set_Text(databuf,8); + } + if (TriggerClass::Event_Need_House(event_idx)) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neutralbtn.Add_Tail(*commands); + Set_House_Buttons(house, commands, BUTTON_GDI); + } + if (TriggerClass::Action_Need_Team(action_idx)) teambtn.Add_Tail(*commands); + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (EVENT_LIST | KN_BUTTON): + if (eventlist.Current_Index() != event_idx) { + event_idx = EventType(eventlist.Current_Index()); + databuf[0] = 0; + CurTrigger->Data = 0; + if (!TriggerClass::Event_Need_House(event_idx)) { + CurTrigger->House = HOUSE_NONE; + } + display = REDRAW_ALL; + } + break; + + case (ACTION_LIST | KN_BUTTON): + if (actionlist.Current_Index() != action_idx) { + action_idx = TriggerClass::ActionType(actionlist.Current_Index()); + display = REDRAW_ALL; + } + break; + + case (NAME_EDIT | KN_BUTTON): + break; + + case (DATA_EDIT | KN_BUTTON): + break; + + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + case (BUTTON_MULTI5 | KN_BUTTON): + case (BUTTON_MULTI6 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + Set_House_Buttons(house, commands, BUTTON_GDI); + break; + + case (BUTTON_TEAM | KN_BUTTON): + Handle_Teams("Select a Team"); + if (CurTeam) { + CurTrigger->Team = CurTeam; + } + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + display = REDRAW_ALL; + break; + + case (BUTTON_VOLATILE | KN_BUTTON): + persistant = TriggerClass::VOLATILE; + volatilebtn.Turn_On(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_Off(); + break; + + case (BUTTON_PERSIST | KN_BUTTON): + persistant = TriggerClass::PERSISTANT; + volatilebtn.Turn_Off(); + persistbtn.Turn_On(); + semipersistbtn.Turn_Off(); + break; + + case (BUTTON_SEMIPERSIST | KN_BUTTON): + persistant = TriggerClass::SEMIPERSISTANT; + volatilebtn.Turn_Off(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + /* + ------------------------------ Save values ------------------------------- + */ + if (!cancel) { + + /* + .......................... Get list indices ........................... + */ + event_idx = EventType(eventlist.Current_Index()); + action_idx = TriggerClass::ActionType(actionlist.Current_Index()); + + /* + ......................... Set Event & Action .......................... + */ + CurTrigger->Event = EventType(event_idx); + CurTrigger->Action = TriggerClass::ActionType(action_idx); + + /* + .............................. Set name ............................... + */ + if (strlen(namebuf)==0) { + CurTrigger->Set_Name("____"); + } else { + CurTrigger->Set_Name(namebuf); + } + + /* + .............................. Set Data ............................... + */ + if (TriggerClass::Event_Need_Data(event_idx)) { + CurTrigger->Data = atol(databuf); + } + + /* + .............................. Set House .............................. + */ + if (TriggerClass::Event_Need_House(event_idx)) { + CurTrigger->House = house; + } else { + CurTrigger->House = HOUSE_NONE; + } + + /* + ........................... Set Persistence .......................... + */ + CurTrigger->IsPersistant = persistant; + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } else { + return(0); + } +} + + +/*************************************************************************** + * MapEditClass::Import_Triggers -- lets user import triggers * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³x Name Event Action House ³³ ³ * + * ³ ³ Name Event Action House ÃÄ´ ³ * + * ³ ³x Name Event Action House ³ ³ ³ * + * ³ ³ Name Event Action House ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = user cancelled * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/29/1995 BRR : Created. * + *=========================================================================*/ +int MapEditClass::Import_Triggers(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 452, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TRIGGER_LIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; + static int tabs[] = + {70, 220, 370, 420}; // list box tab stops + DynamicVectorClass trignames; // list of INI trigger names + char *inibuf; // working INI buffer + CCFileClass file; // file for reading the INI file + char buf[128]; // for reading an INI entry + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + TriggerClass *trigger; // Working trigger pointer. + char *item; // for adding to list box + char *eventptr; + char *actionptr; + char *houseptr; + int i; + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + CheckListClass triggerlist (TRIGGER_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Read the MASTER.INI file + ------------------------------------------------------------------------*/ + /*........................................................................ + Read the file into the staging buffer + ........................................................................*/ + inibuf = new char [30000]; + memset(inibuf, '\0', 30000); + file.Set_Name("MASTER.INI"); + if (!file.Is_Available()) { + file.Close(); + delete [] inibuf; + return(-1); + } else { + file.Read(inibuf, 30000 - 1); + } + file.Close(); + + /*........................................................................ + Read all entry names in the Triggers section into a temp buffer + ........................................................................*/ + len = strlen(inibuf) + 2; + tbuffer = inibuf + len; + WWGetPrivateProfileString(TriggerClass::INI_Name(), NULL, NULL, tbuffer, + 30000 - len, inibuf); + + /*........................................................................ + For each entry in the INI section: + - Get the entry + - Generate a string describing the trigger + - Add that string to the list box + - Add a ptr to the INI entry name to our 'trignames' list + ........................................................................*/ + while (*tbuffer != '\0') { + WWGetPrivateProfileString(TriggerClass::INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, inibuf); + item = new char [60]; + + /* + ** Parse the INI entry + */ + eventptr = strtok(buf,","); + actionptr = strtok(NULL,","); + strtok(NULL,","); + houseptr = strtok(NULL,","); + + /* + ** Generate the descriptive string + */ + sprintf(item, " %s\t%s\t%s\t", tbuffer, eventptr, actionptr); + + /* + ** Add house name if needed + */ + if (TriggerClass::Event_Need_House(TriggerClass::Event_From_Name(eventptr))) { + HousesType house = HouseTypeClass::From_Name(houseptr); + if (house != HOUSE_NONE) { + strcat(item, HouseTypeClass::As_Reference(house).Suffix); + } else { + strcat(item, "!!!"); + } + } else { + strcat(item," "); + } + + /* + ** Add the item to the list box + */ + triggerlist.Add_Item(item); + + /* + ** Add the name to our internal name list + */ + trignames.Add(tbuffer); + + tbuffer += strlen(tbuffer)+1; + } + + /* + ............................ Create the list ............................. + */ + commands = &triggerlist; + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + triggerlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Import Triggers", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /*........................................................................ + Re-parse the INI section; if any item is checked in the list box, create + that trigger for this scenario. + ........................................................................*/ + if (!cancel) { + tbuffer = inibuf + len; + i = 0; + while (*tbuffer != '\0') { + + /* + ** If this item is checked on the list, create a new trigger + ** and fill it in. + */ + if (triggerlist.Is_Checked(i)) { + WWGetPrivateProfileString(TriggerClass::INI_Name(), tbuffer, NULL, + buf, sizeof(buf)-1, inibuf); + + trigger = new TriggerClass(); + trigger->Fill_In(tbuffer, buf); + + if (trigger->House != HOUSE_NONE) + HouseTriggers[trigger->House].Add(trigger); + } + + tbuffer += strlen(tbuffer)+1; + i++; + } + } + + + /*........................................................................ + Clean up memory + ........................................................................*/ + trignames.Clear(); + while (triggerlist.Count()) { + item = (char *)triggerlist.Get_Item(0); + triggerlist.Remove_Item(item); + delete [] item; + } + delete [] inibuf; + + if (cancel) { + return(-1); + } else { + return(0); + } +} + + +/*************************************************************************** + * MapEditClass::Import_Teams -- lets the user import teams * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = user cancelled * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Import_Teams(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 500, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TEAM_LIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; + static int tabs[] = {120, 180}; // list box tab stops + DynamicVectorClass teamnames; // list of INI team names + char *inibuf; // working INI buffer + CCFileClass file; // file for reading the INI file + char buf[128]; // for reading an INI entry + char *tbuffer; // Accumulation buffer of team IDs. + int len; // Length of data in buffer. + TeamTypeClass *team; // Working team pointer. + char *item; // for adding to list box + char *houseptr; + char *classptr; + int numclasses; + int i; + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + CheckListClass teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Read the MASTER.INI file + ------------------------------------------------------------------------*/ + /*........................................................................ + Read the file into the staging buffer + ........................................................................*/ + inibuf = new char [30000]; + memset(inibuf, '\0', 30000); + file.Set_Name("MASTER.INI"); + if (!file.Is_Available()) { + file.Close(); + delete [] inibuf; + return(-1); + } else { + file.Read(inibuf, 30000 - 1); + } + + file.Close(); + /*........................................................................ + Read all entry names in the TeamTypes section into a temp buffer + ........................................................................*/ + len = strlen(inibuf) + 2; + tbuffer = inibuf + len; + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), NULL, NULL, tbuffer, + 30000 - len, inibuf); + + /*........................................................................ + For each entry in the INI section: + - Get the entry + - Generate a string describing the team + - Add that string to the list box + - Add a ptr to the INI entry name to our 'teamnames' list + ........................................................................*/ + while (*tbuffer != '\0') { + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, inibuf); + item = new char [60]; + + /* + ** Parse the INI entry + */ + houseptr = strtok(buf,","); + for (i = 0; i < 9; i++) { + strtok(NULL,","); + } + numclasses = atoi(strtok(NULL,",")); + + /* + ** Generate the descriptive string + */ + sprintf(item," %s\t",tbuffer); + HousesType house = HouseTypeClass::From_Name(houseptr); + if (house != HOUSE_NONE) { + strcat(item, HouseTypeClass::As_Reference(house).Suffix); + } else { + strcat(item, "!!!"); + } + strcat(item, "\t"); + + classptr = strtok(NULL,","); + for (i = 0; i < numclasses; i++) { + if (strlen(item) + strlen(classptr) < 60) { + strcat(item,classptr); + classptr = strtok(NULL,","); + } else { + break; + } + } + + /* + ** Add the item to the list box + */ + teamlist.Add_Item(item); + /* + ** Add the name to our internal name list + */ + teamnames.Add(tbuffer); + + tbuffer += strlen(tbuffer)+1; + } + + /* + ............................ Create the list ............................. + */ + commands = &teamlist; + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + teamlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Import Teams", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /*........................................................................ + Re-parse the INI section; if any item is checked in the list box, create + that team for this scenario. + ........................................................................*/ + if (!cancel) { + tbuffer = inibuf + len; + i = 0; + while (*tbuffer != '\0') { + /* + ** If this item is checked on the list, create a new team + ** and fill it in. + */ + if (teamlist.Is_Checked(i)) { + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), tbuffer, NULL, + buf, sizeof(buf)-1, inibuf); + + team = new TeamTypeClass(); + team->Fill_In(tbuffer,buf); + } + + tbuffer += strlen(tbuffer)+1; + i++; + } + } + + /*........................................................................ + Clean up memory + ........................................................................*/ + teamnames.Clear(); + while (teamlist.Count()) { + item = (char *)teamlist.Get_Item(0); + teamlist.Remove_Item(item); + delete [] item; + } + delete [] inibuf; + + if (cancel) { + return(-1); + } else { + return(0); + } +} + +#endif diff --git a/TIBERIANDAWN/MAPEDIT.CPP b/TIBERIANDAWN/MAPEDIT.CPP new file mode 100644 index 000000000..265187a73 --- /dev/null +++ b/TIBERIANDAWN/MAPEDIT.CPP @@ -0,0 +1,1923 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapedit.cpv 2.18 16 Oct 1995 16:48:40 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor overloaded routines & utility routines * + *-------------------------------------------------------------------------* + * Map Editor modules: * + * (Yes, they're all one huge class.) * + * mapedit.cpp: overloaded routines, utility routines * + * mapeddlg.cpp: map editor dialogs, most of the main menu options * + * mapedplc.cpp: object-placing routines * + * mapedsel.cpp: object-selection & manipulation routines * + * mapedtm.cpp: team-editing routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::MapEditClass -- class constructor * + * MapEditClass::One_Time -- one-time initialization * + * MapEditClass::Read_INI -- overloaded Read_INI function * + * MapEditClass::Clear_List -- clears the internal choosable object list * + * MapEditClass::Add_To_List -- adds a TypeClass to the choosable list * + * MapEditClass::AI -- The map editor's main logic * + * MapEditClass::Draw_It -- overloaded Redraw routine * + * MapEditClass::Main_Menu -- main menu processor for map editor * + * MapEditClass::AI_Menu -- menu of AI options * + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * MapEditClass::Verify_House -- sees if given house can own given obj * + * MapEditClass::Cycle_House -- finds next valid house for object type * + * MapEditClass::Trigger_Needs_Team -- tells if a trigger needs a team * + * MapEditClass::Fatal -- exits with error message * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + +/* +****************************** Globals/Externs ****************************** +*/ +/*........................................................................... +Array of all missions supported by the map editor +...........................................................................*/ +MissionType MapEditClass::MapEditMissions[] = { + MISSION_GUARD, + MISSION_STICKY, + MISSION_HARVEST, + MISSION_GUARD_AREA, + MISSION_RETURN, + MISSION_AMBUSH, + MISSION_HUNT, + MISSION_SLEEP, +}; +#define NUM_EDIT_MISSIONS (sizeof(MapEditClass::MapEditMissions) / sizeof(MapEditClass::MapEditMissions[0])) + + +/*........................................................................... +For menu processing +...........................................................................*/ +extern int UnknownKey; // in menus.cpp + +char MapEditClass::HealthBuf[20]; + + +/*************************************************************************** + * MapEditClass::MapEditClass -- class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +MapEditClass::MapEditClass(void) +{ + /* + ** Init data members. + */ + ScenVar = SCEN_VAR_A; + ObjCount = 0; + LastChoice = 0; + LastHouse = HOUSE_GOOD; + GrabbedObject = 0; + for (int i=0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + TypeOffset[i] = 0; + } + Waypoint[WAYPT_HOME] = 0; + CurrentCell = 0; + CurTrigger = NULL; + Changed = 0; + LMouseDown = 0; + BaseBuilding = 0; + BasePercent = 100; +} + + +/*************************************************************************** + * MapEditClass::One_Time -- one-time initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/02/1995 BR : Created. * + *=========================================================================*/ +void MapEditClass::One_Time(void) +{ + MouseClass::One_Time(); + + /*------------------------------------------------------------------------ + Create the pop-up controls + ------------------------------------------------------------------------*/ + /*........................................................................ + The map: a single large "button" + ........................................................................*/ + //MapArea = new ControlClass(MAP_AREA,0,8,312,192, GadgetClass::LEFTPRESS | + //GadgetClass::LEFTRELEASE, false); + MapArea = new ControlClass(MAP_AREA,0,16,624,384, GadgetClass::LEFTPRESS | + GadgetClass::LEFTRELEASE, false); + + /*........................................................................ + House buttons + ........................................................................*/ + GDIButton = new TextButtonClass (POPUP_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_GDI_X, POPUP_GDI_Y, POPUP_GDI_W, POPUP_GDI_H); + + NODButton = new TextButtonClass (POPUP_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_NOD_X, POPUP_NOD_Y, POPUP_NOD_W, POPUP_NOD_H); + + NeutralButton = new TextButtonClass (POPUP_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_NEUTRAL_X, POPUP_NEUTRAL_Y, POPUP_NEUTRAL_W, POPUP_NEUTRAL_H); + + Multi1Button = new TextButtonClass (POPUP_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI1_X, POPUP_MULTI1_Y, POPUP_MULTI1_W, POPUP_MULTI1_H); + + Multi2Button = new TextButtonClass (POPUP_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI2_X, POPUP_MULTI2_Y, POPUP_MULTI2_W, POPUP_MULTI2_H); + + Multi3Button = new TextButtonClass (POPUP_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI3_X, POPUP_MULTI3_Y, POPUP_MULTI3_W, POPUP_MULTI3_H); + + Multi4Button = new TextButtonClass (POPUP_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI4_X, POPUP_MULTI4_Y, POPUP_MULTI4_W, POPUP_MULTI4_H); + + /*........................................................................ + The mission list box + ........................................................................*/ + MissionList = new ListClass (POPUP_MISSIONLIST, + POPUP_MISSION_X, POPUP_MISSION_Y, POPUP_MISSION_W, POPUP_MISSION_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + for (int i = 0; i < NUM_EDIT_MISSIONS; i++) { + MissionList->Add_Item (MissionClass::Mission_Name(MapEditMissions[i])); + } + + /*........................................................................ + The health bar + ........................................................................*/ + HealthGauge = new TriColorGaugeClass (POPUP_HEALTHGAUGE, + POPUP_HEALTH_X, POPUP_HEALTH_Y, POPUP_HEALTH_W, POPUP_HEALTH_H); + HealthGauge->Use_Thumb(true); + HealthGauge->Set_Maximum(0x100); + HealthGauge->Set_Red_Limit(0x3f - 1); + HealthGauge->Set_Yellow_Limit(0x7f - 1); + + /*........................................................................ + The health text label + ........................................................................*/ + HealthBuf[0] = 0; + HealthText = new TextLabelClass (HealthBuf, + POPUP_HEALTH_X + POPUP_HEALTH_W / 2, + POPUP_HEALTH_Y + POPUP_HEALTH_H + 1, + CC_GREEN, TPF_CENTER | TPF_FULLSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + /*........................................................................ + The facing dial + ........................................................................*/ + FacingDial = new Dial8Class (POPUP_FACINGDIAL, POPUP_FACEBOX_X, + POPUP_FACEBOX_Y, POPUP_FACEBOX_W, POPUP_FACEBOX_H, (DirType)0); + + /*........................................................................ + The base percent-built slider & its label + ........................................................................*/ + BaseGauge = new GaugeClass (POPUP_BASEPERCENT, + POPUP_BASE_X, POPUP_BASE_Y, POPUP_BASE_W, POPUP_BASE_H); + BaseLabel = new TextLabelClass ("Base:", POPUP_BASE_X - 3, POPUP_BASE_Y, + CC_GREEN, TPF_RIGHT | TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + BaseGauge->Set_Maximum(100); + BaseGauge->Set_Value(BasePercent); +} + + +/*********************************************************************************************** + * MapeditClass::Init_IO -- Reinitializes the radar map at scenario start. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Init_IO(void) +{ + /*------------------------------------------------------------------------ + For normal game mode, jump to the parent's Init routine. + ------------------------------------------------------------------------*/ + if (!Debug_Map) { + + MouseClass::Init_IO(); + + } else { + + /*------------------------------------------------------------------------ + For editor mode, add the map area to the button input list + ------------------------------------------------------------------------*/ + Buttons = 0; + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + } +} + + +/*************************************************************************** + * MapEditClass::Read_INI -- overloaded Read_INI function * + * * + * Overloading this function gives the map editor a chance to initialize * + * certain values every time a new INI is read. * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Read_INI(char *buffer) +{ + /* + ------------------------ Invoke parent's Read_INI ------------------------ + */ + MouseClass::Read_INI(buffer); + + BasePercent = WWGetPrivateProfileInt("Basic","Percent",0,buffer); + BaseGauge->Set_Value(BasePercent); +} + + +/*************************************************************************** + * MapEditClass::Write_INI -- overloaded Read_INI function * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Write_INI(char *buffer) +{ + /* + ----------------------- Invoke parent's Write_INI ------------------------ + */ + MouseClass::Write_INI(buffer); + + /* + ** Save the base's percent-built value; this must be saved into the BASIC + ** section of the INI, since the Base section will be entirely erased + ** by the Base's Write_INI routine. + */ + WWWritePrivateProfileInt("Basic", "Percent", BasePercent, buffer); +} + + +/*************************************************************************** + * MapEditClass::Clear_List -- clears the internal choosable object list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Clear_List(void) +{ + /*------------------------------------------------------------------------ + Set # object type ptrs to 0, set NumType for each type to 0 + ------------------------------------------------------------------------*/ + ObjCount = 0; + for (int i = 0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + } +} + + +/*************************************************************************** + * MapEditClass::Add_To_List -- adds a TypeClass to the choosable list * + * * + * Use this routine to add an object to the game object selection list. * + * This list is used by the Add_Object function. All items located in the * + * list will appear and be chooseable by that function. Make sure to * + * clear the list before adding a sequence of items to it. Clearing * + * the list is accomplished by the Clear_List() function. * + * * + * INPUT: * + * object ptr to ObjectTypeClass to add * + * * + * OUTPUT: * + * bool: was the object added to the list? A failure could occur if * + * NULL were passed in or the list is full. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/04/1994 JLB : Created. * + *=========================================================================*/ +bool MapEditClass::Add_To_List(ObjectTypeClass const *object) +{ + /* + ** Add the object if there's room. + */ + if (object && ObjCount < MAX_EDIT_OBJECTS) { + Objects[ObjCount++] = object; + + /* + ** Update type counters. + */ + switch (object->What_Am_I()) { + case RTTI_TEMPLATETYPE: + NumType[0]++; + break; + + case RTTI_OVERLAYTYPE: + NumType[1]++; + break; + + case RTTI_SMUDGETYPE: + NumType[2]++; + break; + + case RTTI_TERRAINTYPE: + NumType[3]++; + break; + + case RTTI_UNITTYPE: + NumType[4]++; + break; + + case RTTI_INFANTRYTYPE: + NumType[5]++; + break; + + case RTTI_AIRCRAFTTYPE: + NumType[6]++; + break; + + case RTTI_BUILDINGTYPE: + NumType[7]++; + break; + } + return(true); + } + + return(false); +} + + +/*************************************************************************** + * MapEditClass::AI -- The map editor's main logic * + * * + * This routine overloads the parent's (DisplayClass) AI function. * + * It checks for any input specific to map editing, and calls the parent * + * AI routine to handle scrolling and other mainstream map stuff. * + * * + * If this detects one of its special input keys, it sets 'input' to 0 * + * before calling the parent AI routine; this prevents input conflict. * + * * + * SUPPORTED INPUT: * + * General: * + * F2/RMOUSE: main menu * + * F6: toggles show-passable mode * + * HOME: go to the Home Cell (scenario's start position)* + * SHIFT-HOME: set the Home Cell to the current TacticalCell* + * ESC: exits to DOS * + * Object Placement: * + * INSERT: go into placement mode * + * ESC: exit placement mode * + * LEFT/RIGHT: prev/next placement object * + * PGUP/PGDN: prev/next placement category * + * HOME: 1st placement object (clear template) * + * h/H: toggle house of placement object * + * LMOUSE: place the placement object * + * MOUSE MOTION: "paint" with the placement object * + * Object selection: * + * LMOUSE: select & "grab" current object * + * If no object is present where the mouse is * + * clicked, the current object is de-selected * + * If the same object is clicked on, it stays * + * selected. Also displays the object-editing * + * gadgets. * + * LMOUSE RLSE: release currently-grabbed object * + * MOUSE MOTION: if an object is grabbed, moves the object * + * SHIFT|ALT|ARROW: moves object in that direction * + * DELETE deletes currently-selected object * + * Object-editing controls: * + * POPUP_GDI: makes GDI the owner of this object * + * POPUP_NOD: makes NOD the owner of this object * + * POPUP_MISSIONLIST: sets that mission for this object * + * POPUP_HEALTHGAUGE: sets that health value for this object * + * POPUP_FACINGDIAL: sets the object's facing * + * * + * Changed is set when you: * + * - place an object * + * - move a grabbed object * + * - delete an object * + * - size the map * + * - create a new scenario * + * Changed is cleared when you: * + * - Save the scenario * + * - Load a scenario * + * - Play the scenario * + * * + * INPUT: * + * input KN_ value, 0 if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI(KeyNumType &input, int x, int y) +{ + int rc; + MissionType mission; + int strength; + CELL cell; + int i; + int found; // for removing a waypoint label + int waypt_idx; // for labelling a waypoint + BaseNodeClass *node; // for removing from an AI Base + HousesType house; + + /*------------------------------------------------------------------------ + Trap 'F2' regardless of whether we're in game or editor mode + ------------------------------------------------------------------------*/ + if (Debug_Flag) { + if (/*(input == KN_F2 && Session == GAME_SOLO) ||*/ input == (KN_F2 | KN_CTRL_BIT)) { + ScenarioInit = 0; + + /* + ** If we're in editor mode & Changed is set, prompt for saving changes + */ + if (Debug_Map && Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + /* + ........................ User wants to save ........................ + */ + if (rc == 0) { + + /* + ................ If save cancelled, abort game .................. + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + } else { + Changed = 0; + Go_Editor(!Debug_Map); + } + } else { + + /* + .................... User doesn't want to save ..................... + */ + Go_Editor(!Debug_Map); + } + } else { + /* + ** If we're in game mode, set Changed to 0 (so if we didn't save our + ** changes above, they won't keep coming back to haunt us with continual + ** Save Changes? prompts!) + */ + if (!Debug_Map) { + Changed = 0; + } + Go_Editor(!Debug_Map); + } + } + } + + /*------------------------------------------------------------------------ + For normal game mode, jump to the parent's AI routine. + ------------------------------------------------------------------------*/ + if (!Debug_Map) { + MouseClass::AI(input, x, y); + return; + } + + ::Frame++; + + /*------------------------------------------------------------------------ + Do special mouse processing if the mouse is over the map + ------------------------------------------------------------------------*/ + if (Get_Mouse_X() > TacPixelX && Get_Mouse_X() < + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() > TacPixelY && Get_Mouse_Y() < + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + /*..................................................................... + When the mouse moves over a scrolling edge, ScrollClass changes its + shape to the appropriate arrow or NO symbol; it's our job to change it + back to normal (or whatever the shape is set to by Set_Default_Mouse()) + when it re-enters the map area. + .....................................................................*/ + if (CurTrigger) { + Override_Mouse_Shape(MOUSE_CAN_MOVE); + } else { + Override_Mouse_Shape(MOUSE_NORMAL); + } + } + + /*..................................................................... + Set 'ZoneCell' to track the mouse cursor around over the map. Do this + even if the map is scrolling. + .....................................................................*/ + if (Get_Mouse_X() >= TacPixelX && Get_Mouse_X() <= + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() >= TacPixelY && Get_Mouse_Y() <= + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + cell = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + if (cell != -1) { + Set_Cursor_Pos(cell); + if (PendingObject) { + Flag_To_Redraw(true); + } + } + } + + /*------------------------------------------------------------------------ + Check for mouse motion while left button is down. + ------------------------------------------------------------------------*/ + rc = Mouse_Moved(); + if (LMouseDown && rc) { + /*..................................................................... + "Paint" mode: place current object, and restart placement + .....................................................................*/ + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { + Changed = 1; + Start_Placement(); + } + } else { + /*..................................................................... + Move the currently-grabbed object + .....................................................................*/ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } + } + } + } + + /*------------------------------------------------------------------------ + Trap special editing keys; if one is detected, set 'input' to 0 to + prevent a conflict with parent's AI(). + ------------------------------------------------------------------------*/ + switch (input) { + /*--------------------------------------------------------------------- + F2/RMOUSE = pop up main menu + ---------------------------------------------------------------------*/ + case KN_RMOUSE: + /* + ..................... Turn off placement mode ...................... + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + } + + /* + ................. Turn off trigger placement mode .................. + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + } + + /* + .............. Unselect object & hide popup controls ............... + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + Main_Menu(); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + F6 = toggle passable/impassable display + ---------------------------------------------------------------------*/ + case KN_F6: + Debug_Passable = (Debug_Passable == false); + HiddenPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + INSERT = go into object-placement mode + ---------------------------------------------------------------------*/ + case KN_INSERT: + if (!PendingObject) { + /* + ......... Unselect current object, hide popup controls .......... + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + /* + .................... Go into placement mode ..................... + */ + Start_Placement(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ESC = exit placement mode, or exit to DOS + ---------------------------------------------------------------------*/ + case KN_ESC: + + /* + .................... Exit object placement mode .................... + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + input = KN_NONE; + break; + } else { + + /* + ................... Exit trigger placement mode .................... + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + input = KN_NONE; + break; + } else { + rc = CCMessageBox().Process("Exit Scenario Editor?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + .......... User doesn't want to exit; return to editor .......... + */ + if (rc==1) { + input = KN_NONE; + break; + } + + /* + ................. If changed, prompt for saving ................. + */ + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ..................... User wants to save ..................... + */ + if (rc == 0) { + + /* + .............. If save cancelled, abort exit .............. + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + break; + } else { + Changed = 0; + } + } + } + } + } + Prog_End(); + exit (0); + break; + + /*--------------------------------------------------------------------- + LEFT = go to previous placement object + ---------------------------------------------------------------------*/ + case KN_LEFT: + if (PendingObject) { + Place_Prev(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + RIGHT = go to next placement object + ---------------------------------------------------------------------*/ + case KN_RIGHT: + if (PendingObject) { + Place_Next(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + PGUP = go to previous placement category + ---------------------------------------------------------------------*/ + case KN_PGUP: + if (PendingObject) { + Place_Prev_Category(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + PGDN = go to next placement category + ---------------------------------------------------------------------*/ + case KN_PGDN: + if (PendingObject) { + Place_Next_Category(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + HOME = jump to first placement object, or go to Home Cell + ---------------------------------------------------------------------*/ + case KN_HOME: + if (PendingObject) { + Place_Home(); + } else { + + /* + ....................... Set map position ........................ + */ + ScenarioInit++; + Set_Tactical_Position(Waypoint[WAYPT_HOME]); + ScenarioInit--; + + /* + ...................... Force map to redraw ...................... + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + SHIFT-HOME: set new Home Cell position + ---------------------------------------------------------------------*/ + case ((int)KN_HOME | (int)KN_SHIFT_BIT): + /* + ** Unflag the old Home Cell, if there are no other waypoints + ** pointing to it + */ + cell = Waypoint[WAYPT_HOME]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_HOME && Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + + /* + ** Now set the new Home cell + */ + Waypoint[WAYPT_HOME] = Coord_Cell(TacticalCoord); + (*this)[Coord_Cell(TacticalCoord)].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + Changed = 1; + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + SHIFT-R: set new Reinforcement Cell position. Don't allow setting + the Reinf. Cell to the same as the Home Cell (for display purposes.) + ---------------------------------------------------------------------*/ + case ((int)KN_R | (int)KN_SHIFT_BIT): + if (CurrentCell==0 || CurrentCell==Waypoint[WAYPT_HOME]) { + break; + } + + /* + ** Unflag the old Reinforcement Cell, if there are no other waypoints + ** pointing to it + */ + cell = Waypoint[WAYPT_REINF]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_REINF && Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + /* + ** Now set the new Reinforcement cell + */ + Waypoint[WAYPT_REINF] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + Changed = 1; + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-Letter: Label a waypoint cell + ---------------------------------------------------------------------*/ + case ((int)KN_A | (int)KN_ALT_BIT): + case ((int)KN_B | (int)KN_ALT_BIT): + case ((int)KN_C | (int)KN_ALT_BIT): + case ((int)KN_D | (int)KN_ALT_BIT): + case ((int)KN_E | (int)KN_ALT_BIT): + case ((int)KN_F | (int)KN_ALT_BIT): + case ((int)KN_G | (int)KN_ALT_BIT): + case ((int)KN_H | (int)KN_ALT_BIT): + case ((int)KN_I | (int)KN_ALT_BIT): + case ((int)KN_J | (int)KN_ALT_BIT): + case ((int)KN_K | (int)KN_ALT_BIT): + case ((int)KN_L | (int)KN_ALT_BIT): + case ((int)KN_M | (int)KN_ALT_BIT): + case ((int)KN_N | (int)KN_ALT_BIT): + case ((int)KN_O | (int)KN_ALT_BIT): + case ((int)KN_P | (int)KN_ALT_BIT): + case ((int)KN_Q | (int)KN_ALT_BIT): + case ((int)KN_R | (int)KN_ALT_BIT): + case ((int)KN_S | (int)KN_ALT_BIT): + case ((int)KN_T | (int)KN_ALT_BIT): + case ((int)KN_U | (int)KN_ALT_BIT): + case ((int)KN_V | (int)KN_ALT_BIT): + case ((int)KN_W | (int)KN_ALT_BIT): + case ((int)KN_X | (int)KN_ALT_BIT): + case ((int)KN_Y | (int)KN_ALT_BIT): + case ((int)KN_Z | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + waypt_idx = KN_To_KA(input & 0xff) - KA_a; + /*............................................................... + Unflag cell for this waypoint if there is one + ...............................................................*/ + cell = Waypoint[waypt_idx]; + if (cell != -1) { + if (Waypoint[WAYPT_HOME] != cell && + Waypoint[WAYPT_REINF] != cell) + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + Waypoint[waypt_idx] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-1-4: Designate a cell as a capture-the-flag cell. + ---------------------------------------------------------------------*/ + case ((int)KN_1 | (int)KN_ALT_BIT): + case ((int)KN_2 | (int)KN_ALT_BIT): + case ((int)KN_3 | (int)KN_ALT_BIT): + case ((int)KN_4 | (int)KN_ALT_BIT): + /*------------------------------------------------------------------ + If there's a current cell, place the flag & waypoint there. + ------------------------------------------------------------------*/ + if (CurrentCell != 0) { + waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house)) { + HouseClass::As_Pointer(house)->Flag_Attach(CurrentCell,true); + } + } else { + /*------------------------------------------------------------------ + If there's a current object, attach the flag to it and clear the + waypoint. + ------------------------------------------------------------------*/ + if (CurrentObject[0] != 0) { + waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house) && CurrentObject[0]->What_Am_I() == RTTI_UNIT) { + HouseClass::As_Pointer(house)->Flag_Attach((UnitClass *)CurrentObject[0], true); + } + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-Space: Remove a waypoint designation + ---------------------------------------------------------------------*/ + case ((int)KN_SPACE | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + /*............................................................... + Loop through letter waypoints; if this cell is one of them, + clear that waypoint. + ...............................................................*/ + for (i = 0 ; i < 26; i++) { + if (Waypoint[i]==CurrentCell) + Waypoint[i] = -1; + } + + /*............................................................... + Loop through flag home values; if this cell is one of them, clear + that waypoint. + ...............................................................*/ + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(HOUSE_MULTI1 + i); + if (HouseClass::As_Pointer(house) && + CurrentCell == HouseClass::As_Pointer(house)->FlagHome) + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(CurrentCell),true); + } + + /*............................................................... + If there are no more waypoints on this cell, clear the cell's + waypoint designation. + ...............................................................*/ + if (Waypoint[WAYPT_HOME]!=CurrentCell && + Waypoint[WAYPT_REINF]!=CurrentCell) + (*this)[CurrentCell].IsWaypoint = 0; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + 'H' = toggle current placement object's house + ---------------------------------------------------------------------*/ + case KN_H: + case ((int)KN_H | (int)KN_SHIFT_BIT): + if (PendingObject) { + Toggle_House(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Left-mouse click: + Button DOWN: + - Toggle LMouseDown + - If we're in placement mode, try to place the current object + - If success, re-enter placement mode + - Otherwise, try to select an object, and "grab" it if there is one + - If no object, then select that cell as the "current" cell + Button UP: + - Toggle LMouseDown + - release any grabbed object + ---------------------------------------------------------------------*/ + case ((int)MAP_AREA | (int)KN_BUTTON): + /* + ------------------------- Left Button DOWN ------------------------- + */ + if (Keyboard::Down(KN_LMOUSE)) { + LMouseDown = 1; + /* + ............... Placement mode: place an object ................. + */ + if (PendingObject) { + if (Place_Object()==0) { + Changed = 1; + Start_Placement(); + } + } else { + /* + ....................... Place a trigger ......................... + */ + if (CurTrigger) { + Place_Trigger(); + Changed = 1; + } else { + /* + ................. Select an object or a cell ................. + .................. Check for double-click .................... + */ + if (CurrentObject.Count() && + ( (TickCount.Time() - LastClickTime) < 15)) { + ; // stub + + } else { + /* + ................ Single-click: select object ................. + */ + if (Select_Object()==0) { + CurrentCell = 0; + Grab_Object(); + } else { + /* + ................ No object: select the cell .................. + */ + CurrentCell = Click_Cell_Calc(_Kbd->MouseQX,_Kbd->MouseQY); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + } + } + LastClickTime = TickCount.Time(); + input = KN_NONE; + } else { + + /* + -------------------------- Left Button UP -------------------------- + */ + LMouseDown = 0; + GrabbedObject = 0; + input = KN_NONE; + } + break; + + /*--------------------------------------------------------------------- + SHIFT-ALT-Arrow: move the current object + ---------------------------------------------------------------------*/ + case (int)KN_UP | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_DOWN | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_LEFT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_RIGHT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + CurrentObject[0]->Move(KN_To_Facing(input)); + Changed = 1; + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + DELETE: delete currently-selected object + ---------------------------------------------------------------------*/ + case KN_DELETE: + /*.................................................................. + Delete currently-selected object's trigger, or the object + ..................................................................*/ + if (CurrentObject.Count()) { + + /* + ........................ Delete trigger ......................... + */ + if (CurrentObject[0]->Trigger) { + CurrentObject[0]->Trigger = NULL; + } else { + /* + ** If the current object is part of the AI's Base, remove it + ** from the Base's Node list. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && + Base.Is_Node((BuildingClass *)CurrentObject[0])) { + node = Base.Get_Node((BuildingClass *)CurrentObject[0]); + Base.Nodes.Delete(*node); + } + + /* + ................... Delete current object .................... + */ + delete CurrentObject[0]; + + /* + .................. Hide the popup controls ................... + */ + Popup_Controls(); + } + + /* + ........................ Force a redraw ......................... + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } else { + /* + ................. Remove trigger from current cell ................. + */ + if (CurrentCell) { + if ((*this)[CurrentCell].IsTrigger) { + (*this)[CurrentCell].IsTrigger = 0; + CellTriggers[CurrentCell] = NULL; + /* + ...................... Force a redraw ........................ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + TAB: select next object on the map + ---------------------------------------------------------------------*/ + case KN_TAB: + Select_Next(); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: House Button + ---------------------------------------------------------------------*/ + case (POPUP_GDI | KN_BUTTON): + case (POPUP_NOD | KN_BUTTON): + case (POPUP_NEUTRAL | KN_BUTTON): + case (POPUP_MULTI1 | KN_BUTTON): + case (POPUP_MULTI2 | KN_BUTTON): + case (POPUP_MULTI3 | KN_BUTTON): + case (POPUP_MULTI4 | KN_BUTTON): + /*.................................................................. + Convert input value into a house value; assume HOUSE_GOOD is 0 + ..................................................................*/ + house = (HousesType)( (input & (~KN_BUTTON)) - POPUP_GDI); + /*.................................................................. + If that house doesn't own this object, try to transfer it + ..................................................................*/ + if (CurrentObject[0]->Owner()!=house) { + if (Change_House(house)) { + Changed = 1; + } + } + Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_GDI); + HiddenPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Mission + ---------------------------------------------------------------------*/ + case (POPUP_MISSIONLIST | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ........................ Set new mission ........................ + */ + mission = MapEditMissions[MissionList->Current_Index()]; + if (CurrentObject[0]->Get_Mission() != mission) { + ((TechnoClass *)CurrentObject[0])->Set_Mission(mission); + Changed = 1; + } + } + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Health + ---------------------------------------------------------------------*/ + case (POPUP_HEALTHGAUGE | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + .......... Derive strength from current gauge reading ........... + */ + strength = Fixed_To_Cardinal( + (unsigned)CurrentObject[0]->Class_Of().MaxStrength, + (unsigned)HealthGauge->Get_Value()); + + /* + ........................... Clip to 1 ........................... + */ + if (strength <= 0) { + strength = 1; + } + + /* + ....................... Set new strength ........................ + */ + if (strength != CurrentObject[0]->Strength) { + CurrentObject[0]->Strength = strength; + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + + /* + ....................... Update text label ....................... + */ + sprintf(HealthBuf,"%d",strength); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Facing + ---------------------------------------------------------------------*/ + case (POPUP_FACINGDIAL | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ........................ Set new facing ......................... + */ + if (FacingDial->Get_Direction() != + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Get()) { + /* + ..................... Set body's facing ...................... + */ + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Set(FacingDial->Get_Direction()); + + /* + ............. Set turret facing, if there is one ............. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_UNIT) { + ((UnitClass *)CurrentObject[0])->SecondaryFacing.Set(FacingDial->Get_Direction()); + } + + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Facing + ---------------------------------------------------------------------*/ + case (POPUP_BASEPERCENT | KN_BUTTON): + if (BaseGauge->Get_Value() != BasePercent) { + BasePercent = BaseGauge->Get_Value(); + Build_Base_To(BasePercent); + HiddenPage.Clear(); + Flag_To_Redraw(true); + } + input = KN_NONE; + break; + + case (KN_LMOUSE): + input = KN_NONE; + break; + + default: + break; + } + + /* + ------------------------ Call parent's AI routine ------------------------ + */ + MouseClass::AI(input, x, y); +} + + +/*************************************************************************** + * MapEditClass::Draw_It -- overloaded Redraw routine * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Draw_It(bool forced) +{ + char const *label; + char buf[40]; + char const *tptr; + + MouseClass::Draw_It(forced); + + if (!Debug_Map) { + return; + } + + // + // Erase scrags at top of screen + // + LogicPage->Fill_Rect(0, 0, 640, 16, BLACK); + + /* + ** Display the total value of all Tiberium on the map. + */ + Fancy_Text_Print("Tiberium=%ld ", 0, 0, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, TotalValue); + + /*------------------------------------------------------------------------ + If there are no object controls displayed, just invoke parent's Redraw + and return. + ------------------------------------------------------------------------*/ + if (!Buttons) { + return; + } + + /*------------------------------------------------------------------------ + Otherwise, if 'display' is set, invoke the parent's Redraw to refresh + the HIDPAGE; then, update the buttons & text labels onto HIDPAGE; + then invoke the parent's Redraw to blit the HIDPAGE to SEENPAGE. + ------------------------------------------------------------------------*/ + if (forced) { + + /* + ....................... Update the text labels ........................ + */ + if (CurrentObject.Count()) { + /* + ------------------ Display the object's name & ID ------------------ + */ + label = Text_String(CurrentObject[0]->Full_Name()); + tptr = label; + sprintf(buf,"%s (%d)",tptr,CurrentObject[0]->As_Target()); + + /* + ......................... print the label .......................... + */ + Fancy_Text_Print (buf, 320, 0, CC_TAN, TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + } + } +} + + +/*************************************************************************** + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * * + * Reports whether the mouse has moved or not. This varies based on the * + * type of object currently selected. If there's an infantry object * + * selected, mouse motion counts even within a cell; for all other types,* + * mouse motion counts only if the mouse changes cells. * + * * + * The reason this routine is needed is to prevent Paint-Mode from putting* + * gobs of trees and such into the same cell if the mouse moves just * + * a little bit. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Mouse_Moved(void) +{ + static int old_mx = 0; + static int old_my = 0; + static CELL old_zonecell = 0; + const ObjectTypeClass * objtype = NULL; + bool retcode = false; + + /* + -------------------------- Return if no motion --------------------------- + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { + return(false); + } + + /* + ---------------------- Get a ptr to ObjectTypeClass ---------------------- + */ + if (PendingObject) { + objtype = PendingObject; + } else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } + + /* + --------------------- Check for motion based on type --------------------- + */ + /* + ............... Infantry: mouse moved if any motion at all ............... + */ + if (objtype->What_Am_I() == RTTI_INFANTRYTYPE) { + retcode = true; + } else { + /* + ................ Others: mouse moved only if cell changed ................ + */ + if (old_zonecell!=ZoneCell) { + retcode = true; + } else { + retcode = false; + } + } + + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(retcode); +} + + +/*************************************************************************** + * MapEditClass::Main_Menu -- main menu processor for map editor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Main_Menu(void) +{ + char const *_menus[MAX_MAIN_MENU_NUM + 1]; + int selection; // option the user picks + bool process; // menu stays up while true + int rc; + + /* + --------------------------- Fill in menu items --------------------------- + */ + _menus[0] = "New Scenario"; + _menus[1] = "Load Scenario"; + _menus[2] = "Save Scenario"; + _menus[3] = "Size Map"; + _menus[4] = "Add Game Object"; + _menus[5] = "Scenario Options"; + _menus[6] = "AI Options"; + _menus[7] = "Play Scenario"; + _menus[8] = NULL; + + /* + ----------------------------- Main Menu loop ----------------------------- + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ................ Invoke game callback, to update music ................ + */ + Call_Back(); + + /* + ............................. Invoke menu ............................. + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + .......................... Process selection .......................... + */ + switch (selection) { + /* + ........................... New scenario ........................... + */ + case 0: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (New_Scenario()==0) { + CarryOverMoney = 0; + Changed = 1; + } + process = false; + break; + + /* + .......................... Load scenario ........................... + */ + case 1: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (Load_Scenario()==0) { + CarryOverMoney = 0; + Changed = 0; + } + process = false; + break; + + /* + .......................... Save scenario ........................... + */ + case 2: + if (Save_Scenario() == 0) { + Changed = 0; + } + process = false; + break; + + /* + .......................... Edit map size ........................... + */ + case 3: + if (Size_Map(MapCellX, MapCellY, MapCellWidth, MapCellHeight)==0) { + process = false; + Changed = 1; + } + break; + + /* + .......................... Add an object ........................... + */ + case 4: + if (Placement_Dialog() == 0) { + Start_Placement(); + process = false; + } + break; + + /* + ......................... Scenario options ......................... + */ + case 5: + if (Scenario_Dialog() == 0) { + Changed = 1; + process = false; + } + break; + + /* + .......................... Other options ........................... + */ + case 6: + AI_Menu(); + process = false; + break; + + /* + ...................... Test-drive this scenario .................... + */ + case 7: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + Changed = 0; + Debug_Map = false; + Start_Scenario(ScenarioName); + return; + } + } + + /*------------------------------------------------------------------------ + Restore the display: + - Clear HIDPAGE to erase any spurious drawing done by the menu system + - Invoke Flag_To_Redraw to tell DisplayClass to re-render the whole screen + - Invoke Redraw() to update the display + ------------------------------------------------------------------------*/ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::AI_Menu -- menu of AI options * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + char const *_menus[MAX_AI_MENU_NUM + 1]; + + /* + -------------------------- Fill in menu strings -------------------------- + */ + _menus[0] = "Pre-Build a Base"; + _menus[1] = "Import Triggers"; + _menus[2] = "Edit Triggers"; + _menus[3] = "Import Teams"; + _menus[4] = "Edit Teams"; + _menus[5] = NULL; + + /* + ----------------------------- Main Menu loop ----------------------------- + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ................ Invoke game callback, to update music ................ + */ + Call_Back(); + + /* + ............................. Invoke menu ............................. + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + .......................... Process selection .......................... + */ + switch (selection) { + /* + ......................... Pre-Build a Base ......................... + */ + case 0: + Start_Base_Building(); + process = false; + break; + + /* + ......................... Import Triggers .......................... + */ + case 1: + if (Import_Triggers()==0) + process = false; + break; + + /* + ......................... Trigger Editing .......................... + */ + case 2: + Handle_Triggers(); + /* + ................ Go into trigger placement mode ................. + */ + if (CurTrigger) { + Start_Trigger_Placement(); + } + process = false; + break; + + /* + ........................... Import Teams ........................... + */ + case 3: + if (Import_Teams()==0) + process = false; + break; + + /* + ........................... Team Editing ........................... + */ + case 4: + Handle_Teams("Teams"); + process = false; + break; + } + } +} + + +/*************************************************************************** + * MapEditClass::Verify_House -- is this objtype ownable by this house? * + * * + * INPUT: * + * house house to check * + * objtype ObjectTypeClass to check * + * * + * OUTPUT: * + * 0 = isn't ownable, 1 = it is * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Verify_House(HousesType house, ObjectTypeClass const *objtype) +{ + /* + --------------- Verify that new house can own this object ---------------- + */ + return((objtype->Get_Ownable() & (1 << house)) != 0); +} + + +/*************************************************************************** + * MapEditClass::Cycle_House -- finds next valid house for object type * + * * + * INPUT: * + * objtype ObjectTypeClass ptr to get house for * + * curhouse current house value to start with * + * * + * OUTPUT: * + * HousesType that's valid for this object type * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +HousesType MapEditClass::Cycle_House(HousesType curhouse, + ObjectTypeClass const *objtype) +{ + HousesType count; // prevents an infinite loop + + /*------------------------------------------------------------------------ + Loop through all house types, starting with the one after 'curhouse'; + return the first one that's valid + ------------------------------------------------------------------------*/ + count = HOUSE_NONE; + while (1) { + + /* + .......................... Go to next house ........................... + */ + curhouse++; + if (curhouse == HOUSE_COUNT) { + curhouse = HOUSE_FIRST; + } + + /* + ................ Count # iterations; don't go forever ................. + */ + count++; + if (count == HOUSE_COUNT) { + curhouse = HOUSE_NONE; + break; + } + + /* + ................... Break if this is a valid house .................... + */ + if (HouseClass::As_Pointer(curhouse) && Verify_House(curhouse,objtype)) { + break; + } + } + + return(curhouse); +} + + +/*************************************************************************** + * MapEditClass::Fatal -- exits with error message * + * * + * INPUT: * + * code tells which message to display; this minimizes the * + * use of character strings in the code. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Fatal(int txt) +{ + Prog_End(); + printf("%s\n",txt); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } +} + + +bool MapEditClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (Debug_Map) { + /* + ** The popup gadgets require the entire map to be redrawn if we scroll. + */ + if (really) { + Flag_To_Redraw(true); + } + } + return(MouseClass::Scroll_Map(facing, distance, really)); +} + + +void MapEditClass::Detach(ObjectClass * object) +{ + if (GrabbedObject == object) { + GrabbedObject = 0; + } +} + + +#endif + +#include "mapedsel.cpp" \ No newline at end of file diff --git a/TIBERIANDAWN/MAPEDIT.H b/TIBERIANDAWN/MAPEDIT.H new file mode 100644 index 000000000..598c3c345 --- /dev/null +++ b/TIBERIANDAWN/MAPEDIT.H @@ -0,0 +1,352 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapedit.h_v 2.19 16 Oct 1995 16:46:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 14, 1994 * + * * + * Last Update : May 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * This class is derived from the normal display map class. It exists * + * only to allow editing and adding items to the map. * + *---------------------------------------------------------------------------------------------* + * House-setting functions: The editor contains several house maintenance routines: * + * Verify_House: tells if the given ObjectType can be owned by the given HousesType * + * Cycle_House: Finds the next valid house for the given ObjectType; used when a new object * + * can't be owned by the current editor HousesType. * + * Change_House: attempts to change the owner of the currently-selected object * + * Toggle_House: cycles the HousesType of a pending placement object * + * Set_House_Buttons: sets house buttons in accordance with the given HousesType * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAPEDIT_H +#define MAPEDIT_H + +/* +********************************* Includes ********************************** +*/ +#include "function.h" + +/* +********************************** Defines ********************************** +*/ +/*........................................................................... +This is the maximum # of ObjectTypeClasses the editor has to deal with. +...........................................................................*/ +enum MapEdit1Enum { + MAX_EDIT_OBJECTS = // max # of ObjectTypeClasses allowed + (int)TEMPLATE_COUNT + + (int)OVERLAY_COUNT + + (int)SMUDGE_COUNT + + (int)TERRAIN_COUNT + + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT + + (int)STRUCT_COUNT, + + MAX_TEAM_CLASSES = // max # ObjectTypeClasses for a team + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT, + +// NUM_EDIT_MISSIONS = 6, // # missions that can be assigned an object + + NUM_EDIT_CLASSES = 8, // # different classes (templates, terrain, etc) + + MAX_MAIN_MENU_NUM = 8, + MAX_MAIN_MENU_LEN = 20, + + MAX_AI_MENU_NUM = 6, + MAX_AI_MENU_LEN = 20, + + POPUP_GDI_W = 100, + POPUP_GDI_H = 18, + POPUP_GDI_X = 20, + POPUP_GDI_Y = 320, + + POPUP_NOD_W = 100, + POPUP_NOD_H = 18, + POPUP_NOD_X = 20, + POPUP_NOD_Y = 338, + + POPUP_NEUTRAL_W = 100, + POPUP_NEUTRAL_H = 18, + POPUP_NEUTRAL_X = 20, + POPUP_NEUTRAL_Y = 356, + + POPUP_MULTI1_W = 50, + POPUP_MULTI1_H = 18, + POPUP_MULTI1_X = 20, + POPUP_MULTI1_Y = 320, + + POPUP_MULTI2_W = 50, + POPUP_MULTI2_H = 18, + POPUP_MULTI2_X = 70, + POPUP_MULTI2_Y = 320, + + POPUP_MULTI3_W = 50, + POPUP_MULTI3_H = 18, + POPUP_MULTI3_X = 20, + POPUP_MULTI3_Y = 330, + + POPUP_MULTI4_W = 50, + POPUP_MULTI4_H = 18, + POPUP_MULTI4_X = 70, + POPUP_MULTI4_Y = 338, + + POPUP_MISSION_W = 160, + POPUP_MISSION_H = 80, + POPUP_MISSION_X = 140, + POPUP_MISSION_Y = 300, + + POPUP_FACEBOX_W = 60, + POPUP_FACEBOX_H = 60, + POPUP_FACEBOX_X = 320, + POPUP_FACEBOX_Y = 320, + + POPUP_HEALTH_W = 100, + POPUP_HEALTH_H = 20, + POPUP_HEALTH_X = 400, + POPUP_HEALTH_Y = 340, + + POPUP_BASE_W = 100, + POPUP_BASE_H = 16, + POPUP_BASE_X = 600 - POPUP_BASE_W, + POPUP_BASE_Y = 0, +}; + +/*........................................................................... +These are the button ID's for the pop-up object-editing gizmos. +The house button ID's must be sequential, with a 1-to-1 correspondence to +the HousesType values. +...........................................................................*/ +enum MapEditButtonIDEnum{ + POPUP_GDI=500, // GDI house button + POPUP_NOD, // NOD house button + POPUP_NEUTRAL, // Neutral house button + POPUP_HOUSE_JP, // not used + POPUP_MULTI1, // Multiplayer 1 house button + POPUP_MULTI2, // Multiplayer 2 house button + POPUP_MULTI3, // Multiplayer 3 house button + POPUP_MULTI4, // Multiplayer 4 house button + POPUP_MULTI5, // Multiplayer 4 house button + POPUP_MULTI6, // Multiplayer 4 house button + POPUP_MISSIONLIST, // list box for missions + POPUP_HEALTHGAUGE, // health of object + POPUP_FACINGDIAL, // object's facing + POPUP_BASEPERCENT, // Base's percent-built slider + MAP_AREA, // map as a click-able thingy + BUTTON_FLAG=0x8000 +}; + + +/* +******************************* Declarations ******************************** +*/ +class TeamTypeClass; + +/* +***************************** Class Declaration ***************************** +*/ +class MapEditClass : public MouseClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ............................. mapedit.cpp ............................. + */ + MapEditClass(void); + virtual void One_Time(void); // One-time init + virtual void Init_IO(void); // Inits button list + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool forced = true); + virtual bool Scroll_Map(DirType facing, int & distance, bool really=true); +// virtual void Flag_To_Redraw(bool complete); + virtual void Read_INI(char *buffer); + virtual void Write_INI(char *buffer); + virtual void Detach(ObjectClass * object); + void Clear_List(void); + bool Add_To_List(ObjectTypeClass const *object); + void Main_Menu(void); + void AI_Menu(void); + bool Mouse_Moved(void); + bool Verify_House(HousesType house, ObjectTypeClass const * objtype); + HousesType Cycle_House(HousesType curhouse, ObjectTypeClass const * objtype); +// int Trigger_Needs_Team(TriggerClass *trigger); + void Fatal(int txt); + + /* + ............................ mapeddlg.cpp ............................. + */ + int New_Scenario(void); + int Load_Scenario(void); + int Save_Scenario(void); + int Pick_Scenario(char const * caption, int *scen_nump, + ScenarioPlayerType *playerp, ScenarioDirType *dirp, + ScenarioVarType *varp, int multi); + int Size_Map(int x, int y, int w, int h); + int Scenario_Dialog(void); + void Handle_Triggers(void); + int Select_Trigger(void); + int Edit_Trigger(void); + int Import_Triggers(void); + int Import_Teams(void); + /* + ............................ mapedplc.cpp ............................. + */ + int Placement_Dialog(void); + void Start_Placement(void); + int Place_Object(void); + void Cancel_Placement(void); + void Place_Next(void); + void Place_Prev(void); + void Place_Next_Category(void); + void Place_Prev_Category(void); + void Place_Home(void); + void Toggle_House(void); + void Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id); + void Start_Trigger_Placement(void); + void Stop_Trigger_Placement(void); + void Place_Trigger(void); + void Start_Base_Building(void); + void Cancel_Base_Building(void); + void Build_Base_To(int percent); + + /* + ............................ mapedsel.cpp ............................. + */ + int Select_Object(void); + void Select_Next(void); + void Popup_Controls(void); + void Grab_Object(void); + int Move_Grabbed_Object(void); + bool Change_House(HousesType newhouse); + + /* + ............................. mapedtm.cpp ............................. + */ + void Draw_Member(TechnoTypeClass const * ptr, int index, + int quant, HousesType house, int pic_x, int pic_y); + void Handle_Teams(char const * caption); + int Select_Team(char const * caption); + int Edit_Team(void); + int Team_Members(HousesType house); + void Build_Mission_List(int missioncount, TeamMissionStruct *missions, + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20], ListClass *list); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This is the last-requested variation of a loaded/saved/new scenario. + .....................................................................*/ + ScenarioVarType ScenVar; + + /*..................................................................... + Array of all TypeClasses the user can add to the map; cleared by + Clear_List(), added to by Add_To_List() + .....................................................................*/ + ObjectTypeClass const * Objects[MAX_EDIT_OBJECTS]; + int ObjCount; // # of objects in the Objects array + + /*..................................................................... + Last-selected object to place, and last-selected house of object + .....................................................................*/ + int LastChoice; // index of item user picked last + HousesType LastHouse; // house of last item picked + + /*..................................................................... + Variables for grabbing/moving objects + .....................................................................*/ + ObjectClass * GrabbedObject; // object "grabbed" with mouse + CELL GrabOffset; // offset to grabbed obj's upper-left + unsigned long LastClickTime; // time of last LMOUSE click + + /*..................................................................... + Number of each type of object in Objects, so we can switch categories + .....................................................................*/ + int NumType[NUM_EDIT_CLASSES]; // # of each type of class: + // 0 = Template + // 1 = Overlay + // 2 = Smudge + // 3 = Terrain + // 4 = Unit + // 5 = Infantry + // 6 = Aircraft + // 7 = Building + + /*..................................................................... + The offset of each type of object within the Objects[] array + .....................................................................*/ + int TypeOffset[NUM_EDIT_CLASSES]; // offsets within Objects[] + + /*..................................................................... + The "current" trigger for point-and-click trigger setting + .....................................................................*/ + TriggerClass * CurTrigger; // current trigger + + /*..................................................................... + The "current" team type for editing & associating with a trigger + .....................................................................*/ + TeamTypeClass * CurTeam; // current team + + /*..................................................................... + Bitfields for flags & such + .....................................................................*/ + int Changed : 1; // 1 = changes are unsaved + int LMouseDown : 1; // 1 = left mouse is held down + int BaseBuilding : 1; // 1 = we're in base-building mode + + /*..................................................................... + Variables for pre-building a base + .....................................................................*/ + int BasePercent; // Percentage the base will be built + + /*..................................................................... + Variables for supporting the object-editing controls at screen bottom + .....................................................................*/ + TextButtonClass *GDIButton; + TextButtonClass *NODButton; + TextButtonClass *NeutralButton; + TextButtonClass *Multi1Button; + TextButtonClass *Multi2Button; + TextButtonClass *Multi3Button; + TextButtonClass *Multi4Button; + ListClass *MissionList; + TriColorGaugeClass *HealthGauge; + Dial8Class *FacingDial; + ControlClass *MapArea; + TextLabelClass *HealthText; + static char HealthBuf[20]; + GaugeClass *BaseGauge; + TextLabelClass *BaseLabel; + static MissionType MapEditMissions[]; +}; + +#endif diff --git a/TIBERIANDAWN/MAPEDPLC.CPP b/TIBERIANDAWN/MAPEDPLC.CPP new file mode 100644 index 000000000..b8d74a034 --- /dev/null +++ b/TIBERIANDAWN/MAPEDPLC.CPP @@ -0,0 +1,1996 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapedplc.cpv 2.16 16 Oct 1995 16:51:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDPLC.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : July 4, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-placement routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * MapEditClass::Start_Placement -- enters placement mode * + * MapEditClass::Place_Object -- attempts to place the current object * + * MapEditClass::Cancel_Placement -- cancels placement mode * + * MapEditClass::Place_Next -- while placing object, goes to next * + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * MapEditClass::Place_Next_Category -- places next object category * + * MapEditClass::Place_Prev_Category -- places previous object category * + * MapEditClass::Place_Home -- homes the placement object * + * MapEditClass::Toggle_House -- toggles current placement object's house* + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode* + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * MapEditClass::Start_Base_Building -- starts base-building mode * + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * MapEditClass::Build_Base_To -- builds the AI base to the given percent* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * * + * This function sets LastChoice & LastHouse to the values chosen * + * by the user. It's up to the caller to call Start_Placement to enter * + * placement mode. * + * This routine does not modify PendingObject or PendingHouse. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ [GDI] [NOD] [Neutral] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ [Template] ³ * + * ³ ³ ³ [Overlay ] ³ * + * ³ ³ ³ [Smudge ] ³ * + * ³ ³ ³ [Terrain ] ³ * + * ³ ³ (Object picture) ³ [Unit ] ³ * + * ³ ³ ³ [Infantry] ³ * + * ³ ³ ³ [Aircraft] ³ * + * ³ ³ ³ [Building] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄ¿ ³ * + * ³ [<-] [->] ³(Grid)³ ³ * + * ³ ³ ³ ³ * + * ³ [OK] [Cancel] ÀÄÄÄÄÄÄÙ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Placement_Dialog(void) +{ + HousesType house; + + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, + D_DIALOG_H = 360, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_PICTURE_W = 304, // must be divisible by 8! + D_PICTURE_H = 210, + D_PICTURE_X = D_DIALOG_X + 16, // must start on a byte boundary! + D_PICTURE_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + D_PICTURE_CX = D_PICTURE_X + D_PICTURE_W / 2, + + D_GDI_W = 90, + D_GDI_H = 18, + D_GDI_X = D_DIALOG_X + D_MARGIN, + D_GDI_Y = D_DIALOG_Y + D_MARGIN, + + D_NOD_W = 90, + D_NOD_H = 18, + D_NOD_X = D_GDI_X + D_GDI_W, + D_NOD_Y = D_DIALOG_Y + D_MARGIN, + + D_NEUTRAL_W = 90, + D_NEUTRAL_H = 18, + D_NEUTRAL_X = D_NOD_X + D_NOD_W, + D_NEUTRAL_Y = D_DIALOG_Y + D_MARGIN, + + D_MULTI1_W = 44, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 44, + D_MULTI2_H = 18, + D_MULTI2_X = D_MULTI1_X + D_MULTI1_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 44, + D_MULTI3_H = 18, + D_MULTI3_X = D_MULTI2_X + D_MULTI2_W, + D_MULTI3_Y = D_GDI_Y, + + D_MULTI4_W = 44, + D_MULTI4_H = 18, + D_MULTI4_X = D_MULTI3_X + D_MULTI3_W, + D_MULTI4_Y = D_GDI_Y, + + D_LEFT_W = 90, + D_LEFT_H = 18, + D_LEFT_X = D_PICTURE_CX - 5 - D_LEFT_W, + D_LEFT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_RIGHT_W = 90, + D_RIGHT_H = 18, + D_RIGHT_X = D_PICTURE_CX + 5, + D_RIGHT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_TEMPLATE_W = 140, + D_TEMPLATE_H = 18, + D_TEMPLATE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TEMPLATE_W, + D_TEMPLATE_Y = D_PICTURE_Y, + + D_OVERLAY_W = 140, + D_OVERLAY_H = 18, + D_OVERLAY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_OVERLAY_W, + D_OVERLAY_Y = D_TEMPLATE_Y + D_TEMPLATE_H, + + D_SMUDGE_W = 140, + D_SMUDGE_H = 18, + D_SMUDGE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_SMUDGE_W, + D_SMUDGE_Y = D_OVERLAY_Y + D_OVERLAY_H, + + D_TERRAIN_W = 140, + D_TERRAIN_H = 18, + D_TERRAIN_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TERRAIN_W, + D_TERRAIN_Y = D_SMUDGE_Y + D_SMUDGE_H, + + D_UNIT_W = 140, + D_UNIT_H = 18, + D_UNIT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_UNIT_W, + D_UNIT_Y = D_TERRAIN_Y + D_TERRAIN_H, + + D_INFANTRY_W = 140, + D_INFANTRY_H = 18, + D_INFANTRY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_INFANTRY_W, + D_INFANTRY_Y = D_UNIT_Y + D_UNIT_H, + + D_AIRCRAFT_W = 140, + D_AIRCRAFT_H = 18, + D_AIRCRAFT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIRCRAFT_W, + D_AIRCRAFT_Y = D_INFANTRY_Y + D_INFANTRY_H, + + D_BUILDING_W = 140, + D_BUILDING_H = 18, + D_BUILDING_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_BUILDING_W, + D_BUILDING_Y = D_AIRCRAFT_Y + D_AIRCRAFT_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_PICTURE_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_PICTURE_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + + }; + /*........................................................................ + Grid Dimensions + ........................................................................*/ + enum { + GRIDSIZE = 10, + GRIDBLOCK_W = 6, + GRIDBLOCK_H = 6, + D_GRID_X = D_DIALOG_X + D_DIALOG_W - (GRIDSIZE * GRIDBLOCK_W) - D_MARGIN, + D_GRID_Y = D_DIALOG_Y + D_DIALOG_H - (GRIDSIZE * GRIDBLOCK_H) - D_MARGIN, + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_NEXT, + BUTTON_PREV, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_TEMPLATE, + BUTTON_OVERLAY, + BUTTON_SMUDGE, + BUTTON_TERRAIN, + BUTTON_UNIT, + BUTTON_INFANTRY, + BUTTON_AIRCRAFT, + BUTTON_BUILDING, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_OBJECT, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // display level + bool process; // loop while true + bool cancel = false; // true = user cancels + const ObjectTypeClass * curobj; // Working object pointer. + int x,y; // for drawing the grid + KeyNumType input; // user input + short const *occupy; // ptr into object's OccupyList + int cell; // cell index for parsing OccupyList + int i; + int typeindex; // index of class type + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands; + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neutbtn (BUTTON_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEUTRAL_X, D_NEUTRAL_Y, D_NEUTRAL_W, D_NEUTRAL_H); + + TextButtonClass multi1btn (BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn (BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn (BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn (BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass nextbtn (BUTTON_NEXT, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_RIGHT_X, D_RIGHT_Y, D_RIGHT_W, D_RIGHT_H); + + TextButtonClass prevbtn (BUTTON_PREV, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEFT_X, D_LEFT_Y, D_LEFT_W, D_LEFT_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + TextButtonClass templatebtn (BUTTON_TEMPLATE, "Template", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TEMPLATE_X, D_TEMPLATE_Y, D_TEMPLATE_W, D_TEMPLATE_H); + + TextButtonClass overlaybtn (BUTTON_OVERLAY, "Overlay", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OVERLAY_X, D_OVERLAY_Y, D_OVERLAY_W, D_OVERLAY_H); + + TextButtonClass smudgebtn (BUTTON_SMUDGE, "Smudge", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SMUDGE_X, D_SMUDGE_Y, D_SMUDGE_W, D_SMUDGE_H); + + TextButtonClass terrainbtn (BUTTON_TERRAIN, "Terrain", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TERRAIN_X, D_TERRAIN_Y, D_TERRAIN_W, D_TERRAIN_H); + + TextButtonClass unitbtn (BUTTON_UNIT, "Unit", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_UNIT_X, D_UNIT_Y, D_UNIT_W, D_UNIT_H); + + TextButtonClass infantrybtn (BUTTON_INFANTRY, "Infantry", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INFANTRY_X, D_INFANTRY_Y, D_INFANTRY_W, D_INFANTRY_H); + + TextButtonClass aircraftbtn (BUTTON_AIRCRAFT, "Aircraft", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_AIRCRAFT_X, D_AIRCRAFT_Y, D_AIRCRAFT_W, D_AIRCRAFT_H); + + TextButtonClass buildingbtn (BUTTON_BUILDING, "Building", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_BUILDING_X, D_BUILDING_Y, D_BUILDING_W, D_BUILDING_H); + + /*------------------------------------------------------------------------ + Initialize addable objects list; we must do this every time in case one + of the object pools has become exhausted; that object won't be available + for adding. (Skip aircraft, since they won't be used in the editor.) + ------------------------------------------------------------------------*/ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + + /*........................................................................ + Compute offset of each class type in the Objects array + ........................................................................*/ + TypeOffset[0] = 0; + for (i=1; i= ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; // current object to choose + + commands = &neutbtn; + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + multi1btn.Add_Tail(*commands); + multi2btn.Add_Tail(*commands); + multi3btn.Add_Tail(*commands); + multi4btn.Add_Tail(*commands); + } else { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + } + nextbtn.Add_Tail(*commands); + prevbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + templatebtn.Add_Tail(*commands); + overlaybtn.Add_Tail(*commands); + smudgebtn.Add_Tail(*commands); + terrainbtn.Add_Tail(*commands); + unitbtn.Add_Tail(*commands); + infantrybtn.Add_Tail(*commands); + aircraftbtn.Add_Tail(*commands); + buildingbtn.Add_Tail(*commands); + + /*........................................................................ + If the current house isn't valid for the current object type, cycle to + the next house. + ........................................................................*/ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + } + + /* + ..................... Set the buttons for this house ..................... + */ + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + + /* + -------------------------- Main processing loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ---------------------- Refresh display if needed ---------------------- + */ + if (display > REDRAW_NONE) { + /* + ---------------------- Display the dialog box ---------------------- + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + } + + /*------------------------------------------------------------------ + Display the current object: + - save the current window dimensions + - adjust the window size to the actual drawable area + - draw the shape + - reset the window dimensions + ------------------------------------------------------------------*/ + if (display >= REDRAW_OBJECT) { + WindowList[WINDOW_EDITOR][WINDOWX] = D_PICTURE_X >> 3; + WindowList[WINDOW_EDITOR][WINDOWY] = D_PICTURE_Y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W >> 3; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + Draw_Box(D_PICTURE_X, D_PICTURE_Y, D_PICTURE_W, D_PICTURE_H, + BOXSTYLE_GREEN_DOWN, true); + curobj->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, LastHouse); + + /* + ........................ Erase the grid ......................... + */ + LogicPage->Fill_Rect(D_GRID_X - GRIDBLOCK_W * 2, D_GRID_Y, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, BLACK); + + /* + .............. Draw a box for every cell occupied ............... + */ + occupy = curobj->Occupy_List(); + while ( (*occupy) != REFRESH_EOL) { + cell = (*occupy); + occupy++; + x = D_GRID_X + ((cell % MAP_CELL_W) * GRIDBLOCK_W); + y = D_GRID_Y + ((cell / MAP_CELL_W) * GRIDBLOCK_H); + LogicPage->Fill_Rect(x, y, x + GRIDBLOCK_W - 1, y + GRIDBLOCK_H - 1, + CC_BRIGHT_GREEN); + } + + /* + ..................... Draw the grid itself ...................... + */ + for (y = 0; y <= GRIDSIZE; y++) { + for (x = 0; x <= GRIDSIZE; x++) { + LogicPage->Draw_Line(D_GRID_X + x * GRIDBLOCK_W, D_GRID_Y, + D_GRID_X + x * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, CC_GREEN_SHADOW); + } + LogicPage->Draw_Line(D_GRID_X, D_GRID_Y + y * GRIDBLOCK_H, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, D_GRID_Y + y * GRIDBLOCK_H, + CC_GREEN_SHADOW); + } + + /*............................................................... + Print the object's label from the class's Full_Name(). + Warning: Text_String returns an EMS pointer, so standard string + functions won't work! + ...............................................................*/ + Fancy_Text_Print (curobj->Full_Name(), + D_PICTURE_CX, D_PICTURE_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + -------------------------- Redraw buttons -------------------------- + */ + if (display >= REDRAW_BUTTONS) { + /*............................................................... + Figure out which class category we're in & highlight that button + This updates 'typeindex', which is used below, and it also updates + the category button states. + ...............................................................*/ + i = 0; + for (typeindex = 0; typeindex < NUM_EDIT_CLASSES; typeindex++) { + i += NumType[typeindex]; + if (LastChoice < i) break; + } + templatebtn.Turn_Off(); + overlaybtn.Turn_Off(); + smudgebtn.Turn_Off(); + terrainbtn.Turn_Off(); + unitbtn.Turn_Off(); + infantrybtn.Turn_Off(); + aircraftbtn.Turn_Off(); + buildingbtn.Turn_Off(); + switch (typeindex + BUTTON_TEMPLATE) { + case BUTTON_TEMPLATE: + templatebtn.Turn_On(); + break; + + case BUTTON_OVERLAY: + overlaybtn.Turn_On(); + break; + + case BUTTON_SMUDGE: + smudgebtn.Turn_On(); + break; + + case BUTTON_TERRAIN: + terrainbtn.Turn_On(); + break; + + case BUTTON_UNIT: + unitbtn.Turn_On(); + break; + + case BUTTON_INFANTRY: + infantrybtn.Turn_On(); + break; + + case BUTTON_AIRCRAFT: + aircraftbtn.Turn_On(); + break; + + case BUTTON_BUILDING: + buildingbtn.Turn_On(); + break; + } + } + + /* + .......................... Redraw buttons .......................... + */ + commands->Draw_All(); + Show_Mouse(); + display = REDRAW_NONE; + + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ------------------------- Process user input -------------------------- + */ + switch (input) { + /* + ---------------------------- GDI House ----------------------------- + */ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + /* + ............... ignore if invalid for this object ............... + */ + if (!Verify_House(house,curobj)) { + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + break; + } + + /* + ...................... Set flags & buttons ...................... + */ + LastHouse = house; + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + display = REDRAW_OBJECT; + break; + + /* + --------------------------- Next in list --------------------------- + */ + case (KN_RIGHT): + case (BUTTON_NEXT | KN_BUTTON): + /* + ..................... Increment to next obj ..................... + */ + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + nextbtn.Turn_Off(); + display = REDRAW_OBJECT; + break; + + /* + ------------------------- Previous in list ------------------------- + */ + case (KN_LEFT): + case (BUTTON_PREV | KN_BUTTON): + /* + ..................... Decrement to prev obj ..................... + */ + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount-1; + } + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + prevbtn.Turn_Off(); + display = REDRAW_OBJECT; + break; + + /* + ----------------------- Select a class type ------------------------ + */ + case (BUTTON_TEMPLATE | KN_BUTTON): + case (BUTTON_OVERLAY | KN_BUTTON): + case (BUTTON_SMUDGE | KN_BUTTON): + case (BUTTON_TERRAIN | KN_BUTTON): + case (BUTTON_UNIT | KN_BUTTON): + case (BUTTON_INFANTRY | KN_BUTTON): + case (BUTTON_AIRCRAFT | KN_BUTTON): + case (BUTTON_BUILDING | KN_BUTTON): + /* + ...................... Find index of class ...................... + */ + typeindex = input - (BUTTON_TEMPLATE | KN_BUTTON); + + /* + ............ If no objects of that type, do nothing ............. + */ + if (NumType[typeindex]==0) { + display = REDRAW_BUTTONS; // force to reset button states + break; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + -------------------------- Next category --------------------------- + */ + case KN_PGDN: + typeindex++; + if (typeindex==NUM_EDIT_CLASSES) { + typeindex = 0; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + ------------------------ Previous category ------------------------- + */ + case KN_PGUP: + typeindex--; + if (typeindex < 0) { + typeindex = NUM_EDIT_CLASSES - 1; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + ------------------------ Jump to 1st choice ------------------------ + */ + case KN_HOME: + LastChoice = 0; + /* + ...................... Set current object ....................... + */ + curobj = Objects[LastChoice]; + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + display = REDRAW_OBJECT; + break; + + /* + -------------------------------- OK -------------------------------- + */ + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /* + ------------------------------ Cancel ------------------------------ + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Start_Placement -- enters placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Placement(void) +{ + int i; + + /*------------------------------------------------------------------------ + Initialize addable objects list; we must do this every time in case one + of the object pools has become exhausted; that object won't be available + for adding. + ------------------------------------------------------------------------*/ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + //AircraftTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + /*........................................................................ + Compute offset of each class type in the Objects array + ........................................................................*/ + TypeOffset[0] = 0; + for (i=1; i= ObjCount) + LastChoice = ObjCount - 1; + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } else { + if (LastChoice < TypeOffset[7]) + LastChoice = TypeOffset[7]; + if (LastChoice >= ObjCount) + LastChoice = ObjCount - 1; + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse = Base.House; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } + + + /* + ------------------- Error if no more objects available ------------------- + */ + if (!PendingObjectPtr) { + CCMessageBox().Process("No more objects of this type available."); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + PendingObject = NULL; + if (BaseBuilding) + Cancel_Base_Building(); + return; + } + + /* + ------------------------ Set the placement cursor ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(PendingObject->Occupy_List()); +} + + +/*************************************************************************** + * MapEditClass::Place_Object -- attempts to place the current object * + * * + * Placement of "real" objects is simply checked via their Unlimbo routine.* + * Placement of templates is more complex: * + * - for every cell in the template's OccupyList, check for objects * + * already in that cell by looking at the cell's OccupyList & * + * OverlapList * + * - "lift" all the objects in the cell by Mark'ing them * + * - temporarily place the template in that cell * + * - try to Unlimbo all the objects that were in the cell. If any * + * objects fail to Unlimbo onto that template, the template cannot * + * be placed here * + * * + * It is assumed that the object being placed is a "new" object; the * + * object's strength & mission are not set during Unlimbo. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = unable to place * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Place_Object(void) +{ + CELL template_cell; // cell being checked for template + COORDINATE obj_coord; // coord of occupier object + int okflag; // OK to place a template? + short const *occupy; // ptr into template's OccupyList + ObjectClass *occupier; // occupying object + TemplateType save_ttype; // for saving cell's TType + unsigned char save_ticon; // for saving cell's TIcon + BaseNodeClass node; // for adding to an AI Base + + /*------------------------------------------------------------------------ + Placing a template: + - first lift up any objects in the cell + - place the template, and try to replace the objects; if they won't go + back, the template can't go there + ------------------------------------------------------------------------*/ + //ScenarioInit++; + if (PendingObject->What_Am_I() == RTTI_TEMPLATETYPE) { + + /* + .......... Loop through all cells this template will occupy ........... + */ + okflag = true; + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ................. Check this cell for an occupier .................. + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + if ((*this)[template_cell].Cell_Occupier()) { + occupier = (*this)[template_cell].Cell_Occupier(); + + /* + .................. Save object's coordinates .................... + */ + obj_coord = occupier->Coord; + + /* + ................... Place the object in limbo ................... + */ + occupier->Mark(MARK_UP); + + /* + ................ Set the cell's template values ................. + */ + save_ttype = (*this)[template_cell].TType; + save_ticon = (*this)[template_cell].TIcon; + (*this)[template_cell].TType = + ((TemplateTypeClass *)PendingObject)->Type; + (*this)[template_cell].TIcon = Cell_X(*occupy) + Cell_Y(*occupy) * + ((TemplateTypeClass *)PendingObject)->Width; + (*this)[template_cell].Recalc_Attributes(); + /* + ................ Try to put the object back down ................ + */ + if (occupier->Can_Enter_Cell(Coord_Cell(obj_coord)) != MOVE_OK) { + okflag = false; + } + + /* + .............. Put everything back the way it was ............... + */ + (*this)[template_cell].TType = save_ttype; + (*this)[template_cell].TIcon = save_ticon; + (*this)[template_cell].Recalc_Attributes(); + + /* + .......... Major error if can't replace the object now .......... + */ + occupier->Mark(MARK_DOWN); + } + occupy++; + } + + /* + ......... If it's still OK after ALL THAT, place the template ......... + */ + if (okflag) { + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + /*............................................................... + Loop through all cells occupied by this template, and clear the + smudge & overlay. + ...............................................................*/ + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + /* + ............... Get cell for this occupy item ................ + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + + /* + ................... Clear smudge & overlay ................... + */ + (*this)[template_cell].Overlay = OVERLAY_NONE; + (*this)[template_cell].OverlayData = 0; + (*this)[template_cell].Smudge = SMUDGE_NONE; + + /* + ............ make adjacent cells recalc attrib's ............. + */ + (*this)[template_cell].Recalc_Attributes(); + (*this)[template_cell].Wall_Update(); + (*this)[template_cell].Concrete_Calc(); + + occupy++; + } + + /* + ......................... Set flags etc ......................... + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + TotalValue = Overpass(); + Flag_To_Redraw(false); + return(0); + } + + /* + ** Failure to deploy results in a returned failure code. + */ + //ScenarioInit--; + return(-1); + } + + /* + ........................ Not OK; return error ......................... + */ + //ScenarioInit--; + return(-1); + } + + /*------------------------------------------------------------------------ + Placing infantry: Infantry can go into cell sub-positions, so find the + sub-position closest to the mouse & put him there + ------------------------------------------------------------------------*/ + if (PendingObject->What_Am_I() == RTTI_INFANTRYTYPE) { + /* + ....................... Find cell sub-position ........................ + */ + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(),Get_Mouse_Y()))) { + obj_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), + Get_Mouse_Y())); + } else { + obj_coord = NULL; + } + + /* + ................ No free spots; don't place the object ................ + */ + if (obj_coord == NULL) { + //ScenarioInit--; + return(-1); + } + + /* + ......................... Unlimbo the object .......................... + */ + if (PendingObjectPtr->Unlimbo(obj_coord)) { + ((InfantryClass *)PendingObjectPtr)->Set_Occupy_Bit(obj_coord); +// Map[Coord_Cell(obj_coord)].Flag.Composite |= +// (1 << CellClass::Spot_Index(obj_coord)); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + //ScenarioInit--; + return(-1); + } + + /*------------------------------------------------------------------------ + Placing an object + ------------------------------------------------------------------------*/ + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Update the Tiberium computation if we're placing an overlay + */ + if (PendingObject->What_Am_I() == RTTI_OVERLAYTYPE && + ((OverlayTypeClass *)PendingObject)->IsTiberium) { + + TotalValue = Overpass(); + Flag_To_Redraw(false); + } + + /* + ** If we're building a base, add this building to the base's Node list. + */ + if (BaseBuilding && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE) { + node.Type = ((BuildingTypeClass *)PendingObject)->Type; + node.Coord = PendingObjectPtr->Coord; + Base.Nodes.Add(node); + } + + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + return(-1); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Placement -- cancels placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Placement(void) +{ + /* + ---------------------- Delete the placement object ----------------------- + */ + delete PendingObjectPtr; + PendingObject = 0; + PendingObjectPtr = 0; + PendingHouse = HOUSE_NONE; + + /* + -------------------------- Restore cursor shape -------------------------- + */ + Set_Cursor_Shape(0); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next -- while placing object, goes to next * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Increments LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * incrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + /* + ................. Go to next object in Objects list ................... + */ + LastChoice++; + if (LastChoice == ObjCount) { + /* + ** If we're in normal placement mode, wrap to the 1st object; + ** if we're in base-building mode, wrap to the 1st building + */ + if (!BaseBuilding) { + LastChoice = 0; + } else { + LastChoice = TypeOffset[7]; + } + } + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + /* + ** If we're in normal placement mode, change the current + ** placement house to the one that can own this object. + ** If we're building a base, skip ahead to the next object if the + ** base's house can't own this one. + */ + if (!BaseBuilding) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } else { + continue; + } + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Decrements LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * decrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + + /* + ................. Go to prev object in Objects list .................. + */ + LastChoice--; + /* + ** If we're in normal placement mode, wrap at the 1st object. + ** If we're building a base, wrap at the 1st building. + */ + if (!BaseBuilding) { + if (LastChoice < 0) + LastChoice = ObjCount - 1; + } else { + if (LastChoice < TypeOffset[7]) + LastChoice = ObjCount - 1; + } + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + /* + ** If we're in normal placement mode, change the current + ** placement house to the one that can own this object. + ** If we're building a base, skip ahead to the next object if the + ** base's house can't own this one. + */ + if (!BaseBuilding) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } else { + continue; + } + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next_Category -- places next category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Go to next category in Objects list ------------------- + */ + i = LastChoice; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i++; + if (i == ObjCount) { + i = 0; + } + } + LastChoice = i; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev_Category -- places previous category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Go to prev category in Objects list ------------------- + */ + i = LastChoice; + /* + ..................... Scan for the previous category ..................... + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + /* + .................... Scan for start of this category ..................... + */ + LastChoice = i; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + LastChoice = i; + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Home -- homes the placement object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Home(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + /* + ------------------ Loop until we create a valid object ------------------- + */ + LastChoice = 0; + while (!PendingObjectPtr) { + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Toggle_House -- toggles current placement object's house * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Toggle_House(void) +{ + TechnoClass *tp; + + /* + ** Don't allow this command if we're building a base; the only valid + ** house for base-building is the one assigned to the base. + */ + if (BaseBuilding) { + return; + } + + /*------------------------------------------------------------------------ + Only techno objects can be owned by a house; return if not a techno + ------------------------------------------------------------------------*/ + if (!PendingObjectPtr->Is_Techno()) { + return; + } + + /*------------------------------------------------------------------------ + Select the house that will own this object + ------------------------------------------------------------------------*/ + LastHouse = Cycle_House(PendingObjectPtr->Owner(), PendingObject); + + /*------------------------------------------------------------------------ + Change the house + ------------------------------------------------------------------------*/ + tp = (TechnoClass *)PendingObjectPtr; + tp->House = HouseClass::As_Pointer(LastHouse); + + /*------------------------------------------------------------------------ + Set house variables to new house + ------------------------------------------------------------------------*/ + PendingHouse = LastHouse; +} + + +/*************************************************************************** + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * * + * Looks in the given button list for the given GDI, NOD & Neutral button * + * id's. Sets the On/Off state of the buttons based on the given house, * + * only if that button is found in the list. * + * * + * INPUT: * + * house house to set buttons to * + * btnlist ptr to button list to search * + * base_id button ID for GDI; assumes other id's are sequential* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id) +{ + HousesType h; + int id; + TextButtonClass *btn; + + /* + ** Loop through all houses, searching the button list for each one. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + + /* + ** Compute the desired button ID; get a pointer to the button + */ + id = (int)h + base_id; + btn = (TextButtonClass *)btnlist->Extract_Gadget(id); + if (btn) { + + /* + ** If this house value is the desired one, turn the button on; + ** otherwise, turn it off. + */ + if (h == house) { + btn->Turn_On(); + } else { + btn->Turn_Off(); + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Trigger_Placement(void) +{ + Set_Default_Mouse(MOUSE_CAN_MOVE); + Override_Mouse_Shape(MOUSE_CAN_MOVE); +} + + +/*************************************************************************** + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Stop_Trigger_Placement(void) +{ + CurTrigger = NULL; + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); +} + + +/*************************************************************************** + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Trigger(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + + /* + -------------------- See if an object was clicked on --------------------- + */ + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + + /* + ............................ Get cell for x,y ............................ + */ + cell = Click_Cell_Calc(x, y); + + /* + ............... Convert x,y to offset from cell upper-left ............... + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ......................... Get object at that x,y ......................... + */ + object = Cell_Object(cell, x, y); + + /* + ---------------------- Assign trigger to an object ----------------------- + */ + if (object && TriggerClass::Event_Need_Object(CurTrigger->Event)) { + object->Trigger = CurTrigger; + } else { + + /* + ------------------------ Assign trigger to a cell ------------------------ + */ + if (CurTrigger->Event <= EVENT_OBJECTFIRST) { + Map[cell].IsTrigger = 1; + CellTriggers[cell] = CurTrigger; + } + } + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Start_Base_Building -- starts base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Base_Building(void) +{ + /* + ** Fully build the base so the user can edit it + */ + Build_Base_To(100); + + /* + ** Start placement mode + */ + BaseBuilding = 1; + Start_Placement(); + + /* + ** Force map to redraw + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Base_Building(void) +{ + /* + ** Build the base to the proper amount + */ + Build_Base_To(BasePercent); + + /* + ** Cancel placement mode + */ + Cancel_Placement(); + BaseBuilding = 0; + + /* + ** Force map to redraw + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Build_Base_To -- builds the AI base to the given percent * + * * + * INPUT: * + * percent percentage to build base to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Base_To(int percent) +{ + int i; + int num_buildings; + BuildingTypeClass const *objtype; + BuildingClass *obj; + + //ScenarioInit++; + + /* + ** Completely dismantle the base, so we start at a known point + */ + for (i = 0; i < Base.Nodes.Count(); i++) { + if (Base.Is_Built(i)) { + obj = Base.Get_Building(i); + delete obj; + } + } + + /* + ** Compute number of buildings to build + */ + num_buildings = (Base.Nodes.Count() * percent) / 100; + + /* + ** Build the base to the desired amount + */ + for (i = 0; i < num_buildings; i++) { + /* + ** Get a ptr to the type of building to build, create one, and unlimbo it. + */ + objtype = &BuildingTypeClass::As_Reference(Base.Nodes[i].Type); + obj = (BuildingClass *)objtype->Create_One_Of(HouseClass::As_Pointer(Base.House)); + /* + ** If unlimbo fails, error out + */ + if (!obj->Unlimbo(Base.Nodes[i].Coord)) { + delete obj; + CCMessageBox().Process("Unable to build base!"); + return; + } + } + + //ScenarioInit--; +} + + +#endif diff --git a/TIBERIANDAWN/MAPEDSEL.CPP b/TIBERIANDAWN/MAPEDSEL.CPP new file mode 100644 index 000000000..c2f35faa3 --- /dev/null +++ b/TIBERIANDAWN/MAPEDSEL.CPP @@ -0,0 +1,604 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapedsel.cpv 2.18 16 Oct 1995 16:49:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDSEL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Object-selection & manipulation routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Select_Object -- selects an object for processing * + * MapEditClass::Select_Next -- selects next object on the map * + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls* + * MapEditClass::Grab_Object -- grabs the current object * + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * MapEditClass::Change_House -- changes CurrentObject's house * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * Select_Object -- selects an object for processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object selected, -1 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Object(void) +{ + ObjectClass *object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + int rc=0; + + /* + -------------------- See if an object was clicked on --------------------- + */ + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + + /* + ............................ Get cell for x,y ............................ + */ + cell = Click_Cell_Calc(x, y); + + /* + ............... Convert x,y to offset from cell upper-left ............... + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ......................... Get object at that x,y ......................... + */ + object = Cell_Object(cell, x, y); + + /* + ----------------- If no object, unselect the current one ----------------- + */ + if (!object) { + if (CurrentObject.Count()) { + /* + ................... Unselect all current objects ................... + */ + Unselect_All(); + + /* + ..................... Turn off object controls ..................... + */ + Popup_Controls(); + } + rc = -1; + } else { + + /* + ------------------ Select object only if it's different ------------------ + */ + if (!CurrentObject.Count() || (CurrentObject.Count() && object != CurrentObject[0])) { + /* + ..................... Unselect all current objects .................... + */ + Unselect_All(); + object->Select(); + + /* + ................... Set mouse shape back to normal .................... + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ....................... Show the popup controls ....................... + */ + Popup_Controls(); + } + } + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + + return(rc); +} + + +/*************************************************************************** + * MapEditClass::Select_Next -- selects next object on the map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Select_Next(void) +{ + ObjectClass * obj; + CELL obj_cell; + int smap_w; // screen map width in icons + int smap_h; // screen map height in icons + int cell_x; // cell-x of next object + int cell_y; // cell-y of next object + int tcell_x; // cell-x of TacticalCell + int tcell_y; // cell-y of TacticalCell + + /* + ----------------------- Get next object on the map ----------------------- + */ + obj = Map.Next_Object(CurrentObject[0]); + + if (obj) { + /* + ............... Unselect current object if there is one ............... + */ + Unselect_All(); + + /* + ......................... Select this object .......................... + */ + obj->Select(); + } + + /* + --------------------- Restore mouse shape to normal ---------------------- + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + -------------------------- Show pop-up controls -------------------------- + */ + Popup_Controls(); + + /* + ---------------- Make sure object is shown on the screen ----------------- + */ + /* + ..................... compute screen map dimensions ...................... + */ + smap_w = Lepton_To_Cell(TacLeptonWidth); + smap_h = Lepton_To_Cell(TacLeptonHeight); + + /* + ...................... compute x,y of object's cell ...................... + */ + obj_cell = Coord_Cell(CurrentObject[0]->Coord); + cell_x = Cell_X(obj_cell); + cell_y = Cell_Y(obj_cell); + tcell_x = Coord_XCell(TacticalCoord); + tcell_y = Coord_YCell(TacticalCoord); + + /* + ................... If object is off-screen, move map .................... + */ + if (cell_x < tcell_x) { + tcell_x = cell_x; + } else { + if (cell_x >= tcell_x + smap_w) { + tcell_x = cell_x - smap_w + 1; + } + } + + if (cell_y < tcell_y) { + tcell_y = cell_y; + } else { + if (cell_y >= tcell_y + smap_h) { + tcell_y = cell_y - smap_h + 1; + } + } + + ScenarioInit++; + Set_Tactical_Position(XY_Coord(Cell_To_Lepton(tcell_x), Cell_To_Lepton(tcell_y))); + ScenarioInit--; + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls * + * * + * Call this routine whenever the CurrentObject changes. The routine will * + * selectively enable or disable the popup controls based on whether * + * CurrentObject is NULL, or if it's a Techno object, or what type of * + * Techno object it is. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Popup_Controls(void) +{ + const TechnoTypeClass * objtype = NULL; + HousesType owner; // object's current owner + int mission_index; // object's current mission + int strength; // object's 0-255 strength value + int i; + + /*------------------------------------------------------------------------ + Remove all buttons from GScreen's button list (so none of them provide + input any more); then, destroy the list by Zapping each button. Then, + we'll have to add at least the MapArea button back to the Input button + list before we return, plus any other buttons to process input for. We + always must add MapArea LAST in the list, so it doesn't intercept the + other buttons' input. + ------------------------------------------------------------------------*/ + Remove_A_Button(*GDIButton); + Remove_A_Button(*NODButton); + Remove_A_Button(*NeutralButton); + Remove_A_Button(*Multi1Button); + Remove_A_Button(*Multi2Button); + Remove_A_Button(*Multi3Button); + Remove_A_Button(*Multi4Button); + Remove_A_Button(*MissionList); + Remove_A_Button(*HealthGauge); + Remove_A_Button(*HealthText); + Remove_A_Button(*FacingDial); + Remove_A_Button(*BaseGauge); + Remove_A_Button(*BaseLabel); + Remove_A_Button(*MapArea); + + /* + ------------------ If no current object, hide the list ------------------- + */ + if (!CurrentObject.Count()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + /* + --------------- If not Techno, no need for editing buttons --------------- + */ + if (!CurrentObject[0]->Is_Techno()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + objtype = (TechnoTypeClass const *)&CurrentObject[0]->Class_Of(); + + /* + ---------------------- Get object's current values ----------------------- + */ + owner = CurrentObject[0]->Owner(); + mission_index = 0; + for (i = 0; i < NUM_EDIT_MISSIONS; i++) { + if (CurrentObject[0]->Get_Mission() == MapEditMissions[i]) { + mission_index = i; + } + } + strength = CurrentObject[0]->Health_Ratio(); + + + /* + ----------------------------- House buttons ------------------------------ + */ + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + if (Verify_House(HOUSE_NEUTRAL, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NeutralButton); + } + if (Verify_House(HOUSE_MULTI1, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi1Button); + } + if (Verify_House(HOUSE_MULTI2, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi2Button); + } + if (Verify_House(HOUSE_MULTI3, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi3Button); + } + if (Verify_House(HOUSE_MULTI4, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi4Button); + } + } else { + if (Verify_House(HOUSE_NEUTRAL, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NeutralButton); + } + if (Verify_House(HOUSE_BAD, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NODButton); + } + if (Verify_House(HOUSE_GOOD, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*GDIButton); + } + } + + /* + ........................ Set house button states ......................... + */ + if (Buttons) { + Set_House_Buttons(owner, Buttons, POPUP_GDI); + } + + switch (objtype->What_Am_I()) { + case RTTI_UNITTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_AIRCRAFTTYPE: + MissionList->Set_Selected_Index(mission_index); + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + FacingDial->Set_Direction(((TechnoClass *)CurrentObject[0])->PrimaryFacing); + + /* + ** Make the list. + */ + Add_A_Button(*MissionList); + Add_A_Button(*HealthGauge); + Add_A_Button(*HealthText); + Add_A_Button(*FacingDial); + break; + + case RTTI_BUILDINGTYPE: + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + Add_A_Button(*HealthGauge); + Add_A_Button(*HealthText); + + if (objtype->IsTurretEquipped) { + FacingDial->Set_Direction(((TechnoClass *) CurrentObject[0])->PrimaryFacing); + Add_A_Button(*FacingDial); + } + break; + } + + /*------------------------------------------------------------------------ + Add the map area last, so it's "underneath" the other buttons, and won't + intercept input for those buttons. + ------------------------------------------------------------------------*/ + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); +} + + +/*************************************************************************** + * MapEditClass::Grab_Object -- grabs the current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Grab_Object(void) +{ + CELL cell; + + if (CurrentObject.Count()) { + GrabbedObject = CurrentObject[0]; + + /*------------------------------------------------------------------------ + Find out which cell 'ZoneCell' is in relation to the object's current cell + ------------------------------------------------------------------------*/ + cell = Coord_Cell(GrabbedObject->Coord); + GrabOffset = cell - ZoneCell; + } +} + + +/*************************************************************************** + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object moved, -1 = it didn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Move_Grabbed_Object(void) +{ + COORDINATE new_coord = 0; + int retval = -1; + + /* + --------------------------- Lift up the object --------------------------- + */ + GrabbedObject->Mark(MARK_UP); + + /*------------------------------------------------------------------------ + If infantry, use a free spot in this cell + ------------------------------------------------------------------------*/ + if (GrabbedObject->Is_Infantry()) { + + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + new_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), + Get_Mouse_Y())); + /*.................................................................. + Clear the occupied bit in this infantry's cell. + ..................................................................*/ + ((InfantryClass *)GrabbedObject)->Clear_Occupy_Bit(GrabbedObject->Coord); +// Map[Coord_Cell(GrabbedObject->Coord)].Flag.Composite &= +// ~(1 << CellClass::Spot_Index(GrabbedObject->Coord)); + } else { + new_coord = NULL; + } + + } else { + + /*------------------------------------------------------------------------ + Non-infantry: use cell's center coordinate + ------------------------------------------------------------------------*/ + new_coord = Cell_Coord(ZoneCell + GrabOffset); + + if (GrabbedObject->What_Am_I() == RTTI_BUILDING || + GrabbedObject->What_Am_I() == RTTI_TERRAIN) { + + new_coord &= 0xFF00FF00L; + } + + /* + ................ Try to place object at new coordinate ................ + */ + if (GrabbedObject->Can_Enter_Cell(Coord_Cell(new_coord)) != MOVE_OK) { + new_coord = NULL; + } + } + if (new_coord != NULL) { + /* + ** If this object is part of the AI's Base list, change the coordinate + ** in the Base's Node list. + */ + if (GrabbedObject->What_Am_I()==RTTI_BUILDING && + Base.Get_Node((BuildingClass *)GrabbedObject)) + Base.Get_Node((BuildingClass *)GrabbedObject)->Coord = new_coord; + + GrabbedObject->Coord = new_coord; + retval = 0; + } + GrabbedObject->Mark(MARK_DOWN); + + /*------------------------------------------------------------------------ + For infantry, set the bit in its new cell marking that spot as occupied. + ------------------------------------------------------------------------*/ + if (GrabbedObject->Is_Infantry()) { + ((InfantryClass *)GrabbedObject)->Set_Occupy_Bit(new_coord); +// Map[Coord_Cell(new_coord)].Flag.Composite |= +// (1 << CellClass::Spot_Index(new_coord)); + } + + /*------------------------------------------------------------------------ + Re-select the object, and reset the mouse pointer + ------------------------------------------------------------------------*/ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + Flag_To_Redraw(true); + + return(retval); +} + + +/*************************************************************************** + * MapEditClass::Change_House -- changes CurrentObject's house * + * * + * INPUT: * + * newhouse house to change to * + * * + * OUTPUT: * + * 1 = house was changed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Change_House(HousesType newhouse) +{ + TechnoClass *tp; + + /*------------------------------------------------------------------------ + Return if no current object + ------------------------------------------------------------------------*/ + if (!CurrentObject.Count()) { + return(false); + } + + /*------------------------------------------------------------------------ + Only techno objects can be owned by a house; return if not a techno + ------------------------------------------------------------------------*/ + if (!CurrentObject[0]->Is_Techno()) { + return(false); + } + + /*------------------------------------------------------------------------ + You can't change the house if the object is part of the AI's Base. + ------------------------------------------------------------------------*/ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && Base.Is_Node((BuildingClass *)CurrentObject[0])) { + return(false); + } + + /*------------------------------------------------------------------------ + Verify that the target house exists + ------------------------------------------------------------------------*/ + if (HouseClass::As_Pointer(newhouse)==NULL) { + return(false); + } + + /*------------------------------------------------------------------------ + Verify that this is a valid owner + ------------------------------------------------------------------------*/ + if (!Verify_House(newhouse, &CurrentObject[0]->Class_Of())) { + return(false); + } + + /*------------------------------------------------------------------------ + Change the house + ------------------------------------------------------------------------*/ + tp = (TechnoClass *)CurrentObject[0]; + tp->House = HouseClass::As_Pointer(newhouse); + + return(true); +} + + +#endif diff --git a/TIBERIANDAWN/MAPEDTM.CPP b/TIBERIANDAWN/MAPEDTM.CPP new file mode 100644 index 000000000..adf8d776d --- /dev/null +++ b/TIBERIANDAWN/MAPEDTM.CPP @@ -0,0 +1,2074 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapedtm.cpv 2.18 16 Oct 1995 16:52:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDTM.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : April 9, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * MapEditClass::Select_Team -- user selects a team from a list * + * MapEditClass::Edit_Team -- user edits a team's options * + * MapEditClass::Team_Members -- user picks makeup of a team * + * MapEditClass::Build_Mission_list -- fills in mission list box * + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * MapEditClass::Team_Members -- Team members dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Handle_Teams(char const * caption) +{ + int rc; + + /*------------------------------------------------------------------------ + Team dialog processing loop: + - Invoke the team selection dialog. If a team's selected, break + & return + - If user wants to edit the current team, do so + - If user wants to create new team, new a TeamTypeClass & edit it + - If user wants to delete team, delete the current team + - Keep looping until 'OK' + ------------------------------------------------------------------------*/ + for (;;) { + + /* + ............................. Select team ............................. + */ + rc = Select_Team(caption); + + /* + ............................. 'OK'; break ............................. + */ + if (rc == 0) { + break; + } else { + + /* + ............................... 'Edit' ................................ + */ + if (rc == 1 && CurTeam) { + if (Edit_Team()==0) { + Changed = 1; + } + } else { + + /* + ................................ 'New' ................................ + */ + if (rc == 2) { + /* + ........................ Create a new team ......................... + */ + CurTeam = new TeamTypeClass(); + if (CurTeam) { + /* + ................... delete it if user cancels ................... + */ + if (Edit_Team()==-1) { + delete CurTeam; + CurTeam = NULL; + } else { + Changed = 1; + } + } else { + + /* + ................. Unable to create; issue warning .................. + */ + CCMessageBox().Process("No more teams available."); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } else { + + /* + .............................. 'Delete' ............................... + */ + if (rc==3) { + if (CurTeam) { + CurTeam->Remove(); + CurTeam = NULL; + } + } + } + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Team -- user selects a team from a list * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Team(char const * caption) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, // dialog width + D_DIALOG_H = 290, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_LIST_W = 500, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_EDIT_W = 90, + D_EDIT_H = 18, + D_EDIT_X = D_DIALOG_X + (D_DIALOG_W / 8) - (D_EDIT_W / 2), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_EDIT_H, + + D_NEW_W = 90, + D_NEW_H = 18, + D_NEW_X = D_DIALOG_X + (D_DIALOG_W / 8) * 3 - (D_NEW_W / 2), + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_NEW_H, + + D_DELETE_W = 90, + D_DELETE_H = 18, + D_DELETE_X = D_DIALOG_X + (D_DIALOG_W / 8) * 5 - (D_DELETE_W / 2), + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_DELETE_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 8) * 7 - (D_OK_W / 2), + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TEAM_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + char *teamtext[TEAMTYPE_MAX + 1]; // text for defined teams + KeyNumType input; // user input + bool edit_team = false; // true = user wants to edit + bool new_team = false; // true = user wants to new + bool del_team = false; // true = user wants to new + int i; // loop counters + int j; + int def_idx; // default list index + static int tabs[] = {120, 180}; // list box tab stops + char txt[10]; +// int housetxt; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands = NULL; // the button list + + ListClass teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + + TextButtonClass newbtn (BUTTON_NEW, "New", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ........................... Fill in team names ........................... + */ + def_idx = 0; + for (i = 0; i < TeamTypes.Count(); i++) { + /* + ................... Generate string for this team ..................... + */ + //teamtext[i] = (char *)HidPage.Get_Graphic_Buffer()->Get_Buffer() + TEAMTXT_LEN * i; + teamtext[i] = new char[255]; + + /* + ........................ Fill in name & house ......................... + */ + strcpy(teamtext[i],TeamTypes.Ptr(i)->IniName); + strcat(teamtext[i],"\t"); + strcat(teamtext[i], HouseTypeClass::As_Reference(TeamTypes.Ptr(i)->House).Suffix); + strcat(teamtext[i],"\t"); + + /* + ................ Fill in class & count for all classes ................ + */ + for (j = 0; j < TeamTypes.Ptr(i)->ClassCount; j++) { + sprintf (txt,"%s:%d", TeamTypes.Ptr(i)->Class[j]->IniName, TeamTypes.Ptr(i)->DesiredNum[j]); + + /*.................................................................. + Add entry if there's room; break otherwise + (+ 3 for the ", " and the NULL; +3 again for the "..." for the next + entry) + ..................................................................*/ + if (strlen(txt) + strlen(teamtext[i]) + 6 < TEAMTXT_LEN) { + if (j > 0) { + strcat(teamtext[i],", "); + } + strcat(teamtext[i],txt); + } else { + strcat(teamtext[i], "..."); + break; + } + } + + /* + .................. Set def_idx if this is CurTeam ..................... + */ + if (TeamTypes.Ptr(i)==CurTeam) { + def_idx = i; + } + + /* + ........................... Add to list box ........................... + */ + teamlist.Add_Item(teamtext[i]); + } + + /* + ....................... Set CurTeam if it isn't .......................... + */ + if (TeamTypes.Count()==0) { + CurTeam = NULL; + } else { + if (!CurTeam) { + CurTeam = TeamTypes.Ptr(def_idx); + } + } + + /* + ............................ Create the list ............................. + */ + commands = &teamlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + teamlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(caption, D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + def_idx = teamlist.Current_Index(); + if (def_idx < TeamTypes.Count()) + CurTeam = TeamTypes.Ptr(def_idx); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTeam) { // only allow if there's one selected + process = false; + edit_team = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_team = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_team = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + for (i = 0; i < TeamTypes.Count(); i++) { + delete [] teamtext[i]; + } + if (edit_team) return(1); + if (new_team) return(2); + if (del_team) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Edit_Team -- user edits a team's options * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Editor ³ * + * ³ ³ * + * ³ Name ______ [Roundabout] ³ * + * ³ Priority ______ [ GDI ] [Learning ] ³ * + * ³ Max Num ______ [ NOD ] [Suicide ] ³ * + * ³ Init Num ______ [Autocreate] ³ * + * ³ Fear ______ [Mercenary ] ³ * + * ³ [Prebuild ] ³ * + * ³ [Reinforce ] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ ³^³ ³ ³^³ ³ * + * ³ ³ ÃÄ´ [Add >>] ³ ÃÄ´ ³ * + * ³ ³ ³ ³ [Insert] ³ ³ ³ ³ * + * ³ ³ ³ ³ [Delete] ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ____ ³ ÃÄ´ ³ * + * ³ ³ ³v³ ³ ³v³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Members] [Cancel] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine invokes the Members dialog, which uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Edit_Team(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 516, + D_DIALOG_H = 376, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_NAME_W = 120, + D_NAME_H = 18, + D_NAME_X = D_DIALOG_X + D_MARGIN + 100, + D_NAME_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_PRIORITY_W = 120, + D_PRIORITY_H = 18, + D_PRIORITY_X = D_DIALOG_X + D_MARGIN + 100, + D_PRIORITY_Y = D_NAME_Y + D_NAME_H, + + D_MAXNUM_W = 120, + D_MAXNUM_H = 18, + D_MAXNUM_X = D_DIALOG_X + D_MARGIN + 100, + D_MAXNUM_Y = D_PRIORITY_Y + D_PRIORITY_H, + + D_INITNUM_W = 120, + D_INITNUM_H = 18, + D_INITNUM_X = D_DIALOG_X + D_MARGIN + 100, + D_INITNUM_Y = D_MAXNUM_Y + D_MAXNUM_H, + + D_FEAR_W = 120, + D_FEAR_H = 18, + D_FEAR_X = D_DIALOG_X + D_MARGIN + 100, + D_FEAR_Y = D_INITNUM_Y + D_INITNUM_H, + + D_GDI_W = 100, + D_GDI_H = 18, + D_GDI_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_GDI_Y = D_NAME_Y + D_NAME_H + D_NAME_H / 2, + + D_NOD_W = 100, + D_NOD_H = 18, + D_NOD_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_NOD_Y = D_GDI_Y + D_GDI_H, + + D_NEU_W = 100, + D_NEU_H = 18, + D_NEU_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_NEU_Y = D_NOD_Y + D_NOD_H, + + D_MULTI1_W = 50, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 50, + D_MULTI2_H = 18, + D_MULTI2_X = D_GDI_X + D_MULTI2_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 50, + D_MULTI3_H = 18, + D_MULTI3_X = D_NOD_X, + D_MULTI3_Y = D_NOD_Y, + + D_MULTI4_W = 50, + D_MULTI4_H = 18, + D_MULTI4_X = D_NOD_X + D_MULTI4_W, + D_MULTI4_Y = D_NOD_Y, + + D_ROUNDABOUT_W = 130, + D_ROUNDABOUT_H = 18, + D_ROUNDABOUT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_ROUNDABOUT_W, + D_ROUNDABOUT_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H - 10, + + D_LEARNING_W = D_ROUNDABOUT_W, + D_LEARNING_H = 18, + D_LEARNING_X = D_ROUNDABOUT_X, + D_LEARNING_Y = D_ROUNDABOUT_Y + D_ROUNDABOUT_H, + + D_SUICIDE_W = D_ROUNDABOUT_W, + D_SUICIDE_H = 18, + D_SUICIDE_X = D_ROUNDABOUT_X, + D_SUICIDE_Y = D_LEARNING_Y + D_LEARNING_H, + + D_AUTOCREATE_W = D_ROUNDABOUT_W, + D_AUTOCREATE_H = 18, + D_AUTOCREATE_X = D_ROUNDABOUT_X, + D_AUTOCREATE_Y = D_SUICIDE_Y + D_SUICIDE_H, + + D_MERCENARY_W = D_ROUNDABOUT_W, + D_MERCENARY_H = 18, + D_MERCENARY_X = D_ROUNDABOUT_X, + D_MERCENARY_Y = D_AUTOCREATE_Y + D_AUTOCREATE_H, + + D_PREBUILT_W = D_ROUNDABOUT_W, + D_PREBUILT_H = 18, + D_PREBUILT_X = D_ROUNDABOUT_X, + D_PREBUILT_Y = D_MERCENARY_Y + D_MERCENARY_H, + + D_REINFORCE_W = D_ROUNDABOUT_W, + D_REINFORCE_H = 18, + D_REINFORCE_X = D_ROUNDABOUT_X, + D_REINFORCE_Y = D_PREBUILT_Y + D_PREBUILT_H, + + D_MISSION1_W = 180, + D_MISSION1_H = 128, + D_MISSION1_X = D_DIALOG_X + D_MARGIN, + D_MISSION1_Y = D_REINFORCE_Y + D_REINFORCE_H + D_MARGIN, + + D_MISSION2_W = 180, + D_MISSION2_H = 128, + D_MISSION2_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_MISSION2_W, + D_MISSION2_Y = D_MISSION1_Y, + + D_ADD_W = 100, + D_ADD_H = 18, + D_ADD_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_ADD_Y = D_MISSION1_Y + D_ADD_H, + + D_INSERT_W = 100, + D_INSERT_H = 18, + D_INSERT_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_INSERT_Y = D_ADD_Y + D_ADD_H, + + D_DEL_W = 100, + D_DEL_H = 18, + D_DEL_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_DEL_Y = D_INSERT_Y + D_INSERT_H, + + D_ARG_W = 100, + D_ARG_H = 18, + D_ARG_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_ARG_Y = D_DEL_Y + D_DEL_H, + + D_MEMBERS_W = 100, + D_MEMBERS_H = 18, + D_MEMBERS_X = D_DIALOG_X + (D_DIALOG_W / 6) - D_MEMBERS_W / 2, + D_MEMBERS_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_MEMBERS_H, + + D_CANCEL_W = 100, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_X + (D_DIALOG_W / 6) * 3 - D_CANCEL_W / 2, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_CANCEL_H, + + D_OK_W = 100, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 6) * 5 - D_OK_W / 2, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_NAME=100, + BUTTON_RECRUIT, + BUTTON_MAXNUM, + BUTTON_INITNUM, + BUTTON_FEAR, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_NEU, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_MULTI5, + BUTTON_MULTI6, + BUTTON_ROUNDABOUT, + BUTTON_LEARNING, + BUTTON_SUICIDE, + BUTTON_AUTO, + BUTTON_MERCENARY, + BUTTON_PREBUILT, + BUTTON_REINFORCE, + BUTTON_MISSION1, + BUTTON_MISSION2, + BUTTON_ADD, + BUTTON_INSERT, + BUTTON_DEL, + BUTTON_ARG, + BUTTON_MEMBERS, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + bool cancel = false; // true = user cancels + char name_buf[12]; + char recr_buf[4]; + char maxnum_buf[4]; + char initnum_buf[4]; + char fear_buf[4]; + HousesType house; + int roundabout; + int learning; + int suicide; + int autocreate; + int mercenary; + int prebuilt; + int reinforce; + int missioncount; + TeamMissionStruct missions[TeamTypeClass::MAX_TEAM_MISSIONS]; + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20]; + int curmission; // currently-selected mission index + + char arg_buf[4] = {0}; + static int tabs[] = {130, 180}; // list box tab stops + int i,j; + + /*........................................................................ + Buttons: + ........................................................................*/ + ControlClass *commands; + EditClass name_edt (BUTTON_NAME, + name_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, D_NAME_W, D_NAME_H, EditClass::ALPHANUMERIC); + + EditClass recr_edt (BUTTON_RECRUIT, + recr_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PRIORITY_X, D_PRIORITY_Y, D_PRIORITY_W, D_PRIORITY_H, EditClass::NUMERIC); + + EditClass maxnum_edt (BUTTON_MAXNUM, + maxnum_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MAXNUM_X, D_MAXNUM_Y, D_MAXNUM_W, D_MAXNUM_H, EditClass::NUMERIC); + + EditClass initnum_edt (BUTTON_INITNUM, + initnum_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INITNUM_X, D_INITNUM_Y, D_INITNUM_W, D_INITNUM_H, EditClass::NUMERIC); + + EditClass fear_edt (BUTTON_FEAR, + fear_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_FEAR_X, D_FEAR_Y, D_FEAR_W, D_FEAR_H, EditClass::NUMERIC); + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neubtn (BUTTON_NEU, "NEUTRAL", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + + TextButtonClass multi1btn (BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn (BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn (BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn (BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass roundbtn (BUTTON_ROUNDABOUT, "Roundabout", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ROUNDABOUT_X, D_ROUNDABOUT_Y, D_ROUNDABOUT_W, D_ROUNDABOUT_H); + + TextButtonClass learnbtn (BUTTON_LEARNING, "Learning", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEARNING_X, D_LEARNING_Y, D_LEARNING_W, D_LEARNING_H); + + TextButtonClass suicidebtn (BUTTON_SUICIDE, "Suicide", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SUICIDE_X, D_SUICIDE_Y, D_SUICIDE_W, D_SUICIDE_H); + + TextButtonClass autocreatebtn (BUTTON_AUTO, "Autocreate", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_AUTOCREATE_X, D_AUTOCREATE_Y, D_AUTOCREATE_W, D_AUTOCREATE_H); + + TextButtonClass mercbtn (BUTTON_MERCENARY, "Mercenary", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MERCENARY_X, D_MERCENARY_Y, D_MERCENARY_W, D_MERCENARY_H); + + TextButtonClass prebuiltbtn (BUTTON_PREBUILT, "Prebuild", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PREBUILT_X, D_PREBUILT_Y, D_PREBUILT_W, D_PREBUILT_H); + + TextButtonClass reinforcebtn (BUTTON_REINFORCE, "Reinforce", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_REINFORCE_X, D_REINFORCE_Y, D_REINFORCE_W, D_REINFORCE_H); + + ListClass missionlist1 (BUTTON_MISSION1, + D_MISSION1_X, D_MISSION1_Y, D_MISSION1_W, D_MISSION1_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + ListClass missionlist2 (BUTTON_MISSION2, + D_MISSION2_X, D_MISSION2_Y, D_MISSION2_W, D_MISSION2_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass addbtn (BUTTON_ADD, "Add >>", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ADD_X, D_ADD_Y, D_ADD_W, D_ADD_H); + + TextButtonClass insertbtn (BUTTON_INSERT, "Insert", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INSERT_X, D_INSERT_Y, D_INSERT_W, D_INSERT_H); + + TextButtonClass delbtn (BUTTON_DEL, "Delete", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DEL_X, D_DEL_Y, D_DEL_W, D_DEL_H); + + EditClass arg_edt (BUTTON_ARG, arg_buf, 4, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ARG_X, D_ARG_Y, D_ARG_W, D_ARG_H, EditClass::ALPHANUMERIC); + + TextButtonClass membersbtn (BUTTON_MEMBERS, "Members", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MEMBERS_X, D_MEMBERS_Y, D_MEMBERS_W, D_MEMBERS_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ........................... Copy team's state ............................ + */ + strcpy(name_buf,CurTeam->IniName); + sprintf(recr_buf,"%d",CurTeam->RecruitPriority); + sprintf(maxnum_buf,"%d",CurTeam->MaxAllowed); + sprintf(initnum_buf,"%d",CurTeam->InitNum); + sprintf(fear_buf,"%d",CurTeam->Fear); + roundabout = CurTeam->IsRoundAbout; + learning = CurTeam->IsLearning; + suicide = CurTeam->IsSuicide; + house = CurTeam->House; + autocreate = CurTeam->IsAutocreate; + mercenary = CurTeam->IsMercenary; + prebuilt = CurTeam->IsPrebuilt; + reinforce = CurTeam->IsReinforcable; + + /* + ......................... Fill in mission lists .......................... + */ + for (i = 0; i < TMISSION_COUNT; i++) { + missionlist1.Add_Item(TeamTypeClass::Name_From_Mission((TeamMissionType)i)); + } + + missioncount = CurTeam->MissionCount; + for (i = 0; i < missioncount; i++) { + missions[i] = CurTeam->MissionList[i]; + } + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + curmission = 0; + if (missioncount) { + if (missions[curmission].Mission == TMISSION_MOVE || missions[curmission].Mission == TMISSION_UNLOAD) { + sprintf(arg_buf,"%c",missions[curmission].Argument + 'A'); + } else { + sprintf(arg_buf,"%d",missions[curmission].Argument); + } + } + missionlist2.Set_Tabs(tabs); + + /* + ......................... Init the button states ......................... + */ + name_edt.Set_Text(name_buf,8); + recr_edt.Set_Text(recr_buf,3); + maxnum_edt.Set_Text(maxnum_buf,3); + initnum_edt.Set_Text(initnum_buf,3); + fear_edt.Set_Text(fear_buf,3); + arg_edt.Set_Text(arg_buf,3); + + if (roundabout) { + roundbtn.Turn_On(); + } + if (learning) { + learnbtn.Turn_On(); + } + if (suicide) { + suicidebtn.Turn_On(); + } + if (autocreate) { + autocreatebtn.Turn_On(); + } + if (mercenary) { + mercbtn.Turn_On(); + } + if (reinforce) { + reinforcebtn.Turn_On(); + } + if (prebuilt) { + prebuiltbtn.Turn_On(); + } + + /* + ............................ Create the list ............................. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + membersbtn.Add_Tail(*commands); + + name_edt.Add_Tail(*commands); + recr_edt.Add_Tail(*commands); + maxnum_edt.Add_Tail(*commands); + initnum_edt.Add_Tail(*commands); + fear_edt.Add_Tail(*commands); + + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neubtn.Add_Tail(*commands); + + roundbtn.Add_Tail(*commands); + learnbtn.Add_Tail(*commands); + suicidebtn.Add_Tail(*commands); + autocreatebtn.Add_Tail(*commands); + mercbtn.Add_Tail(*commands); + prebuiltbtn.Add_Tail(*commands); + reinforcebtn.Add_Tail(*commands); + + missionlist1.Add_Tail(*commands); + missionlist2.Add_Tail(*commands); + addbtn.Add_Tail(*commands); + insertbtn.Add_Tail(*commands); + delbtn.Add_Tail(*commands); + arg_edt.Add_Tail(*commands); + + Set_House_Buttons (house, commands, BUTTON_GDI); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Team Edit", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Name", D_NAME_X - 5, D_NAME_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Priority", D_PRIORITY_X - 5, D_PRIORITY_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Max Num", D_MAXNUM_X - 5, D_MAXNUM_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Init Num", D_INITNUM_X - 5, D_INITNUM_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Fear", D_FEAR_X - 5, D_FEAR_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + break; + + case (BUTTON_RECRUIT | KN_BUTTON): + break; + + case (BUTTON_MAXNUM | KN_BUTTON): + break; + + case (BUTTON_INITNUM | KN_BUTTON): + break; + + case (BUTTON_FEAR | KN_BUTTON): + break; + + /*.................................................................. + Toggle RoundAbout + ..................................................................*/ + case (BUTTON_ROUNDABOUT | KN_BUTTON): + if (roundabout) { + roundabout = 0; + roundbtn.Turn_Off(); + } else { + roundabout = 1; + roundbtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Learning + ..................................................................*/ + case (BUTTON_LEARNING | KN_BUTTON): + if (learning) { + learning = 0; + learnbtn.Turn_Off(); + } else { + learning = 1; + learnbtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Suicide + ..................................................................*/ + case (BUTTON_SUICIDE | KN_BUTTON): + if (suicide) { + suicide = 0; + suicidebtn.Turn_Off(); + } else { + suicide = 1; + suicidebtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Spy + ..................................................................*/ + case (BUTTON_AUTO | KN_BUTTON): + if (autocreate) { + autocreate = 0; + autocreatebtn.Turn_Off(); + } else { + autocreate = 1; + autocreatebtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Mercenary + ..................................................................*/ + case (BUTTON_MERCENARY | KN_BUTTON): + if (mercenary) { + mercenary = 0; + mercbtn.Turn_Off(); + } else { + mercenary = 1; + mercbtn.Turn_On(); + } + break; + + case (BUTTON_PREBUILT | KN_BUTTON): + if (prebuilt) { + prebuilt = 0; + prebuiltbtn.Turn_Off(); + } else { + prebuilt = 1; + prebuiltbtn.Turn_On(); + } + break; + + case (BUTTON_REINFORCE | KN_BUTTON): + if (reinforce) { + reinforce = 0; + reinforcebtn.Turn_Off(); + } else { + reinforce = 1; + reinforcebtn.Turn_On(); + } + break; + + /*.................................................................. + Select a Mission on the left-hand mission list + ..................................................................*/ + case (BUTTON_MISSION1 | KN_BUTTON): + break; + + /*.................................................................. + Select a Mission on the right-hand mission list; update the Argument + field to reflect the current value + ..................................................................*/ + case (BUTTON_MISSION2 | KN_BUTTON): + if (missionlist2.Count() > 0 && + missionlist2.Current_Index() != curmission) { + curmission = missionlist2.Current_Index(); + if (missions[curmission].Mission==TMISSION_MOVE || missions[curmission].Mission == TMISSION_UNLOAD) { + sprintf(arg_buf,"%c",missions[curmission].Argument + 'A'); + } else { + sprintf(arg_buf,"%d",missions[curmission].Argument); + } + arg_edt.Set_Text(arg_buf,3); + } + break; + + /*.................................................................. + Copy mission from left list box to right list box + ..................................................................*/ + case (BUTTON_ADD | KN_BUTTON): + case (BUTTON_INSERT | KN_BUTTON): + if (missioncount < TeamTypeClass::MAX_TEAM_MISSIONS) { + /* + ** Set 'i' to the position we're going to add into; this will + ** be just AFTER the current item if we're adding, and it will + ** be the current item if we're inserting. + */ + if (input == (BUTTON_ADD | KN_BUTTON)) { + i = missionlist2.Current_Index() + 1; + if (i < 0) { + i = 0; + } + if (i > missioncount) { + i = missioncount; + } + } else { + i = missionlist2.Current_Index(); + if (i < 0) { + i = 0; + } + if (i >= missioncount && missioncount > 0) { + i = missioncount - 1; + } + } + + /* + ** Move all other missions forward in the array + */ + for (j = missioncount; j > i; j--) { + missions[j] = missions[j - 1]; + } + + /* + ** Set the Mission value based on 1st list box's index + */ + missions[i].Mission = (TeamMissionType)(TMISSION_FIRST + missionlist1.Current_Index()); + + /* + ** Set the missions argument field + */ + if (missions[i].Mission == TMISSION_MOVE || missions[i].Mission == TMISSION_UNLOAD) { + missions[i].Argument = toupper(arg_buf[0]) - 'A'; + } else { + missions[i].Argument = atoi(arg_buf); + } + missioncount++; + + /* + ** Rebuild the list box from scratch + */ + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + /* + ** Update the list's current item index + */ + missionlist2.Set_Selected_Index(i); + } + break; + + /*.................................................................. + Delete mission from right-hand list box + ..................................................................*/ + case (BUTTON_DEL | KN_BUTTON): + if (missioncount > 0) { + i = missionlist2.Current_Index(); + if (i < 0 || i >= missioncount) { + break; + } + + /* + ** Move all missions back in the array + */ + for (j = i; j < missioncount - 1; j++) { + missions[j] = missions[j + 1]; + } + missioncount--; + + /* + ** Rebuild the list box from scratch + */ + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + /* + ** Update the list's current item index + */ + if (i >= missioncount) { + i--; + if (i < 0) { + i = 0; + } + missionlist2.Set_Selected_Index(i); + } + } + break; + + /*.................................................................. + Set house + ..................................................................*/ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEU | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + Set_House_Buttons(house, commands, BUTTON_GDI); + break; + + /*.................................................................. + Invoke the members dialog + ..................................................................*/ + case (BUTTON_MEMBERS | KN_BUTTON): + /* + .................... Take editor focus away ..................... + */ + membersbtn.Turn_Off(); + + /* + ....................... Invoke the dialog ....................... + */ + Team_Members(house); + + /* + ............................ Redraw ............................. + */ + display = REDRAW_ALL; + break; + + /*.................................................................. + OK: return + ..................................................................*/ + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /*.................................................................. + Cancel: return + ..................................................................*/ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + /*.................................................................. + Pass all other events to the currently-active text editor + ..................................................................*/ + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ------------------------ Save selections & return ------------------------ + */ + CurTeam->Set_Name(name_buf); + CurTeam->RecruitPriority = atoi(recr_buf); + CurTeam->MaxAllowed = atoi(maxnum_buf); + CurTeam->InitNum = atoi(initnum_buf); + CurTeam->IsRoundAbout = roundabout; + CurTeam->IsLearning = learning; + CurTeam->IsSuicide = suicide; + CurTeam->IsAutocreate = autocreate; + CurTeam->IsPrebuilt = prebuilt; + CurTeam->IsReinforcable = reinforce; + CurTeam->IsMercenary = mercenary; + CurTeam->House = house; + CurTeam->MissionCount = missioncount; + for (i = 0 ; i < missioncount; i++) { + CurTeam->MissionList[i] = missions[i]; + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Team_Members -- user picks makeup of a team * + * * + * Team members are rendered in a 24 x 24 area; the Window coordinates * + * have to be set to this area when the object's 'Display()' routine is * + * called. Thus, the dialog's window coords have to be divisible by * + * 24. The height of the dialog is computed based on how many objects * + * there are in it. * + * * + * 10 pixels are left between rows of objects, so the # of that type of * + * object can be displayed underneath the object. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Members ³ * + * ³ ³ * + * ³ ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * house house to display objects for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine uses HIDBUFF for data storage. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +#define TEENSY_WEENSY +/* +** Dialog & button dimensions +*/ +enum { + D_DIALOG_W = 608, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT6_H = 14, + D_MARGIN = 14, + +#ifdef TEENSY_WEENSY + //D_PICTURE_W = 32, + //D_PICTURE_H = 24, + D_PICTURE_W = 64, // 9 pictures / row, 16 pixel margin on each side + D_PICTURE_H = 48, +#else + //D_PICTURE_W = 32, + //D_PICTURE_H = 30, + D_PICTURE_W = 64, + D_PICTURE_H = 60, +#endif + D_ROW_H = (D_PICTURE_H + 6), + + D_OK_W = 100, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - 10 - D_OK_W, + D_OK_Y = 0, + + D_CANCEL_W = 100, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 10, + D_CANCEL_Y = 0, + +}; + +/*************************************************************************** + * MapEditClass::Team_Members -- Team members dialog * + * * + * INPUT: * + * house house to show members for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/09/1996 BRR : Created. * + *=========================================================================*/ +int MapEditClass::Team_Members(HousesType house) +{ + /* + ** Button enumerations: + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + ** (highest enum is the lowest layer). Each section of the map checks + ** the requested redraw level to see if it's supposed to draw; if it's + ** >= its level, it redraws. + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + RedrawType display; // requested redraw level + bool process; // loop while true + + /* + ............................ Dialog variables ............................ + */ + KeyNumType input; // user input + bool cancel = false; // true = user cancels + + /* + ......................... Team display variables ......................... + */ + const TechnoTypeClass **teamclass; // array of team classes + int *teamcount; // array of class counts + int numcols; // # units displayed horizontally + int numrows; // # units displayed vertically +// int col; // horizontal picture index +// int row; // vertical picture index +// int x,y; + + /* + ** Dialog dimensions. + */ + int dlg_y; + int dlg_h; // dialog height + int dlg_picture_top; // coord of top of pictures + int msg_y; // y-coord for object names + + /* + ** Values for parsing the classes. + */ + InfantryType i_id; + AircraftType a_id; + UnitType u_id; + int curclass = -1; // current index into 'teamclass'; can be invalid! + // (is based on current mouse position) + int numclasses; // current # classes in the team (limited to <=5) + int maxclasses; // max # classes available + int i,j; + + /* + ** Values for timing when mouse held down. + */ + int lheld = 0; + int rheld = 0; + long tdelay[3] = {5, 20, 0}; + int tindex = 0; + long heldtime; + + /* + ** Buttons. + */ + ControlClass *commands; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Set up the team data arrays (ObjectTypeClass pointers & count) + */ + teamclass = (const TechnoTypeClass **)(new TechnoTypeClass *[MAX_TEAM_CLASSES]); + teamcount = new int[MAX_TEAM_CLASSES]; + + /* + ** Fill in the ObjectTypeClass array with all available object type ptrs, + ** checking to be sure this house can own the object + */ + i = 0; + for (i_id = INFANTRY_FIRST; i_id < INFANTRY_COUNT; i_id++) { + if (Verify_House(house,&InfantryTypeClass::As_Reference(i_id))) { + teamclass[i] = &InfantryTypeClass::As_Reference(i_id); + i++; + } + } + + for (a_id = AIRCRAFT_FIRST; a_id < AIRCRAFT_COUNT; a_id++) { + if (Verify_House(house,&AircraftTypeClass::As_Reference(a_id))) { + teamclass[i] = &AircraftTypeClass::As_Reference(a_id); + i++; + } + } + + for (u_id = UNIT_FIRST; u_id < UNIT_COUNT; u_id++) { + if (Verify_House(house,&UnitTypeClass::As_Reference(u_id))) { + teamclass[i] = &UnitTypeClass::As_Reference(u_id); + i++; + } + } + + /* + ** Save max # classes. + */ + maxclasses = i; + + /* + ** Fill in the 'count' array with data from the current team: + ** - For every class in the current team, find that class type in the + ** 'teamclass' array & set its count value + */ + for (j = 0; j < maxclasses; j++) { + teamcount[j] = 0; + } + + /* + ** Loop through all classes in the team. + */ + for (i = 0; i < CurTeam->ClassCount; i++) { + + /* + ** Find this class in our array. + */ + for (j = 0; j < maxclasses; j++) { + + /* + ** Set the count; detect a match between the team's class & the + ** 'teamclass' array entry by comparing the actual pointers; typeid + ** won't work because E1 & E2 are the same type class. + */ + if (CurTeam->Class[i] == teamclass[j]) { + teamcount[j] = CurTeam->DesiredNum[i]; + break; + } + } + } + numclasses = CurTeam->ClassCount; + + /* + ** Set up the dialog dimensions based on number of classes we have to draw + ** + ** Compute picture rows & cols. + */ + numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + numrows = (maxclasses + numcols - 1) / numcols; + + // + // Dialog's height = top margin + label + picture rows + + // margin + label + margin + btn + // + dlg_h = (D_MARGIN + D_TXT6_H + D_MARGIN + (numrows * D_ROW_H) + + D_MARGIN + D_TXT6_H + D_MARGIN + D_OK_H + D_MARGIN); + if (dlg_h > 400) { + dlg_h = 400; + } + dlg_y = (400 - dlg_h) / 2; + dlg_picture_top = dlg_y + D_MARGIN + D_TXT6_H + D_MARGIN; + msg_y = dlg_y + D_MARGIN + D_TXT6_H + D_MARGIN + (numrows * D_ROW_H) + D_MARGIN; + + okbtn.Y = dlg_y + dlg_h - D_MARGIN - D_OK_H; + cancelbtn.Y = dlg_y + dlg_h - D_MARGIN - D_CANCEL_H; + + /* + ** Draw to SeenBuff. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Make sure 'house' is valid. + */ + if (house!=HOUSE_GOOD && house!=HOUSE_BAD && house != HOUSE_MULTI1 && + house != HOUSE_MULTI2 && house != HOUSE_MULTI3 && house != HOUSE_MULTI4 ) { + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + house = HOUSE_MULTI1; + } else { + house = HOUSE_GOOD; + } + } + + /* + ** Create the list. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ** Display the constant background of this dialog. + */ + Dialog_Box(D_DIALOG_X, dlg_y, D_DIALOG_W, dlg_h); + Draw_Caption(TXT_NONE, D_DIALOG_X, dlg_y, D_DIALOG_W); + Fancy_Text_Print("Team Members", D_DIALOG_CX, dlg_y + D_MARGIN, CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + // + // Draw the objects. + // + for (i = 0; i < maxclasses; i++) { + // + // Display the object along with any count value for it. + // + Draw_Member(teamclass[i], i, teamcount[i], house, + D_DIALOG_X + 16, dlg_picture_top); + } + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, CC_TAN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + + /* + ** Mouse buttons set or clear 'held' values + */ + case (KN_LMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + lheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case (KN_RMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + rheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + lheld = 0; + break; + + case ((int)KN_RMOUSE | (int)KN_RLSE_BIT): + rheld = 0; + break; + + /* + ** OK: save values & return. + */ + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + /* + ** Cancel: abort & return. + */ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + /* + ** Compute new 'curclass' based on mouse position. + */ + i = (Get_Mouse_X() - 16 - D_DIALOG_X) / D_PICTURE_W + + ((Get_Mouse_Y() - dlg_picture_top) / D_ROW_H) * numcols; + + /* + ** If it's changed, update class label. + */ + if (i != curclass) { + + curclass = i; + + /* + ** Clear out the previously printed name of the item. + */ + Hide_Mouse(); + LogicPage->Fill_Rect(D_DIALOG_X + 8, msg_y, D_DIALOG_X + D_DIALOG_W - 9, msg_y + D_TXT6_H, BLACK); + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, + CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + + /* + ** Force buttons to not be held. + */ + lheld = 0; + rheld = 0; + Show_Mouse(); + } + break; + } + + /* + ** Check for a 'held' mouse button; if it's down, and the correct + ** amount of time has gone by, increment/decrement the count for the + ** current class. + */ + if (lheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount.Time() - heldtime > tdelay[tindex]) { + heldtime = TickCount.Time(); + if (tindex) { + tindex--; + } + + /* + ** Detect addition of a new class. + */ + if (teamcount[curclass]==0) { + + /* + ** Don't allow more classes than we can handle. + */ + if (numclasses == TeamTypeClass::MAX_TEAM_CLASSCOUNT) { + continue; + } + numclasses++; + } + teamcount[curclass]++; + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], + house, D_DIALOG_X + 16, dlg_picture_top); + + } + + } else { + + if (rheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount.Time() - heldtime > tdelay[tindex]) { + if (tindex) { + tindex--; + } + heldtime = TickCount.Time(); + + if (teamcount[curclass] > 0) { + teamcount[curclass]--; + + /* + ** Detect removal of a class. + */ + if (teamcount[curclass] == 0) { + numclasses--; + } + } + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], + house, D_DIALOG_X + 16, dlg_picture_top); + + } + } + } + } + + /* + ** Copy data into team. + */ + if (!cancel) { + CurTeam->ClassCount = numclasses; + i = 0; // current team class index + for (j = 0; j < maxclasses; j++) { + if (teamcount[j] > 0) { + CurTeam->DesiredNum[i] = teamcount[j]; + CurTeam->Class[i] = teamclass[j]; + i++; + } + } + } + + /* + ** Redraw the display. + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + delete [] teamclass; + delete [] teamcount; + + if (cancel) return(-1); + return(0); +} + + +/*********************************************************************************************** + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * * + * This routine will display the cameo image of the potential team member. In the corner, * + * it will show the current quantity of this member for the current team being edited. * + * * + * INPUT: ptr -- Pointer to the member object type. * + * * + * index -- The index into the team dialog box array of selectable objects. This is * + * used to determine the correct X and Y offsets to draw. * + * * + * quant -- The quantity number to display in the corner of the image. * + * * + * house -- The owner of this object. * + * pic_x, pic_y -- x,y coords of upper-left corner to start drawing at + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1995 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Draw_Member(TechnoTypeClass const * ptr, int index, + int quant, HousesType house, int pic_x, int pic_y) +{ + int numcols = (D_DIALOG_W - 32) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int x = pic_x + col * D_PICTURE_W; + int y = pic_y + row * D_ROW_H; + + WindowList[WINDOW_EDITOR][WINDOWX] = 0; + WindowList[WINDOW_EDITOR][WINDOWY] = 0; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = 640 / 8; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = 400; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_GREEN_DOWN, true); + + ptr->Display(x + D_PICTURE_W / 2, y + D_PICTURE_H / 2, WINDOW_EDITOR, house); + + if (quant > 0) { + Fancy_Text_Print("%d", x + 1, y + D_PICTURE_H - 16, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW, quant); + } + + Show_Mouse(); + +#if 0 + int numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int dlg_y = 0; + int x = D_DIALOG_X + 8 + col * D_PICTURE_W; + int y = dlg_y + 8 + 13 + row * D_ROW_H; + + /* + ** Change the window to this box. + */ + WindowList[WINDOW_EDITOR][WINDOWX] = x >> 3; + WindowList[WINDOW_EDITOR][WINDOWY] = y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W >> 3; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_GREEN_DOWN, true); + ptr->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, house); + if (quant > 0) { + Fancy_Text_Print("%d", x+1, y+D_PICTURE_H-8, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_DROPSHADOW, quant); + } + Show_Mouse(); +#endif +} + + +/*************************************************************************** + * MapEditClass::Build_Mission_list -- fills in mission list box * + * * + * INPUT: * + * missioncount # of missions to add to the list * + * missions array of TeamMissionStruct's * + * missionbuf character arrays to store strings in * + * list list box to add strings to * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Mission_List(int missioncount, TeamMissionStruct *missions, + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20], ListClass *list) +{ + /* + ** Start with an empty list + */ + while (list->Count()) { + list->Remove_Item(list->Get_Item(0)); + } + + for (int i = 0; i < missioncount; i++) { + /* + ** generate the string for a MOVE mission; the argument is the + ** letter-designation of the cell to move to. + */ + if (missions[i].Mission == TMISSION_MOVE || missions[i].Mission == TMISSION_UNLOAD) { + sprintf(missionbuf[i],"%s\t%c", + TeamTypeClass::Name_From_Mission(missions[i].Mission), + missions[i].Argument + 'A'); + } else { + + /* + ** All other missions take a numeric argument. + */ + sprintf(missionbuf[i],"%s\t%d", + TeamTypeClass::Name_From_Mission(missions[i].Mission), + missions[i].Argument); + } + + /* + ** Add the string to the list box + */ + list->Add_Item(missionbuf[i]); + } +} + +#endif diff --git a/TIBERIANDAWN/MAPSEL.CPP b/TIBERIANDAWN/MAPSEL.CPP new file mode 100644 index 000000000..673e0d98d --- /dev/null +++ b/TIBERIANDAWN/MAPSEL.CPP @@ -0,0 +1,1274 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mapsel.cpv 1.8 16 Oct 1995 16:49:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPSEL.CPP * + * * + * Programmer : Barry W. Green * + * * + * Start Date : April 17, 1995 * + * * + * Last Update : April 27, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Bit_It_In -- Pixel fade graphic copy. * + * Map_Selection -- Starts the whole process of selecting next map to go to * + * Print_Statistics -- Prints statistics on country selected * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +#ifndef DEMO + +void Map_Selection(void); +void Fading_Byte_Blit(int srcx, int srcy, int destx, int desty, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest); +void Print_Statistics(int country, int xpos, int ypos); +void Cycle_Call_Back_Delay(int time, unsigned char *pal); +int LowMedHiStr(int percentage); +extern int ControlQ; + +unsigned char CountryRemap[256]; +#ifdef OBSOLETE +unsigned char const High16Remap[256]={ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; +#endif + +#define SDE SCEN_DIR_EAST +#define SDW SCEN_DIR_WEST +#define SDN SCEN_DIR_NONE +#define SVA SCEN_VAR_A +#define SVB SCEN_VAR_B +#define SVC SCEN_VAR_C +#define SVN SCEN_VAR_NONE +struct countrylist { + int Choices[2]; // # of map choices this time - 0 = no map selection screen + int Start[2]; + int ContAnim[2]; + int CountryColor[2][3]; + int CountryShape[2][3]; // shape in COUNTRYE.SHP + ScenarioDirType CountryDir[2][3]; + ScenarioVarType CountryVariant[2][3]; +} const CountryArray[27] = { +// GDI SCENARIO CHOICES +/* 0 */ {0}, /*E W*/ /* cont */ /* East colors */ /* West color*/ /* E frame W frame */ +/* 1 */ {{1,1}, { 0, 0}, { 3, 3},{{0x95, 0, 0},{0x95, 0, 0}}, {{17, 0, 0},{17, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 2 */ {{1,1}, {16,16}, { 19, 19},{{0x80, 0, 0},{0x80, 0, 0}}, {{ 0, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 3 */ {{3,3}, {32,32}, { 35, 35},{{0x81,0x82,0x83},{0x81,0x82,0x83}}, {{ 3, 3, 1},{ 3, 3, 1}}, {{SDW,SDW,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVA},{SVN,SVN,SVN}}}, +/* 4 */ {{2,2}, {48,64}, { 51, 67},{{0x84,0x85, 0},{0x86,0x87, 0}}, {{ 4, 4, 0},{ 2, 2, 0}}, {{SDE,SDE,SDN},{SDW,SDW,SDN}},{{SVA,SVA,SVN},{SVA,SVB,SVN}}}, +/* 5 */ {{2,2}, {99,99}, {102,102},{{0x88,0x89, 0},{0x88,0x89, 0}}, {{ 7, 7, 0},{ 7, 7, 0}}, {{SDE,SDE,SDN},{SDE,SDE,SDN}},{{SVA,SVA,SVN},{SVA,SVA,SVN}}}, +/* 6 */ {{2,2}, {80,83}, { 86, 86},{{0x88,0x89, 0},{0x88,0x89, 0}}, {{ 7, 7, 0},{ 7, 7, 0}}, {{SDE,SDE,SDN},{SDE,SDE,SDN}},{{SVA,SVA,SVN},{SVA,SVA,SVN}}}, +/* 7 */ {{2,2}, {115,0}, {118, 0},{{0x8B,0x8A, 0},{0x8B,0x8A, 0}}, {{ 6, 8, 0},{ 6, 8, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 8 */ {{1,1}, {131,0}, {134, 0},{{0x8C, 0, 0},{0x8C, 0, 0}}, {{ 9, 0, 0},{ 9, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 9 */ {{2,1}, {147,0}, {150, 0},{{0x8D,0x8E, 0},{ 0, 0, 0}}, {{10,13, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 10 */ {{1,1}, {163,0}, {166, 0},{{0x8F, 0, 0},{ 0, 0, 0}}, {{16, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 11 */ {{2,1}, {179,0}, {182, 0},{{0x90,0x91, 0},{ 0, 0, 0}}, {{14,15, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 12 */ {{2,1}, {195,0}, {198, 0},{{0x92,0x93, 0},{ 0, 0, 0}}, {{12,12, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 13 */ {{1,1}, {211,0}, {214, 0},{{0x93, 0, 0},{ 0, 0, 0}}, {{12, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 14 */ {{3,1}, { 0,0}, { 3, 0},{{0x81,0x82,0x83},{ 0, 0, 0}}, {{ 0, 0, 0},{ 0, 0, 0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}}}, + +// NOD SCENARIO CHOICES +// choices E/W start continue East colors West colors E shape W shape direction variant +/* 1 */ { {2,1}, { 0,0}, { 3, 0},{{0x80,0x81,0x00},{0,0,0}}, {{ 4, 4, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 2 */ { {2,1}, { 16,0}, { 19, 0},{{0x82,0x83,0x00},{0,0,0}}, {{ 6, 6, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 3 */ { {2,1}, { 32,0}, { 35, 0},{{0x84,0x85,0x00},{0,0,0}}, {{ 5, 5, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 4 */ { {1,1}, { 48,0}, { 51, 0},{{0x86,0x00,0x00},{0,0,0}}, {{ 0, 0, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 5 */ { {3,1}, { 64,0}, { 67, 0},{{0x87,0x88,0x89},{0,0,0}}, {{ 1, 2, 3},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} }, +/* 6 */ { {3,1}, { 80,0}, { 83, 0},{{0x8A,0x8B,0x8C},{0,0,0}}, {{ 9, 7, 8},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} }, +/* 7 */ { {2,1}, { 96,0}, { 99, 0},{{0x8D,0x8E,0x00},{0,0,0}}, {{10,10, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 8 */ { {1,1}, {112,0}, {115, 0},{{0xA0,0x00,0x00},{0,0,0}}, {{ 4, 4, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 9 */ { {2,1}, {128,0}, {131, 0},{{0x8F,0x90,0x00},{0,0,0}}, {{11,15, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 10 */ { {2,1}, {144,0}, {147, 0},{{0x91,0x92,0x00},{0,0,0}}, {{12,16, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 11 */ { {1,1}, {160,0}, {163, 0},{{0x93,0x00,0x00},{0,0,0}}, {{13, 0, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 12 */ { {3,1}, { 0,0}, { 3, 0},{{0x81,0x82,0x83},{0,0,0}}, {{14, 0, 0},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} } +}; + +struct gdistats { + int nameindex; + int pop; + int area; + int capital; + int govt; + int gdp; + int conflict; + int military; +} const GDIStats[]={ +// Name Pop Area Capital Government GDP Conflict Military + { 0,TXT_MAP_P01, TXT_MAP_A00,TXT_MAP_C00, 0, TXT_MAP_GDP00, TXT_MAP_PC00, 0}, + { 1,TXT_MAP_P02, TXT_MAP_A01,TXT_MAP_C01, 1, TXT_MAP_GDP01, TXT_MAP_PC01, 3}, + { 1,TXT_MAP_P02, TXT_MAP_A01,TXT_MAP_C01, 1, TXT_MAP_GDP01, TXT_MAP_PC02, 3}, + { 2,TXT_MAP_P03, TXT_MAP_A02,TXT_MAP_C02, 0, TXT_MAP_GDP00, TXT_MAP_PC03, 1}, + { 3,TXT_MAP_P04, TXT_MAP_A03,TXT_MAP_C03, 3, TXT_MAP_GDP02, TXT_MAP_PC04, 1}, + { 3,TXT_MAP_P04, TXT_MAP_A03,TXT_MAP_C03, 3, TXT_MAP_GDP02, TXT_MAP_PC04, 1}, + { 4,TXT_MAP_P05, TXT_MAP_A04,TXT_MAP_C04, 2, TXT_MAP_GDP03, TXT_MAP_PC05, 5}, + { 4,TXT_MAP_P05, TXT_MAP_A04,TXT_MAP_C04, 2, TXT_MAP_GDP03, TXT_MAP_PC06, 5}, + { 5,TXT_MAP_P06, TXT_MAP_A05,TXT_MAP_C05, 0, TXT_MAP_GDP04, TXT_MAP_PC07, 2}, + { 5,TXT_MAP_P06, TXT_MAP_A05,TXT_MAP_C05, 0, TXT_MAP_GDP04, TXT_MAP_PC07, 2}, + { 6,TXT_MAP_P07, TXT_MAP_A06,TXT_MAP_C06, 0, TXT_MAP_GDP00, TXT_MAP_PC08, 0}, + { 7,TXT_MAP_P08, TXT_MAP_A07,TXT_MAP_C07, 4, TXT_MAP_GDP05, TXT_MAP_PC00, 2}, + { 8,TXT_MAP_P09, TXT_MAP_A08,TXT_MAP_C08, 4, TXT_MAP_GDP06, TXT_MAP_PC10, 2}, + { 9,TXT_MAP_P10, TXT_MAP_A09,TXT_MAP_C09, 0, TXT_MAP_GDP07, TXT_MAP_PC11, 1}, + {10,TXT_MAP_P11, TXT_MAP_A10,TXT_MAP_C10, 0, TXT_MAP_GDP08, TXT_MAP_PC12, 2}, + {11,TXT_MAP_P12, TXT_MAP_A11,TXT_MAP_C11, 5, TXT_MAP_GDP09, TXT_MAP_PC13, 3}, + {12,TXT_MAP_P13, TXT_MAP_A12,TXT_MAP_C12, 6, TXT_MAP_GDP10, TXT_MAP_PC14, 2}, + {13,TXT_MAP_P14, TXT_MAP_A13,TXT_MAP_C13, 0, TXT_MAP_GDP11, TXT_MAP_PC15, 2}, + {14,TXT_MAP_P15, TXT_MAP_A14,TXT_MAP_C14, 0, TXT_MAP_GDP12, TXT_MAP_PC16, 3}, + {14,TXT_MAP_P15, TXT_MAP_A14,TXT_MAP_C14, 0, TXT_MAP_GDP12, TXT_MAP_PC17, 3}, + {15,TXT_MAP_P16, TXT_MAP_A15,TXT_MAP_C15, 7, TXT_MAP_GDP13, TXT_MAP_PC18, 4}, +// Hack in a slot for Estonia + {34,TXT_MAP_P17, TXT_MAP_A16,TXT_MAP_C16, 0, TXT_MAP_GDP00, TXT_MAP_PC19, 0} +}; + +struct nodstats { + int nameindex; + int pop; + int expendable; + int capital; + int govt; + int corruptible; + int worth; + int conflict; + int military; + int probability; +} const NodStats[]={ +// Name Pop Expendable Capital Government Corruptible Worth Conflict Military Probability + {16, TXT_MAP_P18, 38, TXT_MAP_C17, 8, 86, TXT_MAP_GDP14, TXT_MAP_PC20, 0, 23}, + {17, TXT_MAP_P19, 75, TXT_MAP_C18, 0, 18, TXT_MAP_GDP15, TXT_MAP_PC21, 1, 82}, + {17, TXT_MAP_P19, 75, TXT_MAP_C18, 0, 18, TXT_MAP_GDP15, TXT_MAP_PC22, 1, 82}, + {18, TXT_MAP_P20, 50, TXT_MAP_C19, 9, 52, TXT_MAP_GDP16, TXT_MAP_PC23, 0, 72}, + {18, TXT_MAP_P20, 50, TXT_MAP_C19, 9, 52, TXT_MAP_GDP16, TXT_MAP_PC24, 0, 72}, + {19, TXT_MAP_P21, 80, TXT_MAP_C20, 0, 85, TXT_MAP_GDP17, TXT_MAP_PC25, 2, 35}, + {19, TXT_MAP_P21, 80, TXT_MAP_C20, 0, 85, TXT_MAP_GDP17, TXT_MAP_PC26, 2, 35}, + {20, TXT_MAP_P22, 50, TXT_MAP_C21, 10, 48, TXT_MAP_GDP17, TXT_MAP_PC27, 2, 24}, + {21, TXT_MAP_P23, 33, TXT_MAP_C22, 0, 28, TXT_MAP_GDP18, TXT_MAP_PC28, 3, 67}, + {22, TXT_MAP_P24, 75, TXT_MAP_C23, 6, 17, TXT_MAP_GDP19, TXT_MAP_PC29, 2, 80}, + {23, TXT_MAP_P25, 60, TXT_MAP_C24, 7, 93, TXT_MAP_GDP20, TXT_MAP_PC30, 3, 50}, + {24, TXT_MAP_P26, 5, TXT_MAP_C25, 0, 84, TXT_MAP_GDP21, TXT_MAP_PC31, 2, 22}, + {25, TXT_MAP_P27, 55, TXT_MAP_C26, 0, 48, TXT_MAP_GDP22, TXT_MAP_PC32, 3, 62}, + {26, TXT_MAP_P28, 65, TXT_MAP_C27, 0, 41, TXT_MAP_GDP23, TXT_MAP_PC33, 2, 49}, + {27, TXT_MAP_P29, 72, TXT_MAP_C28, 0, 74, TXT_MAP_GDP24, TXT_MAP_PC34, 3, 54}, + {27, TXT_MAP_P29, 72, TXT_MAP_C28, 0, 74, TXT_MAP_GDP24, TXT_MAP_PC35, 3, 54}, + {17, TXT_MAP_P30, 45, TXT_MAP_C29, 6, 3, TXT_MAP_GDP15, TXT_MAP_PC36, 3, 100}, + {28, TXT_MAP_P31, 45, TXT_MAP_C30, 0, 63, TXT_MAP_GDP25, TXT_MAP_PC37, 2, 66}, + {29, TXT_MAP_P32, 55, TXT_MAP_C31, 0, 27, TXT_MAP_GDP26, TXT_MAP_PC38, 2, 68}, + {30, TXT_MAP_P33, 5, TXT_MAP_C32, 0, 65, TXT_MAP_GDP27, TXT_MAP_PC39, 4, 74}, + {31, TXT_MAP_P34, 65, TXT_MAP_C33, 0, 52, TXT_MAP_GDP19, TXT_MAP_PC40, 2, 84}, + {32, TXT_MAP_P35, 2, TXT_MAP_C34, 11, 12, TXT_MAP_GDP28, TXT_MAP_PC41, 2, 92}, + {33, TXT_MAP_P36, 10, TXT_MAP_C35, 0, 8, TXT_MAP_GDP29, TXT_MAP_PC42, 1, 100} +}; + + + +/*********************************************************************************************** + * Map_Selection -- Starts the whole process of selecting next map to go to * + * * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1995 BWG : Created. * + *=============================================================================================*/ +void Map_Selection(void) +{ + void *anim, *progress, *oldfont, *greyearth, *greyearth2; + unsigned char localpalette[768]; + int scenario, lastscenario; + int house = PlayerPtr->Class->House; + int attackxcoord = 0; + + static int const _countryx[]={195,217,115,167, + 244, 97,130,142, + 171,170,139,158, + 180,207,177,213, + 201,198, + /* Nod countries */ + 69, 82,105,119, + 184,149,187,130, + 153,124,162,144, + 145,164,166,200, + 201}; + static int const _countryy[]={ 35, 57, 82, 75, + 93,111,108, 91, + 100,111,120,136, + 136,117,158,143, + 167,21, + /* Nod countries */ + 45, 80, 75, 76, + 31, 64, 69, 89, + 88,106,115,139, + 168,164,183,123, + 154}; + //static char const _greenpal[]={0,1,0x42,3,0x43,5,0x44,7,0x44,9,10,1,12,13,0x41,15}; + static char const _greenpal[]={0,0x41,0x42,0x43,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44}; + static char const _othergreenpal[]={0,0x21,0x22,0x23,0x24,0x25,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26}; + static char const _regpal[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + GraphicBufferClass backpage(20*6,8); + + unsigned char *grey2palette = new unsigned char[768]; + unsigned char *progresspalette = new unsigned char[768]; + + + Keyboard::Clear(); + oldfont = Set_Font(ScoreFontPtr); + Set_Font_Palette(_regpal); + Set_Palette(BlackPalette); + + scenario = Scenario + ((house == HOUSE_GOOD) ? 0 : 14); + if (house == HOUSE_GOOD) { + lastscenario = (Scenario == 14); + if (Scenario == 15) return; + } else { + lastscenario = (Scenario == 12); + if (Scenario == 13) return; + } + + // Check if they're even entitled to map selection this time + if (CountryArray[scenario].Choices[ScenDir]==0) return; + + Theme.Queue_Song(THEME_MAP1); + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + + /* + ** Extra graphic buffer to draw text into + */ + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + + /* + ** Now start the process where we fade the gray earth in. + */ + greyearth = Open_Animation("GREYERTH.WSA", NULL, 0, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), localpalette); + greyearth2 = Open_Animation("E-BWTOCL.WSA", NULL, 0, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), grey2palette); + + /* + ** Load the spinning-globe anim + */ + if (house == HOUSE_GOOD) { + //anim = Open_Animation("EARTH_E.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("HEARTH_E.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + //progress = Open_Animation(lastscenario ? "BOSNIA.WSA" : "EUROPE.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + progress = Open_Animation(lastscenario ? "HBOSNIA.WSA" : "EUROPE.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + } else { + //anim = Open_Animation("EARTH_A.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("HEARTH_A.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + //progress = Open_Animation(lastscenario ? "S_AFRICA.WSA" : "AFRICA.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + progress = Open_Animation(lastscenario ? "HSAFRICA.WSA" : "AFRICA.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + } + + void const * appear1 = MixFileClass::Retrieve("APPEAR1.AUD"); + void const * sfx4 = MixFileClass::Retrieve("SFX4.AUD"); + void const * text2 = MixFileClass::Retrieve("TEXT2.AUD"); + void const * target1 = MixFileClass::Retrieve("TARGET1.AUD"); + void const * target2 = MixFileClass::Retrieve("TARGET2.AUD"); +// void const * target3 = MixFileClass::Retrieve("TARGET3.AUD"); + void const * newtarg1 = MixFileClass::Retrieve("NEWTARG1.AUD"); + void const * beepy2 = MixFileClass::Retrieve("BEEPY2.AUD"); + void const * beepy3 = MixFileClass::Retrieve("BEEPY3.AUD"); + void const * beepy6 = MixFileClass::Retrieve("BEEPY6.AUD"); + void const * world2 = MixFileClass::Retrieve("WORLD2.AUD"); + void const * country1 = MixFileClass::Retrieve("COUNTRY1.AUD"); + void const * scold1 = MixFileClass::Retrieve("SCOLD1.AUD"); + + SysMemPage.Clear(); + PseudoSeenBuff->Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP1.PAL"); +// SeenBuff.Blit(HidPage); + Animate_Frame(greyearth, SysMemPage, 0); + + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff); + PseudoSeenBuff->Put_Pixel(237,92,TBLACK); + PseudoSeenBuff->Put_Pixel(237,93,TBLACK); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,"MAP1.PAL"); + + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOCL.PAL"); + Play_Sample(appear1, 255, Options.Normalize_Sound(110)); + Fade_Palette_To(localpalette, FADE_PALETTE_MEDIUM, Call_Back); + int i; + for (i = 1; i < Get_Animation_Frame_Count(greyearth); i++) { + Call_Back_Delay(4); + Animate_Frame(greyearth, *PseudoSeenBuff, i); + } + Close_Animation(greyearth); + Write_Interpolation_Palette("MAP_LOCL.PAL"); + + Call_Back_Delay(4); + SysMemPage.Clear(); + Animate_Frame(greyearth2, SysMemPage, 0); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = grey2palette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_GRY2.PAL"); + Wait_Vert_Blank(); + Set_Palette(grey2palette); + SysMemPage.Blit(*PseudoSeenBuff); + Call_Back_Delay(4); + for (i = 1; i < Get_Animation_Frame_Count(greyearth2); i++) { + Animate_Frame(greyearth2,*PseudoSeenBuff,i); + Call_Back_Delay(4); + } + Close_Animation(greyearth2); + Write_Interpolation_Palette("MAP_GRY2.PAL"); + + /* + ** Copy the first frame up to the seenpage (while screen is black) + */ + SysMemPage.Clear(); + Animate_Frame(anim, SysMemPage, 1);//, 0,0, (WSAType)0,0,0); + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + Stop_Speaking(); + + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); +// if (Keyboard::Check()) CountDownTimer.Set(0); + } + +// Keyboard::Clear(); + + /* + ** now make the grid appear + */ + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + Play_Sample(sfx4, 255, Options.Normalize_Sound(130)); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + + + int frame = 1; + + while (frame < Get_Animation_Frame_Count(anim)) { + if (frame == 16 || frame == 33 || frame == 44 || frame == 70 || frame == 73) Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (frame == 21 || frame == 27) Play_Sample(target1, 255, Options.Normalize_Sound(90)); + if (frame == 45 || frame == 47 || frame == 49) Play_Sample(beepy6, 255, Options.Normalize_Sound(90)); + if (frame == 51) Play_Sample(world2, 255, Options.Normalize_Sound(90)); + if (frame == 70 || frame == 72) Play_Sample(beepy2, 255, Options.Normalize_Sound(90)); + if (frame == 74) Play_Sample(target2, 255, Options.Normalize_Sound(110)); + + switch (frame){ + case 1: + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_READING_IMAGE_DATA), 0,10,_othergreenpal)); + break; + + case 16: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_READING_IMAGE_DATA)), 2*(10 + 12), BLACK); + break; + + case 17: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_READING_IMAGE_DATA)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass("ANALYZING", 0,10,_othergreenpal)); + break; + + case 33: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ANALYZING)), 2*(10 + 12), BLACK); + break; + + case 34: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ANALYZING)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_ENHANCING_IMAGE_DATA), 0,10,_othergreenpal)); + break; + + case 44: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE_DATA)), 2*(10 + 12), BLACK); + break; + + case 45: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE_DATA)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String(TXT_ISOLATING_OPERATIONAL_THEATER), 0,10,_othergreenpal)); + break; + + case 70: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String(TXT_ISOLATING_OPERATIONAL_THEATER)), 2*(10 + 12), BLACK); + break; + + case 71: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String(TXT_ISOLATING_OPERATIONAL_THEATER)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES), 0,10,_othergreenpal)); + break; + + case 74: + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_FOR_VISUAL_REFERENCE), 0,22,_othergreenpal)); + break; + } + + + Animate_Frame(anim, *PseudoSeenBuff, frame++); + Call_Back_Delay(/*Keyboard::Check() ? 0 :*/ 3); + } + + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES)), 2*(10 + 24), BLACK); + Call_Back_Delay (1); + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES)), 2*(10 + 24), TBLACK); + Call_Back_Delay (1); + + Close_Animation(anim); + + Keyboard::Clear(); + BlitList.Clear(); + + /* + ** Freeze on the map of Europe or Africa + */ + + SysMemPage.Clear(); + Animate_Frame(progress,SysMemPage,0); + SysMemPage.Blit(*PseudoSeenBuff); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = progresspalette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_PROG.PAL"); + + GraphicBufferClass *europe = new GraphicBufferClass(SysMemPage.Get_Width(),SysMemPage.Get_Height(),(GBC_Enum)0); + SysMemPage.Blit(*europe); + + /* + ** Now show territories as they existed last scenario + */ + int startframe = CountryArray[scenario].Start[ScenDir]; + if (startframe) { + Animate_Frame(progress,SysMemPage,startframe); + SysMemPage.Blit(*PseudoSeenBuff); + } + Set_Palette(progresspalette); + Call_Back_Delay(45); + //Write_Interpolation_Palette("MAP_PROG.PAL"); + + /* + ** Now dissolve in first advance of territories + */ + int xcoord = (house == HOUSE_GOOD ? 0 : 204); + SysMemPage.Blit(backpage, xcoord,1, 0,0, 20*6,8); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_MAP_GDI,0,2,_greenpal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_MAP_NOD,xcoord,2,_greenpal)); + } + Call_Back_Delay(60); + + Play_Sample(country1, 255, Options.Normalize_Sound(90)); + Animate_Frame(progress,SysMemPage,startframe+1); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff,&SeenBuff,1,1); + backpage.Blit(SysMemPage, 0,0, xcoord,1, 20*6,8); + Call_Back_Delay(85); + + /* + ** Now dissolve in second advance of territories + */ +#ifdef FRENCH + PseudoSeenBuff->Fill_Rect(xcoord,0,xcoord + 6*16 + 10,8,BLACK); + TextPrintBuffer->Fill_Rect(xcoord*2,0,2*(xcoord + 6*16 + 10),2*8,BLACK); +#else + PseudoSeenBuff->Fill_Rect(xcoord,0,xcoord + 6*16,8,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,0,2*(xcoord + 6*16),2*8,BLACK); +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,NULL); + SysMemPage.Blit(backpage, xcoord,1, 0,0, 20*6,8); + if (!lastscenario) { + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_MAP_NOD,0,12,_greenpal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_MAP_GDI,xcoord,12,_greenpal)); + } + Call_Back_Delay(65); + } + + Play_Sample(country1, 255, Options.Normalize_Sound(90)); + Animate_Frame(progress,SysMemPage,startframe+2); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff,1,1); + backpage.Blit(SysMemPage, 0,0, xcoord,11, 20*6,8); + if (!lastscenario) Call_Back_Delay(85); +// Set_Font(oldfont); +#ifdef FRENCH + PseudoSeenBuff->Fill_Rect(xcoord,12,xcoord+6*16+10,20,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,2*12,2*(xcoord+6*16+10),2*20,BLACK); +#else + PseudoSeenBuff->Fill_Rect(xcoord,12,xcoord+6*16,20,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,2*12,2*(xcoord+6*16),2*20,BLACK); +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,NULL); + + startframe = CountryArray[scenario].ContAnim[ScenDir]; + + /* + ** Now print the text over the page + */ + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_MAP_LOCATE,0,160,_greenpal)); + Call_Back_Delay(20); + Alloc_Object(new ScorePrintClass(TXT_MAP_NEXT_MISSION, 0,170,_greenpal)); +#if (GERMAN | FRENCH) + Call_Back_Delay(20); + Alloc_Object(new ScorePrintClass(TXT_MAP_NEXT_MISS2, 0,180,_greenpal)); +#endif + Call_Back_Delay(50); + + /* + ** If we're on the last scenario, erase that text before doing the crosshairs + */ + if (lastscenario) { +#if (GERMAN | FRENCH) + SysMemPage.Fill_Rect(0,160, 20*6,186, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,186, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*186, BLACK); + SeenBuff.Fill_Rect(0,2*160, 2*20*6,2*186, TBLACK); + HidPage.Fill_Rect(0,2*160, 2*20*6,2*186, TBLACK); +#else + SysMemPage.Fill_Rect(0,160, 20*6,176, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,176, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*176, BLACK); + SeenBuff.Fill_Rect(0,2*160, 2*20*6,2*176, TBLACK); + HidPage.Fill_Rect(0,2*160, 2*20*6,2*176, TBLACK); +#endif + BlitList.Clear(); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff); + } + + /* + ** Fix up the palette that seems different for the last scenario + */ + if (lastscenario){ + InterpolationPaletteChanged = TRUE; + InterpolationPalette = CurrentPalette; + if (house == HOUSE_GOOD){ + Read_Interpolation_Palette("LASTSCNG.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, "LASTSCNG.PAL"); + }else{ + Read_Interpolation_Palette("LASTSCNB.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, "LASTSCNB.PAL"); + } + } + + + int q = 0; + for (frame = 0; frame < ((lastscenario) ? Get_Animation_Frame_Count(progress)-4 : 13); frame++) { + if (!frame) Play_Sample(beepy3, 255, Options.Normalize_Sound(90)); + if (frame == 2) Play_Sample(beepy3, 255, Options.Normalize_Sound(90)); + if (frame == 6) Play_Sample(newtarg1, 255, Options.Normalize_Sound(90)); + + + if (lastscenario){ + switch ( frame ){ + + case 23: + if (house == HOUSE_GOOD){ + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 0, 10, _othergreenpal)); + }else{ +#if (FRENCH) + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 180, 10, _othergreenpal)); +#else + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 210, 10, _othergreenpal)); +#endif //(FRENCH) + } + + + case 35: + if (house == HOUSE_GOOD){ + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE)), 2*(10 + 12), BLACK); + }else{ +#if (FRENCH) + TextPrintBuffer->Fill_Rect (2*180, 2*10, 2*(180+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), BLACK); +#else + TextPrintBuffer->Fill_Rect (2*210, 2*10, 2*(210+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), BLACK); +#endif //(FRENCH) + } + break; + + case 36: + if (house == HOUSE_GOOD){ + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE)), 2*(10 + 12), TBLACK); + }else{ +#if (FRENCH) + TextPrintBuffer->Fill_Rect (2*180, 2*10, 2*(180+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), TBLACK); +#else + TextPrintBuffer->Fill_Rect (2*210, 2*10, 2*(210+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), TBLACK); +#endif //(FRENCH) + } + break; + } + } + + Animate_Frame(progress,*PseudoSeenBuff,startframe + frame); + Call_Back_Delay(6); + /* Cause it to cycle on the flashing on the country for a little while */ + if (!lastscenario && frame == 4 && q < 4) { + frame = 2; + q++; + } + } + + int selection = 0, color = 0; + // erase the "Locating Coordinates" message... + Play_Sample(beepy6, 255, Options.Normalize_Sound(90)); + if (!lastscenario) { +#if (GERMAN | FRENCH) + SysMemPage.Fill_Rect( 0,160, 20*6,186, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,186, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*186, BLACK); +#else + SysMemPage.Fill_Rect( 0,160, 20*6,176, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,176, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*176, BLACK); +#endif + } + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + + /* + ** Now the crosshairs are over the target countries - loop until a + ** selection is made? + */ + int done = 0; + int framecounter = 0; + + if (house == HOUSE_GOOD) { + Load_Uncompress(CCFileClass(lastscenario ? "CLICK_EB.CPS" : "CLICK_E.CPS"), SysMemPage, SysMemPage); + } else { + Load_Uncompress(CCFileClass(lastscenario ? "CLICK_SA.CPS" : "CLICK_A.CPS"), SysMemPage, SysMemPage); + if (lastscenario) attackxcoord = 200; + } + +// Set_Font(ScoreFontPtr); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_MAP_SELECT,attackxcoord,160,_greenpal)); + Cycle_Call_Back_Delay(16,progresspalette); + Alloc_Object(new ScorePrintClass(TXT_MAP_TO_ATTACK,attackxcoord,170,_greenpal)); + Cycle_Call_Back_Delay(24,progresspalette); + while (Get_Mouse_State() > 0) Show_Mouse(); + + Keyboard::Clear(); + while (!done) { + Cycle_Call_Back_Delay(1,progresspalette); + + // Check for the mouse button + if (Keyboard::Check()) { + if ((Keyboard::Get() & 0x10FF) == KN_LMOUSE) { + for (selection = 0; selection < CountryArray[scenario].Choices[ScenDir]; selection++) { + color = SysMemPage.Get_Pixel(Get_Mouse_X()/2,Get_Mouse_Y()/2); + + /* + ** Special hack for Egypt the second time through + */ + if (CountryArray[scenario].CountryColor[ScenDir][selection] == 0xA0) { + if ((color == 0x80) || (color == 0x81)) color = 0xA0; + } + + if (CountryArray[scenario].CountryColor[ScenDir][selection] == color) { + Play_Sample(world2, 255, Options.Normalize_Sound(90)); + done = 1; + break; + } else { + Play_Sample(scold1, 255, Options.Normalize_Sound(90)); + } + } + } + } + } + ScenVar = CountryArray[scenario].CountryVariant[ScenDir][selection]; + ScenDir = CountryArray[scenario].CountryDir[ScenDir][selection]; + + if (!lastscenario) { + Close_Animation(progress); + + /* + ** Now it's time to highlight the country we're going to. + */ + void const * countryshape = MixFileClass::Retrieve(house == HOUSE_GOOD ? "COUNTRYE.SHP" : "COUNTRYA.SHP"); + + Hide_Mouse(); + // erase "Select country to attack" + PseudoSeenBuff->Fill_Rect(attackxcoord,160, attackxcoord+(17*6),178,BLACK); + TextPrintBuffer->Fill_Rect(2*attackxcoord,2*160, 2*(attackxcoord+(17*6)),2*178,BLACK); +#if (GERMAN | FRENCH) + PseudoSeenBuff->Fill_Rect(attackxcoord+(17*6),160, attackxcoord+(21*6),178,BLACK); + TextPrintBuffer->Fill_Rect(2*attackxcoord+(17*6*2),2*160, 2*(attackxcoord+(21*6)),2*178,BLACK); +#endif //GERMAN + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + /* + ** Draw the country's shape in non-fading colors + */ + Set_Logic_Page(SysMemPage); + europe->Blit(SysMemPage); + + int shape = CountryArray[scenario].CountryShape[ScenDir][selection]; + int xyindex = shape + (house == HOUSE_GOOD ? 0 : 18); + CC_Draw_Shape(countryshape, shape, + _countryx[xyindex],_countryy[xyindex], + WINDOW_MAIN, SHAPE_WIN_REL | SHAPE_CENTER, 0,0); + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + /* + ** Now clear the palette of all but the country's colors, and fade + ** the palette down + */ + CCFileClass("DARK_E.PAL").Read(localpalette, 768); +// Load_Data("DARK_E.PAL", localpalette, 768); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(localpalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOC2.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff , "MAP_LOC2.PAL"); + Fade_Palette_To(localpalette, FADE_PALETTE_MEDIUM, Call_Back); + + countryshape = 0; + + Print_Statistics(color & 0x7F, _countryx[xyindex], _countryy[xyindex]); + } else { + CCFileClass(house == HOUSE_GOOD ? "DARK_B.PAL" : "DARK_SA.PAL").Read(localpalette, 768); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(localpalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOC3.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff , "MAP_LOC3.PAL"); + Set_Palette(localpalette); +// Load_Data(house == HOUSE_GOOD ? "DARK_B.PAL" : "DARK_SA.PAL", localpalette, 768); + + Hide_Mouse(); +#if (GERMAN | FRENCH) + PseudoSeenBuff->Fill_Rect(attackxcoord, 160, 319, 178, BLACK); // erase "Select country to attack" + TextPrintBuffer->Fill_Rect(2*attackxcoord, 2*160, 639, 2*178, BLACK); // erase "Select country to attack" +#else + PseudoSeenBuff->Fill_Rect(attackxcoord, 160, attackxcoord + (17*6), 199, BLACK); // erase "Select country to attack" + TextPrintBuffer->Fill_Rect(2*attackxcoord, 2*160, 2*(attackxcoord + (17*6)), 2*199, BLACK); // erase "Select country to attack" +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + Animate_Frame(progress, *PseudoSeenBuff, Get_Animation_Frame_Count(progress)-1); + Set_Palette(localpalette); + Close_Animation(progress); + PseudoSeenBuff->Blit(SysMemPage); + Print_Statistics(20, 160, house == HOUSE_GOOD ? 0 : 160); + } + + Theme.Queue_Song(THEME_NONE); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); + delete europe; + delete progresspalette; + delete grey2palette; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); +} + + +/*************************************************************************** + * Print_Statistics -- Prints statistics on country selected * + * * + * * + * * + * INPUT: shape = country #, x & y = country's on-screen coords * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1995 BWG : Created. * + *=========================================================================*/ +void Print_Statistics(int country, int xpos, int ypos) +{ + int index,newx; + void *oldfont; + static int const _gdistatnames[]={ + TXT_MAP_GDISTAT0, + TXT_MAP_GDISTAT1, + TXT_MAP_GDISTAT2, + TXT_MAP_GDISTAT3, + TXT_MAP_GDISTAT4, + TXT_MAP_GDISTAT5, + TXT_MAP_GDISTAT6 + }; + static int const _nodstatnames[]={ + TXT_MAP_GDISTAT0, + TXT_MAP_NODSTAT0, + TXT_MAP_GDISTAT2, + TXT_MAP_GDISTAT3, + TXT_MAP_NODSTAT1, + TXT_MAP_NODSTAT2, + TXT_MAP_GDISTAT5, + TXT_MAP_NODSTAT3, + TXT_MAP_NODSTAT4 + }; + static int const _countryname[]={ + TXT_MAP_COUNTRYNAME0, + TXT_MAP_COUNTRYNAME1, + TXT_MAP_COUNTRYNAME2, + TXT_MAP_COUNTRYNAME3, + TXT_MAP_COUNTRYNAME4, + TXT_MAP_COUNTRYNAME5, + TXT_MAP_COUNTRYNAME6, + TXT_MAP_COUNTRYNAME7, + TXT_MAP_COUNTRYNAME8, + TXT_MAP_COUNTRYNAME9, + TXT_MAP_COUNTRYNAME10, + TXT_MAP_COUNTRYNAME11, + TXT_MAP_COUNTRYNAME12, + TXT_MAP_COUNTRYNAME13, + TXT_MAP_COUNTRYNAME14, + TXT_MAP_COUNTRYNAME15, + TXT_MAP_COUNTRYNAME16, + TXT_MAP_COUNTRYNAME17, + TXT_MAP_COUNTRYNAME18, + TXT_MAP_COUNTRYNAME19, + TXT_MAP_COUNTRYNAME20, + TXT_MAP_COUNTRYNAME21, + TXT_MAP_COUNTRYNAME22, + TXT_MAP_COUNTRYNAME23, + TXT_MAP_COUNTRYNAME24, + TXT_MAP_COUNTRYNAME25, + TXT_MAP_COUNTRYNAME26, + TXT_MAP_COUNTRYNAME27, + TXT_MAP_COUNTRYNAME28, + TXT_MAP_COUNTRYNAME29, + TXT_MAP_COUNTRYNAME30, + TXT_MAP_COUNTRYNAME31, + TXT_MAP_COUNTRYNAME32, + TXT_MAP_COUNTRYNAME33, + TXT_MAP_COUNTRYNAME34 + }; + + static int const _govtnames[]={ + TXT_MAP_GOVT0, + TXT_MAP_GOVT1, + TXT_MAP_GOVT2, + TXT_MAP_GOVT3, + TXT_MAP_GOVT4, + TXT_MAP_GOVT5, + TXT_MAP_GOVT6, + TXT_MAP_GOVT7, + TXT_MAP_GOVT8, + TXT_MAP_GOVT9, + TXT_MAP_GOVT10, + TXT_MAP_GOVT11 + }; + static int const _armynames[]={ + TXT_MAP_ARMY0, + TXT_MAP_ARMY1, + TXT_MAP_ARMY2, + TXT_MAP_ARMY3, + TXT_MAP_ARMY4, + TXT_MAP_ARMY5 + }; + static int const _military[]={ + TXT_MAP_MILITARY0, + TXT_MAP_MILITARY1, + TXT_MAP_MILITARY2, + TXT_MAP_MILITARY3, + TXT_MAP_MILITARY4 + }; + + static char const _greenpal[]={0,0x41,0x42,0x43,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44}; + //static char const _greenpal[]={0,1,0x42,3,0x43,5,0x44,7,0x44,9,10,1,12,13,0x41,15}; + static char _deststr[16]; + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + +#ifdef GERMAN + xpos = 8; +#else + xpos = (xpos > 128) ? 8 : 136; +#endif + ypos = (ypos > 100) ? 8 : 104-6; + if (PlayerPtr->Class->House == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(_countryname[GDIStats[country].nameindex],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_countryname[GDIStats[country].nameindex]))*3); + ypos += 16; + for (index = 0; index < 7; index ++) { + Alloc_Object(new ScorePrintClass(_gdistatnames[index],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_gdistatnames[index]+3))); + newx = xpos + 6*strlen(Text_String(_gdistatnames[index])); + switch (index) { + case 0: + Alloc_Object(new ScorePrintClass(GDIStats[country].pop,newx,ypos,_greenpal)); + break; + case 1: + Alloc_Object(new ScorePrintClass(GDIStats[country].area,newx,ypos,_greenpal)); + break; + case 2: + Alloc_Object(new ScorePrintClass(GDIStats[country].capital,newx,ypos,_greenpal)); + break; + case 3: + Alloc_Object(new ScorePrintClass(_govtnames[GDIStats[country].govt],newx,ypos,_greenpal)); + break; + case 4: + Alloc_Object(new ScorePrintClass(GDIStats[country].gdp,newx,ypos,_greenpal)); + break; + case 5: + Alloc_Object(new ScorePrintClass(GDIStats[country].conflict,newx,ypos,_greenpal)); + break; + case 6: + Alloc_Object(new ScorePrintClass(_armynames[GDIStats[country].military],newx,ypos,_greenpal)); + break; + } + ypos += 8; + } + } else { // Nod statistics + if (country > 30) { + country = 15; // hack for 2nd time in Egypt + } else { + if (country >=15) country++; // hack to account for Egypt + } + country++; + + Alloc_Object(new ScorePrintClass(_countryname[NodStats[country].nameindex],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_countryname[NodStats[country].nameindex]))*3); + ypos += 16; + for (index = 0; index < 9; index ++) { + Alloc_Object(new ScorePrintClass(_nodstatnames[index],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_nodstatnames[index]+3))); + newx = xpos + 6*strlen(Text_String(_nodstatnames[index])); + switch (index) { + case 0: + Alloc_Object(new ScorePrintClass(NodStats[country].pop,newx,ypos,_greenpal)); + break; + case 1: + sprintf(_deststr,"%d%%",NodStats[country].expendable); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + case 2: + Alloc_Object(new ScorePrintClass(NodStats[country].capital,newx,ypos,_greenpal)); + break; + case 3: + Alloc_Object(new ScorePrintClass(_govtnames[NodStats[country].govt],newx,ypos,_greenpal)); + break; + case 4: +#ifdef FIX_ME_LATER + sprintf(_deststr,"%s %d%%",LowMedHiStr(NodStats[country].corruptible),NodStats[country].corruptible); +#endif //FIX_ME_LATER + sprintf(_deststr,"%d%%",NodStats[country].corruptible); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + case 5: + Alloc_Object(new ScorePrintClass(NodStats[country].worth,newx,ypos,_greenpal)); + break; + case 6: + Alloc_Object(new ScorePrintClass(NodStats[country].conflict,newx,ypos,_greenpal)); + break; + case 7: + Alloc_Object(new ScorePrintClass(_military[NodStats[country].military],newx,ypos,_greenpal)); + break; + case 8: + sprintf(_deststr,"%d%%",NodStats[country].probability); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + } + ypos += 8; + } + } + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2,94,193-6,_greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2,160-(17*3),193-6,_greenpal)); +#endif + + int done = 0; + while (!done) { + done = 1; + for (int x = 0; x < MAXSCOREOBJS; x++) if (ScoreObjs[x]) { + done = 0; + Call_Back_Delay(1); + } + } + Keyboard::Clear(); + while (Keyboard::Check()){ + Keyboard::Clear(); + } + while (!Keyboard::Check() && !ControlQ) { + Call_Back_Delay(1); + } + Keyboard::Clear(); + Set_Font(oldfont); +} + +#ifdef NEVER +/*************************************************************************** + * FADING_BYTE_BLIT -- 'Pixelized' incremental byte blit. * + * * + * This routine will perform the same function as Byte_Blit, but will * + * do so in an incremental (one piece at a time) method. This is * + * usefull for graphic 'fades' from one display to another. * + * * + * INPUT: srcx - Source page X byte position of upper left corner. * + * * + * srcy - Source page Y pixel position of upper left corner. * + * * + * destx - Dest page X byte position of upper left corner. * + * * + * desty - Dest page Y pixel position of upper left corner. * + * * + * w - Width of the blit in bytes. * + * * + * h - Height of the blit in pixels. * + * * + * src - PageType of the source page. * + * * + * dest - Page type of the destination. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine, although functionally equivalent to the * + * normal Byte_Blit, is very slow. Only use this when * + * the 'fading' graphic effect is desired. This means the * + * destination page should probably be the SEENPAGE. * + * * + * HISTORY: * + * 07/17/1991 JLB : Created from Bit_It_In() (LJC code). * + * 04/17/1995 BWG : Adapted to the C++ system. * + *=========================================================================*/ +void Fading_Byte_Blit(int srcx, int srcy, int destx, int desty, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest) +{ + unsigned int xindex, // Working array index var. + yindex; // Working y index var. + unsigned int x,y; // Extraction position indexes. + unsigned int tempy; // Temporary working Y index var. + int _xindex[40]; // X position array. + int _yindex[200]; // Y position array. + + // Anticipate two pixel rows per blit. + h >>= 1; + + // This routine is byte-aligned + srcx >>=3; + destx >>=3; + w >>=3; + + for (xindex = 0; xindex < w; xindex++) _xindex[xindex] = xindex; /* init the index array */ + for (yindex = 0; yindex < h; yindex++) _yindex[yindex] = yindex; /* init the index array */ + + /* + ** Shuffle the X indexes around a bit. This gives it + ** that 'random' feel while remaining precise. + */ + for (xindex = 0; xindex < w; xindex++) { + int temp; + + x = IRandom(0,w-1); + temp = _xindex[x]; + _xindex[x] = _xindex[xindex]; + _xindex[xindex] = temp; + } + + /* + ** Shuffle the Y indexes around a bit for the same reason that + ** the x indexes were shuffled. + */ + for (yindex = 0; yindex < h; yindex++) { + int temp; + + y = IRandom(0,h-1); + temp = _yindex[y]; + _yindex[y] = _yindex[yindex]; + _yindex[yindex] = temp; + } + + /* + ** Sweep through the indexes and 'construct' the destination display + ** from a series of miniature Byte_Blits. + */ + for (yindex = 0; yindex < h; yindex++) { + tempy = yindex; + Call_Back(); + for (xindex = 0; xindex < w; xindex++) { + x = _xindex[xindex]; + y = _yindex[tempy]; + tempy++; + if (tempy >= h) tempy = 0; + src->Blit(*dest, (srcx+x)<<3, srcy+(y<<1), (destx+x)<<3, desty+(y<<1), 1<<3, 2); + } + } +} +#endif + + + + + +void Cycle_Call_Back_Delay(int time, unsigned char *pal) +{ + static int _counter; + unsigned char r,g,b; + int i; + + while (time--) { + _counter = (++_counter & 3); + + if (!(_counter & 3) ) { + r = pal[249*3+0]; + g = pal[249*3+1]; + b = pal[249*3+2]; + + for (i = 249; i < 254; i++) { + pal[i*3+0] = pal[(i+1)*3+0]; + pal[i*3+1] = pal[(i+1)*3+1]; + pal[i*3+2] = pal[(i+1)*3+2]; + } + pal[254*3+0] = r; + pal[254*3+1] = g; + pal[254*3+2] = b; + + Set_Palette(pal); + } + Call_Back_Delay(1); + } +} + +int LowMedHiStr(int percentage) +{ + if (percentage < 30) return(TXT_MAP_LMH0); + if (percentage < 70) return(TXT_MAP_LMH1); + return(TXT_MAP_LMH2); +} + + + +#endif //DEMO + + +/*************************************************************************** + * Bit_It_In -- Pixel fade graphic copy. * + * * + * Copies a block of graphic memory using a 'random' pixel algorithm. * + * Typcial use would be to 'fade' some graphic display into another. * + * * + * INPUT: x,y - Pixel position of upper left corner of block. * + * * + * w,y - Pixel width and height of block to pixel blit. * + * * + * src - Page number of the source page. * + * * + * dest - Page number of the destination page. * + * * + * delay - # of frames to wait after each line fades in * + * * + * OUTPUT: none * + * * + * WARNINGS: This function uses PIXEL coordinates for the X and width * + * parameters. This is unlike the Byte_Blit() routine that * + * it most closely resembles. * + * * + * HISTORY: * + * 04/16/1991 JLB : Created. * + * 04/17/1995 BWG : Adapted to C++ library. * + *=========================================================================*/ + +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicViewPortClass * /*seen*/ , int delay, int dagger) +{ + short int *xindex,*yindex; + int n; + int i,j,k,m,j1; + short ScaleBuffer[320+200]; + + xindex = (short int *) ScaleBuffer; + yindex = xindex + 320; + + for (i=0; iLock() && dest->Lock()){ + for (i=0; i=h) j1=0; + + Buffer_Put_Pixel (dest,k,m,Buffer_Get_Pixel(src,k,m)); + //n=src->Get_Pixel(k,m); + //dest->Put_Pixel(k,m,n); + } + if (dagger) for (int q=j; q>=0; q--) { + Buffer_Put_Pixel(dest,160-(j-q),q,Buffer_Get_Pixel(src,160-(j-q),q)); + Buffer_Put_Pixel(dest,160+(j-q),q,Buffer_Get_Pixel(src,160+(j-q),q)); + //dest->Put_Pixel(160-(j-q),q,src->Get_Pixel(160-(j-q),q)); + //dest->Put_Pixel(160+(j-q),q,src->Get_Pixel(160+(j-q),q)); + } + } + src->Unlock(); + dest->Unlock(); + //if (seen){ + //Interpolate_2X_Scale(dest , seen ,NULL); + //} + } +} + +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay, int dagger) +{ + Bit_It_In_Scale (x, y, w, h, src, dest, NULL , delay, dagger); +} + diff --git a/TIBERIANDAWN/MENUS.CPP b/TIBERIANDAWN/MENUS.CPP new file mode 100644 index 000000000..00e44d54c --- /dev/null +++ b/TIBERIANDAWN/MENUS.CPP @@ -0,0 +1,938 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\menus.cpv 2.17 16 Oct 1995 16:50:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MENUS.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 17, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Main_Menu -- Menu processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ccdde.h" + +/***************************** +** Function prototypes +******************************/ + +#ifdef SCENARIO_EDITOR + +PRIVATE int Coordinates_In_Region(int x,int y,int inx1,int iny1,int inx2,int iny2); +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index); +PRIVATE void Flash_Line(char const *text,int xpix,int ypix,unsigned nfgc,unsigned hfgc,unsigned bgc); + +int UnknownKey; + +PRIVATE int MenuUpdate=1; +PRIVATE int MenuSkip; + + +/*=========================================================================*/ +/* SELECT_TO_ENTRY: */ +/* */ +/* This routine converts a selection to the correct string entry. It */ +/* does this by search through a long bitfield starting at position index */ +/* until it finds the correct conversion to entries. */ +/* */ +/* INPUTS: int selection from menu, long the bit field to search, int */ +/* the starting index within the bit field. */ +/* RETURNS: int the index into the table of entries */ +/*=========================================================================*/ +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index) +{ + int placement; + + if (bitfield==0xFFFFFFFFL) /* if all bits are set */ + return(select); /* then it as is */ + + placement=0; /* current pos zero */ + while (select) { /* while still ones */ + if (bitfield & (1L<<(placement+index))) /* if this flagged then */ + select--; /* decrement counter */ + placement++; /* and we moved a place */ + } + while (!(bitfield & (1L<<(placement+index)))) { + placement++; + } + + return(placement); /* return the position */ +} + + +/*=========================================================================*/ +/* FLASH_LINE: */ +/* */ +/* This routine will flash the line at the desired location for the */ +/* menu routine. It is way cool awesome! */ +/* */ +/* INPUTS: char *text, int x position on line, int y position, char */ +/* normal foreground color, char hilight foreground color, char */ +/* background color */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE void Flash_Line(char const *text,int xpix,int ypix,unsigned nfgc,unsigned hfgc,unsigned bgc) +{ + int loop; + + for (loop=0;loop<3;loop++) { + Hide_Mouse(); + Fancy_Text_Print(text,xpix,ypix,hfgc,bgc, TPF_8POINT|TPF_DROPSHADOW); + Delay(2); + Fancy_Text_Print(text,xpix,ypix,nfgc,bgc, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); + Delay(2); + } +} + +/*=========================================================================*/ +/* COORDINATES_IN_REGION: */ +/* */ +/* Test to see if a given pair of coordinates are within the given */ +/* rectangular region. */ +/* */ +/* INPUTS: int x to be tested, int y to be tested, int left x pos, */ +/* int top y pos, int right x pos, int bottom y pos */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE int Coordinates_In_Region(int x,int y,int inx1,int iny1,int inx2,int iny2) +{ + return((x>=inx1)&&(x<=inx2)&&(y>=iny1)&&(y<=iny2)); +} + +#ifdef NEVER +/*=========================================================================*/ +/* FIND_MENU_ITEMS: */ +/* */ +/* This routine finds the real total items in a menu when certain items */ +/* may be disabled by bit fields and the like. This is done by looping */ +/* through the fields, starting at the position passed in index and */ +/* counting the number of bits that are set. */ +/* */ +/* INPUTS: int the maximum number of items possible on the menu, long */ +/* the bit field of enabled and disabled items, char the index */ +/* point to start at within the list. */ +/* RETURNS: int the total number of items in the menu */ +/*=========================================================================*/ + int Find_Menu_Items(int maxitems, unsigned long field, char index) + { + int loop,ctr; + + if (field==0xFFFFFFFFL) /* if all bits are set */ + return(maxitems); /* then maxitems set */ + + for (loop=ctr=0;loop>1; /* adjustment for menus */ + + menuy = WinY+menuptr[MENUY]; /* get the absolute */ + menux = (WinX+menuptr[MENUX])<<3; /* coords of menu */ + normcol = menuptr[NORMCOL]; + litcol = menuptr[HILITE]; + + /* + ** Fetch a pending keystroke from the buffer if there is a keystroke + ** present. If no keystroke is pending then simple mouse tracking will + ** be done. + */ + key = 0; + UnknownKey = 0; + if (Keyboard::Check()) { + key = (Keyboard::Get()&0x18FF); /* mask off all but release bit */ + } + + /* + ** if we are using the mouse and it is installed, then find the mouse + ** coordinates of the menu and if we are not somewhere on the menu get + ** the heck outta here. If we are somewhere on the menu, then figure + ** out the new selected item, and continue forward. + */ + mx1=(WinX<<3)+(menuptr[MENUX]*FontWidth); /* get menu coords */ + my1=(WinY)+(menuptr[MENUY])-halfskip; /* from the menu */ + mx2=mx1+(menuptr[ITEMWIDTH]*FontWidth)-1; /* structure as */ + my2=my1+(menuptr[ITEMSHIGH]*menuskip)-1; /* necessary */ + + tempy=Get_Mouse_Y(); + if (Coordinates_In_Region(Get_Mouse_X(),tempy,mx1,my1,mx2,my2)&& MenuUpdate) { + newitem=(tempy-my1)/menuskip; + } + + switch (key) { + + case KN_UP: /* if the key moves up */ + newitem--; /* new item up one */ + if (newitem<0) /* if invalid new item */ + newitem=maxitem; /* put at list bottom */ + break; + case KN_DOWN: /* if key moves down */ + newitem++; /* new item down one */ + if (newitem>maxitem) /* if new item past */ + newitem=0; /* list end, clear */ + break; + case KN_HOME: /* if top of list key */ + case KN_PGUP: /* is selected then */ + newitem=0; /* new item = top */ + break; + case KN_END: /* if bottom of list is */ + case KN_PGDN: /* selected then */ + newitem=maxitem; /* new item = bottom */ + break; + + /* + ** Handle mouse button press. Set selection and then fall into the + ** normal menu item select logic. + */ + case KN_RMOUSE: + case KN_LMOUSE: + if (Coordinates_In_Region(_Kbd->MouseQX,_Kbd->MouseQY,mx1,my1,mx2,my2)) { + newitem = (_Kbd->MouseQY - my1) / menuskip; + } else { + UnknownKey = key; // Pass the unprocessed button click back. + break; + } + + /* + ** Normal menu item select logic. Will flash line and exit with menu + ** selection number. + */ + case KN_RETURN: /* if a selection is */ + case KN_SPACE: /* made with key */ + case KN_CENTER: + select=newitem; /* flag it made. */ + break; + + case 0: + break; + + /* + ** When no key was pressed or an unknown key was pressed, set the + ** global record of the key and exit normally. + ** EXCEPTION: If the key matches the first letter of any of the + ** menu entries, then presume it as a selection of + ** that entry. + */ + default: + for (idx = 0; idx < menuptr[ITEMSHIGH]; idx++) { + if (toupper(*(text[Select_To_Entry(idx,field,index)])) == toupper(Keyboard::To_ASCII((KeyNumType)(key&0x0FF)))) { + newitem = select = idx; + break; + } + } + UnknownKey = key; + break; + } + + if (newitem!=item) { + Hide_Mouse(); + idx=Select_To_Entry(item,field,index); + drawy=menuy+(item*menuskip); + Fancy_Text_Print(text[idx],menux,drawy,normcol,TBLACK, TPF_8POINT|TPF_DROPSHADOW); + idx=Select_To_Entry(newitem,field,index); + drawy=menuy+(newitem*menuskip); + Fancy_Text_Print(text[idx],menux,drawy,litcol,TBLACK, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); /* resurrect the mouse */ + } + + if (select!=-1) { + idx=Select_To_Entry(select,field,index); + Hide_Mouse(); /* get rid of the mouse */ + drawy=menuy+(newitem*menuskip); + Flash_Line(text[idx], menux, drawy, normcol, litcol, TBLACK); + Show_Mouse(); + select=idx; + } + + menuptr[MSELECTED]=newitem; /* update menu select */ + + return(select); +} + + +/*************************************************************************** + * Do_Menu -- Generic menu processor. * + * * + * This helper function displays a menu of specified entries and waits * + * for the player to make a selection. If a selection is made, then * + * a whole number (starting at 0) is returned matching the entry * + * selected. If ESC is pressed, then -1 is returned. * + * * + * INPUT: strings -- A pointer to an array of pointers to text strings. * + * Each entry in the list will be a menu entry that * + * can be selected. * + * * + * blue -- Should the special blue color be used to display * + * the menu? * + * * + * OUTPUT: Returns with the cardinal number of the selected menu entry. * + * If ESC was pressed, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=========================================================================*/ +int Do_Menu(char const **strings, bool blue) +{ + int count; // Number of entries in this menu. + int length; // The width of the menu (in pixels). + char const **ptr; // Working menu text pointer. + int selection; // Selection from user. + + if (!strings) return(-1); + Set_Logic_Page(SeenBuff); + Keyboard::Clear(); + + /* + ** Determine the number of entries in this string. + */ + ptr = strings; + count = 0; + while (*ptr++) { + count++; + } + MenuList[0][ITEMSHIGH] = count; + + /* + ** Determine the width of the menu by finding the length of the + ** longest menu entry. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_8POINT|TPF_DROPSHADOW); + length = 0; + ptr = strings; + while (*ptr) { + length = MAX(length, (int)String_Pixel_Width(*ptr)); + ptr++; + } + length += 7; + MenuList[0][ITEMWIDTH] = length >> 3; + + /* + ** Adjust the window values to match the size of the + ** specified menu. + */ + WindowList[WINDOW_MENU][WINDOWWIDTH] = MenuList[0][ITEMWIDTH] + 2; + WindowList[WINDOW_MENU][WINDOWX] = 19 - (length >> 4); + WindowList[WINDOW_MENU][WINDOWY] = 174 - (unsigned)(MenuList[0][ITEMSHIGH] * (FontHeight+FontYSpacing)); + WindowList[WINDOW_MENU][WINDOWHEIGHT] = MenuList[0][ITEMSHIGH] * FontHeight + 5 /*11*/; + + /* + ** Display the menu. + */ + Change_Window((int)WINDOW_MENU); + Show_Mouse(); + Window_Box(WINDOW_MENU, blue ? BOXSTYLE_BLUE_UP : BOXSTYLE_RAISED); + Setup_Menu(0, strings, 0xFFFFL, 0, 0); + + Keyboard::Clear(); + selection = -1; + UnknownKey = 0; + while (selection == -1) { + Call_Back(); + selection = Check_Menu(0, strings, NULL, 0xFFL, 0); + if (UnknownKey != 0 || UnknownKey == KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) break; + } + Keyboard::Clear(); + Hide_Mouse(); + + Blit_Hid_Page_To_Seen_Buff(); + Change_Window((int)WINDOW_MAIN); + Map.Flag_To_Redraw(true); + return(selection); +} +#endif + + +/*************************************************************************** + * Main_Menu -- Menu processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * index of item selected, -1 if time out * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/17/1995 BRR : Created. * + *=========================================================================*/ +int Main_Menu(unsigned long timeout) +{ + enum { + D_DIALOG_W = 152*2, + D_DIALOG_H = 136*2, + D_DIALOG_X = 85*2, + D_DIALOG_Y = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_START_W = 125*2, + D_START_H = 9*2, + D_START_X = 98*2, + D_START_Y = 35*2, + +#ifdef BONUS_MISSIONS + D_BONUS_W = 125*2, + D_BONUS_H = 9*2, + D_BONUS_X = 98*2, + D_BONUS_Y = 0, +#endif //BONUS_MISSIONS + + D_INTERNET_W = 125*2, + D_INTERNET_H = 9*2, + D_INTERNET_X = 98*2, + D_INTERNET_Y = 36*2, + + D_LOAD_W = 125*2, + D_LOAD_H = 9*2, + D_LOAD_X = 98*2, + D_LOAD_Y = 53*2, + + D_MULTI_W = 125*2, + D_MULTI_H = 9*2, + D_MULTI_X = 98*2, + D_MULTI_Y = 71*2, + + D_INTRO_W = 125*2, + D_INTRO_H = 9*2, + D_INTRO_X = 98*2, + D_INTRO_Y = 89*2, +#if (GERMAN | FRENCH) + D_EXIT_W = 83*2, +#else + D_EXIT_W = 63*2, +#endif + D_EXIT_H = 9*2, +#if (GERMAN | FRENCH) + D_EXIT_X = 118*2, +#else + D_EXIT_X = 128*2, +#endif + D_EXIT_Y = 111*2, + + }; + +#ifdef NEWMENU + int starty = 25*2; +#endif + + enum { +#ifdef NEWMENU + BUTTON_EXPAND=100*2, + BUTTON_START, +#ifdef BONUS_MISSIONS + BUTTON_BONUS, +#endif //BONUS_MISSIONS + BUTTON_INTERNET, +#else + BUTTON_START=100*2, +#endif + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; + +#ifdef NEWMENU + bool expansions = Expansion_Present(); +#endif + KeyNumType input; // input from user + int retval; // return value + int curbutton; +#ifdef NEWMENU +#ifdef BONUS_MISSIONS + TextButtonClass *buttons[8]; +#else + TextButtonClass *buttons[7]; +#endif //BONUS_MISSIONS +#else + TextButtonClass *buttons[5]; +#endif +// unsigned long starttime; + + ControlClass *commands = NULL; // the button list + +#ifdef NEWMENU +#ifdef BONUS_MISSIONS + int ystep = 13*2; +#else + int ystep = 15*2; +#endif //BONUS_MISSIONS + + if (expansions) ystep -= 2*2; + TextButtonClass expandbtn (BUTTON_EXPAND, TXT_NEW_MISSIONS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, starty, D_START_W, D_START_H); + if (expansions) starty += ystep; + + TextButtonClass startbtn (BUTTON_START, TXT_START_NEW_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, starty, D_START_W, D_START_H); + starty += ystep; + +#ifdef BONUS_MISSIONS + TextButtonClass bonusbtn (BUTTON_BONUS, TXT_BONUS_MISSIONS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_BONUS_X, starty, D_BONUS_W, D_BONUS_H); + starty += ystep; +#endif //BONUS_MISSIONS + + TextButtonClass internetbutton(BUTTON_INTERNET, TXT_INTERNET, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTERNET_X, starty, D_INTERNET_W, D_INTERNET_H); + starty += ystep; + + TextButtonClass loadbtn (BUTTON_LOAD, TXT_LOAD_MISSION, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LOAD_X, starty, D_LOAD_W, D_LOAD_H); + starty += ystep; +#else + + TextButtonClass startbtn (BUTTON_START, TXT_START_NEW_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, D_START_Y, D_START_W, D_START_H); + + TextButtonClass loadbtn (BUTTON_LOAD, TXT_LOAD_MISSION, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LOAD_X, D_LOAD_Y, D_LOAD_W, D_LOAD_H); + +#endif + + +#ifdef DEMO + TextButtonClass multibtn (BUTTON_MULTI, TXT_ORDER_INFO, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, D_MULTI_Y, D_MULTI_W, D_MULTI_H); +#else + +#ifdef NEWMENU + TextButtonClass multibtn (BUTTON_MULTI, TXT_MULTIPLAYER_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, starty, D_MULTI_W, D_MULTI_H); + starty += ystep; + + //TextButtonClass internetbutton(BUTTON_INTERNET, TXT_INTERNET, + // TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + // D_INTERNET_X, starty, D_INTERNET_W, D_INTERNET_H); + //starty += ystep; +#else + TextButtonClass multibtn (BUTTON_MULTI, TXT_MULTIPLAYER_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, D_MULTI_Y, D_MULTI_W, D_MULTI_H); +#endif +#endif + +#ifdef NEWMENU +#ifdef DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_JUST_INTRO, +#else //DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_INTRO, +#endif //DEMO + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTRO_X, starty, D_INTRO_W, D_INTRO_H); + starty += ystep; + + TextButtonClass exitbtn (BUTTON_EXIT, TXT_EXIT_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + //D_EXIT_X, starty); + D_EXIT_X, starty, D_EXIT_W, D_EXIT_H); +#else + D_EXIT_X, starty, D_EXIT_W, D_EXIT_H); +#endif + starty += ystep; + +#else + +#ifdef DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_JUST_INTRO, +#else //DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_INTRO, +#endif //DEMO + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTRO_X, D_INTRO_Y, D_INTRO_W, D_INTRO_H); + + TextButtonClass exitbtn (BUTTON_EXIT, TXT_EXIT_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + //D_EXIT_X, D_EXIT_Y); + D_EXIT_X, D_EXIT_Y, D_EXIT_W, D_EXIT_H); +#else + D_EXIT_X, D_EXIT_Y, D_EXIT_W, D_EXIT_H); +#endif +#endif + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + Keyboard::Clear(); +#if (0) //PG_TO_FIX + starttime = TickCount.Time(); +#endif + /* + ** Create the list + */ + commands = &startbtn; +#ifdef NEWMENU + if (expansions) { + expandbtn.Add_Tail(*commands); + } +#endif +#ifdef BONUS_MISSIONS + bonusbtn.Add_Tail(*commands); +#endif //BONUS_MISSIONS + + +#ifndef DEMO + internetbutton.Add_Tail(*commands); +#endif //DEMO + loadbtn.Add_Tail(*commands); + multibtn.Add_Tail(*commands); + introbtn.Add_Tail(*commands); + exitbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ +#ifdef NEWMENU + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + int butt = 0; + + buttons[butt++] = &expandbtn; + buttons[butt++] = &startbtn; +#ifdef BONUS_MISSIONS + buttons[butt++] = &bonusbtn; +#endif //BONUS_MISSIONS + buttons[butt++] = &internetbutton; + buttons[butt++] = &loadbtn; + buttons[butt++] = &multibtn; + buttons[butt++] = &introbtn; + buttons[butt++] = &exitbtn; +#else + curbutton = 0; + buttons[0] = &startbtn; + buttons[1] = &loadbtn; + buttons[2] = &multibtn; + buttons[3] = &introbtn; + buttons[4] = &exitbtn; +#endif + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** If timeout expires, bail + */ +//PG_TO_FIX +#if (0) + if (timeout && TickCount.Time() - starttime > timeout) { + retval = -1; + process = false; + } +#endif + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + + /* + ** Display the title and text overlay for the menu. + */ + Set_Logic_Page(HidPage); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption (TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); +#ifdef VIRGIN_CHEAT_KEYS +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("Demo%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#endif +// Fancy_Text_Print("V.%d%s%02d", D_DIALOG_X+D_DIALOG_W-5, D_DIALOG_Y+D_DIALOG_H-10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#else +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("Demo%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText); +#endif +#endif + + /* + ** Copy the menu to the visible page. + */ + Hide_Mouse(); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + Set_Logic_Page(SeenBuff); + startbtn.Draw_All(); + if (ScreenWidth==320){ + //ST - 1/2/2019 5:27PM + //ModeX_Blit (SeenBuff.Get_Graphic_Buffer()); + } + display = false; + } else { + if (RunningAsDLL) { + retval = -1; + process = false; + } + } + +#ifndef DEMO + /* + ** Check to see if WChat has told us to start playing an internet game + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + retval = BUTTON_INTERNET - BUTTON_EXPAND; + process = false; + } +#endif //DEMO + + /* + ** Get and process player input. + */ + input = commands->Input(); + switch (input) { +#ifdef NEWMENU + case (BUTTON_EXPAND | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_INTERNET | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#else +#define BUTTON_EXPAND BUTTON_START +#endif + + case (BUTTON_START | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifdef BONUS_MISSIONS + case (BUTTON_BONUS | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#endif //BONUS_MISSIONS + + case (BUTTON_LOAD | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_MULTI | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_INTRO | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_EXIT | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; +#ifdef NEWMENU + if (expansions) { + if (curbutton < 0) { + curbutton = 6; + } + } else { + if (curbutton < 1) { + curbutton = 6; + } + } +#else + if (curbutton < 0) { + curbutton = 4; + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; +#ifdef NEWMENU + if (curbutton > 6) { + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + } +#else + if (curbutton > 4) { + curbutton = 0; + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + retval = curbutton; + process = false; + break; + + default: + break; + } + } + return(retval); +} \ No newline at end of file diff --git a/TIBERIANDAWN/MESSAGE.H b/TIBERIANDAWN/MESSAGE.H new file mode 100644 index 000000000..f498db91d --- /dev/null +++ b/TIBERIANDAWN/MESSAGE.H @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define MESSAGE_NONE 0 // +#define MESSAGE_BUILD_WINDTRAP 1 // You must build a Windtrap +#define MESSAGE_STRUCT_CONCRETE 2 // Concrete: Use concrete to +#define MESSAGE_STRUCT_PALACE 3 // Palace: This is your +#define MESSAGE_STRUCT_LIGHT 4 // Light Factory: The Light +#define MESSAGE_STRUCT_HEAVY 5 // Heavy Factory: The Heavy +#define MESSAGE_STRUCT_HITECH 6 // Hi-Tech Factory: The +#define MESSAGE_STRUCT_IX 7 // House IX: The IX Research +#define MESSAGE_STRUCT_WOR 8 // WOR: Wor is used to train +#define MESSAGE_STRUCT_CONST 9 // Construction Facility: All +#define MESSAGE_STRUCT_WINDTRAP 10 // Windtrap: The windtrap +#define MESSAGE_STRUCT_BARRACKS 11 // Barracks: The Barracks is +#define MESSAGE_STRUCT_STARPORT 12 // Startport: The Starport is +#define MESSAGE_STRUCT_REFINERY 13 // Spice Refinery: The +#define MESSAGE_STRUCT_REPAIR 14 // Repair Facility: The Repair +#define MESSAGE_STRUCT_WALL 15 // Wall: The wall is used for +#define MESSAGE_STRUCT_TURRET 16 // Gun Turret: The cannon +#define MESSAGE_STRUCT_RTURRET 17 // Rocket Turret: The +#define MESSAGE_STRUCT_SILO 18 // Spice Silo: The Spice silo +#define MESSAGE_STRUCT_OUTPOST 19 // Outpost: The Outpost +#define MESSAGE_NEED_CONCRETE 20 // There isn't enough open +#define MESSAGE_SAND 21 // Sand: This is sand terrain. +#define MESSAGE_DUNE 22 // Sand Dunes: These are an +#define MESSAGE_ROCK 23 // Rock: This is rock terrain. +#define MESSAGE_MOUNT 24 // Mountain: Mountains on +#define MESSAGE_SPICE 25 // Spice Field: This is the +#define MESSAGE_ADJACENT 26 // Structures must be placed +#define MESSAGE_SEARCH4SPICE 27 // Search for spice fields to +#define MESSAGE_SANDWORM 28 // Warning: Sandworms diff --git a/TIBERIANDAWN/MISSION.CPP b/TIBERIANDAWN/MISSION.CPP new file mode 100644 index 000000000..e75536d89 --- /dev/null +++ b/TIBERIANDAWN/MISSION.CPP @@ -0,0 +1,474 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mission.cpv 2.18 16 Oct 1995 16:49:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MissionClass::AI -- Processes order script. * + * MissionClass::Assign_Mission -- Give an order to a unit. * + * MissionClass::Commence -- Start script with new order. * + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * MissionClass::Overide_Mission -- temporarily overides the units mission * + * MissionClass::Restore_Mission -- Restores overidden mission * + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * * + * This is the default constructor for the mission class object. It sets the mission * + * handler into a default -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionClass::MissionClass(void) +{ + Status = 0; + Timer = 0; + Mission = MISSION_NONE; + SuspendedMission = MISSION_NONE; + MissionQueue = MISSION_NONE; +} + + +int MissionClass::Mission_Sleep(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Ambush(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Attack(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Capture(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard_Area(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Harvest(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Timed_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Move(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Retreat(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Return(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Stop(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Unload(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Enter(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Construction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Deconstruction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Repair(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Missile(void) {return TICKS_PER_SECOND*30;}; + + +/*********************************************************************************************** + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * * + * Use this routine to set the current mission for this object. This routine will blast * + * over the current mission, bypassing the queue method. Call it when the mission needs * + * to be changed immediately. * + * * + * INPUT: mission -- The mission to set to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Set_Mission(MissionType mission) +{ + Mission = mission; + MissionQueue = MISSION_NONE; +} + + +/*********************************************************************************************** + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * * + * Use this routine to fetch the mission that this object is CURRENTLY acting under. The * + * mission queue may be filled with a immanent mission change, but this routine does not * + * consider that. It only returns the CURRENT mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the mission that this unit is currently following. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionType MissionClass::Get_Mission(void) const +{ + return(Mission == MISSION_NONE ? MissionQueue : Mission); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * * + * This is a debugging function that dumps this class' status to the monochrome screen * + * for review. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(21, 1);mono->Printf("%5.5s[%4.4s]", MissionClass::Mission_Name(Mission), MissionClass::Mission_Name(MissionQueue)); +// mono->Text_Print(MissionClass::Mission_Name(Mission), 21, 1); + mono->Set_Cursor(20, 7);mono->Printf("%2d", (long)Timer); + mono->Set_Cursor(74, 1);mono->Printf("%2d", Status); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * MissionClass::AI -- Processes order script. * + * * + * This routine will process the order script for as much time as * + * possible or until a script delay is detected. This routine should * + * be called for every unit once per game loop (if possible). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/25/1995 JLB : Added new missions. * + *=============================================================================================*/ +void MissionClass::AI(void) +{ + ObjectClass::AI(); + + /* + ** This is the script AI equivalent processing. + */ + if (Timer.Expired() && Strength > 0) { + switch (Mission) { + default: + case MISSION_STICKY: + case MISSION_SLEEP: + Timer = Mission_Sleep(); + break; + + case MISSION_GUARD: + Timer = Mission_Guard(); + break; + + case MISSION_ENTER: + Timer = Mission_Enter(); + break; + + case MISSION_CONSTRUCTION: + Timer = Mission_Construction(); + break; + + case MISSION_DECONSTRUCTION: + Timer = Mission_Deconstruction(); + break; + + case MISSION_CAPTURE: + case MISSION_SABOTAGE: + Timer = Mission_Capture(); + break; + + case MISSION_MOVE: + Timer = Mission_Move(); + break; + + case MISSION_ATTACK: + Timer = Mission_Attack(); + break; + + case MISSION_RETREAT: + Timer = Mission_Retreat(); + break; + + case MISSION_HARVEST: + Timer = Mission_Harvest(); + break; + + case MISSION_GUARD_AREA: + Timer = Mission_Guard_Area(); + break; + + case MISSION_RETURN: + Timer = Mission_Return(); + break; + + case MISSION_STOP: + Timer = Mission_Stop(); + break; + + case MISSION_AMBUSH: + Timer = Mission_Ambush(); + break; + + case MISSION_HUNT: + case MISSION_RESCUE: + Timer = Mission_Hunt(); + break; + + case MISSION_TIMED_HUNT: + Timer = Mission_Timed_Hunt(); + break; + + case MISSION_UNLOAD: + Timer = Mission_Unload(); + break; + + case MISSION_REPAIR: + Timer = Mission_Repair(); + break; + + case MISSION_MISSILE: + Timer = Mission_Missile(); + break; + } + } +} + + +/*********************************************************************************************** + * MissionClass::Commence -- Start script with new order. * + * * + * This routine will start script processing according to any queued * + * order it may have. If there is no queued order, then this routine * + * does nothing. Call this routine whenever the unit is in a good * + * position to change its order (such as when it is stopped). * + * * + * INPUT: none * + * * + * OUTPUT: Did the mission actually change? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 07/14/1994 JLB : Simplified. * + * 06/17/1995 JLB : Returns success flag. * + *=============================================================================================*/ +bool MissionClass::Commence(void) +{ + if (MissionQueue != MISSION_NONE) { + Mission = MissionQueue; + MissionQueue = MISSION_NONE; + + /* + ** Force immediate state machine processing at the first state machine state value. + */ + Timer = 0; + Status = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Assign_Mission -- Give an order to a unit. * + * * + * This assigns an order to a unit. It does NOT do the AI, but merely * + * flags the unit for normal AI processing. At that time the computer * + * will determine the unit's course of action. * + * * + * INPUT: order -- Mission to give the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MissionClass::Assign_Mission(MissionType order) +{ + if (order == MISSION_NONE || Mission == order) return; + +// Status = 0; + MissionQueue = order; +} + + +/*********************************************************************************************** + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * * + * This routine is used to convert an ASCII order name into the actual * + * order number it represents. Typically, this is used when processing * + * a scenario INI file. * + * * + * INPUT: name -- The ASCII order name to process. * + * * + * OUTPUT: Returns with the actual order number that the ASCII name * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/22/1994 JLB : Converted to static member function. * + *=============================================================================================*/ +MissionType MissionClass::Mission_From_Name(char const *name) +{ + MissionType order; + + if (name) { + for (order = MISSION_FIRST; order < MISSION_COUNT; order++) { + if (stricmp(Missions[order], name) == 0) { + return(order); + } + } + } + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * * + * Use this routine to convert a mission number into the ASCII string that represents * + * it. Typical use of this is when generating an INI file. * + * * + * INPUT: mission -- The mission number to convert. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that represents the mission type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +char const * MissionClass::Mission_Name(MissionType mission) +{ + return(mission == MISSION_NONE ? "None" : Missions[mission]); +} + + +/*********************************************************************************************** + * MissionClass::Override_Mission -- temporarily overides the units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +void MissionClass::Override_Mission(MissionType mission, TARGET, TARGET) +{ + if (MissionQueue != MISSION_NONE) { + SuspendedMission = MissionQueue; + } else { + SuspendedMission = Mission; + } + + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * MissionClass::Restore_Mission -- Restores overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +bool MissionClass::Restore_Mission(void) +{ + if (SuspendedMission != MISSION_NONE) { + Assign_Mission(SuspendedMission); + SuspendedMission= MISSION_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** +** Unit order names. These names correspond to the player selectable orders +** a unit can have. The system initiated orders have no use for the ASCII name +** associated, but they are listed here for completeness sake. +*/ +char const * MissionClass::Missions[MISSION_COUNT] = { + "Sleep", + "Attack", + "Move", + "Retreat", + "Guard", + "Sticky", + "Enter", + "Capture", + "Harvest", + "Area Guard", + "Return", + "Stop", + "Ambush", + "Hunt", + "Timed Hunt", + "Unload", + "Sabotage", + "Construction", + "Selling", + "Repair", + "Rescue", + "Missile", +}; diff --git a/TIBERIANDAWN/MISSION.H b/TIBERIANDAWN/MISSION.H new file mode 100644 index 000000000..5ee9d977f --- /dev/null +++ b/TIBERIANDAWN/MISSION.H @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mission.h_v 2.16 16 Oct 1995 16:45:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISSION_H +#define MISSION_H + +#include "object.h" +#include "monoc.h" + +/**************************************************************************** +** This handles order assignment and tracking. The order is used to guide +** overall AI processing. +*/ +class MissionClass : public ObjectClass +{ + public: + + /* + ** This the tactical strategy to use. It is used by the unit script. This + ** is a general guide for unit AI processing. + */ + MissionType Mission; + MissionType SuspendedMission; + + /* + ** The order queue is used for orders that should take effect when the vehicle + ** has reached the center point of a cell. The queued order number is +1 when stored here + ** so that 0 will indicated there is no queued order. + */ + MissionType MissionQueue; + + char Status; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + MissionClass(void); + virtual ~MissionClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + virtual MissionType Get_Mission(void) const; + virtual void Assign_Mission(MissionType mission); + virtual bool Commence(void); + virtual void AI(void); + + /* + ** Support functions. + */ + virtual int Mission_Sleep(void); + virtual int Mission_Ambush(void); + virtual int Mission_Attack(void); + virtual int Mission_Capture(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int Mission_Timed_Hunt(void); + virtual int Mission_Move(void); + virtual int Mission_Retreat(void); + virtual int Mission_Return(void); + virtual int Mission_Stop(void); + virtual int Mission_Unload(void); + virtual int Mission_Enter(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Repair(void); + virtual int Mission_Missile(void); + virtual void Set_Mission(MissionType mission); + + static char const * Mission_Name(MissionType order); + static MissionType Mission_From_Name(char const *name); + virtual void Override_Mission(MissionType mission, TARGET, TARGET); + virtual bool Restore_Mission(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + private: + + /* + ** This the thread processing timer. When this value counts down to zero, then + ** more script processing may occur. + */ + TCountDownTimerClass Timer; + + /* + ** These are the order names as ASCII strings. + */ + static char const * Missions[MISSION_COUNT]; +}; + + +#endif diff --git a/TIBERIANDAWN/MIXFILE.CPP b/TIBERIANDAWN/MIXFILE.CPP new file mode 100644 index 000000000..2b3923192 --- /dev/null +++ b/TIBERIANDAWN/MIXFILE.CPP @@ -0,0 +1,522 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mixfile.cpv 2.18 16 Oct 1995 16:48:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.* + * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.* + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * MixFileClass::Free -- Uncaches a cached mixfile. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "mixfile.h" + + +template int Compare(T const *obj1, T const *obj2) { + if (*obj1 < *obj2) return(-1); + if (*obj1 > *obj2) return(1); + return(0); +}; + + +/* +** This is the pointer to the first mixfile in the list of mixfiles registered +** with the mixfile system. +*/ +MixFileClass * MixFileClass::First = 0; + + +/*********************************************************************************************** + * MixFileClass::Free -- Uncaches a cached mixfile. * + * * + * Use this routine to uncache a mixfile that has been cached. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. * + * * + * OUTPUT: bool; Was the mixfile found and freed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Free(char const *filename) +{ + MixFileClass * ptr = Finder(filename); + + if (ptr) { + ptr->Free(); + return(true); + } + return(false); +} + + +//#ifndef NOMEMCHECK +void MixFileClass::Free_All(void) +{ + while (First) { + delete First; + } +} +//#endif + + +/*********************************************************************************************** + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * * + * This destructor will free all memory allocated by this mixfile and will remove it from * + * the system. A mixfile removed in this fashion must be created anew in order to be * + * subsequent used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 01/06/1995 JLB : Puts mixfile header table into EMS. * + *=============================================================================================*/ +MixFileClass::~MixFileClass(void) +{ + + /* + ** Deallocate any allocated memory. + */ + if (Filename) { + free((char *)Filename); + } + if (Data) { + delete [] Data; + } + if (Buffer) { + delete [] Buffer; + } + + /* + ** Unlink this mixfile object from the chain. + */ + if (this == First) { + First = (MixFileClass *)Get_Next(); + } else { + Remove(); + } + + // Can't do this here since the link class destructor hasn't been called yet, so clearing out the data will mess up the + // linked list. How did this work before? Did the watcom compiler call the destructors in a different order? + // ST - 1/3/2019 5:34PM + //Zap(); +} + + +/*********************************************************************************************** + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * * + * This is the constructor for the mixfile object. It takes a filename and a memory * + * handler object and registers the mixfile object with the system. The index block is * + * allocated and loaded from disk by this routine. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +MixFileClass::MixFileClass(char const *filename) +{ + CCFileClass file; // Working file object. + + /* + ** Load in the control block. It always remains resident. + */ + Data = 0; + Count = 0; + Buffer = 0; + file.Set_Name(filename); + Filename = strdup(file.File_Name()); + + if (!Force_CD_Available(RequiredCD)) { + Prog_End("MixFileClass::MixFileClass CD not found", true); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + return; + } + + if (file.Is_Available(true)) { + FileHeader fileheader; + + file.Open(); + file.Read(&fileheader, sizeof(fileheader)); + Count = fileheader.count; + DataSize = fileheader.size; + + /* + ** Load up the offset control array. This could be located in + ** EMS if possible. + */ + Buffer = new SubBlock [Count]; + if (Buffer) { + file.Read(Buffer, Count * sizeof(SubBlock)); + } + file.Close(); + } else { +// delete this; + return; + } + + /* + ** Raw data block starts uncached. + */ + Data = 0; + + /* + ** Attach to list of mixfiles. + */ + Zap(); + if (!First) { + First = this; + } else { + Add_Tail(*First); + } +} + + +/*********************************************************************************************** + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * * + * This routine will return with a pointer to the specified data file if the file resides * + * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident * + * file directly rather than going through the process of pseudo disk access. This will * + * save both time and RAM. * + * * + * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. * + * * + * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +void const * MixFileClass::Retrieve(char const *filename) { + void *ptr = 0; + Offset(filename, &ptr); +// if (!ptr) { +// errno = ENOENT; +// File_Fatal(filename); +// } + return(ptr); +}; + + +/*********************************************************************************************** + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * * + * This routine will scan through all registered mixfiles and return with a pointer to * + * the matching mixfile. If no mixfile could be found that matches the name specified, * + * then NULL is returned. * + * * + * INPUT: filename -- Pointer to the filename to search for. * + * * + * OUTPUT: Returns with a pointer to the matching mixfile -- if found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +MixFileClass * MixFileClass::Finder(char const *filename) +{ + MixFileClass * ptr; + + ptr = First; + while (ptr) { + if (stricmp(&ptr->Filename[strlen(ptr->Filename)-strlen(filename)], filename) == 0) { + return(ptr); + } + ptr = (MixFileClass *)ptr->Get_Next(); + } + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * * + * This routine will cache the mixfile, specified by name, into RAM. * + * * + * INPUT: filename -- The name of the mixfile that should be cached. * + * * + * OUTPUT: bool; Was the cache successful? * + * * + * WARNINGS: This routine could go to disk for a very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Cache(char const *filename) +{ + MixFileClass * mixer = Finder(filename); + + if (mixer) { + return(mixer->Cache()); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * * + * This load the mixfile data into ram for this mixfile object. This is the counterpart * + * to the Free() function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Cache(void) +{ + if (Data) return(true); + + Data = new char [DataSize]; + if (Data) { + CCFileClass file(Filename); + + file.Open(); + file.Seek(sizeof(SubBlock) * Count + sizeof(FileHeader)); + long actual = file.Read(Data, DataSize); + if (actual != DataSize) { +#ifdef GERMAN + Fatal("Korrupte .MIX-Datei \"%s\". Beim Versuch, %ld zu lesen, nur %ld gefunden.", Filename, DataSize, actual); +#else +#ifdef FRENCH + Fatal("Fichier .MIX corrumpu \"%s\". Essai de lecture de %ld, mais %ld obtenu.", Filename, DataSize, actual); +#else + Fatal("Corrupt .MIX file \"%s\". Tried to read %ld, but got %ld.", Filename, DataSize, actual); +#endif +#endif + } + file.Close(); + return(true); + } +#ifdef GERMAN + Fatal("Kann Datei \"%s\" nicht laden.", Filename); +#else +#ifdef FRENCH + Fatal("Impossible de charger \"%s\".", Filename); +#else + Fatal("Unable to load \"%s\".", Filename); +#endif +#endif + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * * + * This routine will free the (presumably large) raw data block, but leave the index * + * block intact. By using this in conjunction with the Cache() function, one can maintain * + * tight control of memory usage. If the index block is desired to be freed, then the * + * mixfile object must be deleted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void MixFileClass::Free(void) +{ + if (Data) { + delete [] Data; + Data = 0; + } +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.* + * * + * This routine will scan through all registers mixfiles in an attempt to locate the file * + * in question. Whether the file is located in RAM or on disk does not restrict this * + * search operation. * + * * + * INPUT: filename -- The filename of the file to search within the mixfile system. * + * * + * realptr -- Pointer to pointer that will be filled with the RAM pointer to * + * the file if it happens to be in RAM. NULL if otherwise. * + * * + * mixfile -- Pointer to the mixfile object that contains the file in question. * + * * + * offset -- The offset to the start of the data of the file. If the file is * + * on disk then this is the offset from the start of the file, if * + * it is located in RAM, then it is the offset from the start of the * + * raw data block. * + * * + * size -- Pointer to where the size of the file will be stored. * + * * + * OUTPUT: bool; Was the file found in one of the mixfiles? The value stored in "realptr" * + * will be NULL if it is on disk, otherwise it is in RAM. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +//int _USERENTRY Compare(MixFileClass::SubBlock const *, MixFileClass::SubBlock const *); + +// int _USERENTRY compfunc(void const *ptr1, void const *ptr2) +int compfunc(void const *ptr1, void const *ptr2) +{ +// long diff = *(long const *)ptr1 - *(long const *)ptr2; +// return FP_SEG(diff); + + if (*(long const *)ptr1 < *(long const *)ptr2) return(-1); + if (*(long const *)ptr1 > *(long const *)ptr2) return(1); + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * * + * This routine will take the falename specified and search through the mixfile system * + * looking for it. If the file was found, then the mixfile it was found int, the offset * + * from the start of the mixfile, and the size of the embedded file will be returned. * + * Using this method it is possible for the CCFileClass system to process it as a normal * + * file. * + * * + * INPUT: filename -- The filename to search for. * + * * + * realptr -- Stores a pointer to the start of the file in memory here. If the * + * file is not in memory, then NULL is stored here. * + * * + * mixfile -- The pointer to the corresponding mixfile is placed here. If no * + * mixfile was found that contains the file, then NULL is stored here. * + * * + * offset -- The starting offset from the beginning of the parent mixfile is * + * stored here. * + * * + * size -- The size of the embedded file is stored here. * + * * + * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist * + * and can be opened. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Offset(char const *filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) +{ + MixFileClass * ptr; + + if (!filename) return(false); + + /* + ** Create the key block that will be used to binary search for the file. + */ + char file_name_copy[_MAX_PATH]; + strcpy(file_name_copy, filename); + strupr(file_name_copy); + long crc = Calculate_CRC(file_name_copy, strlen(file_name_copy)); + SubBlock key; + key.CRC = crc; + + /* + ** Sweep through all registered mixfiles, trying to find the file in question. + */ + ptr = First; + while (ptr) { + SubBlock * block; + + /* + ** Binary search for the file in this mixfile. If it is found, then extract the + ** appropriate information and store it in the locations provided and then return. + */ + block = (SubBlock *)bsearch(&key, ptr->Buffer, ptr->Count, sizeof(SubBlock), compfunc); + if (block) { + if (mixfile) *mixfile = ptr; + if (size) *size = block->Size; + if (realptr) *realptr = 0; + if (offset) *offset = block->Offset; + if (realptr && ptr->Data) { + *realptr = Add_Long_To_Pointer(ptr->Data, block->Offset); + } + if (!ptr->Data && offset) { + *offset += sizeof(SubBlock) * ptr->Count + sizeof(FileHeader); + } + return(true); + } + + /* + ** Advance to next mixfile. + */ + ptr = (MixFileClass *)ptr->Get_Next(); + } + + /* + ** All the mixfiles have been examined but no match was found. Return with the non success flag. + */ + return(false); +} \ No newline at end of file diff --git a/TIBERIANDAWN/MIXFILE.H b/TIBERIANDAWN/MIXFILE.H new file mode 100644 index 000000000..408fcbb86 --- /dev/null +++ b/TIBERIANDAWN/MIXFILE.H @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mixfile.h_v 2.18 16 Oct 1995 16:47:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MIXFILE_H +#define MIXFILE_H + +#include +#include "link.h" + +class MixFileClass : public LinkClass +{ + public: + char const *Filename; // Filename of mixfile. + + MixFileClass(char const *filename); + ~MixFileClass(void); + + static bool Free(char const *filename); + static void Free_All(void); + void Free(void); + bool Cache(void); + static bool Cache(char const *filename); + static bool Offset(char const *filename, void ** realptr = 0, MixFileClass ** mixfile = 0, long * offset = 0, long * size = 0); + static void const * Retrieve(char const *filename); + + struct SubBlock { + long CRC; // CRC code for embedded file. + long Offset; // Offset from start of data section. + long Size; // Size of data subfile. + + int operator < (SubBlock & two) const {return (CRC < two.CRC);}; + int operator > (SubBlock & two) const {return (CRC > two.CRC);}; + int operator == (SubBlock & two) const {return (CRC == two.CRC);}; + }; + + private: + static MixFileClass * Finder(char const *filename); + long Offset(long crc, long *size = 0); + + typedef struct { + short count; + long size; + } FileHeader; + + int Count; // Number of sub-blocks. + long DataSize; // Size of raw data. + SubBlock * Buffer; // Array of sub blocks (could be in EMS). + void *Data; // Pointer to raw data. + + static MixFileClass * First; +}; + +#endif diff --git a/TIBERIANDAWN/MMX.ASM b/TIBERIANDAWN/MMX.ASM new file mode 100644 index 000000000..88032aea3 --- /dev/null +++ b/TIBERIANDAWN/MMX.ASM @@ -0,0 +1,328 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : MMX.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : May 19th, 1996 * +;* * +;* Last Update : May 19th 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + ; include + + + .model flat + ;.686 + + + externdef C Detect_MMX_Availability:near + externdef C Single_Line_Trans_Entry:near + externdef C Next_Line:near + externdef C Init_MMX:near + externdef C MMX_Done:near + + externdef EndNewShapeJumpTable:byte + externdef NewShapeJumpTable:dword + externdef C Single_Line_Trans:near + externdef MMX_Single_Line_Trans:near + + + .code + +externdef C CPUType:byte + + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local local_cputype:byte + + ; MMX always available now. ST - 1/3/2019 1:31PM + mov [CPUType], 5 + mov eax, 1 + ret + + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [local_cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [local_cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [local_cputype],al + +@@end_get_cpu: mov al,[local_cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + +;********************************************************************************************* +;* Init_MMX -- Do any special inits required for MMX support * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Init_MMX proc C + + mov edi,offset NewShapeJumpTable + mov ecx,offset EndNewShapeJumpTable + sub ecx,edi + shr ecx,2 + mov eax,offset Single_Line_Trans + mov ebx,offset MMX_Single_Line_Trans + cld + + +@@patch_loop: repnz scasd + jnz @@done + mov [edi-4],ebx + test ecx,ecx + jnz @@patch_loop + +@@done: ret + +Init_MMX endp + + + + + + +;********************************************************************************************* +;* MMX_Done -- Restores floating point capability after MMX usage * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +MMX_Done proc C + + emms + ret + +MMX_Done endp + + + + + + + + code segment page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +;********************************************************************************************* +;* MMX_Single_Line_Trans -- draw a single line of transparent pixels using MMX technology * +;* * +;* * +;* INPUT: Esi - ptr to source data * +;* Edi - ptr to destination data * +;* Ecx - width to draw in bytes * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + + align 16 + +MMX_Single_Line_Trans proc near + +; +; If we are doing less than 8 bytes then dont use MMX +; + cmp ecx,8 + jge @@mmx_loop + push offset Single_Line_Trans_Entry + ret + +; +; Use MMX instructions to mask 8 bytes at once +; +; Creates a bitmask based on the source bytes equality with zero and then uses this to mask +; out the source bytes in the destination data. The advatage that MMX gives us is that there is +; no 'test for zero then jump' required to mask. +; + align 64 ;MMX instructions like 64 byte alignment! + +@@mmx_loop: + movq mm0,[esi] ; move 8 bytes of source into mm0 + pxor mm1,mm1 ; zero out mm1 + pcmpeqb mm1,mm0 ; compare mm0 with 0. Bits get set in mm1 + lea esi,[esi+8] ; adjust the source data pointer + pand mm1,[edi] ; and in the destination data to throw away the bytes which arent zero in the source + sub ecx,8 ; adjust the byte counter + por mm1,mm0 ; or in the source with the destination data + movq [edi],mm1 ; write back the destination data + lea edi,[edi+8] ; adjust the destination pointer + + cmp ecx,8 + jg @@mmx_loop + +; +; Jump to the approprite code for drawing the end of this line or going to the next one +; + push offset Next_Line + jcxz @@next_line + push offset Single_Line_Trans_Entry +@@next_line: ret + + +MMX_Single_Line_Trans endp + + +code ends + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + +end \ No newline at end of file diff --git a/TIBERIANDAWN/MONOC.CPP b/TIBERIANDAWN/MONOC.CPP new file mode 100644 index 000000000..8c6f785e4 --- /dev/null +++ b/TIBERIANDAWN/MONOC.CPP @@ -0,0 +1,804 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\monoc.cpv 2.13 16 Oct 1995 16:50:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#pragma inline +#include "function.h" +#include "monoc.h" + +#include +#include +#include +//#include +#include +#include + + +//extern void output(short port, short data); +//#pragma aux output parm [dx] [ax] = \ +// "out dx,al" \ +// "inc dx" \ +// "mov al,ah" \ +// "out dx,al" + + + +int MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES] = {0,0,0,0,0,0,0,0}; +//DOSSegmentClass MonoClass::MonoSegment(MonoClass::SEGMENT); +void * MonoClass::MonoSegment = (void*)0x000b0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) +{ + int index; + + Attrib = DEFAULT_ATTRIBUTE; // Normal text color. + X = Y = 0; + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, char attrib, BoxStyleType thick) +{ + CellType cell; + char oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Store_Cell(cell, x+xpos+1, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+xpos+1, y)); + cell.Character = CharData[thick].BottomEdge; + Store_Cell(cell, x+xpos+1, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+xpos+1, y+h-1)); + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Store_Cell(cell, x, y+ypos+1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y+ypos+1)); + cell.Character = CharData[thick].RightEdge; + Store_Cell(cell, x+w-1, y+ypos+1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y+ypos+1)); + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Store_Cell(cell, x, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y)); + cell.Character = CharData[thick].UpperRight; + Store_Cell(cell, x+w-1, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y)); + cell.Character = CharData[thick].BottomRight; + Store_Cell(cell, x+w-1, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y+h-1)); + cell.Character = CharData[thick].BottomLeft; + Store_Cell(cell, x, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y+h-1)); + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ + #ifdef FIX_ME_LATER + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + _DX = CONTROL_PORT; + _AX = (short)(0x0E|(pos&0xFF00)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + _DX = CONTROL_PORT; + _AX = (short)(0x0F|(pos<<8)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + +// outportb(CONTROL_PORT,0x0E|(pos&0xFF00)); +// outportb(CONTROL_PORT,0x0F|(pos<<8)); + } + + #endif //FIX_ME_LATER + +x=y; +y=x; +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + CellType cell; +// int offset; + + if (!Enabled) return; + + Set_Cursor(0, 0); + + cell.Attribute = Attrib; + cell.Character = ' '; + +// offset = Offset(0, 0); + for (int y = 0; y < LINES; y++) { + for (int x = 0; x < COLUMNS; x++) { + Store_Cell(cell, x, y); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + CellType cell; + + if (!Enabled || lines <= 0) return; + + memmove( (void*)((long)MonoSegment + Offset(0, 0)), + (void*)((long)MonoSegment + Offset(0, lines)), + (LINES-lines)*COLUMNS*sizeof(CellType)); + +// DOSSegmentClass::Copy(MonoSegment, Offset(0, lines), MonoSegment, Offset(0, 0), (LINES-lines)*COLUMNS*sizeof(CellType)); + + Y--; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int l = LINES-lines; l < LINES; l++) { + for (int index = 0; index < COLUMNS; index++) { + Store_Cell(cell, index, l); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(index, l)); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +#ifdef NEVER +void MonoClass::Printf(int text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} +#endif + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const *ptr) +{ +// int optr; + char startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; +// optr = Offset(X, Y); + while (*text) { + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (*text) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + X = startcol; + Y++; + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + break; + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + X = 0; + Y++; + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + break; + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge is it moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + cell.Character = *text; + Store_Cell(cell, X, Y); +// MonoSegment.Copy_Word_To(*(short*)&cell, optr); +// optr += sizeof(CellType); + + X++; + if (X >= COLUMNS) { + X = 0; + Y++; + + if (Y > (LINES-1)) { + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + } + } + break; + + } + text++; + } + + Set_Cursor(X, Y); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + +#ifdef NEVER +void MonoClass::Text_Print(int text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + if (text != TXT_NONE) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} +#endif + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memcpy((void*)((long)MonoSegment + src.Offset(0, 0)), (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); +// DOSSegmentClass::Copy(MonoSegment, src.Offset(0, 0), MonoSegment, Offset(0,0), SIZE_OF_PAGE); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + MonoClass *displace; // The page that is being displaced. + + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + displace = Get_Current(); + if (displace) { + char temp[SIZE_OF_PAGE]; + + memcpy(&temp[0], MonoSegment, SIZE_OF_PAGE); + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + memcpy((void*)((long)MonoSegment + Offset(0, 0)), &temp[0], SIZE_OF_PAGE); + +// DOSSegmentClass::Swap(MonoSegment, Offset(0, 0), MonoSegment, 0, SIZE_OF_PAGE); + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); +// DOSSegmentClass::Copy(MonoSegment, Offset(0, 0), MonoSegment, 0, SIZE_OF_PAGE); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (char)attrib); + } +} + +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (char)attrib, (MonoClass::BoxStyleType)thick); + } +} + +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + + +#ifdef NEVER +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} +#endif + diff --git a/TIBERIANDAWN/MONOC.H b/TIBERIANDAWN/MONOC.H new file mode 100644 index 000000000..6d268daae --- /dev/null +++ b/TIBERIANDAWN/MONOC.H @@ -0,0 +1,187 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.17 16 Oct 1995 16:45:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + +//#include "dpmi.h" +//#include "function.h" + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + unsigned char UpperLeft; + unsigned char TopEdge; + unsigned char UpperRight; + unsigned char RightEdge; + unsigned char BottomRight; + unsigned char BottomEdge; + unsigned char BottomLeft; + unsigned char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ +// static DOSSegmentClass MonoSegment; + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +//extern int cdecl Mono_Printf(int string, ...); + + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif diff --git a/TIBERIANDAWN/MOUSE.CPP b/TIBERIANDAWN/MOUSE.CPP new file mode 100644 index 000000000..ee3eef759 --- /dev/null +++ b/TIBERIANDAWN/MOUSE.CPP @@ -0,0 +1,357 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mouse.cpv 2.18 16 Oct 1995 16:49:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : June 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MouseClass::AI -- Process player input as it relates to the mouse * + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This points to the loaded mouse shapes. +*/ +void const * MouseClass::MouseShapes; + +/* +** This is the timer that controls the mouse animation. It is always at a fixed +** rate so it uses the constant system timer. +*/ +CountDownTimerClass MouseClass::Timer; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * MouseClass::VTable; + + +/*********************************************************************************************** + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * * + * This routine is used to inform the display system as to which mouse shape is desired. * + * * + * INPUT: mouse -- The mouse shape number to set the mouse to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Set_Default_Mouse(MouseType mouse, bool size) +{ + NormalMouseShape = mouse; + Override_Mouse_Shape(mouse, size); +} + + +/*********************************************************************************************** + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * * + * Use this routine to cancel the effects of Override_Mouse_Shape(). It will revert the * + * mouse back to the original shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/27/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Revert_Mouse_Shape(void) +{ + Override_Mouse_Shape(NormalMouseShape, false); +} + + +void MouseClass::Mouse_Small(bool wwsmall) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (IsSmall == wwsmall) { + return; + } + + IsSmall = wwsmall; + + if (wwsmall) { + if (control->SmallFrame != -1) { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->SmallFrame + Frame/4)); + } else { + Set_Mouse_Cursor(MouseControl[MOUSE_NORMAL].X, MouseControl[MOUSE_NORMAL].Y, Extract_Shape(MouseShapes, MOUSE_NORMAL)); + } + } else { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->StartFrame + Frame/4)); + } +} + + +/*********************************************************************************************** + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * * + * This routine is used to alter the shape of the mouse as needed. * + * Typical mouse shape change occurs when scrolling the map or * + * selecting targets. * + * * + * INPUT: mouse -- The mouse shape number to use. * + * * + * OUTPUT: bool; Was the mouse shape changed? * + * * + * WARNINGS: This is not intended to be used as a means to hide the * + * mouse. Nor will it work correctly if the mouse shape * + * file hasn't been loaded. * + * * + * HISTORY: * + * 03/10/1994 JLB : Created. * + * 06/03/1994 JLB : Made into member function. * + * 12/24/1994 JLB : Added small control parameter. * + *=============================================================================================*/ +bool MouseClass::Override_Mouse_Shape(MouseType mouse, bool wwsmall) +{ + MouseStruct const * control = &MouseControl[mouse]; + static bool startup = false; + int baseshp; + + /* + ** Only certain mouse shapes have a small counterpart. If the requested mouse + ** shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wwsmall = false; + } + + /* + ** If the mouse shape is going to change, then inform the mouse driver of the + ** change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wwsmall != IsSmall)))) { + startup = true; + + Timer.Set(control->FrameRate); + Frame = 0; + +#ifdef OBSOLETE + Control.Set_Stage(0); + int rate = Options.Normalize_Delay(control->FrameRate); + Control.Set_Rate(MAX(rate, 1)); +#endif + baseshp = (wwsmall) ? control->SmallFrame : control->StartFrame; + + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseshp + Frame/4)); + CurrentMouseShape = mouse; + IsSmall = wwsmall; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MouseClass::AI -- Process player input as it relates to the mouse * + * * + * This routine will is to be called once per game tick and is passed the player keyboard * + * or mouse input code. It processes this code and updates the mouse shape as appropriate. * + * * + * INPUT: input -- The player input code as returned from Keyboard::Get(). * + * * + * x,y -- The mouse coordinate values to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 03/27/1995 JLB : New animation control. * + * 05/28/1995 JLB : Moderates animation so is more steady regardless of speed. * + * 06/30/1995 JLB : Uses constant timer system. * + *=============================================================================================*/ +void MouseClass::AI(KeyNumType &input, int x, int y) +{ +// bool doit = false; + void *mouse_shape_ptr; + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (control->FrameRate && Timer.Time() == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer.Set(control->FrameRate); + +#ifdef OBSOLETE + Control.Set_Stage(Control.Fetch_Stage() % control->FrameCount); +#endif + + if (!IsSmall || control->SmallFrame != -1) { + int baseframe = (IsSmall) ? control->SmallFrame : control->StartFrame; + mouse_shape_ptr = Extract_Shape(MouseShapes, baseframe + Frame); + if (mouse_shape_ptr){ + Set_Mouse_Cursor(control->X, control->Y, mouse_shape_ptr); + } + } + } + + ScrollClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * * + * This is the default constructor for the mouse handling class. It merely sets up the * + * mouse system to its default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +MouseClass::MouseClass(void) +{ + CurrentMouseShape = MOUSE_NORMAL; + NormalMouseShape = MOUSE_NORMAL; + Timer.Start(); +} + + +/*********************************************************************************************** + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * * + * Use this routine to load the mouse data file and perform any other necessary one time * + * preparations for the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine ONCE. * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::One_Time(void) +{ + ScrollClass::One_Time(); + + /* + ** Override the mouse shape file with the one in the current directory, but only if there + ** is an override file available. + */ + RawFileClass file("MOUSE.SHP"); + if (file.Is_Available()) { + MouseShapes = Load_Alloc_Data(file); + } else { + MouseShapes = MixFileClass::Retrieve("MOUSE.SHP"); + } + + VTable = ((void **)(((char *)this) + sizeof(VectorClass) - 4))[0]; +} + + +/*********************************************************************************************** + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * * + * This routine will reset the mouse handling system. Typically, this routine is called * + * when preparing for the beginning of a new scenario. * + * * + * INPUT: theater -- The theater that the scenario will take place. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Init_Clear(void) +{ + ScrollClass::Init_Clear(); + IsSmall = false; + NormalMouseShape = MOUSE_NORMAL; +} + + +/* +** This array of structures is used to control the mouse animation +** sequences. +*/ +MouseClass::MouseStruct MouseClass::MouseControl[MOUSE_COUNT] = { + {0, 1, 0, 86, 0, 0}, // MOUSE_NORMAL + {1, 1, 0, -1, 15, 0}, // MOUSE_N + {2, 1, 0, -1, 29, 0}, // MOUSE_NE + {3, 1, 0, -1, 29, 12}, // MOUSE_E + {4, 1, 0, -1, 29, 23}, // MOUSE_SE + {5, 1, 0, -1, 15, 23}, // MOUSE_S + {6, 1, 0, -1, 0, 23}, // MOUSE_SW + {7, 1, 0, -1, 0, 13}, // MOUSE_W + {8, 1, 0, -1, 0, 0}, // MOUSE_NW + + {130, 1, 0, -1, 15, 0}, // MOUSE_NO_N + {131, 1, 0, -1, 29, 0}, // MOUSE_NO_NE + {132, 1, 0, -1, 29, 12}, // MOUSE_NO_E + {133, 1, 0, -1, 29, 23}, // MOUSE_NO_SE + {134, 1, 0, -1, 15, 23}, // MOUSE_NO_S + {135, 1, 0, -1, 0, 23}, // MOUSE_NO_SW + {136, 1, 0, -1, 0, 13}, // MOUSE_NO_W + {137, 1, 0, -1, 0, 0}, // MOUSE_NO_NW + + {11, 1, 0, 27, 15, 12}, // MOUSE_NO_MOVE + {10, 1, 0, 26, 15, 12}, // MOUSE_CAN_MOVE + {119, 3, 4, 148, 15, 12}, // MOUSE_ENTER + {53, 9, 4, -1, 15, 12}, // MOUSE_DEPLOY + {12, 6, 4, -1, 15, 12}, // MOUSE_CAN_SELECT + {18, 8, 4, 140, 15, 12}, // MOUSE_CAN_ATTACK + {62, 24, 2, -1, 15, 12}, // MOUSE_SELL_BACK + {154, 24, 2, -1, 15, 12}, // MOUSE_SELL_UNIT + {29, 24, 2, -1, 15, 12}, // MOUSE_REPAIR + {126, 1, 0, -1, 15, 12}, // MOUSE_NO_REPAIR + {125, 1, 0, -1, 15, 12}, // MOUSE_NO_SELL_BACK + {87, 1, 0, 151, 0, 0}, // MOUSE_RADAR_CURSOR + {103, 16, 2, -1, 15, 12}, // MOUSE_ION_CANNON + {96, 7, 4, -1, 15, 12}, // MOUSE_NUCLEAR_BOMB + {88, 8, 2, -1, 15, 12}, // MOUSE_AIR_STRIKE + {122, 3, 4, 127, 15, 12}, // MOUSE_DEMOLITIONS + {153, 1, 0, 152, 15, 12}, // MOUSE_AREA_GUARD +}; diff --git a/TIBERIANDAWN/MOUSE.H b/TIBERIANDAWN/MOUSE.H new file mode 100644 index 000000000..6bf5524b7 --- /dev/null +++ b/TIBERIANDAWN/MOUSE.H @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mouse.h_v 2.16 16 Oct 1995 16:45:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "stage.h" +#include "scroll.h" + +class MouseClass: public ScrollClass +{ + public: + MouseClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Override_Mouse_Shape(MouseType mouse, bool wwsmall=false); + virtual void Revert_Mouse_Shape(void); + virtual MouseType Get_Mouse_Shape(void) const {return NormalMouseShape;}; + virtual void Mouse_Small(bool wwsmall); + + /* + ** File I/O. + */ + virtual bool Load(FileClass & file); + virtual bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual void Set_Default_Mouse(MouseType mouse, bool wwsmall = false); + + /* + ** This allows the tactical map input gadget access to change the + ** mouse shapes. + */ + friend class TacticalClass; + + private: + + /* + ** This type is used to control the frames and rates of the mouse + ** pointer. Some mouse pointers are actually looping animations. + */ + typedef struct MouseStruct + { + int StartFrame; // Starting frame number. + int FrameCount; // Number of animation frames. + int FrameRate; // Frame delay between changing frames. + int SmallFrame; // Start frame number for small version (if any). + int X,Y; // Hotspot X and Y offset. + } MouseStruct; + + /* + ** The control frames and rates for the various mouse pointers are stored + ** in this static array. + */ + static MouseStruct MouseControl[MOUSE_COUNT]; + + /* + ** If the small representation of the mouse is active, then this flag is true. + */ + unsigned IsSmall:1; + + /* + ** This points to the loaded mouse shapes. + */ + static void const * MouseShapes; + + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseType CurrentMouseShape; + MouseType NormalMouseShape; + + /* + ** For animating mouse shapes, this controls the frame and animation rate. + */ + static CountDownTimerClass Timer; + int Frame; +// StageClass Control; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + + +#endif diff --git a/TIBERIANDAWN/MPLAYER.CPP b/TIBERIANDAWN/MPLAYER.CPP new file mode 100644 index 000000000..22df5b0cd --- /dev/null +++ b/TIBERIANDAWN/MPLAYER.CPP @@ -0,0 +1,1417 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\mplayer.cpv 1.9 16 Oct 1995 16:51:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPLAYER.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 14, 1995 * + * * + * Last Update : July 5, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * Read_MultiPlayer_Settings -- reads multi-player settings from conquer.ini * + * Write_MultiPlayer_Settings -- writes multi-player settings to conquer.ini * + * Read_Scenario_Descriptions -- reads multi-player scenario #'s # descriptions * + * Free_Scenario_Descriptions -- frees memory for the scenario descriptions * + * Computer_Message -- "sends" a message from the computer * + * Garble_Message -- "garbles" a message * + * Surrender_Dialog -- Prompts user for surrendering * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +static void Garble_Message(char *buf); + +int Choose_Internet_Game(void); +int Get_Internet_Host_Or_Join(void); +int Get_IP_Address(void); +void Show_Internet_Connection_Progress(void); + +/*********************************************************************************************** + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_NORMAL, GAME_MODEM, etc. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +GameType Select_MPlayer_Game (void) +{ +//PG_TO_FIX + return GAME_NORMAL; +#if (0) + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + bool ipx_avail = FALSE; + int number_of_buttons; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 190*factor; + int d_dialog_h = 26*4*factor; + int d_dialog_x = ((320*factor - d_dialog_w) / 2); +// d_dialog_y = ((200 - d_dialog_h) / 2), + int d_dialog_y = ((136*factor - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 11 * factor; + int d_margin = 7 *factor; + + int d_modemserial_w = 80*factor; + int d_modemserial_h = 9*factor; + int d_modemserial_x = d_dialog_cx - d_modemserial_w / 2; + int d_modemserial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; +#if (0) + int d_internet_w = 80*factor; + int d_internet_h = 9*factor; + int d_internet_x = d_dialog_cx - d_internet_w / 2; + int d_internet_y = d_modemserial_y + d_modemserial_h + 2*factor; +#endif //(0) + int d_ipx_w = 80*factor; + int d_ipx_h = 9*factor; + int d_ipx_x = d_dialog_cx - d_ipx_w / 2; + int d_ipx_y = d_modemserial_y + d_modemserial_h + 2*factor; +// int d_ipx_y = d_internet_y + d_internet_h + 2*factor; + + int d_cancel_w = 60*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_ipx_y + d_ipx_h + d_margin; + + CountDownTimerClass delay; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_MODEMSERIAL = 100, +#if (0) + BUTTON_INTERNET, +#endif //(0) + BUTTON_IPX, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 3, + }; + number_of_buttons = NUM_OF_BUTTONS; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + GameType retval; // return value + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + // + // If neither IPX or winsock are active then do only the modem serial dialog + // + if (Ipx.Is_IPX()){ + ipx_avail = TRUE; + } + + + TextButtonClass modemserialbtn (BUTTON_MODEMSERIAL, TXT_MODEM_SERIAL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_modemserial_x, d_modemserial_y, d_modemserial_w, d_modemserial_h); +#if (0) + TextButtonClass internetbtn (BUTTON_INTERNET, TXT_INTERNET, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_internet_x, d_internet_y, d_internet_w, d_internet_h); +#endif //(0) + TextButtonClass ipxbtn (BUTTON_IPX, TXT_NETWORK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ipx_x, d_ipx_y, d_ipx_w, d_ipx_h); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Create the list ............................. + */ + commands = &modemserialbtn; +#if (0) + internetbtn.Add_Tail(*commands); +#endif //(0) + if (ipx_avail){ + ipxbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + + /* + ......................... Fill array of button ptrs ...................... + */ + curbutton = 0; + buttons[0] = &modemserialbtn; +#if (0) + buttons[1] = &internetbtn; +#endif //(0) + if (ipx_avail){ + buttons[1] = &ipxbtn; + buttons[2] = &cancelbtn; + }else{ + buttons[1] = &cancelbtn; + number_of_buttons--; + } + + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_SELECT_MPLAYER_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_MODEMSERIAL | KN_BUTTON): + selection = BUTTON_MODEMSERIAL; + pressed = true; + break; + +#if (0) + case (BUTTON_INTERNET | KN_BUTTON): + selection = BUTTON_INTERNET; + pressed = true; + break; +#endif //(0) + + case (BUTTON_IPX | KN_BUTTON): + selection = BUTTON_IPX; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (number_of_buttons - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (number_of_buttons - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_MODEMSERIAL; + if (!ipx_avail) selection--; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_MODEMSERIAL; + buttons[curbutton]->Turn_On(); +// buttons[curbutton]->Flag_To_Redraw(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_MODEMSERIAL): + + // + // Pop up the modem/serial/com port dialog + // + retval = Select_Serial_Dialog(); + + if (retval != GAME_NORMAL) { + process = false; + } else { + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + } + break; + +#if (0) + case (BUTTON_INTERNET): +//#define ONLY_FOR_E3 +#ifdef ONLY_FOR_E3 + Call_Back(); + Show_Internet_Connection_Progress(); //changed to do nothing + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + Call_Back(); + CCMessageBox().Process("Error! - Unable to ping KANE.WESTWOOD.COM"); + + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + + +#endif //ONLY_FOR_E3 + + +#ifdef FORCE_WINSOCK + if (Winsock.Init()){ + Read_MultiPlayer_Settings (); + int result = Choose_Internet_Game(); + + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + + result = Get_Internet_Host_Or_Join(); + if (result == 1){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Server = !result; + + if (Server){ +#if (0) + ModemGameToPlay = INTERNET_HOST; + Winsock.Start_Server(); +#else + result = Get_IP_Address(); + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + Winsock.Start_Server(); +#endif + + }else{ + ModemGameToPlay = INTERNET_JOIN; + result = Get_IP_Address(); + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + Winsock.Start_Client(); + } + + //CountDownTimerClass connect_timeout; + //connect_timeout.Set(15*60); + + ////Show_Internet_Connection_Progress(); + + if (!Winsock.Get_Connected()){ + Winsock.Close(); + return(GAME_NORMAL); + } + + SerialSettingsType *settings; + settings = &SerialDefaults; + Init_Null_Modem(settings); + if (Server){ + if (Com_Scenario_Dialog()){ + return (GAME_INTERNET); + }else{ + Winsock.Close(); + return (GAME_NORMAL); + } + }else{ + if (Com_Show_Scenario_Dialog()){ + return (GAME_INTERNET); + }else{ + Winsock.Close(); + return (GAME_NORMAL); + } + } + } +#endif //FORCE_WINSOCK + break; + +#endif //(0) + + case (BUTTON_IPX): + retval = GAME_IPX; + process = false; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } + return(retval); +#endif +} + + +/*********************************************************************************************** + * Read_MultiPlayer_Settings -- reads multi-player settings from conquer.ini * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Read_MultiPlayer_Settings (void) +{ +//PG_TO_FIX +#if (0) + char *buffer; // INI staging buffer pointer. + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + char *tokenptr; // ptr to token + PhoneEntryClass *phone; // a phone book entry + char *entry; // a phone book entry + char buf[128]; // buffer for parsing INI entry + int i; + CELL cell; + + /*------------------------------------------------------------------------ + Fetch working pointer to the INI staging buffer. Make sure that the buffer + is cleared out before proceeding. (Don't use the HidPage for this, since + the HidPage may be needed for various uncompressions during the INI + parsing.) + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*------------------------------------------------------------------------ + Clear the initstring entries + ------------------------------------------------------------------------*/ + for (i = 0; i < InitStrings.Count(); i++) { + delete[] InitStrings[i]; + } + InitStrings.Clear(); + + /*------------------------------------------------------------------------ + Clear the dialing entries + ------------------------------------------------------------------------*/ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + if (!Special.IsFromWChat){ + /*------------------------------------------------------------------------ + Get the player's last-used Handle + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("MultiPlayer", "Handle", "Noname", MPlayerName, + sizeof(MPlayerName), buffer); + + /*------------------------------------------------------------------------ + Get the player's last-used Color + ------------------------------------------------------------------------*/ + MPlayerPrefColor = WWGetPrivateProfileInt("MultiPlayer", "Color", 0, buffer); + MPlayerHouse = (HousesType)WWGetPrivateProfileInt("MultiPlayer", "Side", + HOUSE_GOOD, buffer); + CurPhoneIdx = WWGetPrivateProfileInt("MultiPlayer", "PhoneIndex", -1, buffer); + }else{ + CurPhoneIdx = -1; + } + +#if 1 + TrapCheckHeap = WWGetPrivateProfileInt( "MultiPlayer", "CheckHeap", 0, buffer); +#endif + + /*------------------------------------------------------------------------ + Read in default serial settings + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString ("SerialDefaults", "ModemName", "NoName", SerialDefaults.ModemName, MODEM_NAME_MAX, buffer); + if (!strcmp ( SerialDefaults.ModemName, "NoName")) { + SerialDefaults.ModemName[0] = 0; + } + WWGetPrivateProfileString ("SerialDefaults", "Port", "0", buf, 5, buffer); + sscanf (buf, "%x", &SerialDefaults.Port); + SerialDefaults.IRQ = WWGetPrivateProfileInt("SerialDefaults", "IRQ", -1, buffer); + SerialDefaults.Baud = WWGetPrivateProfileInt("SerialDefaults", "Baud", -1, buffer); + SerialDefaults.Init = WWGetPrivateProfileInt("SerialDefaults", "Init", 0, buffer); + SerialDefaults.Compression = WWGetPrivateProfileInt ("SerialDefaults", "Compression", 0, buffer); + SerialDefaults.ErrorCorrection = WWGetPrivateProfileInt ("SerialDefaults", "ErrorCorrection", 0, buffer); + SerialDefaults.HardwareFlowControl = WWGetPrivateProfileInt ("SerialDefaults", "HardwareFlowControl", 1, buffer); + WWGetPrivateProfileString ("SerialDefaults", "DialMethod", "T", + buf, 2, buffer); + + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + SerialDefaults.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; + } + + SerialDefaults.InitStringIndex = + WWGetPrivateProfileInt("SerialDefaults", + "InitStringIndex", 0, buffer); + + SerialDefaults.CallWaitStringIndex = + WWGetPrivateProfileInt("SerialDefaults", + "CallWaitStringIndex", CALL_WAIT_CUSTOM, + buffer); + + WWGetPrivateProfileString ("SerialDefaults", "CallWaitString", "", + SerialDefaults.CallWaitString, CWAITSTRBUF_MAX, buffer); + + if (SerialDefaults.IRQ == 0 || + SerialDefaults.Baud == 0) { + + SerialDefaults.Port = 0; + SerialDefaults.IRQ = -1; + SerialDefaults.Baud = -1; + } + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point past the actual INI data + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all Base-Scenario names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("InitStrings", NULL, NULL, tbuffer, + ShapeBufferSize-len, buffer); + + /*------------------------------------------------------------------------ + Read in & store each entry + ------------------------------------------------------------------------*/ + while (*tbuffer != '\0') { + entry = new char[ INITSTRBUF_MAX ]; + + entry[0] = 0; + + WWGetPrivateProfileString("InitStrings", tbuffer, NULL, entry, + INITSTRBUF_MAX, buffer); + + strupr( entry ); + + InitStrings.Add( entry ); + + tbuffer += strlen(tbuffer) + 1; + } + + // if no entries then have at least one + + if ( tbuffer == (buffer + len) ) { + entry = new char[ INITSTRBUF_MAX ]; + strcpy( entry, "ATZ" ); + InitStrings.Add( entry ); + SerialDefaults.InitStringIndex = 0; + } else { + len = strlen(buffer) + 2; + } + + /*------------------------------------------------------------------------ + Repeat the process for the phonebook + ------------------------------------------------------------------------*/ + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read in all phone book listings. + Format: Name=PhoneNum,Port,IRQ,Baud,InitString + ------------------------------------------------------------------------*/ + + /*........................................................................ + Read the entry names in + ........................................................................*/ + WWGetPrivateProfileString("PhoneBook", NULL, NULL, tbuffer, + ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + /*..................................................................... + Create a new phone book entry + .....................................................................*/ + phone = new PhoneEntryClass(); + + /*..................................................................... + Read the entire entry in + .....................................................................*/ + WWGetPrivateProfileString("PhoneBook", tbuffer, NULL, buf, 128, buffer); + + /*..................................................................... + Extract name, phone # & serial port settings + .....................................................................*/ + tokenptr = strtok( buf, "|" ); + if (tokenptr) { + strcpy( phone->Name, tokenptr ); + strupr( phone->Name ); + } else { + phone->Name[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( phone->Number, tokenptr ); + strupr( phone->Number ); + } else { + phone->Number[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + sscanf( tokenptr, "%x", &phone->Settings.Port ); + } else { + phone->Settings.Port = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.IRQ = atoi( tokenptr ); + } else { + phone->Settings.IRQ = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Baud = atoi( tokenptr ); + } else { + phone->Settings.Baud = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Compression = atoi( tokenptr ); + } else { + phone->Settings.Compression = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.ErrorCorrection = atoi( tokenptr ); + } else { + phone->Settings.ErrorCorrection = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.HardwareFlowControl = atoi( tokenptr ); + } else { + phone->Settings.HardwareFlowControl = 1; + } + + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( buf, tokenptr ); + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + phone->Settings.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + } else { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.InitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.InitStringIndex = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.CallWaitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy (phone->Settings.CallWaitString, tokenptr); + } else { + phone->Settings.CallWaitString[0] = 0; + } + + /*..................................................................... + Add it to our list + .....................................................................*/ + PhoneBook.Add(phone); + + tbuffer += strlen(tbuffer) + 1; + } + + /*------------------------------------------------------------------------ + Read special recording playback values, to help find sync bugs + ------------------------------------------------------------------------*/ + if (PlaybackGame) { + TrapFrame = WWGetPrivateProfileInt ("SyncBug","Frame",0x7fffffff, buffer); + + TrapObjType = (RTTIType)WWGetPrivateProfileInt ("SyncBug","Type",RTTI_NONE, buffer); + WWGetPrivateProfileString ("SyncBug","Type","NONE",buf,80,buffer); + if (!stricmp(buf,"AIRCRAFT")) + TrapObjType = RTTI_AIRCRAFT; + else if (!stricmp(buf,"ANIM")) + TrapObjType = RTTI_ANIM; + else if (!stricmp(buf,"BUILDING")) + TrapObjType = RTTI_BUILDING; + else if (!stricmp(buf,"BULLET")) + TrapObjType = RTTI_BULLET; + else if (!stricmp(buf,"INFANTRY")) + TrapObjType = RTTI_INFANTRY; + else if (!stricmp(buf,"UNIT")) + TrapObjType = RTTI_UNIT; + else { + TrapObjType = RTTI_NONE; + } + + WWGetPrivateProfileString ("SyncBug","Coord","0",buf,80,buffer); + sscanf(buf,"%x",&TrapCoord); + + WWGetPrivateProfileString ("SyncBug","this","0",buf,80,buffer); + sscanf(buf,"%x",&TrapThis); + + WWGetPrivateProfileString ("SyncBug","Cell","0",buf,80,buffer); + cell = atoi(buf); + if (cell) { + TrapCell = &(Map[cell]); + } + } +#endif +} + + +/*********************************************************************************************** + * Write_MultiPlayer_Settings -- writes multi-player settings to conquer.ini * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Write_MultiPlayer_Settings (void) +{ +//PG_TO_FIX +#if(0) + char * buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char entrytext[4]; + char buf[128]; // buffer for parsing INI entry + + /*------------------------------------------------------------------------ + Get a working pointer to the INI staging buffer. Make sure that the buffer + starts cleared out of any data. + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("CONQUER.INI"); + if (file.Is_Available()) { + file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + } + + /*------------------------------------------------------------------------ + Save the player's last-used Handle & Color + ------------------------------------------------------------------------*/ + WWWritePrivateProfileInt("MultiPlayer", "PhoneIndex", CurPhoneIdx, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Color", MPlayerPrefColor, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Side", MPlayerHouse, buffer); + WWWritePrivateProfileString("MultiPlayer", "Handle", MPlayerName, buffer); + + /*------------------------------------------------------------------------ + Clear all existing SerialDefault entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("SerialDefaults", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save default serial settings in opposite order you want to see them + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString("SerialDefaults", "CallWaitString", + SerialDefaults.CallWaitString, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Init", SerialDefaults.Init, buffer); + WWWritePrivateProfileString("SerialDefaults", "DialMethod", + DialMethodCheck[ SerialDefaults.DialMethod ], buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Baud", SerialDefaults.Baud, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "IRQ", SerialDefaults.IRQ, buffer); + sprintf(buf, "%x", SerialDefaults.Port); + WWWritePrivateProfileString("SerialDefaults", "Port", buf, buffer); + WWWritePrivateProfileString("SerialDefaults", "ModemName", SerialDefaults.ModemName, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Compression", SerialDefaults.Compression , buffer); + WWWritePrivateProfileInt ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl, buffer); + + /*------------------------------------------------------------------------ + Clear all existing InitString entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("InitStrings", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save all InitString entries. In descending order so they come out in + ascending order. + ------------------------------------------------------------------------*/ + for (i = (InitStrings.Count() - 1); i >= 0; i--) { + sprintf( buf, "%03d", i); + WWWritePrivateProfileString ("InitStrings", buf, InitStrings[i], buffer); + } + + /*------------------------------------------------------------------------ + Clear all existing Phone Book entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("PhoneBook", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save all Phone Book entries. + Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + ------------------------------------------------------------------------*/ + for (i = (PhoneBook.Count() - 1); i >= 0; i--) { + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + WWWritePrivateProfileString ("PhoneBook", entrytext, buf, buffer); + } + + /*------------------------------------------------------------------------ + Write the INI data out to a file. + ------------------------------------------------------------------------*/ + file.Open(WRITE); + file.Write(buffer,strlen(buffer)); + file.Close(); +#endif +} + + +/*********************************************************************************************** + * Read_Scenario_Descriptions -- reads multi-player scenario #'s # descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Read_Scenario_Descriptions (void) +{ + char *buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char fname[20]; + + /*------------------------------------------------------------------------ + Clear the scenario description lists + ------------------------------------------------------------------------*/ + MPlayerScenarios.Clear(); + MPlayerFilenum.Clear(); + + /*------------------------------------------------------------------------ + Loop through all possible scenario numbers; if a file is available, add + its number to the FileNum list. + ------------------------------------------------------------------------*/ + for (i = 0; i < 100; i++) { + Set_Scenario_Name(ScenarioName, i, SCEN_PLAYER_MPLAYER, + SCEN_DIR_EAST, SCEN_VAR_A); + sprintf(fname,"%s.INI",ScenarioName); + file.Set_Name (fname); + + if (file.Is_Available()) { + MPlayerFilenum.Add(i); + } + } + + /*------------------------------------------------------------------------ + Now, for every file in the FileNum list, read in the INI file, and extract + its description. + ------------------------------------------------------------------------*/ + for (i = 0; i < MPlayerFilenum.Count(); i++) { + /*..................................................................... + Fetch working pointer to the INI staging buffer. Make sure that the + buffer is cleared out before proceeding. + .....................................................................*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*..................................................................... + Create filename and read the file. + .....................................................................*/ + Set_Scenario_Name(ScenarioName, MPlayerFilenum[i], SCEN_PLAYER_MPLAYER, + SCEN_DIR_EAST, SCEN_VAR_A); + sprintf(fname,"%s.INI",ScenarioName); + file.Set_Name (fname); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + + /*..................................................................... + Extract description & add it to the list. + .....................................................................*/ + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", + MPlayerDescriptions[i], 40, buffer); + MPlayerScenarios.Add(MPlayerDescriptions[i]); + } +} + + +/*********************************************************************************************** + * Free_Scenario_Descriptions -- frees memory for the scenario descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +void Free_Scenario_Descriptions(void) +{ + /*------------------------------------------------------------------------ + Clear the scenario descriptions & filenames + ------------------------------------------------------------------------*/ + MPlayerScenarios.Clear(); + MPlayerFilenum.Clear(); + +//PG_TO_FIX +#if (0) + int i; + + /*------------------------------------------------------------------------ + Clear the initstring entries + ------------------------------------------------------------------------*/ + for (i = 0; i < InitStrings.Count(); i++) { + delete InitStrings[i]; + } + InitStrings.Clear(); + + /*------------------------------------------------------------------------ + Clear the dialing entries + ------------------------------------------------------------------------*/ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); +#endif +} + + +/*************************************************************************** + * Computer_Message -- "sends" a message from the computer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +void Computer_Message(void) +{ + int color; + char txt[160]; + HousesType house; + HouseClass *ptr; + + /*------------------------------------------------------------------------ + Find the computer house that the message will be from + ------------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + /*..................................................................... + Decode this house's color + .....................................................................*/ + color = MPlayerTColors[ptr->RemapColor]; + + /*..................................................................... + We now have a 1/4 chance of echoing one of the human players' messages + back. + .....................................................................*/ + if (IRandom(0,3) == 2) { + /*.................................................................. + Now we have a 1/3 chance of garbling the human message. + ..................................................................*/ + if (IRandom(0,2) == 1) { + Garble_Message(LastMessage); + } + + /*.................................................................. + Only add the message if there is one to add. + ..................................................................*/ + if (strlen(LastMessage)) { + sprintf(txt,"%s %s",Text_String(TXT_FROM_COMPUTER),LastMessage); + Messages.Add_Message(txt, color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + } + } else { + sprintf(txt,"%s %s",Text_String(TXT_FROM_COMPUTER), + Text_String(TXT_COMP_MSG1 + IRandom(0,12))); + Messages.Add_Message(txt, color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + } + + return; + } +} + + +/*************************************************************************** + * Garble_Message -- "garbles" a message * + * * + * INPUT: * + * buf buffer to garble; stores output message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +static void Garble_Message(char *buf) +{ + char txt[80]; + char punct[20]; // for punctuation + char *p; // working ptr + int numwords; // # words in the phrase + char *words[40]; // ptrs to various words in the phrase + int i,j; + + /*------------------------------------------------------------------------ + Pull off any trailing punctuation + ------------------------------------------------------------------------*/ + p = buf + strlen(buf) - 1; + while (1) { + if (p < buf) + break; + if (p[0]=='!' || p[0]=='.' || p[0]=='?') { + p--; + } else { + p++; + break; + } + if (strlen(p) >= (sizeof(punct) - 1) ) { + break; + } + } + strcpy (punct, p); + p[0] = 0; + + for (i = 0; i < 40; i++) { + words[i] = NULL; + } + + /*------------------------------------------------------------------------ + Copy the original buffer + ------------------------------------------------------------------------*/ + strcpy(txt,buf); + + /*------------------------------------------------------------------------ + Split it up into words + ------------------------------------------------------------------------*/ + p = strtok (txt, " "); + numwords = 0; + while (p) { + words[numwords] = p; + numwords++; + p = strtok (NULL, " "); + } + + /*------------------------------------------------------------------------ + Now randomly put the words back. Don't use the real random-number + generator, since different machines will have different LastMessage's, + and will go out of sync. + ------------------------------------------------------------------------*/ + buf[0] = 0; + for (i = 0; i < numwords; i++) { + j = Sim_IRandom(0,numwords); + if (words[j] == NULL) { // this word has been used already + i--; + continue; + } + strcat(buf,words[j]); + words[j] = NULL; + if (i < numwords-1) + strcat(buf," "); + } + strcat(buf,punct); +} + + +/*************************************************************************** + * Surrender_Dialog -- Prompts user for surrendering * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = user cancels, 1 = user wants to surrender. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +int Surrender_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 170*factor; // dialog width + int d_dialog_h = 53*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // centered x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin = 5*factor; // margin width/height + int d_topmargin = 20*factor; // top margin + + int d_ok_w = 45*factor; // ok width + int d_ok_h = 9*factor; // ok height + int d_ok_x = d_dialog_cx - d_ok_w - 5*factor; // ok x + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; // ok y + + int d_cancel_w = 45*factor; // cancel width + int d_cancel_h = 9*factor; // cancel height + int d_cancel_x = d_dialog_cx + 5*factor; // cancel x + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; // cancel y + + + /*........................................................................ + Button enumerations + ........................................................................*/ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + int retcode; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ......................... Create the button list ......................... + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + if (Main_Loop()) { + retcode = 0; + process = false; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(Text_String(TXT_SURRENDER), + d_dialog_cx, d_dialog_y + d_topmargin, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retcode = 0; + process = false; + break; + + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} \ No newline at end of file diff --git a/TIBERIANDAWN/MSGBOX.CPP b/TIBERIANDAWN/MSGBOX.CPP new file mode 100644 index 000000000..14b9e0061 --- /dev/null +++ b/TIBERIANDAWN/MSGBOX.CPP @@ -0,0 +1,479 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\msgbox.cpv 2.18 16 Oct 1995 16:50:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCMessageBox::Process -- Handles all the options graphic interface. * + * CCMessageBox::Process -- Displays message box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + +#ifdef JAPANESE +CCMessageBox::CCMessageBox(int caption, bool pict) : Caption(caption), IsPicture(pict) +{ +} +#endif + +/*********************************************************************************************** + * CCMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int CCMessageBox::Process(const char *msg, const char *b1txt, const char *b2txt, const char *b3txt, bool preserve) +{ +#define BUFFSIZE (511) +//#define BUFFSIZE (255) + char buffer[BUFFSIZE]; + int retval; + bool process; // loop while true + KeyNumType input; // user input + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[3]; + void *back; + BOOL display; // display level + int realval[5]; + + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(),VisiblePage.Get_Height(),(void*)NULL); + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + if (b1txt && *b1txt == '\0') b1txt = 0; + if (b2txt && *b2txt == '\0') b2txt = 0; + if (b3txt && *b3txt == '\0') b3txt = 0; + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + char b1char, b2char, b3char; // 1st char of each string + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt) { + b1char = toupper(b1txt[0]); + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2*factor); + bwidth = MAX((String_Pixel_Width(b1txt) + (8 *factor)), 30U * (unsigned)factor); + + if (b2txt) { + numbuttons = 2; + b2char = toupper(b2txt[0]); + bwidth = MAX((String_Pixel_Width( b2txt ) + (8*factor)), (unsigned)bwidth); + + if (b3txt) { + numbuttons = 3; + b3char = toupper(b3txt[0]); + } + + } else { + numbuttons = 1; + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-2); + int width; + int height; + +#ifdef JAPANESE + if(IsPicture) { + width = 90; + height = 140 - 60; + } else +#endif + Format_Window_String(buffer, 255 * factor, width, height); + +//BG #ifdef JAPANESE +//BG if(!IsPicture) { +//BG #else + width = MAX(width, 50 * factor); + width += 40 * factor; +//BG #endif +//BG #ifdef JAPANESE +//BG } +//BG #endif + height += (numbuttons == 0) ? (30 * factor) : (60 * factor); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + VisiblePage.Blit(seen_buff_save); + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + TextButtonClass button1(BUTTON_1, b1txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (10*factor)), y + height - (bheight + (5*factor)), bwidth); + + TextButtonClass button2(BUTTON_2, b2txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + x + width - (bwidth + (10*factor)), y + height - (bheight + (5*factor)), bwidth); + + TextButtonClass button3(BUTTON_3, b3txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, 0, y + height - (bheight + (5*factor))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and inialize the buttons to the button list. + */ + if (numbuttons) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); + } + //display = TRUE; +#ifdef JAPANESE + if(IsPicture) { + Load_Uncompress(CCFileClass(msg), SysMemPage, SysMemPage); + SysMemPage.Blit(SeenBuff, 160, 100); + } else { +#endif + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the caption. + */ + Fancy_Text_Print(buffer, x + (20*factor), y + (25*factor), CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); +#ifdef JAPANESE + } +#endif + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + seen_buff_save.Blit(VisiblePage); + display = TRUE; + } + + if (display){ + display = FALSE; + + Hide_Mouse(); + +#ifdef JAPANESE + if(IsPicture) { + Load_Uncompress(CCFileClass(msg), SysMemPage, SysMemPage); + SysMemPage.Blit(SeenBuff, 160, 100); + } else { +#endif + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the caption. + */ + Fancy_Text_Print(buffer, x + (20 * factor), y + (25 * factor), CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); +#ifdef JAPANESE + } +#endif + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + switch (input) { + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (KN_ESC): + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } + break; + + + case (BUTTON_2|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[2]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_1; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: +#ifdef NEVER + if (b1char == toupper(Keyboard::To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_1; + pressed = true; + } else if (b2txt!=NULL && + b2char == toupper(Keyboard::To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_2; + pressed = true; + } +#endif + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + /* + ** Delay for a brief moment so that the dialog box will be visible long + ** enough to read the text. + */ + CountDownTimerClass timer(BT_SYSTEM, 0); + timer.Start(); + timer.Set(TICKS_PER_SECOND*4); + while (timer.Time() > 0) { + Call_Back(); + } + Keyboard::Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()){ + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + } + SeenBuff.Unlock(); + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * CCMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int CCMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * CCMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int CCMessageBox::Process(char const *msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/TIBERIANDAWN/MSGBOX.H b/TIBERIANDAWN/MSGBOX.H new file mode 100644 index 000000000..034e54d61 --- /dev/null +++ b/TIBERIANDAWN/MSGBOX.H @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\msgbox.h_v 2.17 16 Oct 1995 16:47:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGBOX_H +#define MSGBOX_H + +#include "jshell.h" + +class CCMessageBox +{ + int Caption; +#ifdef JAPANESE + bool IsPicture; +#endif + public: +#ifdef JAPANESE + CCMessageBox(int caption=TXT_NONE, bool pict=false); +#else + CCMessageBox(int caption=TXT_NONE) {Caption = caption;}; +#endif + int Process(const char *msg, const char *b1txt, const char *b2txt=NULL, const char *b3txt=NULL, bool preserve=false); + int Process(int msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); + int Process(char const *msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); +}; + +#endif diff --git a/TIBERIANDAWN/MSGLIST.CPP b/TIBERIANDAWN/MSGLIST.CPP new file mode 100644 index 000000000..a9e1e8055 --- /dev/null +++ b/TIBERIANDAWN/MSGLIST.CPP @@ -0,0 +1,809 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\msglist.cpv 1.4 16 Oct 1995 16:48:20 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : June 26, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MessageListClass::Add_Edit -- Adds editable string to message list * + * MessageListClass::Add_Message -- displays the given message * + * MessageListClass::Draw -- Draws the messages * + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * MessageListClass::Init -- Inits message system, sets options * + * MessageListClass::Input -- Handles input for sending messages * + * MessageListClass::Manage -- Manages multiplayer messages * + * MessageListClass::MessageListClass -- constructor * + * MessageListClass::~MessageListClass -- destructor * + * MessageListClass::Num_Messages -- returns # messages in the list * + * MessageListClass::Set_Width -- sets allowable width of messages * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +// ST = 12/17/2018 5:44PM +#ifndef TickCount +extern TimerClass TickCount; +#endif + +char MessageListClass::MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; +char MessageListClass::BufferAvail[MAX_NUM_MESSAGES]; + +/*************************************************************************** + * MessageListClass::MessageListClass -- constructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::MessageListClass(void) +{ + int i; + + MessageList = 0; + MessageX = 0; + MessageY = 0; + MaxMessages = 0; + MaxChars = 0; + Height = 0; + EditLabel = 0; + EditBuf = 0; + EditCurPos = 0; + EditInitPos = 0; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + +} + + +/*************************************************************************** + * MessageListClass::~MessageListClass -- destructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::~MessageListClass() +{ + Init(0,0,0,0,0); +} + + +/*************************************************************************** + * MessageListClass::Init -- Inits message system, sets options * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * maxchars max # characters allowed per message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Init(int x, int y, int max_msg, int maxchars, int height) +{ + TextLabelClass *txtlabel; + int i; + + /*------------------------------------------------------------------------ + Remove every entry in the list + ------------------------------------------------------------------------*/ + txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + /*------------------------------------------------------------------------ + Mark all buffers as available + ------------------------------------------------------------------------*/ + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + + /*------------------------------------------------------------------------ + Init variables + ------------------------------------------------------------------------*/ + MessageList = 0; + MessageX = x; + MessageY = y; + + MaxMessages = max_msg; + if (MaxMessages > MAX_NUM_MESSAGES) + MaxMessages = MAX_NUM_MESSAGES; + + MaxChars = maxchars; + if (MaxChars > MAX_MESSAGE_LENGTH) + MaxChars = MAX_MESSAGE_LENGTH; + + Height = height; + EditLabel = 0; + EditBuf = 0; + EditCurPos = 0; + EditInitPos = 0; +} + + +/*************************************************************************** + * MessageListClass::Add_Message -- displays the given message * + * * + * INPUT: * + * txt text to display * + * color color to draw text in * + * style style to use * + * timeout # of ticks the thing is supposed to last (-1 = forever)* + * * + * OUTPUT: * + * ptr to new TextLabelClass object. * + * * + * WARNINGS: * + * The TextLabelClass's text buffer is free'd when the class is free'd, * + * so never pass it a static buffer. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Message(char *txt, int color, + TextPrintType style, int timeout, unsigned short magic_number, unsigned short crc) +{ + int num_msg; + TextLabelClass *txtlabel; + int x,y; + GadgetClass *gadg; + int i,j; + int found; + int position; + char *raw_string; + char *current_string; + char *s1,*s2; + bool same; +#if (0) +#if (GERMAN) + static int from_adjust = -1; +#else +#if (FRENCH) + static int from_adjust = -2; +#else + static int from_adjust = 0; +#endif +#endif +#endif //(0) + + /*------------------------------------------------------------------------ + Prevent a duplicate message. (The IPXManager Global Channel cannot detect + a resend of a packet, so sometimes two identical messages appear in a row.) + ------------------------------------------------------------------------*/ + if (MessageList) { + txtlabel = MessageList; + while (txtlabel) { + /* + ** Dont check for duplicates in multi-segment strings + */ + if (!txtlabel->Segments){ + if (!strcmp (txtlabel->Text,txt) && txtlabel->Color == color && + txtlabel->Style == style) { + return(txtlabel); + } + } + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + + /* + ** If the magic number is a valid message tail then see if we + ** can add this message to the tail of an existing message (crc's must also match) + */ + if (magic_number > MESSAGE_HEAD_MAGIC_NUMBER && + magic_number < MESSAGE_HEAD_MAGIC_NUMBER+MAX_MESSAGE_SEGMENTS){ + + position = magic_number - MESSAGE_HEAD_MAGIC_NUMBER; + txtlabel = MessageList; + + while (txtlabel) { + if (txtlabel->Color == color && txtlabel->Style == style && txtlabel->CRC == crc) { + + same = true; + + s1 = strchr(txtlabel->Text, ':'); + s2 = strchr(txt, ':'); + + if (s1 && s2){ + *s1 = 0; + *s2 = 0; + + same = !strcmp (txtlabel->Text, txt); + + *s1 = ':'; + *s2 = ':'; + } + + if (same){ + + /* + ** If this message segment hasnt already come through then add it to the existing text + */ + if (! (txtlabel->Segments & (1 << position)) ) { + /* + ** Search for the ':' to find the actual message after the players name + */ + raw_string = s2; + current_string = s1; + if (raw_string++ && current_string++){ + memcpy (current_string + (position*(COMPAT_MESSAGE_LENGTH-5))/*+from_adjust*/, raw_string, COMPAT_MESSAGE_LENGTH-4); + /* + ** Flag this string segment as complete + */ + txtlabel->Segments |= 1<Get_Next(); + } + } + + + + /*------------------------------------------------------------------------ + Count the # of messages; if MaxMessages is going to be exceeded, remove + the top-most message. + ------------------------------------------------------------------------*/ + num_msg = 0; + if (MessageList) { + gadg = MessageList; + while (gadg) { + num_msg++; + gadg = gadg->Get_Next(); + } + } + /*........................................................................ + Remove the top-most message, but don't remove the edit message. + ........................................................................*/ + if ( (MaxMessages > 0) && ((num_msg + 1) > MaxMessages)) { + txtlabel = MessageList; + /*..................................................................... + If the top label is the edit label, go to the next one; if there is + no next one, just return. + .....................................................................*/ + if (txtlabel == EditLabel) + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + if (txtlabel==NULL) + return(NULL); + + /*..................................................................... + Remove this message from the list; mark its buffer as being available. + .....................................................................*/ + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + + /*..................................................................... + Recompute everyone's y-coordinate + .....................................................................*/ + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + } + + /*------------------------------------------------------------------------ + Figure out the message's y-coordinate; put it below the other messages + ------------------------------------------------------------------------*/ + x = MessageX; + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg = gadg->Get_Next(); + y += Height; + } + } + + /*------------------------------------------------------------------------ + Create the message + ------------------------------------------------------------------------*/ + txtlabel = new TextLabelClass (txt, x, y, color, style); + if (timeout==-1) { + txtlabel->UserData = 0; + } else { + txtlabel->UserData = TickCount.Time() + timeout; + } + + /*------------------------------------------------------------------------ + Find a buffer to store our message in; if there are none, don't add the + message. + ------------------------------------------------------------------------*/ + found = 0; + txtlabel->Segments = 0; + txtlabel->CRC = crc; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (BufferAvail[i]) { + BufferAvail[i] = 0; + memset (MessageBuffers[i],0,MAX_MESSAGE_LENGTH + 30); + strcpy (MessageBuffers[i],txt); + + /* + ** If this is a segment from a larger message then put it in the right place + ** in the buffer and clear out the rest with spaces + */ + if (magic_number >= MESSAGE_HEAD_MAGIC_NUMBER && + magic_number < MESSAGE_HEAD_MAGIC_NUMBER+MAX_MESSAGE_SEGMENTS){ + raw_string = strchr(txt, ':'); + char *dest_str = strchr(MessageBuffers[i], ':'); + if (dest_str){ + dest_str++; + }else{ + dest_str = MessageBuffers[i]; + } + + if (raw_string++){ + for (j=0 ; j<3 ; j++){ + if (! ((magic_number - j) == MESSAGE_HEAD_MAGIC_NUMBER)){ + memset (dest_str + j*(COMPAT_MESSAGE_LENGTH-4)/*+from_adjust*/, 32, COMPAT_MESSAGE_LENGTH-4); + }else{ + strcpy(dest_str + j*(COMPAT_MESSAGE_LENGTH-4)/*+from_adjust*/, raw_string); + } + } + *(dest_str +((COMPAT_MESSAGE_LENGTH-4)*MAX_MESSAGE_SEGMENTS-1)) = 0; + + } + position = magic_number - MESSAGE_HEAD_MAGIC_NUMBER; + txtlabel->Segments = 1<Text = MessageBuffers[i]; + found = 1; + break; + } + } + if (!found) { + delete txtlabel; + return (NULL); + } + + /*------------------------------------------------------------------------ + Attach the message to our list + ------------------------------------------------------------------------*/ + if (MessageList) { + txtlabel->Add_Tail (*MessageList); + } else { + MessageList = txtlabel; + } + + return(txtlabel); +} + + +/*************************************************************************** + * MessageListClass::Add_Edit -- Adds editable string to message list * + * * + * INPUT: * + * color color of edit message * + * style style of edit message * + * to string: who to send to * + * width width of editbox in pixels * + * * + * OUTPUT: * + * ptr to new TextLabelClass * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Edit(int color, TextPrintType style, + char *to, int width) +{ + /*------------------------------------------------------------------------ + Do nothing if we're already in "edit" mode + ------------------------------------------------------------------------*/ + if (EditLabel) + return(NULL); + + /*------------------------------------------------------------------------ + Initialize the buffer positions; add a new label to the label list. + ------------------------------------------------------------------------*/ + EditCurPos = EditInitPos = strlen(to); + EditLabel = Add_Message (to, color, style, -1, 0, 0); + Width = width; + + /*------------------------------------------------------------------------ + Save our edit buffer pointer. + ------------------------------------------------------------------------*/ + if (EditLabel) + EditBuf = EditLabel->Text; + else + EditBuf = NULL; + + return(EditLabel); +} + + +/*************************************************************************** + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to edit buffer, minus the "To:" header * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Edit_Buf(void) +{ + if (!EditBuf) + return(NULL); + + return(EditBuf + EditInitPos); +} + + +/*************************************************************************** + * MessageListClass::Manage -- Manages multiplayer messages * + * * + * If this routine returns TRUE, the caller should update the display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 0 = no change has occurred, 1 = changed * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Manage (void) +{ + TextLabelClass *txtlabel; + TextLabelClass *next; + int changed = 0; + int y; + GadgetClass *gadg; + int i; + + /*------------------------------------------------------------------------ + Loop through all messages + ------------------------------------------------------------------------*/ + txtlabel = MessageList; + while (txtlabel) { + /*..................................................................... + If this message's time is up, remove it from the list + .....................................................................*/ + if (txtlabel->UserData != 0 && (unsigned)TickCount.Time() > txtlabel->UserData) { + /*.................................................................. + If we're about to delete the edit message, clear our edit message + values. + ..................................................................*/ + if (txtlabel == EditLabel) { + EditLabel = 0; + EditBuf = 0; + } + /*.................................................................. + Save the next ptr in the list; remove this entry + ..................................................................*/ + next = (TextLabelClass *)txtlabel->Get_Next(); + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + changed = 1; + txtlabel = next; + } else { + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + /*------------------------------------------------------------------------ + If a changed has been made, recompute the y-coord of all messages + ------------------------------------------------------------------------*/ + if (changed) { + + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + } + + return(changed); +} + + +/*************************************************************************** + * MessageListClass::Input -- Handles input for sending messages * + * * + * INPUT: * + * input key value to process * + * * + * OUTPUT: * + * 1 = caller should redraw the message list (no need to complete * + * refresh, though) * + * 2 = caller should completely refresh the display. * + * 3 = caller should send the edit message. * + * (sets 'input' to 0 if it processes it.) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Input(KeyNumType &input) +{ + KeyASCIIType ascii; + int retcode = 0; + + /*------------------------------------------------------------------------ + Do nothing if nothing to do. + ------------------------------------------------------------------------*/ + if (input == KN_NONE) + return(0); + + /*------------------------------------------------------------------------ + Leave mouse events alone. + ------------------------------------------------------------------------*/ + if ( (input & (~KN_RLSE_BIT))==KN_LMOUSE || + (input & (~KN_RLSE_BIT))==KN_RMOUSE) + return(0); + + /*------------------------------------------------------------------------ + If we're in 'edit mode', handle keys + ------------------------------------------------------------------------*/ + if (EditLabel) { + ascii = (KeyASCIIType)(Keyboard::To_ASCII(input) & 0x00ff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((input & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + input = (KeyNumType)(input & ~WWKEY_VK_BIT); + + } else { + /* + ** Filter out all special keys except return, escape and backspace + */ + if ((!(input & WWKEY_VK_BIT) && !(input & KN_BUTTON) + && ascii >= ' ' && ascii <= 127) + || (input & 0xff)== (KN_RETURN & 0xff) + || (input & 0xff)== (KN_BACKSPACE & 0xff) + || (input & 0xff)== (KN_ESC & 0xff) ) { + + //ascii = (KeyASCIIType)(Keyboard->To_ASCII(input)); + } else { + input = KN_NONE; + return (0); + } + } + + + switch (ascii) { + /*------------------------------------------------------------------ + ESC = abort message + ------------------------------------------------------------------*/ + case KA_ESC & 0xff: + EditLabel->UserData = 1; // force it to be removed + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + RETURN = send the message + ------------------------------------------------------------------*/ + case KA_RETURN & 0xff: + EditLabel->UserData = 1; // force it to be removed + retcode = 3; + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + BACKSPACE = remove a character + ------------------------------------------------------------------*/ + case KA_BACKSPACE & 0xff: + if (EditCurPos > EditInitPos) { + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 2; + } + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + default: add a character. Reserve the last buffer position for null. + (EditCurPos - EditInitPos) is the buffer index # of the next + character, after the "To:" prefix. + ------------------------------------------------------------------*/ + default: + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + if (!(input & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 127) { + EditBuf[EditCurPos] = ascii; + EditCurPos++; + retcode = 1; + + /* + ** Verify that the additional character would not overrun the on screen edit box. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, EditLabel->Color, TBLACK, EditLabel->Style); + int width = String_Pixel_Width(EditBuf); + if (width >= Width){ + EditBuf[EditCurPos--] = 0; + retcode = 0; + } + + } + } + input = KN_NONE; + break; + } + } + + return(retcode); +} + + +/*************************************************************************** + * MessageListClass::Draw -- draws messages * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Draw(void) +{ + if (MessageList) { + Hide_Mouse(); + MessageList->Draw_All(); + Show_Mouse(); + } +} + + +/*************************************************************************** + * MessageListClass::Num_Messages -- returns # messages in the list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of messages * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Num_Messages(void) +{ + GadgetClass *gadg; + int num; + + num = 0; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + num++; + gadg = gadg->Get_Next(); + } + } + + return (num); +} + + +/*************************************************************************** + * MessageListClass::Set_Width -- sets allowable width of messages * + * * + * INPUT: * + * width pixel width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Width(int width) +{ + GadgetClass *gadg; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + ((TextLabelClass *)gadg)->PixWidth = width; + gadg = gadg->Get_Next(); + } + } +} + diff --git a/TIBERIANDAWN/MSGLIST.H b/TIBERIANDAWN/MSGLIST.H new file mode 100644 index 000000000..078646183 --- /dev/null +++ b/TIBERIANDAWN/MSGLIST.H @@ -0,0 +1,106 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : May 22, 1995 [BRR] * + * * + * How the messages work: * + * - MPlayerMessageList is a gadget list of all current messages * + * - MPlayerMessageX & Y are the upper left corner of the 1st message * + * - MPlayerMaxMessages is the max # of messages allowed, including * + * the editable message; 0 = no limit. * + * - EditLabel points to the textmessage gadget for the current editable * + * field. EditBuf points to the char buffer being edited. EditInitPos * + * & EditCurPos define buffer index positions. * + * - EditSendAddress is the IPX Address to send the message to when RETURN * + * is pressed. * + * * + * The UserData field in the TextLabelClass tells what the timeout for * + * each message is (0 = none). * + * When a message's timeout expires, it's deleted. When a new message * + * is added, the top message is deleted if MPlayerMaxMessages is exceeded. * + * * + * The Edit-able message is never deleted until ESC or RETURN is pressed. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGLIST_H +#define MSGLIST_H + +/* +** Class declaration +*/ +class MessageListClass { + public: + /* + ** Constructor/Destructor + */ + MessageListClass (void); + ~MessageListClass (); + + /* + ** Initialization + */ + void Init (int x, int y, int max_msg, int maxchars, int height); + TextLabelClass * Add_Message (char *txt, int color, TextPrintType style, int timeout, + unsigned short magic_number, unsigned short crc); + + /* + ** Message-editing routines + */ + TextLabelClass * Add_Edit (int color, TextPrintType style, char *to, int width); + char * Get_Edit_Buf (void); + + /* + ** Maintenance routines + */ + int Manage (void); + int Input (KeyNumType &input); + void Draw(void); + int Num_Messages(void); + void Set_Width(int width); + + private: + TextLabelClass * MessageList; // list of messages + int MessageX; // x-coord of upper-left + int MessageY; // y-coord of upper-left + int MaxMessages; // max messages allowed + int MaxChars; // max allowed chars per message + int Height; // height in pixels + TextLabelClass *EditLabel; // ptr to current edit label + char *EditBuf; // ptr to current edit buffer + int EditCurPos; // current edit position + int EditInitPos; // initial edit position + int Width; // Maximum width in pixels of editable string + + /* + ** Static buffers provided for messages. They must be long enough for + ** both the message, and for the "To" prefix on edited messages, or + ** the "From:" prefix on received messages. + */ + static char MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; + static char BufferAvail[MAX_NUM_MESSAGES]; +}; + +#endif diff --git a/TIBERIANDAWN/MiscAsm.cpp b/TIBERIANDAWN/MiscAsm.cpp new file mode 100644 index 000000000..e78ffc5a6 --- /dev/null +++ b/TIBERIANDAWN/MiscAsm.cpp @@ -0,0 +1,1578 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** +** Misc. assembly code moved from headers +** +** +** +** +** +*/ + +#include "FUNCTION.H" + + + +extern "C" void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy) +{ + memcpy(dest, source, bytes_to_copy); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2) +{ + __asm { + mov eax,[coord1] + mov ebx,[coord2] + mov dx,ax + sub dx,bx + jg okx + neg dx + okx: + shr eax,16 + shr ebx,16 + sub ax,bx + jg oky + neg ax +oky: + cmp ax,dx + jg ok + xchg ax,dx +ok: + shr dx,1 + add ax,dx + } +} + + + + +/* +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ + +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + static const char _new_facing16[] = { + 3, 2, 4,-1, 1, 2,0,-1, + 13,14,12,-1,15,14,0,-1, + 5, 6, 4,-1, 7, 6,8,-1, + 11,10,12,-1, 9,10,8,-1 + }; + + + __asm { + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[_new_facing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ + +int __cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) +{ + + __asm { + xor ebx,ebx //; Facing number. + + ////; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short xnotneg + neg ecx + mov ebx,11000000b //; Set bit 7 and 6 for leftward. +xnotneg: + + //; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ynotneg + xor ebx,01000000b //; Complement bit 6 for downward. + neg eax +ynotneg: + + //; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + //; Determine if the direction is closer to the Y axis and make sure that + //; CX holds the larger of the two deltas. This is in preparation for the + //; divide. + cmp eax,ecx + jb short gotaxis + xchg eax,ecx + xor edx,01000000b //; Closer to Y axis so make DX=64 for quad 0 and 2. +gotaxis: + + //; If closer to the X axis then add 64 for quadrants 0 and 2. If + //; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + //; add value is in DX and save on stack. + push edx + + //; Make sure that the division won't overflow. Reduce precision until + //; the larger number is less than 256 if it appears that an overflow + //; will occur. If the high byte of the divisor is not zero, then this + //; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short nooverflow +again: + test ecx,0FFFFFF00h + jz short nooverflow + shr ecx,1 + shr eax,1 + jmp short again +nooverflow: + + //; Make sure that the division won't underflow (divide by zero). If + //; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short nounderflow + mov eax,0FFFFFFFFh + jmp short divcomplete + + //; Derive a pseudo angle number for the octant. The angle is based + //; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +nounderflow: + xor edx,edx + shld edx,eax,8 //; shift high byte of eax into dl + shl eax,8 + div ecx +divcomplete: + + //; Integrate the 5 most significant bits into the angle index. If DX + //; is not zero, then it is 64. This means that the dividend must be negated + //; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short noneg + dec edx + neg eax +noneg: + add eax,edx + add eax,ebx + and eax,0FFH +// ret + } +} + + + + + + + + + + + + +/* + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +//NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + +// CODESEG +*/ + +/* +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +*/ +int __cdecl Desired_Facing8(long x1, long y1, long x2, long y2) +{ + + static const char _new_facing8[] = {1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4}; + + __asm { + + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 //; Close to major axis bit. + + xor eax,eax + mov al,[_new_facing8+ebx] + + //; Normalize to 0..FF range. + shl eax,5 + +// ret + + } + +} + + + +#if (0) + +/* + ; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/MiscAsm.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + + __asm { + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + +#if (0) + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END +#endif +#endif + + + + + + + + + + + + + + + + +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal) +{ + __asm { + + mov eax,0FFFFh //; establish default return value + + mov ebx,[base] + or ebx, ebx + jz retneg1 //; if base==0, return 65535 + + mov eax,[cardinal] //; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +retneg1: + //ret + + + } +} + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed) +{ +// PROC Fixed_To_Cardinal C near +// USES edx + +// ARG base:DWORD +// ARG fixed:DWORD + + __asm { + mov eax,[base] + mul [fixed] + add eax,080h //; eax = (base * fixed) + 0x80 + + test eax,0FF000000h //; if high byte set, return FFFF + jnz rneg1 + shr eax,8 //; else, return eax/256 + jmp all_done +rneg1: + mov eax,0FFFFh //; establish default return value +all_done: + //ret + } + + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + +} + + + + + + + + + + + + + + + +void __cdecl Set_Bit(void * array, int bit, int value) +{ + __asm { + mov ecx, [bit] + mov eax, [value] + mov esi, [array] + mov ebx,ecx + shr ebx,5 + and ecx,01Fh + btr [esi+ebx*4],ecx + or eax,eax + jz ok + bts [esi+ebx*4],ecx +ok: + } +} + + +int __cdecl Get_Bit(void const * array, int bit) +{ + __asm { + mov eax, [bit] + mov esi, [array] + mov ebx,eax + shr ebx,5 + and eax,01Fh + bt [esi+ebx*4],eax + setc al + } +} + +int __cdecl First_True_Bit(void const * array) +{ + __asm { + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + + +int __cdecl First_False_Bit(void const * array) +{ + __asm { + + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + not ebx + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + +int __cdecl Bound(int original, int min, int max) +{ + __asm { + mov eax,[original] + mov ebx,[min] + mov ecx,[max] + cmp ebx,ecx + jl okorder + xchg ebx,ecx +okorder: cmp eax,ebx + jg okmin + mov eax,ebx +okmin: cmp eax,ecx + jl okmax + mov eax,ecx +okmax: + } +} + + + + + + + + + +CELL __cdecl Coord_Cell(COORDINATE coord) +{ + __asm { + mov eax, coord + mov ebx,eax + shr eax,010h + xor al,al + shr eax,2 + or al,bh + } + +} + + + + + + + + + +/* +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* +*/ +void __cdecl Shake_Screen(int shakes) +{ + // PG_TO_FIX + // Need a different solution for shaking the screen + shakes; +} + + + +#if (0) +GLOBAL C Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + ret + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END + +#endif + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ + +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac) +{ + /* + global C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + */ + +#define ALLOWED_COUNT 16 +#define ALLOWED_START 256-ALLOWED_COUNT + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini1 + cmp [dest],0 + je fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START + innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT + fillerloop: + inc bl + mov al,bl + stosb + loop fillerloop + + fini1: + mov esi,[dest] + mov eax,esi + + //ret + } +} + + + + + + +extern "C" long __cdecl Reverse_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + xchg al,ah + ror eax,16 + xchg al,ah + } +} + + +extern "C" short __cdecl Reverse_Short(short number) +{ + __asm { + mov ax,[number] + xchg ah,al + } +} + + + +extern "C" long __cdecl Swap_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + ror eax,16 + } +} + + + + + + + + +/* + + + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + global C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. +*/ +void __cdecl strtrim(char *buffer) +{ + __asm { + cmp [buffer],0 + je short fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. + looper: + lodsb + cmp al,20h ; Space + je short looper + cmp al,9 ; TAB + je short looper + stosb + + ; Copy the rest of the string. + gruntloop: + lodsb + stosb + or al,al + jnz short gruntloop + dec edi + ; Strip the white space from the end of the string. + looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short looper2 + cmp ah,9 + je short looper2 + + fini: + //ret + } +} + + +/* +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + global C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto +*/ + +void __cdecl Fat_Put_Pixel(int x, int y, int color, int siz, GraphicViewPortClass &gpage) +{ + __asm { + + cmp [siz],0 + je short exit_label + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[ebx]GraphicViewPortClass.Height ;YPIXEL_MAX + jae short exit_label + mov ecx,[ebx]GraphicViewPortClass.Width + add ecx,[ebx]GraphicViewPortClass.XAdd + add ecx,[ebx]GraphicViewPortClass.Pitch + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[ebx]GraphicViewPortClass.Width + cmp edx,[x] + mov edx,ecx + jbe short exit_label + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] + again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short again + + exit_label: + //ret + } +} + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Calculate_CRC :NEAR + + CODESEG +*/ + +extern "C" long __cdecl Calculate_CRC(void *buffer, long length) +{ + unsigned long crc; + + unsigned long local_length = (unsigned long) length; + + __asm { + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + //; Fetch the length of the data block to CRC. + + mov ecx,[local_length] + + jecxz short fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short remainder2 + accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop accumloop + + ; Handle the remainder bytes. + remainder2: + or dl,dl + jz short fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx + nextbyte: + lodsb + ror eax,8 + loop nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + + ;nextbyte: + ; shl eax,8 + ; lodsb + ; loop nextbyte + rol ebx,1 + add ebx,eax + + fini: + mov [crc],ebx + popad + mov eax,[crc] + //ret + } +} + + + + + + +extern "C" void __cdecl Set_Palette_Range(void *palette) +{ + if (palette == NULL) { + return; + } + + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); +} \ No newline at end of file diff --git a/TIBERIANDAWN/NETDLG.CPP b/TIBERIANDAWN/NETDLG.CPP new file mode 100644 index 000000000..7c44e49fb --- /dev/null +++ b/TIBERIANDAWN/NETDLG.CPP @@ -0,0 +1,5434 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\netdlg.cpv 2.17 16 Oct 1995 16:52:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : July 8, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house & color * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * (no other data) * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * Clear_Player_List -- Clears the player-name listbox & Vector * + * Destroy_Connection -- destroys the given connection * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Init_Network -- initializes network stuff * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Net_New_Dialog -- lets user start a new game * + * Process_Global_Packet -- responds to remote queries * + * Remote_Connect -- handles connecting this user to others * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Shutdown_Network -- shuts down network stuff * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include "tcpip.h" +#include "ccdde.h" +#define SHOW_MONO 0 + +// ST = 12/17/2018 5:44PM +#ifndef TickCount +extern TimerClass TickCount; +#endif + +#ifndef DEMO + +/*--------------------------------------------------------------------------- +The possible states of the join-game dialog +---------------------------------------------------------------------------*/ +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting +} JoinStateType; + +/*--------------------------------------------------------------------------- +The possible return codes from Get_Join_Responses() +---------------------------------------------------------------------------*/ +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game was detected + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static void Clear_Game_List (ListClass *gamelist); +static void Clear_Player_List (ListClass *playerlist); +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color); +static void Send_Join_Queries(int curgame, int gamenow, int playernow); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist); +static int Net_Fake_New_Dialog(void); +static int Net_Fake_Join_Dialog(void); + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers, allocates Real-mode + memory, and commands IPX to start listening on the Global Channel. + ------------------------------------------------------------------------*/ + if (!Ipx.Init()) + return(false); + + /*------------------------------------------------------------------------ + Allocate our "meta-packet" buffer + ------------------------------------------------------------------------*/ + if (!MetaPacket) { + MetaPacket = new char [sizeof (EventClass) * MAX_EVENTS]; + } + + /*------------------------------------------------------------------------ + Set up the IPX manager to cross a bridge + ------------------------------------------------------------------------*/ + if (!(GameToPlay == GAME_INTERNET)){ + if (IsBridge) { + BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ + +// +// Note: The thought behind this section of code was that if the program +// terminates early, without an EventClass::EXIT event, it still needs to +// tell the other systems that it's gone, so it would send a SIGN_OFF packet. +// BUT, this causes a sync bug if the systems are running slow and this system +// is running ahead of the others; it will send the NET_SIGN_OFF >>before<< +// the other system execute their EventClass::EXIT event, and the other systems +// will kill the connection at some random Frame # & turn my stuff over to +// the computer possibly at different times. +// BRR, 10/29/96 +// +#if (0) + /*------------------------------------------------------------------------ + Broadcast a sign-off packet, by sending the packet over the Global Channel, + telling the IPX Manager that no ACK is required, and specifying a NULL + destination address. + ------------------------------------------------------------------------*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + } + + /*------------------------------------------------------------------------ + Wait for the packets to finish going out (or the Global Channel times out) + ------------------------------------------------------------------------*/ + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + +#endif //(0) + + /*------------------------------------------------------------------------ + Delete our "meta-packet" + ------------------------------------------------------------------------*/ + delete [] MetaPacket; + MetaPacket = 0; + + /*------------------------------------------------------------------------ + If I was in a game, I'm not now, so clear the game name + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; +} + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must have been filled in before this function * + * can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + /* + ---------------- Another system asking what game this is ----------------- + */ + if (packet->Command==NET_QUERY_GAME && NetStealth==0) { + /*..................................................................... + If the game is closed, let every player respond, and let the sender of + the query sort it all out. This way, if the game's host exits the game, + the game still shows up on other players' dialogs. + If the game is open, only the game owner may respond. + .....................................................................*/ + if (strlen(MPlayerName) > 0 && strlen(MPlayerGameName) > 0 && + ((!NetOpen) || (NetOpen && !strcmp(MPlayerName,MPlayerGameName)))) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, MPlayerGameName); +#ifdef PATCH + if (IsV107) { + mypacket.GameInfo.Version = 1; + } else { + mypacket.GameInfo.Version = 2; + } +#else + mypacket.GameInfo.Version = Version_Number(); +#endif + mypacket.GameInfo.IsOpen = NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } else { + + /* + ----------------- Another system asking what player I am ----------------- + */ + if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, MPlayerGameName) && + (strlen(MPlayerGameName) > 0) && NetStealth==0) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, MPlayerName); + mypacket.PlayerInfo.House = MPlayerHouse; + mypacket.PlayerInfo.Color = MPlayerColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(MPlayerGameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i,j; + HousesType house; + HouseClass *housep; + char txt[80]; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ipx.Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ipx.Connection_Name(id)); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + /*------------------------------------------------------------------------ + Delete the IPX connection, shift the MPlayerID's & MPlayerHouses' back one. + ------------------------------------------------------------------------*/ + Ipx.Delete_Connection(id); + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + housep->IsStarted = true; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + Init my game name to 0-length, since I haven't joined any game yet. + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*------------------------------------------------------------------------ + Keep looping until something useful happens. + ------------------------------------------------------------------------*/ + while (1) { + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Join_Dialog(); + + /*--------------------------------------------------------------------- + -1 = user selected Cancel + ---------------------------------------------------------------------*/ + if (rc==-1) { + NetStealth = stealth; + NetOpen = 0; + return(false); + } else { + + /*--------------------------------------------------------------------- + 0 = user has joined an existing game; save values & return + ---------------------------------------------------------------------*/ + if (rc==0) { + Write_MultiPlayer_Settings (); + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + + /*--------------------------------------------------------------------- + 1 = user requests New Network Game + ---------------------------------------------------------------------*/ + if (rc==1) { + /*.................................................................. + Pop up the New Network Game dialog; if user selects OK, return + 'true'; otherwise, return to the Join Dialog. + ..................................................................*/ + if (Net_New_Dialog()) { + Write_MultiPlayer_Settings (); + NetOpen = 0; + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + } + } +} + + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this host to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Server_Remote_Connect(void) +{ + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + if (!Net_Fake_New_Dialog()){ + Write_MultiPlayer_Settings (); + return (false); + } + + NetOpen = 0; + NetStealth = stealth; + Write_MultiPlayer_Settings (); + return (true); +} + + +/*********************************************************************************************** + * Client_Remote_Connect -- handles connecting this client to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 ST : Created. * + *=============================================================================================*/ +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Fake_Join_Dialog(); + Write_MultiPlayer_Settings (); + + NetStealth = stealth; + NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } +} + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or New; * + * if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains. * + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click * + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message just changes to "Confirmed. Waiting for * + * Entry Signal." or some such nonsense. The user can still click around & see who's * + * in which games. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³³ ³ Peter Parker GDI ³³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 287 *factor; // dialog width + int d_dialog_h = 198*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // large margin + int d_margin2 = 2*factor; // small margin + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx - 10*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx - 10*factor; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w; + int d_nod_y = d_name_y + d_name_h + d_margin2; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_nod_y + d_nod_h + d_margin2; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + int d_gamelist_x = d_dialog_x + d_margin1; + int d_gamelist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_playerlist_w; + int d_playerlist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_msg1_y = d_gamelist_y + d_gamelist_h + d_margin1; + int d_msg2_y = d_msg1_y + d_txt6_h; + int d_msg3_y = d_msg2_y + d_txt6_h; + int d_msg4_y = d_msg3_y + d_txt6_h; + int d_msg5_y = d_msg4_y + d_txt6_h; + + int d_join_w = 40*factor; + int d_join_h = 9*factor; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_msg5_y + d_txt6_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_new_w = 40*factor; + int d_new_h = 9*factor; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*factor}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + char txt[80]; + char const *p; + int parms_received; // 1 = game options received + int found; + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_join_x, d_join_y); +#else + d_join_x, d_join_y, d_join_w, d_join_h); +#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_new_x, d_new_y, d_new_w, d_new_h); + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + + /* + --------------------------- Send network query --------------------------- + */ + Send_Join_Queries (game_index, 1, 0); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), + sizeof(NetCommandType), GlobalPacketNames, 11); + Ipx.Mono_Debug_Print(-1,1); + #endif + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_JOIN_NETWORK_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5, d_name_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5, d_gdi_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5, d_color_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_GAMES, + d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Join-state-specific labels: + ...............................................................*/ + if (joinstate > JOIN_NOTHING) { + Fancy_Text_Print(namebuf, d_name_x, d_name_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (MPlayerHouse==HOUSE_GOOD) { + Fancy_Text_Print(TXT_G_D_I, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print(TXT_N_O_D, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + gdibtn.Zap(); + nodbtn.Zap(); + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + sendbtn.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + /*............................................................... + Only add the name edit field, the House, Join & New buttons if + we're doing nothing, or we've just been rejected. + ...............................................................*/ + if (joinstate <= JOIN_NOTHING) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } + if (joinstate == JOIN_CONFIRMED) + sendbtn.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2, + d_msg1_y, + d_dialog_x + d_dialog_w - 4, + d_msg5_y + d_txt6_h, + BLACK); + + if (joinstate==JOIN_CONFIRMED && parms_received) { + /*............................................................ + Scenario title + ............................................................*/ + p = Text_String(TXT_SCENARIO_COLON); + if (ScenarioIdx != -1) { + sprintf(txt,"%s %s",p, MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_NOD_COLOR, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } + + /*............................................................ + # of credits + ............................................................*/ + p = Text_String(TXT_START_CREDITS_COLON); + sprintf(txt, "%s %d", p, MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_msg2_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + + /*............................................................ + Count & Level values + ............................................................*/ + p = Text_String(TXT_COUNT); + sprintf(txt,"%s %d",p,MPlayerUnitCount); + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + p = Text_String(TXT_LEVEL); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%s %d", p, BuildLevel); + } else { + sprintf(txt, "%s **", p); + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases + ............................................................*/ + p = Text_String(TXT_BASES_COLON); + if (MPlayerBases) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium + ............................................................*/ + p = Text_String(TXT_TIBERIUM_COLON); + if (MPlayerTiberium) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goody boxes + ............................................................*/ + p = Text_String(TXT_CRATES_COLON); + if (MPlayerGoodies) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Computer AI players + ............................................................*/ + if (Special.IsCaptureTheFlag) { + p = Text_String(TXT_CAPTURE_THE_FLAG_COLON); + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + p = Text_String(TXT_AI_PLAYERS_COLON); + if (MPlayerGhosts) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } else { + + /*............................................................... + Rejection notice + ...............................................................*/ + if (joinstate==JOIN_REJECTED) { + Fancy_Text_Print(TXT_REQUEST_DENIED, + d_dialog_cx, d_msg3_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button: + - If we've joined a game, don't allow a new color selection + - otherwise, select that color + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) + break; + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + + display = REDRAW_COLORS; + } + break; + + /*------------------------------------------------------------------ + User clicks on the game list: + - If we've joined a game, don't allow the selected item to change; + otherwise: + - Clear the player list + - Send an immediate player query + ------------------------------------------------------------------*/ + case (BUTTON_GAMELIST | KN_BUTTON): + if (joinstate==JOIN_CONFIRMED) { + gamelist.Set_Selected_Index(game_index); + } else { + if (gamelist.Current_Index() != game_index) { + Clear_Player_List (&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + NEW: bail out with return code 1 + ------------------------------------------------------------------*/ + case (BUTTON_NEW | KN_BUTTON): + /* + .................. Force user to enter a name ................... + */ + if (strlen(namebuf)==0) { + CCMessageBox().Process(TXT_NAME_ERROR); + display = REDRAW_ALL; + break; + } + /* + ..................... Ensure name is unique ..................... + */ + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!stricmp(Games[i]->Name, namebuf)) { + found = 1; + CCMessageBox().Process (TXT_GAMENAME_MUSTBE_UNIQUE); + display = REDRAW_ALL; + break; + } + } + if (found) + break; + /* + .................... Save player & game name .................... + */ + strcpy(MPlayerName,namebuf); + strcpy(MPlayerGameName,namebuf); + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = edit a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if ( (input == KN_M && joinstate==JOIN_CONFIRMED) || + input==(BUTTON_SEND | KN_BUTTON) || input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + if (joinstate <= JOIN_NOTHING) { + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + } + + display = REDRAW_MESSAGE; + + break; + } + } else + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(),message_length) &0xffff); + + while ( sent_so_far < message_length ){ + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, namebuf); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0; //0xff; ST - 12/18/2018 11:36AM + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. The local + system will also receive this message, since it's in the Player list. + ..................................................................*/ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + } + else { + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + } + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { + rc = 0; + process = false; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, (unsigned long)60); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * * + * Assumes each entry in 'Games' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * gamelist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Game_List (ListClass *gamelist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (gamelist->Count()) { + item = (char *)(gamelist->Get_Item (0)); + gamelist->Remove_Item(item); + delete [] item; + } + gamelist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Games' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) + delete Games[i]; + + Games.Clear(); + +} /* end of Clear_Game_List */ + + +/*************************************************************************** + * Clear_Player_List -- Clears the player-name listbox & Vector * + * * + * Assumes each entry in 'Players' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * playerlist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Player_List (ListClass *playerlist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + } + playerlist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Players' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Players.Count(); i++) + delete Players[i]; + + Players.Clear(); + +} /* end of Clear_Player_List */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * playerlist listbox containing other players' names * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color) +{ + int i; + + playerlist = playerlist; // shaddup, Mr stupid compiler! + + /* + --------------------------- Validate join_index -------------------------- + */ + if ( (Games.Count()==0) || join_index > Games.Count() || join_index < 0) { + CCMessageBox().Process (TXT_NOTHING_TO_JOIN); + return(false); + } + + /* + ----------------------- Force user to enter a name ----------------------- + */ + if (strlen(playername)==0) { + CCMessageBox().Process (TXT_NAME_ERROR); + return(false); + } + + /* + ------------------------- The game must be open -------------------------- + */ + if (!Games[join_index]->Game.IsOpen) { + CCMessageBox().Process(TXT_GAME_IS_CLOSED); + return (false); + } + + /* + ------------------------ Make sure name is unique ------------------------ + */ + for (i = 0; i < Players.Count(); i++) { + if (!stricmp(playername, Players[i]->Name)) { + CCMessageBox().Process (TXT_NAME_MUSTBE_UNIQUE); + return(false); + } + } + + /* + ----------------------------- Check version #'s -------------------------- + */ + int v; +#ifdef PATCH + if (IsV107) { + v = 1; + } else { + v = 2; + } +#else + v = Version_Number(); +#endif + if (Games[join_index]->Game.Version > v) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + return(false); + } else { + if (Games[join_index]->Game.Version < v) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + return(false); + } + } + + /* + ----------------------------- Save game name ----------------------------- + */ + strcpy (MPlayerName,playername); + + /* + ----------------------- Send packet to game's owner ---------------------- + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_JOIN; + strcpy (GPacket.Name, MPlayerName); + GPacket.PlayerInfo.House = house; + GPacket.PlayerInfo.Color = color; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Games[join_index]->Address)); + + return(true); +} + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, int gamenow, int playernow) +{ + static int lasttime1 = 0; // time since last Game query sent out + static int lasttime2 = 0; // time since last Player query sent out + + /*------------------------------------------------------------------------ + Send the game-name query if the time has expired, or we're told to do + it right now + ------------------------------------------------------------------------*/ + if ( (TickCount.Time() - lasttime1 > 120) || gamenow) { + lasttime1 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + /*------------------------------------------------------------------------ + Send the player query for the game currently clicked on, if the time has + expired and there is a currently-selected game, or we're told to do it + right now + ------------------------------------------------------------------------*/ + if ( (curgame != -1) && curgame < Games.Count() && + ((TickCount.Time() - lasttime2 > 35) || playernow) ) { + lasttime2 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_PLAYER; + strcpy (GPacket.Name, Games[curgame]->Name); + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * MPlayerHouse (from NET_CONFIRM_JOIN) * + * MPlayerColorIdx (from NET_CONFIRM_JOIN) * + * MPlayerBases (from NET_GAME_OPTIONS) * + * MPlayerTiberium (from NET_GAME_OPTIONS) * + * MPlayerGoodies (from NET_GAME_OPTIONS) * + * MPlayerGhosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) + return(EV_NONE); + + /*------------------------------------------------------------------------ + If we're joined in a game, handle the packet in a standard way; otherwise, + don't answer standard queries. + ------------------------------------------------------------------------*/ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&GPacket,&GAddress)!=0) + return(EV_NONE); + + /*------------------------------------------------------------------------ + NET_ANSWER_GAME: Another system is answering our GAME query, so add that + system to our list box if it's new. + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_ANSWER_GAME) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name)) { + found = 1; + /*............................................................... + If name was found, update the node's time stamp & IsOpen flag. + ...............................................................*/ + Games[i]->Game.LastTime = TickCount.Time(); + if (Games[i]->Game.IsOpen != GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + Games[i]->Game.IsOpen = GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + /*............................................................ + If this game has gone from closed to open, copy the responder's + address into our Game slot, since the guy responding to this + must be game owner. + ............................................................*/ + if (Games[i]->Game.IsOpen) + Games[i]->Address = GAddress; + } + break; + } + } + /*..................................................................... + name not found (or addresses are different); add it to 'Games' + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create a new node structure, fill it in, add it to 'Games' + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Game.Version = GPacket.GameInfo.Version; + who->Game.IsOpen = GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount.Time(); + Games.Add (who); + + /*.................................................................. + Create a string for "xxx's Game", leaving room for brackets around + the string if it's a closed game + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 9]; + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + gamelist->Add_Item(item); + + retcode = EV_NEW_GAME; + } + } + + /*------------------------------------------------------------------------ + NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add it + to our player list box & the Player Vector if it's new + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_ANSWER_PLAYER) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Players.Count(); i++) { + /*.................................................................. + If the address is already present, re-copy their name, color & + house into the existing entry, in case they've changed it without + our knowledge; set the 'found' flag so we won't create a new entry. + ..................................................................*/ + if (Players[i]->Address==GAddress) { + strcpy(Players[i]->Name, GPacket.Name); + Players[i]->Player.House = GPacket.PlayerInfo.House; + Players[i]->Player.Color = GPacket.PlayerInfo.Color; + playerlist->Colors[i] = MPlayerTColors[GPacket.PlayerInfo.Color]; + found = 1; + break; + } + } + /*..................................................................... + Don't add this player if he's not part of the game that's selected. + .....................................................................*/ + i = gamelist->Current_Index(); + if (Games.Count() && GPacket.PlayerInfo.NameCRC != Compute_Name_CRC(Games[i]->Name)) + found = 1; + + /* + ** Dont add this player if its really me! (hack, hack) + */ + if (!strcmp(GPacket.Name, MPlayerName)){ + found = 1; + } + + + /*..................................................................... + name not found (or address didn't match); add to player list box & Vector + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create & add a node to the Vector + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + who->Player.Color = GPacket.PlayerInfo.Color; + Players.Add (who); + + /*.................................................................. + Create & add a string to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item(item, MPlayerTColors[who->Player.Color]); + + retcode = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us as + being confirmed, and start answering queries from other systems + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (MPlayerGameName, GPacket.Name); + MPlayerHouse = GPacket.PlayerInfo.House; + MPlayerColorIdx = GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + the dialog state to its first pop-up state. Broadcast a sign-off to + tell all other systems that I'm no longer a part of any game; this way, + I'll be properly removed from their dialogs. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_REJECT_JOIN) { + if ( (*joinstate) != JOIN_REJECTED) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name,MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + MPlayerGameName[0] = 0; + + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_GAME_OPTIONS: The game owner has changed the game options & is sending + us the new values. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerCredits = GPacket.ScenarioInfo.Credits; + MPlayerBases = GPacket.ScenarioInfo.IsBases; + MPlayerTiberium = GPacket.ScenarioInfo.IsTiberium; + MPlayerGoodies = GPacket.ScenarioInfo.IsGoodies; + MPlayerGhosts = GPacket.ScenarioInfo.IsGhosties; + BuildLevel = GPacket.ScenarioInfo.BuildLevel; + MPlayerUnitCount = GPacket.ScenarioInfo.UnitCount; + Seed = GPacket.ScenarioInfo.Seed; + Special = GPacket.ScenarioInfo.Special; + Options.GameSpeed = GPacket.ScenarioInfo.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + if (Winsock.Get_Connected()){ + ScenarioIdx = GPacket.ScenarioInfo.Scenario; + }else{ + + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (GPacket.ScenarioInfo.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + } + + retcode = EV_GAME_OPTIONS; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + both the game list & player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + /*..................................................................... + Remove this name from the list of games + .....................................................................*/ + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name) && + Games[i]->Address==GAddress) { + /*............................................................... + If the system signing off is the currently-selected list + item, clear the player list since that game is no longer + forming. + ...............................................................*/ + if (i==gamelist->Current_Index()) { + Clear_Player_List (playerlist); + } + + /*............................................................... + If the system signing off was the owner of our game, mark + ourselves as rejected + ...............................................................*/ + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + + /* + ....................... Set my return code ...................... + */ + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + /* + ................. Remove game name from game list ............... + */ + Games.Delete(Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + /*..................................................................... + Remove this name from the list of players + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + /* + ..................... Name found; remove it ..................... + */ + if (Players[i]->Address==GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + Players.Delete(Players[i]); + playerlist->Flag_To_Redraw(); + + if (retcode == EV_NONE) + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + /*------------------------------------------------------------------------ + NET_GO: The game's owner is signalling us to start playing. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerMaxAhead = GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; +CCDebugString ("C&C95 - Received the 'GO' packet\n"); + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retcode = EV_MESSAGE; + } + + /*------------------------------------------------------------------------ + NET_PING: Someone is pinging me to get a response time measure (will only + happen after I've joined a game). Do nothing; the IPX Manager will handle + sending an ACK, and updating the response time measurements. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + /*------------------------------------------------------------------------ + Default case: nothing happened. (This case will be hit every time I + receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + ------------------------------------------------------------------------*/ + else { + retcode = EV_NONE; + } + + return(retcode); +} + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + //D_DIALOG_W = 281; // dialog width + int d_dialog_w = 287*factor; // dialog width + int d_dialog_h = 177*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_margin1; + int d_playerlist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + + int d_scenariolist_w = 162*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + int d_scenariolist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + +#if (GERMAN | FRENCH) + int d_reject_w = 55*factor; +#else + int d_reject_w = 45*factor; +#endif + int d_reject_h = 9*factor; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*factor; + int d_count_h = d_txt6_h; + int d_count_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + int d_level_w = 25*factor; + int d_level_h = d_txt6_h; + int d_level_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 7*factor) + 4*factor; + //int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_dialog_cx + 2*factor; + int d_credits_y = d_level_y + d_level_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//bga:100; +#else + int d_bases_w = 100*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 100*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 100*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 100*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_goodies_y + d_goodies_h + d_margin2; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_dialog_cx - d_margin2 - (d_bases_w / 2) - (d_ok_w / 2); + int d_ok_y = d_ghosts_y + d_ghosts_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + d_margin2 + (d_goodies_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_ghosts_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_UNIT_COUNT, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_reject_x, d_reject_y); +//#else + d_reject_x, d_reject_y, d_reject_w, d_reject_h); +//#endif + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init dialog values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + //ST - 12/18/2018 11:37AM + //randomize(); + //Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, + d_txt6_h); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display == REDRAW_UNIT_COUNT){ + /* + ** Wipe the background behind the unit count then reprint it + */ + LogicPage->Fill_Rect(d_count_x + d_count_w + 2*factor, + d_count_y, + d_count_x + d_count_w + 2*factor + 20, + d_count_y + 12, + 0 ); + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + display = REDRAW_NONE; + } + + + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + /* + ** Reload and draw the title page + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NETGAME_SETUP, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, + d_credits_y + 1*factor, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the messages: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Reject the currently-selected player (don't allow rejecting myself, + who will be the first entry in the list) + ------------------------------------------------------------------*/ + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + if (index == 0) { + CCMessageBox().Process (TXT_CANT_REJECT_SELF, TXT_OOPS); + display = REDRAW_ALL; + break; + } else { + if (index < 0 || index >= playerlist.Count()) { + CCMessageBox().Process (TXT_SELECT_PLAYER_REJECT,TXT_OOPS); + display = REDRAW_ALL; + break; + } + } + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 1, + &(Players[index - 1]->Address)); + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_count_x + d_count_w + 2*factor, d_count_y, + d_count_x + d_count_w + 14*factor, d_count_y + 6*factor, BLACK); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_level_x + d_level_w + 2*factor, d_level_y, + d_level_x + d_level_w + 14*factor, d_level_y + 6*factor, BLACK); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle bases: + - Clear scenario list & rebuild it with new names + - toggle bases button, change its text + - adjust the MPlayerUnitCount to reflect the new allowed range, + using the current gauge setting + - Change the unit count gauge limit & value + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + transmit = 1; + countgauge.Flag_To_Redraw(); + display = REDRAW_UNIT_COUNT; + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle ghosts/capture-the-flag + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } else { + if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } else { + if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + } + } + + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + OK: exit loop with TRUE status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 1 second (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, (unsigned long)60); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + } + } else { + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + while ( sent_so_far < message_length ){ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, MPlayerName); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0; //0xff; ST - 12/18/2018 11:37AM + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + /*.................................................................. + Add the message to our own list, since we're not in the player list + on this dialog. + ..................................................................*/ + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + + } + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), (unsigned long)2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) { + return(EV_NONE); + } + + /*------------------------------------------------------------------------ + Try to handle the packet in a standard way + ------------------------------------------------------------------------*/ + if (Process_Global_Packet(&GPacket,&GAddress) != 0) { + return(EV_NONE); + } else + + /*------------------------------------------------------------------------ + NET_QUERY_JOIN: + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_QUERY_JOIN) { + /*..................................................................... + See if this name is unique: + - If the name matches, but the address is different, reject this player + - If the name & address match, this packet must be a re-send of a + prevous request; in this case, do nothing. The other player must have + received my CONFIRM_JOIN packet (since it was sent with an ACK + required), so we can ignore this resend. + .....................................................................*/ + found = 0; + resend = 0; + for (i = 0; i < Players.Count(); i++) { + if (!strcmp(Players[i]->Name,GPacket.Name)) { + if (Players[i]->Address != GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + if (!strcmp (MPlayerName, GPacket.Name)) { + found = 1; + } + + /*..................................................................... + Reject if name is a duplicate, or if there are too many players: + .....................................................................*/ + if (found || (Players.Count() >= (MPlayerMax - 1) && !resend) ) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + } + + /*..................................................................... + If this packet is NOT a resend, accept the player. Grant him the + requested color if possible. + .....................................................................*/ + else if (!resend) { + /*.................................................................. + Add node to the Vector list + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + Players.Add (who); + + /*.................................................................. + Set player's color; if requested color isn't used, give it to him; + otherwise, give him the 1st available color. Mark the color we + give him as used. + ..................................................................*/ + if (ColorUsed[GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = GPacket.PlayerInfo.Color; + } else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (ColorUsed[i]==0) { + who->Player.Color = i; + break; + } + } + } + ColorUsed[who->Player.Color] = 1; + + /*.................................................................. + Add player name to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item (item, MPlayerTColors[who->Player.Color]); + + /*.................................................................. + Send a confirmation packet + ..................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_CONFIRM_JOIN; + strcpy(GPacket.Name,MPlayerName); + GPacket.PlayerInfo.House = who->Player.House; + GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + + retval = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + the player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Players.Count(); i++) { + /* + ....................... Name found; remove it ...................... + */ + if (!strcmp (Players[i]->Name, GPacket.Name) && + Players[i]->Address==GAddress) { + /*............................................................... + Remove from the list box + ...............................................................*/ + item = (char *)(playerlist->Get_Item(i + 1)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + /*............................................................... + Mark his color as available + ...............................................................*/ + ColorUsed[Players[i]->Player.Color] = 0; + /*............................................................... + Delete from the Vector list + ...............................................................*/ + Players.Delete(Players[i]); + break; + } + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retval = EV_MESSAGE; + } + + return(retval); +} + + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < (int)strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); +} + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_txt6_h = 6*factor+1; + int d_margin = 5*factor; + + + /*------------------------------------------------------------------------ + Draw the dialog from scratch + ------------------------------------------------------------------------*/ + if (fresh) { + Fancy_Text_Print ("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w = MAX(String_Pixel_Width(buf3), (unsigned)w); + w += (d_margin * 4); + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*factor - (w / 2); + y = 100*factor - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*factor, y + (d_margin * 2), CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf3, 160*factor, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } else { + + /*------------------------------------------------------------------------ + Just update the timeout value on the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + int pixwidth = String_Pixel_Width (buf2); + LogicPage->Fill_Rect (160*factor - (pixwidth/2) - 12, y+(d_margin*2) + d_txt6_h + d_margin, + 160*factor + (pixwidth/2) + 12, y+(d_margin*2) + d_txt6_h*2 + d_margin, + TBLACK); + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + } +} + + + + + + +/*********************************************************************************************** + * Wait_For_Focus -- Wait for game to be in focus before proceeding * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/6/97 3:23PM ST : Created * + *=============================================================================================*/ +void Wait_For_Focus (void) +{ + CountDownTimerClass focus_timer; + focus_timer.Set(5*60); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + /* + ** Process the message loop until we are in focus. + */ + if (!GameInFocus){ + CCDebugString ("C&C95 - Waiting for game to come into focus."); + do { + CCDebugString ("."); + Keyboard::Check(); + if (!focus_timer.Time()){ + CCDebugString ("C&C95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + CCDebugString ("C&C95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer.Set(5*60); + } + + }while (!GameInFocus); + CCDebugString ("\n"); + AllSurfaces.SurfacesRestored=FALSE; + } +} + + + + + +extern bool Spawn_WChat(bool can_launch); + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- Just like Net_New_Dialog but without the Dialog. For internet play * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 10:34AM ST : Created * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 120*factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + //int d_playerlist_x = 10 * factor; //off screen + int d_playerlist_x = 500*factor; //10 * factor; //off screen + int d_playerlist_y = d_dialog_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + bool player_joined = false; + CountDownTimerClass join_timer; + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String(TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + JoinEventType whahoppa; // event generated by received packets + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + + CCDebugString ("C&C95 - In new game dialog - initialising lists.\n"); + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + + sprintf(credbuf, "%d", MPlayerCredits); + old_cred = MPlayerCredits; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + //ST - 12/18/2018 11:37AM + //randomize(); + //Seed = rand(); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + Wait_For_Focus(); + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid"); + Set_Logic_Page (SeenBuff); + } + + char a_buffer [128]; + sprintf (a_buffer, "Number of players:%d", Players.Count()); + CCDebugString (a_buffer); + + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Send a bogus packet to wake up the VSS + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = (NetCommandType)50; //Invalid command + strcpy (GPacket.Name, MPlayerName); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); +#endif //VIRTUAL_SUBNET_SERVER + + + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + + /*------------------------------------------------------------------ + default: exit loop with TRUE status + ------------------------------------------------------------------*/ + default: +#ifdef VIRTUAL_SUBNET_SERVER + if (Players.Count() == InternetMaxPlayers-1){ + + +#else //VIRTUAL_SUBNET_SERVER + if (Players.Count() > 0){ +#endif // VIRTUAL_SUBNET_SERVER + +//char ddkks[128]; +//sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +//CCDebugString (ddkks); + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined){ + player_joined = true; + join_timer.Set (3*60, true); + break; + }else{ + if (join_timer.Time()) break; + } + +CCDebugString ("C&C95 - Join timer expired\n"); + + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 2 seconds (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, (unsigned long)120); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + } + break; + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + +CCDebugString ("C&C95 - Exited process loop\n"); + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + //Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), (unsigned long)2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ +CCDebugString ("C&C95 - Sending the 'GO' packet\n"); + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { +char flopbuf [128]; +sprintf (flopbuf, "Sending 'GO' packet to address %d\n", *((unsigned short*)&(Players[i]->Address))); +CCDebugString (flopbuf); + + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + +#ifdef VIRTUAL_SUBNET_SERVER +CCDebugString ("C&C95 - Creating connection to the VSS\n"); + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } +#endif //VIRTUAL_SUBNET_SERVER + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + if (rc){ + Wait_For_Focus(); + } + + return(rc); +} + + + + + + +/*********************************************************************************************** + * Net_Fake_Join_Dialog -- Like Net_Join_Dialog but with no dialogs. For Internet Play. * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: 0 = good, -1 = bad * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 11:07AM ST : Created * + *=============================================================================================*/ + +static int Net_Fake_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + int d_gamelist_x = 500*factor; //230*factor; //Off screen + //int d_gamelist_x = 230*factor; //Off screen + int d_gamelist_y = d_dialog_y + 20; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + //int d_playerlist_x = 10 * factor; //Off screen + int d_playerlist_x = 500*factor; //10 * factor; //Off screen + int d_playerlist_y = d_gamelist_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + bool ready_to_go = false; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String(TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + int parms_received; // 1 = game options received + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + /* + ----------------------------- Various Inits ------------------------------ + */ + //MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + --------------------------- Send network query --------------------------- + */ + CCDebugString ("C&C95 - About to call Send_Join_Queries.\n"); + Send_Join_Queries (game_index, 1, 0); + + Wait_For_Focus(); + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid\n"); + Set_Logic_Page (SeenBuff); + } + + + char a_buffer [128]; + sprintf (a_buffer, "C&C95 - Number of players:%d\n", Players.Count()); + CCDebugString (a_buffer); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + process = false; + rc = -1; +#if (0) + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } +#endif //(0) + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + default: + if (joinstate == JOIN_NOTHING && Games.Count()!=0){ + gamelist.Set_Selected_Index(0); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (MPlayerName, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { +CCDebugString ("C&C95 - Received 'GO' packet\n"); + + ready_to_go = true; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + +//char ddkks[128]; +//sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +//CCDebugString (ddkks); + if (ready_to_go){ // && Players.Count() == InternetMaxPlayers){ + rc = 0; + process = false; + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } +#endif //VIRTUAL_SUBNET_SERVER + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, (unsigned long)120); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + if (rc != -1){ + Wait_For_Focus(); + } + + return(rc); +} + + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/NOSEQCON.CPP b/TIBERIANDAWN/NOSEQCON.CPP new file mode 100644 index 000000000..f2458b8b7 --- /dev/null +++ b/TIBERIANDAWN/NOSEQCON.CPP @@ -0,0 +1,687 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.cpv 1.9 16 Oct 1995 16:49:38 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * NonSequencedConnClass::Receive_Packet -- adds packet to receive queue * + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::NonSequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen); +} + + +/*************************************************************************** + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::~NonSequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NonSequencedConnClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); +} + + +/*************************************************************************** + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { +// Smart_Printf( "Packet ack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendAck++; + } else { +// Smart_Printf( "Packet noack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendNoAck++; + } + return(true); + } else { +// Smart_Printf( "Packet not Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + return(false); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { +// Smart_Printf( "Bad Magic Number\n" ); + return(false); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Get_Send(i); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Received ACK for %d \n", packet->PacketID ); + send_entry->IsACK = 1; + break; + } + } + } + +//{ +// if (i == Queue->Num_Send() ) { +// Smart_Printf( "Received bad ACK for %d \n", packet->PacketID ); +// } +//} + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*--------------------------------------------------------------------- + If there's only one slot left, don't tie up the queue with this packet + ---------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { +// Smart_Printf( "Only one slot left don't tie up with DATA NOACK packet %d \n", packet->PacketID ); + return(false); + } + + /*--------------------------------------------------------------------- + Error if we can't queue the packet + ---------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "Can't Queue the packet %d \n", packet->PacketID ); + return(false); + } + +// Smart_Printf( "Queued DATA NOACK for %d \n", packet->PacketID ); + NumRecNoAck++; + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Looking at ID %d, LastSeqID=%d \n", packet->PacketID, LastSeqID ); + /*.................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + ....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { +// Smart_Printf( "Older than oldest\n" ); + save_packet = 0; + } + /*.................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + ....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { +// Smart_Printf( "It's a resend\n" ); + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*--------------------------------------------------------------------- + Queue the packet & update our LastSeqID value. + ---------------------------------------------------------------------*/ + if (save_packet) { + /*------------------------------------------------------------------ + If there's only one slot left, make sure we only put a packet in it if + this packet will let us increment our LastSeqID; otherwise, we'll get + stuck, forever unable to increment LastSeqID. + ------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { +// Smart_Printf( "One slot left not what we looking for max=%d,num=%d \n", +// Queue->Max_Receive(), Queue->Num_Receive() ); + return(0); + } + } + + /*------------------------------------------------------------------ + If we can't queue the packet, return; don't send an ACK. + ------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "unable to queue packet\n" ); + return(0); + } + + NumRecAck++; + + /*------------------------------------------------------------------ + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ------------------------------------------------------------------*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................ + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*--------------------------------------------------------------------- + Send an ACK, regardless of whether this was a resend or not. + ---------------------------------------------------------------------*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; +// Smart_Printf( "Sending ACK for %d \n", packet->PacketID ); + Send ((char *)&ackpacket, sizeof(CommHeaderType)); + + return(true); + + } else { +// Smart_Printf( "invalid packet type %d \n", packet->Code ); + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + } + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ------------------------- Get this queue entry ------------------------ + */ + send_entry = Queue->Get_Send(i); + /* + ---------------- If ACK has been received, unqueue it ----------------- + */ + if (send_entry->IsACK) { + /* + ................ Update this queue's response time ................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + /* + ....................... unqueue the packet ......................... + */ + Queue->UnQueue_Send(NULL,NULL,i); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ +#if(0) +{ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (send_entry->SendCount) { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Resending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Resending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } else { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Sending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Sending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } +} +#endif + Send (send_entry->Buffer, send_entry->BufLen); + + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { +// Smart_Printf( "Max Retries!!! %d !!! \n", MaxRetries ); + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { +// Smart_Printf( "Timed out!!! Time %d, Timeout %d, buflen %d !!! \n", +// (send_entry->LastTime - send_entry->FirstTime), Timeout, +// send_entry->BufLen ); + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { +// Smart_Printf( "Connection going bad!!! \n" ); + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } else { + if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } + } + } + } + + return(true); +} + + diff --git a/TIBERIANDAWN/NOSEQCON.H b/TIBERIANDAWN/NOSEQCON.H new file mode 100644 index 000000000..22134cab6 --- /dev/null +++ b/TIBERIANDAWN/NOSEQCON.H @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.h_v 1.12 16 Oct 1995 16:46:22 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NOSEQCON.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Non-Sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * however, the performance is better than the Sequenced approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NONSEQCONN_H +#define NONSEQCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "combuf.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class NonSequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NonSequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~NonSequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; +}; + +#endif + + diff --git a/TIBERIANDAWN/NULLCONN.CPP b/TIBERIANDAWN/NULLCONN.CPP new file mode 100644 index 000000000..b1f572e4e --- /dev/null +++ b/TIBERIANDAWN/NULLCONN.CPP @@ -0,0 +1,267 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\nullconn.cpv 1.10 16 Oct 1995 16:51:36 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : April 20, 1995 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemConnClass::NullModemConnClass -- class constructor * + * NullModemConnClass::~NullModemConnClass -- class destructor * + * NullModemConnClass::Init -- hardware-dependent initialization * + * NullModemConnClass::Send -- hardware-dependent packet sending * + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wincomm.h" +#include "tcpip.h" + +//PG_TO_FIX +#if (0) + +/*************************************************************************** + * NullModemConnClass::NullModemConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # send queue entries * + * numreceive desired # send receive entries * + * maxlen max length of application's packets * + * magicnum application-defined magic # for the packets * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::NullModemConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum) : + NonSequencedConnClass (numsend, numreceive, maxlen, magicnum, + 60, // Retry Delta Time + -1, // Max Retries (-1 means ignore this timeout parameter) + 1200) // Timeout: 20 seconds +{ + /*------------------------------------------------------------------------ + Pre-set the port value to NULL, so Send won't send until we've been Init'd + ------------------------------------------------------------------------*/ + PortHandle = NULL; + + /*------------------------------------------------------------------------ + Allocate the Send Buffer; the parent constructor has set MaxPacketLen, + so we can use it in our computation. + ------------------------------------------------------------------------*/ +// SendBuf = new char [MaxPacketLen + sizeof(int) * 3]; + SendBuf = new char [ Actual_Max_Packet() ]; + +} /* end of NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::~NullModemConnClass -- class destructor * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::~NullModemConnClass () +{ + delete [] SendBuf; + +} /* end of ~NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::Init -- hardware-dependent initialization * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NullModemConnClass::Init (HANDLE port_handle) +{ + NonSequencedConnClass::Init(); + PortHandle = port_handle; + +} /* end of Init */ + + +/*************************************************************************** + * NullModemConnClass::Send -- hardware-dependent packet sending * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 1 = OK, 0 = error * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Send (char *buf, int buflen) +{ + //int status; + int *ibuf; + SerialHeaderType *header; + unsigned long sendlen; + + + /*------------------------------------------------------------------------ + Error if we haven't been properly initialized + ------------------------------------------------------------------------*/ + if ( PortHandle == NULL ) + return(false); + + /*------------------------------------------------------------------------ + Package the data into the Send Buffer + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *) SendBuf; + header->MagicNumber = PACKET_SERIAL_START; + header->Length = (short) buflen; + header->MagicNumber2 = PACKET_SERIAL_VERIFY; + + sendlen = sizeof( SerialHeaderType ); + memcpy (SendBuf + sendlen, buf, buflen); + sendlen += buflen; + ibuf = (int *)(SendBuf + sendlen); + *ibuf = Compute_CRC( buf, buflen ); + sendlen += sizeof( int ); + + *(SendBuf + sendlen) = '\r'; + sendlen += 1; + + /*------------------------------------------------------------------------ + Send the data + ------------------------------------------------------------------------*/ + //status = +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected() || GameToPlay == GAME_INTERNET){ + Winsock.Write(SendBuf, (int)sendlen); + }else{ + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); + } +#else + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); +#endif //WINSOCK + + //if ( status == ASSUCCESS ) { + return(true); + //} else { +// Smart_Printf( "Write Buffer status %d, Port->status %d, sendlen %d \n", status, Port->status, sendlen ); + // return(false); + //} +} + + +/*************************************************************************** + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * * + * INPUT: * + * buf buffer to compute CRC for * + * buflen length of buffer in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Compute_CRC (char *buf, int buflen) +{ + unsigned int sum, hibit; + + sum = 0; + for (int i = 0; i < buflen; i++) { + if ( sum & 0x80000000 ) { // check hi bit to rotate into low bit + hibit = 1; + } else { + hibit = 0; + } + + sum <<= 1; + sum += (hibit + (unsigned char)buf[i]); + } + + return((int)sum); +} + + +/*************************************************************************** + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * number of bytes used for communications only. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/20/1995 DRD : Created. * + *=========================================================================*/ +int NullModemConnClass::Packet_Overhead_Size ( void ) +{ + // + // short for Null Modem Magic Number + // short for Null Modem length of packet + // int for Null Modem CRC check + // CommHeaderType for Queued packets + // + + return( (PACKET_SERIAL_OVERHEAD_SIZE + sizeof(CommHeaderType)) ); + +} /* end of Packet_Overhead_Size */ + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/NULLCONN.H b/TIBERIANDAWN/NULLCONN.H new file mode 100644 index 000000000..44bc74f9a --- /dev/null +++ b/TIBERIANDAWN/NULLCONN.H @@ -0,0 +1,137 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\nullconn.h_v 1.12 16 Oct 1995 16:45:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for a NULL-Modem connection. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the non-sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Recieve_Queue from * + * NonSequencedConnClass. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLCONN_H +#define NULLCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "noseqcon.h" +#include "commlib.h" + +/* +********************************** Defines ********************************** +*/ +#define PACKET_SERIAL_START 0xDABD +#define PACKET_SERIAL_VERIFY 0xDEAF + +#define PACKET_SERIAL_OVERHEAD_SIZE (sizeof( SerialHeaderType ) + sizeof( SerialCRCType )) + +typedef struct { + unsigned short MagicNumber; + unsigned short Length; + unsigned short MagicNumber2; +} SerialHeaderType; + +typedef struct { + int SerialCRC; +} SerialCRCType; + + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemConnClass : public NonSequencedConnClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NullModemConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum); + virtual ~NullModemConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + void Init (HANDLE port_handle); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned long Actual_Max_Packet (void) { return (MaxPacketLen + (sizeof(SerialHeaderType)) + sizeof(int) + sizeof (char)); } + + /*..................................................................... + This routine computes a CRC value for the given buffer. + .....................................................................*/ + static int Compute_CRC(char *buf, int buflen); + + /*..................................................................... + This routine returns the number of bytes extra added the packet + for communication. + .....................................................................*/ + static int Packet_Overhead_Size( void ); + + /* + --------------------------- Private Interface ---------------------------- + */ + protected: + /*..................................................................... + This routine actually performs a hardware-dependent data send. + .....................................................................*/ + int Send (char *buf, int buflen); + + /*..................................................................... + This is the PORT value used by the GreenLeaf calls. + .....................................................................*/ + HANDLE PortHandle; + PORT *Port; + + /*..................................................................... + This buffer is a staging area for data sent out; it includes the + packet sent by the parent class (which includes the application's + packet, plus the CommHeaderType header), plus: + - 2-byte buffer start ID + - 2-byte length + - 4-byte CRC value (at the end of the buffer) + This is the actual packet that gets sent across the serial line. + .....................................................................*/ + char *SendBuf; +}; + +#endif + +/************************** end of nullconn.h ******************************/ + diff --git a/TIBERIANDAWN/NULLDLG.CPP b/TIBERIANDAWN/NULLDLG.CPP new file mode 100644 index 000000000..7d72104c5 --- /dev/null +++ b/TIBERIANDAWN/NULLDLG.CPP @@ -0,0 +1,8360 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\nulldlg.cpv 1.9 16 Oct 1995 16:52:12 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : April 29, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Null_Modem -- Initializes Null Modem communications * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * Reconnect_Null_Modem -- allows user to reconnect * + * Destroy_Null_Connection -- destroys the given connection * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Init_String_Compare -- for qsort * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Phone_Compare -- for qsort * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "wincomm.h" +#include "modemreg.h" +#include "tcpip.h" + +//PG_TO_FIX +#if (0) +ModemRegistryEntryClass *ModemRegistry = NULL; //Ptr to modem registry data + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +unsigned char TheirColor; +HousesType TheirHouse; +unsigned char TheirID; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 0, 8, 1, + settings->HardwareFlowControl ) ) { + + + return(true); + } else { + return(false); + } +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!PlaybackGame) { + if (GameToPlay == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!PlaybackGame) { + /*------------------------------------------------------------------------ + Send a sign-off packet + ------------------------------------------------------------------------*/ + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount.Time(); + while( (TickCount.Time() - starttime) < 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200*factor, width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (320*factor - width) / 2; + y = (200*factor - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 *factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()){} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); + + + /*------------------------------------------------------------------------ + Check for a packet. If we detect one, the other system has already been + started. Wait 1/2 sec for him to receive my ACK, then exit with success. + Note: The initial time must be a little longer than the resend delay. + Just in case we just missed the packet. + ------------------------------------------------------------------------*/ + starttime = TickCount.Time(); + while ( TickCount.Time() - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /*------------------------------------------------------------------------ + Send a packet across. As long as Num_Send() is non-zero, the other system + hasn't received it yet. + ------------------------------------------------------------------------*/ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.Seed = TickCount.Time(); + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + //Smart_Printf( "Sending SERIAL_CONNECT %d, ID %d \n", SendPacket.Seed, SendPacket.ID ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received2 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + + break; + } + } + } + } + + starttime = TickCount.Time(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //Smart_Printf( "Canceled waiting for SERIAL_CONNECT\n" ); + retval = 0; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + //Smart_Printf( "No more messages to send.\n" ); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received3 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else { + + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount.Time() - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (ModemGameToPlay) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Dial Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Answer Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + + width = MAX(width, 50); + width += 40; + height += 60; + + x = (320 - width) / 2; + y = (200 - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8)) >> 1), + y + height - (FontHeight + FontYSpacing + 2) - 5); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20, y + 25, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + Show_Mouse(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + starttime = lastmsgtime = TickCount.Time(); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + + /*..................................................................... + Resend our message if it's time + .....................................................................*/ + if (TickCount.Time() - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount.Time(); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + //Smart_Printf( "Sending a SERIAL_CONNECT packet !!!!!!!!\n" ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /*..................................................................... + Check for an incoming message + .....................................................................*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received a SERIAL_CONNECT packet !!!!!!!!\n" ); + + // are we getting our own packets back?? + + if (ReceivePacket.ID == MPlayerLocalID) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /*............................................................... + OK, we got our message; now we have to make certain the other + guy gets his, so send him one with an ACK required. + ...............................................................*/ + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount.Time() - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i,j,idx; + HousesType house; + HouseClass *housep; + char txt[80]; + + + if ( MPlayerCount == 1 ) { + return; + } + + // find index for id + + idx = -1; + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + idx = i; + break; + } + } + + if (idx == -1) { + return; + } + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error == 1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), MPlayerNames[idx] ); + } + else if (error == 0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), MPlayerNames[idx] ); + } + else if (error == -1) { + NullModem.Delete_Connection(); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Null_Connection */ + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 160 *factor; // dialog width + int d_dialog_h = 94 *factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11*factor; // ht of 6-pt text + int d_margin = 7; // margin width/height + + int d_dial_w = 90 *factor; + int d_dial_h = 9 *factor; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 *factor; + int d_answer_h = 9 *factor; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 *factor; + int d_nullmodem_h = 9 *factor; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 *factor; + int d_settings_h = 9 *factor; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77 * factor}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_answer_x, d_answer_y, d_answer_w, d_answer_h); + + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_settings_x, d_settings_y, d_settings_w, d_settings_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /*........................................................................ + Read the CC.INI file to extract default serial settings, scenario numbers + & descriptions, and the phone list. + ........................................................................*/ + Read_MultiPlayer_Settings (); + + if (SerialDefaults.Port == 0 || + SerialDefaults.IRQ == -1 || + SerialDefaults.Baud == -1) { + selectsettings = true; + } else { + if ( NullModem.Detect_Port( &SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + } + + /* + ............................ Create the list ............................. + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ......................... Fill array of button ptrs ...................... + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +Debug_Smart_Print = true; + + MPlayerLocalID = 0xff; // set to invalid value + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ..................... Redraw the buttons ....................... + */ + commands->Draw_All(); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + + /* + ** Remote-connect + */ + else if ( Phone_Dialog() ) { + if (PhoneBook[CurPhoneIdx]->Settings.Port == 0) { + settings = &SerialDefaults; + } else { + settings = &(PhoneBook[CurPhoneIdx]->Settings); + } + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, PhoneBook[ CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &SerialDefaults; + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Otherwise, remote-connect; save values if we're recording + */ + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinNullModemClass; + + if ( Init_Null_Modem( &SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + ModemGameToPlay = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + ModemGameToPlay = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + CCMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &SerialDefaults ) ) { + Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (SerialDefaults.Port != 0 && + SerialDefaults.IRQ != -1 && + SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + +#if 0 + if (retval == GAME_NORMAL) { + Debug_Smart_Print = false; + } +#endif + +Debug_Smart_Print = false; + + return( retval ); +} + + + + +/*********************************************************************************************** + * Advanced_Modem_Settings -- Allows to user to set additional modem settings * + * * + * * + * * + * INPUT: current settings * + * * + * OUTPUT: modified settings * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/16/96 2:29PM ST : Created * + *=============================================================================================*/ +Advanced_Modem_Settings (SerialSettingsType *settings) +{ + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 340; // dialog width + int d_dialog_h = 170; // dialog height + int d_dialog_x = 320 - d_dialog_w/2; // dialog x-coord + int d_dialog_y = 200 - d_dialog_h/ 2; // dialog y-coord + + + int d_compression_w = 50; + int d_compression_h = 18; + int d_compression_x = d_dialog_x + d_dialog_w/2 +40; + int d_compression_y = d_dialog_y + 30; + + int d_errorcorrection_w = 50; + int d_errorcorrection_h = 18; + int d_errorcorrection_x = d_dialog_x + d_dialog_w/2 +40; + int d_errorcorrection_y = d_dialog_y + 52; + + int d_hardwareflowcontrol_w = 50; + int d_hardwareflowcontrol_h = 18; + int d_hardwareflowcontrol_x = d_dialog_x + d_dialog_w/2 +40; + int d_hardwareflowcontrol_y = d_dialog_y + 74; + + int d_default_w = 100; + int d_default_h = 18; + int d_default_x = d_dialog_x + d_dialog_w / 2 - d_default_w / 2; + int d_default_y = d_dialog_y + 110; + + int d_ok_w = 100; + int d_ok_h = 18; + int d_ok_x = d_dialog_x + d_dialog_w/2 - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - 24; + + enum { + BUTTON_COMPRESSION = 100, + BUTTON_ERROR_CORRECTION, + BUTTON_HARDWARE_FLOW_CONTROL, + BUTTON_DEFAULT, + BUTTON_OK, + }; + + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND, + } RedrawType; + + /* + ** Yes/No strings + */ + char compress_text [16]; + char correction_text [16]; + char flowcontrol_text[16]; + + + /* + ** Initialise the button text + */ + strcpy (compress_text, settings->Compression ? Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + + /* + ** Create the buttons + */ + TextButtonClass compressionbutton(BUTTON_COMPRESSION, compress_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_compression_x, d_compression_y, d_compression_w, d_compression_h); + + TextButtonClass errorcorrectionbutton(BUTTON_ERROR_CORRECTION, correction_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_errorcorrection_x, d_errorcorrection_y, d_errorcorrection_w, d_errorcorrection_h); + + TextButtonClass hardwareflowcontrolbutton(BUTTON_HARDWARE_FLOW_CONTROL, flowcontrol_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_hardwareflowcontrol_x, d_hardwareflowcontrol_y, d_hardwareflowcontrol_w, d_hardwareflowcontrol_h); + + TextButtonClass defaultbutton(BUTTON_DEFAULT, TXT_DEFAULT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass okbutton(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + + /* + ** Misc. variables. + */ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + GadgetClass *commands; // button list + + + commands = &okbutton; + defaultbutton.Add_Tail(*commands); + compressionbutton.Add_Tail(*commands); + errorcorrectionbutton.Add_Tail(*commands); + hardwareflowcontrolbutton.Add_Tail(*commands); + + + /* + ** Main process loop + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_MODEM_INITIALISATION, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_DATA_COMPRESSION, + d_compression_x - 26, d_compression_y + 2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_ERROR_CORRECTION, + d_errorcorrection_x - 26, d_errorcorrection_y +2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_HARDWARE_FLOW_CONTROL, + d_hardwareflowcontrol_x -26, d_hardwareflowcontrol_y +2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + compressionbutton.Flag_To_Redraw(); + errorcorrectionbutton.Flag_To_Redraw(); + hardwareflowcontrolbutton.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_COMPRESSION | KN_BUTTON): + settings->Compression = settings->Compression ^ 1; + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_ERROR_CORRECTION | KN_BUTTON): + settings->ErrorCorrection = settings->ErrorCorrection ^ 1; + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_HARDWARE_FLOW_CONTROL | KN_BUTTON): + settings->HardwareFlowControl = settings->HardwareFlowControl ^ 1; + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_DEFAULT | KN_BUTTON): + settings->Compression = false; + settings->ErrorCorrection = false; + settings->HardwareFlowControl = true; + + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } +} + + + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 301 *factor; // dialog width + int d_dialog_h = 200 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *factor +1; // ht of 6-pt text + int d_margin = 5 *factor; // margin width/height + +#ifdef EDIT_IRQ + int d_portlist_w = 80 *factor; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = ((PORTBUF_MAX - 1) * 6 *factor) + 4 *factor; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + + int d_irqlist_w = 80 *factor; + int d_irqlist_h = 35 *factor; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_irq_h = 9 *factor; + int d_irq_x = d_irqlist_x + 25 *factor; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + +#endif //EDIT_IRQ + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 8 + 3 *factor; + int d_initstrlist_h = 21 *factor; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor + + 35*factor + + ((d_margin + d_txt6_h) * 2) + d_margin + 4 *factor; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_initstr_h = 9 *factor; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + +#ifndef EDIT_IRQ + int d_portlist_w = 80 *factor + 80; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_initstrlist_x; + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = d_portlist_w; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x; // + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); + d_baudlist_x -= 32; + //int d_baudlist_x = d_portlist_x + d_portlist_w + 20 * factor; + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_inittype_w = 30*factor; + int d_inittype_h = 9*factor; + int d_inittype_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_inittype_w / 2); + int d_inittype_y = d_baud_y + 20*factor; + +#endif //EDIT_IRQ + + int d_add_w = 45 *factor; + int d_add_h = 9 *factor; +#ifdef FRENCH + int d_add_x = (d_dialog_cx - (d_add_w / 2))+34*factor; +#else + int d_add_x = d_dialog_cx - (d_add_w / 2); +#endif + int d_add_y = d_initstr_y - d_add_h - 3*factor; + + int d_delete_w = 45 *factor; + int d_delete_h = 9 *factor; + +#ifdef FRENCH + int d_delete_x = 14*factor + d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_y = d_initstr_y - d_add_h - 3 *factor; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6 *factor) + 3 *factor; + int d_cwaitstrlist_h = 27 *factor; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2 *factor; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_cwaitstr_h = 9 *factor; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 *factor; + int d_tone_h = 9 *factor; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 *factor; + int d_pulse_h = 9 *factor; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + + int d_save_w = 40 *factor; + int d_save_h = 9 *factor; + int d_save_x = d_dialog_x + (d_dialog_w / 5) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 2 *factor; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 4) / 5) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 2 *factor; + +#if (GERMAN | FRENCH) + int d_advanced_w = 50*factor; +#else + int d_advanced_w = 40*factor; +#endif + int d_advanced_h = 9*factor; + int d_advanced_x = d_dialog_x + ((d_dialog_w) / 2) - (d_advanced_w / 2); + int d_advanced_y = d_dialog_y + d_dialog_h - d_advanced_h - d_margin - 2 *factor; + + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_ADVANCED, + BUTTON_INITTYPE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[4] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8" + }; + + static char custom_port[10 + MODEM_NAME_MAX] = {"CUSTOM - ????"}; + +#ifdef EDIT_IRQ + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; +#endif // EDIT_IRQ + + static char modemnames[10][MODEM_NAME_MAX]; + + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char *init_types[2] = { + "Normal", + "Full", + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port +#ifdef EDIT_IRQ + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq +#endif //EDIT_IRQ + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) + int port_custom_index = 4; //index of custom entry in port list +#ifdef EDIT_IRQ + int irq_index = 1; // index of currently-selected irq (default = 3) +#endif //EDIT_IRQ + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + int firsttime = 1; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + char init_text[32]; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, + portbuf, PORTBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, + d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + +#ifdef EDIT_IRQ + EditClass irq_edt (BUTTON_IRQ, + irqbuf, IRQBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, + d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); +#endif //EDIT_IRQ + + EditClass baud_edt (BUTTON_BAUD, + baudbuf, BAUDBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + + ListClass baudlist(BUTTON_BAUDLIST, + d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass initstr_edt (BUTTON_INITSTR, + initstrbuf, INITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + + ListClass initstrlist(BUTTON_INITSTRLIST, + d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_add_x, d_add_y); +//#else + d_add_x, d_add_y, d_add_w, d_add_h); +//#endif + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_delete_x, d_delete_y); +//#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +//#endif + + EditClass cwaitstr_edt (BUTTON_CWAITSTR, + cwaitstrbuf, CWAITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, + d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tone_x, d_tone_y, d_tone_w, d_tone_h); + + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_save_x, d_save_y); +//#else + d_save_x, d_save_y, d_save_w, d_save_h); +//#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif +#if (0) + TextButtonClass inittypebutton(BUTTON_INITTYPE, init_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_inittype_x, d_inittype_y, d_inittype_w, d_inittype_h); +#endif //(0) + + TextButtonClass advancedbutton(BUTTON_ADVANCED, TXT_ADVANCED, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_advanced_x, d_advanced_y, d_advanced_w, d_advanced_h); + + /* + ----------------------------- Various Inits ------------------------------ + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + strcpy (init_text, init_types[tempsettings.Init]); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { + tempsettings.Baud = 19200; + } + + /*........................................................................ + Set the current indices + ........................................................................*/ + +#ifdef EDIT_IRQ + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //EDIT_IRQ + + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else { + if (tempsettings.Baud == 19200) { + baud_index = 1; + } else { + if (tempsettings.Baud == 28800) { + baud_index = 2; + } else { + if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } + } + } + } + + sprintf (baudbuf, "%d", tempsettings.Baud); + + /*........................................................................ + Set up the port list box & edit box + ........................................................................*/ + for (i = 0; i < 4; i++) { + portlist.Add_Item( portname[ i ] ); + } + + /* + ** Loop through the first 10 possible modem entries in the registry. Frankly, its just + ** tough luck if the user has more than 10 modems attached! + */ + if (ModemRegistry) { + delete ModemRegistry; + } + int modems_found = 0; + for (i=0 ; i<10 ; i++) { + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()) { + strncpy (modemnames[modems_found], ModemRegistry->Get_Modem_Name(), MODEM_NAME_MAX); + portlist.Add_Item( modemnames [modems_found++] ); + port_custom_index ++; + } + delete ModemRegistry; + } + ModemRegistry = NULL; + + portlist.Add_Item ( custom_port ); + + + /* + ** Work out the current port index + */ + port_index = -1; + + if (tempsettings.ModemName[0]) { + for ( i=0 ; i= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_PORT_COLON, + d_port_x - 3, d_port_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +#ifdef EDIT_IRQ + Fancy_Text_Print( TXT_IRQ_COLON, + d_irq_x - 3, d_irq_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //EDIT_IRQ + + Fancy_Text_Print( TXT_BAUD_COLON, + d_baud_x - 3, d_baud_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_INIT_STRING, + d_initstr_x, d_initstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_CWAIT_STRING, + d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#if (0) + Fancy_Text_Print ( "Modem Init", + d_inittype_x, d_inittype_y - d_txt6_h - d_margin, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //(0) + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif // EDIT_IRQ + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); + //inittypebutton.Flag_To_Redraw(); + advancedbutton.Flag_To_Redraw(); + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { +#if (0) + case (BUTTON_INITTYPE | KN_BUTTON): + tempsettings.Init = !tempsettings.Init; + strcpy (init_text, init_types[tempsettings.Init]); + inittypebutton.Flag_To_Redraw(); + break; + + +#endif //(0) + + case (BUTTON_ADVANCED | KN_BUTTON): + Advanced_Modem_Settings (&tempsettings); + display = REDRAW_ALL; + break; + + + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //EDIT_IRQ + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + } + else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: + if (portbuf[3] <= '9' && portbuf[3] >'0') { + portbuf[4] = 0; + port_index = port_custom_index; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + break; + } + CCMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + +#ifdef EDIT_IRQ + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //EDIT_IRQ + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Clear_Focus(); + + // auto select the irq for port + +#ifdef EDIT_IRQ + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //EDIT_IRQ + } else { + if (port_index == port_custom_index) { + /* + ** This is the custom entry + */ + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + }else{ + /* + ** Must be a modem name entry so just copy iy + */ + strncpy (portbuf, item, PORTBUF_MAX); + } + + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + display = REDRAW_BUTTONS; + } else { + if (port_index < port_custom_index) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + } + break; + +#ifdef EDIT_IRQ + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else { + if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; +#endif //EDIT_IRQ + + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Add a new InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + if (item == InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( InitStrings.Count() && initstr_index != -1) { + InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else { + if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + tempsettings.ModemName[0] = 0; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + tempsettings.ModemName[0] = 0; + break; + + default: + if (port_index == port_custom_index) { + strncpy ( tempsettings.ModemName, portbuf, MODEM_NAME_MAX ); + tempsettings.Port = 1; + } else { + /* + ** Must be a modem name index + */ + strcpy (tempsettings.ModemName, portlist.Current_Item()); + tempsettings.Port = 1; + } + break; + } + +#ifdef EDIT_IRQ + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //EDIT_IRQ + + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + } + else if (dpstatus == PORT_INVALID) { + CCMessageBox().Process( TXT_INVALID_SETTINGS ); + firsttime = 1; + display = REDRAW_ALL; + } + else if (dpstatus == PORT_IRQ_INUSE) { + CCMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = 1; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&InitStrings[0]), InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else { + if (curidx >= list->Count() ) { + curidx = 0; + } + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ House: [GDI] [NOD] ³ * + * ³ Credits: ______ Desired Color: [ ][ ][ ][ ] ³ * + * ³ Opponent: Name ³ * + * ³ ³ * + * ³ Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +#define TXT_HOST_INTERNET_GAME 4567+1 +#define TXT_JOIN_INTERNET_GAME 4567+2 +int Com_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 290*factor; // dialog width + int d_dialog_h = 190*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_x + 108*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_name_x; + int d_credits_y = d_name_y + d_name_h + d_margin2; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx + (d_dialog_w / 4); + int d_gdi_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + (d_margin1 / 2); + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_x = d_name_x; + int d_opponent_y = d_color_y + d_color_h + d_margin2; + + int d_scenariolist_w = 182*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_cx - (d_scenariolist_w / 2); + int d_scenariolist_y = d_opponent_y + d_txt6_h + 3*factor + d_txt6_h; + + // d_count_x is calculated below after other enums + int d_count_w = 25*factor; + int d_count_h = 7*factor; + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + // d_level_x is calculated below after other enums + int d_level_w = 25*factor; + int d_level_h = 7*factor; + int d_level_y = d_count_y; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//BGA:100; +#else + int d_bases_w = 110*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_count_y + d_count_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 110*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_bases_y; + + int d_count_x = d_dialog_cx - d_count_w - ((2 * 6*factor) + 3*factor) + - ((d_bases_w - ((13 * 6*factor) + 3*factor + d_count_w)) / 2) - d_margin2; + + int d_level_x = d_dialog_cx + (11 * 6*factor) + + ((d_goodies_w - ((13 * 6*factor) + 3*factor + d_level_w)) / 2) + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 110*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 110*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_tiberium_y; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_tiberium_x + (d_tiberium_w / 2) - (d_ok_w / 2); + int d_ok_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_ghosts_x + (d_ghosts_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CREDITS, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static int first_time = 1; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + bool ready_to_go = false; + CountDownTimerClass ready_time; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_HOST_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, d_credits_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 3*factor, d_count_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 3*factor, d_level_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect (d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_opponent_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print (TXT_OPPONENT_COLON, d_opponent_x - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_opponent_x, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_count_x + d_count_w + 3*factor, + d_count_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 3*factor, + d_level_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + if (!ready_to_go){ + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + display = REDRAW_COLORS; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (!ready_to_go){ + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + case (BUTTON_NOD | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + if (!ready_to_go){ + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx && !ready_to_go) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + if (!ready_to_go){ + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + if (!ready_to_go){ + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle bases + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + strcpy (MPlayerName, namebuf); + transmit = 1; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle ghosts + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!ready_to_go){ + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + else if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + else if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + if (!ready_to_go){ + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + //rc = true; + //process = false; + + // force transmitting of game options packet one last time + + + + SendPacket.Command = SERIAL_READY_TO_GO; + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + ready_to_go = true; + ready_time.Set(120, true); + + transmit = 1; + transmittime = 0; + + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (!ready_to_go){ + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + } + case (BUTTON_CANCEL | KN_BUTTON): + if (!ready_to_go){ + process = false; + rc = false; + } + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (ready_to_go) break; + + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = SendPacket.Message; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + + } /* end of input processing */ + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + while (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} + +// display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): +// Smart_Printf( "received sign off\n" ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): +// Smart_Printf( "received game options\n" ); + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): +// Smart_Printf( "received serial message\n" ); + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + break; + + // + // get their response time + // + case (SERIAL_TIMING): +// Smart_Printf( "received timing\n" ); + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = 1; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: +// Smart_Printf( "received unknown command %X\n", ReceivePacket.Command ); + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + /* + ** If user has clicked 'GO' and the timeout has elapsed then quit the loop + */ + if ( ready_to_go && ready_time.Time() == 0 ){ + rc = 1; + process = false; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); +char flip[128]; +sprintf (flip, "C&C95 - MaxAhead set to %d frames\n", MPlayerMaxAhead); +CCDebugString (flip); + + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 306*factor; // dialog width + int d_dialog_h = 187*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + d_margin2; + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_y = d_color_y + d_color_h + d_margin1; + int d_scenario_y = d_opponent_y + d_txt6_h; + int d_credits_y = d_scenario_y + d_txt6_h; + int d_count_y = d_credits_y + d_txt6_h; + int d_level_y = d_count_y + d_txt6_h; + int d_bases_y = d_level_y + d_txt6_h; + int d_goodies_y = d_bases_y + d_txt6_h; + int d_tiberium_y = d_goodies_y + d_txt6_h; + int d_ghosts_y = d_tiberium_y + d_txt6_h; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int cbox_x[] = { d_dialog_cx, + d_dialog_cx + d_color_w, + d_dialog_cx + (d_color_w * 2), + d_dialog_cx + (d_color_w * 3), + d_dialog_cx + (d_color_w * 4), + d_dialog_cx + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + bool ready_to_go = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + transmit = 1; + first = 1; + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_JOIN_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_ghosts_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Opponent's name + ............................................................*/ + Fancy_Text_Print (TXT_OPPONENT_COLON, d_dialog_cx - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_dialog_cx, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Scenario description + ............................................................*/ + Fancy_Text_Print (TXT_SCENARIO_COLON, d_dialog_cx - 3*factor, + d_scenario_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (ScenarioIdx != -1) { + sprintf(txt,"%s", MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + strcpy(txt,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, RED, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /*............................................................ + Credits + ............................................................*/ + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_dialog_cx - 3*factor, + d_credits_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf(txt,"%d",MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_credits_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Count + ............................................................*/ + + Fancy_Text_Print (TXT_COUNT, d_dialog_cx - 3*factor, + d_count_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_dialog_cx, + d_count_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Level + ............................................................*/ + + Fancy_Text_Print (TXT_LEVEL, d_dialog_cx - 3*factor, + d_level_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_level_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases status + ............................................................*/ + Fancy_Text_Print (TXT_BASES_COLON, d_dialog_cx - 3*factor, + d_bases_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerBases) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_bases_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium status + ............................................................*/ + Fancy_Text_Print (TXT_TIBERIUM_COLON, d_dialog_cx - 3*factor, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerTiberium) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goodies status + ............................................................*/ + Fancy_Text_Print (TXT_CRATES_COLON, d_dialog_cx - 3*factor, + d_goodies_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGoodies) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_goodies_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Capture the flag or AI player ON/OFF + ............................................................*/ + if ( Special.IsCaptureTheFlag ) { + strcpy( txt, Text_String( TXT_CAPTURE_THE_FLAG ) ); + strcat( txt, ":" ); + Fancy_Text_Print (txt, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + strcpy(txt,Text_String(TXT_ON)); + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Ghost player status + ............................................................*/ + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGhosts) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + if (!ready_to_go){ + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + changed = 1; + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (MPlayerPrefColor == TheirColor) + break; + } + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + strcpy (MPlayerName, namebuf); + transmit = 1; + } + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + case (BUTTON_NOD | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (!ready_to_go){ + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (!ready_to_go){ + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + } + case (BUTTON_CANCEL | KN_BUTTON): + if (!ready_to_go){ + process = false; + rc = false; + } + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!ready_to_go){ + + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + display = REDRAW_MESSAGE; + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + display = REDRAW_MESSAGE; + } + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + + /* + ** Once the host is ready to go, we can no longer change game options. + */ + case SERIAL_READY_TO_GO: + ready_to_go = true; + break; + + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = REDRAW_MESSAGE; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (MPlayerColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + + MPlayerColorIdx = TheirColor + 1; + if (MPlayerColorIdx >= 6) + MPlayerColorIdx = 0; + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); +char flip[128]; +sprintf (flip, "C&C95 - MaxAhead set to %d frames\n", MPlayerMaxAhead); +CCDebugString (flip); + + process = false; + rc = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + //if ( ((TickCount.Time() - lastmsgtime) > msg_timeout) || + //(Winsock.Get_Connected() && Winsock.Get_Connection_Status == TcpipManagerClass::CONNECTION_LOST)) { + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 *factor; // dialog width + int d_dialog_h = 150 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor- d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_phonelist_w = 268 *factor; + int d_phonelist_h = 87 *factor; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11*factor; + + int d_add_w = 45*factor; + int d_add_h = 9*factor; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45*factor; + int d_edit_h = 9*factor; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45*factor; + int d_delete_h = 9*factor; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6*factor) + 3*factor; + int d_numedit_h = 9*factor; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45*factor; + int d_dial_h = 9*factor; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*factor, 207*factor}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + int changed = 0; // 1 = save changes to INI file + int firsttime = 0; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ListClass phonelist(BUTTON_PHONELIST, + d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_add_x-4, d_add_y); +//#else + d_add_x, d_add_y, d_add_w, d_add_h); +//#endif + + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, d_edit_h); + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_delete_x, d_delete_y); +//#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +//#endif + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + EditClass numedit (BUTTON_NUMEDIT, + phone_num, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + firsttime = 1; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + phonelist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + if (phonelist.Current_Index() != CurPhoneIdx) { + CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = 1; + } + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*PhoneBook[CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*PhoneBook[CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (PhoneBook[CurPhoneIdx] == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + PhoneBook.Delete (CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = 1; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (CurPhoneIdx == -1 || + strcmp( PhoneBook[CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = false; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + } + } + changed = 1; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&PhoneBook[0]), PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, PhoneBook[i]->Number ); + } else { + strncpy( phonenum, PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || CurPhoneIdx < -1) { + CurPhoneIdx = -1; + } else { + if (CurPhoneIdx >= list->Count() ) { + CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (CurPhoneIdx > -1) { + strcpy (buf, PhoneBook[CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 *factor; // dialog width + int d_dialog_h = 105*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2); // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) + 3 *factor; + int d_name_h = 9 *factor; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 *factor; + int d_name_y = d_dialog_y + 25 *factor; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) + 3 *factor; + int d_number_h = 9 *factor; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 *factor; + int d_number_y = d_name_y + d_name_h + d_margin; + +#if (GERMAN | FRENCH) + int d_default_w = 130 *factor; +#else + int d_default_w = 104 *factor; +#endif + int d_default_h = 9 *factor; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + +#if (GERMAN | FRENCH) + int d_custom_w = 130 *factor; +#else + int d_custom_w = 100 *factor; +#endif + int d_custom_h = 9 *factor; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + +#if (GERMAN | FRENCH) + int d_save_w = 55 *factor; +#else + int d_save_w = 45 *factor; +#endif + int d_save_h = 9 *factor; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 55 *factor; +#else + int d_cancel_w = 45 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + int custom = 0; + int firsttime = 1; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit (BUTTON_NAME, + namebuf, PhoneEntryClass::PHONE_MAX_NAME, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + EditClass numedit (BUTTON_NUMBER, + numbuf, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_custom_x, d_custom_y, d_custom_w, d_custom_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_save_x, d_save_y, d_save_w, d_save_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print (TXT_NAME_COLON, + d_name_x - 5, d_name_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_NUMBER_COLON, + d_number_x - 5, d_number_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = 0; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = 1; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + default: + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + + } + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + ModemService = true; + return( connected ); + } + + + /* + ** Completely disable audio. This is required for MWave devices + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + CCMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + CCMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + ModemService = false; + CCMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } + + ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + default: + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + ModemService = true; + return( connected ); + } + + /* + ** Completely disable audio. This is required for MWave devices + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + CCMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + CCMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + CCMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } + + ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } else { + //Smart_Printf( "Echo buffer full!!!\n" ); + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (Special.IsMonoEnabled) { +// Mono_Set_Cursor(0,0); + Mono_Printf("%s",buf); + } else { +// Mono_Printf("%s",buf); + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if (0) + +int Com_Fake_Scenario_Dialog(void) +{ + bool display = true; // redraw level + bool process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int parms_received = 1; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + process = true; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + //Seed = rand(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + transmit = 1; + }else{ + process = false; + rc = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + ////////MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + + + + + + + + + + + + + + + + + + + + + + + + + +int Com_Show_Fake_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog variables + ........................................................................*/ + bool display = true; // redraw level + BOOL process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + + + transmit = 1; + first = 1; + + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = false; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = false; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + ////////MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); + + process = false; + rc = true; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = false; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +#endif //(0) + + +#endif //(0) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (Special.IsMonoEnabled) { +// Mono_Set_Cursor(0,0); + Mono_Printf("%s",buf); + } else { +// Mono_Printf("%s",buf); + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + diff --git a/TIBERIANDAWN/NULLMGR.CPP b/TIBERIANDAWN/NULLMGR.CPP new file mode 100644 index 000000000..d6aef4f90 --- /dev/null +++ b/TIBERIANDAWN/NULLMGR.CPP @@ -0,0 +1,2350 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\nullmgr.cpv 1.10 16 Oct 1995 16:51:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : May 1, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemClass::NullModemClass -- class constructor * + * NullModemClass::~NullModemClass -- class destructor * + * NullModemClass::Init -- initialization * + * NullModemClass::Send_Message -- sends a message * + * NullModemClass::Get_Message -- polls the Queue for a message * + * NullModemClass::Service -- main polling routine * + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * NullModemClass::Reset_Response_Time -- Resets response time computatio* + * NullModemClass::Oldest_Send -- Returns ptr to oldest unACK'd send buf * + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * NullModemClass::Dial_Modem -- dials a number passed * + * NullModemClass::Answer_Modem -- waits for call and answers * + * NullModemClass::Hangup_Modem -- hangs up the modem * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wincomm.h" +#include "modemreg.h" +//#include "i86.h" +#include "tcpip.h" + +//PG_TO_FIX +#if (0) + +extern ModemRegistryEntryClass *ModemRegistry; + +// the following line was taken from Greenleaf's +// because of other define conflicts + +#define ESC 27 +#define NOKEY 0xffff +#define INIT_COMMAND_RETRIES 2 + +// this time is in milliseconds + +#define DEFAULT_TIMEOUT 2000 + +// +// the following is for a fix around a greenleaf bug +// where they do not check for the value of abortkey +// to determine whether or not they call the abort modem function. +// +extern "C" { + extern void (*_AbortModemFunctionPtr)(int); +} + +static void (*NullModemClass::OrigAbortModemFunc)(int); + +static KeyNumType NullModemClass::Input; +static GadgetClass *NullModemClass::Commands; // button list + +/* +** Ugly hack: this string stores the string received from the modem +*/ +char ModemRXString[80]; + +/*************************************************************************** + * NullModemClass::NullModemClass -- class constructor * + * * + * INPUT: * + * numsend # desired entries for the send queue * + * numreceive # desired entries for the receive queue * + * maxlen application's max packet length * + * magicnum application-specific magic # (so we don't * + * accidentally end up talking to another one of our own * + * products using the same protocol) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::NullModemClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum) : ConnManClass() +{ + /*------------------------------------------------------------------------ + Init Port to NULL; we haven't opened Greenleaf yet. + ------------------------------------------------------------------------*/ + PortHandle = NULL; + Connection = NULL; + + NumSend = numsend; + NumReceive = numreceive; + MaxLen = maxlen; + MagicNum = magicnum; + + RXBuf = 0; + BuildBuf = 0; + + EchoSize = 500; + EchoBuf = 0; + + OldIRQPri = -1; + + ModemVerboseOn = false; // default true + ModemEchoOn = false; // default true + ModemWaitCarrier = 50000; // default 50 * 1000ms = 50 secs + ModemCarrierDetect = 600; // default 6 * 100ms = .6 secs + ModemCarrierLoss = 1400; // default 14 * 100ms = 1.4 secs + ModemHangupDelay = 20000; // default 20 * 1000ms = 20 secs + ModemGuardTime = 1000; // default 50 * 20ms = 1 sec + ModemEscapeCode = '+'; // default ASCII 43 + + SendOverflows = 0; + ReceiveOverflows = 0; + CRCErrors = 0; + + NumConnections = 0; + + /*........................................................................ + Init timing parameters + ........................................................................*/ + RetryDelta = 60; // 60 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 1200; // report bad connection after 20 seconds + +} /* end of NullModemClass */ + + +/*************************************************************************** + * NullModemClass::~NullModemClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::~NullModemClass () +{ + Delete_Connection(); +} /* end of ~NullModemClass */ + + +/*************************************************************************** + * NullModemClass::Init -- initialization * + * * + * INPUT: * + * port address * + * irq 2-15 * + * baud 300, 1200, 9600, etc * + * parity 'O' (odd), 'E' (even), 'N' (none), 'S' (space), * + * 'M' (mark) * + * wordlength 5, 6, 7, or 8 * + * stopbits 1 or 2 * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +//int NullModemClass::Init (int port, int irq, int baud, char parity, int wordlen, int stopbits) +#pragma off (unreferenced) +int NullModemClass::Init (int port, int ,char *dev_name, int baud, char parity, int wordlen, int stopbits, int flowcontrol) +{ + int com; + //int irqnum; + //int address; + //int status; + + + if (PortHandle) { + CloseHandle(PortHandle); + PortHandle = NULL; + } + + if (!Connection){ + /*------------------------------------------------------------------------ + Init our Connection + ------------------------------------------------------------------------*/ + Connection = new NullModemConnClass (NumSend, NumReceive, MaxLen, + MagicNum); + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + + /*------------------------------------------------------------------------ + Allocate our packet parsing buffer; make it the same # of packets as the + # of receive queue entries the application has requested. Use the + "Actual" maximum packet size, given from the connection; this allows for + both headers that get added to the packet. + ------------------------------------------------------------------------*/ + RXSize = Connection->Actual_Max_Packet() * NumReceive; + RXBuf = new char [RXSize]; + + BuildBuf = new char [MaxLen]; + + EchoBuf = new char [ EchoSize ]; + } + + RXCount = 0; + EchoCount = 0; + + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch (port) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + } + + /* + ** Shift up the baud rate to sensible values + */ +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + char *device; + + switch ( port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = dev_name; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++ ){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (dev_name, ModemRegistry->Get_Modem_Name() )){ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + device = NULL; + } + + /* + ** Open the com port + */ + PortHandle = SerialPort->Serial_Port_Open (device, baud, parity, wordlen, stopbits, flowcontrol); + if (PortHandle == INVALID_HANDLE_VALUE) { + Shutdown(); + return(false); + } + + /*------------------------------------------------------------------------ + Init the Connection + ------------------------------------------------------------------------*/ + Connection->Init(PortHandle); + + NumConnections = 1; + + return(true); +} + + +/*********************************************************************************************** + * NMC::Num_Connections -- returns NumConnections member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: NumConnections * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Num_Connections( void ) +{ + return(NumConnections); +} + + + +/*********************************************************************************************** + * NMC::Delete_Connection -- removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Delete_Connection( void ) +{ + if (Connection) { + delete Connection; + Connection = NULL; + } + + if (RXBuf) { + delete [] RXBuf; + RXBuf = NULL; + } + + if (BuildBuf) { + delete [] BuildBuf; + BuildBuf = NULL; + } + + if (EchoBuf) { + delete [] EchoBuf; + EchoBuf = NULL; + } + + NumConnections = 0; + + return( true ); +} /* end of Delete_Connection */ + + +/*********************************************************************************************** + * NMC::Init_Send_Queue -- Initialises the connections send queue * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:46AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Init_Send_Queue( void ) +{ + + /*--------------------------------------------------------------- + Init the send queue + -----------------------------------------------------------------*/ + if ( Connection ) { + Connection->Queue->Init_Send_Queue(); + } + + return(true); +} + + +//DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings ) +/*********************************************************************************************** + * NMC::Detect_Port -- Checks that the specified com port exists * + * * + * * + * * + * INPUT: ptr to SerialSettingsType * + * * + * OUTPUT: true if port is valid * + * * + * HISTORY: * + * 8/2/96 11:47AM ST : Documented / Win32 support * + *=============================================================================================*/ +DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings) +{ + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + }else{ + SerialPort->Serial_Port_Close(); + } + + /* + ** Shift up the baud rate to sensible values + */ + int baud = settings->Baud; +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + + /* + ** Translate the port address into a usable device name + */ + char *device; + + switch ( settings->Port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = settings->ModemName; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (device, ModemRegistry->Get_Modem_Name() )){ + /* + ** Got a match. Break out leaving the registry info intact. + */ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + return (PORT_INVALID); + } + + /* + ** Open the com port + */ + HANDLE porthandle = SerialPort->Serial_Port_Open (device, baud, 0, 8, 1, settings->HardwareFlowControl); + + if (porthandle == INVALID_HANDLE_VALUE){ + return (PORT_INVALID); + } + + SerialPort->Serial_Port_Close(); + return (PORT_VALID); + +} + + + + +/*********************************************************************************************** + * NullModemClass::ShutDown -- Closes serial port and removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:43AM ST : Documented / Win32 support * + *=============================================================================================*/ +void NullModemClass::Shutdown ( void ) +{ + if (PortHandle && SerialPort) { + SerialPort->Serial_Port_Close(); + delete SerialPort; + SerialPort = NULL; + PortHandle = NULL; + Delete_Connection(); + } + +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Delete_Connection(); + } +#endif + +} /* end of Shutdown */ + + +/*************************************************************************** + * NullModemClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/07/1995 DRD : Created. * + *=========================================================================*/ +void NullModemClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + +} /* end of Set_Timing */ + + +/*************************************************************************** + * NullModemClass::Send_Message -- sends a message * + * * + * For clarity's sake, here's what happens to the buffer passed in: * + * - It gets passed to the Connection's Send_Packet() routine * + * - The CommHeaderType header gets tacked onto it * + * - The resulting buffer gets added to the Connection's Send Queue * + * - When Service() determines that it needs to send the data, it * + * copies the entire packet (CommHeaderType and all) into its local * + * SendBuf, adds the packet start ID, length, and CRC, then sends it out.* + * * + * The ack_req argument will almost always be '1' (the default). The only * + * reason to use 0 is if you don't know whether the other system is * + * ready or not, so you have to periodically send out a query packet, * + * and wait for a response. (Using the connection's built-in retry * + * system would just blast out useless data if the other system isn't * + * even there.) * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required; 0 = not * + * * + * OUTPUT: * + * 1 = OK; 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Send_Message (void *buf, int buflen, int ack_req) +{ + int rc; + + if (NumConnections == 0) { + return( false ); + } + + rc = Connection->Send_Packet(buf,buflen,ack_req); + if (!rc) + SendOverflows++; + + return(rc); + +} /* end of Send_Message */ + + +/*************************************************************************** + * NullModemClass::Get_Message -- polls the Queue for a message * + * * + * INPUT: * + * buf buffer to store message in * + * buflen ptr filled in with length of message * + * * + * OUTPUT: * + * 1 = message was received; 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Get_Message (void *buf, int *buflen) +{ + if (NumConnections == 0) { + return( false ); + } + return( Connection->Get_Packet( buf, buflen ) ); +} + + +/*************************************************************************** + * NullModemClass::Service -- main polling routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = connection has gone bad * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Service (void) +{ + int pos; // current position in RXBuf + int i; // loop counter + //int status; + unsigned short length; + SerialHeaderType *header; // decoded packet start, length + SerialCRCType *crc; // decoded packet CRC + char moredata = 0; + + if (NumConnections == 0) { + return( false ); + } + + /*------------------------------------------------------------------------ + First, copy all the bytes we can from the Greenleaf RX buffer to our + own buffer. + ------------------------------------------------------------------------*/ + RXCount += SerialPort->Read_From_Serial_Port((unsigned char*)(RXBuf + RXCount), int(RXSize - RXCount) ); + +// if (RXCount){ +//char port[128]; +//sprintf (port, "C&C95 - RXCount = %d bytes.\n", RXCount); +//CCDebugString (port); +// } + +BOOL enabled = FALSE; + +#if (0) +if (SerialPort->FramingErrors || + SerialPort->IOErrors || + SerialPort->InBufferOverflows || + SerialPort->BufferOverruns || + SerialPort->InBufferOverflows || + SerialPort->OutBufferOverflows){ + + +if (!MonoClass::Is_Enabled()) { +MonoClass::Enable(); +enabled = TRUE; +} +Special.IsMonoEnabled = TRUE; +Debug_Smart_Print = TRUE; +Mono_Set_Cursor(0,0); +Smart_Printf( " In Queue: %5d \n", SerialPort->InQueue); +Smart_Printf( " Out Queue: %5d \n", SerialPort->OutQueue); +Smart_Printf( " Framing errors: %5d \n", SerialPort->FramingErrors); +Smart_Printf( " IO errors: %5d \n", SerialPort->IOErrors); +Smart_Printf( " Parity Errors: %5d \n", SerialPort->InBufferOverflows); +Smart_Printf( " Buffer overruns: %5d \n", SerialPort->BufferOverruns); +Smart_Printf( " In buffer overflows: %5d \n", SerialPort->InBufferOverflows); +Smart_Printf( "Out buffer overflows: %5d \n", SerialPort->OutBufferOverflows); + +MonoClass::Disable(); +Debug_Smart_Print = FALSE; +} +#endif //(0) + + // minimum packet size + + if ( RXCount < (PACKET_SERIAL_OVERHEAD_SIZE + 1) ) { + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Now scan the buffer for the start of a packet. + ------------------------------------------------------------------------*/ + pos = -1; + for (i = 0; i <= RXCount - sizeof( short ); i++) { + if ( *((unsigned short *)(RXBuf + i)) == PACKET_SERIAL_START ) { + pos = i; + break; + } + } + + /*------------------------------------------------------------------------ + No start code was found; throw away all bytes except the last few, and + return. + ------------------------------------------------------------------------*/ + if (pos==-1) { +// Smart_Printf( "No magic number found \n" ); + /*..................................................................... + move the remaining, un-checked bytes to the start of the buffer + .....................................................................*/ + memmove (RXBuf, RXBuf + i, sizeof( short ) - 1); + RXCount = sizeof( short ) - 1; + return( Connection->Service() ); + } + + /*........................................................................ + Check to see if there are enough bytes for the header to be decoded + ........................................................................*/ + if ( (RXCount - pos) < sizeof( SerialHeaderType ) ) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + A start code was found; check the packet's length & CRC + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *)(RXBuf + pos); + + /*........................................................................ + If we lost a byte in the length, we may end up waiting a very long time + for the buffer to get to the right length; check the verify value to + make sure this didn't happen. + ........................................................................*/ + if ( header->MagicNumber2 != PACKET_SERIAL_VERIFY ) { +// Smart_Printf( "Verify failed\n"); +// Hex_Dump_Data( (RXBuf + pos), PACKET_SERIAL_OVERHEAD_SIZE ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + length = header->Length; + + /*........................................................................ + Special case: if the length comes out too long for us to process: + - Assume the packet is bad + - Throw away the bogus packet-start code + - Return; we'll search for another packet-start code next time. + ........................................................................*/ + if (length > MaxLen) { +#if (CONN_DEBUG) + printf( "length too lonnng\n" ); +#endif +// Smart_Printf( "length too lonnng %d, max %d \n", length, MaxLen ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*........................................................................ + If the entire packet isn't stored in our buffer, copy the remaining bytes + to the front of the buffer & return. + ........................................................................*/ + if ( (pos + length + PACKET_SERIAL_OVERHEAD_SIZE) > RXCount) { + + if ( moredata ) { +// Smart_Printf( "waiting for more data %d, pos = %d \n", ((length + PACKET_SERIAL_OVERHEAD_SIZE) - (RXCount - pos)), pos ); + } + + if (pos) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + } + return(Connection->Service()); + } + + /*........................................................................ + Now grab the CRC value in the packet, & compare it to the CRC value + computed from the actual data. If they don't match, throw away the bogus + start-code, move the rest to the front of the buffer, & return. + We'll continue parsing this data when we're called next time. + ........................................................................*/ + crc = (SerialCRCType *)(RXBuf + pos + sizeof( SerialHeaderType ) + length); + if (NullModemConnClass::Compute_CRC(RXBuf + pos + + sizeof( SerialHeaderType ), length) != crc->SerialCRC) { + + CRCErrors++; + +#if (CONN_DEBUG) + printf( "CRC check failed\n" ); +#endif +// Smart_Printf( "CRC check failed for packet of length %d \n", length ); + +// if (length < 100) { +// Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +// } + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + Give the new packet to the Connection to process. + ------------------------------------------------------------------------*/ + if (!Connection->Receive_Packet(RXBuf + pos + sizeof( SerialHeaderType ), length)) { + ReceiveOverflows++; +// Smart_Printf( "Received overflows %d \n", ReceiveOverflows ); + } + +#if (0) + Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +#endif + + /*------------------------------------------------------------------------ + Move all data past this packet to the front of the buffer. + ------------------------------------------------------------------------*/ + pos += (PACKET_SERIAL_OVERHEAD_SIZE + length); + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + + /*------------------------------------------------------------------------ + Now, service the connection's Queue's; this will handle ACK & Retries. + ------------------------------------------------------------------------*/ + return(Connection->Service()); + +} /* end of Service */ + + +/*************************************************************************** + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Send(void) +{ + if (Connection) + return( Connection->Queue->Num_Send() ); + else + return (0); + +} /* end of Num_Send */ + + +/*************************************************************************** + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Receive(void) +{ + if (Connection) + return( Connection->Queue->Num_Receive() ); + else + return (0); + +} /* end of Num_Receive */ + + +/*************************************************************************** + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +unsigned long NullModemClass::Response_Time(void) +{ + if (Connection) + return( Connection->Queue->Avg_Response_Time() ); + else + return (0); + +} /* end of Response_Time */ + + +/*************************************************************************** + * NullModemClass::Reset_Response_Time -- Resets response time computation * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Reset_Response_Time(void) +{ + if (Connection) + Connection->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * Oldest_Send -- Returns ptr to oldest unACK'd send buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void * NullModemClass::Oldest_Send(void) +{ + int i; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < Connection->Queue->Num_Send(); i++) { + send_entry = Connection->Queue->Get_Send(i); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + buf = send_entry->Buffer; + break; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * NullModemClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Configure_Debug(int , int offset, int size, + char **names, int maxnames) +{ + if (Connection) + Connection->Queue->Configure_Debug (offset, size, names, maxnames); +} + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Mono_Debug_Print(int, int refresh) +{ + if (!Connection) + return; + + Connection->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (31,1); + Mono_Printf ("Serial Port Queues"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (20,3); + Mono_Printf ("CRC Errors:"); + + Mono_Set_Cursor (43,2); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,3); + Mono_Printf ("Receive Overflows:"); + } + + Mono_Set_Cursor (32,2); + Mono_Printf ("%d ", Connection->Queue->Avg_Response_Time()); + + Mono_Set_Cursor (32,3); + Mono_Printf ("%d ", CRCErrors); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,3); + Mono_Printf ("%d ", ReceiveOverflows); + + Mono_Set_Cursor (2,5); + Mono_Printf ("%d ", Num_Send()); + + Mono_Set_Cursor (41,5); + Mono_Printf ("%d ", Num_Receive()); + +} /* end of Mono_Debug_Print */ + + +void Timer_Test (int line, char *file) +{ + + char abuffer [128]; + + sprintf (abuffer, "Testing timer at line %d in file %s", line, file); + CCDebugString (abuffer); + + CountDownTimerClass timer; + + timer.Set (1); + + while (timer.Time()){ + CCDebugString ("."); + } + + CCDebugString ("OK\n"); +} + + + + +/*************************************************************************** + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * -1 init string invalid * + * 0 no modem found * + * 1 modem found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +int NullModemClass::Detect_Modem( SerialSettingsType *settings, bool reconnect ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ +// enum { +// BUTTON_CANCEL = 100, +// }; + + int status; +// int modemstatus; + int value; + int error_count = 0; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + int factor = SeenBuff.Get_Width()/320; + + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_INITIALIZING_MODEM ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + HMWaitForOK( 0, NULL ); + + + + + /* + ** OK, lets not mess about any more. Just turn on echo, verbose, and result codes + ** before we even begin. At least this way when we get an error later on we have already + ** removed all the steps we use to try and recover. + ** The timeouts need to be quite small in case the modem is turned off. + */ + + /* + ** Turn on result codes. + */ + Send_Modem_Command ( "ATQ0", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Make result codes verbose. + */ + Send_Modem_Command ( "ATV1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Turn on echo. + */ + Send_Modem_Command ( "ATE1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + ModemVerboseOn = true; + ModemEchoOn = true; + + + + /* + ** Try sending a plain old AT command to the modem. Now that we have theoretically + ** turned on verbose result codes we should get an 'OK' back. + ** + */ + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 2 ); + + if (status < ASSUCCESS) { + /* + ** No 'OK' came back so return failure. We cant print an error message here because + ** the calling function may want to upshift the baud rate (eg from 14400 to 19200) and + ** try again. + */ + return (false); + } + + /* + ** Send the user supplied modem init string + */ + if (settings->InitStringIndex == -1) { + status = Send_Modem_Command( "", '\r', buffer, 81, 300, 1 ); + } else { + /* + ** Split up the init string into seperate strings if it contains one or more '|' characters. + ** This character acts as a carriage return/pause. + */ + char *istr = new char [2 + strlen( InitStrings [settings->InitStringIndex] )]; + char *tokenptr; + strcpy (istr, InitStrings [settings->InitStringIndex] ); + + /* + ** Tokenise the string and send it in chunks + */ + tokenptr = strtok ( istr, "|" ); + while ( tokenptr ) { + + status = Send_Modem_Command( tokenptr, '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + /* + ** Handle error case. + */ + if (status < ASSUCCESS) { + if (CCMessageBox().Process(TXT_ERROR_NO_INIT, TXT_IGNORE, TXT_CANCEL)) { + delete istr; + return( false ); + } + error_count++; + break; + } + + tokenptr = strtok ( NULL, "|"); + + } + } + /* + ** Use the settings from the registry to further initialise the modem + */ + if (settings->Port == 1 && ModemRegistry) { + /* + ** Send the init strings from the registry if available + */ + char send_string[256] = {"AT"}; + + if (settings->HardwareFlowControl){ + /* + ** Send the init string for hardware flow control + */ + if (ModemRegistry->Get_Modem_Hardware_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Hardware_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + /* + ** Send the init string for no flow control + */ + if (ModemRegistry->Get_Modem_No_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_No_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for data compresseion + */ + if (settings->Compression){ + + if (ModemRegistry->Get_Modem_Compression_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + + if (ModemRegistry->Get_Modem_Compression_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for error correction + */ + if (settings->ErrorCorrection){ + + if (ModemRegistry->Get_Modem_Error_Correction_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + if (ModemRegistry->Get_Modem_Error_Correction_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + } + /* + ** We require that auto-answer be disabled so turn it off now. + */ + status = Send_Modem_Command( "ATS0=0", '\r', buffer, 81, DEFAULT_TIMEOUT, INIT_COMMAND_RETRIES ); + if (status != MODEM_CMD_OK) { + if (CCMessageBox().Process(TXT_ERROR_NO_DISABLE, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; + } + + /* + ** If we had an unreasonable number of ignored errors then return failure + */ + if (error_count >= 3) { + CCMessageBox().Process(TXT_ERROR_TOO_MANY, TXT_OK); + return (false); + } + + return( true ); +} + + +/*************************************************************************** + * NullModemClass::Dial_Modem -- dials a number passed * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +DialStatusType NullModemClass::Dial_Modem( char *string, DialMethodType method, bool reconnect ) +{ + +//Timer_Test(__LINE__, __FILE__); + + + int factor = SeenBuff.Get_Width()/320; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + + //int status; + int delay; + DialStatusType dialstatus; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + Input = 0; + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + if (reconnect) { + strcpy( buffer, Text_String( TXT_MODEM_CONNERR_REDIALING ) ); + } else { + strcpy( buffer, Text_String( TXT_DIALING ) ); + } + + +//Timer_Test(__LINE__, __FILE__); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + int text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Create the list ............................. + */ + Commands = &cancelbtn; + + +//Timer_Test(__LINE__, __FILE__); + + Commands->Flag_List_To_Redraw(); + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + } + + +//Timer_Test(__LINE__, __FILE__); + + + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + +//Timer_Test(__LINE__, __FILE__); + + + Fancy_Text_Print(buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + +//Timer_Test(__LINE__, __FILE__); + + + CCDebugString ("C&C95 - About to draw buttons.\n"); + Commands->Draw_All(); + CCDebugString ("C&C95 - About to show mouse.\n"); + Show_Mouse(); + + +//Timer_Test(__LINE__, __FILE__); + + // start waiting + + //CCDebugString ("C&C95 - About to delay for 2 seconds.\n"); + //Delay(120); + //HMSetDialingMethod( Port, (int)method ); + CCDebugString ("C&C95 - About to set modem dial type.\n"); + SerialPort->Set_Modem_Dial_Type((WinCommDialMethodType) method); + + +//Timer_Test(__LINE__, __FILE__); + + //Clear out any old modem result codes + CCDebugString ("C&C95 - About to call 'Get_Modem_Result'.\n"); + SerialPort->Get_Modem_Result(60, buffer, 81); + //status = HMDial( Port, string ); + CCDebugString ("C&C95 - About to call 'SerialPort->Dial_Modem'.\n"); + SerialPort->Dial_Modem(string); + + +//Timer_Test(__LINE__, __FILE__); + + + // + // Sets up the ability to abort modem commands when any input is in the + // Keyboard buffer. This also calls the game CallBack(). + // + CCDebugString ("C&C95 - About to call 'Setup_Abort_Modem'.\n"); + Setup_Abort_Modem(); + + +//Timer_Test(__LINE__, __FILE__); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + process = true; + delay = ModemWaitCarrier; + CCDebugString ("C&C95 - About to enter main process loop.\n"); + while (process) { + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + CCDebugString ("C&C95 - About to restore video surfaces.\n"); + AllSurfaces.SurfacesRestored=FALSE; + Commands->Draw_All(); + } + +//Timer_Test(__LINE__, __FILE__); + + + //delay = HMInputLine( Port, delay, buffer, 81 ); + CCDebugString ("C&C95 - About to call 'Get_Modem_Result 2'.\n"); + + +//Timer_Test(__LINE__, __FILE__); + + + char abuffer [128]; + sprintf (abuffer, "C&C95 - ModemWaitCarrier delay = %d\n", delay); + CCDebugString (abuffer); + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + + + /* + ............................ Process input ............................ + */ + CCDebugString ("C&C95 - About to check for keyboard input.\n"); + if (!Input) Input = Commands->Input(); + + + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } + else if ( strncmp( buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } + else if ( strncmp( buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } + else if ( strncmp( buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } + else if ( strncmp( buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + process = false; + } + } + + if (delay <= 0) { + if (delay < 0) { + } + process = false; + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Dial_Modem */ + + +/*************************************************************************** + * NullModemClass::Answer_Modem -- waits for call and answers * + * * + * INPUT: * + * reconnect whether this is to reconnect * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +DialStatusType NullModemClass::Answer_Modem( bool reconnect ) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + + //int status; + int delay; + DialStatusType dialstatus; + bool ring = false; + + int x,y,width,height; // dialog dimensions + char text_buffer[80*3]; + char comm_buffer[80*3]; + + int text_width; + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + if (reconnect) { + strcpy( text_buffer, Text_String( TXT_MODEM_CONNERR_WAITING ) ); + } else { + strcpy( text_buffer, Text_String( TXT_WAITING_FOR_CALL ) ); + } + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + //Load_Picture("TITLE.CPS", HidPage, HidPage, Palette, BM_DEFAULT); + + Input = 0; + + /* + ............................ Create the list ............................. + */ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + // start waiting + + +// HMWaitForOK( 1000, NULL ); +// status = HMSendString( Port, "ATS0=1" ); + + + // + // Sets up the ability to abort modem commands when any input is in the + // Keyboard buffer. This also calls the game CallBack() and Input(). + // + Setup_Abort_Modem(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + process = true; + delay = 60000; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + Blit_Hid_Page_To_Seen_Buff(); + } + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + //delay = HMInputLine( Port, delay, buffer, 81 ); + delay = SerialPort->Get_Modem_Result(delay, comm_buffer, 81); + + /* + ............................ Process input ............................ + */ + if (!Input) Input = Commands->Input(); + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): +// Sound_Effect(VOC_BUTTON,255); + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( comm_buffer, "RING", 4 ) == 0 ) { + + strcpy( text_buffer, Text_String( TXT_ANSWERING ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + //PortKillTime( Port, 100 ); + + //HMWaitForOK( 0, NULL ); + //status = HMAnswer( Port ); + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATA\r", strlen("ATA\r")); + + ring = true; + delay = ModemWaitCarrier; + display = REDRAW_ALL; + } + else if ( strncmp( comm_buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, comm_buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } + else if ( strncmp( comm_buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } + else if ( strncmp( comm_buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } + else if ( strncmp( comm_buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } + else if ( strncmp( comm_buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + CCMessageBox().Process ("Error - Modem returned error status.", TXT_OK); + process = false; + } + } + + if (delay <= 0) { + if (ring) { + if (SerialPort->Get_Modem_Status() & CD_SET){ + sprintf(ModemRXString, "%s", "Connected"); + dialstatus = DIAL_CONNECTED; + }else{ + dialstatus = DIAL_ERROR; + CCMessageBox().Process ("Error - TIme out waiting for connect.", TXT_OK); + } + process = false; + } else { + delay = 60000; + } + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Answer_Modem */ + + +/*************************************************************************** + * NullModemClass::Hangup_Modem -- hangs up the modem * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * status successful or not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +bool NullModemClass::Hangup_Modem( void ) +{ + int status; + int delay; + char buffer[81]; + char escape[4]; + + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + ModemService = true; + return( true ); + } + + SerialPort->Set_Serial_DTR(FALSE); + Delay(3200/60); + SerialPort->Set_Serial_DTR(TRUE); + + //SetDtr( Port, 0 ); + //PortKillTime( Port, 3200 ); + //SetDtr( Port, 1 ); + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + ModemService = true; + return( true ); + } + + delay = ModemGuardTime; + while ( delay > 0 ) { + //delay = HMInputLine( Port, delay, buffer, 81 ); + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + + } + + escape[0] = ModemEscapeCode; + escape[1] = ModemEscapeCode; + escape[2] = ModemEscapeCode; + escape[3] = 0; + + //status = HMSendStringNoWait( Port, escape, -1 ); + SerialPort->Write_To_Serial_Port((unsigned char*)escape, 3); + + delay = ModemGuardTime; + while ( delay > 0 ) { + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + //delay = HMInputLine( Port, delay, buffer, 81 ); + + if ( strncmp( buffer, "OK", 2 ) == 0 ) { + break; + } + } + + status = Send_Modem_Command( "ATH", '\r', buffer, 81, ModemHangupDelay, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + ModemService = true; + return( false ); + } + + status = Send_Modem_Command( "ATZ", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + ModemService = true; + return( false ); + } + + ModemService = true; + return( true ); + +} /* end of Hangup_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Modem_Echo -- Sets the echo callback function pointer * + * * + * * + * * + * INPUT: Ptr to callback function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:48PM ST : Documented and added WIn32 support * + *=============================================================================================*/ +void NullModemClass::Setup_Modem_Echo( void ( *func )( char c) ) +{ + SerialPort->Set_Echo_Function(func); + //HMSetUpEchoRoutine( func ); + +} /* end of Setup_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Remove_Modem_Echo -- Set the echo function callback pointer to null * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:50PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Modem_Echo( void ) +{ + SerialPort->Set_Echo_Function(NULL); + //HMSetUpEchoRoutine( NULL ); + +} /* end of Remove_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Print_EchoBuf -- Print out the contents of the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Print_EchoBuf( void ) +{ + for (int i = 0; i < strlen(NullModem.EchoBuf); i++) { + if (NullModem.EchoBuf[i] == '\r') { + NullModem.EchoBuf[i] = 1; + } else { + if (NullModem.EchoBuf[i] == '\n') { + NullModem.EchoBuf[i] = 2; + } + } + } +} + + +/*********************************************************************************************** + * NMC::Reset_EchoBuf -- Empties the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Reset_EchoBuf( void ) +{ + *EchoBuf = 0; + EchoCount = 0; +} + + +/*********************************************************************************************** + * NMC::Abort_Modem -- Checks for user input so that modem operations can be aborted * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ASUSERABORT if abort key pressed. ASSUCESS otherwise. * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:52PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Abort_Modem(void) +//int NullModemClass::Abort_Modem( PORT * ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + Input = Commands->Input(); + + switch ( Input ) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + return( ASUSERABORT ); + } + + return( ASSUCCESS ); + +} /* end of Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Abort_Modem -- sets the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 2:59PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Setup_Abort_Modem( void ) +{ + SerialPort->Set_Abort_Function((int(*)(void))Abort_Modem); +} /* end of Setup_Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Remove_Abort_Modem -- Removes the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:01PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Abort_Modem( void ) +{ + SerialPort->Set_Abort_Function(NULL); +} /* end of Remove_Abort_Modem */ + + + +/*********************************************************************************************** + * NMC::Change_IRQ_Priority -- Increases the priority of the serial interrupt * + * * + * * + * * + * INPUT: Interrupt request number * + * * + * OUTPUT: ASSUCCESS if changed * + * * + * WARNINGS: The Win32 version of this function does nothing. * + * Priorities are controlled by windoze * + * * + * HISTORY: * + * 8/2/96 3:03PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Change_IRQ_Priority( int ) +{ + return (ASSUCCESS); +} /* end of Change_IRQ_Priority */ + + +/*********************************************************************************************** + * NMC::Get_Modem_Status -- returns status of modem control bits * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:06PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Get_Modem_Status( void ) +{ + int modemstatus; + int status; + char buffer[81]; + + //modemstatus = GetModemStatus( Port ); + modemstatus = SerialPort->Get_Modem_Status(); + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + modemstatus &= (~CD_SET); + } + + return( modemstatus ); + +} /* end of Get_Modem_Status */ + + +/*********************************************************************************************** + * NMC::Send_Modem_Command -- Sends an 'AT' command to the modem and gets the response * + * * + * * + * * + * INPUT: command to send to modem. e.g. 'ATZ' * + * terminator byte for command string * + * buffer to put modem response into * + * length of above buffer * + * delay to wait for response * + * number of times to retry when modem doesnt respond * + * * + * OUTPUT: input delay less the time it took the modem to respond * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:09PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ) +{ + return (SerialPort->Send_Command_To_Modem(command, terminator, buffer, buflen, delay, retries)); +} + + +/*********************************************************************************************** + * NMC::Verify_And_Convert_To_Int -- converts a text string of numbers to an int * + * * + * * + * * + * INPUT: ptr to buffer * + * * + * OUTPUT: value of text number in buffer * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:13PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Verify_And_Convert_To_Int( char *buffer ) +{ + int value = 0; + int len = strlen(buffer); + + + for (int i = 0; i < len; i++) { + if ( !isdigit( *(buffer + i) ) ) { + value = -1; + break; + } + } + + if (value == 0) { + value = atoi( buffer ); + } + + return( value ); + +} /* end of Verify_And_Convert_To_Int */ + +/*************************** end of nullmgr.cpp ****************************/ +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/NULLMGR.H b/TIBERIANDAWN/NULLMGR.H new file mode 100644 index 000000000..14ae34761 --- /dev/null +++ b/TIBERIANDAWN/NULLMGR.H @@ -0,0 +1,214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\nullmgr.h_v 1.14 16 Oct 1995 16:45:26 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for a NULL-Modem connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLMODEM_H +#define NULLMODEM_H + + +/* +********************************* Includes ********************************** +*/ +#include "nullconn.h" +#include "connmgr.h" +#include "commlib.h" + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + char *BuildBuf; + int MaxLen; + + char *EchoBuf; + int EchoSize; + int EchoCount; + + int OldIRQPri; + + bool ModemVerboseOn; + bool ModemEchoOn; + int ModemWaitCarrier; + int ModemCarrierDetect; + int ModemCarrierLoss; + int ModemHangupDelay; + int ModemGuardTime; + char ModemEscapeCode; + + static void (*OrigAbortModemFunc)(int); + static KeyNumType Input; + static GadgetClass *Commands; // button list + + /* + ** Constructor/destructor. + */ + NullModemClass (int numsend, int numreceive, int maxlen, unsigned short magicnum); + virtual ~NullModemClass (); + + /* + ** This is the main initialization routine. + */ + int Init( int port, int irq, char *dev_name, int baud, char parity, int wordlength, int stopbits, int flowcontrol ); + int Delete_Connection( void ); + virtual int Num_Connections(void); + virtual int Connection_ID(int ) {return (0);} + virtual int Connection_Index(int ) {return (0);} + int Init_Send_Queue( void ); + void Shutdown( void ); + + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + /* + ** This is how the application sends & receives messages. + */ + int Send_Message (void *buf, int buflen, int ack_req = 1); + int Get_Message (void *buf, int *buflen); + + /* + ** These are for compatibility + */ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int = CONNECTION_NONE) + {return (Send_Message(buf,buflen,ack_req));} + virtual int Get_Private_Message (void *buf, int *buflen, int *) + {return (Get_Message(buf,buflen));} + + /* + ** The main polling routine; should be called as often as possible. + */ + virtual int Service (void); + + /* + ** Queue utility routines. The application can determine how many + ** messages are in the send/receive queues, and the queue's average + ** response time (in clock ticks). + */ + int Num_Send(void); + int Num_Receive(void); + virtual unsigned long Response_Time(void); + virtual void Reset_Response_Time(void); + void * Oldest_Send(void); + virtual void Configure_Debug(int index, int offset, int size, + char **names, int maxnames); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + ** These are for compatibility + */ + virtual int Global_Num_Send(void) {return (Num_Send());} + virtual int Global_Num_Receive(void) {return (Num_Receive());} + virtual int Private_Num_Send(int = CONNECTION_NONE) + {return (Num_Send());} + virtual int Private_Num_Receive(int = CONNECTION_NONE) + {return (Num_Receive());} + + DetectPortType Detect_Port( SerialSettingsType *settings ); + int Detect_Modem( SerialSettingsType *settings, bool reconnect = false ); + DialStatusType Dial_Modem(char *string, DialMethodType method, bool reconnect = false); + DialStatusType Answer_Modem(bool reconnect = false); + bool Hangup_Modem(void); + void Setup_Modem_Echo(void (*func)(char c)); + void Remove_Modem_Echo(void); + void Print_EchoBuf(void); + void Reset_EchoBuf(void); + //static int Abort_Modem(PORT *); + static int Abort_Modem(void); + void Setup_Abort_Modem(void); + void Remove_Abort_Modem(void); + + int Change_IRQ_Priority(int irq); + int Get_Modem_Status(void); + int Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ); + int Verify_And_Convert_To_Int( char *buffer ); + + /* + ** Private Interface. + */ + private: + + /* + ** This is a pointer to the NULL-Modem Connection object. + */ + NullModemConnClass *Connection; + int NumConnections; // # connection objects in use + + /* + ** This is the Greenleaf port handle. + */ + PORT *Port; + HANDLE PortHandle; + + int NumSend; + int NumReceive; + unsigned short MagicNum; + + /* + ** This is the staging buffer for parsing incoming packets. + ** RXSize is the allocated size of the RX buffer. + ** RXCount is the # of characters we currently have in our buffer. + */ + char *RXBuf; + int RXSize; + int RXCount; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /* + ** Various Statistics + */ + int SendOverflows; + int ReceiveOverflows; + int CRCErrors; +}; + +#endif diff --git a/TIBERIANDAWN/OBJECT.CPP b/TIBERIANDAWN/OBJECT.CPP new file mode 100644 index 000000000..989a2684b --- /dev/null +++ b/TIBERIANDAWN/OBJECT.CPP @@ -0,0 +1,1706 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\object.cpv 2.17 16 Oct 1995 16:49:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * ObjectClass::Detach_This_From_All -- Detatches this object from all others. * + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * ObjectClass::Init -- Initializes the basic object system. * + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * ObjectClass::Mark -- Handles basic marking logic. * + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * ObjectClass::ObjectClass -- Default constructor for objects. * + * ObjectClass::Passive_Click_With -- Right mouse button click process. * + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * ObjectClass::Render -- Displays the object onto the map. * + * ObjectClass::Repair -- Handles object repair control. * + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * ObjectClass::Select -- Try to make this object the "selected" object. * + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * ObjectClass::Take_Damage -- Applies damage to the object. * + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * ObjectClass::Value -- Fetches the target value of this object. * + * ObjectClass::What_Action -- Deteremines what action to perform on specified object. * + * ObjectClass::What_Am_I -- RTTI query of this object type. * + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object* + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * ObjectClass::Is_Techno -- Checks to see if this object is a techno type. * + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Selected objects have a special marking box around them. This is the shapes that are +** used for this purpose. +*/ +void const * ObjectTypeClass::SelectShapes = 0; + +void const * ObjectTypeClass::PipShapes = 0; + + +bool ObjectClass::Is_Infantry(void) const +{ + return(false); +} + + + +/*********************************************************************************************** + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * * + * This is the base constructor that is used when constructing the object type classes. * + * Every tangible game piece type calls this constructor for the ObjectTypeClass. This * + * class holds static information that is common to objects in general. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/23/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass::ObjectTypeClass( + bool is_sentient, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + int name, + char const *ini, + ArmorType armor, + unsigned short strength) : + AbstractTypeClass(name, ini) +{ + IsSentient = is_sentient; + IsFlammable = is_flammable; + IsCrushable = is_crushable; + IsStealthy = is_stealthy; + IsSelectable = is_selectable; + IsLegalTarget = is_legal_target; + IsInsignificant = is_insignificant; + IsImmune = is_immune; + Armor = armor; + MaxStrength = strength; + ImageData = NULL; + //RadarIcon = NULL; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * * + * This routine will return the maximum number of pips that can be displayed for this * + * object. When dealing with generic objects, this value is always zero. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pip boxes (empty or otherwise) to display. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Max_Pips(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * * + * This routine will fetch the dimensions of this object expressed as pixels width and * + * pixels height. This information can be used to intelligently update the clipping * + * rectangles. * + * * + * INPUT: width -- Reference to the width variable that will be filled in. * + * * + * height -- Reference to the height variable that will be filled in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::Dimensions(int &width, int &height) const +{ + width = 10; + height = 10; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * * + * This routine will return the cost to purchase this unit. This routine is expected to be * + * overridden by the objects that can actually be purchased. All other object types can * + * simply return zero since this value won't be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the cost of the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Cost_Of(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * * + * This routine will fetch the time in takes to construct this object. Objects that can * + * be constructed will override this routine in order to return a useful value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time units (arbitrary) that it takes to construct this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Time_To_Build(HousesType ) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object. * + * * + * This routine will search for a factory building that can build this object type. * + * * + * INPUT: this routine is just here to be overridden by other classes. * + * * + * OUTPUT: Returns with a pointer to the building that can construct the object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass * ObjectTypeClass::Who_Can_Build_Me(bool, bool, HousesType) const +{ + return(NULL); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * * + * This routine will return with the cameo data pointer for this object type. It is * + * expected that objects that can appear on the sidebar will override this routine in order * + * to provide proper cameo data pointer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo shape data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void const * ObjectTypeClass::Get_Cameo_Data(void) const +{ + return(NULL); +} + + + +/*********************************************************************************************** + * ObjectClass::ObjectClass -- Default constructor for objects. * + * * + * This is the default constructor for objects. It is called as an inherent part of the * + * construction process for all the normal game objects instantiated. It serves merely to * + * initialize the object values to a common (default) state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Objects always start in a state of limbo. They must be Unlimbo()ed before they * + * can be used. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass::ObjectClass(void) +{ + Coord = 0xFFFFFFFFL; // Some bogus illegal value. + Next = 0; // Not part of any object list. + Trigger = 0; // No associated trigger. + IsToDamage = false; + IsToDisplay = false; // Redraw is presumed unnecessary. + IsInLimbo = true; // Always presumed to start in limbo state. + IsSelected = false; // Limboed units cannot be selected. + IsDown = false; // Limboed units cannot be on the map. + IsAnimAttached = false; // Anim is not attached. + Strength = 255; // nominal strength value + IsSelectedMask = 0; // Mask showing who has selected this object +} + + +/*********************************************************************************************** + * ObjectClass::What_Am_I -- RTTI query of this object type. * + * * + * This routine will never be called, but is here for completeness. Every object that * + * is derived from object class must overload this function and return their own proper * + * object RTTI value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the RTTI value that coresponds to the object's type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +RTTIType ObjectClass::What_Am_I(void) const +{ + return(RTTI_OBJECT); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Deteremines what action to perform on specified object. * + * * + * This routine will return that action that this object could perform if the mouse were * + * clicked over the object specified. * + * * + * INPUT: object -- Pointer to the object to check this object against when determining * + * the action to perform. * + * * + * OUTPUT: It returns that action that will be performed if the mouse were clicked over the * + * object. Since non-derived objects cannot do anything, and cannot even be * + * instantiated, this routine will always return ACTION_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(ObjectClass *) const +{ + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * * + * This routine is called when information on a potential action if the mouse were clicked * + * on the cell specified. This routine merely serves as a virtual placeholder so that * + * object types that can actually perform some action will override this routine to provide * + * true functionality. * + * * + * INPUT: cell -- The cell that the mouse is over and might be clicked on. * + * * + * OUTPUT: Returns with the action that this object would try to perform if the mouse were * + * clicked. Since objects at this level have no ability to do anything, this routine * + * will always returns ACTION_NONE unless it is overridden. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(CELL) const +{ + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * * + * The default layer for object location is the LAYER_GROUND. Aircraft will override this * + * routine and make adjustments as necessary according to the aircraft's altitude. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this object is located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +LayerType ObjectClass::In_Which_Layer(void) const +{ + return(LAYER_GROUND); +} + + +/*********************************************************************************************** + * ObjectClass::Is_Techno -- Checks to see if this object is a techno type. * + * * + * Most active objects in the game are of the techno type. This routine will return true * + * if called on an object that is derived from TechnoClass. The RTTI interface is * + * insufficient for this purpose -- hence the existence of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Is this object derived from the TechnoClass object? This is true for units, * + * infantry, aircraft, and buildings. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Is_Techno(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * * + * This routine will return the ownable bits for this object. Objects at this level can't * + * really be owned by anyone, but return the full spectrum of legality just to be safe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable flags (as a combined bitfield) for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char ObjectClass::Get_Ownable(void) const +{ + return(0xff); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * * + * Most objects cannot be repaired. This routine defaults to returning "false", but is * + * overridden by derived functions defined by object types that can support repair. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be repaired? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Repair(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * * + * This routine is used to determine if this object can be sold. Most objects cannot be * + * but for those objects that can, this routine will be overridden as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be sold back? Typically, the answer is no. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Demolish(void) const +{ + return(false); +} + + +bool ObjectClass::Can_Demolish_Unit(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * * + * This routine is used to determine if attacking is an option under player control with * + * respect to this unit. This routine will be overridden as necessary for those objects * + * that have the ability to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given an attack order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Fire(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * * + * This routine is used to determine if the player has the ability to command this object * + * with a movement mission. This routine will be overridden as necessary to support this * + * ability. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given a movement mission by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Move(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * * + * When the coordinate to use when firing at this object is needed, this routine will * + * provide it. Normal objects just use the center of the object for this, but there are * + * some more sophisticated objects that are not fired upon the center. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire at if this object is a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Target_Coord(void) const +{ + return(Center_Coord()); +} + +COORDINATE ObjectClass::Center_Coord(void) const {return Coord;}; +COORDINATE ObjectClass::Render_Coord(void) const {return(Center_Coord());} +COORDINATE ObjectClass::Docking_Coord(void) const {return(Center_Coord());} +COORDINATE ObjectClass::Sort_Y(void) const {return Coord;}; +COORDINATE ObjectClass::Fire_Coord(int ) const {return Coord;}; +void ObjectClass::Record_The_Kill(TechnoClass * ) {}; +void ObjectClass::Do_Shimmer(void) {}; +int ObjectClass::Exit_Object(TechnoClass *) {return 0;}; +void ObjectClass::Hidden(void) {}; +void ObjectClass::Look(bool ) {}; +void ObjectClass::Active_Click_With(ActionType , ObjectClass *) {}; +void ObjectClass::Active_Click_With(ActionType , CELL ) {}; +void ObjectClass::Clicked_As_Target(HousesType house, int) {}; // 2019/09/20 JAS - Added record of who clicked on the object +bool ObjectClass::In_Range(COORDINATE , int) const {return false;}; +int ObjectClass::Weapon_Range(int) const {return 0x0000;}; +TARGET ObjectClass::As_Target(void) const {return TARGET_NONE;}; +void ObjectClass::Scatter(COORDINATE , bool, bool) {}; +bool ObjectClass::Catch_Fire(void) {return false;}; + + +/*********************************************************************************************** + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * * + * This routine is called if there is an attached animation on this object and that * + * animation has finished. Typically, this is necessary for when trees are on fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Fire_Out(void) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Value -- Fetches the target value of this object. * + * * + * This routine will return the target value of this object. The higher the number, the * + * better the object will be as a target. This routine is called when searching for * + * targets. Generic objects have no target potential, and this routine returns zero to * + * reflect that. Other object types will override this routine to return the appropriate * + * target value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value of this object as a target. Higher values mean better * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Value(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * * + * Generic objects don't have a mission, so this routine will just return MISSION_NONE. * + * However, techno objects do have a mission and this routine is overloaded to handle * + * those objects in order to return the correct mission value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current mission being followed by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +MissionType ObjectClass::Get_Mission(void) const +{ + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::Repair -- Handles object repair control. * + * * + * This routine will control object repair mode. At the object level, no repair is * + * possible, so it is expected that any object that can repair will override this function * + * as necessary. * + * * + * INPUT: control -- The repair control parameter. * + * 0 = turn repair off * + * 1 = turn repair on * + * -1 = toggle repair state * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Repair(int ) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * * + * This routine is called to sell back the object. Override this routine for the more * + * sophisticated objects that can actually be sold back. Normal objects can't be sold and * + * this routine does nothing as a consequence. * + * * + * INPUT: control -- How to control the sell state of this object. * + * 0 = stop selling. * + * 1 = start selling. * + * -1 = toggle selling state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Sell_Back(int ) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * * + * This routine will instantly move the object one cell in the specified direction. It * + * moves the object by force. This is typically ONLY used by the scenario editor * + * process. * + * * + * INPUT: facing -- The direction to move the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: Naturally, this can cause illegal placement situations -- use with caution. * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Move(FacingType facing) +{ + COORDINATE coord; + + Mark(MARK_UP); + coord = Adjacent_Cell(Coord, facing); + if (Can_Enter_Cell(Coord_Cell(coord)) == MOVE_OK) { + Coord = coord; + } + Mark(MARK_DOWN); +} + +// Object selection list is switched with player context for GlyphX. ST - 4/17/2019 9:42AM +extern void Logic_Switch_Player_Context(ObjectClass *object); + +/*********************************************************************************************** + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * * + * This routine brings a currently selected object into an unselected state. This is * + * needed when another object becomes selected as well as if the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect(void) +{ + //if (IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (Is_Selected_By_Player()) { + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_UP); + } + + //IsSelected = false; + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Unselected_By_Player(); + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_DOWN); + } + } +} + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players -- This will un-select the object if it was selected * +* from all players * + * * + * This routine brings a currently selected object into an unselected state for all players.* +* This is needed when the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players(void) +{ + CurrentObject.Delete_All(this); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + IsSelected = false; + IsSelectedMask = 0; + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players_Except_Owner -- This will un-select the object if it was * +* selected for all players except for the object's owner * + * * + * This routine brings a currently selected object into an unselected state for all players.* +* This is needed when the object cloaks. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players_Except_Owner(void) +{ + CurrentObject.Delete_All_Except(this, Owner()); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + int owner_mask = 1 << Owner(); + if (IsSelectedMask & owner_mask) + { + IsSelected = true; + IsSelectedMask = owner_mask; + } + else + { + IsSelected = false; + IsSelectedMask = 0; + } + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + + +/*********************************************************************************************** + * ObjectClass::Select -- Try to make this object the "selected" object. * + * * + * This routine is used to make this object into the one that is "selected". A selected * + * object usually displays a floating bar graph and is available to be given orders from * + * the player's I/O. * + * * + * INPUT: allow_mixed -- Allow a mix of player and non-player controlled units? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + * 06/12/1995 JLB : Cannot select a loaner object. * + * 07/23/1995 JLB : Adds to head or tail depending on leader type flag. * + *=============================================================================================*/ +bool ObjectClass::Select(bool allow_mixed) +{ + // if (!Debug_Map && (IsSelected || !Class_Of().IsSelectable)) return(false); + // Updated to function for multiplayer - 6/26/2019 JAS + if (!Debug_Map && (Is_Selected_By_Player() || !Class_Of().IsSelectable)) return(false); + + if (Can_Player_Move() && Is_Techno() && ((TechnoClass *)this)->IsALoaner) return(false); + + /* + ** Don't allow selection of object when in building placement mode. + */ + if (Map.PendingObject) return(false); + + if (!allow_mixed) { + /* + ** If selecting an object of a different house than the player's, make sure that + ** the entire selection list is cleared. + */ + for (int i = 0; i < CurrentObject.Count(); i++) { + if (Owner() != CurrentObject[i]->Owner()) { + Unselect_All(); + break; + } + } + } + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_UP); + } + + //IsSelected = true; + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Selected_By_Player(); + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_DOWN); + } + return(true); +} + + +/*********************************************************************************************** + * ObjectClass::Render -- Displays the object onto the map. * + * * + * This routine will determine the location of the object and if it is roughly on the * + * visible screen, it will display it. Not displaying objects that are not on the screen * + * will save valuable time. * + * * + * INPUT: bool; Should the render be forced regardless of whether the object is flagged to * + * be redrawn? * + * * + * OUTPUT: bool; Was the draw code called for this object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Render(bool forced) +{ + int x,y; + COORDINATE coord = Render_Coord(); + CELL cell = Coord_Cell(coord); + + if (Debug_Map || Debug_Unshroud || ((forced || IsToDisplay) && IsDown && !IsInLimbo)) { + IsToDisplay = false; + + /* + ** Draw the path as lines on the map if so directed and the object is one that + ** contains a path. + */ + + //if (Special.IsShowPath && IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (Special.IsShowPath && Is_Selected_By_Player()) { + switch (What_Am_I()) { + case RTTI_INFANTRY: + case RTTI_UNIT: + FootClass * foot = (FootClass *)this; + CELL cell; + int oldx, oldy; + + if (foot->Head_To_Coord() && foot->Path[0] != FACING_NONE) { + cell = Adjacent_Cell(Coord_Cell(foot->Head_To_Coord()), (FacingType)((foot->Path[0] + FACING_S) & FACING_NW)); + Map.Coord_To_Pixel(Cell_Coord(cell), oldx, oldy); + for (int index = 0; index < MAX_PATH; index++) { + if (foot->Path[index] == FACING_NONE) break; + cell = Adjacent_Cell(cell, foot->Path[index]); + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + LogicPage->Draw_Line(oldx, 8+oldy, x, 8+y, BLACK); + } + oldx = x; + oldy = y; + } + } + break; + } + } + + if (Map.Coord_To_Pixel(coord, x, y)) { + + /* + ** Draw the object itself + */ + Draw_It(x, y, WINDOW_TACTICAL); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the trigger attached to the object. Draw_It is window- + ** relative, so add the window's x-coord to 'x'. + */ + if (Debug_Map && Trigger) { + Fancy_Text_Print(Trigger->Get_Name(), x + (WinX<<3), y, PINK, TBLACK, TPF_CENTER | TPF_NOSHADOW | TPF_6POINT); + } +#endif + + return(true); + } + } + return(false); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * * + * This routine is used to display the current status of the object class to the mono * + * monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Debug_Dump(MonoClass *mono) const +{ + mono->Text_Print("X", 16 + (IsToDisplay?2:0), 18); + mono->Text_Print("X", 16 + (IsActive?2:0), 3); + mono->Text_Print("X", 16 + (IsInLimbo?2:0), 4); + //mono->Text_Print("X", 16 + (IsSelected?2:0), 7); + // Updated to function for multiplayer - 6/26/2019 JAS + mono->Text_Print("X", 16 + (Is_Selected_By_Player() ?2:0), 7); + mono->Set_Cursor(56, 1); + mono->Printf("%08lX", Coord); + mono->Set_Cursor(14, 1);mono->Printf("[%04X]", As_Target()); + mono->Set_Cursor(20, 3);mono->Printf("%2d[%d]", Strength, Class_Of().MaxStrength); +} +#endif + + +/*********************************************************************************************** + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * * + * This routine returns a pointer to a simple occupation list for this object. Since at * + * this tier of the object class chain, the exact shape of the object is indeterminate, * + * this function merely returns a single cell occupation list. This actually works for * + * most vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to a simple occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * * + * This function returns a pointer to an overlap list for the object. An overlap list is * + * the offsets from the object's cell to get the cells the imagery overlaps, but is object * + * is not considered to occupy. Since at this stage, the overlap information is not * + * available, this function merely returns a pointer to an empty list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the generic overlap list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Overlap_List(void) const +{ + static short const _list[] = {REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * * + * This routine is used to handle the once per game processing required for object types. * + * This consists of loading any data and initializing any data tables the game requires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk. * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::One_Time(void) +{ + SelectShapes = MixFileClass::Retrieve("SELECT.SHP"); +#if (FRENCH) + PipShapes = Hires_Retrieve("PIPS_F.SHP"); +#else +#if (GERMAN) + PipShapes = Hires_Retrieve("PIPS_G.SHP"); +#else + PipShapes = Hires_Retrieve("PIPS.SHP"); +#endif +#endif +} + + +/*********************************************************************************************** + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * * + * This routine will mark the object and inform the display system * + * that appropriate rendering is needed. Whenever it is determined * + * that an object needs to be redrawn, call this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a subordinate function to the function Mark(). If an object needs to * + * be redrawn it is probably better to call the function Mark(MARK_CHANGE) rather * + * than this function. This function does not inform the map system that * + * overlapping objects are to be redrawn and thus unless you are really sure that * + * this routine should be called, don't. * + * * + * HISTORY: * + * 05/08/1994 JLB : Created. * + * 12/23/1994 JLB : Flags map and flags unit only. * + *=============================================================================================*/ +void ObjectClass::Mark_For_Redraw(void) +{ + if (!IsToDisplay) { + IsToDisplay = true; + + /* + ** This tells the map rendering logic to "go through the motions" and call the + ** rendering function. In the rendering function, it will sort out what gets + ** rendered and what doesn't. + */ + Map.Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * * + * An object brought into a state of limbo by this routine can be safely deleted. This * + * routine will remove the object from all game lists and tracking systems. It is called * + * prior to deleting the object or placing the object "on ice". * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully placed in limbo? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Limbo(void) +{ + if (GameActive && !IsInLimbo) { + + //Unselect(); + // Updated to function for multiplayer - 6/26/2019 JAS + Unselect_All_Players(); + + Detach_All(); + Mark(MARK_UP); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Remove the object from the logic processing list. + */ + if (Class_Of().IsSentient) { + Logic.Delete(this); + } + + Hidden(); + IsInLimbo = true; + IsToDisplay = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * * + * This routine will place the object into the game tracking and display systems. It is * + * called as a consequence of creating the object. Every game object must be unlimboed at * + * some point. * + * * + * INPUT: coord -- The coordinate to place the object into the game system. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the game object successfully unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Sets object strength. * + *=============================================================================================*/ +bool ObjectClass::Unlimbo(COORDINATE coord, DirType ) +{ + if (GameActive && IsInLimbo && !IsDown) { + if (ScenarioInit || Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + IsInLimbo = false; + IsToDisplay = false; + Coord = Class_Of().Coord_Fixup(coord); + + if (Mark(MARK_DOWN)) { + if (IsActive) { + + /* + ** Add the object to the appropriate map layer. This layer is used + ** for rendering purposes. + */ + if (In_Which_Layer() != LAYER_NONE) { + Map.Submit(this, In_Which_Layer()); + } + + if (Class_Of().IsSentient) { + Logic.Submit(this); + } + } + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * * + * This routine will take the object and see that it is removed from all miscellaneous * + * tracking systems in the game. This operation is vital when deleting an object. It is * + * necessary so that when the object is removed from the game, existing game objects won't * + * be referencing a now invalid game object. This typically affects the targeting * + * and navigation computers of other game objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_All(bool all) +{ + /* + ** Unselect this object if it was selected. + */ + //if (all || Owner() != PlayerPtr->Class->House) { + // Unselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + if (all) { + //Unselect(); + // Updated to function for multiplayer - 6/28/2019 JAS + Unselect_All_Players(); + } + else + { + Unselect_All_Players_Except_Owner(); + } + //End of change - JAS 6/28/2019 + + Map.Detach(this); + + /* + ** Remove from targeting computers. + */ + Detach_This_From_All(As_Target(), all); +} + + +/*********************************************************************************************** + * ObjectClass::Detach_This_From_All -- Detatches this object from all others. * + * * + * This routine sweeps through all game objects and makes sure that it is no longer * + * referenced by them. Typically, this is called in preparation for the object's death * + * or limbo state. * + * * + * INPUT: target -- This object expressed as a target number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_This_From_All(TARGET target, bool all) +{ + int index; + if (Target_Legal(target)) { + + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Units.Count(); index++) { + Units.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Infantry.Count(); index++) { + Infantry.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Aircraft.Count(); index++) { + Aircraft.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Bullets.Count(); index++) { + Bullets.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Anims.Count(); index++) { + Anims.Ptr(index)->Detach(target, all); + } + } +} + + +/*********************************************************************************************** + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * * + * Any radio message received that applies to objects in general are handled by this * + * routine. Typically, this is the "redraw" message, which occurs when another object is * + * loading or unloading and thus overlapping. * + * * + * INPUT: message -- The message received. * + * * + * OUTPUT: Returns with the appropriate radio response. If the message was recognized, then * + * RADIO_ROGER is returned, otherwise, just RADIO_STATIC is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType ObjectClass::Receive_Message(RadioClass *, RadioMessageType message, long & ) +{ + switch (message) { + + /* + ** This message serves as a rendering convenience. It lets the system + ** know that there might be a visual conflict and the unit in radio + ** contact should be redrawn. This typically occurs when a vehicle + ** is being unloaded from a hover lander. + */ + case RADIO_REDRAW: + Mark(MARK_CHANGE); + return(RADIO_ROGER); + + default: + break; + } + return(RADIO_STATIC); +} + + +/*********************************************************************************************** + * ObjectClass::Take_Damage -- Applies damage to the object. * + * * + * This routine applies damage to the object according to the damage parameters. It handles * + * reducing the strength of the object and also returns the result of that damage. The * + * result value can be examined to determine if the object was destroyed, greatly damaged, * + * or other results. * + * * + * INPUT: damage -- Reference to the damage number to apply. This number will be adjusted * + * according to defensive armor and distance. Examine this value after * + * the call to determine the actual amount of damage applied. * + * * + * distance -- The distance (in leptons) from the center of the damage causing * + * explosion to the object itself. * + * * + * warhead -- The warhead type that is causing the damage. * + * * + * OUTPUT: Returns the ResultType that indicates what the affect of the damage was. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 12/27/1994 JLB : Trigger event processing for attacked or destroyed. * + * 01/01/1995 JLB : Reduces damage greatly depending on range. * + *=============================================================================================*/ +ResultType ObjectClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + ResultType result = RESULT_NONE; + int oldstrength = Strength; + + if (oldstrength && damage && !Class_Of().IsImmune) { + int maxstrength = Class_Of().MaxStrength; + + /* + ** Modify damage based on the warhead type and the armor of the object. This results + ** in a reduced damage value, but never below 1 damage point. + */ + damage = Modify_Damage(damage, warhead, Class_Of().Armor, distance); + if (!damage) return(RESULT_NONE); + + /* + ** At this point, we KNOW that at least light damage has occurred. + */ + result = RESULT_LIGHT; + + /* + ** A non-fatal blow has occurred. Check to see if the object transitioned to below + ** half strength or if it is now down to one hit point. + */ + if (oldstrength > damage) { + + if (oldstrength >= (maxstrength >> 1) && (oldstrength-damage) < (maxstrength >> 1)) { + result = RESULT_HALF; + } + } else { + + /* + ** When an object is damaged to destruction, it will instead stop at one + ** damage point. This will prolong the damage state as well as + ** give greater satisfaction when it is finally destroyed. + */ + damage = oldstrength; + } + + /* + ** Apply the damage to the object. + */ + Strength = oldstrength - damage; + + /* + ** Check to see if the object is majorly damaged or destroyed. + */ + switch (Strength) { + case 0: + Record_The_Kill(source); + result = RESULT_DESTROYED; + Detach_All(); + break; + + case 1: + result = RESULT_MAJOR; + break; + + default: + break; + } + + /* + ** Handle any trigger event associated with this object. + */ + if (source && Trigger && result != RESULT_DESTROYED) { + Trigger->Spring(EVENT_ATTACKED, this); + } + + /* + ** If any damage was assessed and this object is selected, then flag + ** the object to be redrawn so that the health bar will be updated. + */ + //if (result != RESULT_NONE && IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (result != RESULT_NONE && Is_Selected_By_Player()) { + Mark(MARK_CHANGE); + } + } + + /* + ** Return with the result of the damage taken. + */ + return(result); +} + + +/*********************************************************************************************** + * ObjectClass::Mark -- Handles basic marking logic. * + * * + * This routine handles the base logic for marking an object up or down on the map. It * + * manages the IsDown flag as well as flagging the object to be redrawn if necessary. * + * Whenever an object is to be marked, it should call this base class function first. If * + * this function returns true, then the higher level function should proceed with its own * + * logic. * + * * + * INPUT: mark -- The marking method to use for this object. It can be either MARK_DOWN, * + * MARK_UP, or MARK_CHANGE. * + * * + * OUTPUT: bool; Was the object marked successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Mark(MarkType mark) +{ + TechnoClass *tech; + CELL cell; + int threat; + HousesType house; + + if (!IsInLimbo && IsActive) { + + /* + ** A mark for change is always successful UNLESS the object + ** is not placed down or has already been flagged as changed + ** this game frame. + */ + if (mark == MARK_CHANGE) { + if (IsToDisplay) return(false); + if (IsDown == true) { + Mark_For_Redraw(); + return(true); + } + return(false); + } + + /* + ** Handle adding or removing the object in the cells' overlap lists + */ + if (mark == MARK_OVERLAP_UP) { + if (IsDown == true) { + Map.Overlap_Up(Coord_Cell(Coord),this); + Mark_For_Redraw(); + return(true); + } + } + if (mark == MARK_OVERLAP_DOWN) { + if (IsDown == true) { + Map.Overlap_Down(Coord_Cell(Coord),this); + Mark_For_Redraw(); + return(true); + } + } + + /* + ** It is important to know whether the object is a techno class + ** or not to see if we have to adjust the regional threat ratings + */ + if (Is_Techno()) { + tech = (TechnoClass *)this; + threat = tech->Risk(); + house = tech->Owner(); + cell = Coord_Cell(Coord); + } else + tech = NULL; + + /* + ** Marking down is only successful if the object isn't already + ** placed down. + */ + if (mark == MARK_DOWN && !IsDown) { + if (tech && GameToPlay == GAME_NORMAL) { + Map[cell].Adjust_Threat(house, threat); + } + IsDown = true; + Mark_For_Redraw(); + return(true); + } + + /* + ** Lifting up is only successful if the object isn't already + ** lifted up from the map. + */ + if (mark == MARK_UP && IsDown) { + if (tech && GameToPlay == GAME_NORMAL) { + Map[cell].Adjust_Threat(house, -threat); + } + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Init -- Initializes the basic object system. * + * * + * This routine should be called when the basic object system needs to be initialized. This * + * occurs when the scenario is about to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Init(void) +{ + CurrentObject.Clear_All(); +} + + +/*********************************************************************************************** + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * * + * This routine is called when this object gets revealed to the house specified. * + * * + * INPUT: house -- Pointer to the house that this object is being revealed to. * + * * + * OUTPUT: Was this object revealed for the first time to this house? Generic objects always * + * return true unless an invalid house pointer was specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Revealed(HouseClass * house) +{ + return(house != NULL); +} + +/*********************************************************************************************** + * ObjectClass::Set_Selected_By_Player -- Set this object as selected by the given player or * + * the default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Selected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + if (((TechnoTypeClass const &)Class_Of()).IsLeader) { + CurrentObject.Add_Head(house, this); + } + else { + CurrentObject.Add(house, this); + } + + int shift = (int)house; + IsSelectedMask |= (1 << shift); + + if (GameToPlay == GAME_NORMAL && player == PlayerPtr) { + IsSelected = true; + } +} + +/*********************************************************************************************** + * ObjectClass::Set_Unselected_By_Player -- Set this object as unselected by the given player * + * orthe default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Unselected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + CurrentObject.Delete(house, this); + + int shift = (int)house; + IsSelectedMask &= ~(1 << shift); + + if (GameToPlay == GAME_NORMAL && player == PlayerPtr) { + IsSelected = false; + } +} + +/*********************************************************************************************** + * ObjectClass::Is_Selected_By_Player -- Has this object been selected by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if selected by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +bool ObjectClass::Is_Selected_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int)player->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + else { + int shift = (int)PlayerPtr->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + return false; +} + + + +// These can't be made inline (for various reasons). +short const * ObjectClass::Occupy_List(bool placement) const {return(Class_Of().Occupy_List(placement));}; +short const * ObjectClass::Overlap_List(void) const {return(Class_Of().Overlap_List());}; +BuildingClass * ObjectClass::Who_Can_Build_Me(bool intheory, bool legal) const {return(Class_Of().Who_Can_Build_Me(intheory, legal, Owner()));}; +unsigned ObjectClass::Health_Ratio(void) const {return(Cardinal_To_Fixed(Class_Of().MaxStrength, Strength));}; +int ObjectClass::Full_Name(void) const {return Class_Of().Full_Name();}; + diff --git a/TIBERIANDAWN/OBJECT.H b/TIBERIANDAWN/OBJECT.H new file mode 100644 index 000000000..020e4931f --- /dev/null +++ b/TIBERIANDAWN/OBJECT.H @@ -0,0 +1,261 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\object.h_v 2.15 16 Oct 1995 16:46:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OBJECT_H +#define OBJECT_H + +#include "abstract.h" + +class ObjectClass; +class TechnoClass; +class ObjectTypeClass; +class HouseClass; +class TriggerClass; +class BuildingClass; +class RadioClass; + +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//} + +/********************************************************************** +** Every game object (that can exist on the map) is ultimately derived from this object +** class. It holds the common information between all objects. This is primarily the +** object unique ID number and its location in the world. All common operations between +** game objects are represented by virtual functions in this class. +*/ +class ObjectClass : public AbstractClass +{ + public: + /* + ** The object can be in one of two states -- placed down on the map, or not. If the + ** object is placed down on the map, then this flag will be true. + */ + unsigned IsDown:1; + + /* + ** This is a support flag that is only used while building a list of objects to + ** be damaged by a proximity affect (explosion). When this flag is set, this object + ** will not be added to the list of units to damage. When damage is applied to the + ** object, this flag is cleared again. This process ensures that an object is never + ** subject to "double jeapordy". + */ + unsigned IsToDamage:1; + +// private: + /* + ** Is this object flagged to be displayed during the next rendering process? This + ** flag could be set by many different circumstances. It is automatically cleared + ** when the object is rerendered. + */ + unsigned IsToDisplay:1; + + + public: + /* + ** An object in the game may be valid yet held in a state of "limbo". Units are in such + ** a state if they are being transported or are otherwise "inside" another unit. They can + ** also be in limbo if they have been created but are being held until the proper time + ** for delivery. + */ + unsigned IsInLimbo:1; + + /* + ** When an object is "selected" it is given a floating bar graph or other graphic imagery + ** to display this fact. When the player performs I/O, the actions may have a direct + ** bearing on the actions of the currently selected object. For quick checking purposes, + ** if this object is the one that is "selected", this flag will be true. + */ + unsigned IsSelected:1; + + //Added a mask instead of bool for selecting players. This is because we must now support multiplayer. + // - 6/26/2019 + unsigned short IsSelectedMask; + + /* + ** If an animation is attached to this object, then this flag will be true. + */ + unsigned IsAnimAttached:1; + + /* + ** Several objects could exist in the same cell list. This is a pointer to the + ** next object in the cell list. The objects in this list are not in any + ** significant order. + */ + ObjectClass * Next; + + /* + ** Every object can be assigned a trigger; the same trigger can be assigned + ** to multiple objects. + */ + TriggerClass * Trigger; + + /* + ** This is the current strength of this object. + */ + short Strength; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + ObjectClass(void); + virtual ~ObjectClass(void) {}; + virtual RTTIType What_Am_I(void) const; + int operator < (ObjectClass const & object) const {return Sort_Y() < object.Sort_Y();}; + int operator > (ObjectClass const & object) const {return Sort_Y() > object.Sort_Y();}; + + /* + ** Object selection control. + */ + static void Init(void); + + /* + ** Query functions. + */ + virtual ActionType What_Action(ObjectClass *) const; + virtual ActionType What_Action(CELL) const; + virtual LayerType In_Which_Layer(void) const; + virtual bool Is_Infantry(void) const; + virtual bool Is_Techno(void) const; + virtual unsigned char Get_Ownable(void) const; + virtual ObjectTypeClass const & Class_Of(void) const = 0; + virtual int Full_Name(void) const; + virtual bool Can_Repair(void) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int ) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual void Detach(TARGET, bool) {}; + virtual void Detach_All(bool all=true); + static void Detach_This_From_All(TARGET target, bool all=true); + virtual void Record_The_Kill(TechnoClass * ); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Do_Shimmer(void); + virtual int Exit_Object(TechnoClass *); + virtual bool Render(bool forced); + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual unsigned Health_Ratio(void) const; + virtual void Draw_It(int x, int y, WindowNumberType ) = 0; + virtual void Hidden(void); + virtual void Look(bool =false); + virtual bool Mark(MarkType); + + private: + virtual void Mark_For_Redraw(void); + + public: + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType , ObjectClass *); + virtual void Active_Click_With(ActionType , CELL ); + virtual void Clicked_As_Target(HousesType house,int = 7); + virtual bool Select(bool allow_mixed = false); + virtual void Unselect(void); + + //These selection functions were added to handle the fact that we now need to support + //client-server multiplayer. - JAS 6/26/2019 + virtual void Unselect_All_Players(void); + virtual void Unselect_All_Players_Except_Owner(void); + virtual bool Is_Selected_By_Player(HouseClass *player = NULL) const; + virtual void Set_Selected_By_Player(HouseClass *player = NULL); + virtual void Set_Unselected_By_Player(HouseClass *player = NULL); + + /* + ** Combat related. + */ + virtual bool In_Range(COORDINATE , int=0) const; + virtual int Weapon_Range(int =0) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual void Scatter(COORDINATE , bool=false, bool=false); + virtual bool Catch_Fire(void); + virtual void Fire_Out(void); + virtual int Value(void) const; + virtual MissionType Get_Mission(void) const; + + /* + ** AI. + */ + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int ); + virtual void Sell_Back(int ); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void Move(FacingType); + +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/ODATA.CPP b/TIBERIANDAWN/ODATA.CPP new file mode 100644 index 000000000..67ac74f64 --- /dev/null +++ b/TIBERIANDAWN/ODATA.CPP @@ -0,0 +1,922 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\odata.cpv 2.16 16 Oct 1995 16:50:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ODATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : April 19, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * OverlayTypeClass::Init -- Loads graphic data for overlays. * + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static OverlayTypeClass const Road( + OVERLAY_ROAD, // Overlay type number. + "ROAD", // INI name of overlay. + TXT_CONCRETE, // Full name of overlay. + LAND_ROAD, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Concrete( + OVERLAY_CONCRETE, // Overlay type number. + "CONC", // INI name of overlay. + TXT_CONCRETE, // Full name of overlay. + LAND_ROAD, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Sandbag( + OVERLAY_SANDBAG_WALL, // Overlay type number. + "SBAG", // INI name of overlay. + TXT_SANDBAG_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 20, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Cyclone( + OVERLAY_CYCLONE_WALL, // Overlay type number. + "CYCL", // INI name of overlay. + TXT_CYCLONE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Brick( + OVERLAY_BRICK_WALL, // Overlay type number. + "BRIK", // INI name of overlay. + TXT_BRICK_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 3, // If this is a wall, how many damage levels? + 70, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + true, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Barbwire( + OVERLAY_BARBWIRE_WALL, // Overlay type number. + "BARB", // INI name of overlay. + TXT_BARBWIRE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Wood( + OVERLAY_WOOD_WALL, // Overlay type number. + "WOOD", // INI name of overlay. + TXT_WOOD_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + true, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium1( + OVERLAY_TIBERIUM1, // Overlay type number. + "TI1", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium2( + OVERLAY_TIBERIUM2, // Overlay type number. + "TI2", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium3( + OVERLAY_TIBERIUM3, // Overlay type number. + "TI3", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium4( + OVERLAY_TIBERIUM4, // Overlay type number. + "TI4", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium5( + OVERLAY_TIBERIUM5, // Overlay type number. + "TI5", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium6( + OVERLAY_TIBERIUM6, // Overlay type number. + "TI6", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium7( + OVERLAY_TIBERIUM7, // Overlay type number. + "TI7", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium8( + OVERLAY_TIBERIUM8, // Overlay type number. + "TI8", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium9( + OVERLAY_TIBERIUM9, // Overlay type number. + "TI9", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium10( + OVERLAY_TIBERIUM10, // Overlay type number. + "TI10", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium11( + OVERLAY_TIBERIUM11, // Overlay type number. + "TI11", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium12( + OVERLAY_TIBERIUM12, // Overlay type number. + "TI12", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Squish( + OVERLAY_SQUISH, // Overlay type number. + "SQUISH", // INI name of overlay. + TXT_SQUISH, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const V12( + OVERLAY_V12, // Overlay type number. + "V12", // INI name of overlay. + TXT_CIV12, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V13( + OVERLAY_V13, // Overlay type number. + "V13", // INI name of overlay. + TXT_CIV13, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V14( + OVERLAY_V14, // Overlay type number. + "V14", // INI name of overlay. + TXT_CIV14, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V15( + OVERLAY_V15, // Overlay type number. + "V15", // INI name of overlay. + TXT_CIV15, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V16( + OVERLAY_V16, // Overlay type number. + "V16", // INI name of overlay. + TXT_CIV16, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V17( + OVERLAY_V17, // Overlay type number. + "V17", // INI name of overlay. + TXT_CIV17, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V18( + OVERLAY_V18, // Overlay type number. + "V18", // INI name of overlay. + TXT_CIV18, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const FlagSpot( + OVERLAY_FLAG_SPOT, // Overlay type number. + "FPLS", // INI name of overlay. + TXT_FLAG_SPOT, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const WoodCrate( + OVERLAY_WOOD_CRATE, // Overlay type number. + "WCRATE", // INI name of overlay. + TXT_WOOD_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const SteelCrate( + OVERLAY_STEEL_CRATE, // Overlay type number. + "SCRATE", // INI name of overlay. + TXT_STEEL_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); + + +OverlayTypeClass const * const OverlayTypeClass::Pointers[OVERLAY_COUNT] = { + &Concrete, // OVERLAY_CONCRETE + &Sandbag, // OVERLAY_SANDBAG_WALL + &Cyclone, // OVERLAY_CYCLONE_WALL + &Brick, // OVERLAY_BRICK_WALL + &Barbwire, // OVERLAY_BARBWIRE_WALL + &Wood, // OVERLAY_WOOD_WALL + &Tiberium1, // OVERLAY_TIBERIUM1 + &Tiberium2, // OVERLAY_TIBERIUM2 + &Tiberium3, // OVERLAY_TIBERIUM3 + &Tiberium4, // OVERLAY_TIBERIUM4 + &Tiberium5, // OVERLAY_TIBERIUM5 + &Tiberium6, // OVERLAY_TIBERIUM6 + &Tiberium7, // OVERLAY_TIBERIUM7 + &Tiberium8, // OVERLAY_TIBERIUM8 + &Tiberium9, // OVERLAY_TIBERIUM9 + &Tiberium10, // OVERLAY_TIBERIUM10 + &Tiberium11, // OVERLAY_TIBERIUM11 + &Tiberium12, // OVERLAY_TIBERIUM12 + &Road, // OVERLAY_ROAD + &Squish, // OVERLAY_SQUISH + &V12, // OVERLAY_V12 + &V13, // OVERLAY_V13 + &V14, // OVERLAY_V14 + &V15, // OVERLAY_V15 + &V16, // OVERLAY_V16 + &V17, // OVERLAY_V17 + &V18, // OVERLAY_V18 + &FlagSpot, // OVERLAY_FLAG_SPOT + &WoodCrate, // OVERLAY_WOOD_CRATE + &SteelCrate, // OVERLAY_STEEL_CRATE +}; + + +/*********************************************************************************************** + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * * + * This is the constructor for the overlay types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass::OverlayTypeClass( + OverlayType iconset, + char const *ininame, + int fullname, + LandType ground, + int damagelevels, + int damagepoints, + bool isradarvisible, + bool iswooden, + bool istarget, + bool iscrushable, + bool istiberium, + bool high, + bool theater, + bool walltype, + bool iscrate) : + ObjectTypeClass(false, + false, + iscrushable, + true, + false, + istarget, + true, + false, + fullname, + ininame, + ARMOR_NONE, + 0) +{ + IsRadarVisible = isradarvisible; + IsCrate = iscrate; + IsWooden = iswooden; + IsHigh = high; + IsTheater = theater; + IsTiberium = istiberium; + Type = iconset; + Land = ground; + IsWall = walltype; + DamageLevels = damagelevels; + DamagePoints = damagepoints; +} + + +/*********************************************************************************************** + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * * + * This routine should be called once when the game first starts. It will establish * + * pointers to the graphic data of the overlay objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * * + * This routine is used to determine the overlay number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the overlay. * + * * + * OUTPUT: Returns with the overlay number. If the name had no match, * + * then returns with OVERLAY_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +OverlayType OverlayTypeClass::From_Name(char const *name) +{ + if (name) { + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(OVERLAY_NONE); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the overlay map and build an * + * occupation list. This list is used to render a overlay cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the overlay occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * OverlayTypeClass::Occupy_List(bool) const +{ + static short _simple[] = {0, REFRESH_EOL}; + + return(_simple); +} + + +/*************************************************************************** + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +unsigned char * OverlayTypeClass::Radar_Icon(int data) const +{ + unsigned char *icon = (unsigned char *)Get_Radar_Data(); // Get pointer to radar icons + icon += (data * 9) + 2; // move icon ptr to correct icon + return(icon); // Return the correct icon +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * * + * This routine is used to display a generic view of the overlay * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + /* + ---------------------------- Draw the shape ------------------------------ + */ + if (Get_Image_Data()) { + int frame = 0; + + if (IsTiberium) { + frame = 7; + } + + CC_Draw_Shape(Get_Image_Data(), frame, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * * + * This routine prepares a list of overlay objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a overlay object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created * + *=============================================================================================*/ +void OverlayTypeClass::Prep_For_Add(void) +{ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + if (overlay.Get_Image_Data() && !overlay.IsWall && (!overlay.IsTiberium || index == OVERLAY_TIBERIUM1)) { + Map.Add_To_List(&overlay); + } + } +} +#endif + + +/*********************************************************************************************** + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * * + * This support routine is used by the scenario editor to add a overlay object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the overlay object. * + * * + * OUTPUT: bool; Was the overlay object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new OverlayClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * * + * This routine will create an object of this type. For certain overlay objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a overlay at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this overlay type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * OverlayTypeClass::Create_One_Of(HouseClass *) const +{ + return(new OverlayClass(Type, -1)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * * + * This routine will draw the overlay shape at the coordinates specified. It is presumed * + * that all the underlying layers have already been rendered by the time this routine is * + * called. * + * * + * INPUT: x, y -- Coordinate (upper left) of cell where overlay image is to be drawn. * + * * + * data -- Cell specific data that controls the imagery of the overlay. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Draw_It(int x, int y, int data) const +{ + CC_Draw_Shape(Get_Image_Data(), data, Map.TacPixelX+x+(CELL_PIXEL_W>>1), Map.TacPixelY+y+(CELL_PIXEL_H>>1), WINDOW_MAIN, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * * + * This routine will update the overlay graphic data according to the theater specified. * + * It is typically called when the scenario is first loaded (theater change). * + * * + * INPUT: theater -- The theater to load specific data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 4/25/1996 ST : Modified to load theater specific sidebar icons if available * + *=============================================================================================*/ +void OverlayTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + + if (overlay.IsTheater) { + _makepath(fullname, NULL, NULL, overlay.IniName, Theaters[theater].Suffix); + } else { + _makepath(fullname, NULL, NULL, overlay.IniName, ".SHP"); + } + ((void const *&)overlay.ImageData) = MixFileClass::Retrieve(fullname); + + IsTheaterShape = overlay.IsTheater; + if (overlay.RadarIcon) delete[] (char *)overlay.RadarIcon; + ((void const *&)overlay.RadarIcon) = Get_Radar_Icon(overlay.Get_Image_Data(), 0, -1, 3); + IsTheaterShape = false; + } + } +} diff --git a/TIBERIANDAWN/OPTIONS.CPP b/TIBERIANDAWN/OPTIONS.CPP new file mode 100644 index 000000000..f84e1b2cf --- /dev/null +++ b/TIBERIANDAWN/OPTIONS.CPP @@ -0,0 +1,797 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\options.cpv 2.17 16 Oct 1995 16:51:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * OptionsClass::Get_Color -- Fetches the current color setting. * + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * OptionsClass::Get_Game_Speed -- Fetches the current game speed setting. * + * OptionsClass::Get_Scroll_Rate -- Fetches the current scroll rate setting. * + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * OptionsClass::One_Time -- This performs any one time initialization for the options class.* + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * OptionsClass::Process -- Handles all the options graphic interface. * + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * OptionsClass::Set -- Sets options based on current settings * + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * OptionsClass::Set_Color -- Sets the color to the value specified. * + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * OptionsClass::Set_Game_Speed -- Sets the game speed as specified. * + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * OptionsClass::Set_Scroll_Rate -- Sets the scroll rate as specified. * + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * OptionsClass::Set_Tint -- Sets the tint setting. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "options.h" + + +/*********************************************************************************************** + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * * + * This is the constructor for the options class. It handles setting up all the globals * + * necessary for the options. This includes setting them to their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +OptionsClass::OptionsClass(void) +{ + GameSpeed = TIMER_SECOND / TICKS_PER_SECOND; + ScrollRate = TIMER_SECOND / TICKS_PER_SECOND; + Volume = 0xE0; + ScoreVolume = 0x90; + Contrast = 0x80; + Color = 0x80; + Contrast = 0x80; + Tint = 0x80; + Brightness = 0x80; + AutoScroll = true; +#if (GERMAN | FRENCH) + IsDeathAnnounce = true; +#else + IsDeathAnnounce = false; +#endif + IsScoreRepeat = false; + IsScoreShuffle = false; + IsFreeScroll = false; +} + + +/*********************************************************************************************** + * OptionsClass::One_Time -- This performs any one time initialization for the options class. * + * * + * This routine should be called only once and it will perform any inializations for the * + * options class that is needed. This may include things like file loading and memory * + * allocation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::One_Time(void) +{ + Set_Score_Vol(ScoreVolume); +} + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Process(void) +{ +} + + +/*********************************************************************************************** + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * * + * This routine will control the score shuffle flag. The setting to use is provided as * + * a parameter. When shuffling is on, the score play order is scrambled. * + * * + * INPUT: on -- Should the shuffle option be activated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Shuffle(int on) +{ + IsScoreShuffle = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * * + * This routine is used to control whether scores repeat or not. The setting to use for * + * the repeat flag is provided as a parameter. * + * * + * INPUT: on -- Should the scores repeat? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Repeat(int on) +{ + IsScoreRepeat = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * * + * This routine will set the global score volume to the value specified. The value ranges * + * from zero to 255. * + * * + * INPUT: volume -- The new volume setting to use for scores. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Score_Volume(int volume) +{ + volume = Bound(volume, 0, 255); + ScoreVolume = volume; + Set_Score_Vol(ScoreVolume); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * * + * This routine will set the sound effect volume level as indicated. It can generate a * + * sound effect for feedback purposes if desired. The volume setting can range from zero * + * to 255. The value of 255 is the loudest. * + * * + * INPUT: volume -- The volume setting to use for the new value. 0 to 255. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Sound_Volume(int volume, int feedback) +{ + volume = Bound(volume, 0, 255); + Volume = volume; + if (feedback) { + Sound_Effect(VOC_BLEEPY3, NULL); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * * + * This routine will set the current brightness level to the value specified. This value * + * can range from zero to 255, with 128 being the normal (default) brightness level. * + * * + * INPUT: brightness -- The brightness level to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Brightness(int brightness) +{ + Brightness = 0x40 + Fixed_To_Cardinal(0x80, brightness); + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * * + * This routine will fetch the current setting for the brightness level. The value ranges * + * from zero to 255, with 128 being the normal (default) value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current brightness setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Brightness(void) const +{ + return(Cardinal_To_Fixed(0x80, Brightness-0x40)); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Color -- Sets the color to the value specified. * + * * + * This routine will set the color value to that specified. The value specified can range * + * from zero to 255. The value of 128 is the normal default color setting. * + * * + * INPUT: color -- The new color value to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Color(int color) +{ + Color = color; + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Color -- Fetches the current color setting. * + * * + * This routine will fetch the current color setting. This value ranges from zero to * + * 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current color setting. The value of 128 is the normal (default) * + * color setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Color(void) const +{ + return(Color); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * * + * This routine will set the constrast to the setting specified. This setting ranges from * + * zero to 255. The value o 128 is the normal default value. * + * * + * INPUT: contrast -- The constrast setting to make as the current setting. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Contrast(int contrast) +{ + Contrast = 0x40 + Fixed_To_Cardinal(0x80, contrast); + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * * + * This routine will get the current contrast setting. The value returned is in the range * + * of zero to 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current contrast setting. A setting of 128 is the normal default value.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Contrast(void) const +{ + return(Cardinal_To_Fixed(0x80, Contrast-0x40)); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Tint -- Sets the tint setting. * + * * + * This routine will change the current tint setting according to the value specified. * + * * + * INPUT: tint -- The desired tint setting. This value ranges from zero to 255. * + * * + * OUTPUT: none * + * * + * WARNINGS: The value of 128 is the default (normal) tint setting. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Tint(int tint) +{ + Tint = tint; + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * * + * This fetches the current tint setting. The value is returned as a number between * + * zero and 255. This has been adjusted for the valid range allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current tint setting. Normal tint setting is 128. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Tint(void) const +{ + return(Tint); +} + + +/*********************************************************************************************** + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * * + * This routine is used to adjust the palette according to the settings provided. It is * + * used by the options class to monkey with the palette. * + * * + * INPUT: oldpal -- Pointer to the original (unmodified) palette. * + * * + * newpal -- The new palette to create according to the settings provided. * + * * + * brightness -- The brightness level (0..255). * + * * + * color -- The color level (0..255). * + * * + * tint -- The tint (hue) level (0..255). * + * * + * contrast -- The contrast level (0..255). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Adjust_Palette(void *oldpal, void *newpal, unsigned char brightness, unsigned char color, unsigned char tint, unsigned char contrast) const +{ +// ST - 1/3/2019 10:49AM +#if (0) + int index; + unsigned h,s,v; + unsigned r,g,b; + + if (!oldpal || !newpal) return; + + /* + ** Adjust for palette. + */ + for (index = 0; index < 256; index++) { + if (/*index == LTGREEN ||*/ index == 255) { + memcpy(&((char*)newpal)[index*3], &((char*)oldpal)[index*3], 3); + } else { + r = ((char*)oldpal)[(index*3)+0]; + g = ((char*)oldpal)[(index*3)+1]; + b = ((char*)oldpal)[(index*3)+2]; + Convert_RGB_To_HSV(r, g, b, &h, &s, &v); + + /* + ** Adjust contrast by moving the value toward the center according to the + ** percentage indicated. + */ + int temp; + + temp = (v * brightness) / 0x80; // Brightness + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (((((int)v) - 0x80) * contrast) / 0x80) + 0x80; // Contrast + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (s * color) / 0x80; // Color + temp = Bound(temp, 0, 0xFF); + s = temp; + temp = (h * tint) / 0x80; // Tint + temp = Bound(temp, 0, 0xFF); + h = temp; + Convert_HSV_To_RGB(h, s, v, &r, &g, &b); + ((char*)newpal)[(index*3)+0] = r; + ((char*)newpal)[(index*3)+1] = g; + ((char*)newpal)[(index*3)+2] = b; + } + } +#endif +} + + +/*********************************************************************************************** + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void OptionsClass::Load_Settings (void) +{ + char *buffer; // INI staging buffer pointer. + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /* + ** Create filename and read the file. + */ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + /* + ** Read in the Options values + */ + GameSpeed = WWGetPrivateProfileInt("Options", "GameSpeed", 4, buffer); + ScrollRate = WWGetPrivateProfileInt("Options", "ScrollRate", 4, buffer); + Set_Brightness(WWGetPrivateProfileInt("Options", "Brightness", 0x80, buffer)); + Set_Sound_Volume(WWGetPrivateProfileInt("Options", "Volume", 0xA0, buffer),false); + Set_Score_Volume(WWGetPrivateProfileInt("Options", "ScoreVolume", 0xFF, buffer)); + Set_Contrast(WWGetPrivateProfileInt("Options", "Contrast", 0x80, buffer)); + Set_Color(WWGetPrivateProfileInt("Options", "Color", 0x80, buffer)); + Set_Tint(WWGetPrivateProfileInt("Options", "Tint", 0x80, buffer)); + AutoScroll = WWGetPrivateProfileInt("Options", "AutoScroll", 1, buffer); + Set_Repeat(WWGetPrivateProfileInt("Options", "IsScoreRepeat", 0, buffer)); + Set_Shuffle(WWGetPrivateProfileInt("Options", "IsScoreShuffle", 0, buffer)); + IsDeathAnnounce = WWGetPrivateProfileInt("Options", "DeathAnnounce", 0, buffer); + IsFreeScroll = WWGetPrivateProfileInt("Options", "FreeScrolling", 0, buffer); + SlowPalette = WWGetPrivateProfileInt("Options", "SlowPalette", 1, buffer); + + char workbuf[128]; + + /* + ** Check for and possible enable true object names. + */ + WWGetPrivateProfileString("Options", "TrueNames", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_TRUENAME) { + Special.IsNamed = true; + } + + /* + ** Enable 6 player games if special flag is detected. + */ + WWGetPrivateProfileString("Options", "Players", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_6PLAYER) { + MPlayerMax = 6; + } + + /* + ** Enable three point turning logic as indicated. + */ + WWGetPrivateProfileString("Options", "Rotation", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_3POINT) { + Special.IsThreePoint = true; + } + + /* + ** Allow purchase of the helipad separately from the helicopter. + */ + WWGetPrivateProfileString("Options", "Helipad", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_HELIPAD) { + Special.IsSeparate = true; + } + + /* + ** Allow the MCV to undeploy rather than sell. + */ + WWGetPrivateProfileString("Options", "MCV", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_MCV) { + Special.IsMCVDeploy = true; + } + + /* + ** Allow disabling of building bibs so that tigher building packing can occur. + */ + WWGetPrivateProfileString("Options", "Bibs", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_BIB) { + Special.IsRoad = true; + } + + /* + ** Allow targeting of trees without having to hold down the shift key. + */ + WWGetPrivateProfileString("Options", "TreeTarget", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_TREETARGET) { + Special.IsTreeTarget = true; + } + + /* + ** Allow infantry to fire while moving. Attacker gets advantage with this flag. + */ + WWGetPrivateProfileString("Options", "Combat", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_COMBAT) { + Special.IsDefenderAdvantage = false; + } + + /* + ** Allow custom scores. + */ + WWGetPrivateProfileString("Options", "Scores", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SCORE) { + Special.IsVariation = true; + } + + /* + ** Smarter self defense logic. Tanks will try to run over adjacent infantry. Buildings + ** will automatically return fire if they are fired upon. Infantry will run from an + ** incoming explosive (grenade or napalm) or damage that can't be directly addressed. + */ + WWGetPrivateProfileString("Options", "CombatIQ", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_IQ) { + Special.IsSmartDefense = true; + Special.IsScatter = true; + } + + /* + ** Enable the infantry squish marks when run over by a vehicle. + */ + WWGetPrivateProfileString("Options", "Overrun", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SQUISH) { + Special.IsGross = true; + } + + /* + ** Enable the human generated sound effects. + */ + WWGetPrivateProfileString("Options", "Sounds", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_HUMAN) { + Special.IsJuvenile = true; + } + + /* + ** Scrolling is disabled over the tabs with this option. + */ + WWGetPrivateProfileString("Options", "Scrolling", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SCROLLING) { + Special.IsScrollMod = true; + } +} + + +/*********************************************************************************************** + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void OptionsClass::Save_Settings (void) +{ + char * buffer; // INI staging buffer pointer. + CCFileClass file; + + /* + ** Get a working pointer to the INI staging buffer. Make sure that the buffer + ** starts cleared out of any data. + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("CONQUER.INI"); + if (file.Is_Available()) { + file.Read(buffer, _ShapeBufferSize-1); + } + + /* + ** Save Options settings + */ + WWWritePrivateProfileInt("Options", "GameSpeed", GameSpeed, buffer); + WWWritePrivateProfileInt("Options", "ScrollRate", ScrollRate, buffer); + WWWritePrivateProfileInt("Options", "Brightness", Brightness, buffer); + WWWritePrivateProfileInt("Options", "Volume", Volume, buffer); + WWWritePrivateProfileInt("Options", "ScoreVolume", ScoreVolume, buffer); + WWWritePrivateProfileInt("Options", "Contrast", Contrast, buffer); + WWWritePrivateProfileInt("Options", "Color", Color, buffer); + WWWritePrivateProfileInt("Options", "Tint", Tint, buffer); + WWWritePrivateProfileInt("Options", "AutoScroll", AutoScroll, buffer); + WWWritePrivateProfileInt("Options", "IsScoreRepeat", IsScoreRepeat, buffer); + WWWritePrivateProfileInt("Options", "IsScoreShuffle", IsScoreShuffle, buffer); + WWWritePrivateProfileInt("Options", "DeathAnnounce", IsDeathAnnounce, buffer); + WWWritePrivateProfileInt("Options", "FreeScrolling", IsFreeScroll, buffer); + + /* + ** Write the INI data out to a file. + */ + file.Write(buffer,strlen(buffer)); +} + + +/*********************************************************************************************** + * OptionsClass::Set -- Sets options based on current settings * + * * + * Use this routine to adjust the palette or sound settings after a fresh scenario load. * + * It assumes the values needed are already loaded into OptionsClass. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void OptionsClass::Set(void) +{ + Set_Brightness(Brightness); + Set_Contrast(Contrast); + Set_Color(Color); + Set_Tint(Tint); + Set_Sound_Volume(Volume,false); + Set_Score_Volume(ScoreVolume); + Set_Repeat(IsScoreRepeat); + Set_Shuffle(IsScoreShuffle); +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * * + * This routine is used to adjust delay factors that MUST be synchronized on all machines * + * but should maintain a speed as close to constant as possible. Building animations are * + * a good example of this. * + * * + * INPUT: delay -- The normal delay factor. * + * * + * OUTPUT: Returns with the delay to use that has been modified so that a reasonably constant * + * rate will result. * + * * + * WARNINGS: This calculation is crude due to the coarse resolution that a 1/15 second timer * + * allows. * + * * + * Use of this routine ASSUMES that the GameSpeed is synchronized on all machines. * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + * 06/30/1995 JLB : Handles low values in a more consistent manner. * + *=============================================================================================*/ +int OptionsClass::Normalize_Delay(int delay) const +{ + static int _adjust[][8] = { + {2,2,1,1,1,1,1,1}, + {3,3,3,2,2,2,1,1}, + {5,4,4,3,3,2,2,1}, + {7,6,5,4,4,4,3,2} + }; + if (delay) { + if (delay < 5) { + delay = _adjust[delay-1][GameSpeed]; + } else { + delay = ((delay * 8) / (GameSpeed+1)); + } + } + return(delay); +} + + + +void OptionsClass::Fixup_Palette(void) const +{ + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); +} + + +int OptionsClass::Normalize_Sound(int volume) const +{ + return(Fixed_To_Cardinal(volume, Volume)); +} \ No newline at end of file diff --git a/TIBERIANDAWN/OPTIONS.H b/TIBERIANDAWN/OPTIONS.H new file mode 100644 index 000000000..e2c60975f --- /dev/null +++ b/TIBERIANDAWN/OPTIONS.H @@ -0,0 +1,101 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\options.h_v 2.18 16 Oct 1995 16:46:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +class OptionsClass { + public: + enum { + MAX_SCROLL_SETTING=7, + MAX_SPEED_SETTING=7 + }; + + OptionsClass(void); + + void One_Time(void); + void Process(void); + + void Fixup_Palette(void) const; + void Set_Shuffle(int on); + void Set_Repeat(int on); + void Set_Score_Volume(int volume); + void Set_Sound_Volume(int volume, int feedback); + void Set_Brightness(int brightness); + int Get_Brightness(void) const; + void Set_Color(int color); + int Get_Color(void) const; + void Set_Contrast(int contrast); + int Get_Contrast(void) const; + void Set_Tint(int tint); + int Get_Tint(void) const; + int Normalize_Delay(int delay) const; + int Normalize_Sound(int volume) const; + + /* + ** File I/O routines + */ + void Load_Settings(void); + void Save_Settings(void); + + void Set(void); + + /* + ** This is actually the delay between game frames expressed as 1/60 of + ** a second. The default value is 4 (1/15 second). + */ + unsigned int GameSpeed; + + int ScrollRate; // Distance to scroll. + unsigned char Brightness; + unsigned char Volume; // Volume for sound effects. + unsigned char ScoreVolume; // Volume for scores. + unsigned char Contrast; // Value + unsigned char Color; // Saturation + unsigned char Tint; // Hue + unsigned AutoScroll:1; // Does map autoscroll? + unsigned IsScoreRepeat:1; // Score should repeat? + unsigned IsScoreShuffle:1; // Score list should shuffle? + unsigned IsDeathAnnounce:1;// Announce enemy deaths? + unsigned IsFreeScroll:1; // Allow free direction scrolling? + + protected: + + void Adjust_Palette(void *oldpal, void *newpal, unsigned char brightness, unsigned char color, unsigned char tint, unsigned char contrast) const; + + private: +}; + + +#endif diff --git a/TIBERIANDAWN/OVERLAY.CPP b/TIBERIANDAWN/OVERLAY.CPP new file mode 100644 index 000000000..73a1c541d --- /dev/null +++ b/TIBERIANDAWN/OVERLAY.CPP @@ -0,0 +1,428 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\overlay.cpv 2.17 16 Oct 1995 16:50:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * OverlayClass::Write_INI -- Writes the overlay data to an INI file. * + * OverlayClass::delete -- Returns a overlay object to the pool. * + * OverlayClass::Init -- Resets the overlay object system. * + * OverlayClass::new -- Allocates a overlay object from pool * + * OverlayClass::OverlayClass -- Overlay object constructor. * + * OverlayClass::Mark -- Marks the overlay down on the map. * + * OverlayClass::Validate -- validates overlay * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "overlay.h" + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * OverlayClass::VTable; + +HousesType OverlayClass::ToOwn = HOUSE_NONE; + +OverlayClass::OverlayClass(void) : Class(0) {ToOwn = HOUSE_NONE;}; + + +/*********************************************************************************************** + * OverlayClass::Validate -- validates overlay * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int OverlayClass::Validate(void) const +{ + int num; + + num = Overlays.ID(this); + if (num < 0 || num >= OVERLAY_MAX) { + Validate_Error("OVERLAY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * OverlayClass::Init -- Resets the overlay object system. * + * * + * This routine resets the overlay object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Init(void) +{ + OverlayClass *ptr; + + Overlays.Free_All(); + + ptr = new OverlayClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * OverlayClass::new -- Allocates a overlay object from pool * + * * + * This routine is used to allocate a overlay object from the * + * overlay object pool. * + * * + * INPUT: size -- The size of a overlay object (not used). * + * * + * OUTPUT: Returns with a pointer to an available overlay object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * OverlayClass::operator new(size_t ) +{ + void * ptr = Overlays.Allocate(); + if (ptr) { + ((OverlayClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * OverlayClass::delete -- Returns a overlay object to the pool. * + * * + * This routine will return a overlay object to the overlay object * + * pool. A overlay so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::operator delete(void *ptr) +{ + if (ptr) { + ((OverlayClass *)ptr)->IsActive = false; + } + Overlays.Free((OverlayClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * OverlayClass::OverlayClass -- Overlay object constructor. * + * * + * This is the constructor for a overlay object. * + * * + * INPUT: type -- The overlay object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +OverlayClass::OverlayClass(OverlayType type, CELL pos, HousesType house) : + Class(&OverlayTypeClass::As_Reference(type)) +{ + if (pos != -1) { + ToOwn = house; + Unlimbo(Cell_Coord(pos)); + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * OverlayClass::Mark -- Marks the overlay down on the map. * + * * + * This routine will place the overlay onto the map. The overlay object is deleted by this * + * operation. The map is updated to reflect the presence of the overlay. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the overlay successfully marked? Failure occurs if it is not being * + * marked down. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool OverlayClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL cell = Coord_Cell(Coord); + CellClass * cellptr = &Map[cell]; + + /* + ** Road placement occurs in two steps. First the foundation is placed, but only + ** on buildable terrain. Second, the road is completed, but only if the foundation + ** was previously placed. + */ + if (*this == OVERLAY_ROAD) { + if ((cellptr->Overlay == OVERLAY_ROAD && cellptr->OverlayData == 0) || + (cellptr->Overlay == OVERLAY_NONE && cellptr->Is_Generally_Clear())) { + + if (cellptr->Overlay == OVERLAY_ROAD) { + cellptr->OverlayData = 1; + } else { + cellptr->OverlayData = 0; + } + cellptr->Overlay = Class->Type; + cellptr->Redraw_Objects(); + } + } else { + + /* + ** Walls have special logic when they are marked down. + */ + if (Class->IsWall) { + if (cellptr->Is_Generally_Clear() && cellptr->Overlay != OVERLAY_FLAG_SPOT) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + cellptr->Wall_Update(); + + /* + ** Flag ownership of the cell if the 'global' ownership flag indicates that this + ** is necessary for the overlay. + */ + if (ToOwn != HOUSE_NONE) { + cellptr->Owner = ToOwn; + } + + } else { + Delete_This(); + return(false); + } + } else { + if ((cellptr->Overlay == OVERLAY_NONE || cellptr->Overlay == OVERLAY_SQUISH) && !cellptr->Cell_Terrain() && Ground[cellptr->Land_Type()].Build) { + + /* + ** Increment the global crate counter. This is used to regulate + ** the crate generation. + */ + if (Class->IsCrate) CrateCount++; + + /* + ** Don't show the squish unless the gross flag is active. + */ + if (!Special.IsGross && Class->Type != OVERLAY_SQUISH) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + } + cellptr->Redraw_Objects(); + if (Class->Land == LAND_TIBERIUM) { + cellptr->OverlayData = 1; + cellptr->Tiberium_Adjust(); + } else { + if (*this == OVERLAY_CONCRETE) { + CELL newcell; + + /* + ** Smudges go away when concrete is laid down. + */ + cellptr->Smudge = SMUDGE_NONE; + cellptr->SmudgeData = 0; + cellptr->Concrete_Calc(); + + /* + ** Possibly add concrete to adjacent cells depending on whether this + ** concrete is in an odd or even row. + */ + if (Cell_X(cell) & 0x01) { + newcell = Adjacent_Cell((CELL)(cellptr->Cell_Number()), FACING_W); + } else { + newcell = Adjacent_Cell((CELL)(cellptr->Cell_Number()), FACING_E); + } + if (Map[newcell].Overlay != OVERLAY_CONCRETE) { + Class->Create_And_Place(newcell); + } + + /* + ** The display attributes must be recalculated for all adjacent + ** cells since their shape can be altered by the presence of + ** concrete at this location. + */ + static FacingType _face[4] = {FACING_N, FACING_E, FACING_S, FACING_W}; + + for (int index = 0; index < (sizeof(_face)/sizeof(_face[0])); index++) { + cellptr->Adjacent_Cell(_face[index]).Concrete_Calc(); + } + } + } + } + } + + /* + ** ***** Is this really needed? + */ + cellptr->Recalc_Attributes(); + } + Delete_This(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * * + * This routine is used to load a scenario's overlay data. The overlay objects are read * + * from the INI file and then created on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Specifically forbid manual crates in multiplayer scenarios. * + *=============================================================================================*/ +void OverlayClass::Read_INI(char *buffer) +{ + char *tbuffer; + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + CELL cell; + OverlayType classid; + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + classid = OverlayTypeClass::From_Name(strtok(buf, ",\n\r")); + + /* + ** Don't allow placement of crates in the multiplayer scenarios. + */ + if (classid != OVERLAY_NONE && (GameToPlay == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate)) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * OverlayClass::Write_INI -- Writes the overlay data to an INI file. * + * * + * This is used to output the overlay data to a scenario INI file. Typically, this is * + * only used by the scenario editor. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * cellptr = &Map[index]; + + if (cellptr->Overlay != OVERLAY_NONE) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s", OverlayTypeClass::As_Reference(cellptr->Overlay).IniName); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/OVERLAY.H b/TIBERIANDAWN/OVERLAY.H new file mode 100644 index 000000000..007f384b7 --- /dev/null +++ b/TIBERIANDAWN/OVERLAY.H @@ -0,0 +1,109 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\overlay.h_v 2.16 16 Oct 1995 16:44:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the overlay object. Overlay objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class OverlayClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + OverlayClass(void); + OverlayClass(OverlayType type, CELL pos=-1, HousesType = HOUSE_NONE); + virtual ~OverlayClass(void) {if (GameActive) OverlayClass::Limbo();}; + operator OverlayType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_OVERLAY;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(char *); + static void Write_INI(char *); + static char *INI_Name(void) {return "OVERLAY";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Virtual support functionality. + */ + virtual bool Mark(MarkType); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Draw_It(int , int , WindowNumberType ) {}; + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + /* + ** This is used to control the marking process of the overlay. If this is + ** set to a valid house number, then the cell that the overlay is marked down + ** upon will be flagged as being owned by the specified house. + */ + static HousesType ToOwn; + + /* + ** This is a pointer to the overlay object's class. + */ + OverlayTypeClass const * const Class; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/PACKET.CPP b/TIBERIANDAWN/PACKET.CPP new file mode 100644 index 000000000..2d0b33a4b --- /dev/null +++ b/TIBERIANDAWN/PACKET.CPP @@ -0,0 +1,461 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +//#include +#include + +//enum {false=0,true=1}; +//typedef int bool; + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass(void) +{ + FieldClass *current; + FieldClass *next; + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete current; + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *(unsigned short *)curbuf; + curbuf += sizeof (unsigned short); + Size = ntohs(Size); + ID = *(short *)curbuf; + curbuf += sizeof (short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *(unsigned short *)curbuf = (unsigned short)htons((unsigned short)size); + curbuf += sizeof (unsigned short); + *(short *)curbuf = htons(ID); + curbuf += sizeof (short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + + // + // If there is any pad left over, make sure you memset it + // to zeros, so it looks like a pad. + // + if (pad) { + memset(curbuf, 0, pad); + curbuf += pad; + } + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +#pragma warning(disable:4996) +bool PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, min(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} diff --git a/TIBERIANDAWN/PACKET.H b/TIBERIANDAWN/PACKET.H new file mode 100644 index 000000000..367d80262 --- /dev/null +++ b/TIBERIANDAWN/PACKET.H @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __PACKET_H +#define __PACKET_H + + +#include "field.h" + +class PacketClass { + public: + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(void); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + bool Get_Field(char *id, char &data); + bool Get_Field(char *id, unsigned char &data); + bool Get_Field(char *id, short &data); + bool Get_Field(char *id, unsigned short &data); + bool Get_Field(char *id, long &data); + bool Get_Field(char *id, unsigned long &data); + bool Get_Field(char *id, char *data); + bool Get_Field(char *id, void *data, int &length); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + + +#endif diff --git a/TIBERIANDAWN/PAGFAULT.ASM b/TIBERIANDAWN/PAGFAULT.ASM new file mode 100644 index 000000000..48112bea5 --- /dev/null +++ b/TIBERIANDAWN/PAGFAULT.ASM @@ -0,0 +1,222 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +DIVIDE_ERROR equ 00h +RESET_VIDEO_MODE equ -1 + +global C Install_Page_Fault_Handle : NEAR +global C Set_Video_Mode : NEAR +global C Remove_Mouse : NEAR +global C Remove_Keyboard_Interrupt : NEAR +global C Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + + Old_Div_Error_handle DF ? + Div_Error_SS DD ? + Div_Error_ESP DD ? + + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault and div Error * +;* handles. * +;* This function will install a new page fault handle and Div Error * +;* so in the event that we have a program crash these handles will * +;* remove all interrupts and then will chain to the default Exception * +;* Handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ; Install_Page_Fault_Handle + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + + ; Install_Divide_Error_Handle + mov eax,0202h ; get address of exception handle + mov bl,DIVIDE_ERROR + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default fault handle + mov [dword ptr Old_Div_Error_handle],edx + mov [word ptr Old_Div_Error_handle+4],cx + + ; redirect default handle to a new Divede Handle + mov eax,0203h + mov bl,DIVIDE_ERROR + mov cx,cs + lea edx,[Divide_Error_Handle] + int DPMI_INTR + + ??exit: + + + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + + +;*************************************************************************** +;* Divide_Error -- * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Divide_Error_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Div_Error_SS],eax + mov [Div_Error_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Fault stack frame + mov eax,[Div_Error_SS] + mov ss , ax + mov esp, [Div_Error_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Div_Error_handle] + + ENDP Divide_Error_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END diff --git a/TIBERIANDAWN/PHONE.H b/TIBERIANDAWN/PHONE.H new file mode 100644 index 000000000..bcb6f6599 --- /dev/null +++ b/TIBERIANDAWN/PHONE.H @@ -0,0 +1,66 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\phone.h_v 1.9 16 Oct 1995 16:47:58 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PHONE.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/28/95 * + * * + * Last Update : April 28, 1995 [BRR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PHONE_H +#define PHONE_H + +/* +***************************** Class Declaration ***************************** +*/ +class PhoneEntryClass +{ + public: + enum PhoneEntryEnum { + PHONE_MAX_NAME = 21, + PHONE_MAX_NUM = 21 + }; + + PhoneEntryClass(void) {}; + ~PhoneEntryClass() {}; + + operator == (PhoneEntryClass &obj) + { return (memcmp (Name,obj.Name,strlen(Name))==0);} + operator != (PhoneEntryClass &obj) + { return (memcmp (Name,obj.Name,strlen(Name))!=0);} + operator > (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) > 0);} + operator < (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) < 0);} + operator >= (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) >= 0);} + operator <= (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) <= 0);} + + SerialSettingsType Settings; + char Name[ PHONE_MAX_NAME ]; // destination person's name + char Number[ PHONE_MAX_NUM ]; // phone # +}; + +#endif diff --git a/TIBERIANDAWN/POWER.CPP b/TIBERIANDAWN/POWER.CPP new file mode 100644 index 000000000..021a0ad36 --- /dev/null +++ b/TIBERIANDAWN/POWER.CPP @@ -0,0 +1,459 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\power.cpv 2.18 16 Oct 1995 16:52:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : August 7, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PowerClass::AI -- Process the power bar logic. * + * PowerClass::Draw_It -- Renders the power bar graphic. * + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * PowerClass::One_Time -- One time processing for the power bar. * + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * Power_Height -- Given a value figure where it falls on bar * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Points to the shape to use for the "desired" power level indicator. +*/ +void const * PowerClass::PowerShape; +void const * PowerClass::PowerBarShape; + +PowerClass::PowerButtonClass PowerClass::PowerButton; + + +/*********************************************************************************************** + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * * + * This is the default constructor for the power bar class. It doesn't really do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +PowerClass::PowerClass(void) +{ + IsToRedraw = false; + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; +} + + +/*********************************************************************************************** + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * * + * This routine is called in preparation for the start of a scenario. The power bar is * + * initialized into the null state by this routine. As soon as the scenario starts, the * + * power bar will rise to reflect the actual power output and drain. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Init_Clear(void) +{ + RadarClass::Init_Clear(); + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; +} + + +/*********************************************************************************************** + * PowerClass::One_Time -- One time processing for the power bar. * + * * + * This routine is for code that truly only needs to be done once per game run. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void PowerClass::One_Time(void) +{ + RadarClass::One_Time(); + + int factor = Get_Resolution_Factor(); + PowX = SeenBuff.Get_Width() - Map.RadWidth; + PowY = Map.RadY+Map.RadHeight + (13 << factor); + PowWidth = 8 << factor; + PowHeight = SeenBuff.Get_Height() - PowY; + PowLineSpace = 5 << factor; + PowLineWidth = PowWidth - 4; + + PowerButton.X = PowX; + PowerButton.Y = PowY; + PowerButton.Width = PowWidth-1; + PowerButton.Height = PowHeight; + + PowerShape = MixFileClass::Retrieve((factor)? "HPOWER.SHP" :"POWER.SHP"); + PowerBarShape = Hires_Retrieve("PWRBAR.SHP"); +} + + +/*********************************************************************************************** + * PowerClass::Draw_It -- Renders the power bar graphic. * + * * + * This routine will draw the power bar graphic to the LogicPage. * + * * + * INPUT: complete -- Should the power bar be redrawn even if it isn't specifically flagged * + * to do so? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/27/1994 JLB : Changes power bar color depending on amount of power. * + *=============================================================================================*/ +void PowerClass::Draw_It(bool complete) +{ + static int _modtable[]={ + 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 + }; + int power_color; + + if (complete || IsToRedraw) { +// PowX = TacPixelX + TacWidth*ICON_PIXEL_W; // X position of upper left corner of power bar. + + if (LogicPage->Lock()){ + + if (Map.IsSidebarActive) { + IsToRedraw = false; + + /* + ** 1st get the height of the filled section of the power bar + */ + int bottom = PowY + PowHeight - 1; + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + power_height = Bound(power_height, 0, PowHeight - 2); + drain_height = Bound(drain_height, 0, PowHeight - 2); + + /* + ** Create a clip region to draw the unfilled section of the bar + */ + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = bottom-power_height; + + /* + ** Draw the unfilled section + */ + CC_Draw_Shape(PowerBarShape, 0, PowX, PowY, WINDOW_CUSTOM, SHAPE_WIN_REL); + CC_Draw_Shape(PowerBarShape, 1 ,PowX, PowY+100, WINDOW_CUSTOM, SHAPE_WIN_REL); + + + /* + ** Set up the clip region for the filled section + */ + WindowList[WINDOW_CUSTOM][WINDOWY] = bottom-power_height; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = SeenBuff.Get_Height() - WindowList[WINDOW_CUSTOM][WINDOWY]; + + /* + ** What color is the filled section? + */ + if (power_height) { + power_color = 0; //green + + if (PlayerPtr->Drain > PlayerPtr->Power) { + power_color = 2; + } + if (PlayerPtr->Drain > (PlayerPtr->Power * 2)) { + power_color = 4; + } + + /* + ** Draw the filled section + */ + CC_Draw_Shape(PowerBarShape, 2+power_color, + PowX, + PowY - WindowList[WINDOW_CUSTOM][WINDOWY], + WINDOW_CUSTOM, + SHAPE_WIN_REL); + + CC_Draw_Shape(PowerBarShape, 3+power_color, + PowX, + PowY - WindowList[WINDOW_CUSTOM][WINDOWY] + 100, + WINDOW_CUSTOM, + SHAPE_WIN_REL); + } + + /* + ** Draw the power drain threshold marker. + */ + CC_Draw_Shape(PowerShape, 0, PowX, bottom - drain_height + 1, WINDOW_MAIN, SHAPE_NORMAL); + + } + LogicPage->Unlock(); + } + } + RadarClass::Draw_It(complete); +} + + +/*********************************************************************************************** + * PowerClass::AI -- Process the power bar logic. * + * * + * Use this routine to process the power bar logic. This consists of animation effects. * + * * + * INPUT: input -- The player input value to be consumed or ignored as appropriate. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void PowerClass::AI(KeyNumType &input, int x, int y) +{ +// if (!IsActive) { +// IsActive = true; +// IsToRedraw = true; +// Flag_To_Redraw(false); +// } + + if (Map.IsSidebarActive /*IsActive*/) { + int olddrain = DrainHeight; + int oldpower = PowerHeight; + + + /* + ** If the recorded power value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Power != RecordedPower) { + DesiredPowerHeight = Power_Height(PlayerPtr->Power); + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; + if (PowerHeight > DesiredPowerHeight) { + PowerDir = -1; + } else if (PowerHeight < DesiredPowerHeight) { + PowerDir = 1; + } else { + PowerBounce = 0; + } + } + + /* + ** If the recorded drain value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Drain != RecordedDrain) { + DesiredDrainHeight = Power_Height(PlayerPtr->Drain); + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; + if (DrainHeight > DesiredDrainHeight) { + DrainDir = -1; + } else if (DrainHeight < DesiredDrainHeight) { + DrainDir = 1; + } else { + DrainBounce = 0; + } + } + + if (DrainBounce && DrainHeight == DesiredDrainHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + DrainBounce--; + } else { + /* + ** If we need to move the drain height then do so. + */ + if (DrainHeight != DesiredDrainHeight) { + DrainHeight += DrainDir; + } + } + + if (PowerBounce && PowerHeight == DesiredPowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + PowerBounce--; + } else { + /* + ** If we need to move the power height then do so. + */ + if (PowerHeight != DesiredPowerHeight) { + PowerHeight += PowerDir; + } + } + + if (olddrain != DrainHeight || oldpower != PowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + RadarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * * + * This routine will examine a refresh list request and determine if the sidebar would be * + * affect. If so, it will flag the sidebar to be redrawn. * + * * + * INPUT: cell -- The cell that the offset list is base on. * + * * + * list -- The list of cell offset used to flag for redraw. If the special sidebar * + * affecting cell magic offset number is detected, the sidebar is flagged * + * for redraw and the magic offset is removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + RadarClass::Refresh_Cells(cell, list); +} + + +/*************************************************************************** + * PowHeight -- Given a value figure where it falls on bar * + * * + * INPUT: int value - the value we are testing * + * * + * OUTPUT: int the height of the point that this value is on graph * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 PWG : Created. * + *=========================================================================*/ +int PowerClass::Power_Height(int value) +{ + int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power + + /* + ** Loop through the diffrent hundreds figuring out the fractional piece + ** of each. + */ + for (int lp = 0; lp < num; lp ++) { + retval = retval + (((PowHeight - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; + } + + /* + ** Adjust the retval to factor in the remainder + */ + if (value) { + retval = retval + (((((PowHeight - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); + } + + retval = Bound(retval, 0, PowHeight -2); + return(retval); +} + + +/*********************************************************************************************** + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * * + * This routine handles input on the power bar area. Since no input is used for the power * + * bar, this routine just pops up appropriate help text for the power bar. * + * * + * INPUT: flags -- The event flags that triggered this action call. * + * * + * key -- The key code (if any) associated with the trigger event. * + * * + * OUTPUT: Should further button processing be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +{ + if (!Map.IsSidebarActive) { + return(false); + } + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + Map.Override_Mouse_Shape(MOUSE_NORMAL); + if (PlayerPtr->Power_Fraction() < 0x0100 && PlayerPtr->Power > 0) { + Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, CC_GREEN); + } else { + Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, CC_GREEN); + } + GadgetClass::Action(flags, key); + return(true); +} + + diff --git a/TIBERIANDAWN/POWER.H b/TIBERIANDAWN/POWER.H new file mode 100644 index 000000000..5223e6338 --- /dev/null +++ b/TIBERIANDAWN/POWER.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\power.h_v 2.16 16 Oct 1995 16:48:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef POWER_H +#define POWER_H + +#include "radar.h" + +class PowerClass : public RadarClass +{ + public: + int PowX; + int PowY; + int PowWidth; + int PowHeight; + int PowLineSpace; + int PowLineWidth; + + + PowerClass(); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + + virtual void Init_Clear(void); // Clears all to known state + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Refresh_Cells(CELL cell, short const *list); +// virtual void Must_Redraw_Sidebar(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + unsigned IsToRedraw:1; + + protected: + /* + ** This gadget is used to capture mouse input on the power bar. + */ + class PowerButtonClass : public GadgetClass { + public: + PowerButtonClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class PowerClass; + }; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static PowerButtonClass PowerButton; + + enum PowerEnums { + POWER_STEP_LEVEL=100, + POWER_STEP_FACTOR=6, + }; + + private: + int Power_Height(int value); + + unsigned IsActive:1; + + int RecordedDrain; + int RecordedPower; + int DesiredDrainHeight; + int DesiredPowerHeight; + int DrainHeight; + int PowerHeight; + int DrainBounce; + int PowerBounce; + short PowerDir; + short DrainDir; + + /* + ** Points to the shape to use for the "desired" power level indicator. + */ + static void const * PowerShape; + + /* + ** Points to the shapes to be used for drawing the power bar + */ + static void const * PowerBarShape; +}; + +#endif diff --git a/TIBERIANDAWN/PROFILE.CPP b/TIBERIANDAWN/PROFILE.CPP new file mode 100644 index 000000000..335592b18 --- /dev/null +++ b/TIBERIANDAWN/PROFILE.CPP @@ -0,0 +1,654 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\profile.cpv 2.18 16 Oct 1995 16:51:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWGetPrivateProfileInt -- Fetches integer value from INI. * + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * WWGetPrivateProfileString -- Fetch string from INI. * + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Read_Private_Config_Struct -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +bool Read_Private_Config_Struct (char *profile, NewConfigType *config) +{ + config->DigitCard = WWGetPrivateProfileHex ("Sound", "Card", profile); + config->IRQ = WWGetPrivateProfileInt ("Sound", "IRQ", 0,profile); + config->DMA = WWGetPrivateProfileInt ("Sound", "DMA", 0,profile); + config->Port = WWGetPrivateProfileHex ("Sound", "Port", profile); + config->BitsPerSample= WWGetPrivateProfileInt ("Sound", "BitsPerSample",0,profile); + config->Channels = WWGetPrivateProfileInt ("Sound", "Channels", 0,profile); + config->Reverse = WWGetPrivateProfileInt ("Sound", "Reverse", 0,profile); + config->Speed = WWGetPrivateProfileInt ("Sound", "Speed", 0,profile); + WWGetPrivateProfileString ("Language", "Language", NULL, config->Language, 3, profile); + + return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); +} + + +/*************************************************************************** + * Get_Private_Profile_Hex -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile) +{ + // This buffer was overrun at the end due to the forced termination at MAX_ENTRY_SIZE below + char buffer[MAX_ENTRY_SIZE + 1]; // Integer staging buffer. + unsigned card; + + memset (buffer, '0', MAX_ENTRY_SIZE); // MAX_ENTRY_SIZE = 15 + buffer[MAX_ENTRY_SIZE] = '\0'; + + WWGetPrivateProfileString(section, entry, "0", buffer, MAX_ENTRY_SIZE, profile); + + if (strlen (buffer) > 0) { + sscanf (buffer, "%x", &card); + } else { + card = 0; + } + + return(card); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileInt -- Fetches integer value. * + * * + * INPUT: * + * section section to read from * + * * + * entry name of entry to read * + * * + * def default value, if entry isn't found * + * * + * profile buffer containing INI data * + * * + * OUTPUT: * + * integer requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile) +{ + char buffer[16]; // Integer staging buffer. + + /* + ** Store the default in the buffer. + */ + sprintf(buffer, "%d", def); + + /* + ** Get the buffer; use itself as the default. + */ + WWGetPrivateProfileString(section, entry, buffer, buffer, 15, profile); + + /* + ** Convert to int & return. + */ + return(atoi(buffer)); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * * + * entry name of entry to write; if NULL, the entire section is deleted * + * * + * value value to write * + * * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile) +{ + char buffer[250]; // Working section buffer. + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Generate string to save. + */ + sprintf(buffer, "%d", value); + + /* + ** Save the string. + */ + return(WWWritePrivateProfileString(section, entry, buffer, profile)); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileString -- Fetch game override string. * + * * + * INPUT: * + * section section name to read from * + * * + * entry name of entry to read; if NULL, all entry names are returned * + * * + * def default string to use if not found; can be NULL * + * * + * retbuffer buffer to store result in * + * * + * retlen max length of return buffer * + * * + * profile INI buffer * + * * + * OUTPUT: * + * ptr to entry found in INI buf; NULL if not found * + * * + * WARNINGS: * + * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * + * to disk. This routine must take this into consideration, by searching * + * for \n when scanning backward, and for \r when scanning forward. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile) +{ + char * workptr, // Working pointer into profile block. + * altworkptr; // Alternate work pointer. + char sec[50]; // Working section buffer. + char *retval; // Start of section or entry pointer. + char * next; // Pointer to start of next section (or EOF). + char c,c2; // Working character values. + int len; // Working substring length value. + int entrylen; // Byte length of specified entry. + char *orig_retbuf; // original retbuffer ptr + + /* + ** Fill in the default value just in case the entry could not be found. + */ + if (retbuffer) { + if (def) { + strncpy(retbuffer, def, retlen); + } + retbuffer[retlen-1] = '\0'; + orig_retbuf = retbuffer; + } + + /* + ** Make sure a profile string was passed in + */ + if (!profile || !section) { + return(retbuffer); + } + + /* + ** Build section string to match file image. + */ + sprintf(sec, "[%s]", section); // sec = section name including []'s + strupr(sec); + len = strlen(sec); // len = section name length, incl []'s + + /* + ** Scan for a matching section + */ + retval = profile; + workptr = profile; + for (;;) { + + /* + ** 'workptr' = start of next section + */ + workptr = strchr(workptr, '['); + + /* + ** If the end has been reached without finding the desired section + ** then abort with a failure flag. + */ + if (!workptr) { + return(NULL); + } + + /* + ** 'c' = character just before the '[' + */ + if (workptr==profile) { + c = '\n'; + } else { + c = *(workptr-1); + } + + /* + ** If this is the section name & the character before is a newline, + ** process this section + */ + if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { + + /* + ** Skip work pointer to start of first valid entry. + */ + workptr += len; + while (isspace(*workptr)) { + workptr++; + } + + /* + ** If the section name is empty, we will have stepped onto the start + ** of the next section name; inserting new entries here will leave + ** a blank line between this section's name & 1st entry. So, check + ** for 2 newlines in a row & step backward. + */ + if (workptr - profile > 4) { + if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') + workptr -= 2; + } + + /* + ** 'next = end of section or end of file. + */ + next = strchr(workptr, '['); + for (;;) { + if (next) { + + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if (*(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = workptr + strlen(workptr)-1; + break; + } + } + + /* + ** If a specific entry was specified then return with the associated + ** string. + */ + if (entry) { + retval = workptr; + entrylen = strlen(entry); + + for (;;) { + /* + ** Search for the 1st character of the entry + */ + workptr = strchr(workptr, *entry); + + /* + ** If the end of the file has been reached or we have spilled + ** into the next section, then abort + */ + if (!workptr || workptr >= next) { + return(NULL); + } + + /* + ** 'c' = character before possible entry; must be a newline + ** 'c2' = character after possible entry; must be '=' or space + */ + c = *(workptr-1); + c2 = *(workptr+entrylen); + + /* + ** Entry found; extract it + */ + if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && + (c2 == '=' || isspace(c2))) { + retval = workptr; + workptr += entrylen; // skip entry name + workptr = strchr(workptr, '='); // find '=' + + /* + ** 'altworkptr' = next newline; \r is used here since we're + ** scanning forward! + */ + if (workptr) { + altworkptr = strchr(workptr, '\r'); // find next newline + } + + /* + ** Return if there was no '=', or if the newline is before + ** the next '=' + */ + if (workptr == NULL || altworkptr < workptr) { + return(retval); + } + + /* + ** Skip any white space after the '=' and before the first + ** valid character of the parameter. + */ + workptr++; // Skip the '='. + while (isspace(*workptr)) { + + /* + ** Just return if there's no entry past the '='. + */ + if (workptr >= altworkptr) + return(retval); + + workptr++; // Skip the whitespace + } + + /* + ** Copy the entry into the return buffer. + */ + len = (int)(altworkptr - workptr); + if (len > retlen-1) { + len = retlen-1; + } + + if (retbuffer) { + memcpy(retbuffer, workptr, len); + *(retbuffer + len) = '\0'; // Insert trailing null. + strtrim(retbuffer); + } + return(retval); + } + + /* + ** Entry was not found; go to the next one + */ + workptr++; + } + } else { + + /* + ** No entry was specified, so build a list of all entries. + ** 'workptr' is at 1st entry after section name + ** 'next' is next bracket, or end of file + */ + retval = workptr; + + if (retbuffer) { + + /* + ** Keep accumulating the identifier strings in the retbuffer. + */ + while (workptr && workptr < next) { + altworkptr = strchr(workptr, '='); // find '=' + + if (altworkptr && altworkptr < next) { + int length; // Length of ID string. + + length = (int)(altworkptr - workptr); + + /* + ** Make sure we don't write past the end of the retbuffer; + ** add '3' for the 3 NULL's at the end + */ + if (retbuffer - orig_retbuf + length + 3 < retlen) { + memcpy(retbuffer, workptr, length); // copy entry name + *(retbuffer+length) = '\0'; // NULL-terminate it + strtrim(retbuffer); // trim spaces + retbuffer += strlen(retbuffer)+1; // next pos in dest buf + } else { + break; + } + + /* + ** Advance the work pointer to the start of the next line + ** by skipping the end of line character. + */ + workptr = strchr(altworkptr, '\n'); + if (!workptr) { + break; + } + workptr++; + } else { + + /* + ** If no '=', break out of loop + */ + break; + } + } + + /* + ** Final trailing terminator. Make double sure the double + ** trailing null is added. + */ + *retbuffer++ = '\0'; + *retbuffer++ = '\0'; + } + break; + } + } else { + + /* + ** Section name not found; go to the next bracket & try again + ** Advance past '[' and keep scanning. + */ + workptr++; + } + } + + return(retval); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * entry name of entry to write; if NULL, the section is deleted * + * string string to write; if NULL, the entry is deleted * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * This function has to translate newlines into \r\n sequences. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile) +{ + char buffer[250]; // Working section buffer + char *offset; + char *next; // ptr to next section + char c; // Working character value + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Try to find the section. WWGetPrivateProfileString with NULL entry name + ** will return all entry names in the given buffer, truncated to the given + ** buffer length. 'offset' will point to 1st entry in the section, NULL if + ** section not found. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + + /* + ** If the section could not be found, then add it to the end. Don't add + ** anything if a removal of an entry is requested (it is obviously already + ** non-existent). Make sure two newlines precede the section name. + */ + if (!offset && entry) { + sprintf(buffer, "\r\n[%s]\r\n", section); + strcat(profile, buffer); + } + + /* + ** If the section is there and 'entry' is NULL, remove the entire section + */ + if (offset && !entry) { + + /* + ** 'next = end of section or end of file. + */ + next = strchr(offset, '['); + for (;;) { + if (next) { + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if ( *(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = offset + strlen(offset); + break; + } + } + + /* + ** Remove the section + */ + strcpy(offset,next); + + return(true); + } + + /* + ** Find the matching entry within the desired section. A NULL return buffer + ** with 0 length will just return the offset of the found entry, NULL if + ** entry not found. + */ + offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); + + /* + ** Remove any existing entry + */ + if (offset) { + int eol; // Working EOL offset. + + /* + ** Get # characters up to newline; \n is used since we're after the end + ** of this line + */ + eol = strcspn(offset, "\n"); + + /* + ** Erase the entry by strcpy'ing the entire INI file over this entry + */ + if (eol) { + strcpy(offset, offset + eol + 1); + } + } else { + + /* + ** Entry doesn't exist, so point 'offset' to the 1st entry position in + ** the section. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + } + + /* + ** Add the desired entry. + */ + if (entry && string) { + + /* + ** Generate entry string. + */ + sprintf(buffer, "%s=%s\r\n", entry, string); + + /* + ** Make room for new entry. + */ + memmove(offset+strlen(buffer), offset, strlen(offset)+1); + + /* + ** Copy the entry into the INI buffer. + */ + memcpy(offset, buffer, strlen(buffer)); + } + + return(true); +} diff --git a/TIBERIANDAWN/QUEUE.CPP b/TIBERIANDAWN/QUEUE.CPP new file mode 100644 index 000000000..e0c9aaf4b --- /dev/null +++ b/TIBERIANDAWN/QUEUE.CPP @@ -0,0 +1,4214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\queue.cpv 2.24 11 Oct 1995 13:47:40 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions for Queueing Events: * + * Queue_Mission -- Queue a mega mission event. * + * Queue_Options -- Queue the options event. * + * Queue_Exit -- Add the exit game event to the queue. * + * * + * Functions for processing Queued Events: * + * Queue_AI -- Process all queued events. * + * Queue_AI_Normal -- Process all queued events. * + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * Main Multiplayer Queue Logic: * + * Wait_For_Players -- Waits for other systems to come on-line * + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * Process_Send_Period -- timing for sending packets every 'n' frames * + * Send_Packets -- sends out events from the OutList * + * Send_FrameSync -- Sends a FRAMESYNC packet * + * Process_Receive_Packet -- processes an incoming packet * + * Process_Serial_Packet -- Handles an incoming serial packet * + * Can_Advance -- determines if it's OK to advance to the next frame * + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * Handle_Timeout -- attempts to reconnect; if fails, bails. * + * Stop_Game -- stops the game * + * * + * Packet Compression / Decompression: * + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * Add_Compressed_Events -- adds compressed events to a packet * + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * Extract_Uncompressed_Events -- extracts events from a packet * + * Extract_Compressed_Events -- extracts events from a packet * + * * + * DoList Management: * + * Execute_DoList -- Executes commands from the DoList * + * Clean_DoList -- Cleans out old events from the DoList * + * Queue_Record -- Records the DoList to disk * + * Queue_Playback -- plays back queue entries from a record file * + * * + * Debugging: * + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * Add_CRC -- Adds a value to a CRC * + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * Init_Queue_Mono -- inits mono display * + * Update_Queue_Mono -- updates mono display * + * Print_Framesync_Values -- displays frame-sync variables * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "tcpip.h" + +/********************************** Defines *********************************/ +#define SHOW_MONO 1 + +int tmp_flag = 0; + +/********************************** Globals *********************************/ +//--------------------------------------------------------------------------- +// GameCRC is the current computed CRC value for this frame. +// CRC[] is a record of our last 32 game CRC's. +// ColorNames is for debug output in Print_CRCs +//--------------------------------------------------------------------------- +#ifndef DEMO +static unsigned long GameCRC; +static unsigned long CRC[32] = + {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}; + +static char *ColorNames[6] = { + "Yellow", + "Red", + "BlueGreen", + "Orange", + "Green", + "Blue", +}; +#endif //DEMO + +//........................................................................... +// Mono debugging variables: +// NetMonoMode: 0 = show connection output, 1 = flowcount output +// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen +// IsMono: used for taking control of Mono screen away from the engine +//........................................................................... +#ifndef DEMO +int NetMonoMode = 1; +int NewMonoMode = 1; +static int IsMono = 0; +#endif //DEMO + +//--------------------------------------------------------------------------- +// Several routines return various codes; here's an enum for all of them. +//--------------------------------------------------------------------------- +typedef enum RetcodeEnum { + RC_NORMAL, // no news is good news + RC_PLAYER_READY, // a new player has been heard from + RC_SCENARIO_MISMATCH, // scenario mismatch + RC_DOLIST_FULL, // DoList is full + RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed + RC_PLAYER_LEFT, // modem: other player left the game + RC_HUNG_UP, // modem has hung up + RC_NOT_RESPONDING, // other player not responding (timeout/hung up) + RC_CANCEL, // user cancelled +} RetcodeType; + + +/********************************* Prototypes *******************************/ +//........................................................................... +// Main multiplayer queue logic +//........................................................................... +static void Queue_AI_Normal(void); +#ifndef DEMO +static void Queue_AI_Multiplayer(void); +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv); +static void Generate_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Process_Time_Event(ConnManClass *net); +static int Process_Send_Period(ConnManClass *net); +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent); +static void Send_FrameSync(ConnManClass *net, int cmd_count); +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time); +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh); +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static void Stop_Game(void); +#endif //DEMO + +//........................................................................... +// Packet compression/decompression: +//........................................................................... +#ifndef DEMO +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap); +static int Breakup_Receive_Packet(void *buf, int bufsize ); +#endif //DEMO +int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Extract_Uncompressed_Events(void *buf, int bufsize); +int Extract_Compressed_Events(void *buf, int bufsize); + +//........................................................................... +// DoList management: +//........................................................................... +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, TCountDownTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +static void Clean_DoList(ConnManClass *net); +#ifndef DEMO +static void Queue_Record(void); +static void Queue_Playback(void); +#endif //DEMO + +//........................................................................... +// Debugging: +//........................................................................... +#ifndef DEMO +static void Compute_Game_CRC(void); +static void Init_Queue_Mono(ConnManClass *net); +static void Update_Queue_Mono(ConnManClass *net, int flow_index); +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent); +#endif //DEMO +void Add_CRC(unsigned long *crc, unsigned long val); +void Print_CRCs(EventClass *); + +extern void Keyboard_Process(KeyNumType &input); +void Dump_Packet_Too_Late_Stuff(EventClass *event); + +extern void Register_Game_End_Time(void); +extern void Send_Statistics_Packet(void); + +/*************************************************************************** + * Queue_Mission -- Queue a mega mission event. * + * * + * This routine is called when the player causes a change to a game unit. * + * The event that initiates the change is queued to as a result of a call * + * to this routine. * + * * + * INPUT: * + * whom Whom this mission request applies to (a friendly unit). * + * mission The mission to assign to this object. * + * target The target of this mission (if any). * + * dest The movement destination for this mission (if any). * + * * + * OUTPUT: * + * Was the mission request queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, + TARGET destination) +{ + if (! OutList.Add(EventClass(whom, mission, target, destination))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Mission */ + + +/*************************************************************************** + * Queue_Options -- Queue the options event. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the options screen event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Options(void) +{ + if (! OutList.Add(EventClass(EventClass::OPTIONS))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Options */ + + +/*************************************************************************** + * Queue_Exit -- Add the exit game event to the queue. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the exit event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Exit(void) +{ + if (! OutList.Add(EventClass(EventClass::EXIT))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Exit */ + + +/*************************************************************************** + * Queue_AI -- Process all queued events. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +void Queue_AI(void) +{ +#ifdef DEMO + Queue_AI_Normal(); +#else //DEMO + + if (PlaybackGame) { + Queue_Playback(); + } + + else { + + switch (GameToPlay) { + + case GAME_NORMAL: + Queue_AI_Normal(); + break; + + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: + case GAME_INTERNET: + Queue_AI_Multiplayer(); + break; + } + } +#endif //DEMO + +} /* end of Queue_AI */ + + +/*************************************************************************** + * Queue_AI_Normal -- Process all queued events. * + * * + * This is the "normal" version of the queue management routine. It does * + * the following: * + * - Transfers items in the OutList to the DoList * + * - Executes any commands in the DoList that are supposed to be done on * + * this frame # * + * - Cleans out the DoList * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +static void Queue_AI_Normal(void) +{ + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + OutList.Next(); + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ +#ifndef DEMO + if (RecordGame) { + Queue_Record(); + } +#endif //DEMO + + //------------------------------------------------------------------------ + // Execute the DoList + //------------------------------------------------------------------------ + if (!Execute_DoList(1,PlayerPtr->Class->House, NULL, NULL, NULL, + NULL, NULL)) { + GameActive = 0; + return; + } + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_AI_Normal */ + +#ifndef DEMO + +/*************************************************************************** + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * This is the network version of the queue management routine. It does * + * the following: * + * - If this is the 1st frame, waits for other systems to signal ready * + * - Generates a timing event, to allow the connection time to be dynamic * + * - Handles timing related to sending packets every 'n' frames * + * - Sends outgoing events * + * - Frame-syncs to the other systems (see below) * + * - Executes & cleans out the DoList * + * * + * The Frame-Sync'ing logic is the heart & soul of network play. It works * + * by ensuring that any system won't out-run the other system by more than * + * 'MaxAhead' frames; this in turn ensures that a packet's * + * execution frame # won't have been passed by the time that packet is * + * received by all systems. * + * * + * To achieve this, the system must keep track of all other system's * + * current frame #'s; these are stored in an array called 'their_frame[]'. * + * However, because current frame #'s are sent in FRAMEINFO packets, which * + * don't require an ACK, and command packets are sent in packets requiring * + * an ACK, it's possible for a command packet to get lost, and the next * + * frame's FRAMEINFO packet to not get lost; the other system may then * + * advance past the frame # the command is to execute on! So, to prevent * + * this, all FRAMEINFO packets include a CommandCount field. This value * + * tells the other system how many events it should have received by this * + * time. This system can therefore keep track of how many commands it's * + * actually received, and compare it to the CommandCount field, to see if * + * it's missed an event packet. The # of events we've received from each * + * system is stored in 'their_recv[]', and the # events they say they've * + * sent is stored in 'their_sent[]'. * + * * + * Thus, two conditions must be met in order to advance to the next frame: * + * - Our current frame # must be <= their_frame + MaxAhead * + * - their_recv[i] must be >= their_sent[i] * + * * + * 'their_frame[] is updated by Process_Receive_Packet() * + * 'their_recv[] is updated by Process_Receive_Packet() * + * 'their_sent[] is updated by Process_Receive_Packet() * + * 'my_sent' is updated by this routine. * + * * + * The only routines allowed to pop up dialogs are: * + * Wait_For_Players() (only pops up the reconnect dialog) * + * Execute_DoList() (tells if out of sync, or packet recv'd too late) * + * * + * Sign-off's are detected by: * + * - Timing out while waiting for a packet * + * - Detecting that the other player is now at the score screen or * + * connection dialog (serial) * + * - If we see an EventClass::EXIT event on the private channel * + * * + * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has * + * the following properites: * + * - It compresses packets, so that the minimum number of bytes are * + * transmitted. Packets are compressed by extracting all info common to * + * the events into the packet header, and then sending only the bytes * + * relevant to each type of event. For instance, if 100 infantry guys * + * are told to move to the same location, the command itself & the * + * location will be included in the 1st movement command only; after * + * that, there will be a rep count then 99 infantry TARGET numbers, * + * identifying all the infantry told to move. * + * - The protocol also only sends packets out every 'n' frames. This cuts * + * the data rate dramatically. It means that 'MaxAhead' must be * + * divisible by 'n'; also, the minimum value for 'MaxAhead' is * + * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO * + * packet gets missed. * + * * + * Note: For synchronization-waiting loops (like waiting to hear from all * + * other players, waiting to advance to the next frame, etc), use * + * Net.Num_Connections() rather than NumPlayers; this reflects the * + * actual # of connections, and can be "faked" into playing even when * + * there aren't any other players actually there. A typical example of * + * this is playing back a recorded game. For command-execution loops, use * + * NumPlayers. This ensures all commands get executed, even if * + * there isn't a human generating those commands. * + * * + * The modem works a little differently from the network in this respect: * + * - The connection has to stay "alive" even if the other player exits to * + * the join dialog. This prevents each system from timing out & hanging * + * the modem up. Thus, packets are sent back & forth & just thrown away,* + * but each system knows the other is still there. Messages may be sent * + * between systems, though. * + * - Destroy_Null_Connection doesn't hang up the modem, so * + * Num_Connections() still reports a value of 1 even though the other * + * player has left. * + * - Any waits on Num_Connections() must also check for * + * NumPlayers > 1, to keep from waiting forever if the other * + * guy has left * + * - Packets sent to a player who's left require no ACK * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_AI_Multiplayer(void) +{ + //........................................................................ + // Enums: + //........................................................................ + enum { + MIXFILE_RESEND_DELTA = 120, // ticks b/w resends + MIXFILE_TIMEOUT = 3600, // timeout waiting for mixfiles + FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog + FRAMESYNC_TIMEOUT = (25*60), // timeout waiting for frame sync packet + }; + + int timeout_factor = (GameToPlay == GAME_INTERNET) ? 6 : 1; + + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + ConnManClass *net; // ptr to access all multiplayer functions + EventClass packet; // for sending single frame-sync's + char *multi_packet_buf; // buffer for sending/receiving + int multi_packet_max; // max length of multi_packet_buf + + //........................................................................ + // Frame-sync'ing variables. Values in these arrays are stored in the + // order in which the connections are created. + // (ie net->Connection_Index(id)) + //........................................................................ + static long + their_frame[MAX_PLAYERS - 1]; // other players' frame #'s + static unsigned short + their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent + static unsigned short + their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others + static unsigned short + my_sent; // # cmds I've sent out + + //........................................................................ + // Other misc variables + //........................................................................ + int i; + RetcodeType rc; + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //------------------------------------------------------------------------ + // Initialize the packet buffer pointer & its max size + //------------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ +//PG_TO_FIX +#if (0) + multi_packet_buf = NullModem.BuildBuf; + multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType); + net = &NullModem; +#endif + } + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + multi_packet_buf = MetaPacket; + multi_packet_max = MetaSize; + net = &Ipx; + } + + //------------------------------------------------------------------------ + // Debug stuff + //------------------------------------------------------------------------ + Init_Queue_Mono(net); + Update_Queue_Mono (net, 0); + + //------------------------------------------------------------------------ + // If we've just started a game, or loaded a multiplayer game, we must + // wait for all other systems to signal ready. + //------------------------------------------------------------------------ + if (Frame==0) { + //..................................................................... + // Initialize static locals + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) { + their_frame[i] = -1; + their_sent[i] = 0; + their_recv[i] = 0; + TheirProcessTime[i] = -1; + } + my_sent = 0; + for (i = 0; i < 32; i++) { + CRC[i] = 0; + } + + //..................................................................... + // Send our initial FRAMESYNC packet + //..................................................................... + Send_FrameSync(net, my_sent); + + //..................................................................... + // Wait for the other guys + //..................................................................... + rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor, + MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { + if (rc == RC_NOT_RESPONDING) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + CCMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //..................................................................... + // Re-initialize frame numbers (in case somebody signed off while I was + // waiting for MIX files to load; we would have fallen through, but + // their frame # would still be -1). + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) + their_frame[i] = 0; + + //..................................................................... + // Reset the network response time computation, now that we're both + // sending data again (loading MIX files will have introduced + // deceptively large values). + //..................................................................... + net->Reset_Response_Time(); + + //..................................................................... + // Initialize the frame timers + //..................................................................... + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Process_Send_Period(net); + } + + } // end of Frame 0 wait + + //------------------------------------------------------------------------ + // Adjust connection timing parameters every 128 frames. + //------------------------------------------------------------------------ + else if ( (Frame & 0x007f) == 0) { + // + // If we're using the new spiffy protocol, do proper timing handling. + // If we're the net "master", compute our desired frame rate & new + // 'MaxAhead' value. + // + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // All systems will transmit their required process time. + // + Generate_Process_Time_Event(net); + + // + // The game "host" will transmit timing adjustment events. + // + if (MPlayerLocalID == MPlayerID[0]) { + Generate_Real_Timing_Event(net, my_sent); + } + } else { + // + // For the older protocols, do the old broken timing handling. + // + Generate_Timing_Event(net, my_sent); + } + } + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + //unsigned long save_crc = GameCRC; + //Print_CRCs((EventClass *)NULL); + //GameCRC = save_crc; + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (!Process_Send_Period(net)) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + } + + //------------------------------------------------------------------------ + // Send our data packet(s); update my command-sent counter + //------------------------------------------------------------------------ + my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max, + MPlayerMaxAhead, my_sent); + + //------------------------------------------------------------------------ + // If this is our first time through, we're done. + //------------------------------------------------------------------------ + if (Frame==0) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + + //------------------------------------------------------------------------ + // Frame-sync'ing: wait until it's OK to advance to the next frame. + //------------------------------------------------------------------------ + rc = Wait_For_Players (0, net, + (MPlayerMaxAhead << 3), + MAX ( net->Response_Time() * 3, (unsigned long)FRAMESYNC_DLG_TIME*timeout_factor ), + FRAMESYNC_TIMEOUT * (timeout_factor*2), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { + if (rc == RC_NOT_RESPONDING) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + CCMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (RecordGame) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(MPlayerMax, HOUSE_MULTI1, net, NULL, + their_frame, their_sent, their_recv)) { + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(net); + + if (IsMono) { + MonoClass::Disable(); + } + +} // end of Queue_AI_Multiplayer + + +/*************************************************************************** + * Wait_For_Players -- Waits for other systems to come on-line * + * * + * This routine performs the most critical logic in multiplayer; that of * + * synchronizing my frame number with those of the other systems. * + * * + * INPUT: * + * first_time 1 = 1st time this routine is called * + * net ptr to connection manager * + * resend_delta time (ticks) between FRAMESYNC resends * + * dialog_time time (ticks) until pop up a reconnect dialog * + * timeout time (ticks) until we give up the ghost * + * multi_packet_buf buffer to store packets in * + * my_sent # commands I've sent so far * + * their_frame array of their frame #'s * + * their_sent array of their CommandCount values * + * their_recv array of # cmds I've received from them * + * * + * OUTPUT: * + * RC_NORMAL OK to advance to the next frame * + * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg * + * RC_NOT_RESPONDING other player(s) not responding * + * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) * + * RC_DOLIST_FULL DoList was full * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv) +{ + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + EventClass *event; // event ptr for parsing incoming packets + int packetlen; // size of meta-packet sent, & received + int id; // id of other player + int messages_this_loop; // to limit # messages processed each loop + + //........................................................................ + // Variables used only if 'first_time': + //........................................................................ + int num_ready; // # players signalling ready + + //........................................................................ + // Timing variables + //........................................................................ + CountDownTimerClass retry_timer; // time between FRAMESYNC packet resends + CountDownTimerClass dialog_timer; // time to pop up a dialog + CountDownTimerClass timeout_timer; // general-purpose timeout + + //........................................................................ + // Dialog variables + //........................................................................ + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //........................................................................ + // Other misc variables + //........................................................................ + KeyNumType input; // for user input + int x,y; // for map input + RetcodeType rc; + + //------------------------------------------------------------------------ + // Wait to hear from all other players + //------------------------------------------------------------------------ + num_ready = 0; + retry_timer.Set (resend_delta, true); // time to retry + dialog_timer.Set (dialog_time, true); // time to show dlg + timeout_timer.Set (timeout, true); // time to bail out + + while (1) { + + Update_Queue_Mono (net, 2); + + //--------------------------------------------------------------------- + // Resend a frame-sync packet if longer than one propogation delay goes + // by; this prevents a "deadlock". If he's waiting for me to advance, + // but has missed my last few FRAMEINFO packets, I may be waiting for + // him to advance. Resending a FRAMESYNC ensures he knows what frame + // number I'm on. + //--------------------------------------------------------------------- + if (!retry_timer.Time()) { + retry_timer.Set (resend_delta, true); // time to retry + Update_Queue_Mono (net, 3); + Send_FrameSync(net, my_sent); + } + + //--------------------------------------------------------------------- + // Service the connections + //--------------------------------------------------------------------- + net->Service(); + + //--------------------------------------------------------------------- + // Pop up a reconnect dialog if enough time goes by + //--------------------------------------------------------------------- + if (!dialog_timer.Time() && SpecialDialog==SDLG_NONE) { + if (Process_Reconnect_Dialog(&timeout_timer, their_frame, + net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) { + return (RC_CANCEL); + } + reconnect_dlg = 1; + } + + //--------------------------------------------------------------------- + // Exit if too much time goes by (the other system has crashed or + // bailed) + //--------------------------------------------------------------------- + if (!timeout_timer.Time()) { + //.................................................................. + // For the first-time run, just give up; something's wrong. + //.................................................................. + if (first_time) { + return (RC_NOT_RESPONDING); + } + //.................................................................. + // Otherwise, we're in the middle of a game; so, the modem & + // network must deal with a timeout differently. + //.................................................................. + else { + Update_Queue_Mono (net, 4); + + if (Handle_Timeout(net, their_frame, their_sent, their_recv)) { + Map.Flag_To_Redraw(true); // erase modem reconnect dialog + Map.Render(); + retry_timer.Set (resend_delta, true); + dialog_timer.Set (dialog_time, true); + timeout_timer.Set (timeout, true); + } + else { + return (RC_NOT_RESPONDING); + } + } + } + + //--------------------------------------------------------------------- + // Check for an incoming message. We must still process commands + // even if 'first_time' is set, in case the other system got my 1st + // FRAMESYNC, but I didn't get his; he'll be at the next frame, and + // may be sending commands. + // We have to limit the number of incoming messages we handle; it's + // possible to go into an infinite loop processing modem messages. + //--------------------------------------------------------------------- + messages_this_loop = 0; + while ( (messages_this_loop++ < 5) && + net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) { + Update_Queue_Mono (net, 5); + + /*.................................................................. + Get an event ptr to the incoming message + ..................................................................*/ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------ + // Special processing for a modem game: process SERIAL packets + //------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + rc = Process_Serial_Packet(multi_packet_buf, first_time); + //............................................................... + // SERIAL packet received & processed + //............................................................... + if (rc == RC_SERIAL_PROCESSED) { + net->Service(); + retry_timer.Set (resend_delta, true); + dialog_timer.Set (dialog_time, true); + timeout_timer.Set (timeout, true); + continue; + } + //............................................................... + // other player has left the game + //............................................................... + else if (rc == RC_PLAYER_LEFT) { + if (first_time) { + num_ready++; + } + break; + } + //............................................................... + // Connection was lost + //............................................................... + else if (rc == RC_HUNG_UP) { + return (RC_NOT_RESPONDING); + } + //............................................................... + // If it was any other type of serial packet, break + //............................................................... + else if (rc != RC_NORMAL) { + break; + } + } + + //------------------------------------------------------------------ + // Process the incoming packet + //------------------------------------------------------------------ + rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen, + their_frame, their_sent, their_recv); + //.................................................................. + // New player heard from + //.................................................................. + if (rc == RC_PLAYER_READY) { + num_ready++; + } + //.................................................................. + // Scenario's don't match + //.................................................................. + else if (rc == RC_SCENARIO_MISMATCH) { + return (RC_SCENARIO_MISMATCH); + } + //.................................................................. + // DoList was full + //.................................................................. + else if (rc == RC_DOLIST_FULL) { + return (RC_DOLIST_FULL); + } + + //.................................................................. + // Service the connection, to clean out the receive queues + //.................................................................. + net->Service(); + } + + //--------------------------------------------------------------------- + // Debug output + //--------------------------------------------------------------------- + Print_Framesync_Values(Frame, MPlayerMaxAhead, net->Num_Connections(), + their_recv, their_sent, my_sent); + + //--------------------------------------------------------------------- + // Attempt to advance to the next frame. + //--------------------------------------------------------------------- + //..................................................................... + // For the first-time run, just check to see if we've heard from + // everyone. + //..................................................................... + if (first_time) { + if (num_ready >= net->Num_Connections()) break; + } + //..................................................................... + // For in-game processing, we have to check their_sent, their_recv, + // their_frame, etc. + //..................................................................... + else { + if (Can_Advance(net, MPlayerMaxAhead, their_frame, their_sent, + their_recv)) { + break; + } + } + + //--------------------------------------------------------------------- + // Service game stuff. Servicing the map's input, and rendering the + // map, allows the map to scroll even though we're hung up waiting for + // packets. Don't do this if 'first_time' is set, since users could be + // waiting a very long time for all systems to load the scenario, and + // it gets frustrating being able to scroll around without doing + // anything. + //--------------------------------------------------------------------- + Call_Back(); + if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) + Keyboard_Process(input); + Map.Render(); + } + + } /* end of while */ + + //------------------------------------------------------------------------ + // If the reconnect dialog was shown, force the map to redraw. + //------------------------------------------------------------------------ + if (reconnect_dlg) { + Map.Flag_To_Redraw(true); + Map.Render(); + } + + return (RC_NORMAL); + +} // end of Wait_For_Players + + +/*************************************************************************** + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * * + * This routine adjusts the connection timing on the local system; it also * + * optionally generates a RESPONSE_TIME event, to tell all systems to * + * dynamically adjust the current MaxAhead value. This allows both the * + * MaxAhead & the connection retry logic to have dynamic timing, to adjust * + * to varying line conditions. * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Generate_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + + //------------------------------------------------------------------------ + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, divide again by 4, assuming a game rate of 15 fps. + //------------------------------------------------------------------------ + resp_time = net->Response_Time(); + + //------------------------------------------------------------------------ + // Adjust my connection retry timing; only do this if I've sent out more + // than 5 commands, so I know I have a measure of the response time. + //------------------------------------------------------------------------ + if (my_sent > 5) { + + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + //..................................................................... + // If I'm the network "master", I'm also responsible for updating the + // MaxAhead value on all systems, so do that here too. + //..................................................................... + if (MPlayerLocalID == MPlayerID[0]) { + ev.Type = EventClass::RESPONSE_TIME; + //.................................................................. + // For multi-frame compressed events, the MaxAhead must be an even + // multiple of the FrameSendRate. + //.................................................................. + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + ev.Data.FrameInfo.Delay = MAX( ((((resp_time / 8) + + (FrameSendRate - 1)) / FrameSendRate) * + FrameSendRate), (FrameSendRate * 2) ); +char flip[128]; +sprintf (flip, "C&C95 - Generating timing packet - MaxAhead = %d frames\n", ev.Data.FrameInfo.Delay); +CCDebugString (flip); + + } + //.................................................................. + // For sending packets every frame, just use the 1-way connection + // response time. + //.................................................................. + else { + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = MAX( (resp_time / 8), + (unsigned long)MODEM_MIN_MAX_AHEAD ); + } + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = MAX( (resp_time / 8), + (unsigned long)NETWORK_MIN_MAX_AHEAD ); + } + } + OutList.Add(ev); + } + } + +} // end of Generate_Timing_Event + + +/*************************************************************************** + * Generate_Real_Timing_Event -- Generates a TIMING event * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + int highest_ticks; + int i; + int specified_frame_rate; + int maxahead; + + // + // If we haven't sent out at least 5 guaranteed-delivery packets, don't + // bother trying to measure our connection response time; just return. + // + if (my_sent < 5) { + return; + } + + // + // Find the highest processing time we have stored + // + highest_ticks = 0; + for (i = 0; i < MPlayerCount; i++) { + // + // If we haven't heard from all systems yet, bail out. + // + if (TheirProcessTime[i] == -1) { + return; + } + if (TheirProcessTime[i] > highest_ticks) { + highest_ticks = TheirProcessTime[i]; + } + } + + // + // Compute our "desired" frame rate as the lower of: + // - What the user has dialed into the options screen + // - What we're really able to run at + // + if (highest_ticks == 0) { + DesiredFrameRate = 60; + } else { + DesiredFrameRate = 60 / highest_ticks; + } + + if (Options.GameSpeed == 0) { + specified_frame_rate = 60; + } else { + specified_frame_rate = 60 / Options.GameSpeed; + } + + DesiredFrameRate = MIN (DesiredFrameRate, specified_frame_rate); + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Compute our new 'MaxAhead' value, based upon the response time of our + // connection and our desired frame rate. + // 'MaxAhead' in frames is: + // + // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec) + // + // resp_time is divided by 2 because, as reported, it represents a round- + // trip, and we only want to use a one-way trip. + // + maxahead = (resp_time * DesiredFrameRate) / (2 * 60); + + // + // Now, we have to round 'maxahead' so it's an even multiple of our + // send rate. It also must be at least thrice the FrameSendRate. + // (Isn't "thrice" a cool word?) + // + maxahead = ((maxahead + FrameSendRate - 1) / FrameSendRate) * FrameSendRate; + maxahead = MAX (maxahead, (int)FrameSendRate * 3); + + ev.Type = EventClass::TIMING; + ev.Data.Timing.DesiredFrameRate = DesiredFrameRate; + ev.Data.Timing.MaxAhead = maxahead; + + OutList.Add(ev); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + +} + + +/*************************************************************************** + * Generate_Process_Time_Event -- Generates a PROCESS_TIME event * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Process_Time_Event(ConnManClass *net) +{ + EventClass ev; + int avgticks; + unsigned long resp_time; // connection response time, in ticks + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + if (IsMono) { + MonoClass::Enable(); + Mono_Set_Cursor(0,23); + Mono_Printf("Processing Ticks:%03d Frames:%03d\n", ProcessTicks,ProcessFrames); + MonoClass::Disable(); + } + + avgticks = ProcessTicks / ProcessFrames; + + ev.Type = EventClass::PROCESS_TIME; + ev.Data.ProcessTime.AverageTicks = avgticks; +char flip[128]; +sprintf (flip, "C&C95 - Sending PROCESS_TIME packet of %04x ticks\n", ev.Data.ProcessTime.AverageTicks); +CCDebugString (flip); + + OutList.Add(ev); + + ProcessTicks = 0; + ProcessFrames = 0; +} + + +/*************************************************************************** + * Process_Send_Period -- timing for sending packets every 'n' frames * + * * + * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. * + * It determines if it's time to send a packet or not. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * 1 = it's time to send a packet; 0 = don't send a packet this frame. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Send_Period(ConnManClass *net) +{ + //------------------------------------------------------------------------ + // If the current frame # is not an even multiple of 'FrameSendRate', then + // it's not time to send a packet; just return. + //------------------------------------------------------------------------ + if (Frame != (((Frame + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate) ) { + + net->Service(); + + if (IsMono) { + MonoClass::Disable(); + } + + return (0); + } + + return (1); + +} // end of Process_Send_Period + + +/*************************************************************************** + * Send_Packets -- sends out events from the OutList * + * * + * This routine computes how many events can be sent this frame, and then * + * builds the "meta-packet" & sends it. * + * * + * The 'cap' value is the max # of events we can send. Ideally, it should * + * be based upon the bandwidth of our connection. Currently, it's just * + * hardcoded to prevent the modem from having to resend "too much" data, * + * which is about 200 bytes per frame. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer to store packets in * + * multi_packet_max max size of multi_packet_buf * + * max_ahead current game MaxAhead value * + * my_sent # commands I've sent this game * + * * + * OUTPUT: * + * # events sent, NOT including the FRAMEINFO event * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent) +{ + int cap; // max # events to send, NOT including FRAMEINFO event + int do_once; // true: only go through packet loop once + int ack_req; // 0 = no ack required on outgoing packet + int packetlen; // size of meta-packet sent + + //------------------------------------------------------------------------ + // Determine how many events it's OK to send this frame. + //------------------------------------------------------------------------ + //........................................................................ + // If we have 4 or more packets queue'd for sending, don't add any more + // this frame. + //........................................................................ + if (net->Private_Num_Send() >= 4) { + cap = 0; + do_once = 1; + } + //........................................................................ + // If there are 2 or more packets queued, the entire packet we send must + // fit within a single ComQueue buffer, so limit # events to 5. + // (The Modem connection manager has a max buffer size of 200 bytes, which + // is large enough for 6 uncompressed events, which leaves room for 5 + // events plus a FRAMEINFO.) + //........................................................................ + else if (net->Private_Num_Send() >= 2) { + cap = 5; + do_once = 1; + + } + //........................................................................ + // Otherwise, just send all events in the OutList + //........................................................................ + else { + cap = OutList.Count; + do_once = 0; + } + //........................................................................ + // Make sure we aren't sending more events than are in the OutList + //........................................................................ + if (cap > OutList.Count) { + cap = OutList.Count; + } + + //........................................................................ + // Make sure we don't send so many events that our DoList fills up + //........................................................................ + if (cap > (MAX_EVENTS * 8) - DoList.Count) { + cap = (MAX_EVENTS * 8) - DoList.Count; + } + + /* + ** No cap for internet game + ** + ** Or for serial games for that matter ST - 5/31/96 4:00PM + */ + if (GameToPlay == GAME_INTERNET || GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM){ + cap = OutList.Count; + do_once = 0; + } + + //------------------------------------------------------------------------ + // Build our meta-packet & transmit it. + //------------------------------------------------------------------------ + while (1) { + + Update_Queue_Mono (net, 1); + + //..................................................................... + // If there are no commands this frame, we'll just be sending a FRAMEINFO + // packet; no ack is required. For the modem's sake, check + // MPlayerCount; no ACK is needed if we're just sending to someone + // who's left the game. + //..................................................................... + if (cap == 0 || OutList.Count == 0 || MPlayerCount == 1) { + ack_req = 0; + } + else { + ack_req = 1; + } + + //..................................................................... + // Build & send out our message + //..................................................................... + packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max, + max_ahead, my_sent, cap); + net->Send_Private_Message (multi_packet_buf, packetlen, ack_req); + + //..................................................................... + // Call Service() to actually send the packet + //..................................................................... + net->Service(); + + //..................................................................... + // Stop if there's no more data to send, or if our send queue is + // filling up. + //..................................................................... + if (OutList.Count == 0 || do_once) { + break; + } + } + + return (cap); + +} // end of Send_Packets + + +/*************************************************************************** + * Send_FrameSync -- Sends a FRAMESYNC packet * + * * + * This routine is used to periodically remind the other systems that * + * we're still here, and to tell them what frame # we're on, in case * + * they've missed my FRAMEINFO packets. * + * * + * INPUT: * + * net ptr to connection manager * + * cmd_count # commands I've sent so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Send_FrameSync(ConnManClass *net, int cmd_count) +{ + EventClass packet; + + //------------------------------------------------------------------------ + // Build a frame-sync event to send. FRAMESYNC packets contain a + // scenario-based CRC rather than a game-state-based CRC, to let the + // games compare scenario CRC's on startup. + //------------------------------------------------------------------------ + memset (&packet, 0, sizeof(EventClass)); + packet.Type = EventClass::FRAMESYNC; + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + packet.Frame = ((Frame + MPlayerMaxAhead + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate; + } + else { + packet.Frame = Frame + MPlayerMaxAhead; + } + packet.ID = Houses.ID(PlayerPtr); + packet.MPlayerID = MPlayerLocalID; + packet.Data.FrameInfo.CRC = ScenarioCRC; + packet.Data.FrameInfo.CommandCount = cmd_count; + packet.Data.FrameInfo.Delay = MPlayerMaxAhead; + + //------------------------------------------------------------------------ + // Send the event. For modem, this just sends to the other player; + // for network, it sends to everyone we're connected to. + //------------------------------------------------------------------------ + net->Send_Private_Message (&packet, (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)), 0 ); + + return; + +} // end of Send_FrameSync + + +/*************************************************************************** + * Process_Receive_Packet -- processes an incoming packet * + * * + * This routine receives a packet from another system, adds it to our * + * execution queue (the DoList), and updates my arrays of their frame #, * + * their commands-sent, and their commands-received. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer containing packet(s) to parse * + * id id of sender * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * RC_NORMAL: nothing unusual happened, although * + * their_sent or their_recv may have been * + * altered * + * RC_PLAYER_READY: player has been heard from for the 1st time; * + * this presumes that his original * + * 'their_frame[]' value was -1 when this * + * routine was called * + * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; * + * normally only applies after loading a new * + * scenario or save-game * + * RC_DOLIST_FULL: fatal error; unable to add events to DoList * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + EventClass *event; + int index; + RetcodeType retcode = RC_NORMAL; + int i; + + //------------------------------------------------------------------------ + // Get an event ptr to the incoming message + //------------------------------------------------------------------------ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------------ + // Get the index of the sender + //------------------------------------------------------------------------ + index = net->Connection_Index(id); + + //------------------------------------------------------------------------ + // Compute the other player's frame # (at the time this packet was sent) + //------------------------------------------------------------------------ + if (their_frame[index] < + (int)(event->Frame - event->Data.FrameInfo.Delay)) { + + //..................................................................... + // If the original frame # for this player is -1, it means we've heard + // from this player for the 1st time; return the appropriate value. + //..................................................................... + if (their_frame[index]==-1) { + retcode = RC_PLAYER_READY; + } + + their_frame[index] = event->Frame - event->Data.FrameInfo.Delay; + } + + //------------------------------------------------------------------------ + // Extract the other player's CommandCount. This count will include + // the commands in this packet, if there are any. + //------------------------------------------------------------------------ + if (event->Data.FrameInfo.CommandCount > their_sent[index]) { + their_sent[index] = event->Data.FrameInfo.CommandCount; + } + + //------------------------------------------------------------------------ + // If this packet was not a FRAMESYNC packet: + // - Add the events in it to our DoList + // - Increment our commands-received counter by the number of non- + // FRAMEINFO packets received + //------------------------------------------------------------------------ + if (event->Type != EventClass::FRAMESYNC) { + //..................................................................... + // Break up the packet into its component events. A returned packet + // count of -1 indicates a fatal queue-full error. + //..................................................................... + i = Breakup_Receive_Packet( multi_packet_buf, packetlen); + if (i==-1) { + return (RC_DOLIST_FULL); + } + //..................................................................... + // Compute the actual # commands in the packet by subtracting off the + // FRAMEINFO event + //..................................................................... + if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) { + i--; + } + + their_recv[index] += i; + } + + //------------------------------------------------------------------------ + // If the event was a FRAMESYNC packet, there will be no commands to add, + // but we must check the ScenarioCRC value. + //------------------------------------------------------------------------ + else if (event->Data.FrameInfo.CRC != ScenarioCRC) { + return (RC_SCENARIO_MISMATCH); + } + + return (retcode); + +} // end of Process_Receive_Packet + + +/*************************************************************************** + * Process_Serial_Packet -- Handles an incoming serial packet * + * * + * This routine is needed because the modem classes don't support a * + * "global channel" like the network classes do, but that functionality is * + * still needed for modem communications. Specifically, the modem dialogs * + * transmit their own special packets back & forth, and messages are sent * + * using a special packet type. Thus, we have to call this routine when * + * we receive a modem packet, to allow it to process messages & dialog * + * packets. * + * * + * INPUT: * + * multi_packet_buf packet buffer to process * + * first_time 1 = this is the 1st game frame * + * * + * OUTPUT: * + * RC_NORMAL: this wasn't a SERIAL-type packet * + * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was * + * processed; the other player is still connected, * + * even if he's not in the game. * + * RC_PLAYER_LEFT: other player has left the game * + * RC_HUNG_UP: we're getting our own packets back; thus, the * + * modem is mirroring our packets, which means the * + * modem hung up! * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time) +{ + multi_packet_buf; + first_time; + return (RC_NORMAL); +#if (0) // ST - 1/2/2019 5:28PM + SerialPacketType *serial_packet; // for parsing serial packets + int player_gone; + EventClass *event; + char txt[MAX_MESSAGE_LENGTH+80]; + unsigned short magic_number; + unsigned short crc; + + //------------------------------------------------------------------------ + // Determine if this packet means that the other player has left the game + //------------------------------------------------------------------------ + serial_packet = (SerialPacketType *)multi_packet_buf; + player_gone = 0; + //........................................................................ + // On Frame 0, only a SIGN_OFF means the other player left; the other + // packet types may be left over from a previous session. + //........................................................................ + if (first_time) { + if (serial_packet->Command == SERIAL_SIGN_OFF) { + player_gone = 1; + } + } + //........................................................................ + // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means + // the other player is gone. + //........................................................................ + else { + if (serial_packet->Command == SERIAL_SIGN_OFF || + serial_packet->Command == SERIAL_TIMING || + serial_packet->Command == SERIAL_SCORE_SCREEN ) { + player_gone = 1; + } + } + if (player_gone) { + Destroy_Null_Connection(serial_packet->Color, 0); + return (RC_PLAYER_LEFT); + } + + //------------------------------------------------------------------------ + // Process an incoming message + //------------------------------------------------------------------------ + if (serial_packet->Command == SERIAL_MESSAGE) { + sprintf(txt, Text_String(TXT_FROM), serial_packet->Name, + serial_packet->Message); + + magic_number = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2)); + + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex(serial_packet->ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + + //..................................................................... + // Save this message in our last-message buffer + //..................................................................... + if (strlen (serial_packet->Message)) { + strcpy (LastMessage, serial_packet->Message); + } + + //..................................................................... + // Tell the map to do a partial update (just to force the + // messages to redraw). + //..................................................................... + Map.Flag_To_Redraw(false); + return (RC_SERIAL_PROCESSED); + } + + //------------------------------------------------------------------------ + // Any other SERIAL-type packet means the other player is still there; + // throw them away, but let the caller know the connection is OK. + //------------------------------------------------------------------------ + if ( (serial_packet->Command >= SERIAL_CONNECT && + serial_packet->Command < SERIAL_LAST_COMMAND) || + MPlayerCount == 1) { + return (RC_SERIAL_PROCESSED); + } + + //........................................................................ + // are we getting our own packets back?? + //........................................................................ + event = (EventClass *)multi_packet_buf; + if (event->ID == Houses.ID(PlayerPtr)) { + return (RC_HUNG_UP); + } + + return (RC_NORMAL); +#endif +} // end of Process_Serial_Packet + + +/*************************************************************************** + * Can_Advance -- determines if it's OK to advance to the next frame * + * * + * This routine uses the current values stored in their_frame[], * + * their_send[], and their_recv[] to see if it's OK to advance to the next * + * game frame. We must not advance if: * + * - If our frame # would be too far ahead of the slowest player (the * + * lowest their_frame[] value). "Too far" means * + * (Frame > their_frame + MaxAhead). * + * - our current command count doesn't match the sent command count of one * + * other player (meaning that we've missed a command packet from that * + * player, and thus the frame # we're receiving from him may be due to a * + * FRAMEINFO packet sent later than the command, so we shouldn't use * + * this frame # to see if we should advance; we should wait until we * + * have all the commands before we advance. * + * * + * Of course, this routine assumes the values in their_frame[] etc are * + * kept current by the caller. * + * * + * INPUT: * + * net ptr to connection manager * + * max_ahead max frames ahead * + * their_frame array of their frame #'s * + * their_sent array of their sent command count * + * their_recv array of their # received commands * + * * + * OUTPUT: * + * 1 = OK to advance; 0 = not OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + long their_oldest_frame; // other players' oldest frame # + int count_ok; // true = my cmd count matches theirs + int i; + + //------------------------------------------------------------------------ + // Special case for modem: if the other player has left, go ahead and + // advance to the next frame; don't wait on him. + //------------------------------------------------------------------------ + if (MPlayerCount == 1) { + return (1); + } + //------------------------------------------------------------------------ + // Find the oldest frame # in 'their_frame' + //------------------------------------------------------------------------ + their_oldest_frame = Frame + 1000; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < their_oldest_frame) + their_oldest_frame = their_frame[i]; + } + + //------------------------------------------------------------------------ + // I can advance to the next frame IF: + // 1) I'm less than a one-way propogation delay ahead of the other + // players' frame numbers, AND + // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands + // the other players have sent so far). + //------------------------------------------------------------------------ + count_ok = 1; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_recv[i] < their_sent[i]) { + count_ok = 0; + break; + } + } + if (count_ok && (Frame < (their_oldest_frame + max_ahead))) { + return (1); + } + + return (0); + +} // end of Can_Advance + + +/*************************************************************************** + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * * + * This routine [re]draws the reconnection dialog; if 'reconn' is set, * + * it tells the user who we're trying to reconnect to; otherwise, is just * + * says something generic like "Waiting for connections". * + * * + * INPUT: * + * timeout_timer ptr to count down timer, showing time remaining * + * their_frame array of other players' frame #'s * + * num_conn # connections in 'their_frame' * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * * + * OUTPUT: * + * 1 = user wants to cancel, 0 = not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh) +{ + static int displayed_time = 0; // time value currently displayed + int new_time; + int oldest_index; // index of person requiring a reconnect + int i,j; + + //------------------------------------------------------------------------ + // Convert the timer to seconds + //------------------------------------------------------------------------ + new_time = timeout_timer->Time() / 60; + + //-------------------------------------------------------------------------------- + // If we have just received input focus again after running in the background then + // we need to redraw the whole dialog. + //-------------------------------------------------------------------------------- + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + fresh = true; + } + + //------------------------------------------------------------------------ + // If the timer has changed, or 'fresh' is set, redraw the dialog + //------------------------------------------------------------------------ + if (fresh || (new_time != displayed_time)) { + //..................................................................... + // Find the index of the person we're trying to reconnect to + //..................................................................... + if (reconn) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < num_conn; i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + } + Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time); + } + displayed_time = new_time; + + //........................................................................ + // If user hits ESC, bail out + //........................................................................ + if (Check_Key()) { + if (Get_Key_Num()==KN_ESC) { + return (1); + } + } + + return (0); + +} // end of Process_Reconnect_Dialog + + +/*************************************************************************** + * Handle_Timeout -- handles a timeout in the wait-for-players loop * + * * + * This routine "gracefully" handles a timeout in the frame-sync loop. * + * The timeout must be handled differently by a modem game or network * + * game. * + * * + * The modem game must detect if the other player is still connected * + * physically, even if he's not playing the game any more; if so, this * + * routine returns an OK status. If the other player isn't even * + * physically connected, an error is returned. * + * * + * The network game must find the connection that's causing the timeout, * + * and destroy it. The game continues, even if there are no more human * + * players left. * + * * + * INPUT: * + * net ptr to connection manager * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * 1 = it's OK; reset timeout timers & keep processing * + * 0 = game over, man * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + int oldest_index; // index of person requiring a reconnect + int i,j; + int id; + + //------------------------------------------------------------------------ + // For modem, attempt to reconnect; if that fails, save the game & bail. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) { +#if (0) //ST - 1/2/2019 5:28PM + if ( net->Num_Connections() ) { + if (!Reconnect_Modem()) { + return (0); + } + else { + return (1); + } + } +#endif + } + + //------------------------------------------------------------------------ + // For network, destroy the oldest connection + //------------------------------------------------------------------------ + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + + id = net->Connection_ID(oldest_index); + + /* + ** Send the game statistics packet now if the game is effectivly over + */ + if (MPlayerCount == 2 && + GameToPlay == GAME_INTERNET && + !GameStatisticsPacketSent){ + Register_Game_End_Time(); + ConnectionLost = true; + Send_Statistics_Packet(); + } + + if (id != ConnManClass::CONNECTION_NONE) { + for (i = oldest_index; i < net->Num_Connections() - 1; i++) { + their_frame[i] = their_frame[i+1]; + their_sent[i] = their_sent[i+1]; + their_recv[i] = their_recv[i+1]; + } + CCDebugString ("C&C95 = Destroying connection due to time out\n"); + Destroy_Connection(id,1); + } + } + + return (1); + +} // end of Handle_Timeout + + +/*************************************************************************** + * Stop_Game -- stops the game * + * * + * This routine clears any global flags that need it, in preparation for * + * halting the game prematurely. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1995 BRR : Created. * + *=========================================================================*/ +static void Stop_Game(void) +{ + CCDebugString ("C&C95 - In Stop_Game.\n"); + GameActive = 0; + if (IsMono) { + MonoClass::Disable(); + } + + if (GameToPlay == GAME_INTERNET){ + ConnectionLost = true; + CCDebugString ("C&C95 - About to send statistics packet.\n"); + Register_Game_End_Time(); + Send_Statistics_Packet(); + CCDebugString ("C&C95 - Returned from sending stats packet.\n"); + } + + return; + +} // end of Stop_Game + + +/*************************************************************************** + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * * + * This routine takes events from the OutList, and puts them into a * + * "meta-packet", which is transmitted to all systems we're connected to. * + * Also, these events are added to our own DoList. * + * * + * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this * + * tells the other systems what frame we're on, as well as serving as a * + * standard packet header. * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * num_cmds value to use for the CommandCount field * + * cap max # events to send * + * * + * OUTPUT: * + * new size of packet * + * * + * WARNINGS: * + * 'num_cmds' should be the total of of commands, including all those sent * + * this frame! * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap) +{ + int size = 0; + EventClass *finfo; + + //------------------------------------------------------------------------ + // All events start with a FRAMEINFO event; fill this part in. + //------------------------------------------------------------------------ + //........................................................................ + // Set the event type + //........................................................................ + finfo = (EventClass *)buf; + finfo->Type = EventClass::FRAMEINFO; + //........................................................................ + // Set the frame to execute this event on; this is protocol-specific + //........................................................................ + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + finfo->Frame = ((Frame + frame_delay + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate; + } + else { + finfo->Frame = Frame + frame_delay; + } + //........................................................................ + // Fill in the rest of the event + //........................................................................ + finfo->ID = Houses.ID(PlayerPtr); + finfo->MPlayerID = MPlayerLocalID; + finfo->Data.FrameInfo.CRC = GameCRC; + finfo->Data.FrameInfo.CommandCount = num_cmds; + finfo->Data.FrameInfo.Delay = frame_delay; + + //------------------------------------------------------------------------ + // Initialize the # of bytes processed; this is protocol-specific + //------------------------------------------------------------------------ + if (CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) { + size += sizeof(EventClass); + } + else { + size += (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)); + } + + //------------------------------------------------------------------------ + // Transfer all events from the OutList into the DoList, building our + // packet while we go. + //------------------------------------------------------------------------ + switch (CommProtocol) { + //..................................................................... + // COMM_PROTOCOL_SINGLE_NO_COMP: + // We'll send at least a FRAMEINFO every single frame, no compression + //..................................................................... + case (COMM_PROTOCOL_SINGLE_NO_COMP): + size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // COMM_PROTOCOL_SINGLE_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every frame. + // COMM_PROTOCOL_MULTI_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every 'n' frames. + //..................................................................... + case (COMM_PROTOCOL_SINGLE_E_COMP): + case (COMM_PROTOCOL_MULTI_E_COMP): + size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // Default: We have no idea what to do, so do nothing. + //..................................................................... + default: + size = 0; + break; + } + + return( size ); + +} /* end of Build_Send_Packet */ + + +/*************************************************************************** + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + int ev_size; // size of event we're adding + + //------------------------------------------------------------------------ + // Loop until there are no more events, or we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + ev_size = sizeof(EventClass); + //..................................................................... + // Will the next event exceed the size of the buffer? If so, break. + //..................................................................... + if ( (size + ev_size) > bufsize ) { + return (size); + } + + //..................................................................... + // Set the event's frame delay + //..................................................................... + OutList.First().Frame = Frame + frame_delay; + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = Houses.ID(PlayerPtr); + OutList.First().MPlayerID = MPlayerLocalID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList + // event. If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if (!DoList.Add(OutList.First())) { + return (size); + } + + //..................................................................... + // Add event to the send packet + //..................................................................... + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + + //..................................................................... + // Increment our event counter; delete the last event from the queue + //..................................................................... + num++; + OutList.Next(); + } + + return (size); + +} // end of Add_Uncompressed_Events + + +/*************************************************************************** + * Add_Compressed_Events -- adds an compressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size reference to current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + EventClass::EventType eventtype; // type of event being compressed + EventClass prevevent; // last event processed + int datasize; // size of element plucked from event union + int storedsize; // actual # bytes stored from event + unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count + unsigned char numunits = 0; // megamission rep count value + bool missiondup = false; // flag: is this event a megamission repeat? + + //------------------------------------------------------------------------ + // clear previous event + //------------------------------------------------------------------------ + memset (&prevevent, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Loop until there are no more events, we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + eventtype = OutList.First().Type; + datasize = EventClass::EventLength[ eventtype ]; + //..................................................................... + // For a variable-sized event, pull the size from the event; otherwise, + // the size will be the data element size plus the event type value. + //..................................................................... + storedsize = datasize + sizeof (EventClass::EventType); + + //..................................................................... + // MegaMission compression: MegaMissions are stored as: + // EventType + // Rep Count + // MegaMission structure (event # 1 only) + // Whom #2 + // Whom #3 + // Whom #4 + // ... + // Whom #n + //..................................................................... + if (prevevent.Type == EventClass::MEGAMISSION) { + //.................................................................. + // If previous & current events are both MegaMissions: + //.................................................................. + if (eventtype == EventClass::MEGAMISSION) { + //............................................................... + // If the Mission, Target, & Destination are the same, compress + // the events into one: + // - Change datasize to the size of the 'Whom' field only + // - set total # bytes to store to the size of the 'Whom' only + // - increment the MegaMission rep count + // - set the MegaMission rep flag + //............................................................... + if (OutList.First().Data.MegaMission.Mission == + prevevent.Data.MegaMission.Mission && + OutList.First().Data.MegaMission.Target == + prevevent.Data.MegaMission.Target && + OutList.First().Data.MegaMission.Destination == + prevevent.Data.MegaMission.Destination) { + + datasize = sizeof(prevevent.Data.MegaMission.Whom); + storedsize = datasize; + numunits++; + missiondup = true; + } + //............................................................... + // Data doesn't match; start a new run of MegaMissions: + // - Store previous MegaMission rep count + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //............................................................... + else { + *unitsptr = numunits; + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + } + //.................................................................. + // Previous event was a MegaMission, but this one isn't: end the + // run of MegaMissions: + // - Store previous MegaMission rep count + // - Clear variables + //.................................................................. + else { + *unitsptr = numunits; // save # events in our run + unitsptr = NULL; // init other values + numunits = 0; + missiondup = false; + } + } + + //..................................................................... + // The previous event is not a MEGAMISSION but the current event is: + // Set up a new run of MegaMissions: + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //..................................................................... + else if (eventtype == EventClass::MEGAMISSION) { + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + + //..................................................................... + // Will the next event exceed the size of the buffer? If so, + // stop compressing. + //..................................................................... + if ( (size + storedsize) > bufsize ) + break; + + //..................................................................... + // Set the event's frame delay (this is protocol-dependent) + //..................................................................... + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + OutList.First().Frame = ((Frame + frame_delay + + (FrameSendRate - 1)) / FrameSendRate) * + FrameSendRate; + } + else { + OutList.First().Frame = Frame + frame_delay; + } + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = Houses.ID(PlayerPtr); + OutList.First().MPlayerID = MPlayerLocalID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList event. + // If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if ( !DoList.Add( OutList.First() ) ) { + break; + } + + //--------------------------------------------------------------------- + // Compress the event into the send packet buffer + //--------------------------------------------------------------------- + switch ( eventtype ) { + //.................................................................. + // RESPONSE_TIME: just use the Delay field of the FrameInfo union + //.................................................................. + case (EventClass::RESPONSE_TIME): + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.FrameInfo.Delay, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + break; + + //.................................................................. + // MEGAMISSION: + //.................................................................. + case (EventClass::MEGAMISSION): + //............................................................... + // Repeated mission in a run: + // - Update the rep count (in case we break out) + // - Copy the Whom field only + //............................................................... + if (missiondup) { + *unitsptr = numunits; + + memcpy ( ((char *)buf) + size, + &OutList.First().Data.MegaMission.Whom, datasize ); + + size += datasize; + } + //............................................................... + // 1st mission in a run: + // - Init the rep count (in case we break out) + // - Set the EventType + // - Copy the MegaMission structure, leaving room for 'numunits' + //............................................................... + else { + *unitsptr = numunits; + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + + sizeof(EventClass::EventType) + sizeof(numunits), + &OutList.First().Data.MegaMission, datasize ); + + size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits)); + } + break; + + //.................................................................. + // Default case: Just copy over the data field from the union + //.................................................................. + default: + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + + break; + } + + //--------------------------------------------------------------------- + // update # events processed + //--------------------------------------------------------------------- + num++; + + //--------------------------------------------------------------------- + // Update 'prevevent' + //--------------------------------------------------------------------- + memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) ); + + //--------------------------------------------------------------------- + // Go to the next event to process + //--------------------------------------------------------------------- + OutList.Next(); + } + + return (size); + +} // end of Add_Compressed_Events + + +/*************************************************************************** + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * * + * INPUT: * + * buf buffer to break up * + * bufsize length of buffer * + * * + * OUTPUT: * + * # events added to queue, -1 if fatal error (queue is full) * + * (return value includes any FRAMEINFO packets encountered; * + * FRAMESYNC's are ignored) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Breakup_Receive_Packet(void *buf, int bufsize ) +{ + int count = 0; + + /* + ** is there enough leftover for another record + */ + switch (CommProtocol) { + case (COMM_PROTOCOL_SINGLE_NO_COMP): + count = Extract_Uncompressed_Events(buf, bufsize); + break; + + default: + count = Extract_Compressed_Events(buf, bufsize); + break; + } + + return (count); + +} /* end of Breakup_Receive_Packet */ + + +/*************************************************************************** + * Extract_Uncompressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Uncompressed_Events(void *buf, int bufsize) +{ + int count = 0; + int pos = 0; + int leftover = bufsize; + EventClass *event; + + //------------------------------------------------------------------------ + // Loop until there are no more events in the packet + //------------------------------------------------------------------------ + while (leftover >= sizeof(EventClass) ) { + event = (EventClass *)(((char *)buf) + pos); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + event->IsExecuted = 0; + + if (!DoList.Add( *event )) { + return (-1); + } + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + } + + //..................................................................... + // Point to the next position in the buffer; decrement our 'leftover' + //..................................................................... + pos += sizeof(EventClass); + leftover -= sizeof(EventClass); + } + + return (count); + +} // end of Extract_Uncompressed_Events + + +/*************************************************************************** + * Extract_Compressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Compressed_Events(void *buf, int bufsize) +{ + int pos = 0; // current buffer parsing position + int leftover = bufsize; // # bytes left to process + EventClass *event; // event ptr for parsing buffer + int count = 0; // # events processed + int datasize = 0; // size of data to copy + EventClass eventdata; // stores Frame, ID, etc + unsigned char numunits = 0; // # units stored in compressed MegaMissions +//int lasteventtype=0; + //------------------------------------------------------------------------ + // Clear work event structure + //------------------------------------------------------------------------ + memset (&eventdata, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Assume the first event is a FRAMEINFO event + // Init 'datasize' to the amount of data to copy, minus the EventType value + // For the 1st packet only, this will include all info before the Data + // union, plus the size of the FrameInfo structure, minus the EventType size. + //------------------------------------------------------------------------ + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType); + event = (EventClass *)(((char *)buf) + pos); + + while ((unsigned)leftover >= (datasize + sizeof(EventClass::EventType)) ) { + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + //.................................................................. + // initialize the common data from the FRAMEINFO event + // keeping IsExecuted 0 + //.................................................................. + if (event->Type == EventClass::FRAMEINFO) { + eventdata.Frame = event->Frame; + eventdata.ID = event->ID; + eventdata.MPlayerID = event->MPlayerID; + + //............................................................... + // Adjust position past the common data + //............................................................... + pos += (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + leftover -= (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + } + //.................................................................. + // if MEGAMISSION event get the number of units (events to generate) + //.................................................................. + else if (event->Type == EventClass::MEGAMISSION) { + numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type)); + pos += sizeof(numunits); + leftover -= sizeof(numunits); + } + + //.................................................................. + // clear the union data portion of the event + //.................................................................. + memset (&eventdata.Data, 0, sizeof(eventdata.Data)); + eventdata.Type = event->Type; + datasize = EventClass::EventLength[ eventdata.Type ]; + + switch (eventdata.Type) { + case (EventClass::RESPONSE_TIME): + memcpy ( &eventdata.Data.FrameInfo.Delay, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + + case (EventClass::MEGAMISSION): + memcpy ( &eventdata.Data.MegaMission, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + if (numunits > 1) { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + datasize = sizeof(eventdata.Data.MegaMission.Whom); + + while (numunits) { + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + + //...................................................... + // Keep count of how many events we add to the queue + //...................................................... + count++; + numunits--; + memcpy ( &eventdata.Data.MegaMission.Whom, + ((char *)buf) + pos, datasize ); + + //...................................................... + // if one unit left fall thru to normal code + //...................................................... + if (numunits == 1) { + datasize -= sizeof(EventClass::EventType); + break; + } + else { + pos += datasize; + leftover -= datasize; + } + } + } + break; + + default: + memcpy ( &eventdata.Data, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + } + +char flip[128]; +sprintf (flip, "C&C95 - Adding event type %d to queue\n", eventdata.Type); +CCDebugString (flip); + +//if (lasteventtype == 11){ +// break; +//} + +//lasteventtype = (int) eventdata.Type; + + + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + + if (leftover) { + event = (EventClass *)(((char *)buf) + pos); + datasize = EventClass::EventLength[ event->Type ]; + if (event->Type == EventClass::MEGAMISSION) { + datasize += sizeof(numunits); + } + } + } + //..................................................................... + // FRAMESYNC event: This >should< be the only event in the buffer, + // and it will be uncompressed. + //..................................................................... + else { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + event = (EventClass *)(((char *)buf) + pos); + + //.................................................................. + // size of FRAMESYNC event - EventType size + //.................................................................. + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - + sizeof(EventClass::EventType); + } + } + + return (count); + +} // end of Extract_Compressed_Events + +#endif //DEMO + +/*************************************************************************** + * Execute_DoList -- Executes commands from the DoList * + * * + * This routine executes any events in the DoList that need to be executed * + * on the current game frame. The events must be executed in a special * + * order, so that all systems execute all events in exactly the same * + * order. * + * * + * This routine also handles checking the Game CRC sent by other systems * + * against my own, to be sure we're still in sync. * + * * + * INPUT: * + * max_houses # houses to execute commands for * + * base_house HousesType to start with * + * net ptr to connection manager; NULL if none * + * skip_crc a frame-based countdown timer; if it's non-zero, the * + * CRC check will be skipped. Ignored if NULL. * + * their_frame array of their frame #'s * + * their_sent array of # commands they've sent * + * their_recv array of # commands I've received from them * + * * + * (their_xxx are ignored if 'net' is NULL.) * + * * + * OUTPUT: * + * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Execute_DoList(int , HousesType , + ConnManClass *net, TCountDownTimerClass *, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + int i,j,k,wibble; + int index; + + //------------------------------------------------------------------------ + // For a single-player game, just execute all events in the queue. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_NORMAL) { + for (i = 0; i < DoList.Count; i++) { + if ((unsigned)Frame >= DoList[i].Frame && !DoList[i].IsExecuted) { + DoList[i].Execute(); // execute it + DoList[i].IsExecuted = true; // mark as having been executed + } + } + return (1); + } + +//#if(TIMING_FIX) + // + // If MPlayerMaxAhead is recomputed such that it increases, the systems + // may try to free-run to the new MaxAhead value. If so, they may miss + // an event that was generated after the TIMING event was created, but + // before it executed; this event will be scheduled with the older, + // shorter MaxAhead value. If a system doesn't receive this event, it + // may execute past the frame it's scheduled to execute on, creating + // a Packet-Recieved-Too-Late error. To prevent this, find any events + // that are scheduled to execute during this "period of vulnerability", + // and re-schedule for the end of that period. + // + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + for (j = 0; j < DoList.Count; j++) { + if (DoList[j].Type != EventClass::FRAMEINFO && + DoList[j].Frame > (unsigned)NewMaxAheadFrame1 && + DoList[j].Frame < (unsigned)NewMaxAheadFrame2) { + DoList[j].Frame = (unsigned)NewMaxAheadFrame2; + } + } + } +//#endif + + //------------------------------------------------------------------------ + // Execute the DoList. Events must be executed in the same order on all + // systems; so, execute them in the order of the MPlayerID array. This + // array is stored in the same order on all systems. + //------------------------------------------------------------------------ + for (i = 0; i < MPlayerCount; i++) { + + HousesType house; + HouseClass *housep; + + house = MPlayerHouses [i]; + housep= HouseClass::As_Pointer (house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!housep){ + continue; + } + + if (!housep->IsHuman){ + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (j = 0; j < DoList.Count; j++) { + +#ifndef DEMO + if (net) + Update_Queue_Mono (net, 6); +#endif //DEMO + + //.................................................................. + // If this event was from the currently-executing player ID, and it's + // time to execute it, execute it. + //.................................................................. + if (DoList[j].MPlayerID == MPlayerID[i] && (unsigned)Frame >= DoList[j].Frame && + !DoList[j].IsExecuted) { + + //............................................................... + // Error if it's too late to execute this packet! + //............................................................... + if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type != + EventClass::FRAMEINFO) { +#ifndef DEMO + Dump_Packet_Too_Late_Stuff(&DoList[j]); +#endif //DEMO + CCMessageBox().Process (TXT_PACKET_TOO_LATE); + return (0); + } + + //............................................................... + // Only execute EXIT & OPTIONS commands if they're from myself. + //............................................................... + if (DoList[j].Type==EventClass::EXIT || + DoList[j].Type==EventClass::OPTIONS) { + + + if (DoList[j].Type==EventClass::EXIT){ +CCDebugString ("C&C95 - Received EXIT packet\n"); + + /* + ** Flag that this house lost because it quit. ST - 6/5/96 0:29AM + */ + for (wibble = 0; wibble < MPlayerCount; wibble++) { + if (MPlayerID[wibble] == DoList[j].MPlayerID) { + house = MPlayerHouses[wibble]; + housep = HouseClass::As_Pointer (house); + housep->IGaveUp = true; + break; + } + } + + /* + ** Send the game statistics packet now since the game is effectivly over + */ + if (MPlayerCount == 2 && + GameToPlay == GAME_INTERNET && + !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + } + + + + if (DoList[j].ID == Houses.ID(PlayerPtr)) { + DoList[j].Execute(); + } + + //............................................................ + // If this EXIT event isn't from myself, destroy the connection + // for that player. The HousesType for this event is the + // connection ID. + //............................................................ + else if (DoList[j].Type==EventClass::EXIT) { + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + + //ST - 1/2/2019 5:29PM + //Destroy_Null_Connection( DoList[j].MPlayerID, 0 ); + } + + else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) { + index = net->Connection_Index (DoList[j].MPlayerID); + if (index != -1) { + for (k = index; k < net->Num_Connections() - 1; k++) { + their_frame[k] = their_frame[k+1]; + their_sent[k] = their_sent[k+1]; + their_recv[k] = their_recv[k+1]; + } + CCDebugString ("C&C95 = Destroying connection due to exit event\n"); +#ifndef DEMO + Destroy_Connection(DoList[j].MPlayerID,0); +#endif //DEMO + } + } + } + } + + //............................................................... + // For a FRAMEINFO event, check the CRC value. + // This could be an old FRAMEINFO packet that was floating around + // for awhile and just arrived; if so, its Frame value will be + // old. Ignore these packets. (This created bogus sync bugs on + // the Internet, when packets that were 35 frames old arrived.) + //............................................................... +#ifndef DEMO + else if (DoList[j].Type == EventClass::FRAMEINFO) { + if (DoList[j].Frame == Frame && + DoList[j].Data.FrameInfo.Delay < 32) { + index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) & + 0x001f); + if (CRC[index] != DoList[j].Data.FrameInfo.CRC) { + Print_CRCs(&DoList[j]); + if (CCMessageBox().Process (TXT_OUT_OF_SYNC, + TXT_CONTINUE, TXT_STOP) == 0) { + if (GameToPlay == GAME_MODEM || + GameToPlay == GAME_NULL_MODEM){ +#if (0)//ST - 1/2/2019 5:29PM + Destroy_Null_Connection( DoList[j].MPlayerID, -1 ); + Shutdown_Modem(); +#endif + GameToPlay = GAME_NORMAL; + } + else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) { + CCDebugString ("C&C95 = Destroying connections due to bad frame info packet\n"); + while (net->Num_Connections()) { + Destroy_Connection (net->Connection_ID(0), -1); + } + } + Map.Flag_To_Redraw(true); + } + else { + return (0); + } + return (1); + } + } + } +#endif //DEMO + //............................................................... + // Execute other commands + //............................................................... + else { + DoList[j].Execute(); + } + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + } + } + } + + return (1); + +} // end of Execute_DoList + + +/*************************************************************************** + * Clean_DoList -- Cleans out old events from the DoList * + * * + * Currently, an event can only be removed from the DoList if it's at the * + * head of the list; and event can't be removed from the middle. So, * + * this routine loops as long as the next event in the DoList has been * + * executed, it's removed. * + * * + * INPUT: * + * net ptr to connection manager; ignored if NULL * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Clean_DoList(ConnManClass *net) +{ + while (DoList.Count) { + +#ifndef DEMO + if (net) + Update_Queue_Mono (net, 7); +#else + net = net; +#endif //DEMO + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + } + else { + break; + } + } + +} // end of Clean_DoList + +#ifndef DEMO + +/*************************************************************************** + * Queue_Record -- Records the DoList to disk * + * * + * This routine just saves any events in the DoList to disk; we can later * + * "play back" the recording just be pulling events from disk rather than * + * from the network! * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/14/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Record(void) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # of events to save this frame + //------------------------------------------------------------------------ + j = 0; + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + j++; + } + } + + //------------------------------------------------------------------------ + // Save the # of events, then all events. + //------------------------------------------------------------------------ + RecordFile.Write (&j,sizeof(j)); + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + RecordFile.Write (&DoList[i],sizeof (EventClass)); + j--; + } + } + +} /* end of Queue_Record */ + + +/*************************************************************************** + * Queue_Playback -- plays back queue entries from a record file * + * * + * This routine reads events from disk, putting them into the DoList; * + * it then executes the DoList just like the network version does. The * + * result is that the game "plays back" like a recording. * + * * + * This routine detects mouse motion and stops playback, so it can work * + * like an "attract" mode, showing a demo of the game itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Playback(void) +{ + int numevents; + EventClass event; + int i; + int ok; + static int mx,my; + int max_houses; + HousesType base_house; + int key; + int testframe; + + //------------------------------------------------------------------------ + // If the user hits ESC, stop the playback + //------------------------------------------------------------------------ + if (Check_Key_Num()) { + key = Get_Key(); + // + // If the user hit ESC, end the recording. If this is an Attract-mode + // recording, end it no matter what the user does (any key or mouse). + // + if (key == KA_ESC || AllowAttract) { + GameActive = 0; + return; + } + } + + //------------------------------------------------------------------------ + // If we're in "Attact" mode, and the user moves the mouse, stop the + // playback. + //------------------------------------------------------------------------ + if (AllowAttract && Frame > 0 && + (mx != Get_Mouse_X() || my != Get_Mouse_Y())) { + GameActive = 0; + return; + } + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // Don't read anything the first time through (since the Queue_AI_Network + // routine didn't write anything the first time through); do this after the + // CRC is computed, since we'll still need a CRC for Frame 0. + //------------------------------------------------------------------------ + if (Frame==0 && GameToPlay!=GAME_NORMAL) { + return; + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + testframe = ((Frame + (FrameSendRate - 1)) / FrameSendRate) * FrameSendRate; + + if (GameToPlay != GAME_NORMAL && + CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (Frame != testframe) { + return; + } + } + + //------------------------------------------------------------------------ + // Read the DoList from disk + //------------------------------------------------------------------------ + ok = 1; + if (RecordFile.Read (&numevents, sizeof(numevents)) == + sizeof(numevents)) { + for (i = 0; i < numevents; i++) { + if (RecordFile.Read (&event, sizeof(EventClass)) == + sizeof(EventClass)) { + event.IsExecuted = 0; + DoList.Add (event); + } + else { + ok = 0; + break; + } + } + } + else { + ok = 0; + } + + if (!ok) { + GameActive = 0; + return; + } + + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_NORMAL) { + max_houses = 1; + base_house = PlayerPtr->Class->House; + } + else { + max_houses = MPlayerMax; + base_house = HOUSE_MULTI1; + } + if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_Playback */ + + +/*************************************************************************** + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Compute_Game_CRC(void) +{ + int i; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + //ObjectClass *objp; + + GameCRC = 0; + + //------------------------------------------------------------------------ + // Infantry + //------------------------------------------------------------------------ + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + } + + //------------------------------------------------------------------------ + // Units + //------------------------------------------------------------------------ + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + } + + //------------------------------------------------------------------------ + // Buildings + //------------------------------------------------------------------------ + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + } + +#if(0) + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + for (i = 0; i < LAYER_COUNT; i++) { + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + } + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } +#endif + + //------------------------------------------------------------------------ + // A random # + //------------------------------------------------------------------------ + Add_CRC(&GameCRC, rand()); + +} /* end of Compute_Game_CRC */ + + +/*************************************************************************** + * Add_CRC -- Adds a value to a CRC * + * * + * INPUT: * + * crc ptr to crc * + * val value to add * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Add_CRC(unsigned long *crc, unsigned long val) +{ + int hibit; + + if ( (*crc) & 0x80000000) { + hibit = 1; + } + else { + hibit = 0; + } + + (*crc) <<= 1; + (*crc) += val; + (*crc) += hibit; + +} /* end of Add_CRC */ + + +/*************************************************************************** + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Print_CRCs(EventClass *ev) +{ + ev=ev; + + int i; //,j; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + //ObjectClass *objp; + FILE *fp; + int rnd; + HouseClass *housep; + //HousesType house; + int color; + + Mono_Clear_Screen(); + Mono_Set_Cursor (0,0); + + char filename[80]; + sprintf (filename, "CRC%02d.TXT", Frame & 0x1f); + + fp = fopen(filename, "wt"); //"OUT.TXT","wt"); + if (fp==NULL) { + return; + } + + for (i = 0; i < 32; i++) { + fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]); + } + + + housep = HouseClass::As_Pointer (HOUSE_MULTI1); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi1: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI2); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi2: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI3); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi3: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI4); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi4: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI5); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi5: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI6); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi6: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + + //------------------------------------------------------------------------ + // Multi1 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi1 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi2 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi3 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi4 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi5 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi6 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi1 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi1 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi2 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi3 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi4 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi5 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi6 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi1 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi1 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi2 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi3 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi4 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi5 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi6 Buildings:%d\n",GameCRC); + } +#if (0) + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + GameCRC = 0; + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,">>>> MAP LAYER %d <<<<\n",i); + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",j,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + } + Mono_Printf("Map Layers:%d\n",GameCRC); + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + GameCRC = 0; + fprintf(fp,">>>> LOGIC LAYER <<<<\n"); + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",i,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + Mono_Printf("Logic:%d\n",GameCRC); +#endif + + //------------------------------------------------------------------------ + // Random # generator, frame # + //------------------------------------------------------------------------ + rnd = rand(); + + Mono_Printf("Random Number:%d\n",rnd); + fprintf(fp,"\nRandom Number:%d\n",rnd); + + Mono_Printf("My Frame:%d\n",Frame); + fprintf(fp,"My Frame:%d\n",Frame); +#if (0) + fprintf(fp,"-------------- Offending event: ----------------\n"); + fprintf(fp,"Type: %d\n",ev->Type); + fprintf(fp,"Frame: %d\n",ev->Frame); + fprintf(fp,"MPlayerID: %04x\n",ev->MPlayerID); + if (ev->Type == EventClass::FRAMEINFO) { + fprintf(fp,"CRC: %x\n",ev->Data.FrameInfo.CRC); + fprintf(fp,"CommandCount: %x\n",ev->Data.FrameInfo.CommandCount); + fprintf(fp,"Delay: %x\n",ev->Data.FrameInfo.Delay); + } +#endif + fclose(fp); + +} /* end of Print_CRCs */ + + +/*************************************************************************** + * Init_Queue_Mono -- inits mono display * + * * + * This routine steals control of the mono screen away from the rest of * + * the engine, by setting the global IsMono; if IsMono is set, the other * + * routines in this module turn off the Mono display when they're done * + * with it, so the rest of the engine won't over-write what we're writing. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Init_Queue_Mono(ConnManClass *net) +{ +#if(SHOW_MONO) + //------------------------------------------------------------------------ + // Set 'IsMono' so we can steal the mono screen from the engine + //------------------------------------------------------------------------ + if (Frame==0 && MonoClass::Is_Enabled()) { + IsMono = true; + } + + //------------------------------------------------------------------------ + // Enable mono output for our stuff; we must Disable it before we return + // control to the engine. + //------------------------------------------------------------------------ + if (IsMono) + MonoClass::Enable(); + + if (net->Num_Connections() > 0) { + //..................................................................... + // Network mono debugging screen + //..................................................................... + if (NetMonoMode==0) { + if (Frame==0 || NewMonoMode) { + net->Configure_Debug (0, sizeof (CommHeaderType), + sizeof(EventClass::EventType), EventClass::EventNames, 0); + net->Mono_Debug_Print (0,1); + NewMonoMode = 0; + } + else { + net->Mono_Debug_Print (0,0); + } + } + //..................................................................... + // Flow control debugging output + //..................................................................... + else { + if (NewMonoMode) { + Mono_Clear_Screen(); + Mono_Printf(" Queue AI:\n"); // flowcount[0] + Mono_Printf(" Build Packet Loop:\n"); // flowcount[1] + Mono_Printf(" Frame Sync:\n"); // flowcount[2] + Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3] + Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4] + Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5] + Mono_Printf(" DoList Execution:\n"); // flowcount[6] + Mono_Printf(" DoList Cleaning:\n"); // flowcount[7] + Mono_Printf("\n"); + Mono_Printf(" Frame:\n"); + Mono_Printf(" MPlayerMaxAhead:\n"); + Mono_Printf(" their_recv:\n"); + Mono_Printf(" their_sent:\n"); + Mono_Printf(" my_sent:\n"); + Mono_Printf(" DesiredFrameRate:\n"); + NewMonoMode = 0; + } + } + } +#else + net = net; +#endif +} // end of Init_Queue_Mono + + +/*************************************************************************** + * Update_Queue_Mono -- updates mono display * + * * + * INPUT: * + * net ptr to connection manager * + * flow_index index # for flow-count updates * + * -1: display * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Update_Queue_Mono(ConnManClass *net, int flow_index) +{ +#if(SHOW_MONO) + static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + //------------------------------------------------------------------------ + // If 'NetMonoMode' is 1, display flowcount info + //------------------------------------------------------------------------ + if (NetMonoMode==1) { + if (flow_index >= 0 && flow_index < 20) { + Mono_Set_Cursor(35,flow_index); + flowcount[flow_index]++; + Mono_Printf("%d",flowcount[flow_index]); + } + } + //------------------------------------------------------------------------ + // Otherwise, display the connection debug screen + //------------------------------------------------------------------------ + else { + net->Mono_Debug_Print (0,0); + } + +#else + flow_index = flow_index; + net = net; +#endif + +} // end of Update_Queue_Mono + + +/*************************************************************************** + * Print_Framesync_Values -- displays frame-sync variables * + * * + * INPUT: * + * curframe current game Frame # * + * max_ahead max-ahead value * + * num_connections # connections * + * their_recv # commands I've received from my connections * + * their_sent # commands each connection claims to have sent * + * my_sent # commands I've sent * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent) +{ +#if(SHOW_MONO) + int i; + + if (NetMonoMode==1) { + Mono_Set_Cursor(35,9); + Mono_Printf("%d",curframe); + + Mono_Set_Cursor(35,10); + Mono_Printf("%d",max_ahead); + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,11); + Mono_Printf("%4d",(int)their_recv[i]); + } + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,12); + Mono_Printf("%4d",(int)their_sent[i]); + } + + Mono_Set_Cursor(35,13); + Mono_Printf("%4d",(int)my_sent); + + Mono_Set_Cursor(35,14); + Mono_Printf("%2d",(int)DesiredFrameRate); + } +#else + curframe = curframe; + max_ahead = max_ahead; + num_connections = num_connections; + their_recv = their_recv; + their_sent = their_sent; + my_sent = my_sent; +#endif +} // end of Print_Framesync_Values + + +/*************************************************************************** + * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk * + * * + * INPUT: * + * event ptr to event to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/28/1996 BRR : Created. * + *=========================================================================*/ +void Dump_Packet_Too_Late_Stuff(EventClass *event) +{ + FILE *fp; + int i; + + fp = fopen("toolate.txt", "wt"); + if (!fp) { + return; + } + fprintf(fp,"--------- Event data: -------------------\n"); + fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame: %d\n",event->Frame); + fprintf(fp,"ID: %d\n",event->ID); + fprintf(fp,"MPlayerID: %04x\n",event->MPlayerID); + + for (i = 0; i < MPlayerCount; i++) { + if (event->MPlayerID == MPlayerID[i]) { + fprintf(fp,"Player's Name: %s",MPlayerNames[i]); + } + } + + fprintf(fp,"----------- My data: ------------------\n"); + fprintf(fp,"Frame:%d\n",Frame); + fprintf(fp,"MaxAhead:%d\n",MPlayerMaxAhead); + + fclose(fp); +} + +#endif //DEMO + +/*************************** end of queue.cpp ******************************/ \ No newline at end of file diff --git a/TIBERIANDAWN/QUEUE.H b/TIBERIANDAWN/QUEUE.H new file mode 100644 index 000000000..e184ed400 --- /dev/null +++ b/TIBERIANDAWN/QUEUE.H @@ -0,0 +1,272 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\queue.h_v 2.16 16 Oct 1995 16:45:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/08/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * QueueClass::Add -- Add object to queue. * + * QueueClass::First -- Fetches reference to first object in list. * + * QueueClass::Init -- Initializes queue to empty state. * + * QueueClass::Next -- Throws out the head of the line. * + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include "mission.h" +#include "target.h" +#include "defines.h" + +//#pragma warn -inl + +/* +** This class implements a classic FIFO queue (also known as - standing in line). Objects +** are added to the end (tail) of the line. Objects are removed from the start (first) of +** the line. A keyboard buffer is a good example of a common computer use of a queue. There +** is no provision for "taking cuts" or leaving the line once an object has been added. +** +** The implementation uses a circular list of objects. This allows adding and deleting of +** elements without any maintenance moves of remaining objects that would otherwise be +** necessary in a simple array storage method. A side effect of this means that accessing the +** internal array directly is not allowed. Supporting functions are provided for this purpose. +** +** WARNING WARNING WARNING WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +** The size parameter MUST be an exact power of two (2, 4, 8, 16, etc.) otherwise the internal +** indexing algorithm will fail. +*/ +template +class QueueClass +{ + public: + /* + ** This is the count of the number of objects in the queue. If this count is zero, + ** then the operator[], First(), and Next() functions are undefined. Check this + ** value BEFORE calling these functions. + */ + const int Count; + + //-------------- Functions -------------------- + QueueClass(void); // Default constructor. + + /* + ** The bracket subscript operator functions similarly to the way a normal subscript + ** operator works except that entry [0] matches the first-in-line and entry + ** [Count-1] matches the last-in-line. This is ensured regardless of the actual position + ** of the object in the circular internal list. + */ + T & operator[](int); + + /* + ** This function will return a reference to the "head of the line" object. + */ + T & First(void); + + /* + ** This function clears the list of objects. + */ + void Init(void); + + /* + ** This function discards the head-of-the-line object and advances all the remaining + ** objects up by one. Mnemonic: Imagine a broadway audition and the director yells + ** "NEXT!" + */ + int Next(void); + + /* + ** This will add an object to the tail of the line. If there is no more room to add + ** the object, then false will be returned. + */ + int Add(T const &); + + private: + int Head; // Index of element in list the longest. + int Tail; // Index where next new addition will go. + + T Array[size]; // Raw array of objects. +}; + + +/*********************************************************************************************** + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * * + * This default constructor for QueueClass objects initializes the queue to an empty * + * state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline QueueClass::QueueClass(void) : Count(0) +{ + Init(); +} + + +/*********************************************************************************************** + * QueueClass::Init -- Initializes queue to empty state. * + * * + * This function resets the queue to an empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline void QueueClass::Init(void) +{ + ((int &)Count) = 0; + Head = 0; + Tail = 0; +} + + +/*********************************************************************************************** + * QueueClass::Add -- Add object to queue. * + * * + * This function is used to add an object to the tail of the line. If the queue cannot * + * accept any more entries, then the object won't be added and false will be returned. * + * * + * INPUT: object -- The object that is to be added to the queue. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: If the queue is full, then the object won't be added. Be sure to check for this.* + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Add(T const &q) +{ + if (Count < size) { + Array[Tail] = q; + Tail = (Tail + 1) & (size-1); + ((int &)Count) = Count + 1; + return(true); + } + Mono_Printf( "Queue Add failed Count %d size %d tail %d head %d \n", + Count, size, Tail, Head ); + return (false); +} + + +/*********************************************************************************************** + * QueueClass::Next -- Throws out the head of the line. * + * * + * This routine is used to discard the object at the head of the line. All remaining * + * objects "move up" one. No actual movement occurs, merely the index is adjusted, but * + * the affect is that the next oldest object in the queue will now be returned with the * + * next call to the First() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of object remaining in the queue. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Next(void) +{ + if (Count) { + Head = (Head + 1) & (size-1); + ((int &)Count) = Count - 1; + } + return (Count); +} + + +/*********************************************************************************************** + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * * + * Use this routine to examine individual objects within the queue. The oldest object in * + * the queue is referenced by an index value of zero. The newest object in the queue is * + * referenced by a value of Count-1. If there are no objects in the queue, then this * + * operator is undefined. Although this operator allows examination of the queue, there is * + * no corresponding ability to insert or delete objects from the middle of the queue. * + * * + * INPUT: index -- The index into the queue of objects. Valid values range from zero to * + * Count-1. All other values return an undefined reference! * + * * + * OUTPUT: Returns with a reference to the object indicated by the index. * + * * + * WARNINGS: Check to make sure that Count is not zero before using this operator. Failure * + * to do so will return a reference to an undefined object. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::operator[](int index) +{ + return Array[(Head + index) & (size-1)]; +} + + +/*********************************************************************************************** + * QueueClass::First -- Fetches reference to first object in list. * + * * + * This routine is used to fetch the first object in the list (head of the line). This * + * object is the oldest in the list. Typical use of this function is to get and process * + * the first object so that it may be discarded with the Next() function in order to bring * + * subsequent objects to the first position. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the oldest object in the queue. * + * * + * WARNINGS: If there are no objects in the queue, then this function returns an undefined * + * reference. Be sure to check Count against zero before calling this function. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::First(void) +{ + return Array[Head]; +} + +#endif diff --git a/TIBERIANDAWN/RADAR.CPP b/TIBERIANDAWN/RADAR.CPP new file mode 100644 index 000000000..ff090ee28 --- /dev/null +++ b/TIBERIANDAWN/RADAR.CPP @@ -0,0 +1,1961 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\radar.cpv 2.17 16 Oct 1995 16:49:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : November 17, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Multi_Color -- Get the multi color offset number * + * RadarClass::AI -- Processes radar input (non-tactical). * + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * RadarClass::Click_In_Radar -- Check to see if a click is in radar map * + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * RadarClass::Draw_Names -- draws players' names on the radar map * + * RadarClass::Init_Clear -- Sets the radar map to a known state * + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * RadarClass::Radar_Activate -- Controls radar activation. * + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * RadarClass::Set_Radar_Position -- Sets the radar map coordinates. * + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell.* + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::TacticalClass::Action -- I/O function for the radar map. * + * RadarClass::Zoom_Mode(void) -- Handles toggling zoom on the map * + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to* + * RadarClass::Coord_To_Radar_Pixel -- Converts a coordinate to a radar pixel position * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include + +//void const * RadarClass::CoverShape; +RadarClass::TacticalClass RadarClass::RadarButton; + +void const * RadarClass::RadarAnim = NULL; + +static bool FullRedraw = false; + +#define _MAX_NAME 13 + +static GraphicBufferClass _IconStage(3,3); +static GraphicBufferClass _TileStage(24,24); + +/*********************************************************************************************** + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * * + * This default constructor merely sets the radar specific values to default settings. The * + * radar must be deliberately activated in order for it to be displayed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/16/1994 JLB : Created. * + *=============================================================================================*/ +RadarClass::RadarClass(void) +{ + IsZoomed = true; + IsRadarActive = false; + IsToRedraw = false; + RadarCursorRedraw = false; + PixelPtr = 0; + SpecialRadarFrame = 0; + IsPlayerNames = false; +} + + +/*********************************************************************************************** + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * * + * This routine handles any one time processing required in order for the radar map to * + * function. This actually only requires an allocation of the radar staging buffer. This * + * buffer is needed for those cases where the radar area of the page is being destroyed * + * and it needs to be destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine only ONCE. * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::One_Time(void) +{ + int factor = Get_Resolution_Factor(); + RadWidth = 80 << factor; + RadHeight = 70 << factor; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = Map.Get_Tab_Height() - (1 << factor); + RadPWidth = 64 << factor; + RadPHeight = 64 << factor; + if ( factor ) { + RadOffX = 16; + RadOffY = 7; + RadIWidth = 128; + RadIHeight = 128; + } else { + RadOffX = 4 << factor; + RadOffY = 1 << factor; + RadIWidth = 72 << factor; + RadIHeight = 69 << factor; + } + + DisplayClass::One_Time(); + RadarButton.X = RadX+RadOffX; + RadarButton.Y = RadY+RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +} + + +/*********************************************************************************************** + * RadarClass::Init_Clear -- Sets the radar map to a known state. * + * * + * This routine is used to initialize the radar map at the start of the scenario. It * + * sets the radar map position and starts it in the disabled state. * + * * + * INPUT: theater -- The theater that the scenario is starting (unused by this routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Init_Clear(void) +{ + DisplayClass::Init_Clear(); + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; + + /* + ** If we have a valid map lets make sure that we set it correctly + */ + if (MapCellWidth || MapCellHeight) { + IsZoomed = false; + Zoom_Mode(Coord_Cell(Map.TacticalCoord)); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Activate -- Controls radar activation. * + * * + * Use this routine to turn the radar map on or off. * + * * + * INPUT: control -- What to do with the radar map: * + * 0 = Turn radar off. * + * 1 = Turn radar on. * + * 2 = Remove Radar Gadgets * + * 3 = Add Radar Gadgets * + * 4 = Remove radar. * + * -1= Toggle radar on or off. * + * * + * OUTPUT: bool; Was the radar map already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Radar_Activate(int control) +{ + bool old = IsRadarActive; + + switch (control) { + + /* + ** Toggle the state of the radar map on or off. + */ + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; + } + } + } else { + Radar_Activate(2); + } + return(old); + + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } else { + RadarAnimFrame = 0; + } + } + } + } else { + Radar_Activate(3); + } + return(old); + + case 2: + if (GameToPlay==GAME_NORMAL) { + Map.Zoom.Disable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 3: + if (GameToPlay==GAME_NORMAL) { + Map.Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; + } + + if (IsRadarActive != old) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + FullRedraw = IsRadarActive; + return(old); +} + + +/*********************************************************************************************** + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * * + * This is used to display the radar map that appears in the lower * + * right corner. The main changes to this map are the vehicles and * + * structure pixels. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1991 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Draw_It(bool forced) +{ + DisplayClass::Draw_It(forced); +// if (!In_Debugger) while (!HidPage.Lock()) {} + +#if (0) // Legacy radar rendering not used. ST - 2/26/2020 3:53PM + + /* + ** Don't perform any rendering if none is requested. + */ + if (!forced && !IsToRedraw && !FullRedraw) return; + + static HousesType _house = HOUSE_NONE; + if (PlayerPtr->ActLike != _house) { + char name[_MAX_NAME + _MAX_EXT]; + + if (Special.IsJurassic && AreThingiesEnabled) { + strcpy(name, "RADAR.JP"); + } else { + _makepath(name, NULL, NULL, "RADAR", HouseTypeClass::As_Reference(PlayerPtr->ActLike).Suffix); + } + RadarAnim = Hires_Retrieve(name); + _house = PlayerPtr->ActLike; + } + + /* + ** If in player name mode, just draw player names + */ + if (IsPlayerNames) { + Draw_Names(); + IsToRedraw = false; + return; + } + + if (IsRadarActivating || IsRadarDeactivating) { + Radar_Anim(); + IsToRedraw = false; + return; + } + + if (Map.IsSidebarActive) { + if (IsRadarActive) { + + //HidPage.Lock(); +// ST 8/13/96 2:24PM +//forced = true; + /* + ** If only a few of the radar pixels need to be redrawn, then find and redraw + ** only these. + */ + if (!forced && IsToRedraw && !FullRedraw) { + IsToRedraw = false; + + if (PixelPtr) { + + /* + ** Render all pixels in the "to redraw" stack. + */ + for (unsigned int index = 0; index < PixelPtr; index++) { + CELL cell = PixelStack[index]; + if (Cell_On_Radar(cell)) { + (*this)[cell].IsPlot = false; + Plot_Radar_Pixel(cell); + RadarCursorRedraw |= (*this)[cell].IsRadarCursor; + } + } + + + /* + ** Refill the stack if there is pending pixels yet to be plotted. + ** This should only process in sections for speed reasons + */ + + if (PixelPtr == PIXELSTACK) { + PixelPtr = 0; + + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = XY_Cell(MapCellX + x, MapCellY + y); + if (Cell_On_Radar(cell)) { + + if ((*this)[cell].IsPlot) { + PixelStack[PixelPtr++] = cell; + IsToRedraw = true; + if (PixelPtr == PIXELSTACK) break; + } + } + } + if (PixelPtr == PIXELSTACK) break; + } + } else { + PixelPtr = 0; + } + } + + Radar_Cursor(RadarCursorRedraw); + + } else { + GraphicViewPortClass *oldpage = Set_Logic_Page(HidPage); +// if (LogicPage->Lock()) { + CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + if (BaseX || BaseY) { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + DKGREY); + } else { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + } + + /* + ** Draw the entire radar map. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + Plot_Radar_Pixel(index); + } + Radar_Cursor(true); + FullRedraw = false; + IsToRedraw = false; + LogicPage->Unlock(); + if (oldpage == &SeenBuff) { + Hide_Mouse(); + LogicPage->Blit(SeenBuff, RadX, RadY, RadX, RadY, RadWidth, RadHeight); + Show_Mouse(); + } + +// Set_Logic_Page(oldpage); + +// } + + } + + } else { + + /* + ** If the radar is not active, then only draw the cover plate if forced to do so. + */ +// if (forced) { + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + CC_Draw_Shape(RadarAnim, val, RadX, RadY + 1, WINDOW_MAIN, SHAPE_NORMAL); + FullRedraw = false; + IsToRedraw = false; +// } + } + + //HidPage.Unlock(); +// Map.Activator.Draw_Me(true); + } +#endif +} + +/*************************************************************************** + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) +{ + TerrainClass *list[4]; + int listidx = 0; + int lp,lp2; + + + ObjectClass *obj = Map[cell].Cell_Occupier(); + + /* + ** If the cell is occupied by a terrain type, add it to the sortable + ** list. + */ + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + + /* + ** Now loop through all the occupiers and add them to the list if they + ** are terrain type. + */ + for (lp = 0; lp < 3; lp ++) { + obj = Map[cell].Overlapper[lp]; + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + } + + /* + ** If there are no entrys in our list then just get out. + */ + if (!listidx) return; + + /* + ** If there is terrain in this cell then draw a dark pixel to + ** represent it. + */ + if (size == 1) { + LogicPage->Put_Pixel(x, y, 60); + return; + } + + /* + ** Sort the list by its sort Y value so that we can render in the proper + ** order. + */ + for (lp = 0; lp < listidx - 1; lp ++) { + for (lp2 = lp + 1; lp2 < listidx; lp2++) { + if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { + TerrainClass *terrain = list[lp]; + list[lp] = list[lp2]; + list[lp2] = terrain; + } + } + } + + /* + ** loop through the list and take care of rendering the correct icon. + */ + for (lp = 0; lp < listidx; lp ++) { + unsigned char *icon = list[lp]->Radar_Icon(cell); + if (!icon) continue; + + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char *)&FadingBrighten[0]); + } +} + + +/*********************************************************************************************** + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * * + * This routine will display an object imagery at the location specified according to the * + * condition of the specified cell. * + * * + * INPUT: cell -- The cell to use as reference when drawing the radar pixel. * + * * + * x,y -- The pixel coordinate to render the radar "pixel" at. * + * * + * size -- The size of the "pixel". When zoomed in, this value will be "3". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) +{ + ObjectClass *obj; + int xoff,yoff; + + obj = (ObjectClass *)Map[cell].Cell_Occupier(); + while (obj) { + if (obj->Is_Techno() && (((TechnoClass *)obj)->Cloak != CLOAKED || ((TechnoClass *)obj)->House->Is_Ally(PlayerPtr))) { + switch (obj->What_Am_I()) { + case RTTI_INFANTRY: + { + //int divisor = 255 / ZoomFactor; + int divisor = 86; + if ( ZoomFactor >= 3 ) { + xoff = Coord_XLepton(obj->Coord) / divisor; + yoff = Coord_YLepton(obj->Coord) / divisor; + if ( ZoomFactor >= 6 ) { + xoff<<=1; + yoff<<=1; + } + } else { + xoff = 0; + yoff = 0; + } + LogicPage->Put_Pixel(x+xoff, y+yoff, ((InfantryClass *)obj)->House->Class->BrightColor); + } + break; + + case RTTI_UNIT: + case RTTI_AIRCRAFT: + // PWG: Slowdown? + //if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, ((UnitClass *)obj)->House->Class->BrightColor, size, *LogicPage); + //LogicPage->Unlock(); + //} + break; + } + } + obj = obj->Next; + } +} + + +/*************************************************************************** + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) +{ + OverlayType overlay = (*this)[cell].Overlay; + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const * otype = &OverlayTypeClass::As_Reference(overlay); + + if (otype->IsRadarVisible) { + unsigned char *icon = otype->Radar_Icon((*this)[cell].OverlayData); + if (!icon) return; + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + if (otype->IsTiberium) { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); + } else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + } + } + } +} + + +/*************************************************************************** + * RadarClass::Zoom_Mode -- Handles toggling zoom on the map * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/29/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Zoom_Mode(CELL cell) +{ + int map_c_width; + int map_c_height; + /* + ** Set all of the initial zoom mode variables to the correct + ** setting. + */ + IsZoomed = !IsZoomed; + BaseX = 0; + BaseY = 0; + + /* + ** Figure out exactly what size we need to zoom the map to. + */ + if ( !IsZoomed ) { + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = MIN(xfactor,yfactor); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } else { + ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; + } + + /* + ** Make sure we do not show more cell then are on the map. + */ + map_c_width = MIN(map_c_width, 62); + map_c_height = MIN(map_c_height, 62); + + /* + ** Find the amount of remainder because this will let us calculate + ** how to center the thing. + */ + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); + + /* + ** Finally mark the map so it shows just as much as it is supposed + ** to. + */ + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIWidth - rem_y; + + /* + ** Set the radar position to the current cell. + */ + Set_Radar_Position(cell); + + /* + ** When zoom mode changes then we need to redraw the radar + ** area. + */ + IsToRedraw = true; + + /* + ** Notify the map that we need to redraw a portion + */ + Flag_To_Redraw(false); + + /* + ** Since we have made a vast change we must redraw everything + */ + FullRedraw = true; +} + + +/*********************************************************************************************** + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * * + * This will update the radar map with a pixel. It is used to display * + * vehicle positions on the radar map. * + * * + * INPUT: unit -- Pointer to unit to render at the given position. If * + * NULL is passed in, then the underlying terrain is * + * displayed instead. * + * * + * pos -- Position on the map to update. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine does NOT hide the mouse. It is up to you to * + * do so. * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 06/21/1991 JLB : Large blips for units & buildings. * + * 02/14/1994 JLB : Revamped. * + * 04/17/1995 PWG : Created. * + * 04/18/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Plot_Radar_Pixel(CELL cell) +{ + if (cell == -1) cell = 1; + + int x,y; // Coordinate of cell location. + + /* + ** Perform any clipping on the cell coordinate. + */ + if (!IsRadarActive || (unsigned)cell > MAP_CELL_TOTAL) return; + + if (!In_Radar(cell) || !Cell_On_Radar(cell)) { + return; + } + + /* + ** If we are zoomed in then calculate the pixel based off of the portion + ** of the map the radar is viewing. + */ + x = Cell_X(cell) - RadarX; + y = Cell_Y(cell) - RadarY; + + if (LogicPage->Lock()) { + CellClass * cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); + + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color=TBLACK; // Color of the pixel to plot. + if ((*this)[cell].IsVisible || Debug_Unshroud) { + color = cellptr->Cell_Color(true); + } else { + color = BLACK; + } + +// ST 8/13/96 2:24PM +//if (cellptr->IsRadarCursor){ +// color = WHITE; +//} + + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ + if (color == TBLACK) { + if (ZoomFactor > 1) { + void const *ptr; + long offset; + int icon; + + if (cellptr->TType != TEMPLATE_NONE) { + ptr = TemplateTypeClass::As_Reference(cellptr->TType).Get_Image_Data(); + icon = cellptr->TIcon; + } else { + ptr = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Get_Image_Data(); + icon = cellptr->Clear_Icon(); + } + + /* + ** Convert the logical icon number into the actual icon number. + */ + Mem_Copy(Add_Long_To_Pointer((void *)ptr, 28), &offset, sizeof(offset)); + Mem_Copy(Add_Long_To_Pointer((void *)ptr, offset+icon), &icon, sizeof(char)); + icon &= 0x00FF; + + Mem_Copy(Add_Long_To_Pointer((void *)ptr, 12), &offset, sizeof(offset)); + ptr = Add_Long_To_Pointer((void *)ptr, offset + icon*(24*24)); + + unsigned char * data = (unsigned char *)ptr; + Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + _TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); + + } else { + if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, cellptr->Cell_Color(false), ZoomFactor, *LogicPage); + LogicPage->Unlock(); + } + } + } else { + if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, color, ZoomFactor, *LogicPage); + LogicPage->Unlock(); + } + } + if (color != BLACK) { + Render_Overlay(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); + Render_Infantry(cell, x, y, ZoomFactor); + } + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * * + * This routine is used to inform the system that a pixel needs to be * + * rerendered on the radar map. The pixel(s) will be rendered the * + * next time the map is refreshed. * + * * + * INPUT: cell -- The map cell to be rerendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1992 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Radar_Pixel(CELL cell) +{ + if (IsRadarActive && Map.IsSidebarActive && Cell_On_Radar(cell)) { + IsToRedraw = true; + (*this)[cell].IsPlot = true; + if (PixelPtr < PIXELSTACK) { + PixelStack[PixelPtr++] = cell; + } + } +} + + +/*********************************************************************************************** + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * * + * This routine will examine the X and Y coordinate and convert them into the X and Y * + * cell coordinate value that cooresponds to the location. * + * * + * INPUT: x,y -- The X and Y moouse coordinate already normalized to the radar upper left * + * corner. * + * * + * OUTPUT: Returns with success rating in addition, the X and Y values will now hold the * + * cell coordinates of the cell the pixel offsets indicated. * + * Result 1 = click was in radar region * + * Result 0 = click was outside radar region completly * + * Result-1 = click in radar area but not on clickable reagion of radar. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1995 PWG : Created. * + * 07/16/1995 JLB : Recognizes when sidebar is closed now. * + *=============================================================================================*/ +int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) +{ + int x = ptr_x; + int y = ptr_y; + + /* + ** If radar is not active the click could have been on a radar point + */ + if (!IsRadarActive || !Map.IsSidebarActive) return(0); + + x -= (RadX + RadOffX); + y -= (RadY + RadOffY); + if (x < RadIWidth && y < RadIHeight) { + x -= BaseX; + y -= BaseY; + + if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + x = RadarX + (x / ZoomFactor); + y = RadarY + (y / ZoomFactor); + if (change) { + ptr_x = x; + ptr_y = y; + } + return(1); + } + return(-1); + } + return(0); +} + + +/*********************************************************************************************** + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * * + * This routine will examine the pixel coordinate provided and determine what cell it * + * represents. If the radar map is not active or the coordinates are not positioned over * + * the radar map, then it will fall into the base class corresponding routine. * + * * + * INPUT: x,y -- The pixel coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that the coordinate is over or -1 if not over any * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Click_Cell_Calc(int x, int y) +{ + int result = Click_In_Radar(x, y, true); + switch (result) { + case 1: + return(XY_Cell(x, y)); + + case -1: + return(-1); + } + return(DisplayClass::Click_Cell_Calc(x, y)); +} + + +/*********************************************************************************************** + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * * + * This routine will update the radar map if a cell becomes mapped. * + * * + * INPUT: cell -- The cell that is being mapped. * + * * + * house -- The house that is doing the mapping. * + * * + * OUTPUT: bool; Was the cell mapped (for the first time) by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Map_Cell(CELL cell, HouseClass * house, bool and_for_allies) +{ + if (DisplayClass::Map_Cell(cell, house, and_for_allies)) { + Radar_Pixel(cell); + return(true); + } + return(false); +} + +void RadarClass::Cursor_Cell(CELL cell, int value) +{ + int temp = (*this)[cell].IsRadarCursor; + + /* + ** If this cell is not on the radar don't botther doing anything. + */ + if (In_Radar(cell) && temp != value) { + /* + ** Record the new state of this cell. + */ + (*this)[cell].IsRadarCursor = value; + + /* + ** If we are erasing then erase the cell. + */ +////// ST 8/13/96 2:23PM + if (value == FALSE) { + Plot_Radar_Pixel(cell); +////// + } + } +} + +void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen) +{ + int x, y; + /* + ** First step is to convert pixel coordinates back to a CellX and CellY. + */ + x1 = RadarX + (x1 / ZoomFactor); + y1 = RadarY + (y1 / ZoomFactor); + x2 = RadarX + (x2 / ZoomFactor); + y2 = RadarY + (y2 / ZoomFactor); + + /* + ** Now we need to convert the Pixel length to a cell length. + */ + barlen = (barlen / ZoomFactor)+1; + + /* + ** Now lets loop through and mark the map with the proper value. + */ + for (int lp = 0; lp <= barlen; lp++) { + /* + ** Do Horizontal action to upper and lower left corners. + */ + x = x1 + lp; + Cursor_Cell(XY_Cell(x,y1), value); + Cursor_Cell(XY_Cell(x,y2), value); + /* + ** Do Horizontal Action to upper and lower right corners + */ + x = x2 - lp; + Cursor_Cell(XY_Cell(x,y1), value); + Cursor_Cell(XY_Cell(x,y2), value); + /* + ** Do Vertical Action to left and right upper corners + */ + y = y1 + lp; + Cursor_Cell(XY_Cell(x1,y), value); + Cursor_Cell(XY_Cell(x2,y), value); + + /* + ** Do Vertical action to left and right lower corners. + */ + y = y2 - lp; + Cursor_Cell(XY_Cell(x1,y), value); + Cursor_Cell(XY_Cell(x2,y), value); + } +} + + + +/*********************************************************************************************** + * RadarClass::Cell_XY_To_Radar_Pixel-- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) +{ + x = (cellx - RadarX) * ZoomFactor; + y = (celly - RadarY) * ZoomFactor; +} + +/*********************************************************************************************** + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +//#pragma argsused +void RadarClass::Radar_Cursor(int forced) +{ + static int _last_pos = -1; + static int _last_frame = -1; + GraphicViewPortClass *oldpage; + int x1, y1, x2, y2; + /* + ** figure out these function calls as we will need to call them multiple times. + */ + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; + /* + ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then + ** just skip the redraw process. + */ + if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; + + if ( _last_pos != -1 ) { + /* + ** The first thing we need to do is take care of erasing the last radar cell position. We do this + ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and + ** finally taking care of calling the erase procedure which will convert the pixel coordinates back + ** to the cells that need to be redraw. + **/ + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); + + Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the current coordinates based on the last animation frame. + */ + x1-= _last_frame; + y1-= _last_frame; + x2+= _last_frame; + y2+= _last_frame; + /* + ** Finally mark the map (actually remove the marks that indicate the radar cursor was there + */ + Mark_Radar(x1, y1, x2, y2, FALSE, barlen); + } + + + /* + ** find the upper left and lower right corners of the radar cursor. Remember to adjust x2 and y2 back + ** by one pixel as they will not be pointing to the right value otherwise. They point one cell ahead + ** of where they should. + */ + Cell_XY_To_Radar_Pixel(tac_cell_x, tac_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(tac_cell_x + Lepton_To_Cell(TacLeptonWidth), tac_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the coordinates based on the current frame of radar animation. + */ + x1-= SpecialRadarFrame; + y1-= SpecialRadarFrame; + x2+= SpecialRadarFrame; + y2+= SpecialRadarFrame; + + Mark_Radar(x1, y1, x2, y2, TRUE, barlen); + + /* + ** setup a graphic view port class so we can write all the pixels relative + ** to 0,0 rather than relative to full screen coordinates. + */ + oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); + + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + + // Draw upper right hand corner + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + + // Draw lower left hand corner + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + + // Draw lower right hand corner + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); +#if(0) + draw_window.Draw_Rect(x1, y1, x2, y2, WHITE); +#endif + +#if(FALSE) + if (oldpage == &SeenBuff) { + Hide_Mouse(); + HidPage.Blit( SeenBuff, + (int)(RadX + RadOffX + BaseX), + (int)(RadY + RadOffY + BaseY), + (int)(RadX + RadOffX + BaseX), + (int)(RadY + RadOffY + BaseY), + (int)draw_window.Get_Width(), + (int)draw_window.Get_Height(), + (BOOL)FALSE); + + Show_Mouse(); + } +#endif + Set_Logic_Page(oldpage); + _last_pos = tac_cell; + _last_frame = SpecialRadarFrame; + RadarCursorRedraw = FALSE; +} + + +/*************************************************************************** + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Radar_Anim(void) +{ + /* + ** Do nothing if we're in player-name mode + */ + if (IsPlayerNames) + return; + + if (!Map.IsSidebarActive) return; + + GraphicViewPortClass *oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); + + Draw_Box(RadX+RadOffX-1, RadY+RadOffY-1, RadIWidth+2, RadIHeight+2, BOXSTYLE_RAISED, true); + draw_window.Clear(); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + + Flag_To_Redraw(false); + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * RadarClass::AI -- Processes radar input (non-tactical). * + * * + * This routine intercepts any player input that concerns the radar map, but not those * + * areas that represent the tactical map. These are handled by the tactical map AI * + * processor. Primarily, this routine handles the little buttons that border the radar * + * map. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 12/26/1994 JLB : Moves tactical map with click or drag. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void RadarClass::AI(KeyNumType & input, int x, int y) +{ + /* + ** Check to see if we need to animate the radar cursor + */ + if (IsRadarActive && Map.IsSidebarActive && SpecialRadarFrame) { + SpecialRadarFrame--; + RadarCursorRedraw = TRUE; + IsToRedraw = TRUE; + Flag_To_Redraw(FALSE); + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarActivating) { + if (!DoesRadarExist) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + DoesRadarExist = true; + Radar_Activate(3); + } + } else { + RadarAnimFrame--; + if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + Radar_Activate(3); + } + } + } + if (IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame == MAX_RADAR_FRAMES) { + IsRadarDeactivating = false; + } else { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + + DisplayClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * RadarClass::TacticalClass::Action -- I/O function for the radar map. * + * * + * This is the main action function for handling player I/O on the radar map. It processes * + * mouse clicks as well as mouse moves. * + * * + * INPUT: flags -- The event flags that trigger this function call. * + * * + * key -- Reference the keyboard event that applies to the trigger event. * + * * + * OUTPUT: Should further processing of the input list be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int RadarClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + CELL cell; // cell num click happened over + int x,y; // Sub cell pixel coordinates. + int cellx,celly; // Sub cell pixel coordinates. + bool shadow; // is the cell in shadow or not + ObjectClass *object = 0; // what object is in the cell + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + if (Map.IsSidebarActive) { + Map.Help_Text(TXT_NONE); + } + + if (!Map.IsRadarActive) { + if (Map.IsSidebarActive) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + return(false); + } + + /* + ** Disable processing if the player names are up + */ + if (Map.Is_Player_Names()) { + GadgetClass::Action(0, key); + return(true); + } + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + + int result = Map.RadarClass::Click_In_Radar(x, y, false); + + if (result == 1) { + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); + cellx = 12; + celly = 12; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Cell_Object(cell, cellx, celly); + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + + /* + ** If this is not a valid radar map action then we are not going to do + ** anything. + */ + switch (action) { + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; + + default: + action = ACTION_NONE; + object = NULL; + break; + } + + /* + ** On the radar map the only reason we would want the normal cursor to + ** appear is if we were over one of our own selected units. Otherwise + ** we can't move there. + **/ + if (action == ACTION_NONE) { + if (object && object->Is_Selected_By_Player()) { + object = NULL; + } else { + action = ACTION_NOMOVE; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (flags & LEFTUP) { + Map.Mouse_Left_Up(shadow, object, action, true); + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTPRESS) { + Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); + } + + } else { + + Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); + + if (flags & LEFTPRESS) { + + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + int cellx = Cell_X(cell); + int celly = Cell_Y(cell); + cellx -= Lepton_To_Cell(Map.TacLeptonWidth) / 2; + cellx = MAX(cellx, Map.MapCellX); + celly -= Lepton_To_Cell(Map.TacLeptonHeight) / 2; + celly = MAX(celly, Map.MapCellY); + cell = XY_Cell(cellx, celly); + shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); + Map.Set_Tactical_Position(Cell_Coord(cell)); + cell = Coord_Cell(Map.DesiredTacticalCoord); + Map.DisplayClass::IsToRedraw = true; + //Map.Flag_To_Redraw(false); + Map.Flag_To_Redraw(true); + Map.SpecialRadarFrame = 4; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Zoom_Mode(cell); + } + } + } + } + if (result == -1) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + GadgetClass::Action(0, key); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * * + * This routine intercepts the refresh cells request and if it detects that the sidebar * + * should be rerendered, it flags the radar map to redraw during the next draw operation. * + * * + * INPUT: cell -- The origin cell that the refresh cell offset list is based upon. * + * * + * list -- Pointer to the list of offsets from the origin cell that specifies the * + * cells to be flagged for redraw. If the list starts with the special * + * code to refresh the sidebar, then this routine recognizes it and flags * + * the radar map to be redrawn accordingly. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + DisplayClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell. * + * * + * This routine will try to center the radar map around the cell position specified. * + * * + * INPUT: cell -- The cell to try and position the radar map around. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Radar_Position(CELL cell) +{ + int oldx, oldy; + int newx, newy; + int newcell; + + if (ZoomFactor != 1) { +#if(FALSE) + oldx = (Cell_X(cell) - MapCellX) - (RadarCellWidth / 2); + oldy = (Cell_Y(cell) - MapCellY) - (RadarCellHeight / 2); +#else + oldx = (Cell_X(cell) - MapCellX); + oldy = (Cell_Y(cell) - MapCellY); +#endif + } else { + oldx = 0; + oldy = 0; + } + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); + + if (RadarCell != newcell) { + int forced = FALSE; + int xmod = newx; + int ymod = newy; + + int radx = (Cell_X(RadarCell)) - xmod; + int rady = (Cell_Y(RadarCell)) - ymod; + + RadarX = newx; + RadarY = newy; + RadarCell = newcell; + + if (Map.IsSidebarActive&& Map.IsRadarActive) { + int radw = RadarCellWidth-ABS(radx); // Replicable width. + int radh = RadarCellHeight-ABS(rady); // Replicable height. + + if (radw < 1) forced = true; + if (radh < 1) forced = true; + + if (!forced && (radw != RadarWidth || radh != RadarHeight)) { + /* + ** Blit the section that is actually overlapping. + */ + if (OverlappedVideoBlits || !HidPage.Get_IsDirectDraw()){ + HidPage.Blit(HidPage, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + }else{ + /* + ** System does not support overlapped blitting of video surfaces. + ** Blit it in 2 stages using an intermediate buffer. + */ + GraphicBufferClass temp_surface; + temp_surface.Init((RadarWidth + 16) & 0xfffffff0, + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum) GBC_VIDEOMEM); + + HidPage.Blit(temp_surface, (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); + + temp_surface.Blit (HidPage,0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + } + + /* + ** Now we need to flag the section of the map that is going to redraw. + */ + if ( radx != 0 ) { + int min; + int max; + if ( radx < 0 ) { // this mean regen the right edge + min = radw; + max = radw+ABS(radx); + } else { // this mean regen the left edge + min = 0; + max = radx; + } + for (int x = min; x < max; x++ ) { + for (unsigned int y = 0; y < RadarCellHeight; y++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + if ( newy != 0 ) { + int min; + int max; + if ( rady < 0 ) { // this mean regen the bottom edge + min = radh; + max = radh+ABS(rady); + } else { // this mean regen the top edge + min = 0; + max = rady; + } + for (int y = min; y < max; y++ ) { + for ( unsigned int x = 0; x < RadarCellWidth; x++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + } + } + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + if (ZoomFactor > 4) { + FullRedraw = forced; + } + } else { + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * * + * This returns the cell number of the upper left corner of the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the radar map upper left corner cell position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Radar_Position(void) +{ + return(RadarCell); +} + + +/*********************************************************************************************** + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * * + * This routine is called when the tactical map changes its dimensions. This occurs when * + * the tactical map moves and when the sidebar pops on or off. * + * * + * INPUT: x,y -- The cell coordinate of the upper left corner of the tactical map. * + * * + * w,y -- The cell width and height of the tactical map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + Set_Radar_Position(XY_Cell(x, y)); + DisplayClass::Set_Map_Dimensions(x, y, w, h); +} + + +/*********************************************************************************************** + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to * + * * + * This routine is called when the tactical map is to change position. The radar map might * + * be adjusted as well by this routine. * + * * + * INPUT: coord -- The new coordinate to use for the upper left corner of the tactical * + * map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Tactical_Position(COORDINATE coord) +{ + DisplayClass::Set_Tactical_Position(coord); + Set_Radar_Position(Coord_Cell(DesiredTacticalCoord)); +} + + +/*********************************************************************************************** + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * * + * This routine will examine the specified cell number and return whether it is visible * + * on the radar map. This depends on the radar map position. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: Is the specified cell visible on the radar map currently? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Cell_On_Radar(CELL cell) +{ + if ((unsigned)cell > MAP_CELL_TOTAL) + return(false); + + int x = Cell_X(cell) - RadarX; + int y = Cell_Y(cell) - RadarY; + return (!((unsigned)x >= RadarCellWidth || (unsigned)y >= RadarCellHeight)); + +// if (!IsZoomed) { +// return(true); +// } +// return(!(((Cell_X(cell) - RadarX) > RadarCellWidth) || ((Cell_Y(cell) - RadarY) > RadarCellHeight))); +} + +/*********************************************************************************************** + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * * + * INPUT: * + * on true = turn on; false = turn off * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Player_Names(bool on) +{ + IsPlayerNames = on; + IsToRedraw = true; + if (on) { + Flag_To_Redraw(true); +// Flag_To_Redraw(false); + } else { + Flag_To_Redraw(true); // force drawing of the plate + } +} + + +/*********************************************************************************************** + * Draw_Names -- draws players' names on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Draw_Names(void) +{ + int c_idx; + HousesType house; + HouseClass *ptr; + int y; + char txt[40]; + unsigned char id; + int i; + HousesType h; + int kills; + int color; + TextPrintType style; + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return; + } + + CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + + y = RadY + RadOffY; + + Fancy_Text_Print (TXT_NAME_COLON, RadX + RadOffX, y, LTGREY, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + + Fancy_Text_Print (TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, + LTGREY, TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL); + + y += 6*factor+1; + + LogicPage->Draw_Line(RadX + RadOffX, y, + RadX + RadOffX + RadIWidth - 1, y, LTGREY); + + y += 2*factor; + + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr) + continue; + + /* + ** Decode this house's color + */ + c_idx = ptr->RemapColor; + + if (ptr->IsDefeated) { + color = GREY; + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + } else { + color = MPlayerTColors[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + } + + /* + ** Initialize our message + */ + txt[0] = 0; + + /* + ** If the house is non-human, generate the message + */ + if (!ptr->IsHuman) { + sprintf(txt,"%s", Text_String(TXT_COMPUTER)); + } else { + + /* + ** For a human house: + ** - Compute the multiplayer ID for this house + ** - find the name for this player + */ + id = Build_MPlayerID (c_idx,ptr->ActLike); + for (i = 0; i < MPlayerCount; i++) { + if (id == MPlayerID[i]) { + sprintf(txt,"%s",MPlayerNames[i]); + break; + } + } + } + + /* + ** Print the player name, and the # of kills + */ + if (strlen(txt)) { + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } + Fancy_Text_Print (txt, RadX + RadOffX, y, color, BLACK, style); + + kills = 0; + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + kills += ptr->UnitsKilled[h]; + kills += ptr->BuildingsKilled[h]; + } + sprintf(txt, "%2d", kills); + Fancy_Text_Print (txt, RadX + RadOffX + RadIWidth - 2, y, + color, BLACK, style | TPF_RIGHT); + + y += 6*factor+1; + + } + } +} diff --git a/TIBERIANDAWN/RADAR.H b/TIBERIANDAWN/RADAR.H new file mode 100644 index 000000000..ca7b59d47 --- /dev/null +++ b/TIBERIANDAWN/RADAR.H @@ -0,0 +1,211 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\radar.h_v 2.17 16 Oct 1995 16:48:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADAR_H +#define RADAR_H + +#include "display.h" + +class RadarClass: public DisplayClass +{ + public: + int RadX; + int RadOffX; + int RadY; + int RadOffY; + int RadWidth; + int RadHeight; + int RadIWidth; + int RadIHeight; + int RadPWidth; + int RadPHeight; + + RadarClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual bool Map_Cell(CELL cell, HouseClass * house, bool and_for_allies); + virtual CELL Click_Cell_Calc(int x, int y); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); +// virtual void Set_Tactical_Position(int x, int y, int leptonx=0, int leptony=0); +// virtual void Set_Tactical_Position(CELL cell); + virtual void Set_Tactical_Position(COORDINATE coord); + void Zoom_Mode(CELL cell); + int Click_In_Radar(int &x, int &y, bool change=false); + void Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y); + + void Set_Radar_Position(CELL cell); + CELL Radar_Position(void); + bool Radar_Activate(int control); + void Plot_Radar_Pixel(CELL cell); + void Radar_Pixel(CELL cell); + void Coord_To_Radar_Pixel(COORDINATE coord, int &x, int &y); + void Cursor_Cell(CELL cell, int value); + void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen); + void Radar_Cursor(int forced = false); + void Render_Terrain(CELL cell, int x, int y, int size); + bool Cell_On_Radar(CELL cell); + void Render_Infantry(CELL cell, int x, int y, int size); + void Render_Overlay(CELL cell, int x, int y, int size); + void Radar_Anim(void); + bool Is_Radar_Active(void) {return IsRadarActive;}; + bool Is_Radar_Activating(void) {return IsRadarActivating;}; + bool Is_Radar_Existing(void) {return(DoesRadarExist);}; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Toggles player names on & off + */ + void Player_Names(bool on); + int Is_Player_Names(void) {return IsPlayerNames;} + void Draw_Names(void); + int Is_Zoomed(void) {return IsZoomed;} + + protected: + + /* + ** Radar map constant values. + */ + enum RadarClassEnums { + RADAR_ACTIVATED_FRAME=22, + MAX_RADAR_FRAMES = 41 + }; + + /* + ** If the radar map must be completely redrawn, then this flag will be true. + ** Typical causes of this would be when the radar first appears, or when the + ** screen has been damaged. + */ + unsigned IsToRedraw:1; + unsigned RadarCursorRedraw:1; + + /* + ** If the radar map is visible then this flag is true. + */ + unsigned DoesRadarExist:1; + unsigned IsRadarActive:1; + unsigned IsRadarActivating:1; + unsigned IsRadarDeactivating:1; + + /* + ** Special radar frame is set when a new location is selected on the + ** radar map. It counts down through the special radar cursors until + ** either the radar cursor becomes normal or the radar cursor is moved + ** again. + */ + unsigned SpecialRadarFrame:3; + unsigned RadarAnimFrame:6; + + static void const * RadarAnim; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class RadarClass; + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static TacticalClass RadarButton; + + private: + + /* + ** The current radar position as the upper left corner cell for the + ** radar map display. The width and height is controlled by the + ** actual dimensions of the radar map display box (in pixels). + */ + unsigned RadarX; + unsigned RadarY; + unsigned RadarCell; + + /* + ** This is the origin (pixel offsets) for the upper left corner + ** of the radar map within the full radar map area of the screen. + ** This is biased so that the radar map, when smaller than full + ** size will appear centered. + */ + unsigned BaseX; + unsigned BaseY; + + unsigned RadarWidth; + unsigned RadarCellWidth; + unsigned RadarHeight; + unsigned RadarCellHeight; + + /* + ** If the radar map is in zoom mode, then this value will be true. + */ + unsigned IsZoomed:1; + + /* + ** This flag is true if the radar map is in its special show-the-player + ** names mode. + */ + unsigned IsPlayerNames:1; + + /* + ** This is the list of radar pixels that need to be updated. Only a partial + ** list is maintained for maximum speed. + */ + unsigned PixelPtr; + int ZoomFactor; + enum PixelStackEnums {PIXELSTACK=200}; + CELL PixelStack[PIXELSTACK]; +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/RADIO.CPP b/TIBERIANDAWN/RADIO.CPP new file mode 100644 index 000000000..3ab7b6fc4 --- /dev/null +++ b/TIBERIANDAWN/RADIO.CPP @@ -0,0 +1,249 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\radio.cpv 2.19 16 Oct 1995 16:50:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** These are the text representations of the radio messages that can be transmitted. +*/ +char const * RadioClass::Messages[RADIO_COUNT] = { + "hisssss", + "Roger.", + "Come in.", + "Over and out.", + "Requesting transport.", + "Attach to transport.", + "I've got a delivery for you.", + "I'm performing load/unload maneuver. Be careful.", + "I'm clear.", + "You are clear to unload. Driving away now.", + "Am unable to comply.", + "I'm starting construction now... act busy.", + "I've finished construction. You are free.", + "We bumped, redraw yourself please.", + "I'm trying to load up now.", + "May I become a passenger?", + "Are you ready to receive shipment?", + "Are you trying to become a passenger?", + "Move to location X.", + "Do you need to move?", + "All right already. Now what?", + "I'm a passenger now.", + "Backup into refinery now.", + "Run away!", + "Tether established.", + "Tether broken.", + "Repair one step.", + "Are you prepared to fight?", + "Attack this target please.", + "Reload one step.", + "Take this kick! You... You...", + "Take this punch! You... You...", + "Fancy a little fisticuffs, eh?" +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * * + * This displays the radio connection value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void RadioClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(34, 5);mono->Printf(Messages[LastMessage]); + if (Radio) { + mono->Set_Cursor(50, 1);mono->Printf("%04X", Radio->As_Target()); + } + MissionClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * * + * This is the base version of what should happen when a radio message is received. It * + * turns the radio off when the "OVER_OUT" message is received. All other messages are * + * merely acknowledged with a "ROGER". * + * * + * INPUT: from -- The object that is initiating this radio message (always valid). * + * * + * message -- The radio message received. * + * * + * param -- Reference to optional value that might be used to return more * + * information than can be conveyed in the simple radio response * + * messages. * + * * + * OUTPUT: Returns with the response radio message. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 09/24/1994 JLB : Streamlined to be only a communications carrier. * + * 05/22/1995 JLB : Recognized who is sending the message * + *=============================================================================================*/ +RadioMessageType RadioClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + /* + ** Keep a record of the last message received by this radio. + */ + LastMessage = message; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + ** This only applies if the message is coming from the object that + ** has an established conversation with this object. + */ + if (from == Radio && message == RADIO_OVER_OUT) { + MissionClass::Receive_Message(from, message, param); + Radio_Off(); + return(RADIO_ROGER); + } + + /* + ** The "hello" message is an attempt to establish contact. If this radio + ** is already in an established conversation with another object, then + ** return with "negative". If all is well, return with "roger". + */ + if (message == RADIO_HELLO && Strength) { + if (Radio == from || !Radio) { + Radio = from; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(MissionClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * * + * This routine is used to transmit a radio message from this object to another. Most * + * inter object coordination is handled through this mechanism. * + * * + * INPUT: to -- Pointer to the object that will receive the radio message. * + * * + * message -- The message itself (see RadioType). * + * * + * param -- Optional reference to parameter that might be used to pass or * + * receive additional information. * + * * + * OUTPUT: Returns with the response radio message from the receiving object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, long & param, RadioClass * to) +{ + if (!to) { + to = Contact_With_Whom(); + } + + /* + ** If there is no target for the radio message, then always return static. + */ + if (!to) return(RADIO_STATIC); + + /* + ** Handle some special case processing that occurs when certain messages + ** are transmitted. + */ + if (to == Radio && message == RADIO_OVER_OUT) { + Radio = 0; + } + + /* + ** If this object is not in radio contact but the message + ** indicates that radio contact should be established, then + ** try to do so. If the other party agrees then contact + ** is established. + */ + if (/*!Radio &&*/ message == RADIO_HELLO) { + Transmit_Message(RADIO_OVER_OUT); + if (to->Receive_Message(this, message, param) == RADIO_ROGER) { + Radio = to; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(to->Receive_Message(this, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * * + * This routine will break radio contact as the object is entering limbo state. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool RadioClass::Limbo(void) +{ + if (!IsInLimbo) { + Transmit_Message(RADIO_OVER_OUT); + } + return(MissionClass::Limbo()); +} + + +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, RadioClass * to) {return(Transmit_Message(message, LParam, to));}; diff --git a/TIBERIANDAWN/RADIO.H b/TIBERIANDAWN/RADIO.H new file mode 100644 index 000000000..ce0fe0515 --- /dev/null +++ b/TIBERIANDAWN/RADIO.H @@ -0,0 +1,105 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\radio.h_v 2.15 16 Oct 1995 16:45:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADIO_H +#define RADIO_H + +//#include "object.h" +#include "mission.h" +//#include "flasher.h" + +class ObjectClass; +class TechnoClass; + + +/**************************************************************************** +** Radio contact is controlled by this class. It handles the mundane chore +** of keeping the radio contact alive as well as broadcasting messages +** to the receiving radio. Radio contact is primarily used when one object +** is in "command" of another. +*/ +class RadioClass : public MissionClass { + private: + + /* + ** This is a record of the last message received by this receiver. + */ + RadioMessageType LastMessage; + + /* + ** This is the object that radio communication has been established + ** with. Although is is only a one-way reference, it is required that + ** the receiving radio is also tuned to the object that contains this + ** radio set. + */ + RadioClass * Radio; + + /* + ** This is a text representation of all the possible radio messages. This + ** text is used for monochrome debug printing. + */ + static char const * Messages[RADIO_COUNT]; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + RadioClass(void) {Radio = 0;LastMessage = RADIO_STATIC;}; + virtual ~RadioClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool In_Radio_Contact(void) const {return (Radio != 0);}; + void Radio_Off(void) {Radio = 0;}; + TechnoClass * Contact_With_Whom(void) const {return (TechnoClass*)Radio;}; + + // Inherited from base class(es). + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual RadioMessageType Transmit_Message(RadioMessageType message, long & param=LParam, RadioClass * to=NULL); + virtual RadioMessageType Transmit_Message(RadioMessageType message, RadioClass * to); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Limbo(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/TIBERIANDAWN/RAND.CPP b/TIBERIANDAWN/RAND.CPP new file mode 100644 index 000000000..6557f9bef --- /dev/null +++ b/TIBERIANDAWN/RAND.CPP @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\rand.cpv 1.6 16 Oct 1995 16:51:10 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/25/95 * + * * + * Last Update : June 17, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Sim_Random -- Returns 0 - 255 * + * Sim_IRandom -- Returns minval to maxval, inclusive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +int SimRandIndex = 0; + +/*************************************************************************** + * Sim_Random -- Returns 0 - 255 * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 - 255, at random * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Takes advantage of character math wrap. * + *=========================================================================*/ +int Sim_Random(void) +{ + static unsigned char _randvals[] = { + 0x47, 0xce, 0xc6, 0x6e, 0xd7, 0x9f, 0x98, 0x29, 0x92, 0x0c, 0x74, 0xa2, + 0x65, 0x20, 0x4b, 0x4f, 0x1e, 0xed, 0x3a, 0xdf, 0xa5, 0x7d, 0xb5, 0xc8, + 0x86, 0x01, 0x81, 0xca, 0xf1, 0x17, 0xd6, 0x23, 0xe1, 0xbd, 0x0e, 0xe4, + 0x62, 0xfa, 0xd9, 0x5c, 0x68, 0xf5, 0x7f, 0xdc, 0xe7, 0xb9, 0xc4, 0xb3, + 0x7a, 0xd8, 0x06, 0x3e, 0xeb, 0x09, 0x1a, 0x31, 0x3f, 0x46, 0x28, 0x12, + 0xf0, 0x10, 0x84, 0x76, 0x3b, 0xc5, 0x53, 0x18, 0x14, 0x73, 0x7e, 0x59, + 0x48, 0x93, 0xaa, 0x1d, 0x5d, 0x79, 0x24, 0x61, 0x1b, 0xfd, 0x2b, 0xa8, + 0xc2, 0xdb, 0xe8, 0x2a, 0xb0, 0x25, 0x95, 0xab, 0x96, 0x83, 0xfc, 0x5f, + 0x9c, 0x32, 0x78, 0x9a, 0x9e, 0xe2, 0x8e, 0x35, 0x4c, 0x41, 0xa1, 0x69, + 0x5a, 0xfe, 0xa7, 0xa4, 0xf6, 0x6d, 0xc1, 0x58, 0x0a, 0xcf, 0xea, 0xc3, + 0xba, 0x85, 0x99, 0x8d, 0x36, 0xb6, 0xdd, 0xd3, 0x04, 0xe6, 0x45, 0x0d, + 0x60, 0xae, 0xa3, 0x22, 0x4d, 0xe9, 0xc9, 0x9b, 0xb7, 0x0f, 0x02, 0x42, + 0xf9, 0x0b, 0x8f, 0x43, 0x44, 0x87, 0x70, 0xbe, 0xe3, 0xf8, 0xee, 0xa9, + 0xbc, 0xc0, 0x67, 0x33, 0x16, 0x37, 0x57, 0xad, 0x5e, 0x9d, 0x64, 0x40, + 0x54, 0x05, 0x2c, 0xe0, 0xb2, 0x97, 0x08, 0xaf, 0x75, 0x8a, 0x5b, 0xfb, + 0x4e, 0xbf, 0x91, 0xf3, 0xcb, 0x7c, 0x63, 0xef, 0x89, 0x52, 0x6c, 0x2f, + 0x21, 0x4a, 0xf7, 0xcd, 0x2e, 0xf4, 0xc7, 0x6f, 0x19, 0xb1, 0x66, 0xcc, + 0x90, 0x8c, 0x50, 0x51, 0x26, 0x7b, 0xda, 0x49, 0x80, 0x30, 0x55, 0x1f, + 0xd2, 0xb4, 0xd1, 0xd5, 0x6b, 0xf2, 0x72, 0xbb, 0x13, 0x3d, 0xff, 0x15, + 0x38, 0xe5, 0xd4, 0xde, 0x2d, 0x27, 0x94, 0xa0, 0xd0, 0x39, 0x82, 0x8b, + 0x03, 0xac, 0x3c, 0x34, 0x77, 0xb8, 0xec, 0x00, 0x07, 0x1c, 0x88, 0xa6, + 0x56, 0x11, 0x71, 0x6a, + }; + + ((unsigned char&)SimRandIndex)++; +// SimRandIndex &= 0xff; + return(_randvals[SimRandIndex]); +} + + +/*************************************************************************** + * Sim_IRandom -- returns minval to maxval, inclusive * + * * + * INPUT: * + * minval,maxval range limits * + * * + * OUTPUT: * + * random value * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Uses fixed point math helper routine. * + *=========================================================================*/ +int Sim_IRandom(int minval, int maxval) +{ + return(Fixed_To_Cardinal((maxval-minval), Sim_Random()) + minval); +} + + +/*--------------------------------------------------------------------------- +This routine can be used to create the _RandVals[] table. It will be +different every time it's run. +---------------------------------------------------------------------------*/ +#if 0 +#include +#include +#include +#include + +void main(void); + +void main(void) +{ + int i; + FILE *fp; + int r; + char is_used[256]; + char rand_val[256]; + + srand ( time ( NULL ) ) ; + + for (i = 0; i < 256; i++) { + is_used[i] = 0; + } + + /* + ** Store the digits 0 to 255 in a random order within the 'rand_val' array + ** This ensures our random number sequence represents every value. + */ + for (i = 0; i < 256; i++) { + if (kbhit()!=0) { + printf("ABORTED!\n"); + break; + } + r = (rand() * 256) / RAND_MAX; // r is the index into array + if (is_used[r]==0) { + is_used[r] = 1; + rand_val[r] = i; + } else { + i--; + } + } + + fp = fopen("rand.cpp","wt"); + if (fp==NULL) { + printf("File error\n"); + exit(0); + } + + fprintf(fp, "unsigned char _RandVals[] = {\n"); + for (i = 0; i < 256; i++) { + fprintf(fp,"0x%02x,\n",rand_val[i]); + } + fprintf(fp,"};\n"); + fclose(fp); + +} + +#endif + diff --git a/TIBERIANDAWN/RAWFILE.CPP b/TIBERIANDAWN/RAWFILE.CPP new file mode 100644 index 000000000..90057bc65 --- /dev/null +++ b/TIBERIANDAWN/RAWFILE.CPP @@ -0,0 +1,1346 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include + +#include "rawfile.h" + +#ifndef WIN32 +#include +#include +#include +extern short Hard_Error_Occured; +#endif + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Error(int , int , char const * ) +{ +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const * filename) : + Rights(0), + BiasStart(0), + BiasLength(-1), + Handle(NULL_HANDLE), + Filename(filename), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +#pragma warning(disable:4996) +char const * RawFileClass::Set_Name(char const * filename) +{ + if (Filename != NULL && Allocated) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (filename == NULL) return(NULL); + + Bias(0); + + Filename = strdup(filename); + if (Filename == NULL) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (Filename == NULL) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetitively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ + #ifndef WIN32 + Hard_Error_Occured = 0; + #endif + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + #endif + break; + + case WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_creat(Filename, 0, &Handle); + #endif + break; + + case READ|WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); + #endif + break; + } + + /* + ** Biased files must be positioned past the bias start position. + */ + if (BiasStart != 0 || BiasLength != -1) { + Seek(0, SEEK_SET); + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == NULL_HANDLE) { + +#ifdef WIN32 +// return(false); + Error(GetLastError(), false, Filename); +// continue; +#else + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } + continue; +#endif + } + break; + } + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ +#ifndef WIN32 + bool open_failed; +#endif + + if (Filename == NULL) return(false); + + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +#ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (Handle == NULL_HANDLE) { + return(false); + } + break; +#else + + Hard_Error_Occured = 0; + open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } + } + if (!open_failed) break; +#endif + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ +#ifdef WIN32 + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + if (_dos_close(Handle)) { + Error(errno, false, Filename); + } +#endif + Handle = NULL_HANDLE; + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + +#ifdef WIN32 + /* + ** Try to close the file. If there was an error (who knows what that could be), then + ** call the error routine. + */ + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ + Hard_Error_Occured = 0; + if (_dos_close(Handle)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + break; + } + +#endif + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = NULL_HANDLE; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void * buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** A biased file has the requested read length limited to the bias length of + ** the file. + */ + if (BiasLength != -1) { + int remainder = BiasLength - Seek(0); + size = size < remainder ? size : remainder; + } + +#ifdef WIN32 + long total = 0; + while (size > 0) { + bytesread = 0; + + SetErrorMode(SEM_FAILCRITICALERRORS); + if (!ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + size -= bytesread; + total += bytesread; + Error(GetLastError(), true, Filename); + SetErrorMode(0); + continue; + } + SetErrorMode(0); + size -= bytesread; + total += bytesread; + if (bytesread == 0) break; + } + bytesread = total; + +#else + + int readresult; + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size < 32000L ? size : 32000L; + + Hard_Error_Occured = 0; + readresult = _dos_read(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + } + } + } +#endif //WIN32 + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const * buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + +#ifdef WIN32 + if (!WriteFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + Error(GetLastError(), false, Filename); + } + +#else + + int writeresult; + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + + Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = _dos_write(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } + } + } +#endif //WIN32 + + /* + ** Fixup the bias length if necessary. + */ + if (BiasLength != -1) { + if (Raw_Seek(0) > BiasStart+BiasLength) { + BiasLength = Raw_Seek(0) - BiasStart; + } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + + /* + ** A file that is biased will have a seek operation modified so that the file appears to + ** exist only within the bias range. All bytes outside of this range appear to be + ** non-existant. + */ + if (BiasLength != -1) { + switch (dir) { + case SEEK_SET: + if (pos > BiasLength) { + pos = BiasLength; + } + pos += BiasStart; + break; + + case SEEK_CUR: + break; + + case SEEK_END: + dir = SEEK_SET; + pos += BiasStart + BiasLength; +// pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; +// pos = (pos >= BiasStart) ? pos : BiasStart; + break; + } + + /* + ** Perform the modified raw seek into the file. + */ + long newpos = Raw_Seek(pos, dir) - BiasStart; + + /* + ** Perform a final double check to make sure the file position fits with the bias range. + */ + if (newpos < 0) { + newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; + } + if (newpos > BiasLength) { + newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; + } + return(newpos); + } + + /* + ** If the file is not biased in any fashion, then the normal seek logic will + ** work just fine. + */ + return(Raw_Seek(pos, dir)); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + + /* + ** A biased file already has its length determined. + */ + if (BiasLength != -1) { + return(BiasLength); + } + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + +#ifdef WIN32 + size = GetFileSize(Handle, NULL); + + /* + ** If there was in internal error, then call the error function. + */ + if (size == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { + Hard_Error_Occured = 0; + size = filelength(Handle); + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (size == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + + BiasLength = size-BiasStart; + return(BiasLength); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + + /* + ** A biased file must be at least as long as the bias offset. Seeking to the + ** appropriate start offset has the effect of lengthening the file to the + ** correct length. + */ + if (BiasLength != -1) { + Seek(0, SEEK_SET); + } + + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +#ifdef WIN32 + if (!DeleteFile(Filename)) { + Error(GetLastError(), false, Filename); + return(false); + } +#else + Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } +#endif + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win32 method. * + *=============================================================================================*/ +unsigned long RawFileClass::Get_Date_Time(void) +{ +#ifdef WIN32 + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + WORD dosdate; + WORD dostime; + FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); + return((dosdate << 16) | dostime); + } + return(0); +#else + unsigned short time; + unsigned short date; + unsigned long datetime = 0; + + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + if ( _dos_getftime( Handle, &date, &time ) ) { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + if ( _dos_getftime( Handle, &date, &time ) ) { + RawFileClass::Close(); + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + + RawFileClass::Close(); + } else { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } + + // + // combine the date and time as a long + // + datetime = (date << 16) + time; + + return( datetime ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win 32 method * + *=============================================================================================*/ +bool RawFileClass::Set_Date_Time(unsigned long datetime) +{ +#ifdef WIN32 + if (RawFileClass::Is_Open()) { + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + FILETIME filetime; + if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { + return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime)); + } + } + } + return(false); +#else + unsigned short time; + unsigned short date; + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + // + // only set the date and time if open for READ only + // + if ( Rights == READ ) { + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + // + // return true indicating success + // + return( true ); + } + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + RawFileClass::Close(); + // + // return true indicating success + // + return( true ); + } + + RawFileClass::Close(); + } + } + + // + // return false indicating error + // + return( false ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * * + * This will bias a file by giving it an artificial starting position and length. By * + * using this routine, it is possible to 'fool' the file into ignoring a header and * + * trailing extra data. An example of this would be a file inside of a mixfile. * + * * + * INPUT: start -- The starting offset that will now be considered the start of the * + * file. * + * * + * length -- The forced length of the file. For files that are opened for write, * + * this serves as the artificial constraint on the file's length. For * + * files opened for read, this limits the usable file size. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1996 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Bias(int start, int length) +{ + if (start == 0) { + BiasStart = 0; + BiasLength = -1; + return; + } + + BiasLength = RawFileClass::Size(); + BiasStart += start; + if (length != -1) { + BiasLength = BiasLength < length ? BiasLength : length; + } + BiasLength = BiasLength > 0 ? BiasLength : 0; + + /* + ** Move the current file offset to a legal position if necessary and the + ** file was open. + */ + if (Is_Open()) { + RawFileClass::Seek(0, SEEK_SET); + } +} + + +/*********************************************************************************************** + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * * + * This will perform a seek on the file as if it were unbiased. This is in spite of any * + * bias setting the file may have. The ability to perform a raw seek in this fasion is * + * necessary to maintain the bias ability. * + * * + * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * + * * + * dir -- The origin of the seek operation. * + * * + * OUTPUT: Returns with the new position of the seek operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Raw_Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + +#ifdef WIN32 + switch (dir) { + case SEEK_SET: + dir = FILE_BEGIN; + break; + + case SEEK_CUR: + dir = FILE_CURRENT; + break; + + case SEEK_END: + dir = FILE_END; + break; + } + + pos = SetFilePointer(Handle, pos, NULL, dir); + + /* + ** If there was an error in the seek, then bail with an error condition. + */ + if (pos == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ + Hard_Error_Occured = 0; + pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} diff --git a/TIBERIANDAWN/RAWFILE.H b/TIBERIANDAWN/RAWFILE.H new file mode 100644 index 000000000..c17f35c57 --- /dev/null +++ b/TIBERIANDAWN/RAWFILE.H @@ -0,0 +1,332 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +#define NULL_HANDLE INVALID_HANDLE_VALUE +#define HANDLE_TYPE HANDLE +#else +#define NULL_HANDLE -1 +#define HANDLE_TYPE int +#endif + +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +#ifndef WWERROR +#define WWERROR -1 +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void); + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + void Bias(int start, int length=-1); + + HANDLE_TYPE Get_File_Handle(void) { return (Handle); }; + + /* + ** These bias values enable a sub-portion of a file to appear as if it + ** were the whole file. This comes in very handy for multi-part files such as + ** mixfiles. + */ + int BiasStart; + int BiasLength; + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + long Raw_Seek(long pos, int dir=SEEK_CUR); + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + HANDLE_TYPE Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + const char *Filename; + + // + // file date and time are in the following formats: + // + // date bits 0-4 day (0-31) + // bits 5-8 month (1-12) + // bits 9-15 year (0-119 representing 1980-2099) + // + // time bits 0-4 second/2 (0-29) + // bits 5-10 minutes (0-59) + // bits 11-15 hours (0-23) + // + unsigned short Date; + unsigned short Time; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : + Rights(READ), + BiasStart(0), + BiasLength(-1), + #ifdef WIN32 + Handle(INVALID_HANDLE_VALUE), + #else + Handle(-1), + #endif + Filename(0), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::~RawFileClass(void) +{ + Close(); + if (Allocated && Filename) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } +} + + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ +#ifdef WIN32 + return(Handle != INVALID_HANDLE_VALUE); +#else + return (Handle >= 0); +#endif +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/REAL.H b/TIBERIANDAWN/REAL.H new file mode 100644 index 000000000..70dfda043 --- /dev/null +++ b/TIBERIANDAWN/REAL.H @@ -0,0 +1,937 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\function.h_v 2.21 16 Oct 1995 16:46:44 JOE_BOSTIC $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + TurretClass + ³ + TarComClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ + ³ InfantryTypeClass + AircraftTypeClass +#endif + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +enum {false=0,true=1}; +typedef int bool; + + + +#define _WIN32 +#define WIN32 //_LEAN_AND_MEAN +#include + + +/********************************************************************** +** If the following define is enabled, then the memory checking code +** will be disabled. +*/ +#define NOMEMCHECK + +#include "watcom.h" +#define FILE_H +#define WWMEM_H +#include "compat.h" +#include +#include "jshell.h" + +#ifdef +#undef +#endif +#ifdef _pascal +#undef _pascal +#endif +#ifdef pascal +#undef pascal +#endif + +#define pascal +#define _pascal +#define + + + + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +// Don't complain if these headers aren't referenced. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** VQ player specific includes. +*/ +#include +#include + +extern bool GameActive; +extern long LParam; + +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" + + +/* +** Greenleaf specific includes. +*/ +#include +#include + + +extern long Frame; +inline CELL Coord_XCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+1));} +inline CELL Coord_YCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+3));} +//inline CELL Coord_Cell(COORD coord){return (CELL)(((*(((unsigned short *)&coord)+1) & 0xFF00) >> 2) | *(((unsigned char *)&coord)+1));} +CELL Coord_Cell(COORDINATE coord); +#pragma aux Coord_Cell parm [eax] \ + modify [ebx] \ + value [ax] = \ + "mov ebx,eax" \ + "shr eax,010h" \ + "xor al,al" \ + "shr eax,2" \ + "or al,bh" + + +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "slider.h" +#include "list.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +//#include "mapedit.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "teamtype.h" // Team type objects. +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // ??? +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "ipxmgr.h" +#include "combuf.h" +#include "connect.h" +#include "connmgr.h" +#include "noseqcon.h" +#include "msglist.h" +#include "nullconn.h" +#include "nullmgr.h" +#include "phone.h" +#include "loaddlg.h" +#include "ipxaddr.h" + +/**************************************************************************** +** This is a "node", used for the lists of available games & players. The +** 'Game' structure is used for games; the 'Player' structure for players. +*/ +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; + IPXAddressClass Address; + union { + struct { + int Version; + unsigned char IsOpen; + unsigned long LastTime; + } Game; + struct { + HousesType House; + unsigned char Color; + } Player; + }; +} NodeNameType; + + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); + +/* +** AUDIO.CPP +*/ +int Sound_Effect(VocType voc, VolType volume, int variation=1, signed short panvalue=0); +void Speak(VoxType voice); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord=NULL, int variation=1); +bool Is_Speaking(void); + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true); +bool Main_Loop(); +TheaterType Theater_From_Name(char const *name); +//DirType Rotation_Calc(DirType current, DirType desired, int rate); +void Main_Game(int argc, char *argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const *basename); +SourceType Source_From_Name(char const *name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const *shapefile, int shapenum, int frames, int zoomfactor); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0); +void Go_Editor(bool flag); +long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes); + +char *CC_Get_Shape_Filename(void const *shapeptr ); +void CC_Add_Shape_To_Global(void const *shapeptr, char *filename, char code ); + +void Bubba_Print(char *format,...); + +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +unsigned long Disk_Space_Available(void); + +void Validate_Error(char *name); +void const * Hires_Retrieve(char *name); +int Get_Resolution_Factor(void); + + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + + +/* +** COORD.CPP +*/ +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); +//void Move_Point(unsigned short &x, unsigned short &y, DirType dir, unsigned short distance); + +/* +** COORDA.CPP +*/ +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +//} + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, unsigned fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, unsigned width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int __cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag); + +/* +** DISPLAY.CPP +*/ + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); + +/* +** FINDPATH.CPP +*/ +//PathType * Find_Path(CELL source, CELL dest, FacingType *final_moves, int maxlen, int (*callback)(CELL, FacingType), int threshhold); +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); + +/* +** INI.CPP +*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); +void Write_Scenario_Ini(char *root); +bool Read_Scenario_Ini(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); + +//10/28/2019 JAS - Need ability to read scenarios in any directory +bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const char* root, bool fresh); + +//10/14/2019 JAS - Need ability to read the movies out of each scenario file. +bool Read_Movies_From_Scenario_Ini(char *root, bool fresh = true); + +/* +** INIT.CPP +*/ +void Uninit_Game(void); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); +int Version_Number(void); +void Save_Recording_Values(void); +void Load_Recording_Values(void); + +/* +** JSHELL.CPP +*/ +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); + +/* +** KEYFBUFF.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +#ifdef __cplusplus +} +#endif + +/* +** KEYFRAME.CPP +*/ +int Get_Last_Frame_Length(void); +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Read_MultiPlayer_Settings (void); +void Write_MultiPlayer_Settings (void); +void Read_Scenario_Descriptions (void); +void Free_Scenario_Descriptions(void); +void Computer_Message(void); +int Surrender_Dialog(void); + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(void); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TARGET whom, TARGET target); +bool Queue_Destination(TARGET whom, TARGET target); +bool Queue_Mission(TARGET whom, MissionType mission); +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RAND.CPP +*/ +int Sim_IRandom(int minval, int maxval); +int Sim_Random(void); + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass *team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(FileClass &file); +bool Save_Misc_Values(FileClass &file); +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep); +bool Load_Game(int id); +bool Read_Object (void *ptr, int base_size, int class_size, FileClass & file, void * vtable); +bool Save_Game(int id,char *descr); +bool Write_Object (void *ptr, int class_size, FileClass & file); +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr); +TechnoTypeClass const * Target_To_TechnoType(TARGET target); +void * Get_VTable(void *ptr, int base_size); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); +void Set_VTable(void *ptr, int base_size, void *vtable); + +/* +** SCENARIO.CPP +*/ +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); + +/* +** SCORE.CPP +*/ +void Map_Selection(void); +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicBufferClass *seen , int delay=0, int dagger=0); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); +extern GraphicBufferClass *PseudoSeenBuff; + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(void); + +/* +** SUPPORT.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +void __cdecl Remove_From_List(void **list, int *index, void * ptr); +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac); +void __cdecl Fat_Put_Pixel(int x, int y, int color, int size, GraphicViewPortClass &); +void __cdecl strtrim(char *buffer); +long __cdecl Get_EAX( void ); +#ifdef __cplusplus +} +#endif + +/* +** TARCOM.CPP +*/ + +/* +** TARGET.CPP +*/ +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target); +AnimClass * As_Animation(TARGET target); +BuildingClass * As_Building(TARGET target); +BulletClass * As_Bullet(TARGET target); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target); +TeamClass * As_Team(TARGET target); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target); +//TerrainClass * As_Terrain(TARGET target); +TriggerClass * As_Trigger(TARGET target); +UnitClass * As_Unit(TARGET target); +bool Target_Legal(TARGET target); +ObjectClass * As_Object(TARGET target); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** Inline miscellaneous functions. +*/ +#define XYP_COORD(x,y) (((x)*ICON_LEPTON_W)/CELL_PIXEL_W + ((((y)*ICON_LEPTON_H)/CELL_PIXEL_H)<<16)) +inline FacingType Dir_Facing(DirType facing) {return (FacingType)(((unsigned char)(facing+0x10)&0xFF)>>5);} +inline DirType Facing_Dir(FacingType facing) {return (DirType)((int)facing << 5);} +inline int Cell_To_Lepton(int cell) {return cell<<8;} +inline int Lepton_To_Cell(int lepton) {return ((unsigned)(lepton + 0x0080))>>8;} +inline CELL XY_Cell(int x, int y) {return ((CELL)(((y)<<6)|(x)));} +inline COORDINATE XY_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG(y, x));} +inline int Coord_X(COORDINATE coord) {return (short)(LOW_WORD(coord));} +inline int Coord_Y(COORDINATE coord) {return (short)(HIGH_WORD(coord));} +inline int Cell_X(CELL cell) {return (int)(((unsigned)cell) & 0x3F);} +inline int Cell_Y(CELL cell) {return (int)(((unsigned)cell) >> 6);} +inline int Dir_Diff(DirType dir1, DirType dir2) {return (int)(*((signed char*)&dir2) - *((signed char*)&dir1));} +inline CELL Coord_XLepton(COORDINATE coord) {return (CELL)(*((unsigned char*)&coord));} +inline CELL Coord_YLepton(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+2));} +//inline COORD CellXY_Coord(unsigned x, unsigned y) {return (COORD)(MAKE_LONG(y<<8, x<<8));} +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) + *((short*)(&coord2)+1)), (*((short*)(&coord1)) + *((short*)(&coord2))));} +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) - *((short*)(&coord2)+1)), (*((short*)(&coord1)) - *((short*)(&coord2))));} +inline COORDINATE Coord_Snap(COORDINATE coord) {return (COORDINATE)MAKE_LONG((((*(((unsigned short *)&coord)+1))&0xFF00)|0x80), (((*((unsigned short *)&coord))&0xFF00)|0x80));} +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((unsigned short *)(&coord1)+1) + *((unsigned short *)(&coord2)+1))>>1, (*((unsigned short *)(&coord1)) + *((unsigned short *)(&coord2)))>>1);} +inline COORDINATE Cell_Coord(CELL cell) {return (COORDINATE) MAKE_LONG( (((cell & 0x0FC0)<<2)|0x80), ((((cell & 0x003F)<<1)+1)<<7) );} +inline COORDINATE XYPixel_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG((int)(((long)y*(long)ICON_LEPTON_H)/(long)ICON_PIXEL_H)/*+LEPTON_OFFSET_Y*/, (int)(((long)x*(long)ICON_LEPTON_W)/(long)ICON_PIXEL_W)/*+LEPTON_OFFSET_X*/));} +//inline int Facing_To_16(int facing) {return Facing16[facing];} +inline int Facing_To_32(DirType facing) {return Facing32[facing];} +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +//inline int Direction16(COORDINATE coord1, COORD coord2) {return (Desired_Facing16(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(CELL cell1, CELL cell2) {return (DirType)(Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) {return (Coord_Snap(Coord_Add(AdjacentCoord[dir & 0x07], coord)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) {return Adjacent_Cell(coord, Dir_Facing(dir));} +inline CELL Adjacent_Cell(CELL cell, FacingType dir) {return (CELL)(cell + AdjacentCell[dir]);} +inline CELL Adjacent_Cell(CELL cell, DirType dir) {return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]);} +inline int Lepton_To_Pixel(int lepton) {return ((lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2)) / ICON_LEPTON_W;} +inline int Pixel_To_Lepton(int pixel) {return ((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2)) / ICON_PIXEL_W;} +//inline FacingType Facing_To_8(DirType facing) {return (FacingType)(((unsigned char)(facing|0x10))>>5);} +inline COORDINATE XYP_Coord(int x,int y) {return XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));}; +inline char const * Text_String(int string) {return(Extract_String(SystemStrings, string));}; + + +template inline T Random_Pick(T a, T b) +{ + return (T)IRandom((int)a, (int)b); +}; + +template inline T Sim_Random_Pick(T a, T b) +{ + return (T)Sim_IRandom((int)a, (int)b); +}; + + +#ifdef CHEAT_KEYS +#define Check_Ptr(ptr,file,line) \ +{ \ + if (!ptr) { \ + Mono_Clear_Screen(); \ + Mono_Printf("NULL Pointer, Module:%s, line:%d!\n",file,line); \ + Prog_End(); \ + exit(EXIT_SUCCESS); \ + } \ +} +#else +#define Check_Ptr(ptr,file,line) +#endif + +/* +** These routines are for coding & decoding multiplayer ID's +*/ +inline PlayerColorType MPlayerID_To_ColorIndex(unsigned short id) {return (PlayerColorType)(id >> 4);} +inline HousesType MPlayerID_To_HousesType(unsigned short id) {return ((HousesType)(id & 0x000f)); } +inline unsigned short Build_MPlayerID(int c_idx, HousesType htype) { return ((c_idx << 4) | htype); } + + + + +// +// True if we are the currently in focus windows app +// +extern bool GameInFocus; + +extern int ScreenWidth; +extern int ScreenHeight; +extern "C" void ModeX_Blit (GraphicBufferClass *source); +extern void Colour_Debug (int call_number); + + +extern unsigned char *InterpolatedPalettes[50]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; + +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; +} + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); + + +#define CELL_BLIT_ONLY 1 +#define CELL_DRAW_ONLY 2 + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2); +#pragma aux Distance_Coord parm [eax] [ebx] \ + modify [edx ebx] \ + value [eax] = \ + "mov dx,ax" \ + "sub dx,bx" \ + "jg okx" \ + "neg dx" \ + "okx:" \ + "shr eax,16" \ + "shr ebx,16" \ + "sub ax,bx" \ + "jg oky" \ + "neg ax" \ + "oky:" \ + "cmp ax,dx" \ + "jg ok" \ + "xchg ax,dx" \ + "ok:" \ + "shr dx,1" \ + "add ax,dx" + +inline int Distance(COORDINATE coord1, COORDINATE coord2) +{ +#ifdef NEVER + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +#else + return(Distance_Coord(coord1, coord2)); +#endif +} + + + +/*********************************************************************************************** + * Distance -- Determines the cell distance between two cells. * + * * + * Use this routine to determine the distance between the two cells specified. The distance * + * is returned in cells. * + * * + * INPUT: cell1, cell2 -- The two cells to determine the distance between. * + * * + * OUTPUT: Returns with the distance between the two cells in units of cell size. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +inline int Distance(CELL coord1, CELL coord2) +{ + int diff1, diff2; + + diff1 = Cell_Y(coord1) - Cell_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Cell_X(coord1) - Cell_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Number -- Returns the cell ID number for this cell object. * + * * + * Call this routine if you wish to determine what the cell number ID is for the currrent * + * cell object. This ID number is the index number into the cell array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number for this cell object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +inline CELL CellClass::Cell_Number(void) const +{ + return(Map.ID(this)); +} + +#ifndef NOMEMCHECK +#define NO_INTERCEPT +#include "memcheck.h" +#endif + + +void WWDOS_Shutdown(void); + +#endif + diff --git a/TIBERIANDAWN/REGION.H b/TIBERIANDAWN/REGION.H new file mode 100644 index 000000000..cbcc92846 --- /dev/null +++ b/TIBERIANDAWN/REGION.H @@ -0,0 +1,56 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\region.h_v 2.13 16 Oct 1995 16:45:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REGION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/09/95 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef REGION_H +#define REGION_H + + +class RegionClass { + public: + RegionClass(void) {Threat = 0;}; + ~RegionClass(void) {}; + int operator != (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass));}; + int operator == (RegionClass const & region) {return !memcmp(this, ®ion, sizeof(RegionClass));}; + int operator > (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) > 0;}; + int operator < (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) < 0;}; + + void Reset_Threat(void) {Threat = 0;}; + void Adjust_Threat(int threat, int neg) {if (neg) Threat -= threat; else Threat+= threat;}; + int Threat_Value(void) const {return Threat;}; + + protected: + long Threat; +}; + +#endif diff --git a/TIBERIANDAWN/REG_ICON.RC b/TIBERIANDAWN/REG_ICON.RC new file mode 100644 index 000000000..e8543d8a3 --- /dev/null +++ b/TIBERIANDAWN/REG_ICON.RC @@ -0,0 +1,16 @@ +/**************************************************************************** + + +REG_ICON.RC + +produced by Borland Resource Workshop + + +*****************************************************************************/ + +#include "reg_icon.rh" + + + +ICON_1 ICON "conquer.ico" + diff --git a/TIBERIANDAWN/REINF.CPP b/TIBERIANDAWN/REINF.CPP new file mode 100644 index 000000000..273158223 --- /dev/null +++ b/TIBERIANDAWN/REINF.CPP @@ -0,0 +1,663 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\reinf.cpv 2.17 16 Oct 1995 16:48:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REINF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 24, 1994 * + * * + * Last Update : July 4, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * Do_Reinforcements -- Create and place a reinforcement team. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Do_Reinforcements -- Create and place a reinforcement team. * + * * + * This routine is called when a reinforcement team must be created and placed on the map. * + * It will create all members of the team and place them at the location determined from * + * the team composition. The reinforcement team should follow team orders until overriden * + * by AI or player intervention. * + * * + * INPUT: teamtype -- Pointer to the team type to create as a reinforcement. * + * * + * OUTPUT: Was the reinforcement successfully created and placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/18/1995 JLB : Returns success or failure condition. * + * 06/19/1995 JLB : Announces reinforcements. * + *=============================================================================================*/ +bool Do_Reinforcements(TeamTypeClass *teamtype) +{ + /* + ** preform some preliminary checks for validity. + */ + if (!teamtype || !teamtype->ClassCount) return(false); + + /* + ** Create the controlling team. All the objects are grouped + ** under this team control. If there are no missions for this team + ** then don't actually create the team -- it won't serve a purpose. + */ + TeamClass * team = NULL; + if (teamtype->MissionCount) { + team = new TeamClass(teamtype, HouseClass::As_Pointer(teamtype->House)); + if (!team) return(false); + team->Force_Active(); + } + + /* + ** Determine if this team contains its own transport. In such a case, the + ** transport is used as a loaner. This is only true, if there is other + ** objects to be transport. Without such cargo objects, then the transport + ** is presumed to be the reinforcement itself and thus should not be a + ** loaner. + */ + bool okvoice = true; // Presume ok to announce reinforcement? + bool airtransport = false; // Transport can fly in? + bool watertransport = false; // Transport needs a beach to land at? + bool onlytransport = true; // Just transport is in reinforcement? + bool hastransport = false; // Group comes with transport? + int index; + for (index=0; index < teamtype->ClassCount; index++) { + if (teamtype->Class[index]->IsTransporter || teamtype->Class[index]->What_Am_I() == RTTI_AIRCRAFTTYPE) { + hastransport = true; + if (teamtype->Class[index]->What_Am_I() == RTTI_AIRCRAFTTYPE) { + airtransport = true; + } else { + watertransport = (((UnitTypeClass const *)teamtype->Class[index])->Type == UNIT_HOVER); + } + } else { + onlytransport = false; + } + } + + /* + ** Now determine how the reinforcement should be delivered. This is largely determined + ** by whether there is a transport with the reinforcements. + */ + SourceType source = SOURCE_NONE; + if (airtransport) { + source = SOURCE_AIR; + } else { + + if (watertransport) { + source = SOURCE_BEACH; + } else { + + /* + ** Special case for the gunboat. It always arrives according to the shipping source. + */ + if (teamtype->Class[0]->What_Am_I() == RTTI_UNITTYPE && ((UnitTypeClass const *)teamtype->Class[0])->Type == UNIT_GUNBOAT) { + source = SOURCE_SHIPPING; + } else { + source = HouseClass::As_Pointer(teamtype->House)->Edge; + } + } + } + + /* + ** If we can't determine where the reinforcement should come from, then delete it + ** and return a failure condition. + */ + if (source == SOURCE_NONE) { + if (team) delete team; + return(false); + } + + /* + ** Now that the official source for the reinforcement has been determined, the + ** objects themselves must be created. + */ + TechnoClass * transport = NULL; + TechnoClass * object = NULL; + for (index = 0; index < teamtype->ClassCount; index++) { + TechnoTypeClass const * tclass = teamtype->Class[index]; + + for (int sub = 0; sub < teamtype->DesiredNum[index]; sub++) { + ScenarioInit++; + FootClass * temp = (FootClass *)tclass->Create_One_Of(HouseClass::As_Pointer(teamtype->House)); + ScenarioInit--; + + if (temp) { + + /* + ** Add the object to the team. This is true even for the transports. The one + ** exception is for the hover lander which never becomes part of the team. + */ + if (team && (temp->What_Am_I() != RTTI_UNIT || *((UnitClass*)temp) != UNIT_HOVER)) { + ScenarioInit++; + team->Add(temp); + ScenarioInit--; + } + + /* + ** Build the list of transporters and passengers. + */ + if (tclass->IsTransporter && (!airtransport || tclass->What_Am_I() == RTTI_AIRCRAFTTYPE)) { + + /* + ** Transports are considered loaners if they are transporting + ** something. They are presumed to only serve as a delivery + ** agent. + */ + if (!onlytransport && temp->What_Am_I() != RTTI_UNIT) { + temp->IsALoaner = true; + } + + /* + ** Link to the list of transports. + */ + temp->Next = transport; + transport = temp; + } else { + + /* + ** A-10s are always considered loaners since the player should + ** never be allowed to control them. + */ + if (temp->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)temp) == AIRCRAFT_A10) { + temp->IsALoaner = true; + } + + /* + ** Link to the list of normal objects. + */ + temp->Next = object; + object = temp; + } + } + } + } + + /* + ** Bail on this reinforcement if no reinforcements could be created. + ** This is probably because the object maximum was reached. + */ + if (!object && !transport) { + if (team) delete team; + return(false); + } + + /* + ** Now it is time to place the objects on the map. If there is a transport, then the + ** transported objects must be placed inside the transport at this time as well. + */ + if (transport) { + if (object) { + + /* + ** For cargo planes that carry reinforcements, don't announce arrival + ** when the transport is created. The announcement will occur when the + ** transport unloads. + */ + if (transport->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)transport) == AIRCRAFT_CARGO) { + okvoice = false; + } + + transport->Attach((FootClass *)object); + } + object = transport; + } + + /* + ** Pick the location where the reinforcements appear and then place + ** them there. + */ + bool placed = false; + CELL cell = 0; + FacingType eface; + switch (source) { + + case SOURCE_SHIPPING: + cell = Map.Calculated_Cell(source, teamtype->House); + object->IsALoaner = true; + if (object->Unlimbo(Cell_Coord(cell), DIR_W)) { + object->Assign_Mission(MISSION_GUARD); + object->Assign_Destination(::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(object->Coord)) ))); + } else { + if (team) delete team; + delete object; + return(false); + } + break; + + case SOURCE_NORTH: + case SOURCE_SOUTH: + case SOURCE_EAST: + case SOURCE_WEST: { + eface = (FacingType)(source << 1); // Facing to enter map. + + if (airtransport) ScenarioInit++; + cell = Map.Calculated_Cell(source, teamtype->House); + if (airtransport) ScenarioInit--; + CELL newcell = cell; + + FootClass * o = (FootClass *)object->Next; + object->Next = 0; + bool ok = true; + while (newcell > 0 && object) { + DirType desiredfacing = Facing_Dir(eface); + if (object->What_Am_I() == RTTI_AIRCRAFT) { + desiredfacing = Random_Pick(DIR_N, DIR_MAX); + } + + if (ok && object->Unlimbo(Cell_Coord(newcell), desiredfacing)) { + + /* + ** If this object is part of a team, then the mission for this + ** object will be guard. The team handler will assign the proper + ** mission that it should follow. + */ + if (object->What_Am_I() == RTTI_AIRCRAFT) { + ok = false; + } else { + if (team) { + object->Assign_Mission(MISSION_GUARD); + } else { + object->Assign_Mission(MISSION_MOVE); + object->Assign_Destination(Adjacent_Cell(newcell, eface)); + } + object->Commence(); + } + + } else { + ok = true; + + /* + ** Could not unlimbo at location specified so find an adjacent location that it can + ** be unlimboed at. If this fails, then abort the whole placement process. + */ + FacingType adj; + for (adj = FACING_N; adj < FACING_COUNT; adj++) { + CELL trycell = Adjacent_Cell(newcell, adj); + if (!Map.In_Radar(trycell) && object->Can_Enter_Cell(trycell, adj) == MOVE_OK) { + newcell = trycell; + break; + } + } + if (adj < FACING_COUNT) continue; + newcell = -1; + } + + object = o; + if (object) { + o = (FootClass *)object->Next; + object->Next = 0; + } + } + + /* + ** If there are still objects that could not be placed, then delete them. + */ + if (o) { + while (o) { + FootClass * old = o; + o = (FootClass *)o->Next; + old->Next = 0; + + delete old; + } + + } + } + break; + + /* + ** Bring out the aircraft as separate "groups" of one. + */ + case SOURCE_AIR: { + AircraftClass * thisone = (AircraftClass *)object; + while (thisone) { + AircraftClass * next = (AircraftClass *)thisone->Next; + + /* + ** Find a suitable map entry location. Cargo planes will try to find a cell that + ** exactly lines up with the airfield they will unload at. + */ + CELL newcell; + ScenarioInit++; + newcell = Map.Calculated_Cell(HouseClass::As_Pointer(teamtype->House)->Edge, teamtype->House); + ScenarioInit--; + if (*thisone == AIRCRAFT_CARGO) { + BuildingClass const * building = thisone->Find_Docking_Bay(STRUCT_AIRSTRIP, false); + if (building) { + newcell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Coord_YCell(building->Docking_Coord()+2)); + } + } + thisone->Next = 0; + + ScenarioInit++; + placed = thisone->Unlimbo(Cell_Coord(newcell), DIR_W); + ScenarioInit--; + if (placed) { + if (!team) { + if (thisone->Class->IsFixedWing) { + thisone->Assign_Mission(MISSION_HUNT); + } else { + if (thisone->Class->IsTransporter && thisone->Is_Something_Attached()) { + thisone->Assign_Mission(MISSION_UNLOAD); + } else { + thisone->Assign_Mission(MISSION_MOVE); + } + thisone->Assign_Destination(::As_Target(Map.Calculated_Cell(source, teamtype->House))); + } + thisone->Commence(); + } + } else { + delete thisone; + } + + thisone = next; + } + if (!placed && team) delete team; + + + /* + ** Fixes bug that can happen if the reinforcement cannot be created. + ** This prevent "phantom" teams and team types from being left around. + */ + if (GameToPlay == GAME_NORMAL && !placed) return(false); + + } + break; + + case SOURCE_OCEAN: + case SOURCE_BEACH: + cell = Map.Calculated_Cell(SOURCE_BEACH, teamtype->House); + if (cell) { + CELL edge = XY_Cell(Cell_X(cell), Map.MapCellY+Map.MapCellHeight); + + placed = object->Unlimbo(Cell_Coord(edge), DIR_N); + if (placed) { + if (!team) { + object->IsLocked = false; + object->Assign_Mission(MISSION_UNLOAD); + object->Commence(); + object->Assign_Destination(::As_Target(cell)); + } + } else { + if (team) delete team; + delete object; + return(false); + } + } + break; + + default: + break; + } + + /* + ** Announce when the reinforcements have arrived. + */ + if (okvoice && teamtype->House == PlayerPtr->Class->House) { + Speak(VOX_REINFORCEMENTS, NULL, cell ? Cell_Coord(cell) : 0); + } + + return(true); +} + + +/*********************************************************************************************** + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * * + * Use this routine to bring on a reinforcement that hasn't been anticipated by the trigger * + * system. An example of this would be replacement harvesters or airfield ordered units. * + * The appropriate transport is created (if necessary) and a mission is assigned such that * + * the object will legally bring itself onto the playing field. * + * * + * INPUT: house -- The owner of this reinforcement. * + * * + * type -- The object to bring on. * + * * + * another -- This is reserved for the transport class in those cases where the * + * transport MUST be forced to a specific type. * + * * + * mission -- The mission to assign this reinforcement team. * + * * + * argument -- Optional team mission argument (usually a waypoint). * + * * + * OUTPUT: Was the special reinforcement created without error? * + * * + * WARNINGS: This routine will fail if a team type cannot be created. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission, int argument) +{ + if (house && type) { + TeamTypeClass * team = new TeamTypeClass(); + + if (team) { + + /* + ** If a ground based reinforcement is desired, but the edge of the map that the + ** reinforcement will arrive at is completely covered with water, then add + ** a hover lander for transport. + */ + if (!another && (type->What_Am_I() == RTTI_UNITTYPE || type->What_Am_I() == RTTI_INFANTRYTYPE)) { + + /* + ** Hover lander reinforcements can only arrive from the south. Yes, this is an + ** arbitrary limitation, but that's the way it is (for now). + */ + if (house->Edge == SOURCE_SOUTH) { + bool found = false; + for (int index = Map.MapCellX; index < Map.MapCellX+Map.MapCellWidth-1; index++) { + CELL cell = XY_Cell(index, Map.MapCellY+Map.MapCellHeight); + if (Map[cell].Is_Generally_Clear() && Map[cell-MAP_CELL_W].Is_Generally_Clear()) { + found = true; + break; + } + } + + /* + ** No land route was found for the reinforcement to drive itself onto the + ** map. Assign it a hover lander. This presumes that if the south edge is + ** non drivable, it will have water there instead. Risky, but is not a + ** problem with the current C&C maps. + */ + if (!found) { + mission = TMISSION_NONE; + another = &UnitTypeClass::As_Reference(UNIT_HOVER); + } + } + + if (!another) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(SOURCE_AIR, house->Class->House); + } + } + + /* + ** If there is no overridden mission assign to this special reinforcement, then + ** we must assign something. If not, the reinforcement will just sit at the edge + ** of the map. + */ + if (!another && mission == TMISSION_NONE) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(SOURCE_AIR, house->Class->House); + } + + /* + ** Fill in the team characteristics. + */ + strcpy((char *)&team->IniName[0], "TEMP"); + team->IsReinforcable = false; + team->IsTransient = true; + team->ClassCount = 1; + team->Class[0] = type; + team->DesiredNum[0] = 1; + team->MissionCount = 1; + if (mission == TMISSION_NONE) { + if (another && (another->What_Am_I() != RTTI_UNITTYPE || ((UnitTypeClass const *)another)->Type != UNIT_HOVER)) { + team->MissionList[0].Mission = TMISSION_UNLOAD; + team->MissionList[0].Argument = WAYPT_REINF; + } + } else { + team->MissionList[0].Mission = mission; + team->MissionList[0].Argument = argument; + } + team->House = house->Class->House; + if (another) { + team->ClassCount++; + team->Class[1] = another; + team->DesiredNum[1] = 1; + } + + bool ok = Do_Reinforcements(team); + if (!ok && GameToPlay == GAME_NORMAL) { + delete team; + } + return(ok); + } + } + return(false); +} + + +/*********************************************************************************************** + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * * + * This routine is used to launch an airstrike. It will create the necessary aircraft and * + * assign them to attack the target specified. This routine bypasses the normal * + * reinforcement logic since it doesn't need the sophistication of unloading and following * + * team mission lists. * + * * + * INPUT: house -- The purpetrator of this air strike. * + * * + * air -- The type of aircraft to make up this airstrike. * + * * + * number -- The number of aircraft in this airstrike. * + * * + * mission -- The mission to assign the aircraft. * + * * + * tarcom -- The target to assign these aircraft. * + * * + * navcom -- The navigation target to assign (if necessary). * + * * + * OUTPUT: Returns the number of aircraft created for this airstrike. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom) +{ + /* + ** Get a pointer to the class of the object that we are going to create. + */ + TechnoTypeClass const * type = (TechnoTypeClass *)&AircraftTypeClass::As_Reference(air); + + /* + ** Loop through the number of objects we are supposed to create and + ** create and place them on the map. + */ + int sub; + for (sub = 0; sub < number; sub++) { + + /* + ** Create one of the required objects. If this fails we could have + ** a real problem. + */ + ScenarioInit++; + TechnoClass * obj = (TechnoClass *)type->Create_One_Of(house); + ScenarioInit--; + if (!obj) return(sub); + + /* + ** Flying objects always have the IsALoaner bit set. + */ + obj->IsALoaner = true; + + /* + ** Find a cell for the object to come in on. This is stolen from the + ** the code that handles a SOURCE_AIR in the normal logic. + */ + SourceType source = house->Edge; + switch (source) { + case SOURCE_NORTH: + case SOURCE_EAST: + case SOURCE_SOUTH: + case SOURCE_WEST: + break; + + default: + source = SOURCE_NORTH; + break; + } + CELL newcell = Map.Calculated_Cell(source, house->Class->House); + + /* + ** Try and place the object onto the map. + */ + ScenarioInit++; + int placed = obj->Unlimbo(Cell_Coord(newcell), DIR_N); + ScenarioInit--; + if (placed) { + + /* + ** If we suceeded in placing the obj onto the map then + ** now we need to give it a mission and destination. + */ + obj->Assign_Mission(mission); + + /* + ** If a navcom was specified then set it. + */ + if (navcom != TARGET_NONE) { + obj->Assign_Destination(navcom); + } + + /* + ** If a tarcom was specified then set it. + */ + if (tarcom != TARGET_NONE) { + obj->Assign_Target(tarcom); + } + + /* + ** Start the object into action. + */ + obj->Commence(); + } else { + delete obj; + sub--; + return(sub); + } + } + return(sub); +} diff --git a/TIBERIANDAWN/RESOURCE/TiberianDawn.rc b/TIBERIANDAWN/RESOURCE/TiberianDawn.rc new file mode 100644 index 000000000..0082a2f09 --- /dev/null +++ b/TIBERIANDAWN/RESOURCE/TiberianDawn.rc @@ -0,0 +1,102 @@ +// 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 + +///////////////////////////////////////////////////////////////////////////// +// 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 +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Petroglyph Games Inc." + VALUE "FileDescription", "Command & Conquer(TM) Remastered Collection TiberianDawn DLL" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Command & Conquer(TM) Remastered Collection" + VALUE "LegalCopyright", "(C) 2020 Electronic Arts Inc. All rights reserved." + VALUE "OriginalFilename", "TiberianDawn.dll" + VALUE "ProductName", "Command & Conquer(TM) Remastered Collection" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/TIBERIANDAWN/RESOURCE/resource.h b/TIBERIANDAWN/RESOURCE/resource.h new file mode 100644 index 000000000..5aa3842f2 --- /dev/null +++ b/TIBERIANDAWN/RESOURCE/resource.h @@ -0,0 +1,29 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TiberianDawn.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/TIBERIANDAWN/RULES.CPP b/TIBERIANDAWN/RULES.CPP new file mode 100644 index 000000000..5da69efc0 --- /dev/null +++ b/TIBERIANDAWN/RULES.CPP @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DifficultyClass::DifficultyClass -- Default constructor for difficulty class object. * + * RulesClass::RulesClass -- Default constructor for rules class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + + /*********************************************************************************************** + * DifficultyClass::DifficultyClass -- Default constructor for difficulty class object. * + * * + * This is the default constructor for the difficulty class object. Although it initializes * + * the rule data with default values, it is expected that they will all be overridden by * + * the rules control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/18/2019 SKY : Created. * + *=============================================================================================*/ +DifficultyClass::DifficultyClass(void) : + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1), + RepairDelay(0.02f), + BuildDelay(0.03f), + IsBuildSlowdown(false), + IsWallDestroyer(true), + IsContentScan(false) +{ +} + + +/*********************************************************************************************** + * RulesClass::RulesClass -- Default constructor for rules class object. * + * * + * This is the default constructor for the rules class object. Although it initializes the * + * rule data with default values, it is expected that they will all be overridden by the * + * rules control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +RulesClass::RulesClass(void) : + AttackInterval(3), + BaseSizeAdd(3), + PowerSurplus(50), + AttackDelay(5), + PowerEmergencyFraction(0x00C0), + HelipadRatio(0x1E), + HelipadLimit(5), + AARatio(0x0024), + AALimit(10), + DefenseRatio(0x0066), + DefenseLimit(40), + WarRatio(0x0019), + WarLimit(2), + BarracksRatio(0x0028), + BarracksLimit(2), + RefineryLimit(4), + RefineryRatio(0x0028), + AirstripRatio(0x001E), + AirstripLimit(5), + MaxIQ(5), + IQSuperWeapons(4), + IQProduction(5), + IQGuardArea(4), + IQRepairSell(1), + IQCrush(2), + IQScatter(3), + IQContentScan(4), + IQAircraft(4), + IQHarvester(2), + IQSellBack(2), + InfantryReserve(3000), + InfantryBaseMult(1), + IsComputerParanoid(true), + AllowSuperWeapons(true) +{ +} diff --git a/TIBERIANDAWN/RULES.H b/TIBERIANDAWN/RULES.H new file mode 100644 index 000000000..3bc424c3a --- /dev/null +++ b/TIBERIANDAWN/RULES.H @@ -0,0 +1,275 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RULES_H +#define RULES_H + +class DifficultyClass +{ + public: + DifficultyClass(void); + + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + + float RepairDelay; + float BuildDelay; + + unsigned IsBuildSlowdown:1; + unsigned IsWallDestroyer:1; + unsigned IsContentScan:1; +}; + +class RulesClass { + public: + + RulesClass(void); + + /* + ** The computer is limited in the size of the base it can build. It is limited to the + ** size of the largest human opponent base plus this surplus count. + */ + int BaseSizeAdd; + + /* + ** If the power surplus is less than this amount, then the computer will + ** build power plants. + */ + int PowerSurplus; + + /* + ** This specifies the average number of minutes between each computer attack. + */ + int AttackInterval; + + /* + ** This specifies the average minutes delay before the computer will begin + ** its first attack upon the player. The duration is also modified by the + ** difficulty level. + */ + int AttackDelay; + + /* + ** If the power ratio falls below this percentage, then a power emergency is + ** in effect. At such times, the computer might decide to sell off some + ** power hungry buildings in order to alleviate the situation. + */ + unsigned short PowerEmergencyFraction; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of helipads. + */ + unsigned short HelipadRatio; + + /* + ** Limit the number of helipads to this amount. + */ + int HelipadLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of Tesla Coils. + */ + unsigned short TeslaRatio; + + /* + ** Limit tesla coil production to this maximum. + */ + int TeslaLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of anti-aircraft defense. + */ + unsigned short AARatio; + + /* + ** Limit anti-aircraft building quantity to this amount. + */ + int AALimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of defensive structures. + */ + unsigned short DefenseRatio; + + /* + ** This is the limit to the number of defensive building that can be built. + */ + int DefenseLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of war factories. + */ + unsigned short WarRatio; + + /* + ** War factories are limited to this quantity for the computer controlled player. + */ + int WarLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of infantry producing structures. + */ + unsigned short BarracksRatio; + + /* + ** No more than this many barracks can be built. + */ + int BarracksLimit; + + /* + ** Refinery building is limited to this many refineries. + */ + int RefineryLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of refineries. + */ + unsigned short RefineryRatio; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of airstrips. + */ + unsigned short AirstripRatio; + + /* + ** Limit the number of airstrips to this amount. + */ + int AirstripLimit; + + /* + ** This is the maximum number of IQ settings available. The human player is + ** presumed to be at IQ level zero. + */ + int MaxIQ; + + /* + ** The IQ level at which super weapons will be automatically fired by the computer. + */ + int IQSuperWeapons; + + /* + ** The IQ level at which production is automatically controlled by the computer. + */ + int IQProduction; + + /* + ** The IQ level at which newly produced units start out in guard area mode instead + ** of normal guard mode. + */ + int IQGuardArea; + + /* + ** The IQ level at which the computer will be able to decide what gets repaired + ** or sold. + */ + int IQRepairSell; + + /* + ** At this IQ level or higher, a unit is allowed to automatically try to crush + ** an atagonist if possible. + */ + int IQCrush; + + /* + ** The unit/infantry will try to scatter if an incoming threat + ** is detected. + */ + int IQScatter; + + /* + ** Tech level at which the computer will scan the contents of a transport + ** in order to pick the best target to fire upon. + */ + int IQContentScan; + + /* + ** Aircraft replacement production occurs at this IQ level or higher. + */ + int IQAircraft; + + /* + ** Checks for and replaces lost harvesters. + */ + int IQHarvester; + + /* + ** Is allowed to sell a structure being damaged. + */ + int IQSellBack; + + /* + ** The computer will build infantry if their cash reserve is greater than this amount. + */ + int InfantryReserve; + + /* + ** This factor is multiplied by the number of buildings in the computer's base and infantry + ** are always built until it matches that number. + */ + int InfantryBaseMult; + + /* + ** Is the computer paranoid? If so, then it will band together with other computer + ** paranoid players when the situation looks rough. + */ + bool IsComputerParanoid; + + /* + ** Are superweapons allowed? + */ + bool AllowSuperWeapons; + + /* + ** This array controls the difficulty affects on the game. There is one + ** difficulty class object for each difficulty level. + */ + DifficultyClass Diff[DIFF_COUNT]; +}; + + +#endif diff --git a/TIBERIANDAWN/SAVEDLG.H b/TIBERIANDAWN/SAVEDLG.H new file mode 100644 index 000000000..b02505816 --- /dev/null +++ b/TIBERIANDAWN/SAVEDLG.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\savedlg.h_v 2.14 16 Oct 1995 16:45:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SAVEDLG_H +#define SAVEDLG_H + +#include "gadget.h" + +class SaveOptionsClass +{ + private: + + enum SaveOptionsClassEnums { + BUTTON_CANCEL=200, + BUTTON_SAVE, + OPTION_WIDTH=216, + OPTION_HEIGHT=122, + OPTION_X=((320 - OPTION_WIDTH) / 2) & ~7, + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + NUMBER_OF_BUTTONS=2, + CAPTION_Y_POS=5, + BORDER1_LEN=49, + BUTTON_CANCEL_X=90, + BUTTON_CANCEL_Y=103, + LISTBOX_X=40, + LISTBOX_Y=24, + LISTBOX_W=136, + LISTBOX_H=72 + }; + + public: + SaveOptionsClass (void) { }; + void Process (void); +}; + + +#endif diff --git a/TIBERIANDAWN/SAVELOAD.CPP b/TIBERIANDAWN/SAVELOAD.CPP new file mode 100644 index 000000000..5b5c7900a --- /dev/null +++ b/TIBERIANDAWN/SAVELOAD.CPP @@ -0,0 +1,1614 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\saveload.cpv 2.18 16 Oct 1995 16:48:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVELOAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 23, 1994 * + * * + * Last Update : June 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Code_All_Pointers -- Code all pointers. * + * Decode_All_Pointers -- Decodes all pointers. * + * Get_Savefile_Info -- gets description, scenario #, house * + * Load_Game -- loads a saved game * + * Load_Misc_Values -- Loads miscellaneous variables. * + * Load_Misc_Values -- loads miscellaneous variables * + * Read_Object -- reads an object from disk, in a safe way * + * Save_Game -- saves a game to disk * + * Save_Misc_Values -- saves miscellaneous variables * + * Target_To_TechnoType -- converts TARGET to TechnoTypeClass * + * TechnoType_To_Target -- converts TechnoTypeClass to TARGET * + * Write_Object -- reads an object from disk, in a safe way * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +extern bool DLLSave(FileClass &file); +extern bool DLLLoad(FileClass &file); + + +/* +********************************** Defines ********************************** +*/ +#define SAVEGAME_VERSION (DESCRIP_MAX + \ + 0x01000003 + ( \ + sizeof(AircraftClass) + \ + sizeof(AircraftTypeClass) + \ + sizeof(AnimClass) + \ + sizeof(AnimTypeClass) + \ + sizeof(BuildingClass) + \ + sizeof(BuildingTypeClass) + \ + sizeof(BulletClass) + \ + sizeof(BulletTypeClass) + \ + sizeof(HouseClass) + \ + sizeof(HouseTypeClass) + \ + sizeof(InfantryClass) + \ + sizeof(InfantryTypeClass) + \ + sizeof(OverlayClass) + \ + sizeof(OverlayTypeClass) + \ + sizeof(SmudgeClass) + \ + sizeof(SmudgeTypeClass) + \ + sizeof(TeamClass) + \ + sizeof(TeamTypeClass) + \ + sizeof(TemplateClass) + \ + sizeof(TemplateTypeClass) + \ + sizeof(TerrainClass) + \ + sizeof(TerrainTypeClass) + \ + sizeof(UnitClass) + \ + sizeof(UnitTypeClass) + \ + sizeof(MouseClass) + \ + sizeof(CellClass) + \ + sizeof(FactoryClass) + \ + sizeof(BaseClass) + \ + sizeof(LayerClass) + \ + sizeof(BriefingText) + \ + sizeof(Waypoint))) + + +/*************************************************************************** + * Save_Game -- saves a game to disk * + * * + * Saving the Map: * + * DisplayClass::Save() invokes CellClass's Write() for every cell * + * that needs to be saved. A cell needs to be saved if it contains * + * any special data at all, such as a TIcon, or an Occupier. * + * The cell saves its own CellTrigger pointer, converted to a TARGET. * + * * + * Saving game objects: * + * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* + * Save() routine invokes each object's Write() routine, if that * + * object's IsActive is set. * + * * + * Saving the layers: * + * The Map's Layers (Ground, Air, etc) of things that are on the map, * + * and the Logic's Layer of things to process both need to be saved. * + * LayerClass::Save() writes the entire layer array to disk * + * * + * Saving the houses: * + * Each house needs to be saved, to record its Credits, Power, etc. * + * * + * Saving miscellaneous data: * + * There are a lot of miscellaneous variables to save, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=========================================================================*/ +bool Save_Game(int id,char *descr) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to save + */ + sprintf(name, "SAVEGAME.%03d", id); + + return Save_Game(name, descr); +} + + +/* +** Version that takes file name. ST - 9/9/2019 11:10AM +*/ +bool Save_Game(const char *file_name, const char *descr) +{ + RawFileClass file; + int i; + unsigned long version; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + + scenario = Scenario; // get current scenario # + house = PlayerPtr->Class->House; // get current house + + /* + ** Code everybody's pointers + */ + Code_All_Pointers(); + + /* + ** Open the file + */ + if (!file.Open(file_name, WRITE)) { + Decode_All_Pointers(); + return(false); + } + + /* + ** Save the DLLs variables first, so we can do a version check in the DLL when we begin the load + */ + if (RunningAsDLL) { + if (!DLLSave(file)) { + file.Close(); + Decode_All_Pointers(); + return false; + } + } + + /* + ** Save the description, scenario #, and house + ** (scenario # & house are saved separately from the actual Scenario & + ** PlayerPtr globals for convenience; we can quickly find out which + ** house & scenario this save-game file is for by reading these values. + ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), + ** which may or may not be a HousesType number; so, saving 'house' + ** here ensures we can always pull out the house for this file.) + */ + sprintf(descr_buf, "%s\r\n",descr); // put CR-LF after text + descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL + + if (file.Write(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + if (file.Write(&scenario, sizeof(scenario)) != sizeof(scenario)) { + file.Close(); + return(false); + } + + if (file.Write(&house, sizeof(house)) != sizeof(house)) { + file.Close(); + return(false); + } + + /* + ** Save the save-game version, for loading verification + */ + version = SAVEGAME_VERSION; + + if (file.Write(&version, sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Save the map. The map must be saved first, since it saves the Theater. + */ + Map.Save(file); + + Call_Back(); + /* + ** Save all game objects. This code saves every object that's stored in a + ** TFixedIHeap class. + */ + if (!Houses.Save(file) || + !TeamTypes.Save(file) || + !Teams.Save(file) || + !Triggers.Save(file) || + !Aircraft.Save(file) || + !Anims.Save(file) || + !Buildings.Save(file) || + !Bullets.Save(file) || + !Infantry.Save(file) || + !Overlays.Save(file) || + !Smudges.Save(file) || + !Templates.Save(file) || + !Terrains.Save(file) || + !Units.Save(file) || + !Factories.Save(file)) { + file.Close(); + + Decode_All_Pointers(); + + return(false); + } + + Call_Back(); + /* + ** Save the Logic & Map layers + */ + if (!Logic.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + for (i = 0; i < LAYER_COUNT; i++) { + if (!Map.Layer[i].Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + } + + /* + ** Save the Score + */ + if (!Score.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + /* + ** Save the AI Base + */ + if (!Base.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + /* + ** Save miscellaneous variables. + */ + if (!Save_Misc_Values(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + Call_Back(); + + /* + ** Close the file; we're done + */ + file.Close(); + Decode_All_Pointers(); + + return(true); +} + + +/*************************************************************************** + * Load_Game -- loads a saved game * + * * + * This routine loads the data in the same way it was saved out. * + * * + * Loading the Map: * + * - DisplayClass::Load() invokes CellClass's Load() for every cell * + * that was saved. * + * - The cell loads its own CellTrigger pointer. * + * * + * Loading game objects: * + * - IHeap's Load() routine loads the # of objects stored, and loads * + * each object. * + * - Triggers: Add themselves to the HouseTriggers if they're associated * + * with a house * + * * + * Loading the layers: * + * LayerClass::Load() reads the entire layer array to disk * + * * + * Loading the houses: * + * Each house is loaded in its entirety. * + * * + * Loading miscellaneous data: * + * There are a lot of miscellaneous variables to load, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * If this routine returns false, the entire game will be in an * + * unknown state, so the scenario will have to be re-initialized. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=========================================================================*/ +bool Load_Game(int id) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + + return Load_Game(name); +} + +/* +** Version that takes a file name instead. ST - 9/9/2019 11:13AM +*/ +bool Load_Game(const char *file_name) +{ + RawFileClass file; + int i; + unsigned long version; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + + /* + ** Open the file + */ + if (!file.Open(file_name, READ)) { + return(false); + } + + /* + ** Load the DLLs variables first, in case we need to do something different based on version + */ + if (RunningAsDLL) { + if (!DLLLoad(file)) { + file.Close(); + return false; + } + } + + /* + ** Read & discard the save-game's header info + */ + if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + if (file.Read(&scenario, sizeof(scenario)) != sizeof(scenario)) { + file.Close(); + return(false); + } + + if (file.Read(&house, sizeof(house)) != sizeof(house)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Clear the scenario so we start fresh; this calls the Init_Clear() routine + ** for the Map, and all object arrays. It has the following important + ** effects: + ** - Every cell is cleared to 0's, via MapClass::Init_Clear() + ** - All heap elements' are cleared + ** - The Houses are Initialized, which also clears their HouseTriggers + ** array + ** - The map's Layers & Logic Layer are cleared to empty + ** - The list of currently-selected objects is cleared + */ + Clear_Scenario(); + + /* + ** Read in & verify the save-game ID code + */ + if (file.Read(&version,sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + if (version != SAVEGAME_VERSION) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Set the required CD to be in the drive according to the scenario + ** loaded. + */ + if (RequiredCD != -2) { + if (scenario >= 20 && scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (scenario >= 60){ + /* + ** This is a gateway bonus scenario + */ + RequiredCD = -1; + }else{ + if (house == HOUSE_GOOD) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } + } + } + } + if(!Force_CD_Available(RequiredCD)) { + Prog_End("Load_Game - CD not found", true); + if (!RunningAsDLL) { + exit(EXIT_FAILURE); + } + return false; + } + + Call_Back(); + + /* + ** Load the map. The map comes first, since it loads the Theater & init's + ** mixfiles. The map calls all the type-class's Init routines, telling them + ** what the Theater is; this must be done before any objects are created, so + ** they'll be properly created. + */ + Map.Load(file); + + Call_Back(); + /* + ** Load the object data. + */ + if (!Houses.Load(file) || + !TeamTypes.Load(file) || + !Teams.Load(file) || + !Triggers.Load(file) || + !Aircraft.Load(file) || + !Anims.Load(file) || + !Buildings.Load(file) || + !Bullets.Load(file) || + !Infantry.Load(file) || + !Overlays.Load(file) || + !Smudges.Load(file) || + !Templates.Load(file) || + !Terrains.Load(file) || + !Units.Load(file) || + !Factories.Load(file)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Load the Logic & Map Layers + */ + if (!Logic.Load(file)) { + file.Close(); + return(false); + } + for (i = 0; i < LAYER_COUNT; i++) { + if (!Map.Layer[i].Load(file)) { + file.Close(); + return(false); + } + } + + Call_Back(); + /* + ** Load the Score + */ + if (!Score.Load(file)) { + file.Close(); + return(false); + } + + /* + ** Load the AI Base + */ + if (!Base.Load(file)) { + file.Close(); + return(false); + } + + /* + ** Load miscellaneous variables, including the map size & the Theater + */ + if (!Load_Misc_Values(file)) { + file.Close(); + return(false); + } + + file.Close(); + Decode_All_Pointers(); + Map.Init_IO(); + Map.Flag_To_Redraw(true); + + ScenarioInit = 0; + + /* + ** Fixup remap tables. ST - 2/28/2020 1:50PM + */ + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->Init_Data(hptr->RemapColor, hptr->ActLike, hptr->Credits); + } + } + + /* + ** Re-init unit trackers. They will be garbage pointers after the load + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->Init_Unit_Trackers(); + } + } + +#ifdef DEMO + if (Scenario != 10 && Scenario != 1 && Scenario != 6) { + Clear_Scenario(); + return(false); + } +#endif + + Call_Back(); + return(true); +} + + +/*************************************************************************** + * Save_Misc_Values -- saves miscellaneous variables * + * * + * INPUT: * + * file file to use for writing * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/29/1994 BR : Created. * + *=========================================================================*/ +bool Save_Misc_Values(FileClass &file) +{ + int i, j; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for saving 'CurrentObject' ptrs + + /* + ** Player's House. + */ + if (file.Write(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) { + return(false); + } + + /* + ** Save this scenario number. + */ + if (file.Write(&Scenario, sizeof(Scenario)) != sizeof(Scenario)) { + return(false); + } + + /* + ** Save frame #. + */ + if (file.Write(&Frame, sizeof(Frame)) != sizeof(Frame)) { + return(false); + } + + /* + ** Save VQ Movie names. + */ + if (file.Write(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) { + return(false); + } + + if (file.Write(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) { + return(false); + } + + /* + ** Save currently-selected objects list. + ** Save the # of ptrs in the list. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + count = selection.Count(); + if (file.Write(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Save the pointers. + */ + for (j = 0; j < count; j++) { + ptr = selection[j]; + if (file.Write(&ptr, sizeof(ptr)) != sizeof(ptr)) { + return(false); + } + } + } + + /* + ** Save the list of waypoints. + */ + if (file.Write(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) { + return(false); + } + + file.Write(&ScenDir, sizeof(ScenDir)); + file.Write(&ScenVar, sizeof(ScenVar)); + file.Write(&CarryOverMoney, sizeof(CarryOverMoney)); + file.Write(&CarryOverPercent, sizeof(CarryOverPercent)); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(BriefMovie, sizeof(BriefMovie)); + file.Write(Views, sizeof(Views)); + file.Write(&EndCountDown, sizeof(EndCountDown)); + file.Write(BriefingText, sizeof(BriefingText)); + + // This is new... + file.Write(ActionMovie, sizeof(ActionMovie)); + + return(true); +} + + +/*********************************************************************************************** + * Load_Misc_Values -- Loads miscellaneous variables. * + * * + * INPUT: file -- The file to load the misc values from. * + * * + * OUTPUT: Was the misc load process successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +bool Load_Misc_Values(FileClass &file) +{ + int i, j; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for loading 'CurrentObject' ptrs + + /* + ** Player's House. + */ + if (file.Read(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) { + return(false); + } + + /* + ** Read this scenario number. + */ + if (file.Read(&Scenario,sizeof(Scenario)) != sizeof(Scenario)) { + return(false); + } + + /* + ** Load frame #. + */ + if (file.Read(&Frame, sizeof(Frame)) != sizeof(Frame)) { + return(false); + } + + /* + ** Load VQ Movie names. + */ + if (file.Read(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) { + return(false); + } + + if (file.Read(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) { + return(false); + } + + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + /* + ** Load currently-selected objects list. + ** Load the # of ptrs in the list. + */ + DynamicVectorClass& selection = CurrentObject.Raw(i); + if (file.Read(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Load the pointers. + */ + for (j = 0; j < count; j++) { + if (file.Read(&ptr, sizeof(ptr)) != sizeof(ptr)) { + return(false); + } + selection.Add(ptr); // add to the list + } + } + + /* + ** Save the list of waypoints. + */ + if (file.Read(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) { + return(false); + } + + file.Read(&ScenDir, sizeof(ScenDir)); + file.Read(&ScenVar, sizeof(ScenVar)); + file.Read(&CarryOverMoney, sizeof(CarryOverMoney)); + file.Read(&CarryOverPercent, sizeof(CarryOverPercent)); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(BriefMovie, sizeof(BriefMovie)); + file.Read(Views, sizeof(Views)); + file.Read(&EndCountDown, sizeof(EndCountDown)); + file.Read(BriefingText, sizeof(BriefingText)); + + if (file.Seek(0, SEEK_CUR) < file.Size()) { + file.Read(ActionMovie, sizeof(ActionMovie)); + } + + return(true); +} + + +/* +** ST - 9/26/2019 11:43AM +*/ +extern void DLL_Code_Pointers(void); +extern void DLL_Decode_Pointers(void); + + +/*********************************************************************************************** + * Code_All_Pointers -- Code all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Code_All_Pointers(void) +{ + int i, j; + + /* + ** The Map. + */ + Map.Code_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Code_Pointers(); + Teams.Code_Pointers(); + Triggers.Code_Pointers(); + Aircraft.Code_Pointers(); + Anims.Code_Pointers(); + Buildings.Code_Pointers(); + Bullets.Code_Pointers(); + Infantry.Code_Pointers(); + Overlays.Code_Pointers(); + Smudges.Code_Pointers(); + Templates.Code_Pointers(); + Terrains.Code_Pointers(); + Units.Code_Pointers(); + Factories.Code_Pointers(); + + /* + ** The Layers. + */ + Logic.Code_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Code_Pointers(); + } + + /* + ** The Score. + */ + Score.Code_Pointers(); + + /* + ** The Base. + */ + Base.Code_Pointers(); + + /* + ** PlayerPtr. + */ + PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + for (j = 0; j < selection.Count(); j++) { + selection[j] = (ObjectClass *)selection[j]->As_Target(); + } + } + + /* + ** DLL data + */ + DLL_Code_Pointers(); + + /* + ** Houses must be coded last, because the Class->House member of the HouseClass + ** is used to code HouseClass pointers for all other objects, and if Class is + ** coded, it will point to a meaningless value. + */ + Houses.Code_Pointers(); +} + + +/*********************************************************************************************** + * Decode_All_Pointers -- Decodes all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Decode_All_Pointers(void) +{ + int i, j; + + /* + ** The Map. + */ + Map.Decode_Pointers(); + + /* + ** Decode houses first, so we can properly decode all other objects' + ** House pointers + */ + Houses.Decode_Pointers(); + + /* + ** DLL data + */ + DLL_Decode_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Decode_Pointers(); + Teams.Decode_Pointers(); + Triggers.Decode_Pointers(); + Aircraft.Decode_Pointers(); + Anims.Decode_Pointers(); + Buildings.Decode_Pointers(); + Bullets.Decode_Pointers(); + Infantry.Decode_Pointers(); + Overlays.Decode_Pointers(); + Smudges.Decode_Pointers(); + Templates.Decode_Pointers(); + Terrains.Decode_Pointers(); + Units.Decode_Pointers(); + Factories.Decode_Pointers(); + + /* + ** The Layers. + */ + Logic.Decode_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Decode_Pointers(); + } + + /* + ** The Score. + */ + Score.Decode_Pointers(); + + /* + ** The Base. + */ + Base.Decode_Pointers(); + + /* + ** PlayerPtr. + */ + PlayerPtr = HouseClass::As_Pointer(*((HousesType*)&PlayerPtr)); + Whom = PlayerPtr->Class->House; + switch (PlayerPtr->Class->House) { + case HOUSE_GOOD: + ScenPlayer = SCEN_PLAYER_GDI; + break; + + case HOUSE_BAD: + ScenPlayer = SCEN_PLAYER_NOD; + break; + + case HOUSE_JP: + ScenPlayer = SCEN_PLAYER_JP; + break; + } + Check_Ptr(PlayerPtr,__FILE__,__LINE__); + + if (PlayerPtr->ActLike == HOUSE_JP) { + ScenPlayer = SCEN_PLAYER_JP; + } + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + for (j = 0; j < selection.Count(); j++) { + unsigned long target_as_object_ptr = reinterpret_cast(selection[j]); + TARGET target = (TARGET)target_as_object_ptr; + selection[j] = As_Object(target); + Check_Ptr(selection[j],__FILE__,__LINE__); + } + } + + /* + ** Last-Minute Fixups; to resolve these pointers properly requires all other + ** pointers to be loaded & decoded. + */ + if (Map.PendingObjectPtr) { + Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); + Check_Ptr((void *)Map.PendingObject, __FILE__, __LINE__); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); + } else { + Map.PendingObject = 0; + Map.Set_Cursor_Shape(0); + } +} + + +/*********************************************************************************************** + * Read_Object -- reads an object from a file * + * * + * Replacement for the original code below, which doesn't work with MSVC * + * We now assume that the vtable is 4 bytes, and is at the beginning of the class * + * It's the caller's responsibility to make sure the VTable is correct * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + * 9/10/2019 12:34PM ST : Updated for MS compiler * + *=============================================================================================*/ +bool Read_Object(void *ptr, int class_size, FileClass & file, bool has_vtable) +{ + int size; + + /* + ** Read size of this chunk. + */ + if (file.Read(&size,sizeof(size)) != sizeof(size)) { + return(false); + } + + /* + ** Error if incorrect size. + */ + if (size != class_size) { + return false; + } + + int vtable_adjust = has_vtable ? 4 : 0; + unsigned char *object_ptr = static_cast(ptr); + + if (has_vtable) { + /* + ** Need to skip the vtable read. + */ + int dummy; + file.Read(&dummy, vtable_adjust); + } + + /* + ** Read object data. + */ + if (file.Read(object_ptr + vtable_adjust, class_size - vtable_adjust) != (class_size - vtable_adjust)) { + return(false); + } + + return true; +} + + +#if (0) +/*********************************************************************************************** + * Write_Object -- writes an object to a file * + * * + * This routine writes an object, skipping the embedded virtual function table pointer. * + * * + * INPUT: * + * ptr pointer to object to write * + * class_size size of the class itself * + * file file to use for I/O * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * This routine ASSUMES the program modules are compiled with: * + * -Vb- Always make the virtual function table ptr 2 bytes long * + * -Vt Put the virtual function table after the 1st class's data * + * * + * Also see warnings for Read_Object(). * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + * 9/10/2019 12:34PM ST : Updated for MS compiler * + *=============================================================================================*/ +bool Write_Object(void *ptr, int class_size, FileClass & file) +{ + /* + ** Test assumptions about class size. + */ + class TestClass { + virtual void Test(void) = 0; + }; + + if (sizeof(TestClass) != 4) { + /* + ** Crash. + */ + *((int*)0) = 0; + } + + /* + ** Save size of this chunk. + */ + if (file.Write(&class_size,sizeof(class_size)) != sizeof(class_size)) { + return(false); + } + + /* + ** Save object data. + */ + if (file.Write(ptr, class_size) != (class_size)) { + return(false); + } + + return(true); +} + + +#endif + + +#if (0) //ST - 9/10/2019 12:43PM + +/*********************************************************************************************** + * Read_Object -- reads an object from disk * + * * + * This routine reads in an object and fills in the virtual function table pointer. * + * * + * INPUT: * + * ptr pointer to object to read * + * base_size size of object's absolute base class * + * class_size size of the class itself * + * file file to use for I/O * + * vtable virtual function table pointer value, NULL if none * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * This routine ASSUMES the program modules are compiled with: * + * -Vb- Always make the virtual function table ptr 2 bytes long * + * -Vt Put the virtual function table after the 1st class's data * + * * + * ALSO, the class used to compute 'base_size' must come first in a multiple-inheritence * + * hierarchy. AND, if your class multiply-inherits from other classes, only ONE of those * + * classes can contain virtual functions! If you include virtual functions in the other * + * classes, the compiler will generate multiple virtual function tables, and this load/save * + * technique will fail. * + * * + * Each class hierarchy is stored in memory as a chain: first the data for the base-est * + * class, then the virtual function table pointer for this hierarchy, then the data for * + * all derived classes. If any of these derived classes multiply-inherit, the base class * + * for the multiple inheritance is stored as a separate chain following this chain. The * + * new chain will contain its own virtual function table pointer, if the multiply- * + * inherited hierarchy contains any virtual functions. Thus, the declaration * + * class A * + * class B: public A * + * class C: public B, X * + * is stored as: * + * A data * + * A's Virtual Table Pointer * + * B data * + * X data * + * [X's Virtual Table Pointer] * + * C data * + * * + * and * + * class A * + * class B: public A * + * class C: public X, B * + * is stored in memory as: * + * X data * + * [X's Virtual Table Pointer] * + * A data * + * A's Virtual Table Pointer * + * B data * + * C data * + * * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + *=============================================================================================*/ +bool Read_Object(void *ptr, int base_size, int class_size, FileClass & file, void * vtable) +{ + int size; // object size in bytes + + /* + ** Read size of this chunk. + */ + if (file.Read(&size,sizeof(size)) != sizeof(size)) { + return(false); + } + + /* + ** Error if incorrect size. + */ + if (size != class_size) { + return(false); + } + + /* + ** Read object data. + */ + if (file.Read(ptr, class_size) != (class_size)) { + return(false); + } + + /* + ** Fill in VTable. + */ + if (vtable) { + ((void **)(((char *)ptr) + base_size - 4))[0] = vtable; + } + + return(true); +} + +#endif + + + +/*********************************************************************************************** + * Write_Object -- reads an object from disk, in a safe way * + * * + * This routine writes an object in 2 pieces, skipping the embedded * + * virtual function table pointer. * + * * + * INPUT: * + * ptr pointer to object to write * + * class_size size of the class itself * + * file file to use for I/O * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * This routine ASSUMES the program modules are compiled with: * + * -Vb- Always make the virtual function table ptr 2 bytes long * + * -Vt Put the virtual function table after the 1st class's data * + * * + * Also see warnings for Read_Object(). * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + *=============================================================================================*/ +bool Write_Object(void *ptr, int class_size, FileClass & file) +{ + /* + ** Save size of this chunk. + */ + if (file.Write(&class_size,sizeof(class_size)) != sizeof(class_size)) { + return(false); + } + + /* + ** Save object data. + */ + if (file.Write(ptr, class_size) != (class_size)) { + return(false); + } + + return(true); +} + + +/*************************************************************************** + * Get_Savefile_Info -- gets description, scenario #, house * + * * + * INPUT: * + * id numerical ID, for the file extension * + * buf buffer to store description in * + * scenp ptr to variable to hold scenario * + * housep ptr to variable to hold house * + * * + * OUTPUT: * + * true = OK, false = error (save-game file invalid) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep) +{ + RawFileClass file; + char name[_MAX_FNAME+_MAX_EXT]; + unsigned long version; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + + /* + ** If the file opens OK, read the file + */ + if (file.Open(name, READ)) { + + /* + ** Read in the description, scenario #, and the house + */ + if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF + strcpy(buf, descr_buf); + + if (file.Read(scenp, sizeof(unsigned)) != sizeof(unsigned)) { + file.Close(); + return(false); + } + + if (file.Read(housep, sizeof(HousesType)) != sizeof(HousesType)) { + file.Close(); + return(false); + } + + /* + ** Read & verify the save-game version # + */ + if (file.Read(&version,sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + if (version!=SAVEGAME_VERSION) { + file.Close(); + return(false); + } + + file.Close(); + + return(true); + } + return(false); +} + + +/*************************************************************************** + * TechnoType_To_Target -- converts TechnoTypeClass to TARGET * + * * + * INPUT: * + * ptr pointer to convert * + * * + * OUTPUT: * + * target value * + * * + * WARNINGS: * + * Be certain that you only use the returned target value by passing * + * it to Target_To_TechnoType; do NOT call As_Techno, or you'll get * + * a totally invalid pointer. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr) +{ + TARGET target; + + switch (ptr->What_Am_I()) { + case RTTI_INFANTRYTYPE: + target = Build_Target(KIND_INFANTRY, ((InfantryTypeClass const *)ptr)->Type); + break; + + case RTTI_UNITTYPE: + target = Build_Target(KIND_UNIT, ((UnitTypeClass const *)ptr)->Type); + break; + + case RTTI_AIRCRAFTTYPE: + target = Build_Target(KIND_AIRCRAFT, ((AircraftTypeClass const *)ptr)->Type); + break; + + case RTTI_BUILDINGTYPE: + target = Build_Target(KIND_BUILDING, ((BuildingTypeClass const *)ptr)->Type); + break; + + default: + target = 0; + break; + } + + return(target); +} + + +/*************************************************************************** + * Target_To_TechnoType -- converts TARGET to TechnoTypeClass * + * * + * INPUT: * + * target TARGET value to convert * + * * + * OUTPUT: * + * pointer to the TechnoTypeClass for this target value * + * * + * WARNINGS: * + * The TARGET value MUST have been generated with TechnoType_To_Target;* + * If you give this routine a target generated by an As_Target() * + * routine, it will return a bogus pointer. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +TechnoTypeClass const * Target_To_TechnoType(TARGET target) +{ + switch (Target_Kind(target)) { + case KIND_INFANTRY: + return(&InfantryTypeClass::As_Reference((InfantryType)Target_Value(target))); + + case KIND_UNIT: + return(&UnitTypeClass::As_Reference((UnitType)Target_Value(target))); + + case KIND_AIRCRAFT: + return(&AircraftTypeClass::As_Reference((AircraftType)Target_Value(target))); + + case KIND_BUILDING: + return(&BuildingTypeClass::As_Reference((StructType)Target_Value(target))); + } + return(NULL); +} + + +/*************************************************************************** + * Get_VTable -- gets the VTable pointer for the given object * + * * + * INPUT: * + * ptr pointer to check * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +void * Get_VTable(void *ptr, int base_size) +{ + return(((void **)(((char *)ptr) + base_size - 4))[0]); +} + + +/*************************************************************************** + * Set_VTable -- sets the VTable pointer for the given object * + * * + * INPUT: * + * ptr pointer to check * + * base_size size of base class * + * vtable value of VTable to plug in * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +void Set_VTable(void *ptr, int base_size, void *vtable) +{ + ((void **)(((char *)ptr) + base_size - 4))[0] = vtable; +} + + +#if 0 +/**************************************************************************** +Dump routine: prints everything about everything related to the Save/Load +process (OK, not exactly everything, but lots of stuff) +****************************************************************************/ +void Dump(void) +{ + int i,j; + FILE *fp; + char *layername[] = { + "Ground", + "Air", + "Top" + }; + + /* + ------------------------------- Open file -------------------------------- + */ + fp = fopen("dump.txt","wt"); + + /* + ------------------------------ Logic Layer ------------------------------- + */ + fprintf(fp,"--------------------- Logic Layer ---------------------\n"); + fprintf(fp,"Count: %d\n",Logic.Count()); + for (j = 0; j < Logic.Count(); j++) { + fprintf(fp, "Entry %d: %x \n",j,Logic[j]); + } + fprintf(fp,"\n"); + + /* + ------------------------------- Map Layers ------------------------------- + */ + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,"----------------- Map Layer %s ---------------------\n", + layername[i]); + fprintf(fp,"Count: %d\n",Map.Layer[i].Count()); + for (j = 0; j < Map.Layer[i].Count(); j++) { + fprintf(fp, "Entry %d: %x \n",j,Map.Layer[i][j]); + } + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ TeamTypes --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",TeamTypes.ActiveCount); + for (i = 0; i < TEAMTYPE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,TeamTypes[i].IsActive, + TeamTypes[i].Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Teams --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Teams.ActiveCount); + for (i = 0; i < TEAM_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Teams[i].IsActive, + Teams[i].Class->Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Triggers --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Triggers.ActiveCount); + for (i = 0; i < TRIGGER_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Triggers[i].IsActive, + Triggers[i].Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Aircraft --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Aircraft.ActiveCount); + for (i = 0; i < AIRCRAFT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Aircraft[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Anims --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Anims.ActiveCount); + for (i = 0; i < ANIM_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Anims[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Buildings --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Buildings.ActiveCount); + for (i = 0; i < BUILDING_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Buildings[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Bullets --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Bullets.ActiveCount); + for (i = 0; i < BULLET_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Bullets[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Infantry --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Infantry.ActiveCount); + for (i = 0; i < INFANTRY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Infantry[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Overlays --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Overlays.ActiveCount); + for (i = 0; i < OVERLAY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Overlays[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Reinforcements --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Reinforcements.ActiveCount); + for (i = 0; i < REINFORCEMENT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Reinforcements[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Smudges --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Smudges.ActiveCount); + for (i = 0; i < SMUDGE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Smudges[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Templates --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Templates.ActiveCount); + for (i = 0; i < TEMPLATE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Templates[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Terrains --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Terrains.ActiveCount); + for (i = 0; i < TERRAIN_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Terrains[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Units --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Units.ActiveCount); + for (i = 0; i < UNIT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Units[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Factories --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Factories.ActiveCount); + for (i = 0; i < FACTORY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Factories[i].IsActive); + } + fprintf(fp,"\n"); + + fclose(fp); + + /* + ---------------------------- Flush the cache ----------------------------- + */ + fp = fopen("dummy.bin","wt"); + for (i = 0; i < 100; i++) { + fprintf(fp,"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + } + fclose(fp); +} +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/SCENARIO.CPP b/TIBERIANDAWN/SCENARIO.CPP new file mode 100644 index 000000000..8beaef13c --- /dev/null +++ b/TIBERIANDAWN/SCENARIO.CPP @@ -0,0 +1,789 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\scenario.cpv 2.17 16 Oct 1995 16:52:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + * This module handles the scenario reading and writing. Scenario related * + * code that is executed between scenario play can also be here. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * Do_Lose -- Display losing comments. * + * Do_Restart -- Handle the restart mission process. * + * Do_Win -- Display winning congratulations. * + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * Read_Scenario -- Reads a scenario from disk. * + * Restate_Mission -- Handles restating the mission objective. * + * Start_Scenario -- Starts the scenario. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +extern int PreserveVQAScreen; + + +/*********************************************************************************************** + * Start_Scenario -- Starts the scenario. * + * * + * This routine will start the scenario. In addition to loading the scenario data, it will * + * play the briefing and action movies. * + * * + * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * + * * + * briefing -- Should the briefing be played? Normally this is true except when the * + * scenario is restarting. * + * * + * OUTPUT: Was the scenario started without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Start_Scenario(char *root, bool briefing) +{ + + if (!Read_Scenario(root)) { + CCDebugString ("C&C95 - Failed to read scenario.\n"); + return(false); + } + CCDebugString ("C&C95 - Scenario read OK.\n"); + +#ifdef DEMO + + if (briefing) { + Play_Movie(BriefMovie); + Play_Movie(ActionMovie, TransitTheme); + } + Theme.Queue_Song(THEME_AOI); + +#else + + /* + ** Install some hacks around the movie playing to account for the choose- + ** sides introduction. We don't want an intro movie on scenario 1, and + ** we don't want a briefing movie on GDI scenario 1. + */ + if (Scenario < 20 && (!Special.IsJurassic || !AreThingiesEnabled)) { + if (Scenario != 1 || Whom == HOUSE_GOOD) { + Play_Movie(IntroMovie); + } + + if (briefing) { + PreserveVQAScreen = (Scenario == 1); + Play_Movie(BriefMovie); + } + Play_Movie(ActionMovie, TransitTheme); + if (TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_AOI); + } + } else { + Play_Movie(BriefMovie); + Play_Movie(ActionMovie, TransitTheme); + +#ifdef NEWMENU + + char buffer[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + CCFileClass file(buffer); + + if (GameToPlay == GAME_NORMAL && !file.Is_Available()) { + VisiblePage.Clear(); + Set_Palette(GamePalette); +// Show_Mouse(); + /* + ** Show the mission briefing. Pretend we are inside the main loop so the palette + ** will be correct on the textured buttons. + */ + bool oldinmain = InMainLoop; + InMainLoop = true; + + // TO_FIX - Covert ops missions want to pop up a dialog box. ST - 9/6/2019 1:48PM + //Restate_Mission(ScenarioName, TXT_OK, TXT_NONE); + + + InMainLoop = oldinmain; +// Hide_Mouse(); + if (TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_AOI); + } + } + +#endif + } +#endif + + /* + ** Hack for laser-firing Orcas in the PATSUX secret mission + */ + if (GameToPlay == GAME_NORMAL && Scenario == 72) { + ((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_OBELISK_LASER; + } + else { + ((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_DRAGON; + } + + /* + ** Set the options values, since the palette has been initialized by Read_Scenario + */ + CCDebugString ("C&C95 - About to call Options.Set.\n"); + Options.Set(); + CCDebugString ("C&C95 - About to return from Start_Scenario.\n"); + return(true); +} + + +/*********************************************************************************************** + * Set_Scenario_Difficulty -- Sets the difficulty of the scenario. * + * * + * Updates the player's difficulty in single-player mode. * + * * + * INPUT: difficulty -- Scenario difficulty * + * * + * OUTPUT: none * + * * + * WARNINGS: Only works in single-player. * + * Must call Start_Scenario first to initialize the player. * + * * + * HISTORY: * + * 10/02/2019 SKY : Created. * + *=============================================================================================*/ +void Set_Scenario_Difficulty(int difficulty) +{ + if (GameToPlay == GAME_NORMAL) { + switch (difficulty) { + case 0: + PlayerPtr->Assign_Handicap(DIFF_EASY); + break; + case 1: + PlayerPtr->Assign_Handicap(DIFF_NORMAL); + break; + case 2: + PlayerPtr->Assign_Handicap(DIFF_HARD); + break; + default: + break; + } + } +} + + +/*********************************************************************************************** + * Read_Scenario -- Reads a scenario from disk. * + * * + * This will read a scenario from disk. Use this to begin a scenario. * + * It doesn't perform any rendering, it merely sets up the system * + * with the proper data. Setting of the right game state will start * + * the scenario running. * + * * + * INPUT: root -- Scenario root filename * + * * + * OUTPUT: none * + * * + * WARNINGS: You must clear out the system variables before calling * + * this function. Use the Clear_Scenario() function. * + * It is assumed that Scenario is set to the current scenario number. * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 02/03/1992 JLB : Uses house identification. * + *=============================================================================================*/ +bool Read_Scenario(char *root) +{ + CCDebugString ("C&C95 - In Read_Scenario.\n"); + Clear_Scenario(); + ScenarioInit++; + if (Read_Scenario_Ini(root)) { + + Fill_In_Data(); + + Map.Set_View_Dimensions(0, Map.Get_Tab_Height(), Map.MapCellWidth, Map.MapCellHeight); + + + /* + ** SPECIAL CASE: + ** Clear out the tutor flags for scenarios one and two. This is designed + ** so that tutorial message will reappear in scenario two. + */ + if (Scenario < 5) { + TutorFlags[0] = 0L; + TutorFlags[1] = 0L; + } + + } else { + +#if (1) + char message[200]; + if (root) { + sprintf(message, "Failed to load scenario %s", root); + GlyphX_Debug_Print(message); + } else { + GlyphX_Debug_Print("Failed to load scenario"); + } +#else + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); + CCMessageBox().Process(TXT_UNABLE_READ_SCENARIO); + Hide_Mouse(); +#endif + return(false); + } + ScenarioInit--; + CCDebugString ("C&C95 - Leaving Read_Scenario.\n"); + return(true); +} + + +/*********************************************************************************************** + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * * + * This routine is called after the INI file for the scenario has been processed. It will * + * infer the game state from the scenario INI data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Fill_In_Data(void) +{ + /* + ** The basic scenario data load does not contain the full set of + ** game data. We now must fill in the missing pieces. + */ + ScenarioInit++; + + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + + Map.Flag_To_Redraw(true); + + /* + ** Bring up the score display on the radar map when starting a multiplayer + ** game. + */ + if (GameToPlay != GAME_NORMAL) { + Map.Player_Names(1); + } + + ScenarioInit--; +} + + +/*********************************************************************************************** + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * * + * This routine will clear out all data specific to a scenario in * + * preparation for a subsequent scenario data load. This will free * + * all units, animations, and icon maps. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * + * 07/13/1995 JLB : End count down moved here. * + *=============================================================================================*/ +void Clear_Scenario(void) +{ + EndCountDown = TICKS_PER_SECOND * 30; + CrateCount = 0; + CrateTimer = 0; + CrateMaker = false; + + /* + ** Call everyone's Init routine, except the Map's; for the Map, only call + ** MapClass::Init, which clears the Cell array. The Display::Init requires + ** a Theater argument, and the theater is not known at this point; also, it + ** would reload MixFiles, which isn't desired. Display::Read_INI calls its + ** own Init, which will Init the entire Map hierarchy. + */ + Map.Init_Clear(); + Score.Init(); + Logic.Init(); + + HouseClass::Init(); + ObjectClass::Init(); + TeamTypeClass::Init(); + TeamClass::Init(); + TriggerClass::Init(); + AircraftClass::Init(); + AnimClass::Init(); + BuildingClass::Init(); + BulletClass::Init(); + InfantryClass::Init(); + OverlayClass::Init(); + SmudgeClass::Init(); + TemplateClass::Init(); + TerrainClass::Init(); + UnitClass::Init(); + + FactoryClass::Init(); + + Base.Init(); + + CurrentObject.Clear_All(); + + Invalidate_Cached_Icons(); +} + + +/*********************************************************************************************** + * Do_Win -- Display winning congratulations. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 01/01/1995 JLB : Carries money forward into next scenario. * + *=============================================================================================*/ +void Do_Win(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (GameToPlay != GAME_NORMAL) { + MPlayerGameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32; + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); +#if !(GERMAN | FRENCH) + Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR); +#endif + Fancy_Text_Print(TXT_SCENARIO_WON, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + CountDownTimer.Set(TIMER_SECOND * 3); + Stop_Speaking(); + Speak(VOX_ACCOMPLISHED); + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (GameToPlay != GAME_NORMAL) { + if (!PlaybackGame) { + MPlayerGamesPlayed++; + Multi_Score_Presentation(); + MPlayerCurGame++; + if (MPlayerCurGame >= MAX_MULTI_GAMES) { + MPlayerCurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + /* + ** Play the winning movie and then start the next scenario. + */ + if (RequiredCD != -2) { + if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (Scenario >=60){ + RequiredCD = -1; + }else{ + if (PlayerPtr->Class->House == HOUSE_GOOD) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } + } + } + } + +#ifndef DEMO + Play_Movie(WinMovie); +#endif + + Keyboard::Clear(); + + /* + ** Do the ending screens only if not playing back a recorded game. + */ + if (!PlaybackGame) { + +#ifdef DEMO + + switch (Scenario) { + case 1: + Score.Presentation(); + Scenario = 10; + break; + + case 10: + Score.Presentation(); + Scenario = 6; + break; + + default: + Score.Presentation(); + GDI_Ending(); + GameActive = false; + Show_Mouse(); + return; +// Prog_End(); +// exit(0); +// break; + } + +#else + +#ifdef NEWMENU + if (Scenario >= 20) { + Keyboard::Clear(); + Score.Presentation(); + GameActive = false; + Show_Mouse(); + return; + } +#endif + + if (PlayerPtr->Class->House == HOUSE_BAD && Scenario == 13) { + Nod_Ending(); + //Prog_End(); + //exit(0); + SeenBuff.Clear(); + Show_Mouse(); + GameActive = false; + return; + } + if (PlayerPtr->Class->House == HOUSE_GOOD && Scenario == 15) { + GDI_Ending(); + //Prog_End(); + //exit(0); + SeenBuff.Clear(); + Show_Mouse(); + GameActive = false; + return; + } + + if ( (Special.IsJurassic && AreThingiesEnabled) && Scenario == 5) { + Prog_End("Do_Win - Last Jurassic mission complete"); + if (!RunningAsDLL) { + exit(0); + } + return; + } + + if (!Special.IsJurassic || !AreThingiesEnabled) { + Keyboard::Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Score.Presentation(); + + /* + ** Skip scenario #7 if the airfield was blown up. + */ + if (Scenario == 6 && PlayerPtr->Class->House == HOUSE_GOOD && SabotagedType == STRUCT_AIRSTRIP) { + Scenario++; + } + + Map_Selection(); + } + Scenario++; +#endif + Keyboard::Clear(); + } + + CarryOverMoney = PlayerPtr->Credits; + + int pieces = PlayerPtr->NukePieces; + + /* + ** Generate a new scenario filename + */ + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar); + Start_Scenario(ScenarioName); + + PlayerPtr->NukePieces = pieces; + + /* + ** Destroy the building that was sabotaged in the previous scenario. This only + ** applies to GDI mission #7. + */ + int index; + if (SabotagedType != STRUCT_NONE && Scenario == 7 && PlayerPtr->Class->House == HOUSE_GOOD) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House != PlayerPtr && building->Class->Type == SabotagedType) { + building->Limbo(); + delete building; + break; + } + } + + /* + ** Remove the building from the prebuild list. + */ + for (index = 0; index < Base.Nodes.Count(); index++) { + BaseNodeClass * node = Base.Get_Node(index); + + if (node && node->Type == SabotagedType) { + Base.Nodes.Delete(index); + break; + } + } + } + SabotagedType = STRUCT_NONE; + + Map.Render(); + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Lose -- Display losing comments. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +void Do_Lose(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (GameToPlay != GAME_NORMAL) { + MPlayerGameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32; + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + Fancy_Text_Print(TXT_SCENARIO_LOST, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + CountDownTimer.Set(TIMER_SECOND * 3); + Stop_Speaking(); + Speak(VOX_FAIL); + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); + } + +#ifdef OBSOLETE + if (Debug_Play_Map) { + Go_Editor(true); + Show_Mouse(); + return; + } +#endif + + /* + ** Stop here if this is a multiplayer game. + */ + if (GameToPlay != GAME_NORMAL) { + if (!PlaybackGame) { + MPlayerGamesPlayed++; + Multi_Score_Presentation(); + MPlayerCurGame++; + if (MPlayerCurGame >= MAX_MULTI_GAMES) { + MPlayerCurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Play_Movie(LoseMovie); + + /* + ** Start same scenario again + */ + Set_Palette(GamePalette); + Show_Mouse(); + if (!PlaybackGame && !CCMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { + Hide_Mouse(); + Keyboard::Clear(); + Start_Scenario(ScenarioName, false); + Map.Render(); + } else { + Hide_Mouse(); + GameActive = 0; + } + + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Restart -- Handle the restart mission process. * + * * + * This routine is called in the main game loop when the mission must be restarted. This * + * routine will throw away the current game and reload the appropriate mission. The * + * game will "resume" at the start of the mission. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +void Do_Restart(void) +{ + bool hidden = Get_Mouse_State(); + + if (hidden) Show_Mouse(); + CCMessageBox().Process(TXT_RESTARTING, TXT_NONE); + Map.Set_Default_Mouse(MOUSE_NORMAL); + Keyboard::Clear(); + Start_Scenario(ScenarioName, false); + if (hidden) Hide_Mouse(); + Keyboard::Clear(); + Map.Render(); +} + + +/*********************************************************************************************** + * Restate_Mission -- Handles restating the mission objective. * + * * + * This routine will display the mission objective (as text). It will also give the * + * option to redisplay the mission briefing video. * + * * + * INPUT: name -- The scenario name. This is the unique identifier for the scenario * + * briefing text as it appears in the "MISSION.INI" file. * + * * + * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * + * requested, or 0 if the return to game options button was selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + * 08/06/1995 JLB : Uses preloaded briefing text. * + *=============================================================================================*/ +bool Restate_Mission(char const * name, int button1, int button2) +{ + if (name) { +#ifdef JAPANESE + char fname[14]; + strcpy(fname, name); + strcat(fname,".CPS"); + + if(CCFileClass(fname).Is_Available()) { + CCMessageBox box(TXT_NONE, true); + return(box.Process(fname, button1, button2)); + } +#else + /* + ** Make sure that if there is no briefing movie, that the briefing text is + ** the only option available. + */ + bool brief = true; +#ifdef NEWMENU + char buffer[25]; + char buffer1[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + sprintf(buffer1, "%s.VQA", ActionMovie); + CCFileClass file1(buffer); + CCFileClass file2(buffer1); + if (!file1.Is_Available() && !file2.Is_Available()) { + button1 = TXT_OK; + button2 = TXT_NONE; + brief = false; + } +#endif + + /* + ** If mission object text was found, then display it. + */ + if (strlen(BriefingText)) { + static char _buff[512]; + + strcpy(_buff, BriefingText); +// strcpy(_ShapeBuffer, BriefingText); + + bool hidden = Get_Mouse_State(); + if (hidden) Show_Mouse(); + + if (CCMessageBox(TXT_OBJECTIVE).Process(_buff, button1, button2)) { + if (hidden) Hide_Mouse(); + return(true); + } + if (hidden) Hide_Mouse(); + if (!brief) return(true); + return(false); + } +#endif + } + return(false); +} \ No newline at end of file diff --git a/TIBERIANDAWN/SCORE.CPP b/TIBERIANDAWN/SCORE.CPP new file mode 100644 index 000000000..37bcfc884 --- /dev/null +++ b/TIBERIANDAWN/SCORE.CPP @@ -0,0 +1,2322 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\score.cpv 2.17 16 Oct 1995 16:49:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : May 3, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * Draw_Infantrymen -- Draw all the guys on the score screen * + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen * + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly * + * ScoreClass::Delay -- Pauses waiting for keypress. * + * ScoreClass::DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen * + * ScoreClass::Presentation -- Main routine to display score screen. * + * ScoreClass::Print_Graph_Title -- Prints title on score screen. * + * ScoreClass::Print_Minutes -- Print out hours/minutes up to max * + * ScoreClass::Pulse_Bar_Graph -- Pulses the bargraph color. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" +#define SCORETEXT_X 184 +#define SCORETEXT_Y 8 +#define CASUALTY_Y 88 +#define BUILDING_X 256 +#define BUILDING_Y 128 +#define BARGRAPH_X 266 +#define MAX_BAR_X 318 // max possible is 319 because of bar's right shadow +#define SIZEGBAR 119 +#define HALLFAME_X 19 +#define HALLFAME_Y 120 + +#define MULTISCOREX 30 + +#define TEDIT_FAME 1 +#define NUMINFANTRYMEN 15 +#define NUMFAMENAMES 7 +#define MAX_FAMENAME_LENGTH 12 + +struct InfantryAnim { + int xpos; + int ypos; + void const *shapefile; + void const *remap; + int anim; + int stage; + char delay; + InfantryTypeClass const *Class; +} InfantryMan[NUMINFANTRYMEN]; +void Draw_InfantryMen(void); +void Draw_InfantryMan(int index); +void New_Infantry_Anim(int index, int anim); +void Draw_Bar_Graphs(int i, int gkilled, int nkilled, int ckilled); +void Animate_Cursor(int pos, int ypos); +void Animate_Score_Objs(void); +void Cycle_Wait_Click(void); +int ScorePass; + +void const * Beepy6; +int ControlQ; // cheat key to skip past score/mapsel screens +bool StillUpdating; + +GraphicBufferClass *PseudoSeenBuff; +GraphicBufferClass *TextPrintBuffer; + +#ifdef WRITE_LBM +PUBLIC bool CCWrite_LBM_File(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes, unsigned char *palette); +#endif + +unsigned char RemapCiv[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0xD0,0x18,0x19,0xD1,0xD2,0xD3,0xD4,0x1E,0xD5, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0xD6,0xD7,0xD8,0xD9,0x7E,0xDA, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xDB,0xDC,0xDD,0xDE,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + +unsigned char const ScoreRemapGrey[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0..15 + 16, 17, 18, 19, 20,176, 22,208, 24, 25,209,210,211,212, 30,213, // 16..31 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32..47 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48..63 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 64..79 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 80..95 + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,214,215,216,217,149,218, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,108,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,219,220,221,222,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const ScoreRemapYellow[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0..15 + 16, 17, 18, 19, 20,176, 22,208, 24, 25,209,210,211,212, 30,213, // 16..31 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32..47 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48..63 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 64..79 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 80..95 + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,214,215,216,217,149,218, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,108,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,219,220,221,222,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const ScoreRemapBldg[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0xCE,0xC5,0x49,0x48,0x47,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0xB8,0x83,0x7C,0x7A,0x76,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x74,0x91,0x92,0x93,0x94,0x95,0xB5,0x97,0x98,0xCF,0x4B,0x7F,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0x83,0x79,0xA5,0xA6,0xA7,0x43,0x99,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xBB,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; +unsigned char const ScoreRemapFBall[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0xB0,0xE5,0x82,0xE4,0xE3,0xE2,0xB1,0xD0,0xE1,0xE0,0xD1,0xD2,0xD3,0xD4,0xDF,0xD5, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0xE6,0xE7,0xA0,0xE8,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +TextBlitClass BlitList; + + +char *ScreenNames[2]={"S-GDIIN2.WSA","SCRSCN1.WSA"}; + +//extern short StreamLowImpact; + +// ST - 1/3/2019 10:38AM +int StreamLowImpact = FALSE; + +struct Fame { + char name[MAX_FAMENAME_LENGTH]; + int score; + int level; +}; + +ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + + +ScoreAnimClass::ScoreAnimClass(int x, int y, void const * data) +{ + BlitList.Add (x*2, y*2, x*2, y*2, 2* String_Pixel_Width ( (char*)data ) , 16); + XPos = x; + YPos = y; + Timer.Set(0); + Timer.Start(); + DataPtr = data; +} + + +ScoreTimeClass::ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = max; + TimerReset = timer; +} + +void ScoreTimeClass::Update(void) +{ + GraphicViewPortClass *oldpage; + if (!Timer.Time()) { + Timer.Set(TimerReset); + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(oldpage); + } +} + +ScoreCredsClass::ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = max; + TimerReset = timer; + Clock1 = MixFileClass::Retrieve("CLOCK1.AUD"); + CashTurn = MixFileClass::Retrieve("CASHTURN.AUD"); +} + + +void ScoreCredsClass::Update(void) +{ + GraphicViewPortClass *oldpage; + if (!Timer.Time()) { + Timer.Set(TimerReset); + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(PseudoSeenBuff); + if (Stage <22) { + Play_Sample(Clock1, 255, Options.Normalize_Sound(70)); + } else { + if (Stage==24) { + Play_Sample(CashTurn, 255, Options.Normalize_Sound(70)); + } + } + CC_Draw_Shape(DataPtr,Stage, XPos, YPos, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(oldpage); + } +} + + +ScorePrintClass::ScorePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +ScorePrintClass::ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void ScorePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + BlitList.Add (XPos*2, YPos*2, XPos*2, YPos*2,(Stage*6)+14, 8*2); + delete this; + return; + } + + StillUpdating = true; + + if (!Timer.Time()) { + Timer.Set(1); + + int pos = XPos+(Stage*6); + if (Stage) { + + localstr[0]=((char *)DataPtr)[Stage-1]; + + /* + ** Clear out the white letter overlay + */ + static char const _blackpal[]={BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK}; + Set_Font_Palette(_blackpal); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6+1),2*(YPos), TBLACK, TBLACK); + + Set_Font_Palette(PrimaryPalette); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*YPos, TBLACK, TBLACK); + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, (pos+1)*2,2*YPos , TBLACK, TBLACK); + } + Stage++; + } +} + + + + + + + + + + + +MultiStagePrintClass::MultiStagePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +MultiStagePrintClass::MultiStagePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void MultiStagePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + BlitList.Add (XPos*2, YPos*2, XPos*2, YPos*2,(Stage*6)+14, 8*2); + delete this; + return; + } + + StillUpdating = true; + + if (!Timer.Time()) { + Timer.Set(1); + + /* + ** Do 10 stages at once + */ + for (int wibble = 0 ; wibble < 10 ; wibble ++){ + + int pos = XPos+(Stage*6); + if (Stage) { + + localstr[0]=((char *)DataPtr)[Stage-1]; + + /* + ** Clear out the white letter overlay + */ + static char const _blackpal[]={BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK}; + Set_Font_Palette(_blackpal); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6+1),2*(YPos), TBLACK, TBLACK); + + Set_Font_Palette(PrimaryPalette); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*YPos, TBLACK, TBLACK); + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, (pos+1)*2,2*YPos , TBLACK, TBLACK); + } + Stage++; + + if ( ( (char *) DataPtr) [Stage-1] == 0 ) break; + } + } +} + + + + + + +ScoreScaleClass::ScoreScaleClass(void const * string, int xpos, int ypos, char const palette[]) : + ScoreAnimClass(xpos, ypos, string) +{ + Palette = &palette[0]; + Stage = 5; +} + + +void ScoreScaleClass::Update(void) +{ + static int _destx[]={0,80,107,134,180,228}; + static int _destw[]={6,20, 30, 40, 60, 80}; + + /* + ** Restore the background for the scaled-up letter + */ + if (!Timer.Time()) { + Timer.Set(1); + if (Stage != 5) { + TextPrintBuffer->Blit(HidPage, _destx[Stage+1]*2, YPos*2, _destx[Stage+1]*2, YPos*2, _destw[Stage+1]*2, _destw[Stage+1]*2); + //SysMemPage.Blit(*PseudoSeenBuff, _destx[Stage+1], YPos, _destx[Stage+1], YPos, _destw[Stage+1], _destw[Stage+1]); + } + if (Stage) { + Set_Font_Palette(Palette); + TextPrintBuffer->Fill_Rect(0,0, 7*2,7*2, TBLACK); + TextPrintBuffer->Print((char *)DataPtr, 0,0, TBLACK, TBLACK); + TextPrintBuffer->Scale(HidPage, 0,0, _destx[Stage]*2, YPos*2, 5*2,5*2, _destw[Stage]*2, _destw[Stage]*2, true); + + //SysMemPage.Fill_Rect(0,0, 7,7, TBLACK); + //SysMemPage.Print((char *)DataPtr, 0,0, TBLACK, TBLACK); + //SysMemPage.Scale(*PseudoSeenBuff, 0,0, _destx[Stage], YPos, 5,5, _destw[Stage], _destw[Stage], true); + Stage--; + } else { + Set_Font_Palette(Palette); + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]==this) ScoreObjs[i] = 0; + } + TextPrintBuffer->Print((char *)DataPtr, XPos*2,YPos*2, TBLACK, TBLACK); + //TextPrintBuffer->Blit(HidPage, XPos*2, YPos*2, XPos*2, YPos*2,2*6,2*6); + //BlitList.Add (XPos, YPos, XPos, YPos, 6,6); + + //SysMemPage.Print((char *)DataPtr, XPos,YPos, TBLACK, TBLACK); + //SysMemPage.Blit(*PseudoSeenBuff, XPos,YPos, XPos, YPos, 6,6); + delete this; + return; + } + } +} + +int Alloc_Object(ScoreAnimClass *obj) +{ + int i,ret; + + for (i = ret = 0; i < MAXSCOREOBJS; i++) { + if (!ScoreObjs[i]) { + ScoreObjs[i] = obj; + ret = i; + break; + } + } + return(ret); +} + + + + + +TextBlitClass::TextBlitClass (void) +{ + Clear(); +} + + + +void TextBlitClass::Add (int x, int y, int dx, int dy, int w, int h) +{ + if ( Count < MAX_ENTRIES ){ + + BlitListo [Count].SourceX = x; + BlitListo [Count].SourceY = y; + BlitListo [Count].DestX = dx; + BlitListo [Count].DestY = dy; + BlitListo [Count].Width = w; + BlitListo [Count].Height = h; + + Count++; + } +} + + +void TextBlitClass::Clear(void) +{ + Count = 0; +} + + + + +void TextBlitClass::Update(void) +{ + if (TextPrintBuffer){ + + if (HidPage.Lock()){ + + for (int i=0 ; iBlit (HidPage, BlitListo[i].SourceX, + BlitListo[i].SourceY, + BlitListo[i].DestX, + BlitListo[i].DestY, + BlitListo[i].Width, + BlitListo[i].Height, + true); + } + + HidPage.Unlock(); + } + } +} + + + +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); + +/*********************************************************************************************** + * ScoreClass::Presentation -- Main routine to display score screen. * + * * + * This is the main routine that displays the score screen graphics. * + * It gets called at the end of each scenario and is used to present * + * the results and a rating of the player's battle. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 : Created. * + *=============================================================================================*/ +void ScoreClass::Presentation(void) +{ + //static char const _redpal[]={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x21,0x2F}; + static char const _redpal[]={0x20,0x22,0x24,0x26,0x28,0x28,0x28,0x28,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x21,0x2F}; + static char const _greenpal[]={0x10,0x12,0x14,0x16,0x18,0x18,0x18,0x18,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x10,0x1F}; + static char const _bluepal[]={0x60,0x62,0x64,0x66,0x68,0x68,0x68,0x68,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x61,0x6F}; + //static char const _bluepal[]={0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x61,0x6F}; + static unsigned char const _yellowpal[]={0x0,0x0,0xEC,0x0,0xEB,0x0,0xEA,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; + static int const _casuax[2]={144,146}; + static int const _casuay[2]={ 78, 90}; + static int const _gditxx[2]={150,224}; + static int const _gditxy[2]={ 90, 90}; + static int const _nodtxx[2]={150,224}; + static int const _nodtxy[2]={102,102}; +// static int _bargrx[2]={297,SCORETEXT_X+64}; +// static int _bargry[2]={ 90,CASUALTY_Y + 2}; + static int const _bldggy[2]={138,128}; + static int const _bldgny[2]={150,140}; + +// int gdikilled, nodkilled, civkilled, max, i, k, shapenum; + int i; + int max; + void const * yellowptr; + void const * redptr; + CCFileClass file(FAME_FILE_NAME); + struct Fame hallfame[NUMFAMENAMES]; + void *anim, *oldfont; + int oldfontxspacing = FontXSpacing; + int house = PlayerPtr->Class->House; // 0 or 1 + char inter_pal[15]; + + /* + ** Choose an appropriate palette file for the interpolation + */ + if (house == HOUSE_GOOD){ + sprintf(inter_pal,"SCORPAL1.PAL"); + }else{ + sprintf(inter_pal,"SNODPAL1.PAL"); + } + + + if (Special.IsJurassic && AreThingiesEnabled) return; + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + Disable_Uncompressed_Shapes (); + + ControlQ = 0; + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_WIN1); + + VisiblePage.Clear(); + PseudoSeenBuff->Clear(); + SysMemPage.Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + Set_Palette(BlackPalette); + + Set_Logic_Page(SysMemPage); + + void const * country4 = MixFileClass::Retrieve("COUNTRY4.AUD"); + void const * sfx4 = MixFileClass::Retrieve("SFX4.AUD"); + Beepy6 = MixFileClass::Retrieve("BEEPY6.AUD"); + + /* + ** Load the background for the score screen + */ + anim = Open_Animation(ScreenNames[house],NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + + unsigned minutes = (unsigned)((ElapsedTime / (long)TIMER_MINUTE))+1; + + /* + ** Determine leadership rating. + */ + unsigned leadership = 0; + int index; + for (index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + if (object->Owner() == house) { + leadership++; + } + } + + HouseClass *houses[3]; + for (index = 0; index < 3; index++) { + houses[index] =(HouseClass::As_Pointer((HousesType)(HOUSE_GOOD+index))); + } + + GKilled = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; + NKilled = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; + CKilled = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; + GBKilled = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; + NBKilled = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; + CBKilled = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; + + /* + ** New - ST 6/12/96 2:40PM + */ + GHarvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; + NHarvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; + + if (!leadership) leadership++; + leadership = Cardinal_To_Fixed(GKilled+GBKilled+leadership, leadership); + leadership = Fixed_To_Cardinal(100, leadership); + if (leadership > 100) leadership = 100; + + /* + ** Determine efficiency rating. + */ + int gharv = GHarvested; + int init = PlayerPtr->InitialCredits; + int cred = PlayerPtr->Available_Money(); + + unsigned efficiency = Cardinal_To_Fixed( (house == HOUSE_GOOD ? GHarvested : NHarvested) + (unsigned)PlayerPtr->InitialCredits+1, (unsigned)PlayerPtr->Available_Money()+1); + if (!efficiency) efficiency++; + efficiency = Fixed_To_Cardinal(100, efficiency); + + if (efficiency > 100) efficiency = 100; + /* + ** Calculate total score + */ + long total = ((leadership * 40) + (4600) + (efficiency * 14))/100; + if (!total) total++; + total *= (BuildLevel+1); + +// Load up the shapes for the Nod score screen + if (house == HOUSE_GOOD) { + yellowptr = MixFileClass::Retrieve("BAR3YLW.SHP"); + redptr = MixFileClass::Retrieve("BAR3RED.SHP"); + } + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + +/* --- Now display the background animation --- */ + Hide_Mouse(); + Animate_Frame(anim, SysMemPage, 1); + SysMemPage.Blit(*PseudoSeenBuff); + Increase_Palette_Luminance (Palette , 30,30,30,63); + InterpolationPalette = Palette; + InterpolationPaletteChanged = TRUE; + Read_Interpolation_Palette(inter_pal); + Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , inter_pal); + Fade_Palette_To(Palette, FADE_PALETTE_FAST, Call_Back); + + Play_Sample(country4, 255, Options.Normalize_Sound(90)); + + int frame = 1; + StreamLowImpact = true; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, *PseudoSeenBuff, frame++); + ////////////////Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + Call_Back_Delay(2); + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); + + /* + ** Background's up, so now load various shapes and animations + */ + void const * timeshape = MixFileClass::Retrieve("TIME.SHP"); + ScoreObjs[0] = new ScoreTimeClass(233, 2, timeshape, 30, 4); + + void const * hiscore1shape = MixFileClass::Retrieve("HISCORE1.SHP"); + void const * hiscore2shape = MixFileClass::Retrieve("HISCORE2.SHP"); + ScoreObjs[1] = new ScoreTimeClass(4, 97, hiscore1shape, 10, 4); + ScoreObjs[2] = new ScoreTimeClass(8, 172, hiscore2shape, 10, 4); + + /* Now display the stuff */ + PseudoSeenBuff->Blit(SysMemPage); + + + if (house == HOUSE_BAD) { + + /* + ** load the logo + */ + void const * logoptr = MixFileClass::Retrieve("LOGOS.SHP"); + CC_Draw_Shape(logoptr, 1, 0, 0, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + + Bit_It_In_Scale(0,0, 128,104-16, &SysMemPage, PseudoSeenBuff, &SeenBuff , 1); + } + + Set_Logic_Page(PseudoSeenBuff); + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 200, 3,_greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 206, 3,_greenpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_LEAD, 182, 26,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_EFFI, 182, 38,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOTA, 182, 50,_greenpal)); + Play_Sample(sfx4, 255, Options.Normalize_Sound(120)); + Call_Back_Delay(13); + + max = MAX((long)leadership, (long)efficiency); + int scorecounter = 0; + Keyboard::Clear(); + + BlitList.Add (264*2, 26*2, 264*2, 26*2, 4*12 , 12); + BlitList.Add (264*2, 38*2, 264*2, 38*2, 4*12 , 12); + BlitList.Add (264*2, 50*2, 264*2, 50*2, 4*12 , 12); + BlitList.Add (275*2, 9*2, 275*2, 9*2, 64, 12 ); //Minutes + for (i = 0; i <= 160; i++) { + Set_Font_Palette(_greenpal); + Count_Up_Print("%3d%%", i, leadership, 264, 26); + if (i>=30) Count_Up_Print("%3d%%", i-30, efficiency, 264, 38); + if (i>=60) { + Count_Up_Print("%3d",scorecounter, total, 264, 50); + scorecounter += total/100; + } + Print_Minutes(minutes); + Call_Back_Delay(1); + Play_Sample(Beepy6, 255, Options.Normalize_Sound(60)); + if (Check_Key() && i < (max-5) ) { + i=158; + Keyboard::Clear(); + } + } + Count_Up_Print("%3d", total, total, 264, 50); + + Call_Back_Delay(60); + + if (house == HOUSE_BAD) Show_Credits(house, _greenpal); + + Call_Back_Delay(60); + + /* + ** Show stats on # of units killed + */ + Set_Logic_Page(*PseudoSeenBuff); + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_CASU, _casuax[house], _casuay[house],_redpal)); + Call_Back_Delay(9); + if (house == HOUSE_BAD) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_NEUT, 200, 114,_redpal)); + Call_Back_Delay(4); + } + + Alloc_Object(new ScorePrintClass(TXT_SCORE_GDI, _gditxx[house], _gditxy[house],_redpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NOD, _nodtxx[house], _nodtxy[house],_redpal)); + Call_Back_Delay(6); + + Set_Font_Palette(_redpal); + if (house == HOUSE_BAD) { + Do_Nod_Casualties_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 88); + } + + Set_Logic_Page(*PseudoSeenBuff); + + /* + ** Print out stats on buildings destroyed + */ + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126,_greenpal)); + Call_Back_Delay(9); + } else { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL1, 146, 128,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL2, 146, 136,_greenpal)); + Call_Back_Delay(9); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NEUT, 200, 152,_greenpal)); + Call_Back_Delay(4); + } + Alloc_Object(new ScorePrintClass(TXT_SCORE_GDI, _gditxx[house], _bldggy[house],_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NOD, _gditxx[house], _bldgny[house],_greenpal)); + Call_Back_Delay(7); + + if (house==HOUSE_BAD) { + Call_Back_Delay(6); + Set_Font_Palette(_greenpal); + Do_Nod_Buildings_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 136); + } + + // Wait for text printing to complete + while (StillUpdating){ + Call_Back_Delay(1); + } + + if (Keyboard::Check()) Keyboard::Clear(); + + if (house == HOUSE_GOOD) Show_Credits(house, _greenpal); + + /* + ** Hall of fame display and processing + */ + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 28, 110,_bluepal)); + Call_Back_Delay(9); + + /* + ** First check for the existence of the file, and if there isn't one, + ** make a new one filled with blanks. + */ + if (!file.Is_Available()) { + + // hall of fame doesn't exist, so blank it out & write it + file.Open(WRITE); + + for (i = 0; i < NUMFAMENAMES; i++) { + hallfame[i].name[0] = + hallfame[i].score = + hallfame[i].level = 0; + file.Write(&hallfame[i], sizeof(struct Fame)); + } + + file.Close(); + } + + file.Open(READ); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Read(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + + /* + ** If the player's score is good enough to bump someone off the list, + ** remove their data, move everyone down a notch, and set index = where + ** their info goes + */ + if(hallfame[NUMFAMENAMES-1].score >= total) + hallfame[NUMFAMENAMES-1].score = 0; + for (index = 0; index < NUMFAMENAMES; index++) { + if (total > hallfame[index].score) { + if (index < (NUMFAMENAMES-1)) for (i = (NUMFAMENAMES-1); i > index; i--) hallfame[i] = hallfame[i-1]; + hallfame[index].score = total; + hallfame[index].level = Scenario; +// hallfame[index].level = BuildLevel; + //hallfame[index].name[0] = 0; // blank out the name + memset (hallfame[index].name, ' ', sizeof (hallfame[index].name) -1); + hallfame[index].name[sizeof (hallfame[index].name)-1] = 0; + break; + } + } + + /* + ** Now display the hall of fame + */ + Set_Logic_Page(*PseudoSeenBuff); + for (i = 0; i < NUMFAMENAMES; i++) { + Alloc_Object(new ScorePrintClass(hallfame[i].name, HALLFAME_X, HALLFAME_Y + (i*8), _bluepal)); + if (hallfame[i].score) { + char *str = (char *)(SysMemPage.Get_Buffer()) + i*32; + sprintf(str,"%d", hallfame[i].score); + Alloc_Object(new ScorePrintClass(str, HALLFAME_X+(6*15), HALLFAME_Y + (i*8), _bluepal, BLACK)); + if (hallfame[i].level < 20) { + sprintf(str+16,"%d", hallfame[i].level); + } else { + strcpy(str+16, "**"); + } + Alloc_Object(new ScorePrintClass(str+16, HALLFAME_X+(6*12), HALLFAME_Y + (i*8), _bluepal, BLACK)); + Call_Back_Delay(13); + } + } + + // Wait for text printing to complete + while (StillUpdating){ + Call_Back_Delay(1); + } + + /* + ** If the player's on the hall of fame, have him enter his name now + */ + Keyboard::Clear(); + if (index < NUMFAMENAMES) { + Input_Name(hallfame[index].name, HALLFAME_X, HALLFAME_Y + (index*8), _bluepal); + + file.Open(WRITE); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Write(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + } else { +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 145, 190, _yellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 149, 190, _yellowpal)); +#endif + Cycle_Wait_Click(); + } + +#ifdef WRITE_LBM + file.Open("e:scorscrn.lbm",WRITE); + SeenBuff.Blit(SysMemPage,0,0, 0,0, 320,200); + CCWrite_LBM_File(file, SysMemPage, 8, Palette); + file.Close(); +#endif + + Keyboard::Clear(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + + Show_Mouse(); +// Map_Selection(); + + Theme.Queue_Song(THEME_NONE); + + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + Set_Palette(GamePalette); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + + Set_Logic_Page (SeenBuff); + + delete PseudoSeenBuff; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); + Enable_Uncompressed_Shapes(); +} + +// ST = 12/17/2018 5:44PM +#ifndef TickCount +extern TimerClass TickCount; +#endif + +void Cycle_Wait_Click(void) +{ + int counter = 0; + int minclicks = 20; + unsigned long timingtime = TickCount.Time(); + //SerialPacketType sendpacket; + //SerialPacketType receivepacket; + //int packetlen; + + + Keyboard::Clear(); + while (minclicks || (!Check_Key() && !ControlQ) ) { + + if (GameToPlay == GAME_NULL_MODEM || + GameToPlay == GAME_MODEM){ + //GameToPlay == GAME_INTERNET) { +#if (0) //PG_TO_FIX + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + sendpacket.Command = SERIAL_SCORE_SCREEN; + sendpacket.ResponseTime = NullModem.Response_Time(); + sendpacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&sendpacket, sizeof(sendpacket), 0); + timingtime = TickCount.Time(); + } + + if (NullModem.Get_Message (&receivepacket, &packetlen) > 0) { + // throw packet away + packetlen = packetlen; + } + + NullModem.Service(); +#endif + } + + Call_Back_Delay(1); + if (minclicks) { + minclicks--; + Keyboard::Clear(); + } + + counter = ((++counter) & 7); + + if (!counter) { + unsigned char r,g,b; + + r = Palette[233*3+0]; + g = Palette[233*3+1]; + b = Palette[233*3+2]; + + for (int i = 233; i < 237; i++) { + Palette[i*3+0] = Palette[(i+1)*3+0]; + Palette[i*3+1] = Palette[(i+1)*3+1]; + Palette[i*3+2] = Palette[(i+1)*3+2]; + } + Palette[237*3+0] = r; + Palette[237*3+1] = g; + Palette[237*3+2] = b; + + Set_Palette(Palette); + } + } + Keyboard::Clear(); +} + +void ScoreClass::Do_Nod_Buildings_Graph(void) +{ + int shapenum; + InfantryTypeClass const *ramboclass; + + void const * factptr = MixFileClass::Retrieve("FACT.SHP"); + void const * rmboptr = MixFileClass::Retrieve("RMBO.SHP"); + void const * fball1ptr = MixFileClass::Retrieve("FBALL1.SHP"); + ramboclass = &InfantryTypeClass::As_Reference(INFANTRY_E5); + + /* + ** Print the # of buildings on the hidpage so we only need to do it once + */ + PseudoSeenBuff->Blit(SysMemPage); + Set_Logic_Page(SysMemPage); + Call_Back_Delay(30); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y), 2* (BUILDING_X+8), 2* (BUILDING_Y), 5*12 , 12); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y + 12), 2* (BUILDING_X+8), 2* (BUILDING_Y + 12), 5*12 , 12); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y + 24), 2* (BUILDING_X+8), 2* (BUILDING_Y + 24), 5*12 , 12); + + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, BUILDING_Y*2, TBLACK, TBLACK); + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, (BUILDING_Y + 12)*2, TBLACK, TBLACK); + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, (BUILDING_Y + 24)*2, TBLACK, TBLACK); + + /* + ** Here's the animation/draw loop for blowing up the factory + */ + int i; + for (i=0; i<98; i++) { + SysMemPage.Blit(SysMemPage, BUILDING_X, BUILDING_Y, 0, 0, 320-BUILDING_X,48); + shapenum = 0; // no damage + if (i >= 60) { + shapenum = Extract_Shape_Count(factptr) - 2; // some damage + if (i == 60) { + Shake_The_Screen(6); + Sound_Effect(VOC_CRUMBLE, VOL_FULL, 0); + } + if (i > 65) { + shapenum = Extract_Shape_Count(factptr) - 1; // mega damage + } + } + + /* + ** Draw the building before Rambo + */ + if (i < 68) { + CC_Draw_Shape(factptr,shapenum, 0, 0, WINDOW_MAIN, + SHAPE_FADING|SHAPE_WIN_REL, ScoreRemapBldg,Map.UnitShadow); + + } + + /* + ** Now draw some fires, if appropriate + */ + if (i >= 61) { + int firecount = Extract_Shape_Count(fball1ptr); + int shapeindex = (i-61)>>1; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 10, 10, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapFBall,0); + } + if (i > 64) { + shapeindex = (i-64)>>1; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 50, 30, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapFBall,0); + } + } + } + /* + ** Draw the Rambo character running away from the building + */ + CC_Draw_Shape(rmboptr, (ramboclass->DoControls[DO_WALK].Frame + ramboclass->DoControls[DO_WALK].Jump*6) + ((i>>1)%ramboclass->DoControls[DO_WALK].Count), + i+32, 40, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapYellow,Map.UnitShadow); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BUILDING_X, BUILDING_Y, 320-BUILDING_X,48); + + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + Blit_Hid_Page_To_Seen_Buff(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + //Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + + if (!Check_Key()) Call_Back_Delay(1); + } + + i = MAX(GBKilled, NBKilled); + i = MAX(i, CBKilled); + + for (int q = 0; q <= i; q++) { + Count_Up_Print( "%d", q, GBKilled,BUILDING_X + 8,BUILDING_Y ); + Count_Up_Print( "%d", q, NBKilled,BUILDING_X + 8,BUILDING_Y + 12); + Count_Up_Print( "%d", q, CBKilled,BUILDING_X + 8,BUILDING_Y + 24); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(1); + } + } +} + + +/*************************************************************************** + * DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen* + * * + * * + * * + * INPUT: yellowptr, redptr = pointers to shape file for graphs * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 BWG : Created. * + *=========================================================================*/ + +void ScoreClass::Do_GDI_Graph(void const * yellowptr, void const * redptr, int gkilled, int nkilled, int ypos) +{ + int i, max; + int gdikilled = gkilled, nodkilled=nkilled; + + max = MAX(gdikilled, nodkilled); + if (!max) max=1; + + gdikilled = (gdikilled * SIZEGBAR) / max; + nodkilled = (nodkilled * SIZEGBAR) / max; + if (max < 20) { + gdikilled = gkilled * 5; + nodkilled = nkilled * 5; + } + + max = MAX(gdikilled, nodkilled); + if (!max) max=1; + + // Draw the white-flash shape on the hidpage + Set_Logic_Page(SysMemPage); + SysMemPage.Fill_Rect(0,0, 124,9, TBLACK); + CC_Draw_Shape(redptr, 120, 0,0, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(PseudoSeenBuff); + + BlitList.Add (2* 297, 2* (ypos+2), 2* 297, 2* (ypos+2), 5*12 , 12); + + for (i = 1; i <= gdikilled; i++) { + if (i != gdikilled) { + CC_Draw_Shape(yellowptr,i, 172, ypos, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + } else { + SysMemPage.Blit(*PseudoSeenBuff,0,0, 172, ypos, 3+gdikilled,9); + } + + Count_Up_Print("%d", (i*gkilled) / max, gkilled, 297, ypos+2); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(2); + } + } + CC_Draw_Shape(yellowptr,gdikilled, 172,ypos , WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", gkilled, gkilled, 297, ypos+ 2); + if (!Check_Key()) Call_Back_Delay(40); + + BlitList.Add (2* 297, 2* (ypos+14), 2* 297, 2* (ypos+14), 5*12 , 12); + for (i = 1; i <= nodkilled; i++) { + if (i != nodkilled) { + CC_Draw_Shape(redptr, i, 172, ypos+12, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + } else { + SysMemPage.Blit(*PseudoSeenBuff,0,0, 172,ypos+12, 3+nodkilled,9); + } + + Count_Up_Print("%d", (i*nkilled) / max, nkilled, 297, ypos+14); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(2); + } + } + +// if (Keyboard::Check()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ + CC_Draw_Shape( redptr,nodkilled, 172,ypos+12, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", nkilled, nkilled, 297, ypos+14); + if (!Check_Key()) Call_Back_Delay(40); +} + + +void ScoreClass::Do_Nod_Casualties_Graph(void) +{ + int i, gdikilled, nodkilled, civkilled, max; + + void const * e1ptr = MixFileClass::Retrieve("E1.SHP"); + void const * c1ptr = MixFileClass::Retrieve("C1.SHP"); + + gdikilled = GKilled; + nodkilled = NKilled; + civkilled = CKilled; + max = MAX(gdikilled, nodkilled); + max = MAX(max, civkilled); + + if (!max) max=1; + if ((gdikilled > (MAX_BAR_X - BARGRAPH_X)) || (nodkilled > (MAX_BAR_X - BARGRAPH_X)) || (civkilled > (MAX_BAR_X - BARGRAPH_X))) { + gdikilled = (gdikilled * (MAX_BAR_X - BARGRAPH_X)) / max; + nodkilled = (nodkilled * (MAX_BAR_X - BARGRAPH_X)) / max; + civkilled = (civkilled * (MAX_BAR_X - BARGRAPH_X)) / max; + } + + max = MAX(gdikilled, nodkilled); + max = MAX(max, civkilled); + if (!max) max=1; + + /* + ** Initialize a bunch of objects for the infantrymen who pose for the bar + ** graphs of casualties. + */ + int q = NUMINFANTRYMEN/3; + int r = q*2; + for (i = 0; i < NUMINFANTRYMEN/3; i++) { + InfantryMan[i+0].xpos = + InfantryMan[i+q].xpos = + InfantryMan[i+r].xpos = (i*10) + 7; + InfantryMan[i+0].ypos = 11; + InfantryMan[i+q].ypos = 21; + InfantryMan[i+r].ypos = 31; + InfantryMan[i+0].shapefile = + InfantryMan[i+q].shapefile = e1ptr; + InfantryMan[i+r].shapefile = c1ptr; + InfantryMan[i+0].remap = ScoreRemapYellow; + InfantryMan[i+q].remap = ScoreRemapGrey; + InfantryMan[i+r].remap = RemapCiv; + InfantryMan[i+0].anim = + InfantryMan[i+q].anim = + InfantryMan[i+r].anim = 0; + InfantryMan[i+0].stage = + InfantryMan[i+q].stage = + InfantryMan[i+r].stage = 0; + InfantryMan[i+0].delay = + InfantryMan[i+q].delay = + InfantryMan[i+r].delay = Random() & 0x1F; + InfantryMan[i+0].Class = + InfantryMan[i+q].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + InfantryMan[i+r].Class = &InfantryTypeClass::As_Reference(INFANTRY_C1); + + } + + /* + ** Draw the infantrymen and pause briefly before running the graph + */ + Draw_InfantryMen(); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + //Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff, NULL); + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + Blit_Hid_Page_To_Seen_Buff(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + + Call_Back_Delay(40); + + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 2), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 2), 5*12 , 12); + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 14), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 14), 5*12 , 12); + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 26), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 26), 5*12 , 12); + + for (i = 1; i <= max; i++) { + // Draw & update infantrymen 3 times for every tick on the graph (i) + for (int q = 0; q < 3; q++) { + Draw_InfantryMen(); + Draw_Bar_Graphs(i, gdikilled, nodkilled, civkilled); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + + Count_Up_Print("%d", (i*GKilled) / max, GKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Count_Up_Print("%d", (i*NKilled) / max, NKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + Count_Up_Print("%d", (i*CKilled) / max, CKilled, SCORETEXT_X+64, CASUALTY_Y + 26); + if (!Check_Key()) Call_Back_Delay(3); + } + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + } + if (Check_Key()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ + Count_Up_Print("%d", GKilled, GKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Count_Up_Print("%d", NKilled, NKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + Count_Up_Print("%d", CKilled, CKilled, SCORETEXT_X+64, CASUALTY_Y + 26); + + /* + ** Finish up death animations, if there are any active + */ + int k = 1; + while (k) { + for (i=k=0; i= DO_GUN_DEATH) k=1; + if (k) Draw_InfantryMen(); + Draw_Bar_Graphs(max, gdikilled, nodkilled, civkilled); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(1); + } +} + + +void ScoreClass::Show_Credits(int house, char const pal[]) +{ + static int _credsx[2]={276,276}; + static int _credsy[2]={173,58}; + static int _credpx[2]={228,236}; +#ifdef GERMAN + static int _credpy[2]={181, 74}; + static int _credtx[2]={162,162}; + static int _credty[2]={173, 62}; +#else + static int _credpy[2]={189-12, 74}; + static int _credtx[2]={182,182}; + static int _credty[2]={179-12, 62}; +#endif + + int credobj,i; + int min,add; + + void const * credshape = MixFileClass::Retrieve("CREDS.SHP"); + + Alloc_Object(new ScorePrintClass(TXT_SCORE_ENDCRED, _credtx[house], _credty[house],pal)); + Call_Back_Delay(15); + + credobj = Alloc_Object(new ScoreCredsClass(_credsx[house], _credsy[house], credshape, 32, 2)); + min = PlayerPtr->Available_Money() / 100; + + /* + ** Print out total credits left at end of scenario + */ + i = -50; + + BlitList.Add (2* (_credpx[house]), 2* (_credpy[house]), 2* (_credpx[house]), 2* (_credpy[house]), 5*12 , 12); + + do { + add = 5; + if ((PlayerPtr->Available_Money() - i) > 100 ) add += 15; + if ((PlayerPtr->Available_Money() - i) > 1000) add += 30; + if (add < min) add = min; + i += add; + + if (i < 0) i=0; + + Set_Font_Palette(pal); + Count_Up_Print("%d", i, PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Call_Back_Delay(2); + if (Check_Key()) { + i=PlayerPtr->Available_Money() - 5; + Keyboard::Clear(); + } + } while (i < PlayerPtr->Available_Money()) ; + + // Make sure the credits object doesn't freeze on the white stage + while (((ScoreTimeClass *)ScoreObjs[credobj])->Stage >= 20 && !ControlQ) { + Call_Back_Delay(1); + } + delete ScoreObjs[credobj]; + ScoreObjs[credobj] = 0; +} + + +/*************************************************************************** + * SCORECLASS::PRINT_MINUTES -- Print out hours/minutes up to max * + * * + * Same as count-up-print, but for the time * + * * + * INPUT: current minute count and maximum * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void ScoreClass::Print_Minutes(int minutes) +{ + char str[20]; + if (minutes >= 60) { + if ((minutes/60) > 9) minutes = (9*60 + 59); + sprintf(str,Text_String(TXT_SCORE_TIMEFORMAT1), (minutes / 60),(minutes % 60)); + } else { + sprintf(str,Text_String(TXT_SCORE_TIMEFORMAT2), minutes); + } + TextPrintBuffer->Print(str, 275*2, 9*2, TBLACK, TBLACK); +} + + +/*********************************************************************************************** + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly. * + * * + * This routine prints out a number (like 70) or its maximum number, into a string, onto * + * the screen, on a clean section of the screen, and blits it forward to the seenpage so you* + * can print without flashing and can print over something (to count up %'s). * + * * + * INPUT: str = string to print into * + * percent = # to print * + * max = # to print if percent > max * + * xpos = x pixel coord * + * ypos = y pixel coord * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/07/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Count_Up_Print(char *str, int percent, int max, int xpos, int ypos) +{ + char destbuf[64]; + int width; + + sprintf(destbuf, str, percent <= max ? percent : max); + width = strlen(destbuf) * 7; + + + + +// HidPage.Blit(HidPage, xpos, ypos, 0, 0, width, 8); +// Set_Logic_Page(HidPage); +// LogicPage->Print( destbuf, 0, 0, WHITE, TBLACK); +// HidPage.Blit(SeenBuff, 0, 0, xpos, ypos, width, 8); + + TextPrintBuffer->Fill_Rect (xpos*2, ypos*2, (xpos + width)*2, (ypos+7)*2, BLACK); + TextPrintBuffer->Print (destbuf, xpos*2, ypos*2, WHITE, TBLACK); + + + //TextPrintBuffer->Blit(*TextPrintBuffer, xpos*2, ypos*2, 0, 0, width*2, 8*2); + //TextPrintBuffer->Print(destbuf, 0, 0, WHITE, TBLACK); + //TextPrintBuffer->Blit(SeenBuff, 0, 0, xpos*2, ypos*2, width*2, 8*2); + +// PseudoSeenBuff->Print( destbuf, xpos, ypos, TBLACK, BLACK); +// Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + +} + + +/*********************************************************************************************** + * ScoreClass::Input_Name -- Gets the name from the keyboard * + * * + * This routine handles keyboard input, and does a nifty zooming letter effect too. * + * * + * INPUT: str = string to put user's typing into * + * xpos = x pixel coord * + * ypos = y pixel coord * + * pal = text remapping palette to print using * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Input_Name(char str[], int xpos, int ypos, char const pal[]) +{ + int key, ascii, index=0; + + void const * keystrok = MixFileClass::Retrieve("KEYSTROK.AUD"); + + /* + ** Ready the hidpage so it can restore background under zoomed letters + */ + PseudoSeenBuff->Blit(SysMemPage); + + do { + Call_Back(); + Animate_Score_Objs(); + Animate_Cursor(index, ypos); + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale (PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + Blit_Hid_Page_To_Seen_Buff(); + + if (Check_Key()) { //if (Keyboard::Check()) { + key = Get_Key(); //key = Keyboard::Get(); + + if (index == MAX_FAMENAME_LENGTH-2) { + while (Check_Key()) { + Get_Key(); + } + } + + /* + ** If they hit 'backspace' when they're on the last letter, + ** turn it into a space instead. + */ + if ((key == KA_BACKSPACE) && (index == MAX_FAMENAME_LENGTH-2) ) { + if (str[index] && str[index]!=32) key = 32; + } + if (key == KA_BACKSPACE) { //if (key == KN_BACKSPACE) { + if (index) { + str[--index] = 0; + + int xposindex6 = xpos+(index*6); + + PseudoSeenBuff->Fill_Rect(xposindex6,ypos,xposindex6+6,ypos+6,TBLACK); + SysMemPage.Fill_Rect(xposindex6,ypos,xposindex6+6,ypos+6,TBLACK); + TextPrintBuffer->Fill_Rect(xposindex6*2,ypos*2,(xposindex6+6)*2,(ypos+6)*2, BLACK); + } + + } else if (key!=KA_RETURN) { //else if (key != KN_RETURN && key!=KN_KEYPAD_RETURN) { + ascii = key; //ascii = KN_To_KA(key); + if (ascii >= 'a' && ascii <= 'z') ascii -= ('a' - 'A'); +//if (ascii >='A' && ascii<='Z' || ascii == ' ') { +if ( (ascii >= '!' && ascii <= KA_TILDA) || ascii == ' ') { + PseudoSeenBuff->Fill_Rect(xpos + (index*6), ypos, xpos + (index*6)+6, ypos+5, TBLACK); + SysMemPage.Fill_Rect(xpos + (index*6), ypos, xpos + (index*6)+6, ypos+5, TBLACK); + TextPrintBuffer->Fill_Rect(2*(xpos + (index*6)), ypos*2, 2*(xpos + (index*6)+6), 2*(ypos+6), BLACK); + str[index] = ascii; + str[index+1] = 0; + + int objindex; + Play_Sample(keystrok, 255, Options.Normalize_Sound(255)); + objindex = Alloc_Object(new ScoreScaleClass(str+index,xpos+(index*6), ypos, pal)); + while (ScoreObjs[objindex]) Call_Back_Delay(1); + + if (index < (MAX_FAMENAME_LENGTH-2) ) index++; + } + } + } + } while(key!=KA_RETURN); // } while(key != KN_RETURN && key!=KN_KEYPAD_RETURN); +} + + +void Animate_Cursor(int pos, int ypos) +{ + static int _lastpos, _state; + static CountDownTimerClass _timer; + + ypos += 7; // move cursor to bottom of letter + + // If they moved the cursor, erase old one and force state=0, to make green draw right away + if (pos != _lastpos) { + PseudoSeenBuff->Draw_Line(HALLFAME_X + (_lastpos*6),ypos, HALLFAME_X + (_lastpos*6) + 5, ypos, TBLACK); + TextPrintBuffer->Fill_Rect(2*(HALLFAME_X + (_lastpos*6)),2*ypos, 2*(HALLFAME_X + (_lastpos*6) + 5), 2*ypos+1, BLACK); + _lastpos = pos; + _state = 0; + } + + PseudoSeenBuff->Draw_Line(HALLFAME_X + (pos*6),ypos, HALLFAME_X + (pos*6)+5, ypos, _state ? LTBLUE : TBLACK); + TextPrintBuffer->Fill_Rect(2*(HALLFAME_X + (pos*6)),2*ypos, 2*(HALLFAME_X + (pos*6)+5), 2*ypos+1, _state ? LTBLUE : BLACK); + + /* + ** Toggle the color of the cursor, green or black, if it's time to do so. + */ + if (!_timer.Time()) { + _state ^= 1; + _timer.Set(5); + } +} + + +/*************************************************************************** + * Draw_InfantryMen -- Draw all the guys on the score screen * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMen() +{ + int k; + +// Only draw the infantrymen if we're playing Nod... GDI wouldn't execute +// people like that. + + /* + ** First restore the background + */ + SysMemPage.Blit(SysMemPage, BARGRAPH_X, CASUALTY_Y, 0, 0, 320-BARGRAPH_X, 34); + Set_Logic_Page(SysMemPage); + + /* + ** Then draw all the infantrymen on the clean SysMemPage + */ + for (k = 0; k < NUMINFANTRYMEN; k++) Draw_InfantryMan(k); + /* + ** They'll all be blitted over to the seenpage after the graphs are drawn + */ +} + +/*************************************************************************** + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * * + * This routine draws one of the infantrymen in the "Casualties" area * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMan(int index) +{ + int stage; + + /* If the infantryman's dead, just abort this function */ + if (InfantryMan[index].anim == -1) return; + + stage = InfantryMan[index].stage + InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Frame; + + CC_Draw_Shape(InfantryMan[index].shapefile, + stage, + InfantryMan[index].xpos, + InfantryMan[index].ypos, + WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + InfantryMan[index].remap, + Map.UnitShadow); + /* + ** see if it's time to run a new anim + */ + if (--InfantryMan[index].delay < 0) { + InfantryMan[index].delay = 3; + if (++InfantryMan[index].stage >= InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Count) { + + /* + ** was he playing a death anim? If so, and it's done, erase him + */ + if (InfantryMan[index].anim >= DO_GUN_DEATH) { + InfantryMan[index].anim = -1; + } else { + New_Infantry_Anim(index, DO_STAND_READY); + } + } + } +} + + +/*************************************************************************** + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen* + * * + * * + * * + * INPUT: index: which of the 30 infantrymen to affect * + * anim: which animation sequence to start him into * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void New_Infantry_Anim(int index, int anim) +{ + InfantryMan[index].anim = anim; + InfantryMan[index].stage = 0; + if (anim >= DO_GUN_DEATH) { + InfantryMan[index].delay = 1; // start right away + } else { + InfantryMan[index].delay = Random() & 15; + } +} + + +/*************************************************************************** + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * * + * * + * * + * INPUT: i = current count of how far to draw graph * + * gkilled = # of GDI forces killed (adjusted to fit in space) * + * nkilled = # of Nod forces killed (adjusted to fit in space) * + * ckilled = # of civilians killed (adjusted to fit in space) * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_Bar_Graphs(int i, int gkilled, int nkilled, int ckilled) +{ + if (gkilled) { + LogicPage->Fill_Rect(0, 0+4, 0+MIN(i,gkilled), 0+5, LTCYAN); + LogicPage->Draw_Line(0+1, 0+6, 0+MIN(i,gkilled)+1, 0+6, TBLACK); + LogicPage->Draw_Line(0+MIN(i,gkilled)+1,0+5, 0+MIN(i,gkilled)+1, 0+5, TBLACK); + if (i <= gkilled) { + int anim = InfantryMan[i/11].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(i/11,DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim(i/11,DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + if (nkilled) { + LogicPage->Fill_Rect(0, 0+16, 0+MIN(i,nkilled), 0+17, RED); + LogicPage->Draw_Line(0+1, 0+18, 0+MIN(i,nkilled)+1, 0+18, TBLACK); + LogicPage->Draw_Line(0+MIN(i,nkilled)+1, 0+17, 0+MIN(i,nkilled)+1, 0+17, TBLACK); + if (i <= nkilled) { + int anim = InfantryMan[(NUMINFANTRYMEN/3)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim((NUMINFANTRYMEN/3)+(i/11),DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim((NUMINFANTRYMEN/3)+(i/11),DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + + if (ckilled) { + LogicPage->Fill_Rect(0, 0+28, 0+MIN(i,ckilled), 0+29, RED); + LogicPage->Draw_Line(0+1, 0+30, 0+MIN(i,ckilled)+1, 0+30, TBLACK); + LogicPage->Draw_Line(0+MIN(i,ckilled)+1, 0+29, 0+MIN(i,ckilled)+1, 0+29, TBLACK); + if (i <= ckilled) { + int anim = InfantryMan[((NUMINFANTRYMEN*2)/3)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(((NUMINFANTRYMEN*2)/3)+(i/11),DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim(((NUMINFANTRYMEN*2)/3)+(i/11),DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } +} + + +/*************************************************************************** + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * * + * This is just to cut down on code size and typing a little. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Call_Back_Delay(int time) +{ + CountDownTimerClass cd; + + if (!ControlQ) { + if (Keyboard::Down(KN_LCTRL) && Keyboard::Down(KN_Q)) { + ControlQ = 1; + Keyboard::Clear(); + } + } + if (ControlQ) time=0; + + cd.Set(time); + StreamLowImpact = true; + do { + Call_Back(); + //Animate_Score_Objs(); + //Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff ,NULL); + + //if (Get_Mouse_State()){ + // Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff ,NULL); + //BlitList.Update(); + //}else{ + Animate_Score_Objs(); + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + Blit_Hid_Page_To_Seen_Buff(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + //} + } while(cd.Time()); + StreamLowImpact = false; +} + + +void Animate_Score_Objs() +{ + StillUpdating = false; + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]) { + ScoreObjs[i]->Update(); + } + } +} + +char *Int_Print(int a) +{ + static char str[10]; + + sprintf(str,"%d",a); + return str; +} + + +/*********************************************************************************************** + * Multi_Score_Presentation -- Multiplayer routine to display score screen. * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 BWG: Created. * + *=============================================================================================*/ +void Multi_Score_Presentation(void) +{ + static unsigned char const _cycleyellowpal[]={0x0,0xec,0xEb,0xea,0xE9,0xe9,0xE9,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; + + static unsigned char const _greenpal[]= {0x0,0x12,0x14,0x16,0x18,0x18,0x18,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x10,0x0}; + static unsigned char const _redpal[]= {0x0,0x22,0x24,0x26,0x28,0x28,0x28,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x20,0x0}; + static unsigned char const _graypal[]= {0x0,0xca,0xCb,0xcc,0xCd,0xcd,0xCd,0x0,0xCD,0x0,0x0,0x0,0x0,0x0,0xC8,0x0}; + static unsigned char const _orangepal[]={0x0,0xd1,0xD2,0xd3,0xD4,0xd4,0xD4,0x0,0xD4,0x0,0x0,0x0,0x0,0x0,0xD0,0x0}; + static unsigned char const _bluepal[]= {0x0,0x2,0x0a,0xb,0x0b,0xb,0x0B,0x0,0x0B,0x0,0x0,0x0,0x0,0x0,0x09,0x0}; + static unsigned char const _yellowpal[]={0x0,0x5,0xee,0xf1,0xf2,0xf2,0xF2,0xf2,0xF2,0x0,0x0,0x0,0x0,0x0,0x7D,0x0}; + + //static char const _greenpal[]= {0x0,0x0,0x12,0x0,0x14,0x0,0x16,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x10,0x0}; + //static char const _redpal[]= {0x0,0x0,0x22,0x0,0x24,0x0,0x26,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x20,0x0}; + //static char const _graypal[]= {0x0,0x0,0xCA,0x0,0xCB,0x0,0xCC,0x0,0xCD,0x0,0x0,0x0,0x0,0x0,0xC8,0x0}; + //static char const _orangepal[]={0x0,0x0,0xD1,0x0,0xD2,0x0,0xD3,0x0,0xD4,0x0,0x0,0x0,0x0,0x0,0xD0,0x0}; + //static char const _bluepal[]= {0x0,0x0,0x02,0x0,0x0A,0x0,0x0B,0x0,0x0B,0x0,0x0,0x0,0x0,0x0,0x09,0x0}; + //static char const _yellowpal[]={0x0,0x0,0x05,0x0,0xEE,0x0,0xF1,0x0,0xF2,0x0,0x0,0x0,0x0,0x0,0x7D,0x0}; + + static unsigned char const *_colors[]={_yellowpal,_redpal,_bluepal,_orangepal,_greenpal,_graypal}; + + int i,k; + void *oldfont, *anim; + int oldfontxspacing = FontXSpacing; + char const *pal; + + + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_WIN1); + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height() ,(void*)NULL); + BlitList.Clear(); + + SysMemPage.Clear(); + PseudoSeenBuff->Clear(); + HiddenPage.Clear(); + TextPrintBuffer->Clear(); + + Set_Palette(BlackPalette); + + anim = Open_Animation("MLTIPLYR.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + Hide_Mouse(); + + /* + ** Display the background animation + */ + VisiblePage.Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Increase_Palette_Luminance (Palette , 30,30,30,63); + Animate_Frame(anim, *PseudoSeenBuff, 1); + Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , "MULTSCOR.PAL"); + Fade_Palette_To(Palette, FADE_PALETTE_FAST, Call_Back); + + int frame = 1; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, *PseudoSeenBuff, frame++); + Call_Back_Delay(2); + } + Close_Animation(anim); + + /* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + + Set_Logic_Page(*PseudoSeenBuff); + + /* + ** Move all the scores over a notch if there's more games than can be + ** shown (which is known by MPlayerCurGame == MAX_MULTI_GAMES-1); + */ + if (MPlayerCurGame == MAX_MULTI_GAMES-1) { + for(i = 0; i < MAX_MULTI_NAMES; i++) { + for(k = 0; k < MAX_MULTI_GAMES-1; k++) { + MPlayerScore[i].Kills[k] = MPlayerScore[i].Kills[k+1]; + } + } + } + + int y = 41; + for(i = 0; i < MAX_MULTI_NAMES; i++) { + if (strlen(MPlayerScore[i].Name)) { + pal = (const char*)_colors[MPlayerScore[i].Color]; + + Alloc_Object(new ScorePrintClass(MPlayerScore[i].Name, 15, y,pal)); + Call_Back_Delay(20); + + Alloc_Object(new ScorePrintClass(Int_Print(MPlayerScore[i].Wins), 118, y, pal)); + Call_Back_Delay(6); + + for(k = 0; k <= MIN(MPlayerCurGame, MAX_MULTI_GAMES-2); k++) { + if (MPlayerScore[i].Kills[k] >= 0) { + Alloc_Object(new ScorePrintClass(Int_Print(MPlayerScore[i].Kills[k]), 225+(24*k), y, pal)); + Call_Back_Delay(6); + } + } + y += 12; + } + } + +#if (FRENCH) + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 90 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 185, _cycleyellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 109 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 185, _cycleyellowpal)); +#endif + Cycle_Wait_Click(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + Theme.Queue_Song(THEME_NONE); + + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + Set_Palette(GamePalette); + + Set_Logic_Page (SeenBuff); + + delete PseudoSeenBuff; + delete TextPrintBuffer; + BlitList.Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + Show_Mouse(); +} + +#ifdef WRITE_LBM + // A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size +typedef struct { + unsigned short w, h; // raster width & height in pixels + unsigned short x, y; // position for this image + unsigned char planes; // # source bitplanes + unsigned char masking; // masking technique + unsigned char compression; // compression algoithm + unsigned char pad1; // UNUSED. For consistency, put 0 here. + unsigned short transcolor; // transparent "color number" + unsigned char xaspect, yaspect; // aspect ratio, a rational number x/y + unsigned short pagewidth, pageheight; // source "page" size in pixels +} BitMapHeaderType; + +// All values in LocalHeader are always the same except planes. This is set in Write_BMHD +// the WORD values must be in low-high order for compatibility. + +static BitMapHeaderType LocalHeader = { + 0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask + 1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect + 0x4001, 0xC800 }; // pagewidth, pageheight + +#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L) + +/*=========================================================================*/ +/* The following static functions are in this file: */ +/*=========================================================================*/ + +static long CCWrite_BMHD(CCFileClass &lbmhandle, short bitplanes); +static long CCWrite_CMAP(CCFileClass &lbmhandle, unsigned char * palette, short bitplanes); +static long CCWrite_BODY(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes); +static long CCWrite_Row(CCFileClass &lbmhandle, unsigned char *buffer); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * CCWrite_LBM_File -- Writes out a file in LBM format * + * * + * INPUT: short lbmhandle -- lbm file handle already opened by caller * + * BufferClass buff -- buff where MCGA picture is * + * short bitplane -- number of bitplanes to convert to * + * char *palette -- pointer to palette for buff * + * * + * OUTPUT: Returns bool -- successfull or not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/18/1991 SB : Created. * + *=========================================================================*/ +bool CCWrite_LBM_File(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes, unsigned char *palette) +{ + long filesize; + + + lbmhandle.Seek(0L, SEEK_SET); // goto beginning of file + + lbmhandle.Write("FORM????ILBM", 12L); // First 12 bytes of all .lbm files + // size is unkown so write ???? + filesize = 12L; // 4 bytes for "ILBM" + + filesize += CCWrite_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size) + filesize += CCWrite_CMAP(lbmhandle, palette, bitplanes); // write out CMAP + + // Write out the body, or compressed picture image. This size will depend + // on the compression, but the value passed back is what the compressor + // assumed was written to file + + filesize += CCWrite_BODY(lbmhandle, buff, bitplanes); + + // Verify that we were able to write out the file without running out of space + if (lbmhandle.Seek(0L, SEEK_END) != filesize) { + return(false); + } + + lbmhandle.Seek(4L, SEEK_SET); // goto beginning of file + filesize = Reverse_LONG(filesize - 8L); // - 8 because of "FORM" + short (size) + lbmhandle.Write( (char *) &filesize, 4L); // patch in filesize + + return(true); +} + + +/*************************************************************************** + * CCWrite_BMHD -- writes out the bit map header (LocalHeader) * + * * + * INPUT: short lbmhandle -- file handle for lbm file * + * short pitplanes -- number of bitplanes to write out * + * * + * OUTPUT: LONG number of bytes hopefully written out to .LBM file * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_BMHD(CCFileClass &lbmhandle, short bitplanes) +{ + long size; + + lbmhandle.Write("BMHD", 4L); // write out chunk title + size = Reverse_LONG(sizeof(LocalHeader)); // write out size of LocalHeader chunk + lbmhandle.Write((char *) &size, 4L); + + LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader + + // Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure + + return(lbmhandle.Write((char *) &LocalHeader, + (sizeof(LocalHeader) + 1) & 0xFFFE) + 8L); +} + + +/*************************************************************************** + * CCWrite_CMAP -- Writes out CMAP (palette) information * + * * + * * + * INPUT: short lbmhandle -- file handle of lbm file * + * char * palette -- pointer to paletter information * + * short bitplanes -- used to figure out size of palette * + * * + * OUTPUT: long number of bytes that should have been written out to .LBM. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_CMAP(CCFileClass &lbmhandle, unsigned char * palette, short bitplanes) +{ + short color, r, g, b, colors; + long size; + unsigned char *pal_ptr; + char rgb[3]; + + + lbmhandle.Write("CMAP", 4L); // write out palette info + colors = 1 << bitplanes; // colors = 2 to the bitplanes + size = Reverse_LONG(colors * 3L); // size = colors * 3 guns + + lbmhandle.Write((char *) &size, 4L); + + for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color + + if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values + r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM + } // so LBM_val = (DP_val * 4) | 3 if DP_val != 0 + if ((g = *pal_ptr++) != 0) { + g = (g << 2) | 0x03; + } + if ((b = *pal_ptr++) != 0) { + b = (b << 2) | 0x03; + } + rgb[0] = r; // assign gun values to an array to write out + rgb[1] = g; + rgb[2] = b; + + lbmhandle.Write(rgb, 3L); + } + // size = colors * 3 + return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + short (size) +} + + +/*************************************************************************** + * CCWrite_Body -- writes out compressed data in an LBM file * + * * + * INPUT: short lbmhandle -- file handle of lbm file * + * * + * OUTPUT: long - number of byte written * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_BODY(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes) +{ + long bodysize = 0; + long actualsize; + long size; + short planebit; + short line, plane; + unsigned char buffer[40]; + unsigned char *buffptr; + + lbmhandle.Write("BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize + + buffptr = (unsigned char *) buff.Get_Buffer(); // point to beginning of buff + + for (line = 0; line < 200; line++) { + planebit = 1; // start with bit 1 set + + for (plane = 0; plane < bitplanes; plane++) { + Pack_2_Plane(buffer, buffptr, planebit); // convert to planar + bodysize += CCWrite_Row(lbmhandle, buffer); // write to to the BODY in the LBM + + planebit <<= 1; // set next bit + } + + buffptr += 320; // row size is 320 + } + + actualsize = bodysize + (bodysize&0x01); + + if (actualsize != bodysize) { + lbmhandle.Write(buffer, 1); // Padd the block. + } + + lbmhandle.Seek( -(actualsize + 4L), SEEK_CUR); // Patch in chunksize + size = Reverse_LONG(bodysize); + lbmhandle.Write( (char *) &size ,4L); + + return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes +} + + +/*************************************************************************** + * CCWrite_Row -- compresses and writes a row plane to .lbm file * + * * + * INPUT: short lbmhandle -- lbm file handle * + * unsigned char *buffer -- pointer to buffer to be written out * + * * + * OUTPUT: long size of chunk that should have been written out * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_Row(CCFileClass &lbmhandle, unsigned char *buffer) +{ + short i; + short chunksize = 0; + short dataLength = 40; // 320 rows / 8 ( 1 plane per row) + unsigned char repCode, current, curr_plus_2; + unsigned char *buffptr; + + while (dataLength) { + + // If at least 2 more bytes and they are equal, then replicate + + if ((dataLength >= 2) && (buffer[0] == buffer[1])) { + buffptr = buffer; + for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) { + if (*buffptr != buffptr[1]) { + break; + } + buffptr++; + } + i++; + repCode = -i + 1; + lbmhandle.Write(&repCode, 1L); // Write count as -count+1 + lbmhandle.Write(buffer, 1L); // Write byte to replicate + buffer += i; + dataLength -= i; + chunksize += 2; + + } else { // Copy literally till 3 byte run or two 2 byte runs found + + for (i = 0; (i <= 128) && (i < dataLength); i++) { + current = buffer[i]; + curr_plus_2 = buffer[i + 2]; + + if (i == dataLength - 1) + continue; + if (current != buffer[i + 1]) + continue; + if (i == dataLength - 2) + continue; + if (current == curr_plus_2) + break; + if (i == dataLength - 3) + continue; + if (curr_plus_2 == buffer[i + 3]) + break; + } + repCode = i - 1; + lbmhandle.Write(&repCode, 1L); // Write count as count-1 + lbmhandle.Write(buffer, (long) i); // Write 'count' bytes + buffer += i; + dataLength -= i; + chunksize += i + 1; + } + } // end while + + return(chunksize); +} +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/SCORE.H b/TIBERIANDAWN/SCORE.H new file mode 100644 index 000000000..55fd22afa --- /dev/null +++ b/TIBERIANDAWN/SCORE.H @@ -0,0 +1,156 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\score.h_v 2.18 16 Oct 1995 16:46:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCORE_H +#define SCORE_H + +#include "unit.h" +#include "building.h" + +class ScoreClass { + public: + int Score; + int NKilled; + int GKilled; + int CKilled; + int NBKilled; + int GBKilled; + int CBKilled; + int NHarvested; + int GHarvested; + int CHarvested; + unsigned long ElapsedTime; + + void Init(void) {memset(this, 0, sizeof(ScoreClass));}; + void Presentation(void); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + protected: + + private: + unsigned char *ChangingGun; + + void ScoreDelay(int ticks); + void Pulse_Bar_Graph(void); + void Print_Graph_Title(int,int); + void Print_Minutes(int minutes); + void Count_Up_Print(char *str, int percent, int max, int xpos, int ypos); + void Show_Credits(int house, char const pal[]); + void Do_GDI_Graph(void const * yellowptr, void const * redptr, int gdikilled, int nodkilled, int ypos); + void Do_Nod_Casualties_Graph(void); + void Do_Nod_Buildings_Graph(void); + void Input_Name(char str[], int xpos, int ypos, char const pal[]); +}; + +class ScoreAnimClass { + public: + ScoreAnimClass(int x, int y, void const * data); + int XPos; + int YPos; + CountDownTimerClass Timer; + void const * DataPtr; + virtual void Update(void) {} ; + virtual ~ScoreAnimClass(void) {} ; +}; + +class ScoreCredsClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + void const * CashTurn; + void const * Clock1; + + virtual void Update(void); + ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreCredsClass(void) {}; +}; + +class ScoreTimeClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + virtual void Update(void); + ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreTimeClass(void) {}; +}; + +class ScorePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + ScorePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~ScorePrintClass(void) {}; +}; + + +class MultiStagePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + MultiStagePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + MultiStagePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~MultiStagePrintClass(void) {}; +}; + + +class ScoreScaleClass : public ScoreAnimClass { + public: + int Stage; + char const * Palette; + virtual void Update(void); + ScoreScaleClass(void const * data, int xpos, int ypos, char const pal[]); + virtual ~ScoreScaleClass(void) {}; + +}; + +#define MAXSCOREOBJS 8 +extern ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + +void Multi_Score_Presentation(void); + +#endif diff --git a/TIBERIANDAWN/SCREEN.H b/TIBERIANDAWN/SCREEN.H new file mode 100644 index 000000000..dddb05a26 --- /dev/null +++ b/TIBERIANDAWN/SCREEN.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\screen.h_v 2.15 16 Oct 1995 16:47:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 2, 1994 * + * * + * Last Update : June 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCREEN_H +#define SCREEN_H + + +class ScreenClass +{ + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseShapeType CurrentMouseShape; + MouseShapeType NormalMouseShape; + + public: + + ScreenClass(void) { + CurrentMouseShape = SHP_NONE; + NormalMouseShape = SHP_MOUSE; + }; + + + Init(void); + Set_Default_Mouse(MouseShapeType mouse); + Force_Mouse_Shape(MouseShapeType mouse); + + unsigned char *GamePalette; + unsigned char *BlackPalette; +}; + +#endif diff --git a/TIBERIANDAWN/SCROLL.CPP b/TIBERIANDAWN/SCROLL.CPP new file mode 100644 index 000000000..0ea9f48b6 --- /dev/null +++ b/TIBERIANDAWN/SCROLL.CPP @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\scroll.cpv 2.18 16 Oct 1995 16:49:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/08/95 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ScrollClass::AI -- Handles scroll AI processing. * + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#define SCROLL_DELAY 1 + +CountDownTimerClass ScrollClass::Counter; + + +/*********************************************************************************************** + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * * + * This is the constructor for the scroll class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +ScrollClass::ScrollClass(void) +{ + IsAutoScroll = true; + Counter.Set(SCROLL_DELAY); + Inertia = 0; + Counter.Start(); +} + + +/*********************************************************************************************** + * ScrollClass::AI -- Handles scroll AI processing. * + * * + * This routine is called every game frame for purposes of input processing. * + * * + * INPUT: input -- Reference to the keyboard/mouse event that just occurred. * + * * + * x,y -- The mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + * 08/10/1995 JLB : Revamped for free smooth scrolling. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +void ScrollClass::AI(KeyNumType &input, int x, int y) +{ +#if 0 + static DirType direction; + bool player_scrolled=false; + + /* + ** If rubber band mode is in progress, then don't allow scrolling of the tactical map. + */ + if (!IsRubberBand /*&& !IsTentative*/) { + + /* + ** Special check to not scroll within the special no-scroll regions. + */ + bool noscroll = false; + if (Special.IsScrollMod && y == 0 && ((x > 3 && x < EVA_WIDTH) || (x > SeenBuff.Get_Width()-EVA_WIDTH && x < SeenBuff.Get_Width()-3))) { + noscroll = true; + } + + if (!noscroll) { + + /* + ** Verify that the mouse is over a scroll region. + */ + if (Inertia || y == 0 || x == 0 || x == (SeenBuff.Get_Width()-1) || y == (SeenBuff.Get_Height()-1)) { + + if (y == 0 || x == 0 || x == (SeenBuff.Get_Width()-1) || y == (SeenBuff.Get_Height()-1)) { + + player_scrolled=true; + /* + ** Adjust the mouse coordinates to emphasise the + ** cardinal directions over the diagonals. + */ + int altx = x; + if (altx < 50) altx -= (50-altx)*2; + altx = MAX(altx, 0); + if (altx > (SeenBuff.Get_Width()-50)) altx += (altx-(SeenBuff.Get_Width()-50))*2; + altx = MIN(altx, SeenBuff.Get_Width()); + if (altx > 50 && altx < (SeenBuff.Get_Width()-50)) { + altx += ((SeenBuff.Get_Width()/2)-altx)/2; + } + + int alty = y; + if (alty < 50) alty -= (50-alty); + alty = MAX(alty, 0); + if (alty > (SeenBuff.Get_Height()-50)) alty += ((alty-(SeenBuff.Get_Height()-50))); + alty = MIN(alty, SeenBuff.Get_Height()); + + direction = (DirType)Desired_Facing256((SeenBuff.Get_Width())/2, (SeenBuff.Get_Height())/2, altx, alty); + } + int control = Dir_Facing(direction); + + /* + ** The mouse is over a scroll region so set the mouse shape accordingly if the map + ** can be scrolled in the direction indicated. + */ + static int _rate[9] = { + 0x01C0, + 0x0180, + 0x0140, + 0x0100, + 0x00C0, + 0x0080, + 0x0040, + 0x0020, + 0x0010 + }; + + int rate = 8-Inertia; + + if (rateDraw_Stamp(ptr, w + (h*Width), x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, NULL, WINDOW_TACTICAL); + } + } + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * * + * This routine adds smudge objects to the list of objects that the scenario editor can * + * place upon the ground. It is only called from the scenario editor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Prep_For_Add(void) +{ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * * + * This routine will, in one motion, create a smudge object and place it upon the map. * + * Since placing a smudge on the map will destroy the object, this routine will leave the * + * smudge object count unchanged. Typically, this routine is used by the scenario editor * + * for creating smudges and placing them on the map. * + * * + * INPUT: cell -- The cell to place the smudge object. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new SmudgeClass(Type, Cell_Coord(cell))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * * + * This routine will create a smudge object of the appropriate type. Smudge objects are * + * transitory in nature. They exist only from the point of creation until they are given * + * a spot on the map to reside. At that time the map data is updated and the smudge * + * object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a created smudge object. If none could be created, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * SmudgeTypeClass::Create_One_Of(HouseClass *) const +{ + return(new SmudgeClass(Type, -1)); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * * + * This routine will draw the smudge overlay image at the coordinate (upper left) * + * specified. The underlying terrain icon is presumed to have already been rendered. * + * * + * INPUT: x,y -- Coordinate of the upper left corner of icon to render the smudge object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Draw_It(int x, int y, int data) const +{ + void const * ptr = Get_Image_Data(); + if (ptr) { + IsTheaterShape = true; // Smudges are theater specific + CC_Draw_Shape(ptr, data, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + IsTheaterShape = false; +// LogicPage->Draw_Stamp(ptr, data, x, y, NULL, WINDOW_TACTICAL); + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::One_Time(void) +{ +} + diff --git a/TIBERIANDAWN/SEQCONN.CPP b/TIBERIANDAWN/SEQCONN.CPP new file mode 100644 index 000000000..ea9ffee0b --- /dev/null +++ b/TIBERIANDAWN/SEQCONN.CPP @@ -0,0 +1,561 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.9 16 Oct 1995 16:51:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * SequencedConnClass::SequencedConnClass -- class constructor * + * SequencedConnClass::~SequencedConnClass -- class destructor * + * SequencedConnClass::Init -- Initializes connection queue to empty * + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * SequencedConnClass::Receive_Packet -- adds packet to receive queue * + * SequencedConnClass::Get_Packet -- gets a packet from receive queue * + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * SequencedConnClass::Service_Receive_Queue -- services recieve queue * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +// PG_TO_FIX +#if (0) + +/*************************************************************************** + * SequencedConnClass::SequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::SequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); + +} /* end of SequencedConnClass */ + + +/*************************************************************************** + * SequencedConnClass::~SequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::~SequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * SequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void SequencedConnClass::Init (void) +{ + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { + NumSendAck++; + } else { + NumSendNoAck++; + } + return(true); + + } else { + return(false); + } +} + + +/*************************************************************************** + * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *entry_data; // ptr to queue entry data + int save_packet = 1; // 0 = this is a resend, or + // out-of-order packet + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber!=MagicNum) + return(false); + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Code) { + /*..................................................................... + DATA: If this is a No-Ack packet, always save it. Otherwise, only + save it if it's received in the proper sequence. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + if (packet->Code == PACKET_DATA_NOACK) { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); +#endif + save_packet = 1; + } else { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); +#endif + if ((packet->PacketID == NumRecAck)) { + save_packet = 1; + } else { + save_packet = 0; + /*............................................................... + If this is a resend of our next-available received message, it + means the other app didn't get our ACK, so mark it as + non-acknowledged to tell Service_Receive_Queue to send an ACK. + ...............................................................*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + if (entry_data->PacketID==packet->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + rec_entry->IsACK = 0; +#ifdef DEBUG_SEQ +printf("(Resend)\n"); +#endif + } + } + } + } + + /* + ...................... queue this packet ........................ + */ + if (save_packet) { + if (!Queue->Queue_Receive(buf, buflen)) { + return(false); + } + if (packet->Code == PACKET_DATA_ACK) { + NumRecAck++; + } else { + NumRecNoAck++; + } + } + break; + + /*..................................................................... + ACK: If this ACK is for the latest-sent packet, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: +#ifdef DEBUG_SEQ +printf("ACK received (%d)\n",packet->PacketID); +#endif + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Next_Send(); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry!=NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Next_Receive(); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + + return(false); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Send_Queue (void) +{ + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + + /*------------------------------------------------------------------------ + - If the next packet is ACK'd remove it from the queue + - If the next packet isn't ACK'd, [re-]send it + ------------------------------------------------------------------------*/ + /* + ......................... Get ptr to data to send ........................ + */ + send_entry = Queue->Next_Send(); + if (send_entry==NULL) + return(true); + + /* + ------------------ If ACK has been received, unqueue it ------------------ + */ + if (send_entry->IsACK) { + + /* + .................. Update this queue's response time .................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /* + ......................... unqueue the packet .......................... + */ +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Send(NULL,NULL); + } else { + + /* + ----------------- ACK not received yet, [re-]send packet ----------------- + */ + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ + Send (send_entry->Buffer, send_entry->BufLen); + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) + send_entry->IsACK = 1; + } + +#ifdef DEBUG_SEQ +packet_hdr = (CommHeaderType *)send_entry->Buffer; +if (packet_hdr->Code == PACKET_DATA_NOACK) { + printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); +} else { + printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); +} +#endif + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) + return(false); + if (Timeout != -1 && send_entry->LastTime - + send_entry->FirstTime > Timeout) + return(false); + + } + } + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Receive_Queue (void) +{ + CommHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry==NULL) + return(true); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { +#ifdef DEBUG_SEQ +printf("Sending ACK (%d)\n",packet_hdr->PacketID); +#endif + ackpacket.MagicNumber = MagicNum; + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet_hdr->PacketID; + + Send((char *)&ackpacket, sizeof(CommHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) { +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Receive(NULL,NULL); + } + + return(true); +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/SEQCONN.H b/TIBERIANDAWN/SEQCONN.H new file mode 100644 index 000000000..a6884e480 --- /dev/null +++ b/TIBERIANDAWN/SEQCONN.H @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.h_v 1.12 16 Oct 1995 16:47:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Sequenced" ACK/Retry approach to packet * + * transmission. It waits until the last packet has been ACK'd before * + * sending another packet. Thus, it guarantees order of delivery of * + * packets, but its performance will be slower than the Non-Sequenced * + * approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SEQCONN_H +#define SEQCONN_H + +#include "connect.h" +#include "comqueue.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class SequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + SequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~SequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet queue. + .....................................................................*/ + CommQueueClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + +}; + +#endif +/*************************** end of seqconn.h ******************************/ diff --git a/TIBERIANDAWN/SESSION.H b/TIBERIANDAWN/SESSION.H new file mode 100644 index 000000000..74342823e --- /dev/null +++ b/TIBERIANDAWN/SESSION.H @@ -0,0 +1,551 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + * The purpose of this class is to contain those variables & routines * + * specifically related to a multiplayer game. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SESSION_H +#define SESSION_H + +#include "ipxaddr.h" +#include "msglist.h" +#include "connect.h" + +//--------------------------------------------------------------------------- +// Forward declarations +//--------------------------------------------------------------------------- +class AircraftClass; +class AnimClass; +class BuildingClass; +class BulletClass; +class InfantryClass; +class UnitClass; +class PhoneEntryClass; +class CellClass; + +//--------------------------------------------------------------------------- +// Defines +//--------------------------------------------------------------------------- +//........................................................................... +// Various limiting values +//........................................................................... +#define MAX_PLAYERS 6 // max # of players we can have +#define MPLAYER_BUILD_LEVEL_MAX 7 // max build level in multiplay +#define MAX_MPLAYER_COLORS 6 // max # of colors + +//........................................................................... +// Max sizes of packets we want to send +// The IPX packet's size is IPX's max size (546), rounded down to accommodate +// the max number of events possible. +//........................................................................... +#define MAX_IPX_PACKET_SIZE (((546 - sizeof(CommHeaderType)) / \ + sizeof(EventClass) ) * sizeof(EventClass)) +#define MAX_SERIAL_PACKET_SIZE 200 + +//........................................................................... +// Max length of player names fields; attempt to use the constant for the +// HouseClass, if it's been defined; otherwise, define it myself. +//........................................................................... +#ifdef HOUSE_NAME_MAX +#define MPLAYER_NAME_MAX HOUSE_NAME_MAX +#else +#define MPLAYER_NAME_MAX 12 // max length of a player's name +#endif + +//........................................................................... +// Values to control the multiplayer score screen +//........................................................................... +#define MAX_MULTI_NAMES 8 // max # names (rows) on the score screen +#define MAX_MULTI_GAMES 4 // max # games (columns) on the score screen + +//........................................................................... +// Min value for MaxAhead, for both net & modem; only applies for +// COMM_PROTOCOL_MULTI_E_COMP. +//........................................................................... +#define MODEM_MIN_MAX_AHEAD 5 +#define NETWORK_MIN_MAX_AHEAD 2 + +//........................................................................... +// Send period (in frames) for COMM_PROTOCOL_MULTI_E_COMP and above +//........................................................................... +#define DEFAULT_FRAME_SEND_RATE 3 + +//........................................................................... +// Modem-specific constants +//........................................................................... +#define PORTBUF_MAX 5 // dialog field sizes +#define IRQBUF_MAX 3 +#define BAUDBUF_MAX 7 +#define INITSTRBUF_MAX 41 +#define CWAITSTRBUF_MAX 16 +#define CREDITSBUF_MAX 5 +#define PACKET_TIMING_TIMEOUT 40 // ticks b/w sending a timing packet + +//--------------------------------------------------------------------------- +// Enums +//--------------------------------------------------------------------------- +//........................................................................... +// Types of games; used to tell which protocol we're using +//........................................................................... +typedef enum GameEnum { + GAME_NORMAL, // not multiplayer + GAME_MODEM, // modem game + GAME_NULL_MODEM, // NULL-modem + GAME_IPX, // IPX Network game + GAME_INTERNET, // Winsock game + GAME_GLYPHX_MULTIPLAYER // Multiplayer game controlled by the GLYPHX engine. ST - 3/12/2019 10:04AM +} GameType; + +//........................................................................... +// Various Modem-specific enums +//........................................................................... +typedef enum DetectPortType { + PORT_VALID = 0, + PORT_INVALID, + PORT_IRQ_INUSE +} DetectPortType; + +typedef enum DialStatusType { + DIAL_CONNECTED = 0, + DIAL_NO_CARRIER, + DIAL_BUSY, + DIAL_ERROR, + DIAL_CANCELED +} DialStatusType; + +typedef enum DialMethodType { + DIAL_TOUCH_TONE = 0, + DIAL_PULSE, + DIAL_METHODS +} DialMethodType; + +typedef enum CallWaitStringType { + CALL_WAIT_TONE_1 = 0, + CALL_WAIT_TONE_2, + CALL_WAIT_PULSE, + CALL_WAIT_CUSTOM, + CALL_WAIT_STRINGS_NUM +} CallWaitStringType; + +typedef enum ModemGameType { + MODEM_NULL_HOST = 0, + MODEM_NULL_JOIN, + MODEM_DIALER, + MODEM_ANSWERER, + INTERNET_HOST = MODEM_NULL_HOST, + INTERNET_JOIN = MODEM_NULL_JOIN +} ModemGameType; + +//........................................................................... +// Commands sent over the serial Global Channel +//........................................................................... +typedef enum SerialCommandType { + SERIAL_CONNECT = 100, // Are you there? Hello? McFly? + SERIAL_GAME_OPTIONS = 101, // Hey, dudes, here's some new game options + SERIAL_SIGN_OFF = 102, // Bogus, dudes, my boss is coming; I'm outta here! + SERIAL_GO = 103, // OK, dudes, jump into the game loop! + SERIAL_MESSAGE = 104, // Here's a message + SERIAL_TIMING = 105, // timimg packet + SERIAL_SCORE_SCREEN = 106, // player at score screen + SERIAL_LOADGAME = 107, // Start the game, loading a saved game first + SERIAL_LAST_COMMAND // last command +} SerialCommandType; + +//........................................................................... +// Commands sent over the network Global Channel +//........................................................................... +typedef enum NetCommandType { + NET_QUERY_GAME, // Hey, what games are out there? + NET_ANSWER_GAME, // Yo, Here's my game's name! + NET_QUERY_PLAYER, // Hey, what players are in this game? + NET_ANSWER_PLAYER, // Yo, I'm in that game! + NET_CHAT_ANNOUNCE, // I'm at the chat screen + NET_CHAT_REQUEST, // Respond with a CHAT_ANNOUNCE, please. + NET_QUERY_JOIN, // Hey guys, can I play too? + NET_CONFIRM_JOIN, // Well, OK, if you really want to. + NET_REJECT_JOIN, // No, you can't join; sorry, dude. + NET_GAME_OPTIONS, // Hey, dudes, here's some new game options + NET_SIGN_OFF, // Bogus, dudes, my boss is coming; I'm outta here! + NET_GO, // OK, jump into the game loop! + NET_MESSAGE, // Here's a message + NET_PING, // I'm pinging you to take a time measurement + NET_LOADGAME, // start a game by loading a saved game +} NetCommandType; + +//--------------------------------------------------------------------------- +// Structures +//--------------------------------------------------------------------------- +//........................................................................... +// An entry on the score screen is defined by this structure +//........................................................................... +typedef struct { + char Name[MPLAYER_NAME_MAX]; + int Wins; + int Kills[MAX_MULTI_GAMES]; + int Color; +} MPlayerScoreType; + +//........................................................................... +// Settings for the serial port +//........................................................................... +typedef struct { + int Port; + int IRQ; + int Baud; + DialMethodType DialMethod; + int InitStringIndex; + int CallWaitStringIndex; + char CallWaitString[ CWAITSTRBUF_MAX ]; +} SerialSettingsType; + +//........................................................................... +// This is a "node", used for the lists of available games & players. The +// 'Game' structure is used for games; the 'Player' structure for players. +//........................................................................... +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; // player or game name + IPXAddressClass Address; + union { + struct { + unsigned char IsOpen; // is the game open? + unsigned long LastTime; // last time we heard from this guy + } Game; + struct { + HousesType House; // "ActLike" House of this player + unsigned char Color; // Color of this player + HousesType ID; // Actual House of this player + } Player; + struct { + unsigned long LastTime; // last time we heard from this guy + unsigned char LastChance; // we're about to remove him from the list + unsigned char Color; // chat player's color + } Chat; + }; +} NodeNameType; + +//........................................................................... +// Packet sent over the serial Global Channel +//........................................................................... +typedef struct { + SerialCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + unsigned long MinVersion; // min version this game supports + unsigned long MaxVersion; // max version this game supports + HousesType House; // player's House + unsigned char Color; // player's color or SIGNOFF ID + unsigned char Scenario; // Scenario # + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long ResponseTime; // packet response time + char Message[COMPAT_MESSAGE_LENGTH]; // inter-player message + unsigned char ID; // unique ID of sender of message +} SerialPacketType; + +//........................................................................... +// Packet sent over the network Global Channel +//........................................................................... +typedef struct { + NetCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + union { + struct { + unsigned int IsOpen : 1; // 1 = game is open for joining + } GameInfo; + struct { + HousesType House; // player's House + unsigned int Color; // player's color + unsigned long NameCRC; // CRC of player's game's name + unsigned long MinVersion; // game's min supported version + unsigned long MaxVersion; // game's max supported version + } PlayerInfo; + struct { + unsigned char Scenario; // Scenario # + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long Version; // version # common to all players + } ScenarioInfo; + struct { + char Buf[COMPAT_MESSAGE_LENGTH]; // inter-user message + unsigned char Color; // color of sender of message + unsigned long NameCRC; // CRC of sender's Game Name + } Message; + struct { + int OneWay; // one-way response time + } ResponseTime; + struct { + int Why; // why were we rejected from the game? + } Reject; + struct { + unsigned long ID; // unique ID for this chat node + unsigned char Color; // my color + } Chat; + }; +} GlobalPacketType; + +//........................................................................... +// For finding sync bugs; filled in by the engine when certain conditions +// are met; the pointers allow examination of objects in the debugger. +//........................................................................... +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +typedef struct { + int ScenarioIndex; + int Bases; + int Credits; + int Tiberium; + int Goodies; + int Ghosts; + int UnitCount; +} GameOptionsType; + +//--------------------------------------------------------------------------- +// Class Definition +//--------------------------------------------------------------------------- +class SessionClass +{ + //------------------------------------------------------------------------ + // Public interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + SessionClass(void); + ~SessionClass(void); + + //..................................................................... + // Initialization + //..................................................................... + void One_Time(void); + void Init(void); + + //..................................................................... + // Reads/writes to the INI file + //..................................................................... + void Read_MultiPlayer_Settings (void); + void Write_MultiPlayer_Settings (void); + void Read_Scenario_Descriptions (void); + void Free_Scenario_Descriptions(void); + + //..................................................................... + // Utility functions + //..................................................................... + int Create_Connections(void); + bool Am_I_Master(void); + unsigned long Compute_Unique_ID(void); + + //..................................................................... + // File I/O + //..................................................................... + int Save(FileClass &file); + int Load(FileClass &file); + + //..................................................................... + // Debugging / Sync Bugs + //..................................................................... + void Trap_Object(void); + + //--------------------------------------------------------------------- + // Public Data + //--------------------------------------------------------------------- + //..................................................................... + // The type of session being played + //..................................................................... + GameType Type; + + //..................................................................... + // The current communications protocol + //..................................................................... + CommProtocolType CommProtocol; + + //..................................................................... + // Game options + //..................................................................... + GameOptionsType Options; + + //..................................................................... + // Unique workstation ID, for detecting my own packets + //..................................................................... + unsigned long UniqueID; + + //..................................................................... + // Player's local options + //..................................................................... + char Handle[MPLAYER_NAME_MAX]; // player name + int PrefColor; // preferred color index + int ColorIdx; // actual color index + HousesType House; // GDI / NOD + int Blitz; // 1 = AI blitzes + int ObiWan; // 1 = player can see all + int Solo; // 1 = player can play alone + + //..................................................................... + // Max allowable # of players & actual # of (human) players + //..................................................................... + int MaxPlayers; + int NumPlayers; + + //..................................................................... + // Frame-sync'ing timing variables + // 'MaxAhead' is the number of frames ahead of this one to execute + // a given packet. It's set by the RESPONSE_TIME event. + // 'FrameSendRate' is the # frames between data packets + // 'FrameRateDelay' is the time ticks to wait between frames, for + // smoothing. + //..................................................................... + unsigned long MaxAhead; + unsigned long FrameSendRate; + unsigned long FrameRateDelay; + + //..................................................................... + // This flag is set when we've loaded a multiplayer game. + //..................................................................... + int LoadGame; + + //..................................................................... + // This flag is set when the modem game saves the game due to a lost + // connection. + //..................................................................... + int EmergencySave; + + //..................................................................... + // List of scenarios & their file numbers + //..................................................................... + DynamicVectorClass Scenarios; + DynamicVectorClass Filenum; + + //..................................................................... + // This is the multiplayer messaging system + //..................................................................... + MessageListClass Messages; + IPXAddressClass MessageAddress; + char LastMessage[MAX_MESSAGE_LENGTH]; + int WWChat : 1; // 1 = go into special WW Chat mode + + //..................................................................... + // This is the multiplayer scorekeeping system + //..................................................................... + MPlayerScoreType Score[MAX_MULTI_NAMES]; + int GamesPlayed; // # games played this run + int NumScores; // # active entries in MPlayerScore + int Winner; // index of winner of last game + int CurGame; // index of current game being played + + //..................................................................... + // Static arrays + //..................................................................... + static int GColors[]; + static int TColors[]; + static char Descriptions[100][40]; + static int CountMin[2]; + static int CountMax[2]; + static char * GlobalPacketNames[]; + static char * SerialPacketNames[]; + + //..................................................................... + // For Recording & Playing back a file + //..................................................................... + CCFileClass RecordFile; + int Record : 1; + int Play : 1; + int Attract : 1; + + //..................................................................... + // IPX-specific variables + //..................................................................... + int IsBridge; // 1 = we're crossing a bridge + IPXAddressClass BridgeNet; // address of bridge + bool NetStealth; // makes us invisible + bool NetProtect; // keeps others from messaging us + bool NetOpen; // 1 = game is open for joining + char GameName[MPLAYER_NAME_MAX]; // game's name + GlobalPacketType GPacket; // global packet + int GPacketlen; // global packet length + IPXAddressClass GAddress; // address of sender + unsigned short GProductID; // product ID of sender + char MetaPacket[MAX_IPX_PACKET_SIZE]; // packet building buffer + int MetaSize; // size of MetaPacket + DynamicVectorClass Games; // list of games + DynamicVectorClass Players; // list of players + DynamicVectorClass Chat; // list of chat nodes + + //..................................................................... + // Modem-specific variables + //..................................................................... + bool ModemService : 1; // 1 = service modem in Call_Back + int CurPhoneIdx; // phone listing index + SerialSettingsType SerialDefaults; // default serial settings + ModemGameType ModemType; // caller or answerer? + + DynamicVectorClass PhoneBook; + DynamicVectorClass InitStrings; + static char * DialMethodCheck[ DIAL_METHODS ]; + static char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + + //..................................................................... + // For finding Sync Bugs + //..................................................................... + long TrapFrame; + RTTIType TrapObjType; + TrapObjectType TrapObject; + COORD TrapCoord; + void * TrapThis; + CellClass * TrapCell; + int TrapCheckHeap; + +}; + +#endif // SESSION_H + +/*************************** end of session.h ******************************/ \ No newline at end of file diff --git a/TIBERIANDAWN/SHAPEBTN.CPP b/TIBERIANDAWN/SHAPEBTN.CPP new file mode 100644 index 000000000..6d2290e13 --- /dev/null +++ b/TIBERIANDAWN/SHAPEBTN.CPP @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\shapebtn.cpv 2.17 16 Oct 1995 16:52:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "shapebtn.h" + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Default Constructor for a shape type button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must call Set_Shape() before using a button constructed with this function, * + * and you must set X & Y, and ID. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + ReflectButtonState = false; +} + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * * + * This is the normal constructor for a shape type button. Shape buttons are ones that * + * have their imagery controlled by a shape file. The various states of the button are * + * given a visual form as one of these shapes. Button dimensions are controlled by the * + * first shape. * + * * + * INPUT: id -- The button ID. * + * * + * shape -- Pointer to the shape file that controls the button's display. * + * * + * x,y -- The pixel coordinate of the upper left corner of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The width and height of the shape is controlled by the first shape in the * + * shape file provided. This means that all the shapes in the shape file must be * + * the same size. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(unsigned id, void const * shape, int x, int y) : + ToggleClass(id, x, y, 0, 0) +{ + Width = 0; + Height = 0; + ReflectButtonState = false; + Set_Shape(shape); +} + + +void ShapeButtonClass::Set_Shape(void const * data) +{ + ShapeData = data; + if (ShapeData) { + Width = Get_Build_Frame_Width(ShapeData); + Height = Get_Build_Frame_Height(ShapeData); + } +} + + +/*********************************************************************************************** + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * * + * This function is called when the button detects that it must be redrawn. The actual * + * shape to use is controled by the button's state and the shape file provided when then * + * button was constructed. * + * * + * INPUT: forced -- Should the button be redrawn regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the shape redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ShapeButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced) && ShapeData) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the body & set text color. + */ + int shapenum = 0; + if (IsDisabled) { + shapenum = DISABLED_SHAPE; + } else { + + if (!ReflectButtonState){ + if (IsPressed) { + shapenum = DOWN_SHAPE; + } else { + shapenum = UP_SHAPE; + } + }else{ + shapenum = IsOn; + } + } + CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + + diff --git a/TIBERIANDAWN/SHAPEBTN.H b/TIBERIANDAWN/SHAPEBTN.H new file mode 100644 index 000000000..1ded837de --- /dev/null +++ b/TIBERIANDAWN/SHAPEBTN.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\shapebtn.h_v 2.18 16 Oct 1995 16:46:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SHAPEBTN_H +#define SHAPEBTN_H + +#include "toggle.h" + +class ShapeButtonClass : public ToggleClass +{ + public: + ShapeButtonClass(void); + ShapeButtonClass(unsigned id, void const * shapes, int x, int y); + virtual int Draw_Me(int forced=false); + virtual void Set_Shape(void const * data); + + enum ShapeButtonClassEnums { + UP_SHAPE, // Shape to use when button is "up". + DOWN_SHAPE, // Shape to use when button is "down". + DISABLED_SHAPE, // Shape to use when button is disabled. + }; + + unsigned ReflectButtonState:1; + + protected: + + /* + ** This points to the shape data file. This file contains the appropriate shapes + ** for this button in the offsets specified above. + */ + void const * ShapeData; +}; +#endif diff --git a/TIBERIANDAWN/SIDEBAR.CPP b/TIBERIANDAWN/SIDEBAR.CPP new file mode 100644 index 000000000..9ec327d54 --- /dev/null +++ b/TIBERIANDAWN/SIDEBAR.CPP @@ -0,0 +1,2559 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.cpv 2.13 02 Aug 1995 17:03:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : January 25, 1996 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * SidebarClass::Activate -- Controls the sidebar activation. * + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * SidebarClass::Draw_It -- Renders the sidebar display. * + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * sortfunc -- Utility routine that handles 'qsort' the strip buttons. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** Define "_RETRIEVE" if the palette morphing tables are part of the loaded data. If this +** is undefined, then the files will be created. +*/ +#define _RETRIEVE + + +/*************************************************************************** +** This holds the translucent table for use with the construction clock +** animation. +*/ +char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; + + +/*************************************************************************** +** This points to the main sidebar shapes. These include the upgrade and +** repair buttons. +*/ +TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; + +typedef enum ButtonNumberType { + BUTTON_RADAR = 100, + BUTTON_REPAIR, + BUTTON_DEMOLISH, + BUTTON_UPGRADE, + BUTTON_SELECT, + BUTTON_ZOOM +} ButtonNumberType; + +/* +** Sidebar buttons +*/ +SidebarClass::SBGadgetClass SidebarClass::Background; +ShapeButtonClass SidebarClass::Repair; +ShapeButtonClass SidebarClass::Upgrade; +ShapeButtonClass SidebarClass::Zoom; +ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; +ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; +SidebarClass::StripClass::SelectClass +SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; + +/* +** Shape data pointers +*/ +void const * SidebarClass::StripClass::LogoShapes; +void const * SidebarClass::StripClass::ClockShapes; +void const * SidebarClass::StripClass::SpecialShapes[3]; + +void const * SidebarClass::SidebarShape1; +void const * SidebarClass::SidebarShape2; + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(void) +{ + IsSidebarActive = false; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + IsToRedraw = true; + +} + + +/*********************************************************************************************** + * SidebarClass::One_Time -- Handles the one time game initializations. * + * * + * This routine is used to load the graphic data that is needed by the sidebar display. It * + * should only be called ONCE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once when the game first starts. * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::One_Time(void) +{ + PowerClass::One_Time(); + /* + ** Set up the pixel offsets and widths and heights used to render the + ** sidebar. They are now variables because we need to change them for + ** variable resolutions. + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + SideBarWidth = SIDEBARWIDTH * factor; + SideX = SeenBuff.Get_Width() - SideBarWidth; + SideY = Map.RadY + Map.RadHeight + 1; + SideWidth = SeenBuff.Get_Width() - SideX; + SideHeight = SeenBuff.Get_Height() - SideY; + MaxVisible = 4; + ButtonHeight = 9 * factor; + TopHeight = ButtonHeight + (4 * factor); + + Background.X = SideX+8 * factor; + Background.Y = SideY, + Background.Width = SideWidth-1; + Background.Height = SideHeight-1; + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = (SideX+PowWidth) >> 3; + WindowList[WINDOW_SIDEBAR][WINDOWY] = SideY + 1 + TopHeight; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = SideWidth>>3; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (MaxVisible * (StripClass::OBJECT_HEIGHT * factor)) - 1; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ + int width = (SideWidth - PowWidth) - (((StripClass::STRIP_WIDTH ) * factor) << 1); + int spacing = width / 3; + + Column[0].X = SideX + PowWidth + spacing; + Column[0].Y = SideY + TopHeight + 1; + Column[1].X = Column[0].X + (StripClass::STRIP_WIDTH * factor) + spacing -1; + Column[1].Y = SideY + TopHeight + 1; + + Column[0].One_Time(0); + Column[1].One_Time(1); + + SidebarShape1 = Hires_Retrieve("SIDE1.SHP"); + SidebarShape2 = Hires_Retrieve("SIDE2.SHP"); + + +} + + +/*********************************************************************************************** + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Clear(void) +{ + PowerClass::Init_Clear(); + + IsToRedraw = true; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + Activate(false); +} + + +/*********************************************************************************************** + * SidebarClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_IO(void) +{ + void *oldfont; + int oldx; + PowerClass::Init_IO(); + + /* + ** Add the sidebar's buttons only if we're not in editor mode. + */ + int buttonspacing = (SideBarWidth - (ButtonOneWidth + ButtonTwoWidth + ButtonThreeWidth)) / 4; + + + if (!Debug_Map) { + /* + ** Set the button widths based on the string that goes in them. + */ + oldfont = Set_Font(Font6Ptr); + oldx = FontXSpacing; + FontXSpacing = -1; + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6POINT | TPF_NOSHADOW); + + int maxwidth = String_Pixel_Width(Text_String(TXT_REPAIR_BUTTON)) + 8; + maxwidth = MAX((unsigned)maxwidth, String_Pixel_Width(Text_String(TXT_BUTTON_SELL)) + 8); + maxwidth = MAX((unsigned)maxwidth, String_Pixel_Width(Text_String(TXT_MAP)) + 8); + Repair.Width = maxwidth; + Upgrade.Width = maxwidth; + Zoom.Width = maxwidth; +// Repair.Width = String_Pixel_Width(Text_String(TXT_REPAIR_BUTTON)) + 8; +// Upgrade.Width = String_Pixel_Width(Text_String(TXT_BUTTON_SELL)) + 8; +// Zoom.Width = String_Pixel_Width(Text_String(TXT_MAP)) + 8; + /* + ** find the spacing between buttons by getting remaining width + ** and dividing it between the buttons. + */ + int buttonspacing = (SideBarWidth - (Repair.Width + Upgrade.Width + Zoom.Width)) / 4; + + Repair.IsSticky = true; + Repair.ID = BUTTON_REPAIR; + Repair.X = 484; + Repair.Y = 160; + Repair.IsPressed = false; + Repair.IsToggleType = true; + Repair.ReflectButtonState = true; +#if (FRENCH) + Repair.Set_Shape(Hires_Retrieve("REPAIRF.SHP")); +#else +#if (GERMAN) + Repair.Set_Shape(Hires_Retrieve("REPAIRG.SHP")); +#else + Repair.Set_Shape(Hires_Retrieve("REPAIR.SHP")); +#endif +#endif + + Upgrade.IsSticky = true; + Upgrade.ID = BUTTON_UPGRADE; + Upgrade.X = 480+57; + Upgrade.Y = 160; + Upgrade.IsPressed = false; + Upgrade.IsToggleType = true; + Upgrade.ReflectButtonState = true; +#if (FRENCH) + Upgrade.Set_Shape(Hires_Retrieve("SELLF.SHP")); +#else +#if (GERMAN) + Upgrade.Set_Shape(Hires_Retrieve("SELLG.SHP")); +#else + Upgrade.Set_Shape(Hires_Retrieve("SELL.SHP")); +#endif +#endif + + Zoom.IsSticky = true; + Zoom.ID = BUTTON_ZOOM; + Zoom.X = 480 + 110; + Zoom.Y = 160; + Zoom.IsPressed = false; +#if (FRENCH) + Zoom.Set_Shape(Hires_Retrieve("MAPF.SHP")); +#else +#if (GERMAN) + Zoom.Set_Shape(Hires_Retrieve("MAPG.SHP")); +#else + Zoom.Set_Shape(Hires_Retrieve("MAP.SHP")); +#endif +#endif + + if (IsRadarActive || GameToPlay!=GAME_NORMAL) { + Zoom.Enable(); + } else { + Zoom.Disable(); + } + + Set_Font(oldfont); + FontXSpacing = oldx; + FontXSpacing = -1; + + + Column[0].Init_IO(0); + Column[1].Init_IO(1); + + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + if (IsSidebarActive) { + IsSidebarActive = false; + Activate(1); +// Background.Zap(); +// Add_A_Button(Background); + } + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * + * the data that is loaded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Theater(TheaterType theater) +{ + PowerClass::Init_Theater(theater); + + Column[0].Init_Theater(theater); + Column[1].Init_Theater(theater); +} + + +/*********************************************************************************************** + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) +{ + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + +/*********************************************************************************************** + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * * + * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * + * to be refreshed as well. If the special code to refresh the sidebar was found, it * + * flags the sidebar to be redrawn and then removes the code from the list. * + * * + * INPUT: cell -- The cell to base the refresh list on. * + * * + * list -- Pointer to the cell offset list that elaborates all the cells that * + * need to be flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Column[0].IsToRedraw = true; + Column[1].IsToRedraw = true; + Flag_To_Redraw(false); + } + PowerClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * * + * Use this routine to turn the repair sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure is friendly and damaged. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Repair(int control) +{ + bool old = IsRepairActive; + + if (control == -1) { + control = IsRepairActive ? 0 : 1; + } + switch (control) { + case 1: + IsRepairActive = true; + break; + + default: + case 0: + IsRepairActive = false; + break; + } + if (old != IsRepairActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + + if (!IsRepairActive) { + Help_Text(TXT_NONE); + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * * + * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure can be upgraded and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Upgrade(int control) +{ + bool old = IsUpgradeActive; + if (control == -1) { + control = IsUpgradeActive ? 0 : 1; + } + switch (control) { + case 1: + IsUpgradeActive = true; + break; + + default: + case 0: + IsUpgradeActive = false; + break; + } + if (old != IsUpgradeActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsUpgradeActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * * + * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * + * the button is enabled when a friendly building is selected and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Demolish(int control) +{ + bool old = IsDemolishActive; + + if (control == -1) { + control = IsDemolishActive ? 0 : 1; + } + switch (control) { + case 1: + IsDemolishActive = true; + break; + + default: + case 0: + IsDemolishActive = false; + break; + } + if (old != IsDemolishActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsDemolishActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::Add(RTTIType type, int id, bool via_capture) +{ + int column; + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + Activate(1); + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * * + * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * + * a building is selected that can produce units. If the number of units to produce is * + * greater than what the sidebar can hold, this routine is used to scroll the other object * + * into view so they can be selected. * + * * + * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * + * later in the list of objects. * + * * + * OUTPUT: bool; Did scrolling occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Scroll(bool up, int column) +{ + if (Column[column].Scroll(up)) { + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Draw_It -- Renders the sidebar display. * + * * + * This routine performs the actual drawing of the sidebar display. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the sidebar imagery changed at all? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * + *=============================================================================================*/ +void SidebarClass::Draw_It(bool complete) +{ + PowerClass::Draw_It(complete); + + if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { + IsToRedraw = false; + + if (LogicPage->Lock()){ + /* + ** Draw the outline box around the sidebar buttons. + */ + //CC_Draw_Shape(SidebarShape1, (int)complete, SideX, 158, WINDOW_MAIN, SHAPE_WIN_REL); + //CC_Draw_Shape(SidebarShape2, (int)complete, SideX, 158+118, WINDOW_MAIN, SHAPE_WIN_REL); + LogicPage->Draw_Line(SideX, 157, SeenBuff.Get_Width()-1, 157, 0); + CC_Draw_Shape(SidebarShape1, 0, SideX, 158, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarShape2, 0, SideX, 158+118, WINDOW_MAIN, SHAPE_WIN_REL); + + #if (0) + if ( complete ) { + LogicPage->Fill_Rect(SideX+Map.PowWidth, SideY, SideX+SideWidth-1, SideY+SideHeight-1, LTGREY); + } + LogicPage->Fill_Rect(SideX, SideY, SideX+SideWidth-1, SideY+TopHeight-1, LTGREY); + Draw_Box(SideX+Map.PowWidth, SideY+TopHeight, SideWidth-Map.PowWidth, SideHeight-TopHeight, BOXSTYLE_RAISED, false); + #endif //(0) + //Repair.Draw_Me(true); + //Upgrade.Draw_Me(true); + //Zoom.Draw_Me(true); + // } else { + // if (IsToRedraw || complete) { + // LogicPage->Fill_Rect(TacPixelX + Lepton_To_Pixel(TacLeptonWidth), SIDE_Y, 319, SIDE_Y+TOP_HEIGHT, BLACK); + // } + + LogicPage->Unlock(); + } + + } + /* + ** Draw the side strip elements by calling their respective draw functions. + */ + if (IsSidebarActive){ + Column[0].Draw_It(complete); + Column[1].Draw_It(complete); + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + } + + IsToRedraw = false; +} + + +/*********************************************************************************************** + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarClass::AI(KeyNumType & input, int x, int y) +{ + bool redraw = false; + + // + // We need to process the sidebar differently in multiplayer. ST - 3/22/2019 1:27PM + // + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + PowerClass::AI(input, x, y); + return; + } + + /* + ** Toggle the sidebar in and out with the key. + */ + if (input == KN_TAB) { + Activate(-1); + } + + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } + + if (IsSidebarActive && !Debug_Map) { + + if (input == KN_DOWN) { + redraw |= Column[0].Scroll(false); + redraw |= Column[1].Scroll(false); + input = KN_NONE; + } + if (input == KN_UP) { + redraw |= Column[0].Scroll(true); + redraw |= Column[1].Scroll(true); + input = KN_NONE; + } + } + + if (IsSidebarActive) { + + /* + ** If there are any buildings in the payer's inventory, then allow the repair + ** option. + */ + if (PlayerPtr->BScan) { + Activate_Repair(true); + } else { + Activate_Repair(false); + } + + if (input == (BUTTON_REPAIR|KN_BUTTON)) { + Repair_Mode_Control(-1); + } + + if (input == (BUTTON_ZOOM|KN_BUTTON)) { + /* + ** If radar is active, cycle as follows: + ** Zoomed => not zoomed + ** not zoomed => player status (multiplayer only) + ** player status => zoomed + */ + if (IsRadarActive) { + if (Is_Zoomed() || GameToPlay==GAME_NORMAL) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } else { + if (!Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } else { + if (GameToPlay!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } + } + + if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + Sell_Mode_Control(-1); + } + +#ifdef NEVER +// int index = -1; + if (index != -1) { + /* + ** Display help text if the mouse is over a sidebar button. + */ + switch (index) { + default: + case 2: + Map.Help_Text(TXT_UPGRADE, -1, -1, PlayerPtr->Class->Color); + break; + + case 1: + Map.Help_Text(PlayerPtr->Class->House == HOUSE_GOOD ? TXT_SELL : TXT_DEMOLISH, x, y, PlayerPtr->Class->Color); + break; + + case 0: + Map.Help_Text(TXT_REPAIR, x, y, PlayerPtr->Class->Color); + break; + } + } +#endif + + if (redraw) { + //IsToRedraw = true; + Column[0].Flag_To_Redraw(); + Column[1].Flag_To_Redraw(); + + Flag_To_Redraw(false); + } + } + + if ((!IsRepairMode) && Repair.IsOn){ + Repair.Turn_Off(); + } + + if ((!IsSellMode) && Upgrade.IsOn){ + Upgrade.Turn_Off(); + } + + PowerClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Recalc(void) +{ + bool redraw = false; + + // Done elsewhere for new multiplayer. ST - 3/22/2019 2:06PM + if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { + return; + } + + redraw |= Column[0].Recalc(); + redraw |= Column[1].Recalc(); + + if (redraw) { + IsToRedraw = true; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * SidebarClass::Activate -- Controls the sidebar activation. * + * * + * Use this routine to turn the sidebar on or off. This routine handles updating the * + * necessary flags. * + * * + * INPUT: control -- Tells what to do with the sidebar according to the following: * + * 0 = Turn sidebar off. * + * 1 = Turn sidebar on. * + * -1= Toggle sidebar on or off. * + * * + * OUTPUT: bool; Was the sidebar already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate(int control) +{ + // + // We don't want the original sidebar to be visible. ST - 1/31/2019 11:28AM + // + if (control < 100) { + return IsSidebarActive; + } + + bool old = IsSidebarActive; + + int sidex = SeenBuff.Get_Width() - SideBarWidth; + int sidey = Map.RadY + Map.RadHeight; + int topheight = 13; + int sidewidth = SeenBuff.Get_Width() - sidex; + int sideheight = SeenBuff.Get_Height() - sidey; + + if (PlaybackGame) + return (old); + + /* + ** Determine the new state of the sidebar. + */ + switch (control) { + case -1: + IsSidebarActive = IsSidebarActive == false; + break; + + case 1: + IsSidebarActive = true; + break; + + default: + case 0: + IsSidebarActive = false; + break; + } + + /* + ** Only if there is a change in the state of the sidebar will anything + ** be done to change it. + */ + if (IsSidebarActive != old) { + + /* + ** If the sidebar is activated but was on the right side of the screen, then + ** activate it on the left side of the screen. + */ + if (IsSidebarActive /*&& X*/) { + Set_View_Dimensions(0, Map.Get_Tab_Height(), SeenBuff.Get_Width() - sidewidth); + IsToRedraw = true; + Help_Text(TXT_NONE); + Repair.Zap(); + Add_A_Button(Repair); + Upgrade.Zap(); + Add_A_Button(Upgrade); + Zoom.Zap(); + Add_A_Button(Zoom); + Column[0].Activate(); + Column[1].Activate(); + Background.Zap(); + Add_A_Button(Background); + Map.RadarButton.Zap(); + Add_A_Button(Map.RadarButton); + Map.PowerButton.Zap(); + Add_A_Button(Map.PowerButton); + } else { + Help_Text(TXT_NONE); + Set_View_Dimensions(0, Map.Get_Tab_Height()); + Remove_A_Button(Repair); + Remove_A_Button(Upgrade); + Remove_A_Button(Zoom); + Remove_A_Button(Background); + Column[0].Deactivate(); + Column[1].Deactivate(); + Remove_A_Button(Map.RadarButton); + Remove_A_Button(Map.PowerButton); + } + + /* + ** Since the sidebar status has changed, update the map so that the graphics + ** will be rendered correctly. + */ + Flag_To_Redraw(true); + } + + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::StripClass(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; TopIndex = 0; + Slid = 0; + BuildableCount = 0; + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + + +/*********************************************************************************************** + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * + * * + * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * + * the shape files it needs for rendering. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::One_Time(int ) +{ + static char *_file[3] = { + "ION", + "ATOM", + "BOMB" + }; + int factor = Get_Resolution_Factor(); + + ObjectWidth = OBJECT_WIDTH << factor; + ObjectHeight = OBJECT_HEIGHT << factor; + StripWidth = STRIP_WIDTH << factor; + LeftEdgeOffset = (StripWidth - ObjectWidth) >> 1; + ButtonSpacingOffset = (StripWidth - ((BUTTON_WIDTH << factor) << 1)) / 3; + + LogoShapes = Hires_Retrieve("STRIP.SHP"); + ClockShapes = Hires_Retrieve("CLOCK.SHP"); + + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + + for (int lp = 0; lp < 3; lp++) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", _file[lp]); + } else { + sprintf(buffer, "%sICON", _file[lp]); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + SpecialShapes[lp] = MixFileClass::Retrieve(fullname); + } + + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * * + * This routine will return with a pointer to the cameo data for the special objects that * + * can appear on the sidebar (e.g., nuclear bomb). * + * * + * INPUT: type -- The special type to fetch the cameo imagery for. * + * * + * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : commented * + *=============================================================================================*/ +void const * SidebarClass::StripClass::Get_Special_Cameo(int type) +{ + return(SpecialShapes[type]); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Clear(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; + Flasher = -1; + TopIndex = 0; + Slid = 0; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * + * * + * This routine doesn't actually add any buttons to the list; + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_IO(int id) +{ + ID = id; + + UpButton[ID].IsSticky = true; + UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].X = X+ButtonSpacingOffset+1; + UpButton[ID].Y = Y+MAX_VISIBLE*ObjectHeight-1; + + UpButton[ID].Set_Shape(Hires_Retrieve("STRIPUP.SHP")); + + DownButton[ID].IsSticky = true; + DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].X = UpButton[ID].X + UpButton[ID].Width + ButtonSpacingOffset-2; + DownButton[ID].Y = Y+MAX_VISIBLE*ObjectHeight-1; + + DownButton[ID].Set_Shape(Hires_Retrieve("STRIPDN.SHP")); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectClass & g = SelectButton[ID][index]; + g.ID = BUTTON_SELECT; + g.X = X; + g.Y = Y + (ObjectHeight*index); + g.Width = ObjectWidth; + g.Height = ObjectHeight; + g.Set_Owner(*this, index); + } + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Theater(TheaterType theater) +{ + //if (theater != LastTheater) { + + + static char *_file[3] = { + "ION", + "ATOM", + "BOMB" + }; + int factor = Get_Resolution_Factor(); + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + void const * cameo_ptr; + + for (int lp = 0; lp < 3; lp++) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", _file[lp]); + } else { + sprintf(buffer, "%sICON", _file[lp]); + } + _makepath(fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + SpecialShapes[lp] = cameo_ptr; + } + } + + +#ifndef _RETRIEVE + static TLucentType const ClockCols[1] = { +// {LTGREEN, BLACK, 0, 0}, + {GREEN, LTGREY, 180, 0} + }; + + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + Mem_Copy(GamePalette, OriginalPalette, 768); + memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + + /* + ** Create the translucent table used for the sidebar. + */ + Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); + CCFileClass(Fading_Table_Name("CLOCK", theater)).Write(ClockTranslucentTable, sizeof(ClockTranslucentTable)); + Mem_Copy(OriginalPalette, GamePalette, 768); +#else + CCFileClass(Fading_Table_Name("CLOCK", theater)).Read(ClockTranslucentTable, sizeof(ClockTranslucentTable)); +#endif + LastTheater = theater; + //} +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * * + * This routine will add the side strip buttons to the map's input system. This routine * + * should be called once when the sidebar activates. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine a second time without first calling Deactivate(). * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Activate(void) +{ + UpButton[ID].Zap(); + Map.Add_A_Button(UpButton[ID]); + + DownButton[ID].Zap(); + Map.Add_A_Button(DownButton[ID]); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectButton[ID][index].Zap(); + Map.Add_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * + * * + * Call this routine to remove all the buttons on the side strip from the map's input * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine unless the Activate() function was prevously called. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Deactivate(void) +{ + Map.Remove_A_Button(UpButton[ID]); + Map.Remove_A_Button(DownButton[ID]); + for (int index = 0; index < MAX_VISIBLE; index++) { + Map.Remove_A_Button(SelectButton[ID][index]); + } +} + + +#ifdef NEVER +/*********************************************************************************************** + * sortfunc -- Utility routine that handles 'qsort' the strip buttons. * + * * + * This routine is called by qsort() in order to sort the sidebar buttons. This sorting * + * forces the sidebar buttons to always occur in the order that they can be built in, * + * rather than the order that they were added to the sidebar list. * + * * + * INPUT: ptr1 -- Pointer to the first sidebar class object. * + * * + * ptr2 -- Pointer to the second sidebar class object. * + * * + * OUTPUT: Returns <0 if the first object can be produced before the second. It returns * + * >0 if the reverse is true. It returns exactly 0 if the production scneario for * + * both objects is the same. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +static int sortfunc(void const * ptr1, void const * ptr2) +{ + SidebarClass::StripClass::BuildType * b1 = (SidebarClass::StripClass::BuildType *)ptr1; + SidebarClass::StripClass::BuildType * b2 = (SidebarClass::StripClass::BuildType *)ptr2; + + TechnoTypeClass const * p1 = Fetch_Techno_Type(b1->BuildableType, b1->BuildableID); + TechnoTypeClass const * p2 = Fetch_Techno_Type(b2->BuildableType, b2->BuildableID); + + int i1 = 0; + int i2 = 0; + + if (p1) i1 = p1->What_Am_I()*2; + if (p2) i2 = p2->What_Am_I()*2; + + /* + ** Walls should be sorted after the regular buildings. + */ + if (p1 && p1->What_Am_I() == RTTI_BUILDINGTYPE && ((BuildingTypeClass * const)p1)->IsWall) { + i1++; + } + if (p2 && p2->What_Am_I() == RTTI_BUILDINGTYPE && ((BuildingTypeClass * const)p2)->IsWall) { + i2++; + } + + /* + ** If the object types are identical, then sort by scenario available. + */ + if (i1 == i2) { + + /* + ** In the case of walls (can tell if there is an odd value), then sort + ** by cost. + */ + if (i1 & 0x01) { + i1 = p1->Cost; + i2 = p2->Cost; + } else { + i1 = p1->Scenario; + i2 = p2->Scenario; + } + } + + return(i1 - i2); +} +#endif + + +/*********************************************************************************************** + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + IsToRedraw = true; +#ifdef OBSOLETE + if (GameToPlay == GAME_NORMAL) { + qsort(&Buildables[0], BuildableCount, sizeof(Buildables[0]), sortfunc); + } +#endif + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * * + * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * + * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * + * the AI function and the Draw_It function are required to properly give the appearence * + * of scrolling. * + * * + * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * + * * + * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 07/29/1995 JLB : Simplified scrolling logic. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Scroll(bool up) +{ + if (up) { + Scroller--; + } else { + Scroller++; + } +#ifdef NEVER + if (BuildableCount <= MAX_VISIBLE) return(false); + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (up) { + if (!TopIndex) return(false); + + TopIndex--; + Slid = 0; + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) return(false); + + Slid = ObjectHeight; + } + IsScrollingDown = !up; + IsScrolling = true; +#endif + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * * + * This utility routine is called when something changes on the sidebar and it must be * + * reflected the next time drawing is performed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Flag_To_Redraw(void) +{ + IsToRedraw = true; + //Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +{ + bool redraw = false; + + /* + ** If this is scroll button for this side strip, then scroll the strip as + ** indicated. + */ + if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + UpButton[ID].IsPressed = false; + Scroll(true); + } + if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + DownButton[ID].IsPressed = false; + Scroll(false); + } + + /* + ** Reflect the scroll desired direction/value into the scroll + ** logic handler. This might result in up or down scrolling. + */ + if (!IsScrolling && Scroller) { + if (BuildableCount <= MAX_VISIBLE) { + Scroller = 0; + } else { + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (Scroller < 0) { + if (!TopIndex) { + Scroller = 0; + } else { + Scroller++; + IsScrollingDown = false; + IsScrolling = true; + TopIndex--; + Slid = 0; + } + + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) { + Scroller = 0; + } else { + Scroller--; + Slid = ObjectHeight; + IsScrollingDown = true; + IsScrolling = true; + } + } + } + } + + /* + ** Scroll logic is handled here. + */ + if (IsScrolling) { + if (IsScrollingDown) { + Slid -= SCROLL_RATE; + if (Slid <= 0) { + IsScrolling = false; + Slid = 0; + TopIndex++; + } + } else { + Slid += SCROLL_RATE; + if (Slid >= ObjectHeight) { + IsScrolling = false; + Slid = 0; + } + } + redraw = true; + } + + /* + ** Handle any flashing logic. Flashing occurs when the player selects an object + ** and provides the visual feedback of a recognized and legal selection. + */ + if (Flasher != -1) { + if (Graphic_Logic()) { + redraw = true; + if (Fetch_Stage() >= 7) { + Set_Rate(0); + Set_Stage(0); + Flasher = -1; + } + } + } + + /* + ** Handle any building clock animation logic. + */ + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + redraw = true; + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending) { + switch (pending->What_Am_I()) { + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + // Fall into next case. + + case RTTI_BUILDING: + if (!factory->Is_Blocked()) { + Speak(VOX_CONSTRUCTION); + } + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + /* + ** If any of the logic determined that this side strip needs to be redrawn, then + ** set the redraw flag for this side strip. + */ + if (redraw) { + Flag_To_Redraw(); + } + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * * + * Use this routine to render the sidebar display. It checks to see if it needs to be * + * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * + * will force redraw the entire strip. * + * * + * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * + * flag. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 08/06/1995 JLB : Handles multi factory tracking in same strip. * + *=============================================================================================*/ +void SidebarClass::StripClass::Draw_It(bool complete) +{ + if (IsToRedraw || complete) { + IsToRedraw = false; + + /* + ** Fills the background to the side strip. We shouldnt need to do this if the strip + ** has a full complement of icons. ST - 10/7/96 6:03PM + */ + if (BuildableCount < MAX_VISIBLE){ + CC_Draw_Shape(LogoShapes, ID, X+3, Y-1, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + } + + /* + ** Redraw the scroll buttons. + */ + UpButton[ID].Draw_Me(true); + DownButton[ID].Draw_Me(true); + + /* + ** Loop through all the buildable objects that are visible in the strip and render + ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. + */ + for (int i = 0; i < MAX_VISIBLE + (IsScrolling ? 1 : 0); i++) { + bool production; + bool completed; + int stage; + bool darken = false; + void const * shapefile = 0; + int shapenum = 0; + void const * remapper = 0; + FactoryClass * factory = 0; + int index = i+TopIndex; + int x = X; + int y = Y + i*ObjectHeight; + y--; + + /* + ** If the strip is scrolling, then the offset is adjusted accordingly. + */ + if (IsScrolling) { + y -= ObjectHeight - Slid; + } + + /* + ** Fetch the shape number for the object type located at this current working + ** slot. This shape pointer is used to draw the underlying graphic there. + */ + if (index < BuildableCount) { + ObjectTypeClass const * obj = NULL; + int spc = 0; + + if (Buildables[index].BuildableType != RTTI_SPECIAL) { + + obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (obj) { + bool isbusy = false; + switch (Buildables[index].BuildableType) { + case RTTI_INFANTRYTYPE: + isbusy = (PlayerPtr->InfantryFactory != -1); + break; + + case RTTI_BUILDINGTYPE: + isbusy = (PlayerPtr->BuildingFactory != -1); + if (!BuildingTypeClass::As_Reference((StructType)Buildables[index].BuildableID).IsWall) { + remapper = PlayerPtr->Remap_Table(false, false); + } + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + switch (Buildables[index].BuildableID) { + case UNIT_MCV: + case UNIT_HARVESTER: + remapper = PlayerPtr->Remap_Table(false, false); + break; + + default: + remapper = PlayerPtr->Remap_Table(false, true); + break; + } + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + remapper = PlayerPtr->Remap_Table(false, true); + break; + } + shapefile = obj->Get_Cameo_Data(); + shapenum = 0; + if (Buildables[index].Factory != -1) { + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } else { + production = false; +// darken = IsBuilding; + + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ + darken = isbusy; + } + } + + } else { + + spc = Buildables[index].BuildableID; + shapefile = Get_Special_Cameo(spc - 1); + shapenum = 0; + + switch (spc) { + case SPC_ION_CANNON: + production = true; + completed = PlayerPtr->IonCannon.Is_Ready(); + stage = PlayerPtr->IonCannon.Anim_Stage(); + darken = false; + break; + + case SPC_AIR_STRIKE: + production = true; + completed = PlayerPtr->AirStrike.Is_Ready(); + stage = PlayerPtr->AirStrike.Anim_Stage(); + darken = false; + break; + + case SPC_NUCLEAR_BOMB: + production = true; + completed = PlayerPtr->NukeStrike.Is_Ready(); + stage = PlayerPtr->NukeStrike.Anim_Stage(); + darken = false; + break; + } + } + + if (obj || spc) { + /* + ** If this item is flashing then take care of it. + ** + */ + if (Flasher == index && (Fetch_Stage() & 0x01)) { + remapper = Map.FadingLight; + } + + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + } + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; + } + + remapper = 0; + + /* + ** Now that the shape of the object at the current working slot has been found, + ** draw it and any graphic overlays as necessary. + ** + ** Dont draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM + */ + if (shapenum != SB_BLANK || shapefile != LogoShapes){ + IsTheaterShape = true; // This shape is theater specific + CC_Draw_Shape(shapefile, shapenum, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + remapper); + IsTheaterShape = false; + + + /* + ** Darken this object because it cannot be produced or is otherwise + ** unavailable. + */ + if (darken) { + CC_Draw_Shape(ClockShapes, 0, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + } + } + + /* + ** Draw the overlapping clock shape if this is object is being constructed. + ** If the object is completed, then display "Ready" with no clock shape. + */ + if (production) { + if (completed) { + + /* + ** Display text showing that the object is ready to place. + */ + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8))+LeftEdgeOffset+(ObjectWidth >> 1), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+ObjectHeight-Get_Build_Frame_Height(ObjectTypeClass::PipShapes) -8, + WINDOW_SIDEBAR, SHAPE_CENTER); +// Fancy_Text_Print(TXT_READY, x+TEXT_X_OFFSET, y+TEXT_Y_OFFSET, TEXT_COLOR, TBLACK, TPF_6POINT|TPF_CENTER|TPF_NOSHADOW); + } else { + CC_Draw_Shape(ClockShapes, stage+1, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + /* + ** Display text showing that the construction is temporarily on hold. + */ + if (factory && !factory->Is_Building()) { + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8))+LeftEdgeOffset+(ObjectWidth >> 1), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+ObjectHeight-Get_Build_Frame_Height(ObjectTypeClass::PipShapes) - 8, // Moved up now that icons have names on them + WINDOW_SIDEBAR, SHAPE_CENTER); +// Fancy_Text_Print(TXT_HOLDING, x+TEXT_X_OFFSET, y+TEXT_Y_OFFSET, TEXT_COLOR, TBLACK, TPF_6POINT|TPF_CENTER|TPF_NOSHADOW); + } + } + } + } + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + bool redraw = false; + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech) { + ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; + } else { + switch (Buildables[index].BuildableID) { + case SPC_ION_CANNON: + ok = PlayerPtr->IonCannon.Is_Present(); + break; + + case SPC_NUCLEAR_BOMB: + ok = PlayerPtr->NukeStrike.Is_Present(); + break; + + case SPC_AIR_STRIKE: + ok = PlayerPtr->AirStrike.Is_Present(); + break; + + default: + ok = false; + break; + } + +#ifdef OBSOLETE + } else { + switch (Buildables[index].BuildableID) { + case SPC_ION_CANNON: + ok = (PlayerPtr->BScan & STRUCTF_EYE) != 0 || PlayerPtr->IonOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Ion_Cannon(); + } + break; + + case SPC_NUCLEAR_BOMB: + ok = (PlayerPtr->BScan & STRUCTF_TEMPLE) != 0 && PlayerPtr->Has_Nuke_Device(); + ok = ok || PlayerPtr->NukeOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Nuke_Bomb(); + } + break; + + case SPC_AIR_STRIKE: +// ok = (PlayerPtr->BScan & STRUCTF_SAM) == 0; +// ok = !PlayerPtr->Does_Enemy_Building_Exist(STRUCT_SAM); + ok = (PlayerPtr->AirPresent /*&& !PlayerPtr->Does_Enemy_Building_Exist(STRUCT_SAM)*/) || PlayerPtr->AirOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Air_Strike(); + } + break; + + default: + ok = false; + break; + } +#endif + } + + if (!ok) { + + /* + ** If there was something in production, then abandon it before deleting the + ** factory manager. + */ + //if (Buildables[index].Factory != -1) { + //FactoryClass * factory = Factories.Raw_Ptr(Buildables[index].Factory); + //factory->Abandon(); + //delete factory; + //Buildables[index].Factory = -1; + //} +// Buildables[index].Factory = -1; + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + TopIndex = 0; + IsToRedraw = true; + redraw = true; + BuildableCount--; + index--; + } + } + +#ifdef NEVER + /* + ** If there are no more buildable objects to display, make the sidebar go away. + */ + if (!BuildableCount) { + Map.SidebarClass::Activate(0); + } +#endif + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * * + * This is the default constructor for the button that controls the buildable cameos on * + * the sidebar strip. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * + * correct values before this button will function. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::SelectClass::SelectClass(void) : + ControlClass(0, 0, 0, 0, 0, LEFTPRESS|RIGHTPRESS|LEFTUP) +{ + int factor = Get_Resolution_Factor(); + + Strip = 0; + Index = 0; + Width = StripClass::OBJECT_WIDTH << factor; + Height = StripClass::OBJECT_HEIGHT << factor; +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * + * * + * Use this routine to set custom buildable vars for this particular select button. It * + * uses this information to properly know what buildable object to start or stop production * + * on. * + * * + * INPUT: strip -- Reference to the strip that owns this buildable button. * + * * + * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * + * the owning strip know what index this button refers to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +{ + int factor = Get_Resolution_Factor(); + Strip = &strip; + Index = index; + X = strip.X; + Y = strip.Y + (index * (StripClass::OBJECT_HEIGHT << factor)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * + * * + * This function is called when the buildable icon (cameo) is clicked on. It handles * + * starting and stopping production as indicated. * + * * + * INPUT: flags -- The input event that triggered the call. * + * * + * key -- The keyboard value at the time of the input. * + * * + * OUTPUT: Returns with whether the input list should be scanned further. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +{ + int index = Strip->TopIndex + Index; + RTTIType otype = Strip->Buildables[index].BuildableType; + int oid = Strip->Buildables[index].BuildableID; + int fnumber = Strip->Buildables[index].Factory; + + FactoryClass * factory = NULL; + ObjectTypeClass const * choice = NULL; + int spc = 0; + + /* + ** Determine the factory number that would apply to objects of the type + ** the mouse is currently addressing. This doesn't mean that the factory number + ** fetched is actually producing the indicated object, merely that that particular + ** kind of factory is specified by the "genfactory" value. This can be used to see + ** if the factory type is currently busy or not. + */ + int genfactory = -1; + switch (otype) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + + if (index < Strip->BuildableCount) { + if (otype != RTTI_SPECIAL) { + choice = Fetch_Techno_Type(otype, oid); + } else { + spc = oid; + } + + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + } else { + Map.Help_Text(TXT_NONE); + } + + if (spc) { + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + switch (spc) { + case SPC_ION_CANNON: + Map.Help_Text(TXT_ION_CANNON, X, Y, CC_GREEN, true); + break; + + case SPC_NUCLEAR_BOMB: + Map.Help_Text(TXT_NUKE_STRIKE, X, Y, CC_GREEN, true); + break; + + case SPC_AIR_STRIKE: + Map.Help_Text(TXT_AIR_STRIKE, X, Y, CC_GREEN, true); + break; + } + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". If we are in targetting + ** mode then we don't want to be any more. + */ + if (flags & RIGHTPRESS) { + Map.IsTargettingMode = false; + } + /* + ** A left mouse press signal "activate". If our weapon type is + ** available then we should activate it. + */ + if (flags & LEFTPRESS) { + switch (spc) { + case SPC_ION_CANNON: + if (PlayerPtr->IonCannon.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->IonCannon.Impatient_Click(); + } + break; + + case SPC_AIR_STRIKE: + if (PlayerPtr->AirStrike.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->AirStrike.Impatient_Click(); + } + break; + + case SPC_NUCLEAR_BOMB: + if (PlayerPtr->NukeStrike.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->NukeStrike.Impatient_Click(); + } + break; + } + } + + } else { + + if (choice) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(choice->Full_Name(), X, Y, CC_GREEN, true, choice->Cost_Of() * PlayerPtr->CostBias); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". + */ + if (flags & RIGHTPRESS) { + + /* + ** If production is in progress, put it on hold. If production is already + ** on hold, then abandon it. Money will be refunded, the factory + ** manager deleted, and the object under construction is returned to + ** the free pool. + */ + if (factory) { + + /* + ** Cancels placement mode if the sidebar factory is abandoned or + ** suspended. + */ + if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + + if (!factory->Is_Building()) { + Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + } else { + Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); + } + } + } + + if (flags & LEFTPRESS) { + + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && genfactory != -1) { + Speak(VOX_NO_FACTORY); + ControlClass::Action(flags, key); + return(true); + } + + if (factory) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + } else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + + } else { + + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ +// if (Strip->IsBuilding) { +// Speak(VOX_NO_FACTORY); +// } else { + Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); +// } + } + } + } else { + flags = 0; + } + } + + ControlClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * + * * + * This routine is called whenever the mouse is over the sidebar. It makes sure that the * + * mouse is always the normal shape while over the sidebar. * + * * + * INPUT: flags -- The event flags that resuled in this routine being called. * + * * + * key -- Reference the keyboard code that may be present. * + * * + * OUTPUT: Returns that no further keyboard processing is necessary. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/28/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +{ + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + + /* + ** Flag that all the icons on this strip need to be redrawn + */ + Flag_To_Redraw(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there was a change to the strip, then flag the strip to be redrawn. + */ + if (abandon) { + Flag_To_Redraw(); + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} diff --git a/TIBERIANDAWN/SIDEBAR.H b/TIBERIANDAWN/SIDEBAR.H new file mode 100644 index 000000000..7414d8f44 --- /dev/null +++ b/TIBERIANDAWN/SIDEBAR.H @@ -0,0 +1,395 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.h_v 2.18 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "function.h" +#include "power.h" +#include "factory.h" + +class SidebarClass: public PowerClass +{ + public: + /* + ** These constants are used to control the sidebar rendering. They are instantiated + ** as enumerations since C++ cannot use "const" in this context. + */ + int SideX; // x position for side bar + int SideY; // y position for side bar + int SideBarWidth; // width of the sidebar + int SideWidth; // width of the sidebar + int SideHeight; // height of the sidebar + int TopHeight; // height of top of sidebar + int MaxVisible; // max production icons visible + int ButtonOneWidth; // Button width. + int ButtonTwoWidth; // Button width. + int ButtonThreeWidth; // Button width. + int ButtonHeight; // Button width. + + enum SideBarClassEnums { + BUTTON_ACTIVATOR=100, // Button ID for the activator. + SIDEBARWIDTH = 80, +#if 0 + SIDE_X=RADAR_X, // The X position of sidebar upper left corner. + SIDE_Y=RADAR_Y+RADAR_HEIGHT, // The Y position of sidebar upper left corner. + SIDE_WIDTH=320-SIDE_X, // Width of the entire sidebar (in pixels). + SIDE_HEIGHT=200-SIDE_Y, // Height of the entire sidebar (in pixels). + TOP_HEIGHT=13, // Height of top section (with repair/sell buttons). + COLUMN_ONE_X=SIDE_X+8, // Sidestrip upper left coordinates... + COLUMN_ONE_Y=SIDE_Y+TOP_HEIGHT, + COLUMN_TWO_X=COLUMN_ONE_X+((SIDE_WIDTH-16)/2)+3, + COLUMN_TWO_Y=SIDE_Y+TOP_HEIGHT, +#if (GERMAN | FRENCH) +//BGA: changes to all buttons + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#else + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+36, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+58, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. +#endif + COLUMNS=2, // Number of side strips on sidebar. + }; + + SidebarClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + virtual void AI(KeyNumType & input, int x, int y); + virtual void Draw_It(bool complete); + virtual void Refresh_Cells(CELL cell, short const *list); + + bool Abandon_Production(RTTIType type, int factory); + bool Activate(int control); + bool Add(RTTIType type, int ID, bool via_capture = false); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM + bool Sidebar_Click(KeyNumType & input, int x, int y); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + + void Set_Owner(StripClass & strip, int index); + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + int ObjectWidth; + int ObjectHeight; + int StripWidth; + int LeftEdgeOffset; + int ButtonSpacingOffset; + + + StripClass(void); + bool Add(RTTIType type, int ID, bool via_capture); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM + bool Abandon_Production(int factory); + bool Scroll(bool up); + bool AI(KeyNumType & input, int x, int y); + void Draw_It(bool complete); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + bool Recalc(void); + void Activate(void); + void Deactivate(void); + void Flag_To_Redraw(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(int type); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + BUTTON_UP=200, + BUTTON_DOWN=210, + BUTTON_SELECT=220, + MAX_BUILDABLES=30, // Maximum number of object types in sidebar. + OBJECT_HEIGHT=24, // Pixel height of each buildable object. + OBJECT_WIDTH=32, // Pixel width of each buildable object. + STRIP_WIDTH=35, // Width of strip (not counting border lines). + MAX_VISIBLE=4, // Number of object slots visible at any one time. + SCROLL_RATE=8, // The pixel jump while scrolling (larger is faster). + BUTTON_SPACING_OFFSET = 4, // spacing info for buttons + UP_X_OFFSET=2, // Scroll up arrow coordinates. + UP_Y_OFFSET=MAX_VISIBLE*OBJECT_HEIGHT+1, + DOWN_X_OFFSET=18, // Scroll down arrow coordinates. + DOWN_Y_OFFSET=MAX_VISIBLE*OBJECT_HEIGHT+1, + BUTTON_WIDTH=16, // Width of the mini-scroll button. + BUTTON_HEIGHT=12, // Height of the mini-scroll button. + //LEFT_EDGE_OFFSET=2, // Offset from left edge for building shapes. + TEXT_X_OFFSET=18, // X offset to print "ready" text. + TEXT_Y_OFFSET=15, // Y offset to print "ready" text. + TEXT_COLOR=255, // Color to use for the "Ready" text. + //BUTTON_SPACING_OFFSET = 4, // spacing info for buttons + //LEFT_EDGE_OFFSET=0, // Offset from left edge for building shapes. + //BUTTON_SPACING_OFFSET = 0, // spacing info for buttons + + }; + + /* + ** This is the coordinate of the upper left corner that this side strip + ** uses for rendering. + */ + int X,Y; + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** Shape numbers for the shapes in the STRIP.SHP file. + */ + enum SideBarStipShapeEnums { + SB_BLANK, // The blank rectangle to use if there are no objects present. + SB_FRAME + }; + + /* + ** If this particular side strip needs to be redrawn, then this flag + ** will be true. + */ + unsigned IsToRedraw:1; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This controls the sidebar slide direction. If this is true, then the sidebar + ** will scroll downward -- revealing previous objects. + */ + unsigned IsScrollingDown:1; + + /* + ** If the sidebar is scrolling, then this flag is true. Otherwise it is false. + */ + unsigned IsScrolling:1; + + /* + ** This is the object (sidebar slot) that is flashing. Only one slot can be flashing + ** at any one instant. This is usually the result of a click on the slot and construction + ** has commenced. + */ + int Flasher; + + /* + ** As the sidebar scrolls up and down, this variable holds the index for the topmost + ** visible sidebar slot. + */ + int TopIndex; + + /* + ** This is the queued scroll direction and amount. The sidebar + ** will scroll the number of slots indicated by this value. This + ** value is set according to the scroll buttons. + */ + int Scroller; + + /* + ** The sidebar has smooth scrolling. This is the number of pixels the sidebar + ** has slide down. Thus, if this value were 5, then there would be 5 pixels of + ** the TopIndex-1 sidebar object visible. When the Slid value reaches 24, then + ** the value resets to zero and the TopIndex is decremented. For sliding in the + ** opposite direction, change the IsScrollingDown flag. + */ + int Slid; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + /* + ** Pointer to the shape data for small versions of the logos. These are used as + ** placeholder pieces on the side bar. + */ + static void const * LogoShapes; + + /* + ** This points to the animation sequence of frames used to mark the passage of time + ** as an object is undergoing construction. + */ + static void const * ClockShapes; + + /* + ** This points to the animation sequence which deals with special + ** shapes which handle non-production based icons. + */ + static void const * SpecialShapes[3]; + + /* + ** This is the last theater that the special palette remap table was loaded + ** for. If the current theater differs from this recorded value, then the + ** remap tables are reloaded. + */ + static TheaterType LastTheater; + + static ShapeButtonClass UpButton[COLUMNS]; + static ShapeButtonClass DownButton[COLUMNS]; + static SelectClass SelectButton[COLUMNS][MAX_VISIBLE]; + + /* + ** This points to the shapes that are used for the clock overlay. This displays + ** progress of construction. + */ + static char ClockTranslucentTable[(1+1)*256]; + + } Column[COLUMNS]; + + + /* + ** If the sidebar is active then this flag is true. + */ + unsigned IsSidebarActive:1; + + /* + ** This flag tells the rendering system that the sidebar needs to be redrawn. + */ + unsigned IsToRedraw:1; + + class SBGadgetClass: public GadgetClass { + public: +// SBGadgetClass(void) : GadgetClass(SIDE_X+8, SIDE_Y, SIDE_WIDTH-1, SIDE_HEIGHT-1, LEFTUP) {}; + SBGadgetClass(void) : GadgetClass(0,0,0,0,LEFTUP) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + /* + ** This is the button that is used to collapse and expand the sidebar. + ** These buttons must be available to derived classes, for Save/Load. + */ + static ShapeButtonClass Repair; + static ShapeButtonClass Upgrade; + static ShapeButtonClass Zoom; + static SBGadgetClass Background; + + /* + ** Pointer to the shape data for the sidebar + */ + static void const * SidebarShape1; + static void const * SidebarShape2; + + + private: + bool Activate_Repair(int control); + bool Activate_Upgrade(int control); + bool Activate_Demolish(int control); + bool Scroll(bool up, int column); + int Which_Column(RTTIType type); + + unsigned IsRepairActive:1; + unsigned IsUpgradeActive:1; + unsigned IsDemolishActive:1; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/SIDEBARGlyphx.CPP b/TIBERIANDAWN/SIDEBARGlyphx.CPP new file mode 100644 index 000000000..826731bd5 --- /dev/null +++ b/TIBERIANDAWN/SIDEBARGlyphx.CPP @@ -0,0 +1,833 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.cpv 2.13 02 Aug 1995 17:03:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBARGlyphx.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 14th, 2019 * + * * + * Last Update : March 14th, 2019 * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "SidebarGlyphx.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + +/*********************************************************************************************** + * SidebarGlyphxClass::SidebarGlyphxClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::SidebarGlyphxClass(void) : + SidebarPlayerPtr(NULL) +{ + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_Clear(HouseClass *player_ptr) +{ + SidebarPlayerPtr = player_ptr; + + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + + //Activate(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_IO(void) +{ + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + //if (IsSidebarActive) { + // IsSidebarActive = false; + // Activate(1); + //} +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarGlyphxClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Factory_Link(int factory, RTTIType type, int id) +{ + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Add(RTTIType type, int id, bool via_capture) +{ + int column; + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + //Activate(1); + return(true); + } + return(false); + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarGlyphxClass::AI(KeyNumType & input, int x, int y) +{ + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Recalc(void) +{ + Column[0].Recalc(); + Column[1].Recalc(); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::StripClass::StripClass(void) +{ + IsBuilding = false; + BuildableCount = 0; + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } + ParentSidebar = NULL; +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::StripClass::Init_Clear(void) +{ + IsBuilding = false; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + return(true); + } + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::AI(KeyNumType & input, int , int ) +{ + /* + ** This is needed as it's where units get queued for structure exit. ST -3/14/2019 12:03PM + */ + + + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending) { + switch (pending->What_Am_I()) { + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + + case RTTI_BUILDING: + if (!factory->Is_Blocked()) { + Speak(VOX_CONSTRUCTION); + } + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + return(false); +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech) { + ok = tech->Who_Can_Build_Me(true, false, ParentSidebar->SidebarPlayerPtr->Class->House) != NULL; + } else { + switch (Buildables[index].BuildableID) { + case SPC_ION_CANNON: + ok = ParentSidebar->SidebarPlayerPtr->IonCannon.Is_Present(); + break; + + case SPC_NUCLEAR_BOMB: + ok = ParentSidebar->SidebarPlayerPtr->NukeStrike.Is_Present(); + break; + + case SPC_AIR_STRIKE: + ok = ParentSidebar->SidebarPlayerPtr->AirStrike.Is_Present(); + break; + + default: + ok = false; + break; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + BuildableCount--; + index--; + + Buildables[BuildableCount].BuildableID = 0; + Buildables[BuildableCount].BuildableType = RTTI_NONE; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = false; + } + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + + + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Code_Pointers -- Converts classes pointers to savable representation * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Code_Pointers(void) +{ + if (SidebarPlayerPtr) { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)SidebarPlayerPtr->Class->House; + } else { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Decode_Pointers -- Converts classes savable representation to run-time * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Decode_Pointers(void) +{ + + if (*((HousesType*)&SidebarPlayerPtr) == HOUSE_NONE) { + SidebarPlayerPtr = NULL; + } else { + ((HouseClass *&)SidebarPlayerPtr) = HouseClass::As_Pointer(*((HousesType*)&SidebarPlayerPtr)); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Load(FileClass & file) +{ + ::new (this) SidebarGlyphxClass(); + + bool ok = Read_Object(this, sizeof(*this), file, false); + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + return ok; +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +extern SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr); + + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_Clear(player_ptr); + } +} + +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_IO(); + } +} + +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Abandon_Production(type, factory); + } + + return false; +} + +bool Sidebar_Glyphx_Add(RTTIType type, int id, HouseClass *player_ptr, bool via_capture) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Add(type, id, via_capture); + } + + return false; +} + +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Recalc(); + } +} + +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->AI(input, 0, 0); + } +} + +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Factory_Link(factory, type, id); + } + + return false; +} + +bool Sidebar_Glyphx_Save(FileClass &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Save(file); + } + return false; +} + +bool Sidebar_Glyphx_Load(FileClass &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Load(file); + } + return false; +} + +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Code_Pointers(); + } +} + +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Decode_Pointers(); + } +} + \ No newline at end of file diff --git a/TIBERIANDAWN/SIDEBARGlyphx.H b/TIBERIANDAWN/SIDEBARGlyphx.H new file mode 100644 index 000000000..fbce9786f --- /dev/null +++ b/TIBERIANDAWN/SIDEBARGlyphx.H @@ -0,0 +1,206 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.h_v 2.18 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_GLYPHX_H +#define SIDEBAR_GLYPHX_H + +#pragma once + +#include "function.h" +#include "power.h" +#include "factory.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + + +class SidebarGlyphxClass +{ + public: + + enum SideBarClassEnums { + COLUMNS=2 // Number of side strips on sidebar. + }; + + SidebarGlyphxClass(void); + + /* + ** Initialization + */ + void Init_Clear(HouseClass *player_ptr); // Clears all to known state + void Init_IO(void); // Inits button list + + void AI(KeyNumType & input, int x, int y); + + bool Abandon_Production(RTTIType type, int factory); + bool Add(RTTIType type, int ID, bool via_capture = false); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + bool Load(FileClass & file); + bool Save(FileClass & file); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + + void Set_Owner(StripClass & strip, int index); + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + + StripClass(void); + bool Add(RTTIType type, int ID, bool via_capture); + bool Abandon_Production(int factory); + bool AI(KeyNumType & input, int x, int y); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + bool Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(int type); + + void Set_Parent_Sidebar(SidebarGlyphxClass *parent) {ParentSidebar = parent;} + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + //void Code_Pointers(void); + //void Decode_Pointers(void); + + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + MAX_BUILDABLES = 30 // Maximum number of object types in sidebar. + }; + + SidebarGlyphxClass *ParentSidebar; + + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + } Column[COLUMNS]; + + + private: + int Which_Column(RTTIType type); + + HouseClass *SidebarPlayerPtr; +}; + + + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr = NULL); // Inits button list +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Add(RTTIType type, int ID, HouseClass *player_ptr = NULL, bool via_capture = false); +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input); +bool Sidebar_Glyphx_Save(FileClass &file, SidebarGlyphxClass *sidebar); +bool Sidebar_Glyphx_Load(FileClass &file, SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar); + + +#endif //SIDEBAR_GLYPHX_H \ No newline at end of file diff --git a/TIBERIANDAWN/SLIDER.CPP b/TIBERIANDAWN/SLIDER.CPP new file mode 100644 index 000000000..ceef5dac9 --- /dev/null +++ b/TIBERIANDAWN/SLIDER.CPP @@ -0,0 +1,405 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\slider.cpv 2.17 16 Oct 1995 16:51:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SliderClass::Action -- Handles input processing for the slider. * + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * SliderClass::Set_Thumb_Size -- Sets the size fo the thumb in "slider units". * + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * SliderClass::Step -- Steps the slider one value up or down. * + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "slider.h" + + +/*********************************************************************************************** + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * * + * This is the normal constructor for the slider gadget. * + * * + * INPUT: id -- The ID number to assign to this gadget. * + * x,y -- The pixel coordinate of the upper left corner for this gadget. * + * w,h -- The width and height of the slider gadget. The slider automatically * + * adapts for horizontal or vertical operation depending on which of these * + * dimensions is greater. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list) + : GaugeClass(id, x, y, w, h) +{ + BelongToList = belong_to_list ? true : false; + + PlusGadget = 0; + MinusGadget = 0; + if (!BelongToList) { + PlusGadget = new ShapeButtonClass(id, MixFileClass::Retrieve("BTN-PLUS.SHP"), X+Width+2, Y); + MinusGadget = new ShapeButtonClass(id, MixFileClass::Retrieve("BTN-MINS.SHP"), X-6, Y); + + if (PlusGadget) { + PlusGadget->Make_Peer(*this); + PlusGadget->Add(*this); + PlusGadget->Flag_To_Redraw(); + } + if (MinusGadget) { + MinusGadget->Make_Peer(*this); + MinusGadget->Add(*this); + MinusGadget->Flag_To_Redraw(); + } + } + Set_Thumb_Size(1); + Recalc_Thumb(); + + /* + ** Gauges have at least 2 colors, but sliders should only have one. + */ + IsColorized = 0; +} + + +SliderClass::~SliderClass(void) +{ + if (PlusGadget) { + delete PlusGadget; + PlusGadget = 0; + } + if (MinusGadget) { + delete MinusGadget; + MinusGadget = 0; + } +} + + +/*********************************************************************************************** + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * * + * This sets the maximum value that the slider can be set at. The maximum value controls * + * the size of the thumb and the resolution of the thumb's movement. * + * * + * INPUT: value -- The value to set for the slider's maximum. * + * OUTPUT: bool; Was the maximum value changed? A false indicates a set to the value it * + * is currently set to already. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Maximum(int value) +{ + if (GaugeClass::Set_Maximum(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Set_Thumb_Size -- Sets the size fo the thumb in "slider units". * + * * + * This routine will set the size of the thumb as it relates to the maximum value the * + * slider can achieve. This serves to display a proportionally sized thumb as well as * + * control how the slider "bumps" up or down. * + * * + * INPUT: value -- The new value of the thumb. It should never be larger than the slider * + * maximum. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Set_Thumb_Size(int value) +{ + Thumb = MIN(value, MaxValue); + Thumb = MAX(Thumb, 1); + Flag_To_Redraw(); + Recalc_Thumb(); +} + + +/*********************************************************************************************** + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * * + * This routine will set the thumb position for the slider. * + * * + * INPUT: value -- The position to set the slider. This position is relative to the maximum * + * value for the slider. * + * * + * OUTPUT: bool; Was the slider thumb position changed at all? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Value(int value) +{ + value = MIN(value, MaxValue-Thumb); + + if (GaugeClass::Set_Value(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * * + * This takes the current thumb logical size and starting value and calculates the pixel * + * size and starting offset accordingly. This function should be called whenever one of * + * these elements has changed. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Recalc_Thumb(void) +{ + int length = IsHorizontal ? Width : Height; + int size = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, Thumb)); + ThumbSize = MAX(size, 4); + int start = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, CurValue)); + ThumbStart = MIN(start, length-ThumbSize); +} + + +/*********************************************************************************************** + * SliderClass::Action -- Handles input processing for the slider. * + * * + * This routine is called when a qualifying input event has occured. This routine will * + * process that event and make any adjustments to the slider as necessary. * + * * + * INPUT: flags -- Flag bits that tell the input event that caused this function to * + * be called. * + * key -- Reference to the key that caused the input event. * + * OUTPUT: bool; Was the event consumed and further processing of the gadget list should be * + * aborted? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** Handle the mouse click in a special way. If the click was not on the thumb, then + ** jump the thumb position one "step" in the appropriate direction. Otherwise, let normal + ** processing take place -- the slider then "sticks" and the thumb moves according to + ** mouse position. + */ + if (flags & LEFTPRESS) { + int mouse; // Mouse pixel position. + int edge; // Edge of slider. + + if (IsHorizontal) { + mouse = Get_Mouse_X(); + edge = X; + } else { + mouse = Get_Mouse_Y(); + edge = Y; + } + edge += 1; + + /* + ** Clicking outside the thumb: invoke parent's Action to process flags etc, + ** but turn off the event & return true so processing stops at this button. + */ + if (mouse < edge+ThumbStart) { + Bump(true); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + if (mouse > edge+ThumbStart+ThumbSize) { + Bump(false); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + GaugeClass::Action(flags, key); + key = KN_NONE; + return(true); + } + } + } + + /* + ** CHANGE GAUGECLASS::ACTION -- REMOVE (LEFTRELEASE) FROM IF STMT + */ + return(GaugeClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * * + * This support function will bump the slider one "step" or the size of the thumb up or * + * down as specified. It is typically called when the slider is clicked outside of the * + * thumb region but still inside of the slider. * + * * + * INPUT: up -- Should the bump be to increase the current position? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Bump(int up) +{ + if (up) { + return(Set_Value(CurValue - Thumb)); + } + return(Set_Value(CurValue + Thumb)); +} + + +/*********************************************************************************************** + * SliderClass::Step -- Steps the slider one value up or down. * + * * + * This routine will move the slider thumb one step in the direction specified. * + * * + * INPUT: up -- Should the step be up (i.e., forward)? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Step(int up) +{ + if (up) { + return(Set_Value(CurValue - 1)); + } + return(Set_Value(CurValue + 1)); +} + + +/*********************************************************************************************** + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * * + * This will draw the thumb graphic for this slider. Sometimes the thumb requires special * + * drawing, thus the need for this function separate from the normal Draw_Me function. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: The mouse is guaranteed to be hidden when this routine is called. * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Draw_Thumb(void) +{ + if (IsHorizontal) { + Draw_Box(X+ThumbStart, Y, ThumbSize, Height, BOXSTYLE_GREEN_RAISED, true); + } else { + Draw_Box(X, Y+ThumbStart, Width, ThumbSize, BOXSTYLE_GREEN_RAISED, true); + } +} + + +/*********************************************************************************************** + * SliderClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * OUTPUT: bool; Was the gauge redrawn? * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Draw_Me(int forced) +{ + if (BelongToList) { + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, true); +// if (IsHorizontal) { +// LogicPage->Fill_Rect(X, Y+1, X+Width-1, Y+Height-2, 141); +// LogicPage->Draw_Line(X, Y, X+Width-1, Y, 140); // top +// LogicPage->Draw_Line(X, Y+Height, X+Width, Y+Height, 159); // bottom +// } else { +// LogicPage->Fill_Rect(X+1, Y, X+Width-2, Y+Height-1, 141); +// LogicPage->Draw_Line(X, Y, X, Y+Height, 140); // left +// LogicPage->Draw_Line(X+Width-1, Y, X+Width-1, Y+Height, 159); // right +// } + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + } + + /* + ** If it does not belong to a listbox... + */ + return(GaugeClass::Draw_Me(forced)); +} + + +/*********************************************************************************************** + * SliderClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * key -- The key value at the time of the event. * + * whom -- Which gadget is being touched. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Peer_To_Peer(unsigned flags, KeyNumType & , ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == PlusGadget) { + Step(false); + } + if (&whom == MinusGadget) { + Step(true); + } + } +} + + diff --git a/TIBERIANDAWN/SLIDER.H b/TIBERIANDAWN/SLIDER.H new file mode 100644 index 000000000..6e81b4000 --- /dev/null +++ b/TIBERIANDAWN/SLIDER.H @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\slider.h_v 2.16 16 Oct 1995 16:45:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "gauge.h" +#include "shapebtn.h" + +/*************************************************************************** + * SliderClass -- Like a Windows ListBox structure * + * * + * INPUT: int id-- id of gadget * + * int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * int belong_to_list -- does this slider go with a listclass? * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class SliderClass : public GaugeClass +{ + public: + SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + virtual ~SliderClass(void); +// static SliderClass * Create_One_Of(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + + virtual void Set_Thumb_Size(int value); + virtual int Set_Maximum(int value); + virtual int Set_Value(int); + virtual int Bump(int up); + virtual int Step(int up); + virtual int Draw_Me(int forced); + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + + virtual int Thumb_Pixels(void) { return (ThumbSize);} + + protected: + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + ShapeButtonClass * PlusGadget; + ShapeButtonClass * MinusGadget; + + /* + ** If I belong to a listbox, I have to draw myself differently... + **/ + unsigned BelongToList:1; + + /* + ** This is the logical size of the thumb. This value is used when drawing + ** the thumb imagery. It is also the amount that is bumped when the + ** Bump() function is called. (This value is in application units.) + */ + int Thumb; + + /* + ** This is the current thumb pixel size and starting offset from beginning + ** of slider region. (These values are in pixels.) + */ + int ThumbSize; + int ThumbStart; // x or y position for the thumb + + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Thumb(void); + + private: + void Recalc_Thumb(void); +}; + +#endif diff --git a/TIBERIANDAWN/SMUDGE.CPP b/TIBERIANDAWN/SMUDGE.CPP new file mode 100644 index 000000000..08d346364 --- /dev/null +++ b/TIBERIANDAWN/SMUDGE.CPP @@ -0,0 +1,405 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\smudge.cpv 2.18 16 Oct 1995 16:52:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * SmudgeClass::operator new -- Creator of smudge objects. * + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * SmudgeClass::Write_INI -- Writes the smudge data to an INI file. * + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * SmudgeClass::Validate -- validates smudge pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "smudge.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * SmudgeClass::VTable; + +HousesType SmudgeClass::ToOwn = HOUSE_NONE; + + +/*********************************************************************************************** + * SmudgeClass::Validate -- validates smudge pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int SmudgeClass::Validate(void) const +{ + int num; + + num = Smudges.ID(this); + if (num < 0 || num >= SMUDGE_MAX) { + Validate_Error("SMUDGE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * SmudgeClass::operator new -- Creator of smudge objects. * + * * + * This routine will allocate a smudge object from the smudge tracking pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a newly allocated smudge object. If one couldn't be * + * found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * SmudgeClass::operator new(size_t ) +{ + void * ptr = Smudges.Allocate(); + if (ptr) { + ((SmudgeClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * * + * This routine is used to remove the smudge from the tracking system. * + * * + * INPUT: ptr -- Pointer to the smudge to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::operator delete(void *ptr) +{ + if (ptr) { + ((SmudgeClass *)ptr)->IsActive = false; + } + Smudges.Free((SmudgeClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * * + * This is the typical constructor for smudge objects. If the position to place the * + * smudge is not given, then the smudge will be initialized in a limbo state. If the * + * smudge is placed on the map, then this operation causes the smudge object itself to be * + * deleted and special map values updated to reflect the presence of a smudge. * + * * + * INPUT: type -- The type of smudge to construct. * + * * + * pos -- The position to place the smudge. If -1, then the smudge is initialized * + * into a limbo state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeClass::SmudgeClass(SmudgeType type, COORDINATE pos, HousesType house) : + Class(&SmudgeTypeClass::As_Reference(type)) +{ + if (pos != -1) { + ToOwn = house; + if (!Unlimbo(pos)) { + Delete_This(); + } + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * * + * This routine is used during the scenario clearing process to initialize the smudge * + * object tracking system to a null state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Init(void) +{ + SmudgeClass *ptr; + + Smudges.Free_All(); + + ptr = new SmudgeClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * * + * This routine will place the smudge on the map. If the map cell allows. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the smudge marked successfully? Failure occurs if the smudge isn't * + * marked DOWN. * + * * + * WARNINGS: The smudge object is DELETED by this routine. * + * * + * HISTORY: * + * 09/22/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool SmudgeClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL origin = Coord_Cell(Coord); + + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CELL newcell = origin + w + (h*MAP_CELL_W); + if (Map.In_Radar(newcell)) { + CellClass * cell = &Map[newcell]; + + if (Class->IsBib) { + cell->Smudge = Class->Type; + cell->SmudgeData = w + (h*Class->Width); + cell->Owner = ToOwn; + } else { + if (cell->Is_Generally_Clear()) { + if (Class->IsCrater && cell->Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(cell->Smudge).IsCrater) { + cell->SmudgeData++; + cell->SmudgeData = (int)MIN((int)cell->SmudgeData, (int)4); + } + + if (cell->Smudge == SMUDGE_NONE) { + + /* + ** Special selection of a crater that starts as close to the + ** specified coordinate as possible. + */ + if (Class->IsCrater) { + cell->Smudge = (SmudgeType)(SMUDGE_CRATER1 + CellClass::Spot_Index(Coord)); + } else { + cell->Smudge = Class->Type; + } + cell->SmudgeData = 0; + } + } + } + + /* + ** Flag everything that might be overlapping this cell to redraw itself. + */ + cell->Redraw_Objects(); + } + } + } + + /* + ** Whether it was successful in placing, or not, delete the smudge object. It isn't + ** needed once the map has been updated with the proper smudge data. + */ + Delete_This(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * * + * This routine is used by the scenario loader to read the smudge data in an INI file and * + * create the appropriate smudge objects on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Sets the smudge data value as well. * + *=============================================================================================*/ +void SmudgeClass::Read_INI(char *buffer) +{ + char buf[128]; // Working string staging buffer. + + int len = strlen(buffer) + 2; + char * tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + SmudgeType smudge; // Smudge type. + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + smudge = SmudgeTypeClass::From_Name(strtok(buf, ",")); + if (smudge != SMUDGE_NONE) { + char * ptr = strtok(NULL, ","); + if (ptr) { + int data = 0; + CELL cell = atoi(ptr); + ptr = strtok(NULL, ","); + if (ptr) data = atoi(ptr); + new SmudgeClass(smudge, Cell_Coord(cell)); + if (Map[cell].Smudge == smudge && data) { + Map[cell].SmudgeData = data; + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * SmudgeClass::Write_INI -- Writes the smudge data to an INI file. * + * * + * This routine is used by the scenario editor to write the smudge data to an INI file. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Records the smudge data as well. * + *=============================================================================================*/ +void SmudgeClass::Write_INI(char *buffer) +{ + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing template data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Find all templates and write them to the file. + */ + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass const * stype = &SmudgeTypeClass::As_Reference(ptr->Smudge); + if (!stype->IsBib) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%d,%d", stype->IniName, index, ptr->SmudgeData); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * * + * This routine is used when a building is removed from the game. If there was any bib * + * attached, this routine will be called to disown the cells and remove the bib artwork. * + * * + * INPUT: cell -- The origin cell for this bib removal. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is actually working on a temporary bib object. It is created for the sole * + * purpose of calling this routine. It will be deleted immediately afterward. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Disown(CELL cell) +{ + Validate(); + if (Class->IsBib) { + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CellClass & cellptr = Map[cell + w + (h*MAP_CELL_W)]; + + if (cellptr.Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr.Overlay).IsWall) { + cellptr.Smudge = SMUDGE_NONE; + cellptr.SmudgeData = 0; + cellptr.Owner = HOUSE_NONE; + cellptr.Redraw_Objects(); + } + } + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/SMUDGE.H b/TIBERIANDAWN/SMUDGE.H new file mode 100644 index 000000000..eefdde403 --- /dev/null +++ b/TIBERIANDAWN/SMUDGE.H @@ -0,0 +1,105 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\smudge.h_v 2.16 16 Oct 1995 16:47:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SMUDGE_H +#define SMUDGE_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This is the transitory form for smudges. They exist as independent objects +** only in the transition stage from creation to placement upon the map. Once +** they are placed on the map, they merely become 'smudges' in the cell data. This +** object is then destroyed. +*/ +class SmudgeClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + SmudgeClass(SmudgeType type, COORDINATE pos=-1, HousesType house = HOUSE_NONE); + SmudgeClass(void) : Class(0) {}; + operator SmudgeType(void) const {return Class->Type;}; + virtual ~SmudgeClass(void) {if (GameActive) SmudgeClass::Limbo();}; + virtual RTTIType What_Am_I(void) const {return RTTI_SMUDGE;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(char *); + static void Write_INI(char *); + static char *INI_Name(void) {return "SMUDGE";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual bool Mark(MarkType); + virtual void Draw_It(int , int , WindowNumberType ) {}; + + void Disown(CELL cell); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + static HousesType ToOwn; + + /* + ** This is a pointer to the template object's class. + */ + SmudgeTypeClass const * const Class; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/TIBERIANDAWN/SOUNDDLG.CPP b/TIBERIANDAWN/SOUNDDLG.CPP new file mode 100644 index 000000000..838f0caa8 --- /dev/null +++ b/TIBERIANDAWN/SOUNDDLG.CPP @@ -0,0 +1,431 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sounddlg.cpv 2.17 16 Oct 1995 16:51:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SOUNDDLG.CPP * + * * + * Programmer : Maria del Mar McCready-Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "sounddlg.h" + +class MusicListClass : public ListClass +{ + public: + MusicListClass(int id, int x, int y, int w, int h) : + ListClass(id, x, y, w, h, TPF_6PT_GRAD|TPF_NOSHADOW, Hires_Retrieve("BTN-UP.SHP"), Hires_Retrieve("BTN-DN.SHP")) + {}; + virtual ~MusicListClass(void) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; +int SoundControlsClass::Init(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Option_Width =292 * factor; + Option_Height =146 * factor; + + Option_X =((SeenBuff.Get_Width() - Option_Width) / 2); + Option_Y =(SeenBuff.Get_Height() - Option_Height) / 2; + + Listbox_X =1 * factor; + Listbox_Y =54 * factor; + Listbox_W =290 * factor; + Listbox_H =73 * factor; + + Button_Width =85 * factor; + Button_X =Option_Width-(Button_Width + (7 * factor)); + Button_Y =130 * factor; + + Stop_X =5 * factor; + Stop_Y =129 * factor; + + Play_X =23 * factor; + Play_Y =129 * factor; + + OnOff_Width =25 * factor; + #ifdef GERMAN + Shuffle_X =79 * factor; + #else + Shuffle_X =91 * factor; + #endif + + Shuffle_Y =130 * factor; + + Repeat_X =166 * factor; + Repeat_Y =130 * factor; + + MSlider_X =147 * factor; + MSlider_Y =28 * factor; + MSlider_W =108 * factor; + MSlider_Height =5 * factor; + + FXSlider_X =147 * factor; + FXSlider_Y =40 * factor; + FXSlider_W =108 * factor; + FXSlider_Height=5 * factor; + return(factor); +} + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void SoundControlsClass::Process(void) +{ +// ThemeType theme; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + Init(); + /* + ** List box that holds the score text strings. + */ + MusicListClass listbox(0, Option_X+Listbox_X, Option_Y+Listbox_Y, Listbox_W, Listbox_H); + + /* + ** Return to options menu button. + */ + TextButtonClass returnto(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_6PT_GRAD|TPF_NOSHADOW, +#ifdef FRENCH + Option_X+Button_X-8*2, Option_Y+Button_Y, Button_Width+11*2); +#else + Option_X+Button_X, Option_Y+Button_Y, Button_Width); +#endif + + /* + ** Stop playing button. + */ + char filename[30]; + if (factor == 1) + strcpy(filename,"BTN-ST.SHP"); + else + strcpy(filename,"BTN-STH.SHP"); + ShapeButtonClass stopbtn(BUTTON_STOP, MixFileClass::Retrieve(filename), + Option_X+Stop_X, Option_Y+Stop_Y); + + /* + ** Start playing button. + */ + if (factor == 1) + strcpy(filename,"BTN-PL.SHP"); + else + strcpy(filename,"BTN-PLH.SHP"); + + ShapeButtonClass playbtn(BUTTON_PLAY, MixFileClass::Retrieve(filename), + Option_X+Play_X, Option_Y+Play_Y); + + /* + ** Shuffle control. + */ + TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_6PT_GRAD|TPF_NOSHADOW, + Option_X+Shuffle_X, Option_Y+Shuffle_Y, OnOff_Width); + + /* + ** Repeat control. + */ + TextButtonClass repeatbtn(BUTTON_REPEAT, TXT_OFF, TPF_6PT_GRAD|TPF_NOSHADOW, Option_X+Repeat_X, Option_Y+Repeat_Y, OnOff_Width); + + /* + ** Music volume slider. + */ + SliderClass music(SLIDER_MUSIC, Option_X+MSlider_X, Option_Y+MSlider_Y, MSlider_W, MSlider_Height); + + /* + ** Sound volume slider. + */ + SliderClass sound(SLIDER_SOUND, Option_X+FXSlider_X, Option_Y+FXSlider_Y, FXSlider_W, FXSlider_Height); + + /* + ** Causes left mouse clicks inside the dialog area, but not on any + ** particular button, to be ignored. + */ + GadgetClass area(Option_X, Option_Y, Option_Width, Option_Height, GadgetClass::LEFTPRESS); + + /* + ** Causes right clicks anywhere or left clicks outside of the dialog + ** box area to be the same a clicking the return to game options button. + */ + ControlClass ctrl(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::RIGHTPRESS|GadgetClass::LEFTPRESS); + + /* + ** The repeat and shuffle buttons are of the toggle type. They toggle + ** between saying "on" and "off". + */ + shufflebtn.IsToggleType = true; + if (Options.IsScoreShuffle) { + shufflebtn.Turn_On(); + } else { + shufflebtn.Turn_Off(); + } + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + + repeatbtn.IsToggleType = true; + if (Options.IsScoreRepeat) { + repeatbtn.Turn_On(); + } else { + repeatbtn.Turn_Off(); + } + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + + /* + ** Set the initial values of the sliders. + */ + music.Set_Maximum(255); + music.Set_Thumb_Size(16); + music.Set_Value(Options.ScoreVolume); + sound.Set_Maximum(255); + sound.Set_Thumb_Size(16); + sound.Set_Value(Options.Volume); + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. + */ + GadgetClass * optionsbtn = &returnto; + listbox.Add_Tail(*optionsbtn); + stopbtn.Add_Tail(*optionsbtn); + playbtn.Add_Tail(*optionsbtn); + shufflebtn.Add_Tail(*optionsbtn); + repeatbtn.Add_Tail(*optionsbtn); + music.Add_Tail(*optionsbtn); + sound.Add_Tail(*optionsbtn); + area.Add_Tail(*optionsbtn); + ctrl.Add_Tail(*optionsbtn); + + /* + ** Add all the themes to the list box. The list box entries are constructed + ** and then stored into allocated EMS memory blocks. + */ +// char buffer[100]; + for (ThemeType index = THEME_FIRST; index < Theme.Max_Themes(); index++) { + if (Theme.Is_Allowed(index)) { + int length = Theme.Track_Length(index); + char const * fullname = Theme.Full_Name(index); + + void * ptr = new char [sizeof(100)]; + if (ptr) { + sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s", index, listbox.Count()+1, length / 60, length % 60, fullname); + listbox.Add_Item((char const *)ptr); + } + + if (Theme.What_Is_Playing() == index) { + listbox.Set_Selected_Index(listbox.Count()-1); + } + } + } + static int _tabs[] = { + 55*factor, 72*factor, 90*factor + }; + listbox.Set_Tabs(_tabs); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(Option_X, Option_Y, Option_Width, Option_Height); + + Draw_Caption(TXT_SOUND_CONTROLS, Option_X, Option_Y, Option_Width); + + /* + ** Draw the Music, Speech & Sound titles. + */ + Fancy_Text_Print(TXT_MUSIC_VOLUME, Option_X+MSlider_X-5, Option_Y+MSlider_Y-2, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + Fancy_Text_Print(TXT_SOUND_VOLUME, Option_X+FXSlider_X-5, Option_Y+FXSlider_Y-2, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + Fancy_Text_Print(TXT_SHUFFLE, Option_X+Shuffle_X-5, Option_Y+Shuffle_Y+1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + Fancy_Text_Print(TXT_REPEAT, Option_X+Repeat_X-5, Option_Y+Repeat_Y+1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + optionsbtn->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = optionsbtn->Input(); + + /* + ** Process Input. + */ + switch (input) { + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + process = false; + break; + + /* + ** Control music volume. + */ + case SLIDER_MUSIC|KN_BUTTON: + Options.Set_Score_Volume(music.Get_Value()); + break; + + /* + ** Control sound volume. + */ + case SLIDER_SOUND|KN_BUTTON: + Options.Set_Sound_Volume(sound.Get_Value(), true); + break; + + case BUTTON_LISTBOX|KN_BUTTON: +// Mono_Printf ("%d %s Listbox was pressed.\r",__LINE__, __FILE__); + break; + + /* + ** Stop all themes from playing. + */ + case BUTTON_STOP|KN_BUTTON: + Theme.Queue_Song(THEME_NONE); + break; + + /* + ** Start the currently selected theme to play. + */ + case KN_SPACE: + case BUTTON_PLAY|KN_BUTTON: + if (listbox.Count()) { + Theme.Queue_Song( (ThemeType)*((unsigned char *)listbox.Current_Item()) ); + } + break; + + /* + ** Toggle the shuffle button. + */ + case BUTTON_SHUFFLE|KN_BUTTON: + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Shuffle(shufflebtn.IsOn); + break; + + /* + ** Toggle the repeat button. + */ + case BUTTON_REPEAT|KN_BUTTON: + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Repeat(repeatbtn.IsOn); + break; + } + } + + /* + ** If the score volume was turned all the way down, then actually + ** stop the scores from being played. + */ + if (!Options.ScoreVolume) { + Theme.Stop(); + } + + /* + ** Save them settings - you know it makes sense + */ + Options.Save_Settings(); // save new value + + /* + ** Free the items from the list box. + */ + while (listbox.Count()) { + char const * ptr = listbox.Get_Item(0); + listbox.Remove_Item(ptr); + delete [] (void*)ptr; + } +} + + +void MusicListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print((char *)Add_Long_To_Pointer(List[index], 1), x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print((char *)Add_Long_To_Pointer(List[index], 1), x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} diff --git a/TIBERIANDAWN/SOUNDDLG.H b/TIBERIANDAWN/SOUNDDLG.H new file mode 100644 index 000000000..3fc5cde8b --- /dev/null +++ b/TIBERIANDAWN/SOUNDDLG.H @@ -0,0 +1,156 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sounddlg.h_v 2.18 16 Oct 1995 16:46:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUNDDLG_H +#define SOUNDDLG_H + +#include "gadget.h" + +class SoundControlsClass +{ + enum SoundControlsClassEnums { +#ifdef FRENCH + OPTION_WIDTH=308, +#else + OPTION_WIDTH=292, +#endif + OPTION_HEIGHT=146, + + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + + LISTBOX_X=1, + LISTBOX_Y=54, + LISTBOX_W=290, + LISTBOX_H=72, + + BUTTON_WIDTH=85, + BUTTON_X=OPTION_WIDTH-(BUTTON_WIDTH+7), // Options button x pos + BUTTON_Y=130, // Options button y pos + + STOP_X=5, // Stop button X. + STOP_Y=129, // Stop button Y. + + PLAY_X=23, + PLAY_Y=129, + + ONOFF_WIDTH=25, +#ifdef GERMAN + SHUFFLE_X=79,//BGA:91, +#else +#ifdef FRENCH + SHUFFLE_X=99, +#else + SHUFFLE_X=91, +#endif +#endif + SHUFFLE_Y=130, + +#ifdef FRENCH + REPEAT_X=174, +#else + REPEAT_X=166, +#endif + REPEAT_Y=130, + + MSLIDER_X=147, + MSLIDER_Y=28, + MSLIDER_W=108, + MSLIDER_HEIGHT=5, + + FXSLIDER_X=147, + FXSLIDER_Y=40, + FXSLIDER_W=108, + FXSLIDER_HEIGHT=5, + + BUTTON_STOP = 605, + BUTTON_PLAY, + BUTTON_SHUFFLE, + BUTTON_REPEAT, + BUTTON_OPTIONS, + SLIDER_MUSIC, +// SLIDER_SPEECH, + SLIDER_SOUND, + BUTTON_LISTBOX, + }; + + public: + SoundControlsClass(void) {} + void Process(void); + int Init(void); + + private: + + int Option_Width; + int Option_Height; + + int Option_X; + int Option_Y; + + int Listbox_X; + int Listbox_Y; + int Listbox_W; + int Listbox_H; + + int Button_Width; + int Button_X; + int Button_Y; + + int Stop_X; + int Stop_Y; + + int Play_X; + int Play_Y; + + int OnOff_Width; + + int Shuffle_X; + int Shuffle_Y; + + int Repeat_X; + int Repeat_Y; + + int MSlider_X; + int MSlider_Y; + int MSlider_W; + int MSlider_Height; + + int FXSlider_X; + int FXSlider_Y; + int FXSlider_W; + int FXSlider_Height; + +}; + +#endif diff --git a/TIBERIANDAWN/SPECIAL.CPP b/TIBERIANDAWN/SPECIAL.CPP new file mode 100644 index 000000000..1b0b1c6ad --- /dev/null +++ b/TIBERIANDAWN/SPECIAL.CPP @@ -0,0 +1,271 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\special.cpv 1.4 16 Oct 1995 16:50:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/27/95 * + * * + * Last Update : May 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define OPTION_WIDTH 236 +#define OPTION_HEIGHT 162 +#define OPTION_X ((320 - OPTION_WIDTH) / 2) +#define OPTION_Y (200 - OPTION_HEIGHT) / 2 + +void Special_Dialog(void) +{ + SpecialClass oldspecial = Special; + GadgetClass * buttons = NULL; + static struct { + int Description; + int Setting; + CheckBoxClass * Button; + } _options[] = { +// {TXT_DEFENDER_ADVANTAGE, 0, 0}, + {TXT_SEPARATE_HELIPAD, 0, 0}, + {TXT_VISIBLE_TARGET, 0, 0}, + {TXT_TREE_TARGET, 0, 0}, + {TXT_MCV_DEPLOY, 0, 0}, + {TXT_SMART_DEFENCE, 0, 0}, + {TXT_THREE_POINT, 0, 0}, +// {TXT_TIBERIUM_GROWTH, 0, 0}, +// {TXT_TIBERIUM_SPREAD, 0, 0}, + {TXT_TIBERIUM_FAST, 0, 0}, + {TXT_ROAD_PIECES, 0, 0}, + {TXT_SCATTER, 0, 0}, + {TXT_SHOW_NAMES, 0, 0}, + }; + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, OPTION_X+5, OPTION_Y+OPTION_HEIGHT-15); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, OPTION_X+OPTION_WIDTH-50, OPTION_Y+OPTION_HEIGHT-15); + buttons = &ok; + cancel.Add(*buttons); + int index; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + _options[index].Button = new CheckBoxClass(100+index, OPTION_X+7, OPTION_Y+20+(index*10)); + if (_options[index].Button) { + _options[index].Button->Add(*buttons); + + bool value = false; + switch (_options[index].Description) { + case TXT_SEPARATE_HELIPAD: + value = Special.IsSeparate; + break; + + case TXT_SHOW_NAMES: + value = Special.IsNamed; + break; + + case TXT_DEFENDER_ADVANTAGE: + value = Special.IsDefenderAdvantage; + break; + + case TXT_VISIBLE_TARGET: + value = Special.IsVisibleTarget; + break; + + case TXT_TREE_TARGET: + value = Special.IsTreeTarget; + break; + + case TXT_MCV_DEPLOY: + value = Special.IsMCVDeploy; + break; + + case TXT_SMART_DEFENCE: + value = Special.IsSmartDefense; + break; + + case TXT_THREE_POINT: + value = Special.IsThreePoint; + break; + + case TXT_TIBERIUM_GROWTH: + value = Special.IsTGrowth; + break; + + case TXT_TIBERIUM_SPREAD: + value = Special.IsTSpread; + break; + + case TXT_TIBERIUM_FAST: + value = Special.IsTFast; + break; + + case TXT_ROAD_PIECES: + value = Special.IsRoad; + break; + + case TXT_SCATTER: + value = Special.IsScatter; + break; + } + + _options[index].Setting = value; + if (value) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Draw_Caption(TXT_SPECIAL_OPTIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + Fancy_Text_Print(_options[index].Description, _options[index].Button->X+10, _options[index].Button->Y, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_ESC: + case 200|KN_BUTTON: + process = false; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + switch (_options[index].Description) { + case TXT_SEPARATE_HELIPAD: + oldspecial.IsSeparate = _options[index].Setting; + break; + + case TXT_SHOW_NAMES: + oldspecial.IsNamed = _options[index].Setting; + break; + + case TXT_DEFENDER_ADVANTAGE: + oldspecial.IsDefenderAdvantage = _options[index].Setting; + break; + + case TXT_VISIBLE_TARGET: + oldspecial.IsVisibleTarget = _options[index].Setting; + break; + + case TXT_TREE_TARGET: + oldspecial.IsTreeTarget = _options[index].Setting; + break; + + case TXT_MCV_DEPLOY: + oldspecial.IsMCVDeploy = _options[index].Setting; + break; + + case TXT_SMART_DEFENCE: + oldspecial.IsSmartDefense = _options[index].Setting; + break; + + case TXT_THREE_POINT: + oldspecial.IsThreePoint = _options[index].Setting; + break; + + case TXT_TIBERIUM_GROWTH: + oldspecial.IsTGrowth = _options[index].Setting; + break; + + case TXT_TIBERIUM_SPREAD: + oldspecial.IsTSpread = _options[index].Setting; + break; + + case TXT_TIBERIUM_FAST: + oldspecial.IsTFast = _options[index].Setting; + break; + + case TXT_ROAD_PIECES: + oldspecial.IsRoad = _options[index].Setting; + break; + + case TXT_SCATTER: + oldspecial.IsScatter = _options[index].Setting; + break; + } + } + OutList.Add(EventClass(oldspecial)); + break; + + case 201|KN_BUTTON: + process = false; + break; + + case KN_NONE: + break; + + default: + index = (input & ~KN_BUTTON) - 100; + if ((unsigned)index < sizeof(_options)/sizeof(_options[0])) { + _options[index].Setting = (_options[index].Setting == false); + if (_options[index].Setting) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + break; + } + } + + Map.Revert_Mouse_Shape(); + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + + + + diff --git a/TIBERIANDAWN/SPECIAL.H b/TIBERIANDAWN/SPECIAL.H new file mode 100644 index 000000000..80db3cdb6 --- /dev/null +++ b/TIBERIANDAWN/SPECIAL.H @@ -0,0 +1,265 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\special.h_v 2.15 16 Oct 1995 16:47:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/95 * + * * + * Last Update : February 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +class SpecialClass +{ + public: + void Init(void) { + IsScrollMod = false; + IsGross = false; + IsEasy = false; + IsDifficult = false; + IsSpeedBuild = false; + IsDefenderAdvantage = true; + IsVisibleTarget = false; + IsVariation = false; + IsJurassic = false; + IsJuvenile = false; + IsSmartDefense = false; + IsTreeTarget = false; + IsMCVDeploy = false; + IsVisceroids = false; + IsMonoEnabled = false; + IsInert = false; + IsShowPath = false; + IsThreePoint = false; + IsTGrowth = true; + IsTSpread = true; + IsTFast = true; + IsRoad = false; + IsScatter = false; + IsCaptureTheFlag = false; + IsNamed = false; + IsFromInstall = false; + IsSeparate = false; + IsFromWChat = false; + IsEarlyWin = false; + HealthBarDisplayMode = HB_SELECTED; + ResourceBarDisplayMode = RB_SELECTED; + } + + /* + ** Is the game flagged for easy mode? + */ + unsigned IsEasy:1; + + /* + ** Is the game flagged for difficult? + */ + unsigned IsDifficult:1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild:1; + + /* + ** If the player can build the helipad separate from the helipad and + ** helicopter combo, then this flag will be true. + */ + unsigned IsSeparate:1; + + /* + ** If the defender has the advantage then this will be true. This flag + ** allows the defender to have a better advantage in combat than the + ** attacker. Moving units will not be able to dish out or take as much + ** damage when this flag is true. + */ + unsigned IsDefenderAdvantage:1; + + /* + ** If civilian structures are to have a name, then this flag will be + ** set to true. The default case is to just use generic names for + ** civilians. + */ + unsigned IsNamed:1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall:1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag:1; + + /* + ** Is target selecting by other human opponents visible to the player? + */ + unsigned IsVisibleTarget:1; + + /* + ** If human generated sound effects are to be used, then this + ** flag will be true. + */ + unsigned IsJuvenile:1; + + /* + ** If friendly units should return fire when fired upon, set this + ** flag to true. The default is only for the enemy units to do this. + */ + unsigned IsSmartDefense:1; + + /* + ** If targeting of trees is allowed, then this flag will be true. + */ + unsigned IsTreeTarget:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + */ + unsigned IsMCVDeploy:1; + + /* + ** Controls whether or not visceroids spawn + */ + unsigned IsVisceroids:1; + + /* + ** If the monochrome debugging output is enabled, then this flag will be true. + */ + unsigned IsMonoEnabled:1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert:1; + + /* + ** When this flag is true, the computer findpath algorithm reveals the route being + ** examined. This is used to trace findpath bugs. + */ + unsigned IsShowPath:1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint:1; + + /* + ** If Tiberium is allowed to grow, then this flag will be true. + */ + unsigned IsTGrowth:1; + + /* + ** If Tiberium is allowed to spread, then this flag will be true. + */ + unsigned IsTSpread:1; + + /* + ** This controls whether Tiberium grows&spreads quickly or not. + */ + unsigned IsTFast:1; + + /* + ** This flag controls whether the road additional pieces are added to + ** the bottom of buildings. If true, then the roads are NOT added. + */ + unsigned IsRoad:1; + + /* + ** Controls whether units (especially infantry) will scatter when there + ** is an immediate threat. This gives infantry a "mind of their own" when + ** it comes to self preservation. If set to false, then units will not + ** scatter. + */ + unsigned IsScatter:1; + + /* + ** Special bonus scenario enabled. + */ + unsigned IsJurassic:1; + + /* + ** Are score variations allowed? + */ + unsigned IsVariation:1; + + /* + ** If the gross human splatter marks should be present. + */ + unsigned IsGross:1; + + /* + ** Disables scrolling over the "options" and "sidebar" tabs. + */ + unsigned IsScrollMod:1; + + /* + ** Flag that we were spawned from WChat. + */ + unsigned IsFromWChat:1; + + /* + ** New anti-griefing early win mode. ST - 1/31/2020 3:42PM + */ + unsigned IsEarlyWin:1; + + /* + ** Health bar display mode + */ + enum eHealthBarDisplayMode + { + HB_DAMAGED = 0, + HB_ALWAYS, + HB_SELECTED + } HealthBarDisplayMode; + + /* + ** Resource bar display mode + */ + enum eResourceBarDisplayMode + { + RB_SELECTED = 0, + RB_ALWAYS, + } ResourceBarDisplayMode; + + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[128]; +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/STAGE.H b/TIBERIANDAWN/STAGE.H new file mode 100644 index 000000000..2c39fb55c --- /dev/null +++ b/TIBERIANDAWN/STAGE.H @@ -0,0 +1,97 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\stage.h_v 2.15 16 Oct 1995 16:45:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STAGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 17, 1994 * + * * + * Last Update : June 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STAGE_H +#define STAGE_H + +class StageClass { + + /* + ** This handles the animation stage of the object. This includes smoke, walking, + ** flapping, and rocket flames. + */ + unsigned short Stage; + + /* + ** This is the countdown timer for stage animation. When this counts down + ** to zero, then the stage increments by one and the time cycle starts + ** over again. + */ + unsigned char StageTimer; + + /* + ** This is the value to assign the StageTimer whenever it needs to be reset. Thus, + ** this value is the control of how fast the stage value increments. + */ + unsigned char Rate; + + public: + StageClass(void) { + StageTimer = 0; + Stage = 0; + Rate = 0; + }; + + int Fetch_Stage(void) const {return Stage;}; + int Fetch_Rate(void) const {return Rate;}; + void Set_Stage(int stage) {Stage = stage;}; + void Set_Rate(unsigned char rate) {Rate = StageTimer = rate;}; + void AI(void) {}; + bool Graphic_Logic(void) { + if (Rate) { + StageTimer--; + if (!StageTimer) { + Stage++; + StageTimer = Rate; + return true; + } + } + return false; + }; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const { + mono->Set_Cursor(56, 7); + mono->Printf("%3d[%d]", Stage, Rate); + }; + #endif + + /* + ** File I/O. + */ + void Code_Pointers(void) { return; } + void Decode_Pointers(void) { return; } +}; + + +#endif diff --git a/TIBERIANDAWN/STARTUP.CPP b/TIBERIANDAWN/STARTUP.CPP new file mode 100644 index 000000000..bc7df7ef1 --- /dev/null +++ b/TIBERIANDAWN/STARTUP.CPP @@ -0,0 +1,872 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\startup.cpv 2.17 16 Oct 1995 16:48:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 3, 1994 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Delete_Swap_Files -- Deletes previously existing swap files. * + * Prog_End -- Cleans up library systems in prep for game exit. * + * main -- Initial startup routine (preps library systems). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include "ccdde.h" + +bool Read_Private_Config_Struct(char *profile, NewConfigType *config); +void Delete_Swap_Files(void); +void Print_Error_End_Exit(char *string); +void Print_Error_Exit(char *string); +WinTimerClass *WinTimer; +extern void Create_Main_Window ( HANDLE instance , int command_show , int width , int height); + +extern bool ReadyToQuit; +void Read_Setup_Options(RawFileClass *config_file); + +bool VideoBackBufferAllowed = true; +void Check_From_WChat(char *wchat_name); +bool SpawnedFromWChat = false; +bool ProgEndCalled = false; + + +extern "C"{ + bool __cdecl Detect_MMX_Availability(void); + void __cdecl Init_MMX(void); +} + + + +#if (0) +char WibbleBuffer[1024*1024]; + +void CD_Test(void) +{ + HANDLE handle; + DWORD size; + + handle= CreateFile("e:\\scores.mix", GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle== INVALID_HANDLE_VALUE){ + return; + } + + unsigned bytes_read; + + do{ + bytes_read = ReadFile (handle , WibbleBuffer , 1024*1024, &size, NULL); + + }while(size == 1024*1024); + + + CloseHandle (handle); +} +#endif //(0) + + + +/*********************************************************************************************** + * main -- Initial startup routine (preps library systems). * + * * + * This is the routine that is first called when the program starts up. It basically * + * handles the command line parsing and setting up library systems. * + * * + * INPUT: argc -- Number of command line arguments. * + * * + * argv -- Pointer to array of comman line argument strings. * + * * + * OUTPUT: Returns with execution failure code (if any). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ + +HINSTANCE ProgramInstance; +extern BOOL CC95AlreadyRunning; +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); + +void Check_Use_Compressed_Shapes (void); +extern void DLL_Shutdown(void); + + +BOOL WINAPI DllMain(HINSTANCE instance, unsigned int fdwReason, void *lpvReserved) +{ + lpvReserved; + + switch (fdwReason) { + + case DLL_PROCESS_ATTACH: + ProgramInstance = instance; + break; + + case DLL_PROCESS_DETACH: + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + DLL_Shutdown(); + + MixFileClass::Free_All(); + + Uninit_Game(); + + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + + + return true; +} + + +//int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char * command_line , int command_show ) +//{ +// Heap_Dump_Check( "first thing in main" ); +// malloc(1); + +int DLL_Startup(const char * command_line_in) +{ + RunningAsDLL = true; + int command_show = SW_HIDE; + HINSTANCE instance = ProgramInstance; + char command_line[1024]; + strcpy(command_line, command_line_in); + + CCDebugString ("C&C95 - Starting up.\n"); + + + //WindowsTimer = new WinTimerClass(60,FALSE); + //CD_Test(); + + + + /* + ** These values return 0x47 if code is working correctly + */ +// int temp = Desired_Facing256 (1070, 5419, 1408, 5504); + + + + /* + ** If we are already running then switch to the existing process and exit + */ + SpawnedFromWChat = false; + + if (CC95AlreadyRunning) { //Set in the DDEServer constructor + //MessageBox (NULL, "Error - attempt to restart C&C95 when already running.", "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + + HWND ccwindow; + ccwindow = FindWindow ("Command & Conquer", "Command & Conquer"); + if (ccwindow){ + SetForegroundWindow ( ccwindow ); + ShowWindow ( ccwindow, SW_RESTORE ); + } + + return (EXIT_SUCCESS); + } + + // ST - 3/6/2019 1:36PM + //DDSCAPS surface_capabilities; + + if (Ram_Free(MEM_NORMAL) < 5000000) { +#ifdef GERMAN + printf("Zuwenig Hauptspeicher verfgbar.\n"); +#else +#ifdef FRENCH + printf("M‚moire vive (RAM) insuffisante.\n"); +#else + printf("Insufficient RAM available.\n"); +#endif +#endif + return(EXIT_FAILURE); + } + + + //void *test_buffer = Alloc(20,MEM_NORMAL); + + //memset ((char*)test_buffer, 0, 21); + + //Free(test_buffer); + + + int argc; //Command line argument count + unsigned command_scan; + char command_char; + char * argv[20]; //Pointers to command line arguments + char path_to_exe[280]; + + ProgramInstance = instance; + + /* + ** Get the full path to the .EXE + */ + GetModuleFileName (instance, &path_to_exe[0], 280); + + /* + ** First argument is supposed to be a pointer to the .EXE that is running + ** + */ + argc=1; //Set argument count to 1 + argv[0]=&path_to_exe[0]; //Set 1st command line argument to point to full path + + /* + ** Get pointers to command line arguments just like if we were in DOS + ** + ** The command line we get is cr/zero? terminated. + ** + */ + + command_scan=0; + + do { + /* + ** Scan for non-space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ){ + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + bool in_quotes = false; + do { + command_char = *( command_line+command_scan++ ); + if (command_char == '"') { + in_quotes = !in_quotes; + } + } while ( (in_quotes || command_char!=' ') && command_char != 0 && command_char!=13 ); + + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + + + + /* + ** Remember the current working directory and drive. + */ +#if (0) //PG_TO_FIX + unsigned olddrive; + char oldpath[MAX_PATH]; + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + + /* + ** Change directory to the where the executable is located. Handle the + ** case where there is no path attached to argv[0]. + */ + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + _splitpath(argv[0], drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = ('A' + olddrive)-1; + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); +#endif + +#ifdef JAPANESE + ForceEnglish = false; +#endif + if (Parse_Command_Line(argc, argv)) { + + WindowsTimer = new WinTimerClass(60,FALSE); + + int time_test = WindowsTimer->Get_System_Tick_Count(); + Sleep (1000); + if (WindowsTimer->Get_System_Tick_Count() == time_test){ +#ifdef FRENCH + MessageBox(0, "Error - L'horloge systŠme n'a pas pu s'initialiser en raison de l'instabilit‚ du sytŠme. Vous devez red‚marrer Windows.", "Command & Conquer" , MB_OK|MB_ICONSTOP); +#else +#ifdef GERMAN + MessageBox(0, "Fehler - das Timer-System konnte aufgrund einer Instabilit„t des Systems nicht initialisiert werden. Bitte starten Sie Windows neu.", "Command & Conquer", MB_OK|MB_ICONSTOP); +#else + MessageBox(0, "Error - Timer system failed to start due to system instability. You need to restart Windows.", "Command & Conquer", MB_OK|MB_ICONSTOP); +#endif //GERMAN +#endif //FRENCH + return(EXIT_FAILURE); + } + + RawFileClass cfile("CONQUER.INI"); + +#ifdef JAPANESE + //////////////////////////////////////if(!ForceEnglish) KBLanguage = 1; +#endif + + + /* + ** Check for existance of MMX support on the processor + */ + if (Detect_MMX_Availability()){ + //MessageBox(NULL, "MMX extensions detected - enabling MMX support.", "Command & Conquer",MB_ICONEXCLAMATION|MB_OK); + MMXAvailable = true; + } + + /* + ** If there is loads of memory then use uncompressed shapes + */ + Check_Use_Compressed_Shapes(); + + /* + ** If there is not enough disk space free, dont allow the product to run. + */ + if (Disk_Space_Available() < INIT_FREE_DISK_SPACE) { +#ifdef GERMAN + char disk_space_message [512]; + sprintf (disk_space_message, "Nicht genug Festplattenplatz fr Command & Conquer.\nSie brauchen %d MByte freien Platz auf der Festplatte.", (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + MessageBox(NULL, disk_space_message, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); +#endif +#ifdef FRENCH + char disk_space_message [512]; + sprintf (disk_space_message, "Espace disque insuffisant pour lancer Command & Conquer.\nVous devez disposer de %d Mo d'espace disponsible sur disque dur.", (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + MessageBox(NULL, disk_space_message, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); +#endif +#if !(FRENCH | GERMAN) + int reply = MessageBox(NULL, "Warning - you are critically low on free disk space for virtual memory and save games. Do you want to play C&C anyway?", "Command & Conquer", MB_ICONQUESTION|MB_YESNO); + if (reply == IDNO){ + + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); + } + +#endif + } + +#if (0) // ST - 1/2/2019 5:50PM + CDFileClass::Set_CD_Drive (CDList.Get_First_CD_Drive()); +#endif + if (cfile.Is_Available()) { + +#ifndef NOMEMCHECK + char * cdata = (char *)Load_Alloc_Data(cfile); + Read_Private_Config_Struct(cdata, &NewConfig); + delete [] cdata; +#else + Read_Private_Config_Struct((char *)Load_Alloc_Data(cfile), &NewConfig); +#endif + Read_Setup_Options( &cfile ); + + CCDebugString ("C&C95 - Creating main window.\n"); + + Create_Main_Window( instance , command_show , ScreenWidth , ScreenHeight ); + + CCDebugString ("C&C95 - Initialising audio.\n"); + + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + + Palette = new(MEM_CLEAR) unsigned char[768]; + + BOOL video_success = FALSE; + CCDebugString ("C&C95 - Setting video mode.\n"); + /* + ** Set 640x400 video mode. If its not available then try for 640x480 + */ + if (ScreenHeight == 400){ + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)){ + video_success = TRUE; + }else{ + if (Set_Video_Mode (MainWindow, ScreenWidth, 480, 8)){ + video_success = TRUE; + ScreenHeight = 480; + } + } + }else{ + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)){ + video_success = TRUE; + } + } + + if (!video_success){ + CCDebugString ("C&C95 - Failed to set video mode.\n"); + MessageBox(MainWindow, Text_String(TXT_UNABLE_TO_SET_VIDEO_MODE), "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + if (Palette) delete [] Palette; + return (EXIT_FAILURE); + } + + CCDebugString ("C&C95 - Initialising video surfaces.\n"); + + if (ScreenWidth==320){ + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + ModeXBuff.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + } else { + +#if (1) //ST - 1/3/2019 2:11PM + + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + + +#else + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + + /* + ** Check that we really got a video memory page. Failure is fatal. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY){ + /* + ** Aaaarrgghh! + */ + CCDebugString ("C&C95 - Unable to allocate primary surface.\n"); + MessageBox(MainWindow, Text_String(TXT_UNABLE_TO_ALLOCATE_PRIMARY_VIDEO_BUFFER), "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + if (Palette) delete [] Palette; + return (EXIT_FAILURE); + } + + /* + ** If we have enough left then put the hidpage in video memory unless... + ** + ** If there is no blitter then we will get better performance with a system + ** memory hidpage + ** + ** Use a system memory page if the user has specified it via the ccsetup program. + */ + CCDebugString ("C&C95 - Allocating back buffer "); + long video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + if (video_memory < ScreenWidth*ScreenHeight || + (! (video_capabilities & VIDEO_BLITTER)) || + (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) || + !VideoBackBufferAllowed){ + CCDebugString ("in system memory.\n"); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + //HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + CCDebugString ("in video memory.\n"); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)GBC_VIDEOMEM); + + /* + ** Make sure we really got a video memory hid page. If we didnt then things + ** will run very slowly. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY){ + /* + ** Oh dear, big trub. This must be an IBM Aptiva or something similarly cruddy. + ** We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + }else{ + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } +#endif + } + ScreenHeight = 1536; + + if (VisiblePage.Get_Height() == 480){ + SeenBuff.Attach(&VisiblePage,0, 40, 1536, 1536); + HidPage.Attach(&HiddenPage, 0, 40, 1536, 1536); + }else{ + SeenBuff.Attach(&VisiblePage,0, 0, 1536, 1536); + HidPage.Attach(&HiddenPage, 0, 0, 1536, 1536); + } + CCDebugString ("C&C95 - Adjusting variables for resolution.\n"); + Options.Adjust_Variables_For_Resolution(); + + CCDebugString ("C&C95 - Setting palette.\n"); + /////////Set_Palette(Palette); + + WindowList[0][WINDOWWIDTH] = SeenBuff.Get_Width() >> 3; + WindowList[0][WINDOWHEIGHT] = SeenBuff.Get_Height(); + + /* + ** Install the memory error handler + */ + Memory_Error = &Memory_Error_Handler; + + /* + ** Initialise MMX support if its available + */ + CCDebugString ("C&C95 - Entering MMX detection.\n"); + if (MMXAvailable){ + Init_MMX(); + } + + CCDebugString ("C&C95 - Creating mouse class.\n"); + WWMouse = new WWMouseClass(&SeenBuff, 32, 32); +// MouseInstalled = Install_Mouse(32,24,320,200); + MouseInstalled = TRUE; + + /* + ** See if we should run the intro + */ + CCDebugString ("C&C95 - Reading CONQUER.INI.\n"); + char *buffer = (char*)Alloc(64000 , MEM_NORMAL); //(char *)HidPage.Get_Buffer(); + cfile.Read(buffer, cfile.Size()); + buffer[cfile.Size()] = '\0'; + + /* + ** Check for forced intro movie run disabling. If the conquer + ** configuration file says "no", then don't run the intro. + */ + char tempbuff[5]; + WWGetPrivateProfileString("Intro", "PlayIntro", "Yes", tempbuff, 4, buffer); + if ((stricmp(tempbuff, "No") == 0) || SpawnedFromWChat) { + Special.IsFromInstall = false; + }else{ + Special.IsFromInstall = true; + } + SlowPalette = WWGetPrivateProfileInt("Options", "SlowPalette", 1, buffer); + +#ifdef DEMO + /* + ** Check for override directory path for CD searches. + */ + WWGetPrivateProfileString("CD", "Path", ".", OverridePath, sizeof(OverridePath), buffer); +#endif + + /* + ** Regardless of whether we should run it or not, here we're + ** gonna change it to say "no" in the future. + */ + WWWritePrivateProfileString("Intro", "PlayIntro", "No", buffer); + cfile.Write(buffer, strlen(buffer)); + + Free(buffer); + + CCDebugString ("C&C95 - Checking availability of C&CSPAWN.INI packet from WChat.\n"); + if (DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - C&CSPAWN.INI packet available.\n"); + Check_From_WChat(NULL); + }else{ + CCDebugString ("C&C95 - C&CSPAWN.INI packet not arrived yet.\n"); + //Check_From_WChat("C&CSPAWN.INI"); + //if (Special.IsFromWChat){ + // DDEServer.Disable(); + //} + } + + /* + ** If the intro is being run for the first time, then don't + ** allow breaking out of it with the key. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = false; + } + + Memory_Error_Exit = Print_Error_End_Exit; + + CCDebugString ("C&C95 - Entering main game.\n"); + Main_Game(argc, argv); + + if (RunningAsDLL) { + return (EXIT_SUCCESS); + } + + VisiblePage.Clear(); + HiddenPage.Clear(); +// Set_Video_Mode(RESET_MODE); + + Memory_Error_Exit = Print_Error_Exit; + + CCDebugString ("C&C95 - About to exit.\n"); + ReadyToQuit = 1; + + PostMessage(MainWindow, WM_DESTROY, 0, 0); + do + { + Keyboard::Check(); + }while (ReadyToQuit == 1); + + CCDebugString ("C&C95 - Returned from final message loop.\n"); + //Prog_End(); + //Invalidate_Cached_Icons(); + //VisiblePage.Un_Init(); + //HiddenPage.Un_Init(); + //AllSurfaces.Release(); + //Reset_Video_Mode(); + //Stop_Profiler(); + return (EXIT_SUCCESS); + + } else { +#ifdef GERMAN + puts("Bitte erst das SETUP-Programm starten.\n"); +#else +#ifdef FRENCH + puts("Lancez d'abord le programme de configuration SETUP.\n"); +#else + puts("Run SETUP program first."); + puts("\n"); +#endif + Kbd.Get(); +#endif + } + +// Remove_Keyboard_Interrupt(); + if (WindowsTimer){ + delete WindowsTimer; + WindowsTimer = NULL; + } + + if (Palette){ + delete [] Palette; + Palette = NULL; + } + } + + /* + ** Restore the current drive and directory. + */ +#ifdef NOT_FOR_WIN95 + _dos_setdrive(olddrive, &drivecount); + chdir(oldpath); +#endif //NOT_FOR_WIN95 + + return(EXIT_SUCCESS); +} + + +/*********************************************************************************************** + * Prog_End -- Cleans up library systems in prep for game exit. * + * * + * This routine should be called before the game terminates. It handles cleaning up * + * library systems so that a graceful return to the host operating system is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +void __cdecl Prog_End(const char *why, bool fatal) // Added why and fatal parameters. ST - 6/27/2019 10:10PM +{ + GlyphX_Debug_Print("Prog_End()"); + + if (why) { + GlyphX_Debug_Print(why); + } + if (fatal) { + *((int*)0) = 0; + } + +#ifndef DEMO + if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) { +// NullModem.Change_IRQ_Priority(0); + } +#endif + CCDebugString ("C&C95 - About to call Sound_End.\n"); + Sound_End(); + CCDebugString ("C&C95 - Returned from Sound_End.\n"); + if (WWMouse){ + CCDebugString ("C&C95 - Deleting mouse object.\n"); + delete WWMouse; + WWMouse = NULL; + } + if (WindowsTimer){ + CCDebugString ("C&C95 - Deleting windows timer.\n"); + delete WindowsTimer; + WindowsTimer = NULL; + } + + if (Palette){ + CCDebugString ("C&C95 - Deleting palette object.\n"); + delete [] Palette; + Palette = NULL; + } + + ProgEndCalled = true; +} + + +/*********************************************************************************************** + * Delete_Swap_Files -- Deletes previously existing swap files. * + * * + * This routine will scan through the current directory and delete any swap files it may * + * find. This is used to clear out any left over swap files from previous runs (crashes) * + * of the game. This routine presumes that it cannot delete the swap file that is created * + * by the current run of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +void Delete_Swap_Files(void) +{ +#if (0) + struct find_t ff; // for _dos_findfirst + + if (!_dos_findfirst("*.SWP", _A_NORMAL, &ff)) { + do { + unlink(ff.name); + } while(!_dos_findnext(&ff)); + } +#endif +} + + +void Print_Error_End_Exit(char *string) +{ + printf( "%s\n", string ); + Get_Key(); + Prog_End(); + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + +void Print_Error_Exit(char *string) +{ + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + + + + + + + +/*********************************************************************************************** + * Read_Setup_Options -- Read stuff in from the INI file that we need to know sooner * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 4:09PM ST : Created * + *=============================================================================================*/ +void Read_Setup_Options( RawFileClass *config_file ) +{ + char *buffer = new char [config_file->Size()]; + + if (config_file->Is_Available()){ + + config_file->Read (buffer, config_file->Size()); + + VideoBackBufferAllowed = WWGetPrivateProfileInt ("Options", "VideoBackBuffer", 1, buffer); + AllowHardwareBlitFills = WWGetPrivateProfileInt ("Options", "HardwareFills", 1, buffer); + ScreenHeight = WWGetPrivateProfileInt ("Options", "Resolution", 0, buffer) ? 1536 : 1536; + IsV107 = WWGetPrivateProfileInt ("Options", "Compatibility", 0, buffer); + + /* + ** See if an alternative socket number has been specified + */ + int socket = WWGetPrivateProfileInt ("Options", "Socket", 0, buffer); + if (socket >0 ){ + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + } + + /* + ** See if a destination network has been specified + */ + char netbuf [512]; + memset (netbuf, 0, sizeof (netbuf) ); + char *netptr = WWGetPrivateProfileString ("Options", "DestNet", NULL, netbuf, sizeof (netbuf), buffer); + + if (netptr && strlen (netbuf)){ + NetNumType net; + NetNodeType node; + + /* + ** Scan the string, pulling off each address piece + */ + int i = 0; + char * p = strtok(netbuf,"."); + int x; + while (p) { + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + IsBridge = 1; + memset(node, 0xff, 6); + BridgeNet = IPXAddressClass(net, node); + } + } + + } + + delete [] buffer; +} \ No newline at end of file diff --git a/TIBERIANDAWN/STATS.CPP b/TIBERIANDAWN/STATS.CPP new file mode 100644 index 000000000..44f2c1120 --- /dev/null +++ b/TIBERIANDAWN/STATS.CPP @@ -0,0 +1,676 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 05/29/1996 * + * * + * Last Update : May 29th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Internet game statistics to collect and upload to the server * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" +#include "packet.h" +#include "ccdde.h" + +#define FIELD_PACKET_TYPE "TYPE" +#define FIELD_GAME_ID "IDNO" +#define FIELD_START_CREDITS "CRED" +#define FIELD_BASES "BASE" +#define FIELD_TIBERIUM "TIBR" +#define FIELD_CRATES "CRAT" +#define FIELD_AI_PLAYERS "AIPL" +#define FIELD_CAPTURE_THE_FLAG "FLAG" +#define FIELD_START_UNIT_COUNT "UNIT" +#define FIELD_TECH_LEVEL "TECH" +#define FIELD_SCENARIO "SCEN" +#define FIELD_COMPLETION "CMPL" +#define FIELD_START_TIME "TIME" +#define FIELD_GAME_DURATION "DURA" +#define FIELD_FRAME_RATE "AFPS" +#define FIELD_SPEED_SETTING "SPED" +#define FIELD_GAME_VERSION "VERS" +#define FIELD_GAME_BUILD_DATE "DATE" +#define FIELD_COVERT_PRESENT "COVT" +#define FIELD_CPU_TYPE "PROC" +#define FIELD_MEMORY "MEMO" +#define FIELD_VIDEO_MEMORY "VIDM" +#define FIELD_PLAYER1_HANDLE "NAM1" +#define FIELD_PLAYER2_HANDLE "NAM2" +#define FIELD_PLAYER1_TEAM "SID1" +#define FIELD_PLAYER2_TEAM "SID2" +#define FIELD_PLAYER1_COLOR "COL1" +#define FIELD_PLAYER2_COLOR "COL2" +#define FIELD_PLAYER1_CREDITS "CRD1" +#define FIELD_PLAYER2_CREDITS "CRD2" + +#define FIELD_PLAYER1_UNITS_LEFT "UNL1" +#define FIELD_PLAYER2_UNITS_LEFT "UNL2" +#define FIELD_PLAYER1_INFANTRY_LEFT "INL1" +#define FIELD_PLAYER2_INFANTRY_LEFT "INL2" +#define FIELD_PLAYER1_PLANES_LEFT "PLL1" +#define FIELD_PLAYER2_PLANES_LEFT "PLL2" +#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1" +#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2" + +#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1" +#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2" +#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1" +#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2" +#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1" +#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2" +#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1" +#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2" + +#define FIELD_PLAYER1_UNITS_KILLED "UNK1" +#define FIELD_PLAYER2_UNITS_KILLED "UNK2" +#define FIELD_PLAYER1_INFANTRY_KILLED "INK1" +#define FIELD_PLAYER2_INFANTRY_KILLED "INK2" +#define FIELD_PLAYER1_PLANES_KILLED "PLK1" +#define FIELD_PLAYER2_PLANES_KILLED "PLK2" +#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1" +#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2" + +#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1" +#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2" + +#define FIELD_PLAYER1_CRATES_FOUND "CRA1" +#define FIELD_PLAYER2_CRATES_FOUND "CRA2" +#define FIELD_PLAYER1_HARVESTED "HRV1" +#define FIELD_PLAYER2_HARVESTED "HRV2" + + +#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50 +#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51 + +enum { + COMPLETION_CONNECTION_LOST, + COMPLETION_PLAYER_1_WON, + COMPLETION_PLAYER_1_WON_BY_RESIGNATION, + COMPLETION_PLAYER_1_WON_BY_DISCONNECTION, + COMPLETION_PLAYER_2_WON, + COMPLETION_PLAYER_2_WON_BY_RESIGNATION, + COMPLETION_PLAYER_2_WON_BY_DISCONNECTION +}; + + +extern unsigned long PlanetWestwoodGameID; +extern HINSTANCE ProgramInstance; +extern unsigned long PlanetWestwoodStartTime; + +extern "C" char CPUType; + + +bool GameTimerInUse = false; +TimerClass GameTimer; +long GameEndTime; +void *PacketLater = NULL; + +/*********************************************************************************************** + * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/29/96 12:38PM ST : Created * + *=============================================================================================*/ + +void Send_Statistics_Packet(void) +{ +//PG_TO_FIX +#if (0) +#ifndef DEMO + + PacketClass stats; + HouseClass *player; + static int packet_size; + int index; + bool packet_later = false; // Should the packet be sent later + void *packet; + + static char field_player_handle[5] = { "NAM?" }; + static char field_player_team[5] = { "SID?" }; + static char field_player_color[5] = { "COL?" }; + static char field_player_credits[5] = { "CRD?" }; + static char field_player_units_left[5] = { "UNL?" }; + static char field_player_infantry_left[5] = { "INL?" }; + static char field_player_planes_left[5] = { "PLL?" }; + static char field_player_buildings_left[5] = { "BLL?" }; + static char field_player_units_bought[5] = { "UNB?" }; + static char field_player_infantry_bought[5] = { "INB?" }; + static char field_player_planes_bought[5] = { "PLB?" }; + static char field_player_buildings_bought[5] = { "BLB?" }; + static char field_player_units_killed[5] = { "UNK?" }; + static char field_player_infantry_killed[5] = { "INK?" }; + static char field_player_planes_killed[5] = { "PLK?" }; + static char field_player_buildings_killed[5] = { "BLK?" }; + static char field_player_buildings_captured[5] = { "BLC?" }; + static char field_player_crates_found[5] = { "CRA?" }; + static char field_player_harvested[5] = { "HRV?" }; + + static char *houses[] = { + "GDI", + "NOD", + "NUT", + "JUR", + "M01", + "M02", + "M03", + "M04", + "M05", + "M06" + }; + + CCDebugString ("C&C95 - In Send_Statistics_Packet.\n"); + + + if (!PacketLater){ + + CCDebugString ("C&C95 - PacketLater is false.\n"); + + + /* + ** Field to identify this as C&C 95 internet game statistics packet + */ + if (Server){ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO); + }else{ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO); + } + + /* + ** Game ID. A unique game identifier assigned by WChat. + */ + stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID); + + /* + ** Start credits. + */ + stats.Add_Field(FIELD_START_CREDITS, (unsigned long)MPlayerCredits); + + /* + ** Bases (On/Off) + */ + stats.Add_Field(FIELD_BASES, MPlayerBases ? "ON" : "OFF"); + + /* + ** Tiberium (On/Off) + */ + stats.Add_Field(FIELD_TIBERIUM, MPlayerTiberium ? "ON" : "OFF"); + + /* + ** Crates (On/Off) + */ + stats.Add_Field(FIELD_CRATES, MPlayerGoodies ? "ON" : "OFF"); + + /* + ** AI Players (On/Off/Capture the flag) + */ + stats.Add_Field(FIELD_AI_PLAYERS, MPlayerGhosts ? "ON" : "OFF"); + stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF"); + + /* + ** Start unit count + */ + stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)MPlayerUnitCount); + + /* + ** Tech level. + */ + stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel); + + CCDebugString ("C&C95 - Adding stats field for scenario.\n"); + /* + ** Scenario + */ + char fname[128]; + char namebuffer[40]; + char *abuffer = (char *)_ShapeBuffer; + memset(abuffer, '\0', _ShapeBufferSize); + sprintf(fname,"%s.INI",ScenarioName); + CCFileClass fileo; + fileo.Set_Name (fname); + fileo.Read(abuffer, _ShapeBufferSize-1); + fileo.Close(); + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer); + stats.Add_Field(FIELD_SCENARIO, namebuffer); + //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]); + + + /* + ** Game completion status. + ** + ** Connection lost. + ** Player 1 won + ** Player 2 won + ** Player 1 won by resignation + ** Player 2 won by resignation + ** Player 1 aborted + ** Player 2 aborted + ** + */ + CCDebugString ("C&C95 - Adding stats field for completion status.\n"); + HouseClass *player1 = HouseClass::As_Pointer(MPlayerHouses[0]); + HouseClass *player2 = HouseClass::As_Pointer(MPlayerHouses[1]); + + int completion = -1; + + if (ConnectionLost){ + completion = COMPLETION_CONNECTION_LOST; + CCDebugString ("C&C95 - Completion status is connection lost.\n"); + }else{ + + if (player1 && player2){ + if (player1->IGaveUp){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n"); + } + + if (player2->IGaveUp){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n"); + } + + + if (player2->IsDefeated){ + /* + ** Player 1 won. Find out how. + */ + completion = COMPLETION_PLAYER_1_WON; + if (player2->Resigned){ + completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION; + CCDebugString ("C&C95 - Completion status is player 2 resigned.\n"); + }else{ + if (player2->IGaveUp){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n"); + } + } + + + }else{ + + if (player1->IsDefeated){ + /* + ** Player 2 won. Find out how. + */ + completion = COMPLETION_PLAYER_2_WON; + if (player1->Resigned){ + completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION; + CCDebugString ("C&C95 - Completion status is player 1 resigned.\n"); + }else{ + if (player1->IGaveUp){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n"); + } + } + } + } + } + } + + stats.Add_Field (FIELD_COMPLETION, (char) completion); + + + + + + + /* + ** Game start time (GMT or Pacific?) + ** + ** Passed from WChat + */ + stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime); + + /* + ** Game duration (seconds). + */ + stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60); + + /* + ** Avg. frame rate. + */ + if (GameEndTime/60 == 0){ + stats.Add_Field (FIELD_FRAME_RATE, 0L ); + }else{ + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); + } + + + CCDebugString ("C&C95 - Adding hardware info stats.\n"); + /* + ** CPU type + */ + stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType); + + /* + ** Memory + */ + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys); + + /* + ** Video memory + */ + DDCAPS video_capabilities; + long video_memory; + + if (DirectDrawObject){ + video_capabilities.dwSize = sizeof (video_capabilities); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + video_memory = video_capabilities.dwVidMemTotal; + video_memory += 1024*1024 -1; + video_memory &= 0xfff00000; + stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory); + } + } + + CCDebugString ("C&C95 - Adding game info stats.\n"); + /* + ** Game speed setting. + */ + stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed); + + /* + ** C&C 95 version/build date + */ + char version[128]; + sprintf (version, "%d%s", Version_Number(), VersionText); + stats.Add_Field (FIELD_GAME_VERSION, version); + + char path_to_exe[280]; + FILETIME write_time; //File time is 64 bits + + GetModuleFileName (ProgramInstance, path_to_exe, 280); + RawFileClass file; + file.Set_Name(path_to_exe); + file.Open(); + HANDLE handle = file.Get_File_Handle(); + + if (handle != INVALID_HANDLE_VALUE){ + if (GetFileTime (handle, NULL, NULL, &write_time)){ + write_time.dwLowDateTime = htonl (write_time.dwLowDateTime); + write_time.dwHighDateTime = htonl (write_time.dwHighDateTime); + stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time)); + } + } + + /* + ** Covert installed? (Yes/No) + */ + stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present()); + + CCDebugString ("C&C95 - Adding house specific stats.\n"); + /* + ** Build the player specific statistics + ** + */ + for (int house = 0 ; house < 2 ; house++){ + + player = HouseClass::As_Pointer(MPlayerHouses[house]); + + if (player){ + /* + ** Player handle. + */ + field_player_handle[3] = '1' + (char)house; + stats.Add_Field (field_player_handle, (char*) MPlayerNames[house]); + + /* + ** Player team. (NOD or GDI) + */ + field_player_team[3] = '1' + (char)house; + stats.Add_Field (field_player_team, houses[player->ActLike]); + + /* + ** Player color + */ + field_player_color[3] = '1' + (char)house; + stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1)); + + /* + ** Player end credits. + */ + field_player_credits[3] = '1' + (char)house; + stats.Add_Field (field_player_credits, player->Credits + player->Tiberium); + + /* + ** Number of each unit/building type built + */ + field_player_infantry_bought[3] = '1' + (char)house; + field_player_units_bought[3] = '1' + (char)house; + field_player_planes_bought[3] = '1' + (char)house; + field_player_buildings_bought[3] = '1' + (char)house; + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + + stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + + player->InfantryTotals->To_PC_Format(); + player->UnitTotals->To_PC_Format(); + player->AircraftTotals->To_PC_Format(); + player->BuildingTotals->To_PC_Format(); + + /* + ** Clear out the counts and use the space to count up the current number of units/buildings + */ + player->InfantryTotals->Clear_Unit_Total(); + player->AircraftTotals->Clear_Unit_Total(); + player->UnitTotals->Clear_Unit_Total(); + player->BuildingTotals->Clear_Unit_Total(); + + /* + ** Number of units remaining to player + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (unit->House == player){ + player->UnitTotals->Increment_Unit_Total (unit->Class->Type); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (infantry->House == player && !infantry->Class->IsCivilian){ + player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (aircraft->House == player && aircraft->Class->Type != AIRCRAFT_CARGO){ + player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type); + } + } + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->House == player){ + player->BuildingTotals->Increment_Unit_Total (building->Class->Type); + } + } + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + + field_player_infantry_left[3] = '1' + (char)house; + field_player_units_left[3] = '1' + (char)house; + field_player_planes_left[3] = '1' + (char)house; + field_player_buildings_left[3] = '1' + (char)house; + stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + + + /* + ** Number of enemy units/buildings of each type destroyed. + */ + + player->DestroyedInfantry->To_Network_Format(); + player->DestroyedUnits->To_Network_Format(); + player->DestroyedAircraft->To_Network_Format(); + player->DestroyedBuildings->To_Network_Format(); + + field_player_infantry_killed[3] = '1' + (char)house; + field_player_units_killed[3] = '1' + (char)house; + field_player_planes_killed[3] = '1' + (char)house; + field_player_buildings_killed[3] = '1' + (char)house; + stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); + + + /* + ** Number and type of enemy buildings captured + */ + field_player_buildings_captured[3] = '1' + (char)house; + player->CapturedBuildings->To_Network_Format(); + stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4); + + /* + ** Number of crates discovered and their contents + */ + field_player_crates_found[3] = '1' + (char)house; + player->TotalCrates->To_Network_Format(); + stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4); + + /* + ** Amount of tiberium turned into credits + */ + field_player_harvested[3] = '1' + (char)house; + stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits); + } + } + + CCDebugString ("C&C95 - Calling Create_Comms_Packet.\n"); + /* + ** Create the comms packet to be sent + */ + packet = stats.Create_Comms_Packet(packet_size); + CCDebugString ("C&C95 - Returned from Create_Comms_Packet.\n"); + + /* + ** If a player disconnected then dont send the packet at this time - save it for later + */ + if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION + || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){ + PacketLater = packet; + CCDebugString ("C&C95 - Flagging to send the packet later.\n"); + return; + } + + }else{ //else for if (!PacketLater) + + CCDebugString ("C&C95 - PacketLater is true.\n"); + + /* + ** Send the packet we calculated earlier when the disconnect occurred + */ + packet = PacketLater; + PacketLater = NULL; + } + + /* + ** Send it..... + */ + int times = 100; //100 times max + CountDownTimerClass send_timer; + + CCDebugString ("C&C95 - About to send stats packet to DDE server.\n"); + while ( ! Send_Data_To_DDE_Server ((char*)packet, packet_size, DDEServerClass::DDE_PACKET_GAME_RESULTS)){ + CCDebugString ("C&C95 - Stats packet send failed.\n"); + send_timer.Set (60, true); + while (send_timer.Time()){}; + } + + + /* + ** Save it to disk as well so I can see it + */ +#if (0) + RawFileClass anotherfile ("packet.net"); + anotherfile.Write(packet, packet_size); +#endif //(0) + /* + ** Tidy up + */ + CCDebugString ("C&C95 - About to delete packet memory.\n"); + delete [] packet; + + GameStatisticsPacketSent = true; + CCDebugString ("C&C95 - Returning from Send_Statistics_Packet.\n"); +#endif //DEMO +#endif +} + + + + + + + + + +void Register_Game_Start_Time(void) +{ + + GameTimer.Set (0, true); + GameTimerInUse = true; +} + + +extern void Register_Game_End_Time(void) +{ + GameEndTime = GameTimer.Time(); + GameTimerInUse = false; +} + diff --git a/TIBERIANDAWN/SUPER.CPP b/TIBERIANDAWN/SUPER.CPP new file mode 100644 index 000000000..df0eb5829 --- /dev/null +++ b/TIBERIANDAWN/SUPER.CPP @@ -0,0 +1,383 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\super.cpv 1.5 16 Oct 1995 16:49:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SuperClass::AI -- Process the super weapon AI. * + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * SuperClass::Discharged -- Handles discharged action for special super weapon. * + * SuperClass::Enable -- Enable this super special weapon. * + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * SuperClass::Remove -- Removes super weapon availability. * + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * * + * This is the constructor for the super weapons. * + * * + * INPUT: recharge -- The recharge delay time (in game frames). * + * * + * charging -- Voice to announce that the weapon is charging. * + * * + * ready -- Voice to announce that the weapon is fully charged. * + * * + * impatient -- Voice to announce current charging state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +SuperClass::SuperClass(int recharge, VoxType ready, VoxType charging, VoxType impatient, VoxType suspend) +{ + IsPresent = false; + IsOneTime = false; + IsReady = false; + IsSuspended = false; + OldStage = -1; + Control = 0; + RechargeTime = recharge; + SuspendTime = 0; + VoxRecharge = ready; + VoxCharging = charging; + VoxImpatient = impatient; + VoxSuspend = suspend; +} + + +/*********************************************************************************************** + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * * + * This will temporarily put on hold the charging of the special weapon. This might be the * + * result of insufficient power. * + * * + * INPUT: on -- Should the weapon charging be suspended? Else, it will unsuspend. * + * * + * OUTPUT: Was the weapon suspend state changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Suspend(bool on) +{ + if (IsPresent && !IsReady && !IsOneTime) { + if (on != IsSuspended) { + if (on) { + SuspendTime = Control; + } else { + Control = SuspendTime; + } + IsSuspended = on; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Enable -- Enable this super special weapon. * + * * + * This routine is called when the special weapon needs to be activated. This is used for * + * both the normal super weapons and the speicial one-time super weapons (from crates). * + * * + * INPUT: onetime -- Is this a special one time super weapon? * + * * + * player -- Is this weapon for the player? If true, then there might be a voice * + * announcement of this weapon's availability. * + * * + * quiet -- Request that the weapon start in suspended state (quiet mode). * + * * + * OUTPUT: Was the special super weapon enabled? Failure might indicate that the weapon was * + * already available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Enable(bool onetime, bool player, bool quiet) +{ + if (!IsPresent) { + IsPresent = true; + IsOneTime = onetime; + bool retval = Recharge(player && !quiet); + if (quiet) Suspend(true); + return(retval); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Remove -- Removes super weapon availability. * + * * + * Call this routine when the super weapon should be removed because of some outside * + * force. For one time special super weapons, they can never be removed except as the * + * result of discharging them. * + * * + * INPUT: none * + * * + * OUTPUT: Was the special weapon removed and disabled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Remove(bool forced) +{ + if (IsPresent && (!IsOneTime || forced)) { + IsReady = false; + IsPresent = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * * + * This routine is called when the special weapon is allowed to recharge. Suspension will * + * be disabled and the animation process will begin. * + * * + * INPUT: player -- Is this for a player owned super weapon? If so, then a voice * + * announcement might be in order. * + * * + * OUTPUT: Was the super weapon begun charging up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Recharge(bool player) +{ + if (IsPresent && !IsReady) { + IsSuspended = false; + OldStage = -1; +#ifdef CHEAT_KEYS + if (Special.IsSpeedBuild) { + Control = 1; + } else { + Control = RechargeTime; + } +#else + Control = RechargeTime; +#endif + if (player && VoxCharging != VOX_NONE) { + Speak(VoxCharging); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Superclass::Discharged -- Handles discharged action for special super weapon. * + * * + * This routine should be called when the special super weapon has been discharged. The * + * weapon will either begin charging anew or will be removed entirely -- depends on the * + * one time flag for the weapon. * + * * + * INPUT: player -- Is this special weapon for the player? If so, then there might be a * + * voice announcement. * + * * + * OUTPUT: Should the sidebar be reprocessed because the special weapon has been eliminated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Discharged(bool player) +{ + if (!IsSuspended && IsPresent && IsReady) { + IsReady = false; + if (IsOneTime) { + IsOneTime = false; + return(Remove()); + } else { + Recharge(player); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::AI -- Process the super weapon AI. * + * * + * This routine will process the charge up AI for this super weapon object. If the weapon * + * has advanced far enough to change any sidebar graphic that might represent it, then * + * "true" will be returned. Use this return value to intelligenly update the sidebar. * + * * + * INPUT: player -- Is this for the player? If it is and the weapon is now fully charged, * + * then this fully charged state will be announced to the player. * + * * + * OUTPUT: Was the weapon's state changed such that a sidebar graphic update will be * + * necessary? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::AI(bool player) +{ + if (IsPresent && !IsReady) { + if (IsSuspended) { + if (OldStage != -1) { + OldStage = -1; + return(true); + } + } else { + if (Control.Expired()) { + IsReady = true; + if (player && VoxRecharge != VOX_NONE) { + Speak(VoxRecharge); + } + return(true); + } else { + if (Anim_Stage() != OldStage) { + OldStage = Anim_Stage(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * * + * This will return the current animation stage for this super weapon. The value will be * + * between zero (uncharged) to ANIMATION_STAGES (fully charged). Use this value to render * + * the appropriate graphic on the sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current animation stage for this special super weapon powerup. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +int SuperClass::Anim_Stage(void) const +{ + if (IsPresent) { + if (IsReady) { + return(ANIMATION_STAGES); + } + int time = Control.Time(); + if (IsSuspended) { + time = SuspendTime; + } + + return(Fixed_To_Cardinal(ANIMATION_STAGES, Cardinal_To_Fixed(RechargeTime, RechargeTime-time))); + } + return(0); +} + + +/*********************************************************************************************** + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * * + * This routine is called when the player clicks on the super weapon icon on the sidebar * + * when the super weapon is not ready yet. This results in a voice message feedback to the * + * player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Impatient_Click(void) const +{ + if (IsSuspended) { + if (VoxSuspend != VOX_NONE) { + Speak(VoxSuspend); + } + } else { + if (VoxImpatient != VOX_NONE) { + Speak(VoxImpatient); + } + } +} + + +/*********************************************************************************************** + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * * + * This routine will force the special weapon to full charge state. Call it when the weapon * + * needs to be instantly charged. The airstrike (when it first becomes available) is a * + * good example. * + * * + * INPUT: player -- Is this for the player? If true, then the full charge state will be * + * announced. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Forced_Charge(bool player) +{ + if (IsPresent) { + IsReady = true; + IsSuspended = false; + if (player && VoxRecharge != VOX_NONE) { + Speak(VoxRecharge); + } + } +} diff --git a/TIBERIANDAWN/SUPER.H b/TIBERIANDAWN/SUPER.H new file mode 100644 index 000000000..2500222ce --- /dev/null +++ b/TIBERIANDAWN/SUPER.H @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\super.h_v 1.5 16 Oct 1995 16:47:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SUPER_H +#define SUPER_H + +#include "ftimer.h" + +class SuperClass { + public: + SuperClass(int recharge=0, VoxType charging=VOX_NONE, VoxType ready=VOX_NONE, VoxType impatient=VOX_NONE, VoxType suspend=VOX_NONE); + + bool Suspend(bool on); + bool Enable(bool onetime = false, bool player=false, bool quiet=false); + void Forced_Charge(bool player=false); + bool AI(bool player=false); + bool Remove(bool forced=false); + void Impatient_Click(void) const; + int Anim_Stage(void) const; + bool Discharged(bool player); + bool Is_Ready(void) const {return(IsReady);}; + bool Is_Present(void) const {return(IsPresent);}; + bool Is_One_Time(void) const {return(IsOneTime && IsPresent);}; + + //Needed access to recharge times for tooltips - 2019/08/14 Jason Scott + int Get_Recharge_Time() const { return(RechargeTime); }; + + private: + bool Recharge(bool player=false); + + unsigned IsPresent:1; + unsigned IsOneTime:1; + unsigned IsReady:1; + unsigned IsSuspended:1; + + TCountDownTimerClass Control; + int OldStage; + int SuspendTime; + + VoxType VoxRecharge; + VoxType VoxCharging; + VoxType VoxImpatient; + VoxType VoxSuspend; + int RechargeTime; + + //Needed to make ANIMATION_STAGES public so the animation frame numbers could be turned into progress + //percentages - 2019/08/07 Jason Scott +public: + enum { + ANIMATION_STAGES=102 + }; +}; + + + +#endif diff --git a/TIBERIANDAWN/SUPPORT.ASM b/TIBERIANDAWN/SUPPORT.ASM new file mode 100644 index 000000000..fb131c4c2 --- /dev/null +++ b/TIBERIANDAWN/SUPPORT.ASM @@ -0,0 +1,456 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; $Header: F:\projects\c&c\vcs\code\support.asv 2.13 16 Oct 1995 16:52:36 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + global C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + global C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mov ecx,[(GraphicViewPort ebx).GVPWidth] + add ecx,[(GraphicViewPort ebx).GVPXAdd] + add ecx,[(GraphicViewPort ebx).GVPPitch] + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + mov edx,ecx + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + global C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + global C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + global C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX + +;---------------------------------------------------------------------------- + + END diff --git a/TIBERIANDAWN/Shape.cpp b/TIBERIANDAWN/Shape.cpp new file mode 100644 index 000000000..cca1b42e8 --- /dev/null +++ b/TIBERIANDAWN/Shape.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** +** Replacement for Shape asm files +** +** ST - 12/19/2018 10:15AM +** +** +** +*/ + + +extern "C" unsigned char *_ShapeBuffer = 0; +extern "C" long _ShapeBufferSize = 0; + + + +/* +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +*/ +extern "C" void __cdecl Set_Shape_Buffer(void const *buffer, int size) +{ + _ShapeBuffer = (unsigned char *)buffer; + _ShapeBufferSize = size; +} + + + +#if (0) +global C ShapeBuffer :dword +global C ShapeBufferSize :dword +global C _ShapeBuffer :dword +global C _ShapeBufferSize :dword +global C Set_Shape_Buffer :near + +DATASEG +label ShapeBuffer dword +_ShapeBuffer dd 0 + +label ShapeBufferSize dword +_ShapeBufferSize dd 0 + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL C Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TAB.CPP b/TIBERIANDAWN/TAB.CPP new file mode 100644 index 000000000..7a0faf960 --- /dev/null +++ b/TIBERIANDAWN/TAB.CPP @@ -0,0 +1,261 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\tab.cpv 2.18 16 Oct 1995 16:52:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TabClass::AI -- Handles player I/O with the tab buttons. * + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * TabClass::TabClass -- Default construct for the tab button class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * TabClass::TabShape = NULL; + + +/*********************************************************************************************** + * TabClass::TabClass -- Default construct for the tab button class. * + * * + * The default constructor merely sets the tab buttons to default non-selected state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +TabClass::TabClass(void) +{ + IsToRedraw = false; +// Select = -1; +} + + +/*********************************************************************************************** + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * * + * This routine is called whenever the display is being redrawn (in some fashion). The * + * parameter can be used to force the tab buttons to redraw completely. The default action * + * is to only redraw if the tab buttons have been explicitly flagged to be redraw. The * + * result of this is the elimination of unnecessary redraws. * + * * + * INPUT: complete -- bool; Force redraw of the entire tab button graphics? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 05/19/1995 JLB : New EVA style. * + *=============================================================================================*/ +void TabClass::Draw_It(bool complete) +{ + + SidebarClass::Draw_It(complete); + + if (Debug_Map){ + //HidPage.Unlock(); + return; + } + +// Disable tab drawing. ST - 3/1/2019 11:35AM +#if 0 + /* + ** Redraw the top bar imagery if flagged to do so or if the entire display needs + ** to be redrawn. + */ + int width = SeenBuff.Get_Width(); + int rightx = width - 1; + + if (complete || IsToRedraw) { + + if (Tab_Height != 0) { + + + if (LogicPage->Lock()){ + + LogicPage->Fill_Rect(0, 0, rightx, Tab_Height-2, BLACK); + CC_Draw_Shape(TabShape, 0, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(TabShape, 0, width-Eva_Width, 0, WINDOW_MAIN, SHAPE_NORMAL); + Draw_Credits_Tab(); + LogicPage->Draw_Line(0, Tab_Height-1, rightx, Tab_Height-1, BLACK); + + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, Eva_Width/2, 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL); + Fancy_Text_Print(TXT_TAB_SIDEBAR, width-(Eva_Width/2), 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL); + } + LogicPage->Unlock(); + + + } + + } + + Credits.Graphic_Logic(complete || IsToRedraw); +#endif + IsToRedraw = false; +} + + +void TabClass::Draw_Credits_Tab(void) +{ + CC_Draw_Shape(TabShape, 0, 320, 0, WINDOW_MAIN, SHAPE_NORMAL); +} + + +/*********************************************************************************************** + * TC::Hilite_Tab -- Draw a tab in its depressed state * + * * + * * + * * + * INPUT: Tab to draw (not used) * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 10:47AM ST : Created * + *=============================================================================================*/ + +void TabClass::Hilite_Tab(int tab) +{ + int xpos = 0; + int text = TXT_TAB_BUTTON_CONTROLS; + tab = tab; + + CC_Draw_Shape(TabShape, 1 , xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, 80, 0, 11, TBLACK, TPF_GREEN12|TPF_CENTER | TPF_USE_GRAD_PAL); +} + + +/*********************************************************************************************** + * TabClass::AI -- Handles player I/O with the tab buttons. * + * * + * This routine is called every game tick and passed whatever key the player has supplied. * + * If the input selects a tab button, then the graphic gets updated accordingly. * + * * + * INPUT: input -- The player's input character (might be mouse click). * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 05/31/1995 JLB : Fixed to handle mouse shape properly. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +void TabClass::AI(KeyNumType &input, int x, int y) +{ + if (y >= 0 && y < Tab_Height && x < (SeenBuff.Get_Width() - 1) && x > 0) { + + bool ok = false; + int width = SeenBuff.Get_Width(); + + /* + ** If the mouse is at the top of the screen, then the tab bars only work + ** in certain areas. If the special scroll modification is not active, then + ** the tabs never work when the mouse is at the top of the screen. + */ + if (y > 0 || (Special.IsScrollMod && ((x > 3 && x < Eva_Width) || (x < width-3 && x > width-Eva_Width)))) { + ok = true; + } + + if (ok) { + if (input == KN_LMOUSE) { + int sel = -1; + if (x < Eva_Width) sel = 0; + if (x > width-Eva_Width) sel = 1; + if (sel >= 0) { + Set_Active(sel); + input = KN_NONE; + } + } + + Override_Mouse_Shape(MOUSE_NORMAL, false); + } + } + + Credits.AI(); + + SidebarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * * + * This function is used to activate one of the file folder tab buttons that appear at the * + * top edge of the screen. * + * * + * INPUT: select -- The button to activate. 0 = left button, 1=next button, etc. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ + void TabClass::Set_Active(int select) +{ + switch (select) { + case 0: + Queue_Options(); + break; + + case 1: + Map.SidebarClass::Activate(-1); + break; + + default: + break; + } +} + +void TabClass::One_Time(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Eva_Width = 80 * factor; + //Tab_Height = 8 * factor; + Tab_Height = 0; // Disable tab drawing. ST - 3/1/2019 11:35AM + + SidebarClass::One_Time(); + TabShape = Hires_Retrieve("TABS.SHP"); +} \ No newline at end of file diff --git a/TIBERIANDAWN/TAB.H b/TIBERIANDAWN/TAB.H new file mode 100644 index 000000000..d18461f87 --- /dev/null +++ b/TIBERIANDAWN/TAB.H @@ -0,0 +1,79 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\tab.h_v 2.18 16 Oct 1995 16:45:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TAB_H +#define TAB_H + +#include "sidebar.h" +#include "credits.h" + +class TabClass: public SidebarClass +{ + public: + TabClass(void); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + virtual void One_Time(void); // One-time inits + static void Draw_Credits_Tab(void); + static void Hilite_Tab(int tab); + void Redraw_Tab(void) {IsToRedraw = true;Flag_To_Redraw(false);}; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + inline int Get_Tab_Height(void) { return(Tab_Height); }; + + CreditClass Credits; + + protected: + + /* + ** If the tab graphic is to be redrawn, then this flag is true. + */ + unsigned IsToRedraw:1; + int Eva_Width; + int Tab_Height; + + private: + void Set_Active(int select); + + static void const * TabShape; +}; + + +#endif diff --git a/TIBERIANDAWN/TARCOM.CPP b/TIBERIANDAWN/TARCOM.CPP new file mode 100644 index 000000000..1897605d3 --- /dev/null +++ b/TIBERIANDAWN/TARCOM.CPP @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\tarcom.cpv 2.17 16 Oct 1995 16:52:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARCOM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 16, 1994 * + * * + * Last Update : July 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * TarComClass::~TarComClass -- Destructor for turret object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TarComClass::~TarComClass -- Destructor for turret object. * + * * + * This is the destructor for turret objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +TarComClass::~TarComClass(void) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * * + * This routine is used to display the tarcom class status to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::Debug_Dump(MonoClass *mono) const +{ + TurretClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * * + * This handles the AI logic for the targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::AI(void) +{ + TurretClass::AI(); + + if (Class->Primary != WEAPON_NONE) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + WeaponTypeClass const * weapon = &Weapons[Class->Primary]; + int primary = 0; + FireErrorType ok = Can_Fire(TarCom, 0); + if (ok != FIRE_OK) { + if (Can_Fire(TarCom, 1) == FIRE_OK) { + ok = FIRE_OK; + primary = 1; + weapon = &Weapons[Class->Secondary]; + } + } + + switch (ok) { + case FIRE_OK: + if (What_Am_I() != RTTI_UNIT) { + IsFiring = false; + } else { + if (!((UnitClass *)this)->Class->IsFireAnim) { + IsFiring = false; + } + } + + if (TurretClass::Fire_At(TarCom, primary)) { + Sound_Effect(weapon->Sound, Coord); + } + break; + + case FIRE_FACING: + if (Class->IsLockTurret) { + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { + if (*this == UNIT_FTANK) { + SecondaryFacing.Set_Desired(Facing_Dir(Dir_Facing(Direction(TarCom)))); + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } +// SecondaryFacing.Set_Desired(Direction256(Center_Coord(), As_Coord(TarCom))); + } + break; + + case FIRE_CLOAKED: + IsFiring = false; + Do_Uncloak(); + break; + } + } + + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK || *this == UNIT_BIKE) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { + if (*this == UNIT_FTANK) { + PrimaryFacing.Set_Desired(Facing_Dir(Dir_Facing(dir))); + } else { + PrimaryFacing.Set_Desired(dir); + } + } + } + } +} + + diff --git a/TIBERIANDAWN/TARCOM.H b/TIBERIANDAWN/TARCOM.H new file mode 100644 index 000000000..6906a317b --- /dev/null +++ b/TIBERIANDAWN/TARCOM.H @@ -0,0 +1,75 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\tarcom.h_v 2.16 16 Oct 1995 16:45:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARCOM_H +#define TARCOM_H + +#include "turret.h" +#include "bullet.h" + +/**************************************************************************** +** Units that can perform combat are handled by this class. It performs +** such operations as determining threat value down to actually launching the +** projectile. +*/ +class TarComClass : public TurretClass +{ + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TarComClass(void) {}; + TarComClass(UnitType classid, HousesType house) : TurretClass(classid, house) {}; + virtual ~TarComClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void AI(void); +// virtual bool Target_Something_Nearby(ThreatType rangmatters=THREAT_NORMAL); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + +}; + +#endif + + diff --git a/TIBERIANDAWN/TARGET.CPP b/TIBERIANDAWN/TARGET.CPP new file mode 100644 index 000000000..0e8170c34 --- /dev/null +++ b/TIBERIANDAWN/TARGET.CPP @@ -0,0 +1,529 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\target.cpv 2.17 16 Oct 1995 16:51:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * As_Animation -- Converts target value into animation pointer. * + * As_Building -- Converts a target value into a building object pointer. * + * As_Bullet -- Converts the target into a bullet pointer. * + * As_Cell -- Converts a target value into a cell number. * + * As_Coord -- Converts a target value into a coordinate value. * + * As_Infantry -- If the target is infantry, return a pointer to it. * + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * As_Object -- Converts a target value into an object pointer. * + * As_Team -- Converts a target number into a team pointer. * + * As_TeamType -- Converts a target into a team type pointer. * + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * As_Trigger -- Converts specified target into a trigger pointer. * + * As_Unit -- Converts a target value into a unit pointer. * + * Target_Legal -- Determines if the specified target is legal. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "target.h" + + +/*********************************************************************************************** + * As_Trigger -- Converts specified target into a trigger pointer. * + * * + * This routine will convert the specified target number into a trigger pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the trigger pointer that the specified target number represents. If * + * it doesn't represent a legal trigger object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass * As_Trigger(TARGET target, bool check_active) +{ + TriggerClass* trigger = Is_Target_Trigger(target) ? Triggers.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && trigger != NULL && !trigger->IsActive) { + trigger = NULL; + } + return(trigger); +} + + +/*********************************************************************************************** + * As_Team -- Converts a target number into a team pointer. * + * * + * This routine will convert the specified target number into a team pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the team object that the specified target number represents. If it * + * doesn't represent a legal team then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * As_Team(TARGET target, bool check_active) +{ + TeamClass* team = Is_Target_Team(target) ? Teams.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && team != NULL && !team->IsActive) { + team = NULL; + } + return(team); +} + + +/*********************************************************************************************** + * As_TeamType -- Converts a target into a team type pointer. * + * * + * This routine will convert the specified target number into a team type pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the team type represented by the target number. If the * + * target number doesn't represent a legal team type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * As_TeamType(TARGET target) +{ + return(Is_Target_TeamType(target) ? TeamTypes.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Animation -- Converts target value into animation pointer. * + * * + * This routine will convert the specified target number into an animation pointer. * + * * + * INPUT: target -- The target number to convert into an animation pointer. * + * * + * OUTPUT: Returns with a pointer to the legal animation that this target represents. If it * + * doesn't represent a legal animation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +AnimClass * As_Animation(TARGET target, bool check_active) +{ + AnimClass* anim = Is_Target_Animation(target) ? Anims.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && anim != NULL && !anim->IsActive) { + anim = NULL; + } + return(anim); +} + + +/*********************************************************************************************** + * As_Bullet -- Converts the target into a bullet pointer. * + * * + * This routine will convert the specified target number into a bullet pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the bullet it specifies. If the target doesn't refer to * + * a legal bullet, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * As_Bullet(TARGET target, bool check_active) +{ + BulletClass* bullet = Is_Target_Bullet(target) ? Bullets.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && bullet != NULL && !bullet->IsActive) { + bullet = NULL; + } + return(bullet); +} + + +/*********************************************************************************************** + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * * + * This routine will convert the specified target value into an aircraft object pointer. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with a pointer to the aircraft that this target value represents. If the * + * specified target value doesn't represent an aircraft, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass * As_Aircraft(TARGET target, bool check_active) +{ + AircraftClass* aircraft = Is_Target_Aircraft(target) ? Aircraft.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && aircraft != NULL && !aircraft->IsActive) { + aircraft = NULL; + } + return(aircraft); +} + + +/*********************************************************************************************** + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * * + * This routine will take the target value specified and convert it into a TechnoClass * + * pointer if the target represents an object that has a TechnoClass. * + * * + * INPUT: target -- The target value to convert into a TechnoClass pointer. * + * * + * OUTPUT: Returns with a pointer to the associated object's TechnoClass. If the target * + * cannot be converted into a TechnoClass pointer, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * As_Techno(TARGET target, bool check_active) +{ + ObjectClass * obj = As_Object(target, check_active); + + if (obj && obj->Is_Techno()) { + return(TechnoClass *)obj; + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Object -- Converts a target value into an object pointer. * + * * + * This routine is used to convert the target value specified into an object pointer. If * + * the target doesn't represent an object or the target value is illegal, then NULL is * + * returned. * + * * + * INPUT: target -- The target value to convert from. * + * check_active -- Check if the target is active, return NULL if not. * + * * + * OUTPUT: Returns with a pointer to the object it represent, or NULL if not an object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * As_Object(TARGET target, bool check_active) +{ + int val = Target_Value(target); + ObjectClass * object = NULL; + + switch (Target_Kind(target)) { + case KIND_INFANTRY: + object = Infantry.Raw_Ptr(val); + break; + + case KIND_UNIT: + object = Units.Raw_Ptr(val); + break; + + case KIND_BUILDING: + object = Buildings.Raw_Ptr(val); + break; + + case KIND_AIRCRAFT: + object = Aircraft.Raw_Ptr(val); + break; + + case KIND_TERRAIN: + object = Terrains.Raw_Ptr(val); + break; + + case KIND_BULLET: + object = Bullets.Raw_Ptr(val); + break; + + case KIND_ANIMATION: + object = Anims.Raw_Ptr(val); + break; + + default: + break; + } + + /* + ** Special check to ensure that a target value that references an + ** invalid object will not be converted back into an object pointer. + ** This condition is rare, but could occur in a network game if the + ** object it refers to is destroyed between the time an event message + ** is sent and when it is received. + */ + if (check_active && object != NULL && !object->IsActive) { + object = NULL; + } + + return(object); +} + + +/*********************************************************************************************** + * As_Unit -- Converts a target value into a unit pointer. * + * * + * This routine is used to convert the target value specified into a pointer to a unit * + * object. * + * * + * INPUT: target -- The target value to convert into a unit pointer. * + * * + * OUTPUT: Returns with a pointer to the unit the target value represents or NULL if not * + * a unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * As_Unit(TARGET target, bool check_active) +{ + UnitClass* unit = Is_Target_Unit(target) ? Units.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && unit != NULL && !unit->IsActive) { + unit = NULL; + } + return(unit); +} + + +/*********************************************************************************************** + * As_Infantry -- If the target is infantry, return a pointer to it. * + * * + * This routine will translate the specified target value into an infantry pointer if the * + * target actually represents an infantry object. * + * * + * INPUT: target -- The target to convert to a pointer. * + * * + * OUTPUT: Returns a pointer to the infantry object that this target value represents. If * + * the target doesn't represent an infantry object, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * As_Infantry(TARGET target, bool check_active) +{ + InfantryClass* infantry = Is_Target_Infantry(target) ? Infantry.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && infantry != NULL && !infantry->IsActive) { + infantry = NULL; + } + return(infantry); +} + + +#ifdef NEVER +TerrainClass * As_Terrain(TARGET target) +{ + return(Is_Target_Terrain(target) ? &Terrains[Target_Value(target)] : NULL); +} +#endif + + +/*********************************************************************************************** + * As_Building -- Converts a target value into a building object pointer. * + * * + * This routine is used to convert the target value specified into a building pointer. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the building object that the target value represents. * + * If it doesn't represent a building, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * As_Building(TARGET target, bool check_active) +{ + BuildingClass* building = Is_Target_Building(target) ? Buildings.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && building != NULL && !building->IsActive) { + building = NULL; + } + return(building); +} + + +/*********************************************************************************************** + * Target_Legal -- Determines if the specified target is legal. * + * * + * This routine is used to check for the legality of the target value specified. It is * + * necessary to call this routine if there is doubt about the the legality of the target. * + * It is possible for the unit that a target value represents to be eliminated and thus * + * rendering the target value invalid. * + * * + * INPUT: target -- The target value to check. * + * * + * OUTPUT: bool; Is the target value legal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +bool Target_Legal(TARGET target) +{ + if (target == TARGET_NONE) return(false); + + ObjectClass * obj = As_Object(target, false); + if (obj) { + return(obj->IsActive); + } + return(true); +} + + +/*********************************************************************************************** + * As_Cell -- Converts a target value into a cell number. * + * * + * This routine is used to convert the target value specified, into a cell value. This is * + * necessary for find path and other procedures that need a cell value. * + * * + * INPUT: target -- The target value to convert to a cell value. * + * * + * OUTPUT: Returns with the target value expressed as a cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL As_Cell(TARGET target) +{ + return(Coord_Cell(As_Coord(target))); +} + + +/*********************************************************************************************** + * As_Coord -- Converts a target value into a coordinate value. * + * * + * This routine is used to convert the target value specified into a coordinate value. It * + * is necessary for those procedures that require a coordinate value. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with the target expressed as a COORD value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + * 11/16/1994 JLB : Simplified. * + *=============================================================================================*/ +COORDINATE As_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + + /* + ** If this is invalid memory or the object is dead then return 0 + ** This is a kludge to fix the problem of team target objects being assigned after + ** the object is already destroyed - 1/15/97 3:13PM + */ + if (IsBadReadPtr ((void*)obj, sizeof (ObjectClass) ) || !obj->IsActive){ +//OutputDebugString ("C&C95 - As_Coord called for invalid target object\m"); + return(0x00000000L); + } + + return(obj->Target_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * * + * This routine will convert the specified target into a coordinate location. This location * + * is used when moving to the target specified. For cells, this is the center of the cell. * + * For special buildings that allow docking, it is the center location of the docking * + * bay. * + * * + * INPUT: target -- The target to convert into a coordinate value. * + * * + * OUTPUT: Returns with the docking coordinate of the target value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE As_Movement_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Docking_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} diff --git a/TIBERIANDAWN/TARGET.H b/TIBERIANDAWN/TARGET.H new file mode 100644 index 000000000..344994303 --- /dev/null +++ b/TIBERIANDAWN/TARGET.H @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\target.h_v 2.16 16 Oct 1995 16:45:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARGET_H +#define TARGET_H + +/************************************************************************** +** When a unit proceeds with carrying out its mission, it can have several +** intermediate goals. Each goal (or target if you will) can be one of the +** following kinds. +*/ +typedef enum KindType : unsigned char { + KIND_NONE, + KIND_CELL, + KIND_UNIT, + KIND_INFANTRY, + KIND_BUILDING, + KIND_TERRAIN, + KIND_AIRCRAFT, + KIND_TEMPLATE, + KIND_BULLET, + KIND_ANIMATION, + KIND_TRIGGER, + KIND_TEAM, + KIND_TEAMTYPE +} KindType; + + +#define TARGET_MANTISSA 12 // Bits of value precision. +#define TARGET_MANTISSA_MASK (~((~0)<>TARGET_EXPONENT)) + +inline KindType Target_Kind(TARGET a){return (KindType)(((unsigned)a)>>TARGET_MANTISSA);} +inline unsigned Target_Value(TARGET a){return (((unsigned)a) & TARGET_MANTISSA_MASK);} + +inline bool Is_Target_Team(TARGET a) {return (Target_Kind(a) == KIND_TEAM);} +inline bool Is_Target_TeamType(TARGET a) {return (Target_Kind(a) == KIND_TEAMTYPE);} +inline bool Is_Target_Trigger(TARGET a) {return (Target_Kind(a) == KIND_TRIGGER);} +inline bool Is_Target_Infantry(TARGET a) {return (Target_Kind(a) == KIND_INFANTRY);} +inline bool Is_Target_Bullet(TARGET a){return (Target_Kind(a) == KIND_BULLET);} +inline bool Is_Target_Terrain(TARGET a){return (Target_Kind(a) == KIND_TERRAIN);} +inline bool Is_Target_Cell(TARGET a){return (Target_Kind(a) == KIND_CELL);} +inline bool Is_Target_Unit(TARGET a){return (Target_Kind(a) == KIND_UNIT);} +inline bool Is_Target_Building(TARGET a){return (Target_Kind(a) == KIND_BUILDING);} +inline bool Is_Target_Template(TARGET a){return (Target_Kind(a) == KIND_TEMPLATE);} +inline bool Is_Target_Aircraft(TARGET a){return (Target_Kind(a) == KIND_AIRCRAFT);} +inline bool Is_Target_Animation(TARGET a) {return (Target_Kind(a) == KIND_ANIMATION);} + +inline TARGET Build_Target(KindType kind, int value) {return (TARGET)((((unsigned)kind) << TARGET_MANTISSA) | (unsigned)value);} +inline TARGET As_Target(CELL cell){return (TARGET)(((unsigned)KIND_CELL << TARGET_MANTISSA) | cell);} + +class UnitClass; +class BuildingClass; +class TechnoClass; +class TerrainClass; +class ObjectClass; +class InfantryClass; +class BulletClass; +class TriggerClass; +class TeamClass; +class TeamTypeClass; +class AnimClass; +class AircraftClass; + +#ifdef NEVER +class TargetClass +{ + public: + TargetClass(void) {Target.Raw = 0;}; + + /* + ** This handles assignment from an integer and conversion + ** to an integer. + */ + inline TargetClass(int val, KindType kind) {Target.Component.Value=val; Target.Component.Kind=kind;}; + inline TargetClass(int val) {Target.Raw = val;}; + inline operator int () {return Target.Raw;}; + //inline TargetClass & operator = (const int &val) {*((int*)this)=val; return *this;}; + + inline bool Is_Filled(void) {return Target.Component.Kind != KIND_NONE;}; + inline void Invalidate(void) {Target.Component.Kind = 0;}; + inline bool Is_Cell(void) {return Target.Component.Kind == KIND_CELL;}; + inline bool Is_Unit(void) {return Target.Component.Kind == KIND_UNIT;}; + inline bool Is_Building(void) {return Target.Component.Kind == KIND_BUILDING;}; + inline bool Is_Aircraft(void) {return Target.Component.Kind == KIND_AIRCRAFT;}; + inline int As_Value(void) {return Target.Component.Value;}; + inline KindType As_Kind(void) {return Target.Component.Kind;}; + + // Allows comparing one target to another (for equality). + inline bool operator == (TargetClass t1) { + return (Target.Raw == t1.Target.Raw); + }; + + UnitClass * As_Unit(void); + BuildingClass * As_Building(void); + bool Legal(void); + CELL As_Cell(void); + COORDINATE As_Coord(void); + int Distance(TechnoClass *base); + static int As_Target(UnitClass *unit); + static int As_Target(BuildingClass *building); + static int As_Target(CELL cell); + + private: + + /* + ** This is the special encoded target value. + */ + union { + struct { + unsigned Value:TARGET_MANTISSA; + KindType Kind:TARGET_EXPONENT; + } Component; + int Raw; + } Target; + + + static int Build(int value, KindType kind); +}; +#endif + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TCPIP.CPP b/TIBERIANDAWN/TCPIP.CPP new file mode 100644 index 000000000..0f97cfcce --- /dev/null +++ b/TIBERIANDAWN/TCPIP.CPP @@ -0,0 +1,1002 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 20th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Member functions of the TcpipManagerClass which provides the Winsock * + * interface for C&C * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * TMC::Close -- restores any currently in use Winsock resources * + * TMC::Init -- Initialised Winsock for use. * + * TMC::Start_Server -- Initialise connection and start listening. * + * TMC::Read -- read any pending input from the stream socket * + * TMC::Write -- Send data via the Winsock streaming socket * + * TMC::Add_Client -- A client has requested to connect. * + * TMC::Message_Handler -- Message handler for Winsock. * + * TMC::Set_Host_Address -- Set the address of the host * + * TMC::Start_Client -- Start trying to connect to a game host * + * TMC::Close_Socket -- Close an opened Winsock socket. * + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "tcpip.h" + +#ifdef FORCE_WINSOCK + + +/* +** Nasty globals +*/ +bool Server; //Is this player acting as client or server +TcpipManagerClass Winsock; //The object for interfacing with Winsock + + + +/*********************************************************************************************** + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +TcpipManagerClass::TcpipManagerClass(void) +{ + WinsockInitialised = FALSE; + Connected = FALSE; + UseUDP = TRUE; + SocketReceiveBuffer = 4096; + SocketSendBuffer = 4096; + +} + + +/*********************************************************************************************** + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +TcpipManagerClass::~TcpipManagerClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * TMC::Close -- restores any currently in use Winsock resources * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + if (Async){ + WSACancelAsyncRequest(Async); + } + + /* + ** Close any open sockets + */ + if (ConnectSocket != INVALID_SOCKET){ + Close_Socket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + if (ListenSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + ListenSocket = INVALID_SOCKET; + } + + if (UDPSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + UDPSocket = INVALID_SOCKET; + } + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = FALSE; + Connected = FALSE; +} + + + +/*********************************************************************************************** + * TMC::Init -- Initialised Winsock for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (TRUE); + + /* + ** Initialise sockets to null + */ + ListenSocket = INVALID_SOCKET; + ConnectSocket =INVALID_SOCKET; + UDPSocket = INVALID_SOCKET; + + /* + ** Start WinSock, and fill in our WinSockData + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, &WinsockInfo); + if (rc != 0) { + return (FALSE); + } + + /* + ** Check the Winsock version number + */ + if ((WinsockInfo.wVersion & 0x00ff) != (version & 0x00ff) || + (WinsockInfo.wVersion >> 8) != (version >> 8)) { + return (FALSE); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = TRUE; + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Start_Server -- initialise out connection as the server. Start listening for clients. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:56PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Server(void) +{ + int i; + //struct sockaddr_in addr; + + Start_Client(); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + //InBufferHead = 0; + //InBufferTail = 0; + //OutBufferHead= 0; + //OutBufferTail= 0; + + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; ih_name); + } + Async = 0; + return; + + }else{ + /* + ** We are the client + */ + ConnectStatus = CONTACTING_SERVER; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (Server.Name, hentry->h_name); + } + else { + Server.Name[0] = 0; + } + Async = 0; + return; + } + + + /* + ** Retrieve host by name: Start connecting now that we have the + ** address. + */ + case WM_HOSTBYNAME: + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + memcpy (&(Server.Addr.s_addr), hentry->h_addr, 4); + memcpy(&UDPIPAddress, hentry->h_addr, 4); + strcpy (Server.DotAddr, inet_ntoa(Server.Addr)); + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + Server.Name[0] = 0; + strcpy (Server.DotAddr, "????"); + ConnectStatus = SERVER_ADDRESS_LOOKUP_FAILED; + } + Async = 0; + return; + + + /* + ** Connection is ready - accept the client + */ + case WM_ACCEPT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + return; + } + if (Add_Client()) { + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + } + return; + + + + /* + ** Handle UDP packet events + */ + case WM_UDPASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + + case FD_READ: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + addr_len = sizeof(addr); + rc = recvfrom(UDPSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0, + (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(UDPSocket); + return; + } + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr, 4); + Copy_To_In_Buffer(rc); + return; + + + case FD_WRITE: + if (UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, &UDPIPAddress, 4); + + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (TransmitBuffers[TXBufferTail].InUse){ + rc = sendto(UDPSocket, + TransmitBuffers[TXBufferTail].Buffer, + TransmitBuffers[TXBufferTail].DataLength, + 0, + (LPSOCKADDR)&addr, + sizeof (addr)); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(UDPSocket); + } + break; + } + TransmitBuffers[TXBufferTail++].InUse = false; + TXBufferTail &= WS_NUM_TX_BUFFERS-1; + } + return; + } + } + + + + /* + ** Handle the asynchronous event callbacks + */ + case WM_ASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + /* + ** FD_CLOSE: the client has gone away. Remove the client from our system. + */ + case FD_CLOSE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0 && rc != WSAECONNRESET) { + ConnectStatus = CONNECTION_LOST; + return; + } + if (Async != 0) { + WSACancelAsyncRequest(Async); + } + WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, 0); + Close_Socket (ConnectSocket); + ConnectSocket = INVALID_SOCKET; + //Connected = FALSE; + ConnectStatus = CONNECTION_LOST; + break; +#if (0) + /* + ** FD_READ: Data is available on our socket. This message is sent every time + ** data becomes available so we only have to do one read + */ + case FD_READ: + if (!UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + return; + } + rc = recv(ConnectSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(ConnectSocket); + return; + } + Copy_To_In_Buffer(rc); + return; + } + + /* + ** FD_WRITE: The socket is available for writing. We may actually have sent this + ** message from elewhere in the class. + */ + case FD_WRITE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + return; + } + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (OutBufferHead > OutBufferTail){ + rc = send(ConnectSocket, OutBuffer + OutBufferTail, + OutBufferHead - OutBufferTail, 0); + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(ConnectSocket); + } + break; + } + OutBufferTail+=rc; + } + if (OutBufferHead == OutBufferTail){ + OutBufferHead = OutBufferTail = 0; + } + return; +#endif //(0) + /* + ** FD_CONNECT: A connection was made, or an error occurred. + */ + case FD_CONNECT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_CONNECT; + return; + } + + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + return; + + } + } +} + + + +/*********************************************************************************************** + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer to our internal buffer * + * * + * * + * * + * INPUT: bytes to copy * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:17PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Copy_To_In_Buffer(int bytes) +{ + if (!ReceiveBuffers[RXBufferHead].InUse){ + memcpy (ReceiveBuffers[RXBufferHead].Buffer, ReceiveBuffer, MIN(bytes, WS_INTERNET_BUFFER_LEN)); + ReceiveBuffers[RXBufferHead].InUse = true; + ReceiveBuffers[RXBufferHead++].DataLength = MIN(bytes, WS_INTERNET_BUFFER_LEN); + RXBufferHead &= WS_NUM_RX_BUFFERS-1; + } +} + + + +/*********************************************************************************************** + * TMC::Set_Host_Address -- Set the address of the host game we want to connect to * + * * + * * + * * + * INPUT: ptr to address string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Set_Host_Address(char *address) +{ + strcpy(HostAddress, address); +} + + + +/*********************************************************************************************** + * TMC::Start_Client -- Start trying to connect to a game host * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Client(void) +{ + struct sockaddr_in addr; + bool delay = true; + int i; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ +// InBufferHead = 0; +// InBufferTail = 0; +// OutBufferHead= 0; +// OutBufferTail= 0; + + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; iIniName) == 0) { + return(index); + } + } + } + return(TERRAIN_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TerrainTypeClass::Display -- Display a generic terrain object. * + * * + * This routine is used to display a generic terrain object. Typical * + * use is during scenario editing. * + * * + * INPUT: x,y -- Pixel coordinates to display object at (centered). * + * * + * window-- The window to display the object within. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Display(int x, int y, WindowNumberType window, HousesType) const +{ + CC_Draw_Shape(Get_Image_Data(), 0, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * * + * Submits all of the valid terrain objects to the scenario editor for possible selection * + * and subsequent placement on the map. All terrain objects, that have a valid shape * + * file available, are added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Prep_For_Add(void) +{ + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * * + * This support routine is used by the scenario editor to add a terrain object to the map. * + * * + * INPUT: cell -- The cell to place the terrain object in. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TerrainClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * * + * This is used to create a terrain object by using the terrain type as a guide. This * + * routine is typically used by the scenario editor in order to place a terrain object * + * onto the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the created terrain object or NULL if one couldn't be * + * created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TerrainTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TerrainClass(Type, -1)); +} + + +short const * TerrainTypeClass::Occupy_List(bool ) const +{ + if (Occupy) return(Occupy); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +short const * TerrainTypeClass::Overlap_List(void) const +{ + if (Overlap) return(Overlap); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} diff --git a/TIBERIANDAWN/TEAM.CPP b/TIBERIANDAWN/TEAM.CPP new file mode 100644 index 000000000..f14214fb0 --- /dev/null +++ b/TIBERIANDAWN/TEAM.CPP @@ -0,0 +1,1509 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\team.cpv 2.18 16 Oct 1995 16:48:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : August 6, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Mission_Target -- Sets teams mission target and clears old target * + * TeamClass::Add -- Adds specified object to team. * + * TeamClass::AI -- Process team logic. * + * TeamClass::As_Target -- Converts this team object into a target number. * + * TeamClass::Calc_Center -- Determines average location of team members. * + * TeamClass::Control -- Updates control on a member unit. * + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * TeamClass::Coordinate_Unload -- Tells the team to unload passengers now. * + * TeamClass::Detach -- Removes specified target from team tracking. * + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * TeamClass::Remove -- Removes the specified object from the team. * + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * TeamClass::Validate -- validates team pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "mission.h" + +/* +** This array records the number of teams in existance of each type. +*/ +unsigned char TeamClass::Number[TEAMTYPE_MAX]; + +/* +** This array records the success rating of each of the team types. +*/ +unsigned char TeamClass::Success[TEAMTYPE_MAX]; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TeamClass::VTable; + + +/*********************************************************************************************** + * TeamClass::Validate -- validates team pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TeamClass::Validate(void) const +{ + int num; + + num = Teams.ID(this); + if (num < 0 || num >= TEAM_MAX) { + Validate_Error("TEAM"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * * + * This routine clears out the team object array in preparation for starting a new * + * scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Init(void) +{ + TeamClass *ptr; + + Teams.Free_All(); + memset(Number, 0, sizeof(Number)); + memset(Success, 0, sizeof(Success)); + + ptr = new TeamClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +void * TeamClass::operator new (size_t) +{ + void * ptr = Teams.Allocate(); + if (ptr) { + ((TeamClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +void TeamClass::operator delete(void * ptr) +{ + if (ptr) { + ((TeamClass *)ptr)->IsActive = false; + } + Teams.Free((TeamClass *)ptr); +} + + +TeamClass::~TeamClass(void) +{ + if (GameActive && Class) { + Number[TeamTypes.ID(Class)]--; + while (Member) { + Remove(Member); + } + + if (Class->IsTransient && !Number[TeamTypes.ID(Class)]) { + delete (TeamTypeClass *)Class; + } + } +} + + +TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : + Class(type), + House(owner) +{ + memset(Quantity, 0, sizeof(Quantity)); + IsAltered = true; + IsForcedActive = false; + IsFullStrength = false; + IsUnderStrength = true; + IsReforming = false; + IsLagging = false; + IsMoving = false; + IsHasBeen = false; + Center = 0; + Target = TARGET_NONE; + ObjectiveCenter = 0; + MissionTarget = TARGET_NONE; + Member = 0; + Total = 0; + Risk = 0; + CurrentMission = -1; + IsNextMission = true; + TimeOut = 0; + SuspendTimer.Clear(); + Suspended = false; + Number[TeamTypes.ID(Class)]++; +} + + +/*************************************************************************** + * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Assign_Mission_Target(TARGET new_target) +{ + Validate(); + /* + ** First go through and find anyone who is currently targetting + ** the old mission target and clear their Tarcom. + */ + FootClass * unit = Member; + while (unit) { + bool tar = (unit->TarCom == MissionTarget); + bool nav = (unit->NavCom == MissionTarget); + if (tar || nav) { + + /* + ** If the unit was doing something related to the team mission + ** then we kick him into guard mode so that he is easy to change + ** missions for. + */ + unit->Assign_Mission(MISSION_GUARD); + + /* + ** If the unit's tarcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (nav) { + unit->Assign_Destination(TARGET_NONE); + } + + /* + ** If the unit's navcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (tar) { + unit->Assign_Target(TARGET_NONE); + } + } + unit = (FootClass *)unit->Member; + } + + /* + ** If there is not currently an override on the current mission target + ** then assign both MissionTarget and Target to the new target. If + ** there is an overide, allow the team to keep fighting the overide but + ** make sure they pick up on the new mission when they are ready. + */ + if (Target == MissionTarget || !Target_Legal(Target)) { + MissionTarget = Target = new_target; + } else { + MissionTarget = new_target; + } +} + + +/*********************************************************************************************** + * TeamClass::AI -- Process team logic. * + * * + * General purpose team logic is handled by this routine. It should be called once per * + * active team per game tick. This routine handles recruitment and assigning orders to * + * member units. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/06/1995 JLB : Choreographed gesture. * + *=============================================================================================*/ +void TeamClass::AI(void) +{ + Validate(); + int desired = 0; + int old_under = IsUnderStrength; + int old_full = IsFullStrength; + + /* + ** If the team has been suspended then we need to check if its time for + ** us to reactivate the team. If not, no team logic will be processed + ** for this team. + */ + if (Suspended) { + if (!SuspendTimer.Expired()) { + return; + } + Suspended = false; + } + + /* + ** If this team senses that its composition has been altered, then it should + ** recalculate the under strength and full strength flags. + */ + if (IsAltered) { + + for (int index = 0; index < Class->ClassCount; index++) { + desired += Class->DesiredNum[index]; + } + + if (Total) { + IsFullStrength = (Total == desired); + + /* + ** Human controlled teams are always considered full strength. This ensures + ** that no new team members will be recruited and the team won't go into + ** regroup logic. + */ + if (House->IsHuman) { + IsUnderStrength = false; + } else { + + /* + ** Reinforcable teams will revert (or snap out of) the under strength + ** mode when the members transition the magic 1/3 strength threshhold. + */ + if (Class->IsReinforcable) { + IsUnderStrength = (Total <= desired / 3); + } else { + + /* + ** Teams that are not flagged as reinforcable are never considered under + ** strength if the team has already started its main mission. This + ** ensures that once the team has started, it won't dally to pick up + ** new members. + */ + IsUnderStrength = !IsHasBeen; + } + } + IsAltered = false; + } else { + IsUnderStrength = true; + IsFullStrength = false; + Center = 0; + + /* + ** A team that exists on the player's side is automatically destroyed + ** when there are no team members left. This team was created as a + ** result of reinforcement logic and no longer needs to exist when there + ** are no more team members. + */ + if (House->IsHuman || IsHasBeen) { + Delete_This(); + return; + } + } + + /* + ** If the team has gone from under strength to no longer under + ** strength than the team needs to reform. + */ + if (old_under != IsUnderStrength) { + IsReforming = true; + } + } + + /* + ** If the team is under strength, then flag it to regroup. + */ + if (IsMoving && IsUnderStrength) { + IsMoving = false; + CurrentMission = -1; + if (Total) { + Calc_Center(Center, ObjectiveCenter); + + /* + ** When a team is badly damaged and needs to regroup it should + ** pick a friendly building to go and regroup at. Its first preference + ** should be somewhere near repair factory. If it cannot find a repair + ** factory then it should pick another structure that is friendly to + ** its side. + */ + CELL dest = Center; + int max = 0x7FFFFFFF; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b && !b->IsInLimbo && b->House == House && b->Class->Primary == WEAPON_NONE) { + CELL cell = Coord_Cell(b->Center_Coord()); + int dist = Map.Cell_Distance(cell, Center) * (Map.Cell_Threat(cell, House->Class->House) + 1); + + if (*b == STRUCT_REPAIR) { + dist >>= 1; + } + if (dist < max) { + cell = Member->Safety_Point(Center, cell, 2, 4); + if (cell != -1) { + max = dist; + dest = cell; + } + } + } + } + + // Should calculate a regroup location. + Target = ::As_Target(dest); + Coordinate_Move(); + return; + } else { + Center = 0; + } + } + + /* + ** Flag this team into action when it gets to full strength. Human owned teams are only + ** used for reinforcement purposes -- always consider them at full strength. + */ + if (!IsMoving && (IsFullStrength || IsForcedActive)) { + IsMoving = true; + IsHasBeen = true; + IsUnderStrength = false; + + /* + ** Infantry can do a gesture when they start their mission. Pick + ** a gesture at random. + */ + FootClass * techno = Member; + DoType doaction = (Random_Pick(1, 2) == 1) ? DO_GESTURE1 : DO_GESTURE2; + while (techno) { + if (!techno->IsInLimbo && techno->What_Am_I() == RTTI_INFANTRY) { + ((InfantryClass *)techno)->Do_Action(doaction); + } + + if (IsReforming || IsForcedActive) { + techno->IsInitiated = true; + } + + techno = techno->Member; + } + CurrentMission = -1; + IsNextMission = true; + IsForcedActive = false; + } + + /* + ** If the team is moving or if there is no center position for + ** the team, then the center position must be recalculated. + */ + if (IsReforming || IsMoving || Center == 0) { + Calc_Center(Center, ObjectiveCenter); + } + + /* + ** Try to recruit members if there is room to do so for this team. + ** Only try to recruit members for a non player controlled team. + */ + if (!IsMoving || (!IsFullStrength && Class->IsReinforcable) && !House->IsHuman) { + for (int index = 0; index < Class->ClassCount; index++) { + if (Quantity[index] < Class->DesiredNum[index]) { + Recruit(index); + } + } + } + + /* + ** If there are no members of the team and the team has reached + ** full strength at one time, then delete the team. + */ + if (!Member && IsHasBeen) { + Delete_This(); + return; + } + + /* + ** If the mission should be advanced to the next entry, then do so at + ** this time. Various events may cause the mission to advance, but it + ** all boils down to the following change-mission code. + */ + if (IsMoving && !IsReforming && IsNextMission) { + IsNextMission = false; + CurrentMission++; + if (CurrentMission < Class->MissionCount) { + TeamMissionStruct const * mission = &Class->MissionList[CurrentMission]; + + TimeOut = mission->Argument * (TICKS_PER_MINUTE/10); + Target = TARGET_NONE; + switch (mission->Mission) { + case TMISSION_MOVECELL: + Assign_Mission_Target(::As_Target((CELL)mission->Argument)); + break; + + case TMISSION_MOVE: + case TMISSION_UNLOAD: + /* + ** Argument can be a waypoint index or a direct target. + */ + if (mission->Argument < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target((CELL)Waypoint[mission->Argument])); + } else { + Assign_Mission_Target((TARGET)mission->Argument); + } + break; + + case TMISSION_ATTACKTARCOM: + Assign_Mission_Target(mission->Argument); + break; + + default: + Assign_Mission_Target(TARGET_NONE); + break; + } + } else { + Delete_This(); + return; + } + } + + /* + ** Perform mission of the team. This depends on the mission list. + */ + if (Member && IsMoving && !IsReforming && !IsUnderStrength) { + /* + ** If the current Target has been dealt with but the mission target + ** has not, then the current target needs to be reset to the mission + ** target. + */ + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** If the current mission is one that times out, then check for + ** this case. If it has timed out then advance to the next + ** mission in the list or disband the team. + */ + TeamMissionStruct const * mission = &Class->MissionList[CurrentMission]; + switch (mission->Mission) { + case TMISSION_ATTACKBASE: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_BUILDINGS)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKUNITS: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_VEHICLES|THREAT_INFANTRY)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKCIVILIANS: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_CIVILIANS)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKTARCOM: + case TMISSION_RAMPAGE: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_NORMAL)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_DEFENDBASE: + Coordinate_Move(); + break; + +// case TMISSION_HARVEST: +// Coordinate_Move(); +// break; + + case TMISSION_UNLOAD: + Coordinate_Unload(); + break; + + case TMISSION_MOVE: + Coordinate_Move(); + break; + + case TMISSION_RETREAT: + Coordinate_Move(); + break; + + case TMISSION_GUARD: + Coordinate_Regroup(); + break; + + case TMISSION_LOOP: + CurrentMission = mission->Argument-1; + IsNextMission = true; + break; + } + + /* + ** Check for mission time out condition. If the mission does in fact time out, then + ** flag it so that the team mission list will advance. + */ + switch (mission->Mission) { + case TMISSION_ATTACKBASE: + case TMISSION_ATTACKUNITS: + case TMISSION_ATTACKCIVILIANS: + case TMISSION_RAMPAGE: + case TMISSION_DEFENDBASE: + case TMISSION_UNLOAD: + case TMISSION_RETREAT: + case TMISSION_GUARD: + if (TimeOut.Expired()) { + IsNextMission = true; + } + break; + } + + } else { + if (IsMoving) { + IsReforming = !Coordinate_Regroup(); + } else { + Coordinate_Move(); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Add -- Adds specified object to team. * + * * + * Use this routine to add the specified object to the team. The object is checked to make * + * sure that it can be assigned to the team. If it can't, then the object will be left * + * alone and false will be returned. * + * * + * INPUT: obj -- Pointer to the object that is to be assigned to this team. * + * * + * typeindex-- Optional value that specifies the index in the team type class array * + * that this object belongs. * + * * + * OUTPUT: bool; Was the unit added to the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation flag setup. * + * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * + *=============================================================================================*/ +bool TeamClass::Add(FootClass * obj, int typeindex) +{ + Validate(); + /* + ** If this team doesn't accept new members, then don't accept this one either. + */ +// if (!Class->IsReinforcable && IsMoving) { +// return(false); +// } + + if (!obj || !obj->Strength || (obj->IsInLimbo && !ScenarioInit) || obj->In_Radio_Contact() || obj->House != House) { + return(false); + } + + TeamClass * team = obj->Team; + + /* + ** Trying to add the team member to itself is an error condition. Just return + ** with success, since the end result is the same. + */ + if (team == this) { + return(true); + } + + /* + ** If the object is doing some mission that precludes it from joining + ** a team then don't add it. + */ + if (obj->Mission == MISSION_STICKY || obj->Mission == MISSION_SLEEP || obj->Mission == MISSION_GUARD_AREA || obj->Mission == MISSION_HUNT || obj->Mission == MISSION_HARVEST) { + return(false); + } + + /* + ** If this object is part of another team, then check to make sure that it + ** is permitted to leave the other team in order to join this one. If not, + ** then no further processing is allowed -- bail. + */ + if (team && + (/*team->Total >= Total || team->IsMoving ||*/ + team->Class->RecruitPriority >= Class->RecruitPriority)) { + return(false); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. + ** On the chance that a match could not be found, then it is illegal to add this + ** object to this team -- return with failure flag. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Class[typeindex] == &obj->Class_Of()) { + break; + } + } + } + + /* + ** If the team is already full of this type, then adding the object is not allowed. + ** Return with a failure flag in this case. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + return(false); + } + + /* + ** All is ok to add the object to the team, but if the object is already part of + ** another team, then it must be removed from that team first. + */ + if (team) { + team->Remove(obj); + } + + /* + ** Actually add the object to the team. + */ + Quantity[typeindex]++; + obj->IsInitiated = (Member == NULL); + obj->Member = Member; + Member = obj; + obj->Team = this; + Total++; + Risk += obj->Risk(); + if (!Center) { + Calc_Center(Center, ObjectiveCenter); + } + + /* + ** Return with success, since the object was added to the team. + */ + IsAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Remove -- Removes the specified object from the team. * + * * + * Use this routine to remove an object from a team. Objects removed from the team are * + * then available to be recruited by other teams, or even by the same team at a later time. * + * * + * INPUT: obj -- Pointer to the object that is to be removed from this team. * + * * + * typeindex-- Optional index of where this object type is specified in the type * + * type class. This parameter can be omitted. It only serves to make * + * the removal process faster. * + * * + * OUTPUT: bool; Was the object removed from this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation tracking and team captain selection. * + *=============================================================================================*/ +bool TeamClass::Remove(FootClass * obj, int typeindex) +{ + Validate(); + /* + ** Make sure that the object is in fact a member of this team. If not, then it can't + ** be removed. Return success because the end result is the same. + */ + if (obj->Team != this) { + return(true); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. The + ** team type class will not be set if the appropriate type could not be found + ** for this object. This indicates that the object was illegally added. Continue to + ** process however, since removing this object from the team is a good idea. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Class[typeindex] == &obj->Class_Of()) { + break; + } + } + } + + /* + ** Decrement the counter for the team class. There is now one less of this object type. + */ + if ((unsigned)typeindex < Class->ClassCount) { + Quantity[typeindex]--; + } + + /* + ** Actually remove the object from the team. Scan through the team members + ** looking for the one that matches the one specified. If it is found, it + ** is unlinked from the member chain. During this scan, a check is made to + ** ensure that at least one remaining member is still initiated. If not, then + ** a new team captain must be chosen. + */ + bool initiated = false; + FootClass * prev = 0; + FootClass * curr = Member; + bool found = false; + while (curr && (!found || !initiated)) { + if (curr == obj) { + if (prev) { + prev->Member = curr->Member; + } else { + Member = curr->Member; + } + FootClass * temp = curr->Member; + curr->Member = 0; + curr->Team = 0; + curr = temp; + Total--; + found = true; + Risk -= obj->Risk(); + continue; + } + + /* + ** If this (remaining) member is initiated, then keep a record of this. + */ + initiated |= curr->IsInitiated; + + prev = curr; + curr = curr->Member; + } + + /* + ** If, after removing the team member, there are no initiated members left + ** in the team, then just make the first remaining member of the team the + ** team captain. Mark the center location of the team as invalid so that + ** it will be centered around the captain. + */ + if (!initiated && Member) { + Member->IsInitiated = true; + Center = 0; + } + + /* + ** Must record that the team composition has changed. At the next opportunity, + ** the team members will be counted and appropriate AI adjustments made. + */ + IsAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * * + * This routine will take the given index ID and scan for available objects of that type * + * to recruit to the team. Recruiting will continue until that object type has either * + * been exhausted or if the team's requirment for that type has been filled. * + * * + * INPUT: typeindex -- The index for the object type to recruit. The index is used to * + * look into the type type's array of object types that make up this * + * team. * + * * + * OUTPUT: Returns with the number of objects added to this team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 04/10/1995 JLB : Scans for units too. * + *=============================================================================================*/ +int TeamClass::Recruit(int typeindex) +{ + Validate(); + int added = 0; // Total number added to team. + + /* + ** Quick check to see if recruiting is really allowed for this index or not. + */ + if (Class->DesiredNum[typeindex] > Quantity[typeindex]) { + + /* + ** For infantry objects, sweep through the infantry in the game looking for + ** ones owned by the house that owns the team. When found, try to add. + */ + if (Class->Class[typeindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == House && infantry->Class == Class->Class[typeindex]) { + if (Add(infantry, typeindex)) { + added++; + } + } + + /* + ** If there is sufficient quantity of this type of object recruited to the + ** team, then abort further scanning for members. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + break; + } + } + } + + if (Class->Class[typeindex]->What_Am_I() == RTTI_UNITTYPE) { + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == House && unit->Class == Class->Class[typeindex]) { + if (Add(unit, typeindex)) { + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = unit->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)f->Next; + } + } + } + + /* + ** If there is sufficient quantity of this type of object recruited to the + ** team, then abort further scanning for members. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + break; + } + } + } + } + + return(added); +} + + +/*********************************************************************************************** + * TeamClass::Detach -- Removes specified target from team tracking. * + * * + * When a target object is about to be removed from the game (e.g., it was killed), then * + * any team that is looking at that target must abort from that target. * + * * + * INPUT: target -- The target object that is going to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Detach(TARGET target, bool ) +{ + Validate(); + + /* + ** If the target to detatch matches the target of this team, then remove + ** the target from this team's Tar/Nav com and let the chips fall + ** where they may. + */ + if (Target == target) { + Target = TARGET_NONE; + } + if (MissionTarget == target) { + MissionTarget = TARGET_NONE; + } + +} + + +/*********************************************************************************************** + * TeamClass::As_Target -- Converts this team object into a target number. * + * * + * This routine is used by the save/load code to produce a persistant identifier for this * + * team object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the team represented as a target number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +TARGET TeamClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEAM, Teams.ID(this))); +} + + +/*********************************************************************************************** + * TeamClass::Calc_Center -- Determines average location of team members. * + * * + * Use this routine to calculate the "center" location of the team. This is the average * + * position of all members of the team. Using this center value it is possible to tell * + * if a team member is too far away and where to head to in order to group up. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number of the team's center point. If the team contains * + * no members, then the return value will be zero. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Calc_Center(CELL ¢er, CELL &obj_center) const +{ + Validate(); + long x = 0; + long y = 0; + int dist = 0x7FFFFFFF; + int quantity = 0; + FootClass * unit; + + obj_center = 0; + center = 0; + + unit = Member; + while (unit) { + if (unit->IsInitiated && !unit->IsInLimbo) { + CELL c = Coord_Cell(unit->Center_Coord()); + if (unit->Distance(Target) < dist) { + dist = unit->Distance(Target); + obj_center = c; + } + x += Cell_X(c); + y += Cell_Y(c); + quantity++; + } + unit = unit->Member; + } + if (quantity) { + x /= quantity; + y /= quantity; + CELL cell = XY_Cell((int)x, (int)y); + center = cell; + } +} + + +/*********************************************************************************************** + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * * + * This routine is used when a team member takes damage. Usually the team will react in * + * some fashion to the attack. This reaction can range from running away to assigning this * + * new target as the team's target. * + * * + * INPUT: obj -- The team member that was damaged. * + * * + * result -- The severity of the damage taken. * + * * + * source -- The purpetrator of the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) +{ + Validate(); + if ((result != RESULT_NONE) && (!Class->IsSuicide)) { + if (!IsMoving) { + // Should run to a better hiding place or disband into a group of hunting units. + } else { + + if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT) { + if (Target != source->As_Target()) { + + /* + ** Don't change target if the team's target is one that can fire as well. There is + ** no point in endlessly shuffling between targets that have firepower. + */ + if (Target_Legal(Target)) { + TechnoClass * techno = As_Techno(Target); + + if (techno && ((TechnoTypeClass const &)techno->Class_Of()).Primary != WEAPON_NONE) { + if (techno->In_Range(Cell_Coord(Center), 0)) { + return; + } + } + } + Target = source->As_Target(); + } + } + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * * + * This function is called when the team knows what it should attack. This routine will * + * give the necessary orders to the members of the team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Attack(void) +{ + Validate(); + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (!Target_Legal(Target)) { + IsNextMission = true; + + } else { + + FootClass * unit = Member; + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { + unit->Assign_Mission(MISSION_ATTACK); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + + if (unit->TarCom != Target) { + unit->Assign_Target(Target); + } + } + + unit = unit->Member; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * * + * This routine is called when the team must delay at its current location. Team members * + * are grouped together by this function. It is called when the team needs to sit and * + * wait. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Regroup(void) +{ + Validate(); + FootClass * unit = Member; + bool retval = true; + + /* + ** Regroup default logic. + */ + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->Distance(Center) > STRAY_DISTANCE && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { + if (unit->Mission != MISSION_MOVE || !Target_Legal(unit->NavCom) || ::Distance(As_Cell(unit->NavCom), Center) > STRAY_DISTANCE) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(Center)); + } + retval = false; + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered intiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (unit->Mission != MISSION_GUARD_AREA) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } + return(retval); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * * + * This routine is called when the team must move to a new location. Movement and grouping * + * commands associated with this task are initiated here. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Move(void) +{ + Validate(); + FootClass * unit = Member; + bool finished = true; + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (Target_Legal(Target)) { + + if (!Lagging_Units()) { + + + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->What_Am_I() != RTTI_AIRCRAFT && unit->Distance(Center) > STRAY_DISTANCE) { + IsLagging = true; + finished = false; + } else { + + if ((unit->Distance(Target)/ICON_LEPTON_W) > STRAY_DISTANCE || + (unit->What_Am_I() == RTTI_AIRCRAFT && + ((AircraftClass *)unit)->Altitude > 0 && + Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { + + if (unit->Mission != MISSION_MOVE) { + unit->Assign_Mission(MISSION_MOVE); + } + if (unit->NavCom != Target) { + unit->Assign_Destination(Target); + } + finished = false; + } else { + if (unit->Mission == MISSION_MOVE && !Target_Legal(unit->NavCom)) { + unit->Enter_Idle_Mode(); + } + } + } + } + + unit = unit->Member; + } + } else { + finished = false; + } + } + + /* + ** If all the team members are close enough to the desired destination, then + ** move to the next mission. + */ + if (finished && IsMoving) { + IsNextMission = true; + } +} + + +/*************************************************************************** + * Lagging_Units -- processes units that cant keep up with the pack * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 08/01/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Lagging_Units(void) +{ + Validate(); + FootClass * unit = Member; + bool lag = false; + + /* + ** If the IsLagging bit is not set, then obviously there are no lagging + ** units. + */ + if (!IsLagging) return(false); + + /* + ** Scan through all of the units, searching for units who are having + ** trouble keeping up with the pack. + */ + while (unit) { + + if (!unit->IsInLimbo) { + /* + ** If we find a unit who has fallen to far away from the center of + ** the pack, then we need to order that unit to catch up with the + ** first unit. + */ + if (unit->Distance(ObjectiveCenter) > STRAY_DISTANCE) { + if (unit->Mission != MISSION_MOVE || !Target_Legal(unit->NavCom) || ::Distance(As_Cell(unit->NavCom), ObjectiveCenter) > STRAY_DISTANCE) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(ObjectiveCenter)); + } + lag = true; + } else { + /* + ** We need to order all of the other units to hold there + ** position until all lagging units catch up. + */ + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + unit = unit->Member; + } + + /* + ** Once we have handled the loop we know whether there are any lagging + ** units or not. + */ + IsLagging = lag; + return(lag); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Unload -- Tells the team to unload passengers now. * + * * + * This routine tells all transport vehicles to unload passengers now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Unload(void) +{ + Validate(); + FootClass * unit = Member; + bool finished = true; + + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + if (unit->Is_Something_Attached()) { + + /* + ** Loaner transports will break off of the team at this time. The normal + ** unload logic for the transport will proceed normally. The rest of the team + ** members will be in a dormant state until they are unloaded. + */ + if (unit->IsALoaner) { + Remove(unit); + unit->Commence(); + unit->Assign_Mission(MISSION_UNLOAD); + unit->Assign_Destination(Target); + } else { + if (unit->Mission != MISSION_UNLOAD) { + unit->Assign_Mission(MISSION_UNLOAD); + unit->Assign_Destination(Target); + } + } + finished = false; + } + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * * + * This routine will give the movement orders to the conscript so that it will group * + * with the other members of the team. * + * * + * INPUT: unit -- Pointer to the conscript unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Conscript(FootClass * unit) +{ + Validate(); + if (unit && !unit->IsInitiated && !unit->IsInLimbo) { + if (unit->Distance(Center) > STRAY_DISTANCE) { + if (!Target_Legal(unit->NavCom)) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(::As_Target(Center)); + } + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered intiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + } + } +} + + +/*************************************************************************** + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Is_A_Member(void const * who) const +{ + Validate(); + FootClass * unit = Member; + while (unit) { + if (unit == who) { + return(true); + } + unit = unit->Member; + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * * + * INPUT: int priority - determines what is considered low priority. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/19/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Suspend_Teams(int priority) +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass *team = Teams.Ptr(index); + + /* + ** If a team is below the "survival priority level", then it gets + ** destroyed. The team members are then free to be reassigned. + */ + if (team && team->Class->RecruitPriority < priority) { + FootClass * unit = team->Member; + while (team->Member) { + team->Remove(team->Member); + } + team->IsAltered = true; + team->SuspendTimer = TICKS_PER_MINUTE*2; + team->Suspended = true; + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/TEAM.H b/TIBERIANDAWN/TEAM.H new file mode 100644 index 000000000..2d41072f9 --- /dev/null +++ b/TIBERIANDAWN/TEAM.H @@ -0,0 +1,255 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\team.h_v 2.16 16 Oct 1995 16:48:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : December 11, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAM_H +#define TEAM_H + +#include +#include "teamtype.h" +#include "abstract.h" + +/* +** Units are only allowed to stray a certain distance away from their +** team. When they exceed this distance, some sort of fixup must be +** done. +*/ +#define STRAY_DISTANCE 2 + +class TeamClass : public AbstractClass +{ +#ifdef USE_RA_AI + friend class HouseClass; // Needed for RA AI moved into C&C. ST - 7/24/2019 2:44PM +#endif // USE_RA_AI + + public: + /* + ** This specifies the type of team this is. + */ + TeamTypeClass const * const Class; + + /* + ** This specifies the owner of this team. + */ + HouseClass * const House; + + /* + ** This flag forces the team into active state regardless of whether it + ** is understrength or not. + */ + unsigned IsForcedActive:1; + + /* + ** This flag is set to true when the team initiates into active mode. The + ** flag is never cleared. By examining this flag, it is possible to determine + ** if the team has ever launched into active mode. + */ + unsigned IsHasBeen:1; + + /* + ** If the team is full strength, then this flag is true. A full strength + ** team will not try to recruit members. + */ + unsigned IsFullStrength:1; + + /* + ** A team that is below half strength has this flag true. It means that the + ** the team should hide back at the owner's base and try to recruit + ** members. + */ + unsigned IsUnderStrength:1; + + /* + ** If a team is not understrength but is not yet full strength, then + ** the team is regrouping. If this flag is set and the team becomes + ** full strength, the all members of the team will become initiated + ** and this flag will be reset. + */ + unsigned IsReforming:1; + + /* + ** This bit should be set if a team is determined to have lagging + ** units in its formation. + */ + unsigned IsLagging:1; + + private: + /* + ** If a team member was removed or added, then this flag will be set to true. The + ** team system uses this flag to tell whether it should recalculate the team + ** under strength or full strength flags. This process does not need to occur + ** EVERY time a unit added or deleted from a team, just every so often if the + ** team has been changed. + */ + unsigned IsAltered:1; + + /* + ** If the team is working on it's primary mission (it is past the build up stage) + ** then this flag will be true. The transition between "moving" and "stationary" + ** stages usually requires some action on the team's part. + */ + unsigned IsMoving:1; + + /* + ** When the team determines that the next mission should be advanced to, it will + ** set this flag to true. Mission advance will either change the behavior of the + ** team or cause it to disband. + */ + unsigned IsNextMission:1; + /* + ** Records whether the team is suspended from production. + */ + unsigned Suspended:1; + + public: + /* + ** A team will have a center point. This is the point used to determine if + ** any member of the team is "too far" from the team and must return. This + ** center point is usually calculated as the average position of all the + ** team members. + */ + CELL Center; + CELL ObjectiveCenter; + + /* + ** This is the target of the team. Typically, it is a unit or structure, but + ** for the case of teams with a movement mission, it might represent a + ** destination cell. + */ + TARGET MissionTarget; + TARGET Target; + + /* + ** This is the total number of members in this team. + */ + int Total; + + /* + ** This is the teams combined risk value + */ + int Risk; + /* + ** This is the amount of time the team is suspended for. + */ + TCountDownTimerClass SuspendTimer; + + //------------------------------------------------------------ + TeamClass(void) : Class(0), House(0) {IsActive=false;Member=0;IsAltered=true;}; + TeamClass(TeamTypeClass const * team, HouseClass * owner); + virtual ~TeamClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_TEAM;}; + static void operator delete(void *ptr); + static void * operator new(size_t size); + static void Init(void); + static void Suspend_Teams(int priority); + + TARGET As_Target(void) const; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + void Force_Active(void) {IsForcedActive = true;IsUnderStrength=false;}; + bool Remove(FootClass *, int typeindex=-1); + void Detach(TARGET target, bool all); + void AI(void); + void Took_Damage(FootClass * obj, ResultType result, TechnoClass * source); + bool Add(FootClass *, int typeindex=-1); + void Assign_Mission_Target(TARGET new_target); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** This is a record of the current number of active teams of each + ** type. It can range from zero to MaxAllowed. + */ + static unsigned char Number[TEAMTYPE_MAX]; + + private: + + /* + ** The current mission index into the mission list is recorded here. + */ + int CurrentMission; + + /* + ** Some missions will time out. This is the timer that keeps track of the + ** time to transition between missions. + */ + TCountDownTimerClass TimeOut; + + void Coordinate_Unload(void); + bool Coordinate_Regroup(void); + void Coordinate_Attack(void); + void Coordinate_Move(void); + void Coordinate_Conscript(FootClass * unit); +// void Control(FootClass *, bool initial=false); + void Calc_Center(CELL ¢er, CELL &obj_center) const; + int Recruit(int typeindex); + bool Is_A_Member(void const * who) const; + bool Lagging_Units(void); + + /* + ** Points to the first member in the list of members for this team. + */ + FootClass * Member; + + unsigned char Quantity[TeamTypeClass::MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /* + ** This records the success of each team type. As the team carries out its + ** mission, it increments this counter if it considers the mission + ** to have been successfully completed. Teams with greater success + ** will be created more than the others. + */ + static unsigned char Success[TEAMTYPE_MAX]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TEAMTYPE.CPP b/TIBERIANDAWN/TEAMTYPE.CPP new file mode 100644 index 000000000..1430b79fb --- /dev/null +++ b/TIBERIANDAWN/TEAMTYPE.CPP @@ -0,0 +1,977 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\teamtype.cpv 2.17 16 Oct 1995 16:48:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : July 21, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TeamTypeClass::TeamTypeClass -- class constructor * + * TeamTypeClass::~TeamTypeClass -- class destructor * + * TeamTypeClass::Init -- pre-scenario initialization * + * TeamTypeClass::Read_INI -- reads INI data * + * TeamTypeClass::Write_INI -- writes INI data * + * TeamTypeClass::Read_Old_INI -- reads old INI format * + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * TeamTypeClass::Remove -- removes this team from the system * + * TeamTypeClass::Mission_From_Name -- returns mission for given name * + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * TeamTypeClass::operator new -- 'new' operator * + * TeamTypeClass::operator delete -- 'delete' operator * + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * TeamTypeClass::Validate -- validates teamtype pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +********************************** Globals ********************************** +*/ +char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { + "Attack Base", + "Attack Units", + "Attack Civil.", + "Rampage", + "Defend Base", +// "Harvest", + "Move", + "Move to Cell", + "Retreat", + "Guard", + "Loop", + "Attack Tarcom", + "Unload", +}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TeamTypeClass::VTable; + + +/*********************************************************************************************** + * TeamTypeClass::Validate -- validates teamtype pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TeamTypeClass::Validate(void) const +{ + int num; + + num = TeamTypes.ID(this); + if (num < 0 || num >= TEAMTYPE_MAX) { + Validate_Error("TEAMTYPE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*************************************************************************** + * TeamTypeClass::TeamTypeClass -- class constructor * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass::TeamTypeClass(void) +{ + IsPrebuilt = true; + IsReinforcable = true; + IsRoundAbout = false; + IsLearning = false; + IsSuicide = false; + IsAutocreate = false; + IsTransient = false; + IsMercenary = false; + RecruitPriority = 7; + MaxAllowed = 0; + Fear = 0; + InitNum = 0; + House = HOUSE_NONE; + MissionCount = 0; + IniName[0] = '\0'; + ClassCount = 0; + for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { + Class[i] = NULL; + DesiredNum[i] = 0; + } +} + + +/*************************************************************************** + * TeamTypeClass::Init -- pre-scenario initialization * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Init(void) +{ + TeamTypeClass *ptr; + + TeamTypes.Free_All(); + + ptr = new TeamTypeClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractTypeClass) - 4))[0]; + delete ptr; +} + + +/*************************************************************************** + * TeamTypeClass::Read_INI -- reads INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_INI(char *buffer) +{ + TeamTypeClass * team; // Working team pointer. + char *tbuffer; // Accumulation buffer of team names. + int len; // Length of data in buffer. + char buf[500]; // INI entry buffer + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point just past the INI buffer + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all TeamType entry names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ----------------------- Loop for all team entries ------------------------ + */ + while (*tbuffer != '\0') { + /* + ....................... Create a new team type ........................ + */ + team = new TeamTypeClass(); + + /* + ......................... Get the team entry .......................... + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + .......................... Fill the team in ........................... + */ + team->Fill_In(tbuffer, buf); + + /* + ...................... Go to the next INI entry ....................... + */ + tbuffer += strlen(tbuffer)+1; + } + + /* + ** If no teams were read in, try reading the old INI format. + */ + if (TeamTypes.Count()==0) { + Read_Old_INI(buffer); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given teamtype with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Fill_In(char * name, char *entry) +{ + Validate(); + int num_classes; + char *p1; // parsing pointer + char *p2; // parsing pointer + int i; // loop counter + TechnoTypeClass const *otype; // ptr to type of object + InfantryType i_id; // infantry ID + UnitType u_id; // unit ID + AircraftType a_id; // aircraft ID + TeamMissionStruct mission; + + /* + ------------------------------ Set its name ------------------------------ + */ + Set_Name(name); + + /* + ---------------------------- 1st token: House ---------------------------- + */ + House = HouseTypeClass::From_Name(strtok(entry,",")); + + /* + -------------------------- 2nd token: RoundAbout ------------------------- + */ + IsRoundAbout = atoi(strtok(NULL,",")); + + /* + --------------------------- 3rd token: Learning -------------------------- + */ + IsLearning = atoi(strtok(NULL,",")); + + /* + --------------------------- 4th token: Suicide --------------------------- + */ + IsSuicide = atoi(strtok(NULL,",")); + + /* + ----------------------------- 5th token: Spy ----------------------------- + */ + IsAutocreate = atoi(strtok(NULL,",")); + + /* + -------------------------- 6th token: Mercenary -------------------------- + */ + IsMercenary = atoi(strtok(NULL,",")); + + /* + ----------------------- 7th token: RecruitPriority ----------------------- + */ + RecruitPriority = atoi(strtok(NULL,",")); + + /* + -------------------------- 8th token: MaxAllowed ------------------------- + */ + MaxAllowed = atoi(strtok(NULL,",")); + + /* + --------------------------- 9th token: InitNum --------------------------- + */ + InitNum = atoi(strtok(NULL,",")); + + /* + ------------------------- 10th token: Fear level ------------------------- + */ + Fear = atoi(strtok(NULL,",")); + + /* + ------------------------ 11th token: Class count ------------------------- + */ + num_classes = atoi(strtok(NULL,",")); + + /* + -------------- Loop through entries, setting class ptr & num ------------- + */ + ClassCount = 0; + for (i = 0; i < num_classes; i++) { + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + otype = NULL; + + /* + ------------------- See if this is an infantry name ------------------- + */ + i_id = InfantryTypeClass::From_Name(p1); + if (i_id != INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } + + /* + ---------------------- See if this is a unit name --------------------- + */ + u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } + + /* + ------------------- See if this is an aircraft name ------------------- + */ + a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } + + /* + --------------- If the name was resolved, add this class -------------- + */ + if (otype) { + if (ClassCount < MAX_TEAM_CLASSCOUNT) { + Class[ClassCount] = otype; + DesiredNum[ClassCount] = atoi(p2); + ClassCount++; + } + } + } + + /* + ----------------------- next token: Mission count ------------------------ + */ + MissionCount = atoi(strtok(NULL,",")); + + for (i = 0; i < MissionCount; i++) { + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + mission.Mission = Mission_From_Name(p1); + mission.Argument = atoi(p2); + MissionList[i] = mission; + } + + char * ptr = strtok(NULL, ","); + if (ptr) { + IsReinforcable = atoi(ptr); + } + ptr = strtok(NULL, ","); + if (ptr) { + IsPrebuilt = atoi(ptr); + } +} + + +/*************************************************************************** + * TeamTypeClass::Write_INI -- writes INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission,Arg,Mission,Arg,Mission,Arg,... * + * * + * INPUT: * + * buffer buffer to store INI data in * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Write_INI(char *buffer, bool refresh) +{ + int index; + int i; + char buf[500]; + TeamTypeClass * team; + char const *hname; + + /*------------------------------------------------------------------------ + First, clear out all existing teamtypes in the old-style format. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString("Teams", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Clear out all existing teamtype data from the INI file. + ------------------------------------------------------------------------*/ + if (refresh) { + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + } + + /*------------------------------------------------------------------------ + Now write all the team data out + ------------------------------------------------------------------------*/ + buf[0] = 0; + for (index = 0; index < TeamTypes.Count(); index++) { + /* + .................. Get ptr to next active teamtype .................... + */ + team = TeamTypes.Ptr(index); + + /* + .......................... Find house's name .......................... + */ + if (team->House==HOUSE_NONE) { + hname = "None"; + } else { + hname = HouseClass::As_Pointer(team->House)->Class->IniName; + } + + /* + ......................... Generate INI entry .......................... + */ + sprintf(buf,"%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + hname, + team->IsRoundAbout, + team->IsLearning, + team->IsSuicide, + team->IsAutocreate, + team->IsMercenary, + team->RecruitPriority, + team->MaxAllowed, + team->InitNum, + team->Fear, + team->ClassCount); + + /*..................................................................... + For every class in the team, record the class's name & desired count + .....................................................................*/ + for (i = 0; i < team->ClassCount; i++) { + sprintf (buf + strlen(buf), ",%s:%d", + team->Class[i]->IniName, + team->DesiredNum[i]); + } + + /*..................................................................... + Record the # of missions, and each mission name & argument value. + .....................................................................*/ + sprintf(buf + strlen(buf),",%d",team->MissionCount); + for (i = 0; i < team->MissionCount; i++) { + sprintf (buf + strlen(buf), ",%s:%d", + Name_From_Mission(team->MissionList[i].Mission), + team->MissionList[i].Argument); + } + + if (team->IsReinforcable) { + strcat(buf, ",1"); + } else { + strcat(buf, ",0"); + } + if (team->IsPrebuilt) { + strcat(buf, ",1"); + } else { + strcat(buf, ",0"); + } + + WWWritePrivateProfileString(INI_Name(), team->IniName, buf, buffer); + } +} + + +/*************************************************************************** + * TeamTypeClass::Read_Old_INI -- reads old INI format * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Class:Num,Class:Num,...,Fear * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_Old_INI(char *buffer) +{ + TeamTypeClass * team; // Working team pointer. + char *tbuffer; // Accumulation buffer of team names. + int len; // Length of data in buffer. + char buf[256]; // INI entry buffer + char *p1; // parsing pointer + char *p2; // parsing pointer + int index; + TechnoTypeClass const *otype; // ptr to type of object + InfantryType i_id; // infantry ID + UnitType u_id; // unit ID + AircraftType a_id; // infantry ID + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point just past the INI buffer + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all TeamType entry names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Teams", NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ----------------------- Loop for all team entries ------------------------ + */ + while (*tbuffer != '\0') { + /* + ........................ Create a new trigger ......................... + */ + team = new TeamTypeClass(); + + /* + ............................ Set its name ............................. + */ + team->Set_Name(tbuffer); + + /* + ......................... Get the team entry .......................... + */ + WWGetPrivateProfileString("Teams", tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + .......................... 1st token: House ........................... + */ + team->House = HouseTypeClass::From_Name(strtok(buf,",")); + + /* + ........................ 2nd token: RoundAbout ........................ + */ + team->IsRoundAbout = atoi(strtok(NULL,",")); + + /* + ......................... 3rd token: Learning ......................... + */ + team->IsLearning = atoi(strtok(NULL,",")); + + /* + ......................... 4th token: Suicide .......................... + */ + team->IsSuicide = atoi(strtok(NULL,",")); + + /* + ........................... 5th token: Spy ............................ + */ + team->IsAutocreate = atoi(strtok(NULL,",")); + + /* + ........................ 6th token: Mercenary ......................... + */ + team->IsMercenary = atoi(strtok(NULL,",")); + + /* + ..................... 7th token: RecruitPriority ...................... + */ + team->RecruitPriority = atoi(strtok(NULL,",")); + + /* + ........................ 8th token: MaxAllowed ........................ + */ + team->MaxAllowed = atoi(strtok(NULL,",")); + + /* + ......................... 9th token: InitNum .......................... + */ + team->InitNum = atoi(strtok(NULL,",")); + + /* + ....................... 10th token: Mission name ...................... + */ + strtok(NULL,","); // just throw it away + + /* + ............ Loop through entries, setting class ptr & num ............ + */ + index = 0; + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + while (p1 && p2) { + otype = NULL; + + /* + ................. See if this is an infantry name .................. + */ + i_id = InfantryTypeClass::From_Name(p1); + if (i_id!=INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } + + /* + .................... See if this is a unit name .................... + */ + u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } + + /* + ................. See if this is an aircraft name .................. + */ + a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } + + /* + ............. If the name was resolved, add this class ............. + */ + if (otype) { + team->Class[index] = otype; + team->DesiredNum[index] = atoi(p2); + index++; + team->ClassCount = index; + } + + /* + ................. Go to the next entry on the line ................. + */ + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + } + + team->Fear = 0; + + /* + ...................... Go to the next INI entry ....................... + */ + tbuffer += strlen(tbuffer)+1; + } +} + + +/*************************************************************************** + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * * + * INPUT: * + * name name of teamtype * + * * + * OUTPUT: * + * ptr to TeamType with that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass * TeamTypeClass::As_Pointer(char *name) +{ + int i; + + if (name == NULL) { + return(NULL); + } + + for (i = 0; i < TeamTypes.Count(); i++) { + if (!stricmp(name, TeamTypes.Ptr(i)->IniName)) { + return(TeamTypes.Ptr(i)); + } + } + + return(NULL); +} + + +/*************************************************************************** + * TeamTypeClass::Remove -- removes this team from the system * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/09/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Remove(void) +{ + Validate(); + int i; + TriggerClass * trigger; + + /* + ** Remove all trigger references to this team. + */ + for (i = 0; i < Triggers.Count(); i++) { + trigger = Triggers.Ptr(i); + if (trigger->Team == this) { + trigger->Team = NULL; + } + } + + /* + ** Delete myself. + */ + delete this; +} + + +/*************************************************************************** + * TeamTypeClass::Mission_From_Name -- returns team mission for given name * + * * + * INPUT: * + * name name to compare * + * * + * OUTPUT: * + * mission for that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +TeamMissionType TeamTypeClass::Mission_From_Name(char const *name) +{ + int order; + + if (name) { + for (order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { + if (stricmp(TMissions[order], name) == 0) { + return((TeamMissionType) order); + } + } + } + + return(TMISSION_NONE); +} + + +/*************************************************************************** + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * * + * INPUT: * + * order mission to get name for * + * * + * OUTPUT: * + * name of mission * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) +{ + if (order <= TMISSION_NONE || order >= TMISSION_COUNT) { + return("None"); + } else { + return(TMissions[order]); + } +} + + +/*************************************************************************** + * TeamTypeClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new TeamType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void * TeamTypeClass::operator new(size_t ) +{ + void * ptr = TeamTypes.Allocate(); + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*************************************************************************** + * TeamTypeClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::operator delete(void *ptr) +{ + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = false; + } + TeamTypes.Free((TeamTypeClass *)ptr); +} + + + +TeamClass * TeamTypeClass::Create_One_Of(void) const +{ + if (ScenarioInit || TeamClass::Number[TeamTypes.ID(this)] < MaxAllowed) { + return(new TeamClass(this, HouseClass::As_Pointer(House))); + } + return(NULL); +} + + +TARGET TeamTypeClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEAMTYPE, TeamTypes.ID(this))); +} + + +void TeamTypeClass::Destroy_All_Of(void) const +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + if (team->Class == this) { + delete team; + index--; + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * * + * This routine will scan through the team types available and create teams of the * + * type that can best utilize the existing unit mix. * + * * + * INPUT: house -- Pointer to the house that this team is to be created for. * + * * + * utype -- A bit mask of the unit types available for this house. * + * * + * itypes -- A bit mask of the infantry types available for this house. * + * * + * alerted -- Is this house alerted? If true, then the Autocreate teams will be * + * considered in the selection process. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1995 JLB : Created. * + * 07/21/1995 JLB : Will autocreate team even if no members in field. * + *=============================================================================================*/ +TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long utypes, long itypes, bool alerted) +{ + TeamTypeClass const * best = NULL; + int bestvalue = 0; + + for (int index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * ttype = TeamTypes.Ptr(index); + + if (ttype && + ttype->House == house->Class->House && + TeamClass::Number[index] < ((alerted || !ttype->IsAutocreate) ? ttype->MaxAllowed : 0)) { + + /* + ** Determine what kind of units this team requires. + */ + long uneeded = 0; + long ineeded = 0; + for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { + switch (ttype->Class[ctype]->What_Am_I()) { + case RTTI_INFANTRYTYPE: + ineeded |= (1 << ((InfantryTypeClass *)ttype->Class[ctype])->Type); + break; + + case RTTI_UNITTYPE: + uneeded |= (1 << ((UnitTypeClass *)ttype->Class[ctype])->Type); + break; + } + } + + /* + ** If this team can use the types required, then consider it a possible + ** team type to create. + */ + int value = 0; + if ((ineeded & itypes) || (uneeded & utypes)) { + value = ttype->RecruitPriority; + } else { + value = ttype->RecruitPriority/2; + } + + if (!best || bestvalue < value) { + bestvalue = value; + best = ttype; + } + } + } + + return(best); +} diff --git a/TIBERIANDAWN/TEAMTYPE.H b/TIBERIANDAWN/TEAMTYPE.H new file mode 100644 index 000000000..e9bc11464 --- /dev/null +++ b/TIBERIANDAWN/TEAMTYPE.H @@ -0,0 +1,261 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\teamtype.h_v 2.18 16 Oct 1995 16:45:40 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : December 7, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAMTYPE_H +#define TEAMTYPE_H + +/* +********************************** Defines ********************************** +*/ +//#define TEAMTYPE_MAX 20 // max # of different team types + +/* +** TeamMissionType: the various missions that a team can have. +*/ +typedef enum TeamMissionType : char { + TMISSION_NONE=-1, + TMISSION_ATTACKBASE, // Attack nearest enemy base. + TMISSION_ATTACKUNITS, // Attack all enemy units. + TMISSION_ATTACKCIVILIANS, // Attack all civilians + TMISSION_RAMPAGE, // attack & destroy anything that's not mine + TMISSION_DEFENDBASE, // Protect my base. +// TMISSION_HARVEST, // stake out a Tiberium claim, defend & harvest it + TMISSION_MOVE, // moves to waypoint specified. + TMISSION_MOVECELL, // moves to cell # specified. + TMISSION_RETREAT, // order given by superior team, for coordinating + TMISSION_GUARD, // works like an infantry's guard mission + TMISSION_LOOP, // loop back to start of mission list + TMISSION_ATTACKTARCOM, // attack tarcom + TMISSION_UNLOAD, // Unload at current location. + TMISSION_COUNT, + TMISSION_FIRST=0 +} TeamMissionType; + +/* +** Forward declarations. +*/ +class TechnoTypeClass; + + +/* +** This structure contains one team mission value & its argument. +*/ +typedef struct TeamMissionTag +{ + TeamMissionType Mission; + int Argument; +} TeamMissionStruct; + +/* +** TeamTypeClass declaration +*/ +class TeamTypeClass : public AbstractTypeClass +{ + public: + enum TeamTypeClassEnums { + MAX_TEAM_CLASSCOUNT=5, + MAX_TEAM_MISSIONS=20 + }; + + /* + ** Constructor/Destructor + */ + TeamTypeClass(void); + virtual ~TeamTypeClass(void) {}; + + /* + ** Initialization: clears all team types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + static void Read_INI(char *buffer); + void Fill_In(char *name, char *entry); + static void Write_INI(char *buffer, bool refresh); + static void Read_Old_INI(char *buffer); + static char * INI_Name(void) {return "TeamTypes";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give its name + */ + static TeamTypeClass *As_Pointer(char *name); + + /* + ** Processing routines + */ + void Remove(void); + TeamClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + + /* + ** Utility routines + */ + static char const * Name_From_Mission(TeamMissionType order); + static TeamMissionType Mission_From_Name(char const *name); + static TeamTypeClass const * Suggested_New_Team(HouseClass * house, long utypes, long itypes, bool alerted); + + TARGET As_Target(void) const; + + /* + ** Overloaded operators + */ + void * operator new(size_t ); + void operator delete(void *ptr); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this teamtype object is active, then this flag will be true. + ** TeamType objects that are not active are either not yet created or have + ** been deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** If RoundAbout, the team avoids high-threat areas + */ + unsigned IsRoundAbout:1; + + /* + ** If Learning, the team learns from mistakes + */ + unsigned IsLearning:1; + + /* + ** If Suicide, the team won't stop until it achieves its mission or it's + ** dead + */ + unsigned IsSuicide:1; + + /* + ** Is this team type allowed to be created automatically by the computer + ** when the appropriate trigger indicates? + */ + unsigned IsAutocreate:1; + + /* + ** Mercenaries will change sides if they start to lose. + */ + unsigned IsMercenary:1; + + /* + ** This flag tells the computer that it should build members to fill + ** a team of this type regardless of whether there actually is a team + ** of this type active. + */ + unsigned IsPrebuilt:1; + + /* + ** If this team should allow recruitment of new members, then this flag + ** will be true. A false value results in a team that fights until it + ** is dead. This is similar to IsSuicide, but they will defend themselves. + */ + unsigned IsReinforcable:1; + + /* + ** A transient team type was created exclusively to bring on reinforcements + ** as a result of some special event. As soon as there are no teams + ** existing of this type, then this team type should be deleted. + */ + unsigned IsTransient:1; + + /* + ** Priority given the team for recruiting purposes; higher priority means + ** it can steal members from other teams (scale: 0 - 15) + */ + int RecruitPriority; + + /* + ** Initial # of this type of team + */ + unsigned char InitNum; + + /* + ** Max # of this type of team allowed at one time + */ + unsigned char MaxAllowed; + + /* + ** Fear level of this team + */ + unsigned char Fear; + + /* + ** House the team belongs to + */ + HousesType House; + + /* + ** The mission list for this team + */ + int MissionCount; + TeamMissionStruct MissionList[MAX_TEAM_MISSIONS]; + + /* + ** Number of different classes in the team + */ + unsigned char ClassCount; + + /* + ** Array of object types comprising the team + */ + TechnoTypeClass const * Class[MAX_TEAM_CLASSCOUNT]; + + /* + ** Desired # of each type of object comprising the team + */ + unsigned char DesiredNum[MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + private: + static char const * TMissions[TMISSION_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TECHNO.CPP b/TIBERIANDAWN/TECHNO.CPP new file mode 100644 index 000000000..0d27c9659 --- /dev/null +++ b/TIBERIANDAWN/TECHNO.CPP @@ -0,0 +1,4763 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\techno.cpv 2.13 02 Aug 1995 17:01:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 8, 1994 * + * * + * Last Update : August 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TechnoClass::AI -- Handles AI processing for techno object. * + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * TechnoClass::Captured -- Handles capturing this object. * + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * TechnoClass::Is_Techno -- Confirms that this is a TechnoClass object. * + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * TechnoClass::Mark -- Handles marking of techno objects. * + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * TechnoClass::Owner -- Who is the owner of this object? * + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * TechnoClass::Random_Animate -- Performs some idle animation for the object. * + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * TechnoClass::Response_Move -- Handles the voice repsonse to a movement request. * + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * TechnoClass::Scatter -- Causes the object to scatter to an adjacent cell. * + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * TechnoClass::Set_Mission -- Forced mission set (used by editor). * + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * TechnoClass::Value -- Fetches the target value for this object. * + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * TechnoTypeClass::Max_Passengers -- Fetches the maximum passengers allowed. * + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Cloaking control values. +*/ +#define MAX_UNCLOAK_STAGE 38 +#define UNCLOAK_VIS_TIME (1*TICKS_PER_SECOND) + +//Added for getting the input for special character keys from the client +// - 6/26/2019 JAS +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + + +/*************************************************************************** +** Which shape to use depending on which facing is controlled by these arrays. +*/ +int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + + +/*********************************************************************************************** + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * * + * This is the normal constructor for techno type objects. It is called in the process of * + * constructing all the object type (constant) data for the various techno type objects. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass::TechnoTypeClass( + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_scanner, + bool is_nominal, + bool is_transporter, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_twoshooter, + bool is_turret_equipped, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + MPHType maxspeed, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor) : + ObjectTypeClass( true, + is_flammable, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + name, + ininame, + armor, + strength) +{ + Level = level; + Pre = pre; + MaxAmmo = ammo; + MaxSpeed = maxspeed; + CameoData = NULL; + Primary = primary, + Secondary = secondary, + Cost = cost; + IsLeader = is_leader; + IsScanner = is_scanner; + IsTransporter = is_transporter; + IsTwoShooter = is_twoshooter; + IsBuildable = is_buildable; + IsCrew = is_crew; + IsTheater = is_theater; + IsRepairable = is_repairable; + IsTurretEquipped= is_turret_equipped; + IsNominal = is_nominal; + Ownable = ownable; + Reward = reward; + Scenario = scenario; + SightRange = sightrange; + + /* + ** Units risk value is based on the type of weapon he has and the + ** rate of fire it shoots at. + */ + risk = risk; + Risk = 0; + if (primary != WEAPON_NONE) { + Risk = (Weapons[primary].Attack * (Weapons[primary].Range >> 4)) / Weapons[primary].ROF; + } +} + + +/*********************************************************************************************** + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * * + * This routine is used to find the underlying cost for this object. The underlying cost * + * does not include any free items that normally come with the object when purchased * + * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * + * harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost of the base object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Raw_Cost(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * * + * This routine will return the ownable bits for this object type. The ownable bits are * + * a bitflag composite of the houses that can own (build) this object type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable bits for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +unsigned short TechnoTypeClass::Get_Ownable(void) const +{ + return(Ownable); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * * + * This routine will return the time it takes to construct this object. Usually the time * + * to produce is directly related to cost. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * + * form of game ticks. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Time_To_Build(HousesType house) const +{ + int cost = Raw_Cost(); + + /* + ** For computer controlled buildings, slow down production on + ** cheaper buildings. + */ + if (PlayerPtr->Difficulty != DIFF_HARD && + GameToPlay == GAME_NORMAL && + What_Am_I() == RTTI_BUILDINGTYPE && + PlayerPtr->Class->House != house) { + + cost = (cost + (PlayerPtr->Difficulty == DIFF_EASY ? 4000 : 2000)) / 2; + } + + /* + ** Fudge factor, so that Nod builds a bit faster if the object must be delivered to + ** an airfield. + */ + if (What_Am_I() == RTTI_UNITTYPE && !(Ownable & HOUSEF_GOOD)) { + return (cost - (cost/4)); + } + + return(cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * * + * This routine will return the cost to produce an object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce one object of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Cost_Of(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * * + * This routine will fetch the cameo (sidebar small image) shape of this object type. * + * If there is no cameo data available (typical for non-producable units), then NULL will * + * be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoTypeClass::Get_Cameo_Data(void) const +{ + return(CameoData); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * * + * This routine will return the cost to repair one step. At the TechnoTypeClass level, * + * this merely serves as a placeholder function. The derived classes will provide a * + * functional version of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to repair one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Cost(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * * + * This routine merely serves as placeholder virtual function. The various type classes * + * will override this routine to return the number of health points to repair in one * + * "step". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to repair in one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Step(void) const +{ + return(0); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * * + * This routine is used to dump the status of the object class to the monochrome screen. * + * This display can be used to track down or prevent bugs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(0,0);mono->Printf("(%04X)p=%d,d=%d", House->Power_Fraction(), House->Power, House->Drain); +// mono->Set_Cursor(0,0);mono->Printf("(%d)", House->Blockage); + mono->Text_Print("X", 16 + (IsALoaner?2:0), 11); + mono->Text_Print("X", 16 + (IsLocked?2:0), 9); + + mono->Text_Print("X", 16 + (IsInRecoilState?2:0), 17); + mono->Text_Print("X", 16 + (IsTethered?2:0), 8); + mono->Text_Print("X", 16 + (IsOwnedByPlayer?2:0), 5); + mono->Text_Print("X", 16 + (IsDiscoveredByPlayer?2:0), 6); +// mono->Text_Print("X", 16 + (IsALemon?2:0), 9); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom); + mono->Set_Cursor(29, 3);mono->Printf("%02X", PrimaryFacing.Current()); + + FlasherClass::Debug_Dump(mono); + StageClass::Debug_Dump(mono); + RadioClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * * + * This default constructor for techno objects is used to reset all internal variables to * + * their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(void) : + TarCom(TARGET_NONE), + House(0) +{ + ArchiveTarget = TARGET_NONE; + Arm = 0; + Ammo = -1; + LineCount = 0; + LineFrame = 0; + LineMaxFrames = 0; + PurchasePrice = 0; + IsTickedOff = false; + Cloak = UNCLOAKED; + CloakingDevice.Set_Stage(1); + CloakingDevice.Set_Rate(0); + IsCloakable = false; + IsALemon = false; + IsALoaner = false; + IsDiscoveredByPlayer = false; + IsDiscoveredByComputer = false; + IsInRecoilState = false; + IsLeader = false; + IsLocked = false; + IsOwnedByPlayer = false; + IsSecondShot = true; + IsTethered = false; + SuspendedTarCom = TARGET_NONE; + PrimaryFacing.Set(DIR_N); + + // Added for multiplayer changes. ST - 3/6/2019 11:34AM + IsDiscoveredByPlayerMask = 0; +} + + + +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + * 03/06/2019 ST : Per-player discovery * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + //if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + //if (house != PlayerPtr && IsDiscoveredByComputer) return(false); + if (house == NULL) { + return false; + } + + if (Is_Discovered_By_Player(house)) { + return false; + } + + if (house->IsHuman == false) { + if (IsDiscoveredByComputer) { + return false; + } + } + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (GameToPlay == GAME_NORMAL) { + if (house == PlayerPtr) { + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (Trigger) { + Trigger->Spring(EVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + Look(); + + } + } + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + Look(); + } + + return(true); + } + return(false); +} + +#if (0) // ST - 3/6/2019 11:35AM +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + if (house != PlayerPtr && IsDiscoveredByComputer) return(false); + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (house == PlayerPtr) { + + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (Trigger) { + Trigger->Spring(EVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + } else { + + /* + ** A newly revealed object will always perform a look operation. + */ + if (house == PlayerPtr) IsDiscoveredByPlayer = true; + if (house != PlayerPtr) IsDiscoveredByComputer = true; + Look(); + } + } + + if (house == PlayerPtr) IsDiscoveredByPlayer = true; + if (house != PlayerPtr) IsDiscoveredByComputer = true; + + return(true); + } + return(false); +} +#endif + +/*********************************************************************************************** + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * * + * This routine is called for every object that returns to the darkness shroud or when * + * it gets destroyed. This also occurs when an object enters another (such as infantry * + * entering a transport helicopter). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Hidden(void) +{ + // ST - 3/13/2019 4:56PM + if (!Is_Discovered_By_Player()) { + return; + } + //if (!IsDiscoveredByPlayer) return; + if (!House->IsHuman) { + Clear_Discovered_By_Players(); + } +} + + +/*********************************************************************************************** + * TechnoClass::Mark -- Handles marking of techno objects. * + * * + * On the Techno-level, marking handles transmission of the redraw command to any object * + * that it is 'connected' with. This only occurs during loading and unloading. * + * * + * INPUT: mark -- The marking method. This routine just passes this on to base classes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Mark(MarkType mark) +{ + if (RadioClass::Mark(mark)) { + /* + ** When redrawing an object, if there is another object teathered to this one, + ** redraw it as well. + */ + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * * + * This routine is used to handle inbound radio messages. It only handles those messages * + * that deal with techno objects. Typically, these include loading and unloading. * + * * + * INPUT: from -- Pointer to the originator of the radio message. * + * * + * message -- The inbound radio message. * + * * + * param -- Reference to optional parameter that might be used to transfer * + * more information than is possible with the simple radio message * + * type. * + * * + * OUTPUT: Returns with the radio response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 06/17/1995 JLB : Handles tether contact messages. * + *=============================================================================================*/ +RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + switch (message) { + + /* + ** Just received instructions to attack the specified target. + */ + case RADIO_ATTACK_THIS: + if (Techno_Type_Class()->Primary != WEAPON_NONE) { + Assign_Target(param); + Assign_Mission(MISSION_ATTACK); + return(RADIO_ROGER); + } + break; + + /* + ** Establish a tethered connection to the object in radio contact. + */ + case RADIO_TETHER: + if (!IsTethered) { + IsTethered = true; + Transmit_Message(RADIO_TETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** Break the tethered connection to the object in radio contact. + */ + case RADIO_UNTETHER: + if (IsTethered) { + IsTethered = false; + Transmit_Message(RADIO_UNTETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** A teathered unit has reached it's destination. All is + ** clear, so radio contact can be broken. + */ + case RADIO_UNLOADED: + Transmit_Message(RADIO_UNTETHER, from); + return(Transmit_Message(RADIO_OVER_OUT, from)); + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + Transmit_Message(RADIO_UNTETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Request to be informed when unloaded. This message is transmitted + ** by the transport unit to the transported unit as it is about to be + ** unloaded. It is saying, "All is clear. Drive off now." + */ + case RADIO_UNLOAD: + case RADIO_BACKUP_NOW: + case RADIO_HOLD_STILL: + Transmit_Message(RADIO_TETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Handle reloading one ammo point for this unit. + */ + case RADIO_RELOAD: + if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); + Ammo++; + return(RADIO_ROGER); + + /* + ** Handle repair of this unit. + */ + case RADIO_REPAIR: + if (/*param > 0 &&*/ Health_Ratio() < 0x0100) { + int cost = Techno_Type_Class()->Repair_Cost(); +// int cost = Fixed_To_Cardinal(Techno_Type_Class()->Repair_Cost(), param); + cost = MAX(cost, 1); + int step = Techno_Type_Class()->Repair_Step(); +// int step = Fixed_To_Cardinal(Techno_Type_Class()->Repair_Step(), param); + step = MAX(step, 1); + if (House->Available_Money() >= cost) { +#ifdef OBSOLETE + if (Health_Ratio() >= 0x0100) { + Strength = Class_Of().MaxStrength; + from->Scatter(true); + return(RADIO_NEGATIVE); + } +#endif + House->Spend_Money(cost); + Strength += step; + return(RADIO_ROGER); + } + } + break; + + default: + break; + } + return(RadioClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * * + * This constructor sets the owner of this object and its strength. Any object not owned * + * by the player is marked as a loaner. This is a special flag that indicates off-map * + * movement is allowed. The flag is cleared when the object finally enters the map. * + * * + * INPUT: house -- The house (owner) of this object. * + * * + * strength -- The strength to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(HousesType house) : + House(HouseClass::As_Pointer(house)), + TarCom(TARGET_NONE) +{ + ArchiveTarget = TARGET_NONE; + Arm = 0; + Ammo = -1; + LineCount = 0; + LineFrame = 0; + LineMaxFrames = 0; + PurchasePrice = 0; + IsTickedOff = false; + Cloak = UNCLOAKED; + CloakingDevice.Set_Stage(1); + CloakingDevice.Set_Rate(0); + IsCloakable = false; + IsALemon = false; + IsALoaner = false; + IsDiscoveredByComputer = false; + //IsOwnedByPlayer = (house == PlayerPtr->Class->House); // ST - 4/24/2019 10:41AM + IsDiscoveredByPlayer = false; + IsInRecoilState = false; + IsLeader = false; + IsLocked = false; + IsSecondShot = false; + IsTethered = false; + + SuspendedTarCom = TARGET_NONE; + + PrimaryFacing.Set(DIR_N); + + // Added for multiplayer changes. ST - 4/24/2019 10:40AM + IsDiscoveredByPlayerMask = 0; + if (GameToPlay == GAME_NORMAL) { + IsOwnedByPlayer = (house == PlayerPtr->Class->House); + } else { + IsOwnedByPlayer = House->IsHuman; + } + + /* + ** There is a chance that a vehicle will be a "lemon". + */ + if (Random_Pick(0, 255) < (int)House->Class->Lemon) { + IsALemon = true; + } +} + + +/*********************************************************************************************** + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * + * * + * This routine handles marking a game object as not a loaner. It is set only if the unit * + * is not player owned and is on the regular map. This is necessary so that enemy objects * + * can exist off-map but as soon as they move onto the map, are flagged so that can never * + * leave it again. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Handles scanner units. * + * 12/27/1994 JLB : Checks for an processes any trigger in cell. * + *=============================================================================================*/ +void TechnoClass::Per_Cell_Process(bool ) +{ + CELL cell = Coord_Cell(Center_Coord()); + + /* + ** When enemy units enter the proper map area from off map, they are + ** flagged so that they won't travel back off the map again. + */ + if (Map.In_Radar(cell)) { + + if (What_Am_I() == RTTI_UNIT) { + UnitClass * u = (UnitClass *)this; + + if (*u != UNIT_HOVER && *u != UNIT_GUNBOAT) { + IsLocked = true; + } + } else { + IsLocked = true; + } + } + + /* + ** If this object somehow moves into mapped terrain, but is not yet + ** discovered, then flag it to be discovered. + */ + // ST - 3/13/2019 4:56PM + if (!Is_Discovered_By_Player() && Map[cell].Is_Visible(House)) { + //if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { + //Revealed(PlayerPtr); + Revealed(House); // ST - 8/7/2019 11:20AM + } +} + + +/*********************************************************************************************** + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * * + * This routine will draw the common elements for techno type objects. This element is * + * the health bar. The main game object has already been rendered by the time this * + * routine is called. * + * * + * INPUT: x,y -- The coordinate of the center of the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Knows about radar scanned cells. * + * 12/13/1994 JLB : Clips health bar against map edge. * + * 01/23/1995 JLB : Dynamic selected object rectangle. * + *=============================================================================================*/ +void TechnoClass::Draw_It(int x, int y, WindowNumberType window) +{ + Clear_Redraw_Flag(); + + const bool show_health_bar = (Strength > 0) && (Is_Selected_By_Player() || + ((Special.HealthBarDisplayMode == SpecialClass::HB_DAMAGED) && (Strength < Techno_Type_Class()->MaxStrength)) || + (Special.HealthBarDisplayMode == SpecialClass::HB_ALWAYS)); + + /* + ** Fetch the dimensions of the object. These dimensions will be used to draw + ** the selection box and the health bar. + */ + int width, height; + Class_Of().Dimensions(width, height); + int lx = width/2; + int ly = height/2; + int dx = width/5; + int dy = height/5; + int fudge = show_health_bar ? 4 : 0; + + /* + ** Draw lines + */ + if (LineFrame < LineMaxFrames) { + // Only draw the last line for virtual window + int start_line = (window == WINDOW_VIRTUAL) ? max(0, LineCount - 1) : 0; + for (int i = start_line; i < LineCount; i++) { + CC_Draw_Line(Lines[i][0], Lines[i][1], Lines[i][2], Lines[i][3], (unsigned char)Lines[i][4], LineFrame, window); + } + if ((window == WINDOW_VIRTUAL) && (++LineFrame >= LineMaxFrames)) { + LineCount = 0; + LineFrame = 0; + LineMaxFrames = 0; + } + } + + if (Is_Selected_By_Player() || show_health_bar) { + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + (WindowList[window][WINDOWX] << 3) + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH] << 3, + WindowList[window][WINDOWHEIGHT]); + + + /* + ** The infantry select box should be a bit higher than normal. + */ + if (What_Am_I() == RTTI_INFANTRY) { + y -= 6; + } + + if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { + y -= 5; + } + + if (show_health_bar) { + unsigned ratio = Health_Ratio(); + int pwidth; // Pixel width of bar interior. + int color; // The color to give the interior of the bargraph. + + int xx = x-width/2; + int yy = y-(height/2); + + /* + ** Draw the outline of the bargraph. + */ + draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); + draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); + + /* + ** Determine the width of the interior strength + ** graph. + */ + pwidth = Fixed_To_Cardinal(width-2, ratio); + + pwidth = Bound(pwidth, 1, width-2); + + color = LTGREEN; + if (ratio < 0x7F) { + color = YELLOW; + } + if (ratio < 0x3F) { + color = RED; + } + draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); + } + + /* + ** Draw the selected object graphic. + */ + if (Is_Selected_By_Player()) { + // Upper left corner. + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx+dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx, fudge+y-ly+dy, WHITE); + + // Upper right corner. + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx-dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx, fudge+y-ly+dy, WHITE); + + // Lower right corner. + draw_window.Draw_Line(x+lx, y+ly, x+lx-dx, y+ly, WHITE); + draw_window.Draw_Line(x+lx, y+ly, x+lx, y+ly-dy, WHITE); + + // Lower left corner. + draw_window.Draw_Line(x-lx, y+ly, x-lx+dx, y+ly, WHITE); + draw_window.Draw_Line(x-lx, y+ly, x-lx, y+ly-dy, WHITE); + } + } + + // MBL 04.21.2020 + bool selected = Is_Selected_By_Player() || Special.ResourceBarDisplayMode == SpecialClass::RB_ALWAYS; + // if ((window == WINDOW_VIRTUAL) || (Is_Selected_By_Player() && House->Is_Ally(PlayerPtr))) + if ((window == WINDOW_VIRTUAL) || (selected && House->Is_Ally(PlayerPtr))) + { + Draw_Pips((x-lx)+5, y+ly-3, window); + } +} + + +/*********************************************************************************************** + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * * + * This routine handles the common operation between techno objects when they are * + * unlimboed. This includes revealing the map. * + * * + * INPUT: coord -- The coordinate to unlimbo object at. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) +{ + if (RadioClass::Unlimbo(coord, dir)) { + PrimaryFacing = dir; + Enter_Idle_Mode(true); + Commence(); + + IsLocked = Map.In_Radar(Coord_Cell(coord)); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine is used to compare the distance to the specified target with the range * + * of the weapon. If the target is outside of weapon range, then false is returned. * + * * + * INPUT: target -- The target to check if it is within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the specified target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(TARGET target, int which, bool reciprocal_check) const +{ + if (IsLocked && Target_Legal(target)) { + int range = Weapon_Range(which); + BuildingClass const * building = As_Building(target); + if (building) { + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check && !building) { + ObjectClass *target_object = As_Object(target); + if (target_object) { + RTTIType my_type = What_Am_I(); + if (target_object->What_Am_I() == my_type) { + if (range == target_object->Weapon_Range(which)) { + TechnoClass *tech = As_Techno(target); + if (tech->In_Range(As_Target(), which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine will determine if the pointer to the target object passed into this * + * routine is within weapon range. * + * * + * INPUT: target -- Pointer to the target object to check if within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(ObjectClass const * target, int which, bool reciprocal_check) const +{ + if (IsLocked && target) { + int range = Weapon_Range(which); + if (target->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * building = (BuildingClass const *)target; + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check) { + RTTIType my_type = What_Am_I(); + if (my_type != RTTI_BUILDING) { + if (target->What_Am_I() == my_type) { + if (range == target->Weapon_Range(which)) { + if (((TechnoClass*)target)->In_Range(this, which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * * + * Use this routine to determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to examine against the object to determine range. * + * * + * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the weapon within range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/16/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(COORDINATE coord, int which) const +{ + return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * * + * This routine is used to determing the score value (value as a potential target) of the * + * object specified. This routine will check the specified object for all the various * + * legality checks that threat scanning requires. This is the main workhorse routine for * + * target searching. * + * * + * INPUT: method -- The threat method requested. This is a combined bitflag value that * + * not only specified the kind of targets to consider, but how far away * + * they are allowed to be. * + * * + * mask -- This is an RTTI mask to use for quickly eliminating object types. * + * The mask is created outside of this routine because this routine is * + * usually called from within a loop and this value is constant in that * + * context. * + * * + * range -- The range at which potential target objects are rejected. * + * 0 = must be within weapon range. * + * >0 = must be within this lepton distance. * + * <0 = range doesn't matter. * + * * + * object -- Pointer to the object itself. * + * * + * value -- Reference to the value variable that this routine will fill in. The * + * higher the value the more likely this object will be selected as best. * + * * + * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * + * value parameter will be filled in correctly. * + * * + * WARNINGS: This routine is time consuming. Don't call unless necessary. * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const +{ + /* + ** An object in limbo can never be a valid target. + */ + if (object->IsInLimbo) return(false); + + /* + ** Friendly units are never considered a good target. Bail if this + ** object is a friend. + */ + if (House->Is_Ally(object)) return(false); + + /* + ** If the object is farther away than allowed, bail. + */ + int dist = Distance(object); + if (range > 0 && dist > range) return(false); + if (range == 0 && !In_Range(object, 0) && !In_Range(object, 1)) return(false); + + /* + ** If the object is not visible, then bail. Human controled units + ** are always considered to be visible. + */ + if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && GameToPlay == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { + return(false); + } + + /* + ** Quickly eliminate all unit types that are not allowed according to the mask + ** value. + */ + RTTIType otype = object->What_Am_I(); + if (!((1 << otype) & mask)) return(false); // Mask failure. + + /* + ** If the object is cloaked, then it isn't a legal target. + */ + if (object->Cloak == CLOAKED) return(false); + + /* + ** Determine if the target is theoretically allowed to be a target. If + ** not, then bail. + */ + TechnoTypeClass const * tclass = object->Techno_Type_Class(); + if (!tclass->IsLegalTarget) return(false); // Legality failure. + + /* + ** Never consider agent Delphi a valid target. + */ + if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_DELPHI) { + return(false); + } + + /* + ** Special case so that SAM site doesn't fire on aircraft that are landed. + */ + if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { + if (((AircraftClass *)object)->Altitude == 0) return(false); + } + + /* + ** If only allowed to attack civilians, then eliminate all other types. + */ + if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { + return(false); + } + + /* + ** If the scan is limited to capturable buildings only, then bail if the examined + ** object isn't a capturable building. + */ + if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) { + return(false); + } + + /* + ** If not allowed to attack boats, then eliminate them from consideration. + */ + if (!(method & THREAT_BOATS) && + otype == RTTI_UNIT && + (((UnitTypeClass const *)tclass)->Speed == SPEED_HOVER || ((UnitTypeClass const *)tclass)->Speed == SPEED_FLOAT)) { + return(false); + } + + /* + ** SPECIAL CASE: Friendly units won't automatically fire on buildings + ** if the building is not aggressive. + */ + if (House->IsHuman && otype == RTTI_BUILDING && tclass->Primary == WEAPON_NONE) return(false); + + /* + ** If the search is restricted to Tiberium processing objects, then + ** perform the special qualification check now. + */ + if (method & THREAT_TIBERIUM) { + switch (otype) { + case RTTI_UNIT: + if (!((UnitTypeClass const *)tclass)->IsToHarvest) return(false); + break; + + case RTTI_BUILDING: + if (!((BuildingTypeClass const *)tclass)->Capacity) return(false); + break; + + default: + return(false); + } + } + + /* + ** If this target value is better than the previously recorded best + ** target value then record this target for possible return as the + ** best target available. + */ + int rawval = object->Value(); + value = rawval + object->Kills; + +#ifdef USE_RA_AI + /* + ** If the candidate object is owned by the designated enemy of this house, then + ** give it a higher value. This will tend to gravitate attacks toward the main + ** antagonist of this house. + */ + if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) { + value += 500; + value *= 3; + } +#endif + +#ifdef ADVANCED + /* + ** Lessen threat as a factor of distance. + */ + if (rawval) { + + value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); + + //value = Fixed_To_Cardinal(value, Cardinal_To_Fixed(MAP_CELL_W*2, (MAP_CELL_W*2) - (dist/ICON_LEPTON_W))); + //value = MAX(value, 2); + + if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; + value = MAX(value, 1); + return(true); + } + value = 0; + return(false); + +#else + + /* + ** Lessen threat as a factor of distance. + */ + if (range == -1 && value) { + /* + ** Code from RA so that the value isn't always 2 on a mapwide scan with the range set to -1. ST - 3/2/2020 5:02PM + */ + value = (value * 32000) / ((dist/ICON_LEPTON_W)+1); + } else { + /* + ** Original TD code + */ + int modifier = dist; + int crange = range / ICON_LEPTON_W; + if (crange) modifier /= crange; + if (modifier) value /= modifier; + } + if (rawval) { + value = MAX(value, 2); + } + return(true); +#endif +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * * + * This routine will examine the specified cell and return with the potential target * + * object it contains and the value of it. Use this routine when searching for threats. * + * * + * INPUT: method -- The scan method to use for target searching. * + * * + * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * + * * + * range -- Scan range limit to use for elimination purposes. This ensures that * + * objects in the "corner" of a square scan get properly discarded. * + * * + * object -- Pointer to object pointer to be filled in with the object at this * + * cell as a valid target. * + * * + * value -- Reference to the value of the object in this cell. It will be set * + * according to the object's value. * + * * + * OUTPUT: Was a valid potential target found in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value) const +{ + *object = NULL; + value = 0; + + /* + ** If the cell is not on the legal map, then always ignore it. + */ + if (cell & 0xF000) return(false); + if (!Map.In_Radar(cell)) return(false); + + /* + ** Fetch the techno object from the cell. If there is no + ** techno object there, then bail. + */ + CellClass * cellptr = &Map[cell]; + TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); + while (tentative) { + if (tentative->Is_Techno() && !House->Is_Ally(tentative)) break; + tentative = (TechnoClass const *)tentative->Next; + } + + if (!tentative) return(false); +// if (!tentative->Is_Techno()) return(false); + *object = tentative; + + return(Evaluate_Object(method, mask, range, tentative, value)); +} + + +/*********************************************************************************************** + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * * + * This routine will scan game objects looking for the best target. It is used by the * + * general target searching processes. The type of target scan to perform is controlled * + * by the method control parameter. * + * * + * INPUT: method -- The method control parameter is used to control the type of target * + * scan performed. It consists of a series of bit flags (see ThreatType) * + * that are combined to form the target scan desired. * + * * + * OUTPUT: Returns the target value of a suitable target. If no target was found then the * + * value TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + * 06/20/1995 JLB : Greatly optimized scan method. * + *=============================================================================================*/ +TARGET TechnoClass::Greatest_Threat(ThreatType method) const +{ + ObjectClass const * bestobject = NULL; + int bestval = -1; + + /* + ** Build a quick elimination mask. If the RTTI of the object doesn't + ** qualify with this mask, then we KNOW that it shouldn't be considered. + */ + int mask = 0; + if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); + if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); + if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_BUILDINGS) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_INFANTRY) mask |= (1 << RTTI_INFANTRY); + if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); + + /* + ** Let's pick from the available contenders on a map-wide scan. ST - 3/2/2020 3:49PM + */ + static const int _max_best_objects = 128; + ObjectClass const *best_objects [_max_best_objects]; + int best_object_count = 0; + + /* + ** Limit area target scans use a method where the actual map cells are + ** examined for occupants. The occupant is then examined in turn. The + ** best target within the area is returned as a target. + */ + if (method & (THREAT_AREA|THREAT_RANGE)) { + int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); + +// int range = MAX(Weapon_Range(0), Weapon_Range(1)); +// if (!(method & THREAT_RANGE)) range *= 2; +// range = Bound(range, 0x0100, 0x1400); // Limit maximum scan distance. + int crange = range / ICON_LEPTON_W; + if (range == 0) { + crange = MAX(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; + crange++; + } + CELL cell = Coord_Cell(Fire_Coord(0)); +// CELL cell = Coord_Cell(Center_Coord()); + + /* + ** If aircraft are a legal target, then scan through all of them at this time. + ** Scanning by cell is not possible for aircraft since they are not recorded + ** at the cell level. + */ + if (method & THREAT_AIR) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, range, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + TechnoClass const * object; + int value; + for (int radius = 1; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + } + + /* + ** Bail early if a target has already been found and the range is at + ** one of the breaking points (i.e., normal range or range * 2). + */ + if (bestobject) { + if (radius == crange/4) { + return(bestobject->As_Target()); + } + if (radius == crange/2) { + return(bestobject->As_Target()); + } + } + } + + } else { + + /* + ** A full map scan was requested. First scan through aircraft. The top map layer + ** is NOT scanned since that layer will probably contain more bullets and animations + ** than aircraft. + */ + int index; + for (index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, -1, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + best_object_count = 0; + best_objects[best_object_count++] = object; + } else { + if (value == bestval) { + if (best_object_count < _max_best_objects) { + best_objects[best_object_count++] = object; + } + } + } + } + } + + /* + ** Now scan through the entire ground layer. This is painful, but what other + ** choice is there? + */ + for (index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; + + int value = 0; + if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + best_object_count = 0; + best_objects[best_object_count++] = object; + } else { + if (value == bestval) { + if (best_object_count < _max_best_objects) { + best_objects[best_object_count++] = object; + } + } + } + } + } + } + + /* + ** If a good target object was found, then return with the target value + ** of it. + */ + if (bestobject) { + + /* + ** If there's only one, return that. Otherwise pick from the equal contenders + */ + if (best_object_count > 1) { + + /* + ** Pick one at random from our contenders + */ + int index = Random_Pick(0, best_object_count - 1); + bestobject = As_Object(best_objects[index]->As_Target()); + } + + return(bestobject->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Owner -- Who is the owner of this object? * + * * + * Use this routine to examine this object and return who the owner is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the house number of the owner of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +HousesType TechnoClass::Owner(void) const +{ + return(House->Class->House); +} + + +/*********************************************************************************************** + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * * + * Use this routine to set the flash count for the object. This flash count is the number * + * of times the object will "flash". Typically it is called as a result of the player * + * clicking on this object in order to make it the target of a move or attack. * + * * + * INPUT: count -- The number of times the object should flash. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Clicked_As_Target(HousesType house, int count) // 2019/09/20 JAS - Added record of who clicked on the object +{ + FlashCount = count; + + // 2019/09/20 JAS - Flashing info needs to exist per player + if (house < HOUSE_COUNT) + { + FlashCountPerPlayer[house] = count; + } + else + { + //receiving HOUSE_COUNT means do it for every player + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = count; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::AI -- Handles AI processing for techno object. * + * * + * This routine handles AI processing for techno objects. Typically, this merely dispatches * + * to the appropriate AI routines for the base classes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure that this routine is only called ONCE per game tick. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::AI(void) +{ + CargoClass::AI(); + RadioClass::AI(); + DoorClass::AI(); + + /* + ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. + */ + if (IsCloakable) { + + /* + ** If this object is uncloaked, but it can be cloaked and it thinks that it + ** is a good time do so, then begin cloaking. + */ + if (Cloak == UNCLOAKED) { + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + if (Is_Owned_By_Player()) Mark(MARK_CHANGE); + //if (IsOwnedByPlayer) Mark(MARK_CHANGE); + CloakingDevice.Graphic_Logic(); + if (!Arm && CloakingDevice.Fetch_Stage()) { + if (Health_Ratio() > 0x0040) { + Do_Cloak(); + } else { + if (Random_Pick(0, 25) == 1) { + Do_Cloak(); + } + } + } + } else { + + VisualType pre = Visual_Character(true); + CloakingDevice.Graphic_Logic(); + switch (Cloak) { + + /* + ** Handle the uncloaking process. Always mark to redraw + ** the object and when cloaking is complete, stabilize into + ** the normal uncloaked state. + */ + case UNCLOAKING: + Mark(MARK_CHANGE); + if (Visual_Character(true) == VISUAL_NORMAL) { + CloakingDevice.Set_Rate(UNCLOAK_VIS_TIME); + CloakingDevice.Set_Stage(0); // re-start the stage counter + Cloak = UNCLOAKED; + } + break; + + /* + ** Handle the cloaking process. Always mark to redraw the object + ** and when the cloaking process is complete, stabilize into the + ** normal cloaked state. + */ + case CLOAKING: + Mark(MARK_CHANGE); + switch (Visual_Character(true)) { + + /* + ** If badly damaged, then it can never fully cloak. + */ + case VISUAL_DARKEN: + if (Health_Ratio() < 0x0040 && Random_Pick(1, 3) == 1) { + Cloak = UNCLOAKING; + } + break; + +#ifdef NEVER + case VISUAL_SHADOWY: + if (pre != Visual_Character(true)) { + Detach_All(false); + } + break; +#endif + + case VISUAL_HIDDEN: + Cloak = CLOAKED; + CloakingDevice.Set_Rate(0); + + /* + ** Special check to ensure that if the unit is carring a captured + ** flag, it will never fully cloak. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + Do_Shimmer(); + } else { + Detach_All(false); + } + + /* + ** A computer controlled unit will try to scatter if possible so + ** that it will be much harder to locate. + */ + if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { + Scatter(0, true); + } + break; + } + break; + + /* + ** A cloaked object will always be redrawn if it is owned by the + ** player. This ensures that the shimmering effect will animate. + */ + case CLOAKED: + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + if (Is_Owned_By_Player()) { + //if (IsOwnedByPlayer) { + Mark(MARK_CHANGE); + } + break; + } + } + } + + /* + ** Arming delay always counts down to zero. + */ + if (Arm) Arm--; + + /* + ** Handle line delay logic. + */ + if ((LineMaxFrames > 0) && (LineCount > 0)) { + Map.Flag_To_Redraw(true); + } + + /* + ** Update the animation timer system. If the animation stage + ** changes, then flag the object to be redrawn as well as determine + ** if the current animation process needs to change. + */ + if (What_Am_I() != RTTI_BUILDING) { + if (StageClass::Graphic_Logic() || Time_To_Redraw()) { + Mark(MARK_CHANGE); + } + } + + /* + ** If the object is flashing and a change of flash state has occured, then mark the + ** object to be redrawn. + */ + if (FlasherClass::Process()) { + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * * + * This function checks to see if this techno object can be selected. If it can, then it * + * is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Select(bool allow_mixed) +{ + // ST - 3/13/2019 4:56PM + if (!Is_Discovered_By_Player() && !Is_Owned_By_Player() && !Debug_Unshroud) { + //if (!IsDiscoveredByPlayer && !IsOwnedByPlayer && !Debug_Unshroud) { + return(false); + } + + if (RadioClass::Select(allow_mixed)) { + + /* + ** Speak a confirmation of selection. + */ + // ST - 3/13/2019 4:59PM + if (Is_Owned_By_Player() && AllowVoice) { + //if (IsOwnedByPlayer && AllowVoice) { + Response_Select(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * * + * This performs a simple check to make sure that this techno object can fire. At this * + * level, the only thing checked for is the rearming delay. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the fire legality control code. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const +{ + /* + ** Don't allow firing if the target is illegal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + ObjectClass * object = As_Object(target); + + /* + ** If the object is completely cloaked, then you can't fire on it. + */ +//Mono_Printf("Units[0]=%p.\n", Units.Raw_Ptr(0)); +//Mono_Printf("Infantry[0]=%p.\n", Infantry.Raw_Ptr(0)); +//Mono_Printf("Buildings[0]=%p.\n", Buildings.Raw_Ptr(0)); +//Mono_Printf("Aircraft[0]=%p.\n", Aircraft.Raw_Ptr(0)); +//Mono_Printf("object=%p, Strength=%d, IsActive=%d, IsInLimbo=%d.\n", object, (long)object->Strength, object->IsActive, object->IsInLimbo);Get_Key(); + if (object && /*(object->IsActive || GameToPlay != GAME_NORMAL) &&*/ object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) { + return(FIRE_CANT); + } + + /* + ** If there is no weapon, then firing is not allowed. + */ + WeaponType weap = (which == 0) ? Techno_Type_Class()->Primary : Techno_Type_Class()->Secondary; + if (weap == WEAPON_NONE) { + return(FIRE_CANT); + } + + /* + ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is + ** sitting on the ground. + */ + if (object && object->What_Am_I() == RTTI_AIRCRAFT && + !BulletTypeClass::As_Reference(Weapons[weap].Fires).IsAntiAircraft && + ((AircraftClass *)object)->Altitude > 0) { + + return(FIRE_CANT); + } + + /* + ** Don't allow firing if still rearming. + */ + if (Arm) return(FIRE_REARM); + + /* + ** The target must be within range in order to allow firing. + */ + if (!In_Range(target, which)) { + return(FIRE_RANGE); + } + + /* + ** If there is no ammo left, then it can't fire. + */ + if (!Ammo) { + return(FIRE_AMMO); + } + + /* + ** If cloaked, then firing is disabled. + */ + if (Cloak != UNCLOAKED) { + return(FIRE_CLOAKED); + } + + return(FIRE_OK); +} + + +/*********************************************************************************************** + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * * + * This routine handles cleaning up this techno object from the game system so that when * + * it is subsequently removed, it doesn't leave any loose ends. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Stun(void) +{ + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + Transmit_Message(RADIO_OVER_OUT); + Detach_All(); + //Unselect(); + //When an object is stunned it needs to be deselected from all players, not just the current PlayerPtr. + // - 8/18/2019 JAS + Unselect_All_Players(); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * * + * Use this routine to set the targeting computer for this object. It checks to make sure * + * that targeting of itself is prohibited. * + * * + * INPUT: target -- The target for this object to attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Target(TARGET target) +{ + if (target == TarCom) return; + + if (!Target_Legal(target)) { + target = TARGET_NONE; + } else { + + /* + ** Prevent targeting of self. + */ + if (target == As_Target()) { + target = ::As_Target(Coord_Cell(Coord)); + } else { + + /* + ** Make sure that the target is not already dead. + */ + ObjectClass * object = As_Object(target); + if (object && (object->IsActive == false || object->Strength == 0)) { + target = TARGET_NONE; + } + } + } + + /* + ** Set the unit's targeting computer. + */ + TarCom = target; +} + + +/*********************************************************************************************** + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * * + * This function calculates the delay between shots. It determines this from the standard * + * rate of fire (ROF) of the base class and modifies it according to game speed and * + * whether this is the first or second shot. All single shot attackers consider their * + * shots to be "second" since the second shot is the one handled normally. The first shot * + * usually gets assigned a much shorter delay time before the next shot can fire. * + * * + * INPUT: second -- bool; Is this the second of a two shot salvo? * + * * + * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Rearm_Delay(bool second) const +{ + if (second) { + return(((int)Weapons[Techno_Type_Class()->Primary].ROF * House->ROFBias) + 3); + } + return(9); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * * + * This is the main projectile firing code. Buildings, units, and infantry route fire * + * requests through this function. * + * * + * INPUT: target -- The target that the projectile is to be fired at. * + * * + * which -- Which weapon to fire. * + * * + * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * + * could be created or there was some other illegality detected, the return value * + * will be NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * + *=============================================================================================*/ +BulletClass * TechnoClass::Fire_At(TARGET target, int which) +{ + BulletClass *bullet; // Projectile. + DirType dir; // The facing to impart upon the projectile. + COORDINATE target_coord; // Coordinate of the target. + COORDINATE fire_coord; // Coordinate of firing position. + TechnoTypeClass const & tclass = *Techno_Type_Class(); + ObjectClass *object; + WeaponTypeClass const *weapon = (which == 0) ? &Weapons[tclass.Primary] : &Weapons[tclass.Secondary]; + BulletTypeClass const &btype = BulletTypeClass::As_Reference(weapon->Fires); + + /* + ** Perform a quick legality check to see if firing can occur. + */ + if (Debug_Map || weapon->Fires == BULLET_NONE || !Target_Legal(target)) { + return(NULL); + } + + /* + ** Fetch the target coordinate for the target specified. + */ + object = As_Object(target); + if (object) { + target_coord = object->Target_Coord(); + } else { + target_coord = As_Coord(target); + } + + /* + ** Get the location where the projectile should appear. + */ + fire_coord = Fire_Coord(which); + + /* + ** If the projectile is a homing type (such as a missile), then it will + ** launch in the direction the turret is facing, NOT necessarily the same + ** direction as the target. + */ + if (btype.IsHoming || btype.IsDropping) { + dir = Fire_Direction(); + if (btype.IsDropping) { + fire_coord = Center_Coord(); + } + } else { + dir = ::Direction(fire_coord, target_coord); + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + int firepower = (weapon->Attack > 0) ? ((int)weapon->Attack * House->FirepowerBias) : 0; + bullet = new BulletClass(weapon->Fires); + if (bullet) { + bullet->Assign_Target(target); + bullet->Payback = this; + bullet->Strength = (short)firepower; + + /* + ** If this is firing from a moving platform, then the projectile is inaccurate. + */ + if (Special.IsDefenderAdvantage && What_Am_I() != RTTI_BUILDING && ((FootClass const *)this)->IsDriving) { + bullet->IsInaccurate = true; + } + + if (bullet->Unlimbo(fire_coord, dir)) { +//Mono_Printf("Units[0]=%p.\n", Units.Raw_Ptr(0)); +//Mono_Printf("Infantry[0]=%p.\n", Infantry.Raw_Ptr(0)); +//Mono_Printf("Buildings[0]=%p.\n", Buildings.Raw_Ptr(0)); +//Mono_Printf("Aircraft[0]=%p.\n", Aircraft.Raw_Ptr(0)); +//Mono_Printf("object=%p, Strength=%d, IsActive=%d, IsInLimbo=%d.\n", object, (long)object->Strength, object->IsActive, object->IsInLimbo);Get_Key(); + bullet->Payback = this; + bullet->Strength = (short)firepower; + } else { + delete bullet; + } + if (!bullet->Class->IsFueled) { + IsInRecoilState = true; + } + Arm = Rearm_Delay(IsSecondShot); + if (tclass.IsTwoShooter) { + IsSecondShot = (IsSecondShot == false); + } + + /* + ** Perform any animation effect for this weapon. + */ + AnimType a = weapon->Anim; + switch (a) { + case ANIM_GUN_N: + case ANIM_CHEM_N: + case ANIM_FLAME_N: + a = (AnimType)(a + Dir_Facing(Fire_Direction())); + break; + } + + /* + ** If there is a special firing animation, then create and attach it + ** now. + */ + if (a != ANIM_NONE) { + AnimClass * anim = new AnimClass(a, Fire_Coord(which)); + if (anim) { + anim->Attach_To(this); + } + } + + /* + ** Reduce ammunition for this object. + */ + if (Ammo > 0) { + Ammo--; + } + + /* + ** Firing will in all likelihood, require the unit to be redraw. Flag it to be + ** redrawn here. + */ + Mark(MARK_CHANGE); + + /* + ** If a projectile was fired from a unit that is hidden in the darkness, + ** reveal that unit and a little area around it. + ** For multiplayer games, only reveal the unit if the target is the + ** local player. + */ +#if (0) + if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || !Map[Coord_Cell(Center_Coord())].IsMapped) { + if (GameToPlay == GAME_NORMAL) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, false); + } else { + ObjectClass *obj = As_Object(target); + if (obj) { + HousesType tgt_owner = obj->Owner(); + + if (PlayerPtr->Class->House == tgt_owner) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, false); + } + } + } + } +#else + /* + ** Now need to reveal for any human player that is the target. ST - 3/13/2019 5:43PM + */ + + ObjectClass *obj = As_Object(target); + if (obj) { + HousesType tgt_owner = obj->Owner(); + + HouseClass *player = HouseClass::As_Pointer(tgt_owner); + if (player != nullptr && player->IsHuman) { + if ((!Is_Owned_By_Player(player) && !Is_Discovered_By_Player(player)) || !Map[Coord_Cell(Center_Coord())].Is_Mapped(House)) { + Map.Sight_From(player, Coord_Cell(Center_Coord()), 1, false); + } + } + } + +#endif + } + + if (weapon->Fires == BULLET_LASER) { + int x, y, x1, y1; + COORDINATE source, dest; + source = Fire_Coord(which); + dest = As_Coord(target); + if (SpecialDialog == SDLG_NONE) { + Map.Coord_To_Pixel(source, x, y); + Map.Coord_To_Pixel(dest, x1, y1); + x += Map.TacPixelX; + x1 += Map.TacPixelX; + y += Map.TacPixelY; + y1 += Map.TacPixelY; + Lines[0][0] = x + 1; + Lines[0][1] = y; + Lines[0][2] = x1; + Lines[0][3] = y1; + Lines[0][4] = 0x7D; + Lines[1][0] = x - 1; + Lines[1][1] = y; + Lines[1][2] = x1; + Lines[1][3] = y1; + Lines[1][4] = 0x7D; + Lines[2][0] = x; + Lines[2][1] = y; + Lines[2][2] = x1; + Lines[2][3] = y1; + Lines[2][4] = 0x7F; + LineCount = 3; + LineFrame = 0; + LineMaxFrames = 5; + Map.Flag_To_Redraw(true); + } + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), As_Coord(target)); + } + + return(bullet); +} + + +/*********************************************************************************************** + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * * + * This routine is called when the mission for an object needs to change as a result of * + * player input. The basic operation would be to queue the event and let the action * + * occur at the frame dictated by the queing system. However, if a voice response is * + * indicated, then perform it at this time. This will give a greater illusion of * + * immediate response. * + * * + * INPUT: order -- The mission order to assign to this object. * + * * + * target -- The target of this object. This will be used for combat and attack. * + * * + * destination -- The movement destination for this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(As_Target(), mission, target, destination); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * * + * This routine will examine the object specified and return with the action that will * + * be performed if the mouse button were clicked over the object. * + * * + * INPUT: object -- The object that the mouse button might be clicked on. * + * * + * OUTPUT: Returns with the action that will be performed if the object was clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 03/21/1995 JLB : Special target control for trees. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(ObjectClass * object) const +{ + if (object) { + + /* + ** Return the ACTION_SELF flag if clicking on itself. However, if this + ** object cannot do anything special with itself, then just return with + ** the no action flag. + */ + if (object == this && CurrentObject.Count() == 1 && House == PlayerPtr) { + return(ACTION_SELF); + } + + //bool altdown = (Keyboard::Down(KN_LALT) || Keyboard::Down(KN_RALT)); + //bool ctrldown = (Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL)); + //bool shiftdown = (Keyboard::Down(KN_LSHIFT) || Keyboard::Down(KN_RSHIFT)); + //Added for getting the input for special character keys from the client + // - 6/26/2019 JAS + bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); + bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + //if (IsOwnedByPlayer && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (altdown) { + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && Can_Player_Move()) { + //if (IsOwnedByPlayer && Can_Player_Move()) { + return(ACTION_MOVE); + } + } + + /* + ** Override so that toggled select state can be performed while the key + ** is held down. + */ + bool is_a_loaner = object->Is_Techno() && ((TechnoClass*)object)->IsALoaner; + if (shiftdown) { + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (!is_a_loaner) { + //if (IsOwnedByPlayer && !IsALoaner) { + return(ACTION_TOGGLE_SELECT); + } + } + + /* + ** If firing is possible and legal, then return this action potential. + */ + bool control = Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL); + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Special.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + //if (IsOwnedByPlayer && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Special.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + if (Can_Player_Move() || In_Range(object, 0)) { + // Check for anti-air capability + if ((object->What_Am_I() != RTTI_AIRCRAFT) || + ((Techno_Type_Class()->Primary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) || + ((Techno_Type_Class()->Secondary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Secondary].Fires).IsAntiAircraft) || + (((AircraftClass *)object)->Altitude == 0)) { + return(ACTION_ATTACK); + } + } + } + + /* + ** Possibly try to select the specified object, if that is warranted. + */ + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (!Is_Weapon_Equipped() || !Is_Owned_By_Player() || object->Owner() == Owner()) { + if ((!is_a_loaner || !Is_Owned_By_Player()) && object->Class_Of().IsSelectable && (!object->Is_Selected_By_Player() || CurrentObject.Count())) { + //if (!Is_Weapon_Equipped() || !IsOwnedByPlayer || object->Owner() == Owner()) { + //if ((!IsALoaner || !IsOwnedByPlayer) && object->Class_Of().IsSelectable && !object->IsSelected) { + return(ACTION_SELECT); + } + return(ACTION_NONE); + } + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * * + * Use this routine to determine what action will be performed if the specified cell * + * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * + * nomove is used to perform special case checking for nearby cells if in fact the mouse * + * is clicked over the cell. * + * * + * INPUT: cell -- The cell to check for being clicked over. * + * * + * OUTPUT: Returns with the action that will occur if the cell is clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 07/10/1995 JLB : Force fire for buildings is explicitely disabled. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(CELL cell) const +{ + CellClass const * cellptr = &Map[cell]; + OverlayTypeClass const * optr = NULL; + + //bool ctrldown = Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL); + //bool shiftdown = Keyboard::Down(KN_LSHIFT) || Keyboard::Down(KN_RSHIFT); + //bool altdown = (Keyboard::Down(KN_LALT) || Keyboard::Down(KN_RALT)); + //Added for getting the input for special character keys from the client + // - 6/26/2019 JAS + bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); + bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); + bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); + + /* + ** Disable recognizing the key forced fire option when dealing with buildings. + */ + if (What_Am_I() == RTTI_BUILDING) ctrldown = false; + + if (cellptr->Overlay != OVERLAY_NONE) { + optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + } + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + //if (IsOwnedByPlayer && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && Techno_Type_Class()->Primary != WEAPON_NONE && (ctrldown || (optr && optr->IsLegalTarget))) { + //if (IsOwnedByPlayer && Techno_Type_Class()->Primary != WEAPON_NONE && (ctrldown || (optr && optr->IsLegalTarget))) { + WarheadTypeClass const * whead = &Warheads[BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).Warhead]; + if (!optr || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { + if (Can_Player_Move() || In_Range(::As_Target(cell), 0)) { + return(ACTION_ATTACK); + } + } + } + + // Changed for multiplayer. ST - 3/13/2019 5:52PM + if (Is_Owned_By_Player() && Can_Player_Move()) { + //if (IsOwnedByPlayer && Can_Player_Move()) { + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (shiftdown) { + return(ACTION_MOVE); + } + + /* + ** If the object can enter the cell specified, then allow + ** movement to it. + */ + if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { + return(ACTION_MOVE); + } + return(ACTION_NOMOVE); + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * * + * Use this routine to determine whether a movement order can be given to this object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given a movement order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Move(void) const +{ + return(PlayerPtr == House); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * + * * + * Call this routine to determine if this object can be given a fire order by the player. * + * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * + * cursor to appear. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given firing orders by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Fire(void) const +{ + if (House->IsHuman && Is_Techno() && Techno_Type_Class()->Primary != WEAPON_NONE) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * * + * Use this routine to determine if this object is equipped with a combat weapon. Such * + * determination is used by the AI system to gauge the threat potential of the object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object equipped with a combat weapon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Weapon_Equipped(void) const +{ + return(Techno_Type_Class()->Primary != WEAPON_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * * + * Use this routine to determine if the specified object is a candidate for repair. In * + * order to qualify, the object must be allowed to be repaired (in theory) and it must * + * be below full strength. If these conditions are met, then it can be repaired. * + * * + * INPUT: none * + * * + * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * + * is not allowed to be repaired, or it might be full strength already. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Repair(void) const +{ + /* + ** Temporary hack to disable repair cursor over non-buildings. + */ + if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_AIRCRAFT) { + return(false); + } + return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); +} + + +/*********************************************************************************************** + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * * + * Use this routine to determine the maximum range for the weapon indicated. * + * * + * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the range of the weapon (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Weapon_Range(int which) const +{ + WeaponType weapon = WEAPON_NONE; + TechnoTypeClass const & ttype = *Techno_Type_Class(); + + switch (which) { + case 0: + weapon = ttype.Primary; + break; + + case 1: + weapon = ttype.Secondary; + break; + } + if (weapon != WEAPON_NONE) { + if (weapon == WEAPON_NIKE && GameToPlay == GAME_NORMAL) { + return(Weapons[weapon].Range*2); + } + return(Weapons[weapon].Range); + } + return(0); +} + + +/*************************************************************************** + * TechnoClass::Override_Mission -- temporarily overides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + SuspendedTarCom = TarCom; + RadioClass::Override_Mission(mission, tarcom, navcom); + Assign_Target(tarcom); +} + + +/*************************************************************************** + * TechnoClass::Restore_Mission -- Restores an overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool TechnoClass::Restore_Mission(void) +{ + if (RadioClass::Restore_Mission()) { + Assign_Target(SuspendedTarCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Captured -- Handles capturing this object. * + * * + * This routine is called when this object gets captured by the house specified. It handles * + * removing this object from any targeting computers and then changes the ownership of * + * the object to the new house. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the object captured? Failure would mean that it is already under control of * + * the house specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Captured(HouseClass * newowner) +{ + if (newowner != House) { + + /* + ** Capture attempt springs any "entered" trigger. The entered trigger + ** occurs first since there may be a special trigger attached to this + ** object that flags a capture as a win and a destroy as a loss. This + ** order is necessary because the object is recorded as a kill as well. + */ + if (Trigger /*&& Trigger->House == newowner->Class->House*/) { + Trigger->Spring(EVENT_PLAYER_ENTERED, this); + } + + /* + ** Record this as a kill. + */ + Record_The_Kill(NULL); + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Remove(this); + newowner->Tracking_Add(this); +#endif // USE_RA_AI + + /* + ** Special kill record logic for capture process. + */ + switch (What_Am_I()) { + case RTTI_BUILDING: + if (newowner) newowner->BuildingsKilled[Owner()]++; + break; + + case RTTI_AIRCRAFT: + case RTTI_INFANTRY: + case RTTI_UNIT: + if (newowner) newowner->UnitsKilled[Owner()]++; + break; + + default: + break; + } + House->WhoLastHurtMe = newowner->Class->House; + + /* + ** Remove from targeting computers. + */ + Detach_All(false); + +#ifdef NEVER + /* + ** Break off any radio contact. + */ + Transmit_Message(RADIO_OVER_OUT); +#endif + + /* + ** Change ownership now. + */ + House = newowner; + IsOwnedByPlayer = (House == PlayerPtr); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * * + * This routine is called when this object has taken damage. It handles recording whether * + * this object has been destroyed. If it has, then mark the appropriate kill records as * + * necessary. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + /* + ** Adjust damage according to house override armor value. + */ + if (damage > 0) { + damage = damage * House->ArmorBias; + } + + ResultType result = ObjectClass::Take_Damage(damage, distance, warhead, source); + + switch (result) { + case RESULT_DESTROYED: + Transmit_Message(RADIO_OVER_OUT); + Stun(); + + /* + ** May trigger an achievement. ST - 11/14/2019 1:56PM + */ + if (GameToPlay == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + RTTIType what = What_Am_I(); + if (what == RTTI_AIRCRAFT || what == RTTI_INFANTRY || what == RTTI_UNIT) { + On_Achievement_Event(source->House, "UNIT_DESTROYED", object_type->IniName); + } + } + } + break; + + /* + ** If some damage was received and this object is cloaked, shimmer + ** the cloak a bit. + */ + default: + if (source && !House->Is_Ally(source)) { + IsTickedOff = true; + } + Do_Shimmer(); + break; + + case RESULT_NONE: + break; + } + return(result); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Max_Passengers -- Fetches the maximum passengers allowed. * + * * + * This routine will return with the maximum number of passengers allowed in this * + * transport. This typically applies to APCs and possibley transport helicopters. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of passengers this transport can carry. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Max_Passengers(void) const +{ + if (IsTransporter) { + return(5); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * * + * This routine is used to record the death of this object. It will handle updating the * + * owner house with the kill record as well as springing any trigger events associated with * + * this object's death. * + * * + * INPUT: source -- Pointer to the source of this object's death (if there is a source). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 08/23/1995 JLB : Building loss is only counted if it received damage. * + *=============================================================================================*/ +void TechnoClass::Record_The_Kill(TechnoClass * source) +{ + /* + ** Handle any trigger event associated with this object. + */ + if (Trigger && source) Trigger->Spring(EVENT_ATTACKED, this); + + if (Trigger && source) Trigger->Spring(EVENT_DISCOVERED, this); + + if (Trigger) Trigger->Spring(EVENT_DESTROYED, this); + + if (source) { + /* + ** Call the explicity cast versions of the Made_A_Kill function. This + ** is necessary because we don't want to add a virtual function to the + ** CrewClass. Doing so would complicate the save/load process. + */ + switch (source->What_Am_I()) { + case RTTI_INFANTRY: + ((InfantryClass *)source)->Made_A_Kill(); + break; + + case RTTI_UNIT: + ((UnitClass *)source)->Made_A_Kill(); + break; + + case RTTI_BUILDING: + ((BuildingClass *)source)->Made_A_Kill(); + break; + + case RTTI_AIRCRAFT: + ((AircraftClass *)source)->Made_A_Kill(); + break; + } + + House->WhoLastHurtMe = source->Owner(); + } + + switch (What_Am_I()) { + case RTTI_BUILDING: + if ( ((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { + House->BuildingsLost++; + } + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); + } + source->House->BuildingsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_AIRCRAFT: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_INFANTRY: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_UNIT: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * * + * This routine is used to find a nearby location from center of this object. It can lean * + * toward finding a location closest to an optional object. * + * * + * INPUT: object -- Optional object that the finding algorithm will try to find a close * + * spot to. * + * * + * OUTPUT: Returns with the cell that is closest to this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Nearby_Location(TechnoClass const * ) const +{ + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + CELL best = 0; + CELL cell = Coord_Cell(Center_Coord()); + for (int radius = 0; radius < MAP_CELL_W/2; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell = cell + XY_Cell(x, -radius); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + + newcell = cell + XY_Cell(x, radius); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell = cell + XY_Cell(-radius, y); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + + newcell = cell + XY_Cell(radius, y); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + } + + if (best) break; + } + + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * * + * This routine will start the stealth tank to uncloak. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Uncloak(void) +{ + if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { + Sound_Effect(VOC_CLOAK, Coord); + Cloak = UNCLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * * + * This routine will start the object into its cloaking state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Cloak(void) +{ + if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { + Sound_Effect(VOC_CLOAK, Coord); + Detach_All(false); + Cloak = CLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * * + * This routine is called when this object should shimmer. If the object is cloaked, then * + * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * + * affect occurs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Shimmer(void) +{ + if (IsCloakable && Cloak == CLOAKED) { + Cloak = CLOAKING; + CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * * + * This routine will determine how this object should be drawn. Typically, this is the * + * unmodified visible state, but cloaked objects have a different character. * + * * + * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * + * object? If false, then an object owned by the player will never become * + * completely invisible. * + * * + * OUTPUT: Returns with the visual character to use when displaying this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1995 JLB : Created. * + *=============================================================================================*/ +VisualType TechnoClass::Visual_Character(bool raw) +{ + /* + ** When uncloaked or in map editor mode, always draw the object normally. + */ + if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); + + /* + ** A cloaked unit will not be visible at all unless it is owned + ** by the player. + */ + if (Cloak == CLOAKED) { + // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + return(VISUAL_HIDDEN); + } + + int stage = CloakingDevice.Fetch_Stage(); + if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; + if (stage <= 0) { + return(VISUAL_NORMAL); + } + + stage = Cardinal_To_Fixed(MAX_UNCLOAK_STAGE, stage); + + if (stage < 0x0040) return(VISUAL_INDISTINCT); + if (stage < 0x0080) return(VISUAL_DARKEN); + if (stage < 0x00C0) return(VISUAL_SHADOWY); + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + if (stage < 0x00FF) return(VISUAL_RIPPLE); + return(VISUAL_HIDDEN); +} + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * * + * This routine is used to draw the object. It will handle any remapping or cloaking * + * effects required. This logic is isolated here since all techno object share the same * + * render logic when it comes to remapping and cloaking. * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window) +{ + if (shapefile) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if ((visual == VISUAL_HIDDEN) && (window == WINDOW_VIRTUAL)) { + visual = VISUAL_SHADOWY; + } + + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade); + } else { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, Map.UnitShadow); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL); + } + } +} + + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object_Virtual -- Draw object with virtual window support * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * shape_name -- The name of the shapefile * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/20/2019 1:31PM - ST : Created. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, const char *shape_name) +{ + if (shape_name == NULL || *shape_name == 0) { + /* + ** If there's no override shape name, then call the regular draw + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window); + return; + } + + if (shapefile) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if ((visual == VISUAL_HIDDEN) && (window == WINDOW_VIRTUAL)) { + visual = VISUAL_SHADOWY; + } + + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade); + } else { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, Map.UnitShadow); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL); + } + } +} + + + +/*********************************************************************************************** + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * * + * This routine is used to fetch the appropriate remap table to use for this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoClass::Remap_Table(void) +{ + return(House->Remap_Table(IsBlushing, true)); +} + + +/*********************************************************************************************** + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * * + * This routine is called when the specified object is about to be removed from the game * + * system. The target object is removed from any tracking computers that this object may * + * have. * + * * + * INPUT: target -- The target object (as a target value) that is being removed from the * + * game. * + * * + * all -- Is the target about to die? A false value might indicate that the * + * object is merely cloaking. In such a case, radio contact will not * + * be affected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Detach(TARGET target, bool all) +{ + RadioClass::Detach(target, all); + + if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { + SuspendedMission = MISSION_NONE; + SuspendedTarCom = TARGET_NONE; + } + + /* + ** If the targeting computer is assigned to the target, then the targeting + ** computer must be cleared. + */ + if (TarCom == target) { + Assign_Target(TARGET_NONE); + Restore_Mission(); + } + + /* + ** If it is in radio contact with another object, then that radio contact + ** must be broken. + */ + if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { + Transmit_Message(RADIO_OVER_OUT); + } +} + + +/*********************************************************************************************** + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * * + * This routine handles the destruction of any cargo this object may contain. Typical of * + * this would be when a transport helicopter gets destroyed. * + * * + * INPUT: source -- The source of the destruction of the cargo. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Kill_Cargo(TechnoClass * source) +{ + while (Is_Something_Attached()) { + FootClass * foot = Detach_Object(); + if (foot) { + foot->Record_The_Kill(source); + delete foot; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * * + * This routine is called when generating survivors to this object. This routine returns * + * the type of survivor to generate. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type of a survivor. * + * * + * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * + * generate. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType TechnoClass::Crew_Type(void) const +{ + InfantryType infantry = INFANTRY_E1; + if (House->ActLike == HOUSE_NEUTRAL) { + infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); + } else { + if (Techno_Type_Class()->Primary == WEAPON_NONE && Random_Pick(0, 6) == 1) { + if (Random_Pick(0, 1) == 0) { + infantry = INFANTRY_C1; + } else { + infantry = INFANTRY_C7; + } + } + } + return(infantry); +} + + +/*********************************************************************************************** + * TechnoClass::Value -- Fetches the target value for this object. * + * * + * This routine is used to fetch the target value for this object. The greater the value * + * returned, the better this object is as a target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + * 08/16/1995 JLB : Adjusted for early mission lame-out. * + *=============================================================================================*/ +int TechnoClass::Value(void) const +{ + int value = 0; + + /* + ** In early missions, contents of transports are not figured + ** into the total value. - 8/16/95 + */ + if (BuildLevel > 8 || GameToPlay != GAME_NORMAL) { + if (Is_Something_Attached()) { + FootClass * object = Attached_Object(); + + while (object) { + value += object->Value(); + object = (FootClass *)object->Next; + } + } + } + return Risk() + Techno_Type_Class()->Reward + value; +} + + +/*********************************************************************************************** + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * * + * This routine will return the range to scan based on the control value specified. The * + * value returned by this routine is typically used when scanning for enemies. * + * * + * INPUT: control -- The range control parameter. * + * 0 = Use weapon range (zero is returned in this special case). * + * -1 = Scan without range restrictions (-1 is returned in this case). * + * 1 = Scan up to twice weapon range. * + * * + * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * + * process. If zero is returned, then always check threat against In_Range(). If * + * -1 is returned, then no range limitation restriction exists. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Threat_Range(int control) const +{ + if (control == -1) return(-1); + if (control == 0) return(0); + + int range = MAX(Weapon_Range(0), Weapon_Range(1)); + range *= 2; + range = Bound(range, 0x0000, 0x0A00); + return(range); +} + + +/*********************************************************************************************** + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * * + * This routine is called when the base is being attacked. It will pull units off of the * + * field and send them back to defend the base. This routine will make taking an enemy * + * base much more difficult. * + * * + * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can drastically affect the game play. The computer will probably * + * call off its attacks as a result. * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +void TechnoClass::Base_Is_Attacked(TechnoClass const *enemy) +{ + FootClass *defender[6]; + int value[6]; + int count = 0; + int weakest = 0; + int desired = enemy->Risk() * 2; + int risktotal = 0; + + /* + ** Humans have to deal with their own base is attacked problems. + */ + if (!enemy || House->Is_Ally(enemy) || House->IsHuman) { + return; + } + + /* + ** Don't overreact if this building can defend itself. + */ + if (Techno_Type_Class()->Primary != WEAPON_NONE) return; + + /* + ** If the enemy is not an infantry or a unit there is not much we can + ** do about it. + */ + if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT ) { + return; + } + + /* + ** If the enemy is a gunboat, then don't do anything. + */ + // This should allow helicopters to retaliate however. Hmmm. + if (enemy->What_Am_I() == RTTI_UNIT && (*((UnitClass const *)enemy) == UNIT_GUNBOAT || *((UnitClass const *)enemy) == UNIT_HOVER)) { + return; + } + + /* + ** If the threat has already been dealt with then we don't need to do + ** any work. Check for that here. + */ + if (!((FootClass *)enemy)->BaseAttackTimer.Expired()) { + return; + } + + /* + ** We will need units to defend our base. We need to suspend teams until + ** the situation has been dealt with. + */ + TeamClass::Suspend_Teams(20); + + /* + ** Loop through the infantry looking for those who are capable of going + ** on a rescue mission. + */ + int index; + for (index = 0; index < Infantry.Count() && desired > 0; index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (infantry && infantry->Owner() == Owner()) { + + /* + ** Never recruite sticky guard units to defend a base. + */ + if (infantry->Mission == MISSION_STICKY || infantry->Mission == MISSION_SLEEP) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = infantry->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemys desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < 6) { + defender[count] = (FootClass *)infantry; + value[count] = threat; + count++; + continue; + } + + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) infantry; + continue; + } + if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + /* + ** Loop through the units looking for those who are capable of going + ** on a rescue mission. + */ + for (index = 0; index < Units.Count() && desired > 0; index++) { + UnitClass * unit = Units.Ptr(index); + if (unit && unit->Owner() == Owner()) { + + /* + ** Never recruite sticky guard units to defend a base. + */ + if (unit->Mission == MISSION_STICKY || unit->Mission == MISSION_SLEEP) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = unit->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemys desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < 6) { + defender[count] = (FootClass *)unit; + value[count] = threat; + count++; + continue; + } + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) unit; + continue; + } + if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + if (desired > 0) { + + /* + ** Sort the defenders by value, this doesn't take very long and will + ** help the closest defenders to respond. + */ + int lp; + for (lp = 0; lp < count - 1; lp ++) { + for (int lp2 = lp + 1; lp2 < count; lp2++) { + if (value[lp] < value[lp2]) { + + value[lp] ^= value[lp2]; + value[lp2] ^= value[lp]; + value[lp] ^= value[lp2]; + + FootClass *temp; + temp = defender[lp]; + defender[lp] = defender[lp2]; + defender[lp2] = temp; + } + } + } + + for (lp = 0; lp < count; lp ++) { + defender[lp]->Assign_Mission(MISSION_RESCUE); + defender[lp]->Assign_Target(enemy->As_Target()); + risktotal += defender[lp]->Risk(); + if (risktotal > desired) { + break; + } + } + } + + if (risktotal > desired) { + ((FootClass *)enemy)->BaseAttackTimer.Set(15 * 15); + } +} + + +/*********************************************************************************************** + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * * + * This routine will return the ownable bits for this object. The ownable bits represent * + * the houses that are allowed to own this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the ownable bits for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char TechnoClass::Get_Ownable(void) const +{ + return ((TechnoTypeClass const &)Class_Of()).Ownable; +} + + +/*********************************************************************************************** + * TechnoClass::Is_Techno -- Confirms that this is a TechnoClass object. * + * * + * This routine is called to confirm if this object is derived from the TechnoClass * + * object. At this level, the return value will always be true. * + * * + * INPUT: none * + * * + * OUTPUT: Is this object a TechnoClass or derived from it? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Techno(void) const +{ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * * + * This routine is called when the risk value for this object needs to be determined. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the risk value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Risk(void) const +{ + return(Techno_Type_Class()->Risk); +} + + +/*********************************************************************************************** + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * * + * This routine will return the current Tiberium load (expressed as a fixed point fraction) * + * that this object currently contains. Typical implementor of this function would be * + * the harvester. Any object that can return a non-zero value should derive from this * + * function in order to return the appropriate value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * + * 0x0000 = empty * + * 0x0080 = half full * + * 0x0100 = full * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Tiberium_Load(void) const +{ + return(0x0000); +} + + +/*********************************************************************************************** + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * * + * This routine is called when an object desires to load up on this object. The object * + * desiring to load is specified. The cell that the loading object should move to is * + * determined. The direction that this object should face is also calculated. This routine * + * will be overridden by those objects that can actually load up passengers. * + * * + * INPUT: object -- The object that is desiring to load up. * + * * + * moveto -- Reference to the cell that the loading object should move to before * + * the final load process occurs (this value will be filled in). * + * * + * OUTPUT: Returns with the direction that the transport object should face. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const +{ + moveto = 0; + return(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * * + * This routine will return the number of pips to display on this object when the object * + * is selected. The default condition is to return no pips at all. This routine is * + * derived for those objects that can have pips. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this object when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Pip_Count(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * * + * This routine will fetch the direction that a fired projectile will take. This is * + * usually the facing of the object's weapon. This routine will be derived for the objects * + * that have their weapon barrel facing a different direction than the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction a fired projectile will take. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Fire_Direction(void) const +{ + return(PrimaryFacing.Current()); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * * + * This routine is called when a voice reponse to a select action is desired. This routine * + * should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Select(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Response_Move -- Handles the voice repsonse to a movement request. * + * * + * This routine is called when a voice response to a movement order is desired. This * + * routine should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio repsonse. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Move(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * * + * This routine is called when a voice response to an attack order is desired. This routine * + * should be overridden for any object that actually have a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Attack(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * * + * This routine will search for a nearby target and assign it to this object's TarCom. * + * The method to use when scanning for a target is controlled by the parameter passed. * + * * + * INPUT: threat -- The threat control parameter used to control the range searched. The * + * only values recognized are THREAT_RANGE and THREAT_AREA. * + * * + * OUTPUT: Was a suitable target aquired and assigned to the TarCom? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Target_Something_Nearby(ThreatType threat) +{ + threat = threat & (THREAT_RANGE|THREAT_AREA); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + if ((threat & THREAT_RANGE) && !In_Range(TarCom)) { + Assign_Target(TARGET_NONE); + } + } + + /* + ** If there is no target, then try to find one and assign it as + ** the target for this unit. + */ + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** Return with answer to question: Does this unit have a target? + */ + return(Target_Legal(TarCom)); +} + + +/*********************************************************************************************** + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * * + * This routine is called when there is an attached object that should detach and leave * + * this object. Typical of this would be the refinery and APC. * + * * + * INPUT: object -- The object that is trying to leave this object. * + * * + * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * + * there is insufficient room to detach the specified object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Exit_Object(TechnoClass *) +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Random_Animate -- Performs some idle animation for the object. * + * * + * This is a maintenance routine that is called when the object might want to check to see * + * if it should go into some idle animation. Infantry are a good example of objects that * + * perform idle animations. This routine must be overridden by the derived object types * + * in order to give it some functionality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Random_Animate(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. This routine must be overridden since at this level, movement is not allowed. * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Destination(TARGET ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Scatter -- Causes the object to scatter to an adjacent cell. * + * * + * This routine is called when the object needs to get out of the way. This might be as a * + * result of combat or findpath reasons. * + * * + * INPUT: coord -- The source of the reason to scatter. The object should try to run * + * away from this coordinate. * + * * + * forced -- Is the scatter a forced scatter? If false, then this is merely a * + * request that scattering might be a good idea. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Scatter(COORDINATE , bool , bool ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * * + * This routine is called when the object should intelligently revert to an idle state. * + * Typically this routine is called after some mission has completed. This routine must * + * be overridden by the various object types. It is located at this level merely to provide * + * a virtual function entry point. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Enter_Idle_Mode(bool ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * * + * This routine is used to render the small transportation pip (occupant feedback graphic) * + * used for transporter object. It will also display if the techno object is "primary" * + * if necessary. * + * * + * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsiquent pips * + * are drawn rightward. * + * * + * window-- The window that pip clipping is relative to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) +{ + /* + ** Transporter type objects have a different graphic representation for the pips. The + ** pip color represents the type of occupant. + */ + if (Techno_Type_Class()->IsTransporter) { + ObjectClass const * object = Attached_Object(); + + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + PipEnum pip = PIP_EMPTY; + + if (object) { + pip = PIP_FULL; + if (object->What_Am_I() == RTTI_INFANTRY) { + if (*((InfantryClass *)object) == INFANTRY_RAMBO) { + pip = PIP_COMMANDO; + } + if (*((InfantryClass *)object) == INFANTRY_E7) { + pip = PIP_ENGINEER; + } + if (((InfantryClass *)object)->Class->IsCivilian) { + pip = PIP_CIVILIAN; + } + } + object = object->Next; + } + CC_Draw_Pip(this, Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + } else { + + /* + ** Display number of how many attached objects there are. This is also used + ** to display the fullness rating for a harvester. + */ + int pips = Pip_Count(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + CC_Draw_Pip(this, Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + /* + ** Display whether this unit is a leader unit or not. + */ + if (IsLeader && (window != WINDOW_VIRTUAL)) { + CC_Draw_Pip(this, Class_Of().PipShapes, PIP_PRIMARY, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * + *=============================================================================================*/ +BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + BuildingClass * best = 0; + + /* + ** First check to see if there are ANY buildings of the specified + ** type in thi house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->BScan & (1L << b)) { + int bestval = -1; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (building && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + !building->IsInLimbo && + *building == b && + ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { + best = building; + bestval = Distance(building); + } + } + } + } + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * * + * This routine is called when an object would like to exit from this (presumed) transport. * + * A suitable cell should be returned by this routine. The specified object will probably * + * be unloaded at that cell. * + * * + * INPUT: techno -- Pointer to the object that would like to unload. This is used to * + * determine suitability for placement. * + * * + * OUTPUT: Returns with the cell that is recommended for object exit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const +{ + return(Coord_Cell(Docking_Coord())); +} + + +/*********************************************************************************************** + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * * + * This routine is used by the selling back mechanism in order to credit the owning house * + * with some refund credits. The value returned is the credits to refund to the owner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credits to refund to the owner. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Refund_Amount(void) const +{ + int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias; + + /* + ** If the object is carrying Tiberium directly (i.e., the harvester), then + ** account for the credits of the load. + */ + cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; + + if (House->IsHuman) { + cost /= 2; + } + return(cost); +} + + + + + + + + +/* +** Additions to TechnoClass to track discovery per-player. ST - 3/6/2019 11:18AM +** +** +** +** +** +*/ + + +/*********************************************************************************************** + * TechnoClass::Is_Discovered_By_Player -- Has this object been disovered by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if discovered by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:20AM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Discovered_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int) player->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } else { + int shift = (int) PlayerPtr->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } + return false; +} + + +/*********************************************************************************************** + * TechnoClass::Set_Discovered_By_Player -- Mark that this object been disovered by the player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:23AM - ST * + *=============================================================================================*/ +void TechnoClass::Set_Discovered_By_Player(HouseClass *player) +{ + int shift = 0; + if (player && player->Class) { + shift = (int) player->Class->House; + } else { + shift = (int)PlayerPtr->Class->House; + } + IsDiscoveredByPlayerMask |= (1 << shift); + + if (GameToPlay == GAME_NORMAL && player == PlayerPtr) { + IsDiscoveredByPlayer = true; + } +} + + + +/*********************************************************************************************** + * TechnoClass::Clear_Discovered_By_Players -- Clear player discovery flags * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/27/2020 - SKY * + *=============================================================================================*/ +void TechnoClass::Clear_Discovered_By_Players() +{ + IsDiscoveredByPlayerMask = 0; + IsDiscoveredByPlayer = false; +} + + + +/*********************************************************************************************** + * TechnoClass::Is_Owned_By_Player -- Is this object owned by the active human player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/13/2019 5:00PM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Owned_By_Player(HouseClass *player) const +{ + if (player == NULL) { + return (House == PlayerPtr) ? true : false; + } + return (House == player) ? true : false; +} + + + + + + + + + + + +#ifdef USE_RA_AI + + /* + ** Additions for AI, from Red Alert. ST - 7/16/2019 11:36AM + */ + + +/*********************************************************************************************** + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * * + * This routine will calculate and return the anti-aircraft strength of this object. * + * Typical users of this strength value is the base defense expert system AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned * + * is an abstract number to be used for relative comparisons only. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Air(void) const +{ + //assert(IsActive); + + if (Is_Weapon_Equipped()) { + WeaponType weapon_type = Techno_Type_Class()->Primary; + WeaponTypeClass const * weapon = &Weapons[weapon_type]; + //BulletTypeClass const * bullet = weapon->Bullet; + const BulletTypeClass &bullet = BulletTypeClass::As_Reference(Weapons[weapon_type].Fires); + //WarheadTypeClass const * warhead = weapon->WarheadPtr; + WarheadTypeClass const * warhead = &Warheads[bullet.Warhead]; + + + //if (bullet->IsAntiAircraft) { + if (weapon_type != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[weapon_type].Fires).IsAntiAircraft) { + int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF; + + //if (Techno_Type_Class()->Is_Two_Shooter()) { + // value *= 2; + //} + return(value/50); + } + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * * + * This routine is used to examine and calculate the anti-armor strength of this object. * + * Typical user user of this would be the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the relative anti-armor combat value for this object. The value * + * is abstract and is only to be used in relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Armor(void) const +{ + //assert(IsActive); + + if (Is_Weapon_Equipped()) { + + WeaponType weapon_type = Techno_Type_Class()->Primary; + //if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + if (weapon_type != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[weapon_type].Fires).IsAntiAircraft) { + return 0; + } + + //WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + //BulletTypeClass const * bullet = weapon->Bullet; + //WarheadTypeClass const * warhead = weapon->WarheadPtr; + WeaponTypeClass const * weapon = &Weapons[weapon_type]; + const BulletTypeClass &bullet = BulletTypeClass::As_Reference(Weapons[weapon_type].Fires); + WarheadTypeClass const * warhead = &Warheads[bullet.Warhead]; + + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF; + //if (Techno_Type_Class()->Is_Two_Shooter()) { + // value *= 2; + //} + if (bullet.IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * * + * This routine is used to determine the anti-infantry strength of this object. The * + * typical user of this routine is the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is * + * abstract and should only be used for relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Infantry(void) const +{ + //assert(IsActive); + + if (Is_Weapon_Equipped()) { + //if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + WeaponType weapon_type = Techno_Type_Class()->Primary; + if (weapon_type != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[weapon_type].Fires).IsAntiAircraft) { + return 0; + } + + //WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + //BulletTypeClass const * bullet = weapon->Bullet; + //WarheadTypeClass const * warhead = weapon->WarheadPtr; + WeaponTypeClass const * weapon = &Weapons[weapon_type]; + const BulletTypeClass &bullet = BulletTypeClass::As_Reference(Weapons[weapon_type].Fires); + WarheadTypeClass const * warhead = &Warheads[bullet.Warhead]; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF; + //if (Techno_Type_Class()->Is_Two_Shooter()) { + // value *= 2; + //} + if (bullet.IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + + +int TechnoTypeClass::Legal_Placement(CELL pos) const +{ + if (pos == -1) return(0); + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + short const * offset = Occupy_List(true); + bool build = (What_Am_I() == RTTI_BUILDINGTYPE); + + if (build) { + return ((BuildingTypeClass*)this)->Legal_Placement(pos); + } + + while (offset != NULL && *offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + //if (build) { + // if (!Map[cell].Is_Clear_To_Build(Speed)) { + // return(0); + // } + //} else { + //if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) { + if (!Map[cell].Is_Clear_To_Move(false, false)) { + return(0); + } + //} + } + return(1); +} +#endif //USE_RA_AI \ No newline at end of file diff --git a/TIBERIANDAWN/TECHNO.H b/TIBERIANDAWN/TECHNO.H new file mode 100644 index 000000000..6690db6f9 --- /dev/null +++ b/TIBERIANDAWN/TECHNO.H @@ -0,0 +1,372 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\techno.h_v 2.17 16 Oct 1995 16:46:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TECHNO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TECHNO_H +#define TECHNO_H + +#include "radio.h" +#include "stage.h" +#include "cargo.h" +#include "flasher.h" +#include "house.h" +#include "target.h" +#include "bullet.h" +#include "door.h" +#include "crew.h" + +/**************************************************************************** +** This is the common data between building and units. +*/ +class TechnoClass : public RadioClass, + public FlasherClass, + public StageClass, + public CargoClass, + public DoorClass, + public CrewClass +{ + public: + + /* + ** This flag will be true if the object has been damaged with malace. + ** Damage received due to friendly fire or wear and tear does not count. + ** The computer is not allowed to sell a building unless it has been + ** damaged with malace. + */ + unsigned IsTickedOff:1; + + /* + ** If this object has inherited the ability to cloak, then this bit will + ** be set to true. + */ + unsigned IsCloakable:1; + + /* + ** If this object is designated as special then this flag will be true. For + ** buildings, this means that it is the primary factory. For units, it means + ** that the unit is the team leader. + */ + unsigned IsLeader:1; + + /* + ** Certain units are flagged as "loaners". These units are typically transports that + ** are created solely for the purpose of delivering reinforcements. Such "loaner" + ** units are not owned by the player and thus cannot be directly controlled. These + ** units will leave the game as soon as they have fulfilled their purpose. + */ + unsigned IsALoaner:1; + + /* + ** Once a unit enters the map, then this flag is set. This flag is used to make + ** sure that a unit doesn't leave the map once it enters the map. + */ + unsigned IsLocked:1; + + /* + ** Buildings and units with turrets usually have a recoil animation when they + ** fire. If this flag is true, then the next rendering of the object will be + ** in the "recoil state". The flag will then be cleared pending the next + ** firing event. + */ + unsigned IsInRecoilState:1; + + /* + ** If this unit is "loosely attached" to another unit it is given special + ** processing. A unit is in such a condition when it is in the process of + ** unloading from a transport type object. During the unloading process + ** the transport object must stay still until the unit is free and clear. + ** At that time it radios the transport object and the "tether" is broken - + ** freeing both the unit and the transport object. + */ + unsigned IsTethered:1; + + /* + ** Is this object owned by the player? If not, then it is owned by the computer + ** or remote opponent. This flag facilitates the many logic differences when dealing + ** with player's or computer's units or buildings. + */ + unsigned IsOwnedByPlayer:1; + + /* + ** The more sophisticated game objects must keep track of whether they are discovered + ** or not. This is because the state of discovery can often control how the object + ** behaves. In addition, this fact is used in radar and user I/O processing. + */ + unsigned IsDiscoveredByPlayer:1; + + /* + ** This is used to control the computer recognizing this object. + */ + unsigned IsDiscoveredByComputer:1; + + /* + ** Some game objects can be of the "lemon" variety. This means that they take damage + ** even when everything is ok. This adds a little variety to the game. + */ + unsigned IsALemon:1; + + /* + ** This flag is used to control second shot processing for those units or buildings + ** that fire two shots in quick succession. When this flag is true, it indicates that + ** the second shot is ready to fire. After this shot is fired, regular rearm timing + ** is used rather than the short rearm time. + */ + unsigned IsSecondShot:1; + + /* + ** For units in area guard mode, this is the recorded home position. The guarding + ** unit will try to stay near this location in the course of it's maneuvers. This is + ** also used to record a pending transport for those passengers that are waiting for + ** the transport to become available. It is also used by harvesters so that they know + ** where to head back to after unloading. + */ + TARGET ArchiveTarget; + + /* + ** This is the house that the unit belongs to. + */ + HouseClass * House; + + /* + ** This records the current cloak state for this vehicle. + */ + CloakType Cloak; + StageClass CloakingDevice; + + /* (Targeting Computer) + ** This is the target value for the item that this vehicle should ATTACK. If this + ** is a vehicle with a turret, then it may differ from its movement destination. + */ + TARGET TarCom; + TARGET SuspendedTarCom; + + /* + ** This is the visible facing for the unit or building. + */ + FacingClass PrimaryFacing; + + /* + ** This is the arming countdown. It represents the time necessary + ** to reload the weapon. + */ + unsigned char Arm; + + /* + ** The number of shot this object can fire before running out of ammo. If this + ** value is zero, then firing is not allowed. If -1, then there is no ammunition + ** limit. + */ + int Ammo; + + /* + ** Used by the Obelisk laser to cache its laser weapon rendering data. + ** Line data is cleared after the specified delay. + ** Each line consists of: x, y, x1, y1, color, frame (max frame used to determine duration) + */ + int Lines[3][5]; + int LineCount; + int LineFrame; + int LineMaxFrames; + + /* + ** This is the amount of money spent to produce this object. This value really + ** only comes into play for the case of buildings that have special "free" + ** objects available when purchased at the more expensive rate. + */ + int PurchasePrice; + + /* + ** Per-player view of whether a techno object is discovered. One bit for each house type. ST - 3/6/2019 11:15AM + */ + unsigned int IsDiscoveredByPlayerMask; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TechnoClass(void); + TechnoClass(HousesType house); + virtual ~TechnoClass(void) {}; + + /* + ** Query functions. + */ + virtual int Refund_Amount(void) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual BuildingClass * Find_Docking_Bay(StructType b, bool friendly) const; + virtual int Threat_Range(int control) const; + virtual InfantryType Crew_Type(void) const; + TechnoTypeClass const * Techno_Type_Class(void) const {return((TechnoTypeClass const *)&Class_Of());}; + CELL Nearby_Location(TechnoClass const * from=NULL) const; + virtual unsigned char Get_Ownable(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + virtual bool Is_Weapon_Equipped(void) const; + virtual bool Can_Repair(void) const; + virtual bool Is_Techno(void) const; + virtual HousesType Owner(void) const; + virtual int Risk(void) const; + virtual int Value(void) const; + virtual int Rearm_Delay(bool second=true) const; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual int Tiberium_Load(void) const; + virtual DirType Desired_Load_Dir(ObjectClass * , CELL & moveto) const; + virtual int Pip_Count(void) const; + virtual DirType Fire_Direction(void) const; + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType house, int count=7); // 2019/09/20 JAS - Added record of who clicked on the object + virtual bool Select(bool allow_mixed = false); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Player_Assign_Mission(MissionType order, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + + /* + ** Combat related. + */ + void Base_Is_Attacked(TechnoClass const *enemy); + void Kill_Cargo(TechnoClass * source); + virtual void Record_The_Kill(TechnoClass * source); + virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual void Stun(void); + virtual bool In_Range(COORDINATE coord, int which=0) const; + virtual bool In_Range(TARGET target, int which=0, bool reciprocal_check = true) const; + virtual bool In_Range(ObjectClass const * target, int which=0, bool reciprocal_check = true) const; + virtual void Death_Announcement(TechnoClass const * source=0) const = 0; + virtual FireErrorType Can_Fire(TARGET target, int which=0) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual void Assign_Target(TARGET target); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + virtual BulletClass * Fire_At(TARGET target, int which=0); + virtual int Weapon_Range(int which) const; + virtual bool Captured(HouseClass * newowner); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value) const; + bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual bool Revealed(HouseClass * house); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + VisualType Visual_Character(bool raw = false); + void Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window); + + // Added. ST - 6/20/2019 1:34PM + void Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, const char *shape_name); + + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Draw_Pips(int x, int y, WindowNumberType window); + virtual void Hidden(void); + virtual bool Mark(MarkType mark); + virtual int Exit_Object(TechnoClass *); + virtual void Do_Uncloak(void); + virtual void Do_Cloak(void); + virtual void Do_Shimmer(void); + + /* + ** Movement and animation. + */ + virtual void Random_Animate(void); + virtual void Assign_Destination(TARGET target); + virtual void Scatter(COORDINATE source = NULL, bool forced=false, bool nokidding=false); + virtual void Per_Cell_Process(bool); + virtual void Enter_Idle_Mode(bool initial=false); + + /* + ** Map entry and exit logic. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual void Detach(TARGET target, bool all); + + + + + /* + ** New functions for per-player discovery for multiplayer. ST - 3/6/2019 11:17AM + */ + bool Is_Discovered_By_Player(HouseClass *player = NULL) const; + void Set_Discovered_By_Player(HouseClass *player = NULL); + void Clear_Discovered_By_Players(); + bool Is_Owned_By_Player(HouseClass *player = NULL) const; + + +#ifdef USE_RA_AI + /* + ** For AI, from Red Alert. ST - 7/16/2019 11:38AM + */ + int Anti_Air(void) const; + int Anti_Armor(void) const; + int Anti_Infantry(void) const; +#endif // USE_RA_AI + + /* + ** Facing translation tables that fix the flaw with 3D studio when + ** it renders 45 degree angles. + */ + static int const BodyShape[32]; +// static int const TurretShape[32]; + + +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TEMPLATE.CPP b/TIBERIANDAWN/TEMPLATE.CPP new file mode 100644 index 000000000..e6694fc52 --- /dev/null +++ b/TIBERIANDAWN/TEMPLATE.CPP @@ -0,0 +1,408 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\template.cpv 2.18 16 Oct 1995 16:51:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateClass::As_Target -- Converts a template object into a target number. * + * TemplateClass::Init -- Resets the template object system. * + * TemplateClass::Mark -- Lifts or drops a template object. * + * TemplateClass::Read_INI -- Reads the scenario control INI file. * + * TemplateClass::Select -- Select the template object. * + * TemplateClass::TemplateClass -- Default constructor for template class objects. * + * TemplateClass::TemplateClass -- Template object constructor. * + * TemplateClass::Unlimbo -- Places a template object into the game/map system. * + * TemplateClass::Write_INI -- Writes the template objects to the INI file. * + * TemplateClass::delete -- Returns a template object to the pool. * + * TemplateClass::new -- Allocates a template object from pool * + * TemplateClass::Validate -- validates template pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "template.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TemplateClass::VTable; + + +/*********************************************************************************************** + * TemplateClass::Validate -- validates template pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TemplateClass::Validate(void) const +{ + int num; + + num = Templates.ID(this); + if (num < 0 || num >= TEMPLATE_MAX) { + Validate_Error("TEMPLATE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TemplateClass::Read_INI -- Reads the scenario control INI file. * + * * + * This routine reads the scenario control INI file and creates all * + * templates specified therein. * + * * + * INPUT: buffer -- Pointer to the loaded scenario control INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Read_INI(char *buffer) +{ + char *tbuffer; // Accumulation buffer of unit IDs. + int len; // Size of data in buffer. + CELL cell; // Cell of building. + char buf[128]; // Working string staging buffer. + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + TemplateType temp; // Terrain type. + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + temp = TemplateTypeClass::From_Name(strtok(buf, ",\r\n")); + if (temp != TEMPLATE_NONE) { + new TemplateClass(temp, cell); + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TemplateClass::Write_INI -- Writes the template objects to the INI file. * + * * + * This routine is used to write all the template objects out to the INI file specified. * + * It is used by the scenario editor when saving the game. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Write_INI(char *buffer) +{ + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing template data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Find all templates and write them to the file. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->TType != TEMPLATE_NONE && ptr->TIcon == 0) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s", TemplateTypeClass::As_Reference(ptr->TType).IniName); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Default constructor for template class objects. * + * * + * This is the default constructor for a template class object. This construction method * + * should NEVER be used by the game except as a consequence of declaring an array of * + * uninitialized template objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(void) : + Class(0) +{ +} + + +/*********************************************************************************************** + * TemplateClass::As_Target -- Converts a template object into a target number. * + * * + * This routine will convert a template object into a target number. Because templates * + * never exist as a template object in the game system, this routine will never be called. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TemplateClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEMPLATE, Templates.ID(this))); +} + + +/*********************************************************************************************** + * TemplateClass::Init -- Resets the template object system. * + * * + * This routine resets the template object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Init(void) +{ + TemplateClass *ptr; + + Templates.Free_All(); + + ptr = new TemplateClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * TemplateClass::Mark -- Lifts or drops a template object. * + * * + * This routine handles placing or removing a template object. This * + * entails marking the map as appropriate and redisplaying affected * + * cells. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the template successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + * 12/23/1994 JLB : Examines low level legality before processing. * + *=============================================================================================*/ +bool TemplateClass::Mark(MarkType mark) +{ + Validate(); + static bool noup = false; + void const * iset = Class->Get_Image_Data(); + if (iset && ObjectClass::Mark(mark)) { + + void * map = Get_Icon_Set_Map(iset); + + for (int y = 0; y < Class->Height; y++) { + for (int x = 0; x < Class->Width; x++) { + CELL cell = Coord_Cell(Coord) + y*MAP_CELL_W + x; + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + int number = y*Class->Width + x; + + /* + ** Determine if this logical icon actually maps to a real icon. If no real + ** icon is associated with this logical position, then don't do any action + ** since none is required. + */ + char * mapptr = (char*)map; + bool real = (mapptr[number] != -1); + + if (real) { + /* + ** Lift the terrain object from the map. + */ + if (mark == MARK_UP && !noup) { + if (cellptr->TType == Class->Type && cellptr->TIcon == number) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } + } + + /* + ** Place the terrain object down. + */ + if (mark == MARK_DOWN) { + if (*this == TEMPLATE_CLEAR1) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } else { + cellptr->TType = Class->Type; + // cellptr->TIcon = real; + cellptr->TIcon = number; + } + } + + cellptr->Redraw_Objects(); + cellptr->Recalc_Attributes(); + } + } + } + } + + /* + ** When marking this template down onto the map, the map template numbers are update + ** but the template is removed from existence. Make sure that the deletion of the + ** template object doesn't also lift the template numbers up from the map. + */ + if (mark == MARK_DOWN) { + noup = true; + delete this; + noup = false; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateClass::new -- Allocates a template object from pool * + * * + * This routine is used to allocate a template object from the * + * template object pool. * + * * + * INPUT: size -- The size of a template object (not used). * + * * + * OUTPUT: Returns with a pointer to an available template object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * TemplateClass::operator new(size_t ) +{ + void * ptr = Templates.Allocate(); + if (ptr) { + ((TemplateClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TemplateClass::delete -- Returns a template object to the pool. * + * * + * This routine will return a template object to the template object * + * pool. A template so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::operator delete(void *ptr) +{ + if (ptr) { + ((TemplateClass *)ptr)->IsActive = false; + } + Templates.Free((TemplateClass *)ptr); +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Template object constructor. * + * * + * This is the constructor for a template object. * + * * + * INPUT: type -- The template object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(TemplateType type, CELL pos) : + Class(&TemplateTypeClass::As_Reference(type)) +{ + if (pos != -1) { + Unlimbo(Cell_Coord(pos)); + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/TEMPLATE.H b/TIBERIANDAWN/TEMPLATE.H new file mode 100644 index 000000000..43043cd03 --- /dev/null +++ b/TIBERIANDAWN/TEMPLATE.H @@ -0,0 +1,122 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\template.h_v 2.18 16 Oct 1995 16:45:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the template object. Template objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class TemplateClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + TemplateClass(void); + TemplateClass(TemplateType type, CELL pos=-1); + virtual ~TemplateClass(void) {if (GameActive) TemplateClass::Limbo();}; + operator TemplateType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_TEMPLATE;}; + + static void Init(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + int Icon_Number(CELL cell); + + /* + ** Object entry and exit from the game system. + */ + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int , int , WindowNumberType ) {}; + virtual bool Mark(MarkType mark); + + /* + ** User I/O. + */ + + /* + ** Combat related. + */ + virtual TARGET As_Target(void) const; + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "TEMPLATE";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** This is a pointer to the template object's class. + */ + TemplateTypeClass const * const Class; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TERRAIN.CPP b/TIBERIANDAWN/TERRAIN.CPP new file mode 100644 index 000000000..6aa5e0cc4 --- /dev/null +++ b/TIBERIANDAWN/TERRAIN.CPP @@ -0,0 +1,897 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\terrain.cpv 2.16 16 Oct 1995 16:51:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : May 8, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainClass::AI -- Process the terrain object AI. * + * TerrainClass::As_Target -- Converts the terrain object into a target number. * + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * TerrainClass::Heath_Ratio -- Determines the health ratio for the terrain object. * + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * TerrainClass::Mark -- Marks the terrain object on the map. * + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * TerrainClass::TerrainClass -- Constructor for a terrain class object. * + * TerrainClass::TerrainClass -- This is the constructor for a terrain object. * + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * TerrainClass::Write_INI -- Writes all terrain objects to the INI file. * + * TerrainClass::delete -- Deletes a terrain object. * + * TerrainClass::new -- Creates a new terrain object. * + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * TerrainClass::Validate -- validates terrain pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" + +#define BARNACLE_STAGE 22 +#define FIRST_SPORE_STAGE 30 +#define FIRST_SPORABLE_LEVEL 7 + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TerrainClass::VTable; + + +/*********************************************************************************************** + * TerrainClass::Validate -- validates terrain pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TerrainClass::Validate(void) const +{ + int num; + + num = Terrains.ID(this); + if (num < 0 || num >= TERRAIN_MAX) { + Validate_Error("TERRAIN"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * * + * This is the default destructor for terrain objects. It will remove the object from the * + * map and tracking systems, but only if the game is running. Otherwise, it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::~TerrainClass(void) +{ + if (GameActive && Class) { + TerrainClass::Limbo(); + } +} + + +/*********************************************************************************************** + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * * + * This routine is called when damage is to be inflicted upon the terrain object. It is * + * through this routine that terrain objects are attacked and thereby destroyed. Not all * + * terrain objects can be damaged by this routine however. * + * * + * INPUT: damage -- The damage points to inflict (raw). * + * * + * warhead -- The warhead type the indicates the kind of damage. This is used to * + * determine if the terrain object is damaged and if so, by how much. * + * * + * OUTPUT: bool; Was the terrain object destroyed by this damage? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 12/11/1994 JLB : Shortens attached burning animations. * + *=============================================================================================*/ +ResultType TerrainClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Small arms can never destroy a terrain element. + */ + if ((!IsOnFire || warhead == WARHEAD_FIRE) && warhead != WARHEAD_SA && !Class->IsImmune) { + + res = ObjectClass::Take_Damage(damage, distance, warhead, source); + + if (damage && warhead == WARHEAD_FIRE) { + Catch_Fire(); + } + + /* + ** If the terrain object is destroyed by this damage, then only remove it if it + ** currently isn't on fire and isn't in the process of crumbling. + */ + if (res == RESULT_DESTROYED) { + + /* + ** Remove this terrain object from the targeting computers of all other + ** game objects. No use beating a dead horse. + */ + Detach_All(); + + if (IsOnFire) { + + /* + ** Attached flame animation should be shortened as much as possible so that + ** crumbling can begin soon. + */ + Shorten_Attached_Anims(this); + } else { + Start_To_Crumble(); + } + } + } + return(res); +} + + +/*********************************************************************************************** + * TerrainClass::As_Target -- Converts the terrain object into a target number. * + * * + * This routine will take the terrain object and generate a unique targeting number. This * + * number is used for the NavCom and TarCom of other units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target number of this terrain object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TerrainClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TERRAIN, Terrains.ID(this))); +} + + +/*********************************************************************************************** + * TerrainClass::new -- Creates a new terrain object. * + * * + * This routine creates a new terrain object by grabbing a free slot * + * out of the terrain object pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void * TerrainClass::operator new(size_t) +{ + void * ptr = Terrains.Allocate(); + if (ptr) { + ((TerrainClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TerrainClass::delete -- Deletes a terrain object. * + * * + * This routine deletes a terrain object by returning it to the * + * terrain object pool. * + * * + * INPUT: ptr -- Pointer to the terrain object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::operator delete(void *ptr) +{ + if (ptr) { + ((TerrainClass *)ptr)->IsActive = false; + } + Terrains.Free((TerrainClass *)ptr); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- This is the constructor for a terrain object * + * * + * This constructor for a terrain object will initialize the terrain * + * object with it's proper type and insert it into the access * + * tracking system. * + * * + * INPUT: type -- The terrain object type. * + * * + * cell -- The location of the terrain object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(TerrainType type, CELL cell) : + Class(&TerrainTypeClass::As_Reference(type)) +{ + IsBlossoming = false; + IsBarnacled = false; + IsSporing = false; + IsCrumbling = false; + IsOnFire = false; + Strength = Class->MaxStrength; + if (cell != -1) { + if (!Unlimbo(Cell_Coord(cell))) { + delete this; + } + } + Set_Rate(0); // turn off animation +} + + +/*********************************************************************************************** + * TerrainClass::Mark -- Marks the terrain object on the map. * + * * + * This routine will mark or remove the terrain object from the map * + * tracking system. This is typically called when the terrain object * + * is first created, when it is destroyed, and whenever it needs to be * + * redrawn. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the terrain object successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 12/23/1994 JLB : Performs low level legality check before proceeding. * + *=============================================================================================*/ +bool TerrainClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + short const *overlap = Class->Overlap_List(); + short const *occupy = Class->Occupy_List(); + CELL cell = Coord_Cell(Coord); + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, overlap); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * * + * This routine is used to render the terrain object at the location specified and * + * clipped to the window specified. This is the gruntwork drawing routine for the * + * terrain objects as they are displayed on the map. * + * * + * INPUT: x,y -- The coordinate to draw the terrain object at (centered). * + * * + * window -- The clipping window to draw to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + * 11/09/1994 JLB : Changed selected terrain highlight method. * + *=============================================================================================*/ +void TerrainClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapedata; + + shapedata = Class->Get_Image_Data(); + if (shapedata) { + int shapenum = 0; + + /* + ** Determine the animation stage to render the terrain object. If it is crumbling, then + ** it will display the crumbling animation. + */ + if (IsCrumbling || Class->IsTransformable) { + shapenum = Fetch_Stage()+IsCrumbling; + } else { + if (Strength < 2) { + shapenum++; + } + } + + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Is_Selected_By_Player() && Debug_Map) flags = flags | SHAPE_FADING; + + IsTheaterShape = true; + CC_Draw_Shape(this, shapedata, shapenum, x, y, window, flags|SHAPE_WIN_REL|SHAPE_GHOST, Map.FadingLight, Map.UnitShadow); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * * + * This routine will clear out the terrain object system so that no terrain objects will * + * exists. It is called prior to loading or starting a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Init(void) +{ + TerrainClass *ptr; + + Terrains.Free_All(); + + ptr = new TerrainClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * * + * This routine will examine the cell specified and determine if the the terrain object * + * can legally exist there. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: If the terrain object can be placed in the cell specified, then a value less than * + * 256 will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 01/01/1995 JLB : Actually works now. * + *=============================================================================================*/ +MoveType TerrainClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + Validate(); + short const *offset; // Pointer to cell offset list. + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (!Map[(CELL)(cell + *offset++)].Is_Generally_Clear()) { + return(MOVE_NO); + } + } + return(MOVE_OK); +} + + +/*********************************************************************************************** + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * * + * This routine is called if the terrain object is supposed to catch on fire. The routine * + * performs checking to make sure that only flammable terrain objects that aren't already * + * on fire get caught on fire. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object caught on fire by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 12/11/1994 JLB : Don't catch fire if already on fire or crumbling. * + *=============================================================================================*/ +bool TerrainClass::Catch_Fire(void) +{ + Validate(); + if (!IsCrumbling && !IsOnFire && Class->IsFlammable) { + AnimClass * anim = new AnimClass(ANIM_BURN_BIG, Coord_Add(Sort_Y(), 0xFFB00000L)); + if (anim) { + anim->Attach_To(this); + } + anim = new AnimClass(ANIM_BURN_MED, Coord_Add(Sort_Y(), 0xFF200000L), 15); + if (anim) { + anim->Attach_To(this); + } + IsOnFire = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * * + * When the fire has gone out on a burning terrain object, this routine is called. The * + * animation has already been terminated prior to calling this routine. All this routine * + * needs to perform is any necessary local flag updating. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Fire_Out(void) +{ + Validate(); + if (IsOnFire) { + IsOnFire = false; + if (!IsCrumbling && !Strength) { + Detach_All(); + Mark(); + Start_To_Crumble(); + new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, Class->CenterBase)); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::AI -- Process the terrain object AI. * + * * + * This is used to handle any AI processing necessary for terrain objects. This might * + * include animation effects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 09/28/1994 JLB : Crumbling animation. * + *=============================================================================================*/ +void TerrainClass::AI(void) +{ + Validate(); + ObjectClass::AI(); + + if (StageClass::Graphic_Logic()) { + Mark(); + + /* + ** If the terrain object is in the process of crumbling, then when at the + ** last stage of the crumbling animation, delete the terrain object. + */ + if (IsCrumbling && Fetch_Stage() == Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + delete this; + } + } + + /* + ** if this is a blossom tree, let's update it at this time + */ + if (Class->IsTransformable) { + // If it's already blossomed, is it at barnacled stage? + if (IsBlossoming) { + // if it's not barnacled yet, check if we're at that stage + if (!IsBarnacled) { + if (Fetch_Stage() == BARNACLE_STAGE) { + IsBarnacled = true; + Set_Rate(0); // turn off animation + } + } else { + /* + ** If it's barnacled, see if it's pulsing and spore-ing + */ + if (IsSporing) { + if (Fetch_Stage() >= Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + Explosion_Damage(Sort_Y(), 5, NULL, WARHEAD_SPORE); + Set_Stage(FIRST_SPORE_STAGE); + if (Random() & 1) { + IsSporing = false; + StageClass::Set_Rate(0); + } + } + } else { + if (Random() == 255) { // is it time to start sporing? + IsSporing = true; + StageClass::Set_Rate(Options.Normalize_Delay(1)); + } + } + } + } else { + + // If it hasn't tried to blossom yet, can it do so now? + if (Random_Picky((int)1, (int)5000, (char*)NULL, (int)0) == 1) { + IsBlossoming = true; + StageClass::Set_Stage(1); + StageClass::Set_Rate(Options.Normalize_Delay(1)); + } + } + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * * + * This debugging support routine is used to display the status of the terrain object to * + * the debug screen. * + * * + * INPUT: mono -- The mono screen to display the status to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * * + * This routine is used to unlimbo the terrain object onto a location on the map. Normal * + * unlimbo procedures are sufficient except that the coordinate location of a terrain * + * object is based on the upper left corner of a cell rather than the center. Mask the * + * coordinate value so that it snaps to the upper left corner and then proceed with a * + * normal unlimbo process. * + * * + * INPUT: coord -- The coordinate to mark as the terrain's location. * + * * + * dir -- unused * + * * + * OUTPUT: bool; Was the terrain object successful in the unlimbo process? Failure could be * + * the result of illegal positioning. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + * 11/16/1994 JLB : Checks for theater legality. * + *=============================================================================================*/ +bool TerrainClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + if (Class->Theater & (1 << Map.Theater)) { + return(ObjectClass::Unlimbo(coord, dir)); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * * + * This routine is used to start the crumbling process for terrain object. This only * + * applies to trees. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Start_To_Crumble(void) +{ + Validate(); + if (!IsCrumbling) { + IsCrumbling = true; + Set_Rate(2); + Set_Stage(0); + } +} + + +/*********************************************************************************************** + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * * + * This routine (called as a part of the limbo process) will remove the terrain occupation * + * flag in the cell it occupies. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + CELL cell = Coord_Cell(Coord); + Map[cell].Flag.Occupy.Monolith = false; + } + return(ObjectClass::Limbo()); +} + + +/*********************************************************************************************** + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * * + * Use this routine to fetch the center point terrain * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Center_Coord(void) const +{ + Validate(); + return(Coord_Add(Coord, Class->CenterBase)); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- Constructor for a terrain class object. * + * * + * This is the default constructor for a terrain class object. It basically initializes * + * the object to a null -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(void) : + Class(0) +{ + IsOnFire = false; + IsCrumbling = false; + IsBlossoming = false; + IsBarnacled = false; + IsSporing = false; + Strength = 0; +} + + +/*********************************************************************************************** + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * * + * This routine will return with a pointer to the radar icon to use for the cell number * + * specified. * + * * + * INPUT: cell -- The cell number to use when determine what icon pointer to return. * + * * + * OUTPUT: Returns with a pointer to the 9 pixel values that make up the icon of this * + * terrain object located at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char * TerrainClass::Radar_Icon(CELL cell) +{ + Validate(); + unsigned char *icon = (unsigned char *)Class->Get_Radar_Data(); // get a pointer to radar icons + int width = *icon++; // extract the width from data + int height = *icon++; // extract the width from data + + /* + ** Icon number that we need can be found by converting the cell and base + ** cell to and x and y offset from the upper left of the cell, and then + ** multiplying it by the width of the terrain in icons, which we + ** conveniantly stored out as the first byte of every icon we made. + */ + int basecell = Coord_Cell(Coord); // find the base cell of terrain + int ydiff = Cell_Y(cell) - Cell_Y(basecell); + int xdiff = Cell_X(cell) - Cell_X(basecell); + if (xdiff < width && ydiff < height) { + int iconnum = (ydiff * width) + xdiff; + return(icon + (iconnum * 9)); + } + return(NULL); +} + + +/*********************************************************************************************** + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * * + * This routine reads a scenario control INI file and creates all * + * terrain objects specified therein. Objects so created are placed * + * upon the map. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Read_INI(char *buffer) +{ + char *tbuffer; // Accumulation buffer of unit IDs. + int len; // Size of data in buffer. + char buf[128]; + TerrainClass * tptr; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + TerrainType terrain; // Terrain type. + CELL cell; + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + terrain = TerrainTypeClass::From_Name(strtok(buf, ",")); + if (terrain != TERRAIN_NONE) { + tptr = new TerrainClass(terrain, cell); + tptr->Trigger = TriggerClass::As_Pointer(strtok(NULL,",")); + if (tptr->Trigger) + tptr->Trigger->AttachCount++; + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TerrainClass::Write_INI -- Writes all terrain objects to the INI file. * + * * + * This routine is used to write all the terrain objects out to the INI file specified. * + * It is used by the scenario editor to write out the map data. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing terrain data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the terrain data out. + */ + for (index = 0; index < Terrains.Count(); index++) { + TerrainClass * terrain; + + terrain = Terrains.Ptr(index); + if (!terrain->IsInLimbo && terrain->IsActive) { + + sprintf(uname, "%d", Coord_Cell(terrain->Coord)); + sprintf(buf, "%s,%s", + terrain->Class->IniName, + terrain->Trigger ? terrain->Trigger->Get_Name() : "None" ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} \ No newline at end of file diff --git a/TIBERIANDAWN/TERRAIN.H b/TIBERIANDAWN/TERRAIN.H new file mode 100644 index 000000000..efc55ec7a --- /dev/null +++ b/TIBERIANDAWN/TERRAIN.H @@ -0,0 +1,179 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\terrain.h_v 2.16 16 Oct 1995 16:47:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "object.h" +#include "type.h" + + +/**************************************************************************** +** Each type of terrain has certain pieces of static information associated +** with it. This class elaborates this data. +*/ +class TerrainClass : public ObjectClass, public StageClass +{ + public: + TerrainTypeClass const * const Class; + operator TerrainType(void) const {return Class->Type;}; + + /* + ** Constructor for terrain object class. + */ + static void * TerrainClass::operator new(size_t size); + static void TerrainClass::operator delete(void *ptr); + TerrainClass(void); + TerrainClass(TerrainType id, CELL cell); + virtual ~TerrainClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_TERRAIN;}; + + static void Init(void); + + /* + ** Terrain specific support functions. + */ + void Start_To_Crumble(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const {return Coord;}; + virtual COORDINATE Sort_Y(void) const {return Coord_Add(Coord, Class->CenterBase);}; + virtual COORDINATE Target_Coord(void) const {return Sort_Y();}; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType dir=DIR_N); + virtual bool Limbo(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing = FACING_NONE) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark=MARK_CHANGE); + unsigned char *Radar_Icon(CELL cell); + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType, int) {}; + + /* + ** Combat related. + */ + virtual void Fire_Out(void); + virtual bool Catch_Fire(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + virtual TARGET As_Target(void) const; + + /* + ** AI. + */ + virtual void AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "TERRAIN";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** If this terrain object is on fire, then this flag will be true. + */ + unsigned IsOnFire:1; + + /* + ** Is this a terrain object that undergoes crumbling animation and it is + ** in fact crumbling at this time? + */ + unsigned IsCrumbling:1; + + /* + ** If this is a tree that becomes a blossom tree, is it currently doing so? + */ + unsigned IsBlossoming:1; + + /* + ** If this is a blossom tree, is it barnacled? + */ + unsigned IsBarnacled:1; + + /* + ** If this is a blossom tree that is barnacled, is it pulsing and spewing + ** out spores? + */ + unsigned IsSporing:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TEXTBLIT.H b/TIBERIANDAWN/TEXTBLIT.H new file mode 100644 index 000000000..df3f26d78 --- /dev/null +++ b/TIBERIANDAWN/TEXTBLIT.H @@ -0,0 +1,51 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + + +#define MAX_ENTRIES 128 + +class TextBlitClass { + + public: + + TextBlitClass(void); + ~TextBlitClass(void){}; + + void Add (int x, int y, int dx, int dy, int w, int h); + void Clear (void); + void Update (void); + + + private: + + typedef struct { + int SourceX; + int SourceY; + int DestX; + int DestY; + int Width; + int Height; + } BlitEntryType; + + BlitEntryType BlitListo [MAX_ENTRIES]; + int Count; + +}; + + +extern GraphicBufferClass *TextPrintBuffer; +extern TextBlitClass BlitList; + diff --git a/TIBERIANDAWN/TEXTBTN.CPP b/TIBERIANDAWN/TEXTBTN.CPP new file mode 100644 index 000000000..82eb66d97 --- /dev/null +++ b/TIBERIANDAWN/TEXTBTN.CPP @@ -0,0 +1,374 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\textbtn.cpv 2.18 16 Oct 1995 16:49:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textbtn.h" + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * text -- Pointer to the text string to display on top of the button. * + * x,y -- Pixel coordinate of button's upper left corner. * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass(id, x, y, w, h), + String(text) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; +// if (SeenBuff.Get_Width() != 320) Width *= 2; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; +// if (SeenBuff.Get_Height() != 200) Height *= 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + X = Y = 0; + Width = Height = 0; + IsBlackBorder = 0; + String = 0; + PrintFlags = TPF_8POINT; +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- The text number to use for displaying on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass (unsigned id, int text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass (id, x, y, w, h), + String(0) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + Set_Text(text); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; +// if (SeenBuff.Get_Width() != 320) Width *= 2; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; +// if (SeenBuff.Get_Height() != 200) Height *= 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int TextButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + //Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynmaically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(char const * text, bool resize) +{ + String = text; + Flag_To_Redraw(); + if (resize && String) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String)+8; + Height = FontHeight + FontYSpacing + 2; + } +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * * + * This will set the text for this button. The text is provided as a text number. This * + * number is automatically converted to the appropriate text string before being stored * + * in the button's structure. * + * * + * INPUT: text -- The text number to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text number information is lost when it is assigned to the button. Once * + * the assignment takes place, the text number is NOT remembered by the button. * + * Only the associated text string is. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(int text, bool resize) +{ + if (text != TXT_NONE) { + Set_Text(Text_String(text), resize); + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Background(void) +{ + /* + ** Draw a border if selected style. + */ + if (IsBlackBorder) { + LogicPage->Draw_Rect (X-1, Y-1, X+Width+2, Y+Height+2, BLACK); + } + + /* + ** Draw the body & set text color. + */ + BoxStyleEnum style; + //if (FontPtr == GradFont6Ptr) { + if (PrintFlags & TPF_6PT_GRAD) { + if (IsDisabled) { + style = BOXSTYLE_GREEN_DIS_RAISED; + } else { + if (IsPressed) { + style = BOXSTYLE_GREEN_DOWN; + } else { + style = BOXSTYLE_GREEN_RAISED; + } + } + } else { + if (IsDisabled) { + style = BOXSTYLE_DIS_RAISED; + } else { + if (IsPressed) { + style = BOXSTYLE_DOWN; + } else { + style = BOXSTYLE_RAISED; + } + } + } + Draw_Box(X, Y, Width, Height, style, true); +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Text(char const * text) +{ + /* + ** Display the text. + */ + if (String) { + int color; + //if (FontPtr == GradFont6Ptr) { + if (PrintFlags & TPF_6PT_GRAD) { + TextPrintType flags; + + color = CC_GREEN; + + if (IsDisabled) { + flags = (TextPrintType)0; + } else { + if (IsPressed || IsOn) { + flags = TPF_USE_GRAD_PAL|TPF_BRIGHT_COLOR; + } else { + flags = TPF_USE_GRAD_PAL|TPF_MEDIUM_COLOR; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, color, TBLACK, PrintFlags|flags|TPF_CENTER); + } else { + if (IsDisabled) { +// color = DKGREY; + color = LTGREY; + } else { + if (IsPressed) { + if (PrintFlags & TPF_NOSHADOW) { + color = DKGREY; + } else { + color = LTGREY; + } + } else { + color = WHITE; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, IsOn ? RED : color, TBLACK, PrintFlags|TPF_CENTER); + } + + } +} diff --git a/TIBERIANDAWN/TEXTBTN.H b/TIBERIANDAWN/TEXTBTN.H new file mode 100644 index 000000000..d8b3ae34d --- /dev/null +++ b/TIBERIANDAWN/TEXTBTN.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\textbtn.h_v 2.18 16 Oct 1995 16:46:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEXTBTN_H +#define TEXTBTN_H + +#include "toggle.h" + + +class TextButtonClass : public ToggleClass +{ + public: + TextButtonClass(void); + TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + TextButtonClass(unsigned id, int text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const *text, bool resize = false); + virtual void Set_Text(int text, bool resize = false); + virtual void Set_Style (TextPrintType style) {PrintFlags = style;} + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + unsigned IsBlackBorder:1; + + /* + ** This points to a constant string that is used for the button's text. + */ + char const * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/TIBERIANDAWN/THEME.CPP b/TIBERIANDAWN/THEME.CPP new file mode 100644 index 000000000..bbdd6a771 --- /dev/null +++ b/TIBERIANDAWN/THEME.CPP @@ -0,0 +1,560 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\theme.cpv 2.18 16 Oct 1995 16:51:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : May 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ThemeClass::Scan -- Scans all scores for availability. * + * ThemeClass::AI -- Process the theme engine and restart songs. * + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * ThemeClass::From_Name -- Determines theme number from specified name. * + * ThemeClass::Full_Name -- Retrieves the full score name. * + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * ThemeClass::Stop -- Stops the current theme from playing. * + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "theme.h" + + +/* +** These are the actual filename list for the theme sample files. +*/ +ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { + {"AIRSTRIK", TXT_THEME_AIRSTRIKE, 0, 200, false, false,false,true}, + {"80MX226M", TXT_THEME_80MX, 0, 248, false, false,false,true}, + {"CHRG226M", TXT_THEME_CHRG, 0, 256, true, false,false,true}, + {"CREP226M", TXT_THEME_CREP, 0, 222, true, false,false,true}, + {"DRIL226M", TXT_THEME_DRIL, 0, 272, true, false,false,true}, + {"DRON226M", TXT_THEME_DRON, 0, 275, true, false,false,true}, + {"FIST226M", TXT_THEME_FIST, 0, 212, true, false,false,true}, + {"RECN226M", TXT_THEME_RECON, 0, 261, true, false,false,true}, + {"VOIC226M", TXT_THEME_VOICE, 0, 306, true, false,false,true}, + {"HEAVYG", TXT_THEME_HEAVYG, 0, 180, true, false,false,true}, + {"J1", TXT_THEME_J1, 4, 187, true, false,false,true}, +// {"J1", TXT_THEME_J1, 4, 187, false, false,false,true}, + {"JDI_V2", TXT_THEME_JDI_V2, 5, 183, true, false,false,true}, + {"RADIO", TXT_THEME_RADIO, 6, 183, true, false,false,true}, + {"RAIN", TXT_THEME_RAIN, 7, 156, true, false,false,true}, + {"AOI", TXT_THEME_AOI, 0, 168, true, true, false,true}, + {"CCTHANG", TXT_THEME_CCTHANG, 12, 193, true, false,false,true}, + {"DIE", TXT_THEME_DIE, 11, 162, false, false,false,true}, + {"FWP", TXT_THEME_FWP, 10, 53, true, false,false,true}, + {"IND", TXT_THEME_IND, 1, 175, true, false,false,true}, + {"IND2", TXT_THEME_IND2, 1, 38, true, false,false,true}, + {"JUSTDOIT", TXT_THEME_JUSTDOIT, 9, 142, true, false,false,true}, + {"LINEFIRE", TXT_THEME_LINEFIRE, 8, 125, true, false,false,true}, + {"MARCH", TXT_THEME_MARCH, 7, 157, true, false,false,true}, + {"TARGET", TXT_THEME_TARGET, 0, 173, true, false,false,true}, + {"NOMERCY", TXT_THEME_NOMERCY, 2, 204, true, false,false,true}, + {"OTP", TXT_THEME_OTP, 3, 182, true, false,false,true}, + {"PRP", TXT_THEME_PRP, 4, 211, true, false,false,true}, + {"ROUT", TXT_THEME_ROUT, 12, 121, false, true, false,true}, + {"HEART", TXT_THEME_HEART, 5, 206, false, true, false,true}, + {"STOPTHEM", TXT_THEME_STOPTHEM, 0, 190, true, false,false,true}, + {"TROUBLE", TXT_THEME_TROUBLE, 6, 191, true, true, false,true}, + {"WARFARE", TXT_THEME_WARFARE, 0, 182, true, false,false,true}, + {"BEFEARED", TXT_THEME_BEFEARED, 13, 164, false, true, false,true}, + {"I_AM", TXT_THEME_IAM, 6, 161, false, false,false,true}, + {"WIN1", TXT_THEME_WIN1, 0, 41, false, true, true,true}, + {"MAP1", TXT_THEME_WIN1, 0, 61, false, false,true,true}, + {"VALKYRIE", TXT_THEME_VALK, 0, 306, false, false,true,true}, +}; + + +/*********************************************************************************************** + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * * + * This routine is used to retrieve a pointer to the base filename for the theme * + * specified. * + * * + * INPUT: theme -- The theme number to convert into a base filename. * + * * + * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * + * theme number is invalid, then a pointer to "No Theme" is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Base_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(_themes[theme].Name); + } + return("No theme"); +} + + +/*********************************************************************************************** + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * * + * This is the default constructor for the theme class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ThemeClass::ThemeClass(void) +{ + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; +} + + +/*********************************************************************************************** + * ThemeClass::Full_Name -- Retrieves the full score name. * + * * + * This routine will fetch and return with a pointer to the full name of the theme * + * specified. * + * * + * INPUT: theme -- The theme to fetch the full name for. * + * * + * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * + * EMS memory. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Full_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(Text_String(_themes[theme].Fullname)); + } + return(NULL); +} + + +/*********************************************************************************************** + * ThemeClass::AI -- Process the theme engine and restart songs. * + * * + * This is a maintenance function that will restart an appropriate theme if the current one * + * has finished. This routine should be called frequently. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 01/23/1995 JLB : Picks new song just as it is about to play it. * + *=============================================================================================*/ +void ThemeClass::AI(void) +{ + if (SampleType && !Debug_Quiet) { + if (ScoresPresent && Options.ScoreVolume && !Still_Playing() && Pending != THEME_NONE) { + + /* + ** If the pending song needs to be picked, then pick it now. + */ + if (Pending == THEME_PICK_ANOTHER) { + Pending = Next_Song(Score); + } + + /* + ** Start the song playing and then flag it so that a new song will + ** be picked when this one ends. + */ + Play_Song(Pending); + Pending = THEME_PICK_ANOTHER; + } + Sound_Callback(); + } +} + + +/*********************************************************************************************** + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * * + * use this routine to figure out what song number to play. It examines the option settings * + * for repeat and shuffle so that it can return the correct value. * + * * + * INPUT: theme -- The origin (last) index. The new value is related to this for all but * + * the shuffling method of play. * + * * + * OUTPUT: Returns with the song number for the next song to play. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * + *=============================================================================================*/ +ThemeType ThemeClass::Next_Song(ThemeType theme) +{ + if (theme == THEME_NONE) { + theme = Next_Song(THEME_PICK_ANOTHER); + } else { + if (theme == THEME_PICK_ANOTHER || (!_themes[theme].Repeat && !Options.IsScoreRepeat)) { + if (Options.IsScoreShuffle) { + + /* + ** Shuffle the theme, but never pick the same theme that was just + ** playing. + */ + ThemeType newtheme; + do { + newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); + } while(newtheme == theme || !Is_Allowed(newtheme)); + theme = newtheme; + + } else { + + /* + ** Sequential score playing. + */ + do { + theme++; + if (theme > THEME_LAST) { + theme = THEME_FIRST; + } + } while(!Is_Allowed(theme)); + } + } + } + return(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * * + * This routine will cause the current song to fade and the specified song to start. This * + * is the normal and friendly method of changing the current song. * + * * + * INPUT: theme -- The song to start playing. If -1 is pssed in, then just the current song * + * is faded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Queue_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && (Pending == THEME_NONE || Pending == THEME_PICK_ANOTHER)) { + if (!Options.ScoreVolume && theme != THEME_NONE) return; + + Pending = theme; + Fade_Sample(Current, THEME_DELAY); + } +} + + +/*********************************************************************************************** + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * * + * This routine is used to start the specified theme playing right now. If there is already * + * a theme playing, it is cut short so that this one may start. * + * * + * INPUT: theme -- The theme number to start playing. * + * * + * OUTPUT: Returns with the sample play handle. * + * * + * WARNINGS: This cuts off any current song in a abrubt manner. Only use this routine when * + * necessary. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Play_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume) { + Stop(); + Score = theme; + if (theme >= THEME_FIRST) { + +#ifdef DEMO + if (_themes[theme].Scenario != 99) { + CCFileClass file(Theme_File_Name(theme)); + if (file.Is_Available()) { + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); + } else { + Current = -1; + } + } else { + Current = -1; + } +#else + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); +#endif + } + } + return(Current); +} + + +/*********************************************************************************************** + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * * + * This routine will construct (into a static buffer) a filename that matches the theme * + * number specified. This constructed filename is returned as a pointer. The filename will * + * remain valid until the next call to this routine. * + * * + * INPUT: theme -- The theme number to convert to a filename. * + * * + * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 05/09/1995 JLB : Theme variation support. * + *=============================================================================================*/ +char const * ThemeClass::Theme_File_Name(ThemeType theme) +{ + static char name[_MAX_FNAME+_MAX_EXT]; + + if (_themes[theme].Variation && Special.IsVariation) { + _makepath(name, NULL, NULL, _themes[theme].Name, ".VAR"); + CCFileClass file(name); + if (file.Is_Available()) { + return((char const *)(&name[0])); + } + } + _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); + return((char const *)(&name[0])); +} + + +/*********************************************************************************************** + * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * + * * + * Use this routine to calculate the length of the song. The length is determined by * + * reading the header of the song and dividing the sample rate into the sample length. * + * * + * INPUT: theme -- The song number to examine to find its length. * + * * + * OUTPUT: Returns with the length of the specified theme. This length is in the form of * + * seconds. * + * * + * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Track_Length(ThemeType theme) +{ + if ((unsigned)theme < THEME_COUNT) { + return(_themes[theme].Duration); + } + return(0); +} + + +/*********************************************************************************************** + * ThemeClass::Stop -- Stops the current theme from playing. * + * * + * Use this routine to stop the current theme. After this routine is called, no more music * + * will play until the Start() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Stop(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet) { + if (Current != -1) { + Stop_Sample(Current); + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; + } + } +} + + +/*********************************************************************************************** + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * * + * Use this routine to determine if music is still playing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the music still audible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Still_Playing(void) +{ + if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { + return(Sample_Status(Current)); + } + return(false); +} + + +/*********************************************************************************************** + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * * + * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * + * if the scenario is too early for that score, or the score only is allowed in special * + * cases. * + * * + * INPUT: index -- The score the check to see if it is allowed to play. * + * * + * OUTPUT: Is the specified score allowed to play in the normal score playlist? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + *=============================================================================================*/ +bool ThemeClass::Is_Allowed(ThemeType index) const +{ +#ifdef DEMO + char buffer[128]; + + sprintf(buffer, "%s.AUD", Base_Name(index)); + CCFileClass file(buffer); + if (_themes[index].Scenario == 99 || !file.Is_Available()) { + _themes[index].Scenario = 99; + return(false); + } +#endif + + return( + _themes[index].Available && + (_themes[index].Normal || +// (index == THEME_MAP1 && ScenarioInit) || + ((Special.IsVariation && _themes[index].Variation && index != THEME_WIN1) && +#ifndef DEMO + (GameToPlay != GAME_NORMAL || _themes[index].Scenario <= (int)Scenario) && +#endif + (index != THEME_J1 || Special.IsJurassic)))); +} + + +/*********************************************************************************************** + * ThemeClass::From_Name -- Determines theme number from specified name. * + * * + * Use this routine to convert a name (either the base filename of the theme, or a partial * + * substring of the full name) into the matching ThemeType value. Typical use of this is * + * when parsing the INI file for theme control values. * + * * + * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * + * theme name. * + * * + * OUTPUT: Returns with the matching theme number. If no match could be found, then * + * THEME_NONE is returned. * + * * + * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * + * the full theme name, the comparison is case sensitive. * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +ThemeType ThemeClass::From_Name(char const * name) +{ + if (name && strlen(name) > 0) { + /* + ** First search for an exact name match with the filename + ** of the theme. This is guaranteed to be unique. + */ + ThemeType theme; + for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (stricmp(_themes[theme].Name, name) == 0) { + return(theme); + } + } + + /* + ** If the filename scan failed to find a match, then scan for + ** a substring within the full name of the score. This might + ** yeild a match, but is not guaranteed to be unique. + */ + for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { + return(theme); + } + } + } + + return(THEME_NONE); +} + + +/*********************************************************************************************** + * ThemeClass::Scan -- Scans all scores for availability. * + * * + * This routine should be called whenever a score mixfile is registered. It will scan * + * to see if any score is unavailable. If this is the case, then the score will be so * + * flagged in order not to appear on the play list. This condition is likely to occur * + * when expansion mission disks contain a different score mix than the release version. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Scan(void) +{ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { +// if (theme == THEME_J1 && !Special.IsJurassic) { +// _themes[theme].Available = false; +// } else { + _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); +// } + } +} + diff --git a/TIBERIANDAWN/THEME.H b/TIBERIANDAWN/THEME.H new file mode 100644 index 000000000..7583e95b4 --- /dev/null +++ b/TIBERIANDAWN/THEME.H @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\theme.h_v 2.16 16 Oct 1995 16:45:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef THEME_H +#define THEME_H + +class ThemeClass +{ + private: + static char const * Theme_File_Name(ThemeType theme); + + int Current; // Handle to current score. + ThemeType Score; // Score number currently being played. + ThemeType Pending; // Score to play next. + + typedef struct { + char const * Name; // Filename of score. + int Fullname; // Text number for full score name. + int Scenario; // Scenario when it first becomes available. + int Duration; // Duration of theme in seconds. + bool Normal; // Allowed in normal game play? + bool Variation; // Is there a variation to the score? + bool Repeat; // Always repeat this score? + bool Available; // Is the score available? + } ThemeControl; + + static ThemeControl _themes[THEME_COUNT]; + + enum { + THEME_DELAY=TIMER_SECOND + }; + + public: + ThemeClass(void); + + ThemeType From_Name(char const * name); + int Track_Length(ThemeType index); + int Max_Themes(void) {return THEME_COUNT;}; + char const * Full_Name(ThemeType index) const; + char const * Base_Name(ThemeType index) const; + void AI(void); + void Queue_Song(ThemeType index); + int Play_Song(ThemeType index); + ThemeType What_Is_Playing(void) {return Score;}; + void Stop(void); + void Fade_Out(void) {Queue_Song(THEME_NONE);}; + int Still_Playing(void); + ThemeType Next_Song(ThemeType index); + bool Is_Allowed(ThemeType index) const; + static void /*_pascal*/ Scan(void); +}; + +#endif diff --git a/TIBERIANDAWN/TOGGLE.CPP b/TIBERIANDAWN/TOGGLE.CPP new file mode 100644 index 000000000..47c39928c --- /dev/null +++ b/TIBERIANDAWN/TOGGLE.CPP @@ -0,0 +1,196 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\toggle.cpv 2.18 16 Oct 1995 16:50:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : February 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "toggle.h" + + +/*********************************************************************************************** + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * * + * This is the normal constructor for toggle buttons. A toggle button is one that most * + * closesly resembles the Windows style. It has an up and down state as well as an on * + * and off state. * + * * + * INPUT: id -- ID number for this button. * + * * + * x,y -- Pixel coordinate of upper left corner of this button. * + * * + * w,h -- Width and height of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ToggleClass::ToggleClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTPRESS|LEFTRELEASE, true) +{ + IsPressed = false; + IsOn = false; + IsToggleType = false; +} + + +/*********************************************************************************************** + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * * + * This routine will turn the button on. The button will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_On(void) +{ + IsOn = true; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * * + * This routine will turn the toggle button "off". It will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_Off(void) +{ + IsOn = false; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Action -- Handles mouse clicks on a text button. * + * * + * This routine will process any mouse or keyboard event that is associated with this * + * button object. It detects and flags the text button so that it will properly be drawn * + * in a pressed or raised state. It also handles any toggle state for the button. * + * * + * INPUT: flags -- The event flags that triggered this button. * + * * + * key -- The keyboard code associated with this event. Usually this is KN_LMOUSE * + * or similar, but it could be a regular key if this text button is given * + * a hotkey. * + * * + * OUTPUT: Returns whatever the lower level processing for buttons decides. This is usually * + * true. * + * * + * WARNINGS: The button is flagged to be redrawn by this routine. * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + * 02/02/1995 JLB : Left press doesn't get passed to other buttons now * + *=============================================================================================*/ +int ToggleClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there are no action flag bits set, then this must be a forced call. A forced call + ** must never actually function like a real call, but rather only performs any necessary + ** graphic updating. + */ + if (!flags) { + if ((unsigned)(Get_Mouse_X() - X) < (unsigned)Width && (unsigned)(Get_Mouse_Y() - Y) < (unsigned)Height ) { + if (!IsPressed) { + IsPressed = true; + Flag_To_Redraw(); + } + } else { + if (IsPressed) { + IsPressed = false; + Flag_To_Redraw(); + } + } + } + + /* + ** Handle the sticky state for this gadget. It must be processed here + ** because the event flags might be cleared before the action function + ** is called. + */ + Sticky_Process(flags); + + /* + ** Flag the button to show the pressed down imagery if this mouse button + ** was pressed over this gadget. + */ + if (flags & LEFTPRESS) { + IsPressed = true; + Flag_To_Redraw(); + flags &= ~LEFTPRESS; + ControlClass::Action(flags, key); + key = KN_NONE; // erase the event + return(true); // stop processing other buttons now + } + + if (flags & LEFTRELEASE) { + if (IsPressed) { + if (IsToggleType) { + IsOn = (IsOn == false); + } + IsPressed = false; + } else { + flags &= ~LEFTRELEASE; + } + } + + /* + ** Do normal button processing. This ends up causing the button's ID number to + ** be returned from the controlling Input() function. + */ + return(ControlClass::Action(flags, key)); +} + + diff --git a/TIBERIANDAWN/TOGGLE.H b/TIBERIANDAWN/TOGGLE.H new file mode 100644 index 000000000..b45460c64 --- /dev/null +++ b/TIBERIANDAWN/TOGGLE.H @@ -0,0 +1,79 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\toggle.h_v 2.16 16 Oct 1995 16:47:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TOGGLE_H +#define TOGGLE_H + +#include "control.h" + +/* +** This class handles gadgets that behave like the Windows buttons. That is, once the mouse +** button is clicked over them, they capture the focus until the mouse button is released. +** They have a different imagery for the pressed and released states. They only recognize +** a valid selection when the mouse button is release while over the button. +*/ +class ToggleClass : public ControlClass +{ + public: + ToggleClass(unsigned id, int x, int y, int w, int h); + virtual void Turn_On(void); + virtual void Turn_Off(void); + + /* + ** Is this button in a pressed down state? This occurs when the mouse is clicked on the + ** button and the mouse is still being held down. + */ + unsigned IsPressed:1; + + /* + ** This is the button on/off state. Sometimes a button that is "on" has a different + ** imagery than one that is "off". If the on/off state is not necessary, then just + ** ignore this flag. + */ + unsigned IsOn:1; + + /* + ** If this button can be turned "on" or "off", then this flag should be set to + ** true. Sometimes a button needs to display its on/off state. In the render routine, + ** examine the IsOn flag and display accordingly. If this flag is false, then the + ** IsOn flag will not be changed, regardless of button clicking. + */ + unsigned IsToggleType:1; + + protected: + + virtual int Action(unsigned flags, KeyNumType &key); +}; + +#endif diff --git a/TIBERIANDAWN/TRIGGER.CPP b/TIBERIANDAWN/TRIGGER.CPP new file mode 100644 index 000000000..4255ce787 --- /dev/null +++ b/TIBERIANDAWN/TRIGGER.CPP @@ -0,0 +1,1483 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\trigger.cpv 2.17 16 Oct 1995 16:51:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * + * TriggerClass::Action_From_Name -- retrieves ActionType for given name * + * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * + * TriggerClass::As_Pointer -- returns pointer for the given trigger name * + * TriggerClass::As_Target -- Converts trigger to a target value * + * TriggerClass::Event_From_Name -- retrieves EventType for given name * + * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * + * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * + * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * + * TriggerClass::Init -- clears triggers for new scenario * + * TriggerClass::Name_From_Action -- retrieves name for ActionType * + * TriggerClass::Name_From_Event -- retrieves name for EventType * + * TriggerClass::Read_INI -- reads triggers from the INI file * + * TriggerClass::Remove -- removes this trigger from the game * + * TriggerClass::Spring -- Trigger processing routine for cell-based triggers * + * TriggerClass::Spring -- Trigger processing routine for house-based triggers * + * TriggerClass::Spring -- Trigger processing routine for object-based triggers * + * TriggerClass::TriggerClass -- constructor * + * TriggerClass::Validate -- validates trigger pointer * + * TriggerClass::Write_INI -- writes triggers to the INI file * + * TriggerClass::operator delete -- 'delete' operator * + * TriggerClass::operator new -- 'new' operator * + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static void Do_All_To_Hunt(void); + +#define FIXUP 0 + +/* +********************************** Globals ********************************** +*/ +static const char * EventText[EVENT_COUNT + 1] = { + "None", + "Player Enters", + "Discovered", + "Attacked", + "Destroyed", + "Any", + "House Discov.", + "Units Destr.", + "Bldgs Destr.", + "All Destr.", + "Credits", + "Time", + "# Bldgs Dstr.", + "# Units Dstr.", + "No Factories", + "Civ. Evac.", + "Built It" +}; + + +static const char * ActionText[TriggerClass::ACTION_COUNT + 1] = { + "None", + "Win", + "Lose", + "Production", + "Create Team", + "Dstry Teams", + "All to Hunt", + "Reinforce.", + "DZ at 'Z'", + "Airstrike", + "Nuclear Missile", + "Ion Cannon", + "Dstry Trig 'XXXX'", + "Dstry Trig 'YYYY'", + "Dstry Trig 'ZZZZ'", + "Autocreate", + "Cap=Win/Des=Lose", + "Allow Win" +}; + + +/*********************************************************************************************** + * TriggerClass::Validate -- validates trigger pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TriggerClass::Validate(void) const +{ + int num; + + num = Triggers.ID(this); + if (num < 0 || num >= TRIGGER_MAX) { + Validate_Error("TRIGGER"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * + * * + * This routine determines if the specified event must be attached to an object. Such * + * events can only exist in a parasitic fashion attached to object(s) in the game. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Does the specified event require attachement to an object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_Object(EventType event) +{ + switch (event) { + case EVENT_PLAYER_ENTERED: + case EVENT_DISCOVERED: + case EVENT_ATTACKED: + case EVENT_DESTROYED: + case EVENT_ANY: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * + * * + * This routine is used to determine if the specified event requires a house identifier. * + * All trigger events that affect a house will require a house identifier. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Does the specified event type require a house identifier? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_House(EventType event) +{ + switch (event) { + case EVENT_PLAYER_ENTERED: + case EVENT_HOUSE_DISCOVERED: + case EVENT_UNITS_DESTROYED: + case EVENT_BUILDINGS_DESTROYED: + case EVENT_ALL_DESTROYED: + case EVENT_CREDITS: + case EVENT_TIME: + case EVENT_NBUILDINGS_DESTROYED: + case EVENT_NUNITS_DESTROYED: + case EVENT_NOFACTORIES: + case EVENT_EVAC_CIVILIAN: + case EVENT_BUILD: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * + * * + * This routine will determine if the specified event requires a data number parameter. * + * This is commonly needed for trigger events. * + * * + * INPUT: event -- The event to examine. * + * * + * OUTPUT: Does the specified event require a data number parameter? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_Data(EventType event) +{ + switch (event) { + case EVENT_CREDITS: + case EVENT_TIME: + case EVENT_NBUILDINGS_DESTROYED: + case EVENT_NUNITS_DESTROYED: + case EVENT_BUILD: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * + * * + * This routine will determine if the specified action requires a team name parameter. * + * Typically, this is needed for reinforcements or other trigger events that affect * + * a particular team type. * + * * + * INPUT: action -- The action that is to be examined. * + * * + * OUTPUT: Does the specified action require a team type name? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Action_Need_Team(TriggerClass::ActionType action) +{ + switch (action) { + case ACTION_CREATE_TEAM: + case ACTION_DESTROY_TEAM: + case ACTION_REINFORCEMENTS: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::TriggerClass -- constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::TriggerClass(void) +{ + IsPersistant = VOLATILE; + AttachCount = 0; + Event = EVENT_NONE; + Action = ACTION_NONE; + House = HOUSE_NONE; + DataCopy = Data = 0L; + Name[0] = '\0'; + Team = NULL; +} + + +/*********************************************************************************************** + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * * + * This destructor will update the house blockage value if necessary. No other action need * + * be performed on trigger destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass::~TriggerClass(void) +{ + if (GameActive && House != HOUSE_NONE && Action == ACTION_ALLOWWIN) { + if (Houses.Ptr(House)->Blockage) Houses.Ptr(House)->Blockage--; + Houses.Ptr(House)->BorrowedTime = TICKS_PER_SECOND*4; + } +} + + +/*********************************************************************************************** + * TriggerClass::Init -- clears triggers for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Init(void) +{ + Triggers.Free_All(); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * Checks whether this trigger should "spring" for the given event & object; * + * If it should, then some really cool undocumented stuff magically happens. * + * * + * INPUT: * + * event EventType: What happened? * + * object Ptr to object containing this trigger: What did it happen to? * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + * 06/25/1995 JLB : Added more trigger events. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, ObjectClass *obj) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event && Event != EVENT_ANY) { + return(false); + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** Semi-persistant trigger: first detach it from the calling object, then + ** see if this is the last object we're attached to; if so, the trigger + ** will spring. + */ + if (IsPersistant == SEMIPERSISTANT) { + + /* + ** Detach ourselves from the object + */ + obj->Trigger = NULL; + + /* + ** Decrement our attachment counter + */ + AttachCount--; + + /* + ** If we're attached to more objects, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + if (AttachCount > 0) { + return(false); + } else { + IsPersistant = VOLATILE; + } + } + + /* + ** Otherwise, take an appropriate action. + */ + bool success = true; + TriggerClass * trig = NULL; + switch (Action) { + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + case ACTION_WINLOSE: + switch (event) { + case EVENT_DESTROYED: + if (!PlayerPtr->IsToWin || PlayerPtr->Blockage > 0) PlayerPtr->Flag_To_Lose(); + success = true; + break; + + case EVENT_PLAYER_ENTERED: + if (!PlayerPtr->IsToLose) PlayerPtr->Flag_To_Win(); + success = true; + break; + + default: + success = false; + break; + } + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + PlayerPtr->IsAirstrikePending = true; +// PlayerPtr->Make_Air_Strike_Available(true); + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_NONE: + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + HouseClass::As_Pointer(House)->Begin_Production(); + break; + + case ACTION_AUTOCREATE: + if (obj && obj->Is_Techno()) { + ((TechnoClass *)obj)->House->IsAlerted = true; + } + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * This version of Spring is for cell-based triggers. * + * * + * INPUT: * + * data elapsed time, or credits, depending on what 'Event' is. * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, CELL cell) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event) { + return(false); + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** Semi-persistant trigger: first detach it from the calling cell, then + ** see if this is the last cell we're attached to; if so, the trigger + ** will spring. + */ + if (IsPersistant == SEMIPERSISTANT) { + + /* + ** Detach ourselves from the cell + */ + Map[cell].IsTrigger = 0; + + /* + ** Decrement our attachment counter + */ + AttachCount--; + + /* + ** If we're attached to more cells, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + if (AttachCount > 0) { + return(false); + } else { + IsPersistant = VOLATILE; + } + } + + /* + ** Otherwise, take an appropriate action. + */ + bool success = true; + TriggerClass * trig = NULL; + int index; + switch (Action) { + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + case ACTION_AUTOCREATE: + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->IsAlerted = true; + } + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + HouseClass::As_Pointer(House)->AirStrike.Enable(false, true); + if (House == PlayerPtr->Class->House) { + PlayerPtr->AirStrike.Forced_Charge(true); + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } +// PlayerPtr->Make_Air_Strike_Available(true); + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_NONE: + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + if (PlayerPtr->Class->House == HOUSE_GOOD) { + HouseClass::As_Pointer(HOUSE_BAD)->Begin_Production(); + } else { + HouseClass::As_Pointer(HOUSE_GOOD)->Begin_Production(); + } + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * This version of Spring is for house-specific triggers. * + * For a time-based trigger, 'data' will the the current TickCount. * + * For a credit-based trigger, 'data' will be the credits for the HouseClass * + * containing this trigger. * + * * + * INPUT: * + * event the event that happened * + * house house that this event relates to * + * data elapsed time, or credits, depending on what 'Event' is. * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + * 06/25/1995 JLB : Added more trigger events. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, HousesType house, long data) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event || house != House) { + return(false); + } + + /* + ** If credits-based, check 'data' + */ + if (Event == EVENT_CREDITS && data < Data) { + return(false); + } + + /* + ** Building event check to ensure that the building number matches. + */ + if (Event == EVENT_BUILD && data != Data) { + return(false); + } + + /* + ** Number of objects destroyed checker. If the data supplied indicates that + ** the correct number of objects have been destroyed, then this trigger + ** will succeed. + */ + if (Event == EVENT_NBUILDINGS_DESTROYED || Event == EVENT_NUNITS_DESTROYED) { + if (data < Data) { + return(false); + } + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** The trigger has gone off; take appropriate action + */ + bool success = true; + TriggerClass * trig = NULL; + switch (Action) { + + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + /* + ** This will remove a blockage to the win condition. No action need + ** be performed here since the act of deleting the trigger will + ** remove the blockage. + */ + case ACTION_ALLOWWIN: + break; + + case ACTION_AUTOCREATE: + HouseClass::As_Pointer(House)->IsAlerted = true; + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + PlayerPtr->AirStrike.Enable(false, true); + if (House == PlayerPtr->Class->House) { + PlayerPtr->AirStrike.Forced_Charge(true); + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + break; + + case ACTION_NONE: + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + HouseClass::As_Pointer(House)->Begin_Production(); + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Remove -- removes this trigger from the game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = trigger was removed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + *=============================================================================================*/ +bool TriggerClass::Remove(void) +{ + Validate(); + CELL cell; + HousesType h; + int index; + + /* + ** Loop through all cells; remove any reference to this trigger + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].IsTrigger) { + if (CellTriggers[cell] == this) { + Map[cell].IsTrigger = 0; + CellTriggers[cell] = NULL; + } + } + } + + /* + ** Loop through all objects, removing any reference to this trigger + */ + for (index = 0; index < Infantry.Count(); index++) { + if (Infantry.Ptr(index)->Trigger == this) { + Infantry.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Buildings.Count(); index++) { + if (Buildings.Ptr(index)->Trigger == this) { + Buildings.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Units.Count(); index++) { + if (Units.Ptr(index)->Trigger == this) { + Units.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Terrains.Count(); index++) { + if (Terrains.Ptr(index)->Trigger == this) { + Terrains.Ptr(index)->Trigger = NULL; + } + } + + /* + ** Remove this trigger from any house list it's in. Invoking '-=' with a + ** pointer not in the list has no effect; loop through all houses just to + ** be on the safe side. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseTriggers[h].Delete(this); + } + + delete this; + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Read_INI -- reads triggers from the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * This routine reads in the triggers & creates them. Then, other classes can * + * get pointers to the triggers they're linked to. * + * * + * The routine relies on the TeamTypeClasses already being loaded so it can resolve * + * references to teams in this function. * + * * + * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * + * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * + * * + * Object's pointers are set in: * + * InfantryClass::Read_INI() * + * BuildingClass::Read_INI() * + * UnitClass::Read_INI() * + * TerrainClass::Read_INI() * + * The object trigger pointers are cleared in the ObjectClass constructor. * + * * + * The House's EMSListOf triggers is set in this routine, and cleared in the * + * HouseClass::Init() routine. * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This function must be called before any other class's Read_INI. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Read_INI(char *buffer) +{ + TriggerClass *trigger; // Working trigger pointer. + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + char buf[128]; + + /* + ** Set 'tbuffer' to point just past the INI buffer + */ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /* + ** Read all TRIGGER entry names into 'tbuffer' + */ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ** Loop for all trigger entries. + */ + while (*tbuffer != '\0') { + + /* + ** Create a new trigger. + */ + trigger = new TriggerClass(); + + /* + ** Set its name. + */ + trigger->Set_Name (tbuffer); + + /* + ** Get the trigger entry. + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** Fill in the trigger. + */ + trigger->Fill_In(tbuffer,buf); + + /* + ** Add 'trigger' to the House's list. + */ +// if (trigger->House != HOUSE_NONE && trigger->Event != EVENT_PLAYER_ENTERED) { +// if (Event_Need_House(trigger->Event) && !Event_Need_Object(trigger->Event)) { + if (trigger->House != HOUSE_NONE) { + if (trigger->Action == ACTION_ALLOWWIN) HouseClass::As_Pointer(trigger->House)->Blockage++; + HouseTriggers[trigger->House].Add(trigger); + trigger->AttachCount++; + } + + /* + ** Go to next entry. + */ + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TriggerClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given trigger with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Fill_In(char * name, char *entry) +{ + Validate(); + char *p; + + /* + ** Set its name. + */ + Set_Name(name); + + /* + ** 1st token: Event. + */ + Event = Event_From_Name(strtok(entry, ",")); + + /* + ** 2nd token: Action. + */ + Action = Action_From_Name(strtok(NULL, ",")); + + /* + ** 3rd token: Data. + */ + DataCopy = Data = atol(strtok(NULL, ",")); + + /* + ** 4th token: House. + */ + House = HouseTypeClass::From_Name(strtok(NULL, ",")); + if (House == HOUSE_NONE && Event == EVENT_PLAYER_ENTERED) { + House = PlayerPtr->Class->House; + } + + /* + ** 5th token: Team. + */ + Team = TeamTypeClass::As_Pointer(strtok(NULL, ",")); + + /* + ** 6th token: IsPersistant. This token was added later, so we must check + ** for its existence. + */ + p = strtok(NULL, ","); + if (p) { + IsPersistant = (PersistantType)atoi(p); + } else { + IsPersistant = VOLATILE; + } +} + + +/*********************************************************************************************** + * TriggerClass::Write_INI -- writes triggers to the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Write_INI(char *buffer, bool refresh) +{ + int index; + char buf[128]; + TriggerClass *trigger; + char const *hname; + char const *tname; + + /* + ** First, clear out all existing trigger data from the INI file. + */ + if (refresh) { + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + } + + /* + ** Now write all the trigger data out + */ + for (index = 0; index < Triggers.Count(); index++) { + + /* + ** Get ptr to next active trigger. + */ + trigger = Triggers.Ptr(index); + + /* + ** Generate INI entry. + */ + if (trigger->House==HOUSE_NONE) { + hname = "None"; + } else { + hname = HouseClass::As_Pointer(trigger->House)->Class->IniName; + } + + if (trigger->Team==NULL) { + tname = "None"; + } else { + tname = trigger->Team->IniName; + } + + sprintf(buf,"%s,%s,%ld,%s,%s,%d", + TriggerClass::Name_From_Event(trigger->Event), + TriggerClass::Name_From_Action(trigger->Action), + trigger->Data, + hname, + tname, + trigger->IsPersistant); + WWWritePrivateProfileString(INI_Name(), trigger->Get_Name(), buf, buffer); + } +} + + +/*********************************************************************************************** + * TriggerClass::As_Pointer -- returns pointer for the given trigger name * + * * + * This function is designed for use at initialization time, ie when the * + * trigger mnemonics are read from the INI and the Read_INI functions need * + * to get a pointer to that trigger. * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * * + * OUTPUT: * + * near pointer to that trigger, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass * TriggerClass::As_Pointer(char const * name) +{ + if (name == NULL) { + return(NULL); + } + + for (int i = 0; i < Triggers.Count(); i++) { + TriggerClass * trigger = Triggers.Ptr(i); + + if (!stricmp(name, trigger->Name)) { + return(trigger); + } + } + + return(NULL); +} + + +/*********************************************************************************************** + * TriggerClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new trigger * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void * TriggerClass::operator new(size_t ) +{ + void * ptr = Triggers.Allocate(); + if (ptr) { + ((TriggerClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TriggerClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::operator delete(void *ptr) +{ + if (ptr) { + ((TriggerClass *)ptr)->IsActive = false; + } + Triggers.Free((TriggerClass *)ptr); +} + + +/*********************************************************************************************** + * TriggerClass::Event_From_Name -- retrieves EventType for given name * + * * + * INPUT: * + * name name to get event for * + * * + * OUTPUT: * + * EventType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +EventType TriggerClass::Event_From_Name (char const *name) +{ + int i; + + if (name == NULL) { + return(EVENT_NONE); + } + + for (i = EVENT_NONE; i < EVENT_COUNT; i++) { + if (!stricmp(name,EventText[i + 1])) { + return((EventType)i); + } + } + + return(EVENT_NONE); +} + + +/*********************************************************************************************** + * TriggerClass::Name_From_Event -- retrieves name for EventType * + * * + * INPUT: * + * event EventType to get name for * + * * + * OUTPUT: * + * name for EventType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const *TriggerClass::Name_From_Event(EventType event) +{ + return(EventText[event + 1]); +} + + +/*********************************************************************************************** + * TriggerClass::Action_From_Name -- retrieves ActionType for given name * + * * + * INPUT: * + * name name to get ActionType for * + * * + * OUTPUT: * + * ActionType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::ActionType TriggerClass::Action_From_Name (char const *name) +{ + int i; + + if (name == NULL) { + return(ACTION_NONE); + } + + for (i = ACTION_NONE; i < ACTION_COUNT; i++) { + if (!stricmp(name,ActionText[i + 1])) { + return((ActionType)i); + } + } + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TriggerClass::Name_From_Action -- retrieves name for ActionType * + * * + * INPUT: * + * action ActionType to get name for * + * * + * OUTPUT: * + * name of ActionType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const *TriggerClass::Name_From_Action(ActionType action) +{ + return(ActionText[action + 1]); +} + + +/*********************************************************************************************** + * TriggerClass::As_Target -- Converts trigger to a target value * + * * + * INPUT: none * + * * + * OUTPUT: TARGET value * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TRIGGER, Triggers.ID(this))); +} + + +/*********************************************************************************************** + * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * + * * + * This trigger action will cause the computer units and infantry to go into hunt mode. * + * Use it to bring a scenario to a sudden conclusion. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/20/1995 JLB : Created. * + * 08/14/1995 JLB : Removes the member from a team if necessary. * + *=============================================================================================*/ +static void Do_All_To_Hunt(void) +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (!unit->House->IsHuman && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (!infantry->House->IsHuman && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } +} diff --git a/TIBERIANDAWN/TRIGGER.H b/TIBERIANDAWN/TRIGGER.H new file mode 100644 index 000000000..fc0ab3df9 --- /dev/null +++ b/TIBERIANDAWN/TRIGGER.H @@ -0,0 +1,261 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\trigger.h_v 2.15 16 Oct 1995 16:46:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : November 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TRIGGER_H +#define TRIGGER_H + +typedef enum EventType : char { + EVENT_NONE=-1, + + /* + .......................... Cell-specific events .......................... + */ + EVENT_PLAYER_ENTERED, // player enters this square + EVENT_CELLFIRST = EVENT_PLAYER_ENTERED, + + /* + ......................... Object-specific events ......................... + */ + EVENT_DISCOVERED, // player discovers this object + EVENT_OBJECTFIRST = EVENT_DISCOVERED, + EVENT_ATTACKED, // player attacks this object + EVENT_DESTROYED, // player destroys this object + EVENT_ANY, // Any object event will cause the trigger. + + /* + ......................... House-specific events .......................... + */ + EVENT_HOUSE_DISCOVERED, // any object in this house discovered + EVENT_HOUSEFIRST = EVENT_HOUSE_DISCOVERED, + EVENT_UNITS_DESTROYED, // all house's units destroyed + EVENT_BUILDINGS_DESTROYED, // all house's buildings destroyed + EVENT_ALL_DESTROYED, // all house's units & buildings destroyed + EVENT_CREDITS, // house reaches this many credits + EVENT_TIME, // time elapses for this house + EVENT_NBUILDINGS_DESTROYED, // Number of buildings destroyed. + EVENT_NUNITS_DESTROYED, // Number of units destroyed. + EVENT_NOFACTORIES, // No factories left. + EVENT_EVAC_CIVILIAN, // Civilian has been evacuated. + EVENT_BUILD, // If specified building has been built. + + EVENT_COUNT, + EVENT_FIRST=0 +} EventType; + + +class TriggerClass { + public: + typedef enum ActionType { + ACTION_NONE=-1, + + ACTION_WIN, // player wins! + ACTION_LOSE, // player loses. + ACTION_BEGIN_PRODUCTION, // computer begins production + ACTION_CREATE_TEAM, // computer creates a certain type of team + ACTION_DESTROY_TEAM, + ACTION_ALL_HUNT, // all enemy units go into hunt mode (teams destroyed). + ACTION_REINFORCEMENTS, // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + ACTION_DZ, // Deploy drop zone smoke. + ACTION_AIRSTRIKE, // Enable airstrike. + ACTION_NUKE, // Enable nuke for computer. + ACTION_ION, // Give ion cannon to computer. + ACTION_DESTROY_XXXX, // Destroy trigger XXXX. + ACTION_DESTROY_YYYY, // Destroy trigger YYYY. + ACTION_DESTROY_ZZZZ, // Destroy trigger ZZZZ. + ACTION_AUTOCREATE, // Computer to autocreat teams. + ACTION_WINLOSE, // Win if captured, lose if destroyed. + ACTION_ALLOWWIN, // Allows winning if triggered. + + ACTION_COUNT, + ACTION_FIRST=0 + } ActionType; + + typedef enum PersistantType { + VOLATILE = 0, + SEMIPERSISTANT = 1, + PERSISTANT = 2, + } PersistantType; + + /* + ** Functions: + ** + ** Constructor/Destructor + */ + TriggerClass(void); + ~TriggerClass(void); + + /* + ** Initialization: clears all triggers in preparation for new scenario + */ + static void Init(void); + + /* + ** Processing routines + */ + bool Spring(EventType event, ObjectClass * object); // object-based + bool Spring(EventType event, CELL cell); // cell-based + bool Spring(EventType event, HousesType house, long data=0); // house-based + bool Remove(void); + + /* + ** File I/O routines + */ + static void Read_INI (char *buffer); + void Fill_In(char *name, char *entry); + static void Write_INI (char *buffer, bool refresh); + static char * INI_Name(void) {return "Triggers";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give the mnemonic + */ + static TriggerClass * As_Pointer(char const * name); + + /* + ** Data Access routines + */ +// EventType Get_Event(void) const {return (Event);} +// void Set_Event(EventType event) {Event = event;} +// ActionType Get_Action(void) const {return (Action);} +// void Set_Action(ActionType action) {Action = action;} +// HousesType Get_House(void) const {return(House);} +// void Set_House(HousesType house) {House = house;} +// long Get_Data(void) const {return(Data);} +// void Set_Data(long credits) {Data = credits;} + char const * Get_Name(void) const {return (Name);} + void Set_Name(char const *buf) {strncpy(Name, buf, sizeof(Name)); Name[sizeof(Name)-1] = '\0';} + + /* + ** Utility routines + */ + TARGET As_Target(void) const; + static bool Event_Need_Object(EventType event); + static bool Event_Need_House(EventType event); + static bool Event_Need_Data(EventType event); + static bool Action_Need_Team(ActionType action); + static EventType Event_From_Name(char const *name); + static char const *Name_From_Event(EventType event); + static ActionType Action_From_Name(char const *name); + static char const *Name_From_Action(ActionType action); + + /* + ** Overloaded operators + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** This is the pointer to the team that gets created or destroyed when + ** a team-related trigger goes off, or for reinforcements. The house + ** for reinforcements is determined by the house for that team. + */ + TeamTypeClass *Team; + + /* + ** If this trigger object is active, then this flag will be true. Trigger + ** objects that are not active are either not yet created or have been + ** deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** This flag controls whether the trigger destroys itself after it goes + ** off. + ** 0 = trigger destroys itself immediately after going off, and removes + ** itself from all objects it's attached to + ** 1 = trigger is "Semi-Persistent"; it maintains a count of all objects + ** it's attached to, and only actually "springs" after its been + ** triggered from all the objects; then, it removes itself. + ** 2 = trigger is Fully Persistent; it just won't go away. + */ + PersistantType IsPersistant; + + /* + ** This value tells how many objects or cells this trigger is attached + ** to. The Read_INI routine for all classes that point to a trigger must + ** increment this value! + */ + int AttachCount; + + /* + ** Each trigger must have an event which activates it. This is the event that is + ** used to activate this trigger. + */ + EventType Event; + + /* + ** This is the action to perform when the trigger event occurs. + */ + ActionType Action; + + /* + ** For house-specific events, this is the house for that event. + */ + HousesType House; + + /* + ** For credit-related triggers, this is the number of credits that + ** generate the trigger. For time-based triggers, this is the number + ** of minutes that must elapse. + */ + long Data; + long DataCopy; + + private: + + /* + ** Triggers can be referred to by their name, which can be up to 4 + ** characters. + */ + char Name[5]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + +}; + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/TURRET.CPP b/TIBERIANDAWN/TURRET.CPP new file mode 100644 index 000000000..409fe1e9c --- /dev/null +++ b/TIBERIANDAWN/TURRET.CPP @@ -0,0 +1,508 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\turret.cpv 2.18 16 Oct 1995 16:50:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * TurretClass::Unlimbo -- Unlimboes turret object. * + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "turret.h" + + +/*********************************************************************************************** + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * * + * This is the default destructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::~TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * * + * This is the default constructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * * + * This is the normal constructor for the turret class. It merely sets the turret up to * + * face north. * + * * + * INPUT: classid -- The type id for this particular unit. * + * * + * house -- The house that this unit will belong to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(UnitType classid, HousesType house) : + DriveClass(classid, house) +{ + Reload = 0; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * * + * This routine is used to display the current values of this turret * + * class instance. It is primarily used in the debug output screen. * + * * + * INPUT: x,y -- Monochrome screen coordinates to display data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +void TurretClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(36, 3);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * * + * This virtual routine is used to determine if the vehicle is allowed * + * to start moving. It is typically called when the vehicle desires * + * to move but needs confirmation from the turret logic before * + * proceeding. This happens when dealing with a vehicle that must have * + * its turret face the same direction as the body before the vehicle * + * may begin movement. * + * * + * INPUT: dir -- The facing the unit wants to travel in. * + * * + * OUTPUT: bool; Can the unit begin forward movement now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +bool TurretClass::Ok_To_Move(DirType dir) +{ + if (Class->IsLockTurret) { + if (IsRotating) { + return(false); + } else { + if (SecondaryFacing.Difference(dir)) { + SecondaryFacing.Set_Desired(dir); + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * * + * This processes the reloading of the turret. It does this by decrementing the arming * + * countdown timer and when it reaches zero, the turret may fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +void TurretClass::AI(void) +{ + DriveClass::AI(); + + /* + ** A unit with a constant rotating radar dish is handled here. + */ + if (Class->IsRadarEquipped) { + SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8)); + Mark(MARK_CHANGE); + } else { + + IsRotating = false; + if (Class->IsTurretEquipped) { + if (IsTurretLockedDown) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } + + if (SecondaryFacing.Is_Rotating()) { + if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) { + Mark(MARK_CHANGE); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + IsRotating = SecondaryFacing.Is_Rotating(); + } else { + if (!IsTurretLockedDown && !Target_Legal(TarCom)) { + if (!Target_Legal(NavCom)) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); + } + } + } + } + } +} + + +/*********************************************************************************************** + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the turret. It will check * + * to see if firing is technically legal given the specified target. * + * If it is legal to fire, it does so. It is safe to call this routine * + * every game tick. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * TurretClass::Fire_At(TARGET target, int which) +{ + BulletClass * bullet = NULL; + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + if (Can_Fire(target, which) == FIRE_OK) { + bullet = DriveClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Possible reload timer set. + */ + if (*this == UNIT_MSAM && Reload == 0) { + Reload = TICKS_PER_SECOND * 30; + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * * + * This routine determines if the turret can fire upon the target * + * specified. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use to determine legality to fire. 0=primary, * + * 1=secondary. * + * * + * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + * 06/01/1994 JLB : Returns reason why it can't fire. * + *=============================================================================================*/ +FireErrorType TurretClass::Can_Fire(TARGET target, int which) const +{ + DirType dir; // The facing to impart upon the projectile. + int diff; + FireErrorType fire = DriveClass::Can_Fire(target, which); + + if (fire == FIRE_OK) { + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((!Class->IsTurretEquipped || Class->IsLockTurret) && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && !BulletTypeClass::As_Reference(weapon->Fires).IsHoming) { + return(FIRE_ROTATING); + } + + dir = Direction(target); + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + /* + ** Special flame tank logic. + */ + if (weapon->Fires == BULLET_FLAME) { + if (Dir_Facing(dir) == Dir_Facing(PrimaryFacing)) { + diff = 0; + } + } + + if (BulletTypeClass::As_Reference(weapon->Fires).IsHoming) { + diff >>= 2; + } + if (diff < 8) { + return(DriveClass::Can_Fire(target, which)); + } + return(FIRE_FACING); + } + return(fire); +} + + +/*********************************************************************************************** + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * * + * Use this routine to determine the exact coordinate that a projectile would appear if it * + * were fired from this unit. For units with turrets, typically, this would be at the end * + * of the barrel. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with coordinate of where a projectile should appear if this unit were * + * to fire one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/28/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE TurretClass::Fire_Coord(int which) const +{ + COORDINATE coord = Center_Coord(); + int dist = 0; + int lateral = 0; + DirType dir = PrimaryFacing.Current(); + + if (Class->IsTurretEquipped) { + dir = SecondaryFacing.Current(); + } + + switch (Class->Type) { + case UNIT_GUNBOAT: + coord = Coord_Move(coord, PrimaryFacing.Current(), Pixel2Lepton[Class->TurretOffset]); + dist = 0x0060; + break; + + case UNIT_ARTY: + coord = Coord_Move(coord, DIR_N, 0x0040); + dist = 0x0060; + break; + + case UNIT_FTANK: + dist = 0x30; + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), 0x20); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), 0x20); + } + break; + + case UNIT_HTANK: + coord = Coord_Move(coord, DIR_N, 0x0040); + if (which == 0) { + dist = 0x00C0; + lateral = 0x0028; + } else { + dist = 0x0008; + lateral = 0x0040; + } + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); + } + break; + + case UNIT_LTANK: + coord = Coord_Move(coord, DIR_N, 0x0020); + dist = 0x00C0; + break; + + case UNIT_MTANK: + coord = Coord_Move(coord, DIR_N, 0x0030); + dist = 0x00C0; + break; + + case UNIT_APC: + case UNIT_JEEP: + case UNIT_BUGGY: + coord = Coord_Move(coord, DIR_N, 0x0030); + dist = 0x0030; + break; + +#ifdef PETROGLYPH_EXAMPLE_MOD + case UNIT_NUKE_TANK: + coord = Coord_Move(coord, DIR_N, 0x00A0); + dist = 0x00A0; + break; +#endif //PETROGLYPH_EXAMPLE_MOD + } + + if (dist) { + coord = Coord_Move(coord, dir, dist); + } + + return(coord); +} + + +/*********************************************************************************************** + * TurretClass::Unlimbo -- Unlimboes turret object. * + * * + * This routine is called when a turret equipped unit unlimboes. It sets the turret to * + * face the same direction as the body. * + * * + * INPUT: coord -- The coordinate where the unit is unlimboing. * + * * + * dir -- The desired body and turret facing to use. * + * * + * OUTPUT: Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool TurretClass::Unlimbo(COORDINATE coord, DirType dir) +{ + if (DriveClass::Unlimbo(coord, dir)) { + SecondaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * * + * This routine will return with the facing that a projectile will travel if it was * + * fired at this instant. The facing should match the turret facing for those units * + * equipped with a turret. If the unit doesn't have a turret, then it will be the facing * + * of the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for a projectile. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +DirType TurretClass::Fire_Direction(void) const +{ + if (Class->IsTurretEquipped) { + if (*this == UNIT_MSAM) { + int diff1 = SecondaryFacing.Difference(DIR_E); + int diff2 = SecondaryFacing.Difference(DIR_W); + diff1 = ABS(diff1); + diff2 = ABS(diff2); + int diff = MIN(diff1, diff2); + int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff); + if (SecondaryFacing.Difference(DIR_N) < 0) { + return(DirType)(SecondaryFacing - (DirType)adj); + } else { + return(DirType)(SecondaryFacing + (DirType)adj); + } + } + return(SecondaryFacing.Current()); + } + + return(PrimaryFacing.Current()); +} \ No newline at end of file diff --git a/TIBERIANDAWN/TURRET.H b/TIBERIANDAWN/TURRET.H new file mode 100644 index 000000000..35b478eed --- /dev/null +++ b/TIBERIANDAWN/TURRET.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\turret.h_v 2.17 16 Oct 1995 16:48:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TURRET_H +#define TURRET_H + +#include "drive.h" + +class TurretClass : public DriveClass +{ + public: + + /* + ** This is the timer that controls the reload rate. The MSAM rocket + ** launcher is the primary user of this. + */ + TCountDownTimerClass Reload; + + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + TurretClass(UnitType classid, HousesType house); + TurretClass(void); + virtual ~TurretClass(void); + + BulletClass * Fire_At(TARGET target, int which); + + virtual DirType Fire_Direction(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual bool Ok_To_Move(DirType facing); + virtual void AI(void); + virtual COORDINATE Fire_Coord(int which) const; +}; + + +#endif diff --git a/TIBERIANDAWN/TXTLABEL.CPP b/TIBERIANDAWN/TXTLABEL.CPP new file mode 100644 index 000000000..b8885e944 --- /dev/null +++ b/TIBERIANDAWN/TXTLABEL.CPP @@ -0,0 +1,95 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\txtlabel.cpv 1.9 16 Oct 1995 16:49:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TextLabelClass -- Constructor * + * * + * INPUT: * + * txt pointer to text buffer to print from * + * x x-coord for text printing * + * y y-coord for text printing * + * color color to print in * + * style style to print (determines the meaning of x & y) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +TextLabelClass::TextLabelClass(char *txt, int x, int y, int color, + TextPrintType style) : GadgetClass(x,y,1,1,0,0) +{ + Text = txt; + Color = color; + Style = style; + UserData = 0; + PixWidth = -1; + Segments = 0; +} + + +/*********************************************************************************************** + * Draw_Me -- Graphical update routine * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state * + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int TextLabelClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + if (PixWidth == -1) { + Fancy_Text_Print("%s", X, Y, Color, TBLACK, Style, Text); + } else { + Conquer_Clip_Text_Print(Text, X, Y, Color, TBLACK, Style, PixWidth); + } + return(true); + } + return(false); +} diff --git a/TIBERIANDAWN/TXTLABEL.H b/TIBERIANDAWN/TXTLABEL.H new file mode 100644 index 000000000..d5b014c16 --- /dev/null +++ b/TIBERIANDAWN/TXTLABEL.H @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\txtlabel.h_v 1.14 16 Oct 1995 16:46:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TXTLABEL_H +#define TXTLABEL_H + +class TextLabelClass : public GadgetClass +{ + public: + /* + ** Constructor/Destructor + */ + TextLabelClass(char *txt, int x, int y, int color, TextPrintType style); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + /* + ** Sets the displayed text of the label + */ + virtual void Set_Text(char *txt) {Text = txt;}; + + /* + ** General-purpose data field + */ + unsigned long UserData; + TextPrintType Style; + char *Text; + int Color; + int PixWidth; + char Segments; + unsigned short CRC; +}; + +#endif + diff --git a/TIBERIANDAWN/TXTPRNT.ASM b/TIBERIANDAWN/TXTPRNT.ASM new file mode 100644 index 000000000..ae4fffcd1 --- /dev/null +++ b/TIBERIANDAWN/TXTPRNT.ASM @@ -0,0 +1,520 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Print -- Assembly text print to a buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT +.MODEL FLAT + + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +externdef C Buffer_Print : NEAR + +GraphicViewPort STRUCT +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch DD ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ENDS + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +extern C FontPtr:DWORD +extern C FontXSpacing:DWORD +extern C FontYSpacing:DWORD +externdef C ColorXlat:byte + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +;LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + ;DATASEG + .data + +ColorXlat DB 00H,01H,02H,03H,04H,05H,06H,07H + DB 08H,09H,0AH,0BH,0CH,0DH,0EH,0FH + + DB 01H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 02H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 03H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 04H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 05H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 06H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 07H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 08H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 09H,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0AH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0BH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0CH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0DH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0EH,00H,00H,00H,00H,00H,00H,00H + DB 00H,00H,00H,00H,00H,00H,00H,00H + + DB 0FH + + ;CODESEG + .code + + + +;*************************************************************************** +;* Buffer_Print -- Assembly text print to graphic buffer routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* +Buffer_Print proc C public USES ebx ecx edx esi edi this_object:DWORD, string:DWORD, x_pixel:DWORD, y_pixel:DWORD, fcolor:DWORD, bcolor:DWORD + + ;PROC Buffer_Print C near + ;USES ebx,ecx,edx,esi,edi + + ;ARG this_object:DWORD + ;ARG string:DWORD + ;ARG x_pixel:DWORD + ;ARG y_pixel:DWORD + ;ARG fcolor:DWORD + ;ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this_object] ; get a pointer to dest + mov eax,[ebx].GraphicViewPort.GVPHeight ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[ebx].GraphicViewPort.GVPWidth ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[ebx].GraphicViewPort.GVPXAdd ; add in xadd for bytes_per_line + add eax,[ebx].GraphicViewPort.GVPPitch ; add in pitch for direct daraw + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[ebx].GraphicViewPort.GVPOffset ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,WORD PTR [esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,WORD PTR [esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,WORD PTR [esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,WORD PTR [esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,BYTE PTR [ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov byte ptr [ColorXlat+1],al + mov byte ptr [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov byte ptr [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,BYTE PTR [eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,WORD PTR [esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat ;[ebx] ; get translated color into al + test al,al ; is it transparent black + jnz loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz draw_char ; we are done here, go draw the character. + jmp short loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz next_char ; if no data, go on to next character. + +while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat ;[ebx] ; get new color + + test al,al ; is it a transparent? + jz short skiplo ; skip over write + mov [edi],al ; write it out +skiplo: + inc edi + dec dh ; decrement our width. + jz short nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat ;[ebx] ; get new color + + test al,al ; is it a transparent? + jz short skiphi ; skip over write + mov [edi],al ; write it out +skiphi: + + inc edi + dec dh ; decrement our width. + jnz short while_data ; check if done with width of char + +nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat ;[ebx] ; get translated color into al + test al,al ; is it transparent black + jz next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz next_char ; we are done here, go to the next character. + jmp short loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je lfeed + mov eax,[original_x] +lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp next_char + +overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + +Buffer_Print endp + + + +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + +Get_Font_Palette_Ptr proc C public + + ;GLOBAL C Get_Font_Palette_Ptr:NEAR + + ;PROC Get_Font_Palette_Ptr C near + + mov eax, OFFSET ColorXlat + ret + +Get_Font_Palette_Ptr endp + + +END \ No newline at end of file diff --git a/TIBERIANDAWN/TYPE.H b/TIBERIANDAWN/TYPE.H new file mode 100644 index 000000000..e90b45457 --- /dev/null +++ b/TIBERIANDAWN/TYPE.H @@ -0,0 +1,2010 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\type.h_v 2.20 16 Oct 1995 16:45:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TYPE_H +#define TYPE_H + +#include "mission.h" +#include "target.h" + +class MapEditClass; +class HouseClass; + + +/********************************************************************** +** This is the constant data associated with a weapon. Some objects +** can have multiple weapons and this class is used to isolate and +** specify this data in a convenient and selfcontained way. +*/ +class WeaponTypeClass +{ + public: + + /* + ** This is the unit class of the projectile fired. A subset of the unit types + ** represent projectiles. It is one of these classes that is specified here. + ** If this object does not fire anything, then this value will be UNIT_NONE. + */ + BulletType Fires; + + /* + ** This is the damage (explosive load) to be assigned to the projectile that + ** this object fires. + */ + unsigned char Attack; + + /* + ** Objects that fire (which can be buildings as well) will fire at a + ** frequency controlled by this value. This value serves as a count + ** down timer between shots. The smaller the value, the faster the + ** rate of fire. + */ + unsigned char ROF; + + /* + ** When this object fires, the range at which it's projectiles travel is + ** controlled by this value. The value represents the number of cells the + ** projectile will travel. Objects outside of this range will not be fired + ** upon (in normal circumstances). + */ + int Range; + + /* + ** This is the typical sound generated when firing. + */ + VocType Sound; + + /* + ** This is the animation to display at the firing coordinate. + */ + AnimType Anim; +}; + + +/********************************************************************** +** Each of the warhead types has specific characteristics. This structure +** holds these characteristics. +*/ +class WarheadTypeClass +{ + public: + + /* + ** This value control how damage from this warhead type will reduce + ** over distance. The larger the number, the less the damage is reduced + ** the farther the distance from the source of the damage. + */ + int SpreadFactor; + + /* + ** If this warhead type can destroy walls, then this flag will be true. + */ + bool IsWallDestroyer; + + /* + ** If this warhead can destroy wooden walls, then this flag will be true. + */ + bool IsWoodDestroyer; + + /* + ** Does this warhead damage tiberium? + */ + bool IsTiberiumDestroyer; + + /* + ** The warhead damage is reduced depending on the the type of armor the + ** defender has. This table is what gives weapons their "character". + */ + unsigned Modifier[ARMOR_COUNT]; +}; + + +/********************************************************************** +** Each house has certain unalienable characteristics. This structure +** elaborates these. +*/ +class HouseTypeClass { + public: + + /* + ** This is the house number (enum). This is a unique identification + ** number for the house. + */ + HousesType House; + + /* + ** The INI name of the house is pointed to by this element. This is the + ** identification name used in the scenario INI file. + */ + char const *IniName; + + /* + ** The full name (translated) of the house is identified by this number. + ** The actual text of the name is located in a text file loaded at run + ** time. + */ + int FullName; + + /* + ** This is the filename suffix to use when creating a house specific + ** file name. It is three characters long. + */ + char Suffix[4]; + + /* + ** This is the "lemon percentage" to use when determining if a particular + ** object owned by this house is to be flagged as a "lemon". Objects so + ** flagged have a greater break-down chance. The percentage is expressed + ** as a fixed point number with 0x000 meaning 0% and 0x100 meaning 100%. + */ + unsigned Lemon; + + /* + ** Each house is assigned a unique identification color to be used on the + ** radar map and other color significant areas. + */ + unsigned char Color; + + unsigned char BrightColor; + + /* + ** This points to the default remap table for this house. + */ + unsigned char const * RemapTable; + PlayerColorType RemapColor; + + /* + ** This is a unique ASCII character used when constructing filenames. It + ** serves a similar purpose as the "Suffix" element, but is only one + ** character long. + */ + char Prefix; + + /* + ** This controls the various general adjustments to the house owned + ** unit and building ratings. The default value for these ratings is 1.0. + */ + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + + //------------------------------------------------------------------------ + HouseTypeClass(HousesType house, + char const *ini, + int fullname, + char const *ext, + int lemon, + int color, + int bright_color, + PlayerColorType remapcolor, + unsigned char const * remap, + char prefix); + + static HousesType From_Name(char const *name); + static HouseTypeClass const & As_Reference(HousesType house); + static void One_Time(void); + + private: + static HouseTypeClass const * const Pointers[HOUSE_COUNT]; +}; + + +/*************************************************************************** +** This is the abstract type class. It holds information common to all +** objects that might exist. This contains the name of +** the object type. +*/ +class AbstractTypeClass +{ + public: + + /* + ** This is the internal control name of the object. This name does + ** not change regardless of language specified. This is the name + ** used in scenario control files and for other text based unique + ** identification purposes. + */ + char IniName[9]; + + /* + ** The translated (language specific) text name number of this object. + ** This number is used to fetch the object's name from the language + ** text file. Whenever the name of the object needs to be displayed, + ** this is used to determine the text string. + */ + int Name; + + AbstractTypeClass(void) {}; + AbstractTypeClass(int name, char const * ini); + virtual RTTIType What_Am_I(void) const; + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Full_Name(void) const; + void Set_Name(char const *buf) const + { + strncpy((char *)IniName, buf, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; + }; + virtual unsigned short Get_Ownable(void) const; +}; + + +/*************************************************************************** +** This the the common base class of game objects. Since these values +** represent the unchanging object TYPES, this data is initialized at game +** start and not changed during play. It is "const" data. +*/ +class ObjectTypeClass : public AbstractTypeClass +{ + public: + + /* + ** Is this object squashable by heavy vehicles? If it is, then the vehicle + ** can travel over this object and destroy it in the process. + */ + unsigned IsCrushable:1; + + /* + ** Does this object type NOT show up on radar scans? If true, then in any + ** radar display, only the underlying ground will be show, not this object. + ** Most terrain falls into this category, but only a few special real units/buildings + ** do. + */ + unsigned IsStealthy:1; + + /* + ** It is legal to "select" some objects in the game. If it is legal to select this + ** object type then this flag will be true. Selected game objects typically display + ** a floating health bar and allows special user I/O control. + */ + unsigned IsSelectable:1; + + /* + ** Can this object be the target of an attack or move command? Typically, only objects + ** that take damage or can be destroyed are allowed to be a target. + */ + unsigned IsLegalTarget:1; + + /* + ** "Insignificant" objects will not be announced when they are destroyed or when they + ** appear. Terrain elements and some lesser vehicles have this characteristic. + */ + unsigned IsInsignificant:1; + + /* + ** Is this object immune to normal combat damage? Rocks and other inert type terrain + ** object are typically of this type. + */ + unsigned IsImmune:1; + + /* + ** If this terrain object is flammable (such as trees are) then this + ** flag will be true. Flammable objects can catch fire if damaged by + ** flame type weapons. + */ + unsigned IsFlammable:1; + + /* + ** "Sentient" objects are ones that have logic AI processing performed on them. All + ** vehicles, buildings, infantry, and aircraft are so flagged. Terrain elements also + ** fall under this category, but only because certain animation effects require this. + */ + unsigned IsSentient:1; + + /* + ** The defense of this object is greatly affected by the type of armor + ** it possesses. This value specifies the type of armor. + */ + ArmorType Armor; + + /* + ** This is the maximum strength of this object type. + */ + unsigned short MaxStrength; + + /* + ** These point to the shape imagery for this object type. Since the shape imagery + ** exists in a separate file, the data is filled in after this object is constructed. + ** The "mutable" keyword allows easy modification to this otherwise const object. + */ + void const * ImageData; + + /* + ** This points to the radar imagery for this object. + */ + void const * RadarIcon; + + //-------------------------------------------------------------------- + ObjectTypeClass( bool is_sentient, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + int fullname, + char const *name, + ArmorType armor, + unsigned short strength); + + static void One_Time(void); + + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const = 0; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool, bool, HousesType) const; + virtual void const * Get_Cameo_Data(void) const; + void const * Get_Image_Data(void) const {return ImageData;}; + void const * Get_Radar_Data(void) const {return RadarIcon;}; + + #ifdef SCENARIO_EDITOR + virtual void Display(int, int, WindowNumberType, HousesType) const {}; + #endif + + static void const * SelectShapes; + static void const * PipShapes; +}; + + +/*************************************************************************** +** This class is the common data for all objects that can be owned, produced, +** or delivered as reinforcements. These are objects that typically contain +** crews and weapons -- the fighting objects of the game. +*/ +class TechnoTypeClass : public ObjectTypeClass +{ + public: + + /* + ** If this object can serve as a good leader for a group selected + ** series of objects, then this flag will be true. Unarmed or + ** ability challenged units do not make good leaders. + */ + unsigned IsLeader:1; + + /* + ** Does this object have the ability to detect the presence of a nearby + ** cloaked object? + */ + unsigned IsScanner:1; + + /* + ** If this object is always given its proper name rather than a generic + ** name, then this flag will be true. Typically, civilians and Dr. Mobius + ** fall under this catagory. + */ + unsigned IsNominal:1; + + /* + ** If the artwork for this object (only for generics) is theater specific, then + ** this flag will be true. Civilian buildings are a good example of this. + */ + unsigned IsTheater:1; + + /* + ** Does this object type contain a rotating turret? Gun emplacements, SAM launchers, + ** and many vehicles contain a turret. If a turret is present, special rendering and + ** combat logic must be performed. + */ + unsigned IsTurretEquipped:1; + + /* + ** Certain units and buildings fire two shots in quick succession. If this is + ** the case, then this flag is true. + */ + unsigned IsTwoShooter:1; + + /* + ** Certain objects can be repaired. For buildings, they repair "in place". For units, + ** they must travel to a repair center to be repaired. If this flag is true, then + ** allow the player or computer AI to repair the object. + */ + unsigned IsRepairable:1; + + /* + ** Is this object possible to be constructed? Certain buildings and units cannot + ** be constructed using normal means. They are either initially placed in the scenario + ** or can only arrive by pre arranged reinforcement scheduling. Civilian buildings and + ** vehicles are typical examples of this type of object. They would set this flag to + ** false. + */ + unsigned IsBuildable:1; + + /* + ** Does this object contain a crew? If it does, then when the object is destroyed, there + ** is a distinct possibility that infantry will "pop out". Only units with crews can + ** become "heros". + */ + unsigned IsCrew:1; + + /* + ** Is this object typically used to transport reinforcements or other cargo? + ** Transport aircraft, helicopters, and hovercraft are typicall examples of + ** this. + */ + unsigned IsTransporter:1; + + /* + ** Most objects have the ability to reveal the terrain around themselves. + ** This sight range (expressed in cell distance) is specified here. If + ** this value is 0, then this unit never reveals terrain. Bullets are + ** typically of this nature. + */ + int SightRange; + + /* + ** These values control the cost to produce, the time to produce, and + ** the scenario when production can first start. + */ + int Cost; + unsigned char Scenario; + + /* + ** Special build prerequisite control values. These are primarily used for + ** multi-player or special events. + */ + unsigned char Level; + long Pre; + + /* + ** The risk and reward values are used to determine targets and paths + ** toward targets. When heading toward a target, a path of least + ** risk will be followed. When picking a target, the object of + ** greatest reward will be selected. The values assigned are + ** arbitrary. + */ + int Risk,Reward; + + /* + ** This value indicates the maximum speed that this object can achieve. + */ + MPHType MaxSpeed; + + /* + ** This is the maximum number of ammo shots this object can hold. If + ** this number is -1, then this indicates unlimited ammo. + */ + int MaxAmmo; + + /* + ** This is a bit field representing the houses that are allowed to + ** own (by normal means) this particular object type. This value is + ** typically used in production contexts. It is possible for a side + ** to take possession of an object type otherwise not normally allowed. + ** This event usually occurs as a result of capture. + */ + unsigned short Ownable; + + /* + ** This is the small icon image that is used to display the object in + ** the sidebar for construction selection purposes. + */ + void const * CameoData; + + /* + ** These are the weapons that this techno object is armed with. + */ + WeaponType Primary; + WeaponType Secondary; + + //-------------------------------------------------------------------- + TechnoTypeClass( + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_scanner, + bool is_nominal, + bool is_transporter, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_twoshooter, + bool is_turret_equipped, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + MPHType maxspeed, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor); + + virtual int Raw_Cost(void) const; + virtual int Max_Passengers(void) const; + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + virtual void const * Get_Cameo_Data(void) const; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(HousesType house) const; + virtual unsigned short Get_Ownable(void) const; + +#ifdef USE_RA_AI + int Legal_Placement(CELL pos) const; // From RA for AI. ST - 7/24/2019 5:20PM +#endif // USE_RA_AI + +}; + + +/*************************************************************************** +** Building types need some special information custom to buildings. This +** is a derived class that elaborates these additional data elements. +*/ +class BuildingTypeClass : public TechnoTypeClass { + enum BuildingTypeClassRepairEnums { + //REPAIR_COST=1, // Cost to repair a single "step". + REPAIR_PERCENT=102, // 40% fixed point number. + REPAIR_STEP=5 // Number of damage points recovered per "step". + }; + + public: + + /* + ** This flag controls whether the building is equiped with a dirt + ** bib or not. A building with a bib has a dirt patch automatically + ** attached to the structure when it is placed. + */ + unsigned IsBibbed:1; + + /* + ** If this building is a special wall type, such that it exists as a building + ** for purposes of construction but transforms into an overlay wall object when + ** it is placed on the map, then this flag will be true. + */ + unsigned IsWall:1; + + /* + ** Some buildings are producers. This flag will be true in that case. Producer, + ** or factory, type buildings have special logic performed. + */ + unsigned IsFactory:1; + + /* + ** Buildings can have either simple or complex damage stages. If simple, + ** then the second to the last frame is the half damage stage, and the last + ** frame is the complete damage stage. For non-simple damage, buildings + ** have a complete animation set for damaged as well as undamaged condition. + ** Turrets, oil pumps, and repair facilities are a few examples. + */ + unsigned IsSimpleDamage:1; + + /* + ** Some buildings can be placed directly on raw ground. Such buildings don't require + ** and are not affected by concrete or lack thereof. Typically, concrete itself is + ** considered sturdy. The same goes for walls and similar generic type structures. + ** The more sophisticated buildings are greatly affected by lack of concrete and thus + ** would have this flag set to false. + */ + unsigned IsSturdy:1; + + /* + ** Certain building types can be captures by enemy infantry. For those + ** building types, this flag will be true. Typically, military or hardened + ** structures such as turrets cannot be captured. + */ + unsigned IsCaptureable:1; + + /* + ** If this building really only has cosmetic idle animation, then this flag will be + ** true if this animation should run at a relatively constant rate regardless of game + ** speed setting. + */ + unsigned IsRegulated:1; + + /* + ** If this flag is true, then the building cannot be sold even if it could have been built. This + ** is especially useful for mines which can be built but cannot be sold. + */ + unsigned IsUnsellable:1; + + /* + ** This flag specifies the type of object this factory building can "produce". For non + ** factory buildings, this value will be RTTI_NONE. + */ + RTTIType ToBuild; + + /* + ** For building that produce ground units (infantry and vehicles), there is a default + ** exit poit defined. This point is where the object is first placed on the map. + ** Typically, this is located next to a door. The unit will then travel on to a clear + ** terrain area and enter normal game processing. + */ + COORDINATE ExitPoint; + + /* + ** When determine which cell to head toward when exiting a building, use the + ** list elaborated by this variable. There are directions of exit that are + ** more suitable than others. This list is here to inform the system which + ** directions those are. + */ + short const *ExitList; + + /* + ** This is the structure type identifier. It can serve as a unique + ** identification number for building types. + */ + StructType Type; + + /* + ** This is a bitflag that represents which unit types can enter this + ** building. Determine if a unit can enter by taking 1 and shifting it + ** left by the unit type ID. If the corresponding bit is set, then that + ** unit type can enter this building. + */ + unsigned long CanEnter; + + /* + ** This is the starting facing to give this building when it first + ** gets constructed. The facing matches the final stage of the + ** construction animation. + */ + DirType StartFace; + + /* + ** This is the Tiberium storage capacity of the building. The sum of all + ** building's storage capacity is used to determine how much Tiberium can + ** be accumulated. + */ + unsigned Capacity; + + /* + ** Each building type produces and consumes power. These values tell how + ** much. + */ + int Power; + int Drain; + + /* + ** This is the size of the building. This size value is a rough indication + ** of the building's "footprint". + */ + BSizeType Size; + + /********************************************************************** + ** For each stage that a building may be in, its animation is controlled + ** by this structure. It dictates the starting and length of the animation + ** frames needed for the specified state. In addition it specifies how long + ** to delay between changes in animation. With this data it is possible to + ** control the appearance of all normal buildings. Turrets and SAM sites are + ** an exception since their animation is not merely cosmetic. + */ + typedef struct { + int Start; // Starting frame of animation. + int Count; // Number of frames in this animation. + int Rate; // Number of ticks to delay between each frame. + } AnimControlType; + AnimControlType Anims[BSTATE_COUNT]; + + /* + ** This is a mask flag used to determine if all the necessary prerequisite + ** buildings have been built. + */ + // long Prerequisite; + + /*--------------------------------------------------------------------------- + ** This is the building type explicit constructor. + */ + BuildingTypeClass ( + StructType type, + int name, + char const *ininame, + COORDINATE exitpoint, + unsigned char level, + long pre, + bool is_scanner, + bool is_regulated, + bool is_bibbed, + bool is_nominal, + bool is_wall, + bool is_factory, + bool is_capturable, + bool is_flammable, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_sturdy, + RTTIType tobuild, + DirType sframe, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + unsigned long canenter, + unsigned capacity, + int power, + int drain, + BSizeType size, + short const *exitlist, + short const *sizelist, + short const *overlap, + bool is_unsellable=false); + virtual RTTIType What_Am_I(void) const {return RTTI_BUILDINGTYPE;}; + operator StructType(void) const {return(Type);}; + + static BuildingTypeClass const & As_Reference(StructType type); + static StructType From_Name(char const *name); + static void Init(TheaterType theater); + static void One_Time(void); + static void Prep_For_Add(void); + + int Width(void) const; + int Height(void) const; + + virtual int Cost_Of(void) const; + virtual int Full_Name(void) const; + virtual COORDINATE Coord_Fixup(COORDINATE coord) const {return coord & 0xFF00FF00L;} + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual int Legal_Placement(CELL pos) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual void const * Get_Buildup_Data(void) const {return(BuildupData);}; + + virtual int Raw_Cost(void) const; + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + bool Bib_And_Offset(SmudgeType & bib, CELL & cell) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + + private: + + /* + ** This is a pointer to a list of offsets (from the upper left corner) that + ** are used to indicate the building's "footprint". This footprint is used + ** to determine building placement legality and terrain passibility. + */ + short const *OccupyList; + + /* + ** Buildings can often times overlap a cell but not actually "occupy" it for + ** purposes of movement. This points to a list of offsets that indicate which + ** cells the building has visual overlap but does not occupy. + */ + short const *OverlapList; + + static BuildingTypeClass const * const Pointers[STRUCT_COUNT]; + + /* + ** The construction animation graphic data pointer is + ** pointed to by this element. + */ + void const * BuildupData; + + void Init_Anim(BStateType state, int start, int count, int rate) const; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class UnitTypeClass : public TechnoTypeClass +{ + public: + enum UnitTypeClassRepairEnums { + TIBERIUM_STEP=25, // Credits per step of Tiberium. + STEP_COUNT=28, // Number of steps a harvester can carry. + FULL_LOAD_CREDITS=(TIBERIUM_STEP*STEP_COUNT), + REPAIR_PERCENT=102, // 40% fixed point number. + REPAIR_STEP=4 // Number of damage points recovered per "step". + }; + + /* + ** If this unit can appear out of a crate, then this flag will be true. + */ + unsigned IsCrateGoodie:1; + + /* + ** Does this unit have only 8 facings? Special test units have limited + ** facings. + */ + unsigned IsPieceOfEight:1; + + /* + ** Can this unit squash infantry? If it can then if the player selects + ** an (enemy) infantry unit as the movement target, it will ride over and + ** squish the infantry unit. + */ + unsigned IsCrusher:1; + + /* + ** Does this unit go into harvesting mode when it stops on a tiberium + ** field? Typically, only one unit does this and that is the harvester. + */ + unsigned IsToHarvest:1; + + /* + ** Does this unit's shape data consist of "chunky" facings? This kind of unit + ** art has the unit in only 4 facings (N, W, S, and E) and in each of those + ** directions, the unit's turrets rotates 32 facings (counter clockwise from north). + ** This will result in 32 x 4 = 128 unit shapes in the shape data file. + */ + unsigned IsChunkyShape:1; + + /* + ** Some units are equipped with a rotating radar dish. These units have special + ** animation processing. The rotating radar dish is similar to a turret, but + ** always rotates and does not affect combat. + */ + unsigned IsRadarEquipped:1; + + /* + ** If this unit has a firing animation, this flag is true. Infantry and some special + ** vehicles are the ones with firing animations. + */ + unsigned IsFireAnim:1; + + /* + ** Many vehicles have a turret with restricted motion. These vehicles must move the + ** turret into a locked down position while travelling. Rocket launchers and artillery + ** are good examples of this kind of unit. + */ + unsigned IsLockTurret:1; + + /* + ** Does this unit lay tracks when it travels? Most tracked vehicles and some wheeled + ** vehicles have this ability. + */ + unsigned IsTracked:1; + + /* + ** Is this unit of the humongous size? Harvesters and mobile construction vehicles are + ** of this size. If the vehicle is greater than 24 x 24 but less than 48 x 48, it is + ** considered "Gigundo". + */ + unsigned IsGigundo:1; + + /* + ** Is the unit capable of cloaking? Only Stealth Tank can do so now. + */ + unsigned IsCloakable:1; + + /* + ** Does this unit have a constant animation (like Visceroid?) + */ + unsigned IsAnimating:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + UnitType Type; + + /* + ** This indicates the speed (locomotion) type for this unit. Through this + ** value the movement capabilities are deduced. + */ + SpeedType Speed; + + /* + ** This is the rotational speed of the unit. This value represents the + ** turret rotation speed. + */ + unsigned char ROT; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + UnitTypeClass ( + UnitType type, + int name, + char const *ininame, + AnimType exp, + unsigned char level, + long pre, + bool is_goodie, + bool is_leader, + bool is_eight, + bool is_nominal, + bool is_transporter, + bool is_crushable, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_tracked, + bool is_gigundo, + bool is_chunky, + bool is_cloakable, + bool is_animating, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + SpeedType speed, + MPHType maxSpeed, + unsigned rot, + int toffset, + MissionType order); + virtual RTTIType What_Am_I(void) const {return RTTI_UNITTYPE;}; + + static UnitType From_Name(char const *name); + static UnitTypeClass const & As_Reference(UnitType type); + static void Init(TheaterType ); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual int Max_Pips(void) const; + + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** This is a pointer to the wake shape (as needed by the gunboat). + */ + static void const * WakeShapes; + + private: + static UnitTypeClass const * const Pointers[UNIT_COUNT]; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class InfantryTypeClass : public TechnoTypeClass +{ + private: + static InfantryTypeClass const * const Pointers[INFANTRY_COUNT]; + + public: + + /* + ** If this civilian infantry type is female, then this flag + ** will be true. This information is used to get the correct + ** voice response. + */ + unsigned IsFemale:1; + + /* + ** Does this infantry unit have crawling animation? If not, then this + ** means that the "crawling" frames are actually running animation frames. + */ + unsigned IsCrawling:1; + + /* + ** For those infantry types that can capture buildings, this flag + ** will be set to true. Typically, this is minigun soldiers. + */ + unsigned IsCapture:1; + + /* + ** For infantry types that will run away from any damage causing + ** events, this flag will be true. Typically, this is so for all + ** civilians as well as the flame thrower guys. + */ + unsigned IsFraidyCat:1; + + /* + ** This flags whether this infantry is actually a civilian. A + ** civilian uses different voice responses, has less ammunition, + ** and runs from danger more often. + */ + unsigned IsCivilian:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + InfantryType Type; + + /* + ** This is an array of the various animation frame data for the actions that + ** the infantry may perform. + */ + DoInfoStruct DoControls[DO_COUNT]; + + /* + ** There are certain units with special animation sequences built into the + ** shape file. These values tell how many frames are used for the firing animation. + */ + char FireLaunch; + char ProneLaunch; + + /* + ** This is the explicit unit class constructor. + */ + InfantryTypeClass ( + InfantryType type, + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_female, + bool is_leader, + bool is_crawling, + bool is_civilian, + bool is_nominal, + bool is_fraidycat, + bool is_capture, + bool is_theater, + int ammo, + int *do_table, + int firelaunch, + int pronelaunch, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + MPHType maxSpeed); + virtual RTTIType What_Am_I(void) const {return RTTI_INFANTRYTYPE;}; + + static InfantryType From_Name(char const *name); + static InfantryTypeClass const & As_Reference(InfantryType type) {return *Pointers[type];}; + static void Init(TheaterType ); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const {width = 12;height = 16;}; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual int Full_Name(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/*************************************************************************** +** Bullets and other projectiles need some specific information according +** to their type. +*/ +class BulletTypeClass : public ObjectTypeClass +{ + public: + + /* + ** Does this bullet type fly over walls? + */ + unsigned IsHigh:1; + + /* + ** If this projecile is one that balistically arcs from ground level, up into the air and + ** then back to the ground, where it explodes. Typical uses of this are for grenades and + ** artillery shells. + */ + unsigned IsArcing:1; + + /* + ** If the projectile changes course as it flies in order to home in on the + ** projectile's target, then this flag is true. Missiles are typically ones + ** that do this. + */ + unsigned IsHoming:1; + + /* + ** Certain projectiles do not travel horizontally, but rather, vertically -- they drop + ** from a height. Bombs fall into this category and will have this value set to + ** true. Dropping projectiles do not calculate collision with terrain (such as walls). + */ + unsigned IsDropping:1; + + /* + ** Is this projectile invisible? Some bullets and weapon effects are not directly + ** rendered. Small caliber bullets and flame thrower flames are treated like + ** normal projectiles for damage purposes, but are displayed using custom + ** rules. + */ + unsigned IsInvisible:1; + + /* + ** Does this bullet explode when near the target? Some bullets only explode if + ** it actually hits the target. Some explode even if nearby. + */ + unsigned IsProximityArmed:1; + + /* + ** Does this projectile spew puffs of smoke out its tail while it + ** travels? Missiles are prime examples of this projectile type. + */ + unsigned IsFlameEquipped:1; + + /* + ** Should fuel consumption be tracked for this projectile? Rockets are the primary + ** projectile with this characteristic, but even for bullets it should be checked so that + ** bullets don't travel too far. + */ + unsigned IsFueled:1; + + /* + ** Is this projectile without different facing visuals? Most plain bullets do not change + ** visual imagery if their facing changes. Rockets, on the other hand, are equipped with + ** the full 32 facing imagery. + */ + unsigned IsFaceless:1; + + /* + ** If this is a typically inaccurate projectile, then this flag will be true. Artillery + ** is a prime example of this type. + */ + unsigned IsInaccurate:1; + + /* + ** If the bullet contains translucent pixels, then this flag will be true. These + ** translucent pixels really are "shadow" pixels in the same style as the shadow + ** cast by regular ground units. + */ + unsigned IsTranslucent:1; + + /* + ** If this bullet can be fired on aircraft, then this flag will be true. + */ + unsigned IsAntiAircraft:1; + + /* + ** This element is a unique identification number for the bullet + ** type. + */ + BulletType Type; + + /* + ** Maximum speed for this bullet type. + */ + MPHType MaxSpeed; + + /* + ** This projectile has a certain style of warhead. This value specifies + ** what that warhead type is. + */ + WarheadType Warhead; + + /* + ** This is the "explosion" effect to use when this projectile impacts + */ + AnimType Explosion; + + /* + ** This is the rotation speed of the bullet. It only has practical value + ** for those projectiles that performing homing action during flight -- such + ** as with rockets. + */ + unsigned char ROT; + + /* + ** Some projectiles have a built in arming distance that must elapse before the + ** projectile may explode. If this value is non-zero, then this override is + ** applied. + */ + int Arming; + + /* + ** Some projectiles have a built in override for distance travelled before it + ** automatically explodes. This value, if non-zero, specifies this distance. + */ + int Range; + + //--------------------------------------------------------------------- + BulletTypeClass( + BulletType type, + char const *ininame, + bool is_high, + bool is_homing, + bool is_arcing, + bool is_dropping, + bool is_invisible, + bool is_proximity_armed, + bool is_flame_equipped, + bool is_fueled, + bool is_faceless, + bool is_inaccurate, + bool is_translucent, + bool is_antiair, + int arming, + int range, + MPHType maxspeed, + unsigned rot, + WarheadType warhead, + AnimType explosion); + + virtual RTTIType What_Am_I(void) const {return RTTI_BULLETTYPE;}; + + static BulletTypeClass const & As_Reference(BulletType type) {return *Pointers[type];}; + static void Init(TheaterType ) {}; + static void One_Time(void); + + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const {return false;}; + virtual ObjectClass * Create_One_Of(HouseClass *) const {return 0;}; + + private: + static BulletTypeClass const * const Pointers[BULLET_COUNT]; +}; + + +/**************************************************************************** +** These are the different TYPES of terrain objects. Every terrain object must +** be one of these types. +*/ +class TerrainTypeClass : public ObjectTypeClass +{ + public: + /* + ** Which terrain object does this class type represent. + */ + TerrainType Type; + + /* + ** Does this terrain element consist of a normal frame followed by a + ** series of crumble frames? Trees fall under this case. + */ + unsigned IsDestroyable:1; + + /* + ** Does this object have the capability to transform after a period + ** of time (such as a blossom tree? + */ + unsigned IsTransformable:1; + + /* + ** Does this terrain object spawn the growth of Tiberium? Blossom trees are + ** a good example of this. + */ + unsigned IsTiberiumSpawn:1; + + /* + ** This is the fully translated name for the terrain element. + */ + short FullName; + + /* + ** This is the coordinate offset (from upper left) of where the center base + ** position of the terrain object lies. For trees, this would be the base of + ** the trunk. This is used for sorting purposes. + */ + COORDINATE CenterBase; + + /* + ** This is the bitfield control that tells which theater this terrain object is + ** valid for. If the bit (1 << TheaterType) is true, then this terrain object + ** is allowed. + */ + unsigned char Theater; + + //---------------------------------------------------------------- + TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_spawn, + bool is_destroyable, + bool is_transformable, + bool is_flammable, + bool is_crushable, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + char const *ininame, + int fullname, + unsigned short strength, + ArmorType armor, + short const *occupy, + short const *overlap); + virtual RTTIType What_Am_I(void) const {return RTTI_TERRAINTYPE;}; + + static TerrainType From_Name(char const*name); + static TerrainTypeClass const & As_Reference(TerrainType type) {return *Pointers[type];}; + static void Init(TheaterType theater = THEATER_TEMPERATE); + static void One_Time(void) {}; + static void Prep_For_Add(void); + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const {return coord & 0xFF00FF00L;} + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house=HOUSE_NONE) const; + #endif + + private: + short const *Occupy; + short const *Overlap; + + static TerrainTypeClass const * const Pointers[TERRAIN_COUNT]; +}; + + +/**************************************************************************** +** The tile type objects are controlled by this class. It specifies the form +** of the tile set for the specified object as well as other related datum. +** It is derived from the ObjectTypeClass solely for the purpose of scenario +** editing and creation. +*/ +class TemplateTypeClass: public ObjectTypeClass +{ + public: + /* + ** What template is this. + */ + TemplateType Type; + + /* + ** A bitfield container that indicates which theaters this template is allowed + ** in. A bit set in the (1< + + + + Debug + Win32 + + + Release + Win32 + + + + {1380ED08-82A3-49C2-A171-1915574B3382} + Win32Proj + TiberianDawn + SAK + SAK + SAK + SAK + 8.1 + + + + DynamicLibrary + true + MultiByte + v141 + + + DynamicLibrary + false + false + MultiByte + v141 + + + + + + + + + + + + + + false + ..\bin\$(PlatformName)\ + $(PlatformName)\$(Configuration)\ + $(ProjectName)I + + + false + ..\bin\$(PlatformName)\ + $(PlatformName)\$(Configuration)\ + + + + + + Level3 + Disabled + TRUE_FALSE_DEFINED;WIN32;_DEBUG;_WINDOWS;_USRDLL;TIBERIANDAWN_EXPORTS;%(PreprocessorDefinitions) + ./win32lib + ProgramDatabase + false + MultiThreadedDebug + 4800;4244;4996 + true + 1Byte + false + true + + + Windows + winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + false + false + false + + + DebugFull + + + + + + + + + Level3 + + + MaxSpeed + true + true + TRUE_FALSE_DEFINED;WIN32;NDEBUG;_WINDOWS;_USRDLL;TIBERIANDAWN_EXPORTS;%(PreprocessorDefinitions) + ./win32lib + 4800;4244;4996 + false + MultiThreaded + false + true + 1Byte + true + + + Windows + DebugFull + true + true + winmm.lib;Ws2_32.lib;%(AdditionalDependencies) + + false + false + + + + + + + + + + true + 1 + true + 1 + + + + Document + true + 1 + 1 + true + + + + Document + + + + + Document + 1 + 1 + true + true + + + + + Document + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TIBERIANDAWN/TiberianDawn.vcxproj.filters b/TIBERIANDAWN/TiberianDawn.vcxproj.filters new file mode 100644 index 000000000..fa54c3596 --- /dev/null +++ b/TIBERIANDAWN/TiberianDawn.vcxproj.filters @@ -0,0 +1,1235 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + * + + + {8f4fa00c-59af-42a6-824b-61fde12b0c64} + + + {7bd10c3e-67f3-4051-a861-06db6191bb48} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Resources + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + Source Files + + + Source Files\Win32Lib + + + Source Files + + + Source Files + + + + Source Files + + + + + Source Files\Win32Lib + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Win32Lib + + + Source Files\Win32Lib + + + + + Source Files\Resources + + + \ No newline at end of file diff --git a/TIBERIANDAWN/UDATA.CPP b/TIBERIANDAWN/UDATA.CPP new file mode 100644 index 000000000..50f52db73 --- /dev/null +++ b/TIBERIANDAWN/UDATA.CPP @@ -0,0 +1,1974 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\udata.cpv 2.17 16 Oct 1995 16:50:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * UnitTypeClass::Display -- Displays a generic unit shape. * + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * UnitTypeClass::Occupy_List -- Returns with unit occupation list. * + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * UnitTypeClass::Who_Can_Build_Me -- Determines which factory can build this unit type. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void const * UnitTypeClass::WakeShapes = 0; + +// Visceroid +static UnitTypeClass const UnitVisceroid( + UNIT_VICE, + TXT_VISCEROID, // NAME: Text name of this unit type. + "VICE", // NAME: Text name of this unit type. + ANIM_NAPALM2, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + true, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 150, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 1, // SCENARIO: Starting availability scenario. + 80,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL| + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_CHEMSPRAY,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Flame tank +static UnitTypeClass const UnitFTank( + UNIT_FTANK, + TXT_FTANK, // NAME: Text name of this unit type. + "FTNK", // NAME: Text name of this unit type. + ANIM_NAPALM3, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 300, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 9, // SCENARIO: Starting availability scenario. + 80,66, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_FLAME_TONGUE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Stealth tank +static UnitTypeClass const UnitSTank( + UNIT_STANK, + TXT_STANK, // NAME: Text name of this unit type. + "STNK", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 5, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + true, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 110, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 900, // COST: Cost to build (Credits). + 12, // SCENARIO: Starting availability scenario. + 80,81, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Light tank +static UnitTypeClass const UnitLTank( + UNIT_LTANK, + TXT_LTANK, // NAME: Text name of this unit type. + "LTNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 300, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 600, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,56, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_75MM,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Medium tank +static UnitTypeClass const UnitMTank( + UNIT_MTANK, + TXT_MTANK, // NAME: Text name of this unit type. + "MTNK", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 400, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 7, // SCENARIO: Starting availability scenario. + 80,62, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_105MM,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mastadon tank +static UnitTypeClass const UnitHTank( + UNIT_HTANK, + TXT_HTANK, // NAME: Text name of this unit type. + "HTNK", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 5, // Build level. + STRUCTF_REPAIR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to build (Credits). + 13, // SCENARIO: Starting availability scenario. + 80,80, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_120MM,WEAPON_MAMMOTH_TUSK, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile HQ +static UnitTypeClass const UnitMHQ( + UNIT_MHQ, + TXT_MHQ, // NAME: Text name of this unit type. + "MHQ", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + true, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 110, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 600, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,100, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Landing craft +static UnitTypeClass const UnitHover( + UNIT_HOVER, + TXT_HOVER, // NAME: Text name of this unit type. + "LST", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? +// true, // Is selectable by player? + false, // Is selectable by player? + false, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + true, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + true, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 400, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_HOVER, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 127, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile sam launcher +static UnitTypeClass const UnitSAM( + UNIT_MSAM, + TXT_MSAM, // NAME: Text name of this unit type. + "MLRS", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_ATOWER, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + true, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + 2, // AMMO: Number of shots it has (default). + 120, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 750, // COST: Cost to build (Credits). + 98, // SCENARIO: Starting availability scenario. + 80,30, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| +// HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_HONEST_JOHN,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Artillery +static UnitTypeClass const UnitArty( + UNIT_ARTY, + TXT_ARTY, // NAME: Text name of this unit type. + "ARTY", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 6, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 75, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 450, // COST: Cost to build (Credits). + 9, // SCENARIO: Starting availability scenario. + 80,73, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_155MM,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 2, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Harvester +static UnitTypeClass const UnitHarvester( + UNIT_HARVESTER, + TXT_HARVESTER, // NAME: Text name of this unit type. + "HARV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + true, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). +// 300, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 1400, // COST: Cost to build (Credits). + 7, // SCENARIO: Starting availability scenario. + 80,85, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HARVEST // ORDERS: Default order to give new unit. +); + +// Mobile construction vehicle +static UnitTypeClass const UnitMCV( + UNIT_MCV, + TXT_MCV, // NAME: Text name of this unit type. + "MCV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 5000, // COST: Cost to build (Credits). + 15, // SCENARIO: Starting availability scenario. + 80,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Jeep (hummer) +static UnitTypeClass const UnitJeep( + UNIT_JEEP, + TXT_JEEP, // NAME: Text name of this unit type. + "JEEP", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 150, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 400, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,41, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Buggy +static UnitTypeClass const UnitBuggy( + UNIT_BUGGY, + TXT_DUNE_BUGGY, // NAME: Text name of this unit type. + "BGGY", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 140, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,42, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Attack cycle +static UnitTypeClass const UnitBike( + UNIT_BIKE, + TXT_BIKE, // NAME: Text name of this unit type. + "BIKE", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false/*true*/, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). +#ifdef ADVANCED + 90, // STRENGTH: Strength (in damage points). +#else + 160, // STRENGTH: Strength (in damage points). +#endif + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,45, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Rocket launcher +static UnitTypeClass const UnitMLRS( + UNIT_MLRS, + TXT_MLRS, // NAME: Text name of this unit type. + "MSAM", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + true, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 100, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 11, // SCENARIO: Starting availability scenario. + 80,72, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_MLRS,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Armored personnel carrier +static UnitTypeClass const UnitAPC( + UNIT_APC, + TXT_APC, // NAME: Text name of this unit type. + "APC", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + true, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 200, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 700, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,15, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_BAD| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_FASTER, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Gunboat +static UnitTypeClass const UnitGunBoat( + UNIT_GUNBOAT, + TXT_GUNBOAT, // NAME: Text name of this unit type. + "BOAT", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + true, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 700, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_TOMAHAWK,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_FLOAT, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 1, // ROT: Rate of turn (degrees per tick). + 14, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Triceratops +static UnitTypeClass const UnitTric( + UNIT_TRIC, + TXT_TRIC, // NAME: Text name of this unit type. + "TRIC", // NAME: Text name of this unit type. + ANIM_TRIC_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 700, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_STEG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Tyrannosaurus Rex +static UnitTypeClass const UnitTrex( + UNIT_TREX, + TXT_TREX, // NAME: Text name of this unit type. + "TREX", // NAME: Text name of this unit type. + ANIM_TREX_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 750, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_TREX,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Velociraptor +static UnitTypeClass const UnitRapt( + UNIT_RAPT, + TXT_RAPT, // NAME: Text name of this unit type. + "RAPT", // NAME: Text name of this unit type. + ANIM_RAPT_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false/*true*/, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 180, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_TREX,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_FAST, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Stegosaurus +static UnitTypeClass const UnitSteg( + UNIT_STEG, + TXT_STEG, // NAME: Text name of this unit type. + "STEG", // NAME: Text name of this unit type. + ANIM_STEG_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_STEG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + + +#ifdef PETROGLYPH_EXAMPLE_MOD + +// Nuke tank +static UnitTypeClass const UnitNukeTank( + UNIT_NUKE_TANK, + TXT_HTANK, // NAME: Text name of this unit type. + "NTNK", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_TEMPLE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 6, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to build (Credits). + 10, // SCENARIO: Starting availability scenario. + 80,80, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NUKE_LOB,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_KINDA_SLOW, // SPEED: Miles per hour. + 3, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +#endif PETROGLYPH_EXAMPLE_MOD + +/* +** This is the array of pointers to the static data associated with each +** vehicle type. +*/ +UnitTypeClass const * const UnitTypeClass::Pointers[UNIT_COUNT] = { + &UnitHTank, // UNIT_HTANK + &UnitMTank, // UNIT_MTANK + &UnitLTank, // UNIT_LTANK + &UnitSTank, // UNIT_STANK + &UnitFTank, // UNIT_FTANK + &UnitVisceroid, // UNIT_VICE + &UnitAPC, // UNIT_APC + &UnitMLRS, // UNIT_MLRS + &UnitJeep, // UNIT_JEEP + &UnitBuggy, // UNIT_BUGGY + &UnitHarvester, // UNIT_HARVESTER + &UnitArty, // UNIT_ARTY + &UnitSAM, // UNIT_MSAM + &UnitHover, // UNIT_HOVER + &UnitMHQ, // UNIT_MHQ + &UnitGunBoat, // UNIT_GUNBOAT + &UnitMCV, // UNIT_MCV + &UnitBike, // UNIT_BIKE + &UnitTric, // UNIT_TRIC + &UnitTrex, // UNIT_TREX + &UnitRapt, // UNIT_RAPT + &UnitSteg, // UNIT_STEG +#ifdef PETROGLYPH_EXAMPLE_MOD + &UnitNukeTank, // UNIT_NUKE_TANK +#endif //PETROGLYPH_EXAMPLE_MOD + +}; + + +/*********************************************************************************************** + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the unit types. It is used to initialize the unit type class * + * structure. The unit type class is used to control the behavior of the various types * + * of units in the game. This constructor is called for every unique unit type as it * + * exists in the array of unit types. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass::UnitTypeClass(UnitType type, + int name, + char const *ininame, + AnimType exp, + unsigned char level, + long pre, + bool is_goodie, + bool is_leader, + bool is_eight, + bool is_nominal, + bool is_transporter, + bool is_crushable, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_tracked, + bool is_gigundo, + bool is_chunky, + bool is_cloakable, + bool is_animating, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, WeaponType secondary, + ArmorType armor, + SpeedType speed, + MPHType maxSpeed, + unsigned rot, + int toffset, + MissionType order) : + TechnoTypeClass(name, + ininame, + level, + pre, + is_leader, + false, + is_nominal, + is_transporter, + false, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + false, + is_twoshooter, + is_turret_equipped, + is_repairable, + is_buildable, + is_crew, + ammo, + strength, + maxSpeed, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary,secondary, + armor) +{ + Explosion = exp; + IsCrateGoodie = is_goodie; + IsPieceOfEight = is_eight; + IsCloakable = is_cloakable; + IsChunkyShape = is_chunky; + IsCrusher = is_crusher; + IsFireAnim = is_fire_anim; + IsGigundo = is_gigundo; + IsLockTurret = is_lock_turret; + IsRadarEquipped = is_radar_equipped; + IsToHarvest = is_harvest; + IsTracked = is_tracked; + IsAnimating = is_animating; + Mission = order; + ROT = rot; + Speed = speed; + TurretOffset = toffset; + Type = type; +} + + +/*********************************************************************************************** + * UnitTypeClass::Occupy_List -- Returns with unit occupation list. * + * * + * This routine returns with an occupation list for the unit type. * + * The unit occupation list is used for placing units. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the unit occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +short const * UnitTypeClass::Occupy_List(bool ) const +{ + static short const _simple[] = {0, REFRESH_EOL}; + static short const _gun[] = {0, -1, 1, REFRESH_EOL}; + + if (Type == UNIT_GUNBOAT) { + return(&_gun[0]); + } + return(&_simple[0]); +} + + +/*********************************************************************************************** + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * * + * This routine converts an ASCII representation of a unit class and * + * converts it into a real unit class number. * + * * + * INPUT: name -- ASCII name representing a unit class. * + * * + * OUTPUT: Returns with the actual unit class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +UnitType UnitTypeClass::From_Name(char const *name) +{ + if (name) { + for (UnitType classid = UNIT_FIRST; classid < UNIT_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(UNIT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * UnitTypeClass::Display -- Displays a generic unit shape. * + * * + * This routine displays a generic representation of a unit of this * + * type. Typically, it is used when adding objects to the game map. * + * * + * INPUT: x,y -- Coordinate to render the unit shape. * + * * + * window-- Window to render within. * + * * + * house -- House to render the unit colors. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 11/08/1994 JLB : Handles chunky type vehicles now. * + *=============================================================================================*/ +void UnitTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = IsChunkyShape ? 0 : 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, HouseTypeClass::As_Reference(house).RemapTable); +} + + +/*********************************************************************************************** + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * * + * This routine is used to prepare the generic object adder dialog * + * box so that it will be able to add a unit object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void UnitTypeClass::Prep_For_Add(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * * + * This routine is used to perform the action necessary only once for the unit type class. * + * It loads unit shapes and brain files. This routine should only be called once. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::One_Time(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + UnitTypeClass const & uclass = As_Reference(index); + CCFileClass file; + int largest; // Largest dimension of shape (so far). + + void const *ptr = NULL; // Shape pointer and set pointer. + + largest = 0; + if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", uclass.IniName); + } else { + sprintf(buffer, "%sICON", uclass.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MixFileClass::Retrieve(fullname); + } + + /* + ** Fetch a pointer to the unit's shape data. + */ + // Assume funpark mode might be required. ST - 10/14/2019 11:53AM + //if (!uclass.IsPieceOfEight || (Special.IsJurassic && AreThingiesEnabled) ) { + _makepath(fullname, NULL, NULL, uclass.IniName, ".SHP"); + ptr = MixFileClass::Retrieve(fullname); + //} else { + // ptr = NULL; + //} + +#ifdef PETROGLYPH_EXAMPLE_MOD + /* + ** Need some kind of shape data for our modded unit + */ + if (index == UNIT_NUKE_TANK && ptr == NULL) { + _makepath(fullname, NULL, NULL, "HTNK", ".SHP"); + ptr = MixFileClass::Retrieve(fullname); + } +#endif //PETROGLYPH_EXAMPLE_MOD + + ((void const *&)uclass.ImageData) = ptr; + if (ptr) { + + if (index == UNIT_MLRS || index == UNIT_MSAM) { + largest = 26; + } else { + largest = MAX(largest, (int)Get_Build_Frame_Width(ptr)); + largest = MAX(largest, (int)Get_Build_Frame_Height(ptr)); + } + } + + ((int &)uclass.MaxSize) = MAX(largest, 8); + } + + /* + ** Load the wake shapes in at this time. + */ + if (!WakeShapes) { + WakeShapes = MixFileClass::Retrieve("WAKE.SHP"); + } +} + + + +/*********************************************************************************************** + * UTC::Init -- fetches the sidebar icons for the unittypeclass objects * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/26/96 4:07PM ST : Created * + *=============================================================================================*/ + +void UnitTypeClass::Init(TheaterType theater) +{ + + if (Get_Resolution_Factor()){ + + if (theater != LastTheater){ + + void const * cameo_ptr; + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + + UnitTypeClass const & uclass = As_Reference(index); + + ((void const *&)uclass.CameoData) = NULL; + + if (uclass.IsBuildable) { + sprintf(buffer, "%sICNH", uclass.IniName); + _makepath(fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass.CameoData) = cameo_ptr; + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * * + * This routine is used by the scenario editor to create and place a unit object of this * + * type onto the map. * + * * + * INPUT: cell -- The cell that the unit is to be placed into. * + * * + * house -- The house that the unit belongs to. * + * * + * OUTPUT: bool; Was the unit created and placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + UnitClass * unit = new UnitClass(Type, house); + if (unit) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + return(false); +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * * + * This function creates a unit of this type and keeps it in limbo. A pointer to the * + * created unit object is returned. It is presumed that this object will later be * + * unlimboed at the correct time and place. * + * * + * INPUT: house -- Pointer to the house that is to own the unit. * + * * + * OUTPUT: Returns with a pointer to the created unit object. If the unit object * + * could not be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * UnitTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new UnitClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Who_Can_Build_Me -- Determines which factory can build this unit type. * + * * + * Use this routine to examine the buildings on the map in order to determine which one * + * can build the unit type. * + * * + * INPUT: intheory -- If this parameter is true, then no examination of whether the factory * + * is currently busy is performed. It just checks to see if the unit * + * could be produced "in theory". * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The owner of the unit to be produced. * + * * + * OUTPUT: Returns with pointer to the factory that can produce the unit. If no suitable * + * factory could be found then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/12/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * UnitTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_UNITTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * * + * Use this routine to return a reference to the UnitTypeClass object as indicated by * + * the unit type number speicified. * + * * + * INPUT: type -- The unit type number to convert into a UnitTypeClass object reference. * + * * + * OUTPUT: Returns with a reference to the unit type class object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass const & UnitTypeClass::As_Reference(UnitType type) +{ + return(*Pointers[type]); +} + + +/*********************************************************************************************** + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * * + * This routine will fill in the width and height for this unit type. This width and * + * height are used to render the selection rectangle and the positioning of the health * + * bargraph. * + * * + * INPUT: width -- Reference to the width of the unit (to be filled in). * + * * + * height -- Reference to the height of the unit (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Dimensions(int &width, int &height) const +{ + if (Type == UNIT_GUNBOAT) { + width = 46; + height = 18; + } else { + width = MaxSize-(MaxSize/4); + height = MaxSize-(MaxSize/4); + } +} + +/*********************************************************************************************** + * UnitTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * * + * Use this routine to determine how much it will cost to repair the unit one * + * step. A step is defined as the number of hit points returned from the Repair_Step() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost to repair this unit one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +int UnitTypeClass::Repair_Cost(void) const +{ + return(Fixed_To_Cardinal(Cost/(MaxStrength/REPAIR_STEP), REPAIR_PERCENT)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Repair_Step -- Determines the repair step rate. * + * * + * This routine will determine how many strength points get healed for each "step". The * + * cost to repair one step is determine from the Repair_Cost() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points repaired for each "step". * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +int UnitTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed, but for harvesters, it is the * + * number of credits it holds divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this unit type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int UnitTypeClass::Max_Pips(void) const +{ + if (Type == UNIT_HARVESTER) { + return(FULL_LOAD_CREDITS/100); + } + + if (IsTransporter) { + return(Max_Passengers()); + } + return(0); +} \ No newline at end of file diff --git a/TIBERIANDAWN/UNIT.CPP b/TIBERIANDAWN/UNIT.CPP new file mode 100644 index 000000000..b6a1c44e8 --- /dev/null +++ b/TIBERIANDAWN/UNIT.CPP @@ -0,0 +1,4255 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\unit.cpv 2.17 16 Oct 1995 16:48:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * UnitClass::AI -- AI processing for the unit. * + * UnitClass::APC_Close_Door -- Closes an APC door. * + * UnitClass::APC_Open_Door -- Opens an APC door. * + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * UnitClass::As_Target -- Returns the unit as a target value. * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * UnitClass::Can_Enter_Building -- Determines building entry legality. * + * UnitClass::Can_Fire -- Determines if this unit can fire. * + * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. * + * UnitClass::Click_With -- Handles player map clicking while this unit is selected. * + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * UnitClass::Draw_It -- Draws a unit object. * + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * UnitClass::Find_LZ -- Maintenance function for transport units. * + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * UnitClass::Init -- Clears all units for scenario preparation. * + * UnitClass::Limbo -- Prepares vehicle and then limbos it. * + * UnitClass::Look -- Perform map revelation from a unit's position. * + * UnitClass::Mission_Attack -- Handles the mission attack logic. * + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * UnitClass::Overlap_List -- Determines overlap list for units. * + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. * + * UnitClass::Random_Animate -- Handles random idle animation for the unit. * + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * UnitClass::Remap_Table -- Fetches the remap table to use for this object. * + * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. * + * UnitClass::Response_Select -- Voice feedback when selecting the unit. * + * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * UnitClass::Set_Speed -- Initiate unit movement physics. * + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * UnitClass::Stun -- Stuns the unit in preparation for unit removal. * + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. * + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * UnitClass::UnitClass -- Constructor for units. * + * UnitClass::Unlimbo -- Removes unit from stasis. * + * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. * + * UnitClass::Validate -- validates unit pointer. * + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * UnitClass::What_Am_I -- Returns with the RTTI type this object is. * + * UnitClass::Write_INI -- Writes all the units out to an INI file. * + * UnitClass::delete -- Deletion operator for units. * + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * UnitClass::~UnitClass -- Destructor for unit objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * UnitClass::VTable; + + +/*********************************************************************************************** + * UnitClass::Validate -- validates unit pointer. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int UnitClass::Validate(void) const +{ + int num; + + num = Units.ID(this); + if (num < 0 || num >= UNIT_MAX) { + Validate_Error("UNIT"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * * + * This is a helper routine that modifies the pixel coordinates provided according to the * + * direction specified. The effect is the simulate recoil effects by moving an object 'back'* + * one pixel. Since the pixels moved depend on facing, this routine handles the pixel * + * adjustment quickly. * + * * + * INPUT: dir -- The direction to base the recoil on. * + * * + * x,y -- References to the pixel coordinates that will be adjusted. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Recoil_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {0,1}, // N + {0,1}, + {0,1}, + {-1,1}, + {-1,1}, // NE + {-1,1}, + {-1,0}, + {-1,0}, + {-1,0}, // E + {-1,0}, + {-1,-1}, + {-1,-1}, + {-1,-1}, // SE + {-1,-1}, + {-1,-1}, + {0,-1}, + {0,-1}, // S + {0,-1}, + {0,-1}, + {1,-1}, + {1,-1}, // SW + {1,-1}, + {1,0}, + {1,0}, + {1,0}, // W + {1,0}, + {1,1}, + {1,1}, + {1,1}, // NW + {1,1}, + {0,1}, + {0,1} + }; + + int index = Facing_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * * + * This routine adjusts the pixel coordinates specified to account for the displacement of * + * the turret on the MLRS and MSAM vehicles. * + * * + * INPUT: dir -- The direction of the body of the vehicle. * + * * + * x,y -- References to the turret center pixel position. These will be modified as * + * necessary. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Turret_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {1,2}, // N + {-1,1}, + {-2,0}, + {-3,0}, + {-3,1}, // NW + {-4,-1}, + {-4,-1}, + {-5,-2}, + {-5,-3}, // W + {-5,-3}, + {-3,-3}, + {-3,-4}, + {-3,-4}, // SW + {-3,-5}, + {-2,-5}, + {-1,-5}, + {0,-5}, // S + {1,-6}, + {2,-5}, + {3,-5}, + {4,-5}, // SE + {6,-4}, + {6,-3}, + {6,-3}, + {6,-3}, // E + {5,-1}, + {5,-1}, + {4,0}, + {3,0}, // NE + {2,0}, + {2,1}, + {1,2} + }; + + int index = Facing_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * UnitClass::As_Target -- Returns the unit as a target value. * + * * + * This routine will convert the unit into a target value that is then returned. Target * + * values are typically used for navigation and other computer uses. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with target value of unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET UnitClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_UNIT, Units.ID(this))); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * * + * This displays the current status of the unit class to the mono monitor. By this display * + * bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³Turret Locked.³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³Deploying.....³ ³ ³ \n" + "³Rotating......³ ³ ³ \n" + "³Firing........³ ³ ³ \n" + "³Driving.......³ ³ ³ \n" + "³To Look.......³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + CargoClass::Debug_Dump(mono); + MissionClass::Debug_Dump(mono); + TarComClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * * + * This routine is used by the rendering system in order to sort the * + * game objects in a back to front order. This is now the correct * + * overlap effect is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value that can be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Sort_Y(void) const +{ + Validate(); + if (IsTethered && *this == UNIT_HOVER) { + return(Coord_Add(Coord, 0xFF800000L)); + } + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * UnitClass::AI -- AI processing for the unit. * + * * + * This routine will perform the AI processing necessary for the unit. These are non- * + * graphic related operations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::AI(void) +{ + Validate(); + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { + Commence(); + } + + TarComClass::AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + Delete_This(); + return; + } + + /* + ** Rocket launchers will reload every so often. + */ + if (*this == UNIT_MSAM && Ammo < Class->MaxAmmo) { + if (IsDriving) { + Reload = Reload + 1; + } else { + if (Reload.Expired()) { + Ammo++; + if (Ammo < Class->MaxAmmo) { + Reload = TICKS_PER_SECOND*30; + } + Mark(MARK_CHANGE); + } + } + } + + /* + ** Hover landers always are flagged to redraw since they don't record themselves + ** on the map in the normal fashion. + */ + if (*this == UNIT_HOVER) { +// Mark_For_Redraw(); +//if (IsDown) Mono_Printf("*"); + Mark(MARK_CHANGE); + } + + /* + ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform + ** the heal logic here. + */ + if (*this == UNIT_HTANK && (Frame % 16) == 0 && Health_Ratio() < 0x0080) { + Strength++; + Mark(MARK_CHANGE); + } + if (*this == UNIT_VICE && Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM && Health_Ratio() < 0x0100 && (Frame % 16) == 0) { + Strength++; + Mark(MARK_CHANGE); + } + + /* + ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS + ** mission that they can follow. Passenger loading is merely a part of their normal operation. + */ + if (Class->IsTransporter) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) { + APC_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** Handle recoil recovery here. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE); + } + + /* + ** For animating objects such as Visceroids, max-out their animation + ** stages here + */ + if (Class->IsAnimating) { + if (!Fetch_Rate()) Set_Rate(2); + StageClass::Graphic_Logic(); + if (Fetch_Stage() >= Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + Set_Stage(0); + } + } + + /* + ** for Jurassic objects, animate them if they're walking + */ + // Assume funpark mode might be required. ST - 10/14/2019 11:53AM + if (Class->IsPieceOfEight) { // && Special.IsJurassic && AreThingiesEnabled) { + // Only animate if they're walking + if (IsDriving || IsFiring) { + if (!Fetch_Rate()) { + Set_Rate(Options.Normalize_Delay(2)); + Set_Stage(0); + } + StageClass::Graphic_Logic(); + if (Fetch_Stage() >= ( (IsDriving || *this == UNIT_TREX || *this == UNIT_RAPT) ? 8 : 12) ) { + Set_Stage(0); + if (IsFiring) { + Set_Rate(0); + IsFiring = false; + } + } + } + } + + /* + ** A cloaked object that is carrying the flag will always shimmer. + */ + if (Cloak == CLOAKED && Flagged != HOUSE_NONE) { + Do_Shimmer(); + } + +} + + +/*********************************************************************************************** + * UnitClass::Can_Fire -- Determines if this unit can fire. * + * * + * INPUT: target -- The target that is desired to fire upon. * + * * + * which -- The weapon (primary=0, secondary=1) to fire. * + * * + * OUTPUT: Returns with the fire error number if it cannot fire or else FIRE_OK. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType UnitClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + FireErrorType cf; + + cf = TarComClass::Can_Fire(target, which); + if (cf == FIRE_OK) { + + /* + ** If it's a dinosaur, when it's OK to fire we should start the firing + ** animation, but wait for the proper attack stage before starting the + ** bullet, so things won't die prematurely. + */ + if (Class->IsFireAnim) { + if (!IsFiring) { + UnitClass *nonconst; + + nonconst = (UnitClass *)this; + nonconst->Set_Rate(Options.Normalize_Delay(2)); + nonconst->Set_Stage(0); +// Cast out the const. ST - 12/18/2018 1:03PM +((UnitClass*)this)->IsFiring = true; + cf = FIRE_BUSY; + } else { + if (Fetch_Stage() < 4) cf = FIRE_BUSY; + } + } + } + return(cf); +} + + +/*********************************************************************************************** + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a unit receives a radio * + * message. Typical use of this is when a unit unloads from a hover * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->IsTransporter && How_Many() < Class->Max_Passengers() && from && House->Class->House == from->Owner()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** The refinery has told this harvester that it should begin the backup procedure + ** so that proper unloading may take place. + */ + case RADIO_BACKUP_NOW: + TarComClass::Receive_Message(from, message, param); + if (!IsRotating && PrimaryFacing != DIR_SW) { + Do_Turn(DIR_SW); + } else { + if (!IsDriving) { + Force_Track(BACKUP_INTO_REFINERY, Adjacent_Cell(Center_Coord(), FACING_N)); + Set_Speed(128); + } + } + return(RADIO_ROGER); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + APC_Close_Door(); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->IsTransporter && *this == UNIT_APC && How_Many() < Class->Max_Passengers()) { + TarComClass::Receive_Message(from, message, param); + + /* + ** Check if the APC is busy with an order + */ + bool is_busy = Mission == MISSION_MOVE; + + if ((!IsRotating || Target_Legal(TarCom)) && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + /* + ** If the unit has a target, don't bother with turning logic + */ + CELL cell; + DirType dir; + if (Target_Legal(TarCom)) { + dir = PrimaryFacing.Current(); + cell = Adjacent_Cell(Coord_Cell(Coord), Direction(from)); + } + else { + dir = Desired_Load_Dir(from, cell); + } + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + if (!is_busy && !IsDriving) { + Do_Turn(dir); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is or needs + ** to rotate. + */ + if (IsRotating) { + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + } else { + if (!Is_Door_Open()) { + APC_Open_Door(); + } + } + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + if (Is_Door_Open() || Target_Legal(TarCom)) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + TarComClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(TarComClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * UnitClass::Unlimbo -- Removes unit from stasis. * + * * + * This routine will place a unit into the game and out of its limbo * + * state. This occurs whenever a unit is unloaded from a transport. * + * * + * INPUT: coord -- The coordinate to make the unit appear. * + * * + * dir -- The initial facing to impart upon the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? If the desired * + * coordinate is illegal, then this might very well return * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + /* + ** All units must start out facing one of the 8 major directions. + */ + dir = Facing_Dir(Dir_Facing(dir)); + + if (TarComClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->UScan |= (1L << Class->Type); + House->ActiveUScan |= (1L << Class->Type); + + /* + ** If it starts off the edge of the map, then it already starts cloaked. + */ + if (IsCloakable && !IsLocked) Cloak = CLOAKED; + + /* + ** Units default to no special animation. + */ + Set_Rate(0); + Set_Stage(0); + + /* + ** A gun boat and a hover craft are allowed to exit the map thus we + ** flag them so they can. This undoes the work of Techno::Unlimbo which + ** stole their IsALoaner flag. + */ + if (*this == UNIT_GUNBOAT || *this == UNIT_HOVER) { + IsALoaner = true; + } + + /* + ** Start the gunboat animating when it is unlimboed. + */ + if (*this == UNIT_GUNBOAT) { + Set_Rate(2); + Set_Stage(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * * + * This routine will inflict the specified number of damage points on * + * the given unit. If the unit is destroyed, then this routine will * + * remove the unit cleanly from the game. The return value indicates * + * whether the unit was destroyed. This will allow appropriate death * + * animation or whatever. * + * * + * INPUT: damage-- The number of damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead--The type of damage to inflict. * + * * + * source -- Who is responsible for this damage? * + * * + * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to * + * RESULT_DESTROYED. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1991 JLB : Created. * + * 07/12/1991 JLB : Script initiated by unit destruction. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Warhead modifier. * + * 06/03/1994 JLB : Added the source of the damage target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 06/30/1995 JLB : Lasers do maximum damage against gunboat. * + * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. * + *=============================================================================================*/ +ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Special case: If this is a laser attacking a gunboat, then the gunboat is ALWAYS toasted. + */ + if (*this == UNIT_GUNBOAT && warhead == WARHEAD_LASER) { + damage = Strength*3; + } + + /* + ** Remember if this object was selected. If it was and it gets destroyed and it has + ** passengers that pop out, then the passengers will inherit the select state. + */ + bool select = (Is_Selected_By_Player() && Is_Owned_By_Player()); // Fox for GlyphX multiplayer. ST - 4/16/2019 9:46AM + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = TarComClass::Take_Damage(damage, distance, warhead, source); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + /* + ** SSM launchers will really explode big if they are carrying + ** missiles at the time of the explosion. + */ + if (*this == UNIT_MSAM && Ammo) { + anim = ANIM_NAPALM3; + } + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + Sound_Effect(VOC_DINODIE1, Coord); + } + + new AnimClass(anim, Coord); + + /* + ** When the flame tank blows up, it really blows up. It is + ** equivalent to a napalm strike. + */ + if (Class->Explosion == ANIM_NAPALM3) { + Explosion_Damage(Coord, 200, source, WARHEAD_FIRE); + } + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_The_Screen(3, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(3, source->Owner()); + } + } + } + + /* + ** Possibly have the crew member run away. + */ + Mark(MARK_UP); + if (Class->IsCrew && !Class->IsTransporter) { + if (Random_Pick(0, 1) == 0) { + InfantryClass * i = 0; + if (Class->Primary == WEAPON_NONE) { + i = new InfantryClass(INFANTRY_C1, House->Class->House); + i->IsTechnician = true; + } else { + i = new InfantryClass(INFANTRY_E1, House->Class->House); + } + if (i) { + if (i->Unlimbo(Coord, DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2); + i->Scatter(0, true); + if (!House->IsHuman) { + i->Assign_Mission(MISSION_HUNT); + } else { + i->Assign_Mission(MISSION_GUARD); + } + if (select) i->Select(); + } else { + delete i; + } + } + } + } else { + if (*this != UNIT_HOVER) { + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + if (!object) break; // How can this happen? + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) { + // object->Strength = Random_Pick(5, (int)((InfantryClass*)object)->Class->MaxStrength/2); + object->Look(false); + object->Scatter(0, true); + if (select) object->Select(); + } else { + object->Record_The_Kill(source); + delete object; + } + } + } else { + Kill_Cargo(source); + } + } + + /* + ** When the mobile head quarters blows up, the entire side blows up. + */ + if (*this == UNIT_MHQ) { + House->Flag_To_Die(); + } + + if (*this == UNIT_MCV) { + if (House) { + House->Check_Pertinent_Structures(); + } + } + + /* + ** Finally, delete the vehicle. + */ + Delete_This(); + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking. + */ + if (Health_Ratio() < 0x0080 && !IsAnimAttached && *this != UNIT_VICE && *this != UNIT_STEG && *this != UNIT_TREX && *this != UNIT_TRIC && *this != UNIT_RAPT) { + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim) anim->Attach_To(this); + } + + /* + ** If at half damage, then start smoking or burning as appropriate. + */ + if (res == RESULT_HALF) { + if (*this == UNIT_GUNBOAT) { + AnimClass * anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Add(Coord, XYP_Coord(Random_Pick(0, 16)-8, -2))); + if (anim) anim->Attach_To(this); + } + } + + /* + ** Try to crush anyone that fires on this unit if possible. The harvester + ** typically is the only one that will qualify here. + */ + if (!Team && source && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Special.IsSmartDefense)) { + + /* + ** Try to crush the attacker if it can be crushed by this unit and this unit is + ** not equipped with a flame type weapon. If this unit has a weapon and the target + ** is not very close, then fire on it instead. In easy mode, they never run over the + ** player. In hard mode, they always do. In normal mode, they only overrun past + ** mission #8. + */ + if ((Class->Primary == WEAPON_NONE || (Distance(source) < 0x0180 && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead != WARHEAD_FIRE)) && + (GameToPlay != GAME_NORMAL || + *this != UNIT_HARVESTER || + BuildLevel > 8 || + PlayerPtr->Difficulty == DIFF_HARD) && + !(GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY) && + Class->IsCrusher && source->Is_Techno() && ((TechnoTypeClass const &)source->Class_Of()).IsCrushable) { + + Assign_Destination(source->As_Target()); + Assign_Mission(MISSION_MOVE); + } else { + + /* + ** Try to return to base if possible. + */ + if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() < 0x0080) { + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + } + } + } + } + + /* + ** Computer controlled harvester will radio for help if they are attacked. + */ + if (*this == UNIT_HARVESTER && !House->IsHuman && source) { + Base_Is_Attacked(source); + } + } + return(res); +} + + +/*********************************************************************************************** + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * * + * This routine will allocate a unit from the available unit pool and * + * fixup all the access lists to match. It will allocate a unit slot * + * from within the range allowed for the specified unit type. If no * + * slot was found, then it will fail. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + *=============================================================================================*/ +void * UnitClass::operator new(size_t) +{ + void * ptr = (UnitClass *)Units.Allocate(); + if (ptr) { + ((UnitClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * UnitClass::delete -- Deletion operator for units. * + * * + * This removes the unit from the local allocation system. Since this * + * is a fixed block of memory, not much has to be done to delete the * + * unit. Merely marking it as inactive is enough. * + * * + * INPUT: ptr -- Pointer to the unit to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::operator delete(void *ptr) +{ + if (ptr) { + ((UnitClass *)ptr)->IsActive = false; + } + Units.Free((UnitClass *)ptr); + + //Map.Validate(); +} + + +/*********************************************************************************************** + * UnitClass::~UnitClass -- Destructor for unit objects. * + * * + * This destructor will lower the unit count for the owning house as well as inform any * + * other units in communication, that this unit is about to leave reality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::~UnitClass(void) +{ + if (GameActive && Class) { + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Remove(this); +#endif // USE_RA_AI + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + if (GameActive && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * UnitClass::UnitClass -- Constructor for units. * + * * + * This constructor for units will initialize the unit into the game * + * system. It will be placed in all necessary tracking lists. The initial condition will * + * be in a state of limbo. * + * * + * INPUT: classid -- The type of unit to create. * + * * + * house -- The house owner of this unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::UnitClass(UnitType classid, HousesType house) : + TarComClass(classid, house) +{ + Flagged = HOUSE_NONE; + Reload = 0; + Ammo = Class->MaxAmmo; + IsCloakable = Class->IsCloakable; + if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3)); + + /* + ** Keep count of the number of units created. + */ + if (GameToPlay == GAME_INTERNET){ + House->UnitTotals->Increment_Unit_Total((int)classid); + } + +#ifdef USE_RA_AI + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + House->Tracking_Add(this); +#endif // USE_RA_AI +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (action != What_Action(object)) { + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + action = ACTION_NONE; + break; + } + } + TarComClass::Active_Click_With(action, object); +} + + +void UnitClass::Active_Click_With(ActionType action, CELL cell) {TarComClass::Active_Click_With(action, cell);}; + + +/*********************************************************************************************** + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * * + * This routine is called when the unit completes one mission but does not have a clear * + * follow up mission to perform. In such a case, the unit should enter a default idle * + * state. This idle state varies depending on what the current internal computer * + * settings of the unit is as well as what kind of unit it is. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/03/1994 JLB : Fixed to handle non-combat vehicles. * + * 06/18/1995 JLB : Allows a harvester to stop harvesting. * + *=============================================================================================*/ +void UnitClass::Enter_Idle_Mode(bool initial) +{ + Validate(); + MissionType order; + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + if (Class->Primary == WEAPON_NONE) { + if (Class->IsToHarvest) { + if (!In_Radio_Contact() && Mission != MISSION_HARVEST) { + if (initial || !House->IsHuman || Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM) { + order = MISSION_HARVEST; + } else { + order = MISSION_GUARD; + } + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } else { + return; + } + } else { + if (IsALoaner && Class->IsTransporter && Is_Something_Attached() && !Team) { + order = MISSION_UNLOAD; + } else { + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + } + } else { + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + } else { + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + if (GameToPlay == GAME_NORMAL || House->IsHuman) { + order = MISSION_GUARD; + } else { + //if (GameToPlay != GAME_NORMAL) { +#ifndef USE_RA_AI + // Don't use MISSION_TIMED_HUNT since this can trigger the Blitz behavior + order = MISSION_TIMED_HUNT; +#else + // + // Added for RA AI in TD. ST - 7/26/2019 9:12AM + // + // This applies only to non-human houses in a non-normal game type + // + order = MISSION_GUARD; + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) { + return; + } + + if (!Team) { + if (House->IQ >= Rule.IQGuardArea) { + if (Is_Weapon_Equipped()) { + order = MISSION_GUARD_AREA; + } + } + } + +#endif + //} else { + // order = MISSION_HUNT; + //} + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * UnitClass::Find_LZ -- Maintenance function for transport units. * + * * + * This is a maintenance function for transport units. It checks to see if it is loaded * + * with cargo, but it doesn't know where to put it. In such a case, the unit must look * + * for a landing zone (LZ) to unload the cargo. This routine should be called every so * + * often. Typically, calling this routine is controlled by the scripts. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Find_LZ(void) +{ + Validate(); + CELL cell; // Map exit cell number. + + if (*this == UNIT_HOVER) { + + if (!IsRotating && Is_Something_Attached() && !Target_Legal(NavCom)) { + cell = Map.Calculated_Cell(SOURCE_BEACH, House->Class->House); + Assign_Destination(::As_Target(cell)); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. * + * * + * This is a maintenance routine to handle the special operations necessary for a * + * hovercraft transport. This routine checks to see if special unloading operations are * + * necessary and performs them. These operations can include unloading the transported * + * object, finding a new beach cell, and rotating to a convenient unloading facing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the transport finished its unloading mission? This is true after the * + * hovercraft has completely dispatched its cargo. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 07/31/1995 JLB : Second infantry unloaded MUST be the one tethered. * + *=============================================================================================*/ +bool UnitClass::Unload_Hovercraft_Process(void) +{ + Validate(); + bool unloaded = false; + FootClass *unit; // The unit to be unloaded. + CELL cell; // Cell to unload to. + + /* + ** If the hovercraft is currently waiting for the last unit + ** to completely unload, then don't do anything. + */ + if (IsTethered || IsRotating) { + return(false); + } + + if (Is_Something_Attached()) { + + /* + ** Only unload if the hovercraft has reached the beach. + */ + if (!Target_Legal(NavCom)) { + + cell = Coord_Cell(Adjacent_Cell(Coord, Dir_Facing(PrimaryFacing.Current()))); + + unit = (FootClass *)Attached_Object(); + + Mark(MARK_UP); + if (Map.In_Radar(cell) && !Map[cell].Cell_Unit()) { + + if (unit->Can_Enter_Cell(cell, FACING_NONE) == MOVE_OK) { + + /* + ** Place all the transported units onto the map. + */ + int count = 0; + bool first = true; + FootClass * secondary = 0; + while (Attached_Object()) { + FootClass * u = (FootClass *)Detach_Object(); + + if (!first && !secondary) secondary = u; + first = false; + + /* + ** Place the unit on the map and start it heading in the right direction. + */ + ScenarioInit++; + if (u->Unlimbo(Coord_Add(Coord & 0xFF00FF00L, StoppingCoordAbs[count]), DIR_N)) { + u->Assign_Mission(MISSION_MOVE); + u->NavCom = ::As_Target(cell); + u->Path[0] = Dir_Facing(u->PrimaryFacing.Current()); + u->Path[1] = FACING_NONE; + u->Set_Speed(0x80); + u->IsUnloading = true; + u->Look(false); + } else { + + /* + ** Couldn't unlimbo for some strange reason. Kill the unit off. + */ + delete u; + } + ScenarioInit--; + count++; + } + if (secondary) unit = secondary; + + /* + ** Establish radio contact bond with the transport + ** hovercraft. This bond tells the hovercraft to + ** not move until the unit has completely unloaded. + */ + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + Mark(MARK_DOWN); + Map.Layer[LAYER_GROUND].Sort(); + Map.Layer[LAYER_GROUND].Sort(); + Map.Layer[LAYER_GROUND].Sort(); + return(false); + } else { + + /* + ** If the attached object cannot unload because the desired destination + ** cell is impassable, then abort the landing. This is faked by pretending + ** that the unload was successful. The controlling routine will cause + ** the transport to leave. + */ +Mark(MARK_DOWN); +return(false); +// return(true); + } + } + + Mark(MARK_DOWN); + } + } else { + return(true); + } + + return(unloaded); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * * + * This routine is used by the MCV to find a clear spot to deploy. If a clear spot * + * is found, then the MCV will assign that location to its navigation computer. This only * + * occurs if the MCV isn't already heading toward a spot. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the located at a spot where it can deploy? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Clear_Spot(void) +{ + Validate(); + Mark(MARK_UP); + if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + Mark(MARK_DOWN); + return(true); + } + + if (!Target_Legal(NavCom)) { + +#ifndef USE_RA_AI + /* + ** This scan table is skewed to north scanning only. This should + ** probably be converted to a more flexible method. + */ + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, + 0 + }; + int *ptr; + + ptr = &_offsets[0]; + while (*ptr) { + CELL cell = Coord_Cell(Coord)+*ptr++; + + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + +#else //USE_RA_AI + + // + // Pulled in the extra offsets from RA, and started at a random position in the array. ST - 7/26/2019 2:07PM + // + static int _offset_count = 36; + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, +//BG: Added south scanning + MAP_CELL_W*1, + MAP_CELL_W*2, + (MAP_CELL_W*2)+1, + (MAP_CELL_W*2)-1, + MAP_CELL_W*3, + (MAP_CELL_W*3)+1, + (MAP_CELL_W*3)-1, + (MAP_CELL_W*3)+2, + (MAP_CELL_W*3)-2, + MAP_CELL_W*4, + (MAP_CELL_W*4)+1, + (MAP_CELL_W*4)-1, + (MAP_CELL_W*4)+2, + (MAP_CELL_W*4)-2, + +//BG: Added some token east/west scanning + -1,-2,-3,-4, + + 1, 2, 3, 4, + 0 + }; + + int offset_index = Random_Pick(0, _offset_count); + + for (int i=0 ; i<_offset_count ; i++) { + CELL cell = Coord_Cell(Coord) + _offsets[offset_index++]; + if (offset_index >= _offset_count || _offsets[offset_index] == 0) { + offset_index = 0; + } + + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + +#endif //USE_RA_AI + } + Mark(MARK_DOWN); + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * * + * Certain units have the ability to deploy into a building. When this routine is called * + * for one of those units, it will attempt to deploy at its current location. If the unit * + * is in motion to a destination or it isn't one of the special units that can deploy or * + * it isn't allowed to deploy at this location for some reason it won't deploy. In all * + * other cases, it will begin to deploy and once it begins only a player abort action will * + * stop it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was deployment begun? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Try_To_Deploy(void) +{ + Validate(); + if (!Target_Legal(NavCom) && !IsRotating) { + if (*this == UNIT_MCV) { + + /* + ** Determine if it is legal to deploy at this location. If not, tell the + ** player. + */ + Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + if (PlayerPtr == House) { + Speak(VOX_DEPLOY); + } + Mark(MARK_DOWN); + IsDeploying = false; + return(false); + } + Mark(MARK_DOWN); + + /* + ** If the unit is not facing the correct direction, then start it rotating + ** toward the right facing, but still flag it as if it had deployed. This is + ** because it will deploy as soon as it reaches the correct facing. + */ + if (PrimaryFacing.Current() != DIR_SW) { + Do_Turn(DIR_SW); +// PrimaryFacing.Set_Desired(DIR_SW); + IsDeploying = true; + return(true); + } + + /* + ** Since the unit is already facing the correct direction, actually do the + ** deploy logic. If for some reason this cannot occur, then don't delete the + ** unit, just mark it as not deploying. + */ + Mark(MARK_UP); + BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House); + if (building) { + if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) { + + /* + ** Always reveal the construction yard to the player that owned the + ** mobile construction vehicle. + */ + building->Revealed(House); + + /* + ** Force the newly placed construction yard to be in the same strength + ** ratio as the MCV that deployed into it. + */ + int ratio = Health_Ratio(); + building->Strength = Fixed_To_Cardinal(building->Class->MaxStrength, ratio); + /* + ** Force the MCV to drop any flag it was carrying. This will also set + ** the owner house's flag home cell (since the house's FlagHome is + ** presumably 0 at this point). + */ + Stun(); + Delete_This(); + return(true); + } else { + + /* + ** Could not deploy the construction yard at this location! Just revert + ** back to normal "just sitting there" mode and await further instructions. + */ + delete building; + } + } + Mark(MARK_DOWN); + IsDeploying = false; + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * * + * This routine will perform the operations necessary that occur when a unit is at the * + * center of a cell. These operations could entail deploying into a construction yard, * + * radioing a transport unit, and looking around for the enemy. * + * * + * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" * + * to the center, then this parameter will be false. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + * 06/17/1995 JLB : Handles case when building says "NO!" * + * 06/30/1995 JLB : Gunboats head back and forth now. * + *=============================================================================================*/ +void UnitClass::Per_Cell_Process(bool center) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + TechnoClass * whom; + HousesType house; + + /* + ** If this is a unit that is driving onto a building then the unit must enter + ** the building as the final step. + */ + whom = Contact_With_Whom(); + if (IsTethered && whom && center) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (whom == Map[cell].Cell_Building()) { + switch (Transmit_Message(RADIO_IM_IN, whom)) { + case RADIO_ROGER: + break; + + case RADIO_ATTACH: + Mark(MARK_UP); + SpecialFlag = true; + Limbo(); + SpecialFlag = false; + whom->Attach(this); + return; + + default: + Scatter(true); + break; + } + } + } + } + + /* + ** When breaking away from a transport object or building, possibly + ** scatter or otherwise begin normal unit operations. + */ + if (IsTethered && center && Mission != MISSION_ENTER && (*this != UNIT_APC || IsDriving || !Target_Legal(TarCom))) { + TechnoClass * contact = Contact_With_Whom(); + if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) { + if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING) { + Assign_Mission(MISSION_HARVEST); + } else if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } else { + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } + } + } + } + +#ifdef OBSOLETE + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (IsTethered && center && !Map[cell].Cell_Building()) { + if (!Tiberium || *this != UNIT_HARVESTER) { + Transmit_Message(RADIO_UNLOADED); + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + Scatter(0, true); + } + } + } + } +#endif + +#ifdef OBSOLETE + /* + ** If the unit is at the center of the repair facility, and that was his + ** destination, then start him repairing. + */ + if (center && !IsRepairing) { + BuildingClass * b = As_Building(NavCom); + if (b && *b==STRUCT_REPAIR && Coord == b->Center_Coord()) { + NavCom = 0; + IsRepairing = true; + Transmit_Message(RADIO_REPAIR_BEGIN_ANIM); + } + } +#endif + + /* + ** Check to see if this is merely the end of a rotation for the MCV as it is + ** preparing to deploy. In this case, it should begin its deploy process. + */ + if (center && IsDeploying) { + Try_To_Deploy(); + if (!IsActive) return; // Unit no longer exists -- bail. + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that + ** it is probably carrying an incoming reinforcement and it should not be eliminated. + */ + if (center && IsALoaner && !Map.In_Radar(cell)) { + if (IsReturning || !Is_Something_Attached()) { + if (*this == UNIT_GUNBOAT) { + CELL cell = Coord_Cell(Coord); + if (Cell_X(cell) <= Map.MapCellX) { + Assign_Mission(MISSION_HUNT); + Assign_Destination(::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(cell)))); + Set_Speed(255); + PrimaryFacing = DIR_E; + } else { + Assign_Mission(MISSION_HUNT); + Assign_Destination(::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(cell)))); + Set_Speed(255); + PrimaryFacing = DIR_W; + } + Mark(MARK_DOWN); + } else { + Mark(MARK_DOWN); + Stun(); + Delete_This(); + return; + } + } + } + +#ifdef OBSOLETE + /* + ** Destroy any crushable wall that is driven over by a tracked vehicle. + */ + CellClass * cellptr = &Map[cell]; + if (center && Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrushable) { + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + } + } +#endif + + /* + ** Check to see if crushing of any unfortunate infantry is warranted. + */ + Overrun_Square(Coord_Cell(Coord), false); + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + if (IsPlanningToLook) { + IsPlanningToLook = false; + Look(false); + } else { + Look(true); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (center) { + Commence(); + } + + /* + ** Certain units require some setup time after they come to a halt. + */ + if (Special.IsDefenderAdvantage && /*center &&*/ !Target_Legal(NavCom) && Path[0] == FACING_NONE) { + if (*this == UNIT_MLRS || *this == UNIT_ARTY || *this == UNIT_MSAM) { + Arm = Rearm_Delay(false)*2; + } + } + + /* + ** If there is a house flag here, then this unit just might pick it up. + */ + if (center && Flagged == HOUSE_NONE) { + + if (Map[cell].IsFlagged && !House->Is_Ally(Map[cell].Owner)) { + HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this); + } + } + + /* + ** If this is the unit's own flag-home-cell and the unit is carrying + ** a flag, destroy the house of the flag the unit is carrying. + */ + if (Flagged != HOUSE_NONE) { + + /* + ** If this vehicle is carrying your flag, then it will reveal the + ** map for you as well as itself. This gives you and opportunity to + ** attack the unit. + */ + if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) { + Map.Sight_From(House, Coord_Cell(Coord), Class->SightRange, true); // Passed our house into Map.Sight_From since it now needs to know who it is performing the action on behalf of. ST - 3/28/2019 2:55PM + } + + /* + ** If the flag reaches the home cell for the player, then the flag's + ** owner will be destroyed. + */ + if (cell == HouseClass::As_Pointer(Owner())->FlagHome && center) { + house = Flagged; // Flag_Remove will clear 'Flagged', so save it + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(),true); + HouseClass::As_Pointer(house)->Flag_To_Die(); + } + } + + TarComClass::Per_Cell_Process(center); +} + + + +/*********************************************************************************************** + * UnitClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Removed infantry support. * + * 01/07/1995 JLB : Harvester animation support. * + * 07/08/1995 JLB : Uses general purpose draw routine. * + *=============================================================================================*/ +void UnitClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + int shapenum; // Working shape number. + void const *shapefile; // Working shape file pointer. + int facing = Facing_To_32(PrimaryFacing.Current()); + int tfacing = Facing_To_32(SecondaryFacing.Current()); + int shapestart; + int xx, yy; + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); + if (!is_hidden) { + + /* + ** For eight facing units, adjust the facing number accordingly. + */ + if (Class->IsPieceOfEight) { + facing = Dir_Facing(PrimaryFacing.Current()); + } + + /* + ** Calculations for special wake drawing. + */ + xx = x; + yy = y; + switch (Dir_Facing(PrimaryFacing.Current())) { + case FACING_NE: + case FACING_E: + case FACING_SE: + shapenum = UnitClass::BodyShape[tfacing] + 96; + shapestart = 0; + //xx -= 4; + break; + + case FACING_W: + default: + shapenum = UnitClass::BodyShape[tfacing]; + shapestart = 6; + xx += 4; + break; + } + + /* + ** Some units have only four facings, but are equipped with a turret + ** that has 32 facings. + */ + if (Class->IsChunkyShape) { + + /* + ** Special wake drawing occurs here for non-virtual rendering + */ + if (*this == UNIT_GUNBOAT) { + + if (window != WINDOW_VIRTUAL) { + //Added this and wake name parameters. ST - 8/20/2019 10:54AM + CC_Draw_Shape(this, "WAKE", UnitTypeClass::WakeShapes, shapestart + (Fetch_Stage() % 6), xx-1, yy+3, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + if (Health_Ratio() < 0x0080) shapenum += 32; + if (Health_Ratio() < 0x0040) shapenum += 32; + + } else { + + /* + ** Special hovercraft shape is ALWAYS N/S. + */ + shapenum = 0; + + //shapenum = ((UnitClass::BodyShape[facing] + 4) / 8) & 0x03; + } + + } else { + + /* + ** Fetch the harvesting animation stage as appropriate. + */ + if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) { + static char _hstage[6] = { + 0, 1, 2, 3, 2, 1 + }; + shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*4)+_hstage[Fetch_Stage()%sizeof(_hstage)]; + } else { + shapenum = UnitClass::BodyShape[facing]; + if (Class->IsAnimating) { + shapenum = Fetch_Stage(); + } + if (Class->IsPieceOfEight) { + shapenum = 0; + int numshapes = (IsDriving || (*this == UNIT_TREX) || (*this == UNIT_RAPT)) ? 8 : 12; + if (facing) shapenum = UnitClass::BodyShape[24+facing]; + if (IsDriving) shapenum = (Fetch_Stage()%numshapes) + 16 + (shapenum*numshapes); + else if (IsFiring) shapenum = (Fetch_Stage()%numshapes) + 80 + (shapenum*numshapes); + } else { + + /* + ** Door opening and closing animation must be handled carefully. There are only + ** certain directions where this door animation will work. + */ + if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) { + if (PrimaryFacing == DIR_NE) { + shapenum = 32; + } else { + if (PrimaryFacing == DIR_NW) { + shapenum = 35; + } + } + shapenum += Door_Stage(); + } + } + } + } + + /* + ** The artillery unit should have its entire body recoil when it fires. + */ + if (*this == UNIT_ARTY && IsInRecoilState) { + Recoil_Adjust(PrimaryFacing.Current(), x, y); + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ +//if (*this == UNIT_HOVER) { +// Mono_Printf("Display hover %p %d.\n", shapefile, shapenum); +//} + Techno_Draw_Object(shapefile, shapenum, x, y, window); + + /* + ** Special wake drawing occurs here for virtual rendering + */ + if (Class->IsChunkyShape && (*this == UNIT_GUNBOAT) && (window == WINDOW_VIRTUAL)) { + //Added this and wake name parameters. ST - 8/20/2019 10:54AM + CC_Draw_Shape(this, "WAKE", UnitTypeClass::WakeShapes, shapestart + (Fetch_Stage() % 6), xx-1, yy+3, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + /* + ** If there is a rotating radar dish, draw it now. + */ + if (Class->IsRadarEquipped) { + shapenum = 32 + (Frame % 32); + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); + } + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (!Class->IsChunkyShape && Class->IsTurretEquipped) { + int x1 = x; + int y1 = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + shapenum = TechnoClass::BodyShape[tfacing]+32; + + /* + ** The shape to use for the rocket launcher is dependant on the + ** ammo remaining. + */ + if (*this == UNIT_MSAM) { + if (Ammo == 0) shapenum += 64; + if (Ammo == 1) shapenum += 32; + } + + /* + ** A recoiling turret moves "backward" one pixel. + */ + if (IsInRecoilState) { + Recoil_Adjust(SecondaryFacing.Current(), x1, y1); + } + + /* + ** The Mobile SAM and the Missile Launchers need their turrets moved based + ** on the facing of the vehicle. + */ + if (*this == UNIT_MSAM || *this == UNIT_MLRS) { + Turret_Adjust(PrimaryFacing, x1, y1); + } + if (*this == UNIT_JEEP || *this == UNIT_BUGGY) { + y1 -= 4; + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, x1, y1, window); + } + + /* + ** If this unit has "piggy back" unit(s), then render it at the same time. + */ + if (*this == UNIT_HOVER && Is_Something_Attached()) { + TechnoClass * u = (TechnoClass *)Attached_Object(); + + int counter = 0; + for (;;) { + int x1,y1; + + if (Map.Coord_To_Pixel(Coord_Add(Coord_Add(Coord, 0xFF80FF80L), StoppingCoordAbs[counter++]), x1, y1)) { + // Pass the window through, so that the virtual window will also work. ST - 6/18/2019 12:00PM + //u->Draw_It(x1, y1, WINDOW_TACTICAL); + u->Draw_It(x1, y1, window); + } + if (!u->Next) break; + u = (TechnoClass *)u->Next; + } + } + } + + /* + ** If this unit is carrying the flag, then draw that on top of everything else. + */ + if (Flagged != HOUSE_NONE) { + CC_Draw_Shape(this, "FLAGFLY", MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, false), Map.UnitShadow, Flagged); + } + + TarComClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * * + * This routine is used to move a harvester to a place where it can load up with * + * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets * + * the navigation computer toward the nearest Tiberium and lets the unit head there * + * automatically. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is it located directly over a Tiberium patch? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Tiberium_Check(CELL ¢er, int x, int y) +{ + Validate(); + /* + ** If the specified offset from the origin will cause it + ** to spill past the map edge, then abort this cell check. + */ + if (Cell_X(center)+x < Map.MapCellX) return(false); + if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false); + if (Cell_Y(center)+y < Map.MapCellY) return(false); + if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false); + + center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if ((GameToPlay != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].Is_Visible(PlayerPtr)))) { + if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } + } + return(false); +} + + +bool UnitClass::Goto_Tiberium(void) +{ + Validate(); + if (!Target_Legal(NavCom)) { + CELL center = Coord_Cell(Center_Coord()); + if (Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } else { + + /* + ** Perform a ring search outward from the center. + */ + for (int radius = 1; radius < 64; radius++) { + for (int x = -radius; x <= radius; x++) { + CELL cell = center; + if (Tiberium_Check(cell, x, -radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, x, +radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, -radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + + cell = center; + if (Tiberium_Check(cell, +radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * * + * This routine is used to by the harvester to harvest Tiberium at the current location. * + * When harvesting is complete, this routine will return true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is harvesting complete? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Harvesting(void) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + CellClass * ptr = &Map[cell]; + + /* + ** Keep waiting if still heading toward a spot to harvest or the harvest timer hasn't expired yet. + */ + if (Target_Legal(NavCom) || !HarvestTimer.Expired() || IsDriving || IsRotating) return(true); + + if (Tiberium_Load() < 0x0100 && ptr->Land_Type() == LAND_TIBERIUM) { + + /* + ** Lift some Tiberium from the ground. Try to lift a complete + ** "level" of Tiberium. A level happens to be 6 steps. If there + ** is a partial level, then lift that instead. Never lift more + ** than the harvester can carry. + */ + int reducer = (ptr->OverlayData % 6) + 1; + reducer = ptr->Reduce_Tiberium(MIN(reducer, UnitTypeClass::STEP_COUNT-Tiberium)); + Tiberium += reducer; + Set_Stage(0); + Set_Rate(2); + + HarvestTimer = TICKS_PER_SECOND; + + } else { + + /* + ** If the harvester is stopped on a non Tiberium field and the harvester + ** isn't loaded with Tiberium, then no further action can be performed + ** by this logic routine. Bail with a failure and thus cause a branch to + ** a better suited logic processor. + */ + Set_Stage(0); + Set_Rate(0); + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo and * + * then exit the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Unload(void) +{ + Validate(); + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case UNIT_APC: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + face; + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + + case UNIT_MCV: + switch (Status) { + case 0: + Path[0] = FACING_NONE; + Status = 1; + break; + + case 1: + if (!IsDriving) { + Try_To_Deploy(); + if (IsDeploying) { + Status = 2; + } else { + /* + ** Functionality from Red Alert for AI. ST - 7/25/2019 3:09PM + */ +#ifndef USE_RA_AI + Assign_Mission(MISSION_GUARD); +#else //USE_RA_AI + if (!House->IsHuman && GameToPlay != GAME_NORMAL) { + Assign_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_GUARD); + } +#endif //USE_RA_AI + } + } + break; + + case 2: +#ifdef USE_RA_AI + /* + ** Functionality from Red Alert for AI. ST - 7/25/2019 3:09PM + */ + if (GameToPlay != GAME_NORMAL) { + if (!IsDeploying) { + Assign_Mission(MISSION_GUARD); + } + } +#endif // USE_RA_AI + break; + } + return(1); + + case UNIT_HOVER: + switch (Status) { + case 0: + if (Unload_Hovercraft_Process()) { + Status = 1; + } + break; + + /* + ** Hovercraft always leave the map when they finish + ** unloading. + */ + case 1: + if (Team) { + Team->Remove(this); + } + Exit_Map(); + break; + } + break; + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * * + * This is the AI process used by harvesters when they are doing their harvesting action. * + * This entails searching for nearby Tiberium, heading there, harvesting, and then * + * returning to a refinery for unloading. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 06/21/1995 JLB : Force guard mode if no Tiberium found. * + *=============================================================================================*/ +int UnitClass::Mission_Harvest(void) +{ + Validate(); + enum { + LOOKING, + HARVESTING, + FINDHOME, + HEADINGHOME, + GOINGTOIDLE, + }; + + /* + ** A non-harvesting type unit will just sit still if it is given the harvest mission. This + ** allows combat units to act "brain dead". + */ + if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30); + + switch (Status) { + + /* + ** Go and find a Tiberium field to harvest. + */ + case LOOKING: + IsHarvesting = false; + if (Goto_Tiberium()) { + IsHarvesting = true; + Set_Rate(2); + Set_Stage(0); + Status = HARVESTING; + return(1); + } else { + + /* + ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then + ** force it to go into guard mode. This will prevent the harvester from repeatedly + ** searching for Tiberium. + */ + if (!Target_Legal(NavCom)) { + + /* + ** If the archive target is legal, then head there since it is presumed + ** that the archive target points to the last place it harvested at. This might + ** solve the case where the harvester gets stuck and can't find Tiberium just because + ** it is greater than 32 squares away. + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + } else { + Status = GOINGTOIDLE; + return(TICKS_PER_SECOND*15); + } + } + } + break; + + /* + ** Harvest at current location until full or Tiberium exhausted. + */ + case HARVESTING: + if (!Harvesting()) { + IsHarvesting = false; + if (Tiberium_Load() == 0x0100) { + Status = FINDHOME; + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } else { + if (!Goto_Tiberium() && !Target_Legal(NavCom)) { + ArchiveTarget = TARGET_NONE; + Status = FINDHOME; + } else { + Status = HARVESTING; + IsHarvesting = true; + } + } + return(1); + } + break; + + /* + ** Find and head to refinery. + */ + case FINDHOME: + if (!Target_Legal(NavCom)) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (nearest && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + } else { + ScenarioInit++; + nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + ScenarioInit--; + if (nearest) { + Assign_Destination(::As_Target(nearest->Nearby_Location(this))); + } + } + } + break; + + /* + ** In communication with refinery so that it will successfully dock and + ** unload. If, for some reason, radio contact was lost, then hunt for + ** another refinery to unload at. + */ + case HEADINGHOME: + Assign_Mission(MISSION_ENTER); + return(1); + + case GOINGTOIDLE: + Assign_Mission(MISSION_GUARD); + break; + + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * * + * Computer controlled units must be intelligent enough to find enemies as well as to * + * attack them. This AI process will handle both the simple attack process as well as the * + * scanning for enemy units to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Hunt(void) +{ + Validate(); + if (*this == UNIT_MCV) { + switch (Status) { + + /* + ** This stage handles locating a convenient spot, rotating to face the correct + ** direction and then commencing the deployment operation. + */ + case 0: + if (Goto_Clear_Spot()) { + if (Try_To_Deploy()) { + Status = 1; + } + } + break; + + /* + ** This stage watchdogs the deployment operation and if for some reason, the deployment + ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for + ** a convenient spot to deploy. + */ + case 1: + if (!IsDeploying) { + Status = 0; + } + break; + } + } else { + + if (*this == UNIT_GUNBOAT) { + if (!Target_Legal(NavCom)) { + if (PrimaryFacing == DIR_W) { + Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(Coord)))) ); + } else { + Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(Coord_Cell(Coord)))) ); + } + Set_Speed(255); + } + if (!Speed) { + Set_Speed(255); + } + if (!Target_Legal(TarCom) || !In_Range(TarCom, 0)) { + Target_Something_Nearby(THREAT_AREA); + } + } else { + return(TarComClass::Mission_Hunt()); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Look -- Perform map revelation from a unit's position. * + * * + * Reveal the map around the specified unit with the sighting range * + * associated with the specified unit. * + * * + * INPUT: incremental -- If looking is to process only the cells in the * + * outer ring of the unit's search radius, then * + * set this parameter to true. This method is * + * quite a bit faster than processing all cells, * + * but must be used with caution. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/08/1992 JLB : Created. * + * 03/08/1994 JLB : Added incremental option. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void UnitClass::Look(bool incremental) +{ + Validate(); + //if (!IsInLimbo && IsOwnedByPlayer) { // Changed for mapping of multiple players + if (!IsInLimbo && House && House->IsHuman) { + int sight = Class->SightRange; + + if (sight) { + Map.Sight_From(House, Coord_Cell(Coord), sight, (*this == UNIT_GUNBOAT) ? false : incremental); // Passed our house into Map.Sight_From since it now needs to know who it is performing the action on behalf of. ST - 3/28/2019 2:55PM + } + } +} + + +/*********************************************************************************************** + * UnitClass::Overlap_List -- Determines overlap list for units. * + * * + * The unit overlap list is used to keep track of which cells are to * + * be marked for redraw. This is critical in order to keep the units * + * displayed correctly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the overlap list pointer for the unit at its * + * present position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1994 JLB : Created. * + * 06/19/1994 JLB : Uses Coord_Spillable_List function. * + *=============================================================================================*/ +short const * UnitClass::Overlap_List(void) const +{ + Validate(); + static short const _gunboat[] = {-3, -2, 2, 3, REFRESH_EOL}; + int size; + + /* + ** The gunboat is a special case. + */ + if (*this == UNIT_GUNBOAT) { + return(&_gunboat[0]); + } + + size = ICON_PIXEL_W; + if (Is_Selected_By_Player() || IsFiring) { + size += 24; + } + if (Is_Selected_By_Player() || Class->IsGigundo || IsAnimAttached) { + size = ICON_PIXEL_W*2; + } + return(Coord_Spillage_List(Coord, size)+1); +} + + +#ifdef NEVER +/********************************************************************************************* * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * * + * This routine is used by the Can_Enter_Cell logic when an object is in the desired cell * + * and it needs to know if that causes blockage. If blocked, this routine will return why. * + * * + * INPUT: TechnoClass * pointer to object that is blocking unit * + * * + * CELL the cell the unit is being blocked in * + * * + * OUTPUT: MoveBitType the way that the object is blocking the unit * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +MoveBitType UnitClass::Blocking_Object(TechnoClass const *techno, CELL cell) const +{ + Validate(); + /* + ** There are some extra checks we need to make if the techno is a unit + */ + bool unit = (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT); + CellClass const * cellptr = &Map[cell]; + + if (House->Is_Ally(techno)) { + + if (techno == Contact_With_Whom() && IsTethered) { + return(MOVE_BIT_OK); + } + + if (unit) { + /* + ** If the unit in question has a destination than we should + ** be prepared to wait for the unit to get out of our way. + */ + if (((FootClass *)techno)->NavCom != TARGET_NONE) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)techno)->PrimaryFacing) ^4; + if (face != techface && Distance((AbstractClass const *)techno) > 0x1FF) { + return(MOVE_BIT_MOVING_BLOCK); + } else { +// Mono_Printf("Move No!\r"); + return(MOVE_BIT_NO); + } + } + + return(MOVE_BIT_TEMP); + } + } else { + + /* + ** If its an enemy unit, things are dealt with a little differently + */ + if (unit) { + +#ifdef NEVER + /* + ** If this is an enemy unit and we are not doing a find path then + ** we need to tell the unit to uncloak just in case it is a + ** stealth tank. + */ + if (!IsFindPath) { + techno->Do_Uncloak(); + } +#endif + + /* + ** Can we just run it over? + */ + if (techno->Class_Of().IsCrushable && (cellptr->Flag.Composite & 0xE0) == 0 && Class->IsCrusher) { + + /* + ** Now lets run it over. + */ + return(MOVE_BIT_OK); + } + + /* + ** If the object is cloaked, then consider it passable for findpath purposes, + ** but not so for all other cases. + */ + if (techno->Cloak == CLOAKED) { + if (House == techno->House) return(MOVE_BIT_NO); + if (IsFindPath) return(MOVE_BIT_OK); + return(MOVE_BIT_CLOAK); + } + + /* + ** If our vehicle is weapon equipped, then report that the cell occupier + ** needs only to be destroyed in order to make the cell passable. + */ + if (Class->Primary != WEAPON_NONE) { + return(MOVE_BIT_DESTROYABLE); + } + } + } + return(MOVE_BIT_NO); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * * + * Use this routine to determine if the unit can enter the cell * + * specified and given the direction of entry specified. Typically, * + * this is used when determining unit travel path. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The facing that the unit would enter the specified * + * cell. If this value is -1, then don't consider * + * facing when performing the check. * + * * + * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is * + * allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/16/1994 JLB : Converted to member function. * + * 07/04/1995 JLB : Allowed to drive on building trying to enter it. * + *=============================================================================================*/ +MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + CellClass const * cellptr = &Map[cell]; + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** The gunboat can always move. This prevents it from trying to move around possible hover + ** craft blockage. + */ + if (*this == UNIT_GUNBOAT) return(MOVE_OK); + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** Certain vehicles can drive over walls. Check for this case and + ** and return the appropriate flag. Other units treat walls as impassable. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrate && !House->IsHuman) { + return(MOVE_NO); + } + + if (optr->IsWall) { + if (Class->Primary != WEAPON_NONE) { + WarheadTypeClass const * whead = &Warheads[BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead]; + + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } else { + return(MOVE_NO); + } + } else { + return(MOVE_NO); + } + } + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ +#ifdef ADVANCED + if (retval != MOVE_DESTROYABLE && !Ground[cellptr->Land_Type()].Cost[Class->Speed]) { +#else + if (!Ground[cellptr->Land_Type()].Cost[Class->Speed]) { +#endif + return(MOVE_NO); + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + bool crushable = false; + ObjectClass *obj = cellptr->Cell_Occupier(); + while (obj) { + + if (obj != this) { + + /* + ** Always allow entry if trying to move on a cell with + ** authorization from the occupier. + */ + if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) { + return(MOVE_OK); + } + + bool is_moving = (obj->What_Am_I() == RTTI_INFANTRY || obj->What_Am_I() == RTTI_UNIT) && Target_Legal(((FootClass *)obj)->NavCom); + + if (House->Is_Ally(obj)) { + if (is_moving) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4; + if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) { + return(MOVE_NO); + } + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO); + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** If this unit can crush infantry, and there is an enemy infantry in the + ** cell, don't consider the cell impassible. This is true even if the unit + ** doesn't contain a legitimate weapon. + */ + if (!Class->IsCrusher || !obj->Class_Of().IsCrushable) { + + /* + ** Any non-allied blockage is considered impassible if the unit + ** is not equipped with a weapon. + */ + if (Class->Primary == WEAPON_NONE) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the unit is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + if (((TerrainClass *)obj)->Class->IsFlammable && + BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + crushable = true; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (retval == MOVE_OK && !crushable && cellptr->Flag.Composite) { + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } else { + if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) { + retval = MOVE_MOVING_BLOCK; + } else { + + /* + ** Enemy infantry have reserved the cell. If this unit can crush + ** infantry, consider the cell passable. If not, then consider the + ** cell destroyable if it has a weapon. If neither case applies, then + ** this vehicle should avoid the cell altogether. + */ + if (!Class->IsCrusher) { + if (Class->Primary != WEAPON_NONE) { + retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + } + } + + /* + ** If its ok to move into the cell because we can crush whats in the cell, then + ** make sure no one else is already moving into the cell to crush something. + */ + if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) { + +#ifdef ADVANCED + /* + ** However, if the cell is occupied by a crushable vehicle, then we can + ** never be sure if some other friendly vehicle is also trying to crush + ** the cell at the same time. In the case of a crushable vehicle in the + ** cell, then allow entry. + */ + if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) { + return(MOVE_MOVING_BLOCK); + } +#else + return(MOVE_MOVING_BLOCK); +#endif + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * UnitClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the unit list and unit objects. This routine is typically * + * used in preparation for a new scenario load. All units are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Init(void) +{ + UnitClass * ptr; + + Units.Free_All(); + + ptr = new UnitClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. * + * * + * Sometimes the coordinate to use when targeting an object is not the same as its center * + * coordinate. This is especially true for boats since leading their movement is needed * + * in order have any chance of hitting. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire upon when attacking the unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Target_Coord(void) const +{ + Validate(); +// if (*this == UNIT_GUNBOAT) { +// return(Coord_Move(Coord, PrimaryFacing.Current(), 0x0080)); +// } + return(TarComClass::Center_Coord()); +} + + +/*********************************************************************************************** + * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * * + * This routine is called when the unit discovers that it should get out of the "hot seat" * + * and move to an adjacent cell. Since the safety of the adjacent cell is not determined * + * before the move begins, it will appear that the unit is just scattering (which it * + * should). * + * * + * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move * + * roughly away from the threat. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + Validate(); + if (*this != UNIT_GUNBOAT && *this != UNIT_HOVER) { + if (!PrimaryFacing.Is_Rotating() && ((!Target_Legal(TarCom) && !Target_Legal(NavCom)) || forced || nokidding || Random_Pick(1, 4) == 1)) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + (Random_Pick(0, 2)-1); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Destination(::As_Target(newcell)); + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * * + * This routine will remove the "reservation" flag (if present) when the vehicle is * + * required to stop movement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Stop_Driver(void) +{ + Validate(); + + /* + ** We only need to do something if the vehicle is actually going + ** somewhere. + */ + if (Head_To_Coord()) { + + /* + ** Safe off whether the vehicle is down or not so we know whether + ** we have to put it back down. + */ + int temp = IsDown; + + /* + ** If the vehicle is down, pick it up so it doesnt interfere with + ** our flags. + */ + if (temp) { + Mark(MARK_UP); + } + + /* + ** Call the drive class function which will let us release the + ** reserved track. + */ + Mark_Track(Head_To_Coord(), MARK_UP); + + /* + ** If it was down it should be down when we are done. + */ + if (temp) { + Mark(MARK_DOWN); + } + } + return(TarComClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * * + * This routine will start the vehicle moving by marking the destination cell as * + * reserved. Cells must be reserved in this fashion or else they might become occupied as * + * the vehicle is proceeding toward it. * + * * + * INPUT: headto -- The location where the vehicle will be heading. * + * * + * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell * + * being occupied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Start_Driver(COORDINATE & headto) +{ + Validate(); + if (TarComClass::Start_Driver(headto)) { + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Limbo -- Prepares vehicle and then limbos it. * + * * + * This routine removes the occupation bits for the vehicle and also handles cleaning up * + * any vehicle reservation bits. After this, it then proceeds with limboing the unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + Stop_Driver(); + } + return(TarComClass::Limbo()); +} + + +/*********************************************************************************************** + * UnitClass::Response_Select -- Voice feedback when selecting the unit. * + * * + * This is the voice to play when the unit is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Select(void) +{ + Validate(); + static VocType _response[] = { + VOC_VEHIC, + VOC_UNIT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOYES; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. * + * * + * This plays the audio feedback when ordering this unit to move to a new destination. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Move(void) +{ + Validate(); + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ACKNOWL, + VOC_AFFIRM, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOMOUT; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * * + * This plays the audio feedback when ordering this unit to attack. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Attack(void) +{ + Validate(); + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOMOUT; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/11/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = TarComClass::What_Action(object); + + /* + ** If the unit doesn't have a weapon, but can crush the object, then consider + ** the object as a movable location. + */ + if (action == ACTION_ATTACK && !Can_Player_Fire()) { + if (Class->IsCrusher && object->Class_Of().IsCrushable) { + action = ACTION_MOVE; + } else { + action = ACTION_SELECT; + } + } + + /* + ** Don't allow special deploy action unless there is something to deploy. + */ + if (action == ACTION_SELF) { + if (*this != UNIT_MCV) { + if (!Class->IsTransporter || !How_Many()) { + action = ACTION_NONE; + } + } else { + ((ObjectClass &)(*this)).Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + action = ACTION_NO_DEPLOY; + } + ((ObjectClass &)(*this)).Mark(MARK_DOWN); + } + } + + /* + ** Special return to friendly refinery action. + */ + if (IsOwnedByPlayer && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) { + if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { + action = ACTION_ENTER; + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (IsOwnedByPlayer && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_MOVE; + } + } + + return(action); +} + + +ActionType UnitClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = TarComClass::What_Action(cell); + if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) { + return(ACTION_HARVEST); + } + return(action); +} + + +/*********************************************************************************************** + * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. * + * * + * Use this routine to see if the player can move this object. If the player can move the * + * object, even only in theory, then this function returns true. In all other cases, such * + * as for enemy units, gunboats, or hovercraft, it returns false. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the player give this object a movement order? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Can_Player_Move(void) const +{ + Validate(); + return(TarComClass::Can_Player_Move() && *this != UNIT_GUNBOAT && *this != UNIT_HOVER); +} + + +/*********************************************************************************************** + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Read_INI(char *buffer) +{ + UnitClass *unit; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType inhouse; // Unit house. + UnitType classid; // Unit class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = UnitTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != UNIT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + unit = new UnitClass(classid, inhouse); + if (unit) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\r\n"))); + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + unit->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\r\n")); + if (unit->Trigger) { + unit->Trigger->AttachCount++; + } + + if (unit->Unlimbo(coord, dir)) { + unit->Strength = Fixed_To_Cardinal(unit->Class->MaxStrength, strength); + if (GameToPlay == GAME_NORMAL || unit->House->IsHuman) { + unit->Assign_Mission(mission); + unit->Commence(); + } else { + unit->Enter_Idle_Mode(); + } + + /* + ** The gunboat is a special case: It must "drive" off the edge of the map. + ** Just pick the map edge that it is facing and set that as the destination + ** of the drive. + */ + if (*unit == UNIT_GUNBOAT) { + unit->PrimaryFacing.Set_Desired(DIR_W); + unit->PrimaryFacing.Set_Current(DIR_W); + unit->Assign_Mission(MISSION_HUNT); + unit->Commence(); + unit->Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(unit->Coord))))); + } + + } else { + + /* + ** If the unit could not be unlimboed, then this is a catastrophic error + ** condition. Delete the unit. + */ + delete unit; + } + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * UnitClass::Write_INI -- Writes all the units out to an INI file. * + * * + * This routine writes all of the units in the game out to an INI file. This is used * + * in the scenario editor when the game needs to be saved. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- A pointer to the loaded INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit; + + unit = Units.Ptr(index); + if (!unit->IsInLimbo && unit->IsActive) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio(), + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission), + unit->Trigger ? unit->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Exit_Repair -- Drive the unit off the repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +#define XYCELL(x,y) (y*MAP_CELL_W+x) +void UnitClass::Exit_Repair(void) +{ + Validate(); + int i; + CELL cell; + bool found = false; + static short const ExitRepair[] = { + XYCELL(0,-2), + XYCELL(1,-1), + XYCELL(2, 0), + XYCELL(1, 1), + XYCELL(0, 2), + XYCELL(-1,1), + XYCELL(-2,0), + XYCELL(-1,-1) + }; + + cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())]; + if (Can_Enter_Cell(cell) == MOVE_OK) found = true; + + if (!found) for (i=0; i<8; i++) { + cell = Coord_Cell(Coord) + ExitRepair[i]; + if (Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } + if (found) { + DirType dir = Direction(cell); + + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * * + * This routine will intercept the guard mission and if it is for a hovercraft, assign * + * it the unload mission instead. This prevents the hovercraft from being stuck in the * + * water if something unexpected causes it to drop into guard mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the time delay before this command is executed again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/08/1995 JLB : Fixes gunboat problems. * + *=============================================================================================*/ +int UnitClass::Mission_Guard(void) +{ + Validate(); + if (*this == UNIT_HOVER) { + if (Is_Something_Attached()) { + Assign_Mission(MISSION_UNLOAD); + Find_LZ(); + } else { + Exit_Map(); + } + return(TICKS_PER_SECOND); + } + + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + + if (*this == UNIT_HARVESTER && !House->IsHuman) { + Assign_Mission(MISSION_HARVEST); + return(TICKS_PER_SECOND); + } + +#ifdef USE_RA_AI + /* + ** Copied functionality from RA for AI. ST - 7/25/2019 3:11PM + */ + if (GameToPlay != GAME_NORMAL) { + if (*this == UNIT_MCV && House->IsBaseBuilding && House->IsHuman == false) { + Assign_Mission(MISSION_UNLOAD); + return(TICKS_PER_SECOND + Random_Pick(0, 2)); + } + } +#endif // USE_RA_AI + + return(TarComClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * * + * This routine intercepts the normal move mission and if a gunboat is being processed, * + * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission * + * regardless of what the player did. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Move(void) +{ + Validate(); + IsHarvesting = false; + + /* + ** Always make sure that that transport door is closed if the vehcile is moving. + */ + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + + /* + ** Gunboats must always have the hunt mission. + */ + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + return(TarComClass::Mission_Move()); +} + + +/*********************************************************************************************** + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + Validate(); + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS(Dir_Diff(Facing_Dir(face), faceto)); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; + + /* + ** If the value for the potiential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_S); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Attack -- Handles the mission attack logic. * + * * + * This routine intercepts the normal mission attack logic. If a gunboat is assigned the * + * attack mission then it must be converted back to a hunt mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Attack(void) +{ + Validate(); + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + return(TarComClass::Mission_Attack()); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * * + * This routine will attach a house flag to this unit. * + * * + * INPUT: house -- The house that is having its flag attached to it. * + * * + * OUTPUT: Was the house flag successfully attached to this unit? * + * * + * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure * + * of this routine. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Attach(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && Flagged == HOUSE_NONE) { + Flagged = house; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * * + * This routine will remove the house flag that is attached to this unit. * + * * + * INPUT: none * + * * + * OUTPUT: Was the flag successfully removed? * + * * + * WARNINGS: This routine doesn't put the flag into a new location. That operation must * + * be performed or else the house flag will cease to exist. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Remove(void) +{ + Validate(); + if (Flagged != HOUSE_NONE) { + Flagged = HOUSE_NONE; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Stun -- Stuns the unit in preparation for unit removal. * + * * + * This routine intercepts the stun operation for the unit and if there is a house flag * + * attached, it will drop it to the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Stun(void) +{ + Validate(); + if (Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord)); + } + TarComClass::Stun(); +} + + +/*********************************************************************************************** + * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the unit. * + * This will either be the number of passengers or the percentage full (in 1/5ths) of * + * a harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Pip_Count(void) const +{ + Validate(); + if (Class->IsTransporter) { + return(How_Many()); + } + if (Class->IsToHarvest) { + return(Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS/100, Tiberium_Load())); + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::APC_Close_Door -- Closes an APC door. * + * * + * This routine will initiate closing of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Close_Door(void) +{ + Validate(); + Close_Door(10, 2); +} + + +/*********************************************************************************************** + * UnitClass::APC_Open_Door -- Opens an APC door. * + * * + * This routine will initiate opening of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Open_Door(void) +{ + Validate(); + if (!IsDriving && !IsRotating) { + if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) { + Open_Door(10, 2); + } else { + Open_Door(1, 2); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Remap_Table -- Fetches the remap table to use for this object. * + * * + * Use this routine to determine the rendering remap table to use for this object. The * + * remap table is normally the unit remap table, except for the MCV and the Harvestor. * + * These units use the building remap table since these units become part of the building * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * UnitClass::Remap_Table(void) +{ + Validate(); + if (*this == UNIT_MCV || *this == UNIT_HARVESTER) { + return(House->Remap_Table(IsBlushing, false)); + } + return(TarComClass::Remap_Table()); +} + + +/*********************************************************************************************** + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * * + * When a unit is destroyed, a crew member might be generated. This routine will return * + * with the infantry type to produce for this unit. This routine will be called for every * + * survivor that is generated. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType UnitClass::Crew_Type(void) const +{ + Validate(); + if (Class->Primary == WEAPON_NONE) { + if (Random_Pick(0, 1) == 0) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + } + return(TarComClass::Crew_Type()); +} + + +/*********************************************************************************************** + * UnitClass::What_Am_I -- Returns with the RTTI type this object is. * + * * + * This will return that this is a normal vehicle unit type. Each object class overrides * + * this function in order to provide run time type identification support. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the RTTI type that this object is (i.e., RTTI_UNIT). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +RTTIType UnitClass::What_Am_I(void) const +{ + Validate(); + return(RTTI_UNIT); +} \ No newline at end of file diff --git a/TIBERIANDAWN/UNIT.H b/TIBERIANDAWN/UNIT.H new file mode 100644 index 000000000..26f75f25b --- /dev/null +++ b/TIBERIANDAWN/UNIT.H @@ -0,0 +1,212 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\unit.h_v 2.19 16 Oct 1995 16:45:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef UNIT_H +#define UNIT_H + +#include "tarcom.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + + +/**************************************************************************** +** For each instance of a unit (vehicle) in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular unit. +*/ +class UnitClass : public TarComClass +{ + public: + + /* + ** This records the house flag that this object is currently carrying. + */ + HousesType Flagged; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + UnitClass(void) {}; + UnitClass(UnitType classid, HousesType house); + operator UnitType(void) const {return Class->Type;}; + virtual ~UnitClass(void); + virtual RTTIType What_Am_I(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + bool Goto_Clear_Spot(void); + bool Try_To_Deploy(void); + + bool Tiberium_Check(CELL ¢er, int x, int y); + bool Flag_Attach(HousesType house); + bool Flag_Remove(void); + void Find_LZ(void); + bool Unload_Hovercraft_Process(void); + bool Goto_Tiberium(void); + bool Harvesting(void); + void APC_Close_Door(void); + void APC_Open_Door(void); + + /* + ** Query functions. + */ + virtual bool Can_Player_Move(void) const; + virtual int Pip_Count(void) const; + virtual InfantryType Crew_Type(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual bool Limbo(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual void Look(bool incremental=false); + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + + /* + ** ST Added 1/15/2019 2:44PM + */ + //virtual bool Get_Draw_Parameters(void); + + /* + ** User I/O. + */ + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass * object) const; + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ + virtual COORDINATE Target_Coord(void) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual void Stun(void); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Guard(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int UnitClass::Mission_Move(void); + virtual FireErrorType Can_Fire(TARGET, int which) const; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Enter_Idle_Mode(bool initial=false); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual void Per_Cell_Process(bool center); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + void Exit_Repair(void); +// MoveType Blocking_Object(TechnoClass const *techno, CELL cell) const; + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "UNITS";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** Timer to prevent Harvesters from collecting too often + */ + TCountDownTimerClass HarvestTimer; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/UTRACKER.CPP b/TIBERIANDAWN/UTRACKER.CPP new file mode 100644 index 000000000..38b821126 --- /dev/null +++ b/TIBERIANDAWN/UTRACKER.CPP @@ -0,0 +1,239 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + * * + * Functions: * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#include "function.h" + + +/*********************************************************************************************** + * UTC::UnitTrackerClass -- Class constructor * + * * + * * + * * + * INPUT: Number of unit types to reserve space for * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::UnitTrackerClass (int unit_count) +{ + UnitTotals = new long [unit_count]; // Allocate memory for the unit totals + UnitCount = unit_count; // Keep a record of how many unit entries there are + InNetworkFormat = 0; // The unit entries are in host format + Clear_Unit_Total(); // Clear each entry +} + + +/*********************************************************************************************** + * UTC::~UnitTrackerClass -- Class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::~UnitTrackerClass (void) +{ + delete UnitTotals; +} + + + +/*********************************************************************************************** + * UTC::Increment_Unit_Total -- Increment the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:12AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Increment_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]++; +} + + +/*********************************************************************************************** + * UTC::Decrement_Unit_Total -- Decrement the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Decrement_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]--; +} + + +/*********************************************************************************************** + * UTC::Get_All_Totals -- Returns a pointer to the start of the unit totals list * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Ptr to unit totals list * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +long *UnitTrackerClass::Get_All_Totals (void) +{ + return (UnitTotals); +} + + +/*********************************************************************************************** + * UTC::Clear_Unit_Total -- Clear out all the unit totals * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:14AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Clear_Unit_Total (void) +{ + memset (UnitTotals, 0, UnitCount * sizeof(long) ); +} + + + +/*********************************************************************************************** + * UTC::To_Network_Format -- Changes all unit totals to network format for the internet * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:15AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::To_Network_Format (void) +{ + if (!InNetworkFormat){ + for (int i=0 ; i= 0 && unit_type < UnitCount) + { + return UnitTotals[unit_type]; + } + return 0; +} + + diff --git a/TIBERIANDAWN/UTRACKER.H b/TIBERIANDAWN/UTRACKER.H new file mode 100644 index 000000000..35c216b63 --- /dev/null +++ b/TIBERIANDAWN/UTRACKER.H @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** UnitTracker Class +*/ + +class UnitTrackerClass { + + public: + + UnitTrackerClass(int unit_count); + ~UnitTrackerClass(void); + + void Increment_Unit_Total (int unit_type); + void Decrement_Unit_Total (int unit_type); + void Clear_Unit_Total(void); + + int Get_Unit_Total (int unit_type); + long *Get_All_Totals (void); + int Get_Unit_Count (void){return (UnitCount);}; + + void To_Network_Format(void); + void To_PC_Format(void); + + private: + + long *UnitTotals; + int UnitCount; + int InNetworkFormat; + +}; + + + + + + + + + + + + + + + + diff --git a/TIBERIANDAWN/VECTOR.CPP b/TIBERIANDAWN/VECTOR.CPP new file mode 100644 index 000000000..2c71426f0 --- /dev/null +++ b/TIBERIANDAWN/VECTOR.CPP @@ -0,0 +1,898 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\vector.cpv 2.17 16 Oct 1995 16:49:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : July 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BooleanVectorClass::BooleanVectorClass -- Copy constructor fo boolean array. * + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * BooleanVectorClass::operator = -- Assignment operator. * + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vector.h" +//#include +#include + +/* +** The following template function can be located here ONLY if all the instatiations are +** declared in a header file this module includes. By placing the template functions here, +** it speeds up compiler operation and reduces object module size. +*/ +#if (0) // Moved to header + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) +{ + Vector = 0; + VectorMax = size; + IsAllocated = false; + + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) +{ + VectorMax = 0; + IsAllocated = false; + Vector = 0; + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasable) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assigment operator will + ** only work for the simplist of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previosly allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previosly and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if ((unsigned)ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + int index = ID(object); + if (index != -1){ + return(Delete(index)); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if ((unsigned)index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +#endif //Moved to header + + +//---------------------------------------------------------------------------------------------- + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * * + * This is the constructor for a boolean array. This constructor takes the memory pointer * + * provided as assigns that as the array data pointer. * + * * + * INPUT: size -- The size of the array (in bits). * + * * + * array -- Pointer to the memory that the array is to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must make sure that the memory specified is large enough to contain the * + * bits specified. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(unsigned size, unsigned char * array) +{ + BitArray.Resize(((size + (8-1)) / 8), array); + LastIndex = -1; + BitCount = size; +} + + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Copy constructor fo boolean array. * + * * + * This is the copy constructor for a boolean array. It is used to make a duplicate of the * + * boolean array. * + * * + * INPUT: vector -- Reference to the vector to be duplicated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(BooleanVectorClass const & vector) +{ + LastIndex = -1; + *this = vector; +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator = -- Assignment operator. * + * * + * This routine will make a copy of the specifed boolean vector array. The vector is * + * copied into an already constructed existing vector. The values from the existing vector * + * are destroyed by this copy. * + * * + * INPUT: vector -- Reference to the vector to make a copy of. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass & BooleanVectorClass::operator =(BooleanVectorClass const & vector) +{ + Fixup(); + Copy = vector.Copy; + LastIndex = vector.LastIndex; + BitArray = vector.BitArray; + BitCount = vector.BitCount; + return(*this); +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * * + * This is the comparison operator for a boolean vector class. Boolean vectors are equal * + * if the bit count and bit values are identical. * + * * + * INPUT: vector -- Reference to the vector to compare to. * + * * + * OUTPUT: Are the boolean vectors identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::operator == (const BooleanVectorClass & vector) +{ + Fixup(LastIndex); + return(BitCount == vector.BitCount && BitArray == vector.BitArray); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * * + * This routine will resize the boolean vector object. An index value used with a boolean * + * vector must be less than the value specified in as the new size. * + * * + * INPUT: size -- The new maximum size of this boolean vector. * + * * + * OUTPUT: Was the boolean vector sized successfully? * + * * + * WARNINGS: The boolean array might be reallocated or even deleted by this routine. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::Resize(unsigned size) +{ + Fixup(); + + if (size) { + + /* + ** Record the previous bit count of the boolean vector. This is used + ** to determine if the array has grown in size and thus clearing is + ** necessary. + */ + int oldsize = BitCount; + + /* + ** Actually resize the bit array. Since this is a bit packed array, + ** there are 8 elements per byte (rounded up). + */ + int success = BitArray.Resize(((size + (8-1)) / 8)); + + /* + ** Since there is no default constructor for bit packed integers, a manual + ** clearing of the bits is required. + */ + BitCount = size; + if (success && oldsize < (int)size) { + for (int index = oldsize; index < (int)size; index++) { + (*this)[index] = 0; + } + } + + return(success); + } + + /* + ** Resizing to zero is the same as clearing and deallocating the array. + ** This is always successful. + */ + Clear(); + return(true); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * * + * This routine will clear out the boolean array. This will free any allocated memory and * + * result in the boolean vector being unusable until the Resize function is subsiquently * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The boolean vector cannot be used until it is resized to a non null condition. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Clear(void) +{ + Fixup(); + BitCount = 0; + BitArray.Clear(); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * * + * This is the preferred (and quick) method to clear the boolean array to a false condition.* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Reset(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\0', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * * + * This is the preferred (and fast) way to set all boolean elements to true. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Set(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\xFF', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * * + * Use this routine to set the boolean value copy to match the appropriate bit in the * + * boolean array. The boolean array will be updated with any changes from the last time * + * a value was fetched from the boolean vector. By using this update method, the boolean * + * array can be treated as a normal array even though the elements are composed of * + * otherwise inaccessable bits. * + * * + * INPUT: index -- The index to set the new copy value to. If the index is -1, then the * + * previous value will be updated into the vector array, but no new value * + * will be fetched from it. * + * * + * OUTPUT: none * + * * + * WARNINGS: Always call this routine with "-1" if any direct manipulation of the bit * + * array is to occur. This ensures that the bit array is accurate. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Fixup(int index) const +{ + /* + ** If the requested index value is illegal, then force the index + ** to be -1. This is the default non-index value. + */ + if (index >= BitCount) { + index = -1; + } + + /* + ** If the new index is different than the previous index, there might + ** be some fixing up required. + */ + if (index != LastIndex) { + + /* + ** If the previously fetched boolean value was changed, then update + ** the boolean array accordingly. + */ + if (LastIndex != -1) { + Set_Bit((void*)&BitArray[0], LastIndex, Copy); + } + + /* + ** If this new current index is valid, then fill in the reference boolean + ** value with the approriate data from the bit array. + */ + if (index != -1) { + ((unsigned char&)Copy) = Get_Bit(&BitArray[0], index); + } + + ((int &)LastIndex) = index; + } +} + diff --git a/TIBERIANDAWN/VECTOR.H b/TIBERIANDAWN/VECTOR.H new file mode 100644 index 000000000..830f8b6dd --- /dev/null +++ b/TIBERIANDAWN/VECTOR.H @@ -0,0 +1,988 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\vector.h_v 2.15 16 Oct 1995 16:47:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::Resize -- Changes the size of the vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::ID -- Finds object ID based on value. * + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VECTOR_H +#define VECTOR_H + +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif + +#include +#include + +inline void * operator new(size_t , void * pointer) {return(pointer);} +inline void * operator new[](size_t , void * pointer) {return(pointer);} + + +/************************************************************************** +** This is a general purpose vector class. A vector is defined by this +** class, as an array of arbitrary objects where the array can be dynamically +** sized. Because is deals with arbitrary object types, it can handle everything. +** As a result of this, it is not terribly efficient for integral objects (such +** as char or int). It will function correctly, but the copy constructor and +** equality operator could be highly optimized if the integral type were known. +** This efficiency can be implemented by deriving an integral vector template +** from this one in order to supply more efficient routines. +*/ +template +class VectorClass +{ + public: + VectorClass(unsigned size=0, T const * array=0); + VectorClass(VectorClass const &); // Copy constructor. + virtual ~VectorClass(void); + + T & operator[](unsigned index) {return(Vector[index]);}; + T const & operator[](unsigned index) const {return(Vector[index]);}; + virtual VectorClass & operator =(VectorClass const &); // Assignment operator. + virtual int operator == (VectorClass const &) const; // Equality operator. + virtual int Resize(unsigned newsize, T const * array=0); + virtual void Clear(void); + unsigned Length(void) const {return VectorMax;}; + virtual int ID(T const * ptr); // Pointer based identification. + virtual int ID(T const & ptr); // Value based identification. + + protected: + + /* + ** This is a pointer to the allocated vector array of elements. + */ + T * Vector; + + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; + + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated:1; +}; + + +/************************************************************************** +** This derivative vector class adds the concept of adding and deleting +** objects. The objects are packed to the beginning of the vector array. +** If this is instantiated for a class object, then the assignment operator +** and the equality operator must be supported. If the vector allocates its +** own memory, then the vector can grow if it runs out of room adding items. +** The growth rate is controlled by setting the growth step rate. A growth +** step rate of zero disallows growing. +*/ +template +class DynamicVectorClass : public VectorClass +{ + public: + DynamicVectorClass(unsigned size=0, T const * array=0); + + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const * array=0); + + // Resets and frees the vector array. + virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + + // Fetch number of "allocated" vector objects. + int Count(void) const {return(ActiveCount);}; + + // Add object to vector (growing as necessary). + int Add(T const & object); + int Add_Head(T const & object); + + // Delete object just like this from vector. + int Delete(T const & object); + + // Delete object at this vector index. + int Delete(int index); + + // Deletes all objects in the vector. + void Delete_All(void) {ActiveCount = 0;}; + + // Set amount that vector grows by. + int Set_Growth_Step(int step) {return(GrowthStep = step);}; + + // Fetch current growth step rate. + int Growth_Step(void) {return GrowthStep;}; + + virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; + virtual int ID(T const & ptr); + + protected: + + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; + + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; +}; + + +/************************************************************************** +** A fixed-size array of dynamic vectors. +*/ +template +class DynamicVectorArrayClass +{ +public: + static const int COUNT = COUNT; + + DynamicVectorArrayClass() : Active(DEFAULT) {} + + void Set_Active_Context(int active) + { + Active = active; + } + + void Clear_All() + { + for (int i = FIRST; i < COUNT; ++i) + { + Clear(i); + } + } + + void Clear() + { + Clear(Active); + } + + int Count() const + { + return Count(Active); + } + + int Add(T const & object) + { + return Add(Active, object); + } + + int Add_Head(T const & object) + { + return Add_Head(Active, object); + } + + int Delete(T const & object) + { + return Delete(Active, object); + } + + int Delete_All(T const & object) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + count += Delete(i, object); + } + return count; + } + + int Delete_All_Except(T const & object, int except) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + if (except != i) + { + count += Delete(i, object); + } + } + return count; + } + + int Delete(int index) + { + return Delete(Active, index); + } + + T & operator[](unsigned index) + { + return Collection[Active][index]; + } + + T const & operator[](unsigned index) const + { + return Collection[Active][index]; + } + + void Clear(int context) + { + Collection[context].Clear(); + } + + int Count(int context) const + { + return Collection[context].Count(); + } + + int Add(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Add_Head(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Delete(int context, T const & object) + { + return Collection[context].Delete(object); + } + + int Delete(int context, int index) + { + return Collection[context].Delete(index); + } + + int Raw_Count() const + { + return COUNT; + } + + DynamicVectorClass & Raw() + { + return Collection[Active]; + } + + DynamicVectorClass & Raw(int context) + { + return Collection[context]; + } + +private: + DynamicVectorClass Collection[COUNT]; + int Active; +}; + + +/************************************************************************** +** This is a derivative of a vector class that supports boolean flags. Since +** a boolean flag can be represented by a single bit, this class packs the +** array of boolean flags into an array of bytes containing 8 boolean values +** each. For large boolean arrays, this results in an 87.5% savings. Although +** the indexing "[]" operator is supported, DO NOT pass pointers to sub elements +** of this bit vector class. A pointer derived from the indexing operator is +** only valid until the next call. Because of this, only simple +** direct use of the "[]" operator is allowed. +*/ +class BooleanVectorClass +{ + public: + BooleanVectorClass(unsigned size=0, unsigned char * array=0); + BooleanVectorClass(BooleanVectorClass const & vector); + + // Assignment operator. + BooleanVectorClass & operator =(BooleanVectorClass const & vector); + + // Equivalency operator. + int operator == (BooleanVectorClass const & vector); + + // Fetch number of boolean objects in vector. + int Length(void) {return BitCount;}; + + // Set all boolean values to false; + void Reset(void); + + // Set all boolean values to true. + void Set(void); + + // Resets vector to zero length (frees memory). + void Clear(void); + + // Change size of this boolean vector. + int Resize(unsigned size); + + // Fetch reference to specified index. + bool const & operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool & operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index) ? true : false); + }; + + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_False_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + private: + void Fixup(int index=-1) const; + + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; +}; + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) +{ + Vector = 0; + VectorMax = size; + IsAllocated = false; + + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) +{ + VectorMax = 0; + IsAllocated = false; + Vector = 0; + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasable) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assigment operator will + ** only work for the simplist of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previosly allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previosly and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if ((unsigned)ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if ((unsigned)ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + int index = ID(object); + if (index != -1){ + return(Delete(index)); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if (index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/VISUDLG.CPP b/TIBERIANDAWN/VISUDLG.CPP new file mode 100644 index 000000000..2c52bedf7 --- /dev/null +++ b/TIBERIANDAWN/VISUDLG.CPP @@ -0,0 +1,421 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\visudlg.cpv 2.17 16 Oct 1995 16:51:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : June 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VisualControlsClass::Process -- Process the visual control dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "visudlg.h" +int VisualControlsClass::Init(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Option_Width = 216 * factor; + Option_Height = 122 * factor; + Option_X = (((SeenBuff.Get_Width() - Option_Width) / 2)); + Option_Y = ((SeenBuff.Get_Height() - Option_Height) / 2); + Text_X = Option_X + (28 * factor); + Text_Y = Option_Y + (30 * factor); + Slider_X = Option_X + (105 * factor); + Slider_Y = Option_Y + (30 * factor); + Slider_Width = 70 * factor; + Slider_Height = 5 * factor; + Slider_Y_Spacing = 11 * factor; + Button_X = Option_X + (63 * factor); + Button_Y = Option_Y + (102 * factor); + return(factor); +} +/*********************************************************************************************** + * VisualControlsClass::Process -- Process the visual control dialog box. * + * * + * This routine displays and processes the visual controls dialog box. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +void VisualControlsClass::Process(void) +{ + static int _titles[4] = { + TXT_BRIGHTNESS, + TXT_COLOR, + TXT_CONTRAST, + TXT_TINT + }; + + enum { + NUM_OF_BUTTONS = 6, + }; + + /* + ** Variables. + */ + int selection; + int factor; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + SliderClass *buttonsliders[NUM_OF_BUTTONS]; + + factor = Init(); + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn( + BUTTON_OPTIONS, + TXT_GAME_CONTROLS, + TPF_6PT_GRAD | TPF_NOSHADOW, + 0, + Button_Y ); + + TextButtonClass resetbtn( + BUTTON_RESET, + TXT_RESET_MENU, + TPF_6PT_GRAD | TPF_NOSHADOW, + 0, + Button_Y); + + /* + ** Centers options button. + */ + optionsbtn.X = Option_X + (Option_Width - optionsbtn.Width - (15 * factor)); + resetbtn.X = Option_X + (15 *factor); + + resetbtn.Add_Tail(optionsbtn); + + /* + ** Brightness (value) control. + */ + SliderClass brightness(BUTTON_BRIGHTNESS, Slider_X, Slider_Y + (Slider_Y_Spacing*0), Slider_Width, Slider_Height); + brightness.Set_Thumb_Size(20); + brightness.Set_Value(Options.Get_Brightness()); + brightness.Add_Tail(optionsbtn); + + /* + ** Color (saturation) control. + */ + SliderClass color(BUTTON_COLOR, Slider_X, Slider_Y + (Slider_Y_Spacing*1), Slider_Width, Slider_Height); + color.Set_Thumb_Size(20); + color.Set_Value(Options.Get_Color()); + color.Add_Tail(optionsbtn); + + /* + ** Contrast control. + */ + SliderClass contrast(BUTTON_CONTRAST, Slider_X, Slider_Y + (Slider_Y_Spacing*2), Slider_Width, Slider_Height); + contrast.Set_Thumb_Size(20); + contrast.Set_Value(Options.Get_Contrast()); + contrast.Add_Tail(optionsbtn); + + /* + ** Tint (hue) control. + */ + SliderClass tint(BUTTON_TINT, Slider_X, Slider_Y + (Slider_Y_Spacing*3), Slider_Width, Slider_Height); + tint.Set_Thumb_Size(20); + tint.Set_Value(Options.Get_Tint()); + tint.Add_Tail(optionsbtn); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(Option_X, Option_Y, Option_Width, Option_Height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(optionsbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(optionsbtn); + + curbutton = 0; + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = NULL; + buttons[3] = NULL; + buttons[4] = &resetbtn; + buttons[5] = &optionsbtn; + + buttonsliders[0] = &brightness; + buttonsliders[1] = &color; + buttonsliders[2] = &contrast; + buttonsliders[3] = ∭ + buttonsliders[4] = NULL; + buttonsliders[5] = NULL; + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + bool partial = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(Option_X, Option_Y, Option_Width, Option_Height); + Draw_Caption(TXT_VISUAL_CONTROLS, Option_X, Option_Y, Option_Width); + Show_Mouse(); + display = false; + partial = true; + } + + /* + ** If just the buttons and captions need to be redrawn, then do so now. + */ + if (partial) { + Hide_Mouse(); + + /* + ** Draw the titles. + */ + for (int i = 0; i < (sizeof(_titles)/sizeof(_titles[0])); i++) { + Fancy_Text_Print(_titles[i], Slider_X-8, Text_Y + (i*Slider_Y_Spacing), + CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_RIGHT|TPF_NOSHADOW| ((curbutton == i) ? TPF_BRIGHT_COLOR : TPF_USE_GRAD_PAL)); + } + optionsbtn.Draw_All(); + Show_Mouse(); + partial = false; + } + + /* + ** Get and process player input. + */ + KeyNumType input = optionsbtn.Input(); + switch (input) { + case (BUTTON_BRIGHTNESS | KN_BUTTON): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR | KN_BUTTON): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST | KN_BUTTON): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT | KN_BUTTON): + Options.Set_Tint(tint.Get_Value()); + break; + + case (BUTTON_RESET | KN_BUTTON): + selection = BUTTON_RESET; + pressed = true; + break; + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + selection = BUTTON_OPTIONS; + pressed = true; + break; + + case (KN_LEFT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(1); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(tint.Get_Value()); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_OPTIONS - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(0); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(tint.Get_Value()); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (BUTTON_OPTIONS - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_UP): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton == (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton--; + } + + if (curbutton < 0) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_DOWN): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = 0; + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_BRIGHTNESS; + pressed = true; + break; + + default: + break; + } + + + if (pressed) { + switch (selection) { + case (BUTTON_RESET): + brightness.Set_Value(128); + contrast.Set_Value(128); + color.Set_Value(128); + tint.Set_Value(128); + + Options.Set_Brightness(128); + Options.Set_Contrast(128); + Options.Set_Color(128); + Options.Set_Tint(128); + break; + + case (BUTTON_OPTIONS): + process = false; + break; + } + + pressed = false; + } + } +} + diff --git a/TIBERIANDAWN/VISUDLG.H b/TIBERIANDAWN/VISUDLG.H new file mode 100644 index 000000000..43ed91b0b --- /dev/null +++ b/TIBERIANDAWN/VISUDLG.H @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\visudlg.h_v 2.15 16 Oct 1995 16:47:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + *---------------------------------------------------------------------------------------------*/ + +#ifndef VISUDLG_H +#define VISUDLG_H + +#include "gadget.h" + +class VisualControlsClass +{ + private: + + enum VisualControlEnums { + BUTTON_BRIGHTNESS=1, + BUTTON_COLOR, + BUTTON_CONTRAST, + BUTTON_TINT, + BUTTON_RESET, + BUTTON_OPTIONS, // Button number for "Options menu" + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2)), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+28, // Title's x pos + TEXT_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_X=OPTION_X+105, // Slider's x pos + SLIDER_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_WIDTH=70, // Width of each control slider. + SLIDER_HEIGHT=5, // Height of each control slider. + SLIDER_Y_SPACING=11, // Vertical spacing between sliders. + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + }; + + public: + + VisualControlsClass(void) {}; + void Process(void); + int Init(void); + + int Option_Width; // Width of dialog box. + int Option_Height; // Height of dialog box. + int Option_X; + int Option_Y; + int Text_X; // Title's x pos + int Text_Y; // Add 11 for each following line + int Slider_X; // Slider's x pos + int Slider_Y; // Add 11 for each following line + int Slider_Width; // Width of each control slider. + int Slider_Height; // Height of each control slider. + int Slider_Y_Spacing; // Vertical spacing between sliders. + int Button_X; // Options button x pos + int Button_Y; // Options button y pos + +}; + +#endif diff --git a/TIBERIANDAWN/WATCOM.H b/TIBERIANDAWN/WATCOM.H new file mode 100644 index 000000000..107fab130 --- /dev/null +++ b/TIBERIANDAWN/WATCOM.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\watcom.h_v 2.13 16 Oct 1995 16:45:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WATCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/12/95 * + * * + * Last Update : March 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WATCOM_H +#define WATCOM_H +#if (0) +// Turn all warnings into errors. +#pragma warning * 0 + +// Disables warning when "sizeof" is used on an object with virtual functions. +#pragma warning 549 9 + +// Disable the "Integral value may be truncated during assignment or initialization". +#pragma warning 389 9 + +// Allow constructing a temporary to be used as a parameter. +#pragma warning 604 9 + +// Disable the construct resolved as an expression warning. +#pragma warning 595 9 + +// Disable the strange "construct resolved as a declaration/type" warning. +#pragma warning 594 9 + +// Disable the "pre-compiled header file cannot be used" warning. +#pragma warning 698 9 + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + +// Disable the "pointer or reference truncated by cast. Cast is supposed to REMOVE warnings, not create them. +#pragma warning 579 9 + +// Disable the warning about moving empty constructors/destructors to the class declaration. +#pragma warning 657 9 + +// Turns off unreferenced function parameter warning. +//#pragma off(unreferenced) + +// Turns off "expression with side effect in sizeof()". This is needed if memchecker is used. +#pragma warning 472 9 +#endif +#endif diff --git a/TIBERIANDAWN/WIN32LIB/ALLOC.CPP b/TIBERIANDAWN/WIN32LIB/ALLOC.CPP new file mode 100644 index 000000000..cd3f15fed --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/ALLOC.CPP @@ -0,0 +1,509 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +//#include +//#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; +extern void (*Memory_Error_Exit)(char *string)=NULL; + + +//#define MEM_CHECK + +#ifdef MEM_CHECK +extern "C"{ + extern void __cdecl Int3(void); +} +#endif //MEM_CHECK + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *, long const ) +{ +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *, long const ) +{ +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + * 09/28/1995 ST : Simplified for win95 * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + +#ifdef WIN32 + + void *mem_ptr; + +#ifdef MEM_CHECK + bytes_to_alloc += 32; +#endif //MEM_CHECK + + mem_ptr = malloc ( bytes_to_alloc ); + + if ( !mem_ptr && Memory_Error ){ + Memory_Error(); + } + + if ( mem_ptr && ( flags & MEM_CLEAR ) ){ + memset ( mem_ptr , 0 , bytes_to_alloc ); + } + +#ifdef MEM_CHECK + mem_ptr = (void*)((char*)mem_ptr + 16); + unsigned long *magic_ptr =(unsigned long*) ( ((char *)mem_ptr) - 16 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = bytes_to_alloc - 32; + magic_ptr = (unsigned long*) ( ((char*)mem_ptr) + bytes_to_alloc - 32 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = (unsigned long)mem_ptr; +#endif //MEM_CHECK + + Memory_Calls++; + return ( mem_ptr ); + +#else + + + + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); + +#endif +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +#ifdef WIN32 + +void Free(void const *pointer) +{ + + if ( pointer ){ + +#ifdef MEM_CHECK + + unsigned long *magic_ptr = (unsigned long*) ( ((char*)pointer) - 16 ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + magic_ptr = (unsigned long*) ( ((char*)pointer) + *magic_ptr ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + pointer = (void*) (((char*)pointer)-16); +#endif //MEM_CHECK + + free ( (void*)pointer ); + Memory_Calls--; + } + +#else + +void Free(void const *pointer) +{ + + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } + +#endif +} + + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + return ( 64*1024*1024 ); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + + return ( 64*1024*1024 ); +} + diff --git a/TIBERIANDAWN/WIN32LIB/AUDIO.H b/TIBERIANDAWN/WIN32LIB/AUDIO.H new file mode 100644 index 000000000..d84c6974c --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/AUDIO.H @@ -0,0 +1,158 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +//#pragma pack(1); +#pragma pack(push,1) +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; +#pragma pack(pop) + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; + +extern int StreamLowImpact; diff --git a/TIBERIANDAWN/WIN32LIB/BUFFER.CPP b/TIBERIANDAWN/WIN32LIB/BUFFER.CPP new file mode 100644 index 000000000..7affb0886 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/BUFFER.CPP @@ -0,0 +1,128 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/TIBERIANDAWN/WIN32LIB/BUFFER.H b/TIBERIANDAWN/WIN32LIB/BUFFER.H new file mode 100644 index 000000000..39ce89481 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/BUFFER.H @@ -0,0 +1,121 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif diff --git a/TIBERIANDAWN/WIN32LIB/BUFFGLBL.CPP b/TIBERIANDAWN/WIN32LIB/BUFFGLBL.CPP new file mode 100644 index 000000000..6c16b9d1a --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/BUFFGLBL.CPP @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "gbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +#ifdef not_any_more_it_doesnt +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +#endif //not_any_more_it_doesnt + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicViewPortClass *LogicPage; + +BOOL IconCacheAllowed = TRUE; + +/* +** Pointer to a function we will call if we detect loss of focus +*/ +void (*Gbuffer_Focus_Loss_Function)(void) = NULL; diff --git a/TIBERIANDAWN/WIN32LIB/DDRAW.CPP b/TIBERIANDAWN/WIN32LIB/DDRAW.CPP new file mode 100644 index 000000000..fb84677a2 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DDRAW.CPP @@ -0,0 +1,1002 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Win32 Library * + * * + * File Name : DDRAW.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : October 10, 1995 * + * * + * Last Update : October 10, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +#include "misc.h" +#include +#include +#include "stdio.h" + +LPDIRECTDRAW DirectDrawObject=NULL; // Pointer to the direct draw object +LPDIRECTDRAW2 DirectDraw2Interface = NULL; // Pointer to direct draw 2 interface + +HWND MainWindow; // Handle to programs main window + // this is passed to SetCooperativeLevel + // so DirectDraw knows which window is ours + + +PALETTEENTRY PaletteEntries[256]; // 256 windows palette entries +LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object +BOOL FirstPaletteSet=FALSE; // Is this the first time 'Set_Palette' has been called? +LPDIRECTDRAWSURFACE PaletteSurface=NULL; +SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces +BOOL CanVblankSync = TRUE; + +BOOL SystemToVideoBlits =FALSE; // Does hardware support system mem to video mem blits? +BOOL VideoToSystemBlits =FALSE; // Does hardware support video mem to system mem blits? +BOOL SystemToSystemBlits = FALSE; // Does hardware support system mem to system mem blits? +BOOL OverlappedVideoBlits = TRUE; // Can video driver blit overlapped regions? + +/* +** Function to call if we detect focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void) = NULL; +extern void (*Misc_Focus_Restore_Function)(void) = NULL; + + +/*********************************************************************************************** + * Process_DD_Result -- Does a message box based on the result of a DD command * + * * + * INPUT: HRESULT result - the result returned from the direct draw command * + * int display_ok_msg - should a message be displayed if command ok * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/27/1995 PWG : Created. * + *=============================================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg) +{ + switch (result) { + case DD_OK: + if (display_ok_msg) { + MessageBox(MainWindow, "Direct Draw request went ok.", "Note", MB_ICONEXCLAMATION|MB_OK); + } + break; + case DDERR_ALREADYINITIALIZED: + MessageBox(MainWindow, "This object is already initialized ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_BLTFASTCANTCLIP: + MessageBox(MainWindow, "Return if a clipper object is attached to the source surface passed into a BltFast call.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTATTACHSURFACE: + MessageBox(MainWindow, "This surface can not be attached to the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTDETACHSURFACE: + MessageBox(MainWindow, "This surface can not be detached from the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTCREATEDC: + MessageBox(MainWindow, "Windows can not create any more DCs","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTDUPLICATE: + MessageBox(MainWindow, "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTLOCKSURFACE: + MessageBox(MainWindow, "Unable to lock surface because no driver exists which can supply a pointer to the surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CLIPPERISUSINGHWND: + MessageBox(MainWindow, "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_COLORKEYNOTSET: + MessageBox(MainWindow, "No src color key specified for this operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CURRENTLYNOTAVAIL: + MessageBox(MainWindow, "Support is currently not available. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_DIRECTDRAWALREADYCREATED: + MessageBox(MainWindow, "A DirectDraw object representing this driver has already been created for this process. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCEPTION: + MessageBox(MainWindow, "An exception was encountered while performing the requested operation. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCLUSIVEMODEALREADYSET: + MessageBox(MainWindow, "An attempt was made to set the cooperative level when it was already set to exclusive.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_GENERIC: + MessageBox(MainWindow, "Generic failure.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HEIGHTALIGN: + MessageBox(MainWindow, "Height of rectangle provided is not a multiple of reqd alignment.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDALREADYSET: + MessageBox(MainWindow, "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDSUBCLASSED: + MessageBox(MainWindow, "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_IMPLICITLYCREATED: + MessageBox(MainWindow, "This surface can not be restored because it is an implicitly created surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INCOMPATIBLEPRIMARY: + MessageBox(MainWindow, "Unable to match primary surface creation request with existing primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCAPS: + MessageBox(MainWindow, "One or more of the caps bits passed to the callback are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCLIPLIST: + MessageBox(MainWindow, "DirectDraw does not support the provided cliplist.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDDIRECTDRAWGUID: + MessageBox(MainWindow, "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDMODE: + MessageBox(MainWindow, "DirectDraw does not support the requested mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDOBJECT: + MessageBox(MainWindow, "DirectDraw received a pointer that was an invalid DIRECTDRAW object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPARAMS: + MessageBox(MainWindow, "One or more of the parameters passed to the function are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPIXELFORMAT: + MessageBox(MainWindow, "The pixel format was invalid as specified.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPOSITION: + MessageBox(MainWindow, "Returned when the position of the overlay on the destination is no longer legal for that destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDRECT: + MessageBox(MainWindow, "Rectangle provided was invalid.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + + case DDERR_INVALIDSURFACETYPE: + MessageBox(MainWindow, "The requested action could not be performed because the surface was of the wrong type.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_LOCKEDSURFACES: + MessageBox(MainWindow, "Operation could not be carried out because one or more surfaces are locked.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NO3D: + MessageBox(MainWindow, "There is no 3D present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOALPHAHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no alpha accleration hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOANTITEARHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for synchronizing blts to avoid tearing. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOBLTHW: + MessageBox(MainWindow, "No blter hardware present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOBLTQUEUEHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for asynchronous blting. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOCLIPLIST: + MessageBox(MainWindow, "No cliplist available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCLIPPERATTACHED: + MessageBox(MainWindow, "No clipper object attached to surface object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORCONVHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no color conversion hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEY: + MessageBox(MainWindow, "Surface doesn't currently have a color key","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support of the destination color key.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOOPERATIVELEVELSET: + MessageBox(MainWindow, "Create function called without DirectDraw object method SetCooperativeLevel being called.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODC: + MessageBox(MainWindow, "No DC was ever created for this surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODDROPSHW: + MessageBox(MainWindow, "No DirectDraw ROP hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWHW: + MessageBox(MainWindow, "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWSUPPORT: + MessageBox(MainWindow, "No DirectDraw support possible with current display driver.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEMULATION: + MessageBox(MainWindow, "Software emulation not available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEXCLUSIVEMODE: + MessageBox(MainWindow, "Operation requires the application to have exclusive mode but the application does not have exclusive mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOFLIPHW: + MessageBox(MainWindow, "Flipping visible surfaces is not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOGDI: + MessageBox(MainWindow, "There is no GDI present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOHWND: + MessageBox(MainWindow, "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOMIRRORHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYDEST: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no overlay hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEATTACHED: + MessageBox(MainWindow, "No palette object attached to this surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEHW: + MessageBox(MainWindow, "No hardware support for 16 or 256 color palettes.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NORASTEROPHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no appropriate raster op hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOROTATIONHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no rotation hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOSTRETCHHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for stretching.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLORINDEX: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT8BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTAOVERLAYSURFACE: + MessageBox(MainWindow, "Returned when an overlay member is called for a non-overlay surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTEXTUREHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no texture mapping hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFLIPPABLE: + MessageBox(MainWindow, "An attempt has been made to flip a surface that is not flippable.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFOUND: + MessageBox(MainWindow, "Requested item was not found.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTLOCKED: + MessageBox(MainWindow, "Surface was not locked. An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTPALETTIZED: + MessageBox(MainWindow, "The surface being used is not a palette-based surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOVSYNCHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZBUFFERHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for zbuffer blting.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZOVERLAYHW: + MessageBox(MainWindow, "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFCAPS: + MessageBox(MainWindow, "The hardware needed for the requested operation has already been allocated.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFVIDEOMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCANTCLIP: + MessageBox(MainWindow, "The hardware does not support clipped overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + MessageBox(MainWindow, "Can only have ony color key active at one time for overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYNOTVISIBLE: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on a hidden overlay.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PALETTEBUSY: + MessageBox(MainWindow, "Access to this palette is being refused because the palette is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PRIMARYSURFACEALREADYEXISTS: + MessageBox(MainWindow, "This process already has created a primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_REGIONTOOSMALL: + MessageBox(MainWindow, "Region passed to Clipper::GetClipList is too small.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYATTACHED: + MessageBox(MainWindow, "This surface is already attached to the surface it is being attached to.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYDEPENDENT: + MessageBox(MainWindow, "This surface is already a dependency of the surface it is being made a dependency of.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEBUSY: + MessageBox(MainWindow, "Access to this surface is being refused because the surface is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEISOBSCURED: + MessageBox(MainWindow, "Access to surface refused because the surface is obscured.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACELOST: + MessageBox(MainWindow, "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.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACENOTATTACHED: + MessageBox(MainWindow, "The requested surface is not attached.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGHEIGHT: + MessageBox(MainWindow, "Height requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGSIZE: + MessageBox(MainWindow, "Size requested by DirectDraw is too large -- the individual height and width are OK.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGWIDTH: + MessageBox(MainWindow, "Width requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTED: + MessageBox(MainWindow, "Action not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDFORMAT: + MessageBox(MainWindow, "FOURCC format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDMASK: + MessageBox(MainWindow, "Bitmask in the pixel format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_VERTICALBLANKINPROGRESS: + MessageBox(MainWindow, "Vertical blank is in progress.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WASSTILLDRAWING: + MessageBox(MainWindow, "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WRONGMODE: + MessageBox(MainWindow, "This surface can not be restored because it was created in a different mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_XALIGN: + MessageBox(MainWindow, "Rectangle provided was not horizontally aligned on required boundary.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + default: + char string[256]; + sprintf (string, "Unrecognised Direct Draw result code: %d", result & 0xffff); + MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + break; + } +} + + + +/*********************************************************************************************** + * Check_Overlapped_Blit_Capability -- See if video driver supports blitting overlapped regions* + * * + * We will check for this by drawing something to a video page and blitting it over itself. * + * If we end up with the top line repeating then overlapped region blits dont work. * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 5:06PM ST : Created * + *=============================================================================================*/ +void Check_Overlapped_Blit_Capability(void) +{ + + /* + ** Assume we can until we find out otherwise + */ + OverlappedVideoBlits = TRUE; + + GraphicBufferClass test_buffer; + + test_buffer.Init (64, 64, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + test_buffer.Clear(); + + /* + ** Plot a pixel in the top left corner of the buffer. + */ + test_buffer.Put_Pixel(0, 0, 255); + + /* + ** Blit the buffer down by one line. If we end up with a vertical strip of pixel 255's then + ** overlapped blits dont work + */ + + test_buffer.Blit(test_buffer, 0, 0, 0, 1, test_buffer.Get_Width(), test_buffer.Get_Height()-1); + + if (test_buffer.Get_Pixel (0 ,5) == 255) OverlappedVideoBlits = FALSE; +} + + + +/*********************************************************************************************** + * Set_Video_Mode -- Initializes Direct Draw and sets the required Video Mode * + * * + * INPUT: int width - the width of the video mode in pixels * + * int height - the height of the video mode in pixels * + * int bits_per_pixel - the number of bits per pixel the video mode supports * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel) +{ +// ST - 1/3/2019 10:41AM + return TRUE; +#if (0) + HRESULT result; + // + // If there is not currently a direct draw object then we need to define one. + // + if ( DirectDrawObject == NULL ){ + //MessageBox(MainWindow, "In Set_Video_Mode. About to call DirectDrawCreate.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawCreate(NULL, &DirectDrawObject, NULL); + Process_DD_Result(result, FALSE); + if (result == DD_OK){ + if (w==320){ + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + } else { + //MessageBox(MainWindow, "In Set_Video_Mode. About to call SetCooperativeLevel.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + Process_DD_Result(result, FALSE); + }else{ + return (FALSE); + } + } + + // + // Set the required display mode with 8 bits per pixel + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call call SetDisplayMode.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetDisplayMode ( w , h , bits_per_pixel ); + if (result != DD_OK){ +// Process_DD_Result(result, FALSE); + DirectDrawObject->Release(); + DirectDrawObject = NULL; + return(FALSE); + } + + // + // Create a direct draw palette object + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call CreatePalette.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &PaletteEntries[0] , &PalettePtr ,NULL); + Process_DD_Result(result, FALSE); + if (result != DD_OK){ + return (FALSE); + } + + Check_Overlapped_Blit_Capability(); + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); +#if (0) + /* + ** Find out if DirectX 2 extensions are available + */ + result = DirectDrawObject->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DirectDraw2Interface); + SystemToVideoBlits = FALSE; + VideoToSystemBlits = FALSE; + SystemToSystemBlits= FALSE; + if (result != DD_OK){ + DirectDraw2Interface = NULL; + }else{ + DDCAPS capabilities; + DDCAPS emulated_capabilities; + + memset ((char*)&capabilities, 0, sizeof(capabilities)); + memset ((char*)&emulated_capabilities, 0, sizeof(emulated_capabilities)); + capabilities.dwSize = sizeof (capabilities); + emulated_capabilities.dwSize = sizeof (emulated_capabilities); + + DirectDrawObject->GetCaps (&capabilities, &emulated_capabilities); + + if (capabilities.dwCaps & DDCAPS_CANBLTSYSMEM){ + SystemToVideoBlits = (capabilities.dwSVBCaps & DDCAPS_BLT) ? TRUE : FALSE; + VideoToSystemBlits = (capabilities.dwVSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + SystemToSystemBlits = (capabilities.dwSSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + } + } +#endif //(0) + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + + return (TRUE); +#endif +} + +/*********************************************************************************************** + * Reset_Video_Mode -- Resets video mode and deletes Direct Draw Object * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +void Reset_Video_Mode(void) +{ + HRESULT result; + + // + // If a direct draw object has been declared and a video mode has been set + // then reset the video mode and release the direct draw object. + // + if ( DirectDrawObject ) { + result = DirectDrawObject->RestoreDisplayMode(); + Process_DD_Result(result, FALSE); + result = DirectDrawObject->Release(); + Process_DD_Result(result, FALSE); + + DirectDrawObject = NULL; + } +} + + + + +/*********************************************************************************************** + * Get_Free_Video_Memory -- returns amount of free video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: bytes of available video RAM * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:52PM ST : Created * + *=============================================================================================*/ +unsigned int Get_Free_Video_Memory(void) +{ + + DDCAPS video_capabilities; + + if (DirectDrawObject){ + + video_capabilities.dwSize = sizeof (video_capabilities); + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + char string [256]; + sprintf (string, "In Get_Free_Video_Memory. About to return %d bytes",video_capabilities.dwVidMemFree); + //MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + return (video_capabilities.dwVidMemFree); + } + } + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to return failure","Note", MB_ICONEXCLAMATION|MB_OK); + return (0); +} + + + + +/*********************************************************************************************** + * Get_Video_Hardware_Caps -- returns bitmask of direct draw video hardware support * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: hardware flags * + * * + * WARNINGS: Must call Set_Video_Mode 1st to create the direct draw object * + * * + * HISTORY: * + * 1/12/96 9:14AM ST : Created * + *=============================================================================================*/ +unsigned Get_Video_Hardware_Capabilities(void) +{ + DDCAPS video_capabilities; + unsigned video; + + /* + ** Fail if the direct draw object has not been initialised + */ + if (!DirectDrawObject) return (0); + + /* + ** Get the capabilities of the direct draw object + */ + video_capabilities.dwSize = sizeof(video_capabilities); + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + HRESULT result = DirectDrawObject->GetCaps (&video_capabilities, NULL); + if (result != DD_OK){ + Process_DD_Result(result, FALSE); + return (0); + } + + /* + ** Set flags to indicate the presence of the features we are interested in + */ + video = 0; + + /* Hardware blits supported? */ + if (video_capabilities.dwCaps & DDCAPS_BLT) video |= VIDEO_BLITTER; + + /* Hardware blits asyncronous? */ + if (video_capabilities.dwCaps & DDCAPS_BLTQUEUE) video |= VIDEO_BLITTER_ASYNC; + + /* Can palette changes be synced to vertical refresh? */ + if (video_capabilities.dwCaps & DDCAPS_PALETTEVSYNC) video |= VIDEO_SYNC_PALETTE; + + /* Is the video cards memory bank switched? */ + if (video_capabilities.dwCaps & DDCAPS_BANKSWITCHED) video |= VIDEO_BANK_SWITCHED; + + /* Can the blitter do filled rectangles? */ + if (video_capabilities.dwCaps & DDCAPS_BLTCOLORFILL) video |= VIDEO_COLOR_FILL; + + /* Is there no hardware assistance avaailable at all? */ + if (video_capabilities.dwCaps & DDCAPS_NOHARDWARE) video |= VIDEO_NO_HARDWARE_ASSIST; + + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + return (video); +} + + + + +/*********************************************************************************************** + * Wait_Vert_Blank -- Waits for the start (leading edge) of a vertical blank * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=============================================================================================*/ +extern int ScreenWidth; +void Wait_Vert_Blank(void) +{ + if (DirectDrawObject == NULL) { + return; + } + if( ScreenWidth!=320 && CanVblankSync){ + HRESULT result = DirectDrawObject->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); + if (result == E_NOTIMPL){ + CanVblankSync = FALSE; + return; + } + Process_DD_Result(result, FALSE); + } +} + + + + + +/*********************************************************************************************** + * Set_Palette -- set a direct draw palette * + * * + * * + * * + * INPUT: ptr to 768 rgb palette bytes * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/11/95 3:33PM ST : Created * + *=============================================================================================*/ +void Set_DD_Palette ( void *palette ) +{ + + /* + ** Trap null ptr + */ + if (!palette) return; + + int j; + int k; + char *palette_get; + + if ( DirectDrawObject && PaletteSurface ){ + + k=0; + + palette_get = (char*)palette; + + for( j=0 ; j<768 ; j+=3 ) + { + PaletteEntries[k].peRed = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peGreen = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peBlue = (unsigned char)((*palette_get++)<<2); + k++; + } + + if ( !FirstPaletteSet ){ + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetPalette","Note", MB_ICONEXCLAMATION|MB_OK); + PaletteSurface->SetPalette( PalettePtr ); + FirstPaletteSet=TRUE; + } + + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetEntries","Note", MB_ICONEXCLAMATION|MB_OK); + PalettePtr->SetEntries( 0 , 0 , 256 , &PaletteEntries[0] ); + } + //MessageBox(MainWindow, "Leaving Set_DD_Palette","Note", MB_ICONEXCLAMATION|MB_OK); + +} + + + + + +/*********************************************************************************************** + * Wait_Blit -- waits for the DirectDraw blitter to become idle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 07-25-95 03:53pm ST : Created * + *=============================================================================================*/ + +void Wait_Blit (void) +{ + HRESULT return_code; + + do { + return_code=PaletteSurface->GetBltStatus (DDGBS_ISBLTDONE); + } while (return_code != DD_OK && return_code != DDERR_SURFACELOST); + +} + + + +/*********************************************************************************************** + * SMC::SurfaceMonitorClass -- constructor for surface monitor class * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:23PM ST : Created * + *=============================================================================================*/ + +SurfaceMonitorClass::SurfaceMonitorClass(void) +{ + for (int i=0 ; iRestore() != DD_OK){ + if (Misc_Focus_Loss_Function){ + Misc_Focus_Loss_Function(); + } + return; + } + } + } + + /* + ** PWG/ST: Now that we know all the surfaces are restored call + ** the function pointer to notify the program that it has + ** happened. This function pointer is used to clear the pages, + ** etc. + */ + if (Misc_Focus_Restore_Function){ + Misc_Focus_Restore_Function(); + } + + SurfacesRestored = TRUE; + + /* + ** Restore the palette + */ + Set_DD_Palette (CurrentPalette); + } +} + + +/*********************************************************************************************** + * SMC::Set_Surface_Focus -- set the InFocus flag to the given state * + * * + * The InFocus flag is used to keep track of whether our application is currently in focus. * + * We dont want to be restoring video surfaces when we are supposed to be running in the * + * background. * + * * + * INPUT: bool in focus * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/6/95 12:21PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Set_Surface_Focus ( BOOL in_focus ) +{ + InFocus=in_focus; +} + + + + +/*********************************************************************************************** + * SMC::Release -- releases all direct draw surfaces * + * * + * Call this at the end of the game before called RestoreDisplayMode * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:23PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Release(void) +{ + /* + ** Call release for each Direct Draw surface + */ + for (int i=0 ; iRelease(); + Surface[i] = 0; + } + } + +} diff --git a/TIBERIANDAWN/WIN32LIB/DDRAW.H b/TIBERIANDAWN/WIN32LIB/DDRAW.H new file mode 100644 index 000000000..d1af82f96 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DDRAW.H @@ -0,0 +1,3117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1994-1996 Microsoft Corporation. All Rights Reserved. + * + * File: ddraw.h + * Content: DirectDraw include file + * + ***************************************************************************/ + +#ifndef __DDRAW_INCLUDED__ +#define __DDRAW_INCLUDED__ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#define CO_E_NOTINITIALIZED 0x800401F0L +#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_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 ); + +#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 IDirectDrawPalette FAR *LPDIRECTDRAWPALETTE; +typedef struct IDirectDrawClipper FAR *LPDIRECTDRAWCLIPPER; + +typedef struct _DDFXROP FAR *LPDDFXROP; +typedef struct _DDSURFACEDESC FAR *LPDDSURFACEDESC; + +/* + * 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 ); + #ifdef WINNT + //This is the user-mode entry stub to the kernel mode procedure. + extern HRESULT NtDirectDrawCreate( GUID FAR *lpGUID, HANDLE *lplpDD, IUnknown FAR *pUnkOuter ); + #endif +#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 + +#ifdef WINNT +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 + 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 + +typedef struct _DDCAPS +{ + 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; + +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 (BD_4,8,16,24,32) + DWORD dwYUVBitCount; // how many bits per pixel (BD_4,8,16,24,32) + DWORD dwZBufferBitDepth; // how many bits for z buffers (BD_8,16,24,32) + DWORD dwAlphaBitDepth; // how many bits for alpha channels (BD_1,2,4,8) + }; + 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 + }; +} 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) +#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) (p)->lpVtbl->SetDisplayMode(p, a, b, c, d) +#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) +#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) +#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) +#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) +#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) +#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 + LONG lPitch; // distance to start of next line (return value only) + 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 + + + +/* + * 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 + + +/* + * All input fields are valid. + */ +#define DDSD_ALL 0x0007f9eel + + +/*============================================================================ + * + * 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 currently has no meaning. + */ +#define DDSCAPS_3D 0x00000001l + +/* + * Indicates that this surface contains alpha information. The pixel + * format must be interrogated to determine whether this surface + * contains only alpha information or alpha information interlaced + * with pixel color data (e.g. RGBA or YUVA). + */ +#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 320x200 or 320x240 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 + + + +/* + * Indicates that memory for the surface is not allocated until the surface + * is loaded (via the Direct3D texture Load() function). + */ +#define DDSCAPS_ALLOCONLOAD 0x04000000l + + + + /**************************************************************************** + * + * 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 + +/**************************************************************************** + * + * 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 + +/*=========================================================================== + * + * + * 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 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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 + + +/**************************************************************************** + * + * 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. + */ +#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 ) + + + +/* + * A DC has already been returned for this surface. Only one DC can be + * retrieved per surface. + */ +#define DDERR_DCALREADYCREATED MAKE_DDHRESULT( 620 ) + +/* + * 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 ) + +/* + * 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/TIBERIANDAWN/WIN32LIB/DEFINES.H b/TIBERIANDAWN/WIN32LIB/DEFINES.H new file mode 100644 index 000000000..954e91d23 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DEFINES.H @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/TIBERIANDAWN/WIN32LIB/DESCMGMT.H b/TIBERIANDAWN/WIN32LIB/DESCMGMT.H new file mode 100644 index 000000000..f8102558d --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DESCMGMT.H @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/TIBERIANDAWN/WIN32LIB/DIFFTB.INC b/TIBERIANDAWN/WIN32LIB/DIFFTB.INC new file mode 100644 index 000000000..0c345aa55 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DIFFTB.INC @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/TIBERIANDAWN/WIN32LIB/DIPTHONG.CPP b/TIBERIANDAWN/WIN32LIB/DIPTHONG.CPP new file mode 100644 index 000000000..7202f9b21 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DIPTHONG.CPP @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + char temp; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + temp = *src++; + temp += 127; + *dest++ = temp; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int common, // Common character index. + dipthong; // Dipthong character index. + + unsigned long length=0; // Length of output string + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = (char)KA_EXTEND; + first -= 127; + length++; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char) (common << 3); + first |= (unsigned char)dipthong; + first |= (unsigned char)0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + length++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(length); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ + +#define TXT_GUEST 4567+3 +#define TXT_LOGIN 4567+4 +#define TXT_LOGIN_TO_INTERNET 4567+5 +#define TXT_YOUR_HANDLE 4567+6 +#define TXT_YOUR_PASSWORD 4567+7 +#define TXT_INTERNET_HOST 4567+8 +#define TXT_INTERNET_JOIN 4567+9 +#define TXT_INTERNET_GAME_TYPE 4567+10 +#define TXT_JOIN_INTERNET_GAME 4567+11 +#define TXT_ENTER_IP_ADDRESS 4567+12 +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + +static char InternetTxt[22][40]={ + "Internet H2H", + "Host Internet Game", + "Join Internet Game", + "Guest", + "Login", + "Login to Planet Westwood", + "Planet Westwood Handle", + "Planet Westwood Password", + "Host Game", + "Join Game", + "Choose Type of Internet Game", + "Join Internet Game", + "Address of Host", + "Connecting...", + "Connection Error!", + "Unable to connect to host!", + "Connecting to host...", + "Unable to resolve host address!", + "Unable to accept client connection", + "Unable to connect!", + "Connection lost!", + "Resolving address of host..." +}; + +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + + if (!data || string < 0) return(NULL); + + if (string >= 4567) return (InternetTxt[string-4567]); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/TIBERIANDAWN/WIN32LIB/DIPTHONG.H b/TIBERIANDAWN/WIN32LIB/DIPTHONG.H new file mode 100644 index 000000000..c8ab804cc --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DIPTHONG.H @@ -0,0 +1,21 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/TIBERIANDAWN/WIN32LIB/DPLAY.H b/TIBERIANDAWN/WIN32LIB/DPLAY.H new file mode 100644 index 000000000..c8e703dfe --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DPLAY.H @@ -0,0 +1,323 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1994-1995 Microsoft Corporation. All Rights Reserved. + * + * File: dplay.h + * Content: DirectPlay include file + * + ***************************************************************************/ + +#ifndef __DPLAY_INCLUDED__ +#define __DPLAY_INCLUDED__ +#ifdef _WIN32 +/* for DECLARE_INTERFACE and HRESULT. */ +#include +#endif + +#define _FACDP 0x877 +#define MAKE_DPHRESULT( code ) MAKE_HRESULT( 1, _FACDP, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(push, 1) + + +/*============================================================================ + * + * DirectPlay Structures + * + * Various structures used to invoke DirectPlay. + * + *==========================================================================*/ + +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectPlay; +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#else +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#endif + +typedef DWORD DPID, FAR *LPDPID; + +typedef struct _DPCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMaxBufferSize; + DWORD dwMaxQueueSize; // Function of DPlay, not SP. + DWORD dwMaxPlayers; + DWORD dwHundredBaud; // 24 is 2400, 96 is 9600, etc. + DWORD dwLatency; +} DPCAPS; + +typedef DPCAPS FAR *LPDPCAPS; + +#define DPLONGNAMELEN 52 +#define DPSHORTNAMELEN 20 +#define DPSESSIONNAMELEN 32 +#define DPPASSWORDLEN 16 +#define DPUSERRESERVED 16 + +typedef struct +{ + DWORD dwSize; + GUID guidSession; // Id for Game. Null is all games. + DWORD dwSession; // session identifier + DWORD dwMaxPlayers; // Maximum players allowed in game. + DWORD dwCurrentPlayers; // Current players in Game. + DWORD dwFlags; // DPOPEN_* flags + char szSessionName[DPSESSIONNAMELEN];// Human readable name for Game + char szUserField[DPUSERRESERVED]; + DWORD dwReserved1; // Reserved for future MS use. + char szPassword[DPPASSWORDLEN]; // Password to be allowed into game. + DWORD dwReserved2; // Reserved for future MS use. + DWORD dwUser1; + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC; +typedef DPSESSIONDESC FAR *LPDPSESSIONDESC; + + +/* + * Create API + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACK)( + LPGUID lpSPGuid, + LPSTR lpFriendlyName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +typedef BOOL (FAR PASCAL * LPDPENUMSESSIONSCALLBACK)( + LPDPSESSIONDESC lpDPSGameDesc, + LPVOID lpContext, + LPDWORD lpdwTimeOut, + DWORD dwFlags); + + + +extern HRESULT WINAPI DirectPlayCreate( LPGUID lpGUID, LPDIRECTPLAY FAR *lplpDP, IUnknown FAR *pUnk); +extern HRESULT WINAPI DirectPlayEnumerate( LPDPENUMDPCALLBACK, LPVOID ); + + +/* Player enumeration callback prototype */ +typedef BOOL (FAR PASCAL *LPDPENUMPLAYERSCALLBACK)( + DPID dpId, + LPSTR lpFriendlyName, + LPSTR lpFormalName, + DWORD dwFlags, + LPVOID lpContext ); + +/* + * IDirectPlay + */ +#undef INTERFACE +#define INTERFACE IDirectPlay +#ifdef _WIN32 +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; +}; +#endif + + + + +/**************************************************************************** + * + * DIRECTPLAY ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ +#define DP_OK 0 +#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_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_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 DPOPEN_OPENSESSION 0x00000001 +#define DPOPEN_CREATESESSION 0x00000002 + +#define DPSEND_GUARANTEE 0x00000001 +#define DPSEND_HIGHPRIORITY 0x00000002 +#define DPSEND_TRYONCE 0x00000004 + +#define DPRECEIVE_ALL 0x00000001 +#define DPRECEIVE_TOPLAYER 0x00000002 +#define DPRECEIVE_FROMPLAYER 0x00000004 +#define DPRECEIVE_PEEK 0x00000008 + +#define DPCAPS_NAMESERVICE 0x00000001 // A name server is supported. +#define DPCAPS_NAMESERVER 0x00000002 // You are the name server. +#define DPCAPS_GUARANTEED 0x00000004 // SP's don't have to implement guarantees. + +#define DPENUMSESSIONS_AVAILABLE 0x00000001 // All games that match password (if given) + // and have openings. +#define DPENUMSESSIONS_ALL 0x00000002 +#define DPENUMSESSIONS_PREVIOUS 0x00000004 + +#define DPENUMPLAYERS_ALL 0x00000000 +#define DPENUMPLAYERS_PREVIOUS 0x00000004 +#define DPENUMPLAYERS_LOCAL 0x00000008 +#define DPENUMPLAYERS_REMOTE 0x00000010 +#define DPENUMPLAYERS_GROUP 0x00000020 +#define DPENUMPLAYERS_SESSION 0x00000080 + +// +// This flag is set on the enumsessions callback when the time out has occured. +// This means that there is no session data for this callback. +// If lpdwTimeOut is set to a non-zero value and the EnumSessionsCallback returns +// TRUE then EnumSessions will continue until the next timeout occurs. +// Timeouts are in milliseconds. + +#define DPESC_TIMEDOUT 0x00000001 + + +// +// System message structures and types. +// +// System messages have a leading 4 byte type code to identify the message. +// an app knows it is a system message because it is addressed 'To' player 0. +// + + +#define DPSYS_ADDPLAYER 0x0003 // DPMSG_ADDPLAYER +#define DPSYS_DELETEPLAYER 0x0005 // DPMSG_DELETEPLAYER + +#define DPSYS_ADDPLAYERTOGROUP 0x0007 // DPMSG_GROUPADD + +#define DPSYS_INVITE 0x000e // DPMSG_INVITE, Net only. + +#define DPSYS_DELETEGROUP 0x0020 // DPMSG_DELETEPLAYER +#define DPSYS_DELETEPLAYERFROMGRP 0x0021 // DPMSG_GROUPDELETE +#define DPSYS_SESSIONLOST 0x0031 + +#define DPSYS_CONNECT 0x484b // DPMSG_GENERIC + + + +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 struct +{ + DWORD dwType; + DPSESSIONDESC dpsDesc; +} DPMSG_INVITE; + + + +typedef struct +{ + DWORD dwType; +} DPMSG_GENERIC; + +#pragma pack(pop) + + +DEFINE_GUID( IID_IDirectPlay, 0x5454e9a0, 0xdb65, 0x11ce, 0x92, 0x1c, 0x00, 0xaa, 0x00, 0x6c, 0x49, 0x72); + + + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/DRAWBUFF.H b/TIBERIANDAWN/WIN32LIB/DRAWBUFF.H new file mode 100644 index 000000000..1b08216ae --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DRAWBUFF.H @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef DRAWBUFF_H +#define DRAWBUFF_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +extern "C" { + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + long __cdecl Buffer_Size_Of_Region(void *thisptr, int w, int h); + + void __cdecl Buffer_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + int __cdecl Buffer_Get_Pixel(void * thisptr, int x, int y); + void __cdecl Buffer_Clear(void *thisptr, unsigned char color); + long __cdecl Buffer_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + BOOL __cdecl Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + BOOL __cdecl Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + LONG __cdecl Buffer_Print(void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); +} + +extern GraphicViewPortClass *LogicPage; +extern BOOL AllowHardwareBlitFills; +#endif diff --git a/TIBERIANDAWN/WIN32LIB/DRAWBUFF.INC b/TIBERIANDAWN/WIN32LIB/DRAWBUFF.INC new file mode 100644 index 000000000..fcc061c09 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DRAWBUFF.INC @@ -0,0 +1,90 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWBUFF.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Clear :NEAR + +; Externs from BITBLIT.ASM module of the DRAWBUFF library +GLOBAL C Linear_Blit_To_Linear :NEAR + +; Externs from TOBUFF.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the DRAWBUFF library +GLOBAL C Linear_Scale_To_Linear :NEAR + +; Externs from TXTPRNT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Print :NEAR + + +;*-------------------------------------------------------------------------* +;* Define Buffer only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Line:NEAR + +; Externs from FILLQUAD.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Remap :NEAR + +; Externs from STAMP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Stamp :NEAR + +GLOBAL C get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + diff --git a/TIBERIANDAWN/WIN32LIB/DRAWRECT.CPP b/TIBERIANDAWN/WIN32LIB/DRAWRECT.CPP new file mode 100644 index 000000000..beaaacd07 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DRAWRECT.CPP @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Lock(); + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); + Unlock(); +} + diff --git a/TIBERIANDAWN/WIN32LIB/DSETUP.H b/TIBERIANDAWN/WIN32LIB/DSETUP.H new file mode 100644 index 000000000..54ea585ea --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DSETUP.H @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsetup.h + * Content: DirectXSetup, error codes and flags + ***************************************************************************/ + +#ifndef __DSETUP_H__ +#define __DSETUP_H__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#define GUID void +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#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 MAX_INFLINE (16*1024) +#define MAX_DESCRIPTION 256 + +#define DSETUP_DDRAW 0x00000001 /* install DirectDraw */ +#define DSETUP_DSOUND 0x00000002 /* install DirectSound */ +#define DSETUP_DPLAY 0x00000004 /* install DirectPlay */ +#define DSETUP_DDRAWDRV 0x00000008 /* install DirectDraw Drivers */ +#define DSETUP_DSOUNDDRV 0x00000010 /* install DirectSound Drivers */ +#define DSETUP_DPLAYSP 0x00000020 /* install DirectPlay Providers */ +#define DSETUP_DIRECTX DSETUP_DDRAW | DSETUP_DSOUND | DSETUP_DPLAY | DSETUP_DDRAWDRV | DSETUP_DSOUNDDRV | DSETUP_DPLAYSP +#define DSETUP_REINSTALL 0x00000080 /* install DirectX even if existing components have the same version */ + +int WINAPI DirectXSetup( HWND hwnd, LPSTR root_path, DWORD flags ); +int WINAPI DirectXDeviceDriverSetup( HWND hwnd, LPSTR driver_class, LPSTR inf_path, LPSTR driver_path, DWORD flags ); + +typedef int (WINAPI * LPDIRECTXSETUP)( HWND, LPSTR, DWORD ); +typedef int (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)( HWND, LPSTR, LPSTR, LPSTR, DWORD ); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/DSOUND.H b/TIBERIANDAWN/WIN32LIB/DSOUND.H new file mode 100644 index 000000000..5c31e9bec --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DSOUND.H @@ -0,0 +1,380 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + ***************************************************************************/ + +#ifndef __DSOUND_INCLUDED__ +#define __DSOUND_INCLUDED__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#endif + +#define _FACDS 0x878 +#define MAKE_DSHRESULT( code ) MAKE_HRESULT( 1, _FACDS, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +// 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 279afa83-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSound,0x279AFA83,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); +// DirectSoundBuffer 279afa85-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSoundBuffer,0x279AFA85,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); + + + +//==========================================================================; +// +// Structures... +// +//==========================================================================; +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectSound; +struct IDirectSoundBuffer; +#endif + +typedef struct IDirectSound *LPDIRECTSOUND; +typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; +typedef struct IDirectSoundBuffer **LPLPDIRECTSOUNDBUFFER; + + +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 struct _DSBCAPS +{ + + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} DSBCAPS, *LPDSBCAPS; + +typedef struct _DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSBUFFERDESC, *LPDSBUFFERDESC; + + + +typedef LPVOID* LPLPVOID; + + +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID); +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKA)(GUID FAR *, LPSTR, LPSTR, LPVOID); + +extern HRESULT WINAPI DirectSoundCreate(GUID FAR * lpGUID, LPDIRECTSOUND * ppDS, IUnknown FAR *pUnkOuter ); +extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW lpCallback, LPVOID lpContext ); +extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext ); + +#ifdef UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKW +#define DirectSoundEnumerate DirectSoundEnumerateW +#else +#define LPDSENUMCALLBACK LPDSENUMCALLBACKA +#define DirectSoundEnumerate DirectSoundEnumerateA +#endif + +// +// IDirectSound +// +#undef INTERFACE +#define INTERFACE IDirectSound +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSound, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectSound methods ***/ + + STDMETHOD( CreateSoundBuffer)(THIS_ LPDSBUFFERDESC, LPLPDIRECTSOUNDBUFFER, IUnknown FAR *) PURE; + STDMETHOD( GetCaps)(THIS_ LPDSCAPS ) PURE; + STDMETHOD( DuplicateSoundBuffer)(THIS_ LPDIRECTSOUNDBUFFER, LPLPDIRECTSOUNDBUFFER ) PURE; + STDMETHOD( SetCooperativeLevel)(THIS_ HWND, DWORD ) PURE; + STDMETHOD( Compact)(THIS ) PURE; + STDMETHOD( GetSpeakerConfig)(THIS_ LPDWORD ) PURE; + STDMETHOD( SetSpeakerConfig)(THIS_ DWORD ) PURE; + STDMETHOD( Initialize)(THIS_ GUID FAR * ) 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) +#endif + +#endif + +// +// IDirectSoundBuffer +// +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSoundBuffer, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) 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, LPDSBUFFERDESC ) 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_ LPWAVEFORMATEX ) 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) +#endif + +#endif + + + +/* + * 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 DSound 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 ) +/* + * The DSound subsystem couldn't allocate sufficient memory to complete the + * caller's request. + */ +#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 ) +/* + * The Initialize() member on the Direct Sound Object has not been + * called or called successfully before calls to other members. + */ +#define DSERR_UNINITIALIZED MAKE_DSHRESULT( 170 ) + + + + +//==========================================================================; +// +// 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 DSSCL_NORMAL 1 +#define DSSCL_PRIORITY 2 +#define DSSCL_EXCLUSIVE 3 +#define DSSCL_WRITEPRIMARY 4 + + + +#define DSBCAPS_PRIMARYBUFFER 0x00000001 +#define DSBCAPS_STATIC 0x00000002 +#define DSBCAPS_LOCHARDWARE 0x00000004 +#define DSBCAPS_LOCSOFTWARE 0x00000008 +#define DSBCAPS_CTRLFREQUENCY 0x00000020 +#define DSBCAPS_CTRLPAN 0x00000040 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLDEFAULT 0x000000E0 // Pan + volume + frequency. +#define DSBCAPS_CTRLALL 0x000000E0 // All control capabilities +#define DSBCAPS_STICKYFOCUS 0x00004000 + + + + +#define DSSPEAKER_HEADPHONE 1 +#define DSSPEAKER_MONO 2 +#define DSSPEAKER_QUAD 3 +#define DSSPEAKER_STEREO 4 +#define DSSPEAKER_SURROUND 5 + + + + + + +#ifdef __cplusplus +}; +#endif + +#endif /* __DSOUND_INCLUDED__ */ diff --git a/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp b/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp new file mode 100644 index 000000000..82cbede0a --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp @@ -0,0 +1,5508 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +** +** +** Misc asm functions from ww lib +** ST - 12/19/2018 1:20PM +** +** +** +** +** +** +** +** +** +** +** +*/ + +#include "gbuffer.h" +#include "MISC.H" + +IconCacheClass::IconCacheClass (void) +{ + IsCached =FALSE; + SurfaceLost =FALSE; + DrawFrequency =0; + CacheSurface =NULL; + IconSource =NULL; +} + +IconCacheClass::~IconCacheClass (void) +{ +} + +IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern "C"{ +IconSetType IconSetList[MAX_ICON_SETS]; +short IconCacheLookup[MAX_LOOKUP_ENTRIES]; +} + +int CachedIconsDrawn=0; //Counter of number of cache hits +int UnCachedIconsDrawn=0; //Counter of number of cache misses +BOOL CacheMemoryExhausted; //Flag set if we have run out of video RAM + + +void Invalidate_Cached_Icons (void) {} +void Restore_Cached_Icons (void) {} +void Register_Icon_Set (void *icon_data , BOOL pre_cache) {}; + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void __cdecl Clear_Icon_Pointers (void) {}; +extern "C" void __cdecl Cache_Copy_Icon (void const *icon_ptr ,void * , int) {}; +extern "C" int __cdecl Is_Icon_Cached (void const *icon_data , int icon) {return -1;}; +extern "C" int __cdecl Get_Icon_Index (void *icon_ptr) {return 0;}; +extern "C" int __cdecl Get_Free_Index (void) {return 0;}; +extern "C" BOOL __cdecl Cache_New_Icon (int icon_index, void *icon_ptr) {return -1;}; +extern "C" int __cdecl Get_Free_Cache_Slot(void) {return -1;} + +void IconCacheClass::Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height) {} + + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +extern "C" void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx) +{ +} + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +void __cdecl Buffer_Draw_Line(void *this_object, int sx, int sy, int dx, int dy, unsigned char color) +{ + unsigned int clip_min_x; + unsigned int clip_max_x; + unsigned int clip_min_y; + unsigned int clip_max_y; + unsigned int clip_var; + unsigned int accum; + unsigned int bpr; + + static int _one_time_init = 0; + + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + static void *_clip_table [4*4] = {0}; + + unsigned int int_color = color; + unsigned int x1_pixel = (unsigned int) sx; + unsigned int y1_pixel = (unsigned int) sy; + unsigned int x2_pixel = (unsigned int) dx; + unsigned int y2_pixel = (unsigned int) dy; + + __asm { + mov eax,_one_time_init + and eax,eax + jnz init_done + + call do_init + +init_done: + + //;*================================================================== + //;* Take care of find the clip minimum and maximums + //;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[ebx]GraphicViewPortClass.Width + mov [clip_max_x],eax + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + mov [bpr],eax + mov eax,[ebx]GraphicViewPortClass.Height + mov [clip_max_y],eax + + //;*================================================================== + //;* Adjust max pixels as they are tested inclusively. + //;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + //;*================================================================== + //;* Set the registers with the data for drawing the line + //;*================================================================== + mov eax,[x1_pixel] //; eax = start x pixel position + mov ebx,[y1_pixel] //; ebx = start y pixel position + mov ecx,[x2_pixel] //; ecx = dest x pixel position + mov edx,[y2_pixel] //; edx = dest y pixel position + + //;*================================================================== + //;* This is the section that "pushes" the line into bounds. + //;* I have marked the section with PORTABLE start and end to signify + //;* how much of this routine is 100% portable between graphics modes. + //;* It was just as easy to have variables as it would be for constants + //;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + //;* to clip the line (default is the screen) + //;* PORTABLE start + //;*================================================================== + + cmp eax,[clip_min_x] + jl short clip_it + cmp eax,[clip_max_x] + jg short clip_it + cmp ebx,[clip_min_y] + jl short clip_it + cmp ebx,[clip_max_y] + jg short clip_it + cmp ecx,[clip_min_x] + jl short clip_it + cmp ecx,[clip_max_x] + jg short clip_it + cmp edx,[clip_min_y] + jl short clip_it + cmp edx,[clip_max_y] + jle short on_screen + + //;*================================================================== + //;* Takes care off clipping the line. + //;*================================================================== + clip_it: + call set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call set_bits + mov [clip_var],edi + or [clip_var],esi + jz short on_screen + test edi,esi + jne short off_screen + shl esi,2 + //call [clip_tbl+esi] + call [_clip_table+esi] + jc clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + //call [clip_tbl+edi] + call [_clip_table+edi] + jmp clip_it + + on_screen: + jmp draw_it + + off_screen: + jmp and_out + + //;*================================================================== + //;* Jump table for clipping conditions + //;*================================================================== + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + nada: + clc + ret + + a_up: + mov esi,[clip_min_y] + call clip_vert + stc + ret + + a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call clip_vert + neg ebx + neg edx + stc + ret + + //;*================================================================== + //;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + //;*================================================================== + clip_vert: + push edx + push eax + mov [clip_var],edx //; clip_var = yb + sub [clip_var],ebx //; clip_var = (yb-ya) + neg eax //; eax=-xa + add eax,ecx //; (ebx-xa) + mov edx,esi //; edx=miny + sub edx,ebx //; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + ret + + a_lft: + mov esi,[clip_min_x] + call clip_horiz + stc + ret + + a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call clip_horiz + neg eax + neg ecx + stc + ret + + //;*================================================================== + //;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + //;*================================================================== + clip_horiz: + push edx + mov [clip_var],ecx //; clip_var = xb + sub [clip_var],eax //; clip_var = (xb-xa) + sub edx,ebx //; edx = (yb-ya) + neg eax //; eax = -xa + add eax,esi //; eax = (minx-xa) + imul edx //; eax = (minx-xa)(yb-ya) + idiv [clip_var] //; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax //; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + ret + + //;*================================================================== + //;* Sets the condition bits + //;*================================================================== + set_bits: + xor esi,esi + cmp ebx,[clip_min_y] //; if y >= top its not up + jge short a_not_up + or esi,1 + + a_not_up: + cmp ebx,[clip_max_y] //; if y <= bottom its not down + jle short a_not_down + or esi,2 + + a_not_down: + cmp eax,[clip_min_x] //; if x >= left its not left + jge short a_not_left + or esi,4 + + a_not_left: + cmp eax,[clip_max_x] //; if x <= right its not right + jle short a_not_right + or esi,8 + + a_not_right: + ret + + //;*================================================================== + //;* Draw the line to the screen. + //;* PORTABLE end + //;*================================================================== + draw_it: + sub edx,ebx //; see if line is being draw down + jnz short not_hline //; if not then its not a hline + jmp short hline //; do special case h line + + not_hline: + jg short down //; if so there is no need to rev it + neg edx //; negate for actual pixel length + xchg eax,ecx //; swap x's to rev line draw + sub ebx,edx //; get old edx + + down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + + mov esi,1 //; assume a right mover + sub ecx,eax //; see if line is right + jnz short not_vline //; see if its a vertical line + jmp vline + + not_vline: + jg short right //; if so, the difference = length + + //left: + neg ecx //; else negate for actual pixel length + neg esi //; negate counter to move left + + right: + cmp ecx,edx //; is it a horiz or vert line + jge short horiz //; if ecx > edx then |x|>|y| or horiz + + //vert: + xchg ecx,edx //; make ecx greater and edx lesser + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //vert_loop: + add ebx,eax + mov eax,[int_color] + + v_midloop: + mov [ebx],al + dec ecx + jl and_out + add ebx,[bpr] + sub [accum],edx //; sub the lesser + jge v_midloop //; any line could be new + add [accum],edi //; add greater for new accum + add ebx,esi //; next pixel over + jmp v_midloop + + horiz: + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //horiz_loop: + add ebx,eax + mov eax,[int_color] + + h_midloop: + mov [ebx],al + dec ecx //; dec counter + jl and_out //; end of line + add ebx,esi + sub [accum],edx //; sub the lesser + jge h_midloop + add [accum],edi //; add greater for new accum + add ebx,[bpr] //; goto next line + jmp h_midloop + + //;*================================================================== + //;* Special case routine for horizontal line draws + //;*================================================================== + hline: + cmp eax,ecx //; make eax < ecx + jl short hl_ac + xchg eax,ecx + + hl_ac: + sub ecx,eax //; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg big_line + mov al,[color] + rep stosb //; write as many words as possible + jmp short and_out //; get outt + + + big_line: + mov al,[color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + + aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp and_out + + + //;*================================================================== + //;* a special case routine for vertical line draws + //;*================================================================== + vline: + mov ecx,edx //; get length of line to draw + inc ecx + add ebx,eax + mov eax,[int_color] + + vl_loop: + mov [ebx],al //; store bit + add ebx,[bpr] + dec ecx + jnz vl_loop + jmp and_out + + +do_init: + mov edi, offset _clip_table + + lea esi, nada + mov [edi], esi + mov [edi+12], esi + lea esi, a_up + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + + add edi, 16 + + lea esi, a_lft + mov [edi], esi + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, a_rgt + mov [edi], esi + mov [edi+8], esi + lea esi, a_up + mov [edi+4], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, nada + mov [edi], esi + mov [edi+4], esi + mov [edi+8], esi + mov [edi+12], esi + + mov [_one_time_init], 1 + ret + + and_out: + } +} + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg ??big_line + mov al,[byte color] + rep stosb ; write as many words as possible + jmp short ??out ; get outt + + +??big_line: + mov al,[byte color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + +??aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp ??out + + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP Buffer_Draw_Line + + +*/ + + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* +*/ + +/* +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +*/ +#define OPTIMAL_BYTE_COPY 14 + + +void __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color) +{ +/* + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a member function + ARG x1_pixel:WORD + ARG y1_pixel:WORD + ARG x2_pixel:WORD + ARG y2_pixel:WORD + ARG color:BYTE ; what color should we clear to +*/ + + void *this_object = thisptr; + int x1_pixel = sx; + int y1_pixel = sy; + int x2_pixel = dx; + int y2_pixel = dy; + +/* + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL VPwidth:DWORD ; the width of the viewport + LOCAL VPheight:DWORD ; the height of the viewport + LOCAL VPxadd:DWORD ; the additional x offset of viewport + LOCAL VPbpr:DWORD ; the number of bytes per row of viewport +*/ + + int VPwidth; + int VPheight; + int VPxadd; + int VPbpr; + + int local_ebp; // Can't use ebp + + __asm { + + ;*=================================================================== + ;* save off the viewport characteristics on the stack + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + mov eax,[ebx]GraphicViewPortClass.Width ; get width from viewport + mov ecx,[ebx]GraphicViewPortClass.Height ; get height from viewport + mov edx,[ebx]GraphicViewPortClass.XAdd ; get xadd from viewport + add edx,[ebx]GraphicViewPortClass.Pitch ; extra pitch of direct draw surface + mov [VPwidth],eax ; store the width of locally + mov [VPheight],ecx + mov [VPxadd],edx + add eax,edx + mov [VPbpr],eax + + ;*=================================================================== + ;* move the important parameters into local registers + ;*=================================================================== + mov eax,[x1_pixel] + mov ebx,[y1_pixel] + mov ecx,[x2_pixel] + mov edx,[y2_pixel] + + ;*=================================================================== + ;* Convert the x2 and y2 pixel to a width and height + ;*=================================================================== + cmp eax,ecx + jl no_swap_x + xchg eax,ecx + + no_swap_x: + sub ecx,eax + cmp ebx,edx + jl no_swap_y + xchg ebx,edx + no_swap_y: + sub edx,ebx + inc ecx + inc edx + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + cmp eax, [VPwidth] ; compare with the max + jge done ; starts off screen, then later + jb short sx_done ; if it's not negative, it's ok + + ;------ Clip source X to left edge of screen. + add ecx, eax ; Reduce width (add in negative src X). + xor eax, eax ; Clip to left of screen. + sx_done: + + ;*=================================================================== + ;* Bounds check source Y. + ;*=================================================================== + cmp ebx, [VPheight] ; compare with the max + jge done ; starts off screen, then later + jb short sy_done ; if it's not negative, it's ok + + ;------ Clip source Y to top edge of screen. + add edx, ebx ; Reduce height (add in negative src Y). + xor ebx, ebx ; Clip to top of screen. + + sy_done: + ;*=================================================================== + ;* Bounds check width versus width of source and dest view ports + ;*=================================================================== + push ebx ; save off ebx for later use + mov ebx,[VPwidth] ; get the source width + sub ebx, eax ; Maximum allowed pixel width (given coordinates). + sub ebx, ecx ; Pixel width undershoot. + jns short width_ok ; if not signed no adjustment necessary + add ecx, ebx ; Reduce width to screen limits. + + width_ok: + pop ebx ; restore ebx to old value + + ;*=================================================================== + ;* Bounds check height versus height of source view port + ;*=================================================================== + push eax ; save of eax for later use + mov eax, [VPheight] ; get the source height + sub eax, ebx ; Maximum allowed pixel height (given coordinates). + sub eax, edx ; Pixel height undershoot. + jns short height_ok ; if not signed no adjustment necessary + add edx, eax ; Reduce height to screen limits. + height_ok: + pop eax ; restore eax to old value + + ;*=================================================================== + ;* Perform the last minute checks on the width and height + ;*=================================================================== + or ecx,ecx + jz done + + or edx,edx + jz done + + cmp ecx,[VPwidth] + ja done + cmp edx,[VPheight] + ja done + + ;*=================================================================== + ;* Get the offset into the virtual viewport. + ;*=================================================================== + xchg edi,eax ; save off the contents of eax + xchg esi,edx ; and edx for size test + mov eax,ebx ; move the y pixel into eax + mul [VPbpr] ; multiply by bytes per row + add edi,eax ; add the result into the x position + mov ebx,[this_object] + add edi,[ebx]GraphicViewPortClass.Offset + + mov edx,esi ; restore edx back to real value + mov eax,ecx ; store total width in ecx + sub eax,[VPwidth] ; modify xadd value to include clipped + sub [VPxadd],eax ; width bytes (subtract a negative number) + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ebx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,bx + + ;*=================================================================== + ; If there is no row offset then adjust the width to be the size of + ; the entire viewport and adjust the height to be 1 + ;*=================================================================== + mov esi,[VPxadd] + or esi,esi ; set the flags for esi + jnz row_by_row_aligned ; and act on them + + xchg eax,ecx ; switch bit pattern and width + mul edx ; multiply by edx to get size + xchg eax,ecx ; switch size and bit pattern + mov edx,1 ; only 1 line off view port size to do + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + row_by_row_aligned: + mov [local_ebp],ecx ; width saved in ebp + cmp ecx,OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl row_by_row ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + mov ebx,edi ; get output position + and ebx,3 ; is there a remainder? + jz aligned_loop ; if not we are aligned + xor ebx,3 ; find number of align bytes + inc ebx ; this number is off by one + sub [local_ebp],ebx ; subtract from width + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== + aligned_loop: + mov ecx,ebx ; get number of bytes to align + rep stosb ; and move them over + mov ecx,[local_ebp] ; get number of aligned bytes + shr ecx,2 ; convert to DWORDS + rep stosd ; and move them over + mov ecx,[local_ebp] ; get number of aligned bytes + and ecx,3 ; find the remainder + rep stosb ; and move it over + add edi,esi ; fix the line offset + dec edx ; decrement the height + jnz aligned_loop ; if more to do than do it + jmp done ; we are all done + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== + row_by_row: + mov ecx,[local_ebp] ; get total width in bytes + rep stosb ; store the width + add edi,esi ; handle the xadd + dec edx ; decrement the height + jnz row_by_row ; if any left then next line + done: + } +} + + + + +/* +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* +*/ +void __cdecl Buffer_Clear(void *this_object, unsigned char color) +{ + unsigned int local_color = color; + + __asm { + + cld ; always go forward + + mov ebx,[this_object] ; get a pointer to viewport + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov edx,[ebx]GraphicViewPortClass.Height ; get height from viewport + mov esi,[ebx]GraphicViewPortClass.Width ; get width from viewport + //push [dword (GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + push [ebx]GraphicViewPortClass.Pitch + + mov ebx,[ebx]GraphicViewPortClass.XAdd ; esi = add for each line + add ebx,[esp] ; Yes, I know its nasty but + add esp,4 ; it works! + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl byte_by_byte ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + push ebx + + dword_aligned_loop: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz dword_aligned_loop ; if more to do than do it + pop eax + jmp done + //ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== + byte_by_byte: + mov ecx,esi ; get total width in bytes + rep stosb ; store the width + add edi,ebx ; handle the xadd + dec edx ; decrement the height + jnz byte_by_byte ; if any left then next line + done: + } +} + + + + + + + + + + + + + + +BOOL __cdecl Linear_Blit_To_Linear( void *this_object, void * dest, int x_pixel, int y_pixel, int dest_x0, int dest_y0, int pixel_width, int pixel_height, BOOL trans) +{ +/* + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword +*/ + + int x1_pixel; + int y1_pixel; + int dest_x1; + int dest_y1; + int scr_adjust_width; + int dest_adjust_width; + int source_area; + int dest_area; + + __asm { + + ;This Clipping algorithm is a derivation of the very well known + ;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency + ;it is probably the most commontly implemented algorithm both in software + ;and hardware for clipping lines, rectangles, and convex polygons against + ;a rectagular clipping window. For reference see + ;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes + ; pages 113 to 177". + ; Briefly consist in computing the Sutherland code for both end point of + ; the rectangle to find out if the rectangle is: + ; - trivially accepted (no further clipping test, display rectangle) + ; - trivially rejected (return with no action) + ; - retangle must be iteratively clipped again edges of the clipping window + ; and the remaining retangle is display. + + ; Clip Source Rectangle against source Window boundaries. + mov esi,[this_object] ; get ptr to src + xor ecx,ecx ; Set sutherland code to zero + xor edx,edx ; Set sutherland code to zero + + ; compute the difference in the X axis and get the bit signs into ecx , edx + mov edi,[esi]GraphicViewPortClass.Width ; get width into register + mov ebx,[x_pixel] ; Get first end point x_pixel into register + mov eax,[x_pixel] ; Get second end point x_pixel into register + add ebx,[pixel_width] ; second point x1_pixel = x + width + shld ecx, eax,1 ; the sign bit of x_pixel is sutherland code0 bit4 + mov [x1_pixel],ebx ; save second for future use + inc edi ; move the right edge by one unit + shld edx,ebx,1 ; the sign bit of x1_pixel is sutherland code0 bit4 + sub eax,edi ; compute the difference x0_pixel - width + sub ebx,edi ; compute the difference x1_pixel - width + shld ecx,eax,1 ; the sign bit of the difference is sutherland code0 bit3 + shld edx,ebx,1 ; the sign bit of the difference is sutherland code0 bit3 + + ; the following code is just a repeticion of the above code + ; in the Y axis. + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov ebx,[y_pixel] + mov eax,[y_pixel] + add ebx,[pixel_height] + shld ecx,eax,1 + mov [y1_pixel ],ebx + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + ; Here we have the to Sutherland code into cl and dl + xor cl,5 ; bit 2 and 0 are complented, reverse then + xor dl,5 ; bit 2 and 0 are complented, reverse then + mov al,cl ; save code1 in case we have to clip iteratively + test dl,cl ; if any bit in code0 and its counter bit + jnz real_out ; in code1 is set then the rectangle in outside + or al,dl ; if all bit of code0 the counter bit in + jz clip_against_dest ; in code1 is set to zero, then all + ; end points of the rectangle are + ; inside the clipping window + + ; if we are here the polygon have to be clip iteratively + test cl,1000b ; if bit 4 in code0 is set then + jz scr_left_ok ; x_pixel is smaller than zero + mov [x_pixel],0 ; set x_pixel to cero. + + scr_left_ok: + test cl,0010b ; if bit 2 in code0 is set then + jz scr_bottom_ok ; y_pixel is smaller than zero + mov [ y_pixel ],0 ; set y_pixel to cero. + + scr_bottom_ok: + test dl,0100b ; if bit 3 in code1 is set then + jz scr_right_ok ; x1_pixel is greater than the width + mov eax,[esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ],eax ; set x1_pixel to width. + scr_right_ok: + test dl,0001b ; if bit 0 in code1 is set then + jz clip_against_dest ; y1_pixel is greater than the width + mov eax,[esi]GraphicViewPortClass.Height ; get height into register + mov [ y1_pixel ],eax ; set y1_pixel to height. + + ; Clip Source Rectangle against destination Window boundaries. + clip_against_dest: + + ; build the destination rectangle before clipping + ; dest_x1 = dest_x0 + ( x1_pixel - x_pixel ) + ; dest_y1 = dest_y0 + ( y1_pixel - y_pixel ) + mov eax,[dest_x0] ; get dest_x0 into eax + mov ebx,[dest_y0] ; get dest_y0 into ebx + sub eax,[x_pixel] ; subtract x_pixel from eax + sub ebx,[y_pixel] ; subtract y_pixel from ebx + add eax,[x1_pixel] ; add x1_pixel to eax + add ebx,[y1_pixel] ; add y1_pixel to ebx + mov [dest_x1],eax ; save eax into dest_x1 + mov [dest_y1],ebx ; save eax into dest_y1 + + + ; The followin code is a repeticion of the Sutherland clipping + ; descrived above. + mov esi,[dest] ; get ptr to src + xor ecx,ecx + xor edx,edx + mov edi,[esi]GraphicViewPortClass.Width ; get width into register + mov eax,[dest_x0] + mov ebx,[dest_x1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax,[dest_y0] + mov ebx,[dest_y1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + xor cl,5 + xor dl,5 + mov al,cl + test dl,cl + jnz real_out + or al,dl + jz do_blit + + test cl,1000b + jz dest_left_ok + mov eax,[ dest_x0 ] + mov [ dest_x0 ],0 + sub [ x_pixel ],eax + + dest_left_ok: + test cl,0010b + jz dest_bottom_ok + mov eax,[ dest_y0 ] + mov [ dest_y0 ],0 + sub [ y_pixel ],eax + + + dest_bottom_ok: + test dl,0100b + jz dest_right_ok + mov ebx,[esi]GraphicViewPortClass.Width ; get width into register + mov eax,[ dest_x1 ] + mov [ dest_x1 ],ebx + sub eax,ebx + sub [ x1_pixel ],eax + + dest_right_ok: + test dl,0001b + jz do_blit + mov ebx,[esi]GraphicViewPortClass.Height ; get width into register + mov eax,[ dest_y1 ] + mov [ dest_y1 ],ebx + sub eax,ebx + sub [ y1_pixel ],eax + + + ; Here is where we do the actual blit + do_blit: + cld + mov ebx,[this_object] + mov esi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.Pitch + mov ecx,eax + mul [y_pixel] + add esi,[x_pixel] + mov [source_area],ecx + add esi,eax + + add ecx,[x_pixel ] + sub ecx,[x1_pixel ] + mov [scr_adjust_width ],ecx + + mov ebx,[dest] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.Pitch + mov ecx,eax + mul [ dest_y0 ] + add edi,[ dest_x0 ] + mov [ dest_area ],ecx + add edi,eax + + mov eax,[ dest_x1 ] + sub eax,[ dest_x0 ] + jle real_out + sub ecx,eax + mov [ dest_adjust_width ],ecx + + mov edx,[ dest_y1 ] + sub edx,[ dest_y0 ] + jle real_out + + cmp esi,edi + jz real_out + jl backupward_blit + + ; ******************************************************************** + ; Forward bitblit + + test [ trans ],1 + jnz forward_Blit_trans + + + ; the inner loop is so efficient that + ; the optimal consept no longer apply because + ; the optimal byte have to by a number greather than 9 bytes + cmp eax,10 + jl forward_loop_bytes + + forward_loop_dword: + mov ecx,edi + mov ebx,eax + neg ecx + and ecx,3 + sub ebx,ecx + rep movsb + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,3 + rep movsb + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_dword + jmp real_out //ret + + forward_loop_bytes: + mov ecx,eax + rep movsb + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_bytes + jmp real_out + + forward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + + forward_loop_trans: + mov ecx,eax + jmp [ y1_pixel ] + forward_trans_line: + //REPT 32 + //local transp_pixel + //No REPT in msvc inline assembly. + // Save ECX and use as counter instead. ST - 12/19/2018 5:41PM + push ecx + mov ecx, 32 + + rept_loop: + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + inc esi + inc edi + + dec ecx //ST - 12/19/2018 5:44PM + jnz rept_loop //ST - 12/19/2018 5:44PM + + pop ecx //ST - 12/19/2018 5:44PM + + //ENDM + transp_reference: + dec ecx + jge forward_trans_line + add esi,[ scr_adjust_width ] + add edi,[ dest_adjust_width ] + dec edx + jnz forward_loop_trans + jmp real_out //ret + + + ; ************************************************************************ + ; backward bitblit + + backupward_blit: + + mov ebx,[ source_area ] + dec edx + add esi,eax + imul ebx,edx + std + lea esi,[ esi + ebx - 1 ] + + mov ebx,[ dest_area ] + add edi,eax + imul ebx,edx + lea edi,[ edi + ebx - 1] + + test [ trans ],1 + jnz backward_Blit_trans + + cmp eax,15 + jl backward_loop_bytes + + backward_loop_dword: + push edi + push esi + lea ecx,[edi+1] + mov ebx,eax + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_dword + cld + jmp real_out //ret + + backward_loop_bytes: + push edi + mov ecx,eax ; remove that from the total size to be copied later. + push esi + rep movsb ; do the copy. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_bytes + cld + jmp real_out //ret + + backward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ back_transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + + backward_loop_trans: + mov ecx,eax + push edi + push esi + jmp [ y1_pixel ] + backward_trans_line: + //REPT 32 + //local transp_pixel2 + //No REPT in msvc inline assembly. + // Save ECX and use as counter instead. ST - 12/19/2018 5:41PM + push ecx + mov ecx, 32 + rept_loop2: + mov bl,[ esi ] + test bl,bl + jz transp_pixel2 + mov [ edi ],bl + transp_pixel2: + dec esi + dec edi + + dec ecx //ST - 12/19/2018 5:44PM + jnz rept_loop2 //ST - 12/19/2018 5:44PM + + pop ecx //ST - 12/19/2018 5:44PM + + //ENDM + + back_transp_reference: + dec ecx + jge backward_trans_line + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge backward_loop_trans + cld + //ret + + real_out: + } +} + + + + + + + + + + + + +/* +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +// Ran out of registers so had to use ebp. ST - 12/19/2018 6:22PM +#pragma warning (push) +#pragma warning (disable : 4731) + +BOOL __cdecl Linear_Scale_To_Linear(void *this_object, void *dest, int src_x, int src_y, int dst_x, int dst_y, int src_width, int src_height, int dst_width, int dst_height, BOOL trans, char *remap) +{ +/* + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword +*/ + + int src_x0; + int src_y0; + int src_x1; + int src_y1; + + int dst_x0; + int dst_y0; + int dst_x1; + int dst_y1; + + int src_win_width; + int dst_win_width; + int dy_intr; + int dy_frac; + int dy_acc; + int dx_frac; + + int counter_x; + int counter_y; + int remap_counter; + int entry; + + + __asm { + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je all_done + cmp [dst_height],0 + je all_done + cmp [src_width],0 + je all_done + cmp [src_height],0 + je all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + + ; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz all_done + or al , dl + jz clip_against_dest + mov bl , dl + test cl , 1000b + jz src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + + src_left_ok: + test cl , 0010b + jz src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + + src_bottom_ok: + test bl , 0100b + jz src_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + + src_right_ok: + test bl , 0001b + jz clip_against_dest + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + + ; Clip destination Rectangle against source Window boundaries. + clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz all_done + or al , dl + jz do_scaling + mov bl , dl + test cl , 1000b + jz dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + + dst_left_ok: + test cl , 0010b + jz dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + + dst_bottom_ok: + test bl , 0100b + jz dst_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + + dst_right_ok: + test bl , 0001b + jz do_scaling + + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + + do_scaling: + + cld + mov ebx , [ this_object ] + mov esi , [ebx]GraphicViewPortClass. Offset + mov eax , [ebx]GraphicViewPortClass. XAdd + add eax , [ebx]GraphicViewPortClass. Width + add eax , [ebx]GraphicViewPortClass. Pitch + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ebx]GraphicViewPortClass. Offset + mov eax , [ebx]GraphicViewPortClass. XAdd + add eax , [ebx]GraphicViewPortClass. Width + add eax , [ebx]GraphicViewPortClass. Pitch + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jle all_done + sub ebx , [ dst_x0 ] + jle all_done + + mov [ counter_y ] , ecx + + cmp [ trans ] , 0 + jnz transparency + + cmp [ remap ] , 0 + jnz normal_remap + + ; ************************************************************************* + ; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + outter_loop: + push esi + push edi + xor ecx , ecx + mov ebx , [ counter_x ] + jmp [ entry ] + inner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + push ebx //ST - 12/19/2018 6:11PM + mov ebx,32 //ST - 12/19/2018 6:11PM +rept_loop: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + + dec ebx //ST - 12/19/2018 6:11PM + jnz rept_loop //ST - 12/19/2018 6:11PM + pop ebx //ST - 12/19/2018 6:11PM + //ENDM + ref_point: + dec ebx + jge inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz outter_loop + jmp all_done //ret + + + ; ************************************************************************* + ; normal scale with remap + + normal_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ remapref_point + ecx ] + mov [ entry ] , ecx + + remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + remapinner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + + //ENDM + remapref_point: + dec [ remap_counter ] + jge remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz remapoutter_loop + jmp all_done //ret + + + ;**************************************************************************** + ; scale with trnsparency + + transparency: + cmp [ remap ] , 0 + jnz trans_remap + + ; ************************************************************************* + ; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ trans_ref_point + ecx ] + mov [ entry ] , ecx + + trans_outter_loop: + xor ecx , ecx + push esi + push edi + mov ebx , [ counter_x ] + jmp [ entry ] + trans_inner_loop: + + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + push ebx //ST - 12/19/2018 6:11PM + mov ebx,32 //ST - 12/19/2018 6:11PM +rept_loop2: + + mov cl , [ esi ] + test cl , cl + jz trans_pixel + mov [ edi ] , cl + trans_pixel: + add ecx , eax + adc esi , edx + inc edi + + dec ebx //ST - 12/19/2018 6:11PM + jnz rept_loop2 //ST - 12/19/2018 6:11PM + pop ebx //ST - 12/19/2018 6:11PM + + //ENDM + trans_ref_point: + dec ebx + jge trans_inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle trans_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + trans_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz trans_outter_loop + jmp all_done //ret + + + ; ************************************************************************* + ; normal scale with remap + + trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ trans_remapref_point + ecx ] + mov [ entry ] , ecx + + trans_remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + + + trans_remapinner_loop: + // REPT not supported for inline asm. ST - 12/19/2018 6:11PM + //REPT 32 + // Run out of registers so use ebp + push ebp //ST - 12/19/2018 6:11PM + mov ebp,32 //ST - 12/19/2018 6:11PM +rept_loop3: + mov bl , [ esi ] + test bl , bl + jz trans_pixel2 + mov cl , [ eax + ebx ] + mov [ edi ] , cl + trans_pixel2: + add ecx , [ dx_frac ] + adc esi , edx + inc edi + + dec ebp //ST - 12/19/2018 6:11PM + jnz rept_loop3 //ST - 12/19/2018 6:11PM + pop ebp //ST - 12/19/2018 6:11PM + + //ENDM + + trans_remapref_point: + dec [ remap_counter ] + jge trans_remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] + trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz trans_remapoutter_loop + //ret + + + all_done: + } +} + + +#pragma warning (pop) + + + + + + + + + + + + + + + + + + + + +unsigned int LastIconset = 0; +unsigned int StampPtr = 0; // DD 0 ; Pointer to icon data. + +unsigned int IsTrans = 0; // DD 0 ; Pointer to transparent icon flag table. + +unsigned int MapPtr = 0; // DD 0 ; Pointer to icon map. +unsigned int IconWidth = 0; // DD 0 ; Width of icon in pixels. +unsigned int IconHeight = 0; // DD 0 ; Height of icon in pixels. +unsigned int IconSize = 0; // DD 0 ; Number of bytes for each icon data. +unsigned int IconCount = 0; // DD 0 ; Number of icons in the set. + + + +#if (0) +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + +GLOBAL C Buffer_Draw_Stamp:near +GLOBAL C Buffer_Draw_Stamp_Clip:near + +; 256 color icon system. +#endif + + +/* +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* +*/ +extern "C" void __cdecl Init_Stamps(unsigned int icondata) +{ + + __asm { + pushad // ST - 12/20/2018 10:30AM + + ; Verify legality of parameter. + cmp [icondata],0 + je short fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[edi]IControl_Type.Count + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[edi]IControl_Type.Width + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[edi]IControl_Type.Height + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[edi]IControl_Type.Map + add eax,edi + mov [MapPtr],eax + +//nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[edi]IControl_Type.Icons + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[edi]IControl_Type.TransFlag + mov [IsTrans],eax + +fini: + popad // ST - 12/20/2018 10:30AM + + } +} + + +/* +;*********************************************************** + +;*********************************************************** +; DRAW_STAMP +; +; VOID cdecl Buffer_Draw_Stamp(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* +*/ + +void __cdecl Buffer_Draw_Stamp(void const *this_object, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + unsigned int modulo = 0; + unsigned int iwidth = 0; + unsigned char doremap = 0; + + +/* + PROC Buffer_Draw_Stamp C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL doremap:BYTE ; Should remapping occur? +*/ + + __asm { + + pushad + cmp [icondata],0 + je proc_out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short noreset + push eax + call Init_Stamps + pop eax // Clean up stack. ST - 12/20/2018 10:42AM +noreset: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short notmap + mov edi,[MapPtr] + mov bl,[edi+ebx] +notmap: + cmp ebx,[IconCount] + jae proc_out + mov [icon],ebx ; Updated icon number. + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[IconWidth] + mov [modulo],eax + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short istranscheck + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + ;;; mov edx,[remap] + mov ebx,[remap] + xor eax,eax +xrowloop: + push ecx + mov ecx,[iwidth] + +xcolumnloop: + lodsb + ;;; mov ebx,edx + ;;; add ebx,eax + ;;; mov al,[ebx] ; New real color to draw. + xlatb + or al,al + jz short xskip1 ; Transparency skip check. + mov [edi],al +xskip1: + inc edi + loop xcolumnloop + + pop ecx + add edi,[modulo] + loop xrowloop + jmp short proc_out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +istranscheck: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short rowloop + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + shr ebx,2 + mov edx,[modulo] + mov eax,[iwidth] + shr eax,2 +loop1: + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + dec ebx + jnz loop1 + jmp short proc_out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +rowloop: + push ecx + mov ecx,[iwidth] + +columnloop: + lodsb + or al,al + jz short skip1 ; Transparency check. + mov [edi],al +skip1: + inc edi + loop columnloop + + pop ecx + add edi,[modulo] + loop rowloop + + ; Cleanup and exit icon drawing routine. +proc_out: + popad + //ret + } +} + + + + +/* +;*********************************************************** +; DRAW_STAMP_CLIP +; +; VOID cdecl MCGA_Draw_Stamp_Clip(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap, LONG min_x, LONG min_y, LONG max_x, LONG max_y); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* +*/ +void __cdecl Buffer_Draw_Stamp_Clip(void const *this_object, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int min_x, int min_y, int max_x, int max_y) +{ + + + unsigned int modulo = 0; + unsigned int iwidth = 0; + unsigned int skip = 0; + unsigned char doremap = 0; + + +/* + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + ARG min_x:DWORD ; Clipping rectangle boundary + ARG min_y:DWORD ; Clipping rectangle boundary + ARG max_x:DWORD ; Clipping rectangle boundary + ARG max_y:DWORD ; Clipping rectangle boundary + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL skip:DWORD ; amount to skip per row of icon data + LOCAL doremap:BYTE ; Should remapping occur? +*/ + __asm { + pushad + cmp [icondata],0 + je proc_out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short noreset2 + push eax + call Init_Stamps + pop eax // Clean up stack. ST - 12/20/2018 10:42AM +noreset2: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short notmap2 + mov edi,[MapPtr] + mov bl,[edi+ebx] +notmap2: + cmp ebx,[IconCount] + jae proc_out + mov [icon],ebx ; Updated icon number. + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Update the clipping window coordinates to be valid maxes instead of width & height + ; , and change the coordinates to be window-relative + mov ebx,[min_x] + add [max_x],ebx + add [x_pixel],ebx ; make it window-relative + mov ebx,[min_y] + add [max_y],ebx + add [y_pixel],ebx ; make it window-relative + + ; See if the icon is within the clipping window + ; First, verify that the icon position is less than the maximums + mov ebx,[x_pixel] + cmp ebx,[max_x] + jge proc_out + mov ebx,[y_pixel] + cmp ebx,[max_y] + jge proc_out + ; Now verify that the icon position is >= the minimums + add ebx,[IconHeight] + cmp ebx,[min_y] + jle proc_out + mov ebx,[x_pixel] + add ebx,[IconWidth] + cmp ebx,[min_x] + jle proc_out + + ; Now, clip the x, y, width, and height variables to be within the + ; clipping rectangle + mov ebx,[x_pixel] + cmp ebx,[min_x] + jge nominxclip + ; x < minx, so must clip + mov ebx,[min_x] + sub ebx,[x_pixel] + add esi,ebx ; source ptr += (minx - x) + sub [iwidth],ebx ; icon width -= (minx - x) + mov ebx,[min_x] + mov [x_pixel],ebx + +nominxclip: + mov eax,[IconWidth] + sub eax,[iwidth] + mov [skip],eax + + ; Check for x+width > max_x + mov eax,[x_pixel] + add eax,[iwidth] + cmp eax,[max_x] + jle nomaxxclip + ; x+width is greater than max_x, so must clip width down + mov eax,[iwidth] ; eax = old width + mov ebx,[max_x] + sub ebx,[x_pixel] + mov [iwidth],ebx ; iwidth = max_x - xpixel + sub eax,ebx + add [skip],eax ; skip += (old width - iwidth) +nomaxxclip: + ; check if y < miny + mov eax,[min_y] + cmp eax,[y_pixel] ; if(miny <= y_pixel), no clip needed + jle nominyclip + sub eax,[y_pixel] + sub ecx,eax ; height -= (miny - y) + mul [IconWidth] + add esi,eax ; icon source ptr += (width * (miny - y)) + mov eax,[min_y] + mov [y_pixel],eax ; y = miny +nominyclip: + ; check if (y+height) > max y + mov eax,[y_pixel] + add eax,ecx + cmp eax,[max_y] ; if (y + height <= max_y), no clip needed + jle nomaxyclip + mov ecx,[max_y] ; height = max_y - y_pixel + sub ecx,[y_pixel] +nomaxyclip: + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[ebx]GraphicViewPortClass.Offset + mov eax,[ebx]GraphicViewPortClass.Width + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[iwidth] + mov [modulo],eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short istranscheck2 + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + mov ebx,[remap] + xor eax,eax +xrowloopc: + push ecx + mov ecx,[iwidth] + +xcolumnloopc: + lodsb + xlatb + or al,al + jz short xskip1c ; Transparency skip check. + mov [edi],al +xskip1c: + inc edi + loop xcolumnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop xrowloopc + jmp short proc_out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +istranscheck2: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short rowloopc + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + mov edx,[modulo] + mov eax,[iwidth] + + ; + ; Optimise copy by dword aligning the destination + ; +loop1c: + push eax + //rept 3 // No rept in inline asm. ST - 12/20/2018 10:43AM + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + test edi,3 + jz aligned + movsb + dec eax + jz finishedit + + //endm +aligned: + mov ecx,eax + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,3 + rep movsb + +finishedit: + add edi,edx + add esi,[skip] + pop eax + + dec ebx + jnz loop1c + jmp short proc_out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +rowloopc: + push ecx + mov ecx,[iwidth] + +columnloopc: + lodsb + or al,al + jz short skip1c ; Transparency check. + mov [edi],al +skip1c: + inc edi + loop columnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop rowloopc + + ; Cleanup and exit icon drawing routine. +proc_out: + popad + //ret + } +} + + + + + + + + + + + + + + + + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + + +VOID __cdecl Buffer_Remap(void * this_object, int sx, int sy, int width, int height, void *remap) +{ +/* + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword +*/ + + unsigned int x0_pixel = (unsigned int) sx; + unsigned int y0_pixel = (unsigned int) sy; + unsigned int region_width = (unsigned int) width; + unsigned int region_height = (unsigned int) height; + + unsigned int x1_pixel = 0; + unsigned int y1_pixel = 0; + unsigned int win_width = 0; + unsigned int counter_x = 0; + + __asm { + + cmp [ remap ] , 0 + jz real_out + + ; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_remap + + test cl , 1000b + jz scr_left_ok + mov [ x0_pixel ] , 0 + +scr_left_ok: + test cl , 0010b + jz scr_bottom_ok + mov [ y0_pixel ] , 0 + +scr_bottom_ok: + test dl , 0100b + jz scr_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ] , eax +scr_right_ok: + test dl , 0001b + jz do_remap + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ y1_pixel ] , eax + + +do_remap: + cld + mov edi , [esi]GraphicViewPortClass.Offset + mov eax , [esi]GraphicViewPortClass.XAdd + mov ebx , [ x1_pixel ] + add eax , [esi]GraphicViewPortClass.Width + add eax , [esi]GraphicViewPortClass.Pitch + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +outer_loop: + mov ebx , [ counter_x ] +inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz inner_loop + add edi , esi + dec ecx + jnz outer_loop + + + + +real_out: +// ret + } +} + + + + + + + + + + + + + + + +/* +; ************************************************************************** +; ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S * +; ************************************************************************** +; * * +; * Project Name : WSA Support routines * +; * * +; * File Name : XORDELTA.ASM * +; * * +; * Programmer : Scott K. Bowen * +; * * +; * Last Update :May 23, 1994 [SKB] * +; * * +; *------------------------------------------------------------------------* +; * Functions: * +;* Apply_XOR_Delta -- Apply XOR delta data to a buffer. * +;* Apply_XOR_Delta_To_Page_Or_Viewport -- Calls the copy or the XOR funti* +;* Copy_Delta_buffer -- Copies XOR Delta Data to a section of a page. * +;* XOR_Delta_Buffer -- Xor's the data in a XOR Delta format to a page. * +; * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* + +IDEAL +P386 +MODEL USE32 FLAT +*/ + + +/* +LOCALS ?? + +; These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +; These change, make sure and change their values in wsa.cpp. +DO_XOR equ 0 +DO_COPY equ 1 +TO_VIEWPORT equ 0 +TO_PAGE equ 2 + +; +; Routines defined in this module +; +; +; UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr); +; PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD copy) +; +; PROC C XOR_Delta_Buffer +; PROC C Copy_Delta_Buffer +; + +GLOBAL C Apply_XOR_Delta:NEAR +GLOBAL C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR +*/ + +#define DO_XOR 0 +#define DO_COPY 1 +#define TO_VIEWPORT 0 +#define TO_PAGE 2 + +void __cdecl XOR_Delta_Buffer(int nextrow); +void __cdecl Copy_Delta_Buffer(int nextrow); + + +/* +;*************************************************************************** +;* APPLY_XOR_DELTA -- Apply XOR delta data to a linear buffer. * +;* AN example of this in C is at the botton of the file commented out. * +;* * +;* INPUT: BYTE *target - destination buffer. * +;* BYTE *delta - xor data to be delta uncompress. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 05/23/1994 SKB : Created. * +;*=========================================================================* +*/ +unsigned int __cdecl Apply_XOR_Delta(char *target, char *delta) +{ +/* +PROC Apply_XOR_Delta C near + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointers. + ARG delta:DWORD ; pointers. +*/ + + __asm { + + ; Optimized for 486/pentium by rearanging instructions. + mov edi,[target] ; get our pointers into offset registers. + mov esi,[delta] + + cld ; make sure we go forward + xor ecx,ecx ; use cx for loop + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ;get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edi + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edi ; go to next dest pixel + dec ecx ; one less to go. + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] + lea esi,[esi+2] ; get word code in ax + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + add edi,eax ; do the skip. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + } +} + + +/* +;---------------------------------------------------------------------------- + +;*************************************************************************** +;* APPLY_XOR_DELTA_To_Page_Or_Viewport -- Calls the copy or the XOR funtion. * +;* * +;* * +;* This funtion is call to either xor or copy XOR_Delta data onto a * +;* page instead of a buffer. The routine will set up the registers * +;* need for the actual routines that will perform the copy or xor. * +;* * +;* The registers are setup as follows : * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ + +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy) +{ + /* + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointer to the destination buffer. + ARG delta:DWORD ; pointer to the delta buffer. + ARG width:DWORD ; width of animation. + ARG nextrow:DWORD ; Page/Buffer width - anim width. + ARG copy:DWORD ; should it be copied or xor'd? + */ + + __asm { + + mov edi,[target] ; Get the target pointer. + mov esi,[delta] ; Get the destination pointer. + + xor eax,eax ; clear eax, later put them into ecx and edx. + + cld ; make sure we go forward + + mov ebx,[nextrow] ; get the amount to add to get to next row from end. push it later... + + mov ecx,eax ; use cx for loop + mov edx,eax ; use dx to count the relative column. + + push ebx ; push nextrow onto the stack for Copy/XOR_Delta_Buffer. + mov ebx,[width] ; bx will hold the max column for speed compares + + ; At this point, all the registers have been set up. Now call the correct function + ; to either copy or xor the data. + + cmp [copy],DO_XOR ; Do we want to copy or XOR + je xorfunct ; Jump to XOR if not copy + call Copy_Delta_Buffer ; Call the function to copy the delta buffer. + jmp didcopy ; jump past XOR + xorfunct: + call XOR_Delta_Buffer ; Call funtion to XOR the deltat buffer. + didcopy: + pop ebx ; remove the push done to pass a value. + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* XOR_DELTA_BUFFER -- Xor's the data in a XOR Delta format to a page. * +;* This will only work right if the page has the previous data on it. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* edx,ecx,eax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl XOR_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edx ; increment our count on current column + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* COPY_DELTA_BUFFER -- Copies XOR Delta Data to a section of a page. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl Copy_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + + mov [edi],al ; store that byte on the dest + + inc edx ; increment our count on current column + inc esi + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + mov [edi],al ; store the byte (instead of XOR against current color) + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} +/* +;---------------------------------------------------------------------------- +*/ + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* +*/ +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac) +{ + /* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + */ + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini + cmp [dest],0 + je fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 + innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne mainloop + + fini: + mov eax,[dest] +// ret + + + } +} + + + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;include "keyboard.inc" +FALSE = 0 +TRUE = 1 + +;****************************** Declarations ******************************** +GLOBAL C Set_Palette_Range:NEAR +GLOBAL C Bump_Color:NEAR +GLOBAL C CurrentPalette:BYTE:768 +GLOBAL C PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG +*/ + +extern "C" unsigned char CurrentPalette[768] = {255}; // DB 768 DUP(255) ; copy of current values of DAC regs +extern "C" unsigned char PaletteTable[1024] = {0}; // DB 1024 DUP(0) + + +/* +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* +*/ +void __cdecl Set_Palette_Range(void *palette) +{ + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); + + /* + PROC Set_Palette_Range C NEAR + ARG palette:DWORD + + GLOBAL Set_DD_Palette_:near + GLOBAL Wait_Vert_Blank_:near + + pushad + mov esi,[palette] + mov ecx,768/4 + mov edi,offset CurrentPalette + cld + rep movsd + ;call Wait_Vert_Blank_ + mov eax,[palette] + push eax + call Set_DD_Palette_ + pop eax + popad + ret + */ +} + + +/* +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +*/ +BOOL __cdecl Bump_Color(void *pal, int color, int desired) +{ + /* +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + */ + + short short_color = (short) color; + short short_desired = (short) desired; + bool changed = false; + + __asm { + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[short_color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[short_desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. + colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] + gotit: + inc edi + inc esi + loop colorloop + + movzx eax,[changed] + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : GraphicViewPortClass * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + +/* +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Put_Pixel C near + USES eax,ebx,ecx,edx,edi +*/ + +void __cdecl Buffer_Put_Pixel(void * this_object, int x_pixel, int y_pixel, unsigned char color) +{ + + /* + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + */ + + __asm { + + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short done ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae done ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; add in direct draw pitch + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen + done: + } +} + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Clip_Rect :NEAR +GLOBAL C Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * +*/ + +extern "C" int __cdecl Clip_Rect ( int * x , int * y , int * w , int * h , int width , int height ) +{ + +/* + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword +*/ + + __asm { + + ;This Clipping algorithm is a derivation of the very well known + ;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency + ;it is probably the most commontly implemented algorithm both in software + ;and hardware for clipping lines, rectangles, and convex polygons against + ;a rectagular clipping window. For reference see + ;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes + ; pages 113 to 177". + ; Briefly consist in computing the Sutherland code for both end point of + ; the rectangle to find out if the rectangle is: + ; - trivially accepted (no further clipping test, return the oroginal data) + ; - trivially rejected (return with no action, return error code) + ; - retangle must be iteratively clipped again edges of the clipping window + ; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + + left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + + bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify + clip_out: + //ret + } + + //ENDP Clip_Rect +} + +/* +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * +*/ + +extern "C" int __cdecl Confine_Rect ( int * x , int * y , int w , int h , int width , int height ) +{ + +/* + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword +*/ + __asm { + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl x_axix_ok + mov eax,1 + + test esi,esi + jl shift_right + mov [dword ptr ebx],0 + jmp x_axix_ok + shift_right: + inc edi + sub [ebx],edi + x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl confi_out + mov eax,1 + + test esi,esi + jl shift_top + mov [dword ptr ebx],0 + + //ret + jmp confi_out + shift_top: + inc edi + sub [ebx],edi + confi_out: + //ret + + //ENDP Confine_Rect + } +} + + + + + + + + + +/* +; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- +*/ + +extern "C" unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length_) +{ +//PROC LCW_Uncompress C near +// +// USES ebx,ecx,edx,edi,esi +// +// ARG source:DWORD +// ARG dest:DWORD +// ARG length:DWORD +//;LOCALS +// LOCAL a1stdest:DWORD +// LOCAL maxlen:DWORD +// LOCAL lastbyte:DWORD +// LOCAL lastcom:DWORD +// LOCAL lastcom1:DWORD + + unsigned long a1stdest; + unsigned long maxlen; + unsigned long lastbyte; + //unsigned long lastcom; + //unsigned long lastcom1; + + __asm { + + + mov edi,[dest] + mov esi,[source] + mov edx,[length_] + + ; + ; + ; uncompress data to the following codes in the format b = byte, w = word + ; n = byte code pulled from compressed data + ; Bit field of n command description + ; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 + ; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes + ; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 + ; n=11111111,w1,w2 long copy copy w1 bytes from offset w2 + ; n=11111110,w1,b1 long run run byte b1 for w1 bytes + ; n=10000000 end end of data reached + ; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + + loop_label: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short out_label ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp loop_label + + notshort: + test al,40h ; is it a length? + jne short notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short out_label ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp loop_label + + out_label: + mov eax,edi + sub eax,[a1stdest] + jmp label_exit + + notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runlenok: + test ecx,0ffe0h + jnz dont_use_stosb + rep stosb + jmp loop_label + + + dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + + aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz loop_label + mov ecx,edx + rep stosb + jmp loop_label + + + + + + + notrunlength: + cmp al,0FFh ; is it a long run? + jne short notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + + notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runok: + test ecx,0ffe0h + jnz dont_use_movsb + rep movsb + jmp loop_label + + + + + dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja use_movsb + + test edi,3 + jz aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + + aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx + use_movsb: + rep movsb + jmp loop_label + + + + + label_exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + //ret + + } +} + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + */ + +extern "C" long __cdecl Buffer_To_Page(int x_pixel, int y_pixel, int pixel_width, int pixel_height, void *src, void *dest) +{ + +/* + PROC Buffer_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + local scr_x : dword + local scr_y : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword +*/ + + unsigned long x1_pixel; + unsigned long y1_pixel; + unsigned long scr_x; + unsigned long scr_y; + unsigned long dest_ajust_width; + unsigned long scr_ajust_width; + //unsigned long dest_area; + + __asm { + + cmp [ src ] , 0 + jz real_out + + + ; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi, [esi]GraphicViewPortClass.Height ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_blit + + test cl , 1000b + jz dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + + dest_left_ok: + test cl , 0010b + jz dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + + dest_bottom_ok: + test dl , 0100b + jz dest_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ] , eax + dest_right_ok: + test dl , 0001b + jz do_blit + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ y1_pixel ] , eax + + do_blit: + + cld + + mov eax , [esi]GraphicViewPortClass.XAdd + add eax , [esi]GraphicViewPortClass.Width + add eax , [esi]GraphicViewPortClass.Pitch + mov edi , [esi]GraphicViewPortClass.Offset + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle real_out + sub eax , [ x_pixel ] + jle real_out + + + ; ******************************************************************** + ; Forward bitblit only + + //IF TRANSP + // test [ trans ] , 1 + // jnz forward_Blit_trans + //ENDIF + + + ; the inner loop is so efficient that + ; the optimal consept no longer apply because + ; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl forward_loop_bytes + + forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_dword + jmp real_out //ret + + forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + // ret + + //IF TRANSP + // + // + //forward_Blit_trans: + // + // + // mov ecx , eax + // and ecx , 01fh + // lea ecx , [ ecx + ecx * 4 ] + // neg ecx + // shr eax , 5 + // lea ecx , [ transp_reference + ecx * 2 ] + // mov [ y1_pixel ] , ecx + // + // + //forward_loop_trans: + // mov ecx , eax + // jmp [ y1_pixel ] + //forward_trans_line: + // REPT 32 + // local transp_pixel + // mov bl , [ esi ] + // inc esi + // test bl , bl + // jz transp_pixel + // mov [ edi ] , bl + // transp_pixel: + // inc edi + // ENDM + // transp_reference: + // dec ecx + // jge forward_trans_line + // add esi , [ scr_ajust_width ] + // add edi , [ dest_ajust_width ] + // dec edx + // jnz forward_loop_trans + // ret + //ENDIF + + real_out: + //ret + } +} + + //ENDP Buffer_To_Page + //END + + + + + + + + + +/* + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set +*/ + +extern "C" int __cdecl Buffer_Get_Pixel(void * this_object, int x_pixel, int y_pixel) +{ + __asm { + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short exit_label ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae exit_label ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel + exit_label: + //ret + //ENDP Buffer_Get_Pixel + + } +} + + diff --git a/TIBERIANDAWN/WIN32LIB/EXTERNS.H b/TIBERIANDAWN/WIN32LIB/EXTERNS.H new file mode 100644 index 000000000..441409100 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/EXTERNS.H @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/TIBERIANDAWN/WIN32LIB/FACINGFF.ASM b/TIBERIANDAWN/WIN32LIB/FACINGFF.ASM new file mode 100644 index 000000000..a487324f1 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FACINGFF.ASM @@ -0,0 +1,162 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACINGFF.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing256 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +;IDEAL +;P386 +;MODEL USE32 FLAT + +GLOBAL C Desired_Facing256 :NEAR +; INCLUDE "wwlib.i" +INCLUDE "..\include\gbuffer.inc" + + CODESEG + +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ +; LONG cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) + PROC Desired_Facing256 C near + USES ebx, ecx, edx + + ARG srcx:DWORD + ARG srcy:DWORD + ARG dstx:DWORD + ARG dsty:DWORD + + xor ebx,ebx ; Facing number. + + ; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short ??xnotneg + neg ecx + mov ebx,11000000b ; Set bit 7 and 6 for leftward. +??xnotneg: + + ; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ??ynotneg + xor ebx,01000000b ; Complement bit 6 for downward. + neg eax +??ynotneg: + + ; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + ; Determine if the direction is closer to the Y axis and make sure that + ; CX holds the larger of the two deltas. This is in preparation for the + ; divide. + cmp eax,ecx + jb short ??gotaxis + xchg eax,ecx + xor edx,01000000b ; Closer to Y axis so make DX=64 for quad 0 and 2. +??gotaxis: + + ; If closer to the X axis then add 64 for quadrants 0 and 2. If + ; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + ; add value is in DX and save on stack. + push edx + + ; Make sure that the division won't overflow. Reduce precision until + ; the larger number is less than 256 if it appears that an overflow + ; will occur. If the high byte of the divisor is not zero, then this + ; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short ??nooverflow +??again: + test ecx,0FFFFFF00h + jz short ??nooverflow + shr ecx,1 + shr eax,1 + jmp short ??again +??nooverflow: + + ; Make sure that the division won't underflow (divide by zero). If + ; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short ??nounderflow + mov eax,0FFFFFFFFh + jmp short ??divcomplete + + ; Derive a pseudo angle number for the octant. The angle is based + ; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +??nounderflow: + xor edx,edx + shld edx,eax,8 ; shift high byte of eax into dl + shl eax,8 + div ecx +??divcomplete: + + ; Integrate the 5 most significant bits into the angle index. If DX + ; is not zero, then it is 64. This means that the dividend must be negated + ; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short ??noneg + dec edx + neg eax +??noneg: + add eax,edx + add eax,ebx + and eax,0FFH + ret + + ENDP Desired_Facing256 + + + + END \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/FACINGFF.h b/TIBERIANDAWN/WIN32LIB/FACINGFF.h new file mode 100644 index 000000000..9a14a6028 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FACINGFF.h @@ -0,0 +1,458 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACINGFF.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing256 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + +#ifndef FACINGFF_H +#define FACINGFF_H + +//IDEAL +//P386 +//MODEL USE32 FLAT + +//GLOBAL C Desired_Facing256 :NEAR +//; INCLUDE "wwlib.i" +//INCLUDE "..\include\gbuffer.inc" + +// CODESEG +/* +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ + +int __cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty); + + +#if (0) + PROC Desired_Facing256 C near + USES ebx, ecx, edx + + ARG srcx:DWORD + ARG srcy:DWORD + ARG dstx:DWORD + ARG dsty:DWORD + + xor ebx,ebx ; Facing number. + + ; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short ??xnotneg + neg ecx + mov ebx,11000000b ; Set bit 7 and 6 for leftward. +??xnotneg: + + ; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ??ynotneg + xor ebx,01000000b ; Complement bit 6 for downward. + neg eax +??ynotneg: + + ; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + ; Determine if the direction is closer to the Y axis and make sure that + ; CX holds the larger of the two deltas. This is in preparation for the + ; divide. + cmp eax,ecx + jb short ??gotaxis + xchg eax,ecx + xor edx,01000000b ; Closer to Y axis so make DX=64 for quad 0 and 2. +??gotaxis: + + ; If closer to the X axis then add 64 for quadrants 0 and 2. If + ; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + ; add value is in DX and save on stack. + push edx + + ; Make sure that the division won't overflow. Reduce precision until + ; the larger number is less than 256 if it appears that an overflow + ; will occur. If the high byte of the divisor is not zero, then this + ; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short ??nooverflow +??again: + test ecx,0FFFFFF00h + jz short ??nooverflow + shr ecx,1 + shr eax,1 + jmp short ??again +??nooverflow: + + ; Make sure that the division won't underflow (divide by zero). If + ; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short ??nounderflow + mov eax,0FFFFFFFFh + jmp short ??divcomplete + + ; Derive a pseudo angle number for the octant. The angle is based + ; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +??nounderflow: + xor edx,edx + shld edx,eax,8 ; shift high byte of eax into dl + shl eax,8 + div ecx +??divcomplete: + + ; Integrate the 5 most significant bits into the angle index. If DX + ; is not zero, then it is 64. This means that the dividend must be negated + ; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short ??noneg + dec edx + neg eax +??noneg: + add eax,edx + add eax,ebx + and eax,0FFH + ret + + ENDP Desired_Facing256 + + + + END +#endif + + + + + + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +//NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + +// CODESEG +*/ + +/* +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +*/ +int __cdecl Desired_Facing8(long x1, long y1, long x2, long y2); + +#if (0) + PROC Desired_Facing8 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 ; Close to major axis bit. + + xor eax,eax + mov al,[NewFacing8+ebx] + + ; Normalize to 0..FF range. + shl eax,5 + + ret + + ENDP Desired_Facing8 + + + END + +#endif + + + + +/* + ; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/FACINGFF.h#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2); + +#if (0) + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END +#endif + + + + +#endif FACINGFF_H diff --git a/TIBERIANDAWN/WIN32LIB/FASTFILE.H b/TIBERIANDAWN/WIN32LIB/FASTFILE.H new file mode 100644 index 000000000..2ba372b34 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FASTFILE.H @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: fastfile.h + * Content: Definitions for fastfile access. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + * + ***************************************************************************/ + +typedef LPVOID HFASTFILE; + +extern BOOL FastFileInit( LPSTR fname, int max_handles ); +extern void FastFileFini( void ); +extern HFASTFILE FastFileOpen( LPSTR name ); +extern BOOL FastFileClose( HFASTFILE pfe ); +extern BOOL FastFileRead( HFASTFILE pfh, LPVOID ptr, int size ); +extern BOOL FastFileSeek( HFASTFILE pfe, int off, int how ); +extern long FastFileTell( HFASTFILE pfe ); +extern LPVOID FastFileLock( HFASTFILE pfe, int off, int len ); +extern BOOL FastFileUnlock( HFASTFILE pfe, int off, int len ); diff --git a/TIBERIANDAWN/WIN32LIB/FILE.H b/TIBERIANDAWN/WIN32LIB/FILE.H new file mode 100644 index 000000000..e9009fd6f --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FILE.H @@ -0,0 +1,257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#ifndef READ +#define READ 1 // Read access. +#endif +#ifndef WRITE +#define WRITE 2 // Write access. +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Find_First(unsigned char *fname, unsigned int mode, struct find_t *ffblk); +extern int __cdecl Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/FILEPCX.H b/TIBERIANDAWN/WIN32LIB/FILEPCX.H new file mode 100644 index 000000000..0fa46958f --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FILEPCX.H @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/FILETEMP.H b/TIBERIANDAWN/WIN32LIB/FILETEMP.H new file mode 100644 index 000000000..47e92cbcf --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FILETEMP.H @@ -0,0 +1,58 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD __cdecl ( __cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID __cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H + diff --git a/TIBERIANDAWN/WIN32LIB/FONT.CPP b/TIBERIANDAWN/WIN32LIB/FONT.CPP new file mode 100644 index 000000000..8681a0ee0 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FONT.CPP @@ -0,0 +1,138 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + /*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : FONT.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : July 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Char_Pixel_Width -- Return pixel width of a character. * + * String_Pixel_Width -- Return pixel width of a string of characters. * + * Get_Next_Text_Print_XY -- Calculates X and Y given ret value from Text_P* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "font.h" +#include +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + * CHAR_PIXEL_WIDTH -- Return pixel width of a character. * + * * + * Retreives the pixel width of a character from the font width block. * + * * + * INPUT: Character. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/31/1992 DRD : Created. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +int __cdecl Char_Pixel_Width(char chr) +{ + int width; + + width = (unsigned char)*(FontWidthBlockPtr + (unsigned char)chr) + FontXSpacing; + + return(width); +} + + +/*************************************************************************** + * STRING_PIXEL_WIDTH -- Return pixel width of a string of characters. * + * * + * Calculates the pixel width of a string of characters. This uses * + * the font width block for the widths. * + * * + * INPUT: Pointer to string of characters. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/30/1992 DRD : Created. * + * 01/31/1992 DRD : Use Char_Pixel_Width. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +unsigned int __cdecl String_Pixel_Width(char const *string) +{ + WORD width; // Working accumulator of string width. + WORD largest = 0; // Largest recorded width of the string. + + if (!string) return(0); + + width = 0; + while (*string) { + if (*string == '\r') { + string++; + largest = MAX(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); // add each char's width + } + } + largest = MAX(largest, width); + return(largest); +} + + + +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * unsigned long offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& gp, unsigned long offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = gp.Get_Width() + gp.Get_XAdd(); + offset -= gp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} diff --git a/TIBERIANDAWN/WIN32LIB/FONT.H b/TIBERIANDAWN/WIN32LIB/FONT.H new file mode 100644 index 000000000..1160f5ff9 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FONT.H @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Set_Font(void const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +int __cdecl Char_Pixel_Width(char chr); +unsigned int __cdecl String_Pixel_Width(char const *string); +void __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, unsigned long offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Load_Font(char const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern char FontWidth ; +extern char FontHeight; +extern char *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/TIBERIANDAWN/WIN32LIB/FUNCTION.H b/TIBERIANDAWN/WIN32LIB/FUNCTION.H new file mode 100644 index 000000000..0f0eea9e1 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/FUNCTION.H @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/GBUFFER.CPP b/TIBERIANDAWN/WIN32LIB/GBUFFER.CPP new file mode 100644 index 000000000..3f1365a1b --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/GBUFFER.CPP @@ -0,0 +1,707 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * GBC::DD_Init -- Inits a direct draw surface for a GBC * + * GBC::Init -- Core function responsible for initing a GBC * + * GBC::Lock -- Locks a Direct Draw Surface * + * GBC::Unlock -- Unlocks a direct draw surface * + * GBC::GraphicBufferClass -- Default constructor (requires explicit init)* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif +//#pragma inline + +int TotalLocks; +BOOL AllowHardwareBlitFills = TRUE; + + +//int CacheAllowed; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) : + LockCount(0), + GraphicBuff(NULL) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ + Offset = 0; + Width = 0; // Record width of Buffer + Height = 0; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + Pitch = 0; // Record width of Buffer + IsDirectDraw = FALSE; + LockCount = 0; + GraphicBuff = NULL; +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not attach a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return; + } + + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + ((gbuffer->Get_Width()+gbuffer->Get_Pitch()) * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + Pitch = gbuffer->Get_Pitch(); + GraphicBuff = gbuffer; + IsDirectDraw= gbuffer->IsDirectDraw; +} + + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + + +/*************************************************************************** + * GBC::DD_INIT -- Inits a direct draw surface for a GBC * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::DD_Init(GBC_Enum flags) +{ + // + // Create the direct draw surface description + // + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.dwFlags = DDSD_CAPS; + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + + if (!(flags & GBC_VISIBLE)) { + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags |= DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = Height; + VideoSurfaceDescription.dwWidth = Width; + } + + // + // Need to set the DDSCAPS_MODEX flag if we want a 320 wide mode + // + if ( Width == 320 ) { + VideoSurfaceDescription.ddsCaps.dwCaps |= DDSCAPS_MODEX; + } + + // + // Call CreateSurface + // + DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &VideoSurfacePtr , NULL); + AllSurfaces.Add_DD_Surface (VideoSurfacePtr); + + if ( GBC_VISIBLE & flags ){ + PaletteSurface=VideoSurfacePtr; + } + + Allocated = FALSE; // even if system alloced, dont flag it cuz + // we dont want it freed. + IsDirectDraw = TRUE; // flag it as a video surface + Offset = NOT_LOCKED; // flag it as unavailable for reading or writing + LockCount = 0; // surface is not locked +} + + +void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer) +{ + VideoSurfacePtr->AddAttachedSurface (attach_buffer->Get_DD_Surface()); +} + + +/*************************************************************************** + * GBC::INIT -- Core function responsible for initing a GBC * + * * + * INPUT: int - the width in pixels of the GraphicBufferClass * + * int - the heigh in pixels of the GraphicBufferClass * + * void * - pointer to user supplied buffer (system will * + * allocate space if buffer is NULL) * + * long - size of the user provided buffer * + * GBC_Enum - flags if this is defined as a direct draw * + * surface * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::Init(int w, int h, void *buffer, long size, GBC_Enum flags) +{ + Size = size; // find size of physical buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + + // + // If the surface we are creating is a direct draw object then + // we need to do a direct draw init. Otherwise we will do + // a normal alloc. + // + if (flags & (GBC_VIDEOMEM | GBC_VISIBLE)) { + DD_Init(flags); + } else { + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + if (!Size) Size = w*h; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + Offset = (long)Buffer; // Get offset to the buffer + IsDirectDraw = FALSE; + } + + Pitch = 0; // Record width of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} + + +/*********************************************************************************************** + * GBC::Un_Init -- releases the video surface belonging to this gbuffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:44PM ST : Created * + *=============================================================================================*/ + +void GraphicBufferClass::Un_Init (void) +{ + if ( IsDirectDraw ){ + + if ( VideoSurfacePtr ){ + + while ( LockCount ){ + + if (VideoSurfacePtr->Unlock ( NULL ) == DDERR_SURFACELOST){ + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + } + } + + AllSurfaces.Remove_DD_Surface (VideoSurfacePtr); + VideoSurfacePtr->Release(); + VideoSurfacePtr = NULL; + } + } +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Default constructor (requires explicit init) * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(void) +{ + GraphicBuff = this; // Get a pointer to our self + VideoSurfacePtr = NULL; + memset(&VideoSurfaceDescription, 0, sizeof(DDSURFACEDESC)); +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer, long size) +{ + Init(w, h, buffer, size, GBC_NONE); +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Init(w, h, buffer, w * h, GBC_NONE); +} + +/*====================================================================================* + * GBC::GRAPHICBUFFERCLASS -- contructor for GraphicsBufferClass with special flags * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - unused * + * unsigned flags - flags for creation of special buffer types * + * GBC_VISIBLE - buffer is a visible screen surface * + * GBC_VIDEOMEM - buffer resides in video memory * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09-21-95 04:19pm ST : Created * + *====================================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, GBC_Enum flags) +{ + Init(w, h, NULL, w * h, flags); +} + +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ + +// +// Release the direct draw surface if it exists +// + Un_Init(); +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} + + +/*************************************************************************** + * GBC::LOCK -- Locks a Direct Draw Surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ +extern void Colour_Debug (int call_number); +extern bool GameInFocus; + +extern void Block_Mouse(GraphicBufferClass *buffer); +extern void Unblock_Mouse(GraphicBufferClass *buffer); + +BOOL GraphicBufferClass::Lock(void) +{ + HRESULT result; + int restore_attempts=0; + + // + // If its not a direct draw surface then the lock is always sucessful. + // + if (!IsDirectDraw) return(TRUE); + + /* + ** If the video surface pointer is null then return + */ + if (!VideoSurfacePtr) return (FALSE); + + /* + ** If we dont have focus then return failure + */ + if (!GameInFocus) return (FALSE); + + + Block_Mouse(this); + + + // + // If surface is already locked then inc the lock count and return true + // + if (LockCount){ + LockCount++; + Unblock_Mouse(this); + return(TRUE); + } + + // + // If it isn't locked at all then we will have to request that Direct + // Draw actually lock the surface. + // + + if (VideoSurfacePtr){ + while (!LockCount && restore_attempts<2) { + result = VideoSurfacePtr->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL); + + switch (result){ + case DD_OK : + Offset = (unsigned long)VideoSurfaceDescription.lpSurface; + Pitch = VideoSurfaceDescription.lPitch; + Pitch -= Width; + LockCount++; // increment count so we can track if + TotalLocks++; // Total number of times we have locked (for debugging) + //Colour_Debug (1); + Unblock_Mouse(this); + return (TRUE); // we locked it multiple times. + + case DDERR_SURFACELOST : + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + restore_attempts++; + break; + + default : + Unblock_Mouse(this); + return (FALSE); + } + } + } + //Colour_Debug(1); + Unblock_Mouse(this); + return (FALSE); //Return false because we couldnt lock or restore the surface +} + +/*************************************************************************** + * GBC::UNLOCK -- Unlocks a direct draw surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ + + +BOOL GraphicBufferClass::Unlock(void) +{ + // + // If there is no lock count or this is not a direct draw surface + // then just return true as there is no harm done. + // + if (!(LockCount && IsDirectDraw)) { + return(TRUE); + } + + // + // If lock count is directly equal to one then we actually need to + // unlock so just give it a shot. + // + if (LockCount == 1 && VideoSurfacePtr) { + Block_Mouse(this); + if ( VideoSurfacePtr->Unlock ( NULL ) != DD_OK ){ + Unblock_Mouse(this); + return(FALSE); + } else { + Offset=NOT_LOCKED; + LockCount--; + Unblock_Mouse(this); + return(TRUE); + } + } + //Colour_Debug (0); + LockCount--; + return(TRUE); +} + + +/*********************************************************************************************** + * GVPC::DD_Linear_Blit_To_Linear -- blit using the hardware blitter * + * * + * * + * * + * INPUT: destination vvpc * + * x coord to blit from * + * y coord to blit from * + * x coord to blit to * + * y coord to blit to * + * width to blit * + * height to blit * + * * + * OUTPUT: DD_OK if successful * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-22-95 11:05am ST : Created * + *=============================================================================================*/ + +HRESULT GraphicViewPortClass::DD_Linear_Blit_To_Linear ( + GraphicViewPortClass &dest + , int source_x + , int source_y + , int dest_x + , int dest_y + , int width + , int height + , BOOL mask ) + +{ + RECT source_rectangle; + RECT dest_rectangle; + int key_source=0; + + if ( mask ){ + key_source=DDBLT_KEYSRC; + } + + + source_rectangle.left = source_x; + source_rectangle.top = source_y; + source_rectangle.right = source_x+width; + source_rectangle.bottom = source_y+height; + + dest_rectangle.left = dest_x; + dest_rectangle.top = dest_y; + dest_rectangle.right = dest_x+width; + dest_rectangle.bottom = dest_y+height; + + return ( dest.GraphicBuff->Get_DD_Surface()->Blt ( &dest_rectangle, + GraphicBuff->Get_DD_Surface(), + &source_rectangle, + key_source | DDBLT_WAIT | DDBLT_ASYNC, + NULL ) ); +} + + + diff --git a/TIBERIANDAWN/WIN32LIB/GBUFFER.H b/TIBERIANDAWN/WIN32LIB/GBUFFER.H new file mode 100644 index 000000000..7f2d51b74 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/GBUFFER.H @@ -0,0 +1,1372 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef DRAWBUFF_H +#include "drawbuff.h" +#endif + +//#ifndef BUFFER_H +#include "buffer.h" +//#endif + +#ifndef WINDOWS_H +#include "ww_win.h" +#endif +#include + +#include "iconcach.h" + + + +//#ifndef FUNCTION_H + +//#pragma off (unreferenced) + +//#ifndef BITMAPCLASS +//#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +//#endif + +//#pragma on (unreferenced) +//#endif + + +////////////////////////////////////////////////////////////////////////// +// +// Defines for direct draw +// +// +extern LPDIRECTDRAW DirectDrawObject; //pointer to direct draw object +extern HWND MainWindow; //handle to programs main window + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Gbuffer_Focus_Loss_Function)(void); + +enum GBC_Enum { + GBC_NONE = 0, + GBC_VIDEOMEM = 1, + GBC_VISIBLE = 2, +}; + +#define NOT_LOCKED NULL + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 768 +#define DEFAULT_SCREEN_HEIGHT 768 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr); +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + int Get_Pitch(void); + inline BOOL Get_IsDirectDraw(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + HRESULT Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + +// PG_TO_FIX +// This seems to be missing. Might not be needed anyway since it looks like it's only used for UI drawing. ST - 12/17/2018 6:11PM + void Texture_Fill_Rect (int xpos, int ypos, int width, int height, void const *shape_pointer, int source_width, int source_height) { + return; + } + +// This doesnt seem to exist anywhere?? - Steve T 9/26/95 6:05PM +// VOID Grey_Out_Region(int x, int y, int width, int height, int color); + + // + // New members to lock and unlock the direct draw video memory + // + inline BOOL Lock (); + inline BOOL Unlock(); + inline int Get_LockCount(); + + // Member to blit using direct draw access to hardware blitter + HRESULT DD_Linear_Blit_To_Linear ( GraphicViewPortClass &dest, int source_x, int source_y, int dest_x, int dest_y, int width , int height, BOOL mask ); + + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + protected: + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + long Pitch; //Distance from one line to the next + GraphicBufferClass *GraphicBuff; // related graphic buff + BOOL IsDirectDraw; //Flag to let us know if it is a direct draw surface + int LockCount; // Count for stacking locks if non-zero the buffer +}; // is a locked DD surface + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/* long Pitch - modulo of buffer for reading and writing */ +/* BOOL IsDirectDraw - flag if its a direct draw surface */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + + public: + GraphicBufferClass(int w, int h, GBC_Enum flags); + GraphicBufferClass(int w, int h, void *buffer, long size); + GraphicBufferClass(int w, int h, void *buffer = 0); + GraphicBufferClass(void); + ~GraphicBufferClass(); + + void DD_Init(GBC_Enum flags); + void Init(int w, int h, void *buffer, long size, GBC_Enum flags); + void Un_Init(void); + void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer); + BOOL Lock(void); + BOOL Unlock(void); + + void Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle); + + // Member to get a pointer to a direct draw surface + LPDIRECTDRAWSURFACE Get_DD_Surface ( void ); + + protected: + LPDIRECTDRAWSURFACE VideoSurfacePtr; //Pointer to the related direct draw surface + DDSURFACEDESC VideoSurfaceDescription;//Description of the said surface + +}; + + + +inline int GraphicViewPortClass::Get_LockCount(void) +{ + return (LockCount); +} + + + +/*********************************************************************************************** + * GVPC::Get_IsDirectDraw -- provide read access to the IsDirectDraw flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsDirectDraw * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 1:02PM ST : Created * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Get_IsDirectDraw(void) +{ + return (IsDirectDraw); +} + + + +/*********************************************************************************************** + * GBC::Get_DD_Surface -- returns a pointer to the buffer direct draw surface * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to direct draw surface * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/29/95 9:43AM ST : Created * + *=============================================================================================*/ +inline LPDIRECTDRAWSURFACE GraphicBufferClass::Get_DD_Surface ( void ) +{ + return ( VideoSurfacePtr ); + +} + + + +/*********************************************************************************************** + * GVPC::Lock -- lock the graphics buffer for reading or writing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully locked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 12:33pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Lock(void) +{ + BOOL lock = GraphicBuff->Lock(); + if ( !lock ) return(FALSE); + + if (this != GraphicBuff) { + Attach(GraphicBuff, XPos, YPos, Width, Height); + } + return(TRUE); +} + +/*********************************************************************************************** + * GVPC::Unlock -- unlock the video buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully unlocked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 02:20pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Unlock(void) +{ + BOOL unlock = GraphicBuff->Unlock(); + if (!unlock) return(FALSE); + if (this != GraphicBuff && IsDirectDraw && !GraphicBuff->LockCount) { + Offset = 0; + } + return(TRUE); +} + + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return Buffer_Size_Of_Region(this, w, h); +} + + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + + if (Lock()){ + Buffer_Put_Pixel(this, x, y, color); + } + Unlock(); + + +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + int return_code=0; + + if (Lock()){ + return_code=(Buffer_Get_Pixel(this, x, y)); + } + Unlock(); + return(return_code); + +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + if (Lock()){ + Buffer_Clear(this, color); + } + Unlock(); + +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff, size)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + HRESULT return_code=0; + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos+x_pixel, YPos+y_pixel + , dest.Get_XPos()+dx_pixel, dest.Get_YPos()+dy_pixel + , pixel_width, pixel_height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel + , dx_pixel, dy_pixel + , pixel_width, pixel_height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos()+dx, dest.Get_YPos()+dy + , Width, Height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , dx, dy + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos(), dest.Get_YPos() + , MAX( Width, dest.Get_Width()) + , MAX( Height, dest.Get_Height()) + , trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, str, x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +#pragma warning (disable:4996) + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + if (Lock()){ + Buffer_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); + } + Unlock(); +} + + + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +extern BOOL IconCacheAllowed; +inline void GraphicViewPortClass::Draw_Stamp(void const * icondata, int icon, int x_pixel, int y_pixel, void const * remap, int clip_window) +{ + int cache_index=-1; + + int drewit = 0; + if (IconCacheAllowed){ + if (IsDirectDraw){ + if (!remap){ + cache_index = Is_Icon_Cached(icondata,icon); + } + + if (cache_index != -1){ + if (CachedIcons[cache_index].Get_Is_Cached() ){ + CachedIcons[cache_index].Draw_It (GraphicBuff->Get_DD_Surface() , x_pixel, y_pixel, + WindowList[clip_window][WINDOWX] + XPos, + WindowList[clip_window][WINDOWY] +YPos, + WindowList[clip_window][WINDOWWIDTH], + WindowList[clip_window][WINDOWHEIGHT]); + CachedIconsDrawn++; + drewit = 1; + } + } + } + } + + + if (drewit == 0) { + if (Lock()){ + UnCachedIconsDrawn++; + Buffer_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); + } + } + Unlock(); +} + + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + if (Lock()){ + Buffer_Draw_Line(this, sx, sy, dx, dy, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + if ( AllowHardwareBlitFills + && IsDirectDraw + && ( (dx-sx) * (dy-sy) >= (32*32) ) + && GraphicBuff->Get_DD_Surface()->GetBltStatus(DDGBS_CANBLT) == DD_OK){ + DDBLTFX blit_effects; + RECT dest_rectangle; + + dest_rectangle.left =sx+XPos; + dest_rectangle.top =sy+YPos; + dest_rectangle.right =dx+XPos; + dest_rectangle.bottom=dy+YPos; + + if (dest_rectangle.left= Width + XPos){ + dest_rectangle.right = Width +XPos -1; + } + + if (dest_rectangle.top= Height + YPos){ + dest_rectangle.bottom = Height + YPos -1; + } + + if (dest_rectangle.left >= dest_rectangle.right) return; + if (dest_rectangle.top >= dest_rectangle.bottom) return; + + dest_rectangle.right++; + dest_rectangle.bottom++; + + blit_effects.dwSize=sizeof(blit_effects); + blit_effects.dwFillColor = color; + GraphicBuff->Get_DD_Surface()->Blt(&dest_rectangle, + NULL, + NULL, + DDBLT_WAIT | DDBLT_ASYNC | DDBLT_COLORFILL, + &blit_effects); + } else { + if (Lock()){ + Buffer_Fill_Rect(this, sx, sy, dx, dy, color); + Unlock(); + } + } +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, sx, sy, width, height, remap); + } + Unlock(); +} + + +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + if (Lock()){ + Buffer_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, 0, 0, Width, Height, remap); + } + Unlock(); +} + +inline int GraphicViewPortClass::Get_Pitch(void) +{ + return(Pitch); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + + +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/GBUFFER.INC b/TIBERIANDAWN/WIN32LIB/GBUFFER.INC new file mode 100644 index 000000000..09f2e45b2 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/GBUFFER.INC @@ -0,0 +1,48 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +GraphicViewPort struct +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ends \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/GETSHAPE.CPP b/TIBERIANDAWN/WIN32LIB/GETSHAPE.CPP new file mode 100644 index 000000000..fd0a0ce13 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/GETSHAPE.CPP @@ -0,0 +1,358 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + //int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ diff --git a/TIBERIANDAWN/WIN32LIB/ICONCACH.H b/TIBERIANDAWN/WIN32LIB/ICONCACH.H new file mode 100644 index 000000000..f2e320584 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/ICONCACH.H @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 16th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains definition of the IconCacheClass and associated non member * + * function prototypes. * + * * + * Functions: * + * IconCacheClass::Get_Is_Cached -- member to allow access to private IsCached flag * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#ifndef ICONCACH_H +#define ICONCACH_H + +#include + +#define ICON_WIDTH 24 // Icons must be this width to be cached +#define ICON_HEIGHT 24 // Icons must be this height to be cached +#define MAX_CACHED_ICONS 500 // Maximum number of icons that can be cached +#define MAX_ICON_SETS 100 // Maximum number of icon sets that can be registered +#define MAX_LOOKUP_ENTRIES 3000 // Size of icon index table + + +/* +** IconCacheClass for tracking individual icons cached into video memory +** +** Use Register_Icon_Set to identify a set of icons as cachable. Once registered, the icons +** will be cached automatically when drawn. +** Use Invalidate_Cached_Icons at the end of icon drawing to release the video memory used by the +** caching system. +** Restore_Cached_Icons may be used to reload the icons into video memory after a focus loss. +** +*/ + +class IconCacheClass { + + public: + + IconCacheClass (void); // class constructor + ~IconCacheClass (void); // class destructor + + void Restore(void); // restore the surface + BOOL Cache_It (void * icon_ptr); // Cache the icon to video memory + void Uncache_It (void); // Restore the video memory and flag the icon as uncached + void Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height); + inline BOOL Get_Is_Cached(void); // Return the IsCached member + + int TimesDrawn; // counter of times cached icon has been drawn + int TimesFailed; // counter of times cached icon has failed to draw + + + private: + + LPDIRECTDRAWSURFACE CacheSurface; // Ptr to direct draw surface where icon resides + BOOL IsCached; // Flag to say whether an icon is cached + BOOL SurfaceLost; // Flag to indicate that our icons surface has been lost + int DrawFrequency; // Number of times icon has been drawn + void *IconSource; // Ptr to original icon data in system memory + +}; + + + +/* +** Structure to keep track of registered icon sets +** +*/ + +typedef struct tIconSetType{ + IControl_Type *IconSetPtr; // Ptr to icon set data + int IconListOffset; // Offset into icon index table for this icon set +}IconSetType; + + +extern IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern void Invalidate_Cached_Icons (void); +extern void Restore_Cached_Icons (void); +extern void Register_Icon_Set (void *icon_data , BOOL pre_cache); + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void Clear_Icon_Pointers (void); +extern "C" void Cache_Copy_Icon (void const *icon_ptr ,void * , int); +extern "C" int Is_Icon_Cached (void const *icon_data , int icon); +extern "C" int Get_Icon_Index (void *icon_ptr); +extern "C" int Get_Free_Index (void); +extern "C" BOOL Cache_New_Icon (int icon_index, void *icon_ptr); +extern "C" int Get_Free_Cache_Slot(void); + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/*********************************************************************************************** + * ICC::Get_Is_Cached -- member to allow access to the private IsCached flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsCached * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:42AM ST : Created * + *=============================================================================================*/ +inline BOOL IconCacheClass::Get_Is_Cached (void) +{ + return (IsCached); +} + + + + + +#endif //ICONCACH_H + diff --git a/TIBERIANDAWN/WIN32LIB/ICONSET.CPP b/TIBERIANDAWN/WIN32LIB/ICONSET.CPP new file mode 100644 index 000000000..ad399b758 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/ICONSET.CPP @@ -0,0 +1,340 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" + +//#define _WIN32 +//#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +//#include +#include +#include +#include "tile.h" +#include + + +// Misc? ST - 1/3/2019 10:40AM +//extern int Misc; +int Misc; + +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != WW_ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= (unsigned long)buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = (short)(((short)sinf.Width)<<3); + idata->Height = (short)(((short)sinf.Height)<<3); + idata->Allocated = (short)allocated; + idata->Icons = (unsigned char *)iconsetptr + sizeof(IControl_Type); + idata->Map = (unsigned char *) (idata->Icons + size); + idata->TransFlag = sizeof(IControl_Type) + size + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if ((unsigned long)buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = (void*)idata->Map; + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Icons)); + return(NULL); +} + +void * Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Map)); + return(NULL); +} \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/IFF.CPP b/TIBERIANDAWN/WIN32LIB/IFF.CPP new file mode 100644 index 000000000..b0a47a162 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/IFF.CPP @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +#ifdef MIN +#undef MIN +#endif + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +int __cdecl Open_Iff_File(char const *filename) +{ + int fh; // File handle. + long type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = WW_ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +void __cdecl Close_Iff_File(int fh) +{ + if (fh != WW_ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: int file handle to open IFF file, long id to get size of * + * * + * OUTPUT: long size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id) +{ + long form; // Chunk iff form name. + long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize) +{ + long form; // Chunk iff form name. + unsigned long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length) +{ + long pos; // Current position in the IFF file. + long oldpos; // Record of start of chunk offset. + long endpos; // end of file offset before we write our data + long value; + BOOL odd; // Is length odd? + char pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (short)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size long. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/TIBERIANDAWN/WIN32LIB/IFF.H b/TIBERIANDAWN/WIN32LIB/IFF.H new file mode 100644 index 000000000..51f8c5768 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/IFF.H @@ -0,0 +1,164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((long) ((long) d << 24) | ((long) c << 16) | ((long) b << 8) | (long)(a)) +#define IFFize_WORD(a) Reverse_Word(a) +#define IFFize_LONG(a) Reverse_Long(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + char Method; // Compression method (CompressionType). + char pad; // Reserved pad byte (always 0). + long Size; // Size of the uncompressed data. + short Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +int __cdecl Open_Iff_File(char const *filename); +void __cdecl Close_Iff_File(int fh); +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id); +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize); +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size); +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size); +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags); +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data=NULL); +unsigned long __cdecl Uncompress_Data(void const *src, void *dst); +void __cdecl Set_Uncomp_Buffer(int buffer_segment, int size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern void __cdecl Pack_2_Plane(void *buffer, void * pageptr, int planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Compress(void *source, void *dest, unsigned long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H diff --git a/TIBERIANDAWN/WIN32LIB/INDEXTB.INC b/TIBERIANDAWN/WIN32LIB/INDEXTB.INC new file mode 100644 index 000000000..36d8e03d6 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/INDEXTB.INC @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/TIBERIANDAWN/WIN32LIB/IRANDOM.CPP b/TIBERIANDAWN/WIN32LIB/IRANDOM.CPP new file mode 100644 index 000000000..8802b28b2 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/IRANDOM.CPP @@ -0,0 +1,176 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "misc.h" + + + +extern "C" unsigned long RandNumb = 0x12349876; + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + + + +unsigned char Random(void) +{ + __asm { + lea esi, [RandNumb] //; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 //; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 //; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 //; rcl byte 2 of RandNumb + cmc //; complement carry + sbb al,[esi] //; sbb byte 1 of RandNumb + shr al,1 //; sets carry + rcr [BYTE PTR esi],1 //; rcr byte 1 of RandNumb + mov al,[esi] //; reload byte 1 of RandNumb + xor al,[esi+1] //; xor with byte 2 of RandNumb + } +} + + + +int Get_Random_Mask(int maxval) +{ + __asm { + + bsr ecx,[maxval] // ; put bit position of highest bit in ecx + mov eax, 1 // ; set one bit on in eax + jz invalid // ; if BSR shows maxval==0, return eax=1 + inc ecx // ; get one bit higher than count showed + shl eax,cl // ; move our EAX bit into position + dec eax // ; dec it to create the mask. +invalid: + } +} + + + + +#if (0) + +RandNumb DD 12349876H + +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END +#endif \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/KEYBOARD.CPP b/TIBERIANDAWN/WIN32LIB/KEYBOARD.CPP new file mode 100644 index 000000000..cec88ccfd --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/KEYBOARD.CPP @@ -0,0 +1,533 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 26, 1995 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" +#include "timer.h" +#include "mono.h" + +void Message_Loop(void); + +WWKeyboardClass *_Kbd; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + _Kbd = this; + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 256); + memset(AsciiRemap, 0, 2048); + short lp; + for (lp = 31; lp < 255; lp ++) { + if (isprint(lp)) { + int vk_key = VkKeyScan((unsigned char)lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = (unsigned char)lp; + VKRemap[lp] = (unsigned char)(vk_key & 0xFF); + } + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; + MState = 0; + Conditional = 0; + CurrentCursor = NULL; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[(Head + 1) & 255]; // get the x and y pos + MouseQY = Buffer[(Head + 2) & 255]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + Message_Loop(); + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = (short)key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. Note + // that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + // would be incompatible with the dos version. + // + if (vk_key != VK_LBUTTON && vk_key != VK_MBUTTON && vk_key != VK_RBUTTON) { + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + +int WWKeyboardClass::To_ASCII(int key) +{ + if ( key && WWKEY_RLS_BIT) + return(KN_NONE); + return(key); +} + +int WWKeyboardClass::Down(int key) +{ + return(GetAsyncKeyState(key&0xFF)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + +//#pragma off(unreferenced) +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + return; +#if (0) // ST - 12/20/2018 11:27AM + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message(wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + case WM_MOUSEMOVE: + if (CurrentCursor) + SetCursor(CurrentCursor); + break; + } +#endif +} +//#pragma on(unreferenced) + + + + +void Message_Loop(void) +{ + + MSG msg; + + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + + + +} + + + +/*************************************************************************** + * CHECK_KEY -- compatability routine for old 32 bit library * + * * + * This routine checks to see if there is a key in the keyboard buffer * + * and returns it to the sender if there is. It does not remove the key * + * from the buffer. * + * * + * INPUT: none * + * * + * OUTPUT: The key that was pressed. * + * * + * WARNINGS: You must declare a WWKeyboardClass object before calling * + * this routine. * + * * + * HISTORY: * + * 10/26/1995 : Created. * + *=========================================================================*/ +int Check_Key(void) +{ + if (!_Kbd) return(KA_NONE); + return(_Kbd->Check() & ~WWKEY_SHIFT_BIT); +} + +void Clear_KeyBuffer(void) +{ + if (!_Kbd) return; + _Kbd->Clear(); +} + +int Check_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Check(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !(flags & WWKEY_VK_BIT) ) { + flags |= WWKEY_SHIFT_BIT; + } + } + + return(key | flags); +} + +int Get_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Get(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !(flags & WWKEY_VK_BIT) ) { + flags |= WWKEY_SHIFT_BIT; + } + } + return(key | flags); +} + +int KN_To_KA(int key) +{ + if ( key & WWKEY_RLS_BIT) { + return(KA_NONE); + } + if (!(key & WWKEY_VK_BIT)) { + int flags = key & 0xFF00; + key = key & 0x00FF; + if (flags & WWKEY_SHIFT_BIT) { + key = toupper(key); + flags &= ~WWKEY_SHIFT_BIT; + } + }else{ + /* + ** If its a numeric keypad key then fix it up + */ + if ((key & 0xff) >=VK_NUMPAD0 && (key & 0xff) <=VK_NUMPAD9){ + key = (key & 0xff) - VK_NUMPAD0 + KA_0; + } + } + return(key); +} + +int KN_To_VK(int key) +{ + if (!_Kbd) return(KN_NONE); + if ( key & WWKEY_RLS_BIT) { + return(VK_NONE); + } + + int flags = key & 0xFF00; + if (!(flags & WWKEY_VK_BIT)) { + key = _Kbd->VKRemap[key & 0x00FF] | flags; + } + key &= ~WWKEY_VK_BIT; + return(key); +} + +int Key_Down(int key) +{ + if (!_Kbd) return(FALSE); + return(_Kbd->Down(key)); +} + +int Get_Key(void) +{ + int retval; + + if (!_Kbd) return(KN_NONE); + retval = _Kbd->Get() & ~WWKEY_SHIFT_BIT; + if (retval & WWKEY_RLS_BIT) { + retval = KN_NONE; + } + return(retval); +} diff --git a/TIBERIANDAWN/WIN32LIB/KEYBOARD.H b/TIBERIANDAWN/WIN32LIB/KEYBOARD.H new file mode 100644 index 000000000..8233bc49b --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/KEYBOARD.H @@ -0,0 +1,675 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + unsigned char VKRemap[256]; // gives vk for any ascii char + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT | WWKEY_VK_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT | WWKEY_VK_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); +int KN_To_VK(int key); + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/KEYBOARD.INC b/TIBERIANDAWN/WIN32LIB/KEYBOARD.INC new file mode 100644 index 000000000..58e521f11 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/KEYBOARD.INC @@ -0,0 +1,126 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/TIBERIANDAWN/WIN32LIB/KEYSTRUC.INC b/TIBERIANDAWN/WIN32LIB/KEYSTRUC.INC new file mode 100644 index 000000000..f230404fa --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/KEYSTRUC.INC @@ -0,0 +1,159 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/TIBERIANDAWN/WIN32LIB/LOAD.CPP b/TIBERIANDAWN/WIN32LIB/LOAD.CPP new file mode 100644 index 000000000..1a1055876 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/LOAD.CPP @@ -0,0 +1,375 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include +#include +#include +#include + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + void *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * void * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize=0; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((short *)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * void *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_Word(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((short*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_Word(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Uncompress_Data(void const *src, void *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_Long(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_Word(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((void *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((void *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((void *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((void *) src, (void *) dst, (unsigned long) uncomp_size); + break; + + } + + return(uncomp_size); +} + + diff --git a/TIBERIANDAWN/WIN32LIB/LOADFONT.CPP b/TIBERIANDAWN/WIN32LIB/LOADFONT.CPP new file mode 100644 index 000000000..87cd7f676 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/LOADFONT.CPP @@ -0,0 +1,137 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include +#include +#include + +#if(IBM) +#include +#include + +#include + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +char FontWidth = 8; +char FontHeight = 8; + +// only font.c and set_font.c use the following +char *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Load_Font(char const *name) +{ + char valid; + int fh; // DOS file handle for font file. + unsigned short size; // Size of the data in the file (-2); + char *ptr = NULL; // Pointer to newly loaded font. + + + + fh=Open_File(name,READ); + if ( fh>=0 ){ + if ( Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size , MEM_NORMAL ); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return ((void*)errno); + } + + + +#ifdef cuts + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size, MEM_NORMAL); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } +#endif + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/LOADPAL.CPP b/TIBERIANDAWN/WIN32LIB/LOADPAL.CPP new file mode 100644 index 000000000..e6d7487b3 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/LOADPAL.CPP @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Load_Palette * + * * + * File Name : LOADPAL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 27, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the file I/O system, * + * specifically Load_Data(). * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Palette -- Loads a palette file into the given palette buffer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +//#include +#include "wwstd.h" +#include "iff.h" +#include "palette.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +/*************************************************************************** + * Load_Palette -- Loads a palette file into the given palette buffer. * + * * + * INPUT: * + * BYTE * file_name - name of the file to load. * + * BYTE * palette_pointer - pointer to palette buffer. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer) +{ + #if(IBM) + Load_Data(palette_file_name, palette_pointer, 768); + #else + Load_Data(palette_file_name, palette_pointer, (ULONG)(2<. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/MEM.CPP b/TIBERIANDAWN/WIN32LIB/MEM.CPP new file mode 100644 index 000000000..ca0486429 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MEM.CPP @@ -0,0 +1,1086 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +//#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned long Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned long FreeMem; // Current amount of free ram (in paragraphs). + unsigned long TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + unsigned int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = lsize >> 4; + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/TIBERIANDAWN/WIN32LIB/MEMFLAG.H b/TIBERIANDAWN/WIN32LIB/MEMFLAG.H new file mode 100644 index 000000000..ce47d10c3 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MEMFLAG.H @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/* +** Prototypes for VMPAGEIN.ASM +*/ +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + +//PG_TO_FIX +//#pragma option -Jgd + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + +//#pragma option -Jgd + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +extern "C" { + void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); +} + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/MISC.H b/TIBERIANDAWN/WIN32LIB/MISC.H new file mode 100644 index 000000000..9c3a0a434 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MISC.H @@ -0,0 +1,268 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +#define WIN32_LEAN_AND_MEAN // eliminates unecessary definitions in windows.h +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include + +extern LPDIRECTDRAWSURFACE PaletteSurface; + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: DDRAW.CPP */ +/*=========================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg); +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +unsigned Get_Free_Video_Memory(void); +void Wait_Blit(void); +unsigned Get_Video_Hardware_Capabilities(void); + +extern "C" void Wait_Vert_Blank(void); +extern "C" void Set_DD_Palette (void *palette); + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void); +/* +** Pointer to function to call if we detect a surface restore +*/ +extern void (*Misc_Focus_Restore_Function)(void); + + +/* + * Flags returned by Get_Video_Hardware_Capabilities + */ +/* Hardware blits supported? */ +#define VIDEO_BLITTER 1 + +/* Hardware blits asyncronous? */ +#define VIDEO_BLITTER_ASYNC 2 + +/* Can palette changes be synced to vertical refresh? */ +#define VIDEO_SYNC_PALETTE 4 + +/* Is the video cards memory bank switched? */ +#define VIDEO_BANK_SWITCHED 8 + +/* Can the blitter do filled rectangles? */ +#define VIDEO_COLOR_FILL 16 + +/* Is there no hardware assistance avaailable at all? */ +#define VIDEO_NO_HARDWARE_ASSIST 32 + + + +/* + * Definition of surface monitor class + * + * This class keeps track of all the graphic buffers we generate in video memory so they + * can be restored after a focus switch. +*/ + +#define MAX_SURFACES 20 + +class SurfaceMonitorClass { + + public: + + SurfaceMonitorClass(); + + void Add_DD_Surface (LPDIRECTDRAWSURFACE); + void Remove_DD_Surface (LPDIRECTDRAWSURFACE); + BOOL Got_Surface_Already (LPDIRECTDRAWSURFACE); + void Restore_Surfaces (void); + void Set_Surface_Focus ( BOOL in_focus ); + void Release(void); + + BOOL SurfacesRestored; + + private: + + LPDIRECTDRAWSURFACE Surface[MAX_SURFACES]; + BOOL InFocus; + +}; + +extern SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces + + +/*=========================================================================*/ +/* The following variables are declared in: DDRAW.CPP */ +/*=========================================================================*/ +extern LPDIRECTDRAW DirectDrawObject; +extern LPDIRECTDRAW2 DirectDraw2Interface; +extern HWND MainWindow; +extern BOOL SystemToVideoBlits; +extern BOOL VideoToSystemBlits; +extern BOOL SystemToSystemBlits; +extern BOOL OverlappedVideoBlits; // Can video driver blit overlapped regions? + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID __cdecl Prog_End(const char *why = NULL, bool fatal = false); // Added why and fatal parameters. ST - 6/27/2019 10:10PM +VOID __cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +unsigned char __cdecl Random(void); +int __cdecl Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void __cdecl Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +long __cdecl Reverse_Long(long number); +short __cdecl Reverse_Short(short number); +long __cdecl Swap_Long(long number); +#if (0) +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ +#endif + +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long __cdecl Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD __cdecl Processor(void); +extern WORD __cdecl Operating_System(void); +extern unsigned long random ( unsigned long mod ) ; +//extern void randomize ( void ) ; + +extern int __cdecl Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int __cdecl Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H diff --git a/TIBERIANDAWN/WIN32LIB/MODEMREG.H b/TIBERIANDAWN/WIN32LIB/MODEMREG.H new file mode 100644 index 000000000..4e3b81a61 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MODEMREG.H @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef WIN32 +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif //WIN32 +#include + + + +class ModemRegistryEntryClass { + + public: + + ModemRegistryEntryClass (int modem_number); + ~ModemRegistryEntryClass (void); + + + char *Get_Modem_Name (void) { return (ModemName); } + + char *Get_Modem_Device_Name (void) { return (ModemDeviceName); } + + char *Get_Modem_Error_Correction_Enable (void) { return (ErrorCorrectionEnable); } + + char *Get_Modem_Error_Correction_Disable (void) { return (ErrorCorrectionDisable); } + + char *Get_Modem_Compression_Enable (void) { return (CompressionEnable); } + + char *Get_Modem_Compression_Disable (void) { return (CompressionDisable); } + + char *Get_Modem_Hardware_Flow_Control (void) { return (HardwareFlowControl); } + + char *Get_Modem_No_Flow_Control (void) { return (HardwareFlowControl); } + + private: + + char *ModemName; + char *ModemDeviceName; + char *ErrorCorrectionEnable; + char *ErrorCorrectionDisable; + char *CompressionEnable; + char *CompressionDisable; + char *HardwareFlowControl; + char *NoFlowControl; + +}; + + + + + + + diff --git a/TIBERIANDAWN/WIN32LIB/MONO.H b/TIBERIANDAWN/WIN32LIB/MONO.H new file mode 100644 index 000000000..5b0cf89f3 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MONO.H @@ -0,0 +1,181 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.16 06 Sep 1995 16:29:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + unsigned char UpperLeft; + unsigned char TopEdge; + unsigned char UpperRight; + unsigned char RightEdge; + unsigned char BottomRight; + unsigned char BottomEdge; + unsigned char BottomLeft; + unsigned char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/MORPHPAL.CPP b/TIBERIANDAWN/WIN32LIB/MORPHPAL.CPP new file mode 100644 index 000000000..e140d3265 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MORPHPAL.CPP @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : wwlib32 * + * * + * File Name : PALTOPAL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : May 2, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Morph_Palette -- morphs a palette from source to destination * + * Palette_To_Palette -- morph src palette to a dst palette * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ +#define SCALE(a,b,c) (((((long)(a)<<8) / (long)(b) ) * (unsigned long)(c)) >>8) + + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE int __cdecl Palette_To_Palette(void *src_palette, void *dst_palette, unsigned long current_time, unsigned long delay); + + +/*************************************************************************** + * Morph_Palette -- morphs a palette from source to destination * + * * + * INPUT: * + * void *src_pal - starting palette * + * void *dst_pal - ending palette * + * unsigned int delay - time delay in 60ths of a second * + * void *callback - user-defined callback, NULL if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/02/1994 BR : Created. * + *=========================================================================*/ +void cdecl Morph_Palette (void *src_pal, void *dst_pal, unsigned int delay, + void (*callback) (void) ) +{ + int result; + unsigned long pal_start = TickCount.Time(); + extern void (*cb_ptr) ( void ) ; // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback ; + + /*===================================================================*/ + /* Make sure that we don't go too fast but also make sure we keep */ + /* processing the morph palette if we have one. */ + /*===================================================================*/ + while (1) { + if (src_pal && dst_pal) { + result = Palette_To_Palette (src_pal, dst_pal, + (TickCount.Time() - pal_start), (unsigned long)delay); + if (!result) + break; + + if (callback) { + (*cb_ptr)(); + } + } + } + + return; + +} /* end of Morph_Palette */ + + +/*************************************************************************** + * Palette_To_Palette -- morph src palette to a dst palette * + * * + * Creates & sets a palette that's in-between 'src_palette' & * + * 'dst_palette'; how close it is to dst_palette is based on how close * + * 'current_time' is to 'delay'. 'current_time' & 'delay' are based on * + * 0 being the start time. * + * * + * INPUT: void *src_palette = palette we want to morph from * + * void *dst_palette = palette we want to morph to * + * long current_time = time we started morph pal * + * long delay = time we want the morph to take* + * * + * OUTPUT: int if the time had elapsed and no chages were * + * necessary this routine returns FALSE * + * otherwise it will always return TRUE (this * + * was necessary to detect the end of the ice * + * effect. * + * * + * HISTORY: * + * 05/24/1993 MC : Created. * + *=========================================================================*/ +PRIVATE int cdecl Palette_To_Palette(void *src_palette, void *dst_palette, + unsigned long current_time, unsigned long delay) +{ + char colour; + char diff; + int chgval; + int lp; + int change; + static char palette[768]; + char *src_pal = (char*)src_palette; + char *dst_pal = (char*)dst_palette; + + /*======================================================================*/ + /* Loop through each RGB value attempting to change it to the correct */ + /* color. */ + /*======================================================================*/ + for (change = lp = 0; lp < 768; lp++) { + if (current_time < delay ) { + diff = dst_pal[lp] & (char)63; + diff -= src_pal[lp] & (char)63; + if (diff) + change = TRUE; + chgval = SCALE(diff, delay, current_time); + colour = src_pal[lp] & (char)63; + colour +=(char)chgval; + } + else { + colour = dst_pal[lp] & (char)63; + change = FALSE; + } + palette[lp] = colour; + } + /*======================================================================*/ + /* Set the palette to the color that we created. */ + /*======================================================================*/ + Set_Palette(palette); + return(change); + +} /* end of Palette_To_Palette */ + + +/*************************** End of morphpal.cpp ***************************/ + + diff --git a/TIBERIANDAWN/WIN32LIB/MOUSE.H b/TIBERIANDAWN/WIN32LIB/MOUSE.H new file mode 100644 index 000000000..b40d07e72 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MOUSE.H @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +class WWMouseClass { + public: + WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, void *cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicViewPortClass *scr); + void Erase_Mouse(GraphicViewPortClass *scr, int forced = FALSE); + + void Block_Mouse(GraphicBufferClass *buffer); + void Unblock_Mouse(GraphicBufferClass *buffer); + void Set_Cursor_Clip(void); + void Clear_Cursor_Clip(void); + + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicViewPortClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + CRITICAL_SECTION MouseCriticalSection; // Control for mouse re-enterancy + unsigned TimerHandle; + +}; + +extern "C" { + void __cdecl Mouse_Shadow_Buffer(void *thisptr, GraphicViewPortClass *srcdst, void *buffer, int x, int y, int hotx, int hoty, int store); + void __cdecl Draw_Mouse(void *thisptr, GraphicViewPortClass *srcdst, int x, int y); + void * __cdecl ASM_Set_Mouse_Cursor(void * thisptr, int hotspotx, int hotspoty, VOID *cursor); +}; + +void Hide_Mouse(void); +void Show_Mouse(void); +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/MOUSE.INC b/TIBERIANDAWN/WIN32LIB/MOUSE.INC new file mode 100644 index 000000000..c8031cbc6 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MOUSE.INC @@ -0,0 +1,64 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/TIBERIANDAWN/WIN32LIB/MOUSEWW.CPP b/TIBERIANDAWN/WIN32LIB/MOUSEWW.CPP new file mode 100644 index 000000000..bdb952b10 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/MOUSEWW.CPP @@ -0,0 +1,720 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); +extern bool GameInFocus; + + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + InitializeCriticalSection (&MouseCriticalSection); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); // Removed. ST - 2/13/2019 5:12PM + + /* + ** Force the windows mouse pointer to stay withing the graphic view port region + */ + Set_Cursor_Clip(); +} + +WWMouseClass::~WWMouseClass() +{ + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + timeEndPeriod (1000/60); + DeleteCriticalSection(&MouseCriticalSection); + + /* + ** Free up the windows mouse pointer movement + */ + Clear_Cursor_Clip(); +} + + +void Block_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Block_Mouse(buffer); + } +} + + +void Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Unblock_Mouse(buffer); + } +} + + + +void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + EnterCriticalSection(&MouseCriticalSection); + } +} + + +void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + LeaveCriticalSection(&MouseCriticalSection); + } +} + + + + + +void WWMouseClass::Set_Cursor_Clip(void) +{ +#if (0) // Not needed. ST - 1/3/2019 2:18PM + if (Screen){ + RECT region; + + region.left = 0; + region.top = 0; + region.right = Screen->Get_Width(); + region.bottom = Screen->Get_Height(); + + ClipCursor(®ion); + } +#endif +} + + + +void WWMouseClass::Clear_Cursor_Clip(void) +{ +#if (0) + ClipCursor(NULL); +#endif +} + + + +void WWMouseClass::Process_Mouse(void) +{ +//ST - 1/3/2019 10:50AM + return; +#if (0) + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!Screen || !_Mouse || State > 0 || MouseUpdate || EraseFlags || !GameInFocus) + return; + + // + // Make sure there are no conflicts with other + // threads that may try and lock the screen buffer + // + //Block_Mouse(Screen->Get_Graphic_Buffer()); + + // + // If the screen is already locked by another thread then just exit + // + if (Screen->Get_LockCount()!=0){ + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); + return; + } + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } + + // + // Allow other threads to lock the screen again + // + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); +#endif +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ +//ST - 1/3/2019 10:50AM + xhotspot; + yhotspot; + cursor; + return cursor; + +#if (0) + + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +#endif +} + +void WWMouseClass::Low_Hide_Mouse() +{ +//ST - 1/3/2019 10:50AM +#if (0) + if (!State) { + if (MouseBuffX != -1 || MouseBuffY != -1) { + if (Screen->Lock()){ + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + Screen->Unlock(); + } + } + MouseBuffX = -1; + MouseBuffY = -1; + } +#endif + State++; +} +void WWMouseClass::Hide_Mouse() +{ + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + +//ST - 1/3/2019 10:50AM +#if (0) + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + if (Screen->Lock()){ + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } + } +#endif +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) { + Low_Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; +} + + +void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr) +{ + scr; + return; +//ST - 1/3/2019 10:50AM +#if (0) + + POINT pt; + + if (State != 0) return; + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen - dont do video stuff if we cant. + // + if (scr->Lock()){ + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + } + + MouseUpdate--; +#endif +} + +void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced) +{ +//ST - 1/3/2019 10:50AM + scr; + forced; + return; +#if (0) + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + if (scr->Lock()){ + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + scr->Unlock(); + } + EraseBuffX = -1; + EraseBuffY = -1; + } + } + MouseUpdate--; + EraseFlags = FALSE; +#endif +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +//#pragma off(unreferenced) + +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + static BOOL in_mouse_callback = false; + + if (_Mouse && !in_mouse_callback) { + in_mouse_callback = TRUE; + _Mouse->Process_Mouse(); + in_mouse_callback = FALSE; + } +} +//#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +extern int DLLForceMouseX; +extern int DLLForceMouseY; + + +int Get_Mouse_X(void) +{ + if (DLLForceMouseX >= 0) { + return DLLForceMouseX; + } + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (DLLForceMouseY >= 0) { + return DLLForceMouseY; + } + + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/NEWDEL.CPP b/TIBERIANDAWN/WIN32LIB/NEWDEL.CPP new file mode 100644 index 000000000..4a497880c --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/NEWDEL.CPP @@ -0,0 +1,125 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : October 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/TIBERIANDAWN/WIN32LIB/NYBBTB.INC b/TIBERIANDAWN/WIN32LIB/NYBBTB.INC new file mode 100644 index 000000000..48444c49d --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/NYBBTB.INC @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/TIBERIANDAWN/WIN32LIB/PALETTE.CPP b/TIBERIANDAWN/WIN32LIB/PALETTE.CPP new file mode 100644 index 000000000..3c8b92442 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/PALETTE.CPP @@ -0,0 +1,381 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +//#include + +#include "palette.h" +#include "timer.h" +#include "wwstd.h" + + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate); +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * void *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette(void *palette) +{ + + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * void *palette - palette to set color in * + * int color - which color index to set * + * void *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette_Color(void *palette, int color, void *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(unsigned short*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * char *palette1 - this is the palette to fade to. * + * unsigned int delay - fade with this timer count down * + * void *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + short jump; // Gun values to jump per palette set. + unsigned long timer; // Tick count timer used for timing. + short ticksper; // The ticks (fixed point) per bit jump. + int tickaccum; + + + extern void (*cb_ptr)(void); // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < (int)timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * unsigned char *palette - palette to fade to * + * int delay - desired time delay in 60ths of a second * + * short *ticks - output: loop ticks per color jump * + * short *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, + short *rate) +{ + int gun1; // Palette 1 gun value. + int gun2; // Palette 2 gun value. + int diff; // Maximum color gun difference. + int tp; // Temporary tick accumulator. + int index; // Color gun working index. + long t; // Working tick intermediate value. + int adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((long)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (short)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + int index; // Index to DAC register gun. + int gun1,gun2; // Palette 1 gun value. + unsigned char palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = (unsigned char)gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +void (*cb_ptr)(void); // callback function pointer + +/**************************** End of palette.cpp ***************************/ + + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/PALETTE.H b/TIBERIANDAWN/WIN32LIB/PALETTE.H new file mode 100644 index 000000000..6087d0d64 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/PALETTE.H @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +#include +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +void __cdecl Set_Palette(void *palette); +void __cdecl Set_Palette_Color(void *palette, int color, void *data); +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +void __cdecl Morph_Palette (void *src_palette, void *dst_palette, unsigned int delay, + void *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern void __cdecl Set_Palette_Range(void *palette); +extern BOOL __cdecl Bump_Color(void *palette, int changable, int target); + +#ifdef __cplusplus +} +#endif +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ diff --git a/TIBERIANDAWN/WIN32LIB/PLAYCD.H b/TIBERIANDAWN/WIN32LIB/PLAYCD.H new file mode 100644 index 000000000..0c5a68d85 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/PLAYCD.H @@ -0,0 +1,306 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + + +#ifdef NOT_FOR_WIN95 +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +}; + + + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + + + + + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#ifdef NOT_FOR_WIN95 +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + +#endif //NOT_FOR_WIN95 +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/TIBERIANDAWN/WIN32LIB/PROFILE.H b/TIBERIANDAWN/WIN32LIB/PROFILE.H new file mode 100644 index 000000000..bc20add58 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/PROFILE.H @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Library profiler * + * * + * File Name : PROFILE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define MAX_PROFILE_TIME 60*1 //1 minute(s) @ 14.4 Mb per hour +#define PROFILE_RATE 1000 //samples per sec (max 1000) + + +/* + * Defines for choosing between the old and new profiler system + * +*/ + +#define OLD_PROFILE_SYSTEM 1 +#define NEW_PROFILE_SYSTEM 2 + +//#define PROFILE_SYSTEM OLD_PROFILE_SYSTEM +#define PROFILE_SYSTEM NEW_PROFILE_SYSTEM + + + +extern "C"{ + void __cdecl Profile_Init(void); + void __cdecl Profile_End(void); + void __cdecl Start_Profiler(void); + void __cdecl Stop_Profiler(void); +} + diff --git a/TIBERIANDAWN/WIN32LIB/PROFILE.INC b/TIBERIANDAWN/WIN32LIB/PROFILE.INC new file mode 100644 index 000000000..ec99294c2 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/PROFILE.INC @@ -0,0 +1,41 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;USE_PROFILER =1 + + +macro prologue +Ifdef USE_PROFILER + global __PRO:near + + call __PRO +endif ;USE_PROFILER +endm + + + +macro epilogue +ifdef USE_PROFILER + global __EPI:near + + push ebp + call __EPI + pop ebp +endif ;USE_PROFILER +endm + + + diff --git a/TIBERIANDAWN/WIN32LIB/RAWFILE.H b/TIBERIANDAWN/WIN32LIB/RAWFILE.H new file mode 100644 index 000000000..460c3627d --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/RAWFILE.H @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.15 06 Sep 1995 16:29:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include + +//#include +#include +#include +#include +//#include +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void) {if (Allocated && Filename) free((char *)Filename);}; + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + virtual void Set_Buffer_Size(int size); + + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const *Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return Filename; +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Filename(0) +{ + Handle = -1; + Allocated = false; +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle != -1); +} + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/REGIONSZ.CPP b/TIBERIANDAWN/WIN32LIB/REGIONSZ.CPP new file mode 100644 index 000000000..cafd161ee --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/REGIONSZ.CPP @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB 32 * + * * + * File Name : REGIONSZ.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : November 3, 1994 * + * * + * Last Update : November 3, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Size_Of_Region -- Calculates the size of a given region * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * SIZE_OF_REGION -- Calculates the size of a given region * + * * + * INPUT: int width - the width of the region * + * int height - the height of the region * + * * + * OUTPUT: long - the size of the region * + * * + * HISTORY: * + * 11/03/1994 PWG : Created. * + *=========================================================================*/ +long Size_Of_Region(int width, int height) +{ + return(width * height); +} diff --git a/TIBERIANDAWN/WIN32LIB/REMAP.ASM b/TIBERIANDAWN/WIN32LIB/REMAP.ASM new file mode 100644 index 000000000..99356cfde --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/REMAP.ASM @@ -0,0 +1,172 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword + + + cmp [ remap ] , 0 + jz ??real_out + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_remap + + test cl , 1000b + jz ??scr_left_ok + mov [ x0_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y0_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_remap + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +??do_remap: + cld + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + mov ebx , [ x1_pixel ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle ??real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle ??real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +??outer_loop: + mov ebx , [ counter_x ] +??inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz ??inner_loop + add edi , esi + dec ecx + jnz ??outer_loop + + + + +??real_out: + ret + + ENDP Buffer_Remap + + END diff --git a/TIBERIANDAWN/WIN32LIB/SET_FONT.CPP b/TIBERIANDAWN/WIN32LIB/SET_FONT.CPP new file mode 100644 index 000000000..ed1134209 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SET_FONT.CPP @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : SET_FONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Font -- Changes the default text printing font. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include + + + +/*************************************************************************** + * SET_FONT -- Changes the default text printing font. * + * * + * This routine will change the default text printing font for all * + * text output. It handles updating the system where necessary. * + * * + * INPUT: fontptr -- Pointer to the font to change to. * + * * + * OUTPUT: Returns with a pointer to the previous font. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/06/1991 JLB : Created. * + * 09/17/1991 JLB : Fixed return value bug. * + * 01/31/1992 DRD : Modified to use new font format. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Set_Font(void const *fontptr) +{ + void *oldfont; + char const *blockptr; + + oldfont = (void *) FontPtr; + + if (fontptr) { + FontPtr = (void *) fontptr; + + /* + ** Inform the system about the new font. + */ + + FontWidthBlockPtr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTWIDTHBLOCK); + blockptr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTINFOBLOCK); + FontHeight = *(blockptr + FONTINFOMAXHEIGHT); + FontWidth = *(blockptr + FONTINFOMAXWIDTH); + //Draw_Char_Setup(); + +#if FALSE + WindowLines = WinH / FontHeight; + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / FontWidth; +#endif + } + + return(oldfont); +} diff --git a/TIBERIANDAWN/WIN32LIB/SHAPE.H b/TIBERIANDAWN/WIN32LIB/SHAPE.H new file mode 100644 index 000000000..ce686dd01 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SHAPE.H @@ -0,0 +1,187 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +inline MakeShapeFlags_Type operator|(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline MakeShapeFlags_Type operator&(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline MakeShapeFlags_Type operator~(MakeShapeFlags_Type a) +{return static_cast(~static_cast(a));} + + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_BOTTOM = 0x0040, // Y coord is based on shape's bottom pt + SHAPE_FADING = 0x0100, // Fading effect (void * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (void * color_table) +} ShapeFlags_Type; + +inline ShapeFlags_Type operator|(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline ShapeFlags_Type operator&(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ShapeFlags_Type operator~(ShapeFlags_Type a) +{return static_cast(~static_cast(a));} + + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + unsigned short ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + unsigned char Height; // Height of the shape in scan lines + unsigned short Width; // Width of the shape in bytes + unsigned char OriginalHeight; // Original height of shape in scan lines + unsigned short ShapeSize; // Size of the shape, including header + unsigned short DataLength; // Size of the uncompressed shape (just data) + unsigned char Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +//#pragma warning (push, 4200) +#pragma warning (disable : 4200) +typedef struct { + unsigned short NumShapes; // number of shapes in the block + long Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; +//#pragma warning (pop) + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern void *MaskPage; +extern void *BackGroundPage; +extern long _ShapeBufferSize; +extern char *_ShapeBuffer; +} + + +void __cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +int Draw_Shape(GraphicViewPortClass *gvp, void const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short __cdecl Get_Shape_Data(void const *shape, int data); +int __cdecl Extract_Shape_Count(void const *buffer); +void * __cdecl Extract_Shape(void const *buffer, int shape); +int __cdecl Restore_Shape_Height(void *shape); +int __cdecl Set_Shape_Height(void const *shape, int newheight); + +extern "C" { +int __cdecl Get_Shape_Width(void const *shape); +int __cdecl Get_Shape_Height(void const *shape); +int __cdecl Get_Shape_Original_Height(void const *shape); +int __cdecl Get_Shape_Uncomp_Size(void const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +void __cdecl Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +int __cdecl Get_Shape_Flags(void const *shape); +int __cdecl Get_Shape_Size(void const *shape); +int __cdecl Get_Shape_Scaled_Width(void const *shape, int scale); +int __cdecl Get_Shape_Scaled_Height(void const *shape, int scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/SHAPE.INC b/TIBERIANDAWN/WIN32LIB/SHAPE.INC new file mode 100644 index 000000000..f1cb250e9 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SHAPE.INC @@ -0,0 +1,212 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_BOTTOM EQU 0040h ; Y coord is based on shape bottom pt +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL C ShapeBuffer:DWORD +GLOBAL C ShapeBufferSize:DWORD +GLOBAL C _MaskPage:DWORD +GLOBAL C _BackGroundPage:DWORD +GLOBAL C PredCount:DWORD +GLOBAL C PredTable:BYTE +GLOBAL C PredValue:DWORD +GLOBAL C PartialPred:DWORD +GLOBAL C PartialCount:DWORD +GLOBAL C Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/TIBERIANDAWN/WIN32LIB/SOUND.H b/TIBERIANDAWN/WIN32LIB/SOUND.H new file mode 100644 index 000000000..ff7258209 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SOUND.H @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" +#include "dsound.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/SOUNDINT.H b/TIBERIANDAWN/WIN32LIB/SOUNDINT.H new file mode 100644 index 000000000..76171dfd1 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SOUNDINT.H @@ -0,0 +1,289 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/TIBERIANDAWN/WIN32LIB/STAMP.INC b/TIBERIANDAWN/WIN32LIB/STAMP.INC new file mode 100644 index 000000000..c629e2350 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/STAMP.INC @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + +ifdef NO_WAY_THIS_WILL_BE_DEFINED_HAHAHAHA + + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +else + + + STRUC IControl_Type + +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? +MapHeight DW ? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? +Map DD ? ; Icon map offset. + ENDS + +endif + +ICON256 EQU 1 diff --git a/TIBERIANDAWN/WIN32LIB/STRUCTS.H b/TIBERIANDAWN/WIN32LIB/STRUCTS.H new file mode 100644 index 000000000..995ebb542 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/STRUCTS.H @@ -0,0 +1,34 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/TIBERIANDAWN/WIN32LIB/SVGAPRIM.INC b/TIBERIANDAWN/WIN32LIB/SVGAPRIM.INC new file mode 100644 index 000000000..33813ca90 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/SVGAPRIM.INC @@ -0,0 +1,74 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/TIBERIANDAWN/WIN32LIB/TILE.H b/TIBERIANDAWN/WIN32LIB/TILE.H new file mode 100644 index 000000000..f6a505603 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TILE.H @@ -0,0 +1,100 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + + +#if (1) +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ + +// C&C version of struct +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char *Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char *Map; // Icon map offset (if present). +} IControl_Type; + + +#else + +// RA version of struct +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; + +#endif + + +#endif //TILE_H diff --git a/TIBERIANDAWN/WIN32LIB/TIMER.CPP b/TIBERIANDAWN/WIN32LIB/TIMER.CPP new file mode 100644 index 000000000..fb1cc38fe --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TIMER.CPP @@ -0,0 +1,200 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + TickType=timer; + + if (on && TimerSystemOn) Start(); +} + + + + +/*********************************************************************************************** + * TC:Get_Ticks -- return the number of ticks on the system or user timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:17PM ST : Created * + *=============================================================================================*/ + +long TimerClass::Get_Ticks ( void ) + +{ + if ( WindowsTimer ){ + switch ( TickType ){ + + case BT_SYSTEM : + return ( WindowsTimer->Get_System_Tick_Count() ); + + case BT_USER : + return ( WindowsTimer->Get_User_Tick_Count() ); + + } + } + return 0; +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/TIMER.H b/TIBERIANDAWN/WIN32LIB/TIMER.H new file mode 100644 index 000000000..fb5d98625 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TIMER.H @@ -0,0 +1,198 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include +#include + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; +extern HANDLE TimerThreadHandle; //Handle of timer thread +extern int InTimerCallback; //true if we are currently in a callback + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: +// long (*Get_Ticks)(void); // System timer fetch. + BaseTimerEnum TickType; + long Get_Ticks (void); +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + + +class WinTimerClass { + + public: + WinTimerClass ( UINT freq=60 , BOOL partial=0 ); + ~WinTimerClass(); + + void Update_Tick_Count ( void ); + unsigned Get_System_Tick_Count ( void ); + unsigned Get_User_Tick_Count ( void ); + + private: + + unsigned TimerHandle; //Handle for windows timer event + unsigned Frequency; //Frequency of our windows timer in ticks per second + + unsigned TrueRate; //True rate of clock. (only use word) + unsigned SysTicks; //Tick count of timer. + unsigned UserTicks; //Tick count of timer. + unsigned UserRate; //Desired rate of timer. + + +}; + + +extern WinTimerClass *WindowsTimer; + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// +#ifndef FUNCTION_H +extern TimerClass TickCount; +#endif +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long __cdecl Get_System_Tick_Count(void); + long __cdecl Get_User_Tick_Count(void); + void far __cdecl Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void __cdecl Disable_Timer_Interrupt(void); + void __cdecl Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL __cdecl Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL __cdecl Remove_Timer_System(VOID); + + +#endif // TIMER_H + diff --git a/TIBERIANDAWN/WIN32LIB/TIMERDWN.CPP b/TIBERIANDAWN/WIN32LIB/TIMERDWN.CPP new file mode 100644 index 000000000..90fea6e97 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TIMERDWN.CPP @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/TIBERIANDAWN/WIN32LIB/TIMERINI.CPP b/TIBERIANDAWN/WIN32LIB/TIMERINI.CPP new file mode 100644 index 000000000..6cca62694 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TIMERINI.CPP @@ -0,0 +1,312 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" +#include +#include + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + + +// Prototype for timer callback +void CALLBACK Timer_Callback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +HANDLE TimerThreadHandle = 0; //Handle of timer thread +int InTimerCallback = 0; //Flag to say if we are in a timer callback + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + + +#pragma warning (disable : 4996) + + +/*************************************************************************** + * WinTimerClass::WinTimerClass -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::WinTimerClass (UINT freq, BOOL partial) +{ + BOOL success; + + // + // Inform windows that we want a higher than normal + // timer resolution + // +#ifdef __SW_EP + timeBeginPeriod(1000/PROFILE_RATE); + Frequency = PROFILE_RATE; +#else + timeBeginPeriod ( 1000/freq ); + Frequency = freq; +#endif + + + // + // Install the timer callback event handler + // + //TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC); + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + TimerSystemOn = success = ( TimerHandle !=0 ); + + if (success) { + if (!partial) { + WindowsTimer=this; + TickCount.Start(); + } + }else{ + char error_str [128]; + sprintf (error_str, "Error - timer system failed to start. Error code %d\n", GetLastError()); + OutputDebugString(error_str); + } +} + + + +/*************************************************************************** + * WinTimerClass::~WinTimerClass -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::~WinTimerClass( void ) +{ + + if ( TimerHandle ){ + timeKillEvent ( TimerHandle ); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + + TimerSystemOn = FALSE; + timeEndPeriod ( 1000/Frequency ); +} + + + + + +/*********************************************************************************************** + * Timer_Callback -- Main timer callback. Equivalent to a timer interrupt handler * + * * + * * + * * + * INPUT: uint timer ID * + * uint reserved * + * long 0 (application defined) * + * long reserved * + * long reserved * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:19PM ST : Created * + *=============================================================================================*/ + + +void CALLBACK Timer_Callback (UINT , UINT , DWORD , DWORD , DWORD) +{ + //CONTEXT context; + + InTimerCallback++; + + // Removed. ST - 2/13/2019 5:11PM + //if (!TimerThreadHandle){ + // DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&TimerThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS); + //} + + + if (WindowsTimer) { + WindowsTimer->Update_Tick_Count(); + } + InTimerCallback--; +} + + + + + + +/*********************************************************************************************** + * WinTimerClass::Update_Tick_Count -- update westwood timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:58PM ST : Created * + *=============================================================================================*/ + +void WinTimerClass::Update_Tick_Count ( void ) +{ +/* + * + * Increment westwood timers + * + */ + SysTicks++; + UserTicks++; + +} + + + + + + + + +/* +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + */ + + + + +/*********************************************************************************************** + * WinTimerClass::Get_System_Tick_Count -- returns the system tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_System_Tick_Count ( void ) +{ + return ( SysTicks ); +} + + + +/*********************************************************************************************** + * WinTimerClass::Get_User_Tick_Count -- returns the user tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_User_Tick_Count ( void ) +{ + return ( UserTicks ); +} \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/TOBUFF.ASM b/TIBERIANDAWN/WIN32LIB/TOBUFF.ASM new file mode 100644 index 000000000..b210cf3bc --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/TOBUFF.ASM @@ -0,0 +1,294 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* Last Update : Feb 10, 1995 [jrj] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;IDEAL +;P386 +;MODEL USE32 FLAT +;LOCALS ?? +.model flat + + +TRANSP equ 0 + + +;INCLUDE "drawbuff.inc" +INCLUDE gbuffer.inc + + +;CODESEG +.code + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* +Buffer_To_Buffer proc C public USES ebx ecx edx esi edi this_object:DWORD, x_pixel:DWORD, y_pixel:DWORD, pixel_width:DWORD, pixel_height:DWORD, dest:DWORD, buffer_size:DWORD + + ;PROC Buffer_To_Buffer C near + ;USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ;ARG this_object:DWORD ; this is a class member function + ;ARG x_pixel:DWORD ; Page X pixel coordinate. + ;ARG y_pixel:DWORD ; Page Y pixel coordinate. + ;ARG pixel_width:DWORD ; Width of region in pixels. + ;ARG pixel_height:DWORD ; Height of region in pixels. + ;ARG dest:DWORD ; the buffer to copy to + ;ARG buffer_size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + ;LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this_object ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [esi].GraphicViewPort.GVPWidth ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi].GraphicViewPort.GVPHeight ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_blit + + test cl , 1000b + jz scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +scr_left_ok: + test cl , 0010b + jz scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +scr_bottom_ok: + test dl , 0100b + jz scr_right_ok + mov eax , [esi].GraphicViewPort.GVPWidth ; get width into register + mov [ x1_pixel ] , eax +scr_right_ok: + test dl , 0001b + jz do_blit + mov eax , [esi].GraphicViewPort.GVPHeight ; get width into register + mov [ y1_pixel ] , eax + +do_blit: + + cld + + mov eax , [esi].GraphicViewPort.GVPXAdd + add eax , [esi].GraphicViewPort.GVPWidth + add eax , [esi].GraphicViewPort.GVPPitch + mov esi , [esi].GraphicViewPort.GVPOffset + + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + sub edx , [ y_pixel ] + jle real_out + sub eax , [ x_pixel ] + jle real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ buffer_size ] + jg real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + cmp [ transp ] , 0 + jnz forward_Blit_trans +ENDIF + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl forward_loop_bytes + +forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_dword + ret + +forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz forward_loop_bytes + ret + + +IF TRANSP + +forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + transp_reference: + dec ecx + jge forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz forward_loop_trans + ret +ENDIF + +real_out: + ret + +;ENDP Buffer_To_Buffer +Buffer_To_Buffer endp + +END \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/VIDEO.H b/TIBERIANDAWN/WIN32LIB/VIDEO.H new file mode 100644 index 000000000..223d4e6a8 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/VIDEO.H @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + +#endif //NOT_FOR_WIN95 + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + +extern REALPTR VesaFunc; + +#endif //NOT_FOR_WIN95 + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/WINCOMM.H b/TIBERIANDAWN/WIN32LIB/WINCOMM.H new file mode 100644 index 000000000..5d26049b8 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WINCOMM.H @@ -0,0 +1,454 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WIN32 +#define WIN32 +#define _WIN32 +#endif //WIN32 +#include + +typedef enum WinCommDialMethodType { + WC_TOUCH_TONE = 0, + WC_PULSE +} WinCommDialMethodType; + + + +#define COMMSUCCESS 0 +#define ASTIMEOUT -10 +#define COMMUSERABORT -16 + + +/* +** The size of our serial buffer within the class. +** +** !!!!!! THIS MUST BE A POWER OF 2 !!!!!! +** +*/ +#define SIZE_OF_WINDOWS_SERIAL_BUFFER 2048 + + + +/* +** WinModemClass. +** +** This class provides access to modems under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +*/ + +class WinModemClass +{ + + public: + + WinModemClass (void); //WinModemClass Contructor + virtual ~WinModemClass (void); //WinModemClass Destructor + + + /* + ** Serial port open should be called to get a handle to the COM port + ** This needs to be called first as other class members rely on the handle + ** + ** Replacement for Greenleaf function: PortOpenGreenleafFast + */ + //virtual HANDLE Serial_Port_Open (int port, int baud, int parity, int wordlen, int stopbits); + virtual HANDLE Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol); + + /* + ** This function releases the COM port handle and should be called after + ** communications have finished + ** + ** Replacement for Greenleaf function: PortClose + */ + void Serial_Port_Close (void); + + /* + ** This member copies any bytes from the internal class serial buffer + ** into your user buffer. + ** + ** Replacement for Greenleaf function: ReadBuffer + */ + int Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len); + + /* + ** Write chars to the serial port + ** + ** Replacement for Greenleaf function: WriteBuffer + */ + void Write_To_Serial_Port (unsigned char *buffer, int length); + + /* + ** Wait for the outgoing buffer to empty + */ + void Wait_For_Serial_Write (void); + + /* + ** Set the dial type to DIAL_TOUCH_TONE or DIAL_PULSE + ** + ** Replacement for Greenleaf function: HMSetDiallingMethod + */ + virtual void Set_Modem_Dial_Type (WinCommDialMethodType method); + + /* + ** Get the status of the modem control lines + ** Possible flags are: CTS_SET DSR_SET RI_SET & CD_SET + ** + ** Replacement for Greenleaf function: GetModemStatus + */ + virtual unsigned Get_Modem_Status (void); + + /* + ** Set the DTR line to the given state + ** + ** Replacement for Greenleaf function: SetDtr + */ + virtual void Set_Serial_DTR (BOOL state); + + /* + ** Get the result code from the modem after issuing an 'AT' command + ** + ** Replacement for Greenleaf function: HMInputLine + */ + virtual int Get_Modem_Result (int delay, char *buffer, int buffer_len); + + /* + ** Issue a dial command to the modem. + ** Use Set_Modem_Dial_Type to select pulse or tone dial + ** + ** Replacement for Greenleaf function: HMDial + */ + virtual void Dial_Modem (char *dial_number); + + /* + ** Send a command to the modem. This is usually an 'AT' command. + ** Function will optionally retry until 'OK' is received. + */ + virtual int Send_Command_To_Modem (char *command, char terminator, char *buffer, int buflen, int delay, int retries); + + /* + ** Sets a pointer to a function that will be called for each incoming serial char + ** + ** Replacement for Greenleaf function: HMSetUpEchoRoutine + */ + virtual void Set_Echo_Function (void(*func)(char c)); + + /* + ** Sets a pointer to a function that will be called if ESC is pressed during a dial + ** + ** Replacement for Greenleaf function: HMSetUpAbortKey + */ + virtual void Set_Abort_Function (int (*func)(void)); + + /* + ** Member to allow access to the serial port handle + */ + HANDLE Get_Port_Handle(void); + + /* + ** Status vars for debugging purposes + */ + int FramingErrors; + int IOErrors; + int BufferOverruns; + int InBufferOverflows; + int ParityErrors; + int OutBufferOverflows; + int InQueue; + int OutQueue; + + /* + ** Modem send result codes + */ + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + + /* + ** Enums for modem status flags + */ + enum { + CTS_SET = 0x10, + DSR_SET = 0x20, + RI_SET = 0x40, + CD_SET = 0x80 + }; + + + protected: + + + /* + ** Copy incoming data from the windows file buffer into the internal class buffer + */ + BOOL Read_Serial_Chars(void); + + /* + ** Pointer to the internal class circular buffer for incoming data + */ + unsigned char *SerialBuffer; + + /* + ** Overlap object for asyncronous reads from the serial port + */ + OVERLAPPED ReadOverlap; + + /* + ** Overlap object for asyncronous writes to the serial port + */ + OVERLAPPED WriteOverlap; + + /* + ** Flag that there is no outstanding incoming data in the windows buffer + */ + BOOL WaitingForSerialCharRead; + + /* + ** Flag that we are waiting for the last write to port operation to complete + */ + BOOL WaitingForSerialCharWrite; + + /* + ** Head and Tail pointers for our internal serial buffer + */ + int SerialBufferReadPtr; + int SerialBufferWritePtr; + + /* + ** Windows handle to the COM port device + */ + HANDLE PortHandle; + + /* + ** Dialing method - DIAL_TOUCH_TONE or DIAL_PULSE + */ + WinCommDialMethodType DialingMethod; + + /* + ** Pointer to function for echoing incoming data - can be NULL + */ + void (*EchoFunction)(char c); + + /* + ** Pointer to function for aborting when ESC pressed - can be NULL + */ + int (*AbortFunction)(void); + + /* + ** Serial buffer for asyncronous reads + */ + char TempSerialBuffer[SIZE_OF_WINDOWS_SERIAL_BUFFER]; +}; + + + + + + + + + + +/* +** WinNullModemClass. +** +** This class provides access to serial ports under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +** +** This class just overloads the WinModemClass members that arent required for direct serial communications +** via a 'null modem' cable. +*/ +class WinNullModemClass : public WinModemClass +{ + + public: + + virtual inline void Set_Modem_Dial_Type (int){}; + virtual inline unsigned Get_Modem_Status (void){return (0);}; + virtual inline void Set_Serial_DTR (BOOL){}; + virtual inline int Get_Modem_Result (int, char*, int){return(0);}; + virtual inline void Dial_Modem (char*){}; + virtual inline int Send_Command_To_Modem (char*, char, char*, int, int, int){return (0);}; + virtual inline void Set_Echo_Function (void(*)(char)){}; + virtual inline void Set_Abort_Function (int(*)(void)){}; + +}; + + +extern WinModemClass *SerialPort; + + + + + + + + + +// +// +// This bit swiped from the SDK because its not in the Watcom headers yet +// +// + +/************************************************************************ +* * +* mcx.h -- This module defines the 32-Bit Windows MCX APIs * +* * +* Copyright (c) 1990-1995, Microsoft Corp. All rights reserved. * +* * +************************************************************************/ + +#ifndef _MCX_H_ +#define _MCX_H_ + +typedef struct _MODEMDEVCAPS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // product and version identification + DWORD dwModemProviderVersion; + DWORD dwModemManufacturerOffset; + DWORD dwModemManufacturerSize; + DWORD dwModemModelOffset; + DWORD dwModemModelSize; + DWORD dwModemVersionOffset; + DWORD dwModemVersionSize; + + // local option capabilities + DWORD dwDialOptions; // bitmap of supported values + DWORD dwCallSetupFailTimer; // maximum in seconds + DWORD dwInactivityTimeout; // maximum in seconds + DWORD dwSpeakerVolume; // bitmap of supported values + DWORD dwSpeakerMode; // bitmap of supported values + DWORD dwModemOptions; // bitmap of supported values + DWORD dwMaxDTERate; // maximum value in bit/s + DWORD dwMaxDCERate; // maximum value in bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS; + +typedef struct _MODEMSETTINGS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // static local options (read/write) + DWORD dwCallSetupFailTimer; // seconds + DWORD dwInactivityTimeout; // seconds + DWORD dwSpeakerVolume; // level + DWORD dwSpeakerMode; // mode + DWORD dwPreferredModemOptions; // bitmap + + // negotiated options (read only) for current or last call + DWORD dwNegotiatedModemOptions; // bitmap + DWORD dwNegotiatedDCERate; // bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMSETTINGS, *PMODEMSETTINGS, *LPMODEMSETTINGS; + +// Dial Options +#define DIALOPTION_BILLING 0x00000040 // Supports wait for bong "$" +#define DIALOPTION_QUIET 0x00000080 // Supports wait for quiet "@" +#define DIALOPTION_DIALTONE 0x00000100 // Supports wait for dial tone "W" + +// SpeakerVolume for MODEMDEVCAPS +#define MDMVOLFLAG_LOW 0x00000001 +#define MDMVOLFLAG_MEDIUM 0x00000002 +#define MDMVOLFLAG_HIGH 0x00000004 + +// SpeakerVolume for MODEMSETTINGS +#define MDMVOL_LOW 0x00000000 +#define MDMVOL_MEDIUM 0x00000001 +#define MDMVOL_HIGH 0x00000002 + +// SpeakerMode for MODEMDEVCAPS +#define MDMSPKRFLAG_OFF 0x00000001 +#define MDMSPKRFLAG_DIAL 0x00000002 +#define MDMSPKRFLAG_ON 0x00000004 +#define MDMSPKRFLAG_CALLSETUP 0x00000008 + +// SpeakerMode for MODEMSETTINGS +#define MDMSPKR_OFF 0x00000000 +#define MDMSPKR_DIAL 0x00000001 +#define MDMSPKR_ON 0x00000002 +#define MDMSPKR_CALLSETUP 0x00000003 + +// Modem Options +#define MDM_COMPRESSION 0x00000001 +#define MDM_ERROR_CONTROL 0x00000002 +#define MDM_FORCED_EC 0x00000004 +#define MDM_CELLULAR 0x00000008 +#define MDM_FLOWCONTROL_HARD 0x00000010 +#define MDM_FLOWCONTROL_SOFT 0x00000020 +#define MDM_CCITT_OVERRIDE 0x00000040 +#define MDM_SPEED_ADJUST 0x00000080 +#define MDM_TONE_DIAL 0x00000100 +#define MDM_BLIND_DIAL 0x00000200 +#define MDM_V23_OVERRIDE 0x00000400 + +#endif /* _MCX_H_ */ + + + + + + + + + + + + + + + + diff --git a/TIBERIANDAWN/WIN32LIB/WINDOWS.CPP b/TIBERIANDAWN/WIN32LIB/WINDOWS.CPP new file mode 100644 index 000000000..b31d4345e --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WINDOWS.CPP @@ -0,0 +1,973 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "ww_win.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; + +extern GraphicViewPortClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + // PWG - have to figure out how to do this in windows library + +// Clear_KeyBuffer(); +// Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ +// case KA_LITERAL: +// if (c != (char) 127) { // check if fell thru from extend case +// c = 0; // set to zero for literal case +// } +// c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > (unsigned)WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + (unsigned char)WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= (unsigned)WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= (unsigned)WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > (unsigned)breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= (unsigned)WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & (char)0x07; + c = (char)((c & (char)0x78) >> 3); + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/WINHIDE.CPP b/TIBERIANDAWN/WIN32LIB/WINHIDE.CPP new file mode 100644 index 000000000..93680deaf --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WINHIDE.CPP @@ -0,0 +1,94 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "ww_win.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; +// Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ +// Conditional_Show_Mouse(); +} +#endif + + + diff --git a/TIBERIANDAWN/WIN32LIB/WRITEPCX.CPP b/TIBERIANDAWN/WIN32LIB/WRITEPCX.CPP new file mode 100644 index 000000000..d3681b4a2 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WRITEPCX.CPP @@ -0,0 +1,177 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +#include +static void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + *=========================================================================*/ +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ) +{ + unsigned char palcopy[256 * 3]; + unsigned i ; + //unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == WW_ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + + for ( i = 0 ; i < (unsigned)header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, (unsigned char*)ptr + i * VP_Scan_Line ) ; + + Mem_Copy(palette, palcopy, 256 * 3); + pal = ( RGB * ) palcopy ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palcopy , 256 * sizeof (RGB) ) ; + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + + +void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + unsigned char * file_ptr ; + unsigned char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + + for ( i = 1 ; i < (unsigned)scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else { + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last ) ; + } + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} diff --git a/TIBERIANDAWN/WIN32LIB/WSA.CPP b/TIBERIANDAWN/WIN32LIB/WSA.CPP new file mode 100644 index 000000000..3dcc2f977 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WSA.CPP @@ -0,0 +1,1153 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./wsa.c 1.16 1994/05/20 15:35:27 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library Animation System * + * * + * File Name : WSA.C * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 20, 1991 * + * * + *-------------------------------------------------------------------------* + * There are many different ways that the user can use the WSA library * + * module. The options are as follows : * + * * + * System Allocation vs User Buffer - The user may request that the * + * system allocate the needed buffer from the heap or the user may * + * pass his own buffer in for the animator to use. * + * * + * Resident vs File based - If there is enough RAM, the user may put the * + * entire animation into RAM for fastest animations. If there is * + * not enouph RAM, the system will automatically make it so each * + * frame will be read off disk when needed. * + * * + * Direct to Page vs Use of a user buffer -- Noramally Direct to page * + * is the best method both in speed and in RAM need to hold anim. * + * One may want to use the write to user buffer method if they * + * are using the animation in a non sequencial order. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Animate_Frame -- Displays a frame of a given animation * + * Get_Animation_Frame_Count -- Return Number of frames in an animation. * + * Get_Animation_X -- Gets the x from an animation * + * Get_Animation_Y -- Gets the y from an animation * + * Get_Animation_Width -- Gets the width from an animation * + * Get_Animation_Height -- The height of the animation we are processing * + * Apply_Delta -- Copies frame into delta buffer, then applies to target * + * Close_Animation -- Close the animation, freeing the space if necessary* + * Get_File_Frame_Offset -- Get offset of a delta frame from animate file* + * Get_Resident_Frame_Offset -- Gets frame offset of animate file in RAM * + * Open_Animation -- Opens an animation file and reads into buffer * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "wsa.h" +#include +#include +#include +#include +//#include +#include + +// +// WSA animation header allocation type. +// If we need more then 8 flags for the flags variable, we can combine +// USER_ALLOCATED with SYS_ALLOCATED and combine FILE with RESIDENT. +// +#define WSA_USER_ALLOCATED 0x01 +#define WSA_SYS_ALLOCATED 0x02 +#define WSA_FILE 0x04 +#define WSA_RESIDENT 0x08 +#define WSA_TARGET_IN_BUFFER 0x10 +#define WSA_LINEAR_ONLY 0x20 +#define WSA_FRAME_0_ON_PAGE 0x40 +#define WSA_AMIGA_ANIMATION 0x80 +#define WSA_PALETTE_PRESENT 0x100 +#define WSA_FRAME_0_IS_DELTA 0x200 + +// These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +// These change, make sure and change their values in lp_asm.asm. +#define DO_XOR 0x0 +#define DO_COPY 0x01 +#define TO_VIEWPORT 0x0 +#define TO_PAGE 0x02 + + + + +typedef struct { + unsigned short current_frame; + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + char *delta_buffer; + char *file_buffer; + char file_name[ 13 ]; + short flags; + // New fields that animate does not know about below this point. SEE EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT + short file_handle; + unsigned long anim_mem_size; +} SysAnimHeaderType; + +// NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE +// UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING +// IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. +#define EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT (sizeof(short) + sizeof(unsigned long)) + + +// +// Header structure for the file. +// NOTE: The 'total_frames' field is used to differentiate between Amiga and IBM +// animations. Amiga animations have the HIGH bit set. +// +typedef struct { + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + short flags; + unsigned long frame0_offset; + unsigned long frame0_end; + /* unsigned long data_seek_offset, unsigned short frame_size ... */ +} WSA_FileHeaderType; + +#define WSA_FILE_HEADER_SIZE ( sizeof(WSA_FileHeaderType) - (2 * sizeof(unsigned long)) ) + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ); +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust); +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_ANIMATION -- Opens an animation file and reads into buffer * + * * + * INPUT: char *file_name of animation sequence file. * + * char *user_buffer pointer if one exists (NULL ok) * + * unsigned long user_buffer_size if known (NULL ok) * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to palette space for return (NULL ok) * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: May return NULL, please check. * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette) +{ + int fh, anim_flags; + int palette_adjust; + unsigned int offsets_size; + unsigned int frame0_size; + long target_buffer_size, delta_buffer_size, file_buffer_size; + long max_buffer_size, min_buffer_size; + char *sys_anim_header_buffer; + char *target_buffer; + char *delta_buffer, *delta_back; + SysAnimHeaderType *sys_header; + WSA_FileHeaderType file_header; + + /*======================================================================*/ + /* Open the file to get the header information */ + /*======================================================================*/ + + anim_flags = 0; + fh = Open_File(file_name, READ); + Read_File(fh, (char *) &file_header, sizeof(WSA_FileHeaderType)); + + /*======================================================================*/ + /* If the file has an attached palette then if we have a valid palette */ + /* pointer we need to read it in. */ + /*======================================================================*/ + + if (file_header.flags & 1) { + anim_flags |= WSA_PALETTE_PRESENT; + palette_adjust = 768; + + if (palette != NULL) { + Seek_File(fh, sizeof(unsigned long) * (file_header.total_frames), SEEK_CUR); + Read_File(fh, palette, 768L); + } + + } else { + palette_adjust = 0; + } + + // Check for flag from ANIMATE indicating that this animation was + // created from a .LBM and a .ANM. These means that the first + // frame is a XOR Delta from a picture, not black. + if (file_header.flags & 2) { + anim_flags |= WSA_FRAME_0_IS_DELTA; + } + + + // Get the total file size minus the size of the first frame and the size + // of the file header. These will not be read in to save even more space. + file_buffer_size = Seek_File(fh, 0L, SEEK_END); + + if (file_header.frame0_offset) { + long tlong; + + tlong = file_header.frame0_end - file_header.frame0_offset; + frame0_size = (unsigned short) tlong; + } + else { + anim_flags |= WSA_FRAME_0_ON_PAGE; + frame0_size = 0; + } + + file_buffer_size -= palette_adjust + frame0_size + WSA_FILE_HEADER_SIZE; + + // We need to determine the buffer sizes required for the animation. At a + // minimum, we need a target buffer for the uncompressed frame and a delta + // buffer for the delta data. We may be able to make the file resident, + // so we will determine the file size. + // + // If the target buffer is in the user buffer + // Then figure its size + // and set the allocation flag + // Else size is zero. + // + if (user_flags & WSA_OPEN_DIRECT) { + target_buffer_size = 0L; + } + else { + anim_flags |= WSA_TARGET_IN_BUFFER; + target_buffer_size = (unsigned long) file_header.pixel_width * file_header.pixel_height; + } + + // NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE + // UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING + // IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. + delta_buffer_size = (unsigned long) file_header.largest_frame_size + EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT; + min_buffer_size = target_buffer_size + delta_buffer_size; + max_buffer_size = min_buffer_size + file_buffer_size; + + // check to see if buffer size is big enough for at least min required + if (user_buffer && (user_buffer_size < min_buffer_size)) { + Close_File(fh); + return(NULL); + } + + // A buffer was not passed in, so do allocations + if (user_buffer == NULL) { + + // If the user wants it from the disk, then let us give it to him, + // otherwise, try to give a max allocation he can have. + if (user_flags & WSA_OPEN_FROM_DISK) { + user_buffer_size = min_buffer_size; + } + // else no buffer size, then try max configuration. + else if (!user_buffer_size) { + user_buffer_size = max_buffer_size; + } + // else if buffer specified is less then max needed, give min. + else if (user_buffer_size < max_buffer_size) { + user_buffer_size = min_buffer_size; + } + // otherwise we only want to alloc what we need. + else { + user_buffer_size = max_buffer_size; + } + + + // Check to see if enough RAM available for buffer_size. + if (user_buffer_size > Ram_Free(MEM_NORMAL)) { + + // If not enough room for even the min, return no buffer. + + if (min_buffer_size > Ram_Free(MEM_NORMAL)) { + Close_File(fh); + return(NULL); + } + + // Else make buffer size the min and allocate it. + user_buffer_size = min_buffer_size; + } + + // allocate buffer needed + user_buffer = (char *) Alloc(user_buffer_size, MEM_CLEAR); + + anim_flags |= WSA_SYS_ALLOCATED; + } + else { + // Check to see if the user_buffer_size should be min or max. + if ((user_flags & WSA_OPEN_FROM_DISK) || (user_buffer_size < max_buffer_size)) { + user_buffer_size = min_buffer_size; + } + else { + user_buffer_size = max_buffer_size; + } + anim_flags |= WSA_USER_ALLOCATED; + } + + + // Set the pointers to the RAM buffers + sys_anim_header_buffer = user_buffer; + target_buffer = (char *) Add_Long_To_Pointer(sys_anim_header_buffer, sizeof(SysAnimHeaderType)); + delta_buffer = (char *) Add_Long_To_Pointer(target_buffer, target_buffer_size); + + // Clear target buffer if it is in the user buffer. + if (target_buffer_size) { + memset( target_buffer, 0, (unsigned short) target_buffer_size ); + } + + // Poke data into the system animation header (start of user_buffer) + // current_frame is set to total_frames so that Animate_Frame() knows that + // it needs to clear the target buffer. + + sys_header = ( SysAnimHeaderType * ) sys_anim_header_buffer; + sys_header -> current_frame = + sys_header -> total_frames = file_header.total_frames; + sys_header -> pixel_x = file_header.pixel_x; + sys_header -> pixel_y = file_header.pixel_y; + sys_header -> pixel_width = file_header.pixel_width; + sys_header -> pixel_height = file_header.pixel_height; + sys_header -> anim_mem_size = user_buffer_size; + sys_header -> delta_buffer = delta_buffer; + sys_header -> largest_frame_size = + (unsigned short) (delta_buffer_size - sizeof(SysAnimHeaderType)); + + strcpy(sys_header->file_name, file_name); + + // Figure how much room the frame offsets take up in the file. + // Add 2 - one for the wrap around and one for the final end offset. + offsets_size = (file_header.total_frames + 2) << 2; + + // Can the user_buffer_size handle the maximum case buffer? + if ( user_buffer_size == max_buffer_size) { + + // + // set the file buffer pointer, + // Skip over the header information. + // Read in the offsets. + // Skip over the first frame. + // Read in remaining frames. + // + + sys_header->file_buffer = (char *)Add_Long_To_Pointer(delta_buffer,sys_header->largest_frame_size); + Seek_File( fh, WSA_FILE_HEADER_SIZE, SEEK_SET); + Read_File( fh, sys_header->file_buffer, offsets_size); + Seek_File( fh, frame0_size + palette_adjust, SEEK_CUR); + Read_File( fh, sys_header->file_buffer + offsets_size, + file_buffer_size - offsets_size); + + // + // Find out if there is an ending value for the last frame. + // If there is not, then this animation will not be able to + // loop back to the beginning. + // + if (Get_Resident_Frame_Offset( sys_header->file_buffer, sys_header->total_frames + 1)) + anim_flags |= WSA_RESIDENT; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_RESIDENT; + } + else { // buffer cannot handle max_size of buffer + + if(Get_File_Frame_Offset( fh, sys_header->total_frames + 1, palette_adjust)) + anim_flags |= WSA_FILE; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_FILE; +//// + sys_header->file_buffer = NULL; + } + + // Figure where to back load frame 0 into the delta buffer. + delta_back = (char *)Add_Long_To_Pointer(delta_buffer, + sys_header->largest_frame_size - frame0_size); + + // Read the first frame into the delta buffer and uncompress it. + // Then close it. + Seek_File( fh, WSA_FILE_HEADER_SIZE + offsets_size + palette_adjust, SEEK_SET); + Read_File( fh, delta_back, frame0_size); + + // We do not use the file handle when it is in RAM. + if (anim_flags & WSA_RESIDENT) { + sys_header -> file_handle = (short) -1; + Close_File(fh); + } + else { + sys_header -> file_handle = (short)fh; + } + + LCW_Uncompress(delta_back, delta_buffer, sys_header->largest_frame_size); + + // Finally set the flags, + sys_header->flags = (short)anim_flags; + + // return valid handle + return( user_buffer ); +} + + +/*************************************************************************** + * CLOSE_ANIMATION -- Close the animation, freeing the space if necessary. * + * * + * INPUT: void *handle to the animation data buffer * + * * + * OUTPUT: none * + * * + * WARNINGS: handle MUST have been returned by Open_Animation * + * * + * HISTORY: * + * 11/23/1991 ML : Created. * + *=========================================================================*/ +void __cdecl Close_Animation( void *handle ) +{ + SysAnimHeaderType *sys_header; + + // Assign our local system header pointer to the beginning of the handle space + sys_header = (SysAnimHeaderType *) handle; + + // Close the WSA file in it was disk based. + if (sys_header->flags & WSA_FILE) { + Close_File(sys_header->file_handle); + } + + // Check to see if the buffer was allocated OR the programmer provided the buffer + if (handle && sys_header->flags & WSA_SYS_ALLOCATED) { + Free(handle); + } +} + +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +//#pragma off (unreferenced) +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + if (view.Lock()!=TRUE) return (FALSE); + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd() + view.Get_Pitch(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + view.Unlock(); + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = (short)frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + view.Unlock(); + return TRUE; +} +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +//#pragma argsused +#ifdef cuts +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} +#endif + + +/*************************************************************************** + * ANIMATE_FRAME_COUNT -- Return Number of frames in an animation. * + * * + * INPUT: void *handle to the animation. * + * * + * OUTPUT: int number of frames in animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/05/1991 SB : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Frame_Count(void *handle) +{ + SysAnimHeaderType *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return((short)sys_header->total_frames); +} + + +/*************************************************************************** + * GET_ANIM_X -- Gets the x from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the x of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1992 DRD : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_X(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_x); +} + +/*************************************************************************** + * GET_ANIM_Y -- Gets the y from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the y of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Y(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_y); +} + +/*************************************************************************** + * GET_ANIM_WIDTH -- Gets the width from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the width of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Width(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_width); +} + +/*************************************************************************** + * GET_ANIM_HEIGHT -- The height of the animation we are processing * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the height of the animation we are processing * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Height(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_height); +} + + +/*************************************************************************** + * GET_ANIM_PALETTE -- Returns true if the anim had an attached palette * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int True if the animation has a set palette. False if the * + * animation does not. * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Palette(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->flags & WSA_PALETTE_PRESENT); +} + + +/*************************************************************************** + * GET_ANIMATION_SIZE -- Return the amount of memory the animation is using* + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: unsigned long number of byte used by animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/23/1994 SKB : Created. * + *=========================================================================*/ +unsigned long __cdecl Get_Animation_Size(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->anim_mem_size); +} + +/* :::::::::::::::::::::::::::: PRIVATE FUNCTIONS :::::::::::::::::::::::::::::: */ + + +/*************************************************************************** + * GET_RESIDENT_FRAME_OFFSET -- Gets frame offset of animate file in RAM * + * * + * INPUT: char *file_buffer in RAM of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ) +{ + unsigned long frame0_size; + unsigned long *lptr; + + // If there is a frame 0, the calculate its size. + lptr = (unsigned long *) file_buffer; + + if (*lptr) { + frame0_size = lptr[1] - *lptr; + } else { + frame0_size = 0; + } + + // Return the offset into RAM for the frame. + lptr += frame; + if (*lptr) + return (*lptr - (frame0_size + WSA_FILE_HEADER_SIZE)); + else + return (0L); +} + + +/*************************************************************************** + * GET_FILE_FRAME_OFFSET -- Get offset of a delta frame from animate file. * + * * + * INPUT: int file_handle of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust) +{ + unsigned long offset; + + Seek_File(file_handle, (frame << 2) + WSA_FILE_HEADER_SIZE, SEEK_SET); + + if (Read_File(file_handle, (char *) &offset, sizeof(unsigned long)) != sizeof(unsigned long)) { + offset = 0L; + } + offset += palette_adjust; + return( offset ); +} + + +/*************************************************************************** + * APPLY_DELTA -- Copies frame into delta buffer, then applies to target * + * * + * INPUT: SysAnimHeaderType *sys_header - pointer to animation buffer.* + * int curr_frame - frame to put into target buffer. * + * * + * OUTPUT: BOOL - Return wether or not it worked. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w) +{ + char *data_ptr, *delta_back; + int file_handle, palette_adjust; + unsigned long frame_data_size, frame_offset; + + + palette_adjust = ((sys_header->flags & WSA_PALETTE_PRESENT) ? 768 : 0); + delta_back = sys_header->delta_buffer; + + if (sys_header->flags & WSA_RESIDENT) { + // Get offset of the given frame in the resident file + // Get the size of the frame <- (frame+1 offset) - (offset) + // Point at the delta data + // figure offset to load data into end of delta buffer + // copy it into buffer + + frame_offset = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame); + frame_data_size = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame + 1) - frame_offset; + + data_ptr = (char *)Add_Long_To_Pointer(sys_header->file_buffer, frame_offset); + delta_back = (char *)Add_Long_To_Pointer(delta_back, + sys_header->largest_frame_size - frame_data_size); + + Mem_Copy( data_ptr, delta_back, frame_data_size ); + + } else if (sys_header -> flags & WSA_FILE) { + + // Open up file because not file not in RAM. + // Get offset of the given frame in the file on disk + // Get the size of the frame <- (frame+1 offset) - (offset) + // Return if Get_.._offset() failed. -- need error handling???? + // Seek to delta data. + // figure offset to load data into end of delta buffer + // Read it into buffer -- Return if correct amount not read.-- errors?? + + file_handle = sys_header->file_handle; + Seek_File(file_handle, 0L, SEEK_SET); + + frame_offset = Get_File_Frame_Offset(file_handle, curr_frame, palette_adjust); + frame_data_size = Get_File_Frame_Offset(file_handle, curr_frame + 1, palette_adjust) - frame_offset; + + if (!frame_offset || !frame_data_size) { + return(FALSE); + } + + Seek_File(file_handle, frame_offset, SEEK_SET); + delta_back = (char *)Add_Long_To_Pointer(delta_back, sys_header->largest_frame_size - frame_data_size); + + if (Read_File(file_handle, delta_back, frame_data_size) != frame_data_size) { + return(FALSE); + } + } + + // Uncompress data at end of delta buffer to the beginning of delta buffer. + // Find start of target buffer. + // Apply the XOR delta. + + LCW_Uncompress(delta_back, sys_header->delta_buffer, sys_header->largest_frame_size); + + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + Apply_XOR_Delta(dest_ptr, sys_header->delta_buffer); + } + else { + Apply_XOR_Delta_To_Page_Or_Viewport(dest_ptr, sys_header->delta_buffer, + sys_header->pixel_width, dest_w, DO_XOR); + } + + return(TRUE); +} + diff --git a/TIBERIANDAWN/WIN32LIB/WSA.H b/TIBERIANDAWN/WIN32LIB/WSA.H new file mode 100644 index 000000000..8cb0fd6dc --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WSA.H @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WSA 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette=NULL); +void __cdecl Close_Animation( void *handle ); +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +BOOL __cdecl Animate_Frame(void *handle, VideoViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +int __cdecl Get_Animation_X(void const *handle); +int __cdecl Get_Animation_Y(void const *handle); +int __cdecl Get_Animation_Width(void const *handle); +int __cdecl Get_Animation_Height(void const *handle); +int __cdecl Get_Animation_Palette(void const *handle); +unsigned long __cdecl Get_Animation_Size(void const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, BufferClass& buffer, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, (char *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +unsigned int __cdecl Apply_XOR_Delta(char *source_ptr, char *delta_ptr); +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy); +} + + + +#endif // WSA_H + diff --git a/TIBERIANDAWN/WIN32LIB/WWFILE.H b/TIBERIANDAWN/WIN32LIB/WWFILE.H new file mode 100644 index 000000000..d7dd4069d --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WWFILE.H @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.14 06 Sep 1995 16:30:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/TIBERIANDAWN/WIN32LIB/WWLIB32.H b/TIBERIANDAWN/WIN32LIB/WWLIB32.H new file mode 100644 index 000000000..88fe24e93 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WWLIB32.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H + + diff --git a/TIBERIANDAWN/WIN32LIB/WWMEM.H b/TIBERIANDAWN/WIN32LIB/WWMEM.H new file mode 100644 index 000000000..20bb06ac8 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WWMEM.H @@ -0,0 +1,67 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/TIBERIANDAWN/WIN32LIB/WWMEM.INC b/TIBERIANDAWN/WIN32LIB/WWMEM.INC new file mode 100644 index 000000000..954b433ad --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WWMEM.INC @@ -0,0 +1,40 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/TIBERIANDAWN/WIN32LIB/WWSTD.H b/TIBERIANDAWN/WIN32LIB/WWSTD.H new file mode 100644 index 000000000..83ee8b9b0 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WWSTD.H @@ -0,0 +1,224 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + + +#include +#include + +#ifndef IBM +#define IBM TRUE +#endif + +#define WW_ERROR -1 + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + + + +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +//PG_TO_FIX +//#pragma option -Jg +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +//int ABS(int); +//long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +//short MIN(short, short); +//int MIN(int, int); +//long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + + +#endif //WWSTD_H \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/WW_WIN.H b/TIBERIANDAWN/WIN32LIB/WW_WIN.H new file mode 100644 index 000000000..7f8786166 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/WW_WIN.H @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/XORDELTA.ASM b/TIBERIANDAWN/WIN32LIB/XORDELTA.ASM new file mode 100644 index 000000000..79416fb9c --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/XORDELTA.ASM @@ -0,0 +1,668 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; ************************************************************************** +; ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S * +; ************************************************************************** +; * * +; * Project Name : WSA Support routines * +; * * +; * File Name : XORDELTA.ASM * +; * * +; * Programmer : Scott K. Bowen * +; * * +; * Last Update :May 23, 1994 [SKB] * +; * * +; *------------------------------------------------------------------------* +; * Functions: * +;* Apply_XOR_Delta -- Apply XOR delta data to a buffer. * +;* Apply_XOR_Delta_To_Page_Or_Viewport -- Calls the copy or the XOR funti* +;* Copy_Delta_buffer -- Copies XOR Delta Data to a section of a page. * +;* XOR_Delta_Buffer -- Xor's the data in a XOR Delta format to a page. * +; * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* + +;IDEAL +;P386 +;MODEL USE32 FLAT +.model flat + + + +;LOCALS ?? + +; These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +; These change, make sure and change their values in wsa.cpp. +DO_XOR equ 0 +DO_COPY equ 1 +TO_VIEWPORT equ 0 +TO_PAGE equ 2 + +; +; Routines defined in this module +; +; +; UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr); +; PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD copy) +; +; PROC C XOR_Delta_Buffer +; PROC C Copy_Delta_Buffer +; + +externdef C Apply_XOR_Delta:NEAR +externdef C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR + + + + ;CODESEG + .code + + +;*************************************************************************** +;* APPLY_XOR_DELTA -- Apply XOR delta data to a linear buffer. * +;* AN example of this in C is at the botton of the file commented out. * +;* * +;* INPUT: BYTE *target - destination buffer. * +;* BYTE *delta - xor data to be delta uncompress. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 05/23/1994 SKB : Created. * +;*=========================================================================* +Apply_XOR_Delta proc C public USES ebx ecx edx esi edi target:DWORD, delta:DWORD + + ;USES ebx,ecx,edx,edi,esi + ;ARG target:DWORD ; pointers. + ;ARG delta:DWORD ; pointers. + + ; Optimized for 486/pentium by rearanging instructions. + mov edi,[target] ; get our pointers into offset registers. + mov esi,[delta] + + cld ; make sure we go forward + xor ecx,ecx ; use cx for loop + +top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + +; +; SHORTDUMP +; + mov ecx,eax ; stick count in cx + +dump_loop: + mov al,[esi] ;get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edi + dec ecx + jnz dump_loop + jmp top_loop + +; +; SHORTRUN +; + +short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + +do_run: + mov al,[esi] ; get XOR byte + inc esi + +run_loop: + xor [edi],al ; xor that byte. + + inc edi ; go to next dest pixel + dec ecx ; one less to go. + jnz run_loop + jmp top_loop + +; +; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP +; + +check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] + lea esi,[esi+2] ; get word code in ax + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +do_skip: + add edi,eax ; do the skip. + jmp top_loop + + +not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + +; +; LONGRUN +; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + +; +; LONGDUMP +; + +long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + +stop: + + ret + +Apply_XOR_Delta endp + + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* APPLY_XOR_DELTA_To_Page_Or_Viewport -- Calls the copy or the XOR funtion. * +;* * +;* * +;* This funtion is call to either xor or copy XOR_Delta data onto a * +;* page instead of a buffer. The routine will set up the registers * +;* need for the actual routines that will perform the copy or xor. * +;* * +;* The registers are setup as follows : * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* + + +Apply_XOR_Delta_To_Page_Or_Viewport proc C public USES ebx ecx edx esi edi target:DWORD, delta:DWORD, xwidth:DWORD, nextrow:DWORD, copy:DWORD + + ;USES ebx,ecx,edx,edi,esi + ;ARG target:DWORD ; pointer to the destination buffer. + ;ARG delta:DWORD ; pointer to the delta buffer. + ;ARG width:DWORD ; width of animation. + ;ARG nextrow:DWORD ; Page/Buffer width - anim width. + ;ARG copy:DWORD ; should it be copied or xor'd? + + + mov edi,[target] ; Get the target pointer. + mov esi,[delta] ; Get the destination pointer. + + xor eax,eax ; clear eax, later put them into ecx and edx. + + cld ; make sure we go forward + + mov ebx,[nextrow] ; get the amount to add to get to next row from end. push it later... + + mov ecx,eax ; use cx for loop + mov edx,eax ; use dx to count the relative column. + + push ebx ; push nextrow onto the stack for Copy/XOR_Delta_Buffer. + mov ebx,[xwidth] ; bx will hold the max column for speed compares + +; At this point, all the registers have been set up. Now call the correct function +; to either copy or xor the data. + + cmp [copy],DO_XOR ; Do we want to copy or XOR + je xorfunct ; Jump to XOR if not copy + call Copy_Delta_Buffer ; Call the function to copy the delta buffer. + jmp didcopy ; jump past XOR +xorfunct: + call XOR_Delta_Buffer ; Call funtion to XOR the deltat buffer. +didcopy: + pop ebx ; remove the push done to pass a value. + + ret + +Apply_XOR_Delta_To_Page_Or_Viewport ENDP +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* XOR_DELTA_BUFFER -- Xor's the data in a XOR Delta format to a page. * +;* This will only work right if the page has the previous data on it. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* edx,ecx,eax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* + +XOR_Delta_Buffer proc C nextrow:DWORD + +top_loop2: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run2 + js check_others2 + +; +; SHORTDUMP +; + mov ecx,eax ; stick count in cx + +dump_loop2: + mov al,[esi] ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edx ; increment our count on current column + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row +end_col1: + + dec ecx + jnz dump_loop2 + jmp top_loop2 + +; +; SHORTRUN +; + +short_run2: + mov cl,[esi] ; get count + inc esi ; inc delta source + +do_run2: + mov al,[esi] ; get XOR byte + inc esi + +run_loop2: + xor [edi],al ; xor that byte. + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row +end_col2: + + + dec ecx + jnz run_loop2 + jmp top_loop2 + +; +; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP +; + +check_others2: + sub eax,080h ; opcode -= 0x80 + jnz do_skip2 ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip2 + +; +; SHORTSKIP AND LONGSKIP +; +do_skip2: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row +recheck2: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck2 ; jump up to see if we are at the right row +end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop2 + + +not_long_skip2: + jz stop2 ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump2 + +; +; LONGRUN +; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run2 ; jump to run code. + + +; +; LONGDUMP +; + +long_dump2: + mov ecx,eax ; use cx as loop count + jmp dump_loop2 ; go to the dump loop. + +stop2: + + ret + + +XOR_Delta_Buffer ENDP + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* COPY_DELTA_BUFFER -- Copies XOR Delta Data to a section of a page. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +Copy_Delta_Buffer proc C nextrow:DWORD + + ;ARG nextrow:DWORD + +top_loop3: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run3 + js check_others3 + +; +; SHORTDUMP +; + mov ecx,eax ; stick count in cx + +dump_loop3: + mov al,[esi] ; get delta XOR byte + + mov [edi],al ; store that byte on the dest + + inc edx ; increment our count on current column + inc esi + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1_3 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row +end_col1_3: + + dec ecx + jnz dump_loop3 + jmp top_loop3 + +; +; SHORTRUN +; + +short_run3: + mov cl,[esi] ; get count + inc esi ; inc delta source + +do_run3: + mov al,[esi] ; get XOR byte + inc esi + +run_loop3: + mov [edi],al ; store the byte (instead of XOR against current color) + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2_3 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row +end_col2_3: + + + dec ecx + jnz run_loop3 + jmp top_loop3 + +; +; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP +; + +check_others3: + sub eax,080h ; opcode -= 0x80 + jnz do_skip3 ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip3 + +; +; SHORTSKIP AND LONGSKIP +; +do_skip3: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row +recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3_3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row +end_col3_3: + add edi,edx ; get to correct position in row. + jmp top_loop3 + + +not_long_skip3: + jz stop3 ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump3 + +; +; LONGRUN +; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run3 ; jump to run code. + + +; +; LONGDUMP +; + +long_dump3: + mov ecx,eax ; use cx as loop count + jmp dump_loop3 ; go to the dump loop. + +stop3: + + ret + +Copy_Delta_Buffer ENDP + +;---------------------------------------------------------------------------- + + END + + +;---------------------------------------------------------------------------- +; +;PUBLIC UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr) +;{ +; +; register UWORD loop; +; BYTE opcode, xor_byte; +; UWORD bytes_to_uncompress = 64000U; +; +; +; /* Make our buffer pointer */ +; +; to = MK_FP(page_seg, 0); +; delta = Normalize_Pointer(delta_ptr); +; +; +; while (bytes_to_uncompress) { +; +; opcode = *delta++; +; +; +; /* Check for SHORTDUMP */ +; +; if (opcode > 0) { +; +; +; bytes_to_uncompress -= opcode; +; +; for (loop = 0; loop < opcode; loop++) { +; xor_byte = *delta++; +; *to++ ^= xor_byte; +; } +; continue; +; } +; +; /* Check for SHORTRUN */ +; +; if (opcode == 0) { +; +; word_count = *delta++; +; xor_byte = *delta++; +; +; bytes_to_uncompress -= word_count; +; +; for (loop = 0; loop < word_count; loop++) { +; *to++ ^= xor_byte; +; } +; continue; +; } +; +; /* By now, we know it must be a LONGDUMP, SHORTSKIP, or LONGSKIP */ +; +; opcode -= 0x80; +; +; +; /* Is it a SHORTSKIP? */ +; +; if (opcode != 0) { +; +; to += opcode; +; bytes_to_uncompress -= (WORD) opcode; +; continue; +; } +; +; +; word_count = *((UWORD *) delta)++; +; +; /* Is it a LONGSKIP? */ +; +; if ((WORD) word_count > 0) { +; +; to += word_count; +; bytes_to_uncompress -= (WORD) word_count; +; continue; +; } +; +; +; word_count -= 0x8000; +; +; /* Is it a LONGRUN? */ +; +; if (word_count & 0x4000) { +; +; word_count -= 0x4000; +; +; bytes_to_uncompress -= word_count; +; +; xor_byte = *delta++; +; +; for (loop = 0; loop < word_count; loop++) { +; *to++ ^= xor_byte; +; } +; continue; +; } +; +; +; /* It must be a LONGDUMP */ +; +; bytes_to_uncompress -= word_count; +; +; for (loop = 0; loop < word_count; loop++) { +; xor_byte = *delta++; +; *to++ ^= xor_byte; +; } +; } +; +; +; return(64000U); +;} +; + + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/_DIPTABL.CPP b/TIBERIANDAWN/WIN32LIB/_DIPTABL.CPP new file mode 100644 index 000000000..d3cf78465 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/_DIPTABL.CPP @@ -0,0 +1,55 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/TIBERIANDAWN/WIN32LIB/_FILE.H b/TIBERIANDAWN/WIN32LIB/_FILE.H new file mode 100644 index 000000000..b5a35e9d6 --- /dev/null +++ b/TIBERIANDAWN/WIN32LIB/_FILE.H @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/TIBERIANDAWN/WINASM.ASM b/TIBERIANDAWN/WINASM.ASM new file mode 100644 index 000000000..85283f21b --- /dev/null +++ b/TIBERIANDAWN/WINASM.ASM @@ -0,0 +1,887 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : WINSAM.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : October 26th, 1995 * +;* * +;* Last Update : October 26th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C _AbortModemFunctionPtr:dword +global C Memory_Error_Exit :dword +global C MouseQX :dword +global C MouseQY :dword + +global FastGetPortHardware_ :near +global FastSetPortHardware_ :near +global PortOpenGreenleafFast_ :near +global HMWaitForOK_ :near +global HMSetDialingMethod_ :near +global HMDial_ :near +global HMInputLine_ :near +global HMAnswer_ :near +global PortKillTime_ :near +global HMSendStringNoWait_ :near +global HMSetUpEchoRoutine_ :near +global HMSetUpAbortKey_ :near +global SetAbortModemFunctionPtr_:near +global Change8259Priority_ :near +global HMSendString_ :near +global C Stop_Execution :near + + +global _IPX_Initialise:near +global _ASM_IPX_Initialise:near + + + codeseg + +proc _ASM_IPX_Initialise near + + int 3 + jmp _IPX_Initialise + +endp + + + + +global _Int3:near +proc _Int3 near + ;int 3 + ret +endp + + +proc Stop_Execution C near + + nop + ret + +endp + + + +; +; Stuff needed from the shape library +; +; +; + + +INCLUDE "shape.inc" + + + + +;*************************************************************************** +;* ModeX_Blit -- Copy a 320x200 graphic view port to a modex screen * +;* * +;* * +;* INPUT: eax - graphic view port * +;* * +;* OUTPUT: none * +;* * +;* PROTO: extern "C" void ModeX_Blit (GraphicBufferClass *source); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* + +SEQUENCER =3c4h ; sequencer port +MAP_MASK =2 ; map mask register + + INCLUDE "gbuffer.inc" + global ModeX_Blit_:near + + +proc ModeX_Blit_ NEAR + + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + + mov ebp,200 + +??each_line_lp: mov ah,1 ;1st plane + push ebx + push esi + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + + push esi + push edi + push eax + + rept 10 + mov al,[esi] + mov bl,[esi+8] + mov cl,[esi+16] + mov dl,[esi+24] + mov ah,[esi+4] + mov bh,[esi+12] + mov ch,[esi+20] + mov dh,[esi+28] + shl ebx,16 + shl edx,16 + or ebx,eax + or edx,ecx + mov [edi],ebx + mov [edi+4],edx + add esi,32 + add edi,8 + endm + + pop eax + pop edi + pop esi + inc esi + shl ah,1 + cmp ah,16 + jl ??each_plane_lp + + + pop esi + pop ebx + lea esi,[esi+ebx+320] + add edi,80 + dec ebp + jnz ??each_line_lp + + popad + + ret + +endp ModeX_Blit_ + + + + + + +ifdef cuts + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + mov ah,1 ;1st plane + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + mov ebp,200 ;do 200 lines + push esi + push edi + +??each_line_lp: mov ecx,320/4 + +??each_pixel_lp:mov dl,[esi] + mov [edi],dl + add esi,4 + inc edi + dec ecx + jnz ??each_pixel_lp + + add esi,ebx + dec ebp + jnz ??each_line_lp + + pop edi + pop esi + inc esi + shl ah,1 + + cmp ah,16 + jl ??each_plane_lp +endif + + + + + + + + + + +proc FastGetPortHardware_ NEAR +endp + +proc FastSetPortHardware_ NEAR +endp + +proc PortOpenGreenleafFast_ NEAR +endp + +proc HMWaitForOK_ NEAR +endp + +proc HMSetDialingMethod_ NEAR +endp + +proc HMDial_ NEAR +endp + +proc HMInputLine_ NEAR +endp + +proc HMAnswer_ NEAR +endp + +proc PortKillTime_ NEAR +endp + +proc HMSendStringNoWait_ NEAR +endp + +proc HMSetUpEchoRoutine_ NEAR +endp + +proc HMSetUpAbortKey_ NEAR +endp + +proc SetAbortModemFunctionPtr_ NEAR +endp + +proc Change8259Priority_ NEAR +endp + +proc HMSendString_ NEAR +endp + + ret + + + + + + + + masm +; +; Change a DAC colour register directly +; +; register number in al +; +; bh=red bl=green cl=blue +; + +set_dac_col proc near + pushad + cli + push eax + mov dx,03dah + in al,dx + jmp @@1 +@@1: mov dx,03c8h + pop eax + out dx,al + jmp @@2 +@@2: inc dl + mov al,bh + out dx,al + jmp @@3 +@@3: mov al,bl + out dx,al + jmp @@4 +@@4: mov al,cl + out dx,al + jmp @@5 +@@5: sti + popad + ret +set_dac_col endp + + ideal + + +global Set_Palette_Register_:near + + +proc Set_Palette_Register_ near + + pushad + and cl,63 + mov bh,dl + and bh,63 + and bl,63 + call set_dac_col + popad + ret + +endp Set_Palette_Register_ + + + + + locals ?? + + + + ends + + dataseg + +LineBuffer dd 640 dup (?) + ends + + + + segment mycode page public use32 'code' ; Need stricter segment alignment + +global C Asm_Interpolate:near +global C Asm_Interpolate_Line_Double:near +global C Asm_Interpolate_Line_Interpolate:near +global C PaletteInterpolationTable:byte + + +;********************************************************************************************* +;* Asm_Interpolate -- interpolate a 320x200 buffer to a 640x400 screen * +;* * +;* INPUT: ptr to source buffer (320x200 image) * +;* ptr to dest buffer (640x400) * +;* height of source buffer * +;* width of source buffer * +;* width of dest buffer * +;* * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 12/15/95 ST : Created. * +;*===========================================================================================* + +PROC Asm_Interpolate C near + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + +??each_line_loop: + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov edi,[old_dest] + jmp ??interpolate_loop + + align 32 +; +; convert 2 pixels of source into 4 pixels of destination +; so we can write to video memory with dwords +; +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + +;1st 3 pixels now in ebx + + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank on the end of a row + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + mov edi,[dest_width] + add [old_dest],edi + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate + + + + + + + + + + + + + + +PROC Asm_Interpolate_Line_Double C near + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL width_counter:dword + LOCAL pixel_count:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + mov edi,[dest_ptr] + +??each_line_loop: + mov [width_counter],0 + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov [pixel_count],ecx + mov ecx,offset LineBuffer + mov edi,[old_dest] + jmp ??interpolate_loop + align 16 + +; convert 2 pixels of source into 4 pixels of destination +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + mov [ecx],ebx + add edi,4 + add ecx,4 + + dec [pixel_count] + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + mov [edi],ah + mov [ecx],ah + inc edi + inc ecx + mov [byte edi],0 + mov [byte ecx],0 + + mov edi,[dest_width] + shr edi,1 + add [old_dest],edi + push esi + push edi + mov esi,offset LineBuffer + mov edi,[old_dest] + mov ecx,[source_width] + shr ecx,1 + rep movsd + pop edi + pop esi + add [old_dest],edi + mov edi,[old_dest] + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate_Line_Double + + + + + + + + + + ends + + dataseg + +TopLine dd 640 dup (?) +BottomLine dd 640 dup (?) + + + segment mycode page public use32 'code' ; Need stricter segment alignment + + +proc Interpolate_Single_Line C near + + ARG source_ptr:dword + ARG dest_ptr:dword + ARG source_width:dword + + pushad + + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + + mov esi,[source_ptr] + mov edi,[dest_ptr] + +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + popad + ret + + +endp Interpolate_Single_Line + + +proc Interpolate_Between_Lines C near + + ARG source1:dword + ARG source2:dword + ARG destination:dword + ARG source_width:dword + + pushad + mov esi,[source1] + mov edi,[destination] + mov ebx,[source2] + xor eax,eax + mov ecx,[source_width] + add ecx,ecx + +??interpolate_each_pixel_loop: + mov al,[esi] + mov ah,[ebx] + inc esi + inc ebx + mov dl,[eax+PaletteInterpolationTable] + mov [edi],dl + inc edi + dec ecx + jnz ??interpolate_each_pixel_loop + + popad + ret + +endp Interpolate_Between_Lines + + + + +macro Lineswp + push [next_line] + push [last_line] + pop [next_line] + pop [last_line] +endm + + +PROC Asm_Interpolate_Line_Interpolate C near + + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_lines:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL pixel_count:dword + LOCAL next_line:dword + LOCAL last_line:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov [next_line],offset TopLine + mov [last_line],offset BottomLine + mov ecx,[source_width] + shr ecx,1 + mov [pixel_count],ecx + shr [dest_width],1 + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[source_width] + Lineswp + add [src_ptr],esi + dec [source_lines] + + +??each_line_pair_loop: + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + call Interpolate_Between_Lines C,[last_line],[next_line],offset LineBuffer,[source_width] + + mov esi,[last_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + mov edi,[old_dest] + mov esi,offset LineBuffer + add edi,[dest_width] + mov ecx,[pixel_count] + mov [old_dest],edi + rep movsd + + mov edi,[old_dest] + mov esi,[source_width] + add edi,[dest_width] + add [src_ptr],esi + mov [old_dest],edi + + Lineswp + dec [source_lines] + jnz ??each_line_pair_loop + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[next_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + popad + ret + + +endp Asm_Interpolate_Line_Interpolate + + ends mycode + + + + +global C Asm_Create_Palette_Interpolation_Table:near +global C InterpolationPalette:dword + + codeseg + + +proc Asm_Create_Palette_Interpolation_Table C near + + LOCAL palette_counter:dword + LOCAL first_palette:dword + LOCAL second_palette:dword + LOCAL dest_ptr:dword + LOCAL count:dword + LOCAL closest_colour:dword + LOCAL distance_of_closest:dword + + pushad + + mov [dest_ptr],0 + mov [palette_counter],256 + mov esi,[InterpolationPalette] + +??palette_outer_loop: + mov edi,[InterpolationPalette] + mov ecx,256 + +??palette_inner_loop: + mov bl,[esi] + add bl,[edi] + shr bl,1 + + mov bh,[esi+1] + add bh,[edi+1] + shr bh,1 + + mov dl,[esi+2] + add dl,[edi+2] + shr dl,1 + + mov [closest_colour],0 + mov [distance_of_closest],-1 + + push edi + push ecx + mov edi,[InterpolationPalette] + mov [count],0 + +??cmp_pal_lp: xor eax,eax + xor ecx,ecx + mov al,[edi] + sub al,bl + imul al + mov ecx,eax + mov al,[edi+1] + sub al,bh + imul al + add ecx,eax + mov al,[edi+2] + sub al,dl + imul al + add ecx,eax + + cmp ecx,[distance_of_closest] + ja ??end_cmp_lp + mov [distance_of_closest],ecx + mov eax,[count] + mov [closest_colour],eax + test ecx,ecx + jz ??got_perfect + +??end_cmp_lp: lea edi,[edi+3] + inc [count] + cmp [count],256 + jb ??cmp_pal_lp + + +??got_perfect: mov edi,[dest_ptr] + mov eax,[closest_colour] + mov [edi+PaletteInterpolationTable],al + inc [dest_ptr] + + pop ecx + pop edi + lea edi,[edi+3] + dec ecx + jnz ??palette_inner_loop + + lea esi,[esi+3] + dec [palette_counter] + jnz ??palette_outer_loop + + popad + ret + +endp Asm_Create_Palette_Interpolation_Table + + + + + DATASEG + +_AbortModemFunctionPtr dd 0 +Memory_Error_Exit dd 0 +MouseQX dd 0 +MouseQY dd 0 + +end diff --git a/TIBERIANDAWN/WINSTUB.CPP b/TIBERIANDAWN/WINSTUB.CPP new file mode 100644 index 000000000..62fe62a13 --- /dev/null +++ b/TIBERIANDAWN/WINSTUB.CPP @@ -0,0 +1,938 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : October 4th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This file contains stubs for undefined externals when linked under Watcom for Win 95 * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +void output(short,short) +{} + +bool InDebugger = false; +bool ReadyToQuit = false; + +#if (0) +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int __cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * __cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ +#endif //(0) + + + +unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus +extern void VQA_PauseAudio(void); +extern void VQA_ResumeAudio(void); + + +ThemeType OldTheme = THEME_NONE; + + +/*********************************************************************************************** + * Focus_Loss -- this function is called when a library function detects focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 2:10PM ST : Created * + *=============================================================================================*/ + +void Focus_Loss(void) +{ + if (SoundOn){ + if (OldTheme == THEME_NONE){ + OldTheme = Theme.What_Is_Playing(); + } + } + Theme.Stop(); + Stop_Primary_Sound_Buffer(); + if (WWMouse) WWMouse->Clear_Cursor_Clip(); +} + + +void Focus_Restore(void) +{ + Restore_Cached_Icons(); + Map.Flag_To_Redraw(true); + Start_Primary_Sound_Buffer(TRUE); + if (WWMouse) WWMouse->Set_Cursor_Clip(); + VisiblePage.Clear(); + HiddenPage.Clear(); +} + + + +/*********************************************************************************************** + * Check_For_Focus_Loss -- check for the end of the focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/2/96 10:49AM ST : Created * + *=============================================================================================*/ + +void Check_For_Focus_Loss(void) +{ + +// ST - 1/3/2019 10:40AM +#if (0) + static BOOL focus_last_time = 1; + MSG msg; + + if ( !GameInFocus ){ + Focus_Loss(); + while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ){ + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (!focus_last_time && GameInFocus){ + + VQA_PauseAudio(); + CountDownTimerClass cd; + cd.Set(60*1); + + do { + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } while(cd.Time()); + VQA_ResumeAudio(); + //AllSurfaces.Restore_Surfaces(); + //VisiblePage.Clear(); + //HiddenPage.Clear(); + //Map.Flag_To_Redraw(true); + PostMessage (MainWindow, CCFocusMessage,0,0); + } + + focus_last_time = GameInFocus; +#endif +} + + + +extern BOOL InMovie; +#if (0) //PG_TO_FIX +long FAR PASCAL _export Windows_Procedure (HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + + int low_param = LOWORD(wParam); + + if (message == CCFocusMessage){ + Start_Primary_Sound_Buffer(TRUE); + if (!InMovie){ + Theme.Queue_Song(OldTheme); + OldTheme = THEME_NONE; + } + return(0); + } + + + switch ( message ){ + + case WM_MOUSEMOVE: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + Kbd.Message_Handler(hwnd, message, wParam, lParam); + return(0); + + case WM_DESTROY: + CCDebugString ("C&C95 - WM_DESTROY message received.\n"); + CCDebugString ("C&C95 - About to call Prog_End.\n"); + Prog_End(); + CCDebugString ("C&C95 - About to Invalidate_Cached_Icons.\n"); + Invalidate_Cached_Icons(); + CCDebugString ("C&C95 - About to release the video surfaces.\n"); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger){ + CCDebugString ("C&C95 - About to reset the video mode.\n"); + Reset_Video_Mode(); + } + CCDebugString ("C&C95 - About to stop the profiler.\n"); + Stop_Profiler(); + CCDebugString ("C&C95 - Posting the quit message.\n"); + PostQuitMessage( 0 ); + /* + ** If we are shutting down gracefully than flag that the message loop has finished. + ** If this is a forced shutdown (ReadyToQuit == 0) then try and close down everything + ** before we exit. + */ + if (ReadyToQuit){ + CCDebugString ("C&C95 - We are now ready to quit.\n"); + ReadyToQuit = 2; + }else{ + CCDebugString ("C&C95 - Emergency shutdown.\n"); + CCDebugString ("C&C95 - Shut down the network stuff.\n"); +#ifndef DEMO + Shutdown_Network(); +#endif + CCDebugString ("C&C95 - Kill the Winsock stuff.\n"); + if (Winsock.Get_Connected()) Winsock.Close(); + CCDebugString ("C&C95 - Call ExitProcess.\n"); + ExitProcess(0); + } + CCDebugString ("C&C95 - Clean & ready to quit.\n"); + return(0); + + case WM_ACTIVATEAPP: + GameInFocus=(BOOL)wParam; + if (!GameInFocus) { + Focus_Loss(); + } + AllSurfaces.Set_Surface_Focus (GameInFocus); + AllSurfaces.Restore_Surfaces(); +// if (GameInFocus){ +// Restore_Cached_Icons(); +// Map.Flag_To_Redraw(true); +// Start_Primary_Sound_Buffer(TRUE); +// if (WWMouse) WWMouse->Set_Cursor_Clip(); +// } + return(0); +#if (0) + case WM_ACTIVATE: + if (low_param == WA_INACTIVE){ + GameInFocus = FALSE; + Focus_Loss(); + } + return(0); +#endif //(0) + + case WM_SYSCOMMAND: + switch ( wParam ) { + + case SC_CLOSE: + /* + ** Windows sent us a close message. Probably in response to Alt-F4. Ignore it by + ** pretending to handle the message and returning 0; + */ + return (0); + + case SC_SCREENSAVE: + /* + ** Windoze is about to start the screen saver. If we just return without passing + ** this message to DefWindowProc then the screen saver will not be allowed to start. + */ + return (0); + } + break; + +#ifdef FORCE_WINSOCK + case WM_ACCEPT: + case WM_HOSTBYADDRESS: + case WM_HOSTBYNAME: + case WM_ASYNCEVENT: + case WM_UDPASYNCEVENT: + Winsock.Message_Handler(hwnd, message, wParam, lParam); + return (0); +#endif //FORCE_WINSOCK + } + + return (DefWindowProc (hwnd, message, wParam, lParam)); +} + +#endif + + + + + + + + +/*********************************************************************************************** + * Create_Main_Window -- opens the MainWindow for C&C * + * * + * * + * * + * INPUT: instance -- handle to program instance * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/10/95 4:08PM ST : Created * + *=============================================================================================*/ + +#define CC_ICON 1 +int ShowCommand; + +void Create_Main_Window ( HANDLE instance ,int command_show , int width , int height ) + +{ + MainWindow = NULL; + return; +#if (0) + HWND hwnd ; + WNDCLASS wndclass ; + // + // Register the window class + // + + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = Windows_Procedure ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = instance ; + wndclass.hIcon = LoadIcon (instance, MAKEINTRESOURCE(CC_ICON)) ; + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = "Command & Conquer"; //NULL + wndclass.lpszClassName = "Command & Conquer"; + + RegisterClass (&wndclass) ; + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + "Command & Conquer", + "Command & Conquer", + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + width, + height, + NULL, + NULL, + instance, + NULL ); + + ShowWindow (hwnd, command_show ); + ShowCommand = command_show; + UpdateWindow (hwnd); + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + hInstance = instance; + + CCFocusMessage = RegisterWindowMessage ("CC_GOT_FOCUS"); + + Audio_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Restore_Function = &Focus_Restore; + Gbuffer_Focus_Loss_Function = &Focus_Loss; +#endif +} + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc) +{ +#if (0) + MSG msg; + /* + ** Get rid of the Westwood mouse cursor because we are showing a + ** dialog box and we want to have the right windows cursor showing + ** for it. + */ + Hide_Mouse(); + ShowCursor(TRUE); + + /* + ** Pop up the dialog box and then run a standard message handler + ** until the dialog box is closed. + */ + + DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc); + while (GetMessage(&msg, NULL, 0, 0) && !AllDone) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* + ** Restore the westwood mouse cursor and get rid of the windows one + ** because it is now time to restore back to the westwood way of + ** doing things. + */ + ShowCursor(FALSE); + Show_Mouse(); +#endif +} + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + +int DebugColour=1; + + +extern "C" void Set_Palette_Register(int number,int red ,int green ,int blue); +//#pragma off (unreferenced) +void Colour_Debug (int call_number) +{ + //#if 0 + //if (DebugColour==call_number || !call_number){ + + //if (call_number){ + // Wait_Vert_Blank(); + //} + + // ST - 1/3/2019 10:43AM + //Set_Palette_Register (0,ColourLookup[call_number].Red , + // ColourLookup[call_number].Green, + // ColourLookup[call_number].Blue); + //} + //#endif +} + +//#pragma on (unreferenced) + + + + + +BOOL Any_Locked (void) +{ + if (SeenBuff.Get_LockCount() || + HidPage.Get_LockCount()){ + return (TRUE); + }else{ + return(FALSE); + } +} + + + + + + + +HANDLE DebugFile = INVALID_HANDLE_VALUE; + +/*********************************************************************************************** + * CCDebugString -- sends a string to the debugger and echos it to disk * + * * + * * + * * + * INPUT: string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/28/96 12:48PM ST : Created * + *=============================================================================================*/ +void CCDebugString (char *string) +{ +#if (0) + + char outstr[256]; + + sprintf (outstr, "%s", string); + + DWORD actual; + if (DebugFile == INVALID_HANDLE_VALUE){ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + }else{ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (DebugFile != INVALID_HANDLE_VALUE){ + SetFilePointer (DebugFile, 0, NULL, FILE_END); + WriteFile(DebugFile, outstr, strlen(outstr)+1, &actual, NULL); + CloseHandle (DebugFile); + } + + OutputDebugString (string); + +#else + + string = string; + +#endif + +} + + + + + + + + + + + + + + +// +// Miscellaneous stubs. Mainly for multi player stuff +// +// +// + +//IPXAddressClass::IPXAddressClass(void) { +// int i; +// i++; +//} +//int IPXManagerClass::Num_Connections(void){ return (0); } +//int IPXManagerClass::Connection_ID( int ) { return (0); } +//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); } +//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); } +//int IPXAddressClass::Is_Broadcast() { return (0); } +//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); } +//int IPXManagerClass::Service() { return (0); } +//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); } +//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); } +//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {} +//IPXManagerClass::~IPXManagerClass() { +// int i; +// i++; +// } +//int IPXManagerClass::Delete_Connection( int ) { return (0); } +//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ){} +//void IPXManagerClass::Set_Socket( short unsigned ){} +//int IPXManagerClass::Is_IPX() { return (0); } +//int IPXManagerClass::Init() { return (0); } +//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ){} +//void IPXManagerClass::Set_Bridge( char unsigned * ){} +//int IPXManagerClass::Global_Num_Send() { return (0); } +//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ){} +//unsigned long IPXManagerClass::Global_Response_Time() { return (0); } +//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); } +//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); } +//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); } +//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); } +//int IPXManagerClass::Connection_Index( int ) { return (0); } +//void IPXManagerClass::Reset_Response_Time(){} +//long unsigned IPXManagerClass::Response_Time() { return (0); } +//int IPXManagerClass::Private_Num_Send( int ) { return (0); } + +//_VQAHandle * VQA_Alloc(void){ return ((_VQAHandle *)0); } +//void VQA_Init( _VQAHandle *, long ( *)()) {} +//long VQA_Open( _VQAHandle *, char const *, _VQAConfig * ) { return (0); } +//void VQA_Free( _VQAHandle * ) {} +//void VQA_Close( _VQAHandle * ) {} +//long VQA_Play( _VQAHandle *, long ) { return (0); } + +//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)){} + +//long VQA_Open(VQAHandle *, char const *, VQAConfig *) +//{ +// return (0); +//} + +//void VQA_Close(VQAHandle *){} + +//long VQA_Play(VQAHandle *, long) +//{ +// return (0); +//} + + +unsigned char *VQPalette; +long VQNumBytes; +unsigned long VQSlowpal; +bool VQPaletteChange = false; + + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +} + + + +void Flag_To_Set_Palette(unsigned char *palette,long numbytes,unsigned long slowpal) +{ + VQPalette = palette; + VQNumBytes = numbytes; + VQSlowpal = slowpal; + VQPaletteChange = true; +} + + + +void Check_VQ_Palette_Set(void) +{ + if (VQPaletteChange){ + SetPalette(VQPalette, VQNumBytes, VQSlowpal); + VQPaletteChange = false; + } +} + + + + + +void __cdecl SetPalette(unsigned char *palette,long,unsigned long) +{ + for (int i=0 ; i<256*3 ; i++){ + *(palette+i)&=63; + } + Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63); + + if (PalettesRead){ + memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536); + } + Set_Palette(palette); +} + + + +/*********************************************************************************************** + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 3:57PM ST : Created * + *=============================================================================================*/ +void Memory_Error_Handler(void) +{ + GlyphX_Debug_Print("Error - out of memory."); + VisiblePage.Clear(); + Set_Palette(GamePalette); + while (Get_Mouse_State()){Show_Mouse();}; + CCMessageBox().Process("Error - out of memory.", "Abort", false); + Invalidate_Cached_Icons(); + + // Nope. ST - 1/10/2019 10:38AM + //PostQuitMessage( 0 ); + //ExitProcess(0); +} + + + + + + + + + + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size); + + + +/*********************************************************************************************** + * Load_Title_Screen -- loads the title screen into the given video buffer * + * * + * * + * * + * INPUT: screen name * + * video buffer * + * ptr to buffer for palette * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 7/5/96 11:30AM ST : Created * + *=============================================================================================*/ + +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette) +{ + + GraphicBufferClass *load_buffer; + + + load_buffer = Read_PCX_File (name, (char*)palette, NULL, 0); + + if (load_buffer){ + load_buffer->Blit(*video_page); + delete load_buffer; + } +} + + + +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optinal, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointd by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * Appears to be a comment-free zone * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + * 04/30/1996 ST : Tidied up and modified to use CCFileClass * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + file_handle.Read (pool , POOL_SIZE ); \ + file_ptr = pool ; \ + } + +/* +** The old RGB was signed, so the shift right didn't work. ST - 1/4/2019 4:49PM +*/ +typedef struct { + unsigned char red ; + unsigned char green ; + unsigned char blue ; +} PG_RGB ; + +GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size) +{ + int i, j; + unsigned rle; + unsigned color; + int scan_pos; + unsigned char *file_ptr; + int width; + int height; + unsigned char *buffer; + PCX_HEADER header; + PG_RGB *pal; + unsigned char pool [POOL_SIZE]; + GraphicBufferClass *pic; + + CCFileClass file_handle(name); + + if (!file_handle.Is_Available()) return (NULL); + + file_handle.Open(READ); + + file_handle.Read (&header, sizeof (PCX_HEADER)); + + if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1; + height = header.height - header.y + 1; + + if (Buff) { + buffer = (unsigned char*) Buff; + i = Size / width; + height = MIN (i - 1, height); + pic = new GraphicBufferClass(width, height, buffer, Size); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } else { + pic = new GraphicBufferClass(width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } + + buffer = (unsigned char *) pic->Get_Buffer(); + file_ptr = pool ; + file_handle.Read (pool , POOL_SIZE); + + if ( header.byte_per_line != width ){ + + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ); + i += rle; + } else { + *(buffer+scan_pos + i++ ) = (char)rle; + } + } + } + + if ( i == width ) rle = READ_CHAR (); + if ( rle > 192 ) rle = READ_CHAR (); + + } else { + + for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ); + i += rle ; + }else{ + *(buffer + i++) = rle; + } + } + } + + if ( palette ) { + file_handle.Seek (- (256 * (int)sizeof ( PG_RGB )) , SEEK_END ); + file_handle.Read (palette , 256L * sizeof ( PG_RGB )); + + pal = ( PG_RGB * ) palette; + for (i = 0 ; i < 256 ; i ++) { + pal ->red >>= 2; + pal ->green >>= 2; + pal ->blue >>= 2; + pal ++ ; + } + } + + file_handle.Close(); + return pic; +} \ No newline at end of file diff --git a/TIBERIANDAWN/WWALLOC.H b/TIBERIANDAWN/WWALLOC.H new file mode 100644 index 000000000..1cb86f58d --- /dev/null +++ b/TIBERIANDAWN/WWALLOC.H @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\wwalloc.h_v 2.18 16 Oct 1995 16:47:52 JOE_BOSTIC $ */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** This should be located in the wwlib32.h file, but is located here for +** test purposes. +*/ +#ifdef __FLAT__ +#define PRIVATE static +#endif + +typedef enum MemoryFlagType { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_PUBLIC = 0x0000, // Default memory (normal). + MEM_CHIP = 0x0000, // Graphic & sound buffer memory (Amiga). + MEM_UNUSED = 0x0001, // + MEM_SYSTEM = 0x0002, // Allocate out of system heap (XMS or EMS only). + MEM_RELAXED= 0x0004, // Don't worry about page conservation in EMS. + MEM_TEMP = 0x0008, // Temporary allocation (used by library only). + MEM_CLEAR = 0x0010, // Fill memory with '\0' characters. + MEM_PARA = 0x0020, // Paragraph aligned (IBM only). + MEM_XMS = 0x0040, // XMS memory. + MEM_EMS = 0x0080, // EMS memory (not implemented). + MEM_X = 0x8000 // Here to force this enum to be unsigned sized. +} MemoryFlagType; +MemoryFlagType operator |(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator &(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator ~(MemoryFlagType); + + +/* Prototypes for functions defined in this file */ +void * __cdecl Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void __cdecl Free(void const *pointer); +void * __cdecl Resize_Alloc(void const *original_ptr, unsigned long new_size_in_bytes); +long __cdecl Ram_Free(MemoryFlagType flag); +long __cdecl Total_Ram_Free(MemoryFlagType flag); +long __cdecl Heap_Size(MemoryFlagType flag); + +extern unsigned long __cdecl MinRam; // Record of least memory at worst case. +extern unsigned long __cdecl MaxRam; // Record of total allocated at worst case. + +#ifdef __cplusplus +} +#endif + diff --git a/TIBERIANDAWN/WWFILE.H b/TIBERIANDAWN/WWFILE.H new file mode 100644 index 000000000..9910ff220 --- /dev/null +++ b/TIBERIANDAWN/WWFILE.H @@ -0,0 +1,75 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.15 16 Oct 1995 16:46:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +//#include + + +#ifndef _READ +#define READ 1 +#define WRITE 2 +#endif + + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif